diff --git a/src/Dialog/Content/Panel.tsx b/src/Dialog/Content/Panel.tsx index a6eebaee..64f70268 100644 --- a/src/Dialog/Content/Panel.tsx +++ b/src/Dialog/Content/Panel.tsx @@ -13,6 +13,8 @@ export interface PanelProps extends Omit { onMouseDown?: React.MouseEventHandler; onMouseUp?: React.MouseEventHandler; holderRef?: React.Ref; + /** Used for focus lock. When true and open, focus will lock into the panel */ + isFixedPos?: boolean; } export type PanelRef = { @@ -43,6 +45,7 @@ const Panel = React.forwardRef((props, ref) => { height, classNames: modalClassNames, styles: modalStyles, + isFixedPos, } = props; // ================================= Refs ================================= @@ -50,7 +53,7 @@ const Panel = React.forwardRef((props, ref) => { const internalRef = useRef(null); const mergedRef = useComposeRef(holderRef, panelRef, internalRef); - useLockFocus(visible, () => internalRef.current); + useLockFocus(visible && isFixedPos, () => internalRef.current); React.useImperativeHandle(ref, () => ({ focus: () => { diff --git a/src/Dialog/index.tsx b/src/Dialog/index.tsx index 460f263b..efe87e09 100644 --- a/src/Dialog/index.tsx +++ b/src/Dialog/index.tsx @@ -59,6 +59,7 @@ const Dialog: React.FC = (props) => { const contentRef = useRef(null); const [animatedVisible, setAnimatedVisible] = React.useState(visible); + const [isFixedPos, setIsFixedPos] = React.useState(false); // ========================== Init ========================== const ariaId = useId(); @@ -116,7 +117,7 @@ const Dialog: React.FC = (props) => { const contentClickRef = useRef(false); const contentTimeoutRef = useRef>(null); - // We need record content click incase content popup out of dialog + // We need record content click in case content popup out of dialog const onContentMouseDown: React.MouseEventHandler = () => { clearTimeout(contentTimeoutRef.current); contentClickRef.current = true; @@ -154,6 +155,12 @@ const Dialog: React.FC = (props) => { if (visible) { setAnimatedVisible(true); saveLastOutSideActiveElementRef(); + + // Calc the position style + if (wrapperRef.current) { + const computedWrapStyle = getComputedStyle(wrapperRef.current); + setIsFixedPos(computedWrapStyle.position === 'fixed'); + } } else if ( animatedVisible && contentRef.current.enableMotion() && @@ -203,6 +210,7 @@ const Dialog: React.FC = (props) => { > { + const actual = jest.requireActual('@rc-component/util/lib/Dom/focus'); + + const useLockFocus = (visible: boolean, ...rest: any[]) => { + globalThis.__useLockFocusVisible = visible; + return actual.useLockFocus(visible, ...rest); + }; + + return { + ...actual, + useLockFocus, + }; +}); + +/** + * Since overflow scroll test need a clear env which may affect by other test. + * Use a clean env instead. + */ +describe('Dialog.Focus', () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('Should lock when fixed', () => { + render( + , + ); + + act(() => { + jest.runAllTimers(); + }); + + expect(globalThis.__useLockFocusVisible).toBe(true); + }); + + it('Should not lock when not fixed', () => { + render( + , + ); + + act(() => { + jest.runAllTimers(); + }); + + expect(globalThis.__useLockFocusVisible).toBe(false); + }); +});