@bundle-stats/ui 4.13.0-beta.16 → 4.13.0-beta.17

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 (81) hide show
  1. package/lib/assets/icons.svg +70 -67
  2. package/lib/assets/icons.svg.js +14 -12
  3. package/lib/assets/icons.svg.js.map +1 -1
  4. package/lib/components/bundle-assets/bundle-assets.js +39 -7
  5. package/lib/components/bundle-assets/bundle-assets.js.map +1 -1
  6. package/lib/components/bundle-assets-totals/bundle-assets-totals.js +4 -4
  7. package/lib/components/bundle-assets-totals/bundle-assets-totals.js.map +1 -1
  8. package/lib/components/bundle-modules/bundle-modules.js +28 -7
  9. package/lib/components/bundle-modules/bundle-modules.js.map +1 -1
  10. package/lib/components/bundle-packages/bundle-packages.js +39 -7
  11. package/lib/components/bundle-packages/bundle-packages.js.map +1 -1
  12. package/lib/components/metrics-display-selector/metrics-display-selector.js +41 -4
  13. package/lib/components/metrics-display-selector/metrics-display-selector.js.map +1 -1
  14. package/lib/components/metrics-display-selector/metrics-display-selector.module.css +15 -7
  15. package/lib/components/metrics-treemap/index.js +4 -0
  16. package/lib/components/metrics-treemap/index.js.map +1 -1
  17. package/lib/components/metrics-treemap/metrics-treemap.constants.js +3 -0
  18. package/lib/components/metrics-treemap/metrics-treemap.constants.js.map +1 -0
  19. package/lib/components/metrics-treemap/metrics-treemap.js +57 -38
  20. package/lib/components/metrics-treemap/metrics-treemap.js.map +1 -1
  21. package/lib/components/metrics-treemap/metrics-treemap.module.css +55 -25
  22. package/lib/components/metrics-treemap/metrics-treemap.utils.js +70 -0
  23. package/lib/components/metrics-treemap/metrics-treemap.utils.js.map +1 -0
  24. package/lib/hooks/metrics-display-type.js +56 -24
  25. package/lib/hooks/metrics-display-type.js.map +1 -1
  26. package/lib/ui/icon/icon.js +1 -0
  27. package/lib/ui/icon/icon.js.map +1 -1
  28. package/lib-esm/assets/icons.svg +70 -67
  29. package/lib-esm/assets/icons.svg.js +14 -12
  30. package/lib-esm/assets/icons.svg.js.map +1 -1
  31. package/lib-esm/components/bundle-assets/bundle-assets.js +40 -8
  32. package/lib-esm/components/bundle-assets/bundle-assets.js.map +1 -1
  33. package/lib-esm/components/bundle-assets-totals/bundle-assets-totals.js +5 -5
  34. package/lib-esm/components/bundle-assets-totals/bundle-assets-totals.js.map +1 -1
  35. package/lib-esm/components/bundle-modules/bundle-modules.js +29 -8
  36. package/lib-esm/components/bundle-modules/bundle-modules.js.map +1 -1
  37. package/lib-esm/components/bundle-packages/bundle-packages.js +40 -8
  38. package/lib-esm/components/bundle-packages/bundle-packages.js.map +1 -1
  39. package/lib-esm/components/metrics-display-selector/metrics-display-selector.js +41 -4
  40. package/lib-esm/components/metrics-display-selector/metrics-display-selector.js.map +1 -1
  41. package/lib-esm/components/metrics-display-selector/metrics-display-selector.module.css +15 -7
  42. package/lib-esm/components/metrics-treemap/index.js +1 -0
  43. package/lib-esm/components/metrics-treemap/index.js.map +1 -1
  44. package/lib-esm/components/metrics-treemap/metrics-treemap.constants.js +2 -0
  45. package/lib-esm/components/metrics-treemap/metrics-treemap.constants.js.map +1 -0
  46. package/lib-esm/components/metrics-treemap/metrics-treemap.js +58 -39
  47. package/lib-esm/components/metrics-treemap/metrics-treemap.js.map +1 -1
  48. package/lib-esm/components/metrics-treemap/metrics-treemap.module.css +55 -25
  49. package/lib-esm/components/metrics-treemap/metrics-treemap.utils.js +65 -0
  50. package/lib-esm/components/metrics-treemap/metrics-treemap.utils.js.map +1 -0
  51. package/lib-esm/hooks/metrics-display-type.js +57 -24
  52. package/lib-esm/hooks/metrics-display-type.js.map +1 -1
  53. package/lib-esm/ui/icon/icon.js +1 -0
  54. package/lib-esm/ui/icon/icon.js.map +1 -1
  55. package/package.json +7 -7
  56. package/src/assets/icons/check.svg +4 -0
  57. package/src/assets/icons.svg +70 -67
  58. package/src/assets/icons.svg.jsx +12 -12
  59. package/src/components/bundle-assets/bundle-assets.jsx +76 -20
  60. package/src/components/bundle-assets-totals/bundle-assets-totals.jsx +5 -4
  61. package/src/components/bundle-modules/bundle-modules.tsx +76 -16
  62. package/src/components/bundle-packages/bundle-packages.jsx +71 -16
  63. package/src/components/metrics-display-selector/metrics-display-selector.module.css +15 -7
  64. package/src/components/metrics-display-selector/metrics-display-selector.stories.tsx +33 -0
  65. package/src/components/metrics-display-selector/metrics-display-selector.tsx +83 -25
  66. package/src/components/metrics-treemap/__tests__/metrics-treemap.utils.ts +143 -0
  67. package/src/components/metrics-treemap/index.ts +1 -0
  68. package/src/components/metrics-treemap/metrics-treemap.constants.ts +43 -0
  69. package/src/components/metrics-treemap/metrics-treemap.module.css +55 -25
  70. package/src/components/metrics-treemap/metrics-treemap.stories.tsx +44 -10
  71. package/src/components/metrics-treemap/metrics-treemap.tsx +247 -84
  72. package/src/components/metrics-treemap/metrics-treemap.utils.ts +107 -0
  73. package/src/hooks/metrics-display-type.ts +87 -29
  74. package/src/ui/icon/icon.tsx +1 -0
  75. package/types/components/metrics-display-selector/metrics-display-selector.d.ts +3 -1
  76. package/types/components/metrics-treemap/index.d.ts +1 -0
  77. package/types/components/metrics-treemap/metrics-treemap.constants.d.ts +15 -0
  78. package/types/components/metrics-treemap/metrics-treemap.d.ts +7 -5
  79. package/types/components/metrics-treemap/metrics-treemap.utils.d.ts +4 -0
  80. package/types/hooks/metrics-display-type.d.ts +8 -2
  81. package/types/ui/icon/icon.d.ts +2 -0
