Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release: Prerelease 8.5.0-alpha.12 #29725

Merged
merged 37 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
1bf3b54
fix(addon-toolbars): suppress deprecation warning
ValeraS Nov 5, 2024
5f21921
refactor: Use better sanitisation for the intersect util
Sidnioulz Nov 20, 2024
103c6b2
Manager: Add tags property to GroupEntry objects
Sidnioulz Nov 20, 2024
247e65c
refactor: Further improve sanitisation in intersect
Sidnioulz Nov 20, 2024
f69b7f3
Extract test provider render method to a component
ghengeveld Nov 22, 2024
049cec1
Draw horizontal line between each test provider
ghengeveld Nov 22, 2024
f0720db
Merge branch 'next' into fix/addon-toolbars
yannbf Nov 25, 2024
c1a3bf2
Implement settings UI and merge branch 'norbert/testmodule-options' i…
ghengeveld Nov 25, 2024
9efed90
Glow testing module when changing settings
ghengeveld Nov 25, 2024
2a2c8f2
Manager: Generalise tag intersection to root entries and ensure all e…
Sidnioulz Nov 26, 2024
a71d901
Add story for updated state
ghengeveld Nov 26, 2024
dba14dc
Fix cursor on checkbox
ghengeveld Nov 26, 2024
40cce2b
Refactor useConfig to debounce update synchronization
ghengeveld Nov 26, 2024
d9cb32f
Merge branch 'norbert/testmodule-options' into testing-module-settings
ghengeveld Nov 26, 2024
8789ac4
Fix story
ghengeveld Nov 26, 2024
cd203fa
Use proper type for timeouts
ghengeveld Nov 26, 2024
c4071cf
Show 'Settings updated' when changing config, and don't update config…
ghengeveld Nov 26, 2024
84b6a1d
Fix stories
ghengeveld Nov 26, 2024
155741e
Add aria labels
ghengeveld Nov 26, 2024
2f3ec95
Disable coverage and a11y checkboxes for now
ghengeveld Nov 26, 2024
1fd5f55
Merge branch 'next' into sidnioulz/group-entry-tags-intersection
shilman Nov 26, 2024
f0f2a4c
Fix check
shilman Nov 26, 2024
73e55e0
Merge branch 'next' into testing-module-settings
ghengeveld Nov 27, 2024
9954cd1
Collapsed by default
ghengeveld Nov 27, 2024
e2d2e53
Don't show updated state when mounting
ghengeveld Nov 27, 2024
a74f511
Only apply hover styling to labels that contain an enabled input
ghengeveld Nov 27, 2024
e68fd8b
Fix E2E tests for Testing Module
ghengeveld Nov 27, 2024
d77b321
Merge branch 'next' into testing-module-settings
ndelangen Nov 27, 2024
f80d23a
Merge pull request #29545 from ValeraS/fix/addon-toolbars
yannbf Nov 27, 2024
3a0f922
Merge branch 'next-release' into next
storybook-bot Nov 27, 2024
8496e3e
Merge branch 'next' into sidnioulz/group-entry-tags-intersection
shilman Nov 27, 2024
adb2594
Merge pull request #29672 from Sidnioulz/sidnioulz/group-entry-tags-i…
shilman Nov 28, 2024
4d08f6d
Merge pull request #29708 from storybookjs/testing-module-settings
ghengeveld Nov 28, 2024
37a0a19
support @sveltejs/vite-plugin-svelte@5
JReinhold Nov 28, 2024
ce85320
add safeguard
yannbf Nov 28, 2024
e870c8c
Merge pull request #29731 from storybookjs/jeppe/support-svelte-vite-…
yannbf Nov 28, 2024
69de98c
Write changelog for 8.5.0-alpha.12 [skip ci]
storybook-bot Nov 28, 2024
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
7 changes: 7 additions & 0 deletions CHANGELOG.prerelease.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 8.5.0-alpha.12

- Core / Addon Test: Add config UI to Testing Module - [#29708](https://github.com/storybookjs/storybook/pull/29708), thanks @ghengeveld!
- Manager: Add tags property to GroupEntry objects - [#29672](https://github.com/storybookjs/storybook/pull/29672), thanks @Sidnioulz!
- Svelte: Support `@sveltejs/vite-plugin-svelte` v5 - [#29731](https://github.com/storybookjs/storybook/pull/29731), thanks @JReinhold!
- Toolbars: Suppress deprecation warning when using dynamic icons - [#29545](https://github.com/storybookjs/storybook/pull/29545), thanks @ValeraS!

## 8.5.0-alpha.11

- Core + Addon Test: Refactor test API and fix total test count - [#29656](https://github.com/storybookjs/storybook/pull/29656), thanks @ghengeveld!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function GuidedTour({
const theme = useTheme();

useEffect(() => {
let timeout: NodeJS.Timeout;
let timeout: ReturnType<typeof setTimeout>;
setStepIndex((current) => {
const index = steps.findIndex(({ key }) => key === step);

Expand Down
25 changes: 22 additions & 3 deletions code/addons/test/src/components/Description.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useEffect } from 'react';

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

const PositiveText = styled.span(({ theme }) => ({
color: theme.color.positiveText,
}));

export function Description({
errorMessage,
setIsModalOpen,
Expand All @@ -20,9 +24,24 @@ export function Description({
errorMessage: string;
setIsModalOpen: React.Dispatch<React.SetStateAction<boolean>>;
}) {
let description: string | React.ReactNode = 'Not run';
const isMounted = React.useRef(false);
const [isUpdated, setUpdated] = React.useState(false);

if (state.running) {
useEffect(() => {
if (isMounted.current) {
setUpdated(true);
const timeout = setTimeout(setUpdated, 2000, false);
return () => {
clearTimeout(timeout);
};
}
isMounted.current = true;
}, [state.config]);

let description: string | React.ReactNode = 'Not run';
if (isUpdated) {
description = <PositiveText>Settings updated</PositiveText>;
} else if (state.running) {
description = state.progress
? `Testing... ${state.progress.numPassedTests}/${state.progress.numTotalTests}`
: 'Starting...';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ const managerContext: any = {
},
},
api: {
getDocsUrl: fn().mockName('api::getDocsUrl'),
getDocsUrl: fn(({ subpath }) => `https://storybook.js.org/docs/${subpath}`).mockName(
'api::getDocsUrl'
),
emit: fn().mockName('api::emit'),
updateTestProviderState: fn().mockName('api::updateTestProviderState'),
},
Expand Down Expand Up @@ -98,6 +100,9 @@ export default {
</ManagerContext.Provider>
),
],
parameters: {
layout: 'fullscreen',
},
} as Meta<typeof TestProviderRender>;

export const Default: Story = {
Expand Down Expand Up @@ -153,6 +158,6 @@ export const EnableEditing: Story = {
play: async ({ canvasElement }) => {
const screen = within(canvasElement);

screen.getByLabelText('Edit').click();
screen.getByLabelText(/Open settings/).click();
},
};
185 changes: 123 additions & 62 deletions code/addons/test/src/components/TestProviderRender.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,43 @@
import React, { type FC, Fragment, useCallback, useRef, useState } from 'react';
import React, { type FC, useCallback, useRef, useState } from 'react';

import { Button } from 'storybook/internal/components';
import { Button, ListItem } from 'storybook/internal/components';
import {
TESTING_MODULE_CONFIG_CHANGE,
type TestProviderConfig,
type TestProviderState,
type TestingModuleConfigChangePayload,
} from 'storybook/internal/core-events';
import type { API } from 'storybook/internal/manager-api';
import { styled } from 'storybook/internal/theming';
import { styled, useTheme } from 'storybook/internal/theming';

import { EditIcon, EyeIcon, PlayHollowIcon, StopAltHollowIcon } from '@storybook/icons';
import {
AccessibilityIcon,
EditIcon,
EyeIcon,
PlayHollowIcon,
PointerHandIcon,
ShieldIcon,
StopAltHollowIcon,
} from '@storybook/icons';

import { isEqual } from 'es-toolkit';
import { debounce } from 'es-toolkit/compat';

import { type Config, type Details, TEST_PROVIDER_ID } from '../constants';
import { Description } from './Description';
import { GlobalErrorModal } from './GlobalErrorModal';
import { TestStatusIcon } from './TestStatusIcon';

const Container = styled.div({
display: 'flex',
flexDirection: 'column',
});

const Heading = styled.div({
display: 'flex',
justifyContent: 'space-between',
padding: '8px 2px',
gap: 6,
});

const Info = styled.div({
display: 'flex',
Expand All @@ -33,32 +56,37 @@ const Actions = styled.div({
gap: 6,
});

const Head = styled.div({
display: 'flex',
justifyContent: 'space-between',
gap: 6,
const Extras = styled.div({
marginBottom: 2,
});

const Checkbox = styled.input({
margin: 0,
'&:enabled': {
cursor: 'pointer',
},
});

export const TestProviderRender: FC<{
api: API;
state: TestProviderConfig & TestProviderState<Details, Config>;
}> = ({ state, api }) => {
const [isEditing, setIsEditing] = useState(false);
const [isModalOpen, setIsModalOpen] = useState(false);
const theme = useTheme();

const title = state.crashed || state.failed ? 'Component tests failed' : 'Component tests';
const title = state.crashed || state.failed ? 'Local tests failed' : 'Run local tests';
const errorMessage = state.error?.message;

const [config, changeConfig] = useConfig(
const [config, updateConfig] = useConfig(
api,
state.id,
state.config || { a11y: false, coverage: false },
api
state.config || { a11y: false, coverage: false }
);

const [isEditing, setIsEditing] = useState(false);

return (
<Fragment>
<Head>
<Container>
<Heading>
<Info>
<Title crashed={state.crashed} id="testing-module-title">
{title}
Expand All @@ -68,11 +96,11 @@ export const TestProviderRender: FC<{

<Actions>
<Button
aria-label={`Edit`}
aria-label={`${isEditing ? 'Close' : 'Open'} settings for ${state.name}`}
variant="ghost"
padding="small"
active={isEditing}
onClick={() => setIsEditing((v) => !v)}
onClick={() => setIsEditing(!isEditing)}
>
<EditIcon />
</Button>
Expand Down Expand Up @@ -105,7 +133,7 @@ export const TestProviderRender: FC<{
aria-label={`Start ${state.name}`}
variant="ghost"
padding="small"
onClick={() => api.runTestProvider(state.id, {})}
onClick={() => api.runTestProvider(state.id)}
disabled={state.crashed || state.running}
>
<PlayHollowIcon />
Expand All @@ -114,29 +142,60 @@ export const TestProviderRender: FC<{
</>
)}
</Actions>
</Head>

{!isEditing ? (
<Fragment>
{Object.entries(config).map(([key, value]) => (
<div key={key}>
{key}: {value ? 'ON' : 'OFF'}
</div>
))}
</Fragment>
</Heading>

{isEditing ? (
<Extras>
<ListItem
as="label"
title="Component tests"
icon={<PointerHandIcon color={theme.textMutedColor} />}
right={<Checkbox type="checkbox" checked disabled />}
/>
<ListItem
as="label"
title="Coverage"
icon={<ShieldIcon color={theme.textMutedColor} />}
right={
<Checkbox
type="checkbox"
disabled // TODO: Implement coverage
checked={config.coverage}
onChange={() => updateConfig({ coverage: !config.coverage })}
/>
}
/>
<ListItem
as="label"
title="Accessibility"
icon={<AccessibilityIcon color={theme.textMutedColor} />}
right={
<Checkbox
type="checkbox"
disabled // TODO: Implement a11y
checked={config.a11y}
onChange={() => updateConfig({ a11y: !config.a11y })}
/>
}
/>
</Extras>
) : (
<Fragment>
{Object.entries(config).map(([key, value]) => (
<div
key={key}
onClick={() => {
changeConfig({ [key]: !value });
}}
>
{key}: {value ? 'ON' : 'OFF'}
</div>
))}
</Fragment>
<Extras>
<ListItem
title="Component tests"
icon={<TestStatusIcon status="positive" aria-label="status: passed" />}
/>
<ListItem
title="Coverage"
icon={<TestStatusIcon percentage={60} status="warning" aria-label="status: warning" />}
right={`60%`}
/>
<ListItem
title="Accessibility"
icon={<TestStatusIcon status="negative" aria-label="status: failed" />}
right={73}
/>
</Extras>
)}

<GlobalErrorModal
Expand All @@ -150,33 +209,35 @@ export const TestProviderRender: FC<{
api.runTestProvider(TEST_PROVIDER_ID);
}}
/>
</Fragment>
</Container>
);
};

function useConfig(id: string, config: Config, api: API) {
const data = useRef<Config>(config);
data.current = config || {
a11y: false,
coverage: false,
};
function useConfig(api: API, providerId: string, initialConfig: Config) {
const [currentConfig, setConfig] = useState<Config>(initialConfig);
const lastConfig = useRef(initialConfig);

const saveConfig = useCallback(
debounce((config: Config) => {
if (!isEqual(config, lastConfig.current)) {
api.updateTestProviderState(providerId, { config });
api.emit(TESTING_MODULE_CONFIG_CHANGE, { providerId, config });
lastConfig.current = config;
}
}, 500),
[api, providerId]
);

const changeConfig = useCallback(
const updateConfig = useCallback(
(update: Partial<Config>) => {
const newConfig = {
...data.current,
...update,
};
api.updateTestProviderState(id, {
config: newConfig,
setConfig((value) => {
const updated = { ...value, ...update };
saveConfig(updated);
return updated;
});
api.emit(TESTING_MODULE_CONFIG_CHANGE, {
providerId: id,
config: newConfig,
} as TestingModuleConfigChangePayload);
},
[api, id]
[saveConfig]
);

return [data.current, changeConfig] as const;
return [currentConfig, updateConfig] as const;
}
Loading