Enforce usage of CSF3 story in storybook (#32610)

* chore: enforce usage of CSF3 story by eslint

* chore: migrate remaining stories to CSF3
This commit is contained in:
Florian Duros
2026-02-23 17:14:25 +00:00
committed by GitHub
parent 39317123c7
commit 9c050c58b7
9 changed files with 124 additions and 106 deletions
+1
View File
@@ -65,6 +65,7 @@ module.exports = {
allowInterfaces: "with-single-extends", allowInterfaces: "with-single-extends",
}, },
], ],
"storybook/meta-satisfies-type": "error",
}, },
overrides: [ overrides: [
{ {
@@ -5,25 +5,25 @@
* Please see LICENSE files in the repository root for full details. * Please see LICENSE files in the repository root for full details.
*/ */
import React from "react"; import type { Meta, StoryObj } from "@storybook/react-vite";
import type { Meta, StoryFn } from "@storybook/react-vite";
import { Clock } from "./Clock"; import { Clock } from "./Clock";
export default { const meta = {
title: "Audio/Clock", title: "Audio/Clock",
component: Clock, component: Clock,
tags: ["autodocs"], tags: ["autodocs"],
args: { args: {
seconds: 20, seconds: 20,
}, },
} as Meta<typeof Clock>; } satisfies Meta<typeof Clock>;
const Template: StoryFn<typeof Clock> = (args) => <Clock {...args} />; export default meta;
type Story = StoryObj<typeof meta>;
export const Default = Template.bind({}); export const Default: Story = {};
export const LotOfSeconds = Template.bind({}); export const LotOfSeconds: Story = {
LotOfSeconds.args = { args: {
seconds: 99999999999999, seconds: 99999999999999,
},
}; };
@@ -9,9 +9,9 @@ import React from "react";
import { useArgs } from "storybook/preview-api"; import { useArgs } from "storybook/preview-api";
import { SeekBar } from "./SeekBar"; import { SeekBar } from "./SeekBar";
import type { Meta, StoryFn } from "@storybook/react-vite"; import type { Meta, StoryObj } from "@storybook/react-vite";
export default { const meta = {
title: "Audio/SeekBar", title: "Audio/SeekBar",
component: SeekBar, component: SeekBar,
tags: ["autodocs"], tags: ["autodocs"],
@@ -23,16 +23,19 @@ export default {
args: { args: {
value: 50, value: 50,
}, },
} as Meta<typeof SeekBar>; render: function Render(args) {
const [, updateArgs] = useArgs();
return <SeekBar onChange={(evt) => updateArgs({ value: parseInt(evt.target.value, 10) })} {...args} />;
},
} satisfies Meta<typeof SeekBar>;
const Template: StoryFn<typeof SeekBar> = (args) => { export default meta;
const [, updateArgs] = useArgs(); type Story = StoryObj<typeof meta>;
return <SeekBar onChange={(evt) => updateArgs({ value: parseInt(evt.target.value, 10) })} {...args} />;
};
export const Default = Template.bind({}); export const Default: Story = {};
export const Disabled = Template.bind({}); export const Disabled: Story = {
Disabled.args = { args: {
disabled: true, disabled: true,
},
}; };
@@ -8,10 +8,10 @@
import React from "react"; import React from "react";
import { LockSolidIcon, ErrorSolidIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; import { LockSolidIcon, ErrorSolidIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import type { Meta, StoryFn } from "@storybook/react-vite"; import type { Meta, StoryObj } from "@storybook/react-vite";
import { EventTileBubble } from "./EventTileBubble"; import { EventTileBubble } from "./EventTileBubble";
export default { const meta = {
title: "Event/EventTileBubble", title: "Event/EventTileBubble",
component: EventTileBubble, component: EventTileBubble,
tags: ["autodocs"], tags: ["autodocs"],
@@ -21,29 +21,33 @@ export default {
subtitle: "Subtitle goes here", subtitle: "Subtitle goes here",
className: "custom-class", className: "custom-class",
}, },
} as Meta<typeof EventTileBubble>; } satisfies Meta<typeof EventTileBubble>;
const Template: StoryFn<typeof EventTileBubble> = (args) => <EventTileBubble {...args} />; export default meta;
type Story = StoryObj<typeof meta>;
export const Default = Template.bind({}); export const Default: Story = {};
export const HasLockSolidIcon = Template.bind({}); export const HasLockSolidIcon: Story = {
HasLockSolidIcon.args = { args: {
className: undefined, className: undefined,
icon: <LockSolidIcon />, icon: <LockSolidIcon />,
children: undefined, children: undefined,
},
}; };
export const HasChildren = Template.bind({}); export const HasChildren: Story = {
HasChildren.args = { args: {
className: undefined, className: undefined,
children: <div>children</div>, children: <div>children</div>,
},
}; };
export const IsCryptoEventBubble = Template.bind({}); export const IsCryptoEventBubble: Story = {
IsCryptoEventBubble.args = { args: {
className: undefined, className: undefined,
icon: <LockSolidIcon />, icon: <LockSolidIcon />,
title: "Encryption enabled", title: "Encryption enabled",
subtitle: "Messages here are end-to-end encrypted. Verify XYZ in their profile - tap on their profile picture.", subtitle: "Messages here are end-to-end encrypted. Verify XYZ in their profile - tap on their profile picture.",
},
}; };
@@ -5,21 +5,21 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details. Please see LICENSE files in the repository root for full details.
*/ */
import React from "react"; import { type Meta, type StoryObj } from "@storybook/react-vite";
import { type Meta, type StoryFn } from "@storybook/react-vite";
import { TextualEventView as TextualEventComponent } from "./TextualEventView"; import { TextualEventView as TextualEventComponent } from "./TextualEventView";
import { MockViewModel } from "../../viewmodel/MockViewModel"; import { MockViewModel } from "../../viewmodel/MockViewModel";
export default { const meta = {
title: "Event/TextualEvent", title: "Event/TextualEvent",
component: TextualEventComponent, component: TextualEventComponent,
tags: ["autodocs"], tags: ["autodocs"],
args: { args: {
vm: new MockViewModel({ content: "Dummy textual event text" }), vm: new MockViewModel({ content: "Dummy textual event text" }),
}, },
} as Meta<typeof TextualEventComponent>; } satisfies Meta<typeof TextualEventComponent>;
const Template: StoryFn<typeof TextualEventComponent> = (args) => <TextualEventComponent {...args} />; export default meta;
type Story = StoryObj<typeof meta>;
export const Default = Template.bind({}); export const Default: Story = {};
@@ -5,20 +5,19 @@
* Please see LICENSE files in the repository root for full details. * Please see LICENSE files in the repository root for full details.
*/ */
import React from "react";
import { MediaBody } from "./MediaBody"; import { MediaBody } from "./MediaBody";
import type { Meta, StoryFn } from "@storybook/react-vite"; import type { Meta, StoryObj } from "@storybook/react-vite";
export default { const meta = {
title: "MessageBody/MediaBody", title: "MessageBody/MediaBody",
component: MediaBody, component: MediaBody,
tags: ["autodocs"], tags: ["autodocs"],
args: { args: {
children: "Media content goes here", children: "Media content goes here",
}, },
} as Meta<typeof MediaBody>; } satisfies Meta<typeof MediaBody>;
const Template: StoryFn<typeof MediaBody> = (args) => <MediaBody {...args} />; export default meta;
type Story = StoryObj<typeof meta>;
export const Default = Template.bind({}); export const Default: Story = {};
@@ -7,11 +7,11 @@
import React from "react"; import React from "react";
import type { Meta, StoryFn } from "@storybook/react-vite"; import type { Meta, StoryObj } from "@storybook/react-vite";
import TimelineSeparator from "./TimelineSeparator"; import TimelineSeparator from "./TimelineSeparator";
import styles from "./TimelineSeparator.module.css"; import styles from "./TimelineSeparator.module.css";
export default { const meta = {
title: "MessageBody/TimelineSeparator", title: "MessageBody/TimelineSeparator",
component: TimelineSeparator, component: TimelineSeparator,
tags: ["autodocs"], tags: ["autodocs"],
@@ -19,36 +19,41 @@ export default {
label: "Label Separator", label: "Label Separator",
children: "Timeline Separator", children: "Timeline Separator",
}, },
} as Meta<typeof TimelineSeparator>; } satisfies Meta<typeof TimelineSeparator>;
const Template: StoryFn<typeof TimelineSeparator> = (args) => <TimelineSeparator {...args} />; export default meta;
type Story = StoryObj<typeof meta>;
export const Default = Template.bind({}); export const Default: Story = {};
export const WithHtmlChild = Template.bind({}); export const WithHtmlChild: Story = {
WithHtmlChild.args = { args: {
label: "Custom Label", label: "Custom Label",
children: ( children: (
<h2 className={styles.timelineSeparator} aria-hidden="true"> <h2 className={styles.timelineSeparator} aria-hidden="true">
Thursday Thursday
</h2> </h2>
), ),
},
}; };
export const WithDateEvent = Template.bind({}); export const WithDateEvent: Story = {
WithDateEvent.args = { args: {
label: "Date Event Separator", label: "Date Event Separator",
children: "Wednesday", children: "Wednesday",
},
}; };
export const WithLateEvent = Template.bind({}); export const WithLateEvent: Story = {
WithLateEvent.args = { args: {
label: "Late Event Separator", label: "Late Event Separator",
children: "Fri, Jan 9, 2026", children: "Fri, Jan 9, 2026",
},
}; };
export const WithoutChildren = Template.bind({}); export const WithoutChildren: Story = {
WithoutChildren.args = { args: {
children: undefined, children: undefined,
label: "Separator without children", label: "Separator without children",
},
}; };
@@ -8,12 +8,12 @@
import React from "react"; import React from "react";
import { fn } from "storybook/test"; import { fn } from "storybook/test";
import type { Meta, StoryFn } from "@storybook/react-vite"; import type { Meta, StoryObj } from "@storybook/react-vite";
import { RichItem } from "./RichItem"; import { RichItem } from "./RichItem";
const currentTimestamp = new Date("2025-03-09T12:00:00Z").getTime(); const currentTimestamp = new Date("2025-03-09T12:00:00Z").getTime();
export default { const meta = {
title: "RichList/RichItem", title: "RichList/RichItem",
component: RichItem, component: RichItem,
tags: ["autodocs"], tags: ["autodocs"],
@@ -32,33 +32,39 @@ export default {
context: "button", context: "button",
}, },
}, },
} as Meta<typeof RichItem>; render: (args) => (
<ul role="listbox" style={{ all: "unset", listStyle: "none" }}>
<RichItem {...args} />
</ul>
),
} satisfies Meta<typeof RichItem>;
const Template: StoryFn<typeof RichItem> = (args) => ( export default meta;
<ul role="listbox" style={{ all: "unset", listStyle: "none" }}> type Story = StoryObj<typeof meta>;
<RichItem {...args} />
</ul>
);
export const Default = Template.bind({}); export const Default: Story = {};
export const Selected = Template.bind({}); export const Selected: Story = {
Selected.args = { args: {
selected: true, selected: true,
},
}; };
export const WithoutTimestamp = Template.bind({}); export const WithoutTimestamp: Story = {
WithoutTimestamp.args = { args: {
timestamp: undefined, timestamp: undefined,
},
}; };
export const Hover = Template.bind({}); export const Hover: Story = {
Hover.parameters = { pseudo: { hover: true } }; parameters: { pseudo: { hover: true } },
};
const TemplateSeparator: StoryFn<typeof RichItem> = (args) => ( export const Separator: Story = {
<ul role="listbox" style={{ all: "unset", listStyle: "none" }}> render: (args) => (
<RichItem {...args} /> <ul role="listbox" style={{ all: "unset", listStyle: "none" }}>
<RichItem {...args} /> <RichItem {...args} />
</ul> <RichItem {...args} />
); </ul>
export const Separator = TemplateSeparator.bind({}); ),
};
@@ -12,7 +12,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite";
import { RoomListPrimaryFilters } from "./RoomListPrimaryFilters"; import { RoomListPrimaryFilters } from "./RoomListPrimaryFilters";
import type { FilterId } from "./useVisibleFilters"; import type { FilterId } from "./useVisibleFilters";
const meta: Meta<typeof RoomListPrimaryFilters> = { const meta = {
title: "Room List/RoomListPrimaryFilters", title: "Room List/RoomListPrimaryFilters",
component: RoomListPrimaryFilters, component: RoomListPrimaryFilters,
tags: ["autodocs"], tags: ["autodocs"],
@@ -25,10 +25,10 @@ const meta: Meta<typeof RoomListPrimaryFilters> = {
url: "https://www.figma.com/design/vlmt46QDdE4dgXDiyBJXqp/ER-33-Left-Panel-2025?node-id=98-1979&t=vafb4zoYMNLRuAbh-4", url: "https://www.figma.com/design/vlmt46QDdE4dgXDiyBJXqp/ER-33-Left-Panel-2025?node-id=98-1979&t=vafb4zoYMNLRuAbh-4",
}, },
}, },
}; } satisfies Meta<typeof RoomListPrimaryFilters>;
export default meta; export default meta;
type Story = StoryObj<typeof RoomListPrimaryFilters>; type Story = StoryObj<typeof meta>;
// All available filter IDs // All available filter IDs
const allFilterIds: FilterId[] = ["unread", "people", "rooms", "favourite", "mentions", "invites", "low_priority"]; const allFilterIds: FilterId[] = ["unread", "people", "rooms", "favourite", "mentions", "invites", "low_priority"];