Skip to content
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

feat(compose): add slots and mapPropsToSlotProps #12858

Merged
merged 71 commits into from
Apr 28, 2020

Conversation

mnajdova
Copy link
Contributor

@mnajdova mnajdova commented Apr 24, 2020

This PR introduced the slots and mapSlotsToPropSlots concepts in compose.

Those can be used in the following manner:

  1. When defining a component:
const Button = compose<'button', ButtonProps, ButtonStylesProps, {}, {}>(
  (props, ref, composeOptions) => {
    // component's logic
    const slotProps = composeOptions.resolveSlotProps(props);
    // some other code...
    const contentElement = createShorthand(composeOptions.slots.content, props.content, slotProps.content);
    // return..
  },
  {
    displayName: 'Button',
    // other compose configs
    slots: {
      content: ButtonContent,
      icon: Box,
      loader: Loader,
    },
    mapPropsToSlotProps: props => ({
      content: {
        size: props.size,
        as: 'span',
      },
      loader: {
        role: undefined,
      },
    }),
  }
);

Or when composing existing component

const TertiaryButton = compose<'button', TertiaryButtonProps, TertiaryButtonStylesProps, ButtonProps, {}>(Button, {
  className: 'ui-tertiary-button',
  displayName: 'TertiaryButton',
  mapPropsToStylesProps: props => ({ tertiary: props.tertiary }),
  handledProps: ['tertiary'],
  slots: { content: TertiaryButtonContent },
  mapPropsToSlotProps: props => ({
    content: { tertiary: props.tertiary },
  }),
});

For now I did not include the root in the slots, but we may easily add it :)

layershifter and others added 30 commits April 17, 2020 12:23
-fixed screener
Co-Authored-By: Oleksandr Fediashov <olfedias@microsoft.com>
# Conflicts:
#	packages/fluentui/react-northstar/src/components/Attachment/AttachmentAction.tsx
#	packages/fluentui/react-northstar/src/components/Attachment/AttachmentBody.tsx
#	packages/fluentui/react-northstar/src/components/Attachment/AttachmentDescription.tsx
#	packages/fluentui/react-northstar/src/components/Attachment/AttachmentIcon.tsx
#	packages/fluentui/react-northstar/src/components/Box/Box.tsx
#	packages/fluentui/react-northstar/src/components/Button/Button.tsx
#	packages/fluentui/react-northstar/src/components/Button/ButtonContent.tsx
#	packages/react-compose/etc/react-compose.api.md
#	packages/react-compose/src/compose.ts
#	packages/react-compose/src/types.ts
# Conflicts:
#	packages/fluentui/react-northstar/test/specs/utils/factories-test.tsx
# Conflicts:
#	packages/fluentui/react-northstar/test/specs/utils/factories-test.tsx
# Conflicts:
#	packages/fluentui/react-northstar/src/components/Button/Button.tsx
Co-Authored-By: Roman Sudarikov <pompomon@users.noreply.github.com>
….tsx

Co-Authored-By: Roman Sudarikov <pompomon@users.noreply.github.com>
@msft-github-bot
Copy link
Contributor

msft-github-bot commented Apr 24, 2020

Perf Analysis

No significant results to display.

All results

Scenario Master Ticks PR Ticks Iterations Status
BaseButton 717 716 5000
Checkbox 1398 1451 5000
CheckboxBase 1154 1136 5000
ChoiceGroup 4470 4340 5000
ComboBox 854 831 1000
CommandBar 6734 6853 1000
ContextualMenu 12762 12455 1000
DefaultButton 943 951 5000
DetailsRow 3061 3075 5000
DetailsRow (fast icons) 3064 3104 5000
DetailsRow without styles 2878 2841 5000
Dialog 1295 1288 1000
DocumentCardTitle with truncation 1514 1492 1000
Dropdown 2151 2116 5000
FocusZone 1444 1429 5000
IconButton 1498 1500 5000
Label 274 266 5000
Link 388 382 5000
MenuButton 1258 1241 5000
Nav 2794 2747 1000
Panel 1258 1252 1000
Persona 751 749 1000
Pivot 1189 1158 1000
PrimaryButton 1048 1076 5000
SearchBox 1142 1125 5000
Slider 1317 1319 5000
Spinner 350 350 5000
SplitButton 2735 2816 5000
Stack 400 407 5000
Stack with Intrinsic children 979 1000 5000
Stack with Text children 3599 3607 5000
TagPicker 2412 2439 5000
Text 318 321 5000
TextField 1213 1186 5000
Toggle 755 807 5000
button 62 58 5000

Perf Analysis (Fluent)

⚠️ 3 potential perf regressions detected

Potential regressions comparing to master

Scenario Current PR Ticks Baseline Ticks Ratio Regression Analysis
Button.Fluent 478 452 1.06:1 analysis
AttachmentMinimalPerf.default 138 132 1.05:1 analysis
CarouselMinimalPerf.default 547 526 1.04:1 analysis
Perf comparison
Status Scenario Fluent TPI Fabric TPI Ratio Iterations Ticks
🎯 Avatar.Fluent 0.44 0.45 0.98:1 2000 877
🦄 Button.Fluent 0.1 0.17 0.59:1 5000 478
🔧 Checkbox.Fluent 0.63 0.33 1.91:1 1000 627
🔧 Dialog.Fluent 0.32 0.19 1.68:1 5000 1602
🔧 Dropdown.Fluent 3.12 0.4 7.8:1 1000 3116
🔧 Icon.Fluent 0.13 0.04 3.25:1 5000 639
🎯 Image.Fluent 0.07 0.09 0.78:1 5000 335
🔧 Slider.Fluent 1.35 0.31 4.35:1 1000 1349
🔧 Text.Fluent 0.06 0.02 3:1 5000 321
🦄 Tooltip.Fluent 0.08 15.53 0.01:1 5000 408

🔧 Needs work     🎯 On target     🦄 Amazing

Perf tests with no regressions
Scenario Current PR Ticks Baseline Ticks Ratio
TextMinimalPerf.default 340 303 1.12:1
AvatarMinimalPerf.default 466 439 1.06:1
VideoMinimalPerf.default 575 547 1.05:1
ChatDuplicateMessagesPerf.default 397 382 1.04:1
LayoutMinimalPerf.default 529 509 1.04:1
ListMinimalPerf.default 442 424 1.04:1
TableMinimalPerf.default 362 347 1.04:1
Slider.Fluent 1349 1301 1.04:1
GridMinimalPerf.default 593 578 1.03:1
ProviderMinimalPerf.default 613 595 1.03:1
SegmentMinimalPerf.default 865 841 1.03:1
ButtonSlotsPerf.default 540 528 1.02:1
CheckboxMinimalPerf.default 2736 2671 1.02:1
DividerMinimalPerf.default 643 631 1.02:1
DropdownManyItemsPerf.default 1254 1234 1.02:1
HierarchicalTreeMinimalPerf.default 921 902 1.02:1
MenuMinimalPerf.default 1774 1736 1.02:1
MenuButtonMinimalPerf.default 1476 1444 1.02:1
PopupMinimalPerf.default 250 245 1.02:1
TooltipMinimalPerf.default 694 680 1.02:1
TreeMinimalPerf.default 1127 1110 1.02:1
Image.Fluent 335 329 1.02:1
Text.Fluent 321 315 1.02:1
DropdownMinimalPerf.default 3039 3015 1.01:1
InputMinimalPerf.default 947 941 1.01:1
ReactionMinimalPerf.default 1784 1765 1.01:1
SliderMinimalPerf.default 1343 1334 1.01:1
StatusMinimalPerf.default 607 600 1.01:1
IconMinimalPerf.default 617 612 1.01:1
CustomToolbarPrototype.default 3455 3427 1.01:1
Checkbox.Fluent 627 619 1.01:1
ChatWithPopoverPerf.default 530 529 1:1
EmbedMinimalPerf.default 3943 3926 1:1
ImageMinimalPerf.default 345 345 1:1
ListWith60ListItems.default 1080 1078 1:1
LoaderMinimalPerf.default 743 744 1:1
SplitButtonMinimalPerf.default 3143 3137 1:1
TextAreaMinimalPerf.default 415 413 1:1
Dropdown.Fluent 3116 3119 1:1
Icon.Fluent 639 637 1:1
AnimationMinimalPerf.default 566 574 0.99:1
BoxMinimalPerf.default 280 284 0.99:1
ButtonMinimalPerf.default 146 147 0.99:1
CardMinimalPerf.default 482 485 0.99:1
DialogMinimalPerf.default 1544 1563 0.99:1
ItemLayoutMinimalPerf.default 1513 1521 0.99:1
ProviderMergeThemesPerf.default 1561 1582 0.99:1
AccordionMinimalPerf.default 173 177 0.98:1
AttachmentSlotsPerf.default 1030 1046 0.98:1
FormMinimalPerf.default 636 651 0.98:1
HeaderMinimalPerf.default 431 440 0.98:1
ListCommonPerf.default 903 922 0.98:1
ToolbarMinimalPerf.default 934 949 0.98:1
Dialog.Fluent 1602 1630 0.98:1
Tooltip.Fluent 408 417 0.98:1
ListNestedPerf.default 831 854 0.97:1
RadioGroupMinimalPerf.default 515 529 0.97:1
Avatar.Fluent 877 902 0.97:1
ChatMinimalPerf.default 539 560 0.96:1
HeaderSlotsPerf.default 1321 1370 0.96:1
LabelMinimalPerf.default 353 371 0.95:1
FlexMinimalPerf.default 238 254 0.94:1
TreeWith60ListItems.default 201 213 0.94:1
PortalMinimalPerf.default 269 290 0.93:1
RefMinimalPerf.default 186 201 0.93:1
AlertMinimalPerf.default 258 282 0.91:1

