mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
# Backport This will backport the following commits from `main` to `8.x`: - [[ML] Remove legacy scss overwrites Single Metric Viewer (#195259)](https://github.com/elastic/kibana/pull/195259) <!--- Backport version: 9.4.3 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Robert Jaszczurek","email":"92210485+rbrtj@users.noreply.github.com"},"sourceCommit":{"committedDate":"2024-10-14T10:14:37Z","message":"[ML] Remove legacy scss overwrites Single Metric Viewer (#195259)\n\n## Summary\r\n\r\nRemoves SCSS files for the Single Metric Viewer and adds BEM classes for\r\n`annotations`.\r\nAffects the Single Metric Viewer in ML and the embeddable.\r\nPart of [#140695](https://github.com/elastic/kibana/issues/140695)","sha":"87c91f4e258db1910e13daec7e8267d1110735dd","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":[":ml","release_note:skip","v9.0.0","Team:ML","v8.16.0","backport:version"],"title":"[ML] Remove legacy scss overwrites Single Metric Viewer","number":195259,"url":"https://github.com/elastic/kibana/pull/195259","mergeCommit":{"message":"[ML] Remove legacy scss overwrites Single Metric Viewer (#195259)\n\n## Summary\r\n\r\nRemoves SCSS files for the Single Metric Viewer and adds BEM classes for\r\n`annotations`.\r\nAffects the Single Metric Viewer in ML and the embeddable.\r\nPart of [#140695](https://github.com/elastic/kibana/issues/140695)","sha":"87c91f4e258db1910e13daec7e8267d1110735dd"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/195259","number":195259,"mergeCommit":{"message":"[ML] Remove legacy scss overwrites Single Metric Viewer (#195259)\n\n## Summary\r\n\r\nRemoves SCSS files for the Single Metric Viewer and adds BEM classes for\r\n`annotations`.\r\nAffects the Single Metric Viewer in ML and the embeddable.\r\nPart of [#140695](https://github.com/elastic/kibana/issues/140695)","sha":"87c91f4e258db1910e13daec7e8267d1110735dd"}},{"branch":"8.x","label":"v8.16.0","branchLabelMappingKey":"^v8.16.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: Robert Jaszczurek <92210485+rbrtj@users.noreply.github.com>
This commit is contained in:
parent
1c98c7acce
commit
7a80e6f1a7
14 changed files with 403 additions and 417 deletions
|
@ -7,7 +7,6 @@
|
|||
// Sub applications
|
||||
@import 'data_frame_analytics/index';
|
||||
@import 'explorer/index'; // SASSTODO: This file needs to be rewritten
|
||||
@import 'timeseriesexplorer/index';
|
||||
|
||||
// Components
|
||||
@import 'components/annotations/annotation_description_list/index'; // SASSTODO: This file overwrites EUI directly
|
||||
|
|
|
@ -139,7 +139,7 @@ export const AnnotationTimeline = <T extends { timestamp: number; end_timestamp?
|
|||
: startingXPos;
|
||||
svg
|
||||
.append('rect')
|
||||
.classed('mlAnnotationRect', true)
|
||||
.classed('ml-annotation__rect', true)
|
||||
// If annotation is at the end, prevent overflow by shifting it back
|
||||
.attr('x', xPos + annotationWidth >= endingXPos ? endingXPos - annotationWidth : xPos)
|
||||
.attr('y', 0)
|
||||
|
|
|
@ -15,6 +15,7 @@ import { useCurrentThemeVars } from '../contexts/kibana';
|
|||
import type { Annotation, AnnotationsTable } from '../../../common/types/annotations';
|
||||
import type { ChartTooltipService } from '../components/chart_tooltip';
|
||||
import { Y_AXIS_LABEL_PADDING, Y_AXIS_LABEL_WIDTH } from './constants';
|
||||
import { getAnnotationStyles } from '../timeseriesexplorer/styles';
|
||||
|
||||
const ANNOTATION_CONTAINER_HEIGHT = 12;
|
||||
const ANNOTATION_MIN_WIDTH = 8;
|
||||
|
@ -29,6 +30,8 @@ interface SwimlaneAnnotationContainerProps {
|
|||
tooltipService: ChartTooltipService;
|
||||
}
|
||||
|
||||
const annotationStyles = getAnnotationStyles();
|
||||
|
||||
export const SwimlaneAnnotationContainer: FC<SwimlaneAnnotationContainerProps> = ({
|
||||
chartWidth,
|
||||
domain,
|
||||
|
@ -135,7 +138,7 @@ export const SwimlaneAnnotationContainer: FC<SwimlaneAnnotationContainerProps> =
|
|||
const xPos = d.start >= domain.min ? (xScale(d.start) as number) : startingXPos;
|
||||
svg
|
||||
.append('rect')
|
||||
.classed('mlAnnotationRect', true)
|
||||
.classed('ml-annotation__rect', true)
|
||||
// If annotation is at the end, prevent overflow by shifting it back
|
||||
.attr('x', xPos + annotationWidth >= endingXPos ? endingXPos - annotationWidth : xPos)
|
||||
.attr('y', 0)
|
||||
|
@ -221,5 +224,5 @@ export const SwimlaneAnnotationContainer: FC<SwimlaneAnnotationContainerProps> =
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [chartWidth, domain, annotationsData, tooltipService]);
|
||||
|
||||
return <div ref={canvasRef} />;
|
||||
return <div css={annotationStyles} ref={canvasRef} />;
|
||||
};
|
||||
|
|
17
x-pack/plugins/ml/public/application/styles.ts
Normal file
17
x-pack/plugins/ml/public/application/styles.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Replacement for ./_variables.scss as we aim to remove the scss files
|
||||
|
||||
export const mlColors = {
|
||||
critical: '#FE5050',
|
||||
major: '#FBA740',
|
||||
minor: '#FDEC25',
|
||||
warning: '#8BC8FB',
|
||||
lowWarning: '#D2E9F7',
|
||||
unknown: '#C0C0C0',
|
||||
};
|
|
@ -1,2 +0,0 @@
|
|||
@import 'timeseriesexplorer';
|
||||
@import 'timeseriesexplorer_annotations';
|
|
@ -1,267 +0,0 @@
|
|||
// stylelint-disable selector-no-qualifying-type
|
||||
.ml-time-series-explorer {
|
||||
color: $euiColorDarkShade;
|
||||
|
||||
.forecast-controls {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.ml-timeseries-chart {
|
||||
svg {
|
||||
font-size: $euiFontSizeXS;
|
||||
font-family: $euiFontFamily;
|
||||
}
|
||||
|
||||
.axis path,
|
||||
.axis line {
|
||||
fill: none;
|
||||
stroke: $euiBorderColor;
|
||||
shape-rendering: crispEdges;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.axis text {
|
||||
fill: $euiTextColor;
|
||||
}
|
||||
|
||||
.axis .tick line {
|
||||
stroke: $euiColorLightShade;
|
||||
}
|
||||
|
||||
.chart-border {
|
||||
stroke: $euiBorderColor;
|
||||
fill: none;
|
||||
stroke-width: 1;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.chart-border-highlight {
|
||||
stroke: $euiColorDarkShade;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.chart-border-highlight:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.area {
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.area.bounds {
|
||||
fill: transparentize($euiColorPrimary, .8);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.values-line {
|
||||
fill: none;
|
||||
stroke: $euiColorPrimary;
|
||||
stroke-width: 2;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.values-line.forecast {
|
||||
stroke: $euiColorVis5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.area.forecast {
|
||||
fill: transparentize($euiColorVis5, .7);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.values-dots circle {
|
||||
fill: $euiColorPrimary;
|
||||
stroke-width: 0;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
opacity: 1;
|
||||
fill: transparent;
|
||||
stroke: $euiColorPrimary;
|
||||
stroke-width: 0;
|
||||
}
|
||||
|
||||
.anomaly-marker {
|
||||
stroke-width: 1px;
|
||||
stroke: $euiColorMediumShade;
|
||||
}
|
||||
|
||||
.anomaly-marker.critical {
|
||||
fill: $mlColorCritical;
|
||||
}
|
||||
|
||||
.anomaly-marker.major {
|
||||
fill: $mlColorMajor;
|
||||
}
|
||||
|
||||
.anomaly-marker.minor {
|
||||
fill: $mlColorMinor;
|
||||
}
|
||||
|
||||
.anomaly-marker.warning {
|
||||
fill: $mlColorWarning;
|
||||
}
|
||||
|
||||
.anomaly-marker.low {
|
||||
fill: $mlColorLowWarning;
|
||||
}
|
||||
|
||||
.metric-value:hover,
|
||||
.anomaly-marker:hover,
|
||||
.anomaly-marker.highlighted {
|
||||
stroke-width: 6px;
|
||||
stroke-opacity: .65;
|
||||
stroke: $euiColorPrimary;
|
||||
}
|
||||
|
||||
rect.scheduled-event-marker {
|
||||
stroke-width: 1px;
|
||||
stroke: $euiColorDarkShade;
|
||||
fill: $euiColorLightShade;
|
||||
}
|
||||
|
||||
.forecast {
|
||||
.metric-value,
|
||||
.metric-value:hover {
|
||||
stroke: $euiColorVis5;
|
||||
}
|
||||
}
|
||||
|
||||
.focus-chart {
|
||||
.x-axis-background {
|
||||
line {
|
||||
fill: none;
|
||||
shape-rendering: crispEdges;
|
||||
stroke: $euiColorLightestShade;
|
||||
}
|
||||
|
||||
rect {
|
||||
fill: $euiColorLightestShade;
|
||||
}
|
||||
}
|
||||
|
||||
.focus-zoom {
|
||||
fill: $euiColorDarkShade;
|
||||
|
||||
a {
|
||||
text {
|
||||
fill: $euiColorPrimary;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
a:hover,
|
||||
a:active,
|
||||
a:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.context-chart {
|
||||
.x.axis path {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.axis text {
|
||||
font-size: 10px;
|
||||
fill: $euiTextColor;
|
||||
}
|
||||
|
||||
.values-line {
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.mask {
|
||||
polygon {
|
||||
fill-opacity: .1;
|
||||
}
|
||||
|
||||
.area.bounds {
|
||||
fill: $euiColorLightShade;
|
||||
}
|
||||
|
||||
.values-line {
|
||||
stroke-width: 1;
|
||||
stroke: $euiColorMediumShade;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.swimlane .axis text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.swimlane rect.swimlane-cell-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.brush .extent {
|
||||
fill-opacity: 0;
|
||||
shape-rendering: crispEdges;
|
||||
stroke: $euiColorDarkShade;
|
||||
stroke-width: 2;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.brush .extent:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.top-border {
|
||||
fill: $euiColorEmptyShade;
|
||||
}
|
||||
|
||||
foreignObject.brush-handle {
|
||||
pointer-events: none;
|
||||
padding-top: 1px;
|
||||
}
|
||||
|
||||
div.brush-handle-inner {
|
||||
border: 1px solid $euiColorDarkShade;
|
||||
background-color: $euiColorLightShade;
|
||||
height: 70px;
|
||||
width: 10px;
|
||||
text-align: center;
|
||||
cursor: ew-resize;
|
||||
margin-top: 9px;
|
||||
font-size: $euiFontSizeS;
|
||||
fill: $euiColorDarkShade;
|
||||
}
|
||||
|
||||
div.brush-handle-inner-left {
|
||||
border-radius: $euiBorderRadius 0 0 $euiBorderRadius;
|
||||
}
|
||||
|
||||
div.brush-handle-inner-right {
|
||||
border-radius: 0 $euiBorderRadius $euiBorderRadius 0;
|
||||
}
|
||||
|
||||
rect.brush-handle {
|
||||
stroke-width: 1;
|
||||
stroke: $euiColorDarkShade;
|
||||
fill: $euiColorLightShade;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
rect.brush-handle:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Hides the progress bar's background so it doesn't look like it increases the thickness
|
||||
of the horizontal bar below the tab menu elements in its inactive state. */
|
||||
.mlTimeSeriesExplorerProgress {
|
||||
background-color: $euiColorEmptyShade;
|
||||
|
||||
&::-moz-progress-bar,
|
||||
&::-webkit-progress-bar {
|
||||
background-color: $euiColorEmptyShade;
|
||||
}
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
// SASS TODO: This uses non-BEM styles to be in line with the existing
|
||||
// legacy Time Series Viewer style. Where applicable it tries to avoid
|
||||
// overrides. The one override with `.extent` is because of d3.
|
||||
|
||||
$mlAnnotationBorderWidth: 2px;
|
||||
|
||||
// Replicates $euiBorderEditable for SVG
|
||||
.mlAnnotationBrush .extent {
|
||||
stroke: $euiColorLightShade;
|
||||
stroke-width: $mlAnnotationBorderWidth;
|
||||
stroke-dasharray: 2 2;
|
||||
fill: $euiColorLightestShade;
|
||||
shape-rendering: geometricPrecision;
|
||||
}
|
||||
|
||||
// Instead of different EUI colors we use opacity settings
|
||||
// here to avoid opaque layers on top of existing chart elements.
|
||||
$mlAnnotationRectDefaultStrokeOpacity: .2;
|
||||
$mlAnnotationRectDefaultFillOpacity: .05;
|
||||
|
||||
.mlAnnotationRect {
|
||||
stroke: $euiColorFullShade;
|
||||
stroke-width: $mlAnnotationBorderWidth;
|
||||
stroke-opacity: $mlAnnotationRectDefaultStrokeOpacity;
|
||||
transition: stroke-opacity $euiAnimSpeedFast;
|
||||
|
||||
fill: $euiColorFullShade;
|
||||
fill-opacity: $mlAnnotationRectDefaultFillOpacity;
|
||||
transition: fill-opacity $euiAnimSpeedFast;
|
||||
|
||||
shape-rendering: geometricPrecision;
|
||||
}
|
||||
|
||||
.mlAnnotationRect-isHighlight {
|
||||
stroke-opacity: $mlAnnotationRectDefaultStrokeOpacity * 2;
|
||||
transition: stroke-opacity $euiAnimSpeedFast;
|
||||
|
||||
fill-opacity: $mlAnnotationRectDefaultFillOpacity * 2;
|
||||
transition: fill-opacity $euiAnimSpeedFast;
|
||||
}
|
||||
|
||||
.mlAnnotationRect-isBlur {
|
||||
stroke-opacity: calc($mlAnnotationRectDefaultStrokeOpacity / 2);
|
||||
transition: stroke-opacity $euiAnimSpeedFast;
|
||||
|
||||
fill-opacity: calc($mlAnnotationRectDefaultFillOpacity / 2);
|
||||
transition: fill-opacity $euiAnimSpeedFast;
|
||||
}
|
||||
|
||||
// Replace the EuiBadge text style for SVG
|
||||
.mlAnnotationText {
|
||||
text-anchor: middle;
|
||||
font-size: $euiFontSizeXS;
|
||||
font-family: $euiFontFamily;
|
||||
font-weight: $euiFontWeightMedium;
|
||||
|
||||
fill: $euiColorFullShade;
|
||||
transition: fill $euiAnimSpeedFast;
|
||||
|
||||
user-select: none;
|
||||
|
||||
}
|
||||
|
||||
.mlAnnotationText-isBlur {
|
||||
fill: $euiColorMediumShade;
|
||||
transition: fill $euiAnimSpeedFast;
|
||||
}
|
||||
|
||||
.mlAnnotationTextRect {
|
||||
fill: $euiColorLightShade;
|
||||
transition: fill $euiAnimSpeedFast;
|
||||
}
|
||||
|
||||
.mlAnnotationTextRect-isBlur {
|
||||
fill: $euiColorLightestShade;
|
||||
transition: fill $euiAnimSpeedFast;
|
||||
}
|
||||
|
||||
.mlAnnotationHidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// context annotation marker
|
||||
.mlContextAnnotationRect {
|
||||
stroke: $euiColorFullShade;
|
||||
stroke-width: $mlAnnotationBorderWidth;
|
||||
stroke-opacity: $mlAnnotationRectDefaultStrokeOpacity;
|
||||
transition: stroke-opacity $euiAnimSpeedFast;
|
||||
|
||||
fill: $euiColorFullShade;
|
||||
fill-opacity: $mlAnnotationRectDefaultFillOpacity;
|
||||
transition: fill-opacity $euiAnimSpeedFast;
|
||||
|
||||
shape-rendering: geometricPrecision;
|
||||
}
|
||||
|
||||
.mlContextAnnotationRect-isBlur {
|
||||
stroke-opacity: calc($mlAnnotationRectDefaultStrokeOpacity / 2);
|
||||
transition: stroke-opacity $euiAnimSpeedFast;
|
||||
|
||||
fill-opacity: calc($mlAnnotationRectDefaultFillOpacity / 2);
|
||||
transition: fill-opacity $euiAnimSpeedFast;
|
||||
}
|
|
@ -307,7 +307,7 @@ class TimeseriesChartIntl extends Component {
|
|||
|
||||
if (this.props.annotation === null) {
|
||||
const chartElement = d3.select(this.rootNode);
|
||||
chartElement.select('g.mlAnnotationBrush').call(this.annotateBrush.extent([0, 0]));
|
||||
chartElement.select('g.ml-annotation__brush').call(this.annotateBrush.extent([0, 0]));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -560,7 +560,7 @@ class TimeseriesChartIntl extends Component {
|
|||
|
||||
fcsGroup
|
||||
.append('g')
|
||||
.attr('class', 'mlAnnotationBrush')
|
||||
.attr('class', 'ml-annotation__brush')
|
||||
.call(annotateBrush)
|
||||
.selectAll('rect')
|
||||
.attr('x', brushX)
|
||||
|
@ -568,7 +568,7 @@ class TimeseriesChartIntl extends Component {
|
|||
.attr('width', brushWidth)
|
||||
.attr('height', focusChartIncoming ?? focusChartHeight);
|
||||
|
||||
fcsGroup.append('g').classed('mlAnnotations', true);
|
||||
fcsGroup.append('g').classed('ml-annotations', true);
|
||||
|
||||
// Add border round plot area.
|
||||
fcsGroup
|
||||
|
@ -828,7 +828,7 @@ class TimeseriesChartIntl extends Component {
|
|||
|
||||
// disable brushing (creation of annotations) when annotations aren't shown or when in embeddable mode
|
||||
focusChart
|
||||
.select('.mlAnnotationBrush')
|
||||
.select('.ml-annotation__brush')
|
||||
.style('display', !showAnnotations || embeddableMode ? 'none' : null);
|
||||
|
||||
focusChart.select('.values-line').attr('d', this.focusValuesLine(data));
|
||||
|
@ -1245,18 +1245,18 @@ class TimeseriesChartIntl extends Component {
|
|||
drawLineChartDots(data, cxtGroup, contextValuesLine, 1);
|
||||
|
||||
// Add annotation markers to the context area
|
||||
cxtGroup.append('g').classed('mlContextAnnotations', true);
|
||||
cxtGroup.append('g').classed('ml-annotation__context', true);
|
||||
|
||||
const [contextXRangeStart, contextXRangeEnd] = this.contextXScale.range();
|
||||
const ctxAnnotations = cxtGroup
|
||||
.select('.mlContextAnnotations')
|
||||
.selectAll('g.mlContextAnnotation')
|
||||
.select('.ml-annotation__context')
|
||||
.selectAll('g.ml-annotation__context-item')
|
||||
.data(mergedAnnotations, (d) => `${d.start}-${d.end}` || '');
|
||||
|
||||
ctxAnnotations.enter().append('g').classed('mlContextAnnotation', true);
|
||||
ctxAnnotations.enter().append('g').classed('ml-annotation__context-item', true);
|
||||
|
||||
const ctxAnnotationRects = ctxAnnotations
|
||||
.selectAll('.mlContextAnnotationRect')
|
||||
.selectAll('.ml-annotation__context-rect')
|
||||
.data((d) => [d]);
|
||||
|
||||
ctxAnnotationRects
|
||||
|
@ -1266,7 +1266,7 @@ class TimeseriesChartIntl extends Component {
|
|||
showFocusChartTooltip(d.annotations.length === 1 ? d.annotations[0] : d, this);
|
||||
})
|
||||
.on('mouseout', () => hideFocusChartTooltip())
|
||||
.classed('mlContextAnnotationRect', true);
|
||||
.classed('ml-annotation__context-rect', true);
|
||||
|
||||
ctxAnnotationRects
|
||||
.attr('x', (item) => {
|
||||
|
|
|
@ -145,18 +145,18 @@ export function renderAnnotations(
|
|||
};
|
||||
|
||||
const annotations = focusChart
|
||||
.select('.mlAnnotations')
|
||||
.selectAll('g.mlAnnotation')
|
||||
.select('.ml-annotations')
|
||||
.selectAll('g.ml-annotation')
|
||||
.data(focusAnnotationData || [], (d: Annotation) => d._id || '');
|
||||
|
||||
annotations.enter().append('g').classed('mlAnnotation', true);
|
||||
annotations.enter().append('g').classed('ml-annotation', true);
|
||||
|
||||
const rects = annotations.selectAll('.mlAnnotationRect').data((d: Annotation) => [d]);
|
||||
const rects = annotations.selectAll('.ml-annotation__rect').data((d: Annotation) => [d]);
|
||||
|
||||
rects
|
||||
.enter()
|
||||
.append('rect')
|
||||
.classed('mlAnnotationRect', true)
|
||||
.classed('ml-annotation__rect', true)
|
||||
.attr('mask', `url(#${ANNOTATION_MASK_ID})`)
|
||||
.on('mouseover', onAnnotationMouseOver)
|
||||
.on('mouseout', hideFocusChartTooltip)
|
||||
|
@ -187,13 +187,13 @@ export function renderAnnotations(
|
|||
|
||||
rects.exit().remove();
|
||||
|
||||
const textRects = annotations.selectAll('.mlAnnotationTextRect').data((d) => [d]);
|
||||
const texts = annotations.selectAll('.mlAnnotationText').data((d) => [d]);
|
||||
const textRects = annotations.selectAll('.ml-annotation__text-rect').data((d) => [d]);
|
||||
const texts = annotations.selectAll('.ml-annotation__text').data((d) => [d]);
|
||||
|
||||
textRects
|
||||
.enter()
|
||||
.append('rect')
|
||||
.classed('mlAnnotationTextRect', true)
|
||||
.classed('ml-annotation__text-rect', true)
|
||||
.attr('width', ANNOTATION_TEXT_RECT_WIDTH)
|
||||
.attr('height', ANNOTATION_TEXT_RECT_HEIGHT)
|
||||
.on('mouseover', onAnnotationMouseOver)
|
||||
|
@ -203,7 +203,7 @@ export function renderAnnotations(
|
|||
texts
|
||||
.enter()
|
||||
.append('text')
|
||||
.classed('mlAnnotationText', true)
|
||||
.classed('ml-annotation__text', true)
|
||||
.on('mouseover', onAnnotationMouseOver)
|
||||
.on('mouseout', hideFocusChartTooltip)
|
||||
.on('click', onAnnotationClick);
|
||||
|
@ -253,7 +253,7 @@ export function renderAnnotations(
|
|||
textRects.exit().remove();
|
||||
texts.exit().remove();
|
||||
|
||||
annotations.classed('mlAnnotationHidden', !showAnnotations);
|
||||
annotations.classed('ml-annotation--hidden', !showAnnotations);
|
||||
annotations.exit().remove();
|
||||
}
|
||||
|
||||
|
@ -271,34 +271,36 @@ export function getAnnotationWidth(
|
|||
}
|
||||
|
||||
export function highlightFocusChartAnnotation(annotation: Annotation) {
|
||||
const annotations = d3.selectAll('.mlAnnotation');
|
||||
const annotations = d3.selectAll('.ml-annotation');
|
||||
|
||||
annotations.each(function (d) {
|
||||
// @ts-ignore
|
||||
const element = d3.select(this);
|
||||
|
||||
if (d._id === annotation._id) {
|
||||
element.selectAll('.mlAnnotationRect').classed('mlAnnotationRect-isHighlight', true);
|
||||
element.selectAll('.ml-annotation__rect').classed('ml-annotation__rect--highlight', true);
|
||||
} else {
|
||||
element.selectAll('.mlAnnotationTextRect').classed('mlAnnotationTextRect-isBlur', true);
|
||||
element.selectAll('.mlAnnotationText').classed('mlAnnotationText-isBlur', true);
|
||||
element.selectAll('.mlAnnotationRect').classed('mlAnnotationRect-isBlur', true);
|
||||
element
|
||||
.selectAll('.ml-annotation__text-rect')
|
||||
.classed('ml-annotation__text-rect--blur', true);
|
||||
element.selectAll('.ml-annotation__text').classed('ml-annotation__text--blur', true);
|
||||
element.selectAll('.ml-annotation__rect').classed('ml-annotation__rect--blur', true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function unhighlightFocusChartAnnotation() {
|
||||
const annotations = d3.selectAll('.mlAnnotation');
|
||||
const annotations = d3.selectAll('.ml-annotation');
|
||||
|
||||
annotations.each(function () {
|
||||
// @ts-ignore
|
||||
const element = d3.select(this);
|
||||
|
||||
element.selectAll('.mlAnnotationTextRect').classed('mlAnnotationTextRect-isBlur', false);
|
||||
element.selectAll('.ml-annotation__text-rect').classed('ml-annotation__text-rect--blur', false);
|
||||
element
|
||||
.selectAll('.mlAnnotationRect')
|
||||
.classed('mlAnnotationRect-isHighlight', false)
|
||||
.classed('mlAnnotationRect-isBlur', false);
|
||||
element.selectAll('.mlAnnotationText').classed('mlAnnotationText-isBlur', false);
|
||||
.selectAll('.ml-annotation__rect')
|
||||
.classed('ml-annotation__rect--highlight', false)
|
||||
.classed('ml-annotation__rect--blur', false);
|
||||
element.selectAll('.ml-annotation__text').classed('ml-annotation__text--blur', false);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,332 @@
|
|||
/*
|
||||
* 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 { css } from '@emotion/react';
|
||||
import { euiThemeVars } from '@kbn/ui-theme';
|
||||
import { transparentize } from '@elastic/eui';
|
||||
import { mlColors } from '../styles';
|
||||
|
||||
// Annotations constants
|
||||
const mlAnnotationBorderWidth = '2px';
|
||||
const mlAnnotationRectDefaultStrokeOpacity = 0.2;
|
||||
const mlAnnotationRectDefaultFillOpacity = 0.05;
|
||||
|
||||
export const getTimeseriesExplorerStyles = () =>
|
||||
css({
|
||||
color: euiThemeVars.euiColorDarkShade,
|
||||
|
||||
'.ml-timeseries-chart': {
|
||||
svg: {
|
||||
fontSize: euiThemeVars.euiFontSizeXS,
|
||||
fontFamily: euiThemeVars.euiFontFamily,
|
||||
},
|
||||
|
||||
'.axis': {
|
||||
'path, line': {
|
||||
fill: 'none',
|
||||
stroke: euiThemeVars.euiBorderColor,
|
||||
shapeRendering: 'crispEdges',
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
|
||||
text: {
|
||||
fill: euiThemeVars.euiTextColor,
|
||||
},
|
||||
|
||||
'.tick line': {
|
||||
stroke: euiThemeVars.euiColorLightShade,
|
||||
},
|
||||
},
|
||||
|
||||
'.chart-border': {
|
||||
stroke: euiThemeVars.euiBorderColor,
|
||||
fill: 'none',
|
||||
strokeWidth: 1,
|
||||
shapeRendering: 'crispEdges',
|
||||
},
|
||||
|
||||
'.chart-border-highlight': {
|
||||
stroke: euiThemeVars.euiColorDarkShade,
|
||||
strokeWidth: 2,
|
||||
|
||||
'&:hover': {
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
|
||||
'.area': {
|
||||
strokeWidth: 1,
|
||||
|
||||
'&.bounds': {
|
||||
fill: transparentize(euiThemeVars.euiColorPrimary, 0.2),
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
|
||||
'&.forecast': {
|
||||
fill: transparentize(euiThemeVars.euiColorVis5, 0.3),
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
},
|
||||
|
||||
'.values-line': {
|
||||
fill: 'none',
|
||||
stroke: euiThemeVars.euiColorPrimary,
|
||||
strokeWidth: 2,
|
||||
pointerEvents: 'none',
|
||||
|
||||
'&.forecast': {
|
||||
stroke: euiThemeVars.euiColorVis5,
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
},
|
||||
|
||||
'.hidden': {
|
||||
visibility: 'hidden',
|
||||
},
|
||||
|
||||
'.values-dots circle': {
|
||||
fill: euiThemeVars.euiColorPrimary,
|
||||
strokeWidth: 0,
|
||||
},
|
||||
|
||||
'.metric-value': {
|
||||
opacity: 1,
|
||||
fill: 'transparent',
|
||||
stroke: euiThemeVars.euiColorPrimary,
|
||||
strokeWidth: 0,
|
||||
},
|
||||
|
||||
'.anomaly-marker': {
|
||||
strokeWidth: 1,
|
||||
stroke: euiThemeVars.euiColorMediumShade,
|
||||
|
||||
'&.critical': {
|
||||
fill: mlColors.critical,
|
||||
},
|
||||
|
||||
'&.major': {
|
||||
fill: mlColors.major,
|
||||
},
|
||||
|
||||
'&.minor': {
|
||||
fill: mlColors.minor,
|
||||
},
|
||||
|
||||
'&.warning': {
|
||||
fill: mlColors.warning,
|
||||
},
|
||||
|
||||
'&.low': {
|
||||
fill: mlColors.lowWarning,
|
||||
},
|
||||
},
|
||||
|
||||
'.metric-value:hover, .anomaly-marker:hover, .anomaly-marker.highlighted': {
|
||||
strokeWidth: 6,
|
||||
strokeOpacity: 0.65,
|
||||
stroke: euiThemeVars.euiColorPrimary,
|
||||
},
|
||||
|
||||
'rect.scheduled-event-marker': {
|
||||
strokeWidth: 1,
|
||||
stroke: euiThemeVars.euiColorDarkShade,
|
||||
fill: euiThemeVars.euiColorLightShade,
|
||||
},
|
||||
|
||||
'.forecast': {
|
||||
'.metric-value, .metric-value:hover': {
|
||||
stroke: euiThemeVars.euiColorVis5,
|
||||
},
|
||||
},
|
||||
|
||||
'.focus-chart': {
|
||||
'.x-axis-background': {
|
||||
line: {
|
||||
fill: 'none',
|
||||
shapeRendering: 'crispEdges',
|
||||
stroke: euiThemeVars.euiColorLightestShade,
|
||||
},
|
||||
rect: {
|
||||
fill: euiThemeVars.euiColorLightestShade,
|
||||
},
|
||||
},
|
||||
'.focus-zoom': {
|
||||
fill: euiThemeVars.euiColorDarkShade,
|
||||
a: {
|
||||
text: {
|
||||
fill: euiThemeVars.euiColorPrimary,
|
||||
cursor: 'pointer',
|
||||
},
|
||||
'&:hover, &:active, &:focus': {
|
||||
textDecoration: 'underline',
|
||||
fill: euiThemeVars.euiColorPrimary,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
'.context-chart': {
|
||||
'.x.axis path': {
|
||||
display: 'none',
|
||||
},
|
||||
'.axis text': {
|
||||
fontSize: '10px',
|
||||
fill: euiThemeVars.euiTextColor,
|
||||
},
|
||||
'.values-line': {
|
||||
strokeWidth: 1,
|
||||
},
|
||||
'.mask': {
|
||||
polygon: {
|
||||
fillOpacity: 0.1,
|
||||
},
|
||||
'.area.bounds': {
|
||||
fill: euiThemeVars.euiColorLightShade,
|
||||
},
|
||||
'.values-line': {
|
||||
strokeWidth: 1,
|
||||
stroke: euiThemeVars.euiColorMediumShade,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
'.swimlane .axis text': {
|
||||
display: 'none',
|
||||
},
|
||||
|
||||
'.swimlane rect.swimlane-cell-hidden': {
|
||||
display: 'none',
|
||||
},
|
||||
|
||||
'.brush .extent': {
|
||||
fillOpacity: 0,
|
||||
shapeRendering: 'crispEdges',
|
||||
stroke: euiThemeVars.euiColorDarkShade,
|
||||
strokeWidth: 2,
|
||||
cursor: 'move',
|
||||
'&:hover': {
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
|
||||
'.top-border': {
|
||||
fill: euiThemeVars.euiColorEmptyShade,
|
||||
},
|
||||
|
||||
'foreignObject.brush-handle': {
|
||||
pointerEvents: 'none',
|
||||
paddingTop: '1px',
|
||||
},
|
||||
|
||||
'div.brush-handle-inner': {
|
||||
border: `1px solid ${euiThemeVars.euiColorDarkShade}`,
|
||||
backgroundColor: euiThemeVars.euiColorLightShade,
|
||||
height: '70px',
|
||||
width: '10px',
|
||||
textAlign: 'center',
|
||||
cursor: 'ew-resize',
|
||||
marginTop: '9px',
|
||||
fontSize: euiThemeVars.euiFontSizeS,
|
||||
fill: euiThemeVars.euiColorDarkShade,
|
||||
},
|
||||
|
||||
'div.brush-handle-inner-left': {
|
||||
borderRadius: `${euiThemeVars.euiBorderRadius} 0 0 ${euiThemeVars.euiBorderRadius}`,
|
||||
},
|
||||
|
||||
'div.brush-handle-inner-right': {
|
||||
borderRadius: `0 ${euiThemeVars.euiBorderRadius} ${euiThemeVars.euiBorderRadius} 0`,
|
||||
},
|
||||
|
||||
'rect.brush-handle': {
|
||||
strokeWidth: 1,
|
||||
stroke: euiThemeVars.euiColorDarkShade,
|
||||
fill: euiThemeVars.euiColorLightShade,
|
||||
pointerEvents: 'none',
|
||||
'&:hover': {
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const getAnnotationStyles = () =>
|
||||
css({
|
||||
'.ml-annotation': {
|
||||
'&__brush': {
|
||||
'.extent': {
|
||||
stroke: euiThemeVars.euiColorLightShade,
|
||||
strokeWidth: mlAnnotationBorderWidth,
|
||||
strokeDasharray: '2 2',
|
||||
fill: euiThemeVars.euiColorLightestShade,
|
||||
shapeRendering: 'geometricPrecision',
|
||||
},
|
||||
},
|
||||
|
||||
'&__rect': {
|
||||
stroke: euiThemeVars.euiColorFullShade,
|
||||
strokeWidth: mlAnnotationBorderWidth,
|
||||
strokeOpacity: mlAnnotationRectDefaultStrokeOpacity,
|
||||
fill: euiThemeVars.euiColorFullShade,
|
||||
fillOpacity: mlAnnotationRectDefaultFillOpacity,
|
||||
shapeRendering: 'geometricPrecision',
|
||||
transition: `stroke-opacity ${euiThemeVars.euiAnimSpeedFast}, fill-opacity ${euiThemeVars.euiAnimSpeedFast}`,
|
||||
|
||||
'&--highlight': {
|
||||
strokeOpacity: mlAnnotationRectDefaultStrokeOpacity * 2,
|
||||
fillOpacity: mlAnnotationRectDefaultFillOpacity * 2,
|
||||
},
|
||||
|
||||
'&--blur': {
|
||||
strokeOpacity: mlAnnotationRectDefaultStrokeOpacity / 2,
|
||||
fillOpacity: mlAnnotationRectDefaultFillOpacity / 2,
|
||||
},
|
||||
},
|
||||
|
||||
'&__text': {
|
||||
textAnchor: 'middle',
|
||||
fontSize: euiThemeVars.euiFontSizeXS,
|
||||
fontFamily: euiThemeVars.euiFontFamily,
|
||||
fontWeight: euiThemeVars.euiFontWeightMedium,
|
||||
fill: euiThemeVars.euiColorFullShade,
|
||||
transition: `fill ${euiThemeVars.euiAnimSpeedFast}`,
|
||||
userSelect: 'none',
|
||||
|
||||
'&--blur': {
|
||||
fill: euiThemeVars.euiColorMediumShade,
|
||||
},
|
||||
},
|
||||
|
||||
'&__text-rect': {
|
||||
fill: euiThemeVars.euiColorLightShade,
|
||||
transition: `fill ${euiThemeVars.euiAnimSpeedFast}`,
|
||||
|
||||
'&--blur': {
|
||||
fill: euiThemeVars.euiColorLightestShade,
|
||||
},
|
||||
},
|
||||
|
||||
'&--hidden': {
|
||||
display: 'none',
|
||||
},
|
||||
|
||||
'&__context-rect': {
|
||||
stroke: euiThemeVars.euiColorFullShade,
|
||||
strokeWidth: mlAnnotationBorderWidth,
|
||||
strokeOpacity: mlAnnotationRectDefaultStrokeOpacity,
|
||||
fill: euiThemeVars.euiColorFullShade,
|
||||
fillOpacity: mlAnnotationRectDefaultFillOpacity,
|
||||
transition: `stroke-opacity ${euiThemeVars.euiAnimSpeedFast}, fill-opacity ${euiThemeVars.euiAnimSpeedFast}`,
|
||||
shapeRendering: 'geometricPrecision',
|
||||
|
||||
'&--blur': {
|
||||
strokeOpacity: mlAnnotationRectDefaultStrokeOpacity / 2,
|
||||
fillOpacity: mlAnnotationRectDefaultFillOpacity / 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
|
@ -19,6 +19,7 @@ import { HelpMenu } from '../components/help_menu';
|
|||
import { useMlKibana } from '../contexts/kibana';
|
||||
import { MlPageHeader } from '../components/page_header';
|
||||
import { PageTitle } from '../components/page_title';
|
||||
import { getAnnotationStyles, getTimeseriesExplorerStyles } from './styles';
|
||||
|
||||
interface TimeSeriesExplorerPageProps {
|
||||
dateFormatTz?: string;
|
||||
|
@ -26,6 +27,9 @@ interface TimeSeriesExplorerPageProps {
|
|||
noSingleMetricJobsFound?: boolean;
|
||||
}
|
||||
|
||||
const timeseriesExplorerStyles = getTimeseriesExplorerStyles();
|
||||
const annotationStyles = getAnnotationStyles();
|
||||
|
||||
export const TimeSeriesExplorerPage: FC<PropsWithChildren<TimeSeriesExplorerPageProps>> = ({
|
||||
children,
|
||||
dateFormatTz,
|
||||
|
@ -38,10 +42,11 @@ export const TimeSeriesExplorerPage: FC<PropsWithChildren<TimeSeriesExplorerPage
|
|||
const CasesContext = cases?.ui.getCasesContext() ?? React.Fragment;
|
||||
const casesPermissions = cases?.helpers.canUseCases();
|
||||
const helpLink = docLinks.links.ml.anomalyDetection;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="ml-time-series-explorer"
|
||||
css={[timeseriesExplorerStyles, annotationStyles]}
|
||||
ref={resizeRef}
|
||||
data-test-subj="mlPageSingleMetricViewer"
|
||||
>
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
// ML has it's own variables for coloring
|
||||
@import '../../application/variables';
|
||||
|
||||
// Protect the rest of Kibana from ML generic namespacing
|
||||
@import '../../application/timeseriesexplorer/timeseriesexplorer';
|
||||
@import '../../application/timeseriesexplorer/timeseriesexplorer_annotations';
|
|
@ -26,7 +26,10 @@ import type { MlDependencies } from '../../application/app';
|
|||
import { TimeSeriesExplorerEmbeddableChart } from '../../application/timeseriesexplorer/timeseriesexplorer_embeddable_chart';
|
||||
import { APP_STATE_ACTION } from '../../application/timeseriesexplorer/timeseriesexplorer_constants';
|
||||
import type { SingleMetricViewerServices, MlEntity } from '../../embeddables/types';
|
||||
import './_index.scss';
|
||||
import {
|
||||
getTimeseriesExplorerStyles,
|
||||
getAnnotationStyles,
|
||||
} from '../../application/timeseriesexplorer/styles';
|
||||
|
||||
const containerPadding = 20;
|
||||
const minElemAndChartDiff = 20;
|
||||
|
@ -72,6 +75,9 @@ export interface SingleMetricViewerProps {
|
|||
type Zoom = AppStateZoom | undefined;
|
||||
type ForecastId = string | undefined;
|
||||
|
||||
const timeseriesExplorerStyles = getTimeseriesExplorerStyles();
|
||||
const annotationStyles = getAnnotationStyles();
|
||||
|
||||
const SingleMetricViewerWrapper: FC<SingleMetricViewerPropsWithDeps> = ({
|
||||
// Component dependencies
|
||||
coreStart,
|
||||
|
@ -217,7 +223,7 @@ const SingleMetricViewerWrapper: FC<SingleMetricViewerPropsWithDeps> = ({
|
|||
}}
|
||||
data-test-subj={`mlSingleMetricViewer_${uuid}`}
|
||||
ref={resizeRef}
|
||||
className="ml-time-series-explorer"
|
||||
css={[timeseriesExplorerStyles, annotationStyles]}
|
||||
data-shared-item="" // TODO: Remove data-shared-item as part of https://github.com/elastic/kibana/issues/179376
|
||||
data-rendering-count={1}
|
||||
>
|
||||
|
|
|
@ -285,7 +285,7 @@ export function MachineLearningJobAnnotationsProvider({ getService }: FtrProvide
|
|||
|
||||
public async openCreateAnnotationFlyout() {
|
||||
await retry.tryForTime(30 * 1000, async () => {
|
||||
const el = await find.byClassName('mlAnnotationBrush');
|
||||
const el = await find.byClassName('ml-annotation__brush');
|
||||
|
||||
// simulate click and drag on the focus chart
|
||||
// to generate annotation
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue