Skip to content
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
23 changes: 3 additions & 20 deletions apps/code-infra-dashboard/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { formatMaterialUiDocPath } from '@/lib/ciReports/formatMaterialUiDocPath';

export const DASHBOARD_ORIGIN =
process.env.DASHBOARD_ORIGIN ||
process.env.RENDER_EXTERNAL_URL ||
Expand Down Expand Up @@ -68,26 +70,7 @@ export const repositories = new Map<string, Repository>(
benchmark: true,
netlifyDocs: {
siteId: 'material-ui',
formatDocPath: (filePath) => {
if (
!filePath.startsWith('docs/data/') ||
!/\.(md|mdx|jsx?|tsx?|json)$/.test(filePath)
) {
return null;
}
// Map a file to its page directory: demos and data files live alongside
// the page's markdown, so the containing folder identifies the page.
let url = filePath.replace('docs/data', '').replace(/\/[^/]+$/, '');
if (url.startsWith('/material')) {
url = url
.replace('/material', '/material-ui')
.replace(
/(guides|customization|getting-started|discover-more|experimental-api|migration|integrations)/,
'material-ui/$1',
);
}
return url;
},
formatDocPath: formatMaterialUiDocPath,
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,27 +64,25 @@ describe('generateDeployPreviewReport', () => {

const report = await generateDeployPreviewReport(reportOptions('mui/material-ui', 42));

const pageUrl =
'https://deploy-preview-42--material-ui.netlify.app/material-ui/components/buttons';
const pageUrl = 'https://deploy-preview-42--material-ui.netlify.app/material-ui/react-button/';
expect(report?.content).toContain(
`- <details><summary><a href="${pageUrl}">docs/data/material/components/buttons/buttons.md</a></summary>`,
);
expect(report?.content).not.toContain('Button.tsx');
});

it('should map a changed demo file to its page', async () => {
it('should map a changed Material UI demo file to its public page route', async () => {
mockOctokit.pulls.listFiles.mockResolvedValue({
data: [
{ filename: 'docs/data/material/components/buttons/BasicButtons.tsx', status: 'modified' },
],
data: [{ filename: 'docs/data/material/components/tabs/BasicTabs.js', status: 'modified' }],
});

const report = await generateDeployPreviewReport(reportOptions('mui/material-ui', 42));

const pageUrl =
'https://deploy-preview-42--material-ui.netlify.app/material-ui/components/buttons';
expect(report?.content).toContain(`<a href="${pageUrl}">`);
expect(report?.content).toContain('docs/data/material/components/buttons/BasicButtons.tsx</a>');
const pageUrl = 'https://deploy-preview-42--material-ui.netlify.app/material-ui/react-tabs/';
expect(report?.content).toContain(
`<a href="${pageUrl}">docs/data/material/components/tabs/BasicTabs.js</a>`,
);
expect(report?.content).not.toContain('/material-ui/components/tabs');
});

it('should map a changed JSON data file to its page', async () => {
Expand Down Expand Up @@ -113,8 +111,7 @@ describe('generateDeployPreviewReport', () => {

const report = await generateDeployPreviewReport(reportOptions('mui/material-ui', 42));

const pageUrl =
'https://deploy-preview-42--material-ui.netlify.app/material-ui/components/buttons';
const pageUrl = 'https://deploy-preview-42--material-ui.netlify.app/material-ui/react-button/';
expect(report?.content.match(new RegExp(`href="${pageUrl}"`, 'g'))).toHaveLength(1);
});

Expand Down Expand Up @@ -180,7 +177,7 @@ describe('generateDeployPreviewReport', () => {
it('should cap the number of doc links', async () => {
mockOctokit.pulls.listFiles.mockResolvedValue({
data: Array.from({ length: 10 }, (unused, index) => ({
filename: `docs/data/material/components/page-${index}/page-${index}.md`,
filename: `docs/data/page-${index}/page-${index}.md`,
status: 'modified',
})),
});
Expand All @@ -192,7 +189,7 @@ describe('generateDeployPreviewReport', () => {

it('should escape HTML-special characters in the file path', async () => {
mockOctokit.pulls.listFiles.mockResolvedValue({
data: [{ filename: 'docs/data/material/components/a<b>&"c/page.md', status: 'modified' }],
data: [{ filename: 'docs/data/a<b>&"c/page.md', status: 'modified' }],
});

const report = await generateDeployPreviewReport(reportOptions('mui/material-ui', 42));
Expand All @@ -210,7 +207,7 @@ describe('generateDeployPreviewReport', () => {
const report = await generateDeployPreviewReport(reportOptions('mui/material-ui', 42));

expect(report?.content).toContain(
'- <a href="https://deploy-preview-42--material-ui.netlify.app/material-ui/components/buttons">docs/data/material/components/buttons/buttons.md</a>',
'- <a href="https://deploy-preview-42--material-ui.netlify.app/material-ui/react-button/">docs/data/material/components/buttons/buttons.md</a>',
);
expect(report?.content).not.toContain('<details>');
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { describe, expect, it } from 'vitest';
import { formatMaterialUiDocPath } from './formatMaterialUiDocPath';

describe('formatMaterialUiDocPath', () => {
it.each([
['buttons', 'react-button'],
['radio-buttons', 'react-radio-button'],
['tabs', 'react-tabs'],
['icons', 'icons'],
['transitions', 'transitions'],
['new-component', 'react-new-component'],
])('should map the %s source directory to %s', (sourceDirectory, routeSegment) => {
expect(
formatMaterialUiDocPath(`docs/data/material/components/${sourceDirectory}/ExampleDemo.tsx`),
).toBe(`/material-ui/${routeSegment}/`);
});

it('should map non-component Material UI pages without duplicating the product prefix', () => {
expect(formatMaterialUiDocPath('docs/data/material/guides/testing/testing.md')).toBe(
'/material-ui/guides/testing',
);
});

it('should map root Material UI data files to the product root', () => {
expect(formatMaterialUiDocPath('docs/data/material/pages.ts')).toBe('/material-ui/');
});

it('should map shared documentation data to its containing directory', () => {
expect(formatMaterialUiDocPath('docs/data/about/teamMembers.json')).toBe('/about');
});

it('should return null for unsupported files', () => {
expect(formatMaterialUiDocPath('docs/data/material/components/tabs/preview.png')).toBeNull();
expect(formatMaterialUiDocPath('packages/mui-material/src/Tabs/Tabs.tsx')).toBeNull();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
const DOCS_DATA_PREFIX = 'docs/data/';
const MATERIAL_UI_COMPONENTS_PREFIX = 'material/components/';
const MATERIAL_UI_PREFIX = 'material/';
const SUPPORTED_DOC_FILE_EXTENSIONS = ['.md', '.mdx', '.js', '.jsx', '.ts', '.tsx', '.json'];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

One could make this a set as well and do the check with

SUPPORTED_DOC_FILE_EXTENSIONS.has(path.extname(theFilePath))

to avoid the O(n) complexity. It's not that critical for this use-case though.


const MATERIAL_UI_ROUTES_WITHOUT_REACT_PREFIX = new Set([
'about-the-lab',
'icons',
'material-icons',
'transitions',
]);

const MATERIAL_UI_COMPONENT_ROUTE_ALIASES = new Map([
['avatars', 'avatar'],
['badges', 'badge'],
['buttons', 'button'],
['cards', 'card'],
['checkboxes', 'checkbox'],
['dialogs', 'dialog'],
['dividers', 'divider'],
['drawers', 'drawer'],
['links', 'link'],
['lists', 'list'],
['menus', 'menu'],
['radio-buttons', 'radio-button'],
['selects', 'select'],
['snackbars', 'snackbar'],
['steppers', 'stepper'],
['switches', 'switch'],
['text-fields', 'text-field'],
['tooltips', 'tooltip'],
]);

/**
* Maps a Material UI documentation source file to its public page path.
*/
export function formatMaterialUiDocPath(filePath: string): string | null {
if (
!filePath.startsWith(DOCS_DATA_PREFIX) ||
!SUPPORTED_DOC_FILE_EXTENSIONS.some((extension) => filePath.endsWith(extension))
) {
return null;
}

const fileNameSeparatorIndex = filePath.lastIndexOf('/');
if (fileNameSeparatorIndex < DOCS_DATA_PREFIX.length) {
return null;
}

const pageDirectory = filePath.slice(DOCS_DATA_PREFIX.length, fileNameSeparatorIndex);
if (pageDirectory === 'material') {
return '/material-ui/';
}

if (pageDirectory.startsWith(MATERIAL_UI_COMPONENTS_PREFIX)) {
const componentPath = pageDirectory.slice(MATERIAL_UI_COMPONENTS_PREFIX.length);
const nestedDirectorySeparatorIndex = componentPath.indexOf('/');
const componentDirectory =
nestedDirectorySeparatorIndex === -1
? componentPath
: componentPath.slice(0, nestedDirectorySeparatorIndex);

// Get from exception route alias collection for plural folders to singlular routes or fallback
// to normal component directory for other and new components without plural folders to singular routes.
// This will keep the logic in sync for newly introduced components.
const routeName =
MATERIAL_UI_COMPONENT_ROUTE_ALIASES.get(componentDirectory) ?? componentDirectory;
const routeSegment = MATERIAL_UI_ROUTES_WITHOUT_REACT_PREFIX.has(componentDirectory)
? routeName
: `react-${routeName}`;
return `/material-ui/${routeSegment}/`;
}

if (pageDirectory.startsWith(MATERIAL_UI_PREFIX)) {
return `/material-ui/${pageDirectory.slice(MATERIAL_UI_PREFIX.length)}`;
}

return `/${pageDirectory}`;
}
Loading