Skip to content

RFCish: Manual check of vNext props consistency #19775

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from

Conversation

GeoffCoxMSFT
Copy link
Member

@GeoffCoxMSFT GeoffCoxMSFT commented Sep 13, 2021

Pull request checklist

Description of changes

This is a PR to allow conversation/comments on the vNext props consistency. It isn't intended to be checked in.

Focus areas to test

(optional)

@GeoffCoxMSFT GeoffCoxMSFT self-assigned this Sep 13, 2021

## Nit: Explicit component type declaration

> Some components declare their type explicity, others do not.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From @ecraig12345 in HackMD:
At least IMO it's strongly preferable to have explicit type declarations, and this is one of the things I'd like to write a conformance test for. (Also the & React.RefAttributes part isn't needed because it's included in ComponentProps somewhere.) It's a cleaner API from a consumer standpoint and sometimes shows up MUCH cleaner in API reports by preventing unnecessary type expansion.

It can be simplified to: export const Accordion: React.FunctionComponent<AccordionProps> = React.forwardRef((props, ref) => ...)

Copy link
Member Author

@GeoffCoxMSFT GeoffCoxMSFT Sep 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from Guest Rodriquez in HackMD:
#19614 (comment)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from Guest Weaver in HackMD:
Please use at least ForwardRefExoticComponent


## Slots and HTML attributes

> Only Accordian and Menu components use ComponentProps<> with slots currently.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from @ecraig12345 in HackMD:
Maybe this should be obvious but I'm not totally clear on what you mean here, sorry. Can you give an example of what it looks like in a component that doesn't use this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't a consistency problem per se. I think we want to make sure that the types of each of the slots make sense. As the components using the ComponentPropsCompat are converted to using ComponentProps and start having slots then we will want to check the consistency.

