3

I'm building an app with the Material UI library for ReactJS. Using the Theme Overrides API, I'm trying to figure out how I can globally style a component, but only when it is a child of another specific component.

For example, I'm trying to set the background/text coloring of MenuItems inside a <Select> menu, where each <MenuItem> contains a <listItemText>. Here's my component:

import { MenuItem, Select, ListItemText } from '@material-ui/core';
import { MuiThemeProvider } from '@material-ui/core/styles';
import * as React from 'react';
import theme from './theme';

const MySelect = props => {
    return (
        <MuiThemeProvider theme={theme}>
            <Select variant="standard" value="2" open>
                <MenuItem value="1">
                    <ListItemText>One</ListItemText>
                </MenuItem>
                <MenuItem value="2">
                    <ListItemText>Two</ListItemText>
                </MenuItem>
                <MenuItem value="3">
                    <ListItemText>Three</ListItemText>
                </MenuItem>
                <MenuItem value="4">
                    <ListItemText>Four</ListItemText>
                </MenuItem>
            </Select>
        </MuiThemeProvider>
    );
};

export default MySelect;

Unfortunately, applying a color to the <MenuItem> directly doesn't work because the <ListItemText> overrides it with a <Typography> that has its own coloring set. This is fine for a non-hovered, non-selected state, but if I style the "selected" MenuItem to have a darker background, I need it to have a lighter text.

MUI select dropdown with darkened selected item and dark text

Here is my theme file:

import { createMuiTheme, createStyles } from '@material-ui/core';

const myTheme = createMuiTheme({
    overrides: {
        MuiMenuItem: createStyles({
            root: {
                '&&:hover': {
                    backgroundColor: 'pink',
                    color: 'white'
                }
            },
            selected: {
                '&&': {
                    backgroundColor: 'blue',
                    color: 'white'
                },
                '&&:hover': {
                    backgroundColor: 'darkblue',
                    color: 'white'
                }
            }
        }),

        // How do I enforce this ONLY inside of MuiMenuItem and only for
        // the selected variant of that?
        MuiTypography: {
            subheading: {
                color: 'white'
            }
        }
    }
});

export default myTheme;

So, my question is: is there a way to do this using just Theme Overrides? Or do I need to conditionally pass this styling into the <ListItemText> component using props? Since most of the styling here fits nicely into Theme Overrides, that seems like a nicer way to do it, but maybe I'm misusing the API.

For a working demo of my above code, see: https://codesandbox.io/s/3r9mkxq231

Any insight is welcome! Thank you!

Olivier Tassinari
  • 8,238
  • 4
  • 23
  • 23
dwat
  • 312
  • 2
  • 6
  • 14

3 Answers3

6

One way to accomplish that is to target the descendant html element (e.g. the span for the ListItemText) from the ancestor styles (MenuItem in this case).

Here's an example of how the MenuItem.selected style could be specified:

  selected: {
    "&&": {
      backgroundColor: "blue",
      color: "white",
      "&& span": {
        color: "white"
      }
    },
    "&&:hover": {
      backgroundColor: "darkblue",
      color: "white"
    }
  }

The full code (forked from your CodeSandbox) is here:

Edit MUI How to: Override Tab Theme

Ryan Cogswell
  • 75,046
  • 9
  • 218
  • 198
  • Is there a way to do it without targeting HTML elements in the selector (`& span`)? It feels pretty brittle to just target the sub-element via an element selector, rather than a semantic class selector. But I have not found a way to get it to work with a class selector. Any ideas? – Jason Frank Feb 22 '19 at 19:08
  • 1
    Actually I found the answer. You have to use a `$` in the selector, as shown in this answer: https://stackoverflow.com/a/53773194/718325 – Jason Frank Feb 22 '19 at 19:17
0

First of all, I don't think we can do that in the theme overrides. Theme overrides is a way to override a default style configuration of an existing material-ui component.

Second, I don't think you need to make it too complex with conditional statements. This can be solved without that also. I didn't understand why did you need to use <ListItemText> when <MenuItem> itself has functionality to display text.
Just simply remove <ListItemText> from you code and then you can use theme overrides to modify your <MenuItem> as you like.

Find the modified code here : https://codesandbox.io/s/30p3o4jjz5

Let me know if this clarifies your doubt.

Mr Lister
  • 45,515
  • 15
  • 108
  • 150
Arpit
  • 438
  • 1
  • 6
  • 15
0

Yes you can do this in theme overrides using jss-nested syntax:

const myTheme = createMuiTheme({
  overrides: {
    MuiMenuItem: createStyles({
      root: {
        "&&:hover": {
          backgroundColor: "pink",
          "& *": {
            color: "white"
          }
        }
      },
      selected: {
        "&&": {
          backgroundColor: "blue",
          "& *": {
            color: "white"
          }
        },
        "&&:hover": {
          backgroundColor: "darkblue",
          "& *": {
            color: "white"
          }
        }
      }
    })
  }
});

export default myTheme;

See working codepen here: https://codesandbox.io/embed/308mk7k5x6?fontsize=14

Meli
  • 467
  • 4
  • 10