@bundle-stats/ui 4.13.0-beta.19 → 4.13.0-beta.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/lib/components/bundle-assets-totals/bundle-assets-totals.js +1 -1
  2. package/lib/components/bundle-assets-totals/bundle-assets-totals.js.map +1 -1
  3. package/lib/components/metrics-treemap/metrics-treemap.i18n.js +6 -0
  4. package/lib/components/metrics-treemap/metrics-treemap.i18n.js.map +1 -0
  5. package/lib/components/metrics-treemap/metrics-treemap.js +24 -12
  6. package/lib/components/metrics-treemap/metrics-treemap.js.map +1 -1
  7. package/lib/components/metrics-treemap/metrics-treemap.module.css +70 -25
  8. package/lib/components/metrics-treemap/metrics-treemap.utils.js +44 -5
  9. package/lib/components/metrics-treemap/metrics-treemap.utils.js.map +1 -1
  10. package/lib-esm/components/bundle-assets-totals/bundle-assets-totals.js +1 -1
  11. package/lib-esm/components/bundle-assets-totals/bundle-assets-totals.js.map +1 -1
  12. package/lib-esm/components/metrics-treemap/metrics-treemap.i18n.js +3 -0
  13. package/lib-esm/components/metrics-treemap/metrics-treemap.i18n.js.map +1 -0
  14. package/lib-esm/components/metrics-treemap/metrics-treemap.js +25 -12
  15. package/lib-esm/components/metrics-treemap/metrics-treemap.js.map +1 -1
  16. package/lib-esm/components/metrics-treemap/metrics-treemap.module.css +70 -25
  17. package/lib-esm/components/metrics-treemap/metrics-treemap.utils.js +44 -4
  18. package/lib-esm/components/metrics-treemap/metrics-treemap.utils.js.map +1 -1
  19. package/package.json +3 -3
  20. package/src/components/bundle-assets-totals/bundle-assets-totals.jsx +0 -1
  21. package/src/components/metrics-treemap/__tests__/metrics-treemap.utils.ts +59 -14
  22. package/src/components/metrics-treemap/metrics-treemap.constants.ts +9 -0
  23. package/src/components/metrics-treemap/metrics-treemap.i18n.ts +2 -0
  24. package/src/components/metrics-treemap/metrics-treemap.module.css +70 -25
  25. package/src/components/metrics-treemap/metrics-treemap.tsx +55 -21
  26. package/src/components/metrics-treemap/metrics-treemap.utils.ts +63 -8
  27. package/types/components/metrics-treemap/metrics-treemap.constants.d.ts +5 -0
  28. package/types/components/metrics-treemap/metrics-treemap.i18n.d.ts +2 -0
  29. package/types/components/metrics-treemap/metrics-treemap.utils.d.ts +2 -1
@@ -13,19 +13,27 @@ import { useDebounce, useHoverDirty, useMeasure, useMouseHovered } from 'react-u
13
13
  import cx from 'classnames';
14
14
  import { HierarchyRectangularNode, hierarchy, treemap, treemapSquarify } from 'd3';
15
15
  import { Tooltip, TooltipArrow, TooltipAnchor, useTooltipState } from 'ariakit/tooltip';
16
- import type { ReportMetricRow, MetricRunInfo, MetricRunInfoBaseline } from '@bundle-stats/utils';
16
+ import {
17
+ type ReportMetricRow,
18
+ type MetricRunInfo,
19
+ type MetricRunInfoBaseline,
20
+ getMetricRunInfo,
21
+ METRIC_TYPE_CONFIGS,
22
+ } from '@bundle-stats/utils';
17
23
 
18
24
  import { Stack } from '../../layout/stack';
19
25
  import { FileName } from '../../ui/file-name';
20
26
  import { Delta } from '../delta';
21
27
  import { RunInfo } from '../run-info';
22
28
  import type { TreeLeaf, TreeNode, Tree } from './metrics-treemap.constants';
29
+ import * as I18N from './metrics-treemap.i18n';
23
30
  import css from './metrics-treemap.module.css';
31
+ import { resolveGroupDeltaType } from './metrics-treemap.utils';
24
32
 
25
- const SQUARIFY_RATIO = 1.33;
33
+ const SQUARIFY_RATIO = 1.66;
26
34
  const PADDING_OUTER = 1;
27
35
  const PADDING_INNER = 1;
28
- const GROUPED_PADDING_TOP = 16;
36
+ const GROUPED_PADDING_TOP = 18;
29
37
 
30
38
  /**
31
39
  * Resolve the tile's size using predefined values to avoid
@@ -33,7 +41,7 @@ const GROUPED_PADDING_TOP = 16;
33
41
  */
34
42
  const PADDING_TOP = 4;
35
43
  const PADDING_BOTTOM = 2; // substracted to allow longer texts to appear
36
- const PADDING_LEFT = 16;
44
+ const PADDING_LEFT = 2;
37
45
  const PADDING_RIGHT = 4; // substracted to allow longer texts to not break
38
46
  const VERTICAL_SPACING = 4;
39
47
  const LINE_HEIGHT = 16;
@@ -195,7 +203,7 @@ const Tile = (props: TileProps) => {
195
203
  const runInfo = item.runs?.[0] as MetricRunInfo;
196
204
 
197
205
  const sizeDisplay = useMemo(() => resolveTileSizeDisplay(width, height), [width, height]);
198
- const handleOnClick: MouseEventHandler<HTMLButtonElement> = useCallback(
206
+ const handleOnClick: MouseEventHandler<HTMLDivElement> = useCallback(
199
207
  (event) => {
200
208
  event.stopPropagation();
201
209
  onClick?.(item.key);
@@ -203,7 +211,7 @@ const Tile = (props: TileProps) => {
203
211
  [item.key, onClick],
204
212
  );
205
213
 
206
- const contentRef = useRef<HTMLButtonElement>(null);
214
+ const contentRef = useRef<HTMLDivElement>(null);
207
215
  const hover = useHoverDirty(contentRef);
208
216
 
209
217
  const className = cx(
@@ -214,10 +222,12 @@ const Tile = (props: TileProps) => {
214
222
  left === 0 && css.tileFirstCol,
215
223
  );
216
224
 
225
+ /* eslint-disable jsx-a11y/click-events-have-key-events, jsx-a11y/interactive-supports-focus */
217
226
  return (
218
- <button
219
- type="button"
227
+ <div
220
228
  onClick={handleOnClick}
229
+ role="button"
230
+ aria-label={I18N.TILE_LABEL}
221
231
  ref={contentRef}
222
232
  style={{ left, top, width, height }}
223
233
  className={className}
@@ -233,9 +243,8 @@ const Tile = (props: TileProps) => {
233
243
  ) : (
234
244
  <TileContent label={label} sizeDisplay={sizeDisplay} item={item} runInfo={runInfo} />
235
245
  )}
236
- </button>
246
+ </div>
237
247
  );
238
- /* eslint-disable jsx-a11y/click-events-have-key-events, jsx-a11y/interactive-supports-focus */
239
248
  };
240
249
 
241
250
  interface TileGroupProps extends ComponentProps<'div'> {
@@ -243,6 +252,10 @@ interface TileGroupProps extends ComponentProps<'div'> {
243
252
  * Node title
244
253
  */
245
254
  title?: string;
255
+ /**
256
+ * Group sum
257
+ */
258
+ total?: Tree['total'];
246
259
  /**
247
260
  * Node id
248
261
  */
@@ -282,6 +295,7 @@ interface TileGroupProps extends ComponentProps<'div'> {
282
295
  const TileGroup = (props: TileGroupProps) => {
283
296
  const {
284
297
  title = '',
298
+ total,
285
299
  id,
286
300
  childNodes,
287
301
  onItemClick,
@@ -307,13 +321,19 @@ const TileGroup = (props: TileGroupProps) => {
307
321
  [width, height],
308
322
  );
309
323
 
324
+ const metricRunInfo =
325
+ total &&
326
+ getMetricRunInfo(METRIC_TYPE_CONFIGS.METRIC_TYPE_FILE_SIZE, total.current, total.baseline);
327
+ const groupDeltaType = resolveGroupDeltaType(metricRunInfo);
328
+ const rootClassName = cx(css.tileGroup, css[`tileGroup--${groupDeltaType}`]);
329
+
310
330
  if (title && displaySize === 'minimal') {
311
331
  return (
312
332
  <div
313
333
  onClick={onClick}
314
334
  role="button"
315
- aria-label="View children entries"
316
- className={css.tileGroup}
335
+ aria-label={I18N.TILE_GROUP_LABEL}
336
+ className={rootClassName}
317
337
  style={{ left, top, width, height }}
318
338
  />
319
339
  );
@@ -324,9 +344,9 @@ const TileGroup = (props: TileGroupProps) => {
324
344
  <div
325
345
  onClick={onClick}
326
346
  role="button"
327
- aria-label="View children entries"
347
+ aria-label={I18N.TILE_GROUP_LABEL}
328
348
  style={{ left, top, width, height }}
329
- className={css.tileGroup}
349
+ className={rootClassName}
330
350
  >
331
351
  <div className={css.tileGroupTitle}>{title}</div>
332
352
  </div>
@@ -337,17 +357,30 @@ const TileGroup = (props: TileGroupProps) => {
337
357
  <div
338
358
  onClick={onClick}
339
359
  role="button"
340
- aria-label="View children entries"
341
- className={cx(css.tileGroup, css.tileGroupSizeDefault)}
360
+ aria-label={I18N.TILE_GROUP_LABEL}
361
+ className={cx(rootClassName, css.tileGroupSizeDefault)}
342
362
  style={{ left, top, width, height }}
343
363
  >
344
- {title && <div className={css.tileGroupTitle}>{title}</div>}
364
+ {title && (
365
+ <div className={css.tileGroupTitle}>
366
+ {title}
367
+ {metricRunInfo && (
368
+ <span className={css.tileGroupTitleTotal}>
369
+ {`${metricRunInfo.displayValue}`}
370
+ {'displayDelta' in metricRunInfo && ` (${metricRunInfo.displayDeltaPercentage})`}
371
+ </span>
372
+ )}
373
+ </div>
374
+ )}
345
375
  {childNodes?.map((childNode) => {
346
376
  if ('children' in childNode) {
377
+ const groupData = childNode.data as Tree;
378
+
347
379
  return (
348
380
  <TileGroup
349
- title={childNode.data.label}
350
- id={childNode.data.id}
381
+ title={groupData.label}
382
+ total={groupData.total}
383
+ id={groupData.id}
351
384
  childNodes={childNode.children}
352
385
  left={childNode.x0 - absoluteLeft}
353
386
  top={childNode.y0 - absoluteTop}
@@ -357,7 +390,7 @@ const TileGroup = (props: TileGroupProps) => {
357
390
  height={childNode.y1 - childNode.y0}
358
391
  onItemClick={onItemClick}
359
392
  onGroupClick={onGroupClick}
360
- key={childNode.data.id}
393
+ key={groupData.id}
361
394
  />
362
395
  );
363
396
  }
@@ -446,10 +479,11 @@ export const MetricsTreemap = (props: MetricsTreemapProps & ComponentProps<'div'
446
479
  {...restProps}
447
480
  ref={containerRef}
448
481
  >
449
- {rootNode.children && rootNode.children.length > 0 ? (
482
+ {rootNode.children && rootNode.children?.length > 0 ? (
450
483
  <div className={css.canvas}>
451
484
  <TileGroup
452
485
  title={nested ? rootNode.data.label : undefined}
486
+ total={(rootNode.data as Tree).total}
453
487
  id={rootNode.data.id}
454
488
  childNodes={rootNode.children}
455
489
  onItemClick={onItemClick}
@@ -1,6 +1,11 @@
1
- import type { ReportMetricRow } from '@bundle-stats/utils';
1
+ import {
2
+ DeltaType,
3
+ type MetricRunInfoBaseline,
4
+ type MetricRunInfo,
5
+ type ReportMetricRow,
6
+ } from '@bundle-stats/utils';
2
7
 
3
- import type { TreeLeaf, TreeNodeChildren, Tree } from './metrics-treemap.constants';
8
+ import type { TreeLeaf, TreeNodeChildren, Tree, TreeTotal } from './metrics-treemap.constants';
4
9
 
5
10
  const ROOT_LABEL = '(root)';
6
11
 
@@ -32,6 +37,20 @@ export function getTreemapNodes(items: Array<ReportMetricRow>): Tree {
32
37
  };
33
38
  }
34
39
 
40
+ function getItemValues(runs: ReportMetricRow['runs']): TreeTotal {
41
+ const runCount = runs?.length;
42
+ const current = runs[0]?.value || 0;
43
+
44
+ if (runCount < 2) {
45
+ return { current, baseline: 0 };
46
+ }
47
+
48
+ return {
49
+ current,
50
+ baseline: runs[runCount - 1]?.value || 0,
51
+ };
52
+ }
53
+
35
54
  /**
36
55
  * Recursively set treemap nodes
37
56
  */
@@ -40,14 +59,14 @@ function setTreeNode(
40
59
  slugs: Array<string>,
41
60
  currentSlugId: number,
42
61
  newNode: TreeLeaf,
43
- ): void {
62
+ ): TreeTotal {
44
63
  const baseSlugs = slugs.slice(0, currentSlugId);
45
64
  const [currentSlug, ...restSlug] = slugs.slice(currentSlugId);
46
65
 
47
66
  // Add to current nodes if there are no child nodes
48
67
  if (!currentSlug) {
49
68
  nodes.push(newNode);
50
- return;
69
+ return getItemValues(newNode.item.runs);
51
70
  }
52
71
 
53
72
  const currentNodePath = [...baseSlugs, currentSlug].join('/');
@@ -62,18 +81,30 @@ function setTreeNode(
62
81
  label: currentSlug,
63
82
  value: 0,
64
83
  children: [],
84
+ total: {
85
+ current: 0,
86
+ baseline: 0,
87
+ },
65
88
  };
66
89
 
67
90
  nodes.push(parentNode);
68
91
  }
69
92
 
93
+ // accumulate children values
94
+ const nodeValues = getItemValues(newNode?.item?.runs);
95
+
96
+ parentNode.total = {
97
+ current: (parentNode.total?.current || 0) + nodeValues.current,
98
+ baseline: (parentNode.total?.baseline || 0) + (nodeValues.baseline || 0),
99
+ };
100
+
70
101
  // If there are no other slugs, the new node is as leaf and we add it to the parent
71
102
  if (restSlug.length === 0) {
72
103
  parentNode.children.push(newNode);
73
- return;
104
+ return getItemValues(newNode.item.runs);
74
105
  }
75
106
 
76
- setTreeNode(parentNode.children, slugs, currentSlugId + 1, newNode);
107
+ return setTreeNode(parentNode.children, slugs, currentSlugId + 1, newNode);
77
108
  }
78
109
 
79
110
  /**
@@ -81,9 +112,9 @@ function setTreeNode(
81
112
  */
82
113
  export function getTreemapNodesGroupedByPath(items: Array<ReportMetricRow>): Tree {
83
114
  const treeNodes: TreeNodeChildren = [];
115
+ const total = { current: 0, baseline: 0 };
84
116
 
85
117
  items.forEach((item) => {
86
- // const normalizedPath = item.key.replace(/^\.?\//, ''); // replace './' or '/' prefix
87
118
  const slugs = item.key.split('/');
88
119
  const baseSlugs = slugs.slice(0, -1);
89
120
  const baseName = slugs.slice(-1)[0];
@@ -95,7 +126,10 @@ export function getTreemapNodesGroupedByPath(items: Array<ReportMetricRow>): Tre
95
126
  item,
96
127
  };
97
128
 
98
- setTreeNode(treeNodes, baseSlugs, 0, treeNode);
129
+ const childrenTotal = setTreeNode(treeNodes, baseSlugs, 0, treeNode);
130
+
131
+ total.current += childrenTotal.current || 0;
132
+ total.baseline += childrenTotal.baseline || 0;
99
133
  });
100
134
 
101
135
  return {
@@ -103,5 +137,26 @@ export function getTreemapNodesGroupedByPath(items: Array<ReportMetricRow>): Tre
103
137
  label: ROOT_LABEL,
104
138
  value: 0,
105
139
  children: treeNodes,
140
+ total,
106
141
  };
107
142
  }
143
+
144
+ export function resolveGroupDeltaType(
145
+ metricRunInfo?: MetricRunInfo | MetricRunInfoBaseline,
146
+ ): DeltaType.NO_CHANGE | DeltaType.NEGATIVE | DeltaType.POSITIVE {
147
+ if (!metricRunInfo) {
148
+ return DeltaType.NO_CHANGE;
149
+ }
150
+
151
+ const deltaType = 'displayDelta' in metricRunInfo ? metricRunInfo.deltaType : undefined;
152
+
153
+ if (deltaType?.match(/NEGATIVE/)) {
154
+ return DeltaType.NEGATIVE;
155
+ }
156
+
157
+ if (deltaType?.match(/POSITIVE/)) {
158
+ return DeltaType.POSITIVE;
159
+ }
160
+
161
+ return DeltaType.NO_CHANGE;
162
+ }
@@ -1,4 +1,8 @@
1
1
  import type { ReportMetricRow } from '@bundle-stats/utils';
2
+ export interface TreeTotal {
3
+ current: number;
4
+ baseline: number;
5
+ }
2
6
  export interface TreeLeaf {
3
7
  id: string;
4
8
  label: string;
@@ -10,6 +14,7 @@ export interface Tree {
10
14
  label: string;
11
15
  value: number;
12
16
  children: TreeNodeChildren;
17
+ total?: TreeTotal;
13
18
  }
14
19
  export type TreeNode = Tree | TreeLeaf;
15
20
  export type TreeNodeChildren = Array<TreeNode>;
@@ -0,0 +1,2 @@
1
+ export declare const TILE_GROUP_LABEL = "View children entries";
2
+ export declare const TILE_LABEL = "View entry info";
@@ -1,4 +1,5 @@
1
- import type { ReportMetricRow } from '@bundle-stats/utils';
1
+ import { DeltaType, type MetricRunInfoBaseline, type MetricRunInfo, type ReportMetricRow } from '@bundle-stats/utils';
2
2
  import type { Tree } from './metrics-treemap.constants';
3
3
  export declare function getTreemapNodes(items: Array<ReportMetricRow>): Tree;
4
4
  export declare function getTreemapNodesGroupedByPath(items: Array<ReportMetricRow>): Tree;
5
+ export declare function resolveGroupDeltaType(metricRunInfo?: MetricRunInfo | MetricRunInfoBaseline): DeltaType.NO_CHANGE | DeltaType.NEGATIVE | DeltaType.POSITIVE;