Skip to content

Commit

Permalink
improvements and add test story
Browse files Browse the repository at this point in the history
  • Loading branch information
yannbf committed Oct 31, 2024
1 parent c54f671 commit 95e970d
Show file tree
Hide file tree
Showing 10 changed files with 273 additions and 82 deletions.
4 changes: 4 additions & 0 deletions code/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ const config: StorybookConfig = {
directory: '../lib/blocks/src',
titlePrefix: 'blocks',
},
{
directory: '../lib/test/src',
titlePrefix: 'test',
},
{
directory: '../addons/a11y/template/stories',
titlePrefix: 'addons/a11y',
Expand Down
10 changes: 7 additions & 3 deletions code/.storybook/storybook.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ import * as projectAnnotations from './preview';
vi.spyOn(console, 'warn').mockImplementation((...args) => console.log(...args));

const annotations = setProjectAnnotations([
// @ts-expect-error check type errors later
projectAnnotations,
// @ts-expect-error check type errors later
componentAnnotations,
coreAnnotations,
testAnnotations,
Expand All @@ -29,7 +27,13 @@ const annotations = setProjectAnnotations([
if (globalThis.__vitest_browser__) {
const vitest = await import('@vitest/browser/context');
const { userEvent: browserEvent } = vitest;
context.userEvent = browserEvent.setup();
// @ts-expect-error check type errors later
context.userEvent = {
// we add storybook user event here as browserEvent is limited
// and does not have a few methods like pointer so we fallback to Storybook's userEvent
...storybookEvent.setup(),
...browserEvent.setup(),
};
context.expect = vitestExpect;
} else {
context.userEvent = storybookEvent.setup();
Expand Down
1 change: 1 addition & 0 deletions code/.storybook/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export default mergeConfig(
test: {
name: 'storybook-ui',
include: [
'../lib/**/*.{story,stories}.?(c|m)[jt]s?(x)',
'../addons/**/*.{story,stories}.?(c|m)[jt]s?(x)',
// '../core/template/stories/**/*.{story,stories}.?(c|m)[jt]s?(x)',
'../core/src/manager/**/*.{story,stories}.?(c|m)[jt]s?(x)',
Expand Down
7 changes: 2 additions & 5 deletions code/addons/test/src/Panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,6 @@ export const Panel = memo<{ storyId: string }>(function PanelMemoized({ storyId
unhandledErrors: undefined,
});

console.log({ globals });

// local state
const [scrollTarget, setScrollTarget] = useState<HTMLElement | undefined>(undefined);
const [collapsed, setCollapsed] = useState<Set<Call['id']>>(new Set());
Expand Down Expand Up @@ -175,8 +173,7 @@ export const Panel = memo<{ storyId: string }>(function PanelMemoized({ storyId
},
[STORY_RENDER_PHASE_CHANGED]: (event) => {
if (event.newPhase === 'preparing') {
set((s) => ({
...s,
set({
controlStates: INITIAL_CONTROL_STATES,
isErrored: false,
pausedAt: undefined,
Expand All @@ -186,7 +183,7 @@ export const Panel = memo<{ storyId: string }>(function PanelMemoized({ storyId
caughtException: undefined,
interactionsCount: 0,
unhandledErrors: undefined,
}));
});
return;
}
set((s) => {
Expand Down
2 changes: 1 addition & 1 deletion code/addons/test/src/components/Subnav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ export const Subnav: React.FC<SubnavProps> = ({
key="outline"
active={isDemoMode}
aria-label="Demo mode"
title="Apply outlines to the preview"
title="Toggle demo mode"
onClick={controls.toggleDemoMode}
>
<PointerHandIcon />
Expand Down
12 changes: 7 additions & 5 deletions code/addons/test/template/stories/basics.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,15 @@ export const WithLoaders = {
const UserEventSetup = {
play: async (context) => {
const { args, canvasElement, step } = context;
const user = userEvent.setup();
const canvas = within(canvasElement);
await step('Select and type on input using user-event v14 setup!!!!', async () => {
await step('Select and type on input using user-event v14 setup', async () => {
const input = canvas.getByRole('textbox');
await context.userEvent.click(input);
await context.userEvent.type(input, 'Typing ...');
await user.click(input);
await user.type(input, 'Typing ...');
});
await step('Tab and press enter on submit button', async () => {
await context.userEvent.pointer([
await user.pointer([
{ keys: '[TouchA>]', target: canvas.getByRole('textbox') },
{ keys: '[/TouchA]' },
]);
Expand All @@ -120,7 +121,8 @@ const UserEventSetup = {
// user event has a few issues on firefox, therefore we do it differently
await fireEvent.click(submitButton);
} else {
await context.userEvent.click(submitButton);
await user.tab();
await user.keyboard('{enter}');
await expect(submitButton).toHaveFocus();
}

Expand Down
54 changes: 11 additions & 43 deletions code/core/src/manager/components/preview/tools/remount.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { ComponentProps } from 'react';
import React, { useState } from 'react';

import { IconButton, WithTooltip } from '@storybook/core/components';
import { IconButton } from '@storybook/core/components';
import { styled } from '@storybook/core/theming';
import type { Addon_BaseType } from '@storybook/core/types';
import { ButtonIcon, SyncIcon } from '@storybook/icons';
import { SyncIcon } from '@storybook/icons';

import { FORCE_REMOUNT } from '@storybook/core/core-events';
import { Consumer, types } from '@storybook/core/manager-api';
Expand Down Expand Up @@ -41,7 +41,6 @@ export const remountTool: Addon_BaseType = {
<Consumer filter={menuMapper}>
{({ remount, storyId, api }) => {
const [isAnimating, setIsAnimating] = useState(false);
const [currentMode, setCurrentMode] = useState('normal');
const remountComponent = () => {
if (!storyId) {
return;
Expand All @@ -54,47 +53,16 @@ export const remountTool: Addon_BaseType = {
});

return (
<WithTooltip
closeOnOutsideClick
tooltip={
<div style={{ display: 'grid' }}>
<IconButton
aria-label="Remount using demo mode"
onClick={() => {
setCurrentMode('demo');
api.updateGlobals({
interactionsDemoMode: true,
});
remountComponent();
}}
>
<ButtonIcon /> Demo mode
</IconButton>
<IconButton
aria-label="Remount component"
onClick={() => {
setCurrentMode('normal');
api.updateGlobals({
interactionsDemoMode: false,
});
remountComponent();
}}
>
<SyncIcon /> Remount
</IconButton>
</div>
}
<StyledAnimatedIconButton
key="remount"
title="Remount component"
onClick={remountComponent}
onAnimationEnd={() => setIsAnimating(false)}
animating={isAnimating}
disabled={!storyId}
>
<StyledAnimatedIconButton
key="remount"
title="Remount component"
onAnimationEnd={() => setIsAnimating(false)}
animating={currentMode === 'normal' && isAnimating}
disabled={!storyId}
>
{currentMode === 'demo' ? <ButtonIcon /> : <SyncIcon />}
</StyledAnimatedIconButton>
</WithTooltip>
<SyncIcon />
</StyledAnimatedIconButton>
);
}}
</Consumer>
Expand Down
184 changes: 184 additions & 0 deletions code/lib/test/src/DemoMode.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import React, { useState } from 'react';

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

const styles = {
container: {
display: 'flex',
flexDirection: 'column',
gap: '20px',
alignItems: 'center',
width: '100%',
maxWidth: '600px',
margin: '0 auto',
},
inputGroup: {
display: 'flex',
gap: '10px',
},
smallInput: {
width: '150px',
padding: '5px',
},
button: {
padding: '5px 10px',
cursor: 'pointer',
},
largeTextArea: {
width: '100%',
height: '150px',
padding: '10px',
resize: 'vertical',
},
card: {
width: '100%',
maxWidth: '300px',
height: '200px',
perspective: '1000px',
position: 'relative',
cursor: 'pointer',
transition: 'transform 0.6s',
transformStyle: 'preserve-3d',
},
cardSide: {
position: 'absolute',
width: '100%',
padding: '16px',
height: '100%',
backfaceVisibility: 'hidden',
borderRadius: '8px',
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.1)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
transition: 'opacity 0.3s ease',
},
cardFront: {
backgroundColor: '#f1f1f1',
},
cardBack: {
backgroundColor: '#4CAF50',
transform: 'rotateY(180deg)',
},
} as const;

const DemoModeComponent = () => {
const [isFlipped, setIsFlipped] = useState(false);
const [startX, setStartX] = useState(0);

const handleMouseDown = (event: React.MouseEvent<HTMLDivElement>) => {
setStartX(event.clientX);
};

const handleTouchStart = (event: React.TouchEvent<HTMLDivElement>) => {
setStartX(event.touches[0].clientX);
};

const handleMouseUp = (event: React.MouseEvent<HTMLDivElement>) => {
if (event.clientX > startX) {
setIsFlipped(!isFlipped);
}
};

const handleTouchEnd = (event: React.TouchEvent<HTMLDivElement>) => {
const endX = event.changedTouches[0].clientX;
if (endX > startX) {
setIsFlipped(!isFlipped);
}
};

return (
<div style={styles.container}>
<div style={styles.inputGroup}>
<input type="text" placeholder="Type here..." style={styles.smallInput} />
<button style={styles.button}>Submit</button>
</div>

<textarea placeholder="Enter more details here..." style={styles.largeTextArea}></textarea>

<div
aria-label="card"
style={{
...styles.card,
transform: isFlipped ? 'rotateY(180deg)' : 'rotateY(0deg)',
}}
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}
onTouchStart={handleTouchStart}
onTouchEnd={handleTouchEnd}
>
<div
style={{
...styles.cardSide,
...styles.cardFront,
opacity: isFlipped ? 0 : 1,
zIndex: isFlipped ? 0 : 1,
}}
>
<h3>Front Side</h3>
<p>This is the front side of the card. Drag to flip!</p>
</div>

{/* Back Side */}
<div
style={{
...styles.cardSide,
...styles.cardBack,
opacity: isFlipped ? 1 : 0,
zIndex: isFlipped ? 1 : 0,
}}
>
<h3>Back Side</h3>
<p>This is the back side of the card. Drag to flip back!</p>
</div>
</div>
</div>
);
};

const meta = {
render: DemoModeComponent,
} satisfies Meta;

export default meta;

export const DemoModeHand = {
play: async (context) => {
const { userEvent, canvas } = context;
const firstInput = canvas.getByPlaceholderText('Type here...');
await userEvent.click(firstInput);
const submitButton = canvas.getByRole('button', { name: /Submit/i });
await userEvent.type(firstInput, 'Hello world');
await userEvent.click(submitButton);
const secondInput = canvas.getByPlaceholderText('Enter more details here...');
await userEvent.type(
secondInput,
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam quis augue non nulla tincidunt condimentum sit amet.'
);

const card = canvas.getByLabelText('card');
await userEvent.pointer([
{
target: card,
keys: '[TouchA>]',
coords: { x: 50, y: 50 },
},
{
target: card,
keys: '[TouchA]',
coords: { x: 80, y: 50 },
},
]);
},
} satisfies StoryObj<typeof meta>;

export const DemoModeCircle = {
...DemoModeHand,
parameters: {
test: {
cursorStyle: 'circle',
// demoModeDelay: 100,
},
},
};
Loading

0 comments on commit 95e970d

Please sign in to comment.