` element.
+
+
## Examples
### With Subtitle
-Here's a view when using both title and subtitle props.
+Accordions can display a subtitle below the title.
}
code={withSubtitleSnippet}
+ layout="side-by-side"
/>
### Custom Trigger
-Here's a view when providing custom multi-line content as children.
+Pass custom content as children instead of using the title prop.
}
code={customTriggerSnippet}
/>
### Default Expanded
-Here's a view when the panel is expanded by default.
-
}
code={defaultExpandedSnippet}
+ layout="side-by-side"
/>
### Group Single Open
-Here's a view when only one accordion can be open at a time.
+Use `AccordionGroup` to allow only one accordion open at a time.
= {
children: {
type: 'enum',
values: ['ReactNode', '(state: { isExpanded: boolean }) => ReactNode'],
+ description:
+ 'Content of the accordion. Can be a render function to access expanded state.',
},
defaultExpanded: {
type: 'boolean',
default: 'false',
+ description: 'Whether the accordion is expanded on initial render.',
},
isExpanded: {
type: 'boolean',
+ description: 'Controls the expanded state (controlled mode).',
},
onExpandedChange: {
type: 'enum',
values: ['(isExpanded: boolean) => void'],
+ description: 'Called when the expanded state changes.',
},
...classNamePropDefs,
...stylePropDefs,
@@ -29,22 +34,33 @@ export const accordionTriggerPropDefs: Record = {
type: 'enum',
values: ['1', '2', '3', '4', '5', '6'],
default: '3',
+ description:
+ 'Heading level for accessibility (renders h1-h6). Match your page hierarchy.',
},
title: {
type: 'string',
+ description: 'Primary text displayed in the trigger.',
},
subtitle: {
type: 'string',
+ description: 'Secondary text displayed next to the title.',
},
children: {
type: 'enum',
values: ['ReactNode'],
+ description:
+ 'Custom trigger content. When provided, title and subtitle are ignored.',
},
...classNamePropDefs,
...stylePropDefs,
};
export const accordionPanelPropDefs: Record = {
+ children: {
+ type: 'enum',
+ values: ['ReactNode'],
+ description: 'Content displayed when the accordion is expanded.',
+ },
...classNamePropDefs,
...stylePropDefs,
};
@@ -53,6 +69,13 @@ export const accordionGroupPropDefs: Record = {
allowsMultiple: {
type: 'boolean',
default: 'false',
+ description:
+ 'Whether multiple accordions can be expanded at the same time.',
+ },
+ children: {
+ type: 'enum',
+ values: ['ReactNode'],
+ description: 'Accordion components to group together.',
},
...classNamePropDefs,
...stylePropDefs,
diff --git a/docs-ui/src/app/components/avatar/page.mdx b/docs-ui/src/app/components/avatar/page.mdx
index 0074eda5b8..7020eb90c4 100644
--- a/docs-ui/src/app/components/avatar/page.mdx
+++ b/docs-ui/src/app/components/avatar/page.mdx
@@ -17,7 +17,7 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
} code={defaultSnippet} />
@@ -30,13 +30,22 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
+Avatar also accepts all standard HTML div attributes (`onClick`, `onMouseEnter`, etc.) since it extends `React.ComponentPropsWithoutRef<'div'>`.
+
## Examples
### Sizes
Avatar sizes can be set using the `size` prop.
-} code={sizesSnippet} />
+}
+ code={sizesSnippet}
+ layout="side-by-side"
+/>
### Fallback
@@ -48,6 +57,7 @@ If the image is not available, the avatar will show the initials of the name.
open
preview={}
code={fallbackSnippet}
+ layout="side-by-side"
/>
### The `purpose` prop
diff --git a/docs-ui/src/app/components/avatar/props-definition.ts b/docs-ui/src/app/components/avatar/props-definition.ts
deleted file mode 100644
index 1a6de676c1..0000000000
--- a/docs-ui/src/app/components/avatar/props-definition.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import {
- classNamePropDefs,
- stylePropDefs,
- type PropDef,
-} from '@/utils/propDefs';
-
-export const avatarPropDefs: Record = {
- src: {
- type: 'string',
- },
- name: {
- type: 'string',
- required: true,
- },
- size: {
- type: 'enum',
- values: ['x-small', 'small', 'medium', 'large', 'x-large'],
- default: 'medium',
- responsive: true,
- },
- purpose: {
- type: 'enum',
- values: ['informative', 'decoration'],
- default: 'informative',
- },
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/docs-ui/src/app/components/avatar/props-definition.tsx b/docs-ui/src/app/components/avatar/props-definition.tsx
new file mode 100644
index 0000000000..4b56b94be5
--- /dev/null
+++ b/docs-ui/src/app/components/avatar/props-definition.tsx
@@ -0,0 +1,41 @@
+import {
+ classNamePropDefs,
+ stylePropDefs,
+ type PropDef,
+} from '@/utils/propDefs';
+import { Chip } from '@/components/Chip';
+
+export const avatarPropDefs: Record = {
+ src: {
+ type: 'string',
+ description:
+ 'URL of the image to display. Pass an empty string to show initials fallback. Falls back to initials if the image fails to load.',
+ },
+ name: {
+ type: 'string',
+ required: true,
+ description:
+ 'Name of the person. Used for generating initials fallback and accessibility label.',
+ },
+ size: {
+ type: 'enum',
+ values: ['x-small', 'small', 'medium', 'large', 'x-large'],
+ default: 'medium',
+ responsive: true,
+ description:
+ 'Visual size. Smaller sizes show 1 initial, larger sizes show 2.',
+ },
+ purpose: {
+ type: 'enum',
+ values: ['informative', 'decoration'],
+ default: 'informative',
+ description: (
+ <>
+ Accessibility behavior. Use decoration when name appears in
+ adjacent text.
+ >
+ ),
+ },
+ ...classNamePropDefs,
+ ...stylePropDefs,
+};
diff --git a/docs-ui/src/app/components/avatar/snippets.ts b/docs-ui/src/app/components/avatar/snippets.ts
index ba3254dcd9..2deb8037a6 100644
--- a/docs-ui/src/app/components/avatar/snippets.ts
+++ b/docs-ui/src/app/components/avatar/snippets.ts
@@ -1,6 +1,6 @@
export const avatarUsageSnippet = `import { Avatar } from '@backstage/ui';
-`;
+`;
export const defaultSnippet = `
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
`;
diff --git a/docs-ui/src/app/components/box/components.tsx b/docs-ui/src/app/components/box/components.tsx
index 466ecd3708..e197ce2eb8 100644
--- a/docs-ui/src/app/components/box/components.tsx
+++ b/docs-ui/src/app/components/box/components.tsx
@@ -1,34 +1,42 @@
'use client';
import { Box } from '../../../../../packages/ui/src/components/Box/Box';
-
-const diagonalStripePattern = (() => {
- const svg = `
-
- `.trim();
- return `data:image/svg+xml,${encodeURIComponent(svg)}`;
-})();
+import { Flex } from '../../../../../packages/ui/src/components/Flex/Flex';
export const Default = () => {
return (
-
+
+ Hello World
+
+ );
+};
+
+export const Surface = () => {
+ return (
+
+
+ Surface 0
+
+
+ Surface 1
+
+
+ Surface 2
+
+
+ Surface 3
+
+
+ );
+};
+
+export const Responsive = () => {
+ return (
+
+ Hello World
+
);
};
diff --git a/docs-ui/src/app/components/box/page.mdx b/docs-ui/src/app/components/box/page.mdx
index cc8641580f..f825ddfdbf 100644
--- a/docs-ui/src/app/components/box/page.mdx
+++ b/docs-ui/src/app/components/box/page.mdx
@@ -5,10 +5,10 @@ import { boxPropDefs } from './props-definition';
import {
snippetUsage,
defaultSnippet,
- boxSimpleSnippet,
+ boxSurfaceSnippet,
boxResponsiveSnippet,
} from './snippets';
-import { Default } from './components';
+import { Default, Surface, Responsive } from './components';
import { spacingPropDefs } from '@/utils/propDefs';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
@@ -18,10 +18,10 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
-} code={defaultSnippet} align="center" />
+} code={defaultSnippet} align="center" />
## Usage
@@ -29,11 +29,6 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
## API reference
-### Box
-
-This is the Box component, our lowest-level component. Here are all the
-available properties.
-
Padding and margin are used to create space around your component using our
@@ -44,17 +39,21 @@ avoid collapsing margins but both are available.
## Examples
-### Simple example
+### Surface
-A simple example of how to use the Box component.
+Here's a view when boxes have different surface levels.
-
+} code={boxSurfaceSnippet} layout="side-by-side" />
-### Responsive
+### Responsive props
-Here's a view when buttons are responsive.
+Props can accept breakpoint objects for responsive behavior.
-
+}
+ code={boxResponsiveSnippet}
+ layout="side-by-side"
+/>
diff --git a/docs-ui/src/app/components/box/props-definition.ts b/docs-ui/src/app/components/box/props-definition.ts
deleted file mode 100644
index a18a0f272e..0000000000
--- a/docs-ui/src/app/components/box/props-definition.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import {
- classNamePropDefs,
- displayPropDefs,
- heightPropDefs,
- positionPropDefs,
- stylePropDefs,
- widthPropDefs,
- type PropDef,
-} from '@/utils/propDefs';
-
-export const boxPropDefs: Record = {
- as: {
- type: 'enum',
- values: ['div', 'span'],
- default: 'div',
- responsive: true,
- },
- ...widthPropDefs,
- ...heightPropDefs,
- ...positionPropDefs,
- ...displayPropDefs,
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/docs-ui/src/app/components/box/props-definition.tsx b/docs-ui/src/app/components/box/props-definition.tsx
new file mode 100644
index 0000000000..168db2f445
--- /dev/null
+++ b/docs-ui/src/app/components/box/props-definition.tsx
@@ -0,0 +1,46 @@
+import {
+ classNamePropDefs,
+ heightPropDefs,
+ positionPropDefs,
+ stylePropDefs,
+ widthPropDefs,
+ type PropDef,
+} from '@/utils/propDefs';
+import { Chip } from '@/components/Chip';
+
+export const boxPropDefs: Record = {
+ as: {
+ type: 'string',
+ default: 'div',
+ description:
+ 'HTML element to render. Accepts any valid HTML tag (div, span, section, etc.).',
+ },
+ surface: {
+ type: 'enum',
+ values: ['0', '1', '2', '3', 'danger', 'warning', 'success', 'auto'],
+ responsive: true,
+ description:
+ 'Background surface level for visual hierarchy. Higher numbers create elevation.',
+ },
+ children: {
+ type: 'enum',
+ values: ['ReactNode'],
+ description: 'Content to render inside the box.',
+ },
+ ...widthPropDefs,
+ ...heightPropDefs,
+ ...positionPropDefs,
+ display: {
+ type: 'enum',
+ values: ['none', 'flex', 'block', 'inline'],
+ responsive: true,
+ description: (
+ <>
+ Controls layout behavior. Use flex for flexbox layouts,{' '}
+ none to hide.
+ >
+ ),
+ },
+ ...classNamePropDefs,
+ ...stylePropDefs,
+};
diff --git a/docs-ui/src/app/components/box/snippets.ts b/docs-ui/src/app/components/box/snippets.ts
index 5c19bbe59b..5da9dbc34c 100644
--- a/docs-ui/src/app/components/box/snippets.ts
+++ b/docs-ui/src/app/components/box/snippets.ts
@@ -1,21 +1,23 @@
export const snippetUsage = `import { Box } from '@backstage/ui';
-`;
+
+ Content with padding and background
+`;
-export const defaultSnippet = ``;
-
-export const boxSimpleSnippet = `Hello World`;
-
-export const boxResponsiveSnippet = `
+export const defaultSnippet = `
+ Hello World
+`;
+
+export const boxSurfaceSnippet = `
+ Surface 0
+ Surface 1
+ Surface 2
+ Surface 3
+`;
+
+export const boxResponsiveSnippet = `
Hello World
`;
diff --git a/docs-ui/src/app/components/button-icon/page.mdx b/docs-ui/src/app/components/button-icon/page.mdx
index 776b016671..51ff0ea5a0 100644
--- a/docs-ui/src/app/components/button-icon/page.mdx
+++ b/docs-ui/src/app/components/button-icon/page.mdx
@@ -14,10 +14,15 @@ import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ButtonIconDefinition } from '../../../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
+import { ReactAriaLink } from '@/components/ReactAriaLink';
+
+export const reactAriaUrls = {
+ button: 'https://react-aria.adobe.com/Button',
+};
} code={variantsSnippet} />
@@ -30,12 +35,12 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
+
+
## Examples
### Variants
-Here's a view when buttons have different variants.
-
} code={sizesSnippet} />
### Disabled
-Here's a view when buttons are disabled.
-
= {
- variant: {
- type: 'enum',
- values: ['primary', 'secondary', 'tertiary'],
- default: 'primary',
- responsive: true,
- },
- size: {
- type: 'enum',
- values: ['small', 'medium'],
- default: 'medium',
- responsive: true,
- },
- icon: { type: 'enum', values: ['ReactNode'], responsive: false },
- isDisabled: { type: 'boolean', default: 'false', responsive: false },
- loading: { type: 'boolean', default: 'false', responsive: false },
- type: {
- type: 'enum',
- values: ['button', 'submit', 'reset'],
- default: 'button',
- responsive: false,
- },
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/docs-ui/src/app/components/button-icon/props-definition.tsx b/docs-ui/src/app/components/button-icon/props-definition.tsx
new file mode 100644
index 0000000000..05917cc2b4
--- /dev/null
+++ b/docs-ui/src/app/components/button-icon/props-definition.tsx
@@ -0,0 +1,64 @@
+import {
+ classNamePropDefs,
+ stylePropDefs,
+ type PropDef,
+} from '@/utils/propDefs';
+import { Chip } from '@/components/Chip';
+
+export const buttonIconPropDefs: Record = {
+ variant: {
+ type: 'enum',
+ values: ['primary', 'secondary', 'tertiary'],
+ default: 'primary',
+ responsive: true,
+ description: (
+ <>
+ Visual style. Use primary for main actions,{' '}
+ secondary for alternatives, tertiary for
+ low-emphasis.
+ >
+ ),
+ },
+ size: {
+ type: 'enum',
+ values: ['small', 'medium'],
+ default: 'small',
+ responsive: true,
+ description: (
+ <>
+ Button size. Use small for toolbars, medium{' '}
+ for standalone actions.
+ >
+ ),
+ },
+ icon: {
+ type: 'enum',
+ values: ['ReactElement'],
+ description:
+ 'Icon element to display. Required for accessibility via aria-label.',
+ },
+ isDisabled: {
+ type: 'boolean',
+ default: 'false',
+ description: 'Prevents interaction and applies disabled styling.',
+ },
+ loading: {
+ type: 'boolean',
+ default: 'false',
+ description: 'Shows a spinner and disables the button.',
+ },
+ type: {
+ type: 'enum',
+ values: ['button', 'submit', 'reset'],
+ default: 'button',
+ description: 'HTML button type attribute.',
+ },
+ onSurface: {
+ type: 'enum',
+ values: ['0', '1', '2', '3', 'danger', 'warning', 'success', 'auto'],
+ responsive: true,
+ description: 'Surface context for correct color contrast.',
+ },
+ ...classNamePropDefs,
+ ...stylePropDefs,
+};
diff --git a/docs-ui/src/app/components/button-icon/snippets.ts b/docs-ui/src/app/components/button-icon/snippets.ts
index 1ea9acc169..b1ab27b77b 100644
--- a/docs-ui/src/app/components/button-icon/snippets.ts
+++ b/docs-ui/src/app/components/button-icon/snippets.ts
@@ -1,6 +1,7 @@
export const buttonIconUsageSnippet = `import { ButtonIcon } from '@backstage/ui';
+import { RiCloseLine } from '@remixicon/react';
-`;
+} aria-label="Close" />`;
export const variantsSnippet = `
} variant="primary" aria-label="Cloud" />
diff --git a/docs-ui/src/app/components/button-link/page.mdx b/docs-ui/src/app/components/button-link/page.mdx
index 2da3177c55..35e4de68c5 100644
--- a/docs-ui/src/app/components/button-link/page.mdx
+++ b/docs-ui/src/app/components/button-link/page.mdx
@@ -14,10 +14,15 @@ import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ButtonLinkDefinition } from '../../../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
+import { ReactAriaLink } from '@/components/ReactAriaLink';
+
+export const reactAriaUrls = {
+ link: 'https://react-aria.adobe.com/Link',
+};
} code={variantsSnippet} />
@@ -30,30 +35,16 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
+
+
## Examples
-### Variants
-
-Here's a view when buttons have different variants.
-
-}
- code={variantsSnippet}
-/>
-
### Sizes
-Here's a view when buttons have different sizes.
-
} code={sizesSnippet} />
### With Icons
-Here's a view when buttons have icons.
-
= {
- variant: {
- type: 'enum',
- values: ['primary', 'secondary', 'tertiary'],
- default: 'primary',
- responsive: true,
- },
- size: {
- type: 'enum',
- values: ['small', 'medium'],
- default: 'medium',
- responsive: true,
- },
- iconStart: { type: 'enum', values: ['ReactNode'], responsive: false },
- iconEnd: { type: 'enum', values: ['ReactNode'], responsive: false },
- isDisabled: { type: 'boolean', default: 'false', responsive: false },
- href: { type: 'string', required: true },
- target: {
- type: 'enum',
- values: ['_self', '_blank', '_parent', '_top'],
- },
- ...childrenPropDefs,
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/docs-ui/src/app/components/button-link/props-definition.tsx b/docs-ui/src/app/components/button-link/props-definition.tsx
new file mode 100644
index 0000000000..deae5364df
--- /dev/null
+++ b/docs-ui/src/app/components/button-link/props-definition.tsx
@@ -0,0 +1,74 @@
+import {
+ classNamePropDefs,
+ stylePropDefs,
+ childrenPropDefs,
+ type PropDef,
+} from '@/utils/propDefs';
+import { Chip } from '@/components/Chip';
+
+export const buttonLinkPropDefs: Record = {
+ variant: {
+ type: 'enum',
+ values: ['primary', 'secondary', 'tertiary'],
+ default: 'primary',
+ responsive: true,
+ description: (
+ <>
+ Visual style. Use primary for main actions,{' '}
+ secondary for alternatives, tertiary for
+ low-emphasis.
+ >
+ ),
+ },
+ size: {
+ type: 'enum',
+ values: ['small', 'medium'],
+ default: 'small',
+ responsive: true,
+ description: (
+ <>
+ Link size. Use small for inline contexts,{' '}
+ medium for standalone.
+ >
+ ),
+ },
+ iconStart: {
+ type: 'enum',
+ values: ['ReactElement'],
+ description: 'Icon displayed before the link text.',
+ },
+ iconEnd: {
+ type: 'enum',
+ values: ['ReactElement'],
+ description: 'Icon displayed after the link text.',
+ },
+ isDisabled: {
+ type: 'boolean',
+ default: 'false',
+ description: 'Prevents interaction and applies disabled styling.',
+ },
+ href: {
+ type: 'string',
+ required: true,
+ description: 'URL the link navigates to.',
+ },
+ target: {
+ type: 'enum',
+ values: ['_self', '_blank', '_parent', '_top'],
+ description: (
+ <>
+ Where to open the linked URL. Use _blank for external
+ links.
+ >
+ ),
+ },
+ onSurface: {
+ type: 'enum',
+ values: ['0', '1', '2', '3', 'danger', 'warning', 'success', 'auto'],
+ responsive: true,
+ description: 'Surface context for correct color contrast.',
+ },
+ ...childrenPropDefs,
+ ...classNamePropDefs,
+ ...stylePropDefs,
+};
diff --git a/docs-ui/src/app/components/button/page.mdx b/docs-ui/src/app/components/button/page.mdx
index 38ff2e2d57..d56420ecf7 100644
--- a/docs-ui/src/app/components/button/page.mdx
+++ b/docs-ui/src/app/components/button/page.mdx
@@ -16,6 +16,7 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ButtonDefinition } from '../../../utils/definitions';
+import { ReactAriaLink } from '@/components/ReactAriaLink';
import {
Variants,
Sizes,
@@ -25,9 +26,13 @@ import {
AsLink,
} from './components';
+export const reactAriaUrls = {
+ button: 'https://react-spectrum.adobe.com/react-aria/Button.html',
+};
+
} code={variantsSnippet} />
@@ -40,29 +45,35 @@ import {
+
+
## Examples
### Variants
-Here's a view when buttons have different variants.
-
}
code={variantsSnippet}
+ layout="side-by-side"
/>
### Sizes
-Here's a view when buttons have different sizes.
-
-} code={sizesSnippet} />
+}
+ code={sizesSnippet}
+ layout="side-by-side"
+/>
### With Icons
-Here's a view when buttons have icons.
+Icons can appear before or after the label.
}
code={withIconsSnippet}
+ layout="side-by-side"
/>
### Disabled
-Here's a view when buttons are disabled.
-
}
code={disabledSnippet}
+ layout="side-by-side"
/>
### Loading
-Here's a view when buttons are in a loading state.
+Shows a spinner and disables interaction during async operations.
}
code={loadingSnippet}
+ layout="side-by-side"
/>
### Responsive
-Here's a view when buttons are responsive.
+Button props accept responsive breakpoint objects.
@@ -106,7 +118,14 @@ Here's a view when buttons are responsive.
If you want to use a button as a link, please use the `ButtonLink` component.
-} code={asLinkSnippet} />
+}
+ code={asLinkSnippet}
+ layout="side-by-side"
+/>
diff --git a/docs-ui/src/app/components/button/props-definition.ts b/docs-ui/src/app/components/button/props-definition.ts
deleted file mode 100644
index 4b225ad460..0000000000
--- a/docs-ui/src/app/components/button/props-definition.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { classNamePropDefs, stylePropDefs } from '@/utils/propDefs';
-import type { PropDef } from '@/utils/propDefs';
-
-export const buttonPropDefs: Record = {
- variant: {
- type: 'enum',
- values: ['primary', 'secondary'],
- default: 'primary',
- responsive: true,
- },
- size: {
- type: 'enum',
- values: ['small', 'medium'],
- default: 'medium',
- responsive: true,
- },
- iconStart: { type: 'enum', values: ['ReactNode'], responsive: false },
- iconEnd: { type: 'enum', values: ['ReactNode'], responsive: false },
- isDisabled: { type: 'boolean', default: 'false', responsive: false },
- loading: { type: 'boolean', default: 'false', responsive: false },
- children: { type: 'enum', values: ['ReactNode'], responsive: false },
- type: {
- type: 'enum',
- values: ['button', 'submit', 'reset'],
- default: 'button',
- responsive: false,
- },
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/docs-ui/src/app/components/button/props-definition.tsx b/docs-ui/src/app/components/button/props-definition.tsx
new file mode 100644
index 0000000000..fffd2b2b61
--- /dev/null
+++ b/docs-ui/src/app/components/button/props-definition.tsx
@@ -0,0 +1,69 @@
+import { classNamePropDefs, stylePropDefs } from '@/utils/propDefs';
+import type { PropDef } from '@/utils/propDefs';
+import { Chip } from '@/components/Chip';
+
+export const buttonPropDefs: Record = {
+ variant: {
+ type: 'enum',
+ values: ['primary', 'secondary', 'tertiary'],
+ default: 'primary',
+ responsive: true,
+ description: (
+ <>
+ Visual style. Use primary for main actions,{' '}
+ secondary for alternatives, tertiary for
+ low-emphasis.
+ >
+ ),
+ },
+ size: {
+ type: 'enum',
+ values: ['small', 'medium'],
+ default: 'small',
+ responsive: true,
+ description: (
+ <>
+ Button size. Use small for dense layouts.
+ >
+ ),
+ },
+ iconStart: {
+ type: 'enum',
+ values: ['ReactElement'],
+ description: 'Icon displayed before the button text.',
+ },
+ iconEnd: {
+ type: 'enum',
+ values: ['ReactElement'],
+ description: 'Icon displayed after the button text.',
+ },
+ isDisabled: {
+ type: 'boolean',
+ default: 'false',
+ description: 'Prevents interaction and applies disabled styling.',
+ },
+ loading: {
+ type: 'boolean',
+ default: 'false',
+ description: 'Shows a spinner and disables the button.',
+ },
+ children: {
+ type: 'enum',
+ values: ['ReactNode'],
+ description: 'Button label text or content.',
+ },
+ type: {
+ type: 'enum',
+ values: ['button', 'submit', 'reset'],
+ default: 'button',
+ description: 'HTML button type attribute.',
+ },
+ onSurface: {
+ type: 'enum',
+ values: ['0', '1', '2', '3', 'danger', 'warning', 'success', 'auto'],
+ responsive: true,
+ description: 'Surface context for correct color contrast.',
+ },
+ ...classNamePropDefs,
+ ...stylePropDefs,
+};
diff --git a/docs-ui/src/app/components/button/snippets.ts b/docs-ui/src/app/components/button/snippets.ts
index a31523b064..465b7e6cf1 100644
--- a/docs-ui/src/app/components/button/snippets.ts
+++ b/docs-ui/src/app/components/button/snippets.ts
@@ -1,54 +1,54 @@
export const buttonSnippetUsage = `import { Button } from '@backstage/ui';
-`;
+`;
export const buttonResponsiveSnippet = ``;
export const variantsSnippet = `
- }>
- Button
-
- }>
- Button
-
- }>
- Button
-
- `;
+ }>
+ Button
+
+ }>
+ Button
+
+ }>
+ Button
+
+`;
export const sizesSnippet = `
-
-
- `;
+
+
+`;
export const withIconsSnippet = `
- }>Button
- }>Button
- } iconEnd={}>
- Button
-
- `;
+ }>Button
+ }>Button
+ } iconEnd={}>
+ Button
+
+`;
export const disabledSnippet = `
-
-
-
- `;
+
+
+
+`;
export const loadingSnippet = ``;
+ Load more items
+`;
export const asLinkSnippet = `
-
- Button
-
- `;
+
+ Button
+
+`;
diff --git a/docs-ui/src/app/components/card/components.tsx b/docs-ui/src/app/components/card/components.tsx
index 08fa940be8..d10e4d7bce 100644
--- a/docs-ui/src/app/components/card/components.tsx
+++ b/docs-ui/src/app/components/card/components.tsx
@@ -18,7 +18,7 @@ export const Default = () => {
);
};
-export const CustomSize = () => {
+export const HeaderAndBody = () => {
return (
{
}}
>
Header
- Body
- Footer
+ Body content without a footer
);
};
diff --git a/docs-ui/src/app/components/card/page.mdx b/docs-ui/src/app/components/card/page.mdx
index e0b6e81fff..6f410fcd97 100644
--- a/docs-ui/src/app/components/card/page.mdx
+++ b/docs-ui/src/app/components/card/page.mdx
@@ -10,10 +10,10 @@ import {
import {
cardUsageSnippet,
defaultSnippet,
- customSizeSnippet,
+ headerAndBodySnippet,
withLongBodySnippet,
} from './snippets';
-import { Default, CustomSize, WithLongBody } from './components';
+import { Default, HeaderAndBody, WithLongBody } from './components';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { CardDefinition } from '../../../utils/definitions';
@@ -21,7 +21,7 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
} code={defaultSnippet} />
@@ -32,54 +32,53 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
## API reference
-### Card
+All Card components extend `HTMLDivElement` attributes.
-A card component that can be used to display content in a box.
+### Card
### CardHeader
-To display a header in a card, use the `CardHeader` component. This will be fixed at the top of the card.
+Fixed at the top of the card.
### CardBody
-To display content in a card, use the `CardBody` component. This will automatically fill the card.
+Scrollable content area that fills available space.
### CardFooter
-To display a footer in a card, use the `CardFooter` component. This will be fixed at the bottom of the card.
+Fixed at the bottom of the card.
## Examples
-### Custom size
+### Header and body only
-Here's a view when card has a custom size.
+Cards can omit the footer section.
}
- code={customSizeSnippet}
- open
+ layout="side-by-side"
+ preview={}
+ code={headerAndBodySnippet}
/>
-### With long body
+### Scrollable body
-Here's a view when card has a long body.
+When body content exceeds the available height, CardBody scrolls while header and footer remain fixed.
}
code={withLongBodySnippet}
- open
/>
diff --git a/docs-ui/src/app/components/card/props-definition.ts b/docs-ui/src/app/components/card/props-definition.ts
index bb7bdad0dd..b3e3776648 100644
--- a/docs-ui/src/app/components/card/props-definition.ts
+++ b/docs-ui/src/app/components/card/props-definition.ts
@@ -1,30 +1,38 @@
import {
classNamePropDefs,
stylePropDefs,
- childrenPropDefs,
type PropDef,
} from '@/utils/propDefs';
+const optionalChildrenPropDef: Record = {
+ children: {
+ type: 'enum',
+ values: ['ReactNode'],
+ responsive: false,
+ description: 'Content to display inside the component.',
+ },
+};
+
export const cardPropDefs: Record = {
- ...childrenPropDefs,
+ ...optionalChildrenPropDef,
...classNamePropDefs,
...stylePropDefs,
};
export const cardHeaderPropDefs: Record = {
- ...childrenPropDefs,
+ ...optionalChildrenPropDef,
...classNamePropDefs,
...stylePropDefs,
};
export const cardBodyPropDefs: Record = {
- ...childrenPropDefs,
+ ...optionalChildrenPropDef,
...classNamePropDefs,
...stylePropDefs,
};
export const cardFooterPropDefs: Record = {
- ...childrenPropDefs,
+ ...optionalChildrenPropDef,
...classNamePropDefs,
...stylePropDefs,
};
diff --git a/docs-ui/src/app/components/card/snippets.ts b/docs-ui/src/app/components/card/snippets.ts
index b5de9a7008..6f956e5518 100644
--- a/docs-ui/src/app/components/card/snippets.ts
+++ b/docs-ui/src/app/components/card/snippets.ts
@@ -6,33 +6,40 @@ export const cardUsageSnippet = `import { Card, CardHeader, CardBody, CardFooter
Footer
`;
-export const defaultSnippet = `
+export const defaultSnippet = `
Header
Body
Footer
`;
-export const customSizeSnippet = `
+export const headerAndBodySnippet = `
Header
- Body
- Footer
+ Body content without a footer
`;
-export const withLongBodySnippet = `
+export const withLongBodySnippet = `import { Text } from '@backstage/ui';
+
+
Header
This is the first paragraph of a long body text that demonstrates how
- the Card component handles extensive content.
+ the Card component handles extensive content. The card should adjust
+ accordingly to display all the text properly while maintaining its
+ structure.
Here's a second paragraph that adds more content to our card body.
+ Having multiple paragraphs helps to visualize how spacing works within
+ the card component.
This third paragraph continues to add more text to ensure we have a
- proper demonstration of a card with significant content.
+ proper demonstration of a card with significant content. This makes it
+ easier to test scrolling behavior and overall layout when content
+ exceeds the initial view.
diff --git a/docs-ui/src/app/components/checkbox/page.mdx b/docs-ui/src/app/components/checkbox/page.mdx
index aaf73d7210..9657b471da 100644
--- a/docs-ui/src/app/components/checkbox/page.mdx
+++ b/docs-ui/src/app/components/checkbox/page.mdx
@@ -12,10 +12,15 @@ import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { CheckboxDefinition } from '../../../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
+import { ReactAriaLink } from '@/components/ReactAriaLink';
+
+export const reactAriaUrls = {
+ checkbox: 'https://react-aria.adobe.com/Checkbox',
+};
} code={defaultSnippet} />
@@ -28,12 +33,12 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
+
+
## Examples
### All variants
-Here's a view when checkboxes have different variants.
-
= {
children: {
type: 'enum',
- values: ['React.ReactNode'],
- responsive: false,
+ values: ['ReactNode'],
+ required: true,
+ description: 'Label displayed next to the checkbox.',
},
isSelected: {
- type: 'enum',
- values: ['boolean'],
- responsive: false,
+ type: 'boolean',
+ description: 'Controls checked state (controlled mode).',
},
defaultSelected: {
- type: 'enum',
- values: ['boolean'],
- responsive: false,
+ type: 'boolean',
+ description: 'Initial checked state (uncontrolled mode).',
},
onChange: {
type: 'enum',
values: ['(isSelected: boolean) => void'],
- responsive: false,
+ description: 'Called when the checked state changes.',
},
isDisabled: {
- type: 'enum',
- values: ['boolean'],
- responsive: false,
+ type: 'boolean',
+ description: 'Prevents interaction and applies disabled styling.',
},
isRequired: {
- type: 'enum',
- values: ['boolean'],
- responsive: false,
+ type: 'boolean',
+ description: 'Marks the checkbox as required for form validation.',
+ },
+ isIndeterminate: {
+ type: 'boolean',
+ description: 'Shows a mixed state, typically for "select all" checkboxes.',
},
name: {
type: 'string',
- responsive: false,
+ description: 'Name attribute for form submission.',
},
value: {
type: 'string',
- responsive: false,
+ description: 'Value attribute for form submission.',
},
...classNamePropDefs,
...stylePropDefs,
diff --git a/docs-ui/src/app/components/container/components.tsx b/docs-ui/src/app/components/container/components.tsx
index 7032b3f620..76eaf56ff1 100644
--- a/docs-ui/src/app/components/container/components.tsx
+++ b/docs-ui/src/app/components/container/components.tsx
@@ -1,24 +1,33 @@
'use client';
+import { Container } from '../../../../../packages/ui/src/components/Container/Container';
import { Box } from '../../../../../packages/ui/src/components/Box/Box';
-
-const DecorativeBox = () => (
-
-);
+import { Text } from '../../../../../packages/ui/src/components/Text/Text';
export const Preview = () => {
return (
-
-
-
+
+ Content is centered with max-width
+
+ );
+};
+
+export const BasicUsagePreview = () => {
+ return (
+
+ Hello World
+ Hello World
+ Hello World
+
+ );
+};
+
+export const ResponsiveSpacingPreview = () => {
+ return (
+
+ Hello World
+ Hello World
+ Hello World
+
);
};
diff --git a/docs-ui/src/app/components/container/page.mdx b/docs-ui/src/app/components/container/page.mdx
index 87bc55829e..4ffca3bfc5 100644
--- a/docs-ui/src/app/components/container/page.mdx
+++ b/docs-ui/src/app/components/container/page.mdx
@@ -8,7 +8,11 @@ import {
containerSimpleSnippet,
containerResponsiveSnippet,
} from './snippets';
-import { Preview } from './components';
+import {
+ Preview,
+ BasicUsagePreview,
+ ResponsiveSpacingPreview,
+} from './components';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ContainerDefinition } from '../../../utils/definitions';
@@ -17,7 +21,7 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
} code={previewSnippet} />
@@ -32,18 +36,23 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
## Examples
-### Simple
+### Basic Usage
-A simple example of how to use the Container component.
+}
+ code={containerSimpleSnippet}
+ layout="side-by-side"
+/>
-
+### Responsive Spacing
-### Responsive padding & margin
+Spacing props accept breakpoint objects for responsive behavior.
-The Container component also supports responsive values, making it easy to
-create responsive designs.
-
-
+}
+ code={containerResponsiveSnippet}
+ layout="side-by-side"
+/>
diff --git a/docs-ui/src/app/components/container/props-definition.ts b/docs-ui/src/app/components/container/props-definition.ts
index 0980520365..797aec9e92 100644
--- a/docs-ui/src/app/components/container/props-definition.ts
+++ b/docs-ui/src/app/components/container/props-definition.ts
@@ -1,12 +1,52 @@
import {
classNamePropDefs,
stylePropDefs,
- gapPropDefs,
+ spacingValues,
type PropDef,
} from '@/utils/propDefs';
export const containerPropDefs: Record = {
- ...gapPropDefs,
+ children: {
+ type: 'enum',
+ values: ['ReactNode'],
+ description: 'Content to render inside the container.',
+ },
+ my: {
+ type: 'enum | string',
+ values: spacingValues,
+ responsive: true,
+ description: 'Vertical margin (top and bottom).',
+ },
+ mt: {
+ type: 'enum | string',
+ values: spacingValues,
+ responsive: true,
+ description: 'Top margin.',
+ },
+ mb: {
+ type: 'enum | string',
+ values: spacingValues,
+ responsive: true,
+ description: 'Bottom margin.',
+ },
+ py: {
+ type: 'enum | string',
+ values: spacingValues,
+ responsive: true,
+ description: 'Vertical padding (top and bottom).',
+ },
+ pt: {
+ type: 'enum | string',
+ values: spacingValues,
+ responsive: true,
+ description: 'Top padding.',
+ },
+ pb: {
+ type: 'enum | string',
+ values: spacingValues,
+ responsive: true,
+ description: 'Bottom padding.',
+ },
...classNamePropDefs,
...stylePropDefs,
};
diff --git a/docs-ui/src/app/components/container/snippets.ts b/docs-ui/src/app/components/container/snippets.ts
index 83f4dabbbc..b16c1b81fd 100644
--- a/docs-ui/src/app/components/container/snippets.ts
+++ b/docs-ui/src/app/components/container/snippets.ts
@@ -2,9 +2,9 @@ export const containerUsageSnippet = `import { Container } from "@backstage/ui";
Hello World!`;
-export const previewSnippet = `
-
-
`;
+export const previewSnippet = `
+ Content is centered with max-width
+`;
export const containerSimpleSnippet = `
Hello World
@@ -12,7 +12,7 @@ export const containerSimpleSnippet = `
Hello World
`;
-export const containerResponsiveSnippet = `
+export const containerResponsiveSnippet = `
Hello World
Hello World
Hello World
diff --git a/docs-ui/src/app/components/dialog/components.tsx b/docs-ui/src/app/components/dialog/components.tsx
index 3a1fd8c2c9..18ab1bf9c5 100644
--- a/docs-ui/src/app/components/dialog/components.tsx
+++ b/docs-ui/src/app/components/dialog/components.tsx
@@ -58,6 +58,20 @@ export const PreviewFixedWidthAndHeight = () => (
quae ab illo inventore veritatis et quasi architecto beatae vitae
dicta sunt explicabo.
+
+ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut
+ fugit, sed quia consequuntur magni dolores eos qui ratione
+ voluptatem sequi nesciunt.
+
+
+ Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet,
+ consectetur, adipisci velit, sed quia non numquam eius modi tempora
+ incidunt ut labore et dolore magnam aliquam quaerat voluptatem.
+
+
+ Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis
+ suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur.
+
diff --git a/docs-ui/src/app/components/dialog/page.mdx b/docs-ui/src/app/components/dialog/page.mdx
index 1910cef5a8..12ce2d4135 100644
--- a/docs-ui/src/app/components/dialog/page.mdx
+++ b/docs-ui/src/app/components/dialog/page.mdx
@@ -12,7 +12,6 @@ import {
dialogHeaderPropDefs,
dialogBodyPropDefs,
dialogFooterPropDefs,
- dialogClosePropDefs,
} from './props-definition';
import {
dialogUsageSnippet,
@@ -26,10 +25,16 @@ import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { DialogDefinition } from '../../../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
+import { ReactAriaLink } from '@/components/ReactAriaLink';
+
+export const reactAriaUrls = {
+ dialogTrigger: 'https://react-aria.adobe.com/Modal#dialogtrigger',
+ modal: 'https://react-aria.adobe.com/Modal',
+};
+
+
### Dialog
The main dialog container that renders as a modal overlay.
+
+
### DialogHeader
Displays the dialog title with a built-in close button.
+
+
### DialogBody
The main content area of the dialog with optional scrolling.
@@ -94,7 +108,7 @@ Dialog with a fixed height body that scrolls when content overflows.
### Dialog with Form
-Dialog containing form elements for user input.
+Forms can be embedded in the dialog body.
= {
- children: { type: 'enum', values: ['ReactNode'], responsive: false },
+ children: {
+ type: 'enum',
+ values: ['ReactNode'],
+ description: 'Trigger element and dialog content.',
+ },
isOpen: {
type: 'boolean',
description: 'Whether the overlay is open by default (controlled).',
@@ -23,66 +27,66 @@ export const dialogTriggerPropDefs: Record = {
};
export const dialogPropDefs: Record = {
- children: { type: 'enum', values: ['ReactNode'], responsive: false },
+ children: {
+ type: 'enum',
+ values: ['ReactNode'],
+ description: 'Dialog content (DialogHeader, DialogBody, DialogFooter).',
+ },
isOpen: {
type: 'boolean',
- description: 'Whether the overlay is open by default (controlled).',
+ description: 'Whether the overlay is open (controlled mode).',
},
defaultOpen: {
type: 'boolean',
- description: 'Whether the overlay is open by default (uncontrolled).',
+ description: 'Initial open state (uncontrolled mode).',
},
onOpenChange: {
type: 'enum',
values: ['(isOpen: boolean) => void'],
- description:
- "Handler that is called when the overlay's open state changes.",
+ description: 'Called when the open state changes.',
},
width: {
type: 'enum',
values: ['number', 'string'],
- responsive: false,
+ default: '400',
+ description: 'Fixed width in pixels (number) or CSS units (string).',
},
height: {
type: 'enum',
values: ['number', 'string'],
- responsive: false,
+ default: 'auto',
+ description: 'Fixed height in pixels (number) or CSS units (string).',
},
...classNamePropDefs,
...stylePropDefs,
};
export const dialogHeaderPropDefs: Record = {
- children: { type: 'enum', values: ['ReactNode'], responsive: false },
+ children: {
+ type: 'enum',
+ values: ['ReactNode'],
+ description: 'Dialog title text.',
+ },
...classNamePropDefs,
...stylePropDefs,
};
export const dialogBodyPropDefs: Record = {
- children: { type: 'enum', values: ['ReactNode'], responsive: false },
- height: {
+ children: {
type: 'enum',
- values: ['number', 'string'],
- responsive: false,
+ values: ['ReactNode'],
+ description: 'Main content of the dialog.',
},
...classNamePropDefs,
...stylePropDefs,
};
export const dialogFooterPropDefs: Record = {
- children: { type: 'enum', values: ['ReactNode'], responsive: false },
- ...classNamePropDefs,
- ...stylePropDefs,
-};
-
-export const dialogClosePropDefs: Record = {
- variant: {
+ children: {
type: 'enum',
- values: ['primary', 'secondary', 'tertiary'],
- default: 'secondary',
- responsive: false,
+ values: ['ReactNode'],
+ description: 'Action buttons or footer content.',
},
- children: { type: 'enum', values: ['ReactNode'], responsive: false },
...classNamePropDefs,
...stylePropDefs,
};
diff --git a/docs-ui/src/app/components/dialog/snippets.ts b/docs-ui/src/app/components/dialog/snippets.ts
index c1ad50578b..440ff417e8 100644
--- a/docs-ui/src/app/components/dialog/snippets.ts
+++ b/docs-ui/src/app/components/dialog/snippets.ts
@@ -53,11 +53,14 @@ export const dialogWithFormSnippet = `
-
+
diff --git a/docs-ui/src/app/components/flex/components.tsx b/docs-ui/src/app/components/flex/components.tsx
index 2068555048..fba9aff201 100644
--- a/docs-ui/src/app/components/flex/components.tsx
+++ b/docs-ui/src/app/components/flex/components.tsx
@@ -3,36 +3,57 @@
import { Flex } from '../../../../../packages/ui/src/components/Flex/Flex';
import { Box } from '../../../../../packages/ui/src/components/Box/Box';
+const boxStyle = {
+ width: '64px',
+ height: '64px',
+ background: '#eaf2fd',
+ border: '1px solid #2563eb',
+ borderRadius: '4px',
+};
+
+const smallBoxStyle = {
+ padding: '8px 12px',
+ background: '#eaf2fd',
+ border: '1px solid #2563eb',
+ borderRadius: '4px',
+};
+
export const Default = () => {
return (
-
-
-
+
+
+
+
+ );
+};
+
+export const DirectionExample = () => {
+ return (
+
+ First
+ Second
+ Third
+
+ );
+};
+
+export const ResponsiveExample = () => {
+ return (
+
+ Item 1
+ Item 2
+ Item 3
+
+ );
+};
+
+export const AlignExample = () => {
+ return (
+
+ Start
+ Middle
+ End
);
};
diff --git a/docs-ui/src/app/components/flex/page.mdx b/docs-ui/src/app/components/flex/page.mdx
index c45730e26f..e58b6000d4 100644
--- a/docs-ui/src/app/components/flex/page.mdx
+++ b/docs-ui/src/app/components/flex/page.mdx
@@ -5,11 +5,16 @@ import { flexPropDefs } from './props-definition';
import {
flexUsageSnippet,
defaultSnippet,
- flexSimpleSnippet,
flexResponsiveSnippet,
flexAlignSnippet,
+ flexDirectionSnippet,
} from './snippets';
-import { Default } from './components';
+import {
+ Default,
+ ResponsiveExample,
+ AlignExample,
+ DirectionExample,
+} from './components';
import { spacingPropDefs } from '@/utils/propDefs';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
@@ -19,7 +24,7 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
} code={defaultSnippet} />
@@ -32,31 +37,43 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
-The grid component also accepts all the spacing props from the Box component.
+The Flex component also accepts all the spacing props from the Box component.
## Examples
-### Simple
+### Direction
-A simple example of how to use the Flex component.
+}
+ code={flexDirectionSnippet}
+ layout="side-by-side"
+/>
-
+### Responsive gap
-### Responsive
+Gap values can be responsive using breakpoint objects.
-The Flex component also supports responsive values, making it easy to create
-responsive designs.
+}
+ code={flexResponsiveSnippet}
+ layout="side-by-side"
+/>
-
+### Alignment
-### Align
-
-The Flex component also supports responsive alignment, making it easy to
-create responsive designs.
-
-
+}
+ code={flexAlignSnippet}
+ layout="side-by-side"
+/>
diff --git a/docs-ui/src/app/components/flex/props-definition.ts b/docs-ui/src/app/components/flex/props-definition.ts
deleted file mode 100644
index bfd3e008a8..0000000000
--- a/docs-ui/src/app/components/flex/props-definition.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import {
- childrenPropDefs,
- classNamePropDefs,
- stylePropDefs,
- type PropDef,
-} from '@/utils/propDefs';
-
-export const flexPropDefs: Record = {
- align: {
- type: 'enum',
- values: ['start', 'center', 'end', 'baseline', 'stretch'],
- responsive: true,
- },
- direction: {
- type: 'enum',
- values: ['row', 'column', 'row-reverse', 'column-reverse'],
- responsive: true,
- },
- justify: {
- type: 'enum',
- values: ['start', 'center', 'end', 'between'],
- responsive: true,
- },
- ...childrenPropDefs,
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/docs-ui/src/app/components/flex/props-definition.tsx b/docs-ui/src/app/components/flex/props-definition.tsx
new file mode 100644
index 0000000000..f0e36f370a
--- /dev/null
+++ b/docs-ui/src/app/components/flex/props-definition.tsx
@@ -0,0 +1,64 @@
+import {
+ classNamePropDefs,
+ stylePropDefs,
+ gapPropDefs,
+ type PropDef,
+} from '@/utils/propDefs';
+import { Chip } from '@/components/Chip';
+
+export const flexPropDefs: Record = {
+ direction: {
+ type: 'enum',
+ values: ['row', 'column', 'row-reverse', 'column-reverse'],
+ responsive: true,
+ description: (
+ <>
+ Main axis direction. Use row for horizontal,{' '}
+ column for vertical layouts.
+ >
+ ),
+ },
+ align: {
+ type: 'enum',
+ values: ['start', 'center', 'end', 'baseline', 'stretch'],
+ responsive: true,
+ description:
+ 'Cross-axis alignment. Controls how children align perpendicular to the main axis.',
+ },
+ justify: {
+ type: 'enum',
+ values: ['start', 'center', 'end', 'between'],
+ responsive: true,
+ description: (
+ <>
+ Main-axis distribution. Use between to space children
+ evenly with no edge gaps.
+ >
+ ),
+ },
+ gap: {
+ ...gapPropDefs.gap,
+ default: '4',
+ description:
+ 'Space between children. Accepts spacing scale values or responsive objects.',
+ },
+ surface: {
+ type: 'enum',
+ values: ['0', '1', '2', '3', 'danger', 'warning', 'success', 'auto'],
+ responsive: true,
+ description: (
+ <>
+ Surface level for theming. Use auto to increment from
+ parent context.
+ >
+ ),
+ },
+ children: {
+ type: 'enum',
+ values: ['ReactNode'],
+ responsive: false,
+ description: 'Content to render inside the flex container.',
+ },
+ ...classNamePropDefs,
+ ...stylePropDefs,
+};
diff --git a/docs-ui/src/app/components/flex/snippets.ts b/docs-ui/src/app/components/flex/snippets.ts
index 138da7ddd9..b0c08ed140 100644
--- a/docs-ui/src/app/components/flex/snippets.ts
+++ b/docs-ui/src/app/components/flex/snippets.ts
@@ -1,6 +1,10 @@
export const flexUsageSnippet = `import { Flex } from '@backstage/ui';
-`;
+
+ Item 1
+ Item 2
+ Item 3
+`;
export const defaultSnippet = `
@@ -8,20 +12,20 @@ export const defaultSnippet = `
`;
-export const flexSimpleSnippet = `
- Hello World
- Hello World
- Hello World
+export const flexDirectionSnippet = `
+ First
+ Second
+ Third
`;
export const flexResponsiveSnippet = `
- Hello World
- Hello World
- Hello World
+ Item 1
+ Item 2
+ Item 3
`;
-export const flexAlignSnippet = `
- Hello World
- Hello World
- Hello World
+export const flexAlignSnippet = `
+ Start
+ Middle
+ End
`;
diff --git a/docs-ui/src/app/components/grid/components.tsx b/docs-ui/src/app/components/grid/components.tsx
index f1215019f8..088897f004 100644
--- a/docs-ui/src/app/components/grid/components.tsx
+++ b/docs-ui/src/app/components/grid/components.tsx
@@ -3,57 +3,52 @@
import { Grid } from '../../../../../packages/ui/src/components/Grid/Grid';
import { Box } from '../../../../../packages/ui/src/components/Box/Box';
+const boxStyle = {
+ height: '64px',
+ background: '#eaf2fd',
+ border: '1px solid #2563eb',
+ borderRadius: '4px',
+};
+
+const labelBoxStyle = {
+ padding: '8px 12px',
+ background: '#eaf2fd',
+ border: '1px solid #2563eb',
+ borderRadius: '4px',
+};
+
export const Default = () => {
return (
-
-
-
-
-
-
+
+
+
+
+
+
+
+ );
+};
+
+export const ResponsiveExample = () => {
+ return (
+
+ 1
+ 2
+ 3
+ 4
+
+ );
+};
+
+export const GridItemExample = () => {
+ return (
+
+
+ Spans 2 columns
+
+ 1 column
+ 1 column
);
};
diff --git a/docs-ui/src/app/components/grid/page.mdx b/docs-ui/src/app/components/grid/page.mdx
index e75f277226..8070516b21 100644
--- a/docs-ui/src/app/components/grid/page.mdx
+++ b/docs-ui/src/app/components/grid/page.mdx
@@ -1,9 +1,14 @@
import { CodeBlock } from '@/components/CodeBlock';
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
-import { gridPropDefs } from './props-definition';
-import { gridUsageSnippet, defaultSnippet } from './snippets';
-import { Default } from './components';
+import { gridPropDefs, gridItemPropDefs } from './props-definition';
+import {
+ gridUsageSnippet,
+ defaultSnippet,
+ gridResponsiveSnippet,
+ gridItemSnippet,
+} from './snippets';
+import { Default, ResponsiveExample, GridItemExample } from './components';
import { spacingPropDefs } from '@/utils/propDefs';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
@@ -13,7 +18,7 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
} code={defaultSnippet} />
@@ -26,16 +31,39 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
### Grid.Root
-This is the grid container component. It will help to define the number of
-columns that will be used in the grid. You can also define the gap between the
-columns. All values are responsive.
+The grid container. Defines column count and gap between items.
-The grid component also accepts all the spacing props from the Box component.
+Grid.Root also accepts all spacing props from the Box component.
+### Grid.Item
+
+A grid child with column and row spanning control.
+
+
+
+## Examples
+
+### Responsive columns
+
+Column count can change at different breakpoints.
+
+}
+ code={gridResponsiveSnippet}
+ open
+/>
+
+### Column spanning
+
+Use Grid.Item to span multiple columns.
+
+} code={gridItemSnippet} open />
+
diff --git a/docs-ui/src/app/components/grid/props-definition.ts b/docs-ui/src/app/components/grid/props-definition.ts
deleted file mode 100644
index d07b59d156..0000000000
--- a/docs-ui/src/app/components/grid/props-definition.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import {
- childrenPropDefs,
- classNamePropDefs,
- gapPropDefs,
- stylePropDefs,
- type PropDef,
-} from '@/utils/propDefs';
-
-export const gridPropDefs: Record = {
- columns: {
- type: 'string',
- responsive: true,
- },
- rows: {
- type: 'string',
- responsive: true,
- },
- ...gapPropDefs,
- ...childrenPropDefs,
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/docs-ui/src/app/components/grid/props-definition.tsx b/docs-ui/src/app/components/grid/props-definition.tsx
new file mode 100644
index 0000000000..c04c544f1a
--- /dev/null
+++ b/docs-ui/src/app/components/grid/props-definition.tsx
@@ -0,0 +1,113 @@
+import {
+ childrenPropDefs,
+ classNamePropDefs,
+ gapPropDefs,
+ stylePropDefs,
+ type PropDef,
+} from '@/utils/propDefs';
+import { Chip } from '@/components/Chip';
+
+const columnValues = [
+ '1',
+ '2',
+ '3',
+ '4',
+ '5',
+ '6',
+ '7',
+ '8',
+ '9',
+ '10',
+ '11',
+ '12',
+ 'auto',
+];
+
+const surfaceValues = [
+ '0',
+ '1',
+ '2',
+ '3',
+ 'danger',
+ 'warning',
+ 'success',
+ 'auto',
+];
+
+export const gridPropDefs: Record = {
+ columns: {
+ type: 'enum',
+ values: columnValues,
+ default: 'auto',
+ responsive: true,
+ description: (
+ <>
+ Number of columns. Use 1-12 for fixed layouts, auto to fit
+ content.
+ >
+ ),
+ },
+ gap: {
+ ...gapPropDefs.gap,
+ default: '4',
+ description:
+ 'Space between items. Use higher values for separated layouts, lower for compact.',
+ },
+ surface: {
+ type: 'enum',
+ values: surfaceValues,
+ responsive: true,
+ description: (
+ <>
+ Surface level for theming. Use auto to increment from
+ parent context.
+ >
+ ),
+ },
+ ...childrenPropDefs,
+ ...classNamePropDefs,
+ ...stylePropDefs,
+};
+
+export const gridItemPropDefs: Record = {
+ colSpan: {
+ type: 'enum',
+ values: columnValues,
+ responsive: true,
+ description: 'Number of columns the item spans across.',
+ },
+ colStart: {
+ type: 'enum',
+ values: columnValues,
+ responsive: true,
+ description:
+ 'Starting column position. Use with colEnd for explicit placement.',
+ },
+ colEnd: {
+ type: 'enum',
+ values: columnValues,
+ responsive: true,
+ description:
+ 'Ending column position. Use with colStart for explicit placement.',
+ },
+ rowSpan: {
+ type: 'enum',
+ values: columnValues,
+ responsive: true,
+ description: 'Number of rows the item spans. Useful for tall content.',
+ },
+ surface: {
+ type: 'enum',
+ values: surfaceValues,
+ responsive: true,
+ description: (
+ <>
+ Surface level for theming. Use auto to increment from
+ parent context.
+ >
+ ),
+ },
+ ...childrenPropDefs,
+ ...classNamePropDefs,
+ ...stylePropDefs,
+};
diff --git a/docs-ui/src/app/components/grid/snippets.ts b/docs-ui/src/app/components/grid/snippets.ts
index 65ef323047..f6d32b8802 100644
--- a/docs-ui/src/app/components/grid/snippets.ts
+++ b/docs-ui/src/app/components/grid/snippets.ts
@@ -1,6 +1,10 @@
export const gridUsageSnippet = `import { Grid } from '@backstage/ui';
-`;
+
+ Item 1
+ Item 2
+ Item 3
+`;
export const defaultSnippet = `
@@ -10,3 +14,16 @@ export const defaultSnippet = `
`;
+
+export const gridResponsiveSnippet = `
+ 1
+ 2
+ 3
+ 4
+`;
+
+export const gridItemSnippet = `
+ Spans 2 columns
+ 1 column
+ 1 column
+`;
diff --git a/docs-ui/src/app/components/header-page/components.tsx b/docs-ui/src/app/components/header-page/components.tsx
index 439d12b8f0..791afc5bbe 100644
--- a/docs-ui/src/app/components/header-page/components.tsx
+++ b/docs-ui/src/app/components/header-page/components.tsx
@@ -19,22 +19,6 @@ const tabs = [
{ id: 'integrations', label: 'Integrations', href: '/integrations' },
];
-const menuItems = [
- { label: 'Settings', value: 'settings', href: '/settings' },
- {
- label: 'Invite new members',
- value: 'invite-new-members',
- href: '/invite-new-members',
- },
- {
- label: 'Logout',
- value: 'logout',
- onClick: () => {
- alert('logout');
- },
- },
-];
-
const breadcrumbs = [
{ label: 'Home', href: '/' },
{ label: 'Long Breadcrumb Name', href: '/long-breadcrumb' },
@@ -49,8 +33,8 @@ export const WithEverything = () => (
@@ -63,13 +47,13 @@ export const WithEverything = () => (
export const WithLongBreadcrumbs = () => (
-
+
);
export const WithTabs = () => (
-
+
);
@@ -82,7 +66,7 @@ export const WithCustomActions = () => (
);
-export const WithMenuItems = () => (
+export const WithMenu = () => (
(
} />
}
diff --git a/docs-ui/src/app/components/header-page/page.mdx b/docs-ui/src/app/components/header-page/page.mdx
index cf17bf46b7..ac99fb1af0 100644
--- a/docs-ui/src/app/components/header-page/page.mdx
+++ b/docs-ui/src/app/components/header-page/page.mdx
@@ -6,16 +6,16 @@ import {
WithLongBreadcrumbs,
WithTabs,
WithCustomActions,
- WithMenuItems,
+ WithMenu,
} from './components';
-import { propDefs } from './props-definition';
+import { headerPagePropDefs } from './props-definition';
import {
usage,
defaultSnippet,
withTabs,
withBreadcrumbs,
withCustomActions,
- withMenuItems,
+ withMenu,
} from './snippets';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
@@ -23,9 +23,8 @@ import { HeaderPageDefinition } from '../../../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
} code={defaultSnippet} />
@@ -36,38 +35,32 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
## API reference
-
+
## Examples
-### With Breadcrumbs
+### Breadcrumbs
-You can add breadcrumbs to the header page to help users navigate to the previous page. The `breadcrumbs`
-prop is an array of objects with a `label` and `href` property. By default we truncate the breadcrumb label to 240px.
+Labels are truncated at 240px.
} code={withBreadcrumbs} />
-### With Tabs
+### Tabs
-You can add tabs to the header page to help users navigate to the different sections of the page. The `tabs`
-prop is an array of objects with a `label` and `href` property.
+Tabs use React Router and highlight based on the current route.
} code={withTabs} />
-### With Custom Actions
-
-You can add custom actions to the header page to help users navigate to the different sections of the page. The `customActions`
-prop is a React node.
+### Custom actions
} code={withCustomActions} />
-### With Menu Items
+### With menu
-You can add menu items to the header page to help users navigate to the different sections of the page. The `menuItems`
-prop is an array of objects with a `label`, `value` and `onClick` property.
+Use `customActions` to add a dropdown menu.
-} code={withMenuItems} />
+} code={withMenu} />
-
+
diff --git a/docs-ui/src/app/components/header-page/props-definition.ts b/docs-ui/src/app/components/header-page/props-definition.ts
deleted file mode 100644
index 6b9208b07a..0000000000
--- a/docs-ui/src/app/components/header-page/props-definition.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-import {
- childrenPropDefs,
- classNamePropDefs,
- stylePropDefs,
- type PropDef,
-} from '@/utils/propDefs';
-
-export const propDefs: Record = {
- title: {
- type: 'string',
- default: 'Your plugin',
- },
- customActions: {
- type: 'enum',
- values: ['ReactNode'],
- },
- menuItems: {
- type: 'complex',
- complexType: {
- name: 'MenuItem[]',
- properties: {
- label: {
- type: 'string',
- required: true,
- description: 'Display text for the menu item',
- },
- value: {
- type: 'string',
- required: true,
- description: 'Unique value for the menu item',
- },
- onClick: {
- type: '() => void',
- required: false,
- description: 'Callback function when menu item is clicked',
- },
- },
- },
- },
- tabs: {
- type: 'complex',
- complexType: {
- name: 'HeaderTab[]',
- properties: {
- id: {
- type: 'string',
- required: true,
- description: 'Unique identifier for the tab',
- },
- label: {
- type: 'string',
- required: true,
- description: 'Display text for the tab',
- },
- href: {
- type: 'string',
- required: false,
- description: 'URL to navigate to when tab is clicked',
- },
- matchStrategy: {
- type: "'exact' | 'prefix'",
- required: false,
- description: 'How to match the current route to highlight the tab',
- },
- },
- },
- },
- breadcrumbs: {
- type: 'complex',
- complexType: {
- name: 'Breadcrumb[]',
- properties: {
- label: {
- type: 'string',
- required: true,
- description: 'Display text for the breadcrumb',
- },
- href: {
- type: 'string',
- required: true,
- description: 'URL for the breadcrumb link',
- },
- },
- },
- },
- ...childrenPropDefs,
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/docs-ui/src/app/components/header-page/props-definition.tsx b/docs-ui/src/app/components/header-page/props-definition.tsx
new file mode 100644
index 0000000000..393141870f
--- /dev/null
+++ b/docs-ui/src/app/components/header-page/props-definition.tsx
@@ -0,0 +1,69 @@
+import { classNamePropDefs, type PropDef } from '@/utils/propDefs';
+import { Chip } from '@/components/Chip';
+
+export const headerPagePropDefs: Record = {
+ title: {
+ type: 'string',
+ description: 'Page heading displayed in the header.',
+ },
+ customActions: {
+ type: 'enum',
+ values: ['ReactNode'],
+ description: 'Custom elements rendered in the actions area.',
+ },
+ tabs: {
+ type: 'complex',
+ description: 'Navigation tabs displayed below the title.',
+ complexType: {
+ name: 'HeaderTab[]',
+ properties: {
+ id: {
+ type: 'string',
+ required: true,
+ description: 'Unique identifier for the tab.',
+ },
+ label: {
+ type: 'string',
+ required: true,
+ description: 'Display text for the tab.',
+ },
+ href: {
+ type: 'string',
+ required: true,
+ description: 'URL to navigate to when tab is clicked.',
+ },
+ matchStrategy: {
+ type: "'exact' | 'prefix'",
+ required: false,
+ default: "'exact'",
+ description: (
+ <>
+ Route matching strategy. Use exact for exact path
+ match, prefix if pathname starts with href.
+ >
+ ),
+ },
+ },
+ },
+ },
+ breadcrumbs: {
+ type: 'complex',
+ description: 'Breadcrumb trail displayed above the title.',
+ complexType: {
+ name: 'HeaderPageBreadcrumb[]',
+ properties: {
+ label: {
+ type: 'string',
+ required: true,
+ description: 'Display text for the breadcrumb. Truncated at 240px.',
+ },
+ href: {
+ type: 'string',
+ required: true,
+ description: 'URL for the breadcrumb link.',
+ },
+ },
+ },
+ },
+ ...classNamePropDefs,
+};
diff --git a/docs-ui/src/app/components/header-page/snippets.ts b/docs-ui/src/app/components/header-page/snippets.ts
index 5012416af7..607675717c 100644
--- a/docs-ui/src/app/components/header-page/snippets.ts
+++ b/docs-ui/src/app/components/header-page/snippets.ts
@@ -1,21 +1,23 @@
export const usage = `import { HeaderPage } from '@backstage/ui';
-`;
+`;
export const defaultSnippet = `Custom action}
+ customActions={
+ <>
+
+
+ >
+ }
/>`;
export const withBreadcrumbs = ``;
@@ -42,10 +42,15 @@ export const withCustomActions = `Custom action}
/>`;
-export const withMenuItems = ` {} },
- { label: 'Invite new members', value: 'invite-new-members', onClick: () => {} },
- ]}
+ customActions={
+
+ } />
+
+
+ }
/>`;
diff --git a/docs-ui/src/app/components/header/components.tsx b/docs-ui/src/app/components/header/components.tsx
index 2a55b75784..80a4d1dd6e 100644
--- a/docs-ui/src/app/components/header/components.tsx
+++ b/docs-ui/src/app/components/header/components.tsx
@@ -20,20 +20,16 @@ const tabs = [
];
const tabs2 = [
- { id: 'Banana', label: 'Banana', href: '/banana' },
- { id: 'Apple', label: 'Apple', href: '/apple' },
- { id: 'Orange', label: 'Orange', href: '/orange' },
-];
-
-const breadcrumbs = [
- { label: 'Home', href: '/' },
- { label: 'Dashboard', href: '/dashboard' },
- { label: 'Settings', href: '/settings' },
+ { id: 'banana', label: 'Banana', href: '/banana' },
+ { id: 'apple', label: 'Apple', href: '/apple' },
+ { id: 'orange', label: 'Orange', href: '/orange' },
];
export const WithAllOptionsAndTabs = () => (
@@ -49,6 +45,8 @@ export const WithAllOptionsAndTabs = () => (
export const WithAllOptions = () => (
} />
@@ -60,30 +58,14 @@ export const WithAllOptions = () => (
);
-export const WithBreadcrumbs = () => (
-
-
-
-);
-
export const WithHeaderPage = () => (
<>
-
- } />
- } />
- } />
- >
- }
- />
+
Custom action}
- breadcrumbs={breadcrumbs}
/>
>
diff --git a/docs-ui/src/app/components/header/page.mdx b/docs-ui/src/app/components/header/page.mdx
index 22343cd6c5..71e9950933 100644
--- a/docs-ui/src/app/components/header/page.mdx
+++ b/docs-ui/src/app/components/header/page.mdx
@@ -4,16 +4,14 @@ import { Snippet } from '@/components/Snippet';
import {
WithAllOptionsAndTabs,
WithAllOptions,
- WithBreadcrumbs,
WithHeaderPage,
} from './components';
-import { propDefs } from './props-definition';
+import { headerPropDefs } from './props-definition';
import {
usage,
simple,
defaultSnippet,
withTabs,
- withBreadcrumbs,
withHeaderPage,
} from './snippets';
import { PageTitle } from '@/components/PageTitle';
@@ -22,9 +20,8 @@ import { HeaderDefinition } from '../../../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
} code={defaultSnippet} />
@@ -35,40 +32,26 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
## API reference
-
+
## Examples
### Simple header
-A simple example of how to use the Header component.
-
} code={simple} open />
### Header with tabs
-A simple example of how to use the Header component with tabs. All links are using React Router
-under the hood and will be active when you are on the corresponding page.
+Tabs use React Router and highlight automatically based on the current route.
-}
-code={withTabs}
-open
-/>
-
-### Header with breadcrumbs
-
-Breacrumbs should appear when you scroll down (and not directly visible as it is in the demo below).
-
-} code={withBreadcrumbs} open />
+} code={withTabs} open />
### Header with HeaderPage
-You can use the `Header` component inside the [HeaderPage](/components/header-page) component to compose your multi-level navigation.
+Combine with [HeaderPage](/components/header-page) for multi-level navigation.
} code={withHeaderPage} open />
-
+
diff --git a/docs-ui/src/app/components/header/props-definition.ts b/docs-ui/src/app/components/header/props-definition.ts
deleted file mode 100644
index 4ef166761c..0000000000
--- a/docs-ui/src/app/components/header/props-definition.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-import {
- childrenPropDefs,
- classNamePropDefs,
- stylePropDefs,
- type PropDef,
-} from '@/utils/propDefs';
-
-export const propDefs: Record = {
- icon: {
- type: 'enum',
- values: ['ReactNode'],
- },
- title: {
- type: 'string',
- default: 'Your plugin',
- },
- titleLink: {
- type: 'string',
- default: '/',
- },
- customActions: {
- type: 'enum',
- values: ['ReactNode'],
- },
- menuItems: {
- type: 'complex',
- complexType: {
- name: 'MenuItem[]',
- properties: {
- label: {
- type: 'string',
- required: true,
- description: 'Display text for the menu item',
- },
- value: {
- type: 'string',
- required: true,
- description: 'Unique value for the menu item',
- },
- onClick: {
- type: '() => void',
- required: false,
- description: 'Callback function when menu item is clicked',
- },
- },
- },
- },
- tabs: {
- type: 'complex',
- complexType: {
- name: 'HeaderTab[]',
- properties: {
- id: {
- type: 'string',
- required: true,
- description: 'Unique identifier for the tab',
- },
- label: {
- type: 'string',
- required: true,
- description: 'Display text for the tab',
- },
- href: {
- type: 'string',
- required: false,
- description: 'URL to navigate to when tab is clicked',
- },
- matchStrategy: {
- type: "'exact' | 'prefix'",
- required: false,
- description: 'How to match the current route to highlight the tab',
- },
- },
- },
- },
- onTabSelectionChange: {
- type: 'enum',
- values: ['(key: string) => void'],
- },
- ...childrenPropDefs,
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/docs-ui/src/app/components/header/props-definition.tsx b/docs-ui/src/app/components/header/props-definition.tsx
new file mode 100644
index 0000000000..4a651cdb72
--- /dev/null
+++ b/docs-ui/src/app/components/header/props-definition.tsx
@@ -0,0 +1,63 @@
+import { classNamePropDefs, type PropDef } from '@/utils/propDefs';
+import { Chip } from '@/components/Chip';
+
+export const headerPropDefs: Record = {
+ icon: {
+ type: 'enum',
+ values: ['ReactNode'],
+ description: 'Icon displayed before the title.',
+ },
+ title: {
+ type: 'string',
+ description: 'Main heading text for the header.',
+ },
+ titleLink: {
+ type: 'string',
+ description: 'URL the title links to when clicked.',
+ },
+ customActions: {
+ type: 'enum',
+ values: ['ReactNode'],
+ description: 'Custom elements rendered in the toolbar area.',
+ },
+ tabs: {
+ type: 'complex',
+ description: 'Navigation tabs displayed below the toolbar.',
+ complexType: {
+ name: 'HeaderTab[]',
+ properties: {
+ id: {
+ type: 'string',
+ required: true,
+ description: 'Unique identifier for the tab.',
+ },
+ label: {
+ type: 'string',
+ required: true,
+ description: 'Display text for the tab.',
+ },
+ href: {
+ type: 'string',
+ required: true,
+ description: 'URL to navigate to when tab is clicked.',
+ },
+ matchStrategy: {
+ type: "'exact' | 'prefix'",
+ required: false,
+ description: (
+ <>
+ Route matching strategy. Use exact for exact path
+ match, prefix if pathname starts with href.
+ >
+ ),
+ },
+ },
+ },
+ },
+ onTabSelectionChange: {
+ type: 'enum',
+ values: ['(key: Key) => void'],
+ description: 'Handler called when the selected tab changes.',
+ },
+ ...classNamePropDefs,
+};
diff --git a/docs-ui/src/app/components/header/snippets.ts b/docs-ui/src/app/components/header/snippets.ts
index 530cc56c77..b83f1b68b2 100644
--- a/docs-ui/src/app/components/header/snippets.ts
+++ b/docs-ui/src/app/components/header/snippets.ts
@@ -1,25 +1,14 @@
export const usage = `import { Header } from '@backstage/ui';
-`;
+`;
export const defaultSnippet = `
@@ -33,17 +22,6 @@ export const defaultSnippet = `
} />
@@ -53,64 +31,31 @@ export const simple = ``;
-export const withTabs = `
-
- } />
- } />
- } />
- >
- }
- tabs={[
- { id: 'overview', label: 'Overview' },
- { id: 'checks', label: 'Checks' },
- { id: 'tracks', label: 'Tracks' },
- { id: 'campaigns', label: 'Campaigns' },
- { id: 'integrations', label: 'Integrations' },
- ]}
-/>
-`;
-
-export const withBreadcrumbs = ``;
export const withHeaderPage = `
Custom action}
/>`;
diff --git a/docs-ui/src/app/components/link/components.tsx b/docs-ui/src/app/components/link/components.tsx
index 3447a6330a..ac4bd05801 100644
--- a/docs-ui/src/app/components/link/components.tsx
+++ b/docs-ui/src/app/components/link/components.tsx
@@ -26,29 +26,29 @@ export const AllVariants = () => {
return (
-
- Sign up for Backstage
+
+ title-large
-
- Sign up for Backstage
+
+ title-medium
-
- Sign up for Backstage
+
+ title-small
-
- Sign up for Backstage
+
+ title-x-small
-
- Sign up for Backstage
+
+ body-large
-
- Sign up for Backstage
+
+ body-medium
-
- Sign up for Backstage
+
+ body-small
-
- Sign up for Backstage
+
+ body-x-small
@@ -59,51 +59,35 @@ export const AllColors = () => {
return (
-
- I am primary
+
+ Primary
-
- I am secondary
+
+ Secondary
-
- I am tertiary
+
+ Danger
-
- I am inherit
+
+ Warning
+
+
+ Success
);
};
-export const Underline = () => {
+export const Weight = () => {
return (
-
-
- Always underlined
+
+
+ Regular
-
- Underlined on hover
-
-
- Never underlined
+
+ Bold
diff --git a/docs-ui/src/app/components/link/page.mdx b/docs-ui/src/app/components/link/page.mdx
index fb44c7084f..13710ee259 100644
--- a/docs-ui/src/app/components/link/page.mdx
+++ b/docs-ui/src/app/components/link/page.mdx
@@ -1,6 +1,7 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { CodeBlock } from '@/components/CodeBlock';
+import { ReactAriaLink } from '@/components/ReactAriaLink';
import { linkPropDefs } from './props-definition';
import {
linkUsageSnippet,
@@ -8,23 +9,27 @@ import {
externalLinkSnippet,
allVariantsSnippet,
allColorsSnippet,
- underlineSnippet,
+ weightSnippet,
} from './snippets';
import {
Default,
ExternalLink,
AllVariants,
AllColors,
- Underline,
+ Weight,
} from './components';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { LinkDefinition } from '../../../utils/definitions';
+export const reactAriaUrls = {
+ link: 'https://react-aria.adobe.com/Link',
+};
+
} code={defaultSnippet} />
@@ -37,55 +42,44 @@ import { LinkDefinition } from '../../../utils/definitions';
+
+
## Examples
-### External Link
+### External link
-Here's how to create an external link that opens in a new tab.
+Use `target="_blank"` to open links in a new tab.
}
code={externalLinkSnippet}
/>
### Variants
-Here's a view when links have different variants.
-
}
code={allVariantsSnippet}
/>
### Colors
-Here's a view when links have different colors.
+Status colors for contextual links.
}
code={allColorsSnippet}
/>
-### Underline
+### Weight
-Here's a view when links have different underline styles.
-
-}
- code={underlineSnippet}
-/>
+} code={weightSnippet} />
diff --git a/docs-ui/src/app/components/link/props-definition.ts b/docs-ui/src/app/components/link/props-definition.ts
deleted file mode 100644
index 63710b9e0b..0000000000
--- a/docs-ui/src/app/components/link/props-definition.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import {
- childrenPropDefs,
- classNamePropDefs,
- stylePropDefs,
- type PropDef,
-} from '@/utils/propDefs';
-
-export const linkPropDefs: Record = {
- href: {
- type: 'string',
- required: true,
- },
- target: {
- type: 'enum',
- values: ['_self', '_blank', '_parent', '_top'],
- },
- variant: {
- type: 'enum',
- values: [
- 'title-large',
- 'title-medium',
- 'title-small',
- 'title-x-small',
- 'body-large',
- 'body-medium',
- 'body-small',
- 'body-x-small',
- ],
- default: 'body-medium',
- responsive: true,
- },
- color: {
- type: 'enum',
- values: ['primary', 'secondary', 'tertiary', 'inherit'],
- default: 'primary',
- },
- underline: {
- type: 'enum',
- values: ['always', 'hover', 'none'],
- default: 'hover',
- },
- ...childrenPropDefs,
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/docs-ui/src/app/components/link/props-definition.tsx b/docs-ui/src/app/components/link/props-definition.tsx
new file mode 100644
index 0000000000..7d3b762a24
--- /dev/null
+++ b/docs-ui/src/app/components/link/props-definition.tsx
@@ -0,0 +1,73 @@
+import {
+ childrenPropDefs,
+ classNamePropDefs,
+ stylePropDefs,
+ type PropDef,
+} from '@/utils/propDefs';
+import { Chip } from '@/components/Chip';
+
+export const linkPropDefs: Record = {
+ href: {
+ type: 'string',
+ description:
+ 'URL the link navigates to. Supports internal and external URLs.',
+ },
+ target: {
+ type: 'enum',
+ values: ['_self', '_blank', '_parent', '_top'],
+ description: (
+ <>
+ Where to open the link. Use _blank for external links that
+ open in new tabs.
+ >
+ ),
+ },
+ title: {
+ type: 'string',
+ description: 'Tooltip text shown on hover. Useful for accessibility.',
+ },
+ variant: {
+ type: 'enum',
+ values: [
+ 'title-large',
+ 'title-medium',
+ 'title-small',
+ 'title-x-small',
+ 'body-large',
+ 'body-medium',
+ 'body-small',
+ 'body-x-small',
+ ],
+ default: 'body',
+ responsive: true,
+ description:
+ 'Typography style. Title variants for headings, body for paragraph text.',
+ },
+ weight: {
+ type: 'enum',
+ values: ['regular', 'bold'],
+ default: 'regular',
+ responsive: true,
+ description: (
+ <>
+ Font weight. Use bold for emphasis.
+ >
+ ),
+ },
+ color: {
+ type: 'enum',
+ values: ['primary', 'secondary', 'danger', 'warning', 'success'],
+ default: 'primary',
+ responsive: true,
+ description:
+ 'Text color. Status colors (danger, warning, success) for contextual links.',
+ },
+ truncate: {
+ type: 'boolean',
+ description:
+ 'Truncates text with ellipsis when it overflows its container.',
+ },
+ ...childrenPropDefs,
+ ...classNamePropDefs,
+ ...stylePropDefs,
+};
diff --git a/docs-ui/src/app/components/link/snippets.ts b/docs-ui/src/app/components/link/snippets.ts
index 347f2b708c..234b3fe983 100644
--- a/docs-ui/src/app/components/link/snippets.ts
+++ b/docs-ui/src/app/components/link/snippets.ts
@@ -9,46 +9,25 @@ export const externalLinkSnippet = `
-
- Sign up for Backstage
-
-
- Sign up for Backstage
-
-
- Sign up for Backstage
-
-
- Sign up for Backstage
-
-
- Sign up for Backstage
-
+ title-large
+ title-medium
+ title-small
+ title-x-small
+ body-large
+ body-medium
+ body-small
+ body-x-small
`;
export const allColorsSnippet = `
-
- I am primary
-
-
- I am secondary
-
-
- I am tertiary
-
-
- I am inherit
-
+ Primary
+ Secondary
+ Danger
+ Warning
+ Success
`;
-export const underlineSnippet = `
-
- Always underlined
-
-
- Underlined on hover
-
-
- Never underlined
-
+export const weightSnippet = `
+ Regular
+ Bold
`;
diff --git a/docs-ui/src/app/components/menu/components.tsx b/docs-ui/src/app/components/menu/components.tsx
index d4ddfbf4ba..fbb481f5ef 100644
--- a/docs-ui/src/app/components/menu/components.tsx
+++ b/docs-ui/src/app/components/menu/components.tsx
@@ -8,6 +8,7 @@ import {
MenuSeparator,
SubmenuTrigger,
MenuAutocomplete,
+ MenuAutocompleteListbox,
MenuListBoxItem,
} from '../../../../../packages/ui/src/components/Menu/Menu';
import { Button } from '../../../../../packages/ui/src/components/Button/Button';
@@ -54,10 +55,9 @@ export const PreviewSubmenu = () => (
-
@@ -128,30 +128,42 @@ export const PreviewSeparators = () => (
export const PreviewAutocompleteMenu = () => (
-
-
-
-
-
+
+
+
+
+
+
+
+
);
export const PreviewAutocompleteListbox = () => (
-
- Option 1
- Option 2
- Option 3
-
+
+
+
+ Option 1
+ Option 2
+ Option 3
+
+
);
export const PreviewAutocompleteListboxMultiple = () => (
-
- Option 1
- Option 2
- Option 3
-
+
+
+
+ Option 1
+ Option 2
+ Option 3
+
+
);
diff --git a/docs-ui/src/app/components/menu/page.mdx b/docs-ui/src/app/components/menu/page.mdx
index 86f2778e01..f1de264d73 100644
--- a/docs-ui/src/app/components/menu/page.mdx
+++ b/docs-ui/src/app/components/menu/page.mdx
@@ -40,10 +40,21 @@ import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { MenuDefinition } from '../../../utils/definitions';
+import { ReactAriaLink } from '@/components/ReactAriaLink';
+
+export const reactAriaUrls = {
+ menuTrigger: 'https://react-aria.adobe.com/Menu#menutrigger',
+ submenuTrigger: 'https://react-aria.adobe.com/Menu#submenutrigger',
+ menu: 'https://react-aria.adobe.com/Menu',
+ menuItem: 'https://react-aria.adobe.com/Menu#menuitem',
+ menuSection: 'https://react-aria.adobe.com/Menu#menusection',
+ listBox: 'https://react-aria.adobe.com/ListBox',
+ listBoxItem: 'https://react-aria.adobe.com/ListBox#listboxitem',
+};
} code={preview} />
@@ -54,100 +65,103 @@ import { MenuDefinition } from '../../../utils/definitions';
### Triggers
-- `MenuTrigger` is a wrapper component that combines a button or other trigger element with a menu displayed in a popover.
-- `SubmenuTrigger` is a wrapper component that combines a `MenuItem` with a menu displayed in a popover.
+- `MenuTrigger` combines a trigger element with a menu popover.
+- `SubmenuTrigger` combines a `MenuItem` with a nested submenu.
### Containers
-- `Menu` is a container component that contains a list of menu items or sections.
-- `MenuListBox` is a container component that contains a list of menu items or sections.
-- `MenuAutocomplete` is a container component that contains a list of menu items or sections.
-- `MenuAutocompleteListbox` is a container component that contains a list of menu items or sections.
+- `Menu` contains menu items or sections.
+- `MenuListBox` supports selection with checkmarks.
+- `MenuAutocomplete` adds a search input to filter items.
+- `MenuAutocompleteListbox` combines search with selection.
### Items
-- `MenuItem` is an individual interactive item in the menu.
-- `MenuListBoxItem` is an individual interactive item in the menu list box.
+- `MenuItem` is an interactive action in the menu.
+- `MenuListBoxItem` is a selectable item in a list box.
-### Separators
+### Grouping
-- `MenuSeparator` is a component that renders a horizontal line to separate menu items.
-- `MenuSection` is a component that renders a section in the menu.
+- `MenuSection` groups items with a title.
+- `MenuSeparator` adds a horizontal divider.
## API reference
### MenuTrigger
-`MenuTrigger` accepts exactly two children: the first child should be the trigger element, and second child should be
-one of the menu containers containing the menu.
+Accepts two children: the trigger element and a menu container.
+
+
### SubmenuTrigger
-The `SubmenuTrigger` accepts exactly two children: the first child should be the `MenuItem` which triggers opening
-of the submenu, and second child should be one of the menu containers containing the submenu.
+Accepts two children: a `MenuItem` and a menu container.
-### Menu
+
-`Menu` is a container component that contains a list of menu items or sections.
+### Menu
-### MenuListBox
+
-`MenuListBox` is a container component that contains a list of menu items or sections.
+### MenuListBox
-### MenuAutocomplete
+
-`MenuAutocomplete` is a container component that contains a list of menu items or sections.
+### MenuAutocomplete
-### MenuAutocompleteListbox
+
-`MenuAutocompleteListbox` is a container component that contains a list of menu items or sections.
+### MenuAutocompleteListbox
-### MenuItem
+
-`MenuItem` is an individual interactive item in the menu.
+### MenuItem
-### MenuListBoxItem
+
-`MenuListBoxItem` is an individual interactive item in the menu list box.
+### MenuListBoxItem
-### MenuSection
+
-`MenuSection` is a component that renders a section in the menu.
+### MenuSection
+
+
### MenuSeparator
-`MenuSeparator` is a component that renders a horizontal line to separate menu items.
-
+
+
## Examples
### Nested navigation
-You can nest menus to create a more complex navigation structure. It is important to use the `placement` prop to ensure
-the submenu is displayed in the correct position. The best practice is to use the `right top` placement for the submenu.
+Submenus open to the right of their parent item.
}
code={submenu}
@@ -155,24 +169,18 @@ the submenu is displayed in the correct position. The best practice is to use th
### With icons
-You can use the `iconStart` prop to add an icon to the menu item.
-
-} code={icons} />
+} code={icons} />
### With links
-You can use the `href` prop to add a link to the menu item. This is using our router provider under the hood
-to work for both internal and external links.
+The `href` prop works with both internal and external links.
-} code={links} />
+} code={links} />
### With sections
-You can use the `MenuSection` component to add a section to the menu.
-
}
code={sections}
@@ -180,11 +188,8 @@ You can use the `MenuSection` component to add a section to the menu.
### With separators
-You can use the `MenuSeparator` component to add a separator to the menu.
-
}
code={separators}
@@ -192,11 +197,8 @@ You can use the `MenuSeparator` component to add a separator to the menu.
### With autocomplete
-You can use the `MenuAutocomplete` component to add a autocomplete to the menu.
-
}
code={autocomplete}
@@ -204,24 +206,19 @@ You can use the `MenuAutocomplete` component to add a autocomplete to the menu.
### With list box
-You can use the `MenuListBox` component to add a list box to the menu.
-
}
code={autocompleteListbox}
/>
-### With list box with multiple selection
+### Multiple selection
-You can use the `MenuListBox` component to add a list box to the menu. You can also use the `selectionMode` prop to
-allow multiple selection.
+Set `selectionMode="multiple"` to allow multiple selections.
}
code={autocompleteListboxMultiple}
diff --git a/docs-ui/src/app/components/menu/props-definition.ts b/docs-ui/src/app/components/menu/props-definition.ts
deleted file mode 100644
index 108bf85e8d..0000000000
--- a/docs-ui/src/app/components/menu/props-definition.ts
+++ /dev/null
@@ -1,241 +0,0 @@
-import {
- classNamePropDefs,
- stylePropDefs,
- type PropDef,
-} from '@/utils/propDefs';
-
-export const menuTriggerPropDefs: Record = {
- isOpen: {
- type: 'boolean',
- },
- defaultOpen: {
- type: 'boolean',
- },
- onOpenChange: {
- type: 'enum',
- values: ['(isOpen: boolean) => void'],
- },
-};
-
-export const submenuTriggerPropDefs: Record = {
- delay: {
- type: 'number',
- default: '200',
- },
-};
-
-export const menuPropDefs: Record = {
- disabledKeys: {
- type: 'enum',
- values: ['Iterable'],
- },
- selectionMode: {
- type: 'enum',
- values: ['none', 'single', 'multiple'],
- },
- selectedKeys: {
- type: 'enum',
- values: ['all', 'Iterable'],
- },
- defaultSelectedKeys: {
- type: 'enum',
- values: ['all', 'Iterable'],
- },
- placement: {
- type: 'enum',
- values: [
- 'top',
- 'bottom',
- 'left',
- 'right',
- 'top start',
- 'top end',
- 'bottom start',
- 'bottom end',
- 'left start',
- 'left end',
- 'right start',
- 'right end',
- ],
- },
- virtualized: {
- type: 'boolean',
- default: 'false',
- },
- maxWidth: {
- type: 'number',
- },
- maxHeight: {
- type: 'number',
- },
- ...classNamePropDefs,
- ...stylePropDefs,
-};
-
-export const menuListBoxPropDefs: Record = {
- disabledKeys: {
- type: 'enum',
- values: ['Iterable'],
- },
- selectionMode: {
- type: 'enum',
- values: ['none', 'single', 'multiple'],
- },
- selectedKeys: {
- type: 'enum',
- values: ['all', 'Iterable'],
- },
- defaultSelectedKeys: {
- type: 'enum',
- values: ['all', 'Iterable'],
- },
- placement: {
- type: 'enum',
- values: [
- 'top',
- 'bottom',
- 'left',
- 'right',
- 'top start',
- 'top end',
- 'bottom start',
- 'bottom end',
- 'left start',
- 'left end',
- 'right start',
- 'right end',
- ],
- },
- virtualized: {
- type: 'boolean',
- default: 'false',
- },
- maxWidth: {
- type: 'number',
- },
- maxHeight: {
- type: 'number',
- },
- ...classNamePropDefs,
- ...stylePropDefs,
-};
-
-export const menuAutocompletePropDefs: Record = {
- placement: {
- type: 'enum',
- values: [
- 'top',
- 'bottom',
- 'left',
- 'right',
- 'top start',
- 'top end',
- 'bottom start',
- 'bottom end',
- 'left start',
- 'left end',
- 'right start',
- 'right end',
- ],
- },
- virtualized: {
- type: 'boolean',
- default: 'false',
- },
- maxWidth: {
- type: 'number',
- },
- maxHeight: {
- type: 'number',
- },
- ...classNamePropDefs,
- ...stylePropDefs,
-};
-
-export const menuAutocompleteListboxPropDefs: Record = {
- placement: {
- type: 'enum',
- values: [
- 'top',
- 'bottom',
- 'left',
- 'right',
- 'top start',
- 'top end',
- 'bottom start',
- 'bottom end',
- 'left start',
- 'left end',
- 'right start',
- 'right end',
- ],
- },
- virtualized: {
- type: 'boolean',
- default: 'false',
- },
- maxWidth: {
- type: 'number',
- },
- maxHeight: {
- type: 'number',
- },
- ...classNamePropDefs,
- ...stylePropDefs,
-};
-
-export const menuItemPropDefs: Record = {
- id: {
- type: 'enum',
- values: ['Key'],
- },
- value: {
- type: 'string',
- },
- textValue: {
- type: 'string',
- },
- isDisabled: {
- type: 'boolean',
- },
- href: {
- type: 'string',
- },
- onAction: {
- type: 'enum',
- values: ['(event) => void'],
- },
- ...classNamePropDefs,
- ...stylePropDefs,
-};
-
-export const menuListBoxItemPropDefs: Record = {
- id: {
- type: 'enum',
- values: ['Key'],
- },
- value: {
- type: 'string',
- },
- textValue: {
- type: 'string',
- },
- isDisabled: {
- type: 'boolean',
- },
- ...classNamePropDefs,
- ...stylePropDefs,
-};
-
-export const menuSectionPropDefs: Record = {
- title: {
- type: 'string',
- },
- ...classNamePropDefs,
- ...stylePropDefs,
-};
-
-export const menuSeparatorPropDefs: Record = {
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/docs-ui/src/app/components/menu/props-definition.tsx b/docs-ui/src/app/components/menu/props-definition.tsx
new file mode 100644
index 0000000000..5e11b22737
--- /dev/null
+++ b/docs-ui/src/app/components/menu/props-definition.tsx
@@ -0,0 +1,364 @@
+import { classNamePropDefs, type PropDef } from '@/utils/propDefs';
+import { Chip } from '@/components/Chip';
+
+export const menuTriggerPropDefs: Record = {
+ isOpen: {
+ type: 'boolean',
+ description: 'Controlled open state of the menu.',
+ },
+ defaultOpen: {
+ type: 'boolean',
+ description: 'Whether the menu is open by default.',
+ },
+ onOpenChange: {
+ type: 'enum',
+ values: ['(isOpen: boolean) => void'],
+ description: 'Handler called when the open state changes.',
+ },
+};
+
+export const submenuTriggerPropDefs: Record = {
+ delay: {
+ type: 'number',
+ default: '200',
+ description: 'Delay in milliseconds before the submenu opens on hover.',
+ },
+};
+
+export const menuPropDefs: Record = {
+ placement: {
+ type: 'enum',
+ values: [
+ 'top',
+ 'bottom',
+ 'left',
+ 'right',
+ 'top start',
+ 'top end',
+ 'bottom start',
+ 'bottom end',
+ 'left start',
+ 'left end',
+ 'right start',
+ 'right end',
+ ],
+ description: 'Position of the menu relative to the trigger.',
+ },
+ onAction: {
+ type: 'enum',
+ values: ['(key: Key) => void'],
+ description:
+ 'Handler called when an item is activated. Receives the item key.',
+ },
+ selectionMode: {
+ type: 'enum',
+ values: ['none', 'single', 'multiple'],
+ description: 'How items can be selected.',
+ },
+ selectedKeys: {
+ type: 'enum',
+ values: ['Iterable'],
+ description: 'Controlled selected keys.',
+ },
+ defaultSelectedKeys: {
+ type: 'enum',
+ values: ['Iterable'],
+ description: 'Default selected keys for uncontrolled usage.',
+ },
+ disabledKeys: {
+ type: 'enum',
+ values: ['Iterable'],
+ description: 'Keys of items that are disabled.',
+ },
+ virtualized: {
+ type: 'boolean',
+ default: false,
+ description: 'Enable virtualization for large lists.',
+ },
+ maxWidth: {
+ type: 'string',
+ description: 'Maximum width of the menu popover.',
+ },
+ maxHeight: {
+ type: 'string',
+ description: 'Maximum height of the menu popover.',
+ },
+ ...classNamePropDefs,
+};
+
+export const menuListBoxPropDefs: Record = {
+ placement: {
+ type: 'enum',
+ values: [
+ 'top',
+ 'bottom',
+ 'left',
+ 'right',
+ 'top start',
+ 'top end',
+ 'bottom start',
+ 'bottom end',
+ 'left start',
+ 'left end',
+ 'right start',
+ 'right end',
+ ],
+ description: 'Position of the list box relative to the trigger.',
+ },
+ selectionMode: {
+ type: 'enum',
+ values: ['none', 'single', 'multiple'],
+ description: 'How items can be selected.',
+ },
+ selectedKeys: {
+ type: 'enum',
+ values: ['Iterable'],
+ description: 'Controlled selected keys.',
+ },
+ defaultSelectedKeys: {
+ type: 'enum',
+ values: ['Iterable'],
+ description: 'Default selected keys for uncontrolled usage.',
+ },
+ onSelectionChange: {
+ type: 'enum',
+ values: ['(keys: Selection) => void'],
+ description: 'Handler called when selection changes.',
+ },
+ disabledKeys: {
+ type: 'enum',
+ values: ['Iterable'],
+ description: 'Keys of items that are disabled.',
+ },
+ virtualized: {
+ type: 'boolean',
+ default: false,
+ description: 'Enable virtualization for large lists.',
+ },
+ maxWidth: {
+ type: 'string',
+ description: 'Maximum width of the list box popover.',
+ },
+ maxHeight: {
+ type: 'string',
+ description: 'Maximum height of the list box popover.',
+ },
+ ...classNamePropDefs,
+};
+
+export const menuAutocompletePropDefs: Record = {
+ placeholder: {
+ type: 'string',
+ description: 'Placeholder text for the search input.',
+ },
+ placement: {
+ type: 'enum',
+ values: [
+ 'top',
+ 'bottom',
+ 'left',
+ 'right',
+ 'top start',
+ 'top end',
+ 'bottom start',
+ 'bottom end',
+ 'left start',
+ 'left end',
+ 'right start',
+ 'right end',
+ ],
+ description: 'Position of the menu relative to the trigger.',
+ },
+ onAction: {
+ type: 'enum',
+ values: ['(key: Key) => void'],
+ description:
+ 'Handler called when an item is activated. Receives the item key.',
+ },
+ selectionMode: {
+ type: 'enum',
+ values: ['none', 'single', 'multiple'],
+ description: 'How items can be selected.',
+ },
+ selectedKeys: {
+ type: 'enum',
+ values: ['Iterable'],
+ description: 'Controlled selected keys.',
+ },
+ defaultSelectedKeys: {
+ type: 'enum',
+ values: ['Iterable'],
+ description: 'Default selected keys for uncontrolled usage.',
+ },
+ disabledKeys: {
+ type: 'enum',
+ values: ['Iterable'],
+ description: 'Keys of items that are disabled.',
+ },
+ onSelectionChange: {
+ type: 'enum',
+ values: ['(keys: Selection) => void'],
+ description: 'Handler called when selection changes.',
+ },
+ virtualized: {
+ type: 'boolean',
+ default: false,
+ description: 'Enable virtualization for large lists.',
+ },
+ maxWidth: {
+ type: 'string',
+ description: 'Maximum width of the menu popover.',
+ },
+ maxHeight: {
+ type: 'string',
+ description: 'Maximum height of the menu popover.',
+ },
+ ...classNamePropDefs,
+};
+
+export const menuAutocompleteListboxPropDefs: Record = {
+ placeholder: {
+ type: 'string',
+ description: 'Placeholder text for the search input.',
+ },
+ placement: {
+ type: 'enum',
+ values: [
+ 'top',
+ 'bottom',
+ 'left',
+ 'right',
+ 'top start',
+ 'top end',
+ 'bottom start',
+ 'bottom end',
+ 'left start',
+ 'left end',
+ 'right start',
+ 'right end',
+ ],
+ description: 'Position of the list box relative to the trigger.',
+ },
+ selectionMode: {
+ type: 'enum',
+ values: ['none', 'single', 'multiple'],
+ description: 'How items can be selected.',
+ },
+ selectedKeys: {
+ type: 'enum',
+ values: ['Iterable'],
+ description: 'Controlled selected keys.',
+ },
+ defaultSelectedKeys: {
+ type: 'enum',
+ values: ['Iterable'],
+ description: 'Default selected keys for uncontrolled usage.',
+ },
+ onSelectionChange: {
+ type: 'enum',
+ values: ['(keys: Selection) => void'],
+ description: 'Handler called when selection changes.',
+ },
+ virtualized: {
+ type: 'boolean',
+ default: false,
+ description: 'Enable virtualization for large lists.',
+ },
+ maxWidth: {
+ type: 'string',
+ description: 'Maximum width of the list box popover.',
+ },
+ maxHeight: {
+ type: 'string',
+ description: 'Maximum height of the list box popover.',
+ },
+ ...classNamePropDefs,
+};
+
+export const menuItemPropDefs: Record = {
+ children: {
+ type: 'enum',
+ values: ['ReactNode'],
+ required: true,
+ description: 'Content displayed in the menu item.',
+ },
+ id: {
+ type: 'enum',
+ values: ['Key'],
+ description: 'Unique key for the item.',
+ },
+ iconStart: {
+ type: 'enum',
+ values: ['ReactNode'],
+ description: 'Icon displayed before the item content.',
+ },
+ color: {
+ type: 'enum',
+ values: ['primary', 'danger'],
+ description: (
+ <>
+ Color variant. Use danger for destructive actions.
+ >
+ ),
+ },
+ href: {
+ type: 'string',
+ description: 'URL to navigate to when the item is clicked.',
+ },
+ isDisabled: {
+ type: 'boolean',
+ description: 'Whether the item is disabled.',
+ },
+ textValue: {
+ type: 'string',
+ description: 'Text used for typeahead and accessibility.',
+ },
+ onAction: {
+ type: 'enum',
+ values: ['() => void'],
+ description: 'Handler called when the item is activated.',
+ },
+ ...classNamePropDefs,
+};
+
+export const menuListBoxItemPropDefs: Record = {
+ children: {
+ type: 'enum',
+ values: ['ReactNode'],
+ required: true,
+ description: 'Content displayed in the list box item.',
+ },
+ id: {
+ type: 'enum',
+ values: ['Key'],
+ description: 'Unique key for the item.',
+ },
+ textValue: {
+ type: 'string',
+ description: 'Text used for typeahead and accessibility.',
+ },
+ isDisabled: {
+ type: 'boolean',
+ description: 'Whether the item is disabled.',
+ },
+ ...classNamePropDefs,
+};
+
+export const menuSectionPropDefs: Record = {
+ title: {
+ type: 'string',
+ required: true,
+ description: 'Heading displayed above the section.',
+ },
+ children: {
+ type: 'enum',
+ values: ['ReactNode'],
+ required: true,
+ description: 'Menu items within the section.',
+ },
+ ...classNamePropDefs,
+};
+
+export const menuSeparatorPropDefs: Record = {
+ ...classNamePropDefs,
+};
diff --git a/docs-ui/src/app/components/menu/snippets.ts b/docs-ui/src/app/components/menu/snippets.ts
index 3434f58328..ee7e77e8c2 100644
--- a/docs-ui/src/app/components/menu/snippets.ts
+++ b/docs-ui/src/app/components/menu/snippets.ts
@@ -9,12 +9,23 @@ export const usage = `import { MenuTrigger, Menu, MenuItem } from '@backstage/ui
`;
export const preview = `
-
+
-
-
-
-
+
+
+
+
+ }>Share
+ }>Feedback
+
+
+ }>Settings
+
+
+
+
+
+
`;
@@ -24,7 +35,7 @@ export const submenu = `
-
+
@@ -36,9 +47,9 @@ export const submenu = `
export const icons = `
- }>New File
- }>New Folder
- }>New Image
+ }>New File
+ }>New Folder
+ }>New Image
`;
@@ -77,24 +88,32 @@ export const links = `
`;
-export const autocomplete = `
-
-
-
-`;
+export const autocomplete = `
+
+
+
+
+
+
+`;
-export const autocompleteListbox = `
- Option 1
- Option 2
- Option 3
-`;
+export const autocompleteListbox = `
+
+
+ Option 1
+ Option 2
+ Option 3
+
+`;
-export const autocompleteListboxMultiple = `
- Option 1
- Option 2
- Option 3
-`;
+export const autocompleteListboxMultiple = `
+
+
+ Option 1
+ Option 2
+ Option 3
+
+`;
diff --git a/docs-ui/src/app/components/password-field/components.tsx b/docs-ui/src/app/components/password-field/components.tsx
index 9f0b4de233..a9f96b9267 100644
--- a/docs-ui/src/app/components/password-field/components.tsx
+++ b/docs-ui/src/app/components/password-field/components.tsx
@@ -2,6 +2,7 @@
import { PasswordField } from '../../../../../packages/ui/src/components/PasswordField/PasswordField';
import { Flex } from '../../../../../packages/ui/src/components/Flex/Flex';
+import { RiLockLine } from '@remixicon/react';
export const WithLabel = () => {
return (
@@ -15,7 +16,11 @@ export const WithLabel = () => {
export const Sizes = () => {
return (
-
+
@@ -32,3 +37,27 @@ export const WithDescription = () => {
/>
);
};
+
+export const WithIcon = () => {
+ return (
+ }
+ style={{ maxWidth: '300px' }}
+ />
+ );
+};
+
+export const Validation = () => {
+ return (
+
+ );
+};
diff --git a/docs-ui/src/app/components/password-field/page.mdx b/docs-ui/src/app/components/password-field/page.mdx
index 5cd556e2fb..2b79425453 100644
--- a/docs-ui/src/app/components/password-field/page.mdx
+++ b/docs-ui/src/app/components/password-field/page.mdx
@@ -1,22 +1,35 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
-import { inputPropDefs } from './props-definition';
+import { passwordFieldPropDefs } from './props-definition';
import {
passwordFieldUsageSnippet,
withLabelSnippet,
sizesSnippet,
withDescriptionSnippet,
+ withIconSnippet,
+ validationSnippet,
} from './snippets';
-import { WithLabel, Sizes, WithDescription } from './components';
+import {
+ WithLabel,
+ Sizes,
+ WithDescription,
+ WithIcon,
+ Validation,
+} from './components';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { CodeBlock } from '@/components/CodeBlock';
import { PasswordFieldDefinition } from '../../../utils/definitions';
+import { ReactAriaLink } from '@/components/ReactAriaLink';
+
+export const reactAriaUrls = {
+ textField: 'https://react-aria.adobe.com/TextField',
+};
+
+
+
## Examples
### Sizes
-We support two different sizes: `small`, `medium`.
-
-} code={sizesSnippet} />
+} code={sizesSnippet} />
### With description
-Here's a simple PasswordField with a description.
-
}
code={withDescriptionSnippet}
/>
+### With icon
+
+}
+ code={withIconSnippet}
+/>
+
+### Validation
+
+}
+ code={validationSnippet}
+/>
+
diff --git a/docs-ui/src/app/components/password-field/props-definition.ts b/docs-ui/src/app/components/password-field/props-definition.ts
deleted file mode 100644
index aeeecc690c..0000000000
--- a/docs-ui/src/app/components/password-field/props-definition.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import {
- classNamePropDefs,
- stylePropDefs,
- type PropDef,
-} from '@/utils/propDefs';
-
-export const inputPropDefs: Record = {
- size: {
- type: 'enum',
- values: ['small', 'medium'],
- default: 'small',
- responsive: true,
- },
- label: {
- type: 'string',
- },
- description: {
- type: 'string',
- },
- name: {
- type: 'string',
- required: true,
- },
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/docs-ui/src/app/components/password-field/props-definition.tsx b/docs-ui/src/app/components/password-field/props-definition.tsx
new file mode 100644
index 0000000000..63f0f56d25
--- /dev/null
+++ b/docs-ui/src/app/components/password-field/props-definition.tsx
@@ -0,0 +1,77 @@
+import { classNamePropDefs, type PropDef } from '@/utils/propDefs';
+import { Chip } from '@/components/Chip';
+
+export const passwordFieldPropDefs: Record = {
+ size: {
+ type: 'enum',
+ values: ['small', 'medium'],
+ default: 'small',
+ responsive: true,
+ description: (
+ <>
+ Visual size of the input. Use small for dense layouts,{' '}
+ medium for prominent fields.
+ >
+ ),
+ },
+ label: {
+ type: 'string',
+ description: 'Visible label displayed above the input.',
+ },
+ secondaryLabel: {
+ type: 'string',
+ description: (
+ <>
+ Secondary text shown next to the label. If not provided and isRequired
+ is true, displays Required.
+ >
+ ),
+ },
+ description: {
+ type: 'string',
+ description: 'Help text displayed below the label.',
+ },
+ icon: {
+ type: 'enum',
+ values: ['ReactNode'],
+ description: 'Icon rendered before the input.',
+ },
+ placeholder: {
+ type: 'string',
+ description: 'Text displayed when the input is empty.',
+ },
+ name: {
+ type: 'string',
+ description: 'Form field name for submission.',
+ },
+ isRequired: {
+ type: 'boolean',
+ description: 'Whether the field is required for form submission.',
+ },
+ isDisabled: {
+ type: 'boolean',
+ description: 'Whether the input is disabled.',
+ },
+ isReadOnly: {
+ type: 'boolean',
+ description: 'Whether the input is read-only.',
+ },
+ value: {
+ type: 'string',
+ description: 'Controlled value of the input.',
+ },
+ defaultValue: {
+ type: 'string',
+ description: 'Default value for uncontrolled usage.',
+ },
+ onChange: {
+ type: 'enum',
+ values: ['(value: string) => void'],
+ description: 'Handler called when the input value changes.',
+ },
+ isInvalid: {
+ type: 'boolean',
+ description: 'Whether the field is in an invalid state.',
+ },
+ ...classNamePropDefs,
+};
diff --git a/docs-ui/src/app/components/password-field/snippets.ts b/docs-ui/src/app/components/password-field/snippets.ts
index 6151b0891a..6bc4b9760b 100644
--- a/docs-ui/src/app/components/password-field/snippets.ts
+++ b/docs-ui/src/app/components/password-field/snippets.ts
@@ -1,15 +1,15 @@
export const passwordFieldUsageSnippet = `import { PasswordField } from '@backstage/ui';
-`;
+`;
export const withLabelSnippet = ``;
-export const sizesSnippet = `
-
-
+export const sizesSnippet = `
+
+
`;
export const withDescriptionSnippet = ``;
+
+export const withIconSnippet = `}
+/>`;
+
+export const validationSnippet = ``;
diff --git a/docs-ui/src/app/components/popover/components.tsx b/docs-ui/src/app/components/popover/components.tsx
index 1deaab90f5..4135d88059 100644
--- a/docs-ui/src/app/components/popover/components.tsx
+++ b/docs-ui/src/app/components/popover/components.tsx
@@ -3,15 +3,57 @@
import { Popover } from '../../../../../packages/ui/src/components/Popover/Popover';
import { DialogTrigger } from '../../../../../packages/ui/src/components/Dialog/Dialog';
import { Button } from '../../../../../packages/ui/src/components/Button/Button';
+import { Text } from '../../../../../packages/ui/src/components/Text/Text';
+import { Flex } from '../../../../../packages/ui/src/components/Flex/Flex';
export const Default = () => {
return (
-
+ Popover content
+
+
+ );
+};
+
+export const Placement = () => {
+ return (
+
+
+
+
+ Content above trigger
+
+
+
+
+
+ Content to the right
+
+
+
+
+
+ Content below trigger
+
+
+
+
+
+ Content to the left
+
+
+
+ );
+};
+
+export const HideArrow = () => {
+ return (
+
+
+
+ Popover without arrow
);
diff --git a/docs-ui/src/app/components/popover/page.mdx b/docs-ui/src/app/components/popover/page.mdx
index 5a3b0e990c..0d546cdc05 100644
--- a/docs-ui/src/app/components/popover/page.mdx
+++ b/docs-ui/src/app/components/popover/page.mdx
@@ -1,17 +1,28 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { CodeBlock } from '@/components/CodeBlock';
+import { ReactAriaLink } from '@/components/ReactAriaLink';
import { dialogTriggerPropDefs, popoverPropDefs } from './props-definition';
-import { popoverUsageSnippet, defaultSnippet } from './snippets';
-import { Default } from './components';
+import {
+ popoverUsageSnippet,
+ defaultSnippet,
+ placementSnippet,
+ hideArrowSnippet,
+} from './snippets';
+import { Default, Placement, HideArrow } from './components';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { PopoverDefinition } from '../../../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
+export const reactAriaUrls = {
+ dialogTrigger: 'https://react-aria.adobe.com/Modal#dialogtrigger',
+ popover: 'https://react-aria.adobe.com/Popover',
+};
+
} code={defaultSnippet} />
@@ -24,16 +35,40 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
### DialogTrigger
-The trigger component that wraps both the trigger button and the popover content.
+Wraps the trigger button and popover content.
+
+
### Popover
-The main popover component that displays floating content with automatic positioning.
+Displays floating content with automatic positioning.
+
+
+## Examples
+
+### Placement
+
+}
+ code={placementSnippet}
+/>
+
+### Hidden arrow
+
+}
+ code={hideArrowSnippet}
+/>
+
diff --git a/docs-ui/src/app/components/popover/props-definition.ts b/docs-ui/src/app/components/popover/props-definition.ts
index 4ca6f5a76e..563bfa7d66 100644
--- a/docs-ui/src/app/components/popover/props-definition.ts
+++ b/docs-ui/src/app/components/popover/props-definition.ts
@@ -1,55 +1,47 @@
-import {
- childrenPropDefs,
- classNamePropDefs,
- stylePropDefs,
-} from '@/utils/propDefs';
+import { childrenPropDefs, classNamePropDefs } from '@/utils/propDefs';
import type { PropDef } from '@/utils/propDefs';
export const dialogTriggerPropDefs: Record = {
- isDisabled: {
+ defaultOpen: {
type: 'boolean',
- },
- delay: {
- type: 'number',
- default: '0',
- },
- closeDelay: {
- type: 'number',
- default: '0',
+ description: 'Whether the popover is open by default (uncontrolled).',
},
isOpen: {
type: 'boolean',
+ description: 'Whether the popover is open (controlled).',
},
- defaultOpen: {
- type: 'boolean',
+ onOpenChange: {
+ type: 'enum',
+ values: ['(isOpen: boolean) => void'],
+ description: 'Handler called when the popover open state changes.',
},
...childrenPropDefs,
};
export const popoverPropDefs: Record = {
- triggerRef: {
- type: 'enum',
- values: ['RefObject'],
- },
- isEntering: {
- type: 'boolean',
- },
- isExiting: {
- type: 'boolean',
- },
placement: {
type: 'enum',
values: ['top', 'right', 'bottom', 'left'],
- },
- containerPadding: {
- type: 'number',
- default: '12',
+ description:
+ 'The placement of the popover relative to the trigger element.',
},
offset: {
type: 'number',
default: '8',
+ description:
+ 'The distance in pixels between the popover and the trigger element.',
+ },
+ containerPadding: {
+ type: 'number',
+ default: '12',
+ description:
+ 'The minimum distance in pixels from the edge of the viewport.',
+ },
+ hideArrow: {
+ type: 'boolean',
+ default: 'false',
+ description: 'Whether to hide the arrow pointing to the trigger element.',
},
...childrenPropDefs,
...classNamePropDefs,
- ...stylePropDefs,
};
diff --git a/docs-ui/src/app/components/popover/snippets.ts b/docs-ui/src/app/components/popover/snippets.ts
index 2de0d664a4..986b33c770 100644
--- a/docs-ui/src/app/components/popover/snippets.ts
+++ b/docs-ui/src/app/components/popover/snippets.ts
@@ -1,19 +1,49 @@
-export const popoverUsageSnippet = `import { DialogTrigger, Popover, Button } from '@backstage/ui';
+export const popoverUsageSnippet = `import { DialogTrigger, Popover, Button, Text } from '@backstage/ui';
-
+ Popover content
`;
export const defaultSnippet = `
-
+ Popover content
+
+`;
+
+export const placementSnippet = `
+
+
+
+ Content above trigger
+
+
+
+
+
+ Content to the right
+
+
+
+
+
+ Content below trigger
+
+
+
+
+
+ Content to the left
+
+
+`;
+
+export const hideArrowSnippet = `
+
+
+ Popover without arrow
`;
diff --git a/docs-ui/src/app/components/radio-group/page.mdx b/docs-ui/src/app/components/radio-group/page.mdx
index 9f76c0be9c..5cc296b959 100644
--- a/docs-ui/src/app/components/radio-group/page.mdx
+++ b/docs-ui/src/app/components/radio-group/page.mdx
@@ -1,6 +1,7 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
-import { radioGroupPropDefs } from './props-definition';
+import { ReactAriaLink } from '@/components/ReactAriaLink';
+import { radioGroupPropDefs, radioPropDefs } from './props-definition';
import {
radioGroupUsageSnippet,
defaultSnippet,
@@ -24,9 +25,13 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
import { CodeBlock } from '@/components/CodeBlock';
import { RadioGroupDefinition } from '../../../utils/definitions';
+export const reactAriaUrls = {
+ radioGroup: 'https://react-aria.adobe.com/RadioGroup',
+};
+
} code={defaultSnippet} />
@@ -37,66 +42,73 @@ import { RadioGroupDefinition } from '../../../utils/definitions';
## API reference
+### RadioGroup
+
+
+
+### Radio
+
+Individual radio button within a group.
+
+
+
+
+
## Examples
### Horizontal
-Here's a simple TextField with a description.
-
}
code={horizontalSnippet}
/>
### Disabled
-You can disable the entire radio group by adding the `isDisabled` prop to the `RadioGroup` component.
-
}
code={disabledSnippet}
/>
-### Disabled Single radio
-
-You can disable a single radio by adding the `isDisabled` prop to the `Radio` component.
+### Disabled single radio
}
code={disabledSingleSnippet}
/>
### Validation
-Here's an example of a radio group with errors.
-
}
code={validationSnippet}
/>
### Read only
-You can make the radio group read only by adding the `isReadOnly` prop to the `RadioGroup` component.
-
}
code={readOnlySnippet}
/>
diff --git a/docs-ui/src/app/components/radio-group/props-definition.ts b/docs-ui/src/app/components/radio-group/props-definition.ts
deleted file mode 100644
index e66f9f5841..0000000000
--- a/docs-ui/src/app/components/radio-group/props-definition.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import {
- classNamePropDefs,
- stylePropDefs,
- type PropDef,
-} from '@/utils/propDefs';
-
-export const radioGroupPropDefs: Record = {
- size: {
- type: 'enum',
- values: ['small', 'medium'],
- default: 'small',
- responsive: true,
- },
- label: {
- type: 'string',
- },
- icon: {
- type: 'enum',
- values: ['ReactNode'],
- },
- description: {
- type: 'string',
- },
- name: {
- type: 'string',
- required: true,
- },
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/docs-ui/src/app/components/radio-group/props-definition.tsx b/docs-ui/src/app/components/radio-group/props-definition.tsx
new file mode 100644
index 0000000000..54da42d93a
--- /dev/null
+++ b/docs-ui/src/app/components/radio-group/props-definition.tsx
@@ -0,0 +1,91 @@
+import {
+ classNamePropDefs,
+ childrenPropDefs,
+ type PropDef,
+} from '@/utils/propDefs';
+import { Chip } from '@/components/Chip';
+
+export const radioGroupPropDefs: Record = {
+ label: {
+ type: 'string',
+ description: 'The visible label for the radio group.',
+ },
+ 'aria-label': {
+ type: 'string',
+ description:
+ 'Accessible label when a visible label is not provided. Either label, aria-label, or aria-labelledby is required.',
+ },
+ 'aria-labelledby': {
+ type: 'string',
+ description:
+ 'ID of an element that labels the radio group. Either label, aria-label, or aria-labelledby is required.',
+ },
+ secondaryLabel: {
+ type: 'string',
+ description: (
+ <>
+ Secondary label text. Defaults to Required when isRequired
+ is true.
+ >
+ ),
+ },
+ description: {
+ type: 'string',
+ description: 'Helper text displayed below the label.',
+ },
+ orientation: {
+ type: 'enum',
+ values: ['horizontal', 'vertical'],
+ default: 'vertical',
+ description: 'The axis the radio buttons should align with.',
+ },
+ value: {
+ type: 'string',
+ description: 'The current value (controlled).',
+ },
+ defaultValue: {
+ type: 'string',
+ description: 'The default value (uncontrolled).',
+ },
+ onChange: {
+ type: 'enum',
+ values: ['(value: string) => void'],
+ description: 'Handler called when the value changes.',
+ },
+ name: {
+ type: 'string',
+ description: 'The name of the radio group for form submission.',
+ },
+ isDisabled: {
+ type: 'boolean',
+ description: 'Whether all radio buttons in the group are disabled.',
+ },
+ isReadOnly: {
+ type: 'boolean',
+ description: 'Whether the radio group is read-only.',
+ },
+ isRequired: {
+ type: 'boolean',
+ description: 'Whether a selection is required before form submission.',
+ },
+ isInvalid: {
+ type: 'boolean',
+ description: 'Whether the radio group is in an invalid state.',
+ },
+ ...childrenPropDefs,
+ ...classNamePropDefs,
+};
+
+export const radioPropDefs: Record = {
+ value: {
+ type: 'string',
+ required: true,
+ description: 'The value of the radio button.',
+ },
+ isDisabled: {
+ type: 'boolean',
+ description: 'Whether this radio button is disabled.',
+ },
+ ...childrenPropDefs,
+ ...classNamePropDefs,
+};
diff --git a/docs-ui/src/app/components/radio-group/snippets.ts b/docs-ui/src/app/components/radio-group/snippets.ts
index 1f7be7007e..d0b14ef5c4 100644
--- a/docs-ui/src/app/components/radio-group/snippets.ts
+++ b/docs-ui/src/app/components/radio-group/snippets.ts
@@ -1,6 +1,9 @@
-export const radioGroupUsageSnippet = `import { RadioGroup } from '@backstage/ui';
+export const radioGroupUsageSnippet = `import { RadioGroup, Radio } from '@backstage/ui';
-`;
+
+ Option 1
+ Option 2
+`;
export const defaultSnippet = `
Bulbasaur
diff --git a/docs-ui/src/app/components/search-field/components.tsx b/docs-ui/src/app/components/search-field/components.tsx
index 33a6ba3540..84a4c6018d 100644
--- a/docs-ui/src/app/components/search-field/components.tsx
+++ b/docs-ui/src/app/components/search-field/components.tsx
@@ -5,20 +5,15 @@ import { Flex } from '../../../../../packages/ui/src/components/Flex/Flex';
export const WithLabel = () => {
return (
-
+
);
};
export const Sizes = () => {
return (
-
-
+
+
);
};
@@ -26,10 +21,8 @@ export const Sizes = () => {
export const WithDescription = () => {
return (
);
@@ -37,21 +30,9 @@ export const WithDescription = () => {
export const StartCollapsed = () => {
return (
-
-
-
-
-
+
+
+
);
};
diff --git a/docs-ui/src/app/components/search-field/page.mdx b/docs-ui/src/app/components/search-field/page.mdx
index f5b7054361..425dbc8f62 100644
--- a/docs-ui/src/app/components/search-field/page.mdx
+++ b/docs-ui/src/app/components/search-field/page.mdx
@@ -1,5 +1,6 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
+import { ReactAriaLink } from '@/components/ReactAriaLink';
import { searchFieldPropDefs } from './props-definition';
import {
searchFieldUsageSnippet,
@@ -20,9 +21,13 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
import { CodeBlock } from '@/components/CodeBlock';
import { SearchFieldDefinition } from '../../../utils/definitions';
+export const reactAriaUrls = {
+ searchField: 'https://react-aria.adobe.com/SearchField',
+};
+
+
+
## Examples
### Sizes
-We support two different sizes: `small`, `medium`.
-
-} code={sizesSnippet} />
+}
+ code={sizesSnippet}
+ layout="side-by-side"
+/>
### With description
-Here's a simple SearchField with a description.
+Add context below the input with the `description` prop.
}
code={withDescriptionSnippet}
+ layout="side-by-side"
/>
### Collapsible
-You can make the SearchField collapsible by setting the `startCollapsed` prop to `true`.
+Use `startCollapsed` for space-constrained layouts where the field expands on focus.
}
code={startCollapsedSnippet}
+ layout="side-by-side"
/>
diff --git a/docs-ui/src/app/components/search-field/props-definition.ts b/docs-ui/src/app/components/search-field/props-definition.ts
deleted file mode 100644
index 1b2f7a36e7..0000000000
--- a/docs-ui/src/app/components/search-field/props-definition.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import {
- classNamePropDefs,
- stylePropDefs,
- type PropDef,
-} from '@/utils/propDefs';
-
-export const searchFieldPropDefs: Record = {
- size: {
- type: 'enum',
- values: ['small', 'medium'],
- default: 'small',
- responsive: true,
- },
- label: {
- type: 'string',
- },
- icon: {
- type: 'enum',
- values: ['ReactNode'],
- },
- description: {
- type: 'string',
- },
- name: {
- type: 'string',
- required: true,
- },
- startCollapsed: {
- type: 'boolean',
- default: 'false',
- },
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/docs-ui/src/app/components/search-field/props-definition.tsx b/docs-ui/src/app/components/search-field/props-definition.tsx
new file mode 100644
index 0000000000..58d2cb0988
--- /dev/null
+++ b/docs-ui/src/app/components/search-field/props-definition.tsx
@@ -0,0 +1,85 @@
+import { classNamePropDefs, type PropDef } from '@/utils/propDefs';
+import { Chip } from '@/components/Chip';
+
+export const searchFieldPropDefs: Record = {
+ size: {
+ type: 'enum',
+ values: ['small', 'medium'],
+ default: 'small',
+ responsive: true,
+ description: (
+ <>
+ Visual size of the input. Use small for inline or dense
+ layouts, medium for standalone fields.
+ >
+ ),
+ },
+ label: {
+ type: 'string',
+ description: 'The visible label for the search field.',
+ },
+ secondaryLabel: {
+ type: 'string',
+ description: (
+ <>
+ Secondary label text. Defaults to Required when isRequired
+ is true.
+ >
+ ),
+ },
+ description: {
+ type: 'string',
+ description: 'Helper text displayed below the label.',
+ },
+ placeholder: {
+ type: 'string',
+ default: 'Search',
+ description: 'Placeholder text shown when the field is empty.',
+ },
+ icon: {
+ type: 'enum',
+ values: ['ReactNode', 'false'],
+ description:
+ 'Icon displayed before the input. Set to false to hide the icon.',
+ },
+ startCollapsed: {
+ type: 'boolean',
+ default: 'false',
+ description:
+ 'Whether the search field starts in a collapsed state. Expands on focus.',
+ },
+ name: {
+ type: 'string',
+ description: 'The name of the input for form submission.',
+ },
+ value: {
+ type: 'string',
+ description: 'The current value (controlled).',
+ },
+ defaultValue: {
+ type: 'string',
+ description: 'The default value (uncontrolled).',
+ },
+ onChange: {
+ type: 'enum',
+ values: ['(value: string) => void'],
+ description: 'Handler called when the value changes.',
+ },
+ isDisabled: {
+ type: 'boolean',
+ description: 'Whether the search field is disabled.',
+ },
+ isReadOnly: {
+ type: 'boolean',
+ description: 'Whether the search field is read-only.',
+ },
+ isRequired: {
+ type: 'boolean',
+ description: 'Whether a value is required before form submission.',
+ },
+ isInvalid: {
+ type: 'boolean',
+ description: 'Whether the search field is in an invalid state.',
+ },
+ ...classNamePropDefs,
+};
diff --git a/docs-ui/src/app/components/search-field/snippets.ts b/docs-ui/src/app/components/search-field/snippets.ts
index 924542520a..e54bca1bf4 100644
--- a/docs-ui/src/app/components/search-field/snippets.ts
+++ b/docs-ui/src/app/components/search-field/snippets.ts
@@ -2,34 +2,19 @@ export const searchFieldUsageSnippet = `import { SearchField } from '@backstage/
`;
-export const withLabelSnippet = ``;
+export const withLabelSnippet = ``;
export const sizesSnippet = `
-
-
+
+
`;
export const withDescriptionSnippet = ``;
-export const startCollapsedSnippet = `
-
-
-
-
-
+export const startCollapsedSnippet = `
+
+
`;
diff --git a/docs-ui/src/app/components/select/page.mdx b/docs-ui/src/app/components/select/page.mdx
index 08e29c4a29..8e7df65ef9 100644
--- a/docs-ui/src/app/components/select/page.mdx
+++ b/docs-ui/src/app/components/select/page.mdx
@@ -1,6 +1,7 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { CodeBlock } from '@/components/CodeBlock';
+import { ReactAriaLink } from '@/components/ReactAriaLink';
import {
Preview,
WithLabelAndDescription,
@@ -31,9 +32,13 @@ import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { SelectDefinition } from '../../../utils/definitions';
+export const reactAriaUrls = {
+ select: 'https://react-aria.adobe.com/Select',
+};
+
+
+
## Examples
-### With Label and description
-
-Select component with label and description.
+### Label and description
}
code={selectDescriptionSnippet}
@@ -67,36 +71,26 @@ Select component with label and description.
### Sizes
-Here's a view when the selects have different sizes.
-
}
code={selectSizesSnippet}
/>
-### With Icon
-
-Here's a view when the select has an icon.
+### With icon
}
-code={selectIconSnippet}
+ layout="side-by-side"
+ open
+ preview={}
+ code={selectIconSnippet}
/>
### Disabled
-Here's a view when the select is disabled.
-
}
code={selectDisabledSnippet}
@@ -104,11 +98,8 @@ Here's a view when the select is disabled.
### Disabled options
-You can disable specific options within the Select component using `disabledKeys`.
-
}
code={selectDisabledOptionsSnippet}
@@ -116,35 +107,30 @@ You can disable specific options within the Select component using `disabledKeys
### Searchable
-Here's a view when the select has searchable filtering.
+Enable filtering with the `searchable` prop.
}
code={selectSearchableSnippet}
/>
-### Multiple Selection
-
-Here's a view when the select allows multiple selections.
+### Multiple selection
}
code={selectMultipleSnippet}
/>
-### Searchable with Multiple Selection
+### Searchable multiple
-Here's a view when the select combines search and multiple selection.
+Combine search and multiple selection.
}
code={selectSearchableMultipleSnippet}
@@ -152,7 +138,7 @@ Here's a view when the select combines search and multiple selection.
### Responsive
-Here's a view when the select is responsive.
+Size can change at different breakpoints.
diff --git a/docs-ui/src/app/components/select/props-definition.ts b/docs-ui/src/app/components/select/props-definition.ts
deleted file mode 100644
index e2953134f2..0000000000
--- a/docs-ui/src/app/components/select/props-definition.ts
+++ /dev/null
@@ -1,111 +0,0 @@
-import {
- classNamePropDefs,
- stylePropDefs,
- type PropDef,
-} from '@/utils/propDefs';
-
-export const selectPropDefs: Record = {
- label: {
- type: 'string',
- responsive: false,
- },
- description: {
- type: 'string',
- responsive: false,
- },
- name: {
- type: 'string',
- responsive: false,
- required: true,
- },
- options: {
- type: 'enum',
- values: ['Array<{ value: string, label: string }>'],
- required: true,
- },
- selectionMode: {
- type: 'enum',
- values: ['single', 'multiple'],
- default: 'single',
- responsive: false,
- },
- placeholder: {
- type: 'string',
- default: 'Select an item',
- responsive: false,
- },
- icon: {
- type: 'enum',
- values: ['ReactNode'],
- responsive: false,
- },
- value: {
- type: 'enum',
- values: ['string', 'string[]'],
- responsive: false,
- description:
- 'Selected value (controlled). String for single selection, array for multiple.',
- },
- defaultValue: {
- type: 'enum',
- values: ['string', 'string[]'],
- responsive: false,
- description:
- 'Initial value (uncontrolled). String for single selection, array for multiple.',
- },
- size: {
- type: 'enum',
- values: ['small', 'medium'],
- default: 'small',
- responsive: true,
- },
- isOpen: {
- type: 'boolean',
- responsive: false,
- },
- defaultOpen: {
- type: 'boolean',
- responsive: false,
- },
- disabledKeys: {
- type: 'enum',
- values: ['Iterable'],
- responsive: false,
- },
- isDisabled: {
- type: 'boolean',
- responsive: false,
- },
- isRequired: {
- type: 'boolean',
- responsive: false,
- },
- isInvalid: {
- type: 'boolean',
- responsive: false,
- },
- onOpenChange: {
- type: 'enum',
- values: ['(isOpen: boolean) => void'],
- responsive: false,
- },
- onSelectionChange: {
- type: 'enum',
- values: ['(key: Key | null) => void', '(keys: Selection) => void'],
- responsive: false,
- description:
- 'Handler called when selection changes. Single mode: receives Key | null. Multiple mode: receives Selection.',
- },
- searchable: {
- type: 'boolean',
- default: 'false',
- responsive: false,
- },
- searchPlaceholder: {
- type: 'string',
- default: 'Search...',
- responsive: false,
- },
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/docs-ui/src/app/components/select/props-definition.tsx b/docs-ui/src/app/components/select/props-definition.tsx
new file mode 100644
index 0000000000..0d1ab22202
--- /dev/null
+++ b/docs-ui/src/app/components/select/props-definition.tsx
@@ -0,0 +1,137 @@
+import {
+ classNamePropDefs,
+ stylePropDefs,
+ type PropDef,
+} from '@/utils/propDefs';
+import { Chip } from '@/components/Chip';
+
+export const selectPropDefs: Record = {
+ options: {
+ type: 'complex',
+ description: 'Array of options to display in the dropdown.',
+ complexType: {
+ name: 'SelectOption[]',
+ properties: {
+ value: {
+ type: 'string',
+ required: true,
+ description: 'Unique value for the option.',
+ },
+ label: {
+ type: 'string',
+ required: true,
+ description: 'Display text for the option.',
+ },
+ disabled: {
+ type: 'boolean',
+ required: false,
+ description: 'Whether the option is disabled.',
+ },
+ },
+ },
+ },
+ selectionMode: {
+ type: 'enum',
+ values: ['single', 'multiple'],
+ default: 'single',
+ description: 'Single or multiple selection mode.',
+ },
+ value: {
+ type: 'enum',
+ values: ['string', 'string[]'],
+ description:
+ 'Controlled selected value. String for single, array for multiple.',
+ },
+ defaultValue: {
+ type: 'enum',
+ values: ['string', 'string[]'],
+ description:
+ 'Initial value for uncontrolled usage. String for single, array for multiple.',
+ },
+ onSelectionChange: {
+ type: 'enum',
+ values: ['(key: Key | null) => void', '(keys: Selection) => void'],
+ description: 'Called when selection changes.',
+ },
+ label: {
+ type: 'string',
+ description: 'Visible label above the select.',
+ },
+ secondaryLabel: {
+ type: 'string',
+ description: (
+ <>
+ Secondary text shown next to the label. If not provided and isRequired
+ is true, displays Required.
+ >
+ ),
+ },
+ description: {
+ type: 'string',
+ description: 'Helper text displayed below the label.',
+ },
+ placeholder: {
+ type: 'string',
+ default: 'Select an option',
+ description: 'Text shown when no option is selected.',
+ },
+ size: {
+ type: 'enum',
+ values: ['small', 'medium'],
+ default: 'small',
+ responsive: true,
+ description: 'Visual size of the select field.',
+ },
+ icon: {
+ type: 'enum',
+ values: ['ReactNode'],
+ description: 'Icon displayed before the selected value.',
+ },
+ searchable: {
+ type: 'boolean',
+ default: false,
+ description: 'Enables search/filter functionality in the dropdown.',
+ },
+ searchPlaceholder: {
+ type: 'string',
+ default: 'Search...',
+ description:
+ 'Placeholder text for the search input when searchable is true.',
+ },
+ isOpen: {
+ type: 'boolean',
+ description: 'Controlled open state. Use with onOpenChange.',
+ },
+ defaultOpen: {
+ type: 'boolean',
+ description: 'Initial open state for uncontrolled usage.',
+ },
+ onOpenChange: {
+ type: 'enum',
+ values: ['(isOpen: boolean) => void'],
+ description: 'Called when the dropdown opens or closes.',
+ },
+ isDisabled: {
+ type: 'boolean',
+ description: 'Prevents user interaction when true.',
+ },
+ disabledKeys: {
+ type: 'enum',
+ values: ['Iterable'],
+ description: 'Keys of options that should be disabled.',
+ },
+ isRequired: {
+ type: 'boolean',
+ description: 'Marks the field as required for form validation.',
+ },
+ isInvalid: {
+ type: 'boolean',
+ description: 'Displays the select in an error state.',
+ },
+ name: {
+ type: 'string',
+ description: 'Form field name for form submission.',
+ },
+ ...classNamePropDefs,
+ ...stylePropDefs,
+};
diff --git a/docs-ui/src/app/components/select/snippets.ts b/docs-ui/src/app/components/select/snippets.ts
index 6af7851195..ca16481eaa 100644
--- a/docs-ui/src/app/components/select/snippets.ts
+++ b/docs-ui/src/app/components/select/snippets.ts
@@ -45,7 +45,7 @@ export const selectSizesSnippet = `
`;
export const selectDisabledSnippet = ``;
diff --git a/docs-ui/src/app/components/skeleton/components.tsx b/docs-ui/src/app/components/skeleton/components.tsx
index 277aa17ae3..9f83a013db 100644
--- a/docs-ui/src/app/components/skeleton/components.tsx
+++ b/docs-ui/src/app/components/skeleton/components.tsx
@@ -2,30 +2,43 @@
import { Skeleton } from '../../../../../packages/ui/src/components/Skeleton/Skeleton';
import { Flex } from '../../../../../packages/ui/src/components/Flex/Flex';
+import { Box } from '../../../../../packages/ui/src/components/Box/Box';
-export const Demo1 = () => {
+export const CardPlaceholder = () => {
return (
-
-
+
-
-
-
-
-
-
+
+
+
+
+
+ );
+};
+
+export const AvatarWithText = () => {
+ return (
+
+
+
+
+
+
+
-
+
);
};
-export const Demo2 = () => {
+export const Rounded = () => {
return (
-
-
-
-
-
+
+
+
+
+
+
+
);
};
diff --git a/docs-ui/src/app/components/skeleton/page.mdx b/docs-ui/src/app/components/skeleton/page.mdx
index 42f8975b5a..b7d218af89 100644
--- a/docs-ui/src/app/components/skeleton/page.mdx
+++ b/docs-ui/src/app/components/skeleton/page.mdx
@@ -2,8 +2,13 @@ import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { CodeBlock } from '@/components/CodeBlock';
import { skeletonPropDefs } from './props-definition';
-import { skeletonUsageSnippet, demo1Snippet, demo2Snippet } from './snippets';
-import { Demo1, Demo2 } from './components';
+import {
+ skeletonUsageSnippet,
+ cardPlaceholderSnippet,
+ avatarWithTextSnippet,
+ roundedSnippet,
+} from './snippets';
+import { CardPlaceholder, AvatarWithText, Rounded } from './components';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
@@ -14,7 +19,12 @@ import { SkeletonDefinition } from '../../../utils/definitions';
description="Use to show a placeholder while content is loading."
/>
-} code={demo2Snippet} />
+}
+ code={cardPlaceholderSnippet}
+/>
## Usage
@@ -22,21 +32,29 @@ import { SkeletonDefinition } from '../../../utils/definitions';
## API reference
+Skeleton extends standard HTML div attributes.
+
## Examples
-### Demo 1
+### Rounded
-You can use a mix of different sizes to create a more complex skeleton.
+}
+ code={roundedSnippet}
+/>
-} code={demo1Snippet} open />
+### Avatar with text
-### Demo 2
-
-You can use a mix of different sizes to create a more complex skeleton.
-
-} code={demo2Snippet} open />
+}
+ code={avatarWithTextSnippet}
+/>
diff --git a/docs-ui/src/app/components/skeleton/props-definition.ts b/docs-ui/src/app/components/skeleton/props-definition.ts
index bb80024bcd..8c14c4c0ec 100644
--- a/docs-ui/src/app/components/skeleton/props-definition.ts
+++ b/docs-ui/src/app/components/skeleton/props-definition.ts
@@ -6,19 +6,22 @@ import {
export const skeletonPropDefs: Record = {
width: {
- type: 'number',
+ type: 'string',
default: '80',
- responsive: false,
+ description:
+ 'The width of the skeleton. Accepts a number (pixels) or CSS string value.',
},
height: {
- type: 'number',
+ type: 'string',
default: '24',
- responsive: false,
+ description:
+ 'The height of the skeleton. Accepts a number (pixels) or CSS string value.',
},
rounded: {
type: 'boolean',
default: 'false',
- responsive: false,
+ description:
+ 'Whether to apply fully rounded corners (for circular shapes).',
},
...classNamePropDefs,
...stylePropDefs,
diff --git a/docs-ui/src/app/components/skeleton/snippets.ts b/docs-ui/src/app/components/skeleton/snippets.ts
index 981ac5ff0c..84bbec1616 100644
--- a/docs-ui/src/app/components/skeleton/snippets.ts
+++ b/docs-ui/src/app/components/skeleton/snippets.ts
@@ -1,26 +1,30 @@
-export const skeletonUsageSnippet = `import { Flex, Skeleton } from '@backstage/ui';
+export const skeletonUsageSnippet = `import { Skeleton } from '@backstage/ui';
-
-
-
-
-`;
+`;
-export const demo1Snippet = `
-
+export const cardPlaceholderSnippet = `
-
-
-
-
-
-
+
+
+
+
+`;
+
+export const avatarWithTextSnippet = `
+
+
+
+
+
+
-`;
+`;
-export const demo2Snippet = `
-
-
-
-`;
+export const roundedSnippet = `
+
+
+
+
+
+`;
diff --git a/docs-ui/src/app/components/switch/page.mdx b/docs-ui/src/app/components/switch/page.mdx
index 494a046f47..1f893aea36 100644
--- a/docs-ui/src/app/components/switch/page.mdx
+++ b/docs-ui/src/app/components/switch/page.mdx
@@ -1,6 +1,7 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { CodeBlock } from '@/components/CodeBlock';
+import { ReactAriaLink } from '@/components/ReactAriaLink';
import { switchPropDefs } from './props-definition';
import { snippetUsage, defaultSnippet, disabledSnippet } from './snippets';
import { Default, Disabled } from './components';
@@ -9,9 +10,13 @@ import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { SwitchDefinition } from '../../../utils/definitions';
+export const reactAriaUrls = {
+ switch: 'https://react-aria.adobe.com/Switch',
+};
+
} code={defaultSnippet} />
@@ -24,19 +29,13 @@ import { SwitchDefinition } from '../../../utils/definitions';
+
+
## Examples
### Disabled
-A switch can be disabled using the `isDisabled` prop.
-
-}
- code={disabledSnippet}
-/>
+} code={disabledSnippet} />
diff --git a/docs-ui/src/app/components/switch/props-definition.ts b/docs-ui/src/app/components/switch/props-definition.ts
index 769c93aac8..eeab3e64b8 100644
--- a/docs-ui/src/app/components/switch/props-definition.ts
+++ b/docs-ui/src/app/components/switch/props-definition.ts
@@ -2,66 +2,44 @@ import { classNamePropDefs, stylePropDefs } from '@/utils/propDefs';
import type { PropDef } from '@/utils/propDefs';
export const switchPropDefs: Record = {
- autoFocus: {
- type: 'boolean',
- },
- defaultSelected: {
- type: 'boolean',
- },
- ...classNamePropDefs,
- isDisabled: {
- type: 'boolean',
- },
- isReadOnly: {
- type: 'boolean',
+ label: {
+ type: 'string',
+ description: 'Text label displayed next to the switch.',
},
isSelected: {
type: 'boolean',
+ description:
+ 'Controlled selected state. Use with onChange for controlled behavior.',
},
- label: {
- type: 'string',
- },
- name: {
- type: 'string',
+ defaultSelected: {
+ type: 'boolean',
+ description: 'Initial selected state for uncontrolled usage.',
},
onChange: {
type: 'enum',
values: ['(isSelected: boolean) => void'],
+ description: 'Called when the switch state changes.',
},
- onFocus: {
- type: 'enum',
- values: ['(e: FocusEvent) => void'],
+ isDisabled: {
+ type: 'boolean',
+ description: 'Prevents user interaction when true.',
},
- onBlur: {
- type: 'enum',
- values: ['(e: FocusEvent) => void'],
+ isReadOnly: {
+ type: 'boolean',
+ description: 'Makes the switch non-interactive but still focusable.',
},
- onFocusChange: {
- type: 'enum',
- values: ['(isFocused: boolean) => void'],
+ name: {
+ type: 'string',
+ description: 'Form field name for form submission.',
},
- onKeyDown: {
- type: 'enum',
- values: ['(e: KeyboardEvent) => void'],
- },
- onKeyUp: {
- type: 'enum',
- values: ['(e: KeyboardEvent) => void'],
- },
- onHoverStart: {
- type: 'enum',
- values: ['(e: HoverEvent) => void'],
- },
- onHoverEnd: {
- type: 'enum',
- values: ['(e: HoverEvent) => void'],
- },
- onHoverChange: {
- type: 'enum',
- values: ['(isHovered: boolean) => void'],
- },
- ...stylePropDefs,
value: {
type: 'string',
+ description: 'Form field value submitted when selected.',
},
+ autoFocus: {
+ type: 'boolean',
+ description: 'Focuses the switch on mount.',
+ },
+ ...classNamePropDefs,
+ ...stylePropDefs,
};
diff --git a/docs-ui/src/app/components/table/page.mdx b/docs-ui/src/app/components/table/page.mdx
index e39f89f5e9..6eb280cbe2 100644
--- a/docs-ui/src/app/components/table/page.mdx
+++ b/docs-ui/src/app/components/table/page.mdx
@@ -13,7 +13,6 @@ import {
PrimitivesExample,
} from './components';
import {
- tablePropsColumns,
tableReturnColumns,
useTableOptionsPropDefs,
useTableReturnPropDefs,
@@ -163,7 +162,7 @@ You can also disable specific rows from being clicked using `getIsDisabled`:
-} />
+} code={tableRowActionsHrefSnippet} />
### Empty State
@@ -225,7 +224,7 @@ The `useTable` hook manages data fetching, pagination, sorting, and filtering.
**Options**
-
+
**Return Value**
@@ -235,27 +234,27 @@ The `useTable` hook manages data fetching, pagination, sorting, and filtering.
The main table component.
-
+
### ColumnConfig
-
+
### CellText
-
+
### CellProfile
-
+
### TablePagination
-
+
### Primitives
@@ -263,7 +262,7 @@ Low-level components for building custom table layouts.
#### TableRoot
-
+
@@ -277,13 +276,13 @@ Low-level components for building custom table layouts.
#### Column
-
+
#### Row
-
+
diff --git a/docs-ui/src/app/components/table/props-definition.tsx b/docs-ui/src/app/components/table/props-definition.tsx
index 58c8f197a7..e653f67c8a 100644
--- a/docs-ui/src/app/components/table/props-definition.tsx
+++ b/docs-ui/src/app/components/table/props-definition.tsx
@@ -9,13 +9,6 @@ import { Chip } from '@/components/Chip';
// PropsTable Column Configuration (Table docs use description instead of responsive)
// =============================================================================
-export const tablePropsColumns = [
- { key: 'prop' as const, width: '15%' },
- { key: 'type' as const, width: '25%' },
- { key: 'default' as const, width: '15%' },
- { key: 'description' as const, width: '45%' },
-];
-
// For return values (no default column)
export const tableReturnColumns = [
{ key: 'prop' as const, width: '15%' },
diff --git a/docs-ui/src/app/components/tabs/page.mdx b/docs-ui/src/app/components/tabs/page.mdx
index 0b9478a503..6925127943 100644
--- a/docs-ui/src/app/components/tabs/page.mdx
+++ b/docs-ui/src/app/components/tabs/page.mdx
@@ -1,6 +1,7 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { CodeBlock } from '@/components/CodeBlock';
+import { ReactAriaLink } from '@/components/ReactAriaLink';
import {
tabsPropDefs,
tabListPropDefs,
@@ -13,6 +14,7 @@ import {
defaultSelectedKeySnippet,
disabledTabsSnippet,
orientationSnippet,
+ urlNavigationSnippet,
} from './snippets';
import {
Default,
@@ -25,9 +27,13 @@ import { Theming } from '@/components/Theming';
import { TabsDefinition } from '../../../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
+export const reactAriaUrls = {
+ tabs: 'https://react-aria.adobe.com/Tabs',
+};
+
} code={defaultSnippet} />
@@ -40,40 +46,70 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
### Tabs
-Groups the tabs and the corresponding panels. Renders a `` element.
+Container that groups the tab list and panels.
+
+
+### TabList
+
+Container for the tab buttons.
+
+
+
+
+
### Tab
-An individual interactive tab button that toggles the corresponding panel. Renders a `