diff --git a/x-pack/plugins/ml/public/application/_index.scss b/x-pack/plugins/ml/public/application/_index.scss index ac9e16e5f3e78..3025b8f7d921b 100644 --- a/x-pack/plugins/ml/public/application/_index.scss +++ b/x-pack/plugins/ml/public/application/_index.scss @@ -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 diff --git a/x-pack/plugins/ml/public/application/explorer/annotation_timeline.tsx b/x-pack/plugins/ml/public/application/explorer/annotation_timeline.tsx index 2dd0376a739c6..3d12c55e05e16 100644 --- a/x-pack/plugins/ml/public/application/explorer/annotation_timeline.tsx +++ b/x-pack/plugins/ml/public/application/explorer/annotation_timeline.tsx @@ -139,7 +139,7 @@ export const AnnotationTimeline = = endingXPos ? endingXPos - annotationWidth : xPos) .attr('y', 0) diff --git a/x-pack/plugins/ml/public/application/explorer/swimlane_annotation_container.tsx b/x-pack/plugins/ml/public/application/explorer/swimlane_annotation_container.tsx index ea4599c47d4ff..ce506e03dae31 100644 --- a/x-pack/plugins/ml/public/application/explorer/swimlane_annotation_container.tsx +++ b/x-pack/plugins/ml/public/application/explorer/swimlane_annotation_container.tsx @@ -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 = ({ chartWidth, domain, @@ -135,7 +138,7 @@ export const SwimlaneAnnotationContainer: FC = 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 = // eslint-disable-next-line react-hooks/exhaustive-deps }, [chartWidth, domain, annotationsData, tooltipService]); - return
; + return
; }; diff --git a/x-pack/plugins/ml/public/application/styles.ts b/x-pack/plugins/ml/public/application/styles.ts new file mode 100644 index 0000000000000..86f4dbe3bbcd4 --- /dev/null +++ b/x-pack/plugins/ml/public/application/styles.ts @@ -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', +}; diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/_index.scss b/x-pack/plugins/ml/public/application/timeseriesexplorer/_index.scss deleted file mode 100644 index 236e50dc747bf..0000000000000 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/_index.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import 'timeseriesexplorer'; -@import 'timeseriesexplorer_annotations'; diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer.scss b/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer.scss deleted file mode 100644 index e47e69c741a90..0000000000000 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer.scss +++ /dev/null @@ -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; - } -} diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer_annotations.scss b/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer_annotations.scss deleted file mode 100644 index 656f38590d3a5..0000000000000 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer_annotations.scss +++ /dev/null @@ -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; -} diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js index 51abbf77dbeba..d78ed1ed6e7fc 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js @@ -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) => { diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_annotations.ts b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_annotations.ts index 2a104eadad117..58dc3e84f2794 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_annotations.ts +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_annotations.ts @@ -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); }); } diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/styles.ts b/x-pack/plugins/ml/public/application/timeseriesexplorer/styles.ts new file mode 100644 index 0000000000000..74ddf08503803 --- /dev/null +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/styles.ts @@ -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, + }, + }, + }, + }); diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_page.tsx b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_page.tsx index d673aca1e74e3..dc7cacdba5d0a 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_page.tsx +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_page.tsx @@ -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> = ({ children, dateFormatTz, @@ -38,10 +42,11 @@ export const TimeSeriesExplorerPage: FC
diff --git a/x-pack/plugins/ml/public/shared_components/single_metric_viewer/_index.scss b/x-pack/plugins/ml/public/shared_components/single_metric_viewer/_index.scss deleted file mode 100644 index b6f91cc749dcc..0000000000000 --- a/x-pack/plugins/ml/public/shared_components/single_metric_viewer/_index.scss +++ /dev/null @@ -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'; \ No newline at end of file diff --git a/x-pack/plugins/ml/public/shared_components/single_metric_viewer/single_metric_viewer.tsx b/x-pack/plugins/ml/public/shared_components/single_metric_viewer/single_metric_viewer.tsx index 96e678407f626..27ed864fbd012 100644 --- a/x-pack/plugins/ml/public/shared_components/single_metric_viewer/single_metric_viewer.tsx +++ b/x-pack/plugins/ml/public/shared_components/single_metric_viewer/single_metric_viewer.tsx @@ -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 = ({ // Component dependencies coreStart, @@ -217,7 +223,7 @@ const SingleMetricViewerWrapper: FC = ({ }} 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} > diff --git a/x-pack/test/functional/services/ml/job_annotations_table.ts b/x-pack/test/functional/services/ml/job_annotations_table.ts index 7945abd7e9608..0dc477113f1ea 100644 --- a/x-pack/test/functional/services/ml/job_annotations_table.ts +++ b/x-pack/test/functional/services/ml/job_annotations_table.ts @@ -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