[ML] Replace legacy SCSS overwrites (#216698)

Part of https://github.com/elastic/kibana/issues/140695

This PR replaces all remaining SCSS overrides in ML.

> ml/public/application/_index.scss
> ./job_selector/_index.scss
> ./job_selector/_job_selector.scss
> ./rule_editor/_index.scss
> ./rule_editor/_rule_editor.scss
> ./anomalies_table/_index.scss 
> ./anomalies_table/_anomalies_table.scss
>
data_visualizer/public/application/common/components/stats_table/components/field_data_row/column_chart.scss
>
data_visualizer/public/application/common/components/field_type_icon/_index.scss
>
data_visualizer/public/application/common/components/field_type_icon/_field_type_icon.scss
>
x-pack/platform/packages/private/ml/aiops_components/src/dual_brush/dual_brush.scss

There are minor color changes in the `dual brush` and `GanttBar` in the
Job Selector.

| Before  | After |
| ------------- | ------------- |
| <img width="1217" alt="dual-brush-before-light"
src="https://github.com/user-attachments/assets/e87f1600-c1f1-42ef-a4f8-a8d5b21e2ca7"
/> | <img width="881" alt="dual-brush-after-light"
src="https://github.com/user-attachments/assets/17996e65-50dc-42e8-9b9a-4757f2b30309"
/> |
| <img width="863" alt="dual-brush-before-dark"
src="https://github.com/user-attachments/assets/685fe511-b715-457b-8173-ece0d41f7bef"
/> | <img width="865" alt="dual-brush-after-dark"
src="https://github.com/user-attachments/assets/593b6085-281b-49b2-b0fd-9ae6f44b6684"
/> |
| <img width="576" alt="job_selector_before_light"
src="https://github.com/user-attachments/assets/ce2438c1-a54f-4066-bb4d-b86412d55e1e"
/> | <img width="564" alt="job_selector_after_light"
src="https://github.com/user-attachments/assets/74cb28d3-73c3-4836-ae53-f64f7730cf09"
/> |
| <img width="581" alt="job_selector_before_dark"
src="https://github.com/user-attachments/assets/7be93e06-12a0-4715-ac35-74711e08c761"
/> | <img width="567" alt="job_selector_after_dark"
src="https://github.com/user-attachments/assets/c3af92ee-f510-4f40-a99a-04f446652d91"
/> |
| <img width="574" alt="role_editor_before_light"
src="https://github.com/user-attachments/assets/8e8e33b8-2688-4526-9062-20dab205dcbf"
/> | <img width="564" alt="rule_editor_after_light"
src="https://github.com/user-attachments/assets/98142dfc-b74a-4bbd-af8c-c6c041805826"
/> |
| <img width="576" alt="role_editor_before_dark"
src="https://github.com/user-attachments/assets/e534c5f1-f75a-433d-91d8-dc57e059e407"
/> | <img width="572" alt="rule_editor_after_dark"
src="https://github.com/user-attachments/assets/2f56394f-4585-4176-a178-ef85394ab46d"
/> |
| <img width="572" alt="quick_role_editor_before_light"
src="https://github.com/user-attachments/assets/7f414295-e799-4073-84b8-d2bd94eb293f"
/> | <img width="567" alt="quick_role_editor_after_light"
src="https://github.com/user-attachments/assets/1d7bbc7b-bc44-4753-b9b6-6cac8cfb8953"
/> |
| <img width="578" alt="quick_role_editor_before_dark"
src="https://github.com/user-attachments/assets/f1d5291e-ef72-4e40-a614-b909193ec060"
/> | <img width="562" alt="quick_role_editor_after_dark"
src="https://github.com/user-attachments/assets/90c2927a-0bf2-4f70-a13c-7937ae2bf476"
/> |
| <img width="1205" alt="discover_vis_before_light"
src="https://github.com/user-attachments/assets/899311e7-d10b-48fe-91e1-95c3af7f5608"
/> | <img width="1201" alt="discover-vis-after-light"
src="https://github.com/user-attachments/assets/05f0dcde-6a1b-4139-95ba-19a24ad4fdcf"
/> |
| <img width="1207" alt="discover-vis-before-dark"
src="https://github.com/user-attachments/assets/cde9e49e-b9c3-4bd5-9bd5-32b4f09ce834"
/> | <img width="1207" alt="discover-viz-after-dark"
src="https://github.com/user-attachments/assets/544e44a9-3676-448d-9348-d88a67284a59"
/> |
| <img width="1160" alt="anomalies_table_before_light"
src="https://github.com/user-attachments/assets/9be79294-9808-4509-a1cb-02e342d9abe2"
/> | <img width="1136" alt="anomalies_table_after_light"
src="https://github.com/user-attachments/assets/3b3ce7ad-6f67-4caf-b12d-1839bb2c08ab"
/> |
| <img width="1152" alt="anomalies_table_before_dark"
src="https://github.com/user-attachments/assets/8e5af9fd-90f5-4f85-bd5b-40dc0ab74d0a"
/> | <img width="1144" alt="anomalies_table_after_dark"
src="https://github.com/user-attachments/assets/c6ca08b6-e816-49d2-8c15-9ec9bb1dd983"
/> |
| <img width="668" alt="category_examples_before"
src="https://github.com/user-attachments/assets/9bfd1978-27fe-41bc-9828-f94314e420a6"
/> | <img width="1101" alt="category_examples_after"
src="https://github.com/user-attachments/assets/72c58a22-6d1e-4901-898c-9c54c46eb3a9"
/> |
This commit is contained in:
Robert Jaszczurek 2025-04-07 16:23:17 +02:00 committed by GitHub
parent 86fdbe5379
commit 9f932a099b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
37 changed files with 265 additions and 334 deletions

View file

@ -1,11 +0,0 @@
.aiops-dual-brush {
.handle {
fill: $euiColorDarkShade;
}
.brush .selection {
stroke: none;
fill: $euiColorDarkShade !important;
opacity: .5 !important;
}
}

View file

@ -8,7 +8,7 @@
import { isEqual } from 'lodash';
import React, { useEffect, useMemo, useRef, type FC } from 'react';
import { htmlIdGenerator } from '@elastic/eui';
import { htmlIdGenerator, useEuiTheme } from '@elastic/eui';
import * as d3Brush from 'd3-brush';
import * as d3Scale from 'd3-scale';
@ -18,7 +18,7 @@ import * as d3Transition from 'd3-transition';
import { getSnappedWindowParameters } from '@kbn/aiops-log-rate-analysis';
import type { WindowParameters } from '@kbn/aiops-log-rate-analysis';
import './dual_brush.scss';
import { css } from '@emotion/react';
const { brush, brushSelection, brushX } = d3Brush;
const { scaleLinear } = d3Scale;
@ -113,6 +113,7 @@ export const DualBrush: FC<DualBrushProps> = (props) => {
width,
nonInteractive,
} = props;
const { euiTheme } = useEuiTheme();
const d3BrushContainer = useRef(null);
const brushes = useRef<DualBrush[]>([]);
@ -130,6 +131,20 @@ export const DualBrush: FC<DualBrushProps> = (props) => {
const { baselineMin, baselineMax, deviationMin, deviationMax } = windowParameters;
const cssOverrides = useMemo(
() =>
css({
'.handle': {
fill: euiTheme.colors.vis.euiColorVisGrey2,
},
'.brush .selection': {
stroke: 'none',
fill: euiTheme.colors.vis.euiColorVisGrey2,
},
}),
[euiTheme.colors.vis.euiColorVisGrey2]
);
useEffect(() => {
if (d3BrushContainer.current && width > 0) {
const gBrushes = d3.select(d3BrushContainer.current);
@ -380,7 +395,7 @@ export const DualBrush: FC<DualBrushProps> = (props) => {
<>
{width > 0 && (
<svg
className="aiops-dual-brush"
css={cssOverrides}
data-test-subj="aiopsDualBrush"
width={width}
height={BRUSH_HEIGHT}

View file

@ -1,4 +0,0 @@
.dvFieldTypeIcon__anchor {
display: flex;
align-items: center;
}

View file

@ -11,7 +11,6 @@ import { EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FieldIcon } from '@kbn/react-field';
import { getFieldTypeName } from '@kbn/field-utils';
import './_index.scss';
interface FieldTypeIconProps {
tooltipEnabled: boolean;
@ -28,7 +27,12 @@ export const FieldTypeIcon: FC<FieldTypeIconProps> = ({ tooltipEnabled = false,
if (tooltipEnabled === true) {
return (
<EuiToolTip
anchorClassName="dvFieldTypeIcon__anchor"
anchorProps={{
css: {
display: 'flex',
alignItems: 'center',
},
}}
content={label}
data-test-subj="dvFieldTypeTooltip"
position="left"

View file

@ -1,33 +0,0 @@
.dataGridChart__histogram {
width: 100%;
}
.dataGridChart__column-chart {
width: 100%;
}
.dataGridChart__legend {
@include euiTextTruncate;
color: $euiColorMediumShade;
display: block;
overflow-x: hidden;
font-style: italic;
font-weight: normal;
text-align: left;
line-height: 1.1;
font-size: #{calc($euiFontSizeL / 2)}; // 10px
}
.dataGridChart__legend--numeric {
text-align: right;
}
.dataGridChart__legendBoolean {
width: #{$euiSizeXS * 2.5} // 10px
}
/* Override to align column header to bottom of cell when no chart is available */
.dataGrid .euiDataGridHeaderCell__content {
margin-top: auto;
}

View file

@ -7,7 +7,6 @@
import type { FC } from 'react';
import React from 'react';
import classNames from 'classnames';
import {
Axis,
@ -22,10 +21,9 @@ import type { EuiDataGridColumn } from '@elastic/eui';
import { isUnsupportedChartData, type ChartData } from '@kbn/ml-data-grid';
import './column_chart.scss';
import { i18n } from '@kbn/i18n';
import { useColumnChart } from './use_column_chart';
import { useColumnChartStyles } from './column_chart_styles';
interface Props {
chartData: ChartData;
@ -48,6 +46,7 @@ export const ColumnChart: FC<Props> = ({
isNumeric,
}) => {
const { data, legendText } = useColumnChart(chartData, columnType, maxChartColumns, isNumeric);
const styles = useColumnChartStyles();
return (
<div data-test-subj={dataTestSubj} style={{ width: '100%' }}>
@ -83,12 +82,7 @@ export const ColumnChart: FC<Props> = ({
/>
</Chart>
)}
<div
className={classNames('dataGridChart__legend', {
'dataGridChart__legend--numeric': columnType.schema === 'number',
})}
data-test-subj={`${dataTestSubj}-legend`}
>
<div css={styles.legend} data-test-subj={`${dataTestSubj}-legend`}>
{legendText}
</div>
{!hideLabel && <div data-test-subj={`${dataTestSubj}-id`}>{columnType.id}</div>}

View file

@ -0,0 +1,42 @@
/*
* 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 { useEuiTheme, useEuiFontSize, euiTextTruncate } from '@elastic/eui';
import { css } from '@emotion/react';
export const useColumnChartStyles = () => {
const { euiTheme } = useEuiTheme();
const { fontSize: euiFontSizeL } = useEuiFontSize('l');
return {
histogram: css({
width: '100%',
}),
legend: css`
${euiTextTruncate()};
color: ${euiTheme.colors.textSubdued};
display: block;
overflow-x: hidden;
font-style: italic;
font-weight: ${euiTheme.font.weight.regular};
text-align: left;
line-height: 1.1;
font-size: calc(${euiFontSizeL} / 2);
`,
legendNumeric: css({
textAlign: 'right',
}),
legendBoolean: css`
width: calc(${euiTheme.size.xs} * 2.5);
`,
dataGridHeader: css({
'.euiDataGridHeaderCell__content': {
marginTop: 'auto',
},
}),
};
};

View file

@ -11,6 +11,7 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { MetricDistributionChart, buildChartDataFromStats } from '../metric_distribution_chart';
import type { FieldVisConfig } from '../../types';
import { kibanaFieldFormat, formatSingleValue } from '../../../utils';
import { useColumnChartStyles } from './column_chart_styles';
const METRIC_DISTRIBUTION_CHART_WIDTH = 100;
const METRIC_DISTRIBUTION_CHART_HEIGHT = 10;
@ -20,6 +21,7 @@ export interface NumberContentPreviewProps {
}
export const IndexBasedNumberContentPreview: FC<NumberContentPreviewProps> = ({ config }) => {
const styles = useColumnChartStyles();
const { stats, fieldFormat, fieldName } = config;
const dataTestSubj = `dataVisualizerDataGridChart-${fieldName}`;
@ -43,7 +45,7 @@ export const IndexBasedNumberContentPreview: FC<NumberContentPreviewProps> = ({
return (
<div data-test-subj={dataTestSubj} style={{ width: '100%' }}>
<div className="dataGridChart__histogram" data-test-subj={`${dataTestSubj}-histogram`}>
<div css={styles.histogram} data-test-subj={`${dataTestSubj}-histogram`}>
<MetricDistributionChart
width={METRIC_DISTRIBUTION_CHART_WIDTH}
height={METRIC_DISTRIBUTION_CHART_HEIGHT}
@ -52,7 +54,7 @@ export const IndexBasedNumberContentPreview: FC<NumberContentPreviewProps> = ({
hideXAxis={true}
/>
</div>
<div className={'dataGridChart__legend'} data-test-subj={`${dataTestSubj}-legend`}>
<div css={styles.legend} data-test-subj={`${dataTestSubj}-legend`}>
{legendText && (
<>
<EuiFlexGroup
@ -61,15 +63,10 @@ export const IndexBasedNumberContentPreview: FC<NumberContentPreviewProps> = ({
responsive={false}
gutterSize="m"
>
<EuiFlexItem
className={'dataGridChart__legend'}
style={{ maxWidth: METRIC_DISTRIBUTION_CHART_WIDTH * 0.75 }}
>
<EuiFlexItem style={{ maxWidth: METRIC_DISTRIBUTION_CHART_WIDTH * 0.75 }}>
{kibanaFieldFormat(legendText.min, fieldFormat)}
</EuiFlexItem>
<EuiFlexItem className={'dataGridChart__legend'}>
{kibanaFieldFormat(legendText.max, fieldFormat)}
</EuiFlexItem>
<EuiFlexItem>{kibanaFieldFormat(legendText.max, fieldFormat)}</EuiFlexItem>
</EuiFlexGroup>
</>
)}

View file

@ -14,6 +14,9 @@ import type { NumericChartData, OrdinalChartData, UnsupportedChartData } from '@
import { isNumericChartData, isOrdinalChartData, isUnsupportedChartData } from '@kbn/ml-data-grid';
import { getFieldType, getLegendText, getXScaleType, useColumnChart } from './use_column_chart';
import type { useColumnChartStyles } from './column_chart_styles';
const styles = {} as ReturnType<typeof useColumnChartStyles>;
describe('getFieldType()', () => {
it('should return the Kibana field type for a given EUI data grid schema', () => {
@ -95,10 +98,12 @@ describe('isUnsupportedChartData()', () => {
describe('getLegendText()', () => {
it('should return the chart legend text for unsupported chart types', () => {
expect(getLegendText(validUnsupportedChartData, 20)).toBe('Chart not supported.');
expect(getLegendText(validUnsupportedChartData, 20, false, styles)).toBe(
'Chart not supported.'
);
});
it('should return the chart legend text for empty datasets', () => {
expect(getLegendText(validNumericChartData, 20)).toBe('');
expect(getLegendText(validNumericChartData, 20, false, styles)).toBe('');
});
it('should return the chart legend text for boolean chart types', () => {
const { getByText } = render(
@ -113,7 +118,9 @@ describe('getLegendText()', () => {
id: 'the-id',
type: 'boolean',
},
20
20,
false,
styles
)}
</>
);
@ -122,7 +129,12 @@ describe('getLegendText()', () => {
});
it('should return the chart legend text for ordinal chart data with less than max categories', () => {
expect(
getLegendText({ ...validOrdinalChartData, data: [{ key: 'cat', doc_count: 10 }] }, 20)
getLegendText(
{ ...validOrdinalChartData, data: [{ key: 'cat', doc_count: 10 }] },
20,
false,
styles
)
).toBe('10 categories');
});
it('should return the chart legend text for ordinal chart data with more than max categories', () => {
@ -133,7 +145,9 @@ describe('getLegendText()', () => {
cardinality: 30,
data: [{ key: 'cat', doc_count: 10 }],
},
20
20,
false,
styles
)
).toBe('top 20 of 30 categories');
});
@ -145,7 +159,9 @@ describe('getLegendText()', () => {
data: [{ key: 1, doc_count: 10 }],
stats: [1, 100],
},
20
20,
false,
styles
)
).toBe('1 - 100');
expect(
@ -155,7 +171,9 @@ describe('getLegendText()', () => {
data: [{ key: 1, doc_count: 10 }],
stats: [100, 100],
},
20
20,
false,
styles
)
).toBe('100');
expect(
@ -165,7 +183,9 @@ describe('getLegendText()', () => {
data: [{ key: 1, doc_count: 10 }],
stats: [1.2345, 6.3456],
},
20
20,
false,
styles
)
).toBe('1.23 - 6.35');
});

View file

@ -24,6 +24,7 @@ import {
type NumericDataItem,
type OrdinalDataItem,
} from '@kbn/ml-data-grid';
import { useColumnChartStyles } from './column_chart_styles';
const NON_AGGREGATABLE = 'non-aggregatable';
@ -77,7 +78,8 @@ type LegendText = string | JSX.Element;
export const getLegendText = (
chartData: ChartData,
maxChartColumns: number,
isNumeric = false
isNumeric = false,
styles: ReturnType<typeof useColumnChartStyles>
): LegendText => {
if (chartData.type === 'unsupported') {
return i18n.translate('xpack.dataVisualizer.dataGridChart.histogramNotAvailable', {
@ -95,12 +97,12 @@ export const getLegendText = (
<tbody>
<tr>
{chartData.data[0] !== undefined && (
<td className="dataGridChart__legendBoolean">
<td css={styles.legendBoolean}>
{chartData.data[0].key_as_string?.slice(0, 1) ?? ''}
</td>
)}
{chartData.data[1] !== undefined && (
<td className="dataGridChart__legendBoolean">
<td css={styles.legendBoolean}>
{chartData.data[1].key_as_string?.slice(0, 1) ?? ''}
</td>
)}
@ -163,6 +165,8 @@ export const useColumnChart = (
const xScaleType = getXScaleType(fieldType);
const styles = useColumnChartStyles();
const getColor = (d: ChartDataItem) => {
if (hoveredRow === undefined || hoveredRow === null) {
return BAR_COLOR;
@ -219,7 +223,7 @@ export const useColumnChart = (
return {
data,
legendText: getLegendText(chartData, maxChartColumns, isNumeric),
legendText: getLegendText(chartData, maxChartColumns, isNumeric, styles),
xScaleType,
};
};

View file

@ -26,6 +26,7 @@ import {
import { MetricDistributionChartTooltipHeader } from './metric_distribution_chart_tooltip_header';
import { kibanaFieldFormat } from '../../../utils';
import { useDataVizChartTheme } from '../../hooks';
import { useColumnChartStyles } from '../field_data_row/column_chart_styles';
export interface MetricDistributionChartData {
x: number;
@ -64,6 +65,8 @@ export const MetricDistributionChart: FC<Props> = ({
const theme = useDataVizChartTheme();
const styles = useColumnChartStyles();
const headerFormatter: TooltipHeaderFormatter = (tooltipData) => {
const xValue = tooltipData.value;
const chartPoint: MetricDistributionChartData | undefined = chartData.find(
@ -80,10 +83,7 @@ export const MetricDistributionChart: FC<Props> = ({
};
return (
<div
data-test-subj="dataVisualizerFieldDataMetricDistributionChart"
className="dataGridChart__histogram"
>
<div data-test-subj="dataVisualizerFieldDataMetricDistributionChart" css={styles.histogram}>
<Chart size={{ width, height }}>
<Tooltip headerFormatter={headerFormatter} />
<Settings

View file

@ -1,9 +0,0 @@
// Protect the rest of Kibana from ML generic namespacing
// SASSTODO: Prefix ml selectors instead
.ml-app {
// Components
@import 'components/anomalies_table/index'; // SASSTODO: This file overwrites EUI directly
@import 'components/job_selector/index';
@import 'components/rule_editor/index'; // SASSTODO: This file overwrites EUI directly
}

View file

@ -6,7 +6,6 @@
*/
import React, { type FC, useMemo } from 'react';
import './_index.scss';
import ReactDOM from 'react-dom';
import { pick } from 'lodash';

View file

@ -1,56 +0,0 @@
// SASSTODO: This file has several direct EUI overwrites that need to be removed
.ml-anomalies-table {
tr th:first-child,
tr td:first-child {
width: $euiSizeXL;
}
// SASSTODO: These need to be separate classes, not overrides
.euiTableCellContent {
.euiHealth {
font-size: inherit;
}
.detector-rules-icon {
margin-left: $euiSizeXS;
opacity: .5;
}
}
.euiContextMenuItem {
min-width: 150px;
}
.mlAnomalyCategoryExamples__header {
display: inline;
}
.mlAnomalyCategoryExamples__link {
width: 100%;
}
.category-example {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.mlAnomalyCategoryExamples {
padding: $euiSizeL;
}
.mlAnomalyCategoryExamples__item {
display: block;
white-space: wrap;
font-family: $euiCodeFontFamily;
}
.interim-result {
font-style: italic;
}
.ml-anomalies-table-details {
padding: $euiSizeM;
}
}

View file

@ -315,7 +315,7 @@ export const AnomaliesTable: FC<AnomaliesTableProps> = React.memo(
unsetShowFunction={handleUnsetShowFunction}
/>
<EuiInMemoryTable
className="ml-anomalies-table eui-textBreakWord"
className="eui-textBreakWord"
items={tableData.anomalies}
// TODO - fix type
columns={columns as Array<EuiBasicTableColumn<MlAnomaliesTableRecordExtended>>}

View file

@ -318,12 +318,22 @@ export function getColumns(
const examples = get(examplesByJobId, [item.jobId, item.entityValue], []);
return (
<EuiLink
className="mlAnomalyCategoryExamples__link"
css={{
width: '100%',
}}
onClick={() => toggleRow(item, ANOMALIES_TABLE_TABS.CATEGORY_EXAMPLES)}
>
{examples.map((example, i) => {
return (
<span key={`example${i}`} className="category-example">
<span
key={`example${i}`}
css={{
display: 'block',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}}
>
{example}
</span>
);

View file

@ -122,20 +122,23 @@ const Contents: FC<{
filter?: EntityCellFilter;
influencerFilter?: EntityCellFilter;
}> = ({ anomaly, isAggregatedData, filter, influencersLimit, influencerFilter, job }) => {
const {
euiTheme: { colors },
} = useEuiTheme();
const { euiTheme } = useEuiTheme();
const dividerStyle = useMemo(() => {
return isPopulatedObject(anomaly.source.anomaly_score_explanation)
? { borderRight: `1px solid ${colors.lightShade}` }
? { borderRight: `1px solid ${euiTheme.colors.lightShade}` }
: {};
}, [colors, anomaly]);
}, [euiTheme.colors, anomaly]);
return (
<EuiFlexGroup>
<EuiFlexItem>
<div className="ml-anomalies-table-details" data-test-subj="mlAnomaliesListRowDetails">
<div
css={{
padding: euiTheme.size.m,
}}
data-test-subj="mlAnomaliesListRowDetails"
>
<Description anomaly={anomaly} />
<EuiSpacer size="m" />
@ -210,7 +213,11 @@ const Details: FC<{
{isInterimResult === true && (
<>
<EuiIcon type="warning" />
<span className="interim-result">
<span
css={{
fontStyle: 'italic',
}}
>
<FormattedMessage
id="xpack.ml.anomaliesTable.anomalyDetails.interimResultLabel"
defaultMessage="Interim result"
@ -306,18 +313,25 @@ const CategoryExamples: FC<{ definition?: CategoryDefinition; examples: string[]
definition,
examples,
}) => {
const { euiTheme } = useEuiTheme();
return (
<EuiFlexGroup
direction="column"
justifyContent="center"
gutterSize="xs"
className="mlAnomalyCategoryExamples"
css={{
padding: euiTheme.size.l,
}}
>
{definition !== undefined && definition.terms && (
<>
<EuiFlexItem key={`example-terms`}>
<EuiText size="xs">
<h4 className="mlAnomalyCategoryExamples__header">
<h4
css={{
display: 'inline',
}}
>
{i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.termsTitle', {
defaultMessage: 'Terms',
})}
@ -352,7 +366,11 @@ const CategoryExamples: FC<{ definition?: CategoryDefinition; examples: string[]
<>
<EuiFlexItem key={`example-regex`}>
<EuiText size="xs">
<h4 className="mlAnomalyCategoryExamples__header">
<h4
css={{
display: 'inline',
}}
>
{i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.regexTitle', {
defaultMessage: 'Regex',
})}
@ -396,7 +414,13 @@ const CategoryExamples: FC<{ definition?: CategoryDefinition; examples: string[]
</h4>
</EuiText>
)}
<span className="mlAnomalyCategoryExamples__item">{example}</span>
<span
css={{
fontFamily: euiTheme.font.familyCode,
}}
>
{example}
</span>
</EuiFlexItem>
);
})}

View file

@ -8,7 +8,7 @@
import PropTypes from 'prop-types';
import React from 'react';
import { EuiIcon, EuiToolTip } from '@elastic/eui';
import { EuiIcon, EuiToolTip, useEuiTheme } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
/*
@ -16,6 +16,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
* description of the detector, and an icon if rules have been configured for the detector.
*/
export function DetectorCell({ detectorDescription, numberOfRules }) {
const { euiTheme } = useEuiTheme();
let rulesIcon;
if (numberOfRules !== undefined && numberOfRules > 0) {
rulesIcon = (
@ -27,7 +28,12 @@ export function DetectorCell({ detectorDescription, numberOfRules }) {
/>
}
>
<EuiIcon type="controlsHorizontal" className="detector-rules-icon" />
<EuiIcon
type="controlsHorizontal"
css={{
marginLeft: euiTheme.size.xs,
}}
/>
</EuiToolTip>
);
}

View file

@ -41,6 +41,8 @@ export const SeverityCell: FC<SeverityCellProps> = memo(({ score, isMultiBucketA
<EuiFlexItem grow={false}>{severity}</EuiFlexItem>
</EuiFlexGroup>
) : (
<EuiHealth color={color}>{severity}</EuiHealth>
<EuiHealth textSize="inherit" color={color}>
{severity}
</EuiHealth>
);
});

View file

@ -1,35 +0,0 @@
.mlJobSelectorFlyoutBody>.euiFlyoutBody__overflow {
padding-top: $euiSizeS;
}
.mlJobSelector__ganttBar {
background-color: #79ADDA;
height: $euiSizeM;
border-radius: 2px;
}
.mlJobSelector__ganttBarBackEdge {
height: $euiSize;
border-left: 1px solid #D6D6D6;
border-right: 1px solid #D6D6D6;
margin-bottom: -14px;
padding-top: $euiSizeS;
}
.mlJobSelector__ganttBarDashed {
height: 1px;
border-top: 1px dashed #D6D6D6;
}
.mlJobSelector__ganttBarRunning {
background-image: linear-gradient(45deg,
rgba(255, 255, 255, .15) 25%,
transparent 25%,
transparent 50%,
rgba(255, 255, 255, .15) 50%,
rgba(255, 255, 255, .15) 75%,
transparent 75%,
transparent);
background-size: $euiSizeXXL $euiSizeXXL;
animation: progress-bar-stripes 2s linear infinite;
}

View file

@ -10,7 +10,6 @@ import React, { useState, useEffect, useMemo, useCallback } from 'react';
import { EuiButtonEmpty, EuiFlexItem, EuiFlexGroup, EuiHorizontalRule } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import './_index.scss';
import { ML_PAGES } from '../../../locator';
import type { Dictionary } from '../../../../common/types/common';

View file

@ -202,7 +202,7 @@ export const JobSelectorFlyoutContent: FC<JobSelectorFlyoutProps> = ({
</EuiTitle>
</EuiFlyoutHeader>
<EuiFlyoutBody className="mlJobSelectorFlyoutBody" data-test-subj={'mlJobSelectorFlyoutBody'}>
<EuiFlyoutBody data-test-subj={'mlJobSelectorFlyoutBody'}>
<EuiResizeObserver onResize={handleResize}>
{(resizeRef) => (
<div

View file

@ -8,22 +8,26 @@
import React, { Fragment } from 'react';
import { PropTypes } from 'prop-types';
import { EuiToolTip } from '@elastic/eui';
import { useTimerangeBarStyles } from './timerange_bar_styles';
export function TimeRangeBar({ isRunning, timerange, ganttBarWidth }) {
const styles = useTimerangeBarStyles();
const style = {
width: timerange.widthPx,
marginLeft: timerange.fromPx,
};
const className = `mlJobSelector__ganttBar${isRunning ? ' mlJobSelector__ganttBarRunning' : ''}`;
return (
<EuiToolTip position="top" content={timerange.label}>
<Fragment>
<div className="mlJobSelector__ganttBarBackEdge">
<div className="mlJobSelector__ganttBarDashed" style={{ width: `${ganttBarWidth}px` }} />
<div css={styles.ganttBarBackEdge}>
<div css={styles.ganttBarDashed} style={{ width: `${ganttBarWidth}px` }} />
</div>
<div style={style} className={className} />
<div
css={[styles.ganttBar, ...(isRunning ? [styles.ganttBarRunning] : [])]}
style={style}
data-test-subj={`mlJobSelectorGanttBar${isRunning ? 'Running' : ''}`}
/>
</Fragment>
</EuiToolTip>
);

View file

@ -18,17 +18,15 @@ describe('TimeRangeBar', () => {
test('Renders gantt bar when isRunning is false', () => {
const wrapper = mount(<TimeRangeBar timerange={timeRange} />);
const ganttBar = wrapper.find('.mlJobSelector__ganttBar');
const ganttBar = wrapper.find('[data-test-subj="mlJobSelectorGanttBar"]');
expect(
ganttBar.containsMatchingElement(<div className="mlJobSelector__ganttBar" />)
).toBeTruthy();
expect(ganttBar).toHaveLength(1);
});
test('Renders running animation bar when isRunning is true', () => {
const wrapper = mount(<TimeRangeBar timerange={timeRange} isRunning={true} />);
const runningBar = wrapper.find('.mlJobSelector__ganttBarRunning');
const runningBar = wrapper.find('[data-test-subj="mlJobSelectorGanttBarRunning"]');
expect(runningBar.length).toEqual(1);
expect(runningBar).toHaveLength(1);
});
});

View file

@ -0,0 +1,44 @@
/*
* 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 { useEuiTheme } from '@elastic/eui';
import { css } from '@emotion/react';
export const useTimerangeBarStyles = () => {
const { euiTheme } = useEuiTheme();
return {
ganttBar: css({
backgroundColor: euiTheme.colors.vis.euiColorVis2,
height: euiTheme.size.m,
borderRadius: '2px',
}),
ganttBarBackEdge: css({
height: euiTheme.size.base,
borderLeft: `1px solid ${euiTheme.colors.borderBasePlain}`,
borderRight: `1px solid ${euiTheme.colors.borderBasePlain}`,
marginBottom: '-14px',
paddingTop: euiTheme.size.s,
}),
ganttBarDashed: css({
height: '1px',
borderTop: `1px dashed ${euiTheme.colors.borderBasePlain}`,
}),
ganttBarRunning: css({
backgroundImage: `linear-gradient(45deg,
rgba(255, 255, 255, .15) 25%,
transparent 25%,
transparent 50%,
rgba(255, 255, 255, .15) 50%,
rgba(255, 255, 255, .15) 75%,
transparent 75%,
transparent)`,
backgroundSize: `${euiTheme.size.xxl} ${euiTheme.size.xxl}`,
animation: 'progress-bar-stripes 2s linear infinite',
}),
};
};

View file

@ -8,7 +8,6 @@ exports[`RuleEditorFlyout renders the flyout after adding a condition to a rule
<Fragment>
<EuiFlyout
aria-labelledby="flyoutTitle"
className="ml-rule-editor-flyout"
data-test-subj="mlRuleEditorFlyout"
onClose={[Function]}
>
@ -143,7 +142,6 @@ exports[`RuleEditorFlyout renders the flyout after adding a condition to a rule
/>
<EuiCheckbox
checked={true}
className="scope-enable-checkbox"
disabled={true}
id="enable_conditions_checkbox"
label="Add numeric conditions for when the job rule applies. Multiple conditions are combined using AND."
@ -243,7 +241,6 @@ exports[`RuleEditorFlyout renders the flyout after setting the rule to edit 1`]
<Fragment>
<EuiFlyout
aria-labelledby="flyoutTitle"
className="ml-rule-editor-flyout"
data-test-subj="mlRuleEditorFlyout"
onClose={[Function]}
>
@ -392,7 +389,6 @@ exports[`RuleEditorFlyout renders the flyout after setting the rule to edit 1`]
/>
<EuiCheckbox
checked={true}
className="scope-enable-checkbox"
disabled={true}
id="enable_conditions_checkbox"
label="Add numeric conditions for when the job rule applies. Multiple conditions are combined using AND."
@ -492,7 +488,6 @@ exports[`RuleEditorFlyout renders the flyout for creating a rule with conditions
<Fragment>
<EuiFlyout
aria-labelledby="flyoutTitle"
className="ml-rule-editor-flyout"
data-test-subj="mlRuleEditorFlyout"
onClose={[Function]}
>
@ -627,7 +622,6 @@ exports[`RuleEditorFlyout renders the flyout for creating a rule with conditions
/>
<EuiCheckbox
checked={true}
className="scope-enable-checkbox"
disabled={true}
id="enable_conditions_checkbox"
label="Add numeric conditions for when the job rule applies. Multiple conditions are combined using AND."
@ -719,7 +713,6 @@ exports[`RuleEditorFlyout renders the select action component for a detector wit
<Fragment>
<EuiFlyout
aria-labelledby="flyoutTitle"
className="ml-rule-editor-flyout"
onClose={[Function]}
>
<EuiFlyoutHeader

View file

@ -2,10 +2,10 @@
exports[`ScopeExpression renders when empty list of filter IDs is supplied 1`] = `
<EuiFlexGroup
alignItems="center"
gutterSize="m"
>
<EuiFlexItem
className="scope-field-checkbox"
grow={false}
>
<EuiCheckbox
@ -18,7 +18,11 @@ exports[`ScopeExpression renders when empty list of filter IDs is supplied 1`] =
grow={false}
>
<EuiExpression
className="scope-field-button"
css={
Object {
"pointerEvents": "none",
}
}
description={
<Memo(MemoizedFormattedMessage)
defaultMessage="when"
@ -35,10 +39,10 @@ exports[`ScopeExpression renders when empty list of filter IDs is supplied 1`] =
exports[`ScopeExpression renders when enabled set to false 1`] = `
<EuiFlexGroup
alignItems="center"
gutterSize="m"
>
<EuiFlexItem
className="scope-field-checkbox"
grow={false}
>
<EuiCheckbox
@ -51,7 +55,11 @@ exports[`ScopeExpression renders when enabled set to false 1`] = `
grow={false}
>
<EuiExpression
className="scope-field-button"
css={
Object {
"pointerEvents": "none",
}
}
description={
<Memo(MemoizedFormattedMessage)
defaultMessage="when"
@ -176,10 +184,10 @@ exports[`ScopeExpression renders when enabled set to false 1`] = `
exports[`ScopeExpression renders when filter ID and type supplied 1`] = `
<EuiFlexGroup
alignItems="center"
gutterSize="m"
>
<EuiFlexItem
className="scope-field-checkbox"
grow={false}
>
<EuiCheckbox
@ -192,7 +200,11 @@ exports[`ScopeExpression renders when filter ID and type supplied 1`] = `
grow={false}
>
<EuiExpression
className="scope-field-button"
css={
Object {
"pointerEvents": "none",
}
}
description={
<Memo(MemoizedFormattedMessage)
defaultMessage="when"
@ -317,10 +329,10 @@ exports[`ScopeExpression renders when filter ID and type supplied 1`] = `
exports[`ScopeExpression renders when no filter ID or type supplied 1`] = `
<EuiFlexGroup
alignItems="center"
gutterSize="m"
>
<EuiFlexItem
className="scope-field-checkbox"
grow={false}
>
<EuiCheckbox
@ -333,7 +345,11 @@ exports[`ScopeExpression renders when no filter ID or type supplied 1`] = `
grow={false}
>
<EuiExpression
className="scope-field-button"
css={
Object {
"pointerEvents": "none",
}
}
description={
<Memo(MemoizedFormattedMessage)
defaultMessage="when"

View file

@ -1,81 +0,0 @@
// SASSTODO: This file needs a rewrite. It is overwriting EUI in very dangerous / brittle ways.
.ml-rule-editor-flyout {
font-size: $euiFontSizeS;
.select-rule-action-panel {
padding: $euiSizeS 0;
// SASSTODO: Dangerous EUI overwrite
.euiDescriptionList {
row-gap: $euiSizeXS;
.euiDescriptionList__title {
padding: 0 $euiSize;
}
.euiDescriptionList__title:nth-child(1),
.euiDescriptionList__description:nth-child(2) {
color: $euiTitleColor;
font-weight: $euiFontWeightBold;
border-bottom: $euiBorderThin;
padding-bottom: $euiSizeM;
}
.euiDescriptionList__title:nth-child(3),
.euiDescriptionList__description:nth-child(4) {
padding-top: $euiSizeS;
}
}
}
// SASSTODO: Dangerous EUI overwrite
.scope-enable-checkbox {
.euiCheckbox__label {
color: inherit;
}
}
// SASSTODO: Dangerous EUI overwrite
.scope-field-checkbox {
margin-right: calc($euiSizeXS / 2);
.euiCheckbox {
margin-top: $euiSizeXS;
}
}
.scope-field-button {
pointer-events: none;
border-bottom: none;
}
.scope-edit-filter-link {
line-height: $euiSizeXL;
font-size: $euiFontSizeXS;
}
// SASSTODO: Needs proper calculated values
.condition-edit-value-field {
width: 170px;
height: 28px;
margin: 0 2px;
input {
height: 28px;
}
}
.euiExpressionButton.disabled {
pointer-events: none;
.euiExpressionButton__value,
.euiExpressionButton__description {
color: $euiColorLightShade;
}
}
.text-highlight {
font-weight: $euiFontWeightBold;
}
}

View file

@ -509,11 +509,7 @@ class RuleEditorFlyoutUI extends Component {
if (ruleIndex === -1) {
flyout = (
<EuiFlyout
className="ml-rule-editor-flyout"
onClose={this.closeFlyout}
aria-labelledby="flyoutTitle"
>
<EuiFlyout onClose={this.closeFlyout} aria-labelledby="flyoutTitle">
<EuiFlyoutHeader hasBorder={true}>
<EuiTitle size="m">
<h1 id="flyoutTitle">
@ -571,7 +567,6 @@ class RuleEditorFlyoutUI extends Component {
flyout = (
<EuiFlyout
data-test-subj="mlRuleEditorFlyout"
className="ml-rule-editor-flyout"
onClose={this.closeFlyout}
aria-labelledby="flyoutTitle"
>
@ -648,7 +643,6 @@ class RuleEditorFlyoutUI extends Component {
{conditionSupported === true ? (
<EuiCheckbox
id="enable_conditions_checkbox"
className="scope-enable-checkbox"
label={conditionsText}
checked={isConditionsEnabled}
onChange={this.onConditionsEnabledChange}

View file

@ -117,8 +117,8 @@ export class ScopeExpression extends Component {
const { fieldName, filterId, filterType, enabled, filterListIds } = this.props;
return (
<EuiFlexGroup gutterSize="m">
<EuiFlexItem grow={false} className="scope-field-checkbox">
<EuiFlexGroup gutterSize="m" alignItems="center">
<EuiFlexItem grow={false}>
<EuiCheckbox
id={`scope_cb_${fieldName}`}
checked={enabled}
@ -127,7 +127,6 @@ export class ScopeExpression extends Component {
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiExpression
className="scope-field-button"
description={
<FormattedMessage
id="xpack.ml.ruleEditor.scopeExpression.scopeFieldWhenLabel"
@ -136,6 +135,9 @@ export class ScopeExpression extends Component {
}
value={fieldName}
isActive={false}
css={{
pointerEvents: 'none',
}}
onClick={(event) => event.preventDefault()}
/>
</EuiFlexItem>

View file

@ -23,7 +23,6 @@ exports[`EditConditionLink renders for a condition using actual 1`] = `
</EuiText>
</EuiFlexItem>
<EuiFlexItem
className="condition-edit-value-field"
grow={false}
>
<EuiFieldNumber
@ -73,7 +72,6 @@ exports[`EditConditionLink renders for a condition using diff from typical 1`] =
</EuiText>
</EuiFlexItem>
<EuiFlexItem
className="condition-edit-value-field"
grow={false}
>
<EuiFieldNumber
@ -123,7 +121,6 @@ exports[`EditConditionLink renders for a condition using typical 1`] = `
</EuiText>
</EuiFlexItem>
<EuiFlexItem
className="condition-edit-value-field"
grow={false}
>
<EuiFieldNumber

View file

@ -79,7 +79,7 @@ export class EditConditionLink extends Component {
/>
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false} className="condition-edit-value-field">
<EuiFlexItem grow={false}>
<EuiFieldNumber
placeholder={i18n.translate(
'xpack.ml.ruleEditor.editConditionLink.enterValuePlaceholder',

View file

@ -198,7 +198,7 @@ export class RuleActionPanel extends Component {
);
return (
<EuiPanel paddingSize="m" className="select-rule-action-panel">
<EuiPanel paddingSize="m">
<EuiDescriptionList
type="column"
columnWidths={[15, 85]}