@bundle-stats/ui 4.3.0 → 4.3.3

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 (129) hide show
  1. package/lib/app/app.module.css +7 -4
  2. package/lib/assets/icons.svg +3 -0
  3. package/lib/assets/icons.svg.js +2 -0
  4. package/lib/assets/icons.svg.js.map +1 -1
  5. package/lib/components/asset-info/asset-info.js +8 -1
  6. package/lib/components/asset-info/asset-info.js.map +1 -1
  7. package/lib/components/asset-info/asset-info.module.css +10 -0
  8. package/lib/components/bundle-assets/bundle-assets.utils.js +3 -1
  9. package/lib/components/bundle-assets/bundle-assets.utils.js.map +1 -1
  10. package/lib/components/bundle-assets-totals-chart-bars/bundle-assets-totals-chart-bars.js +4 -2
  11. package/lib/components/bundle-assets-totals-chart-bars/bundle-assets-totals-chart-bars.js.map +1 -1
  12. package/lib/components/bundle-assets-totals-chart-bars/bundle-assets-totals-chart-bars.module.css +1 -1
  13. package/lib/components/bundle-modules/bundle-modules.js +11 -11
  14. package/lib/components/bundle-modules/bundle-modules.js.map +1 -1
  15. package/lib/components/bundle-modules/bundle-modules.utils.js +5 -5
  16. package/lib/components/bundle-modules/bundle-modules.utils.js.map +1 -1
  17. package/lib/components/bundle-modules/index.js +1 -1
  18. package/lib/components/bundle-modules/index.js.map +1 -1
  19. package/lib/components/index.js +1 -3
  20. package/lib/components/index.js.map +1 -1
  21. package/lib/components/job-header/job-header.js +12 -11
  22. package/lib/components/job-header/job-header.js.map +1 -1
  23. package/lib/components/jobs-header/index.js.map +1 -1
  24. package/lib/components/jobs-header/jobs-header.js +21 -14
  25. package/lib/components/jobs-header/jobs-header.js.map +1 -1
  26. package/lib/components/jobs-header/jobs-header.module.css +51 -4
  27. package/lib/components/module-info/module-info.js +20 -9
  28. package/lib/components/module-info/module-info.js.map +1 -1
  29. package/lib/components/module-info/module-info.module.css +7 -2
  30. package/lib/components/package-info/package-info.js +15 -8
  31. package/lib/components/package-info/package-info.js.map +1 -1
  32. package/lib/components/package-info/package-info.module.css +10 -0
  33. package/lib/components/summary/summary.js +1 -2
  34. package/lib/components/summary/summary.js.map +1 -1
  35. package/lib/components/summary/summary.module.css +11 -2
  36. package/lib/ui/horizontal-bar-chart/horizontal-bar-chart.module.css +2 -0
  37. package/lib/ui/icon/icon.js +1 -0
  38. package/lib/ui/icon/icon.js.map +1 -1
  39. package/lib/ui/tooltip/tooltip.module.css +0 -1
  40. package/lib-esm/app/app.module.css +7 -4
  41. package/lib-esm/assets/icons.svg +3 -0
  42. package/lib-esm/assets/icons.svg.js +2 -0
  43. package/lib-esm/assets/icons.svg.js.map +1 -1
  44. package/lib-esm/components/asset-info/asset-info.js +9 -2
  45. package/lib-esm/components/asset-info/asset-info.js.map +1 -1
  46. package/lib-esm/components/asset-info/asset-info.module.css +10 -0
  47. package/lib-esm/components/bundle-assets/bundle-assets.utils.js +3 -1
  48. package/lib-esm/components/bundle-assets/bundle-assets.utils.js.map +1 -1
  49. package/lib-esm/components/bundle-assets-totals-chart-bars/bundle-assets-totals-chart-bars.js +4 -2
  50. package/lib-esm/components/bundle-assets-totals-chart-bars/bundle-assets-totals-chart-bars.js.map +1 -1
  51. package/lib-esm/components/bundle-assets-totals-chart-bars/bundle-assets-totals-chart-bars.module.css +1 -1
  52. package/lib-esm/components/bundle-modules/bundle-modules.js +11 -11
  53. package/lib-esm/components/bundle-modules/bundle-modules.js.map +1 -1
  54. package/lib-esm/components/bundle-modules/bundle-modules.utils.js +3 -3
  55. package/lib-esm/components/bundle-modules/bundle-modules.utils.js.map +1 -1
  56. package/lib-esm/components/bundle-modules/index.js +2 -2
  57. package/lib-esm/components/bundle-modules/index.js.map +1 -1
  58. package/lib-esm/components/index.js +0 -1
  59. package/lib-esm/components/index.js.map +1 -1
  60. package/lib-esm/components/job-header/job-header.js +12 -11
  61. package/lib-esm/components/job-header/job-header.js.map +1 -1
  62. package/lib-esm/components/jobs-header/index.js.map +1 -1
  63. package/lib-esm/components/jobs-header/jobs-header.js +20 -13
  64. package/lib-esm/components/jobs-header/jobs-header.js.map +1 -1
  65. package/lib-esm/components/jobs-header/jobs-header.module.css +51 -4
  66. package/lib-esm/components/module-info/module-info.js +21 -10
  67. package/lib-esm/components/module-info/module-info.js.map +1 -1
  68. package/lib-esm/components/module-info/module-info.module.css +7 -2
  69. package/lib-esm/components/package-info/package-info.js +15 -8
  70. package/lib-esm/components/package-info/package-info.js.map +1 -1
  71. package/lib-esm/components/package-info/package-info.module.css +10 -0
  72. package/lib-esm/components/summary/summary.js +1 -2
  73. package/lib-esm/components/summary/summary.js.map +1 -1
  74. package/lib-esm/components/summary/summary.module.css +11 -2
  75. package/lib-esm/ui/horizontal-bar-chart/horizontal-bar-chart.module.css +2 -0
  76. package/lib-esm/ui/icon/icon.js +1 -0
  77. package/lib-esm/ui/icon/icon.js.map +1 -1
  78. package/lib-esm/ui/tooltip/tooltip.module.css +0 -1
  79. package/package.json +5 -5
  80. package/src/app/app.module.css +7 -4
  81. package/src/assets/icons/external-link.svg +1 -0
  82. package/src/assets/icons.svg +3 -0
  83. package/src/assets/icons.svg.jsx +13 -0
  84. package/src/components/asset-info/asset-info.module.css +10 -0
  85. package/src/components/asset-info/asset-info.tsx +30 -9
  86. package/src/components/bundle-assets/bundle-assets.utils.js +3 -2
  87. package/src/components/bundle-assets-totals-chart-bars/bundle-assets-totals-chart-bars.jsx +3 -2
  88. package/src/components/bundle-assets-totals-chart-bars/bundle-assets-totals-chart-bars.module.css +1 -1
  89. package/src/components/bundle-modules/bundle-modules.jsx +12 -11
  90. package/src/components/bundle-modules/bundle-modules.utils.js +5 -3
  91. package/src/components/bundle-modules/index.jsx +2 -2
  92. package/src/components/index.js +0 -1
  93. package/src/components/job-header/job-header.jsx +16 -13
  94. package/src/components/jobs-header/jobs-header.module.css +51 -4
  95. package/src/components/jobs-header/jobs-header.stories.tsx +51 -0
  96. package/src/components/jobs-header/jobs-header.tsx +90 -0
  97. package/src/components/module-info/module-info.module.css +7 -2
  98. package/src/components/module-info/module-info.tsx +45 -3
  99. package/src/components/package-info/package-info.module.css +10 -0
  100. package/src/components/package-info/package-info.tsx +46 -17
  101. package/src/components/summary/summary.module.css +11 -2
  102. package/src/components/summary/summary.tsx +15 -15
  103. package/src/ui/horizontal-bar-chart/horizontal-bar-chart.module.css +2 -0
  104. package/src/ui/icon/icon.jsx +1 -0
  105. package/src/ui/tooltip/tooltip.module.css +0 -1
  106. package/types/components/asset-info/asset-info.d.ts +1 -0
  107. package/types/components/bundle-modules/bundle-modules.utils.d.ts +1 -1
  108. package/types/components/index.d.ts +0 -1
  109. package/types/components/jobs-header/index.d.ts +1 -1
  110. package/types/components/jobs-header/jobs-header.d.ts +18 -15
  111. package/types/components/module-info/module-info.d.ts +2 -0
  112. package/types/ui/icon/icon.d.ts +1 -0
  113. package/lib/components/job-meta/index.js +0 -18
  114. package/lib/components/job-meta/index.js.map +0 -1
  115. package/lib/components/job-meta/job-meta.js +0 -34
  116. package/lib/components/job-meta/job-meta.js.map +0 -1
  117. package/lib/components/job-meta/job-meta.module.css +0 -19
  118. package/lib-esm/components/job-meta/index.js +0 -2
  119. package/lib-esm/components/job-meta/index.js.map +0 -1
  120. package/lib-esm/components/job-meta/job-meta.js +0 -27
  121. package/lib-esm/components/job-meta/job-meta.js.map +0 -1
  122. package/lib-esm/components/job-meta/job-meta.module.css +0 -19
  123. package/src/components/job-meta/index.js +0 -1
  124. package/src/components/job-meta/job-meta.jsx +0 -47
  125. package/src/components/job-meta/job-meta.module.css +0 -19
  126. package/src/components/jobs-header/jobs-header.jsx +0 -43
  127. package/types/components/job-meta/index.d.ts +0 -1
  128. package/types/components/job-meta/job-meta.d.ts +0 -18
  129. /package/src/components/jobs-header/{index.js → index.ts} +0 -0
