mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-11 08:00:21 +00:00
<img width="373" height="535" alt="스크린샷 2026-03-09 오후 2 48 10" src="https://github.com/user-attachments/assets/7fea3fd4-0d90-4022-ad78-c53e3d5be887" /> ## Summary - Reorganize Select-related stories under `Components/Select/` hierarchy (SingleSelect, MultiSelect, Select) - Add `size` prop (`lg`/`md`) to SingleSelect, MultiSelect, SelectTrigger for Figma Large (40px) / Medium (32px) variants - Add `invalid` prop (red border) to SingleSelect and SelectTrigger - Add `loading` prop (spinner) to SingleSelect - Add `hover:bg-secondary-background-hover` to all select triggers - Align disabled opacity to 30% per Figma spec - Add new stories: Disabled, Invalid, Loading, MediumSize, AllStates ## Test plan - [ ] Verify Storybook renders all stories under `Components/Select/` - [ ] Check hover state visually on all select triggers - [ ] Verify Medium size (32px) renders correctly - [ ] Verify Invalid state shows red border - [ ] Verify Loading state shows spinner - [ ] Verify Disabled state has 30% opacity and no hover effect ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9639-refactor-reorganize-Select-stories-and-add-size-state-variants-31e6d73d36508142b835f04ab6bdaefe) by [Unito](https://www.unito.io) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
163 lines
4.9 KiB
TypeScript
163 lines
4.9 KiB
TypeScript
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
|
import { ref } from 'vue'
|
|
|
|
import MultiSelect from './MultiSelect.vue'
|
|
import type { SelectOption } from './types'
|
|
|
|
const meta: Meta<typeof MultiSelect> = {
|
|
title: 'Components/Select/MultiSelect',
|
|
component: MultiSelect,
|
|
tags: ['autodocs'],
|
|
parameters: { layout: 'padded' },
|
|
decorators: [
|
|
() => ({
|
|
template: '<div class="pt-4"><story /></div>'
|
|
})
|
|
],
|
|
argTypes: {
|
|
label: { control: 'text' },
|
|
size: {
|
|
control: { type: 'select' },
|
|
options: ['lg', 'md']
|
|
},
|
|
showSearchBox: { control: 'boolean' },
|
|
showSelectedCount: { control: 'boolean' },
|
|
showClearButton: { control: 'boolean' },
|
|
searchPlaceholder: { control: 'text' }
|
|
},
|
|
args: {
|
|
label: 'Category',
|
|
size: 'lg',
|
|
showSearchBox: false,
|
|
showSelectedCount: false,
|
|
showClearButton: false,
|
|
searchPlaceholder: 'Search...'
|
|
}
|
|
}
|
|
|
|
export default meta
|
|
type Story = StoryObj<typeof meta>
|
|
|
|
const sampleOptions: SelectOption[] = [
|
|
{ name: 'Vue', value: 'vue' },
|
|
{ name: 'React', value: 'react' },
|
|
{ name: 'Angular', value: 'angular' },
|
|
{ name: 'Svelte', value: 'svelte' }
|
|
]
|
|
|
|
export const Default: Story = {
|
|
render: (args) => ({
|
|
components: { MultiSelect },
|
|
setup() {
|
|
const selected = ref<SelectOption[]>([])
|
|
return { selected, sampleOptions, args }
|
|
},
|
|
template:
|
|
'<MultiSelect v-model="selected" :options="sampleOptions" :label="args.label" :size="args.size" :show-search-box="args.showSearchBox" :show-selected-count="args.showSelectedCount" :show-clear-button="args.showClearButton" />'
|
|
})
|
|
}
|
|
|
|
export const MediumSize: Story = {
|
|
render: () => ({
|
|
components: { MultiSelect },
|
|
setup() {
|
|
const selected = ref<SelectOption[]>([sampleOptions[0]])
|
|
return { selected, sampleOptions }
|
|
},
|
|
template:
|
|
'<MultiSelect v-model="selected" :options="sampleOptions" label="Category" size="md" />'
|
|
}),
|
|
parameters: { controls: { disable: true } }
|
|
}
|
|
|
|
export const WithPreselectedValues: Story = {
|
|
render: () => ({
|
|
components: { MultiSelect },
|
|
setup() {
|
|
const selected = ref<SelectOption[]>([sampleOptions[0], sampleOptions[1]])
|
|
return { selected, sampleOptions }
|
|
},
|
|
template:
|
|
'<MultiSelect v-model="selected" :options="sampleOptions" label="Category" />'
|
|
}),
|
|
parameters: { controls: { disable: true } }
|
|
}
|
|
|
|
export const Disabled: Story = {
|
|
render: () => ({
|
|
components: { MultiSelect },
|
|
setup() {
|
|
const selected = ref<SelectOption[]>([sampleOptions[0]])
|
|
return { selected, sampleOptions }
|
|
},
|
|
template:
|
|
'<MultiSelect v-model="selected" :options="sampleOptions" label="Category" disabled />'
|
|
}),
|
|
parameters: { controls: { disable: true } }
|
|
}
|
|
|
|
export const WithSearchBox: Story = {
|
|
args: { showSearchBox: true },
|
|
render: (args) => ({
|
|
components: { MultiSelect },
|
|
setup() {
|
|
const selected = ref<SelectOption[]>([])
|
|
return { selected, sampleOptions, args }
|
|
},
|
|
template:
|
|
'<MultiSelect v-model="selected" :options="sampleOptions" label="Category" :show-search-box="args.showSearchBox" />'
|
|
})
|
|
}
|
|
|
|
export const AllHeaderFeatures: Story = {
|
|
args: {
|
|
showSearchBox: true,
|
|
showSelectedCount: true,
|
|
showClearButton: true
|
|
},
|
|
render: (args) => ({
|
|
components: { MultiSelect },
|
|
setup() {
|
|
const selected = ref<SelectOption[]>([])
|
|
return { selected, sampleOptions, args }
|
|
},
|
|
template:
|
|
'<MultiSelect v-model="selected" :options="sampleOptions" label="Category" :show-search-box="args.showSearchBox" :show-selected-count="args.showSelectedCount" :show-clear-button="args.showClearButton" />'
|
|
})
|
|
}
|
|
|
|
export const AllStates: Story = {
|
|
render: () => ({
|
|
components: { MultiSelect },
|
|
setup() {
|
|
const a = ref<SelectOption[]>([])
|
|
const b = ref<SelectOption[]>([sampleOptions[0]])
|
|
const c = ref<SelectOption[]>([sampleOptions[0]])
|
|
return { sampleOptions, a, b, c }
|
|
},
|
|
template: `
|
|
<div class="flex flex-col gap-6">
|
|
<div>
|
|
<p class="mb-2 text-xs text-muted-foreground">Large (Interface)</p>
|
|
<div class="flex flex-col gap-3">
|
|
<MultiSelect v-model="a" :options="sampleOptions" label="Default" />
|
|
<MultiSelect v-model="b" :options="sampleOptions" label="With Selection" />
|
|
<MultiSelect v-model="c" :options="sampleOptions" label="Disabled" disabled />
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<p class="mb-2 text-xs text-muted-foreground">Medium (Node)</p>
|
|
<div class="flex flex-col gap-3">
|
|
<MultiSelect v-model="a" :options="sampleOptions" label="Default" size="md" />
|
|
<MultiSelect v-model="b" :options="sampleOptions" label="With Selection" size="md" />
|
|
<MultiSelect v-model="c" :options="sampleOptions" label="Disabled" size="md" disabled />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`
|
|
}),
|
|
parameters: {
|
|
controls: { disable: true }
|
|
}
|
|
}
|