cf.completefrontend

Write the codeMedium

Build a Modal that gives the focus back to whatever opened it

Modal takes open, title, onClose, and children. While open is false it renders nothing. While it's true it renders a <div role="dialog" aria-modal="true"> labelled by its <h2> title, then its children, then a Close button that calls onClose. The starter does all of that.

Add the focus handling a keyboard user needs. When the dialog opens, move the focus to its Close button. When it closes, for any reason (Close, a button in children that sets open to false, or the parent unmounting the Modal), put the focus back on the element that had it just before the dialog opened, usually the button that opened it. It should work in <StrictMode> too.

<>
  <button onClick={() => setOpen(true)}>Clear cart</button>
  <Modal open={open} title="Clear the cart?" onClose={() => setOpen(false)}>
    <p>This removes every tea from your cart.</p>
    <button onClick={() => setOpen(false)}>Keep teas</button>
  </Modal>
</>
Write this (Modal.jsx)
import { useEffect, useId, useRef } from "react";
 
export default function Modal({ open, title, onClose, children }) {
  const titleId = useId();
  // Move the focus to Close when the dialog opens,
  // and give it back to the opener when it closes.
 
  if (!open) return null;
  return (
    <div role="dialog" aria-modal="true" aria-labelledby={titleId}>
      <h2 id={titleId}>{title}</h2>
      {children}
      <button onClick={onClose}>Close</button>
    </div>
  );
}
The checks your code must pass
  • renders nothing while it's closed
  • opens as a dialog named by its title
  • moves the focus to Close when it opens
  • gives the focus back to the opener when Close is clicked
  • gives the focus back when a button inside closes it
  • gives the focus back to whichever button opened it
  • gives the focus back when the parent unmounts it
  • works in Strict Mode

Try it in the editor

The answer

Write it yourself first. Then compare it with a reference solution and the checks it passes.

Checking your sign-in…