From 1ec4a8d9d5ab2b7e519c768fb42c95134b9ee380 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Mon, 31 May 2021 15:45:02 -0400 Subject: [PATCH] [Fleet] Better input for multi text input in agent policy builder --- .../components/multi_text_input.tsx | 187 ++++++++++++++++++ .../package_policy_input_var_field.tsx | 10 +- 2 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/multi_text_input.tsx diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/multi_text_input.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/multi_text_input.tsx new file mode 100644 index 0000000000000..c18eb9f578452 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/multi_text_input.tsx @@ -0,0 +1,187 @@ +/* + * 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 React, { useMemo, useCallback, useState } from 'react'; +import type { FunctionComponent, ChangeEvent } from 'react'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, + EuiFieldText, + EuiButtonIcon, + EuiSpacer, + EuiFormErrorText, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +interface Props { + value: string[]; + onChange: (newValue: string[]) => void; + errors?: Array<{ message: string; index?: number }>; + isInvalid?: boolean; + isDisabled?: boolean; +} + +interface RowProps { + index: number; + value: string; + onChange: (e: ChangeEvent) => void; + onDelete: (index: number) => void; + errors?: string[]; + autoFocus?: boolean; + isDisabled?: boolean; +} + +function displayErrors(errors?: string[]) { + return errors?.length + ? errors.map((error, errorIndex) => ( + {error} + )) + : null; +} + +const Row: FunctionComponent = React.memo( + ({ index, value, onChange, onDelete, autoFocus, errors, isDisabled }) => { + const onDeleteHandler = useCallback(() => { + onDelete(index); + }, [onDelete, index]); + + const isInvalid = (errors?.length ?? 0) > 0; + + return ( + + + + {displayErrors(errors)} + + + + + + ); + } +); + +function defaultValue(value: string[]) { + return value.length > 0 ? value : ['']; +} + +export const MultiTextInput: FunctionComponent = ({ + value, + onChange, + isInvalid, + isDisabled, + errors, +}) => { + const [autoFocus, setAutoFocus] = useState(false); + const rows = useMemo(() => { + return defaultValue(value).map((val, idx) => ({ + value: val, + onChange: (e: ChangeEvent) => { + const newValue = [...value]; + newValue[idx] = e.target.value; + + onChange(newValue); + }, + })); + }, [value, onChange]); + + const onDelete = useCallback( + (idx: number) => { + onChange([...value.slice(0, idx), ...value.slice(idx + 1)]); + }, + [value, onChange] + ); + + const addRowHandler = useCallback(() => { + setAutoFocus(true); + onChange([...defaultValue(value), '']); + }, [value, onChange]); + + const globalErrors = useMemo(() => { + return errors && errors.filter((err) => err.index === undefined).map(({ message }) => message); + }, [errors]); + + const indexedErrors = useMemo(() => { + if (!errors) { + return []; + } + return errors.reduce((acc, err) => { + if (err.index === undefined) { + return acc; + } + + if (!acc[err.index]) { + acc[err.index] = []; + } + + acc[err.index].push(err.message); + + return acc; + }, [] as string[][]); + }, [errors]); + + return ( + <> + + {rows.map((row, idx) => ( + + {rows.length > 1 ? ( + + ) : ( + <> + + {displayErrors(indexedErrors[idx])} + + )} + + ))} + {displayErrors(globalErrors)} + + + + + + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/package_policy_input_var_field.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/package_policy_input_var_field.tsx index 7841e8bb62452..6b5163c8d77af 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/package_policy_input_var_field.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/package_policy_input_var_field.tsx @@ -23,6 +23,7 @@ import type { RegistryVarsEntry } from '../../../../types'; import 'brace/mode/yaml'; import 'brace/theme/textmate'; +import { MultiTextInput } from './multi_text_input'; export const PackagePolicyInputVarField: React.FunctionComponent<{ varDef: RegistryVarsEntry; @@ -40,6 +41,14 @@ export const PackagePolicyInputVarField: React.FunctionComponent<{ const field = useMemo(() => { if (multi) { + return ( + setIsDirty(true)} + isDisabled={frozen} + /> + ); return ( val.label)); }} onBlur={() => setIsDirty(true)} - isDisabled={frozen} /> ); }