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

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 +3 -0
  2. package/lib/app/app.js.map +1 -1
  3. package/lib/app/app.module.css +11 -1
  4. package/lib/components/delta/delta.js +3 -13
  5. package/lib/components/delta/delta.js.map +1 -1
  6. package/lib/components/delta/delta.module.css +13 -12
  7. package/lib/components/delta/index.js.map +1 -1
  8. package/lib/components/index.js +3 -1
  9. package/lib/components/index.js.map +1 -1
  10. package/lib/components/insight-icon/insight-icon.module.css +6 -6
  11. package/lib/components/job-name/job-name.js +1 -2
  12. package/lib/components/job-name/job-name.js.map +1 -1
  13. package/lib/components/metric-change-legend/index.js +18 -0
  14. package/lib/components/metric-change-legend/index.js.map +1 -0
  15. package/lib/components/metric-change-legend/metric-change-legend.js +91 -0
  16. package/lib/components/metric-change-legend/metric-change-legend.js.map +1 -0
  17. package/lib/components/metric-change-legend/metric-change-legend.module.css +69 -0
  18. package/lib/components/metric-run-info/metric-run-info.js +30 -7
  19. package/lib/components/metric-run-info/metric-run-info.js.map +1 -1
  20. package/lib/components/metrics-display-selector/metrics-display-selector.js +7 -9
  21. package/lib/components/metrics-display-selector/metrics-display-selector.js.map +1 -1
  22. package/lib/components/metrics-display-selector/metrics-display-selector.module.css +0 -25
  23. package/lib/components/metrics-table-header/metrics-table-header.js +6 -6
  24. package/lib/components/metrics-table-header/metrics-table-header.js.map +1 -1
  25. package/lib/components/metrics-table-header/metrics-table-header.module.css +8 -12
  26. package/lib/components/metrics-table-options/metrics-table-options.js +1 -1
  27. package/lib/components/metrics-table-options/metrics-table-options.js.map +1 -1
  28. package/lib/components/metrics-table-title/metrics-table-title.js +1 -1
  29. package/lib/components/metrics-table-title/metrics-table-title.js.map +1 -1
  30. package/lib/components/run-info/run-info.js +26 -14
  31. package/lib/components/run-info/run-info.js.map +1 -1
  32. package/lib/components/run-info/run-info.module.css +35 -26
  33. package/lib/components/summary/summary.js +9 -10
  34. package/lib/components/summary/summary.js.map +1 -1
  35. package/lib/components/summary/summary.module.css +60 -10
  36. package/lib/constants.js +13 -1
  37. package/lib/constants.js.map +1 -1
  38. package/lib/css/variables.css +71 -58
  39. package/lib/ui/dialog/dialog.js +4 -2
  40. package/lib/ui/dialog/dialog.js.map +1 -1
  41. package/lib/ui/dialog/dialog.module.css +22 -0
  42. package/lib/ui/dropdown/dropdown.js +4 -3
  43. package/lib/ui/dropdown/dropdown.js.map +1 -1
  44. package/lib/ui/dropdown/dropdown.module.css +11 -0
  45. package/lib/ui/hover-card/hover-card.js +1 -1
  46. package/lib/ui/hover-card/hover-card.js.map +1 -1
  47. package/lib/ui/hover-card/hover-card.module.css +14 -0
  48. package/lib/ui/table/table.module.css +0 -1
  49. package/lib/ui/tooltip/tooltip.js +1 -1
  50. package/lib/ui/tooltip/tooltip.js.map +1 -1
  51. package/lib/ui/tooltip/tooltip.module.css +11 -0
  52. package/lib-esm/app/app.js +4 -1
  53. package/lib-esm/app/app.js.map +1 -1
  54. package/lib-esm/app/app.module.css +11 -1
  55. package/lib-esm/components/delta/delta.js +3 -13
  56. package/lib-esm/components/delta/delta.js.map +1 -1
  57. package/lib-esm/components/delta/delta.module.css +13 -12
  58. package/lib-esm/components/delta/index.js.map +1 -1
  59. package/lib-esm/components/index.js +1 -0
  60. package/lib-esm/components/index.js.map +1 -1
  61. package/lib-esm/components/insight-icon/insight-icon.module.css +6 -6
  62. package/lib-esm/components/job-name/job-name.js +1 -2
  63. package/lib-esm/components/job-name/job-name.js.map +1 -1
  64. package/lib-esm/components/metric-change-legend/index.js +2 -0
  65. package/lib-esm/components/metric-change-legend/index.js.map +1 -0
  66. package/lib-esm/components/metric-change-legend/metric-change-legend.js +84 -0
  67. package/lib-esm/components/metric-change-legend/metric-change-legend.js.map +1 -0
  68. package/lib-esm/components/metric-change-legend/metric-change-legend.module.css +69 -0
  69. package/lib-esm/components/metric-run-info/metric-run-info.js +31 -8
  70. package/lib-esm/components/metric-run-info/metric-run-info.js.map +1 -1
  71. package/lib-esm/components/metrics-display-selector/metrics-display-selector.js +7 -9
  72. package/lib-esm/components/metrics-display-selector/metrics-display-selector.js.map +1 -1
  73. package/lib-esm/components/metrics-display-selector/metrics-display-selector.module.css +0 -25
  74. package/lib-esm/components/metrics-table-header/metrics-table-header.js +6 -6
  75. package/lib-esm/components/metrics-table-header/metrics-table-header.js.map +1 -1
  76. package/lib-esm/components/metrics-table-header/metrics-table-header.module.css +8 -12
  77. package/lib-esm/components/metrics-table-options/metrics-table-options.js +1 -1
  78. package/lib-esm/components/metrics-table-options/metrics-table-options.js.map +1 -1
  79. package/lib-esm/components/metrics-table-title/metrics-table-title.js +1 -1
  80. package/lib-esm/components/metrics-table-title/metrics-table-title.js.map +1 -1
  81. package/lib-esm/components/run-info/run-info.js +26 -14
  82. package/lib-esm/components/run-info/run-info.js.map +1 -1
  83. package/lib-esm/components/run-info/run-info.module.css +35 -26
  84. package/lib-esm/components/summary/summary.js +10 -11
  85. package/lib-esm/components/summary/summary.js.map +1 -1
  86. package/lib-esm/components/summary/summary.module.css +60 -10
  87. package/lib-esm/constants.js +12 -0
  88. package/lib-esm/constants.js.map +1 -1
  89. package/lib-esm/css/variables.css +71 -58
  90. package/lib-esm/ui/dialog/dialog.js +4 -2
  91. package/lib-esm/ui/dialog/dialog.js.map +1 -1
  92. package/lib-esm/ui/dialog/dialog.module.css +22 -0
  93. package/lib-esm/ui/dropdown/dropdown.js +4 -3
  94. package/lib-esm/ui/dropdown/dropdown.js.map +1 -1
  95. package/lib-esm/ui/dropdown/dropdown.module.css +11 -0
  96. package/lib-esm/ui/hover-card/hover-card.js +1 -1
  97. package/lib-esm/ui/hover-card/hover-card.js.map +1 -1
  98. package/lib-esm/ui/hover-card/hover-card.module.css +14 -0
  99. package/lib-esm/ui/table/table.module.css +0 -1
  100. package/lib-esm/ui/tooltip/tooltip.js +1 -1
  101. package/lib-esm/ui/tooltip/tooltip.js.map +1 -1
  102. package/lib-esm/ui/tooltip/tooltip.module.css +11 -0
  103. package/package.json +12 -12
  104. package/src/app/app.jsx +6 -1
  105. package/src/app/app.module.css +11 -1
  106. package/src/components/delta/delta.module.css +13 -12
  107. package/src/components/delta/delta.tsx +32 -0
  108. package/src/components/index.ts +3 -0
  109. package/src/components/insight-icon/insight-icon.module.css +6 -6
  110. package/src/components/job-name/job-name.jsx +2 -3
  111. package/src/components/metric-change-legend/index.ts +1 -0
  112. package/src/components/metric-change-legend/metric-change-legend.module.css +69 -0
  113. package/src/components/metric-change-legend/metric-change-legend.stories.tsx +13 -0
  114. package/src/components/metric-change-legend/metric-change-legend.tsx +148 -0
  115. package/src/components/metric-run-info/metric-run-info.tsx +41 -17
  116. package/src/components/metrics-display-selector/metrics-display-selector.module.css +0 -25
  117. package/src/components/metrics-display-selector/metrics-display-selector.tsx +25 -37
  118. package/src/components/metrics-table-header/metrics-table-header.module.css +8 -12
  119. package/src/components/metrics-table-header/metrics-table-header.tsx +5 -6
  120. package/src/components/metrics-table-options/metrics-table-options.tsx +1 -1
  121. package/src/components/metrics-table-title/metrics-table-title.jsx +1 -1
  122. package/src/components/run-info/run-info.module.css +35 -26
  123. package/src/components/run-info/run-info.stories.tsx +16 -10
  124. package/src/components/run-info/run-info.tsx +132 -73
  125. package/src/components/summary/summary.module.css +60 -10
  126. package/src/components/summary/summary.tsx +47 -50
  127. package/src/constants.ts +13 -0
  128. package/src/css/variables.css +71 -58
  129. package/src/ui/dialog/dialog.module.css +22 -0
  130. package/src/ui/dialog/dialog.tsx +9 -2
  131. package/src/ui/dropdown/dropdown.module.css +11 -0
  132. package/src/ui/dropdown/dropdown.tsx +5 -1
  133. package/src/ui/hover-card/hover-card.module.css +14 -0
  134. package/src/ui/hover-card/hover-card.tsx +1 -1
  135. package/src/ui/table/table.module.css +0 -1
  136. package/src/ui/tooltip/tooltip.module.css +11 -0
  137. package/src/ui/tooltip/tooltip.tsx +1 -1
  138. package/types/components/delta/delta.d.ts +7 -21
  139. package/types/components/delta/index.d.ts +1 -1
  140. package/types/components/index.d.ts +3 -0
  141. package/types/components/metric-change-legend/index.d.ts +1 -0
  142. package/types/components/metric-change-legend/metric-change-legend.d.ts +4 -0
  143. package/types/components/metric-run-info/metric-run-info.d.ts +2 -6
  144. package/types/components/run-info/run-info.d.ts +16 -7
  145. package/types/constants.d.ts +12 -0
  146. package/types/ui/dialog/dialog.d.ts +3 -2
  147. package/types/ui/dropdown/dropdown.d.ts +1 -0
  148. package/src/components/delta/delta.jsx +0 -22
  149. /package/src/components/delta/{index.js → index.ts} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"tooltip.js","sourceRoot":"","sources":["../../../src/ui/tooltip/tooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EACL,OAAO,IAAI,SAAS,EACpB,aAAa,IAAI,eAAe,EAChC,YAAY,IAAI,cAAc,EAC9B,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,GAAG,MAAM,sBAAsB,CAAC;AAUvC,MAAM,CAAC,MAAM,OAAO,GAAG,CACrB,KAA6E,EAC7E,EAAE;;IACF,MAAM,EACJ,SAAS,GAAG,EAAE,EACd,gBAAgB,GAAG,EAAE,EACrB,KAAK,GAAG,EAAE,EACV,EAAE,EAAE,SAAS,GAAG,MAAM,EACtB,YAAY,EAAE,GAAG,EACjB,GAAG,SAAS,EACb,GAAG,KAAK,CAAC;IAEV,MAAM,OAAO,GAAG,eAAe,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;IAEpE,OAAO,CACL;QACE,oBAAC,eAAe,IACd,EAAE,EAAE,SAA8B,EAClC,SAAS,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,EAClC,KAAK,EAAE,OAAO,mBACC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,MAAA,OAAO,CAAC,cAAc,0CAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KACzD,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KACpB,SAAS,GACb;QACD,KAAK,IAAI,CACR,oBAAC,SAAS,IAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,gBAAgB,CAAC;YACrE,oBAAC,cAAc,IAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAI;YACjE,KAAK,CACI,CACb,CACA,CACJ,CAAC;AACJ,CAAC,CAAC"}
