cengage / react-magma

https://react-magma.cengage.com
MIT License
21 stars 12 forks source link

Modal: Focus lock is failing with total content repaints #1302

Open shawn-haley opened 3 months ago

shawn-haley commented 3 months ago

Describe the bug In the modal the focus is trapped on initial render. In scenarios where content is dynamic, for example a button changes the content within the modal entireley, the focus become unstable. Tabbing forward will get to the close button and stop. Tabbing backward will escape the modal.

To Reproduce Steps to reproduce the behavior:

  1. Go to 'this codesandbox.'
  2. Click on 'show modal'
  3. press tab repeatedly and see focus remain within the modal
  4. Click "this is a button"
  5. press tab repeatedly and see focus remain within the modal
  6. observe focus stop at the 'x'
  7. click shift + tab several times and see focus escape.

Expected behavior focus works as does on the initial render

Desktop (please complete the following information):

Additional context https://jam.dev/c/b501b537-fdcb-423d-9bea-a9bdec8d9268

silvalaura commented 3 months ago

We should add some sort of prop so adopters can alert when the content of a modal has changed, so we can re-call the focus lock.

Another simple example of the focus lock not "refreshing":

export const FocusLock = () => {
  const [showModal, setShowModal] = React.useState(false);
  const [currentStep, setCurrentStep] = React.useState(0);
  const buttonRef = React.useRef();
  const steps = 6;

  function handleOnClose() {
    setShowModal(false);
    setCurrentStep(0);
  }

  function handleNextClick() {
    setCurrentStep(currentStep + 1);
  }
  function handleBackClick() {
    setCurrentStep(currentStep - 1);
  }

  return (
    <>
      <Modal header="Modal Title" onClose={handleOnClose} isOpen={showModal}>
        <Flex
          behavior={FlexBehavior.container}
          justify={FlexJustify.spaceBetween}
        >
          <Flex behavior={FlexBehavior.item}>
            {currentStep !== 0 ? (
              <Button
                onClick={handleBackClick}
              >
                <span>BACK</span>
              </Button>
            ) : (
              <></>
            )}
          </Flex>
          <Flex behavior={FlexBehavior.item}>
            <span>{currentStep}</span>
          </Flex>
          <Flex behavior={FlexBehavior.item}>
            {currentStep < steps ? (
              <Button
                onClick={handleNextClick}
              >
                <span>NEXT</span>
              </Button>
            ) : (
              <Button
                onClick={handleOnClose}
              >
                <span>END</span>
              </Button>
            )}
          </Flex>
        </Flex>
      </Modal>
      <Button onClick={() => setShowModal(true)} ref={buttonRef}>
        Show Modal
      </Button>
    </>
  )
}