Skip to content
This repository has been archived by the owner on Mar 4, 2020. It is now read-only.

feat(Provider): Make Provider a UIComponent #852

Merged
merged 24 commits into from
Mar 26, 2019

Conversation

miroslavstastny
Copy link
Member

@miroslavstastny miroslavstastny commented Feb 6, 2019

BREAKING CHANGE

<Provider> component now renders a <div> element with dir attribute and styles setting background and color CSS attributes.

Fixes #586.

Change description

<Provider> component now creates a <div> element.

Provider can now contain multiple children

Before

<Provider theme={themes.teamsDark}>
  <> <!-- React.Fragment or other explicit wrapping element required -->
    <p>Text inside p tag inside dark theme</p>
    <Button content="Stardust button inside dark theme"  />
  </>
</Provider>

After

<Provider theme={themes.teamsDark}>
  <p>Text inside p tag inside dark theme</p>
  <Button content="Stardust button inside dark theme"  />
</Provider>

Theme colors (bodyBackground and bodyColor) are correctly applied for all children

Code

<Provider theme={themes.teamsDark}>
  <p>Text inside p tag inside dark theme</p>
  <Button content="Stardust button inside dark theme"  />
</Provider>

Before
image

After
image

There is also dir attribute correctly set on the div so RTL works without any additional changes:

Before

<div dir="rtl"> <!-- this div must be explicitly added for RTL to work -->
  <Provider theme={{rtl: true}}>
  </Provider>
</div>

After

<Provider theme={{rtl: true}}> <!-- Provider renders div with dir attribute set -->
</Provider>

If rtl is not defined in the nested Provider, it is now inherited from the outer provider

<Provider theme={{rtl: true}}> <!-- Provider renders div with dir attribute set -->
  <Provider theme={themes.teamsDark}> <!-- Rtl attribute not set, div is rendered without dir attribute, RTL is inherited from parent -->
  </Provider>
</Provider>

It is possible to override variables and styles for the ProviderBox

<Provider variables={{background: 'red'}} styles={{borderBottom: '3px solid blue'}} theme={mergeThemes(themes.teamsDark, {
  siteVariables: {
    bodyBackground: 'salmon'
  },
  componentVariables: {
    ProviderBox: {
      color: 'yellow',
    }
  },
  componentStyles: {
    ProviderBox: {
      root: {
        borderTop: '3px solid green'
      }
    }
  }
})}>
  <p>with custom overrides</p>
</Provider>

After
image

Known limitations

Docsite displays div created by the example-wrapping Provider as part of the Rendered HTML.

@codecov
Copy link

codecov bot commented Feb 6, 2019

Codecov Report

Merging #852 into master will increase coverage by 0.02%.
The diff coverage is 92.59%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #852      +/-   ##
==========================================
+ Coverage   82.08%   82.11%   +0.02%     
==========================================
  Files         713      716       +3     
  Lines        8536     8559      +23     
  Branches     1161     1230      +69     
==========================================
+ Hits         7007     7028      +21     
- Misses       1513     1515       +2     
  Partials       16       16
Impacted Files Coverage Δ
packages/react/src/lib/renderComponent.tsx 92.1% <ø> (+0.1%) ⬆️
packages/react/src/lib/mergeThemes.ts 100% <100%> (ø) ⬆️
packages/react/src/themes/base/componentStyles.ts 100% <100%> (ø) ⬆️
...s/base/components/Provider/providerBoxVariables.ts 100% <100%> (ø)
...ckages/react/src/themes/base/componentVariables.ts 100% <100%> (ø) ⬆️
...ackages/react/src/components/Provider/Provider.tsx 96.77% <100%> (+0.34%) ⬆️
...emes/base/components/Provider/providerBoxStyles.ts 50% <50%> (ø)
...ages/react/src/components/Provider/ProviderBox.tsx 90% <90%> (ø)
... and 2 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e62ce21...7b0c3ec. Read the comment docs.

