@bundle-stats/ui 4.4.2 → 4.5.0-beta.0

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 (152) hide show
  1. package/lib/assets/icons.svg +16 -10
  2. package/lib/assets/icons.svg.js +14 -10
  3. package/lib/assets/icons.svg.js.map +1 -1
  4. package/lib/components/bundle-assets/bundle-assets.js +2 -10
  5. package/lib/components/bundle-assets/bundle-assets.js.map +1 -1
  6. package/lib/components/bundle-assets/bundle-assets.utils.js +8 -20
  7. package/lib/components/bundle-assets/bundle-assets.utils.js.map +1 -1
  8. package/lib/components/bundle-assets/index.js +0 -2
  9. package/lib/components/bundle-assets/index.js.map +1 -1
  10. package/lib/components/bundle-modules/bundle-modules.js +2 -10
  11. package/lib/components/bundle-modules/bundle-modules.js.map +1 -1
  12. package/lib/components/bundle-modules/bundle-modules.utils.js +1 -13
  13. package/lib/components/bundle-modules/bundle-modules.utils.js.map +1 -1
  14. package/lib/components/bundle-modules/index.js +2 -4
  15. package/lib/components/bundle-modules/index.js.map +1 -1
  16. package/lib/components/bundle-packages/bundle-packages.js +2 -10
  17. package/lib/components/bundle-packages/bundle-packages.js.map +1 -1
  18. package/lib/components/bundle-packages/bundle-packages.utils.js +1 -13
  19. package/lib/components/bundle-packages/bundle-packages.utils.js.map +1 -1
  20. package/lib/components/bundle-packages/index.js +2 -4
  21. package/lib/components/bundle-packages/index.js.map +1 -1
  22. package/lib/components/metrics-table/metrics-table.js +41 -104
  23. package/lib/components/metrics-table/metrics-table.js.map +1 -1
  24. package/lib/components/metrics-table/metrics-table.module.css +10 -15
  25. package/lib/components/metrics-table-title/metrics-table-title.js +1 -1
  26. package/lib/components/metrics-table-title/metrics-table-title.js.map +1 -1
  27. package/lib/components/metrics-table-title/metrics-table-title.module.css +3 -1
  28. package/lib/{ui/sort-dropdown → components/sort-button}/index.js +1 -1
  29. package/lib/components/sort-button/index.js.map +1 -0
  30. package/lib/components/sort-button/sort-button.js +73 -0
  31. package/lib/components/sort-button/sort-button.js.map +1 -0
  32. package/lib/components/sort-button/sort-button.module.css +89 -0
  33. package/lib/constants.js +5 -1
  34. package/lib/constants.js.map +1 -1
  35. package/lib/hooks/rows-sort.js +13 -5
  36. package/lib/hooks/rows-sort.js.map +1 -1
  37. package/lib/ui/icon/icon.js +2 -0
  38. package/lib/ui/icon/icon.js.map +1 -1
  39. package/lib/ui/index.js +1 -3
  40. package/lib/ui/index.js.map +1 -1
  41. package/lib-esm/assets/icons.svg +16 -10
  42. package/lib-esm/assets/icons.svg.js +14 -10
  43. package/lib-esm/assets/icons.svg.js.map +1 -1
  44. package/lib-esm/components/bundle-assets/bundle-assets.js +2 -10
  45. package/lib-esm/components/bundle-assets/bundle-assets.js.map +1 -1
  46. package/lib-esm/components/bundle-assets/bundle-assets.utils.js +8 -21
  47. package/lib-esm/components/bundle-assets/bundle-assets.utils.js.map +1 -1
  48. package/lib-esm/components/bundle-assets/index.js +0 -2
  49. package/lib-esm/components/bundle-assets/index.js.map +1 -1
  50. package/lib-esm/components/bundle-modules/bundle-modules.js +2 -10
  51. package/lib-esm/components/bundle-modules/bundle-modules.js.map +1 -1
  52. package/lib-esm/components/bundle-modules/bundle-modules.utils.js +1 -14
  53. package/lib-esm/components/bundle-modules/bundle-modules.utils.js.map +1 -1
  54. package/lib-esm/components/bundle-modules/index.js +2 -4
  55. package/lib-esm/components/bundle-modules/index.js.map +1 -1
  56. package/lib-esm/components/bundle-packages/bundle-packages.js +2 -10
  57. package/lib-esm/components/bundle-packages/bundle-packages.js.map +1 -1
  58. package/lib-esm/components/bundle-packages/bundle-packages.utils.js +1 -14
  59. package/lib-esm/components/bundle-packages/bundle-packages.utils.js.map +1 -1
  60. package/lib-esm/components/bundle-packages/index.js +2 -4
  61. package/lib-esm/components/bundle-packages/index.js.map +1 -1
  62. package/lib-esm/components/metrics-table/metrics-table.js +41 -81
  63. package/lib-esm/components/metrics-table/metrics-table.js.map +1 -1
  64. package/lib-esm/components/metrics-table/metrics-table.module.css +10 -15
  65. package/lib-esm/components/metrics-table-title/metrics-table-title.js +1 -1
  66. package/lib-esm/components/metrics-table-title/metrics-table-title.js.map +1 -1
  67. package/lib-esm/components/metrics-table-title/metrics-table-title.module.css +3 -1
  68. package/lib-esm/components/sort-button/index.js +2 -0
  69. package/lib-esm/components/sort-button/index.js.map +1 -0
  70. package/lib-esm/components/sort-button/sort-button.js +43 -0
  71. package/lib-esm/components/sort-button/sort-button.js.map +1 -0
  72. package/lib-esm/components/sort-button/sort-button.module.css +89 -0
  73. package/lib-esm/constants.js +4 -0
  74. package/lib-esm/constants.js.map +1 -1
  75. package/lib-esm/hooks/rows-sort.js +11 -4
  76. package/lib-esm/hooks/rows-sort.js.map +1 -1
  77. package/lib-esm/ui/icon/icon.js +2 -0
  78. package/lib-esm/ui/icon/icon.js.map +1 -1
  79. package/lib-esm/ui/index.js +0 -1
  80. package/lib-esm/ui/index.js.map +1 -1
  81. package/package.json +3 -3
  82. package/src/assets/icons/chevron-down.svg +1 -0
  83. package/src/assets/icons/chevron-up.svg +1 -0
  84. package/src/assets/icons.svg +16 -10
  85. package/src/assets/icons.svg.jsx +24 -10
  86. package/src/components/bundle-assets/bundle-assets.jsx +2 -9
  87. package/src/components/bundle-assets/bundle-assets.utils.js +8 -24
  88. package/src/components/bundle-assets/index.jsx +0 -2
  89. package/src/components/bundle-modules/bundle-modules.jsx +2 -14
  90. package/src/components/bundle-modules/bundle-modules.utils.js +1 -17
  91. package/src/components/bundle-modules/index.jsx +2 -4
  92. package/src/components/bundle-packages/bundle-packages.jsx +2 -9
  93. package/src/components/bundle-packages/bundle-packages.utils.js +1 -17
  94. package/src/components/bundle-packages/index.jsx +2 -4
  95. package/src/components/metrics-table/metrics-table.jsx +134 -131
  96. package/src/components/metrics-table/metrics-table.module.css +10 -15
  97. package/src/components/metrics-table-title/metrics-table-title.jsx +1 -1
  98. package/src/components/metrics-table-title/metrics-table-title.module.css +3 -1
  99. package/src/components/sort-button/index.ts +1 -0
  100. package/src/components/sort-button/sort-button.module.css +89 -0
  101. package/src/components/sort-button/sort-button.tsx +108 -0
  102. package/src/constants.js +5 -0
  103. package/src/hooks/rows-sort.ts +48 -0
  104. package/src/types.d.ts +4 -0
  105. package/src/ui/icon/icon.tsx +2 -0
  106. package/src/ui/index.js +0 -1
  107. package/types/components/bundle-assets/bundle-assets.d.ts +0 -1
  108. package/types/components/bundle-assets/bundle-assets.utils.d.ts +1 -1
  109. package/types/components/bundle-modules/bundle-modules.d.ts +1 -3
  110. package/types/components/bundle-modules/bundle-modules.utils.d.ts +1 -1
  111. package/types/components/bundle-packages/bundle-packages.d.ts +0 -1
  112. package/types/components/bundle-packages/bundle-packages.utils.d.ts +1 -1
  113. package/types/components/metrics-table/metrics-table.d.ts +3 -7
  114. package/types/components/sort-button/index.d.ts +1 -0
  115. package/types/components/sort-button/sort-button.d.ts +11 -0
  116. package/types/constants.d.ts +4 -0
  117. package/types/hooks/rows-sort.d.ts +14 -11
  118. package/types/ui/icon/icon.d.ts +4 -0
  119. package/types/ui/index.d.ts +0 -1
  120. package/lib/components/bundle-assets/bundle-assets.constants.js +0 -21
  121. package/lib/components/bundle-assets/bundle-assets.constants.js.map +0 -1
  122. package/lib/components/bundle-modules/bundle-modules.constants.js +0 -21
  123. package/lib/components/bundle-modules/bundle-modules.constants.js.map +0 -1
  124. package/lib/components/bundle-packages/bundle-packages.constants.js +0 -21
  125. package/lib/components/bundle-packages/bundle-packages.constants.js.map +0 -1
  126. package/lib/ui/sort-dropdown/index.js.map +0 -1
  127. package/lib/ui/sort-dropdown/sort-dropdown.js +0 -101
  128. package/lib/ui/sort-dropdown/sort-dropdown.js.map +0 -1
  129. package/lib/ui/sort-dropdown/sort-dropdown.module.css +0 -22
  130. package/lib-esm/components/bundle-assets/bundle-assets.constants.js +0 -18
  131. package/lib-esm/components/bundle-assets/bundle-assets.constants.js.map +0 -1
  132. package/lib-esm/components/bundle-modules/bundle-modules.constants.js +0 -18
  133. package/lib-esm/components/bundle-modules/bundle-modules.constants.js.map +0 -1
  134. package/lib-esm/components/bundle-packages/bundle-packages.constants.js +0 -18
  135. package/lib-esm/components/bundle-packages/bundle-packages.constants.js.map +0 -1
  136. package/lib-esm/ui/sort-dropdown/index.js +0 -2
  137. package/lib-esm/ui/sort-dropdown/index.js.map +0 -1
  138. package/lib-esm/ui/sort-dropdown/sort-dropdown.js +0 -71
  139. package/lib-esm/ui/sort-dropdown/sort-dropdown.js.map +0 -1
  140. package/lib-esm/ui/sort-dropdown/sort-dropdown.module.css +0 -22
  141. package/src/components/bundle-assets/bundle-assets.constants.js +0 -18
  142. package/src/components/bundle-modules/bundle-modules.constants.js +0 -18
  143. package/src/components/bundle-packages/bundle-packages.constants.js +0 -18
  144. package/src/hooks/rows-sort.js +0 -24
  145. package/src/ui/sort-dropdown/index.js +0 -1
  146. package/src/ui/sort-dropdown/sort-dropdown.jsx +0 -121
  147. package/src/ui/sort-dropdown/sort-dropdown.module.css +0 -22
  148. package/types/components/bundle-assets/bundle-assets.constants.d.ts +0 -21
  149. package/types/components/bundle-modules/bundle-modules.constants.d.ts +0 -21
  150. package/types/components/bundle-packages/bundle-packages.constants.d.ts +0 -21
  151. package/types/ui/sort-dropdown/index.d.ts +0 -1
  152. package/types/ui/sort-dropdown/sort-dropdown.d.ts +0 -24
