Skip to content

Commit

Permalink
[ML] Fixes for anomaly swim lane (#80299) (#80357)
Browse files Browse the repository at this point in the history
* [ML] add swim lane styles for dark theme

* [ML] fix global time range update on sell selection

* [ML] fix getSelectionTimeRange

* [ML] fix range selection for embeddable

* [ML] fix job selection

* [ML] fix swim lane limit
  • Loading branch information
darnautov authored Oct 13, 2020
1 parent 651d052 commit 9fd8212
Show file tree
Hide file tree
Showing 12 changed files with 123 additions and 109 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useCallback } from 'react';

import { EuiButtonEmpty, EuiFlexItem, EuiFlexGroup, EuiFlyout } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
Expand Down Expand Up @@ -109,24 +109,22 @@ export function JobSelector({ dateFormatTz, singleSelection, timeseriesOnly }: J
showFlyout();
}

const applySelection: JobSelectorFlyoutProps['onSelectionConfirmed'] = ({
newSelection,
jobIds,
groups: newGroups,
time,
}) => {
setSelectedIds(newSelection);

setGlobalState({
ml: {
jobIds,
groups: newGroups,
},
...(time !== undefined ? { time } : {}),
});

closeFlyout();
};
const applySelection: JobSelectorFlyoutProps['onSelectionConfirmed'] = useCallback(
({ newSelection, jobIds, groups: newGroups, time }) => {
setSelectedIds(newSelection);

setGlobalState({
ml: {
jobIds,
groups: newGroups,
},
...(time !== undefined ? { time } : {}),
});

closeFlyout();
},
[setGlobalState, setSelectedIds]
);

function renderJobSelectionBar() {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export const JobSelectorFlyoutContent: FC<JobSelectorFlyoutProps> = ({

const flyoutEl = useRef<HTMLElement | null>(null);

function applySelection() {
const applySelection = useCallback(() => {
// allNewSelection will be a list of all job ids (including those from groups) selected from the table
const allNewSelection: string[] = [];
const groupSelection: Array<{ groupId: string; jobIds: string[] }> = [];
Expand Down Expand Up @@ -110,7 +110,7 @@ export const JobSelectorFlyoutContent: FC<JobSelectorFlyoutProps> = ({
groups: groupSelection,
time,
});
}
}, [onSelectionConfirmed, newSelection, jobGroupsMaps, applyTimeRange]);

function removeId(id: string) {
setNewSelection(newSelection.filter((item) => item !== id));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export const useJobSelection = (jobs: MlJobWithTimeRange[]) => {
...(time !== undefined ? { time } : {}),
});
}
}, [jobs, validIds]);
}, [jobs, validIds, setGlobalState, globalState?.ml]);

return jobSelection;
};
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const DatePickerWrapper: FC = () => {
useEffect(() => {
setGlobalState({ refreshInterval });
timefilter.setRefreshInterval(refreshInterval);
}, [refreshInterval?.pause, refreshInterval?.value]);
}, [refreshInterval?.pause, refreshInterval?.value, setGlobalState]);

const [time, setTime] = useState(timefilter.getTime());
const [recentlyUsedRanges, setRecentlyUsedRanges] = useState(getRecentlyUsedRanges());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ export function getSelectionTimeRange(selectedCells, interval, bounds) {
latestMs = bounds.max.valueOf();
if (selectedCells.times[1] !== undefined) {
// Subtract 1 ms so search does not include start of next bucket.
latestMs = (selectedCells.times[1] + interval) * 1000 - 1;
latestMs = selectedCells.times[1] * 1000 - 1;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const useSelectedCells = (
setAppState('mlExplorerSwimlane', mlExplorerSwimlane);
}
},
[appState?.mlExplorerSwimlane, selectedCells]
[appState?.mlExplorerSwimlane, selectedCells, setAppState]
);

return [selectedCells, setSelectedCells];
Expand Down
136 changes: 74 additions & 62 deletions x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { getFormattedSeverityScore } from '../../../common/util/anomaly_utils';

import './_explorer.scss';
import { EMPTY_FIELD_VALUE_LABEL } from '../timeseriesexplorer/components/entity_control/entity_control';
import { useUiSettings } from '../contexts/kibana';

