From f85d05d3cac768a277379eec5b296a93613c97fa Mon Sep 17 00:00:00 2001 From: sarahgm <38324334+sarahgm@users.noreply.github.com> Date: Mon, 5 Aug 2024 14:03:34 +0200 Subject: [PATCH] docs: make props table more awesome (#4067) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: aromko Co-authored-by: Marcel Köhler <77496890+aromko@users.noreply.github.com> --- docs/scripts/build-component-props.mjs | 174 +++++++++++++++++- docs/ui/PropsTable.tsx | 92 +++++---- packages/components/src/Card/Card.tsx | 4 +- packages/components/src/Headline/Headline.tsx | 2 +- packages/components/src/Inset/Inset.tsx | 6 +- packages/components/src/Table/Table.tsx | 3 +- packages/components/src/Table/TableBody.tsx | 10 +- packages/system/src/style-props.tsx | 35 ++-- .../theme-docs/src/components/Table.styles.ts | 2 +- 9 files changed, 239 insertions(+), 89 deletions(-) diff --git a/docs/scripts/build-component-props.mjs b/docs/scripts/build-component-props.mjs index 42683868bf..4be272179f 100644 --- a/docs/scripts/build-component-props.mjs +++ b/docs/scripts/build-component-props.mjs @@ -1,5 +1,7 @@ // @ts-check +import * as prettier from 'prettier'; import docgen from 'react-docgen-typescript'; +import { codeToHtml } from 'shiki'; import { fileURLToPath } from 'url'; import { fs, globby, path } from 'zx'; @@ -11,7 +13,14 @@ const parser = docgen.withCustomConfig('./tsconfig.json', { shouldExtractValuesFromUnion: false, skipChildrenPropWithoutDoc: false, propFilter: { - skipPropsWithName: ['variant', 'size', 'key', 'style'], + skipPropsWithName: [ + 'variant', + 'size', + 'key', + 'style', + 'UNSTABLE_childItems', + 'UNSAFE_selectionState', + ], }, customComponentTypes: [ 'AutocompleteComponent', @@ -22,6 +31,152 @@ const parser = docgen.withCustomConfig('./tsconfig.json', { ], }); +const transformDefaultValue = async val => { + let x = val.defaultValue.value; + x = /^[a-zA-Z]/.test(x) ? `"${x}"` : x; + + return await codeToHtml(`${x}`, { + lang: 'ts', + theme: 'min-light', + }); +}; + +const formatText = (text, pattern, replacementText) => { + if (pattern.test(text)) { + return text.replace(pattern, replacementText); + } + + return text; +}; + +const applyFormatSteps = text => { + text = formatText(text, /\=>\s+void/g, '=> xxx'); + text = formatText(text, /<\.\.\.>/g, ''); + + return text; +}; + +const revertFormatSteps = text => { + text = formatText(text, /\=>\s+xxx/g, '=> void'); + text = formatText(text, //g, '<...>'); + + return text; +}; + +const replacePropName = property => { + let spaceTypeName = 'GapSpaceProp'; + + if ( + property.name === 'space' && + property.declarations !== undefined && + property.declarations.some(declaration => + ['Inset.tsx'].some(fileName => declaration.fileName.includes(fileName)) + ) + ) { + spaceTypeName = 'PaddingSpaceProp'; + } + + const transformations = { + width: { + typeName: 'WidthProp', + }, + space: { + typeName: spaceTypeName, + }, + height: { + typeName: 'HeightProp', + }, + p: { + typeName: 'PaddingSpaceProp', + }, + pb: { + typeName: 'PaddingBottomProp', + }, + pt: { + typeName: 'PaddingTopProp', + }, + pl: { + typeName: 'PaddingLeftProp', + }, + pr: { + typeName: 'PaddingRightProp', + }, + py: { + typeName: 'PaddingSpacePropY', + }, + px: { + typeName: 'PaddingSpacePropX', + }, + spaceY: { + typeName: 'PaddingSpacePropY', + }, + spaceX: { + typeName: 'PaddingSpacePropX', + }, + position: { + typeName: 'ObjectFitProp', + }, + fontSize: { + typeName: 'FontSizeProp', + }, + weight: { + typeName: 'FontWeightProp', + }, + cursor: { + typeName: 'CursorProp', + }, + orientation: { + typeName: 'AlignmentProp', + }, + }; + + const transformation = transformations[property.name]; + if (transformation) { + property.type.name = transformation.typeName; + } +}; + +const transformTypeValue = async val => { + //List of types prettier can't handle see https://prettier.io/playground + const ignorePrettier = [ + 'any[]', + 'string | number | readonly string[]', + 'string[]', + '(number | "fit")[]', + 'TemplateValue[]', + '0 | "auto" | "full" | "fit" | "min" | "max" | "screen" | "svh" | "lvh" | "dvh" | "px" | "0.5" | 1 | "1.5" | 2 | "2.5" | 3 | "3.5" | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 16 | 20 | 24 | 28 | ... 37 more ...', + '{ vertical?: { alignY?: "none" | "center" | "top" | "bottom"; alignX?: "none" | "left" | "center" | "right"; } | undefined; horizontal?: { alignX?: "none" | "left" | "center" | "right" | undefined; alignY?: "none" | ... 3 more ... | undefined; } | undefined; } | undefined', + '{ input?: string; action?: string; } | undefined', + '(path: string, routerOptions: undefined) => void', + 'ReactNode[]', + 'number | number[]', + 'CellElement | CellElement[] | CellRenderer', + '"none" | "auto" | "default" | "pointer" | "wait" | "text" | "move" | "help" | "notAllowed" | "progress" | "cell" | "crosshair" | "vertical" | "alias" | "copy" | "noDrop" | "grap" | ... 8 more ...', + '"Accordion" | "Badge" | "Body" | "Button" | "Card" | "DateField" | "Dialog" | "Divider" | "Field" | "Footer" | "Header" | "Headline" | "Popover" | "HelpText" | "Image" | "Checkbox" | ... 21 more ... | "ComboBox"', + 'string | { [slot in keyof ThemeComponent]?: string; }', + 'keyof NumberFormatOptionsCurrencyDisplayRegistry', + 'boolean | keyof NumberFormatOptionsUseGroupingRegistry | "true" | "false"', + 'keyof NumberFormatOptionsSignDisplayRegistry', + ]; + let text = val.type.name; + + if (!ignorePrettier.includes(text)) { + text = applyFormatSteps(text); + + text = await prettier + .format(text, { + printWidth: 85, + parser: 'typescript', + }) + .then(text => revertFormatSteps(text)); + } + + return codeToHtml(text.replace(/^\((.*)\)$/, '$1'), { + lang: 'ts', + theme: 'min-light', + }); +}; + // Resolve __dirname for ESM const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -46,11 +201,11 @@ const files = await globby([ const output = {}; -files.forEach(file => { +for await (const file of files) { const docs = parser.parse(file); if (docs.length === 0) { - return; + continue; } const { name } = path.parse(file); @@ -60,10 +215,19 @@ files.forEach(file => { for (const key in props) { // Remove properties we do not need. - const { parent, declarations, ...val } = props[key]; + const { parent, ...val } = props[key]; + + replacePropName(val); + + val.type.value = await transformTypeValue(val); + + if (val.defaultValue) { + val.defaultValue.value = await transformDefaultValue(val); + } + output[name][key] = val; } -}); +} await fs.writeJson(outputFilePath, output); console.log(`✅ Successfully generated props table!`); diff --git a/docs/ui/PropsTable.tsx b/docs/ui/PropsTable.tsx index 4be5123b19..a1b4ed1291 100644 --- a/docs/ui/PropsTable.tsx +++ b/docs/ui/PropsTable.tsx @@ -1,14 +1,8 @@ import componentProps from '@/registry/props.json'; -import { Inline, Table, Text } from '@/ui'; +import { Inline, Stack, Text } from '@/ui'; import { BlankCanvas } from './icons'; import { Markdown } from './mdx'; -// Helper -// --------------- -const parseType = (val: string) => - // Remove "()" when the type is wrapped im them (this is done by prettier) - val.replace(/^\((.*)\)$/, '$1'); - // Types // --------------- export interface PropsTableProps { @@ -19,11 +13,13 @@ interface Prop { name: string; type: { name: string; + value: string; }; defaultValue: { value: any; }; description: string; + required: boolean; } // Component @@ -46,49 +42,43 @@ export const PropsTable = ({ component }: PropsTableProps) => { } return ( - - - - Property - - - Type - - - Default - - - Description - - - - {props.map(prop => ( - - - - {prop.name} - - - - - {parseType(prop.type.name)} - - - - - {prop.defaultValue ? prop.defaultValue.value : '-'} - - - - for now - className="text-pretty *:bg-transparent *:p-0 *:text-xs" - contents={prop.description} - /> - - - ))} - -
+
+ {props.map(prop => ( +
+ + + {prop.name} + {prop.required ? '' : '?'} + +
+ + + + for now + className="text-pretty text-xs *:bg-transparent *:p-0 *:text-xs" + contents={prop.description} + /> + {prop.defaultValue ? ( + + Defaults to:{' '} +
+ + ) : null} + +
+ ))} +
); }; diff --git a/packages/components/src/Card/Card.tsx b/packages/components/src/Card/Card.tsx index 1c721cc51c..fc0773c8f9 100644 --- a/packages/components/src/Card/Card.tsx +++ b/packages/components/src/Card/Card.tsx @@ -36,7 +36,7 @@ export interface CardProps size?: string; /** - * Padding of the component. + * Padding of the component. You can see allowed tokens [here](../../introduction/design-tokens?theme=core#spacing). */ p?: PaddingSpaceProp['space']; @@ -46,7 +46,7 @@ export interface CardProps px?: PaddingSpacePropX['spaceX']; /** - * Padding vertical (top and bottom) of the component. + * Padding vertical (top and bottom) of the component. You can see allowed tokens [here](../../introduction/design-tokens?theme=core#spacing). */ py?: PaddingSpacePropY['spaceY']; } diff --git a/packages/components/src/Headline/Headline.tsx b/packages/components/src/Headline/Headline.tsx index b570412ef5..7724b1c08b 100644 --- a/packages/components/src/Headline/Headline.tsx +++ b/packages/components/src/Headline/Headline.tsx @@ -18,7 +18,7 @@ export interface HeadlineProps extends TextAlignProp { variant?: string; size?: string; /** - * Set a different level from theme, values are from 1 - 6. + * Set a different level. */ level?: '1' | '2' | '3' | '4' | '5' | '6' | 1 | 2 | 3 | 4 | 5 | 6; /** diff --git a/packages/components/src/Inset/Inset.tsx b/packages/components/src/Inset/Inset.tsx index 93ac1fc316..c1b456571b 100644 --- a/packages/components/src/Inset/Inset.tsx +++ b/packages/components/src/Inset/Inset.tsx @@ -17,18 +17,18 @@ export type InsetProps = children: ReactNode; space?: never; /** - * Horizontal alignment for the children + * Horizontal alignment for the children. You can see allowed tokens [here](../../introduction/design-tokens?theme=core#spacing). */ spaceX?: PaddingSpacePropX['spaceX']; /** - * Vertical alignment for the children + * Vertical alignment for the children. You can see allowed tokens [here](../../introduction/design-tokens?theme=core#spacing). */ spaceY?: PaddingSpacePropY['spaceY']; } | { children: ReactNode; /** - * The space between the children + * The space between the children. You can see allowed tokens [here](../../introduction/design-tokens?theme=core#spacing). */ space?: PaddingSpaceProp['space']; spaceX?: never; diff --git a/packages/components/src/Table/Table.tsx b/packages/components/src/Table/Table.tsx index fd7800881b..d68311cbde 100755 --- a/packages/components/src/Table/Table.tsx +++ b/packages/components/src/Table/Table.tsx @@ -1,5 +1,4 @@ -import type { ReactNode } from 'react'; -import { useRef } from 'react'; +import { ReactNode, useRef } from 'react'; import { AriaTableProps, useTable } from '@react-aria/table'; import { TableBody as Body, diff --git a/packages/components/src/Table/TableBody.tsx b/packages/components/src/Table/TableBody.tsx index fcbe2f916c..39d8673a26 100644 --- a/packages/components/src/Table/TableBody.tsx +++ b/packages/components/src/Table/TableBody.tsx @@ -3,12 +3,16 @@ import { useTableRowGroup } from '@react-aria/table'; import { TableBodyProps as BodyProps } from '@react-stately/table'; import { useTableContext } from './Context'; -export interface TableBodyProps extends Omit, 'children'> { +export interface TableBodyProps + extends Omit, 'children' | 'loadingState' | 'items'> { /** - * The chilren of the component. + * The children of the component. */ - children: ReactNode; + children?: ReactNode; + /** + * Provides content to display when there are no rows in the table. + */ emptyState?: () => ReactNode; } diff --git a/packages/system/src/style-props.tsx b/packages/system/src/style-props.tsx index 81bbed3b9f..b9ca2cf15c 100644 --- a/packages/system/src/style-props.tsx +++ b/packages/system/src/style-props.tsx @@ -600,7 +600,7 @@ export type AlignmentProp = { }; export type CursorProp = { /** - * Set the cursor for the element. + * Set the cursor for the element. You can see allowed tokens [here](https://tailwindcss.com/docs/cursor). */ cursor?: keyof typeof cursorStyle; }; @@ -614,14 +614,14 @@ export type FontStyleProp = { export type FontWeightProp = { /** - * Set the font weight for the text element. + * Set the font weight for the text element. You can see allowed tokens [here](../../introduction/design-tokens?theme=core#typography). */ weight?: keyof typeof fontWeight; }; export type FontSizeProp = { /** - * Set the font size for the text element. + * Set the font size for the text element. You can see allowed tokens [here](../../introduction/design-tokens?theme=core#typography). */ fontSize?: keyof typeof textSize; }; @@ -633,16 +633,9 @@ export type GridColsAlignProp = { align?: keyof typeof gridColsAlign; }; -export type GridColumn = { - /** - * Set the alignment of a grid column. - */ - align?: keyof typeof gridColumn; -}; - export type GapSpaceProp = { /** - * The space between the children. + * The space between the children. You can see allowed tokens [here](../../introduction/design-tokens?theme=core#spacing). */ space?: keyof typeof gapSpace; }; @@ -656,56 +649,56 @@ export type ObjectFitProp = { export type ObjectPositionProp = { /** - * Set the object-position property for the element. + * Set the object-position property for the element. You can see allowed tokens [here](https://tailwindcss.com/docs/object-position). */ position?: keyof typeof objectPosition; }; export type PaddingSpaceProp = { /** - * Set the padding space for the element. + * Set the padding space for the element. You can see allowed tokens [here](../../introduction/design-tokens?theme=core#spacing). */ space?: keyof typeof paddingSpace; }; export type PaddingSpacePropX = { /** - * Set the horizontal padding space for the element. + * Set the horizontal padding space for the element. You can see allowed tokens [here](../../introduction/design-tokens?theme=core#spacing). */ spaceX?: keyof typeof paddingSpaceX; }; export type PaddingSpacePropY = { /** - * Set the vertical padding space for the element. + * Set the vertical padding space for the element. You can see allowed tokens [here](../../introduction/design-tokens?theme=core#spacing). */ spaceY?: keyof typeof paddingSpaceY; }; export type PaddingRightProp = { /** - * Set the right padding for the element. + * Set the right padding for the element. You can see allowed tokens [here](../../introduction/design-tokens?theme=core#spacing). */ pr?: keyof typeof paddingRight; }; export type PaddingLeftProp = { /** - * Set the left padding for the element. + * Set the left padding for the element. You can see allowed tokens [here](../../introduction/design-tokens?theme=core#spacing). */ pl?: keyof typeof paddingLeft; }; export type PaddingTopProp = { /** - * Set the top padding for the element. + * Set the top padding for the element. You can see allowed tokens [here](../../introduction/design-tokens?theme=core#spacing). */ pt?: keyof typeof paddingTop; }; export type PaddingBottomProp = { /** - * Set the bottom padding for the element. + * Set the bottom padding for the element. You can see allowed tokens [here](../../introduction/design-tokens?theme=core#spacing). */ pb?: keyof typeof paddingBottom; }; @@ -726,14 +719,14 @@ export type TextAlignProp = { export type WidthProp = { /** - * Set the width of the element. + * Sets the width of the field. You can see allowed tokens [here](https://tailwindcss.com/docs/width). */ width?: keyof typeof width; }; export type HeightProp = { /** - * Set the height of the element. + * Set the height of the element. You can see allowed tokens [here](https://tailwindcss.com/docs/height). */ height?: keyof typeof height; }; diff --git a/themes/theme-docs/src/components/Table.styles.ts b/themes/theme-docs/src/components/Table.styles.ts index dd253c8234..42de9979ff 100644 --- a/themes/theme-docs/src/components/Table.styles.ts +++ b/themes/theme-docs/src/components/Table.styles.ts @@ -2,7 +2,7 @@ import { ThemeComponent, cva } from '@marigold/system'; export const Table: ThemeComponent<'Table'> = { table: cva([ - 'w-full table-fixed overflow-hidden rounded-lg bg-white/40 text-sm', + 'w-full overflow-hidden rounded-lg bg-white/40 text-sm', 'border-secondary-200 border-separate border-spacing-0 border', ]), header: cva('px-3 pb-2 pt-3 text-start'),