@@ -2,14 +2,17 @@ import React, { useMemo } from 'react';
2
2
  import cx from 'classnames';
3
3
  import noop from 'lodash/noop';
4
4
  import {
5
+ FILE_TYPE_LABELS,
5
6
  MetricRunInfo,
6
7
  getBundleModulesByChunk,
7
8
  getBundleAssetsByEntryType,
9
+ getBundleAssetsFileTypeComponentLink,
8
10
  getModuleFileType,
9
11
  } from '@bundle-stats/utils';
10
12
  import { Asset, MetaChunk } from '@bundle-stats/utils/types/webpack';
11
13
 
12
14
  import { FlexStack } from '../../layout/flex-stack';
15
+ import { Stack } from '../../layout/stack';
13
16
  import { Tag } from '../../ui/tag';
14
17
  import { ComponentLink } from '../component-link';
15
18
  import { EntryInfo } from '../entry-info';
@@ -57,6 +60,7 @@ interface AssetInfoProps {
57
60
  isEntry?: boolean;
58
61
  isInitial?: boolean;
59
62
  isNotPredicative?: boolean;
63
+ fileType?: string;
60
64
  runs: Array<Asset & MetricRunInfo>;
61
65
  };
62
66
  chunks?: Array<MetaChunk>;
@@ -117,17 +121,34 @@ export const AssetInfo = (props: AssetInfoProps & React.ComponentProps<'div'>) =
117
121
  );
