Move stories to CSF Next

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
Charles de Dreuille
2025-12-17 19:38:44 +00:00
parent cb29b60d8b
commit 523f320133
37 changed files with 890 additions and 976 deletions
+3 -6
View File
@@ -1,7 +1,6 @@
// This file has been automatically migrated to valid ESM format by Storybook.
import { defineMain } from '@storybook/react-vite/node';
import { fileURLToPath } from 'node:url';
import { createRequire } from 'node:module';
import type { StorybookConfig } from '@storybook/react-vite';
import { join, dirname, posix } from 'path';
@@ -42,7 +41,7 @@ function getAbsolutePath(value: string): any {
return dirname(require.resolve(join(value, 'package.json')));
}
const config: StorybookConfig = {
export default defineMain({
stories,
addons: [
getAbsolutePath('@storybook/addon-links'),
@@ -130,6 +129,4 @@ const config: StorybookConfig = {
return config;
},
};
export default config;
});
+12 -4
View File
@@ -1,8 +1,13 @@
import addonA11y from '@storybook/addon-a11y';
import addonDocs from '@storybook/addon-docs';
import addonThemes from '@storybook/addon-themes';
import addonLinks from '@storybook/addon-links';
import { definePreview } from '@storybook/react-vite';
import React, { useEffect } from 'react';
import { TestApiProvider } from '@backstage/test-utils';
import { Content, AlertDisplay } from '@backstage/core-components';
import { apis } from './support/apis';
import type { Decorator, Preview } from '@storybook/react-vite';
import type { Decorator } from '@storybook/react-vite';
import { useGlobals } from 'storybook/preview-api';
import { UnifiedThemeProvider, themes } from '@backstage/theme';
import { allModes } from './modes';
@@ -16,7 +21,7 @@ import './storybook.css';
// Custom themes
import './themes/spotify.css';
const preview: Preview = {
export default definePreview({
globalTypes: {
themeMode: {
name: 'Theme Mode',
@@ -47,10 +52,12 @@ const preview: Preview = {
},
},
},
initialGlobals: {
themeMode: 'light',
themeName: 'backstage',
},
parameters: {
layout: 'fullscreen',
@@ -102,6 +109,7 @@ const preview: Preview = {
},
},
},
decorators: [
Story => {
const [globals] = useGlobals();
@@ -140,6 +148,6 @@ const preview: Preview = {
);
},
],
};
export default preview;
addons: [addonLinks(), addonThemes(), addonDocs(), addonA11y()],
});
@@ -13,8 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import preview from '../../../../../.storybook/preview';
import {
Accordion,
AccordionTrigger,
@@ -36,15 +35,12 @@ const Content = () => (
</Box>
);
const meta = {
const meta = preview.meta({
title: 'Backstage UI/Accordion',
component: Accordion,
} satisfies Meta<typeof Accordion>;
});
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
export const Default = meta.story({
render: () => (
<Accordion>
<AccordionTrigger title="Toggle Panel" />
@@ -53,9 +49,9 @@ export const Default: Story = {
</AccordionPanel>
</Accordion>
),
};
});
export const WithSubtitle: Story = {
export const WithSubtitle = meta.story({
render: () => (
<Accordion>
<AccordionTrigger
@@ -67,9 +63,9 @@ export const WithSubtitle: Story = {
</AccordionPanel>
</Accordion>
),
};
});
export const CustomTrigger: Story = {
export const CustomTrigger = meta.story({
render: () => (
<Accordion>
<AccordionTrigger>
@@ -87,9 +83,9 @@ export const CustomTrigger: Story = {
</AccordionPanel>
</Accordion>
),
};
});
export const DefaultExpanded: Story = {
export const DefaultExpanded = meta.story({
render: () => (
<Accordion defaultExpanded>
<AccordionTrigger title="Toggle Panel" />
@@ -98,9 +94,9 @@ export const DefaultExpanded: Story = {
</AccordionPanel>
</Accordion>
),
};
});
export const GroupSingleOpen: Story = {
export const GroupSingleOpen = meta.story({
render: () => (
<AccordionGroup>
<Accordion>
@@ -136,9 +132,9 @@ export const GroupSingleOpen: Story = {
</Accordion>
</AccordionGroup>
),
};
});
export const GroupMultipleOpen: Story = {
export const GroupMultipleOpen = meta.story({
render: () => (
<AccordionGroup allowsMultiple>
<Accordion>
@@ -174,4 +170,4 @@ export const GroupMultipleOpen: Story = {
</Accordion>
</AccordionGroup>
),
};
});
@@ -13,36 +13,32 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import preview from '../../../../../.storybook/preview';
import { Avatar } from './index';
import { Flex, Text } from '../..';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/Avatar',
component: Avatar,
} satisfies Meta<typeof Avatar>;
});
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
export const Default = meta.story({
args: {
src: 'https://avatars.githubusercontent.com/u/1540635?v=4',
name: 'Charles de Dreuille',
},
};
});
export const Fallback: Story = {
export const Fallback = meta.story({
args: {
...Default.args,
...Default.input.args,
src: 'https://avatars.githubusercontent.com/u/15406AAAAAAAAA',
},
};
});
export const Sizes: Story = {
export const Sizes = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: args => (
<Flex direction="column" gap="6">
@@ -62,11 +58,11 @@ export const Sizes: Story = {
</Flex>
</Flex>
),
};
});
export const Purpose: Story = {
export const Purpose = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: args => (
<Flex direction="column" gap="4">
@@ -93,4 +89,4 @@ export const Purpose: Story = {
</Flex>
</Flex>
),
};
});
+31 -34
View File
@@ -1,3 +1,4 @@
import preview from '../../../../../.storybook/preview';
/*
* Copyright 2024 The Backstage Authors
*
@@ -15,11 +16,10 @@
*/
import { ReactNode } from 'react';
import type { Meta, StoryObj } from '@storybook/react-vite';
import { Box } from './Box';
import { Flex } from '../Flex';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/Box',
component: Box,
argTypes: {
@@ -31,10 +31,7 @@ const meta = {
control: false,
},
},
} satisfies Meta<typeof Box>;
export default meta;
type Story = StoryObj<typeof meta>;
});
const diagonalStripePattern = (() => {
const svg = `
@@ -47,7 +44,7 @@ const diagonalStripePattern = (() => {
return `data:image/svg+xml,${encodeURIComponent(svg)}`;
})();
export const Default: Story = {
export const Default = meta.story({
args: {
width: '64px',
height: '64px',
@@ -63,10 +60,10 @@ export const Default: Story = {
color: '#2563eb',
},
},
};
});
export const Margin: Story = {
args: { ...Default.args },
export const Margin = meta.story({
args: { ...Default.input.args },
render: args => (
<Flex align="center">
<Box style={{ border: '1px solid red', borderRadius: '5px' }}>
@@ -89,10 +86,10 @@ export const Margin: Story = {
</Box>
</Flex>
),
};
});
export const ResponsiveMargin: Story = {
args: { ...Default.args },
export const ResponsiveMargin = meta.story({
args: { ...Default.input.args },
render: args => (
<Flex align="center">
<Box style={{ border: '1px solid red', borderRadius: '5px' }}>
@@ -115,10 +112,10 @@ export const ResponsiveMargin: Story = {
</Box>
</Flex>
),
};
});
export const CustomMargin: Story = {
args: { ...Default.args },
export const CustomMargin = meta.story({
args: { ...Default.input.args },
render: args => (
<Flex align="center">
<Box style={{ border: '1px solid red', borderRadius: '5px' }}>
@@ -141,10 +138,10 @@ export const CustomMargin: Story = {
</Box>
</Flex>
),
};
});
export const CustomResponsiveMargin: Story = {
args: { ...Default.args },
export const CustomResponsiveMargin = meta.story({
args: { ...Default.input.args },
render: args => (
<Flex align="center">
<Box style={{ border: '1px solid red', borderRadius: '5px' }}>
@@ -167,10 +164,10 @@ export const CustomResponsiveMargin: Story = {
</Box>
</Flex>
),
};
});
export const Padding: Story = {
args: { ...Default.args },
export const Padding = meta.story({
args: { ...Default.input.args },
render: args => (
<Flex align="center">
<Box pl="6" style={{ border: '1px solid red', borderRadius: '5px' }}>
@@ -193,10 +190,10 @@ export const Padding: Story = {
</Box>
</Flex>
),
};
});
export const ResponsivePadding: Story = {
args: { ...Default.args },
export const ResponsivePadding = meta.story({
args: { ...Default.input.args },
render: args => (
<Flex align="center">
<Box
@@ -237,10 +234,10 @@ export const ResponsivePadding: Story = {
</Box>
</Flex>
),
};
});
export const CustomPadding: Story = {
args: { ...Default.args },
export const CustomPadding = meta.story({
args: { ...Default.input.args },
render: args => (
<Flex align="center">
<Box pl="42px" style={{ border: '1px solid red', borderRadius: '5px' }}>
@@ -263,10 +260,10 @@ export const CustomPadding: Story = {
</Box>
</Flex>
),
};
});
export const CustomResponsivePadding: Story = {
args: { ...Default.args },
export const CustomResponsivePadding = meta.story({
args: { ...Default.input.args },
render: args => (
<Flex align="center">
<Box
@@ -307,7 +304,7 @@ export const CustomResponsivePadding: Story = {
</Box>
</Flex>
),
};
});
const CardDisplay = ({ children }: { children?: ReactNode }) => {
return (
@@ -327,7 +324,7 @@ const CardDisplay = ({ children }: { children?: ReactNode }) => {
);
};
export const Display: Story = {
export const Display = meta.story({
render: args => (
<Flex direction="column" align="center">
<Flex>
@@ -346,4 +343,4 @@ export const Display: Story = {
</Box>
</Flex>
),
};
});
@@ -1,5 +1,5 @@
/*
* Copyright 2024 The Backstage Authors
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,15 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import preview from '../../../../../.storybook/preview';
import { Button } from './Button';
import { Flex } from '../Flex';
import { Text } from '../Text';
import { RiArrowRightSLine, RiCloudLine } from '@remixicon/react';
import { useState } from 'react';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/Button',
component: Button,
argTypes: {
@@ -34,18 +33,15 @@ const meta = {
options: ['primary', 'secondary'],
},
},
} satisfies Meta<typeof Button>;
});
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
export const Default = meta.story({
args: {
children: 'Button',
},
};
});
export const Variants: Story = {
export const Variants = meta.story({
args: {
children: 'Button',
},
@@ -69,9 +65,9 @@ export const Variants: Story = {
</Button>
</Flex>
),
};
});
export const Sizes: Story = {
export const Sizes = meta.story({
args: {
children: 'Button',
},
@@ -85,9 +81,9 @@ export const Sizes: Story = {
</Button>
</Flex>
),
};
});
export const WithIcons: Story = {
export const WithIcons = meta.story({
args: {
children: 'Button',
},
@@ -102,9 +98,9 @@ export const WithIcons: Story = {
/>
</Flex>
),
};
});
export const FullWidth: Story = {
export const FullWidth = meta.story({
args: {
children: 'Button',
},
@@ -119,9 +115,9 @@ export const FullWidth: Story = {
/>
</Flex>
),
};
});
export const Disabled: Story = {
export const Disabled = meta.story({
render: () => (
<Flex direction="row" gap="4">
<Button variant="primary" isDisabled>
@@ -135,9 +131,9 @@ export const Disabled: Story = {
</Button>
</Flex>
),
};
});
export const Responsive: Story = {
export const Responsive = meta.story({
args: {
children: 'Button',
variant: {
@@ -149,12 +145,12 @@ export const Responsive: Story = {
sm: 'medium',
},
},
};
});
const variants = ['primary', 'secondary'] as const;
const sizes = ['small', 'medium'] as const;
export const Playground: Story = {
export const Playground = meta.story({
args: {
children: 'Button',
},
@@ -212,9 +208,9 @@ export const Playground: Story = {
))}
</Flex>
),
};
});
export const Loading: Story = {
export const Loading = meta.story({
render: () => {
const [isLoading, setIsLoading] = useState(false);
@@ -231,9 +227,9 @@ export const Loading: Story = {
</Button>
);
},
};
});
export const LoadingVariants: Story = {
export const LoadingVariants = meta.story({
render: () => (
<Flex direction="column" gap="4">
<Text>Primary</Text>
@@ -289,4 +285,4 @@ export const LoadingVariants: Story = {
</Flex>
</Flex>
),
};
});
@@ -1,5 +1,5 @@
/*
* Copyright 2024 The Backstage Authors
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,15 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import preview from '../../../../../.storybook/preview';
import { ButtonIcon } from './ButtonIcon';
import { Flex } from '../Flex';
import { Text } from '../Text';
import { RiCloudLine } from '@remixicon/react';
import { useState } from 'react';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/ButtonIcon',
component: ButtonIcon,
argTypes: {
@@ -34,16 +33,13 @@ const meta = {
options: ['primary', 'secondary'],
},
},
} satisfies Meta<typeof ButtonIcon>;
});
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
export const Default = meta.story({
render: () => <ButtonIcon icon={<RiCloudLine />} />,
};
});
export const Variants: Story = {
export const Variants = meta.story({
render: () => (
<Flex align="center" gap="2">
<ButtonIcon icon={<RiCloudLine />} variant="primary" />
@@ -51,18 +47,18 @@ export const Variants: Story = {
<ButtonIcon icon={<RiCloudLine />} variant="tertiary" />
</Flex>
),
};
});
export const Sizes: Story = {
export const Sizes = meta.story({
render: () => (
<Flex align="center" gap="2">
<ButtonIcon icon={<RiCloudLine />} size="small" />
<ButtonIcon icon={<RiCloudLine />} size="medium" />
</Flex>
),
};
});
export const Disabled: Story = {
export const Disabled = meta.story({
render: () => (
<Flex direction="row" gap="2">
<ButtonIcon isDisabled icon={<RiCloudLine />} variant="primary" />
@@ -70,9 +66,9 @@ export const Disabled: Story = {
<ButtonIcon isDisabled icon={<RiCloudLine />} variant="tertiary" />
</Flex>
),
};
});
export const Responsive: Story = {
export const Responsive = meta.story({
args: {
variant: {
initial: 'primary',
@@ -84,9 +80,9 @@ export const Responsive: Story = {
},
},
render: args => <ButtonIcon {...args} icon={<RiCloudLine />} />,
};
});
export const Loading: Story = {
export const Loading = meta.story({
render: () => {
const [isLoading, setIsLoading] = useState(false);
@@ -106,9 +102,9 @@ export const Loading: Story = {
/>
);
},
};
});
export const LoadingVariants: Story = {
export const LoadingVariants = meta.story({
render: () => (
<Flex direction="column" gap="4">
<Text>Primary</Text>
@@ -172,4 +168,4 @@ export const LoadingVariants: Story = {
</Flex>
</Flex>
),
};
});
@@ -1,3 +1,4 @@
import preview from '../../../../../.storybook/preview';
/*
* Copyright 2024 The Backstage Authors
*
@@ -14,13 +15,13 @@
* limitations under the License.
*/
import type { Meta, StoryFn, StoryObj } from '@storybook/react-vite';
import type { StoryFn, StoryObj } from '@storybook/react-vite';
import { ButtonLink } from './ButtonLink';
import { Flex } from '../Flex';
import { MemoryRouter } from 'react-router-dom';
import { RiArrowRightSLine, RiCloudLine } from '@remixicon/react';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/ButtonLink',
component: ButtonLink,
decorators: [
@@ -40,18 +41,17 @@ const meta = {
options: ['primary', 'secondary'],
},
},
} satisfies Meta<typeof ButtonLink>;
});
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
export const Default = meta.story({
args: {
children: 'Button',
},
};
});
export const Variants: Story = {
export const Variants = meta.story({
render: () => (
<Flex align="center">
<ButtonLink
@@ -80,9 +80,9 @@ export const Variants: Story = {
</ButtonLink>
</Flex>
),
};
});
export const Sizes: Story = {
export const Sizes = meta.story({
args: {
children: 'Button',
},
@@ -96,9 +96,9 @@ export const Sizes: Story = {
</ButtonLink>
</Flex>
),
};
});
export const WithIcons: Story = {
export const WithIcons = meta.story({
args: {
children: 'Button',
},
@@ -113,9 +113,9 @@ export const WithIcons: Story = {
/>
</Flex>
),
};
});
export const FullWidth: Story = {
export const FullWidth = meta.story({
args: {
children: 'Button',
},
@@ -130,9 +130,9 @@ export const FullWidth: Story = {
/>
</Flex>
),
};
});
export const Disabled: Story = {
export const Disabled = meta.story({
render: () => (
<Flex direction="row" gap="4">
<ButtonLink variant="primary" isDisabled>
@@ -146,9 +146,9 @@ export const Disabled: Story = {
</ButtonLink>
</Flex>
),
};
});
export const Responsive: Story = {
export const Responsive = meta.story({
args: {
children: 'Button',
variant: {
@@ -160,4 +160,4 @@ export const Responsive: Story = {
sm: 'medium',
},
},
};
});
@@ -1,5 +1,5 @@
/*
* Copyright 2024 The Backstage Authors
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,21 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import preview from '../../../../../.storybook/preview';
import { Card, CardHeader, CardBody, CardFooter } from './Card';
import { Text } from '../..';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/Card',
component: Card,
subcomponents: { CardHeader, CardBody, CardFooter },
} satisfies Meta<typeof Card>;
});
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
export const Default = meta.story({
render: args => (
<Card {...args}>
<CardHeader>Header</CardHeader>
@@ -35,19 +31,19 @@ export const Default: Story = {
<CardFooter>Footer</CardFooter>
</Card>
),
};
});
export const CustomSize: Story = {
export const CustomSize = meta.story({
args: {
style: {
width: '300px',
height: '200px',
},
},
render: Default.render,
};
render: Default.input.render,
});
export const WithLongBody: Story = {
export const WithLongBody = meta.story({
render: () => (
<Card style={{ width: '300px', height: '200px' }}>
<CardHeader>
@@ -77,7 +73,7 @@ export const WithLongBody: Story = {
</CardFooter>
</Card>
),
};
});
const ListRow = ({ children }: { children: React.ReactNode }) => {
return (
@@ -99,7 +95,7 @@ const ListRow = ({ children }: { children: React.ReactNode }) => {
);
};
export const WithListRow: Story = {
export const WithListRow = meta.story({
render: () => (
<Card style={{ width: '300px', height: '200px' }}>
<CardHeader>
@@ -128,4 +124,4 @@ export const WithListRow: Story = {
</CardFooter>
</Card>
),
};
});
@@ -1,5 +1,5 @@
/*
* Copyright 2024 The Backstage Authors
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,27 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import preview from '../../../../../.storybook/preview';
import { Checkbox } from './Checkbox';
import { Flex } from '../Flex';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/Checkbox',
component: Checkbox,
} satisfies Meta<typeof Checkbox>;
});
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
export const Default = meta.story({
args: {
children: 'Accept terms and conditions',
},
};
});
export const AllVariants: Story = {
...Default,
export const AllVariants = meta.story({
...Default.input,
render: () => (
<Flex direction="column" gap="2">
<Checkbox>Unchecked</Checkbox>
@@ -44,4 +40,4 @@ export const AllVariants: Story = {
</Checkbox>
</Flex>
),
};
});
@@ -1,5 +1,5 @@
/*
* Copyright 2024 The Backstage Authors
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,12 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import preview from '../../../../../.storybook/preview';
import { Box } from '../Box/Box';
import { Container } from './Container';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/Container',
component: Container,
parameters: {
@@ -32,10 +31,7 @@ const meta = {
control: 'text',
},
},
} satisfies Meta<typeof Container>;
export default meta;
type Story = StoryObj<typeof meta>;
});
const DecorativeBox = () => (
<Box
@@ -50,58 +46,58 @@ const DecorativeBox = () => (
/>
);
export const Default: Story = {
export const Default = meta.story({
args: {
children: <DecorativeBox />,
},
};
});
export const Preview: Story = {
export const Preview = meta.story({
render: () => (
<div style={{ maxWidth: '600px', margin: '0 auto' }}>
<DecorativeBox />
</div>
),
};
});
export const WithPaddingY: Story = {
export const WithPaddingY = meta.story({
args: {
...Default.args,
...Default.input.args,
py: '4',
},
};
});
export const WithPaddingTop: Story = {
export const WithPaddingTop = meta.story({
args: {
...Default.args,
...Default.input.args,
pt: '4',
},
};
});
export const WithPaddingBottom: Story = {
export const WithPaddingBottom = meta.story({
args: {
...Default.args,
...Default.input.args,
pb: '4',
},
};
});
export const WithMarginY: Story = {
export const WithMarginY = meta.story({
args: {
...Default.args,
...Default.input.args,
my: '4',
},
};
});
export const WithMarginTop: Story = {
export const WithMarginTop = meta.story({
args: {
...Default.args,
...Default.input.args,
mt: '4',
},
};
});
export const WithMarginBottom: Story = {
export const WithMarginBottom = meta.story({
args: {
...Default.args,
...Default.input.args,
mb: '4',
},
};
});
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import preview from '../../../../../.storybook/preview';
import {
Dialog,
DialogTrigger,
@@ -24,7 +24,7 @@ import {
import { Button, Flex, Text, TextField, Select } from '@backstage/ui';
import { useArgs } from 'storybook/preview-api';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/Dialog',
component: Dialog,
args: {
@@ -35,12 +35,9 @@ const meta = {
isOpen: { control: 'boolean' },
defaultOpen: { control: 'boolean' },
},
} satisfies Meta<typeof Dialog>;
});
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
export const Default = meta.story({
render: args => {
return (
<DialogTrigger>
@@ -62,17 +59,17 @@ export const Default: Story = {
</DialogTrigger>
);
},
};
});
export const Open: Story = {
export const Open = meta.story({
args: {
...Default.args,
...Default.input.args,
defaultOpen: true,
},
render: Default.render,
};
render: Default.input.render,
});
export const NoTrigger: Story = {
export const NoTrigger = meta.story({
args: {
isOpen: true,
},
@@ -100,9 +97,9 @@ export const NoTrigger: Story = {
</Dialog>
);
},
};
});
export const FixedWidth: Story = {
export const FixedWidth = meta.story({
args: {
defaultOpen: true,
width: 600,
@@ -145,35 +142,35 @@ export const FixedWidth: Story = {
</Dialog>
</DialogTrigger>
),
};
});
export const FixedHeight: Story = {
export const FixedHeight = meta.story({
args: {
defaultOpen: true,
height: 500,
},
render: FixedWidth.render,
};
render: FixedWidth.input.render,
});
export const FixedWidthAndHeight: Story = {
export const FixedWidthAndHeight = meta.story({
args: {
defaultOpen: true,
width: 600,
height: 400,
},
render: FixedWidth.render,
};
render: FixedWidth.input.render,
});
export const FullWidthAndHeight: Story = {
export const FullWidthAndHeight = meta.story({
args: {
defaultOpen: true,
width: '100%',
height: '100%',
},
render: FixedWidth.render,
};
render: FixedWidth.input.render,
});
export const Confirmation: Story = {
export const Confirmation = meta.story({
args: {
isOpen: true,
},
@@ -199,9 +196,9 @@ export const Confirmation: Story = {
</Dialog>
</DialogTrigger>
),
};
});
export const WithForm: Story = {
export const WithForm = meta.story({
args: {
isOpen: true,
},
@@ -235,20 +232,20 @@ export const WithForm: Story = {
</Dialog>
</DialogTrigger>
),
};
});
export const PreviewFixedWidthAndHeight: Story = {
export const PreviewFixedWidthAndHeight = meta.story({
args: {
defaultOpen: undefined,
width: 600,
height: 400,
},
render: FixedWidth.render,
};
render: FixedWidth.input.render,
});
export const PreviewWithForm: Story = {
export const PreviewWithForm = meta.story({
args: {
defaultOpen: undefined,
},
render: WithForm.render,
};
render: WithForm.input.render,
});
@@ -1,5 +1,5 @@
/*
* Copyright 2024 The Backstage Authors
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,21 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import preview from '../../../../../.storybook/preview';
import { TextField, Input, Form } from 'react-aria-components';
import { FieldError } from './FieldError';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/FieldError',
component: FieldError,
} satisfies Meta<typeof FieldError>;
export default meta;
type Story = StoryObj<typeof meta>;
});
// Show error with server validation using Form component
export const WithServerValidation: Story = {
export const WithServerValidation = meta.story({
render: () => (
<Form validationErrors={{ demo: 'This is a server validation error.' }}>
<TextField
@@ -43,10 +39,10 @@ export const WithServerValidation: Story = {
</TextField>
</Form>
),
};
});
// Show error using children
export const WithCustomMessage: Story = {
export const WithCustomMessage = meta.story({
render: () => (
<TextField
isInvalid
@@ -61,10 +57,10 @@ export const WithCustomMessage: Story = {
<FieldError>This is a custom error message.</FieldError>
</TextField>
),
};
});
// Show error with render prop function
export const WithRenderProp: Story = {
export const WithRenderProp = meta.story({
render: () => (
<TextField
isInvalid
@@ -84,4 +80,4 @@ export const WithRenderProp: Story = {
</FieldError>
</TextField>
),
};
});
@@ -1,5 +1,5 @@
/*
* Copyright 2024 The Backstage Authors
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,11 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import preview from '../../../../../.storybook/preview';
import { FieldLabel } from './FieldLabel';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/FieldLabel',
component: FieldLabel,
argTypes: {
@@ -31,35 +30,32 @@ const meta = {
control: 'text',
},
},
} satisfies Meta<typeof FieldLabel>;
});
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
export const Default = meta.story({
args: {
label: 'Label',
},
};
});
export const WithSecondaryLabel: Story = {
export const WithSecondaryLabel = meta.story({
args: {
...Default.args,
...Default.input.args,
secondaryLabel: 'Secondary Label',
},
};
});
export const WithDescription: Story = {
export const WithDescription = meta.story({
args: {
...Default.args,
...Default.input.args,
description: 'Description',
},
};
});
export const WithAllFields: Story = {
export const WithAllFields = meta.story({
args: {
...Default.args,
...Default.input.args,
secondaryLabel: 'Secondary Label',
description: 'Description',
},
};
});
@@ -1,5 +1,5 @@
/*
* Copyright 2024 The Backstage Authors
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,13 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import preview from '../../../../../.storybook/preview';
import { Flex } from './Flex';
import { Text } from '../Text';
import { Box } from '../Box';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/Flex',
component: Flex,
argTypes: {
@@ -36,10 +35,7 @@ const meta = {
options: ['row', 'column', 'row-reverse', 'column-reverse'],
},
},
} satisfies Meta<typeof Flex>;
export default meta;
type Story = StoryObj<typeof meta>;
});
const DecorativeBox = ({
width = '48px',
@@ -78,7 +74,7 @@ const DecorativeBox = ({
);
};
export const Default: Story = {
export const Default = meta.story({
args: {
children: (
<>
@@ -88,23 +84,23 @@ export const Default: Story = {
</>
),
},
};
});
export const ColumnDirection: Story = {
export const ColumnDirection = meta.story({
args: {
...Default.args,
...Default.input.args,
direction: 'column',
},
};
});
export const RowDirection: Story = {
export const RowDirection = meta.story({
args: {
...Default.args,
...Default.input.args,
direction: 'row',
},
};
});
export const AlignStartInColumn: Story = {
export const AlignStartInColumn = meta.story({
args: {
align: 'start',
direction: 'column',
@@ -116,9 +112,9 @@ export const AlignStartInColumn: Story = {
<DecorativeBox height="48px" />
</Flex>
),
};
});
export const AlignStartInRow: Story = {
export const AlignStartInRow = meta.story({
args: {
align: 'start',
direction: 'row',
@@ -130,9 +126,9 @@ export const AlignStartInRow: Story = {
<DecorativeBox height="48px" />
</Flex>
),
};
});
export const AlignCenterInColumn: Story = {
export const AlignCenterInColumn = meta.story({
args: {
align: 'center',
direction: 'column',
@@ -144,9 +140,9 @@ export const AlignCenterInColumn: Story = {
<DecorativeBox height="48px" />
</Flex>
),
};
});
export const AlignCenterInRow: Story = {
export const AlignCenterInRow = meta.story({
args: {
align: 'center',
direction: 'row',
@@ -158,9 +154,9 @@ export const AlignCenterInRow: Story = {
<DecorativeBox height="48px" />
</Flex>
),
};
});
export const AlignEndInColumn: Story = {
export const AlignEndInColumn = meta.story({
args: {
align: 'end',
direction: 'column',
@@ -172,9 +168,9 @@ export const AlignEndInColumn: Story = {
<DecorativeBox height="48px" />
</Flex>
),
};
});
export const AlignEndInRow: Story = {
export const AlignEndInRow = meta.story({
args: {
align: 'end',
direction: 'row',
@@ -186,9 +182,9 @@ export const AlignEndInRow: Story = {
<DecorativeBox height="48px" />
</Flex>
),
};
});
export const ResponsiveAlign: Story = {
export const ResponsiveAlign = meta.story({
args: {
align: { xs: 'start', md: 'center', lg: 'end' },
},
@@ -199,9 +195,9 @@ export const ResponsiveAlign: Story = {
<DecorativeBox height="48px" />
</Flex>
),
};
});
export const ResponsiveGap: Story = {
export const ResponsiveGap = meta.story({
args: {
gap: { xs: '4', md: '8', lg: '12' },
},
@@ -212,9 +208,9 @@ export const ResponsiveGap: Story = {
<DecorativeBox />
</Flex>
),
};
});
export const LargeGap: Story = {
export const LargeGap = meta.story({
args: {
gap: '8',
},
@@ -225,9 +221,9 @@ export const LargeGap: Story = {
<DecorativeBox />
</Flex>
),
};
});
export const WithTextTruncate: Story = {
export const WithTextTruncate = meta.story({
render: () => (
<Flex direction="row" gap="8">
<Flex>
@@ -246,4 +242,4 @@ export const WithTextTruncate: Story = {
</Flex>
</Flex>
),
};
});
@@ -1,5 +1,5 @@
/*
* Copyright 2024 The Backstage Authors
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,20 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import preview from '../../../../../.storybook/preview';
import { Grid } from './Grid';
import type { GridItemProps } from './types';
import { Box } from '../Box/Box';
import { Flex } from '../Flex';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/Grid',
component: Grid.Root,
} satisfies Meta<typeof Grid.Root>;
export default meta;
type Story = StoryObj<typeof meta>;
});
const FakeBox = () => (
<Box
@@ -41,7 +37,7 @@ const FakeBox = () => (
/>
);
export const Default: Story = {
export const Default = meta.story({
args: {
children: (
<>
@@ -51,16 +47,16 @@ export const Default: Story = {
</>
),
},
};
});
export const LargeGap: Story = {
export const LargeGap = meta.story({
args: {
...Default.args,
...Default.input.args,
gap: '64px',
},
};
});
export const ColumnSizes: Story = {
export const ColumnSizes = meta.story({
args: {
columns: '12',
},
@@ -78,9 +74,9 @@ export const ColumnSizes: Story = {
))}
</Flex>
),
};
});
export const RowAndColumns: Story = {
export const RowAndColumns = meta.story({
args: {
columns: '12',
},
@@ -106,4 +102,4 @@ export const RowAndColumns: Story = {
</Grid.Item>
</Grid.Root>
),
};
});
@@ -1,3 +1,4 @@
import preview from '../../../../../.storybook/preview';
/*
* Copyright 2025 The Backstage Authors
*
@@ -14,7 +15,7 @@
* limitations under the License.
*/
import type { Meta, StoryObj, StoryFn } from '@storybook/react-vite';
import type { StoryObj, StoryFn } from '@storybook/react-vite';
import { Header } from './Header';
import type { HeaderTab } from './types';
import {
@@ -36,15 +37,14 @@ import {
} from '@remixicon/react';
import { HeaderPageBreadcrumb } from '../HeaderPage/types';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/Header',
component: Header,
parameters: {
layout: 'fullscreen',
},
} satisfies Meta<typeof Header>;
});
export default meta;
type Story = StoryObj<typeof meta>;
const withRouter = (Story: StoryFn) => (
@@ -199,19 +199,19 @@ const layoutDecorator = [
withRouter,
];
export const Default: Story = {
export const Default = meta.story({
args: {},
decorators: [withRouter],
};
});
export const WithTabs: Story = {
export const WithTabs = meta.story({
args: {
tabs,
},
decorators: [withRouter],
};
});
export const WithCustomActions: Story = {
export const WithCustomActions = meta.story({
args: {},
decorators: [withRouter],
render: args => (
@@ -240,20 +240,20 @@ export const WithCustomActions: Story = {
}
/>
),
};
});
export const WithAllOptionsAndTabs: Story = {
export const WithAllOptionsAndTabs = meta.story({
args: {
...WithCustomActions.args,
...WithCustomActions.input.args,
tabs,
},
decorators: [withRouter],
render: WithCustomActions.render,
};
render: WithCustomActions.input.render,
});
export const WithHeaderPage: Story = {
export const WithHeaderPage = meta.story({
args: {
...WithAllOptionsAndTabs.args,
...WithAllOptionsAndTabs.input.args,
},
decorators: [withRouter],
render: args => (
@@ -276,9 +276,9 @@ export const WithHeaderPage: Story = {
/>
</>
),
};
});
export const WithLayout: Story = {
export const WithLayout = meta.story({
decorators: layoutDecorator,
render: args => (
<>
@@ -291,9 +291,9 @@ export const WithLayout: Story = {
/>
</>
),
};
});
export const WithLayoutNoTabs: Story = {
export const WithLayoutNoTabs = meta.story({
decorators: layoutDecorator,
render: args => (
<>
@@ -301,9 +301,9 @@ export const WithLayoutNoTabs: Story = {
<HeaderPage title="Page title" tabs={tabs2} />
</>
),
};
});
export const WithEverything: Story = {
export const WithEverything = meta.story({
args: {
tabs,
titleLink: '/',
@@ -333,9 +333,9 @@ export const WithEverything: Story = {
/>
</>
),
};
});
export const WithMockedURLCampaigns: Story = {
export const WithMockedURLCampaigns = meta.story({
args: {
tabs,
},
@@ -353,9 +353,9 @@ export const WithMockedURLCampaigns: Story = {
</Container>
</MemoryRouter>
),
};
});
export const WithMockedURLIntegrations: Story = {
export const WithMockedURLIntegrations = meta.story({
args: {
tabs,
},
@@ -373,9 +373,9 @@ export const WithMockedURLIntegrations: Story = {
</Container>
</MemoryRouter>
),
};
});
export const WithMockedURLNoMatch: Story = {
export const WithMockedURLNoMatch = meta.story({
args: {
tabs,
},
@@ -397,9 +397,9 @@ export const WithMockedURLNoMatch: Story = {
</Container>
</MemoryRouter>
),
};
});
export const WithTabsMatchingStrategies: Story = {
export const WithTabsMatchingStrategies = meta.story({
args: {
title: 'Route Matching Demo',
tabs: [
@@ -457,9 +457,9 @@ export const WithTabsMatchingStrategies: Story = {
</Container>
</MemoryRouter>
),
};
});
export const WithTabsExactMatching: Story = {
export const WithTabsExactMatching = meta.story({
args: {
title: 'Exact Matching Demo',
tabs: [
@@ -496,9 +496,9 @@ export const WithTabsExactMatching: Story = {
</Container>
</MemoryRouter>
),
};
});
export const WithTabsPrefixMatchingDeep: Story = {
export const WithTabsPrefixMatchingDeep = meta.story({
args: {
title: 'Deep Nesting Demo',
tabs: [
@@ -548,4 +548,4 @@ export const WithTabsPrefixMatchingDeep: Story = {
</Container>
</MemoryRouter>
),
};
});
@@ -1,3 +1,4 @@
import preview from '../../../../../.storybook/preview';
/*
* Copyright 2025 The Backstage Authors
*
@@ -14,7 +15,7 @@
* limitations under the License.
*/
import type { Meta, StoryObj, StoryFn } from '@storybook/react-vite';
import type { StoryObj, StoryFn } from '@storybook/react-vite';
import { HeaderPage } from './HeaderPage';
import type { HeaderTab } from '../Header/types';
import { MemoryRouter } from 'react-router-dom';
@@ -29,15 +30,14 @@ import {
} from '../../';
import { RiMore2Line } from '@remixicon/react';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/HeaderPage',
component: HeaderPage,
parameters: {
layout: 'fullscreen',
},
} satisfies Meta<typeof HeaderPage>;
});
export default meta;
type Story = StoryObj<typeof meta>;
const tabs: HeaderTab[] = [
@@ -130,25 +130,25 @@ const layoutDecorator = [
),
];
export const Default: Story = {
export const Default = meta.story({
args: {
title: 'Page Title',
},
};
});
export const WithTabs: Story = {
export const WithTabs = meta.story({
args: {
...Default.args,
...Default.input.args,
tabs,
},
decorators: [withRouter],
};
});
export const WithCustomActions: Story = {
export const WithCustomActions = meta.story({
decorators: [withRouter],
render: () => (
<HeaderPage
{...Default.args}
{...Default.input.args}
customActions={
<>
<Button>Custom action</Button>
@@ -174,48 +174,48 @@ export const WithCustomActions: Story = {
}
/>
),
};
});
export const WithBreadcrumbs: Story = {
export const WithBreadcrumbs = meta.story({
decorators: [withRouter],
args: {
...Default.args,
...Default.input.args,
breadcrumbs: [{ label: 'Home', href: '/' }],
},
};
});
export const WithLongBreadcrumbs: Story = {
export const WithLongBreadcrumbs = meta.story({
decorators: [withRouter],
args: {
...Default.args,
...Default.input.args,
breadcrumbs: [
{ label: 'Home', href: '/' },
{ label: 'Long Breadcrumb Name', href: '/long-breadcrumb' },
],
},
};
});
export const WithEverything: Story = {
export const WithEverything = meta.story({
decorators: [withRouter],
render: () => (
<HeaderPage
{...Default.args}
{...Default.input.args}
tabs={tabs}
customActions={<Button>Custom action</Button>}
breadcrumbs={[{ label: 'Home', href: '/' }]}
/>
),
};
});
export const WithLayout: Story = {
export const WithLayout = meta.story({
args: {
...WithEverything.args,
...WithEverything.input.args,
},
decorators: [withRouter, ...layoutDecorator],
render: WithEverything.render,
};
render: WithEverything.input.render,
});
export const WithTabsMatchingStrategies: Story = {
export const WithTabsMatchingStrategies = meta.story({
args: {
title: 'Route Matching Demo',
tabs: [
@@ -273,9 +273,9 @@ export const WithTabsMatchingStrategies: Story = {
</Container>
</MemoryRouter>
),
};
});
export const WithTabsExactMatching: Story = {
export const WithTabsExactMatching = meta.story({
args: {
title: 'Exact Matching Demo',
tabs: [
@@ -312,9 +312,9 @@ export const WithTabsExactMatching: Story = {
</Container>
</MemoryRouter>
),
};
});
export const WithTabsPrefixMatchingDeep: Story = {
export const WithTabsPrefixMatchingDeep = meta.story({
args: {
title: 'Deep Nesting Demo',
tabs: [
@@ -364,4 +364,4 @@ export const WithTabsPrefixMatchingDeep: Story = {
</Container>
</MemoryRouter>
),
};
});
@@ -1,3 +1,4 @@
import preview from '../../../../../.storybook/preview';
/*
* Copyright 2024 The Backstage Authors
*
@@ -14,13 +15,13 @@
* limitations under the License.
*/
import type { Meta, StoryFn, StoryObj } from '@storybook/react-vite';
import type { StoryFn, StoryObj } from '@storybook/react-vite';
import { Link } from './Link';
import { Flex } from '../Flex';
import { Text } from '../Text';
import { MemoryRouter } from 'react-router-dom';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/Link',
component: Link,
args: {
@@ -33,29 +34,28 @@ const meta = {
</MemoryRouter>
),
],
} satisfies Meta<typeof Link>;
});
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
export const Default = meta.story({
args: {
href: '/',
children: 'Sign up for Backstage',
},
};
});
export const ExternalLink: Story = {
export const ExternalLink = meta.story({
args: {
href: 'https://backstage.io',
children: 'Sign up for Backstage',
target: '_blank',
},
};
});
export const AllVariants: Story = {
export const AllVariants = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: args => (
<Flex gap="4" direction="column">
@@ -69,9 +69,9 @@ export const AllVariants: Story = {
<Link href="https://ui.backstage.io" variant="body-x-small" {...args} />
</Flex>
),
};
});
export const AllColors: Story = {
export const AllColors = meta.story({
render: () => (
<Flex gap="4" direction="column">
<Link
@@ -106,9 +106,9 @@ export const AllColors: Story = {
/>
</Flex>
),
};
});
export const AllWeights: Story = {
export const AllWeights = meta.story({
render: () => (
<Flex gap="4" direction="column">
<Flex>
@@ -225,9 +225,9 @@ export const AllWeights: Story = {
</Flex>
</Flex>
),
};
});
export const Truncate: Story = {
export const Truncate = meta.story({
args: {
children:
"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?",
@@ -235,21 +235,21 @@ export const Truncate: Story = {
truncate: true,
style: { width: '480px' },
},
};
});
export const Responsive: Story = {
export const Responsive = meta.story({
args: {
...Default.args,
...Default.input.args,
variant: {
xs: 'title-x-small',
md: 'body-x-small',
},
},
};
});
export const Playground: Story = {
export const Playground = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: args => (
<Flex gap="4" direction="column">
@@ -269,4 +269,4 @@ export const Playground: Story = {
<Link variant="title-medium" style={{ maxWidth: '600px' }} {...args} />
</Flex>
),
};
});
@@ -1,3 +1,4 @@
import preview from '../../../../../.storybook/preview';
/*
* Copyright 2025 The Backstage Authors
*
@@ -14,7 +15,7 @@
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import type { StoryObj } from '@storybook/react-vite';
import {
MenuTrigger,
SubmenuTrigger,
@@ -38,7 +39,7 @@ import {
import { MemoryRouter } from 'react-router-dom';
import { useEffect, useState } from 'react';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/Menu',
component: MenuTrigger,
decorators: [
@@ -48,9 +49,8 @@ const meta = {
</MemoryRouter>
),
],
} satisfies Meta<typeof MenuTrigger>;
});
export default meta;
type Story = StoryObj<typeof meta>;
const options = [
@@ -65,7 +65,7 @@ const options = [
{ label: 'Honeydew', value: 'honeydew' },
];
export const Preview: Story = {
export const Preview = meta.story({
args: {
children: null,
},
@@ -91,11 +91,11 @@ export const Preview: Story = {
</Menu>
</MenuTrigger>
),
};
});
export const PreviewSubmenu: Story = {
export const PreviewSubmenu = meta.story({
args: {
...Preview.args,
...Preview.input.args,
},
render: () => (
<MenuTrigger>
@@ -119,11 +119,11 @@ export const PreviewSubmenu: Story = {
</Menu>
</MenuTrigger>
),
};
});
export const PreviewIcons: Story = {
export const PreviewIcons = meta.story({
args: {
...Preview.args,
...Preview.input.args,
},
render: () => (
<MenuTrigger>
@@ -135,11 +135,11 @@ export const PreviewIcons: Story = {
</Menu>
</MenuTrigger>
),
};
});
export const PreviewSections: Story = {
export const PreviewSections = meta.story({
args: {
...Preview.args,
...Preview.input.args,
},
render: () => (
<MenuTrigger>
@@ -159,11 +159,11 @@ export const PreviewSections: Story = {
</Menu>
</MenuTrigger>
),
};
});
export const PreviewSeparators: Story = {
export const PreviewSeparators = meta.story({
args: {
...Preview.args,
...Preview.input.args,
},
render: () => (
<MenuTrigger>
@@ -180,11 +180,11 @@ export const PreviewSeparators: Story = {
</Menu>
</MenuTrigger>
),
};
});
export const PreviewLinks: Story = {
export const PreviewLinks = meta.story({
args: {
...Preview.args,
...Preview.input.args,
},
render: () => (
<MenuTrigger>
@@ -198,11 +198,11 @@ export const PreviewLinks: Story = {
</Menu>
</MenuTrigger>
),
};
});
export const Opened: Story = {
export const Opened = meta.story({
args: {
...Preview.args,
...Preview.input.args,
},
render: () => (
<MenuTrigger isOpen>
@@ -214,11 +214,11 @@ export const Opened: Story = {
</Menu>
</MenuTrigger>
),
};
});
export const WithIcons: Story = {
export const WithIcons = meta.story({
args: {
...Preview.args,
...Preview.input.args,
},
render: () => (
<MenuTrigger isOpen>
@@ -230,11 +230,11 @@ export const WithIcons: Story = {
</Menu>
</MenuTrigger>
),
};
});
export const WithSections: Story = {
export const WithSections = meta.story({
args: {
...Preview.args,
...Preview.input.args,
},
render: () => (
<MenuTrigger isOpen>
@@ -254,11 +254,11 @@ export const WithSections: Story = {
</Menu>
</MenuTrigger>
),
};
});
export const WithSeparators: Story = {
export const WithSeparators = meta.story({
args: {
...Preview.args,
...Preview.input.args,
},
render: () => (
<MenuTrigger isOpen>
@@ -275,11 +275,11 @@ export const WithSeparators: Story = {
</Menu>
</MenuTrigger>
),
};
});
export const WithColors: Story = {
export const WithColors = meta.story({
args: {
...Preview.args,
...Preview.input.args,
},
render: () => (
<MenuTrigger isOpen>
@@ -295,11 +295,11 @@ export const WithColors: Story = {
</Menu>
</MenuTrigger>
),
};
});
export const WithLinks: Story = {
export const WithLinks = meta.story({
args: {
...Preview.args,
...Preview.input.args,
},
render: () => (
<MenuTrigger isOpen>
@@ -313,11 +313,11 @@ export const WithLinks: Story = {
</Menu>
</MenuTrigger>
),
};
});
export const Submenu: Story = {
export const Submenu = meta.story({
args: {
...Preview.args,
...Preview.input.args,
},
render: () => (
<MenuTrigger isOpen>
@@ -341,11 +341,11 @@ export const Submenu: Story = {
</Menu>
</MenuTrigger>
),
};
});
export const Virtualized: Story = {
export const Virtualized = meta.story({
args: {
...Preview.args,
...Preview.input.args,
},
render: () => {
const [pokemon, setPokemon] = useState<
@@ -376,11 +376,11 @@ export const Virtualized: Story = {
</MenuTrigger>
);
},
};
});
export const VirtualizedMaxHeight: Story = {
export const VirtualizedMaxHeight = meta.story({
args: {
...Preview.args,
...Preview.input.args,
},
render: () => {
const [pokemon, setPokemon] = useState<
@@ -411,9 +411,9 @@ export const VirtualizedMaxHeight: Story = {
</MenuTrigger>
);
},
};
});
export const WithScroll: Story = {
export const WithScroll = meta.story({
args: {
children: null,
},
@@ -446,4 +446,4 @@ export const WithScroll: Story = {
</Menu>
</MenuTrigger>
),
};
});
@@ -1,3 +1,4 @@
import preview from '../../../../../.storybook/preview';
/*
* Copyright 2025 The Backstage Authors
*
@@ -14,7 +15,7 @@
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import type { StoryObj } from '@storybook/react-vite';
import {
MenuTrigger,
SubmenuTrigger,
@@ -26,7 +27,7 @@ import { Button } from '../..';
import { useState, useEffect } from 'react';
import { MemoryRouter } from 'react-router-dom';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/MenuAutocomplete',
component: MenuTrigger,
decorators: [
@@ -36,9 +37,8 @@ const meta = {
</MemoryRouter>
),
],
} satisfies Meta<typeof MenuTrigger>;
});
export default meta;
type Story = StoryObj<typeof meta>;
const options = [
@@ -53,7 +53,7 @@ const options = [
{ label: 'Honeydew', value: 'honeydew' },
];
export const Default: Story = {
export const Default = meta.story({
args: {
children: null,
},
@@ -72,11 +72,11 @@ export const Default: Story = {
</MenuAutocomplete>
</MenuTrigger>
),
};
});
export const PreviewAutocompleteMenu: Story = {
export const PreviewAutocompleteMenu = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: () => (
<MenuTrigger>
@@ -93,11 +93,11 @@ export const PreviewAutocompleteMenu: Story = {
</MenuAutocomplete>
</MenuTrigger>
),
};
});
export const Virtualized: Story = {
export const Virtualized = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: () => {
const [pokemon, setPokemon] = useState<
@@ -132,11 +132,11 @@ export const Virtualized: Story = {
</MenuTrigger>
);
},
};
});
export const VirtualizedMaxHeight: Story = {
export const VirtualizedMaxHeight = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: () => {
const [pokemon, setPokemon] = useState<
@@ -172,11 +172,11 @@ export const VirtualizedMaxHeight: Story = {
</MenuTrigger>
);
},
};
});
export const Submenu: Story = {
export const Submenu = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: () => (
<MenuTrigger isOpen>
@@ -197,4 +197,4 @@ export const Submenu: Story = {
</Menu>
</MenuTrigger>
),
};
});
@@ -1,3 +1,4 @@
import preview from '../../../../../.storybook/preview';
/*
* Copyright 2025 The Backstage Authors
*
@@ -14,7 +15,7 @@
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import type { StoryObj } from '@storybook/react-vite';
import {
MenuTrigger,
MenuAutocompleteListbox,
@@ -28,7 +29,7 @@ import { useEffect, useState } from 'react';
import { Selection } from 'react-aria-components';
import { MemoryRouter } from 'react-router-dom';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/MenuAutocompleteListBox',
component: MenuTrigger,
decorators: [
@@ -38,9 +39,8 @@ const meta = {
</MemoryRouter>
),
],
} satisfies Meta<typeof MenuTrigger>;
});
export default meta;
type Story = StoryObj<typeof meta>;
const options = [
@@ -55,7 +55,7 @@ const options = [
{ label: 'Honeydew', value: 'honeydew' },
];
export const Default: Story = {
export const Default = meta.story({
args: {
children: null,
},
@@ -83,11 +83,11 @@ export const Default: Story = {
</Flex>
);
},
};
});
export const PreviewListbox: Story = {
export const PreviewListbox = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: () => {
const [selected, setSelected] = useState<Selection>(
@@ -113,11 +113,11 @@ export const PreviewListbox: Story = {
</Flex>
);
},
};
});
export const PreviewListboxMultiple: Story = {
export const PreviewListboxMultiple = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: () => {
const [selected, setSelected] = useState<Selection>(
@@ -144,11 +144,11 @@ export const PreviewListboxMultiple: Story = {
</Flex>
);
},
};
});
export const Submenu: Story = {
export const Submenu = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: () => {
const [selected, setSelected] = useState<Selection>(
@@ -182,11 +182,11 @@ export const Submenu: Story = {
</Flex>
);
},
};
});
export const Virtualized: Story = {
export const Virtualized = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: () => {
const [pokemon, setPokemon] = useState<
@@ -221,11 +221,11 @@ export const Virtualized: Story = {
</MenuTrigger>
);
},
};
});
export const VirtualizedMaxHeight: Story = {
export const VirtualizedMaxHeight = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: () => {
const [pokemon, setPokemon] = useState<
@@ -261,4 +261,4 @@ export const VirtualizedMaxHeight: Story = {
</MenuTrigger>
);
},
};
});
@@ -1,3 +1,4 @@
import preview from '../../../../../.storybook/preview';
/*
* Copyright 2025 The Backstage Authors
*
@@ -14,14 +15,14 @@
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import type { StoryObj } from '@storybook/react-vite';
import { MenuTrigger, MenuListBox, MenuListBoxItem } from './index';
import { Button, Flex, Text } from '../..';
import { useEffect, useState } from 'react';
import { Selection } from 'react-aria-components';
import { MemoryRouter } from 'react-router-dom';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/MenuListBox',
component: MenuTrigger,
decorators: [
@@ -31,12 +32,11 @@ const meta = {
</MemoryRouter>
),
],
} satisfies Meta<typeof MenuTrigger>;
});
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
export const Default = meta.story({
args: {
children: null,
},
@@ -50,11 +50,11 @@ export const Default: Story = {
</MenuListBox>
</MenuTrigger>
),
};
});
export const Controlled: Story = {
export const Controlled = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: () => {
const [selected, setSelected] = useState<Selection>(new Set(['paul']));
@@ -86,11 +86,11 @@ export const Controlled: Story = {
</Flex>
);
},
};
});
export const Virtualized: Story = {
export const Virtualized = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: () => {
const [pokemon, setPokemon] = useState<
@@ -121,11 +121,11 @@ export const Virtualized: Story = {
</MenuTrigger>
);
},
};
});
export const VirtualizedMaxHeight: Story = {
export const VirtualizedMaxHeight = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: () => {
const [pokemon, setPokemon] = useState<
@@ -156,4 +156,4 @@ export const VirtualizedMaxHeight: Story = {
</MenuTrigger>
);
},
};
});
@@ -1,5 +1,5 @@
/*
* Copyright 2024 The Backstage Authors
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,15 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import preview from '../../../../../.storybook/preview';
import { PasswordField } from './PasswordField';
import { Form } from 'react-aria-components';
import { Flex } from '../Flex';
import { FieldLabel } from '../FieldLabel';
import { RiSparklingLine } from '@remixicon/react';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/PasswordField',
component: PasswordField,
argTypes: {
@@ -32,12 +31,9 @@ const meta = {
control: 'object',
},
},
} satisfies Meta<typeof PasswordField>;
});
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
export const Default = meta.story({
args: {
name: 'secret',
placeholder: 'Enter a secret',
@@ -45,11 +41,11 @@ export const Default: Story = {
maxWidth: '300px',
},
},
};
});
export const Sizes: Story = {
export const Sizes = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: args => (
<Flex direction="row" gap="4" style={{ width: '100%', maxWidth: '600px' }}>
@@ -57,79 +53,79 @@ export const Sizes: Story = {
<PasswordField {...args} size="medium" icon={<RiSparklingLine />} />
</Flex>
),
};
});
export const DefaultValue: Story = {
export const DefaultValue = meta.story({
args: {
...Default.args,
...Default.input.args,
defaultValue: 'https://example.com',
},
};
});
export const WithLabel: Story = {
export const WithLabel = meta.story({
args: {
...Default.args,
...Default.input.args,
label: 'Label',
},
};
});
export const WithDescription: Story = {
export const WithDescription = meta.story({
args: {
...WithLabel.args,
...WithLabel.input.args,
description: 'Description',
},
};
});
export const Required: Story = {
export const Required = meta.story({
args: {
...WithLabel.args,
...WithLabel.input.args,
isRequired: true,
},
};
});
export const Disabled: Story = {
export const Disabled = meta.story({
args: {
...Default.args,
...Default.input.args,
isDisabled: true,
},
};
});
export const WithIcon: Story = {
export const WithIcon = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: args => (
<PasswordField {...args} size="small" icon={<RiSparklingLine />} />
),
};
});
export const DisabledWithIcon: Story = {
export const DisabledWithIcon = meta.story({
args: {
...WithIcon.args,
...WithIcon.input.args,
isDisabled: true,
},
render: WithIcon.render,
};
render: WithIcon.input.render,
});
export const ShowError: Story = {
export const ShowError = meta.story({
args: {
...WithLabel.args,
...WithLabel.input.args,
},
render: args => (
<Form validationErrors={{ secret: 'Invalid secret' }}>
<PasswordField {...args} />
</Form>
),
};
});
export const Validation: Story = {
export const Validation = meta.story({
args: {
...WithLabel.args,
...WithLabel.input.args,
validate: value => (value === 'admin' ? 'Nice try!' : null),
},
};
});
export const CustomField: Story = {
export const CustomField = meta.story({
render: () => (
<>
<FieldLabel
@@ -145,4 +141,4 @@ export const CustomField: Story = {
/>
</>
),
};
});
@@ -1,5 +1,5 @@
/*
* Copyright 2024 The Backstage Authors
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,19 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import preview from '../../../../../.storybook/preview';
import { RadioGroup, Radio } from './RadioGroup';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/RadioGroup',
component: RadioGroup,
} satisfies Meta<typeof RadioGroup>;
});
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
export const Default = meta.story({
args: {
label: 'What is your favorite pokemon?',
},
@@ -36,11 +32,11 @@ export const Default: Story = {
<Radio value="squirtle">Squirtle</Radio>
</RadioGroup>
),
};
});
export const Horizontal: Story = {
export const Horizontal = meta.story({
args: {
...Default.args,
...Default.input.args,
orientation: 'horizontal',
},
render: args => (
@@ -50,11 +46,11 @@ export const Horizontal: Story = {
<Radio value="squirtle">Squirtle</Radio>
</RadioGroup>
),
};
});
export const Disabled: Story = {
export const Disabled = meta.story({
args: {
...Default.args,
...Default.input.args,
isDisabled: true,
},
render: args => (
@@ -64,11 +60,11 @@ export const Disabled: Story = {
<Radio value="squirtle">Squirtle</Radio>
</RadioGroup>
),
};
});
export const DisabledSingle: Story = {
export const DisabledSingle = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: args => (
<RadioGroup {...args}>
@@ -79,11 +75,11 @@ export const DisabledSingle: Story = {
<Radio value="squirtle">Squirtle</Radio>
</RadioGroup>
),
};
});
export const DisabledAndSelected: Story = {
export const DisabledAndSelected = meta.story({
args: {
...Default.args,
...Default.input.args,
value: 'charmander',
},
render: args => (
@@ -95,11 +91,11 @@ export const DisabledAndSelected: Story = {
<Radio value="squirtle">Squirtle</Radio>
</RadioGroup>
),
};
});
export const Invalid: Story = {
export const Invalid = meta.story({
args: {
...Default.args,
...Default.input.args,
name: 'pokemon',
isInvalid: true,
},
@@ -112,11 +108,11 @@ export const Invalid: Story = {
<Radio value="squirtle">Squirtle</Radio>
</RadioGroup>
),
};
});
export const Validation: Story = {
export const Validation = meta.story({
args: {
...Default.args,
...Default.input.args,
name: 'pokemon',
defaultValue: 'charmander',
validationBehavior: 'aria',
@@ -129,11 +125,11 @@ export const Validation: Story = {
<Radio value="squirtle">Squirtle</Radio>
</RadioGroup>
),
};
});
export const ReadOnly: Story = {
export const ReadOnly = meta.story({
args: {
...Default.args,
...Default.input.args,
isReadOnly: true,
defaultValue: 'charmander',
},
@@ -144,4 +140,4 @@ export const ReadOnly: Story = {
<Radio value="squirtle">Squirtle</Radio>
</RadioGroup>
),
};
});
@@ -1,3 +1,4 @@
import preview from '../../../../../.storybook/preview';
/*
* Copyright 2024 The Backstage Authors
*
@@ -15,7 +16,7 @@
*/
import { useState } from 'react';
import type { Meta, StoryObj } from '@storybook/react-vite';
import type { StoryObj } from '@storybook/react-vite';
import { SearchField } from './SearchField';
import { Form } from 'react-aria-components';
import { Flex } from '../Flex';
@@ -26,7 +27,7 @@ import { Button } from '../Button';
import { Header } from '../Header';
import { MemoryRouter } from 'react-router-dom';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/SearchField',
component: SearchField,
argTypes: {
@@ -40,12 +41,11 @@ const meta = {
control: 'text',
},
},
} satisfies Meta<typeof SearchField>;
});
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
export const Default = meta.story({
args: {
name: 'url',
style: {
@@ -53,11 +53,11 @@ export const Default: Story = {
},
'aria-label': 'Search',
},
};
});
export const Sizes: Story = {
export const Sizes = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: args => (
<Flex direction="row" gap="4" style={{ width: '100%', maxWidth: '600px' }}>
@@ -65,46 +65,46 @@ export const Sizes: Story = {
<SearchField {...args} size="medium" />
</Flex>
),
};
});
export const DefaultValue: Story = {
export const DefaultValue = meta.story({
args: {
...Default.args,
...Default.input.args,
defaultValue: 'https://example.com',
},
};
});
export const WithLabel: Story = {
export const WithLabel = meta.story({
args: {
...Default.args,
...Default.input.args,
label: 'Label',
},
};
});
export const WithDescription: Story = {
export const WithDescription = meta.story({
args: {
...WithLabel.args,
...WithLabel.input.args,
description: 'Description',
},
};
});
export const Required: Story = {
export const Required = meta.story({
args: {
...WithLabel.args,
...WithLabel.input.args,
isRequired: true,
},
};
});
export const Disabled: Story = {
export const Disabled = meta.story({
args: {
...Default.args,
...Default.input.args,
isDisabled: true,
},
};
});
export const WithIcon: Story = {
export const WithIcon = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: args => (
<SearchField
@@ -114,34 +114,34 @@ export const WithIcon: Story = {
icon={<RiEBike2Line />}
/>
),
};
});
export const DisabledWithIcon: Story = {
export const DisabledWithIcon = meta.story({
args: {
...WithIcon.args,
...WithIcon.input.args,
isDisabled: true,
},
};
});
export const ShowError: Story = {
export const ShowError = meta.story({
args: {
...WithLabel.args,
...WithLabel.input.args,
},
render: args => (
<Form validationErrors={{ url: 'Invalid URL' }}>
<SearchField {...args} />
</Form>
),
};
});
export const Validation: Story = {
export const Validation = meta.story({
args: {
...WithLabel.args,
...WithLabel.input.args,
validate: value => (value === 'admin' ? 'Nice try!' : null),
},
};
});
export const CustomField: Story = {
export const CustomField = meta.story({
render: () => (
<>
<FieldLabel
@@ -157,11 +157,11 @@ export const CustomField: Story = {
/>
</>
),
};
});
export const StartCollapsed: Story = {
export const StartCollapsed = meta.story({
args: {
...Default.args,
...Default.input.args,
startCollapsed: true,
},
@@ -174,18 +174,18 @@ export const StartCollapsed: Story = {
<SearchField {...args} size="small" />
</Flex>
),
};
});
export const StartCollapsedWithValue: Story = {
export const StartCollapsedWithValue = meta.story({
args: {
...StartCollapsed.args,
...StartCollapsed.input.args,
defaultValue: 'https://example.com',
},
render: args => <SearchField {...args} size="small" />,
};
});
export const InHeader: Story = {
export const InHeader = meta.story({
decorators: [
Story => (
<MemoryRouter>
@@ -217,11 +217,11 @@ export const InHeader: Story = {
/>
</>
),
};
});
export const StartCollapsedInHeader: Story = {
export const StartCollapsedInHeader = meta.story({
args: {
...StartCollapsed.args,
...StartCollapsed.input.args,
},
decorators: [
Story => (
@@ -254,11 +254,11 @@ export const StartCollapsedInHeader: Story = {
/>
</>
),
};
});
export const StartCollapsedWithButtons: Story = {
export const StartCollapsedWithButtons = meta.story({
args: {
...StartCollapsed.args,
...StartCollapsed.input.args,
},
render: args => (
<Flex direction="row" gap="2" style={{ width: '100%', maxWidth: '600px' }}>
@@ -284,11 +284,11 @@ export const StartCollapsedWithButtons: Story = {
</Button>
</Flex>
),
};
});
export const StartCollapsedWithOnChange: Story = {
export const StartCollapsedWithOnChange = meta.story({
args: {
...StartCollapsed.args,
...StartCollapsed.input.args,
},
render: args => {
const handleChange = (value: string) => {
@@ -305,11 +305,11 @@ export const StartCollapsedWithOnChange: Story = {
</Flex>
);
},
};
});
export const StartCollapsedControlledEmpty: Story = {
export const StartCollapsedControlledEmpty = meta.story({
args: {
...StartCollapsed.args,
...StartCollapsed.input.args,
},
render: function Render(args) {
const [value, setValue] = useState('');
@@ -324,11 +324,11 @@ export const StartCollapsedControlledEmpty: Story = {
</Flex>
);
},
};
});
export const StartCollapsedControlledWithValue: Story = {
export const StartCollapsedControlledWithValue = meta.story({
args: {
...StartCollapsed.args,
...StartCollapsed.input.args,
},
render: function Render(args) {
const [value, setValue] = useState('Component');
@@ -343,4 +343,4 @@ export const StartCollapsedControlledWithValue: Story = {
</Flex>
);
},
};
});
@@ -1,3 +1,4 @@
import preview from '../../../../../.storybook/preview';
/*
* Copyright 2025 The Backstage Authors
*
@@ -13,21 +14,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import type { StoryObj } from '@storybook/react-vite';
import { Select } from './Select';
import { Flex } from '../Flex';
import { Form } from 'react-aria-components';
import { RiCloudLine } from '@remixicon/react';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/Select',
component: Select,
args: {
style: { width: 300 },
},
} satisfies Meta<typeof Select>;
});
export default meta;
type Story = StoryObj<typeof meta>;
const fontOptions = [
@@ -66,23 +66,23 @@ const skills = [
{ value: 'swift', label: 'Swift' },
];
export const Default: Story = {
export const Default = meta.story({
args: {
options: fontOptions,
name: 'font',
},
};
});
export const Searchable: Story = {
export const Searchable = meta.story({
args: {
label: 'Country',
searchable: true,
searchPlaceholder: 'Search countries...',
options: countries,
},
};
});
export const MultipleSelection: Story = {
export const MultipleSelection = meta.story({
args: {
label: 'Select multiple options',
selectionMode: 'multiple',
@@ -93,9 +93,9 @@ export const MultipleSelection: Story = {
{ value: 'option4', label: 'Option 4' },
],
},
};
});
export const SearchableMultiple: Story = {
export const SearchableMultiple = meta.story({
args: {
label: 'Skills',
searchable: true,
@@ -103,9 +103,9 @@ export const SearchableMultiple: Story = {
searchPlaceholder: 'Filter skills...',
options: skills,
},
};
});
export const Preview: Story = {
export const Preview = meta.story({
args: {
label: 'Font Family',
options: fontOptions,
@@ -113,40 +113,40 @@ export const Preview: Story = {
name: 'font',
style: { maxWidth: 260 },
},
};
});
export const WithLabel: Story = {
export const WithLabel = meta.story({
args: {
...Default.args,
...Default.input.args,
label: 'Font Family',
},
};
});
export const WithFullWidth: Story = {
export const WithFullWidth = meta.story({
args: {
...Default.args,
...Default.input.args,
label: 'Font Family',
style: { width: '100%' },
},
};
});
export const WithLabelAndDescription: Story = {
export const WithLabelAndDescription = meta.story({
args: {
...WithLabel.args,
...WithLabel.input.args,
description: 'Choose a font family for your document',
},
};
});
export const WithIcon: Story = {
export const WithIcon = meta.story({
args: {
...WithLabel.args,
...WithLabel.input.args,
},
render: args => <Select {...args} icon={<RiCloudLine />} />,
};
});
export const Sizes: Story = {
export const Sizes = meta.story({
args: {
...Preview.args,
...Preview.input.args,
},
render: args => (
<Flex direction="row" gap="2">
@@ -154,52 +154,52 @@ export const Sizes: Story = {
<Select {...args} size="medium" icon={<RiCloudLine />} />
</Flex>
),
};
});
export const Required: Story = {
export const Required = meta.story({
args: {
...Preview.args,
...Preview.input.args,
isRequired: true,
},
};
});
export const Disabled: Story = {
export const Disabled = meta.story({
args: {
...Preview.args,
...Preview.input.args,
isDisabled: true,
},
};
});
export const DisabledOption: Story = {
export const DisabledOption = meta.story({
args: {
...Preview.args,
...Preview.input.args,
disabledKeys: ['cursive', 'serif'],
},
};
});
export const NoOptions: Story = {
export const NoOptions = meta.story({
args: {
...Preview.args,
...Preview.input.args,
options: undefined,
},
};
});
export const WithValue: Story = {
export const WithValue = meta.story({
args: {
...Preview.args,
...Preview.input.args,
selectedKey: 'mono',
defaultSelectedKey: 'serif',
},
};
});
export const WithDefaultValue: Story = {
export const WithDefaultValue = meta.story({
args: {
...Preview.args,
...Preview.input.args,
defaultSelectedKey: 'serif',
options: fontOptions,
name: 'font',
},
};
});
const generateOptions = (count = 100) => {
const firstWords = [
@@ -323,17 +323,17 @@ const generateOptions = (count = 100) => {
}));
};
export const WithManyOptions: Story = {
export const WithManyOptions = meta.story({
args: {
label: 'Font Family',
options: generateOptions(),
name: 'font',
},
};
});
export const WithError: Story = {
export const WithError = meta.story({
args: {
...WithLabel.args,
...WithLabel.input.args,
name: 'font',
},
render: args => (
@@ -341,9 +341,9 @@ export const WithError: Story = {
<Select {...args} />
</Form>
),
};
});
export const WithLongNames: Story = {
export const WithLongNames = meta.story({
args: {
label: 'Document Template',
options: [
@@ -378,11 +378,11 @@ export const WithLongNames: Story = {
style: { maxWidth: 400 },
defaultSelectedKey: 'annual-report-2024',
},
};
});
export const WithLongNamesAndPadding: Story = {
export const WithLongNamesAndPadding = meta.story({
args: {
...WithLongNames.args,
...WithLongNames.input.args,
},
decorators: [
(Story, { args }) => (
@@ -391,11 +391,11 @@ export const WithLongNamesAndPadding: Story = {
</div>
),
],
};
});
export const WithAccessibilityProps: Story = {
export const WithAccessibilityProps = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: args => (
<Flex direction="column" gap="4">
@@ -422,4 +422,4 @@ export const WithAccessibilityProps: Story = {
</div>
</Flex>
),
};
});
@@ -13,12 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import preview from '../../../../../.storybook/preview';
import { Skeleton } from './Skeleton';
import { Flex } from '../Flex';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/Skeleton',
component: Skeleton,
argTypes: {
@@ -37,24 +36,21 @@ const meta = {
height: 24,
rounded: false,
},
} satisfies Meta<typeof Skeleton>;
});
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
export const Default = meta.story({
args: {},
};
});
export const Rounded: Story = {
export const Rounded = meta.story({
args: {
rounded: true,
width: 48,
height: 48,
},
};
});
export const Demo1: Story = {
export const Demo1 = meta.story({
render: () => (
<Flex gap="4">
<Skeleton rounded width={48} height={48} />
@@ -69,9 +65,9 @@ export const Demo1: Story = {
</Flex>
</Flex>
),
};
});
export const Demo2: Story = {
export const Demo2 = meta.story({
render: () => (
<Flex direction="column" gap="4">
<Skeleton width={400} height={160} />
@@ -79,4 +75,4 @@ export const Demo2: Story = {
<Skeleton width={240} height={12} />
</Flex>
),
};
});
@@ -13,27 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import preview from '../../../../../.storybook/preview';
import { Switch } from './Switch';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/Switch',
component: Switch,
} satisfies Meta<typeof Switch>;
});
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
export const Default = meta.story({
args: {
label: 'Switch',
},
};
});
export const Disabled: Story = {
export const Disabled = meta.story({
args: {
...Default.args,
...Default.input.args,
isDisabled: true,
},
};
});
@@ -1,3 +1,4 @@
import preview from '../../../../../.storybook/preview';
/*
* Copyright 2025 The Backstage Authors
*
@@ -15,7 +16,7 @@
*/
import { useState } from 'react';
import type { Meta, StoryFn, StoryObj } from '@storybook/react-vite';
import type { StoryFn, StoryObj } from '@storybook/react-vite';
import { type Selection } from 'react-aria-components';
import {
Table,
@@ -39,7 +40,7 @@ import { RiCactusLine } from '@remixicon/react';
import { TablePagination } from '../TablePagination';
import { Text } from '../Text';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/Table',
decorators: [
(Story: StoryFn) => (
@@ -48,16 +49,15 @@ const meta = {
</MemoryRouter>
),
],
} satisfies Meta;
});
// Added this fix to fix Chromatic timeout error. This bug is due to rerendering the table with too many rows.
// Work in progress to fix it here - https://github.com/backstage/backstage/pull/30687
const data1 = data1Raw.slice(0, 10);
export default meta;
type Story = StoryObj<typeof meta>;
export const TableOnly: Story = {
export const TableOnly = meta.story({
render: () => {
return (
<Table>
@@ -88,9 +88,9 @@ export const TableOnly: Story = {
</Table>
);
},
};
});
export const WithPaginationUncontrolled: Story = {
export const WithPaginationUncontrolled = meta.story({
render: () => {
const { data, paginationProps } = useTable({ data: data1 });
@@ -122,9 +122,9 @@ export const WithPaginationUncontrolled: Story = {
</>
);
},
};
});
export const WithPaginationControlled: Story = {
export const WithPaginationControlled = meta.story({
render: () => {
const [offset, setOffset] = useState(0);
const [pageSize, setPageSize] = useState(5);
@@ -172,9 +172,9 @@ export const WithPaginationControlled: Story = {
</>
);
},
};
});
export const Sorting: Story = {
export const Sorting = meta.story({
render: () => {
return (
<Table>
@@ -207,9 +207,9 @@ export const Sorting: Story = {
</Table>
);
},
};
});
export const TableRockBand: Story = {
export const TableRockBand = meta.story({
render: () => {
const { data, paginationProps } = useTable({
data: data4,
@@ -246,9 +246,9 @@ export const TableRockBand: Story = {
</>
);
},
};
});
export const RowClick: Story = {
export const RowClick = meta.story({
render: () => {
const { data, paginationProps } = useTable({
data: data4,
@@ -285,9 +285,9 @@ export const RowClick: Story = {
</>
);
},
};
});
export const RowLink: Story = {
export const RowLink = meta.story({
render: () => {
const { data, paginationProps } = useTable({
data: data4,
@@ -324,9 +324,9 @@ export const RowLink: Story = {
</>
);
},
};
});
export const CellComponent: Story = {
export const CellComponent = meta.story({
name: 'Cell',
render: () => {
return (
@@ -354,9 +354,9 @@ export const CellComponent: Story = {
</Table>
);
},
};
});
export const CellTextComponent: Story = {
export const CellTextComponent = meta.story({
name: 'CellText',
render: () => {
return (
@@ -379,9 +379,9 @@ export const CellTextComponent: Story = {
</Table>
);
},
};
});
export const CellProfileComponent: Story = {
export const CellProfileComponent = meta.story({
name: 'CellProfile',
render: () => {
return (
@@ -404,9 +404,9 @@ export const CellProfileComponent: Story = {
</Table>
);
},
};
});
export const SelectionSingleToggle: Story = {
export const SelectionSingleToggle = meta.story({
render: () => {
const [selectedKeys, setSelectedKeys] = useState<Selection>(new Set([]));
@@ -442,9 +442,9 @@ export const SelectionSingleToggle: Story = {
</Table>
);
},
};
});
export const SelectionMultiToggle: Story = {
export const SelectionMultiToggle = meta.story({
render: () => {
const [selectedKeys, setSelectedKeys] = useState<Selection>(new Set([]));
@@ -480,9 +480,9 @@ export const SelectionMultiToggle: Story = {
</Table>
);
},
};
});
export const SelectionSingleReplace: Story = {
export const SelectionSingleReplace = meta.story({
render: () => {
const [selectedKeys, setSelectedKeys] = useState<Selection>(new Set([]));
@@ -518,9 +518,9 @@ export const SelectionSingleReplace: Story = {
</Table>
);
},
};
});
export const SelectionMultiReplace: Story = {
export const SelectionMultiReplace = meta.story({
render: () => {
const [selectedKeys, setSelectedKeys] = useState<Selection>(new Set([]));
@@ -556,9 +556,9 @@ export const SelectionMultiReplace: Story = {
</Table>
);
},
};
});
export const SelectionToggleWithActions: Story = {
export const SelectionToggleWithActions = meta.story({
render: () => {
const [selectedKeys, setSelectedKeys] = useState<Selection>(new Set([]));
@@ -595,9 +595,9 @@ export const SelectionToggleWithActions: Story = {
</Table>
);
},
};
});
export const SelectionReplaceWithActions: Story = {
export const SelectionReplaceWithActions = meta.story({
render: () => {
const [selectedKeys, setSelectedKeys] = useState<Selection>(new Set([]));
@@ -634,9 +634,9 @@ export const SelectionReplaceWithActions: Story = {
</Table>
);
},
};
});
export const SelectionToggleWithLinks: Story = {
export const SelectionToggleWithLinks = meta.story({
render: () => {
const [selectedKeys, setSelectedKeys] = useState<Selection>(new Set([]));
@@ -672,9 +672,9 @@ export const SelectionToggleWithLinks: Story = {
</Table>
);
},
};
});
export const SelectionReplaceWithLinks: Story = {
export const SelectionReplaceWithLinks = meta.story({
render: () => {
const [selectedKeys, setSelectedKeys] = useState<Selection>(new Set([]));
@@ -710,9 +710,9 @@ export const SelectionReplaceWithLinks: Story = {
</Table>
);
},
};
});
export const SelectionWithDisabledRows: Story = {
export const SelectionWithDisabledRows = meta.story({
render: () => {
const [selectedKeys, setSelectedKeys] = useState<Selection>(new Set([]));
@@ -749,9 +749,9 @@ export const SelectionWithDisabledRows: Story = {
</Table>
);
},
};
});
export const SelectionWithPagination: Story = {
export const SelectionWithPagination = meta.story({
render: () => {
const [selectedKeys, setSelectedKeys] = useState<Selection>(new Set([]));
@@ -789,9 +789,9 @@ export const SelectionWithPagination: Story = {
</>
);
},
};
});
export const SelectionModePlayground: Story = {
export const SelectionModePlayground = meta.story({
render: () => {
const [selectionMode, setSelectionMode] = useState<'single' | 'multiple'>(
'multiple',
@@ -849,9 +849,9 @@ export const SelectionModePlayground: Story = {
</Flex>
);
},
};
});
export const SelectionBehaviorPlayground: Story = {
export const SelectionBehaviorPlayground = meta.story({
render: () => {
const [selectionBehavior, setSelectionBehavior] = useState<
'toggle' | 'replace'
@@ -909,4 +909,4 @@ export const SelectionBehaviorPlayground: Story = {
</Flex>
);
},
};
});
@@ -1,5 +1,5 @@
/*
* Copyright 2024 The Backstage Authors
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,13 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// TODO: Bring useArgs() back when we update Storybook to 9
// import { useArgs } from 'storybook/preview-api';
import type { Meta, StoryObj } from '@storybook/react-vite';
import preview from '../../../../../.storybook/preview';
import { TablePagination } from './TablePagination';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/TablePagination',
component: TablePagination,
argTypes: {
@@ -30,12 +27,9 @@ const meta = {
setOffset: { action: 'setOffset' },
setPageSize: { action: 'setPageSize' },
},
} satisfies Meta<typeof TablePagination>;
});
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
export const Default = meta.story({
args: {
offset: 0,
pageSize: 10,
@@ -56,4 +50,4 @@ export const Default: Story = {
/>
);
},
};
});
@@ -1,3 +1,4 @@
import preview from '../../../../../.storybook/preview';
/*
* Copyright 2024 The Backstage Authors
*
@@ -14,18 +15,17 @@
* limitations under the License.
*/
import type { Meta, StoryFn, StoryObj } from '@storybook/react-vite';
import type { StoryFn, StoryObj } from '@storybook/react-vite';
import { Tabs, TabList, Tab, TabPanel } from './Tabs';
import { MemoryRouter } from 'react-router-dom';
import { Box } from '../Box';
import { Text } from '../Text';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/Tabs',
component: Tabs,
} satisfies Meta<typeof Tabs>;
});
export default meta;
type Story = StoryObj<typeof meta>;
const withRouter = (Story: StoryFn) => (
@@ -34,7 +34,7 @@ const withRouter = (Story: StoryFn) => (
</MemoryRouter>
);
export const Default: Story = {
export const Default = meta.story({
args: {
children: '',
},
@@ -48,9 +48,9 @@ export const Default: Story = {
</TabList>
</Tabs>
),
};
});
export const WithTabPanels: Story = {
export const WithTabPanels = meta.story({
args: {
children: '',
},
@@ -73,9 +73,9 @@ export const WithTabPanels: Story = {
</TabPanel>
</Tabs>
),
};
});
export const WithMockedURLTab2: Story = {
export const WithMockedURLTab2 = meta.story({
args: {
children: '',
},
@@ -105,9 +105,9 @@ export const WithMockedURLTab2: Story = {
</Box>
</MemoryRouter>
),
};
});
export const WithMockedURLTab3: Story = {
export const WithMockedURLTab3 = meta.story({
args: {
children: '',
},
@@ -137,9 +137,9 @@ export const WithMockedURLTab3: Story = {
</Box>
</MemoryRouter>
),
};
});
export const WithMockedURLNoMatch: Story = {
export const WithMockedURLNoMatch = meta.story({
args: {
children: '',
},
@@ -173,11 +173,11 @@ export const WithMockedURLNoMatch: Story = {
</Box>
</MemoryRouter>
),
};
});
// New stories for testing match strategies
export const ExactMatchingDefault: Story = {
export const ExactMatchingDefault = meta.story({
args: {
children: '',
},
@@ -211,9 +211,9 @@ export const ExactMatchingDefault: Story = {
</Box>
</MemoryRouter>
),
};
});
export const PrefixMatchingForNestedRoutes: Story = {
export const PrefixMatchingForNestedRoutes = meta.story({
args: {
children: '',
},
@@ -251,9 +251,9 @@ export const PrefixMatchingForNestedRoutes: Story = {
</Box>
</MemoryRouter>
),
};
});
export const PrefixMatchingDeepNesting: Story = {
export const PrefixMatchingDeepNesting = meta.story({
args: {
children: '',
},
@@ -286,9 +286,9 @@ export const PrefixMatchingDeepNesting: Story = {
</Box>
</MemoryRouter>
),
};
});
export const MixedMatchingStrategies: Story = {
export const MixedMatchingStrategies = meta.story({
args: {
children: '',
},
@@ -337,9 +337,9 @@ export const MixedMatchingStrategies: Story = {
</Box>
</MemoryRouter>
),
};
});
export const PrefixMatchingEdgeCases: Story = {
export const PrefixMatchingEdgeCases = meta.story({
args: {
children: '',
},
@@ -379,9 +379,9 @@ export const PrefixMatchingEdgeCases: Story = {
</Box>
</MemoryRouter>
),
};
});
export const PrefixMatchingWithSlash: Story = {
export const PrefixMatchingWithSlash = meta.story({
args: {
children: '',
},
@@ -420,9 +420,9 @@ export const PrefixMatchingWithSlash: Story = {
</Box>
</MemoryRouter>
),
};
});
export const RootPathMatching: Story = {
export const RootPathMatching = meta.story({
args: {
children: '',
},
@@ -454,9 +454,9 @@ export const RootPathMatching: Story = {
</Box>
</MemoryRouter>
),
};
});
export const AutoSelectionOfTabs: Story = {
export const AutoSelectionOfTabs = meta.story({
args: {
children: '',
},
@@ -511,4 +511,4 @@ export const AutoSelectionOfTabs: Story = {
</div>
</MemoryRouter>
),
};
});
@@ -1,3 +1,4 @@
import preview from '../../../../../.storybook/preview';
/*
* Copyright 2025 The Backstage Authors
*
@@ -15,7 +16,7 @@
*/
import { useState } from 'react';
import type { Meta, StoryObj } from '@storybook/react-vite';
import type { StoryObj } from '@storybook/react-vite';
import { TagGroup, Tag } from '.';
import type { Selection } from 'react-aria-components';
import { Flex } from '../../';
@@ -35,7 +36,7 @@ export interface ListItem {
isDisabled?: boolean;
}
const meta = {
const meta = preview.meta({
title: 'Backstage UI/TagGroup',
component: TagGroup<ListItem>,
argTypes: {
@@ -54,9 +55,8 @@ const meta = {
</MemoryRouter>
),
],
} satisfies Meta<typeof TagGroup<ListItem>>;
});
export default meta;
type Story = StoryObj<typeof meta>;
const initialList: ListItem[] = [
@@ -74,7 +74,7 @@ const initialList: ListItem[] = [
{ id: 'strawberry', name: 'Strawberry', icon: <RiHeartLine /> },
];
export const Default: Story = {
export const Default = meta.story({
args: {
'aria-label': 'Tag Group',
},
@@ -85,11 +85,11 @@ export const Default: Story = {
))}
</TagGroup>
),
};
});
export const Sizes: Story = {
export const Sizes = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: args => (
<Flex direction="column">
@@ -109,9 +109,9 @@ export const Sizes: Story = {
</TagGroup>
</Flex>
),
};
});
export const SelectionModeSingle: Story = {
export const SelectionModeSingle = meta.story({
args: {
selectionMode: 'single',
'aria-label': 'Tag Group',
@@ -130,9 +130,9 @@ export const SelectionModeSingle: Story = {
</TagGroup>
);
},
};
});
export const SelectionModeMultiple: Story = {
export const SelectionModeMultiple = meta.story({
args: {
selectionMode: 'multiple',
'aria-label': 'Tag Group',
@@ -153,11 +153,11 @@ export const SelectionModeMultiple: Story = {
</TagGroup>
);
},
};
});
export const WithIcon: Story = {
export const WithIcon = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: args => (
<TagGroup {...args}>
@@ -168,9 +168,9 @@ export const WithIcon: Story = {
))}
</TagGroup>
),
};
});
export const WithLink: Story = {
export const WithLink = meta.story({
render: args => (
<TagGroup {...args}>
{initialList.map(item => (
@@ -180,9 +180,9 @@ export const WithLink: Story = {
))}
</TagGroup>
),
};
});
export const Disabled: Story = {
export const Disabled = meta.story({
render: args => (
<TagGroup {...args}>
{initialList.map(item => (
@@ -192,11 +192,11 @@ export const Disabled: Story = {
))}
</TagGroup>
),
};
});
export const RemovingTags: Story = {
export const RemovingTags = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: args => {
const [selected, setSelected] = useState<Selection>(new Set(['travel']));
@@ -217,11 +217,11 @@ export const RemovingTags: Story = {
</TagGroup>
);
},
};
});
export const WithIconAndRemoveButton: Story = {
export const WithIconAndRemoveButton = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: args => {
const [selected, setSelected] = useState<Selection>(new Set(['travel']));
@@ -244,4 +244,4 @@ export const WithIconAndRemoveButton: Story = {
</TagGroup>
);
},
};
});
@@ -1,3 +1,4 @@
import preview from '../../../../../.storybook/preview';
/*
* Copyright 2024 The Backstage Authors
*
@@ -14,31 +15,30 @@
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import type { StoryObj } from '@storybook/react-vite';
import { Text } from './Text';
import { Flex } from '../Flex';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/Text',
component: Text,
args: {
children: 'Text',
},
} satisfies Meta<typeof Text>;
});
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
export const Default = meta.story({
args: {
children:
"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?",
},
};
});
export const AllVariants: Story = {
export const AllVariants = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: args => (
<Flex gap="6" direction="column">
@@ -52,9 +52,9 @@ export const AllVariants: Story = {
<Text {...args} variant="body-x-small" style={{ maxWidth: '224px' }} />
</Flex>
),
};
});
export const AllWeights: Story = {
export const AllWeights = meta.story({
render: () => (
<Flex gap="4" direction="column">
<Flex>
@@ -91,11 +91,11 @@ export const AllWeights: Story = {
</Flex>
</Flex>
),
};
});
export const AllColors: Story = {
export const AllColors = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: args => (
<Flex gap="4" direction="column">
@@ -106,30 +106,30 @@ export const AllColors: Story = {
<Text {...args} color="success" children="I am success" />
</Flex>
),
};
});
export const Truncate: Story = {
export const Truncate = meta.story({
args: {
...Default.args,
...Default.input.args,
truncate: true,
as: 'p',
style: { width: '480px' },
},
};
});
export const Responsive: Story = {
export const Responsive = meta.story({
args: {
...Default.args,
...Default.input.args,
variant: {
xs: 'title-x-small',
md: 'body-large',
},
},
};
});
export const WrappedInLink: Story = {
export const WrappedInLink = meta.story({
args: {
...Default.args,
...Default.input.args,
},
decorators: [
Story => (
@@ -138,16 +138,16 @@ export const WrappedInLink: Story = {
</a>
),
],
};
});
export const CustomRender: Story = {
export const CustomRender = meta.story({
args: {
...Default.args,
...Default.input.args,
as: 'label',
},
};
});
export const Playground: Story = {
export const Playground = meta.story({
render: () => (
<Flex gap="4" direction="column">
<Text>Subtitle</Text>
@@ -176,4 +176,4 @@ export const Playground: Story = {
</Text>
</Flex>
),
};
});
@@ -1,5 +1,5 @@
/*
* Copyright 2024 The Backstage Authors
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,15 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import preview from '../../../../../.storybook/preview';
import { TextField } from './TextField';
import { Form } from 'react-aria-components';
import { Flex } from '../Flex';
import { FieldLabel } from '../FieldLabel';
import { RiEyeLine, RiSparklingLine } from '@remixicon/react';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/TextField',
component: TextField,
argTypes: {
@@ -32,12 +31,9 @@ const meta = {
control: 'object',
},
},
} satisfies Meta<typeof TextField>;
});
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
export const Default = meta.story({
args: {
name: 'url',
placeholder: 'Enter a URL',
@@ -45,11 +41,11 @@ export const Default: Story = {
maxWidth: '300px',
},
},
};
});
export const Sizes: Story = {
export const Sizes = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: args => (
<Flex direction="row" gap="4" style={{ width: '100%', maxWidth: '600px' }}>
@@ -57,46 +53,46 @@ export const Sizes: Story = {
<TextField {...args} size="medium" icon={<RiSparklingLine />} />
</Flex>
),
};
});
export const DefaultValue: Story = {
export const DefaultValue = meta.story({
args: {
...Default.args,
...Default.input.args,
defaultValue: 'https://example.com',
},
};
});
export const WithLabel: Story = {
export const WithLabel = meta.story({
args: {
...Default.args,
...Default.input.args,
label: 'Label',
},
};
});
export const WithDescription: Story = {
export const WithDescription = meta.story({
args: {
...WithLabel.args,
...WithLabel.input.args,
description: 'Description',
},
};
});
export const Required: Story = {
export const Required = meta.story({
args: {
...WithLabel.args,
...WithLabel.input.args,
isRequired: true,
},
};
});
export const Disabled: Story = {
export const Disabled = meta.story({
args: {
...Default.args,
...Default.input.args,
isDisabled: true,
},
};
});
export const WithIcon: Story = {
export const WithIcon = meta.story({
args: {
...Default.args,
...Default.input.args,
},
render: args => (
<TextField
@@ -106,35 +102,35 @@ export const WithIcon: Story = {
icon={<RiEyeLine />}
/>
),
};
});
export const DisabledWithIcon: Story = {
export const DisabledWithIcon = meta.story({
args: {
...WithIcon.args,
...WithIcon.input.args,
isDisabled: true,
},
render: WithIcon.render,
};
render: WithIcon.input.render,
});
export const ShowError: Story = {
export const ShowError = meta.story({
args: {
...WithLabel.args,
...WithLabel.input.args,
},
render: args => (
<Form validationErrors={{ url: 'Invalid URL' }}>
<TextField {...args} />
</Form>
),
};
});
export const Validation: Story = {
export const Validation = meta.story({
args: {
...WithLabel.args,
...WithLabel.input.args,
validate: value => (value === 'admin' ? 'Nice try!' : null),
},
};
});
export const CustomField: Story = {
export const CustomField = meta.story({
render: () => (
<>
<FieldLabel
@@ -150,4 +146,4 @@ export const CustomField: Story = {
/>
</>
),
};
});
@@ -1,5 +1,5 @@
/*
* Copyright 2024 The Backstage Authors
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,13 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import preview from '../../../../../.storybook/preview';
import { Placement } from '@react-types/overlays';
import { TooltipTrigger, Tooltip } from './Tooltip';
import { Button } from '../Button/Button';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/Tooltip',
component: TooltipTrigger,
parameters: { layout: 'centered' },
@@ -52,54 +51,44 @@ const meta = {
<Tooltip placement={placement}>{children ?? 'I am a tooltip'}</Tooltip>
</TooltipTrigger>
),
} as Meta<{
children?: string;
isOpen?: boolean;
isDisabled?: boolean;
placement?: Placement;
delay?: number;
closeDelay?: number;
}>;
});
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
export const Default = meta.story({
args: {
children: 'I am a tooltip',
},
};
});
export const IsOpen: Story = {
export const IsOpen = meta.story({
args: {
...Default.args,
...Default.input.args,
isOpen: true,
},
};
});
export const IsDisabled: Story = {
export const IsDisabled = meta.story({
args: {
...Default.args,
...Default.input.args,
isDisabled: true,
},
};
});
export const NoDelays: Story = {
export const NoDelays = meta.story({
args: {
...Default.args,
...Default.input.args,
delay: 0,
closeDelay: 0,
},
};
});
export const OrthogonalPlacements: Story = {
export const OrthogonalPlacements = meta.story({
parameters: {
controls: {
exclude: ['placement'],
},
},
args: {
...Default.args,
...Default.input.args,
isOpen: true,
},
render: ({ isOpen, children }) => {
@@ -113,13 +102,13 @@ export const OrthogonalPlacements: Story = {
</TooltipTrigger>
);
},
};
});
export const WithLongText: Story = {
export const WithLongText = meta.story({
args: {
...Default.args,
...Default.input.args,
isOpen: true,
children:
'I am a tooltip with a very long text. orem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
},
};
});
@@ -1,5 +1,5 @@
/*
* Copyright 2024 The Backstage Authors
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,13 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import preview from '../../../../../.storybook/preview';
import { VisuallyHidden } from './VisuallyHidden';
import { Text } from '../Text';
import { Flex } from '../Flex';
const meta = {
const meta = preview.meta({
title: 'Backstage UI/VisuallyHidden',
component: VisuallyHidden,
parameters: {
@@ -30,12 +29,9 @@ const meta = {
},
},
},
} satisfies Meta<typeof VisuallyHidden>;
});
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
export const Default = meta.story({
render: () => (
<Flex direction="column" gap="4">
<Text as="p">
@@ -48,9 +44,9 @@ export const Default: Story = {
</VisuallyHidden>
</Flex>
),
};
});
export const ExampleUsage: Story = {
export const ExampleUsage = meta.story({
render: () => (
<Flex direction="column" gap="4">
<VisuallyHidden>
@@ -70,4 +66,4 @@ export const ExampleUsage: Story = {
</Text>
</Flex>
),
};
});