From d8e57c60bfa609aa95ff94b746864ef54aa44e3a Mon Sep 17 00:00:00 2001 From: Benjamin Lu Date: Tue, 23 Dec 2025 13:57:12 -0800 Subject: [PATCH] Add stories for list view and general job card --- .storybook/main.ts | 29 ++- .../tabs/AssetsSidebarListView.stories.ts | 155 ++++++++++++++++ .../components/AssetsListCard.stories.ts | 170 +++++++++++------- src/storybook/mocks/useJobActions.ts | 25 +++ src/storybook/mocks/useJobList.ts | 58 ++++++ 5 files changed, 366 insertions(+), 71 deletions(-) create mode 100644 src/components/sidebar/tabs/AssetsSidebarListView.stories.ts create mode 100644 src/storybook/mocks/useJobActions.ts create mode 100644 src/storybook/mocks/useJobList.ts diff --git a/.storybook/main.ts b/.storybook/main.ts index 897094ade..3db75ceb2 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -69,9 +69,32 @@ const config: StorybookConfig = { allowedHosts: true }, resolve: { - alias: { - '@': process.cwd() + '/src' - } + alias: [ + { + find: '@/composables/queue/useJobList', + replacement: process.cwd() + '/src/storybook/mocks/useJobList.ts' + }, + { + find: '@/composables/queue/useJobActions', + replacement: process.cwd() + '/src/storybook/mocks/useJobActions.ts' + }, + { + find: '@/utils/formatUtil', + replacement: + process.cwd() + + '/packages/shared-frontend-utils/src/formatUtil.ts' + }, + { + find: '@/utils/networkUtil', + replacement: + process.cwd() + + '/packages/shared-frontend-utils/src/networkUtil.ts' + }, + { + find: '@', + replacement: process.cwd() + '/src' + } + ] }, esbuild: { // Prevent minification of identifiers to preserve _sfc_main diff --git a/src/components/sidebar/tabs/AssetsSidebarListView.stories.ts b/src/components/sidebar/tabs/AssetsSidebarListView.stories.ts new file mode 100644 index 000000000..fcbdf655d --- /dev/null +++ b/src/components/sidebar/tabs/AssetsSidebarListView.stories.ts @@ -0,0 +1,155 @@ +import type { Meta, StoryObj } from '@storybook/vue3-vite' + +import type { JobAction } from '@/composables/queue/useJobActions' +import type { JobListItem } from '@/composables/queue/useJobList' +import type { AssetItem } from '@/platform/assets/schemas/assetSchema' +import { setMockJobActions } from '@/storybook/mocks/useJobActions' +import { setMockJobItems } from '@/storybook/mocks/useJobList' +import { iconForJobState } from '@/utils/queueDisplay' + +import AssetsSidebarListView from './AssetsSidebarListView.vue' + +type StoryArgs = { + assets: AssetItem[] + jobs: JobListItem[] + selectedAssetIds?: string[] + actionsByJobId?: Record +} + +function baseDecorator() { + return { + template: ` +
+ +
+ ` + } +} + +const meta: Meta = { + title: 'Components/Sidebar/AssetsSidebarListView', + component: AssetsSidebarListView, + parameters: { + layout: 'centered' + }, + decorators: [baseDecorator] +} + +export default meta +type Story = StoryObj + +const baseTimestamp = '2024-01-15T10:00:00Z' + +const sampleJobs: JobListItem[] = [ + { + id: 'job-pending-1', + title: 'In queue', + meta: '8:59:30pm', + state: 'pending', + iconName: iconForJobState('pending'), + showClear: true + }, + { + id: 'job-init-1', + title: 'Initializing...', + meta: '8:59:35pm', + state: 'initialization', + iconName: iconForJobState('initialization'), + showClear: true + }, + { + id: 'job-running-1', + title: 'Total: 30%', + meta: 'KSampler: 70%', + state: 'running', + iconName: iconForJobState('running'), + showClear: true, + progressTotalPercent: 30, + progressCurrentPercent: 70 + } +] + +const sampleAssets: AssetItem[] = [ + { + id: 'asset-image-1', + name: 'image-032.png', + created_at: baseTimestamp, + preview_url: '/assets/images/comfy-logo-single.svg', + size: 1887437, + tags: [], + user_metadata: { + promptId: 'job-running-1', + nodeId: 12, + executionTimeInSeconds: 1.84 + } + }, + { + id: 'asset-video-1', + name: 'clip-01.mp4', + created_at: baseTimestamp, + preview_url: '/assets/images/default-template.png', + size: 8394820, + tags: [], + user_metadata: { + duration: 132000 + } + }, + { + id: 'asset-audio-1', + name: 'soundtrack-01.mp3', + created_at: baseTimestamp, + size: 5242880, + tags: [], + user_metadata: { + duration: 200000 + } + }, + { + id: 'asset-3d-1', + name: 'scene-01.glb', + created_at: baseTimestamp, + size: 134217728, + tags: [] + } +] + +const cancelAction: JobAction = { + key: 'cancel', + icon: 'icon-[lucide--x]', + label: 'Cancel', + variant: 'destructive' +} + +export const RunningAndGenerated: Story = { + args: { + assets: sampleAssets, + jobs: sampleJobs, + actionsByJobId: { + 'job-pending-1': [cancelAction], + 'job-init-1': [cancelAction], + 'job-running-1': [cancelAction] + } + }, + render: renderAssetsSidebarListView +} + +function renderAssetsSidebarListView(args: StoryArgs) { + return { + components: { AssetsSidebarListView }, + setup() { + setMockJobItems(args.jobs) + setMockJobActions(args.actionsByJobId ?? {}) + const selectedIds = new Set(args.selectedAssetIds ?? []) + function isSelected(assetId: string) { + return selectedIds.has(assetId) + } + + return { args, isSelected } + }, + template: ` +
+ +
+ ` + } +} diff --git a/src/platform/assets/components/AssetsListCard.stories.ts b/src/platform/assets/components/AssetsListCard.stories.ts index a639cb000..03221f726 100644 --- a/src/platform/assets/components/AssetsListCard.stories.ts +++ b/src/platform/assets/components/AssetsListCard.stories.ts @@ -1,7 +1,19 @@ import type { Meta, StoryObj } from '@storybook/vue3-vite' import Button from '@/components/ui/button/Button.vue' -import AssetsListCard from '@/platform/assets/components/AssetsListCard.vue' +import { iconForJobState } from '@/utils/queueDisplay' + +import AssetsListCard from './AssetsListCard.vue' + +function centeredDecorator() { + return { + template: ` +
+ +
+ ` + } +} const meta: Meta = { title: 'Platform/Assets/AssetsListCard', @@ -9,37 +21,107 @@ const meta: Meta = { parameters: { layout: 'centered' }, - decorators: [ - () => ({ - template: '
' - }) - ] + decorators: [centeredDecorator] } export default meta type Story = StoryObj + +const IMAGE_PREVIEW = '/assets/images/comfy-logo-single.svg' +const VIDEO_PREVIEW = '/assets/images/default-template.png' + +export const PendingJob: Story = { + args: { + iconName: iconForJobState('pending'), + iconClass: 'animate-spin', + primaryText: 'In queue', + secondaryText: '8:59:30pm' + } +} + +export const InitializationJob: Story = { + args: { + iconName: iconForJobState('initialization'), + primaryText: 'Initializing...', + secondaryText: '8:59:35pm' + } +} + +export const RunningJob: Story = { + args: { + iconName: iconForJobState('running'), + primaryText: 'Total: 30%', + secondaryText: 'CLIP Text Encode: 70%', + progressTotalPercent: 30, + progressCurrentPercent: 70 + } +} + +export const RunningJobWithActions: Story = { + args: { + iconName: iconForJobState('running'), + primaryText: 'Total: 30%', + secondaryText: 'KSampler: 70%', + progressTotalPercent: 30, + progressCurrentPercent: 70 + }, + render: renderRunningJobWithActions +} + +export const FailedJob: Story = { + args: { + iconName: iconForJobState('failed'), + iconClass: 'text-destructive-background', + iconWrapperClass: 'bg-modal-card-placeholder-background', + primaryText: 'Failed', + secondaryText: '8:59:30pm' + } +} + +export const GeneratedImage: Story = { + args: { + previewUrl: IMAGE_PREVIEW, + previewAlt: 'image-032.png', + primaryText: 'image-032.png', + secondaryText: '1.84s' + } +} + +export const GeneratedVideo: Story = { + args: { + previewUrl: VIDEO_PREVIEW, + previewAlt: 'clip-01.mp4', + primaryText: 'clip-01.mp4', + secondaryText: '2m 12s' + } +} + +export const GeneratedAudio: Story = { + args: { + iconName: 'icon-[lucide--music]', + primaryText: 'soundtrack-01.mp3', + secondaryText: '3m 20s' + } +} + +export const Generated3D: Story = { + args: { + iconName: 'icon-[lucide--box]', + primaryText: 'scene-01.glb', + secondaryText: '128 MB' + } +} + type AssetsListCardProps = InstanceType['$props'] -function renderActiveJob(args: AssetsListCardProps) { +function renderRunningJobWithActions(args: AssetsListCardProps) { return { - components: { Button, AssetsListCard }, + components: { AssetsListCard, Button }, setup() { return { args } }, template: ` - -