Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit de5507c

Browse files
committedJan 4, 2023
WIP: feat(ui): Checks
1 parent 3eab204 commit de5507c

File tree

4 files changed

+253
-0
lines changed

4 files changed

+253
-0
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
.checkMetric {
2+
display: inline;
3+
}
4+
5+
.checkMetric::before {
6+
content: ' (';
7+
}
8+
9+
.checkMetric::after {
10+
content: ') ';
11+
}
12+
13+
.checkMetricDelta {
14+
padding: 2px;
15+
font-size: 8px;
16+
vertical-align: super;
17+
}
18+
19+
.checkThreshold {
20+
display: inline;
21+
font-weight: bold;
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import React from 'react';
2+
import { CheckResult, CheckStatus, MetricRunInfoDeltaType } from '@bundle-stats/utils';
3+
4+
import { Checks } from '.';
5+
6+
export default {
7+
title: 'Components/Checks',
8+
comonent: Checks,
9+
};
10+
11+
const CHECKS: Array<CheckResult> = [
12+
{
13+
condition: {
14+
fact: 'webpack.duplicatePackagesCount.delta',
15+
operator: 'greaterThan',
16+
value: 0,
17+
},
18+
value: 2,
19+
data: {
20+
value: 4,
21+
displayValue: '4',
22+
delta: 2,
23+
displayDelta: '+2',
24+
deltaPercentage: 50,
25+
deltaType: MetricRunInfoDeltaType.HIGH_NEGATIVE,
26+
displayDeltaPercentage: '+50%',
27+
},
28+
matched: true,
29+
status: CheckStatus.FAILURE,
30+
},
31+
{
32+
condition: {
33+
fact: 'webpack.duplicatePackagesCount.value',
34+
operator: 'greaterThan',
35+
value: 0,
36+
},
37+
value: 2,
38+
data: {
39+
value: 2,
40+
displayValue: '2',
41+
delta: 0,
42+
displayDelta: '0',
43+
deltaType: MetricRunInfoDeltaType.NO_CHANGE,
44+
deltaPercentage: 0,
45+
displayDeltaPercentage: '0%',
46+
},
47+
matched: true,
48+
status: CheckStatus.WARNING,
49+
},
50+
{
51+
condition: {
52+
fact: 'webpack.packageCount.delta',
53+
operator: 'greaterThan',
54+
value: 0,
55+
},
56+
value: 10,
57+
data: {
58+
value: 10,
59+
displayValue: '1',
60+
delta: 1,
61+
displayDelta: '+1',
62+
deltaType: MetricRunInfoDeltaType.NEGATIVE,
63+
deltaPercentage: 10,
64+
displayDeltaPercentage: '+10%',
65+
},
66+
matched: true,
67+
status: CheckStatus.WARNING,
68+
},
69+
{
70+
condition: {
71+
fact: 'webpack.totalSizeByTypeALL.delta',
72+
operator: 'greaterThan',
73+
value: 5 * 1024,
74+
},
75+
value: 0,
76+
data: {
77+
value: 1048576,
78+
displayValue: '1MiB',
79+
delta: 10 * 1024,
80+
displayDelta: '+10KiB',
81+
deltaType: MetricRunInfoDeltaType.NEGATIVE,
82+
deltaPercentage: 1,
83+
displayDeltaPercentage: '+1%',
84+
},
85+
matched: true,
86+
status: CheckStatus.WARNING,
87+
},
88+
];
89+
90+
export const Default = () => <Checks checks={CHECKS} />;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import React, { useMemo } from 'react';
2+
import {
3+
CheckEvaluated,
4+
CheckResult,
5+
CheckStatus,
6+
ConditionOperator,
7+
getGlobalMetricType,
8+
MetricRunInfoDeltaType,
9+
} from '@bundle-stats/utils';
10+
11+
import { Stack } from '../../layout/stack';
12+
import { Alert } from '../../ui/alert';
13+
import { Delta } from '../delta';
14+
import { Metric } from '../metric';
15+
// @ts-ignore
16+
import css from './checks.module.css';
17+
18+
const OPERATOR_MAP: Record<ConditionOperator, string> = {
19+
smallerThanInclusive: 'equal',
20+
smallerThan: 'bellow',
21+
equal: 'equal',
22+
notEqual: 'not equal',
23+
greaterThan: 'above',
24+
greaterThanInclusive: 'equal',
25+
};
26+
27+
const VALUE_MAP: Record<string, string> = {
28+
value: 'to',
29+
delta: 'by',
30+
deltaPercentage: 'by',
31+
};
32+
33+
const ALERT_MAP: Record<string, string> = {
34+
FAILURE: 'danger',
35+
WARNING: 'warning',
36+
SUCCESS: 'success',
37+
};
38+
39+
interface CheckMatched extends Omit<CheckEvaluated, 'status'> {
40+
status: CheckStatus;
41+
}
42+
43+
interface CheckProps extends React.HTMLAttributes<HTMLElement> {
44+
check: CheckMatched;
45+
}
46+
47+
const Check = (props: CheckProps) => {
48+
const { check, ...restProps } = props;
49+
50+
const metricSegments = check.condition.fact.split('.');
51+
const metricKey = metricSegments.slice(0, -1).join('.');
52+
53+
const field = metricSegments[metricSegments.length - 1];
54+
55+
let displayField = '';
56+
let deltaDisplayField: string;
57+
58+
switch (field) {
59+
case 'deltaPercentage':
60+
displayField = 'relative difference';
61+
deltaDisplayField = check.data.displayDeltaPercentage as string;
62+
break;
63+
case 'delta':
64+
displayField = 'absolute difference';
65+
deltaDisplayField = check.data.displayDelta as string;
66+
break;
67+
default:
68+
displayField = 'value';
69+
deltaDisplayField = check.data.displayDeltaPercentage as string;
70+
}
71+
72+
const metric = getGlobalMetricType(metricKey);
73+
74+
return (
75+
<Alert kind={ALERT_MAP[check.status]} {...restProps}>
76+
<p>
77+
<strong>{metric.label}</strong>
78+
{` ${displayField} `}
79+
<Metric inline value={check.data.value} formatter={metric.formatter} className={css.checkMetric}>
80+
<Delta
81+
displayValue={deltaDisplayField}
82+
deltaType={check.data.deltaType as MetricRunInfoDeltaType}
83+
inverted
84+
className={css.checkMetricDelta}
85+
/>
86+
</Metric>
87+
{` is ${OPERATOR_MAP[check.condition.operator]} `}
88+
<Metric
89+
value={check.condition.value}
90+
formatter={metric.formatter}
91+
inline
92+
className={css.checkThreshold}
93+
/>
94+
</p>
95+
</Alert>
96+
);
97+
};
98+
99+
export interface ChecksProps extends React.HTMLAttributes<HTMLElement> {
100+
checks: Array<CheckResult>;
101+
}
102+
103+
export const Checks = (props: ChecksProps) => {
104+
const { checks, ...restProps } = props;
105+
106+
const [matchedChecks] = useMemo(() => {
107+
const matched: Array<CheckMatched> = [];
108+
const unmatched: Array<CheckEvaluated> = [];
109+
110+
checks.forEach((check) => {
111+
// @ts-expect-error
112+
if (typeof check.data === 'undefined') {
113+
return;
114+
}
115+
116+
// @ts-expect-error
117+
if (typeof check.status === 'undefined') {
118+
return;
119+
}
120+
121+
// @ts-expect-error
122+
if (check.matched === true) {
123+
matched.push(check as CheckMatched);
124+
// @ts-expect-error
125+
} else if (check.matched === false) {
126+
unmatched.push(check as CheckEvaluated);
127+
}
128+
});
129+
130+
return [matched, unmatched];
131+
}, [checks]);
132+
133+
return (
134+
<Stack space="xxsmall" {...restProps}>
135+
{matchedChecks.map((check) => (
136+
<Check check={check} />
137+
))}
138+
</Stack>
139+
);
140+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './checks';

0 commit comments

Comments
 (0)
Please sign in to comment.