thekashey / react-focus-on Goto Github PK
View Code? Open in Web Editor NEWπ― Solution for WAI ARIA compatible modal dialogs or full-screen tasks, you were looking for
License: MIT License
π― Solution for WAI ARIA compatible modal dialogs or full-screen tasks, you were looking for
License: MIT License
Hi @theKashey π
I used this library on a bunch of projects, it's really great except for one small thing: onClickOutside
. I struggle to make it works on mobile because it doesn't prevent the initial event
.
To explain my pain, suppose that we have a Profile Button which open a profile form modal:
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ββββββββββ
β βProfileββ
β ββββββββββ
β ββββββββββββββββββββββββββββ β
β β β β
β β β β
β β β β
β β β β
β β β β
β β Profile form modal β β
β β β β
β β β β
β β β β
β β β β
β β β β
β ββββββββββββββββββββββββββββ β
β β
β β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
If I choose to close my modal on click outside, it's easy, but it will not works if I click on the profile button (it will close on reopen the modal immediately).
Hi! π
Firstly, thanks for your work on this project! π
Today I used patch-package to patch [email protected]
for the project I'm working on.
Here is the diff that solved my problem:
diff --git a/node_modules/react-focus-on/dist/es2015/Effect.js b/node_modules/react-focus-on/dist/es2015/Effect.js
index 06fe55c..1994a65 100644
--- a/node_modules/react-focus-on/dist/es2015/Effect.js
+++ b/node_modules/react-focus-on/dist/es2015/Effect.js
@@ -50,15 +50,15 @@ export function Effect(_a) {
mouseTouches.current = event.touches.length;
};
if (activeNode) {
- document.addEventListener('keydown', onKeyDown);
- document.addEventListener('mousedown', onMouseDown);
- document.addEventListener('touchstart', onTouchStart);
- document.addEventListener('touchend', onTouchEnd);
+ document.addEventListener('keydown', onKeyDown, { capture: true });
+ document.addEventListener('mousedown', onMouseDown, { capture: true });
+ document.addEventListener('touchstart', onTouchStart, { capture: true });
+ document.addEventListener('touchend', onTouchEnd, { capture: true });
return function () {
- document.removeEventListener('keydown', onKeyDown);
- document.removeEventListener('mousedown', onMouseDown);
- document.removeEventListener('touchstart', onTouchStart);
- document.removeEventListener('touchend', onTouchEnd);
+ document.removeEventListener('keydown', onKeyDown, { capture: true });
+ document.removeEventListener('mousedown', onMouseDown, { capture: true });
+ document.removeEventListener('touchstart', onTouchStart, { capture: true });
+ document.removeEventListener('touchend', onTouchEnd, { capture: true });
};
}
}, [activeNode, onClickOutside, onEscapeKey]);
This issue body was partially generated by patch-package.
Hi, I'm using this library and so far it's been working great but unfortunately I need noFocusGuards
prop which is currently not exposed.
Happy to open a pull request, but I wanted to ask your preference:
Allow noFocusGuards
prop only.
Hi there i'm trying to add some styling to the div that is rendered using styled-components
- but no className
prop seems to be passed down to the underlying div.
I'm currently experimenting with react-focus-on in a project of mine to see if that works as I need it. So far, it did what I expected, but I cannot get one thing to work: onClickOutside
is never triggered for me, so I cannot automatically close popups. I'm testing on Firefox mostly, but I guess this doesn't play a role here.
My portal render code is this:
public render(): React.ReactNode {
const { children, container = document.body } = this.props;
const { open, options } = this.state;
const className = this.getEffectiveClassNames([
"portal",
]);
const element = <div ref={this.contentRef}>
{children}
</div>;
return (
open && createPortal(
<FocusOn
shards={[this.contentRef]}
className={className}
enabled={true}
scrollLock={true}
focusLock={true}
autoFocus={true}
returnFocus={true}
onClickOutside={this.handlClickOutside}
onEscapeKey={this.handleEscapeKey}
>
{element}
</FocusOn>, container,
)
);
}
First I rendered the children directly in the portal element, then I tried to wrap it with a div and use the ref of that for the shards property. But that didn't change anything. I'm assuming here that shards
plays a role in this misfunction, but I could be completely wrong.
Can you please tell me how this should be implemented actually? The documentation is pretty sparse overall. Thanks.
Hi @theKashey,
After doing some research, I found another behaviour similar to the work we did on the escape key behaviour (#15).
Similarly here, looking at what the OS does for all types of modal layers (things like save system menus, app menus, right click menus, open selects, etc) click outside always executes instantly as soon as pressing down, on mousedown
basically.
I believe we should also bind to mousedown
rather than click
?
Small change again, but I think offers much better consistency.
Let me know your thoughts, I'll send you a PR for it as well in a minute.
Cheers π
Im seeing performance logs in the latest version e.g,
initialization: 0.014999997802078724
Combination.js:7 initialization: 0.010000003385357559
Combination.js:7 initialization: 0.0050000089686363935
Combination.js:7 initialization: 0.014999997802078724
2Combination.js:7 initialization: 0.004999994416721165
2Combination.js:7 initialization: 0.004999994416721165
Combination.js:7 initialization: 0.0050000089686363935
Combination.js:7 initialization: 0.004999994416721165
Combination.js:7 initialization: 0.0050000089686363935
Combination.js:7 initialization: 0.00999998883344233
from Combination.js
import * as tslib_1 from "tslib";
import * as React from 'react';
import { FocusOn as ReactFocusOn } from "./UI";
var RequireSideCar = function (props) {
var now = performance.now();
var SideCar = require('./sidecar').default;
>>>> console.log('initialization:', performance.now() - now);
return React.createElement(SideCar, tslib_1.__assign({}, props));
};
export function FocusOn(props) {
return (React.createElement(ReactFocusOn, tslib_1.__assign({}, props, { sideCar: RequireSideCar })));
}
I have FocusOn up and running. When I click outside of the focused area, it's running onClickOutside
and also clicking on the item in the background. Typically I'd just add something like e.preventDefault();
but that's not working. How do I get it to close FocusOn, but not also click something in the background?
Currently, the repository description is The final solution for WAI ARIA compatible modal dialogs or full-screen tasks.
. Without going into the details, the term "final solution" has a terrible connotation.
May I suggest changing this to something like "the ultimate solution"?
After upgrading i get the following error in production mode
Error: Sidecar medium not found
I'd love to be able to remove the scroll bar but can't seem to access this prop. Would be nice to have it available!
Awesome package, does basically everything I need aside from this.
Any chance react-focus-on's react-focus-lock dependency will be bumped?
I really need the theKashey/react-focus-lock#108 (tabindex="-1"
) fix
I'm getting [email protected]
but need [email protected]
Thanks in advance!
π¨ You need to enable Continuous Integration on all branches of this repository. π¨
To enable Greenkeeper, you need to make sure that a commit status is reported on all branches. This is required by Greenkeeper because it uses your CI build statuses to figure out when to notify you about breaking changes.
Since we didnβt receive a CI status on the greenkeeper/initial
branch, itβs possible that you donβt have CI set up yet. We recommend using Travis CI, but Greenkeeper will work with every other CI service as well.
If you have already set up a CI for this repository, you might need to check how itβs configured. Make sure it is set to run on all new branches. If you donβt want it to run on absolutely every branch, you can whitelist branches starting with greenkeeper/
.
Once you have installed and configured CI on this repository correctly, youβll need to re-trigger Greenkeeperβs initial pull request. To do this, please delete the greenkeeper/initial
branch in this repository, and then remove and re-add this repository to the Greenkeeper Appβs white list on Github. You'll find this list on your repo or organizationβs settings page, under Installed GitHub Apps.
im seeing lots of logging in my console with the latest release like this:
initialization: 0.0050000089686363935
Despite being documented in the main README, setting gapMode="padding"
as a prop does not appear to correctly change the CSS applied to the page body
to set padding-right
instead of margin-right
(which the underlying react-remove-scroll-bar library implies it should).
gapMode
usage appears to be nonexistent, and its original implementation no longer exists in the codebase.
Demo of broken behavior: https://codesandbox.io/s/romantic-jennings-tv3l31?file=/demo.js:397-444
Notice that even with gapMode="padding"
passed, when the focus lock is open, the CSS rendered is still using margin-right:
A bit more context:
Our team could really use support for this, as unfortunately one of our major consumers has width: 100%; min-width: 100%
CSS set on their body, which causes margin-right
to not work as expected. We don't really have standing to tell them to remove this CSS, so the only other alternative to use padding-right
instead (which works correctly with 100% width).
As a workaround we're currently grabbing the --removed-body-scroll-bar-size
CSS var and manually setting padding-right
, but we'd love to be able to remove this workaround.
Hi,
I am using a react-focus-lock in a nextjs app and I am running an issue with the no shadowroot error in
next.js frameworks.
react-focus-on
have a dependency on react-focus-lock
which relies on focus-lock
.
There is a bug in focus-lock":"0.11.5 which causes unit tests to fail with no shadowroot error in
next.js frameworks. The issue is resolved in [email protected]
, but requires bumping [email protected]
to resolve the issue in this package.
Hey again @theKashey! π We just got a question about iframe behavior from one of our consumers. While I was looking for a solution or workaround, I noticed that react-focus-lock
supports a crossFrame
prop that was added a few years ago (theKashey/react-focus-lock#104) and would likely be exactly what we need.
Is there any chance we could extend FocusOn
to allow the same crossFrame
configuration and pass that to FocusLock
? π That would be incredibly helpful to us. Would you accept a PR with this addition?
After updating to 3.8.0
I am getting the following warning:
Warning: React does not recognize the `gapMode` prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase `gapmode` instead. If you accidentally passed it from a parent component, remove it from the DOM element.
at div
at /home/timo/Code/asti/my-app/node_modules/react-remove-scroll/dist/es5/UI.js:16:21
at FocusLockUI (/home/timo/Code/asti/my-app/node_modules/react-focus-lock/dist/cjs/Lock.js:40:31)
at /home/timo/Code/asti/my-app/node_modules/react-focus-on/dist/es5/UI.js:10:20
at div
at Modal (/home/timo/Code/asti/my-app/src/components/Modal/Modal.tsx:12:34)
Although the source is authored with ES-Modules, there is a require
statement inside Combination.tsx
. Mixing both import
statements and require
statements is invalid according to the spec.
react-focus-on/src/Combination.tsx
Line 7 in 7640331
It seems to me that this code should load the component lazily and makes use of a webpack specific behaviour which will load that code lazily. Nowadays it probably should use the standardized import()
call instead.
FYI: There is a workaround for the invalid portion by setting the transformMixedEsModules: true
for @rollup/plugin-commonjs
, but that just turns it into a synchronous import.
After running npm install
on a project with dependencies on multiple major versions of tslib
(i.e., ^1.0.0 and ^2.0.0), I'm getting the following error:
ERROR in: ./node_modules/react-focus-on/dist/es2015/Combination.js
Module not found: Error: Can't resolve 'tslib' in '<path>/node_modules/react-focus-on/dist/es2015'
Should tslib
be declared as a dependency?
[Intervention] Ignored attempt to cancel a touchmove event with cancelable=false
Possible fix: if (event.cancelable) event.preventDefault();
Would it be possible to have a new version of this library, with the latest versions of its dependencies? Looks like react-focus-lock recently got support, and it would be awesome to have it in react-focus-on as well!
I have modals that contain links and forms (with some custom validation) that stopped working when I added FocusOn to them. Digging deeper, it appears this onClick
listener that's passed to focus-lock is the culprit https://github.com/theKashey/react-focus-on/blob/master/src/component.tsx#L33. Unfortunately removing it causes any clicks within the modal to trigger a the onClickOutside
callback.
Any tips for moving forward without having to add custom click handlers to everything?
I am trying to use react-focus-on with the as= prop to render my own nav without it being nested inside a div. As seen here:
however, it doesnt seem to be exposed from react-focus-lock.
EDIT: Have done a little digging and it seems internally the as is being used as RemoveScroll, and RemoveScroll is using a div, maybe we could pass through the as prop to removeScroll and that renders the component if provided?
Ive tried to use and implement forwardProps and that removed the div but then the lock didnt work.
Hello @theKashey,
I have realised that onClickOutside
and onEscapeKey
doesn't update callbacks.
I did some research, and I think it because useEffect
in Effect.tsx
doesn't get deps for update those funcs (here)
Or maybe there is another way to do this?
The following code doesn't work in IE:
var code = event.key || event.keyCode;
if ((event.code === 'Escape' || code === 'Escape' || code === 27) && _this.props.onEscapeKey) {
_this.props.onEscapeKey(event);
}
it should be:
if ((event.code === 'Escape' || event.key === 'Escape' || event.keyCode === 27) && _this.props.onEscapeKey) {
_this.props.onEscapeKey(event);
}
IE11 sets event.key
to "Esc"
so the keyCode
is skipped and thereby ignored.
It seems to work correctly in Edge 18.
Started getting the following warnings after upgrading:
Warning: Unknown event handler property `onActivation`. It will be ignored.
Warning: Unknown event handler property `onDeactivation`. It will be ignored.
Currently in my app I need to pass persistentFocus prop to react-focus-lock and noRelative prop to react-scroll-locky. Would it be possible to add either these extra props or possibility to pass all react-focus-lock and react-scroll-locky props?
In one of our components, we have a prop which allows you to be able to disable/enable scrolling while it's open. We'd like to keep this available for backwards compatibility.
What I propose is a prop like disableScrollLock
to achieve this.
Hi!
I use react-focus-on in my app for implementing proper modal dialogs. Discovered an issue on iOS: when dialog contains a long form and user clicks on input, Safari puts focus on it, pops up keyboard and shifts viewport up so that the input stays in the viewport above the keyboard. The problem is that locked document scroll is also affected by this behaviour. Iβve set up a reproduction sandbox and made a recording demonstrating the issue:
By the end of the recording you can see the bottom of the page. Is it possible to prevent document scroll in this case?
Hi,
How can I prevent pointer-events: none
being applied to external resources(non React elements)?
Example:
I have a sticky chat button loaded via Google tag manager. It renders outside root. Is there a way to apply it to shards
somehow? or even better, prevent adding pointer-events: none
to anything outside #root
when <FocusOn>
is enabled?
As of 2.7.0, react-focus-lock has supported a focusOptions
prop, primarily for allowing consumers to pass preventScroll
to the initial focus event. react-focus-on unfortunately has not exposed nor is passing that prop directly to react-focus-lock
, which potentially leads to scroll jumping issues that cannot be overridden (see theKashey/react-focus-lock#162 for more info).
It should be relatively trivial to pull focusOptions
from props and pass it directly to <ReactFocusLock>
here: https://github.com/theKashey/react-focus-on/blob/master/src/UI.tsx#L49
Hello π !
I'm running into unit test issues that fail with no shadowRoot
. From what I can gather, it is similar to issues described in react-focus-lock
:
I was able to get this working by resolving focus-lock
to v0.11.6
in my package.json. Is it possible to upgrade react-focus-lock
to the latest version (v2.9.4
)? It looks like I'm unable to open up a PR with my local branch.
So I have a modal with a copy-to-clipboard functionality, using sindresorhus/copy-text-to-clipboard. This consistently works as long as my modal isnβt wrapped in react-focus-on
. With it, copying consistently stops working in Chrome and Firefox. In Safari it continues to work, somehow. I donβt experience this problem at all using the standalone react-focus-lock
.
Iβm not entirely sure whatβs going on, but I think the way copying to clipboard works in some browsers, is that an event (maybe the one proving that a user did indeed interact and that the code should be allowed to manipulate the OSβ clipboard?) propagates all the way up to document
?
Iβm guessing this is some sort of side effect of stopping event propagation from scroll events in the scroll lock part of this utility, so if itβs possible to pinpoint these events more specifically and leave others alone, thatβd probably be an improvement.
I'm rendering a canvas with interactive elements that take keyboard input inside a FocusOn container. With the focusLock enabled, I am unable to add any input to these elements. I believe it makes sense since they aren't actually focusable text inputs but I haven't found a way to work around this. I was hoping to find some way to make the focus lock "ignore" that area but haven't succeeded.
P.S. Thanks for the awesome library.
Similar to how className
is applied, style
could be applied to the main div
(the one with the data-focus-lock-disabled
attribute).
Happy to open a pull request, but wanted to ask your preference:
style
prop only.lockProps
prop similar to react-focus-lock
where any valid HTML or React attribute can be applied.After switching to react-focus-on
from react-focus-lock
this has been the only API gap. Thanks for your work!
Shard values are not always exists by the time of the lock activation, and that is not expected.
Hi there, thanks for making this package it's really handled a lot of the situations I was manually catering for.
One issue I have with it is that I want scroll lock enabled, to prevent the page behind a modal scrolling, but I also want multi-touch zoom and swipe to go back to work. Is there currently any flags that will allow this while maintaining the scroll-lock for the rest of the page?
I have a modal inside a Portal if that's relevant to it.
Please update the aria-hidden dependency version to 1.1.3 as the previous versions on IE11 will crash if the DOM tree contains e.g. SVGElement(s).
Please note, the update will fix the crash, but won't fix aria-hidden to function perfectly on IE11.
If an onKeyDown
handler causes FocusOn
to render and the first focusable element has an onKeyUp
or onClick
handler, that handler will also be called.
Reproduced here: https://codesandbox.io/s/wizardly-vaughan-2y8in
Doesn't happen when focusing using refs: https://codesandbox.io/s/naughty-curie-kmmu8
Hi @theKashey,
Looking at what the OS does for all types of modal layers (things like save dialog, confirmation dialogs, open menus, open selects, etc) close on escape always executes instantly as soon as touching the escape key, on keydown
basically.
https://github.com/theKashey/react-focus-on/blob/master/src/Effect.tsx#L66
We are bound to keyup
here which puts us a bit behind in terms of timing, and offers a different UX.
I believe we should also bind to keydown
rather than keyup
?
Small change, but I think offers much better consistency.
Let me know your thoughts, I can do a PR if needed, although I doubt you need one considering the change.
Cheers π
When using a radio button group inside FocusOn, keyboard tabbing is broken. When the focus is on a radio button the focus get "stuck" and doesn't move to the next element when pressing tab.
In the test case below, when the focus trap is enabled:
When the first radio button has focus, tabbing doesn't do anything. Shift + tab works and the focus is set to the button.
When the second radio button has focus, tabbing doesn't do anything. Shift + tab doesn't work either.
When the last radio button has focus, tabbing and shift + tab both work as expected.
Compare it to the second example with checkboxes which work correctly.
I have an implementation where I'm using react-focus-on with react-transition-group. The idea is that as I transition in some JSX I lock focus to the area I need. The initial load is fine but when the section is closed but clicking outside or escape key, the elements that were hidden still have the aria-hidden attributes as well the button having pointer-events: none
.
https://codesandbox.io/s/dry-wind-4h4tt?file=/src/App.js
Is there a way to undo the aria-hidden attributes that were set? Or perhaps a suggestion on a different approach?
Hello! I recently added this library to add accessibility features to a modal dialog component (and it works like a charm, thanks!) but I'm having some issues with the tests that use that component. The tests are now failing with the error Sidecar medium was not found
.
Using react-testing-library and jest. The test also gives some warnings about updates to sidecar not being wrapped inside an act
function, which they are so that seems strange too
Warning: An update to Sidecar inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
Unfortunately the error messages aren't very helpful so I would appreciate some advice
Thanks
Sometimes you want to be able to lock and unlock before the component has fully unmounted, e.g. when doing animations.
To illustrate my problem I've created a simple codesandbox with react-spring.
https://codesandbox.io/s/runtime-lake-76g5v
When you open the dialog everything works as expected, however when you close it you can notice a obvious delay. The focus isn't returned until the component has fully unmounted and the scroll is locked too. This behavior isn't ideal imo.
Is there some way to solve this without any API change?
Hi,
First, thanks for this great library.
I was wondering why returnToFocus
was hardcoded as true and not passed as an option to FocusLock
like the others. (see https://github.com/theKashey/react-focus-on/blob/master/src/UI.tsx#L37)
The use case I am trying to provide is, by default let your library handle the return focus, but if the user passes a custom ref to focus when the modal gets close, focus on that instead.
The issue is that for this to work, I have to write my ref.focus()
in something like setImmediate
or requestAnimationFrame
to wait for the normal returnToFocus
to do its thing first (I'm doing this in onDeactivation
callback). Instead I would like to set returnToFocus={false}
when a custom ref is passed into my component.
I hope this make sense.
I tested it with react-focus-lock
and it worked well by setting returnToFocus={false}
and focusing my own thing using onDeactivation
callback.
Thanks π
With the addition of react-focus-on
, we got this strange behavior of not being able to select any text inside the FocusOn
component.
user-select
. pointer-events
is set to auto
but that shouldn't stop text-selectionI couldn't figure out what caused the issue. What could be the reason?
Hi @theKashey
Since #19 got merged and 3.1.1
got released, the activation lifecycle is not working properly.
I haven't managed to specifically find out why, but he's a simple reproduction:
Working activation lifecycle
https://codesandbox.io/s/react-focus-on-310-wg4js
Activation lifecycle not working correctly
https://codesandbox.io/s/react-focus-on-311-3rz2r
Both examples are exactly the same, only the version of react-focus-on
differs.
Pay attention to the logs:
activated
, and when closing you get deactivated
deactivated
and activated
, and when closing you get nothingThis is a breaking issue for us and I believe we should rollback that PR (#19) until we can figure out a way to incorporate the intended fix (#17) without breaking the lifecycle.
Let me know what you think!
βοΈ
Hi @theKashey,
Whilst implementing a modal dialog using react-focus-on
I have realised that the onClickOutside
callback doesn't get triggered on iOS (possibly all touch devices?)
I have done some investigation and I saw that the lib attaches a click
handler on the document
in Effect.tsx (here).
However, after some investigation, it appears iOS doesn't trigger any click
events on nodes unless they "look" clickable (see this comment).
I have a fix for it (basically attaching a touchstart
handler on top of the click
handler).
I will send you a PR and we can continue the discussion there.
βοΈ
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.