department-of-veterans-affairs/vets-website

View on GitHub
src/applications/proxy-rewrite/utilities/vcl-modal-behavior.js

Summary

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

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

// Used to add overlay behavior to VCL modal
export const addOverlayTriggers = () => {
  const vclModalTriggers = document?.querySelectorAll('.vcl-modal-open');
  const vclModalClose = document?.getElementById('vcl-modal-close');
  const vclModal = document?.getElementById('ts-modal-crisisline');

  const openModal = () => {
    vclModal?.classList.add('vcl-overlay--open');
    vclModal?.querySelector('a').focus();
    document.body?.classList.add('va-pos-fixed');
  };

  const closeModal = () => {
    vclModal?.classList.remove('vcl-overlay--open');
    document.body?.classList.remove('va-pos-fixed');
  };

  if (vclModalTriggers?.length) {
    Array.from(vclModalTriggers).forEach(trigger => {
      trigger.addEventListener('click', openModal);
    });
  }

  if (vclModalClose) {
    vclModalClose.addEventListener('click', closeModal);
  }
};

/*
 * Creates function that captures/releases Veterans Crisis Line modal focus.
 */
export const addFocusBehaviorToCrisisLineModal = () => {
  const overlay = document?.getElementById('ts-modal-crisisline');
  const modal = document?.querySelector('.vcl-crisis-panel.va-modal-inner');

  if (modal) {
    const tabbableElements = getTabbableElements(modal);
    let openControl;
    const closeControl = tabbableElements[0];
    const lastTabbableElement = tabbableElements?.[tabbableElements.length - 1];
    const triggers = Array.from(document?.querySelectorAll('.vcl-modal-open'));

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

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

    const 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);
  }
};