From cfd765beced042c397568a7e1507f40590295132 Mon Sep 17 00:00:00 2001 From: Shahed Nasser Date: Tue, 23 Apr 2024 14:25:06 +0300 Subject: [PATCH] chore(ui): added API reference comments for Toast component (#7129) --- .../src/classes/typedoc-manager.ts | 80 +++++++++++++++++-- packages/design-system/ui/README.md | 2 +- .../ui/src/components/toast/toast.tsx | 36 +++++++++ .../ui/src/components/toaster/toaster.tsx | 18 +++++ packages/design-system/ui/src/types.ts | 12 +++ 5 files changed, 139 insertions(+), 9 deletions(-) diff --git a/docs-util/packages/react-docs-generator/src/classes/typedoc-manager.ts b/docs-util/packages/react-docs-generator/src/classes/typedoc-manager.ts index 78bddecb97..d77081948a 100644 --- a/docs-util/packages/react-docs-generator/src/classes/typedoc-manager.ts +++ b/docs-util/packages/react-docs-generator/src/classes/typedoc-manager.ts @@ -154,10 +154,10 @@ export default class TypedocManager { // push a prop into the `propsToRemove` set. Object.entries(spec.props!).forEach(([propName, propDetails]) => { // retrieve the reflection of the prop - const reflectionPropType = props.find( + const reflectionProp = props.find( (propType) => propType.name === propName ) - if (!reflectionPropType) { + if (!reflectionProp) { // if the reflection doesn't exist and the // prop doesn't have a description, it should // be removed. @@ -172,7 +172,7 @@ export default class TypedocManager { if ( this.shouldExcludeExternal({ parentReflection: reflection, - childReflection: reflectionPropType, + childReflection: reflectionProp, propDescription: propDetails.description, signature, }) @@ -180,9 +180,18 @@ export default class TypedocManager { propsToRemove.add(propName) return } + + // if the prop doesn't have a default value, try to retrieve it + if (!propDetails.defaultValue) { + propDetails.defaultValue = this.getReflectionDefaultValue( + reflectionProp, + propDetails.description + ) + } + // if the prop doesn't have description, retrieve it using Typedoc propDetails.description = - propDetails.description || this.getDescription(reflectionPropType) + propDetails.description || this.getDescription(reflectionProp) // if the prop still doesn't have description, remove it. if (!propDetails.description) { propsToRemove.add(propName) @@ -218,15 +227,15 @@ export default class TypedocManager { .forEach((prop) => { // If the prop has description (retrieved) // through Typedoc, it's added into the spec. - const description = this.normalizeDescription( - this.getDescription(prop) - ) + const rawDescription = this.getDescription(prop) + const description = this.normalizeDescription(rawDescription) if (!description) { return } spec.props![prop.name] = { description, required: !prop.flags.isOptional, + defaultValue: this.getReflectionDefaultValue(prop, rawDescription), tsType: prop.type ? this.getTsType(prop.type) : prop.signatures?.length @@ -379,11 +388,29 @@ export default class TypedocManager { // prop or component's description. These aren't removed // by React Docgen. normalizeDescription(description: string): string { - return description + let normalizedDescription = description .replace("@keep", "") .replace("@ignore", "") .replace("@excludeExternal", "") .trim() + + // check if `@defaultValue` tag is in the description and remove it + const defaultValueIndex = normalizedDescription.indexOf("@defaultValue") + if (defaultValueIndex !== -1) { + // if there are tags after the default value, keep them and only + // remove the default value. + const possibleEndIndex = normalizedDescription.indexOf( + "@", + defaultValueIndex + "@defaultValue".length + ) + normalizedDescription = + normalizedDescription.slice(0, defaultValueIndex) + + (possibleEndIndex === -1 + ? "" + : normalizedDescription.slice(possibleEndIndex)) + } + + return normalizedDescription } // Retrieve the description of a reflection (component or prop) @@ -610,4 +637,41 @@ export default class TypedocManager { }) as DeclarationReflection) : undefined } + + getReflectionDefaultValue( + reflection: DeclarationReflection, + description?: string + ): + | { + value: string + computed: boolean + } + | undefined { + let value: string | undefined + if (!reflection.defaultValue) { + // try to infer default value from description + const valueIndexInDescription = description + ? description.indexOf("@defaultValue") + : -1 + if (valueIndexInDescription !== -1) { + const sliceStart = valueIndexInDescription + "@defaultValue ".length + // the default value ends either at the end of the description or + // until the next tag. + const sliceEnd = description!.indexOf("@", sliceStart) + value = description!.slice( + sliceStart, + sliceEnd !== -1 ? sliceEnd : undefined + ) + } + } else { + value = JSON.stringify(reflection.defaultValue) + } + + return value + ? { + value, + computed: false, + } + : undefined + } } diff --git a/packages/design-system/ui/README.md b/packages/design-system/ui/README.md index a4f522926a..12f4afd29a 100644 --- a/packages/design-system/ui/README.md +++ b/packages/design-system/ui/README.md @@ -36,7 +36,7 @@ ### Installation ```sh -yarn add @medusajs/medusa-ui +yarn add @medusajs/ui ``` ### Usage diff --git a/packages/design-system/ui/src/components/toast/toast.tsx b/packages/design-system/ui/src/components/toast/toast.tsx index dbf332e65a..b85edd3c08 100644 --- a/packages/design-system/ui/src/components/toast/toast.tsx +++ b/packages/design-system/ui/src/components/toast/toast.tsx @@ -14,13 +14,49 @@ interface ToastComponentProps { dismissLabel?: string } +/** + * This component is based on the [Sonner](https://sonner.emilkowal.ski/toast) toast library. + */ export const Toast = ({ + /** + * Optional ID of the toast. + */ id, + /** + * @ignore + * + * @privateRemarks + * As the Toast component is created using + * the `toast` utility functions, the variant is inferred + * from the utility function. + */ variant = "info", + /** + * @ignore + * + * @privateRemarks + * The `toast` utility functions accept this as a parameter. + */ title, + /** + * The toast's text. + */ description, + /** + * The toast's action buttons. + */ action, + /** + * @ignore + * + * @privateRemarks + * The `toast` utility functions don't allow + * passing this prop. + */ onDismiss, + /** + * The label of the dismiss button, if available. + */ dismissLabel = "Close", }: ToastComponentProps) => { const hasActionables = !!action || onDismiss diff --git a/packages/design-system/ui/src/components/toaster/toaster.tsx b/packages/design-system/ui/src/components/toaster/toaster.tsx index 1c6b51122d..6aec7acd0e 100644 --- a/packages/design-system/ui/src/components/toaster/toaster.tsx +++ b/packages/design-system/ui/src/components/toaster/toaster.tsx @@ -18,10 +18,28 @@ interface ToasterProps | "toastOptions" > {} +/** + * This component is based on the [Toaster component of the Sonner library](https://sonner.emilkowal.ski/toaster). + */ const Toaster = ({ + /** + * The position of the created toasts. + */ position = "bottom-right", + /** + * The gap between the toast components. + */ gap = 12, + /** + * The space from the edges of the screen. + */ offset = 24, + /** + * The time in milliseconds that a toast is shown before it's + * automatically dismissed. + * + * @defaultValue 4000 + */ duration, ...props }: ToasterProps) => { diff --git a/packages/design-system/ui/src/types.ts b/packages/design-system/ui/src/types.ts index 0785720a66..aa493a1ae0 100644 --- a/packages/design-system/ui/src/types.ts +++ b/packages/design-system/ui/src/types.ts @@ -36,8 +36,20 @@ export type ToastVariant = export type ToastActionVariant = "default" | "destructive" export type ToastAction = { + /** + * The button's text. + */ label: string + /** + * The button's alt text. + */ altText: string + /** + * The function to execute when the button is clicked. + */ onClick: () => void | Promise + /** + * The button's variant. + */ variant?: ToastActionVariant }