Skip to content

Conversation

@emplums
Copy link

@emplums emplums commented Nov 19, 2018

This PR is to test out different private API prototypes. This is very WIP and probably won't ship.

I'd like to test the following:

... more to come

Baseline Success Criteria:

  • Must be able to extend a component within another component (ex. styled(Box))
  • Must use styled-system
  • Can use is prop in the public API to easily change rendered tag
  • No system props carried over as HTML attributes
  • Must provide a clean, enjoyable developer experience
  • Must be able to use themeGet or something similar to use values from a custom provided theme

Nice to have:

  • Doesn't create any breaking changes to the public API

This works, but props related to CSS are not getting removed from the rendered HTML
@vercel
Copy link

vercel bot commented Nov 19, 2018

This pull request is automatically deployed with Now.
To access deployments, click Details below or on the icon next to each push.

@emplums emplums changed the title [WIP WIP WIP WIP WIP] Private API prototypes [RFC] Private API prototypes Nov 19, 2018
@colebemis
Copy link
Contributor

How would we expose system props on "composite" components like Dropdown without withSystemProps? https://github.com/primer/components/blob/034c19ca1a6406de09ff56518fd4541adb2ebf86/src/Dropdown.js#L13-L52

@emplums
Copy link
Author

emplums commented Nov 19, 2018

@colebemis in this situation, instead of calling withSystemProps(Dropdown you'd replace that line with:

const styledDropdown = styled(Dropdown)`
${space}
${color}
`

export default styledDropdown

(probably with better naming though 😂 )

borderColor: colors.gray[2],
borderRadius: theme.radii[1],
theme // set our theme to the default if no theme is provided
}
Copy link
Contributor

Choose a reason for hiding this comment

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

These default props won't update if the consumer is using a custom theme, right? Is that a problem?

Copy link
Contributor

Choose a reason for hiding this comment

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

Not as written, no. I think we still have to figure out what the "right" way is to do a default theme prop, if that's even a thing. 🙁

Copy link
Author

@emplums emplums Nov 20, 2018

Choose a reason for hiding this comment

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

@shawnbot this is setting the default theme here only if the user doesn't provide one, or are you talking about something else?

@colebemis - yeah, these values won't use the provided theme value it always defaults to our theme values... though I'm not sure if that's a huge problem or not? IMO the default values should be set to values from the Primer theme, and can always be overridden if you'd like to provide your own theme by providing a value for that prop. cc @broccolini

Copy link
Contributor

Choose a reason for hiding this comment

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

@emplums I think the problem with this approach is that themeGet() doesn't work with it — but I could be wrong, and maybe it will magically work the way I expect it to with styled-components? IIRC, this wasn't working last I checked:

const Blue = styled.div`
  background-color: ${themeGet('colors.blue.4')}
`

Blue.defaultProps = {theme}

// <Blue /> → no background-color

Last I remember looking at this, I suspected it was an issue with the theme not being available in context as opposed to props, but I might be totally off base. 🤔

Copy link
Author

@emplums emplums Nov 20, 2018

Choose a reason for hiding this comment

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

Ah right we'd have to do it like this if we want to use themeGet since props aren't available outside the context of the component: 7e1c2c1

Doesn't feel too bad though?

Copy link
Author

Choose a reason for hiding this comment

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

fwiw this isn't working currently in primer/components anyways 😂

Copy link
Author

Choose a reason for hiding this comment

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

I discovered something interesting... the reason this isn't working in primer/components (more accurately... the docs for primer/components) is because the theme prop for every component rendered in the docs is being overridden by the theme provided to Layout from mdx-docs... so the default is never set in any component because there is already a theme prop provided 🤦‍♀️

Two take-aways from this:

  1. We should really have some sort of clean-ish dev environment so we aren't deciphering between primer/components issues and issues due to clashes with our docs stack 😂

  2. I'd be keen to pair with you @shawnbot tomorrow to pull mdx-docs out and use your system for primer.style/design so that we're on the same page between repos and don't need to worry about theme clashes with Layout.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yay, nice detective work! I think this is a simple fix: we can just use an <MDXProvider> in place of the Layout. Let me know if that works, and we can pair if need be!

Copy link

@mannybecerra mannybecerra Nov 24, 2018

Choose a reason for hiding this comment

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

Ah right we'd have to do it like this if we want to use themeGet since props aren't available outside the context of the component: 7e1c2c1

Doesn't feel too bad though?

@emplums and team, I read through this RFC PR, dig it. I apologize if you arrived at a conclusion or answer already regarding the themeGet utility, however, in the off chance that you're still exploring approaches, I have found that you can still leverage themeGet successfully in your custom Primer component when the theme provider is not defined, but the default theme prop is, as follows (ala styled-components)

...
  themeGet
} from 'styled-system'
import theme from './theme'
...
border: ${props => themeGet('borders.1'), theme.borders[1])(props)};
...
Box.defaultProps = {
  theme
}
...

Copy link
Author

Choose a reason for hiding this comment

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

@mannybecerra ah yes! We found a bug that was preventing the defaultProps from kicking in, which is why we were having issues with themeGet. We've resolved that and now it should work swimmingly! 🙌

${FLEX_CONTAINER}
${COMMON}
${display}
`
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason we use template literal syntax instead of passing the system prop functions as arguments?

const Flex = styled.div`
  ${FLEX_CONTAINER}
  ${COMMON}
  ${display}
`

// vs

const Flex = styled.div(FLEX_CONTAINER, COMMON, display)

Copy link
Contributor

Choose a reason for hiding this comment

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

I'll be honest, I prefer the latter form for components that only use system props. However, I think there's a lot of value in consistency, and if folks need to add additional styles, it should be obvious how to do that. So leaving all of the components using tagged template literals might be good for that.

OTOH: in theory, there's a performance penalty for using template literals when we don't need them. Maybe we could compile those away with a babel plugin or something? 🤔

Copy link
Author

Choose a reason for hiding this comment

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

@colebemis @shawnbot hmm yeah honestly the only reason I left the syntax like that was because I'd previously had some CSS in there 😂 I like having consistent syntax but if there's a performance penalty then I don't think doing const Flex = styled.div(FLEX_CONTAINER, COMMON, display) is too confusing of a departure from the tagged template literal syntax

Copy link
Contributor

@mxstbr mxstbr Nov 28, 2018

Choose a reason for hiding this comment

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

Chiming in here 👋 since I know a little bit about styled-components 🙈: our Babel plugin automatically optimizes tagged template literals under the hood for perf. For example, by default babel-preset-env (or whatever the relevant Babel plugin is that's used under the hood) would turn this component:

const Simple = styled.div`
  width: 100%;
`

into this JS:

var _templateObject = _taggedTemplateLiteral(['width: 100%;'], ['width: 100%;'])
function _taggedTemplateLiteral(strings, raw) {
  return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } }))
}
var Simple = _styledComponents2.default.div(_templateObject)

which is done to be 100% spec-compliant, most of which is 100% irrelevant for writing styled components.

Our Babel plugin pre-empts that transformation and instead turns it into this much faster and smaller version ✨:

var Simple = _styledComponents2.default.div(['width: 100%;'])

You can read more about that transformation in the docs, but TL;DR: if you use the Babel plugin it's not a problem since the two forms are basically identical perf-wise, if not then... use the Babel plugin! 😉

@emplums
Copy link
Author

emplums commented Dec 3, 2018

Closing this since I've started the refactor in #368

@emplums emplums closed this Dec 5, 2018
@emplums emplums deleted the prototypes branch January 25, 2019 20:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants