@bundle-stats/ui 4.13.0-rc.0 → 4.13.0-rc.2

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 +24 -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 +11 -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 +12 -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 +58 -26
  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 +24 -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 +11 -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 +12 -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 +58 -26
  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 +74 -48
  57. package/src/components/bundle-assets/index.jsx +4 -0
  58. package/src/components/bundle-modules/bundle-modules.tsx +33 -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 +21 -7
  63. package/src/components/metrics-treemap/metrics-treemap.constants.ts +11 -0
  64. package/src/components/metrics-treemap/metrics-treemap.module.css +58 -26
  65. package/src/components/metrics-treemap/metrics-treemap.tsx +173 -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
@@ -2,6 +2,7 @@ import React, { useCallback, useMemo } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import cx from 'classnames';
4
4
  import get from 'lodash/get';
5
+ import escapeRegExp from 'lodash/escapeRegExp';
5
6
  import {
6
7
  ASSET_ENTRY_TYPE,
7
8
  ASSET_FILE_TYPE,
@@ -48,12 +49,11 @@ const DISPLAY_TYPE_GROUPS = {
48
49
  };
49
50
 
50
51
  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
- }));
52
+ Object.entries(FILE_TYPE_LABELS).map(([key, label]) => ({
53
+ key,
54
+ label,
55
+ defaultValue: get(filters, `${ASSET_FILE_TYPE}.${key}`, true),
56
+ }));
57
57
 
