Skip to content

Commit 021d6bf

Browse files
authored
Merge pull request #29725 from storybookjs/version-non-patch-from-8.5.0-alpha.11
Release: Prerelease 8.5.0-alpha.12
2 parents da1f1a0 + 69de98c commit 021d6bf

File tree

27 files changed

+520
-190
lines changed

27 files changed

+520
-190
lines changed

CHANGELOG.prerelease.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 8.5.0-alpha.12
2+
3+
- Core / Addon Test: Add config UI to Testing Module - [#29708](https://github.com/storybookjs/storybook/pull/29708), thanks @ghengeveld!
4+
- Manager: Add tags property to GroupEntry objects - [#29672](https://github.com/storybookjs/storybook/pull/29672), thanks @Sidnioulz!
5+
- Svelte: Support `@sveltejs/vite-plugin-svelte` v5 - [#29731](https://github.com/storybookjs/storybook/pull/29731), thanks @JReinhold!
6+
- Toolbars: Suppress deprecation warning when using dynamic icons - [#29545](https://github.com/storybookjs/storybook/pull/29545), thanks @ValeraS!
7+
18
## 8.5.0-alpha.11
29

310
- Core + Addon Test: Refactor test API and fix total test count - [#29656](https://github.com/storybookjs/storybook/pull/29656), thanks @ghengeveld!

code/addons/onboarding/src/features/GuidedTour/GuidedTour.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export function GuidedTour({
2323
const theme = useTheme();
2424

2525
useEffect(() => {
26-
let timeout: NodeJS.Timeout;
26+
let timeout: ReturnType<typeof setTimeout>;
2727
setStepIndex((current) => {
2828
const index = steps.findIndex(({ key }) => key === step);
2929

code/addons/test/src/components/Description.tsx

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { useEffect } from 'react';
22

33
import { Link as LinkComponent } from 'storybook/internal/components';
44
import { type TestProviderConfig, type TestProviderState } from 'storybook/internal/core-events';
@@ -11,6 +11,10 @@ export const DescriptionStyle = styled.div(({ theme }) => ({
1111
color: theme.barTextColor,
1212
}));
1313

14+
const PositiveText = styled.span(({ theme }) => ({
15+
color: theme.color.positiveText,
16+
}));
17+
1418
export function Description({
1519
errorMessage,
1620
setIsModalOpen,
@@ -20,9 +24,24 @@ export function Description({
2024
errorMessage: string;
2125
setIsModalOpen: React.Dispatch<React.SetStateAction<boolean>>;
2226
}) {
23-
let description: string | React.ReactNode = 'Not run';
27+
const isMounted = React.useRef(false);
28+
const [isUpdated, setUpdated] = React.useState(false);
2429

25-
if (state.running) {
30+
useEffect(() => {
31+
if (isMounted.current) {
32+
setUpdated(true);
33+
const timeout = setTimeout(setUpdated, 2000, false);
34+
return () => {
35+
clearTimeout(timeout);
36+
};
37+
}
38+
isMounted.current = true;
39+
}, [state.config]);
40+
41+
let description: string | React.ReactNode = 'Not run';
42+
if (isUpdated) {
43+
description = <PositiveText>Settings updated</PositiveText>;
44+
} else if (state.running) {
2645
description = state.progress
2746
? `Testing... ${state.progress.numPassedTests}/${state.progress.numTotalTests}`
2847
: 'Starting...';

code/addons/test/src/components/TestProviderRender.stories.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ const managerContext: any = {
2323
},
2424
},
2525
api: {
26-
getDocsUrl: fn().mockName('api::getDocsUrl'),
26+
getDocsUrl: fn(({ subpath }) => `https://storybook.js.org/docs/${subpath}`).mockName(
27+
'api::getDocsUrl'
28+
),
2729
emit: fn().mockName('api::emit'),
2830
updateTestProviderState: fn().mockName('api::updateTestProviderState'),
2931
},
@@ -98,6 +100,9 @@ export default {
98100
</ManagerContext.Provider>
99101
),
100102
],
103+
parameters: {
104+
layout: 'fullscreen',
105+
},
101106
} as Meta<typeof TestProviderRender>;
102107

103108
export const Default: Story = {
@@ -153,6 +158,6 @@ export const EnableEditing: Story = {
153158
play: async ({ canvasElement }) => {
154159
const screen = within(canvasElement);
155160

156-
screen.getByLabelText('Edit').click();
161+
screen.getByLabelText(/Open settings/).click();
157162
},
158163
};
Lines changed: 123 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,43 @@
1-
import React, { type FC, Fragment, useCallback, useRef, useState } from 'react';
1+
import React, { type FC, useCallback, useRef, useState } from 'react';
22

3-
import { Button } from 'storybook/internal/components';
3+
import { Button, ListItem } from 'storybook/internal/components';
44
import {
55
TESTING_MODULE_CONFIG_CHANGE,
66
type TestProviderConfig,
77
type TestProviderState,
8-
type TestingModuleConfigChangePayload,
98
} from 'storybook/internal/core-events';
109
import type { API } from 'storybook/internal/manager-api';
11-
import { styled } from 'storybook/internal/theming';
10+
import { styled, useTheme } from 'storybook/internal/theming';
1211

13-
import { EditIcon, EyeIcon, PlayHollowIcon, StopAltHollowIcon } from '@storybook/icons';
12+
import {
13+
AccessibilityIcon,
14+
EditIcon,
15+
EyeIcon,
16+
PlayHollowIcon,
17+
PointerHandIcon,
18+
ShieldIcon,
19+
StopAltHollowIcon,
20+
} from '@storybook/icons';
21+
22+
import { isEqual } from 'es-toolkit';
23+
import { debounce } from 'es-toolkit/compat';
1424

1525
import { type Config, type Details, TEST_PROVIDER_ID } from '../constants';
1626
import { Description } from './Description';
1727
import { GlobalErrorModal } from './GlobalErrorModal';
28+
import { TestStatusIcon } from './TestStatusIcon';
29+
30+
const Container = styled.div({
31+
display: 'flex',
32+
flexDirection: 'column',
33+
});
34+
35+
const Heading = styled.div({
36+
display: 'flex',
37+
justifyContent: 'space-between',
38+
padding: '8px 2px',
39+
gap: 6,
40+
});
1841

1942
const Info = styled.div({
2043
display: 'flex',
@@ -33,32 +56,37 @@ const Actions = styled.div({
3356
gap: 6,
3457
});
3558

36-
const Head = styled.div({
37-
display: 'flex',
38-
justifyContent: 'space-between',
39-
gap: 6,
59+
const Extras = styled.div({
60+
marginBottom: 2,
61+
});
62+
63+
const Checkbox = styled.input({
64+
margin: 0,
65+
'&:enabled': {
66+
cursor: 'pointer',
67+
},
4068
});
4169

4270
export const TestProviderRender: FC<{
4371
api: API;
4472
state: TestProviderConfig & TestProviderState<Details, Config>;
4573
}> = ({ state, api }) => {
74+
const [isEditing, setIsEditing] = useState(false);
4675
const [isModalOpen, setIsModalOpen] = useState(false);
76+
const theme = useTheme();
4777

48-
const title = state.crashed || state.failed ? 'Component tests failed' : 'Component tests';
78+
const title = state.crashed || state.failed ? 'Local tests failed' : 'Run local tests';
4979
const errorMessage = state.error?.message;
5080

51-
const [config, changeConfig] = useConfig(
81+
const [config, updateConfig] = useConfig(
82+
api,
5283
state.id,
53-
state.config || { a11y: false, coverage: false },
54-
api
84+
state.config || { a11y: false, coverage: false }
5585
);
5686

57-
const [isEditing, setIsEditing] = useState(false);
58-
5987
return (
60-
<Fragment>
61-
<Head>
88+
<Container>
89+
<Heading>
6290
<Info>
6391
<Title crashed={state.crashed} id="testing-module-title">
6492
{title}
@@ -68,11 +96,11 @@ export const TestProviderRender: FC<{
6896

6997
<Actions>
7098
<Button
71-
aria-label={`Edit`}
99+
aria-label={`${isEditing ? 'Close' : 'Open'} settings for ${state.name}`}
72100
variant="ghost"
73101
padding="small"
74102
active={isEditing}
75-
onClick={() => setIsEditing((v) => !v)}
103+
onClick={() => setIsEditing(!isEditing)}
76104
>
77105
<EditIcon />
78106
</Button>
@@ -105,7 +133,7 @@ export const TestProviderRender: FC<{
105133
aria-label={`Start ${state.name}`}
106134
variant="ghost"
107135
padding="small"
108-
onClick={() => api.runTestProvider(state.id, {})}
136+
onClick={() => api.runTestProvider(state.id)}
109137
disabled={state.crashed || state.running}
110138
>
111139
<PlayHollowIcon />
@@ -114,29 +142,60 @@ export const TestProviderRender: FC<{
114142
</>
115143
)}
116144
</Actions>
117-
</Head>
118-
119-
{!isEditing ? (
120-
<Fragment>
121-
{Object.entries(config).map(([key, value]) => (
122-
<div key={key}>
123-
{key}: {value ? 'ON' : 'OFF'}
124-
</div>
125-
))}
126-
</Fragment>
145+
</Heading>
146+
147+
{isEditing ? (
148+
<Extras>
149+
<ListItem
150+
as="label"
151+
title="Component tests"
152+
icon={<PointerHandIcon color={theme.textMutedColor} />}
153+
right={<Checkbox type="checkbox" checked disabled />}
154+
/>
155+
<ListItem
156+
as="label"
157+
title="Coverage"
158+
icon={<ShieldIcon color={theme.textMutedColor} />}
159+
right={
160+
<Checkbox
161+
type="checkbox"
162+
disabled // TODO: Implement coverage
163+
checked={config.coverage}
164+
onChange={() => updateConfig({ coverage: !config.coverage })}
165+
/>
166+
}
167+
/>
168+
<ListItem
169+
as="label"
170+
title="Accessibility"
171+
icon={<AccessibilityIcon color={theme.textMutedColor} />}
172+
right={
173+
<Checkbox
174+
type="checkbox"
175+
disabled // TODO: Implement a11y
176+
checked={config.a11y}
177+
onChange={() => updateConfig({ a11y: !config.a11y })}
178+
/>
179+
}
180+
/>
181+
</Extras>
127182
) : (
128-
<Fragment>
129-
{Object.entries(config).map(([key, value]) => (
130-
<div
131-
key={key}
132-
onClick={() => {
133-
changeConfig({ [key]: !value });
134-
}}
135-
>
136-
{key}: {value ? 'ON' : 'OFF'}
137-
</div>
138-
))}
139-
</Fragment>
183+
<Extras>
184+
<ListItem
185+
title="Component tests"
186+
icon={<TestStatusIcon status="positive" aria-label="status: passed" />}
187+
/>
188+
<ListItem
189+
title="Coverage"
190+
icon={<TestStatusIcon percentage={60} status="warning" aria-label="status: warning" />}
191+
right={`60%`}
192+
/>
193+
<ListItem
194+
title="Accessibility"
195+
icon={<TestStatusIcon status="negative" aria-label="status: failed" />}
196+
right={73}
197+
/>
198+
</Extras>
140199
)}
141200

142201
<GlobalErrorModal
@@ -150,33 +209,35 @@ export const TestProviderRender: FC<{
150209
api.runTestProvider(TEST_PROVIDER_ID);
151210
}}
152211
/>
153-
</Fragment>
212+
</Container>
154213
);
155214
};
156215

157-
function useConfig(id: string, config: Config, api: API) {
158-
const data = useRef<Config>(config);
159-
data.current = config || {
160-
a11y: false,
161-
coverage: false,
162-
};
216+
function useConfig(api: API, providerId: string, initialConfig: Config) {
217+
const [currentConfig, setConfig] = useState<Config>(initialConfig);
218+
const lastConfig = useRef(initialConfig);
219+
220+
const saveConfig = useCallback(
221+
debounce((config: Config) => {
222+
if (!isEqual(config, lastConfig.current)) {
223+
api.updateTestProviderState(providerId, { config });
224+
api.emit(TESTING_MODULE_CONFIG_CHANGE, { providerId, config });
225+
lastConfig.current = config;
226+
}
227+
}, 500),
228+
[api, providerId]
229+
);
163230

164-
const changeConfig = useCallback(
231+
const updateConfig = useCallback(
165232
(update: Partial<Config>) => {
166-
const newConfig = {
167-
...data.current,
168-
...update,
169-
};
170-
api.updateTestProviderState(id, {
171-
config: newConfig,
233+
setConfig((value) => {
234+
const updated = { ...value, ...update };
235+
saveConfig(updated);
236+
return updated;
172237
});
173-
api.emit(TESTING_MODULE_CONFIG_CHANGE, {
174-
providerId: id,
175-
config: newConfig,
176-
} as TestingModuleConfigChangePayload);
177238
},
178-
[api, id]
239+
[saveConfig]
179240
);
180241

181-
return [data.current, changeConfig] as const;
242+
return [currentConfig, updateConfig] as const;
182243
}

0 commit comments

Comments
 (0)