focus-trap / focus-trap-react Goto Github PK
View Code? Open in Web Editor NEWA React component that traps focus
Home Page: http://focus-trap.github.io/focus-trap-react/demo/
License: MIT License
A React component that traps focus
Home Page: http://focus-trap.github.io/focus-trap-react/demo/
License: MIT License
Inside of FocusTrap, I've set an svg to be the initial focusable element. However, this doesn't trap the focus, which means that the elements in the background are tabbable. This is only in IE11 (wish I didn't have to work with IE11 trust me).
For brevity, here's a basic composition. The "dialog" div is appended to the body tag.
<FocusTrap><div id="dialog"><svg focusable tabIndex="0"> ... </svg></div></FocusTrap>
By doing this, I can verify in the DOM in IE11 that the svg does have the tabIndex="0" and focusable="true". But the focus is not trapped and I cannot reach the svg by tabbing.
I can work around this by putting the initial focus on the "dialog" div instead. This traps the focus correctly and the svg is in the tab flow. Curious to know if anyone has insight as to why focus isn't trapped if an svg is the initial focusable element.
I’ve build a tool that allows you to try out a bunch of different webfonts in the browser. You can change the font family or style; set the size, leading, tracking, and margins; and toggle on and off any of its stylistic sets or open type features.
The editor itself is a <div />
with contentEditable
turned on and role
set to textbox
. This is the element I need to trap focus on conditionally, but I’m seeing the error that says:
Error: You can't have a focus-trap without at least one focusable element
Is there any way to use focus-trap-react with a contentEditable element?
Hope not to be a nuisance, but can we get a new release of this that includes the latest version of focus-trap (2.0.2)?
I just went into my package.json and updated to react 16 and focus trap is giving me this error:
Error: You can't have a focus-trap without at least one focusable element
"focus-trap-react": "^3.0.4",
"react": "^16.0.0",
"react-dom": "^16.0.0",
Not really sure what happened exactly. Looking into it but wanted to let you know.
I have a code like this in react:
constructor(props) {
super(props);
this.node = React.createRef();
}
And then in the return call, I have this:
<FocusTrap>
<ul
ref={this.node}
>
{children}
</ul>
</FocusTrap>
If I go ahead and fetch this.node.current
, I get null
.
However, if I do it without the <FocusTrap>
everything works fine.
Any idea why <FocusTrap>
would be messing with the node?
If however I do this, then it fetches the this.node.current
:
<FocusTrap>
<div>
<ul
ref={this.node}
>
{children}
</ul>
</div>
</FocusTrap>
I do not want to give that extra containing class, as it is messing up the layout.
A button that uses clipboard js does not work if it exists within the focustrap.
https://clipboardjs.com/
Is it possible to release a newer version of this repo with latest version from original focustrap repo so that newer features can be used?
The new 7.0.0/7.0.1 upgrade breaks down jest/enzyme tests with the error:
Error: Uncaught [Error: Your focus-trap needs to have at least one focusable element].
The above error occurred in the component:
in FocusTrap (created by WrapperComponent)
in WrapperComponent
Seems that focus trap cannot locate any child element.
I have added the isolated test file as well as package.json - everything is up to date there.
When using FocusTrap component in a typescript project I get the following error:
error TS2605: JSX element type 'FocusTrap' is not a constructor function for JSX elements.
Type 'FocusTrap' is missing the following properties from type 'ElementClass': render, context, setState, forceUpdate, and 3 more.
Component.tsx(63,7): error TS2607: JSX element class does not support attributes because it does not have a 'props' property.
With the release of React 15.5 the use of CreateClass instead of ES6 classes and referencing PropTypes from React itself are marked as deprecation warnings and this will be removed in React 16.
This package is referenced in react-aria-modal
where I have also submitted a PR but without updating this package as well the fixes there does not remove the React warnings.
I have opened a PR on this for your consideration #11
I have a hidden dialog box wrapped in a FocusTrap
. I make the dialog box visible at the same time that I activate the FocusTrap
. This causes the error:
index.js:163 Uncaught Error: You can't have a focus-trap without at least one focusable element
.
It seems that as the FocusTrap activates, it's upset that there isn't already a non-hidden focusable element, even though I'm trying to create one simultaneously. If I add a fake always-visible focusable element it stops complaining. But I don't want that in production.
My setup is basically this:
<FocusTrap active={showIt}>
<input style={{visibility: "visible"}} tabIndex={0}/> // FAKE focusable element makes it work.
<div style={{visibility: showIt ? "visible" : "hidden"}}>
<input style={{visibility: "inherit"}} tabIndex={0}/>
</div>
</FocusTrap>
I don't think I can use fallbackFocus
since none of the contained elements are focusable while they're hidden. Is there a way just to suppress this error from being thrown?
Thanks!
Looking at the demo page at http://davidtheclark.github.io/focus-trap-react/demo/, I notice a few issues:
Notably, when I build and run the demo page locally, everything works fine.
setReturnFocus
seems to be ignored in the focusTrapOptions
; went looking for solutions in the original focus-trap
repository, then realized the deactivation logic is being hijacked for the react variant. It doesn't look like it was taken into account during implementation.
Going to use the more generic onDeactivate
as a workaround for the time being; would love to see an update to README though, in case anyone else stumbles into this issue as well.
Hi all,
How would you feel about using Kent Dodds' React Testing Library package to unit test this project? That's probably the most commonly used testing library at this point and is more of an "industry standard" than using the test utils directly like what's currently being done.
I'm happy to submit an MR adding this soon! I just wanted to check if there was some good reason that this is not currently being used.
Thanks!
React.StrictMode throws an error as focus-trap-react uses deprecated React API. See https://reactjs.org/docs/strict-mode.html#warning-about-deprecated-finddomnode-usage
Could you please update the package to work with RefObject?
I've been shifting my focus away from UI development, so don't plan on addressing new issues myself. If you use this library and want to see development continue, you can make that happen by becoming a co-maintainer — with permissions to triage issues, merge PRs, cut releases, etc.
Please comment below if you're interested!
Another possibility is for a dedicated owner to fork this code and create a new package. I'd be happy to link to that library from the README of this one.
The existing tests could be replaced with Jest tests. Although we'd lose the browser variety we get with zuul, I think it's well worth the simplification.
I've just spent a while hunting down a little memory leak that we traced to this library. The event handlers on inputs that are left focussed when we unmounted a route in our app were being held and causing a leak. Have you come across this before? Is there anyway to ensure these are released from within focus-trap-react?
I have a button that toggles the activation of a focus trap. However I want the the FocusTrap to go away when I click anywhere outside of it, including the button (which is outside the focustrap). The problem is if I click the button the onDeactivate function is called which kills the focus trap, but then the onClick function for the button then thinks the focus trap is dead so it turns it back on.
How do I prevent the onClick handler for the button from firing when clickOutsideDeactivates: true calls the the onDeactivate function?? The button lies outside the focus trap.
<FocusTrap active={"0" === this.state.active }>
<button>Hello</button>
</FocusTrap>
<FocusTrap active={"1" === this.state.active }>
<button>Hello2</button>
</FocusTrap>
I’m using the active flag to change focus between two FocusTraps.
active == "0" works fine
active == "1" works fine, the first FocusTrap loses focus
active == "0" again, works fine, the first FocusTrap gains focus
active == "1" the focus disappears!
I looked at the source code of focus-trap and can't figure it out.
Thanks.
When I use react server side render by razzle, I encounter this:
ReferenceError: document is not defined
at focusTrap (/dat/react-dat-shopping-list/node_modules/focus-trap/index.js:39:13)
at processChild (/dat/react-dat-shopping-list/node_modules/react-dom/cjs/react-dom-server.node.development.js:3090:14)
at resolve (/dat/react-dat-shopping-list/node_modules/react-dom/cjs/react-dom-server.node.development.js:3013:5)
My code is normal:
//...
render() {
let { show, text, className } = this.props;
return (
<FocusTrap>
<div
onClick={this.closeAlert}
className={className + (show ? " show" : "")}
>
<div className="alertContent">
<div>{text}</div>
<Button label="OK" onClick={this.closeAlert}></Button>
</div>
</div>
</FocusTrap>
);
//...
Im using focus-trap in a modal component. When the modal is closed I need to set the focus on a specific element (using a ref). I have this working in a setTimeout but it doesn't work without. Is this expected or is this a code smell?
Here is my code. The modal is closed and focus is set based on a URL change provided by React Router.
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.buttonRef = React.createRef();
}
componentDidUpdate(prevProps) {
if (this.props.url === '' && prevProps.url = "old-url") {
console.log('target element: ', this.buttonRef.current.firstChild)
// This doenst work if not in a setTimeout
// this.buttonRef.current.firstChild.focus();
setTimeout(() => {
this.buttonRef.current.firstChild.focus();
}, 1);
}
}
Hi @davidtheclark, thanks for the awesome project!
Something we noticed is that when using <FocusTrap />
in an SSR environment, we get the following stack trace:
ReferenceError: Element is not defined
at Object.<anonymous> (node_modules/tabbable/index.js:14:15)
at Object.<anonymous> (node_modules/focus-trap/index.js:1:105)
at Object.<anonymous> (node_modules/y-modal/node_modules/focus-trap-react/dist/focus-trap-react.js:12:23)
This is because of this line https://github.com/davidtheclark/tabbable/blob/e8ecf52617fe97066361356070ae42364e089c8b/index.js#L14 - Element
only exists on window
, and not in a node environment
Clearly this component only really makes sense in a browser environment, and the answer here is the detect when we're in SSR and stub out the component as necessary.
I'm wondering if we could do this automagically behind the scenes tho, so this is transparent to application developers? Maybe we could just render a dumb <div>
(or whatever element was specified) if we're in a SSR environment.
Would you be open to such a PR? Thanks!
@stefcameron how do you feel about moving this project to using yarn?
While building a Gatsby project that uses focus-trap-react, I'm getting this build failure:
Building static HTML failed
See our docs page for more info on this error: https://gatsby.dev/debug-html
160 | onActivate: PropTypes.func,
161 | onDeactivate: PropTypes.func,
> 162 | initialFocus: PropTypes.oneOfType([PropTypes.instanceOf(Element), PropTypes.string, PropTypes.func]),
| ^
163 | fallbackFocus: PropTypes.oneOfType([PropTypes.instanceOf(Element), PropTypes.string, PropTypes.func]),
164 | escapeDeactivates: PropTypes.bool,
165 | clickOutsideDeactivates: PropTypes.bool,
WebpackError: ReferenceError: Element is not defined
- focus-trap-react.js:162 Object.../../node_modules/focus-trap-react/dist/focus-trap-react.js
[dds]/[focus-trap-react]/dist/focus-trap-react.js:162:1
This is because Gatsby uses server-side rendering when doing its production build. See the following two similar issues:
Proposed fix is to add this definition before defining the prop types:
const ElementType = typeof Element === "undefined" ? Function : Element;
and then change each occurrence of PropTypes.instanceOf(Element)
to PropTypes.instanceOf(ElementType)
, which is essentially the same as what was done in remix-run/react-router#6610, however what they did there results in nothing being an instance of function() {}
because it's a new, unique function. By using Function
(which is native to JavaScript), the result is that anything that's a function is an instance of it. That basically renders the type check moot, but I think it's better than having it fail and maybe output some type of warning that you can't fix anyway.
Applying the fix above fixes the issue with Gatsby, and the validation continues to work fine in the browser.
The prop type for allowOutsideClick
is func
:
focus-trap-react/src/focus-trap-react.js
Line 143 in 749f021
This is fine (and mimics the focus-trap
options), but the name implies that it should also accept a boolean. "allows" is one of those words – like "has", "is", and "will" – that indicates to the user that the value is either true
or false
.
Passing true
to the allowOutsideClick
works as expected.
A run-time warning is logged and the option is not active.
In this loop:
focus-trap-react/src/focus-trap-react.js
Lines 31 to 47 in 749f021
We could add a type check like:
if (optionName === 'allowOutsideClick' && typeof specifiedFocusTrapOptions[optionName] === 'boolean') {
tailoredFocusTrapOptions[optionName] = () => specifiedFocusTrapOptions[optionName]
}
Then update the prop types at the bottom to be:
allowOutsideClick: PropTypes.oneOfType([
PropTypes.func,
PropTypes.bool
]),
This is a small patch that would make this little feature a little more usable. I'd be happy to contribute and make this addition, but yield to the maintainers for further discussion.
I didn't see it in the demo and from looking at the code it looks like there is a single var called trap to track the current trap (https://github.com/davidtheclark/focus-trap/blob/master/index.js)
If not, I can probably add that feature.
Very nicely done.
Entirely possible this is already possible and I just haven't seen it in the docs, but is there a way to reactivate a trap upon clicking inside the form?
Test case - https://codesandbox.io/s/9y9lmmko84
A stated use case for React's portals it to avoid overflow issues with things like modals & tooltips. In Elastic UI we use quite a few portals, and have a case where our focused-trapped modal (in a portal) tries to open a focus-trapped context menu (in another portal), but the context menu items won't receive mouse clicks because the on-outside-click detection doesn't understand the portal.
FWIW I don't think this is solvable by focus-trap-react
without underlying changes to react-dom
, but I wanted to start the conversation here. Since outside click detection is often implemented by asking if the parent DOM element contains the even target, I think ReactDOM should expose a similar contains
method that continues through portals, just as event bubbling does.
Any similar change would require focus-trap
to allow a configuration parameter to specify a contains
function, which could retain the current logic as the default for backwards compatibility.
Please let me know your thoughts on this (if the would-be required changes here and focus-trap
make sense, etc), and I'm happy to open the related issue on the React github.
Hi there,
I am running into an issue on IE11 in which the user "closes" the FocusTrap
component and I run into this exception:
Object doesn't support property or method on 'focus'
Looking around the code, I notice I do not pass in focusTrapOptions.returnFocusOnDeactivate
which is checked here in componentWillUnmount()
.
Since that prop is undefined
it actually fails the !== false
comparison and the component attempts to call previouslyFocusedElement.focus
.
I figure I could:
a. Just pass the prop in as false.
b. Change the check to be a little less strict on false
.
c. Provide a default value of false on the prop.
Any thoughts or completely different suggestions?
Thanks for your work on this!
I haven't had much exposure to classic (class based) React, and I'm trying to create a reusable dialog component which makes use of a FocusTrap using this component. I have the FocusTrap as a wrapper around my <Dialog/>
but can't figure out how to set the fallBackFocus attribute of the focusTrap to my <Dialog>
(which is a styled div) - can anybody help?
Heeeeeey, first off, thanks for creating this standalone component! It's been useful for me in creating some modal dialog components.
Unfortunately, my component's original transitions get broken when I wrap the dialog with the FocusTrap
. I use a ref to pass to another component which provides callbacks when transitions start/end, but it seems that this component overrides that ref with another instead of looking for one that is existing.
This is the conditional for the render:
{ariaModal && open
? (<FocusTrap focusTrapOptions={focusTrapOptions}>{renderDialog()}</FocusTrap>)
: renderDialog()
}
This is the behavior without the focus trap:
And this is the behavior with the focus trap:
It would seem to me that it might be trivial to update code that attaches the ref to look for an existing ref, but I've never done that before so it's hard to say. If there is any workaround or anything, I'd love to know! It's also possible that something else is going on as I don't understand why the transitions themselves are broken given that they are specified 100% in the css and only their events are tracked based on the ref. Any insight here is welcome!
EDIT: (renamed issue after discovering root issue) Now that I'm thinking about it, the different conditional renders might be causing the css transform transitions to fail since there is new parent markup. That markup probably causes a brand new render on the dialog whose initial state is open and therefore transitions from nowhere. The only reason I did it that way is that always rendering the focus trap means that the initial mount is the only time the trap grabs the trigger element (so it can be refocused on dialog close). I originally tried to get around this by using state to grab the document.activeElement
when the dialog is set to open and then passing that to the trap component via a setReturnFocus
function, but it didn't seem to get honored on subsequent re-renders when toggling the trap's active
state.
It would seem this library doesn't take into account elements which always exist in a view and are either hidden via display/visibility properties or via positioning properties that shove elements off screen until their "open" state is triggered. Please help! 😓
I would like the focus-trap
to be disabled when I click outside of the container.
I'm using react, so I would like to refrain from doing something like body.click()
etc.
The focus trap is sitting within an <Overlay>
element from react bootstrap, and that closes when you click outside of it, but with focus-trap
that functionality no longer seems to work.
Run E2E testing at each push
and pull-request
on major browsers:
Chrome
cypress supported
Firefox (done with #206)
cypress supported
Having problem running on git action with error msg Browser: 'firefox' was not found on your system or is not supported by Cypress
, see cypress image open issue
Edge(chromium-based)
cypress supported
Edge(EdgeHTML/pre-chromium/IE11?)
Not supported by cypress at this moment. See cypress IE 11 support github issue
Safari
Not supported by cypress at this moment. See cypress macOS Safari support github issue
My scenario: I want to limit focus to the child of FocusTrap
, and a set of CSS selectors. Here's what my code has to look like:
export const MyFocusTrap = props => {
const ref = React.useRef();
const [dummy, setDummy] = React.useState();
React.useEffect(() => {
setDummy(Math.random());
}, []);
// `array` is my own array utils library.
const elts = array.compact(array.flatten([
props.containerCss.map(s => Array.from(document.querySelectorAll(s))),
ref.current
]));
return (
<FocusTrapReact containerElements={elts}>
<div ref={ref}>{props.children}</div>
</FocusTrapReact>
);
};
The issues I see are:
div
to exist at all.containerElements
must be HTMLElements, not selectors, so I have to do a lot of extra work to get them into the right format.Ive noticed that when you are using the active
prop, there is code which ignores the returnFocusOnDeactivate
option. Is this intentional?
I would of thought that the componentDidUpdate
function should be this:
componentDidUpdate(prevProps) {
if (prevProps.active && !this.props.active) {
const { focusTrapOptions } = this.props;
const returnFocus = focusTrapOptions.returnFocusOnDeactivate || false;
this.focusTrap.deactivate({ returnFocus });
} else if (!prevProps.active && this.props.active) {
this.focusTrap.activate();
}
if (prevProps.paused && !this.props.paused) {
this.focusTrap.unpause();
} else if (!prevProps.paused && this.props.paused) {
this.focusTrap.pause();
}
}
that way - when the focus trap is made inactive, then we still get the returnFocus
behaviour.
Im happy to P.R this if I am correct in understanding the intended behaviour.
Cheers.
Id like to bump focus-trap-react to use 4.0.2 of focus-trap. We tested bumping to 4.0.2 in the current version of focus-trap-react to test if it fixes a nested focus trap issue we were having and it looks good.
Ill prepare a P.R for your review.
Hi,
We are using focus-trap-react
in our Modal component.
I noticed that focus-trap-react
is wrapping the childern
inside a div. Unfortunately, this div
is causing a conflict in our css
and it would be helpful if we could pass a specific class
that will align the modal correctly.
Is there a way to pass that as a prop? Or to solve this conflict?
Thanks.
I'm getting this error thrown by the tabbable library which focus-trap-react depends on by way of focus-trap. At first I thought this might be a jest/enzyme problem but I'm also seeing it in non-test code.
TypeError: Cannot read property 'ownerDocument' of null
at tabbable (index.js:21)
at updateTabbableNodes (index.js:267)
at Object.activate (index.js:72)
at FocusTrap.componentDidMount (focus-trap-react.js:55)
I also noticed that the line that's throwing this error is an older version than your most recent update of tabbable...perhaps a version update would help? I haven't tested that yet, but I would love any input you have.
I've used this library before and had really great results with it, so thanks!
You get autoFocus
for free. My understanding is that whenever a focusable element with autoFocus
is added by react, it automatically gets focused.
autoFocus
doesn't work right now with focus-trap-react.
initialFocus
is unspecified, it ignores any autoFocus
elements and focuses the first element.focus-trap-react
throws an error and breaks the component.autoFocus
in the first placefocus-trap-react
provides support for react v0.14.x || v^15.0.0
. React 16 is expected to be released soon and can currently be tested with react 16.0.0-rc
(more information can be found on facebook/react#10294).
focus-trap-react
is a dependency for a few of the packages in the terra-core monorepo, which will need to be React 16 compatible. From my initial assessment of our repository, it seems focus-trap behaves as expected with react v16.0.0-rc
, but confirmed testing is needed.
The clickOutsideDeactivates
option appears to work fine in the regular focus-trap demo page (not focus-trap-react), on all browsers and devices I've tested.
However, when using the focus-trap-react
demo page on my iPad, when I tap the "activate trap" button on the "special element" demo, tapping outside the trap does not deactivate it.
This problem seems to be unique to the react version of the library, and unique to iOS. Testing on Android and on desktop browsers, it works fine. And the normal library's demo of clickOutsideDeactivates
seems to work fine on iOS.
Hi, we have to use allowOutsideClick function provided by v5.0.2 of focus-trap
Possible to upgrade it please?
The version of focus-trap
supported by focus-trap-react
allows you to trap focus to a single element, without exceptions. The latest version of focus-trap
(released today - 6.2) fixes focus-trap/focus-trap#199 , allowing exceptions. Could you update your package.json
to support the latest version?
Thanks!
Steps to reproduce:
Another focusable thing
is never get focusedExpected:
Another focusable thing
should be focus-able
Actual:
This issue found on Firefox 82 (haven't tested on previous versions), but working as expected on Chrome.
There are probably other examples in the demo website which are also not working correctly, I just highlighted the last example.
The following lint issues had to be disabled in order to maintain long backward compatibility to React 16.0:
TL/DR: Using callback refs should clear all these up.
Sooner or later, however, these issues will have to be rectified in order for the code to be compatible with the next major release of React, at which point, we'll have to drop support for < React 16.3 which is when React.createRef()
and callback refs were introduced (AFAICT).
I have consumed this component and it works fine. while writing test cases i m getting this error:
Error: initialFocus
refers to no known node
I actually passed a selector to initialFocus prop. I tried passing a well mounted node which already exists (tested by querying), but i m still getting the same error. Any idea why is this happening?
Found an issue where one trap is incorrectly forced to unpause when another trap is disabled/destroyed.
Here is simple example:
Steps to reproduce:
Click on 'btn 2' to remove second trap.
Result:
Trap 1 (with child 'btn 1') becomes active preventing 'btn 3' from being focused or clicked.
Expectation:
Trap 1 (with child 'btn 1') should remain paused due <FocusTrap paused={true}>
.
'btn 3' should be focusable and clickable.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.