terra-abstract-modal
Advanced tools
Comparing version
@@ -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; |
{ | ||
"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" | ||
} |
@@ -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> |
@@ -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 @@ }); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
168888
-0.16%12
9.09%2600
-0.27%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added