Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
fd0ccae
Ensure body never scrolls out of the viewport
ghengeveld Dec 10, 2025
d26a688
Reset panel height so guided tour tooltip can render correctly
ghengeveld Dec 10, 2025
77aee15
Tweak controls panel styling to fix overflow issue and design consist…
ghengeveld Dec 10, 2025
a232aec
Emit 'openSurvey' event type when opening the IntentSurvey
ghengeveld Dec 10, 2025
4877857
Fix onboarding survey open state handling
ghengeveld Dec 10, 2025
fbb8802
Synchronize vision simulator state with globals and apply vision filt…
ghengeveld Dec 23, 2025
8bf98c1
Ensure error display is not affected by vision filter, and add stories
ghengeveld Dec 23, 2025
74f7123
Fix import
ghengeveld Dec 23, 2025
db0548d
Drop unit tests in favor of story with play function assertions
ghengeveld Dec 23, 2025
3830aed
Avoid splitting filter string, which may not work properly
ghengeveld Dec 23, 2025
9d81a4b
Merge branch 'next' into fix-onboarding-survey
ndelangen Dec 24, 2025
30b5a03
Merge branch 'next' into vision-simulator-global
ghengeveld Jan 7, 2026
b7a6c6a
Add Jest DOM types reference in A11YPanel test file
ghengeveld Jan 7, 2026
22f271f
Merge branch 'next' into fix-onboarding-survey
ghengeveld Jan 7, 2026
ef16fc8
Slight improvement on interdependencies
ghengeveld Jan 7, 2026
73828b0
Merge pull request #33326 from storybookjs/fix-onboarding-survey
ghengeveld Jan 8, 2026
e3601ea
Remove invalid closing semicolon from triple-slash directives
ghengeveld Jan 8, 2026
d03a3ee
Merge pull request #33418 from storybookjs/vision-simulator-global
ghengeveld Jan 8, 2026
99f1de7
Write changelog for 10.2.0-alpha.13 [skip ci]
storybook-bot Jan 8, 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
5 changes: 5 additions & 0 deletions CHANGELOG.prerelease.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 10.2.0-alpha.13

