Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
304fd3f
fix(addon-a11y): clear status transition timer on unmount to prevent …
mixelburg Mar 18, 2026
2a3bdbf
Merge branch 'next' into fix/a11y-context-clear-timeout-on-unmount
yannbf Mar 20, 2026
f5567b4
Automigrations: fix nextjs-to-nextjs-vite corrupting configs already …
nathanjessen Mar 20, 2026
53ff088
fix: apply prettier formatting to A11yContext
Mar 22, 2026
262d541
Fix builder-vite hash collision causing duplicate variable declaratio…
chida09 Mar 23, 2026
8863f78
Apply >>> 0 per iteration to prevent precision loss on long paths
chida09 Mar 23, 2026
30d5489
Merge branch 'next' into fix/34270-builder-vite-hash-collision
kasperpeulen Mar 23, 2026
cbbcd7b
Merge branch 'next' into fix/nextjs-to-nextjs-vite-regex
kasperpeulen Mar 23, 2026
3b14409
Merge branch 'next' into fix/a11y-context-clear-timeout-on-unmount
kasperpeulen Mar 23, 2026
c8e8253
Manager: URL-based tag filter state + filter-aware initial story sele…
valentinpalkovic Mar 23, 2026
21219f7
Clear tags appropriately from internal state
valentinpalkovic Mar 23, 2026
e73920d
Fix typing
valentinpalkovic Mar 23, 2026
c9da280
improve handling of null and undefined values in query parameters and…
valentinpalkovic Mar 24, 2026
2f80eb0
Merge remote-tracking branch 'origin/next' into valentin/url-based-ta…
valentinpalkovic Mar 24, 2026
0e26cda
handle optional chaining for location and customQueryParams in naviga…
valentinpalkovic Mar 24, 2026
ce7f042
Merge branch 'next' into fix/a11y-context-clear-timeout-on-unmount
valentinpalkovic Mar 24, 2026
01763b6
Linting
valentinpalkovic Mar 24, 2026
5f92309
Core: Add changeDetection feature flag
valentinpalkovic Mar 25, 2026
f1256e9
Fix tests
valentinpalkovic Mar 25, 2026
7f4b44e
Formatting
valentinpalkovic Mar 25, 2026
f4adf64
Merge branch 'next' into fix/34270-builder-vite-hash-collision
valentinpalkovic Mar 25, 2026
d5392f3
Merge branch 'next' into fix/nextjs-to-nextjs-vite-regex
JReinhold Mar 25, 2026
a72ebc8
Don't check format of version JSON files
JReinhold Mar 25, 2026
1a90d3b
fix format 🙃
JReinhold Mar 25, 2026
f65c67a
Merge pull request #34314 from storybookjs/valentin/change-detection-…
valentinpalkovic Mar 25, 2026
db1ddc8
Merge branch 'next-release' into next
storybook-bot Mar 25, 2026
1c38a36
Merge pull request #34274 from chida09/fix/34270-builder-vite-hash-co…
valentinpalkovic Mar 25, 2026
78b7e12
Apply requested changes
valentinpalkovic Mar 26, 2026
4e8f2f7
Merge remote-tracking branch 'origin/next' into valentin/url-based-ta…
valentinpalkovic Mar 26, 2026
cdaa547
Format
valentinpalkovic Mar 26, 2026
febea63
Merge pull request #34203 from mixelburg/fix/a11y-context-clear-timeo…
valentinpalkovic Mar 26, 2026
b1ebc97
React-Vite: Upgrade @joshwooding/vite-plugin-react-docgen-typescript …
beeswhacks Mar 26, 2026
747b613
Initial plan
Copilot Mar 26, 2026
7fb9150
Merge branch 'next' into upgrade-vite-plugin-react-docgen-typescript-…
beeswhacks Mar 26, 2026
776a248
refactor: extract shared PseudoStateGrid component to eliminate dupli…
Copilot Mar 26, 2026
17b4191
Merge branch 'next' into valentin/url-based-tag-state
valentinpalkovic Mar 26, 2026
f4e4a53
Merge pull request #34283 from storybookjs/valentin/url-based-tag-state
valentinpalkovic Mar 26, 2026
544d8c9
Merge pull request #34335 from beeswhacks/upgrade-vite-plugin-react-d…
valentinpalkovic Mar 26, 2026
382d78b
Formatting
valentinpalkovic Mar 26, 2026
d19270e
Merge pull request #34249 from nathanjessen/fix/nextjs-to-nextjs-vite…
valentinpalkovic Mar 26, 2026
a413a7f
Merge branch 'next' into copilot/fix-duplicate-pseudo-state-grid
valentinpalkovic Mar 26, 2026
f6a6ba7
Merge pull request #34321 from storybookjs/jeppe/fix-release-format
JReinhold Mar 26, 2026
810093c
Merge pull request #34334 from storybookjs/copilot/fix-duplicate-pseu…
valentinpalkovic Mar 26, 2026
6de8c90
Write changelog for 10.4.0-alpha.5 [skip ci]
storybook-bot Mar 26, 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
3 changes: 2 additions & 1 deletion .oxfmtrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
"*.yaml",
"*.md",
"*.mdx",
"!docs/_snippets/**"
"!docs/_snippets/**",
"!docs/versions/*.json"
],
"overrides": [
{
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.prerelease.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
## 10.4.0-alpha.5

- Addon-a11y: Clear status transition timer on unmount to prevent test flake - [#34203](https://github.com/storybookjs/storybook/pull/34203), thanks @mixelburg!
- Builder-Vite: Use djb2 hash to prevent variable name collisions in builder-vite - [#34274](https://github.com/storybookjs/storybook/pull/34274), thanks @chida09!
- CLI: Fix Next.js Vite automigration corrupting configs already using `@storybook/nextjs-vite` - [#34249](https://github.com/storybookjs/storybook/pull/34249), thanks @nathanjessen!
- Core: Add changeDetection feature flag - [#34314](https://github.com/storybookjs/storybook/pull/34314), thanks @valentinpalkovic!
- Manager: URL-based tag filter state + filter-aware initial story selection - [#34283](https://github.com/storybookjs/storybook/pull/34283), thanks @valentinpalkovic!
- React-Vite: Upgrade @joshwooding/vite-plugin-react-docgen-typescript to 0.7.0 - [#34335](https://github.com/storybookjs/storybook/pull/34335), thanks @beeswhacks!
- Refactor: Extract shared `PseudoStateGrid` component in pseudo-states stories - [#34334](https://github.com/storybookjs/storybook/pull/34334), thanks @copilot-swe-agent!

## 10.4.0-alpha.4

- Addon-Docs: Add Reset story button to re-render stories in docs - [#34086](https://github.com/storybookjs/storybook/pull/34086), thanks @6810779s!
Expand Down
31 changes: 28 additions & 3 deletions code/addons/a11y/src/components/A11yContext.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import type { FC, PropsWithChildren } from 'react';
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import React, {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useRef,
useState,
} from 'react';

import {
STORY_CHANGED,
Expand Down Expand Up @@ -140,9 +148,22 @@ export const A11yContextProvider: FC<PropsWithChildren> = (props) => {
}, [setState, storyId]);

const handleToggleHighlight = useCallback(() => {
setState((prev) => ({ ...prev, ui: { ...prev.ui, highlighted: !prev.ui.highlighted } }));
setState((prev) => ({
...prev,
ui: { ...prev.ui, highlighted: !prev.ui.highlighted },
}));
}, [setState]);

const statusTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);

useEffect(() => {
return () => {
if (statusTimerRef.current !== null) {
clearTimeout(statusTimerRef.current);
}
};
}, []);

const [selectedItems, setSelectedItems] = useState<Map<string, string>>(() => {
const initialValue = new Map();
// Check if the a11ySelection param is a valid format before parsing it
Expand Down Expand Up @@ -202,7 +223,11 @@ export const A11yContextProvider: FC<PropsWithChildren> = (props) => {
if (storyId === id) {
setState((prev) => ({ ...prev, status: 'ran', results: axeResults }));

setTimeout(() => {
if (statusTimerRef.current !== null) {
clearTimeout(statusTimerRef.current);
}
statusTimerRef.current = setTimeout(() => {
statusTimerRef.current = null;
setState((prev) => {
if (prev.status === 'ran') {
return { ...prev, status: 'ready' };
Expand Down
28 changes: 2 additions & 26 deletions code/addons/pseudo-states/src/stories/Button.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { styled } from 'storybook/theming';

import { Button } from './Button';
import './grid.css';
import { PseudoStateGrid } from './PseudoStateGrid';

const meta = {
title: 'Button',
Expand All @@ -18,32 +19,7 @@ type Story = StoryObj<typeof meta>;

export const All: Story = {
render: (args: ComponentProps<typeof Button>) => (
<div className="story-grid">
<div>
<Button {...args}>Normal</Button>
</div>
<div className="pseudo-hover-all">
<Button {...args}>Hover</Button>
</div>
<div className="pseudo-focus-all">
<Button {...args}>Focus</Button>
</div>
<div className="pseudo-active-all">
<Button {...args}>Active</Button>
</div>
<div className="pseudo-hover-all pseudo-focus-all">
<Button {...args}>Hover Focus</Button>
</div>
<div className="pseudo-hover-all pseudo-active-all">
<Button {...args}>Hover Active</Button>
</div>
<div className="pseudo-focus-all pseudo-active-all">
<Button {...args}>Focus Active</Button>
</div>
<div className="pseudo-hover-all pseudo-focus-all pseudo-active-all">
<Button {...args}>Hover Focus Active</Button>
</div>
</div>
<PseudoStateGrid render={(label) => <Button {...args}>{label}</Button>} />
),
};

Expand Down
28 changes: 2 additions & 26 deletions code/addons/pseudo-states/src/stories/CSSAtRules.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useChannel, useStoryContext } from 'storybook/preview-api';

import { Button } from './CSSAtRules';
import './grid.css';
import { PseudoStateGrid } from './PseudoStateGrid';

const meta = {
title: 'CSSAtRules',
Expand All @@ -21,32 +22,7 @@ type Story = StoryObj<typeof meta>;

export const All: Story = {
render: (args: ComponentProps<typeof Button>) => (
<div className="story-grid">
<div>
<Button {...args}>Normal</Button>
</div>
<div className="pseudo-hover-all">
<Button {...args}>Hover</Button>
</div>
<div className="pseudo-focus-all">
<Button {...args}>Focus</Button>
</div>
<div className="pseudo-active-all">
<Button {...args}>Active</Button>
</div>
<div className="pseudo-hover-all pseudo-focus-all">
<Button {...args}>Hover Focus</Button>
</div>
<div className="pseudo-hover-all pseudo-active-all">
<Button {...args}>Hover Active</Button>
</div>
<div className="pseudo-focus-all pseudo-active-all">
<Button {...args}>Focus Active</Button>
</div>
<div className="pseudo-hover-all pseudo-focus-all pseudo-active-all">
<Button {...args}>Hover Focus Active</Button>
</div>
</div>
<PseudoStateGrid render={(label) => <Button {...args}>{label}</Button>} />
),
};

Expand Down
41 changes: 7 additions & 34 deletions code/addons/pseudo-states/src/stories/CustomElement.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { Meta, StoryObj } from '@storybook/react-vite';

import './CustomElement';
import './grid.css';
import { PseudoStateGrid } from './PseudoStateGrid';

const meta = {
title: 'CustomElement',
Expand All @@ -20,40 +21,12 @@ type Story = StoryObj<typeof meta>;

export const All: Story = {
render: () => (
<div className="story-grid">
<div>
{/* @ts-expect-error We're dealing with a web component here */}
<custom-element>Normal</custom-element>
</div>
<div className="pseudo-hover-all">
{/* @ts-expect-error We're dealing with a web component here */}
<custom-element>Hover</custom-element>
</div>
<div className="pseudo-focus-all">
{/* @ts-expect-error We're dealing with a web component here */}
<custom-element>Focus</custom-element>
</div>
<div className="pseudo-active-all">
{/* @ts-expect-error We're dealing with a web component here */}
<custom-element>Active</custom-element>
</div>
<div className="pseudo-hover-all pseudo-focus-all">
{/* @ts-expect-error We're dealing with a web component here */}
<custom-element>Hover Focus</custom-element>
</div>
<div className="pseudo-hover-all pseudo-active-all">
{/* @ts-expect-error We're dealing with a web component here */}
<custom-element>Hover Active</custom-element>
</div>
<div className="pseudo-focus-all pseudo-active-all">
{/* @ts-expect-error We're dealing with a web component here */}
<custom-element>Focus Active</custom-element>
</div>
<div className="pseudo-hover-all pseudo-focus-all pseudo-active-all">
{/* @ts-expect-error We're dealing with a web component here */}
<custom-element>Hover Focus Active</custom-element>
</div>
</div>
<PseudoStateGrid
render={(label) => (
// @ts-expect-error We're dealing with a web component here
<custom-element>{label}</custom-element>
)}
/>
),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { Meta, StoryObj } from '@storybook/react-vite';

import './CustomElementNested';
import './grid.css';
import { PseudoStateGrid } from './PseudoStateGrid';

const meta = {
title: 'CustomElementNested',
Expand All @@ -20,40 +21,12 @@ type Story = StoryObj<typeof meta>;

export const All: Story = {
render: () => (
<div className="story-grid">
<div>
{/* @ts-expect-error We're dealing with a web component here */}
<custom-element-nested>Normal</custom-element-nested>
</div>
<div className="pseudo-hover-all">
{/* @ts-expect-error We're dealing with a web component here */}
<custom-element-nested>Hover</custom-element-nested>
</div>
<div className="pseudo-focus-all">
{/* @ts-expect-error We're dealing with a web component here */}
<custom-element-nested>Focus</custom-element-nested>
</div>
<div className="pseudo-active-all">
{/* @ts-expect-error We're dealing with a web component here */}
<custom-element-nested>Active</custom-element-nested>
</div>
<div className="pseudo-hover-all pseudo-focus-all">
{/* @ts-expect-error We're dealing with a web component here */}
<custom-element-nested>Hover Focus</custom-element-nested>
</div>
<div className="pseudo-hover-all pseudo-active-all">
{/* @ts-expect-error We're dealing with a web component here */}
<custom-element-nested>Hover Active</custom-element-nested>
</div>
<div className="pseudo-focus-all pseudo-active-all">
{/* @ts-expect-error We're dealing with a web component here */}
<custom-element-nested>Focus Active</custom-element-nested>
</div>
<div className="pseudo-hover-all pseudo-focus-all pseudo-active-all">
{/* @ts-expect-error We're dealing with a web component here */}
<custom-element-nested>Hover Focus Active</custom-element-nested>
</div>
</div>
<PseudoStateGrid
render={(label) => (
// @ts-expect-error We're dealing with a web component here
<custom-element-nested>{label}</custom-element-nested>
)}
/>
),
};

Expand Down
20 changes: 20 additions & 0 deletions code/addons/pseudo-states/src/stories/PseudoStateGrid.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React, { type ReactNode } from 'react';

interface PseudoStateGridProps {
render: (label: string) => ReactNode;
}

export const PseudoStateGrid = ({ render }: PseudoStateGridProps) => (
<div className="story-grid">
<div>{render('Normal')}</div>
<div className="pseudo-hover-all">{render('Hover')}</div>
<div className="pseudo-focus-all">{render('Focus')}</div>
<div className="pseudo-active-all">{render('Active')}</div>
<div className="pseudo-hover-all pseudo-focus-all">{render('Hover Focus')}</div>
<div className="pseudo-hover-all pseudo-active-all">{render('Hover Active')}</div>
<div className="pseudo-focus-all pseudo-active-all">{render('Focus Active')}</div>
<div className="pseudo-hover-all pseudo-focus-all pseudo-active-all">
{render('Hover Focus Active')}
</div>
</div>
);
30 changes: 2 additions & 28 deletions code/addons/pseudo-states/src/stories/ShadowRoot.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { Meta, StoryObj } from '@storybook/react-vite';

import { ShadowRoot } from './ShadowRoot';
import './grid.css';
import { PseudoStateGrid } from './PseudoStateGrid';

const meta = {
title: 'ShadowRoot',
Expand All @@ -15,34 +16,7 @@ export default meta;
type Story = StoryObj<typeof meta>;

export const All: Story = {
render: () => (
<div className="story-grid">
<div>
<ShadowRoot label="Normal" />
</div>
<div className="pseudo-hover-all">
<ShadowRoot label="Hover" />
</div>
<div className="pseudo-focus-all">
<ShadowRoot label="Focus" />
</div>
<div className="pseudo-active-all">
<ShadowRoot label="Active" />
</div>
<div className="pseudo-hover-all pseudo-focus-all">
<ShadowRoot label="Hover Focus" />
</div>
<div className="pseudo-hover-all pseudo-active-all">
<ShadowRoot label="Hover Active" />
</div>
<div className="pseudo-focus-all pseudo-active-all">
<ShadowRoot label="Focus Active" />
</div>
<div className="pseudo-hover-all pseudo-focus-all pseudo-active-all">
<ShadowRoot label="Hover Focus Active" />
</div>
</div>
),
render: () => <PseudoStateGrid render={(label) => <ShadowRoot label={label} />} />,
};

export const Default: Story = {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { Meta, StoryObj } from '@storybook/react-vite';

import { ShadowRoot } from './ShadowRootWithPart';
import './grid.css';
import { PseudoStateGrid } from './PseudoStateGrid';

const meta = {
title: 'ShadowRootWithPart',
Expand All @@ -15,34 +16,7 @@ export default meta;
type Story = StoryObj<typeof meta>;

export const All: Story = {
render: () => (
<div className="story-grid">
<div>
<ShadowRoot label="Normal" />
</div>
<div className="pseudo-hover-all">
<ShadowRoot label="Hover" />
</div>
<div className="pseudo-focus-all">
<ShadowRoot label="Focus" />
</div>
<div className="pseudo-active-all">
<ShadowRoot label="Active" />
</div>
<div className="pseudo-hover-all pseudo-focus-all">
<ShadowRoot label="Hover Focus" />
</div>
<div className="pseudo-hover-all pseudo-active-all">
<ShadowRoot label="Hover Active" />
</div>
<div className="pseudo-focus-all pseudo-active-all">
<ShadowRoot label="Focus Active" />
</div>
<div className="pseudo-hover-all pseudo-focus-all pseudo-active-all">
<ShadowRoot label="Hover Focus Active" />
</div>
</div>
),
render: () => <PseudoStateGrid render={(label) => <ShadowRoot label={label} />} />,
};

export const Default: Story = {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ export function generateProjectAnnotationsCodeFromPreviews(options: {
`.trim();
}

/** djb2 hash — http://www.cse.yorku.ca/~oz/hash.html */
function hash(value: string) {
return value.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0);
let acc = 5381;
for (let i = 0; i < value.length; i++) {
acc = ((acc << 5) + acc + value.charCodeAt(i)) >>> 0;
}
return acc;
}
Loading
Loading