@bundle-stats/ui 3.2.2 → 3.3.0-alpha.0

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 (99) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/lib/app/app.js +31 -6
  3. package/lib/app/app.stories.js +24 -3
  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/browsertime-table/browsertime-table.js +1 -1
  8. package/lib/components/browsertime-table/browsertime-table.stories.js +1 -1
  9. package/lib/components/budget-info/budget-info.js +53 -0
  10. package/lib/components/budget-info/budget-info.module.css +28 -0
  11. package/lib/components/budget-info/budget-info.stories.js +42 -0
  12. package/lib/components/budget-info/index.js +18 -0
  13. package/lib/components/budget-insights/budget-insights.js +200 -0
  14. package/lib/components/budget-insights/budget-insights.module.css +53 -0
  15. package/lib/components/budget-insights/budget-insights.stories.js +104 -0
  16. package/lib/components/budget-insights/index.js +18 -0
  17. package/lib/components/bundle-assets/bundle-assets.js +3 -3
  18. package/lib/components/bundle-assets/bundle-assets.utils.js +2 -2
  19. package/lib/components/bundle-assets/index.js +3 -3
  20. package/lib/components/bundle-assets-totals-chart-bars/bundle-assets-totals-chart-bars.js +1 -1
  21. package/lib/components/bundle-assets-totals-table/bundle-assets-totals-table.js +45 -8
  22. package/lib/components/bundle-assets-totals-table/bundle-assets-totals-table.module.css +2 -2
  23. package/lib/components/bundle-assets-totals-table/bundle-assets-totals-table.stories.js +20 -1
  24. package/lib/components/bundle-modules/bundle-modules.js +3 -3
  25. package/lib/components/bundle-modules/bundle-modules.utils.js +2 -2
  26. package/lib/components/bundle-modules/index.js +3 -3
  27. package/lib/components/bundle-packages/bundle-packages.js +1 -1
  28. package/lib/components/bundle-packages/bundle-packages.utils.js +2 -2
  29. package/lib/components/bundle-packages/index.js +1 -1
  30. package/lib/components/lighthouse-table/lighthouse-table.js +1 -1
  31. package/lib/components/metrics-table/metrics-table.js +1 -1
  32. package/lib/components/metrics-table-search/metrics-table-search.js +1 -1
  33. package/lib/components/metrics-table-search/metrics-table-search.stories.js +1 -1
  34. package/lib/components/summary/summary.js +12 -7
  35. package/lib/components/summary/summary.module.css +1 -1
  36. package/lib/components/summary/summary.stories.js +29 -0
  37. package/lib/components/summary-item/summary-item.js +22 -5
  38. package/lib/components/summary-item/summary-item.module.css +15 -0
  39. package/lib/components/summary-item/summary-item.stories.js +51 -53
  40. package/lib/constants.js +6 -6
  41. package/lib/hooks/search-params.js +2 -2
  42. package/lib/query-state.js +1 -1
  43. package/lib/stories/wrapper.js +2 -2
  44. package/lib/ui/alert/alert.js +13 -3
  45. package/lib/ui/filters/filters.container.js +5 -5
  46. package/lib/ui/filters/filters.js +2 -2
  47. package/lib/ui/filters/filters.stories.js +3 -3
  48. package/lib/ui/horizontal-bar-chart/horizontal-bar-chart.js +3 -3
  49. package/lib/ui/icon/icon.js +2 -0
  50. package/lib/ui/panels/panels.js +2 -2
  51. package/lib/ui/popover/popover.js +1 -1
  52. package/lib/ui/tabs/tabs.js +2 -2
  53. package/lib/utils.js +2 -2
  54. package/lib-esm/app/app.js +57 -31
  55. package/lib-esm/app/app.stories.js +22 -1
  56. package/lib-esm/assets/icons/alert-circle.svg +1 -0
  57. package/lib-esm/assets/icons/check-circle.svg +1 -0
  58. package/lib-esm/assets/icons.svg +8 -0
  59. package/lib-esm/components/budget-info/budget-info.js +37 -0
  60. package/lib-esm/components/budget-info/budget-info.module.css +28 -0
  61. package/lib-esm/components/budget-info/budget-info.stories.js +26 -0
  62. package/lib-esm/components/budget-info/index.js +1 -0
  63. package/lib-esm/components/budget-insights/budget-insights.js +147 -0
  64. package/lib-esm/components/budget-insights/budget-insights.module.css +53 -0
  65. package/lib-esm/components/budget-insights/budget-insights.stories.js +84 -0
  66. package/lib-esm/components/budget-insights/index.js +1 -0
  67. package/lib-esm/components/bundle-assets-totals-table/bundle-assets-totals-table.js +24 -7
  68. package/lib-esm/components/bundle-assets-totals-table/bundle-assets-totals-table.module.css +2 -2
  69. package/lib-esm/components/bundle-assets-totals-table/bundle-assets-totals-table.stories.js +18 -0
  70. package/lib-esm/components/summary/summary.js +11 -7
  71. package/lib-esm/components/summary/summary.module.css +1 -1
  72. package/lib-esm/components/summary/summary.stories.js +27 -0
  73. package/lib-esm/components/summary-item/summary-item.js +20 -4
  74. package/lib-esm/components/summary-item/summary-item.module.css +15 -0
  75. package/lib-esm/components/summary-item/summary-item.stories.js +50 -38
  76. package/lib-esm/constants.js +4 -4
  77. package/lib-esm/ui/alert/alert.js +6 -3
  78. package/lib-esm/ui/filters/filters.container.js +39 -41
  79. package/lib-esm/ui/icon/icon.js +2 -0
  80. package/package.json +3 -3
  81. package/src/app/app.jsx +24 -8
  82. package/src/assets/icons/alert-circle.svg +1 -0
  83. package/src/assets/icons/check-circle.svg +1 -0
  84. package/src/assets/icons.svg +8 -0
  85. package/src/components/budget-info/budget-info.jsx +49 -0
  86. package/src/components/budget-info/budget-info.module.css +28 -0
  87. package/src/components/budget-info/index.js +1 -0
  88. package/src/components/budget-insights/budget-insights.jsx +152 -0
  89. package/src/components/budget-insights/budget-insights.module.css +53 -0
  90. package/src/components/budget-insights/index.js +1 -0
  91. package/src/components/bundle-assets-totals-table/bundle-assets-totals-table.jsx +28 -13
  92. package/src/components/bundle-assets-totals-table/bundle-assets-totals-table.module.css +2 -2
  93. package/src/components/summary/summary.jsx +8 -7
  94. package/src/components/summary/summary.module.css +1 -1
  95. package/src/components/summary-item/summary-item.jsx +13 -2
  96. package/src/components/summary-item/summary-item.module.css +15 -0
  97. package/src/constants.js +7 -7
  98. package/src/ui/alert/alert.jsx +7 -2
  99. package/src/ui/icon/icon.jsx +2 -0
@@ -1,6 +1,6 @@
1
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
2
 
3
- import React, { useEffect } from 'react';
3
+ import React, { useEffect, useMemo } from 'react';
4
4
  import PropTypes from 'prop-types';
5
5
  import { HashRouter, NavLink, Route, Switch, useLocation } from 'react-router-dom';
6
6
  import { COMPONENT } from '@bundle-stats/utils';
@@ -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';
@@ -61,6 +62,8 @@ Layout.defaultProps = {
61
62
  };
62
63
 
63
64
  const AppComponent = _ref2 => {
65
+ var _insights$webpack;
66
+
64
67
  let {
65
68
  version,
66
69
  jobs
@@ -77,9 +80,13 @@ const AppComponent = _ref2 => {
77
80
  }, I18N.NO_DATA)));
78
81
  }
79
82
 
83
+ const {
84
+ summary,
85
+ insights
86
+ } = useMemo(() => jobs[0], jobs);
80
87
  const {
81
88
  duplicatePackagesCount
82
- } = jobs[0].summary.webpack;
89
+ } = summary.webpack;
83
90
  const duplicatePackagesInsights = Boolean(duplicatePackagesCount.current || duplicatePackagesCount.baseline);
84
91
  return /*#__PURE__*/React.createElement(Layout, {
85
92
  jobs: jobs,
@@ -89,7 +96,8 @@ const AppComponent = _ref2 => {
89
96
  }, /*#__PURE__*/React.createElement(Summary, {
90
97
  size: "large",
91
98
  keys: METRICS_WEBPACK_GENERAL,
92
- data: jobs[0].summary,
99
+ data: summary,
100
+ budgets: insights == null ? void 0 : (_insights$webpack = insights.webpack) == null ? void 0 : _insights$webpack.budgets,
93
101
  showSummaryItemDelta: jobs.length !== 1
94
102
  })), /*#__PURE__*/React.createElement(Container, {
95
103
  className: css.tabsContainer
@@ -117,6 +125,8 @@ const AppComponent = _ref2 => {
117
125
  exact: true,
118
126
  path: URLS.ASSETS,
119
127
  render: _ref3 => {
128
+ var _insights$webpack2;
129
+
120
130
  let {
121
131
  location
122
132
  } = _ref3;
@@ -124,7 +134,8 @@ const AppComponent = _ref2 => {
124
134
  space: "medium"
125
135
  }, /*#__PURE__*/React.createElement(Summary, {
126
136
  keys: METRICS_WEBPACK_ASSETS,
127
- data: jobs[0].summary,
137
+ data: summary,
138
+ budgets: insights == null ? void 0 : (_insights$webpack2 = insights.webpack) == null ? void 0 : _insights$webpack2.budgets,
128
139
  showSummaryItemDelta: jobs.length !== 1
129
140
  }), /*#__PURE__*/React.createElement(Box, {
130
141
  outline: true
@@ -138,22 +149,29 @@ const AppComponent = _ref2 => {
138
149
  }), /*#__PURE__*/React.createElement(Route, {
139
150
  exact: true,
140
151
  path: URLS.MODULES,
141
- render: () => /*#__PURE__*/React.createElement(Container, null, /*#__PURE__*/React.createElement(Stack, {
142
- space: "medium"
143
- }, /*#__PURE__*/React.createElement(Summary, {
144
- keys: METRICS_WEBPACK_MODULES,
145
- data: jobs[0].summary,
146
- showSummaryItemDelta: jobs.length !== 1
147
- }), /*#__PURE__*/React.createElement(Box, {
148
- outline: true
149
- }, /*#__PURE__*/React.createElement(BundleModules, _extends({
150
- jobs: jobs,
151
- setState: bundleModulesSetState
152
- }, bundleModulesState)))))
152
+ render: () => {
153
+ var _insights$webpack3;
154
+
155
+ return /*#__PURE__*/React.createElement(Container, null, /*#__PURE__*/React.createElement(Stack, {
156
+ space: "medium"
157
+ }, /*#__PURE__*/React.createElement(Summary, {
158
+ keys: METRICS_WEBPACK_MODULES,
159
+ data: summary,
160
+ budgets: insights == null ? void 0 : (_insights$webpack3 = insights.webpack) == null ? void 0 : _insights$webpack3.budgets,
161
+ showSummaryItemDelta: jobs.length !== 1
162
+ }), /*#__PURE__*/React.createElement(Box, {
163
+ outline: true
164
+ }, /*#__PURE__*/React.createElement(BundleModules, _extends({
165
+ jobs: jobs,
166
+ setState: bundleModulesSetState
167
+ }, bundleModulesState)))));
168
+ }
153
169
  }), /*#__PURE__*/React.createElement(Route, {
154
170
  exact: true,
155
171
  path: URLS.PACKAGES,
156
172
  render: _ref4 => {
173
+ var _insights$webpack4;
174
+
157
175
  let {
158
176
  location
159
177
  } = _ref4;
@@ -161,7 +179,8 @@ const AppComponent = _ref2 => {
161
179
  space: "medium"
162
180
  }, /*#__PURE__*/React.createElement(Summary, {
163
181
  keys: METRICS_WEBPACK_PACKAGES,
164
- data: jobs[0].summary,
182
+ data: summary,
183
+ budgets: insights == null ? void 0 : (_insights$webpack4 = insights.webpack) == null ? void 0 : _insights$webpack4.budgets,
165
184
  showSummaryItemDelta: jobs.length !== 1
166
185
  }), /*#__PURE__*/React.createElement(Box, {
167
186
  outline: true
@@ -175,20 +194,27 @@ const AppComponent = _ref2 => {
175
194
  }), /*#__PURE__*/React.createElement(Route, {
176
195
  exact: true,
177
196
  path: URLS.OVERVIEW,
178
- render: () => /*#__PURE__*/React.createElement(Stack, {
179
- space: "large"
180
- }, duplicatePackagesInsights && /*#__PURE__*/React.createElement(Container, null, /*#__PURE__*/React.createElement(DuplicatePackagesWarning, {
181
- duplicatePackagesCount: duplicatePackagesCount,
182
- showDelta: jobs.length > 1
183
- })), /*#__PURE__*/React.createElement(Container, null, /*#__PURE__*/React.createElement(Stack, {
184
- space: "small"
185
- }, /*#__PURE__*/React.createElement(TotalSizeTypeTitle, null), /*#__PURE__*/React.createElement(BundleAssetsTotalsChartBars, {
186
- jobs: jobs
187
- }), /*#__PURE__*/React.createElement(Box, {
188
- outline: true
189
- }, /*#__PURE__*/React.createElement(BundleAssetsTotalsTable, {
190
- jobs: jobs
191
- })))))
197
+ render: () => {
198
+ var _insights$webpack5;
199
+
200
+ return /*#__PURE__*/React.createElement(Stack, {
201
+ space: "large"
202
+ }, duplicatePackagesInsights && /*#__PURE__*/React.createElement(Container, null, /*#__PURE__*/React.createElement(DuplicatePackagesWarning, {
203
+ duplicatePackagesCount: duplicatePackagesCount,
204
+ showDelta: jobs.length > 1
205
+ })), (insights == null ? void 0 : (_insights$webpack5 = insights.webpack) == null ? void 0 : _insights$webpack5.budgets) && /*#__PURE__*/React.createElement(Container, null, /*#__PURE__*/React.createElement(BudgetInsights, {
206
+ source: "webpack",
207
+ budgets: insights.webpack.budgets
208
+ })), /*#__PURE__*/React.createElement(Container, null, /*#__PURE__*/React.createElement(Stack, {
209
+ space: "small"
210
+ }, /*#__PURE__*/React.createElement(TotalSizeTypeTitle, null), /*#__PURE__*/React.createElement(BundleAssetsTotalsChartBars, {
211
+ jobs: jobs
212
+ }), /*#__PURE__*/React.createElement(Box, {
213
+ outline: true
214
+ }, /*#__PURE__*/React.createElement(BundleAssetsTotalsTable, {
215
+ jobs: jobs
216
+ })))));
217
+ }
192
218
  }))));
193
219
  };
194
220
 
@@ -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,37 @@
1
+ import React from 'react';
2
+ import cx from 'classnames';
3
+ import PropTypes from 'prop-types';
4
+ import { Icon } from '../../ui/icon';
5
+ import { Tooltip } from '../../ui/tooltip';
6
+ import css from './budget-info.module.css';
7
+ export const BudgetInfo = _ref => {
8
+ let {
9
+ className = '',
10
+ budget,
11
+ metric
12
+ } = _ref;
13
+ const rootClassName = cx(css.root, budget.failed ? css.over : css.under, className);
14
+ return /*#__PURE__*/React.createElement(Tooltip, {
15
+ className: rootClassName,
16
+ title: /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("strong", null, metric.label), ` is ${budget.failed ? 'over' : 'under'} `, metric.formatter(budget.budgetValue), ` budget`)
17
+ }, /*#__PURE__*/React.createElement(Icon, {
18
+ className: css.icon,
19
+ glyph: budget.failed ? Icon.ICONS.ALERT_CIRCLE : Icon.ICONS.CHECK_CIRCLE,
20
+ size: "medium"
21
+ }));
22
+ };
23
+ BudgetInfo.propTypes = {
24
+ budget: PropTypes.shape({
25
+ currentValue: PropTypes.number,
26
+ budgetValue: PropTypes.number,
27
+ failed: PropTypes.bool
28
+ }).isRequired,
29
+ metric: PropTypes.shape({
30
+ label: PropTypes.string,
31
+ formatter: PropTypes.func
32
+ }).isRequired,
33
+ className: PropTypes.string
34
+ };
35
+ BudgetInfo.defaultProps = {
36
+ className: ''
37
+ };
@@ -0,0 +1,28 @@
1
+ .root {
2
+ display: inline-block;
3
+ line-height: 1em;
4
+ }
5
+
6
+ /* Budget over */
7
+ .over .icon {
8
+ color: var(--color-danger-light);
9
+ }
10
+
11
+ .over .icon:hover,
12
+ .over .icon:focus,
13
+ .over .icon:active
14
+ {
15
+ color: var(--color-danger);
16
+ }
17
+
18
+ /* Budget under */
19
+ .under .icon {
20
+ color: var(--color-success-light);
21
+ }
22
+
23
+ .under .icon:hover,
24
+ .under .icon:focus,
25
+ .under .icon:active
26
+ {
27
+ color: var(--color-success);
28
+ }
@@ -0,0 +1,26 @@
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
+ budget: {
9
+ budget: 300,
10
+ value: 250,
11
+ overBudget: false
12
+ },
13
+ metric: {
14
+ formatter: v => v
15
+ }
16
+ }));
17
+ stories.add('over', () => /*#__PURE__*/React.createElement(BudgetInfo, {
18
+ budget: {
19
+ budget: 300,
20
+ value: 350,
21
+ overBudget: true
22
+ },
23
+ metric: {
24
+ formatter: v => v
25
+ }
26
+ }));
@@ -0,0 +1 @@
1
+ export * from './budget-info';
@@ -0,0 +1,147 @@
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, getGlobalMetricType } from '@bundle-stats/utils';
7
+ import { Box } from '../../layout/box';
8
+ import { Stack } from '../../layout/stack';
9
+ import { FlexStack } from '../../layout/flex-stack';
10
+ import { Alert } from '../../ui/alert';
11
+ import { Icon } from '../../ui/icon';
12
+ import { ComponentLink } from '../component-link';
13
+ import css from './budget-insights.module.css';
14
+
15
+ const Budget = props => {
16
+ const {
17
+ className,
18
+ metricKey,
19
+ CustomLink,
20
+ failed,
21
+ budgetValue
22
+ } = props;
23
+ const metric = getGlobalMetricType(metricKey);
24
+ const componentLinkOptions = METRIC_COMPONENT_LINKS.get(metricKey);
25
+ return /*#__PURE__*/React.createElement(CustomLink, _extends({
26
+ className: cx(css.budget, className)
27
+ }, componentLinkOptions == null ? void 0 : componentLinkOptions.link), /*#__PURE__*/React.createElement("span", {
28
+ className: css.budgetLabel
29
+ }, metric.label), ` is ${failed ? 'over' : 'under'} `, /*#__PURE__*/React.createElement("span", {
30
+ className: css.budgetValue
31
+ }, metric.formatter(budgetValue)), ` budget`);
32
+ };
33
+
34
+ Budget.propTypes = {
35
+ className: PropTypes.string,
36
+ metricKey: PropTypes.string.isRequired,
37
+ failed: PropTypes.bool.isRequired,
38
+ currentValue: PropTypes.number.isRequired,
39
+ budgetValue: PropTypes.number.isRequired,
40
+ CustomLink: PropTypes.elementType.isRequired
41
+ };
42
+ Budget.defaultProps = {
43
+ className: ''
44
+ };
45
+
46
+ const BudgetsGroup = props => {
47
+ const {
48
+ className,
49
+ kind,
50
+ source,
51
+ budgets,
52
+ CustomLink
53
+ } = props;
54
+ const [showBudgets, setShowBudgets] = useState(false);
55
+ const isFailed = kind === 'danger';
56
+ return /*#__PURE__*/React.createElement(Alert, {
57
+ kind: kind,
58
+ padding: "none",
59
+ className: cx(css.group, css[kind], className)
60
+ }, /*#__PURE__*/React.createElement(Box, {
61
+ padding: ['xsmall', 'small'],
62
+ className: css.groupHeader,
63
+ as: "button",
64
+ type: "button",
65
+ onClick: () => setShowBudgets(!showBudgets)
66
+ }, /*#__PURE__*/React.createElement(FlexStack, {
67
+ space: "xxsmall",
68
+ className: css.groupTitle
69
+ }, /*#__PURE__*/React.createElement(Icon, {
70
+ className: css.groupIcon,
71
+ glyph: isFailed ? Icon.ICONS.ALERT_CIRCLE : Icon.ICONS.CHECK_CIRCLE
72
+ }), /*#__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", {
73
+ className: css.groupTitleToggle
74
+ }, showBudgets ? 'hide' : 'show', " all"))), showBudgets && /*#__PURE__*/React.createElement(Box, {
75
+ padding: ['xsmall', 'small'],
76
+ className: css.groupContent
77
+ }, /*#__PURE__*/React.createElement(Stack, {
78
+ space: "none"
79
+ }, budgets.map(_ref => {
80
+ let [key, budget] = _ref;
81
+ return /*#__PURE__*/React.createElement(Budget, _extends({
82
+ key: key,
83
+ source: source,
84
+ metricKey: `${source}.${key}`,
85
+ CustomLink: CustomLink
86
+ }, budget));
87
+ }))));
88
+ };
89
+
90
+ BudgetsGroup.propTypes = {
91
+ className: PropTypes.string,
92
+ kind: PropTypes.oneOf(['success', 'danger']).isRequired,
93
+ source: PropTypes.string.isRequired,
94
+ budgets: PropTypes.object.isRequired,
95
+ // eslint-disable-line react/forbid-prop-types
96
+ CustomLink: PropTypes.elementType.isRequired
97
+ };
98
+ BudgetsGroup.defaultProps = {
99
+ className: ''
100
+ };
101
+ export const BudgetInsights = props => {
102
+ const {
103
+ className,
104
+ source,
105
+ budgets,
106
+ CustomLink
107
+ } = props;
108
+ const [failedBudgets, passedBudgets] = useMemo(() => {
109
+ const passed = [];
110
+ const failed = [];
111
+ Object.entries(budgets).forEach(_ref2 => {
112
+ let [key, budget] = _ref2;
113
+
114
+ if (budget.failed) {
115
+ failed.push([key, budget]);
116
+ } else {
117
+ passed.push([key, budget]);
118
+ }
119
+ });
120
+ return [failed, passed];
121
+ }, [budgets]);
122
+ return /*#__PURE__*/React.createElement(Stack, {
123
+ className: className,
124
+ space: "xsmall"
125
+ }, failedBudgets.length > 0 && /*#__PURE__*/React.createElement(BudgetsGroup, {
126
+ kind: "danger",
127
+ source: source,
128
+ budgets: failedBudgets,
129
+ CustomLink: CustomLink
130
+ }), passedBudgets.length > 0 && /*#__PURE__*/React.createElement(BudgetsGroup, {
131
+ kind: "success",
132
+ source: source,
133
+ budgets: passedBudgets,
134
+ CustomLink: CustomLink
135
+ }));
136
+ };
137
+ BudgetInsights.propTypes = {
138
+ className: PropTypes.string,
139
+ source: PropTypes.string.isRequired,
140
+ budgets: PropTypes.object.isRequired,
141
+ // eslint-disable-line react/forbid-prop-types
142
+ CustomLink: PropTypes.elementType
143
+ };
144
+ BudgetInsights.defaultProps = {
145
+ className: '',
146
+ CustomLink: ComponentLink
147
+ };
@@ -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
+ value: 2097917,
18
+ budget: 2097152,
19
+ overBudget: true
20
+ },
21
+ totalInitialSizeJS: {
22
+ value: 1265645,
23
+ budget: 524288,
24
+ overBudget: true
25
+ },
26
+ totalInitialSizeCSS: {
27
+ value: 48866,
28
+ budget: 51200,
29
+ overBudget: false
30
+ },
31
+ chunkCount: {
32
+ value: 12,
33
+ budget: 50,
34
+ overBudget: false
35
+ },
36
+ moduleCount: {
37
+ value: 1059,
38
+ budget: 2000,
39
+ overBudget: false
40
+ },
41
+ duplicatePackagesCount: {
42
+ value: 13,
43
+ budget: 5,
44
+ overBudget: true
45
+ },
46
+ 'sizes.totalSizeByTypeJS': {
47
+ value: 1981470,
48
+ budget: 2097152,
49
+ overBudget: false
50
+ },
51
+ 'sizes.totalSizeByTypeCSS': {
52
+ value: 56091,
53
+ budget: 20480,
54
+ overBudget: true
55
+ }
56
+ }
57
+ };
58
+ export const FailedBudgets = Template.bind();
59
+ FailedBudgets.args = {
60
+ source: 'webpack',
61
+ budgets: {
62
+ totalSizeByTypeALL: {
63
+ value: 2097917,
64
+ budget: 2097152,
65
+ overBudget: true
66
+ },
67
+ totalInitialSizeJS: {
68
+ value: 1265645,
69
+ budget: 524288,
70
+ overBudget: true
71
+ }
72
+ }
73
+ };
74
+ export const SingleBudget = Template.bind();
75
+ SingleBudget.args = {
76
+ source: 'webpack',
77
+ budgets: {
78
+ totalInitialSizeJS: {
79
+ value: 524288,
80
+ budget: 1265645,
81
+ overBudget: false
82
+ }
83
+ }
84
+ };
@@ -0,0 +1 @@
1
+ export * from './budget-insights';
@@ -1,32 +1,49 @@
1
- import React from 'react';
1
+ import _get from "lodash/get";
2
+ import React, { useCallback, useMemo } from 'react';
2
3
  import PropTypes from 'prop-types';
