Skip to content

Commit 1704890

Browse files
committed
feat: rework metric cards to tabs
1 parent 7a7f038 commit 1704890

File tree

30 files changed

+659
-583
lines changed

30 files changed

+659
-583
lines changed

src/components/DiagnosticCard/DiagnosticCard.scss

Lines changed: 0 additions & 35 deletions
This file was deleted.

src/components/DiagnosticCard/DiagnosticCard.tsx

Lines changed: 0 additions & 23 deletions
This file was deleted.

src/components/DoughnutMetrics/DoughnutMetrics.scss

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
.ydb-doughnut-metrics {
22
--doughnut-border: 16px;
3-
--doughnut-color: var(--ydb-color-status-green);
4-
--doughnut-backdrop-color: var(--g-color-base-positive-light);
3+
--doughnut-width: 100px;
4+
--doughnut-wrapper-indent: calc(var(--doughnut-border) + 5px);
5+
--doughnut-color: var(--g-color-base-positive-heavy);
6+
--doughnut-backdrop-color: var(--g-color-sfx-fade);
57
--doughnut-overlap-color: var(--g-color-base-positive-heavy-hover);
8+
69
&__doughnut {
710
position: relative;
811

9-
width: 100px;
12+
width: var(--doughnut-width);
1013
aspect-ratio: 1;
1114

1215
border-radius: 50%;
@@ -20,45 +23,64 @@
2023
content: '';
2124

2225
border-radius: 50%;
23-
background-color: var(--g-color-base-background);
26+
background-color: var(--center-color, var(--g-color-base-background));
2427

2528
transform: translate(var(--doughnut-border), var(--doughnut-border));
2629
aspect-ratio: 1;
2730
}
2831
}
32+
33+
// Size modifiers - using visually centered values
34+
&__doughnut_size_small {
35+
--doughnut-border: 12px;
36+
--doughnut-width: 65px;
37+
--doughnut-wrapper-indent: 15px;
38+
}
39+
40+
&__doughnut_size_medium {
41+
--doughnut-border: 16px;
42+
--doughnut-width: 100px;
43+
--doughnut-wrapper-indent: calc(var(--doughnut-border) + 5px);
44+
}
45+
46+
&__doughnut_size_large {
47+
--doughnut-border: 20px;
48+
--doughnut-width: 130px;
49+
--doughnut-wrapper-indent: 25px;
50+
}
51+
2952
&__doughnut_status_warning {
30-
--doughnut-color: var(--ydb-color-status-yellow);
31-
--doughnut-backdrop-color: var(--g-color-base-warning-light);
53+
--doughnut-color: var(--g-color-base-warning-heavy);
54+
--doughnut-backdrop-color: var(--g-color-sfx-fade);
3255
--doughnut-overlap-color: var(--g-color-base-warning-heavy-hover);
3356
}
3457
&__doughnut_status_danger {
35-
--doughnut-color: var(--ydb-color-status-red);
36-
--doughnut-backdrop-color: var(--g-color-base-danger-light);
58+
--doughnut-color: var(--g-color-base-danger-heavy);
59+
--doughnut-backdrop-color: var(--g-color-sfx-fade);
3760
--doughnut-overlap-color: var(--g-color-base-danger-heavy-hover);
3861
}
3962
&__text-wrapper {
40-
--wrapper-indent: calc(var(--doughnut-border) + 5px);
41-
4263
position: absolute;
43-
top: var(--wrapper-indent);
44-
right: var(--wrapper-indent);
64+
top: var(--doughnut-wrapper-indent);
65+
right: var(--doughnut-wrapper-indent);
4566

4667
display: flex;
4768
flex-direction: column;
4869
justify-content: center;
4970
align-items: center;
5071

51-
width: calc(100% - calc(var(--wrapper-indent) * 2));
72+
width: calc(100% - calc(var(--doughnut-wrapper-indent) * 2));
5273

5374
text-align: center;
5475

5576
transform: rotate(180deg);
5677
aspect-ratio: 1;
5778
}
79+
5880
&__value {
59-
position: absolute;
60-
bottom: 20px;
81+
color: var(--doughnut-color);
6182
}
83+
6284
&__legend-note {
6385
display: flex;
6486
}

