@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
@@ -24,11 +24,13 @@ function _objectWithoutProperties(source, excluded) { if (source == null) return
24
24
  function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
25
25
 
26
26
  var ICONS = {
27
+ ALERT_CIRCLE: 'alert-circle',
27
28
  ARROW: 'arrow',
28
29
  ARROW_RIGHT_CIRLCE: 'arrow-right-circle',
29
30
  CANCEL: 'cancel',
30
31
  CLOSE: 'close',
31
32
  CLOCK: 'clock',
33
+ CHECK_CIRCLE: 'check-circle',
32
34
  COMMIT: 'commit',
33
35
  FILTER: 'filter',
34
36
  HELP: 'help',
@@ -8,6 +8,7 @@ import { METRICS_WEBPACK_ASSETS, METRICS_WEBPACK_GENERAL, METRICS_WEBPACK_MODULE
8
8
  import { Box } from '../layout/box';
9
9
  import { Container } from '../ui/container';
10
10
  import { DuplicatePackagesWarning } from '../components/duplicate-packages-warning';
11
+ import { BudgetInsights } from '../components/budget-insights';
11
12
  import { Summary } from '../components/summary';
12
13
  import { BundleAssets } from '../components/bundle-assets';
13
14
  import { BundleAssetsTotalsChartBars } from '../components/bundle-assets-totals-chart-bars';
@@ -81,6 +82,8 @@ JobsProvider.propTypes = {
81
82
  };
82
83
 
83
84
  const OverviewContent = () => {
85
+ var _jobs$0$insights, _jobs$0$insights$webp;
86
+
84
87
  const {
85
88
  jobs
86
89
  } = useContext(JobsContext);
@@ -90,10 +93,15 @@ const OverviewContent = () => {
90
93
  const duplicatePackagesInsights = Boolean(duplicatePackagesCount.current || duplicatePackagesCount.baseline);
91
94
  return /*#__PURE__*/React.createElement(Stack, {
92
95
  space: "medium"
93
- }, duplicatePackagesInsights && /*#__PURE__*/React.createElement(Container, null, /*#__PURE__*/React.createElement(DuplicatePackagesWarning, {
96
+ }, duplicatePackagesInsights && /*#__PURE__*/React.createElement(Container, null, /*#__PURE__*/React.createElement(Stack, {
97
+ space: "small"
98
+ }, /*#__PURE__*/React.createElement(DuplicatePackagesWarning, {
94
99
  duplicatePackagesCount: duplicatePackagesCount,
95
100
  showDelta: jobs.length > 1
96
- })), /*#__PURE__*/React.createElement(Container, null, /*#__PURE__*/React.createElement(Stack, {
101
+ }), /*#__PURE__*/React.createElement(BudgetInsights, {
102
+ source: "webpack",
103
+ budgets: (_jobs$0$insights = jobs[0].insights) == null ? void 0 : (_jobs$0$insights$webp = _jobs$0$insights.webpack) == null ? void 0 : _jobs$0$insights$webp.budgets
104
+ }))), /*#__PURE__*/React.createElement(Container, null, /*#__PURE__*/React.createElement(Stack, {
97
105
  space: "small"
98
106
  }, /*#__PURE__*/React.createElement(TotalSizeTypeTitle, null), /*#__PURE__*/React.createElement(BundleAssetsTotalsChartBars, {
99
107
  jobs: jobs
@@ -104,7 +112,7 @@ const OverviewContent = () => {
104
112
  };
105
113
 
106
114
  const AssetsContent = () => {
107
- var _jobs$;
115
+ var _jobs$, _jobs$0$insights2, _jobs$0$insights2$web;
108
116
 
109
117
  const [bundleStatsState, bundleStatsSetState] = useComponentQueryState(COMPONENT.BUNDLE_ASSETS);
110
118
  const {
@@ -115,6 +123,7 @@ const AssetsContent = () => {
115
123
  }, /*#__PURE__*/React.createElement(Summary, {
116
124
  keys: METRICS_WEBPACK_ASSETS,
117
125
  data: jobs == null ? void 0 : (_jobs$ = jobs[0]) == null ? void 0 : _jobs$.summary,
126
+ budgets: (_jobs$0$insights2 = jobs[0].insights) == null ? void 0 : (_jobs$0$insights2$web = _jobs$0$insights2.webpack) == null ? void 0 : _jobs$0$insights2$web.budgets,
118
127
  showSummaryItemDelta: (jobs == null ? void 0 : jobs.length) !== 1
119
128
  }), /*#__PURE__*/React.createElement(Box, {
120
129
  outline: true
@@ -125,7 +134,7 @@ const AssetsContent = () => {
125
134
  };
126
135
 
127
136
  const ModulesContent = () => {
128
- var _jobs$2;
137
+ var _jobs$2, _jobs$0$insights3, _jobs$0$insights3$web;
129
138
 
130
139
  const [bundleModulesState, bundleModulesSetState] = useComponentQueryState(COMPONENT.BUNDLE_MODULES);
131
140
  const {
@@ -136,6 +145,7 @@ const ModulesContent = () => {
136
145
  }, /*#__PURE__*/React.createElement(Summary, {
137
146
  keys: METRICS_WEBPACK_MODULES,
138
147
  data: jobs == null ? void 0 : (_jobs$2 = jobs[0]) == null ? void 0 : _jobs$2.summary,
148
+ budgets: (_jobs$0$insights3 = jobs[0].insights) == null ? void 0 : (_jobs$0$insights3$web = _jobs$0$insights3.webpack) == null ? void 0 : _jobs$0$insights3$web.budgets,
139
149
  showSummaryItemDelta: (jobs == null ? void 0 : jobs.length) !== 1
140
150
  }), /*#__PURE__*/React.createElement(Box, {
141
151
  outline: true
@@ -146,7 +156,7 @@ const ModulesContent = () => {
146
156
  };
147
157
 
148
158
  const PackagesContent = () => {
149
- var _jobs$3;
159
+ var _jobs$3, _jobs$0$insights4, _jobs$0$insights4$web;
150
160
 
151
161
  const [bundlePackagesState, bundlePackagesSetState] = useComponentQueryState(COMPONENT.BUNDLE_PACKAGES);
152
162
  const {
@@ -157,6 +167,7 @@ const PackagesContent = () => {
157
167
  }, /*#__PURE__*/React.createElement(Summary, {
158
168
  keys: METRICS_WEBPACK_PACKAGES,
159
169
  data: jobs == null ? void 0 : (_jobs$3 = jobs[0]) == null ? void 0 : _jobs$3.summary,
170
+ budgets: (_jobs$0$insights4 = jobs[0].insights) == null ? void 0 : (_jobs$0$insights4$web = _jobs$0$insights4.webpack) == null ? void 0 : _jobs$0$insights4$web.budgets,
160
171
  showSummaryItemDelta: (jobs == null ? void 0 : jobs.length) !== 1
161
172
  }), /*#__PURE__*/React.createElement(Box, {
162
173
  outline: true
@@ -168,6 +179,8 @@ const PackagesContent = () => {
168
179
  };
169
180
 
170
181
  const AppComponent = _ref3 => {
182
+ var _jobs$0$insights5, _jobs$0$insights5$web;
183
+
171
184
  let {
172
185
  version,
173
186
  jobs
@@ -192,6 +205,7 @@ const AppComponent = _ref3 => {
192
205
  size: "large",
193
206
  keys: METRICS_WEBPACK_GENERAL,
194
207
  data: jobs[0].summary,
208
+ budgets: (_jobs$0$insights5 = jobs[0].insights) == null ? void 0 : (_jobs$0$insights5$web = _jobs$0$insights5.webpack) == null ? void 0 : _jobs$0$insights5$web.budgets,
195
209
  showSummaryItemDelta: jobs.length !== 1
196
210
  })), /*#__PURE__*/React.createElement(Container, {
197
211
  className: css.tabsContainer
@@ -28,7 +28,28 @@ const JOBS = createJobs([CURRENT_SOURCE, BASELINE_SOURCE], {
28
28
  webpack: {
29
29
  budgets: [{
30
30
  metric: 'totalSizeByTypeALL',
31
- value: 1024 * 1024
31
+ value: 2 * 1024 * 1024
32
+ }, {
33
+ metric: 'totalInitialSizeJS',
34
+ value: 512 * 1024
35
+ }, {
36
+ metric: 'totalInitialSizeCSS',
37
+ value: 50 * 1024
38
+ }, {
39
+ metric: 'chunkCount',
40
+ value: 50
41
+ }, {
42
+ metric: 'moduleCount',
43
+ value: 2000
44
+ }, {
45
+ metric: 'duplicatePackagesCount',
46
+ value: 5
47
+ }, {
48
+ metric: 'sizes.totalSizeByTypeJS',
49
+ value: 2 * 1024 * 1024
50
+ }, {
51
+ metric: 'sizes.totalSizeByTypeCSS',
52
+ value: 20 * 1024
32
53
  }]
33
54
  }
34
55
  });
@@ -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,58 @@
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
+ import { Icon } from '../../ui/icon';
7
+ import { Tooltip } from '../../ui/tooltip';
8
+ import css from './budget-info.module.css';
9
+ const INSIGHT_TYPE_MAP = {
10
+ [InsightType.SUCCESS]: {
11
+ className: css.success,
12
+ glyph: Icon.ICONS.CHECK_CIRCLE
13
+ },
14
+ [InsightType.ERROR]: {
15
+ className: css.error,
16
+ glyph: Icon.ICONS.ALERT_CIRCLE
17
+ },
18
+ [InsightType.WARNING]: {
19
+ className: css.warning,
20
+ glyph: Icon.ICONS.WARNING
21
+ }
22
+ };
23
+ export const BudgetInfo = _ref => {
24
+ let {
25
+ className = '',
26
+ metricId,
27
+ budgetInsight
28
+ } = _ref;
29
+ const budgetInsightInfo = budgetsInsightsTransformer.getInfo(metricId, budgetInsight);
30
+ const {
31
+ className: insightTypeClassName,
32
+ glyph
33
+ } = INSIGHT_TYPE_MAP[budgetInsightInfo.type];
34
+ const rootClassName = cx(css.root, insightTypeClassName, className);
35
+ const {
36
+ data: messageData
37
+ } = budgetInsightInfo.message;
38
+ return /*#__PURE__*/React.createElement(Tooltip, {
39
+ className: rootClassName,
40
+ title: /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("strong", null, messageData.metricLabel), ` value (`, /*#__PURE__*/React.createElement("strong", null, messageData.currentValue), `) is ${messageData.diffLabel} `, /*#__PURE__*/React.createElement("strong", null, messageData.budgetValue), ` budget `)
41
+ }, /*#__PURE__*/React.createElement(Icon, {
42
+ className: css.icon,
43
+ glyph: glyph,
44
+ size: "medium"
45
+ }));
46
+ };
47
+ BudgetInfo.propTypes = {
48
+ metricId: PropTypes.string.isRequired,
49
+ budgetInsight: PropTypes.shape({
50
+ currentValue: PropTypes.number,
51
+ budgetValue: PropTypes.number,
52
+ failed: PropTypes.bool
53
+ }).isRequired,
54
+ className: PropTypes.string
55
+ };
56
+ BudgetInfo.defaultProps = {
57
+ className: ''
58
+ };
@@ -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,30 @@
1
+ import React from 'react';
2
+ import { storiesOf } from '@storybook/react';
3
+ import { getWrapperDecorator } from '../../stories';
4
+ import { BudgetInfo } from '.';
5
+ const stories = storiesOf('Components/BudgetInfo', module);
6
+ stories.addDecorator(getWrapperDecorator());
7
+ stories.add('default', () => /*#__PURE__*/React.createElement(BudgetInfo, {
8
+ metricId: "webpack.assetCount",
9
+ budgetInsight: {
10
+ budgetValue: 300,
11
+ currentValue: 250,
12
+ failed: false
13
+ }
14
+ }));
15
+ stories.add('error', () => /*#__PURE__*/React.createElement(BudgetInfo, {
16
+ metricId: "webpack.assetCount",
17
+ budgetInsight: {
18
+ budgetValue: 300,
19
+ currentValue: 350,
20
+ failed: true
21
+ }
22
+ }));
23
+ stories.add('warning', () => /*#__PURE__*/React.createElement(BudgetInfo, {
24
+ metricId: "webpack.assetCount",
25
+ budgetInsight: {
26
+ budgetValue: 300,
27
+ currentValue: 300,
28
+ failed: false
29
+ }
30
+ }));
@@ -0,0 +1 @@
1
+ export * from './budget-info';
@@ -0,0 +1,148 @@
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
+
3
+ import React, { useMemo, useState } from 'react';
4
+ import PropTypes from 'prop-types';
5
+ import cx from 'classnames';
6
+ import { METRIC_COMPONENT_LINKS } from '@bundle-stats/utils';
7
+ import * as budgetsInsightsTransformer from '@bundle-stats/utils/lib-esm/transformers/budgets-insights';
8
+ import { Box } from '../../layout/box';
9
+ import { Stack } from '../../layout/stack';
10
+ import { FlexStack } from '../../layout/flex-stack';
11
+ import { Alert } from '../../ui/alert';
12
+ import { Icon } from '../../ui/icon';
13
+ import { ComponentLink } from '../component-link';
14
+ import css from './budget-insights.module.css';
15
+
16
+ const Budget = props => {
17
+ const {
18
+ className,
19
+ metricId,
20
+ budgetInsight,
21
+ CustomLink
22
+ } = props;
23
+ const componentLinkOptions = METRIC_COMPONENT_LINKS.get(metricId);
24
+ const budgetInsightInfo = budgetsInsightsTransformer.getInfo(metricId, budgetInsight);
25
+ const {
26
+ data: messageData
27
+ } = budgetInsightInfo.message;
28
+ return /*#__PURE__*/React.createElement(CustomLink, _extends({
29
+ className: cx(css.budget, className)
30
+ }, componentLinkOptions == null ? void 0 : componentLinkOptions.link), /*#__PURE__*/React.createElement("strong", null, messageData.metricLabel), ` value (`, /*#__PURE__*/React.createElement("strong", null, messageData.currentValue), `) is ${messageData.diffLabel} `, /*#__PURE__*/React.createElement("strong", null, messageData.budgetValue), ` budget `);
31
+ };
32
+
33
+ Budget.propTypes = {
34
+ className: PropTypes.string,
35
+ metricId: PropTypes.string.isRequired,
36
+ budgetInsight: PropTypes.shape({
37
+ currentValue: PropTypes.number.isRequired,
38
+ budgetValue: PropTypes.number.isRequired,
39
+ failed: PropTypes.bool.isRequired
40
+ }).isRequired,
41
+ CustomLink: PropTypes.elementType.isRequired
42
+ };
43
+ Budget.defaultProps = {
44
+ className: ''
45
+ };
46
+
47
+ const BudgetsGroup = props => {
48
+ const {
49
+ className,
50
+ kind,
51
+ source,
52
+ budgets,
53
+ CustomLink
54
+ } = props;
55
+ const [showBudgets, setShowBudgets] = useState(false);
56
+ const isFailed = kind === 'danger';
57
+ return /*#__PURE__*/React.createElement(Alert, {
58
+ kind: kind,
59
+ padding: "none",
60
+ className: cx(css.group, css[kind], className)
61
+ }, /*#__PURE__*/React.createElement(Box, {
62
+ padding: ['xsmall', 'small'],
63
+ className: css.groupHeader,
64
+ as: "button",
65
+ type: "button",
66
+ onClick: () => setShowBudgets(!showBudgets)
67
+ }, /*#__PURE__*/React.createElement(FlexStack, {
68
+ space: "xxsmall",
69
+ className: css.groupTitle
70
+ }, /*#__PURE__*/React.createElement(Icon, {
71
+ className: css.groupIcon,
72
+ glyph: isFailed ? Icon.ICONS.ALERT_CIRCLE : Icon.ICONS.CHECK_CIRCLE
73
+ }), /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement("strong", null, budgets.length), ` budget ${budgets.length > 1 ? 'checks' : 'check'} ${isFailed ? 'failed' : 'passed'}`), /*#__PURE__*/React.createElement("span", {
74
+ className: css.groupTitleToggle
75
+ }, showBudgets ? 'hide' : 'show', " all"))), showBudgets && /*#__PURE__*/React.createElement(Box, {
76
+ padding: ['xsmall', 'small'],
77
+ className: css.groupContent
78
+ }, /*#__PURE__*/React.createElement(Stack, {
79
+ space: "none"
80
+ }, budgets.map(_ref => {
81
+ let [key, budget] = _ref;
82
+ return /*#__PURE__*/React.createElement(Budget, {
83
+ key: key,
84
+ metricId: `${source}.${key}`,
85
+ budgetInsight: budget,
86
+ CustomLink: CustomLink
87
+ });
88
+ }))));
89
+ };
90
+
91
+ BudgetsGroup.propTypes = {
92
+ className: PropTypes.string,
93
+ kind: PropTypes.oneOf(['success', 'danger']).isRequired,
94
+ source: PropTypes.string.isRequired,
95
+ budgets: PropTypes.object.isRequired,
96
+ // eslint-disable-line react/forbid-prop-types
97
+ CustomLink: PropTypes.elementType.isRequired
98
+ };
99
+ BudgetsGroup.defaultProps = {
100
+ className: ''
101
+ };
102
+ export const BudgetInsights = props => {
103
+ const {
104
+ className,
105
+ source,
106
+ budgets,
107
+ CustomLink
108
+ } = props;
109
+ const [failedBudgets, passedBudgets] = useMemo(() => {
110
+ const passed = [];
111
+ const failed = [];
112
+ Object.entries(budgets).forEach(_ref2 => {
113
+ let [key, budget] = _ref2;
114
+
115
+ if (budget.failed) {
116
+ failed.push([key, budget]);
117
+ } else {
118
+ passed.push([key, budget]);
119
+ }
120
+ });
121
+ return [failed, passed];
122
+ }, [budgets]);
123
+ return /*#__PURE__*/React.createElement(Stack, {
124
+ className: className,
125
+ space: "xsmall"
126
+ }, failedBudgets.length > 0 && /*#__PURE__*/React.createElement(BudgetsGroup, {
127
+ kind: "danger",
128
+ source: source,
129
+ budgets: failedBudgets,
130
+ CustomLink: CustomLink
131
+ }), passedBudgets.length > 0 && /*#__PURE__*/React.createElement(BudgetsGroup, {
132
+ kind: "success",
133
+ source: source,
134
+ budgets: passedBudgets,
135
+ CustomLink: CustomLink
136
+ }));
137
+ };
138
+ BudgetInsights.propTypes = {
139
+ className: PropTypes.string,
140
+ source: PropTypes.string.isRequired,
141
+ budgets: PropTypes.object.isRequired,
142
+ // eslint-disable-line react/forbid-prop-types
143
+ CustomLink: PropTypes.elementType
144
+ };
145
+ BudgetInsights.defaultProps = {
146
+ className: '',
147
+ CustomLink: ComponentLink
148
+ };
@@ -0,0 +1,53 @@
1
+ .groupHeader {
2
+ width: 100%;
3
+ border: none;
4
+ background: none;
5
+ cursor: pointer;
6
+ color: inherit;
7
+ }
8
+
9
+ .groupTitle {
10
+ width: 100%;
11
+ align-items: center;
12
+ }
13
+
14
+ .groupTitle:hover {
15
+ color: var(--color-text-dark);
16
+ text-decoration: underline;
17
+ }
18
+
19
+ .groupTitleToggle {
20
+ margin: 0 0 0 auto !important;
21
+ }
22
+
23
+ .danger .groupIcon {
24
+ color: var(--color-danger);
25
+ }
26
+
27
+ .success .groupIcon {
28
+ color: var(--color-success);
29
+ }
30
+
31
+ .groupContent {
32
+ padding-top: 0px;
33
+ background: transparent;
34
+ }
35
+
36
+ .budget {
37
+ display: block;
38
+ color: var(--color-text);
39
+ text-decoration: none;
40
+ }
41
+
42
+ .budgetLabel,
43
+ .budgetValue {
44
+ color: var(--color-text-dark);
45
+ font-weight: 500;
46
+ }
47
+
48
+ .budget:hover,
49
+ .budget:focus,
50
+ .budget:active {
51
+ color: var(--color-text);
52
+ text-decoration: underline;
53
+ }
@@ -0,0 +1,84 @@
1
+ import React from 'react';
2
+ import { MemoryRouter } from 'react-router-dom';
3
+ import { BudgetInsights } from '.';
4
+ export default {
5
+ title: 'Components/BudgetInsights',
6
+ component: BudgetInsights,
7
+ decorators: [Story => /*#__PURE__*/React.createElement(MemoryRouter, null, /*#__PURE__*/React.createElement(Story, null))]
8
+ };
9
+
10
+ const Template = args => /*#__PURE__*/React.createElement(BudgetInsights, args);
11
+
12
+ export const Default = Template.bind();
13
+ Default.args = {
14
+ source: 'webpack',
15
+ budgets: {
16
+ totalSizeByTypeALL: {
17
+ currentValue: 2097917,
18
+ budgetValue: 2097152,
19
+ failed: true
20
+ },
21
+ totalInitialSizeJS: {
22
+ currentValue: 1265645,
23
+ budgetValue: 524288,
24
+ failed: true
25
+ },
26
+ totalInitialSizeCSS: {
27
+ currentValue: 48866,
28
+ budgetValue: 51200,
29
+ failed: false
30
+ },
31
+ chunkCount: {
32
+ currentValue: 12,
33
+ budgetValue: 50,
34
+ failed: false
35
+ },
36
+ moduleCount: {
37
+ currentValue: 1059,
38
+ budgetValue: 2000,
39
+ failed: false
40
+ },
41
+ duplicatePackagesCount: {
42
+ currentValue: 13,
43
+ budgetValue: 5,
44
+ failed: true
45
+ },
46
+ 'sizes.totalSizeByTypeJS': {
47
+ currentValue: 1981470,
48
+ budgetValue: 2097152,
49
+ failed: false
50
+ },
51
+ 'sizes.totalSizeByTypeCSS': {
52
+ currentValue: 56091,
53
+ budgetValue: 20480,
54
+ failed: true
55
+ }
56
+ }
57
+ };
58
+ export const FailedBudgets = Template.bind();
59
+ FailedBudgets.args = {
60
+ source: 'webpack',
61
+ budgets: {
62
+ totalSizeByTypeALL: {
63
+ currentValue: 2097917,
64
+ budgetValue: 2097152,
65
+ failed: true
66
+ },
67
+ totalInitialSizeJS: {
68
+ currentValue: 1265645,
69
+ budgetValue: 524288,
70
+ failed: true
71
+ }
72
+ }
73
+ };
74
+ export const SingleBudget = Template.bind();
75
+ SingleBudget.args = {
76
+ source: 'webpack',
77
+ budgets: {
78
+ totalInitialSizeJS: {
79
+ currentValue: 524288,
80
+ budgetValue: 1265645,
81
+ failed: false
82
+ }
83
+ }
84
+ };
@@ -0,0 +1 @@
1
+ export * from './budget-insights';
@@ -1,35 +1,52 @@
1
+ import _get from "lodash/get";
2
+
1
3
  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
4
 
3
- import React from 'react';
5
+ import React, { useCallback, useMemo } from 'react';
4
6
  import PropTypes from 'prop-types';
5
7
  import { getBundleAssetsFileTypeComponentLink } from '@bundle-stats/utils';
6
8
  import * as webpack from '@bundle-stats/utils/lib-esm/webpack';
7
9
  import { ASSETS_SIZES_FILE_TYPE_MAP } from '../../constants';
10
+ import { FlexStack } from '../../layout/flex-stack';
8
11
  import { MetricsTable } from '../metrics-table';
9
12
  import { ComponentLink } from '../component-link';
13
+ import { BudgetInfo } from '../budget-info';
14
+ import css from './bundle-assets-totals-table.module.css';
10
15
  export const BundleAssetsTotalsTable = _ref => {
16
+ var _jobs$0$insights, _jobs$0$insights$webp;
17
+
11
18
  let {
12
19
  className,
13
20
  jobs,
14
21
  customComponentLink: CustomComponentLink,
15
22
  ...restProps
16
23
  } = _ref;
17
- const items = webpack.compareBySection.sizes(jobs);
18
-
19
- const renderRowHeader = item => {
24
+ const items = useMemo(() => webpack.compareBySection.sizes(jobs), [jobs]);
25
+ const budgets = (_jobs$0$insights = jobs[0].insights) == null ? void 0 : (_jobs$0$insights$webp = _jobs$0$insights.webpack) == null ? void 0 : _jobs$0$insights$webp.budgets;
26
+ const renderRowHeader = useCallback(item => {
20
27
  const fileType = ASSETS_SIZES_FILE_TYPE_MAP[item.key];
21
28
  const {
22
29
  section,
23
30
  title,
24
31
  params
25
32
  } = getBundleAssetsFileTypeComponentLink(fileType, item.label);
33
+ const [_, ...metricSlugs] = item.key.split('.');
34
+ const metricKey = metricSlugs.join('.');
35
+
36
+ const budget = _get(budgets, metricKey);
37
+
26
38
  return /*#__PURE__*/React.createElement(CustomComponentLink, {
27
39
  section: section,
28
40
  title: title,
29
41
  params: params
30
- }, item.label);
31
- };
32
-
42
+ }, /*#__PURE__*/React.createElement(FlexStack, {
43
+ space: "xxsmall",
44
+ className: css.itemTitleText
45
+ }, /*#__PURE__*/React.createElement("span", null, item.label), budget && /*#__PURE__*/React.createElement(BudgetInfo, {
46
+ metricId: item.key,
47
+ budgetInsight: budget
48
+ })));
49
+ }, [CustomComponentLink]);
33
50
  return /*#__PURE__*/React.createElement(MetricsTable, _extends({
34
51
  className: className,
35
52
  runs: jobs,
@@ -1,3 +1,3 @@
1
- .title {
2
- color: var(--color-text-light);
1
+ .itemTitleText {
2
+ align-items: center;
3
3
  }