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

[AppProvider] Create basic router adapters #3638

Merged
merged 19 commits into from
Jun 5, 2024
32 changes: 32 additions & 0 deletions docs/data/toolpad/core/introduction/TutorialDefault.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import DashboardIcon from '@mui/icons-material/Dashboard';
import { AppProvider, Navigation } from '@toolpad/core/AppProvider';
import { DashboardLayout } from '@toolpad/core/DashboardLayout';

const NAVIGATION: Navigation = [
{
title: 'Page',
icon: <DashboardIcon />,
},
];

export default function TutorialDefault() {
return (
<AppProvider navigation={NAVIGATION}>
<DashboardLayout>
<Box
sx={{
py: 4,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<Typography>Dashboard content</Typography>
</Box>
</DashboardLayout>
</AppProvider>
);
}
12 changes: 12 additions & 0 deletions docs/data/toolpad/core/introduction/TutorialDefault.tsx.preview
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<DashboardLayout>
<Box
sx={{
py: 4,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<Typography>Dashboard content</Typography>
</Box>
</DashboardLayout>
54 changes: 54 additions & 0 deletions docs/data/toolpad/core/introduction/TutorialPages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import DashboardIcon from '@mui/icons-material/Dashboard';
import TimelineIcon from '@mui/icons-material/Timeline';
import { AppProvider } from '@toolpad/core/AppProvider';
import { DashboardLayout } from '@toolpad/core/DashboardLayout';

const NAVIGATION = [
{
kind: 'header',
title: 'Main items',
},
{
slug: '/page',
title: 'Page',
icon: <DashboardIcon />,
},
// Add the following new item:
{
slug: '/page-2',
title: 'Page 2',
icon: <TimelineIcon />,
},
];

export default function TutorialPages() {
const [pathname, setPathname] = React.useState('/page');

const router = React.useMemo(() => {
return {
pathname,
searchParams: new URLSearchParams(),
navigate: (path) => setPathname(String(path)),
};
}, [pathname]);

return (
<AppProvider router={router} navigation={NAVIGATION}>
<DashboardLayout>
<Box
sx={{
py: 4,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<Typography>Dashboard content for {pathname}</Typography>
</Box>
</DashboardLayout>
</AppProvider>
);
}
54 changes: 54 additions & 0 deletions docs/data/toolpad/core/introduction/TutorialPages.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import DashboardIcon from '@mui/icons-material/Dashboard';
import TimelineIcon from '@mui/icons-material/Timeline';
import { AppProvider, Navigation, Router } from '@toolpad/core/AppProvider';
import { DashboardLayout } from '@toolpad/core/DashboardLayout';

const NAVIGATION: Navigation = [
{
kind: 'header',
title: 'Main items',
},
{
slug: '/page',
title: 'Page',
icon: <DashboardIcon />,
},
// Add the following new item:
{
slug: '/page-2',
title: 'Page 2',
icon: <TimelineIcon />,
},
];

export default function TutorialPages() {
const [pathname, setPathname] = React.useState('/page');

const router = React.useMemo<Router>(() => {
return {
pathname,
searchParams: new URLSearchParams(),
navigate: (path) => setPathname(String(path)),
};
}, [pathname]);

return (
<AppProvider router={router} navigation={NAVIGATION}>
<DashboardLayout>
<Box
sx={{
py: 4,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<Typography>Dashboard content for {pathname}</Typography>
</Box>
</DashboardLayout>
</AppProvider>
);
}
12 changes: 12 additions & 0 deletions docs/data/toolpad/core/introduction/TutorialPages.tsx.preview
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<DashboardLayout>
<Box
sx={{
py: 4,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<Typography>Dashboard content for {pathname}</Typography>
</Box>
</DashboardLayout>
4 changes: 2 additions & 2 deletions docs/data/toolpad/core/introduction/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,14 @@ const NAVIGATION: Navigation = [
{
slug: '/page-2',
title: 'Page 2',
icon: <DashboardIcon />,
icon: <TimelineIcon />,
},
];
```

The newly created page can now be navigated to from the sidebar, like the following:

{{"component": "modules/components/DocsImage.tsx", "src": "/static/toolpad/docs/core/tutorial-2.gif", "alt": "Toolpad Core new page", "caption": "Adding pages to navigation", "zoom": true, "indent": 1 }}
{{"demo": "TutorialPages.js", "iframe": true, "hideToolbar": true }}

## Dashboard content

Expand Down
23 changes: 3 additions & 20 deletions docs/pages/toolpad/core/api/app-provider.json
Original file line number Diff line number Diff line change
@@ -1,29 +1,12 @@
{
"props": {
"children": { "type": { "name": "node" }, "required": true },
"branding": {
"type": { "name": "shape", "description": "{ logo?: node, title?: string }" },
"default": "null"
},
"navigation": {
"type": {
"name": "arrayOf",
"description": "Array&lt;{ children?: Array&lt;object<br>&#124;&nbsp;{ kind: 'header', title: string }<br>&#124;&nbsp;{ kind: 'divider' }&gt;, icon?: node, kind?: 'page', slug?: string, title: string }<br>&#124;&nbsp;{ kind: 'header', title: string }<br>&#124;&nbsp;{ kind: 'divider' }&gt;"
},
"default": "[]"
},
"theme": { "type": { "name": "object" }, "default": "baseTheme" }
},
"props": {},
"name": "AppProvider",
"imports": [
"import { AppProvider } from '@toolpad-core/AppProvider';",
"import { AppProvider } from '@toolpad-core';"
],
"imports": ["import { AppProvider } from '@toolpad-core/nextjs';"],
"classes": [],
"spread": true,
"themeDefaultProps": null,
"muiName": "AppProvider",
"filename": "/packages/toolpad-core/src/AppProvider/AppProvider.tsx",
"filename": "/packages/toolpad-core/src/nextjs/AppProvider.tsx",
"inheritance": null,
"demos": "<ul><li><a href=\"/toolpad/core/react-app-provider/\">App Provider</a></li>\n<li><a href=\"/toolpad/core/react-dashboard-layout/\">Dashboard Layout</a></li></ul>",
"cssComponent": false
Expand Down
13 changes: 1 addition & 12 deletions docs/translations/api-docs/app-provider/app-provider.json
Original file line number Diff line number Diff line change
@@ -1,12 +1 @@
{
"componentDescription": "",
"propDescriptions": {
"branding": { "description": "Branding options for the app." },
"children": { "description": "The content of the app provider." },
"navigation": { "description": "Navigation definition for the app." },
"theme": {
"description": "<a href=\"https://mui.com/material-ui/customization/theming/\">Theme</a> used by the app."
}
},
"classDescriptions": {}
}
{ "componentDescription": "", "propDescriptions": {}, "classDescriptions": {} }
8 changes: 8 additions & 0 deletions packages/toolpad-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,21 @@
"@types/react": "18.3.1",
"@types/react-dom": "18.3.0",
"@types/sinon": "^17.0.3",
"next": "^14.2.3",
"next-router-mock": "^0.9.13",
"sinon": "^18.0.0"
},
"peerDependencies": {
"@mui/icons-material": "^5",
"@mui/material": "^5",
"next": "^14",
"react": "^18"
},
"peerDependenciesMeta": {
"next": {
"optional": true
}
},
"sideEffects": false,
"publishConfig": {
"access": "public"
Expand Down
54 changes: 46 additions & 8 deletions packages/toolpad-core/src/AppProvider/AppProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
import { ThemeProvider, Theme } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import { baseTheme } from '../themes';

export interface NavigateOptions {
history?: 'auto' | 'push' | 'replace';
}

export interface Navigate {
(url: string | URL, options?: NavigateOptions): void;
}

/**
* Abstract router used by Toolpad components.
*/
export interface Router {
pathname: string;
searchParams: URLSearchParams;
navigate: Navigate;
}

export interface Branding {
title?: string;
logo?: React.ReactNode;
Expand All @@ -30,11 +48,14 @@ export type NavigationItem = NavigationPageItem | NavigationSubheaderItem | Navi

export type Navigation = NavigationItem[];

// TODO: hide these contexts from public API
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we need to export the contexts in AppProvider/index, if was probably just an oversight from me.

Copy link
Member

Choose a reason for hiding this comment

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

Or we can just move the to their own file.

export const BrandingContext = React.createContext<Branding | null>(null);

export const NavigationContext = React.createContext<Navigation>([]);

interface AppProviderProps {
export const RouterContext = React.createContext<Router | null>(null);

export interface AppProviderProps {
/**
* The content of the app provider.
*/
Expand All @@ -54,6 +75,12 @@ interface AppProviderProps {
* @default []
*/
navigation?: Navigation;

/**
* Router implementation used inside Toolpad components.
* @default null
*/
router?: Router;
}

/**
Expand All @@ -68,15 +95,17 @@ interface AppProviderProps {
* - [AppProvider API](https://mui.com/toolpad/core/api/app-provider)
*/
function AppProvider(props: AppProviderProps) {
const { children, theme = baseTheme, branding = null, navigation = [] } = props;
const { children, theme = baseTheme, branding = null, navigation = [], router = null } = props;

return (
<ThemeProvider theme={theme}>
<CssBaseline />
<BrandingContext.Provider value={branding}>
<NavigationContext.Provider value={navigation}>{children}</NavigationContext.Provider>
</BrandingContext.Provider>
</ThemeProvider>
<RouterContext.Provider value={router}>
<ThemeProvider theme={theme}>
<CssBaseline />
<BrandingContext.Provider value={branding}>
<NavigationContext.Provider value={navigation}>{children}</NavigationContext.Provider>
</BrandingContext.Provider>
</ThemeProvider>
</RouterContext.Provider>
);
}

Expand Down Expand Up @@ -130,6 +159,15 @@ AppProvider.propTypes /* remove-proptypes */ = {
}),
]).isRequired,
),
/**
* Router implementation used inside Toolpad components.
* @default null
*/
router: PropTypes /* @typescript-to-proptypes-ignore */.shape({
navigate: PropTypes.func.isRequired,
pathname: PropTypes.string.isRequired,
searchParams: PropTypes.instanceOf(URLSearchParams),
}),
/**
* [Theme](https://mui.com/material-ui/customization/theming/) used by the app.
* @default baseTheme
Expand Down
1 change: 0 additions & 1 deletion packages/toolpad-core/src/AppProvider/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
'use client';
export * from './AppProvider';
Loading
Loading