@bundle-stats/ui 4.0.0-rc.6 → 4.0.0-rc.7

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 (29) hide show
  1. package/lib/components/bundle-assets/bundle-assets.js +64 -49
  2. package/lib/components/bundle-modules/bundle-modules.js +94 -79
  3. package/lib/components/bundle-packages/bundle-packages.js +60 -45
  4. package/lib/components/metrics-table-title/metrics-table-title.js +10 -7
  5. package/lib/components/metrics-table-title/metrics-table-title.module.css +9 -3
  6. package/lib/ui/filters/filters.js +5 -7
  7. package/lib/ui/filters/filters.utils.js +40 -2
  8. package/lib/ui/filters/index.js +96 -3
  9. package/lib-esm/components/bundle-assets/bundle-assets.js +63 -50
  10. package/lib-esm/components/bundle-modules/bundle-modules.js +78 -64
  11. package/lib-esm/components/bundle-packages/bundle-packages.js +44 -31
  12. package/lib-esm/components/metrics-table-title/metrics-table-title.js +10 -7
  13. package/lib-esm/components/metrics-table-title/metrics-table-title.module.css +9 -3
  14. package/lib-esm/ui/filters/filters.js +3 -4
  15. package/lib-esm/ui/filters/filters.utils.js +18 -0
  16. package/lib-esm/ui/filters/index.js +65 -3
  17. package/package.json +4 -4
  18. package/src/components/bundle-assets/bundle-assets.jsx +54 -45
  19. package/src/components/bundle-modules/bundle-modules.jsx +72 -65
  20. package/src/components/bundle-packages/bundle-packages.jsx +36 -26
  21. package/src/components/metrics-table-title/metrics-table-title.jsx +20 -18
  22. package/src/components/metrics-table-title/metrics-table-title.module.css +9 -3
  23. package/src/ui/filters/filters.jsx +4 -4
  24. package/src/ui/filters/filters.utils.js +21 -0
  25. package/src/ui/filters/index.jsx +72 -0
  26. package/lib/ui/filters/filters.container.js +0 -187
  27. package/lib-esm/ui/filters/filters.container.js +0 -111
  28. package/src/ui/filters/filters.container.jsx +0 -108
  29. package/src/ui/filters/index.js +0 -4
@@ -22,14 +22,32 @@ import { MetricsTableOptions } from '../metrics-table-options';
22
22
  import { MetricsTableTitle } from '../metrics-table-title';
23
23
  import css from './bundle-packages.module.css';
24
24
 
25
- const PackagePopoverContent = _ref => {
25
+ const getDropdownFilters = _ref => {
26
+ let {
27
+ compareMode,
28
+ filters
29
+ } = _ref;
30
+ return {
31
+ [PACKAGE_FILTERS.CHANGED]: {
32
+ label: 'Changed',
33
+ defaultValue: filters[PACKAGE_FILTERS.CHANGED],
34
+ disabled: !compareMode
35
+ },
36
+ [PACKAGE_FILTERS.DUPLICATE]: {
37
+ label: 'Duplicate',
38
+ defaultValue: filters[PACKAGE_FILTERS.DUPLICATE]
39
+ }
40
+ };
41
+ };
42
+
43
+ const PackagePopoverContent = _ref2 => {
26
44
  let {
27
45
  name,
28
46
  fullName,
29
47
  path,
30
48
  duplicate,
31
49
  CustomComponentLink
32
- } = _ref;
50
+ } = _ref2;
33
51
  const fallbackPackagePath = `node_modules/${fullName.split(PACKAGES_SEPARATOR).join('/node_modules/')}`;
34
52
  const normalizedPackagePath = `${path || fallbackPackagePath}/`;
35
53
  const [normalizedName, packageId] = name.split(PACKAGE_ID_SEPARATOR);
@@ -78,14 +96,14 @@ PackagePopoverContent.defaultProps = {
78
96
  path: ''
79
97
  };
80
98
 
81
- const PackageName = _ref2 => {
99
+ const PackageName = _ref3 => {
82
100
  let {
83
101
  packageName,
84
102
  path,
85
103
  showDuplicateFlag,
86
104
  row,
87
105
  CustomComponentLink
88
- } = _ref2;
106
+ } = _ref3;
89
107
  const [showHoverCard, setHoverCard] = useState(false);
90
108
  const handleOnMouseEnter = useCallback(() => setHoverCard(true), [showHoverCard]);
91
109
  const label = useMemo(() => /*#__PURE__*/React.createElement(React.Fragment, null, showDuplicateFlag && row.duplicate && /*#__PURE__*/React.createElement(Tag, {
@@ -132,11 +150,11 @@ PackageName.propTypes = {
132
150
  CustomComponentLink: PropTypes.elementType.isRequired
133
151
  };
134
152
 
135
- const RowHeader = _ref3 => {
153
+ const RowHeader = _ref4 => {
136
154
  let {
137
155
  row,
138
156
  CustomComponentLink
139
- } = _ref3;
157
+ } = _ref4;
140
158
  const packageNames = row.label.split(PACKAGES_SEPARATOR);
141
159
  const {
142
160
  path
@@ -181,24 +199,34 @@ export const BundlePackages = props => {
181
199
  hasActiveFilters,
182
200
  customComponentLink: CustomComponentLink
183
201
  } = props;
184
- const emptyMessage = /*#__PURE__*/React.createElement(EmptySet, {
185
- resources: "packages",
186
- filtered: totalRowCount !== 0,
187
- handleResetFilters: resetFilters,
188
- handleViewAll: resetAllFilters
189
- });
202
+ const dropdownFilters = useMemo(() => getDropdownFilters({
203
+ compareMode: (jobs == null ? void 0 : jobs.length) > 1,
204
+ filters
205
+ }), [jobs, filters]);
206
+ const metricsTableTitle = useMemo(() => /*#__PURE__*/React.createElement(MetricsTableTitle, {
207
+ title: I18N.PACKAGES,
208
+ info: `${items.length}/${totalRowCount}`,
209
+ popoverInfo: I18N.PACKAGES_INFO,
210
+ popoverHref: config.documentation.packages
211
+ }), [items, totalRowCount]);
190
212
  const renderRowHeader = useCallback(row => /*#__PURE__*/React.createElement(RowHeader, {
191
213
  row: row,
192
214
  CustomComponentLink: CustomComponentLink
193
215
  }), [CustomComponentLink]);
216
+ const emptyMessage = useMemo(() => /*#__PURE__*/React.createElement(EmptySet, {
217
+ resources: "packages",
218
+ filtered: totalRowCount !== 0,
219
+ handleResetFilters: resetFilters,
220
+ handleViewAll: resetAllFilters
221
+ }), [totalRowCount, resetFilters, resetAllFilters]);
194
222
  return /*#__PURE__*/React.createElement("section", {
195
223
  className: cx(css.root, className)
196
224
  }, /*#__PURE__*/React.createElement(Toolbar, {
197
225
  className: css.toolbar,
198
- renderActions: _ref4 => {
226
+ renderActions: _ref5 => {
199
227
  let {
200
228
  actionClassName
201
- } = _ref4;
229
+ } = _ref5;
202
230
  return /*#__PURE__*/React.createElement(FlexStack, {
203
231
  space: "xxsmall",
204
232
  className: cx(css.dropdown, actionClassName)
@@ -219,17 +247,7 @@ export const BundlePackages = props => {
219
247
  search: search,
220
248
  updateSearch: updateSearch
221
249
  }), /*#__PURE__*/React.createElement(Filters, {
222
- filters: {
223
- [PACKAGE_FILTERS.CHANGED]: {
224
- label: 'Changed',
225
- defaultValue: filters[PACKAGE_FILTERS.CHANGED],
226
- disabled: jobs.length <= 1
227
- },
228
- [PACKAGE_FILTERS.DUPLICATE]: {
229
- label: 'Duplicate',
230
- defaultValue: filters[PACKAGE_FILTERS.DUPLICATE]
231
- }
232
- },
250
+ filters: dropdownFilters,
233
251
  label: `Filters (${items.length}/${totalRowCount})`,
234
252
  onChange: updateFilters,
235
253
  hasActiveFilters: hasActiveFilters
@@ -239,12 +257,7 @@ export const BundlePackages = props => {
239
257
  emptyMessage: emptyMessage,
240
258
  renderRowHeader: renderRowHeader,
241
259
  showHeaderSum: true,
242
- title: /*#__PURE__*/React.createElement(MetricsTableTitle, {
243
- title: I18N.PACKAGES,
244
- info: `(${items.length}/${totalRowCount})`,
245
- popoverInfo: I18N.PACKAGES_INFO,
246
- popoverHref: config.documentation.packages
247
- })
260
+ title: metricsTableTitle
248
261
  })));
249
262
  };
250
263
  BundlePackages.defaultProps = {
@@ -16,22 +16,25 @@ export const MetricsTableTitle = props => {
16
16
  popoverHref
17
17
  } = props;
18
18
  const rootClassName = cx(css.root, className);
19
- return /*#__PURE__*/React.createElement(FlexStack, {
20
- space: "xxxsmall",
19
+ return /*#__PURE__*/React.createElement(Stack, {
21
20
  className: rootClassName
22
- }, /*#__PURE__*/React.createElement("span", null, title), info && /*#__PURE__*/React.createElement("span", {
23
- className: css.info
24
- }, info), (popoverInfo || popoverHref) && /*#__PURE__*/React.createElement(HoverCard, {
21
+ }, /*#__PURE__*/React.createElement(FlexStack, {
22
+ space: "xxxsmall",
23
+ className: css.title
24
+ }, /*#__PURE__*/React.createElement("span", null, title), (popoverInfo || popoverHref) && /*#__PURE__*/React.createElement(HoverCard, {
25
25
  label: /*#__PURE__*/React.createElement(Icon, {
26
26
  glyph: "help"
27
- })
27
+ }),
28
+ className: css.hoverCard
28
29
  }, /*#__PURE__*/React.createElement(Stack, {
29
30
  space: "xxxsmall"
30
31
  }, popoverInfo && /*#__PURE__*/React.createElement("p", null, popoverInfo), popoverHref && /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("button", {
31
32
  type: "button",
32
33
  onClick: () => window.open(popoverHref),
33
34
  className: css.readMoreLink
34
- }, I18N.READ_MORE)))));
35
+ }, I18N.READ_MORE))))), info && /*#__PURE__*/React.createElement("p", {
36
+ className: css.info
37
+ }, info));
35
38
  };
36
39
  MetricsTableTitle.propTypes = {
37
40
  className: PropTypes.string,
@@ -1,14 +1,20 @@
1
1
  .root {
2
+ }
3
+
4
+ .title {
5
+ color: var(--color-text-light);
2
6
  align-items: center;
3
7
  font-weight: bold;
4
- line-height: var(--space-small);
5
8
  text-transform: uppercase;
6
- color: var(--color-text-light);
9
+ }
10
+
11
+ .hoverCard {
12
+ line-height: 1;
7
13
  }
8
14
 
9
15
  .info {
16
+ font-size: var(--size-xsmall);
10
17
  font-weight: normal;
11
- text-transform: none;
12
18
  }
13
19
 
14
20
  .readMoreLink {
@@ -1,4 +1,3 @@
1
- import _get from "lodash/get";
2
1
  import "core-js/modules/es.array.includes.js";
3
2
 
4
3
  function _extends() { _extends = Object.assign ? Object.assign.bind() : 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); }
@@ -75,9 +74,9 @@ const FilterGroup = props => {
75
74
  let [itemKey, item] = _ref;
76
75
  return typeof (item == null ? void 0 : item.defaultValue) !== 'undefined';
77
76
  });
78
- const isGroupChecked = groupCheckboxes.map(_ref2 => {
77
+ const areAllGroupItemsChecked = groupCheckboxes.map(_ref2 => {
79
78
  let [itemKey] = _ref2;
80
- return _get(values, `${groupKey}.${itemKey}`);
79
+ return values == null ? void 0 : values[`${groupKey}.${itemKey}`];
81
80
  }).reduce((agg, val) => agg && val, true);
82
81
  const filterSuffix = getGroupFiltersLabelSuffix(groupItems);
83
82
  const hasCustomFilterSuffix = !Object.values(LABELS).includes(filterSuffix);
@@ -138,7 +137,7 @@ const FilterGroup = props => {
138
137
  }));
139
138
  })), /*#__PURE__*/React.createElement("div", {
140
139
  className: css.filterGroupActions
141
- }, isGroupChecked ? /*#__PURE__*/React.createElement(MenuItem, {
140
+ }, areAllGroupItemsChecked ? /*#__PURE__*/React.createElement(MenuItem, {
142
141
  id: "clear-all",
143
142
  as: "button",
144
143
  className: menuItemClassName,
@@ -65,4 +65,22 @@ export const getGroupFiltersLabelSuffix = filters => {
65
65
 
66
66
  const skippedLabels = filterCheckedCount - inlinedLabelCount;
67
67
  return `${suffix}${skippedLabels > 0 ? ` +${skippedLabels}` : ''}`;
68
+ };
69
+ export const getInitialValues = (key, filters) => {
70
+ if (typeof (filters == null ? void 0 : filters.defaultValue) !== 'undefined') {
71
+ return {
72
+ [key]: filters.defaultValue
73
+ };
74
+ }
75
+
76
+ if (typeof filters === 'object') {
77
+ return Object.entries(filters).map(_ref3 => {
78
+ let [groupKey, groupFilters] = _ref3;
79
+ return getInitialValues([...(key ? [key] : []), groupKey].join('.'), groupFilters);
80
+ }).reduce((agg, current) => ({ ...agg,
81
+ ...current
82
+ }), {});
83
+ }
84
+
85
+ return {};
68
86
  };
@@ -1,3 +1,65 @@
1
- import { filtersDropdownContainer } from './filters.container';
2
- import { Filters as FiltersComponent } from './filters';
3
- export const Filters = filtersDropdownContainer(FiltersComponent);
1
+ import _merge from "lodash/merge";
2
+ import _isEqual from "lodash/isEqual";
3
+
4
+ function _extends() { _extends = Object.assign ? Object.assign.bind() : 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); }
5
+
6
+ import React, { useCallback, useEffect, useState } from 'react';
7
+ import PropTypes from 'prop-types';
8
+ import { getInitialValues } from './filters.utils';
9
+ import { Filters as BaseComponent } from './filters';
10
+ export const Filters = props => {
11
+ const {
12
+ onChange,
13
+ filters,
14
+ ...restProps
15
+ } = props;
16
+ const [values, setValues] = useState(getInitialValues('', filters)); // Update values when the parent filters are changing (route with query string change)
17
+
18
+ useEffect(() => {
19
+ const parentValues = getInitialValues('', filters);
20
+
21
+ if (_isEqual(parentValues, values)) {
22
+ return;
23
+ }
24
+
25
+ setValues(parentValues);
26
+ }, // Do not trigger effect when the values are changing locally
27
+ [filters, setValues]);
28
+ const toggleFilter = useCallback((key, value) => {
29
+ const nextValues = _merge({}, values, {
30
+ [key]: value
31
+ });
32
+
33
+ setValues(nextValues);
34
+
35
+ if (onChange) {
36
+ onChange(nextValues);
37
+ }
38
+ }, [onChange, setValues, values]);
39
+ const toggleFilters = useCallback(newFilters => {
40
+ const nextValues = _merge({}, values, newFilters);
41
+
42
+ setValues(nextValues);
43
+
44
+ if (onChange) {
45
+ onChange(nextValues);
46
+ }
47
+ }, [onChange, setValues, values]);
48
+ return /*#__PURE__*/React.createElement(BaseComponent, _extends({}, restProps, {
49
+ filters: filters,
50
+ values: values,
51
+ toggleFilter: toggleFilter,
52
+ toggleFilters: toggleFilters
53
+ }));
54
+ };
55
+ Filters.defaultProps = {
56
+ onChange: null
57
+ };
58
+ Filters.propTypes = {
59
+ /** Filter config */
60
+ filters: PropTypes.object.isRequired,
61
+ // eslint-disable-line react/forbid-prop-types
62
+
63
+ /** OnChange handler */
64
+ onChange: PropTypes.func
65
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bundle-stats/ui",
3
- "version": "4.0.0-rc.6",
3
+ "version": "4.0.0-rc.7",
4
4
  "description": "UI components for BundleStats projects.",
5
5
  "main": "lib/index.js",
6
6
  "module": "lib-esm/index.js",
@@ -51,7 +51,7 @@
51
51
  "@storybook/manager-webpack5": "6.5.9",
52
52
  "@storybook/react": "6.5.9",
53
53
  "babel-core": "7.0.0-bridge.0",
54
- "babel-jest": "28.1.2",
54
+ "babel-jest": "28.1.3",
55
55
  "babel-plugin-lodash": "3.3.4",
56
56
  "babel-plugin-require-context-hook": "1.0.0",
57
57
  "chromatic": "6.7.0",
@@ -85,12 +85,12 @@
85
85
  "react-router-dom": "^5.0.0"
86
86
  },
87
87
  "dependencies": {
88
- "@bundle-stats/utils": "^4.0.0-rc.6",
88
+ "@bundle-stats/utils": "^4.0.0-rc.7",
89
89
  "ariakit": "^2.0.0-next.33",
90
90
  "date-fns": "^2.28.0",
91
91
  "modern-css-reset": "^1.4.0",
92
92
  "query-string": "^7.1.0",
93
93
  "use-query-params": "^1.2.3"
94
94
  },
95
- "gitHead": "84d6e7d2df43ec89117238f676f32df9e56bfbeb"
95
+ "gitHead": "90c1cf0f7dd95326140e4184ac72b4baeeab8df1"
96
96
  }
@@ -49,6 +49,37 @@ const getFileTypeFilters = (filters) =>
49
49
  {},
50
50
  );
51
51
 
52
+ const getFilters = ({ compareMode, filters }) => ({
53
+ [ASSET_FILTERS.CHANGED]: {
54
+ label: 'Changed',
55
+ defaultValue: filters[ASSET_FILTERS.CHANGED],
56
+ disabled: !compareMode,
57
+ },
58
+ [ASSET_ENTRY_TYPE]: {
59
+ label: 'Entry type',
60
+ [ASSET_FILTERS.ENTRY]: {
61
+ label: 'Entry',
62
+ defaultValue: get(filters, `${ASSET_ENTRY_TYPE}.${ASSET_FILTERS.ENTRY}`, true),
63
+ },
64
+ [ASSET_FILTERS.INITIAL]: {
65
+ label: 'Initial',
66
+ defaultValue: get(filters, `${ASSET_ENTRY_TYPE}.${ASSET_FILTERS.INITIAL}`, true),
67
+ },
68
+ [ASSET_FILTERS.CHUNK]: {
69
+ label: 'Chunk',
70
+ defaultValue: get(filters, `${ASSET_ENTRY_TYPE}.${ASSET_FILTERS.CHUNK}`, true),
71
+ },
72
+ [ASSET_FILTERS.ASSET]: {
73
+ label: 'Asset',
74
+ defaultValue: get(filters, `${ASSET_ENTRY_TYPE}.${ASSET_FILTERS.ASSET}`, true),
75
+ },
76
+ },
77
+ [ASSET_FILE_TYPE]: {
78
+ label: 'File type',
79
+ ...getFileTypeFilters(filters),
80
+ },
81
+ });
82
+
52
83
  const TooltipNotPredictive = ({ runs }) => (
53
84
  <div className={css.tooltipNotPredictive}>
54
85
  <p className={css.tooltipNotPredictiveText}>File name is the same, but the size has changed:</p>
@@ -181,15 +212,20 @@ export const BundleAssets = (props) => {
181
212
  customComponentLink: CustomComponentLink,
182
213
  } = props;
183
214
 
184
- const emptyMessage = (
185
- <EmptySet
186
- resources="assets"
187
- filtered={totalRowCount !== 0}
188
- handleResetFilters={resetFilters}
189
- handleViewAll={resetAllFilters}
190
- />
215
+ const dropdownFilters = useMemo(
216
+ () => getFilters({ compareMode: jobs?.length > 1, filters }),
217
+ [jobs, filters],
191
218
  );
192
219
 
220
+ const metricsTableTitle = useMemo(() => (
221
+ <MetricsTableTitle
222
+ title={I18N.ASSETS}
223
+ info={`${items.length}/${totalRowCount}`}
224
+ popoverInfo={I18N.ASSETS_INFO}
225
+ popoverHref={config.documentation.assets}
226
+ />
227
+ ), [items, totalRowCount]);
228
+
193
229
  const renderRowHeader = useCallback(
194
230
  (row) => (
195
231
  <RowHeader
@@ -202,6 +238,15 @@ export const BundleAssets = (props) => {
202
238
  [jobs, CustomComponentLink],
203
239
  );
204
240
 
241
+ const emptyMessage = useMemo(() => (
242
+ <EmptySet
243
+ resources="assets"
244
+ filtered={totalRowCount !== 0}
245
+ handleResetFilters={resetFilters}
246
+ handleViewAll={resetAllFilters}
247
+ />
248
+ ), [totalRowCount, resetFilters, resetAllFilters]);
249
+
205
250
  return (
206
251
  <section className={cx(css.root, className)}>
207
252
  <Toolbar
@@ -225,36 +270,7 @@ export const BundleAssets = (props) => {
225
270
  />
226
271
  <Filters
227
272
  className={css.toolbarFilters}
228
- filters={{
229
- [ASSET_FILTERS.CHANGED]: {
230
- label: 'Changed',
231
- defaultValue: filters[ASSET_FILTERS.CHANGED],
232
- disabled: jobs.length <= 1,
233
- },
234
- [ASSET_ENTRY_TYPE]: {
235
- label: 'Entry type',
236
- [ASSET_FILTERS.ENTRY]: {
237
- label: 'Entry',
238
- defaultValue: get(filters, `${ASSET_ENTRY_TYPE}.${ASSET_FILTERS.ENTRY}`, true),
239
- },
240
- [ASSET_FILTERS.INITIAL]: {
241
- label: 'Initial',
242
- defaultValue: get(filters, `${ASSET_ENTRY_TYPE}.${ASSET_FILTERS.INITIAL}`, true),
243
- },
244
- [ASSET_FILTERS.CHUNK]: {
245
- label: 'Chunk',
246
- defaultValue: get(filters, `${ASSET_ENTRY_TYPE}.${ASSET_FILTERS.CHUNK}`, true),
247
- },
248
- [ASSET_FILTERS.ASSET]: {
249
- label: 'Asset',
250
- defaultValue: get(filters, `${ASSET_ENTRY_TYPE}.${ASSET_FILTERS.ASSET}`, true),
251
- },
252
- },
253
- [ASSET_FILE_TYPE]: {
254
- label: 'File type',
255
- ...getFileTypeFilters(filters),
256
- },
257
- }}
273
+ filters={dropdownFilters}
258
274
  hasActiveFilters={hasActiveFilters}
259
275
  onChange={updateFilters}
260
276
  />
@@ -267,14 +283,7 @@ export const BundleAssets = (props) => {
267
283
  renderRowHeader={renderRowHeader}
268
284
  emptyMessage={emptyMessage}
269
285
  showHeaderSum
270
- title={
271
- <MetricsTableTitle
272
- title={I18N.ASSETS}
273
- info={`(${items.length}/${totalRowCount})`}
274
- popoverInfo={I18N.ASSETS_INFO}
275
- popoverHref={config.documentation.assets}
276
- />
277
- }
286
+ title={metricsTableTitle}
278
287
  />
279
288
  </main>
280
289
  </section>
@@ -29,6 +29,61 @@ import { MetricsTableTitle } from '../metrics-table-title';
29
29
  import { ModuleInfo } from '../module-info';
30
30
  import css from './bundle-modules.module.css';
31
31
 
32
+ const getFilters = ({ filters, compareMode, chunks }) => ({
33
+ [MODULE_FILTERS.CHANGED]: {
34
+ label: 'Changed',
35
+ defaultValue: filters.changed,
36
+ disabled: !compareMode,
37
+ },
38
+ [MODULE_FILTERS.DUPLICATED]: {
39
+ label: 'Duplicate',
40
+ defaultValue: filters[MODULE_FILTERS.DUPLICATED],
41
+ },
42
+ [MODULE_SOURCE_TYPE]: {
43
+ label: 'Source',
44
+ [MODULE_FILTERS.FIRST_PARTY]: {
45
+ label: 'First party',
46
+ defaultValue: get(filters, `${MODULE_SOURCE_TYPE}.${MODULE_FILTERS.FIRST_PARTY}`, true),
47
+ },
48
+ [MODULE_FILTERS.THIRD_PARTY]: {
49
+ label: 'Third party',
50
+ defaultValue: get(filters, `${MODULE_SOURCE_TYPE}.${MODULE_FILTERS.THIRD_PARTY}`, true),
51
+ },
52
+ },
53
+
54
+ // When chunks data available, list available chunks as filters
55
+ ...(!isEmpty(chunks) && {
56
+ [MODULE_CHUNK]: {
57
+ label: 'Chunk',
58
+ ...chunks.reduce(
59
+ (chunkFilters, { id, name }) => ({
60
+ ...chunkFilters,
61
+ [id]: {
62
+ label: name,
63
+ defaultValue: get(filters, `${MODULE_CHUNK}.${id}`, true),
64
+ },
65
+ }),
66
+ {},
67
+ ),
68
+ },
69
+ }),
70
+
71
+ // Module source types
72
+ [MODULE_FILE_TYPE]: {
73
+ label: 'File type',
74
+ ...MODULE_SOURCE_FILE_TYPES.reduce(
75
+ (agg, fileType) => ({
76
+ ...agg,
77
+ [fileType]: {
78
+ label: FILE_TYPE_LABELS[fileType],
79
+ defaultValue: get(filters, `${MODULE_FILE_TYPE}.${fileType}`, true),
80
+ },
81
+ }),
82
+ {},
83
+ ),
84
+ },
85
+ })
86
+
32
87
  const RowHeader = ({ row, chunks, labels, CustomComponentLink }) => {
33
88
  const chunkIds = map(chunks, 'id');
34
89
 
@@ -103,6 +158,20 @@ export const BundleModules = ({
103
158
  }) => {
104
159
  const rootClassName = cx(css.root, className);
105
160
 
161
+ const dropdownFilters = useMemo(
162
+ () => getFilters({ filters, chunks, compareMode: jobs.length > 1 }),
163
+ [jobs, filters, chunks]
164
+ );
165
+
166
+ const metricsTableTitle = useMemo(() => (
167
+ <MetricsTableTitle
168
+ title={I18N.MODULES}
169
+ info={`${items.length}/${totalRowCount}`}
170
+ popoverInfo={I18N.MODULES_INFO}
171
+ popoverHref={config.documentation.modules}
172
+ />
173
+ ), [items, totalRowCount]);
174
+
106
175
  const renderRowHeader = useCallback(
107
176
  (row) => (
108
177
  <RowHeader
@@ -115,69 +184,14 @@ export const BundleModules = ({
115
184
  [jobs, chunks, CustomComponentLink],
116
185
  );
117
186
 
118
- const emptyMessage = (
187
+ const emptyMessage = useMemo(() => (
119
188
  <EmptySet
120
189
  resources="modules"
121
190
  filtered={totalRowCount !== 0}
122
191
  handleResetFilters={resetFilters}
123
192
  handleViewAll={resetAllFilters}
124
193
  />
125
- );
126
-
127
- const dropdownFilters = {
128
- [MODULE_FILTERS.CHANGED]: {
129
- label: 'Changed',
130
- defaultValue: filters.changed,
131
- disabled: jobs.length <= 1,
132
- },
133
- [MODULE_FILTERS.DUPLICATED]: {
134
- label: 'Duplicate',
135
- defaultValue: filters[MODULE_FILTERS.DUPLICATED],
136
- },
137
- [MODULE_SOURCE_TYPE]: {
138
- label: 'Source',
139
- [MODULE_FILTERS.FIRST_PARTY]: {
140
- label: 'First party',
141
- defaultValue: get(filters, `${MODULE_SOURCE_TYPE}.${MODULE_FILTERS.FIRST_PARTY}`, true),
142
- },
143
- [MODULE_FILTERS.THIRD_PARTY]: {
144
- label: 'Third party',
145
- defaultValue: get(filters, `${MODULE_SOURCE_TYPE}.${MODULE_FILTERS.THIRD_PARTY}`, true),
146
- },
147
- },
148
-
149
- // When chunks data available, list available chunks as filters
150
- ...(!isEmpty(chunks) && {
151
- [MODULE_CHUNK]: {
152
- label: 'Chunk',
153
- ...chunks.reduce(
154
- (chunkFilters, { id, name }) => ({
155
- ...chunkFilters,
156
- [id]: {
157
- label: name,
158
- defaultValue: get(filters, `${MODULE_CHUNK}.${id}`, true),
159
- },
160
- }),
161
- {},
162
- ),
163
- },
164
- }),
165
-
166
- // Module source types
167
- [MODULE_FILE_TYPE]: {
168
- label: 'File type',
169
- ...MODULE_SOURCE_FILE_TYPES.reduce(
170
- (agg, fileType) => ({
171
- ...agg,
172
- [fileType]: {
173
- label: FILE_TYPE_LABELS[fileType],
174
- defaultValue: get(filters, `${MODULE_FILE_TYPE}.${fileType}`, true),
175
- },
176
- }),
177
- {},
178
- ),
179
- },
180
- };
194
+ ), [totalRowCount, resetFilters, resetAllFilters]);
181
195
 
182
196
  return (
183
197
  <div className={rootClassName}>
@@ -221,14 +235,7 @@ export const BundleModules = ({
221
235
  renderRowHeader={renderRowHeader}
222
236
  emptyMessage={emptyMessage}
223
237
  showHeaderSum
224
- title={
225
- <MetricsTableTitle
226
- title={I18N.MODULES}
227
- info={`(${items.length}/${totalRowCount})`}
228
- popoverInfo={I18N.MODULES_INFO}
229
- popoverHref={config.documentation.modules}
230
- />
231
- }
238
+ title={metricsTableTitle}
232
239
  />
233
240
  </div>
234
241
  );