Search code examples
reactjsstyled-components

styled component default props values are not getting picked up


styled component

const Button = styled.button`
  width: 88px;
  height: 36px;
  background:${(props) => buttonTheme[props.buttonTheme].backgroundValue}; 
  box-shadow:${(props) => buttonTheme[props.buttonTheme].boxShadowValue};
  border:${(props) => buttonTheme[props.buttonTheme].borderValue} ;
  border-radius: 6px;
  margin: 5px 0;

`;

Default values

Button.defaultProps = {
  buttonTheme: "defaultButtonTheme"
};

const buttonTheme = {
  defaultButtonTheme: {
    borderValue: "0",
    boxShadowValue: "0",
    backgroundValue: "0"
  },
  defaultButton: {
    backgroundValue: "#E0E0E0",
    boxShadowValue: "0px 2px 3px rgba(51, 51, 51, 0.2)",
    color: "#3F3F3F"
  },
  hoverFocusType1Button: {
    backgroundValue: "#AEAEAE",
    boxShadowValue: "0px 2px 3px rgba(51, 51, 51, 0.2)",
    color: "#3F3F3F"
  },
  OutlineVariantButton: {
    borderValue: "1px solid #3D5AFE",
    color: "#3D5AFE"
  },
  hoverFocusType2Button: {
    backgroundValue: "rgba(41, 98, 255, 0.1)",
    borderValue: "1px solid #3D5AFE",
    color: "#3D5AFE"
  },
  textVariantButton: {
    color: "#3D5AFE"
  },
  hoverFocusType3Button: {
    backgroundValue: "rgba(41, 98, 255, 0.1)",
    color: "#3D5AFE"
  },
}

Let's say I return JSX

<div>
        <Button buttonTheme="defaultButton">Default</Button>
</div>

My expectation is which ever values are missing in this prop, it should pick up from default but it's not working as expected.

e.g. In above case I expect

borderValue: "0",

But I still see border to this button and not sure from where is it coming ?


Solution

  • You are not getting what you want because defaultButtonTheme is not being merged at any time with the other themes.

    To accomplish what you want, you can do:

    1. Create two objects theme, one with the default values and another one with the themes:

    const defaultButtonTheme = {
      borderValue: "0",
      boxShadowValue: "0",
      backgroundValue: "0",
    };
    
    const buttonTheme = {
      defaultButton: {
        backgroundValue: "#E0E0E0",
        boxShadowValue: "0px 2px 3px rgba(51, 51, 51, 0.2)",
        color: "#3F3F3F",
      },
      hoverFocusType1Button: {
        backgroundValue: "#AEAEAE",
        boxShadowValue: "0px 2px 3px rgba(51, 51, 51, 0.2)",
        color: "#3F3F3F",
      },
      OutlineVariantButton: {
        borderValue: "1px solid #3D5AFE",
        color: "#3D5AFE",
      },
      hoverFocusType2Button: {
        backgroundValue: "rgba(41, 98, 255, 0.1)",
        borderValue: "1px solid #3D5AFE",
        color: "#3D5AFE",
      },
      textVariantButton: {
        color: "#3D5AFE",
      },
      hoverFocusType3Button: {
        backgroundValue: "rgba(41, 98, 255, 0.1)",
        color: "#3D5AFE",
      },
    };
    

    2. Change your styled button to:

    const Button = styled.button`
      width: 88px;
      height: 36px;
      border-radius: 6px;
      margin: 5px 0;
    
      ${(props) => {
        const defaultThemeConcatenated = Object.assign(
          {},
          defaultButtonTheme,
          buttonTheme[props.buttonTheme]
        );
    
        return css`
          background: ${defaultThemeConcatenated.backgroundValue};
          box-shadow: ${defaultThemeConcatenated.boxShadowValue};
          border: ${defaultThemeConcatenated.borderValue};
        `;
      }}
    `;
    

    Now we are concatenating the informed theme with the default theme, and any properties that the informed theme doesn't have, it will get from default theme.

    And as you seen, i'm using css, so do don´t forget to import it too.

    import styled, { css } from "styled-components";
    

    In this case, i included return css to let it clear that i'm returning some styles from this part of code and than, it will set the theme color of my vscode to this lines.

    You can do like:

    return `
          background: ${defaultThemeConcatenated.backgroundValue};
          box-shadow: ${defaultThemeConcatenated.boxShadowValue};
          border: ${defaultThemeConcatenated.borderValue};
        `;
    

    But in this case, background, box-shadow and border will not be set with the theme color that i like. It's more like a convention.

    Here's a code snippet to check the code working.

    const defaultButtonTheme = {
      borderValue: "0",
      boxShadowValue: "0",
      backgroundValue: "0",
    };
    
    const buttonTheme = {
      defaultButton: {
        backgroundValue: "#E0E0E0",
        boxShadowValue: "0px 2px 3px rgba(51, 51, 51, 0.2)",
        color: "#3F3F3F",
      },
      hoverFocusType1Button: {
        backgroundValue: "#AEAEAE",
        boxShadowValue: "0px 2px 3px rgba(51, 51, 51, 0.2)",
        color: "#3F3F3F",
      },
      OutlineVariantButton: {
        borderValue: "1px solid #3D5AFE",
        color: "#3D5AFE",
      },
      hoverFocusType2Button: {
        backgroundValue: "rgba(41, 98, 255, 0.1)",
        borderValue: "1px solid #3D5AFE",
        color: "#3D5AFE",
      },
      textVariantButton: {
        color: "#3D5AFE",
      },
      hoverFocusType3Button: {
        backgroundValue: "rgba(41, 98, 255, 0.1)",
        color: "#3D5AFE",
      },
    };
    
    const Button = window.styled.button`
      width: 88px;
      height: 36px;
      border-radius: 6px;
      margin: 5px 0;
    
      ${(props) => {
        const defaultThemeConcatenated = Object.assign(
          {},
          defaultButtonTheme,
          buttonTheme[props.buttonTheme]
        );
    
        return window.styled.css`
          background: ${defaultThemeConcatenated.backgroundValue};
          box-shadow: ${defaultThemeConcatenated.boxShadowValue};
          border: ${defaultThemeConcatenated.borderValue};
        `;
      }}
    `;
    
    const App = () => {
      return (
     <div>
      <Button buttonTheme="defaultButton">Default</Button>
      <Button buttonTheme="hoverFocusType1Button">Default</Button>
      <Button buttonTheme="OutlineVariantButton">Default</Button>
      <Button buttonTheme="textVariantButton">Default</Button>
      <Button buttonTheme="hoverFocusType3Button">Default</Button>
     </div>     
      );
    };
    
    ReactDOM.render(
      <React.StrictMode>
        <App />
      </React.StrictMode>,
      document.getElementById('root')
    );
    div {
     display: flex;
     flex-direction:column; 
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/[email protected]/umd/react-is.production.min.jss"></script>
    <script src="//unpkg.com/[email protected]/dist/styled-components.min.js"></script>
    <div id="root"></div>