Skip to content

Commit 73aa0d4

Browse files
committed
feat(storybook): add Storybooks for data-inspector + og, dock all plugins in the hub
Give the two plugins that lacked a Storybook their own Vue setup (`.storybook` config mirroring the existing plugins, plus presentational component stories with static fixtures), and dock every plugin's Storybook in the unified hub — data-inspector, og, and the pre-existing messages Storybook now join git/inspect/code-server/terminals/a11y as launcher docks.
1 parent 1da2f7b commit 73aa0d4

20 files changed

Lines changed: 547 additions & 1 deletion
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import type { StorybookConfig } from '@storybook/vue3-vite'
2+
import vue from '@vitejs/plugin-vue'
3+
import UnoCSS from 'unocss/vite'
4+
import { mergeConfig } from 'vite'
5+
import { alias } from '../../../alias'
6+
7+
const config: StorybookConfig = {
8+
stories: ['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
9+
addons: ['@storybook/addon-docs'],
10+
framework: {
11+
name: '@storybook/vue3-vite',
12+
options: {},
13+
},
14+
docs: {},
15+
async viteFinal(config) {
16+
return mergeConfig(config, {
17+
resolve: { alias },
18+
plugins: [vue(), UnoCSS()],
19+
// Dev tool reached from arbitrary hostnames (LAN IPs, tunnels,
20+
// tailnets), e.g. when iframed by the storybook hub.
21+
server: { allowedHosts: true },
22+
})
23+
},
24+
}
25+
export default config
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import type { Decorator, Preview } from '@storybook/vue3-vite'
2+
import 'virtual:uno.css'
3+
import '@antfu/design/styles.css'
4+
import '../src/spa/style.css'
5+
6+
// Drive the shared `@antfu/design` tokens off the toolbar theme toggle: dark mode
7+
// is the `.dark` class on `<html>`, and the canvas takes the semantic
8+
// `bg-base`/`color-base` surface — matching every other devframe surface.
9+
function applyTheme(theme: string): void {
10+
document.documentElement.classList.toggle('dark', theme !== 'light')
11+
document.body.classList.add('bg-base', 'color-base', 'font-sans')
12+
}
13+
14+
const withTheme: Decorator = (story, context) => {
15+
applyTheme(context.globals.theme ?? 'dark')
16+
return { components: { story }, template: '<story />' }
17+
}
18+
19+
const preview: Preview = {
20+
parameters: {
21+
layout: 'fullscreen',
22+
controls: {
23+
expanded: true,
24+
matchers: {
25+
color: /(background|color)$/i,
26+
date: /Date$/i,
27+
},
28+
},
29+
},
30+
globalTypes: {
31+
theme: {
32+
description: 'Color theme',
33+
defaultValue: 'dark',
34+
toolbar: {
35+
title: 'Theme',
36+
icon: 'contrast',
37+
items: [
38+
{ value: 'light', title: 'Light', icon: 'sun' },
39+
{ value: 'dark', title: 'Dark', icon: 'moon' },
40+
],
41+
dynamicTitle: true,
42+
},
43+
},
44+
},
45+
decorators: [withTheme],
46+
}
47+
48+
export default preview

plugins/data-inspector/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
"build": "tsdown && vite build --config src/spa/vite.config.ts",
4444
"dev": "vite --config src/spa/vite.config.ts --host 0.0.0.0",
4545
"watch": "tsdown --watch",
46+
"storybook": "storybook dev -p 6016 --host 0.0.0.0",
47+
"build-storybook": "storybook build",
4648
"prepack": "pnpm build",
4749
"test": "vitest run",
4850
"typecheck": "tsc --noEmit"
@@ -66,13 +68,16 @@
6668
"@antfu/design": "catalog:frontend",
6769
"@discoveryjs/discovery": "catalog:frontend",
6870
"@iconify-json/ph": "catalog:frontend",
71+
"@storybook/addon-docs": "catalog:storybook",
72+
"@storybook/vue3-vite": "catalog:storybook",
6973
"@types/codemirror": "catalog:types",
7074
"@vitejs/plugin-vue": "catalog:build",
7175
"devframe": "workspace:*",
7276
"dompurify": "catalog:frontend",
7377
"floating-vue": "catalog:frontend",
7478
"reka-ui": "catalog:frontend",
7579
"splitpanes": "catalog:frontend",
80+
"storybook": "catalog:storybook",
7681
"tsdown": "catalog:build",
7782
"unocss": "catalog:frontend",
7883
"vite": "catalog:build",
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import type { Meta, StoryObj } from '@storybook/vue3-vite'
2+
import { sampleSkeleton, sampleSources } from './_fixtures'
3+
import DataShapePanel from './DataShapePanel.vue'
4+
5+
const meta = {
6+
title: 'DataInspector/DataShapePanel',
7+
component: DataShapePanel,
8+
tags: ['autodocs'],
9+
} satisfies Meta<typeof DataShapePanel>
10+
11+
export default meta
12+
type Story = StoryObj<typeof meta>
13+
14+
export const Overview: Story = {
15+
args: {
16+
source: sampleSources[0],
17+
skeleton: sampleSkeleton,
18+
error: null,
19+
loading: false,
20+
},
21+
}
22+
23+
export const Loading: Story = {
24+
args: {
25+
source: sampleSources[0],
26+
skeleton: null,
27+
error: null,
28+
loading: true,
29+
},
30+
}
31+
32+
export const Errored: Story = {
33+
args: {
34+
source: sampleSources[0],
35+
skeleton: null,
36+
error: 'Failed to resolve the data source skeleton.',
37+
loading: false,
38+
},
39+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import type { Meta, StoryObj } from '@storybook/vue3-vite'
2+
import { ref } from 'vue'
3+
import { sampleSources } from './_fixtures'
4+
import DataSourceSelect from './DataSourceSelect.vue'
5+
6+
const meta = {
7+
title: 'DataInspector/DataSourceSelect',
8+
component: DataSourceSelect,
9+
tags: ['autodocs'],
10+
} satisfies Meta<typeof DataSourceSelect>
11+
12+
export default meta
13+
type Story = StoryObj<typeof meta>
14+
15+
export const Default: Story = {
16+
render: () => ({
17+
components: { DataSourceSelect },
18+
setup() {
19+
const model = ref('devframe')
20+
return { model, sources: sampleSources }
21+
},
22+
template: `<div class="p-4"><DataSourceSelect v-model="model" :sources="sources" /></div>`,
23+
}),
24+
}
25+
26+
export const Placeholder: Story = {
27+
render: () => ({
28+
components: { DataSourceSelect },
29+
setup() {
30+
const model = ref<string>()
31+
return { model, sources: sampleSources }
32+
},
33+
template: `<div class="p-4"><DataSourceSelect v-model="model" :sources="sources" placeholder="Pick a data source" /></div>`,
34+
}),
35+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import type { Meta, StoryObj } from '@storybook/vue3-vite'
2+
import { ref } from 'vue'
3+
import { sampleFilters } from './_fixtures'
4+
import QuerySettings from './QuerySettings.vue'
5+
6+
const meta = {
7+
title: 'DataInspector/QuerySettings',
8+
component: QuerySettings,
9+
tags: ['autodocs'],
10+
} satisfies Meta<typeof QuerySettings>
11+
12+
export default meta
13+
type Story = StoryObj<typeof meta>
14+
15+
export const Default: Story = {
16+
render: () => ({
17+
components: { QuerySettings },
18+
setup() {
19+
const settings = ref({ ...sampleFilters })
20+
const autoRun = ref(false)
21+
const autoRunSeconds = ref(5)
22+
return { settings, autoRun, autoRunSeconds }
23+
},
24+
template: `<div class="p-4 max-w-2xl"><QuerySettings v-model="settings" v-model:auto-run="autoRun" v-model:auto-run-seconds="autoRunSeconds" /></div>`,
25+
}),
26+
}
27+
28+
export const AutoRunEnabled: Story = {
29+
render: () => ({
30+
components: { QuerySettings },
31+
setup() {
32+
const settings = ref({ excludeFunctions: true, excludeUnderscoreProps: true, excludeDollarProps: false })
33+
const autoRun = ref(true)
34+
const autoRunSeconds = ref(10)
35+
return { settings, autoRun, autoRunSeconds }
36+
},
37+
template: `<div class="p-4 max-w-2xl"><QuerySettings v-model="settings" v-model:auto-run="autoRun" v-model:auto-run-seconds="autoRunSeconds" /></div>`,
38+
}),
39+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import type { Meta, StoryObj } from '@storybook/vue3-vite'
2+
import { sampleFilters, sampleSaved, sampleSuggested } from './_fixtures'
3+
import SavedQueriesPanel from './SavedQueriesPanel.vue'
4+
5+
const meta = {
6+
title: 'DataInspector/SavedQueriesPanel',
7+
component: SavedQueriesPanel,
8+
tags: ['autodocs'],
9+
} satisfies Meta<typeof SavedQueriesPanel>
10+
11+
export default meta
12+
type Story = StoryObj<typeof meta>
13+
14+
export const WithSavedAndSuggested: Story = {
15+
args: {
16+
saved: sampleSaved,
17+
suggested: sampleSuggested,
18+
currentQuery: 'build.modules.size()',
19+
currentFilters: sampleFilters,
20+
},
21+
}
22+
23+
export const SuggestedOnly: Story = {
24+
args: {
25+
saved: [],
26+
suggested: sampleSuggested,
27+
currentQuery: 'os.platform',
28+
currentFilters: sampleFilters,
29+
},
30+
}
31+
32+
export const Readonly: Story = {
33+
args: {
34+
saved: sampleSaved,
35+
suggested: sampleSuggested,
36+
currentQuery: 'build.modules.size()',
37+
currentFilters: sampleFilters,
38+
readonly: true,
39+
},
40+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import type { DataSourceMeta, FilterOptions, Query, SavedQuery } from '../../engine'
2+
3+
// Shared, static sample data so the presentational components render in
4+
// isolation without a live RPC connection to a server-side data source.
5+
6+
export const sampleSources: DataSourceMeta[] = [
7+
{
8+
id: 'devframe',
9+
title: 'Devframe Context',
10+
description: 'Runtime context, OS and process info, plus a playground graph.',
11+
icon: 'i-ph:cube-duotone',
12+
static: false,
13+
queries: [
14+
{ query: 'playground.requests.entries.mapEntries().value.sort(hits desc)', title: 'Top requests' },
15+
{ query: 'playground.build.modules.[sizeKb > 80].({ id, sizeKb })', title: 'Heavy modules' },
16+
],
17+
},
18+
{
19+
id: 'build',
20+
title: 'Build Modules',
21+
description: 'The production module graph from the last build.',
22+
icon: 'i-ph:package-duotone',
23+
static: true,
24+
},
25+
]
26+
27+
/** A one-level type skeleton, as the shape panel receives from the backend. */
28+
export const sampleSkeleton: Record<string, unknown> = {
29+
requests: 'Map(3)',
30+
middlewares: 'array',
31+
build: 'object',
32+
os: 'object',
33+
uptimeMs: 'number',
34+
startedAt: 'Date',
35+
}
36+
37+
export const sampleFilters: Required<FilterOptions> = {
38+
excludeFunctions: true,
39+
excludeUnderscoreProps: false,
40+
excludeDollarProps: false,
41+
}
42+
43+
export const sampleSuggested: Query[] = [
44+
{ query: 'playground.requests.size()', title: 'Request count' },
45+
{ query: 'os.({ platform, arch, release })', title: 'OS summary' },
46+
]
47+
48+
export const sampleSaved: SavedQuery[] = [
49+
{
50+
id: 'heavy-modules',
51+
query: 'build.modules.[sizeKb > 80].({ id, sizeKb }).sort(sizeKb desc)',
52+
title: 'Heavy modules',
53+
description: 'Modules over 80 kB, largest first.',
54+
scope: 'project',
55+
updatedAt: Date.now() - 1000 * 60 * 12,
56+
},
57+
{
58+
id: 'slow-requests',
59+
query: 'playground.requests.entries.mapEntries().value.[ms > 100]',
60+
title: 'Slow requests',
61+
scope: 'workspace',
62+
updatedAt: Date.now() - 1000 * 60 * 60 * 26,
63+
},
64+
]

plugins/og/.storybook/main.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import type { StorybookConfig } from '@storybook/vue3-vite'
2+
import vue from '@vitejs/plugin-vue'
3+
import UnoCSS from 'unocss/vite'
4+
import { mergeConfig } from 'vite'
5+
import { alias } from '../../../alias'
6+
7+
const config: StorybookConfig = {
8+
stories: ['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
9+
addons: ['@storybook/addon-docs'],
10+
framework: {
11+
name: '@storybook/vue3-vite',
12+
options: {},
13+
},
14+
docs: {},
15+
async viteFinal(config) {
16+
return mergeConfig(config, {
17+
resolve: { alias },
18+
plugins: [vue(), UnoCSS()],
19+
// Dev tool reached from arbitrary hostnames (LAN IPs, tunnels,
20+
// tailnets), e.g. when iframed by the storybook hub.
21+
server: { allowedHosts: true },
22+
})
23+
},
24+
}
25+
export default config

plugins/og/.storybook/preview.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import type { Decorator, Preview } from '@storybook/vue3-vite'
2+
import 'virtual:uno.css'
3+
import '@antfu/design/styles.css'
4+
import '../src/spa/app/assets/main.css'
5+
6+
// Drive the shared `@antfu/design` tokens off the toolbar theme toggle: dark mode
7+
// is the `.dark` class on `<html>`, and the canvas takes the semantic
8+
// `bg-base`/`color-base` surface — matching every other devframe surface.
9+
function applyTheme(theme: string): void {
10+
document.documentElement.classList.toggle('dark', theme !== 'light')
11+
document.body.classList.add('bg-base', 'color-base', 'font-sans')
12+
}
13+
14+
const withTheme: Decorator = (story, context) => {
15+
applyTheme(context.globals.theme ?? 'dark')
16+
return { components: { story }, template: '<story />' }
17+
}
18+
19+
const preview: Preview = {
20+
parameters: {
21+
layout: 'fullscreen',
22+
controls: {
23+
expanded: true,
24+
matchers: {
25+
color: /(background|color)$/i,
26+
date: /Date$/i,
27+
},
28+
},
29+
},
30+
globalTypes: {
31+
theme: {
32+
description: 'Color theme',
33+
defaultValue: 'dark',
34+
toolbar: {
35+
title: 'Theme',
36+
icon: 'contrast',
37+
items: [
38+
{ value: 'light', title: 'Light', icon: 'sun' },
39+
{ value: 'dark', title: 'Dark', icon: 'moon' },
40+
],
41+
dynamicTitle: true,
42+
},
43+
},
44+
},
45+
decorators: [withTheme],
46+
}
47+
48+
export default preview

0 commit comments

Comments
 (0)