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] Add Toolpad Core template link #44415

Merged
merged 38 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
a7cde8b
wip: Add Toolpad Core template
bharatkashyap Nov 15, 2024
60300ff
fix: No dark mode on sign in page
bharatkashyap Nov 15, 2024
7f35451
Merge branch 'master' into docs/toolpad-core-template
bharatkashyap Nov 15, 2024
acb11e4
fix: Incorrect
bharatkashyap Nov 15, 2024
84fe467
Merge branch 'docs/toolpad-core-template' of github.com:bharatkashyap…
bharatkashyap Nov 15, 2024
94b22ed
fix: add `pnpm template:screenshot toolpad` output
bharatkashyap Nov 15, 2024
2f7a1e1
fix: Move back to original section
bharatkashyap Nov 19, 2024
885ef1d
fix: CI
bharatkashyap Nov 19, 2024
7dc4267
Merge branch 'master' of github.com:mui-org/material-ui into docs/too…
bharatkashyap Nov 19, 2024
62f3aff
fix: CI again
bharatkashyap Nov 19, 2024
6245ac8
Merge branch 'master' of github.com:mui-org/material-ui into docs/too…
bharatkashyap Nov 22, 2024
a231e38
Merge branch 'master' into docs/toolpad-core-template
bharatkashyap Nov 22, 2024
2f65933
fix: Olivier review, Vale
bharatkashyap Nov 22, 2024
56a0626
Merge branch 'docs/toolpad-core-template' of github.com:bharatkashyap…
bharatkashyap Nov 22, 2024
50ac8ae
fix: Link, add template redirects
bharatkashyap Nov 25, 2024
72a85d8
Merge branch 'master' of github.com:mui-org/material-ui into docs/too…
bharatkashyap Nov 25, 2024
c015dea
fix: Add image, callout from examples page
bharatkashyap Nov 28, 2024
050eade
fix: CI
bharatkashyap Nov 28, 2024
e0e443a
Merge branch 'master' into docs/toolpad-core-template
bharatkashyap Nov 28, 2024
c0dc8bd
Merge branch 'master' into docs/toolpad-core-template
bharatkashyap Nov 28, 2024
dc6ce80
fix: CI
bharatkashyap Nov 28, 2024
5856a7e
Merge branch 'docs/toolpad-core-template' of github.com:bharatkashyap…
bharatkashyap Nov 28, 2024
fdd86f9
fix: CI
bharatkashyap Nov 28, 2024
cd7f3c8
fix: Missed removing
bharatkashyap Nov 28, 2024
1a4db18
Merge branch 'master' into docs/toolpad-core-template
bharatkashyap Nov 29, 2024
6a2202c
Merge branch 'master' of github.com:mui-org/material-ui into docs/too…
bharatkashyap Dec 2, 2024
b1c8367
fix: image borders
bharatkashyap Dec 2, 2024
3a1b516
Merge branch 'docs/toolpad-core-template' of github.com:bharatkashyap…
bharatkashyap Dec 2, 2024
6839785
Merge branch 'master' into docs/toolpad-core-template
bharatkashyap Dec 2, 2024
0ee61aa
fix: Images, copy on examples page
bharatkashyap Dec 3, 2024
80ae9a8
Merge branch 'docs/toolpad-core-template' of github.com:bharatkashyap…
bharatkashyap Dec 3, 2024
b4bc25d
Merge branch 'master' into docs/toolpad-core-template
bharatkashyap Dec 3, 2024
991de3e
Merge branch 'master' into docs/toolpad-core-template
bharatkashyap Dec 3, 2024
fddfce7
Merge branch 'master' into docs/toolpad-core-template
bharatkashyap Dec 3, 2024
47478bb
Merge branch 'master' into docs/toolpad-core-template
bharatkashyap Dec 3, 2024
56a50fc
Olivier's feedback
prakhargupta1 Dec 4, 2024
ae61db3
missed
prakhargupta1 Dec 4, 2024
951e298
missed
prakhargupta1 Dec 4, 2024
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
74 changes: 55 additions & 19 deletions docs/scripts/generateTemplateScreenshots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { chromium } from 'playwright';
* - `pnpm template:screenshot material-ui` to generate all screenshots for Material-UI templates
* - `pnpm template:screenshot order-dashboard` to generate screenshots for file named `order-dashboard.tsx`
* - `pnpm template:screenshot material-ui dashboard` to generate screenshots for file named `dashboard.tsx` of Material UI templates
* - `pnpm template:screenshot toolpad` to generate screenshots for Toolpad Core templates
*
* Note:
* - The screenshot with `-dark` suffix is generated if the page has a button with id `toggle-mode`
Expand All @@ -26,10 +27,20 @@ import { chromium } from 'playwright';

