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

Foundation: Component Creation #5721

Closed
2 of 9 tasks
JasonGore opened this issue Jul 30, 2018 · 3 comments · Fixed by #6474, #5867, #5799 or #5693
Closed
2 of 9 tasks

Foundation: Component Creation #5721

JasonGore opened this issue Jul 30, 2018 · 3 comments · Fixed by #6474, #5867, #5799 or #5693

Comments

@JasonGore
Copy link
Member

JasonGore commented Jul 30, 2018

Component Details

There is a need to create a foundation package that improves and extends the existing styled functionality that OUFR provides. This new Foundation package has the following goals:

Goal Rationale Goal Currently Met Status
Centralize style processing and merging. Reduce error-prone component boilerplate of using, merging and applying theming and styling.
Create separation of concerns for improved maintenance and scalability: state, view and styling. Improve ease of testability, maintenance, reusability and scalability for component implementations.
Introduce optional state component. State handles statefulness and passing transformed props and state to view. State components do not generate JSX and simply call a view function in their render function. State does not need to be concerned with using or managing styling props. Unit tests can be concerned with testing statefulness without worrying about rendered output.
Automatic processing and merging of styles. Foundation handles passing component props and state component output through styling and passing resulting styled output to view.
Introduce core view component. Views are pure functions whose input are all props. Since views do not have state their unit test coverage simply becomes snapshot tests covering prop permutations. Components without states are effectively stateless components where styling is automatically applied to component props.
Centralize common controlled state patterns, like toggle. Reduce the need for components to duplicate common state logic patterns. Improve reliability of controlled vs. uncontrolled behavior.
Maintain backwards compatibility. For existing components and for consumers who wish to continue creating combined state/view components, continue to offer existing styled implementation in Foundation package. Resolution: Continue to support styled separately until deprecated.
Be agnostic of component library and styling approach and impose as minimal structure on styling and component surface as possible. Do not depend on any other OUFR package. Allow adoption by component libraries other than OUFR.
Eliminate state component layer if at all possible, leaving simply composed component and theme consuming layers. Maximize scalability and perf. State component layer ultimately not eliminated. See #5799 for rationale.

Design Assets

createComponent

createComponent is the core function for Foundation, consuming the view, state and styles objects to create an HOC that handles all styling and props processing.

image

Arguments

Name Type Description
options IComponentOptions See IComponentOptions below.
providers IStyleProviders Functions needed by Foundation to process and merge style sets.
StateComponent IStateComponent Optional state component with additional props provided by Foundation, such as renderView prop.

IComponentOptions

Name Type Description
displayName string Display name to identify component in React hierarchy.
styles IStylesProp<TViewProps, TStyleSet> Styles prop to pass into component.
view (props: IViewComponentProps<TViewProps, TProcessedStyledSet>) => JSX.Element Functional React view component.
statics TStatics Optional static object to pass into constructed component.

Types

Types are used to eliminate dependencies on specific styling library, allowing Foundation to have no other OUFR dependencies. This requires the consuming component library to provide these types.

Type Description
TComponentProps A styleable props interface for the created component. This is the API surface for the created component.
TViewProps The props specific to the view, including processed properties outputted by optional state component. If state component is not provided, TComponentProps is the same as TViewProps.
TStyleSet The type for styles properties.
TProcessedStyleSet The type provided by mergeStyleSets provider after processing TStyleSet and provided to views.
TTheme The type for contextual and prop themes.

Component Library Interface

It is recommended that the component library provide an interface file that passes in global types (such as OUFR's ITheme and IProccessedStyleSet) to Foundation so that the interfaces for components are simplified. For example:

export type IViewProps<TProps, TStyleSet extends IStyleSet<TStyleSet>> = IViewProps<TProps, IProcessedStyleSet<TStyleSet>>;
export type IStyleableComponent<TProps, TStyleSet> = IStyleableComponent<TProps, TStyleSet, ITheme>;

Imports

As one of the primary goals of Foundation is to be agnostic of styling and component libraries, Foundation should have no other OUFR dependencies.

Exports/ Component Breakdown

Intended Package

@uifabric/foundation

Code mockup/example

The API approach is similar to styled, the main differences being the explicit callout of state and view arguments.

Code Sample

import { createComponent } from '../../Foundation';
import { CollapsibleSectionView } from './CollapsibleSection.view';
import { CollapsibleSectionState } from './CollapsibleSection.state';
import { getStyles as styles } from './CollapsibleSection.styles';
import {
  ICollapsibleSectionProps,
  ICollapsibleSectionControlledProps,
  ICollapsibleSectionViewProps,
  ICollapsibleSectionStyles
} from './CollapsibleSection.types';

export const CollapsibleSection: React.StatelessComponent<ICollapsibleSectionProps> = createComponent<
  ICollapsibleSectionProps,
  ICollapsibleSectionViewProps,
  ICollapsibleSectionStyles
>(
  {
    displayName: 'CollapsibleSection',
    view: CollapsibleSectionView,
    styles
  },
  CollapsibleSectionState
);

Component Ownership

This will be a core Fabric package with initial implementation and support provided by the core Fabric team (main POC @JasonGore.)

Deadlines

TODOs

TODO Complete PR
Is it possible to combine state objects within HOC component to eliminate a separate StateComponent level in the React hierarchy? #5799
Chainable state / autocontrolled patterns. Can we have predefined autocontrolled patterns like 'toggle' so that such state components don't have to be duplicated for each component? #5799
A pattern for making controlled vs. uncontrolled components. Avoid duplicating sources of truth when consuming component provides controlled props. Minimize logic required to get source of truth with core components ideally not storing state at all. Potential ideas include a controlled state object that is the source of truth for when consumer does not define a value for controlled props. TextField as potential conversion example (default props vs. controlled props) #5799
Add support for contextual theming and remove getTheme provider. #5867
Resolve (requiring new TS ver) or find potential workarounds for TypeScript-related issues: Having to export unused vars (can't export unimported external types.) Needing two implementations of createComponent for type safety (need conditional types.) Superfluous types (love to be able to have components extend types so that users don't need to define as many types.) #6474
Update create-component script and New Component wiki. #7569
Support React 16 context. (See #5701) #6268
Consolidate all copies of createComponent into Foundation. #5693
Add type safety and formalize state/view relationship and typing as well as props merging and handling #5693

Steps

  • is themable (base/styles)
  • has unit tests
  • has snapshot tests
  • has visual tests
  • has documentation and examples
  • Design Review
  • API review
  • Public Preview
  • Ready for Publishing
@kenotron
Copy link
Member

In the example above, is import { CollapsibleSectionState } from './CollapsibleSection.state'; a StateComponent?

@JasonGore
Copy link
Member Author

Yep. It's implemented in experiments and handles the collapsed state based on defaultCollapsed and user input from the view.

@JasonGore
Copy link
Member Author

I've pulled out incomplete items into separate issues so will close this issue.

@microsoft microsoft locked as resolved and limited conversation to collaborators Aug 30, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.