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

Experimental v2 component framework #335

Merged
merged 35 commits into from
Jul 28, 2020
Merged

Experimental v2 component framework #335

merged 35 commits into from
Jul 28, 2020

Conversation

JasonVMo
Copy link
Contributor

Platforms Impacted

None and all. This change should be looked at like a proposal. It is an alternate component framework that is smaller, lighter, hopefully easier to use and understand, and potentially a bit better aligned with where web is going. Feedback is welcome and because it is an experimental section of code it can be iterated on after merge as well.

Description of changes

This reworks the component building system, in particular the foundation-composable, foundation-tokens, themed-settings, most of foundation-settings, and foundation-compose packages. It then creates four new packages, which provide a parallel experimental way of building components.

It also includes reworking Button, Text, and Stack to use the new framework. The packages are as follows:

@fluentui-react-native/use-styling

This is used almost identically to the useStyling function passed into usePrepareProps, this module creates a generic way of building an opinionated styling hook and puts it in its own package. This has a number of changes from the way the current system is set up:

  • Instead of specifying full settings, with tokens and slotProps merged together, this now only includes tokens. This makes things a bit simpler and creates a smaller support surface area. It also makes things more prescriptive.
  • Instead of having a collection called _overrides with the precedence array in the settings, the layers/states are just directly included in the tokens, and the precedence becomes a direct setting for the component.
  • Formal way to declare which props are tokens in the options
  • options.styles got renamed to options.slotProps, this feels more correct given that is what they are.
  • slotProps get specified with objects or style functions, no more magic arrays

I did write a doc file for this one included in this PR. It can also be found at: https://github.com/JasonVMo/fluentui-react-native/tree/experimental-framework/packages/experimental/use-styling

'@fluentui-react-native/use-slots`

This reworks the slots mechanism into a more focused and reusable package. I don't have a README.md written for this yet but it effectively looks like:

buildUseSlots

const useSlots = buildUseSlots({ slots: { root: View, content: Text }, filters: { content: filterTextProps }});

const MyComp = props => {
  const { root: Root, content: Content } = useSlots(props);
  return (
    <Root {...props}>
      <Content />
    </Root>
  );
}

This can attach styling, or not, all parameters to useSlots get passed to the styling hook, if set, then those properties bubble back out.

stagedComponent

The other thing included in here is a new pattern for splitting hooks and rendering. This is necessary for tree compression and is the reason why the framework today has usePrepareProps / render. The issue with the current pattern is that it is awkward to pass information from usePrepareProps to render, the whole IRenderData and state are expressly for that purpose. It also is a bit too tied to the framework. Instead we can solve the same problem by writing components as follows:

const MyComp = (props: MyPropsType) => {
  const Slots = useSlots(props);
  const { transformed, state } = useSomeFancyFunctionality(props);

  return (additional: MyPropsType, ...children: React.ReactNode[]) => {
    <Slots.root {...mergeProps(props, additional)}>
      <Slots.content disabled={state.disabled}>{children}</Slots.content>
    </Slots.root>
  }
}

stagedComponent wraps any function of this form into a traditional component while also exposing an attached reference to the original staged component for aware frameworks.

'@fluentui-react-native/composition`

This package effectively implements a generic compose called composeFactory and adds some of the type extraction goodness that the previous compose used. This is pretty small and is primarily a packager for the other two implementations.

`@fluentui-react-native/experimental-framework'

This rolls the packages above together, and adds the opinion on how theming works. For generics parameterized on the type of the theme this package re-exports them to make them opinionated.

Components (Button, Text, Stack)

This is probably the best place to look for how the new code functions in practice. Existing tests have been ported and run against the previous snapshots.

Verification

(how the change was tested, including both manual and automated tests)

Before After
Screenshot or description before this change Screenshot or description with this change

Pull request checklist

This PR has considered (when applicable):

  • Automated Tests
  • Documentation and examples
  • Keyboard Accessibility
  • Voiceover
  • Internationalization and Right-to-left Layouts

@JasonVMo
Copy link
Contributor Author

For comparison bundle sizes for the relevant portions of the frameworks look like:

  • New stuff: use-styling (2.14 KB parsed, 0.86 KB zipped), use-slots (2.15 / 0.78), composition (0.82 / 0.45), experimental-framework (1.05 / 0.39), Total: 6.16 KB parsed, 2.47 KB zipped
  • Old stuff: Total: ~10.2 KB parsed, 3.95 KB zipped

color?: string;

/** alternate mode for disabled look and feel */
disabled?: TextTokens;
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm unsure what this is for. Is it used to define a style for disabled? I'm confused why TextTokens is used in the definition of TextTokens.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, this is to define a style for disabled. The reason it is recursive (as a pattern) is that if there are multiple states they each can contain the other ones. (This way you can do hovered + pressed, or primary + disabled, if those are states you have.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is equivalent to what before was:

settings: {
  _overrides: {
    disabled : {
      tokens: {
        // same content here
      }
    }
  }

}),
buttonName
],
states: ['hovered', 'focused', 'pressed', 'disabled'],
Copy link
Contributor

Choose a reason for hiding this comment

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

This replaces __overrides, right? How does this change for nested states (focused + pressed or whatever)

Copy link
Contributor

@FalseLobster FalseLobster Jul 17, 2020

Choose a reason for hiding this comment

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

I just saw your response to Krystal elsewhere, this is still recursive

Copy link
Contributor

@ksiler ksiler left a comment

Choose a reason for hiding this comment

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

These changes look good to me. I think it definitely simplifies everything and makes component creation easier to understand. Great work!

@JasonVMo JasonVMo merged commit 585007a into microsoft:master Jul 28, 2020
@JasonVMo JasonVMo deleted the experimental-framework branch July 28, 2020 18:13
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.

3 participants