diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.helpers.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.helpers.tsx index 227513dcdaacc..5ac43953e79bc 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.helpers.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.helpers.tsx @@ -89,7 +89,7 @@ const createActions = (testBed: TestBed) => { async addProcessor(processorsSelector: string, type: string, options: Record) { find(`${processorsSelector}.addProcessorButton`).simulate('click'); await act(async () => { - find('processorTypeSelector').simulate('change', [{ value: type, label: type }]); + find('processorTypeSelector.input').simulate('change', [{ value: type, label: type }]); }); component.update(); await act(async () => { @@ -129,7 +129,7 @@ const createActions = (testBed: TestBed) => { find(`${processorSelector}.moreMenu.button`).simulate('click'); find(`${processorSelector}.moreMenu.addOnFailureButton`).simulate('click'); await act(async () => { - find('processorTypeSelector').simulate('change', [{ value: type, label: type }]); + find('processorTypeSelector.input').simulate('change', [{ value: type, label: type }]); }); component.update(); await act(async () => { diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/pipeline_processors_editor_item.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/pipeline_processors_editor_item.tsx index b43e2bc1342c3..edabbe277e5d9 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/pipeline_processors_editor_item.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/pipeline_processors_editor_item.tsx @@ -22,6 +22,8 @@ import { ProcessorsDispatch } from '../../processors_reducer'; import { ProcessorInfo } from '../processors_tree'; +import { getProcessorDescriptor } from '../shared'; + import './pipeline_processors_editor_item.scss'; import { InlineTextInput } from './inline_text_input'; @@ -139,7 +141,7 @@ export const PipelineProcessorsEditorItem: FunctionComponent = memo( className="pipelineProcessorsEditor__item__processorTypeLabel" color={isDimmed ? 'subdued' : undefined} > - {processor.type} + {getProcessorDescriptor(processor.type)?.label ?? processor.type} diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processor_settings_form.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processor_settings_form.tsx index 015adae83e71e..b5b3a38bb6a6c 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processor_settings_form.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processor_settings_form.tsx @@ -23,8 +23,9 @@ import { import { Form, FormDataProvider, FormHook } from '../../../../../shared_imports'; import { ProcessorInternal } from '../../types'; +import { getProcessorDescriptor } from '../shared'; + import { DocumentationButton } from './documentation_button'; -import { getProcessorFormDescriptor } from './map_processor_type_to_form'; import { CommonProcessorFields, ProcessorTypeField } from './processors/common_fields'; import { Custom } from './processors/custom'; @@ -88,7 +89,7 @@ export const ProcessorSettingsForm: FunctionComponent = memo( {({ type }) => { - const formDescriptor = getProcessorFormDescriptor(type as any); + const formDescriptor = getProcessorDescriptor(type as any); if (formDescriptor) { return ( @@ -114,7 +115,7 @@ export const ProcessorSettingsForm: FunctionComponent = memo( const { type } = arg; if (type?.length) { - const formDescriptor = getProcessorFormDescriptor(type as any); + const formDescriptor = getProcessorDescriptor(type as any); if (formDescriptor?.FieldsComponent) { return ( diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processors/common_fields/processor_type_field.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processors/common_fields/processor_type_field.tsx index 4b82fbfad9b52..71ee4a714a28e 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processors/common_fields/processor_type_field.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processors/common_fields/processor_type_field.tsx @@ -3,16 +3,42 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { FunctionComponent } from 'react'; +import { flow } from 'fp-ts/lib/function'; +import { map } from 'fp-ts/lib/Array'; + import { FIELD_TYPES, FieldConfig, UseField, fieldValidators, - ComboBoxField, } from '../../../../../../../shared_imports'; -import { types } from '../../map_processor_type_to_form'; + +import { getProcessorDescriptor, mapProcessorTypeToDescriptor } from '../../../shared'; +import { + FieldValidateResponse, + VALIDATION_TYPES, +} from '../../../../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib'; + +const extractProcessorTypesAndLabels = flow( + Object.entries, + map(([type, { label }]) => ({ + label, + value: type, + })), + (arr) => arr.sort((a, b) => a.label.localeCompare(b.label)) +); + +interface ProcessorTypeAndLabel { + value: string; + label: string; +} + +const processorTypesAndLabels: ProcessorTypeAndLabel[] = extractProcessorTypesAndLabels( + mapProcessorTypeToDescriptor +); interface Props { initialType?: string; @@ -47,22 +73,76 @@ const typeConfig: FieldConfig = { export const ProcessorTypeField: FunctionComponent = ({ initialType }) => { return ( - ({ label: type, value: type })), - noSuggestions: false, - singleSelection: { - asPlainText: true, - }, - }, + + {(typeField) => { + let selectedOptions: ProcessorTypeAndLabel[]; + if ((typeField.value as string[]).length) { + const [type] = typeField.value as string[]; + const descriptor = getProcessorDescriptor(type); + selectedOptions = descriptor + ? [{ label: descriptor.label, value: type }] + : // If there is no label for this processor type, just use the type as the label + [{ label: type, value: type }]; + } else { + selectedOptions = []; + } + + const error = typeField.getErrorsMessages(); + const isInvalid = error ? Boolean(error.length) : false; + + const onCreateComboOption = (value: string) => { + // Note: for now, all validations for a comboBox array item have to be synchronous + // If there is a need to support asynchronous validation, we'll work on it (and will need to update the logic). + const { isValid } = typeField.validate({ + value, + validationType: VALIDATION_TYPES.ARRAY_ITEM, + }) as FieldValidateResponse; + + if (!isValid) { + // Return false to explicitly reject the user's input. + return false; + } + + const newValue = [...(typeField.value as string[]), value]; + + typeField.setValue(newValue); + }; + + return ( + + { + typeField.setValue(options.map(({ value }) => value)); + }} + noSuggestions={false} + singleSelection={{ + asPlainText: true, + }} + data-test-subj="input" + /> + + ); }} - /> + ); }; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/index.ts new file mode 100644 index 0000000000000..1b4b975b5305e --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/index.ts @@ -0,0 +1,11 @@ +/* + * 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. + */ + +export { + getProcessorDescriptor, + mapProcessorTypeToDescriptor, + ProcessorType, +} from './map_processor_type_to_form'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/map_processor_type_to_form.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/map_processor_type_to_form.tsx similarity index 95% rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/map_processor_type_to_form.tsx rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/map_processor_type_to_form.tsx index 5993d7fb3f87a..7055721fc8b07 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/map_processor_type_to_form.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/map_processor_type_to_form.tsx @@ -10,7 +10,7 @@ import { FunctionComponent } from 'react'; // import { SetProcessor } from './processors/set'; // import { Gsub } from './processors/gsub'; -interface FieldsFormDescriptor { +interface FieldDescriptor { FieldsComponent?: FunctionComponent; docLinkPath: string; /** @@ -19,7 +19,9 @@ interface FieldsFormDescriptor { label: string; } -const mapProcessorTypeToFormDescriptor: Record = { +type MapProcessorTypeToDescriptor = Record; + +export const mapProcessorTypeToDescriptor: MapProcessorTypeToDescriptor = { append: { FieldsComponent: undefined, // TODO: Implement docLinkPath: '/append-processor.html', @@ -262,12 +264,10 @@ const mapProcessorTypeToFormDescriptor: Record = { }, }; -export const types = Object.keys(mapProcessorTypeToFormDescriptor).sort(); - -export type ProcessorType = keyof typeof mapProcessorTypeToFormDescriptor; +export type ProcessorType = keyof typeof mapProcessorTypeToDescriptor; -export const getProcessorFormDescriptor = ( +export const getProcessorDescriptor = ( type: ProcessorType | string -): FieldsFormDescriptor | undefined => { - return mapProcessorTypeToFormDescriptor[type as ProcessorType]; +): FieldDescriptor | undefined => { + return mapProcessorTypeToDescriptor[type as ProcessorType]; };