Skip to content

Commit

Permalink
chore(slo): Improve form field selectors (elastic#167564)
Browse files Browse the repository at this point in the history
  • Loading branch information
kdelemme authored Sep 29, 2023
1 parent 8353a7e commit f7095d5
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 107 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/

import { EuiComboBox, EuiComboBoxOptionOption, EuiFlexItem, EuiFormRow } from '@elastic/eui';
import { ALL_VALUE } from '@kbn/slo-schema';
import React, { useEffect, useState } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import { Field } from '../../../../hooks/slo/use_fetch_index_pattern_fields';
Expand Down Expand Up @@ -42,7 +41,7 @@ export function IndexFieldSelector({
<EuiFlexItem>
<EuiFormRow label={label} isInvalid={getFieldState(name).invalid}>
<Controller
defaultValue={ALL_VALUE}
defaultValue=""
name={name}
control={control}
rules={{ required: isRequired }}
Expand All @@ -61,7 +60,7 @@ export function IndexFieldSelector({
return field.onChange(selected[0].value);
}

field.onChange(ALL_VALUE);
field.onChange('');
}}
options={options}
onSearchChange={(searchValue: string) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,18 @@ import { MetricIndicator } from './metric_indicator';

export { NEW_CUSTOM_METRIC } from './metric_indicator';

const SUPPORTED_METRIC_FIELD_TYPES = ['number', 'histogram'];

export function CustomMetricIndicatorTypeForm() {
const { watch } = useFormContext<CreateSLOForm>();
const index = watch('indicator.params.index');
const { isLoading: isIndexFieldsLoading, data: indexFields = [] } =
useFetchIndexPatternFields(index);
const timestampFields = indexFields.filter((field) => field.type === 'date');
const partitionByFields = indexFields.filter((field) => field.aggregatable);
const metricFields = indexFields.filter((field) =>
SUPPORTED_METRIC_FIELD_TYPES.includes(field.type)
);

return (
<>
Expand Down Expand Up @@ -115,7 +120,7 @@ export function CustomMetricIndicatorTypeForm() {
<EuiSpacer size="s" />
<MetricIndicator
type="good"
indexFields={indexFields}
metricFields={metricFields}
isLoadingIndex={isIndexFieldsLoading}
/>
</EuiFlexItem>
Expand All @@ -136,7 +141,7 @@ export function CustomMetricIndicatorTypeForm() {
<EuiSpacer size="s" />
<MetricIndicator
type="total"
indexFields={indexFields}
metricFields={metricFields}
isLoadingIndex={isIndexFieldsLoading}
/>
</EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@ import {
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { first, range, xor } from 'lodash';
import React from 'react';
import React, { useEffect, useState } from 'react';
import { Controller, useFieldArray, useFormContext } from 'react-hook-form';
import { Field } from '../../../../hooks/slo/use_fetch_index_pattern_fields';
import { createOptionsFromFields } from '../../helpers/create_options';
import { createOptionsFromFields, Option } from '../../helpers/create_options';
import { CreateSLOForm } from '../../types';
import { QueryBuilder } from '../common/query_builder';

interface MetricIndicatorProps {
type: 'good' | 'total';
indexFields: Field[];
metricFields: Field[];
isLoadingIndex: boolean;
}

Expand All @@ -47,51 +47,52 @@ function createEquationFromMetric(names: string[]) {
return names.join(' + ');
}

const SUPPORTED_FIELD_TYPES = ['number', 'histogram'];
const metricLabel = i18n.translate(
'xpack.observability.slo.sloEdit.sliType.customMetric.metricLabel',
{ defaultMessage: 'Metric' }
);

export function MetricIndicator({ type, indexFields, isLoadingIndex }: MetricIndicatorProps) {
const metricLabel = i18n.translate(
'xpack.observability.slo.sloEdit.sliType.customMetric.metricLabel',
{ defaultMessage: 'Metric' }
);
const filterLabel = i18n.translate(
'xpack.observability.slo.sloEdit.sliType.customMetric.filterLabel',
{ defaultMessage: 'Filter' }
);

const filterLabel = i18n.translate(
'xpack.observability.slo.sloEdit.sliType.customMetric.filterLabel',
{ defaultMessage: 'Filter' }
);
const metricTooltip = (
<EuiIconTip
content={i18n.translate(
'xpack.observability.slo.sloEdit.sliType.customMetric.totalMetric.tooltip',
{
defaultMessage: 'This data from this field will be aggregated with the "sum" aggregation.',
}
)}
position="top"
/>
);

const metricTooltip = (
<EuiIconTip
content={i18n.translate(
'xpack.observability.slo.sloEdit.sliType.customMetric.totalMetric.tooltip',
{
defaultMessage:
'This data from this field will be aggregated with the "sum" aggregation.',
}
)}
position="top"
/>
);
const equationLabel = i18n.translate(
'xpack.observability.slo.sloEdit.sliType.customMetric.equationLabel',
{ defaultMessage: 'Equation' }
);

const equationLabel = i18n.translate(
'xpack.observability.slo.sloEdit.sliType.customMetric.equationLabel',
{ defaultMessage: 'Equation' }
);
const equationTooltip = (
<EuiIconTip
content={i18n.translate(
'xpack.observability.slo.sloEdit.sliType.customMetric.totalEquation.tooltip',
{
defaultMessage: 'This supports basic math (A + B / C) and boolean logic (A < B ? A : B).',
}
)}
position="top"
/>
);

const equationTooltip = (
<EuiIconTip
content={i18n.translate(
'xpack.observability.slo.sloEdit.sliType.customMetric.totalEquation.tooltip',
{
defaultMessage: 'This supports basic math (A + B / C) and boolean logic (A < B ? A : B).',
}
)}
position="top"
/>
);
export function MetricIndicator({ type, metricFields, isLoadingIndex }: MetricIndicatorProps) {
const { control, watch, setValue, register, getFieldState } = useFormContext<CreateSLOForm>();
const [options, setOptions] = useState<Option[]>(createOptionsFromFields(metricFields));

const { control, watch, setValue, register } = useFormContext<CreateSLOForm>();
const metricFields = indexFields.filter((field) => SUPPORTED_FIELD_TYPES.includes(field.type));
useEffect(() => {
setOptions(createOptionsFromFields(metricFields));
}, [metricFields]);

const { fields, append, remove } = useFieldArray({
control,
Expand Down Expand Up @@ -134,6 +135,7 @@ export function MetricIndicator({ type, indexFields, isLoadingIndex }: MetricInd
<EuiFlexItem>
<EuiFormRow
fullWidth
isInvalid={getFieldState(`indicator.params.${type}.metrics.${index}.field`).invalid}
label={
<span>
{metricLabel} {metric.name} {metricTooltip}
Expand Down Expand Up @@ -163,8 +165,9 @@ export function MetricIndicator({ type, indexFields, isLoadingIndex }: MetricInd
'xpack.observability.slo.sloEdit.sliType.customMetric.metricField.placeholder',
{ defaultMessage: 'Select a metric field' }
)}
isClearable
isInvalid={fieldState.invalid}
isDisabled={!indexPattern}
isDisabled={isLoadingIndex || !indexPattern}
isLoading={!!indexPattern && isLoadingIndex}
onChange={(selected: EuiComboBoxOptionOption[]) => {
if (selected.length) {
Expand All @@ -184,7 +187,14 @@ export function MetricIndicator({ type, indexFields, isLoadingIndex }: MetricInd
]
: []
}
options={createOptionsFromFields(metricFields)}
onSearchChange={(searchValue: string) => {
setOptions(
createOptionsFromFields(metricFields, ({ value }) =>
value.includes(searchValue)
)
);
}}
options={options}
/>
)}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
* 2.0.
*/

import React, { Fragment } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import { i18n } from '@kbn/i18n';
import {
EuiComboBox,
EuiComboBoxOptionOption,
Expand All @@ -18,14 +15,17 @@ import {
EuiIconTip,
EuiSpacer,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { Fragment, useEffect, useState } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import { Field } from '../../../../hooks/slo/use_fetch_index_pattern_fields';
import { createOptionsFromFields, Option } from '../../helpers/create_options';
import { CreateSLOForm } from '../../types';
import { QueryBuilder } from '../common/query_builder';
import { Field } from '../../../../hooks/slo/use_fetch_index_pattern_fields';
import { createOptionsFromFields } from '../../helpers/create_options';

interface HistogramIndicatorProps {
type: 'good' | 'total';
indexFields: Field[];
histogramFields: Field[];
isLoadingIndex: boolean;
}

Expand All @@ -46,61 +46,69 @@ const AGGREGATIONS = {

const AGGREGATION_OPTIONS = Object.values(AGGREGATIONS);

export function HistogramIndicator({ type, indexFields, isLoadingIndex }: HistogramIndicatorProps) {
const { control, watch } = useFormContext<CreateSLOForm>();
const aggregationTooltip = (
<EuiIconTip
content={i18n.translate(
'xpack.observability.slo.sloEdit.sliType.histogram.aggregationTooltip',
{
defaultMessage:
'The "value count" aggreation will return the total count for the histogram field. Range will return the count from the histogram field that is within the range defined below.',
}
)}
position="top"
/>
);

const histogramFields = indexFields.filter((field) => field.type === 'histogram');
const indexPattern = watch('indicator.params.index');
const aggregation = watch(`indicator.params.${type}.aggregation`);
const fromTooltip = (
<EuiIconTip
content={i18n.translate('xpack.observability.slo.sloEdit.sliType.histogram.fromTooltip', {
defaultMessage: 'The "from" value is inclusive.',
})}
position="top"
/>
);

const aggregationTooltip = (
<EuiIconTip
content={i18n.translate(
'xpack.observability.slo.sloEdit.sliType.histogram.aggregationTooltip',
{
defaultMessage:
'The "value count" aggreation will return the total count for the histogram field. Range will return the count from the histogram field that is within the range defined below.',
}
)}
position="top"
/>
);
const toTooltip = (
<EuiIconTip
content={i18n.translate('xpack.observability.slo.sloEdit.sliType.histogram.toTooltip', {
defaultMessage: 'The "to" value is NOT inclusive.',
})}
position="top"
/>
);

const fromTooltip = (
<EuiIconTip
content={i18n.translate('xpack.observability.slo.sloEdit.sliType.histogram.fromTooltip', {
defaultMessage: 'The "from" value is inclusive.',
})}
position="top"
/>
);
const aggregationLabel = i18n.translate(
'xpack.observability.slo.sloEdit.sliType.histogram.aggregationLabel',
{ defaultMessage: 'Aggregation' }
);

const toTooltip = (
<EuiIconTip
content={i18n.translate('xpack.observability.slo.sloEdit.sliType.histogram.toTooltip', {
defaultMessage: 'The "to" value is NOT inclusive.',
})}
position="top"
/>
);
const metricLabel = i18n.translate(
'xpack.observability.slo.sloEdit.sliType.histogram.metricLabel',
{ defaultMessage: 'Field' }
);

const aggregationLabel = i18n.translate(
'xpack.observability.slo.sloEdit.sliType.histogram.aggregationLabel',
{ defaultMessage: 'Aggregation' }
);
const toLabel = i18n.translate('xpack.observability.slo.sloEdit.sliType.histogram.toLabel', {
defaultMessage: 'To',
});

const metricLabel = i18n.translate(
'xpack.observability.slo.sloEdit.sliType.histogram.metricLabel',
{ defaultMessage: 'Field' }
);
const fromLabel = i18n.translate('xpack.observability.slo.sloEdit.sliType.histogram.fromLabel', {
defaultMessage: 'From',
});

export function HistogramIndicator({
type,
histogramFields,
isLoadingIndex,
}: HistogramIndicatorProps) {
const { control, watch, getFieldState } = useFormContext<CreateSLOForm>();
const [options, setOptions] = useState<Option[]>(createOptionsFromFields(histogramFields));

const toLabel = i18n.translate('xpack.observability.slo.sloEdit.sliType.histogram.toLabel', {
defaultMessage: 'To',
});
useEffect(() => {
setOptions(createOptionsFromFields(histogramFields));
}, [histogramFields]);

const fromLabel = i18n.translate('xpack.observability.slo.sloEdit.sliType.histogram.fromLabel', {
defaultMessage: 'From',
});
const indexPattern = watch('indicator.params.index');
const aggregation = watch(`indicator.params.${type}.aggregation`);

return (
<Fragment>
Expand Down Expand Up @@ -149,7 +157,11 @@ export function HistogramIndicator({ type, indexFields, isLoadingIndex }: Histog
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow fullWidth label={<span>{metricLabel}</span>}>
<EuiFormRow
fullWidth
isInvalid={getFieldState(`indicator.params.${type}.field`).invalid}
label={<span>{metricLabel}</span>}
>
<Controller
name={`indicator.params.${type}.field`}
defaultValue=""
Expand All @@ -170,7 +182,7 @@ export function HistogramIndicator({ type, indexFields, isLoadingIndex }: Histog
{ defaultMessage: 'Select a histogram field' }
)}
isInvalid={fieldState.invalid}
isDisabled={!indexPattern}
isDisabled={isLoadingIndex || !indexPattern}
isLoading={!!indexPattern && isLoadingIndex}
onChange={(selected: EuiComboBoxOptionOption[]) => {
if (selected.length) {
Expand All @@ -190,7 +202,14 @@ export function HistogramIndicator({ type, indexFields, isLoadingIndex }: Histog
]
: []
}
options={createOptionsFromFields(histogramFields)}
onSearchChange={(searchValue: string) => {
setOptions(
createOptionsFromFields(histogramFields, ({ value }) =>
value.includes(searchValue)
)
);
}}
options={options}
/>
)}
/>
Expand Down
Loading

0 comments on commit f7095d5

Please sign in to comment.