diff --git a/.storybook/main.ts b/.storybook/main.ts index a9cb4b243c..a62960db85 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -55,9 +55,8 @@ export default defineMain({ name: getAbsolutePath('@storybook/react-vite'), options: {}, }, - features: { - experimentalComponentsManifest: true, - experimentalCodeExamples: true, // optional + typescript: { + reactDocgen: 'react-docgen', }, viteFinal: async (config, { configType }) => { // Add Node.js polyfills for browser compatibility diff --git a/package.json b/package.json index 69114ed743..93a6317951 100644 --- a/package.json +++ b/package.json @@ -136,13 +136,13 @@ "@octokit/rest": "^19.0.3", "@playwright/test": "^1.32.3", "@spotify/eslint-plugin": "^15.0.0", - "@storybook/addon-a11y": "^10.3.0-alpha.1", - "@storybook/addon-docs": "^10.3.0-alpha.1", - "@storybook/addon-links": "^10.3.0-alpha.1", - "@storybook/addon-mcp": "^0.3.0", - "@storybook/addon-themes": "^10.3.0-alpha.1", - "@storybook/addon-vitest": "^10.3.0-alpha.1", - "@storybook/react-vite": "^10.3.0-alpha.1", + "@storybook/addon-a11y": "^10.3.3", + "@storybook/addon-docs": "^10.3.3", + "@storybook/addon-links": "^10.3.3", + "@storybook/addon-mcp": "^0.4.2", + "@storybook/addon-themes": "^10.3.3", + "@storybook/addon-vitest": "^10.3.3", + "@storybook/react-vite": "^10.3.3", "@techdocs/cli": "workspace:*", "@types/cacheable-request": "^8.3.6", "@types/jest": "^30", @@ -173,7 +173,7 @@ "shx": "^0.4.0", "sloc": "^0.3.1", "sort-package-json": "^3.0.0", - "storybook": "^10.3.0-alpha.1", + "storybook": "^10.3.3", "ts-morph": "^24.0.0", "typedoc": "^0.28.0", "typescript": "~5.7.0", diff --git a/packages/ui/package.json b/packages/ui/package.json index 534109651d..6d99d851b7 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -63,13 +63,13 @@ "@types/react": "^18.0.0", "@types/react-dom": "^18.0.0", "@types/use-sync-external-store": "^0.0.6", - "eslint-plugin-storybook": "^10.3.0-alpha.1", + "eslint-plugin-storybook": "^10.3.3", "glob": "^11.0.1", "globals": "^17.0.0", "react": "^18.0.2", "react-dom": "^18.0.2", "react-router-dom": "^6.30.2", - "storybook": "^10.3.0-alpha.1" + "storybook": "^10.3.3" }, "peerDependencies": { "@types/react": "^18.0.0", diff --git a/packages/ui/src/components/Accordion/Accordion.tsx b/packages/ui/src/components/Accordion/Accordion.tsx index 1a1eb2f2c8..5ea4e92dbc 100644 --- a/packages/ui/src/components/Accordion/Accordion.tsx +++ b/packages/ui/src/components/Accordion/Accordion.tsx @@ -38,7 +38,11 @@ import { } from './definition'; import { Flex } from '../Flex'; -/** @public */ +/** + * A collapsible section that reveals or hides its content when the trigger is activated. + * + * @public + */ export const Accordion = forwardRef( (props: AccordionProps, ref: Ref>) => { const { ownProps, restProps, dataAttributes } = useDefinition( @@ -62,7 +66,11 @@ export const Accordion = forwardRef( Accordion.displayName = 'Accordion'; -/** @public */ +/** + * The clickable heading that toggles the visibility of an accordion panel. + * + * @public + */ export const AccordionTrigger = forwardRef( ( props: AccordionTriggerProps, @@ -100,7 +108,11 @@ export const AccordionTrigger = forwardRef( AccordionTrigger.displayName = 'AccordionTrigger'; -/** @public */ +/** + * The content area of an accordion that is revealed when the trigger is activated. + * + * @public + */ export const AccordionPanel = forwardRef( ( props: AccordionPanelProps, @@ -125,7 +137,11 @@ export const AccordionPanel = forwardRef( AccordionPanel.displayName = 'AccordionPanel'; -/** @public */ +/** + * A container that groups multiple Accordion items, optionally allowing several panels to be expanded at once. + * + * @public + */ export const AccordionGroup = forwardRef( ( props: AccordionGroupProps, diff --git a/packages/ui/src/components/Avatar/Avatar.tsx b/packages/ui/src/components/Avatar/Avatar.tsx index e1694d738a..eecf3458b6 100644 --- a/packages/ui/src/components/Avatar/Avatar.tsx +++ b/packages/ui/src/components/Avatar/Avatar.tsx @@ -19,7 +19,11 @@ import { AvatarProps } from './types'; import { useDefinition } from '../../hooks/useDefinition'; import { AvatarDefinition } from './definition'; -/** @public */ +/** + * Displays a user's profile image with an automatic fallback to their initials when the image fails to load. + * + * @public + */ export const Avatar = forwardRef((props, ref) => { const { ownProps, restProps, dataAttributes } = useDefinition( AvatarDefinition, diff --git a/packages/ui/src/components/Box/Box.tsx b/packages/ui/src/components/Box/Box.tsx index fb87be9ae1..146292a344 100644 --- a/packages/ui/src/components/Box/Box.tsx +++ b/packages/ui/src/components/Box/Box.tsx @@ -19,7 +19,11 @@ import { BoxProps } from './types'; import { useDefinition } from '../../hooks/useDefinition'; import { BoxDefinition } from './definition'; -/** @public */ +/** + * A general-purpose layout primitive that can render as any HTML element and supports spacing, sizing, and background props. + * + * @public + */ export const Box = forwardRef((props, ref) => { const { ownProps, restProps, dataAttributes, utilityStyle } = useDefinition( BoxDefinition, diff --git a/packages/ui/src/components/ButtonIcon/ButtonIcon.tsx b/packages/ui/src/components/ButtonIcon/ButtonIcon.tsx index 3e9953fafa..e5ac723f42 100644 --- a/packages/ui/src/components/ButtonIcon/ButtonIcon.tsx +++ b/packages/ui/src/components/ButtonIcon/ButtonIcon.tsx @@ -21,7 +21,11 @@ import type { ButtonIconProps } from './types'; import { useDefinition } from '../../hooks/useDefinition'; import { ButtonIconDefinition } from './definition'; -/** @public */ +/** + * An icon-only button that supports a loading state and requires an accessible label. + * + * @public + */ export const ButtonIcon = forwardRef( (props: ButtonIconProps, ref: Ref) => { const { ownProps, restProps, dataAttributes } = useDefinition( diff --git a/packages/ui/src/components/ButtonLink/ButtonLink.tsx b/packages/ui/src/components/ButtonLink/ButtonLink.tsx index 4d6906f32f..cbbbc957fa 100644 --- a/packages/ui/src/components/ButtonLink/ButtonLink.tsx +++ b/packages/ui/src/components/ButtonLink/ButtonLink.tsx @@ -21,7 +21,11 @@ import { useDefinition } from '../../hooks/useDefinition'; import { ButtonLinkDefinition } from './definition'; import { getNodeText } from '../../analytics/getNodeText'; -/** @public */ +/** + * A button-styled anchor element for navigation, supporting optional start and end icon slots and analytics event tracking. + * + * @public + */ export const ButtonLink = forwardRef( (props: ButtonLinkProps, ref: Ref) => { const { ownProps, restProps, dataAttributes, analytics } = useDefinition( diff --git a/packages/ui/src/components/Checkbox/Checkbox.tsx b/packages/ui/src/components/Checkbox/Checkbox.tsx index e0d4605739..f502b3a1c1 100644 --- a/packages/ui/src/components/Checkbox/Checkbox.tsx +++ b/packages/ui/src/components/Checkbox/Checkbox.tsx @@ -21,7 +21,11 @@ import { useDefinition } from '../../hooks/useDefinition'; import { CheckboxDefinition } from './definition'; import { RiCheckLine, RiSubtractLine } from '@remixicon/react'; -/** @public */ +/** + * A form checkbox input with support for indeterminate state and accessible labeling. + * + * @public + */ export const Checkbox = forwardRef( (props, ref) => { const { ownProps, restProps, dataAttributes } = useDefinition( diff --git a/packages/ui/src/components/Container/Container.tsx b/packages/ui/src/components/Container/Container.tsx index 743704b00e..ced35712d1 100644 --- a/packages/ui/src/components/Container/Container.tsx +++ b/packages/ui/src/components/Container/Container.tsx @@ -19,7 +19,11 @@ import type { ContainerProps } from './types'; import { useDefinition } from '../../hooks/useDefinition'; import { ContainerDefinition } from './definition'; -/** @public */ +/** + * A centered layout wrapper that constrains content to a maximum width and provides consistent page-level padding. + * + * @public + */ export const Container = forwardRef( (props, ref) => { const { ownProps, restProps, utilityStyle } = useDefinition( diff --git a/packages/ui/src/components/Dialog/Dialog.tsx b/packages/ui/src/components/Dialog/Dialog.tsx index 3a23ee27c9..7f421ff783 100644 --- a/packages/ui/src/components/Dialog/Dialog.tsx +++ b/packages/ui/src/components/Dialog/Dialog.tsx @@ -41,12 +41,20 @@ import { Box } from '../Box'; import { BgReset } from '../../hooks/useBg'; import { Flex } from '../Flex'; -/** @public */ +/** + * A wrapper that connects a trigger element to a Dialog, controlling its open and close state. + * + * @public + */ export const DialogTrigger = (props: DialogTriggerProps) => { return ; }; -/** @public */ +/** + * A modal overlay that presents content requiring user interaction or acknowledgment, dismissible by clicking outside or pressing Escape. + * + * @public + */ export const Dialog = forwardRef, DialogProps>( (props, ref) => { const { ownProps, restProps } = useDefinition(DialogDefinition, props, { @@ -89,7 +97,11 @@ export const Dialog = forwardRef, DialogProps>( Dialog.displayName = 'Dialog'; -/** @public */ +/** + * The header section of a Dialog, containing the title and a close button. + * + * @public + */ export const DialogHeader = forwardRef< React.ElementRef<'div'>, DialogHeaderProps @@ -110,7 +122,11 @@ export const DialogHeader = forwardRef< }); DialogHeader.displayName = 'DialogHeader'; -/** @public */ +/** + * The main scrollable content area of a Dialog. + * + * @public + */ export const DialogBody = forwardRef, DialogBodyProps>( (props, ref) => { const { ownProps, restProps } = useDefinition(DialogBodyDefinition, props); @@ -126,7 +142,11 @@ export const DialogBody = forwardRef, DialogBodyProps>( DialogBody.displayName = 'DialogBody'; -/** @public */ +/** + * The footer section of a Dialog, typically used to place action buttons. + * + * @public + */ export const DialogFooter = forwardRef< React.ElementRef<'div'>, DialogFooterProps diff --git a/packages/ui/src/components/FieldError/FieldError.tsx b/packages/ui/src/components/FieldError/FieldError.tsx index 8331bd0385..5169066c77 100644 --- a/packages/ui/src/components/FieldError/FieldError.tsx +++ b/packages/ui/src/components/FieldError/FieldError.tsx @@ -20,7 +20,11 @@ import type { FieldErrorProps } from './types'; import { useDefinition } from '../../hooks/useDefinition'; import { FieldErrorDefinition } from './definition'; -/** @public */ +/** + * Displays a validation error message associated with a form field. + * + * @public + */ export const FieldError = forwardRef( (props: FieldErrorProps, ref) => { const { ownProps, restProps } = useDefinition(FieldErrorDefinition, props); diff --git a/packages/ui/src/components/FieldLabel/FieldLabel.tsx b/packages/ui/src/components/FieldLabel/FieldLabel.tsx index 7656cacc5e..1b451be909 100644 --- a/packages/ui/src/components/FieldLabel/FieldLabel.tsx +++ b/packages/ui/src/components/FieldLabel/FieldLabel.tsx @@ -19,7 +19,11 @@ import type { FieldLabelProps } from './types'; import { useDefinition } from '../../hooks/useDefinition'; import { FieldLabelDefinition } from './definition'; -/** @public */ +/** + * Renders a label for a form field with optional secondary label and description text. + * + * @public + */ export const FieldLabel = forwardRef( (props: FieldLabelProps, ref) => { const { ownProps, restProps } = useDefinition(FieldLabelDefinition, props); diff --git a/packages/ui/src/components/Flex/Flex.tsx b/packages/ui/src/components/Flex/Flex.tsx index 1e4e4fecb3..4bf0a49428 100644 --- a/packages/ui/src/components/Flex/Flex.tsx +++ b/packages/ui/src/components/Flex/Flex.tsx @@ -19,7 +19,11 @@ import type { FlexProps } from './types'; import { useDefinition } from '../../hooks/useDefinition'; import { FlexDefinition } from './definition'; -/** @public */ +/** + * A flexbox layout container with props for controlling gap, alignment, justification, and direction. + * + * @public + */ export const Flex = forwardRef((props, ref) => { const { ownProps, dataAttributes, utilityStyle, restProps } = useDefinition( FlexDefinition, diff --git a/packages/ui/src/components/Link/Link.tsx b/packages/ui/src/components/Link/Link.tsx index a4509576c9..fca4843753 100644 --- a/packages/ui/src/components/Link/Link.tsx +++ b/packages/ui/src/components/Link/Link.tsx @@ -61,7 +61,11 @@ const LinkInternal = forwardRef((props, ref) => { LinkInternal.displayName = 'LinkInternal'; -/** @public */ +/** + * A styled anchor element that supports analytics event tracking on click. + * + * @public + */ export const Link = forwardRef((props, ref) => { return ; }); diff --git a/packages/ui/src/components/Menu/Menu.tsx b/packages/ui/src/components/Menu/Menu.tsx index 8008a071bb..da46ead3ea 100644 --- a/packages/ui/src/components/Menu/Menu.tsx +++ b/packages/ui/src/components/Menu/Menu.tsx @@ -76,7 +76,11 @@ const MenuEmptyState = () => { return
No results found.
; }; -/** @public */ +/** + * A wrapper that connects a trigger element to a dropdown menu, controlling its open and close state. + * + * @public + */ export const MenuTrigger = (props: MenuTriggerProps) => { return ; }; diff --git a/packages/ui/src/components/PasswordField/PasswordField.tsx b/packages/ui/src/components/PasswordField/PasswordField.tsx index 7dd10f8536..cbff6063a4 100644 --- a/packages/ui/src/components/PasswordField/PasswordField.tsx +++ b/packages/ui/src/components/PasswordField/PasswordField.tsx @@ -28,7 +28,11 @@ import { useDefinition } from '../../hooks/useDefinition'; import { PasswordFieldDefinition } from './definition'; import { RiEyeLine, RiEyeOffLine } from '@remixicon/react'; -/** @public */ +/** + * A text input for password entry with a toggleable visibility button, integrated label, and inline error display. + * + * @public + */ export const PasswordField = forwardRef( (props, ref) => { const { ownProps, restProps, dataAttributes } = useDefinition( diff --git a/packages/ui/src/components/RadioGroup/RadioGroup.tsx b/packages/ui/src/components/RadioGroup/RadioGroup.tsx index 91796b8984..8e869da988 100644 --- a/packages/ui/src/components/RadioGroup/RadioGroup.tsx +++ b/packages/ui/src/components/RadioGroup/RadioGroup.tsx @@ -26,7 +26,11 @@ import { RadioGroupDefinition, RadioDefinition } from './definition'; import type { RadioGroupProps, RadioProps } from './types'; -/** @public */ +/** + * A group of radio buttons for selecting a single option from a set, with an integrated label, description, and error display. + * + * @public + */ export const RadioGroup = forwardRef( (props, ref) => { const { ownProps, restProps } = useDefinition(RadioGroupDefinition, props); @@ -70,7 +74,11 @@ export const RadioGroup = forwardRef( RadioGroup.displayName = 'RadioGroup'; -/** @public */ +/** + * A single radio button for use within a RadioGroup. + * + * @public + */ export const Radio = forwardRef((props, ref) => { const { ownProps, restProps } = useDefinition(RadioDefinition, props); diff --git a/packages/ui/src/components/SearchAutocomplete/SearchAutocomplete.tsx b/packages/ui/src/components/SearchAutocomplete/SearchAutocomplete.tsx index 5fd63661f4..e9c6642d60 100644 --- a/packages/ui/src/components/SearchAutocomplete/SearchAutocomplete.tsx +++ b/packages/ui/src/components/SearchAutocomplete/SearchAutocomplete.tsx @@ -47,7 +47,11 @@ const SearchAutocompleteEmptyState = () => { return
No results found.
; }; -/** @public */ +/** + * A search input that shows a dropdown list of suggestions as the user types, with loading and empty states. + * + * @public + */ export function SearchAutocomplete(props: SearchAutocompleteProps) { const { ownProps, dataAttributes } = useDefinition( SearchAutocompleteDefinition, @@ -184,7 +188,11 @@ export function SearchAutocomplete(props: SearchAutocompleteProps) { ); } -/** @public */ +/** + * An individual option item within a SearchAutocomplete dropdown. + * + * @public + */ export function SearchAutocompleteItem(props: SearchAutocompleteItemProps) { const { ownProps, restProps } = useDefinition( SearchAutocompleteItemDefinition, diff --git a/packages/ui/src/components/SearchField/SearchField.tsx b/packages/ui/src/components/SearchField/SearchField.tsx index 56c838833a..1df389d5a3 100644 --- a/packages/ui/src/components/SearchField/SearchField.tsx +++ b/packages/ui/src/components/SearchField/SearchField.tsx @@ -28,7 +28,11 @@ import { SearchFieldDefinition } from './definition'; import type { SearchFieldProps } from './types'; -/** @public */ +/** + * A text input optimized for search queries, with a built-in clear button, optional icon, and support for a collapsible mode. + * + * @public + */ export const SearchField = forwardRef( (props, ref) => { const { ownProps, restProps, dataAttributes } = useDefinition( diff --git a/packages/ui/src/components/Select/Select.tsx b/packages/ui/src/components/Select/Select.tsx index 4b0ce5a6b6..5671557389 100644 --- a/packages/ui/src/components/Select/Select.tsx +++ b/packages/ui/src/components/Select/Select.tsx @@ -26,7 +26,11 @@ import { FieldError } from '../FieldError'; import { SelectTrigger } from './SelectTrigger'; import { SelectContent } from './SelectContent'; -/** @public */ +/** + * A dropdown picker for selecting one or multiple options from a list, with optional search filtering and inline error display. + * + * @public + */ export const Select = forwardRef< HTMLDivElement, SelectProps<'single' | 'multiple'> diff --git a/packages/ui/src/components/Skeleton/Skeleton.tsx b/packages/ui/src/components/Skeleton/Skeleton.tsx index be8c430789..fefea32884 100644 --- a/packages/ui/src/components/Skeleton/Skeleton.tsx +++ b/packages/ui/src/components/Skeleton/Skeleton.tsx @@ -18,7 +18,11 @@ import type { SkeletonProps } from './types'; import { useDefinition } from '../../hooks/useDefinition'; import { SkeletonDefinition } from './definition'; -/** @public */ +/** + * A placeholder shape displayed while content is loading, matching the size and layout of the content it replaces. + * + * @public + */ export const Skeleton = (props: SkeletonProps) => { const { ownProps, restProps, dataAttributes } = useDefinition( SkeletonDefinition, diff --git a/packages/ui/src/components/Slider/Slider.tsx b/packages/ui/src/components/Slider/Slider.tsx index 9aa2d73beb..e7f5254956 100644 --- a/packages/ui/src/components/Slider/Slider.tsx +++ b/packages/ui/src/components/Slider/Slider.tsx @@ -115,7 +115,11 @@ function SliderImpl( ); } -/** @public */ +/** + * A draggable input for selecting a numeric value or range within a defined interval, with an integrated label and value output. + * + * @public + */ export const Slider = forwardRef(SliderImpl) as (( props: SliderProps & { ref?: React.ForwardedRef }, ) => JSX.Element) & { displayName: string }; diff --git a/packages/ui/src/components/Switch/Switch.tsx b/packages/ui/src/components/Switch/Switch.tsx index 892dbba82a..51299c15a1 100644 --- a/packages/ui/src/components/Switch/Switch.tsx +++ b/packages/ui/src/components/Switch/Switch.tsx @@ -20,7 +20,11 @@ import type { SwitchProps } from './types'; import { useDefinition } from '../../hooks/useDefinition'; import { SwitchDefinition } from './definition'; -/** @public */ +/** + * A toggle control for switching between on and off states, with an optional visible label. + * + * @public + */ export const Switch = forwardRef( (props, ref) => { const { ownProps, restProps } = useDefinition(SwitchDefinition, props); diff --git a/packages/ui/src/components/Table/components/Cell.tsx b/packages/ui/src/components/Table/components/Cell.tsx index b5fc96d520..844cfb3111 100644 --- a/packages/ui/src/components/Table/components/Cell.tsx +++ b/packages/ui/src/components/Table/components/Cell.tsx @@ -19,7 +19,12 @@ import type { CellProps } from '../types'; import { useDefinition } from '../../../hooks/useDefinition'; import { CellDefinition } from '../definition'; -/** @public */ +/** + * A low-level table cell primitive for building custom cell content. + * For standard use cases, prefer `CellText` or `CellProfile`. + * + * @public + */ const Cell = (props: CellProps) => { const { ownProps, restProps } = useDefinition(CellDefinition, props); diff --git a/packages/ui/src/components/Table/components/CellProfile.tsx b/packages/ui/src/components/Table/components/CellProfile.tsx index 0565fff909..4154aeaf45 100644 --- a/packages/ui/src/components/Table/components/CellProfile.tsx +++ b/packages/ui/src/components/Table/components/CellProfile.tsx @@ -22,7 +22,11 @@ import { useDefinition } from '../../../hooks/useDefinition'; import { CellProfileDefinition } from '../definition'; import { Cell as ReactAriaCell } from 'react-aria-components'; -/** @public */ +/** + * A table cell that renders an avatar image alongside a name, with an optional description and link. + * + * @public + */ export const CellProfile = (props: CellProfileProps) => { const { ownProps, restProps } = useDefinition(CellProfileDefinition, props); const { classes, src, name, href, description, color } = ownProps; diff --git a/packages/ui/src/components/Table/components/CellText.tsx b/packages/ui/src/components/Table/components/CellText.tsx index 30b02b1e44..64809a7419 100644 --- a/packages/ui/src/components/Table/components/CellText.tsx +++ b/packages/ui/src/components/Table/components/CellText.tsx @@ -21,7 +21,11 @@ import type { CellTextProps } from '../types'; import { useDefinition } from '../../../hooks/useDefinition'; import { CellTextDefinition } from '../definition'; -/** @public */ +/** + * A table cell that renders a primary text title with an optional secondary description, leading icon, and link. + * + * @public + */ const CellText = (props: CellTextProps) => { const { ownProps, restProps } = useDefinition(CellTextDefinition, props); const { classes, title, description, color, leadingIcon, href } = ownProps; diff --git a/packages/ui/src/components/Table/components/Column.tsx b/packages/ui/src/components/Table/components/Column.tsx index 229ccf8a24..77fd997c0f 100644 --- a/packages/ui/src/components/Table/components/Column.tsx +++ b/packages/ui/src/components/Table/components/Column.tsx @@ -20,7 +20,11 @@ import { ColumnDefinition } from '../definition'; import { ColumnProps } from '../types'; import { RiArrowUpLine } from '@remixicon/react'; -/** @public */ +/** + * A table column header cell with an optional sort toggle and support for resizable widths. + * + * @public + */ export const Column = (props: ColumnProps) => { const { ownProps, restProps } = useDefinition(ColumnDefinition, props); const { classes, children } = ownProps; diff --git a/packages/ui/src/components/Table/components/Row.tsx b/packages/ui/src/components/Table/components/Row.tsx index e82bc23e6f..e293dc7b4e 100644 --- a/packages/ui/src/components/Table/components/Row.tsx +++ b/packages/ui/src/components/Table/components/Row.tsx @@ -28,7 +28,11 @@ import { isExternalLink } from '../../../utils/linkUtils'; import clsx from 'clsx'; import { Flex } from '../../Flex'; -/** @public */ +/** + * A table row that can optionally act as a navigation link or trigger an action when clicked, with analytics tracking. + * + * @public + */ export function Row(props: RowProps) { const { ownProps, restProps, dataAttributes, analytics } = useDefinition( RowDefinition, diff --git a/packages/ui/src/components/Table/components/Table.tsx b/packages/ui/src/components/Table/components/Table.tsx index 81b336afcc..dcd6d50813 100644 --- a/packages/ui/src/components/Table/components/Table.tsx +++ b/packages/ui/src/components/Table/components/Table.tsx @@ -98,7 +98,12 @@ function useLiveRegionLabel( return liveRegionLabel; } -/** @public */ +/** + * A full-featured data table with built-in pagination, sorting, row selection, loading and error states, and optional virtualization. + * Pair with `useTable` to manage data fetching and state, or pass `data` and `pagination` directly for manual control. + * + * @public + */ export function Table({ columnConfig, data, diff --git a/packages/ui/src/components/Table/components/TableBody.tsx b/packages/ui/src/components/Table/components/TableBody.tsx index eb59a8033e..cee109eb37 100644 --- a/packages/ui/src/components/Table/components/TableBody.tsx +++ b/packages/ui/src/components/Table/components/TableBody.tsx @@ -19,7 +19,11 @@ import { useDefinition } from '../../../hooks/useDefinition'; import { TableBodyDefinition } from '../definition'; import type { TableBodyProps } from '../types'; -/** @public */ +/** + * The body section of a table that renders data rows from the provided items collection. + * + * @public + */ export const TableBody = (props: TableBodyProps) => { const { ownProps, restProps } = useDefinition(TableBodyDefinition, props); diff --git a/packages/ui/src/components/Table/components/TableHeader.tsx b/packages/ui/src/components/Table/components/TableHeader.tsx index 572dd022d0..eea73bdf2a 100644 --- a/packages/ui/src/components/Table/components/TableHeader.tsx +++ b/packages/ui/src/components/Table/components/TableHeader.tsx @@ -26,7 +26,11 @@ import { TableHeaderDefinition } from '../definition'; import type { TableHeaderProps } from '../types'; import { Flex } from '../../Flex'; -/** @public */ +/** + * The header row of a table, rendering column labels and an optional select-all checkbox for toggle selection mode. + * + * @public + */ export const TableHeader = (props: TableHeaderProps) => { let { selectionBehavior, selectionMode } = useTableOptions(); diff --git a/packages/ui/src/components/Table/components/TableRoot.tsx b/packages/ui/src/components/Table/components/TableRoot.tsx index 223e9d648b..5ea21046df 100644 --- a/packages/ui/src/components/Table/components/TableRoot.tsx +++ b/packages/ui/src/components/Table/components/TableRoot.tsx @@ -19,7 +19,12 @@ import { TableDefinition } from '../definition'; import { Table as ReactAriaTable } from 'react-aria-components'; import { TableRootProps } from '../types'; -/** @public */ +/** + * The low-level table root element for building custom table layouts from atomic components. + * For most use cases, prefer the `Table` convenience wrapper. + * + * @public + */ export const TableRoot = (props: TableRootProps) => { const { ownProps, restProps, dataAttributes } = useDefinition( TableDefinition, diff --git a/packages/ui/src/components/Table/hooks/useTable.ts b/packages/ui/src/components/Table/hooks/useTable.ts index 5997416915..7d1019f541 100644 --- a/packages/ui/src/components/Table/hooks/useTable.ts +++ b/packages/ui/src/components/Table/hooks/useTable.ts @@ -121,7 +121,12 @@ function useTableProps( ); } -/** @public */ +/** + * A hook that manages table data fetching, pagination, sorting, search, and filtering. + * Supports three modes: `complete` for client-side data, `offset` for server-side offset pagination, and `cursor` for server-side cursor pagination. + * + * @public + */ export function useTable( options: UseTableCompleteOptions, ): UseTableResult; diff --git a/packages/ui/src/components/Table/stories/Table.docs.stories.tsx b/packages/ui/src/components/Table/stories/Table.docs.stories.tsx deleted file mode 100644 index aff656b083..0000000000 --- a/packages/ui/src/components/Table/stories/Table.docs.stories.tsx +++ /dev/null @@ -1,202 +0,0 @@ -/* eslint-disable no-restricted-syntax */ -/* - * Copyright 2025 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { useState } from 'react'; -import type { Meta, StoryObj } from '@storybook/react-vite'; -import { Table, CellText, CellProfile, useTable, type ColumnConfig } from '..'; -import { Flex } from '../../Flex'; -import { Text } from '../../Text'; -import { RadioGroup, Radio } from '../../RadioGroup'; -import { data as data4 } from './mocked-data4'; -import { selectionData, selectionColumns, tableStoriesMeta } from './utils'; - -const meta = { - title: 'Backstage UI/Table/docs', - ...tableStoriesMeta, -} satisfies Meta; - -export default meta; -type Story = StoryObj; -type Data4Item = (typeof data4)[0]; - -export const TableRockBand: Story = { - render: () => { - const columns: ColumnConfig[] = [ - { - id: 'name', - label: 'Band name', - isRowHeader: true, - defaultWidth: '4fr', - cell: item => ( - - ), - }, - { - id: 'genre', - label: 'Genre', - defaultWidth: '4fr', - cell: item => , - }, - { - id: 'yearFormed', - label: 'Year formed', - defaultWidth: '1fr', - cell: item => , - }, - { - id: 'albums', - label: 'Albums', - defaultWidth: '1fr', - cell: item => , - }, - ]; - - const { tableProps } = useTable({ - mode: 'complete', - getData: () => data4, - paginationOptions: { pageSize: 5 }, - }); - - return ; - }, -}; - -export const SelectionToggleWithActions: Story = { - render: () => { - const [selected, setSelected] = useState | 'all'>( - new Set(), - ); - - const { tableProps } = useTable({ - mode: 'complete', - getData: () => selectionData, - paginationOptions: { pageSize: 10 }, - }); - - return ( -
alert(`Clicked: ${item.name}`) }} - /> - ); - }, -}; - -export const SelectionModePlayground: Story = { - render: () => { - const [selectionMode, setSelectionMode] = useState<'single' | 'multiple'>( - 'multiple', - ); - const [selected, setSelected] = useState | 'all'>( - new Set(), - ); - - const { tableProps } = useTable({ - mode: 'complete', - getData: () => selectionData, - paginationOptions: { pageSize: 10 }, - }); - - return ( - -
-
- - Selection mode: - - { - setSelectionMode(value as 'single' | 'multiple'); - setSelected(new Set()); - }} - > - single - multiple - -
- - ); - }, -}; - -export const SelectionBehaviorPlayground: Story = { - render: () => { - const [selectionBehavior, setSelectionBehavior] = useState< - 'toggle' | 'replace' - >('toggle'); - const [selected, setSelected] = useState | 'all'>( - new Set(), - ); - - const { tableProps } = useTable({ - mode: 'complete', - getData: () => selectionData, - paginationOptions: { pageSize: 10 }, - }); - - return ( - -
-
- - Selection behavior: - - { - setSelectionBehavior(value as 'toggle' | 'replace'); - setSelected(new Set()); - }} - > - toggle - replace - -
- - ); - }, -}; diff --git a/packages/ui/src/components/Text/Text.tsx b/packages/ui/src/components/Text/Text.tsx index 0839b144c3..c2ed20e38c 100644 --- a/packages/ui/src/components/Text/Text.tsx +++ b/packages/ui/src/components/Text/Text.tsx @@ -47,7 +47,11 @@ function TextComponent( TextComponent.displayName = 'Text'; -/** @public */ +/** + * A typographic primitive that renders text with design system variants, weights, and colors, and can render as any HTML element. + * + * @public + */ export const Text = forwardRef(TextComponent) as { ( props: TextProps & { ref?: React.ComponentPropsWithRef['ref'] }, diff --git a/packages/ui/src/components/TextField/TextField.tsx b/packages/ui/src/components/TextField/TextField.tsx index acea45722f..a2318d3d08 100644 --- a/packages/ui/src/components/TextField/TextField.tsx +++ b/packages/ui/src/components/TextField/TextField.tsx @@ -22,7 +22,11 @@ import type { TextFieldProps } from './types'; import { useDefinition } from '../../hooks/useDefinition'; import { TextFieldDefinition } from './definition'; -/** @public */ +/** + * A single-line text input with an integrated label, optional icon, and inline error display. + * + * @public + */ export const TextField = forwardRef( (props, ref) => { const { ownProps, restProps, dataAttributes } = useDefinition( diff --git a/packages/ui/src/components/ToggleButton/ToggleButton.tsx b/packages/ui/src/components/ToggleButton/ToggleButton.tsx index c827b2f88e..430ced34a6 100644 --- a/packages/ui/src/components/ToggleButton/ToggleButton.tsx +++ b/packages/ui/src/components/ToggleButton/ToggleButton.tsx @@ -20,7 +20,11 @@ import type { ToggleButtonProps } from './types'; import { useDefinition } from '../../hooks/useDefinition'; import { ToggleButtonDefinition } from './definition'; -/** @public */ +/** + * A button that maintains a pressed or unpressed state, with optional start and end icon slots. + * + * @public + */ export const ToggleButton = forwardRef( (props: ToggleButtonProps, ref: Ref) => { const { ownProps, restProps, dataAttributes } = useDefinition( diff --git a/packages/ui/src/components/ToggleButtonGroup/ToggleButtonGroup.tsx b/packages/ui/src/components/ToggleButtonGroup/ToggleButtonGroup.tsx index 61c715b59d..e37c4da2d8 100644 --- a/packages/ui/src/components/ToggleButtonGroup/ToggleButtonGroup.tsx +++ b/packages/ui/src/components/ToggleButtonGroup/ToggleButtonGroup.tsx @@ -20,7 +20,11 @@ import type { ToggleButtonGroupProps } from './types'; import { useDefinition } from '../../hooks/useDefinition'; import { ToggleButtonGroupDefinition } from './definition'; -/** @public */ +/** + * A container that groups ToggleButton items and manages their collective selection state. + * + * @public + */ export const ToggleButtonGroup = forwardRef( (props: ToggleButtonGroupProps, ref: Ref) => { const { ownProps, restProps } = useDefinition( diff --git a/packages/ui/src/components/Tooltip/Tooltip.tsx b/packages/ui/src/components/Tooltip/Tooltip.tsx index 1d88fd0b41..2d5b42265a 100644 --- a/packages/ui/src/components/Tooltip/Tooltip.tsx +++ b/packages/ui/src/components/Tooltip/Tooltip.tsx @@ -28,14 +28,22 @@ import { TooltipDefinition } from './definition'; import { Box } from '../Box'; import { BgReset } from '../../hooks/useBg'; -/** @public */ +/** + * A wrapper that connects a trigger element to a Tooltip, controlling its show and hide behavior with a configurable delay. + * + * @public + */ export const TooltipTrigger = (props: TooltipTriggerComponentProps) => { const { delay = 600 } = props; return ; }; -/** @public */ +/** + * A floating label that provides contextual information about an element when it receives hover or focus. + * + * @public + */ export const Tooltip = forwardRef( (props, ref) => { const { ownProps, restProps } = useDefinition(TooltipDefinition, props); diff --git a/packages/ui/src/css/colors.stories.tsx b/packages/ui/src/css/colors.stories.tsx index faa85aceda..2d1a3614a7 100644 --- a/packages/ui/src/css/colors.stories.tsx +++ b/packages/ui/src/css/colors.stories.tsx @@ -20,6 +20,7 @@ import { Text } from '../components/Text'; const meta = preview.meta({ title: 'Backstage UI/Colors', + tags: ['!manifest'], }); export const Default = meta.story({ diff --git a/yarn.lock b/yarn.lock index 4cda789aed..aa6bd6204d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7946,7 +7946,7 @@ __metadata: "@types/react-dom": "npm:^18.0.0" "@types/use-sync-external-store": "npm:^0.0.6" clsx: "npm:^2.1.1" - eslint-plugin-storybook: "npm:^10.3.0-alpha.1" + eslint-plugin-storybook: "npm:^10.3.3" glob: "npm:^11.0.1" globals: "npm:^17.0.0" react: "npm:^18.0.2" @@ -7955,7 +7955,7 @@ __metadata: react-dom: "npm:^18.0.2" react-router-dom: "npm:^6.30.2" react-stately: "npm:^3.45.0" - storybook: "npm:^10.3.0-alpha.1" + storybook: "npm:^10.3.3" use-sync-external-store: "npm:^1.4.0" peerDependencies: "@types/react": ^18.0.0 @@ -19267,7 +19267,7 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-a11y@npm:^10.3.0-alpha.1": +"@storybook/addon-a11y@npm:^10.3.3": version: 10.3.3 resolution: "@storybook/addon-a11y@npm:10.3.3" dependencies: @@ -19279,7 +19279,7 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-docs@npm:^10.3.0-alpha.1": +"@storybook/addon-docs@npm:^10.3.3": version: 10.3.3 resolution: "@storybook/addon-docs@npm:10.3.3" dependencies: @@ -19296,7 +19296,7 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-links@npm:^10.3.0-alpha.1": +"@storybook/addon-links@npm:^10.3.3": version: 10.3.3 resolution: "@storybook/addon-links@npm:10.3.3" dependencies: @@ -19311,11 +19311,11 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-mcp@npm:^0.3.0": - version: 0.3.4 - resolution: "@storybook/addon-mcp@npm:0.3.4" +"@storybook/addon-mcp@npm:^0.4.2": + version: 0.4.2 + resolution: "@storybook/addon-mcp@npm:0.4.2" dependencies: - "@storybook/mcp": "npm:0.5.1" + "@storybook/mcp": "npm:0.6.1" "@tmcp/adapter-valibot": "npm:^0.1.4" "@tmcp/transport-http": "npm:^0.8.0" picoquery: "npm:^2.5.0" @@ -19327,11 +19327,11 @@ __metadata: peerDependenciesMeta: "@storybook/addon-vitest": optional: true - checksum: 10/0fb377d8ee96055703cfee48c4a47d25367a090b4e343a0d5db15341137463ae01f167725e7087bc20fe072b474a798a6488452a3afce1b27c47049e65e56177 + checksum: 10/42408a05ec7e9a7bfd5f168e7047b32a67d3a8cfcfaf68e74ddd820c9e188af006883e69173c4514f7845e29ebc58f6412be3c0ccab396590b3da4d1f3866bc6 languageName: node linkType: hard -"@storybook/addon-themes@npm:^10.3.0-alpha.1": +"@storybook/addon-themes@npm:^10.3.3": version: 10.3.3 resolution: "@storybook/addon-themes@npm:10.3.3" dependencies: @@ -19342,7 +19342,7 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-vitest@npm:^10.3.0-alpha.1": +"@storybook/addon-vitest@npm:^10.3.3": version: 10.3.3 resolution: "@storybook/addon-vitest@npm:10.3.3" dependencies: @@ -19421,15 +19421,15 @@ __metadata: languageName: node linkType: hard -"@storybook/mcp@npm:0.5.1": - version: 0.5.1 - resolution: "@storybook/mcp@npm:0.5.1" +"@storybook/mcp@npm:0.6.1": + version: 0.6.1 + resolution: "@storybook/mcp@npm:0.6.1" dependencies: "@tmcp/adapter-valibot": "npm:^0.1.4" "@tmcp/transport-http": "npm:^0.8.0" tmcp: "npm:^1.16.0" valibot: "npm:1.2.0" - checksum: 10/a5924be156db45ac86ee57c325878ee1a46cf3bf9523fc9f231d8f491e3a77f92d74801d138a4af6256cebcd74c8aa5985d0c9fded4fcbdbd4708ccdcc70ee31 + checksum: 10/0f61c6a3866197ee894eda99642896d83dbbcc946dce089b8b5f099b6de3ec5ec4ec0fc40e0a36fd9e5ef9ff0d3241a49e371aaf5d9ef75ef4f50fd46b5787f8 languageName: node linkType: hard @@ -19444,7 +19444,7 @@ __metadata: languageName: node linkType: hard -"@storybook/react-vite@npm:^10.3.0-alpha.1": +"@storybook/react-vite@npm:^10.3.3": version: 10.3.3 resolution: "@storybook/react-vite@npm:10.3.3" dependencies: @@ -30157,7 +30157,7 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-storybook@npm:^10.3.0-alpha.1": +"eslint-plugin-storybook@npm:^10.3.3": version: 10.3.3 resolution: "eslint-plugin-storybook@npm:10.3.3" dependencies: @@ -45112,13 +45112,13 @@ __metadata: "@octokit/rest": "npm:^19.0.3" "@playwright/test": "npm:^1.32.3" "@spotify/eslint-plugin": "npm:^15.0.0" - "@storybook/addon-a11y": "npm:^10.3.0-alpha.1" - "@storybook/addon-docs": "npm:^10.3.0-alpha.1" - "@storybook/addon-links": "npm:^10.3.0-alpha.1" - "@storybook/addon-mcp": "npm:^0.3.0" - "@storybook/addon-themes": "npm:^10.3.0-alpha.1" - "@storybook/addon-vitest": "npm:^10.3.0-alpha.1" - "@storybook/react-vite": "npm:^10.3.0-alpha.1" + "@storybook/addon-a11y": "npm:^10.3.3" + "@storybook/addon-docs": "npm:^10.3.3" + "@storybook/addon-links": "npm:^10.3.3" + "@storybook/addon-mcp": "npm:^0.4.2" + "@storybook/addon-themes": "npm:^10.3.3" + "@storybook/addon-vitest": "npm:^10.3.3" + "@storybook/react-vite": "npm:^10.3.3" "@techdocs/cli": "workspace:*" "@types/cacheable-request": "npm:^8.3.6" "@types/global-agent": "npm:^2.1.3" @@ -45151,7 +45151,7 @@ __metadata: shx: "npm:^0.4.0" sloc: "npm:^0.3.1" sort-package-json: "npm:^3.0.0" - storybook: "npm:^10.3.0-alpha.1" + storybook: "npm:^10.3.3" ts-morph: "npm:^24.0.0" typedoc: "npm:^0.28.0" typescript: "npm:~5.7.0" @@ -46617,7 +46617,7 @@ __metadata: languageName: node linkType: hard -"storybook@npm:^10.3.0-alpha.1": +"storybook@npm:^10.3.3": version: 10.3.3 resolution: "storybook@npm:10.3.3" dependencies: