department-of-veterans-affairs/vets-website

View on GitHub
src/platform/site-wide/accessible-VCL-modal.js

Summary

Maintainability
B
5 hrs
Test Coverage
import {
  isEscape,
  isTab,
  isReverseTab,
  getTabbableElements,
} from '../utilities/accessibility';

import { focusElement } from 'platform/utilities/ui';

/*
 * Creates function that captures/releases Veterans Crisis Line modal focus.
 */
export default function addFocusBehaviorToCrisisLineModal() {
  const overlay = document.getElementById('modal-crisisline');
  const modal = document.querySelector('.va-crisis-panel.va-modal-inner');
  const tabbableElements = getTabbableElements(modal);
  let openControl;
  const closeControl = tabbableElements[0];
  const lastTabbableElement = tabbableElements[tabbableElements.length - 1];
  const triggers = Array.from(
    document.querySelectorAll('[data-show="#modal-crisisline"]'),
  );

  function captureFocus(e) {
    if (e.target === closeControl && isReverseTab(e)) {
      e.preventDefault();
      focusElement(lastTabbableElement);
    }
    if (e.target === lastTabbableElement && isTab(e)) {
      e.preventDefault();
      focusElement(closeControl);
    }
  }

  function closeModal(e) {
    if (isEscape(e)) {
      overlay.classList.remove('va-overlay--open');
      document.body.classList.remove('va-pos-fixed');
      focusElement(openControl);
    }
  }

  function resetFocus() {
    focusElement(openControl);
  }

  // We're saving the element that triggered this modal
  // in openControl, so that we can focus back on it later,
  // when the modal is closed
  triggers.forEach(trigger => {
    trigger.addEventListener('click', () => {
      openControl = trigger;
    });
  });

  modal.addEventListener('keydown', closeModal);
  closeControl.addEventListener('click', resetFocus);
  closeControl.addEventListener('keydown', captureFocus);
  lastTabbableElement.addEventListener('keydown', captureFocus);
}