@storybook/addon-interactions 7.0.0-alpha.2 → 7.0.0-alpha.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +9 -6
  2. package/dist/cjs/Panel.js +88 -77
  3. package/dist/cjs/components/AccountForm/addon-interactions.stories.js +23 -3
  4. package/dist/cjs/components/{Interaction/Interaction.js → Interaction.js} +109 -17
  5. package/dist/cjs/components/{Interaction/Interaction.stories.js → Interaction.stories.js} +17 -9
  6. package/dist/cjs/components/InteractionsPanel.js +130 -0
  7. package/dist/cjs/components/InteractionsPanel.stories.js +158 -0
  8. package/dist/cjs/components/List.js +9 -9
  9. package/dist/cjs/components/MatcherResult.js +1 -1
  10. package/dist/cjs/components/MethodCall.js +63 -63
  11. package/dist/cjs/components/MethodCall.stories.js +106 -19
  12. package/dist/cjs/components/{StatusBadge/StatusBadge.js → StatusBadge.js} +0 -0
  13. package/dist/cjs/components/{StatusBadge/StatusBadge.stories.js → StatusBadge.stories.js} +0 -0
  14. package/dist/cjs/components/{StatusIcon/StatusIcon.js → StatusIcon.js} +1 -1
  15. package/dist/cjs/components/{StatusIcon/StatusIcon.stories.js → StatusIcon.stories.js} +0 -0
  16. package/dist/cjs/components/{Subnav/Subnav.js → Subnav.js} +6 -2
  17. package/dist/cjs/components/{Subnav/Subnav.stories.js → Subnav.stories.js} +2 -1
  18. package/dist/cjs/components/TabStatus.js +27 -0
  19. package/dist/cjs/mocks/index.js +144 -21
  20. package/dist/cjs/preset/preview.js +29 -11
  21. package/dist/esm/Panel.js +84 -72
  22. package/dist/esm/components/AccountForm/addon-interactions.stories.js +20 -2
  23. package/dist/esm/components/Interaction.js +173 -0
  24. package/dist/esm/components/{Interaction/Interaction.stories.js → Interaction.stories.js} +15 -8
  25. package/dist/esm/components/InteractionsPanel.js +103 -0
  26. package/dist/esm/components/InteractionsPanel.stories.js +130 -0
  27. package/dist/esm/components/List.js +9 -9
  28. package/dist/esm/components/MatcherResult.js +1 -1
  29. package/dist/esm/components/MethodCall.js +62 -61
  30. package/dist/esm/components/MethodCall.stories.js +106 -20
  31. package/dist/esm/components/{StatusBadge/StatusBadge.js → StatusBadge.js} +0 -0
  32. package/dist/esm/components/{StatusBadge/StatusBadge.stories.js → StatusBadge.stories.js} +0 -0
  33. package/dist/esm/components/{StatusIcon/StatusIcon.js → StatusIcon.js} +1 -1
  34. package/dist/esm/components/{StatusIcon/StatusIcon.stories.js → StatusIcon.stories.js} +0 -0
  35. package/dist/esm/components/{Subnav/Subnav.js → Subnav.js} +6 -2
  36. package/dist/esm/components/{Subnav/Subnav.stories.js → Subnav.stories.js} +2 -1
  37. package/dist/esm/components/TabStatus.js +12 -0
  38. package/dist/esm/mocks/index.js +140 -20
  39. package/dist/esm/preset/preview.js +29 -11
  40. package/dist/types/Panel.d.ts +14 -28
  41. package/dist/types/components/Interaction.d.ts +16 -0
  42. package/dist/types/components/InteractionsPanel.d.ts +33 -0
  43. package/dist/types/components/MethodCall.d.ts +17 -16
  44. package/dist/types/components/{StatusBadge/StatusBadge.d.ts → StatusBadge.d.ts} +0 -0
  45. package/dist/types/components/{StatusIcon/StatusIcon.d.ts → StatusIcon.d.ts} +0 -0
  46. package/dist/types/components/{Subnav/Subnav.d.ts → Subnav.d.ts} +4 -2
  47. package/dist/types/components/TabStatus.d.ts +9 -0
  48. package/dist/types/mocks/index.d.ts +22 -1
  49. package/dist/types/preset/preview.d.ts +1 -2
  50. package/package.json +17 -15
  51. package/LICENSE +0 -21
  52. package/dist/cjs/Panel.stories.js +0 -110
  53. package/dist/esm/Panel.stories.js +0 -86
  54. package/dist/esm/components/Interaction/Interaction.js +0 -88
  55. package/dist/types/components/Interaction/Interaction.d.ts +0 -9
package/README.md CHANGED
@@ -41,22 +41,25 @@ Interactions relies on "instrumented" versions of Jest and Testing Library, that
41
41
  `@storybook/testing-library` instead of their original package. You can then use these libraries in your `play` function.
42
42
 
43
43
  ```js
44
+ import { Button } from './Button';
44
45
  import { expect } from '@storybook/jest';
45
46
  import { within, userEvent } from '@storybook/testing-library';
46
47
 
47
48
  export default {
48
49
  title: 'Button',
50
+ component: Button,
49
51
  argTypes: {
50
52
  onClick: { action: true },
51
53
  },
52
54
  };
53
55
 
54
- export const Demo = {
55
- play: async ({ args, canvasElement }) => {
56
- const canvas = within(canvasElement);
57
- await userEvent.click(canvas.getByRole('button'));
58
- await expect(args.onClick).toHaveBeenCalled();
59
- },
56
+ const Template = (args) => <Button {...args} />;
57
+
58
+ export const Demo = Template.bind({});
59
+ Demo.play = async ({ args, canvasElement }) => {
60
+ const canvas = within(canvasElement);
61
+ await userEvent.click(canvas.getByRole('button'));
62
+ await expect(args.onClick).toHaveBeenCalled();
60
63
  };
61
64
  ```
62
65
 
package/dist/cjs/Panel.js CHANGED
@@ -3,32 +3,23 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.Panel = exports.AddonPanelPure = void 0;
6
+ exports.getInteractions = exports.Panel = void 0;
7
7
 
8
8
  var _global = _interopRequireDefault(require("global"));
9
9
 
10
10
  var React = _interopRequireWildcard(require("react"));
11
11
 
12
- var _reactDom = _interopRequireDefault(require("react-dom"));
13
-
14
12
  var _api = require("@storybook/api");
15
13
 
16
14
  var _coreEvents = require("@storybook/core-events");
17
15
 
18
- var _components = require("@storybook/components");
19
-
20
16
  var _instrumenter = require("@storybook/instrumenter");
21
17
 
22
- var _theming = require("@storybook/theming");
23
-
24
- var _StatusIcon = require("./components/StatusIcon/StatusIcon");
25
-
26
- var _Subnav = require("./components/Subnav/Subnav");
18
+ var _InteractionsPanel = require("./components/InteractionsPanel");
27
19
 
28
- var _Interaction = require("./components/Interaction/Interaction");
20
+ var _TabStatus = require("./components/TabStatus");
29
21
 
30
- const _excluded = ["calls", "controls", "controlStates", "interactions", "fileName", "hasException", "isPlaying", "onScrollToEnd", "endRef", "isRerunAnimating", "setIsRerunAnimating"],
31
- _excluded2 = ["status"];
22
+ const _excluded = ["status"];
32
23
 
33
24
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
34
25
 
@@ -48,81 +39,69 @@ const INITIAL_CONTROL_STATES = {
48
39
  next: false,
49
40
  end: false
50
41
  };
51
- const TabIcon = (0, _theming.styled)(_StatusIcon.StatusIcon)({
52
- marginLeft: 5
53
- });
54
42
 
