fix(ui): patch radix focus scope ref loop
This commit is contained in:
parent
7c3c852520
commit
a3a286c652
4 changed files with 103 additions and 6 deletions
|
|
@ -353,7 +353,8 @@
|
|||
"cpu-features"
|
||||
],
|
||||
"patchedDependencies": {
|
||||
"@radix-ui/react-presence@1.1.5": "patches/@radix-ui__react-presence@1.1.5.patch"
|
||||
"@radix-ui/react-presence@1.1.5": "patches/@radix-ui__react-presence@1.1.5.patch",
|
||||
"@radix-ui/react-focus-scope@1.1.7": "patches/@radix-ui__react-focus-scope@1.1.7.patch"
|
||||
}
|
||||
},
|
||||
"knip": {
|
||||
|
|
|
|||
38
patches/@radix-ui__react-focus-scope@1.1.7.patch
Normal file
38
patches/@radix-ui__react-focus-scope@1.1.7.patch
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
diff --git a/dist/index.js b/dist/index.js
|
||||
index c91ae9196280060974778cbb1164839d5610e7d0..a2dd82afe79d7d0a6640e983166b4b205686dae9 100644
|
||||
--- a/dist/index.js
|
||||
+++ b/dist/index.js
|
||||
@@ -58,7 +58,13 @@ var FocusScope = React.forwardRef((props, forwardedRef) => {
|
||||
const onMountAutoFocus = (0, import_react_use_callback_ref.useCallbackRef)(onMountAutoFocusProp);
|
||||
const onUnmountAutoFocus = (0, import_react_use_callback_ref.useCallbackRef)(onUnmountAutoFocusProp);
|
||||
const lastFocusedElementRef = React.useRef(null);
|
||||
- const composedRefs = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, (node) => setContainer(node));
|
||||
+ const containerRef = React.useRef(null);
|
||||
+ const setContainerRef = React.useCallback((node) => {
|
||||
+ if (containerRef.current === node) return;
|
||||
+ containerRef.current = node;
|
||||
+ setContainer(node);
|
||||
+ }, []);
|
||||
+ const composedRefs = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, setContainerRef);
|
||||
const focusScope = React.useRef({
|
||||
paused: false,
|
||||
pause() {
|
||||
diff --git a/dist/index.mjs b/dist/index.mjs
|
||||
index e39d5c9105b3f8060d037bf5490843d20d1c859a..70781360acc81bff33c36b8ebd8d6b278df58450 100644
|
||||
--- a/dist/index.mjs
|
||||
+++ b/dist/index.mjs
|
||||
@@ -22,7 +22,13 @@ var FocusScope = React.forwardRef((props, forwardedRef) => {
|
||||
const onMountAutoFocus = useCallbackRef(onMountAutoFocusProp);
|
||||
const onUnmountAutoFocus = useCallbackRef(onUnmountAutoFocusProp);
|
||||
const lastFocusedElementRef = React.useRef(null);
|
||||
- const composedRefs = useComposedRefs(forwardedRef, (node) => setContainer(node));
|
||||
+ const containerRef = React.useRef(null);
|
||||
+ const setContainerRef = React.useCallback((node) => {
|
||||
+ if (containerRef.current === node) return;
|
||||
+ containerRef.current = node;
|
||||
+ setContainer(node);
|
||||
+ }, []);
|
||||
+ const composedRefs = useComposedRefs(forwardedRef, setContainerRef);
|
||||
const focusScope = React.useRef({
|
||||
paused: false,
|
||||
pause() {
|
||||
|
|
@ -9,6 +9,9 @@ overrides:
|
|||
uuid: ^11.1.1
|
||||
|
||||
patchedDependencies:
|
||||
'@radix-ui/react-focus-scope@1.1.7':
|
||||
hash: 7804f20913d10756d0a4eb0a19936c4caffcfbd0142137ea30c880624940344d
|
||||
path: patches/@radix-ui__react-focus-scope@1.1.7.patch
|
||||
'@radix-ui/react-presence@1.1.5':
|
||||
hash: afe90f800cfb3b1ce1a9c457772e2441a9202e1aa3f8658eb3b9613b3ba0ef7e
|
||||
path: patches/@radix-ui__react-presence@1.1.5.patch
|
||||
|
|
@ -13989,7 +13992,7 @@ snapshots:
|
|||
'@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.4)
|
||||
'@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.14)(react@19.2.4)
|
||||
'@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@radix-ui/react-focus-scope': 1.1.7(patch_hash=7804f20913d10756d0a4eb0a19936c4caffcfbd0142137ea30c880624940344d)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.4)
|
||||
'@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@radix-ui/react-presence': 1.1.5(patch_hash=afe90f800cfb3b1ce1a9c457772e2441a9202e1aa3f8658eb3b9613b3ba0ef7e)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
|
|
@ -14044,7 +14047,7 @@ snapshots:
|
|||
optionalDependencies:
|
||||
'@types/react': 19.2.14
|
||||
|
||||
'@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
|
||||
'@radix-ui/react-focus-scope@1.1.7(patch_hash=7804f20913d10756d0a4eb0a19936c4caffcfbd0142137ea30c880624940344d)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
|
||||
dependencies:
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4)
|
||||
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
|
|
@ -14097,7 +14100,7 @@ snapshots:
|
|||
'@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.4)
|
||||
'@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.14)(react@19.2.4)
|
||||
'@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@radix-ui/react-focus-scope': 1.1.7(patch_hash=7804f20913d10756d0a4eb0a19936c4caffcfbd0142137ea30c880624940344d)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.4)
|
||||
'@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
|
|
@ -14121,7 +14124,7 @@ snapshots:
|
|||
'@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.4)
|
||||
'@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.14)(react@19.2.4)
|
||||
'@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@radix-ui/react-focus-scope': 1.1.7(patch_hash=7804f20913d10756d0a4eb0a19936c4caffcfbd0142137ea30c880624940344d)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.4)
|
||||
'@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
|
|
@ -14220,7 +14223,7 @@ snapshots:
|
|||
'@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.4)
|
||||
'@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.14)(react@19.2.4)
|
||||
'@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@radix-ui/react-focus-scope': 1.1.7(patch_hash=7804f20913d10756d0a4eb0a19936c4caffcfbd0142137ea30c880624940344d)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.4)
|
||||
'@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
|
|
|
|||
55
test/renderer/components/ui/DialogContentFocusScope.test.tsx
Normal file
55
test/renderer/components/ui/DialogContentFocusScope.test.tsx
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import React, { act } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogTitle,
|
||||
} from '@renderer/components/ui/dialog';
|
||||
|
||||
describe('DialogContent FocusScope integration', () => {
|
||||
let host: HTMLDivElement;
|
||||
let root: ReturnType<typeof createRoot>;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
|
||||
host = document.createElement('div');
|
||||
document.body.appendChild(host);
|
||||
root = createRoot(host);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
act(() => {
|
||||
root.unmount();
|
||||
});
|
||||
document.body.innerHTML = '';
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
it('keeps the Radix focus scope stable while an open dialog rerenders', () => {
|
||||
const renderDialog = (label: string): void => {
|
||||
root.render(
|
||||
<Dialog open>
|
||||
<DialogContent>
|
||||
<DialogTitle>{label}</DialogTitle>
|
||||
<DialogDescription>Provider model settings</DialogDescription>
|
||||
<button type="button">Focusable action</button>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
expect(() => {
|
||||
act(() => {
|
||||
renderDialog('Create team');
|
||||
});
|
||||
act(() => {
|
||||
renderDialog('Create team updated');
|
||||
});
|
||||
}).not.toThrow();
|
||||
|
||||
expect(document.body.textContent).toContain('Create team updated');
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue