forked from elastic/kibana
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ML] Configure sorting for partition values on Single Metric Viewer (e…
…lastic#81510) (elastic#82295) * [ML] fix callout styles * [ML] refactor timeseriesexplorer.js, add series_controls.tsx, storage support for partition config * [ML] anomalousOnly support * [ML] sort by control * [ML] update query * [ML] sort order controls * [ML] adjust query * [ML] merge default and local configs, add info * [ML] fix types, adjust sorting logic for model plot results * [ML] fix translation keys * [ML] fixed size for the icon flex item * [ML] fix time range condition, refactor * [ML] change info messages and the icon color * Fix model plot info message Co-authored-by: István Zoltán Szabó <istvan.szabo@elastic.co> * [ML] functional tests * [ML] rename ML_ENTITY_FIELDS_CONFIG * [ML] support manual input * [ML] show max record score color indicator * [ML] use :checked selector * [ML] refactor functional tests * [ML] extend config with "applyTimeRange", refactor with entity_config.tsx * [ML] info messages * [ML] remove custom message * [ML] adjust the endpoint * [ML] customOptionText * [ML] sort by name UI tweak * [ML] change text * [ML] remove TODO comment * [ML] fix functional test * [ML] move "Anomalous only"/"Apply time range" control to the bottom of the popover * [ML] update types
- Loading branch information
Showing
23 changed files
with
1,210 additions
and
343 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import { EntityFieldType } from './anomalies'; | ||
|
||
export const ML_ENTITY_FIELDS_CONFIG = 'ml.singleMetricViewer.partitionFields'; | ||
|
||
export type PartitionFieldConfig = | ||
| { | ||
/** | ||
* Relevant for jobs with enabled model plot. | ||
* If true, entity values are based on records with anomalies. | ||
* Otherwise aggregated from the model plot results. | ||
*/ | ||
anomalousOnly: boolean; | ||
/** | ||
* Relevant for jobs with disabled model plot. | ||
* If true, entity values are filtered by the active time range. | ||
* If false, the lists consist of the values from all existing records. | ||
*/ | ||
applyTimeRange: boolean; | ||
sort: { | ||
by: 'anomaly_score' | 'name'; | ||
order: 'asc' | 'desc'; | ||
}; | ||
} | ||
| undefined; | ||
|
||
export type PartitionFieldsConfig = | ||
| Partial<Record<EntityFieldType, PartitionFieldConfig>> | ||
| undefined; | ||
|
||
export type MlStorage = Partial<{ | ||
[ML_ENTITY_FIELDS_CONFIG]: PartitionFieldsConfig; | ||
}> | null; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 32 additions & 0 deletions
32
x-pack/plugins/ml/public/application/contexts/ml/use_storage.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import { useCallback, useState } from 'react'; | ||
import { useMlKibana } from '../kibana'; | ||
|
||
/** | ||
* Hook for accessing and changing a value in the storage. | ||
* @param key - Storage key | ||
* @param initValue | ||
*/ | ||
export function useStorage<T>(key: string, initValue?: T): [T, (value: T) => void] { | ||
const { | ||
services: { storage }, | ||
} = useMlKibana(); | ||
|
||
const [val, setVal] = useState<T>(storage.get(key) ?? initValue); | ||
|
||
const setStorage = useCallback((value: T): void => { | ||
try { | ||
storage.set(key, value); | ||
setVal(value); | ||
} catch (e) { | ||
throw new Error('Unable to update storage with provided value'); | ||
} | ||
}, []); | ||
|
||
return [val, setStorage]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
227 changes: 227 additions & 0 deletions
227
...gins/ml/public/application/timeseriesexplorer/components/entity_control/entity_config.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import React, { FC, useMemo, useState } from 'react'; | ||
import { | ||
EuiButtonIcon, | ||
EuiFlexGroup, | ||
EuiFlexItem, | ||
EuiFormRow, | ||
EuiHorizontalRule, | ||
EuiIcon, | ||
EuiPopover, | ||
EuiRadioGroup, | ||
EuiRadioGroupOption, | ||
EuiSwitch, | ||
EuiToolTip, | ||
} from '@elastic/eui'; | ||
import { i18n } from '@kbn/i18n'; | ||
import { FormattedMessage } from '@kbn/i18n/react'; | ||
import { Entity } from './entity_control'; | ||
import { UiPartitionFieldConfig } from '../series_controls/series_controls'; | ||
import { EntityFieldType } from '../../../../../common/types/anomalies'; | ||
|
||
interface EntityConfigProps { | ||
entity: Entity; | ||
isModelPlotEnabled: boolean; | ||
config: UiPartitionFieldConfig; | ||
onConfigChange: (fieldType: EntityFieldType, config: Partial<UiPartitionFieldConfig>) => void; | ||
} | ||
|
||
export const EntityConfig: FC<EntityConfigProps> = ({ | ||
entity, | ||
isModelPlotEnabled, | ||
config, | ||
onConfigChange, | ||
}) => { | ||
const [isEntityConfigPopoverOpen, setIsEntityConfigPopoverOpen] = useState(false); | ||
|
||
const forceSortByName = isModelPlotEnabled && !config?.anomalousOnly; | ||
|
||
const sortOptions: EuiRadioGroupOption[] = useMemo(() => { | ||
return [ | ||
{ | ||
id: 'anomaly_score', | ||
label: i18n.translate('xpack.ml.timeSeriesExplorer.sortByScoreLabel', { | ||
defaultMessage: 'Anomaly score', | ||
}), | ||
disabled: forceSortByName, | ||
}, | ||
{ | ||
id: 'name', | ||
label: i18n.translate('xpack.ml.timeSeriesExplorer.sortByNameLabel', { | ||
defaultMessage: 'Name', | ||
}), | ||
}, | ||
]; | ||
}, [isModelPlotEnabled, config]); | ||
|
||
const orderOptions: EuiRadioGroupOption[] = useMemo(() => { | ||
return [ | ||
{ | ||
id: 'asc', | ||
label: i18n.translate('xpack.ml.timeSeriesExplorer.ascOptionsOrderLabel', { | ||
defaultMessage: 'asc', | ||
}), | ||
}, | ||
{ | ||
id: 'desc', | ||
label: i18n.translate('xpack.ml.timeSeriesExplorer.descOptionsOrderLabel', { | ||
defaultMessage: 'desc', | ||
}), | ||
}, | ||
]; | ||
}, []); | ||
|
||
return ( | ||
<EuiPopover | ||
ownFocus | ||
style={{ height: '40px' }} | ||
button={ | ||
<EuiButtonIcon | ||
color="text" | ||
iconSize="m" | ||
iconType="gear" | ||
aria-label={i18n.translate('xpack.ml.timeSeriesExplorer.editControlConfiguration', { | ||
defaultMessage: 'Edit field configuration', | ||
})} | ||
onClick={() => { | ||
setIsEntityConfigPopoverOpen(!isEntityConfigPopoverOpen); | ||
}} | ||
data-test-subj={`mlSingleMetricViewerEntitySelectionConfigButton_${entity.fieldName}`} | ||
/> | ||
} | ||
isOpen={isEntityConfigPopoverOpen} | ||
closePopover={() => { | ||
setIsEntityConfigPopoverOpen(false); | ||
}} | ||
> | ||
<div data-test-subj={`mlSingleMetricViewerEntitySelectionConfigPopover_${entity.fieldName}`}> | ||
<EuiFormRow | ||
label={ | ||
<FormattedMessage | ||
id="xpack.ml.timeSeriesExplorer.sortByLabel" | ||
defaultMessage="Sort by" | ||
/> | ||
} | ||
> | ||
<EuiRadioGroup | ||
options={sortOptions} | ||
idSelected={forceSortByName ? 'name' : config?.sort?.by} | ||
onChange={(id) => { | ||
onConfigChange(entity.fieldType, { | ||
sort: { | ||
order: config.sort.order, | ||
by: id as UiPartitionFieldConfig['sort']['by'], | ||
}, | ||
}); | ||
}} | ||
compressed | ||
data-test-subj={`mlSingleMetricViewerEntitySelectionConfigSortBy_${entity.fieldName}`} | ||
/> | ||
</EuiFormRow> | ||
<EuiFormRow | ||
label={ | ||
<FormattedMessage id="xpack.ml.timeSeriesExplorer.orderLabel" defaultMessage="Order" /> | ||
} | ||
> | ||
<EuiRadioGroup | ||
options={orderOptions} | ||
idSelected={config?.sort?.order} | ||
onChange={(id) => { | ||
onConfigChange(entity.fieldType, { | ||
sort: { | ||
by: config.sort.by, | ||
order: id as UiPartitionFieldConfig['sort']['order'], | ||
}, | ||
}); | ||
}} | ||
compressed | ||
data-test-subj={`mlSingleMetricViewerEntitySelectionConfigOrder_${entity.fieldName}`} | ||
/> | ||
</EuiFormRow> | ||
|
||
<EuiHorizontalRule margin="s" /> | ||
|
||
<EuiFlexGroup gutterSize={'xs'} alignItems={'center'}> | ||
<EuiFlexItem grow={false}> | ||
{isModelPlotEnabled ? ( | ||
<EuiSwitch | ||
label={ | ||
<FormattedMessage | ||
id="xpack.ml.timeSeriesExplorer.anomalousOnlyLabel" | ||
defaultMessage="Anomalous only" | ||
/> | ||
} | ||
checked={config.anomalousOnly} | ||
onChange={(e) => { | ||
const isAnomalousOnly = e.target.checked; | ||
onConfigChange(entity.fieldType, { | ||
anomalousOnly: isAnomalousOnly, | ||
sort: { | ||
order: config.sort.order, | ||
by: config.sort.by, | ||
}, | ||
}); | ||
}} | ||
compressed | ||
data-test-subj={`mlSingleMetricViewerEntitySelectionConfigAnomalousOnly_${entity.fieldName}`} | ||
/> | ||
) : ( | ||
<EuiSwitch | ||
label={ | ||
<FormattedMessage | ||
id="xpack.ml.timeSeriesExplorer.applyTimeRangeLabel" | ||
defaultMessage="Apply time range" | ||
/> | ||
} | ||
checked={config.applyTimeRange} | ||
onChange={(e) => { | ||
const applyTimeRange = e.target.checked; | ||
onConfigChange(entity.fieldType, { | ||
applyTimeRange, | ||
}); | ||
}} | ||
compressed | ||
data-test-subj={`mlSingleMetricViewerEntitySelectionConfigAnomalousOnly_${entity.fieldName}`} | ||
/> | ||
)} | ||
</EuiFlexItem> | ||
|
||
<EuiFlexItem grow={false} style={{ width: '16px' }}> | ||
{isModelPlotEnabled && !config?.anomalousOnly ? ( | ||
<EuiToolTip | ||
position="top" | ||
content={ | ||
<FormattedMessage | ||
id="xpack.ml.timeSeriesExplorer.nonAnomalousResultsWithModelPlotInfo" | ||
defaultMessage="The list contains values from the model plot results." | ||
/> | ||
} | ||
> | ||
<EuiIcon tabIndex={0} type="iInCircle" color={'subdued'} /> | ||
</EuiToolTip> | ||
) : null} | ||
|
||
{!isModelPlotEnabled && !config?.applyTimeRange ? ( | ||
<EuiToolTip | ||
position="top" | ||
content={ | ||
<FormattedMessage | ||
id="xpack.ml.timeSeriesExplorer.ignoreTimeRangeInfo" | ||
defaultMessage="The list contains values from all anomalies created during the lifetime of the job." | ||
/> | ||
} | ||
> | ||
<EuiIcon tabIndex={0} type="iInCircle" color={'subdued'} /> | ||
</EuiToolTip> | ||
) : null} | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
</div> | ||
</EuiPopover> | ||
); | ||
}; |
Oops, something went wrong.