return slotPropsChain.reduce(
(acc, slotProps) => ({
...acc,
...(typeof slotProps[slot] === 'function' ? slotProps[slot](props) : slotProps[slot] || {}),
Copy link
Member

Choose a reason for hiding this comment

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

Why this is not a part of compose?

Copy link
Member

Choose a reason for hiding this comment

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

I mean the whole function

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you mean we should have as part of composeOption the getSlotProps function, so we can just use composeOptions.getSlotProps('content') for example?

Copy link
Member

Choose a reason for hiding this comment

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

Yep. Or even composeOptions.slotProps.content as I don't understand why we need getSlotProps() as function: to component we pass resolved options already

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We do not have the props inside compose, that is the reason why I initially made it a separate hook. If we return some function from compose, I think we will need to still invoke it with the properties inside the component. Am I missing something?

Copy link
Member

Choose a reason for hiding this comment

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

It's a different as in useStyles we are using mapPropsToStyles in useStyles options. But here as functions in chain are available at composeOptions

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok let me try to see if everything will work as expected then... I am going to update slotProps to be mapPropsToSlots: (props) => ({ content: {} }) and try to remove the useSlotProps hook

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just to conclude, after offline discussions we decided to change API of compose to mapPropsToSlots: (props) => ({ content: {} }), but still use a hook for getting the slot props, as for some components we will need both props + state for resolving the slot props.

Copy link
Member

@dzearing dzearing Apr 24, 2020

Choose a reason for hiding this comment

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

My preference would be in compose: keep slots and slotProps as names. Either of these could be and static object map, or a function taking in state and returning the object map. It keeps the consumer experience super clean:

compose(Button, {
  slots: {
     icon: MyContainer
  },
  slotProps: {
     icon: { data-is-foo: 'bar'
  }
}

We should consider moving mapPropsToStyles to something maybe higher level to be layered on demand: it is a css-in-js ism specifically for caching so as we move to static

I think a function like mergeProps would be responsible for deriving the final slots and slotProps to be distributed. It would need to be called after state has been reconciled. It could manage resolving things like slots/slotProps and classes functions which all should take in state to resolve their stuff.

This means that compose simply glues together the options and passes them along, while the mergeProps helper could finalize the results for consumption in the template.

Copy link
Contributor Author

@mnajdova mnajdova Apr 27, 2020

Choose a reason for hiding this comment

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

We already have mapPropsToStyleProps, that's why I decided in the end to go with mapPropsToSlotPros. If we decide to change the API of the compose we can rename those easily to just stylesProps and slotProps, but for now I would rather keep the names similar...

@layershifter
Copy link
Member

Why have you decided to go with slotProps instead of mapPropsToSlots?

mapPropsToSlots: (props) => ({ content: {} })

just asking

@mnajdova
Copy link
Contributor Author

Why have you decided to go with slotProps instead of mapPropsToSlots?

mapPropsToSlots: (props) => ({ content: {} })

just asking

@layershifter actually this looks much closer to the other mappings in compose already.. 👍

@mnajdova mnajdova changed the title feat(compose): add slots and slotProps feat(compose): add slots and mapPropsToSlotProps Apr 27, 2020
@mnajdova mnajdova merged commit 79c2112 into microsoft:master Apr 28, 2020
@msft-github-bot
Copy link
Contributor

🎉@fluentui/react-compose@v0.2.10 has been released which incorporates this pull request.:tada:

Handy links:

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.

None yet

6 participants