Skip to content

Commit

Permalink
[Form Helpers + Template UI] Refactor Template UI using Form lib + he…
Browse files Browse the repository at this point in the history
…lpers (#45287) (#46231)
  • Loading branch information
sebelga authored Sep 20, 2019
1 parent 7c5e01f commit 8651c64
Show file tree
Hide file tree
Showing 85 changed files with 2,503 additions and 899 deletions.
53 changes: 53 additions & 0 deletions src/plugins/es_ui_shared/static/forms/components/field.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import React from 'react';
import { FieldHook, FIELD_TYPES } from '../hook_form_lib';

interface Props {
field: FieldHook;
euiFieldProps?: Record<string, any>;
idAria?: string;
[key: string]: any;
}

import {
TextField,
NumericField,
CheckBoxField,
ComboBoxField,
MultiSelectField,
SelectField,
ToggleField,
} from './fields';

const mapTypeToFieldComponent = {
[FIELD_TYPES.TEXT]: TextField,
[FIELD_TYPES.NUMBER]: NumericField,
[FIELD_TYPES.CHECKBOX]: CheckBoxField,
[FIELD_TYPES.COMBO_BOX]: ComboBoxField,
[FIELD_TYPES.MULTI_SELECT]: MultiSelectField,
[FIELD_TYPES.SELECT]: SelectField,
[FIELD_TYPES.TOGGLE]: ToggleField,
};

export const Field = (props: Props) => {
const FieldComponent = mapTypeToFieldComponent[props.field.type] || TextField;
return <FieldComponent {...props} />;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import React from 'react';
import { EuiFormRow, EuiCheckbox } from '@elastic/eui';
import uuid from 'uuid';

import { FieldHook } from '../../hook_form_lib';
import { getFieldValidityAndErrorMessage } from '../helpers';

interface Props {
field: FieldHook;
euiFieldProps?: Record<string, any>;
idAria?: string;
[key: string]: any;
}

export const CheckBoxField = ({ field, euiFieldProps = {}, ...rest }: Props) => {
const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field);

return (
<EuiFormRow
helpText={field.helpText}
error={errorMessage}
isInvalid={isInvalid}
fullWidth
data-test-subj={rest['data-test-subj']}
describedByIds={rest.idAria ? [rest.idAria] : undefined}
>
<EuiCheckbox
label={field.label}
checked={field.value as boolean}
onChange={field.onChange}
id={euiFieldProps.id || uuid()}
data-test-subj="input"
{...euiFieldProps}
/>
</EuiFormRow>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import React from 'react';
import { i18n } from '@kbn/i18n';
import { EuiFormRow, EuiComboBox, EuiComboBoxOptionProps } from '@elastic/eui';

import { FieldHook, VALIDATION_TYPES, FieldValidateResponse } from '../../hook_form_lib';

interface Props {
field: FieldHook;
euiFieldProps?: Record<string, any>;
idAria?: string;
[key: string]: any;
}

export const ComboBoxField = ({ field, euiFieldProps = {}, ...rest }: Props) => {
// Errors for the comboBox value (the "array")
const errorMessageField = field.getErrorsMessages();

// Errors for comboBox option added (the array "item")
const errorMessageArrayItem = field.getErrorsMessages({
validationType: VALIDATION_TYPES.ARRAY_ITEM,
});

const isInvalid = field.errors.length
? errorMessageField !== null || errorMessageArrayItem !== null
: false;

// Concatenate error messages.
const errorMessage =
errorMessageField && errorMessageArrayItem
? `${errorMessageField}, ${errorMessageArrayItem}`
: errorMessageField
? errorMessageField
: errorMessageArrayItem;

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 <EuiComboBox /> logic).
const { isValid } = field.validate({
value,
validationType: VALIDATION_TYPES.ARRAY_ITEM,
}) as FieldValidateResponse;

if (!isValid) {
// Return false to explicitly reject the user's input.
return false;
}

const newValue = [...(field.value as string[]), value];

field.setValue(newValue);
};

const onComboChange = (options: EuiComboBoxOptionProps[]) => {
field.setValue(options.map(option => option.label));
};

const onSearchComboChange = (value: string) => {
if (value) {
field.clearErrors(VALIDATION_TYPES.ARRAY_ITEM);
}
};

return (
<EuiFormRow
label={field.label}
helpText={field.helpText}
error={errorMessage}
isInvalid={isInvalid}
fullWidth
data-test-subj={rest['data-test-subj']}
describedByIds={rest.idAria ? [rest.idAria] : undefined}
>
<EuiComboBox
noSuggestions
placeholder={i18n.translate('esUi.forms.comboBoxField.placeHolderText', {
defaultMessage: 'Type and then hit "ENTER"',
})}
selectedOptions={(field.value as any[]).map(v => ({ label: v }))}
onCreateOption={onCreateComboOption}
onChange={onComboChange}
onSearchChange={onSearchComboChange}
fullWidth
data-test-subj="input"
{...euiFieldProps}
/>
</EuiFormRow>
);
};
26 changes: 26 additions & 0 deletions src/plugins/es_ui_shared/static/forms/components/fields/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

export * from './text_field';
export * from './numeric_field';
export * from './checkbox_field';
export * from './combobox_field';
export * from './multi_select_field';
export * from './select_field';
export * from './toggle_field';
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import React from 'react';
import { EuiFormRow, EuiSelectable, EuiPanel } from '@elastic/eui';

import { FieldHook } from '../../hook_form_lib';
import { getFieldValidityAndErrorMessage } from '../helpers';

interface Props {
field: FieldHook;
euiFieldProps?: Record<string, any>;
idAria?: string;
[key: string]: any;
}

export const MultiSelectField = ({ field, euiFieldProps = {}, ...rest }: Props) => {
const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field);

return (
<EuiFormRow
label={field.label}
helpText={field.helpText}
error={errorMessage}
isInvalid={isInvalid}
fullWidth
data-test-subj={rest['data-test-subj']}
describedByIds={rest.idAria ? [rest.idAria] : undefined}
>
<EuiSelectable
allowExclusions={false}
height={300}
onChange={options => {
field.setValue(options);
}}
options={field.value as any[]}
data-test-subj="select"
{...euiFieldProps}
>
{(list, search) => (
<EuiPanel paddingSize="s" hasShadow={false}>
{search}
{list}
</EuiPanel>
)}
</EuiSelectable>
</EuiFormRow>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import React from 'react';
import { EuiFormRow, EuiFieldNumber } from '@elastic/eui';

import { FieldHook } from '../../hook_form_lib';
import { getFieldValidityAndErrorMessage } from '../helpers';

interface Props {
field: FieldHook;
euiFieldProps?: Record<string, any>;
idAria?: string;
[key: string]: any;
}

export const NumericField = ({ field, euiFieldProps = {}, ...rest }: Props) => {
const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field);

return (
<EuiFormRow
label={field.label}
helpText={field.helpText}
error={errorMessage}
isInvalid={isInvalid}
fullWidth
data-test-subj={rest['data-test-subj']}
describedByIds={rest.idAria ? [rest.idAria] : undefined}
>
<EuiFieldNumber
isInvalid={isInvalid}
value={field.value as string}
onChange={field.onChange}
isLoading={field.isValidating}
fullWidth
data-test-subj="input"
{...euiFieldProps}
/>
</EuiFormRow>
);
};
Loading

0 comments on commit 8651c64

Please sign in to comment.