mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[ML] Anomaly Explorer: Migrate Explorer from SCSS to Emotion (#215196)
Migrate remaining Anomaly Explorer styles from SCSS to Emotion: | Before | After | | ------------- | ------------- | | Anomaly Swimlane | Anomaly Swimlane | | <img width="983" alt="image" src="https://github.com/user-attachments/assets/d654bf74-f04a-4f57-8891-af0c0a0d3b85" /> | <img width="824" alt="Pasted Graphic 1" src="https://github.com/user-attachments/assets/38e00adf-dba1-43be-a6da-6141221dc82b" /> | | Swimlane embeddable | Swimlane embeddable | | <img width="573" alt="image" src="https://github.com/user-attachments/assets/304d0073-a194-41cd-a379-5fc1fbb734a6" /> | <img width="580" alt="Create visualization" src="https://github.com/user-attachments/assets/28982191-16c1-437d-9955-77ca73fbe4f0" /> | | Anomalies charts tooltip and label | Anomalies charts tooltip and label | | <img width="970" alt="image" src="https://github.com/user-attachments/assets/f6cb53f3-b79e-4eac-84c2-18d1d0a53cc0" /> | <img width="974" alt="Pasted Graphic 3" src="https://github.com/user-attachments/assets/2f553118-8c4f-4678-809d-f7f25816fb1c" /> |
This commit is contained in:
parent
42183d6039
commit
14c6204dca
15 changed files with 211 additions and 182 deletions
|
@ -1,9 +1,6 @@
|
|||
// Protect the rest of Kibana from ML generic namespacing
|
||||
// SASSTODO: Prefix ml selectors instead
|
||||
.ml-app {
|
||||
// Sub applications
|
||||
@import 'explorer/index'; // SASSTODO: This file needs to be rewritten
|
||||
|
||||
// Components
|
||||
@import 'components/anomalies_table/index'; // SASSTODO: This file overwrites EUI directly
|
||||
@import 'components/job_selector/index';
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
.mlSwimLaneContainer {
|
||||
/* Override legend styles */
|
||||
.echLegendListContainer {
|
||||
height: 34px !important;
|
||||
}
|
||||
|
||||
.echLegendList {
|
||||
display: flex !important;
|
||||
justify-content: space-between !important;
|
||||
flex-wrap: nowrap;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
@import 'explorer';
|
||||
@import 'explorer_charts/index';
|
|
@ -2,7 +2,15 @@
|
|||
|
||||
exports[`ExplorerChartTooltip Render tooltip based on infoTooltip data. 1`] = `
|
||||
<div
|
||||
className="ml-explorer-chart-info-tooltip"
|
||||
css={
|
||||
Object {
|
||||
"map": undefined,
|
||||
"name": "xotwi",
|
||||
"next": undefined,
|
||||
"styles": "max-width:384px;",
|
||||
"toString": [Function],
|
||||
}
|
||||
}
|
||||
>
|
||||
<TooltipDefinitionList
|
||||
toolTipData={
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
.ml-explorer-chart-info-tooltip {
|
||||
max-width: 384px;
|
||||
}
|
||||
|
||||
.ml-explorer-chart-description {
|
||||
font-size: $euiFontSizeXS;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.ml-explorer-chart-info-tooltip .mlDescriptionList > * {
|
||||
margin-top: $euiSizeXS;
|
||||
}
|
||||
|
||||
.ml-explorer-chart-info-tooltip .mlDescriptionList {
|
||||
display: grid;
|
||||
grid-template-columns: max-content auto;
|
||||
|
||||
.mlDescriptionList__title {
|
||||
color: $euiColorGhost;
|
||||
font-size: $euiFontSizeXS;
|
||||
font-weight: normal;
|
||||
white-space: nowrap;
|
||||
grid-column-start: 1;
|
||||
}
|
||||
|
||||
.mlDescriptionList__description {
|
||||
color: $euiColorGhost;
|
||||
font-size: $euiFontSizeXS;
|
||||
font-weight: bold;
|
||||
padding-left: $euiSizeS;
|
||||
max-width: 256px;
|
||||
grid-column-start: 2;
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
@import 'components/explorer_chart_label/index';
|
|
@ -2,12 +2,8 @@
|
|||
|
||||
exports[`ExplorerChartLabelBadge Render the chart label in one line. 1`] = `
|
||||
<Fragment>
|
||||
<span
|
||||
className="ml-explorer-chart-label"
|
||||
>
|
||||
<span
|
||||
className="ml-explorer-chart-label-detector"
|
||||
>
|
||||
<span>
|
||||
<span>
|
||||
high_sum(nginx.access.body_sent.bytes) over nginx.access.remote_ip (population-03)
|
||||
–
|
||||
</span>
|
||||
|
@ -22,11 +18,8 @@ exports[`ExplorerChartLabelBadge Render the chart label in one line. 1`] = `
|
|||
/>
|
||||
|
||||
|
||||
<span
|
||||
className="ml-explorer-chart-info-icon"
|
||||
>
|
||||
<span>
|
||||
<EuiIconTip
|
||||
className="ml-explorer-chart-eui-icon-tip"
|
||||
content={
|
||||
<ExplorerChartInfoTooltip
|
||||
aggregationInterval="1h"
|
||||
|
@ -42,6 +35,11 @@ exports[`ExplorerChartLabelBadge Render the chart label in one line. 1`] = `
|
|||
jobId="population-03"
|
||||
/>
|
||||
}
|
||||
css={
|
||||
Object {
|
||||
"maxWidth": "none",
|
||||
}
|
||||
}
|
||||
position="top"
|
||||
size="s"
|
||||
/>
|
||||
|
@ -52,20 +50,13 @@ exports[`ExplorerChartLabelBadge Render the chart label in one line. 1`] = `
|
|||
|
||||
exports[`ExplorerChartLabelBadge Render the chart label in two lines. 1`] = `
|
||||
<Fragment>
|
||||
<span
|
||||
className="ml-explorer-chart-label"
|
||||
>
|
||||
<span
|
||||
className="ml-explorer-chart-label-detector"
|
||||
>
|
||||
<span>
|
||||
<span>
|
||||
high_sum(nginx.access.body_sent.bytes) over nginx.access.remote_ip (population-03)
|
||||
|
||||
</span>
|
||||
<span
|
||||
className="ml-explorer-chart-info-icon"
|
||||
>
|
||||
<span>
|
||||
<EuiIconTip
|
||||
className="ml-explorer-chart-eui-icon-tip"
|
||||
content={
|
||||
<ExplorerChartInfoTooltip
|
||||
aggregationInterval="1h"
|
||||
|
@ -81,13 +72,26 @@ exports[`ExplorerChartLabelBadge Render the chart label in two lines. 1`] = `
|
|||
jobId="population-03"
|
||||
/>
|
||||
}
|
||||
css={
|
||||
Object {
|
||||
"maxWidth": "none",
|
||||
}
|
||||
}
|
||||
position="top"
|
||||
size="s"
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
className="ml-explorer-chart-label-badges"
|
||||
css={
|
||||
Object {
|
||||
"map": undefined,
|
||||
"name": "1ivb442",
|
||||
"next": undefined,
|
||||
"styles": "display:flex;align-items:center;margin-top:4px;",
|
||||
"toString": [Function],
|
||||
}
|
||||
}
|
||||
>
|
||||
<ExplorerChartLabelBadge
|
||||
entity={
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
.ml-explorer-chart-eui-icon-tip {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.ml-explorer-chart-label-badges {
|
||||
margin-top: $euiSizeXS;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
@import 'explorer_chart_label';
|
|
@ -5,15 +5,15 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import './_explorer_chart_label.scss';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Fragment, useCallback } from 'react';
|
||||
|
||||
import { EuiIconTip } from '@elastic/eui';
|
||||
import { EuiIconTip, useEuiTheme } from '@elastic/eui';
|
||||
|
||||
import { ExplorerChartLabelBadge } from './explorer_chart_label_badge';
|
||||
import { ExplorerChartInfoTooltip } from '../../explorer_chart_info_tooltip';
|
||||
import { EntityFilter } from './entity_filter';
|
||||
import { css } from '@emotion/react';
|
||||
|
||||
export function ExplorerChartLabel({
|
||||
detectorLabel,
|
||||
|
@ -67,9 +67,11 @@ export function ExplorerChartLabel({
|
|||
});
|
||||
|
||||
const infoIcon = (
|
||||
<span className="ml-explorer-chart-info-icon">
|
||||
<span>
|
||||
<EuiIconTip
|
||||
className="ml-explorer-chart-eui-icon-tip"
|
||||
css={{
|
||||
maxWidth: 'none',
|
||||
}}
|
||||
content={<ExplorerChartInfoTooltip {...infoTooltip} />}
|
||||
position="top"
|
||||
size="s"
|
||||
|
@ -77,10 +79,18 @@ export function ExplorerChartLabel({
|
|||
</span>
|
||||
);
|
||||
|
||||
const { euiTheme } = useEuiTheme();
|
||||
|
||||
const badgesStyles = css({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginTop: euiTheme.size.xs,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className="ml-explorer-chart-label">
|
||||
<span className="ml-explorer-chart-label-detector">
|
||||
<span>
|
||||
<span>
|
||||
{detectorLabel}
|
||||
{labelSeparator}
|
||||
</span>
|
||||
|
@ -91,7 +101,7 @@ export function ExplorerChartLabel({
|
|||
</>
|
||||
)}
|
||||
</span>
|
||||
{wrapLabel && <span className="ml-explorer-chart-label-badges">{entityFieldBadges}</span>}
|
||||
{wrapLabel && <span css={badgesStyles}>{entityFieldBadges}</span>}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import './_explorer_chart_tooltip.scss';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
|
||||
import { CHART_TYPE } from '../explorer_constants';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useExplorerChartTooltipStyles } from './explorer_chart_tooltip_styles';
|
||||
|
||||
const CHART_DESCRIPTION = {
|
||||
[CHART_TYPE.EVENT_DISTRIBUTION]: i18n.translate(
|
||||
|
@ -36,12 +36,18 @@ const CHART_DESCRIPTION = {
|
|||
import { EuiSpacer } from '@elastic/eui';
|
||||
|
||||
function TooltipDefinitionList({ toolTipData }) {
|
||||
const {
|
||||
title: titleStyle,
|
||||
description: descriptionStyle,
|
||||
descriptionList,
|
||||
} = useExplorerChartTooltipStyles();
|
||||
|
||||
return (
|
||||
<dl className="mlDescriptionList">
|
||||
<dl css={descriptionList}>
|
||||
{toolTipData.map(({ title, description }) => (
|
||||
<React.Fragment key={`${title} ${description}`}>
|
||||
<dt className="mlDescriptionList__title">{title}</dt>
|
||||
<dd className="mlDescriptionList__description">{description}</dd>
|
||||
<dt css={titleStyle}>{title}</dt>
|
||||
<dd css={descriptionStyle}>{description}</dd>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</dl>
|
||||
|
@ -55,6 +61,8 @@ export const ExplorerChartInfoTooltip = ({
|
|||
chartType,
|
||||
entityFields = [],
|
||||
}) => {
|
||||
const { tooltip, chartDescription: chartDescriptionStyle } = useExplorerChartTooltipStyles();
|
||||
|
||||
const chartDescription = CHART_DESCRIPTION[chartType];
|
||||
|
||||
const toolTipData = [
|
||||
|
@ -86,12 +94,12 @@ export const ExplorerChartInfoTooltip = ({
|
|||
});
|
||||
|
||||
return (
|
||||
<div className="ml-explorer-chart-info-tooltip">
|
||||
<div css={tooltip}>
|
||||
<TooltipDefinitionList toolTipData={toolTipData} />
|
||||
{chartDescription && (
|
||||
<React.Fragment>
|
||||
<EuiSpacer size="s" />
|
||||
<div className="ml-explorer-chart-description">{chartDescription}</div>
|
||||
<div css={chartDescriptionStyle}>{chartDescription}</div>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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 { useEuiFontSize, useEuiTheme } from '@elastic/eui';
|
||||
import { useMemo } from 'react';
|
||||
import { css } from '@emotion/react';
|
||||
|
||||
export const useExplorerChartTooltipStyles = () => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
const euiFontSizeXS = useEuiFontSize('xs').fontSize;
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
tooltip: css({
|
||||
maxWidth: '384px',
|
||||
}),
|
||||
descriptionList: css({
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'max-content auto',
|
||||
'& > *': {
|
||||
marginTop: euiTheme.size.xs,
|
||||
},
|
||||
}),
|
||||
title: css({
|
||||
color: euiTheme.colors.ghost,
|
||||
fontSize: euiFontSizeXS,
|
||||
fontWeight: 'normal',
|
||||
whiteSpace: 'nowrap',
|
||||
gridColumnStart: 1,
|
||||
}),
|
||||
description: css({
|
||||
color: euiTheme.colors.ghost,
|
||||
fontSize: euiFontSizeXS,
|
||||
fontWeight: 'bold',
|
||||
paddingLeft: euiTheme.size.s,
|
||||
maxWidth: '256px',
|
||||
gridColumnStart: 2,
|
||||
}),
|
||||
chartDescription: css({
|
||||
fontSize: euiFontSizeXS,
|
||||
fontStyle: 'italic',
|
||||
}),
|
||||
}),
|
||||
[euiFontSizeXS, euiTheme]
|
||||
);
|
||||
};
|
|
@ -4,8 +4,6 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import './_index.scss';
|
||||
|
||||
import React, { useEffect, useState, useCallback, useRef } from 'react';
|
||||
|
||||
import {
|
||||
|
|
|
@ -54,7 +54,6 @@ import type { SwimlaneType } from './explorer_constants';
|
|||
import { SWIMLANE_TYPE } from './explorer_constants';
|
||||
import { mlEscape } from '../util/string_utils';
|
||||
import { FormattedTooltip } from '../components/chart_tooltip/chart_tooltip';
|
||||
import './_explorer.scss';
|
||||
import { EMPTY_FIELD_VALUE_LABEL } from '../timeseriesexplorer/components/entity_control/entity_control';
|
||||
import { SWIM_LANE_LABEL_WIDTH, Y_AXIS_LABEL_PADDING } from './constants';
|
||||
|
||||
|
@ -418,7 +417,18 @@ export const SwimlaneContainer: FC<SwimlaneProps> = ({
|
|||
if (noSwimLaneData) {
|
||||
onRenderComplete?.();
|
||||
}
|
||||
|
||||
const swimlaneStyles = css({
|
||||
'.echLegendListContainer': {
|
||||
height: '34px !important',
|
||||
},
|
||||
'.echLegendList': {
|
||||
display: 'flex !important',
|
||||
justifyContent: 'space-between !important',
|
||||
flexWrap: 'nowrap',
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
},
|
||||
});
|
||||
// A resize observer is required to compute the bucket span based on the chart width to fetch the data accordingly
|
||||
return (
|
||||
<EuiResizeObserver onResize={resizeHandler}>
|
||||
|
@ -452,86 +462,91 @@ export const SwimlaneContainer: FC<SwimlaneProps> = ({
|
|||
hidden={noSwimLaneData}
|
||||
>
|
||||
{showSwimlane && !isLoading && (
|
||||
<Chart className={'mlSwimLaneContainer'} ref={chartRef}>
|
||||
<Tooltip {...tooltipOptions} />
|
||||
<Settings
|
||||
theme={themeOverrides}
|
||||
baseTheme={baseTheme}
|
||||
onElementClick={onElementClick}
|
||||
onPointerUpdate={handleCursorUpdate}
|
||||
showLegend={showLegend}
|
||||
legendPosition={Position.Top}
|
||||
xDomain={xDomain}
|
||||
debugState={window._echDebugStateFlag ?? false}
|
||||
onBrushEnd={onBrushEnd as BrushEndListener}
|
||||
locale={i18n.getLocale()}
|
||||
onRenderChange={(isRendered) => {
|
||||
if (isRendered && onRenderComplete) {
|
||||
onRenderComplete();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
data-test-subj="mlSwimLaneContainer"
|
||||
css={{ height: '100%', width: '100%' }}
|
||||
>
|
||||
<Chart css={swimlaneStyles} ref={chartRef}>
|
||||
<Tooltip {...tooltipOptions} />
|
||||
<Settings
|
||||
theme={themeOverrides}
|
||||
baseTheme={baseTheme}
|
||||
onElementClick={onElementClick}
|
||||
onPointerUpdate={handleCursorUpdate}
|
||||
showLegend={showLegend}
|
||||
legendPosition={Position.Top}
|
||||
xDomain={xDomain}
|
||||
debugState={window._echDebugStateFlag ?? false}
|
||||
onBrushEnd={onBrushEnd as BrushEndListener}
|
||||
locale={i18n.getLocale()}
|
||||
onRenderChange={(isRendered) => {
|
||||
if (isRendered && onRenderComplete) {
|
||||
onRenderComplete();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<Heatmap
|
||||
id={id}
|
||||
timeZone="UTC"
|
||||
colorScale={{
|
||||
type: 'bands',
|
||||
bands: [
|
||||
{
|
||||
start: ML_ANOMALY_THRESHOLD.LOW,
|
||||
end: ML_ANOMALY_THRESHOLD.WARNING,
|
||||
color: ML_SEVERITY_COLORS.LOW,
|
||||
<Heatmap
|
||||
id={id}
|
||||
timeZone="UTC"
|
||||
colorScale={{
|
||||
type: 'bands',
|
||||
bands: [
|
||||
{
|
||||
start: ML_ANOMALY_THRESHOLD.LOW,
|
||||
end: ML_ANOMALY_THRESHOLD.WARNING,
|
||||
color: ML_SEVERITY_COLORS.LOW,
|
||||
},
|
||||
{
|
||||
start: ML_ANOMALY_THRESHOLD.WARNING,
|
||||
end: ML_ANOMALY_THRESHOLD.MINOR,
|
||||
color: ML_SEVERITY_COLORS.WARNING,
|
||||
},
|
||||
{
|
||||
start: ML_ANOMALY_THRESHOLD.MINOR,
|
||||
end: ML_ANOMALY_THRESHOLD.MAJOR,
|
||||
color: ML_SEVERITY_COLORS.MINOR,
|
||||
},
|
||||
{
|
||||
start: ML_ANOMALY_THRESHOLD.MAJOR,
|
||||
end: ML_ANOMALY_THRESHOLD.CRITICAL,
|
||||
color: ML_SEVERITY_COLORS.MAJOR,
|
||||
},
|
||||
{
|
||||
start: ML_ANOMALY_THRESHOLD.CRITICAL,
|
||||
end: Infinity,
|
||||
color: ML_SEVERITY_COLORS.CRITICAL,
|
||||
},
|
||||
],
|
||||
}}
|
||||
data={swimLanePoints}
|
||||
xAccessor="time"
|
||||
yAccessor="laneLabel"
|
||||
valueAccessor="value"
|
||||
highlightedData={highlightedData}
|
||||
valueFormatter={getFormattedSeverityScore}
|
||||
xScale={{
|
||||
type: ScaleType.Time,
|
||||
interval: {
|
||||
type: 'fixed',
|
||||
unit: 'ms',
|
||||
// the xDomain.minInterval should always be available at rendering time
|
||||
// adding a fallback to 1m bucket
|
||||
value: xDomain?.minInterval ?? 1000 * 60,
|
||||
},
|
||||
{
|
||||
start: ML_ANOMALY_THRESHOLD.WARNING,
|
||||
end: ML_ANOMALY_THRESHOLD.MINOR,
|
||||
color: ML_SEVERITY_COLORS.WARNING,
|
||||
},
|
||||
{
|
||||
start: ML_ANOMALY_THRESHOLD.MINOR,
|
||||
end: ML_ANOMALY_THRESHOLD.MAJOR,
|
||||
color: ML_SEVERITY_COLORS.MINOR,
|
||||
},
|
||||
{
|
||||
start: ML_ANOMALY_THRESHOLD.MAJOR,
|
||||
end: ML_ANOMALY_THRESHOLD.CRITICAL,
|
||||
color: ML_SEVERITY_COLORS.MAJOR,
|
||||
},
|
||||
{
|
||||
start: ML_ANOMALY_THRESHOLD.CRITICAL,
|
||||
end: Infinity,
|
||||
color: ML_SEVERITY_COLORS.CRITICAL,
|
||||
},
|
||||
],
|
||||
}}
|
||||
data={swimLanePoints}
|
||||
xAccessor="time"
|
||||
yAccessor="laneLabel"
|
||||
valueAccessor="value"
|
||||
highlightedData={highlightedData}
|
||||
valueFormatter={getFormattedSeverityScore}
|
||||
xScale={{
|
||||
type: ScaleType.Time,
|
||||
interval: {
|
||||
type: 'fixed',
|
||||
unit: 'ms',
|
||||
// the xDomain.minInterval should always be available at rendering time
|
||||
// adding a fallback to 1m bucket
|
||||
value: xDomain?.minInterval ?? 1000 * 60,
|
||||
},
|
||||
}}
|
||||
ySortPredicate="dataIndex"
|
||||
yAxisLabelFormatter={(laneLabel) => {
|
||||
return laneLabel === '' ? EMPTY_FIELD_VALUE_LABEL : String(laneLabel);
|
||||
}}
|
||||
xAxisLabelFormatter={(v) => {
|
||||
timeBuckets.setInterval(`${swimlaneData.interval}s`);
|
||||
const scaledDateFormat = timeBuckets.getScaledDateFormat();
|
||||
return moment(v).format(scaledDateFormat);
|
||||
}}
|
||||
/>
|
||||
</Chart>
|
||||
}}
|
||||
ySortPredicate="dataIndex"
|
||||
yAxisLabelFormatter={(laneLabel) => {
|
||||
return laneLabel === '' ? EMPTY_FIELD_VALUE_LABEL : String(laneLabel);
|
||||
}}
|
||||
xAxisLabelFormatter={(v) => {
|
||||
timeBuckets.setInterval(`${swimlaneData.interval}s`);
|
||||
const scaledDateFormat = timeBuckets.getScaledDateFormat();
|
||||
return moment(v).format(scaledDateFormat);
|
||||
}}
|
||||
/>
|
||||
</Chart>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isLoading && (
|
||||
|
|
|
@ -160,7 +160,7 @@ export function MachineLearningAnomalyExplorerProvider(
|
|||
});
|
||||
// changing to the dashboard app might take some time
|
||||
const embeddable = await testSubjects.find('mlAnomalySwimlaneEmbeddableWrapper', 30 * 1000);
|
||||
const swimlane = await embeddable.findByClassName('mlSwimLaneContainer');
|
||||
const swimlane = await embeddable.findByTestSubject('mlSwimLaneContainer');
|
||||
expect(await swimlane.isDisplayed()).to.eql(
|
||||
true,
|
||||
'Anomaly swim lane should be displayed in dashboard'
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue