Skip to content

Commit

Permalink
port the rest of the changes
Browse files Browse the repository at this point in the history
  • Loading branch information
Gr3q committed Sep 20, 2024
1 parent 65618d3 commit c56cb33
Show file tree
Hide file tree
Showing 7 changed files with 377 additions and 32 deletions.
121 changes: 120 additions & 1 deletion packages/mui-base/src/FocusTrap/FocusTrap.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { expect } from 'chai';
import { act, createRenderer, screen } from '@mui/internal-test-utils';
import { act, createRenderer, screen, fireEvent } from '@mui/internal-test-utils';
import { FocusTrap } from '@mui/base/FocusTrap';
import { Portal } from '@mui/base/Portal';

Expand Down Expand Up @@ -407,4 +407,123 @@ describe('<FocusTrap />', () => {
});
});
});

describe('focusTrap respects explicit/implicit tabindex', () => {
clock.withFakeTimers();

const nextItemKey = 'Tab';

it('respects implicit tabindex', () => {
const { getByRole } = render(
<div>
<FocusTrap open>
<div>
<div />
{/* This will have the starting focus */}
<input />
<button />
</div>
</FocusTrap>
</div>,
);

clock.tick(500); // trigger an interval call
expect(getByRole('textbox')).toHaveFocus();
});

it('respects explicit tabindex on non-interactive element', () => {
const { getByTestId } = render(
<div>
<FocusTrap open>
<div>
<div />
<input data-testid="first" />
<button data-testid="second" />
{/* This will have the starting focus, but cannot be focused afterwards
as https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex
*/}
<div data-testid="focused" tabIndex={-1} />
</div>
</FocusTrap>
</div>,
);

clock.tick(500);
// The order of focusable elements should be: focused, first, second here
expect(getByTestId('focused')).toHaveFocus();
// The order of focusable elements should be: first, second from now on

// Going forward
fireEvent.keyDown(getByTestId('focused'), { key: nextItemKey });
getByTestId('sentinelEnd').focus();
clock.tick(500);

expect(getByTestId('first')).toHaveFocus();

// Going backwards
fireEvent.keyDown(getByTestId('first'), { key: nextItemKey, shiftKey: true });
getByTestId('sentinelStart').focus();
clock.tick(500);

expect(getByTestId('second')).toHaveFocus();
});

it('respects explicit tabindex on interactive element ', () => {
const { getByTestId } = render(
<div>
<FocusTrap open>
<div>
<div />
<input data-testid="first" />
<button data-testid="second" />
{/* This will have the starting focus, but cannot be focused afterwards
as https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex
If yoy would want this to remain focusable you should use a positive tabindex
*/}
<button data-testid="third" tabIndex={-1} />
</div>
</FocusTrap>
</div>,
);

clock.tick(500);
// The order or focusable elements should be: third, first, second here
expect(getByTestId('third')).toHaveFocus();
// After this the order of focusable elements should be: first, second from now on

// Going forward
fireEvent.keyDown(getByTestId('third'), { key: nextItemKey });
getByTestId('sentinelEnd').focus();
clock.tick(500);

expect(getByTestId('first')).toHaveFocus();

// Going backwards
fireEvent.keyDown(getByTestId('first'), { key: nextItemKey, shiftKey: true });
getByTestId('sentinelStart').focus();
clock.tick(500);

expect(getByTestId('second')).toHaveFocus();

// We can't test native browser behaviour but after tabbing on the second button it should got to the sentinelEnd too.
});

it('no tabbable elements are handled', () => {
expect(() =>
render(
<div>
<FocusTrap open>
<div data-testid="to-be-focused">
<div />
<div />
</div>
</FocusTrap>
</div>,
),
).toErrorDev('MUI: The modal content node does not have focusable elements');

expect(screen.getByTestId('to-be-focused')).toHaveFocus();
});
});
});
3 changes: 2 additions & 1 deletion packages/mui-base/src/FocusTrap/FocusTrap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ function FocusTrap(props: FocusTrapProps): React.JSX.Element {
}

const doc = ownerDocument(rootRef.current);
const openFocusElement = defaultGetTabbable(rootRef.current, true, true)[0] ?? rootRef.current;
const openFocusElement =
defaultGetTabbable(rootRef.current, true, true)?.[0] ?? rootRef.current;

if (!rootRef.current.contains(doc.activeElement)) {
// No focusable child was found and rootRef.current cannot be focused
Expand Down
69 changes: 69 additions & 0 deletions packages/mui-material/src/Dialog/Dialog.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -429,4 +429,73 @@ describe('<Dialog />', () => {
});
});
});

describe('focus is handled properly', () => {
clock.withFakeTimers();

it('paper should have focus because it has role=dialog and content', function test() {
render(<Dialog open />);

const container = document.querySelector(`.${classes.paper}`);

clock.tick(500); // trigger an interval call
expect(container).toHaveFocus();
});

it('respects implicit tabindex', () => {
const { getByRole } = render(
<div>
<Dialog open disableInitialContentFocus>
<div>
<div />
{/* This will have the focus */}
<input />
<button />
</div>
</Dialog>
</div>,
);

clock.tick(500); // trigger an interval call
expect(getByRole('textbox')).toHaveFocus();
});

it('respects explicit tabindex', () => {
const { getByTestId } = render(
<div>
<Dialog open disableInitialContentFocus>
<div>
<div />
{/* This will have the focus */}
<input />
<button />
<div data-testid="focused" tabIndex={-1} />
</div>
</Dialog>
</div>,
);

clock.tick(500); // trigger an interval call
expect(getByTestId('focused')).toHaveFocus();
});

it('no tabbable elements are handled', () => {
expect(() =>
render(
<div>
<Dialog open disableInitialContentFocus>
<div data-testid="to-be-focused">
<div />
<div />
</div>
</Dialog>
</div>,
),
).toErrorDev('MUI: The modal content node does not have focusable elements');

clock.tick(500); // trigger an interval call
const container = document.querySelector(`.${classes.container}`);
expect(container).toHaveFocus();
});
});
});
3 changes: 0 additions & 3 deletions packages/mui-material/src/Modal/Modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,6 @@ const Modal = React.forwardRef(function Modal(inProps, ref) {
const classes = useUtilityClasses(ownerState);

const childProps = {};
if (children.props.tabIndex === undefined) {
childProps.tabIndex = '-1';
}

// It's a Transition like component
if (hasTransition) {
Expand Down
Loading

0 comments on commit c56cb33

Please sign in to comment.