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

Best pattern for components of unknown type? #291

Closed
joshwcomeau opened this issue Sep 2, 2017 · 8 comments
Closed

Best pattern for components of unknown type? #291

joshwcomeau opened this issue Sep 2, 2017 · 8 comments

Comments

@joshwcomeau
Copy link

joshwcomeau commented Sep 2, 2017

In many apps, I wind up creating a "Button" component. Crucially, this element accepts a "domType" prop, so that it can either render a <button> or an <a>.

My first pass at doing this in emotion looked like this:

const buttonStyles = css`
  background: blue;
  color: white;
`;

const ButtonElem = styled.button`
  composes: ${buttonStyles};
`;

const LinkButtonElem = styled.button`
  composes: ${buttonStyles};
`;

The problem, though, is that sometimes the properties depend on props. Having them defined in css means they can't be variable, so I wind up duplicating a bunch of logic:

const buttonStyles = css`
  color: white;
`;

const ButtonElem = styled.button`
  composes: ${buttonStyles};
  background: ${props => props.background};
`;

const LinkButtonElem = styled.button`
  composes: ${buttonStyles};
  background: ${props => props.background};
`;

I just realized that a better pattern is to create element factories:

const generateElem = elem => styled(elem)`
  background: ${props => props.background};
  border-bottom: 5px solid ${props => props.borderColor};
`;

const ButtonElem = generateElem('button');
const LinkButtonElem = generateElem('a');

I'm curious, though, if this is indeed the optimal pattern? Is there another way to do this, that I'm not aware of?

Happy to contribute a solution to the docs, depending on what the solution is :)

Thanks for all your great work on this project, really enjoying using Emotion.

@tkh44
Copy link
Member

tkh44 commented Sep 2, 2017

Great question.

  1. Because of recent changes in emotion 7 you don't even need the composes.
  2. Your partial styles can be a function that takes in props and returns a css`` wrapped style block.
const buttonStyles = (props) => css`
  color: ${props.color};
  background: ${props.background};
`;

const ButtonElem = styled.button`
  ${buttonStyles};
`;

@tkh44
Copy link
Member

tkh44 commented Sep 2, 2017

I'm going to close this but feel free to ask more in this thread.

@tkh44 tkh44 closed this as completed Sep 2, 2017
@mrmartineau
Copy link
Contributor

mrmartineau commented Sep 19, 2017

@tkh44 I am trying to do something similar to what you're suggesting but it is not working..

const ButtonCSS = (props) => css`
  display: ${props => props.block ? 'block' : 'inline-block'};
  width: ${props => props.block ? '100%' : 'auto'};
  vertical-align: middle;
  white-space: nowrap;
  font-family: inherit;
  font-size: 100%;
  cursor: pointer;
  border: none;
  margin: 0;
  padding-top: 0;
  padding-bottom: 0;
  line-height: 2.5;
  height: 2.5em;
  padding-right: 1.5em;
  padding-left: 1.5em;
  overflow: visible;
  text-align: center;
  border-radius: 5px;
  user-select: none;
  text-decoration: none;
  transition: all 300ms cubic-bezier(0.77, 0, 0.175, 1); /* Var */

  &[disabled] {
    cursor: default;
    background-image: none;
    opacity: .5;
  }
`

Can you see why, if a block prop is passed to the button component that uses these styles, the styles do not get modified? The Button component that consumes these styles is below:

const ButtonStyles = css`
  ${ButtonCSS};

  &,
  &:link,
  &:visited {
    background-color: #EDEDED; /* Var */
    color: #444; /* Var */
  }

  &:hover,
  &:active,
  &:focus {
    background-color: #D5D5D5; /* Var */
    outline: none;
  }
`
<Button block>foo bar</Button>

What am I missing?

@joshwcomeau
Copy link
Author

@mrmartineau so there was a slight typo in the original message. You actually want to do this:

const ButtonCSS = (props) => css`
  display: ${props.block ? 'block' : 'inline-block'};
  width: ${props.block ? '100%' : 'auto'};

You don't need to give each property a function, because the props are already given as a function :)

@mrmartineau
Copy link
Contributor

@joshwcomeau thanks! it seems there are so many patterns when working with emotion that I've found by searching the issues. It would be great to collect these and add them to the docs.. cc @tkh44

@tkh44
Copy link
Member

tkh44 commented Sep 19, 2017

Oh man, my bad! Sorry about that @mrmartineau. I'll edit the original post so anyone else that finds this will get the right info.

@tkh44
Copy link
Member

tkh44 commented Sep 19, 2017

@mrmartineau would you mind reviewing this https://github.com/emotion-js/emotion/pull/299/files#diff-fe5dc5e956fa21363a9babe4424d9236? If this is missing any composition patterns could you please make a note in as a comment. Thanks.

@mrmartineau
Copy link
Contributor

@tkh44 no worries, it works as expected now. I'll review the PR now.

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

No branches or pull requests

3 participants