From 6bcd396a593882fd79f55afbce08965e763028bf Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 1 Oct 2024 23:58:07 +1000 Subject: [PATCH] [8.x] fix(slo): timestamp field was allowing mutliple values (#194311) (#194562) # Backport This will backport the following commits from `main` to `8.x`: - [fix(slo): timestamp field was allowing mutliple values (#194311)](https://github.com/elastic/kibana/pull/194311) ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) Co-authored-by: Kevin Delemme --- .../components/common/group_by_field.tsx | 30 +------ ...lector.tsx => group_by_field_selector.tsx} | 67 ++++++++-------- .../common/timestamp_field_selector.tsx | 79 +++++++++++++++++++ .../index_and_timestamp_field.tsx | 19 ++--- 4 files changed, 124 insertions(+), 71 deletions(-) rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/common/{index_field_selector.tsx => group_by_field_selector.tsx} (59%) create mode 100644 x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/common/timestamp_field_selector.tsx diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/common/group_by_field.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/common/group_by_field.tsx index 939755b8a9e3e..a7450bfb2a465 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/common/group_by_field.tsx +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/common/group_by_field.tsx @@ -5,16 +5,13 @@ * 2.0. */ -import { ALL_VALUE, QuerySchema } from '@kbn/slo-schema'; -import { i18n } from '@kbn/i18n'; -import { EuiIconTip } from '@elastic/eui'; -import React from 'react'; import { DataView, FieldSpec } from '@kbn/data-views-plugin/common'; +import { QuerySchema } from '@kbn/slo-schema'; +import React from 'react'; import { useFormContext } from 'react-hook-form'; -import { OptionalText } from './optional_text'; import { CreateSLOForm } from '../../types'; -import { IndexFieldSelector } from './index_field_selector'; import { GroupByCardinality } from './group_by_cardinality'; +import { GroupByFieldSelector } from './group_by_field_selector'; export function GroupByField({ dataView, @@ -32,27 +29,8 @@ export function GroupByField({ return ( <> - - {i18n.translate('xpack.slo.sloEdit.groupBy.label', { - defaultMessage: 'Group by', - })}{' '} - - - } - labelAppend={} - placeholder={i18n.translate('xpack.slo.sloEdit.groupBy.placeholder', { - defaultMessage: 'Select an optional field to group by', - })} isLoading={!!index && isLoading} isDisabled={!index} /> diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/common/index_field_selector.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/common/group_by_field_selector.tsx similarity index 59% rename from x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/common/index_field_selector.tsx rename to x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/common/group_by_field_selector.tsx index 0a277900ac31f..c45cc1d337aad 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/common/index_field_selector.tsx +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/common/group_by_field_selector.tsx @@ -5,35 +5,27 @@ * 2.0. */ -import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow } from '@elastic/eui'; -import React, { useEffect, useState, ReactNode } from 'react'; -import { Controller, useFormContext } from 'react-hook-form'; +import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow, EuiIconTip } from '@elastic/eui'; import { FieldSpec } from '@kbn/data-views-plugin/common'; -import { createOptionsFromFields, Option } from '../../helpers/create_options'; +import { i18n } from '@kbn/i18n'; +import { ALL_VALUE } from '@kbn/slo-schema'; +import React, { useEffect, useState } from 'react'; +import { Controller, useFormContext } from 'react-hook-form'; +import { Option, createOptionsFromFields } from '../../helpers/create_options'; import { CreateSLOForm } from '../../types'; +import { OptionalText } from './optional_text'; interface Props { indexFields: FieldSpec[]; - name: 'groupBy' | 'indicator.params.timestampField'; - label: ReactNode | string; - placeholder: string; isDisabled: boolean; isLoading: boolean; - isRequired?: boolean; - defaultValue?: string; - labelAppend?: ReactNode; } -export function IndexFieldSelector({ - indexFields, - name, - label, - labelAppend, - placeholder, - isDisabled, - isLoading, - isRequired = false, - defaultValue = '', -}: Props) { + +const placeholder = i18n.translate('xpack.slo.sloEdit.groupBy.placeholder', { + defaultMessage: 'Select an optional field to group by', +}); + +export function GroupByFieldSelector({ indexFields, isDisabled, isLoading }: Props) { const { control, getFieldState } = useFormContext(); const [options, setOptions] = useState(createOptionsFromFields(indexFields)); @@ -41,10 +33,10 @@ export function IndexFieldSelector({ setOptions(createOptionsFromFields(indexFields)); }, [indexFields]); - const getSelectedItems = (value: string | string[], fields: FieldSpec[]) => { + const getSelectedItems = (value: string | string[]) => { const values = [value].flat(); const selectedItems: Array> = []; - fields.forEach((field) => { + indexFields.forEach((field) => { if (values.includes(field.name)) { selectedItems.push({ value: field.name, label: field.name }); } @@ -53,12 +45,27 @@ export function IndexFieldSelector({ }; return ( - + + {i18n.translate('xpack.slo.sloEdit.groupBy.label', { + defaultMessage: 'Group by', + })}{' '} + + + } + isInvalid={getFieldState('groupBy').invalid} + labelAppend={} + > { return ( @@ -75,7 +82,7 @@ export function IndexFieldSelector({ return field.onChange(selected.map((selection) => selection.value)); } - field.onChange(defaultValue); + field.onChange([ALL_VALUE]); }} options={options} onSearchChange={(searchValue: string) => { @@ -83,9 +90,7 @@ export function IndexFieldSelector({ createOptionsFromFields(indexFields, ({ value }) => value.includes(searchValue)) ); }} - selectedOptions={ - !!indexFields && !!field.value ? getSelectedItems(field.value, indexFields) : [] - } + selectedOptions={!!indexFields && !!field.value ? getSelectedItems(field.value) : []} /> ); }} diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/common/timestamp_field_selector.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/common/timestamp_field_selector.tsx new file mode 100644 index 0000000000000..dc3289ca895c1 --- /dev/null +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/common/timestamp_field_selector.tsx @@ -0,0 +1,79 @@ +/* + * 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 { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow } from '@elastic/eui'; +import { FieldSpec } from '@kbn/data-views-plugin/common'; +import { i18n } from '@kbn/i18n'; +import React, { useEffect, useState } from 'react'; +import { Controller, useFormContext } from 'react-hook-form'; +import { Option, createOptionsFromFields } from '../../helpers/create_options'; +import { CreateSLOForm } from '../../types'; + +interface Props { + fields: FieldSpec[]; + isDisabled: boolean; + isLoading: boolean; +} + +const placeholder = i18n.translate('xpack.slo.sloEdit.timestampField.placeholder', { + defaultMessage: 'Select a timestamp field', +}); + +export function TimestampFieldSelector({ fields, isDisabled, isLoading }: Props) { + const { control, getFieldState } = useFormContext(); + const [options, setOptions] = useState(createOptionsFromFields(fields)); + + useEffect(() => { + setOptions(createOptionsFromFields(fields)); + }, [fields]); + + return ( + + { + return ( + + {...field} + async + placeholder={placeholder} + aria-label={placeholder} + isClearable + isDisabled={isLoading || isDisabled} + isInvalid={fieldState.invalid} + isLoading={isLoading} + onChange={(selected: EuiComboBoxOptionOption[]) => { + if (selected.length) { + return field.onChange(selected[0].value); + } + + field.onChange(''); + }} + singleSelection={{ asPlainText: true }} + options={options} + onSearchChange={(searchValue: string) => { + setOptions( + createOptionsFromFields(fields, ({ value }) => value.includes(searchValue)) + ); + }} + selectedOptions={ + !!fields && !!field.value ? [{ value: field.value, label: field.value }] : [] + } + /> + ); + }} + /> + + ); +} diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_common/index_and_timestamp_field.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_common/index_and_timestamp_field.tsx index b4b5bdd4557d4..7059810a5aae0 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_common/index_and_timestamp_field.tsx +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_common/index_and_timestamp_field.tsx @@ -6,13 +6,12 @@ */ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import React from 'react'; import { DataView } from '@kbn/data-views-plugin/public'; +import React from 'react'; import { useFormContext } from 'react-hook-form'; -import { IndexSelection } from './index_selection'; -import { IndexFieldSelector } from '../common/index_field_selector'; import { CreateSLOForm } from '../../types'; +import { TimestampFieldSelector } from '../common/timestamp_field_selector'; +import { IndexSelection } from './index_selection'; export function IndexAndTimestampField({ dataView, @@ -32,18 +31,10 @@ export function IndexAndTimestampField({ -