All files / src/components Modal.tsx

92.85% Statements 13/14
50% Branches 1/2
100% Functions 5/5
92.85% Lines 13/14

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78                          3x             1x 1x   1x 1x 1x           1x 1x 1x 1x 1x           1x                                             1x                          
import { PropsWithChildren, useEffect, useId, useRef } from 'react'
 
import { FocusOn } from 'react-focus-on'
 
import ActionButton, { ActionButtonProps } from './ActionButton'
 
export interface ModalProps extends PropsWithChildren {
  actionButtons: ActionButtonProps[]
  header: string
  onClose: EventListener
  open: boolean
}
 
const Modal = ({
  actionButtons,
  children,
  header,
  onClose,
  open,
}: ModalProps) => {
  const dialogRef = useRef<HTMLDialogElement>(null)
  const id = useId()
 
  useEffect(() => {
    if (open) {
      dialogRef.current?.showModal()
    } else E{
      dialogRef.current?.close()
    }
  }, [open])
 
  useEffect(() => {
    const el = dialogRef.current
    el?.addEventListener('close', onClose)
    return () => {
      el?.removeEventListener('close', onClose)
    }
  }, [onClose])
 
  // "Confirm" button of form triggers "close" on dialog because of [method="dialog"]
 
  return (
    <dialog
      ref={dialogRef}
      className="w-full border-none bg-transparent p-1 backdrop:bg-black backdrop:bg-opacity-80 md:w-2/3 lg:w-2/5"
    >
      <FocusOn enabled={open}>
        <section
          data-autofocus
          tabIndex={-1}
          className="rounded-md bg-white ring-2 ring-gray-modal"
          aria-describedby={`${id}-modal-header`}
        >
          <header
            id={`${id}-modal-header`}
            className="rounded-t-md border-b border-black bg-blue-deep p-3 text-white"
          >
            <h1>{header}</h1>
          </header>
          <div id={`${id}-modal-desc`} className="p-3">
            {children}
          </div>
          <div className="flex justify-end gap-2 border-t border-gray-modal p-2">
            {actionButtons.map((actionButtonProps) => (
              <ActionButton
                key={actionButtonProps.text}
                {...actionButtonProps}
              />
            ))}
          </div>
        </section>
      </FocusOn>
    </dialog>
  )
}
 
export default Modal