Skip to content

Commit

Permalink
fix: better manage cross-frame relations
Browse files Browse the repository at this point in the history
  • Loading branch information
theKashey committed Aug 25, 2024
1 parent a109314 commit 72b4d4b
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 6 deletions.
4 changes: 2 additions & 2 deletions .size-limit.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
},
{
"path": "dist/es2015/sidecar.js",
"limit": "4.8 KB",
"limit": "4.85 KB",
"ignore": [
"prop-types",
"@babel/runtime",
Expand All @@ -19,7 +19,7 @@
},
{
"path": "dist/es2015/index.js",
"limit": "7.1 KB",
"limit": "7.2 KB",
"ignore": [
"prop-types",
"@babel/runtime",
Expand Down
1 change: 1 addition & 0 deletions src/Lock.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ const FocusLock = React.forwardRef(function FocusLockUI(props, parentRef) {
onDeactivation={onDeactivation}
returnFocus={returnFocus}
focusOptions={focusOptions}
noFocusGuards={noFocusGuards}
/>
)}
<Container
Expand Down
29 changes: 26 additions & 3 deletions src/Trap.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ let tryRestoreFocus = () => null;
let lastPortaledElement = null;

let focusWasOutsideWindow = false;
let windowFocused = false;

const defaultWhitelist = () => true;

Expand Down Expand Up @@ -89,15 +90,16 @@ const withinHost = (activeElement, workingArea) => (
workingArea.some(area => checkInHost(activeElement, area, area))
);

const getNodeFocusables = nodes => getFocusableNodes(nodes, new Map());
const isNotFocusable = node => (
!getFocusableNodes([node.parentNode], new Map()).some(el => el.node === node)
!getNodeFocusables([node.parentNode]).some(el => el.node === node)
);

const activateTrap = () => {
let result = false;
if (lastActiveTrap) {
const {
observed, persistentFocus, autoFocus, shards, crossFrame, focusOptions,
observed, persistentFocus, autoFocus, shards, crossFrame, focusOptions, noFocusGuards,
} = lastActiveTrap;
const workingNode = observed || (lastPortaledElement && lastPortaledElement.portaledElement);

Expand Down Expand Up @@ -125,9 +127,24 @@ const activateTrap = () => {
...shards.map(extractRef).filter(Boolean),
];

const shouldForceRestoreFocus = () => {
// force restoration happens when
// - focus is not inside now
// - focusWasOutside
// - there are go guards
// - the last active element was the first or the last focusable one
if (!focusWasOutside(crossFrame) || !noFocusGuards || !lastActiveFocus || windowFocused) {
return false;
}
const nodes = getNodeFocusables(workingArea);
const lastIndex = nodes.findIndex(({ node }) => node === lastActiveFocus);

return lastIndex === 0 || lastIndex === nodes.length - 1;
};

if (!activeElement || focusWhitelisted(activeElement)) {
if (
(persistentFocus || focusWasOutside(crossFrame))
(persistentFocus || shouldForceRestoreFocus())
|| !isFreeFocus()
|| (!lastActiveFocus && autoFocus)
) {
Expand Down Expand Up @@ -215,7 +232,11 @@ FocusTrap.propTypes = {
children: PropTypes.node.isRequired,
};

const onWindowFocus = () => {
windowFocused = true;
};
const onWindowBlur = () => {
windowFocused = false;
focusWasOutsideWindow = 'just';
// using setTimeout to set this variable after React/sidecar reaction
deferAction(() => {
Expand All @@ -226,12 +247,14 @@ const onWindowBlur = () => {
const attachHandler = () => {
document.addEventListener('focusin', onTrap);
document.addEventListener('focusout', onBlur);
window.addEventListener('focus', onWindowFocus);
window.addEventListener('blur', onWindowBlur);
};

const detachHandler = () => {
document.removeEventListener('focusin', onTrap);
document.removeEventListener('focusout', onBlur);
window.removeEventListener('focus', onWindowFocus);
window.removeEventListener('blur', onWindowBlur);
};

Expand Down
16 changes: 15 additions & 1 deletion stories/Iframe.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,23 @@ export const IFrame = props => (
{' '}
outside
</div>
<hr />
<Trap {...props} />
<hr />

<Trap {...props}>
<iframe src={`/iframe.html?id=focus-lock--codesandbox-example&crossFrame=${props.crossFrame}`} style={{ width: '100%', height: '400px' }} />
<iframe
src={`/iframe.html?id=focus-lock--codesandbox-example&crossFrame=${props.crossFrame}`}
style={{ width: '100%', height: '400px' }}
/>
<iframe
src={`/iframe.html?id=focus-lock--codesandbox-example&crossFrame=${props.crossFrame}`}
style={{ width: '100%', height: '400px' }}
/>
</Trap>
<hr />
<Trap {...props} />
<hr />
<div style={bg}>
{' '}
Inaccessible
Expand Down

0 comments on commit 72b4d4b

Please sign in to comment.