diff --git a/package.json b/package.json index 58105354..6f100996 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/patches/@radix-ui__react-focus-scope@1.1.7.patch b/patches/@radix-ui__react-focus-scope@1.1.7.patch new file mode 100644 index 00000000..13c9d141 --- /dev/null +++ b/patches/@radix-ui__react-focus-scope@1.1.7.patch @@ -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() { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index be2c7ae6..58becb07 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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) diff --git a/test/renderer/components/ui/DialogContentFocusScope.test.tsx b/test/renderer/components/ui/DialogContentFocusScope.test.tsx new file mode 100644 index 00000000..dda40cfa --- /dev/null +++ b/test/renderer/components/ui/DialogContentFocusScope.test.tsx @@ -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; + + 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( + + + {label} + Provider model settings + + + + ); + }; + + expect(() => { + act(() => { + renderDialog('Create team'); + }); + act(() => { + renderDialog('Create team updated'); + }); + }).not.toThrow(); + + expect(document.body.textContent).toContain('Create team updated'); + }); +});