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

[POC][docs] JSDoc comments as deprecation source of truth #42280

Closed
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
4 changes: 3 additions & 1 deletion docs/pages/material-ui/api/hidden.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,7 @@
"filename": "/packages/mui-material/src/Hidden/Hidden.js",
"inheritance": null,
"demos": "<ul><li><a href=\"/material-ui/react-hidden/\">Hidden</a></li></ul>",
"cssComponent": false
"cssComponent": false,
"deprecated": true,
"deprecationInfo": "The Hidden component was deprecated in Material UI v5. To learn more, see <a href=\"/material-ui/migration/v5-component-changes/#hidden\">the Hidden section</a> of the migration docs."
}
101 changes: 72 additions & 29 deletions docs/src/modules/components/ApiPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Typography from '@mui/material/Typography';
import AdGuest from 'docs/src/modules/components/AdGuest';
import Alert from '@mui/material/Alert';
import VerifiedRoundedIcon from '@mui/icons-material/VerifiedRounded';
import DangerousIcon from '@mui/icons-material/Dangerous';
import { alpha } from '@mui/material/styles';
import { useTranslate, useUserLanguage } from '@mui/docs/i18n';
import { HighlightedCode } from '@mui/docs/HighlightedCode';
Expand Down Expand Up @@ -79,6 +80,8 @@ export default function ApiPage(props) {
spread,
slots: componentSlots,
classes,
deprecated,
deprecationInfo,
} = pageContent;

