Search code examples
reactjsmaterial-uistyled-components

Give diferent className to MenuProps and Parent Select Componens with styled-components


I'm trying to customize a MUI TextField Select component with styled components.

The ideia is styled-compoents provide diferent classes to Select field and Menu, so i can have their styled separated.

const StyledSelect = styled(({ className, ...props }) => {
    return (
        <TextField {...props}
            classes={{ root: className }}
            SelectProps={{
                MenuProps: {
                    classes: { paper: className, list: className },
                    anchorOrigin: {
                        vertical: "bottom",
                        horizontal: "left"
                    },
                    transformOrigin: {
                        vertical: "top",
                        horizontal: "left"
                    },
                    getContentAnchorEl: null
                },
            }}
        />
    )
})`
    & {
      background-color: #454D5D;
      border-radius: 10px;
      margin-top: 5px;
    }
    & li {
        color: #FFF;
    }
    &.MuiFormControl-root {
        background-color: transparent;
    }
    & .MuiListItem-root {
        font-size: 18px;
    }
    & .MuiListItem-root.Mui-selected {
        background-color: #1A2233;
    }
    & .MuiFormLabel-root {
        font-family: 'Roboto';
        font-weight: 300;
    }
    & .MuiInputLabel-shrink {
        color: ${props => props.color};
        font-weight: normal;
    }
    & .MuiInput-underline:after {
        border-bottom: 2px solid ${props => props.errors[props.field.name] && props.touched[props.field.name]
        ? CASABLANCA : props.color};
        transition: none;
        transform: none;
    }
    & .MuiInput-underline:before {
        border-bottom: 1px solid ${props => props.color}
    }
    & .MuiSelect-roo {
        color: black;
        font-family: 'Roboto';
        font-weight: 300;
    }  
    & .MuiSelect-select:focus {
        background: transparent;
    }
  `;

I wish my TextField class would be different from MenuProps class


Solution

  • One way to solve this is to have one wrapper component per class name you need to generate. In my example below, StyledTextField takes care of the root class name for TextField (the className property is equivalent to classes.root) and then MenuPaperClass provides an additional class name.

    import React from "react";
    import ReactDOM from "react-dom";
    
    import TextField from "@material-ui/core/TextField";
    import MenuItem from "@material-ui/core/MenuItem";
    import styled from "styled-components";
    
    const StyledTextField = styled(TextField)`
      /* && to add specificity */
      && {
        border: 1px solid green;
      }
    `;
    
    const MenuPaperClass = styled(({ className, ...props }) => {
      return (
        <StyledTextField
          SelectProps={{ MenuProps: { classes: { paper: className } } }}
          value="1"
          select
          {...props}
        >
          <MenuItem value="1">One</MenuItem>
          <MenuItem value="2">Two</MenuItem>
          <MenuItem value="3">Three</MenuItem>
        </StyledTextField>
      );
    })`
      && {
        background-color: lightblue;
      }
    `;
    function App() {
      return (
        <div className="App">
          <MenuPaperClass />
        </div>
      );
    }
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    

    Edit styled-components multiple classes

    This isn't a particularly elegant solution and gets pretty tedious if you have 3 or more separate classes you want to use, so I may come back to this later to consider alternative approaches/syntax, but this does work.