chore(stories): eslint/tsconfig migration
Migrate Storybook package to eslint flat config + composite tsconfig.
This commit is contained in:
@@ -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>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
@@ -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 };
|
||||
@@ -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>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
@@ -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>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
@@ -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>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
@@ -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 }),
|
||||
};
|
||||
Reference in New Issue
Block a user