diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form_data_provider.test.tsx b/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form_data_provider.test.tsx
index 3e4ce4a412b3b..25448dff18e8a 100644
--- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form_data_provider.test.tsx
+++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form_data_provider.test.tsx
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import React from 'react';
+import React, { useState } from 'react';
import { act } from 'react-dom/test-utils';
import { registerTestBed, TestBed } from '../shared_imports';
@@ -36,13 +36,14 @@ describe('', () => {
return (
);
};
@@ -95,6 +96,63 @@ describe('', () => {
});
});
+ test('should subscribe to the latest updated form data when mounting late', async () => {
+ const onFormData = jest.fn();
+
+ const TestComp = () => {
+ const { form } = useForm();
+ const [isOn, setIsOn] = useState(false);
+
+ return (
+
+ );
+ };
+
+ const setup = registerTestBed(TestComp, {
+ memoryRouter: { wrapComponent: false },
+ });
+
+ const {
+ form: { setInputValue },
+ find,
+ } = setup() as TestBed;
+
+ expect(onFormData.mock.calls.length).toBe(0); // Not present in the DOM yet
+
+ // Make some changes to the form fields
+ await act(async () => {
+ setInputValue('nameField', 'updated value');
+ });
+
+ // Update state to trigger the mounting of the FormDataProvider
+ await act(async () => {
+ find('btn').simulate('click').update();
+ });
+
+ expect(onFormData.mock.calls.length).toBe(1);
+
+ const [formDataUpdated] = onFormData.mock.calls[onFormData.mock.calls.length - 1] as Parameters<
+ OnUpdateHandler
+ >;
+
+ expect(formDataUpdated).toEqual({
+ name: 'updated value',
+ });
+ });
+
test('props.pathsToWatch (string): should not re-render the children when the field that changed is not the one provided', async () => {
const onFormData = jest.fn();
diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form_data_provider.ts b/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form_data_provider.ts
index 4c8e91b13b1b7..3630b902f0564 100644
--- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form_data_provider.ts
+++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form_data_provider.ts
@@ -31,6 +31,7 @@ export const FormDataProvider = React.memo(({ children, pathsToWatch }: Props) =
const form = useFormContext();
const { subscribe } = form;
const previousRawData = useRef(form.__getFormData$().value);
+ const isMounted = useRef(false);
const [formData, setFormData] = useState(previousRawData.current);
const onFormData = useCallback(
@@ -59,5 +60,17 @@ export const FormDataProvider = React.memo(({ children, pathsToWatch }: Props) =
return subscription.unsubscribe;
}, [subscribe, onFormData]);
+ useEffect(() => {
+ isMounted.current = true;
+ return () => {
+ isMounted.current = false;
+ };
+ }, []);
+
+ if (!isMounted.current && Object.keys(formData).length === 0) {
+ // No field has mounted yet, don't render anything
+ return null;
+ }
+
return children(formData);
});
diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_field.ts b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_field.ts
index 01d9f8a59129a..9d22e4eb2ee5e 100644
--- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_field.ts
+++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_field.ts
@@ -43,38 +43,41 @@ export const useField = (
deserializer,
} = config;
- const { getFormData, __removeField, __updateFormDataAt, __validateFields } = form;
+ const {
+ getFormData,
+ getFields,
+ __addField,
+ __removeField,
+ __updateFormDataAt,
+ __validateFields,
+ } = form;
- /**
- * This callback is both used as the initial "value" state getter, **and** for when we reset the form
- * (and thus reset the field value). When we reset the form, we can provide a new default value (which will be
- * passed through this "initialValueGetter" handler).
- */
- const initialValueGetter = useCallback(
- (updatedDefaultValue = initialValue) => {
- if (typeof updatedDefaultValue === 'function') {
- return deserializer ? deserializer(updatedDefaultValue()) : updatedDefaultValue();
+ const deserializeValue = useCallback(
+ (rawValue = initialValue) => {
+ if (typeof rawValue === 'function') {
+ return deserializer ? deserializer(rawValue()) : rawValue();
}
- return deserializer ? deserializer(updatedDefaultValue) : updatedDefaultValue;
+ return deserializer ? deserializer(rawValue) : rawValue;
},
[initialValue, deserializer]
);
- const [value, setStateValue] = useState(initialValueGetter);
+ const [value, setStateValue] = useState(deserializeValue);
const [errors, setErrors] = useState([]);
const [isPristine, setPristine] = useState(true);
const [isValidating, setValidating] = useState(false);
const [isChangingValue, setIsChangingValue] = useState(false);
const [isValidated, setIsValidated] = useState(false);
+
const validateCounter = useRef(0);
const changeCounter = useRef(0);
const inflightValidation = useRef | null>(null);
const debounceTimeout = useRef(null);
- const isUnmounted = useRef(false);
+ const isMounted = useRef(false);
// -- HELPERS
// ----------------------------------
- const serializeOutput: FieldHook['__serializeOutput'] = useCallback(
+ const serializeValue: FieldHook['__serializeValue'] = useCallback(
(rawValue = value) => {
return serializer ? serializer(rawValue) : rawValue;
},
@@ -121,8 +124,11 @@ export const useField = (
if (debounceTimeout.current) {
clearTimeout(debounceTimeout.current);
+ debounceTimeout.current = null;
}
+ setPristine(false);
+
if (errorDisplayDelay > 0) {
setIsChangingValue(true);
}
@@ -135,10 +141,14 @@ export const useField = (
// Update the form data observable
__updateFormDataAt(path, value);
- // Validate field(s) and update form.isValid state
- await __validateFields(fieldsToValidateOnChange ?? [path]);
+ // Validate field(s) (that will update form.isValid state)
+ // We only validate if the value is different than the initial or default value
+ // to avoid validating after a form.reset() call.
+ if (value !== initialValue && value !== defaultValue) {
+ await __validateFields(fieldsToValidateOnChange ?? [path]);
+ }
- if (isUnmounted.current) {
+ if (isMounted.current === false) {
return;
}
@@ -160,10 +170,12 @@ export const useField = (
}
}
}, [
- valueChangeListener,
- errorDisplayDelay,
path,
value,
+ defaultValue,
+ initialValue,
+ valueChangeListener,
+ errorDisplayDelay,
fieldsToValidateOnChange,
__updateFormDataAt,
__validateFields,
@@ -229,7 +241,7 @@ export const useField = (
inflightValidation.current = validator({
value: (valueToValidate as unknown) as string,
errors: validationErrors,
- form,
+ form: { getFormData, getFields },
formData,
path,
}) as Promise;
@@ -273,7 +285,7 @@ export const useField = (
const validationResult = validator({
value: (valueToValidate as unknown) as string,
errors: validationErrors,
- form,
+ form: { getFormData, getFields },
formData,
path,
});
@@ -308,7 +320,7 @@ export const useField = (
// We first try to run the validations synchronously
return runSync();
},
- [clearErrors, cancelInflightValidation, validations, form, path]
+ [clearErrors, cancelInflightValidation, validations, getFormData, getFields, path]
);
// -- API
@@ -331,12 +343,12 @@ export const useField = (
setValidating(true);
// By the time our validate function has reached completion, it’s possible
- // that validate() will have been called again. If this is the case, we need
+ // that we have called validate() again. If this is the case, we need
// to ignore the results of this invocation and only use the results of
// the most recent invocation to update the error state for a field
const validateIteration = ++validateCounter.current;
- const onValidationErrors = (_validationErrors: ValidationError[]): FieldValidateResponse => {
+ const onValidationResult = (_validationErrors: ValidationError[]): FieldValidateResponse => {
if (validateIteration === validateCounter.current) {
// This is the most recent invocation
setValidating(false);
@@ -360,9 +372,9 @@ export const useField = (
});
if (Reflect.has(validationErrors, 'then')) {
- return (validationErrors as Promise).then(onValidationErrors);
+ return (validationErrors as Promise).then(onValidationResult);
}
- return onValidationErrors(validationErrors as ValidationError[]);
+ return onValidationResult(validationErrors as ValidationError[]);
},
[getFormData, value, runValidations]
);
@@ -374,15 +386,11 @@ export const useField = (
*/
const setValue: FieldHook['setValue'] = useCallback(
(newValue) => {
- if (isPristine) {
- setPristine(false);
- }
-
const formattedValue = formatInputValue(newValue);
setStateValue(formattedValue);
return formattedValue;
},
- [formatInputValue, isPristine]
+ [formatInputValue]
);
const _setErrors: FieldHook['setErrors'] = useCallback((_errors) => {
@@ -447,32 +455,17 @@ export const useField = (
setErrors([]);
if (resetValue) {
- const newValue = initialValueGetter(updatedDefaultValue ?? defaultValue);
+ const newValue = deserializeValue(updatedDefaultValue ?? defaultValue);
setValue(newValue);
return newValue;
}
},
- [setValue, initialValueGetter, defaultValue]
+ [setValue, deserializeValue, defaultValue]
);
- // -- EFFECTS
- // ----------------------------------
- useEffect(() => {
- if (isPristine) {
- // Avoid validate on mount
- return;
- }
-
- onValueChange();
+ const isValid = errors.length === 0;
- return () => {
- if (debounceTimeout.current) {
- clearTimeout(debounceTimeout.current);
- }
- };
- }, [isPristine, onValueChange]);
-
- const field: FieldHook = useMemo(() => {
+ const field = useMemo>(() => {
return {
path,
type,
@@ -481,9 +474,8 @@ export const useField = (
helpText,
value,
errors,
- form,
isPristine,
- isValid: errors.length === 0,
+ isValid,
isValidating,
isValidated,
isChangingValue,
@@ -494,7 +486,7 @@ export const useField = (
clearErrors,
validate,
reset,
- __serializeOutput: serializeOutput,
+ __serializeValue: serializeValue,
};
}, [
path,
@@ -503,9 +495,9 @@ export const useField = (
labelAppend,
helpText,
value,
- form,
isPristine,
errors,
+ isValid,
isValidating,
isValidated,
isChangingValue,
@@ -516,18 +508,43 @@ export const useField = (
clearErrors,
validate,
reset,
- serializeOutput,
+ serializeValue,
]);
- form.__addField(field as FieldHook);
+ // ----------------------------------
+ // -- EFFECTS
+ // ----------------------------------
+ useEffect(() => {
+ __addField(field as FieldHook);
+ }, [field, __addField]);
useEffect(() => {
return () => {
- // Remove field from the form when it is unmounted or if its path changes.
- isUnmounted.current = true;
__removeField(path);
};
}, [path, __removeField]);
+ useEffect(() => {
+ if (!isMounted.current) {
+ return;
+ }
+
+ onValueChange();
+
+ return () => {
+ if (debounceTimeout.current) {
+ clearTimeout(debounceTimeout.current);
+ }
+ };
+ }, [onValueChange]);
+
+ useEffect(() => {
+ isMounted.current = true;
+
+ return () => {
+ isMounted.current = false;
+ };
+ }, []);
+
return field;
};
diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.ts b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.ts
index c3f6ecc7f4831..35bac5b9a58c6 100644
--- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.ts
+++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.ts
@@ -40,19 +40,26 @@ export function useForm(
const { onSubmit, schema, serializer, deserializer, options, id = 'default', defaultValue } =
formConfig ?? {};
- const formDefaultValue = useMemo<{ [key: string]: any }>(() => {
- if (defaultValue === undefined || Object.keys(defaultValue).length === 0) {
- return {};
- }
+ const initDefaultValue = useCallback(
+ (_defaultValue?: Partial): { [key: string]: any } => {
+ if (_defaultValue === undefined || Object.keys(_defaultValue).length === 0) {
+ return {};
+ }
- const defaultValueFiltered = Object.entries(defaultValue as object)
- .filter(({ 1: value }) => value !== undefined)
- .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});
+ const filtered = Object.entries(_defaultValue as object)
+ .filter(({ 1: value }) => value !== undefined)
+ .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});
- return deserializer ? (deserializer(defaultValueFiltered) as any) : defaultValueFiltered;
- }, [defaultValue, deserializer]);
+ return deserializer ? (deserializer(filtered) as any) : filtered;
+ },
+ [deserializer]
+ );
- const defaultValueDeserialized = useRef(formDefaultValue);
+ const defaultValueMemoized = useMemo<{ [key: string]: any }>(() => {
+ return initDefaultValue(defaultValue);
+ }, [defaultValue, initDefaultValue]);
+
+ const defaultValueDeserialized = useRef(defaultValueMemoized);
const { errorDisplayDelay, stripEmptyFields: doStripEmptyFields } = options ?? {};
const formOptions = useMemo(
@@ -68,7 +75,7 @@ export function useForm(
const [isValid, setIsValid] = useState(undefined);
const fieldsRefs = useRef({});
const formUpdateSubscribers = useRef([]);
- const isUnmounted = useRef(false);
+ const isMounted = useRef(false);
// formData$ is an observable we can subscribe to in order to receive live
// update of the raw form data. As an observable it does not trigger any React
@@ -77,14 +84,6 @@ export function useForm(
// and updating its state to trigger the necessary view render.
const formData$ = useRef | null>(null);
- useEffect(() => {
- return () => {
- formUpdateSubscribers.current.forEach((subscription) => subscription.unsubscribe());
- formUpdateSubscribers.current = [];
- isUnmounted.current = true;
- };
- }, []);
-
// -- HELPERS
// ----------------------------------
const getFormData$ = useCallback((): Subject => {
@@ -135,7 +134,7 @@ export function useForm(
(getDataOptions: Parameters['getFormData']>[0] = { unflatten: true }) => {
if (getDataOptions.unflatten) {
const nonEmptyFields = stripEmptyFields(fieldsRefs.current);
- const fieldsValue = mapFormFields(nonEmptyFields, (field) => field.__serializeOutput());
+ const fieldsValue = mapFormFields(nonEmptyFields, (field) => field.__serializeValue());
return serializer
? (serializer(unflattenObject(fieldsValue)) as T)
: (unflattenObject(fieldsValue) as T);
@@ -168,45 +167,53 @@ export function useForm(
const isFieldValid = (field: FieldHook) => field.isValid && !field.isValidating;
- const updateFormValidity = useCallback(() => {
- if (isUnmounted.current) {
- return;
- }
-
- const fieldsArray = fieldsToArray();
- const areAllFieldsValidated = fieldsArray.every((field) => field.isValidated);
-
- if (!areAllFieldsValidated) {
- // If *not* all the fiels have been validated, the validity of the form is unknown, thus still "undefined"
- return undefined;
- }
-
- const isFormValid = fieldsArray.every(isFieldValid);
-
- setIsValid(isFormValid);
- return isFormValid;
- }, [fieldsToArray]);
-
const validateFields: FormHook['__validateFields'] = useCallback(
async (fieldNames) => {
const fieldsToValidate = fieldNames
.map((name) => fieldsRefs.current[name])
.filter((field) => field !== undefined);
- if (fieldsToValidate.length === 0) {
- // Nothing to validate
+ const formData = getFormData({ unflatten: false });
+ const validationResult = await Promise.all(
+ fieldsToValidate.map((field) => field.validate({ formData }))
+ );
+
+ if (isMounted.current === false) {
return { areFieldsValid: true, isFormValid: true };
}
- const formData = getFormData({ unflatten: false });
- await Promise.all(fieldsToValidate.map((field) => field.validate({ formData })));
+ const areFieldsValid = validationResult.every(Boolean);
- const isFormValid = updateFormValidity();
- const areFieldsValid = fieldsToValidate.every(isFieldValid);
+ const validationResultByPath = fieldsToValidate.reduce((acc, field, i) => {
+ acc[field.path] = validationResult[i].isValid;
+ return acc;
+ }, {} as { [key: string]: boolean });
+
+ // At this stage we have an updated field validation state inside the "validationResultByPath" object.
+ // The fields we have in our "fieldsRefs.current" have not been updated yet with the new validation state
+ // (isValid, isValidated...) as this will happen _after_, when the "useEffect" triggers and calls "addField()".
+ // This means that we have **stale state value** in our fieldsRefs.
+ // To know the current form validity, we will then merge the "validationResult" _with_ the fieldsRefs object state,
+ // the "validationResult" taking presedence over the fieldsRefs values.
+ const formFieldsValidity = fieldsToArray().map((field) => {
+ const _isValid = validationResultByPath[field.path] ?? field.isValid;
+ const _isValidated =
+ validationResultByPath[field.path] !== undefined ? true : field.isValidated;
+ return [_isValid, _isValidated];
+ });
+
+ const areAllFieldsValidated = formFieldsValidity.every(({ 1: isValidated }) => isValidated);
+
+ // If *not* all the fiels have been validated, the validity of the form is unknown, thus still "undefined"
+ const isFormValid = areAllFieldsValidated
+ ? formFieldsValidity.every(([_isValid]) => _isValid)
+ : undefined;
+
+ setIsValid(isFormValid);
return { areFieldsValid, isFormValid };
},
- [getFormData, updateFormValidity]
+ [getFormData, fieldsToArray]
);
const validateAllFields = useCallback(async (): Promise => {
@@ -216,19 +223,12 @@ export function useForm(
let isFormValid: boolean | undefined;
if (fieldsToValidate.length === 0) {
- // We should never enter this condition as the form validity is updated each time
- // a field is validated. But sometimes, during tests or race conditions it does not happen and we need
- // to wait the next tick (hooks lifecycle being tricky) to make sure the "isValid" state is updated.
- // In order to avoid this unintentional behaviour, we add this if condition here.
-
- // TODO: Fix this when adding tests to the form lib.
isFormValid = fieldsArray.every(isFieldValid);
- setIsValid(isFormValid);
- return isFormValid;
+ } else {
+ ({ isFormValid } = await validateFields(fieldsToValidate.map((field) => field.path)));
}
- ({ isFormValid } = await validateFields(fieldsToValidate.map((field) => field.path)));
-
+ setIsValid(isFormValid);
return isFormValid!;
}, [fieldsToArray, validateFields]);
@@ -236,11 +236,13 @@ export function useForm(
(field) => {
fieldsRefs.current[field.path] = field;
- if (!{}.hasOwnProperty.call(getFormData$().value, field.path)) {
- updateFormDataAt(field.path, field.value);
+ updateFormDataAt(field.path, field.value);
+
+ if (!field.isValidated) {
+ setIsValid(undefined);
}
},
- [getFormData$, updateFormDataAt]
+ [updateFormDataAt]
);
const removeField: FormHook['__removeField'] = useCallback(
@@ -259,9 +261,16 @@ export function useForm(
* After removing a field, the form validity might have changed
* (an invalid field might have been removed and now the form is valid)
*/
- updateFormValidity();
+ setIsValid((prev) => {
+ if (prev === false) {
+ const isFormValid = fieldsToArray().every(isFieldValid);
+ return isFormValid;
+ }
+ // If the form validity is "true" or "undefined", it does not change after removing a field
+ return prev;
+ });
},
- [getFormData$, updateFormValidity]
+ [getFormData$, fieldsToArray]
);
const setFieldValue: FormHook['setFieldValue'] = useCallback((fieldName, value) => {
@@ -310,7 +319,7 @@ export function useForm(
await onSubmit(formData, isFormValid!);
}
- if (isUnmounted.current === false) {
+ if (isMounted.current) {
setSubmitting(false);
}
@@ -322,9 +331,7 @@ export function useForm(
const subscribe: FormHook['subscribe'] = useCallback(
(handler) => {
const subscription = getFormData$().subscribe((raw) => {
- if (!isUnmounted.current) {
- handler({ isValid, data: { raw, format: getFormData }, validate: validateAllFields });
- }
+ handler({ isValid, data: { raw, format: getFormData }, validate: validateAllFields });
});
formUpdateSubscribers.current.push(subscription);
@@ -351,9 +358,7 @@ export function useForm(
const currentFormData = { ...getFormData$().value } as FormData;
if (updatedDefaultValue) {
- defaultValueDeserialized.current = deserializer
- ? (deserializer(updatedDefaultValue) as any)
- : updatedDefaultValue;
+ defaultValueDeserialized.current = initDefaultValue(updatedDefaultValue);
}
Object.entries(fieldsRefs.current).forEach(([path, field]) => {
@@ -374,7 +379,7 @@ export function useForm(
setSubmitting(false);
setIsValid(undefined);
},
- [getFormData$, deserializer, getFieldDefaultValue]
+ [getFormData$, initDefaultValue, getFieldDefaultValue]
);
const form = useMemo>(() => {
@@ -425,6 +430,25 @@ export function useForm(
validateFields,
]);
+ useEffect(() => {
+ if (!isMounted.current) {
+ return;
+ }
+
+ // Whenever the "defaultValue" prop changes, reinitialize our ref
+ defaultValueDeserialized.current = defaultValueMemoized;
+ }, [defaultValueMemoized]);
+
+ useEffect(() => {
+ isMounted.current = true;
+
+ return () => {
+ isMounted.current = false;
+ formUpdateSubscribers.current.forEach((subscription) => subscription.unsubscribe());
+ formUpdateSubscribers.current = [];
+ };
+ }, []);
+
return {
form,
};
diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/types.ts b/src/plugins/es_ui_shared/static/forms/hook_form_lib/types.ts
index 4b203c3927ffd..dc495f6eb56b4 100644
--- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/types.ts
+++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/types.ts
@@ -38,7 +38,7 @@ export interface FormHook {
getFieldDefaultValue: (fieldName: string) => unknown;
/* Returns a list of all errors in the form */
getErrors: () => string[];
- reset: (options?: { resetValues?: boolean; defaultValue?: FormData }) => void;
+ reset: (options?: { resetValues?: boolean; defaultValue?: Partial }) => void;
readonly __options: Required;
__getFormData$: () => Subject;
__addField: (field: FieldHook) => void;
@@ -102,7 +102,6 @@ export interface FieldHook {
readonly isValidating: boolean;
readonly isValidated: boolean;
readonly isChangingValue: boolean;
- readonly form: FormHook;
getErrorsMessages: (args?: {
validationType?: 'field' | string;
errorCode?: string;
@@ -117,7 +116,7 @@ export interface FieldHook {
validationType?: string;
}) => FieldValidateResponse | Promise;
reset: (options?: { resetValue?: boolean; defaultValue?: T }) => unknown | undefined;
- __serializeOutput: (rawValue?: unknown) => unknown;
+ __serializeValue: (rawValue?: unknown) => unknown;
}
export interface FieldConfig {
@@ -154,7 +153,10 @@ export interface ValidationError {
export interface ValidationFuncArg {
path: string;
value: V;
- form: FormHook;
+ form: {
+ getFormData: FormHook['getFormData'];
+ getFields: FormHook['getFields'];
+ };
formData: T;
errors: readonly ValidationError[];
}
diff --git a/x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/helpers/component_template_form.helpers.ts b/x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/helpers/component_template_form.helpers.ts
index f92f46d71e7c7..870b8b7ec5509 100644
--- a/x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/helpers/component_template_form.helpers.ts
+++ b/x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/helpers/component_template_form.helpers.ts
@@ -85,6 +85,9 @@ export const getFormActions = (testBed: TestBed) => {
value: type,
},
]);
+ });
+
+ await act(async () => {
find('createFieldForm.addButton').simulate('click');
});
diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx
index 0320f2ff51da3..9b27b930b47c4 100644
--- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx
@@ -4,33 +4,42 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React from 'react';
+import React, { useCallback, useMemo } from 'react';
import { TextField, UseField, FieldConfig } from '../../../shared_imports';
import { validateUniqueName } from '../../../lib';
import { PARAMETERS_DEFINITION } from '../../../constants';
import { useMappingsState } from '../../../mappings_state_context';
+const { validations, ...rest } = PARAMETERS_DEFINITION.name.fieldConfig as FieldConfig;
+
export const NameParameter = () => {
const {
fields: { rootLevelFields, byId },
documentFields: { fieldToAddFieldTo, fieldToEdit },
} = useMappingsState();
- const { validations, ...rest } = PARAMETERS_DEFINITION.name.fieldConfig as FieldConfig;
const initialName = fieldToEdit ? byId[fieldToEdit].source.name : undefined;
const parentId = fieldToEdit ? byId[fieldToEdit].parentId : fieldToAddFieldTo;
- const uniqueNameValidator = validateUniqueName({ rootLevelFields, byId }, initialName, parentId);
+ const uniqueNameValidator = useCallback(
+ (arg: any) => {
+ return validateUniqueName({ rootLevelFields, byId }, initialName, parentId)(arg);
+ },
+ [rootLevelFields, byId, initialName, parentId]
+ );
- const nameConfig: FieldConfig = {
- ...rest,
- validations: [
- ...validations!,
- {
- validator: uniqueNameValidator,
- },
- ],
- };
+ const nameConfig: FieldConfig = useMemo(
+ () => ({
+ ...rest,
+ validations: [
+ ...validations!,
+ {
+ validator: uniqueNameValidator,
+ },
+ ],
+ }),
+ [uniqueNameValidator]
+ );
return (
{
const suggestedFields = getSuggestedFields(allFields, field);
+ const fieldConfig = useMemo(
+ () => ({
+ ...getFieldConfig('path'),
+ deserializer: getDeserializer(allFields),
+ }),
+ [allFields]
+ );
+
return (
-
+
{(pathField) => {
const error = pathField.getErrorsMessages();
const isInvalid = error ? Boolean(error.length) : false;
diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx
index 95575124b6abd..6b5a848ce85d3 100644
--- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx
@@ -163,7 +163,7 @@ export const EditField = React.memo(({ form, field, allFields, exitEdit, updateF
- {form.isSubmitted && !form.isValid && (
+ {form.isSubmitted && form.isValid === false && (
<>
{i18n.translate('xpack.idxMgmt.mappingsEditor.editFieldUpdateButtonLabel', {
diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx
index f2ad37cb45818..3b55c5ac076c2 100644
--- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx
@@ -59,6 +59,9 @@ export const EditFieldHeaderForm = React.memo(
{({ type, subType }) => {
+ if (!type) {
+ return null;
+ }
const typeDefinition = TYPE_DEFINITION[type[0].value as MainType];
const hasSubType = typeDefinition.subTypes !== undefined;
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processor_settings_fields.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processor_settings_fields.tsx
index 6a70592bc2f70..9adb3957ea9f4 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processor_settings_fields.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processor_settings_fields.tsx
@@ -35,7 +35,7 @@ export const ProcessorSettingsFields: FunctionComponent = ({ processor })
if (formDescriptor?.FieldsComponent) {
return (
<>
-
+
>
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/append.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/append.tsx
index 09d0981adf1c2..23425297f3420 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/append.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/append.tsx
@@ -21,6 +21,7 @@ const { emptyField } = fieldValidators;
const fieldsConfig: FieldsConfig = {
value: {
+ defaultValue: [],
type: FIELD_TYPES.COMBO_BOX,
deserializer: to.arrayOfStrings,
label: i18n.translate('xpack.ingestPipelines.pipelineEditor.appendForm.valueFieldLabel', {
diff --git a/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx b/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx
index 9ead8171bfef6..c7810af13eb74 100644
--- a/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx
+++ b/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx
@@ -23,7 +23,7 @@ import {
createKibanaContextProviderMock,
createStartServicesMock,
} from '../lib/kibana/kibana_react.mock';
-import { FieldHook, useForm } from '../../shared_imports';
+import { FieldHook } from '../../shared_imports';
import { SUB_PLUGINS_REDUCER } from './utils';
import { createSecuritySolutionStorageMock, localStorageMock } from './mock_local_storage';
@@ -78,8 +78,6 @@ const TestProvidersComponent: React.FC = ({
export const TestProviders = React.memo(TestProvidersComponent);
export const useFormFieldMock = (options?: Partial): FieldHook => {
- const { form } = useForm();
-
return {
path: 'path',
type: 'type',
@@ -88,7 +86,6 @@ export const useFormFieldMock = (options?: Partial): FieldHook => {
isValidating: false,
isValidated: false,
isChangingValue: false,
- form,
errors: [],
isValid: true,
getErrorsMessages: jest.fn(),
@@ -98,7 +95,7 @@ export const useFormFieldMock = (options?: Partial): FieldHook => {
clearErrors: jest.fn(),
validate: jest.fn(),
reset: jest.fn(),
- __serializeOutput: jest.fn(),
+ __serializeValue: jest.fn(),
...options,
};
};
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/risk_score_mapping/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/risk_score_mapping/index.tsx
index a0384ef52a841..cdeca54bfc39b 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/risk_score_mapping/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/risk_score_mapping/index.tsx
@@ -13,13 +13,13 @@ import {
EuiFormLabel,
EuiIcon,
EuiSpacer,
+ EuiRange,
} from '@elastic/eui';
import React, { useCallback, useMemo } from 'react';
import styled from 'styled-components';
import { noop } from 'lodash/fp';
import * as i18n from './translations';
import { FieldHook } from '../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib';
-import { CommonUseField } from '../../../../cases/components/create';
import { AboutStepRiskScore } from '../../../pages/detection_engine/rules/types';
import { FieldComponent } from '../../../../common/components/autocomplete/field';
import { IFieldType } from '../../../../../../../../src/plugins/data/common/index_patterns/fields';
@@ -59,11 +59,12 @@ export const RiskScoreField = ({
placeholder,
}: RiskScoreFieldProps) => {
const fieldTypeFilter = useMemo(() => ['number'], []);
+ const { value: fieldValue, setValue } = field;
const handleFieldChange = useCallback(
([newField]: IFieldType[]): void => {
- const values = field.value as AboutStepRiskScore;
- field.setValue({
+ const values = fieldValue as AboutStepRiskScore;
+ setValue({
value: values.value,
isMappingChecked: values.isMappingChecked,
mapping: [
@@ -76,25 +77,37 @@ export const RiskScoreField = ({
],
});
},
- [field]
+ [setValue, fieldValue]
+ );
+
+ const handleRangeFieldChange = useCallback(
+ (e: React.ChangeEvent | React.MouseEvent): void => {
+ const range = (e.target as HTMLInputElement).value;
+ setValue({
+ value: range.trim() === '' ? '' : +range,
+ isMappingChecked: (fieldValue as AboutStepRiskScore).isMappingChecked,
+ mapping: (fieldValue as AboutStepRiskScore).mapping,
+ });
+ },
+ [fieldValue, setValue]
);
const selectedField = useMemo(() => {
- const existingField = (field.value as AboutStepRiskScore).mapping?.[0]?.field ?? '';
+ const existingField = (fieldValue as AboutStepRiskScore).mapping?.[0]?.field ?? '';
const [newSelectedField] = indices.fields.filter(
({ name }) => existingField != null && existingField === name
);
return newSelectedField;
- }, [field.value, indices]);
+ }, [fieldValue, indices]);
const handleRiskScoreMappingChecked = useCallback(() => {
- const values = field.value as AboutStepRiskScore;
- field.setValue({
+ const values = fieldValue as AboutStepRiskScore;
+ setValue({
value: values.value,
mapping: [...values.mapping],
isMappingChecked: !values.isMappingChecked,
});
- }, [field]);
+ }, [fieldValue, setValue]);
const riskScoreLabel = useMemo(() => {
return (
@@ -119,7 +132,7 @@ export const RiskScoreField = ({
@@ -132,7 +145,7 @@ export const RiskScoreField = ({
);
- }, [field.value, handleRiskScoreMappingChecked, isDisabled]);
+ }, [fieldValue, handleRiskScoreMappingChecked, isDisabled]);
return (
@@ -144,24 +157,20 @@ export const RiskScoreField = ({
error={'errorMessage'}
isInvalid={false}
fullWidth
- data-test-subj={dataTestSubj}
- describedByIds={idAria ? [idAria] : undefined}
+ data-test-subj="detectionEngineStepAboutRuleRiskScore"
+ describedByIds={['detectionEngineStepAboutRuleRiskScore']}
>
-
@@ -170,7 +179,7 @@ export const RiskScoreField = ({
label={riskScoreMappingLabel}
labelAppend={field.labelAppend}
helpText={
- (field.value as AboutStepRiskScore).isMappingChecked ? (
+ (fieldValue as AboutStepRiskScore).isMappingChecked ? (
{i18n.RISK_SCORE_MAPPING_DETAILS}
) : (
''
@@ -184,7 +193,7 @@ export const RiskScoreField = ({
>
- {(field.value as AboutStepRiskScore).isMappingChecked && (
+ {(fieldValue as AboutStepRiskScore).isMappingChecked && (
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.test.tsx
index a9bde76126b6e..20c3073789b2a 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.test.tsx
@@ -8,6 +8,7 @@ import React from 'react';
import { shallow } from 'enzyme';
import { RuleActionsField } from './index';
+import { useForm, Form } from '../../../../shared_imports';
import { useKibana } from '../../../../common/lib/kibana';
import { useFormFieldMock } from '../../../../common/mock';
jest.mock('../../../../common/lib/kibana');
@@ -32,8 +33,13 @@ describe('RuleActionsField', () => {
});
const Component = () => {
const field = useFormFieldMock();
+ const { form } = useForm();
- return ;
+ return (
+
+ );
};
const wrapper = shallow();
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx
index c6ff25f311d9c..b9097949bd20a 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx
@@ -12,7 +12,7 @@ import ReactMarkdown from 'react-markdown';
import styled from 'styled-components';
import { NOTIFICATION_SUPPORTED_ACTION_TYPES_IDS } from '../../../../../common/constants';
-import { SelectField } from '../../../../shared_imports';
+import { SelectField, useFormContext } from '../../../../shared_imports';
import {
ActionForm,
ActionType,
@@ -37,6 +37,8 @@ const FieldErrorsContainer = styled.div`
export const RuleActionsField: ThrottleSelectField = ({ field, messageVariables }) => {
const [fieldErrors, setFieldErrors] = useState(null);
const [supportedActionTypes, setSupportedActionTypes] = useState();
+ const form = useFormContext();
+ const { isSubmitted, isSubmitting, isValid } = form;
const {
http,
triggers_actions_ui: { actionTypeRegistry },
@@ -88,26 +90,14 @@ export const RuleActionsField: ThrottleSelectField = ({ field, messageVariables
}, []);
useEffect(() => {
- if (field.form.isSubmitting || !field.errors.length) {
+ if (isSubmitting || !field.errors.length) {
return setFieldErrors(null);
}
- if (
- field.form.isSubmitted &&
- !field.form.isSubmitting &&
- field.form.isValid === false &&
- field.errors.length
- ) {
+ if (isSubmitted && !isSubmitting && isValid === false && field.errors.length) {
const errorsString = field.errors.map(({ message }) => message).join('\n');
return setFieldErrors(errorsString);
}
- }, [
- field.form.isSubmitted,
- field.form.isSubmitting,
- field.isChangingValue,
- field.form.isValid,
- field.errors,
- setFieldErrors,
- ]);
+ }, [isSubmitted, isSubmitting, field.isChangingValue, isValid, field.errors, setFieldErrors]);
if (!supportedActionTypes) return <>>;
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/severity_mapping/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/severity_mapping/index.tsx
index 733e701cff204..70e66af25f69e 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/severity_mapping/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/severity_mapping/index.tsx
@@ -13,6 +13,7 @@ import {
EuiFormLabel,
EuiIcon,
EuiSpacer,
+ EuiSuperSelect,
} from '@elastic/eui';
import { noop } from 'lodash/fp';
import React, { useCallback, useMemo } from 'react';
@@ -20,7 +21,6 @@ import styled from 'styled-components';
import * as i18n from './translations';
import { FieldHook } from '../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib';
import { SeverityOptionItem } from '../step_about_rule/data';
-import { CommonUseField } from '../../../../cases/components/create';
import { AboutStepSeverity } from '../../../pages/detection_engine/rules/types';
import {
IFieldType,
@@ -68,58 +68,61 @@ export const SeverityField = ({
options,
}: SeverityFieldProps) => {
const fieldValueInputWidth = 160;
+ const { setValue } = field;
+ const { value, isMappingChecked, mapping } = field.value as AboutStepSeverity;
const handleFieldValueChange = useCallback(
(newMappingItems: SeverityMapping, index: number): void => {
- const values = field.value as AboutStepSeverity;
- field.setValue({
- value: values.value,
- isMappingChecked: values.isMappingChecked,
- mapping: [
- ...values.mapping.slice(0, index),
- ...newMappingItems,
- ...values.mapping.slice(index + 1),
- ],
+ setValue({
+ value,
+ isMappingChecked,
+ mapping: [...mapping.slice(0, index), ...newMappingItems, ...mapping.slice(index + 1)],
});
},
- [field]
+ [value, isMappingChecked, mapping, setValue]
);
const handleFieldChange = useCallback(
(index: number, severity: Severity, [newField]: IFieldType[]): void => {
- const values = field.value as AboutStepSeverity;
const newMappingItems: SeverityMapping = [
{
- ...values.mapping[index],
+ ...mapping[index],
field: newField?.name ?? '',
- value: newField != null ? values.mapping[index].value : '',
+ value: newField != null ? mapping[index].value : '',
operator: 'equals',
severity,
},
];
handleFieldValueChange(newMappingItems, index);
},
- [field, handleFieldValueChange]
+ [mapping, handleFieldValueChange]
+ );
+
+ const handleSecurityLevelChange = useCallback(
+ (newValue: string) => {
+ setValue({
+ value: newValue,
+ isMappingChecked,
+ mapping,
+ });
+ },
+ [isMappingChecked, mapping, setValue]
);
const handleFieldMatchValueChange = useCallback(
(index: number, severity: Severity, newMatchValue: string): void => {
- const values = field.value as AboutStepSeverity;
const newMappingItems: SeverityMapping = [
{
- ...values.mapping[index],
- field: values.mapping[index].field,
- value:
- values.mapping[index].field != null && values.mapping[index].field !== ''
- ? newMatchValue
- : '',
+ ...mapping[index],
+ field: mapping[index].field,
+ value: mapping[index].field != null && mapping[index].field !== '' ? newMatchValue : '',
operator: 'equals',
severity,
},
];
handleFieldValueChange(newMappingItems, index);
},
- [field, handleFieldValueChange]
+ [mapping, handleFieldValueChange]
);
const getIFieldTypeFromFieldName = (
@@ -131,13 +134,12 @@ export const SeverityField = ({
};
const handleSeverityMappingChecked = useCallback(() => {
- const values = field.value as AboutStepSeverity;
- field.setValue({
- value: values.value,
- mapping: [...values.mapping],
- isMappingChecked: !values.isMappingChecked,
+ setValue({
+ value,
+ mapping: [...mapping],
+ isMappingChecked: !isMappingChecked,
});
- }, [field]);
+ }, [isMappingChecked, mapping, value, setValue]);
const severityLabel = useMemo(() => {
return (
@@ -162,7 +164,7 @@ export const SeverityField = ({
@@ -175,7 +177,7 @@ export const SeverityField = ({
);
- }, [field.value, handleSeverityMappingChecked, isDisabled]);
+ }, [handleSeverityMappingChecked, isDisabled, isMappingChecked]);
return (
@@ -187,21 +189,16 @@ export const SeverityField = ({
error={'errorMessage'}
isInvalid={false}
fullWidth
- data-test-subj={dataTestSubj}
- describedByIds={idAria ? [idAria] : undefined}
+ data-test-subj="detectionEngineStepAboutRuleSeverity"
+ describedByIds={['detectionEngineStepAboutRuleSeverity']}
>
-
@@ -211,11 +208,7 @@ export const SeverityField = ({
label={severityMappingLabel}
labelAppend={field.labelAppend}
helpText={
- (field.value as AboutStepSeverity).isMappingChecked ? (
- {i18n.SEVERITY_MAPPING_DETAILS}
- ) : (
- ''
- )
+ isMappingChecked ? {i18n.SEVERITY_MAPPING_DETAILS} : ''
}
error={'errorMessage'}
isInvalid={false}
@@ -225,7 +218,7 @@ export const SeverityField = ({
>
- {(field.value as AboutStepSeverity).isMappingChecked && (
+ {isMappingChecked && (
@@ -242,71 +235,69 @@ export const SeverityField = ({
- {(field.value as AboutStepSeverity).mapping.map(
- (severityMappingItem: SeverityMappingItem, index) => (
-
-
-
-
-
+ {mapping.map((severityMappingItem: SeverityMappingItem, index) => (
+
+
+
+
+
-
-
-
-
-
-
-
- {
- options.find((o) => o.value === severityMappingItem.severity)
- ?.inputDisplay
- }
-
-
-
- )
- )}
+
+
+
+
+
+
+
+ {
+ options.find((o) => o.value === severityMappingItem.severity)
+ ?.inputDisplay
+ }
+
+
+
+ ))}
)}
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.test.tsx
index cb3fd5e5bec32..0c834b9fff33a 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.test.tsx
@@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
+import { act } from 'react-dom/test-utils';
import { mount, shallow } from 'enzyme';
import { ThemeProvider } from 'styled-components';
import euiDarkVars from '@elastic/eui/dist/eui_theme_light.json';
@@ -223,32 +224,33 @@ describe('StepAboutRuleComponent', () => {
.first()
.simulate('change', { target: { value: '80' } });
- wrapper.find('[data-test-subj="about-continue"]').first().simulate('click').update();
- await waitFor(() => {
- const expected: Omit = {
- author: [],
- isAssociatedToEndpointList: false,
- isBuildingBlock: false,
- license: '',
- ruleNameOverride: '',
- timestampOverride: '',
- description: 'Test description text',
- falsePositives: [''],
- name: 'Test name text',
- note: '',
- references: [''],
- riskScore: { value: 80, mapping: [], isMappingChecked: false },
- severity: { value: 'low', mapping: fillEmptySeverityMappings([]), isMappingChecked: false },
- tags: [],
- threat: [
- {
- framework: 'MITRE ATT&CK',
- tactic: { id: 'none', name: 'none', reference: 'none' },
- technique: [],
- },
- ],
- };
- expect(stepDataMock.mock.calls[1][1]).toEqual(expected);
+ await act(async () => {
+ wrapper.find('[data-test-subj="about-continue"]').first().simulate('click').update();
});
+
+ const expected: Omit = {
+ author: [],
+ isAssociatedToEndpointList: false,
+ isBuildingBlock: false,
+ license: '',
+ ruleNameOverride: '',
+ timestampOverride: '',
+ description: 'Test description text',
+ falsePositives: [''],
+ name: 'Test name text',
+ note: '',
+ references: [''],
+ riskScore: { value: 80, mapping: [], isMappingChecked: false },
+ severity: { value: 'low', mapping: fillEmptySeverityMappings([]), isMappingChecked: false },
+ tags: [],
+ threat: [
+ {
+ framework: 'MITRE ATT&CK',
+ tactic: { id: 'none', name: 'none', reference: 'none' },
+ technique: [],
+ },
+ ],
+ };
+ expect(stepDataMock.mock.calls[1][1]).toEqual(expected);
});
});
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/schema.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/schema.tsx
index a3db8fe659d84..2264a11341eb8 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/schema.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/schema.tsx
@@ -102,29 +102,12 @@ export const schema: FormSchema = {
labelAppend: OptionalFieldLabel,
},
severity: {
- value: {
- type: FIELD_TYPES.SUPER_SELECT,
- validations: [
- {
- validator: emptyField(
- i18n.translate(
- 'xpack.securitySolution.detectionEngine.createRule.stepAboutRule.severityFieldRequiredError',
- {
- defaultMessage: 'A severity is required.',
- }
- )
- ),
- },
- ],
- },
+ value: {},
mapping: {},
isMappingChecked: {},
},
riskScore: {
- value: {
- type: FIELD_TYPES.RANGE,
- serializer: (input: string) => Number(input),
- },
+ value: {},
mapping: {},
isMappingChecked: {},
},
diff --git a/x-pack/plugins/security_solution/public/shared_imports.ts b/x-pack/plugins/security_solution/public/shared_imports.ts
index b2c7319b94576..097166a9c866a 100644
--- a/x-pack/plugins/security_solution/public/shared_imports.ts
+++ b/x-pack/plugins/security_solution/public/shared_imports.ts
@@ -20,6 +20,7 @@ export {
UseField,
UseMultiFields,
useForm,
+ useFormContext,
ValidationFunc,
VALIDATION_TYPES,
} from '../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib';
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 7c99127982cf4..6c86888145f49 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -15651,7 +15651,6 @@
"xpack.securitySolution.detectionEngine.createRule.stepAboutRule.guideLabel": "調査ガイド",
"xpack.securitySolution.detectionEngine.createRule.stepAboutRule.nameFieldRequiredError": "名前が必要です。",
"xpack.securitySolution.detectionEngine.createRule.stepAboutrule.noteHelpText": "ルール調査ガイドを追加...",
- "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.severityFieldRequiredError": "深刻度が必要です。",
"xpack.securitySolution.detectionEngine.createRule.stepAboutRuleForm.addFalsePositiveDescription": "誤検出の例を追加します",
"xpack.securitySolution.detectionEngine.createRule.stepAboutRuleForm.addReferenceDescription": "参照URLを追加します",
"xpack.securitySolution.detectionEngine.createRule.stepAboutRuleForm.advancedSettingsButton": "高度な設定",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index f81b989575be9..84d4387a4cbbd 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -15657,7 +15657,6 @@
"xpack.securitySolution.detectionEngine.createRule.stepAboutRule.guideLabel": "调查指南",
"xpack.securitySolution.detectionEngine.createRule.stepAboutRule.nameFieldRequiredError": "名称必填。",
"xpack.securitySolution.detectionEngine.createRule.stepAboutrule.noteHelpText": "添加规则调查指南......",
- "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.severityFieldRequiredError": "严重性必填。",
"xpack.securitySolution.detectionEngine.createRule.stepAboutRuleForm.addFalsePositiveDescription": "添加误报示例",
"xpack.securitySolution.detectionEngine.createRule.stepAboutRuleForm.addReferenceDescription": "添加引用 URL",
"xpack.securitySolution.detectionEngine.createRule.stepAboutRuleForm.advancedSettingsButton": "高级设置",