[ML] Explain log rate spikes: Fix brush issues. (#138113)

- Fixes overlapping brush badges.
- Adds missing tooltips to brush badges.
This commit is contained in:
Walter Rafelsberger 2022-08-04 17:08:32 +02:00 committed by GitHub
parent fb3e7185df
commit 9512635a7d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 116 additions and 38 deletions

View file

@ -100,10 +100,10 @@ export function DualBrush({
const xMax = x(max) ?? 0;
const minExtentPx = Math.round((xMax - xMin) / 100);
const baselineBrush = d3.select('#brush-baseline');
const baselineBrush = d3.select('#aiops-brush-baseline');
const baselineSelection = d3.brushSelection(baselineBrush.node() as SVGGElement);
const deviationBrush = d3.select('#brush-deviation');
const deviationBrush = d3.select('#aiops-brush-deviation');
const deviationSelection = d3.brushSelection(deviationBrush.node() as SVGGElement);
if (!isBrushXSelection(deviationSelection) || !isBrushXSelection(baselineSelection)) {
@ -221,7 +221,7 @@ export function DualBrush({
.insert('g', '.brush')
.attr('class', 'brush')
.attr('id', (b: DualBrush) => {
return 'brush-' + b.id;
return 'aiops-brush-' + b.id;
})
.each((brushObject: DualBrush, i, n) => {
const x = d3.scaleLinear().domain([min, max]).rangeRound([0, widthRef.current]);

View file

@ -0,0 +1,64 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React, { FC } from 'react';
import { EuiBadge, EuiText, EuiToolTip } from '@elastic/eui';
// @ts-ignore
import { formatDate } from '@elastic/eui/lib/services/format';
const DATE_FORMAT = 'YYYY-MM-DD';
const TIME_FORMAT = 'HH:mm:ss';
interface BrushBadgeProps {
label: string;
marginLeft: number;
timestampFrom: number;
timestampTo: number;
width: number;
}
export const BrushBadge: FC<BrushBadgeProps> = ({
label,
marginLeft,
timestampFrom,
timestampTo,
width,
}) => {
// If "from" and "to" are on the same day, we skip displaying the date twice.
const dateFrom = formatDate(timestampFrom, DATE_FORMAT);
const dateTo = formatDate(timestampTo, DATE_FORMAT);
const timeFrom = formatDate(timestampFrom, TIME_FORMAT);
const timeTo = formatDate(timestampTo, TIME_FORMAT);
return (
<div
css={{
position: 'absolute',
'margin-left': `${marginLeft}px`,
}}
>
<EuiToolTip
content={
<EuiText size="xs">
{dateFrom} {timeFrom} -{' '}
{dateFrom !== dateTo && (
<>
<br />
{dateTo}{' '}
</>
)}
{timeTo}
</EuiText>
}
position="top"
>
<EuiBadge css={{ width, 'text-align': 'center' }}>{label}</EuiBadge>
</EuiToolTip>
</div>
);
};

View file

@ -20,10 +20,8 @@ import {
XYChartElementEvent,
XYBrushEvent,
} from '@elastic/charts';
import { EuiBadge } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { IUiSettingsClient } from '@kbn/core/public';
import { DualBrush, DualBrushAnnotation } from '@kbn/aiops-components';
import { getSnappedWindowParameters, getWindowParameters } from '@kbn/aiops-utils';
@ -33,6 +31,8 @@ import type { ChangePoint } from '@kbn/ml-agg-utils';
import { useAiOpsKibana } from '../../../kibana_context';
import { BrushBadge } from './brush_badge';
export interface DocumentCountChartPoint {
time: number | string;
value: number;
@ -52,6 +52,9 @@ interface DocumentCountChartProps {
const SPEC_ID = 'document_count';
const BADGE_HEIGHT = 20;
const BADGE_WIDTH = 75;
enum VIEW_MODE {
ZOOM = 'zoom',
BRUSH = 'brush',
@ -67,6 +70,19 @@ function getTimezone(uiSettings: IUiSettingsClient) {
}
}
function getBaselineBadgeOverflow(
windowParametersAsPixels: WindowParameters,
baselineBadgeWidth: number
) {
const { baselineMin, baselineMax, deviationMin } = windowParametersAsPixels;
const baselineBrushWidth = baselineMax - baselineMin;
const baselineBadgeActualMax = baselineMin + baselineBadgeWidth;
return deviationMin < baselineBadgeActualMax
? Math.max(0, baselineBadgeWidth - baselineBrushWidth)
: 0;
}
export const DocumentCountChart: FC<DocumentCountChartProps> = ({
brushSelectionUpdateHandler,
width,
@ -239,47 +255,45 @@ export const DocumentCountChart: FC<DocumentCountChartProps> = ({
}, [viewMode]);
const isBrushVisible =
originalWindowParameters && mlBrushMarginLeft && mlBrushWidth && mlBrushWidth > 0;
originalWindowParameters &&
windowParameters &&
mlBrushMarginLeft &&
mlBrushWidth &&
mlBrushWidth > 0;
// Avoid overlap of brush badges when the brushes are quite narrow.
const baselineBadgeOverflow = windowParametersAsPixels
? getBaselineBadgeOverflow(windowParametersAsPixels, BADGE_WIDTH)
: 0;
const baselineBadgeMarginLeft =
(mlBrushMarginLeft ?? 0) + (windowParametersAsPixels?.baselineMin ?? 0);
return (
<>
{isBrushVisible && (
<div className="aiopsHistogramBrushes">
<div
css={{
position: 'absolute',
'margin-left': `${
mlBrushMarginLeft + (windowParametersAsPixels?.baselineMin ?? 0)
}px`,
}}
>
<EuiBadge>
<FormattedMessage
id="xpack.aiops.documentCountChart.baselineBadgeContent"
defaultMessage="Baseline"
/>
</EuiBadge>
<div css={{ height: BADGE_HEIGHT }}>
<BrushBadge
label={i18n.translate('xpack.aiops.documentCountChart.baselineBadgeLabel', {
defaultMessage: 'Baseline',
})}
marginLeft={baselineBadgeMarginLeft - baselineBadgeOverflow}
timestampFrom={windowParameters.baselineMin}
timestampTo={windowParameters.baselineMax}
width={BADGE_WIDTH}
/>
<BrushBadge
label={i18n.translate('xpack.aiops.documentCountChart.deviationBadgeLabel', {
defaultMessage: 'Deviation',
})}
marginLeft={mlBrushMarginLeft + (windowParametersAsPixels?.deviationMin ?? 0)}
timestampFrom={windowParameters.deviationMin}
timestampTo={windowParameters.deviationMax}
width={BADGE_WIDTH}
/>
</div>
<div
css={{
position: 'absolute',
'margin-left': `${
mlBrushMarginLeft + (windowParametersAsPixels?.deviationMin ?? 0)
}px`,
}}
>
<EuiBadge>
<FormattedMessage
id="xpack.aiops.documentCountChart.deviationBadgeContent"
defaultMessage="Deviation"
/>
</EuiBadge>
</div>
<div
css={{
position: 'relative',
clear: 'both',
'padding-top': '20px',
'margin-bottom': '-4px',
}}
>