@bundle-stats/ui 3.4.0-alpha.3 → 3.4.0-beta.0
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/app/app.js +3 -1
- package/lib/app/header/header.js +2 -1
- package/lib/components/budget-info/budget-info.js +11 -17
- package/lib/components/budget-info/budget-info.stories.js +27 -12
- package/lib/components/budget-insights/budget-insights.js +37 -75
- package/lib/components/budget-insights/budget-insights.module.css +10 -2
- package/lib/components/budget-insights/budget-insights.stories.js +124 -63
- package/lib/components/bundle-assets/bundle-assets.js +2 -1
- package/lib/components/bundle-assets-totals-table/bundle-assets-totals-table.js +3 -4
- package/lib/components/bundle-modules/bundle-modules.js +1 -1
- package/lib/components/summary/summary.js +6 -12
- package/lib/components/summary/summary.module.css +1 -45
- package/lib/components/summary/summary.stories.js +41 -13
- package/lib/components/summary-item/summary-item.js +14 -22
- package/lib/components/summary-item/summary-item.module.css +25 -7
- package/lib/components/summary-item/summary-item.stories.js +20 -6
- package/lib/ui/hover-card/hover-card.js +7 -1
- package/lib-esm/app/app.js +3 -1
- package/lib-esm/app/header/header.js +2 -1
- package/lib-esm/components/budget-info/budget-info.js +11 -12
- package/lib-esm/components/budget-info/budget-info.stories.js +27 -12
- package/lib-esm/components/budget-insights/budget-insights.js +35 -71
- package/lib-esm/components/budget-insights/budget-insights.module.css +10 -2
- package/lib-esm/components/budget-insights/budget-insights.stories.js +115 -55
- package/lib-esm/components/bundle-assets/bundle-assets.js +2 -1
- package/lib-esm/components/bundle-assets-totals-table/bundle-assets-totals-table.js +3 -4
- package/lib-esm/components/bundle-modules/bundle-modules.js +1 -1
- package/lib-esm/components/summary/summary.js +6 -11
- package/lib-esm/components/summary/summary.module.css +1 -45
- package/lib-esm/components/summary/summary.stories.js +41 -13
- package/lib-esm/components/summary-item/summary-item.js +11 -20
- package/lib-esm/components/summary-item/summary-item.module.css +25 -7
- package/lib-esm/components/summary-item/summary-item.stories.js +20 -6
- package/lib-esm/ui/hover-card/hover-card.js +7 -1
- package/package.json +25 -25
- package/src/app/app.jsx +2 -2
- package/src/app/header/header.jsx +1 -1
- package/src/components/budget-info/budget-info.jsx +12 -22
- package/src/components/budget-insights/budget-insights.jsx +49 -82
- package/src/components/budget-insights/budget-insights.module.css +10 -2
- package/src/components/bundle-assets/bundle-assets.jsx +1 -1
- package/src/components/bundle-assets-totals-table/bundle-assets-totals-table.jsx +3 -3
- package/src/components/bundle-modules/bundle-modules.jsx +1 -1
- package/src/components/summary/summary.jsx +13 -17
- package/src/components/summary/summary.module.css +1 -45
- package/src/components/summary-item/summary-item.jsx +11 -17
- package/src/components/summary-item/summary-item.module.css +25 -7
- package/src/ui/hover-card/hover-card.jsx +5 -0
package/src/app/app.jsx
CHANGED
|
@@ -48,7 +48,7 @@ const Layout = ({ jobs, version, ...props }) => (
|
|
|
48
48
|
);
|
|
49
49
|
|
|
50
50
|
Layout.propTypes = {
|
|
51
|
-
jobs: PropTypes.arrayOf(PropTypes.object),
|
|
51
|
+
jobs: PropTypes.arrayOf(PropTypes.object), // eslint-disable-line react/forbid-prop-types
|
|
52
52
|
version: PropTypes.string,
|
|
53
53
|
};
|
|
54
54
|
|
|
@@ -64,7 +64,7 @@ const JobsProvider = ({ jobs, ...restProps }) => {
|
|
|
64
64
|
};
|
|
65
65
|
|
|
66
66
|
JobsProvider.propTypes = {
|
|
67
|
-
jobs: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
67
|
+
jobs: PropTypes.arrayOf(PropTypes.object).isRequired, // eslint-disable-line react/forbid-prop-types
|
|
68
68
|
};
|
|
69
69
|
|
|
70
70
|
const OverviewContent = () => {
|
|
@@ -16,7 +16,7 @@ export const Header = ({ className, jobs }) => (
|
|
|
16
16
|
|
|
17
17
|
Header.propTypes = {
|
|
18
18
|
className: PropTypes.string,
|
|
19
|
-
jobs: PropTypes.arrayOf(PropTypes.object),
|
|
19
|
+
jobs: PropTypes.arrayOf(PropTypes.object), // eslint-disable-line react/forbid-prop-types
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
Header.defaultProps = {
|
|
@@ -2,7 +2,6 @@ import React from 'react';
|
|
|
2
2
|
import cx from 'classnames';
|
|
3
3
|
import PropTypes from 'prop-types';
|
|
4
4
|
import { InsightType } from '@bundle-stats/utils';
|
|
5
|
-
import * as budgetsInsightsTransformer from '@bundle-stats/utils/lib-esm/transformers/budgets-insights';
|
|
6
5
|
|
|
7
6
|
import { Icon } from '../../ui/icon';
|
|
8
7
|
import { Tooltip } from '../../ui/tooltip';
|
|
@@ -24,38 +23,29 @@ const INSIGHT_TYPE_MAP = {
|
|
|
24
23
|
},
|
|
25
24
|
};
|
|
26
25
|
|
|
27
|
-
export const BudgetInfo = ({ className = '',
|
|
28
|
-
const
|
|
29
|
-
const { className: insightTypeClassName, glyph } = INSIGHT_TYPE_MAP[budgetInsightInfo.type];
|
|
26
|
+
export const BudgetInfo = ({ className = '', budgetInsight }) => {
|
|
27
|
+
const { className: insightTypeClassName, glyph } = INSIGHT_TYPE_MAP[budgetInsight.type];
|
|
30
28
|
|
|
31
29
|
const rootClassName = cx(css.root, insightTypeClassName, className);
|
|
32
|
-
const { data: messageData } = budgetInsightInfo.message;
|
|
33
30
|
|
|
34
31
|
return (
|
|
35
|
-
<Tooltip
|
|
36
|
-
className={rootClassName}
|
|
37
|
-
title={
|
|
38
|
-
<>
|
|
39
|
-
<strong>{messageData.metricLabel}</strong>
|
|
40
|
-
{` value (`}
|
|
41
|
-
<strong>{messageData.currentValue}</strong>
|
|
42
|
-
{`) is ${messageData.diffLabel} `}
|
|
43
|
-
<strong>{messageData.budgetValue}</strong>
|
|
44
|
-
{` budget `}
|
|
45
|
-
</>
|
|
46
|
-
}
|
|
47
|
-
>
|
|
32
|
+
<Tooltip className={rootClassName} title={budgetInsight.message.text}>
|
|
48
33
|
<Icon className={css.icon} glyph={glyph} size="medium" />
|
|
49
34
|
</Tooltip>
|
|
50
35
|
);
|
|
51
36
|
};
|
|
52
37
|
|
|
53
38
|
BudgetInfo.propTypes = {
|
|
54
|
-
metricId: PropTypes.string.isRequired,
|
|
55
39
|
budgetInsight: PropTypes.shape({
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
40
|
+
type: PropTypes.string,
|
|
41
|
+
message: PropTypes.shape({
|
|
42
|
+
text: PropTypes.string,
|
|
43
|
+
}),
|
|
44
|
+
data: PropTypes.shape({
|
|
45
|
+
currentValue: PropTypes.number,
|
|
46
|
+
budgetValue: PropTypes.number,
|
|
47
|
+
failed: PropTypes.bool,
|
|
48
|
+
}),
|
|
59
49
|
}).isRequired,
|
|
60
50
|
className: PropTypes.string,
|
|
61
51
|
};
|
|
@@ -2,7 +2,6 @@ import React, { useMemo, useState } from 'react';
|
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import cx from 'classnames';
|
|
4
4
|
import { METRIC_COMPONENT_LINKS } from '@bundle-stats/utils';
|
|
5
|
-
import * as budgetsInsightsTransformer from '@bundle-stats/utils/lib-esm/transformers/budgets-insights';
|
|
6
5
|
|
|
7
6
|
import { Box } from '../../layout/box';
|
|
8
7
|
import { Stack } from '../../layout/stack';
|
|
@@ -13,19 +12,15 @@ import { ComponentLink } from '../component-link';
|
|
|
13
12
|
import css from './budget-insights.module.css';
|
|
14
13
|
|
|
15
14
|
const Budget = (props) => {
|
|
16
|
-
const { className, metricId,
|
|
15
|
+
const { className, metricId, budget, CustomLink } = props;
|
|
17
16
|
const componentLinkOptions = METRIC_COMPONENT_LINKS.get(metricId);
|
|
18
|
-
const budgetInsightInfo = budgetsInsightsTransformer.getInfo(metricId, budgetInsight);
|
|
19
|
-
const { data: messageData } = budgetInsightInfo.message;
|
|
20
17
|
|
|
21
18
|
return (
|
|
22
|
-
<CustomLink
|
|
23
|
-
|
|
24
|
-
{
|
|
25
|
-
|
|
26
|
-
{
|
|
27
|
-
<strong>{messageData.budgetValue}</strong>
|
|
28
|
-
{` budget `}
|
|
19
|
+
<CustomLink
|
|
20
|
+
className={cx(css.budget, className, budget.type === 'SUCCESS' && css.budgetSuccess)}
|
|
21
|
+
{...componentLinkOptions?.link}
|
|
22
|
+
>
|
|
23
|
+
{budget.message.text}
|
|
29
24
|
</CustomLink>
|
|
30
25
|
);
|
|
31
26
|
};
|
|
@@ -33,10 +28,16 @@ const Budget = (props) => {
|
|
|
33
28
|
Budget.propTypes = {
|
|
34
29
|
className: PropTypes.string,
|
|
35
30
|
metricId: PropTypes.string.isRequired,
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
31
|
+
budget: PropTypes.shape({
|
|
32
|
+
type: PropTypes.string,
|
|
33
|
+
message: PropTypes.shape({
|
|
34
|
+
text: PropTypes.string,
|
|
35
|
+
}),
|
|
36
|
+
data: PropTypes.shape({
|
|
37
|
+
currentValue: PropTypes.number.isRequired,
|
|
38
|
+
budgetValue: PropTypes.number.isRequired,
|
|
39
|
+
failed: PropTypes.bool.isRequired,
|
|
40
|
+
}),
|
|
40
41
|
}).isRequired,
|
|
41
42
|
CustomLink: PropTypes.elementType.isRequired,
|
|
42
43
|
};
|
|
@@ -45,14 +46,39 @@ Budget.defaultProps = {
|
|
|
45
46
|
className: '',
|
|
46
47
|
};
|
|
47
48
|
|
|
48
|
-
const
|
|
49
|
-
|
|
49
|
+
const BUDGET_ALERT_KIND = new Map([
|
|
50
|
+
['ERROR', 'danger'],
|
|
51
|
+
['WARNING', 'warning'],
|
|
52
|
+
['SUCCESS', 'success'],
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
const BUDGET_ICON = new Map([
|
|
56
|
+
['ERROR', Icon.ICONS.ALERT_CIRCLE],
|
|
57
|
+
['WARNING', Icon.ICONS.WARNING],
|
|
58
|
+
['SUCCESS', Icon.ICONS.CHECK_CIRCLE],
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
export const BudgetInsights = (props) => {
|
|
62
|
+
const { className, source, budgets, CustomLink } = props;
|
|
50
63
|
const [showBudgets, setShowBudgets] = useState(false);
|
|
51
64
|
|
|
52
|
-
const
|
|
65
|
+
const iconGlyph = BUDGET_ICON.get(budgets.type);
|
|
66
|
+
const alertKind = BUDGET_ALERT_KIND.get(budgets.type);
|
|
67
|
+
const rootClassName = cx(css.group, className, css[`group-${alertKind}`]);
|
|
68
|
+
|
|
69
|
+
const budgetItems = useMemo(() => {
|
|
70
|
+
const items = Object.entries(budgets.data);
|
|
71
|
+
|
|
72
|
+
// When there are errors/warnings, sort budget items first
|
|
73
|
+
if (['ERROR', 'WARNING'].includes(budgets.type)) {
|
|
74
|
+
items.sort((a) => (a[1].type === budgets.type ? -1 : 1));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return items;
|
|
78
|
+
}, [budgets]);
|
|
53
79
|
|
|
54
80
|
return (
|
|
55
|
-
<Alert kind={
|
|
81
|
+
<Alert kind={alertKind} padding="none" className={rootClassName}>
|
|
56
82
|
<Box
|
|
57
83
|
padding={['xsmall', 'small']}
|
|
58
84
|
className={css.groupHeader}
|
|
@@ -61,14 +87,8 @@ const BudgetsGroup = (props) => {
|
|
|
61
87
|
onClick={() => setShowBudgets(!showBudgets)}
|
|
62
88
|
>
|
|
63
89
|
<FlexStack space="xxsmall" className={css.groupTitle}>
|
|
64
|
-
<Icon
|
|
65
|
-
|
|
66
|
-
glyph={isFailed ? Icon.ICONS.ALERT_CIRCLE : Icon.ICONS.CHECK_CIRCLE}
|
|
67
|
-
/>
|
|
68
|
-
<span>
|
|
69
|
-
<strong>{budgets.length}</strong>
|
|
70
|
-
{` budget ${budgets.length > 1 ? 'checks' : 'check'} ${isFailed ? 'failed' : 'passed'}`}
|
|
71
|
-
</span>
|
|
90
|
+
{iconGlyph && <Icon className={css.groupIcon} glyph={iconGlyph} />}
|
|
91
|
+
<span>{budgets.message.text}</span>
|
|
72
92
|
<span className={css.groupTitleToggle}>{showBudgets ? 'hide' : 'show'} all</span>
|
|
73
93
|
</FlexStack>
|
|
74
94
|
</Box>
|
|
@@ -76,11 +96,11 @@ const BudgetsGroup = (props) => {
|
|
|
76
96
|
{showBudgets && (
|
|
77
97
|
<Box padding={['xsmall', 'small']} className={css.groupContent}>
|
|
78
98
|
<Stack space="none">
|
|
79
|
-
{
|
|
99
|
+
{budgetItems.map(([key, budget]) => (
|
|
80
100
|
<Budget
|
|
81
101
|
key={key}
|
|
82
102
|
metricId={`${source}.${key}`}
|
|
83
|
-
|
|
103
|
+
budget={budget}
|
|
84
104
|
CustomLink={CustomLink}
|
|
85
105
|
/>
|
|
86
106
|
))}
|
|
@@ -91,59 +111,6 @@ const BudgetsGroup = (props) => {
|
|
|
91
111
|
);
|
|
92
112
|
};
|
|
93
113
|
|
|
94
|
-
BudgetsGroup.propTypes = {
|
|
95
|
-
className: PropTypes.string,
|
|
96
|
-
kind: PropTypes.oneOf(['success', 'danger']).isRequired,
|
|
97
|
-
source: PropTypes.string.isRequired,
|
|
98
|
-
budgets: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
|
99
|
-
CustomLink: PropTypes.elementType.isRequired,
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
BudgetsGroup.defaultProps = {
|
|
103
|
-
className: '',
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
export const BudgetInsights = (props) => {
|
|
107
|
-
const { className, source, budgets, CustomLink } = props;
|
|
108
|
-
|
|
109
|
-
const [failedBudgets, passedBudgets] = useMemo(() => {
|
|
110
|
-
const passed = [];
|
|
111
|
-
const failed = [];
|
|
112
|
-
|
|
113
|
-
Object.entries(budgets).forEach(([key, budget]) => {
|
|
114
|
-
if (budget.failed) {
|
|
115
|
-
failed.push([key, budget]);
|
|
116
|
-
} else {
|
|
117
|
-
passed.push([key, budget]);
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
return [failed, passed];
|
|
122
|
-
}, [budgets]);
|
|
123
|
-
|
|
124
|
-
return (
|
|
125
|
-
<Stack className={className} space="xsmall">
|
|
126
|
-
{failedBudgets.length > 0 && (
|
|
127
|
-
<BudgetsGroup
|
|
128
|
-
kind="danger"
|
|
129
|
-
source={source}
|
|
130
|
-
budgets={failedBudgets}
|
|
131
|
-
CustomLink={CustomLink}
|
|
132
|
-
/>
|
|
133
|
-
)}
|
|
134
|
-
|
|
135
|
-
{passedBudgets.length > 0 && (
|
|
136
|
-
<BudgetsGroup
|
|
137
|
-
kind="success"
|
|
138
|
-
source={source}
|
|
139
|
-
budgets={passedBudgets}
|
|
140
|
-
CustomLink={CustomLink}
|
|
141
|
-
/>
|
|
142
|
-
)}
|
|
143
|
-
</Stack>
|
|
144
|
-
);
|
|
145
|
-
};
|
|
146
|
-
|
|
147
114
|
BudgetInsights.propTypes = {
|
|
148
115
|
className: PropTypes.string,
|
|
149
116
|
source: PropTypes.string.isRequired,
|
|
@@ -20,11 +20,15 @@
|
|
|
20
20
|
margin: 0 0 0 auto !important;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
.danger .groupIcon {
|
|
23
|
+
.group-danger .groupIcon {
|
|
24
24
|
color: var(--color-danger);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
.
|
|
27
|
+
.group-warning .groupIcon {
|
|
28
|
+
color: var(--color-warning);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.group-success .groupIcon {
|
|
28
32
|
color: var(--color-success);
|
|
29
33
|
}
|
|
30
34
|
|
|
@@ -39,6 +43,10 @@
|
|
|
39
43
|
text-decoration: none;
|
|
40
44
|
}
|
|
41
45
|
|
|
46
|
+
.budgetSuccess {
|
|
47
|
+
color: var(--color-text-light);
|
|
48
|
+
}
|
|
49
|
+
|
|
42
50
|
.budgetLabel,
|
|
43
51
|
.budgetValue {
|
|
44
52
|
color: var(--color-text-dark);
|
|
@@ -146,7 +146,7 @@ RowHeader.propTypes = {
|
|
|
146
146
|
isChunk: PropTypes.bool,
|
|
147
147
|
isInitial: PropTypes.bool,
|
|
148
148
|
isEntry: PropTypes.bool,
|
|
149
|
-
runs: PropTypes.arrayOf(PropTypes.object),
|
|
149
|
+
runs: PropTypes.arrayOf(PropTypes.object), // eslint-disable-line react/forbid-prop-types
|
|
150
150
|
}).isRequired,
|
|
151
151
|
chunks: PropTypes.arrayOf(
|
|
152
152
|
PropTypes.shape({
|
|
@@ -18,7 +18,7 @@ export const BundleAssetsTotalsTable = ({
|
|
|
18
18
|
...restProps
|
|
19
19
|
}) => {
|
|
20
20
|
const items = useMemo(() => webpack.compareBySection.sizes(jobs), [jobs]);
|
|
21
|
-
const budgets = jobs[0].insights?.webpack?.budgets;
|
|
21
|
+
const budgets = jobs[0].insights?.webpack?.budgets?.data;
|
|
22
22
|
|
|
23
23
|
const renderRowHeader = useCallback(
|
|
24
24
|
(item) => {
|
|
@@ -30,9 +30,9 @@ export const BundleAssetsTotalsTable = ({
|
|
|
30
30
|
|
|
31
31
|
return (
|
|
32
32
|
<CustomComponentLink section={section} title={title} params={params}>
|
|
33
|
-
<FlexStack space="
|
|
33
|
+
<FlexStack space="xxxsmall" className={css.itemTitleText}>
|
|
34
34
|
<span>{item.label}</span>
|
|
35
|
-
{budget && <BudgetInfo
|
|
35
|
+
{budget && <BudgetInfo budgetInsight={budget} />}
|
|
36
36
|
</FlexStack>
|
|
37
37
|
</CustomComponentLink>
|
|
38
38
|
);
|
|
@@ -5,7 +5,6 @@ import { get } from 'lodash';
|
|
|
5
5
|
import { METRIC_COMPONENT_LINKS } from '@bundle-stats/utils';
|
|
6
6
|
|
|
7
7
|
import { METRICS_WEBPACK_GENERAL } from '../../constants';
|
|
8
|
-
import { Icon } from '../../ui/icon';
|
|
9
8
|
import { Box } from '../../layout/box';
|
|
10
9
|
import { FlexStack } from '../../layout/flex-stack';
|
|
11
10
|
import { ComponentLink } from '../component-link';
|
|
@@ -29,22 +28,19 @@ export const Summary = ({
|
|
|
29
28
|
{Array.from(METRIC_COMPONENT_LINKS)
|
|
30
29
|
.filter(([metricId]) => keys.includes(getSourceMetricId(metricId)))
|
|
31
30
|
.map(([metricId, metricOptions]) => (
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
</SummaryItemCustomLink>
|
|
46
|
-
)}
|
|
47
|
-
</div>
|
|
31
|
+
<SummaryItem
|
|
32
|
+
key={metricId}
|
|
33
|
+
className={css.item}
|
|
34
|
+
as={SummaryItemCustomLink}
|
|
35
|
+
{...metricOptions.link}
|
|
36
|
+
size={size}
|
|
37
|
+
id={metricId}
|
|
38
|
+
data={get(data, metricId)}
|
|
39
|
+
budget={get(budgets, ['data', getSourceMetricId(metricId)])}
|
|
40
|
+
loading={loading}
|
|
41
|
+
showMetricDescription
|
|
42
|
+
showDelta={showSummaryItemDelta && metricOptions.showDelta !== false}
|
|
43
|
+
/>
|
|
48
44
|
))}
|
|
49
45
|
</FlexStack>
|
|
50
46
|
</Box>
|
|
@@ -6,49 +6,5 @@
|
|
|
6
6
|
.item {
|
|
7
7
|
padding: var(--space-small);
|
|
8
8
|
flex: 1 0 25%; /* force equal sizes */
|
|
9
|
-
|
|
10
|
-
z-index: 1;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
.item::before {
|
|
14
|
-
content: '';
|
|
15
|
-
display: block;
|
|
16
|
-
width: 1px;
|
|
17
|
-
position: absolute;
|
|
18
|
-
left: -1px;
|
|
19
|
-
top: var(--space-small);
|
|
20
|
-
bottom: var(--space-small);
|
|
21
|
-
background: var(--color-outline);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
.readMore {
|
|
25
|
-
display: block;
|
|
26
|
-
position: absolute;
|
|
27
|
-
right: 0;
|
|
28
|
-
bottom: 0;
|
|
29
|
-
padding: var(--space-small);
|
|
30
|
-
color: var(--color-text-ultra-light);
|
|
31
|
-
transition: var(--transition-out);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
.readMore:hover,
|
|
35
|
-
.readMore:focus {
|
|
36
|
-
color: var(--color-primary);
|
|
37
|
-
transition: var(--transition-in);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.readMoreIcon {
|
|
41
|
-
display: block;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** hover state */
|
|
45
|
-
.readMore {
|
|
46
|
-
opacity: 0;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
.item:hover .readMore,
|
|
50
|
-
.item:active .readMore,
|
|
51
|
-
.item:focus .readMore {
|
|
52
|
-
opacity: 1;
|
|
53
|
-
transition: var(--transition-in);
|
|
9
|
+
text-decoration: none;
|
|
54
10
|
}
|
|
@@ -4,10 +4,9 @@ import cx from 'classnames';
|
|
|
4
4
|
import { getGlobalMetricType, getMetricRunInfo } from '@bundle-stats/utils';
|
|
5
5
|
|
|
6
6
|
import { HoverCard } from '../../ui/hover-card';
|
|
7
|
-
import { Icon } from '../../ui/icon';
|
|
8
7
|
import { Skeleton } from '../../ui/skeleton';
|
|
9
|
-
import { Stack } from '../../layout/stack';
|
|
10
8
|
import { FlexStack } from '../../layout/flex-stack';
|
|
9
|
+
import { Stack } from '../../layout/stack';
|
|
11
10
|
import { Metric } from '../metric';
|
|
12
11
|
import { Delta } from '../delta';
|
|
13
12
|
import { BudgetInfo } from '../budget-info';
|
|
@@ -18,9 +17,9 @@ const MetricInfo = ({ description, url }) => (
|
|
|
18
17
|
<p>{description}</p>
|
|
19
18
|
{url && (
|
|
20
19
|
<p>
|
|
21
|
-
<
|
|
20
|
+
<button type="button" onClick={() => window.open(url)} className={css.readMoreLink}>
|
|
22
21
|
Read more
|
|
23
|
-
</
|
|
22
|
+
</button>
|
|
24
23
|
</p>
|
|
25
24
|
)}
|
|
26
25
|
</Stack>
|
|
@@ -55,17 +54,16 @@ export const SummaryItem = ({
|
|
|
55
54
|
const rootClassName = cx(css.root, className, css[size], showDelta && css.showDelta);
|
|
56
55
|
|
|
57
56
|
return (
|
|
58
|
-
<Stack space="
|
|
59
|
-
<FlexStack
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
{showMetricDescriptionTooltip && (
|
|
63
|
-
<HoverCard className={css.icon} label={<Icon glyph="help" />}>
|
|
57
|
+
<Stack space="xxsmall" as={Component} className={rootClassName} {...props}>
|
|
58
|
+
<FlexStack space="xxxsmall" as="h3" className={css.title}>
|
|
59
|
+
{showMetricDescriptionTooltip ? (
|
|
60
|
+
<HoverCard label={metric.label}>
|
|
64
61
|
<MetricInfo {...metric} />
|
|
65
62
|
</HoverCard>
|
|
63
|
+
) : (
|
|
64
|
+
metric.label
|
|
66
65
|
)}
|
|
67
|
-
|
|
68
|
-
{budget && <BudgetInfo className={css.budgetInfo} metricId={id} budgetInsight={budget} />}
|
|
66
|
+
{budget && <BudgetInfo className={css.budgetInfo} budgetInsight={budget} />}
|
|
69
67
|
</FlexStack>
|
|
70
68
|
|
|
71
69
|
{!loading ? (
|
|
@@ -134,9 +132,5 @@ SummaryItem.propTypes = {
|
|
|
134
132
|
showDelta: PropTypes.bool,
|
|
135
133
|
|
|
136
134
|
/** Budget insight data */
|
|
137
|
-
budget: PropTypes.
|
|
138
|
-
currentBalue: PropTypes.number,
|
|
139
|
-
budgetValue: PropTypes.number,
|
|
140
|
-
failed: PropTypes.bool,
|
|
141
|
-
}),
|
|
135
|
+
budget: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
|
142
136
|
};
|
|
@@ -8,17 +8,11 @@
|
|
|
8
8
|
color: var(--color-text-ultra-light);
|
|
9
9
|
font-weight: bold;
|
|
10
10
|
font-size: var(--size-xsmall);
|
|
11
|
-
line-height:
|
|
11
|
+
line-height: 1;
|
|
12
12
|
text-transform: uppercase;
|
|
13
13
|
white-space: nowrap;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
.budgetInfo {
|
|
17
|
-
position: absolute;
|
|
18
|
-
right: 0;
|
|
19
|
-
top: 0;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
16
|
.currentMetric {
|
|
23
17
|
display: block;
|
|
24
18
|
color: var(--color-heading);
|
|
@@ -39,6 +33,30 @@
|
|
|
39
33
|
line-height: var(--space-small);
|
|
40
34
|
}
|
|
41
35
|
|
|
36
|
+
.readMoreLink {
|
|
37
|
+
appearance: none;
|
|
38
|
+
background: none;
|
|
39
|
+
margin: 0;
|
|
40
|
+
border: 0;
|
|
41
|
+
padding: 0;
|
|
42
|
+
color: var(--color-primary);
|
|
43
|
+
text-decoration: underline;
|
|
44
|
+
cursor: pointer;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.readMoreLink:hover,
|
|
48
|
+
.readMoreLink:active,
|
|
49
|
+
.readMoreLink:focus {
|
|
50
|
+
color: var(--color-primary-dark);
|
|
51
|
+
text-decoration: none;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* Hover state */
|
|
55
|
+
.root:hover .title {
|
|
56
|
+
color: var(--color-text-light);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
42
60
|
/* Loading state */
|
|
43
61
|
.loading.currentMetric {
|
|
44
62
|
width: 8em;
|
|
@@ -31,6 +31,10 @@ export const HoverCard = (props) => {
|
|
|
31
31
|
// Fallback to span if no href
|
|
32
32
|
const Component = resolveComponent(as, href);
|
|
33
33
|
|
|
34
|
+
const handleOnClick = (event) => {
|
|
35
|
+
event.preventDefault();
|
|
36
|
+
};
|
|
37
|
+
|
|
34
38
|
return (
|
|
35
39
|
<div className={cx(css.root, className)}>
|
|
36
40
|
<HovercardAnchor state={state} href={href} className={css.anchor} as={Component}>
|
|
@@ -41,6 +45,7 @@ export const HoverCard = (props) => {
|
|
|
41
45
|
state={state}
|
|
42
46
|
className={cx(css.hoverCard, hoverCardClassName)}
|
|
43
47
|
style={{ zIndex: 10000 }}
|
|
48
|
+
onClick={handleOnClick}
|
|
44
49
|
>
|
|
45
50
|
<HovercardArrow size={24} />
|
|
46
51
|
{typeof children === 'function' ? children({ close: state.hide }) : children}
|