const componentClasses = Array.isArray(classes)
Expand Down Expand Up @@ -180,56 +183,96 @@ export default function ApiPage(props) {
>
<MarkdownElement>
<h1>{pageContent.name} API</h1>
<Typography variant="h5" component="p" className="description" gutterBottom>
{description}
{disableAd ? null : (
<BrandingProvider>
<AdGuest>
<Ad />
</AdGuest>
</BrandingProvider>
)}
</Typography>
<Heading hash="demos" />
<Alert
severity="success"
variant="outlined"
icon={<VerifiedRoundedIcon sx={{ fontSize: 20 }} />}
sx={[
(theme) => ({
{deprecated ? (
<Alert
severity="error"
variant="outlined"
icon={<DangerousIcon sx={{ fontSize: 20 }} />}
sx={(theme) => ({
mt: 1.5,
pt: 1,
px: 2,
pb: 0,
mb: 1.5,
fontSize: theme.typography.pxToRem(16),
backgroundColor: (theme.vars || theme).palette.success[50],
borderColor: (theme.vars || theme).palette.success[100],
backgroundColor: (theme.vars || theme).palette.error[50],
borderColor: (theme.vars || theme).palette.error[100],
'& .MuiAlert-message': {
'& * p': {
color: (theme.vars || theme).palette.text.primary,
mb: 1,
},
'& * a': {
fontWeight: theme.typography.fontWeightMedium,
color: (theme.vars || theme).palette.success[900],
textDecorationColor: alpha(theme.palette.success[600], 0.3),
color: (theme.vars || theme).palette.error[900],
textDecorationColor: alpha(theme.palette.error[600], 0.3),
},
},
...theme.applyDarkStyles({
backgroundColor: alpha(theme.palette.success[700], 0.12),
borderColor: alpha(theme.palette.success[400], 0.1),
backgroundColor: alpha(theme.palette.error[700], 0.12),
borderColor: alpha(theme.palette.error[400], 0.1),
'& .MuiAlert-message': {
ul: {
pl: 3,
},
'& * a': {
color: (theme.vars || theme).palette.success[100],
textDecorationColor: alpha(theme.palette.success[100], 0.3),
color: (theme.vars || theme).palette.error[100],
textDecorationColor: alpha(theme.palette.error[100], 0.3),
},
},
}),
})}
>
<span
dangerouslySetInnerHTML={{
__html: deprecationInfo,
}}
/>
</Alert>
) : null}
<Typography variant="h5" component="p" className="description" gutterBottom>
{description}
{disableAd ? null : (
<BrandingProvider>
<AdGuest>
<Ad />
</AdGuest>
</BrandingProvider>
)}
</Typography>
<Heading hash="demos" />
<Alert
severity="success"
variant="outlined"
icon={<VerifiedRoundedIcon sx={{ fontSize: 20 }} />}
sx={(theme) => ({
mt: 1.5,
pb: 0,
fontSize: theme.typography.pxToRem(16),
backgroundColor: (theme.vars || theme).palette.success[50],
borderColor: (theme.vars || theme).palette.success[100],
'& .MuiAlert-message': {
'& * p': {
color: (theme.vars || theme).palette.text.primary,
mb: 1,
},
'& * a': {
fontWeight: theme.typography.fontWeightMedium,
color: (theme.vars || theme).palette.success[900],
textDecorationColor: alpha(theme.palette.success[600], 0.3),
},
},
...theme.applyDarkStyles({
backgroundColor: alpha(theme.palette.success[700], 0.12),
borderColor: alpha(theme.palette.success[400], 0.1),
'& .MuiAlert-message': {
ul: {
pl: 3,
},
'& * a': {
color: (theme.vars || theme).palette.success[100],
textDecorationColor: alpha(theme.palette.success[100], 0.3),
},
},
}),
]}
})}
>
<span
dangerouslySetInnerHTML={{
Expand Down
26 changes: 23 additions & 3 deletions packages/api-docs-builder/ApiBuilders/ComponentApiBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type { Link } from 'mdast';
import { defaultHandlers, parse as docgenParse, ReactDocgenApi } from 'react-docgen';
import { renderMarkdown } from '@mui/internal-markdown';
import { ComponentClassDefinition } from '@mui/internal-docs-utils';
import { parse as parseDoctrine, Annotation } from 'doctrine';
import { ProjectSettings, SortingStrategiesType } from '../ProjectSettings';
import { ComponentInfo, toGitHubPath, writePrettifiedFile } from '../buildApiUtils';
import muiDefaultPropsHandler from '../utils/defaultPropsHandler';
Expand Down Expand Up @@ -52,6 +53,8 @@ export interface ReactApi extends ReactDocgenApi {
muiName: string;
description: string;
spread: boolean | undefined;
deprecated: true | undefined;
deprecationInfo: string | undefined;
/**
* If `true`, the component supports theme default props customization.
* If `null`, we couldn't infer this information.
Expand Down Expand Up @@ -147,7 +150,7 @@ export async function computeApiDescription(
* *
* * - [Icon API](https://mui.com/api/icon/)
*/
async function annotateComponentDefinition(api: ReactApi) {
async function annotateComponentDefinition(api: ReactApi, componentJsdoc: Annotation) {
const HOST = 'https://mui.com';

const typesFilename = api.filename.replace(/\.js$/, '.d.ts');
Expand Down Expand Up @@ -318,6 +321,10 @@ async function annotateComponentDefinition(api: ReactApi) {
markdownLines.push(`- inherits ${inheritanceAPILink}`);
}

componentJsdoc.tags.forEach((tag) => {
markdownLines.push('', `@${tag.title} ${tag.description}`);
});

const jsdoc = `/**\n${markdownLines
.map((line) => (line.length > 0 ? ` * ${line}` : ` *`))
.join('\n')}\n */`;
Expand Down Expand Up @@ -400,6 +407,8 @@ const generateApiPage = async (
.map((item) => `<li><a href="${item.demoPathname}">${item.demoPageTitle}</a></li>`)
.join('\n')}</ul>`,
cssComponent: cssComponents.indexOf(reactApi.name) >= 0,
deprecated: reactApi.deprecated,
deprecationInfo: reactApi.deprecationInfo,
};

const { classesSort = sortAlphabetical('key'), slotsSort = null } = {
Expand Down Expand Up @@ -738,13 +747,24 @@ export default async function generateComponentApi(
reactApi.props = {};
}

const { getComponentImports = defaultGetComponentImports } = projectSettings;
const componentJsdoc = parseDoctrine(reactApi.description);

// We override `reactApi.description` with `componentJsdoc.description` because
// the former includes JSDoc tags that we don't want to render in the API page.
reactApi.description = componentJsdoc.description;

// Ignore what we might have generated in `annotateComponentDefinition`
const annotatedDescriptionMatch = reactApi.description.match(/(Demos|API):\r?\n\r?\n/);
if (annotatedDescriptionMatch !== null) {
reactApi.description = reactApi.description.slice(0, annotatedDescriptionMatch.index).trim();
}

const { getComponentImports = defaultGetComponentImports } = projectSettings;
const deprecation = componentJsdoc.tags.find((tag) => tag.title === 'deprecated');

reactApi.deprecated = !!deprecation || undefined;
reactApi.deprecationInfo = renderMarkdown(deprecation?.description || '') || undefined;

reactApi.filename = filename;
reactApi.name = componentInfo.name;
reactApi.imports = getComponentImports(componentInfo.name, filename);
Expand Down Expand Up @@ -825,7 +845,7 @@ export default async function generateComponentApi(
: !skipAnnotatingComponentDefinition
) {
// Add comment about demo & api links (including inherited component) to the component file
await annotateComponentDefinition(reactApi);
await annotateComponentDefinition(reactApi, componentJsdoc);
}
}

Expand Down
2 changes: 2 additions & 0 deletions packages/mui-material/src/Hidden/Hidden.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ export interface HiddenProps {
* API:
*
* - [Hidden API](https://mui.com/material-ui/api/hidden/)
*
* @deprecated The Hidden component was deprecated in Material UI v5. To learn more, see [the Hidden section](/material-ui/migration/v5-component-changes/#hidden) of the migration docs.
*/
declare const Hidden: React.JSXElementConstructor<HiddenProps>;

Expand Down
2 changes: 2 additions & 0 deletions packages/mui-material/src/Hidden/Hidden.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import HiddenCss from './HiddenCss';

/**
* Responsively hides children based on the selected implementation.
*
* @deprecated The Hidden component was deprecated in Material UI v5. To learn more, see [the Hidden section](/material-ui/migration/v5-component-changes/#hidden) of the migration docs.
*/
function Hidden(props) {
const {
Expand Down
Loading