focus-trap-react
Advanced tools
Comparing version 8.5.1 to 8.6.0
# Changelog | ||
## 8.6.0 | ||
### Minor Changes | ||
- 5292ae8: | ||
- Adding support for new focus-trap options from focus-trap v6.5.0: `checkCanFocusTrap()`, `onPostActivate()`, `checkCanReturnFocus()`, and `onPostDeactivate()`. | ||
- Adding support (bug fix) for existing focus-trap `setReturnFocus` option that had thus far been ignored, with focus-trap-react always returning focus to the previously-focused element prior to activation regardless of the use of the `setReturnFocus` option. The option is now respected the same as it is when using focus-trap directly. | ||
### Patch Changes | ||
- 24704c7: Bump focus-trap dependency to 6.5.1 for bug fix to onPostDeactivate. | ||
## 8.5.1 | ||
@@ -4,0 +16,0 @@ |
@@ -73,2 +73,7 @@ "use strict"; | ||
if (optionName === 'onPostDeactivate') { | ||
_this.onPostDeactivate = focusTrapOptions[optionName]; | ||
continue; | ||
} | ||
_this.tailoredFocusTrapOptions[optionName] = focusTrapOptions[optionName]; | ||
@@ -85,7 +90,43 @@ } // elements from which to create the focus trap on mount; if a child is used | ||
return _this; | ||
} | ||
/** Update the previously focused element with the currently focused element. */ | ||
} // TODO: Need more test coverage for this function | ||
_createClass(FocusTrap, [{ | ||
key: "getNodeForOption", | ||
value: function getNodeForOption(optionName) { | ||
var optionValue = this.tailoredFocusTrapOptions[optionName]; | ||
if (!optionValue) { | ||
return null; | ||
} | ||
var node = optionValue; | ||
if (typeof optionValue === 'string') { | ||
node = document.querySelector(optionValue); | ||
if (!node) { | ||
throw new Error("`".concat(optionName, "` refers to no known node")); | ||
} | ||
} | ||
if (typeof optionValue === 'function') { | ||
node = optionValue(); | ||
if (!node) { | ||
throw new Error("`".concat(optionName, "` did not return a node")); | ||
} | ||
} | ||
return node; | ||
} | ||
}, { | ||
key: "getReturnFocusNode", | ||
value: function getReturnFocusNode() { | ||
var node = this.getNodeForOption('setReturnFocus'); | ||
return node ? node : this.previouslyFocusedElement; | ||
} | ||
/** Update the previously focused element with the currently focused element. */ | ||
}, { | ||
key: "updatePreviousElement", | ||
@@ -97,10 +138,37 @@ value: function updatePreviousElement() { | ||
} | ||
/** Returns focus to the element that had focus when the trap was activated. */ | ||
}, { | ||
key: "deactivateTrap", | ||
value: function deactivateTrap() { | ||
var _this2 = this; | ||
}, { | ||
key: "returnFocus", | ||
value: function returnFocus() { | ||
if (this.previouslyFocusedElement && this.previouslyFocusedElement.focus) { | ||
this.previouslyFocusedElement.focus(); | ||
var checkCanReturnFocus = this.tailoredFocusTrapOptions.checkCanReturnFocus; | ||
if (this.focusTrap) { | ||
// NOTE: we never let the trap return the focus since we do that ourselves | ||
this.focusTrap.deactivate({ | ||
returnFocus: false | ||
}); | ||
} | ||
var finishDeactivation = function finishDeactivation() { | ||
var returnFocusNode = _this2.getReturnFocusNode(); | ||
var canReturnFocus = (returnFocusNode === null || returnFocusNode === void 0 ? void 0 : returnFocusNode.focus) && _this2.returnFocusOnDeactivate; | ||
if (canReturnFocus) { | ||
/** Returns focus to the element that had focus when the trap was activated. */ | ||
returnFocusNode.focus(); | ||
} | ||
if (_this2.onPostDeactivate) { | ||
_this2.onPostDeactivate.call(null); // don't call it in context of "this" | ||
} | ||
}; | ||
if (checkCanReturnFocus) { | ||
checkCanReturnFocus(this.getReturnFocusNode()).then(finishDeactivation, finishDeactivation); | ||
} else { | ||
finishDeactivation(); | ||
} | ||
} | ||
@@ -144,24 +212,23 @@ }, { | ||
if (prevProps.active && !this.props.active) { | ||
// NOTE: we never let the trap return the focus since we do that ourselves | ||
this.focusTrap.deactivate({ | ||
returnFocus: false | ||
}); | ||
var hasActivated = !prevProps.active && this.props.active; | ||
var hasDeactivated = prevProps.active && !this.props.active; | ||
var hasPaused = !prevProps.paused && this.props.paused; | ||
var hasUnpaused = prevProps.paused && !this.props.paused; | ||
if (this.returnFocusOnDeactivate) { | ||
this.returnFocus(); | ||
} | ||
if (hasActivated) { | ||
this.updatePreviousElement(); | ||
this.focusTrap.activate(); | ||
} | ||
if (hasDeactivated) { | ||
this.deactivateTrap(); | ||
return; // un/pause does nothing on an inactive trap | ||
} | ||
if (!prevProps.active && this.props.active) { | ||
this.updatePreviousElement(); | ||
this.focusTrap.activate(); | ||
if (hasPaused) { | ||
this.focusTrap.pause(); | ||
} | ||
if (prevProps.paused && !this.props.paused) { | ||
if (hasUnpaused) { | ||
this.focusTrap.unpause(); | ||
} else if (!prevProps.paused && this.props.paused) { | ||
this.focusTrap.pause(); | ||
} | ||
@@ -176,12 +243,3 @@ } else if (prevProps.containerElements !== this.props.containerElements) { | ||
value: function componentWillUnmount() { | ||
if (this.focusTrap) { | ||
// NOTE: we never let the trap return the focus since we do that ourselves | ||
this.focusTrap.deactivate({ | ||
returnFocus: false | ||
}); | ||
} | ||
if (this.returnFocusOnDeactivate) { | ||
this.returnFocus(); | ||
} | ||
this.deactivateTrap(); | ||
} | ||
@@ -191,3 +249,3 @@ }, { | ||
value: function render() { | ||
var _this2 = this; | ||
var _this3 = this; | ||
@@ -202,3 +260,3 @@ var child = this.props.children ? React.Children.only(this.props.children) : undefined; | ||
var composedRefCallback = function composedRefCallback(element) { | ||
var containerElements = _this2.props.containerElements; | ||
var containerElements = _this3.props.containerElements; | ||
@@ -213,3 +271,3 @@ if (child) { | ||
_this2.focusTrapElements = containerElements ? containerElements : [element]; | ||
_this3.focusTrapElements = containerElements ? containerElements : [element]; | ||
}; | ||
@@ -237,3 +295,7 @@ | ||
onActivate: PropTypes.func, | ||
onPostActivate: PropTypes.func, | ||
checkCanFocusTrap: PropTypes.func, | ||
onDeactivate: PropTypes.func, | ||
onPostDeactivate: PropTypes.func, | ||
checkCanReturnFocus: PropTypes.func, | ||
initialFocus: PropTypes.oneOfType([PropTypes.instanceOf(ElementType), PropTypes.string, PropTypes.func]), | ||
@@ -240,0 +302,0 @@ fallbackFocus: PropTypes.oneOfType([PropTypes.instanceOf(ElementType), PropTypes.string, PropTypes.func]), |
{ | ||
"name": "focus-trap-react", | ||
"version": "8.5.1", | ||
"version": "8.6.0", | ||
"description": "A React component that traps focus.", | ||
@@ -58,11 +58,11 @@ "main": "dist/focus-trap-react.js", | ||
"devDependencies": { | ||
"@babel/cli": "^7.14.3", | ||
"@babel/core": "^7.14.3", | ||
"@babel/cli": "^7.14.5", | ||
"@babel/core": "^7.14.6", | ||
"@babel/plugin-proposal-class-properties": "^7.12.13", | ||
"@babel/preset-env": "^7.14.4", | ||
"@babel/preset-react": "^7.13.13", | ||
"@babel/preset-env": "^7.14.5", | ||
"@babel/preset-react": "^7.14.5", | ||
"@changesets/cli": "^2.16.0", | ||
"@testing-library/cypress": "^7.0.6", | ||
"@testing-library/dom": "^7.31.0", | ||
"@testing-library/jest-dom": "^5.12.0", | ||
"@testing-library/dom": "^7.31.2", | ||
"@testing-library/jest-dom": "^5.14.1", | ||
"@testing-library/react": "^11.2.7", | ||
@@ -77,12 +77,12 @@ "@testing-library/user-event": "^13.1.9", | ||
"budo": "^11.6.4", | ||
"cypress": "^7.4.0", | ||
"cypress": "^7.5.0", | ||
"cypress-plugin-tab": "^1.0.5", | ||
"eslint": "^7.27.0", | ||
"eslint": "^7.28.0", | ||
"eslint-config-prettier": "^8.3.0", | ||
"eslint-plugin-cypress": "^2.11.3", | ||
"eslint-plugin-react": "^7.24.0", | ||
"jest": "^26.6.3", | ||
"jest": "^27.0.4", | ||
"jest-watch-typeahead": "^0.6.4", | ||
"onchange": "^7.1.0", | ||
"prettier": "^2.3.0", | ||
"prettier": "^2.3.1", | ||
"prop-types": "^15.7.2", | ||
@@ -92,7 +92,7 @@ "react": "^17.0.2", | ||
"regenerator-runtime": "^0.13.7", | ||
"start-server-and-test": "^1.12.3", | ||
"start-server-and-test": "^1.12.5", | ||
"typescript": "^4.3.2" | ||
}, | ||
"dependencies": { | ||
"focus-trap": "^6.5.0" | ||
"focus-trap": "^6.5.1" | ||
}, | ||
@@ -99,0 +99,0 @@ "peerDependencies": { |
# focus-trap-react [![CI](https://github.com/focus-trap/focus-trap-react/workflows/CI/badge.svg?branch=master&event=push)](https://github.com/focus-trap/focus-trap-react/actions?query=workflow:CI+branch:master) [![Codecov](https://img.shields.io/codecov/c/github/focus-trap/focus-trap-react)](https://codecov.io/gh/focus-trap/focus-trap-react) [![license](https://badgen.now.sh/badge/license/MIT)](./LICENSE) | ||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section --> | ||
[![All Contributors](https://img.shields.io/badge/all_contributors-17-orange.svg?style=flat-square)](#contributors) | ||
[![All Contributors](https://img.shields.io/badge/all_contributors-18-orange.svg?style=flat-square)](#contributors) | ||
<!-- ALL-CONTRIBUTORS-BADGE:END --> | ||
@@ -189,7 +189,8 @@ | ||
<td align="center"><a href="https://github.com/DSil"><img src="https://avatars1.githubusercontent.com/u/6265045?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel</b></sub></a><br /><a href="#maintenance-DSil" title="Maintenance">🚧</a> <a href="https://github.com/focus-trap/focus-trap-react/commits?author=DSil" title="Tests">⚠️</a></td> | ||
<td align="center"><a href="https://github.com/Dan503"><img src="https://avatars.githubusercontent.com/u/10610368?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel Tonon</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/commits?author=Dan503" title="Documentation">📖</a> <a href="https://github.com/focus-trap/focus-trap-react/commits?author=Dan503" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap-react/commits?author=Dan503" title="Tests">⚠️</a></td> | ||
<td align="center"><a href="http://davidtheclark.com/"><img src="https://avatars2.githubusercontent.com/u/628431?v=4?s=100" width="100px;" alt=""/><br /><sub><b>David Clark</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/commits?author=davidtheclark" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap-react/issues?q=author%3Adavidtheclark" title="Bug reports">🐛</a> <a href="#infra-davidtheclark" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/focus-trap/focus-trap-react/commits?author=davidtheclark" title="Tests">⚠️</a> <a href="https://github.com/focus-trap/focus-trap-react/commits?author=davidtheclark" title="Documentation">📖</a> <a href="#maintenance-davidtheclark" title="Maintenance">🚧</a></td> | ||
<td align="center"><a href="https://github.com/features/security"><img src="https://avatars1.githubusercontent.com/u/27347476?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dependabot</b></sub></a><br /><a href="#maintenance-dependabot" title="Maintenance">🚧</a></td> | ||
<td align="center"><a href="http://josuzuki.me"><img src="https://avatars1.githubusercontent.com/u/9583920?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jonathan Suzuki</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/issues?q=author%3AJoSuzuki" title="Bug reports">🐛</a></td> | ||
</tr> | ||
<tr> | ||
<td align="center"><a href="http://josuzuki.me"><img src="https://avatars1.githubusercontent.com/u/9583920?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jonathan Suzuki</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/issues?q=author%3AJoSuzuki" title="Bug reports">🐛</a></td> | ||
<td align="center"><a href="http://kathleenmcmahon.dev/"><img src="https://avatars1.githubusercontent.com/u/11621935?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kathleen McMahon</b></sub></a><br /><a href="#maintenance-resource11" title="Maintenance">🚧</a></td> | ||
@@ -201,5 +202,5 @@ <td align="center"><a href="https://marais.io/"><img src="https://avatars2.githubusercontent.com/u/599459?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Marais Rossouw</b></sub></a><br /><a href="#infra-maraisr" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td> | ||
<td align="center"><a href="https://seanmcp.com/"><img src="https://avatars1.githubusercontent.com/u/6360367?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sean McPherson</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/commits?author=SeanMcP" title="Code">💻</a></td> | ||
<td align="center"><a href="https://recollectr.io"><img src="https://avatars2.githubusercontent.com/u/6835891?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Slapbox</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/commits?author=Slapbox" title="Documentation">📖</a></td> | ||
</tr> | ||
<tr> | ||
<td align="center"><a href="https://recollectr.io"><img src="https://avatars2.githubusercontent.com/u/6835891?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Slapbox</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/commits?author=Slapbox" title="Documentation">📖</a></td> | ||
<td align="center"><a href="https://stefancameron.com/"><img src="https://avatars3.githubusercontent.com/u/2855350?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stefan Cameron</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/commits?author=stefcameron" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap-react/issues?q=author%3Astefcameron" title="Bug reports">🐛</a> <a href="#infra-stefcameron" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/focus-trap/focus-trap-react/commits?author=stefcameron" title="Tests">⚠️</a> <a href="https://github.com/focus-trap/focus-trap-react/commits?author=stefcameron" title="Documentation">📖</a> <a href="#maintenance-stefcameron" title="Maintenance">🚧</a></td> | ||
@@ -206,0 +207,0 @@ <td align="center"><a href="http://tylerhawkins.info/201R/"><img src="https://avatars0.githubusercontent.com/u/13806458?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tyler Hawkins</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/commits?author=thawkin3" title="Documentation">📖</a> <a href="#example-thawkin3" title="Examples">💡</a> <a href="https://github.com/focus-trap/focus-trap-react/commits?author=thawkin3" title="Tests">⚠️</a> <a href="#tool-thawkin3" title="Tools">🔧</a></td> |
@@ -39,2 +39,7 @@ const React = require('react'); | ||
if (optionName === 'onPostDeactivate') { | ||
this.onPostDeactivate = focusTrapOptions[optionName]; | ||
continue; | ||
} | ||
this.tailoredFocusTrapOptions[optionName] = focusTrapOptions[optionName]; | ||
@@ -52,2 +57,34 @@ } | ||
// TODO: Need more test coverage for this function | ||
getNodeForOption(optionName) { | ||
const optionValue = this.tailoredFocusTrapOptions[optionName]; | ||
if (!optionValue) { | ||
return null; | ||
} | ||
let node = optionValue; | ||
if (typeof optionValue === 'string') { | ||
node = document.querySelector(optionValue); | ||
if (!node) { | ||
throw new Error(`\`${optionName}\` refers to no known node`); | ||
} | ||
} | ||
if (typeof optionValue === 'function') { | ||
node = optionValue(); | ||
if (!node) { | ||
throw new Error(`\`${optionName}\` did not return a node`); | ||
} | ||
} | ||
return node; | ||
} | ||
getReturnFocusNode() { | ||
const node = this.getNodeForOption('setReturnFocus'); | ||
return node ? node : this.previouslyFocusedElement; | ||
} | ||
/** Update the previously focused element with the currently focused element. */ | ||
@@ -60,7 +97,33 @@ updatePreviousElement() { | ||
/** Returns focus to the element that had focus when the trap was activated. */ | ||
returnFocus() { | ||
if (this.previouslyFocusedElement && this.previouslyFocusedElement.focus) { | ||
this.previouslyFocusedElement.focus(); | ||
deactivateTrap() { | ||
const { checkCanReturnFocus } = this.tailoredFocusTrapOptions; | ||
if (this.focusTrap) { | ||
// NOTE: we never let the trap return the focus since we do that ourselves | ||
this.focusTrap.deactivate({ returnFocus: false }); | ||
} | ||
const finishDeactivation = () => { | ||
const returnFocusNode = this.getReturnFocusNode(); | ||
const canReturnFocus = | ||
returnFocusNode?.focus && this.returnFocusOnDeactivate; | ||
if (canReturnFocus) { | ||
/** Returns focus to the element that had focus when the trap was activated. */ | ||
returnFocusNode.focus(); | ||
} | ||
if (this.onPostDeactivate) { | ||
this.onPostDeactivate.call(null); // don't call it in context of "this" | ||
} | ||
}; | ||
if (checkCanReturnFocus) { | ||
checkCanReturnFocus(this.getReturnFocusNode()).then( | ||
finishDeactivation, | ||
finishDeactivation | ||
); | ||
} else { | ||
finishDeactivation(); | ||
} | ||
} | ||
@@ -106,12 +169,8 @@ | ||
if (prevProps.active && !this.props.active) { | ||
// NOTE: we never let the trap return the focus since we do that ourselves | ||
this.focusTrap.deactivate({ returnFocus: false }); | ||
if (this.returnFocusOnDeactivate) { | ||
this.returnFocus(); | ||
} | ||
return; // un/pause does nothing on an inactive trap | ||
} | ||
const hasActivated = !prevProps.active && this.props.active; | ||
const hasDeactivated = prevProps.active && !this.props.active; | ||
const hasPaused = !prevProps.paused && this.props.paused; | ||
const hasUnpaused = prevProps.paused && !this.props.paused; | ||
if (!prevProps.active && this.props.active) { | ||
if (hasActivated) { | ||
this.updatePreviousElement(); | ||
@@ -121,7 +180,14 @@ this.focusTrap.activate(); | ||
if (prevProps.paused && !this.props.paused) { | ||
this.focusTrap.unpause(); | ||
} else if (!prevProps.paused && this.props.paused) { | ||
if (hasDeactivated) { | ||
this.deactivateTrap(); | ||
return; // un/pause does nothing on an inactive trap | ||
} | ||
if (hasPaused) { | ||
this.focusTrap.pause(); | ||
} | ||
if (hasUnpaused) { | ||
this.focusTrap.unpause(); | ||
} | ||
} else if (prevProps.containerElements !== this.props.containerElements) { | ||
@@ -134,10 +200,3 @@ this.focusTrapElements = this.props.containerElements; | ||
componentWillUnmount() { | ||
if (this.focusTrap) { | ||
// NOTE: we never let the trap return the focus since we do that ourselves | ||
this.focusTrap.deactivate({ returnFocus: false }); | ||
} | ||
if (this.returnFocusOnDeactivate) { | ||
this.returnFocus(); | ||
} | ||
this.deactivateTrap(); | ||
} | ||
@@ -192,3 +251,7 @@ | ||
onActivate: PropTypes.func, | ||
onPostActivate: PropTypes.func, | ||
checkCanFocusTrap: PropTypes.func, | ||
onDeactivate: PropTypes.func, | ||
onPostDeactivate: PropTypes.func, | ||
checkCanReturnFocus: PropTypes.func, | ||
initialFocus: PropTypes.oneOfType([ | ||
@@ -195,0 +258,0 @@ PropTypes.instanceOf(ElementType), |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
52774
506
214
Updatedfocus-trap@^6.5.1