@bundle-stats/ui 4.13.0-beta.5 → 4.13.0-beta.7

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 (96) hide show
  1. package/lib/components/bundle-assets/bundle-assets.js +7 -2
  2. package/lib/components/bundle-assets/bundle-assets.js.map +1 -1
  3. package/lib/components/bundle-assets-totals/bundle-assets-totals.js +13 -6
  4. package/lib/components/bundle-assets-totals/bundle-assets-totals.js.map +1 -1
  5. package/lib/components/bundle-modules/bundle-modules.js +6 -1
  6. package/lib/components/bundle-modules/bundle-modules.js.map +1 -1
  7. package/lib/components/bundle-packages/bundle-packages.js +6 -1
  8. package/lib/components/bundle-packages/bundle-packages.js.map +1 -1
  9. package/lib/components/index.js +1 -3
  10. package/lib/components/index.js.map +1 -1
  11. package/lib/components/metrics-table/metrics-table.i18n.js +1 -6
  12. package/lib/components/metrics-table/metrics-table.i18n.js.map +1 -1
  13. package/lib/components/metrics-table/metrics-table.js +4 -37
  14. package/lib/components/metrics-table/metrics-table.js.map +1 -1
  15. package/lib/components/metrics-table/metrics-table.module.css +1 -44
  16. package/lib/components/{total-size-type-title → metrics-table-header}/index.js +1 -1
  17. package/lib/components/metrics-table-header/index.js.map +1 -0
  18. package/lib/components/metrics-table-header/metrics-table-header.i18n.js +9 -0
  19. package/lib/components/metrics-table-header/metrics-table-header.i18n.js.map +1 -0
  20. package/lib/components/metrics-table-header/metrics-table-header.js +79 -0
  21. package/lib/components/metrics-table-header/metrics-table-header.js.map +1 -0
  22. package/lib/components/metrics-table-header/metrics-table-header.module.css +44 -0
  23. package/lib/components/metrics-table-title/metrics-table-title.js +1 -1
  24. package/lib/components/metrics-table-title/metrics-table-title.js.map +1 -1
  25. package/lib/components/metrics-table-title/metrics-table-title.module.css +3 -4
  26. package/lib/components/metrics-treemap/metrics-treemap.js +4 -6
  27. package/lib/components/metrics-treemap/metrics-treemap.js.map +1 -1
  28. package/lib/components/metrics-treemap/metrics-treemap.module.css +51 -32
  29. package/lib/components/sort-button/sort-button.module.css +1 -0
  30. package/lib-esm/components/bundle-assets/bundle-assets.js +7 -2
  31. package/lib-esm/components/bundle-assets/bundle-assets.js.map +1 -1
  32. package/lib-esm/components/bundle-assets-totals/bundle-assets-totals.js +14 -7
  33. package/lib-esm/components/bundle-assets-totals/bundle-assets-totals.js.map +1 -1
  34. package/lib-esm/components/bundle-modules/bundle-modules.js +6 -1
  35. package/lib-esm/components/bundle-modules/bundle-modules.js.map +1 -1
  36. package/lib-esm/components/bundle-packages/bundle-packages.js +6 -1
  37. package/lib-esm/components/bundle-packages/bundle-packages.js.map +1 -1
  38. package/lib-esm/components/index.js +0 -1
  39. package/lib-esm/components/index.js.map +1 -1
  40. package/lib-esm/components/metrics-table/metrics-table.i18n.js +0 -5
  41. package/lib-esm/components/metrics-table/metrics-table.i18n.js.map +1 -1
  42. package/lib-esm/components/metrics-table/metrics-table.js +4 -37
  43. package/lib-esm/components/metrics-table/metrics-table.js.map +1 -1
  44. package/lib-esm/components/metrics-table/metrics-table.module.css +1 -44
  45. package/lib-esm/components/metrics-table-header/index.js +2 -0
  46. package/lib-esm/components/metrics-table-header/index.js.map +1 -0
  47. package/lib-esm/components/metrics-table-header/metrics-table-header.i18n.js +6 -0
  48. package/lib-esm/components/metrics-table-header/metrics-table-header.i18n.js.map +1 -0
  49. package/lib-esm/components/metrics-table-header/metrics-table-header.js +49 -0
  50. package/lib-esm/components/metrics-table-header/metrics-table-header.js.map +1 -0
  51. package/lib-esm/components/metrics-table-header/metrics-table-header.module.css +44 -0
  52. package/lib-esm/components/metrics-table-title/metrics-table-title.js +1 -1
  53. package/lib-esm/components/metrics-table-title/metrics-table-title.js.map +1 -1
  54. package/lib-esm/components/metrics-table-title/metrics-table-title.module.css +3 -4
  55. package/lib-esm/components/metrics-treemap/metrics-treemap.js +4 -6
  56. package/lib-esm/components/metrics-treemap/metrics-treemap.js.map +1 -1
  57. package/lib-esm/components/metrics-treemap/metrics-treemap.module.css +51 -32
  58. package/lib-esm/components/sort-button/sort-button.module.css +1 -0
  59. package/package.json +5 -5
  60. package/src/components/bundle-assets/bundle-assets.jsx +18 -2
  61. package/src/components/bundle-assets-totals/bundle-assets-totals.jsx +29 -10
  62. package/src/components/bundle-modules/bundle-modules.tsx +13 -1
  63. package/src/components/bundle-packages/bundle-packages.jsx +13 -1
  64. package/src/components/index.js +0 -1
  65. package/src/components/metrics-table/metrics-table.i18n.ts +0 -7
  66. package/src/components/metrics-table/metrics-table.module.css +1 -44
  67. package/src/components/metrics-table/metrics-table.tsx +17 -153
  68. package/src/components/metrics-table-header/index.ts +1 -0
  69. package/src/components/metrics-table-header/metrics-table-header.i18n.ts +6 -0
  70. package/src/components/metrics-table-header/metrics-table-header.module.css +44 -0
  71. package/src/components/metrics-table-header/metrics-table-header.tsx +172 -0
  72. package/src/components/metrics-table-title/metrics-table-title.jsx +1 -1
  73. package/src/components/metrics-table-title/metrics-table-title.module.css +3 -4
  74. package/src/components/metrics-treemap/metrics-treemap.module.css +51 -32
  75. package/src/components/metrics-treemap/metrics-treemap.tsx +16 -19
  76. package/src/components/sort-button/sort-button.module.css +1 -0
  77. package/types/components/index.d.ts +0 -1
  78. package/types/components/metrics-table/metrics-table.d.ts +4 -10
  79. package/types/components/metrics-table/metrics-table.i18n.d.ts +0 -5
  80. package/types/components/metrics-table-header/index.d.ts +1 -0
  81. package/types/components/metrics-table-header/metrics-table-header.d.ts +15 -0
  82. package/types/components/metrics-table-header/metrics-table-header.i18n.d.ts +5 -0
  83. package/lib/components/total-size-type-title/index.js.map +0 -1
  84. package/lib/components/total-size-type-title/total-size-type-title.js +0 -32
  85. package/lib/components/total-size-type-title/total-size-type-title.js.map +0 -1
  86. package/lib/components/total-size-type-title/total-size-type-title.module.css +0 -14
  87. package/lib-esm/components/total-size-type-title/index.js +0 -2
  88. package/lib-esm/components/total-size-type-title/index.js.map +0 -1
  89. package/lib-esm/components/total-size-type-title/total-size-type-title.js +0 -25
  90. package/lib-esm/components/total-size-type-title/total-size-type-title.js.map +0 -1
  91. package/lib-esm/components/total-size-type-title/total-size-type-title.module.css +0 -14
  92. package/src/components/total-size-type-title/index.js +0 -1
  93. package/src/components/total-size-type-title/total-size-type-title.jsx +0 -41
  94. package/src/components/total-size-type-title/total-size-type-title.module.css +0 -14
  95. package/types/components/total-size-type-title/index.d.ts +0 -1
  96. package/types/components/total-size-type-title/total-size-type-title.d.ts +0 -14
@@ -1,4 +1,4 @@
1
- import React, { useCallback } from 'react';
1
+ import React, { useCallback, useMemo } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { useHistory } from 'react-router-dom';
4
4
  import {
@@ -7,13 +7,17 @@ import {
7
7
  } from '@bundle-stats/utils';
8
8
  import * as webpack from '@bundle-stats/utils/lib-esm/webpack';
9
9
 
10
+ import config from '../../config.json';
10
11
  import { ASSETS_SIZES_FILE_TYPE_MAP, SECTION_URLS, MetricsDisplayType } from '../../constants';
11
12
  import { useMetricsDisplayType } from '../../hooks/metrics-display-type';
13
+ import I18N from '../../i18n';
12
14
  import { Toolbar } from '../../ui/toolbar';
13
- import { MetricsTreemap } from '../metrics-treemap';
15
+ import { Table } from '../../ui/table';
14
16
  import { MetricsDisplaySelector } from '../metrics-display-selector';
15
- import { TotalSizeTypeTitle } from '../total-size-type-title';
16
17
  import { MetricsTable } from '../metrics-table';
18
+ import { MetricsTableTitle } from '../metrics-table-title';
19
+ import { MetricsTableHeader } from '../metrics-table-header';
20
+ import { MetricsTreemap } from '../metrics-treemap';
17
21
  import { ComponentLink } from '../component-link';
18
22
  import css from './bundle-assets-totals.module.css';
19
23
 
@@ -52,6 +56,17 @@ export const BundleAssetsTotals = ({
52
56
  [history],
53
57
  );
54
58
 
59
+ const metricsTableTitle = useMemo(
60
+ () => (
61
+ <MetricsTableTitle
62
+ title={I18N.ASSET_TOTALS}
63
+ popoverInfo={I18N.ASSET_TOTALS_INFO}
64
+ popoverHref={config.documentation.assets}
65
+ />
66
+ ),
67
+ [],
68
+ );
69
+
55
70
  return (
56
71
  <div className={className}>
57
72
  <Toolbar
@@ -59,11 +74,10 @@ export const BundleAssetsTotals = ({
59
74
  renderActions={() => (
60
75
  <MetricsDisplaySelector onSelect={setDisplayType} value={displayType} />
61
76
  )}
62
- >
63
- <TotalSizeTypeTitle />
64
- </Toolbar>
77
+ />
65
78
  {displayType === MetricsDisplayType.TABLE && (
66
79
  <MetricsTable
80
+ title={metricsTableTitle}
67
81
  runs={jobs}
68
82
  items={items}
69
83
  renderRowHeader={renderRowHeader}
@@ -72,10 +86,15 @@ export const BundleAssetsTotals = ({
72
86
  />
73
87
  )}
74
88
  {displayType === MetricsDisplayType.TREEMAP && (
75
- <MetricsTreemap
76
- items={items}
77
- onItemClick={onTreemapItemClick || handleMetricsTreemapItemClick}
78
- />
89
+ <>
90
+ <Table compact>
91
+ <MetricsTableHeader metricTitle={metricsTableTitle} showSum jobs={jobs} rows={items} />
92
+ </Table>
93
+ <MetricsTreemap
94
+ items={items}
95
+ onItemClick={onTreemapItemClick || handleMetricsTreemapItemClick}
96
+ />
97
+ </>
79
98
  )}
80
99
  </div>
81
100
  );
@@ -14,10 +14,12 @@ import { FileName } from '../../ui/file-name';
14
14
  import { Filters } from '../../ui/filters';
15
15
  import { InputSearch } from '../../ui/input-search';
16
16
  import { Tag } from '../../ui/tag';
17
+ import { Table } from '../../ui/table';
17
18
  import { Toolbar } from '../../ui/toolbar';
18
19
  import { Tooltip } from '../../ui/tooltip';
19
20
  import { ComponentLink } from '../component-link';
20
21
  import { MetricsTable } from '../metrics-table';
22
+ import { MetricsTableHeader } from '../metrics-table-header';
21
23
  import { MetricsTreemap } from '../metrics-treemap';
22
24
  import { MetricsTableOptions } from '../metrics-table-options';
23
25
  import { MetricsDisplaySelector } from '../metrics-display-selector';
@@ -230,7 +232,17 @@ export const BundleModules = (props: BundleModulesProps) => {
230
232
  />
231
233
  )}
232
234
  {displayType === MetricsDisplayType.TREEMAP && (
233
- <MetricsTreemap emptyMessage={emptyMessage} items={items} onItemClick={showEntryInfo} />
235
+ <>
236
+ <Table compact>
237
+ <MetricsTableHeader
238
+ metricTitle={metricsTableTitle}
239
+ showSum
240
+ jobs={jobs}
241
+ rows={items}
242
+ />
243
+ </Table>
244
+ <MetricsTreemap emptyMessage={emptyMessage} items={items} onItemClick={showEntryInfo} />
245
+ </>
234
246
  )}
235
247
  </div>
236
248
  {entryItem && (
@@ -10,9 +10,11 @@ import { InputSearch } from '../../ui/input-search';
10
10
  import { EmptySet } from '../../ui/empty-set';
11
11
  import { Filters } from '../../ui/filters';
12
12
  import { Tag } from '../../ui/tag';
13
+ import { Table } from '../../ui/table';
13
14
  import { Toolbar } from '../../ui/toolbar';
14
15
  import { ComponentLink } from '../component-link';
15
16
  import { MetricsTable } from '../metrics-table';
17
+ import { MetricsTableHeader } from '../metrics-table-header';
16
18
  import { MetricsTableOptions } from '../metrics-table-options';
17
19
  import { MetricsTableTitle } from '../metrics-table-title';
18
20
  import { PackageInfo } from '../package-info';
@@ -231,7 +233,17 @@ export const BundlePackages = (props) => {
231
233
  />
232
234
  )}
233
235
  {displayType === MetricsDisplayType.TREEMAP && (
234
- <MetricsTreemap emptyMessage={emptyMessage} items={items} onItemClick={showEntryInfo} />
236
+ <>
237
+ <Table compact>
238
+ <MetricsTableHeader
239
+ metricTitle={metricsTableTitle}
240
+ showSum
241
+ jobs={jobs}
242
+ rows={items}
243
+ />
244
+ </Table>
245
+ <MetricsTreemap emptyMessage={emptyMessage} items={items} onItemClick={showEntryInfo} />
246
+ </>
235
247
  )}
236
248
  </main>
237
249
  </section>
@@ -18,4 +18,3 @@ export { MetricsTableTitle } from './metrics-table-title';
18
18
  export { ModuleInfo } from './module-info';
19
19
  export { RunInfo, RunInfoProps } from './run-info';
20
20
  export { Summary } from './summary';
21
- export { TotalSizeTypeTitle } from './total-size-type-title';
@@ -1,10 +1,3 @@
1
- export const BASELINE_TITLE = 'Baseline';
2
- export const CURRENT_TITLE = 'Current';
3
-
4
- export const SORT_VALUE = 'value';
5
- export const SORT_CHANGE = 'change';
6
- export const SORT_PERCENTUAL_CHANGE = 'percentual change';
7
-
8
1
  export const EMPTY_MESSAGE = 'No items found.';
9
2
  export const SHOW_ALL = 'Show all';
10
3
  export const SHOW_LESS = 'Show less';
@@ -10,52 +10,9 @@
10
10
  text-decoration: underline;
11
11
  }
12
12
 
13
- /* columns */
14
- .metricName {
15
- width: 55%;
16
- }
17
-
18
- .root .jobCol {
19
- text-align: right;
20
- }
21
-
22
- .root .value {
23
- text-align: right;
24
- vertical-align: baseline;
25
- width: 15%;
26
- }
27
-
13
+ .root .value,
28
14
  .root .delta {
29
15
  text-align: right;
30
- font-size: var(--size-xsmall);
31
- vertical-align: baseline;
32
- width: 10%;
33
- color: pink;
34
- }
35
-
36
- .root .deltaPercentage {
37
- width: 5%;
38
- }
39
-
40
- .root tbody .delta {
41
- line-height: 1.6rem; /* baseline alignment with the column value */
42
- }
43
-
44
- .jobName {
45
- white-space: nowrap;
46
- }
47
-
48
- /* columns overrides for showHeaderSum */
49
- .showHeaderSum .headerRow .jobCol {
50
- border-bottom: 0;
51
- padding-bottom: 0;
52
- }
53
-
54
- .showHeaderSum .headerRow .sum {
55
- padding-top: var(--space-xxxsmall);
56
- font-size: var(--size-small);
57
- vertical-align: middle;
58
- text-transform: none;
59
16
  }
60
17
 
61
18
  /** rows */
@@ -1,13 +1,7 @@
1
1
  import React from 'react';
2
2
  import cx from 'classnames';
3
3
  import isEmpty from 'lodash/isEmpty';
4
- import sum from 'lodash/sum';
5
- import {
6
- METRIC_TYPE_CONFIGS,
7
- type MetricRunInfo,
8
- MetricTypes,
9
- getMetricRunInfo,
10
- } from '@bundle-stats/utils';
4
+ import type { ReportMetricRow } from '@bundle-stats/utils';
11
5
 
12
6
  import { Button } from '../../ui/button';
13
7
  import { Icon } from '../../ui/icon';
@@ -15,128 +9,17 @@ import { Table } from '../../ui/table';
15
9
  import { Stack } from '../../layout/stack';
16
10
  import { Metric } from '../metric';
17
11
  import { Delta } from '../delta';
18
- import { JobName } from '../job-name';
19
- import { SortButton } from '../sort-button';
12
+ import { MetricsTableHeader } from '../metrics-table-header';
20
13
  import * as I18N from './metrics-table.i18n';
21
14
  import css from './metrics-table.module.css';
22
15
 
23
- const METRIC_TYPE = METRIC_TYPE_CONFIGS[MetricTypes.FileSize];
24
16
  const VISIBLE_COUNT = 500;
25
17
  const BASELINE_COLUMN_SPAN = 1;
26
18
  const CURRENT_COLUMN_SPAN = 3;
27
19
 
28
- interface Run {
29
- /**
30
- * Run job label
31
- */
32
- label?: React.ReactNode;
33
- /**
34
- * Job internal build number
35
- */
36
- internalBuildNumber: number;
37
- }
38
-
39
- interface Item {
40
- key: string;
41
- label?: string;
42
- changed: boolean;
43
- biggerIsBetter: boolean;
44
- runs: Array<MetricRunInfo | null>;
45
- }
46
-
47
- const getRowsRunTotal = (rows: Array<Item>, runIndex: number): number =>
48
- sum(rows.map((row) => row?.runs?.[runIndex]?.value || 0));
49
-
50
- interface JobColumnProps {
51
- run: Run;
52
- isBaseline: boolean;
53
- }
54
-
55
- const JobColumn = ({ run, isBaseline }: JobColumnProps) => {
56
- const colSpan = isBaseline ? BASELINE_COLUMN_SPAN : CURRENT_COLUMN_SPAN;
57
- const { label, internalBuildNumber } = run;
58
-
59
- if (!run) {
60
- return <Table.Th colSpan={colSpan}>-</Table.Th>;
61
- }
62
-
63
- return (
64
- <Table.Th className={css.jobCol} colSpan={colSpan}>
65
- <JobName
66
- title={isBaseline ? I18N.BASELINE_TITLE : I18N.CURRENT_TITLE}
67
- internalBuildNumber={internalBuildNumber}
68
- className={css.jobName}
69
- >
70
- {label}
71
- </JobName>
72
- </Table.Th>
73
- );
74
- };
75
-
76
- interface ColumnSumProps {
77
- rows: Array<Item>;
78
- isBaseline: boolean;
79
- runIndex: number;
80
- sort?: any;
81
- updateSort?: (val: any) => void;
82
- }
83
-
84
- const SumColumn = ({ rows, isBaseline, runIndex, updateSort, sort }: ColumnSumProps) => {
85
- const currentRunTotal = getRowsRunTotal(rows, runIndex);
86
- const baselineRunTotal = !isBaseline ? getRowsRunTotal(rows, runIndex + 1) : 0;
87
- const total = getMetricRunInfo(METRIC_TYPE, currentRunTotal, baselineRunTotal);
88
- const fieldPath = `runs[${runIndex}]`;
89
-
90
- return (
91
- <>
92
- <Table.Th className={cx(css.value, css.sum)}>
93
- <SortButton
94
- fieldPath={fieldPath}
95
- fieldName="value"
96
- label={I18N.SORT_VALUE}
97
- updateSort={updateSort}
98
- sort={sort}
99
- >
100
- <Metric value={total.displayValue} />
101
- </SortButton>
102
- </Table.Th>
103
- {!isBaseline && (
104
- <>
105
- <Table.Th className={cx(css.delta, css.sum)}>
106
- <SortButton
107
- fieldPath={fieldPath}
108
- fieldName="delta"
109
- label={I18N.SORT_CHANGE}
110
- updateSort={updateSort}
111
- sort={sort}
112
- >
113
- {'delta' in total && (
114
- <Delta displayValue={total.displayDelta} deltaType={total.deltaType} />
115
- )}
116
- </SortButton>
117
- </Table.Th>
118
- <Table.Th className={cx(css.delta, css.deltaPercentage, css.sum)}>
119
- <SortButton
120
- fieldPath={fieldPath}
121
- fieldName="deltaPercentage"
122
- label={I18N.SORT_PERCENTUAL_CHANGE}
123
- updateSort={updateSort}
124
- sort={sort}
125
- >
126
- {'delta' in total && (
127
- <Delta displayValue={total.displayDeltaPercentage} deltaType={total.deltaType} />
128
- )}
129
- </SortButton>
130
- </Table.Th>
131
- </>
132
- )}
133
- </>
134
- );
135
- };
136
-
137
20
  interface RowProps extends React.ComponentProps<typeof Table.Tr> {
138
- item: Item;
139
- renderHeader: (item: Item) => React.ReactNode;
21
+ item: ReportMetricRow;
22
+ renderHeader: (item: ReportMetricRow) => React.ReactNode;
140
23
  }
141
24
 
142
25
  const Row = ({ className = '', item, renderHeader, ...restProps }: RowProps) => (
@@ -185,14 +68,15 @@ const Row = ({ className = '', item, renderHeader, ...restProps }: RowProps) =>
185
68
 
186
69
  interface MetricsTableProps extends Omit<React.ComponentProps<typeof Table>, 'title'> {
187
70
  runs: Array<{
71
+ label: string;
188
72
  internalBuildNumber: number;
189
73
  }>;
190
- items: Array<Item>;
74
+ items: Array<ReportMetricRow>;
191
75
  title?: React.ReactNode;
192
76
  showHeaderSum?: boolean;
193
77
  sort?: any;
194
78
  updateSort?: (val: any) => void;
195
- renderRowHeader?: (item: Item) => React.ReactNode;
79
+ renderRowHeader?: (item: ReportMetricRow) => React.ReactNode;
196
80
  emptyMessage?: React.ReactNode;
197
81
  showAllItems: boolean;
198
82
  setShowAllItems: (value: boolean) => void;
@@ -203,7 +87,7 @@ export const MetricsTable = ({
203
87
  runs,
204
88
  items,
205
89
  showHeaderSum = false,
206
- title = '',
90
+ title,
207
91
  sort,
208
92
  updateSort,
209
93
  renderRowHeader = (item: any) => item.label,
@@ -214,12 +98,7 @@ export const MetricsTable = ({
214
98
  }: MetricsTableProps) => {
215
99
  const columnCount = (runs.length - 1) * CURRENT_COLUMN_SPAN + BASELINE_COLUMN_SPAN + 1;
216
100
 
217
- const rootClassName = cx(
218
- css.root,
219
- className,
220
- runs.length > 1 && css.multipleRuns,
221
- showHeaderSum && css.showHeaderSum,
222
- );
101
+ const rootClassName = cx(css.root, className, runs.length > 1 && css.multipleRuns);
223
102
 
224
103
  const showEmpty = isEmpty(items);
225
104
  const showItems = !showEmpty;
@@ -228,29 +107,14 @@ export const MetricsTable = ({
228
107
 
229
108
  return (
230
109
  <Table className={rootClassName} compact {...restProps}>
231
- <Table.THead>
232
- <Table.Tr className={css.headerRow}>
233
- <Table.Th className={css.metricName} rowSpan={showHeaderSum ? 2 : 1}>
234
- {title || ' '}
235
- </Table.Th>
236
- {runs.map((run, runIndex) => (
237
- <JobColumn run={run} isBaseline={runIndex === runs.length - 1} />
238
- ))}
239
- </Table.Tr>
240
- {showHeaderSum && (
241
- <Table.Tr className={css.headerRow}>
242
- {runs.map((_, runIndex) => (
243
- <SumColumn
244
- rows={items}
245
- isBaseline={runIndex === runs.length - 1}
246
- runIndex={runIndex}
247
- updateSort={updateSort}
248
- sort={sort}
249
- />
250
- ))}
251
- </Table.Tr>
252
- )}
253
- </Table.THead>
110
+ <MetricsTableHeader
111
+ metricTitle={title}
112
+ showSum={showHeaderSum}
113
+ jobs={runs}
114
+ rows={items}
115
+ sort={sort}
116
+ updateSort={updateSort}
117
+ />
254
118
  <Table.TBody>
255
119
  {showEmpty && (
256
120
  <Table.Tr>
@@ -0,0 +1 @@
1
+ export * from './metrics-table-header';
@@ -0,0 +1,6 @@
1
+ export const BASELINE_TITLE = 'Baseline';
2
+ export const CURRENT_TITLE = 'Current';
3
+
4
+ export const SORT_VALUE = 'value';
5
+ export const SORT_CHANGE = 'change';
6
+ export const SORT_PERCENTUAL_CHANGE = 'percentual change';
@@ -0,0 +1,44 @@
1
+ /* columns */
2
+ .root .metricName {
3
+ width: 55%;
4
+ }
5
+
6
+ .root .job {
7
+ text-align: right;
8
+ }
9
+
10
+ .root .value {
11
+ text-align: right;
12
+ vertical-align: baseline;
13
+ width: 15%;
14
+ }
15
+
16
+ .root .delta {
17
+ text-align: right;
18
+ font-size: var(--size-xsmall);
19
+ vertical-align: baseline;
20
+ width: 10%;
21
+ color: pink;
22
+ }
23
+
24
+ .root .deltaPercentage {
25
+ width: 5%;
26
+ }
27
+
28
+ .jobName {
29
+ white-space: nowrap;
30
+ }
31
+
32
+ /* columns overrides for showHeaderSum */
33
+ .showHeaderSum .headerRow .job {
34
+ border-bottom: 0;
35
+ padding-bottom: 0;
36
+ }
37
+
38
+ .showHeaderSum .headerRow .sum {
39
+ padding-top: var(--space-xxxsmall);
40
+ font-size: var(--size-small);
41
+ vertical-align: middle;
42
+ text-transform: none;
43
+ }
44
+
@@ -0,0 +1,172 @@
1
+ import React from 'react';
2
+ import cx from 'classnames';
3
+ import sum from 'lodash/sum';
4
+ import {
5
+ METRIC_TYPE_CONFIGS,
6
+ MetricTypes,
7
+ type ReportMetricRow,
8
+ getMetricRunInfo,
9
+ } from '@bundle-stats/utils';
10
+
11
+ import { Table } from '../../ui/table';
12
+ import { Delta } from '../delta';
13
+ import { JobName } from '../job-name';
14
+ import { Metric } from '../metric';
15
+ import { SortButton } from '../sort-button';
16
+ import * as I18N from './metrics-table-header.i18n';
17
+ import css from './metrics-table-header.module.css';
18
+
19
+ const METRIC_TYPE = METRIC_TYPE_CONFIGS[MetricTypes.FileSize];
20
+ const BASELINE_COLUMN_SPAN = 1;
21
+ const CURRENT_COLUMN_SPAN = 3;
22
+
23
+ interface JobColumnProps {
24
+ job: { label: string; internalBuildNumber: number };
25
+ isBaseline: boolean;
26
+ }
27
+
28
+ const JobColumn = ({ job, isBaseline }: JobColumnProps) => {
29
+ const colSpan = isBaseline ? BASELINE_COLUMN_SPAN : CURRENT_COLUMN_SPAN;
30
+ const { label, internalBuildNumber } = job;
31
+
32
+ if (!job) {
33
+ return <Table.Th colSpan={colSpan}>-</Table.Th>;
34
+ }
35
+
36
+ return (
37
+ <Table.Th className={css.job} colSpan={colSpan}>
38
+ <JobName
39
+ title={isBaseline ? I18N.BASELINE_TITLE : I18N.CURRENT_TITLE}
40
+ internalBuildNumber={internalBuildNumber}
41
+ className={css.jobName}
42
+ >
43
+ {label}
44
+ </JobName>
45
+ </Table.Th>
46
+ );
47
+ };
48
+
49
+ const getRowsRunTotal = (rows: Array<ReportMetricRow>, runIndex: number): number =>
50
+ sum(rows.map((row) => row?.runs?.[runIndex]?.value || 0));
51
+
52
+ interface ColumnSumProps {
53
+ rows: Array<ReportMetricRow>;
54
+ isBaseline: boolean;
55
+ runIndex: number;
56
+ sort?: any;
57
+ updateSort?: (val: any) => void;
58
+ }
59
+
60
+ const SumColumn = ({ rows, isBaseline, runIndex, updateSort, sort }: ColumnSumProps) => {
61
+ const currentRunTotal = getRowsRunTotal(rows, runIndex);
62
+ const baselineRunTotal = !isBaseline ? getRowsRunTotal(rows, runIndex + 1) : 0;
63
+ const total = getMetricRunInfo(METRIC_TYPE, currentRunTotal, baselineRunTotal);
64
+ const fieldPath = `runs[${runIndex}]`;
65
+
66
+ return (
67
+ <>
68
+ <Table.Th className={cx(css.value, css.sum)}>
69
+ <SortButton
70
+ fieldPath={fieldPath}
71
+ fieldName="value"
72
+ label={I18N.SORT_VALUE}
73
+ updateSort={updateSort}
74
+ sort={sort}
75
+ >
76
+ <Metric value={total.displayValue} />
77
+ </SortButton>
78
+ </Table.Th>
79
+ {!isBaseline && (
80
+ <>
81
+ <Table.Th className={cx(css.delta, css.sum)}>
82
+ <SortButton
83
+ fieldPath={fieldPath}
84
+ fieldName="delta"
85
+ label={I18N.SORT_CHANGE}
86
+ updateSort={updateSort}
87
+ sort={sort}
88
+ >
89
+ {'delta' in total && (
90
+ <Delta displayValue={total.displayDelta} deltaType={total.deltaType} />
91
+ )}
92
+ </SortButton>
93
+ </Table.Th>
94
+ <Table.Th className={cx(css.delta, css.deltaPercentage, css.sum)}>
95
+ <SortButton
96
+ fieldPath={fieldPath}
97
+ fieldName="deltaPercentage"
98
+ label={I18N.SORT_PERCENTUAL_CHANGE}
99
+ updateSort={updateSort}
100
+ sort={sort}
101
+ >
102
+ {'delta' in total && (
103
+ <Delta displayValue={total.displayDeltaPercentage} deltaType={total.deltaType} />
104
+ )}
105
+ </SortButton>
106
+ </Table.Th>
107
+ </>
108
+ )}
109
+ </>
110
+ );
111
+ };
112
+
113
+ interface MetricsTableHeaderProps {
114
+ /**
115
+ * Metric column title
116
+ */
117
+ metricTitle?: React.ReactNode;
118
+ /**
119
+ * Show column sum flag
120
+ */
121
+ showSum?: boolean;
122
+ /**
123
+ * Array of jobs
124
+ */
125
+ jobs: Array<{ label: string; internalBuildNumber: number }>;
126
+ /**
127
+ * Report rows
128
+ */
129
+ rows: Array<ReportMetricRow>;
130
+ /**
131
+ * Sort field data
132
+ */
133
+ sort?: any;
134
+ /**
135
+ * Update sort field
136
+ */
137
+ updateSort?: (val: any) => void;
138
+ }
139
+
140
+ export const MetricsTableHeader = (
141
+ props: MetricsTableHeaderProps & React.ComponentProps<'thead'>,
142
+ ) => {
143
+ const { className = '', metricTitle = '', showSum = false, jobs, rows, sort, updateSort } = props;
144
+
145
+ const rootClassName = cx(css.root, showSum && css.showHeaderSum, className);
146
+
147
+ return (
148
+ <Table.THead className={rootClassName}>
149
+ <Table.Tr className={css.headerRow}>
150
+ <Table.Th className={css.metricName} rowSpan={showSum ? 2 : 1}>
151
+ {metricTitle || ' '}
152
+ </Table.Th>
153
+ {jobs.map((job, runIndex) => (
154
+ <JobColumn job={job} isBaseline={runIndex === jobs.length - 1} />
155
+ ))}
156
+ </Table.Tr>
157
+ {showSum && (
158
+ <Table.Tr className={css.headerRow}>
159
+ {jobs.map((_, runIndex) => (
160
+ <SumColumn
161
+ rows={rows}
162
+ isBaseline={runIndex === jobs.length - 1}
163
+ runIndex={runIndex}
164
+ updateSort={updateSort}
165
+ sort={sort}
166
+ />
167
+ ))}
168
+ </Table.Tr>
169
+ )}
170
+ </Table.THead>
171
+ );
172
+ };
@@ -15,7 +15,7 @@ export const MetricsTableTitle = (props) => {
15
15
  const rootClassName = cx(css.root, className);
16
16
 
17
17
  return (
18
- <Stack space="xxsmall" className={rootClassName}>
18
+ <Stack space="xxxsmall" className={rootClassName}>
19
19
  <FlexStack space="xxxsmall" alignItems="center" as="h3" className={css.title}>
20
20
  <span>{title}</span>
21
21
 
@@ -1,8 +1,9 @@
1
1
  .root {
2
+ color: var(--color-text-ultra-light);
2
3
  }
3
4
 
4
5
  .title {
5
- color: var(--color-text-light);
6
+ color: inherit;
6
7
  font-size: 1em;
7
8
  font-weight: bold;
8
9
  text-transform: uppercase;
@@ -18,8 +19,6 @@
18
19
  }
19
20
 
20
21
  .info {
21
- color: var(--color-text-light);
22
22
  font-family: var(--font-family-fixed);
23
- font-size: var(--size-xsmall);
24
- font-weight: bold;
23
+ font-weight: normal;
25
24
  }