From 83e9dd1209d2a702c905f6c961796dcee2953353 Mon Sep 17 00:00:00 2001 From: Vikas Gurjar <13435960+vikasgurjar@users.noreply.github.com> Date: Fri, 6 Dec 2024 13:57:41 +0530 Subject: [PATCH 1/4] add prop sidebarCollapsed and onToggleSidebar --- .../DashboardLayoutSidebarCollapsedProp.js | 122 ++++++++++++++++++ .../DashboardLayoutSidebarCollapsedProp.tsx | 112 ++++++++++++++++ ...oardLayoutSidebarCollapsedProp.tsx.preview | 6 + .../toolpad/core/api/dashboard-layout.json | 2 + .../dashboard-layout/dashboard-layout.json | 6 + .../DashboardLayout/DashboardLayout.test.tsx | 51 ++++++++ .../src/DashboardLayout/DashboardLayout.tsx | 28 ++++ 7 files changed, 327 insertions(+) create mode 100644 docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.js create mode 100644 docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.tsx create mode 100644 docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.tsx.preview diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.js b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.js new file mode 100644 index 00000000000..07f006e592f --- /dev/null +++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.js @@ -0,0 +1,122 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import { createTheme } from '@mui/material/styles'; +import DashboardIcon from '@mui/icons-material/Dashboard'; +import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; +import BarChartIcon from '@mui/icons-material/BarChart'; +import { AppProvider } from '@toolpad/core/AppProvider'; +import { DashboardLayout } from '@toolpad/core/DashboardLayout'; +import { Button } from '@mui/material'; + +const NAVIGATION = [ + { + segment: 'dashboard', + title: 'Dashboard', + icon: , + }, + { + segment: 'orders', + title: 'Orders', + icon: , + }, + { + segment: 'reports', + title: 'Reports', + icon: , + }, +]; + +const demoTheme = createTheme({ + cssVariables: { + colorSchemeSelector: 'data-toolpad-color-scheme', + }, + colorSchemes: { light: true, dark: true }, + breakpoints: { + values: { + xs: 0, + sm: 600, + md: 600, + lg: 1200, + xl: 1536, + }, + }, +}); + +function DemoPageContent({ pathname, toggleSidebar }) { + return ( + + Dashboard content for {pathname} + + + ); +} + +DemoPageContent.propTypes = { + pathname: PropTypes.string.isRequired, + toggleSidebar: PropTypes.shape({ + '__@hasInstance@2095': PropTypes.func.isRequired, + '__@metadata@2097': PropTypes.any, + apply: PropTypes.func.isRequired, + arguments: PropTypes.any.isRequired, + bind: PropTypes.func.isRequired, + call: PropTypes.func.isRequired, + caller: PropTypes.object.isRequired, + length: PropTypes.number.isRequired, + name: PropTypes.string.isRequired, + prototype: PropTypes.any.isRequired, + toString: PropTypes.func.isRequired, + }).isRequired, +}; + +function DashboardLayoutSidebarCollapsedProp(props) { + const { window } = props; + + const [pathname, setPathname] = React.useState('/dashboard'); + const [sidebarCollapsed, toggleSidebar] = React.useState(true); + const router = React.useMemo(() => { + return { + pathname, + searchParams: new URLSearchParams(), + navigate: (path) => setPathname(String(path)), + }; + }, [pathname]); + + // Remove this const when copying and pasting into your project. + const demoWindow = window !== undefined ? window() : undefined; + + return ( + + + toggleSidebar(!sidebarCollapsed)} + /> + + + ); +} + +DashboardLayoutSidebarCollapsedProp.propTypes = { + /** + * Injected by the documentation to work in an iframe. + * Remove this when copying and pasting into your project. + */ + window: PropTypes.func, +}; + +export default DashboardLayoutSidebarCollapsedProp; diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.tsx b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.tsx new file mode 100644 index 00000000000..a693e39ecd1 --- /dev/null +++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.tsx @@ -0,0 +1,112 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import { createTheme } from '@mui/material/styles'; +import DashboardIcon from '@mui/icons-material/Dashboard'; +import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; +import BarChartIcon from '@mui/icons-material/BarChart'; +import { + AppProvider, + type Router, + type Navigation, +} from '@toolpad/core/AppProvider'; +import { DashboardLayout } from '@toolpad/core/DashboardLayout'; +import { Button } from '@mui/material'; + +const NAVIGATION: Navigation = [ + { + segment: 'dashboard', + title: 'Dashboard', + icon: , + }, + { + segment: 'orders', + title: 'Orders', + icon: , + }, + { + segment: 'reports', + title: 'Reports', + icon: , + }, +]; + +const demoTheme = createTheme({ + cssVariables: { + colorSchemeSelector: 'data-toolpad-color-scheme', + }, + colorSchemes: { light: true, dark: true }, + breakpoints: { + values: { + xs: 0, + sm: 600, + md: 600, + lg: 1200, + xl: 1536, + }, + }, +}); + +function DemoPageContent({ + pathname, + toggleSidebar, +}: { + pathname: string; + toggleSidebar: () => void; +}) { + return ( + + Dashboard content for {pathname} + + + ); +} + +interface DemoProps { + /** + * Injected by the documentation to work in an iframe. + * Remove this when copying and pasting into your project. + */ + window?: () => Window; +} + +export default function DashboardLayoutSidebarCollapsedProp(props: DemoProps) { + const { window } = props; + + const [pathname, setPathname] = React.useState('/dashboard'); + const [sidebarCollapsed, toggleSidebar] = React.useState(true); + const router = React.useMemo(() => { + return { + pathname, + searchParams: new URLSearchParams(), + navigate: (path) => setPathname(String(path)), + }; + }, [pathname]); + + // Remove this const when copying and pasting into your project. + const demoWindow = window !== undefined ? window() : undefined; + + return ( + + + toggleSidebar(!sidebarCollapsed)} + /> + + + ); +} diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.tsx.preview b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.tsx.preview new file mode 100644 index 00000000000..36d7e84465e --- /dev/null +++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.tsx.preview @@ -0,0 +1,6 @@ + + toggleSidebar(!sidebarCollapsed)} + /> + \ No newline at end of file diff --git a/docs/pages/toolpad/core/api/dashboard-layout.json b/docs/pages/toolpad/core/api/dashboard-layout.json index fb900ae907a..bc2ba69513a 100644 --- a/docs/pages/toolpad/core/api/dashboard-layout.json +++ b/docs/pages/toolpad/core/api/dashboard-layout.json @@ -11,6 +11,8 @@ "defaultSidebarCollapsed": { "type": { "name": "bool" }, "default": "false" }, "disableCollapsibleSidebar": { "type": { "name": "bool" }, "default": "false" }, "hideNavigation": { "type": { "name": "bool" }, "default": "false" }, + "onToggleSidebar": { "type": { "name": "func" } }, + "sidebarCollapsed": { "type": { "name": "bool" }, "default": "false" }, "sidebarExpandedWidth": { "type": { "name": "union", "description": "number
| string" }, "default": "320" diff --git a/docs/translations/api-docs/dashboard-layout/dashboard-layout.json b/docs/translations/api-docs/dashboard-layout/dashboard-layout.json index 3cd8c58bad6..bee0ce4246b 100644 --- a/docs/translations/api-docs/dashboard-layout/dashboard-layout.json +++ b/docs/translations/api-docs/dashboard-layout/dashboard-layout.json @@ -12,6 +12,12 @@ "hideNavigation": { "description": "Whether the navigation bar and menu icon should be hidden" }, + "onToggleSidebar": { + "description": "Callback function to be executed on sidebarCollased state change" + }, + "sidebarCollapsed": { + "description": "A prop that controls the collapsed state of the sidebar." + }, "sidebarExpandedWidth": { "description": "Width of the sidebar when expanded." }, "slotProps": { "description": "The props used for each slot inside." }, "slots": { "description": "The components used for each slot inside." }, diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.test.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.test.tsx index 8881519cc84..c6af2d57ffa 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.test.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.test.tsx @@ -410,4 +410,55 @@ describe('DashboardLayout', () => { // Ensure that main content is still rendered expect(screen.getByText('Hello world')).toBeTruthy(); }); + + test('renders the sidebar in collapsed state when sidebarCollapsed is true', () => { + render( + +
Test Content
+
, + ); + + // Expect that menu button has expand action + expect(screen.getAllByLabelText('Expand menu')).toBeTruthy(); + expect(screen.queryByLabelText('Collapse menu')).toBeNull(); + }); + + test('renders the sidebar in expanded state when sidebarCollapsed is false', () => { + render( + +
Test Content
+
, + ); + + expect(screen.getAllByLabelText('Collapse menu')).toBeTruthy(); + }); + + test('calls onToggleSidebar callback when sidebarCollapsed state changes', () => { + const mockToggleSidebar = vi.fn(); + const { rerender } = render( + +
Test Content
+
, + ); + + // Trigger sidebar toggle (simulate a collapse action) + rerender( + +
Test Content
+
, + ); + + // Assert the callback was called with the new state + expect(mockToggleSidebar).toHaveBeenCalledWith(true); + + // Trigger expand action + rerender( + +
Test Content
+
, + ); + + // Assert the callback was called again with the updated state + expect(mockToggleSidebar).toHaveBeenCalledWith(false); + }); }); diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx index da545108989..ae9b7c31388 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx @@ -89,6 +89,14 @@ export interface DashboardLayoutProps { * @default false */ hideNavigation?: boolean; + /** A prop that controls the collapsed state of the sidebar. + * @default false + */ + sidebarCollapsed?: boolean; + /** + * Callback function to be executed on sidebarCollased state change + */ + onToggleSidebar?: (sidebarCollapsed: boolean) => void; /** * Width of the sidebar when expanded. * @default 320 @@ -131,6 +139,8 @@ function DashboardLayout(props: DashboardLayoutProps) { slots, slotProps, sx, + sidebarCollapsed, + onToggleSidebar, } = props; const theme = useTheme(); @@ -174,6 +184,15 @@ function DashboardLayout(props: DashboardLayoutProps) { const [isNavigationFullyExpanded, setIsNavigationFullyExpanded] = React.useState(isNavigationExpanded); + React.useEffect(() => { + if (typeof sidebarCollapsed === 'boolean') { + setIsNavigationExpanded(!sidebarCollapsed); + if (onToggleSidebar) { + onToggleSidebar(sidebarCollapsed); + } + } + }, [sidebarCollapsed, setIsNavigationExpanded, onToggleSidebar]); + React.useEffect(() => { if (isNavigationExpanded) { const drawerWidthTransitionTimeout = setTimeout(() => { @@ -483,6 +502,15 @@ DashboardLayout.propTypes /* remove-proptypes */ = { * @default false */ hideNavigation: PropTypes.bool, + /** + * Callback function to be executed on sidebarCollased state change + */ + onToggleSidebar: PropTypes.func, + /** + * A prop that controls the collapsed state of the sidebar. + * @default false + */ + sidebarCollapsed: PropTypes.bool, /** * Width of the sidebar when expanded. * @default 320 From 374dfbf9e4ec210d04fc1f89dca1c31f6ba56fc7 Mon Sep 17 00:00:00 2001 From: Vikas Gurjar <13435960+vikasgurjar@users.noreply.github.com> Date: Fri, 6 Dec 2024 14:16:10 +0530 Subject: [PATCH 2/4] run pnpm docs:typescript:formatted --- .../DashboardLayoutSidebarCollapsedProp.js | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.js b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.js index 07f006e592f..cbd2357620d 100644 --- a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.js +++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.js @@ -63,19 +63,7 @@ function DemoPageContent({ pathname, toggleSidebar }) { DemoPageContent.propTypes = { pathname: PropTypes.string.isRequired, - toggleSidebar: PropTypes.shape({ - '__@hasInstance@2095': PropTypes.func.isRequired, - '__@metadata@2097': PropTypes.any, - apply: PropTypes.func.isRequired, - arguments: PropTypes.any.isRequired, - bind: PropTypes.func.isRequired, - call: PropTypes.func.isRequired, - caller: PropTypes.object.isRequired, - length: PropTypes.number.isRequired, - name: PropTypes.string.isRequired, - prototype: PropTypes.any.isRequired, - toString: PropTypes.func.isRequired, - }).isRequired, + toggleSidebar: PropTypes.func.isRequired, }; function DashboardLayoutSidebarCollapsedProp(props) { From 9aaee1f6eb4a6d2d223d2edea26a83cb6a31a4c7 Mon Sep 17 00:00:00 2001 From: Vikas Gurjar <13435960+vikasgurjar@users.noreply.github.com> Date: Fri, 6 Dec 2024 14:28:56 +0530 Subject: [PATCH 3/4] add demo for sidebarCollapsed --- .../core/components/dashboard-layout/dashboard-layout.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md b/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md index dc46918b677..11fba8c0bec 100644 --- a/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md +++ b/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md @@ -126,6 +126,12 @@ The layout sidebar can be hidden if needed with the `hideNavigation` prop. {{"demo": "DashboardLayoutSidebarHidden.js", "height": 400, "iframe": true}} +### Toggle sidebar + +The sidebar can be toggled if needed with the `sidebarCollapsed` prop. + +{{"demo": "DashboardLayoutSidebarCollapsedProp.js", "height": 400, "iframe": true}} + ## Full-size content The layout content can take up the full available area with styles such as `flex: 1` or `height: 100%`. From f7f3397921bdcf1a48c0b37dbbf27c090334836c Mon Sep 17 00:00:00 2001 From: Vikas Gurjar <13435960+vikasgurjar@users.noreply.github.com> Date: Sat, 7 Dec 2024 15:54:31 +0530 Subject: [PATCH 4/4] use navigationMenuOpen instead of sidebarCollapsed --- .../DashboardLayoutSidebarCollapsedProp.js | 6 +-- .../DashboardLayoutSidebarCollapsedProp.tsx | 6 +-- ...oardLayoutSidebarCollapsedProp.tsx.preview | 4 +- .../dashboard-layout/dashboard-layout.md | 2 +- .../toolpad/core/api/dashboard-layout.json | 5 ++- .../dashboard-layout/dashboard-layout.json | 11 +++-- .../DashboardLayout/DashboardLayout.test.tsx | 35 +++++++++------- .../src/DashboardLayout/DashboardLayout.tsx | 41 ++++++++++++------- 8 files changed, 66 insertions(+), 44 deletions(-) diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.js b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.js index cbd2357620d..6ef9969bee5 100644 --- a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.js +++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.js @@ -70,7 +70,7 @@ function DashboardLayoutSidebarCollapsedProp(props) { const { window } = props; const [pathname, setPathname] = React.useState('/dashboard'); - const [sidebarCollapsed, toggleSidebar] = React.useState(true); + const [navigationMenuOpen, toggleSidebar] = React.useState(true); const router = React.useMemo(() => { return { pathname, @@ -89,10 +89,10 @@ function DashboardLayoutSidebarCollapsedProp(props) { theme={demoTheme} window={demoWindow} > - + toggleSidebar(!sidebarCollapsed)} + toggleSidebar={() => toggleSidebar(!navigationMenuOpen)} /> diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.tsx b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.tsx index a693e39ecd1..f00eadeb905 100644 --- a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.tsx +++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.tsx @@ -82,7 +82,7 @@ export default function DashboardLayoutSidebarCollapsedProp(props: DemoProps) { const { window } = props; const [pathname, setPathname] = React.useState('/dashboard'); - const [sidebarCollapsed, toggleSidebar] = React.useState(true); + const [navigationMenuOpen, toggleSidebar] = React.useState(true); const router = React.useMemo(() => { return { pathname, @@ -101,10 +101,10 @@ export default function DashboardLayoutSidebarCollapsedProp(props: DemoProps) { theme={demoTheme} window={demoWindow} > - + toggleSidebar(!sidebarCollapsed)} + toggleSidebar={() => toggleSidebar(!navigationMenuOpen)} /> diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.tsx.preview b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.tsx.preview index 36d7e84465e..3e5869c9ede 100644 --- a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.tsx.preview +++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSidebarCollapsedProp.tsx.preview @@ -1,6 +1,6 @@ - + toggleSidebar(!sidebarCollapsed)} + toggleSidebar={() => toggleSidebar(!navigationMenuOpen)} /> \ No newline at end of file diff --git a/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md b/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md index 11fba8c0bec..b27e92c67dd 100644 --- a/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md +++ b/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md @@ -128,7 +128,7 @@ The layout sidebar can be hidden if needed with the `hideNavigation` prop. ### Toggle sidebar -The sidebar can be toggled if needed with the `sidebarCollapsed` prop. +The sidebar can be toggled if needed with the `navigationMenuOpen` prop. {{"demo": "DashboardLayoutSidebarCollapsedProp.js", "height": 400, "iframe": true}} diff --git a/docs/pages/toolpad/core/api/dashboard-layout.json b/docs/pages/toolpad/core/api/dashboard-layout.json index bc2ba69513a..217260c8f13 100644 --- a/docs/pages/toolpad/core/api/dashboard-layout.json +++ b/docs/pages/toolpad/core/api/dashboard-layout.json @@ -11,8 +11,9 @@ "defaultSidebarCollapsed": { "type": { "name": "bool" }, "default": "false" }, "disableCollapsibleSidebar": { "type": { "name": "bool" }, "default": "false" }, "hideNavigation": { "type": { "name": "bool" }, "default": "false" }, - "onToggleSidebar": { "type": { "name": "func" } }, - "sidebarCollapsed": { "type": { "name": "bool" }, "default": "false" }, + "navigationMenuOpen": { "type": { "name": "bool" }, "default": "false" }, + "onNavigationMenuClose": { "type": { "name": "func" } }, + "onNavigationMenuOpen": { "type": { "name": "func" } }, "sidebarExpandedWidth": { "type": { "name": "union", "description": "number
| string" }, "default": "320" diff --git a/docs/translations/api-docs/dashboard-layout/dashboard-layout.json b/docs/translations/api-docs/dashboard-layout/dashboard-layout.json index bee0ce4246b..c95c460fb05 100644 --- a/docs/translations/api-docs/dashboard-layout/dashboard-layout.json +++ b/docs/translations/api-docs/dashboard-layout/dashboard-layout.json @@ -12,12 +12,15 @@ "hideNavigation": { "description": "Whether the navigation bar and menu icon should be hidden" }, - "onToggleSidebar": { - "description": "Callback function to be executed on sidebarCollased state change" - }, - "sidebarCollapsed": { + "navigationMenuOpen": { "description": "A prop that controls the collapsed state of the sidebar." }, + "onNavigationMenuClose": { + "description": "Callback function to be executed on navigation menu state changes to closed" + }, + "onNavigationMenuOpen": { + "description": "Callback function to be executed on navigation menu state changes to open" + }, "sidebarExpandedWidth": { "description": "Width of the sidebar when expanded." }, "slotProps": { "description": "The props used for each slot inside." }, "slots": { "description": "The components used for each slot inside." }, diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.test.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.test.tsx index c6af2d57ffa..2d7b6f4f411 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.test.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.test.tsx @@ -411,9 +411,9 @@ describe('DashboardLayout', () => { expect(screen.getByText('Hello world')).toBeTruthy(); }); - test('renders the sidebar in collapsed state when sidebarCollapsed is true', () => { + test('renders the sidebar in collapsed state when navigationMenuOpen is false', () => { render( - +
Test Content
, ); @@ -423,9 +423,9 @@ describe('DashboardLayout', () => { expect(screen.queryByLabelText('Collapse menu')).toBeNull(); }); - test('renders the sidebar in expanded state when sidebarCollapsed is false', () => { + test('renders the sidebar in expanded state when navigationMenuOpen is true', () => { render( - +
Test Content
, ); @@ -433,32 +433,39 @@ describe('DashboardLayout', () => { expect(screen.getAllByLabelText('Collapse menu')).toBeTruthy(); }); - test('calls onToggleSidebar callback when sidebarCollapsed state changes', () => { + test('calls onNavigationMenuOpen callback when navigationMenuOpen state changes to open', () => { const mockToggleSidebar = vi.fn(); const { rerender } = render( - +
Test Content
, ); - // Trigger sidebar toggle (simulate a collapse action) + // Trigger sidebar open action rerender( - +
Test Content
, ); - // Assert the callback was called with the new state - expect(mockToggleSidebar).toHaveBeenCalledWith(true); + expect(mockToggleSidebar).toHaveBeenCalledOnce(); + }); + + test('calls onNavigationMenuClose callback when navigationMenuOpen state changes to close', () => { + const mockToggleSidebar = vi.fn(); + const { rerender } = render( + +
Test Content
+
, + ); - // Trigger expand action + // Trigger sidebar close action rerender( - +
Test Content
, ); - // Assert the callback was called again with the updated state - expect(mockToggleSidebar).toHaveBeenCalledWith(false); + expect(mockToggleSidebar).toHaveBeenCalledOnce(); }); }); diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx index ae9b7c31388..14ef364bd8d 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx @@ -92,11 +92,15 @@ export interface DashboardLayoutProps { /** A prop that controls the collapsed state of the sidebar. * @default false */ - sidebarCollapsed?: boolean; + navigationMenuOpen?: boolean; /** - * Callback function to be executed on sidebarCollased state change + * Callback function to be executed on navigation menu state changes to open */ - onToggleSidebar?: (sidebarCollapsed: boolean) => void; + onNavigationMenuOpen?: () => void; + /** + * Callback function to be executed on navigation menu state changes to closed + */ + onNavigationMenuClose?: () => void; /** * Width of the sidebar when expanded. * @default 320 @@ -139,8 +143,9 @@ function DashboardLayout(props: DashboardLayoutProps) { slots, slotProps, sx, - sidebarCollapsed, - onToggleSidebar, + navigationMenuOpen, + onNavigationMenuOpen, + onNavigationMenuClose, } = props; const theme = useTheme(); @@ -185,13 +190,15 @@ function DashboardLayout(props: DashboardLayoutProps) { React.useState(isNavigationExpanded); React.useEffect(() => { - if (typeof sidebarCollapsed === 'boolean') { - setIsNavigationExpanded(!sidebarCollapsed); - if (onToggleSidebar) { - onToggleSidebar(sidebarCollapsed); + if (typeof navigationMenuOpen === 'boolean') { + setIsNavigationExpanded(navigationMenuOpen); + if (navigationMenuOpen) { + onNavigationMenuOpen?.(); + } else { + onNavigationMenuClose?.(); } } - }, [sidebarCollapsed, setIsNavigationExpanded, onToggleSidebar]); + }, [navigationMenuOpen, setIsNavigationExpanded, onNavigationMenuOpen, onNavigationMenuClose]); React.useEffect(() => { if (isNavigationExpanded) { @@ -502,15 +509,19 @@ DashboardLayout.propTypes /* remove-proptypes */ = { * @default false */ hideNavigation: PropTypes.bool, - /** - * Callback function to be executed on sidebarCollased state change - */ - onToggleSidebar: PropTypes.func, /** * A prop that controls the collapsed state of the sidebar. * @default false */ - sidebarCollapsed: PropTypes.bool, + navigationMenuOpen: PropTypes.bool, + /** + * Callback function to be executed on navigation menu state changes to closed + */ + onNavigationMenuClose: PropTypes.func, + /** + * Callback function to be executed on navigation menu state changes to open + */ + onNavigationMenuOpen: PropTypes.func, /** * Width of the sidebar when expanded. * @default 320