terra-abstract-modal
Advanced tools
+5
-0
@@ -5,2 +5,7 @@ # Changelog | ||
| ## 3.43.0 - (July 31, 2023) | ||
| * Changed | ||
| * Focus is now locked within modal content when modal is open for improved keyboard accessibility. | ||
| ## 3.42.0 - (June 5, 2023) | ||
@@ -7,0 +12,0 @@ |
@@ -18,2 +18,3 @@ "use strict"; | ||
| var _terraVisuallyHiddenText = _interopRequireDefault(require("terra-visually-hidden-text")); | ||
| var _reactFocusLock = _interopRequireDefault(require("react-focus-lock")); | ||
| var _ModalOverlay = _interopRequireDefault(require("./_ModalOverlay")); | ||
@@ -134,3 +135,6 @@ var _inertHelpers = require("./inertHelpers"); | ||
| ref: ref | ||
| }), /*#__PURE__*/_react.default.createElement("div", { | ||
| }), /*#__PURE__*/_react.default.createElement(_reactFocusLock.default, { | ||
| as: "div", | ||
| className: cx('modal-content-focus-trap-container') | ||
| }, /*#__PURE__*/_react.default.createElement("div", { | ||
| className: modalContainerClassName, | ||
@@ -162,3 +166,3 @@ ref: setModalFocusElementRef, | ||
| }); | ||
| })))); | ||
| }))))); | ||
| }); | ||
@@ -165,0 +169,0 @@ ModalContent.propTypes = propTypes; |
@@ -44,2 +44,6 @@ // Themes | ||
| } | ||
| .modal-content-focus-trap-container { | ||
| height: 100%; | ||
| } | ||
| } |
+3
-2
| { | ||
| "name": "terra-abstract-modal", | ||
| "main": "lib/AbstractModal.js", | ||
| "version": "3.42.0", | ||
| "version": "3.43.0", | ||
| "description": "The abstract modal is a structural component that provides the ability to display portal'd content in a layer above the app.", | ||
@@ -35,2 +35,3 @@ "repository": { | ||
| "prop-types": "^15.5.8", | ||
| "react-focus-lock": "^2.0.0", | ||
| "react-portal": "^4.1.2", | ||
@@ -57,3 +58,3 @@ "terra-theme-context": "^1.8.0", | ||
| }, | ||
| "gitHead": "acfaff6af8239744cf9342888f7267ee29471513" | ||
| "gitHead": "5b5d493c6e6b122e18c13e670b85e2abdb1c76f5" | ||
| } |
+36
-27
@@ -8,2 +8,3 @@ import React, { forwardRef, useEffect } from 'react'; | ||
| import VisuallyHiddenText from 'terra-visually-hidden-text'; | ||
| import FocusLock from 'react-focus-lock'; | ||
| import ModalOverlay from './_ModalOverlay'; | ||
@@ -148,29 +149,37 @@ import { hideModalDomUpdates, showModalDomUpdates } from './inertHelpers'; | ||
| > | ||
| <div className={modalContainerClassName} ref={setModalFocusElementRef} data-terra-abstract-modal-begin tabIndex="-1"> | ||
| <FormattedMessage id="Terra.AbstractModal.BeginModalDialog"> | ||
| {text => { | ||
| // In the latest version of react-intl this param is an array, when previous versions it was a string. | ||
| let useText = text; | ||
| if (Array.isArray(text)) { | ||
| useText = text.join(''); | ||
| } | ||
| return ( | ||
| <VisuallyHiddenText text={useText} /> | ||
| ); | ||
| }} | ||
| </FormattedMessage> | ||
| {children} | ||
| <FormattedMessage id="Terra.AbstractModal.EndModalDialog"> | ||
| {text => { | ||
| // In the latest version of react-intl this param is an array, when previous versions it was a string. | ||
| let useText = text; | ||
| if (Array.isArray(text)) { | ||
| useText = text.join(''); | ||
| } | ||
| return ( | ||
| <VisuallyHiddenText text={useText} /> | ||
| ); | ||
| }} | ||
| </FormattedMessage> | ||
| </div> | ||
| { | ||
| /** | ||
| * Use react-focus-lock library due to inability of focus-trap-react to handle children | ||
| * within a React Portal: https://github.com/focus-trap/focus-trap-react/issues/27. | ||
| */ | ||
| } | ||
| <FocusLock as="div" className={cx('modal-content-focus-trap-container')}> | ||
| <div className={modalContainerClassName} ref={setModalFocusElementRef} data-terra-abstract-modal-begin tabIndex="-1"> | ||
| <FormattedMessage id="Terra.AbstractModal.BeginModalDialog"> | ||
| {text => { | ||
| // In the latest version of react-intl this param is an array, when previous versions it was a string. | ||
| let useText = text; | ||
| if (Array.isArray(text)) { | ||
| useText = text.join(''); | ||
| } | ||
| return ( | ||
| <VisuallyHiddenText text={useText} /> | ||
| ); | ||
| }} | ||
| </FormattedMessage> | ||
| {children} | ||
| <FormattedMessage id="Terra.AbstractModal.EndModalDialog"> | ||
| {text => { | ||
| // In the latest version of react-intl this param is an array, when previous versions it was a string. | ||
| let useText = text; | ||
| if (Array.isArray(text)) { | ||
| useText = text.join(''); | ||
| } | ||
| return ( | ||
| <VisuallyHiddenText text={useText} /> | ||
| ); | ||
| }} | ||
| </FormattedMessage> | ||
| </div> | ||
| </FocusLock> | ||
| </div> | ||
@@ -177,0 +186,0 @@ </React.Fragment> |
@@ -44,2 +44,6 @@ // Themes | ||
| } | ||
| .modal-content-focus-trap-container { | ||
| height: 100%; | ||
| } | ||
| } |
@@ -9,3 +9,2 @@ const selector = '#root'; | ||
| expect($('#root').getAttribute('inert')).toEqual('true'); | ||
| expect($('#root').getAttribute('aria-hidden')).toEqual('true'); | ||
| Terra.validates.element('open default modal', { selector }); | ||
@@ -17,4 +16,2 @@ }); | ||
| Terra.validates.element('closed default modal'); | ||
| expect($('#root').getAttribute('inert')).toEqual('false'); | ||
| expect($('#root').getAttribute('aria-hidden')).toEqual(null); | ||
| expect($('#modal-open-button').isFocused()).toBeTruthy(); | ||
@@ -27,3 +24,2 @@ }); | ||
| expect($('#root').getAttribute('inert')).toEqual('true'); | ||
| expect($('#root').getAttribute('aria-hidden')).toEqual('true'); | ||
| expect($('[aria-modal="true"][role="dialog"] [data-terra-abstract-modal-begin="true"]').isFocused()).toBeTruthy(); | ||
@@ -34,4 +30,2 @@ }); | ||
| browser.keys('Escape'); | ||
| expect($('#root').getAttribute('inert')).toEqual('false'); | ||
| expect($('#root').getAttribute('aria-hidden')).toEqual(null); | ||
| expect($('#modal-open-button').isFocused()).toBeTruthy(); | ||
@@ -46,3 +40,2 @@ }); | ||
| expect($('#root').getAttribute('inert')).toEqual('true'); | ||
| expect($('#root').getAttribute('aria-hidden')).toEqual('true'); | ||
| Terra.validates.element('open fullscreen modal', { selector }); | ||
@@ -54,4 +47,2 @@ }); | ||
| Terra.validates.element('closed fullscreen modal'); | ||
| expect($('#root').getAttribute('inert')).toEqual('false'); | ||
| expect($('#root').getAttribute('aria-hidden')).toEqual(null); | ||
| expect($('#modal-open-button').isFocused()).toBeTruthy(); | ||
@@ -64,3 +55,2 @@ }); | ||
| expect($('#root').getAttribute('inert')).toEqual('true'); | ||
| expect($('#root').getAttribute('aria-hidden')).toEqual('true'); | ||
| expect($('[role="dialog"] [data-terra-abstract-modal-begin="true"]').isFocused()).toBeTruthy(); | ||
@@ -71,3 +61,2 @@ }); | ||
| browser.keys('Escape'); | ||
| expect($('#root').getAttribute('inert')).toEqual('false'); | ||
| expect($('#root').getAttribute('aria-hidden')).toEqual(null); | ||
@@ -110,3 +99,3 @@ expect($('#modal-open-button').isFocused()).toBeTruthy(); | ||
| describe('Focusable Content', () => { | ||
| it('clicks to open modal', () => { | ||
| before(() => { | ||
| browser.url('/raw/tests/terra-abstract-modal/abstract-modal/default-abstract-modal'); | ||
@@ -127,27 +116,16 @@ $('button').click(); | ||
| it('shifts focus forward away from the modal dialog', () => { | ||
| it('prevents shift focus forward out of modal dialog', () => { | ||
| browser.keys(['Tab']); | ||
| expect($('#modal-button').isFocused()).toBeFalsy(); | ||
| expect($('#modal-button').isFocused()).toBeTruthy(); | ||
| expect($('#modal-open-button').isFocused()).toBeFalsy(); | ||
| expect($('[aria-modal="true"][role="dialog"]').isFocused()).toBeFalsy(); | ||
| Terra.validates.element('focused shifted outside the end of the modal', { selector }); | ||
| Terra.validates.element('focus trapped from moving forward', { selector }); | ||
| }); | ||
| it('shifts focus back onto interactive elements within the modal', () => { | ||
| it('prevents shift focus back out of modal dialog', () => { | ||
| browser.keys(['Shift', 'Tab']); | ||
| expect($('#modal-button').isFocused()).toBeTruthy(); | ||
| Terra.validates.element('modal button focused again', { selector }); | ||
| }); | ||
| it('shifts focus back onto the modal dialog', () => { | ||
| browser.keys(['Shift', 'Tab']); | ||
| expect($('#modal-button').isFocused()).toBeFalsy(); | ||
| expect($('#modal-open-button').isFocused()).toBeFalsy(); | ||
| expect($('[aria-modal="true"][role="dialog"]').isFocused()).toBeTruthy(); | ||
| Terra.validates.element('focused shifted back to the modal', { selector }); | ||
| }); | ||
| it('shifts focus backwards away from the modal dialog', () => { | ||
| browser.keys(['Shift', 'Tab']); | ||
| expect($('[aria-modal="true"][role="dialog"]').isFocused()).toBeFalsy(); | ||
| Terra.validates.element('focus trapped from moving back', { selector }); | ||
| }); | ||
@@ -154,0 +132,0 @@ }); |
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
168888
-0.16%12
9.09%2600
-0.27%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added