@@ -4,44 +4,102 @@ import cx from 'classnames';
4
4
  import { MetricsDisplayType } from '../../constants';
5
5
  import { Button } from '../../ui/button';
6
6
  import { ControlGroup } from '../../ui/control-group';
7
+ import { Dropdown, DropdownItem } from '../../ui/dropdown';
7
8
  import { Icon } from '../../ui/icon';
8
9
  import css from './metrics-display-selector.module.css';
10
+ import { FlexStack } from '../../layout';
11
+
12
+ const DEFAULT_GROUP_BY = '';
13
+
14
+ const METRICS_DISPLAY_MAP = {
15
+ [MetricsDisplayType.TABLE]: {
16
+ glyph: Icon.ICONS.TABLE,
17
+ label: 'Table',
18
+ },
19
+ [MetricsDisplayType.TREEMAP]: {
20
+ glyph: Icon.ICONS.TREEMAP,
21
+ label: 'Treemap',
22
+ },
23
+ };
24
+
25
+ const displayTypes = Object.values(MetricsDisplayType);
9
26
 
10
27
  interface MetricsDisplaySelectorProps {
11
28
  value: MetricsDisplayType;
12
- onSelect: (newValue: MetricsDisplayType) => void;
29
+ groupBy?: string;
30
+ groups?: Partial<Record<MetricsDisplayType, Array<string>>>;
31
+ onSelect: (newValue: MetricsDisplayType, groupBy?: string) => void;
13
32
  }
14
33
 
