Prevent Material-UI InputLabel from moving to the upper left of Select component
Asked Answered
G

2

5

No matter what I try, I can't seem to get true Placeholder functionality for the Material-UI Select component. Using a placeholder prop on <Select /> did not work, and it looks like it's either not passing that prop to the input, or it's not displaying it somehow.

Reading through the Material-UI demo code for the Select component, it appears they achieve Placeholder functionality by using <InputLabel />. They have this cheeky little animation that moves it to the upper left of the input. Ok cool, it's a nice effect. But it doesn't fit with the design of our site and I'd like to disable that. Unfortunately, the disableAnimation prop just causes the label to jump to the upper left, not to disappear entirely.

What I'd like is for my dropdown component to say "Select Item" as a placeholder text, then when the menu is open, that text should be gone. It only returns if the user clicks out of the dropdown without selecting anything. If they select a value, the value should replace the placeholder and "Select Item" shouldn't appear anywhere.

(NOTE: I had this working fine when using react-select, but I need to use Material-UI's ripple effect, so I tried overriding their MenuList and MenuItem components with Material UI's, but it passed all the props down to the DOM elements and threw a bunch of errors. So I went back to the drawing board and decided to use the entire Material UI Select component.)

Current code:

const Dropdown = ({options, ...props}) => {
  const [selected, setSelected] = useState('');

  const testOptions = [
  { value: 'chocolate', label: 'Chocolate' },
  { value: 'strawberry', label: 'Strawberry' },
  { value: 'vanilla', label: 'Vanilla' },
];

  const handleChange = (selected) => {
    setSelected(selected);
    console.log('is anything happening')
  }

  options = options || testOptions;

  const menuItems = options.map((option, index) => (
      <StyledMenuItem
        key={index}
        value={option.value}
        label={option.label}
      />
    ));

  return (
    <FormControl hiddenLabel>
      <InputLabel disableAnimation>Select Item</InputLabel>
      <StyledSelect
        value={selected}
        onChange={handleChange}

        variant="outlined"
        disableUnderline={true}
      >
        <MenuItem value="">
          <em>Select Item</em>
        </MenuItem>
        {menuItems}
      </StyledSelect>
    </FormControl>
  )
};


const StyledMenuItem = styled(MenuItem)`
  min-height: 32px;
  height: 32px;
  padding: 0 12px;
  font-family: 'Work Sans', sans-serif;
  font-weight: 400;
  font-size: 17px;
  line-height: 20px;
  color: ${colors.primary800};
  outline: none;

  &:hover {
    background-color: ${colors.primary100};
  }

  & .MuiTouchRipple-root {
    color: ${colors.primary050};
  }
`

const StyledSelect = styled(Select)`

  input::-webkit-contacts-auto-fill-button,
    input::-webkit-credentials-auto-fill-button {
        display: none !important;
}
    border: 1px solid ${colors.primary400};
    border-radius: 2px;
    height: 40px;
    box-shadow: none;
    outline: none;

  & .MuiSelect-icon {
    fill: ${colors.primary300};
    width: 36px;
    height: 36px;
    top: inherit;
  }
`
Grochow answered 30/8, 2019 at 0:2 Comment(1)
``` lang-js <FormControl> <InputLabel htmlFor='selectItem'>Select Item</InputLabel> <Select ... inputProps={{ id: 'selectItem' }} ... /> </FormControl> ``` binding id to htmlFor should solve the shrink issueLaureate
V
15

I couldn't find a really clean way, but the following seems to do the trick:

<InputLabel shrink={false}>
    {selected !== '' && 'Select item'}
</InputLabel>

Adding the shrink={false} makes sure the label doesn't move up when focussed. With the default Material-UI styling the options will be over the InputLabel, so you won't see it when selecting. Then, when a value is selected the selected variable will be set and the text will hide from the label.

If the select items don't appear over the InputLabel because of your custom styling you can maybe track the focus with onFocus and onBlur to hide the label content when the select is focussed.

Viewy answered 30/8, 2019 at 9:52 Comment(4)
I'm doing something similar and I'm using your method. It works with shrink={false}, the label doesn't move when I click on the dropdown. However, it is not disappearing, it remains there and the selected option too, both of them are visible. Here is my code: pastebin.com/caYK7r7U . Could you help me with what's wrong here, please?Ballottement
shrink={true} on InputLabel helped for me for NativeSelect, final one is <InputLabel shrink={true}>Select A Role</InputLabel>Abuttal
shrink{false} on the InputLabel component was exactly what I needed, thank you so much for the great advice.Adamek
How we can handle the accessibility with this solution? I mean, "" is not a good label.Solander
S
0

Wrapping the MenuItem component inside a TextField component with a select property instead of wrapping instead a Select component will do the job. Hope this helps.

<TextField select name="categoryName" fullWidth label="Select Category">
  <MenuItem value={10}>Ten</MenuItem>
  <MenuItem value={20}>Twenty</MenuItem>
  <MenuItem value={30}>Thirty</MenuItem>
</TextField>
Schwejda answered 8/6, 2023 at 18:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.