Improve BUI manifest

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
Charles de Dreuille
2026-03-30 11:02:31 +01:00
parent 0336f92de8
commit dc009ed8a9
42 changed files with 274 additions and 288 deletions
+2 -3
View File
@@ -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
+8 -8
View File
@@ -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",
+2 -2
View File
@@ -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",
@@ -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<React.ElementRef<typeof RADisclosure>>) => {
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,
+5 -1
View File
@@ -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<HTMLDivElement, AvatarProps>((props, ref) => {
const { ownProps, restProps, dataAttributes } = useDefinition(
AvatarDefinition,
+5 -1
View File
@@ -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<HTMLDivElement, BoxProps>((props, ref) => {
const { ownProps, restProps, dataAttributes, utilityStyle } = useDefinition(
BoxDefinition,
@@ -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<HTMLButtonElement>) => {
const { ownProps, restProps, dataAttributes } = useDefinition(
@@ -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<HTMLAnchorElement>) => {
const { ownProps, restProps, dataAttributes, analytics } = useDefinition(
@@ -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<HTMLLabelElement, CheckboxProps>(
(props, ref) => {
const { ownProps, restProps, dataAttributes } = useDefinition(
@@ -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<HTMLDivElement, ContainerProps>(
(props, ref) => {
const { ownProps, restProps, utilityStyle } = useDefinition(
+25 -5
View File
@@ -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 <RADialogTrigger {...props} />;
};
/** @public */
/**
* A modal overlay that presents content requiring user interaction or acknowledgment, dismissible by clicking outside or pressing Escape.
*
* @public
*/
export const Dialog = forwardRef<React.ElementRef<typeof Modal>, DialogProps>(
(props, ref) => {
const { ownProps, restProps } = useDefinition(DialogDefinition, props, {
@@ -89,7 +97,11 @@ export const Dialog = forwardRef<React.ElementRef<typeof Modal>, 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<React.ElementRef<'div'>, DialogBodyProps>(
(props, ref) => {
const { ownProps, restProps } = useDefinition(DialogBodyDefinition, props);
@@ -126,7 +142,11 @@ export const DialogBody = forwardRef<React.ElementRef<'div'>, 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
@@ -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<HTMLDivElement, FieldErrorProps>(
(props: FieldErrorProps, ref) => {
const { ownProps, restProps } = useDefinition(FieldErrorDefinition, props);
@@ -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<HTMLDivElement, FieldLabelProps>(
(props: FieldLabelProps, ref) => {
const { ownProps, restProps } = useDefinition(FieldLabelDefinition, props);
+5 -1
View File
@@ -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<HTMLDivElement, FlexProps>((props, ref) => {
const { ownProps, dataAttributes, utilityStyle, restProps } = useDefinition(
FlexDefinition,
+5 -1
View File
@@ -61,7 +61,11 @@ const LinkInternal = forwardRef<HTMLAnchorElement, LinkProps>((props, ref) => {
LinkInternal.displayName = 'LinkInternal';
/** @public */
/**
* A styled anchor element that supports analytics event tracking on click.
*
* @public
*/
export const Link = forwardRef<HTMLAnchorElement, LinkProps>((props, ref) => {
return <LinkInternal {...props} ref={ref} />;
});
+5 -1
View File
@@ -76,7 +76,11 @@ const MenuEmptyState = () => {
return <div className={ownProps.classes.root}>No results found.</div>;
};
/** @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 <RAMenuTrigger {...props} />;
};
@@ -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<HTMLDivElement, PasswordFieldProps>(
(props, ref) => {
const { ownProps, restProps, dataAttributes } = useDefinition(
@@ -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<HTMLDivElement, RadioGroupProps>(
(props, ref) => {
const { ownProps, restProps } = useDefinition(RadioGroupDefinition, props);
@@ -70,7 +74,11 @@ export const RadioGroup = forwardRef<HTMLDivElement, RadioGroupProps>(
RadioGroup.displayName = 'RadioGroup';
/** @public */
/**
* A single radio button for use within a RadioGroup.
*
* @public
*/
export const Radio = forwardRef<HTMLLabelElement, RadioProps>((props, ref) => {
const { ownProps, restProps } = useDefinition(RadioDefinition, props);
@@ -47,7 +47,11 @@ const SearchAutocompleteEmptyState = () => {
return <div className={ownProps.classes.emptyState}>No results found.</div>;
};
/** @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,
@@ -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<HTMLDivElement, SearchFieldProps>(
(props, ref) => {
const { ownProps, restProps, dataAttributes } = useDefinition(
+5 -1
View File
@@ -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'>
@@ -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,
+5 -1
View File
@@ -115,7 +115,11 @@ function SliderImpl<T extends number | number[]>(
);
}
/** @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 (<T extends number | number[]>(
props: SliderProps<T> & { ref?: React.ForwardedRef<HTMLDivElement> },
) => JSX.Element) & { displayName: string };
+5 -1
View File
@@ -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<HTMLLabelElement, SwitchProps>(
(props, ref) => {
const { ownProps, restProps } = useDefinition(SwitchDefinition, props);
@@ -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);
@@ -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;
@@ -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;
@@ -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;
@@ -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<T extends object>(props: RowProps<T>) {
const { ownProps, restProps, dataAttributes, analytics } = useDefinition(
RowDefinition,
@@ -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<T extends TableItem>({
columnConfig,
data,
@@ -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 = <T extends object>(props: TableBodyProps<T>) => {
const { ownProps, restProps } = useDefinition(TableBodyDefinition, props);
@@ -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 = <T extends object>(props: TableHeaderProps<T>) => {
let { selectionBehavior, selectionMode } = useTableOptions();
@@ -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,
@@ -121,7 +121,12 @@ function useTableProps<T extends TableItem>(
);
}
/** @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<T extends TableItem, TFilter = unknown>(
options: UseTableCompleteOptions<T, TFilter>,
): UseTableResult<T, TFilter>;
@@ -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<typeof meta>;
type Data4Item = (typeof data4)[0];
export const TableRockBand: Story = {
render: () => {
const columns: ColumnConfig<Data4Item>[] = [
{
id: 'name',
label: 'Band name',
isRowHeader: true,
defaultWidth: '4fr',
cell: item => (
<CellProfile name={item.name} src={item.image} href={item.website} />
),
},
{
id: 'genre',
label: 'Genre',
defaultWidth: '4fr',
cell: item => <CellText title={item.genre} />,
},
{
id: 'yearFormed',
label: 'Year formed',
defaultWidth: '1fr',
cell: item => <CellText title={item.yearFormed.toString()} />,
},
{
id: 'albums',
label: 'Albums',
defaultWidth: '1fr',
cell: item => <CellText title={item.albums.toString()} />,
},
];
const { tableProps } = useTable({
mode: 'complete',
getData: () => data4,
paginationOptions: { pageSize: 5 },
});
return <Table columnConfig={columns} {...tableProps} />;
},
};
export const SelectionToggleWithActions: Story = {
render: () => {
const [selected, setSelected] = useState<Set<string | number> | 'all'>(
new Set(),
);
const { tableProps } = useTable({
mode: 'complete',
getData: () => selectionData,
paginationOptions: { pageSize: 10 },
});
return (
<Table
{...tableProps}
columnConfig={selectionColumns}
selection={{
mode: 'multiple',
behavior: 'toggle',
selected,
onSelectionChange: setSelected,
}}
rowConfig={{ onClick: item => alert(`Clicked: ${item.name}`) }}
/>
);
},
};
export const SelectionModePlayground: Story = {
render: () => {
const [selectionMode, setSelectionMode] = useState<'single' | 'multiple'>(
'multiple',
);
const [selected, setSelected] = useState<Set<string | number> | 'all'>(
new Set(),
);
const { tableProps } = useTable({
mode: 'complete',
getData: () => selectionData,
paginationOptions: { pageSize: 10 },
});
return (
<Flex direction="column" gap="8">
<Table
{...tableProps}
columnConfig={selectionColumns}
selection={{
mode: selectionMode,
behavior: 'toggle',
selected,
onSelectionChange: setSelected,
}}
/>
<div>
<Text as="h4" style={{ marginBottom: 'var(--bui-space-2)' }}>
Selection mode:
</Text>
<RadioGroup
aria-label="Selection mode"
orientation="horizontal"
value={selectionMode}
onChange={value => {
setSelectionMode(value as 'single' | 'multiple');
setSelected(new Set());
}}
>
<Radio value="single">single</Radio>
<Radio value="multiple">multiple</Radio>
</RadioGroup>
</div>
</Flex>
);
},
};
export const SelectionBehaviorPlayground: Story = {
render: () => {
const [selectionBehavior, setSelectionBehavior] = useState<
'toggle' | 'replace'
>('toggle');
const [selected, setSelected] = useState<Set<string | number> | 'all'>(
new Set(),
);
const { tableProps } = useTable({
mode: 'complete',
getData: () => selectionData,
paginationOptions: { pageSize: 10 },
});
return (
<Flex direction="column" gap="8">
<Table
{...tableProps}
columnConfig={selectionColumns}
selection={{
mode: 'multiple',
behavior: selectionBehavior,
selected,
onSelectionChange: setSelected,
}}
/>
<div>
<Text as="h4" style={{ marginBottom: 'var(--bui-space-2)' }}>
Selection behavior:
</Text>
<RadioGroup
aria-label="Selection behavior"
orientation="horizontal"
value={selectionBehavior}
onChange={value => {
setSelectionBehavior(value as 'toggle' | 'replace');
setSelected(new Set());
}}
>
<Radio value="toggle">toggle</Radio>
<Radio value="replace">replace</Radio>
</RadioGroup>
</div>
</Flex>
);
},
};
+5 -1
View File
@@ -47,7 +47,11 @@ function TextComponent<T extends ElementType = 'span'>(
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 {
<T extends ElementType = 'p'>(
props: TextProps<T> & { ref?: React.ComponentPropsWithRef<T>['ref'] },
@@ -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<HTMLDivElement, TextFieldProps>(
(props, ref) => {
const { ownProps, restProps, dataAttributes } = useDefinition(
@@ -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<HTMLButtonElement>) => {
const { ownProps, restProps, dataAttributes } = useDefinition(
@@ -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<HTMLDivElement>) => {
const { ownProps, restProps } = useDefinition(
+10 -2
View File
@@ -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 <AriaTooltipTrigger delay={delay} {...props} />;
};
/** @public */
/**
* A floating label that provides contextual information about an element when it receives hover or focus.
*
* @public
*/
export const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
(props, ref) => {
const { ownProps, restProps } = useDefinition(TooltipDefinition, props);
+1
View File
@@ -20,6 +20,7 @@ import { Text } from '../components/Text';
const meta = preview.meta({
title: 'Backstage UI/Colors',
tags: ['!manifest'],
});
export const Default = meta.story({
+27 -27
View File
@@ -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: