add UI in context

This commit is contained in:
Norbert de Langen 2024-11-12 14:55:15 +01:00
parent a2d07fbfbc
commit 82c5cde97e
4 changed files with 81 additions and 33 deletions

View File

@ -1,16 +1,34 @@
import React, { useCallback, useEffect, useState } from 'react';
import React, { type FC, type SyntheticEvent, useCallback, useEffect, useState } from 'react';
import { AddonPanel, Badge, Link as LinkComponent, Spaced } from 'storybook/internal/components';
import {
AddonPanel,
Badge,
Button,
Link as LinkComponent,
type ListItem,
Spaced,
} from 'storybook/internal/components';
import { TESTING_MODULE_RUN_ALL_REQUEST } from 'storybook/internal/core-events';
import type { Combo } from 'storybook/internal/manager-api';
import { Consumer, addons, types, useAddonState } from 'storybook/internal/manager-api';
import {
Consumer,
addons,
types,
useAddonState,
useStorybookApi,
} from 'storybook/internal/manager-api';
import {
type API_HashEntry,
type API_StatusObject,
type API_StatusValue,
type Addon_TestProviderState,
type Addon_TestProviderType,
Addon_TypesEnum,
} from 'storybook/internal/types';
import { PlayIcon } from '@storybook/icons';
import { useTheme } from '@storybook/theming';
import { Panel } from './Panel';
import { GlobalErrorModal } from './components/GlobalErrorModal';
import { ADDON_ID, PANEL_ID, TEST_PROVIDER_ID } from './constants';
@ -79,18 +97,38 @@ const RelativeTime = ({ timestamp, testCount }: { timestamp: Date; testCount: nu
);
};
const COunter = () => {
const [count, setCount] = useState(0);
const ContextMenuItem: FC<{
context: API_HashEntry;
state: Addon_TestProviderState<{
testResults: TestResult[];
}>;
ListItem: typeof ListItem;
}> = ({ context, state, ListItem }) => {
const api = useStorybookApi();
useEffect(() => {
const interval = setInterval(() => {
setCount((prev) => prev + 1);
}, 1000);
const onClick = useCallback(
(event: SyntheticEvent) => {
event.stopPropagation();
// TODO - actually send along a sub-set based on `context` to test.
api.getChannel().emit(TESTING_MODULE_RUN_ALL_REQUEST, { providerId: TEST_PROVIDER_ID });
},
[api]
);
return () => clearInterval(interval);
}, []);
const theme = useTheme();
return <div>{count}</div>;
return (
<ListItem
title={'Component tests'}
right={
<Button variant="ghost" padding="small" disabled={state.crashed || state.running}>
<PlayIcon fill={theme.barTextColor} />
</Button>
}
center={state.running ? 'Running...' : 'Run tests'}
onClick={onClick}
/>
);
};
addons.register(ADDON_ID, (api) => {
@ -107,17 +145,12 @@ addons.register(ADDON_ID, (api) => {
watchable: true,
name: 'Component tests',
contextMenu: ({ context, state }) => {
contextMenu: ({ context, state }, { ListItem }) => {
if (context.type === 'docs') {
return null;
}
return (
<div>
Testing {state?.progress?.percentageCompleted} {state.running ? '!' : '?'}
</div>
);
// return <COunter />;
return <ContextMenuItem context={context} state={state} ListItem={ListItem} />;
},
title: ({ crashed, failed }) =>
crashed || failed ? 'Component tests failed' : 'Component tests',
@ -207,20 +240,20 @@ addons.register(ADDON_ID, (api) => {
}>);
}
const filter = ({ state }: Combo) => {
return {
storyId: state.storyId,
};
};
addons.add(PANEL_ID, {
type: types.PANEL,
title: Title,
match: ({ viewMode }) => viewMode === 'story',
render: ({ active }) => {
const newLocal = useCallback(({ state }: Combo) => {
return {
storyId: state.storyId,
};
}, []);
return (
<AddonPanel active={active}>
<Consumer filter={newLocal}>{({ storyId }) => <Panel storyId={storyId} />}</Consumer>
<Consumer filter={filter}>{({ storyId }) => <Panel storyId={storyId} />}</Consumer>
</AddonPanel>
);
},

View File

@ -15,7 +15,8 @@ const List = styled.div(
},
({ theme }) => ({
borderRadius: theme.appBorderRadius + 2,
})
}),
({ theme }) => (theme.base === 'dark' ? { background: theme.background.content } : {})
);
const Group = styled.div(({ theme }) => ({

View File

@ -1,7 +1,13 @@
import type { ComponentProps, FC, MutableRefObject } from 'react';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { Button, IconButton, TooltipLinkList, WithTooltip } from '@storybook/core/components';
import {
Button,
IconButton,
ListItem,
TooltipLinkList,
WithTooltip,
} from '@storybook/core/components';
import { styled, useTheme } from '@storybook/core/theming';
import {
type API_HashEntry,
@ -176,6 +182,10 @@ const StatusIconMap = {
unknown: null,
};
const ContextMenu = {
ListItem,
};
const statusOrder: API_StatusValue[] = ['success', 'error', 'warn', 'pending', 'unknown'];
const Node = React.memo<NodeProps>(function Node({
@ -746,7 +756,7 @@ function generateTestProviderLinks(testProviders: TestProviders, context: API_Ha
return null;
}
const content = e.contextMenu?.({ context, state });
const content = e.contextMenu?.({ context, state }, ContextMenu);
if (!content) {
return null;

View File

@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/naming-convention */
import type { FC, PropsWithChildren, ReactElement, ReactNode } from 'react';
import type { ListItem } from '../../components';
import type { TestingModuleProgressReportProgress } from '../../core-events';
import type { Addon, StoryEntry } from '../../manager-api';
import type { RenderData as RouterData } from '../../router/types';
@ -476,10 +477,13 @@ export interface Addon_TestProviderType<
name: string;
title: (state: Addon_TestProviderState<Details>) => ReactNode;
description: (state: Addon_TestProviderState<Details>) => ReactNode;
contextMenu?: (options: {
context: API_HashEntry;
state: Addon_TestProviderState<Details>;
}) => ReactNode;
contextMenu?: (
options: {
context: API_HashEntry;
state: Addon_TestProviderState<Details>;
},
components: { ListItem: typeof ListItem }
) => ReactNode;
mapStatusUpdate?: (
state: Addon_TestProviderState<Details>
) => API_StatusUpdate | ((state: API_StatusState) => API_StatusUpdate);