/**
* Ignore insignificant resize, e.g. browser scrollbar appearance.
Expand Down Expand Up @@ -159,6 +160,8 @@ export const SwimlaneContainer: FC<SwimlaneProps> = ({
}) => {
const [chartWidth, setChartWidth] = useState<number>(0);

const isDarkTheme = !!useUiSettings().get('theme:darkMode');

// Holds the container height for previously fetched data
const containerHeightRef = useRef<number>();

Expand Down Expand Up @@ -235,67 +238,76 @@ export const SwimlaneContainer: FC<SwimlaneProps> = ({
return { x: selection.times.map((v) => v * 1000), y: selection.lanes };
}, [selection, swimlaneData, swimlaneType]);

const swimLaneConfig: HeatmapSpec['config'] = useMemo(
() =>
showSwimlane
? {
onBrushEnd: (e: HeatmapBrushEvent) => {
onCellsSelection({
lanes: e.y as string[],
times: e.x.map((v) => (v as number) / 1000),
type: swimlaneType,
viewByFieldName: swimlaneData.fieldName,
});
},
grid: {
cellHeight: {
min: CELL_HEIGHT,
max: CELL_HEIGHT,
},
stroke: {
width: 1,
color: '#D3DAE6',
},
},
cell: {
maxWidth: 'fill',
maxHeight: 'fill',
label: {
visible: false,
},
border: {
stroke: '#D3DAE6',
strokeWidth: 0,
},
},
yAxisLabel: {
visible: true,
width: 170,
// eui color subdued
fill: `#6a717d`,
padding: 8,
formatter: (laneLabel: string) => {
return laneLabel === '' ? EMPTY_FIELD_VALUE_LABEL : laneLabel;
},
},
xAxisLabel: {
visible: showTimeline,
// eui color subdued
fill: `#98A2B3`,
formatter: (v: number) => {
timeBuckets.setInterval(`${swimlaneData.interval}s`);
const a = timeBuckets.getScaledDateFormat();
return moment(v).format(a);
},
},
brushMask: {
fill: 'rgb(247 247 247 / 50%)',
},
maxLegendHeight: LEGEND_HEIGHT,
}
: {},
[showSwimlane, swimlaneType, swimlaneData?.fieldName]
);
const swimLaneConfig: HeatmapSpec['config'] = useMemo(() => {
if (!showSwimlane) return {};

return {
onBrushEnd: (e: HeatmapBrushEvent) => {
onCellsSelection({
lanes: e.y as string[],
times: e.x.map((v) => (v as number) / 1000),
type: swimlaneType,
viewByFieldName: swimlaneData.fieldName,
});
},
grid: {
cellHeight: {
min: CELL_HEIGHT,
max: CELL_HEIGHT,
},
stroke: {
width: 1,
color: '#D3DAE6',
},
},
cell: {
maxWidth: 'fill',
maxHeight: 'fill',
label: {
visible: false,
},
border: {
stroke: '#D3DAE6',
strokeWidth: 0,
},
},
yAxisLabel: {
visible: true,
width: 170,
// eui color subdued
fill: `#6a717d`,
padding: 8,
formatter: (laneLabel: string) => {
return laneLabel === '' ? EMPTY_FIELD_VALUE_LABEL : laneLabel;
},
},
xAxisLabel: {
visible: showTimeline,
// eui color subdued
fill: `#98A2B3`,
formatter: (v: number) => {
timeBuckets.setInterval(`${swimlaneData.interval}s`);
const scaledDateFormat = timeBuckets.getScaledDateFormat();
return moment(v).format(scaledDateFormat);
},
},
brushMask: {
fill: isDarkTheme ? 'rgb(30,31,35,80%)' : 'rgb(247,247,247,50%)',
},
brushArea: {
stroke: isDarkTheme ? 'rgb(255, 255, 255)' : 'rgb(105, 112, 125)',
},
maxLegendHeight: LEGEND_HEIGHT,
timeZone: 'UTC',
};
}, [
showSwimlane,
swimlaneType,
swimlaneData?.fieldName,
isDarkTheme,
timeBuckets,
onCellsSelection,
]);

// @ts-ignore
const onElementClick: ElementClickListener = useCallback(
Expand All @@ -310,7 +322,7 @@ export const SwimlaneContainer: FC<SwimlaneProps> = ({
};
onCellsSelection(payload);
},
[swimlaneType, swimlaneData?.fieldName, swimlaneData?.interval]
[swimlaneType, swimlaneData?.fieldName, swimlaneData?.interval, onCellsSelection]
);

const tooltipOptions: TooltipSettings = useMemo(
Expand Down
12 changes: 7 additions & 5 deletions x-pack/plugins/ml/public/application/routing/routes/explorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,9 @@ const ExplorerUrlStateManager: FC<ExplorerUrlStateManagerProps> = ({ jobsWithTim
const { jobIds } = useJobSelection(jobsWithTimeRange);

const refresh = useRefresh();

useEffect(() => {
if (refresh !== undefined) {
if (refresh !== undefined && lastRefresh !== refresh.lastRefresh) {
setLastRefresh(refresh?.lastRefresh);

if (refresh.timeRange !== undefined) {
Expand All @@ -94,7 +95,7 @@ const ExplorerUrlStateManager: FC<ExplorerUrlStateManagerProps> = ({ jobsWithTim
});
}
}
}, [refresh?.lastRefresh]);
}, [refresh?.lastRefresh, lastRefresh, setLastRefresh, setGlobalState]);

// We cannot simply infer bounds from the globalState's `time` attribute
// with `moment` since it can contain custom strings such as `now-15m`.
Expand Down Expand Up @@ -194,6 +195,7 @@ const ExplorerUrlStateManager: FC<ExplorerUrlStateManagerProps> = ({ jobsWithTim
const [tableSeverity] = useTableSeverity();

const [selectedCells, setSelectedCells] = useSelectedCells(appState, setAppState);

useEffect(() => {
explorerService.setSelectedCells(selectedCells);
}, [JSON.stringify(selectedCells)]);
Expand All @@ -220,9 +222,9 @@ const ExplorerUrlStateManager: FC<ExplorerUrlStateManagerProps> = ({ jobsWithTim
if (explorerState && explorerState.swimlaneContainerWidth > 0) {
loadExplorerData({
...loadExplorerDataConfig,
swimlaneLimit:
isViewBySwimLaneData(explorerState?.viewBySwimlaneData) &&
explorerState?.viewBySwimlaneData.cardinality,
swimlaneLimit: isViewBySwimLaneData(explorerState?.viewBySwimlaneData)
? explorerState?.viewBySwimlaneData.cardinality
: undefined,
});
}
}, [JSON.stringify(loadExplorerDataConfig)]);
Expand Down
6 changes: 3 additions & 3 deletions x-pack/plugins/ml/public/application/util/url_state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,12 @@ export const useUrlState = (accessor: Accessor) => {
if (typeof fullUrlState === 'object') {
return fullUrlState[accessor];
}
return undefined;
}, [searchString]);

const setUrlState = useCallback(
(attribute: string | Dictionary<any>, value?: any) =>
setUrlStateContext(accessor, attribute, value),
(attribute: string | Dictionary<any>, value?: any) => {
setUrlStateContext(accessor, attribute, value);
},
[accessor, setUrlStateContext]
);
return [urlState, setUrlState];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import ReactDOM from 'react-dom';
import { CoreStart } from 'kibana/public';
import { i18n } from '@kbn/i18n';
import { Subject } from 'rxjs';
import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
import { Embeddable, IContainer } from '../../../../../../src/plugins/embeddable/public';
import { EmbeddableSwimLaneContainer } from './embeddable_swim_lane_container_lazy';
import type { JobId } from '../../../common/types/anomaly_detection_jobs';
Expand Down Expand Up @@ -59,17 +60,19 @@ export class AnomalySwimlaneEmbeddable extends Embeddable<

ReactDOM.render(
<I18nContext>
<Suspense fallback={null}>
<EmbeddableSwimLaneContainer
id={this.input.id}
embeddableContext={this}
embeddableInput={this.getInput$()}
services={this.services}
refresh={this.reload$.asObservable()}
onInputChange={this.updateInput.bind(this)}
onOutputChange={this.updateOutput.bind(this)}
/>
</Suspense>
<KibanaContextProvider services={{ ...this.services[0] }}>
<Suspense fallback={null}>
<EmbeddableSwimLaneContainer
id={this.input.id}
embeddableContext={this}
embeddableInput={this.getInput$()}
services={this.services}
refresh={this.reload$.asObservable()}
onInputChange={this.updateInput.bind(this)}
onOutputChange={this.updateOutput.bind(this)}
/>
</Suspense>
</KibanaContextProvider>
</I18nContext>,
node
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export const EmbeddableSwimLaneContainer: FC<ExplorerSwimlaneContainerProps> = (
});
}
},
[swimlaneData, perPage, fromPage]
[swimlaneData, perPage, fromPage, setSelectedCells]
);

if (error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ export function createApplyTimeRangeSelectionAction(

let [from, to] = data.times;
from = from * 1000;
// extend bounds with the interval
to = to * 1000 + interval * 1000;
to = to * 1000;

timefilter.setTime({
from: moment(from),
Expand Down

0 comments on commit 9fd8212

Please sign in to comment.