diff --git a/packages/api-docs-builder/ApiBuilders/ComponentApiBuilder.ts b/packages/api-docs-builder/ApiBuilders/ComponentApiBuilder.ts index 726674e48b01e4..d02f449cad1b99 100644 --- a/packages/api-docs-builder/ApiBuilders/ComponentApiBuilder.ts +++ b/packages/api-docs-builder/ApiBuilders/ComponentApiBuilder.ts @@ -32,7 +32,7 @@ import { ComponentReactApi, ParsedProperty, } from '../types/ApiBuilder.types'; -import { Slot, ComponentInfo } from '../types/utils.types'; +import { Slot, ComponentInfo, ApiItemDescription } from '../types/utils.types'; import extractInfoFromEnum from '../utils/extractInfoFromEnum'; const cssComponents = ['Box', 'Grid', 'Typography', 'Stack']; @@ -335,6 +335,9 @@ const generateApiPage = async ( imports: reactApi.imports, ...(reactApi.slots?.length > 0 && { slots: reactApi.slots }), ...(Object.keys(reactApi.cssVariables).length > 0 && { cssVariables: reactApi.cssVariables }), + ...(Object.keys(reactApi.dataAttributes).length > 0 && { + dataAttributes: reactApi.dataAttributes, + }), classes: reactApi.classes, spread: reactApi.spread, themeDefaultProps: reactApi.themeDefaultProps, @@ -488,6 +491,20 @@ const attachTranslations = ( }); } + /** + * Data attributes descriptions. + */ + if (Object.keys(reactApi.dataAttributes).length > 0) { + translations.dataAttributesDescriptions = {}; + [...Object.keys(reactApi.dataAttributes)] + .sort() // Sort to ensure consistency of object key order + .forEach((dataAttributeName: string) => { + const dataAttribute = reactApi.dataAttributes[dataAttributeName]; + const { description } = dataAttribute; + translations.dataAttributesDescriptions![dataAttributeName] = renderMarkdown(description); + }); + } + reactApi.translations = translations; }; @@ -635,16 +652,21 @@ const defaultGetComponentImports = (name: string, filename: string) => { return [subpathImport, rootImport]; }; -const attachCssVariables = (reactApi: ComponentReactApi, params: ParsedProperty[]) => { - const cssVarsErrors: Array<[propName: string, error: Error]> = []; - const cssVariables: ComponentReactApi['cssVariables'] = params +const attachTable = ( + reactApi: ComponentReactApi, + params: ParsedProperty[], + attribute: 'cssVariables' | 'dataAttributes', + defaultType?: string, +) => { + const errors: Array<[propName: string, error: Error]> = []; + const data: { [key: string]: ApiItemDescription } = params .map((p) => { const { name: propName, ...propDescriptor } = p; let prop: Omit | null; try { prop = propDescriptor; } catch (error) { - cssVarsErrors.push([propName, error as Error]); + errors.push([propName, error as Error]); prop = null; } if (prop === null) { @@ -656,7 +678,11 @@ const attachCssVariables = (reactApi: ComponentReactApi, params: ParsedProperty[ const deprecation = deprecationTag?.text?.[0]?.text; const typeTag = propDescriptor.tags?.type; - const type = (typeTag?.text?.[0]?.text ?? 'string').replace(/{|}/g, ''); + + let type = typeTag?.text?.[0]?.text ?? defaultType; + if (typeof type === 'string') { + type = type.replace(/{|}/g, ''); + } return { name: propName, @@ -674,17 +700,17 @@ const attachCssVariables = (reactApi: ComponentReactApi, params: ParsedProperty[ }; }, {}); - if (cssVarsErrors.length > 0) { + if (errors.length > 0) { throw new Error( - `There were errors creating CSS variable descriptions:\n${cssVarsErrors - .map(([cssVarName, error]) => { - return ` - ${cssVarName}: ${error}`; + `There were errors creating ${attribute.replace(/([A-Z])/g, ' $1')} descriptions:\n${errors + .map(([item, error]) => { + return ` - ${item}: ${error}`; }) .join('\n')}`, ); } - reactApi.cssVariables = cssVariables; + reactApi[attribute] = data; }; /** @@ -844,8 +870,15 @@ export default async function generateComponentApi( project, ); + const dataAttributes = await extractInfoFromEnum( + `${componentInfo.name}DataAttributes`, + new RegExp(`${componentInfo.name}(DataAttributes)?.tsx?$`, 'i'), + project, + ); + attachPropsTable(reactApi, projectSettings.propsSettings); - attachCssVariables(reactApi, cssVars); + attachTable(reactApi, cssVars, 'cssVariables', 'string'); + attachTable(reactApi, dataAttributes, 'dataAttributes'); attachTranslations(reactApi, deprecationInfo, projectSettings.propsSettings); // eslint-disable-next-line no-console diff --git a/packages/api-docs-builder/types/ApiBuilder.types.ts b/packages/api-docs-builder/types/ApiBuilder.types.ts index a78e4d44fb5ba8..37f636d37b7e9a 100644 --- a/packages/api-docs-builder/types/ApiBuilder.types.ts +++ b/packages/api-docs-builder/types/ApiBuilder.types.ts @@ -1,6 +1,6 @@ import { ReactDocgenApi } from 'react-docgen'; import { JSDocTagInfo } from 'typescript'; -import { ComponentInfo, Slot, HookInfo, SeeMore, CssVariable } from './utils.types'; +import { ComponentInfo, Slot, HookInfo, SeeMore, ApiItemDescription } from './utils.types'; export type AdditionalPropsInfo = { cssApi?: boolean; @@ -58,6 +58,7 @@ export interface PropsTranslations { }; slotDescriptions?: { [key: string]: string }; cssVariablesDescriptions?: { [key: string]: string }; + dataAttributesDescriptions?: { [key: string]: string }; } interface PropDescription { @@ -93,7 +94,8 @@ export interface ComponentReactApi extends CommonReactApi { themeDefaultProps: boolean | undefined | null; classes: ComponentClassDefinition[]; slots: Slot[]; - cssVariables: { [key: string]: CssVariable }; + cssVariables: { [key: string]: ApiItemDescription }; + dataAttributes: { [key: string]: ApiItemDescription }; propsTable: _.Dictionary; translations: PropsTranslations; } @@ -103,7 +105,8 @@ export interface ComponentApiContent { name: string; imports: string[]; slots?: Slot[]; - cssVariables?: { [key: string]: CssVariable }; + cssVariables?: { [key: string]: ApiItemDescription }; + dataAttributes?: { [key: string]: ApiItemDescription }; classes: ComponentClassDefinition[]; spread: boolean | undefined; themeDefaultProps: boolean | null | undefined; diff --git a/packages/api-docs-builder/types/utils.types.ts b/packages/api-docs-builder/types/utils.types.ts index 62a1177cc00d80..6cd4f256bdd028 100644 --- a/packages/api-docs-builder/types/utils.types.ts +++ b/packages/api-docs-builder/types/utils.types.ts @@ -7,7 +7,7 @@ export interface Slot { default?: string; } -export interface CssVariable { +export interface ApiItemDescription { name: string; description: string; } diff --git a/packages/api-docs-builder/utils/extractInfoFromEnum.ts b/packages/api-docs-builder/utils/extractInfoFromEnum.ts index 77449c527a3de0..c3eb6c18a43e56 100644 --- a/packages/api-docs-builder/utils/extractInfoFromEnum.ts +++ b/packages/api-docs-builder/utils/extractInfoFromEnum.ts @@ -22,14 +22,13 @@ const extractInfoFromEnum = async ( ): Promise => { // Generate the params let result: ParsedProperty[] = []; - try { - const cssVarDeclarationCandidates = project.program + const declarationCandidates = project.program .getSourceFiles() .filter((file) => sourceFileNamePattern.test(file.fileName)); let enumSymbol: Symbol | null = null; - cssVarDeclarationCandidates.forEach((file) => { + declarationCandidates.forEach((file) => { forEachChild(file, (node: Node) => { if (isEnumDeclaration(node) && node.name.getText() === typeName) { enumSymbol = project.checker.getSymbolAtLocation(node.name)!;