@etsoo/toolpad 1.0.9 → 1.0.11
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/build/Account/Account.d.ts +0 -3
- package/build/Account/Account.js +0 -41
- package/build/Account/AccountPreview.d.ts +0 -3
- package/build/Account/AccountPreview.js +0 -37
- package/build/AppProvider/AppProvider.d.ts +2 -1
- package/build/AppProvider/AppProviderComponent.d.ts +0 -3
- package/build/AppProvider/AppProviderComponent.js +0 -90
- package/build/DashboardLayout/DashboardLayout.d.ts +0 -3
- package/build/DashboardLayout/DashboardLayout.js +0 -82
- package/build/DashboardLayout/DashboardSidebarSubNavigation.js +4 -6
- package/build/DashboardLayout/TitleBar.js +5 -2
- package/build/PageContainer/PageContainer.d.ts +11 -16
- package/build/PageContainer/PageContainer.js +43 -40
- package/build/PageContainer/PageContainer.test.js +40 -30
- package/build/shared/navigation.js +15 -7
- package/build/useActivePage/useActivePage.d.ts +1 -4
- package/build/useActivePage/useActivePage.js +2 -3
- package/package.json +1 -3
- package/src/Account/Account.tsx +0 -42
- package/src/Account/AccountPreview.tsx +0 -38
- package/src/AppProvider/AppProvider.tsx +1 -1
- package/src/AppProvider/AppProviderComponent.tsx +0 -95
- package/src/DashboardLayout/DashboardLayout.tsx +0 -87
- package/src/DashboardLayout/DashboardSidebarSubNavigation.tsx +4 -15
- package/src/DashboardLayout/TitleBar.tsx +12 -10
- package/src/PageContainer/PageContainer.test.tsx +88 -59
- package/src/PageContainer/PageContainer.tsx +73 -65
- package/src/shared/navigation.tsx +15 -8
- package/src/useActivePage/useActivePage.ts +3 -7
|
@@ -9,9 +9,13 @@ export function getPageItemFullPath(basePath, navigationItem) {
|
|
|
9
9
|
return `${basePath}${basePath && !navigationItem.segment ? "" : "/"}${navigationItem.segment ?? ""}`;
|
|
10
10
|
}
|
|
11
11
|
export function isPageItemSelected(navigationItem, basePath, pathname) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
if (navigationItem.pattern) {
|
|
13
|
+
return pathToRegexp(`${basePath}/${navigationItem.pattern}`).test(pathname);
|
|
14
|
+
}
|
|
15
|
+
if (navigationItem.subs) {
|
|
16
|
+
return navigationItem.subs.some((sub) => new RegExp(sub).test(pathname));
|
|
17
|
+
}
|
|
18
|
+
return getPageItemFullPath(basePath, navigationItem) === pathname;
|
|
15
19
|
}
|
|
16
20
|
export function hasSelectedNavigationChildren(navigationItem, basePath, pathname) {
|
|
17
21
|
if (isPageItem(navigationItem) && navigationItem.children) {
|
|
@@ -109,11 +113,15 @@ function getItemLookup(navigation) {
|
|
|
109
113
|
export function matchPath(navigation, path) {
|
|
110
114
|
const lookup = getItemLookup(navigation);
|
|
111
115
|
for (const [key, item] of lookup.entries()) {
|
|
112
|
-
if (typeof key === "string"
|
|
113
|
-
|
|
116
|
+
if (typeof key === "string") {
|
|
117
|
+
if (key === path)
|
|
118
|
+
return item;
|
|
119
|
+
else if (item.subs?.some((sub) => new RegExp(sub).test(path)))
|
|
120
|
+
return item;
|
|
114
121
|
}
|
|
115
|
-
if (key instanceof RegExp
|
|
116
|
-
|
|
122
|
+
else if (key instanceof RegExp) {
|
|
123
|
+
if (key.test(path))
|
|
124
|
+
return item;
|
|
117
125
|
}
|
|
118
126
|
}
|
|
119
127
|
return null;
|
|
@@ -2,10 +2,7 @@ import type { Breadcrumb } from "../PageContainer";
|
|
|
2
2
|
export interface ActivePage {
|
|
3
3
|
title: string;
|
|
4
4
|
path: string;
|
|
5
|
-
|
|
6
|
-
* @deprecated Use `breadcrumbs` instead.
|
|
7
|
-
*/
|
|
8
|
-
breadCrumbs: Breadcrumb[];
|
|
5
|
+
sourcePath: string;
|
|
9
6
|
breadcrumbs: Breadcrumb[];
|
|
10
7
|
}
|
|
11
8
|
export declare function useActivePage(): ActivePage | null;
|
|
@@ -40,9 +40,8 @@ export function useActivePage() {
|
|
|
40
40
|
return {
|
|
41
41
|
title: getItemTitle(activeItem),
|
|
42
42
|
path: getItemPath(navigationContext, activeItem),
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
breadCrumbs: breadcrumbs
|
|
43
|
+
sourcePath: pathname,
|
|
44
|
+
breadcrumbs
|
|
46
45
|
};
|
|
47
46
|
}, [activeItem, rootItem, pathname, navigationContext]);
|
|
48
47
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etsoo/toolpad",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"author": "ETSOO",
|
|
5
5
|
"description": "Dashboard framework extention based on Toolpad Core",
|
|
6
6
|
"main": "build/index.js",
|
|
@@ -50,7 +50,6 @@
|
|
|
50
50
|
"@mui/utils": "6.3.1",
|
|
51
51
|
"invariant": "2.2.4",
|
|
52
52
|
"path-to-regexp": "6.3.0",
|
|
53
|
-
"prop-types": "15.8.1",
|
|
54
53
|
"react": "^18.3.1"
|
|
55
54
|
},
|
|
56
55
|
"overrides": {
|
|
@@ -62,7 +61,6 @@
|
|
|
62
61
|
"@testing-library/jest-dom": "^6.6.3",
|
|
63
62
|
"@testing-library/react": "^16.1.0",
|
|
64
63
|
"@types/invariant": "2.2.37",
|
|
65
|
-
"@types/prop-types": "15.7.14",
|
|
66
64
|
"@types/react": "18.3.12",
|
|
67
65
|
"@types/react-dom": "18.3.1",
|
|
68
66
|
"@vitejs/plugin-react": "4.3.4",
|
package/src/Account/Account.tsx
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import PropTypes from "prop-types";
|
|
3
2
|
import Button from "@mui/material/Button";
|
|
4
3
|
import Popover from "@mui/material/Popover";
|
|
5
4
|
import Divider from "@mui/material/Divider";
|
|
@@ -164,45 +163,4 @@ function Account(props: AccountProps) {
|
|
|
164
163
|
);
|
|
165
164
|
}
|
|
166
165
|
|
|
167
|
-
Account.propTypes /* remove-proptypes */ = {
|
|
168
|
-
// ┌────────────────────────────── Warning ──────────────────────────────┐
|
|
169
|
-
// │ These PropTypes are generated from the TypeScript type definitions. │
|
|
170
|
-
// │ To update them, edit the TypeScript types and run `pnpm proptypes`. │
|
|
171
|
-
// └─────────────────────────────────────────────────────────────────────┘
|
|
172
|
-
/**
|
|
173
|
-
* The props used for each slot inside.
|
|
174
|
-
*/
|
|
175
|
-
slotProps: PropTypes.shape({
|
|
176
|
-
popover: PropTypes.object,
|
|
177
|
-
popoverContent: PropTypes.object,
|
|
178
|
-
preview: PropTypes.shape({
|
|
179
|
-
handleClick: PropTypes.func,
|
|
180
|
-
open: PropTypes.bool,
|
|
181
|
-
slotProps: PropTypes.shape({
|
|
182
|
-
avatar: PropTypes.object,
|
|
183
|
-
avatarIconButton: PropTypes.object,
|
|
184
|
-
moreIconButton: PropTypes.object
|
|
185
|
-
}),
|
|
186
|
-
slots: PropTypes.shape({
|
|
187
|
-
avatar: PropTypes.elementType,
|
|
188
|
-
avatarIconButton: PropTypes.elementType,
|
|
189
|
-
moreIconButton: PropTypes.elementType
|
|
190
|
-
}),
|
|
191
|
-
variant: PropTypes.oneOf(["condensed", "expanded"])
|
|
192
|
-
}),
|
|
193
|
-
signInButton: PropTypes.object,
|
|
194
|
-
signOutButton: PropTypes.object
|
|
195
|
-
}),
|
|
196
|
-
/**
|
|
197
|
-
* The components used for each slot inside.
|
|
198
|
-
*/
|
|
199
|
-
slots: PropTypes.shape({
|
|
200
|
-
popover: PropTypes.elementType,
|
|
201
|
-
popoverContent: PropTypes.elementType,
|
|
202
|
-
preview: PropTypes.elementType,
|
|
203
|
-
signInButton: PropTypes.elementType,
|
|
204
|
-
signOutButton: PropTypes.elementType
|
|
205
|
-
})
|
|
206
|
-
} as any;
|
|
207
|
-
|
|
208
166
|
export { Account };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import PropTypes from "prop-types";
|
|
3
2
|
import Avatar, { AvatarProps } from "@mui/material/Avatar";
|
|
4
3
|
import Typography from "@mui/material/Typography";
|
|
5
4
|
import Tooltip from "@mui/material/Tooltip";
|
|
@@ -172,41 +171,4 @@ function AccountPreview(props: AccountPreviewProps) {
|
|
|
172
171
|
);
|
|
173
172
|
}
|
|
174
173
|
|
|
175
|
-
AccountPreview.propTypes /* remove-proptypes */ = {
|
|
176
|
-
// ┌────────────────────────────── Warning ──────────────────────────────┐
|
|
177
|
-
// │ These PropTypes are generated from the TypeScript type definitions. │
|
|
178
|
-
// │ To update them, edit the TypeScript types and run `pnpm proptypes`. │
|
|
179
|
-
// └─────────────────────────────────────────────────────────────────────┘
|
|
180
|
-
/**
|
|
181
|
-
* The handler used when the preview is expanded
|
|
182
|
-
*/
|
|
183
|
-
handleClick: PropTypes.func,
|
|
184
|
-
/**
|
|
185
|
-
* The state of the Account popover
|
|
186
|
-
* @default false
|
|
187
|
-
*/
|
|
188
|
-
open: PropTypes.bool,
|
|
189
|
-
/**
|
|
190
|
-
* The props used for each slot inside.
|
|
191
|
-
*/
|
|
192
|
-
slotProps: PropTypes.shape({
|
|
193
|
-
avatar: PropTypes.object,
|
|
194
|
-
avatarIconButton: PropTypes.object,
|
|
195
|
-
moreIconButton: PropTypes.object
|
|
196
|
-
}),
|
|
197
|
-
/**
|
|
198
|
-
* The components used for each slot inside.
|
|
199
|
-
*/
|
|
200
|
-
slots: PropTypes.shape({
|
|
201
|
-
avatar: PropTypes.elementType
|
|
202
|
-
}),
|
|
203
|
-
/**
|
|
204
|
-
* The type of account details to display.
|
|
205
|
-
* @property {'condensed'} condensed - Shows only the user's avatar.
|
|
206
|
-
* @property {'expanded'} expanded - Displays the user's avatar, name, and email if available.
|
|
207
|
-
* @default 'condensed'
|
|
208
|
-
*/
|
|
209
|
-
variant: PropTypes.oneOf(["condensed", "expanded"])
|
|
210
|
-
} as any;
|
|
211
|
-
|
|
212
174
|
export { AccountPreview };
|
|
@@ -22,7 +22,6 @@ export interface Router {
|
|
|
22
22
|
|
|
23
23
|
export interface Branding {
|
|
24
24
|
title?:
|
|
25
|
-
| string
|
|
26
25
|
| React.ReactNode
|
|
27
26
|
| [string, (handler: React.MouseEvent<HTMLSpanElement>) => void];
|
|
28
27
|
logo?: React.ReactNode;
|
|
@@ -36,6 +35,7 @@ export interface NavigationPageItem {
|
|
|
36
35
|
pattern?: string;
|
|
37
36
|
action?: React.ReactNode;
|
|
38
37
|
children?: Navigation;
|
|
38
|
+
subs?: string[];
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
export interface NavigationSubheaderItem {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import PropTypes from "prop-types";
|
|
3
2
|
import {
|
|
4
3
|
BrandingContext,
|
|
5
4
|
NavigationContext,
|
|
@@ -69,98 +68,4 @@ function AppProvider(props: AppProviderProps) {
|
|
|
69
68
|
);
|
|
70
69
|
}
|
|
71
70
|
|
|
72
|
-
AppProvider.propTypes /* remove-proptypes */ = {
|
|
73
|
-
// ┌────────────────────────────── Warning ──────────────────────────────┐
|
|
74
|
-
// │ These PropTypes are generated from the TypeScript type definitions. │
|
|
75
|
-
// │ To update them, edit the TypeScript types and run `pnpm proptypes`. │
|
|
76
|
-
// └─────────────────────────────────────────────────────────────────────┘
|
|
77
|
-
/**
|
|
78
|
-
* Authentication methods.
|
|
79
|
-
* @default null
|
|
80
|
-
*/
|
|
81
|
-
authentication: PropTypes.shape({
|
|
82
|
-
signIn: PropTypes.func.isRequired,
|
|
83
|
-
signOut: PropTypes.func.isRequired
|
|
84
|
-
}),
|
|
85
|
-
/**
|
|
86
|
-
* Branding options for the app.
|
|
87
|
-
* @default null
|
|
88
|
-
*/
|
|
89
|
-
branding: PropTypes.shape({
|
|
90
|
-
logo: PropTypes.node,
|
|
91
|
-
title: PropTypes.node
|
|
92
|
-
}),
|
|
93
|
-
/**
|
|
94
|
-
* The content of the app provider.
|
|
95
|
-
*/
|
|
96
|
-
children: PropTypes.node,
|
|
97
|
-
/**
|
|
98
|
-
* Navigation definition for the app.
|
|
99
|
-
* @default []
|
|
100
|
-
*/
|
|
101
|
-
navigation: PropTypes.arrayOf(
|
|
102
|
-
PropTypes.oneOfType([
|
|
103
|
-
PropTypes.shape({
|
|
104
|
-
action: PropTypes.node,
|
|
105
|
-
children: PropTypes.arrayOf(
|
|
106
|
-
PropTypes.oneOfType([
|
|
107
|
-
PropTypes.object,
|
|
108
|
-
PropTypes.shape({
|
|
109
|
-
kind: PropTypes.oneOf(["header"]).isRequired,
|
|
110
|
-
title: PropTypes.string.isRequired
|
|
111
|
-
}),
|
|
112
|
-
PropTypes.shape({
|
|
113
|
-
kind: PropTypes.oneOf(["divider"]).isRequired
|
|
114
|
-
})
|
|
115
|
-
]).isRequired
|
|
116
|
-
),
|
|
117
|
-
icon: PropTypes.node,
|
|
118
|
-
kind: PropTypes.oneOf(["page"]),
|
|
119
|
-
pattern: PropTypes.string,
|
|
120
|
-
segment: PropTypes.string,
|
|
121
|
-
title: PropTypes.string
|
|
122
|
-
}),
|
|
123
|
-
PropTypes.shape({
|
|
124
|
-
kind: PropTypes.oneOf(["header"]).isRequired,
|
|
125
|
-
title: PropTypes.string.isRequired
|
|
126
|
-
}),
|
|
127
|
-
PropTypes.shape({
|
|
128
|
-
kind: PropTypes.oneOf(["divider"]).isRequired
|
|
129
|
-
})
|
|
130
|
-
]).isRequired
|
|
131
|
-
),
|
|
132
|
-
/**
|
|
133
|
-
* Router implementation used inside Toolpad components.
|
|
134
|
-
* @default null
|
|
135
|
-
*/
|
|
136
|
-
router: PropTypes.shape({
|
|
137
|
-
navigate: PropTypes.func.isRequired,
|
|
138
|
-
pathname: PropTypes.string.isRequired,
|
|
139
|
-
searchParams: PropTypes.instanceOf(URLSearchParams).isRequired
|
|
140
|
-
}),
|
|
141
|
-
/**
|
|
142
|
-
* Session info about the current user.
|
|
143
|
-
* @default null
|
|
144
|
-
*/
|
|
145
|
-
session: PropTypes.shape({
|
|
146
|
-
user: PropTypes.shape({
|
|
147
|
-
email: PropTypes.string,
|
|
148
|
-
id: PropTypes.string,
|
|
149
|
-
image: PropTypes.string,
|
|
150
|
-
name: PropTypes.string
|
|
151
|
-
})
|
|
152
|
-
}),
|
|
153
|
-
/**
|
|
154
|
-
* [Theme or themes](https://mui.com/toolpad/core/react-app-provider/#theming) to be used by the app in light/dark mode. A [CSS variables theme](https://mui.com/material-ui/customization/css-theme-variables/overview/) is recommended.
|
|
155
|
-
* @default createTheme()
|
|
156
|
-
*/
|
|
157
|
-
theme: PropTypes.object,
|
|
158
|
-
/**
|
|
159
|
-
* The window where the application is rendered.
|
|
160
|
-
* This is needed when rendering the app inside an iframe, for example.
|
|
161
|
-
* @default window
|
|
162
|
-
*/
|
|
163
|
-
window: PropTypes.object
|
|
164
|
-
} as any;
|
|
165
|
-
|
|
166
71
|
export { AppProvider };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import * as React from "react";
|
|
3
|
-
import PropTypes from "prop-types";
|
|
4
3
|
import { styled, useTheme, type Theme, SxProps } from "@mui/material";
|
|
5
4
|
import MuiAppBar from "@mui/material/AppBar";
|
|
6
5
|
import Box from "@mui/material/Box";
|
|
@@ -9,12 +8,10 @@ import IconButton from "@mui/material/IconButton";
|
|
|
9
8
|
import Stack from "@mui/material/Stack";
|
|
10
9
|
import Toolbar from "@mui/material/Toolbar";
|
|
11
10
|
import Tooltip from "@mui/material/Tooltip";
|
|
12
|
-
import Typography from "@mui/material/Typography";
|
|
13
11
|
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
14
12
|
import type {} from "@mui/material/themeCssVarsAugmentation";
|
|
15
13
|
import MenuIcon from "@mui/icons-material/Menu";
|
|
16
14
|
import MenuOpenIcon from "@mui/icons-material/MenuOpen";
|
|
17
|
-
import { Link } from "../shared/Link";
|
|
18
15
|
import { NavigationContext, WindowContext } from "../shared/context";
|
|
19
16
|
import { Account, type AccountProps } from "../Account";
|
|
20
17
|
import { DashboardSidebarSubNavigation } from "./DashboardSidebarSubNavigation";
|
|
@@ -456,88 +453,4 @@ function DashboardLayout(props: DashboardLayoutProps) {
|
|
|
456
453
|
);
|
|
457
454
|
}
|
|
458
455
|
|
|
459
|
-
DashboardLayout.propTypes /* remove-proptypes */ = {
|
|
460
|
-
// ┌────────────────────────────── Warning ──────────────────────────────┐
|
|
461
|
-
// │ These PropTypes are generated from the TypeScript type definitions. │
|
|
462
|
-
// │ To update them, edit the TypeScript types and run `pnpm proptypes`. │
|
|
463
|
-
// └─────────────────────────────────────────────────────────────────────┘
|
|
464
|
-
/**
|
|
465
|
-
* The content of the dashboard.
|
|
466
|
-
*/
|
|
467
|
-
children: PropTypes.node,
|
|
468
|
-
/**
|
|
469
|
-
* Whether the sidebar should start collapsed in desktop size screens.
|
|
470
|
-
* @default false
|
|
471
|
-
*/
|
|
472
|
-
defaultSidebarCollapsed: PropTypes.bool,
|
|
473
|
-
/**
|
|
474
|
-
* Whether the sidebar should not be collapsible to a mini variant in desktop and tablet viewports.
|
|
475
|
-
* @default false
|
|
476
|
-
*/
|
|
477
|
-
disableCollapsibleSidebar: PropTypes.bool,
|
|
478
|
-
/**
|
|
479
|
-
* Whether the navigation bar and menu icon should be hidden
|
|
480
|
-
* @default false
|
|
481
|
-
*/
|
|
482
|
-
hideNavigation: PropTypes.bool,
|
|
483
|
-
/**
|
|
484
|
-
* Width of the sidebar when expanded.
|
|
485
|
-
* @default 320
|
|
486
|
-
*/
|
|
487
|
-
sidebarExpandedWidth: PropTypes.oneOfType([
|
|
488
|
-
PropTypes.number,
|
|
489
|
-
PropTypes.string
|
|
490
|
-
]),
|
|
491
|
-
/**
|
|
492
|
-
* The props used for each slot inside.
|
|
493
|
-
* @default {}
|
|
494
|
-
*/
|
|
495
|
-
slotProps: PropTypes.shape({
|
|
496
|
-
sidebarFooter: PropTypes.shape({
|
|
497
|
-
mini: PropTypes.bool.isRequired
|
|
498
|
-
}),
|
|
499
|
-
toolbarAccount: PropTypes.shape({
|
|
500
|
-
localeText: PropTypes.shape({
|
|
501
|
-
iconButtonAriaLabel: PropTypes.string,
|
|
502
|
-
signInLabel: PropTypes.string,
|
|
503
|
-
signOutLabel: PropTypes.string
|
|
504
|
-
}),
|
|
505
|
-
slotProps: PropTypes.shape({
|
|
506
|
-
popover: PropTypes.object,
|
|
507
|
-
popoverContent: PropTypes.object,
|
|
508
|
-
preview: PropTypes.object,
|
|
509
|
-
signInButton: PropTypes.object,
|
|
510
|
-
signOutButton: PropTypes.object
|
|
511
|
-
}),
|
|
512
|
-
slots: PropTypes.shape({
|
|
513
|
-
popover: PropTypes.elementType,
|
|
514
|
-
popoverContent: PropTypes.elementType,
|
|
515
|
-
preview: PropTypes.elementType,
|
|
516
|
-
signInButton: PropTypes.elementType,
|
|
517
|
-
signOutButton: PropTypes.elementType
|
|
518
|
-
})
|
|
519
|
-
}),
|
|
520
|
-
toolbarActions: PropTypes.object
|
|
521
|
-
}),
|
|
522
|
-
/**
|
|
523
|
-
* The components used for each slot inside.
|
|
524
|
-
* @default {}
|
|
525
|
-
*/
|
|
526
|
-
slots: PropTypes.shape({
|
|
527
|
-
sidebarFooter: PropTypes.elementType,
|
|
528
|
-
toolbarAccount: PropTypes.elementType,
|
|
529
|
-
toolbarActions: PropTypes.elementType
|
|
530
|
-
}),
|
|
531
|
-
/**
|
|
532
|
-
* The system prop that allows defining system overrides as well as additional CSS styles.
|
|
533
|
-
*/
|
|
534
|
-
sx: PropTypes.oneOfType([
|
|
535
|
-
PropTypes.arrayOf(
|
|
536
|
-
PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])
|
|
537
|
-
),
|
|
538
|
-
PropTypes.func,
|
|
539
|
-
PropTypes.object
|
|
540
|
-
])
|
|
541
|
-
} as any;
|
|
542
|
-
|
|
543
456
|
export { DashboardLayout };
|
|
@@ -175,21 +175,10 @@ function DashboardSidebarSubNavigation({
|
|
|
175
175
|
|
|
176
176
|
const listItemIconSize = 34;
|
|
177
177
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
pathname
|
|
182
|
-
);
|
|
183
|
-
|
|
184
|
-
if (
|
|
185
|
-
process.env.NODE_ENV !== "production" &&
|
|
186
|
-
isSelected &&
|
|
187
|
-
selectedItemId
|
|
188
|
-
) {
|
|
189
|
-
console.warn(
|
|
190
|
-
`Duplicate selected path in navigation: ${navigationItemFullPath}`
|
|
191
|
-
);
|
|
192
|
-
}
|
|
178
|
+
// If the item is selected, we don't want to select more
|
|
179
|
+
const isSelected = selectedItemId
|
|
180
|
+
? false
|
|
181
|
+
: isPageItemSelected(navigationItem, basePath, pathname);
|
|
193
182
|
|
|
194
183
|
if (isSelected && !selectedItemId) {
|
|
195
184
|
selectedItemId = navigationItemId;
|
|
@@ -54,14 +54,16 @@ export function TitleBar() {
|
|
|
54
54
|
return [title, false];
|
|
55
55
|
}, [title]);
|
|
56
56
|
|
|
57
|
-
if (
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
</
|
|
65
|
-
|
|
66
|
-
|
|
57
|
+
if (hasLink) {
|
|
58
|
+
return (
|
|
59
|
+
<Link href="/" style={{ color: "inherit", textDecoration: "none" }}>
|
|
60
|
+
<Stack direction="row" alignItems="center">
|
|
61
|
+
{branding?.logo && <LogoContainer>{branding.logo}</LogoContainer>}
|
|
62
|
+
{titleUI}
|
|
63
|
+
</Stack>
|
|
64
|
+
</Link>
|
|
65
|
+
);
|
|
66
|
+
} else {
|
|
67
|
+
return titleUI;
|
|
68
|
+
}
|
|
67
69
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { expect, describe, test, vi } from "vitest";
|
|
2
|
-
import { render, within, screen } from "@testing-library/react";
|
|
2
|
+
import { render, within, screen, act } from "@testing-library/react";
|
|
3
3
|
import { userEvent } from "@testing-library/user-event";
|
|
4
|
-
import { PageContainer } from "./PageContainer";
|
|
4
|
+
import { PageContainer, PageDataContextProvider } from "./PageContainer";
|
|
5
5
|
import describeConformance from "../utils/describeConformance";
|
|
6
6
|
import { AppProvider } from "../AppProvider/AppProviderComponent";
|
|
7
7
|
|
|
@@ -14,23 +14,28 @@ describe("PageContainer", () => {
|
|
|
14
14
|
}));
|
|
15
15
|
|
|
16
16
|
test("renders page container correctly", async () => {
|
|
17
|
-
const user =
|
|
17
|
+
const user = userEvent.setup();
|
|
18
18
|
const router = {
|
|
19
19
|
pathname: "/orders",
|
|
20
20
|
searchParams: new URLSearchParams(),
|
|
21
21
|
navigate: vi.fn()
|
|
22
22
|
};
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
{
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
23
|
+
|
|
24
|
+
act(() => {
|
|
25
|
+
render(
|
|
26
|
+
<AppProvider
|
|
27
|
+
navigation={[
|
|
28
|
+
{ segment: "", title: "Home" },
|
|
29
|
+
{ segment: "orders", title: "Orders" }
|
|
30
|
+
]}
|
|
31
|
+
router={router}
|
|
32
|
+
>
|
|
33
|
+
<PageDataContextProvider>
|
|
34
|
+
<PageContainer />
|
|
35
|
+
</PageDataContextProvider>
|
|
36
|
+
</AppProvider>
|
|
37
|
+
);
|
|
38
|
+
});
|
|
34
39
|
|
|
35
40
|
const breadcrumbs = screen.getByRole("navigation", { name: "breadcrumb" });
|
|
36
41
|
|
|
@@ -70,11 +75,20 @@ describe("PageContainer", () => {
|
|
|
70
75
|
};
|
|
71
76
|
|
|
72
77
|
const branding = { title: "ACME" };
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
+
|
|
79
|
+
act(() => {
|
|
80
|
+
render(
|
|
81
|
+
<AppProvider
|
|
82
|
+
branding={branding}
|
|
83
|
+
navigation={navigation}
|
|
84
|
+
router={router}
|
|
85
|
+
>
|
|
86
|
+
<PageDataContextProvider>
|
|
87
|
+
<PageContainer />
|
|
88
|
+
</PageDataContextProvider>
|
|
89
|
+
</AppProvider>
|
|
90
|
+
);
|
|
91
|
+
});
|
|
78
92
|
|
|
79
93
|
const breadcrumbs = screen.getByRole("navigation", { name: "breadcrumb" });
|
|
80
94
|
|
|
@@ -84,27 +98,33 @@ describe("PageContainer", () => {
|
|
|
84
98
|
});
|
|
85
99
|
|
|
86
100
|
test("renders dynamic correctly", async () => {
|
|
87
|
-
const user =
|
|
101
|
+
const user = userEvent.setup();
|
|
88
102
|
const router = {
|
|
89
103
|
pathname: "/orders/123",
|
|
90
104
|
searchParams: new URLSearchParams(),
|
|
91
105
|
navigate: vi.fn()
|
|
92
106
|
};
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
{
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
107
|
+
|
|
108
|
+
act(() => {
|
|
109
|
+
render(
|
|
110
|
+
<AppProvider
|
|
111
|
+
navigation={[
|
|
112
|
+
{ segment: "", title: "Home" },
|
|
113
|
+
{ segment: "orders", title: "Orders", pattern: "orders/:id" }
|
|
114
|
+
]}
|
|
115
|
+
router={router}
|
|
116
|
+
>
|
|
117
|
+
<PageDataContextProvider>
|
|
118
|
+
<PageContainer />
|
|
119
|
+
</PageDataContextProvider>
|
|
120
|
+
</AppProvider>
|
|
121
|
+
);
|
|
122
|
+
});
|
|
104
123
|
|
|
105
124
|
const breadcrumbs = screen.getByRole("navigation", { name: "breadcrumb" });
|
|
106
125
|
|
|
107
126
|
const homeLink = within(breadcrumbs).getByRole("link", { name: "Home" });
|
|
127
|
+
|
|
108
128
|
await user.click(homeLink);
|
|
109
129
|
|
|
110
130
|
expect(router.navigate).toHaveBeenCalledWith(
|
|
@@ -124,26 +144,31 @@ describe("PageContainer", () => {
|
|
|
124
144
|
searchParams: new URLSearchParams(),
|
|
125
145
|
navigate: vi.fn()
|
|
126
146
|
};
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
+
|
|
148
|
+
act(() => {
|
|
149
|
+
render(
|
|
150
|
+
<AppProvider
|
|
151
|
+
navigation={[
|
|
152
|
+
{
|
|
153
|
+
segment: "users",
|
|
154
|
+
title: "Users",
|
|
155
|
+
children: [
|
|
156
|
+
{
|
|
157
|
+
segment: "invoices",
|
|
158
|
+
title: "Invoices",
|
|
159
|
+
pattern: "invoices/:id"
|
|
160
|
+
}
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
]}
|
|
164
|
+
router={router}
|
|
165
|
+
>
|
|
166
|
+
<PageDataContextProvider>
|
|
167
|
+
<PageContainer />
|
|
168
|
+
</PageDataContextProvider>
|
|
169
|
+
</AppProvider>
|
|
170
|
+
);
|
|
171
|
+
});
|
|
147
172
|
|
|
148
173
|
const breadcrumbs = screen.getByRole("navigation", { name: "breadcrumb" });
|
|
149
174
|
|
|
@@ -153,14 +178,18 @@ describe("PageContainer", () => {
|
|
|
153
178
|
});
|
|
154
179
|
|
|
155
180
|
test("renders custom breadcrumbs", async () => {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
{
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
181
|
+
act(() => {
|
|
182
|
+
render(
|
|
183
|
+
<PageDataContextProvider
|
|
184
|
+
breadcrumbs={[
|
|
185
|
+
{ title: "Hello", path: "/hello" },
|
|
186
|
+
{ title: "World", path: "/world" }
|
|
187
|
+
]}
|
|
188
|
+
>
|
|
189
|
+
<PageContainer />
|
|
190
|
+
</PageDataContextProvider>
|
|
191
|
+
);
|
|
192
|
+
});
|
|
164
193
|
|
|
165
194
|
const breadcrumbs = screen.getByRole("navigation", { name: "breadcrumb" });
|
|
166
195
|
|