fluentui
fluentui copied to clipboard
feat(react-jsx-runtime): implements next steps (option D)
Previous Behavior
New Behavior
Related Issue(s)
- Fixes #
📊 Bundle size report
| Package & Exports | Baseline (minified/GZIP) | PR | Change |
|---|---|---|---|
| react-accordion Accordion (including children components) |
88.516 kB26.795 kB |
88.501 kB26.845 kB |
-15 B 50 B |
| react-alert Alert |
93.549 kB22.537 kB |
93.74 kB22.643 kB |
191 B 106 B |
| react-avatar Avatar |
57.797 kB15.091 kB |
57.755 kB15.073 kB |
-42 B -18 B |
| react-avatar AvatarGroup |
15.646 kB6.298 kB |
15.822 kB6.382 kB |
176 B 84 B |
| react-avatar AvatarGroupItem |
73.973 kB19.582 kB |
73.938 kB19.578 kB |
-35 B -4 B |
| react-badge Badge |
23.555 kB7.256 kB |
23.421 kB7.226 kB |
-134 B -30 B |
| react-badge CounterBadge |
24.457 kB7.559 kB |
24.329 kB7.529 kB |
-128 B -30 B |
| react-badge PresenceBadge |
32.135 kB8.423 kB |
32.02 kB8.392 kB |
-115 B -31 B |
| react-button Button |
36.742 kB9.5 kB |
36.788 kB9.504 kB |
46 B 4 B |
| react-button CompoundButton |
43.896 kB10.98 kB |
43.946 kB10.975 kB |
50 B -5 B |
| react-button MenuButton |
41.427 kB10.836 kB |
41.482 kB10.845 kB |
55 B 9 B |
| react-button SplitButton |
49.649 kB12.42 kB |
49.706 kB12.463 kB |
57 B 43 B |
| react-button ToggleButton |
55.024 kB11.436 kB |
55.065 kB11.44 kB |
41 B 4 B |
| react-card Card - All |
88.716 kB25.114 kB |
88.696 kB25.099 kB |
-20 B -15 B |
| react-card Card |
83.651 kB23.658 kB |
83.525 kB23.659 kB |
-126 B 1 B |
| react-card CardFooter |
9.193 kB3.892 kB |
9.06 kB3.863 kB |
-133 B -29 B |
| react-card CardHeader |
11.089 kB4.588 kB |
11.003 kB4.551 kB |
-86 B -37 B |
| react-card CardPreview |
9.998 kB4.24 kB |
9.873 kB4.213 kB |
-125 B -27 B |
| react-checkbox Checkbox |
34.5 kB10.878 kB |
34.405 kB10.868 kB |
-95 B -10 B |
| react-combobox Combobox (including child components) |
87.735 kB28.243 kB |
87.65 kB28.212 kB |
-85 B -31 B |
| react-combobox Dropdown (including child components) |
86.074 kB27.848 kB |
85.989 kB27.822 kB |
-85 B -26 B |
| react-components react-components: Button, FluentProvider & webLightTheme |
64.899 kB17.91 kB |
64.965 kB17.944 kB |
66 B 34 B |
| react-components react-components: Accordion, Button, FluentProvider, Image, Menu, Popover |
206.425 kB57.914 kB |
206.488 kB57.953 kB |
63 B 39 B |
| react-components react-components: FluentProvider & webLightTheme |
36.132 kB11.954 kB |
36.302 kB12.019 kB |
170 B 65 B |
| react-datepicker-compat DatePicker Compat |
222.56 kB59.204 kB |
222.486 kB59.165 kB |
-74 B -39 B |
| react-dialog Dialog (including children components) |
92.076 kB27.492 kB |
91.961 kB27.484 kB |
-115 B -8 B |
| react-divider Divider |
17.441 kB6.349 kB |
17.303 kB6.306 kB |
-138 B -43 B |
| react-field Field |
18.9 kB7.083 kB |
18.844 kB7.045 kB |
-56 B -38 B |
| react-image Image |
11.514 kB4.619 kB |
11.691 kB4.709 kB |
177 B 90 B |
| react-infobutton InfoButton |
130.121 kB39.785 kB |
130.029 kB39.767 kB |
-92 B -18 B |
| react-infobutton InfoLabel |
133.586 kB40.852 kB |
133.523 kB40.868 kB |
-63 B 16 B |
| react-input Input |
24.183 kB7.772 kB |
24.047 kB7.716 kB |
-136 B -56 B |
| react-label Label |
10.139 kB4.231 kB |
10.005 kB4.207 kB |
-134 B -24 B |
| react-link Link |
12.339 kB5.105 kB |
12.52 kB5.195 kB |
181 B 90 B |
| react-menu Menu (including children components) |
130.848 kB39.946 kB |
130.774 kB39.9 kB |
-74 B -46 B |
| react-menu Menu (including selectable components) |
133.832 kB40.479 kB |
133.666 kB40.421 kB |
-166 B -58 B |
| react-persona Persona |
64.718 kB17.012 kB |
64.752 kB16.989 kB |
34 B -23 B |
| react-popover Popover |
117.083 kB36.122 kB |
117.278 kB36.189 kB |
195 B 67 B |
| react-progress ProgressBar |
13.891 kB5.482 kB |
13.757 kB5.438 kB |
-134 B -44 B |
| react-provider FluentProvider |
18.079 kB6.713 kB |
18.249 kB6.779 kB |
170 B 66 B |
| react-radio Radio |
27.404 kB8.722 kB |
27.313 kB8.713 kB |
-91 B -9 B |
| react-radio RadioGroup |
11.326 kB4.743 kB |
11.503 kB4.821 kB |
177 B 78 B |
| react-select Select |
25.373 kB8.826 kB |
25.241 kB8.785 kB |
-132 B -41 B |
| react-slider Slider |
34.322 kB11.099 kB |
34.209 kB11.057 kB |
-113 B -42 B |
| react-spinbutton SpinButton |
34.121 kB10.421 kB |
33.981 kB10.376 kB |
-140 B -45 B |
| react-spinner Spinner |
21.327 kB7.015 kB |
21.247 kB7.01 kB |
-80 B -5 B |
| react-switch Switch |
29.924 kB9.342 kB |
29.834 kB9.349 kB |
-90 B 7 B |
| react-table DataGrid |
150.868 kB41.518 kB |
150.899 kB41.587 kB |
31 B 69 B |
| react-table Table (Primitives only) |
45.111 kB12.567 kB |
45.191 kB12.568 kB |
80 B 1 B |
| react-table Table as DataGrid |
133.356 kB34.002 kB |
133.481 kB34.027 kB |
125 B 25 B |
| react-table Table (Selection only) |
79.125 kB19.379 kB |
79.248 kB19.448 kB |
123 B 69 B |
| react-table Table (Sort only) |
78.455 kB19.187 kB |
78.578 kB19.241 kB |
123 B 54 B |
| react-tags Tag |
22.004 kB7.93 kB |
21.923 kB7.888 kB |
-81 B -42 B |
| react-text Text - Default |
12.527 kB4.963 kB |
12.705 kB5.051 kB |
178 B 88 B |
| react-text Text - Wrappers |
15.677 kB5.284 kB |
15.855 kB5.368 kB |
178 B 84 B |
| react-textarea Textarea |
27.686 kB9.126 kB |
27.53 kB9.094 kB |
-156 B -32 B |
| react-tooltip Tooltip |
47.119 kB16.528 kB |
46.956 kB16.487 kB |
-163 B -41 B |
Unchanged fixtures
| Package & Exports | Size (minified/GZIP) |
|---|---|
| global-context createContext |
510 B330 B |
| global-context createContextSelector |
537 B342 B |
| react-overflow hooks only |
11.206 kB4.266 kB |
| react-portal Portal |
11.676 kB4.31 kB |
| react-portal-compat PortalCompatProvider |
6.473 kB2.196 kB |
| react-positioning usePositioning |
24.249 kB8.856 kB |
| react-utilities SSRProvider |
180 B159 B |
Asset size changes
Size Auditor did not detect a change in bundle size for any component!
Baseline commit: 65f0125dd6a85c3c772d63ad3350561af77addbc (build)
Perf Analysis (@fluentui/react-components)
| Scenario | Render type | Master Ticks | PR Ticks | Iterations | Status |
|---|---|---|---|---|---|
| Avatar | mount | 578 | 599 | 5000 | Possible regression |
| Button | mount | 294 | 302 | 5000 | Possible regression |
| InfoButton | mount | 17 | 17 | 5000 | Possible regression |
| SpinButton | mount | 1279 | 1334 | 5000 | Possible regression |
All results
| Scenario | Render type | Master Ticks | PR Ticks | Iterations | Status |
|---|---|---|---|---|---|
| Avatar | mount | 578 | 599 | 5000 | Possible regression |
| Button | mount | 294 | 302 | 5000 | Possible regression |
| Field | mount | 1009 | 1051 | 5000 | |
| FluentProvider | mount | 659 | 669 | 5000 | |
| FluentProviderWithTheme | mount | 78 | 85 | 10 | |
| FluentProviderWithTheme | virtual-rerender | 72 | 72 | 10 | |
| FluentProviderWithTheme | virtual-rerender-with-unmount | 76 | 76 | 10 | |
| InfoButton | mount | 17 | 17 | 5000 | Possible regression |
| MakeStyles | mount | 904 | 875 | 50000 | |
| Persona | mount | 1601 | 1654 | 5000 | |
| SpinButton | mount | 1279 | 1334 | 5000 | Possible regression |
It would be good to summarize changes required from consumers/developers to upgrade to a new approach
I am in favor of Option D (this PR)
- it's less changes ➡️ faster upgrade path
- smaller bundle size as
.propscan't be minified
Anyway, before we will do any changes we need to ensure that a new release with these changes will not break consumers
The root problem is more than just being a required slot problem @behowell. There are a few problems involved here.
getNativeElementProps is harmful to our typings
is basically a blackhole that allows you to do so many wrong things
Here's an example:
In TreeItem component we have a handler that is used for both cases of focus and mouseover
const handleActionsVisible = useEventCallback((event: React.FocusEvent) => {/**/})
return {
components: {
root: 'div',
},
root: getNativeElementProps(as, {
onMouseOver: handleActionsVisible,
onFocus: handleActionsVisible,
}),
};
Those are 2 completely different types of events React.FocusEvent | React.MouseEvent, but by a mistake I've only added React.FocusEvent as the argument type, and since getNativeElementProps is not very strict about type inferences (there's none) this works just fine and I got no error on TS side.
We remove ref from the primary slot type on ComponentProps
So if we properly type root as ExtractSlotProps<Slots['root']> we can get that type, but that's a little verbose.
{
// this works, is just too verbose and I'm assuming nobody will do this,
// and instead they'll just use `DialogActionsProps` which is wrong.
root: slot<ExtractSlotProps<DialogActionsSlots['root']>>({
shorthand: { ...props, ref },
required: true,
elementType: as,
}),
position,
fluid,
}
It's not possible to validate by types the splitting of props that are supposed to go to the root slot
This is the reason why we can't simply ditch getNativeElementProps
const { position = 'end', fluid = false, as = 'div' } = props;
return {
root: slot<ExtractSlotProps<DialogActionsSlots['root']>>({
// spreading props here is a problem, and here's the worst part: TS doesn't get that 🚨
shorthand: { ...props, ref },
required: true,
elementType: as,
}),
position,
fluid,
};
we have to repeat elementType twice (not a big deal)
{
root: slot<ExtractSlotProps<DialogActionsSlots['root']>>({
shorthand: getNativeElementProps(as /* 1 */, { ...props, ref }),
required: true,
elementType: as, /* 2 */
}),
position,
fluid,
}
Solution?
We should probably provide a slot method that makes the use of getNativeElementProps internal to it to make sure those edge cases are properly done (I guess I'll have to investigate component with primary slot different than root just to be sure how to make this work properly in other edge cases), otherwise one of the problems above will always be a problem
Another possible solution would be to refactor getNativeElementProps to be properly typed (but I'm afraid of doing that as it's a wide spread method)
If we keep everything as it is, this is how root slots should look like:
export const useDialogActions_unstable = (
props: DialogActionsProps,
ref: React.Ref<HTMLDivElement>,
): DialogActionsState => {
const { position = 'end', fluid = false, as = 'div' } = props;
return {
root: slot<ExtractSlotProps<DialogActionsSlots['root']>>({
// keep in mind also that this is only enforcing the return type of `getNativeElementProps`,
// the second argument to `getNativeElementProps` is still quite harmful to our
// typings as you can basically pass anything you want there
shorthand: getNativeElementProps<ExtractSlotProps<DialogActionsSlots['root']>>(as, { ...props, ref }),
required: true,
elementType: as,
}),
position,
fluid,
};
};
This pull request is automatically built and testable in CodeSandbox.
To see build info of the built libraries, click here or the icon next to each commit SHA.
Latest deployment of this branch, based on commit 72baa4bb624c2d75229174c9e68ae31a784edb4b:
| Sandbox | Source |
|---|---|
| @fluentui/react 8 starter | Configuration |
| @fluentui/react-components 9 starter | Configuration |

