Skip to content

Commit

Permalink
Integrate Toolpad Core in Toolpad Studio runtime (#4119)
Browse files Browse the repository at this point in the history
Co-authored-by: MUI bot <[email protected]>
  • Loading branch information
apedroferreira and Janpot authored Oct 10, 2024
1 parent 3f75eb5 commit a0c12c9
Show file tree
Hide file tree
Showing 37 changed files with 449 additions and 377 deletions.
16 changes: 12 additions & 4 deletions docs/data/toolpad/core/components/app-provider/app-provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ The `AppProvider` for Next.js applications includes some Next.js integrations ou
By using the specific `AppProvider` for Next.js you do not have to manually configure the integration between some Toolpad features and the corresponding Next.js features (such as routing), making the integration automatic and seamless.

```tsx
import { AppProvider } from '@toolpad/core/nextjs/AppProvider';
// or
import { AppProvider } from '@toolpad/core/nextjs';
```

Expand All @@ -43,7 +41,7 @@ When using the **Next.js App Router**, the most typical file where to import and
```tsx
// app/layout.tsx

import { AppProvider } from '@toolpad/core/nextjs/AppProvider';
import { AppProvider } from '@toolpad/core/nextjs';

export default function Layout(props) {
const { children } = props;
Expand All @@ -65,7 +63,7 @@ When using the **Next.js Pages Router**, the most typical file where to import a
```tsx
// pages/_app.tsx

import { AppProvider } from '@toolpad/core/nextjs/AppProvider';
import { AppProvider } from '@toolpad/core/nextjs';

export default function App(props) {
const { Component, pageProps } = props;
Expand All @@ -78,6 +76,16 @@ export default function App(props) {
}
```

## Client-side Routing

The `AppProvider` for React Router includes routing out-of-the-box for projects using [react-router-dom](https://www.npmjs.com/package/react-router-dom).

This specific `AppProvider` is recommended when building single-page applications with tools such as [Vite](https://vite.dev/), as you do not have to manually configure your app routing, making the integration automatic and seamless.

```tsx
import { AppProvider } from '@toolpad/core/react-router-dom';
```

## Theming

An `AppProvider` can set a visual theme for all elements inside it to adopt via the `theme` prop. This prop can be set in a few distinct ways with different advantages and disadvantages:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { createTheme } from '@mui/material/styles';
import MapIcon from '@mui/icons-material/Map';
import { AppProvider } from '@toolpad/core/AppProvider';
import { DashboardLayout } from '@toolpad/core/DashboardLayout';

const NAVIGATION = [
{
segment: 'map',
title: 'Map',
icon: <MapIcon />,
},
];

const demoTheme = createTheme({
cssVariables: {
colorSchemeSelector: 'data-toolpad-color-scheme',
},
colorSchemes: { light: true, dark: true },
breakpoints: {
values: {
xs: 0,
sm: 600,
md: 600,
lg: 1200,
xl: 1536,
},
},
});

function DashboardLayoutFullScreen(props) {
const { window } = props;

const [pathname, setPathname] = React.useState('/map');

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

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

return (
<AppProvider
navigation={NAVIGATION}
router={router}
theme={demoTheme}
window={demoWindow}
>
<DashboardLayout>
<iframe
title="Google Map"
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d387190.2799181496!2d-74.25987571760744!3d40.69767006358627!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x89c259af18b60165%3A0x8b621f8a7a7d28a4!2sNew%20York%2C%20NY%2C%20USA!5e0!3m2!1sen!2s!4v1633452834502!5m2!1sen!2s"
style={{ flex: 1, border: 0 }}
allowFullScreen
loading="lazy"
/>
</DashboardLayout>
</AppProvider>
);
}

DashboardLayoutFullScreen.propTypes = {
/**
* Injected by the documentation to work in an iframe.
* Remove this when copying and pasting into your project.
*/
window: PropTypes.func,
};

export default DashboardLayoutFullScreen;
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import * as React from 'react';
import { createTheme } from '@mui/material/styles';
import MapIcon from '@mui/icons-material/Map';
import { AppProvider } from '@toolpad/core/AppProvider';
import { DashboardLayout } from '@toolpad/core/DashboardLayout';
import type { Router, Navigation } from '@toolpad/core';

const NAVIGATION: Navigation = [
{
segment: 'map',
title: 'Map',
icon: <MapIcon />,
},
];

const demoTheme = createTheme({
cssVariables: {
colorSchemeSelector: 'data-toolpad-color-scheme',
},
colorSchemes: { light: true, dark: true },
breakpoints: {
values: {
xs: 0,
sm: 600,
md: 600,
lg: 1200,
xl: 1536,
},
},
});

interface DemoProps {
/**
* Injected by the documentation to work in an iframe.
* Remove this when copying and pasting into your project.
*/
window?: () => Window;
}

export default function DashboardLayoutFullScreen(props: DemoProps) {
const { window } = props;

const [pathname, setPathname] = React.useState('/map');

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

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

return (
<AppProvider
navigation={NAVIGATION}
router={router}
theme={demoTheme}
window={demoWindow}
>
<DashboardLayout>
<iframe
title="Google Map"
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d387190.2799181496!2d-74.25987571760744!3d40.69767006358627!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x89c259af18b60165%3A0x8b621f8a7a7d28a4!2sNew%20York%2C%20NY%2C%20USA!5e0!3m2!1sen!2s!4v1633452834502!5m2!1sen!2s"
style={{ flex: 1, border: 0 }}
allowFullScreen
loading="lazy"
/>
</DashboardLayout>
</AppProvider>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<DashboardLayout>
<iframe
title="Google Map"
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d387190.2799181496!2d-74.25987571760744!3d40.69767006358627!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x89c259af18b60165%3A0x8b621f8a7a7d28a4!2sNew%20York%2C%20NY%2C%20USA!5e0!3m2!1sen!2s!4v1633452834502!5m2!1sen!2s"
style={{ flex: 1, border: 0 }}
allowFullScreen
loading="lazy"
/>
</DashboardLayout>
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,19 @@ This feature is built on top of the [path-to-regexp](https://www.npmjs.com/packa

{{"demo": "DashboardLayoutPattern.js", "height": 400, "iframe": true}}

### Disabling collapsible sidebar
### Disable collapsible sidebar

The layout sidebar is collapsible to a mini-drawer (with icons only) in desktop and tablet viewports. This behavior can be disabled with the `disableCollapsibleSidebar` prop.

{{"demo": "DashboardLayoutNoMiniSidebar.js", "height": 400, "iframe": true}}

### Hiding the sidebar
### Full-size content

The layout content can take up the full available area with styles such as `flex: 1` or `height: 100%`.

{{"demo": "DashboardLayoutFullScreen.js", "height": 400, "iframe": true}}

### Hide navigation

The layout sidebar can be hidden if needed with the `hideNavigation` prop.

Expand Down
6 changes: 5 additions & 1 deletion docs/data/toolpad/core/introduction/base-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ You can pass the router implementation to the `AppProvider` component using the
:::

:::success
If you are using Next.js, use the `AppProvider` exported from `@toolpad/core/nextjs`. This automatically sets up the router for you and you do not need to pass the `router` prop.
If you are using Next.js, use the `AppProvider` exported from `@toolpad/core/nextjs`.

If you are building a single-page application with React Router for routing, use the `AppProvider` exported from `@toolpad/core/react-router-dom`.

This automatically sets up the router for you, so that you don't need to pass the `router` prop.
:::

## Slots
Expand Down
4 changes: 2 additions & 2 deletions docs/pages/toolpad/core/api/app-provider.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@
},
"name": "AppProvider",
"imports": [
"import { AppProvider } from '@toolpad/core/AppProvider';\nimport { AppProvider } from '@toolpad/core/nextjs/AppProvider'; // Next.js",
"import { AppProvider } from '@toolpad/core';\nimport { AppProvider } from '@toolpad/core/nextjs'; // Next.js"
"import { AppProvider } from '@toolpad/core/AppProvider';",
"import { AppProvider } from '@toolpad/core';\nimport { AppProvider } from '@toolpad/core/nextjs'; // Next.js\nimport { AppProvider } from '@toolpad/core/react-router-dom'; // React Router"
],
"classes": [],
"spread": true,
Expand Down
7 changes: 7 additions & 0 deletions docs/pages/toolpad/core/api/dashboard-layout.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@
},
"default": "{}",
"additionalInfo": { "slotsApi": true }
},
"sx": {
"type": {
"name": "union",
"description": "Array&lt;func<br>&#124;&nbsp;object<br>&#124;&nbsp;bool&gt;<br>&#124;&nbsp;func<br>&#124;&nbsp;object"
},
"additionalInfo": { "sx": true }
}
},
"name": "DashboardLayout",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
"description": "Whether the navigation bar and menu icon should be hidden"
},
"slotProps": { "description": "The props used for each slot inside." },
"slots": { "description": "The components used for each slot inside." }
"slots": { "description": "The components used for each slot inside." },
"sx": {
"description": "The system prop that allows defining system overrides as well as additional CSS styles."
}
},
"classDescriptions": {},
"slotDescriptions": {
Expand Down
12 changes: 8 additions & 4 deletions packages/toolpad-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,13 @@
"scripts": {
"clean": "rimraf build",
"prebuild": "pnpm clean",
"build": "pnpm build:node && pnpm build:stable && pnpm build:types && pnpm build:copy-files && pnpm esmify",
"esmify": "rm -rf ./build/*/package.json",
"build": "pnpm build:node && pnpm build:stable && pnpm build:types && pnpm build:copy-files",
"build:node": "node ../../scripts/build.mjs node",
"build:stable": "node ../../scripts/build.mjs stable",
"build:copy-files": "node ../../scripts/copyFiles.mjs",
"build:types": "tsc -b tsconfig.build.json",
"predev": "pnpm clean",
"dev": "concurrently \"pnpm build:stable --watch\" \"pnpm build:types --watch --preserveWatchOutput\"",
"dev": "mkdir -p build && concurrently \"pnpm build:stable --watch\" \"pnpm build:types --watch --preserveWatchOutput\" \"pnpm build:copy-files\"",
"check-types": "pnpm build:types && tsc --noEmit",
"test": "vitest run --coverage",
"test:dev": "vitest",
Expand Down Expand Up @@ -77,18 +76,23 @@
"next": "^14.2.14",
"next-router-mock": "^0.9.13",
"playwright": "^1.47.2",
"react-router-dom": "6.26.2",
"sinon": "^19.0.2",
"vitest": "2.1.2"
},
"peerDependencies": {
"@mui/icons-material": "5 - 6",
"@mui/material": "5 - 6",
"next": "^14",
"react": "^18"
"react": "^18",
"react-router-dom": "^6"
},
"peerDependenciesMeta": {
"next": {
"optional": true
},
"react-router-dom": {
"optional": true
}
},
"sideEffects": false,
Expand Down
9 changes: 9 additions & 0 deletions packages/toolpad-core/src/AppProvider/AppProvider.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import * as React from 'react';
import { describe, test, expect } from 'vitest';
import { render, screen } from '@testing-library/react';
import { createTheme } from '@mui/material/styles';
import { AppProvider } from './AppProvider';

describe('AppProvider', () => {
Expand All @@ -13,4 +14,12 @@ describe('AppProvider', () => {

expect(screen.getByText('Hello world')).toBeTruthy();
});

test('renders content correctly when using legacy theme', async () => {
const legacyTheme = createTheme();

render(<AppProvider theme={legacyTheme}>Hello world</AppProvider>);

expect(screen.getByText('Hello world')).toBeTruthy();
});
});
9 changes: 1 addition & 8 deletions packages/toolpad-core/src/AppProvider/AppThemeProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,7 @@ function LegacyThemeProvider(props: LegacyThemeProviderProps) {
);

return (
<ThemeProvider
theme={dualAwareTheme}
documentNode={appWindow?.document}
colorSchemeNode={appWindow?.document?.body}
disableNestedContext
colorSchemeStorageKey={COLOR_SCHEME_STORAGE_KEY}
modeStorageKey={MODE_STORAGE_KEY}
>
<ThemeProvider theme={dualAwareTheme}>
<PaletteModeContext.Provider value={paletteModeContextValue}>
<CssBaseline enableColorScheme />
{children}
Expand Down
Loading

0 comments on commit a0c12c9

Please sign in to comment.