1
+ {"version":3,"file":"tooltip.js","sourceRoot":"","sources":["../../../src/ui/tooltip/tooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EACL,OAAO,IAAI,SAAS,EACpB,aAAa,IAAI,eAAe,EAChC,YAAY,IAAI,cAAc,EAC9B,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,GAAG,MAAM,sBAAsB,CAAC;AAUvC,MAAM,CAAC,MAAM,OAAO,GAAG,CACrB,KAA6E,EAC7E,EAAE;;IACF,MAAM,EACJ,SAAS,GAAG,EAAE,EACd,gBAAgB,GAAG,EAAE,EACrB,KAAK,GAAG,EAAE,EACV,EAAE,EAAE,SAAS,GAAG,MAAM,EACtB,YAAY,EAAE,GAAG,EACjB,GAAG,SAAS,EACb,GAAG,KAAK,CAAC;IAEV,MAAM,OAAO,GAAG,eAAe,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;IAEpF,OAAO,CACL;QACE,oBAAC,eAAe,IACd,EAAE,EAAE,SAA8B,EAClC,SAAS,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,EAClC,KAAK,EAAE,OAAO,mBACC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,MAAA,OAAO,CAAC,cAAc,0CAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KACzD,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KACpB,SAAS,GACb;QACD,KAAK,IAAI,CACR,oBAAC,SAAS,IAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,gBAAgB,CAAC;YACrE,oBAAC,cAAc,IAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAI;YACjE,KAAK,CACI,CACb,CACA,CACJ,CAAC;AACJ,CAAC,CAAC"}
@@ -21,6 +21,17 @@
21
21
  text-align: center;
22
22
  filter: drop-shadow(var(--shadow-layer));
23
23
  will-change: filter;
24
+ opacity: 0;
25
+ transition: var(--transition-out);
26
+ }
27
+
28
+ .tooltip[data-enter] {
29
+ opacity: 1;
30
+ transition: var(--transition-in);
31
+ }
32
+
33
+ .tooltip[data-leave] {
34
+ pointer-events: none;
24
35
  }
25
36
 
26
37
  .arrow svg {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bundle-stats/ui",
3
- "version": "4.22.3",
3
+ "version": "4.23.0-beta.1",
4
4
  "description": "UI components for BundleStats projects.",
5
5
  "main": "lib/index.js",
6
6
  "module": "lib-esm/index.js",
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "homepage": "https://github.com/relative-ci/bundle-stats/blob/master/packages/ui#readme",
40
40
  "devDependencies": {
41
- "@babel/preset-env": "8.0.2",
41
+ "@babel/preset-env": "8.0.5",
42
42
  "@babel/preset-react": "8.0.1",
43
43
  "@babel/preset-typescript": "8.0.1",
44
44
  "@storybook/addon-vitest": "10.5.10",
@@ -47,9 +47,9 @@
47
47
  "@types/d3": "7.4.3",
48
48
  "@types/react": "18.3.13",
49
49
  "@types/react-router-dom": "5.3.3",
50
- "@vitejs/plugin-react": "6.1.0",
51
- "@vitest/browser": "4.1.11",
52
- "@vitest/browser-playwright": "4.1.11",
50
+ "@vitejs/plugin-react": "6.1.1",
51
+ "@vitest/browser": "5.0.0",
52
+ "@vitest/browser-playwright": "5.0.0",
53
53
  "babel-plugin-polyfill-corejs3": "1.0.0",
54
54
  "babel-plugin-require-context-hook": "1.0.0",
55
55
  "classnames": "2.5.1",
@@ -58,11 +58,11 @@
58
58
  "cross-env": "10.1.0",
59
59
  "hex-rgb": "5.0.0",
60
60
  "identity-obj-proxy": "3.0.0",
61
- "jest": "30.4.2",
61
+ "jest": "^30.5.0",
62
62
  "jest-date-mock": "1.0.10",
63
- "jest-environment-jsdom": "30.4.1",
63
+ "jest-environment-jsdom": "30.5.1",
64
64
  "lodash": "4.18.1",
65
- "playwright": "1.62.1",
65
+ "playwright": "1.63.0",
66
66
  "prop-types": "15.8.1",
67
67
  "react": "18.3.1",
68
68
  "react-dom": "18.3.1",
@@ -70,9 +70,9 @@
70
70
  "rimraf": "6.1.3",
71
71
  "storybook": "10.5.10",
72
72
  "typescript": "5.9.3",
73
- "vite": "8.2.2",
73
+ "vite": "8.3.0",
74
74
  "vite-plugin-svgr": "5.2.0",
75
- "vitest": "4.1.11"
75
+ "vitest": "5.0.0"
76
76
  },
77
77
  "peerDependencies": {
78
78
  "classnames": "^2.0.0",
@@ -83,7 +83,7 @@
83
83
  "react-router-dom": "^5.0.0"
84
84
  },
85
85
  "dependencies": {
86
- "@bundle-stats/utils": "^4.22.3",
86
+ "@bundle-stats/utils": "^4.23.0-beta.1",
87
87
  "ariakit": "2.0.0-next.44",
88
88
  "d3": "7.9.0",
89
89
  "modern-css-reset": "1.4.0",
@@ -91,5 +91,5 @@
91
91
  "snarkdown": "2.0.0",
92
92
  "use-query-params": "2.2.2"
93
93
  },
94
- "gitHead": "40d689d101ffc69421f87e29fb7449b4f1ae8bff"
94
+ "gitHead": "5ed30de2bfdfe488b8a5f3490b11d1429d89b6ad"
95
95
  }
