@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
@@ -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
  };
@@ -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
+ value: MULTIPLE_JOBS[0].summary.webpack.totalSizeByTypeALL,
30
+ budget: 1024 * 1024,
31
+ overBudget: false
32
+ },
33
+ totalInitialSizeJS: {
34
+ value: MULTIPLE_JOBS[0].summary.webpack.totalInitialSizeJS,
35
+ budget: 512 * 1024,
36
+ overBudget: true
37
+ },
38
+ totalInitialSizeCSS: {
39
+ value: MULTIPLE_JOBS[0].summary.webpack.totalInitialSizeCSS,
40
+ budget: 50 * 1024,
41
+ overBudget: false
42
+ },
43
+ chunkCount: {
44
+ value: MULTIPLE_JOBS[0].summary.webpack.chunkCount,
45
+ budget: 50,
46
+ overBudget: 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
@@ -10,6 +10,7 @@ import { Stack } from '../../layout/stack';
10
10
  import { FlexStack } from '../../layout/flex-stack';
11
11
  import { Metric } from '../metric';
12
12
  import { Delta } from '../delta';
13
+ import { BudgetInfo } from '../budget-info';
13
14
  import css from './summary-item.module.css';
14
15
 
15
16
  const MetricInfoPopover = _ref => {
@@ -49,6 +50,7 @@ export const SummaryItem = _ref2 => {
49
50
  loading,
50
51
  showDelta,
51
52
  showMetricDescription,
53
+ budget,
52
54
  ...props
53
55
  } = _ref2;
54
56
  const {
@@ -70,10 +72,16 @@ export const SummaryItem = _ref2 => {
70
72
  as: "h3",
71
73
  space: "xxxsmall",
72
74
  className: css.title
73
- }, /*#__PURE__*/React.createElement("span", null, metric.label), showMetricDescriptionTooltip && /*#__PURE__*/React.createElement(Popover, {
75
+ }, /*#__PURE__*/React.createElement("span", {
76
+ className: css.titleLabel
77
+ }, metric.label), showMetricDescriptionTooltip && /*#__PURE__*/React.createElement(Popover, {
74
78
  className: css.icon,
75
79
  icon: "help"
76
- }, /*#__PURE__*/React.createElement(MetricInfoPopover, metric))), !loading ? /*#__PURE__*/React.createElement(Stack, null, /*#__PURE__*/React.createElement(Metric, {
80
+ }, /*#__PURE__*/React.createElement(MetricInfoPopover, metric)), budget && /*#__PURE__*/React.createElement(BudgetInfo, {
81
+ className: css.budgetInfo,
82
+ metric: metric,
83
+ budget: budget
84
+ })), !loading ? /*#__PURE__*/React.createElement(Stack, null, /*#__PURE__*/React.createElement(Metric, {
77
85
  className: css.currentMetric,
78
86
  value: current,
79
87
  formatter: metric.formatter,
@@ -100,7 +108,8 @@ SummaryItem.defaultProps = {
100
108
  size: 'medium',
101
109
  loading: false,
102
110
  showMetricDescription: false,
103
- showDelta: true
111
+ showDelta: true,
112
+ budget: undefined
104
113
  };
105
114
  SummaryItem.propTypes = {
106
115
  /** Adopted child class name */
@@ -126,5 +135,12 @@ SummaryItem.propTypes = {
126
135
  showMetricDescription: PropTypes.bool,
127
136
 
128
137
  /** Show delta */
129
- showDelta: PropTypes.bool
138
+ showDelta: PropTypes.bool,
139
+
140
+ /** Budget data */
141
+ budget: PropTypes.shape({
142
+ value: PropTypes.number,
143
+ budget: PropTypes.number,
144
+ overBudget: PropTypes.bool
145
+ })
130
146
  };
@@ -1,3 +1,8 @@
1
+ .root {
2
+ position: relative;
3
+ padding-right: var(--space-xlarge);
4
+ }
5
+
1
6
  .title {
2
7
  color: var(--color-text-ultra-light);
3
8
  font-weight: bold;
@@ -7,6 +12,10 @@
7
12
  white-space: nowrap;
8
13
  }
9
14
 
15
+ .titleLabel {
16
+ flex: 0 0 auto;
17
+ }
18
+
10
19
  .readMoreBtn {
11
20
  appearance: none;
12
21
  padding: 0;
@@ -26,6 +35,12 @@
26
35
  text-decoration: none;
27
36
  }
28
37
 
38
+ .budgetInfo {
39
+ position: absolute;
40
+ right: 0;
41
+ top: 0;
42
+ }
43
+
29
44
  .currentMetric {
30
45
  display: block;
31
46
  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 BudgetOver = Template.bind();
37
+ BudgetOver.args = {
38
+ showMetricDescription: true,
39
+ budget: {
40
+ value: 120 * 1000,
41
+ budget: 100 * 1000,
42
+ overBudget: true
43
+ }
44
+ };
45
+ export const BudgetUnder = Template.bind();
46
+ BudgetUnder.args = {
47
+ showMetricDescription: true,
48
+ budget: {
49
+ value: 120 * 1000,
50
+ budget: 120 * 1024,
51
+ overBudget: 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,
@@ -28,45 +28,6 @@ export const filtersDropdownContainer = BaseComponent => {
28
28
  class FiltersContainer extends React.Component {
29
29
  constructor(props) {
30
30
  super(props);
31
-
32
- this.toggleFilter = (key, value) => this.setState(_ref2 => {
33
- let {
34
- values,
35
- ...state
36
- } = _ref2;
37
- const newState = { ...state,
38
- values: { ...values,
39
- [key]: value
40
- }
41
- };
42
- const {
43
- onChange
44
- } = this.props;
45
-
46
- if (onChange) {
47
- onChange(newState.values);
48
- }
49
-
50
- return newState;
51
- });
52
-
53
- this.toggleFilters = newFilters => this.setState(prevState => {
54
- const newState = { ...prevState,
55
- values: { ...prevState.values,
56
- ...newFilters
57
- }
58
- };
59
- const {
60
- onChange
61
- } = this.props;
62
-
63
- if (onChange) {
64
- onChange(newState.values);
65
- }
66
-
67
- return newState;
68
- });
69
-
70
31
  const {
71
32
  filters
72
33
  } = this.props;
@@ -75,10 +36,10 @@ export const filtersDropdownContainer = BaseComponent => {
75
36
  };
76
37
  }
77
38
 
78
- static getDerivedStateFromProps(_ref3, state) {
39
+ static getDerivedStateFromProps(_ref2, state) {
79
40
  let {
80
41
  filters
81
- } = _ref3;
42
+ } = _ref2;
82
43
 
83
44
  if (_isEqual(filters, state.values)) {
84
45
  return null;
@@ -89,6 +50,43 @@ export const filtersDropdownContainer = BaseComponent => {
89
50
  };
90
51
  }
91
52
 
53
+ toggleFilter = (key, value) => this.setState(_ref3 => {
54
+ let {
55
+ values,
56
+ ...state
57
+ } = _ref3;
58
+ const newState = { ...state,
59
+ values: { ...values,
60
+ [key]: value
61
+ }
62
+ };
63
+ const {
64
+ onChange
65
+ } = this.props;
66
+
67
+ if (onChange) {
68
+ onChange(newState.values);
69
+ }
70
+
71
+ return newState;
72
+ });
73
+ toggleFilters = newFilters => this.setState(prevState => {
74
+ const newState = { ...prevState,
75
+ values: { ...prevState.values,
76
+ ...newFilters
77
+ }
78
+ };
79
+ const {
80
+ onChange
81
+ } = this.props;
82
+
83
+ if (onChange) {
84
+ onChange(newState.values);
85
+ }
86
+
87
+ return newState;
88
+ });
89
+
92
90
  render() {
93
91
  return /*#__PURE__*/React.createElement(BaseComponent, _extends({}, this.props, this.state, {
94
92
  toggleFilter: this.toggleFilter,
@@ -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.2",
3
+ "version": "3.3.0-alpha.0",
4
4
  "description": "UI components for BundleStats projects.",
5
5
  "main": "lib/index.js",
6
6
  "module": "lib-esm/index.js",
@@ -84,11 +84,11 @@
84
84
  "react-router-dom": "^5.3.0"
85
85
  },
86
86
  "dependencies": {
87
- "@bundle-stats/utils": "^3.2.2",
87
+ "@bundle-stats/utils": "^3.3.0-alpha.0",
88
88
  "date-fns": "^2.6.0",
89
89
  "query-string": "^7.0.0",
90
90
  "reakit": "^1.3.6",
91
91
  "use-query-params": "^1.2.2"
92
92
  },
93
- "gitHead": "e9caf650b9f5c2e4408011ccb302c2dfa3ccdb0f"
93
+ "gitHead": "7c7e664296ad7aed38a6972f4421d754098119eb"
94
94
  }
package/src/app/app.jsx CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useEffect } from 'react';
1
+ import React, { useEffect, useMemo } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { HashRouter, NavLink, Route, Switch, useLocation } from 'react-router-dom';
4
4
  import { COMPONENT } from '@bundle-stats/utils';
@@ -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';
@@ -58,8 +59,12 @@ Layout.defaultProps = {
58
59
 
59
60
  const AppComponent = ({ version, jobs }) => {
60
61
  const [bundleStatsState, bundleStatsSetState] = useComponentQueryState(COMPONENT.BUNDLE_ASSETS);
61
- const [bundlePackagesState, bundlePackagesSetState] = useComponentQueryState(COMPONENT.BUNDLE_PACKAGES);
62
- const [bundleModulesState, bundleModulesSetState] = useComponentQueryState(COMPONENT.BUNDLE_MODULES);
62
+ const [bundlePackagesState, bundlePackagesSetState] = useComponentQueryState(
63
+ COMPONENT.BUNDLE_PACKAGES,
64
+ );
65
+ const [bundleModulesState, bundleModulesSetState] = useComponentQueryState(
66
+ COMPONENT.BUNDLE_MODULES,
67
+ );
63
68
 
64
69
  if (jobs.length === 0) {
65
70
  return (
@@ -71,7 +76,9 @@ const AppComponent = ({ version, jobs }) => {
71
76
  );
72
77
  }
73
78
 
74
- const { duplicatePackagesCount } = jobs[0].summary.webpack;
79
+ const { summary, insights } = useMemo(() => jobs[0], jobs);
80
+
81
+ const { duplicatePackagesCount } = summary.webpack;
75
82
  const duplicatePackagesInsights = Boolean(
76
83
  duplicatePackagesCount.current || duplicatePackagesCount.baseline,
77
84
  );
@@ -82,7 +89,8 @@ const AppComponent = ({ version, jobs }) => {
82
89
  <Summary
83
90
  size="large"
84
91
  keys={METRICS_WEBPACK_GENERAL}
85
- data={jobs[0].summary}
92
+ data={summary}
93
+ budgets={insights?.webpack?.budgets}
86
94
  showSummaryItemDelta={jobs.length !== 1}
87
95
  />
88
96
  </Container>
@@ -114,7 +122,8 @@ const AppComponent = ({ version, jobs }) => {
114
122
  <Stack space="medium">
115
123
  <Summary
116
124
  keys={METRICS_WEBPACK_ASSETS}
117
- data={jobs[0].summary}
125
+ data={summary}
126
+ budgets={insights?.webpack?.budgets}
118
127
  showSummaryItemDelta={jobs.length !== 1}
119
128
  />
120
129
  <Box outline>
@@ -137,7 +146,8 @@ const AppComponent = ({ version, jobs }) => {
137
146
  <Stack space="medium">
138
147
  <Summary
139
148
  keys={METRICS_WEBPACK_MODULES}
140
- data={jobs[0].summary}
149
+ data={summary}
150
+ budgets={insights?.webpack?.budgets}
141
151
  showSummaryItemDelta={jobs.length !== 1}
142
152
  />
143
153
  <Box outline>
@@ -159,7 +169,8 @@ const AppComponent = ({ version, jobs }) => {
159
169
  <Stack space="medium">
160
170
  <Summary
161
171
  keys={METRICS_WEBPACK_PACKAGES}
162
- data={jobs[0].summary}
172
+ data={summary}
173
+ budgets={insights?.webpack?.budgets}
163
174
  showSummaryItemDelta={jobs.length !== 1}
164
175
  />
165
176
  <Box outline>
@@ -187,6 +198,11 @@ const AppComponent = ({ version, jobs }) => {
187
198
  />
188
199
  </Container>
189
200
  )}
201
+ {insights?.webpack?.budgets && (
202
+ <Container>
203
+ <BudgetInsights source="webpack" budgets={insights.webpack.budgets} />
204
+ </Container>
205
+ )}
190
206
  <Container>
191
207
  <Stack space="small">
192
208
  <TotalSizeTypeTitle />
@@ -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,49 @@
1
+ import React from 'react';
2
+ import cx from 'classnames';
3
+ import PropTypes from 'prop-types';
4
+
5
+ import { Icon } from '../../ui/icon';
6
+ import { Tooltip } from '../../ui/tooltip';
7
+
8
+ import css from './budget-info.module.css';
9
+
10
+ export const BudgetInfo = ({ className = '', budget, metric }) => {
11
+ const rootClassName = cx(css.root, budget.failed ? css.over : css.under, className);
12
+
13
+ return (
14
+ <Tooltip
15
+ className={rootClassName}
16
+ title={
17
+ <>
18
+ <strong>{metric.label}</strong>
19
+ {` is ${budget.failed ? 'over' : 'under'} `}
20
+ {metric.formatter(budget.budgetValue)}
21
+ {` budget`}
22
+ </>
23
+ }
24
+ >
25
+ <Icon
26
+ className={css.icon}
27
+ glyph={budget.failed ? Icon.ICONS.ALERT_CIRCLE : Icon.ICONS.CHECK_CIRCLE}
28
+ size="medium"
29
+ />
30
+ </Tooltip>
31
+ );
32
+ };
33
+
34
+ BudgetInfo.propTypes = {
35
+ budget: PropTypes.shape({
36
+ currentValue: PropTypes.number,
37
+ budgetValue: PropTypes.number,
38
+ failed: PropTypes.bool,
39
+ }).isRequired,
40
+ metric: PropTypes.shape({
41
+ label: PropTypes.string,
42
+ formatter: PropTypes.func,
43
+ }).isRequired,
44
+ className: PropTypes.string,
45
+ };
46
+
47
+ BudgetInfo.defaultProps = {
48
+ className: '',
49
+ };