Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
c87abf7
Fix ArgsTable borders not visible in Windows High Contrast Mode
TheSeydiCharyyev Mar 23, 2026
0753371
Merge branch 'next' into fix/issue-24147-argstable-high-contrast-border
kasperpeulen Mar 23, 2026
3a9dfd8
Implement oxc based change detection
valentinpalkovic Apr 23, 2026
e7816ce
Add Vue and Svelte SFC import parsers for change detection
valentinpalkovic Apr 23, 2026
ba1d137
Remove legacy builder-level change detection
valentinpalkovic Apr 23, 2026
14fb211
Add change-detection benchmarks and profile timing
valentinpalkovic Apr 23, 2026
8ce32b3
oxc-parse: gate require() walk on ESM-only extensions and a source-le…
valentinpalkovic Apr 23, 2026
ce71d78
DependencyGraphBuilder: cache per-file resolution across story walks
valentinpalkovic Apr 23, 2026
e1a4fa4
dev-server: start change detection immediately after preview builder …
valentinpalkovic Apr 23, 2026
8eec664
oxc-parse: move parsing to a worker thread pool
valentinpalkovic Apr 24, 2026
b787df3
feat: add recovery path for direct-importer scan in IncrementalPatcher
valentinpalkovic Apr 27, 2026
5488fe2
OxcWorkerPool: drop STORYBOOK_CHANGE_DETECTION_* env-var escape hatches
valentinpalkovic Apr 27, 2026
a910043
Change Detection: optimize ReverseIndex and IncrementalPatcher
valentinpalkovic Apr 27, 2026
de58783
Change Detection: simplify and parallelise IncrementalPatcher
valentinpalkovic Apr 27, 2026
03b8fe1
Change Detection: address review findings
valentinpalkovic Apr 27, 2026
a9972de
Merge branch 'next' into fix/issue-24147-argstable-high-contrast-border
JReinhold Apr 27, 2026
e092e28
oxc-parser: extract sub-package out of change-detection
valentinpalkovic Apr 27, 2026
a789040
Change Detection: address CCG review findings
valentinpalkovic Apr 28, 2026
470721f
Change Detection: simplify per CCG review (~1000 LOC removed)
valentinpalkovic Apr 28, 2026
7834e25
Change Detection: promote ResolveConfig to shared ModuleResolveConfig
valentinpalkovic Apr 28, 2026
b2c2792
Change Detection: oxfmt formatting fix
valentinpalkovic Apr 28, 2026
0daa93c
Change Detection: fix svelte format + skip re-walk on unchanged dep set
valentinpalkovic Apr 28, 2026
c878a31
chore: deduplicate @emnapi/* entries in yarn.lock
valentinpalkovic Apr 28, 2026
19030f6
Change Detection: move setsEqual below imports in IncrementalPatcher
valentinpalkovic Apr 28, 2026
554ad3a
docs: fix quote style in eslint-plugin.mdx for oxfmt format check
valentinpalkovic Apr 28, 2026
1748799
Change Detection: remove unused benchmark scripts and related files
valentinpalkovic Apr 28, 2026
90efe22
Add E2E tests
valentinpalkovic Apr 29, 2026
d68f779
Fix formatting
valentinpalkovic Apr 29, 2026
11c5970
Merge branch 'next' into valentin/change-detection-perf
valentinpalkovic Apr 29, 2026
03c1037
Change Detection: final cleanup — remove AI slop, fix types and API
valentinpalkovic Apr 29, 2026
6f9e726
chore: initial plan for addressing review comments
Copilot Apr 29, 2026
8dbec7d
fix: remove tsconfigPath from Vite adapter; use static p-limit import
Copilot Apr 29, 2026
3f12c4e
Change Detection: add webpack5 builder adapter
valentinpalkovic Apr 29, 2026
e9dd8c6
Fix formatting
valentinpalkovic Apr 29, 2026
93adea8
Change Detection: extend E2E tests to cover webpack sandboxes
valentinpalkovic Apr 29, 2026
f48669b
Merge branch 'next' into valentin/change-detection-perf
valentinpalkovic Apr 30, 2026
b1efbaa
Auto resolve tsconfig paths in oxc-resolver lookups
valentinpalkovic Apr 30, 2026
dfd045a
Enhance ChangeDetectionResolverFactory to support yarn workspace cros…
valentinpalkovic Apr 30, 2026
9cf5088
Merge origin/next into valentin/change-detection-perf
valentinpalkovic Apr 30, 2026
ff915cf
Enable change detection feature flag in E2E sandbox templates
valentinpalkovic Apr 30, 2026
5f86246
Add clear filter button on sidebar empty state
ghengeveld May 1, 2026
716d5b7
Prevent race condition between two filter operations
ghengeveld May 1, 2026
1a1e6d1
Show tooltip on status filters
ghengeveld May 1, 2026
654b6a9
yarn dedupe
valentinpalkovic May 3, 2026
11c0deb
Merge branch 'next' into valentin/change-detection-perf
valentinpalkovic May 3, 2026
c1514d0
fix: cleanup AI-slop in tests, drain patchQueue before OXC pool dispose
valentinpalkovic May 3, 2026
41fe804
fix: init git during sandbox generation so change detection works at …
valentinpalkovic May 3, 2026
4a248d8
fix: scope change-detection git init to e2e-tests-dev task only
valentinpalkovic May 3, 2026
83a865d
Fix E2E tests
valentinpalkovic May 4, 2026
96433df
Cleanup
valentinpalkovic May 4, 2026
bdd7f73
Merge remote-tracking branch 'origin/next' into valentin/change-detec…
valentinpalkovic May 4, 2026
cbe567d
Merge branch 'next' into valentin/change-detection-perf
valentinpalkovic May 4, 2026
bb3f4c7
Dedupe lock file
valentinpalkovic May 4, 2026
1159b2a
Only show Clear filters button if there are active filters
ghengeveld May 4, 2026
4a1e48f
Move common logic into a shared util
ghengeveld May 5, 2026
0293537
Enhance metroConfig tests by adding formatted diff comparison for cod…
ndelangen May 5, 2026
cf431f3
Refactor metroConfig tests to use 'oxfmt' for formatting and update i…
ndelangen May 5, 2026
6d337b7
Merge pull request #34264 from TheSeydiCharyyev/fix/issue-24147-argst…
JReinhold May 5, 2026
6750e05
Merge pull request #34715 from storybookjs/norbert/rn-tests
ndelangen May 5, 2026
7dddaf0
Apply review feedback for change detection
valentinpalkovic May 5, 2026
7567d67
Merge remote-tracking branch 'origin/next' into valentin/change-detec…
valentinpalkovic May 5, 2026
10895d7
fix: init git for change detection on every dev task run
valentinpalkovic May 5, 2026
6f04e62
Cleanup
valentinpalkovic May 5, 2026
507f2bc
try catch git init in sandboxes
valentinpalkovic May 5, 2026
0c49e41
Merge pull request #34625 from storybookjs/valentin/change-detection-…
valentinpalkovic May 5, 2026
91f8d25
Merge pull request #34683 from storybookjs/sidebar-clear-filters
ghengeveld May 5, 2026
3e11a52
fix run oxfmt after generating source files
huang-julien May 5, 2026
5ca4d6f
Merge branch 'next' of https://github.com/storybookjs/storybook into …
huang-julien May 5, 2026
e56ff0e
Revert "fix run oxfmt after generating source files"
huang-julien May 5, 2026
78089ab
Write changelog for 10.4.0-alpha.17 [skip ci]
storybook-bot May 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.prerelease.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
## 10.4.0-alpha.17

- Agentic Setup: Keep sample content if users want onboarding - [#34704](https://github.com/storybookjs/storybook/pull/34704), thanks @Sidnioulz!
- Fix ArgsTable borders not visible in Windows High Contrast Mode - [#34264](https://github.com/storybookjs/storybook/pull/34264), thanks @TheSeydiCharyyev!
- MDX: Replace `@storybook/docs-mdx` with inline implementation - [#34611](https://github.com/storybookjs/storybook/pull/34611), thanks @copilot-swe-agent!
- Maintenance: Enhance ghost stories internal tests - [#34707](https://github.com/storybookjs/storybook/pull/34707), thanks @yannbf!
- ReactNative: Add Metro config AST codemod for init - [#34660](https://github.com/storybookjs/storybook/pull/34660), thanks @ndelangen!
- ReactNative: New init setup - [#34665](https://github.com/storybookjs/storybook/pull/34665), thanks @ndelangen!
- Tanstack: Add `@storybook/tanstack-react` package - [#34403](https://github.com/storybookjs/storybook/pull/34403), thanks @huang-julien!
- UI: Fix showing and hiding copy prompt in the correct scenarios - [#34706](https://github.com/storybookjs/storybook/pull/34706), thanks @yannbf!

## 10.4.0-alpha.16

- Automigration: Move RN on-device addons to `deviceAddons` - [#34659](https://github.com/storybookjs/storybook/pull/34659), thanks @ndelangen!
Expand Down
11 changes: 11 additions & 0 deletions code/addons/docs/src/blocks/components/ArgsTable/ArgsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,17 @@ export const TableWrapper = styled.table<{
},
}),
},
// High contrast mode: ensure borders are visible with system colors
'@media (forced-colors: active)': {
tbody: {
filter: 'none',

'> tr > *': {
borderColor: 'CanvasText',
},
},
},

// End awesome table styling
},
}));
Expand Down
143 changes: 143 additions & 0 deletions code/builders/builder-vite/src/change-detection-adapter/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// Tests the Vite implementation of ChangeDetectionAdapter — wiring of resolve config
// snapshot and chokidar event normalisation.
import { EventEmitter } from 'node:events';

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

import type { ViteDevServer } from 'vite';

import { createViteChangeDetectionAdapter } from './index.ts';

vi.mock('vite', { spy: true });

interface FakeViteDevServer {
config: {
root: string;
resolve?: {
alias?: unknown;
conditions?: string[];
tsconfig?: string;
};
};
watcher: EventEmitter;
}

function createFakeServer(overrides: Partial<FakeViteDevServer['config']> = {}): {
server: ViteDevServer;
watcher: EventEmitter;
} {
const watcher = new EventEmitter();
const server: FakeViteDevServer = {
config: {
root: '/repo',
...overrides,
},
watcher,
};
return { server: server as unknown as ViteDevServer, watcher };
}

describe('createViteChangeDetectionAdapter', () => {
beforeEach(() => {
vi.clearAllMocks();
});

afterEach(() => {
vi.restoreAllMocks();
});

it('snapshots projectRoot, alias and conditions from server.config in getResolveConfig()', async () => {
const alias = [{ find: '@', replacement: '/repo/src' }];
const conditions = ['import', 'module', 'default'];
const { server } = createFakeServer({
root: '/repo',
resolve: { alias, conditions },
});

const adapter = createViteChangeDetectionAdapter(server);
const config = await adapter.getResolveConfig();

expect(config).toEqual({
projectRoot: '/repo',
alias,
conditions,
});
});

it('forwards chokidar `add` events as kind: "add"', () => {
const { server, watcher } = createFakeServer();
const adapter = createViteChangeDetectionAdapter(server);
const handler = vi.fn();
adapter.onFileChange(handler);

watcher.emit('all', 'add', '/repo/src/A.tsx');

expect(handler).toHaveBeenCalledWith({ kind: 'add', path: '/repo/src/A.tsx' });
});

it('forwards chokidar `change` events as kind: "change"', () => {
const { server, watcher } = createFakeServer();
const adapter = createViteChangeDetectionAdapter(server);
const handler = vi.fn();
adapter.onFileChange(handler);

watcher.emit('all', 'change', '/repo/src/A.tsx');

expect(handler).toHaveBeenCalledWith({ kind: 'change', path: '/repo/src/A.tsx' });
});

it('forwards chokidar `unlink` events as kind: "unlink"', () => {
const { server, watcher } = createFakeServer();
const adapter = createViteChangeDetectionAdapter(server);
const handler = vi.fn();
adapter.onFileChange(handler);

watcher.emit('all', 'unlink', '/repo/src/A.tsx');

expect(handler).toHaveBeenCalledWith({ kind: 'unlink', path: '/repo/src/A.tsx' });
});

it('does NOT forward `addDir`, `unlinkDir`, `ready`, `raw`, or `error` chokidar events', () => {
const { server, watcher } = createFakeServer();
const adapter = createViteChangeDetectionAdapter(server);
const handler = vi.fn();
adapter.onFileChange(handler);

watcher.emit('all', 'addDir', '/repo/src/some-dir');
watcher.emit('all', 'unlinkDir', '/repo/src/some-dir');
watcher.emit('all', 'ready');
watcher.emit('all', 'raw', '/repo/src/A.tsx');
watcher.emit('all', 'error', new Error('boom'));

expect(handler).not.toHaveBeenCalled();
});

it('normalises chokidar paths via pathe.normalize before forwarding', () => {
const { server, watcher } = createFakeServer();
const adapter = createViteChangeDetectionAdapter(server);
const handler = vi.fn();
adapter.onFileChange(handler);

// Path with `/./` and mixed-case noise that pathe.normalize collapses.
watcher.emit('all', 'change', '/repo/src/./A.tsx');

expect(handler).toHaveBeenCalledWith({
kind: 'change',
path: '/repo/src/A.tsx',
});
});

it('returns an unsubscribe function that removes the listener', () => {
const { server, watcher } = createFakeServer();
const adapter = createViteChangeDetectionAdapter(server);
const handler = vi.fn();
const unsubscribe = adapter.onFileChange(handler);

watcher.emit('all', 'change', '/repo/src/A.tsx');
expect(handler).toHaveBeenCalledTimes(1);

unsubscribe();
watcher.emit('all', 'change', '/repo/src/B.tsx');
expect(handler).toHaveBeenCalledTimes(1);
});
});
53 changes: 53 additions & 0 deletions code/builders/builder-vite/src/change-detection-adapter/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type {
ChangeDetectionAdapter,
FileChangeEvent,
ModuleResolveConfig,
} from 'storybook/internal/core-server';

import { normalize } from 'pathe';
import type { ViteDevServer } from 'vite';

/**
* Vite implementation of {@link ChangeDetectionAdapter}.
*
* - `getResolveConfig()` snapshots `server.config.resolve.alias`, `server.config.resolve.conditions`
* and `server.config.root` once at startup. The detector caches the result.
* - `onFileChange()` subscribes to `server.watcher` (chokidar) and forwards `add`/`change`/`unlink`
* events with normalised absolute paths. Other chokidar event names (`addDir`, `unlinkDir`,
* `ready`, `raw`, `error`) are intentionally filtered out.
*/
export function createViteChangeDetectionAdapter(server: ViteDevServer): ChangeDetectionAdapter {
return {
async getResolveConfig(): Promise<ModuleResolveConfig> {
const resolveOpts = server.config.resolve;
// Vite normalises `resolve.alias` to its array form (`Array<{find, replacement, ...}>`)
// before we ever see it. The detector accepts both Record and Array shapes, so we pass
// the array through unchanged.
const alias = resolveOpts?.alias as ModuleResolveConfig['alias'];
const conditions = resolveOpts?.conditions;

return {
projectRoot: server.config.root,
alias,
conditions,
};
},

onFileChange(handler) {
const FORWARDED_EVENTS = new Set<FileChangeEvent['kind']>(['add', 'change', 'unlink']);
const isForwardedEvent = (name: string): name is FileChangeEvent['kind'] =>
FORWARDED_EVENTS.has(name as FileChangeEvent['kind']);

const onAll = (eventName: string, path: string) => {
if (!isForwardedEvent(eventName)) {
return;
}
handler({ kind: eventName, path: normalize(path) });
};
server.watcher.on('all', onAll);
return () => {
server.watcher.off('all', onAll);
};
},
};
}
Loading