Skip to content

Commit 51fbd23

Browse files
authored
feat: add focusTrap prop to preview config (#507)
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.
1 parent f6ffd6b commit 51fbd23

File tree

2 files changed

+30
-1
lines changed

2 files changed

+30
-1
lines changed

src/Preview/index.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ export interface InternalPreviewConfig {
9090
maskClosable?: boolean;
9191
afterOpenChange?: (open: boolean) => void;
9292

93+
// Focus
94+
/** Whether to trap focus within the preview when open. Default is true. */
95+
focusTrap?: boolean;
96+
9397
// Operation
9498
movable?: boolean;
9599
icons?: OperationIcons;
@@ -195,6 +199,7 @@ const Preview: React.FC<PreviewProps> = props => {
195199
styles = {},
196200
mousePosition,
197201
zIndex,
202+
focusTrap = true,
198203
} = props;
199204

200205
const imgRef = useRef<HTMLImageElement>();
@@ -399,7 +404,7 @@ const Preview: React.FC<PreviewProps> = props => {
399404
}
400405
}, [open]);
401406

402-
useLockFocus(open && portalRender, () => wrapperRef.current);
407+
useLockFocus(focusTrap && open && portalRender, () => wrapperRef.current);
403408

404409
// ========================== Render ==========================
405410
const bodyStyle: React.CSSProperties = {

tests/preview.test.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,4 +1323,28 @@ describe('Preview', () => {
13231323

13241324
rectSpy.mockRestore();
13251325
});
1326+
1327+
it('Focus should not be trapped when focusTrap is false', () => {
1328+
const rectSpy = jest.spyOn(HTMLElement.prototype, 'getBoundingClientRect').mockReturnValue({
1329+
x: 0, y: 0, width: 100, height: 100,
1330+
top: 0, right: 100, bottom: 100, left: 0,
1331+
toJSON: () => undefined,
1332+
} as DOMRect);
1333+
1334+
const { container } = render(<Image src="src" alt="no trap" preview={{ open: true, focusTrap: false }} />);
1335+
1336+
act(() => {
1337+
jest.runAllTimers();
1338+
});
1339+
1340+
const preview = document.querySelector('.rc-image-preview') as HTMLElement;
1341+
expect(preview).toBeTruthy();
1342+
1343+
// Focus outside the preview should not be redirected back
1344+
const wrapper = container.querySelector('.rc-image') as HTMLElement;
1345+
wrapper.focus();
1346+
expect(document.activeElement).toBe(wrapper);
1347+
1348+
rectSpy.mockRestore();
1349+
});
13261350
});

0 commit comments

Comments
 (0)