Giter Club home page Giter Club logo

focus-trap-react's Issues

IE11 focus is not trapped when SVG is first focusable element

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.

Possible to use with contentEditable?

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?

focus-trap version bump

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)?

Updated to react 16 and getting Error

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.

`this.node.current` is null.

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.

Error: Uncaught [Error: Your focus-trap needs to have at least one focusable element]

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.

package.txt
focustest.txt

Types in index.d.ts not resolved

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.

Using this in a React 15.5 application causes deprecation warnings

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

"No focusable element" error thrown if children are hidden.

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!

Demo page appears to be broken

Looking at the demo page at http://davidtheclark.github.io/focus-trap-react/demo/, I notice a few issues:

  • In the "first focus, no escape" demo, the "activate trap" button does nothing. The button disappears, and nothing appears.
  • the "special element" and "demo autofocus" demos do not appear to render at all.

Notably, when I build and run the demo page locally, everything works fine.

Option: `setReturnFocus` is not being respected on component unmount

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.

Use React Testing Library for unit tests

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!

Seeking co-maintainers!

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.

Use Jest for testing

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.

Listener memory leak

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?

Button that toggles focus trap when clickOutsideDeactivates: true does not work

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.

Strange bug

<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.

ReferenceError: document is not defined

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>
    );
   //...

Change focus after focus-trap unmounts requires setTimeout

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);
    }
  }

<FocusTrap /> fails to render when doing SSR

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!

New prop types do not work well with server-side rendering

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.

Option `allowOutsideClick` should accept a boolean

Description

The prop type for allowOutsideClick is func:

allowOutsideClick: PropTypes.func,

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.

Expected behavior

Passing true to the allowOutsideClick works as expected.

Actual behavior

A run-time warning is logged and the option is not active.

Possible solution

In this loop:

for (const optionName in specifiedFocusTrapOptions) {
if (
!Object.prototype.hasOwnProperty.call(
specifiedFocusTrapOptions,
optionName
)
) {
continue;
}
if (optionName === 'returnFocusOnDeactivate') {
continue;
}
tailoredFocusTrapOptions[optionName] =
specifiedFocusTrapOptions[optionName];
}

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
]),

Conclusion

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.

clickInsideReactivates or equiv?

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?

Trap doesn't work if the children are rendered into a React portal

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.

Object doesn't support property or method 'focus'

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!

Problems resetting trigger element focus for always-rendered elements

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:
modal_dialog_no_focus_trap

And this is the behavior with the focus trap:
modal_dialog_with_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! 😓

Focus trap not disabled when clicking outside the container

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.

E2E testing on major browsers

Run E2E testing at each push and pull-request on major browsers:

New containerElements option doesn't lend itself to React

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:

  1. In order to include the child, I have to use a ref. I don't want that div to exist at all.
  2. Because the ref is necessary, I have to use dummy state to force the component to render twice on mount. Otherwise, the ref wouldn't be added to the elements list. This is the biggest issue, because it causes a potentially significant performance hit.
  3. containerElements must be HTMLElements, not selectors, so I have to do a lot of extra work to get them into the right format.

returnFocusOnDeactivate does not work when using active prop

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.

Bump focus-trap-react to use focus-trap 4.0.2

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.

Add className to focus-trap div

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.

Cannot read property 'ownerDocument' of null

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!

autoFocus and initialFocus

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.

  • If initialFocus is unspecified, it ignores any autoFocus elements and focuses the first element.
  • If you pass a selector that matches no elements, focus-trap-react throws an error and breaks the component.
  • If you pass a valid selector, then you're not taking advantage of the convenience and simplicity of autoFocus in the first place

Add React 16 Support

focus-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.

clickOutsideDeactivates option does not work on iOS devices

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.

Last example on demo website is not working on Firefox

Steps to reproduce:

  1. Open Firefox (I used version 82)
  2. Go to http://focus-trap.github.io/focus-trap-react/demo/
  3. Open the last example
  4. Press tab multiple times, notice that Another focusable thing is never get focused

Expected:
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.

Code base needs to be modernized to drop findDOMNode and string refs

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).

Error: `initialFocus` refers to no known node in Enzyme

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?

Focus trap should not be forced to 'unpause' when another trap is disabled

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.