58
58
  const getFilters = ({ compareMode, filters }) => ({
59
59
  [ASSET_FILTERS.CHANGED]: {
@@ -146,6 +146,7 @@ const RowHeader = ({ row, customComponentLink: CustomComponentLink, filters, sea
146
146
 
147
147
  RowHeader.propTypes = {
148
148
  row: PropTypes.shape({
149
+ key: PropTypes.string,
149
150
  label: PropTypes.string,
150
151
  isNotPredictive: PropTypes.bool,
151
152
  isChunk: PropTypes.bool,
@@ -153,23 +154,28 @@ RowHeader.propTypes = {
153
154
  isEntry: PropTypes.bool,
154
155
  runs: PropTypes.arrayOf(PropTypes.object), // eslint-disable-line react/forbid-prop-types
155
156
  }).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,
157
+ customComponentLink: PropTypes.elementType.isRequired,
158
+ filters: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
159
+ search: PropTypes.string,
164
160
  };
165
161
 
166
162
  RowHeader.defaultProps = {
167
- chunks: [],
163
+ search: '',
168
164
  };
169
165
 
170
166
  const ViewMetricsTreemap = (props) => {
171
- const { metricsTableTitle, jobs, items, displayType, emptyMessage, showEntryInfo, updateSearch } = props;
167
+ const {
168
+ metricsTableTitle,
169
+ jobs,
170
+ items,
171
+ displayType,
172
+ emptyMessage,
173
+ showEntryInfo,
174
+ updateSearch,
175
+ search,
176
+ } = props;
172
177
 
178
+ // Get treenodes based on group
173
179
  const treeNodes = useMemo(() => {
174
180
  if (displayType.groupBy === 'folder') {
175
181
  return getTreemapNodesGroupedByPath(items);
@@ -178,19 +184,28 @@ const ViewMetricsTreemap = (props) => {
178
184
  return getTreemapNodes(items);
179
185
  }, [items, displayType.groupBy]);
180
186
 
187
+ // Search based on the group path on group title click
181
188
  const onGroupClick = useCallback(
182
189
  (groupPath) => {
190
+ // Clear seach when groupPath is emty (root)
191
+ if (groupPath === '') {
192
+ updateSearch('');
193
+ return;
194
+ }
183
195
  // Search by group path
184
196
  // 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 {
197
+ // 2. add `/` suffix to exactly match the directory
198
+ const newSearch = `^${escapeRegExp(groupPath)}/`;
199
+
200
+ // Reset search when toggling the same groupPath
201
+ if (newSearch === search) {
190
202
  updateSearch('');
203
+ return;
191
204
  }
205
+
206
+ updateSearch(`^${groupPath}/`);
192
207
  },
193
- [updateSearch],
208
+ [updateSearch, search],
194
209
  );
195
210
 
196
211
  return (
@@ -219,12 +234,14 @@ ViewMetricsTreemap.propTypes = {
219
234
  emptyMessage: PropTypes.node.isRequired,
220
235
  showEntryInfo: PropTypes.func.isRequired,
221
236
  updateSearch: PropTypes.func.isRequired,
237
+ search: PropTypes.string.isRequired,
222
238
  };
223
239
 
224
240
  export const BundleAssets = (props) => {
225
241
  const {
226
242
  className,
227
243
  jobs,
244
+ chunks,
228
245
  items,
229
246
  allItems,
230
247
  updateFilters,
@@ -252,14 +269,17 @@ export const BundleAssets = (props) => {
252
269
  [jobs, filters],
253
270
  );
254
271
 
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]);
272
+ const metricsTableTitle = useMemo(
273
+ () => (
274
+ <MetricsTableTitle
275
+ title={I18N.ASSETS}
276
+ info={`${items.length}/${totalRowCount}`}
277
+ popoverInfo={I18N.ASSETS_INFO}
278
+ popoverHref={config.documentation.assets}
279
+ />
280
+ ),
281
+ [items, totalRowCount],
282
+ );
263
283
 
264
284
  const renderRowHeader = useCallback(
265
285
  (row) => (
@@ -273,21 +293,24 @@ export const BundleAssets = (props) => {
273
293
  [CustomComponentLink, filters, search],
274
294
  );
275
295
 
276
- const emptyMessage = useMemo(() => (
277
- <EmptySet
278
- resources="assets"
279
- filtered={totalRowCount !== 0}
280
- handleResetFilters={resetFilters}
281
- handleViewAll={resetAllFilters}
282
- />
283
- ), [totalRowCount, resetFilters, resetAllFilters]);
296
+ const emptyMessage = useMemo(
297
+ () => (
298
+ <EmptySet
299
+ resources="assets"
300
+ filtered={totalRowCount !== 0}
301
+ handleResetFilters={resetFilters}
302
+ handleViewAll={resetAllFilters}
303
+ />
304
+ ),
305
+ [totalRowCount, resetFilters, resetAllFilters],
306
+ );
284
307
 
285
308
  const entryItem = useMemo(() => {
286
309
  if (!entryId) {
287
310
  return null;
288
311
  }
289
312
 
290
- return allItems.find(({ key }) => key === entryId)
313
+ return allItems.find(({ key }) => key === entryId);
291
314
  }, [allItems, entryId]);
292
315
 
293
316
  return (
@@ -341,10 +364,12 @@ export const BundleAssets = (props) => {
341
364
  <ViewMetricsTreemap
342
365
  jobs={jobs}
343
366
  items={items}
367
+ metricsTableTitle={metricsTableTitle}
344
368
  displayType={displayType}
345
369
  emptyMessage={emptyMessage}
346
370
  showEntryInfo={showEntryInfo}
347
371
  updateSearch={updateSearch}
372
+ search={search}
348
373
  />
349
374
  )}
350
375
  </Box>
@@ -355,7 +380,7 @@ export const BundleAssets = (props) => {
355
380
  className={css.assetInfo}
356
381
  item={entryItem}
357
382
  labels={jobLabels}
358
- chunks={jobs[0]?.meta?.webpack?.chunks || []}
383
+ chunks={chunks}
359
384
  customComponentLink={CustomComponentLink}
360
385
  onClose={hideEntryInfo}
361
386
  />
@@ -378,13 +403,12 @@ BundleAssets.propTypes = {
378
403
  PropTypes.shape({
379
404
  internalBuildNumber: PropTypes.number,
380
405
  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
406
  }),
387
407
  ).isRequired,
408
+ chunks: PropTypes.arrayOf({
409
+ id: PropTypes.string,
410
+ name: PropTypes.string,
411
+ }).isRequired,
388
412
  items: PropTypes.arrayOf(
389
413
  PropTypes.shape({
390
414
  key: PropTypes.string,
@@ -414,9 +438,11 @@ BundleAssets.propTypes = {
414
438
  direction: PropTypes.string,
415
439
  }).isRequired,
416
440
  updateSort: PropTypes.func.isRequired,
417
- allItems: PropTypes.arrayOf(PropTypes.shape({
418
- key: PropTypes.string,
419
- })).isRequired,
441
+ allItems: PropTypes.arrayOf(
442
+ PropTypes.shape({
443
+ key: PropTypes.string,
444
+ }),
445
+ ).isRequired,
420
446
  customComponentLink: PropTypes.elementType,
421
447
  hideEntryInfo: PropTypes.func.isRequired,
422
448
  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,9 @@
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
+ import escapeRegExp from 'lodash/escapeRegExp';
4
5
 
6
+ import { WebpackChunk } from '@bundle-stats/utils';
5
7
  import { SortAction } from '../../types';
6
8
  import config from '../../config.json';
7
9
  import I18N from '../../i18n';
@@ -29,7 +31,7 @@ import { MetricsTableTitle } from '../metrics-table-title';
29
31
  import { ModuleInfo } from '../module-info';
30
32
  import { generateFilterFieldsData } from './bundle-modules.utils';
31
33
  import { ModuleMetric } from './bundle-modules.constants';
32
- import type { Chunk, Job, ReportMetricModuleRow } from './bundle-modules.types';
34
+ import type { ReportMetricModuleRow } from './bundle-modules.types';
33
35
  import * as I18N_MODULES from './bundle-modules.i18n';
34
36
  import css from './bundle-modules.module.css';
35
37
  import { useMetricsDisplayType } from '../../hooks/metrics-display-type';
@@ -73,12 +75,22 @@ interface ViewMetricsTreemapProps {
73
75
  emptyMessage: React.ReactNode;
74
76
  showEntryInfo: React.ComponentProps<typeof MetricsTreemap>['onItemClick'];
75
77
  updateSearch: (newSerarch: string) => void;
78
+ search: string;
76
79
  }
77
80
 
78
81
  const ViewMetricsTreemap = (props: ViewMetricsTreemapProps) => {
79
- const { metricsTableTitle, jobs, items, displayType, emptyMessage, showEntryInfo, updateSearch } =
80
- props;
82
+ const {
83
+ metricsTableTitle,
84
+ jobs,
85
+ items,
86
+ displayType,
87
+ emptyMessage,
88
+ showEntryInfo,
89
+ updateSearch,
90
+ search,
91
+ } = props;
81
92
 
93
+ // Get treenodes based on group
82
94
  const treeNodes = useMemo(() => {
83
95
  if (displayType.groupBy === 'folder') {
84
96
  return getTreemapNodesGroupedByPath(items);
@@ -87,19 +99,28 @@ const ViewMetricsTreemap = (props: ViewMetricsTreemapProps) => {
87
99
  return getTreemapNodes(items);
88
100
  }, [items, displayType]);
89
101
 
102
+ // Search based on the group path on group title click
90
103
  const onGroupClick = useCallback(
91
104
  (groupPath: string) => {
105
+ // Clear seach when groupPath is emty (root)
106
+ if (groupPath === '') {
107
+ updateSearch('');
108
+ return;
109
+ }
92
110
  // Search by group path
93
111
  // 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 {
112
+ // 2. add `/` suffix to exactly match the directory
113
+ const newSearch = `^${escapeRegExp(groupPath)}/`;
114
+
115
+ // Reset search when toggling the same groupPath
116
+ if (newSearch === search) {
99
117
  updateSearch('');
118
+ return;
100
119
  }
120
+
121
+ updateSearch(newSearch);
101
122
  },
102
- [updateSearch],
123
+ [updateSearch, search],
103
124
  );
104
125
 
105
126
  return (
@@ -122,7 +143,7 @@ interface BundleModulesProps extends React.ComponentProps<'div'> {
122
143
  jobs?: Array<Job>;
123
144
  items?: Array<ReportMetricModuleRow>;
124
145
  allItems?: Array<ReportMetricModuleRow>;
125
- chunks?: Array<Chunk>;
146
+ chunks?: Array<WebpackChunk>;
126
147
 
127
148
  totalRowCount: number;
128
149
 
@@ -305,6 +326,7 @@ export const BundleModules = (props: BundleModulesProps) => {
305
326
  emptyMessage={emptyMessage}
306
327
  showEntryInfo={showEntryInfo}
307
328
  updateSearch={updateSearch}
329
+ search={search}
308
330
  />
309
331
  )}
310
332
  </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),
@@ -1,6 +1,7 @@
1
1
  import React, { useCallback, useMemo } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import cx from 'classnames';
4
+ import { escapeRegExp } from 'lodash/escapeRegExp';
4
5
  import { COMPONENT, PACKAGE_FILTERS, PACKAGES_SEPARATOR, SECTIONS } from '@bundle-stats/utils';
5
6
 
6
7
  import config from '../../config.json';
@@ -120,8 +121,9 @@ RowHeader.propTypes = {
120
121
  };
121
122
 
122
123
  const ViewMetricsTreemap = (props) => {
123
- const { metricsTableTitle, jobs, items, displayType, emptyMessage, showEntryInfo, updateSearch } = props;
124
+ const { metricsTableTitle, jobs, items, displayType, emptyMessage, showEntryInfo, updateSearch, search } = props;
124
125
 
126
+ // Get treenodes based on group
125
127
  const treeNodes = useMemo(() => {
126
128
  if (displayType.groupBy === 'folder') {
127
129
  return getTreemapNodesGroupedByPath(items);
@@ -130,19 +132,28 @@ const ViewMetricsTreemap = (props) => {
130
132
  return getTreemapNodes(items);
131
133
  }, [items, displayType.groupBy]);
132
134
 
135
+ // Search based on the group path on group title click
133
136
  const onGroupClick = useCallback(
134
137
  (groupPath) => {
138
+ // Clear seach when groupPath is emty (root)
139
+ if (groupPath === '') {
140
+ updateSearch('');
141
+ return;
142
+ }
135
143
  // Search by group path
136
144
  // 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 {
145
+ // 2. add `/` suffix to exactly match the directory
146
+ const newSearch = `^${escapeRegExp(groupPath)}/`;
147
+
148
+ // Reset search when toggling the same groupPath
149
+ if (newSearch === search) {
142
150
  updateSearch('');
151
+ return;
143
152
  }
153
+
154
+ updateSearch(newSearch);
144
155
  },
145
- [updateSearch],
156
+ [updateSearch, search],
146
157
  );
147
158
 
148
159
  return (
@@ -171,6 +182,7 @@ ViewMetricsTreemap.propTypes = {
171
182
  emptyMessage: PropTypes.node.isRequired,
172
183
  showEntryInfo: PropTypes.func.isRequired,
173
184
  updateSearch: PropTypes.func.isRequired,
185
+ search: PropTypes.string.isRequired,
174
186
  };
175
187
 
176
188
  export const BundlePackages = (props) => {
@@ -296,10 +308,12 @@ export const BundlePackages = (props) => {
296
308
  <ViewMetricsTreemap
297
309
  jobs={jobs}
298
310
  items={items}
311
+ metricsTableTitle={metricsTableTitle}
299
312
  displayType={displayType}
300
313
  emptyMessage={emptyMessage}
301
314
  showEntryInfo={showEntryInfo}
302
315
  updateSearch={updateSearch}
316
+ search={search}
303
317
  />
304
318
  )}
305
319
  </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;