@etsoo/toolpad 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/main.yml +49 -0
- package/.vscode/settings.json +3 -0
- package/LICENSE +21 -0
- package/README.md +11 -0
- package/build/src/Account/Account.d.ts +71 -0
- package/build/src/Account/Account.js +117 -0
- package/build/src/Account/Account.test.d.ts +1 -0
- package/build/src/Account/Account.test.js +35 -0
- package/build/src/Account/AccountPopoverFooter.d.ts +6 -0
- package/build/src/Account/AccountPopoverFooter.js +21 -0
- package/build/src/Account/AccountPopoverHeader.d.ts +6 -0
- package/build/src/Account/AccountPopoverHeader.js +15 -0
- package/build/src/Account/AccountPreview.d.ts +67 -0
- package/build/src/Account/AccountPreview.js +76 -0
- package/build/src/Account/AccountPreview.test.d.ts +1 -0
- package/build/src/Account/AccountPreview.test.js +47 -0
- package/build/src/Account/SignInButton.d.ts +2 -0
- package/build/src/Account/SignInButton.js +26 -0
- package/build/src/Account/SignOutButton.d.ts +3 -0
- package/build/src/Account/SignOutButton.js +28 -0
- package/build/src/Account/index.d.ts +6 -0
- package/build/src/Account/index.js +6 -0
- package/build/src/AppProvider/AppProvider.d.ts +98 -0
- package/build/src/AppProvider/AppProvider.js +4 -0
- package/build/src/AppProvider/AppProvider.test.d.ts +4 -0
- package/build/src/AppProvider/AppProvider.test.js +16 -0
- package/build/src/AppProvider/AppProviderComponent.d.ts +17 -0
- package/build/src/AppProvider/AppProviderComponent.js +120 -0
- package/build/src/AppProvider/AppThemeProvider.d.ts +12 -0
- package/build/src/AppProvider/AppThemeProvider.js +73 -0
- package/build/src/AppProvider/index.d.ts +1 -0
- package/build/src/AppProvider/index.js +1 -0
- package/build/src/DashboardLayout/DashboardLayout.d.ts +83 -0
- package/build/src/DashboardLayout/DashboardLayout.js +286 -0
- package/build/src/DashboardLayout/DashboardLayout.test.d.ts +1 -0
- package/build/src/DashboardLayout/DashboardLayout.test.js +291 -0
- package/build/src/DashboardLayout/DashboardSidebarSubNavigation.d.ts +16 -0
- package/build/src/DashboardLayout/DashboardSidebarSubNavigation.js +149 -0
- package/build/src/DashboardLayout/ThemeSwitcher.d.ts +5 -0
- package/build/src/DashboardLayout/ThemeSwitcher.js +40 -0
- package/build/src/DashboardLayout/ToolbarActions.d.ts +5 -0
- package/build/src/DashboardLayout/ToolbarActions.js +9 -0
- package/build/src/DashboardLayout/index.d.ts +2 -0
- package/build/src/DashboardLayout/index.js +3 -0
- package/build/src/DashboardLayout/utils.d.ts +8 -0
- package/build/src/DashboardLayout/utils.js +16 -0
- package/build/src/PageContainer/PageContainer.d.ts +66 -0
- package/build/src/PageContainer/PageContainer.js +97 -0
- package/build/src/PageContainer/PageContainer.test.d.ts +1 -0
- package/build/src/PageContainer/PageContainer.test.js +127 -0
- package/build/src/PageContainer/PageContainerToolbar.d.ts +16 -0
- package/build/src/PageContainer/PageContainerToolbar.js +24 -0
- package/build/src/PageContainer/PageContainerToolbar.test.d.ts +1 -0
- package/build/src/PageContainer/PageContainerToolbar.test.js +10 -0
- package/build/src/PageContainer/index.d.ts +2 -0
- package/build/src/PageContainer/index.js +2 -0
- package/build/src/index.d.ts +8 -0
- package/build/src/index.js +8 -0
- package/build/src/nextjs/AppProvider.d.ts +6 -0
- package/build/src/nextjs/AppProvider.js +16 -0
- package/build/src/nextjs/AppProvider.test.d.ts +1 -0
- package/build/src/nextjs/AppProvider.test.js +36 -0
- package/build/src/nextjs/AppProviderNextApp.d.ts +5 -0
- package/build/src/nextjs/AppProviderNextApp.js +27 -0
- package/build/src/nextjs/AppProviderNextPages.d.ts +5 -0
- package/build/src/nextjs/AppProviderNextPages.js +37 -0
- package/build/src/nextjs/index.d.ts +1 -0
- package/build/src/nextjs/index.js +1 -0
- package/build/src/persistence/codec.d.ts +47 -0
- package/build/src/persistence/codec.js +57 -0
- package/build/src/persistence/index.d.ts +1 -0
- package/build/src/persistence/index.js +1 -0
- package/build/src/persistence/useStorageState.d.ts +50 -0
- package/build/src/persistence/useStorageState.js +116 -0
- package/build/src/persistence/useStorageState.test.d.ts +1 -0
- package/build/src/persistence/useStorageState.test.js +61 -0
- package/build/src/react-router-dom/AppProvider.d.ts +6 -0
- package/build/src/react-router-dom/AppProvider.js +29 -0
- package/build/src/react-router-dom/AppProvider.test.d.ts +1 -0
- package/build/src/react-router-dom/AppProvider.test.js +12 -0
- package/build/src/react-router-dom/index.d.ts +1 -0
- package/build/src/react-router-dom/index.js +1 -0
- package/build/src/shared/Link.d.ts +8 -0
- package/build/src/shared/Link.js +19 -0
- package/build/src/shared/branding.d.ts +1 -0
- package/build/src/shared/branding.js +6 -0
- package/build/src/shared/components.d.ts +5 -0
- package/build/src/shared/components.js +23 -0
- package/build/src/shared/context.d.ts +12 -0
- package/build/src/shared/context.js +10 -0
- package/build/src/shared/locales/LocaleContext.d.ts +19 -0
- package/build/src/shared/locales/LocaleContext.js +18 -0
- package/build/src/shared/locales/en.d.ts +6 -0
- package/build/src/shared/locales/en.js +8 -0
- package/build/src/shared/navigation.d.ts +16 -0
- package/build/src/shared/navigation.js +129 -0
- package/build/src/useActivePage/index.d.ts +1 -0
- package/build/src/useActivePage/index.js +1 -0
- package/build/src/useActivePage/useActivePage.d.ts +11 -0
- package/build/src/useActivePage/useActivePage.js +48 -0
- package/build/src/useLocalStorageState/index.d.ts +1 -0
- package/build/src/useLocalStorageState/index.js +1 -0
- package/build/src/useLocalStorageState/useLocalStorageState.d.ts +2 -0
- package/build/src/useLocalStorageState/useLocalStorageState.js +14 -0
- package/build/src/useSessionStorageState/index.d.ts +1 -0
- package/build/src/useSessionStorageState/index.js +1 -0
- package/build/src/useSessionStorageState/useSessionStorageState.d.ts +2 -0
- package/build/src/useSessionStorageState/useSessionStorageState.js +14 -0
- package/build/src/utils/collections.d.ts +44 -0
- package/build/src/utils/collections.js +43 -0
- package/build/src/utils/describeConformance.d.ts +7 -0
- package/build/src/utils/describeConformance.js +3 -0
- package/build/src/utils/events.d.ts +27 -0
- package/build/src/utils/events.js +54 -0
- package/build/src/utils/hooks/index.d.ts +2 -0
- package/build/src/utils/hooks/index.js +2 -0
- package/build/src/utils/hooks/useBoolean.d.ts +11 -0
- package/build/src/utils/hooks/useBoolean.js +11 -0
- package/build/src/utils/hooks/useDebounced.d.ts +8 -0
- package/build/src/utils/hooks/useDebounced.js +28 -0
- package/build/src/utils/hooks/useDebouncedHandler.d.ts +12 -0
- package/build/src/utils/hooks/useDebouncedHandler.js +41 -0
- package/build/src/utils/hooks/useLatest.d.ts +6 -0
- package/build/src/utils/hooks/useLatest.js +9 -0
- package/build/src/utils/hooks/usePageTitle.d.ts +4 -0
- package/build/src/utils/hooks/usePageTitle.js +13 -0
- package/build/src/utils/hooks/useSsr.d.ts +4 -0
- package/build/src/utils/hooks/useSsr.js +16 -0
- package/build/src/utils/hooks/useStorageState.d.ts +10 -0
- package/build/src/utils/hooks/useStorageState.js +51 -0
- package/build/src/utils/warnOnce.d.ts +5 -0
- package/build/src/utils/warnOnce.js +11 -0
- package/build/vitest.setup.d.ts +1 -0
- package/build/vitest.setup.js +19 -0
- package/package.json +60 -0
- package/src/Account/Account.test.tsx +56 -0
- package/src/Account/Account.tsx +226 -0
- package/src/Account/AccountPopoverFooter.tsx +33 -0
- package/src/Account/AccountPopoverHeader.tsx +20 -0
- package/src/Account/AccountPreview.test.tsx +79 -0
- package/src/Account/AccountPreview.tsx +189 -0
- package/src/Account/SignInButton.tsx +38 -0
- package/src/Account/SignOutButton.tsx +44 -0
- package/src/Account/index.ts +6 -0
- package/src/AppProvider/AppProvider.test.tsx +25 -0
- package/src/AppProvider/AppProvider.tsx +116 -0
- package/src/AppProvider/AppProviderComponent.tsx +162 -0
- package/src/AppProvider/AppThemeProvider.tsx +177 -0
- package/src/AppProvider/index.ts +1 -0
- package/src/DashboardLayout/DashboardLayout.test.tsx +429 -0
- package/src/DashboardLayout/DashboardLayout.tsx +558 -0
- package/src/DashboardLayout/DashboardSidebarSubNavigation.tsx +301 -0
- package/src/DashboardLayout/ThemeSwitcher.tsx +81 -0
- package/src/DashboardLayout/ToolbarActions.tsx +11 -0
- package/src/DashboardLayout/index.ts +4 -0
- package/src/DashboardLayout/utils.ts +23 -0
- package/src/PageContainer/PageContainer.test.tsx +194 -0
- package/src/PageContainer/PageContainer.tsx +201 -0
- package/src/PageContainer/PageContainerToolbar.test.tsx +11 -0
- package/src/PageContainer/PageContainerToolbar.tsx +31 -0
- package/src/PageContainer/index.ts +2 -0
- package/src/index.ts +15 -0
- package/src/nextjs/AppProvider.test.tsx +48 -0
- package/src/nextjs/AppProvider.tsx +18 -0
- package/src/nextjs/AppProviderNextApp.tsx +37 -0
- package/src/nextjs/AppProviderNextPages.tsx +52 -0
- package/src/nextjs/index.tsx +1 -0
- package/src/persistence/codec.tsx +81 -0
- package/src/persistence/index.ts +1 -0
- package/src/persistence/useStorageState.test.tsx +101 -0
- package/src/persistence/useStorageState.tsx +244 -0
- package/src/react-router-dom/AppProvider.test.tsx +17 -0
- package/src/react-router-dom/AppProvider.tsx +40 -0
- package/src/react-router-dom/index.tsx +1 -0
- package/src/shared/Link.tsx +37 -0
- package/src/shared/branding.ts +7 -0
- package/src/shared/components.tsx +46 -0
- package/src/shared/context.ts +21 -0
- package/src/shared/locales/LocaleContext.tsx +44 -0
- package/src/shared/locales/en.tsx +9 -0
- package/src/shared/navigation.tsx +197 -0
- package/src/useActivePage/index.ts +1 -0
- package/src/useActivePage/useActivePage.ts +66 -0
- package/src/useLocalStorageState/index.ts +1 -0
- package/src/useLocalStorageState/useLocalStorageState.tsx +23 -0
- package/src/useSessionStorageState/index.tsx +1 -0
- package/src/useSessionStorageState/useSessionStorageState.tsx +23 -0
- package/src/utils/collections.ts +125 -0
- package/src/utils/describeConformance.ts +14 -0
- package/src/utils/events.ts +81 -0
- package/src/utils/hooks/index.ts +2 -0
- package/src/utils/hooks/useBoolean.ts +12 -0
- package/src/utils/hooks/useDebounced.ts +39 -0
- package/src/utils/hooks/useDebouncedHandler.ts +71 -0
- package/src/utils/hooks/useLatest.ts +16 -0
- package/src/utils/hooks/usePageTitle.ts +14 -0
- package/src/utils/hooks/useSsr.ts +24 -0
- package/src/utils/hooks/useStorageState.ts +130 -0
- package/src/utils/warnOnce.ts +12 -0
- package/tsconfig.json +19 -0
- package/vite.config.ts +27 -0
- package/vitest.setup.ts +22 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import PropTypes from "prop-types";
|
|
5
|
+
import { styled, useTheme } from "@mui/material";
|
|
6
|
+
import MuiAppBar from "@mui/material/AppBar";
|
|
7
|
+
import Box from "@mui/material/Box";
|
|
8
|
+
import Drawer from "@mui/material/Drawer";
|
|
9
|
+
import IconButton from "@mui/material/IconButton";
|
|
10
|
+
import Stack from "@mui/material/Stack";
|
|
11
|
+
import Toolbar from "@mui/material/Toolbar";
|
|
12
|
+
import Tooltip from "@mui/material/Tooltip";
|
|
13
|
+
import Typography from "@mui/material/Typography";
|
|
14
|
+
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
15
|
+
import MenuIcon from "@mui/icons-material/Menu";
|
|
16
|
+
import MenuOpenIcon from "@mui/icons-material/MenuOpen";
|
|
17
|
+
import { Link } from "../shared/Link";
|
|
18
|
+
import { BrandingContext, NavigationContext, WindowContext } from "../shared/context";
|
|
19
|
+
import { Account } from "../Account";
|
|
20
|
+
import { useApplicationTitle } from "../shared/branding";
|
|
21
|
+
import { DashboardSidebarSubNavigation } from "./DashboardSidebarSubNavigation";
|
|
22
|
+
import { ToolbarActions } from "./ToolbarActions";
|
|
23
|
+
import { ThemeSwitcher } from "./ThemeSwitcher";
|
|
24
|
+
import { getDrawerSxTransitionMixin, getDrawerWidthTransitionMixin } from "./utils";
|
|
25
|
+
const AppBar = styled(MuiAppBar)(({ theme }) => ({
|
|
26
|
+
borderWidth: 0,
|
|
27
|
+
borderBottomWidth: 1,
|
|
28
|
+
borderStyle: "solid",
|
|
29
|
+
borderColor: (theme.vars ?? theme).palette.divider,
|
|
30
|
+
boxShadow: "none",
|
|
31
|
+
zIndex: theme.zIndex.drawer + 1
|
|
32
|
+
}));
|
|
33
|
+
const LogoContainer = styled("div")({
|
|
34
|
+
position: "relative",
|
|
35
|
+
height: 40,
|
|
36
|
+
"& img": {
|
|
37
|
+
maxHeight: 40
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
/**
|
|
41
|
+
*
|
|
42
|
+
* Demos:
|
|
43
|
+
*
|
|
44
|
+
* - [Dashboard Layout](https://mui.com/toolpad/core/react-dashboard-layout/)
|
|
45
|
+
*
|
|
46
|
+
* API:
|
|
47
|
+
*
|
|
48
|
+
* - [DashboardLayout API](https://mui.com/toolpad/core/api/dashboard-layout)
|
|
49
|
+
*/
|
|
50
|
+
function DashboardLayout(props) {
|
|
51
|
+
const { children, disableCollapsibleSidebar = false, defaultSidebarCollapsed = false, hideNavigation = false, sidebarExpandedWidth = 320, slots, slotProps, sx } = props;
|
|
52
|
+
const theme = useTheme();
|
|
53
|
+
const branding = React.useContext(BrandingContext);
|
|
54
|
+
const navigation = React.useContext(NavigationContext);
|
|
55
|
+
const appWindow = React.useContext(WindowContext);
|
|
56
|
+
const applicationTitle = useApplicationTitle();
|
|
57
|
+
const [isDesktopNavigationExpanded, setIsDesktopNavigationExpanded] = React.useState(!defaultSidebarCollapsed);
|
|
58
|
+
const [isMobileNavigationExpanded, setIsMobileNavigationExpanded] = React.useState(false);
|
|
59
|
+
const isUnderMdViewport = useMediaQuery(theme.breakpoints.down("md"), appWindow && {
|
|
60
|
+
matchMedia: appWindow.matchMedia
|
|
61
|
+
});
|
|
62
|
+
const isOverSmViewport = useMediaQuery(theme.breakpoints.up("sm"), appWindow && {
|
|
63
|
+
matchMedia: appWindow.matchMedia
|
|
64
|
+
});
|
|
65
|
+
const isNavigationExpanded = isUnderMdViewport
|
|
66
|
+
? isMobileNavigationExpanded
|
|
67
|
+
: isDesktopNavigationExpanded;
|
|
68
|
+
const setIsNavigationExpanded = React.useCallback((newExpanded) => {
|
|
69
|
+
if (isUnderMdViewport) {
|
|
70
|
+
setIsMobileNavigationExpanded(newExpanded);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
setIsDesktopNavigationExpanded(newExpanded);
|
|
74
|
+
}
|
|
75
|
+
}, [isUnderMdViewport]);
|
|
76
|
+
const [isNavigationFullyExpanded, setIsNavigationFullyExpanded] = React.useState(isNavigationExpanded);
|
|
77
|
+
React.useEffect(() => {
|
|
78
|
+
if (isNavigationExpanded) {
|
|
79
|
+
const drawerWidthTransitionTimeout = setTimeout(() => {
|
|
80
|
+
setIsNavigationFullyExpanded(true);
|
|
81
|
+
}, theme.transitions.duration.enteringScreen);
|
|
82
|
+
return () => clearTimeout(drawerWidthTransitionTimeout);
|
|
83
|
+
}
|
|
84
|
+
setIsNavigationFullyExpanded(false);
|
|
85
|
+
return () => { };
|
|
86
|
+
}, [isNavigationExpanded, theme]);
|
|
87
|
+
const selectedItemIdRef = React.useRef("");
|
|
88
|
+
const handleSetNavigationExpanded = React.useCallback((newExpanded) => () => {
|
|
89
|
+
setIsNavigationExpanded(newExpanded);
|
|
90
|
+
}, [setIsNavigationExpanded]);
|
|
91
|
+
const toggleNavigationExpanded = React.useCallback(() => {
|
|
92
|
+
setIsNavigationExpanded(!isNavigationExpanded);
|
|
93
|
+
}, [isNavigationExpanded, setIsNavigationExpanded]);
|
|
94
|
+
const handleNavigationLinkClick = React.useCallback(() => {
|
|
95
|
+
selectedItemIdRef.current = "";
|
|
96
|
+
setIsMobileNavigationExpanded(false);
|
|
97
|
+
}, [setIsMobileNavigationExpanded]);
|
|
98
|
+
// If useEffect was used, the reset would also happen on the client render after SSR which we don't need
|
|
99
|
+
React.useMemo(() => {
|
|
100
|
+
if (navigation) {
|
|
101
|
+
selectedItemIdRef.current = "";
|
|
102
|
+
}
|
|
103
|
+
}, [navigation]);
|
|
104
|
+
const isDesktopMini = !disableCollapsibleSidebar && !isDesktopNavigationExpanded;
|
|
105
|
+
const isMobileMini = !disableCollapsibleSidebar && !isMobileNavigationExpanded;
|
|
106
|
+
const getMenuIcon = React.useCallback((isExpanded) => {
|
|
107
|
+
const expandMenuActionText = "Expand";
|
|
108
|
+
const collapseMenuActionText = "Collapse";
|
|
109
|
+
return (_jsx(Tooltip, { title: `${isExpanded ? collapseMenuActionText : expandMenuActionText} menu`, enterDelay: 1000, children: _jsx("div", { children: _jsx(IconButton, { "aria-label": `${isExpanded ? collapseMenuActionText : expandMenuActionText} navigation menu`, onClick: toggleNavigationExpanded, children: isExpanded ? _jsx(MenuOpenIcon, {}) : _jsx(MenuIcon, {}) }) }) }));
|
|
110
|
+
}, [toggleNavigationExpanded]);
|
|
111
|
+
const hasDrawerTransitions = isOverSmViewport && (disableCollapsibleSidebar || !isUnderMdViewport);
|
|
112
|
+
const ToolbarActionsSlot = slots?.toolbarActions ?? ToolbarActions;
|
|
113
|
+
const ToolbarAccountSlot = slots?.toolbarAccount ?? Account;
|
|
114
|
+
const SidebarFooterSlot = slots?.sidebarFooter ?? null;
|
|
115
|
+
const getDrawerContent = React.useCallback((isMini, viewport) => (_jsxs(React.Fragment, { children: [_jsx(Toolbar, {}), _jsxs(Box, { component: "nav", "aria-label": `${viewport.charAt(0).toUpperCase()}${viewport.slice(1)}`, sx: {
|
|
116
|
+
height: "100%",
|
|
117
|
+
display: "flex",
|
|
118
|
+
flexDirection: "column",
|
|
119
|
+
justifyContent: "space-between",
|
|
120
|
+
overflow: "auto",
|
|
121
|
+
pt: navigation[0]?.kind === "header" && !isMini ? 0 : 2,
|
|
122
|
+
...(hasDrawerTransitions
|
|
123
|
+
? getDrawerSxTransitionMixin(isNavigationFullyExpanded, "padding")
|
|
124
|
+
: {})
|
|
125
|
+
}, children: [_jsx(DashboardSidebarSubNavigation, { subNavigation: navigation, onLinkClick: handleNavigationLinkClick, isMini: isMini, isFullyExpanded: isNavigationFullyExpanded, hasDrawerTransitions: hasDrawerTransitions, selectedItemId: selectedItemIdRef.current }), SidebarFooterSlot ? (_jsx(SidebarFooterSlot, { mini: isMini, ...slotProps?.sidebarFooter })) : null] })] })), [
|
|
126
|
+
SidebarFooterSlot,
|
|
127
|
+
handleNavigationLinkClick,
|
|
128
|
+
hasDrawerTransitions,
|
|
129
|
+
isNavigationFullyExpanded,
|
|
130
|
+
navigation,
|
|
131
|
+
slotProps?.sidebarFooter
|
|
132
|
+
]);
|
|
133
|
+
const getDrawerSharedSx = React.useCallback((isMini, isTemporary) => {
|
|
134
|
+
const drawerWidth = isMini ? 64 : sidebarExpandedWidth;
|
|
135
|
+
return {
|
|
136
|
+
width: drawerWidth,
|
|
137
|
+
flexShrink: 0,
|
|
138
|
+
...getDrawerWidthTransitionMixin(isNavigationExpanded),
|
|
139
|
+
...(isTemporary ? { position: "absolute" } : {}),
|
|
140
|
+
[`& .MuiDrawer-paper`]: {
|
|
141
|
+
position: "absolute",
|
|
142
|
+
width: drawerWidth,
|
|
143
|
+
boxSizing: "border-box",
|
|
144
|
+
backgroundImage: "none",
|
|
145
|
+
...getDrawerWidthTransitionMixin(isNavigationExpanded)
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
}, [isNavigationExpanded, sidebarExpandedWidth]);
|
|
149
|
+
const layoutRef = React.useRef(null);
|
|
150
|
+
return (_jsxs(Box, { ref: layoutRef, sx: {
|
|
151
|
+
position: "relative",
|
|
152
|
+
display: "flex",
|
|
153
|
+
overflow: "hidden",
|
|
154
|
+
height: "100vh",
|
|
155
|
+
width: "100vw",
|
|
156
|
+
...sx
|
|
157
|
+
}, children: [_jsx(AppBar, { color: "inherit", position: "absolute", children: _jsxs(Toolbar, { sx: { backgroundColor: "inherit", mx: { xs: -0.75, sm: -1.5 } }, children: [!hideNavigation ? (_jsxs(React.Fragment, { children: [_jsx(Box, { sx: {
|
|
158
|
+
mr: { sm: disableCollapsibleSidebar ? 0 : 1 },
|
|
159
|
+
display: { md: "none" }
|
|
160
|
+
}, children: getMenuIcon(isMobileNavigationExpanded) }), _jsx(Box, { sx: {
|
|
161
|
+
display: {
|
|
162
|
+
xs: "none",
|
|
163
|
+
md: disableCollapsibleSidebar ? "none" : "block"
|
|
164
|
+
},
|
|
165
|
+
mr: disableCollapsibleSidebar ? 0 : 1
|
|
166
|
+
}, children: getMenuIcon(isDesktopNavigationExpanded) })] })) : null, _jsx(Box, { sx: {
|
|
167
|
+
position: { xs: "absolute", md: "static" },
|
|
168
|
+
left: { xs: "50%", md: "auto" },
|
|
169
|
+
transform: { xs: "translateX(-50%)", md: "none" }
|
|
170
|
+
}, children: _jsx(Link, { href: "/", style: { color: "inherit", textDecoration: "none" }, children: _jsxs(Stack, { direction: "row", alignItems: "center", children: [branding?.logo && (_jsx(LogoContainer, { children: branding.logo })), _jsx(Typography, { variant: "h6", sx: {
|
|
171
|
+
color: (theme.vars ?? theme).palette.primary.main,
|
|
172
|
+
fontWeight: "700",
|
|
173
|
+
ml: 0.5,
|
|
174
|
+
whiteSpace: "nowrap"
|
|
175
|
+
}, children: applicationTitle })] }) }) }), _jsx(Box, { sx: { flexGrow: 1 } }), _jsxs(Stack, { direction: "row", spacing: 1, children: [_jsx(ToolbarActionsSlot, { ...slotProps?.toolbarActions }), _jsx(ThemeSwitcher, {}), _jsx(ToolbarAccountSlot, { ...slotProps?.toolbarAccount })] })] }) }), !hideNavigation ? (_jsxs(React.Fragment, { children: [_jsx(Drawer, { container: layoutRef.current, variant: "temporary", open: isMobileNavigationExpanded, onClose: handleSetNavigationExpanded(false), ModalProps: {
|
|
176
|
+
keepMounted: true // Better open performance on mobile.
|
|
177
|
+
}, sx: {
|
|
178
|
+
display: {
|
|
179
|
+
xs: "block",
|
|
180
|
+
sm: disableCollapsibleSidebar ? "block" : "none",
|
|
181
|
+
md: "none"
|
|
182
|
+
},
|
|
183
|
+
...getDrawerSharedSx(false, true)
|
|
184
|
+
}, children: getDrawerContent(false, "phone") }), _jsx(Drawer, { variant: "permanent", sx: {
|
|
185
|
+
display: {
|
|
186
|
+
xs: "none",
|
|
187
|
+
sm: disableCollapsibleSidebar ? "none" : "block",
|
|
188
|
+
md: "none"
|
|
189
|
+
},
|
|
190
|
+
...getDrawerSharedSx(isMobileMini, false)
|
|
191
|
+
}, children: getDrawerContent(isMobileMini, "tablet") }), _jsx(Drawer, { variant: "permanent", sx: {
|
|
192
|
+
display: { xs: "none", md: "block" },
|
|
193
|
+
...getDrawerSharedSx(isDesktopMini, false)
|
|
194
|
+
}, children: getDrawerContent(isDesktopMini, "desktop") })] })) : null, _jsxs(Box, { sx: {
|
|
195
|
+
display: "flex",
|
|
196
|
+
flexDirection: "column",
|
|
197
|
+
flex: 1
|
|
198
|
+
}, children: [_jsx(Toolbar, {}), _jsx(Box, { component: "main", sx: {
|
|
199
|
+
display: "flex",
|
|
200
|
+
flexDirection: "column",
|
|
201
|
+
flex: 1,
|
|
202
|
+
overflow: "auto"
|
|
203
|
+
}, children: children })] })] }));
|
|
204
|
+
}
|
|
205
|
+
DashboardLayout.propTypes /* remove-proptypes */ = {
|
|
206
|
+
// ┌────────────────────────────── Warning ──────────────────────────────┐
|
|
207
|
+
// │ These PropTypes are generated from the TypeScript type definitions. │
|
|
208
|
+
// │ To update them, edit the TypeScript types and run `pnpm proptypes`. │
|
|
209
|
+
// └─────────────────────────────────────────────────────────────────────┘
|
|
210
|
+
/**
|
|
211
|
+
* The content of the dashboard.
|
|
212
|
+
*/
|
|
213
|
+
children: PropTypes.node,
|
|
214
|
+
/**
|
|
215
|
+
* Whether the sidebar should start collapsed in desktop size screens.
|
|
216
|
+
* @default false
|
|
217
|
+
*/
|
|
218
|
+
defaultSidebarCollapsed: PropTypes.bool,
|
|
219
|
+
/**
|
|
220
|
+
* Whether the sidebar should not be collapsible to a mini variant in desktop and tablet viewports.
|
|
221
|
+
* @default false
|
|
222
|
+
*/
|
|
223
|
+
disableCollapsibleSidebar: PropTypes.bool,
|
|
224
|
+
/**
|
|
225
|
+
* Whether the navigation bar and menu icon should be hidden
|
|
226
|
+
* @default false
|
|
227
|
+
*/
|
|
228
|
+
hideNavigation: PropTypes.bool,
|
|
229
|
+
/**
|
|
230
|
+
* Width of the sidebar when expanded.
|
|
231
|
+
* @default 320
|
|
232
|
+
*/
|
|
233
|
+
sidebarExpandedWidth: PropTypes.oneOfType([
|
|
234
|
+
PropTypes.number,
|
|
235
|
+
PropTypes.string
|
|
236
|
+
]),
|
|
237
|
+
/**
|
|
238
|
+
* The props used for each slot inside.
|
|
239
|
+
* @default {}
|
|
240
|
+
*/
|
|
241
|
+
slotProps: PropTypes.shape({
|
|
242
|
+
sidebarFooter: PropTypes.shape({
|
|
243
|
+
mini: PropTypes.bool.isRequired
|
|
244
|
+
}),
|
|
245
|
+
toolbarAccount: PropTypes.shape({
|
|
246
|
+
localeText: PropTypes.shape({
|
|
247
|
+
iconButtonAriaLabel: PropTypes.string,
|
|
248
|
+
signInLabel: PropTypes.string,
|
|
249
|
+
signOutLabel: PropTypes.string
|
|
250
|
+
}),
|
|
251
|
+
slotProps: PropTypes.shape({
|
|
252
|
+
popover: PropTypes.object,
|
|
253
|
+
popoverContent: PropTypes.object,
|
|
254
|
+
preview: PropTypes.object,
|
|
255
|
+
signInButton: PropTypes.object,
|
|
256
|
+
signOutButton: PropTypes.object
|
|
257
|
+
}),
|
|
258
|
+
slots: PropTypes.shape({
|
|
259
|
+
popover: PropTypes.elementType,
|
|
260
|
+
popoverContent: PropTypes.elementType,
|
|
261
|
+
preview: PropTypes.elementType,
|
|
262
|
+
signInButton: PropTypes.elementType,
|
|
263
|
+
signOutButton: PropTypes.elementType
|
|
264
|
+
})
|
|
265
|
+
}),
|
|
266
|
+
toolbarActions: PropTypes.object
|
|
267
|
+
}),
|
|
268
|
+
/**
|
|
269
|
+
* The components used for each slot inside.
|
|
270
|
+
* @default {}
|
|
271
|
+
*/
|
|
272
|
+
slots: PropTypes.shape({
|
|
273
|
+
sidebarFooter: PropTypes.elementType,
|
|
274
|
+
toolbarAccount: PropTypes.elementType,
|
|
275
|
+
toolbarActions: PropTypes.elementType
|
|
276
|
+
}),
|
|
277
|
+
/**
|
|
278
|
+
* The system prop that allows defining system overrides as well as additional CSS styles.
|
|
279
|
+
*/
|
|
280
|
+
sx: PropTypes.oneOfType([
|
|
281
|
+
PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])),
|
|
282
|
+
PropTypes.func,
|
|
283
|
+
PropTypes.object
|
|
284
|
+
])
|
|
285
|
+
};
|
|
286
|
+
export { DashboardLayout };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "@testing-library/jest-dom/vitest";
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { describe, test, expect, vi } from "vitest";
|
|
3
|
+
import { render, within, screen } from "@testing-library/react";
|
|
4
|
+
import DashboardIcon from "@mui/icons-material/Dashboard";
|
|
5
|
+
import ShoppingCartIcon from "@mui/icons-material/ShoppingCart";
|
|
6
|
+
import BarChartIcon from "@mui/icons-material/BarChart";
|
|
7
|
+
import DescriptionIcon from "@mui/icons-material/Description";
|
|
8
|
+
import LayersIcon from "@mui/icons-material/Layers";
|
|
9
|
+
import userEvent from "@testing-library/user-event";
|
|
10
|
+
import "@testing-library/jest-dom/vitest";
|
|
11
|
+
import { DashboardLayout } from "./DashboardLayout";
|
|
12
|
+
import { AppProvider } from "../AppProvider/AppProviderComponent";
|
|
13
|
+
describe("DashboardLayout", () => {
|
|
14
|
+
test("renders content correctly", async () => {
|
|
15
|
+
render(_jsx(DashboardLayout, { children: "Hello world" }));
|
|
16
|
+
expect(screen.getByText("Hello world")).toBeTruthy();
|
|
17
|
+
});
|
|
18
|
+
test("renders branding correctly in header", async () => {
|
|
19
|
+
const BRANDING = {
|
|
20
|
+
title: "My Company",
|
|
21
|
+
logo: _jsx("img", { src: "https://placehold.co/600x400", alt: "Placeholder Logo" })
|
|
22
|
+
};
|
|
23
|
+
render(_jsx(AppProvider, { branding: BRANDING, children: _jsx(DashboardLayout, { children: "Hello world" }) }));
|
|
24
|
+
const header = screen.getByRole("banner");
|
|
25
|
+
expect(within(header).getByText("My Company")).toBeTruthy();
|
|
26
|
+
expect(within(header).getByAltText("Placeholder Logo")).toBeTruthy();
|
|
27
|
+
});
|
|
28
|
+
test("can switch theme", async () => {
|
|
29
|
+
const user = userEvent.setup();
|
|
30
|
+
render(_jsx(AppProvider, { children: _jsx(DashboardLayout, { children: "Hello world" }) }));
|
|
31
|
+
const getBackgroundColorCSSVariable = () => getComputedStyle(document.documentElement).getPropertyValue("--mui-palette-common-background");
|
|
32
|
+
const header = screen.getByRole("banner");
|
|
33
|
+
const themeSwitcherButton = within(header).getByLabelText("Switch to dark mode");
|
|
34
|
+
expect(getBackgroundColorCSSVariable()).toBe("#fff");
|
|
35
|
+
await user.click(themeSwitcherButton);
|
|
36
|
+
expect(getBackgroundColorCSSVariable()).toBe("#000");
|
|
37
|
+
await user.click(themeSwitcherButton);
|
|
38
|
+
expect(getBackgroundColorCSSVariable()).toBe("#fff");
|
|
39
|
+
});
|
|
40
|
+
test("navigation works correctly", async () => {
|
|
41
|
+
const NAVIGATION = [
|
|
42
|
+
{
|
|
43
|
+
kind: "header",
|
|
44
|
+
title: "Main items"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
title: "Dashboard",
|
|
48
|
+
segment: "dashboard",
|
|
49
|
+
icon: _jsx(DashboardIcon, {})
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
title: "Orders",
|
|
53
|
+
segment: "orders",
|
|
54
|
+
icon: _jsx(ShoppingCartIcon, {})
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
kind: "divider"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
kind: "header",
|
|
61
|
+
title: "Analytics"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
segment: "reports",
|
|
65
|
+
title: "Reports",
|
|
66
|
+
icon: _jsx(BarChartIcon, {}),
|
|
67
|
+
children: [
|
|
68
|
+
{
|
|
69
|
+
segment: "sales",
|
|
70
|
+
title: "Sales",
|
|
71
|
+
icon: _jsx(DescriptionIcon, {})
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
segment: "traffic",
|
|
75
|
+
title: "Traffic",
|
|
76
|
+
icon: _jsx(DescriptionIcon, {})
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
segment: "integrations",
|
|
82
|
+
title: "Integrations",
|
|
83
|
+
icon: _jsx(LayersIcon, {})
|
|
84
|
+
}
|
|
85
|
+
];
|
|
86
|
+
const user = userEvent.setup();
|
|
87
|
+
render(_jsx(AppProvider, { navigation: NAVIGATION, children: _jsx(DashboardLayout, { children: "Hello world" }) }));
|
|
88
|
+
const desktopNavigation = screen.getByRole("navigation", {
|
|
89
|
+
name: "Desktop"
|
|
90
|
+
});
|
|
91
|
+
// List subheaders are present
|
|
92
|
+
expect(within(desktopNavigation).getByText("Main items")).toBeTruthy();
|
|
93
|
+
expect(within(desktopNavigation).getByText("Analytics")).toBeTruthy();
|
|
94
|
+
// List items and their links are present
|
|
95
|
+
const dashboardLink = within(desktopNavigation).getByRole("link", {
|
|
96
|
+
name: "Dashboard"
|
|
97
|
+
});
|
|
98
|
+
const ordersLink = within(desktopNavigation).getByRole("link", {
|
|
99
|
+
name: "Orders"
|
|
100
|
+
});
|
|
101
|
+
expect(dashboardLink.getAttribute("href")).toBe("/dashboard");
|
|
102
|
+
expect(ordersLink.getAttribute("href")).toBe("/orders");
|
|
103
|
+
const reportsItem = within(desktopNavigation).getByText("Reports");
|
|
104
|
+
expect(reportsItem).toBeTruthy();
|
|
105
|
+
expect(within(desktopNavigation).getByText("Integrations")).toBeTruthy();
|
|
106
|
+
// Nested list items show when parent item is clicked
|
|
107
|
+
expect(within(desktopNavigation).queryByText("Sales")).toBeNull();
|
|
108
|
+
expect(within(desktopNavigation).queryByText("Traffic")).toBeNull();
|
|
109
|
+
await user.click(reportsItem);
|
|
110
|
+
expect(within(desktopNavigation).getByText("Sales")).toBeTruthy();
|
|
111
|
+
expect(within(desktopNavigation).getByText("Traffic")).toBeTruthy();
|
|
112
|
+
});
|
|
113
|
+
test("starts with parent items expanded if any of their children is the current page", () => {
|
|
114
|
+
const NAVIGATION = [
|
|
115
|
+
{
|
|
116
|
+
segment: "reports",
|
|
117
|
+
title: "Reports",
|
|
118
|
+
icon: _jsx(BarChartIcon, {}),
|
|
119
|
+
children: [
|
|
120
|
+
{
|
|
121
|
+
segment: "sales",
|
|
122
|
+
title: "Sales",
|
|
123
|
+
icon: _jsx(DescriptionIcon, {})
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
segment: "traffic",
|
|
127
|
+
title: "Traffic",
|
|
128
|
+
icon: _jsx(DescriptionIcon, {})
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
}
|
|
132
|
+
];
|
|
133
|
+
const mockRouter = {
|
|
134
|
+
pathname: "/reports/sales",
|
|
135
|
+
searchParams: new URLSearchParams(),
|
|
136
|
+
navigate: vi.fn()
|
|
137
|
+
};
|
|
138
|
+
render(_jsx(AppProvider, { navigation: NAVIGATION, router: mockRouter, children: _jsx(DashboardLayout, { children: "Hello world" }) }));
|
|
139
|
+
const desktopNavigation = screen.getByRole("navigation", {
|
|
140
|
+
name: "Desktop"
|
|
141
|
+
});
|
|
142
|
+
expect(within(desktopNavigation).getByText("Sales")).toBeTruthy();
|
|
143
|
+
expect(within(desktopNavigation).getByText("Traffic")).toBeTruthy();
|
|
144
|
+
});
|
|
145
|
+
test("shows correct selected page item", () => {
|
|
146
|
+
const NAVIGATION = [
|
|
147
|
+
{
|
|
148
|
+
title: "Dashboard",
|
|
149
|
+
segment: "dashboard",
|
|
150
|
+
icon: _jsx(DashboardIcon, {})
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
title: "Orders",
|
|
154
|
+
segment: "orders",
|
|
155
|
+
icon: _jsx(ShoppingCartIcon, {})
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
segment: "dynamic",
|
|
159
|
+
title: "Dynamic",
|
|
160
|
+
icon: _jsx(BarChartIcon, {}),
|
|
161
|
+
pattern: "dynamic/:dynamicId"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
segment: "optional",
|
|
165
|
+
title: "Optional",
|
|
166
|
+
pattern: "optional{/:optionalId}?"
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
segment: "oneOrMore",
|
|
170
|
+
title: "One or more",
|
|
171
|
+
pattern: "oneormore{/:oneormoreId}+"
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
segment: "zeroOrMore",
|
|
175
|
+
title: "Zero or more",
|
|
176
|
+
pattern: "zeroormore{/:zeroormoreId}*"
|
|
177
|
+
}
|
|
178
|
+
];
|
|
179
|
+
function AppWithPathname({ pathname }) {
|
|
180
|
+
const mockRouter = {
|
|
181
|
+
pathname,
|
|
182
|
+
searchParams: new URLSearchParams(),
|
|
183
|
+
navigate: vi.fn()
|
|
184
|
+
};
|
|
185
|
+
return (_jsx(AppProvider, { navigation: NAVIGATION, router: mockRouter, children: _jsx(DashboardLayout, { children: "Hello world" }) }));
|
|
186
|
+
}
|
|
187
|
+
const { rerender } = render(_jsx(AppWithPathname, { pathname: "/dashboard" }));
|
|
188
|
+
const desktopNavigation = screen.getByRole("navigation", {
|
|
189
|
+
name: "Desktop"
|
|
190
|
+
});
|
|
191
|
+
expect(within(desktopNavigation).getByRole("link", { name: "Dashboard" })).toHaveClass("Mui-selected");
|
|
192
|
+
rerender(_jsx(AppWithPathname, { pathname: "/orders" }));
|
|
193
|
+
expect(within(desktopNavigation).getByRole("link", { name: "Dashboard" })).not.toHaveClass("Mui-selected");
|
|
194
|
+
expect(within(desktopNavigation).getByRole("link", { name: "Orders" })).toHaveClass("Mui-selected");
|
|
195
|
+
rerender(_jsx(AppWithPathname, { pathname: "/dynamic" }));
|
|
196
|
+
expect(within(desktopNavigation).getByRole("link", { name: "Dynamic" })).not.toHaveClass("Mui-selected");
|
|
197
|
+
rerender(_jsx(AppWithPathname, { pathname: "/dynamic/123" }));
|
|
198
|
+
expect(within(desktopNavigation).getByRole("link", { name: "Dynamic" })).toHaveClass("Mui-selected");
|
|
199
|
+
rerender(_jsx(AppWithPathname, { pathname: "/dynamic/123/456" }));
|
|
200
|
+
expect(within(desktopNavigation).getByRole("link", { name: "Dynamic" })).not.toHaveClass("Mui-selected");
|
|
201
|
+
rerender(_jsx(AppWithPathname, { pathname: "/optional" }));
|
|
202
|
+
expect(within(desktopNavigation).getByRole("link", { name: "Optional" })).toHaveClass("Mui-selected");
|
|
203
|
+
rerender(_jsx(AppWithPathname, { pathname: "/optional/123" }));
|
|
204
|
+
expect(within(desktopNavigation).getByRole("link", { name: "Optional" })).toHaveClass("Mui-selected");
|
|
205
|
+
rerender(_jsx(AppWithPathname, { pathname: "/optional/123/456" }));
|
|
206
|
+
expect(within(desktopNavigation).getByRole("link", { name: "Optional" })).not.toHaveClass("Mui-selected");
|
|
207
|
+
rerender(_jsx(AppWithPathname, { pathname: "/oneormore" }));
|
|
208
|
+
expect(within(desktopNavigation).getByRole("link", { name: "One or more" })).not.toHaveClass("Mui-selected");
|
|
209
|
+
rerender(_jsx(AppWithPathname, { pathname: "/oneormore/123" }));
|
|
210
|
+
expect(within(desktopNavigation).getByRole("link", { name: "One or more" })).toHaveClass("Mui-selected");
|
|
211
|
+
rerender(_jsx(AppWithPathname, { pathname: "/oneormore/123/456" }));
|
|
212
|
+
expect(within(desktopNavigation).getByRole("link", { name: "One or more" })).toHaveClass("Mui-selected");
|
|
213
|
+
rerender(_jsx(AppWithPathname, { pathname: "/zeroormore" }));
|
|
214
|
+
expect(within(desktopNavigation).getByRole("link", { name: "Zero or more" })).toHaveClass("Mui-selected");
|
|
215
|
+
rerender(_jsx(AppWithPathname, { pathname: "/zeroormore/123" }));
|
|
216
|
+
expect(within(desktopNavigation).getByRole("link", { name: "Zero or more" })).toHaveClass("Mui-selected");
|
|
217
|
+
rerender(_jsx(AppWithPathname, { pathname: "/zeroormore/123/456" }));
|
|
218
|
+
expect(within(desktopNavigation).getByRole("link", { name: "Zero or more" })).toHaveClass("Mui-selected");
|
|
219
|
+
});
|
|
220
|
+
test("renders navigation actions", async () => {
|
|
221
|
+
const NAVIGATION = [
|
|
222
|
+
{
|
|
223
|
+
title: "Item 1",
|
|
224
|
+
segment: "item1",
|
|
225
|
+
icon: _jsx(DescriptionIcon, {}),
|
|
226
|
+
action: _jsx("div", { children: "Action 1" })
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
title: "Item",
|
|
230
|
+
segment: "item2",
|
|
231
|
+
icon: _jsx(DescriptionIcon, {}),
|
|
232
|
+
action: _jsx("div", { children: "Action 2" })
|
|
233
|
+
}
|
|
234
|
+
];
|
|
235
|
+
render(_jsx(AppProvider, { navigation: NAVIGATION, children: _jsx(DashboardLayout, { children: "Hello world" }) }));
|
|
236
|
+
const desktopNavigation = screen.getByRole("navigation", {
|
|
237
|
+
name: "Desktop"
|
|
238
|
+
});
|
|
239
|
+
expect(within(desktopNavigation).getByText("Action 1")).toBeTruthy();
|
|
240
|
+
expect(within(desktopNavigation).getByText("Action 2")).toBeTruthy();
|
|
241
|
+
});
|
|
242
|
+
test("renders sidebar footer slot content", async () => {
|
|
243
|
+
function SidebarFooter() {
|
|
244
|
+
return _jsx("div", { children: "I am footer" });
|
|
245
|
+
}
|
|
246
|
+
render(_jsx(AppProvider, { children: _jsx(DashboardLayout, { slots: { sidebarFooter: SidebarFooter }, children: "Hello world" }) }));
|
|
247
|
+
const desktopNavigation = screen.getByRole("navigation", {
|
|
248
|
+
name: "Desktop"
|
|
249
|
+
});
|
|
250
|
+
expect(within(desktopNavigation).getByText("I am footer")).toBeTruthy();
|
|
251
|
+
});
|
|
252
|
+
test("renders without the navigation and toggle button", async () => {
|
|
253
|
+
const NAVIGATION = [
|
|
254
|
+
{
|
|
255
|
+
title: "Dashboard",
|
|
256
|
+
segment: "dashboard",
|
|
257
|
+
icon: _jsx(DashboardIcon, {})
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
title: "Orders",
|
|
261
|
+
segment: "orders",
|
|
262
|
+
icon: _jsx(ShoppingCartIcon, {})
|
|
263
|
+
}
|
|
264
|
+
];
|
|
265
|
+
render(_jsx(AppProvider, { navigation: NAVIGATION, children: _jsx(DashboardLayout, { hideNavigation: true, children: "Hello world" }) }));
|
|
266
|
+
const desktopNavigation = screen.queryByRole("navigation", {
|
|
267
|
+
name: "Desktop"
|
|
268
|
+
});
|
|
269
|
+
const navigationToggle = screen.queryByLabelText("Collapse menu");
|
|
270
|
+
// Expect that navigation and menu button are not rendered
|
|
271
|
+
expect(desktopNavigation).toBeNull();
|
|
272
|
+
expect(navigationToggle).toBeNull();
|
|
273
|
+
// Ensure that main content is still rendered
|
|
274
|
+
expect(screen.getByText("Hello world")).toBeTruthy();
|
|
275
|
+
});
|
|
276
|
+
test("renders without default collapsed navigation on desktop", async () => {
|
|
277
|
+
const NAVIGATION = [
|
|
278
|
+
{
|
|
279
|
+
title: "Dashboard",
|
|
280
|
+
segment: "dashboard",
|
|
281
|
+
icon: _jsx(DashboardIcon, {})
|
|
282
|
+
}
|
|
283
|
+
];
|
|
284
|
+
render(_jsx(AppProvider, { navigation: NAVIGATION, children: _jsx(DashboardLayout, { defaultSidebarCollapsed: true, children: "Hello world" }) }));
|
|
285
|
+
// Expect that menu button has expand action
|
|
286
|
+
expect(screen.getAllByLabelText("Expand menu")).toBeTruthy();
|
|
287
|
+
expect(screen.queryByLabelText("Collapse menu")).toBeNull();
|
|
288
|
+
// Ensure that main content is still rendered
|
|
289
|
+
expect(screen.getByText("Hello world")).toBeTruthy();
|
|
290
|
+
});
|
|
291
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Navigation } from "../AppProvider";
|
|
2
|
+
interface DashboardSidebarSubNavigationProps {
|
|
3
|
+
subNavigation: Navigation;
|
|
4
|
+
basePath?: string;
|
|
5
|
+
depth?: number;
|
|
6
|
+
onLinkClick: () => void;
|
|
7
|
+
isMini?: boolean;
|
|
8
|
+
isFullyExpanded?: boolean;
|
|
9
|
+
hasDrawerTransitions?: boolean;
|
|
10
|
+
selectedItemId: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* @ignore - internal component.
|
|
14
|
+
*/
|
|
15
|
+
declare function DashboardSidebarSubNavigation({ subNavigation, basePath, depth, onLinkClick, isMini, isFullyExpanded, hasDrawerTransitions, selectedItemId }: DashboardSidebarSubNavigationProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export { DashboardSidebarSubNavigation };
|