Skip to content

Commit

Permalink
[DashboardLayout] Support collapsed mini-drawer by default (#4234)
Browse files Browse the repository at this point in the history
  • Loading branch information
apedroferreira authored Oct 16, 2024
1 parent b0132c4 commit 5827f7a
Show file tree
Hide file tree
Showing 10 changed files with 258 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
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';

const NAVIGATION = [
{
segment: 'dashboard',
title: 'Dashboard',
icon: <DashboardIcon />,
},
{
segment: 'orders',
title: 'Orders',
icon: <ShoppingCartIcon />,
},
{
segment: 'reports',
title: 'Reports',
icon: <BarChartIcon />,
},
];

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 }) {
return (
<Box
sx={{
py: 4,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
textAlign: 'center',
}}
>
<Typography>Dashboard content for {pathname}</Typography>
</Box>
);
}

DemoPageContent.propTypes = {
pathname: PropTypes.string.isRequired,
};

function DashboardLayoutSidebarCollapsed(props) {
const { window } = props;

const [pathname, setPathname] = React.useState('/dashboard');

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 (
<AppProvider
navigation={NAVIGATION}
router={router}
theme={demoTheme}
window={demoWindow}
>
<DashboardLayout defaultSidebarCollapsed>
<DemoPageContent pathname={pathname} />
</DashboardLayout>
</AppProvider>
);
}

DashboardLayoutSidebarCollapsed.propTypes = {
/**
* Injected by the documentation to work in an iframe.
* Remove this when copying and pasting into your project.
*/
window: PropTypes.func,
};

export default DashboardLayoutSidebarCollapsed;
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
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 } from '@toolpad/core/AppProvider';
import { DashboardLayout } from '@toolpad/core/DashboardLayout';
import type { Router, Navigation } from '@toolpad/core';