Copy link
Member

@layershifter layershifter left a comment

Choose a reason for hiding this comment

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

1. ProviderBox

image

<Provider as='div' theme={theme}>

This the most confusing part to me. Why we need this? I tried to use styles directly on Provider and use as=div and it works like a charm 🌸

Copy link
Member

@layershifter layershifter left a comment

Choose a reason for hiding this comment

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

2. changes in mergeRTL()

This is a second point of my confusion.

<Provider theme={{}} />
  <Provider theme={{}} />  // rtl: false
// ---
<Provider theme={{}} />
  <Provider theme={{ rtl: true }} /> // rtl: true
// ---
<Provider theme={{ rtl: true }} />
  <Provider theme={{}} /> // rtl: true

Why it should be undefined? It will add dir='ltr' to the root node, but why this is not correct?
Fela uses direction prop on theme, robinweser/fela#673. I think that we should do the same thing instead of rtl: boolean.

@miroslavstastny
Copy link
Member Author

  1. changes in mergeRTL()

The original code works for merging themes in Provider, that's correct. Where it breaks is this code:

teamsTheme = mergeThemes(base, {/*...*/})

RTL is defined neither in base theme nor in teams overrides but with the original code mergeThemes() results in rtl: false. And when you use this theme in nested provider it force-overwrites rtl to false and that's not what you want.

@miroslavstastny
Copy link
Member Author

miroslavstastny commented Mar 5, 2019

  1. ProviderBox

The reason to add ProviderBox was to make the div created by Provider themable. With this approach you can:

  1. style the div by the theme passed as a prop to the Provider,
  2. override the theme variables for this single div (without affecting div styles of any nested Provider) using <Provider variables={{...}} ...>,
  3. override styles for this single div using <Provider styles={{...}} ...>.

The reason why we cannot do that directly in Provider is that Fela classes are computed in renderComponent function before Provider.renderComponent() is called but the new theme is merged in Provider.renderComponent().

As discussed offline with @layershifter the main problems of the approach taken are:

  • Provider should be as lightweight as possible,
  • users can (accidentally) misuse as to do something like <Provider as={Menu} ... />,
  • having a ProviderBox component together with Box component is confusing.

Alternative proposal (@layershifter) is:

  • change Provider back to be a regular component, not UIComponent
    • that removes as = no confusion or misuse
  • add nodiv (working name, not final) prop to Provider to add possibility to opt out of creating the div
  • do not pass any props not handled by Provider to ProviderBox - this makes it impossible to override variables and styles for the ProviderBox when creating a Provider but when that is required, user can use noDiv and create her own div
  • as a next step try to remove ProviderBox component and:
    • recompute Fela classes directly in Provider.renderComponent() after the themes are merged
    • create div directly in Provider.renderComponent(). Not ElementType as Provider is not UIComponent but hardcoded div (again, if that does not work for you, you can go with nodivand create whatever element inside the Provider manually).

@levithomason
Copy link
Member

First shipment should be as minimal as possible. We won't advertise opting out of the styled div until we get requests. Even then, the initial workaround suggestion will be as={React.Fragment}. However, let's remove it from the breaking change notice for now.

@@ -19,6 +19,7 @@ class App extends React.Component<any, ThemeContextData> {
return (
<ThemeContext.Provider value={this.state}>
<Provider
as={React.Fragment}
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 is temporary hotfix for Docsite when switching themes for examples. We should fix theme switching to apply to examples only, not to the whole docsite.

<div
dir={theme.rtl ? 'rtl' : undefined}
style={{
color: siteVariables.bodyColor,
Copy link
Member Author

Choose a reason for hiding this comment

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

Reverted #1050, removed unnecessary div.

@miroslavstastny miroslavstastny merged commit 9e33e06 into master Mar 26, 2019
@delete-merged-branch delete-merged-branch bot deleted the feat/provider-as-ui-component branch March 26, 2019 10:50
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants