@bundle-stats/ui 4.23.0-beta.4 → 4.23.0-beta.6

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 (149) hide show
  1. package/lib/app/app.js +2 -1
  2. package/lib/app/app.js.map +1 -1
  3. package/lib/app/app.module.css +0 -5
  4. package/lib/components/asset-name/asset-name.js +8 -7
  5. package/lib/components/asset-name/asset-name.js.map +1 -1
  6. package/lib/components/asset-name/asset-name.module.css +11 -10
  7. package/lib/components/bundle-assets/bundle-assets.js +4 -2
  8. package/lib/components/bundle-assets/bundle-assets.js.map +1 -1
  9. package/lib/components/bundle-assets/bundle-assets.module.css +0 -12
  10. package/lib/components/bundle-assets-totals/bundle-assets-totals.js +4 -2
  11. package/lib/components/bundle-assets-totals/bundle-assets-totals.js.map +1 -1
  12. package/lib/components/bundle-modules/bundle-modules.js +5 -3
  13. package/lib/components/bundle-modules/bundle-modules.js.map +1 -1
  14. package/lib/components/bundle-modules/bundle-modules.module.css +2 -16
  15. package/lib/components/bundle-packages/bundle-packages.js +5 -3
  16. package/lib/components/bundle-packages/bundle-packages.js.map +1 -1
  17. package/lib/components/bundle-packages/bundle-packages.module.css +18 -22
  18. package/lib/components/jobs-header/jobs-header.js +1 -1
  19. package/lib/components/jobs-header/jobs-header.js.map +1 -1
  20. package/lib/components/jobs-header/jobs-header.module.css +0 -1
  21. package/lib/components/metrics-table/metrics-table.js +31 -20
  22. package/lib/components/metrics-table/metrics-table.js.map +1 -1
  23. package/lib/components/metrics-table/metrics-table.module.css +4 -12
  24. package/lib/components/metrics-table-options/metrics-table-options.js +4 -4
  25. package/lib/components/metrics-table-options/metrics-table-options.js.map +1 -1
  26. package/lib/components/metrics-table-row-count/index.js +18 -0
  27. package/lib/components/metrics-table-row-count/index.js.map +1 -0
  28. package/lib/components/metrics-table-row-count/metrics-table-row-count.js +17 -0
  29. package/lib/components/metrics-table-row-count/metrics-table-row-count.js.map +1 -0
  30. package/lib/components/metrics-table-row-count/metrics-table-row-count.module.css +5 -0
  31. package/lib/components/metrics-treemap/metrics-treemap.js +6 -4
  32. package/lib/components/metrics-treemap/metrics-treemap.js.map +1 -1
  33. package/lib/components/metrics-treemap/metrics-treemap.module.css +0 -7
  34. package/lib/components/summary/summary.js +5 -4
  35. package/lib/components/summary/summary.js.map +1 -1
  36. package/lib/components/summary/summary.module.css +0 -11
  37. package/lib/css/variables.css +3 -3
  38. package/lib/layout/flex-stack/flex-stack.js +2 -2
  39. package/lib/layout/flex-stack/flex-stack.js.map +1 -1
  40. package/lib/layout/flex-stack/flex-stack.module.css +65 -10
  41. package/lib/ui/button/button.js +1 -0
  42. package/lib/ui/button/button.js.map +1 -1
  43. package/lib/ui/button/button.module.css +15 -4
  44. package/lib/ui/control-group/control-group.js +1 -1
  45. package/lib/ui/control-group/control-group.js.map +1 -1
  46. package/lib/ui/filters/filters.js +7 -6
  47. package/lib/ui/filters/filters.js.map +1 -1
  48. package/lib/ui/filters/filters.module.css +1 -3
  49. package/lib/ui/table/table.module.css +11 -4
  50. package/lib/ui/tabs/tabs.module.css +1 -1
  51. package/lib/ui/toolbar/toolbar.js +2 -1
  52. package/lib/ui/toolbar/toolbar.js.map +1 -1
  53. package/lib/ui/toolbar/toolbar.module.css +0 -6
  54. package/lib-esm/app/app.js +2 -1
  55. package/lib-esm/app/app.js.map +1 -1
  56. package/lib-esm/app/app.module.css +0 -5
  57. package/lib-esm/components/asset-name/asset-name.js +8 -7
  58. package/lib-esm/components/asset-name/asset-name.js.map +1 -1
  59. package/lib-esm/components/asset-name/asset-name.module.css +11 -10
  60. package/lib-esm/components/bundle-assets/bundle-assets.js +4 -2
  61. package/lib-esm/components/bundle-assets/bundle-assets.js.map +1 -1
  62. package/lib-esm/components/bundle-assets/bundle-assets.module.css +0 -12
  63. package/lib-esm/components/bundle-assets-totals/bundle-assets-totals.js +4 -2
  64. package/lib-esm/components/bundle-assets-totals/bundle-assets-totals.js.map +1 -1
  65. package/lib-esm/components/bundle-modules/bundle-modules.js +5 -3
  66. package/lib-esm/components/bundle-modules/bundle-modules.js.map +1 -1
  67. package/lib-esm/components/bundle-modules/bundle-modules.module.css +2 -16
  68. package/lib-esm/components/bundle-packages/bundle-packages.js +5 -3
  69. package/lib-esm/components/bundle-packages/bundle-packages.js.map +1 -1
  70. package/lib-esm/components/bundle-packages/bundle-packages.module.css +18 -22
  71. package/lib-esm/components/jobs-header/jobs-header.js +1 -1
  72. package/lib-esm/components/jobs-header/jobs-header.js.map +1 -1
  73. package/lib-esm/components/jobs-header/jobs-header.module.css +0 -1
  74. package/lib-esm/components/metrics-table/metrics-table.js +32 -20
  75. package/lib-esm/components/metrics-table/metrics-table.js.map +1 -1
  76. package/lib-esm/components/metrics-table/metrics-table.module.css +4 -12
  77. package/lib-esm/components/metrics-table-options/metrics-table-options.js +4 -4
  78. package/lib-esm/components/metrics-table-options/metrics-table-options.js.map +1 -1
  79. package/lib-esm/components/metrics-table-row-count/index.js +2 -0
  80. package/lib-esm/components/metrics-table-row-count/index.js.map +1 -0
  81. package/lib-esm/components/metrics-table-row-count/metrics-table-row-count.js +10 -0
  82. package/lib-esm/components/metrics-table-row-count/metrics-table-row-count.js.map +1 -0
  83. package/lib-esm/components/metrics-table-row-count/metrics-table-row-count.module.css +5 -0
  84. package/lib-esm/components/metrics-treemap/metrics-treemap.js +6 -4
  85. package/lib-esm/components/metrics-treemap/metrics-treemap.js.map +1 -1
  86. package/lib-esm/components/metrics-treemap/metrics-treemap.module.css +0 -7
  87. package/lib-esm/components/summary/summary.js +5 -4
  88. package/lib-esm/components/summary/summary.js.map +1 -1
  89. package/lib-esm/components/summary/summary.module.css +0 -11
  90. package/lib-esm/css/variables.css +3 -3
  91. package/lib-esm/layout/flex-stack/flex-stack.js +3 -2
  92. package/lib-esm/layout/flex-stack/flex-stack.js.map +1 -1
  93. package/lib-esm/layout/flex-stack/flex-stack.module.css +65 -10
  94. package/lib-esm/ui/button/button.js +1 -0
  95. package/lib-esm/ui/button/button.js.map +1 -1
  96. package/lib-esm/ui/button/button.module.css +15 -4
  97. package/lib-esm/ui/control-group/control-group.js +1 -1
  98. package/lib-esm/ui/control-group/control-group.js.map +1 -1
  99. package/lib-esm/ui/filters/filters.js +7 -6
  100. package/lib-esm/ui/filters/filters.js.map +1 -1
  101. package/lib-esm/ui/filters/filters.module.css +1 -3
  102. package/lib-esm/ui/table/table.module.css +11 -4
  103. package/lib-esm/ui/tabs/tabs.module.css +1 -1
  104. package/lib-esm/ui/toolbar/toolbar.js +2 -1
  105. package/lib-esm/ui/toolbar/toolbar.js.map +1 -1
  106. package/lib-esm/ui/toolbar/toolbar.module.css +0 -6
  107. package/package.json +3 -3
  108. package/src/app/app.jsx +3 -2
  109. package/src/app/app.module.css +0 -5
  110. package/src/components/asset-name/asset-name.module.css +11 -10
  111. package/src/components/asset-name/asset-name.tsx +17 -14
  112. package/src/components/bundle-assets/bundle-assets.module.css +0 -12
  113. package/src/components/bundle-assets/bundle-assets.tsx +8 -1
  114. package/src/components/bundle-assets-totals/bundle-assets-totals.jsx +6 -4
  115. package/src/components/bundle-modules/bundle-modules.module.css +2 -16
  116. package/src/components/bundle-modules/bundle-modules.tsx +10 -3
  117. package/src/components/bundle-packages/bundle-packages.jsx +14 -3
  118. package/src/components/bundle-packages/bundle-packages.module.css +18 -22
  119. package/src/components/jobs-header/jobs-header.module.css +0 -1
  120. package/src/components/jobs-header/jobs-header.tsx +2 -2
  121. package/src/components/metrics-table/metrics-table.module.css +4 -12
  122. package/src/components/metrics-table/metrics-table.tsx +70 -39
  123. package/src/components/metrics-table-options/metrics-table-options.tsx +12 -12
  124. package/src/components/metrics-table-row-count/index.ts +1 -0
  125. package/src/components/metrics-table-row-count/metrics-table-row-count.module.css +5 -0
  126. package/src/components/metrics-table-row-count/metrics-table-row-count.tsx +21 -0
  127. package/src/components/metrics-treemap/metrics-treemap.module.css +0 -7
  128. package/src/components/metrics-treemap/metrics-treemap.tsx +18 -7
  129. package/src/components/summary/summary.module.css +0 -11
  130. package/src/components/summary/summary.tsx +9 -8
  131. package/src/css/variables.css +3 -3
  132. package/src/layout/flex-stack/flex-stack.module.css +65 -10
  133. package/src/layout/flex-stack/flex-stack.stories.tsx +68 -9
  134. package/src/layout/flex-stack/flex-stack.tsx +24 -4
  135. package/src/ui/button/button.module.css +15 -4
  136. package/src/ui/button/button.stories.tsx +1 -1
  137. package/src/ui/button/button.tsx +1 -0
  138. package/src/ui/control-group/control-group.tsx +1 -1
  139. package/src/ui/filters/filters.module.css +1 -3
  140. package/src/ui/filters/filters.tsx +22 -22
  141. package/src/ui/table/table.module.css +11 -4
  142. package/src/ui/tabs/tabs.module.css +1 -1
  143. package/src/ui/toolbar/toolbar.jsx +4 -1
  144. package/src/ui/toolbar/toolbar.module.css +0 -6
  145. package/types/components/metrics-table/metrics-table.d.ts +2 -1
  146. package/types/components/metrics-table-row-count/index.d.ts +1 -0
  147. package/types/components/metrics-table-row-count/metrics-table-row-count.d.ts +7 -0
  148. package/types/layout/flex-stack/flex-stack.d.ts +6 -2
  149. package/types/ui/button/button.d.ts +1 -0
@@ -26,6 +26,7 @@ import { MetricsTable } from '../metrics-table';
26
26
  import { MetricsTableExport } from '../metrics-table-export';
27
27
  import { MetricsTableOptions } from '../metrics-table-options';
28
28
  import { MetricsTableTitle } from '../metrics-table-title';
29
+ import { MetricsTableRowCount } from '../metrics-table-row-count';
29
30
  import { MetricsDisplaySelector } from '../metrics-display-selector';
30
31
  import { MetricsTableHeader } from '../metrics-table-header';
31
32
  import { MetricsTreemap, getTreemapNodes, getTreemapNodesGroupedByPath } from '../metrics-treemap';
@@ -164,7 +165,7 @@ export const BundleAssets = (props: BundleAssetsProps) => {
164
165
  () => (
165
166
  <MetricsTableTitle
166
167
  title={I18N.ASSETS}
167
- info={`${items.length}/${totalRowCount}`}
168
+ info={<MetricsTableRowCount count={items.length} total={totalRowCount} />}
168
169
  popoverInfo={I18N.ASSETS_INFO}
169
170
  popoverHref={config.documentation.assets}
170
171
  />
@@ -205,6 +206,11 @@ export const BundleAssets = (props: BundleAssetsProps) => {
205
206
  [EntryComponentLink],
206
207
  );
207
208
 
209
+ const handleRowClick = useCallback(
210
+ (row: ReportMetricRow) => showEntryInfo(row.key),
211
+ [showEntryInfo],
212
+ );
213
+
208
214
  const emptyMessage = useMemo(
209
215
  () => (
210
216
  <EmptySet
@@ -276,6 +282,7 @@ export const BundleAssets = (props: BundleAssetsProps) => {
276
282
  runs={jobs}
277
283
  items={items}
278
284
  renderRowHeader={renderRowHeader}
285
+ onRowClick={handleRowClick}
279
286
  emptyMessage={emptyMessage}
280
287
  showHeaderSum
281
288
  title={metricsTableTitle}
@@ -72,6 +72,10 @@ export const BundleAssetsTotals = ({
72
72
  [history],
73
73
  );
74
74
 
75
+ const handleItemClick = onTreemapItemClick || handleMetricsTreemapItemClick;
76
+
77
+ const handleRowClick = useCallback((row) => handleItemClick(row.key), [handleItemClick]);
78
+
75
79
  const exportDialog = useDialogState();
76
80
  const [exportSourceType, setExportSourceType] = useState(undefined);
77
81
 
@@ -98,6 +102,7 @@ export const BundleAssetsTotals = ({
98
102
  runs={jobs}
99
103
  items={items}
100
104
  renderRowHeader={renderRowHeader}
105
+ onRowClick={handleRowClick}
101
106
  showHeaderSum
102
107
  {...restProps}
103
108
  />
@@ -112,10 +117,7 @@ export const BundleAssetsTotals = ({
112
117
  rows={items}
113
118
  />
114
119
  </Table>
115
- <MetricsTreemap
116
- treeNodes={getTreemapNodes(items)}
117
- onItemClick={onTreemapItemClick || handleMetricsTreemapItemClick}
118
- />
120
+ <MetricsTreemap treeNodes={getTreemapNodes(items)} onItemClick={handleItemClick} />
119
121
  </>
120
122
  )}
121
123
  </Box>
@@ -12,8 +12,7 @@
12
12
  }
13
13
 
14
14
  .nameTagDuplicated {
15
- margin-right: var(--space-xxxsmall);
16
- vertical-align: baseline;
15
+ flex: 0 0 auto;
17
16
  }
18
17
 
19
18
  .nameTagDuplicated::before {
@@ -21,18 +20,5 @@
21
20
  }
22
21
 
23
22
  .nameText {
24
- display: inline-block;
25
- vertical-align: middle;
26
- }
27
-
28
- .root .name:hover,
29
- .root .name:active,
30
- .root .name:focus {
31
- text-decoration: none;
32
- }
33
-
34
- .root .name:hover .nameText,
35
- .root .name:active .nameText,
36
- .root .name:focus .nameText {
37
- text-decoration: underline;
23
+ flex: 1 1 auto;
38
24
  }
@@ -36,6 +36,7 @@ import { MetricsTreemap, getTreemapNodes, getTreemapNodesGroupedByPath } from '.
36
36
  import { MetricsTableOptions } from '../metrics-table-options';
37
37
  import { MetricsDisplaySelector } from '../metrics-display-selector';
38
38
  import { MetricsTableTitle } from '../metrics-table-title';
39
+ import { MetricsTableRowCount } from '../metrics-table-row-count';
39
40
  import { ModuleInfo } from '../module-info';
40
41
  import { generateFilterFieldsData } from './bundle-modules.utils';
41
42
  import * as I18N_MODULES from './bundle-modules.i18n';
@@ -57,12 +58,12 @@ const RowHeader = (props: RowHeaderProps) => {
57
58
  const moduleRow = row as ReportMetricModuleRow;
58
59
 
59
60
  return (
60
- <EntryComponentLink entryId={row.key} className={css.name}>
61
+ <FlexStack as={EntryComponentLink} entryId={row.key} inline alignItems="center" gap="xxxsmall">
61
62
  {moduleRow.duplicated && (
62
63
  <Tag className={css.nameTagDuplicated} size="small" kind={Tag.KINDS.DANGER} />
63
64
  )}
64
65
  <FileName className={css.nameText} name={moduleRow.label} />
65
- </EntryComponentLink>
66
+ </FlexStack>
66
67
  );
67
68
  };
68
69
 
@@ -228,7 +229,7 @@ export const BundleModules = (props: BundleModulesProps) => {
228
229
  () => (
229
230
  <MetricsTableTitle
230
231
  title={I18N.MODULES}
231
- info={`${items.length}/${totalRowCount}`}
232
+ info={<MetricsTableRowCount count={items.length} total={totalRowCount} />}
232
233
  popoverInfo={I18N.MODULES_INFO}
233
234
  popoverHref={config.documentation.modules}
234
235
  />
@@ -241,6 +242,11 @@ export const BundleModules = (props: BundleModulesProps) => {
241
242
  [EntryComponentLink],
242
243
  );
243
244
 
245
+ const handleRowClick = useCallback(
246
+ (row: ReportMetricRow) => showEntryInfo(row.key),
247
+ [showEntryInfo],
248
+ );
249
+
244
250
  const emptyMessage = useMemo(
245
251
  () => (
246
252
  <EmptySet
@@ -348,6 +354,7 @@ export const BundleModules = (props: BundleModulesProps) => {
348
354
  items={items}
349
355
  runs={jobs}
350
356
  renderRowHeader={renderRowHeader}
357
+ onRowClick={handleRowClick}
351
358
  emptyMessage={emptyMessage}
352
359
  showHeaderSum
353
360
  title={metricsTableTitle}
@@ -22,6 +22,7 @@ import { MetricsTableExport } from '../metrics-table-export';
22
22
  import { MetricsTableHeader } from '../metrics-table-header';
23
23
  import { MetricsTableOptions } from '../metrics-table-options';
24
24
  import { MetricsTableTitle } from '../metrics-table-title';
25
+ import { MetricsTableRowCount } from '../metrics-table-row-count';
25
26
  import { PackageInfo } from '../package-info';
26
27
  import { SEARCH_PLACEHOLDER } from './bundle-packages.i18n';
27
28
  import css from './bundle-packages.module.css';
@@ -48,7 +49,14 @@ const getDropdownFilters = ({ compareMode, filters }) => ({
48
49
 
49
50
  const PackageName = ({ row, packageName, showDetails, EntryComponentLink }) => {
50
51
  return (
51
- <EntryComponentLink entryId={showDetails ? row.key : packageName} className={css.packageName}>
52
+ <FlexStack
53
+ as={EntryComponentLink}
54
+ entryId={showDetails ? row.key : packageName}
55
+ inline
56
+ alignItems="center"
57
+ gap="xxxsmall"
58
+ className={css.packageName}
59
+ >
52
60
  {showDetails && row.duplicate && (
53
61
  <Tag
54
62
  className={css.packageNameTagDuplicate}
@@ -58,7 +66,7 @@ const PackageName = ({ row, packageName, showDetails, EntryComponentLink }) => {
58
66
  />
59
67
  )}
60
68
  <span className={css.packageNameLabel}>{packageName}</span>
61
- </EntryComponentLink>
69
+ </FlexStack>
62
70
  );
63
71
  };
64
72
 
@@ -217,7 +225,7 @@ export const BundlePackages = (props) => {
217
225
  () => (
218
226
  <MetricsTableTitle
219
227
  title={I18N.PACKAGES}
220
- info={`${items.length}/${totalRowCount}`}
228
+ info={<MetricsTableRowCount count={items.length} total={totalRowCount} />}
221
229
  popoverInfo={I18N.PACKAGES_INFO}
222
230
  popoverHref={config.documentation.packages}
223
231
  />
@@ -249,6 +257,8 @@ export const BundlePackages = (props) => {
249
257
  [PackageNameCustomComponentLink],
250
258
  );
251
259
 
260
+ const handleRowClick = useCallback((row) => showEntryInfo(row.key), [showEntryInfo]);
261
+
252
262
  const emptyMessage = useMemo(
253
263
  () => (
254
264
  <EmptySet
@@ -320,6 +330,7 @@ export const BundlePackages = (props) => {
320
330
  items={items}
321
331
  emptyMessage={emptyMessage}
322
332
  renderRowHeader={renderRowHeader}
333
+ onRowClick={handleRowClick}
323
334
  showHeaderSum
324
335
  title={metricsTableTitle}
325
336
  sort={sort}
@@ -12,8 +12,20 @@
12
12
  }
13
13
 
14
14
  .packageName {
15
- display: inline-block;
16
15
  color: var(--color-text-light);
16
+ text-decoration: none;
17
+ }
18
+
19
+ .packageNameTagDuplicate {
20
+ flex: 0 0 auto;
21
+ }
22
+
23
+ .packageNameTagDuplicate::before {
24
+ content: 'd';
25
+ }
26
+
27
+ .packageNameLabel {
28
+ flex: 1 1 auto;
17
29
  }
18
30
 
19
31
  .packageName:last-child {
@@ -25,29 +37,13 @@
25
37
  margin: 0 var(--space-xxxsmall);
26
38
  content: '/';
27
39
  color: var(--color-text-ultra-light);
40
+ text-decoration: none;
28
41
  }
29
42
 
30
- .packageNameTagDuplicate {
31
- margin-right: var(--space-xxxsmall);
32
- vertical-align: middle;
33
- }
34
-
35
- .packageNameLabel {
36
- vertical-align: middle;
37
- }
38
-
39
- .packageNameTagDuplicate::before {
40
- content: 'd';
41
- }
42
-
43
- .root .packageName:hover,
44
- .root .packageName:active,
45
- .root .packageName:focus {
46
- text-decoration: none;
43
+ .packageName:hover {
44
+ text-decoration: none !important;
47
45
  }
48
46
 
49
- .root .packageName:hover .packageNameLabel,
50
- .root .packageName:active .packageNameLabel,
51
- .root .packageName:focus .packageNameLabel {
52
- text-decoration: underline;
47
+ .packageName:hover .packageNameLabel {
48
+ text-decoration: underline !important;
53
49
  }
@@ -1,7 +1,6 @@
1
1
  .root {
2
2
  padding: var(--space-small) 0;
3
3
  width: 100%;
4
- display: flex;
5
4
  }
6
5
 
7
6
  .item {
@@ -76,7 +76,7 @@ export const JobsHeader = (props: JobsHeaderProps & React.ComponentProps<'header
76
76
  const rootClassName = cx(css.root, className);
77
77
 
78
78
  return (
79
- <header className={rootClassName}>
79
+ <FlexStack as="header" className={rootClassName}>
80
80
  {jobs?.map((job, index) => (
81
81
  <Item
82
82
  key={job.internalBuildNumber || index}
@@ -84,6 +84,6 @@ export const JobsHeader = (props: JobsHeaderProps & React.ComponentProps<'header
84
84
  tag={index === 0 ? I18N.CURRENT : I18N.BASELINE}
85
85
  />
86
86
  ))}
87
- </header>
87
+ </FlexStack>
88
88
  );
89
89
  };
@@ -1,21 +1,13 @@
1
- .root a {
2
- color: inherit;
3
- text-decoration: none;
4
- }
5
-
6
- .root a:hover,
7
- .root a:active,
8
- .root a:focus {
9
- color: var(--color-primary);
10
- text-decoration: underline;
11
- }
12
-
13
1
  .root .value,
14
2
  .root .delta {
15
3
  text-align: right;
16
4
  }
17
5
 
18
6
  /** rows */
7
+ .clickable {
8
+ cursor: default;
9
+ }
10
+
19
11
  .multipleRuns .unchanged th,
20
12
  .multipleRuns .unchanged td {
21
13
  color: var(--color-text-light);
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useCallback } from 'react';
2
2
  import cx from 'classnames';
3
3
  import isEmpty from 'lodash/isEmpty';
4
4
  import type { ReportMetricRow } from '@bundle-stats/utils';
@@ -20,51 +20,75 @@ const CURRENT_COLUMN_SPAN = 3;
20
20
  interface RowProps extends React.ComponentProps<typeof Table.Tr> {
21
21
  item: ReportMetricRow;
22
22
  renderHeader: (item: ReportMetricRow) => React.ReactNode;
23
+ onRowClick?: (item: ReportMetricRow) => void;
23
24
  }
24
25
 
25
- const Row = ({ className = '', item, renderHeader, ...restProps }: RowProps) => (
26
- <Table.Tr className={cx(!item.changed && css.unchanged, className)} {...restProps}>
27
- <Table.Th className={css.metricName}>{renderHeader(item)}</Table.Th>
26
+ const Row = ({ className = '', item, renderHeader, onRowClick, ...restProps }: RowProps) => {
27
+ const handleClick = useCallback(
28
+ (event: React.MouseEvent<HTMLTableRowElement>) => {
29
+ // Skip clicks on interactive elements (eg: links, buttons)
30
+ if ((event.target as HTMLElement).closest('a, button')) {
31
+ return;
32
+ }
33
+
34
+ // Skip when the user is selecting text
35
+ if (window.getSelection()?.toString()) {
36
+ return;
37
+ }
28
38
 
29
- {item.runs.map((run, index) => {
30
- const isBaseline = index === item.runs.length - 1;
31
- const valueClassName = cx(css.value, !isBaseline && css.current);
39
+ onRowClick?.(item);
40
+ },
41
+ [onRowClick, item],
42
+ );
43
+
44
+ return (
45
+ <Table.Tr
46
+ className={cx(!item.changed && css.unchanged, onRowClick && css.clickable, className)}
47
+ onClick={onRowClick ? handleClick : undefined}
48
+ {...restProps}
49
+ >
50
+ <Table.Th className={css.metricName}>{renderHeader(item)}</Table.Th>
51
+
52
+ {item.runs.map((run, index) => {
53
+ const isBaseline = index === item.runs.length - 1;
54
+ const valueClassName = cx(css.value, !isBaseline && css.current);
55
+
56
+ // Empty cells if no value
57
+ if (!run || typeof run.value === 'undefined') {
58
+ return (
59
+ <>
60
+ <Table.Td className={valueClassName}>-</Table.Td>
61
+ {!isBaseline && <Table.Td className={css.delta} />}
62
+ {!isBaseline && <Table.Td className={cx(css.delta, css.deltaPercentage)} />}
63
+ </>
64
+ );
65
+ }
32
66
 
33
- // Empty cells if no value
34
- if (!run || typeof run.value === 'undefined') {
35
67
  return (
36
68
  <>
37
- <Table.Td className={valueClassName}>-</Table.Td>
38
- {!isBaseline && <Table.Td className={css.delta} />}
39
- {!isBaseline && <Table.Td className={cx(css.delta, css.deltaPercentage)} />}
69
+ <Table.Td className={valueClassName}>
70
+ <Metric value={run.displayValue} />
71
+ </Table.Td>
72
+ {!isBaseline && (
73
+ <>
74
+ <Table.Td className={css.delta}>
75
+ {'delta' in run && (
76
+ <Delta displayValue={run.displayDelta} deltaType={run.deltaType} />
77
+ )}
78
+ </Table.Td>
79
+ <Table.Td className={cx(css.delta, css.deltaPercentage)}>
80
+ {'delta' in run && (
81
+ <Delta displayValue={run.displayDeltaPercentage} deltaType={run.deltaType} />
82
+ )}
83
+ </Table.Td>
84
+ </>
85
+ )}
40
86
  </>
41
87
  );
42
- }
43
-
44
- return (
45
- <>
46
- <Table.Td className={valueClassName}>
47
- <Metric value={run.displayValue} />
48
- </Table.Td>
49
- {!isBaseline && (
50
- <>
51
- <Table.Td className={css.delta}>
52
- {'delta' in run && (
53
- <Delta displayValue={run.displayDelta} deltaType={run.deltaType} />
54
- )}
55
- </Table.Td>
56
- <Table.Td className={cx(css.delta, css.deltaPercentage)}>
57
- {'delta' in run && (
58
- <Delta displayValue={run.displayDeltaPercentage} deltaType={run.deltaType} />
59
- )}
60
- </Table.Td>
61
- </>
62
- )}
63
- </>
64
- );
65
- })}
66
- </Table.Tr>
67
- );
88
+ })}
89
+ </Table.Tr>
90
+ );
91
+ };
68
92
 
69
93
  interface MetricsTableProps extends Omit<React.ComponentProps<typeof Table>, 'title'> {
70
94
  runs: Array<{
@@ -77,6 +101,7 @@ interface MetricsTableProps extends Omit<React.ComponentProps<typeof Table>, 'ti
77
101
  sort?: MetricsTableHeaderProps['sort'];
78
102
  updateSort?: MetricsTableHeaderProps['updateSort'];
79
103
  renderRowHeader?: (item: ReportMetricRow) => React.ReactNode;
104
+ onRowClick?: (item: ReportMetricRow) => void;
80
105
  emptyMessage?: React.ReactNode;
81
106
  showAllItems: boolean;
82
107
  setShowAllItems: (value: boolean) => void;
@@ -91,6 +116,7 @@ export const MetricsTable = ({
91
116
  sort,
92
117
  updateSort,
93
118
  renderRowHeader = (item: any) => item.label,
119
+ onRowClick,
94
120
  emptyMessage = I18N.EMPTY_MESSAGE,
95
121
  showAllItems,
96
122
  setShowAllItems,
@@ -132,7 +158,12 @@ export const MetricsTable = ({
132
158
  {showItems && (
133
159
  <>
134
160
  {visibleItems.map((item) => (
135
- <Row key={item.key} item={item} renderHeader={renderRowHeader} />
161
+ <Row
162
+ key={item.key}
163
+ item={item}
164
+ renderHeader={renderRowHeader}
165
+ onRowClick={onRowClick}
166
+ />
136
167
  ))}
137
168
 
138
169
  {hasHiddenItems && (
@@ -15,18 +15,18 @@ export const MetricsTableOptions = (props: MetricsTableOptionsProps) => {
15
15
 
16
16
  return (
17
17
  <Dropdown glyph="more-vertical" placement="bottom-end" {...restProps}>
18
- <DropdownGroup>
19
- {onResetClick && <DropdownItem onClick={onResetClick}>{I18N.RESET_FILTERS}</DropdownItem>}
20
- {onViewAllClick && <DropdownItem onClick={onViewAllClick}>{I18N.VIEW_ALL}</DropdownItem>}
21
- </DropdownGroup>
22
- <DropdownGroup>
23
- {onExportClick && (
24
- <>
25
- <DropdownItem onClick={() => onExportClick('csv')}>{I18N.EXPORT_CSV}</DropdownItem>
26
- <DropdownItem onClick={() => onExportClick('json')}>{I18N.EXPORT_JSON}</DropdownItem>
27
- </>
28
- )}
29
- </DropdownGroup>
18
+ {(onResetClick || onViewAllClick) && (
19
+ <DropdownGroup>
20
+ {onResetClick && <DropdownItem onClick={onResetClick}>{I18N.RESET_FILTERS}</DropdownItem>}
21
+ {onViewAllClick && <DropdownItem onClick={onViewAllClick}>{I18N.VIEW_ALL}</DropdownItem>}
22
+ </DropdownGroup>
23
+ )}
24
+ {onExportClick && (
25
+ <DropdownGroup>
26
+ <DropdownItem onClick={() => onExportClick('csv')}>{I18N.EXPORT_CSV}</DropdownItem>
27
+ <DropdownItem onClick={() => onExportClick('json')}>{I18N.EXPORT_JSON}</DropdownItem>
28
+ </DropdownGroup>
29
+ )}
30
30
  </Dropdown>
31
31
  );
32
32
  };
@@ -0,0 +1 @@
1
+ export * from './metrics-table-row-count';
@@ -0,0 +1,5 @@
1
+ .root {
2
+ display: inline-flex;
3
+ align-items: baseline;
4
+ gap: 2px;
5
+ }
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+
3
+ import { FlexStack } from '../../layout';
4
+ import { FlexStackProps } from '../../layout/flex-stack';
5
+
6
+ export type MetricsTableRowCountProps = {
7
+ count: number;
8
+ total: number;
9
+ } & FlexStackProps<'span'>;
10
+
11
+ export const MetricsTableRowCount = (props: MetricsTableRowCountProps) => {
12
+ const { count, total, ...restProps } = props;
13
+
14
+ return (
15
+ <FlexStack inline gap="xxxsmall" alignItems="bottom" {...restProps}>
16
+ <span>{count}</span>
17
+ <span>/</span>
18
+ <span>{total}</span>
19
+ </FlexStack>
20
+ );
21
+ };
@@ -163,11 +163,7 @@
163
163
  z-index: 1;
164
164
  width: 100%;
165
165
  height: 100%;
166
- display: flex;
167
- flex-flow: column;
168
- justify-content: center;
169
166
  padding: var(--space-xxsmall);
170
- gap: var(--space-xxxsmall);
171
167
  line-height: var(--line-height-heading);
172
168
  cursor: default;
173
169
  }
@@ -284,9 +280,6 @@
284
280
  top: 0;
285
281
  width: 100%;
286
282
  height: 100%;
287
- display: flex;
288
- align-items: center;
289
- justify-content: center;
290
283
  }
291
284
 
292
285
  .emptyMessageWrapper {
@@ -20,6 +20,7 @@ import {
20
20
  getMetricRunInfo,
21
21
  } from '@bundle-stats/utils';
22
22
 
23
+ import { FlexStack } from '../../layout/flex-stack';
23
24
  import { Stack } from '../../layout/stack';
24
25
  import { FileName } from '../../ui/file-name';
25
26
  import { Delta } from '../delta';
@@ -110,12 +111,22 @@ interface TileContentProps {
110
111
  runInfo: MetricRunInfo;
111
112
  }
112
113
 
114
+ const TileContentContainer = (props: ComponentProps<'div'>) => (
115
+ <FlexStack
116
+ direction="column"
117
+ justifyContent="center"
118
+ gap="xxxsmall"
119
+ className={css.tileContent}
120
+ {...props}
121
+ />
122
+ );
123
+
113
124
  const TileContent = (props: TileContentProps) => {
114
125
  const { label, sizeDisplay, item, runInfo } = props;
115
126
 
116
127
  // Render only the container
117
128
  if (sizeDisplay === 'minimal') {
118
- return <div className={css.tileContent} />;
129
+ return <TileContentContainer />;
119
130
  }
120
131
 
121
132
  const resolvedLabel = label || item.label;
@@ -123,14 +134,14 @@ const TileContent = (props: TileContentProps) => {
123
134
  // Render only the label
124
135
  if (sizeDisplay === 'small') {
125
136
  return (
126
- <div className={css.tileContent}>
137
+ <TileContentContainer>
127
138
  <p className={css.tileContentLabel}>{resolvedLabel}</p>
128
- </div>
139
+ </TileContentContainer>
129
140
  );
130
141
  }
131
142
 
132
143
  return (
133
- <div className={css.tileContent}>
144
+ <TileContentContainer>
134
145
  <p className={css.tileContentLabel}>{label || item.label}</p>
135
146
  <p className={css.tileContentValue}>
136
147
  <span className={css.tileContentMetric}>{runInfo.displayValue}</span>
@@ -140,7 +151,7 @@ const TileContent = (props: TileContentProps) => {
140
151
  deltaType={runInfo.deltaType}
141
152
  />
142
153
  </p>
143
- </div>
154
+ </TileContentContainer>
144
155
  );
145
156
  };
146
157
 
@@ -585,9 +596,9 @@ export const MetricsTreemap = (props: MetricsTreemapProps & ComponentProps<'div'
585
596
  {canvasContent}
586
597
  </div>
587
598
  ) : (
588
- <div className={css.emptyMessage}>
599
+ <FlexStack alignItems="center" justifyContent="center" className={css.emptyMessage}>
589
600
  <div className={css.emptyMessageWrapper}>{emptyMessage}</div>
590
- </div>
601
+ </FlexStack>
591
602
  )}
592
603
  {tooltipNode && (
593
604
  <Tooltip state={tooltipState} className={css.tooltip}>
@@ -5,10 +5,6 @@
5
5
 
6
6
  .row {
7
7
  width: 100%;
8
- display: flex;
9
- align-items: stretch;
10
- flex-wrap: wrap;
11
- gap: var(--space-small);
12
8
  }
13
9
 
14
10
  .rowGroup {
@@ -26,13 +22,6 @@
26
22
  margin-top: var(--summary-gap);
27
23
  }
28
24
 
29
- .items {
30
- display: flex;
31
- align-items: stretch;
32
- flex-wrap: wrap;
33
- gap: var(--space-medium);
34
- }
35
-
36
25
  .item {
37
26
  flex: 1 1 10%; /* force equal sizes */
38
27
  min-width: 8em;