@bundle-stats/ui 4.13.0-rc.0 → 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 (86) hide show
  1. package/lib/components/bundle-assets/bundle-assets.js +23 -22
  2. package/lib/components/bundle-assets/bundle-assets.js.map +1 -1
  3. package/lib/components/bundle-assets/index.js +3 -1
  4. package/lib/components/bundle-assets/index.js.map +1 -1
  5. package/lib/components/bundle-modules/bundle-modules.js +10 -6
  6. package/lib/components/bundle-modules/bundle-modules.js.map +1 -1
  7. package/lib/components/bundle-modules/bundle-modules.utils.js +2 -13
  8. package/lib/components/bundle-modules/bundle-modules.utils.js.map +1 -1
  9. package/lib/components/bundle-modules/index.js +2 -1
  10. package/lib/components/bundle-modules/index.js.map +1 -1
  11. package/lib/components/bundle-packages/bundle-packages.js +11 -6
  12. package/lib/components/bundle-packages/bundle-packages.js.map +1 -1
  13. package/lib/components/metrics-treemap/metrics-treemap.constants.js +7 -0
  14. package/lib/components/metrics-treemap/metrics-treemap.constants.js.map +1 -1
  15. package/lib/components/metrics-treemap/metrics-treemap.js +69 -71
  16. package/lib/components/metrics-treemap/metrics-treemap.js.map +1 -1
  17. package/lib/components/metrics-treemap/metrics-treemap.module.css +47 -10
  18. package/lib/components/metrics-treemap/metrics-treemap.utils.js +60 -1
  19. package/lib/components/metrics-treemap/metrics-treemap.utils.js.map +1 -1
  20. package/lib/utils/colors.js +22 -0
  21. package/lib/utils/colors.js.map +1 -0
  22. package/lib/{utils.js → utils/components.js} +2 -17
  23. package/lib/utils/components.js.map +1 -0
  24. package/lib/utils/index.js +20 -0
  25. package/lib/utils/index.js.map +1 -0
  26. package/lib/utils/jobs.js +18 -0
  27. package/lib/utils/jobs.js.map +1 -0
  28. package/lib-esm/components/bundle-assets/bundle-assets.js +23 -23
  29. package/lib-esm/components/bundle-assets/bundle-assets.js.map +1 -1
  30. package/lib-esm/components/bundle-assets/index.js +3 -1
  31. package/lib-esm/components/bundle-assets/index.js.map +1 -1
  32. package/lib-esm/components/bundle-modules/bundle-modules.js +10 -6
  33. package/lib-esm/components/bundle-modules/bundle-modules.js.map +1 -1
  34. package/lib-esm/components/bundle-modules/bundle-modules.utils.js +1 -11
  35. package/lib-esm/components/bundle-modules/bundle-modules.utils.js.map +1 -1
  36. package/lib-esm/components/bundle-modules/index.js +3 -2
  37. package/lib-esm/components/bundle-modules/index.js.map +1 -1
  38. package/lib-esm/components/bundle-packages/bundle-packages.js +11 -6
  39. package/lib-esm/components/bundle-packages/bundle-packages.js.map +1 -1
  40. package/lib-esm/components/metrics-treemap/metrics-treemap.constants.js +6 -1
  41. package/lib-esm/components/metrics-treemap/metrics-treemap.constants.js.map +1 -1
  42. package/lib-esm/components/metrics-treemap/metrics-treemap.js +65 -68
  43. package/lib-esm/components/metrics-treemap/metrics-treemap.js.map +1 -1
  44. package/lib-esm/components/metrics-treemap/metrics-treemap.module.css +47 -10
  45. package/lib-esm/components/metrics-treemap/metrics-treemap.utils.js +57 -0
  46. package/lib-esm/components/metrics-treemap/metrics-treemap.utils.js.map +1 -1
  47. package/lib-esm/utils/colors.js +15 -0
  48. package/lib-esm/utils/colors.js.map +1 -0
  49. package/lib-esm/utils/components.js +16 -0
  50. package/lib-esm/utils/components.js.map +1 -0
  51. package/lib-esm/utils/index.js +4 -0
  52. package/lib-esm/utils/index.js.map +1 -0
  53. package/lib-esm/utils/jobs.js +11 -0
  54. package/lib-esm/utils/jobs.js.map +1 -0
  55. package/package.json +3 -3
  56. package/src/components/bundle-assets/bundle-assets.jsx +72 -48
  57. package/src/components/bundle-assets/index.jsx +4 -0
  58. package/src/components/bundle-modules/bundle-modules.tsx +32 -11
  59. package/src/components/bundle-modules/bundle-modules.types.d.ts +2 -17
  60. package/src/components/bundle-modules/bundle-modules.utils.ts +7 -18
  61. package/src/components/bundle-modules/index.tsx +4 -3
  62. package/src/components/bundle-packages/bundle-packages.jsx +19 -7
  63. package/src/components/metrics-treemap/metrics-treemap.constants.ts +11 -0
  64. package/src/components/metrics-treemap/metrics-treemap.module.css +47 -10
  65. package/src/components/metrics-treemap/metrics-treemap.tsx +171 -110
  66. package/src/components/metrics-treemap/metrics-treemap.utils.ts +107 -1
  67. package/src/utils/colors.ts +19 -0
  68. package/src/utils/components.tsx +19 -0
  69. package/src/utils/index.ts +3 -0
  70. package/src/utils/jobs.ts +22 -0
  71. package/types/components/bundle-assets/bundle-assets.d.ts +1 -5
  72. package/types/components/bundle-modules/bundle-modules.d.ts +4 -2
  73. package/types/components/bundle-modules/bundle-modules.utils.d.ts +3 -7
  74. package/types/components/bundle-modules/index.d.ts +2 -2
  75. package/types/components/metrics-treemap/metrics-treemap.constants.d.ts +8 -0
  76. package/types/components/metrics-treemap/metrics-treemap.d.ts +1 -1
  77. package/types/components/metrics-treemap/metrics-treemap.utils.d.ts +11 -1
  78. package/types/utils/colors.d.ts +1 -0
  79. package/types/utils/components.d.ts +2 -0
  80. package/types/utils/index.d.ts +3 -0
  81. package/types/utils/jobs.d.ts +7 -0
  82. package/lib/utils.js.map +0 -1
  83. package/lib-esm/utils.js +0 -30
  84. package/lib-esm/utils.js.map +0 -1
  85. package/src/utils.jsx +0 -38
  86. package/types/utils.d.ts +0 -3