secondaryContent?: React.HTMLAttributes<HTMLElement>;
```

## Compat and HTML attributes
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from @ecraig12345 in HackMD:
This should also be addressed by the simplified prop merging issues


## Property overrides of native properties

> Several components declare properties that are also declared by extending from the DOM types (e.g. React.HtmlAttributes<T>).
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from @ecraig12345 in HackMD:
This is an interesting one, and a very good case for conformance testing. There are a couple sub-cases:

  1. Explicitly declaring (as original type) for documentation purposes => probably fine (example: people get confused if the docs for Link don't explicitly mention href)
  2. Explicitly declaring as limited sub-type => TBD if this is okay. If we allow it, it should be paired with Omit on the native props.
  3. Accidental reuse of name for a different purpose => should come up with a different name (example: for Input I wanted to use size but had to rename it since that's a native input prop with different meaning)


checked, children, color, defaultChecked, disabled, href, image, name, onClick, open, ref, target, type, value

> Several components use declar properties that are also HTML element names.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from @ecraig12345 in HackMD:
Are you aware of any cases where this would cause conflicts?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it would cause a compile or runtime issue, but it could confuse users who might think it has to be set to an HTML element.

@codesandbox-ci
Copy link

codesandbox-ci bot commented Sep 13, 2021

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 fce4728:

Sandbox Source
Fluent UI React Starter Configuration

@size-auditor
Copy link

size-auditor bot commented Sep 13, 2021

Asset size changes

Size Auditor did not detect a change in bundle size for any component!

Baseline commit: 90d9972fef1d2d76b0c28e360b05455a1a602915 (build)

@fabricteam
Copy link
Collaborator

fabricteam commented Sep 13, 2021

📊 Bundle size report

Unchanged fixtures
Package & Exports Size (minified/GZIP)
react-accordion
Accordion (including children components)
55.248 kB
17.403 kB
react-avatar
Avatar
56.558 kB
15.66 kB
react-badge
Badge
24.343 kB
7.165 kB
react-badge
CounterBadge
27.156 kB
7.851 kB
react-badge
PresenseBadge
237 B
177 B
react-button
Button
22.932 kB
6.984 kB
react-button
CompoundButton
28.215 kB
7.834 kB
react-button
MenuButton
24.733 kB
7.546 kB
react-button
ToggleButton
32.527 kB
7.601 kB
react-components
react-components: Accordion, Button, FluentProvider, Image, Menu, Popover
163.935 kB
46.761 kB
react-components
react-components: FluentProvider & webLightTheme
35.75 kB
11.392 kB
react-divider
Divider
15.889 kB
5.747 kB
react-image
Image
9.882 kB
3.975 kB
react-input
Input
31.636 kB
11.312 kB
react-label
Label
9.397 kB
3.839 kB
react-link
Link
12.609 kB
4.948 kB
react-make-styles
makeStaticStyles (runtime)
7.59 kB
3.321 kB
react-make-styles
makeStyles + mergeClasses (runtime)
22.135 kB
8.356 kB
react-make-styles
makeStyles + mergeClasses (build time)
2.557 kB
1.202 kB
react-menu
Menu (including children components)
102.963 kB
31.313 kB
react-menu
Menu (including selectable components)
105.239 kB
31.665 kB
react-popover
Popover
100.411 kB
30.075 kB
react-portal
Portal
6.725 kB
2.237 kB
react-positioning
usePopper
23.145 kB
7.942 kB
react-provider
FluentProvider
15.748 kB
5.773 kB
react-slider
Slider
30.661 kB
9.712 kB
react-switch
Switch
18.067 kB
6.181 kB
react-text
Text - Default
11.798 kB
4.452 kB
react-text
Text - Wrappers
15.414 kB
4.734 kB
react-theme
Teams: all themes
32.941 kB
6.674 kB
react-theme
Teams: Light theme
20.247 kB
5.662 kB
react-tooltip
Tooltip
46.029 kB
15.655 kB
react-utilities
SSRProvider
213 B
170 B
🤖 This report was generated against 90d9972fef1d2d76b0c28e360b05455a1a602915

@fabricteam
Copy link
Collaborator

fabricteam commented Sep 13, 2021

Perf Analysis (@fluentui/react)

Scenario Render type Master Ticks PR Ticks Iterations Status
Panel mount 2439 1430 1000 Possible regression
All results

Scenario Render type Master Ticks PR Ticks Iterations Status
Avatar mount 938 939 5000
BaseButton mount 960 944 5000
Breadcrumb mount 2768 2783 1000
ButtonNext mount 461 466 5000
Checkbox mount 1578 1614 5000
CheckboxBase mount 1434 1341 5000
ChoiceGroup mount 4934 4948 5000
ComboBox mount 1040 1036 1000
CommandBar mount 10955 10906 1000
ContextualMenu mount 7210 6936 1000
DefaultButton mount 1236 1243 5000
DetailsRow mount 3984 3888 5000
DetailsRowFast mount 3890 4116 5000
DetailsRowNoStyles mount 3698 3775 5000
Dialog mount 2568 2503 1000
DocumentCardTitle mount 161 153 1000
Dropdown mount 3434 3388 5000
FluentProviderNext mount 7741 7779 5000
FluentProviderWithTheme mount 372 384 10
FluentProviderWithTheme virtual-rerender 100 101 10
FluentProviderWithTheme virtual-rerender-with-unmount 510 512 10
FocusTrapZone mount 1881 1916 5000
FocusZone mount 1896 1927 5000
IconButton mount 1810 1839 5000
Label mount 353 345 5000
Layer mount 3139 3104 5000
Link mount 472 481 5000
MakeStyles mount 1892 1875 50000
MenuButton mount 1533 1541 5000
MessageBar mount 2110 2143 5000
Nav mount 3436 3426 1000
OverflowSet mount 1161 1148 5000
Panel mount 2439 1430 1000 Possible regression
Persona mount 865 868 1000
Pivot mount 1494 1465 1000
PrimaryButton mount 1336 1338 5000
Rating mount 8093 8089 5000
SearchBox mount 1374 1369 5000
Shimmer mount 2702 2662 5000
Slider mount 2044 2051 5000
SpinButton mount 5262 5262 5000
Spinner mount 452 446 5000
SplitButton mount 3323 3311 5000
Stack mount 522 532 5000
StackWithIntrinsicChildren mount 1696 1698 5000
StackWithTextChildren mount 4766 4763 5000
SwatchColorPicker mount 10941 10857 5000
Tabs mount 1517 1501 1000
TagPicker mount 2780 2735 5000
TeachingBubble mount 13825 14162 5000
Text mount 459 417 5000
TextField mount 1446 1436 5000
ThemeProvider mount 1249 1256 5000
ThemeProvider virtual-rerender 608 627 5000
ThemeProvider virtual-rerender-with-unmount 1999 1967 5000
Toggle mount 856 833 5000
buttonNative mount 129 109 5000

Perf Analysis (@fluentui/react-northstar)

Perf tests with no regressions
Scenario Current PR Ticks Baseline Ticks Ratio
AttachmentMinimalPerf.default 193 162 1.19:1
AlertMinimalPerf.default 320 278 1.15:1
ButtonMinimalPerf.default 195 178 1.1:1
RefMinimalPerf.default 246 225 1.09:1
TreeWith60ListItems.default 190 174 1.09:1
ImageMinimalPerf.default 393 369 1.07:1
RadioGroupMinimalPerf.default 484 451 1.07:1
ChatDuplicateMessagesPerf.default 323 306 1.06:1
AttachmentSlotsPerf.default 1156 1097 1.05:1
DividerMinimalPerf.default 387 367 1.05:1
DropdownManyItemsPerf.default 735 701 1.05:1
FlexMinimalPerf.default 311 297 1.05:1
HeaderSlotsPerf.default 804 773 1.04:1
ListWith60ListItems.default 683 659 1.04:1
ProviderMinimalPerf.default 1125 1081 1.04:1
CarouselMinimalPerf.default 498 484 1.03:1
FormMinimalPerf.default 421 408 1.03:1
LabelMinimalPerf.default 407 395 1.03:1
SliderMinimalPerf.default 1687 1643 1.03:1
ButtonSlotsPerf.default 576 567 1.02:1
EmbedMinimalPerf.default 4431 4357 1.02:1
GridMinimalPerf.default 361 354 1.02:1
LayoutMinimalPerf.default 388 381 1.02:1
ListNestedPerf.default 587 578 1.02:1
LoaderMinimalPerf.default 743 726 1.02:1
PortalMinimalPerf.default 188 185 1.02:1
ReactionMinimalPerf.default 393 384 1.02:1
TextMinimalPerf.default 353 346 1.02:1
AnimationMinimalPerf.default 431 428 1.01:1
DatepickerMinimalPerf.default 5933 5873 1.01:1
HeaderMinimalPerf.default 374 370 1.01:1
ItemLayoutMinimalPerf.default 1275 1263 1.01:1
ListCommonPerf.default 672 668 1.01:1
ListMinimalPerf.default 547 543 1.01:1
MenuButtonMinimalPerf.default 1700 1677 1.01:1
ProviderMergeThemesPerf.default 1752 1735 1.01:1
SplitButtonMinimalPerf.default 4280 4246 1.01:1
CustomToolbarPrototype.default 4094 4062 1.01:1
AccordionMinimalPerf.default 158 158 1:1
ButtonOverridesMissPerf.default 1812 1808 1:1
ChatWithPopoverPerf.default 404 402 1:1
InputMinimalPerf.default 1379 1377 1:1
PopupMinimalPerf.default 608 610 1:1
SegmentMinimalPerf.default 355 355 1:1
TableMinimalPerf.default 403 404 1:1
TreeMinimalPerf.default 811 810 1:1
VideoMinimalPerf.default 641 644 1:1
BoxMinimalPerf.default 359 363 0.99:1
ChatMinimalPerf.default 684 689 0.99:1
CheckboxMinimalPerf.default 2858 2888 0.99:1
DialogMinimalPerf.default 791 795 0.99:1
DropdownMinimalPerf.default 3296 3318 0.99:1
TableManyItemsPerf.default 1887 1911 0.99:1
ToolbarMinimalPerf.default 935 945 0.99:1
TooltipMinimalPerf.default 1031 1041 0.99:1
AvatarMinimalPerf.default 200 204 0.98:1
CardMinimalPerf.default 553 563 0.98:1
MenuMinimalPerf.default 868 889 0.98:1
SkeletonMinimalPerf.default 358 367 0.98:1
IconMinimalPerf.default 604 617 0.98:1
StatusMinimalPerf.default 671 690 0.97:1
TextAreaMinimalPerf.default 492 506 0.97:1
RosterPerf.default 1169 1213 0.96:1

PopoverSurfaceProps

```ts
extends ComponentPropsCompat,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ComponentPropsCompat has been removed from Popover since #19767

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to know. As expected, this document will age out quickly as beta work continues.


