Skip to content

Commit 8495972

Browse files
[docs-infra] Type interface API pages (#14138)
1 parent 9b6b52a commit 8495972

File tree

5 files changed

+139
-42
lines changed

5 files changed

+139
-42
lines changed

docs/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
"@types/luxon": "^3.4.2",
113113
"@types/moment-hijri": "^2.1.4",
114114
"@types/moment-jalaali": "^0.7.9",
115+
"@types/prop-types": "^15.7.12",
115116
"@types/react-dom": "^18.3.0",
116117
"@types/react-router-dom": "^5.3.3",
117118
"@types/stylis": "^4.2.6",

docs/scripts/api/buildInterfacesDocumentation.ts

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,40 @@ type BuildApiInterfacesJsonOptions = BuildInterfacesCommonOptions & {
191191
interfacesWithDedicatedPage: DocumentedInterfaces;
192192
};
193193

194+
export interface InterfaceApiContent {
195+
/**
196+
* The name of the documented interface.
197+
*/
198+
name: string;
199+
/**
200+
* The array of way to import this interface.
201+
*/
202+
imports: string[];
203+
/**
204+
* The HTML content of the demonstrations list.
205+
*/
206+
demos?: string;
207+
/**
208+
* The mapping of property name to their typing.
209+
*/
210+
properties: {
211+
[property: string]: {
212+
/**
213+
* The initial type definition.
214+
*/
215+
type: { description: string };
216+
default?: string;
217+
required?: true;
218+
isProPlan?: true;
219+
isPremiumPlan?: true;
220+
};
221+
};
222+
}
223+
224+
export interface InterfaceApiTranslation {
225+
interfaceDescription: string;
226+
propertiesDescriptions: { [property: string]: { description: string } };
227+
}
194228
export async function buildApiInterfacesJson(options: BuildApiInterfacesJsonOptions) {
195229
const { projects, apiPagesFolder, folder, interfaces, interfacesWithDedicatedPage } = options;
196230

@@ -295,14 +329,14 @@ export async function buildInterfacesDocumentationPage(
295329

296330
const slug = kebabCase(parsedInterface.name);
297331

298-
const content = {
332+
const content: InterfaceApiContent = {
299333
name: parsedInterface.name,
300334
imports: generateImportStatement(parsedInterface, projects),
301335
...extractDemos(parsedInterface.tags.demos),
302336
properties: {},
303337
};
304338

305-
const translations = {
339+
const translations: InterfaceApiTranslation = {
306340
interfaceDescription: renderMarkdown(
307341
linkify(
308342
escapeCell(parsedInterface.description || ''),

docs/src/modules/components/InterfaceApiPage.js renamed to docs/src/modules/components/InterfaceApiPage.tsx

Lines changed: 96 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,37 @@ import Typography from '@mui/material/Typography';
66
import Alert from '@mui/material/Alert';
77
import VerifiedRoundedIcon from '@mui/icons-material/VerifiedRounded';
88
import { alpha } from '@mui/material/styles';
9-
import { useTranslate, useUserLanguage } from '@mui/docs/i18n';
9+
import { Translate, useTranslate, useUserLanguage } from '@mui/docs/i18n';
1010
import { HighlightedCode } from '@mui/docs/HighlightedCode';
1111
import { MarkdownElement } from '@mui/docs/MarkdownElement';
12-
import { SectionTitle } from '@mui/docs/SectionTitle';
12+
import { SectionTitle, SectionTitleProps } from '@mui/docs/SectionTitle';
1313
import AppLayoutDocs from 'docs/src/modules/components/AppLayoutDocs';
1414
import PropertiesSection from 'docs/src/modules/components/ApiPage/sections/PropertiesSection';
15-
import { DEFAULT_API_LAYOUT_STORAGE_KEYS } from 'docs/src/modules/components/ApiPage/sections/ToggleDisplayOption';
15+
import { PropertyDefinition } from 'docs/src/modules/components/ApiPage/definitions/properties';
16+
import { LayoutStorageKeys } from 'docs/src/modules/components/ApiPage';
17+
import {
18+
DEFAULT_API_LAYOUT_STORAGE_KEYS,
19+
ApiDisplayOptions,
20+
} from 'docs/src/modules/components/ApiPage/sections/ToggleDisplayOption';
21+
import {
22+
InterfaceApiTranslation,
23+
InterfaceApiContent,
24+
} from 'docsx/scripts/api/buildInterfacesDocumentation';
25+
import { TableOfContentsEntry } from '@mui/internal-markdown';
26+
import kebabCase from 'lodash/kebabCase';
1627

17-
export function getTranslatedHeader(t, header) {
28+
type HeaderHash = 'demos' | 'import';
29+
30+
export function getTranslatedHeader(t: Translate, header: HeaderHash) {
1831
const translations = {
1932
demos: t('api-docs.demos'),
2033
import: t('api-docs.import'),
2134
};
2235

23-
// TODO Drop runtime type-checking once we type-check this file
24-
if (!translations.hasOwnProperty(header)) {
25-
throw new TypeError(
26-
`Unable to translate header '${header}'. Did you mean one of '${Object.keys(
27-
translations,
28-
).join("', '")}'`,
29-
);
30-
}
31-
3236
return translations[header] || header;
3337
}
3438

35-
function Heading(props) {
39+
function Heading(props: Pick<SectionTitleProps<HeaderHash>, 'hash' | 'level'>) {
3640
const { hash, level = 'h2' } = props;
3741
const t = useTranslate();
3842

@@ -44,7 +48,62 @@ Heading.propTypes = {
4448
level: PropTypes.string,
4549
};
4650

47-
export default function ApiPage(props) {
51+
interface ApiPageProps {
52+
descriptions: {
53+
[lang: string]: InterfaceApiTranslation & {
54+
// Table of Content added by the mapApiPageTranslations function
55+
componentDescriptionToc: TableOfContentsEntry[];
56+
};
57+
};
58+
pageContent: InterfaceApiContent;
59+
defaultLayout?: ApiDisplayOptions;
60+
/**
61+
* The localStorage key used to save the user layout for each section.
62+
* It's useful to dave different preferences on different pages.
63+
* For example, the data grid has a different key that the core.
64+
*/
65+
layoutStorageKey?: LayoutStorageKeys;
66+
}
67+
68+
interface GetInterfaceApiDefinitionsParams {
69+
interfaceName: string;
70+
properties: InterfaceApiContent['properties'];
71+
propertiesDescriptions: InterfaceApiTranslation['propertiesDescriptions'];
72+
/**
73+
* Add indicators that the properties is optional instead of showing it is required.
74+
*/
75+
showOptionalAbbr?: boolean;
76+
}
77+
78+
export function getInterfaceApiDefinitions(
79+
params: GetInterfaceApiDefinitionsParams,
80+
): PropertyDefinition[] {
81+
const { properties, propertiesDescriptions, interfaceName, showOptionalAbbr = false } = params;
82+
83+
return Object.entries(properties).map(([propertyName, propertyData]) => {
84+
const isRequired = propertyData.required && !showOptionalAbbr;
85+
const isOptional = !propertyData.required && showOptionalAbbr;
86+
87+
const typeName = propertyData.type.description;
88+
const propDefault = propertyData.default;
89+
const propDescription = propertiesDescriptions[propertyName];
90+
91+
return {
92+
propName: propertyName,
93+
hash: `${kebabCase(interfaceName)}-prop-${propertyName}`,
94+
propertyName,
95+
description: propDescription?.description,
96+
isOptional,
97+
isRequired,
98+
typeName,
99+
propDefault,
100+
isProPlan: propertyData.isProPlan,
101+
isPremiumPlan: propertyData.isPremiumPlan,
102+
};
103+
});
104+
}
105+
106+
export default function ApiPage(props: ApiPageProps) {
48107
const {
49108
descriptions,
50109
pageContent,
@@ -54,21 +113,17 @@ export default function ApiPage(props) {
54113
const t = useTranslate();
55114
const userLanguage = useUserLanguage();
56115

57-
const { demos, filename = '', properties } = pageContent;
116+
const { demos, properties } = pageContent;
58117

59-
const { componentDescription, propertiesDescriptions, interfaceDescription } =
60-
descriptions[userLanguage];
61-
const description = t('api-docs.pageDescription').replace(/{{name}}/, pageContent.name);
62-
63-
// Prefer linking the .tsx or .d.ts for the "Edit this page" link.
64-
const apiSourceLocation = filename.replace('.js', '.d.ts');
118+
const { propertiesDescriptions, interfaceDescription } = descriptions[userLanguage];
119+
const description = t('api-docs.interfacePageDescription').replace(/{{name}}/, pageContent.name);
65120

66121
return (
67122
<AppLayoutDocs
68123
description={description}
69124
disableToc={false}
70125
toc={[]}
71-
location={apiSourceLocation}
126+
location=""
72127
title={`${pageContent.name} API`}
73128
disableAd
74129
>
@@ -79,7 +134,7 @@ export default function ApiPage(props) {
79134
component="p"
80135
className="description"
81136
gutterBottom
82-
dangerouslySetInnerHTML={{ __html: interfaceDescription }}
137+
dangerouslySetInnerHTML={{ __html: description }}
83138
/>
84139
<Heading hash="demos" />
85140
{demos && (
@@ -131,26 +186,29 @@ export default function ApiPage(props) {
131186
language="jsx"
132187
/>
133188

134-
{componentDescription ? (
189+
{interfaceDescription ? (
135190
<React.Fragment>
136191
<br />
137192
<br />
138193
<span
139194
dangerouslySetInnerHTML={{
140-
__html: componentDescription,
195+
__html: interfaceDescription,
141196
}}
142197
/>
143198
</React.Fragment>
144199
) : null}
200+
145201
<PropertiesSection
146-
properties={properties}
147-
propertiesDescriptions={propertiesDescriptions}
148-
componentName={pageContent.name}
202+
properties={getInterfaceApiDefinitions({
203+
propertiesDescriptions,
204+
properties,
205+
interfaceName: pageContent.name,
206+
showOptionalAbbr: true,
207+
})}
149208
title="api-docs.properties"
150209
titleHash="properties"
151210
defaultLayout={defaultLayout}
152211
layoutStorageKey={layoutStorageKey.props}
153-
showOptionalAbbr
154212
/>
155213
</MarkdownElement>
156214
<svg style={{ display: 'none' }} xmlns="http://www.w3.org/2000/svg">
@@ -162,15 +220,13 @@ export default function ApiPage(props) {
162220
);
163221
}
164222

165-
ApiPage.propTypes = {
166-
defaultLayout: PropTypes.oneOf(['collapsed', 'expanded', 'table']),
167-
descriptions: PropTypes.object.isRequired,
168-
layoutStorageKey: PropTypes.shape({
169-
props: PropTypes.string,
170-
}),
171-
pageContent: PropTypes.object.isRequired,
172-
};
173-
174223
if (process.env.NODE_ENV !== 'production') {
175-
ApiPage.propTypes = exactProp(ApiPage.propTypes);
224+
ApiPage.propTypes = exactProp({
225+
defaultLayout: PropTypes.oneOf(['collapsed', 'expanded', 'table']),
226+
descriptions: PropTypes.object.isRequired,
227+
layoutStorageKey: PropTypes.shape({
228+
props: PropTypes.string,
229+
}),
230+
pageContent: PropTypes.object.isRequired,
231+
});
176232
}

docs/translations/translations.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,5 +142,8 @@
142142
"/components/backdrop": "Backdrop",
143143
"/components/alert": "Alert",
144144
"/components/pagination": "Pagination"
145+
},
146+
"api-docs": {
147+
"interfacePageDescription": "Extended documentation for the {{name}} interface with detailed information on the module's properties and available APIs."
145148
}
146149
}

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)