package/src/app/app.jsx CHANGED
@@ -10,7 +10,7 @@ import { BundleAssetsTotals } from '../components/bundle-assets-totals';
10
10
  import { BundleModules } from '../components/bundle-modules';
11
11
  import { BundlePackages } from '../components/bundle-packages';
12
12
  import { Insights } from '../components/insights';
13
- import { MetricsTableTitle } from '../components';
13
+ import { MetricChangeLegend, MetricsTableTitle } from '../components';
14
14
  import { Summary } from '../components/summary';
15
15
  import { Box } from '../layout/box';
16
16
  import { Footer } from '../layout/footer';
@@ -150,6 +150,11 @@ const AppComponent = ({ version, jobs }) => {
150
150
  return (
151
151
  <JobsProvider jobs={jobs}>
152
152
  <Layout jobs={jobs} version={version}>
153
+ <Container className={css.metricChangeLegendContainer}>
154
+ <div className={css.metricChangeLegendContainerInner}>
155
+ <MetricChangeLegend />
156
+ </div>
157
+ </Container>
153
158
  <Container className={css.summaryContainer}>
154
159
  <Summary
155
160
  data={jobs[0].summary}
@@ -17,11 +17,21 @@
17
17
  border-bottom: 1px solid var(--color-outline);
18
18
  }
19
19
 
20
- .summaryContainer {
20
+ .metricChangeLegendContainer {
21
21
  padding-top: var(--space-small);
22
22
  padding-bottom: var(--space-xxsmall);
23
23
  }
24
24
 
25
+ .metricChangeLegendContainerInner {
26
+ display: flex;
27
+ justify-content: flex-end;
28
+ }
29
+
30
+ .summaryContainer {
31
+ padding-top: var(--space-xxsmall);
32
+ padding-bottom: var(--space-xxsmall);
33
+ }
34
+
25
35
  .tabsContainer {
26
36
  border-bottom: 1px solid var(--color-outline);
27
37
  background: var(--color-light);
@@ -8,16 +8,16 @@
8
8
  color: var(--color-info);
9
9
  }
10
10
 
11
- .HIGH_NEGATIVE {
12
- color: var(--color-red-dark);
11
+ .LOW_NEGATIVE {
12
+ color: var(--color-red-light);
13
13
  }
14
14
 
15
15
  .NEGATIVE {
16
- color: var(--color-red-dark);
16
+ color: var(--color-red);
17
17
  }
18
18
 
19
- .LOW_NEGATIVE {
20
- color: var(--color-red-light);
19
+ .HIGH_NEGATIVE {
20
+ color: var(--color-red-dark);
21
21
  }
22
22
 
23
23
  .LOW_POSITIVE {
@@ -25,7 +25,7 @@
25
25
  }
26
26
 
27
27
  .POSITIVE {
28
- color: var(--color-green-dark);
28
+ color: var(--color-green);
29
29
  }
30
30
 
31
31
  .HIGH_POSITIVE {
@@ -43,24 +43,25 @@
43
43
  background: var(--color-info-light);
44
44
  }
45
45
 
46
- .inverted.HIGH_NEGATIVE {
47
- background: var(--color-red-dark);
46
+ .inverted.LOW_NEGATIVE {
47
+ background: var(--color-red-light);
48
48
  }
49
49
 
50
50
  .inverted.NEGATIVE {
51
- background: var(--color-red-dark);
51
+ background: var(--color-red);
52
52
  }
53
53
 
54
- .inverted.LOW_NEGATIVE {
55
- background: var(--color-red-light);
54
+ .inverted.HIGH_NEGATIVE {
55
+ background: var(--color-red-dark);
56
56
  }
57
57
 
58
+
58
59
  .inverted.LOW_POSITIVE {
59
60
  background: var(--color-green-light);
60
61
  }
61
62
 
62
63
  .inverted.POSITIVE {
63
- background: var(--color-green-dark);
64
+ background: var(--color-green);
64
65
  }
65
66
 
66
67
  .inverted.HIGH_POSITIVE {
@@ -0,0 +1,32 @@
1
+ import React from 'react';
2
+ import cx from 'classnames';
3
+ import type { MetricRunInfoDeltaType } from '@bundle-stats/utils';
4
+
5
+ import css from './delta.module.css';
6
+
7
+ export interface DeltaProps {
8
+ /**
9
+ * Formatted delta value
10
+ */
11
+ displayValue: string;
12
+ /**
13
+ * Delta type - drives the color scale
14
+ */
15
+ deltaType: MetricRunInfoDeltaType;
16
+ /**
17
+ * Inverted variant - render the delta type as background color
18
+ */
19
+ inverted?: boolean;
20
+ }
21
+
22
+ export const Delta = (props: DeltaProps & React.ComponentProps<'span'>) => {
23
+ const { className = '', displayValue, deltaType, inverted = false, ...restProps } = props;
24
+
25
+ const rootClassName = cx(css.root, className, inverted && css.inverted, css[deltaType]);
26
+
27
+ return (
28
+ <span className={rootClassName} {...restProps}>
29
+ {displayValue}
30
+ </span>
31
+ );
32
+ };
@@ -5,12 +5,15 @@ export { BundlePackages } from './bundle-packages';
5
5
  export { BundleModules } from './bundle-modules';
6
6
  export { ComponentLink } from './component-link';
7
7
  export { Delta } from './delta';
8
+ export type { DeltaProps } from './delta';
8
9
  export { InsightIcon } from './insight-icon';
9
10
  export { Insights } from './insights';
10
11
  export { JobName } from './job-name';
11
12
  export { JobsHeader } from './jobs-header';
12
13
  export { LighthouseTable } from './lighthouse-table';
13
14
  export { Metric } from './metric';
15
+ export { MetricChangeLegend } from './metric-change-legend';
16
+ export type { MetricChangeLegendProps } from './metric-change-legend';
14
17
  export { MetricRunInfo } from './metric-run-info';
15
18
  export type { MetricRunInfoProps } from './metric-run-info';
16
19
  export { MetricsTable } from './metrics-table';
@@ -1,20 +1,20 @@
1
1
  .root {
2
- width: calc(var(--space-small) + 6px);
3
- height: calc(var(--space-small) + 6px);
4
- padding: 3px;
2
+ width: calc(var(--space-small) + var(--space-xxsmall));
3
+ height: calc(var(--space-small) + var(--space-xxsmall));
4
+ padding: var(--space-xxxsmall);
5
5
  border-radius: 50%;
6
6
  background: var(--color-highlight);
7
7
  }
8
8
 
9
9
  .error {
10
- background: var(--color-red-50);
10
+ background: var(--color-highlight-danger);
11
11
  color: var(--color-danger);
12
12
  }
13
13
 
14
14
  .warning {
15
15
  padding-top: 1px;
16
- background: var(--color-yellow-100);
17
- color: var(--color-yellow-900);
16
+ background: var(--color-highlight-warning);
17
+ color: var(--color-warning);
18
18
  }
19
19
 
20
20
  .info {
@@ -1,7 +1,6 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
 
4
- import { Tooltip } from '../../ui/tooltip';
5
4
  import css from './job-name.module.css';
6
5
 
7
6
  export const JobName = ({
@@ -11,9 +10,9 @@ export const JobName = ({
11
10
  children,
12
11
  ...restProps
13
12
  }) => (
14
- <Tooltip className={css.root} as={Component} title={title} {...restProps}>
13
+ <Component className={css.root} title={title} {...restProps}>
15
14
  {children || `Job #${internalBuildNumber}`}
16
- </Tooltip>
15
+ </Component>
17
16
  );
18
17
 
19
18
  JobName.defaultProps = {
@@ -0,0 +1 @@
1
+ export * from './metric-change-legend';
@@ -0,0 +1,69 @@
1
+ .root {
2
+ font-size: var(--size-small);
3
+ }
4
+
5
+ .itemAnchor {
6
+ color: var(--color-text-light);
7
+ }
8
+
9
+ .itemAnchor:hover {
10
+ color: var(--color-text);
11
+ }
12
+
13
+ .variants {
14
+ display: flex;
15
+ gap: 1px;
16
+ overflow: hidden;
17
+ border-radius: var(--radius-xsmall);
18
+ }
19
+
20
+ .infoPreviewCell {
21
+ width: 1%;
22
+ white-space: nowrap;
23
+ }
24
+
25
+ .infoDescriptionCell {
26
+ padding-left: 0 !important;
27
+ }
28
+
29
+ .variant {
30
+ width: var(--space-xxsmall);
31
+ height: var(--space-xxsmall);
32
+ }
33
+
34
+ .large .variant {
35
+ width: var(--space-small);
36
+ height: var(--space-small);
37
+ }
38
+
39
+ .bar-improvement .variant-low {
40
+ background: var(--color-green-light);
41
+ }
42
+
43
+ .bar-improvement .variant-medium {
44
+ background: var(--color-green);
45
+ }
46
+
47
+ .bar-improvement .variant-high {
48
+ background: var(--color-green-dark);
49
+ }
50
+
51
+ .bar-regression .variant-low {
52
+ background: var(--color-red-light);
53
+ }
54
+
55
+ .bar-regression .variant-medium {
56
+ background: var(--color-red);
57
+ }
58
+
59
+ .bar-regression .variant-high {
60
+ background: var(--color-red-dark);
61
+ }
62
+
63
+ .bar-neutral .variant-medium {
64
+ background: var(--color-info);
65
+ }
66
+
67
+ .bar-no-change .variant-medium {
68
+ background: var(--color-gray-ultra-light);
69
+ }
@@ -0,0 +1,13 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+
3
+ import { MetricChangeLegend } from '.';
4
+
5
+ const meta = {
6
+ title: 'Components/MetricChangeLegend',
7
+ component: MetricChangeLegend,
8
+ } satisfies Meta<typeof MetricChangeLegend>;
9
+
10
+ export default meta;
11
+ type Story = StoryObj<typeof meta>;
12
+
13
+ export const Default: Story = {};
@@ -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} />
139
+ <span>{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,13 @@ 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
+ } & Pick<RunInfoProps, 'showDelta' | 'showBaseline' | 'size' | 'loading'>;
64
60
 
65
61
  export const MetricRunInfo = (props: MetricRunInfoProps & React.ComponentProps<'div'>) => {
66
62
  const {
@@ -92,16 +88,47 @@ export const MetricRunInfo = (props: MetricRunInfoProps & React.ComponentProps<'
92
88
  [CustomTitleWrapper, metric.label],
93
89
  );
94
90
 
91
+ const baselineProps = useMemo(() => {
92
+ if (showBaseline) {
93
+ return {
94
+ showBaseline,
95
+ baseline: metric.formatter(baseline),
96
+ };
97
+ }
98
+
99
+ return { showBaseline };
100
+ }, [showBaseline, baseline]);
101
+
95
102
  const deltaProps = useMemo(() => {
96
- if (!showDelta || metric.skipDelta || !('delta' in metricRunInfo)) {
97
- return {};
103
+ if (!showDelta || metric.skipDelta) {
104
+ return {
105
+ showDelta: false,
106
+ };
107
+ }
108
+
109
+ // If the delta data is missing, do not render the dats
110
+ if (!('delta' in metricRunInfo)) {
111
+ return {
112
+ showDelta: false,
113
+ };
114
+ }
115
+
116
+ // For file sizes, show the real data
117
+ if (metric.type === MetricTypes.FileSize) {
118
+ return {
119
+ showDelta: true,
120
+ delta: metricRunInfo.displayDelta,
121
+ deltaType: metricRunInfo.deltaType,
122
+ deltaPercentage: metricRunInfo.displayDeltaPercentage,
123
+ };
98
124
  }
99
125
 
100
126
  return {
101
- delta: metricRunInfo.displayDeltaPercentage,
127
+ showDelta: true,
128
+ delta: metricRunInfo.displayDelta,
102
129
  deltaType: metricRunInfo.deltaType,
103
130
  };
104
- }, [metricRunInfo]);
131
+ }, [metric, metricRunInfo, showDelta]);
105
132
 
106
133
  return (
107
134
  <RunInfo
@@ -109,10 +136,7 @@ export const MetricRunInfo = (props: MetricRunInfoProps & React.ComponentProps<'
109
136
  titleHoverCard={titleHoverCard}
110
137
  enhance
111
138
  current={metricRunInfo.displayValue}
112
- showBaseline={showBaseline}
113
- {...(showBaseline && {
114
- baseline: metric.formatter(baseline),
115
- })}
139
+ {...baselineProps}
116
140
  {...deltaProps}
117
141
  {...restProps}
118
142
  />
@@ -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);