const host = process.env.DEPLOY_PREVIEW || 'http://localhost:3000';

interface ProjectConfig {
input?: string;
output?: string;
externalInput?: string[];
viewport?: {
width: number;
height: number;
};
}

/**
* project key should be `mui.com/<project-key>/*`
*/
const projects = {
const projects: Record<string, ProjectConfig> = {
'material-ui': {
input: path.join(process.cwd(), 'docs/pages/material-ui/getting-started/templates'),
output: 'docs/public/static/screenshots',
Expand All @@ -40,6 +51,13 @@ const projects = {
output: 'docs/public/static/screenshots',
viewport: { width: 1600, height: 800 },
},
toolpad: {
externalInput: [
'https://deploy-preview-4415--mui-toolpad-docs.netlify.app/toolpad/core/templates/nextjs-dashboard',
],
output: 'docs/public/static/screenshots',
viewport: { width: 1600, height: 800 },
},
};

const names = new Set(process.argv.slice(2));
Expand All @@ -52,25 +70,30 @@ const names = new Set(process.argv.slice(2));
await Promise.all(
Object.entries(projects)
.filter(([project]) => names.size === 0 || names.has(project))
.map(async ([project, { input, output, viewport }]) => {
.map(async ([project, config]) => {
const page = await browser.newPage({
viewport,
viewport: config.viewport,
reducedMotion: 'reduce',
});

names.delete(project);

const files = await fs.readdir(input);
const urls = files
.filter(
(file) =>
!file.startsWith('index') &&
(names.size === 0 || names.has(file.replace(/\.(js|tsx)$/, ''))),
)
.map(
(file) => `/${project}/getting-started/templates/${file.replace(/\.(js|tsx)$/, '/')}`,
);

let urls: string[];

if (config.externalInput) {
urls = config.externalInput;
} else if (config.input) {
const files = await fs.readdir(config.input);
urls = files
.filter(
(file) =>
!file.startsWith('index') &&
(names.size === 0 || names.has(file.replace(/\.(js|tsx)$/, ''))),
)
.map(
(file) => `/${project}/getting-started/templates/${file.replace(/\.(js|tsx)$/, '/')}`,
);
}
async function captureDarkMode(outputPath: string) {
const btn = await page.$('[data-screenshot="toggle-mode"]');
if (btn) {
Expand Down Expand Up @@ -116,12 +139,25 @@ const names = new Set(process.argv.slice(2));
}

try {
await Promise.resolve().then(() =>
urls.reduce(async (sequence, aUrl) => {
await Promise.resolve().then(async () => {
if (config.externalInput) {
return Promise.all(
config.externalInput.map(async (url) => {
await page.goto(url, { waitUntil: 'networkidle' });
const urlPath = new URL(url).pathname;
const filePath = `${config.output}${urlPath.replace(/\/$/, '')}.jpg`;
// eslint-disable-next-line no-console
console.info('Saving screenshot to:', filePath);
await page.screenshot({ path: filePath, animations: 'disabled' });
}),
);
}

return urls.reduce(async (sequence, aUrl) => {
await sequence;
await page.goto(`${host}${aUrl}?hideFrame=true`, { waitUntil: 'networkidle' });

const filePath = `${output}${aUrl.replace(/\/$/, '')}.jpg`;
const filePath = `${config.output}${aUrl.replace(/\/$/, '')}.jpg`;
// eslint-disable-next-line no-console
console.info('Saving screenshot to:', filePath);
await page.screenshot({ path: filePath, animations: 'disabled' });
Expand All @@ -141,8 +177,8 @@ const names = new Set(process.argv.slice(2));
}

return Promise.resolve();
}, Promise.resolve()),
);
}, Promise.resolve());
});
} catch (error) {
console.error(error);
}
Expand Down
136 changes: 80 additions & 56 deletions docs/src/modules/components/MaterialFreeTemplatesCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as React from 'react';
import Box from '@mui/material/Box';
import Card from '@mui/material/Card';
import CardMedia from '@mui/material/CardMedia';
import Chip from '@mui/material/Chip';
import Button from '@mui/material/Button';
import IconButton from '@mui/material/IconButton';
import Link from '@mui/material/Link';
Expand All @@ -17,56 +18,68 @@ import { pascalCase } from 'docs/src/modules/utils/helpers';
import sourceMaterialTemplates from 'docs/src/modules/material/sourceMaterialTemplates';
import codeSandbox from 'docs/src/modules/sandbox/CodeSandbox';
import stackBlitz from 'docs/src/modules/sandbox/StackBlitz';
import { sxChip } from 'docs/src/modules/components/AppNavDrawerItem';

const sourcePrefix = `${process.env.SOURCE_CODE_REPO}/tree/v${process.env.LIB_VERSION}`;

function layouts(translatation) {
function layouts(translation) {
return [
{
title: translatation('dashboardTitle'),
description: translatation('dashboardDescr'),
title: translation('dashboardTitle'),
description: translation('dashboardDescr'),
href: '/material-ui/getting-started/templates/dashboard/',
source: `${sourcePrefix}/docs/data/material/getting-started/templates/dashboard`,
hasDarkMode: true,
},
{
title: translatation('marketingPageTitle'),
description: translatation('marketingPageDescr'),
title: translation('dashboardToolpadTitle'),
Copy link
Member

@oliviertassinari oliviertassinari Nov 17, 2024

Choose a reason for hiding this comment

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

Interleaving different types of content feels like a big no-no.

Copy link
Member Author

Choose a reason for hiding this comment

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

Do you mean we should create a separate section for Toolpad Core templates on this page, or create a separate page for them, or do you mean that the Toolpad-specific copy should be stored separately from the rest of the content (not in translations.json?)

Copy link
Contributor

Choose a reason for hiding this comment

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

I would keep the Toolpad templates separate from the rest of the templates, because they seem to serve somewhat different use cases and I'm afraid we'd be presenting users with too many options. I think the way this page is currently structured with Toolpad Core as its own section makes the most sense. (I've suggested some copyedits to this section in #44461 to say more about Toolpad's value proposition.)

Copy link
Member

@oliviertassinari oliviertassinari Nov 18, 2024

Choose a reason for hiding this comment

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

@bharatkashyap Right so not the current shape.

Now, I'm not sure what's the right way to approach this, but at first sight, I would imagine we would have nothing else? I mean, having a link to Toolpad from this page that lists all the existing Toolpad templates seems great, similar to how the premium store items can be browsed.

We covered a bit of Toolpad product architecture in today's https://www.notion.so/mui-org/product-Company-Product-Meeting-2024-11-18-135cbfe7b66080518203cff028b28579. The main thought was that:

  • We want to spend 70% of our time optimizing what works, low risks. 20% of things there are more risky. 10% in bolder bets, high chance to fail but high payouts. Existing teams can do the 70% and the 20%, but the 10% is usually out of reach for them. Part of why resources are allocated to Toolpad is to allow us to go after those 10%
  • https://pro.ant.design/, Refine is closer to what Toolpad is about, ready-to-use primitives/components specifically for building quickly CRUD apps, and internal tools.

How do we know if a feature is right for Toolpad or another place in the stack? In my view, it's about:

  • It would make sense to have an integration with most of the other UI libraries.
  • It's not about component complexity, if its, Base UI X - MUI X sounds like the right place.
  • It's not about design, if it's, MUI sounds like the right place.
  • It relates to building internal tools (and potentially, but didn't commit to this yet, anything that can support components with a backend API)

For example:

  • Blocks, Layouts are Material UI features, so almost all design output from Toolpad should be coming from Material UI, e.g. https://tremor.so/ is not what Toolpad is about but what Material UI is about. Toolpad supports Material UI.
  • Rich layouts, things that rely on Material UI design, but are operationalized them specifically for internal tools make sense in Toolpad, e.g. https://procomponents.ant.design/en-US/components/layout?tab=api. I definitely don't see this a MUI X (not advanced enough, too design focused) or Material UI feature (too internal tool focused). Now if the API is composable, and not a big moonlight, then OK, could be Material UI, same reason as why https://ui.shadcn.com/docs/components/sidebar. If those are purely about UI (visual, no logic), MUI X could be the perfect place to host them.
  • Form components, e.g. https://procomponents.ant.design/en-US/components/form. This could fit great in Base UI, but could be too advanced, so potentially Base UI X.

cc @joserodolfofreitas see if this resonates.

Copy link
Member

Choose a reason for hiding this comment

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

I agree that putting Toolpad template in it's own section will help users distinguish between the templates. We were earlier thinking that it would be better if the user could see Toolpad template before scroll, but due to the design revamp of this page, scrolling is inevitable. So, a separate section will help visually differentiate.

We'll replace the dashboardLayout demo with the hosted Toolpad core template. 👍

Copy link
Member

@oliviertassinari oliviertassinari Nov 20, 2024

Choose a reason for hiding this comment

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

Yeah, but this list of templates demos using the Toolpad primitives should be in Toolpad's docs, not in Material UI's docs because it's not a Material UI feature or value proposition of Material UI.

Copy link
Member

Choose a reason for hiding this comment

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

Listing Toolpad template here will only make it more discoverable to the Material UI users who are looking for templates. Some of them may be interested in it, so I think we should have it here, at the bottom, in a section of its own.
Also, the templates docs page attracts many users, so not listing it here, would be a missed opportunity.

Copy link
Member

@oliviertassinari oliviertassinari Nov 21, 2024

Choose a reason for hiding this comment

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

Listing Toolpad template here will only make it more discoverable to the Material UI users who are looking for templates. Some of them may be interested in it, so I think we should have it here, at the bottom, in a section of its own.

@prakhargupta1 Referencing Toolpad here makes sense to me, a person reaching for a template has about a 25% probability to be a user we build Toolpad for (internal tool 0.5 * simple use cases 0.5). It's also at the right time in the user journey.

However,

  • for the majority of the people (75%) it's noise
  • for the people that use the different product it creates confusion as to what is what
  • for the team maintaining Toolpad, it makes them update places outside of their scope

So It seems that the only viable option is to reference Toolapd like we do for the template market: so no list, but a link.

description: translation('dashboardToolpadDescr'),
href: '/toolpad/core/templates/nextjs-dashboard',
source: 'https://github.com/mui/toolpad/tree/master/examples/core/auth-nextjs-themed',
codeSandbox:
'https://codesandbox.io/s/github/mui/toolpad/tree/master/examples/core/auth-nextjs-themed',
stackBlitz: null,
hasDarkMode: false,
new: true,
},
{
title: translation('marketingPageTitle'),
description: translation('marketingPageDescr'),
href: '/material-ui/getting-started/templates/marketing-page/',
source: `${sourcePrefix}/docs/data/material/getting-started/templates/marketing-page`,
hasDarkMode: true,
},
{
title: translatation('checkoutTitle'),
description: translatation('checkoutDescr'),
title: translation('checkoutTitle'),
description: translation('checkoutDescr'),
href: '/material-ui/getting-started/templates/checkout/',
source: `${sourcePrefix}/docs/data/material/getting-started/templates/checkout`,
hasDarkMode: true,
},
{
title: translatation('signInTitle'),
description: translatation('signInDescr'),
title: translation('signInTitle'),
description: translation('signInDescr'),
href: '/material-ui/getting-started/templates/sign-in/',
source: `${sourcePrefix}/docs/data/material/getting-started/templates/sign-in`,
hasDarkMode: true,
},
{
title: translatation('signInSideTitle'),
description: translatation('signInSideDescr'),
title: translation('signInSideTitle'),
description: translation('signInSideDescr'),
href: '/material-ui/getting-started/templates/sign-in-side/',
source: `${sourcePrefix}/docs/data/material/getting-started/templates/sign-in-side`,
hasDarkMode: true,
},
{
title: translatation('signUpTitle'),
description: translatation('signUpDescr'),
title: translation('signUpTitle'),
description: translation('signUpDescr'),
href: '/material-ui/getting-started/templates/sign-up/',
source: `${sourcePrefix}/docs/data/material/getting-started/templates/sign-up`,
hasDarkMode: true,
},
{
title: translatation('blogTitle'),
description: translatation('blogDescr'),
title: translation('blogTitle'),
description: translation('blogDescr'),
href: '/material-ui/getting-started/templates/blog/',
source: `${sourcePrefix}/docs/data/material/getting-started/templates/blog`,
hasDarkMode: true,
Expand All @@ -75,18 +88,19 @@ function layouts(translatation) {
}

export default function Templates() {
const translatation = useTranslate();
const translation = useTranslate();
const materialTemplates = sourceMaterialTemplates();
return (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 4, mb: 4 }}>
{layouts(translatation).map((layout) => {
{layouts(translation).map((layout) => {
const templateId = layout.source.split('/').pop();
const templateName = pascalCase(templateId);
const item = materialTemplates.map.get(templateId);
return (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }} key={layout.title}>
<Typography component="h3" variant="h6" sx={{ fontWeight: 'semiBold' }}>
{layout.title}
{layout.new && <Chip label="NEW" sx={sxChip('success')} />}
</Typography>
<Typography variant="body2" sx={{ color: 'text.secondary', mb: 2 }}>
{layout.description}
Expand Down Expand Up @@ -170,42 +184,48 @@ export default function Templates() {
gap: 1,
}}
>
<Tooltip title="Edit in StackBlitz">
<IconButton
color="primary"
size="small"
aria-label="StackBlitz playground"
data-ga-event-category="material-ui-template"
data-ga-event-label={templateId}
data-ga-event-action="stackblitz"
onClick={() =>
stackBlitz
.createMaterialTemplate({
...item,
files: { ...item.files, ...materialTemplates.sharedTheme?.files },
title: `${templateName} Template - Material UI`,
githubLocation: `${process.env.SOURCE_CODE_REPO}/blob/v${
process.env.LIB_VERSION
}/docs/data/material/templates/${templateId}/${templateName}.${
item.codeVariant === 'TS' ? 'tsx' : 'js'
}`,
})
.replaceContent((content) => {
if (typeof content === 'string') {
return content
.replace(/\.\.\/shared-theme\//g, './theme/')
.replace('./App', `./${templateName}`);
}
return content;
})
.openStackBlitz(`/${templateName}`)
}
>
<SvgIcon viewBox="0 0 19 28">
<path d="M8.13378 16.1087H0L14.8696 0L10.8662 11.1522L19 11.1522L4.13043 27.2609L8.13378 16.1087Z" />
</SvgIcon>
</IconButton>
</Tooltip>
{layout.stackBlitz !== null && (
<Tooltip title="Edit in StackBlitz">
<IconButton
color="primary"
size="small"
aria-label="StackBlitz playground"
data-ga-event-category="material-ui-template"
data-ga-event-label={templateId}
data-ga-event-action="stackblitz"
onClick={() => {
if (layout.stackBlitz) {
window.open(layout.stackBlitz, '_blank', 'noopener,noreferrer');
return;
}
stackBlitz
.createMaterialTemplate({
...item,
files: { ...item.files, ...materialTemplates.sharedTheme?.files },
title: `${templateName} Template - Material UI`,
githubLocation: `${process.env.SOURCE_CODE_REPO}/blob/v${
process.env.LIB_VERSION
}/docs/data/material/templates/${templateId}/${templateName}.${
item.codeVariant === 'TS' ? 'tsx' : 'js'
}`,
})
.replaceContent((content) => {
if (typeof content === 'string') {
return content
.replace(/\.\.\/shared-theme\//g, './theme/')
.replace('./App', `./${templateName}`);
}
return content;
})
.openStackBlitz(`/${templateName}`);
}}
>
<SvgIcon viewBox="0 0 19 28">
<path d="M8.13378 16.1087H0L14.8696 0L10.8662 11.1522L19 11.1522L4.13043 27.2609L8.13378 16.1087Z" />
</SvgIcon>
</IconButton>
</Tooltip>
)}
<Tooltip title="Edit in CodeSandbox">
<IconButton
color="primary"
Expand All @@ -214,7 +234,11 @@ export default function Templates() {
data-ga-event-category="material-ui-template"
data-ga-event-label={templateId}
data-ga-event-action="codesandbox"
onClick={() =>
onClick={() => {
if (layout.codeSandbox) {
window.open(layout.codeSandbox, '_blank', 'noopener,noreferrer');
return;
}
codeSandbox
.createMaterialTemplate({
...item,
Expand All @@ -234,8 +258,8 @@ export default function Templates() {
}
return content;
})
.openSandbox(`/${templateName}`)
}
.openSandbox(`/${templateName}`);
}}
>
<SvgIcon viewBox="0 0 1080 1080">
<path d="M755 140.3l0.5-0.3h0.3L512 0 268.3 140h-0.3l0.8 0.4L68.6 256v512L512 1024l443.4-256V256L755 140.3z m-30 506.4v171.2L548 920.1V534.7L883.4 341v215.7l-158.4 90z m-584.4-90.6V340.8L476 534.4v385.7L300 818.5V646.7l-159.4-90.6zM511.7 280l171.1-98.3 166.3 96-336.9 194.5-337-194.6 165.7-95.7L511.7 280z" />
Expand Down
2 changes: 2 additions & 0 deletions packages/mui-docs/src/translations/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@
"copySourceLinkTS": "Copy link to TypeScript source",
"dashboardDescr": "A collection of charts and complex components in a responsive dashboard layout.",
"dashboardTitle": "Dashboard",
"dashboardToolpadTitle": "Functional Dashboard",
"dashboardToolpadDescr": "A collection of charts and grids integrated with authentication, layout and navigation. Built using Toolpad Core components.",
"decreaseSpacing": "decrease spacing",
"demoToolbarLabel": "demo source",
"demoStylingSelectSystem": "MUI System",
Expand Down
Loading