Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 0 additions & 35 deletions src/components/DiagnosticCard/DiagnosticCard.scss

This file was deleted.

23 changes: 0 additions & 23 deletions src/components/DiagnosticCard/DiagnosticCard.tsx

This file was deleted.

65 changes: 38 additions & 27 deletions src/components/DoughnutMetrics/DoughnutMetrics.scss
Original file line number Diff line number Diff line change
@@ -1,64 +1,75 @@
.ydb-doughnut-metrics {
--doughnut-border: 16px;
--doughnut-color: var(--ydb-color-status-green);
--doughnut-backdrop-color: var(--g-color-base-positive-light);
--doughnut-width: 100px;
--doughnut-wrapper-indent: calc(var(--doughnut-border) + 5px);
--doughnut-color: var(--g-color-base-positive-heavy);
--doughnut-backdrop-color: var(--g-color-base-generic);
--doughnut-overlap-color: var(--g-color-base-positive-heavy-hover);
--doughnut-text-color: var(--g-color-text-positive-heavy);

&__doughnut {
position: relative;

width: 100px;
width: var(--doughnut-width);
aspect-ratio: 1;

border-radius: 50%;
mask: radial-gradient(circle at center, transparent 46%, #000 46.5%);

transform: rotate(180deg);
&::before {
display: block;

height: calc(100% - calc(var(--doughnut-border) * 2));
}

content: '';
// Size modifiers - using visually centered values
&__doughnut_size_small {
--doughnut-border: 12px;
--doughnut-width: 65px;
--doughnut-wrapper-indent: 15px;
}

border-radius: 50%;
background-color: var(--g-color-base-background);
&__doughnut_size_medium {
--doughnut-border: 16px;
--doughnut-width: 100px;
--doughnut-wrapper-indent: calc(var(--doughnut-border) + 5px);
}

transform: translate(var(--doughnut-border), var(--doughnut-border));
aspect-ratio: 1;
}
&__doughnut_size_large {
--doughnut-border: 20px;
--doughnut-width: 130px;
--doughnut-wrapper-indent: 25px;
}
&__doughnut_status_warning {
--doughnut-color: var(--ydb-color-status-yellow);
--doughnut-backdrop-color: var(--g-color-base-warning-light);

&_status_warning {
--doughnut-color: var(--g-color-base-warning-heavy);
--doughnut-overlap-color: var(--g-color-base-warning-heavy-hover);
--doughnut-text-color: var(--g-color-text-warning);
}
&__doughnut_status_danger {
--doughnut-color: var(--ydb-color-status-red);
--doughnut-backdrop-color: var(--g-color-base-danger-light);
&_status_danger {
--doughnut-color: var(--g-color-base-danger-heavy);
--doughnut-overlap-color: var(--g-color-base-danger-heavy-hover);
--doughnut-text-color: var(--g-color-base-danger-heavy);
}
&__text-wrapper {
--wrapper-indent: calc(var(--doughnut-border) + 5px);

position: absolute;
top: var(--wrapper-indent);
right: var(--wrapper-indent);
z-index: 1;
top: var(--doughnut-wrapper-indent);
left: var(--doughnut-wrapper-indent);

display: flex;
flex-direction: column;
justify-content: center;
align-items: center;

width: calc(100% - calc(var(--wrapper-indent) * 2));
width: calc(100% - calc(var(--doughnut-wrapper-indent) * 2));

text-align: center;

transform: rotate(180deg);
aspect-ratio: 1;
}

&__value {
position: absolute;
bottom: 20px;
color: var(--doughnut-text-color);
}

&__legend-note {
display: flex;
}
Expand Down
41 changes: 30 additions & 11 deletions src/components/DoughnutMetrics/DoughnutMetrics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import './DoughnutMetrics.scss';

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

const SizeContext = React.createContext<'small' | 'medium' | 'large'>('medium');

interface LegendProps {
children?: React.ReactNode;
variant?: TextProps['variant'];
Expand All @@ -31,9 +33,19 @@ function Legend({children, variant = 'subheader-3', color = 'primary', note}: Le
</Flex>
);
}
function Value({children, variant = 'subheader-2'}: LegendProps) {
function Value({children, variant}: LegendProps) {
const size = React.useContext(SizeContext);

const sizeVariantMap = {
small: 'subheader-1',
medium: 'subheader-2',
large: 'subheader-3',
} as const;

const finalVariant = variant || sizeVariantMap[size];

return (
<Text variant={variant} className={b('value')}>
<Text variant={finalVariant} className={b('value')}>
{children}
</Text>
);
Expand All @@ -44,9 +56,16 @@ interface DoughnutProps {
fillWidth: number;
children?: React.ReactNode;
className?: string;
size?: 'small' | 'medium' | 'large';
}

export function DoughnutMetrics({status, fillWidth, children, className}: DoughnutProps) {
export function DoughnutMetrics({
status,
fillWidth,
children,
className,
size = 'medium',
}: DoughnutProps) {
let filledDegrees = fillWidth * 3.6;
let doughnutFillVar = 'var(--doughnut-color)';
let doughnutBackdropVar = 'var(--doughnut-backdrop-color)';
Expand All @@ -57,17 +76,17 @@ export function DoughnutMetrics({status, fillWidth, children, className}: Doughn
doughnutFillVar = 'var(--doughnut-overlap-color)';
}

const doughnutStyle: React.CSSProperties = {
background: `conic-gradient(${doughnutFillVar} 0deg ${filledDegrees}deg, ${doughnutBackdropVar} ${filledDegrees}deg 360deg)`,
};

return (
<div className={b(null, className)}>
<div
style={{
background: `conic-gradient(${doughnutFillVar} 0deg ${filledDegrees}deg, ${doughnutBackdropVar} ${filledDegrees}deg 360deg)`,
}}
className={b('doughnut', {status})}
>
<SizeContext.Provider value={size}>
<div className={b({status}, className)} style={{position: 'relative'}}>
<div style={doughnutStyle} className={b('doughnut', {size})}></div>
<div className={b('text-wrapper')}>{children}</div>
</div>
</div>
</SizeContext.Provider>
);
}

Expand Down
12 changes: 5 additions & 7 deletions src/components/MetricChart/MetricChart.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@
display: flex;
flex-direction: column;

padding: var(--g-spacing-4) var(--g-spacing-4) var(--g-spacing-2);
width: 100%;
padding: 0;

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

&_noBorder {
padding: 0;

border: none;
&__toolbar {
margin-bottom: 10px;
}

&__chart {
Expand Down
49 changes: 27 additions & 22 deletions src/components/MetricChart/MetricChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import React from 'react';
import ChartKit, {settings} from '@gravity-ui/chartkit';
import type {YagrWidgetData} from '@gravity-ui/chartkit/yagr';
import {YagrPlugin} from '@gravity-ui/chartkit/yagr';
import {Flex} from '@gravity-ui/uikit';

import {cn} from '../../utils/cn';
import type {TimeFrame} from '../../utils/timeframes';
import {ResponseError} from '../Errors/ResponseError';
import {Loader} from '../Loader';
import {TimeFrameDropdown} from '../TimeFrameDropdown/TimeFrameDropdown';

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

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

// Constants
const DEFAULT_EFFECTIVE_WIDTH = 600; // Used for maxDataPoints calculation when using fullWidth

settings.set({plugins: [YagrPlugin]});

const prepareWidgetData = (
Expand Down Expand Up @@ -102,17 +107,18 @@ const emptyChartData: PreparedMetricsData = {timeline: [], metrics: []};

interface DiagnosticsChartProps {
database: string;

metrics: MetricDescription[];
timeFrame?: TimeFrame;

autorefresh?: number;
/** Default timeframe for uncontrolled usage */
defaultTimeFrame?: TimeFrame;

/** Callback for timeframe changes - required when using controlled mode */
onTimeFrameChange?: (timeFrame: TimeFrame) => void;

autorefresh?: number;
height?: number;
width?: number;

chartOptions?: ChartOptions;

onChartDataStatusChange?: OnChartDataStatusChange;

/**
Expand All @@ -122,33 +128,26 @@ interface DiagnosticsChartProps {
*/
isChartVisible?: boolean;

/** Remove border from chart */
noBorder?: boolean;

/** Make chart take full width of container */
fullWidth?: boolean;

/** Render custom toolbar content to the right of chart title */
renderChartToolbar?: () => React.ReactNode;
/** Chart title displayed in the toolbar */
title: string;
}

export const MetricChart = ({
database,
metrics,
timeFrame = '1h',
defaultTimeFrame = '1h',
autorefresh,
width = 400,
height = width / 1.5,
chartOptions,
onChartDataStatusChange,
isChartVisible,
noBorder,
fullWidth,
renderChartToolbar,
title,
}: DiagnosticsChartProps) => {
const [timeFrame, setTimeFrame] = React.useState<TimeFrame>(defaultTimeFrame);

// Use a reasonable default for maxDataPoints when fullWidth is true
const effectiveWidth = fullWidth ? 600 : width;
const maxDataPoints = effectiveWidth / 2;
const maxDataPoints = DEFAULT_EFFECTIVE_WIDTH / 2;

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

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

const renderToolbar = () => (
<Flex className={b('toolbar')} justifyContent="space-between" alignItems="center">
<div>{title}</div>
<TimeFrameDropdown value={timeFrame} onChange={setTimeFrame} />
</Flex>
);

const renderContent = () => {
if (loading) {
return <Loader />;
Expand All @@ -190,13 +196,12 @@ export const MetricChart = ({

return (
<div
className={b({noBorder})}
className={b()}
style={{
height,
width: fullWidth ? '100%' : width,
}}
>
{renderChartToolbar?.()}
{renderToolbar()}
{renderContent()}
</div>
);
Expand Down
Loading
Loading