[8.x] [ML] Remove legacy scss overwrites Single Metric Viewer (#195259) (#196085)

# 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:
Kibana Machine 2024-10-14 23:05:14 +11:00 committed by GitHub
parent 1c98c7acce
commit 7a80e6f1a7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 403 additions and 417 deletions

View file

@ -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

View file

@ -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)

View file

@ -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} />;
};

View 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',
};

View file

@ -1,2 +0,0 @@
@import 'timeseriesexplorer';
@import 'timeseriesexplorer_annotations';

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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) => {

View file

@ -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);
});
}

View file

@ -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,
},
},
},
});

View file

@ -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"
>

View file

@ -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';

View file

@ -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}
>

View file

@ -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