viewstools / morph

Views Tools morpher
1 stars 0 forks source link

feat: morph/react add support for design tokens #255

Closed alex-vladut closed 3 years ago

alex-vladut commented 3 years ago

With these change support was added for design tokens. In order the make use of the design tokens an application should be wrapped by the ThemeDesignTokensProvider like this:

  import design_tokens from 'DesignSystem/design-tokens.json'

  <ThemeDesignTokensProvider
    theme="light"
    design_tokens={design_tokens}
  >
    ...
  </ThemeDesignTokensProvider>

Let me know if you think this approach is not right or there is a better way and we can discuss.

Here are a couple of cases supported with this approach:

Simple reference to a design token:

  backgroundColor dt.colors.primary

morphed as:

let dtColorsPrimary = fromData.useDesignTokenValue({ viewPath, path: 'colors.primary' })
...
 style={{'--backgroundColor': dtColorsPrimary}}
...

Reference to a design token and allow to override through component props:

  backgroundColor < dt.colors.primary

morphed as:

... // other props
backgroundColor
}) {
let dtColorsPrimary = fromData.useDesignTokenValue({ viewPath, path: 'colors.primary' })
...
 style={{'--backgroundColor':  backgroundColor || dtColorsPrimary}}
...

With when conditions:

  backgroundColor dt.colors.primary
  when <isMediaMobile
  backgroundColor dt.colors.secondary

will be morphed to:

  let isMedia = useIsMedia()
  let dtColorsPrimary = fromData.useDesignTokenValue({viewPath, path: 'colors.primary' })
  let dtColorsSecondary = fromData.useDesignTokenValue({ viewPath, path: 'colors.secondary' })
...
 style={{'--backgroundColor': isMedia.mobile ? dtColorsSecondary : dtColorsPrimary}}
...

With when conditions and override through component props:

  backgroundColor < dt.colors.primary
  when <isMediaMobile
  backgroundColor <secondaryBackgroundColor dt.colors.secondary

will be morphed to:

backgroundColor,
secondaryBackgroundColor
}) {
  let isMedia = useIsMedia()
  let dtColorsPrimary = fromData.useDesignTokenValue({ viewPath, path: 'colors.primary' })
  let dtColorsSecondary = fromData.useDesignTokenValue({ viewPath, path: 'colors.secondary' })
...
 style={{'--backgroundColor': isMedia.mobile ? secondaryBackgroundColor || dtColorsSecondary : backgroundColor || dtColorsPrimary}}
...

In this case it will take into account first the prop and, if not specified, will default to the design token value.

neil-buckley commented 3 years ago

What happens if the path specified to the token in the object doesn't exist?

alex-vladut commented 3 years ago

The property passed to then CSS will be undefined, I think this is what the data providers return when the key doesn't exist. In that case I think it shouldn't throw an error, but most likely the property will be ignored. Would you think we should rather throw an error in that case or maybe at least show a warning for the developers to fix it?

dariocravero commented 3 years ago

Amazing work @alex-vladut!! πŸ‘ πŸ‘ πŸš€ !!!

Regarding the DT files, what about putting them into their own Views/DesignTokens.js file so we keep them separate from the data implementation?

On its internals, I have two suggestions, let me know what you think about them:

E.g.:

import { DataProvider, useDataValue } from 'Views/Data.js'
import get from 'lodash/get'
import tokens from 'DesignSystem/tokens.json'
import React from 'react'

let defaultTheme = Object.keys(tokens)[0]

export function DesignTokens({ theme = defaultTheme, children, viewPath }) {
  return (
    <DataProvider
      context="design_tokens_theme"
      value={theme}
      viewPath={viewPath}
    >
      {children}
    </DataProvider>
  )
}

export function useDesignTokenValue({ path, viewPath }) {
  let theme = useDataValue({
    context: 'design_tokens_theme',
    viewPath,
  })

  return get(tokens, `${theme}.${path}`)
}
alex-vladut commented 3 years ago

Your suggestion makes sense to me, I will give it a try and implement it like this πŸ‘