- Core: Fix onboarding visual bugs, survey telemetry and modal dismissal - [#33326](https://github.com/storybookjs/storybook/pull/33326), thanks @ghengeveld!
- Core: Track vision simulator state through globals and apply styles in preview - [#33418](https://github.com/storybookjs/storybook/pull/33418), thanks @ghengeveld!

## 10.2.0-alpha.12

- Addon-docs: Add MDX manifest generation - [#33408](https://github.com/storybookjs/storybook/pull/33408), thanks @copilot-swe-agent!
Expand Down
1 change: 1 addition & 0 deletions code/addons/a11y/src/components/A11YPanel.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// @vitest-environment happy-dom
/// <reference types="@testing-library/jest-dom" />
import { fireEvent, render } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';

Expand Down
57 changes: 0 additions & 57 deletions code/addons/a11y/src/components/ColorFilters.tsx

This file was deleted.

57 changes: 57 additions & 0 deletions code/addons/a11y/src/components/VisionSimulator.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import type { PlayFunction, PlayFunctionContext } from 'storybook/internal/types';

import { ManagerContext } from 'storybook/manager-api';
import { expect, fn, screen } from 'storybook/test';

import preview from '../../../../.storybook/preview';
import { VisionSimulator } from './VisionSimulator';

const managerContext: any = {
state: {},
api: {
getGlobals: fn(() => ({ vision: undefined })),
updateGlobals: fn(),
getStoryGlobals: fn(() => ({ vision: undefined })),
getUserGlobals: fn(() => ({ vision: undefined })),
},
};

const meta = preview.meta({
title: 'Vision Simulator',
component: VisionSimulator,
decorators: [
(Story: any) => (
<ManagerContext.Provider value={managerContext}>
<Story />
</ManagerContext.Provider>
),
],
});

export default meta;

const openMenu: PlayFunction = async ({ canvas, userEvent }) => {
await userEvent.click(canvas.getByRole('button', { name: 'Vision simulator' }));
};

export const Default = meta.story({
play: openMenu,
});

export const WithFilter = meta.story({
play: openMenu,
globals: {
vision: 'achromatopsia',
},
});

export const Selection = meta.story({
play: async (context) => {
await openMenu(context);
await context.userEvent.click(await screen.findByText('Blurred vision'));
await expect(managerContext.api.updateGlobals).toHaveBeenCalledWith({ vision: 'blurred' });
await expect(
context.canvas.getByRole('button', { name: 'Vision simulator Blurred vision' })
).toBeVisible();
},
});
80 changes: 0 additions & 80 deletions code/addons/a11y/src/components/VisionSimulator.test.tsx

This file was deleted.

81 changes: 18 additions & 63 deletions code/addons/a11y/src/components/VisionSimulator.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,14 @@
import React, { useState } from 'react';
import React from 'react';

import { Select } from 'storybook/internal/components';

import { AccessibilityIcon } from '@storybook/icons';

import { Global, styled } from 'storybook/theming';
import { useGlobals } from 'storybook/manager-api';
import { styled } from 'storybook/theming';

import { Filters } from './ColorFilters';

const iframeId = 'storybook-preview-iframe';

interface Option {
name: string;
percentage?: number;
}

export const baseList = [
{ name: 'blurred vision', percentage: 22.9 },
{ name: 'deuteranomaly', percentage: 2.7 },
{ name: 'deuteranopia', percentage: 0.56 },
{ name: 'protanomaly', percentage: 0.66 },
{ name: 'protanopia', percentage: 0.59 },
{ name: 'tritanomaly', percentage: 0.01 },
{ name: 'tritanopia', percentage: 0.016 },
{ name: 'achromatopsia', percentage: 0.0001 },
{ name: 'grayscale' },
] as Option[];

type Filter = Option | null;

const getFilter = (filterName: string) => {
if (!filterName) {
return 'none';
}
if (filterName === 'blurred vision') {
return 'blur(2px)';
}
if (filterName === 'grayscale') {
return 'grayscale(100%)';
}
return `url('#${filterName}')`;
};
import { VISION_GLOBAL_KEY } from '../constants';
import { filterDefs, filters } from '../visionSimulatorFilters';

const Hidden = styled.div({
'&, & svg': {
Expand All @@ -59,49 +27,36 @@ const ColorIcon = styled.span<{ $filter: string }>(
width: '1rem',
},
({ $filter }) => ({
filter: getFilter($filter),
filter: filters[$filter as keyof typeof filters].filter || 'none',
}),
({ theme }) => ({
boxShadow: `${theme.appBorderColor} 0 0 0 1px inset`,
})
);

export const VisionSimulator = () => {
const [filter, setFilter] = useState<Filter>(null);
const [globals, updateGlobals] = useGlobals();
const value = globals[VISION_GLOBAL_KEY];

const options = baseList.map(({ name, percentage }) => {
const description = percentage !== undefined ? `${percentage}% of users` : undefined;
return {
title: name,
description,
icon: <ColorIcon $filter={name} />,
value: name,
};
});
const options = Object.entries(filters).map(([key, { label, percentage }]) => ({
title: label,
description: percentage ? `${percentage}% of users` : undefined,
icon: <ColorIcon $filter={key} />,
value: key,
}));

return (
<>
{filter && (
<Global
styles={{
[`#${iframeId}`]: {
filter: getFilter(filter.name),
},
}}
/>
)}
<Select
resetLabel="Reset color filter"
onReset={() => setFilter(null)}
onReset={() => updateGlobals({ [VISION_GLOBAL_KEY]: undefined })}
icon={<AccessibilityIcon />}
ariaLabel="Vision simulator"
defaultOptions={filter?.name}
defaultOptions={value}
options={options}
onSelect={(selected) => setFilter(() => ({ name: selected }))}
onSelect={(selected) => updateGlobals({ [VISION_GLOBAL_KEY]: selected })}
/>
<Hidden>
<Filters />
</Hidden>
<Hidden dangerouslySetInnerHTML={{ __html: filterDefs }} />
</>
);
};
1 change: 1 addition & 0 deletions code/addons/a11y/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export const ADDON_ID = 'storybook/a11y';
export const PANEL_ID = `${ADDON_ID}/panel`;
export const PARAM_KEY = `a11y`;
export const VISION_GLOBAL_KEY = `vision`;
export const UI_STATE_ID = `${ADDON_ID}/ui`;
const RESULT = `${ADDON_ID}/result`;
const REQUEST = `${ADDON_ID}/request`;
Expand Down
4 changes: 4 additions & 0 deletions code/addons/a11y/src/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import { expect } from 'storybook/test';
import { run } from './a11yRunner';
import type { A11yParameters } from './params';
import { getIsVitestStandaloneRun } from './utils';
import { withVisionSimulator } from './withVisionSimulator';

let vitestMatchersExtended = false;

export const decorators = [withVisionSimulator];

export const afterEach: AfterEach<any> = async ({
id: storyId,
reporting,
Expand Down Expand Up @@ -94,6 +97,7 @@ export const initialGlobals = {
a11y: {
manual: false,
},
vision: undefined,
};

export const parameters = {
Expand Down
Loading
Loading