@@ -48,12 +48,11 @@ const DISPLAY_TYPE_GROUPS = {
48
48
  };
49
49
 
50
50
  const getFileTypeFilters = (filters) =>
51
- Object.entries(FILE_TYPE_LABELS)
52
- .map(([key, label]) => ({
53
- key,
54
- label,
55
- defaultValue: get(filters, `${ASSET_FILE_TYPE}.${key}`, true),
56
- }));
51
+ Object.entries(FILE_TYPE_LABELS).map(([key, label]) => ({
52
+ key,
53
+ label,
54
+ defaultValue: get(filters, `${ASSET_FILE_TYPE}.${key}`, true),
55
+ }));
57
56
 
58
57
  const getFilters = ({ compareMode, filters }) => ({
59
58
  [ASSET_FILTERS.CHANGED]: {
@@ -146,6 +145,7 @@ const RowHeader = ({ row, customComponentLink: CustomComponentLink, filters, sea
146
145
 
147
146
  RowHeader.propTypes = {
148
147
  row: PropTypes.shape({
148
+ key: PropTypes.string,
149
149
  label: PropTypes.string,
150
150
  isNotPredictive: PropTypes.bool,
151
151
  isChunk: PropTypes.bool,
@@ -153,23 +153,28 @@ RowHeader.propTypes = {
153
153
  isEntry: PropTypes.bool,
154
154
  runs: PropTypes.arrayOf(PropTypes.object), // eslint-disable-line react/forbid-prop-types
155
155
  }).isRequired,
156
- chunks: PropTypes.arrayOf(
157
- PropTypes.shape({
158
- id: PropTypes.string,
159
- name: PropTypes.string,
160
- }),
161
- ),
162
- labels: PropTypes.arrayOf(PropTypes.string).isRequired,
163
- CustomComponentLink: PropTypes.elementType.isRequired,
156
+ customComponentLink: PropTypes.elementType.isRequired,
157
+ filters: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
158
+ search: PropTypes.string,
164
159
  };
165
160
 
166
161
  RowHeader.defaultProps = {
167
- chunks: [],
162
+ search: '',
168
163
  };
169
164
 
170
165
  const ViewMetricsTreemap = (props) => {
171
- const { metricsTableTitle, jobs, items, displayType, emptyMessage, showEntryInfo, updateSearch } = props;
166
+ const {
167
+ metricsTableTitle,
168
+ jobs,
169
+ items,
170
+ displayType,
171
+ emptyMessage,
172
+ showEntryInfo,
173
+ updateSearch,
174
+ search,
175
+ } = props;
172
176
 
177
+ // Get treenodes based on group
173
178
  const treeNodes = useMemo(() => {
174
179
  if (displayType.groupBy === 'folder') {
175
180
  return getTreemapNodesGroupedByPath(items);
@@ -178,19 +183,28 @@ const ViewMetricsTreemap = (props) => {
178
183
  return getTreemapNodes(items);
179
184
  }, [items, displayType.groupBy]);
180
185
 
186
+ // Search based on the group path on group title click
181
187
  const onGroupClick = useCallback(
182
188
  (groupPath) => {
189
+ // Clear seach when groupPath is emty (root)
190
+ if (groupPath === '') {
191
+ updateSearch('');
192
+ return;
193
+ }
183
194
  // Search by group path
184
195
  // 1. use `^` to match only the string beggining
185
- // 2. add `/` suffix to match only exact directories
186
- // 3. if the group path is empty(root), clear search
187
- if (groupPath) {
188
- updateSearch(`^${groupPath}/`);
189
- } else {
196
+ // 2. add `/` suffix to exactly match the directory
197
+ const newSearch = `^${groupPath}/`;
198
+
199
+ // Reset search when toggling the same groupPath
200
+ if (newSearch === search) {
190
201
  updateSearch('');
202
+ return;
191
203
  }
204
+
205
+ updateSearch(`^${groupPath}/`);
192
206
  },
193
- [updateSearch],
207
+ [updateSearch, search],
194
208
  );
195
209
 
196
210
  return (
@@ -219,12 +233,14 @@ ViewMetricsTreemap.propTypes = {
219
233
  emptyMessage: PropTypes.node.isRequired,
220
234
  showEntryInfo: PropTypes.func.isRequired,
221
235
  updateSearch: PropTypes.func.isRequired,
236
+ search: PropTypes.string.isRequired,
222
237
  };
223
238
 
224
239
  export const BundleAssets = (props) => {
225
240
  const {
226
241
  className,
227
242
  jobs,
243
+ chunks,
228
244
  items,
229
245
  allItems,
230
246
  updateFilters,
@@ -252,14 +268,17 @@ export const BundleAssets = (props) => {
252
268
  [jobs, filters],
253
269
  );
254
270
 
255
- const metricsTableTitle = useMemo(() => (
256
- <MetricsTableTitle
257
- title={I18N.ASSETS}
258
- info={`${items.length}/${totalRowCount}`}
259
- popoverInfo={I18N.ASSETS_INFO}
260
- popoverHref={config.documentation.assets}
261
- />
262
- ), [items, totalRowCount]);
271
+ const metricsTableTitle = useMemo(
272
+ () => (
273
+ <MetricsTableTitle
274
+ title={I18N.ASSETS}
275
+ info={`${items.length}/${totalRowCount}`}
276
+ popoverInfo={I18N.ASSETS_INFO}
277
+ popoverHref={config.documentation.assets}
278
+ />
279
+ ),
280
+ [items, totalRowCount],
281
+ );
263
282
 
264
283
  const renderRowHeader = useCallback(
265
284
  (row) => (
@@ -273,21 +292,24 @@ export const BundleAssets = (props) => {
273
292
  [CustomComponentLink, filters, search],
274
293
  );
275
294
 
276
- const emptyMessage = useMemo(() => (
277
- <EmptySet
278
- resources="assets"
279
- filtered={totalRowCount !== 0}
280
- handleResetFilters={resetFilters}
281
- handleViewAll={resetAllFilters}
282
- />
283
- ), [totalRowCount, resetFilters, resetAllFilters]);
295
+ const emptyMessage = useMemo(
296
+ () => (
297
+ <EmptySet
298
+ resources="assets"
299
+ filtered={totalRowCount !== 0}
300
+ handleResetFilters={resetFilters}
301
+ handleViewAll={resetAllFilters}
302
+ />
303
+ ),
304
+ [totalRowCount, resetFilters, resetAllFilters],
305
+ );
284
306
 
285
307
  const entryItem = useMemo(() => {
286
308
  if (!entryId) {
287
309
  return null;
288
310
  }
289
311
 
290
- return allItems.find(({ key }) => key === entryId)
312
+ return allItems.find(({ key }) => key === entryId);
291
313
  }, [allItems, entryId]);
292
314
 
293
315
  return (
@@ -345,6 +367,7 @@ export const BundleAssets = (props) => {
345
367
  emptyMessage={emptyMessage}
346
368
  showEntryInfo={showEntryInfo}
347
369
  updateSearch={updateSearch}
370
+ search={search}
348
371
  />
349
372
  )}
350
373
  </Box>
@@ -355,7 +378,7 @@ export const BundleAssets = (props) => {
355
378
  className={css.assetInfo}
356
379
  item={entryItem}
357
380
  labels={jobLabels}
358
- chunks={jobs[0]?.meta?.webpack?.chunks || []}
381
+ chunks={chunks}
359
382
  customComponentLink={CustomComponentLink}
360
383
  onClose={hideEntryInfo}
361
384
  />
@@ -378,13 +401,12 @@ BundleAssets.propTypes = {
378
401
  PropTypes.shape({
379
402
  internalBuildNumber: PropTypes.number,
380
403
  label: PropTypes.string,
381
- meta: PropTypes.shape({
382
- webpack: PropTypes.shape({
383
- chunks: PropTypes.array, // eslint-disable-line react/forbid-prop-types
384
- }),
385
- }),
386
404
  }),
387
405
  ).isRequired,
406
+ chunks: PropTypes.arrayOf({
407
+ id: PropTypes.string,
408
+ name: PropTypes.string,
409
+ }).isRequired,
388
410
  items: PropTypes.arrayOf(
389
411
  PropTypes.shape({
390
412
  key: PropTypes.string,
@@ -414,9 +436,11 @@ BundleAssets.propTypes = {
414
436
  direction: PropTypes.string,
415
437
  }).isRequired,
416
438
  updateSort: PropTypes.func.isRequired,
417
- allItems: PropTypes.arrayOf(PropTypes.shape({
418
- key: PropTypes.string,
419
- })).isRequired,
439
+ allItems: PropTypes.arrayOf(
440
+ PropTypes.shape({
441
+ key: PropTypes.string,
442
+ }),
443
+ ).isRequired,
420
444
  customComponentLink: PropTypes.elementType,
421
445
  hideEntryInfo: PropTypes.func.isRequired,
422
446
  showEntryInfo: PropTypes.func.isRequired,
@@ -11,6 +11,7 @@ import { useRowsFilter } from '../../hooks/rows-filter';
11
11
  import { useRowsSort } from '../../hooks/rows-sort';
12
12
  import { useSearchParams } from '../../hooks/search-params';
13
13
  import { useEntryInfo } from '../../hooks/entry-info';
14
+ import { getJobsChunksData } from '../../utils/jobs';
14
15
  import { BundleAssets as BundleAssetsComponent } from './bundle-assets';
15
16
  import {
16
17
  addRowAssetFlags,
@@ -22,6 +23,8 @@ import {
22
23
  export const BundleAssets = (props) => {
23
24
  const { jobs, filters, search, setState, sortBy, direction, ...restProps } = props;
24
25
 
26
+ const { chunks } = useMemo(() => getJobsChunksData(jobs), [jobs]);
27
+
25
28
  const { defaultFilters, allEntriesFilters } = useMemo(
26
29
  () => ({
27
30
  defaultFilters: {
@@ -70,6 +73,7 @@ export const BundleAssets = (props) => {
70
73
  return (
71
74
  <BundleAssetsComponent
72
75
  jobs={jobs}
76
+ chunks={chunks}
73
77
  {...restProps}
74
78
  {...searchParams}
75
79
  {...sortParams}
@@ -1,7 +1,8 @@
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';
@@ -29,7 +30,7 @@ import { MetricsTableTitle } from '../metrics-table-title';
29
30
  import { ModuleInfo } from '../module-info';
30
31
  import { generateFilterFieldsData } from './bundle-modules.utils';
31
32
  import { ModuleMetric } from './bundle-modules.constants';
32
- import type { Chunk, Job, ReportMetricModuleRow } from './bundle-modules.types';
33
+ import type { ReportMetricModuleRow } from './bundle-modules.types';
33
34
  import * as I18N_MODULES from './bundle-modules.i18n';
34
35
  import css from './bundle-modules.module.css';
35
36
  import { useMetricsDisplayType } from '../../hooks/metrics-display-type';
@@ -73,12 +74,22 @@ interface ViewMetricsTreemapProps {
73
74
  emptyMessage: React.ReactNode;
74
75
  showEntryInfo: React.ComponentProps<typeof MetricsTreemap>['onItemClick'];
75
76
  updateSearch: (newSerarch: string) => void;
77
+ search: string;
76
78
  }
77
79
 
78
80
  const ViewMetricsTreemap = (props: ViewMetricsTreemapProps) => {
79
- const { metricsTableTitle, jobs, items, displayType, emptyMessage, showEntryInfo, updateSearch } =
80
- props;
81
+ const {
82
+ metricsTableTitle,
83
+ jobs,
84
+ items,
85
+ displayType,
86
+ emptyMessage,
87
+ showEntryInfo,
88
+ updateSearch,
89
+ search,
90
+ } = props;
81
91
 
92
+ // Get treenodes based on group
82
93
  const treeNodes = useMemo(() => {
83
94
  if (displayType.groupBy === 'folder') {
84
95
  return getTreemapNodesGroupedByPath(items);
@@ -87,19 +98,28 @@ const ViewMetricsTreemap = (props: ViewMetricsTreemapProps) => {
87
98
  return getTreemapNodes(items);
88
99
  }, [items, displayType]);
89
100
 
101
+ // Search based on the group path on group title click
90
102
  const onGroupClick = useCallback(
91
103
  (groupPath: string) => {
104
+ // Clear seach when groupPath is emty (root)
105
+ if (groupPath === '') {
106
+ updateSearch('');
107
+ return;
108
+ }
92
109
  // Search by group path
93
110
  // 1. use `^` to match only the string beggining
94
- // 2. add `/` suffix to match only exact directories
95
- // 3. if the group path is empty(root), clear search
96
- if (groupPath) {
97
- updateSearch(`^${groupPath}/`);
98
- } else {
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) {
99
116
  updateSearch('');
117
+ return;
100
118
  }
119
+
120
+ updateSearch(newSearch);
101
121
  },
102
- [updateSearch],
122
+ [updateSearch, search],
103
123
  );
104
124
 
105
125
  return (
@@ -122,7 +142,7 @@ interface BundleModulesProps extends React.ComponentProps<'div'> {
122
142
  jobs?: Array<Job>;
123
143
  items?: Array<ReportMetricModuleRow>;
124
144
  allItems?: Array<ReportMetricModuleRow>;
125
- chunks?: Array<Chunk>;
145
+ chunks?: Array<WebpackChunk>;
126
146
 
127
147
  totalRowCount: number;
128
148
 
@@ -305,6 +325,7 @@ export const BundleModules = (props: BundleModulesProps) => {
305
325
  emptyMessage={emptyMessage}
306
326
  showEntryInfo={showEntryInfo}
307
327
  updateSearch={updateSearch}
328
+ search={search}
308
329
  />
309
330
  )}
310
331
  </Box>
@@ -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),
@@ -120,8 +120,9 @@ RowHeader.propTypes = {
120
120
  };
121
121
 
122
122
  const ViewMetricsTreemap = (props) => {
123
- const { metricsTableTitle, jobs, items, displayType, emptyMessage, showEntryInfo, updateSearch } = props;
123
+ const { metricsTableTitle, jobs, items, displayType, emptyMessage, showEntryInfo, updateSearch, search } = props;
124
124
 
125
+ // Get treenodes based on group
125
126
  const treeNodes = useMemo(() => {
126
127
  if (displayType.groupBy === 'folder') {
127
128
  return getTreemapNodesGroupedByPath(items);
@@ -130,19 +131,28 @@ const ViewMetricsTreemap = (props) => {
130
131
  return getTreemapNodes(items);
131
132
  }, [items, displayType.groupBy]);
132
133
 
134
+ // Search based on the group path on group title click
133
135
  const onGroupClick = useCallback(
134
136
  (groupPath) => {
137
+ // Clear seach when groupPath is emty (root)
138
+ if (groupPath === '') {
139
+ updateSearch('');
140
+ return;
141
+ }
135
142
  // Search by group path
136
143
  // 1. use `^` to match only the string beggining
137
- // 2. add `/` suffix to match only exact directories
138
- // 3. if the group path is empty(root), clear search
139
- if (groupPath) {
140
- updateSearch(`^${groupPath}/`);
141
- } else {
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) {
142
149
  updateSearch('');
150
+ return;
143
151
  }
152
+
153
+ updateSearch(newSearch);
144
154
  },
145
- [updateSearch],
155
+ [updateSearch, search],
146
156
  );
147
157
 
148
158
  return (
@@ -171,6 +181,7 @@ ViewMetricsTreemap.propTypes = {
171
181
  emptyMessage: PropTypes.node.isRequired,
172
182
  showEntryInfo: PropTypes.func.isRequired,
173
183
  updateSearch: PropTypes.func.isRequired,
184
+ search: PropTypes.string.isRequired,
174
185
  };
175
186
 
176
187
  export const BundlePackages = (props) => {
@@ -300,6 +311,7 @@ export const BundlePackages = (props) => {
300
311
  emptyMessage={emptyMessage}
301
312
  showEntryInfo={showEntryInfo}
302
313
  updateSearch={updateSearch}
314
+ search={search}
303
315
  />
304
316
  )}
305
317
  </Box>
@@ -50,3 +50,14 @@ export interface Tree {
50
50
  export type TreeNode = Tree | TreeLeaf;
51
51
 
52
52
  export type TreeNodeChildren = Array<TreeNode>;
53
+
54
+ export type TileGroupSizeDisplay = 'minimal' | 'small' | 'default';
55
+
56
+ export type TileSizeDisplay = 'minimal' | 'small' | 'default';
57
+
58
+ export const SQUARIFY_RATIO = 1.66;
59
+ export const PADDING_OUTER = 1;
60
+ export const PADDING_INNER = 2;
61
+ export const NESTED_PADDING = 4;
62
+ export const NESTED_PADDING_TOP = 16 + NESTED_PADDING * 2;
63
+ export const NESTED_PADDING_LEFT = 8;
@@ -25,26 +25,58 @@
25
25
 
26
26
  .tileGroupTitle {
27
27
  width: 100%;
28
- padding: 4px 2px 4px var(--space-xxsmall);
28
+ }
29
+
30
+ .tileGroupTitleContent {
31
+ width: 100%;
32
+ padding: var(--space-xxxsmall) 2px var(--space-xxxsmall) var(--space-xxsmall);
33
+ display: flex;
34
+ align-items: center;
29
35
  color: var(--color-text);
30
36
  font-family: var(--font-family-fixed);
31
37
  font-size: 10px;
32
- line-height: 16px;
38
+ line-height: var(--space-small);
33
39
  overflow: hidden;
34
40
  white-space: nowrap;
35
41
  text-overflow: ellipsis;
36
42
  }
37
43
 
44
+ .tileGroupTitleText,
38
45
  .tileGroupTitleTotal {
39
- margin-left: var(--space-xxxsmall);
46
+ flex: 0 0 auto;
47
+ min-width: 0;
48
+ }
49
+
50
+ .tileGroupTitleTotal {
51
+ margin-left: var(--space-xxsmall);
52
+ color: var(--color-text-light);
53
+ }
54
+
55
+ .tileGroupTitle {
56
+ transition: var(--ui-transition-out);
57
+ }
58
+
59
+ .tileGroupTitle:hover .tileGroupTitleText {
60
+ text-decoration: underline;
61
+ transition: var(--ui-transition-in);
40
62
  }
41
63
 
42
64
  /** Tile group size variation */
43
- .tileGroupSizeDefault {
65
+ .tileGroup {
44
66
  transition: var(--ui-transition-in);
45
67
  transition-property: left, top, width, height;
46
68
  }
47
69
 
70
+ .tileGroup--minimal .tileGroupTitle,
71
+ .tileGroup--small .tileGroupTitle {
72
+ height: 100%; /* full height when title is missing or we do not render any children */
73
+ }
74
+
75
+ .tileGroup--minimal .tileGroupTitleContent,
76
+ .tileGroup--small .tileGroupTitleContent {
77
+ min-height: 100%;
78
+ }
79
+
48
80
  /** Tile group nested variation */
49
81
  .nested .tileGroup {
50
82
  background-color: rgba(27, 26, 33, 0.05);
@@ -141,7 +173,13 @@
141
173
  flex: 0 1 auto;
142
174
  width: 100%;
143
175
  min-width: 0;
144
- max-height: calc(2em * var(--line-height-heading));
176
+ max-height: calc(2em * var(--line-height-heading)); /* render max 2 rows */
177
+ overflow-wrap: break-word;
178
+ word-wrap: break-word;
179
+ word-break: break-all;
180
+ word-break: break-word;
181
+ hyphens: auto;
182
+ white-space: normal;
145
183
  overflow: hidden;
146
184
  }
147
185
 
@@ -162,9 +200,8 @@
162
200
 
163
201
  /** Tile size variation */
164
202
  .tileSizeSmall .tileContent {
165
- padding: var(--space-xxxsmall) var(--space-xxsmall);
166
- gap: 0;
167
- font-size: var(--size-xsmall);
203
+ padding: var(--space-xxxsmall);
204
+ font-size: 10px;
168
205
  }
169
206
 
170
207
  .tileSizeSmall .tileContentLabel {
@@ -173,7 +210,7 @@
173
210
 
174
211
  .tileSizeDefault {
175
212
  transition: var(--ui-transition-in);
176
- transition-property: left top width height;
213
+ transition-property: left, top, width, height;
177
214
  }
178
215
 
179
216
  /** Tile diff variation */
@@ -196,7 +233,7 @@
196
233
  }
197
234
 
198
235
  .tile-NO_CHANGE {
199
- color: var(--color-text-ultra-light);
236
+ color: var(--color-text-light);
200
237
  }
201
238
 
202
239
  .tile-NO_CHANGE::before {