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

Adding Component Attribute into Styled Components #29492

Open
consciousnessdev opened this issue Nov 3, 2021 · 11 comments
Open

Adding Component Attribute into Styled Components #29492

consciousnessdev opened this issue Nov 3, 2021 · 11 comments
Labels
support: question Community support but can be turned into an improvement

Comments

@consciousnessdev
Copy link

consciousnessdev commented Nov 3, 2021

Before, i use styling with styledlike this

const ComponentStyled = styled(Grid)`
    background-color: red;
`

component used as

...
<ComponentStyled>
  {* Content *}
</ComponentStyled>
...

but how if i want to styling from this form

<Grid item md={2}>
  {* Content *}
</Grid>

in to styled version with passed md value or 'item' attribute?

@consciousnessdev consciousnessdev added the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Nov 3, 2021
@siriwatknp
Copy link
Member

You can directly pass the available props in the host component (Grid) to the styled component.

const ComponentStyled = styled(Grid)`
    background-color: red;
`

<ComponentStyled item md={2}>
  {* Content *}
</ComponentStyled>

I hope this answer your question, feel free to close this issue if that works for you.

@siriwatknp siriwatknp added support: question Community support but can be turned into an improvement and removed status: waiting for maintainer These issues haven't been looked at yet by a maintainer labels Nov 4, 2021
@soujvnunes
Copy link

Yeah @siriwatknp. He can use the attrs function and pass this props directly on the constructor to make it cleaner or every instance to have them:

const StyledGrid = styled(Grid).attrs({
    item: true,
    md: 2,
})`
    background-color: red;
`

<StyledGrid />

@Roy412
Copy link

Roy412 commented Nov 5, 2021

attrs is not working with the styled from mui V5.

import { styled } from '@mui/material/styles'

const StyledGrid = styled(Grid).attrs({  <<<<<----  error here: attrs does not exist
      item: true,
      md: 2,
})`
     background-color: red;
`

<StyledGrid />

@siriwatknp
Copy link
Member

attrs is not working with the styled from mui V5.

import { styled } from '@mui/material/styles'

const StyledGrid = styled(Grid).attrs({  <<<<<----  error here: attrs does not exist
      item: true,
      md: 2,
})`
     background-color: red;
`

<StyledGrid />

attrs is only available in styled-components. We don't support it yet. cc @mnajdova

@mnajdova
Copy link
Member

I don't see a big value of adding this API. @emotion doesn't support it - emotion-js/emotion#821 It can be easily added in userland, for example - emotion-js/emotion#821 (comment)

@AriaMoradi
Copy link

AriaMoradi commented Dec 13, 2021

I had a similar issue with setting props on Virtuoso from https://virtuoso.dev/

Let's say I want to make a Virtuoso component that behaves in a certain way by default, and have an internal API, that's not possible without a similar api like attr

const StyledVirtuoso = styled(Virtuoso)((({ theme }) => ({
    listStyle: 'none',
    padding: 0,
    minHeight: '200px',
    [theme.breakpoints.up('md')]: {
        width: '50vw',
        height: 'calc(100vh - 64px)',
        margin: 0,
    },
})));
                <StyledVirtuoso
                    style={{ // override Virtuoso default values and set them with class
                        height: 'undefined',
                        overflowY: window.innerWidth < 900 ? 'visible' : 'auto',
                    }}
                    totalCount={items.length}
                    itemContent={(index:number) => (
                        <ItemRow
                            item={item[index]}
                        />
                    )}
                    useWindowScroll={window.innerWidth < 900}
                    overscan={window.innerHeight * 0.5}
                />

What I want:

const StyledVirtuoso = styled(Virtuoso)((({ theme }) => ({
    listStyle: 'none',
    padding: 0,
    minHeight: '200px',
    [theme.breakpoints.up('md')]: {
        width: '50vw',
        height: 'calc(100vh - 64px)',
        margin: 0,
    },
})))
.attr({
    style: { // override Virtuoso default values and set them with class
        height: 'undefined',
        overflowY: window.innerWidth < 900 ? 'visible' : 'auto',
    },
    useWindowScroll: window.innerWidth < 900,
    overscan: window.innerHeight * 0.5
})
;
                <StyledVirtuoso
                    totalCount={items.length}
                    itemContent={(index:number) => (
                        <ItemRow
                            item={item[index]}
                        />
                    )}
                />

@mnajdova
Copy link
Member

You can do that with different API, for example:

const withAttrs = (Component, attrs) => props => <Component {...attrs} {...props}/>;

const BasicStyledVirtuoso = styled(Virtuoso)((({ theme }) => ({
  listStyle: 'none',
  padding: 0,
  minHeight: '200px',
  [theme.breakpoints.up('md')]: {
    width: '50vw',
    height: 'calc(100vh - 64px)',
    margin: 0,
  },
})));

const StyledVirtuoso = withAttrs(BasicStyledVirtuoso, {
  style: { // override Virtuoso default values and set them with class
      height: 'undefined',
      overflowY: window.innerWidth < 900 ? 'visible' : 'auto',
  },
  useWindowScroll: window.innerWidth < 900,
  overscan: window.innerHeight * 0.5
});

// And then use it

<StyledVirtuoso
  totalCount={items.length}
  itemContent={(index:number) => (
    <ItemRow
      item={item[index]}
    />
  )}
/>

@d4rky-pl
Copy link

d4rky-pl commented Jan 12, 2022

For those of us that do "see a big value of adding this API" and like to keep decorative props with the rest of the styles where they belong:

import React from "react"
import { styled as muiStyled } from "@mui/material/styles"

const styled = (StyledComponent, ...args) => {
  const Component = muiStyled(StyledComponent, ...args)
  Component.attrs = (defaultProps) =>
    muiStyled(
      React.memo((props) => <StyledComponent {...defaultProps(props)} {...props} />),
      ...args
    )
  return Component
}

export * from "@mui/material/styles"
export default styled

There's an updated version below ⬇️

@arunmmanoharan
Copy link

const withAttrs = (Component, attrs) => props => <Component {...attrs} {...props}/>;

Nice. Where should this file sit?

@d4rky-pl
Copy link

While working on my side project I've realized I'm not mixing the theme in the props passed to attrs so for those coming here from Google, here's an updated version:

import React from "react"
import { styled as muiStyled, useTheme } from "@mui/material/styles"

const styled = (StyledComponent, ...args) => {
  const Component = muiStyled(StyledComponent, ...args)
  Component.attrs = (defaultProps) =>
    muiStyled(
      React.memo((props) => {
        const theme = useTheme()
        return <StyledComponent {...defaultProps({ theme, ...props })} {...props} />
      }),
      ...args
    )
  return Component
}

export * from "@mui/material/styles"
export default styled

@KrasserTommy
Copy link

KrasserTommy commented Mar 14, 2024

I am currently using a solution from the Storybook team to use an alternative to attrs with Emotion.

This also works very well:

export const Paragraph = styled((props: Omit<React.ComponentProps<typeof Typography>, "variant">) => (
    <Typography variant="body1" {...props} />
))`
    margin: 12px 0;
    padding-right: 24px;
`;

But if I use the styled() function from mui, I get a TypeScript error when I set the components prop:

const Header = styled(
    (props: Omit<React.ComponentProps<typeof ListSubheader>, "component" | "disableGutters" | "disableSticky'">) => (
        <ListSubheader component="div" disableGutters disableSticky {...props} />
    ),
)`
    line-height: 1em;
    color: black;
`;

The only solution at the moment is not to use typing, but I would like to avoid that:

const Header = styled(
    (props) => <ListSubheader component="div" disableGutters disableSticky {...props} />
)`
    line-height: 1em;
    color: black;
`;

Does anyone here have an idea for a solution?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
support: question Community support but can be turned into an improvement
Projects
None yet
Development

No branches or pull requests

9 participants