@bundle-stats/ui 3.2.1 → 3.3.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 (121) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/lib/app/app.js +31 -6
  3. package/lib/app/app.stories.js +31 -3
  4. package/lib/assets/icons/alert-circle.svg +1 -0
  5. package/lib/assets/icons/arrow-right-circle.svg +1 -0
  6. package/lib/assets/icons/check-circle.svg +1 -0
  7. package/lib/assets/icons/close.svg +1 -1
  8. package/lib/assets/icons.svg +50 -1
  9. package/lib/components/browsertime-table/browsertime-table.js +1 -1
  10. package/lib/components/browsertime-table/browsertime-table.stories.js +1 -1
  11. package/lib/components/budget-info/budget-info.js +80 -0
  12. package/lib/components/budget-info/budget-info.module.css +40 -0
  13. package/lib/components/budget-info/budget-info.stories.js +44 -0
  14. package/lib/components/budget-info/index.js +18 -0
  15. package/lib/components/budget-insights/budget-insights.js +200 -0
  16. package/lib/components/budget-insights/budget-insights.module.css +53 -0
  17. package/lib/components/budget-insights/budget-insights.stories.js +104 -0
  18. package/lib/components/budget-insights/index.js +18 -0
  19. package/lib/components/bundle-assets/bundle-assets.js +3 -3
  20. package/lib/components/bundle-assets/bundle-assets.utils.js +2 -2
  21. package/lib/components/bundle-assets/index.js +3 -3
  22. package/lib/components/bundle-assets-totals-chart-bars/bundle-assets-totals-chart-bars.js +1 -1
  23. package/lib/components/bundle-assets-totals-table/bundle-assets-totals-table.js +44 -8
  24. package/lib/components/bundle-assets-totals-table/bundle-assets-totals-table.module.css +2 -2
  25. package/lib/components/bundle-assets-totals-table/bundle-assets-totals-table.stories.js +58 -43
  26. package/lib/components/bundle-modules/bundle-modules.js +3 -3
  27. package/lib/components/bundle-modules/bundle-modules.utils.js +2 -2
  28. package/lib/components/bundle-modules/index.js +3 -3
  29. package/lib/components/bundle-packages/bundle-packages.js +1 -1
  30. package/lib/components/bundle-packages/bundle-packages.utils.js +2 -2
  31. package/lib/components/bundle-packages/index.js +1 -1
  32. package/lib/components/index.js +8 -0
  33. package/lib/components/lighthouse-table/lighthouse-table.js +1 -1
  34. package/lib/components/metrics-table/metrics-table.js +1 -1
  35. package/lib/components/metrics-table-search/metrics-table-search.js +1 -1
  36. package/lib/components/metrics-table-search/metrics-table-search.stories.js +1 -1
  37. package/lib/components/summary/summary.js +40 -62
  38. package/lib/components/summary/summary.module.css +31 -14
  39. package/lib/components/summary/summary.stories.js +29 -0
  40. package/lib/components/summary-item/summary-item.js +30 -13
  41. package/lib/components/summary-item/summary-item.module.css +18 -9
  42. package/lib/components/summary-item/summary-item.stories.js +67 -52
  43. package/lib/constants.js +6 -6
  44. package/lib/css/variables.css +15 -11
  45. package/lib/hooks/search-params.js +2 -2
  46. package/lib/query-state.js +1 -1
  47. package/lib/stories/wrapper.js +2 -2
  48. package/lib/ui/alert/alert.js +13 -3
  49. package/lib/ui/filters/filters.container.js +5 -5
  50. package/lib/ui/filters/filters.js +2 -2
  51. package/lib/ui/filters/filters.stories.js +3 -3
  52. package/lib/ui/horizontal-bar-chart/horizontal-bar-chart.js +3 -3
  53. package/lib/ui/icon/icon.js +3 -0
  54. package/lib/ui/icon/icon.stories.js +40 -22
  55. package/lib/ui/panels/panels.js +2 -2
  56. package/lib/ui/popover/popover.js +3 -3
  57. package/lib/ui/popover/popover.module.css +2 -1
  58. package/lib/ui/tabs/tabs.js +2 -2
  59. package/lib/ui/tooltip/tooltip.module.css +1 -0
  60. package/lib/utils.js +2 -2
  61. package/lib-esm/app/app.js +57 -31
  62. package/lib-esm/app/app.stories.js +29 -1
  63. package/lib-esm/assets/icons/alert-circle.svg +1 -0
  64. package/lib-esm/assets/icons/arrow-right-circle.svg +1 -0
  65. package/lib-esm/assets/icons/check-circle.svg +1 -0
  66. package/lib-esm/assets/icons/close.svg +1 -1
  67. package/lib-esm/assets/icons.svg +50 -1
  68. package/lib-esm/components/budget-info/budget-info.js +58 -0
  69. package/lib-esm/components/budget-info/budget-info.module.css +40 -0
  70. package/lib-esm/components/budget-info/budget-info.stories.js +30 -0
  71. package/lib-esm/components/budget-info/index.js +1 -0
  72. package/lib-esm/components/budget-insights/budget-insights.js +148 -0
  73. package/lib-esm/components/budget-insights/budget-insights.module.css +53 -0
  74. package/lib-esm/components/budget-insights/budget-insights.stories.js +84 -0
  75. package/lib-esm/components/budget-insights/index.js +1 -0
  76. package/lib-esm/components/bundle-assets-totals-table/bundle-assets-totals-table.js +23 -7
  77. package/lib-esm/components/bundle-assets-totals-table/bundle-assets-totals-table.module.css +2 -2
  78. package/lib-esm/components/bundle-assets-totals-table/bundle-assets-totals-table.stories.js +46 -21
  79. package/lib-esm/components/index.js +1 -0
  80. package/lib-esm/components/summary/summary.js +26 -45
  81. package/lib-esm/components/summary/summary.module.css +31 -14
  82. package/lib-esm/components/summary/summary.stories.js +27 -0
  83. package/lib-esm/components/summary-item/summary-item.js +28 -12
  84. package/lib-esm/components/summary-item/summary-item.module.css +18 -9
  85. package/lib-esm/components/summary-item/summary-item.stories.js +52 -42
  86. package/lib-esm/constants.js +4 -4
  87. package/lib-esm/css/variables.css +15 -11
  88. package/lib-esm/ui/alert/alert.js +6 -3
  89. package/lib-esm/ui/filters/filters.container.js +39 -41
  90. package/lib-esm/ui/icon/icon.js +3 -0
  91. package/lib-esm/ui/icon/icon.stories.js +22 -11
  92. package/lib-esm/ui/popover/popover.js +2 -2
  93. package/lib-esm/ui/popover/popover.module.css +2 -1
  94. package/lib-esm/ui/tooltip/tooltip.module.css +1 -0
  95. package/package.json +15 -15
  96. package/src/app/app.jsx +24 -8
  97. package/src/assets/icons/alert-circle.svg +1 -0
  98. package/src/assets/icons/arrow-right-circle.svg +1 -0
  99. package/src/assets/icons/check-circle.svg +1 -0
  100. package/src/assets/icons/close.svg +1 -1
  101. package/src/assets/icons.svg +50 -1
  102. package/src/components/budget-info/budget-info.jsx +65 -0
  103. package/src/components/budget-info/budget-info.module.css +40 -0
  104. package/src/components/budget-info/index.js +1 -0
  105. package/src/components/budget-insights/budget-insights.jsx +157 -0
  106. package/src/components/budget-insights/budget-insights.module.css +53 -0
  107. package/src/components/budget-insights/index.js +1 -0
  108. package/src/components/bundle-assets-totals-table/bundle-assets-totals-table.jsx +27 -13
  109. package/src/components/bundle-assets-totals-table/bundle-assets-totals-table.module.css +2 -2
  110. package/src/components/index.js +1 -0
  111. package/src/components/summary/summary.jsx +26 -44
  112. package/src/components/summary/summary.module.css +31 -14
  113. package/src/components/summary-item/summary-item.jsx +26 -24
  114. package/src/components/summary-item/summary-item.module.css +18 -9
  115. package/src/constants.js +7 -7
  116. package/src/css/variables.css +15 -11
  117. package/src/ui/alert/alert.jsx +7 -2
  118. package/src/ui/icon/icon.jsx +3 -0
  119. package/src/ui/popover/popover.jsx +3 -3
  120. package/src/ui/popover/popover.module.css +2 -1
  121. package/src/ui/tooltip/tooltip.module.css +1 -0
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-arrow-right-circle"><circle cx="12" cy="12" r="10"></circle><polyline points="12 16 16 12 12 8"></polyline><line x1="8" y1="12" x2="16" y2="12"></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 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
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-x-circle"><circle cx="12" cy="12" r="10"></circle><line x1="15" y1="9" x2="9" y2="15"></line><line x1="9" y1="9" x2="15" y2="15"></line></svg>
@@ -1 +1,50 @@
1
- <svg width="16" height="16"><symbol viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="afeather afeather-arrow-down" id="arrow" xmlns="http://www.w3.org/2000/svg"><path d="M12 5v14M19 12l-7 7-7-7"/></symbol><symbol viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="bfeather bfeather-x-circle" id="cancel" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10"/><path d="M15 9l-6 6M9 9l6 6"/></symbol><symbol viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="cfeather cfeather-clock" id="clock" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></symbol><symbol viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="dfeather dfeather-x" id="close" xmlns="http://www.w3.org/2000/svg"><path d="M18 6L6 18M6 6l12 12"/></symbol><symbol viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="efeather efeather-git-commit" id="commit" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="4"/><path d="M1.05 12H7M17.01 12h5.95"/></symbol><symbol viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="ffeather ffeather-bar-chart" id="filter" xmlns="http://www.w3.org/2000/svg"><path d="M12 20V10M18 20V4M6 20v-4"/></symbol><symbol viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="gfeather gfeather-help-circle" id="help" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 015.83 1c0 2-3 3-3 3M12 17h.01"/></symbol><symbol viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="hfeather hfeather-menu" id="menu" xmlns="http://www.w3.org/2000/svg"><path d="M3 12h18M3 6h18M3 18h18"/></symbol><symbol viewBox="0 0 24 24" fill="currentColor" id="sort" xmlns="http://www.w3.org/2000/svg"><path d="M3 18h6v-2H3v2zM3 6v2h18V6H3zm0 7h12v-2H3v2z"/><path d="M0 0h24v24H0z" fill="none"/></symbol><symbol viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="jfeather jfeather-alert-triangle" id="warning" xmlns="http://www.w3.org/2000/svg"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0zM12 9v4M12 17h.01"/></symbol></svg>
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>
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">
7
+ <path d="M12 5v14M19 12l-7 7-7-7"/>
8
+ </symbol>
9
+ <symbol fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-arrow-right-circle" viewBox="0 0 24 24" id="arrow-right-circle" xmlns="http://www.w3.org/2000/svg">
10
+ <circle cx="12" cy="12" r="10"/>
11
+ <path d="M12 16l4-4-4-4M8 12h8"/>
12
+ </symbol>
13
+ <symbol viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x-circle" id="cancel" xmlns="http://www.w3.org/2000/svg">
14
+ <circle cx="12" cy="12" r="10"/>
15
+ <path d="M15 9l-6 6M9 9l6 6"/>
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>
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">
22
+ <circle cx="12" cy="12" r="10"/>
23
+ <path d="M12 6v6l4 2"/>
24
+ </symbol>
25
+ <symbol fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x-circle" viewBox="0 0 24 24" id="close" xmlns="http://www.w3.org/2000/svg">
26
+ <circle cx="12" cy="12" r="10"/>
27
+ <path d="M15 9l-6 6M9 9l6 6"/>
28
+ </symbol>
29
+ <symbol viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-git-commit" id="commit" xmlns="http://www.w3.org/2000/svg">
30
+ <circle cx="12" cy="12" r="4"/>
31
+ <path d="M1.05 12H7M17.01 12h5.95"/>
32
+ </symbol>
33
+ <symbol viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-bar-chart" id="filter" xmlns="http://www.w3.org/2000/svg">
34
+ <path d="M12 20V10M18 20V4M6 20v-4"/>
35
+ </symbol>
36
+ <symbol viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-help-circle" id="help" xmlns="http://www.w3.org/2000/svg">
37
+ <circle cx="12" cy="12" r="10"/>
38
+ <path d="M9.09 9a3 3 0 015.83 1c0 2-3 3-3 3M12 17h.01"/>
39
+ </symbol>
40
+ <symbol viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-menu" id="menu" xmlns="http://www.w3.org/2000/svg">
41
+ <path d="M3 12h18M3 6h18M3 18h18"/>
42
+ </symbol>
43
+ <symbol viewBox="0 0 24 24" fill="currentColor" id="sort" xmlns="http://www.w3.org/2000/svg">
44
+ <path d="M3 18h6v-2H3v2zM3 6v2h18V6H3zm0 7h12v-2H3v2z"/>
45
+ <path d="M0 0h24v24H0z" fill="none"/>
46
+ </symbol>
47
+ <symbol viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-alert-triangle" id="warning" xmlns="http://www.w3.org/2000/svg">
48
+ <path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0zM12 9v4M12 17h.01"/>
49
+ </symbol>
50
+ </svg>
@@ -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';
@@ -0,0 +1,157 @@
1
+ import React, { useMemo, useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import cx from 'classnames';
4
+ import { METRIC_COMPONENT_LINKS } from '@bundle-stats/utils';
5
+ import * as budgetsInsightsTransformer from '@bundle-stats/utils/lib-esm/transformers/budgets-insights';
6
+
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 { className, metricId, budgetInsight, CustomLink } = props;
17
+ const componentLinkOptions = METRIC_COMPONENT_LINKS.get(metricId);
18
+ const budgetInsightInfo = budgetsInsightsTransformer.getInfo(metricId, budgetInsight);
19
+ const { data: messageData } = budgetInsightInfo.message;
20
+
21
+ return (
22
+ <CustomLink className={cx(css.budget, className)} {...componentLinkOptions?.link}>
23
+ <strong>{messageData.metricLabel}</strong>
24
+ {` value (`}
25
+ <strong>{messageData.currentValue}</strong>
26
+ {`) is ${messageData.diffLabel} `}
27
+ <strong>{messageData.budgetValue}</strong>
28
+ {` budget `}
29
+ </CustomLink>
30
+ );
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
+
44
+ Budget.defaultProps = {
45
+ className: '',
46
+ };
47
+
48
+ const BudgetsGroup = (props) => {
49
+ const { className, kind, source, budgets, CustomLink } = props;
50
+ const [showBudgets, setShowBudgets] = useState(false);
51
+
52
+ const isFailed = kind === 'danger';
53
+
54
+ return (
55
+ <Alert kind={kind} padding="none" className={cx(css.group, css[kind], className)}>
56
+ <Box
57
+ padding={['xsmall', 'small']}
58
+ className={css.groupHeader}
59
+ as="button"
60
+ type="button"
61
+ onClick={() => setShowBudgets(!showBudgets)}
62
+ >
63
+ <FlexStack space="xxsmall" className={css.groupTitle}>
64
+ <Icon
65
+ className={css.groupIcon}
66
+ glyph={isFailed ? Icon.ICONS.ALERT_CIRCLE : Icon.ICONS.CHECK_CIRCLE}
67
+ />
68
+ <span>
69
+ <strong>{budgets.length}</strong>
70
+ {` budget ${budgets.length > 1 ? 'checks' : 'check'} ${isFailed ? 'failed' : 'passed'}`}
71
+ </span>
72
+ <span className={css.groupTitleToggle}>{showBudgets ? 'hide' : 'show'} all</span>
73
+ </FlexStack>
74
+ </Box>
75
+
76
+ {showBudgets && (
77
+ <Box padding={['xsmall', 'small']} className={css.groupContent}>
78
+ <Stack space="none">
79
+ {budgets.map(([key, budget]) => (
80
+ <Budget
81
+ key={key}
82
+ metricId={`${source}.${key}`}
83
+ budgetInsight={budget}
84
+ CustomLink={CustomLink}
85
+ />
86
+ ))}
87
+ </Stack>
88
+ </Box>
89
+ )}
90
+ </Alert>
91
+ );
92
+ };
93
+
94
+ BudgetsGroup.propTypes = {
95
+ className: PropTypes.string,
96
+ kind: PropTypes.oneOf(['success', 'danger']).isRequired,
97
+ source: PropTypes.string.isRequired,
98
+ budgets: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
99
+ CustomLink: PropTypes.elementType.isRequired,
100
+ };
101
+
102
+ BudgetsGroup.defaultProps = {
103
+ className: '',
104
+ };
105
+
106
+ export const BudgetInsights = (props) => {
107
+ const { className, source, budgets, CustomLink } = props;
108
+
109
+ const [failedBudgets, passedBudgets] = useMemo(() => {
110
+ const passed = [];
111
+ const failed = [];
112
+
113
+ Object.entries(budgets).forEach(([key, budget]) => {
114
+ if (budget.failed) {
115
+ failed.push([key, budget]);
116
+ } else {
117
+ passed.push([key, budget]);
118
+ }
119
+ });
120
+
121
+ return [failed, passed];
122
+ }, [budgets]);
123
+
124
+ return (
125
+ <Stack className={className} space="xsmall">
126
+ {failedBudgets.length > 0 && (
127
+ <BudgetsGroup
128
+ kind="danger"
129
+ source={source}
130
+ budgets={failedBudgets}
131
+ CustomLink={CustomLink}
132
+ />
133
+ )}
134
+
135
+ {passedBudgets.length > 0 && (
136
+ <BudgetsGroup
137
+ kind="success"
138
+ source={source}
139
+ budgets={passedBudgets}
140
+ CustomLink={CustomLink}
141
+ />
142
+ )}
143
+ </Stack>
144
+ );
145
+ };
146
+
147
+ BudgetInsights.propTypes = {
148
+ className: PropTypes.string,
149
+ source: PropTypes.string.isRequired,
150
+ budgets: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
151
+ CustomLink: PropTypes.elementType,
152
+ };
153
+
154
+ BudgetInsights.defaultProps = {
155
+ className: '',
156
+ CustomLink: ComponentLink,
157
+ };
@@ -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 @@
1
+ export * from './budget-insights';
@@ -1,29 +1,43 @@
1
- import React from 'react';
1
+ import React, { useCallback, useMemo } from 'react';
2
2
  import PropTypes from 'prop-types';
3
+ import { get } from 'lodash';
3
4
  import { getBundleAssetsFileTypeComponentLink } from '@bundle-stats/utils';
4
5
  import * as webpack from '@bundle-stats/utils/lib-esm/webpack';
5
6
 
6
7
  import { ASSETS_SIZES_FILE_TYPE_MAP } from '../../constants';
8
+ import { FlexStack } from '../../layout/flex-stack';
7
9
  import { MetricsTable } from '../metrics-table';
8
10
  import { ComponentLink } from '../component-link';
11
+ import { BudgetInfo } from '../budget-info';
12
+ import css from './bundle-assets-totals-table.module.css';
9
13
 
10
14
  export const BundleAssetsTotalsTable = ({
11
15
  className,
12
16
  jobs,
13
17
  customComponentLink: CustomComponentLink,
14
18
  }) => {
15
- const items = webpack.compareBySection.sizes(jobs);
16
-
17
- const renderRowHeader = (item) => {
18
- const fileType = ASSETS_SIZES_FILE_TYPE_MAP[item.key];
19
- const { section, title, params } = getBundleAssetsFileTypeComponentLink(fileType, item.label);
20
-
21
- return (
22
- <CustomComponentLink section={section} title={title} params={params}>
23
- {item.label}
24
- </CustomComponentLink>
25
- );
26
- };
19
+ const items = useMemo(() => webpack.compareBySection.sizes(jobs), [jobs]);
20
+ const budgets = jobs[0].insights?.webpack?.budgets;
21
+
22
+ const renderRowHeader = useCallback(
23
+ (item) => {
24
+ const fileType = ASSETS_SIZES_FILE_TYPE_MAP[item.key];
25
+ const { section, title, params } = getBundleAssetsFileTypeComponentLink(fileType, item.label);
26
+ const [_, ...metricSlugs] = item.key.split('.');
27
+ const metricKey = metricSlugs.join('.');
28
+ const budget = get(budgets, metricKey);
29
+
30
+ return (
31
+ <CustomComponentLink section={section} title={title} params={params}>
32
+ <FlexStack space="xxsmall" className={css.itemTitleText}>
33
+ <span>{item.label}</span>
34
+ {budget && <BudgetInfo metricId={item.key} budgetInsight={budget} />}
35
+ </FlexStack>
36
+ </CustomComponentLink>
37
+ );
38
+ },
39
+ [CustomComponentLink],
40
+ );
27
41
 
28
42
  return (
29
43
  <MetricsTable
@@ -1,3 +1,3 @@
1
- .title {
2
- color: var(--color-text-light);
1
+ .itemTitleText {
2
+ align-items: center;
3
3
  }
@@ -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';