@bundle-stats/ui 4.22.3 → 4.23.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/lib/app/app.js +3 -0
  2. package/lib/app/app.js.map +1 -1
  3. package/lib/app/app.module.css +12 -2
  4. package/lib/chart-colors.json +58 -44
  5. package/lib/components/delta/delta.js +3 -13
  6. package/lib/components/delta/delta.js.map +1 -1
  7. package/lib/components/delta/delta.module.css +13 -12
  8. package/lib/components/delta/index.js.map +1 -1
  9. package/lib/components/entry-info/entry-info.js +1 -1
  10. package/lib/components/entry-info/entry-info.js.map +1 -1
  11. package/lib/components/entry-info/entry-info.module.css +1 -1
  12. package/lib/components/index.js +3 -1
  13. package/lib/components/index.js.map +1 -1
  14. package/lib/components/insight-icon/insight-icon.module.css +6 -6
  15. package/lib/components/job-name/job-name.js +1 -2
  16. package/lib/components/job-name/job-name.js.map +1 -1
  17. package/lib/components/metric-change-legend/index.js +18 -0
  18. package/lib/components/metric-change-legend/index.js.map +1 -0
  19. package/lib/components/metric-change-legend/metric-change-legend.js +91 -0
  20. package/lib/components/metric-change-legend/metric-change-legend.js.map +1 -0
  21. package/lib/components/metric-change-legend/metric-change-legend.module.css +80 -0
  22. package/lib/components/metric-run-info/metric-run-info.js +22 -7
  23. package/lib/components/metric-run-info/metric-run-info.js.map +1 -1
  24. package/lib/components/metrics-display-selector/metrics-display-selector.js +7 -9
  25. package/lib/components/metrics-display-selector/metrics-display-selector.js.map +1 -1
  26. package/lib/components/metrics-display-selector/metrics-display-selector.module.css +0 -25
  27. package/lib/components/metrics-table-header/metrics-table-header.js +6 -6
  28. package/lib/components/metrics-table-header/metrics-table-header.js.map +1 -1
  29. package/lib/components/metrics-table-header/metrics-table-header.module.css +8 -12
  30. package/lib/components/metrics-table-options/metrics-table-options.js +2 -3
  31. package/lib/components/metrics-table-options/metrics-table-options.js.map +1 -1
  32. package/lib/components/metrics-table-title/metrics-table-title.js +1 -1
  33. package/lib/components/metrics-table-title/metrics-table-title.js.map +1 -1
  34. package/lib/components/metrics-treemap/metrics-treemap.constants.js +3 -1
  35. package/lib/components/metrics-treemap/metrics-treemap.constants.js.map +1 -1
  36. package/lib/components/metrics-treemap/metrics-treemap.js +68 -57
  37. package/lib/components/metrics-treemap/metrics-treemap.js.map +1 -1
  38. package/lib/components/metrics-treemap/metrics-treemap.module.css +9 -16
  39. package/lib/components/metrics-treemap/metrics-treemap.utils.js +72 -16
  40. package/lib/components/metrics-treemap/metrics-treemap.utils.js.map +1 -1
  41. package/lib/components/run-info/run-info.js +26 -14
  42. package/lib/components/run-info/run-info.js.map +1 -1
  43. package/lib/components/run-info/run-info.module.css +35 -26
  44. package/lib/components/summary/summary.js +9 -10
  45. package/lib/components/summary/summary.js.map +1 -1
  46. package/lib/components/summary/summary.module.css +60 -10
  47. package/lib/constants.js +13 -1
  48. package/lib/constants.js.map +1 -1
  49. package/lib/css/variables.css +71 -58
  50. package/lib/ui/button/button.js +2 -2
  51. package/lib/ui/button/button.js.map +1 -1
  52. package/lib/ui/dialog/dialog.js +4 -2
  53. package/lib/ui/dialog/dialog.js.map +1 -1
  54. package/lib/ui/dialog/dialog.module.css +22 -0
  55. package/lib/ui/dropdown/dropdown.js +13 -4
  56. package/lib/ui/dropdown/dropdown.js.map +1 -1
  57. package/lib/ui/dropdown/dropdown.module.css +51 -6
  58. package/lib/ui/filters/filters.js +22 -11
  59. package/lib/ui/filters/filters.js.map +1 -1
  60. package/lib/ui/filters/filters.module.css +0 -14
  61. package/lib/ui/hover-card/hover-card.js +7 -1
  62. package/lib/ui/hover-card/hover-card.js.map +1 -1
  63. package/lib/ui/hover-card/hover-card.module.css +14 -0
  64. package/lib/ui/table/table.module.css +0 -1
  65. package/lib/ui/tooltip/tooltip.js +6 -1
  66. package/lib/ui/tooltip/tooltip.js.map +1 -1
  67. package/lib/ui/tooltip/tooltip.module.css +11 -0
  68. package/lib-esm/app/app.js +4 -1
  69. package/lib-esm/app/app.js.map +1 -1
  70. package/lib-esm/app/app.module.css +12 -2
  71. package/lib-esm/chart-colors.json +58 -44
  72. package/lib-esm/components/delta/delta.js +3 -13
  73. package/lib-esm/components/delta/delta.js.map +1 -1
  74. package/lib-esm/components/delta/delta.module.css +13 -12
  75. package/lib-esm/components/delta/index.js.map +1 -1
  76. package/lib-esm/components/entry-info/entry-info.js +1 -1
  77. package/lib-esm/components/entry-info/entry-info.js.map +1 -1
  78. package/lib-esm/components/entry-info/entry-info.module.css +1 -1
  79. package/lib-esm/components/index.js +1 -0
  80. package/lib-esm/components/index.js.map +1 -1
  81. package/lib-esm/components/insight-icon/insight-icon.module.css +6 -6
  82. package/lib-esm/components/job-name/job-name.js +1 -2
  83. package/lib-esm/components/job-name/job-name.js.map +1 -1
  84. package/lib-esm/components/metric-change-legend/index.js +2 -0
  85. package/lib-esm/components/metric-change-legend/index.js.map +1 -0
  86. package/lib-esm/components/metric-change-legend/metric-change-legend.js +84 -0
  87. package/lib-esm/components/metric-change-legend/metric-change-legend.js.map +1 -0
  88. package/lib-esm/components/metric-change-legend/metric-change-legend.module.css +80 -0
  89. package/lib-esm/components/metric-run-info/metric-run-info.js +22 -7
  90. package/lib-esm/components/metric-run-info/metric-run-info.js.map +1 -1
  91. package/lib-esm/components/metrics-display-selector/metrics-display-selector.js +7 -9
  92. package/lib-esm/components/metrics-display-selector/metrics-display-selector.js.map +1 -1
  93. package/lib-esm/components/metrics-display-selector/metrics-display-selector.module.css +0 -25
  94. package/lib-esm/components/metrics-table-header/metrics-table-header.js +6 -6
  95. package/lib-esm/components/metrics-table-header/metrics-table-header.js.map +1 -1
  96. package/lib-esm/components/metrics-table-header/metrics-table-header.module.css +8 -12
  97. package/lib-esm/components/metrics-table-options/metrics-table-options.js +3 -4
  98. package/lib-esm/components/metrics-table-options/metrics-table-options.js.map +1 -1
  99. package/lib-esm/components/metrics-table-title/metrics-table-title.js +1 -1
  100. package/lib-esm/components/metrics-table-title/metrics-table-title.js.map +1 -1
  101. package/lib-esm/components/metrics-treemap/metrics-treemap.constants.js +2 -0
  102. package/lib-esm/components/metrics-treemap/metrics-treemap.constants.js.map +1 -1
  103. package/lib-esm/components/metrics-treemap/metrics-treemap.js +74 -63
  104. package/lib-esm/components/metrics-treemap/metrics-treemap.js.map +1 -1
  105. package/lib-esm/components/metrics-treemap/metrics-treemap.module.css +9 -16
  106. package/lib-esm/components/metrics-treemap/metrics-treemap.utils.js +71 -17
  107. package/lib-esm/components/metrics-treemap/metrics-treemap.utils.js.map +1 -1
  108. package/lib-esm/components/run-info/run-info.js +26 -14
  109. package/lib-esm/components/run-info/run-info.js.map +1 -1
  110. package/lib-esm/components/run-info/run-info.module.css +35 -26
  111. package/lib-esm/components/summary/summary.js +10 -11
  112. package/lib-esm/components/summary/summary.js.map +1 -1
  113. package/lib-esm/components/summary/summary.module.css +60 -10
  114. package/lib-esm/constants.js +12 -0
  115. package/lib-esm/constants.js.map +1 -1
  116. package/lib-esm/css/variables.css +71 -58
  117. package/lib-esm/ui/button/button.js +2 -2
  118. package/lib-esm/ui/button/button.js.map +1 -1
  119. package/lib-esm/ui/dialog/dialog.js +4 -2
  120. package/lib-esm/ui/dialog/dialog.js.map +1 -1
  121. package/lib-esm/ui/dialog/dialog.module.css +22 -0
  122. package/lib-esm/ui/dropdown/dropdown.js +11 -3
  123. package/lib-esm/ui/dropdown/dropdown.js.map +1 -1
  124. package/lib-esm/ui/dropdown/dropdown.module.css +51 -6
  125. package/lib-esm/ui/filters/filters.js +23 -12
  126. package/lib-esm/ui/filters/filters.js.map +1 -1
  127. package/lib-esm/ui/filters/filters.module.css +0 -14
  128. package/lib-esm/ui/hover-card/hover-card.js +7 -1
  129. package/lib-esm/ui/hover-card/hover-card.js.map +1 -1
  130. package/lib-esm/ui/hover-card/hover-card.module.css +14 -0
  131. package/lib-esm/ui/table/table.module.css +0 -1
  132. package/lib-esm/ui/tooltip/tooltip.js +6 -1
  133. package/lib-esm/ui/tooltip/tooltip.js.map +1 -1
  134. package/lib-esm/ui/tooltip/tooltip.module.css +11 -0
  135. package/package.json +16 -16
  136. package/src/app/app.jsx +6 -1
  137. package/src/app/app.module.css +12 -2
  138. package/src/chart-colors.json +58 -44
  139. package/src/components/delta/delta.module.css +13 -12
  140. package/src/components/delta/delta.tsx +32 -0
  141. package/src/components/entry-info/entry-info.module.css +1 -1
  142. package/src/components/entry-info/entry-info.tsx +2 -2
  143. package/src/components/index.ts +3 -0
  144. package/src/components/insight-icon/insight-icon.module.css +6 -6
  145. package/src/components/job-name/job-name.jsx +2 -3
  146. package/src/components/metric-change-legend/index.ts +1 -0
  147. package/src/components/metric-change-legend/metric-change-legend.module.css +80 -0
  148. package/src/components/metric-change-legend/metric-change-legend.stories.tsx +13 -0
  149. package/src/components/metric-change-legend/metric-change-legend.tsx +148 -0
  150. package/src/components/metric-run-info/metric-run-info.tsx +32 -17
  151. package/src/components/metrics-display-selector/metrics-display-selector.module.css +0 -25
  152. package/src/components/metrics-display-selector/metrics-display-selector.tsx +25 -37
  153. package/src/components/metrics-table-header/metrics-table-header.module.css +8 -12
  154. package/src/components/metrics-table-header/metrics-table-header.tsx +5 -6
  155. package/src/components/metrics-table-options/metrics-table-options.tsx +5 -6
  156. package/src/components/metrics-table-title/metrics-table-title.jsx +1 -1
  157. package/src/components/metrics-treemap/metrics-treemap.constants.ts +8 -0
  158. package/src/components/metrics-treemap/metrics-treemap.module.css +9 -16
  159. package/src/components/metrics-treemap/metrics-treemap.tsx +160 -135
  160. package/src/components/metrics-treemap/metrics-treemap.utils.ts +141 -27
  161. package/src/components/run-info/run-info.module.css +35 -26
  162. package/src/components/run-info/run-info.stories.tsx +16 -10
  163. package/src/components/run-info/run-info.tsx +132 -73
  164. package/src/components/summary/summary.module.css +60 -10
  165. package/src/components/summary/summary.tsx +47 -50
  166. package/src/constants.ts +13 -0
  167. package/src/css/variables.css +71 -58
  168. package/src/ui/button/button.tsx +2 -1
  169. package/src/ui/dialog/dialog.module.css +22 -0
  170. package/src/ui/dialog/dialog.tsx +9 -2
  171. package/src/ui/dropdown/dropdown.module.css +51 -6
  172. package/src/ui/dropdown/dropdown.tsx +17 -2
  173. package/src/ui/filters/filters.module.css +0 -14
  174. package/src/ui/filters/filters.tsx +111 -47
  175. package/src/ui/hover-card/hover-card.module.css +14 -0
  176. package/src/ui/hover-card/hover-card.tsx +7 -1
  177. package/src/ui/table/table.module.css +0 -1
  178. package/src/ui/tooltip/tooltip.module.css +11 -0
  179. package/src/ui/tooltip/tooltip.tsx +6 -1
  180. package/types/components/delta/delta.d.ts +7 -21
  181. package/types/components/delta/index.d.ts +1 -1
  182. package/types/components/index.d.ts +3 -0
  183. package/types/components/metric-change-legend/index.d.ts +1 -0
  184. package/types/components/metric-change-legend/metric-change-legend.d.ts +4 -0
  185. package/types/components/metric-run-info/metric-run-info.d.ts +3 -6
  186. package/types/components/metrics-treemap/metrics-treemap.constants.d.ts +2 -0
  187. package/types/components/metrics-treemap/metrics-treemap.utils.d.ts +11 -5
  188. package/types/components/run-info/run-info.d.ts +16 -7
  189. package/types/constants.d.ts +12 -0
  190. package/types/ui/dialog/dialog.d.ts +3 -2
  191. package/types/ui/dropdown/dropdown.d.ts +4 -1
  192. package/types/ui/filters/filters.d.ts +2 -1
  193. package/vitest.config.ts +4 -0
  194. package/lib/components/metrics-table-options/metrics-table-options.module.css +0 -15
  195. package/lib-esm/components/metrics-table-options/metrics-table-options.module.css +0 -15
  196. package/src/components/delta/delta.jsx +0 -22
  197. package/src/components/metrics-table-options/metrics-table-options.module.css +0 -15
  198. /package/src/components/delta/{index.js → index.ts} +0 -0
@@ -0,0 +1,148 @@
1
+ import React, { ReactNode } from 'react';
2
+ import cx from 'classnames';
3
+
4
+ import { FlexStack } from '../../layout/flex-stack';
5
+ import { Stack } from '../../layout/stack';
6
+ import { HoverCard } from '../../ui/hover-card';
7
+ import { Table } from '../../ui/table';
8
+ import css from './metric-change-legend.module.css';
9
+ import { Separator } from '../../layout';
10
+
11
+ type ChangeKind = 'improvement' | 'regression' | 'neutral' | 'no-change';
12
+ type ChangeKindVariant = 'low' | 'medium' | 'high';
13
+
14
+ type BarProps = {
15
+ className?: string;
16
+ kind: ChangeKind;
17
+ variants: ChangeKindVariant[];
18
+ size?: 'medium' | 'large';
19
+ };
20
+
21
+ const Variants = (props: BarProps) => {
22
+ const { className, kind, variants, size = 'medium' } = props;
23
+
24
+ return (
25
+ <span className={cx(css.variants, css[`bar-${kind}`], css[size], className)}>
26
+ {variants.map((variant) => (
27
+ <span key={variant} className={cx(css.variant, css[`variant-${variant}`])} />
28
+ ))}
29
+ </span>
30
+ );
31
+ };
32
+
33
+ type ChangeMagnitudeInfoProps = {
34
+ kind: 'improvement' | 'regression';
35
+ impact: string;
36
+ };
37
+
38
+ const ChangeMagnitudeInfo = (props: ChangeMagnitudeInfoProps) => {
39
+ const { kind, impact } = props;
40
+
41
+ return (
42
+ <Stack space="small">
43
+ <p>
44
+ Changes flagged as <strong>{kind}s</strong> have a {impact} impact on user performance.
45
+ </p>
46
+ <p>
47
+ The report shows different intensity colors depending on the magnitude of the percentage
48
+ change.
49
+ </p>
50
+ <Stack space="xxsmall">
51
+ <Separator />
52
+ <FlexStack alignItems="center" space="xxsmall">
53
+ <Variants kind={kind} variants={['low']} size="large" />
54
+ Change is smaller or equal than <strong>5%</strong>
55
+ </FlexStack>
56
+ <Separator />
57
+ <FlexStack alignItems="center" space="xxsmall">
58
+ <Variants kind={kind} variants={['medium']} size="large" />
59
+ Change is smaller or equal than <strong>50%</strong>
60
+ </FlexStack>
61
+ <Separator />
62
+ <FlexStack alignItems="center" space="xxsmall">
63
+ <Variants kind={kind} variants={['high']} size="large" />
64
+ Change is larger than <strong>50%</strong>
65
+ </FlexStack>
66
+ </Stack>
67
+ </Stack>
68
+ );
69
+ };
70
+
71
+ const ImprovementInfo = () => <ChangeMagnitudeInfo kind="improvement" impact="positive" />;
72
+ const RegressionInfo = () => <ChangeMagnitudeInfo kind="regression" impact="negative" />;
73
+
74
+ const NeutralInfo = () => (
75
+ <Stack space="small">
76
+ <p>
77
+ <strong>Neutral</strong> changes do not have a clear positive or negative impact on user
78
+ performance.
79
+ </p>
80
+ </Stack>
81
+ );
82
+
83
+ const NoChangeInfo = () => (
84
+ <Stack space="small">
85
+ <p>
86
+ The metric value is <strong>unchanged</strong> between the compared builds.
87
+ </p>
88
+ </Stack>
89
+ );
90
+
91
+ type ChangeConfig = {
92
+ id: ChangeKind;
93
+ label: string;
94
+ variants: ChangeKindVariant[];
95
+ info: ReactNode;
96
+ };
97
+
98
+ const ITEMS: ChangeConfig[] = [
99
+ {
100
+ id: 'improvement',
101
+ label: 'Improvement',
102
+ variants: ['low', 'medium', 'high'],
103
+ info: <ImprovementInfo />,
104
+ },
105
+ {
106
+ id: 'regression',
107
+ label: 'Regression',
108
+ variants: ['low', 'medium', 'high'],
109
+ info: <RegressionInfo />,
110
+ },
111
+ {
112
+ id: 'neutral',
113
+ label: 'Neutral',
114
+ variants: ['medium'],
115
+ info: <NeutralInfo />,
116
+ },
117
+ {
118
+ id: 'no-change',
119
+ label: 'No change',
120
+ variants: ['medium'],
121
+ info: <NoChangeInfo />,
122
+ },
123
+ ];
124
+
125
+ export interface MetricChangeLegendProps extends React.ComponentProps<'div'> {}
126
+
127
+ export const MetricChangeLegend = (props: MetricChangeLegendProps) => {
128
+ const { className, ...restProps } = props;
129
+
130
+ return (
131
+ <FlexStack inline space="small" className={cx(css.root, className)} {...restProps}>
132
+ {ITEMS.map(({ id, variants, label, info }) => (
133
+ <HoverCard
134
+ key={id}
135
+ anchorClassName={css.itemAnchor}
136
+ label={
137
+ <FlexStack inline alignItems="center" space="xxsmall" className={css.item}>
138
+ <Variants kind={id} variants={variants} className={css.itemVariants} />
139
+ <span className={css.itemLabel}>{label}</span>
140
+ </FlexStack>
141
+ }
142
+ >
143
+ {info}
144
+ </HoverCard>
145
+ ))}
146
+ </FlexStack>
147
+ );
148
+ };
@@ -1,5 +1,5 @@
1
1
  import React, { type MouseEvent, useCallback, useMemo, ElementType } from 'react';
2
- import { getGlobalMetricType, getMetricRunInfo } from '@bundle-stats/utils';
2
+ import { getGlobalMetricType, getMetricRunInfo, MetricTypes } from '@bundle-stats/utils';
3
3
  import { Focusable } from 'ariakit/focusable';
4
4
 
5
5
  import { Stack } from '../../layout/stack';
@@ -8,11 +8,11 @@ import { Icon } from '../../ui/icon';
8
8
  import { RunInfo, RunInfoProps } from '../run-info';
9
9
  import css from './metric-run-info.module.css';
10
10
 
11
- interface MetricInfoProps {
11
+ type MetricInfoProps = {
12
12
  title: string;
13
13
  description?: string;
14
14
  url?: string;
15
- }
15
+ };
16
16
 
17
17
  const MetricHoverCard = ({ title, description, url }: MetricInfoProps) => {
18
18
  // The component parent can be rendered inside a link or a button
@@ -50,17 +50,14 @@ const MetricHoverCard = ({ title, description, url }: MetricInfoProps) => {
50
50
  );
51
51
  };
52
52
 
53
- export interface MetricRunInfoProps {
53
+ export type MetricRunInfoProps = {
54
54
  metricId: string;
55
55
  current: number;
56
56
  baseline?: number;
57
- showDelta?: boolean;
58
57
  showMetricDescription?: boolean;
59
- showBaseline?: boolean;
60
58
  titleWrapper?: ElementType;
61
- size?: RunInfoProps['size'];
62
- loading?: RunInfoProps['loading'];
63
- }
59
+ showDeltaValue?: boolean;
60
+ } & Pick<RunInfoProps, 'showDelta' | 'showBaseline' | 'size' | 'loading'>;
64
61
 
65
62
  export const MetricRunInfo = (props: MetricRunInfoProps & React.ComponentProps<'div'>) => {
66
63
  const {
@@ -92,16 +89,37 @@ export const MetricRunInfo = (props: MetricRunInfoProps & React.ComponentProps<'
92
89
  [CustomTitleWrapper, metric.label],
93
90
  );
94
91
 
92
+ const baselineProps = useMemo(() => {
93
+ if (showBaseline) {
94
+ return {
95
+ showBaseline,
96
+ baseline: metric.formatter(baseline),
97
+ };
98
+ }
99
+
100
+ return { showBaseline };
101
+ }, [showBaseline, baseline]);
102
+
95
103
  const deltaProps = useMemo(() => {
96
- if (!showDelta || metric.skipDelta || !('delta' in metricRunInfo)) {
97
- return {};
104
+ if (!showDelta || metric.skipDelta) {
105
+ return {
106
+ showDelta: false,
107
+ };
108
+ }
109
+
110
+ // If the delta data is missing, do not render the dats
111
+ if (!('delta' in metricRunInfo)) {
112
+ return {
113
+ showDelta: false,
114
+ };
98
115
  }
99
116
 
100
117
  return {
101
- delta: metricRunInfo.displayDeltaPercentage,
118
+ showDelta: true,
102
119
  deltaType: metricRunInfo.deltaType,
120
+ deltaPercentage: metricRunInfo.displayDeltaPercentage,
103
121
  };
104
- }, [metricRunInfo]);
122
+ }, [metric, metricRunInfo, showDelta]);
105
123
 
106
124
  return (
107
125
  <RunInfo
@@ -109,10 +127,7 @@ export const MetricRunInfo = (props: MetricRunInfoProps & React.ComponentProps<'
109
127
  titleHoverCard={titleHoverCard}
110
128
  enhance
111
129
  current={metricRunInfo.displayValue}
112
- showBaseline={showBaseline}
113
- {...(showBaseline && {
114
- baseline: metric.formatter(baseline),
115
- })}
130
+ {...baselineProps}
116
131
  {...deltaProps}
117
132
  {...restProps}
118
133
  />
@@ -7,31 +7,6 @@
7
7
  padding: calc(var(--space-xxsmall) - 1px);
8
8
  }
9
9
 
10
- .dropdownGroupAnchor {
11
- border: none;
12
- border-radius: 0;
13
- position: relative;
14
- }
15
-
16
- .dropdownGroupAnchor::before {
17
- content: '';
18
- display: block;
19
- width: 1px;
20
- background: var(--color-outline-dark);
21
- position: absolute;
22
- left: 0;
23
- top: var(--space-xxxsmall);
24
- bottom: var(--space-xxxsmall);
25
- }
26
-
27
- .dropdownGroupAnchor:hover,
28
- .dropdownGroupAnchor:focus,
29
- .dropdownGroupAnchor:active {
30
- border-color: var(--color-outline);
31
- box-shadow: none;
32
- background: none;
33
- }
34
-
35
10
  .dropdownGroup:hover {
36
11
  border-color: var(--color-outline-dark);
37
12
  transition: var(--transition-in);
@@ -90,44 +90,32 @@ export const MetricsDisplaySelector = (
90
90
  );
91
91
 
92
92
  return (
93
- <FlexStack className={itemClassName} key={displayType}>
94
- <Button
95
- size="small"
96
- glyph={displayProps.glyph}
97
- onClick={() => onSelect(displayType)}
98
- className={css.dropdownGroupButton}
99
- >
100
- {displayProps.label}
101
- </Button>
102
- <Dropdown
103
- glyph={Icon.ICONS.CHEVRON_DOWN}
104
- placement="bottom-end"
105
- shift={-1}
106
- className={css.dropdownGroupAnchor}
107
- >
108
- {displayGroupsData.map((displayGroupData) => {
109
- const isGroupActive = isActive && displayGroupData.value === groupBy;
93
+ <Dropdown
94
+ label={displayProps.label}
95
+ glyph={displayProps.glyph}
96
+ placement="bottom-end"
97
+ shift={-1}
98
+ className={itemClassName}
99
+ key={displayType}
100
+ >
101
+ {displayGroupsData.map((displayGroupData) => {
102
+ const isGroupActive = isActive && displayGroupData.value === groupBy;
110
103
 
111
- return (
112
- <DropdownItem
113
- onClick={() => onSelect(displayType, displayGroupData.value)}
114
- isActive={isGroupActive}
115
- className={cx(css.dropdownItem, isGroupActive && css.dropdownItemActive)}
116
- key={displayGroupData.value}
117
- >
118
- <FlexStack space="xxxsmall" alignItems="center">
119
- <Icon
120
- glyph={Icon.ICONS.CHECK}
121
- size="small"
122
- className={css.dropdownItemIcon}
123
- />
124
- <span>{displayGroupData.label}</span>
125
- </FlexStack>
126
- </DropdownItem>
127
- );
128
- })}
129
- </Dropdown>
130
- </FlexStack>
104
+ return (
105
+ <DropdownItem
106
+ onClick={() => onSelect(displayType, displayGroupData.value)}
107
+ isActive={isGroupActive}
108
+ className={cx(css.dropdownItem, isGroupActive && css.dropdownItemActive)}
109
+ key={displayGroupData.value}
110
+ >
111
+ <FlexStack space="xxxsmall" alignItems="center">
112
+ <Icon glyph={Icon.ICONS.CHECK} size="small" className={css.dropdownItemIcon} />
113
+ <span>{displayGroupData.label}</span>
114
+ </FlexStack>
115
+ </DropdownItem>
116
+ );
117
+ })}
118
+ </Dropdown>
131
119
  );
132
120
  })}
