chore(stories): eslint/tsconfig migration

Migrate Storybook package to eslint flat config + composite tsconfig.
This commit is contained in:
2026-06-07 16:30:05 +07:00
parent 09272dffeb
commit 23a2795523
18 changed files with 687 additions and 0 deletions
@@ -0,0 +1,48 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite';
import { AspectRatio } from '@robonen/primitives';
const meta = {
title: 'Layout/AspectRatio',
component: AspectRatio,
tags: ['autodocs'],
argTypes: {
ratio: { control: { type: 'number', min: 0.1, step: 0.1 } },
},
args: { ratio: 16 / 9 },
} satisfies Meta<typeof AspectRatio>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Widescreen: Story = {
render: args => ({
components: { AspectRatio },
setup: () => ({ args }),
template: `
<div style="width: 400px; border-radius: 8px; overflow: hidden">
<AspectRatio v-bind="args">
<img
src="https://images.unsplash.com/photo-1535025183041-0991a977e25b?w=800"
alt="landscape"
style="width:100%;height:100%;object-fit:cover"
/>
</AspectRatio>
</div>
`,
}),
};
export const Square: Story = {
args: { ratio: 1 },
render: args => ({
components: { AspectRatio },
setup: () => ({ args }),
template: `
<div style="width: 200px; background: #eee; border-radius: 8px; overflow: hidden">
<AspectRatio v-bind="args">
<div style="display:flex;align-items:center;justify-content:center;height:100%;font-family:system-ui">1 : 1</div>
</AspectRatio>
</div>
`,
}),
};
+35
View File
@@ -0,0 +1,35 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite';
import { AvatarFallback, AvatarImage, AvatarRoot } from '@robonen/primitives';
const meta = {
title: 'Media/Avatar',
component: AvatarRoot,
tags: ['autodocs'],
} satisfies Meta<typeof AvatarRoot>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Loaded: Story = {
render: () => ({
components: { AvatarRoot, AvatarImage, AvatarFallback },
template: `
<AvatarRoot class="sb-avatar">
<AvatarImage src="https://i.pravatar.cc/96?img=5" alt="User" class="sb-avatar-img" />
<AvatarFallback class="sb-avatar-fallback" :delayMs="300">CT</AvatarFallback>
</AvatarRoot>
`,
}),
};
export const Fallback: Story = {
render: () => ({
components: { AvatarRoot, AvatarImage, AvatarFallback },
template: `
<AvatarRoot class="sb-avatar">
<AvatarImage src="https://invalid.example.com/missing.png" alt="User" class="sb-avatar-img" />
<AvatarFallback class="sb-avatar-fallback">AB</AvatarFallback>
</AvatarRoot>
`,
}),
};
@@ -0,0 +1,32 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite';
import { CollapsibleContent, CollapsibleRoot, CollapsibleTrigger } from '@robonen/primitives';
const meta = {
title: 'Disclosure/Collapsible',
component: CollapsibleRoot,
tags: ['autodocs'],
args: { defaultOpen: false, disabled: false },
} satisfies Meta<typeof CollapsibleRoot>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
render: args => ({
components: { CollapsibleRoot, CollapsibleTrigger, CollapsibleContent },
setup: () => ({ args }),
template: `
<CollapsibleRoot v-bind="args" class="sb-collapsible">
<CollapsibleTrigger class="sb-collapsible-trigger">
<template #default="{ open }">{{ open ? 'Hide' : 'Show' }} details</template>
</CollapsibleTrigger>
<CollapsibleContent class="sb-collapsible-content">
<p style="margin:0.5rem 0 0">Hidden content revealed when the trigger is activated.</p>
</CollapsibleContent>
</CollapsibleRoot>
`,
}),
};
export const OpenByDefault: Story = { args: { defaultOpen: true }, render: Default.render };
export const Disabled: Story = { args: { disabled: true }, render: Default.render };
+94
View File
@@ -0,0 +1,94 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite';
import {
DialogClose,
DialogContent,
DialogDescription,
DialogOverlay,
DialogPortal,
DialogRoot,
DialogTitle,
DialogTrigger,
} from '@robonen/primitives';
const meta = {
title: 'Overlays/Dialog',
component: DialogRoot,
tags: ['autodocs'],
argTypes: {
modal: { control: 'boolean' },
defaultOpen: { control: 'boolean' },
},
args: {
modal: true,
defaultOpen: false,
},
} satisfies Meta<typeof DialogRoot>;
export default meta;
type Story = StoryObj<typeof meta>;
const render = (args: Record<string, unknown>) => ({
components: {
DialogRoot,
DialogTrigger,
DialogPortal,
DialogOverlay,
DialogContent,
DialogTitle,
DialogDescription,
DialogClose,
},
setup: () => ({ args }),
template: `
<DialogRoot v-bind="args">
<DialogTrigger class="sb-dialog-trigger">Open Dialog</DialogTrigger>
<DialogPortal>
<DialogOverlay class="sb-dialog-overlay" />
<DialogContent class="sb-dialog-content">
<DialogTitle class="sb-dialog-title">Dialog Title</DialogTitle>
<DialogDescription class="sb-dialog-desc">
Traps focus, locks scroll, and dismisses on Escape or outside click.
</DialogDescription>
<DialogClose class="sb-dialog-close">Close</DialogClose>
</DialogContent>
</DialogPortal>
</DialogRoot>
`,
});
export const Default: Story = { render };
export const OpenByDefault: Story = {
args: { defaultOpen: true },
render,
};
export const NonModal: Story = {
args: { modal: false },
render: args => ({
components: {
DialogRoot,
DialogTrigger,
DialogPortal,
DialogContent,
DialogTitle,
DialogDescription,
DialogClose,
},
setup: () => ({ args }),
template: `
<DialogRoot v-bind="args">
<DialogTrigger class="sb-dialog-trigger">Open non-modal</DialogTrigger>
<DialogPortal>
<DialogContent class="sb-dialog-content">
<DialogTitle class="sb-dialog-title">Non-modal dialog</DialogTitle>
<DialogDescription class="sb-dialog-desc">
No overlay, no scroll lock, no focus trap.
</DialogDescription>
<DialogClose class="sb-dialog-close">Close</DialogClose>
</DialogContent>
</DialogPortal>
</DialogRoot>
`,
}),
};
+37
View File
@@ -0,0 +1,37 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite';
import { Label } from '@robonen/primitives';
const meta = {
title: 'Forms/Label',
component: Label,
tags: ['autodocs'],
} satisfies Meta<typeof Label>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
render: () => ({
components: { Label },
template: `
<div style="font-family: system-ui; display: grid; gap: 0.25rem; max-width: 300px">
<Label for="email">Email address</Label>
<input id="email" type="email" placeholder="you@example.com" style="padding:0.5rem;border:1px solid #888;border-radius:4px" />
</div>
`,
}),
};
export const WithCheckbox: Story = {
render: () => ({
components: { Label },
template: `
<div style="font-family: system-ui">
<Label style="display:flex;align-items:center;gap:0.5rem;cursor:pointer">
<input type="checkbox" />
Subscribe to the newsletter
</Label>
</div>
`,
}),
};
+43
View File
@@ -0,0 +1,43 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite';
import { ProgressIndicator, ProgressRoot } from '@robonen/primitives';
const meta = {
title: 'Feedback/Progress',
component: ProgressRoot,
tags: ['autodocs'],
argTypes: {
modelValue: { control: { type: 'number', min: 0, max: 100, step: 1 } },
max: { control: { type: 'number', min: 1 } },
},
args: { modelValue: 40, max: 100 },
} satisfies Meta<typeof ProgressRoot>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Determinate: Story = {
render: args => ({
components: { ProgressRoot, ProgressIndicator },
setup: () => ({ args }),
template: `
<ProgressRoot v-bind="args" class="sb-progress">
<template #default="{ value, max }">
<ProgressIndicator
class="sb-progress-ind"
:style="{ width: value == null ? '100%' : (value / max * 100) + '%' }"
/>
</template>
</ProgressRoot>
`,
}),
};
export const Indeterminate: Story = {
args: { modelValue: null },
render: Determinate.render,
};
export const Complete: Story = {
args: { modelValue: 100 },
render: Determinate.render,
};
+54
View File
@@ -0,0 +1,54 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite';
import { Separator } from '@robonen/primitives';
const meta = {
title: 'Layout/Separator',
component: Separator,
tags: ['autodocs'],
argTypes: {
orientation: { control: 'radio', options: ['horizontal', 'vertical'] },
decorative: { control: 'boolean' },
},
args: { orientation: 'horizontal', decorative: false },
} satisfies Meta<typeof Separator>;
export default meta;
type Story = StoryObj<typeof meta>;
const baseStyle = `
background: #888;
display: block;
`;
const horizontal = `${baseStyle} height: 1px; width: 100%;`;
const vertical = `${baseStyle} height: 24px; width: 1px; display: inline-block; margin: 0 0.75rem;`;
export const Horizontal: Story = {
render: args => ({
components: { Separator },
setup: () => ({ args, horizontal }),
template: `
<div style="max-width: 320px; font-family: system-ui">
<p style="margin:0 0 0.5rem">Section one</p>
<Separator v-bind="args" :style="horizontal" />
<p style="margin:0.5rem 0 0">Section two</p>
</div>
`,
}),
};
export const Vertical: Story = {
args: { orientation: 'vertical' },
render: args => ({
components: { Separator },
setup: () => ({ args, vertical }),
template: `
<nav style="font-family: system-ui">
<a href="#">Home</a>
<Separator v-bind="args" :style="vertical" />
<a href="#">About</a>
<Separator v-bind="args" :style="vertical" />
<a href="#">Contact</a>
</nav>
`,
}),
};
+94
View File
@@ -0,0 +1,94 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite';
import { Label, Switch } from '@robonen/primitives';
import { ref } from 'vue';
const meta = {
title: 'Forms/Switch',
component: Switch,
tags: ['autodocs'],
} satisfies Meta<typeof Switch>;
export default meta;
type Story = StoryObj<typeof meta>;
export const BooleanDefault: Story = {
name: 'Boolean (default)',
render: () => ({
components: { Switch, Label },
template: `
<div style="display:flex;align-items:center;gap:0.5rem;font-family:system-ui">
<Switch id="airplane" class="sb-switch">
<span class="sb-switch-thumb" />
</Switch>
<Label for="airplane">Airplane mode</Label>
</div>
`,
}),
};
export const StringPair: Story = {
name: 'String pair ("on" / "off")',
render: () => ({
components: { Switch, Label },
setup() {
const value = ref<'on' | 'off'>('off');
return { value };
},
template: `
<div style="display:flex;align-items:center;gap:0.5rem;font-family:system-ui">
<Switch
v-model="value"
truthy="on"
falsy="off"
id="mode"
class="sb-switch"
>
<span class="sb-switch-thumb" />
</Switch>
<Label for="mode">Mode: {{ value }}</Label>
</div>
`,
}),
};
export const ObjectPair: Story = {
name: 'Object pair (generic)',
render: () => ({
components: { Switch, Label },
setup() {
const LIGHT = { theme: 'light' as const };
const DARK = { theme: 'dark' as const };
const value = ref<typeof LIGHT | typeof DARK>(LIGHT);
return { value, LIGHT, DARK };
},
template: `
<div style="display:flex;align-items:center;gap:0.5rem;font-family:system-ui">
<Switch
v-model="value"
:truthy="DARK"
:falsy="LIGHT"
id="theme"
class="sb-switch"
>
<span class="sb-switch-thumb" />
</Switch>
<Label for="theme">Theme: {{ value.theme }}</Label>
</div>
`,
}),
};
export const Disabled: Story = {
name: 'Disabled',
render: () => ({
components: { Switch, Label },
template: `
<div style="display:flex;align-items:center;gap:0.5rem;font-family:system-ui">
<Switch id="disabled-sw" class="sb-switch" disabled :default-value="true">
<span class="sb-switch-thumb" />
</Switch>
<Label for="disabled-sw">Disabled (checked)</Label>
</div>
`,
}),
};
+33
View File
@@ -0,0 +1,33 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite';
import { Toggle } from '@robonen/primitives';
const meta = {
title: 'Forms/Toggle',
component: Toggle,
tags: ['autodocs'],
argTypes: { disabled: { control: 'boolean' }, defaultPressed: { control: 'boolean' } },
args: { disabled: false, defaultPressed: false },
} satisfies Meta<typeof Toggle>;
export default meta;
type Story = StoryObj<typeof meta>;
const template = `
<Toggle v-bind="args" class="sb-toggle">
<template #default="{ pressed }">{{ pressed ? 'Bold ●' : 'Bold' }}</template>
</Toggle>
`;
export const Default: Story = {
render: args => ({ components: { Toggle }, setup: () => ({ args }), template }),
};
export const Pressed: Story = {
args: { defaultPressed: true },
render: args => ({ components: { Toggle }, setup: () => ({ args }), template }),
};
export const Disabled: Story = {
args: { disabled: true },
render: args => ({ components: { Toggle }, setup: () => ({ args }), template }),
};