Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[core] Make DashboardLayout navigation responsive #3750

Merged
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
739e913
Make DashboardLayout navigation responsive
apedroferreira Jul 3, 2024
5657f06
Simplify
apedroferreira Jul 3, 2024
cb290e4
Naming adjustments
apedroferreira Jul 3, 2024
0ec086f
Merge remote-tracking branch 'upstream/master' into dashboard-layout-…
apedroferreira Jul 3, 2024
08df769
Fix dashboard layout tests
apedroferreira Jul 3, 2024
9b6c268
Better like this
apedroferreira Jul 3, 2024
aaaf110
Wording
apedroferreira Jul 3, 2024
f88ad84
Set larger viewport dimensions for browser tests
apedroferreira Jul 3, 2024
00f187f
Review improvements/fixes + center logo and title better
apedroferreira Jul 5, 2024
5266f4d
Set min page content width
apedroferreira Jul 5, 2024
4a8563f
Merge remote-tracking branch 'upstream/master' into dashboard-layout-…
apedroferreira Jul 5, 2024
0f16640
More review feedback: make demo prop internal and make mobile breakpo…
apedroferreira Jul 8, 2024
ceb8cb7
Merge branch 'master' into dashboard-layout-responsive-nav
apedroferreira Jul 8, 2024
c18db83
Attempt to set breakpoints with theme but doesn't do anything so far...
apedroferreira Jul 10, 2024
b6297dc
Use theme with breakpoints in docs demos
apedroferreira Jul 10, 2024
a7c311e
Revert back to temporary navigation on mobile
apedroferreira Jul 10, 2024
eb22409
More mobile fixes
apedroferreira Jul 10, 2024
accb7ce
Add solution for responsive drawer in docs but needs an internal prop…
apedroferreira Jul 10, 2024
296d37a
Rename prop
apedroferreira Jul 10, 2024
a2089c9
Add container hack to all docs examples that use DashboardLayout
apedroferreira Jul 11, 2024
1b84d85
Make DashboardLayout have a public container prop instead
apedroferreira Jul 11, 2024
6efc05d
Fix menu closing in nested items
apedroferreira Jul 16, 2024
adf49df
Revert unwanted playground changes
apedroferreira Jul 16, 2024
3080479
Prettier wasn't working for some reason
apedroferreira Jul 16, 2024
b611de7
Merge remote-tracking branch 'upstream/master' into dashboard-layout-…
apedroferreira Jul 16, 2024
53e33ce
Merge remote-tracking branch 'upstream/master' into dashboard-layout-…
apedroferreira Jul 19, 2024
5ddc2aa
Just need to fix mobile sidebar color somehow
apedroferreira Jul 19, 2024
056e997
Fix drawer color
apedroferreira Jul 19, 2024
1c1e932
Adjustments
apedroferreira Jul 19, 2024
1cc6535
Fix colors
apedroferreira Jul 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default function AppProviderBasic() {
return (
// preview-start
<AppProvider navigation={NAVIGATION} router={router}>
<DashboardLayout>
<DashboardLayout initialNavigationOpen>
<Box
sx={{
py: 4,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export default function AppProviderBasic() {
return (
// preview-start
<AppProvider navigation={NAVIGATION} router={router}>
<DashboardLayout>
<DashboardLayout initialNavigationOpen>
<Box
sx={{
py: 4,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<AppProvider navigation={NAVIGATION} router={router}>
<DashboardLayout>
<DashboardLayout initialNavigationOpen>
<Box
sx={{
py: 4,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export default function DashboardLayoutBasic() {

return (
<AppProvider navigation={NAVIGATION} router={router}>
<DashboardLayout>
<DashboardLayout initialNavigationOpen>
<Box
sx={{
py: 4,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export default function DashboardLayoutBasic() {

return (
<AppProvider navigation={NAVIGATION} router={router}>
<DashboardLayout>
<DashboardLayout initialNavigationOpen>
<Box
sx={{
py: 4,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<DashboardLayout>
<DashboardLayout initialNavigationOpen>
<Box
sx={{
py: 4,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default function DashboardLayoutBranding() {

return (
<AppProvider navigation={NAVIGATION} branding={BRANDING} router={router}>
<DashboardLayout>
<DashboardLayout initialNavigationOpen>
<Box
sx={{
py: 4,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default function DashboardLayoutBranding() {

return (
<AppProvider navigation={NAVIGATION} branding={BRANDING} router={router}>
<DashboardLayout>
<DashboardLayout initialNavigationOpen>
<Box
sx={{
py: 4,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<DashboardLayout>
<DashboardLayout initialNavigationOpen>
<Box
sx={{
py: 4,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ export default function DashboardLayoutNavigation() {

return (
<AppProvider navigation={NAVIGATION} router={router}>
<DashboardLayout>
<DashboardLayout initialNavigationOpen>
<Box
sx={{
py: 4,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ export default function DashboardLayoutNavigation() {

return (
<AppProvider navigation={NAVIGATION} router={router}>
<DashboardLayout>
<DashboardLayout initialNavigationOpen>
<Box
sx={{
py: 4,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<DashboardLayout>
<DashboardLayout initialNavigationOpen>
<Box
sx={{
py: 4,
Expand Down
5 changes: 4 additions & 1 deletion docs/pages/toolpad/core/api/dashboard-layout.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"props": { "children": { "type": { "name": "node" }, "required": true } },
"props": {
"children": { "type": { "name": "node" }, "required": true },
"initialNavigationOpen": { "type": { "name": "bool" }, "default": "false" }
},
"name": "DashboardLayout",
"imports": [
"import { DashboardLayout } from '@toolpad-core/DashboardLayout';",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
{
"componentDescription": "",
"propDescriptions": { "children": { "description": "The content of the dashboard." } },
"propDescriptions": {
"children": { "description": "The content of the dashboard." },
"initialNavigationOpen": {
"description": "Whether the mobile navigation drawer should start open."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be external API? Why would users need this?
Besides, can't we, for demo purposes just force the large screen behavior. Maybe change the breakpoints or something. But in a hidden way, users shouldn't copy+paste our hacks accidentally.

Copy link
Member Author

@apedroferreira apedroferreira Jul 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be internal API if possible, just not sure what the best way to do that is? Will try to find a solution. (edit: I guess we can just mark it as internal but it's still secretly usable?)

Also wasn't sure if it's better to have the mobile or the desktop version on the docs, so I guess we can try to use the desktop one there or change the breakpoints.

}
},
"classDescriptions": {}
}
131 changes: 103 additions & 28 deletions packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ import ListSubheader from '@mui/material/ListSubheader';
import Stack from '@mui/material/Stack';
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
import IconButton from '@mui/material/IconButton';
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import MenuIcon from '@mui/icons-material/Menu';
import {
BrandingContext,
Navigation,
Expand All @@ -27,7 +29,8 @@ import {
} from '../AppProvider/AppProvider';
import { ToolpadLogo } from './ToolpadLogo';

const DRAWER_WIDTH = 320;
const DRAWER_WIDTH = 320; // px
const MIN_CONTENT_WIDTH = 320; // px

const LogoContainer = styled('div')({
position: 'relative',
Expand All @@ -37,6 +40,27 @@ const LogoContainer = styled('div')({
},
});

const Main = styled('main', { shouldForwardProp: (prop) => prop !== 'isMobileNavigationOpen' })<{
isMobileNavigationOpen: boolean;
}>(({ theme, isMobileNavigationOpen }) => ({
flexGrow: 1,
minWidth: MIN_CONTENT_WIDTH,
[theme.breakpoints.down('md')]: {
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
marginLeft: `-${DRAWER_WIDTH}px`,
...(isMobileNavigationOpen && {
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen,
}),
marginLeft: 0,
}),
},
}));

interface DashboardSidebarSubNavigationProps {
subNavigation: Navigation;
basePath?: string;
Expand Down Expand Up @@ -145,7 +169,7 @@ function DashboardSidebarSubNavigation({
selected={pathname === navigationItemFullPath}
onClick={handleSidebarItemClick(navigationItemId)}
>
<ListItemIcon>{navigationItem.icon}</ListItemIcon>
{navigationItem.icon ? <ListItemIcon>{navigationItem.icon}</ListItemIcon> : null}
<ListItemText primary={navigationItem.title} />
{navigationItem.children ? nestedNavigationCollapseIcon : null}
</ListItemButton>
Expand Down Expand Up @@ -187,6 +211,11 @@ export interface DashboardLayoutProps {
* The content of the dashboard.
*/
children: React.ReactNode;
/**
* Whether the mobile navigation drawer should start open.
* @default false
*/
initialNavigationOpen?: boolean;
}

/**
Expand All @@ -200,11 +229,35 @@ export interface DashboardLayoutProps {
* - [DashboardLayout API](https://mui.com/toolpad/core/api/dashboard-layout)
*/
function DashboardLayout(props: DashboardLayoutProps) {
const { children } = props;
const { children, initialNavigationOpen = false } = props;

const branding = React.useContext(BrandingContext);
const navigation = React.useContext(NavigationContext);

const [isMobileNavigationOpen, setIsMobileNavigationOpen] = React.useState(initialNavigationOpen);

const toggleMobileNavigation = React.useCallback(() => {
setIsMobileNavigationOpen((previousIsOpen) => !previousIsOpen);
}, []);

const drawerContent = (
<React.Fragment>
<Toolbar />
<Box component="nav" sx={{ overflow: 'auto', pt: navigation[0]?.kind === 'header' ? 0 : 2 }}>
<DashboardSidebarSubNavigation subNavigation={navigation} />
</Box>
</React.Fragment>
);

const drawerSx = {
width: DRAWER_WIDTH,
flexShrink: 0,
[`& .MuiDrawer-paper`]: {
width: DRAWER_WIDTH,
boxSizing: 'border-box',
},
};

return (
<Box sx={{ display: 'flex' }}>
<AppBar
Expand All @@ -215,42 +268,59 @@ function DashboardLayout(props: DashboardLayoutProps) {
}}
>
<Toolbar>
<a href="/" style={{ color: 'inherit', textDecoration: 'none' }}>
<Stack direction="row" alignItems="center">
<Box sx={{ mr: 1 }}>
<IconButton
aria-label="Open navigation menu"
onClick={toggleMobileNavigation}
edge="start"
sx={{ display: { xs: 'block', md: 'none' } }}
>
<MenuIcon />
</IconButton>
<Box
sx={{
position: { xs: 'absolute', md: 'static' },
left: { xs: '50%', md: 'auto' },
transform: { xs: 'translateX(-50%)', md: 'none' },
}}
>
<a href="/" style={{ color: 'inherit', textDecoration: 'none' }}>
<Stack direction="row" alignItems="center">
<LogoContainer>{branding?.logo ?? <ToolpadLogo size={40} />}</LogoContainer>
</Box>
<Typography variant="h6" sx={{ color: (theme) => theme.palette.primary.main }}>
{branding?.title ?? 'Toolpad'}
</Typography>
</Stack>
</a>
<Box sx={{ flexGrow: 1 }} />
<Typography variant="h6" sx={{ color: (theme) => theme.palette.primary.main }}>
{branding?.title ?? 'Toolpad'}
</Typography>
</Stack>
</a>
</Box>
<Box sx={{ display: { xs: 'none', md: 'block' }, flexGrow: 1 }} />
</Toolbar>
</AppBar>
<Drawer
variant="persistent"
open={isMobileNavigationOpen}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
}}
sx={{
display: { xs: 'block', md: 'none' },
...drawerSx,
}}
>
{drawerContent}
</Drawer>
<Drawer
variant="permanent"
sx={{
width: DRAWER_WIDTH,
flexShrink: 0,
[`& .MuiDrawer-paper`]: {
width: DRAWER_WIDTH,
boxSizing: 'border-box',
},
display: { xs: 'none', md: 'block' },
...drawerSx,
}}
>
<Toolbar />
<Box
component="nav"
sx={{ overflow: 'auto', pt: navigation[0]?.kind === 'header' ? 0 : 2 }}
>
<DashboardSidebarSubNavigation subNavigation={navigation} />
</Box>
{drawerContent}
</Drawer>
<Box component="main" sx={{ flexGrow: 1 }}>
<Main isMobileNavigationOpen={isMobileNavigationOpen}>
<Toolbar />
<Container maxWidth="lg">{children}</Container>
</Box>
</Main>
</Box>
);
}
Expand All @@ -264,6 +334,11 @@ DashboardLayout.propTypes /* remove-proptypes */ = {
* The content of the dashboard.
*/
children: PropTypes.node,
/**
* Whether the mobile navigation drawer should start open.
* @default false
*/
initialNavigationOpen: PropTypes.bool,
} as any;

export { DashboardLayout };
4 changes: 4 additions & 0 deletions packages/toolpad-core/vitest.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export default defineConfig({
name: 'chromium',
provider: 'playwright',
headless: !!process.env.CI,
viewport: {
width: 1024,
height: 896,
},
},
coverage: {
exclude: ['./build/**'],
Expand Down
Loading