Search code examples
reactjsmaterial-uifocus

Material UI - closing modal leaves focus state on button that opened it


Let's say I have a button that opens a Dialog component. The button has custom theming/styling to specify various states, one of them being the :focus state:

const useStyles = makeStyles({
  root: {
    "&:focus": {
      backgroundColor: "#3A7DA9"
    }
  }
});

export default function App() {
  const [open, setOpen] = useState(false);
  const classes = useStyles();

  return (
    <div className="App">
      <Button 
        id="button-that-opens-modal"
        className={classes.root} 
        onClick={() => setOpen(true)} 
      >
        Open the modal
      </Button>
      <Dialog open={open}>
        <h3>This is the modal</h3>
        <Button onClick={() => setOpen(false)}>
          Close
        </Button>
      </Dialog>
    </div>
  );
}

What I've noticed is that every time I have this pattern, (where a button opens a dialog modal), when the modal is closed, the #button-that-opens-modal is left with a :focus state, which looks bad in terms of styling. Here's a quick gif:

enter image description here

Codesandbox demonstrating the issue

Is this a known issue? I don't see why the :focus should be automatically applied to the button when the modal closes. How can I stop this?

I tried this:

I can add a ref to the button, and make sure to manually unfocus the button in various places. Adding it in the onExited method of the Dialog works, but flashes the focus state for a second:

export default function App() {
  const [open, setOpen] = useState(false);
  const buttonRef = useRef();
  const classes = useStyles();

  return (
    <div className="App">
      <Button
        ref={buttonRef}
        className={classes.root}
        onClick={() => setOpen(true)}
      >
        Open the modal
      </Button>
      <Dialog
        open={open}
        TransitionProps={{
          onExited: () => {
            buttonRef.current?.blur(); // helps but creates a flash
          }
        }}
      >
        <h3>This is the modal</h3>
        <Button onClick={() => {setOpen(false)}}>
          Close
        </Button>
      </Dialog>
    </div>
  );
}

sandbox showing this very imperfect solution

And even if I found exactly the right event handler to blur the button such the styling looks correct, this is not something I want to do for every Dialog in an app that has many Button - Dialog pairs. Is there a Material-UI prop I can use to disable this 'auto-focus' back on the button, rather than having to create a ref and manually .blur it for every Dialog?


Solution

  • This is for accessibilty purpose. You can disable it by adding prop disableRestoreFocus on your Dialog :)