-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
RFC: Customizing application of native element props and ref #18983
Conversation
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 511c32a:
|
Asset size changesSize Auditor did not detect a change in bundle size for any component! Baseline commit: 1e5b888607626cae4ff69b5b9055bf8b9537c45b (build) |
📊 Bundle size reportUnchanged fixtures
|
Perf Analysis (
|
Scenario | Render type | Master Ticks | PR Ticks | Iterations | Status |
---|---|---|---|---|---|
Avatar | mount | 1034 | 1029 | 5000 | |
BaseButton | mount | 1038 | 1043 | 5000 | |
Breadcrumb | mount | 2917 | 2856 | 1000 | |
ButtonNext | mount | 512 | 516 | 5000 | |
Checkbox | mount | 1741 | 1697 | 5000 | |
CheckboxBase | mount | 1520 | 1504 | 5000 | |
ChoiceGroup | mount | 5544 | 5520 | 5000 | |
ComboBox | mount | 1063 | 1115 | 1000 | |
CommandBar | mount | 11382 | 11289 | 1000 | |
ContextualMenu | mount | 7263 | 7249 | 1000 | |
DefaultButton | mount | 1328 | 1330 | 5000 | |
DetailsRow | mount | 4311 | 4313 | 5000 | |
DetailsRowFast | mount | 4329 | 4324 | 5000 | |
DetailsRowNoStyles | mount | 4141 | 4078 | 5000 | |
Dialog | mount | 2699 | 2739 | 1000 | |
DocumentCardTitle | mount | 174 | 147 | 1000 | |
Dropdown | mount | 3717 | 3686 | 5000 | |
FluentProviderNext | mount | 7478 | 7670 | 5000 | |
FluentProviderWithTheme | mount | 383 | 392 | 10 | |
FluentProviderWithTheme | virtual-rerender | 95 | 109 | 10 | |
FluentProviderWithTheme | virtual-rerender-with-unmount | 510 | 542 | 10 | |
FocusTrapZone | mount | 2149 | 2088 | 5000 | |
FocusZone | mount | 2037 | 2083 | 5000 | |
IconButton | mount | 2079 | 2065 | 5000 | |
Label | mount | 378 | 398 | 5000 | |
Layer | mount | 3457 | 3504 | 5000 | |
Link | mount | 530 | 583 | 5000 | |
MakeStyles | mount | 2093 | 2096 | 50000 | |
MenuButton | mount | 1725 | 1764 | 5000 | |
MessageBar | mount | 2296 | 2326 | 5000 | |
Nav | mount | 3778 | 3785 | 1000 | |
OverflowSet | mount | 1235 | 1196 | 5000 | |
Panel | mount | 2578 | 2557 | 1000 | |
Persona | mount | 937 | 957 | 1000 | |
Pivot | mount | 1602 | 1577 | 1000 | |
PrimaryButton | mount | 1430 | 1485 | 5000 | |
Rating | mount | 8845 | 9048 | 5000 | |
SearchBox | mount | 1494 | 1550 | 5000 | |
Shimmer | mount | 2938 | 2933 | 5000 | |
Slider | mount | 2278 | 2209 | 5000 | |
SpinButton | mount | 5464 | 5572 | 5000 | |
Spinner | mount | 455 | 468 | 5000 | |
SplitButton | mount | 3914 | 3530 | 5000 | |
Stack | mount | 574 | 604 | 5000 | |
StackWithIntrinsicChildren | mount | 1859 | 1837 | 5000 | |
StackWithTextChildren | mount | 5300 | 5324 | 5000 | |
SwatchColorPicker | mount | 11253 | 11309 | 5000 | |
Tabs | mount | 1555 | 1565 | 1000 | |
TagPicker | mount | 2960 | 2985 | 5000 | |
TeachingBubble | mount | 14346 | 14423 | 5000 | |
Text | mount | 470 | 499 | 5000 | |
TextField | mount | 1521 | 1591 | 5000 | |
ThemeProvider | mount | 1301 | 1285 | 5000 | |
ThemeProvider | virtual-rerender | 641 | 619 | 5000 | |
ThemeProvider | virtual-rerender-with-unmount | 2087 | 2068 | 5000 | |
Toggle | mount | 928 | 889 | 5000 | |
buttonNative | mount | 120 | 124 | 5000 |
Perf Analysis (@fluentui/react-northstar
)
Perf tests with no regressions
Scenario | Current PR Ticks | Baseline Ticks | Ratio |
---|---|---|---|
ChatDuplicateMessagesPerf.default | 360 | 325 | 1.11:1 |
AttachmentMinimalPerf.default | 190 | 172 | 1.1:1 |
BoxMinimalPerf.default | 412 | 374 | 1.1:1 |
ButtonMinimalPerf.default | 199 | 181 | 1.1:1 |
ListWith60ListItems.default | 790 | 716 | 1.1:1 |
CarouselMinimalPerf.default | 547 | 503 | 1.09:1 |
ChatWithPopoverPerf.default | 463 | 425 | 1.09:1 |
FormMinimalPerf.default | 516 | 480 | 1.08:1 |
AnimationMinimalPerf.default | 489 | 456 | 1.07:1 |
ImageMinimalPerf.default | 458 | 427 | 1.07:1 |
ListMinimalPerf.default | 631 | 587 | 1.07:1 |
HeaderMinimalPerf.default | 427 | 402 | 1.06:1 |
TableMinimalPerf.default | 471 | 443 | 1.06:1 |
TreeWith60ListItems.default | 210 | 198 | 1.06:1 |
InputMinimalPerf.default | 1448 | 1381 | 1.05:1 |
ListNestedPerf.default | 663 | 632 | 1.05:1 |
MenuButtonMinimalPerf.default | 1947 | 1852 | 1.05:1 |
TextMinimalPerf.default | 412 | 392 | 1.05:1 |
LayoutMinimalPerf.default | 422 | 407 | 1.04:1 |
ListCommonPerf.default | 761 | 734 | 1.04:1 |
LoaderMinimalPerf.default | 825 | 797 | 1.04:1 |
RefMinimalPerf.default | 264 | 254 | 1.04:1 |
TableManyItemsPerf.default | 2273 | 2180 | 1.04:1 |
ChatMinimalPerf.default | 770 | 751 | 1.03:1 |
CheckboxMinimalPerf.default | 3121 | 3034 | 1.03:1 |
DatepickerMinimalPerf.default | 6216 | 6028 | 1.03:1 |
DividerMinimalPerf.default | 431 | 420 | 1.03:1 |
PortalMinimalPerf.default | 193 | 188 | 1.03:1 |
ReactionMinimalPerf.default | 446 | 431 | 1.03:1 |
SliderMinimalPerf.default | 1852 | 1791 | 1.03:1 |
DialogMinimalPerf.default | 856 | 838 | 1.02:1 |
MenuMinimalPerf.default | 988 | 968 | 1.02:1 |
PopupMinimalPerf.default | 674 | 658 | 1.02:1 |
StatusMinimalPerf.default | 773 | 760 | 1.02:1 |
TextAreaMinimalPerf.default | 601 | 588 | 1.02:1 |
AlertMinimalPerf.default | 308 | 304 | 1.01:1 |
DropdownManyItemsPerf.default | 785 | 775 | 1.01:1 |
CustomToolbarPrototype.default | 4304 | 4248 | 1.01:1 |
TooltipMinimalPerf.default | 1183 | 1172 | 1.01:1 |
TreeMinimalPerf.default | 947 | 934 | 1.01:1 |
AvatarMinimalPerf.default | 227 | 227 | 1:1 |
ButtonOverridesMissPerf.default | 1889 | 1896 | 1:1 |
LabelMinimalPerf.default | 458 | 459 | 1:1 |
ProviderMergeThemesPerf.default | 1818 | 1812 | 1:1 |
RadioGroupMinimalPerf.default | 537 | 538 | 1:1 |
SplitButtonMinimalPerf.default | 4746 | 4757 | 1:1 |
DropdownMinimalPerf.default | 3332 | 3364 | 0.99:1 |
FlexMinimalPerf.default | 316 | 320 | 0.99:1 |
GridMinimalPerf.default | 390 | 393 | 0.99:1 |
SkeletonMinimalPerf.default | 414 | 418 | 0.99:1 |
IconMinimalPerf.default | 716 | 720 | 0.99:1 |
ToolbarMinimalPerf.default | 1093 | 1104 | 0.99:1 |
EmbedMinimalPerf.default | 4619 | 4704 | 0.98:1 |
ProviderMinimalPerf.default | 1126 | 1146 | 0.98:1 |
ButtonSlotsPerf.default | 623 | 645 | 0.97:1 |
HeaderSlotsPerf.default | 870 | 901 | 0.97:1 |
ItemLayoutMinimalPerf.default | 1423 | 1460 | 0.97:1 |
RosterPerf.default | 1350 | 1392 | 0.97:1 |
AttachmentSlotsPerf.default | 1196 | 1244 | 0.96:1 |
SegmentMinimalPerf.default | 387 | 405 | 0.96:1 |
AccordionMinimalPerf.default | 174 | 183 | 0.95:1 |
CardMinimalPerf.default | 640 | 671 | 0.95:1 |
VideoMinimalPerf.default | 742 | 790 | 0.94:1 |
- For interactive controls, props are passed to the **interative element** | ||
- Need examples here too | ||
|
||
### Should top-level `className` always be applied to the root? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO this is not ideal and also not intuitive, and I don't see which big impact would it make in layout if this wasn't adopted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the scenario for this was if someone wanted to apply styling (for example flex properties) to fields within a form. Then they would probably expect the top-level className to go to the top-level element.
|
||
## Summary | ||
|
||
Instead of always applying native props and `ref` to the root, update `makeMergeProps` to allow setting a different "primary" slot where these props should be applied. This option should be used sparingly: for input components and possibly a few other cases (options for exact criteria discussed below). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makeMergeProps
will be deprecated after as prop simplification RFC
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah okay, I was wondering about that after trying to copy some of the Accordion code in Input and noticing makeMergeProps was gone. Where would this go instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now this is done manually on state construction as you can verify on Accordion code. Biggest change from as prop simplification RFC is the usage of a more explicit approach for state declaration, it becomes dev responsibility to pass ref
and other props to the proper slot
.
IMO this RFC makes
I believe that this changes are valid and makes slots implementation even more generic and explicit. |
@ecraig12345 is this ready for review or do you plan to update it? |
@layershifter It's not ready for review. I need to update it but haven't gotten to do it yet. |
No.
Unfortunately, Northstar is not that consistent,
There are different use cases - for positioning the control inside a flex, you need to style the outermost element.
This is the right approach how we should make decisions, thank you very much for that 👍 My main concern is not about where For <Button className="only-here" /> |
I agree that table wasn't really enough to get a full picture. So I did a larger survey of components in a number of react-based libraries. I found that a large majority follow the rule " Also for what it's worth, we may want to weight FluentUI flavors more heavily, since that's what many of our users will be upgrading from. Option C does make the upgrade scenario more difficult, as it changes the behavior in a way that can't be caught by build breaks or warnings. We could likely write a codemod to help with that though. |
Option C works and gets my vote 👍, and gives instant compatibility to form validation libraries. I just see no point in calling it Let's just call this |
Thanks everyone for the input! I'll give a more detailed response later but quick FYI about @behowell's comparison table: note that similar to what @miroslavstastny said about |
I'm proposing Option D based on a meeting including @ecraig12345, @miroslavstastny, @levithomason and others: Start with Option C, with the following changes:
Some background on the reasoning:
|
I think we are kind of bound by our Let's look at evergreen's checkbox: <label @class @style @data>
<input type="checkbox" @id @name />
<div />
<span />
</label> It is just a mix and match of attributes, and styling the I think we need consistency becuase we are actively pushing slots as a part of our general architecture. Therefore my vote is still on option C |
My vote is still on C. IMO we should be consistent and // "id" & "className" should go to the same slot
<Input id="bar" className="foo" />
|
My 2c; Agree with @behowell above; Beyond those 2 props, ideally 99% of the other top level props "do the right thing" and you only need to break into slot props in a very very rare occasion. I think Option C (with the exceptions) should lead to this. You can style the |
The chosen solution needs to adhere to the following principles (thanks @levithomason): | ||
|
||
- User needs to retain access to any part of DOM at any time (can't make a piece of DOM that's unable to receive props) | ||
- Consider what the user expects when using the component |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One way that I've used Fluent components in several applications is along with StyledComponents and Emotion. I extended or modified the style of a Fluent component using the style extension mechanism.
const StyledLink = styled(Link)`
color: palevioletred;
font-weight: bold;
`;
My expectation is that the styles apply to the root element of the component. In the checkbox example, If I applied a margin of 4px and this was applied to the input - moving the bookends away from the input part - I would be very surprised.
When using StyledComponents with Fluent, I often examine the output DOM of a component and then apply selectors to update the style of child parts. Sometimes this doesn't work because the default part styles override. I then either need to create a styles object to style that part directly, or apply important to my style.
Whichever solution is chosen, I think it should support the expected behavior of developers working with other styling libraries.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dzearing's argument re styling is definitely valid.
But also
Predictability is key.
Once we will do prop splitting we need to add slots for both root
and input
to be able to override the split - and this "three names for the two places" and "which one wins?" is what I see as breaking the predictability.
So styles+className go always to the outermost element versus never do automatic prop splitting?
I still prefer the latter but if quorum prefers the styles, I am ok with D:
#18983 (comment)
@miroslavstastny @ling1726 @bsunderhus Thanks for the approvals! Just to be 100% clear, which option are you approving? |
I've updated the RFC to reflect the Option D proposal from above. |
As @miroslavstastny exposed, we're still more inclined to the never do prop splitting approach (Option C). We have the feeling that this discussion is kind of stuck and none of the the parties are coming to an agreement, so if majority prefers Option D, let's go with Option D and simply get it done 💪🏼, we can always come back to this discussion latter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approving the current proposed design (aka Option D)
Add support for the primary slot described in RFC [RFC: Customizing application of native element props and ref](https://github.com/microsoft/fluentui/blob/master/rfcs/convergence/native-element-props.md) (#18983) * Add `getPartitionedNativeProps` helper function to make sure the right props go to the right slots * Update conformance tests to support primarySlot * Update utility types * Change Checkbox's primary slot to `input`, and other misc cleanup for Checkbox
) Add support for the primary slot described in RFC [RFC: Customizing application of native element props and ref](https://github.com/microsoft/fluentui/blob/master/rfcs/convergence/native-element-props.md) (microsoft#18983) * Add `getPartitionedNativeProps` helper function to make sure the right props go to the right slots * Update conformance tests to support primarySlot * Update utility types * Change Checkbox's primary slot to `input`, and other misc cleanup for Checkbox
Alternate version of #18804 with a slightly different approach, and broadening the focus beyond input components in some aspects. This came after a discussion in the Redmond components crew sync and incorporates various people's feedback from the previous PR.
EDIT: removed description because it was very outdated--see the final text and the discussion for details