|

   Veteran Tools Platform - Developer Documentation

There are no notes for this item.

PropRequired?TypeDefaultDescription
visible Yes bool If the modal is visible or not
onClose Yes func Handler for when the modal is closed
contents No node Contents of modal when displayed. You can also pass the contents as children, which is preferred
cssClass No string CSS class to set on the modal
id No string Id of the modal, used for aria attributes
title No string Title/header text for the modal
hideCloseButton No bool Hide the close button that's normally in the top right
focusSelector No string 'button, input, select, a' Selector to use to find elements to focus on when the modal is opened
<div id="reactMount" data-tpl="modal">
    <div class="va-modal" id="default" role="alertdialog" aria-labelledby="default-title">
        <div class="va-modal-inner">
            <h3 id="default-title" class="va-modal-title">Default Modal</h3><button class="va-modal-close" type="button"><i class="fa fa-close"></i><span class="usa-sr-only">Close this modal</span></button>
            <div class="va-modal-body">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam id felis pulvinar ligula ultricies sollicitudin eget nec dui. Cras augue velit, pellentesque sit amet nisl ut, tristique suscipit sem. Cras sollicitudin auctor mattis.</div>
        </div>
    </div>
</div>
<script>
    window.currentProps = {
        "package": {
            "name": "department-of-veteran-affairs/jean-pants",
            "version": "0.1.0"
        },
        "assetPath": "/design-system/",
        "isProduction": true,
        "componentSourcePath": "Modal.jsx",
        "id": "default",
        "title": "Default Modal",
        "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam id felis pulvinar ligula ultricies sollicitudin eget nec dui. Cras augue velit, pellentesque sit amet nisl ut, tristique suscipit sem. Cras sollicitudin auctor mattis."
    }
</script>
import React from 'react';
import Modal from './Modal';

export default function ModalExample(props) {
  return (
    <Modal
      title={props.title}
      id={props.id}
      visible
      onClose={() => {}}>
      {props.content}
    </Modal>
  );
}
package:
  name: department-of-veteran-affairs/jean-pants
  version: 0.1.0
assetPath: /design-system/
isProduction: true
componentSourcePath: Modal.jsx
id: default
title: Default Modal
content: >-
  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam id felis
  pulvinar ligula ultricies sollicitudin eget nec dui. Cras augue velit,
  pellentesque sit amet nisl ut, tristique suscipit sem. Cras sollicitudin
  auctor mattis.
  • Content:
    import PropTypes from 'prop-types';
    import React from 'react';
    import classNames from 'classnames';
    
    function focusListener(selector) {
      const listener = event => {
        const modal = document.querySelector('.va-modal');
        if (!modal.contains(event.target)) {
          event.stopPropagation();
          const focusableElement = modal.querySelector(selector);
          if (focusableElement) {
            focusableElement.focus();
          }
        }
      };
      document.addEventListener('focus', listener, true);
      return listener;
    }
    
    class Modal extends React.Component {
      constructor(props) {
        super(props);
        this.handleClose = this.handleClose.bind(this);
        this.state = { lastFocus: null, focusListener: null };
      }
    
      componentDidMount() {
        if (this.props.visible) {
          document.body.classList.add('modal-open');
        }
      }
    
      componentWillReceiveProps(newProps) {
        if (newProps.visible && !this.props.visible) {
          this.setState({ lastFocus: document.activeElement, focusListener: focusListener(newProps.focusSelector) });
        } else if (!newProps.visible && this.props.visible) {
          document.removeEventListener('focus', this.state.focusListener, true);
          this.state.lastFocus.focus();
          document.body.classList.remove('modal-open');
        }
      }
    
      componentDidUpdate(prevProps) {
        if (!prevProps.visible && this.props.visible) {
          const focusableElement = document.querySelector('.va-modal').querySelector(this.props.focusSelector);
          if (focusableElement) {
            focusableElement.focus();
          }
        }
      }
    
      componentWillUnmount() {
        document.removeEventListener('focus', this.state.focusListener, true);
        document.body.classList.remove('modal-open');
      }
    
      handleClose(e) {
        e.preventDefault();
        this.props.onClose();
      }
    
      render() {
        const { id, title, visible } = this.props;
        const modalCss = classNames('va-modal', this.props.cssClass);
        const modalTitle = title && (
          <h3 id={`${id}-title`} className="va-modal-title">{title}</h3>
        );
    
        if (!visible) { return <div/>; }
    
        let closeButton;
        if (!this.props.hideCloseButton) {
          closeButton = (
            <button
              className="va-modal-close"
              type="button"
              onClick={this.handleClose}>
              <i className="fa fa-close"></i>
              <span className="usa-sr-only">Close this modal</span>
            </button>
          );
        }
    
        return (
          <div className={modalCss} id={id} role="alertdialog" aria-labelledby={`${id}-title`}>
            <div className="va-modal-inner">
              {modalTitle}
              {closeButton}
              <div className="va-modal-body">
                {this.props.contents || this.props.children}
              </div>
            </div>
          </div>
        );
      }
    }
    
    Modal.propTypes = {
      /**
       * If the modal is visible or not
       */
      visible: PropTypes.bool.isRequired,
      /**
       * Handler for when the modal is closed
       */
      onClose: PropTypes.func.isRequired,
      /**
       * Contents of modal when displayed. You can also pass the contents as children, which is preferred
       */
      contents: PropTypes.node,
      /**
       * CSS class to set on the modal
       */
      cssClass: PropTypes.string,
      /**
       * Id of the modal, used for aria attributes
       */
      id: PropTypes.string,
      /**
       * Title/header text for the modal
       */
      title: PropTypes.string,
      /**
       * Hide the close button that's normally in the top right
       */
      hideCloseButton: PropTypes.bool,
      /**
       * Selector to use to find elements to focus on when the
       * modal is opened
       */
      focusSelector: PropTypes.string
    };
    
    Modal.defaultProps = {
      focusSelector: 'button, input, select, a'
    };
    
    export default Modal;
    
  • URL: /components/raw/modal/Modal.jsx
  • Filesystem Path: src/components/modal/Modal.jsx
  • Size: 3.6 KB
  • Content:
    import React from 'react';
    import { shallow } from 'enzyme';
    import { expect } from 'chai';
    import { axeCheck } from '../../../lib/testing/helpers';
    
    import Modal from './Modal.jsx';
    
    describe('<Modal/>', () => {
      it('should render', () => {
        const tree = shallow(<Modal id="modal" title="Modal title" visible onClose={() => {}}>Modal contents</Modal>);
        expect(tree.text()).to.contain('Modal contents');
      });
    
      it('should pass aXe check', () => {
        return axeCheck(<Modal id="modal" title="Modal title" visible onClose={() => {}}/>);
      });
    });
    
  • URL: /components/raw/modal/Modal.unit.spec.jsx
  • Filesystem Path: src/components/modal/Modal.unit.spec.jsx
  • Size: 556 Bytes