Not sure if this is a bug or if I messed something up. The problem is that I can't reproduce the bug on localhost, and can only reproduce it in prod :/
Tabbing does not work inside the modal. The modal never gains focus, and after I click inside inputs, pressing tab has no effect.
I'm using OSX Catalina, and the same issue happens in Chrome 77 and Firefox 67.
(Sorry I don't have time for a proper demo right now. I can possibly revisit this again to create a demo someday.)
The code for this modal can be found here.
Expected the modal to take focus once it is shown, and for tab to be able to navigate between the focusable elements. Also expect this behavior to be consistent on both localhost and prod.
Figured out my issue. I had the modal inside a wrapper, and I gave that wrapper position: absolute; z-index: 101; because I wanted the modal and the wrapper/backdrop to be positioned over other elements on the page. That resulted in the wrapper having 0 for height and width. I saw in recent commit #774 that scrollHeight is somehow needed by tabbable, so a wrapper with 0 height/width seemed like a problem, so I tried giving the wrapper the following rules:
position: absolute;
z-index: 101;
height: 100vh;
width: 100vw;
And the modal began accepting tab focus after that. I'll go ahead and close this issue.
Actually I'll reopen in case this is a new behavior you want to track.
Thanks for writing this issue.
I just had the same problem because we are using a predefined Portal container for all our Modals, which has always been a div without any height or width and tabbing stopped working on the latest version, and now I understand why 馃槃
Can confirm this was introduced in v3.11.1
Rolling back to v3.10.1 resolves the issue
If you happened to have some portal wrapper this can cause a pretty serious accessibility defect.
Hi guys, just wondering if there's any plan to look into this as I'd say it's pretty commonplace to wrap react-modal to have a reusable Dialog component? And adding height to the wrapper isn't really a solid solution.
same here: i had position: fixed; z-index: 1100; and tab button didnt work. Rolling back to v3.10.1 resolves the issue
I encountered this issue in v3.11 as well. Not sure what the best solution is but tabbing is crucial for me since I have a lot of modals with forms.
Possibly related - sometimes I use 2 modals that layer on top of each other. Even when I roll back to v3.10.1, the tabbing doesn't work in the 2nd modal UNLESS there is an input field somewhere in the 1st modal. Something like this produces the issue in v3.10.1:
<Modal isOpen/>
<input/>
<Modal isOpen/>
// tabbing works here
<input/>
<input/>
</Modal>
</Modal>
<Modal isOpen/>
<Modal isOpen/>
// tabbing does NOT work here
<input/>
<input/>
</Modal>
</Modal>
<Modal isOpen/>
<input style={{display: 'none'}}/>
<Modal isOpen/>
// tried this as a hacky workaround but even this does not work
<input/>
<input/>
</Modal>
</Modal>
<Modal isOpen/>
<input style={{visibility: 'hidden', position: 'absolute'}}/>
<Modal isOpen/>
// finally this workaround does work without affecting styles
<input/>
<input/>
</Modal>
</Modal>
@willsmanley can you create a repo with this examples, please? It will make it easy to debug...
I can confirm that reverting to 3.10.1 fixes the tabbing issue. The issue persists in 3.11.2.
@diasbruno here's a reproduction of this issue:
https://github.com/podverse/sandbox/tree/react-modal/issue-782/demo
You'll need to be on the react-modal/issue-782 branch for the demo.
I had the same issue and was not able to get other solutions working quickly, so I came up with a brute force approach. Make a ref to the container element that holds the focusable elements that you wish to make tabbable.
import React, { useRef} from 'react';
import ReactModalTabbing from '../../yourpath';
const YourComponent = (props) =>{
const formRef = useRef();
return (
<ReactModalTabbing containerRef={formRef}>
<form ref={formRef} onSubmit={handleSubmit} >
<input type="text" />
<input type="text" />
<input type="text" />
<input type="text" />
</form>
</ReactModalTabbing>
);
}
And this is the component
import React, { useState, useEffect } from 'react';
const ReactModalTabbing = ({ containerRef, children }) => {
const [configuredTabIndexes, setConfiguredTabIndexes] = useState(false);
const focusableElements = () => {
// found this function body here https://zellwk.com/blog/keyboard-focusable-elements/
return [...containerRef?.current?.querySelectorAll(
'a, button, input, textarea, select, details, [tabindex]:not([tabindex="-1"]):not([type="hidden"])'
)].filter(el => !el.hasAttribute('disabled'));
}
const isTabbable = (element) =>{
if(element.getAttribute('tabindex')){
return true;
}
return false;
}
const findElementByTabIndex = (tabIndex) => {
return containerRef?.current?.querySelector(`[tabindex="${tabIndex}"]`);
}
const moveFocusToTabIndex = (tabIndex) => {
findElementByTabIndex(tabIndex)?.focus();
}
const handleKeyDownEvent = (event) => {
if(!isTabbable(event.target)){
return;
}
const tabIndex = parseInt(event.target.getAttribute('tabindex'));
if(event.shiftKey && event.key === 'Tab'){
moveFocusToTabIndex(tabIndex - 1);
}else if(event.key === 'Tab'){ //should probably make sure there is no other modifier key pressed.
moveFocusToTabIndex(tabIndex + 1);
}
}
useEffect(() => {
if(!configuredTabIndexes && containerRef.current){
setConfiguredTabIndexes(true);
focusableElements().forEach((el, index) => el.setAttribute('tabindex', index + 1));
containerRef?.current?.addEventListener('keydown', handleKeyDownEvent);
}
});
return children;
}
export default ReactModalTabbing;
Caused by zero-sized div.ReactModalPortal as it's child has position: fixed.
The culprit is tabbable helper that thinks that all inputs inside the modal are invisible and disables tab key behavior.
To fix, set .ReactModalPortal style to:
position: absolute;
height: 1px;
width: 1px;
Most helpful comment
Caused by zero-sized
div.ReactModalPortalas it's child hasposition: fixed.The culprit is tabbable helper that thinks that all inputs inside the modal are invisible and disables tab key behavior.
To fix, set
.ReactModalPortalstyle to: