Files
ComfyUI_frontend/src/platform/assets/components/AssetBrowserModal.stories.ts
Arjan Singh fd12591756 [feat] integrate asset browser with widget system (#5629)
## Summary

Add asset browser dialog integration for combo widgets with full
animation support and proper state management.

(Thank you Claude from saving me me from merge conflict hell on this
one.)

## Changes

- Widget integration: combo widgets now use AssetBrowserModal for
eligible asset types
- Dialog animations: added animateHide() for smooth close transitions
- Async operations: proper sequencing of widget updates and dialog
animations
- Service layer: added getAssetsForNodeType() and getAssetDetails()
methods
- Type safety: comprehensive TypeScript types and error handling
- Test coverage: unit tests for all new functionality
- Bonus: fixed the hardcoded labels in AssetFilterBar

Widget behavior:
- Shows asset browser button for eligible widgets when asset API enabled
- Handles asset selection with proper callback sequencing
- Maintains widget value updates and litegraph notification

## Review Focus

I will call out some stuff inline.

## Screenshots


https://github.com/user-attachments/assets/9d3a72cf-d2b0-445f-8022-4c49daa04637

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5629-feat-integrate-asset-browser-with-widget-system-2726d73d365081a9a98be9a2307aee0b)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
2025-09-20 11:44:18 -07:00

180 lines
4.5 KiB
TypeScript

import type { Meta, StoryObj } from '@storybook/vue3-vite'
import AssetBrowserModal from '@/platform/assets/components/AssetBrowserModal.vue'
import type { AssetDisplayItem } from '@/platform/assets/composables/useAssetBrowser'
import {
createMockAssets,
mockAssets
} from '@/platform/assets/fixtures/ui-mock-assets'
// Story arguments interface
interface StoryArgs {
nodeType: string
inputName: string
currentValue: string
showLeftPanel?: boolean
}
const meta: Meta<StoryArgs> = {
title: 'Platform/Assets/AssetBrowserModal',
component: AssetBrowserModal,
parameters: {
layout: 'fullscreen'
},
argTypes: {
nodeType: {
control: 'select',
options: ['CheckpointLoaderSimple', 'VAELoader', 'ControlNetLoader'],
description: 'ComfyUI node type for context'
},
inputName: {
control: 'select',
options: ['ckpt_name', 'vae_name', 'control_net_name'],
description: 'Widget input name'
},
currentValue: {
control: 'text',
description: 'Current selected asset value'
},
showLeftPanel: {
control: 'boolean',
description: 'Whether to show the left panel with categories'
}
}
}
export default meta
type Story = StoryObj<typeof meta>
// Modal Layout Stories
export const Default: Story = {
args: {
nodeType: 'CheckpointLoaderSimple',
inputName: 'ckpt_name',
currentValue: '',
showLeftPanel: false
},
render: (args) => ({
components: { AssetBrowserModal },
setup() {
const onAssetSelect = (asset: AssetDisplayItem) => {
console.log('Selected asset:', asset)
}
const onClose = () => {
console.log('Modal closed')
}
return {
...args,
onAssetSelect,
onClose,
assets: mockAssets
}
},
template: `
<div class="flex items-center justify-center min-h-screen bg-stone-200 dark-theme:bg-stone-200 p-4">
<AssetBrowserModal
:node-type="nodeType"
:input-name="inputName"
:show-left-panel="showLeftPanel"
:assets="assets"
@asset-select="onAssetSelect"
@close="onClose"
/>
</div>
`
})
}
// Story demonstrating single asset type (auto-hides left panel)
export const SingleAssetType: Story = {
args: {
nodeType: 'CheckpointLoaderSimple',
inputName: 'ckpt_name',
currentValue: '',
showLeftPanel: false
},
render: (args) => ({
components: { AssetBrowserModal },
setup() {
const onAssetSelect = (asset: AssetDisplayItem) => {
console.log('Selected asset:', asset)
}
const onClose = () => {
console.log('Modal closed')
}
// Create assets with only one type (checkpoints)
const singleTypeAssets = createMockAssets(15).map((asset) => ({
...asset,
type: 'checkpoint'
}))
return { ...args, onAssetSelect, onClose, assets: singleTypeAssets }
},
template: `
<div class="flex items-center justify-center min-h-screen bg-stone-200 dark-theme:bg-stone-200 p-4">
<AssetBrowserModal
:node-type="nodeType"
:input-name="inputName"
:show-left-panel="showLeftPanel"
:assets="assets"
@asset-select="onAssetSelect"
@close="onClose"
/>
</div>
`
}),
parameters: {
docs: {
description: {
story:
'Modal with assets of only one type (checkpoint) - left panel auto-hidden.'
}
}
}
}
// Story with left panel explicitly hidden
export const NoLeftPanel: Story = {
args: {
nodeType: 'CheckpointLoaderSimple',
inputName: 'ckpt_name',
currentValue: '',
showLeftPanel: false
},
render: (args) => ({
components: { AssetBrowserModal },
setup() {
const onAssetSelect = (asset: AssetDisplayItem) => {
console.log('Selected asset:', asset)
}
const onClose = () => {
console.log('Modal closed')
}
return { ...args, onAssetSelect, onClose, assets: mockAssets }
},
template: `
<div class="flex items-center justify-center min-h-screen bg-stone-200 dark-theme:bg-stone-200 p-4">
<AssetBrowserModal
:node-type="nodeType"
:input-name="inputName"
:show-left-panel="showLeftPanel"
:assets="assets"
@asset-select="onAssetSelect"
@close="onClose"
/>
</div>
`
}),
parameters: {
docs: {
description: {
story:
'Modal with left panel explicitly disabled via showLeftPanel=false.'
}
}
}
}