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

UI: Sidebar context menu addon API #29557

Merged
merged 59 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
1abc353
allow custom link in context menu in sidebar, add addon type for inje…
ndelangen Nov 6, 2024
c69c1f3
hoist testProvider state to root, add WIP sidebar contextMenu
ndelangen Nov 8, 2024
716917d
improve UI
ndelangen Nov 12, 2024
a2d07fb
Merge branch 'next' into norbert/addon-api-context-menu
ndelangen Nov 12, 2024
82c5cde
add UI in context
ndelangen Nov 12, 2024
7bfc2ad
cleanup
ndelangen Nov 12, 2024
15e0f2c
cleanup
ndelangen Nov 12, 2024
9faaa59
cleanup
ndelangen Nov 12, 2024
653ff53
fixes
ndelangen Nov 12, 2024
b833d03
Merge branch 'next' into norbert/addon-api-context-menu
ndelangen Nov 12, 2024
d5995fd
Merge branch 'next' into norbert/addon-api-context-menu
ndelangen Nov 13, 2024
9d4eae2
remove contextMenu from test addon until it's ready for primetime
ndelangen Nov 13, 2024
b20b07c
Merge branch 'norbert/addon-api-context-menu' of https://github.com/s…
ndelangen Nov 13, 2024
7ad41a7
improvements
ndelangen Nov 13, 2024
4ea557e
fix build race condition
ndelangen Nov 13, 2024
74972a7
refactor
ndelangen Nov 13, 2024
cab4b71
fixing the core prep script race condition
ndelangen Nov 13, 2024
17b2890
fix for dev-mode
ndelangen Nov 13, 2024
76687a7
move components into components dir
ndelangen Nov 13, 2024
0a0c36b
renames
ndelangen Nov 13, 2024
39be69f
Merge branch 'next' into norbert/addon-api-context-menu
ndelangen Nov 13, 2024
4d8c845
fixes
ndelangen Nov 13, 2024
4d4bc94
fix tooltip not hiding after click in the sidebar gear menu
ndelangen Nov 14, 2024
800de95
Merge branch 'next' into norbert/addon-api-context-menu
ndelangen Nov 14, 2024
d99f0e6
fixes
ndelangen Nov 14, 2024
350cee7
fix initial state
ndelangen Nov 14, 2024
d23899e
Replace test provider title and description with render function
ghengeveld Nov 14, 2024
4582440
Fix story mocks and api call
ghengeveld Nov 14, 2024
6c411d4
fix condition hooks rendering bug
ndelangen Nov 14, 2024
011b854
fix bug with persisting state with function
ndelangen Nov 14, 2024
ad55d61
Merge branch 'norbert/addon-api-context-menu' of https://github.com/s…
ndelangen Nov 14, 2024
1c85387
use correct title component in panel
ndelangen Nov 14, 2024
f8cbd83
fix
ndelangen Nov 14, 2024
a35701d
fixes
ndelangen Nov 14, 2024
166aeb6
fix api method renames
ndelangen Nov 14, 2024
8b63bcb
Merge branch 'next' into norbert/addon-api-context-menu
ndelangen Nov 14, 2024
dfc147a
send specific stories when running from context menu
JReinhold Nov 14, 2024
a9e7905
Merge branch 'norbert/addon-api-context-menu' of github.com:storybook…
JReinhold Nov 14, 2024
e5e4499
set testNamePattern from story names
JReinhold Nov 14, 2024
8147355
update test run message, allow canceling
JReinhold Nov 14, 2024
7e43ecd
use root cancelTestProvider function
JReinhold Nov 14, 2024
c912e0e
do not depend on mouseLeave event, as it's often missed when user qui…
ndelangen Nov 15, 2024
9e841f8
simplify, add comment for clarity
ndelangen Nov 15, 2024
3a2415b
ensure the status icons are visible again, but hidden during hover
ndelangen Nov 15, 2024
5811cdb
fix duplicate keys in stories
JReinhold Nov 18, 2024
7454f3c
properly disable docs context loader in PS
JReinhold Nov 18, 2024
2b254b7
add testNamePattern to vitest mock
JReinhold Nov 18, 2024
ec28fa2
fix sidebarbottom story
ndelangen Nov 18, 2024
d2b09b1
Merge branch 'norbert/addon-api-context-menu' of https://github.com/s…
ndelangen Nov 18, 2024
dbdd73a
move stories into the same index ancestor
ndelangen Nov 18, 2024
2873064
enable addon-test context menu
JReinhold Nov 18, 2024
5101314
Merge branch 'norbert/addon-api-context-menu' of github.com:storybook…
JReinhold Nov 18, 2024
3d70d51
rename testproviders to testProviders & move RelativeTime component
ndelangen Nov 18, 2024
14f3ab4
Merge branch 'norbert/addon-api-context-menu' of https://github.com/s…
ndelangen Nov 18, 2024
adf6180
add interaction component test
ndelangen Nov 18, 2024
ac49cf1
cleanup
JReinhold Nov 19, 2024
fe2cec7
rename contextMenu > sidebarContextMenu in addon API
JReinhold Nov 19, 2024
b6aa053
hide context menu from test-less stories
JReinhold Nov 19, 2024
f771bdf
fix React key warning
JReinhold Nov 19, 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
10 changes: 5 additions & 5 deletions code/.storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ const ThemedSetRoot = () => {
};

// eslint-disable-next-line no-underscore-dangle
const preview = (window as any).__STORYBOOK_PREVIEW__ as PreviewWeb<ReactRenderer>;
const channel = (window as any).__STORYBOOK_ADDONS_CHANNEL__ as Channel;
const preview = (window as any).__STORYBOOK_PREVIEW__ as PreviewWeb<ReactRenderer> | undefined;
const channel = (window as any).__STORYBOOK_ADDONS_CHANNEL__ as Channel | undefined;
export const loaders = [
/**
* This loader adds a DocsContext to the story, which is required for the most Blocks to work. A
Expand All @@ -133,9 +133,9 @@ export const loaders = [
* The DocsContext will then be added via the decorator below.
*/
async ({ parameters: { relativeCsfPaths, attached = true } }) => {
// TODO bring a better way to skip tests when running as part of the vitest plugin instead of __STORYBOOK_URL__
// eslint-disable-next-line no-underscore-dangle
if (!relativeCsfPaths || (import.meta as any).env?.__STORYBOOK_URL__) {
// __STORYBOOK_PREVIEW__ and __STORYBOOK_ADDONS_CHANNEL__ is set in the PreviewWeb constructor
// which isn't loaded in portable stories/vitest
if (!relativeCsfPaths || !preview || !channel) {
return {};
}
const csfFiles = await Promise.all(
Expand Down
101 changes: 101 additions & 0 deletions code/addons/test/src/components/ContextMenuItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React, {
type FC,
type SyntheticEvent,
useCallback,
useEffect,
useRef,
useState,
} from 'react';

import { Button, type ListItem } from 'storybook/internal/components';
import { useStorybookApi } from 'storybook/internal/manager-api';
import { useTheme } from 'storybook/internal/theming';
import { type API_HashEntry, type Addon_TestProviderState } from 'storybook/internal/types';

import { PlayHollowIcon, StopAltHollowIcon } from '@storybook/icons';

import { TEST_PROVIDER_ID } from '../constants';
import type { TestResult } from '../node/reporter';
import { RelativeTime } from './RelativeTime';

export const ContextMenuItem: FC<{
context: API_HashEntry;
state: Addon_TestProviderState<{
testResults: TestResult[];
}>;
ListItem: typeof ListItem;
}> = ({ context, state, ListItem }) => {
const api = useStorybookApi();
const [isDisabled, setDisabled] = useState(false);

const id = useRef(context.id);
id.current = context.id;

const Icon = state.running ? StopAltHollowIcon : PlayHollowIcon;

useEffect(() => {
setDisabled(false);
}, [state.running]);

const onClick = useCallback(
(event: SyntheticEvent) => {
setDisabled(true);
event.stopPropagation();
if (state.running) {
api.cancelTestProvider(TEST_PROVIDER_ID);
} else {
api.runTestProvider(TEST_PROVIDER_ID, { entryId: id.current });
}
},
[api, state.running]
);

const theme = useTheme();

const title = state.crashed || state.failed ? 'Component tests failed' : 'Component tests';
const errorMessage = state.error?.message;
let description: string | React.ReactNode = 'Not run';

if (state.running) {
description = state.progress
? `Testing... ${state.progress.numPassedTests}/${state.progress.numTotalTests}`
: 'Starting...';
} else if (state.failed && !errorMessage) {
description = '';
} else if (state.crashed || (state.failed && errorMessage)) {
description = 'An error occured';
} else if (state.progress?.finishedAt) {
description = (
<RelativeTime
timestamp={new Date(state.progress.finishedAt)}
testCount={state.progress.numTotalTests}
/>
);
} else if (state.watching) {
description = 'Watching for file changes';
}

return (
<div
onClick={(event) => {
// stopPropagation to prevent the parent from closing the context menu, which is the default behavior onClick
event.stopPropagation();
}}
>
<ListItem
title={title}
center={description}
right={
<Button
onClick={onClick}
variant="ghost"
padding="small"
disabled={state.crashed || isDisabled}
>
<Icon fill={theme.barTextColor} />
</Button>
}
/>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import { global } from '@storybook/global';
import { type Call, CallStates, EVENTS, type LogItem } from '@storybook/instrumenter';
import type { API_StatusValue } from '@storybook/types';

import { InteractionsPanel } from './components/InteractionsPanel';
import { ADDON_ID, TEST_PROVIDER_ID } from './constants';
import { ADDON_ID, TEST_PROVIDER_ID } from '../constants';
import { InteractionsPanel } from './InteractionsPanel';

interface Interaction extends Call {
status: Call['status'];
Expand Down
23 changes: 23 additions & 0 deletions code/addons/test/src/components/PanelTitle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';

import { Badge, Spaced } from 'storybook/internal/components';
import { useAddonState } from 'storybook/internal/manager-api';

import { ADDON_ID } from '../constants';

export function PanelTitle() {
const [addonState = {}] = useAddonState(ADDON_ID);
const { hasException, interactionsCount } = addonState as any;

return (
<div>
<Spaced col={1}>
<span style={{ display: 'inline-block', verticalAlign: 'middle' }}>Component tests</span>
{interactionsCount && !hasException ? (
<Badge status="neutral">{interactionsCount}</Badge>
) : null}
{hasException ? <Badge status="negative">{interactionsCount}</Badge> : null}
</Spaced>
</div>
);
}
24 changes: 24 additions & 0 deletions code/addons/test/src/components/RelativeTime.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useEffect, useState } from 'react';

import { getRelativeTimeString } from '../manager';

export const RelativeTime = ({ timestamp, testCount }: { timestamp: Date; testCount: number }) => {
const [relativeTimeString, setRelativeTimeString] = useState(null);

useEffect(() => {
if (timestamp) {
setRelativeTimeString(getRelativeTimeString(timestamp).replace(/^now$/, 'just now'));

const interval = setInterval(() => {
setRelativeTimeString(getRelativeTimeString(timestamp).replace(/^now$/, 'just now'));
}, 10000);

return () => clearInterval(interval);
}
}, [timestamp]);

return (
relativeTimeString &&
`Ran ${testCount} ${testCount === 1 ? 'test' : 'tests'} ${relativeTimeString}`
);
};
Loading