15
34
  export const MetricsDisplaySelector = (
16
35
  props: MetricsDisplaySelectorProps &
17
36
  Omit<React.ComponentProps<typeof ControlGroup>, 'onSelect' | 'value'>,
18
37
  ) => {
19
- const { className = '', value, onSelect, ...restProps } = props;
38
+ const { value, groupBy = DEFAULT_GROUP_BY, groups = {}, onSelect, ...restProps } = props;
20
39
 
21
40
  return (
22
- <ControlGroup className={cx(css.root, className)} {...restProps}>
23
- <Button
24
- outline
25
- active={value === MetricsDisplayType.TABLE}
26
- size="small"
27
- type="button"
28
- glyph={Icon.ICONS.TABLE}
29
- onClick={() => onSelect(MetricsDisplayType.TABLE)}
30
- className={css.button}
31
- >
32
- Table
33
- </Button>
34
- <Button
35
- outline
36
- active={value === MetricsDisplayType.TREEMAP}
37
- size="small"
38
- type="button"
39
- glyph={Icon.ICONS.TREEMAP}
40
- onClick={() => onSelect(MetricsDisplayType.TREEMAP)}
41
- className={cx(css.button)}
42
- >
43
- Treemap
44
- </Button>
41
+ <ControlGroup {...restProps}>
42
+ {displayTypes.map((displayType, index) => {
43
+ const displayGroups = groups?.[displayType];
44
+ const displayProps = METRICS_DISPLAY_MAP[displayType];
45
+ const isLast = displayTypes.length - 1 === index;
46
+ const isActive = displayType === value;
47
+
48
+ if (!displayGroups || displayGroups.length === 0) {
49
+ return (
50
+ <Button
51
+ outline
52
+ active={isActive}
53
+ size="small"
54
+ type="button"
55
+ glyph={displayProps.glyph}
56
+ onClick={() => onSelect(displayType)}
57
+ className={isLast && css.itemLast}
58
+ key={displayType}
59
+ >
60
+ {displayProps.label}
61
+ </Button>
62
+ );
63
+ }
64
+
65
+ const displayGroupsData = [
66
+ {
67
+ value: DEFAULT_GROUP_BY,
68
+ label: 'No groups',
69
+ },
70
+ ...displayGroups.map((displayGroup) => ({
71
+ value: displayGroup,
72
+ label: `Group by ${displayGroup}`,
73
+ })),
74
+ ];
75
+
76
+ return (
77
+ <Dropdown
78
+ glyph={displayProps.glyph}
79
+ label={displayProps.label}
80
+ className={cx(isLast && css.itemLast, isActive && css.buttonActive)}
81
+ key={displayType}
82
+ >
83
+ {displayGroupsData.map((displayGroupData) => {
84
+ const isGroupActive = isActive && displayGroupData.value === groupBy;
85
+
86
+ return (
87
+ <DropdownItem
88
+ onClick={() => onSelect(displayType, displayGroupData.value)}
89
+ isActive={isGroupActive}
90
+ className={cx(css.dropdownItem, isGroupActive && css.dropdownItemActive)}
91
+ key={displayGroupData.value}
92
+ >
93
+ <FlexStack space="xxxsmall" alignItems="center">
94
+ <Icon glyph={Icon.ICONS.CHECK} size="small" className={css.dropdownItemIcon} />
95
+ <span>{displayGroupData.label}</span>
96
+ </FlexStack>
97
+ </DropdownItem>
98
+ );
99
+ })}
100
+ </Dropdown>
101
+ );
102
+ })}
45
103
  </ControlGroup>
46
104
  );
47
105
  };
