@bundle-stats/ui 3.2.9 → 3.4.0-alpha.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 (74) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/lib/app/app.js +20 -5
  3. package/lib/app/app.stories.js +22 -1
  4. package/lib/assets/icons/alert-circle.svg +1 -0
  5. package/lib/assets/icons/check-circle.svg +1 -0
  6. package/lib/assets/icons.svg +8 -0
  7. package/lib/components/budget-info/budget-info.js +80 -0
  8. package/lib/components/budget-info/budget-info.module.css +40 -0
  9. package/lib/components/budget-info/budget-info.stories.js +44 -0
  10. package/lib/components/budget-info/index.js +18 -0
  11. package/lib/components/budget-insights/budget-insights.js +200 -0
  12. package/lib/components/budget-insights/budget-insights.module.css +53 -0
  13. package/lib/components/budget-insights/budget-insights.stories.js +104 -0
  14. package/lib/components/budget-insights/index.js +18 -0
  15. package/lib/components/bundle-assets-totals-table/bundle-assets-totals-table.js +43 -7
  16. package/lib/components/bundle-assets-totals-table/bundle-assets-totals-table.module.css +2 -2
  17. package/lib/components/bundle-assets-totals-table/bundle-assets-totals-table.stories.js +20 -1
  18. package/lib/components/index.js +8 -0
  19. package/lib/components/summary/summary.js +12 -7
  20. package/lib/components/summary/summary.module.css +1 -1
  21. package/lib/components/summary/summary.stories.js +29 -0
  22. package/lib/components/summary-item/summary-item.js +22 -5
  23. package/lib/components/summary-item/summary-item.module.css +11 -0
  24. package/lib/components/summary-item/summary-item.stories.js +51 -53
  25. package/lib/constants.js +4 -4
  26. package/lib/ui/alert/alert.js +13 -3
  27. package/lib/ui/icon/icon.js +2 -0
  28. package/lib-esm/app/app.js +19 -5
  29. package/lib-esm/app/app.stories.js +22 -1
  30. package/lib-esm/assets/icons/alert-circle.svg +1 -0
  31. package/lib-esm/assets/icons/check-circle.svg +1 -0
  32. package/lib-esm/assets/icons.svg +8 -0
  33. package/lib-esm/components/budget-info/budget-info.js +58 -0
  34. package/lib-esm/components/budget-info/budget-info.module.css +40 -0
  35. package/lib-esm/components/budget-info/budget-info.stories.js +30 -0
  36. package/lib-esm/components/budget-info/index.js +1 -0
  37. package/lib-esm/components/budget-insights/budget-insights.js +148 -0
  38. package/lib-esm/components/budget-insights/budget-insights.module.css +53 -0
  39. package/lib-esm/components/budget-insights/budget-insights.stories.js +84 -0
  40. package/lib-esm/components/budget-insights/index.js +1 -0
  41. package/lib-esm/components/bundle-assets-totals-table/bundle-assets-totals-table.js +24 -7
  42. package/lib-esm/components/bundle-assets-totals-table/bundle-assets-totals-table.module.css +2 -2
  43. package/lib-esm/components/bundle-assets-totals-table/bundle-assets-totals-table.stories.js +18 -0
  44. package/lib-esm/components/index.js +1 -0
  45. package/lib-esm/components/summary/summary.js +11 -7
  46. package/lib-esm/components/summary/summary.module.css +1 -1
  47. package/lib-esm/components/summary/summary.stories.js +27 -0
  48. package/lib-esm/components/summary-item/summary-item.js +20 -4
  49. package/lib-esm/components/summary-item/summary-item.module.css +11 -0
  50. package/lib-esm/components/summary-item/summary-item.stories.js +50 -38
  51. package/lib-esm/constants.js +4 -4
  52. package/lib-esm/ui/alert/alert.js +6 -3
  53. package/lib-esm/ui/icon/icon.js +2 -0
  54. package/package.json +3 -3
  55. package/src/app/app.jsx +12 -5
  56. package/src/assets/icons/alert-circle.svg +1 -0
  57. package/src/assets/icons/check-circle.svg +1 -0
  58. package/src/assets/icons.svg +8 -0
  59. package/src/components/budget-info/budget-info.jsx +65 -0
  60. package/src/components/budget-info/budget-info.module.css +40 -0
  61. package/src/components/budget-info/index.js +1 -0
  62. package/src/components/budget-insights/budget-insights.jsx +157 -0
  63. package/src/components/budget-insights/budget-insights.module.css +53 -0
  64. package/src/components/budget-insights/index.js +1 -0
  65. package/src/components/bundle-assets-totals-table/bundle-assets-totals-table.jsx +27 -13
  66. package/src/components/bundle-assets-totals-table/bundle-assets-totals-table.module.css +2 -2
  67. package/src/components/index.js +1 -0
  68. package/src/components/summary/summary.jsx +8 -7
  69. package/src/components/summary/summary.module.css +1 -1
  70. package/src/components/summary-item/summary-item.jsx +13 -2
  71. package/src/components/summary-item/summary-item.module.css +11 -0
  72. package/src/constants.js +7 -7
  73. package/src/ui/alert/alert.jsx +7 -2
  74. package/src/ui/icon/icon.jsx +2 -0
