feat(core): Allow Storybook components to be viewed in light and dark mode (#34361)

This commit is contained in:
Sebastien Powell 2026-07-27 12:50:28 +01:00 committed by GitHub
parent 54db083953
commit e592b210c1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 481 additions and 325 deletions

View File

@ -39,6 +39,23 @@ function findItemByLabel(
return undefined;
}
function getProviderLogoStyle(logoUrl: string) {
return {
width: '20px',
height: '20px',
borderRadius: '999px',
backgroundColor: 'var(--icon-color)',
maskImage: `url(${logoUrl})`,
WebkitMaskImage: `url(${logoUrl})`,
maskSize: 'contain',
WebkitMaskSize: 'contain',
maskRepeat: 'no-repeat',
WebkitMaskRepeat: 'no-repeat',
maskPosition: 'center',
WebkitMaskPosition: 'center',
};
}
const meta = {
title: 'AI/AiModelSelectorDropdown',
component: N8nAiModelSelectorDropdown,
@ -94,25 +111,32 @@ const meta = {
},
);
return { storyArgs, selectedProviderLogo, getProviderLogo, handleSearch, handleSelect };
return {
storyArgs,
selectedProviderLogo,
getProviderLogo,
getProviderLogoStyle,
handleSearch,
handleSelect,
};
},
template: `
<N8nAiModelSelectorDropdown v-bind="storyArgs" @search="handleSearch" @select="handleSelect">
<template #trigger-leading="{ ui }">
<img
<span
v-if="selectedProviderLogo"
:class="ui.class"
:src="selectedProviderLogo"
alt="OpenAI"
style="width: 20px; height: 20px; border-radius: 999px;"
role="img"
aria-label="Selected provider"
:style="getProviderLogoStyle(selectedProviderLogo)"
/>
</template>
<template #item-leading="{ item, ui }">
<img
<span
:class="ui.class"
:src="getProviderLogo(item.id)"
alt=""
style="width: 20px; height: 20px; border-radius: 999px;"
role="img"
aria-hidden="true"
:style="getProviderLogoStyle(getProviderLogo(item.id))"
/>
</template>
</N8nAiModelSelectorDropdown>

View File

@ -386,7 +386,7 @@ const InteractiveTemplate: StoryFn = (args, { argTypes }) => ({
},
template: `
<div>
<div style="width: 500px; max-width: 100%; margin-bottom: 20px;">
<div style="width: 500px; max-width: 100%; margin-bottom: var(--spacing--md);">
<n8n-chat-input
v-bind="args"
:modelValue="val"
@ -398,11 +398,30 @@ const InteractiveTemplate: StoryFn = (args, { argTypes }) => ({
@blur="onBlur"
/>
</div>
<div style="padding: 10px; background: #f0f0f0; border-radius: 4px;">
<div
style="
padding: var(--spacing--xs);
background: var(--background--subtle);
color: var(--text-color);
border: var(--border);
border-radius: var(--radius);
"
>
<p><strong>Current value:</strong> {{ val }}</p>
<p><strong>Character count:</strong> {{ val.length }} / {{ args.maxLength }}</p>
<p><strong>Streaming:</strong> {{ streaming }}</p>
<button @click="streaming = !streaming" style="margin-top: 10px;">
<button
style="
margin-top: var(--spacing--xs);
padding: var(--spacing--3xs) var(--spacing--xs);
color: var(--text-color);
background: var(--background--surface);
border: var(--border);
border-radius: var(--radius);
cursor: pointer;
"
@click="streaming = !streaming"
>
Toggle Streaming (Current: {{ streaming ? 'ON' : 'OFF' }})
</button>
</div>

View File

@ -1,10 +1,10 @@
import type { StoryFn } from '@storybook/vue3-vite';
import type { Meta, StoryObj } from '@storybook/vue3-vite';
import { action } from 'storybook/actions';
import N8nFloatingWindow from './FloatingWindow.vue';
import N8nIcon from '../N8nIcon';
export default {
const meta: Meta<typeof N8nFloatingWindow> = {
title: 'Core/FloatingWindow',
component: N8nFloatingWindow,
argTypes: {
@ -31,40 +31,63 @@ export default {
iframeHeight: 500,
},
},
themePreview: {
minHeight: 520,
},
},
};
const Template: StoryFn = (args, { argTypes }) => ({
setup: () => ({
args,
onClose: action('close'),
onResize: action('resize'),
onMove: action('move'),
render: (args) => ({
components: {
N8nFloatingWindow,
N8nIcon,
},
setup: () => ({
args,
onClose: action('close'),
onResize: action('resize'),
onMove: action('move'),
}),
template: `
<div
style="
position: relative;
width: 100%;
height: 480px;
overflow: hidden;
"
>
<n8n-floating-window
v-bind="args"
style="position: absolute;"
@close="onClose"
@resize="onResize"
@move="onMove"
>
<template #header-icon>
<n8n-icon icon="comment-dots" size="medium" />
</template>
<template #header>Floating Window</template>
<template #header-actions>
<n8n-icon icon="expand" size="small" style="cursor: pointer;" />
</template>
<div style="padding: 16px;">
<p>This is the default slot content. The window can be dragged by its header and resized from any edge or corner.</p>
</div>
</n8n-floating-window>
</div>
`,
}),
props: Object.keys(argTypes),
components: {
N8nFloatingWindow,
N8nIcon,
},
template: `<n8n-floating-window v-bind="args" @close="onClose" @resize="onResize" @move="onMove">
<template #header-icon>
<n8n-icon icon="comment-dots" size="medium" />
</template>
<template #header>Floating Window</template>
<template #header-actions>
<n8n-icon icon="expand" size="small" style="cursor: pointer;" />
</template>
<div style="padding: 16px;">
<p>This is the default slot content. The window can be dragged by its header and resized from any edge or corner.</p>
</div>
</n8n-floating-window>`,
});
export const Default = Template.bind({});
Default.args = {
width: 560,
height: 400,
minWidth: 400,
minHeight: 300,
initialPosition: { x: 0, y: 0 },
};
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
width: 360,
height: 280,
minWidth: 240,
minHeight: 180,
initialPosition: { x: 16, y: 16 },
},
};

View File

@ -1,10 +1,10 @@
import type { StoryFn } from '@storybook/vue3-vite';
import N8nKeyboardShorcut from './N8nKeyboardShortcut.vue';
import N8nKeyboardShortcut from './N8nKeyboardShortcut.vue';
export default {
title: 'Core/KeyboardShortcut',
component: N8nKeyboardShorcut,
component: N8nKeyboardShortcut,
parameters: {
docs: {
@ -17,7 +17,7 @@ const template: StoryFn = (args, { argTypes }) => ({
setup: () => ({ args }),
props: Object.keys(argTypes),
components: {
N8nKeyboardShorcut,
N8nKeyboardShortcut,
},
template: '<n8n-keyboard-shortcut v-bind="args" />',
});

View File

@ -1,5 +1,6 @@
import type { StoryFn } from '@storybook/vue3-vite';
import type { Meta, StoryObj } from '@storybook/vue3-vite';
import { action } from 'storybook/actions';
import { computed, ref } from 'vue';
import N8nMarkdownEditor from './MarkdownEditor.vue';
@ -63,15 +64,7 @@ Escalate to a human when:
When a workflow error is provided, identify the failing node, summarize the likely cause, and suggest the smallest next diagnostic step.`;
const methods = {
onUpdateModelValue: action('update:modelValue'),
onInput: action('input'),
onFocus: action('focus'),
onBlur: action('blur'),
onReady: action('ready'),
};
export default {
const meta: Meta<typeof N8nMarkdownEditor> = {
title: 'Core/Markdown Editor',
component: N8nMarkdownEditor,
argTypes: {
@ -107,108 +100,123 @@ export default {
},
},
},
};
render: (args) => ({
components: { N8nMarkdownEditor },
setup() {
const value = ref(args.modelValue);
const editorArgs = computed(() => {
const { modelValue: _modelValue, ...rest } = args;
return rest;
});
const Template: StoryFn = (args, { argTypes }) => ({
setup: () => ({ args }),
props: Object.keys(argTypes),
components: {
N8nMarkdownEditor,
},
data() {
return {
value: args.modelValue,
};
},
watch: {
'args.modelValue'(value: string) {
this.value = value;
return {
value,
editorArgs,
onUpdateModelValue: action('update:modelValue'),
onInput: action('input'),
onFocus: action('focus'),
onBlur: action('blur'),
onReady: action('ready'),
};
},
template: `
<div style="max-width: 760px; display: flex; flex-direction: column; gap: 16px;">
<n8n-markdown-editor
v-bind="editorArgs"
v-model="value"
@update:modelValue="onUpdateModelValue"
@input="onInput"
@focus="onFocus"
@blur="onBlur"
@ready="onReady"
/>
</div>
`,
}),
};
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
modelValue: defaultMarkdown,
variant: 'contained',
placeholder: 'Write Markdown...',
showToolbar: 'always',
maxHeight: '480px',
disabled: false,
readonly: false,
},
template: `
<div style="max-width: 760px; display: flex; flex-direction: column; gap: 16px;">
<n8n-markdown-editor
v-bind="args"
v-model="value"
@update:modelValue="onUpdateModelValue"
@input="onInput"
@focus="onFocus"
@blur="onBlur"
@ready="onReady"
/>
</div>
`,
methods,
});
export const Default = Template.bind({});
Default.args = {
modelValue: defaultMarkdown,
variant: 'contained',
placeholder: 'Write Markdown...',
showToolbar: 'always',
maxHeight: '480px',
disabled: false,
readonly: false,
};
export const Contained = Template.bind({});
Contained.args = {
...Default.args,
variant: 'contained',
export const Contained: Story = {
args: {
...Default.args,
variant: 'contained',
},
};
export const Ghost = Template.bind({});
Ghost.args = {
...Default.args,
variant: 'ghost',
export const Ghost: Story = {
args: {
...Default.args,
variant: 'ghost',
},
};
export const WithoutToolbar = Template.bind({});
WithoutToolbar.args = {
...Default.args,
showToolbar: 'never',
export const WithoutToolbar: Story = {
args: {
...Default.args,
showToolbar: 'never',
},
};
export const AlwaysVisibleToolbar = Template.bind({});
AlwaysVisibleToolbar.args = {
...Default.args,
showToolbar: 'always',
export const AlwaysVisibleToolbar: Story = {
args: {
...Default.args,
showToolbar: 'always',
},
};
export const GfmContent = Template.bind({});
GfmContent.args = {
...Default.args,
modelValue: gfmMarkdown,
variant: 'contained',
export const GfmContent: Story = {
args: {
...Default.args,
modelValue: gfmMarkdown,
variant: 'contained',
},
};
export const Disabled = Template.bind({});
Disabled.args = {
...Default.args,
disabled: true,
variant: 'contained',
export const Disabled: Story = {
args: {
...Default.args,
disabled: true,
variant: 'contained',
},
};
export const Readonly = Template.bind({});
Readonly.args = {
...Default.args,
readonly: true,
variant: 'contained',
export const Readonly: Story = {
args: {
...Default.args,
readonly: true,
variant: 'contained',
},
};
export const LongInstructions = Template.bind({});
LongInstructions.args = {
...Default.args,
modelValue: longInstructionsMarkdown,
variant: 'contained',
export const LongInstructions: Story = {
args: {
...Default.args,
modelValue: longInstructionsMarkdown,
variant: 'contained',
},
};
export const Empty = Template.bind({});
Empty.args = {
...Default.args,
modelValue: '',
showToolbar: 'never',
variant: 'contained',
placeholder: 'Write instructions...',
export const Empty: Story = {
args: {
...Default.args,
modelValue: '',
showToolbar: 'never',
variant: 'contained',
placeholder: 'Write instructions...',
},
};

View File

@ -1,4 +1,6 @@
import type { StoryFn } from '@storybook/vue3-vite';
import type { Component } from 'vue';
import { ref } from 'vue';
import N8nSelectableList from './SelectableList.vue';
@ -14,14 +16,14 @@ export default {
},
};
const Template: StoryFn = (args, { argTypes }) => ({
setup: () => ({
args: { ...args, modelValue: undefined },
model: args.modelValue,
}),
props: Object.keys(argTypes),
// Generics make this difficult to type
components: N8nSelectableList as never,
const Template: StoryFn = (args) => ({
setup: () => {
const model = ref(args.modelValue);
return { args, model };
},
components: {
N8nSelectableList: N8nSelectableList as unknown as Component,
},
template:
'<n8n-selectable-list v-bind="args" v-model="model"><template #displayItem="{ name }">Slot content for {{name}}</template></n8n-selectable-list>',
});

View File

@ -1,11 +1,11 @@
import type { StoryFn } from '@storybook/vue3-vite';
import type { Meta, StoryObj } from '@storybook/vue3-vite';
import { action } from 'storybook/actions';
import { ref } from 'vue';
import { computed, ref } from 'vue';
import N8nSticky from './Sticky.vue';
import N8nResizeWrapper from '../N8nResizeWrapper/ResizeWrapper.vue';
export default {
const meta: Meta<typeof N8nSticky> = {
title: 'Core/Sticky',
component: N8nSticky,
argTypes: {
@ -17,27 +17,27 @@ export default {
},
height: {
control: {
control: 'number',
type: 'number',
},
},
minHeight: {
control: {
control: 'number',
type: 'number',
},
},
minWidth: {
control: {
control: 'number',
type: 'number',
},
},
readOnly: {
control: {
control: 'Boolean',
type: 'boolean',
},
},
width: {
control: {
control: 'number',
type: 'number',
},
},
},
@ -47,93 +47,144 @@ export default {
component: 'An editable sticky-note component with markdown content and color variants.',
},
},
themePreview: {
minHeight: 240,
},
},
};
render: (args) => ({
components: { N8nSticky },
setup() {
const value = ref(args.modelValue);
const editMode = ref(false);
const stickyArgs = computed(() => {
const { modelValue: _modelValue, editMode: _editMode, ...rest } = args;
return rest;
});
const methods = {
onEdit: action('edit'),
onMarkdownClick: action('markdown-click'),
onUpdateModelValue: action('update:modelValue'),
onResize: action('resize'),
onResizeEnd: action('resizeend'),
onResizeStart: action('resizestart'),
};
const onEdit = (editing: boolean) => {
editMode.value = editing;
action('edit')(editing);
};
const Template: StoryFn = (args, { argTypes }) => ({
setup: () => ({ args }),
props: Object.keys(argTypes),
components: {
N8nSticky,
},
template: `
<n8n-sticky
v-bind="args"
@edit="onEdit"
@markdown-click="onMarkdownClick"
@update:modelValue="onUpdateModelValue"
></n8n-sticky>
`,
methods,
});
export const Sticky = Template.bind({});
Sticky.args = {
height: 180,
width: 240,
modelValue:
"## I'm a note \n**Double click** to edit me. [Guide](https://docs.n8n.io/workflows/components/sticky-notes/)",
minHeight: 80,
minWidth: 150,
readOnly: false,
backgroundColor: 1,
};
const ResizableTemplate: StoryFn = (args) => ({
setup: () => {
const width = ref(args.width ?? 240);
const height = ref(args.height ?? 180);
const onResize = (resizeData: { height: number; width: number }) => {
action('resize')(resizeData);
height.value = resizeData.height;
width.value = resizeData.width;
};
return { args, width, height, onResize };
},
components: {
N8nResizeWrapper,
N8nSticky,
},
template: `
<div style="width: fit-content; height: fit-content; position: relative;">
<n8n-resize-wrapper
:is-resizing-enabled="!args.readOnly"
:width="width"
:height="height"
:min-width="args.minWidth"
:min-height="args.minHeight"
:grid-size="20"
:scale="1"
@resize="onResize"
@resizeend="onResizeEnd"
@resizestart="onResizeStart"
>
return {
value,
editMode,
stickyArgs,
onEdit,
onMarkdownClick: action('markdown-click'),
onUpdateModelValue: action('update:modelValue'),
};
},
template: `
<div :style="{ position: 'relative', width: stickyArgs.width + 'px', height: stickyArgs.height + 'px' }">
<n8n-sticky
v-bind="args"
:width="width"
:height="height"
v-bind="stickyArgs"
v-model="value"
:edit-mode="editMode"
@edit="onEdit"
@markdown-click="onMarkdownClick"
@update:modelValue="onUpdateModelValue"
></n8n-sticky>
</n8n-resize-wrapper>
</div>
`,
methods,
});
export const Resizable = ResizableTemplate.bind({});
Resizable.args = {
...Sticky.args,
/>
</div>
`,
}),
};
export default meta;
type Story = StoryObj<typeof meta>;
export const Sticky: Story = {
args: {
height: 180,
width: 240,
modelValue:
"## I'm a note \n**Double click** to edit me. [Guide](https://docs.n8n.io/workflows/components/sticky-notes/)",
minHeight: 80,
minWidth: 150,
readOnly: false,
backgroundColor: 1,
},
};
export const Resizable: Story = {
args: {
...Sticky.args,
},
render: (args) => ({
components: {
N8nResizeWrapper,
N8nSticky,
},
setup() {
const value = ref(args.modelValue);
const editMode = ref(false);
const width = ref(args.width ?? 240);
const height = ref(args.height ?? 180);
const stickyArgs = computed(() => {
const {
modelValue: _modelValue,
editMode: _editMode,
width: _width,
height: _height,
...rest
} = args;
return rest;
});
const onEdit = (editing: boolean) => {
editMode.value = editing;
action('edit')(editing);
};
const onResize = (resizeData: { height: number; width: number }) => {
action('resize')(resizeData);
height.value = resizeData.height;
width.value = resizeData.width;
};
return {
args,
value,
editMode,
width,
height,
stickyArgs,
onEdit,
onResize,
onMarkdownClick: action('markdown-click'),
onUpdateModelValue: action('update:modelValue'),
onResizeEnd: action('resizeend'),
onResizeStart: action('resizestart'),
};
},
template: `
<div style="width: fit-content; height: fit-content; position: relative;">
<n8n-resize-wrapper
:is-resizing-enabled="!args.readOnly"
:width="width"
:height="height"
:min-width="args.minWidth"
:min-height="args.minHeight"
:grid-size="20"
:scale="1"
@resize="onResize"
@resizeend="onResizeEnd"
@resizestart="onResizeStart"
>
<n8n-sticky
v-bind="stickyArgs"
v-model="value"
:edit-mode="editMode"
:width="width"
:height="height"
@edit="onEdit"
@markdown-click="onMarkdownClick"
@update:modelValue="onUpdateModelValue"
/>
</n8n-resize-wrapper>
</div>
`,
}),
};

View File

@ -1,11 +1,11 @@
/* eslint-disable @typescript-eslint/naming-convention */
import type { StoryFn } from '@storybook/vue3-vite';
import UserStack from './UserStack.vue';
import N8nUserStack from './UserStack.vue';
export default {
title: 'Core/UserStack',
component: UserStack,
component: N8nUserStack,
parameters: {
docs: {
@ -16,9 +16,8 @@ export default {
const Template: StoryFn = (args) => ({
setup: () => ({ args }),
props: args,
components: {
UserStack,
N8nUserStack,
},
template: '<n8n-user-stack v-bind="args" />',
});

View File

@ -1245,11 +1245,12 @@
@include theme;
}
body[data-theme='light'] {
[data-theme='light'] {
@include theme;
color-scheme: light;
}
body[data-theme='dark'] {
[data-theme='dark'] {
@include theme-dark;
color-scheme: dark;
}

View File

@ -6,7 +6,7 @@
@include meta.load-css('highlight.js/styles/github-dark-dimmed.css');
}
body[data-theme='dark'] {
[data-theme='dark'] {
@include hljs-dark-theme;
}

View File

@ -31,7 +31,6 @@ const config: StorybookConfig = {
},
},
},
getAbsolutePath('@storybook/addon-themes'),
getAbsolutePath('storybook-addon-vue-mdx'),
],
framework: getAbsolutePath('@storybook/vue3-vite'),

View File

@ -1,8 +0,0 @@
export const allModes = {
dark: {
theme: 'dark',
},
light: {
theme: 'light',
},
} as const;

View File

@ -1,7 +1,6 @@
import { IconBodyLoaderKey, N8nPlugin } from '@n8n/design-system';
import { loadLucideIconBody } from '@n8n/design-system/icons/lucide';
import { i18nInstance } from '@n8n/i18n';
import { withThemeByDataAttribute } from '@storybook/addon-themes';
import { setup } from '@storybook/vue3';
import ElementPlus from 'element-plus';
// @ts-expect-error no types
@ -10,7 +9,7 @@ import { createPinia } from 'pinia';
import { createMemoryHistory, createRouter } from 'vue-router';
import './storybook.scss';
import { allModes } from './modes';
import { withThemePreview } from './withThemePreview';
// import '../src/css/tailwind/index.css';
setup((app) => {
@ -42,40 +41,10 @@ export const parameters = {
},
},
backgrounds: {
default: '--color--background--light-3',
values: [
{
name: '--color--background--shade-2',
value: 'var(--color--background--shade-2)',
},
{
name: '--color--background',
value: 'var(--color--background)',
},
{
name: '--color--background--light-2',
value: 'var(--color--background--light-2)',
},
{
name: '--color--background--light-3',
value: 'var(--color--background--light-3)',
},
],
disable: true,
},
themes: {
default: 'light',
list: [
{
name: 'light',
class: 'theme-light',
color: '#fff',
},
{
name: 'dark',
class: 'theme-dark-beta',
color: '#000',
},
],
disable: true,
},
options: {
storySort: {
@ -93,24 +62,10 @@ export const parameters = {
},
},
chromatic: {
modes: {
light: allModes['light'],
dark: allModes['dark'],
},
disableSnapshot: false,
},
};
export const decorators = [
withThemeByDataAttribute({
themes: {
light: 'light',
dark: 'dark',
},
defaultTheme: 'light',
attributeName: 'data-theme',
parentSelector: 'body',
}),
];
export const decorators = [withThemePreview];
export const tags = ['autodocs'];

View File

@ -21,6 +21,25 @@ body {
gap: 1rem;
}
.theme-side-by-side {
display: flex;
flex-direction: column;
gap: var(--spacing--md);
}
.theme-side-by-side__panel {
position: relative;
padding: var(--spacing--md);
background: var(--background--surface);
color: var(--text-color);
border-radius: var(--radius--lg);
overflow: auto;
&[data-theme='light'] {
border: var(--border);
}
}
.sbdocs .sbdocs-content table:not(.sbdocs-preview table, .docs-story table) {
width: 100%;
border-collapse: collapse;

View File

@ -0,0 +1,82 @@
import type { Decorator } from '@storybook/vue3';
type Theme = 'light' | 'dark';
function applyBodyTheme(theme: Theme) {
document.body.dataset.theme = theme;
document.body.style.colorScheme = theme;
}
function panelMinHeight(value: unknown): string | undefined {
if (typeof value === 'number') {
return `${value}px`;
}
if (typeof value === 'string' && value.length > 0) {
return value;
}
return undefined;
}
function themePreviewMinHeight(parameters: Record<string, unknown>): unknown {
const themePreview = parameters.themePreview;
if (typeof themePreview !== 'object' || themePreview === null || !('minHeight' in themePreview)) {
return undefined;
}
return themePreview.minHeight;
}
function isTheme(value: string | undefined): value is Theme {
return value === 'light' || value === 'dark';
}
export const withThemePreview: Decorator = (story, context) => {
const minHeight = panelMinHeight(themePreviewMinHeight(context.parameters));
return {
components: { storyComponent: story() },
setup: () => ({
panelStyle: minHeight ? { minHeight } : undefined,
}),
mounted() {
applyBodyTheme('light');
},
unmounted() {
document.body.removeAttribute('data-theme');
document.body.style.removeProperty('color-scheme');
},
methods: {
onPanelInteract(event: Event) {
const target = event.currentTarget;
if (!(target instanceof HTMLElement)) {
return;
}
const theme = target.dataset.theme;
if (isTheme(theme)) {
applyBodyTheme(theme);
}
},
},
template: `
<div class="theme-side-by-side">
<section
class="theme-side-by-side__panel"
:style="panelStyle"
data-theme="light"
@pointerenter="onPanelInteract"
@pointerdown.capture="onPanelInteract"
>
<storyComponent />
</section>
<section
class="theme-side-by-side__panel"
:style="panelStyle"
data-theme="dark"
@pointerenter="onPanelInteract"
@pointerdown.capture="onPanelInteract"
>
<storyComponent />
</section>
</div>
`,
};
};

View File

@ -33,7 +33,6 @@
"@n8n/typescript-config": "workspace:*",
"@storybook/addon-a11y": "catalog:storybook",
"@storybook/addon-docs": "catalog:storybook",
"@storybook/addon-themes": "catalog:storybook",
"@storybook/addon-vitest": "catalog:storybook",
"@storybook/vue3": "catalog:storybook",
"@storybook/vue3-vite": "catalog:storybook",

View File

@ -643,9 +643,6 @@ catalogs:
'@storybook/addon-docs':
specifier: ^10.1.11
version: 10.1.11
'@storybook/addon-themes':
specifier: ^10.1.11
version: 10.1.11
'@storybook/addon-vitest':
specifier: ^10.1.11
version: 10.1.11
@ -5649,9 +5646,6 @@ importers:
'@storybook/addon-docs':
specifier: catalog:storybook
version: 10.1.11(@types/react@18.0.27)(esbuild@0.28.1)(rollup@4.52.4)(storybook@10.1.11(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(utf-8-validate@5.0.10))(vite@8.0.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@20.19.41)(esbuild@0.28.1)(jiti@2.6.1)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.16.1)(tsx@4.19.3)(yaml@2.8.3))
'@storybook/addon-themes':
specifier: catalog:storybook
version: 10.1.11(storybook@10.1.11(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(utf-8-validate@5.0.10))
'@storybook/addon-vitest':
specifier: catalog:storybook
version: 10.1.11(@vitest/browser-playwright@4.1.9)(@vitest/browser@4.1.9)(@vitest/runner@4.1.9)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@10.1.11(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(utf-8-validate@5.0.10))(vitest@4.1.9)
@ -11908,11 +11902,6 @@ packages:
peerDependencies:
storybook: ^10.1.11
'@storybook/addon-themes@10.1.11':
resolution: {integrity: sha512-tUX5C1ms+W4GFK8UBWd3Fq4irkLc3h092BqW90tZghcoOmGY/sfKR+PlcLhoaTs/kkHQSSHPrz8HSFR1AXVbHA==}
peerDependencies:
storybook: ^10.1.11
'@storybook/addon-vitest@10.1.11':
resolution: {integrity: sha512-YbZzeKO3v+Xr97/malT4DZIATkVZT5EHNYx3xzEfPVuk19dDETAVYXO+tzcqCQHsgdKQHkmd56vv8nN3J3/kvw==}
peerDependencies:
@ -29238,11 +29227,6 @@ snapshots:
- vite
- webpack
'@storybook/addon-themes@10.1.11(storybook@10.1.11(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(utf-8-validate@5.0.10))':
dependencies:
storybook: 10.1.11(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(utf-8-validate@5.0.10)
ts-dedent: 2.2.0
'@storybook/addon-vitest@10.1.11(@vitest/browser-playwright@4.1.9)(@vitest/browser@4.1.9)(@vitest/runner@4.1.9)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@10.1.11(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(utf-8-validate@5.0.10))(vitest@4.1.9)':
dependencies:
'@storybook/global': 5.0.0

View File

@ -262,7 +262,6 @@ catalogs:
'@chromatic-com/storybook': ^4.1.3
'@storybook/addon-a11y': ^10.1.11
'@storybook/addon-docs': ^10.1.11
'@storybook/addon-themes': ^10.1.11
'@storybook/addon-vitest': ^10.1.11
'@storybook/vue3': ^10.1.11
'@storybook/vue3-vite': ^10.1.11