From 17d963cfa0447ab6bcbcd0948cee7e51f30e5101 Mon Sep 17 00:00:00 2001 From: aojunhao123 <1844749591@qq.com> Date: Wed, 8 Apr 2026 17:05:12 +0800 Subject: [PATCH] feat: add focusTrap prop to preview config Add `focusTrap` option to `InternalPreviewConfig` (default `true`). When set to `false`, the preview will not trap focus via `useLockFocus`. This aligns with rc-dialog and rc-drawer which both support controlling focus trapping behavior. The main use case is rendering an always-open preview inline (e.g. for documentation semantic demos), where the global focus lock incorrectly prevents keyboard interaction with the rest of the page. --- src/Preview/index.tsx | 7 ++++++- tests/preview.test.tsx | 24 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/Preview/index.tsx b/src/Preview/index.tsx index 065fdb1..44106ab 100644 --- a/src/Preview/index.tsx +++ b/src/Preview/index.tsx @@ -89,6 +89,10 @@ export interface InternalPreviewConfig { zIndex?: number; afterOpenChange?: (open: boolean) => void; + // Focus + /** Whether to trap focus within the preview when open. Default is true. */ + focusTrap?: boolean; + // Operation movable?: boolean; icons?: OperationIcons; @@ -193,6 +197,7 @@ const Preview: React.FC = props => { styles = {}, mousePosition, zIndex, + focusTrap = true, } = props; const imgRef = useRef(); @@ -397,7 +402,7 @@ const Preview: React.FC = props => { } }, [open]); - useLockFocus(open && portalRender, () => wrapperRef.current); + useLockFocus(focusTrap && open && portalRender, () => wrapperRef.current); // ========================== Render ========================== const bodyStyle: React.CSSProperties = { diff --git a/tests/preview.test.tsx b/tests/preview.test.tsx index f81744c..c40a293 100644 --- a/tests/preview.test.tsx +++ b/tests/preview.test.tsx @@ -1296,4 +1296,28 @@ describe('Preview', () => { rectSpy.mockRestore(); }); + + it('Focus should not be trapped when focusTrap is false', () => { + const rectSpy = jest.spyOn(HTMLElement.prototype, 'getBoundingClientRect').mockReturnValue({ + x: 0, y: 0, width: 100, height: 100, + top: 0, right: 100, bottom: 100, left: 0, + toJSON: () => undefined, + } as DOMRect); + + const { container } = render(no trap); + + act(() => { + jest.runAllTimers(); + }); + + const preview = document.querySelector('.rc-image-preview') as HTMLElement; + expect(preview).toBeTruthy(); + + // Focus outside the preview should not be redirected back + const wrapper = container.querySelector('.rc-image') as HTMLElement; + wrapper.focus(); + expect(document.activeElement).toBe(wrapper); + + rectSpy.mockRestore(); + }); });