> Some components have mutually exclusive booleans and others use discriminated unions.
> Some components define a color property that are a set of colors, others are a set semantic names.
> Some naming is inconsistent (e.g. noArrow, pointing)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

funnily enough, noArrow was used both in Popover and Tooltip initially, I wonder what requirement resulted in the divergence @behowell ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The design for Tooltip was changed to not have an arrow by default, so I inverted the property naming so its default value would still be false.

I think I asked design to follow up about whether Popover's default should also change to not have an arrow, but I'm not sure if that happened.

AvatarProps

```ts
activeDisplay: 'ring' | 'shadow' | 'glow' | 'ring-shadow' | 'ring-glow';
Copy link
Member Author

@GeoffCoxMSFT GeoffCoxMSFT Sep 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if there should be two props here: One for that lets you specify 'ring' and maybe future values and another for 'shadow' | 'glow'. Are these specific appearances or is this union trying to cover the permutations of two different things?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per chat with Levi, we should break these up if they are enumerating permutations. I'll follow up with Avatar owner to find out.


**Proposed WorkItems**

- [ ] Update all vNext component props to use types and intersections.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add an example of what the final result would look like in code ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the best example would be a PR :)


- Avoid re-declaring a native property and redefining its type.
- Avoid naming properties that conflict with native prop names.
- Provide a suffix of 'slot' to properties that provide a slot.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that this worth a separate RFC as it that affects all components. This a huge breaking change even for beta as there are already customers that are using v9 components.

We should clearly understand motivation, pros and cons of this proposal. This affects not only us, but also customers that will develop reusable components as they should follow similar patterns.


For example, to cons I can add bundle size/memory concern that should be validated first.

JSX props are compiled to JS objects:

<Button iconSlot={<div />} />
// ⬇⬇⬇
React.createElement(Button, { iconSlot: React.createElement('div') })
  • properties of such objects cannot be mangled by Terser, this means that "iconSlot" will stay in minified JS
  • (is it valid?) properties of objects in memory will be longer

So, Slot suffix will add additional 4 bytes to every slot's usage, at least for bundle size. 4 bytes is nothing, but in terms of huge apps with thousands of components/slots usages it's something.

For example, 1k definitions for slots will result in 4000 bytes in minified code, that gives us almost 4kB for nothing ¯_(ツ)_/¯

Yes, GZIP will compress this, but browsers will still parse uncompressed code.

@GeoffCoxMSFT GeoffCoxMSFT deleted the rfc-vNext-props-consistency branch October 4, 2021 03:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Review component APIs to ensure overall consistency
5 participants