Skip to content

Commit 348bd9d

Browse files
authored
🤖 feat: add Storybook for component development (#245)
Add Storybook 8.6 with stories for 11 components, CI integration, and visual testing setup. ## Usage ```bash make storybook # Start dev server at http://localhost:6006 make storybook-build # Build static site make test-storybook # Run interaction tests ``` ## What's Included - Stories for all key UI components (CommandPalette, Messages, Modals, etc.) - Inherits project styling (colors, fonts, scrollbars) and aliases - CI integration with Playwright interaction tests - Proper context providers for realistic component rendering See `docs/storybook.md` for details. _Generated with `cmux`_ touched up by hand. Signed-off-by: Thomas Kosiewski <tk@coder.com>
1 parent 3e7c7b7 commit 348bd9d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+5559
-782
lines changed

.github/workflows/ci.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,31 @@ jobs:
7979
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
8080
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
8181

82+
storybook-test:
83+
name: Storybook Interaction Tests
84+
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-16' || 'ubuntu-latest' }}
85+
steps:
86+
- name: Checkout code
87+
uses: actions/checkout@v4
88+
with:
89+
fetch-depth: 0 # Required for git describe to find tags
90+
91+
- uses: ./.github/actions/setup-cmux
92+
93+
- name: Install Playwright browsers
94+
run: bun x playwright install --with-deps
95+
96+
- name: Build Storybook
97+
run: make storybook-build
98+
99+
- name: Serve Storybook
100+
run: |
101+
bun x http-server storybook-static -p 6006 &
102+
sleep 5
103+
104+
- name: Run Storybook tests
105+
run: make test-storybook
106+
82107
e2e-test:
83108
name: End-to-End Tests
84109
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-16' || 'ubuntu-latest' }}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,4 @@ __pycache__
9494

9595
tmpfork
9696
.cmux-agent-cli
97+
storybook-static/

.storybook/main.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import type { StorybookConfig } from "@storybook/react-vite";
2+
import { mergeConfig } from "vite";
3+
import path from "path";
4+
5+
const config: StorybookConfig = {
6+
stories: ["../src/**/*.stories.@(ts|tsx)"],
7+
addons: [
8+
"@storybook/addon-essentials",
9+
"@storybook/addon-interactions",
10+
"@storybook/addon-links",
11+
],
12+
framework: {
13+
name: "@storybook/react-vite",
14+
options: {},
15+
},
16+
viteFinal: async (config) => {
17+
return mergeConfig(config, {
18+
// Inherit project aliases
19+
resolve: {
20+
alias: {
21+
"@": path.resolve(__dirname, "../src"),
22+
},
23+
},
24+
});
25+
},
26+
};
27+
28+
export default config;

.storybook/preview.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import type { Preview } from "@storybook/react";
2+
import { GlobalColors } from "../src/styles/colors";
3+
import { GlobalFonts } from "../src/styles/fonts";
4+
import { GlobalScrollbars } from "../src/styles/scrollbars";
5+
6+
const preview: Preview = {
7+
decorators: [
8+
(Story) => (
9+
<>
10+
<GlobalColors />
11+
<GlobalFonts />
12+
<GlobalScrollbars />
13+
<Story />
14+
</>
15+
),
16+
],
17+
parameters: {
18+
controls: {
19+
matchers: {
20+
color: /(background|color)$/i,
21+
date: /Date$/i,
22+
},
23+
},
24+
},
25+
};
26+
27+
export default preview;

Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ include fmt.mk
3232
.PHONY: test test-unit test-integration test-watch test-coverage test-e2e
3333
.PHONY: dist dist-mac dist-win dist-linux
3434
.PHONY: docs docs-build docs-watch
35+
.PHONY: storybook storybook-build test-storybook
3536
.PHONY: benchmark-terminal
3637
.PHONY: ensure-deps
3738
.PHONY: check-eager-imports check-bundle-size check-startup
@@ -223,6 +224,16 @@ docs-build: ## Build documentation
223224
docs-watch: ## Watch and rebuild documentation
224225
@cd docs && mdbook watch
225226

227+
## Storybook
228+
storybook: node_modules/.installed ## Start Storybook development server
229+
@bun x storybook dev -p 6006
230+
231+
storybook-build: node_modules/.installed ## Build static Storybook
232+
@bun x storybook build
233+
234+
test-storybook: node_modules/.installed ## Run Storybook interaction tests (requires Storybook to be running or built)
235+
@bun x test-storybook
236+
226237
## Benchmarks
227238
benchmark-terminal: ## Run Terminal-Bench with the cmux agent (use TB_DATASET/TB_ARGS to customize)
228239
@TB_DATASET=$${TB_DATASET:-terminal-bench-core==0.1.1}; \

bun.lock

Lines changed: 1203 additions & 122 deletions
Large diffs are not rendered by default.

docs/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@
2323

2424
# Development
2525

26+
- [Storybook](./storybook.md)
2627
- [Terminal Benchmarking](./benchmarking.md)
2728
- [AGENTS](./AGENTS.md)

docs/storybook.md

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# Storybook
2+
3+
Storybook is a tool for developing and testing UI components in isolation. It provides a sandboxed environment where you can build, view, and test components without running the full Electron application.
4+
5+
## Starting Storybook
6+
7+
```bash
8+
make storybook
9+
# or
10+
bun run storybook
11+
```
12+
13+
This will start the Storybook development server at `http://localhost:6006`.
14+
15+
## Building Static Storybook
16+
17+
To build a static version of Storybook that can be deployed:
18+
19+
```bash
20+
make storybook-build
21+
# or
22+
bun run storybook:build
23+
```
24+
25+
The output will be in `storybook-static/`.
26+
27+
## Writing Stories
28+
29+
Stories are colocated with their components. For example, `ErrorMessage.tsx` has its stories in `ErrorMessage.stories.tsx` in the same directory.
30+
31+
### Basic Story Structure
32+
33+
```typescript
34+
import type { Meta, StoryObj } from "@storybook/react";
35+
import { MyComponent } from "./MyComponent";
36+
37+
const meta = {
38+
title: "Components/MyComponent",
39+
component: MyComponent,
40+
parameters: {
41+
layout: "centered", // or "fullscreen" or "padded"
42+
},
43+
tags: ["autodocs"], // Enables automatic documentation
44+
} satisfies Meta<typeof MyComponent>;
45+
46+
export default meta;
47+
type Story = StoryObj<typeof meta>;
48+
49+
export const Default: Story = {
50+
args: {
51+
prop1: "value1",
52+
prop2: "value2",
53+
},
54+
};
55+
56+
export const Variant: Story = {
57+
args: {
58+
prop1: "different value",
59+
prop2: "another value",
60+
},
61+
};
62+
```
63+
64+
### Component Examples
65+
66+
See the existing stories for reference:
67+
68+
- `src/components/ErrorMessage.stories.tsx` - Simple component with multiple states
69+
- `src/components/Modal.stories.tsx` - Complex component with children and multiple variants
70+
71+
## Global Styles
72+
73+
Storybook automatically applies the same global styles as the main app:
74+
75+
- Color variables (`GlobalColors`)
76+
- Font definitions (`GlobalFonts`)
77+
- Scrollbar styles (`GlobalScrollbars`)
78+
79+
These are configured in `.storybook/preview.tsx`.
80+
81+
## Handling Electron APIs
82+
83+
Some components depend on `window.api` for Electron IPC communication. For these components:
84+
85+
1. **Preferred**: Extract the component logic to accept props instead of calling IPC directly
86+
2. **Alternative**: Mock the `window.api` object in `.storybook/preview.tsx`
87+
88+
Example mock structure:
89+
90+
```typescript
91+
window.api = {
92+
workspace: {
93+
create: async () => ({ success: true, metadata: { ... } }),
94+
list: async () => ({ success: true, workspaces: [...] }),
95+
// ...
96+
},
97+
// ... other IPC channels
98+
};
99+
```
100+
101+
## Benefits
102+
103+
- **Isolated Development**: Build components without running the full Electron app
104+
- **Visual Testing**: See all component states at once
105+
- **Documentation**: Stories serve as living documentation with `autodocs`
106+
- **Faster Iteration**: Hot reload is faster than Electron rebuilds
107+
- **Accessibility**: Storybook addons can check accessibility issues
108+
109+
## Configuration
110+
111+
- `.storybook/main.ts` - Main Storybook configuration
112+
- `.storybook/preview.tsx` - Global decorators and parameters
113+
- `tsconfig.json` - Includes `.storybook/**/*.ts` for type checking
114+
115+
## Tips
116+
117+
- Keep stories simple and focused on visual states
118+
- Use Storybook's Controls addon to make props interactive
119+
- Add multiple stories for different states (loading, error, success, etc.)
120+
- Use the `tags: ["autodocs"]` option to generate automatic documentation

eslint.config.mjs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,11 @@ export default defineConfig([
250250
},
251251
{
252252
// Allow dynamic imports for lazy-loading AI SDK packages (startup optimization)
253-
files: ["src/services/aiService.ts", "src/utils/tools/tools.ts", "src/utils/ai/providerFactory.ts"],
253+
files: [
254+
"src/services/aiService.ts",
255+
"src/utils/tools/tools.ts",
256+
"src/utils/ai/providerFactory.ts",
257+
],
254258
rules: {
255259
"no-restricted-syntax": "off",
256260
},

package.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@
2727
"dist:linux": "make dist-linux",
2828
"docs": "make docs",
2929
"docs:build": "make docs-build",
30-
"docs:watch": "make docs-watch"
30+
"docs:watch": "make docs-watch",
31+
"storybook": "make storybook",
32+
"storybook:build": "make storybook-build",
33+
"test:storybook": "make test-storybook"
3134
},
3235
"dependencies": {
3336
"@ai-sdk/anthropic": "^2.0.29",
@@ -65,6 +68,14 @@
6568
"devDependencies": {
6669
"@eslint/js": "^9.36.0",
6770
"@playwright/test": "^1.56.0",
71+
"@storybook/addon-essentials": "^8.6.14",
72+
"@storybook/addon-interactions": "^8.6.14",
73+
"@storybook/addon-links": "^8.6.14",
74+
"@storybook/blocks": "^8.6.14",
75+
"@storybook/react": "^8.6.14",
76+
"@storybook/react-vite": "^8.6.14",
77+
"@storybook/test-runner": "^0.23.0",
78+
"@testing-library/react": "^16.3.0",
6879
"@types/bun": "^1.2.23",
6980
"@types/diff": "^8.0.0",
7081
"@types/jest": "^30.0.0",
@@ -91,6 +102,7 @@
91102
"jest": "^30.1.3",
92103
"playwright": "^1.56.0",
93104
"prettier": "^3.6.2",
105+
"storybook": "^8.6.14",
94106
"ts-jest": "^29.4.4",
95107
"tsc-alias": "^1.8.16",
96108
"typescript": "^5.1.3",

0 commit comments

Comments
 (0)