diff --git a/docs/pages/material-ui/api/tab-panel.json b/docs/pages/material-ui/api/tab-panel.json index 01ec545ad24756..7c80cfabce2bc7 100644 --- a/docs/pages/material-ui/api/tab-panel.json +++ b/docs/pages/material-ui/api/tab-panel.json @@ -3,6 +3,7 @@ "value": { "type": { "name": "string" }, "required": true }, "children": { "type": { "name": "node" } }, "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, + "keepMounted": { "type": { "name": "bool" }, "default": "false" }, "sx": { "type": { "name": "union", @@ -14,6 +15,12 @@ "name": "TabPanel", "imports": ["import TabPanel from '@mui/lab/TabPanel';", "import { TabPanel } from '@mui/lab';"], "classes": [ + { + "key": "hidden", + "className": "MuiTabPanel-hidden", + "description": "State class applied to the root `div` element if `hidden={true}`.", + "isGlobal": false + }, { "key": "root", "className": "MuiTabPanel-root", diff --git a/docs/translations/api-docs/tab-panel/tab-panel.json b/docs/translations/api-docs/tab-panel/tab-panel.json index 97fdf34bb33934..9f39bbeca855b1 100644 --- a/docs/translations/api-docs/tab-panel/tab-panel.json +++ b/docs/translations/api-docs/tab-panel/tab-panel.json @@ -3,6 +3,7 @@ "propDescriptions": { "children": { "description": "The content of the component." }, "classes": { "description": "Override or extend the styles applied to the component." }, + "keepMounted": { "description": "Always keep the children in the DOM." }, "sx": { "description": "The system prop that allows defining system overrides as well as additional CSS styles." }, @@ -10,5 +11,12 @@ "description": "The value of the corresponding Tab. Must use the index of the Tab when no value was passed to Tab." } }, - "classDescriptions": { "root": { "description": "Styles applied to the root element." } } + "classDescriptions": { + "hidden": { + "description": "State class applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root div element", + "conditions": "hidden={true}" + }, + "root": { "description": "Styles applied to the root element." } + } } diff --git a/packages/mui-lab/src/TabPanel/TabPanel.d.ts b/packages/mui-lab/src/TabPanel/TabPanel.d.ts index 235ccbc3d6dc59..378190b80f60e7 100644 --- a/packages/mui-lab/src/TabPanel/TabPanel.d.ts +++ b/packages/mui-lab/src/TabPanel/TabPanel.d.ts @@ -22,6 +22,11 @@ export interface TabPanelProps extends StandardProps { - const { classes } = ownerState; + const { classes, hidden } = ownerState; const slots = { - root: ['root'], + root: ['root', hidden && 'hidden'], }; return composeClasses(slots, getTabPanelUtilityClass, classes); @@ -28,7 +28,7 @@ const TabPanelRoot = styled('div', { const TabPanel = React.forwardRef(function TabPanel(inProps, ref) { const props = useThemeProps({ props: inProps, name: 'MuiTabPanel' }); - const { children, className, value, ...other } = props; + const { children, className, value, keepMounted = false, ...other } = props; const ownerState = { ...props, @@ -54,7 +54,7 @@ const TabPanel = React.forwardRef(function TabPanel(inProps, ref) { ownerState={ownerState} {...other} > - {value === context.value && children} + {(keepMounted || value === context.value) && children} ); }); @@ -76,6 +76,11 @@ TabPanel.propTypes /* remove-proptypes */ = { * @ignore */ className: PropTypes.string, + /** + * Always keep the children in the DOM. + * @default false + */ + keepMounted: PropTypes.bool, /** * The system prop that allows defining system overrides as well as additional CSS styles. */ diff --git a/packages/mui-lab/src/TabPanel/TabPanel.test.tsx b/packages/mui-lab/src/TabPanel/TabPanel.test.tsx index ba0487616e45f9..bf6ec9394c9737 100644 --- a/packages/mui-lab/src/TabPanel/TabPanel.test.tsx +++ b/packages/mui-lab/src/TabPanel/TabPanel.test.tsx @@ -24,14 +24,17 @@ describe('', () => { ], })); - it('renders a [role="tabpanel"]', () => { - const { getByTestId } = render( + it('renders a [role="tabpanel"] and mounts children', () => { + const { getByTestId, queryByTestId } = render( - + +
+ , ); expect(getByTestId('tabpanel')).to.have.attribute('role', 'tabpanel'); + expect(queryByTestId('child')).to.not.equal(null); }); it('is [hidden] when TabPanel#value !== TabContext#value and does not mount children', () => { @@ -47,6 +50,19 @@ describe('', () => { expect(queryByTestId('child')).to.equal(null); }); + it('is [hidden] when TabPanel#value !== TabContext#value but does mount children when keepMounted', () => { + const { getByTestId, queryByTestId } = render( + + +
+ + , + ); + + expect(getByTestId('tabpanel')).to.have.property('hidden', true); + expect(queryByTestId('child')).to.not.equal(null); + }); + it('is accessible when TabPanel#value === TabContext#value', () => { const { getByTestId } = render( diff --git a/packages/mui-lab/src/TabPanel/tabPanelClasses.ts b/packages/mui-lab/src/TabPanel/tabPanelClasses.ts index 0adc5d0626c42d..cbcff1befa4d5d 100644 --- a/packages/mui-lab/src/TabPanel/tabPanelClasses.ts +++ b/packages/mui-lab/src/TabPanel/tabPanelClasses.ts @@ -4,6 +4,8 @@ import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; export interface TabPanelClasses { /** Styles applied to the root element. */ root: string; + /** State class applied to the root `div` element if `hidden={true}`. */ + hidden: string; } export type TabPanelClassKey = keyof TabPanelClasses; @@ -12,6 +14,6 @@ export function getTabPanelUtilityClass(slot: string): string { return generateUtilityClass('MuiTabPanel', slot); } -const tabPanelClasses: TabPanelClasses = generateUtilityClasses('MuiTabPanel', ['root']); +const tabPanelClasses: TabPanelClasses = generateUtilityClasses('MuiTabPanel', ['root', 'hidden']); export default tabPanelClasses;