diff --git a/docs/data/base/components/trap-focus/ContainedToggleTrappedFocus.js b/docs/data/base/components/trap-focus/ContainedToggleTrappedFocus.js
new file mode 100644
index 00000000000000..0fb77c38f0aeac
--- /dev/null
+++ b/docs/data/base/components/trap-focus/ContainedToggleTrappedFocus.js
@@ -0,0 +1,33 @@
+import * as React from 'react';
+import Box from '@mui/material/Box';
+import Stack from '@mui/material/Stack';
+import TrapFocus from '@mui/base/TrapFocus';
+
+export default function BasicTrapFocus() {
+ const [open, setOpen] = React.useState(false);
+
+ return (
+
+
+
+
+
+
+ {open && (
+
+ )}
+
+
+
+ );
+}
diff --git a/docs/data/base/components/trap-focus/ContainedToggleTrappedFocus.tsx b/docs/data/base/components/trap-focus/ContainedToggleTrappedFocus.tsx
new file mode 100644
index 00000000000000..0fb77c38f0aeac
--- /dev/null
+++ b/docs/data/base/components/trap-focus/ContainedToggleTrappedFocus.tsx
@@ -0,0 +1,33 @@
+import * as React from 'react';
+import Box from '@mui/material/Box';
+import Stack from '@mui/material/Stack';
+import TrapFocus from '@mui/base/TrapFocus';
+
+export default function BasicTrapFocus() {
+ const [open, setOpen] = React.useState(false);
+
+ return (
+
+
+
+
+
+
+ {open && (
+
+ )}
+
+
+
+ );
+}
diff --git a/docs/data/base/components/trap-focus/ContainedToggleTrappedFocus.tsx.preview b/docs/data/base/components/trap-focus/ContainedToggleTrappedFocus.tsx.preview
new file mode 100644
index 00000000000000..d748ddf123628c
--- /dev/null
+++ b/docs/data/base/components/trap-focus/ContainedToggleTrappedFocus.tsx.preview
@@ -0,0 +1,14 @@
+
+
+
+
+
+ {open && (
+
+ )}
+
+
\ No newline at end of file
diff --git a/docs/data/base/components/trap-focus/trap-focus.md b/docs/data/base/components/trap-focus/trap-focus.md
index 627624e3bde9ef..c35e010bfbcf92 100644
--- a/docs/data/base/components/trap-focus/trap-focus.md
+++ b/docs/data/base/components/trap-focus/trap-focus.md
@@ -84,3 +84,10 @@ When auto focus is disabled—as in the demo below—the component only traps th
The following demo uses the [`Portal`](/base/react-portal/) component to render a subset of the `TrapFocus` children into a new "subtree" outside of the current DOM hierarchy, so they are no longer part of the focus loop:
{{"demo": "PortalTrapFocus.js"}}
+
+### Using a toggle inside the trap
+
+The most common use case for the `TrapFocus` component is to maintain focus within a [modal](/base/react-modal/) component that is entirely separate from the element that opens the modal.
+But you can also create a toggle button for the `open` prop of the `TrapFocus` component that is stored inside of the component itself, as shown in the following demo:
+
+{{"demo": "ContainedToggleTrappedFocus.js"}}
diff --git a/package.json b/package.json
index 287c842a12ebd2..662c044ddaee6d 100644
--- a/package.json
+++ b/package.json
@@ -88,6 +88,7 @@
"@rollup/plugin-replace": "^4.0.0",
"@testing-library/dom": "^8.16.0",
"@testing-library/react": "^13.3.0",
+ "@testing-library/user-event": "^14.3.0",
"@types/chai": "^4.3.1",
"@types/chai-dom": "^0.0.13",
"@types/enzyme": "^3.10.12",
diff --git a/packages/mui-base/src/TrapFocus/TrapFocus.js b/packages/mui-base/src/TrapFocus/TrapFocus.js
index 30b20e59e13f53..09a8f72f77c80a 100644
--- a/packages/mui-base/src/TrapFocus/TrapFocus.js
+++ b/packages/mui-base/src/TrapFocus/TrapFocus.js
@@ -325,13 +325,18 @@ function TrapFocus(props) {
return (
{React.cloneElement(children, { ref: handleRef, onFocus })}
-
+
);
}
diff --git a/packages/mui-base/src/TrapFocus/TrapFocus.test.js b/packages/mui-base/src/TrapFocus/TrapFocus.test.js
index d65f3951bab254..26324e14f53c67 100644
--- a/packages/mui-base/src/TrapFocus/TrapFocus.test.js
+++ b/packages/mui-base/src/TrapFocus/TrapFocus.test.js
@@ -4,6 +4,7 @@ import { expect } from 'chai';
import { act, createRenderer, screen } from 'test/utils';
import TrapFocus from '@mui/base/TrapFocus';
import Portal from '@mui/base/Portal';
+import userEvent from '@testing-library/user-event';
describe('', () => {
const { clock, render } = createRenderer();
@@ -283,6 +284,36 @@ describe('', () => {
expect(screen.getByTestId('root')).toHaveFocus();
});
+ it('does not create any tabbable elements when open={false}', () => {
+ function Test(props) {
+ return (
+