src/components/DoughnutMetrics/DoughnutMetrics.tsx

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import './DoughnutMetrics.scss';
1010

1111
const b = cn('ydb-doughnut-metrics');
1212

13+
const SizeContext = React.createContext<'small' | 'medium' | 'large'>('medium');
14+
1315
interface LegendProps {
1416
children?: React.ReactNode;
1517
variant?: TextProps['variant'];
@@ -31,9 +33,19 @@ function Legend({children, variant = 'subheader-3', color = 'primary', note}: Le
3133
</Flex>
3234
);
3335
}
34-
function Value({children, variant = 'subheader-2'}: LegendProps) {
36+
function Value({children, variant}: LegendProps) {
37+
const size = React.useContext(SizeContext);
38+
39+
const sizeVariantMap = {
40+
small: 'subheader-1',
41+
medium: 'subheader-2',
42+
large: 'subheader-3',
43+
} as const;
44+
45+
const finalVariant = variant || sizeVariantMap[size];
46+
3547
return (
36-
<Text variant={variant} className={b('value')}>
48+
<Text variant={finalVariant} className={b('value')}>
3749
{children}
3850
</Text>
3951
);
@@ -44,9 +56,18 @@ interface DoughnutProps {
4456
fillWidth: number;
4557
children?: React.ReactNode;
4658
className?: string;
59+
size?: 'small' | 'medium' | 'large';
60+
centerColor?: string;
4761
}
4862

49-
export function DoughnutMetrics({status, fillWidth, children, className}: DoughnutProps) {
63+
export function DoughnutMetrics({
64+
status,
65+
fillWidth,
66+
children,
67+
className,
68+
size = 'medium',
69+
centerColor,
70+
}: DoughnutProps) {
5071
let filledDegrees = fillWidth * 3.6;
5172
let doughnutFillVar = 'var(--doughnut-color)';
5273
let doughnutBackdropVar = 'var(--doughnut-backdrop-color)';
@@ -57,17 +78,19 @@ export function DoughnutMetrics({status, fillWidth, children, className}: Doughn
5778
doughnutFillVar = 'var(--doughnut-overlap-color)';
5879
}
5980

81+
const doughnutStyle: React.CSSProperties = {
82+
background: `conic-gradient(${doughnutFillVar} 0deg ${filledDegrees}deg, ${doughnutBackdropVar} ${filledDegrees}deg 360deg)`,
83+
...(centerColor && ({'--center-color': centerColor} as React.CSSProperties)),
84+
};
85+
6086
return (
61-
<div className={b(null, className)}>
62-
<div
63-
style={{
64-
background: `conic-gradient(${doughnutFillVar} 0deg ${filledDegrees}deg, ${doughnutBackdropVar} ${filledDegrees}deg 360deg)`,
65-
}}
66-
className={b('doughnut', {status})}
67-
>
68-
<div className={b('text-wrapper')}>{children}</div>
87+
<SizeContext.Provider value={size}>
88+
<div className={b(null, className)}>
89+
<div style={doughnutStyle} className={b('doughnut', {status, size})}>
90+
<div className={b('text-wrapper')}>{children}</div>
91+
</div>
6992
</div>
70-
</div>
93+
</SizeContext.Provider>
7194
);
7295
}
7396

src/components/MetricChart/MetricChart.scss

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@
22
display: flex;
33
flex-direction: column;
44

5-
padding: var(--g-spacing-4) var(--g-spacing-4) var(--g-spacing-2);
5+
width: 100%;
6+
padding: 0;
67

7-
border: 1px solid var(--g-color-line-generic);
8-
border-radius: 8px;
8+
border: none;
99

10-
&_noBorder {
11-
padding: 0;
12-
13-
border: none;
10+
&__toolbar {
11+
margin-bottom: 10px;
1412
}
1513

1614
&__chart {

src/components/MetricChart/MetricChart.tsx

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ import React from 'react';
33
import ChartKit, {settings} from '@gravity-ui/chartkit';
44
import type {YagrWidgetData} from '@gravity-ui/chartkit/yagr';
55
import {YagrPlugin} from '@gravity-ui/chartkit/yagr';
6+
import {Flex} from '@gravity-ui/uikit';
67

78
import {cn} from '../../utils/cn';
89
import type {TimeFrame} from '../../utils/timeframes';
910
import {ResponseError} from '../Errors/ResponseError';
1011
import {Loader} from '../Loader';
12+
import {TimeFrameDropdown} from '../TimeFrameDropdown/TimeFrameDropdown';
1113

1214
import {colorToRGBA, colors} from './colors';
1315
import {getDefaultDataFormatter} from './getDefaultDataFormatter';
@@ -23,6 +25,9 @@ import './MetricChart.scss';
2325

2426
const b = cn('ydb-metric-chart');
2527

28+
// Constants
29+
const DEFAULT_EFFECTIVE_WIDTH = 600; // Used for maxDataPoints calculation when using fullWidth
30+
2631
settings.set({plugins: [YagrPlugin]});
2732

2833
const prepareWidgetData = (
@@ -104,7 +109,8 @@ interface DiagnosticsChartProps {
104109
database: string;
105110

106111
metrics: MetricDescription[];
107-
timeFrame?: TimeFrame;
112+
timeFrame: TimeFrame;
113+
onTimeFrameChange: (timeFrame: TimeFrame) => void;
108114

109115
autorefresh?: number;
110116

@@ -122,33 +128,25 @@ interface DiagnosticsChartProps {
122128
*/
123129
isChartVisible?: boolean;
124130

125-
/** Remove border from chart */
126-
noBorder?: boolean;
127-
128-
/** Make chart take full width of container */
129-
fullWidth?: boolean;
130-
131-
/** Render custom toolbar content to the right of chart title */
132-
renderChartToolbar?: () => React.ReactNode;
131+
/** Chart title displayed in the toolbar */
132+
title: string;
133133
}
134134

135135
export const MetricChart = ({
136136
database,
137137
metrics,
138-
timeFrame = '1h',
138+
timeFrame,
139+
onTimeFrameChange,
139140
autorefresh,
140141
width = 400,
141142
height = width / 1.5,
142143
chartOptions,
143144
onChartDataStatusChange,
144145
isChartVisible,
145-
noBorder,
146-
fullWidth,
147-
renderChartToolbar,
146+
title,
148147
}: DiagnosticsChartProps) => {
149148
// Use a reasonable default for maxDataPoints when fullWidth is true
150-
const effectiveWidth = fullWidth ? 600 : width;
151-
const maxDataPoints = effectiveWidth / 2;
149+
const maxDataPoints = DEFAULT_EFFECTIVE_WIDTH / 2;
152150

153151
const {currentData, error, isFetching, status} = chartApi.useGetChartDataQuery(
154152
// maxDataPoints param is calculated based on width
@@ -171,6 +169,13 @@ export const MetricChart = ({
171169

172170
const convertedData = prepareWidgetData(currentData || emptyChartData, chartOptions);
173171

172+
const renderToolbar = () => (
173+
<Flex className={b('toolbar')} justifyContent="space-between" alignItems="center">
174+
<div>{title}</div>
175+
<TimeFrameDropdown value={timeFrame} onChange={onTimeFrameChange} />
176+
</Flex>
177+
);
178+
174179
const renderContent = () => {
175180
if (loading) {
176181
return <Loader />;
@@ -190,13 +195,12 @@ export const MetricChart = ({
190195

191196
return (
192197
<div
193-
className={b({noBorder})}
198+
className={b()}
194199
style={{
195200
height,
196-
width: fullWidth ? '100%' : width,
197201
}}
198202
>
199-
{renderChartToolbar?.()}
203+
{renderToolbar()}
200204
{renderContent()}
201205
</div>
202206
);

0 commit comments

Comments
 (0)