@@ -19,7 +19,6 @@ import {
19
19
  getCustomSort,
20
20
  useModuleFilterByChunk,
21
21
  } from './bundle-modules.utils';
22
- import { SORT_BY } from './bundle-modules.constants';
23
22
 
24
23
  export const BundleModules = (props) => {
25
24
  const { jobs, filters, search, setState, sortBy, direction, ...restProps } = props;
@@ -88,9 +87,8 @@ export const BundleModules = (props) => {
88
87
 
89
88
  const sortParams = useRowsSort({
90
89
  rows: filteredRows,
91
- sortFields: SORT_BY,
92
- sortBy,
93
- sortDirection: direction,
90
+ fieldPath: sortBy,
91
+ direction,
94
92
  getCustomSort,
95
93
  });
96
94
 
@@ -8,7 +8,6 @@ import I18N from '../../i18n';
8
8
  import { FlexStack } from '../../layout/flex-stack';
9
9
  import { EmptySet } from '../../ui/empty-set';
10
10
  import { Filters } from '../../ui/filters';
11
- import { SortDropdown } from '../../ui/sort-dropdown';
12
11
  import { Tag } from '../../ui/tag';
13
12
  import { Toolbar } from '../../ui/toolbar';
14
13
  import { ComponentLink } from '../component-link';
@@ -117,7 +116,6 @@ export const BundlePackages = (props) => {
117
116
  resetAllFilters,
118
117
  totalRowCount,
119
118
  filters,
120
- sortFields,
121
119
  sort,
122
120
  updateSort,
123
121
  search,
@@ -178,7 +176,6 @@ export const BundlePackages = (props) => {
178
176
  className={css.toolbar}
179
177
  renderActions={({ actionClassName }) => (
180
178
  <FlexStack space="xxsmall" className={cx(css.dropdown, actionClassName)}>
181
- <SortDropdown fields={sortFields} {...sort} onChange={updateSort} />
182
179
  <MetricsTableOptions
183
180
  handleViewAll={resetAllFilters}
184
181
  handleResetFilters={resetFilters}
@@ -209,6 +206,8 @@ export const BundlePackages = (props) => {
209
206
  renderRowHeader={renderRowHeader}
210
207
  showHeaderSum
211
208
  title={metricsTableTitle}
209
+ sort={sort}
210
+ updateSort={updateSort}
212
211
  />
213
212
  </main>
214
213
  </section>
@@ -259,12 +258,6 @@ BundlePackages.propTypes = {
259
258
  changed: PropTypes.bool,
260
259
  }).isRequired,
261
260
  hasActiveFilters: PropTypes.bool,
262
- sortFields: PropTypes.shape({
263
- [PropTypes.string]: PropTypes.shape({
264
- label: PropTypes.string,
265
- defaultDirection: PropTypes.bool,
266
- }),
267
- }).isRequired,
268
261
  sort: PropTypes.shape({
269
262
  sortBy: PropTypes.string,
270
263
  direction: PropTypes.string,
@@ -1,8 +1,6 @@
1
1
  import uniq from 'lodash/uniq';
2
2
  import { PACKAGE_FILTERS } from '@bundle-stats/utils';
3
3
 
4
- import { SORT_BY_NAME, SORT_BY_DELTA, SORT_BY_SIZE } from './bundle-packages.constants';
5
-
6
4
  // Get a list of duplicate packages across jobs
7
5
  export const getDuplicatePackages = (jobs) => {
8
6
  const jobsDuplicatePackages = jobs.map((job) => {
@@ -25,21 +23,7 @@ export const getRowFilter = (filters) => (item) => {
25
23
  return true;
26
24
  };
27
25
 
28
- export const getCustomSort = (sortId) => (item) => {
29
- if (sortId === SORT_BY_NAME) {
30
- return item.key;
31
- }
32
-
33
- if (sortId === SORT_BY_DELTA) {
34
- return item?.runs?.[0]?.delta ? Math.abs(item.runs[0].delta) : 0;
35
- }
36
-
37
- if (sortId === SORT_BY_SIZE) {
38
- return item?.runs?.[0]?.value || 0;
39
- }
40
-
41
- return [!item.changed, item.key];
42
- };
26
+ export const getCustomSort = (item) => [!item.changed, item.key];
43
27
 
44
28
  export const getAddRowDuplicateFlag = (duplicateJobs) => (row) => ({
45
29
  ...row,
@@ -6,7 +6,6 @@ import * as webpack from '@bundle-stats/utils/lib-esm/webpack/compare';
6
6
  import { useRowsFilter } from '../../hooks/rows-filter';
7
7
  import { useRowsSort } from '../../hooks/rows-sort';
8
8
  import { useSearchParams } from '../../hooks/search-params';
9
- import { SORT_BY } from './bundle-packages.constants';
10
9
  import { BundlePackages as BundlePackagesComponent } from './bundle-packages';
11
10
  import { getDuplicatePackages, getAddRowDuplicateFlag, getRowFilter, getCustomSort } from './bundle-packages.utils';
12
11
 
@@ -50,9 +49,8 @@ export const BundlePackages = (props) => {
50
49
 
51
50
  const sortParams = useRowsSort({
52
51
  rows: filteredRows,
53
- sortFields: SORT_BY,
54
- sortBy,
55
- sortDirection: direction,
52
+ fieldPath: sortBy,
53
+ direction,
56
54
  getCustomSort,
57
55
  });
58
56
 
@@ -1,4 +1,4 @@
1
- import React, { useMemo } from 'react';
1
+ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import cx from 'classnames';
4
4
  import isEmpty from 'lodash/isEmpty';
@@ -11,102 +11,109 @@ import { Stack } from '../../layout/stack';
11
11
  import { Metric } from '../metric';
12
12
  import { Delta } from '../delta';
13
13
  import { JobName } from '../job-name';
14
+ import { SortButton } from '../sort-button';
14
15
  import styles from './metrics-table.module.css';
15
16
 
16
17
  const METRIC_TYPE_DATA = getGlobalMetricType(null, METRIC_TYPE_FILE_SIZE);
17
18
  const VISIBLE_COUNT = 500;
19
+ const BASELINE_COLUMN_SPAN = 1;
20
+ const CURRENT_COLUMN_SPAN = 3;
21
+ const BASELINE_TITLE = 'Baseline';
22
+ const CURRENT_TITLE = 'Current';
18
23
 
19
- const getRowsRunTotal = (rows, runIndex) => sum(rows.map((row) => row?.runs?.[runIndex]?.value || 0));
24
+ const getRowsRunTotal = (rows, runIndex) =>
25
+ sum(rows.map((row) => row?.runs?.[runIndex]?.value || 0));
20
26
 
21
- const getHeaderLabelCells = (rows) => (run, runIndex, runs) => {
22
- const isBaseline = runIndex === runs.length - 1;
23
- const className = cx(styles.value, isBaseline ? styles.baseline : styles.current);
27
+ const ColumnJob = ({ run, isBaseline }) => {
28
+ const colSpan = isBaseline ? BASELINE_COLUMN_SPAN : CURRENT_COLUMN_SPAN;
24
29
 
25
30
  if (!run) {
26
- return [
27
- { children: '-', className },
28
- ...(!isBaseline ? [{ children: ' ', className: styles.delta }] : []),
29
- ];
31
+ return <Table.Th colSpan={colSpan}>-</Table.Th>;
30
32
  }
31
33
 
32
34
  const { label, internalBuildNumber } = run;
33
35
 
34
- const jobName = (
35
- <JobName
36
- title={runIndex === 0 ? 'Current' : 'Baseline'}
37
- internalBuildNumber={internalBuildNumber}
38
- className={styles.jobName}
39
- >
40
- {label}
41
- </JobName>
36
+ return (
37
+ <Table.Th className={styles.job} colSpan={colSpan}>
38
+ <JobName
39
+ title={isBaseline ? BASELINE_TITLE : CURRENT_TITLE}
40
+ internalBuildNumber={internalBuildNumber}
41
+ className={styles.jobName}
42
+ >
43
+ {label}
44
+ </JobName>
45
+ </Table.Th>
42
46
  );
43
-
44
- return [
45
- // Value column
46
- { children: jobName, className },
47
- // Delta column
48
- ...(!isBaseline ? [{ children: ' ', className: cx(styles.delta) }] : []),
49
- ];
50
47
  };
51
48
 
52
- const getHeaderTotalCells = (rows) => (run, runIndex, runs) => {
53
- const isBaseline = runIndex === runs.length - 1;
54
- const className = cx(styles.value, isBaseline ? styles.baseline : styles.current);
55
-
49
+ const ColumnSum = ({ rows, isBaseline, runIndex, updateSort, sort }) => {
56
50
  const currentRunTotal = getRowsRunTotal(rows, runIndex);
57
51
  const baselineRunTotal = !isBaseline && getRowsRunTotal(rows, runIndex + 1);
58
52
  const infoTotal = getMetricRunInfo(METRIC_TYPE_DATA, currentRunTotal, baselineRunTotal);
53
+ const fieldPath = `runs[${runIndex}]`;
59
54
 
60
- return [
61
- // Value column
62
- {
63
- children: <Metric className={styles.tableHeaderRunMetric} value={infoTotal.displayValue} />,
64
- className,
65
- },
66
-
67
- // Delta column
68
- ...(!isBaseline
69
- ? [
70
- {
71
- children: (
55
+ return (
56
+ <>
57
+ <Table.Th className={cx(styles.value, styles.sum)}>
58
+ {(updateSort && sort) ? (
59
+ <SortButton
60
+ fieldPath={fieldPath}
61
+ fieldName="value"
62
+ label="absolute value"
63
+ updateSort={updateSort}
64
+ sort={sort}
65
+ >
66
+ <Metric value={infoTotal.displayValue} />
67
+ </SortButton>
68
+ ) : (
69
+ <Metric value={infoTotal.displayValue} />
70
+ )}
71
+ </Table.Th>
72
+ {!isBaseline && (
73
+ <>
74
+ <Table.Th className={cx(styles.delta, styles.sum)}>
75
+ {(updateSort && sort) ? (
76
+ <SortButton
77
+ fieldPath={fieldPath}
78
+ fieldName="delta"
79
+ label="absolute change"
80
+ updateSort={updateSort}
81
+ sort={sort}
82
+ >
83
+ <Delta displayValue={infoTotal.displayDelta} deltaType={infoTotal.deltaType} />
84
+ </SortButton>
85
+ ) : (
86
+ <Delta displayValue={infoTotal.displayDelta} deltaType={infoTotal.deltaType} />
87
+ )}
88
+ </Table.Th>
89
+ <Table.Th className={cx(styles.delta, styles.sum)}>
90
+ {(updateSort && sort) ? (
91
+ <SortButton
92
+ fieldPath={fieldPath}
93
+ fieldName="deltaPercentage"
94
+ label="absolute percentual change"
95
+ updateSort={updateSort}
96
+ sort={sort}
97
+ >
98
+ <Delta
99
+ displayValue={infoTotal.displayDeltaPercentage}
100
+ deltaType={infoTotal.deltaType}
101
+ />
102
+ </SortButton>
103
+ ) : (
72
104
  <Delta
73
105
  displayValue={infoTotal.displayDeltaPercentage}
74
106
  deltaType={infoTotal.deltaType}
75
107
  />
76
- ),
77
- className: styles.delta,
78
- },
79
- ]
80
- : []),
81
- ];
108
+ )}
109
+ </Table.Th>
110
+ </>
111
+ )}
112
+ </>
113
+ );
82
114
  };
83
115
 
84
- const getHeaderRows = (runs, items, showHeaderSum, title) => [
85
- {
86
- className: styles.headerRowColumns,
87
- cells: [
88
- // Metric name column - one empty strying to render the column
89
- {
90
- children: title || ' ',
91
- className: styles.metricName,
92
- rowSpan: showHeaderSum ? 2 : 1,
93
- },
94
-
95
- // Runs
96
- ...runs.map(getHeaderLabelCells(items)).flat(),
97
- ],
98
- },
99
- ...(showHeaderSum
100
- ? [
101
- {
102
- className: styles.headerRowTotals,
103
- cells: runs.map(getHeaderTotalCells(items)).flat(),
104
- },
105
- ]
106
- : []),
107
- ];
108
-
109
- const MetricsTableRow = ({ item, renderRowHeader }) => (
116
+ const Row = ({ item, renderRowHeader }) => (
110
117
  <Table.Tr className={cx(!item.changed && styles.unchanged)}>
111
118
  <Table.Th className={styles.metricName}>{renderRowHeader(item)}</Table.Th>
112
119
 
@@ -120,11 +127,13 @@ const MetricsTableRow = ({ item, renderRowHeader }) => (
120
127
  <>
121
128
  <Table.Td className={valueClassName}>-</Table.Td>
122
129
  {!isBaseline && <Table.Td className={styles.delta} />}
130
+ {!isBaseline && <Table.Td className={cx(styles.delta, styles.deltaPercentage)} />}
123
131
  </>
124
132
  );
125
133
  }
126
134
 
127
- const { displayValue, deltaPercentage, displayDeltaPercentage, deltaType } = run;
135
+ const { displayValue, deltaPercentage, displayDelta, displayDeltaPercentage, deltaType } =
136
+ run;
128
137
 
129
138
  return (
130
139
  <>
@@ -132,9 +141,14 @@ const MetricsTableRow = ({ item, renderRowHeader }) => (
132
141
  <Metric value={displayValue} />
133
142
  </Table.Td>
134
143
  {!isBaseline && typeof deltaPercentage === 'number' && (
135
- <Table.Td className={styles.delta}>
136
- <Delta displayValue={displayDeltaPercentage} deltaType={deltaType} />
137
- </Table.Td>
144
+ <>
145
+ <Table.Td className={styles.delta}>
146
+ <Delta displayValue={displayDelta} deltaType={deltaType} />
147
+ </Table.Td>
148
+ <Table.Td className={cx(styles.delta, styles.deltaPercentage)}>
149
+ <Delta displayValue={displayDeltaPercentage} deltaType={deltaType} />
150
+ </Table.Td>
151
+ </>
138
152
  )}
139
153
  </>
140
154
  );
@@ -142,7 +156,7 @@ const MetricsTableRow = ({ item, renderRowHeader }) => (
142
156
  </Table.Tr>
143
157
  );
144
158
 
145
- MetricsTableRow.propTypes = {
159
+ Row.propTypes = {
146
160
  item: PropTypes.shape({
147
161
  changed: PropTypes.bool,
148
162
  runs: PropTypes.arrayOf(
@@ -165,21 +179,14 @@ export const MetricsTable = ({
165
179
  items,
166
180
  emptyMessage,
167
181
  showHeaderSum,
168
- headerRows,
169
182
  title,
170
183
  showAllItems,
171
184
  setShowAllItems,
185
+ sort,
186
+ updateSort,
172
187
  ...restProps
173
188
  }) => {
174
- const { headers, columnClassNames } = useMemo(() => {
175
- const headerColumns = getHeaderRows(runs, items, showHeaderSum, title);
176
-
177
- return {
178
- headers: [...headerRows, ...headerColumns],
179
- // First header row has the column class names
180
- columnClassNames: headerColumns[0].cells.map((headerColumn) => headerColumn.className),
181
- };
182
- }, [headerRows, runs, items, showHeaderSum, title]);
189
+ const columnCount = (runs.length - 1) * CURRENT_COLUMN_SPAN + BASELINE_COLUMN_SPAN + 1;
183
190
 
184
191
  const rootClassName = cx(
185
192
  styles.root,
@@ -196,24 +203,32 @@ export const MetricsTable = ({
196
203
  return (
197
204
  <Table className={rootClassName} compact {...restProps}>
198
205
  <Table.THead>
199
- {headers.map((headerRow) => {
200
- const { cells, className: rowClassName } = headerRow.cells
201
- ? headerRow
202
- : { cells: headerRow };
203
-
204
- return (
205
- <Table.Tr className={rowClassName}>
206
- {cells.map((header) => (
207
- <Table.Th {...header} />
208
- ))}
209
- </Table.Tr>
210
- );
211
- })}
206
+ <Table.Tr className={styles.headerRow}>
207
+ <Table.Th className={styles.metricName} rowSpan={showHeaderSum ? 2 : 1}>
208
+ {title || ' '}
209
+ </Table.Th>
210
+ {runs.map((run, runIndex) => (
211
+ <ColumnJob run={run} isBaseline={runIndex === runs.length - 1} />
212
+ ))}
213
+ </Table.Tr>
214
+ {showHeaderSum && (
215
+ <Table.Tr className={styles.headerRow}>
216
+ {runs.map((run, runIndex) => (
217
+ <ColumnSum
218
+ rows={items}
219
+ isBaseline={runIndex === runs.length - 1}
220
+ runIndex={runIndex}
221
+ updateSort={updateSort}
222
+ sort={sort}
223
+ />
224
+ ))}
225
+ </Table.Tr>
226
+ )}
212
227
  </Table.THead>
213
228
  <Table.TBody>
214
229
  {showEmpty && (
215
230
  <Table.Tr>
216
- <Table.Td className={styles.empty} colSpan={columnClassNames?.length || 1}>
231
+ <Table.Td className={styles.empty} colSpan={columnCount}>
217
232
  <Stack space="xxsmall">
218
233
  <div>
219
234
  <Icon glyph={Icon.ICONS.INFO} className={styles.emptyIcon} size="large" />
@@ -227,30 +242,29 @@ export const MetricsTable = ({
227
242
  {showItems && (
228
243
  <>
229
244
  {visibleItems.map((item) => (
230
- <MetricsTableRow key={item.key} item={item} renderRowHeader={renderRowHeader} />
245
+ <Row key={item.key} item={item} renderRowHeader={renderRowHeader} />
231
246
  ))}
232
247
 
233
248
  {hasHiddenItems && (
234
249
  <Table.Tr>
235
- <Table.Td className={styles.showAllItems} colSpan={columnClassNames?.length || 1}>
250
+ <Table.Td className={styles.showAllItems} colSpan={columnCount}>
236
251
  {showAllItems ? (
237
- <button
238
- onClick={() => setShowAllItems(false)}
239
- type="button"
240
- className={styles.showAllItemsButton}
241
- >
242
- Show less
243
- </button>
244
- ) : (
245
- <button
246
- onClick={() => setShowAllItems(true)}
247
- type="button"
248
- className={styles.showAllItemsButton}
249
- >
250
- Show all
251
- </button>
252
- )
253
- }
252
+ <button
253
+ onClick={() => setShowAllItems(false)}
254
+ type="button"
255
+ className={styles.showAllItemsButton}
256
+ >
257
+ Show less
258
+ </button>
259
+ ) : (
260
+ <button
261
+ onClick={() => setShowAllItems(true)}
262
+ type="button"
263
+ className={styles.showAllItemsButton}
264
+ >
265
+ Show all
266
+ </button>
267
+ )}
254
268
  </Table.Td>
255
269
  </Table.Tr>
256
270
  )}
@@ -266,7 +280,6 @@ MetricsTable.defaultProps = {
266
280
  renderRowHeader: (item) => item.label,
267
281
  emptyMessage: 'No entries found.',
268
282
  showHeaderSum: false,
269
- headerRows: [],
270
283
  title: '',
271
284
  };
272
285
 
@@ -292,15 +305,5 @@ MetricsTable.propTypes = {
292
305
  ).isRequired,
293
306
  emptyMessage: PropTypes.element,
294
307
  showHeaderSum: PropTypes.bool,
295
- headerRows: PropTypes.arrayOf(
296
- PropTypes.arrayOf(
297
- PropTypes.oneOfType([
298
- PropTypes.element,
299
- PropTypes.shape({
300
- children: PropTypes.node,
301
- }),
302
- ]),
303
- ),
304
- ),
305
308
  title: PropTypes.element,
306
309
  };
@@ -15,8 +15,9 @@
15
15
  width: 100%;
16
16
  }
17
17
 
18
- .tableHeaderRunMetric {
19
- text-transform: none;
18
+ .root .job {
19
+ padding-right: calc(var(--space-xxxsmall) + var(--space-small));
20
+ text-align: right;
20
21
  }
21
22
 
22
23
  .root .value {
@@ -26,18 +27,13 @@
26
27
  }
27
28
 
28
29
  .root .delta {
29
- padding-left: 0;
30
- text-align: left;
30
+ text-align: right;
31
31
  font-size: var(--size-xsmall);
32
32
  vertical-align: baseline;
33
33
  }
34
34
 
35
35
  .root tbody .delta {
36
- line-height: 1.6rem; /* baseline alignment with the value */
37
- }
38
-
39
- .root .value.current {
40
- padding-right: var(--space-xxsmall);
36
+ line-height: 1.6rem; /* baseline alignment with the column value */
41
37
  }
42
38
 
43
39
  .jobName {
@@ -45,17 +41,16 @@
45
41
  }
46
42
 
47
43
  /* columns overrides for showHeaderSum */
48
- .showHeaderSum .headerRowColumns .value,
49
- .showHeaderSum .headerRowColumns .delta {
44
+ .showHeaderSum .headerRow .job {
50
45
  border-bottom: 0;
51
46
  padding-bottom: 0;
52
47
  }
53
48
 
54
- .headerRowTotals th {
55
- padding-top: 0 !important;
56
- font-weight: normal;
57
- font-size: var(--size-xsmall);
49
+ .showHeaderSum .headerRow .sum {
50
+ padding-top: var(--space-xxxsmall);
51
+ font-size: var(--size-small);
58
52
  vertical-align: middle;
53
+ text-transform: none;
59
54
  }
60
55
 
61
56
  /** rows */
@@ -14,7 +14,7 @@ export const MetricsTableTitle = (props) => {
14
14
  const rootClassName = cx(css.root, className);
15
15
 
16
16
  return (
17
- <Stack className={rootClassName}>
17
+ <Stack space="xxsmall" className={rootClassName}>
18
18
  <FlexStack space="xxxsmall" alignItems="center" as="h3" className={css.title}>
19
19
  <span>{title}</span>
20
20
 
@@ -18,8 +18,10 @@
18
18
  }
19
19
 
20
20
  .info {
21
+ color: var(--color-text-light);
22
+ font-family: var(--font-family-fixed);
21
23
  font-size: var(--size-xsmall);
22
- font-weight: normal;
24
+ font-weight: bold;
23
25
  }
24
26
 
25
27
  .readMoreLink {
@@ -0,0 +1 @@
1
+ export * from './sort-button';
@@ -0,0 +1,89 @@
1
+ .root {
2
+ --icon-dimension: 15px;
3
+ display: inline-block;
4
+ margin: calc(0px - var(--space-xxxsmall));
5
+ margin-right: calc(0px - 1px - var(--icon-dimension));
6
+ border-radius: var(--radius-small);
7
+ position: relative;
8
+ }
9
+
10
+ .button {
11
+ appearance: none;
12
+ cursor: pointer;
13
+ border: 0;
14
+ padding: 0;
15
+ margin: 0;
16
+ background: transparent;
17
+ transition: var(--transition-out);
18
+ }
19
+
20
+ .toggle {
21
+ padding: var(--space-xxxsmall);
22
+ padding-right: calc(1px + var(--icon-dimension));
23
+ }
24
+
25
+ .direction {
26
+ position: absolute;
27
+ padding: 2px;
28
+ display: block;
29
+ overflow: hidden;
30
+ right: -2px; /* compensate for the button padding */
31
+ color: var(--color-text-ultra-light);
32
+ transition: var(--transition-out);
33
+ }
34
+
35
+ .directionIcon {
36
+ display: block;
37
+ width: var(--icon-dimension);
38
+ height: var(--icon-dimension);
39
+ }
40
+
41
+ .directionActive {
42
+ color: var(--color-text);
43
+ transition: var(--transition-in);
44
+ }
45
+
46
+ .direction:hover,
47
+ .direction:active,
48
+ .direction:focus {
49
+ color: var(--color-text-dark);
50
+ transition: var(--transition-in);
51
+ }
52
+
53
+ .directionAsc {
54
+ padding-bottom: 0;
55
+ bottom: 50%;
56
+ }
57
+
58
+ .directionAsc .directionIcon {
59
+ transform: translateY(3px);
60
+ }
61
+
62
+ .directionDesc {
63
+ padding-top: 0;
64
+ top: 50%;
65
+ }
66
+
67
+ .directionDesc .directionIcon {
68
+ transform: translateY(-5px);
69
+ }
70
+
71
+ .root:hover,
72
+ .root:active,
73
+ .root:focus {
74
+ background: var(--color-outline);
75
+ }
76
+
77
+ .direction {
78
+ pointer-events: none;
79
+ opacity: 0;
80
+ }
81
+
82
+ .active .direction,
83
+ .root:hover .direction,
84
+ .root:active .direction,
85
+ .root:focus .direction {
86
+ pointer-events: auto;
87
+ opacity: 1;
88
+ transition: var(--transition-in);
89
+ }