9

I need to customize the material-ui autocomplete control so it's not so tall. I found a sample that works well to change the MuiOutlinedInput outline color using createMuiTheme on the Autocomplete's TextField. The codesandbox is here: Code Example

This is my theme override code and I added an override for the padding of the input class:

const theme = createMuiTheme({
  overrides: {
    MuiOutlinedInput: {
      root: {
        "& $notchedOutline": {
          borderColor: "green"
        },
        "&:hover $notchedOutline": {
          borderColor: "red"
        },
        "&$focused $notchedOutline": {
          borderColor: "purple"
        },
        "& $input": {
          padding: "1px"
        }
      }
    }
  }
});

And this is the component code:

 <Autocomplete
  id="country-select-demo"
  style={{ width: 300 }}
  options={countries}
  getOptionLabel={option => option.label}
  renderInput={params => (
    <MuiThemeProvider theme={theme}>
      <TextField {...params} label={"Countries"} variant="outlined" />
    </MuiThemeProvider>
  )}
/>

The problem is my padding for the input class gets overridden by this:

.MuiAutocomplete-inputRoot[class*="MuiOutlinedInput-root"] .MuiAutocomplete-input

Anything I can do to make my customization not get overridden by the above? I'm also open to other techniques other than createMuiTheme, or perhaps styling other parts of the autocomplete. I just need to make it not so tall.

Thanks!

cakidnyc
  • 379
  • 1
  • 4
  • 13

1 Answers1

16

This is a matter of getting the appropriate CSS specificity for your override. One easy way to increase the specificity is to repeat a class.

In the example below, I use &&& $input which is equivalent to having .MuiOutlinedInput-root.MuiOutlinedInput-root.MuiOutlinedInput-root .MuiOutlinedInput-input:

const theme = createTheme({
  overrides: {
    MuiInputLabel: {
      outlined: {
        transform: "translate(14px, 12.5px) scale(1)"
      }
    },
    MuiOutlinedInput: {
      root: {
        "& $notchedOutline": {
          borderColor: "green"
        },
        "&:hover $notchedOutline": {
          borderColor: "red"
        },
        "&$focused $notchedOutline": {
          borderColor: "purple"
        },
        "&&& $input": {
          padding: "1px"
        }
      }
    }
  }
});

Edit Increase override specificity

Another option which is a bit uglier, but makes it more obvious which default styling you are overriding is the following:

const theme = createTheme({
  overrides: {
    MuiInputLabel: {
      outlined: {
        ".MuiAutocomplete-root &:not(.MuiInputLabel-shrink)": {
          transform: "translate(14px, 12.5px) scale(1)"
        }
      }
    },
    MuiOutlinedInput: {
      root: {
        "& $notchedOutline": {
          borderColor: "green"
        },
        "&:hover $notchedOutline": {
          borderColor: "red"
        },
        "&$focused $notchedOutline": {
          borderColor: "purple"
        }
      }
    },
    MuiAutocomplete: {
      inputRoot: {
        '&&[class*="MuiOutlinedInput-root"] $input': {
          padding: 1
        }
      }
    }
  }
});

Edit Increase override specificity (forked)

Notice that you still need to double the & in order to get specificity that wins over the default styles. This version is specific to auto complete rather than impacting all outlined inputs.

If you don't want to apply this override to the whole application in your theme, you can just customize the Autocomplete component using withStyles like the following:

import React from "react";
import TextField from "@material-ui/core/TextField";
import Autocomplete from "@material-ui/lab/Autocomplete";
import { withStyles } from "@material-ui/core/styles";

const NoPaddingAutocomplete = withStyles({
  inputRoot: {
    '&&[class*="MuiOutlinedInput-root"] $input': {
      padding: 1
    },
    "& .MuiOutlinedInput-notchedOutline": {
      borderColor: "green"
    },
    "&:hover .MuiOutlinedInput-notchedOutline": {
      borderColor: "red"
    },
    "&.Mui-focused .MuiOutlinedInput-notchedOutline": {
      borderColor: "purple"
    }
  },
  input: {}
})(Autocomplete);
export default function CountrySelect() {
  return (
    <NoPaddingAutocomplete
      id="country-select-demo"
      style={{ width: 300 }}
      options={countries}
      getOptionLabel={option => option.label}
      renderInput={params => (
        <TextField {...params} label={"Countries"} variant="outlined" />
      )}
    />
  );
}

Edit Increase override specificity

Related answer:

Related documentation:

Ryan Cogswell
  • 75,046
  • 9
  • 218
  • 198
  • That worked, thanks! Not very intuitive, but it will do for now. – cakidnyc Mar 04 '20 at 19:26
  • 1
    @cakidnyc I added a couple alternatives to my answer. – Ryan Cogswell Mar 04 '20 at 19:50
  • 1
    Thanks. These are really helpful! – cakidnyc Mar 06 '20 at 15:13
  • I decided to go with the "withStyles" approach, however, I now can't figure out how to also style the borders. Is it possible, or do I have to use the MuiTheme approach for borders? – cakidnyc Mar 09 '20 at 15:36
  • @cakidnyc There isn't anything that you can do via the theme that you can't also do using `withStyles`. See my answer [here](https://stackoverflow.com/questions/58984406/setting-text-color-outline-and-padding-on-material-ui-autocomplete-component/58985002#58985002) for styling the outline. – Ryan Cogswell Mar 09 '20 at 15:59
  • @cakidnyc I've also now updated my example here to include the outline customizations. – Ryan Cogswell Mar 09 '20 at 16:36
  • 1
    Thanks! Reading your other post really helped. Especially the link for the autocomplete css api, that helps reveal a lot of the mystery. – cakidnyc Mar 09 '20 at 18:53
  • Just noticed there is this warning in the console in your 3rd example. Any idea why? Warning: [JSS] Could not find the referenced rule "input" in "WithStyles(ForwardRef(Autocomplete))". in WithStyles(WithStyles(ForwardRef(Autocomplete))) (at demo.js:25) in CountrySelect (at index.js:6) – cakidnyc Mar 09 '20 at 19:20
  • 1
    @cakidnyc 3rd example warning is fixed now. In theme overrides you can reference the different CSS classes of the component (such as $input) without explicitly defining them within the overrides, but for `withStyles`/`makeStyles` you need to define any classes (e.g. `input: {}`) that you reference. The only reason it still worked is because it fell back to replacing it with just `input` meaning it matched the elements with the `` tag which happened to still target the desired element. – Ryan Cogswell Mar 09 '20 at 19:58
  • Nice one, but unfortunately not working with multiple select Autocomplete (e.g. add the "multiple" attribute). It ignores the override and always sets the padding to the default 9px. – Rui Miguel Pinheiro Aug 13 '20 at 16:41
  • @RuiMiguelPinheiro I don't see any difference when I add `multiple`: https://codesandbox.io/s/increase-override-specificity-forked-f7y86?file=/demo.js. – Ryan Cogswell Aug 13 '20 at 16:52
  • Changing the padding in your code seems to afect only the inner input field, not the "temp" label written over the input when there is nothing selected. – Rui Miguel Pinheiro Sep 05 '20 at 13:49
  • Thanks @RyanCogswell, ```MuiAutocomplete: { inputRoot: { '&&[class*="MuiOutlinedInput-root"] $input': { padding: 1 } } }``` worked for my use case, but I want to know, what double `&&` is doing? – Hari Kishore Jan 12 '21 at 11:30
  • 1
    @kishore I've added the relevant JSS documentation link to my answer. `&` resolves to the class generated for the parent rule (`.MuiAutocomplete-inputRoot` in this case). Doubling it (`&&`) just repeats that class name (e.g. `.MuiAutocomplete-inputRoot.MuiAutocomplete-inputRoot`) which increases the [specificity](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity) of the rule. – Ryan Cogswell Jan 12 '21 at 15:13