@bundle-stats/ui 4.13.0-beta.15 → 4.13.0-beta.17
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.
- package/lib/assets/icons.svg +70 -67
- package/lib/assets/icons.svg.js +14 -12
- package/lib/assets/icons.svg.js.map +1 -1
- package/lib/components/bundle-assets/bundle-assets.js +39 -7
- package/lib/components/bundle-assets/bundle-assets.js.map +1 -1
- package/lib/components/bundle-assets-totals/bundle-assets-totals.js +4 -4
- package/lib/components/bundle-assets-totals/bundle-assets-totals.js.map +1 -1
- package/lib/components/bundle-modules/bundle-modules.js +28 -7
- package/lib/components/bundle-modules/bundle-modules.js.map +1 -1
- package/lib/components/bundle-packages/bundle-packages.js +39 -7
- package/lib/components/bundle-packages/bundle-packages.js.map +1 -1
- package/lib/components/delta/delta.module.css +10 -1
- package/lib/components/metrics-display-selector/metrics-display-selector.js +41 -4
- package/lib/components/metrics-display-selector/metrics-display-selector.js.map +1 -1
- package/lib/components/metrics-display-selector/metrics-display-selector.module.css +15 -7
- package/lib/components/metrics-treemap/index.js +4 -0
- package/lib/components/metrics-treemap/index.js.map +1 -1
- package/lib/components/metrics-treemap/metrics-treemap.constants.js +3 -0
- package/lib/components/metrics-treemap/metrics-treemap.constants.js.map +1 -0
- package/lib/components/metrics-treemap/metrics-treemap.js +57 -38
- package/lib/components/metrics-treemap/metrics-treemap.js.map +1 -1
- package/lib/components/metrics-treemap/metrics-treemap.module.css +55 -25
- package/lib/components/metrics-treemap/metrics-treemap.utils.js +70 -0
- package/lib/components/metrics-treemap/metrics-treemap.utils.js.map +1 -0
- package/lib/components/run-info/run-info.module.css +7 -7
- package/lib/hooks/metrics-display-type.js +56 -24
- package/lib/hooks/metrics-display-type.js.map +1 -1
- package/lib/ui/icon/icon.js +1 -0
- package/lib/ui/icon/icon.js.map +1 -1
- package/lib-esm/assets/icons.svg +70 -67
- package/lib-esm/assets/icons.svg.js +14 -12
- package/lib-esm/assets/icons.svg.js.map +1 -1
- package/lib-esm/components/bundle-assets/bundle-assets.js +40 -8
- package/lib-esm/components/bundle-assets/bundle-assets.js.map +1 -1
- package/lib-esm/components/bundle-assets-totals/bundle-assets-totals.js +5 -5
- package/lib-esm/components/bundle-assets-totals/bundle-assets-totals.js.map +1 -1
- package/lib-esm/components/bundle-modules/bundle-modules.js +29 -8
- package/lib-esm/components/bundle-modules/bundle-modules.js.map +1 -1
- package/lib-esm/components/bundle-packages/bundle-packages.js +40 -8
- package/lib-esm/components/bundle-packages/bundle-packages.js.map +1 -1
- package/lib-esm/components/delta/delta.module.css +10 -1
- package/lib-esm/components/metrics-display-selector/metrics-display-selector.js +41 -4
- package/lib-esm/components/metrics-display-selector/metrics-display-selector.js.map +1 -1
- package/lib-esm/components/metrics-display-selector/metrics-display-selector.module.css +15 -7
- package/lib-esm/components/metrics-treemap/index.js +1 -0
- package/lib-esm/components/metrics-treemap/index.js.map +1 -1
- package/lib-esm/components/metrics-treemap/metrics-treemap.constants.js +2 -0
- package/lib-esm/components/metrics-treemap/metrics-treemap.constants.js.map +1 -0
- package/lib-esm/components/metrics-treemap/metrics-treemap.js +58 -39
- package/lib-esm/components/metrics-treemap/metrics-treemap.js.map +1 -1
- package/lib-esm/components/metrics-treemap/metrics-treemap.module.css +55 -25
- package/lib-esm/components/metrics-treemap/metrics-treemap.utils.js +65 -0
- package/lib-esm/components/metrics-treemap/metrics-treemap.utils.js.map +1 -0
- package/lib-esm/components/run-info/run-info.module.css +7 -7
- package/lib-esm/hooks/metrics-display-type.js +57 -24
- package/lib-esm/hooks/metrics-display-type.js.map +1 -1
- package/lib-esm/ui/icon/icon.js +1 -0
- package/lib-esm/ui/icon/icon.js.map +1 -1
- package/package.json +8 -8
- package/src/assets/icons/check.svg +4 -0
- package/src/assets/icons.svg +70 -67
- package/src/assets/icons.svg.jsx +12 -12
- package/src/components/bundle-assets/bundle-assets.jsx +76 -20
- package/src/components/bundle-assets-totals/bundle-assets-totals.jsx +5 -4
- package/src/components/bundle-modules/bundle-modules.tsx +76 -16
- package/src/components/bundle-packages/bundle-packages.jsx +71 -16
- package/src/components/delta/delta.module.css +10 -1
- package/src/components/metrics-display-selector/metrics-display-selector.module.css +15 -7
- package/src/components/metrics-display-selector/metrics-display-selector.stories.tsx +33 -0
- package/src/components/metrics-display-selector/metrics-display-selector.tsx +83 -25
- package/src/components/metrics-treemap/__tests__/metrics-treemap.utils.ts +143 -0
- package/src/components/metrics-treemap/index.ts +1 -0
- package/src/components/metrics-treemap/metrics-treemap.constants.ts +43 -0
- package/src/components/metrics-treemap/metrics-treemap.module.css +55 -25
- package/src/components/metrics-treemap/metrics-treemap.stories.tsx +44 -10
- package/src/components/metrics-treemap/metrics-treemap.tsx +247 -84
- package/src/components/metrics-treemap/metrics-treemap.utils.ts +107 -0
- package/src/components/run-info/run-info.module.css +7 -7
- package/src/hooks/metrics-display-type.ts +87 -29
- package/src/ui/icon/icon.tsx +1 -0
- package/types/components/metrics-display-selector/metrics-display-selector.d.ts +3 -1
- package/types/components/metrics-treemap/index.d.ts +1 -0
- package/types/components/metrics-treemap/metrics-treemap.constants.d.ts +15 -0
- package/types/components/metrics-treemap/metrics-treemap.d.ts +7 -5
- package/types/components/metrics-treemap/metrics-treemap.utils.d.ts +4 -0
- package/types/hooks/metrics-display-type.d.ts +8 -2
- package/types/ui/icon/icon.d.ts +2 -0
|
@@ -24,9 +24,13 @@ import { SEARCH_PLACEHOLDER } from './bundle-packages.i18n';
|
|
|
24
24
|
import css from './bundle-packages.module.css';
|
|
25
25
|
import { MetricsDisplaySelector } from '../metrics-display-selector';
|
|
26
26
|
import { MetricsDisplayType } from '../../constants';
|
|
27
|
-
import { MetricsTreemap } from '../metrics-treemap';
|
|
27
|
+
import { MetricsTreemap, getTreemapNodes, getTreemapNodesGroupedByPath } from '../metrics-treemap';
|
|
28
28
|
import { useMetricsDisplayType } from '../../hooks/metrics-display-type';
|
|
29
29
|
|
|
30
|
+
const DISPLAY_TYPE_GROUPS = {
|
|
31
|
+
[MetricsDisplayType.TREEMAP]: ['folder'],
|
|
32
|
+
};
|
|
33
|
+
|
|
30
34
|
const getDropdownFilters = ({ compareMode, filters }) => ({
|
|
31
35
|
[PACKAGE_FILTERS.CHANGED]: {
|
|
32
36
|
label: 'Changed',
|
|
@@ -115,6 +119,60 @@ RowHeader.propTypes = {
|
|
|
115
119
|
search: PropTypes.string,
|
|
116
120
|
};
|
|
117
121
|
|
|
122
|
+
const ViewMetricsTreemap = (props) => {
|
|
123
|
+
const { metricsTableTitle, jobs, items, displayType, emptyMessage, showEntryInfo, updateSearch } = props;
|
|
124
|
+
|
|
125
|
+
const treeNodes = useMemo(() => {
|
|
126
|
+
if (displayType.groupBy === 'folder') {
|
|
127
|
+
return getTreemapNodesGroupedByPath(items);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return getTreemapNodes(items);
|
|
131
|
+
}, [items, displayType.groupBy]);
|
|
132
|
+
|
|
133
|
+
const onGroupClick = useCallback(
|
|
134
|
+
(groupPath) => {
|
|
135
|
+
// Search by group path
|
|
136
|
+
// 1. use `^` to match only the string beggining
|
|
137
|
+
// 2. add `/` suffix to match only exact directories
|
|
138
|
+
// 3. if the group path is empty(root), clear search
|
|
139
|
+
if (groupPath) {
|
|
140
|
+
updateSearch(`^${groupPath}/`);
|
|
141
|
+
} else {
|
|
142
|
+
updateSearch('');
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
[updateSearch],
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<>
|
|
150
|
+
<Table compact>
|
|
151
|
+
<MetricsTableHeader metricTitle={metricsTableTitle} showSum jobs={jobs} rows={items} />
|
|
152
|
+
</Table>
|
|
153
|
+
<MetricsTreemap
|
|
154
|
+
treeNodes={treeNodes}
|
|
155
|
+
nested={Boolean(displayType.groupBy)}
|
|
156
|
+
emptyMessage={emptyMessage}
|
|
157
|
+
onItemClick={showEntryInfo}
|
|
158
|
+
onGroupClick={onGroupClick}
|
|
159
|
+
/>
|
|
160
|
+
</>
|
|
161
|
+
);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
ViewMetricsTreemap.propTypes = {
|
|
165
|
+
jobs: PropTypes.arrayOf(PropTypes.object).isRequired, // eslint-disable-line react/forbid-prop-types
|
|
166
|
+
items: PropTypes.arrayOf(PropTypes.object).isRequired, // eslint-disable-line react/forbid-prop-types
|
|
167
|
+
metricsTableTitle: PropTypes.node.isRequired,
|
|
168
|
+
displayType: PropTypes.shape({
|
|
169
|
+
groupBy: PropTypes.string,
|
|
170
|
+
}).isRequired,
|
|
171
|
+
emptyMessage: PropTypes.node.isRequired,
|
|
172
|
+
showEntryInfo: PropTypes.func.isRequired,
|
|
173
|
+
updateSearch: PropTypes.func.isRequired,
|
|
174
|
+
};
|
|
175
|
+
|
|
118
176
|
export const BundlePackages = (props) => {
|
|
119
177
|
const {
|
|
120
178
|
className,
|
|
@@ -139,7 +197,7 @@ export const BundlePackages = (props) => {
|
|
|
139
197
|
|
|
140
198
|
const jobLabels = jobs?.map((job) => job?.label);
|
|
141
199
|
|
|
142
|
-
const [displayType, setDisplayType] = useMetricsDisplayType();
|
|
200
|
+
const [displayType, setDisplayType] = useMetricsDisplayType(DISPLAY_TYPE_GROUPS);
|
|
143
201
|
|
|
144
202
|
const dropdownFilters = useMemo(
|
|
145
203
|
() => getDropdownFilters({ compareMode: jobs?.length > 1, filters }),
|
|
@@ -198,7 +256,7 @@ export const BundlePackages = (props) => {
|
|
|
198
256
|
className={css.toolbar}
|
|
199
257
|
renderActions={({ actionClassName }) => (
|
|
200
258
|
<FlexStack space="xxsmall" className={cx(css.dropdown, actionClassName)}>
|
|
201
|
-
<MetricsDisplaySelector onSelect={setDisplayType} value={displayType} />
|
|
259
|
+
<MetricsDisplaySelector onSelect={setDisplayType} value={displayType.value} groupBy={displayType.groupBy} groups={DISPLAY_TYPE_GROUPS} />
|
|
202
260
|
<MetricsTableOptions
|
|
203
261
|
handleViewAll={resetAllFilters}
|
|
204
262
|
handleResetFilters={resetFilters}
|
|
@@ -222,7 +280,7 @@ export const BundlePackages = (props) => {
|
|
|
222
280
|
</FlexStack>
|
|
223
281
|
</Toolbar>
|
|
224
282
|
<Box outline as="main">
|
|
225
|
-
{displayType === MetricsDisplayType.TABLE && (
|
|
283
|
+
{displayType.value === MetricsDisplayType.TABLE && (
|
|
226
284
|
<MetricsTable
|
|
227
285
|
runs={jobs}
|
|
228
286
|
items={items}
|
|
@@ -234,18 +292,15 @@ export const BundlePackages = (props) => {
|
|
|
234
292
|
updateSort={updateSort}
|
|
235
293
|
/>
|
|
236
294
|
)}
|
|
237
|
-
{displayType === MetricsDisplayType.TREEMAP && (
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
</Table>
|
|
247
|
-
<MetricsTreemap emptyMessage={emptyMessage} items={items} onItemClick={showEntryInfo} />
|
|
248
|
-
</>
|
|
295
|
+
{displayType.value === MetricsDisplayType.TREEMAP && (
|
|
296
|
+
<ViewMetricsTreemap
|
|
297
|
+
jobs={jobs}
|
|
298
|
+
items={items}
|
|
299
|
+
displayType={displayType}
|
|
300
|
+
emptyMessage={emptyMessage}
|
|
301
|
+
showEntryInfo={showEntryInfo}
|
|
302
|
+
updateSearch={updateSearch}
|
|
303
|
+
/>
|
|
249
304
|
)}
|
|
250
305
|
</Box>
|
|
251
306
|
</Stack>
|
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
white-space: nowrap;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
.CHANGE {
|
|
8
|
+
color: var(--color-info);
|
|
9
|
+
}
|
|
10
|
+
|
|
7
11
|
.HIGH_NEGATIVE {
|
|
8
12
|
color: var(--color-red-dark);
|
|
9
13
|
}
|
|
@@ -29,11 +33,16 @@
|
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
.inverted {
|
|
32
|
-
|
|
36
|
+
padding: 0 2px;
|
|
37
|
+
border-radius: var(--radius-xsmall);
|
|
33
38
|
background-color: var(--color-gray-ultra-light);
|
|
34
39
|
color: var(--color-light);
|
|
35
40
|
}
|
|
36
41
|
|
|
42
|
+
.inverted.CHANGE {
|
|
43
|
+
background: var(--color-info-light);
|
|
44
|
+
}
|
|
45
|
+
|
|
37
46
|
.inverted.HIGH_NEGATIVE {
|
|
38
47
|
background: var(--color-red-dark);
|
|
39
48
|
}
|
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
.
|
|
2
|
-
|
|
1
|
+
.buttonActive {
|
|
2
|
+
background: var(--color-highlight);
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
/* explicit ControlGroup last item styles to override ariakit injected sibling nodes */
|
|
6
|
+
.itemLast {
|
|
7
|
+
border-top-right-radius: var(--radius-small) !important;
|
|
8
|
+
border-bottom-right-radius: var(--radius-small) !important;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.dropdownItemIcon {
|
|
12
|
+
opacity: 0;
|
|
13
|
+
transition: var(--transition-out);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.dropdownItemActive .dropdownItemIcon {
|
|
17
|
+
opacity: 1;
|
|
10
18
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { action } from '@storybook/addon-actions'; // eslint-disable-line
|
|
2
|
+
import { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
|
|
4
|
+
import { MetricsDisplayType } from '../../constants';
|
|
5
|
+
import { MetricsDisplaySelector } from '.';
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof MetricsDisplaySelector> = {
|
|
8
|
+
title: 'Components/MetricsDisplaySelector',
|
|
9
|
+
component: MetricsDisplaySelector,
|
|
10
|
+
args: {
|
|
11
|
+
onSelect: action('SELECT'),
|
|
12
|
+
},
|
|
13
|
+
} as Meta;
|
|
14
|
+
|
|
15
|
+
export default meta;
|
|
16
|
+
|
|
17
|
+
type Story = StoryObj<typeof meta>;
|
|
18
|
+
|
|
19
|
+
export const Default: Story = {
|
|
20
|
+
args: {
|
|
21
|
+
value: MetricsDisplayType.TABLE,
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const TreemapGroups: Story = {
|
|
26
|
+
args: {
|
|
27
|
+
value: MetricsDisplayType.TREEMAP,
|
|
28
|
+
groupBy: '',
|
|
29
|
+
groups: {
|
|
30
|
+
[MetricsDisplayType.TREEMAP]: ['folder'],
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
};
|
|
@@ -4,44 +4,102 @@ import cx from 'classnames';
|
|
|
4
4
|
import { MetricsDisplayType } from '../../constants';
|
|
5
5
|
import { Button } from '../../ui/button';
|
|
6
6
|
import { ControlGroup } from '../../ui/control-group';
|
|
7
|
+
import { Dropdown, DropdownItem } from '../../ui/dropdown';
|
|
7
8
|
import { Icon } from '../../ui/icon';
|
|
8
9
|
import css from './metrics-display-selector.module.css';
|
|
10
|
+
import { FlexStack } from '../../layout';
|
|
11
|
+
|
|
12
|
+
const DEFAULT_GROUP_BY = '';
|
|
13
|
+
|
|
14
|
+
const METRICS_DISPLAY_MAP = {
|
|
15
|
+
[MetricsDisplayType.TABLE]: {
|
|
16
|
+
glyph: Icon.ICONS.TABLE,
|
|
17
|
+
label: 'Table',
|
|
18
|
+
},
|
|
19
|
+
[MetricsDisplayType.TREEMAP]: {
|
|
20
|
+
glyph: Icon.ICONS.TREEMAP,
|
|
21
|
+
label: 'Treemap',
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const displayTypes = Object.values(MetricsDisplayType);
|
|
9
26
|
|
|
10
27
|
interface MetricsDisplaySelectorProps {
|
|
11
28
|
value: MetricsDisplayType;
|
|
12
|
-
|
|
29
|
+
groupBy?: string;
|
|
30
|
+
groups?: Partial<Record<MetricsDisplayType, Array<string>>>;
|
|
31
|
+
onSelect: (newValue: MetricsDisplayType, groupBy?: string) => void;
|
|
13
32
|
}
|
|
14
33
|
|
|
15
34
|
export const MetricsDisplaySelector = (
|
|
16
35
|
props: MetricsDisplaySelectorProps &
|
|
17
36
|
Omit<React.ComponentProps<typeof ControlGroup>, 'onSelect' | 'value'>,
|
|
18
37
|
) => {
|
|
19
|
-
const {
|
|
38
|
+
const { value, groupBy = DEFAULT_GROUP_BY, groups = {}, onSelect, ...restProps } = props;
|
|
20
39
|
|
|
21
40
|
return (
|
|
22
|
-
<ControlGroup
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
<ControlGroup {...restProps}>
|
|
42
|
+
{displayTypes.map((displayType, index) => {
|
|
43
|
+
const displayGroups = groups?.[displayType];
|
|
44
|
+
const displayProps = METRICS_DISPLAY_MAP[displayType];
|
|
45
|
+
const isLast = displayTypes.length - 1 === index;
|
|
46
|
+
const isActive = displayType === value;
|
|
47
|
+
|
|
48
|
+
if (!displayGroups || displayGroups.length === 0) {
|
|
49
|
+
return (
|
|
50
|
+
<Button
|
|
51
|
+
outline
|
|
52
|
+
active={isActive}
|
|
53
|
+
size="small"
|
|
54
|
+
type="button"
|
|
55
|
+
glyph={displayProps.glyph}
|
|
56
|
+
onClick={() => onSelect(displayType)}
|
|
57
|
+
className={isLast && css.itemLast}
|
|
58
|
+
key={displayType}
|
|
59
|
+
>
|
|
60
|
+
{displayProps.label}
|
|
61
|
+
</Button>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const displayGroupsData = [
|
|
66
|
+
{
|
|
67
|
+
value: DEFAULT_GROUP_BY,
|
|
68
|
+
label: 'No groups',
|
|
69
|
+
},
|
|
70
|
+
...displayGroups.map((displayGroup) => ({
|
|
71
|
+
value: displayGroup,
|
|
72
|
+
label: `Group by ${displayGroup}`,
|
|
73
|
+
})),
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<Dropdown
|
|
78
|
+
glyph={displayProps.glyph}
|
|
79
|
+
label={displayProps.label}
|
|
80
|
+
className={cx(isLast && css.itemLast, isActive && css.buttonActive)}
|
|
81
|
+
key={displayType}
|
|
82
|
+
>
|
|
83
|
+
{displayGroupsData.map((displayGroupData) => {
|
|
84
|
+
const isGroupActive = isActive && displayGroupData.value === groupBy;
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<DropdownItem
|
|
88
|
+
onClick={() => onSelect(displayType, displayGroupData.value)}
|
|
89
|
+
isActive={isGroupActive}
|
|
90
|
+
className={cx(css.dropdownItem, isGroupActive && css.dropdownItemActive)}
|
|
91
|
+
key={displayGroupData.value}
|
|
92
|
+
>
|
|
93
|
+
<FlexStack space="xxxsmall" alignItems="center">
|
|
94
|
+
<Icon glyph={Icon.ICONS.CHECK} size="small" className={css.dropdownItemIcon} />
|
|
95
|
+
<span>{displayGroupData.label}</span>
|
|
96
|
+
</FlexStack>
|
|
97
|
+
</DropdownItem>
|
|
98
|
+
);
|
|
99
|
+
})}
|
|
100
|
+
</Dropdown>
|
|
101
|
+
);
|
|
102
|
+
})}
|
|
45
103
|
</ControlGroup>
|
|
46
104
|
);
|
|
47
105
|
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { getTreemapNodesGroupedByPath } from '../metrics-treemap.utils';
|
|
2
|
+
|
|
3
|
+
describe('MetricsTreemap / getTreeNodesGroupedByPath', () => {
|
|
4
|
+
test('should group tree ndoes by path', () => {
|
|
5
|
+
const actual = getTreemapNodesGroupedByPath([
|
|
6
|
+
{
|
|
7
|
+
key: './src/app.jsx',
|
|
8
|
+
runs: [{ value: 100 }],
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
key: './src/components/header.jsx',
|
|
12
|
+
runs: [{ value: 100 }],
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
key: './src/components/footer.jsx',
|
|
16
|
+
runs: [{ value: 100 }],
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
key: './src/ui/button.jsx',
|
|
20
|
+
runs: [{ value: 100 }],
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
key: './node_modules/react/dist/react.production.js',
|
|
24
|
+
runs: [{ value: 100 }],
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
key: 'favicon.ico',
|
|
28
|
+
runs: [{ value: 10 }],
|
|
29
|
+
},
|
|
30
|
+
] as any);
|
|
31
|
+
|
|
32
|
+
expect(actual).toStrictEqual({
|
|
33
|
+
id: '',
|
|
34
|
+
label: '(root)',
|
|
35
|
+
value: 0,
|
|
36
|
+
children: [
|
|
37
|
+
{
|
|
38
|
+
id: '.',
|
|
39
|
+
label: '.',
|
|
40
|
+
value: 0,
|
|
41
|
+
children: [
|
|
42
|
+
{
|
|
43
|
+
id: './src',
|
|
44
|
+
label: 'src',
|
|
45
|
+
value: 0,
|
|
46
|
+
children: [
|
|
47
|
+
{
|
|
48
|
+
id: './src/app.jsx',
|
|
49
|
+
label: 'app.jsx',
|
|
50
|
+
value: 100,
|
|
51
|
+
item: {
|
|
52
|
+
key: './src/app.jsx',
|
|
53
|
+
runs: [{ value: 100 }],
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: './src/components',
|
|
58
|
+
label: 'components',
|
|
59
|
+
value: 0,
|
|
60
|
+
children: [
|
|
61
|
+
{
|
|
62
|
+
id: './src/components/header.jsx',
|
|
63
|
+
label: 'header.jsx',
|
|
64
|
+
value: 100,
|
|
65
|
+
item: {
|
|
66
|
+
key: './src/components/header.jsx',
|
|
67
|
+
runs: [{ value: 100 }],
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: './src/components/footer.jsx',
|
|
72
|
+
label: 'footer.jsx',
|
|
73
|
+
value: 100,
|
|
74
|
+
item: {
|
|
75
|
+
key: './src/components/footer.jsx',
|
|
76
|
+
runs: [{ value: 100 }],
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: './src/ui',
|
|
83
|
+
label: 'ui',
|
|
84
|
+
value: 0,
|
|
85
|
+
children: [
|
|
86
|
+
{
|
|
87
|
+
id: './src/ui/button.jsx',
|
|
88
|
+
label: 'button.jsx',
|
|
89
|
+
value: 100,
|
|
90
|
+
item: {
|
|
91
|
+
key: './src/ui/button.jsx',
|
|
92
|
+
runs: [{ value: 100 }],
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
id: './node_modules',
|
|
101
|
+
label: 'node_modules',
|
|
102
|
+
value: 0,
|
|
103
|
+
children: [
|
|
104
|
+
{
|
|
105
|
+
id: './node_modules/react',
|
|
106
|
+
label: 'react',
|
|
107
|
+
value: 0,
|
|
108
|
+
children: [
|
|
109
|
+
{
|
|
110
|
+
id: './node_modules/react/dist',
|
|
111
|
+
label: 'dist',
|
|
112
|
+
value: 0,
|
|
113
|
+
children: [
|
|
114
|
+
{
|
|
115
|
+
id: './node_modules/react/dist/react.production.js',
|
|
116
|
+
label: 'react.production.js',
|
|
117
|
+
value: 100,
|
|
118
|
+
item: {
|
|
119
|
+
key: './node_modules/react/dist/react.production.js',
|
|
120
|
+
runs: [{ value: 100 }],
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
id: 'favicon.ico',
|
|
133
|
+
label: 'favicon.ico',
|
|
134
|
+
value: 10,
|
|
135
|
+
item: {
|
|
136
|
+
key: 'favicon.ico',
|
|
137
|
+
runs: [{ value: 10 }],
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ReportMetricRow } from '@bundle-stats/utils';
|
|
2
|
+
|
|
3
|
+
export interface TreeLeaf {
|
|
4
|
+
/**
|
|
5
|
+
* Node id - file/directory path
|
|
6
|
+
*/
|
|
7
|
+
id: string;
|
|
8
|
+
/**
|
|
9
|
+
* Node label - file/directory baseline
|
|
10
|
+
*/
|
|
11
|
+
label: string;
|
|
12
|
+
/**
|
|
13
|
+
* Node value - the max amount between item run values
|
|
14
|
+
*/
|
|
15
|
+
value: number;
|
|
16
|
+
/**
|
|
17
|
+
* Corresponding report metric item
|
|
18
|
+
*/
|
|
19
|
+
item: ReportMetricRow;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface Tree {
|
|
23
|
+
/**
|
|
24
|
+
* Node id - file/directory path
|
|
25
|
+
*/
|
|
26
|
+
id: string;
|
|
27
|
+
/**
|
|
28
|
+
* Node label - file/directory baseline
|
|
29
|
+
*/
|
|
30
|
+
label: string;
|
|
31
|
+
/**
|
|
32
|
+
* Node value - 0 for groups
|
|
33
|
+
*/
|
|
34
|
+
value: number;
|
|
35
|
+
/**
|
|
36
|
+
* List of children nodes
|
|
37
|
+
*/
|
|
38
|
+
children: TreeNodeChildren;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type TreeNode = Tree | TreeLeaf;
|
|
42
|
+
|
|
43
|
+
export type TreeNodeChildren = Array<TreeNode>;
|
|
@@ -13,15 +13,44 @@
|
|
|
13
13
|
top: 0;
|
|
14
14
|
width: 100%;
|
|
15
15
|
height: 100%;
|
|
16
|
-
fill: var(--color-text);
|
|
17
|
-
font-family: var(--font-family);
|
|
18
16
|
background: var(--color-background);
|
|
19
17
|
}
|
|
20
18
|
|
|
21
|
-
/**
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Tile group
|
|
21
|
+
*/
|
|
22
|
+
.tileGroup {
|
|
23
|
+
position: absolute;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.tileGroupTitle {
|
|
27
|
+
width: 100%;
|
|
28
|
+
padding: 2px var(--space-xxxsmall);
|
|
29
|
+
color: var(--color-text);
|
|
30
|
+
font-size: 10px;
|
|
31
|
+
line-height: 1em;
|
|
32
|
+
overflow: hidden;
|
|
33
|
+
white-space: nowrap;
|
|
34
|
+
text-overflow: ellipsis;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Tile group size variation */
|
|
38
|
+
.tileGroupSizeDefault {
|
|
39
|
+
transition: var(--ui-transition-in);
|
|
40
|
+
transition-property: left top width height;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Tile group nested variation */
|
|
44
|
+
.nested .tileGroup {
|
|
45
|
+
background: rgba(128, 128, 128, 0.05);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.nested .tileGroup:hover {
|
|
49
|
+
background: rgba(128, 128, 128, 0.08);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.nested .tileGroup:hover .tileGroupTitle {
|
|
53
|
+
color: var(--color-text-dark);
|
|
25
54
|
}
|
|
26
55
|
|
|
27
56
|
/** Leaf wrapper button */
|
|
@@ -31,7 +60,7 @@
|
|
|
31
60
|
border: 0;
|
|
32
61
|
border-radius: 0;
|
|
33
62
|
padding: 0;
|
|
34
|
-
background:
|
|
63
|
+
background: var(--color-background);
|
|
35
64
|
display: flex;
|
|
36
65
|
align-items: center;
|
|
37
66
|
font-family: var(--font-family-fixed);
|
|
@@ -57,12 +86,9 @@
|
|
|
57
86
|
opacity: 0.75;
|
|
58
87
|
}
|
|
59
88
|
|
|
60
|
-
.tile:hover {
|
|
61
|
-
transition: var(--ui-transition-in);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
89
|
.tile:hover::before {
|
|
65
90
|
transition: var(--ui-transition-in);
|
|
91
|
+
transition-property: opacity;
|
|
66
92
|
opacity: 0.5;
|
|
67
93
|
}
|
|
68
94
|
|
|
@@ -84,7 +110,7 @@
|
|
|
84
110
|
padding-left: var(--space-small);
|
|
85
111
|
}
|
|
86
112
|
|
|
87
|
-
.
|
|
113
|
+
.tileContentLabel {
|
|
88
114
|
flex: 0 1 auto;
|
|
89
115
|
width: 100%;
|
|
90
116
|
min-width: 0;
|
|
@@ -92,32 +118,37 @@
|
|
|
92
118
|
overflow: hidden;
|
|
93
119
|
}
|
|
94
120
|
|
|
95
|
-
.
|
|
121
|
+
.tileContentValue {
|
|
96
122
|
flex: 0 0 auto;
|
|
97
123
|
max-width: calc(100% + var(--space-xxsmall));
|
|
98
124
|
white-space: nowrap;
|
|
99
125
|
overflow: hidden;
|
|
100
126
|
}
|
|
101
127
|
|
|
102
|
-
.
|
|
128
|
+
.tileContentMetric {
|
|
103
129
|
font-weight: bold;
|
|
104
130
|
}
|
|
105
131
|
|
|
106
|
-
.
|
|
132
|
+
.tileContentDelta {
|
|
107
133
|
margin-left: var(--space-xxxsmall);
|
|
108
134
|
}
|
|
109
135
|
|
|
110
136
|
/** Tile size variation */
|
|
111
|
-
.
|
|
137
|
+
.tileSizeSmall .tileContent {
|
|
112
138
|
padding: var(--space-xxxsmall) var(--space-xxsmall);
|
|
113
139
|
gap: 0;
|
|
114
140
|
font-size: var(--size-xsmall);
|
|
115
141
|
}
|
|
116
142
|
|
|
117
|
-
.
|
|
143
|
+
.tileSizeSmall .tileContentLabel {
|
|
118
144
|
margin-top: 0;
|
|
119
145
|
}
|
|
120
146
|
|
|
147
|
+
.tileSizeDefault {
|
|
148
|
+
transition: var(--ui-transition-in);
|
|
149
|
+
transition-property: left top width height;
|
|
150
|
+
}
|
|
151
|
+
|
|
121
152
|
/** Tile diff variation */
|
|
122
153
|
.tile-NO_CHANGE {
|
|
123
154
|
color: var(--color-text-light);
|
|
@@ -125,7 +156,7 @@
|
|
|
125
156
|
|
|
126
157
|
.tile-NO_CHANGE::before {
|
|
127
158
|
color: var(--color-gray-50);
|
|
128
|
-
opacity: 0.
|
|
159
|
+
opacity: 0.3;
|
|
129
160
|
}
|
|
130
161
|
|
|
131
162
|
.tile-NO_CHANGE:hover::before {
|
|
@@ -193,17 +224,16 @@
|
|
|
193
224
|
pointer-events: none;
|
|
194
225
|
}
|
|
195
226
|
|
|
196
|
-
/* prevent tooltip portal wrapper from capturing events */
|
|
197
|
-
div[role="presentation"]:has(.tooltip) {
|
|
198
|
-
pointer-events: none;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
|
|
202
227
|
.tileTooltipArrow svg {
|
|
203
228
|
display: block;
|
|
204
229
|
fill: var(--color-background) !important;
|
|
205
230
|
}
|
|
206
231
|
|
|
207
232
|
.tileTooltipContentTitle {
|
|
208
|
-
font-size: var(--size-
|
|
233
|
+
font-size: var(--size-small);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/* prevent tooltip portal wrapper from capturing events */
|
|
237
|
+
div[role="presentation"]:has(.tooltip) {
|
|
238
|
+
pointer-events: none;
|
|
209
239
|
}
|