@bundle-stats/ui 3.3.0-alpha.1 → 3.3.1-beta.8

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 (205) hide show
  1. package/CHANGELOG.md +65 -13
  2. package/build-storybook.log +4691 -0
  3. package/lib/app/app.js +137 -102
  4. package/lib/app/app.module.css +11 -4
  5. package/lib/app/header/header.js +1 -10
  6. package/lib/components/asset-info/asset-info.module.css +1 -0
  7. package/lib/components/bundle-assets/bundle-assets.js +104 -52
  8. package/lib/components/bundle-assets/bundle-assets.module.css +24 -26
  9. package/lib/components/bundle-assets-totals-table/bundle-assets-totals-table.js +13 -3
  10. package/lib/components/bundle-modules/bundle-modules.js +68 -68
  11. package/lib/components/bundle-modules/bundle-modules.module.css +11 -2
  12. package/lib/components/bundle-modules/bundle-modules.utils.js +14 -10
  13. package/lib/components/bundle-modules/index.js +4 -4
  14. package/lib/components/bundle-packages/bundle-packages.js +100 -44
  15. package/lib/components/bundle-packages/bundle-packages.module.css +17 -21
  16. package/lib/components/delta/delta.module.css +1 -0
  17. package/lib/components/job-header/job-header.js +3 -1
  18. package/lib/components/job-header/job-header.module.css +6 -8
  19. package/lib/components/jobs-header/jobs-header.module.css +5 -0
  20. package/lib/components/metrics-table/metrics-table.js +72 -59
  21. package/lib/components/metrics-table/metrics-table.module.css +18 -7
  22. package/lib/components/metrics-table-search/metrics-table-search.module.css +5 -4
  23. package/lib/components/metrics-table-title/metrics-table-title.js +9 -7
  24. package/lib/components/metrics-table-title/metrics-table-title.module.css +2 -0
  25. package/lib/components/module-info/module-info.js +2 -2
  26. package/lib/components/module-info/module-info.module.css +1 -0
  27. package/lib/components/summary/summary.js +1 -1
  28. package/lib/components/summary-item/summary-item.js +15 -18
  29. package/lib/components/summary-item/summary-item.module.css +1 -23
  30. package/lib/components/total-size-type-title/total-size-type-title.js +7 -3
  31. package/lib/constants.js +1 -1
  32. package/lib/css/default.css +14 -3
  33. package/lib/css/variables.css +17 -17
  34. package/lib/hooks/search-params.js +31 -31
  35. package/lib/layout/box/box.module.css +9 -0
  36. package/lib/layout/footer/footer.module.css +1 -1
  37. package/lib/layout/index.js +8 -0
  38. package/lib/layout/separator.js +48 -0
  39. package/lib/layout/separator.module.css +5 -0
  40. package/lib/ui/dropdown/dropdown.js +21 -26
  41. package/lib/ui/dropdown/dropdown.module.css +11 -12
  42. package/lib/ui/dropdown/dropdown.stories.js +2 -9
  43. package/lib/ui/empty-set/empty-set.js +5 -4
  44. package/lib/ui/empty-set/empty-set.module.css +2 -2
  45. package/lib/ui/filters/filters.container.js +1 -1
  46. package/lib/ui/filters/filters.js +22 -7
  47. package/lib/ui/filters/filters.module.css +3 -3
  48. package/lib/ui/horizontal-bar-chart/horizontal-bar-chart.js +1 -1
  49. package/lib/ui/hover-card/hover-card.js +102 -0
  50. package/lib/ui/hover-card/hover-card.module.css +29 -0
  51. package/lib/ui/hover-card/hover-card.stories.js +56 -0
  52. package/lib/ui/hover-card/index.js +18 -0
  53. package/lib/ui/icon/icon.js +6 -6
  54. package/lib/ui/icon/icon.module.css +1 -0
  55. package/lib/ui/index.js +8 -8
  56. package/lib/ui/table/table.js +5 -74
  57. package/lib/ui/table/table.module.css +41 -25
  58. package/lib/ui/table/table.stories.js +1 -1
  59. package/lib/ui/{popover → tag}/index.js +4 -4
  60. package/lib/ui/tag/tag.js +72 -0
  61. package/lib/ui/tag/tag.module.css +55 -0
  62. package/lib/ui/tag/tag.stories.js +38 -0
  63. package/lib/ui/tooltip/tooltip.js +10 -9
  64. package/lib/ui/tooltip/tooltip.module.css +2 -1
  65. package/lib-esm/app/app.js +132 -102
  66. package/lib-esm/app/app.module.css +11 -4
  67. package/lib-esm/app/header/header.js +1 -9
  68. package/lib-esm/components/asset-info/asset-info.module.css +1 -0
  69. package/lib-esm/components/bundle-assets/bundle-assets.js +97 -56
  70. package/lib-esm/components/bundle-assets/bundle-assets.module.css +24 -26
  71. package/lib-esm/components/bundle-assets-totals-table/bundle-assets-totals-table.js +7 -3
  72. package/lib-esm/components/bundle-modules/bundle-modules.js +50 -51
  73. package/lib-esm/components/bundle-modules/bundle-modules.module.css +11 -2
  74. package/lib-esm/components/bundle-modules/bundle-modules.utils.js +14 -6
  75. package/lib-esm/components/bundle-modules/index.js +5 -3
  76. package/lib-esm/components/bundle-packages/bundle-packages.js +88 -41
  77. package/lib-esm/components/bundle-packages/bundle-packages.module.css +17 -21
  78. package/lib-esm/components/delta/delta.module.css +1 -0
  79. package/lib-esm/components/job-header/job-header.js +2 -1
  80. package/lib-esm/components/job-header/job-header.module.css +6 -8
  81. package/lib-esm/components/jobs-header/jobs-header.module.css +5 -0
  82. package/lib-esm/components/metrics-table/metrics-table.js +61 -53
  83. package/lib-esm/components/metrics-table/metrics-table.module.css +18 -7
  84. package/lib-esm/components/metrics-table-search/metrics-table-search.module.css +5 -4
  85. package/lib-esm/components/metrics-table-title/metrics-table-title.js +8 -7
  86. package/lib-esm/components/metrics-table-title/metrics-table-title.module.css +2 -0
  87. package/lib-esm/components/module-info/module-info.js +2 -2
  88. package/lib-esm/components/module-info/module-info.module.css +1 -0
  89. package/lib-esm/components/summary/summary.js +1 -1
  90. package/lib-esm/components/summary-item/summary-item.js +14 -18
  91. package/lib-esm/components/summary-item/summary-item.module.css +1 -23
  92. package/lib-esm/components/total-size-type-title/total-size-type-title.js +6 -3
  93. package/lib-esm/constants.js +1 -1
  94. package/lib-esm/css/default.css +14 -3
  95. package/lib-esm/css/variables.css +17 -17
  96. package/lib-esm/hooks/search-params.js +27 -28
  97. package/lib-esm/layout/box/box.module.css +9 -0
  98. package/lib-esm/layout/footer/footer.module.css +1 -1
  99. package/lib-esm/layout/index.js +1 -0
  100. package/lib-esm/layout/separator.js +28 -0
  101. package/lib-esm/layout/separator.module.css +5 -0
  102. package/lib-esm/ui/dropdown/dropdown.js +20 -25
  103. package/lib-esm/ui/dropdown/dropdown.module.css +11 -12
  104. package/lib-esm/ui/dropdown/dropdown.stories.js +2 -7
  105. package/lib-esm/ui/empty-set/empty-set.js +4 -4
  106. package/lib-esm/ui/empty-set/empty-set.module.css +2 -2
  107. package/lib-esm/ui/filters/filters.js +20 -5
  108. package/lib-esm/ui/filters/filters.module.css +3 -3
  109. package/lib-esm/ui/hover-card/hover-card.js +83 -0
  110. package/lib-esm/ui/hover-card/hover-card.module.css +29 -0
  111. package/lib-esm/ui/hover-card/hover-card.stories.js +35 -0
  112. package/lib-esm/ui/hover-card/index.js +1 -0
  113. package/lib-esm/ui/icon/icon.js +3 -3
  114. package/lib-esm/ui/icon/icon.module.css +1 -0
  115. package/lib-esm/ui/index.js +1 -1
  116. package/lib-esm/ui/table/table.js +5 -65
  117. package/lib-esm/ui/table/table.module.css +41 -25
  118. package/lib-esm/ui/table/table.stories.js +1 -1
  119. package/lib-esm/ui/tag/index.js +1 -0
  120. package/lib-esm/ui/tag/tag.js +45 -0
  121. package/lib-esm/ui/tag/tag.module.css +55 -0
  122. package/lib-esm/ui/tag/tag.stories.js +23 -0
  123. package/lib-esm/ui/tooltip/tooltip.js +10 -9
  124. package/lib-esm/ui/tooltip/tooltip.module.css +2 -1
  125. package/package.json +33 -31
  126. package/src/app/app.jsx +136 -146
  127. package/src/app/app.module.css +11 -4
  128. package/src/app/header/header.jsx +1 -11
  129. package/src/assets/icons.svg +0 -8
  130. package/src/components/asset-info/asset-info.module.css +1 -0
  131. package/src/components/bundle-assets/bundle-assets.jsx +85 -31
  132. package/src/components/bundle-assets/bundle-assets.module.css +24 -26
  133. package/src/components/bundle-assets-totals-table/bundle-assets-totals-table.jsx +13 -25
  134. package/src/components/bundle-assets-totals-table/bundle-assets-totals-table.module.css +2 -2
  135. package/src/components/bundle-modules/bundle-modules.jsx +53 -36
  136. package/src/components/bundle-modules/bundle-modules.module.css +11 -2
  137. package/src/components/bundle-modules/bundle-modules.utils.js +16 -4
  138. package/src/components/bundle-modules/index.jsx +3 -0
  139. package/src/components/bundle-packages/bundle-packages.jsx +89 -39
  140. package/src/components/bundle-packages/bundle-packages.module.css +17 -21
  141. package/src/components/delta/delta.module.css +1 -0
  142. package/src/components/index.js +0 -1
  143. package/src/components/job-header/job-header.jsx +2 -1
  144. package/src/components/job-header/job-header.module.css +6 -8
  145. package/src/components/jobs-header/jobs-header.module.css +5 -0
  146. package/src/components/metrics-table/metrics-table.jsx +53 -44
  147. package/src/components/metrics-table/metrics-table.module.css +18 -7
  148. package/src/components/metrics-table-search/metrics-table-search.module.css +5 -4
  149. package/src/components/metrics-table-title/metrics-table-title.jsx +20 -21
  150. package/src/components/metrics-table-title/metrics-table-title.module.css +2 -0
  151. package/src/components/module-info/module-info.jsx +3 -7
  152. package/src/components/module-info/module-info.module.css +1 -0
  153. package/src/components/summary/summary.jsx +7 -8
  154. package/src/components/summary/summary.module.css +1 -1
  155. package/src/components/summary-item/summary-item.jsx +23 -40
  156. package/src/components/summary-item/summary-item.module.css +1 -34
  157. package/src/components/total-size-type-title/total-size-type-title.jsx +6 -9
  158. package/src/constants.js +11 -7
  159. package/src/css/default.css +14 -3
  160. package/src/css/variables.css +17 -17
  161. package/src/hooks/search-params.js +42 -33
  162. package/src/layout/box/box.module.css +9 -0
  163. package/src/layout/footer/footer.module.css +1 -1
  164. package/src/layout/index.js +1 -0
  165. package/src/layout/separator.jsx +25 -0
  166. package/src/layout/separator.module.css +5 -0
  167. package/src/ui/alert/alert.jsx +2 -7
  168. package/src/ui/dropdown/dropdown.jsx +16 -22
  169. package/src/ui/dropdown/dropdown.module.css +11 -12
  170. package/src/ui/empty-set/empty-set.jsx +25 -21
  171. package/src/ui/empty-set/empty-set.module.css +2 -2
  172. package/src/ui/filters/filters.jsx +64 -53
  173. package/src/ui/filters/filters.module.css +3 -3
  174. package/src/ui/hover-card/hover-card.jsx +73 -0
  175. package/src/ui/hover-card/hover-card.module.css +29 -0
  176. package/src/ui/hover-card/index.js +1 -0
  177. package/src/ui/icon/icon.jsx +11 -9
  178. package/src/ui/icon/icon.module.css +1 -0
  179. package/src/ui/index.js +1 -1
  180. package/src/ui/table/table.jsx +5 -52
  181. package/src/ui/table/table.module.css +41 -25
  182. package/src/ui/tag/index.js +1 -0
  183. package/src/ui/tag/tag.jsx +49 -0
  184. package/src/ui/tag/tag.module.css +55 -0
  185. package/src/ui/tooltip/tooltip.jsx +10 -14
  186. package/src/ui/tooltip/tooltip.module.css +2 -1
  187. package/storybook-static/favicon.ico +0 -0
  188. package/lib/ui/popover/popover.js +0 -92
  189. package/lib/ui/popover/popover.module.css +0 -66
  190. package/lib/ui/popover/popover.stories.js +0 -26
  191. package/lib-esm/ui/popover/index.js +0 -1
  192. package/lib-esm/ui/popover/popover.js +0 -71
  193. package/lib-esm/ui/popover/popover.module.css +0 -66
  194. package/lib-esm/ui/popover/popover.stories.js +0 -14
  195. package/src/assets/icons/alert-circle.svg +0 -1
  196. package/src/assets/icons/check-circle.svg +0 -1
  197. package/src/components/budget-info/budget-info.jsx +0 -65
  198. package/src/components/budget-info/budget-info.module.css +0 -40
  199. package/src/components/budget-info/index.js +0 -1
  200. package/src/components/budget-insights/budget-insights.jsx +0 -157
  201. package/src/components/budget-insights/budget-insights.module.css +0 -53
  202. package/src/components/budget-insights/index.js +0 -1
  203. package/src/ui/popover/index.js +0 -1
  204. package/src/ui/popover/popover.jsx +0 -79
  205. package/src/ui/popover/popover.module.css +0 -66