@@ -0,0 +1,143 @@
1
+ import { getTreemapNodesGroupedByPath } from '../metrics-treemap.utils';
2
+
3
+ describe('MetricsTreemap / getTreeNodesGroupedByPath', () => {
4
+ test('should group tree ndoes by path', () => {
5
+ const actual = getTreemapNodesGroupedByPath([
6
+ {
7
+ key: './src/app.jsx',
8
+ runs: [{ value: 100 }],
9
+ },
10
+ {
11
+ key: './src/components/header.jsx',
12
+ runs: [{ value: 100 }],
13
+ },
14
+ {
15
+ key: './src/components/footer.jsx',
16
+ runs: [{ value: 100 }],
17
+ },
18
+ {
19
+ key: './src/ui/button.jsx',
20
+ runs: [{ value: 100 }],
21
+ },
22
+ {
23
+ key: './node_modules/react/dist/react.production.js',
24
+ runs: [{ value: 100 }],
25
+ },
26
+ {
27
+ key: 'favicon.ico',
28
+ runs: [{ value: 10 }],
29
+ },
30
+ ] as any);
31
+
32
+ expect(actual).toStrictEqual({
33
+ id: '',
34
+ label: '(root)',
35
+ value: 0,
36
+ children: [
37
+ {
38
+ id: '.',
39
+ label: '.',
40
+ value: 0,
41
+ children: [
42
+ {
43
+ id: './src',
44
+ label: 'src',
45
+ value: 0,
46
+ children: [
47
+ {
48
+ id: './src/app.jsx',
49
+ label: 'app.jsx',
50
+ value: 100,
51
+ item: {
52
+ key: './src/app.jsx',
53
+ runs: [{ value: 100 }],
54
+ },
55
+ },
56
+ {
57
+ id: './src/components',
58
+ label: 'components',
59
+ value: 0,
60
+ children: [
61
+ {
62
+ id: './src/components/header.jsx',
63
+ label: 'header.jsx',
64
+ value: 100,
65
+ item: {
66
+ key: './src/components/header.jsx',
67
+ runs: [{ value: 100 }],
68
+ },
69
+ },
70
+ {
71
+ id: './src/components/footer.jsx',
72
+ label: 'footer.jsx',
73
+ value: 100,
74
+ item: {
75
+ key: './src/components/footer.jsx',
76
+ runs: [{ value: 100 }],
77
+ },
78
+ },
79
+ ],
80
+ },
81
+ {
82
+ id: './src/ui',
83
+ label: 'ui',
84
+ value: 0,
85
+ children: [
86
+ {
87
+ id: './src/ui/button.jsx',
88
+ label: 'button.jsx',
89
+ value: 100,
90
+ item: {
91
+ key: './src/ui/button.jsx',
92
+ runs: [{ value: 100 }],
93
+ },
94
+ },
95
+ ],
96
+ },
97
+ ],
98
+ },
99
+ {
100
+ id: './node_modules',
101
+ label: 'node_modules',
102
+ value: 0,
103
+ children: [
104
+ {
105
+ id: './node_modules/react',
106
+ label: 'react',
107
+ value: 0,
108
+ children: [
109
+ {
110
+ id: './node_modules/react/dist',
111
+ label: 'dist',
112
+ value: 0,
113
+ children: [
114
+ {
115
+ id: './node_modules/react/dist/react.production.js',
116
+ label: 'react.production.js',
117
+ value: 100,
118
+ item: {
119
+ key: './node_modules/react/dist/react.production.js',
120
+ runs: [{ value: 100 }],
121
+ },
122
+ },
123
+ ],
124
+ },
125
+ ],
126
+ },
127
+ ],
128
+ },
129
+ ],
130
+ },
131
+ {
132
+ id: 'favicon.ico',
133
+ label: 'favicon.ico',
134
+ value: 10,
135
+ item: {
136
+ key: 'favicon.ico',
137
+ runs: [{ value: 10 }],
138
+ },
139
+ },
140
+ ],
141
+ });
142
+ });
143
+ });
@@ -1 +1,2 @@
1
1
  export * from './metrics-treemap';
2
+ export { getTreemapNodes, getTreemapNodesGroupedByPath } from './metrics-treemap.utils';
@@ -0,0 +1,43 @@
1
+ import type { ReportMetricRow } from '@bundle-stats/utils';
2
+
3
+ export interface TreeLeaf {
4
+ /**
5
+ * Node id - file/directory path
6
+ */
7
+ id: string;
8
+ /**
9
+ * Node label - file/directory baseline
10
+ */
11
+ label: string;
12
+ /**
13
+ * Node value - the max amount between item run values
14
+ */
15
+ value: number;
16
+ /**
17
+ * Corresponding report metric item
18
+ */
19
+ item: ReportMetricRow;
20
+ }
21
+
22
+ export interface Tree {
23
+ /**
24
+ * Node id - file/directory path
25
+ */
26
+ id: string;
27
+ /**
28
+ * Node label - file/directory baseline
29
+ */
30
+ label: string;
31
+ /**
32
+ * Node value - 0 for groups
33
+ */
34
+ value: number;
35
+ /**
36
+ * List of children nodes
37
+ */
38
+ children: TreeNodeChildren;
39
+ }
40
+
41
+ export type TreeNode = Tree | TreeLeaf;
42
+
43
+ export type TreeNodeChildren = Array<TreeNode>;
@@ -13,15 +13,44 @@
13
13
  top: 0;
