mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-03 12:42:01 +00:00
<img width="1048" height="482" alt="스크린샷 2026-03-12 오전 9 11 56" src="https://github.com/user-attachments/assets/68009980-097c-4736-b7c4-eb8f9a6f05be" /> ## Summary - Extract inline value control button from `WidgetWithControl` into reusable `SeedControlButton` component - Support `badge` (pill) and `button` (square) variants per Figma design system spec - Use native `<button>` element for proper a11y (works with Reka UI's `PopoverTrigger as-child`) ## Test plan - [x] Verify seed control button renders correctly on KSampler node's seed widget - [x] Verify popover opens on click and mode selection works - [x] Verify all 4 modes display correct icon/text (shuffle, pencil-off, +1, -1) 🤖 Generated with [Claude Code](https://claude.com/claude-code) ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9744-feat-extract-SeedControlButton-component-3206d73d365081a3823cc19e48d205c1) by [Unito](https://www.unito.io) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions <github-actions@github.com>
6.3 KiB
6.3 KiB
name, description
| name | description |
|---|---|
| writing-storybook-stories | Write or update Storybook stories for Vue components in ComfyUI_frontend. Use when adding, modifying, reviewing, or debugging `.stories.ts` files, Storybook docs, component demos, or visual catalog entries in `src/` or `apps/desktop-ui/`. |
Write Storybook Stories for ComfyUI_frontend
Workflow
- !!!!IMPORTANT Confirm the worktree is on a
feat/*orfix/*branch. Base PRs on the localmain, not a fork branch. - Read the component source first. Understand props, emits, slots, exposed methods, and any supporting types or composables.
- Read nearby stories before writing anything.
- Search stories:
rg --files src apps | rg '\.stories\.ts$' - Inspect title patterns:
rg -n "title:\\s*'" src apps --glob '*.stories.ts'
- Search stories:
- If a Figma link is provided, list the states you need to cover before writing stories.
- Co-locate the story file with the component:
ComponentName.stories.ts. - Add each variation on separate stories, except hover state. this should be automatically applied by the implementation and not require a separate story.
- Run Storybook and validation checks before handing off.
Match Local Conventions
- Copy the closest neighboring story instead of forcing one universal template.
- Most repo stories use
@storybook/vue3-vite. Some stories underapps/desktop-uistill use@storybook/vue3; keep the local convention for that area. - Add
tags: ['autodocs']unless the surrounding stories in that area intentionally omit it. - Use
ComponentPropsAndSlots<typeof Component>when it helps with prop and slot typing. - Keep
renderfunctions stateful when needed. Useref(),computed(), andtoRefs(args)instead of mutating Storybook args directly. - Use
args.defaultor other slot-shaped args when the component content is provided through slots. - Use
ComponentExposedonly when a component's exposed API breaks the normal Storybook typing. - Add decorators for realistic width or background context when the component needs it.
Title Patterns
Do not invent titles from scratch when a close sibling story already exists. Match the nearest domain pattern.
| Component area | Typical title pattern |
|---|---|
src/components/ui/button/Button.vue |
Components/Button/Button |
src/components/ui/input/Input.vue |
Components/Input |
src/components/ui/search-input/SearchInput.vue |
Components/Input/SearchInput |
src/components/common/SearchBox.vue |
Components/Input/SearchBox |
src/renderer/extensions/vueNodes/widgets/components/* |
Widgets/<WidgetName> |
src/platform/assets/components/* |
Platform/Assets/<ComponentName> |
apps/desktop-ui/src/components/* |
Desktop/Components/<ComponentName> |
apps/desktop-ui/src/views/* |
Desktop/Views/<ViewName> |
If multiple patterns seem plausible, follow the closest sibling story in the same folder tree.
Common Story Shapes
Stateful input or v-model
export const Default: Story = {
render: (args) => ({
components: { MyComponent },
setup() {
const { disabled, size } = toRefs(args)
const value = ref('Hello world')
return { value, disabled, size }
},
template:
'<MyComponent v-model="value" :disabled="disabled" :size="size" />'
})
}
Slot-driven content
const meta: Meta<ComponentPropsAndSlots<typeof Button>> = {
argTypes: {
default: { control: 'text' }
},
args: {
default: 'Button'
}
}
export const SingleButton: Story = {
render: (args) => ({
components: { Button },
setup() {
return { args }
},
template: '<Button v-bind="args">{{ args.default }}</Button>'
})
}
Variants or edge cases grid
export const AllVariants: Story = {
render: () => ({
components: { MyComponent },
template: `
<div class="grid gap-4 sm:grid-cols-2">
<MyComponent />
<MyComponent disabled />
<MyComponent loading />
<MyComponent invalid />
</div>
`
})
}
Figma Mapping
- Extract the named states from the design first.
- Prefer explicit prop-driven stories such as
Disabled,Loading,Invalid,WithPlaceholder,AllSizes, orEdgeCases. - Add an aggregate story such as
AllVariants,AllSizes, orEdgeCaseswhen side-by-side comparison is useful. - Use pseudo-state parameters only if the addon is already configured in this repo.
- If a Figma state cannot be represented exactly, capture the closest prop-driven version and explain the gap in the story docs.
Component-Specific Notes
- Widget components often need a minimal
SimplifiedWidgetobject. Build it insetup()and usecomputed()whenargschangewidget.options. - Input and search components often need a width-constrained wrapper so they render at realistic sizes.
- Asset and platform cards often need background decorators such as
bg-base-backgroundand fixed-width containers. - Desktop installer stories may need custom
backgroundsparameters and may intentionally keep the older Storybook import style used by neighboring files. - Use semantic tokens such as
bg-base-backgroundandbg-node-component-surfaceinstead ofdark:variants or hardcoded theme assumptions.
Checklist
- Read the component source and any supporting types or composables
- Match the nearest local title pattern and story style
- Include a baseline story; name it
Defaultonly when that matches nearby conventions - Add focused stories for meaningful states
- Add
tags: ['autodocs'] - Keep the story co-located with the component
- Run
pnpm storybook - Run
pnpm typecheck - Run
pnpm lint
Avoid
- Do not guess props, emits, slots, or exposed methods.
- Do not force one generic title convention across the repo.
- Do not mutate Storybook args directly for
v-modelcomponents. - Do not introduce
dark:Tailwind variants in story wrappers. - Do not create barrel files.
- Do not assume every story needs
layout: 'centered'or aDefaultexport; follow the nearest existing pattern.