55
- const TabStatus = ({
56
- children
43
+ const getInteractions = ({
44
+ log,
45
+ calls,
46
+ collapsed,
47
+ setCollapsed
57
48
  }) => {
58
- const container = _global.default.document.getElementById('tabbutton-interactions');
59
-
60
- return container && /*#__PURE__*/_reactDom.default.createPortal(children, container);
49
+ const callsById = new Map();
50
+ const childCallMap = new Map();
51
+ return log.filter(({
52
+ callId,
53
+ parentId
54
+ }) => {
55
+ if (!parentId) return true;
56
+ childCallMap.set(parentId, (childCallMap.get(parentId) || []).concat(callId));
57
+ return !collapsed.has(parentId);
58
+ }).map(({
59
+ callId,
60
+ status
61
+ }) => Object.assign({}, calls.get(callId), {
62
+ status
63
+ })).map(call => {
64
+ var _callsById$get;
65
+
66
+ const status = call.status === _instrumenter.CallStates.ERROR && ((_callsById$get = callsById.get(call.parentId)) === null || _callsById$get === void 0 ? void 0 : _callsById$get.status) === _instrumenter.CallStates.ACTIVE ? _instrumenter.CallStates.ACTIVE : call.status;
67
+ callsById.set(call.id, Object.assign({}, call, {
68
+ status
69
+ }));
70
+ return Object.assign({}, call, {
71
+ status,
72
+ childCallIds: childCallMap.get(call.id),
73
+ isCollapsed: collapsed.has(call.id),
74
+ toggleCollapsed: () => setCollapsed(ids => {
75
+ if (ids.has(call.id)) ids.delete(call.id);else ids.add(call.id);
76
+ return new Set(ids);
77
+ })
78
+ });
79
+ });
61
80
  };
62
81
 
63
- const AddonPanelPure = /*#__PURE__*/React.memo(_ref => {
64
- let {
65
- calls,
66
- controls,
67
- controlStates,
68
- interactions,
69
- fileName,
70
- hasException,
71
- isPlaying,
72
- onScrollToEnd,
73
- endRef,
74
- isRerunAnimating,
75
- setIsRerunAnimating
76
- } = _ref,
77
- panelProps = _objectWithoutPropertiesLoose(_ref, _excluded);
78
-
79
- return /*#__PURE__*/React.createElement(_components.AddonPanel, panelProps, controlStates.debugger && interactions.length > 0 && /*#__PURE__*/React.createElement(_Subnav.Subnav, {
80
- controls: controls,
81
- controlStates: controlStates,
82
- status: // eslint-disable-next-line no-nested-ternary
83
- isPlaying ? _instrumenter.CallStates.ACTIVE : hasException ? _instrumenter.CallStates.ERROR : _instrumenter.CallStates.DONE,
84
- storyFileName: fileName,
85
- onScrollToEnd: onScrollToEnd,
86
- isRerunAnimating: isRerunAnimating,
87
- setIsRerunAnimating: setIsRerunAnimating
88
- }), interactions.map(call => /*#__PURE__*/React.createElement(_Interaction.Interaction, {
89
- key: call.id,
90
- call: call,
91
- callsById: calls,
92
- controls: controls,
93
- controlStates: controlStates
94
- })), /*#__PURE__*/React.createElement("div", {
95
- ref: endRef
96
- }), !isPlaying && interactions.length === 0 && /*#__PURE__*/React.createElement(_components.Placeholder, null, "No interactions found", /*#__PURE__*/React.createElement(_components.Link, {
97
- href: "https://github.com/storybookjs/storybook/blob/next/addons/interactions/README.md",
98
- target: "_blank",
99
- withArrow: true
100
- }, "Learn how to add interactions to your story")));
101
- });
102
- exports.AddonPanelPure = AddonPanelPure;
82
+ exports.getInteractions = getInteractions;
103
83
 
104
84
  const Panel = props => {
105
85
  const [storyId, setStoryId] = React.useState();
106
86
  const [controlStates, setControlStates] = React.useState(INITIAL_CONTROL_STATES);
87
+ const [pausedAt, setPausedAt] = React.useState();
88
+ const [isErrored, setErrored] = React.useState(false);
107
89
  const [isPlaying, setPlaying] = React.useState(false);
108
90
  const [isRerunAnimating, setIsRerunAnimating] = React.useState(false);
109
- const [scrollTarget, setScrollTarget] = React.useState(); // Calls are tracked in a ref so we don't needlessly rerender.
91
+ const [scrollTarget, setScrollTarget] = React.useState();
92
+ const [collapsed, setCollapsed] = React.useState(new Set());
93
+ const [caughtException, setCaughtException] = React.useState();
94
+ const [interactions, setInteractions] = React.useState([]);
95
+ const [interactionsCount, setInteractionsCount] = React.useState(); // Calls are tracked in a ref so we don't needlessly rerender.
110
96
 
111
97
  const calls = React.useRef(new Map());
112
98
 
113
- const setCall = _ref2 => {
114
- let call = _objectWithoutPropertiesLoose(_ref2, _excluded2);
99
+ const setCall = _ref => {
100
+ let call = _objectWithoutPropertiesLoose(_ref, _excluded);
115
101
 
116
102
  return calls.current.set(call.id, call);
117
103
  };
118
104
 
119
- const [log, setLog] = React.useState([]);
120
- const interactions = log.map(({
121
- callId,
122
- status
123
- }) => Object.assign({}, calls.current.get(callId), {
124
- status
125
- }));
126
105
  const endRef = React.useRef();
127
106
  React.useEffect(() => {
128
107
  let observer;
@@ -144,13 +123,36 @@ const Panel = props => {
144
123
  [_instrumenter.EVENTS.CALL]: setCall,
145
124
  [_instrumenter.EVENTS.SYNC]: payload => {
146
125
  setControlStates(payload.controlStates);
147
- setLog(payload.logItems);
126
+ setPausedAt(payload.pausedAt);
127
+ setInteractions(getInteractions({
128
+ log: payload.logItems,
129
+ calls: calls.current,
130
+ collapsed,
131
+ setCollapsed
132
+ }));
148
133
  },
149
134
  [_coreEvents.STORY_RENDER_PHASE_CHANGED]: event => {
150
135
  setStoryId(event.storyId);
151
136
  setPlaying(event.newPhase === 'playing');
137
+ setPausedAt(undefined);
138
+
139
+ if (event.newPhase === 'rendering') {
140
+ setErrored(false);
141
+ setCaughtException(undefined);
142
+ }
143
+ },
144
+ [_coreEvents.STORY_THREW_EXCEPTION]: () => {
145
+ setErrored(true);
146
+ },
147
+ [_coreEvents.PLAY_FUNCTION_THREW_EXCEPTION]: e => {
148
+ console.log('PLAY_FUNCTION_THREW_EXCEPTION');
149
+ if ((e === null || e === void 0 ? void 0 : e.message) !== _coreEvents.IGNORED_EXCEPTION.message) setCaughtException(e);else setCaughtException(undefined);
152
150
  }
153
- }, []);
151
+ }, [collapsed]);
152
+ React.useEffect(() => {
153
+ if (isPlaying || isRerunAnimating) return;
154
+ setInteractionsCount(interactions.length);
155
+ }, [interactions, isPlaying, isRerunAnimating]);
154
156
  const controls = React.useMemo(() => ({
155
157
  start: () => emit(_instrumenter.EVENTS.START, {
156
158
  storyId
@@ -183,20 +185,29 @@ const Panel = props => {
183
185
  block: 'end'
184
186
  });
185
187
 
186
- const showStatus = log.length > 0 && !isPlaying;
187
- const hasException = log.some(item => item.status === _instrumenter.CallStates.ERROR);
188
+ const showStatus = interactionsCount > 0 || !!caughtException || isRerunAnimating;
189
+ const hasException = !!caughtException || interactions.some(v => v.status === _instrumenter.CallStates.ERROR);
190
+
191
+ if (isErrored) {
192
+ return /*#__PURE__*/React.createElement(React.Fragment, {
193
+ key: "interactions"
194
+ });
195
+ }
196
+
188
197
  return /*#__PURE__*/React.createElement(React.Fragment, {
189
198
  key: "interactions"
190
- }, /*#__PURE__*/React.createElement(TabStatus, null, showStatus && (hasException ? /*#__PURE__*/React.createElement(TabIcon, {
199
+ }, /*#__PURE__*/React.createElement(_TabStatus.TabStatus, null, showStatus && (hasException ? /*#__PURE__*/React.createElement(_TabStatus.TabIcon, {
191
200
  status: _instrumenter.CallStates.ERROR
192
- }) : ` (${interactions.length})`)), /*#__PURE__*/React.createElement(AddonPanelPure, _extends({
201
+ }) : ` (${interactionsCount})`)), /*#__PURE__*/React.createElement(_InteractionsPanel.InteractionsPanel, _extends({
193
202
  calls: calls.current,
194
203
  controls: controls,
195
204
  controlStates: controlStates,
196
205
  interactions: interactions,
197
206
  fileName: fileName,
198
207
  hasException: hasException,
208
+ caughtException: caughtException,
199
209
  isPlaying: isPlaying,
210
+ pausedAt: pausedAt,
200
211
  endRef: endRef,
201
212
  onScrollToEnd: scrollTarget && scrollToTarget,
202
213
  isRerunAnimating: isRerunAnimating,
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.default = exports.WithLoaders = exports.WaitForElementToBeRemoved = exports.WaitFor = exports.VerificationSuccess = exports.VerificationPasswordMismatch = exports.VerificationPassword = exports.Verification = exports.StandardPasswordFailed = exports.StandardFailHover = exports.StandardEmailSuccess = exports.StandardEmailFilled = exports.StandardEmailFailed = exports.Standard = exports.FindBy = exports.Demo = void 0;
6
+ exports.default = exports.WithLoaders = exports.WaitForElementToBeRemoved = exports.WaitFor = exports.VerificationSuccess = exports.VerificationPasswordMismatch = exports.VerificationPassword = exports.Verification = exports.StandardPasswordFailed = exports.StandardFailHover = exports.StandardEmailSuccess = exports.StandardEmailFilled = exports.StandardEmailFailed = exports.Standard = exports.FindBy = exports.Exception = exports.Demo = void 0;
7
7
 
8
8
  var _jest = require("@storybook/jest");
9
9
 
@@ -46,6 +46,26 @@ Demo.play = async ({
46
46
  }) => {
47
47
  await _testingLibrary.userEvent.click((0, _testingLibrary.within)(canvasElement).getByRole('button'));
48
48
  await (0, _jest.expect)(args.onSubmit).toHaveBeenCalledWith(_jest.expect.stringMatching(/([A-Z])\w+/gi));
49
+ await (0, _jest.expect)([{
50
+ name: 'John',
51
+ age: 42
52
+ }]).toEqual(_jest.expect.arrayContaining([_jest.expect.objectContaining({
53
+ name: 'John'
54
+ }), _jest.expect.objectContaining({
55
+ age: 42
56
+ })]));
57
+ };
58
+
59
+ const Exception = Demo.bind({});
60
+ exports.Exception = Exception;
61
+
62
+ Exception.play = () => Demo.play(undefined); // deepscan-disable-line
63
+
64
+
65
+ Exception.parameters = {
66
+ chromatic: {
67
+ disableSnapshot: true
68
+ }
49
69
  };
50
70
 
51
71
  const FindBy = args => {
@@ -110,7 +130,7 @@ WaitForElementToBeRemoved.play = async ({
110
130
  timeout: 2000
111
131
  });
112
132
  const button = await canvas.findByText('Loaded!');
113
- await (0, _jest.expect)(button).not.toBeNull();
133
+ await (0, _jest.expect)(button).toBeInTheDocument();
114
134
  };
115
135
 
116
136
  const WithLoaders = (args, {
@@ -179,7 +199,7 @@ const StandardEmailFailed = Object.assign({}, Standard, {
179
199
  name: /create account/i
180
200
  }));
181
201
  await canvas.findByText('Please enter a correctly formatted email address');
182
- (0, _jest.expect)(args.onSubmit).not.toHaveBeenCalled();
202
+ await (0, _jest.expect)(args.onSubmit).not.toHaveBeenCalled();
183
203
  }
184
204
  });
185
205
  exports.StandardEmailFailed = StandardEmailFailed;
@@ -3,21 +3,23 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.Interaction = void 0;
6
+ exports.StyledIconButton = exports.Interaction = void 0;
7
7
 
8
8
  var React = _interopRequireWildcard(require("react"));
9
9
 
10
+ var _components = require("@storybook/components");
11
+
10
12
  var _instrumenter = require("@storybook/instrumenter");
11
13
 
12
14
  var _theming = require("@storybook/theming");
13
15
 
14
16
  var _polished = require("polished");
15
17
 
16
- var _MatcherResult = require("../MatcherResult");
18
+ var _MatcherResult = require("./MatcherResult");
17
19
 
18
- var _MethodCall = require("../MethodCall");
20
+ var _MethodCall = require("./MethodCall");
19
21
 
20
- var _StatusIcon = require("../StatusIcon/StatusIcon");
22
+ var _StatusIcon = require("./StatusIcon");
21
23
 
22
24
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
23
25
 
@@ -31,11 +33,12 @@ const MethodCallWrapper = _theming.styled.div(() => ({
31
33
  }));
32
34
 
33
35
  const RowContainer = (0, _theming.styled)('div', {
34
- shouldForwardProp: prop => !['call'].includes(prop)
36
+ shouldForwardProp: prop => !['call', 'pausedAt'].includes(prop.toString())
35
37
  })(({
36
38
  theme,
37
39
  call
38
40
  }) => Object.assign({
41
+ position: 'relative',
39
42
  display: 'flex',
40
43
  flexDirection: 'column',
41
44
  borderBottom: `1px solid ${theme.appBorderColor}`,
@@ -43,14 +46,50 @@ const RowContainer = (0, _theming.styled)('div', {
43
46
  fontSize: 13
44
47
  }, call.status === _instrumenter.CallStates.ERROR && {
45
48
  backgroundColor: theme.base === 'dark' ? (0, _polished.transparentize)(0.93, theme.color.negative) : theme.background.warning
49
+ }, {
50
+ paddingLeft: call.parentId ? 20 : 0
51
+ }), ({
52
+ theme,
53
+ call,
54
+ pausedAt
55
+ }) => pausedAt === call.id && {
56
+ '&::before': {
57
+ content: '""',
58
+ position: 'absolute',
59
+ top: -5,
60
+ zIndex: 1,
61
+ borderTop: '4.5px solid transparent',
62
+ borderLeft: `7px solid ${theme.color.warning}`,
63
+ borderBottom: '4.5px solid transparent'
64
+ },
65
+ '&::after': {
66
+ content: '""',
67
+ position: 'absolute',
68
+ top: -1,
69
+ zIndex: 1,
70
+ width: '100%',
71
+ borderTop: `1.5px solid ${theme.color.warning}`
72
+ }
73
+ });
74
+
75
+ const RowHeader = _theming.styled.div(({
76
+ theme,
77
+ disabled
78
+ }) => ({
79
+ display: 'flex',
80
+ '&:hover': disabled ? {} : {
81
+ background: theme.background.hoverable
82
+ }
46
83
  }));
84
+
47
85
  const RowLabel = (0, _theming.styled)('button', {
48
- shouldForwardProp: prop => !['call'].includes(prop)
86
+ shouldForwardProp: prop => !['call'].includes(prop.toString())
49
87
  })(({
50
88
  theme,
51
89
  disabled,
52
90
  call
53
91
  }) => ({
92
+ flex: 1,
54
93
  display: 'grid',
55
94
  background: 'none',
56
95
  border: 0,
@@ -61,9 +100,6 @@ const RowLabel = (0, _theming.styled)('button', {
61
100
  padding: '8px 15px',
62
101
  textAlign: 'start',
63
102
  cursor: disabled || call.status === _instrumenter.CallStates.ERROR ? 'default' : 'pointer',
64
- '&:hover': disabled ? {} : {
65
- background: theme.background.hoverable
66
- },
67
103
  '&:focus-visible': {
68
104
  outline: 0,
69
105
  boxShadow: `inset 3px 0 0 0 ${call.status === _instrumenter.CallStates.ERROR ? theme.color.warning : theme.color.secondary}`,
@@ -73,25 +109,69 @@ const RowLabel = (0, _theming.styled)('button', {
73
109
  opacity: call.status === _instrumenter.CallStates.WAITING ? 0.5 : 1
74
110
  }
75
111
  }));
76
- const RowMessage = (0, _theming.styled)('pre')({
77
- margin: 0,
78
- padding: '8px 10px 8px 30px',
79
- fontSize: _theming.typography.size.s1
112
+
113
+ const RowActions = _theming.styled.div({
114
+ padding: 6
80
115
  });
81
116
 
117
+ const StyledIconButton = (0, _theming.styled)(_components.IconButton)(({
118
+ theme
119
+ }) => ({
120
+ color: theme.color.mediumdark,
121
+ margin: '0 3px'
122
+ }));
123
+ exports.StyledIconButton = StyledIconButton;
124
+ const Note = (0, _theming.styled)(_components.TooltipNote)(({
125
+ theme
126
+ }) => ({
127
+ fontFamily: theme.typography.fonts.base
128
+ }));
129
+ const RowMessage = (0, _theming.styled)('div')(({
130
+ theme
131
+ }) => ({
132
+ padding: '8px 10px 8px 36px',
133
+ fontSize: _theming.typography.size.s1,
134
+ color: theme.color.defaultText,
135
+ pre: {
136
+ margin: 0,
137
+ padding: 0
138
+ }
139
+ }));
140
+
141
+ const Exception = ({
142
+ exception
143
+ }) => {
144
+ if (exception.message.startsWith('expect(')) {
145
+ return /*#__PURE__*/React.createElement(_MatcherResult.MatcherResult, exception);
146
+ }
147
+
148
+ const paragraphs = exception.message.split('\n\n');
149
+ const more = paragraphs.length > 1;
150
+ return /*#__PURE__*/React.createElement(RowMessage, null, /*#__PURE__*/React.createElement("pre", null, paragraphs[0]), more && /*#__PURE__*/React.createElement("p", null, "See the full stack trace in the browser console."));
151
+ };
152
+
82
153
  const Interaction = ({
83
154
  call,
84
155
  callsById,
85
156
  controls,
86
- controlStates
157
+ controlStates,
158
+ childCallIds,
159
+ isCollapsed,
160
+ toggleCollapsed,
161
+ pausedAt
87
162
  }) => {
163
+ var _call$exception;
164
+
88
165
  const [isHovered, setIsHovered] = React.useState(false);
89
166
  return /*#__PURE__*/React.createElement(RowContainer, {
90
- call: call
167
+ call: call,
168
+ pausedAt: pausedAt
169
+ }, /*#__PURE__*/React.createElement(RowHeader, {
170
+ disabled: !controlStates.goto || !call.interceptable || !!call.parentId
91
171
  }, /*#__PURE__*/React.createElement(RowLabel, {
92
172
  call: call,
93
173
  onClick: () => controls.goto(call.id),
94
- disabled: !controlStates.goto,
174
+ disabled: !controlStates.goto || !call.interceptable || !!call.parentId,
95
175
  onMouseEnter: () => controlStates.goto && setIsHovered(true),
96
176
  onMouseLeave: () => controlStates.goto && setIsHovered(false)
97
177
  }, /*#__PURE__*/React.createElement(_StatusIcon.StatusIcon, {
@@ -104,7 +184,19 @@ const Interaction = ({
104
184
  }, /*#__PURE__*/React.createElement(_MethodCall.MethodCall, {
105
185
  call: call,
106
186
  callsById: callsById
107
- }))), call.status === _instrumenter.CallStates.ERROR && call.exception && (call.exception.message.startsWith('expect(') ? /*#__PURE__*/React.createElement(_MatcherResult.MatcherResult, call.exception) : /*#__PURE__*/React.createElement(RowMessage, null, call.exception.message)));
187
+ }))), /*#__PURE__*/React.createElement(RowActions, null, (childCallIds === null || childCallIds === void 0 ? void 0 : childCallIds.length) > 0 && /*#__PURE__*/React.createElement(_components.WithTooltip, {
188
+ hasChrome: false,
189
+ tooltip: /*#__PURE__*/React.createElement(Note, {
190
+ note: `${isCollapsed ? 'Show' : 'Hide'} interactions (${childCallIds.length})`
191
+ })
192
+ }, /*#__PURE__*/React.createElement(StyledIconButton, {
193
+ containsIcon: true,
194
+ onClick: toggleCollapsed
195
+ }, /*#__PURE__*/React.createElement(_components.Icons, {
196
+ icon: "listunordered"
197
+ }))))), call.status === _instrumenter.CallStates.ERROR && ((_call$exception = call.exception) === null || _call$exception === void 0 ? void 0 : _call$exception.callId) === call.id && /*#__PURE__*/React.createElement(Exception, {
198
+ exception: call.exception
199
+ }));
108
200
  };
109
201
 
110
202
  exports.Interaction = Interaction;
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.default = exports.Waiting = exports.Hovered = exports.Failed = exports.Done = exports.Disabled = exports.Active = void 0;
6
+ exports.default = exports.WithParent = exports.Waiting = exports.Hovered = exports.Failed = exports.Done = exports.Disabled = exports.Active = void 0;
7
7
 
8
8
  var _jest = require("@storybook/jest");
9
9
 
@@ -11,11 +11,11 @@ var _instrumenter = require("@storybook/instrumenter");
11
11
 
12
12
  var _testingLibrary = require("@storybook/testing-library");
13
13
 
14
- var _mocks = require("../../mocks");
14
+ var _mocks = require("../mocks");
15
15
 
16
16
  var _Interaction = require("./Interaction");
17
17
 
18
- var _Subnav = _interopRequireDefault(require("../Subnav/Subnav.stories"));
18
+ var _Subnav = _interopRequireDefault(require("./Subnav.stories"));
19
19
 
20
20
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
21
21
 
@@ -23,7 +23,7 @@ var _default = {
23
23
  title: 'Addons/Interactions/Interaction',
24
24
  component: _Interaction.Interaction,
25
25
  args: {
26
- callsById: new Map(),
26
+ callsById: new Map((0, _mocks.getCalls)(_instrumenter.CallStates.DONE).map(call => [call.id, call])),
27
27
  controls: _Subnav.default.args.controls,
28
28
  controlStates: _Subnav.default.args.controlStates
29
29
  }
@@ -31,28 +31,36 @@ var _default = {
31
31
  exports.default = _default;
32
32
  const Active = {
33
33
  args: {
34
- call: (0, _mocks.getCall)(_instrumenter.CallStates.ACTIVE)
34
+ call: (0, _mocks.getCalls)(_instrumenter.CallStates.ACTIVE).slice(-1)[0]
35
35
  }
36
36
  };
37
37
  exports.Active = Active;
38
38
  const Waiting = {
39
39
  args: {
40
- call: (0, _mocks.getCall)(_instrumenter.CallStates.WAITING)
40
+ call: (0, _mocks.getCalls)(_instrumenter.CallStates.WAITING).slice(-1)[0]
41
41
  }
42
42
  };
43
43
  exports.Waiting = Waiting;
44
44
  const Failed = {
45
45
  args: {
46
- call: (0, _mocks.getCall)(_instrumenter.CallStates.ERROR)
46
+ call: (0, _mocks.getCalls)(_instrumenter.CallStates.ERROR).slice(-1)[0]
47
47
  }
48
48
  };
49
49
  exports.Failed = Failed;
50
50
  const Done = {
51
51
  args: {
52
- call: (0, _mocks.getCall)(_instrumenter.CallStates.DONE)
52
+ call: (0, _mocks.getCalls)(_instrumenter.CallStates.DONE).slice(-1)[0]
53
53
  }
54
54
  };
55
55
  exports.Done = Done;
56
+ const WithParent = {
57
+ args: {
58
+ call: Object.assign({}, (0, _mocks.getCalls)(_instrumenter.CallStates.DONE).slice(-1)[0], {
59
+ parentId: 'parent-id'
60
+ })
61
+ }
62
+ };
63
+ exports.WithParent = WithParent;
56
64
  const Disabled = {
57
65
  args: Object.assign({}, Done.args, {
58
66
  controlStates: Object.assign({}, _Subnav.default.args.controlStates, {
@@ -71,7 +79,7 @@ const Hovered = Object.assign({}, Done, {
71
79
  }) => {
72
80
  const canvas = (0, _testingLibrary.within)(canvasElement);
73
81
  await _testingLibrary.userEvent.hover(canvas.getByRole('button'));
74
- await (0, _jest.expect)(canvas.getByTestId('icon-active')).not.toBeNull();
82
+ await (0, _jest.expect)(canvas.getByTestId('icon-active')).toBeInTheDocument();
75
83
  }
76
84
  });
77
85
  exports.Hovered = Hovered;