14
14
  width: 100%;
15
15
  height: 100%;
16
- fill: var(--color-text);
17
- font-family: var(--font-family);
18
16
  background: var(--color-background);
19
17
  }
20
18
 
21
- /** Tile overlay fill background transition */
22
- .root {
23
- transition: var(--ui-transition-out);
24
- transition-property: opacity;
19
+ /**
20
+ * Tile group
21
+ */
22
+ .tileGroup {
23
+ position: absolute;
24
+ }
25
+
26
+ .tileGroupTitle {
27
+ width: 100%;
28
+ padding: 2px var(--space-xxxsmall);
29
+ color: var(--color-text);
30
+ font-size: 10px;
31
+ line-height: 1em;
32
+ overflow: hidden;
33
+ white-space: nowrap;
34
+ text-overflow: ellipsis;
35
+ }
36
+
37
+ /** Tile group size variation */
38
+ .tileGroupSizeDefault {
39
+ transition: var(--ui-transition-in);
40
+ transition-property: left top width height;
41
+ }
42
+
43
+ /** Tile group nested variation */
44
+ .nested .tileGroup {
45
+ background: rgba(128, 128, 128, 0.05);
46
+ }
47
+
48
+ .nested .tileGroup:hover {
49
+ background: rgba(128, 128, 128, 0.08);
50
+ }
51
+
52
+ .nested .tileGroup:hover .tileGroupTitle {
53
+ color: var(--color-text-dark);
25
54
  }
26
55
 
27
56
  /** Leaf wrapper button */
@@ -31,7 +60,7 @@
31
60
  border: 0;
32
61
  border-radius: 0;
33
62
  padding: 0;
34
- background: none;
63
+ background: var(--color-background);
35
64
  display: flex;
36
65
  align-items: center;
37
66
  font-family: var(--font-family-fixed);
@@ -57,12 +86,9 @@
57
86
  opacity: 0.75;
58
87
  }
59
88
 
60
- .tile:hover {
61
- transition: var(--ui-transition-in);
62
- }
63
-
64
89
  .tile:hover::before {
65
90
  transition: var(--ui-transition-in);
91
+ transition-property: opacity;
66
92
  opacity: 0.5;
67
93
  }
68
94
 
@@ -84,7 +110,7 @@
84
110
  padding-left: var(--space-small);
85
111
  }
86
112
 
