Skip to content

Commit

Permalink
[material-ui][docs] Open Material UI template with CodeSandbox/StackB…
Browse files Browse the repository at this point in the history
…litz (#43604)
  • Loading branch information
siriwatknp committed Sep 11, 2024
1 parent 4977aa0 commit ff9406a
Show file tree
Hide file tree
Showing 107 changed files with 2,045 additions and 6,856 deletions.
6 changes: 0 additions & 6 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -299,12 +299,6 @@ jobs:
command: |
pnpm docs:link-check
git add -A && git diff --exit-code --staged
- run:
name: Update the templates shared themes
command: pnpm template:update-theme
- run:
name: '`pnpm template:update-theme` changes committed?'
command: git add -A && git diff --exit-code --staged
test_types:
<<: *default-job
resource_class: 'medium+'
Expand Down
75 changes: 75 additions & 0 deletions docs/data/material/getting-started/faq/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,81 @@ return (

If you are getting the error: `TypeError: Cannot convert a Symbol value to a string`, take a look at the [styled()](/system/styled/#how-to-use-components-selector-api) docs page for instructions on how you can fix this.

## How can I contribute to the free templates?

The templates are built using a [shared theme](https://github.com/mui/material-ui/tree/v6.0.2/docs/data/material/getting-started/templates/shared-theme). Below are the structure to create a new template:

### Template page

Create a new page in the `docs/pages/material-ui/getting-started/templates/<name>.js` directory with the following code:

```js
import * as React from 'react';
import AppTheme from 'docs/src/modules/components/AppTheme';
import TemplateFrame from 'docs/src/modules/components/TemplateFrame';
import Template from 'docs/data/material/getting-started/templates/<name>/<Template>';

export default function Page() {
return (
<AppTheme>
<TemplateFrame>
<Template />
</TemplateFrame>
</AppTheme>
);
}
```

Then create a template file at `docs/data/material/getting-started/templates/<name>/<Template>.tsx` (add more files if needed):

> Note: The `<Template>` must be a pascal case string of the `<name>` folder.
### Shared theme

The template must use `AppTheme` from `../shared-theme/AppTheme` to ensure a consistent look and feel across all templates.

If the template includes custom-themed components, such as the dashboard template with MUI X themed components, pass them to the `AppTheme`'s `themedComponents` prop:

```js
import AppTheme from '../shared-theme/AppTheme';

const xThemeComponents = {
...chartsCustomizations,
...dataGridCustomizations,
...datePickersCustomizations,
...treeViewCustomizations,
};

export default function Dashboard(props: { disableCustomTheme?: boolean }) {
return (
<AppTheme {...props} themeComponents={xThemeComponents}>...</AppTheme>
)
}
```

### Color mode toggle

The shared theme provides 2 appearance of the color mode toggle, `ColorModeSelect` and `ColorModeIconDropdown`.
You can use either of them in your template, it will be hidden within the `TemplateFrame` but will be visible in the Code Sandbox and Stackblitz.

### Template frame

If the template has a sidebar or a header that needs to stick to the top, refer to the CSS variable `--template-frame-height` to adjust.

For example, the dashboard template has a fixed header that needs to be accounted for the template frame height:

```js
<AppBar
position="fixed"
sx={{
top: 'var(--template-frame-height, 0px)',
// ...other styles
}}
>
```

This will make the `AppBar` stay below the `TemplateFrame` in a preview mode but stick to the top in the CodeSandbox and Stackblitz.

## [legacy] I have several instances of styles on the page

If you are seeing a warning message in the console like the one below, you probably have several instances of `@mui/styles` initialized on the page.
Expand Down
111 changes: 45 additions & 66 deletions docs/data/material/getting-started/templates/dashboard/Dashboard.js
Original file line number Diff line number Diff line change
@@ -1,81 +1,60 @@
import * as React from 'react';
import { createTheme, ThemeProvider, alpha } from '@mui/material/styles';

import { alpha } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import Box from '@mui/material/Box';
import Stack from '@mui/material/Stack';
import getDashboardTheme from './theme/getDashboardTheme';
import AppNavbar from './components/AppNavbar';
import Header from './components/Header';
import MainGrid from './components/MainGrid';
import SideMenu from './components/SideMenu';
import TemplateFrame from './TemplateFrame';

export default function Dashboard() {
const [mode, setMode] = React.useState('light');
const [showCustomTheme, setShowCustomTheme] = React.useState(true);
const dashboardTheme = createTheme(getDashboardTheme(mode));
const defaultTheme = createTheme({ palette: { mode } });
// This code only runs on the client side, to determine the system color preference
React.useEffect(() => {
// Check if there is a preferred mode in localStorage
const savedMode = localStorage.getItem('themeMode');
if (savedMode) {
setMode(savedMode);
} else {
// If no preference is found, it uses system preference
const systemPrefersDark = window.matchMedia(
'(prefers-color-scheme: dark)',
).matches;
setMode(systemPrefersDark ? 'dark' : 'light');
}
}, []);

const toggleColorMode = () => {
const newMode = mode === 'dark' ? 'light' : 'dark';
setMode(newMode);
localStorage.setItem('themeMode', newMode); // Save the selected mode to localStorage
};
import AppTheme from '../shared-theme/AppTheme';
import {
chartsCustomizations,
dataGridCustomizations,
datePickersCustomizations,
treeViewCustomizations,
} from './theme/customizations';

const toggleCustomTheme = () => {
setShowCustomTheme((prev) => !prev);
};
const xThemeComponents = {
...chartsCustomizations,
...dataGridCustomizations,
...datePickersCustomizations,
...treeViewCustomizations,
};

export default function Dashboard(props) {
return (
<TemplateFrame
toggleCustomTheme={toggleCustomTheme}
showCustomTheme={showCustomTheme}
mode={mode}
toggleColorMode={toggleColorMode}
>
<ThemeProvider theme={showCustomTheme ? dashboardTheme : defaultTheme}>
<CssBaseline enableColorScheme />
<Box sx={{ display: 'flex' }}>
<SideMenu />
<AppNavbar />
{/* Main content */}
<Box
component="main"
sx={(theme) => ({
flexGrow: 1,
backgroundColor: alpha(theme.palette.background.default, 1),
overflow: 'auto',
})}
<AppTheme {...props} themeComponents={xThemeComponents}>
<CssBaseline enableColorScheme />
<Box sx={{ display: 'flex' }}>
<SideMenu />
<AppNavbar />
{/* Main content */}
<Box
component="main"
sx={(theme) => ({
flexGrow: 1,
backgroundColor: theme.vars
? `rgba(${theme.vars.palette.background.defaultChannel} / 1)`
: alpha(theme.palette.background.default, 1),
overflow: 'auto',
})}
>
<Stack
spacing={2}
sx={{
alignItems: 'center',
mx: 3,
pb: 10,
mt: { xs: 8, md: 0 },
}}
>
<Stack
spacing={2}
sx={{
alignItems: 'center',
mx: 3,
pb: 10,
mt: { xs: 8, md: 0 },
}}
>
<Header />
<MainGrid />
</Stack>
</Box>
<Header />
<MainGrid />
</Stack>
</Box>
</ThemeProvider>
</TemplateFrame>
</Box>
</AppTheme>
);
}
119 changes: 48 additions & 71 deletions docs/data/material/getting-started/templates/dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,86 +1,63 @@
import * as React from 'react';
import {
PaletteMode,
createTheme,
ThemeProvider,
alpha,
} from '@mui/material/styles';
import type {} from '@mui/x-date-pickers/themeAugmentation';
import type {} from '@mui/x-charts/themeAugmentation';
import type {} from '@mui/x-data-grid/themeAugmentation';
import type {} from '@mui/x-tree-view/themeAugmentation';
import { alpha } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import Box from '@mui/material/Box';
import Stack from '@mui/material/Stack';
import getDashboardTheme from './theme/getDashboardTheme';
import AppNavbar from './components/AppNavbar';
import Header from './components/Header';
import MainGrid from './components/MainGrid';
import SideMenu from './components/SideMenu';
import TemplateFrame from './TemplateFrame';

export default function Dashboard() {
const [mode, setMode] = React.useState<PaletteMode>('light');
const [showCustomTheme, setShowCustomTheme] = React.useState(true);
const dashboardTheme = createTheme(getDashboardTheme(mode));
const defaultTheme = createTheme({ palette: { mode } });
// This code only runs on the client side, to determine the system color preference
React.useEffect(() => {
// Check if there is a preferred mode in localStorage
const savedMode = localStorage.getItem('themeMode') as PaletteMode | null;
if (savedMode) {
setMode(savedMode);
} else {
// If no preference is found, it uses system preference
const systemPrefersDark = window.matchMedia(
'(prefers-color-scheme: dark)',
).matches;
setMode(systemPrefersDark ? 'dark' : 'light');
}
}, []);

const toggleColorMode = () => {
const newMode = mode === 'dark' ? 'light' : 'dark';
setMode(newMode);
localStorage.setItem('themeMode', newMode); // Save the selected mode to localStorage
};
import AppTheme from '../shared-theme/AppTheme';
import {
chartsCustomizations,
dataGridCustomizations,
datePickersCustomizations,
treeViewCustomizations,
} from './theme/customizations';

const toggleCustomTheme = () => {
setShowCustomTheme((prev) => !prev);
};
const xThemeComponents = {
...chartsCustomizations,
...dataGridCustomizations,
...datePickersCustomizations,
...treeViewCustomizations,
};

export default function Dashboard(props: { disableCustomTheme?: boolean }) {
return (
<TemplateFrame
toggleCustomTheme={toggleCustomTheme}
showCustomTheme={showCustomTheme}
mode={mode}
toggleColorMode={toggleColorMode}
>
<ThemeProvider theme={showCustomTheme ? dashboardTheme : defaultTheme}>
<CssBaseline enableColorScheme />
<Box sx={{ display: 'flex' }}>
<SideMenu />
<AppNavbar />
{/* Main content */}
<Box
component="main"
sx={(theme) => ({
flexGrow: 1,
backgroundColor: alpha(theme.palette.background.default, 1),
overflow: 'auto',
})}
<AppTheme {...props} themeComponents={xThemeComponents}>
<CssBaseline enableColorScheme />
<Box sx={{ display: 'flex' }}>
<SideMenu />
<AppNavbar />
{/* Main content */}
<Box
component="main"
sx={(theme) => ({
flexGrow: 1,
backgroundColor: theme.vars
? `rgba(${theme.vars.palette.background.defaultChannel} / 1)`
: alpha(theme.palette.background.default, 1),
overflow: 'auto',
})}
>
<Stack
spacing={2}
sx={{
alignItems: 'center',
mx: 3,
pb: 10,
mt: { xs: 8, md: 0 },
}}
>
<Stack
spacing={2}
sx={{
alignItems: 'center',
mx: 3,
pb: 10,
mt: { xs: 8, md: 0 },
}}
>
<Header />
<MainGrid />
</Stack>
</Box>
<Header />
<MainGrid />
</Stack>
</Box>
</ThemeProvider>
</TemplateFrame>
</Box>
</AppTheme>
);
}
Loading

0 comments on commit ff9406a

Please sign in to comment.