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

Addon Test: Use ProgressSpinner for stop button in Testing Module #29997

Merged
merged 3 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
18 changes: 14 additions & 4 deletions code/addons/test/src/components/TestProviderRender.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { type ComponentProps, type FC, useCallback, useMemo, useRef, useState } from 'react';

import { Button, ListItem } from 'storybook/internal/components';
import { Button, ListItem, ProgressSpinner } from 'storybook/internal/components';
import {
TESTING_MODULE_CONFIG_CHANGE,
type TestProviderConfig,
Expand All @@ -17,7 +17,7 @@ import {
PlayHollowIcon,
PointerHandIcon,
ShieldIcon,
StopAltHollowIcon,
StopAltIcon,
} from '@storybook/icons';

import { isEqual } from 'es-toolkit';
Expand Down Expand Up @@ -66,6 +66,14 @@ const Checkbox = styled.input({
},
});

const Progress = styled(ProgressSpinner)({
margin: 2,
});
ghengeveld marked this conversation as resolved.
Show resolved Hide resolved

const StopIcon = styled(StopAltIcon)({
width: 10,
});

const statusOrder: TestStatus[] = ['failed', 'warning', 'pending', 'passed', 'skipped'];
const statusMap: Record<TestStatus, ComponentProps<typeof TestStatusIcon>['status']> = {
failed: 'negative',
Expand Down Expand Up @@ -182,11 +190,13 @@ export const TestProviderRender: FC<
<Button
aria-label={`Stop ${state.name}`}
variant="ghost"
padding="small"
padding="none"
ghengeveld marked this conversation as resolved.
Show resolved Hide resolved
onClick={() => api.cancelTestProvider(state.id)}
disabled={state.cancelling}
>
<StopAltHollowIcon />
<Progress percentage={state.progress?.percentageCompleted}>
<StopIcon />
</Progress>
</Button>
) : (
<Button
Expand Down
3 changes: 3 additions & 0 deletions code/addons/test/src/node/reporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ export class StorybookReporter implements Reporter {
numTotalTests,
startedAt: this.start,
finishedAt,
percentageCompleted: finishedAt
? 100
: ((numPassedTests + numFailedTests) / numTotalTests) * 100,
ghengeveld marked this conversation as resolved.
Show resolved Hide resolved
} as TestingModuleProgressReportProgress,
details: {
testResults,
Expand Down
8 changes: 4 additions & 4 deletions code/core/src/components/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { darken, lighten, rgba, transparentize } from 'polished';
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
asChild?: boolean;
size?: 'small' | 'medium';
padding?: 'small' | 'medium';
padding?: 'small' | 'medium' | 'none';
variant?: 'outline' | 'solid' | 'ghost';
onClick?: (event: SyntheticEvent) => void;
disabled?: boolean;
Expand Down Expand Up @@ -159,18 +159,18 @@ const StyledButton = styled('button', {
justifyContent: 'center',
overflow: 'hidden',
padding: (() => {
ghengeveld marked this conversation as resolved.
Show resolved Hide resolved
if (padding === 'none') {
return 0;
}
if (padding === 'small' && size === 'small') {
return '0 7px';
}

if (padding === 'small' && size === 'medium') {
return '0 9px';
}

if (size === 'small') {
return '0 10px';
}

if (size === 'medium') {
return '0 12px';
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from 'react';

import { StopAltIcon } from '@storybook/icons';
import type { Meta, StoryObj } from '@storybook/react';

import { ProgressSpinner } from './ProgressSpinner';

const meta = {
component: ProgressSpinner,
} satisfies Meta<typeof ProgressSpinner>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Forty: Story = {
args: {
percentage: 40,
},
};

export const Seventy: Story = {
args: {
percentage: 70,
},
};

export const Zero: Story = {
args: {
percentage: 0,
},
};

export const Small: Story = {
args: {
percentage: 40,
size: 16,
},
};

export const Thick: Story = {
args: {
percentage: 40,
width: 3,
},
};

export const Paused: Story = {
args: {
percentage: 40,
running: false,
},
};

export const Colored: Story = {
args: Forty.args,
decorators: [
(Story) => (
<div style={{ color: 'hotpink' }}>
<Story />
</div>
),
],
};

export const WithContent: Story = {
...Colored,
args: {
...Colored.args,
children: <StopAltIcon size={10} />,
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import React, { type ComponentProps } from 'react';

import { keyframes, styled } from '@storybook/core/theming';

const XMLNS = 'http://www.w3.org/2000/svg';

const rotate = keyframes({
'0%': {
transform: 'rotate(0deg)',
},
'100%': {
transform: 'rotate(360deg)',
},
});

const Wrapper = styled.div<{ size: number }>(({ size }) => ({
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
position: 'relative',
minWidth: size,
minHeight: size,
}));

const Circle = styled.svg<{ size: number; width: number; progress?: boolean; spinner?: boolean }>(
({ size, width }) => ({
position: 'absolute',
width: `${size}px!important`,
height: `${size}px!important`,
ghengeveld marked this conversation as resolved.
Show resolved Hide resolved
transform: 'rotate(-90deg)',
circle: {
r: (size - Math.ceil(width)) / 2,
cx: size / 2,
cy: size / 2,
opacity: 0.15,
fill: 'transparent',
stroke: 'currentColor',
strokeWidth: width,
strokeLinecap: 'round',
strokeDasharray: Math.PI * (size - Math.ceil(width)),
},
}),
({ progress }) =>
progress && {
circle: {
opacity: 0.75,
},
},
({ spinner }) =>
spinner && {
animation: `${rotate} 1s linear infinite`,
circle: {
opacity: 0.25,
},
}
);

interface ProgressSpinnerProps extends Omit<ComponentProps<typeof Wrapper>, 'size'> {
percentage?: number;
running?: boolean;
size?: number;
width?: number;
children?: React.ReactNode;
}

export const ProgressSpinner = ({
percentage = undefined,
running = true,
size = 24,
width = 1.5,
children = null,
...props
}: ProgressSpinnerProps) =>
typeof percentage === 'number' ? (
ghengeveld marked this conversation as resolved.
Show resolved Hide resolved
<Wrapper size={size} {...props}>
{children}
<Circle size={size} width={width} xmlns={XMLNS}>
<circle />
</Circle>
{running && (
<Circle size={size} width={width} xmlns={XMLNS} spinner>
<circle strokeDashoffset={Math.PI * (size - 2) * (1 - percentage / 100)} />
ghengeveld marked this conversation as resolved.
Show resolved Hide resolved
</Circle>
)}
<Circle size={size} width={width} xmlns={XMLNS} progress>
<circle strokeDashoffset={Math.PI * (size - 2) * (1 - percentage / 100)} />
</Circle>
</Wrapper>
) : (
<Wrapper size={size} {...props}>
{children}
</Wrapper>
);
1 change: 1 addition & 0 deletions code/core/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export { StorybookIcon } from './brand/StorybookIcon';

// Loader
export { Loader } from './components/Loader/Loader';
export { ProgressSpinner } from './components/ProgressSpinner/ProgressSpinner';

// Utils
export { getStoryHref } from './components/utils/getStoryHref';
Expand Down
23 changes: 19 additions & 4 deletions code/core/src/manager/components/sidebar/LegacyRender.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from 'react';

import { Button } from '@storybook/core/components';
import { Button, ProgressSpinner } from '@storybook/core/components';
import { styled } from '@storybook/core/theming';
import { EyeIcon, PlayHollowIcon, StopAltHollowIcon } from '@storybook/icons';
import { EyeIcon, PlayHollowIcon, StopAltIcon } from '@storybook/icons';

import type { TestProviders } from '@storybook/core/core-events';
import { useStorybookApi } from '@storybook/core/manager-api';
Expand Down Expand Up @@ -35,6 +35,14 @@ const DescriptionWrapper = styled.div(({ theme }) => ({
color: theme.barTextColor,
}));

const Progress = styled(ProgressSpinner)({
margin: 2,
});

const StopIcon = styled(StopAltIcon)({
width: 10,
});

export const LegacyRender = ({ ...state }: TestProviders[keyof TestProviders]) => {
const Description = state.description!;
const Title = state.title!;
Expand Down Expand Up @@ -70,11 +78,18 @@ export const LegacyRender = ({ ...state }: TestProviders[keyof TestProviders]) =
<Button
aria-label={`Stop ${name}`}
variant="ghost"
padding="small"
padding="none"
onClick={() => api.cancelTestProvider(state.id)}
disabled={state.cancelling}
>
<StopAltHollowIcon />
<Progress
percentage={
state.progress?.percentageCompleted ??
(state.details as any)?.buildProgressPercentage
}
ghengeveld marked this conversation as resolved.
Show resolved Hide resolved
>
<StopIcon />
</Progress>
</Button>
) : (
<Button
Expand Down
3 changes: 3 additions & 0 deletions code/core/src/manager/globals/exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ export default {
'P',
'Placeholder',
'Pre',
'ProgressSpinner',
'ResetWrapper',
'ScrollArea',
'Separator',
Expand Down Expand Up @@ -404,6 +405,7 @@ export default {
'P',
'Placeholder',
'Pre',
'ProgressSpinner',
'ResetWrapper',
'ScrollArea',
'Separator',
Expand Down Expand Up @@ -474,6 +476,7 @@ export default {
'P',
'Placeholder',
'Pre',
'ProgressSpinner',
'ResetWrapper',
'ScrollArea',
'Separator',
Expand Down
Loading