3
4
  import { getBundleAssetsFileTypeComponentLink } from '@bundle-stats/utils';
4
5
  import * as webpack from '@bundle-stats/utils/lib-esm/webpack';
5
6
  import { ASSETS_SIZES_FILE_TYPE_MAP } from '../../constants';
7
+ import { FlexStack } from '../../layout/flex-stack';
6
8
  import { MetricsTable } from '../metrics-table';
7
9
  import { ComponentLink } from '../component-link';
10
+ import { BudgetInfo } from '../budget-info';
11
+ import css from './bundle-assets-totals-table.module.css';
8
12
  export const BundleAssetsTotalsTable = _ref => {
13
+ var _jobs$0$insights, _jobs$0$insights$webp;
14
+
9
15
  let {
10
16
  className,
11
17
  jobs,
12
18
  customComponentLink: CustomComponentLink
13
19
  } = _ref;
14
- const items = webpack.compareBySection.sizes(jobs);
15
-
16
- const renderRowHeader = item => {
20
+ const items = useMemo(() => webpack.compareBySection.sizes(jobs), [jobs]);
21
+ 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;
22
+ const renderRowHeader = useCallback(item => {
17
23
  const fileType = ASSETS_SIZES_FILE_TYPE_MAP[item.key];
18
24
  const {
19
25
  section,
20
26
  title,
21
27
  params
22
28
  } = getBundleAssetsFileTypeComponentLink(fileType, item.label);
29
+ const [_, ...metricSlugs] = item.key.split('.');
30
+ const metricKey = metricSlugs.join('.');
31
+
32
+ const budget = _get(budgets, metricKey);
33
+
34
+ const metric = webpack.getMetricType(metricKey);
23
35
  return /*#__PURE__*/React.createElement(CustomComponentLink, {
24
36
  section: section,
25
37
  title: title,
26
38
  params: params
27
- }, item.label);
28
- };
29
-
39
+ }, /*#__PURE__*/React.createElement(FlexStack, {
40
+ space: "xxsmall",
41
+ className: css.itemTitleText
42
+ }, /*#__PURE__*/React.createElement("span", null, item.label), budget && /*#__PURE__*/React.createElement(BudgetInfo, {
43
+ budget: budget,
44
+ metric: metric
45
+ })));
46
+ }, [CustomComponentLink]);
30
47
  return /*#__PURE__*/React.createElement(MetricsTable, {
31
48
  className: className,
32
49
  runs: jobs,
@@ -1,3 +1,3 @@
1
- .title {
2
- color: var(--color-text-light);
1
+ .itemTitleText {
2
+ align-items: center;
3
3
  }