@bundle-stats/ui 4.13.0-beta.9 → 4.13.0-rc.1

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 (247) hide show
  1. package/lib/app/app.js +4 -8
  2. package/lib/app/app.js.map +1 -1
  3. package/lib/app/app.module.css +2 -2
  4. package/lib/assets/icons.svg +70 -67
  5. package/lib/assets/icons.svg.js +14 -12
  6. package/lib/assets/icons.svg.js.map +1 -1
  7. package/lib/components/bundle-assets/bundle-assets.js +61 -26
  8. package/lib/components/bundle-assets/bundle-assets.js.map +1 -1
  9. package/lib/components/bundle-assets/bundle-assets.module.css +0 -5
  10. package/lib/components/bundle-assets/index.js +3 -1
  11. package/lib/components/bundle-assets/index.js.map +1 -1
  12. package/lib/components/bundle-assets-totals/bundle-assets-totals.js +14 -12
  13. package/lib/components/bundle-assets-totals/bundle-assets-totals.js.map +1 -1
  14. package/lib/components/bundle-modules/bundle-modules.i18n.js +1 -1
  15. package/lib/components/bundle-modules/bundle-modules.js +42 -15
  16. package/lib/components/bundle-modules/bundle-modules.js.map +1 -1
  17. package/lib/components/bundle-modules/bundle-modules.module.css +0 -5
  18. package/lib/components/bundle-modules/bundle-modules.utils.js +2 -13
  19. package/lib/components/bundle-modules/bundle-modules.utils.js.map +1 -1
  20. package/lib/components/bundle-modules/index.js +2 -1
  21. package/lib/components/bundle-modules/index.js.map +1 -1
  22. package/lib/components/bundle-packages/bundle-packages.js +48 -9
  23. package/lib/components/bundle-packages/bundle-packages.js.map +1 -1
  24. package/lib/components/bundle-packages/bundle-packages.module.css +0 -5
  25. package/lib/components/delta/delta.js +1 -1
  26. package/lib/components/delta/delta.js.map +1 -1
  27. package/lib/components/delta/delta.module.css +10 -1
  28. package/lib/components/jobs-header/jobs-header.module.css +0 -4
  29. package/lib/components/metrics-display-selector/metrics-display-selector.js +41 -4
  30. package/lib/components/metrics-display-selector/metrics-display-selector.js.map +1 -1
  31. package/lib/components/metrics-display-selector/metrics-display-selector.module.css +15 -7
  32. package/lib/components/metrics-treemap/index.js +4 -0
  33. package/lib/components/metrics-treemap/index.js.map +1 -1
  34. package/lib/components/metrics-treemap/metrics-treemap.constants.js +10 -0
  35. package/lib/components/metrics-treemap/metrics-treemap.constants.js.map +1 -0
  36. package/lib/components/metrics-treemap/metrics-treemap.i18n.js +6 -0
  37. package/lib/components/metrics-treemap/metrics-treemap.i18n.js.map +1 -0
  38. package/lib/components/metrics-treemap/metrics-treemap.js +131 -95
  39. package/lib/components/metrics-treemap/metrics-treemap.js.map +1 -1
  40. package/lib/components/metrics-treemap/metrics-treemap.module.css +179 -54
  41. package/lib/components/metrics-treemap/metrics-treemap.utils.js +168 -0
  42. package/lib/components/metrics-treemap/metrics-treemap.utils.js.map +1 -0
  43. package/lib/components/module-info/module-info.js +1 -2
  44. package/lib/components/module-info/module-info.js.map +1 -1
  45. package/lib/components/run-info/run-info.module.css +7 -7
  46. package/lib/css/variables.css +14 -13
  47. package/lib/hooks/metrics-display-type.js +45 -24
  48. package/lib/hooks/metrics-display-type.js.map +1 -1
  49. package/lib/hooks/search-params.js +1 -0
  50. package/lib/hooks/search-params.js.map +1 -1
  51. package/lib/layout/box/box.module.css +4 -2
  52. package/lib/layout/flex-stack/flex-stack.js +3 -4
  53. package/lib/layout/flex-stack/flex-stack.js.map +1 -1
  54. package/lib/layout/flex-stack/flex-stack.module.css +19 -23
  55. package/lib/ui/button/button.js +5 -4
  56. package/lib/ui/button/button.js.map +1 -1
  57. package/lib/ui/button/button.module.css +55 -53
  58. package/lib/ui/dropdown/dropdown.js +2 -2
  59. package/lib/ui/dropdown/dropdown.js.map +1 -1
  60. package/lib/ui/filters/filters.js +2 -2
  61. package/lib/ui/filters/filters.js.map +1 -1
  62. package/lib/ui/filters/filters.module.css +8 -1
  63. package/lib/ui/hover-card/hover-card.js +1 -1
  64. package/lib/ui/hover-card/hover-card.js.map +1 -1
  65. package/lib/ui/icon/icon.js +2 -1
  66. package/lib/ui/icon/icon.js.map +1 -1
  67. package/lib/ui/input-search/input-search.js +1 -1
  68. package/lib/ui/input-search/input-search.js.map +1 -1
  69. package/lib/ui/loader/loader.js +1 -1
  70. package/lib/ui/loader/loader.js.map +1 -1
  71. package/lib/ui/table/table.module.css +30 -0
  72. package/lib/ui/tag/tag.module.css +3 -3
  73. package/lib/ui/toolbar/toolbar.module.css +1 -3
  74. package/lib/utils/colors.js +22 -0
  75. package/lib/utils/colors.js.map +1 -0
  76. package/lib/{utils.js → utils/components.js} +2 -17
  77. package/lib/utils/components.js.map +1 -0
  78. package/lib/utils/index.js +20 -0
  79. package/lib/utils/index.js.map +1 -0
  80. package/lib/utils/jobs.js +18 -0
  81. package/lib/utils/jobs.js.map +1 -0
  82. package/lib-esm/app/app.js +4 -8
  83. package/lib-esm/app/app.js.map +1 -1
  84. package/lib-esm/app/app.module.css +2 -2
  85. package/lib-esm/assets/icons.svg +70 -67
  86. package/lib-esm/assets/icons.svg.js +14 -12
  87. package/lib-esm/assets/icons.svg.js.map +1 -1
  88. package/lib-esm/components/bundle-assets/bundle-assets.js +62 -28
  89. package/lib-esm/components/bundle-assets/bundle-assets.js.map +1 -1
  90. package/lib-esm/components/bundle-assets/bundle-assets.module.css +0 -5
  91. package/lib-esm/components/bundle-assets/index.js +3 -1
  92. package/lib-esm/components/bundle-assets/index.js.map +1 -1
  93. package/lib-esm/components/bundle-assets-totals/bundle-assets-totals.js +15 -13
  94. package/lib-esm/components/bundle-assets-totals/bundle-assets-totals.js.map +1 -1
  95. package/lib-esm/components/bundle-modules/bundle-modules.i18n.js +1 -1
  96. package/lib-esm/components/bundle-modules/bundle-modules.js +43 -16
  97. package/lib-esm/components/bundle-modules/bundle-modules.js.map +1 -1
  98. package/lib-esm/components/bundle-modules/bundle-modules.module.css +0 -5
  99. package/lib-esm/components/bundle-modules/bundle-modules.utils.js +1 -11
  100. package/lib-esm/components/bundle-modules/bundle-modules.utils.js.map +1 -1
  101. package/lib-esm/components/bundle-modules/index.js +3 -2
  102. package/lib-esm/components/bundle-modules/index.js.map +1 -1
  103. package/lib-esm/components/bundle-packages/bundle-packages.js +49 -10
  104. package/lib-esm/components/bundle-packages/bundle-packages.js.map +1 -1
  105. package/lib-esm/components/bundle-packages/bundle-packages.module.css +0 -5
  106. package/lib-esm/components/delta/delta.js +1 -1
  107. package/lib-esm/components/delta/delta.js.map +1 -1
  108. package/lib-esm/components/delta/delta.module.css +10 -1
  109. package/lib-esm/components/jobs-header/jobs-header.module.css +0 -4
  110. package/lib-esm/components/metrics-display-selector/metrics-display-selector.js +41 -4
  111. package/lib-esm/components/metrics-display-selector/metrics-display-selector.js.map +1 -1
  112. package/lib-esm/components/metrics-display-selector/metrics-display-selector.module.css +15 -7
  113. package/lib-esm/components/metrics-treemap/index.js +1 -0
  114. package/lib-esm/components/metrics-treemap/index.js.map +1 -1
  115. package/lib-esm/components/metrics-treemap/metrics-treemap.constants.js +7 -0
  116. package/lib-esm/components/metrics-treemap/metrics-treemap.constants.js.map +1 -0
  117. package/lib-esm/components/metrics-treemap/metrics-treemap.i18n.js +3 -0
  118. package/lib-esm/components/metrics-treemap/metrics-treemap.i18n.js.map +1 -0
  119. package/lib-esm/components/metrics-treemap/metrics-treemap.js +134 -98
  120. package/lib-esm/components/metrics-treemap/metrics-treemap.js.map +1 -1
  121. package/lib-esm/components/metrics-treemap/metrics-treemap.module.css +179 -54
  122. package/lib-esm/components/metrics-treemap/metrics-treemap.utils.js +162 -0
  123. package/lib-esm/components/metrics-treemap/metrics-treemap.utils.js.map +1 -0
  124. package/lib-esm/components/module-info/module-info.js +1 -2
  125. package/lib-esm/components/module-info/module-info.js.map +1 -1
  126. package/lib-esm/components/run-info/run-info.module.css +7 -7
  127. package/lib-esm/css/variables.css +14 -13
  128. package/lib-esm/hooks/metrics-display-type.js +46 -24
  129. package/lib-esm/hooks/metrics-display-type.js.map +1 -1
  130. package/lib-esm/hooks/search-params.js +1 -0
  131. package/lib-esm/hooks/search-params.js.map +1 -1
  132. package/lib-esm/layout/box/box.module.css +4 -2
  133. package/lib-esm/layout/flex-stack/flex-stack.js +3 -4
  134. package/lib-esm/layout/flex-stack/flex-stack.js.map +1 -1
  135. package/lib-esm/layout/flex-stack/flex-stack.module.css +19 -23
  136. package/lib-esm/ui/button/button.js +5 -4
  137. package/lib-esm/ui/button/button.js.map +1 -1
  138. package/lib-esm/ui/button/button.module.css +55 -53
  139. package/lib-esm/ui/dropdown/dropdown.js +2 -2
  140. package/lib-esm/ui/dropdown/dropdown.js.map +1 -1
  141. package/lib-esm/ui/filters/filters.js +2 -2
  142. package/lib-esm/ui/filters/filters.js.map +1 -1
  143. package/lib-esm/ui/filters/filters.module.css +8 -1
  144. package/lib-esm/ui/hover-card/hover-card.js +1 -1
  145. package/lib-esm/ui/hover-card/hover-card.js.map +1 -1
  146. package/lib-esm/ui/icon/icon.js +2 -1
  147. package/lib-esm/ui/icon/icon.js.map +1 -1
  148. package/lib-esm/ui/input-search/input-search.js +1 -1
  149. package/lib-esm/ui/input-search/input-search.js.map +1 -1
  150. package/lib-esm/ui/loader/loader.js +1 -1
  151. package/lib-esm/ui/loader/loader.js.map +1 -1
  152. package/lib-esm/ui/table/table.module.css +30 -0
  153. package/lib-esm/ui/tag/tag.module.css +3 -3
  154. package/lib-esm/ui/toolbar/toolbar.module.css +1 -3
  155. package/lib-esm/utils/colors.js +15 -0
  156. package/lib-esm/utils/colors.js.map +1 -0
  157. package/lib-esm/utils/components.js +16 -0
  158. package/lib-esm/utils/components.js.map +1 -0
  159. package/lib-esm/utils/index.js +4 -0
  160. package/lib-esm/utils/index.js.map +1 -0
  161. package/lib-esm/utils/jobs.js +11 -0
  162. package/lib-esm/utils/jobs.js.map +1 -0
  163. package/package.json +9 -9
  164. package/src/app/app.jsx +4 -12
  165. package/src/app/app.module.css +2 -2
  166. package/src/assets/icons/check.svg +4 -0
  167. package/src/assets/icons.svg +70 -67
  168. package/src/assets/icons.svg.jsx +12 -12
  169. package/src/components/bundle-assets/bundle-assets.jsx +147 -66
  170. package/src/components/bundle-assets/bundle-assets.module.css +0 -5
  171. package/src/components/bundle-assets/index.jsx +4 -0
  172. package/src/components/bundle-assets-totals/bundle-assets-totals.jsx +53 -46
  173. package/src/components/bundle-modules/bundle-modules.i18n.ts +1 -1
  174. package/src/components/bundle-modules/bundle-modules.module.css +0 -5
  175. package/src/components/bundle-modules/bundle-modules.tsx +140 -56
  176. package/src/components/bundle-modules/bundle-modules.types.d.ts +2 -17
  177. package/src/components/bundle-modules/bundle-modules.utils.ts +7 -18
  178. package/src/components/bundle-modules/index.tsx +4 -3
  179. package/src/components/bundle-packages/bundle-packages.jsx +89 -20
  180. package/src/components/bundle-packages/bundle-packages.module.css +0 -5
  181. package/src/components/delta/delta.jsx +1 -1
  182. package/src/components/delta/delta.module.css +10 -1
  183. package/src/components/jobs-header/jobs-header.module.css +0 -4
  184. package/src/components/metrics-display-selector/metrics-display-selector.module.css +15 -7
  185. package/src/components/metrics-display-selector/metrics-display-selector.stories.tsx +33 -0
  186. package/src/components/metrics-display-selector/metrics-display-selector.tsx +83 -25
  187. package/src/components/metrics-treemap/__tests__/metrics-treemap.utils.ts +188 -0
  188. package/src/components/metrics-treemap/index.ts +1 -0
  189. package/src/components/metrics-treemap/metrics-treemap.constants.ts +63 -0
  190. package/src/components/metrics-treemap/metrics-treemap.i18n.ts +2 -0
  191. package/src/components/metrics-treemap/metrics-treemap.module.css +179 -54
  192. package/src/components/metrics-treemap/metrics-treemap.stories.tsx +44 -10
  193. package/src/components/metrics-treemap/metrics-treemap.tsx +453 -184
  194. package/src/components/metrics-treemap/metrics-treemap.utils.ts +268 -0
  195. package/src/components/module-info/module-info.tsx +1 -1
  196. package/src/components/run-info/run-info.module.css +7 -7
  197. package/src/css/variables.css +14 -13
  198. package/src/hooks/metrics-display-type.ts +79 -29
  199. package/src/hooks/search-params.js +1 -0
  200. package/src/layout/box/box.module.css +4 -2
  201. package/src/layout/flex-stack/flex-stack.module.css +19 -23
  202. package/src/layout/flex-stack/flex-stack.tsx +2 -8
  203. package/src/ui/button/button.module.css +55 -53
  204. package/src/ui/button/button.stories.tsx +16 -9
  205. package/src/ui/button/button.tsx +8 -7
  206. package/src/ui/dropdown/dropdown.tsx +2 -3
  207. package/src/ui/filters/filters.module.css +8 -1
  208. package/src/ui/filters/filters.tsx +2 -3
  209. package/src/ui/hover-card/hover-card.tsx +1 -1
  210. package/src/ui/icon/icon.tsx +2 -1
  211. package/src/ui/input-search/input-search.tsx +1 -0
  212. package/src/ui/loader/loader.jsx +1 -1
  213. package/src/ui/table/table.module.css +30 -0
  214. package/src/ui/tag/tag.module.css +3 -3
  215. package/src/ui/toolbar/toolbar.module.css +1 -3
  216. package/src/utils/colors.ts +19 -0
  217. package/src/utils/components.tsx +19 -0
  218. package/src/utils/index.ts +3 -0
  219. package/src/utils/jobs.ts +22 -0
  220. package/types/components/bundle-assets/bundle-assets.d.ts +1 -5
  221. package/types/components/bundle-modules/bundle-modules.d.ts +4 -2
  222. package/types/components/bundle-modules/bundle-modules.i18n.d.ts +1 -1
  223. package/types/components/bundle-modules/bundle-modules.utils.d.ts +3 -7
  224. package/types/components/bundle-modules/index.d.ts +2 -2
  225. package/types/components/delta/delta.d.ts +2 -2
  226. package/types/components/metrics-display-selector/metrics-display-selector.d.ts +3 -1
  227. package/types/components/metrics-treemap/index.d.ts +1 -0
  228. package/types/components/metrics-treemap/metrics-treemap.constants.d.ts +28 -0
  229. package/types/components/metrics-treemap/metrics-treemap.d.ts +7 -5
  230. package/types/components/metrics-treemap/metrics-treemap.i18n.d.ts +2 -0
  231. package/types/components/metrics-treemap/metrics-treemap.utils.d.ts +15 -0
  232. package/types/hooks/metrics-display-type.d.ts +8 -2
  233. package/types/ui/dropdown/dropdown.d.ts +0 -1
  234. package/types/ui/icon/icon.d.ts +4 -2
  235. package/types/ui/loader/loader.d.ts +2 -2
  236. package/types/utils/colors.d.ts +1 -0
  237. package/types/utils/components.d.ts +2 -0
  238. package/types/utils/index.d.ts +3 -0
  239. package/types/utils/jobs.d.ts +7 -0
  240. package/lib/components/bundle-assets-totals/bundle-assets-totals.module.css +0 -7
  241. package/lib/utils.js.map +0 -1
  242. package/lib-esm/components/bundle-assets-totals/bundle-assets-totals.module.css +0 -7
  243. package/lib-esm/utils.js +0 -30
  244. package/lib-esm/utils.js.map +0 -1
  245. package/src/components/bundle-assets-totals/bundle-assets-totals.module.css +0 -7
  246. package/src/utils.jsx +0 -38
  247. package/types/utils.d.ts +0 -3
@@ -1,13 +1,15 @@
1
1
  import React, { useCallback, useMemo } from 'react';
2
2
  import cx from 'classnames';
3
- import { SECTIONS, COMPONENT } from '@bundle-stats/utils';
3
+ import { SECTIONS, COMPONENT, type Job } from '@bundle-stats/utils';
4
4
 
5
+ import { WebpackChunk } from '@bundle-stats/utils';
5
6
  import { SortAction } from '../../types';
6
7
  import config from '../../config.json';
7
8
  import I18N from '../../i18n';
8
- import { Box } from '../../layout/box';
9
9
  import { MetricsDisplayType } from '../../constants';
10
+ import { Box } from '../../layout/box';
10
11
  import { FlexStack } from '../../layout/flex-stack';
12
+ import { Stack } from '../../layout/stack';
11
13
  import { Button } from '../../ui/button';
12
14
  import { ControlGroup } from '../../ui/control-group';
13
15
  import { EmptySet } from '../../ui/empty-set';
@@ -21,18 +23,22 @@ import { Tooltip } from '../../ui/tooltip';
21
23
  import { ComponentLink } from '../component-link';
22
24
  import { MetricsTable } from '../metrics-table';
23
25
  import { MetricsTableHeader } from '../metrics-table-header';
24
- import { MetricsTreemap } from '../metrics-treemap';
26
+ import { MetricsTreemap, getTreemapNodes, getTreemapNodesGroupedByPath } from '../metrics-treemap';
25
27
  import { MetricsTableOptions } from '../metrics-table-options';
