Merge branch 'master' into docs-ui

This commit is contained in:
Charles de Dreuille
2025-07-22 16:05:38 +01:00
35 changed files with 597 additions and 1010 deletions
File diff suppressed because one or more lines are too long
@@ -1,71 +0,0 @@
import { PropsTable } from '@/components/PropsTable';
import { HeadingSnippet } from '@/snippets/stories-snippets';
import { Snippet } from '@/components/Snippet';
import { CodeBlock } from '@/components/CodeBlock';
import {
headingPropDefs,
headingUsageSnippet,
headingDefaultSnippet,
headingVariantsSnippet,
headingTruncateSnippet,
headingResponsiveSnippet,
} from './heading.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
title="Heading"
description="Headings are used to structure the content of your page."
/>
<Snippet
py={4}
preview={<HeadingSnippet story="Title1" />}
code={headingDefaultSnippet}
/>
## Usage
<CodeBlock code={headingUsageSnippet} />
## API reference
<PropsTable data={headingPropDefs} />
## Examples
### All variants
The `Heading` component has a `variant` prop that can be used to change the
appearance of the heading.
<Snippet
py={2}
open
preview={<HeadingSnippet story="AllVariants" />}
code={headingVariantsSnippet}
/>
### Truncate
The `Heading` component has a `truncate` prop that can be used to truncate the
heading.
<Snippet
py={2}
open
preview={<HeadingSnippet story="Truncate" />}
code={headingTruncateSnippet}
/>
### Responsive
You can also use the `variant` prop to change the appearance of the text based
on the screen size.
<CodeBlock code={headingResponsiveSnippet} />
<Theming component="Heading" />
<ChangelogComponent component="heading" />
@@ -1,48 +0,0 @@
import {
classNamePropDefs,
stylePropDefs,
type PropDef,
} from '@/utils/propDefs';
export const headingPropDefs: Record<string, PropDef> = {
variant: {
type: 'enum',
values: ['display', 'title1', 'title2', 'title3', 'title4', 'title5'],
responsive: true,
},
children: {
type: 'enum',
values: ['ReactNode'],
responsive: false,
},
truncate: {
type: 'boolean',
default: 'false',
},
...classNamePropDefs,
...stylePropDefs,
};
export const headingUsageSnippet = `import { Heading } from '@backstage/ui';
<Heading />`;
export const headingDefaultSnippet = `<Heading variant="title1">Hello World!</Heading>`;
export const headingVariantsSnippet = `<Flex direction="column" gap="4">
<Heading variant="display">Display</Heading>
<Heading variant="title1">Title 1</Heading>
<Heading variant="title2">Title 2</Heading>
<Heading variant="title3">Title 3</Heading>
<Heading variant="title4">Title 4</Heading>
</Flex>`;
export const headingTruncateSnippet = `<Heading style={{ maxWidth: '400px' }} truncate>
A man looks at a painting in a museum and says, "Brothers and sisters I
have none, but that man's father is my father's son." Who is
in the painting?
</Heading>`;
export const headingResponsiveSnippet = `<Heading variant={{ initial: 'title2', lg: 'title1' }}>
Responsive heading
</Heading>`;
+34
View File
@@ -12,6 +12,9 @@ import {
linkDefaultSnippet,
linkVariantsSnippet,
linkWeightsSnippet,
linkColorsSnippet,
linkRouterSnippet,
linkTruncateSnippet,
} from './link.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
@@ -37,6 +40,15 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
<PropsTable data={linkPropDefs} />
## Router Integration
The `Link` component handles both internal and external navigation. It automatically detects whether the provided URL is internal (relative path) or external (absolute URL with protocol) and renders the appropriate element:
- **Internal routes**: Uses `react-router-dom`'s `Link` component for client-side navigation
- **External URLs**: Renders a standard `<a>` element for traditional navigation
<CodeBlock language="tsx" code={linkRouterSnippet} />
## Examples
### Variants
@@ -63,6 +75,28 @@ Here's a view when links have different weights.
code={linkWeightsSnippet}
/>
### Colors
Here's a view when links have different colors.
<Snippet
align="center"
py={4}
open
preview={<LinkSnippet story="AllColors" />}
code={linkColorsSnippet}
/>
### Truncate
The `Link` component has a `truncate` prop that can be used to truncate the text.
<Snippet
open
preview={<LinkSnippet story="Truncate" />}
code={linkTruncateSnippet}
/>
<Theming component="Link" />
<ChangelogComponent component="link" />
+49 -11
View File
@@ -5,13 +5,22 @@ import {
} from '@/utils/propDefs';
export const linkPropDefs: Record<string, PropDef> = {
to: {
href: {
type: 'string',
},
variant: {
type: 'enum',
values: ['subtitle', 'body', 'caption', 'label'],
default: 'body',
values: [
'title-large',
'title-medium',
'title-small',
'title-x-small',
'body-large',
'body-medium',
'body-small',
'body-x-small',
],
default: 'body-medium',
responsive: true,
},
weight: {
@@ -20,24 +29,53 @@ export const linkPropDefs: Record<string, PropDef> = {
default: 'regular',
responsive: true,
},
color: {
type: 'enum',
values: ['primary', 'secondary', 'danger', 'warning', 'success'],
default: 'primary',
responsive: true,
},
...classNamePropDefs,
...stylePropDefs,
};
export const linkUsageSnippet = `import { Link } from '@backstage/ui';
<Link href="https://backstage.io">Sign up for Backstage</Link>`;
<Link href="/sign-up">Sign up for Backstage</Link>`;
export const linkDefaultSnippet = `<Link href="https://backstage.io">Sign up for Backstage</Link>`;
export const linkDefaultSnippet = `<Link href="/">Sign up for Backstage</Link>`;
export const linkVariantsSnippet = `<Flex gap="4" direction="column">
<Link href="https://ui.backstage.io" variant="subtitle" />
<Link href="https://ui.backstage.io" variant="body" />
<Link href="https://ui.backstage.io" variant="caption" />
<Link href="https://ui.backstage.io" variant="label" />
<Link href="/" variant="title-large">...</Link>
<Link href="/" variant="title-medium">...</Link>
<Link href="/" variant="title-small">...</Link>
<Link href="/" variant="title-x-small">...</Link>
<Link href="/" variant="body-large">...</Link>
<Link href="/" variant="body-medium">...</Link>
<Link href="/" variant="body-small">...</Link>
<Link href="/" variant="body-x-small">...</Link>
</Flex>`;
export const linkWeightsSnippet = `<Flex gap="4" direction="column">
<Link href="https://ui.backstage.io" weight="regular" />
<Link href="https://ui.backstage.io" weight="bold" />
<Link href="/" weight="regular" />
<Link href="/" weight="bold" />
</Flex>`;
export const linkColorsSnippet = `<Flex gap="4" direction="column">
<Link href="/" color="primary">I am primary</Link>
<Link href="/" color="secondary">I am secondary</Link>
<Link href="/" color="danger">I am danger</Link>
<Link href="/" color="warning">I am warning</Link>
<Link href="/" color="success">I am success</Link>
</Flex>`;
export const linkRouterSnippet = `import { Link } from '@backstage/ui';
// Internal route
<Link href="/home">Home</Link>
// External URL
<Link href="https://backstage.io">Backstage</Link>
`;
export const linkTruncateSnippet = `<Link href="/" truncate>...</Link>`;
+13 -2
View File
@@ -10,6 +10,7 @@ import {
textWeightsSnippet,
textTruncateSnippet,
textResponsiveSnippet,
textColorsSnippet,
} from './text.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
@@ -58,10 +59,20 @@ appearance of the text.
code={textWeightsSnippet}
/>
### All colors
The `Text` component has a `color` prop that can be used to change the
appearance of the text.
<Snippet
open
preview={<TextSnippet story="AllColors" />}
code={textColorsSnippet}
/>
### Truncate
The `Text` component has a `truncate` prop that can be used to truncate the
text.
The `Text` component has a `truncate` prop that can be used to truncate the text.
<Snippet
open
+85 -36
View File
@@ -6,9 +6,38 @@ import {
import type { PropDef } from '@/utils/propDefs';
export const textPropDefs: Record<string, PropDef> = {
as: {
type: 'enum',
values: [
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'p',
'span',
'label',
'div',
'strong',
'em',
'small',
],
default: 'span',
responsive: true,
},
variant: {
type: 'enum',
values: ['display', 'title1', 'title2', 'title3', 'title4', 'title5'],
values: [
'title-large',
'title-medium',
'title-small',
'title-x-small',
'body-large',
'body-medium',
'body-small',
'body-x-small',
],
responsive: true,
},
weight: {
@@ -16,6 +45,12 @@ export const textPropDefs: Record<string, PropDef> = {
values: ['regular', 'bold'],
responsive: true,
},
color: {
type: 'enum',
values: ['primary', 'secondary', 'danger', 'warning', 'success'],
default: 'primary',
responsive: true,
},
truncate: {
type: 'boolean',
default: 'false',
@@ -36,46 +71,60 @@ export const textDefaultSnippet = `<Text style={{ maxWidth: '600px' }}>
</Text>`;
export const textVariantsSnippet = `<Flex direction="column" gap="4">
<Text variant="subtitle" style={{ maxWidth: '600px' }}>
A man looks at a painting in a museum and says, "Brothers and sisters I
have none, but that man&apos;s father is my father&apos;s son." Who is
in the painting?
</Text>
<Text variant="body" style={{ maxWidth: '600px' }}>
A man looks at a painting in a museum and says, "Brothers and sisters I
have none, but that man&apos;s father is my father&apos;s son." Who is
in the painting?
</Text>
<Text variant="caption" style={{ maxWidth: '600px' }}>
A man looks at a painting in a museum and says, "Brothers and sisters I
have none, but that man&apos;s father is my father&apos;s son." Who is
in the painting?
</Text>
<Text variant="label" style={{ maxWidth: '600px' }}>
A man looks at a painting in a museum and says, "Brothers and sisters I
have none, but that man&apos;s father is my father&apos;s son." Who is
in the painting?
</Text>
<Text variant="title-large">...</Text>
<Text variant="title-medium">...</Text>
<Text variant="title-small">...</Text>
<Text variant="title-x-small">...</Text>
<Text variant="body-large">...</Text>
<Text variant="body-medium">...</Text>
<Text variant="body-small">...</Text>
<Text variant="body-x-small">...</Text>
</Flex>`;
export const textWeightsSnippet = `<Flex direction="column" gap="4">
<Text weight="regular" style={{ maxWidth: '600px' }}>
A man looks at a painting in a museum and says, "Brothers and sisters I
have none, but that man&apos;s father is my father&apos;s son." Who is
in the painting?
</Text>
<Text weight="bold" style={{ maxWidth: '600px' }}>
A man looks at a painting in a museum and says, "Brothers and sisters I
have none, but that man&apos;s father is my father&apos;s son." Who is
in the painting?
</Text>
<Flex>
<Text variant="title-large" weight="regular">A fox</Text>
<Text variant="title-large" weight="bold">A turtle</Text>
</Flex>
<Flex>
<Text variant="title-medium" weight="regular">A fox</Text>
<Text variant="title-medium" weight="bold">A turtle</Text>
</Flex>
<Flex>
<Text variant="title-small" weight="regular">A fox</Text>
<Text variant="title-small" weight="bold">A turtle</Text>
</Flex>
<Flex>
<Text variant="title-x-small" weight="regular">A fox</Text>
<Text variant="title-x-small" weight="bold">A turtle</Text>
</Flex>
<Flex>
<Text variant="body-large" weight="regular">A fox</Text>
<Text variant="body-large" weight="bold">A turtle</Text>
</Flex>
<Flex>
<Text variant="body-medium" weight="regular">A fox</Text>
<Text variant="body-medium" weight="bold">A turtle</Text>
</Flex>
<Flex>
<Text variant="body-small" weight="regular">A fox</Text>
<Text variant="body-small" weight="bold">A turtle</Text>
</Flex>
<Flex>
<Text variant="body-x-small" weight="regular">A fox</Text>
<Text variant="body-x-small" weight="bold">A turtle</Text>
</Flex>
</Flex>`;
export const textTruncateSnippet = `<Text weight="regular" style={{ maxWidth: '600px' }} truncate>
A man looks at a painting in a museum and says, "Brothers and sisters I
have none, but that man&apos;s father is my father&apos;s son." Who is
in the painting?
</Text>`;
export const textColorsSnippet = `<Flex direction="column" gap="4">
<Text color="primary">I am primary</Text>
<Text color="secondary">I am secondary</Text>
<Text color="danger">I am danger</Text>
<Text color="warning">I am warning</Text>
<Text color="success">I am success</Text>
</Flex>`;
export const textTruncateSnippet = `<Text as="p" truncate>...</Text>`;
export const textResponsiveSnippet = `<Text variant={{ initial: 'body', lg: 'subtitle' }}>
Responsive text
-12
View File
@@ -8,7 +8,6 @@ import * as ButtonLinkStories from '../../../packages/ui/src/components/ButtonLi
import * as CheckboxStories from '../../../packages/ui/src/components/Checkbox/Checkbox.stories';
import * as ContainerStories from '../../../packages/ui/src/components/Container/Container.stories';
import * as GridStories from '../../../packages/ui/src/components/Grid/Grid.stories';
import * as HeadingStories from '../../../packages/ui/src/components/Heading/Heading.stories';
import * as IconStories from '../../../packages/ui/src/components/Icon/Icon.stories';
import * as TextFieldStories from '../../../packages/ui/src/components/TextField/TextField.stories';
import * as TextStories from '../../../packages/ui/src/components/Text/Text.stories';
@@ -113,17 +112,6 @@ export const GridSnippet = ({ story }: { story: keyof typeof GridStories }) => {
return StoryComponent ? <StoryComponent /> : null;
};
export const HeadingSnippet = ({
story,
}: {
story: keyof typeof HeadingStories;
}) => {
const stories = composeStories(HeadingStories);
const StoryComponent = stories[story as keyof typeof stories];
return StoryComponent ? <StoryComponent /> : null;
};
export const IconSnippet = ({ story }: { story: keyof typeof IconStories }) => {
const stories = composeStories(IconStories);
const StoryComponent = stories[story as keyof typeof stories];
-5
View File
@@ -101,11 +101,6 @@ export const components: Page[] = [
slug: 'collapsible',
status: 'alpha',
},
{
title: 'Heading',
slug: 'heading',
status: 'alpha',
},
{
title: 'Icon',
slug: 'icon',