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

[docs] Improve Toolpad Core docs #43796

Merged
merged 20 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -97,7 +97,7 @@ export default function DashboardLayoutBasic(props) {
const router = useDemoRouter('/dashboard');

// Remove this const when copying and pasting into your project.
const demoWindow = window !== undefined ? window() : undefined;
const demoWindow = window ? window() : undefined;

return (
<AppProvider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export default function DashboardLayoutBasic(props: any) {
const router = useDemoRouter('/dashboard');

// Remove this const when copying and pasting into your project.
const demoWindow = window !== undefined ? window() : undefined;
const demoWindow = window ? window() : undefined;

return (
<AppProvider
Expand Down
6 changes: 3 additions & 3 deletions docs/data/material/components/app-bar/app-bar.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,10 @@ You can override this behavior by setting the `enableColorOnDark` prop to `true`

{{"demo": "EnableColorOnDarkAppBar.js", "bg": true}}

## Experimental APIs
## Toolpad (Beta)

### DashboardLayout
### Dashboard Layout

oliviertassinari marked this conversation as resolved.
Show resolved Hide resolved
The [DashboardLayout](https://mui.com/toolpad/core/react-dashboard-layout/) component from `@toolpad/core` is the starting point for dashboarding applications. It takes care of application layout, theming, navigation, and more. An example usage of this component:

{{"demo": "DashboardLayoutBasic.js", "height": 400, "iframe": true, "hideToolbar": true}}
{{"demo": "DashboardLayoutBasic.js", "height": 400, "iframe": true, "bg": "inline", "defaultExpanded": false}}
22 changes: 4 additions & 18 deletions docs/data/material/components/breadcrumbs/PageContainerBasic.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { extendTheme, styled } from '@mui/material/styles';
import { styled, useTheme } from '@mui/material/styles';
import DashboardIcon from '@mui/icons-material/Dashboard';
import { AppProvider } from '@toolpad/core/AppProvider';
import { PageContainer, PageContainerToolbar } from '@toolpad/core/PageContainer';
Expand All @@ -18,20 +18,6 @@ const NAVIGATION = [
},
];

const demoTheme = extendTheme({
colorSchemes: { light: true, dark: true },
colorSchemeSelector: 'class',
breakpoints: {
values: {
xs: 0,
sm: 600,
md: 600,
lg: 1200,
xl: 1536,
},
},
});

function useDemoRouter(initialPath) {
const [pathname, setPathname] = React.useState(initialPath);

Expand Down Expand Up @@ -81,15 +67,15 @@ function PageToolbar() {
export default function PageContainerBasic(props) {
const { window } = props;
const router = useDemoRouter('/orders');

const theme = useTheme();
// Remove this const when copying and pasting into your project.
const demoWindow = window !== undefined ? window() : undefined;
const demoWindow = window ? window() : undefined;

return (
<AppProvider
navigation={NAVIGATION}
router={router}
theme={demoTheme}
theme={theme}
window={demoWindow}
branding={{
title: 'ACME Inc.',
Expand Down
22 changes: 4 additions & 18 deletions docs/data/material/components/breadcrumbs/PageContainerBasic.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { extendTheme, styled } from '@mui/material/styles';
import { styled, useTheme } from '@mui/material/styles';
import DashboardIcon from '@mui/icons-material/Dashboard';
import { AppProvider, Navigation, Router } from '@toolpad/core/AppProvider';
import { PageContainer, PageContainerToolbar } from '@toolpad/core/PageContainer';
Expand All @@ -18,20 +18,6 @@ const NAVIGATION: Navigation = [
},
];

const demoTheme = extendTheme({
colorSchemes: { light: true, dark: true },
colorSchemeSelector: 'class',
breakpoints: {
values: {
xs: 0,
sm: 600,
md: 600,
lg: 1200,
xl: 1536,
},
},
});

function useDemoRouter(initialPath: string): Router {
const [pathname, setPathname] = React.useState(initialPath);

Expand Down Expand Up @@ -81,15 +67,15 @@ function PageToolbar() {
export default function PageContainerBasic(props: any) {
const { window } = props;
const router = useDemoRouter('/orders');

const theme = useTheme();
// Remove this const when copying and pasting into your project.
const demoWindow = window !== undefined ? window() : undefined;
const demoWindow = window ? window() : undefined;

return (
<AppProvider
navigation={NAVIGATION}
router={router}
theme={demoTheme}
theme={theme}
window={demoWindow}
branding={{
title: 'ACME Inc.',
Expand Down
6 changes: 3 additions & 3 deletions docs/data/material/components/breadcrumbs/breadcrumbs.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,10 @@ The accessibility of this component relies on:
- To prevent screen reader announcement of the visual separators between links, they are hidden with `aria-hidden`.
- A nav element labeled with `aria-label` identifies the structure as a breadcrumb trail and makes it a navigation landmark so that it is easy to locate.

## Experimental APIs
## Toolpad (Beta)

### Page Container

The [PageContainer](https://mui.com/toolpad/core/react-page-container/) component in `@toolpad/core` is the ideal wrapper for the content of your dashboard. It makes the Material UI Container navigation aware and extends it with page title, breadcrumbs, actions, and more.
The [PageContainer](https://mui.com/toolpad/core/react-page-container/) component in `@toolpad/core` is the ideal wrapper for the content of your dashboard. It makes the Material UI Container navigation-aware and extends it with page title, breadcrumbs, actions, and more.

{{"demo": "./PageContainerBasic.js", "height": 400, "hideToolbar": true}}
{{"demo": "PageContainerBasic.js", "height": 400, "bg": "inline", "defaultExpanded": false}}
6 changes: 3 additions & 3 deletions docs/data/material/components/container/container.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ The max-width matches the min-width of the current breakpoint.
<Container fixed>
```

## Experimental APIs
## Toolpad (Beta)

### Page Container

The [PageContainer](https://mui.com/toolpad/core/react-page-container/) component in `@toolpad/core` is the ideal wrapper for the content of your dashboard. It makes the Material UI Container navigation aware and extends it with page title, breadcrumbs, actions, and more.
The [PageContainer](https://mui.com/toolpad/core/react-page-container/) component in `@toolpad/core` is the ideal wrapper for the content of your dashboard. It makes the Material UI Container navigation-aware and extends it with page title, breadcrumbs, actions, and more.

{{"demo": "../breadcrumbs/PageContainerBasic.js", "height": 400, "hideToolbar": true}}
{{"demo": "../breadcrumbs/PageContainerBasic.js", "height": 400, "bg": "inline", "defaultExpanded": false}}
120 changes: 120 additions & 0 deletions docs/data/material/components/dialogs/ToolpadDialogsNoSnap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { DialogsProvider, useDialogs } from '@toolpad/core/useDialogs';
import Button from '@mui/material/Button';
import LoadingButton from '@mui/lab/LoadingButton';
import Dialog from '@mui/material/Dialog';
import Alert from '@mui/material/Alert';
import DialogTitle from '@mui/material/DialogTitle';
import DialogContent from '@mui/material/DialogContent';
import DialogActions from '@mui/material/DialogActions';

function MyCustomDialog({ open, onClose, payload }) {
return (
<Dialog fullWidth open={open} onClose={() => onClose()}>
<DialogTitle>Custom Error Handler</DialogTitle>
<DialogContent>
<Alert severity="error">
{`An error occurred while deleting item "${payload.id}":`}
<pre>{payload.error}</pre>
</Alert>
</DialogContent>
<DialogActions>
<Button onClick={() => onClose()}>Close me</Button>
</DialogActions>
</Dialog>
);
}

MyCustomDialog.propTypes = {
/**
* A function to call when the dialog should be closed. If the dialog has a return
* value, it should be passed as an argument to this function. You should use the promise
* that is returned to show a loading state while the dialog is performing async actions
* on close.
* @param result The result to return from the dialog.
* @returns A promise that resolves when the dialog can be fully closed.
*/
onClose: PropTypes.func.isRequired,
/**
* Whether the dialog is open.
*/
open: PropTypes.bool.isRequired,
/**
* The payload that was passed when the dialog was opened.
*/
payload: PropTypes.shape({
error: PropTypes.string,
id: PropTypes.string,
}).isRequired,
};

const mockApiDelete = async (id) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (!id) {
reject(new Error('ID is required'));
} else if (parseInt(id, 10) % 2 === 0) {
console.log('id', parseInt(id, 10));
resolve(true);
} else if (parseInt(id, 10) % 2 === 1) {
reject(new Error('Can not delete odd numbered elements'));
} else if (Number.isNaN(parseInt(id, 10))) {
reject(new Error('ID must be a number'));
} else {
reject(new Error('Unknown error'));
}
}, 1000);
});
};

function DemoContent() {
const dialogs = useDialogs();
const [isDeleting, setIsDeleting] = React.useState(false);

const handleDelete = async () => {
const id = await dialogs.prompt('Enter the ID to delete', {
okText: 'Delete',
cancelText: 'Cancel',
});

if (id) {
const deleteConfirmed = await dialogs.confirm(
`Are you sure you want to delete "${id}"?`,
);
if (deleteConfirmed) {
try {
setIsDeleting(true);
await mockApiDelete(id);
dialogs.alert('Deleted!');
} catch (error) {
const message = error instanceof Error ? error.message : 'Unknown error';
await dialogs.open(MyCustomDialog, { id, error: message });
} finally {
setIsDeleting(false);
}
}
}
};
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
<div style={{ display: 'flex', gap: 16 }}>
<LoadingButton
variant="contained"
loading={isDeleting}
onClick={handleDelete}
>
Delete
</LoadingButton>
</div>
</div>
);
}

export default function ToolpadDialogsNoSnap() {
return (
<DialogsProvider>
<DemoContent />
</DialogsProvider>
);
}
101 changes: 101 additions & 0 deletions docs/data/material/components/dialogs/ToolpadDialogsNoSnap.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import * as React from 'react';
import { DialogsProvider, useDialogs, DialogProps } from '@toolpad/core/useDialogs';
import Button from '@mui/material/Button';
import LoadingButton from '@mui/lab/LoadingButton';
import Dialog from '@mui/material/Dialog';
import Alert from '@mui/material/Alert';
import DialogTitle from '@mui/material/DialogTitle';
import DialogContent from '@mui/material/DialogContent';
import DialogActions from '@mui/material/DialogActions';

interface DeleteError {
id: string | null;
error: string | null;
}

function MyCustomDialog({ open, onClose, payload }: DialogProps<DeleteError>) {
return (
<Dialog fullWidth open={open} onClose={() => onClose()}>
<DialogTitle>Custom Error Handler</DialogTitle>
<DialogContent>
<Alert severity="error">
{`An error occurred while deleting item "${payload.id}":`}
<pre>{payload.error}</pre>
</Alert>
</DialogContent>
<DialogActions>
<Button onClick={() => onClose()}>Close me</Button>
</DialogActions>
</Dialog>
);
}

const mockApiDelete = async (id: string | null) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (!id) {
reject(new Error('ID is required'));
} else if (parseInt(id, 10) % 2 === 0) {
console.log('id', parseInt(id, 10));
resolve(true);
} else if (parseInt(id, 10) % 2 === 1) {
reject(new Error('Can not delete odd numbered elements'));
} else if (Number.isNaN(parseInt(id, 10))) {
reject(new Error('ID must be a number'));
} else {
reject(new Error('Unknown error'));
}
}, 1000);
});
};

function DemoContent() {
const dialogs = useDialogs();
const [isDeleting, setIsDeleting] = React.useState(false);

const handleDelete = async () => {
const id = await dialogs.prompt('Enter the ID to delete', {
okText: 'Delete',
cancelText: 'Cancel',
});

if (id) {
const deleteConfirmed = await dialogs.confirm(
`Are you sure you want to delete "${id}"?`,
);
if (deleteConfirmed) {
try {
setIsDeleting(true);
await mockApiDelete(id);
dialogs.alert('Deleted!');
} catch (error) {
const message = error instanceof Error ? error.message : 'Unknown error';
await dialogs.open(MyCustomDialog, { id, error: message });
} finally {
setIsDeleting(false);
}
}
}
};
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
<div style={{ display: 'flex', gap: 16 }}>
<LoadingButton
variant="contained"
loading={isDeleting}
onClick={handleDelete}
>
Delete
</LoadingButton>
</div>
</div>
);
}

export default function ToolpadDialogsNoSnap() {
return (
<DialogsProvider>
<DemoContent />
</DialogsProvider>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<DialogsProvider>
<DemoContent />
</DialogsProvider>
Loading