133
121
  </ControlGroup>
@@ -4,23 +4,23 @@
4
4
  padding-bottom: var(--space-xxsmall);
5
5
  }
6
6
 
7
- .root .metric {
7
+ .root .colMetric {
8
8
  width: 55%;
9
9
  vertical-align: middle;
10
10
  }
11
11
 
12
- .root .job {
12
+ .root .colJob {
13
13
  padding-top: var(--space-xxsmall);
14
14
  text-align: right;
15
15
  }
16
16
 
17
- .root .value {
17
+ .root .colValue {
18
18
  text-align: right;
19
19
  vertical-align: baseline;
20
20
  width: 15%;
21
21
  }
22
22
 
23
- .root .delta {
23
+ .root .colDelta {
24
24
  text-align: right;
25
25
  font-size: var(--size-xsmall);
26
26
  vertical-align: baseline;
@@ -28,21 +28,17 @@
28
28
  color: pink;
29
29
  }
30
30
 
31
- .root .deltaPercentage {
31
+ .root .colDeltaPercentage {
32
32
  width: 5%;
33
33
  }
34
34
 
35
- .jobName {
36
- white-space: nowrap;
37
- }
38
-
39
35
  /* columns overrides for showHeaderSum */
40
- .showHeaderSum .job {
36
+ .showHeaderSum .colJob {
41
37
  border-bottom: 0;
42
- padding-bottom: 0;
38
+ padding-bottom: var(--space-xxxsmall);
43
39
  }
44
40
 
45
- .showHeaderSum .sum {
41
+ .showHeaderSum .colSum {
46
42
  padding-top: 0;
47
43
  font-size: var(--size-small);
48
44
  vertical-align: middle;
@@ -34,11 +34,10 @@ const JobColumn = ({ job, isBaseline }: JobColumnProps) => {
34
34
  }
35
35
 
36
36
  return (
37
- <Table.Th className={cx(css.col, css.job)} colSpan={colSpan}>
37
+ <Table.Th className={cx(css.col, css.colJob)} colSpan={colSpan}>
38
38
  <JobName
39
39
  title={isBaseline ? I18N.BASELINE_TITLE : I18N.CURRENT_TITLE}
40
40
  internalBuildNumber={internalBuildNumber}
41
- className={css.jobName}
42
41
  >
43
42
  {label}
44
43
  </JobName>
@@ -65,7 +64,7 @@ const SumColumn = ({ rows, isBaseline, runIndex, updateSort, sort }: ColumnSumPr
65
64
 
66
65
  return (
67
66
  <>
68
- <Table.Th className={cx(css.col, css.value, css.sum)}>
67
+ <Table.Th className={cx(css.col, css.colValue, css.colSum)}>
69
68
  <SortButton
70
69
  fieldPath={fieldPath}
71
70
  fieldName="value"
@@ -78,7 +77,7 @@ const SumColumn = ({ rows, isBaseline, runIndex, updateSort, sort }: ColumnSumPr
78
77
  </Table.Th>
79
78
  {!isBaseline && (
80
79
  <>
81
- <Table.Th className={cx(css.col, css.delta, css.sum)}>
80
+ <Table.Th className={cx(css.col, css.colDelta, css.colSum)}>
82
81
  <SortButton
83
82
  fieldPath={fieldPath}
84
83
  fieldName="delta"
@@ -91,7 +90,7 @@ const SumColumn = ({ rows, isBaseline, runIndex, updateSort, sort }: ColumnSumPr
91
90
  )}
92
91
  </SortButton>
93
92
  </Table.Th>
94
- <Table.Th className={cx(css.col, css.delta, css.deltaPercentage, css.sum)}>
93
+ <Table.Th className={cx(css.col, css.colDelta, css.colDeltaPercentage, css.colSum)}>
95
94
  <SortButton
96
95
  fieldPath={fieldPath}
97
96
  fieldName="deltaPercentage"
@@ -147,7 +146,7 @@ export const MetricsTableHeader = (
147
146
  return (
148
147
  <Table.THead className={rootClassName}>
149
148
  <Table.Tr>
150
- <Table.Th className={cx(css.col, css.metric)} rowSpan={showSum ? 2 : 1}>
149
+ <Table.Th className={cx(css.col, css.colMetric)} rowSpan={showSum ? 2 : 1}>
151
150
  {metricTitle || ' '}
152
151
  </Table.Th>
153
152
  {jobs.map((job, runIndex) => (
@@ -2,8 +2,7 @@ import type { ComponentProps } from 'react';
2
2
  import React from 'react';
3
3
 
4
4
  import I18N from '../../i18n';
5
- import { Dropdown, DropdownItem } from '../../ui/dropdown';
6
- import css from './metrics-table-options.module.css';
5
+ import { Dropdown, DropdownGroup, DropdownItem } from '../../ui/dropdown';
7
6
 
8
7
  type MetricsTableOptionsProps = {
9
8
  onViewAllClick?: () => void;
@@ -16,18 +15,18 @@ export const MetricsTableOptions = (props: MetricsTableOptionsProps) => {
16
15
 
17
16
  return (
18
17
  <Dropdown glyph="more-vertical" placement="bottom-end" {...restProps}>
19
- <div className={css.group}>
18
+ <DropdownGroup>
20
19
  {onResetClick && <DropdownItem onClick={onResetClick}>{I18N.RESET_FILTERS}</DropdownItem>}
21
20
  {onViewAllClick && <DropdownItem onClick={onViewAllClick}>{I18N.VIEW_ALL}</DropdownItem>}
22
- </div>
23
- <div className={css.group}>
21
+ </DropdownGroup>
22
+ <DropdownGroup>
24
23
  {onExportClick && (
25
24
  <>
26
25
  <DropdownItem onClick={() => onExportClick('csv')}>{I18N.EXPORT_CSV}</DropdownItem>
27
26
  <DropdownItem onClick={() => onExportClick('json')}>{I18N.EXPORT_JSON}</DropdownItem>
28
27
  </>
29
28
  )}
30
- </div>
29
+ </DropdownGroup>
31
30
  </Dropdown>
32
31
  );
33
32
  };
@@ -15,7 +15,7 @@ export const MetricsTableTitle = (props) => {
15
15
  const rootClassName = cx(css.root, className);
16
16
 
17
17
  return (
18
- <Stack space="none" 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
 
@@ -55,6 +55,14 @@ export type TileGroupSizeDisplay = 'minimal' | 'small' | 'default';
55
55
 
56
56
  export type TileSizeDisplay = 'minimal' | 'small' | 'default';
57
57
 
58
+ /**
59
+ * DOM marker set on every tile and tile group title (see `metrics-treemap.tsx`).
60
+ * `MetricsTreemap` uses it to resolve the hovered node from a single delegated
61
+ * pointer handler, instead of attaching hover listeners to every cell.
62
+ */
63
+ export const NODE_ID_ATTRIBUTE = 'data-treemap-id';
64
+ export const NODE_ID_SELECTOR = `[${NODE_ID_ATTRIBUTE}]`;
65
+
58
66
  export const SQUARIFY_RATIO = 1.66;
59
67
  export const PADDING_OUTER = 1;
60
68
  export const PADDING_INNER = 2;
@@ -20,6 +20,7 @@
20
20
  * Tile group
21
21
  */
22
22
  .tileGroup {
23
+ border-radius: var(--radius-xsmall);
23
24
  position: absolute;
24
25
  }
25
26
 
@@ -31,6 +32,7 @@
31
32
  .tileGroupTitleContent {
32
33
  width: 100%;
33
34
  padding: var(--space-xxxsmall) 2px var(--space-xxxsmall) var(--space-xxsmall);
35
+ border-radius: var(--radius-xsmall);
34
36
  display: flex;
35
37
  align-items: center;
36
38
  color: var(--color-text);
@@ -66,12 +68,6 @@
66
68
  .tileGroup {
67
69
  transition: var(--transition-out);
68
70
  transition-property: background-color, outline;
69
- border-radius: var(--radius-xsmall);
70
- }
71
-
72
- .tileGroup--minimal,
73
- .tileGroup--small {
74
- border-radius: calc(var(--radius-xsmall) / 2);
75
71
  }
76
72
 
77
73
  .tileGroup--minimal .tileGroupTitle,
@@ -136,9 +132,8 @@
136
132
  position: absolute;
137
133
  appearance: none;
138
134
  border: 0;
139
- border-radius: 0;
135
+ border-radius: var(--radius-xsmall);
140
136
  padding: 0;
141
- border-radius: calc(var(--radius-xsmall) / 2);
142
137
  background: var(--color-background);
143
138
  display: flex;
144
139
  align-items: center;
@@ -159,6 +154,7 @@
159
154
  top: 0;
160
155
  width: 100%;
161
156
  height: 100%;
157
+ border-radius: inherit;
162
158
  background-color: currentColor;
163
159
  }
164
160
 
@@ -170,9 +166,10 @@
170
166
  display: flex;
171
167
  flex-flow: column;
172
168
  justify-content: center;
173
- padding: var(--space-xxsmall) var(--space-xxsmall);
169
+ padding: var(--space-xxsmall);
174
170
  gap: var(--space-xxxsmall);
175
171
  line-height: var(--line-height-heading);
172
+ cursor: default;
176
173
  }
177
174
 
178
175
  /** Align label to other elements on the first column cells */
@@ -213,7 +210,7 @@
213
210
  /** Tile size variation */
214
211
  .tileSizeSmall .tileContent {
215
212
  padding: var(--space-xxxsmall);
216
- font-size: 10px;
213
+ font-size: var(--size-xsmall);
217
214
  }
218
215
 
219
216
  .tileSizeSmall .tileContentLabel {
@@ -298,11 +295,6 @@
298
295
  }
299
296
 
300
297
  /** tooltip */
301
- .tileContentTooltipAnchor {
302
- width: 100%;
303
- height: 100%;
304
- }
305
-
306
298
  .tooltip {
307
299
  z-index: var(--layer-high);
308
300
  padding: var(--space-xsmall) var(--space-small);
@@ -326,7 +318,8 @@
326
318
  font-size: var(--size-small);
327
319
  }
328
320
 
329
- /* prevent tooltip portal wrapper from capturing events */
321
+ /* prevent the tooltip portal wrapper from capturing pointer events - the
322
+ treemap relies on pointer events reaching the canvas to track the hovered cell */
330
323
  div[role='presentation']:has(.tooltip) {
331
324
  pointer-events: none;
332
325
  }