const NAVIGATION: Navigation = [
{
segment: 'dashboard',
title: 'Dashboard',
icon: <DashboardIcon />,
},
{
segment: 'orders',
title: 'Orders',
icon: <ShoppingCartIcon />,
},
{
segment: 'reports',
title: 'Reports',
icon: <BarChartIcon />,
},
];

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 }: { pathname: string }) {
return (
<Box
sx={{
py: 4,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
textAlign: 'center',
}}
>
<Typography>Dashboard content for {pathname}</Typography>
</Box>
);
}

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 DashboardLayoutSidebarCollapsed(props: DemoProps) {
const { window } = props;

const [pathname, setPathname] = React.useState('/dashboard');

const router = React.useMemo<Router>(() => {
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 (
<AppProvider
navigation={NAVIGATION}
router={router}
theme={demoTheme}
window={demoWindow}
>
<DashboardLayout defaultSidebarCollapsed>
<DemoPageContent pathname={pathname} />
</DashboardLayout>
</AppProvider>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<DashboardLayout defaultSidebarCollapsed>
<DemoPageContent pathname={pathname} />
</DashboardLayout>
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { createTheme } from '@mui/material/styles';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import { createTheme } from '@mui/material/styles';
import { AppProvider } from '@toolpad/core/AppProvider';
import { DashboardLayout } from '@toolpad/core/DashboardLayout';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react';
import { createTheme } from '@mui/material/styles';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import { createTheme } from '@mui/material/styles';
import { AppProvider } from '@toolpad/core/AppProvider';
import { DashboardLayout } from '@toolpad/core/DashboardLayout';
import type { Router } from '@toolpad/core';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,18 +109,24 @@ The layout sidebar is collapsible to a mini-drawer (with icons only) in desktop

{{"demo": "DashboardLayoutNoMiniSidebar.js", "height": 400, "iframe": true}}

### Full-size content
### Start with mini-drawer on desktop

The layout content can take up the full available area with styles such as `flex: 1` or `height: 100%`.
The layout sidebar can default to a collapsed mini-drawer on desktop viewport sizes with the `defaultSidebarCollapsed` prop.

{{"demo": "DashboardLayoutFullScreen.js", "height": 400, "iframe": true}}
{{"demo": "DashboardLayoutSidebarCollapsed.js", "height": 400, "iframe": true}}

### Hide navigation

The layout sidebar can be hidden if needed with the `hideNavigation` prop.

{{"demo": "DashboardLayoutSidebarHidden.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%`.

{{"demo": "DashboardLayoutFullScreen.js", "height": 400, "iframe": true}}

## Account

The `DashboardLayout` comes integrated with the [`<Account />`](/toolpad/core/react-account/) component. It renders as an account management menu when a user is signed in – a `session` object is present – and a button when not.
Expand Down
1 change: 1 addition & 0 deletions docs/pages/toolpad/core/api/dashboard-layout.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"props": {
"children": { "type": { "name": "node" }, "required": true },
"defaultSidebarCollapsed": { "type": { "name": "bool" }, "default": "false" },
"disableCollapsibleSidebar": { "type": { "name": "bool" }, "default": "false" },
"hideNavigation": { "type": { "name": "bool" }, "default": "false" },
"slotProps": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"componentDescription": "",
"propDescriptions": {
"children": { "description": "The content of the dashboard." },
"defaultSidebarCollapsed": {
"description": "Whether the sidebar should start collapsed in desktop size screens."
},
"disableCollapsibleSidebar": {
"description": "Whether the sidebar should not be collapsible to a mini variant in desktop and tablet viewports."
},
Expand Down
27 changes: 25 additions & 2 deletions packages/toolpad-core/src/DashboardLayout/DashboardLayout.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -380,11 +380,34 @@ describe('DashboardLayout', () => {
const desktopNavigation = screen.queryByRole('navigation', { name: 'Desktop' });
const navigationToggle = screen.queryByLabelText('Collapse menu');

// Expect that the navigation and toggle button are not rendered
// Expect that navigation and menu button are not rendered
expect(desktopNavigation).toBeNull();
expect(navigationToggle).toBeNull();

// Ensure the main content is still rendered
// Ensure that main content is still rendered
expect(screen.getByText('Hello world')).toBeTruthy();
});

test('renders without default collapsed navigation on desktop', async () => {
const NAVIGATION: Navigation = [
{
title: 'Dashboard',
segment: 'dashboard',
icon: <DashboardIcon />,
},
];

render(
<AppProvider navigation={NAVIGATION}>
<DashboardLayout defaultSidebarCollapsed>Hello world</DashboardLayout>
</AppProvider>,
);

// Expect that menu button has expand action
expect(screen.getAllByLabelText('Expand menu')).toBeTruthy();
expect(screen.queryByLabelText('Collapse menu')).toBeNull();

// Ensure that main content is still rendered
expect(screen.getByText('Hello world')).toBeTruthy();
});
});
14 changes: 13 additions & 1 deletion packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,11 @@ export interface DashboardLayoutProps {
* @default false
*/
disableCollapsibleSidebar?: boolean;
/**
* Whether the sidebar should start collapsed in desktop size screens.
* @default false
*/
defaultSidebarCollapsed?: boolean;
/**
* Whether the navigation bar and menu icon should be hidden
* @default false
Expand Down Expand Up @@ -396,6 +401,7 @@ function DashboardLayout(props: DashboardLayoutProps) {
const {
children,
disableCollapsibleSidebar = false,
defaultSidebarCollapsed = false,
hideNavigation = false,
slots,
slotProps,
Expand All @@ -409,7 +415,8 @@ function DashboardLayout(props: DashboardLayoutProps) {
const appWindow = React.useContext(WindowContext);
const applicationTitle = useApplicationTitle();

const [isDesktopNavigationExpanded, setIsDesktopNavigationExpanded] = React.useState(true);
const [isDesktopNavigationExpanded, setIsDesktopNavigationExpanded] =
React.useState(!defaultSidebarCollapsed);
const [isMobileNavigationExpanded, setIsMobileNavigationExpanded] = React.useState(false);

const isUnderMdViewport = useMediaQuery(
Expand Down Expand Up @@ -729,6 +736,11 @@ DashboardLayout.propTypes /* remove-proptypes */ = {
* The content of the dashboard.
*/
children: PropTypes.node,
/**
* Whether the sidebar should start collapsed in desktop size screens.
* @default false
*/
defaultSidebarCollapsed: PropTypes.bool,
/**
* Whether the sidebar should not be collapsible to a mini variant in desktop and tablet viewports.
* @default false
Expand Down

0 comments on commit 5827f7a

Please sign in to comment.