@bundle-stats/ui 4.22.2 → 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 (227) hide show
  1. package/.babelrc.js +65 -24
  2. package/globals.d.ts +13 -0
  3. package/lib/app/app.js +3 -0
  4. package/lib/app/app.js.map +1 -1
  5. package/lib/app/app.module.css +11 -1
  6. package/lib/components/bundle-modules/index.js.map +1 -1
  7. package/lib/components/delta/delta.js +3 -13
  8. package/lib/components/delta/delta.js.map +1 -1
  9. package/lib/components/delta/delta.module.css +13 -12
  10. package/lib/components/delta/index.js.map +1 -1
  11. package/lib/components/index.js +3 -1
  12. package/lib/components/index.js.map +1 -1
  13. package/lib/components/insight-icon/insight-icon.module.css +6 -6
  14. package/lib/components/job-name/job-name.js +1 -2
  15. package/lib/components/job-name/job-name.js.map +1 -1
  16. package/lib/components/metric-change-legend/index.js +18 -0
  17. package/lib/components/metric-change-legend/index.js.map +1 -0
  18. package/lib/components/metric-change-legend/metric-change-legend.js +91 -0
  19. package/lib/components/metric-change-legend/metric-change-legend.js.map +1 -0
  20. package/lib/components/metric-change-legend/metric-change-legend.module.css +69 -0
  21. package/lib/components/metric-run-info/metric-run-info.js +30 -7
  22. package/lib/components/metric-run-info/metric-run-info.js.map +1 -1
  23. package/lib/components/metrics-display-selector/metrics-display-selector.js +7 -9
  24. package/lib/components/metrics-display-selector/metrics-display-selector.js.map +1 -1
  25. package/lib/components/metrics-display-selector/metrics-display-selector.module.css +0 -25
  26. package/lib/components/metrics-table-header/metrics-table-header.js +8 -8
  27. package/lib/components/metrics-table-header/metrics-table-header.js.map +1 -1
  28. package/lib/components/metrics-table-header/metrics-table-header.module.css +8 -12
  29. package/lib/components/metrics-table-options/metrics-table-options.js +1 -1
  30. package/lib/components/metrics-table-options/metrics-table-options.js.map +1 -1
  31. package/lib/components/metrics-table-title/metrics-table-title.js +1 -1
  32. package/lib/components/metrics-table-title/metrics-table-title.js.map +1 -1
  33. package/lib/components/run-info/run-info.js +26 -14
  34. package/lib/components/run-info/run-info.js.map +1 -1
  35. package/lib/components/run-info/run-info.module.css +35 -26
  36. package/lib/components/summary/summary.js +9 -10
  37. package/lib/components/summary/summary.js.map +1 -1
  38. package/lib/components/summary/summary.module.css +60 -10
  39. package/lib/constants.js +13 -1
  40. package/lib/constants.js.map +1 -1
  41. package/lib/css/default.css +1 -1
  42. package/lib/css/variables.css +71 -58
  43. package/lib/hooks/rows-sort.js +13 -12
  44. package/lib/hooks/rows-sort.js.map +1 -1
  45. package/lib/ui/button/button.js.map +1 -1
  46. package/lib/ui/dialog/dialog.js +4 -2
  47. package/lib/ui/dialog/dialog.js.map +1 -1
  48. package/lib/ui/dialog/dialog.module.css +22 -0
  49. package/lib/ui/dropdown/dropdown.js +4 -3
  50. package/lib/ui/dropdown/dropdown.js.map +1 -1
  51. package/lib/ui/dropdown/dropdown.module.css +11 -0
  52. package/lib/ui/empty-set/empty-set.js +3 -3
  53. package/lib/ui/empty-set/empty-set.js.map +1 -1
  54. package/lib/ui/hover-card/hover-card.js +1 -1
  55. package/lib/ui/hover-card/hover-card.js.map +1 -1
  56. package/lib/ui/hover-card/hover-card.module.css +14 -0
  57. package/lib/ui/table/table.module.css +0 -1
  58. package/lib/ui/tooltip/tooltip.js +1 -1
  59. package/lib/ui/tooltip/tooltip.js.map +1 -1
  60. package/lib/ui/tooltip/tooltip.module.css +11 -0
  61. package/lib-esm/app/app.js +4 -1
  62. package/lib-esm/app/app.js.map +1 -1
  63. package/lib-esm/app/app.module.css +11 -1
  64. package/lib-esm/components/bundle-modules/index.js.map +1 -1
  65. package/lib-esm/components/delta/delta.js +3 -13
  66. package/lib-esm/components/delta/delta.js.map +1 -1
  67. package/lib-esm/components/delta/delta.module.css +13 -12
  68. package/lib-esm/components/delta/index.js.map +1 -1
  69. package/lib-esm/components/index.js +1 -0
  70. package/lib-esm/components/index.js.map +1 -1
  71. package/lib-esm/components/insight-icon/insight-icon.module.css +6 -6
  72. package/lib-esm/components/job-name/job-name.js +1 -2
  73. package/lib-esm/components/job-name/job-name.js.map +1 -1
  74. package/lib-esm/components/metric-change-legend/index.js +2 -0
  75. package/lib-esm/components/metric-change-legend/index.js.map +1 -0
  76. package/lib-esm/components/metric-change-legend/metric-change-legend.js +84 -0
  77. package/lib-esm/components/metric-change-legend/metric-change-legend.js.map +1 -0
  78. package/lib-esm/components/metric-change-legend/metric-change-legend.module.css +69 -0
  79. package/lib-esm/components/metric-run-info/metric-run-info.js +31 -8
  80. package/lib-esm/components/metric-run-info/metric-run-info.js.map +1 -1
  81. package/lib-esm/components/metrics-display-selector/metrics-display-selector.js +7 -9
  82. package/lib-esm/components/metrics-display-selector/metrics-display-selector.js.map +1 -1
  83. package/lib-esm/components/metrics-display-selector/metrics-display-selector.module.css +0 -25
  84. package/lib-esm/components/metrics-table-header/metrics-table-header.js +8 -8
  85. package/lib-esm/components/metrics-table-header/metrics-table-header.js.map +1 -1
  86. package/lib-esm/components/metrics-table-header/metrics-table-header.module.css +8 -12
  87. package/lib-esm/components/metrics-table-options/metrics-table-options.js +1 -1
  88. package/lib-esm/components/metrics-table-options/metrics-table-options.js.map +1 -1
  89. package/lib-esm/components/metrics-table-title/metrics-table-title.js +1 -1
  90. package/lib-esm/components/metrics-table-title/metrics-table-title.js.map +1 -1
  91. package/lib-esm/components/run-info/run-info.js +26 -14
  92. package/lib-esm/components/run-info/run-info.js.map +1 -1
  93. package/lib-esm/components/run-info/run-info.module.css +35 -26
  94. package/lib-esm/components/summary/summary.js +10 -11
  95. package/lib-esm/components/summary/summary.js.map +1 -1
  96. package/lib-esm/components/summary/summary.module.css +60 -10
  97. package/lib-esm/constants.js +12 -0
  98. package/lib-esm/constants.js.map +1 -1
  99. package/lib-esm/css/default.css +1 -1
  100. package/lib-esm/css/variables.css +71 -58
  101. package/lib-esm/hooks/rows-sort.js +10 -8
  102. package/lib-esm/hooks/rows-sort.js.map +1 -1
  103. package/lib-esm/ui/button/button.js.map +1 -1
  104. package/lib-esm/ui/dialog/dialog.js +4 -2
  105. package/lib-esm/ui/dialog/dialog.js.map +1 -1
  106. package/lib-esm/ui/dialog/dialog.module.css +22 -0
  107. package/lib-esm/ui/dropdown/dropdown.js +4 -3
  108. package/lib-esm/ui/dropdown/dropdown.js.map +1 -1
  109. package/lib-esm/ui/dropdown/dropdown.module.css +11 -0
  110. package/lib-esm/ui/empty-set/empty-set.js +3 -3
  111. package/lib-esm/ui/empty-set/empty-set.js.map +1 -1
  112. package/lib-esm/ui/hover-card/hover-card.js +1 -1
  113. package/lib-esm/ui/hover-card/hover-card.js.map +1 -1
  114. package/lib-esm/ui/hover-card/hover-card.module.css +14 -0
  115. package/lib-esm/ui/table/table.module.css +0 -1
  116. package/lib-esm/ui/tooltip/tooltip.js +1 -1
  117. package/lib-esm/ui/tooltip/tooltip.js.map +1 -1
  118. package/lib-esm/ui/tooltip/tooltip.module.css +11 -0
  119. package/package.json +26 -15
  120. package/src/app/app.jsx +6 -1
  121. package/src/app/app.module.css +11 -1
  122. package/src/app/app.stories.tsx +2 -2
  123. package/src/components/asset-info/asset-info.stories.tsx +47 -39
  124. package/src/components/asset-name/asset-name.stories.tsx +1 -3
  125. package/src/components/asset-not-predictive/asset-not-predictive.stories.tsx +22 -15
  126. package/src/components/browsertime-table/browsertime-table.stories.tsx +45 -0
  127. package/src/components/bundle-assets-totals/bundle-assets-totals.stories.tsx +47 -0
  128. package/src/components/bundle-modules/bundle-modules.stories.tsx +191 -0
  129. package/src/components/bundle-modules/bundle-modules.tsx +1 -1
  130. package/src/components/bundle-modules/index.tsx +4 -0
  131. package/src/components/bundle-packages/bundle-packages.stories.tsx +87 -0
  132. package/src/components/delta/delta.module.css +13 -12
  133. package/src/components/delta/delta.stories.tsx +57 -0
  134. package/src/components/delta/delta.tsx +32 -0
  135. package/src/components/entry-info/entry-info.stories.tsx +49 -54
  136. package/src/components/{index.js → index.ts} +7 -2
  137. package/src/components/insight-icon/insight-icon.module.css +6 -6
  138. package/src/components/insights/insights.stories.tsx +36 -33
  139. package/src/components/job-name/job-name.jsx +2 -3
  140. package/src/components/job-name/job-name.stories.tsx +46 -0
  141. package/src/components/jobs-header/jobs-header.stories.tsx +19 -15
  142. package/src/components/lighthouse-table/lighthouse-table.stories.tsx +30 -0
  143. package/src/components/metric/metric.stories.tsx +24 -22
  144. package/src/components/metric-change-legend/index.ts +1 -0
  145. package/src/components/metric-change-legend/metric-change-legend.module.css +69 -0
  146. package/src/components/metric-change-legend/metric-change-legend.stories.tsx +13 -0
  147. package/src/components/metric-change-legend/metric-change-legend.tsx +148 -0
  148. package/src/components/metric-run-info/metric-run-info.stories.tsx +45 -41
  149. package/src/components/metric-run-info/metric-run-info.tsx +41 -17
  150. package/src/components/metrics-display-selector/metrics-display-selector.module.css +0 -25
  151. package/src/components/metrics-display-selector/metrics-display-selector.tsx +25 -37
  152. package/src/components/metrics-table/metrics-table.stories.tsx +33 -13
  153. package/src/components/metrics-table/metrics-table.tsx +1 -1
  154. package/src/components/metrics-table-header/metrics-table-header.module.css +8 -12
  155. package/src/components/metrics-table-header/metrics-table-header.tsx +14 -10
  156. package/src/components/metrics-table-options/metrics-table-options.tsx +1 -1
  157. package/src/components/metrics-table-title/metrics-table-title.jsx +1 -1
  158. package/src/components/module-info/module-info.stories.tsx +41 -30
  159. package/src/components/package-info/package-info.stories.tsx +20 -15
  160. package/src/components/run-info/run-info.module.css +35 -26
  161. package/src/components/run-info/run-info.stories.tsx +47 -39
  162. package/src/components/run-info/run-info.tsx +132 -73
  163. package/src/components/summary/summary.module.css +60 -10
  164. package/src/components/summary/summary.stories.tsx +26 -32
  165. package/src/components/summary/summary.tsx +47 -50
  166. package/src/constants.ts +13 -0
  167. package/src/css/default.css +1 -1
  168. package/src/css/variables.css +71 -58
  169. package/src/hooks/rows-sort.ts +9 -6
  170. package/src/layout/box/box.stories.tsx +35 -28
  171. package/src/layout/flex-stack/flex-stack.stories.tsx +79 -48
  172. package/src/layout/footer/footer.stories.tsx +30 -0
  173. package/src/layout/stack/stack.stories.tsx +68 -44
  174. package/src/ui/alert/alert.stories.tsx +25 -21
  175. package/src/ui/button/button.stories.tsx +181 -176
  176. package/src/ui/button/button.tsx +8 -1
  177. package/src/ui/container/container.stories.tsx +28 -21
  178. package/src/ui/control-group/control-group.stories.tsx +23 -16
  179. package/src/ui/dialog/dialog.module.css +22 -0
  180. package/src/ui/dialog/dialog.tsx +9 -2
  181. package/src/ui/dropdown/dropdown.module.css +11 -0
  182. package/src/ui/dropdown/dropdown.stories.tsx +23 -15
  183. package/src/ui/dropdown/dropdown.tsx +5 -1
  184. package/src/ui/empty-set/empty-set.jsx +16 -4
  185. package/src/ui/empty-set/empty-set.stories.tsx +41 -0
  186. package/src/ui/file-name/file-name.stories.tsx +45 -0
  187. package/src/ui/filters/filters.stories.tsx +29 -26
  188. package/src/ui/hover-card/hover-card.module.css +14 -0
  189. package/src/ui/hover-card/hover-card.stories.tsx +35 -26
  190. package/src/ui/hover-card/hover-card.tsx +1 -1
  191. package/src/ui/icon/icon.stories.tsx +36 -28
  192. package/src/ui/input/input.stories.tsx +17 -5
  193. package/src/ui/input-search/input-search.stories.tsx +31 -0
  194. package/src/ui/loader/loader.stories.tsx +22 -0
  195. package/src/ui/skeleton/skeleton.stories.tsx +42 -0
  196. package/src/ui/table/table.module.css +0 -1
  197. package/src/ui/table/table.stories.tsx +244 -218
  198. package/src/ui/tag/tag.stories.tsx +11 -10
  199. package/src/ui/toolbar/toolbar.stories.tsx +41 -0
  200. package/src/ui/tooltip/tooltip.module.css +11 -0
  201. package/src/ui/tooltip/tooltip.tsx +1 -1
  202. package/types/components/bundle-modules/bundle-modules.d.ts +1 -1
  203. package/types/components/bundle-modules/index.d.ts +2 -1
  204. package/types/components/delta/delta.d.ts +7 -21
  205. package/types/components/delta/index.d.ts +1 -1
  206. package/types/components/index.d.ts +25 -20
  207. package/types/components/metric-change-legend/index.d.ts +1 -0
  208. package/types/components/metric-change-legend/metric-change-legend.d.ts +4 -0
  209. package/types/components/metric-run-info/metric-run-info.d.ts +2 -6
  210. package/types/components/metrics-table/metrics-table.d.ts +1 -1
  211. package/types/components/metrics-table-header/metrics-table-header.d.ts +1 -1
  212. package/types/components/run-info/run-info.d.ts +16 -7
  213. package/types/constants.d.ts +12 -0
  214. package/types/hooks/rows-sort.d.ts +2 -2
  215. package/types/ui/button/button.d.ts +1 -1
  216. package/types/ui/dialog/dialog.d.ts +3 -2
  217. package/types/ui/dropdown/dropdown.d.ts +1 -0
  218. package/vitest.config.ts +47 -0
  219. package/vitest.setup.ts +26 -0
  220. package/lib/assets/icons.svg.js +0 -100
  221. package/lib/assets/icons.svg.js.map +0 -1
  222. package/lib-esm/assets/icons.svg.js +0 -63
  223. package/lib-esm/assets/icons.svg.js.map +0 -1
  224. package/src/assets/icons.svg.jsx +0 -279
  225. package/src/components/delta/delta.jsx +0 -22
  226. package/types/assets/icons.svg.d.ts +0 -2
  227. /package/src/components/delta/{index.js → index.ts} +0 -0
@@ -1,53 +1,56 @@
1
- import React from 'react';
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { InsightType } from '@bundle-stats/utils';
2
3
 
3
4
  import { getWrapperDecorator } from '../../stories';
4
5
  import { Insights } from '.';
5
6
 
6
- export default {
7
+ const meta = {
7
8
  title: 'Components/Insights',
8
9
  component: Insights,
9
10
  decorators: [getWrapperDecorator()],
10
- };
11
+ } satisfies Meta<typeof Insights>;
11
12
 
12
- const Template = (props: any) => <Insights {...props} />;
13
+ export default meta;
13
14
 
14
- export const Default = Template.bind({});
15
+ type Story = StoryObj<typeof meta>;
15
16
 
16
- Default.args = {
17
- insights: {
18
- duplicatePackages: {
19
- type: 'error',
20
- data: {
21
- packages: { 'package-a': ['package-a', 'package-a~1'] },
22
- text: 'Bundle introduced 1 and removed 2 duplicate packages',
17
+ export const Default: Story = {
18
+ args: {
19
+ insights: {
20
+ duplicatePackages: {
21
+ type: InsightType.ERROR,
22
+ data: {
23
+ packages: { 'package-a': ['package-a', 'package-a~1'] },
24
+ text: 'Bundle introduced 1 and removed 2 duplicate packages',
25
+ },
23
26
  },
24
- },
25
- newPackages: {
26
- type: 'warning',
27
- data: {
28
- text: 'Bundle introduced 2 new packages: package-c, package-d',
29
- packages: ['package-c', 'package-d'],
27
+ newPackages: {
28
+ type: InsightType.WARNING,
29
+ data: {
30
+ text: 'Bundle introduced 2 new packages: package-c, package-d',
31
+ packages: ['package-c', 'package-d'],
32
+ },
30
33
  },
31
34
  },
32
35
  },
33
36
  };
34
37
 
35
- export const Info = Template.bind({});
36
-
37
- Info.args = {
38
- insights: {
39
- duplicatePackages: {
40
- type: 'info',
41
- data: {
42
- packages: { 'package-a': ['package-a', 'package-a~1'] },
43
- text: 'Bundle removed 1 duplicate packages',
38
+ export const Info: Story = {
39
+ args: {
40
+ insights: {
41
+ duplicatePackages: {
42
+ type: InsightType.INFO,
43
+ data: {
44
+ packages: { 'package-a': ['package-a', 'package-a~1'] },
45
+ text: 'Bundle removed 1 duplicate packages',
46
+ },
44
47
  },
45
- },
46
- newPackages: {
47
- type: 'warning',
48
- data: {
49
- text: 'Bundle introduced 2 new packages: package-c, package-d',
50
- packages: ['package-c', 'package-d'],
48
+ newPackages: {
49
+ type: InsightType.WARNING,
50
+ data: {
51
+ text: 'Bundle introduced 2 new packages: package-c, package-d',
52
+ packages: ['package-c', 'package-d'],
53
+ },
51
54
  },
52
55
  },
53
56
  },
@@ -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,46 @@
1
+ import React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+
4
+ import { getWrapperDecorator } from '../../stories';
5
+ import { JobName } from '.';
6
+
7
+ // JobName is a plain .jsx component with defaultProps instead of inline
8
+ // destructuring defaults, so TS can't infer its props as optional on its own.
9
+ const TypedJobName = JobName as unknown as React.ComponentType<{
10
+ as?: React.ElementType;
11
+ title?: string;
12
+ internalBuildNumber?: number;
13
+ children?: React.ReactNode;
14
+ }>;
15
+
16
+ const meta = {
17
+ title: 'Components/JobName',
18
+ component: TypedJobName,
19
+ decorators: [getWrapperDecorator({ padding: '64px' })],
20
+ args: {
21
+ title: 'View job details',
22
+ },
23
+ } satisfies Meta<typeof TypedJobName>;
24
+
25
+ export default meta;
26
+
27
+ type Story = StoryObj<typeof meta>;
28
+
29
+ export const Default: Story = {
30
+ args: {
31
+ internalBuildNumber: 10,
32
+ },
33
+ };
34
+
35
+ export const CustomComponent: Story = {
36
+ args: {
37
+ internalBuildNumber: 10,
38
+ as: 'strong',
39
+ },
40
+ };
41
+
42
+ export const Render: Story = {
43
+ args: {
44
+ children: <a href="https://relative-ci.com/test">#10</a>,
45
+ },
46
+ };
@@ -1,17 +1,21 @@
1
- import React from 'react';
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
2
 
3
3
  import { getWrapperDecorator } from '../../stories';
4
4
  import { JobsHeader } from '.';
5
5
 
6
- export default {
6
+ const meta = {
7
7
  title: 'Components/JobsHeader',
8
8
  component: JobsHeader,
9
9
  decorators: [getWrapperDecorator()],
10
- };
10
+ } satisfies Meta<typeof JobsHeader>;
11
+
12
+ export default meta;
13
+
14
+ type Story = StoryObj<typeof meta>;
11
15
 
12
- export const Default = () => (
13
- <JobsHeader
14
- jobs={[
16
+ export const Default: Story = {
17
+ args: {
18
+ jobs: [
15
19
  {
16
20
  internalBuildNumber: 1,
17
21
  meta: {
@@ -21,13 +25,13 @@ export const Default = () => (
21
25
  },
22
26
  },
23
27
  },
24
- ]}
25
- />
26
- );
28
+ ],
29
+ },
30
+ };
27
31
 
28
- export const MultipleJobs = () => (
29
- <JobsHeader
30
- jobs={[
32
+ export const MultipleJobs: Story = {
33
+ args: {
34
+ jobs: [
31
35
  {
32
36
  internalBuildNumber: 1,
33
37
  meta: {
@@ -46,6 +50,6 @@ export const MultipleJobs = () => (
46
50
  },
47
51
  },
48
52
  },
49
- ]}
50
- />
51
- );
53
+ ],
54
+ },
55
+ };
@@ -0,0 +1,30 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { createJobs } from '@bundle-stats/utils';
3
+
4
+ import lighthouse from '../../../__mocks__/lighthouse.json';
5
+ import { getWrapperDecorator } from '../../stories';
6
+ import { LighthouseTable } from '.';
7
+
8
+ const JOBS = createJobs([{ lighthouse }, { lighthouse }]);
9
+
10
+ const meta = {
11
+ title: 'Components/LighthouseTable',
12
+ component: LighthouseTable,
13
+ decorators: [getWrapperDecorator()],
14
+ } satisfies Meta<typeof LighthouseTable>;
15
+
16
+ export default meta;
17
+
18
+ type Story = StoryObj<typeof meta>;
19
+
20
+ export const Default: Story = {
21
+ args: {
22
+ jobs: JOBS,
23
+ },
24
+ };
25
+
26
+ export const NoBaseline: Story = {
27
+ args: {
28
+ jobs: [JOBS[1]],
29
+ },
30
+ };
@@ -1,41 +1,43 @@
1
1
  import React from 'react';
2
- import { Meta, Story } from '@storybook/react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
3
 
4
4
  import { getWrapperDecorator } from '../../stories';
5
5
  import { Delta } from '../delta';
6
6
  import { Metric } from '.';
7
7
 
8
- export default {
8
+ const meta = {
9
9
  title: 'Components/Metric',
10
10
  component: Metric,
11
11
  decorators: [getWrapperDecorator({ maxWidth: '200px' })],
12
- } as Meta;
12
+ } satisfies Meta<typeof Metric>;
13
13
 
14
- const Template: Story = (args) => <Metric {...args} />;
14
+ export default meta;
15
15
 
16
- export const Default = Template.bind({});
16
+ type Story = StoryObj<typeof meta>;
17
17
 
18
- Default.args = {
19
- value: '100',
18
+ export const Default: Story = {
19
+ args: {
20
+ value: '100',
21
+ },
20
22
  };
21
23
 
22
- export const WithUnit = Template.bind({});
23
-
24
- WithUnit.args = {
25
- value: '100',
26
- unit: 'KiB',
24
+ export const WithUnit: Story = {
25
+ args: {
26
+ value: '100',
27
+ unit: 'KiB',
28
+ },
27
29
  };
28
30
 
29
- export const WithDelta = Template.bind({});
30
-
31
- WithDelta.args = {
32
- ...WithUnit.args,
33
- children: <Delta displayValue="+10%" deltaType="POSITIVE" />,
31
+ export const WithDelta: Story = {
32
+ args: {
33
+ ...WithUnit.args,
34
+ children: <Delta displayValue="+10%" deltaType="POSITIVE" />,
35
+ },
34
36
  };
35
37
 
36
- export const Inline = Template.bind({});
37
-
38
- Inline.args = {
39
- ...WithDelta.args,
40
- inline: true,
38
+ export const Inline: Story = {
39
+ args: {
40
+ ...WithDelta.args,
41
+ inline: true,
42
+ },
41
43
  };
@@ -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,61 +1,65 @@
1
1
  import React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
2
3
 
3
- import { Meta, Story } from '@storybook/react';
4
4
  import { MetricRunInfo } from '.';
5
5
 
6
- export default {
6
+ const meta = {
7
7
  title: 'Components/MetricRunInfo',
8
8
  component: MetricRunInfo,
9
- } as Meta;
9
+ } satisfies Meta<typeof MetricRunInfo>;
10
10
 
11
- const Template: Story = (args: any) => <MetricRunInfo {...args} />;
11
+ export default meta;
12
12
 
13
- export const Default = Template.bind({});
13
+ type Story = StoryObj<typeof meta>;
14
14
 
15
- Default.args = {
16
- metricId: 'webpack.totalSizeByTypeALL',
17
- current: 120 * 1024,
18
- baseline: 100 * 1024,
15
+ export const Default: Story = {
16
+ args: {
17
+ metricId: 'webpack.totalSizeByTypeALL',
18
+ current: 120 * 1024,
19
+ baseline: 100 * 1024,
20
+ },
19
21
  };
20
22
 
21
- export const NoBaseline = Template.bind({});
22
-
23
- NoBaseline.args = {
24
- metricId: 'webpack.totalSizeByTypeALL',
25
- current: 120 * 1024,
26
- showBaseline: false,
23
+ export const NoBaseline: Story = {
24
+ args: {
25
+ metricId: 'webpack.totalSizeByTypeALL',
26
+ current: 120 * 1024,
27
+ showBaseline: false,
28
+ },
27
29
  };
28
30
 
29
- export const NoDelta = Template.bind({});
30
-
31
- NoDelta.args = {
32
- ...NoBaseline.args,
33
- showDelta: false,
31
+ export const NoDelta: Story = {
32
+ args: {
33
+ ...NoBaseline.args,
34
+ showDelta: false,
35
+ },
34
36
  };
35
37
 
36
- export const MetricNoDelta = Template.bind({});
37
-
38
- MetricNoDelta.args = {
39
- metricId: 'webpack.cacheInvalidation',
40
- current: 85,
41
- baseline: 50,
38
+ export const MetricNoDelta: Story = {
39
+ args: {
40
+ metricId: 'webpack.cacheInvalidation',
41
+ current: 85,
42
+ baseline: 50,
43
+ },
42
44
  };
43
45
 
44
- export const CustomTitle = Template.bind({});
45
-
46
- CustomTitle.args = {
47
- ...Default.args,
48
- titleWrapper: ({ children, style, ...restProps }: React.ComponentProps<'h1'>) => (
49
- <h1 style={{ ...style, fontStyle: 'italic' }} {...restProps}>
50
- {children}
51
- </h1>
52
- ),
46
+ export const CustomTitle: Story = {
47
+ args: {
48
+ ...Default.args,
49
+ titleWrapper: ({ children, style, ...restProps }) => (
50
+ <h1 style={{ ...style, fontStyle: 'italic' }} {...restProps}>
51
+ {children}
52
+ </h1>
53
+ ),
54
+ },
53
55
  };
54
56
 
55
- export const CustomComponent = Template.bind({});
56
-
57
- CustomComponent.args = {
58
- ...Default.args,
59
- as: 'a',
60
- href: 'https://google.com/search?query=bundle+stats',
57
+ export const CustomComponent: Story = {
58
+ // MetricRunInfoProps only types its passthrough props as ComponentProps<'div'>,
59
+ // but they're forwarded to RunInfo, which supports polymorphic `as` at runtime.
60
+ args: {
61
+ ...Default.args,
62
+ as: 'a',
63
+ href: 'https://google.com/search?query=bundle+stats',
64
+ } as unknown as Story['args'],
61
65
  };