Skip to content

Commit

Permalink
Merge branch 'next' into future/base
Browse files Browse the repository at this point in the history
  • Loading branch information
shilman committed Jun 16, 2022
2 parents bf745c3 + ef2bc81 commit cc4a40b
Show file tree
Hide file tree
Showing 15 changed files with 599 additions and 263 deletions.
17 changes: 10 additions & 7 deletions addons/interactions/src/Panel.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ComponentStoryObj, ComponentMeta } from '@storybook/react';
import { CallStates } from '@storybook/instrumenter';
import { styled } from '@storybook/theming';

import { getCall } from './mocks';
import { getCalls, getInteractions } from './mocks';
import { AddonPanelPure } from './Panel';
import SubnavStories from './components/Subnav/Subnav.stories';

Expand All @@ -20,6 +20,8 @@ const StyledWrapper = styled.div(({ theme }) => ({
overflow: 'auto',
}));

const interactions = getInteractions(CallStates.DONE);

export default {
title: 'Addons/Interactions/Panel',
component: AddonPanelPure,
Expand All @@ -34,10 +36,10 @@ export default {
layout: 'fullscreen',
},
args: {
calls: new Map(),
calls: new Map(getCalls(CallStates.DONE).map((call) => [call.id, call])),
controls: SubnavStories.args.controls,
controlStates: SubnavStories.args.controlStates,
interactions: [getCall(CallStates.DONE)],
interactions,
fileName: 'addon-interactions.stories.tsx',
hasException: false,
isPlaying: false,
Expand All @@ -52,14 +54,14 @@ type Story = ComponentStoryObj<typeof AddonPanelPure>;

export const Passing: Story = {
args: {
interactions: [getCall(CallStates.DONE)],
interactions: getInteractions(CallStates.DONE),
},
};

export const Paused: Story = {
args: {
isPlaying: true,
interactions: [getCall(CallStates.WAITING)],
interactions: getInteractions(CallStates.WAITING),
controlStates: {
debugger: true,
start: false,
Expand All @@ -68,20 +70,21 @@ export const Paused: Story = {
next: true,
end: true,
},
pausedAt: interactions[interactions.length - 1].id,
},
};

export const Playing: Story = {
args: {
isPlaying: true,
interactions: [getCall(CallStates.ACTIVE)],
interactions: getInteractions(CallStates.ACTIVE),
},
};

export const Failed: Story = {
args: {
hasException: true,
interactions: [getCall(CallStates.ERROR)],
interactions: getInteractions(CallStates.ERROR),
},
};

Expand Down
58 changes: 47 additions & 11 deletions addons/interactions/src/Panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,16 @@ interface InteractionsPanelProps {
active: boolean;
controls: Controls;
controlStates: ControlStates;
interactions: (Call & { status?: CallStates })[];
interactions: (Call & {
status?: CallStates;
childCallIds: Call['id'][];
isCollapsed: boolean;
toggleCollapsed: () => void;
})[];
fileName?: string;
hasException?: boolean;
isPlaying?: boolean;
pausedAt?: Call['id'];
calls: Map<string, any>;
endRef?: React.Ref<HTMLDivElement>;
onScrollToEnd?: () => void;
Expand Down Expand Up @@ -66,6 +72,7 @@ export const AddonPanelPure: React.FC<InteractionsPanelProps> = React.memo(
fileName,
hasException,
isPlaying,
pausedAt,
onScrollToEnd,
endRef,
isRerunAnimating,
Expand All @@ -87,15 +94,21 @@ export const AddonPanelPure: React.FC<InteractionsPanelProps> = React.memo(
setIsRerunAnimating={setIsRerunAnimating}
/>
)}
{interactions.map((call) => (
<Interaction
key={call.id}
call={call}
callsById={calls}
controls={controls}
controlStates={controlStates}
/>
))}
<div>
{interactions.map((call) => (
<Interaction
key={call.id}
call={call}
callsById={calls}
controls={controls}
controlStates={controlStates}
childCallIds={call.childCallIds}
isCollapsed={call.isCollapsed}
toggleCollapsed={call.toggleCollapsed}
pausedAt={pausedAt}
/>
))}
</div>
<div ref={endRef} />
{!isPlaying && interactions.length === 0 && (
<Placeholder>
Expand All @@ -116,16 +129,36 @@ export const AddonPanelPure: React.FC<InteractionsPanelProps> = React.memo(
export const Panel: React.FC<AddonPanelProps> = (props) => {
const [storyId, setStoryId] = React.useState<StoryId>();
const [controlStates, setControlStates] = React.useState<ControlStates>(INITIAL_CONTROL_STATES);
const [pausedAt, setPausedAt] = React.useState<Call['id']>();
const [isPlaying, setPlaying] = React.useState(false);
const [isRerunAnimating, setIsRerunAnimating] = React.useState(false);
const [scrollTarget, setScrollTarget] = React.useState<HTMLElement>();
const [collapsed, setCollapsed] = React.useState<Set<Call['id']>>(new Set());

// Calls are tracked in a ref so we don't needlessly rerender.
const calls = React.useRef<Map<Call['id'], Omit<Call, 'status'>>>(new Map());
const setCall = ({ status, ...call }: Call) => calls.current.set(call.id, call);

const [log, setLog] = React.useState<LogItem[]>([]);
const interactions = log.map(({ callId, status }) => ({ ...calls.current.get(callId), status }));
const childCallMap = new Map<Call['id'], Call['id'][]>();
const interactions = log
.filter((call) => {
if (!call.parentId) return true;
childCallMap.set(call.parentId, (childCallMap.get(call.parentId) || []).concat(call.callId));
return !collapsed.has(call.parentId);
})
.map(({ callId, status }) => ({
...calls.current.get(callId),
status,
childCallIds: childCallMap.get(callId),
isCollapsed: collapsed.has(callId),
toggleCollapsed: () =>
setCollapsed((ids) => {
if (ids.has(callId)) ids.delete(callId);
else ids.add(callId);
return new Set(ids);
}),
}));

const endRef = React.useRef();
React.useEffect(() => {
Expand All @@ -146,10 +179,12 @@ export const Panel: React.FC<AddonPanelProps> = (props) => {
[EVENTS.SYNC]: (payload) => {
setControlStates(payload.controlStates);
setLog(payload.logItems);
setPausedAt(payload.pausedAt);
},
[STORY_RENDER_PHASE_CHANGED]: (event) => {
setStoryId(event.storyId);
setPlaying(event.newPhase === 'playing');
setPausedAt(undefined);
},
},
[]
Expand Down Expand Up @@ -191,6 +226,7 @@ export const Panel: React.FC<AddonPanelProps> = (props) => {
fileName={fileName}
hasException={hasException}
isPlaying={isPlaying}
pausedAt={pausedAt}
endRef={endRef}
onScrollToEnd={scrollTarget && scrollToTarget}
isRerunAnimating={isRerunAnimating}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export const StandardEmailFailed: CSF3Story = {
await userEvent.click(canvas.getByRole('button', { name: /create account/i }));

await canvas.findByText('Please enter a correctly formatted email address');
expect(args.onSubmit).not.toHaveBeenCalled();
await expect(args.onSubmit).not.toHaveBeenCalled();
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ComponentStoryObj, ComponentMeta } from '@storybook/react';
import { expect } from '@storybook/jest';
import { CallStates } from '@storybook/instrumenter';
import { userEvent, within } from '@storybook/testing-library';
import { getCall } from '../../mocks';
import { getCalls } from '../../mocks';

import { Interaction } from './Interaction';
import SubnavStories from '../Subnav/Subnav.stories';
Expand All @@ -13,33 +13,39 @@ export default {
title: 'Addons/Interactions/Interaction',
component: Interaction,
args: {
callsById: new Map(),
callsById: new Map(getCalls(CallStates.DONE).map((call) => [call.id, call])),
controls: SubnavStories.args.controls,
controlStates: SubnavStories.args.controlStates,
},
} as ComponentMeta<typeof Interaction>;

export const Active: Story = {
args: {
call: getCall(CallStates.ACTIVE),
call: getCalls(CallStates.ACTIVE).slice(-1)[0],
},
};

export const Waiting: Story = {
args: {
call: getCall(CallStates.WAITING),
call: getCalls(CallStates.WAITING).slice(-1)[0],
},
};

export const Failed: Story = {
args: {
call: getCall(CallStates.ERROR),
call: getCalls(CallStates.ERROR).slice(-1)[0],
},
};

export const Done: Story = {
args: {
call: getCall(CallStates.DONE),
call: getCalls(CallStates.DONE).slice(-1)[0],
},
};

export const WithParent: Story = {
args: {
call: { ...getCalls(CallStates.DONE).slice(-1)[0], parentId: 'parent-id' },
},
};

Expand Down
Loading

0 comments on commit cc4a40b

Please sign in to comment.