@@ -31,4 +31,22 @@ EmptyBaseline.args = {
31
31
  jobs: createJobs([{
32
32
  webpack: currentStats
33
33
  }, null])
34
+ };
35
+ export const WithBudgets = Template.bind();
36
+ WithBudgets.args = {
37
+ jobs: createJobs([{
38
+ webpack: currentStats
39
+ }, {
40
+ webpack: baselineStats
41
+ }], {
42
+ webpack: {
43
+ budgets: [{
44
+ metric: 'sizes.totalSizeByTypeJS',
45
+ value: 2 * 1024 * 1024
46
+ }, {
47
+ metric: 'sizes.totalSizeByTypeCSS',
48
+ value: 20 * 1024
49
+ }]
50
+ }
51
+ })
34
52
  };
@@ -4,6 +4,7 @@ export { BundleAssetsTotalsTable } from './bundle-assets-totals-table';
4
4
  export { BundleAssetsTotalsChartBars } from './bundle-assets-totals-chart-bars';
5
5
  export { BundlePackages } from './bundle-packages';
6
6
  export { BundleModules } from './bundle-modules';
7
+ export { BudgetInsights } from './budget-insights';
7
8
  export { ComponentLink } from './component-link';
8
9
  export { DuplicatePackagesWarning } from './duplicate-packages-warning';
9
10
  export { Delta } from './delta';
@@ -13,12 +13,16 @@ import { FlexStack } from '../../layout/flex-stack';
13
13
  import { ComponentLink } from '../component-link';
14
14
  import { SummaryItem } from '../summary-item';
15
15
  import css from './summary.module.css';
16
+
17
+ const getSourceMetricId = fullMetricId => fullMetricId.split('.').slice(1).join('.');
18
+
16
19
  export const Summary = _ref => {
17
20
  let {
18
21
  className,
19
22
  size,
20
23
  keys,
21
24
  data,
25
+ budgets,
22
26
  loading,
23
27
  showSummaryItemDelta,
24
28
  summaryItemLink: SummaryItemCustomLink
@@ -30,7 +34,7 @@ export const Summary = _ref => {
30
34
  className: css.items
31
35
  }, Array.from(METRIC_COMPONENT_LINKS).filter(_ref2 => {
32
36
  let [metricId] = _ref2;
33
- return keys.includes(metricId);
37
+ return keys.includes(getSourceMetricId(metricId));
34
38
  }).map(_ref3 => {
35
39
  let [metricId, metricOptions] = _ref3;
36
40
  return /*#__PURE__*/React.createElement("div", {
@@ -40,6 +44,7 @@ export const Summary = _ref => {
40
44
  size: size,
41
45
  id: metricId,
42
46
  data: _get(data, metricId),
47
+ budget: _get(budgets, getSourceMetricId(metricId)),
43
48
  loading: loading,
44
49
  showMetricDescription: true,
45
50
  showDelta: showSummaryItemDelta && metricOptions.showDelta !== false
@@ -54,6 +59,7 @@ export const Summary = _ref => {
54
59
  Summary.defaultProps = {
55
60
  className: '',
56
61
  data: null,
62
+ budgets: null,
57
63
  loading: false,
58
64
  size: '',
59
65
  keys: METRICS_WEBPACK_GENERAL,
@@ -64,12 +70,10 @@ Summary.propTypes = {
64
70
  className: PropTypes.string,
65
71
  size: PropTypes.string,
66
72
  keys: PropTypes.arrayOf(PropTypes.string),
67
- data: PropTypes.shape({
68
- [PropTypes.string]: PropTypes.shape({
69
- baseline: PropTypes.number,
70
- current: PropTypes.number
71
- })
72
- }),
73
+ data: PropTypes.object,
74
+ // eslint-disable-line react/forbid-prop-types
75
+ budgets: PropTypes.object,
76
+ // eslint-disable-line react/forbid-prop-types
73
77
  loading: PropTypes.bool,
74
78
  showSummaryItemDelta: PropTypes.bool,
75
79
  summaryItemLink: PropTypes.elementType
@@ -4,7 +4,7 @@
4
4
  }
5
5
 
6
6
  .item {
7
- padding: var(--space-small) var(--space-xxlarge) var(--space-small) var(--space-small);
7
+ padding: var(--space-small);
8
8
  flex: 1 0 25%; /* force equal sizes */
9
9
  position: relative;
10
10
  z-index: 1;
@@ -21,6 +21,33 @@ stories.addDecorator(getWrapperDecorator());
21
21
  stories.add('default', () => /*#__PURE__*/React.createElement(Summary, {
22
22
  data: MULTIPLE_JOBS[0].summary
23
23
  }));
24
+ stories.add('budgets', () => /*#__PURE__*/React.createElement(Summary, {
25
+ data: MULTIPLE_JOBS[0].summary,
26
+ budgets: {
27
+ webpack: {
28
+ totalSizeByTypeALL: {
29
+ currentValue: MULTIPLE_JOBS[0].summary.webpack.totalSizeByTypeALL.current,
30
+ budgetValue: 1024 * 1024,
31
+ failed: false
32
+ },
33
+ totalInitialSizeJS: {
34
+ currentValue: MULTIPLE_JOBS[0].summary.webpack.totalInitialSizeJS.current,
35
+ budgetValue: 512 * 1024,
36
+ failed: true
37
+ },
38
+ totalInitialSizeCSS: {
39
+ currentValue: MULTIPLE_JOBS[0].summary.webpack.totalInitialSizeCSS.current,
40
+ budgetValue: 50 * 1024,
41
+ failed: false
42
+ },
43
+ chunkCount: {
44
+ currentValue: MULTIPLE_JOBS[0].summary.webpack.chunkCount.current,
45
+ budgetValue: 50,
46
+ failed: false
47
+ }
48
+ }
49
+ }
50
+ }));
24
51
  stories.add('custom keys', () => /*#__PURE__*/React.createElement(Summary, {
25
52
  keys: METRICS_WEBPACK_ASSETS,
26
53
  data: MULTIPLE_JOBS[0].summary
@@ -11,6 +11,7 @@ import { Stack } from '../../layout/stack';
11
11
  import { FlexStack } from '../../layout/flex-stack';
12
12
  import { Metric } from '../metric';
13
13
  import { Delta } from '../delta';
14
+ import { BudgetInfo } from '../budget-info';
14
15
  import css from './summary-item.module.css';
15
16
 
16
17
  const MetricInfo = _ref => {
@@ -43,6 +44,7 @@ export const SummaryItem = _ref2 => {
43
44
  loading,
44
45
  showDelta,
45
46
  showMetricDescription,
47
+ budget,
46
48
  ...props
47
49
  } = _ref2;
48
50
  const {
@@ -64,12 +66,18 @@ export const SummaryItem = _ref2 => {
64
66
  as: "h3",
65
67
  space: "xxxsmall",
66
68
  className: css.title
67
- }, /*#__PURE__*/React.createElement("span", null, metric.label), showMetricDescriptionTooltip && /*#__PURE__*/React.createElement(HoverCard, {
69
+ }, /*#__PURE__*/React.createElement("span", {
70
+ className: css.titleLabel
71
+ }, metric.label), showMetricDescriptionTooltip && /*#__PURE__*/React.createElement(HoverCard, {
68
72
  className: css.icon,
69
73
  label: /*#__PURE__*/React.createElement(Icon, {
70
74
  glyph: "help"
71
75
  })
72
- }, /*#__PURE__*/React.createElement(MetricInfo, metric))), !loading ? /*#__PURE__*/React.createElement(Stack, null, /*#__PURE__*/React.createElement(Metric, {
76
+ }, /*#__PURE__*/React.createElement(MetricInfo, metric)), budget && /*#__PURE__*/React.createElement(BudgetInfo, {
77
+ className: css.budgetInfo,
78
+ metricId: id,
79
+ budgetInsight: budget
80
+ })), !loading ? /*#__PURE__*/React.createElement(Stack, null, /*#__PURE__*/React.createElement(Metric, {
73
81
  className: css.currentMetric,
74
82
  value: current,
75
83
  formatter: metric.formatter,
@@ -96,7 +104,8 @@ SummaryItem.defaultProps = {
96
104
  size: 'medium',
97
105
  loading: false,
98
106
  showMetricDescription: false,
99
- showDelta: true
107
+ showDelta: true,
108
+ budget: undefined
100
109
  };
101
110
  SummaryItem.propTypes = {
102
111
  /** Adopted child class name */
@@ -122,5 +131,12 @@ SummaryItem.propTypes = {
122
131
  showMetricDescription: PropTypes.bool,
123
132
 
124
133
  /** Show delta */
125
- showDelta: PropTypes.bool
134
+ showDelta: PropTypes.bool,
135
+
136
+ /** Budget insight data */
137
+ budget: PropTypes.shape({
138
+ currentBalue: PropTypes.number,
139
+ budgetValue: PropTypes.number,
140
+ failed: PropTypes.bool
141
+ })
126
142
  };
@@ -1,3 +1,8 @@
1
+ .root {
2
+ position: relative;
3
+ padding-right: var(--space-xlarge);
4
+ }
5
+
1
6
  .title {
2
7
  align-items: center;
3
8
  color: var(--color-text-ultra-light);
@@ -8,6 +13,12 @@
8
13
  white-space: nowrap;
9
14
  }
10
15
 
16
+ .budgetInfo {
17
+ position: absolute;
18
+ right: 0;
19
+ top: 0;
20
+ }
21
+
11
22
  .currentMetric {
12
23
  display: block;
13
24
  color: var(--color-heading);
@@ -2,44 +2,56 @@ import React from 'react';
2
2
  import { SummaryItem } from './summary-item';
3
3
  export default {
4
4
  title: 'Components/SummaryItem',
5
- component: SummaryItem
6
- };
7
- export const Standard = () => /*#__PURE__*/React.createElement(SummaryItem, {
8
- loading: false,
9
- id: "webpack.totalSizeByTypeALL",
10
- data: {
11
- current: 120 * 1000,
12
- baseline: 100 * 1000
13
- }
14
- });
15
- export const SizeLarge = () => /*#__PURE__*/React.createElement(SummaryItem, {
16
- size: "large",
17
- loading: false,
18
- id: "webpack.totalSizeByTypeALL",
19
- data: {
20
- current: 120 * 1000,
21
- baseline: 100 * 1000
5
+ component: SummaryItem,
6
+ decorators: [Story => /*#__PURE__*/React.createElement("div", {
7
+ style: {
8
+ maxWidth: '320px'
9
+ }
10
+ }, /*#__PURE__*/React.createElement(Story, null))],
11
+ args: {
12
+ loading: false,
13
+ id: 'webpack.totalSizeByTypeALL',
14
+ data: {
15
+ current: 120 * 1000,
16
+ baseline: 100 * 1000
17
+ }
22
18
  }
23
- });
24
- export const ShowMetricDescription = () => /*#__PURE__*/React.createElement(SummaryItem, {
25
- loading: false,
26
- id: "webpack.totalSizeByTypeALL",
27
- data: {
28
- current: 120 * 1000,
29
- baseline: 100 * 1000
30
- },
19
+ };
20
+
21
+ const Template = args => /*#__PURE__*/React.createElement(SummaryItem, args);
22
+
23
+ export const Standard = Template.bind();
24
+ export const SizeLarge = Template.bind();
25
+ SizeLarge.args = {
26
+ size: 'large'
27
+ };
28
+ export const ShowMetricDescription = Template.bind();
29
+ ShowMetricDescription.args = {
31
30
  showMetricDescription: true
32
- });
33
- export const ShowDeltaFalse = () => /*#__PURE__*/React.createElement(SummaryItem, {
34
- loading: false,
35
- id: "webpack.totalSizeByTypeALL",
36
- data: {
37
- current: 120 * 1000,
38
- baseline: 0
39
- },
31
+ };
32
+ export const ShowDeltaFalse = Template.bind();
33
+ ShowDeltaFalse.args = {
40
34
  showDelta: false
41
- });
42
- export const Loading = () => /*#__PURE__*/React.createElement(SummaryItem, {
43
- loading: true,
44
- id: "webpack.totalSizeByTypeALL"
45
- });
35
+ };
36
+ export const BudgetError = Template.bind();
37
+ BudgetError.args = {
38
+ showMetricDescription: true,
39
+ budget: {
40
+ currentValue: 120 * 1000,
41
+ budgetValue: 100 * 1000,
42
+ failed: true
43
+ }
44
+ };
45
+ export const BudgetSuccess = Template.bind();
46
+ BudgetSuccess.args = {
47
+ showMetricDescription: true,
48
+ budget: {
49
+ currentValue: 120 * 1000,
50
+ budgetValue: 120 * 1024,
51
+ failed: false
52
+ }
53
+ };
54
+ export const Loading = Template.bind();
55
+ Loading.args = {
56
+ loading: true
57
+ };
@@ -25,7 +25,7 @@ export const SECTION_URLS = {
25
25
  export const ASSETS_SIZES_FILE_TYPE_MAP = FILE_TYPES.reduce((agg, fileType) => ({ ...agg,
26
26
  [`webpack.sizes.totalSizeByType${fileType}`]: fileType
27
27
  }), {});
28
- export const METRICS_WEBPACK_GENERAL = ['webpack.totalSizeByTypeALL', 'webpack.totalInitialSizeJS', 'webpack.totalInitialSizeCSS', 'webpack.cacheInvalidation'];
29
- export const METRICS_WEBPACK_ASSETS = ['webpack.assetCount', 'webpack.chunkCount'];
30
- export const METRICS_WEBPACK_MODULES = ['webpack.moduleCount'];
31
- export const METRICS_WEBPACK_PACKAGES = ['webpack.packageCount', 'webpack.duplicatePackagesCount'];
28
+ export const METRICS_WEBPACK_GENERAL = ['totalSizeByTypeALL', 'totalInitialSizeJS', 'totalInitialSizeCSS', 'cacheInvalidation'];
29
+ export const METRICS_WEBPACK_ASSETS = ['assetCount', 'chunkCount'];
30
+ export const METRICS_WEBPACK_MODULES = ['moduleCount'];
31
+ export const METRICS_WEBPACK_PACKAGES = ['packageCount', 'duplicatePackagesCount'];
@@ -1,3 +1,5 @@
1
+ function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
+
1
3
  import React from 'react';
2
4
  import cx from 'classnames';
3
5
  import PropTypes from 'prop-types';
@@ -7,13 +9,14 @@ export const Alert = props => {
7
9
  const {
8
10
  className,
9
11
  kind,
10
- children
12
+ children,
13
+ ...restProps
11
14
  } = props;
12
- return /*#__PURE__*/React.createElement(Box, {
15
+ return /*#__PURE__*/React.createElement(Box, _extends({
13
16
  padding: ['xsmall', 'small'],
14
17
  outline: true,
15
18
  className: cx(css.root, className, css[kind])
16
- }, children);
19
+ }, restProps), children);
17
20
  };
18
21
  Alert.propTypes = {
19
22
  className: PropTypes.string,
@@ -5,11 +5,13 @@ import PropTypes from 'prop-types';
5
5
  import cx from 'classnames';
6
6
  import css from './icon.module.css';
7
7
  const ICONS = {
8
+ ALERT_CIRCLE: 'alert-circle',
8
9
  ARROW: 'arrow',
9
10
  ARROW_RIGHT_CIRLCE: 'arrow-right-circle',
10
11
  CANCEL: 'cancel',
11
12
  CLOSE: 'close',
12
13
  CLOCK: 'clock',
14
+ CHECK_CIRCLE: 'check-circle',
13
15
  COMMIT: 'commit',
14
16
  FILTER: 'filter',
15
17
  HELP: 'help',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bundle-stats/ui",
3
- "version": "3.2.9",
3
+ "version": "3.4.0-alpha.1",
4
4
  "description": "UI components for BundleStats projects.",
5
5
  "main": "lib/index.js",
6
6
  "module": "lib-esm/index.js",
@@ -85,12 +85,12 @@
85
85
  "react-router-dom": "5.3.0"
86
86
  },
87
87
  "dependencies": {
88
- "@bundle-stats/utils": "^3.2.9",
88
+ "@bundle-stats/utils": "^3.4.0-alpha.1",
89
89
  "ariakit": "2.0.0-alpha.19",
90
90
  "date-fns": "2.28.0",
91
91
  "modern-css-reset": "1.4.0",
92
92
  "query-string": "7.1.1",
93
93
  "use-query-params": "1.2.3"
94
94
  },
95
- "gitHead": "f2fe3a81b73db8cd9e65f145f1b420c78f74f186"
95
+ "gitHead": "a7ba8010b047028c4e56e407719616fde7d8ea88"
96
96
  }
package/src/app/app.jsx CHANGED
@@ -13,6 +13,7 @@ import {
13
13
  import { Box } from '../layout/box';
14
14
  import { Container } from '../ui/container';
15
15
  import { DuplicatePackagesWarning } from '../components/duplicate-packages-warning';
16
+ import { BudgetInsights } from '../components/budget-insights';
16
17
  import { Summary } from '../components/summary';
17
18
  import { BundleAssets } from '../components/bundle-assets';
18
19
  import { BundleAssetsTotalsChartBars } from '../components/bundle-assets-totals-chart-bars';
@@ -77,10 +78,13 @@ const OverviewContent = () => {
77
78
  <Stack space="medium">
78
79
  {duplicatePackagesInsights && (
79
80
  <Container>
80
- <DuplicatePackagesWarning
81
- duplicatePackagesCount={duplicatePackagesCount}
82
- showDelta={jobs.length > 1}
83
- />
81
+ <Stack space="small">
82
+ <DuplicatePackagesWarning
83
+ duplicatePackagesCount={duplicatePackagesCount}
84
+ showDelta={jobs.length > 1}
85
+ />
86
+ <BudgetInsights source="webpack" budgets={jobs[0].insights?.webpack?.budgets} />
87
+ </Stack>
84
88
  </Container>
85
89
  )}
86
90
  <Container>
@@ -104,6 +108,7 @@ const AssetsContent = () => {
104
108
  <Summary
105
109
  keys={METRICS_WEBPACK_ASSETS}
106
110
  data={jobs?.[0]?.summary}
111
+ budgets={jobs[0].insights?.webpack?.budgets}
107
112
  showSummaryItemDelta={jobs?.length !== 1}
108
113
  />
109
114
  <Box outline>
@@ -126,6 +131,7 @@ const ModulesContent = () => {
126
131
  <Summary
127
132
  keys={METRICS_WEBPACK_MODULES}
128
133
  data={jobs?.[0]?.summary}
134
+ budgets={jobs[0].insights?.webpack?.budgets}
129
135
  showSummaryItemDelta={jobs?.length !== 1}
130
136
  />
131
137
  <Box outline>
@@ -148,6 +154,7 @@ const PackagesContent = () => {
148
154
  <Summary
149
155
  keys={METRICS_WEBPACK_PACKAGES}
150
156
  data={jobs?.[0]?.summary}
157
+ budgets={jobs[0].insights?.webpack?.budgets}
151
158
  showSummaryItemDelta={jobs?.length !== 1}
152
159
  />
153
160
  <Box outline>
@@ -177,10 +184,10 @@ const AppComponent = ({ version, jobs }) => {
177
184
  size="large"
178
185
  keys={METRICS_WEBPACK_GENERAL}
179
186
  data={jobs[0].summary}
187
+ budgets={jobs[0].insights?.webpack?.budgets}
180
188
  showSummaryItemDelta={jobs.length !== 1}
181
189
  />
182
190
  </Container>
183
-
184
191
  <Container className={css.tabsContainer}>
185
192
  <Tabs className={css.tabs}>
186
193
  <NavLink exact to={URLS.OVERVIEW} activeClassName={css.tabActive}>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-alert-circle"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check-circle"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>
@@ -1,4 +1,8 @@
1
1
  <svg width="16" height="16">
2
+ <symbol fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-alert-circle" viewBox="0 0 24 24" id="alert-circle" xmlns="http://www.w3.org/2000/svg">
3
+ <circle cx="12" cy="12" r="10"/>
4
+ <path d="M12 8v4M12 16h.01"/>
5
+ </symbol>
2
6
  <symbol viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-arrow-down" id="arrow" xmlns="http://www.w3.org/2000/svg">
3
7
  <path d="M12 5v14M19 12l-7 7-7-7"/>
4
8
  </symbol>
@@ -10,6 +14,10 @@
10
14
  <circle cx="12" cy="12" r="10"/>
11
15
  <path d="M15 9l-6 6M9 9l6 6"/>
12
16
  </symbol>
17
+ <symbol fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check-circle" viewBox="0 0 24 24" id="check-circle" xmlns="http://www.w3.org/2000/svg">
18
+ <path d="M22 11.08V12a10 10 0 11-5.93-9.14"/>
19
+ <path d="M22 4L12 14.01l-3-3"/>
20
+ </symbol>
13
21
  <symbol viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-clock" id="clock" xmlns="http://www.w3.org/2000/svg">
14
22
  <circle cx="12" cy="12" r="10"/>
15
23
  <path d="M12 6v6l4 2"/>
@@ -0,0 +1,65 @@
1
+ import React from 'react';
2
+ import cx from 'classnames';
3
+ import PropTypes from 'prop-types';
4
+ import { InsightType } from '@bundle-stats/utils';
5
+ import * as budgetsInsightsTransformer from '@bundle-stats/utils/lib-esm/transformers/budgets-insights';
6
+
7
+ import { Icon } from '../../ui/icon';
8
+ import { Tooltip } from '../../ui/tooltip';
9
+
10
+ import css from './budget-info.module.css';
11
+
12
+ const INSIGHT_TYPE_MAP = {
13
+ [InsightType.SUCCESS]: {
14
+ className: css.success,
15
+ glyph: Icon.ICONS.CHECK_CIRCLE,
16
+ },
17
+ [InsightType.ERROR]: {
18
+ className: css.error,
19
+ glyph: Icon.ICONS.ALERT_CIRCLE,
20
+ },
21
+ [InsightType.WARNING]: {
22
+ className: css.warning,
23
+ glyph: Icon.ICONS.WARNING,
24
+ },
25
+ };
26
+
27
+ export const BudgetInfo = ({ className = '', metricId, budgetInsight }) => {
28
+ const budgetInsightInfo = budgetsInsightsTransformer.getInfo(metricId, budgetInsight);
29
+ const { className: insightTypeClassName, glyph } = INSIGHT_TYPE_MAP[budgetInsightInfo.type];
30
+
31
+ const rootClassName = cx(css.root, insightTypeClassName, className);
32
+ const { data: messageData } = budgetInsightInfo.message;
33
+
34
+ return (
35
+ <Tooltip
36
+ className={rootClassName}
37
+ title={
38
+ <>
39
+ <strong>{messageData.metricLabel}</strong>
40
+ {` value (`}
41
+ <strong>{messageData.currentValue}</strong>
42
+ {`) is ${messageData.diffLabel} `}
43
+ <strong>{messageData.budgetValue}</strong>
44
+ {` budget `}
45
+ </>
46
+ }
47
+ >
48
+ <Icon className={css.icon} glyph={glyph} size="medium" />
49
+ </Tooltip>
50
+ );
51
+ };
52
+
53
+ BudgetInfo.propTypes = {
54
+ metricId: PropTypes.string.isRequired,
55
+ budgetInsight: PropTypes.shape({
56
+ currentValue: PropTypes.number,
57
+ budgetValue: PropTypes.number,
58
+ failed: PropTypes.bool,
59
+ }).isRequired,
60
+ className: PropTypes.string,
61
+ };
62
+
63
+ BudgetInfo.defaultProps = {
64
+ className: '',
65
+ };
@@ -0,0 +1,40 @@
1
+ .root {
2
+ display: inline-block;
3
+ line-height: 1em;
4
+ }
5
+
6
+ /* Budget error */
7
+ .error .icon {
8
+ color: var(--color-danger);
9
+ }
10
+
11
+ .error .icon:hover,
12
+ .error .icon:focus,
13
+ .error .icon:active
14
+ {
15
+ color: var(--color-danger-light);
16
+ }
17
+
18
+ /* Budget success */
19
+ .success .icon {
20
+ color: var(--color-success);
21
+ }
22
+
23
+ .success .icon:hover,
24
+ .success .icon:focus,
25
+ .success .icon:active
26
+ {
27
+ color: var(--color-success-light);
28
+ }
29
+
30
+ /* Budget warning */
31
+ .warning .icon {
32
+ color: var(--color-warning-dark);
33
+ }
34
+
35
+ .warning .icon:hover,
36
+ .warning .icon:focus,
37
+ .warning .icon:active
38
+ {
39
+ color: var(--color-warning);
40
+ }
@@ -0,0 +1 @@
1
+ export * from './budget-info';