118
122
  }, [item]);
119
123
 
124
+ const fileTypeLabel = FILE_TYPE_LABELS[item.fileType as keyof typeof FILE_TYPE_LABELS];
125
+
120
126
  return (
121
127
  <EntryInfo item={item} labels={labels} tags={tags} className={cx(css.root, className)}>
122
- {currentRun?.chunkId && chunks && (
123
- <ChunkModulesLink
124
- as={CustomComponentLink}
125
- chunks={chunks}
126
- chunkId={currentRun?.chunkId}
127
- name={currentRun?.name}
128
- onClick={onClick}
129
- />
130
- )}
128
+ <Stack space="small">
129
+ {item.fileType && (
130
+ <p>
131
+ <span className={css.label}>File type</span>
132
+ <Tag
133
+ as={CustomComponentLink}
134
+ {...getBundleAssetsFileTypeComponentLink(item.fileType, fileTypeLabel)}
135
+ onClick={onClick}
136
+ >
137
+ {fileTypeLabel}
138
+ </Tag>
139
+ </p>
140
+ )}
141
+
142
+ {currentRun?.chunkId && chunks && (
143
+ <ChunkModulesLink
144
+ as={CustomComponentLink}
145
+ chunks={chunks}
146
+ chunkId={currentRun?.chunkId}
147
+ name={currentRun?.name}
148
+ onClick={onClick}
149
+ />
150
+ )}
151
+ </Stack>
131
152
  </EntryInfo>
132
153
  );
133
154
  };
@@ -8,8 +8,8 @@ export const addRowAssetFlags = (row) => {
8
8
  const isEntry = runs.map((run) => run?.isEntry).includes(true);
9
9
  const isInitial = runs.map((run) => run?.isInitial).includes(true);
10
10
  const isChunk = runs.map((run) => run?.isChunk).includes(true);
11
-
12
11
  const isAsset = !(isEntry || isInitial || isChunk);
12
+ const fileType = getFileType(row.key);
13
13
 
14
14
  return {
15
15
  ...row,
@@ -17,6 +17,7 @@ export const addRowAssetFlags = (row) => {
17
17
  isInitial,
18
18
  isChunk,
19
19
  isAsset,
20
+ fileType,
20
21
  };
21
22
  };
22
23
 
@@ -64,7 +65,7 @@ export const getRowFilter = (filters) => (item) => {
64
65
  return false;
65
66
  }
66
67
 
67
- if (!filters[`${ASSET_FILE_TYPE}.${getFileType(item.key)}`]) {
68
+ if (!filters[`${ASSET_FILE_TYPE}.${item.fileType}`]) {
68
69
  return false;
69
70
  }
70
71
 
@@ -12,6 +12,7 @@ import { HorizontalBarChart } from '../../ui/horizontal-bar-chart';
12
12
  import { ComponentLink } from '../component-link'
13
13
  import { getColors } from '../../utils';
14
14
  import { Stack } from '../../layout/stack';
15
+ import { JobName } from '../job-name';
15
16
  import { MetricRunInfo } from '../metric-run-info';
16
17
  import css from './bundle-assets-totals-chart-bars.module.css';
17
18
 
@@ -63,7 +64,7 @@ export const BundleAssetsTotalsChartBars = ({
63
64
  const colors = getColors(max(map(dataGraphs, (values) => values.length)));
64
65
 
65
66
  return (
66
- <Stack className={rootClassName} space="medium">
67
+ <Stack className={rootClassName} space="small">
67
68
  {dataGraphs.map((data, runIndex) => {
68
69
  const { internalBuildNumber } = jobs[runIndex];
69
70
 
@@ -76,7 +77,7 @@ export const BundleAssetsTotalsChartBars = ({
76
77
 
77
78
  return (
78
79
  <div key={internalBuildNumber || runIndex} className={css.item}>
79
- <h3 className={css.itemTitle}>{`Job #${internalBuildNumber}`}</h3>
80
+ <h3 className={css.itemTitle}><JobName internalBuildNumber={internalBuildNumber} /></h3>
80
81
  <HorizontalBarChart
81
82
  className={css.itemChart}
82
83
  data={{ labels, values }}
@@ -6,7 +6,7 @@
6
6
  position: absolute;
7
7
  right: 0;
8
8
  bottom: 100%;
9
- margin: 0 0 var(--space-xxxsmall);
9
+ margin: 0 0 2px;
10
10
 
11
11
  color: var(--color-text-light);
12
12
  font-size: var(--size-xsmall);
@@ -40,17 +40,6 @@ const getFilters = ({ filters, compareMode, chunks }) => ({
40
40
  label: 'Duplicate',
41
41
  defaultValue: filters[MODULE_FILTERS.DUPLICATED],
42
42
  },
43
- [MODULE_SOURCE_TYPE]: {
44
- label: 'Source',
45
- [MODULE_FILTERS.FIRST_PARTY]: {
46
- label: 'First party',
47
- defaultValue: get(filters, `${MODULE_SOURCE_TYPE}.${MODULE_FILTERS.FIRST_PARTY}`, true),
48
- },
49
- [MODULE_FILTERS.THIRD_PARTY]: {
50
- label: 'Third party',
51
- defaultValue: get(filters, `${MODULE_SOURCE_TYPE}.${MODULE_FILTERS.THIRD_PARTY}`, true),
52
- },
53
- },
54
43
 
55
44
  // When chunks data available, list available chunks as filters
56
45
  ...(!isEmpty(chunks) && {
@@ -69,6 +58,18 @@ const getFilters = ({ filters, compareMode, chunks }) => ({
69
58
  },
70
59
  }),
71
60
 
61
+ [MODULE_SOURCE_TYPE]: {
62
+ label: 'Source',
63
+ [MODULE_FILTERS.FIRST_PARTY]: {
64
+ label: 'First party',
65
+ defaultValue: get(filters, `${MODULE_SOURCE_TYPE}.${MODULE_FILTERS.FIRST_PARTY}`, true),
66
+ },
67
+ [MODULE_FILTERS.THIRD_PARTY]: {
68
+ label: 'Third party',
69
+ defaultValue: get(filters, `${MODULE_SOURCE_TYPE}.${MODULE_FILTERS.THIRD_PARTY}`, true),
70
+ },
71
+ },
72
+
72
73
  // Module source types
73
74
  [MODULE_FILE_TYPE]: {
74
75
  label: 'File type',
@@ -13,14 +13,17 @@ import {
13
13
 
14
14
  import { SORT_BY_NAME, SORT_BY_SIZE, SORT_BY_DELTA } from './bundle-modules.constants';
15
15
 
16
- export const addRowSourceFlag = (row) => {
16
+ export const addRowFlags = (row) => {
17
17
  const { key, runs } = row;
18
18
 
19
19
  // @NOTE Assign instead destructuring for perf reasons
20
+
20
21
  // eslint-disable-next-line no-param-reassign
21
22
  row.thirdParty = Boolean(key.match(MODULE_PATH_PACKAGES));
22
23
  // eslint-disable-next-line no-param-reassign
23
24
  row.duplicated = Boolean(runs.find((run) => run?.duplicated === true));
25
+ // eslint-disable-next-line no-param-reassign
26
+ row.fileType = getModuleSourceFileType(row.key);
24
27
 
25
28
  return row;
26
29
  };
@@ -64,8 +67,7 @@ export const getRowFilter = (filters) => (row) => {
64
67
  }
65
68
 
66
69
  // Skip not matching source file types
67
- const fileType = getModuleSourceFileType(row.key);
68
- if (!filters[`${MODULE_FILE_TYPE}.${fileType}`]) {
70
+ if (!filters[`${MODULE_FILE_TYPE}.${row.fileType}`]) {
69
71
  return false;
70
72
  }
71
73
 
@@ -14,7 +14,7 @@ import { useRowsSort } from '../../hooks/rows-sort';
14
14
  import { useSearchParams } from '../../hooks/search-params';
15
15
  import { BundleModules as BundleModulesComponent } from './bundle-modules';
16
16
  import {
17
- addRowSourceFlag,
17
+ addRowFlags,
18
18
  getRowFilter,
19
19
  getCustomSort,
20
20
  useModuleFilterByChunk,
@@ -75,7 +75,7 @@ export const BundleModules = (props) => {
75
75
  });
76
76
 
77
77
  const { rows, totalRowCount } = useMemo(() => {
78
- const result = webpack.compareBySection.modules(filteredJobsByChunkIds, [addRowSourceFlag]);
78
+ const result = webpack.compareBySection.modules(filteredJobsByChunkIds, [addRowFlags]);
79
79
  return { rows: result, totalRowCount: result.length };
80
80
  }, [filteredJobsByChunkIds]);
81
81
 
@@ -9,7 +9,6 @@ export { Delta } from './delta';
9
9
  export { InsightIcon } from './insight-icon';
10
10
  export { Insights } from './insights';
11
11
  export { JobName } from './job-name';
12
- export { JobHeader } from './job-header';
13
12
  export { JobsHeader } from './jobs-header';
14
13
  export { LighthouseTable } from './lighthouse-table';
15
14
  export { Metric } from './metric';
@@ -18,29 +18,32 @@ export const JobHeader = (props) => {
18
18
 
19
19
  return (
20
20
  <Stack className={rootClassName} space="xxsmall">
21
- <h1 className={css.title}>
22
- <span>{`#${job.internalBuildNumber}`}</span>
23
- {tag && <Tag className={css.tag}>{tag}</Tag>}
24
- </h1>
25
- <FlexStack space="xxsmall" className={css.meta}>
21
+ <FlexStack space="xsmall" alignItems="center">
22
+ <FlexStack space="xxxsmall" as="h1" className={css.title}>
23
+ <span>{`#${job.internalBuildNumber}`}</span>
24
+ {tag && <Tag className={css.tag}>{tag}</Tag>}
25
+ </FlexStack>
26
+
26
27
  {builtAt && (
27
- <FlexStack space="xxxsmall" alignItems="center" inline as="span" title={builtAt}>
28
- <Icon glyph="clock" />
29
- <span>
28
+ <FlexStack space="xxxsmall" alignItems="center" inline as="span" title={builtAt} className={css.meta}>
29
+ <Icon glyph="clock" className={css.metaIcon} />
30
+ <span className={css.metaText}>
30
31
  {formatDateTime({ dateStyle: 'medium', timeStyle: 'medium' }, builtAt)}
31
32
  </span>
32
33
  </FlexStack>
33
34
  )}
34
35
 
35
36
  {hash && (
36
- <FlexStack space="xxxsmall" alignItems="center" inline title="Webpack bundle hash">
37
- <Icon glyph="commit" />
38
- <span>{hash}</span>
37
+ <FlexStack space="xxxsmall" alignItems="center" inline title="Webpack bundle hash" className={css.meta}>
38
+ <Icon glyph="commit" className={css.metaIcon} />
39
+ <span className={css.metaText}>{hash.slice(0, 7)}</span>
39
40
  </FlexStack>
40
41
  )}
41
-
42
- <div className={css.content}>{children}</div>
43
42
  </FlexStack>
43
+ {children && (<FlexStack space="xxsmall" className={css.meta}>
44
+ <div className={css.content}>{children}</div>
45
+ </FlexStack>
46
+ )}
44
47
  </Stack>
45
48
  );
46
49
  };
@@ -1,16 +1,63 @@
1
1
  .root {
2
- align-items: stretch;
3
- flex-wrap: wrap;
4
2
  padding: var(--space-small) 0;
3
+ width: 100%;
4
+ display: flex;
5
5
  }
6
6
 
7
7
  .item {
8
- flex: 0 0 auto;
9
- padding: 0 var(--space-medium);
8
+ position: relative;
9
+ flex: 1 0 10%;
10
+ max-width: 50%;
11
+ overflow: hidden;
12
+ padding: 0 var(--space-small);
10
13
  border-left: 1px solid var(--color-outline);
11
14
  }
12
15
 
16
+ .item::after {
17
+ content: '';
18
+ display: block;
19
+ position: absolute;
20
+ top: 0;
21
+ right: 0;
22
+ bottom: 0;
23
+ width: var(--space-xxsmall);
24
+ background: linear-gradient(90deg, transparent, var(--color-background));
25
+ }
26
+
27
+ .item + .item {
28
+ margin-left: var(--space-small);
29
+ }
30
+
13
31
  .item:first-child {
14
32
  border-left: 0;
15
33
  padding-left: 0;
16
34
  }
35
+
36
+ .itemTitle {
37
+ font-size: var(--size-xlarge);
38
+ line-height: 1;
39
+ font-weight: bold;
40
+ vertical-align: top;
41
+ }
42
+
43
+ .itemTag {
44
+ margin-left: var(--space-xxsmall);
45
+ vertical-align: top;
46
+ outline: 1px solid var(--color-outline);
47
+ background: var(--color-highlight);
48
+ color: var(--color-text-light);
49
+ }
50
+
51
+ .itemMeta {
52
+ font-size: var(--size-small);
53
+ color: var(--color-text-light);
54
+ }
55
+
56
+ .itemMetaIcon {
57
+ flex: 0 0 auto;
58
+ }
59
+
60
+ .itemMetaText {
61
+ flex: 0 0 auto;
62
+ white-space: nowrap;
63
+ }
@@ -0,0 +1,51 @@
1
+ import React from 'react';
2
+
3
+ import { getWrapperDecorator } from '../../stories';
4
+ import { JobsHeader } from '.';
5
+
6
+ export default {
7
+ title: 'Components/JobsHeader',
8
+ component: JobsHeader,
9
+ decorators: [getWrapperDecorator()],
10
+ };
11
+
12
+ export const Default = () => (
13
+ <JobsHeader
14
+ jobs={[
15
+ {
16
+ internalBuildNumber: 1,
17
+ meta: {
18
+ webpack: {
19
+ builtAt: '2019-01-01T00:00:00.000Z',
20
+ hash: 'abcd1234',
21
+ },
22
+ },
23
+ },
24
+ ]}
25
+ />
26
+ );
27
+
28
+ export const MultipleJobs = () => (
29
+ <JobsHeader
30
+ jobs={[
31
+ {
32
+ internalBuildNumber: 1,
33
+ meta: {
34
+ webpack: {
35
+ builtAt: '2019-01-01T00:00:00.000Z',
36
+ hash: 'abcd1234',
37
+ },
38
+ },
39
+ },
40
+ {
41
+ internalBuildNumber: 2,
42
+ meta: {
43
+ webpack: {
44
+ builtAt: '2019-01-02T00:00:00.000Z',
45
+ hash: 'efgh1234',
46
+ },
47
+ },
48
+ },
49
+ ]}
50
+ />
51
+ );
@@ -0,0 +1,90 @@
1
+ import React from 'react';
2
+ import cx from 'classnames';
3
+ import { formatDateTime } from '@bundle-stats/utils';
4
+
5
+ import I18N from '../../i18n';
6
+ import { FlexStack } from '../../layout/flex-stack';
7
+ import { Stack } from '../../layout/stack';
8
+ import { Icon } from '../../ui/icon';
9
+ import { Tag } from '../../ui/tag';
10
+ import css from './jobs-header.module.css';
11
+
12
+ interface ItemProps {
13
+ job: {
14
+ internalBuildNumber: number;
15
+ meta?: {
16
+ webpack?: {
17
+ builtAt?: string;
18
+ hash?: string;
19
+ };
20
+ };
21
+ };
22
+ tag: string;
23
+ }
24
+
25
+ export const Item = (props: ItemProps & React.ComponentProps<'div'>) => {
26
+ const { className, tag, job } = props;
27
+
28
+ const { builtAt, hash } = job.meta?.webpack || {};
29
+ const rootClassName = cx(css.item, className);
30
+
31
+ return (
32
+ <FlexStack space="xsmall" alignItems="center" className={rootClassName}>
33
+ <FlexStack space="xxxsmall" as="h1" className={css.itemTitle}>
34
+ <span>{`#${job.internalBuildNumber}`}</span>
35
+ {tag && <Tag className={css.itemTag}>{tag}</Tag>}
36
+ </FlexStack>
37
+
38
+ {builtAt && (
39
+ <FlexStack
40
+ space="xxxsmall"
41
+ alignItems="center"
42
+ inline
43
+ as="span"
44
+ title={builtAt}
45
+ className={css.itemMeta}
46
+ >
47
+ <Icon glyph="clock" className={css.itemMetaIcon} />
48
+ <span className={css.itemMetaText}>
49
+ {formatDateTime({ dateStyle: 'medium', timeStyle: 'medium' }, builtAt)}
50
+ </span>
51
+ </FlexStack>
52
+ )}
53
+
54
+ {hash && (
55
+ <FlexStack
56
+ space="xxxsmall"
57
+ alignItems="center"
58
+ inline
59
+ as="span"
60
+ title="Webpack bundle hash"
61
+ className={css.itemMeta}
62
+ >
63
+ <Icon glyph="commit" className={css.itemMetaIcon} />
64
+ <span className={css.itemMetaText}>{hash.slice(0, 7)}</span>
65
+ </FlexStack>
66
+ )}
67
+ </FlexStack>
68
+ );
69
+ };
70
+
71
+ interface JobsHeaderProps {
72
+ jobs?: Array<ItemProps['job']> | null;
73
+ }
74
+
75
+ export const JobsHeader = (props: JobsHeaderProps & React.ComponentProps<'header'>) => {
76
+ const { className, jobs } = props;
77
+ const rootClassName = cx(css.root, className);
78
+
79
+ return (
80
+ <header className={rootClassName}>
81
+ {jobs?.map((job, index) => (
82
+ <Item
83
+ key={job.internalBuildNumber || index}
84
+ job={job}
85
+ tag={index === 0 ? I18N.CURRENT : I18N.BASELINE}
86
+ />
87
+ ))}
88
+ </header>
89
+ );
90
+ };
@@ -1,5 +1,10 @@
1
- .chunksTitle {
2
- margin-right: var(--space-xxxsmall);
1
+ .label {
2
+ display: inline-block;
3
+ min-width: 6em;
4
+ }
5
+
6
+ .label::after {
7
+ content: ' ';
3
8
  }
4
9
 
5
10
  .chunksItems {
@@ -2,7 +2,15 @@ import React, { useMemo } from 'react';
2
2
  import cx from 'classnames';
3
3
  import isEmpty from 'lodash/isEmpty';
4
4
  import noop from 'lodash/noop';
5
- import { BUNDLE_MODULES_DUPLICATE, MetricRunInfo, getBundleModulesByChunk } from '@bundle-stats/utils';
5
+ import {
6
+ BUNDLE_MODULES_DUPLICATE,
7
+ FILE_TYPE_LABELS,
8
+ MODULE_SOURCE_TYPE_LABELS,
9
+ MetricRunInfo,
10
+ getBundleModulesByChunk,
11
+ getBundleModulesByFileTpe,
12
+ getBundleModulesBySource,
13
+ } from '@bundle-stats/utils';
6
14
  import { Module, MetaChunk } from '@bundle-stats/utils/types/webpack';
7
15
 
8
16
  import { Tag } from '../../ui/tag';
@@ -15,6 +23,8 @@ interface ModuleInfoProps {
15
23
  label: string;
16
24
  changed?: boolean;
17
25
  duplicated?: boolean;
26
+ thirdParty?: boolean;
27
+ fileType?: string;
18
28
  runs: Array<Module & MetricRunInfo>;
19
29
  };
20
30
  chunks?: Array<MetaChunk>;
@@ -44,16 +54,24 @@ export const ModuleInfo = (props: ModuleInfoProps & React.ComponentProps<'div'>)
44
54
 
45
55
  return (
46
56
  <div>
47
- <Tag as={CustomComponentLink} {...BUNDLE_MODULES_DUPLICATE} onClick={onClick} kind="danger">Duplicate</Tag>
57
+ <Tag as={CustomComponentLink} {...BUNDLE_MODULES_DUPLICATE} onClick={onClick} kind="danger">
58
+ Duplicate
59
+ </Tag>
48
60
  </div>
49
61
  );
50
62
  }, [item]);
51
63
 
64
+ const fileTypeLabel = FILE_TYPE_LABELS[item.fileType as keyof typeof FILE_TYPE_LABELS];
65
+
66
+ const sourceTypeLabel = item.thirdParty
67
+ ? MODULE_SOURCE_TYPE_LABELS.THIRD_PARTY
68
+ : MODULE_SOURCE_TYPE_LABELS.FIRST_PARTY;
69
+
52
70
  return (
53
71
  <EntryInfo item={item} labels={labels} tags={tags} className={rootClassName}>
54
72
  {!isEmpty(currentRun?.chunkIds) && (
55
73
  <div className={css.chunks}>
56
- <span className={css.chunksTitle}>Chunks:</span>
74
+ <span className={css.label}>Chunks</span>
57
75
  {currentRun.chunkIds.map((chunkId) => {
58
76
  const chunk = chunks?.find(({ id }) => id === chunkId);
59
77
 
@@ -74,6 +92,30 @@ export const ModuleInfo = (props: ModuleInfoProps & React.ComponentProps<'div'>)
74
92
  })}
75
93
  </div>
76
94
  )}
95
+
96
+ {item?.fileType && (
97
+ <p>
98
+ <span className={css.label}>File type</span>
99
+ <Tag
100
+ as={CustomComponentLink}
101
+ {...getBundleModulesByFileTpe(item.fileType, fileTypeLabel)}
102
+ onClick={onClick}
103
+ >
104
+ {fileTypeLabel}
105
+ </Tag>
106
+ </p>
107
+ )}
108
+
109
+ <p>
110
+ <span className={css.label}>Source</span>
111
+ <Tag
112
+ as={CustomComponentLink}
113
+ {...getBundleModulesBySource(item.thirdParty || false, sourceTypeLabel)}
114
+ onClick={onClick}
115
+ >
116
+ {sourceTypeLabel}
117
+ </Tag>
118
+ </p>
77
119
  </EntryInfo>
78
120
  );
79
121
  };
@@ -11,3 +11,13 @@
11
11
  .packageTitleId {
12
12
  color: var(--color-text-ultra-light);
13
13
  }
14
+
15
+ .label {
16
+ display: inline-block;
17
+ min-width: 6em;
18
+ }
19
+
20
+ .label::after {
21
+ content: ' ';
22
+ }
23
+