26
28
  import { MetricsDisplaySelector } from '../metrics-display-selector';
27
29
  import { MetricsTableTitle } from '../metrics-table-title';
28
30
  import { ModuleInfo } from '../module-info';
29
31
  import { generateFilterFieldsData } from './bundle-modules.utils';
30
32
  import { ModuleMetric } from './bundle-modules.constants';
31
- import type { Chunk, Job, ReportMetricModuleRow } from './bundle-modules.types';
33
+ import type { ReportMetricModuleRow } from './bundle-modules.types';
32
34
  import * as I18N_MODULES from './bundle-modules.i18n';
33
35
  import css from './bundle-modules.module.css';
34
36
  import { useMetricsDisplayType } from '../../hooks/metrics-display-type';
35
37
 
38
+ const DISPLAY_TYPE_GROUPS = {
39
+ [MetricsDisplayType.TREEMAP]: ['folder'],
40
+ };
41
+
36
42
  interface RowHeaderProps {
37
43
  row: ReportMetricModuleRow;
38
44
  filters?: any;
@@ -60,11 +66,83 @@ const RowHeader = (props: RowHeaderProps) => {
60
66
  );
61
67
  };
62
68
 
69
+ interface ViewMetricsTreemapProps {
70
+ metricsTableTitle: React.ReactNode;
71
+ jobs: Array<any>;
72
+ items: Array<any>;
73
+ displayType: { value: MetricsDisplayType; groupBy?: string };
74
+ emptyMessage: React.ReactNode;
75
+ showEntryInfo: React.ComponentProps<typeof MetricsTreemap>['onItemClick'];
76
+ updateSearch: (newSerarch: string) => void;
77
+ search: string;
78
+ }
79
+
80
+ const ViewMetricsTreemap = (props: ViewMetricsTreemapProps) => {
81
+ const {
82
+ metricsTableTitle,
83
+ jobs,
84
+ items,
85
+ displayType,
86
+ emptyMessage,
87
+ showEntryInfo,
88
+ updateSearch,
89
+ search,
90
+ } = props;
91
+
92
+ // Get treenodes based on group
93
+ const treeNodes = useMemo(() => {
94
+ if (displayType.groupBy === 'folder') {
95
+ return getTreemapNodesGroupedByPath(items);
96
+ }
97
+
98
+ return getTreemapNodes(items);
99
+ }, [items, displayType]);
100
+
101
+ // Search based on the group path on group title click
102
+ const onGroupClick = useCallback(
103
+ (groupPath: string) => {
104
+ // Clear seach when groupPath is emty (root)
105
+ if (groupPath === '') {
106
+ updateSearch('');
107
+ return;
108
+ }
109
+ // Search by group path
110
+ // 1. use `^` to match only the string beggining
111
+ // 2. add `/` suffix to exactly match the directory
112
+ const newSearch = `^${groupPath}/`;
113
+
114
+ // Reset search when toggling the same groupPath
115
+ if (newSearch === search) {
116
+ updateSearch('');
117
+ return;
118
+ }
119
+
120
+ updateSearch(newSearch);
121
+ },
122
+ [updateSearch, search],
123
+ );
124
+
125
+ return (
126
+ <>
127
+ <Table compact>
128
+ <MetricsTableHeader metricTitle={metricsTableTitle} showSum jobs={jobs} rows={items} />
129
+ </Table>
130
+ <MetricsTreemap
131
+ treeNodes={treeNodes}
132
+ nested={Boolean(displayType.groupBy)}
133
+ emptyMessage={emptyMessage}
134
+ onItemClick={showEntryInfo}
135
+ onGroupClick={onGroupClick}
136
+ />
137
+ </>
138
+ );
139
+ };
140
+
63
141
  interface BundleModulesProps extends React.ComponentProps<'div'> {
64
142
  jobs?: Array<Job>;
65
143
  items?: Array<ReportMetricModuleRow>;
66
144
  allItems?: Array<ReportMetricModuleRow>;
67
- chunks?: Array<Chunk>;
145
+ chunks?: Array<WebpackChunk>;
68
146
 
69
147
  totalRowCount: number;
70
148
 
@@ -116,7 +194,7 @@ export const BundleModules = (props: BundleModulesProps) => {
116
194
  const rootClassName = cx(css.root, className);
117
195
  const jobLabels = jobs?.map((job) => job?.label);
118
196
 
119
- const [displayType, setDisplayType] = useMetricsDisplayType();
197
+ const [displayType, setDisplayType] = useMetricsDisplayType(DISPLAY_TYPE_GROUPS);
120
198
 
121
199
  const filterFieldsData = useMemo(
122
200
  () => generateFilterFieldsData({ filters, chunks, compareMode: jobs.length > 1 }),
@@ -170,12 +248,17 @@ export const BundleModules = (props: BundleModulesProps) => {
170
248
 
171
249
  return (
172
250
  <>
173
- <div className={rootClassName}>
251
+ <Stack space="xsmall" as="section" className={rootClassName}>
174
252
  <Toolbar
175
253
  className={css.toolbar}
176
254
  renderActions={({ actionClassName }) => (
177
255
  <FlexStack space="xxsmall" className={cx(css.dropdown, actionClassName)}>
178
- <MetricsDisplaySelector onSelect={setDisplayType} value={displayType} />
256
+ <MetricsDisplaySelector
257
+ onSelect={setDisplayType}
258
+ value={displayType.value}
259
+ groupBy={displayType.groupBy}
260
+ groups={DISPLAY_TYPE_GROUPS}
261
+ />
179
262
  <MetricsTableOptions
180
263
  handleViewAll={resetAllFilters}
181
264
  handleResetFilters={resetFilters}
@@ -197,55 +280,56 @@ export const BundleModules = (props: BundleModulesProps) => {
197
280
  />
198
281
  </FlexStack>
199
282
  </Toolbar>
200
- <Box padding={['xsmall', 'small']} className={css.metricSelector}>
201
- <ControlGroup as="nav">
202
- <Button
203
- outline
204
- active={moduleMetric === ModuleMetric.SIZE}
205
- size="small"
206
- type="button"
207
- onClick={() => setModuleMetric(ModuleMetric.SIZE)}
208
- >
209
- <Tooltip title="Size (excluding duplicate modules)">Module size</Tooltip>
210
- </Button>
211
- <Button
212
- outline
213
- active={moduleMetric === ModuleMetric.TOTAL_SIZE}
214
- size="small"
215
- type="button"
216
- onClick={() => setModuleMetric(ModuleMetric.TOTAL_SIZE)}
217
- >
218
- <Tooltip title="Size (including duplicate modules)">Module total size</Tooltip>
219
- </Button>
220
- </ControlGroup>
283
+ <Box outline as="main">
284
+ <Box padding={['xsmall', 'small']} className={css.metricSelector}>
285
+ <ControlGroup as="nav">
286
+ <Button
287
+ outline
288
+ active={moduleMetric === ModuleMetric.SIZE}
289
+ size="small"
290
+ type="button"
291
+ onClick={() => setModuleMetric(ModuleMetric.SIZE)}
292
+ >
293
+ <Tooltip title="Size (excluding duplicate modules)">Module size</Tooltip>
294
+ </Button>
295
+ <Button
296
+ outline
297
+ active={moduleMetric === ModuleMetric.TOTAL_SIZE}
298
+ size="small"
299
+ type="button"
300
+ onClick={() => setModuleMetric(ModuleMetric.TOTAL_SIZE)}
301
+ >
302
+ <Tooltip title="Size (including duplicate modules)">Module total size</Tooltip>
303
+ </Button>
304
+ </ControlGroup>
305
+ </Box>
306
+ {displayType.value === MetricsDisplayType.TABLE && (
307
+ <MetricsTable
308
+ className={css.table}
309
+ items={items}
310
+ runs={jobs}
311
+ renderRowHeader={renderRowHeader}
312
+ emptyMessage={emptyMessage}
313
+ showHeaderSum
314
+ title={metricsTableTitle}
315
+ sort={sort}
316
+ updateSort={updateSort}
317
+ />
318
+ )}
319
+ {displayType.value === MetricsDisplayType.TREEMAP && (
320
+ <ViewMetricsTreemap
321
+ metricsTableTitle={metricsTableTitle}
322
+ jobs={jobs}
323
+ items={items}
324
+ displayType={displayType}
325
+ emptyMessage={emptyMessage}
326
+ showEntryInfo={showEntryInfo}
327
+ updateSearch={updateSearch}
328
+ search={search}
329
+ />
330
+ )}
221
331
  </Box>
222
- {displayType === MetricsDisplayType.TABLE && (
223
- <MetricsTable
224
- className={css.table}
225
- items={items}
226
- runs={jobs}
227
- renderRowHeader={renderRowHeader}
228
- emptyMessage={emptyMessage}
229
- showHeaderSum
230
- title={metricsTableTitle}
231
- sort={sort}
232
- updateSort={updateSort}
233
- />
234
- )}
235
- {displayType === MetricsDisplayType.TREEMAP && (
236
- <>
237
- <Table compact>
238
- <MetricsTableHeader
239
- metricTitle={metricsTableTitle}
240
- showSum
241
- jobs={jobs}
242
- rows={items}
243
- />
244
- </Table>
245
- <MetricsTreemap emptyMessage={emptyMessage} items={items} onItemClick={showEntryInfo} />
246
- </>
247
- )}
248
- </div>
332
+ </Stack>
249
333
  {entryItem && (
250
334
  <ModuleInfo
251
335
  className={css.moduleInfo}
@@ -1,10 +1,5 @@
1
- import { Module } from '@bundle-stats/utils/lib-esm/webpack/types';
2
- import { ReportMetricRun } from '@bundle-stats/utils/types/report/types';
3
-
4
- export interface Chunk {
5
- id: string;
6
- name: string;
7
- }
1
+ import type { Module } from '@bundle-stats/utils/lib-esm/webpack/types';
2
+ import type { ReportMetricRun } from '@bundle-stats/utils/types/report/types';
8
3
 
9
4
  export type ReportMetricModuleRow = {
10
5
  thirdParty: boolean;
@@ -12,13 +7,3 @@ export type ReportMetricModuleRow = {
12
7
  fileType: string;
13
8
  } & ReportMetricRun &
14
9
  Module;
15
-
16
- export interface Job {
17
- label: string;
18
- internalBuildNumber: number;
19
- meta?: {
20
- webpack?: {
21
- chunks?: Array<Chunk>;
22
- };
23
- };
24
- }
@@ -2,12 +2,11 @@ import intersection from 'lodash/intersection';
2
2
  import get from 'lodash/get';
3
3
  import isEmpty from 'lodash/isEmpty';
4
4
  import union from 'lodash/union';
5
- import uniqBy from 'lodash/uniqBy';
6
5
  import orderBy from 'lodash/orderBy';
7
- import type { MetricRunInfo, ReportMetricRow } from '@bundle-stats/utils';
8
- // @ts-ignore
9
- import { MODULE_PATH_PACKAGES, type Module } from '@bundle-stats/utils/lib-esm/webpack';
10
6
  import {
7
+ type WebpackChunk,
8
+ type MetricRunInfo,
9
+ type ReportMetricRow,
11
10
  FILE_TYPE_LABELS,
12
11
  MODULE_CHUNK,
13
12
  MODULE_FILTERS,
@@ -19,10 +18,12 @@ import {
19
18
  getModuleSourceTypeFilters,
20
19
  getModuleFileTypeFilters,
21
20
  } from '@bundle-stats/utils';
21
+ // @ts-ignore
22
+ import { MODULE_PATH_PACKAGES, type Module } from '@bundle-stats/utils/lib-esm/webpack';
22
23
  import type { ReportMetricRun } from '@bundle-stats/utils/types/report/types';
23
24
 
24
25
  import type { FilterFieldsData, FilterGroupFieldData } from '../../types';
25
- import type { Chunk, Job, ReportMetricModuleRow } from './bundle-modules.types';
26
+ import type { ReportMetricModuleRow } from './bundle-modules.types';
26
27
  import * as I18N from './bundle-modules.i18n';
27
28
 
28
29
  export const addRowFlags = (row: Module & ReportMetricRow): ReportMetricModuleRow => {
@@ -98,18 +99,6 @@ export const generateGetRowFilter =
98
99
  };
99
100
  /* eslint-enable prettier/prettier */
100
101
 
101
- export const extractChunkData = (jobs: Array<Job>) => {
102
- const jobChunks = jobs.map((job) => job?.meta?.webpack?.chunks || []);
103
- const chunks = uniqBy(jobChunks.flat(), ({ id }) => id);
104
-
105
- const chunkIds = chunks?.map(({ id }) => id);
106
-
107
- return {
108
- chunks,
109
- chunkIds,
110
- };
111
- };
112
-
113
102
  export const generateFilters = (chunkIds: Array<string>, multipleJobs: boolean) => {
114
103
  const allEntriesFilters = {
115
104
  [MODULE_FILTERS.CHANGED]: false,
@@ -137,7 +126,7 @@ interface GetFiltersFormDataParams {
137
126
  /**
138
127
  * Chunk data
139
128
  */
140
- chunks: Array<Chunk>;
129
+ chunks: Array<WebpackChunk>;
141
130
  /**
142
131
  * Compare mode
143
132
  */
@@ -1,8 +1,10 @@
1
1
  import React, { useCallback, useMemo } from 'react';
2
+ import type { Job } from '@bundle-stats/utils';
2
3
  // @ts-ignore
3
4
  import * as webpack from '@bundle-stats/utils/lib-esm/webpack';
4
5
 
5
6
  import type { SortAction } from '../../types';
7
+ import { getJobsChunksData } from '../../utils/jobs';
6
8
  import { useRowsFilter } from '../../hooks/rows-filter';
7
9
  import { useRowsSort } from '../../hooks/rows-sort';
8
10
  import { useSearchParams } from '../../hooks/search-params';
@@ -10,7 +12,6 @@ import { useEntryInfo } from '../../hooks/entry-info';
10
12
  import { BundleModules as BaseComponent } from './bundle-modules';
11
13
  import {
12
14
  addRowFlags,
13
- extractChunkData,
14
15
  generateGetRowFilter,
15
16
  generateFilters,
16
17
  getCustomSort,
@@ -64,7 +65,7 @@ interface BundleModulesProps
64
65
  | 'hideEntryInfo'
65
66
  | 'showEntryInfo'
66
67
  > {
67
- jobs: Array<types.Job>;
68
+ jobs: Array<Job>;
68
69
  filters: Record<string, boolean>;
69
70
  search?: string;
70
71
  metric?: string;
@@ -78,7 +79,7 @@ export const BundleModules = (props: BundleModulesProps) => {
78
79
 
79
80
  const [moduleMetric, setModuleMetric] = useModuleMetric({ metric, setState });
80
81
 
81
- const { chunks, chunkIds } = useMemo(() => extractChunkData(jobs), [jobs]);
82
+ const { chunks, chunkIds } = useMemo(() => getJobsChunksData(jobs), [jobs]);
82
83
 
83
84
  const { defaultFilters, allEntriesFilters } = useMemo(
84
85
  () => generateFilters(chunkIds, jobs.length > 1),
@@ -5,7 +5,9 @@ import { COMPONENT, PACKAGE_FILTERS, PACKAGES_SEPARATOR, SECTIONS } from '@bundl
5
5
 
6
6
  import config from '../../config.json';
7
7
  import I18N from '../../i18n';
8
+ import { Box } from '../../layout/box';
8
9
  import { FlexStack } from '../../layout/flex-stack';
10
+ import { Stack } from '../../layout/stack';
9
11
  import { InputSearch } from '../../ui/input-search';
10
12
  import { EmptySet } from '../../ui/empty-set';
11
13
  import { Filters } from '../../ui/filters';
@@ -22,9 +24,13 @@ import { SEARCH_PLACEHOLDER } from './bundle-packages.i18n';
22
24
  import css from './bundle-packages.module.css';
23
25
  import { MetricsDisplaySelector } from '../metrics-display-selector';
24
26
  import { MetricsDisplayType } from '../../constants';
25
- import { MetricsTreemap } from '../metrics-treemap';
27
+ import { MetricsTreemap, getTreemapNodes, getTreemapNodesGroupedByPath } from '../metrics-treemap';
26
28
  import { useMetricsDisplayType } from '../../hooks/metrics-display-type';
27
29
 
30
+ const DISPLAY_TYPE_GROUPS = {
31
+ [MetricsDisplayType.TREEMAP]: ['folder'],
32
+ };
33
+
28
34
  const getDropdownFilters = ({ compareMode, filters }) => ({
29
35
  [PACKAGE_FILTERS.CHANGED]: {
30
36
  label: 'Changed',
@@ -113,6 +119,71 @@ RowHeader.propTypes = {
113
119
  search: PropTypes.string,
114
120
  };
115
121
 
122
+ const ViewMetricsTreemap = (props) => {
123
+ const { metricsTableTitle, jobs, items, displayType, emptyMessage, showEntryInfo, updateSearch, search } = props;
124
+
125
+ // Get treenodes based on group
126
+ const treeNodes = useMemo(() => {
127
+ if (displayType.groupBy === 'folder') {
128
+ return getTreemapNodesGroupedByPath(items);
129
+ }
130
+
131
+ return getTreemapNodes(items);
132
+ }, [items, displayType.groupBy]);
133
+
134
+ // Search based on the group path on group title click
135
+ const onGroupClick = useCallback(
136
+ (groupPath) => {
137
+ // Clear seach when groupPath is emty (root)
138
+ if (groupPath === '') {
139
+ updateSearch('');
140
+ return;
141
+ }
142
+ // Search by group path
143
+ // 1. use `^` to match only the string beggining
144
+ // 2. add `/` suffix to exactly match the directory
145
+ const newSearch = `^${groupPath}/`;
146
+
147
+ // Reset search when toggling the same groupPath
148
+ if (newSearch === search) {
149
+ updateSearch('');
150
+ return;
151
+ }
152
+
153
+ updateSearch(newSearch);
154
+ },
155
+ [updateSearch, search],
156
+ );
157
+
158
+ return (
159
+ <>
160
+ <Table compact>
161
+ <MetricsTableHeader metricTitle={metricsTableTitle} showSum jobs={jobs} rows={items} />
162
+ </Table>
163
+ <MetricsTreemap
164
+ treeNodes={treeNodes}
165
+ nested={Boolean(displayType.groupBy)}
166
+ emptyMessage={emptyMessage}
167
+ onItemClick={showEntryInfo}
168
+ onGroupClick={onGroupClick}
169
+ />
170
+ </>
171
+ );
172
+ };
173
+
174
+ ViewMetricsTreemap.propTypes = {
175
+ jobs: PropTypes.arrayOf(PropTypes.object).isRequired, // eslint-disable-line react/forbid-prop-types
176
+ items: PropTypes.arrayOf(PropTypes.object).isRequired, // eslint-disable-line react/forbid-prop-types
177
+ metricsTableTitle: PropTypes.node.isRequired,
178
+ displayType: PropTypes.shape({
179
+ groupBy: PropTypes.string,
180
+ }).isRequired,
181
+ emptyMessage: PropTypes.node.isRequired,
182
+ showEntryInfo: PropTypes.func.isRequired,
183
+ updateSearch: PropTypes.func.isRequired,
184
+ search: PropTypes.string.isRequired,
185
+ };
186
+
116
187
  export const BundlePackages = (props) => {
117
188
  const {
118
189
  className,
@@ -137,7 +208,7 @@ export const BundlePackages = (props) => {
137
208
 
138
209
  const jobLabels = jobs?.map((job) => job?.label);
139
210
 
140
- const [displayType, setDisplayType] = useMetricsDisplayType();
211
+ const [displayType, setDisplayType] = useMetricsDisplayType(DISPLAY_TYPE_GROUPS);
141
212
 
142
213
  const dropdownFilters = useMemo(
143
214
  () => getDropdownFilters({ compareMode: jobs?.length > 1, filters }),
@@ -191,12 +262,12 @@ export const BundlePackages = (props) => {
191
262
 
192
263
  return (
193
264
  <>
194
- <section className={cx(css.root, className)}>
265
+ <Stack space="xsmall" as="section" className={cx(css.root, className)}>
195
266
  <Toolbar
196
267
  className={css.toolbar}
197
268
  renderActions={({ actionClassName }) => (
198
269
  <FlexStack space="xxsmall" className={cx(css.dropdown, actionClassName)}>
199
- <MetricsDisplaySelector onSelect={setDisplayType} value={displayType} />
270
+ <MetricsDisplaySelector onSelect={setDisplayType} value={displayType.value} groupBy={displayType.groupBy} groups={DISPLAY_TYPE_GROUPS} />
200
271
  <MetricsTableOptions
201
272
  handleViewAll={resetAllFilters}
202
273
  handleResetFilters={resetFilters}
@@ -219,8 +290,8 @@ export const BundlePackages = (props) => {
219
290
  />
220
291
  </FlexStack>
221
292
  </Toolbar>
222
- <main>
223
- {displayType === MetricsDisplayType.TABLE && (
293
+ <Box outline as="main">
294
+ {displayType.value === MetricsDisplayType.TABLE && (
224
295
  <MetricsTable
225
296
  runs={jobs}
226
297
  items={items}
@@ -232,21 +303,19 @@ export const BundlePackages = (props) => {
232
303
  updateSort={updateSort}
233
304
  />
234
305
  )}
235
- {displayType === MetricsDisplayType.TREEMAP && (
236
- <>
237
- <Table compact>
238
- <MetricsTableHeader
239
- metricTitle={metricsTableTitle}
240
- showSum
241
- jobs={jobs}
242
- rows={items}
243
- />
244
- </Table>
245
- <MetricsTreemap emptyMessage={emptyMessage} items={items} onItemClick={showEntryInfo} />
246
- </>
306
+ {displayType.value === MetricsDisplayType.TREEMAP && (
307
+ <ViewMetricsTreemap
308
+ jobs={jobs}
309
+ items={items}
310
+ displayType={displayType}
311
+ emptyMessage={emptyMessage}
312
+ showEntryInfo={showEntryInfo}
313
+ updateSearch={updateSearch}
314
+ search={search}
315
+ />
247
316
  )}
248
- </main>
249
- </section>
317
+ </Box>
318
+ </Stack>
250
319
  {entryItem && (
251
320
  <PackageInfo
252
321
  className={css.packageInfo}
@@ -2,14 +2,9 @@
2
2
  color: var(--color-text-light);
3
3
  }
4
4
 
5
- .toolbar {
6
- border-bottom: 1px solid var(--color-outline);
7
- }
8
-
9
5
  .toolbarSearch {
10
6
  width: 100%;
11
7
  max-width: 240px;
12
- margin-right: -1px; /** collapse border */
13
8
  }
14
9
 
15
10
  .hoverCard {
@@ -4,7 +4,7 @@ import cx from 'classnames';
4
4
 
5
5
  import css from './delta.module.css';
6
6
 
7
- export const Delta = ({ className, displayValue, deltaType, inverted }) => {
7
+ export const Delta = ({ className = '', displayValue, deltaType, inverted = false }) => {
8
8
  const rootClassName = cx(css.root, className, inverted && css.inverted, css[deltaType]);
9
9
  return <span className={rootClassName}>{displayValue}</span>;
10
10
  };
@@ -4,6 +4,10 @@
4
4
  white-space: nowrap;
5
5
  }
6
6
 
7
+ .CHANGE {
8
+ color: var(--color-info);
9
+ }
10
+
7
11
  .HIGH_NEGATIVE {
8
12
  color: var(--color-red-dark);
9
13
  }
@@ -29,11 +33,16 @@
29
33
  }
30
34
 
31
35
  .inverted {
32
- border-radius: var(--radius-small);
36
+ padding: 0 2px;
37
+ border-radius: var(--radius-xsmall);
33
38
  background-color: var(--color-gray-ultra-light);
34
39
  color: var(--color-light);
35
40
  }
36
41
 
42
+ .inverted.CHANGE {
43
+ background: var(--color-info-light);
44
+ }
45
+
37
46
  .inverted.HIGH_NEGATIVE {
38
47
  background: var(--color-red-dark);
39
48
  }
@@ -24,10 +24,6 @@
24
24
  background: linear-gradient(90deg, transparent, var(--color-background));
25
25
  }
26
26
 
27
- .item + .item {
28
- margin-left: var(--space-small);
29
- }
30
-
31
27
  .item:first-child {
32
28
  border-left: 0;
33
29
  padding-left: 0;
@@ -1,10 +1,18 @@
1
- .button {
2
- text-decoration: none;
1
+ .buttonActive {
2
+ background: var(--color-highlight);
3
3
  }
4
4
 
5
- .buttonActive,
6
- .buttonActive:active,
7
- .buttonActive:focus {
8
- color: var(--color-primary);
9
- border-color: var(--color-primary);
5
+ /* explicit ControlGroup last item styles to override ariakit injected sibling nodes */
6
+ .itemLast {
7
+ border-top-right-radius: var(--radius-small) !important;
8
+ border-bottom-right-radius: var(--radius-small) !important;
9
+ }
10
+
11
+ .dropdownItemIcon {
12
+ opacity: 0;
13
+ transition: var(--transition-out);
14
+ }
15
+
16
+ .dropdownItemActive .dropdownItemIcon {
17
+ opacity: 1;
10
18
  }
@@ -0,0 +1,33 @@
1
+ import { action } from '@storybook/addon-actions'; // eslint-disable-line
2
+ import { Meta, StoryObj } from '@storybook/react';
3
+
4
+ import { MetricsDisplayType } from '../../constants';
5
+ import { MetricsDisplaySelector } from '.';
6
+
7
+ const meta: Meta<typeof MetricsDisplaySelector> = {
8
+ title: 'Components/MetricsDisplaySelector',
9
+ component: MetricsDisplaySelector,
10
+ args: {
11
+ onSelect: action('SELECT'),
12
+ },
13
+ } as Meta;
14
+
15
+ export default meta;
16
+
17
+ type Story = StoryObj<typeof meta>;
18
+
19
+ export const Default: Story = {
20
+ args: {
21
+ value: MetricsDisplayType.TABLE,
22
+ },
23
+ };
24
+
25
+ export const TreemapGroups: Story = {
26
+ args: {
27
+ value: MetricsDisplayType.TREEMAP,
28
+ groupBy: '',
29
+ groups: {
30
+ [MetricsDisplayType.TREEMAP]: ['folder'],
31
+ },
32
+ },
33
+ };