87
- .leadContentLabel {
113
+ .tileContentLabel {
88
114
  flex: 0 1 auto;
89
115
  width: 100%;
90
116
  min-width: 0;
@@ -92,32 +118,37 @@
92
118
  overflow: hidden;
93
119
  }
94
120
 
95
- .leadContentValue {
121
+ .tileContentValue {
96
122
  flex: 0 0 auto;
97
123
  max-width: calc(100% + var(--space-xxsmall));
98
124
  white-space: nowrap;
99
125
  overflow: hidden;
100
126
  }
101
127
 
102
- .leadContentMetric {
128
+ .tileContentMetric {
103
129
  font-weight: bold;
104
130
  }
105
131
 
106
- .leadContentDelta {
132
+ .tileContentDelta {
107
133
  margin-left: var(--space-xxxsmall);
108
134
  }
109
135
 
110
136
  /** Tile size variation */
111
- .tileSmall .tileContent {
137
+ .tileSizeSmall .tileContent {
112
138
  padding: var(--space-xxxsmall) var(--space-xxsmall);
113
139
  gap: 0;
114
140
  font-size: var(--size-xsmall);
115
141
  }
116
142
 
117
- .tileSmall .leadContentLabel {
143
+ .tileSizeSmall .tileContentLabel {
118
144
  margin-top: 0;
119
145
  }
120
146
 
147
+ .tileSizeDefault {
148
+ transition: var(--ui-transition-in);
149
+ transition-property: left top width height;
150
+ }
151
+
121
152
  /** Tile diff variation */
122
153
  .tile-NO_CHANGE {
123
154
  color: var(--color-text-light);
@@ -125,7 +156,7 @@
125
156
 
126
157
  .tile-NO_CHANGE::before {
127
158
  color: var(--color-gray-50);
128
- opacity: 0.4;
159
+ opacity: 0.3;
129
160
  }
130
161
 
131
162
  .tile-NO_CHANGE:hover::before {
@@ -193,17 +224,16 @@
193
224
  pointer-events: none;
194
225
  }
195
226
 
196
- /* prevent tooltip portal wrapper from capturing events */
197
- div[role="presentation"]:has(.tooltip) {
198
- pointer-events: none;
199
- }
200
-
201
-
202
227
  .tileTooltipArrow svg {
203
228
  display: block;
204
229
  fill: var(--color-background) !important;
205
230
  }
206
231
 
207
232
  .tileTooltipContentTitle {
208
- font-size: var(--size-medium);
233
+ font-size: var(--size-small);
234
+ }
235
+
236
+ /* prevent tooltip portal wrapper from capturing events */
237
+ div[role="presentation"]:has(.tooltip) {
238
+ pointer-events: none;
209
239
  }
@@ -2,22 +2,56 @@ import React from 'react';
2
2
  import { Meta, StoryObj } from '@storybook/react';
3
3
  // eslint-disable-next-line import/no-extraneous-dependencies
4
4
  import { action } from '@storybook/addon-actions';
5
+ import { createJobs } from '@bundle-stats/utils';
6
+ import * as webpack from '@bundle-stats/utils/lib-esm/webpack';
5
7
 
6
- // eslint-disable-next-line import/no-relative-packages
8
+ /* eslint-disable import/no-relative-packages */
9
+ import baselineData from '../../../../../fixtures/webpack-stats.baseline.json';
10
+ import currentData from '../../../../../fixtures/webpack-stats.current.json';
7
11
  import moduleItemsFixture from '../../../../../fixtures/module-items.json';
8
- import { MetricsTreemap } from '.';
12
+ /* eslint-enable import/no-relative-packages */
13
+ import { MetricsTreemap, getTreemapNodes, getTreemapNodesGroupedByPath } from '.';
9
14
 
10
- export default {
15
+ const meta: Meta<typeof MetricsTreemap> = {
11
16
  title: 'Components/MetricsTreemap',
12
17
  component: MetricsTreemap,
13
- } as Meta;
14
-
15
- export const Default: StoryObj = {
16
- render: (props: React.ComponentProps<typeof MetricsTreemap>) => (
17
- <MetricsTreemap style={{ maxWidth: 'var(--max-width)' }} {...props} />
18
- ),
19
18
  args: {
20
- items: moduleItemsFixture,
21
19
  onItemClick: action('CLICK'),
22
20
  },
23
21
  };
22
+
23
+ type Story = StoryObj<typeof meta>;
24
+
25
+ export default meta;
26
+
27
+ const MULTIPLE_JOBS = createJobs([{ webpack: currentData }, { webpack: baselineData }]);
28
+
29
+ export const Assets: Story = {
30
+ render: (props) => <MetricsTreemap style={{ maxWidth: 'var(--max-width)' }} {...props} />,
31
+ args: {
32
+ treeNodes: getTreemapNodes(webpack.compareBySection.assets(MULTIPLE_JOBS)),
33
+ },
34
+ };
35
+
36
+ export const AssetsNested: Story = {
37
+ render: (props) => <MetricsTreemap style={{ maxWidth: 'var(--max-width)' }} {...props} />,
38
+ args: {
39
+ treeNodes: getTreemapNodesGroupedByPath(webpack.compareBySection.assets(MULTIPLE_JOBS)),
40
+ nested: true,
41
+ },
42
+ };
43
+
44
+ export const Modules: Story = {
45
+ render: (props) => <MetricsTreemap style={{ maxWidth: 'var(--max-width)' }} {...props} />,
46
+ args: {
47
+ treeNodes: getTreemapNodes(moduleItemsFixture),
48
+ },
49
+ };
50
+
51
+ export const ModulesNested: Story = {
52
+ render: (props) => <MetricsTreemap style={{ maxWidth: 'var(--max-width)' }} {...props} />,
53
+ args: {
54
+ treeNodes: getTreemapNodesGroupedByPath(moduleItemsFixture),
55
+ nested: true,
56
+ },
57
+ };