@@ -1,43 +1,30 @@
1
- import React, { useCallback, useMemo } from 'react';
1
+ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { get } from 'lodash';
4
3
  import { getBundleAssetsFileTypeComponentLink } from '@bundle-stats/utils';
5
4
  import * as webpack from '@bundle-stats/utils/lib-esm/webpack';
6
5
 
7
6
  import { ASSETS_SIZES_FILE_TYPE_MAP } from '../../constants';
8
- import { FlexStack } from '../../layout/flex-stack';
9
7
  import { MetricsTable } from '../metrics-table';
10
8
  import { ComponentLink } from '../component-link';
11
- import { BudgetInfo } from '../budget-info';
12
- import css from './bundle-assets-totals-table.module.css';
13
9
 
14
10
  export const BundleAssetsTotalsTable = ({
15
11
  className,
16
12
  jobs,
17
13
  customComponentLink: CustomComponentLink,
14
+ ...restProps
18
15
  }) => {
19
- const items = useMemo(() => webpack.compareBySection.sizes(jobs), [jobs]);
20
- const budgets = jobs[0].insights?.webpack?.budgets;
16
+ const items = webpack.compareBySection.sizes(jobs);
21
17
 
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);
18
+ const renderRowHeader = (item) => {
19
+ const fileType = ASSETS_SIZES_FILE_TYPE_MAP[item.key];
20
+ const { section, title, params } = getBundleAssetsFileTypeComponentLink(fileType, item.label);
29
21
 
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
- );
22
+ return (
23
+ <CustomComponentLink section={section} title={title} params={params}>
24
+ {item.label}
25
+ </CustomComponentLink>
26
+ );
27
+ };
41
28
 
42
29
  return (
43
30
  <MetricsTable
@@ -46,6 +33,7 @@ export const BundleAssetsTotalsTable = ({
46
33
  items={items}
47
34
  renderRowHeader={renderRowHeader}
48
35
  showHeaderSum
36
+ {...restProps}
49
37
  />
50
38
  );
51
39
  };
@@ -1,3 +1,3 @@
1
- .itemTitleText {
2
- align-items: center;
1
+ .title {
2
+ color: var(--color-text-light);
3
3
  }
@@ -18,8 +18,9 @@ import { FlexStack } from '../../layout/flex-stack';
18
18
  import { EmptySet } from '../../ui/empty-set';
19
19
  import { FileName } from '../../ui/file-name';
20
20
  import { Filters } from '../../ui/filters';
21
- import { Popover } from '../../ui/popover';
21
+ import { HoverCard } from '../../ui/hover-card';
22
22
  import { SortDropdown } from '../../ui/sort-dropdown';
23
+ import { Tag } from '../../ui/tag';
23
24
  import { Toolbar } from '../../ui/toolbar';
24
25
  import { MetricsTable } from '../metrics-table';
25
26
  import { MetricsTableSearch } from '../metrics-table-search';
@@ -31,36 +32,42 @@ import css from './bundle-modules.module.css';
31
32
  const RowHeader = ({ row, chunks, labels, CustomComponentLink }) => {
32
33
  const chunkIds = map(chunks, 'id');
33
34
 
34
- const [showPopopver, setPopover] = useState(false);
35
- const handleOnMouseEnter = useCallback(() => setPopover(true), [showPopopver]);
36
- const content = <FileName name={row.label} />;
35
+ const [showHoverCard, setHoverCard] = useState(false);
36
+ const handleOnMouseEnter = useCallback(() => setHoverCard(true), [showHoverCard]);
37
+ const content = useMemo(
38
+ () => (
39
+ <span className={css.name}>
40
+ {row.duplicated && (
41
+ <Tag className={css.nameTagDuplicated} size="small" kind={Tag.KINDS.DANGER} />
42
+ )}
43
+ <FileName className={css.nameText} name={row.label} />
44
+ </span>
45
+ ),
46
+ [row],
47
+ );
48
+
49
+ if (!showHoverCard) {
50
+ return <div onMouseEnter={handleOnMouseEnter}>{content}</div>;
51
+ }
37
52
 
38
53
  return (
39
- <div onMouseEnter={handleOnMouseEnter}>
40
- {!showPopopver ?
41
- content
42
- : (
43
- <Popover ariaLabel="View module info" label={content}>
44
- {({ popoverToggle }) => (
45
- <ModuleInfo
46
- className={css.namePopover}
47
- item={row}
48
- chunks={chunks}
49
- chunkIds={chunkIds}
50
- labels={labels}
51
- customComponentLink={CustomComponentLink}
52
- onClick={popoverToggle}
53
- />
54
- )}
55
- </Popover>
56
- )}
57
- </div>
54
+ <HoverCard label={content}>
55
+ <ModuleInfo
56
+ className={css.namePopover}
57
+ item={row}
58
+ chunks={chunks}
59
+ chunkIds={chunkIds}
60
+ labels={labels}
61
+ customComponentLink={CustomComponentLink}
62
+ />
63
+ </HoverCard>
58
64
  );
59
65
  };
60
66
 
61
67
  RowHeader.propTypes = {
62
68
  row: PropTypes.shape({
63
69
  label: PropTypes.string,
70
+ duplicated: PropTypes.bool,
64
71
  }).isRequired,
65
72
  chunks: PropTypes.arrayOf(
66
73
  PropTypes.shape({
@@ -76,10 +83,6 @@ RowHeader.defaultProps = {
76
83
  chunks: [],
77
84
  };
78
85
 
79
- const getRenderRowHeader = ({ labels, chunks, CustomComponentLink }) => (row) => (
80
- <RowHeader row={row} chunks={chunks} labels={labels} CustomComponentLink={CustomComponentLink} />
81
- );
82
-
83
86
  export const BundleModules = ({
84
87
  className,
85
88
  jobs,
@@ -100,11 +103,18 @@ export const BundleModules = ({
100
103
  }) => {
101
104
  const rootClassName = cx(css.root, className);
102
105
 
103
- const labels = useMemo(() => map(jobs, 'label'), [jobs]);
104
- const renderRowHeader = useMemo(
105
- () => getRenderRowHeader({ labels, chunks, CustomComponentLink }),
106
- [labels, chunks],
106
+ const renderRowHeader = useCallback(
107
+ (row) => (
108
+ <RowHeader
109
+ row={row}
110
+ chunks={chunks}
111
+ labels={map(jobs, 'label')}
112
+ CustomComponentLink={CustomComponentLink}
113
+ />
114
+ ),
115
+ [jobs, chunks, CustomComponentLink],
107
116
  );
117
+
108
118
  const emptyMessage = (
109
119
  <EmptySet
110
120
  resources="modules"
@@ -120,6 +130,11 @@ export const BundleModules = ({
120
130
  defaultValue: filters.changed,
121
131
  disabled: jobs.length <= 1,
122
132
  },
133
+ [MODULE_FILTERS.DUPLICATED]: {
134
+ label: 'Duplicated',
135
+ defaultValue: filters[MODULE_FILTERS.DUPLICATED],
136
+ disabled: jobs.length <= 1,
137
+ },
123
138
  [MODULE_SOURCE_TYPE]: {
124
139
  label: 'Source',
125
140
  [MODULE_FILTERS.FIRST_PARTY]: {
@@ -184,7 +199,7 @@ export const BundleModules = ({
184
199
  </FlexStack>
185
200
  )}
186
201
  >
187
- <FlexStack>
202
+ <FlexStack space="xxsmall">
188
203
  <MetricsTableSearch
189
204
  className={css.toolbarSearch}
190
205
  search={search}
@@ -240,10 +255,12 @@ BundleModules.propTypes = {
240
255
  jobs: PropTypes.array, // eslint-disable-line react/forbid-prop-types
241
256
 
242
257
  /** Chunks data */
243
- chunks: PropTypes.arrayOf(PropTypes.shape({
244
- id: PropTypes.string,
245
- name: PropTypes.string,
246
- })).isRequired,
258
+ chunks: PropTypes.arrayOf(
259
+ PropTypes.shape({
260
+ id: PropTypes.string,
261
+ name: PropTypes.string,
262
+ }),
263
+ ).isRequired,
247
264
 
248
265
  /** total row count */
249
266
  totalRowCount: PropTypes.number,
@@ -12,6 +12,15 @@
12
12
  margin-right: -1px; /** collapse border */
13
13
  }
14
14
 
15
- .namePopover {
16
- min-width: 280px;
15
+ .nameTagDuplicated {
16
+ margin-right: var(--space-xxxsmall);
17
+ vertical-align: baseline;
18
+ }
19
+
20
+ .nameTagDuplicated::before {
21
+ content: 'D';
22
+ }
23
+
24
+ .nameText {
25
+ vertical-align: middle;
17
26
  }
@@ -12,9 +12,15 @@ import {
12
12
  import { SORT_BY_NAME, SORT_BY_SIZE, SORT_BY_DELTA } from './bundle-modules.constants';
13
13
 
14
14
  export const addRowSourceFlag = (row) => {
15
- const { key } = row;
16
- const thirdParty = Boolean(key.match(MODULE_PATH_PACKAGES));
17
- return { ...row, thirdParty };
15
+ const { key, runs } = row;
16
+
17
+ // @NOTE Assign instead destructuring for perf reasons
18
+ // eslint-disable-next-line no-param-reassign
19
+ row.thirdParty = Boolean(key.match(MODULE_PATH_PACKAGES));
20
+ // eslint-disable-next-line no-param-reassign
21
+ row.duplicated = Boolean(runs.find((run) => run?.duplicated === true));
22
+
23
+ return row;
18
24
  };
19
25
 
20
26
  export const getCustomSort = (sortBy) => (item) => {
@@ -39,10 +45,16 @@ export const getRowFilter = (filters) => (row) => {
39
45
  return false;
40
46
  }
41
47
 
48
+ // Skip not duplicated rows
49
+ if (filters[MODULE_FILTERS.DUPLICATED] && !row.duplicated) {
50
+ return false;
51
+ }
52
+
42
53
  // Skip not matching source type
43
54
  if (
44
55
  !(
45
- (filters[`${MODULE_SOURCE_TYPE}.${MODULE_FILTERS.FIRST_PARTY}`] && row.thirdParty === false) ||
56
+ (filters[`${MODULE_SOURCE_TYPE}.${MODULE_FILTERS.FIRST_PARTY}`] &&
57
+ row.thirdParty === false) ||
46
58
  (filters[`${MODULE_SOURCE_TYPE}.${MODULE_FILTERS.THIRD_PARTY}`] && row.thirdParty === true)
47
59
  )
48
60
  ) {
@@ -2,6 +2,7 @@ import React, { useMemo } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { uniqBy, map } from 'lodash';
4
4
  import {
5
+ MODULE_FILTERS,
5
6
  getModuleChunkFilters,
6
7
  getModuleSourceTypeFilters,
7
8
  getModuleFileTypeFilters,
@@ -33,12 +34,14 @@ export const BundleModules = (props) => {
33
34
  ...getModuleSourceTypeFilters(true),
34
35
  ...getModuleChunkFilters(chunkIds, true),
35
36
  ...getModuleFileTypeFilters(true),
37
+ [MODULE_FILTERS.DUPLICATED]: false,
36
38
  },
37
39
  allEntriesFilters: {
38
40
  changed: false,
39
41
  ...getModuleSourceTypeFilters(true),
40
42
  ...getModuleChunkFilters(chunkIds, true),
41
43
  ...getModuleFileTypeFilters(true),
44
+ [MODULE_FILTERS.DUPLICATED]: false,
42
45
  },
43
46
  }),
44
47
  [jobs],
@@ -1,4 +1,4 @@
1
- import React, {useCallback} from 'react';
1
+ import React, { useCallback, useMemo, useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import cx from 'classnames';
4
4
  import {
@@ -15,8 +15,9 @@ import { Stack } from '../../layout/stack';
15
15
  import { FlexStack } from '../../layout/flex-stack';
16
16
  import { EmptySet } from '../../ui/empty-set';
17
17
  import { Filters } from '../../ui/filters';
18
- import { Popover } from '../../ui/popover';
18
+ import { HoverCard } from '../../ui/hover-card';
19
19
  import { SortDropdown } from '../../ui/sort-dropdown';
20
+ import { Tag } from '../../ui/tag';
20
21
  import { Toolbar } from '../../ui/toolbar';
21
22
  import { FileName } from '../../ui/file-name';
22
23
  import { ComponentLink } from '../component-link';
@@ -27,27 +28,28 @@ import { MetricsTableTitle } from '../metrics-table-title';
27
28
  import css from './bundle-packages.module.css';
28
29
 
29
30
  const PackagePopoverContent = ({ name, fullName, path, duplicate, CustomComponentLink }) => {
30
- const normalizedPackagePath = path || `node_modules/${fullName.split(PACKAGES_SEPARATOR).join('/node_modules/')}/`;
31
+ const normalizedPackagePath =
32
+ path || `node_modules/${fullName.split(PACKAGES_SEPARATOR).join('/node_modules/')}/`;
31
33
  const [normalizedName, packageId] = name.split(PACKAGE_ID_SEPARATOR);
32
34
 
33
35
  return (
34
- <Stack space="xxsmall" className={css.packagePopover}>
36
+ <Stack space="xsmall" className={css.packageHoverCard}>
35
37
  <Stack space="xxxsmall">
36
- <h3 className={css.packagePopoverTitle}>
38
+ <h3 className={css.packageHoverCardTitle}>
37
39
  {normalizedName}
38
40
  {packageId && (
39
- <span className={css.packagePopoverTitleIndex}>
41
+ <span className={css.packageHoverCardTitleIndex}>
40
42
  {`${PACKAGE_ID_SEPARATOR}${packageId}`}
41
43
  </span>
42
44
  )}
43
45
  </h3>
44
- <p className={css.packagePopoverPath}>
45
- <FileName className={css.packagePopoverPathValue} name={normalizedPackagePath} />
46
+ <p className={css.packageHoverCardPath}>
47
+ <FileName className={css.packageHoverCardPathValue} name={normalizedPackagePath} />
46
48
  </p>
47
49
  </Stack>
48
50
 
49
- <ul className={css.packagePopoverList}>
50
- <li className={css.packagePopoverItem}>
51
+ <ul className={css.packageHoverCardList}>
52
+ <li className={css.packageHoverCardItem}>
51
53
  <a
52
54
  href={`https://www.npmjs.com/package/${normalizedName}`}
53
55
  target="_blank"
@@ -56,7 +58,7 @@ const PackagePopoverContent = ({ name, fullName, path, duplicate, CustomComponen
56
58
  npmjs.com
57
59
  </a>
58
60
  </li>
59
- <li className={css.packagePopoverItem}>
61
+ <li className={css.packageHoverCardItem}>
60
62
  <a
61
63
  href={`https://bundlephobia.com/result?p=${normalizedName}`}
62
64
  target="_blank"
@@ -67,7 +69,7 @@ const PackagePopoverContent = ({ name, fullName, path, duplicate, CustomComponen
67
69
  </li>
68
70
  </ul>
69
71
 
70
- <Stack space="xxxsmall" className={css.packagePopover.actions}>
72
+ <Stack space="xxxsmall" className={css.packageHoverCardActions}>
71
73
  {duplicate && (
72
74
  <div>
73
75
  <CustomComponentLink {...getBundlePackagesByNameComponentLink(normalizedName)}>
@@ -96,38 +98,86 @@ PackagePopoverContent.defaultProps = {
96
98
  path: '',
97
99
  };
98
100
 
99
- const PackageRowHeader = ({ item, CustomComponentLink }) => {
100
- const packageNames = item.label.split(PACKAGES_SEPARATOR);
101
- const { path } = item.runs[0] || {};
101
+ const PackageName = ({ packageName, path, showDuplicateFlag, row, CustomComponentLink }) => {
102
+ const [showHoverCard, setHoverCard] = useState(false);
103
+ const handleOnMouseEnter = useCallback(() => setHoverCard(true), [showHoverCard]);
104
+
105
+ const label = useMemo(
106
+ () => (
107
+ <>
108
+ {showDuplicateFlag && row.duplicate && (
109
+ <Tag
110
+ className={css.packageNameTagDuplicate}
111
+ title="Duplicate package"
112
+ kind={Tag.KINDS.DANGER}
113
+ size={Tag.SIZES.SMALL}
114
+ />
115
+ )}
116
+ <span className={css.packageNameLabel}>{packageName}</span>
117
+ </>
118
+ ),
119
+ [packageName, showDuplicateFlag, row],
120
+ );
121
+
122
+ if (!showHoverCard) {
123
+ return (
124
+ <span className={css.packageName} onMouseEnter={handleOnMouseEnter}>
125
+ {label}
126
+ </span>
127
+ );
128
+ }
102
129
 
103
130
  return (
104
- <span className={css.packageNames}>
105
- {packageNames.map((packageName, index) => {
106
- // Render duplicate flag only for the last entry
107
- const duplicateFlag = index === packageNames.length - 1 && item.duplicate && (
108
- <span className={css.duplicate} title="Duplicate package">
109
- D
110
- </span>
111
- );
131
+ <HoverCard className={css.packageName} label={<span className={css.packageName}>{label}</span>}>
132
+ <PackagePopoverContent
133
+ name={packageName}
134
+ path={path}
135
+ fullName={row.label}
136
+ duplicate={row.duplicate}
137
+ CustomComponentLink={CustomComponentLink}
138
+ />
139
+ </HoverCard>
140
+ );
141
+ };
112
142
 
113
- return (
114
- <Popover className={css.packageName} icon={duplicateFlag} label={packageName}>
115
- <PackagePopoverContent
116
- name={packageName}
117
- path={path}
118
- fullName={item.label}
119
- duplicate={item.duplicate}
120
- CustomComponentLink={CustomComponentLink}
121
- />
122
- </Popover>
123
- );
124
- })}
143
+ PackageName.propTypes = {
144
+ packageName: PropTypes.string.isRequired,
145
+ path: PropTypes.string.isRequired,
146
+ showDuplicateFlag: PropTypes.bool.isRequired,
147
+ row: PropTypes.shape({
148
+ label: PropTypes.string,
149
+ duplicate: PropTypes.bool,
150
+ runs: PropTypes.arrayOf(
151
+ PropTypes.shape({
152
+ path: PropTypes.string,
153
+ }),
154
+ ).isRequired,
155
+ }).isRequired,
156
+ CustomComponentLink: PropTypes.elementType.isRequired,
157
+ };
158
+
159
+ const RowHeader = ({ row, CustomComponentLink }) => {
160
+ const packageNames = row.label.split(PACKAGES_SEPARATOR);
161
+ const { path } = row.runs[0] || {};
162
+
163
+ return (
164
+ <span className={css.packageNames}>
165
+ {packageNames.map((packageName, index) => (
166
+ <PackageName
167
+ packageName={packageName}
168
+ path={path}
169
+ // Render duplicate flag only for the last entry
170
+ showDuplicateFlag={index === packageNames.length - 1}
171
+ row={row}
172
+ CustomComponentLink={CustomComponentLink}
173
+ />
174
+ ))}
125
175
  </span>
126
176
  );
127
177
  };
128
178
 
129
- PackageRowHeader.propTypes = {
130
- item: PropTypes.shape({
179
+ RowHeader.propTypes = {
180
+ row: PropTypes.shape({
131
181
  label: PropTypes.string,
132
182
  duplicate: PropTypes.bool,
133
183
  runs: PropTypes.arrayOf(
@@ -168,7 +218,7 @@ export const BundlePackages = (props) => {
168
218
  );
169
219
 
170
220
  const renderRowHeader = useCallback(
171
- (item) => <PackageRowHeader item={item} CustomComponentLink={CustomComponentLink} />,
221
+ (row) => <RowHeader row={row} CustomComponentLink={CustomComponentLink} />,
172
222
  [CustomComponentLink],
173
223
  );
174
224
 
@@ -186,7 +236,7 @@ export const BundlePackages = (props) => {
186
236
  </FlexStack>
187
237
  )}
188
238
  >
189
- <FlexStack>
239
+ <FlexStack space="xxsmall">
190
240
  <MetricsTableSearch
191
241
  className={css.toolbarSearch}
192
242
  placeholder="Search by name"
@@ -12,23 +12,8 @@
12
12
  margin-right: -1px; /** collapse border */
13
13
  }
14
14
 
15
- .duplicate {
16
- margin-right: var(--space-xxxsmall);
17
- display: inline-block;
18
- width: var(--space-small);
19
- height: var(--space-small);
20
- font-size: 10px;
21
- font-weight: bold;
22
- line-height: var(--space-small);
23
- text-align: center;
24
- text-transform: uppercase;
25
- vertical-align: middle;
26
-
27
- background: var(--color-danger);
28
- color: var(--color-light);
29
- }
30
-
31
15
  .packageName {
16
+ display: inline-block;
32
17
  color: var(--color-text-light);
33
18
  }
34
19
 
@@ -43,22 +28,33 @@
43
28
  color: var(--color-text-ultra-light);
44
29
  }
45
30
 
46
- .packagePopover {
47
- min-width: 320px;
31
+ .packageNameTagDuplicate {
32
+ margin-right: var(--space-xxxsmall);
33
+ vertical-align: middle;
34
+ }
35
+
36
+ .packageNameLabel {
37
+ vertical-align: middle;
38
+ }
39
+
40
+ .packageNameTagDuplicate::before {
41
+ content: 'd';
48
42
  }
49
43
 
50
- .packagePopoverTitleIndex {
44
+
45
+ .packageHoverCardTitleIndex {
51
46
  color: var(--color-text-light);
52
47
  font-weight: normal;
53
48
  }
54
49
 
55
- .packagePopoverPathValue {
50
+ .packageHoverCardPathValue {
51
+ width: 100%;
56
52
  padding: var(--space-xxxsmall);
57
53
  background: var(--color-highlight-warning);
58
54
  font-family: var(--font-family-monospace);
59
55
  }
60
56
 
61
- .packagePopoverList {
57
+ .packageHoverCardList {
62
58
  padding: 0;
63
59
  list-style: inside;
64
60
  }
@@ -28,6 +28,7 @@
28
28
  }
29
29
 
30
30
  .inverted {
31
+ border-radius: var(--radius-small);
31
32
  background-color: var(--color-gray-ultra-light);
32
33
  color: var(--color-light);
33
34
  }
@@ -4,7 +4,6 @@ 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';
8
7
  export { ComponentLink } from './component-link';
9
8
  export { DuplicatePackagesWarning } from './duplicate-packages-warning';
10
9
  export { Delta } from './delta';
@@ -8,6 +8,7 @@ import { SOURCE_PATH_WEBPACK_STATS, formatDate, formatTime } from '@bundle-stats
8
8
  import { FlexStack } from '../../layout/flex-stack';
9
9
  import { Stack } from '../../layout/stack';
10
10
  import { Icon } from '../../ui/icon';
11
+ import { Tag } from '../../ui/tag';
11
12
  import css from './job-header.module.css';
12
13
 
13
14
  export const JobHeader = (props) => {
@@ -20,7 +21,7 @@ export const JobHeader = (props) => {
20
21
  <Stack className={rootClassName} space="xxsmall">
21
22
  <h1 className={css.title}>
22
23
  <span>{`#${job.internalBuildNumber}`}</span>
23
- {tag && <span className={css.tag}>{tag}</span>}
24
+ {tag && <Tag className={css.tag}>{tag}</Tag>}
24
25
  </h1>
25
26
  <FlexStack className={css.meta} space="xxsmall">
26
27
  {builtAt && (
@@ -1,22 +1,20 @@
1
1
  .title {
2
+ font-size: var(--size-xlarge);
2
3
  line-height: 1;
3
4
  font-weight: bold;
4
5
  vertical-align: top;
5
6
  }
6
7
 
7
8
  .tag {
8
- padding: var(--space-xxxsmall);
9
- margin-left: var(--space-xxxsmall);
10
- background-color: var(--color-highlight);
11
- color: var(--color-text-light);
12
- display: inline-block;
13
- font-size: 9px;
14
- line-height: var(--space-xxsmall);
15
- font-weight: normal;
9
+ margin-left: var(--space-xxsmall);
16
10
  vertical-align: top;
11
+ outline: 1px solid var(--color-outline);
12
+ background: var(--color-highlight);
13
+ color: var(--color-text-light);
17
14
  }
18
15
 
19
16
  .meta {
17
+ font-size: var(--size-small);
20
18
  color: var(--color-text-light);
21
19
  }
22
20
 
@@ -9,3 +9,8 @@
9
9
  padding: 0 var(--space-medium);
10
10
  border-left: 1px solid var(--color-outline);
11
11
  }
12
+
13
+ .item:first-child {
14
+ border-left: 0;
15
+ padding-left: 0;
16
+ }