mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
# Backport This will backport the following commits from `main` to `8.7`: - [Fix validation for entry fields in exception form (#151654)](https://github.com/elastic/kibana/pull/151654) <!--- Backport version: 8.9.7 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Khristinin Nikita","email":"nikita.khristinin@elastic.co"},"sourceCommit":{"committedDate":"2023-02-21T15:59:54Z","message":"Fix validation for entry fields in exception form (#151654)\n\n## Change validation logic for entry exception field.\r\n\r\nClose:\r\n[https://github.com/elastic/kibana/issues/143051](https://github.com/elastic/kibana/issues/143051)\r\n\r\nPreviously we didn't keep a validation state per field which caused a\r\nreset of validation if we still had invalid fields. Or we can have an\r\ninvalid state for the form, but we removed the invalid field. You can\r\nsee the videos on the ticket above.\r\n\r\n## Solution:\r\nKeep validation state per field, like:\r\n```js \r\n{\r\n [entry.id]: true,\r\n}\r\n```\r\nThis state can keep old fields, which already were removed, this is why\r\nwe use the selector to get the actual amount of errors.\r\n\r\n\r\n\r\nhttps://user-images.githubusercontent.com/7609147/220337447-95c1558c-aa85-43d1-87e8-76370aeaf141.mov\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"d93eaa010935518c45fbab1f8d57542f1ac1a83a","branchLabelMapping":{"^v8.8.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","Team:Security Solution Platform","backport:prev-minor","v8.8.0"],"number":151654,"url":"https://github.com/elastic/kibana/pull/151654","mergeCommit":{"message":"Fix validation for entry fields in exception form (#151654)\n\n## Change validation logic for entry exception field.\r\n\r\nClose:\r\n[https://github.com/elastic/kibana/issues/143051](https://github.com/elastic/kibana/issues/143051)\r\n\r\nPreviously we didn't keep a validation state per field which caused a\r\nreset of validation if we still had invalid fields. Or we can have an\r\ninvalid state for the form, but we removed the invalid field. You can\r\nsee the videos on the ticket above.\r\n\r\n## Solution:\r\nKeep validation state per field, like:\r\n```js \r\n{\r\n [entry.id]: true,\r\n}\r\n```\r\nThis state can keep old fields, which already were removed, this is why\r\nwe use the selector to get the actual amount of errors.\r\n\r\n\r\n\r\nhttps://user-images.githubusercontent.com/7609147/220337447-95c1558c-aa85-43d1-87e8-76370aeaf141.mov\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"d93eaa010935518c45fbab1f8d57542f1ac1a83a"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v8.8.0","labelRegex":"^v8.8.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/151654","number":151654,"mergeCommit":{"message":"Fix validation for entry fields in exception form (#151654)\n\n## Change validation logic for entry exception field.\r\n\r\nClose:\r\n[https://github.com/elastic/kibana/issues/143051](https://github.com/elastic/kibana/issues/143051)\r\n\r\nPreviously we didn't keep a validation state per field which caused a\r\nreset of validation if we still had invalid fields. Or we can have an\r\ninvalid state for the form, but we removed the invalid field. You can\r\nsee the videos on the ticket above.\r\n\r\n## Solution:\r\nKeep validation state per field, like:\r\n```js \r\n{\r\n [entry.id]: true,\r\n}\r\n```\r\nThis state can keep old fields, which already were removed, this is why\r\nwe use the selector to get the actual amount of errors.\r\n\r\n\r\n\r\nhttps://user-images.githubusercontent.com/7609147/220337447-95c1558c-aa85-43d1-87e8-76370aeaf141.mov\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"d93eaa010935518c45fbab1f8d57542f1ac1a83a"}}]}] BACKPORT--> Co-authored-by: Khristinin Nikita <nikita.khristinin@elastic.co>
This commit is contained in:
parent
6caf499314
commit
dbecc74dea
7 changed files with 59 additions and 30 deletions
|
@ -214,7 +214,10 @@ export const AutocompleteFieldMatchComponent: React.FC<AutocompleteFieldMatchPro
|
|||
if (onError != null) onError(false);
|
||||
|
||||
handleSpacesWarning(selectedValue);
|
||||
}, [selectedField, selectedValue, handleSpacesWarning, onError]);
|
||||
// Looks like selectedField return new object every time when we for example add "and" entry
|
||||
// that's why we need to check for name and type here
|
||||
// Probably we should use some kind of memoization on parent components for entries
|
||||
}, [selectedField?.name, selectedField?.type, selectedValue, handleSpacesWarning, onError]);
|
||||
|
||||
const defaultInput = useMemo((): JSX.Element => {
|
||||
return (
|
||||
|
|
|
@ -913,7 +913,7 @@ describe('BuilderEntryItem', () => {
|
|||
).onBlur();
|
||||
});
|
||||
|
||||
expect(mockSetErrorExists).toHaveBeenCalledWith(true);
|
||||
expect(mockSetErrorExists).toHaveBeenCalledWith({ '123': true });
|
||||
});
|
||||
|
||||
test('it invokes "setErrorsExist" when invalid value inputted for field value input', async () => {
|
||||
|
@ -960,7 +960,7 @@ describe('BuilderEntryItem', () => {
|
|||
).onSearchChange('hellooo');
|
||||
});
|
||||
|
||||
expect(mockSetErrorExists).toHaveBeenCalledWith(true);
|
||||
expect(mockSetErrorExists).toHaveBeenCalledWith({ '123': true });
|
||||
});
|
||||
|
||||
test('it invokes "setWarningsExist" when invalid value in field value input', async () => {
|
||||
|
|
|
@ -60,6 +60,7 @@ import { HttpStart } from '@kbn/core/public';
|
|||
import { getEmptyValue } from '../../../common/empty_value';
|
||||
|
||||
import * as i18n from './translations';
|
||||
import { EntryFieldError } from './reducer';
|
||||
|
||||
const FieldFlexItem = styled(EuiFlexItem)`
|
||||
overflow: hidden;
|
||||
|
@ -81,7 +82,7 @@ export interface EntryItemProps {
|
|||
) => DataViewBase;
|
||||
onChange: (arg: BuilderEntry, i: number) => void;
|
||||
onlyShowListOperators?: boolean;
|
||||
setErrorsExist: (arg: boolean) => void;
|
||||
setErrorsExist: (arg: EntryFieldError) => void;
|
||||
setWarningsExist: (arg: boolean) => void;
|
||||
isDisabled?: boolean;
|
||||
operatorsList?: OperatorOption[];
|
||||
|
@ -110,9 +111,9 @@ export const BuilderEntryItem: React.FC<EntryItemProps> = ({
|
|||
|
||||
const handleError = useCallback(
|
||||
(err: boolean): void => {
|
||||
setErrorsExist(err);
|
||||
setErrorsExist({ [entry.id]: err });
|
||||
},
|
||||
[setErrorsExist]
|
||||
[setErrorsExist, entry.id]
|
||||
);
|
||||
const handleWarning = useCallback(
|
||||
(warn: boolean): void => {
|
||||
|
|
|
@ -24,6 +24,7 @@ import { DataViewBase } from '@kbn/es-query';
|
|||
import { BuilderAndBadgeComponent } from './and_badge';
|
||||
import { BuilderEntryDeleteButtonComponent } from './entry_delete_button';
|
||||
import { BuilderEntryItem } from './entry_renderer';
|
||||
import { EntryFieldError } from './reducer';
|
||||
|
||||
const MyBeautifulLine = styled(EuiFlexItem)`
|
||||
&:after {
|
||||
|
@ -58,7 +59,7 @@ interface BuilderExceptionListItemProps {
|
|||
) => DataViewBase;
|
||||
onDeleteExceptionItem: (item: ExceptionsBuilderExceptionItem, index: number) => void;
|
||||
onChangeExceptionItem: (item: ExceptionsBuilderExceptionItem, index: number) => void;
|
||||
setErrorsExist: (arg: boolean) => void;
|
||||
setErrorsExist: (arg: EntryFieldError) => void;
|
||||
setWarningsExist: (arg: boolean) => void;
|
||||
onlyShowListOperators?: boolean;
|
||||
isDisabled?: boolean;
|
||||
|
|
|
@ -38,7 +38,8 @@ import { AndOrBadge } from '../and_or_badge';
|
|||
|
||||
import { BuilderExceptionListItemComponent } from './exception_item_renderer';
|
||||
import { BuilderLogicButtons } from './logic_buttons';
|
||||
import { State, exceptionsBuilderReducer } from './reducer';
|
||||
import { getTotalErrorExist } from './selectors';
|
||||
import { EntryFieldError, State, exceptionsBuilderReducer } from './reducer';
|
||||
|
||||
const MyInvisibleAndBadge = styled(EuiFlexItem)`
|
||||
visibility: hidden;
|
||||
|
@ -60,7 +61,7 @@ const initialState: State = {
|
|||
disableAnd: false,
|
||||
disableNested: false,
|
||||
disableOr: false,
|
||||
errorExists: 0,
|
||||
errors: {},
|
||||
exceptions: [],
|
||||
exceptionsToDelete: [],
|
||||
warningExists: 0,
|
||||
|
@ -121,30 +122,30 @@ export const ExceptionBuilderComponent = ({
|
|||
operatorsList,
|
||||
allowCustomFieldOptions = false,
|
||||
}: ExceptionBuilderProps): JSX.Element => {
|
||||
const [
|
||||
{
|
||||
addNested,
|
||||
andLogicIncluded,
|
||||
disableAnd,
|
||||
disableNested,
|
||||
disableOr,
|
||||
errorExists,
|
||||
warningExists,
|
||||
exceptions,
|
||||
exceptionsToDelete,
|
||||
},
|
||||
dispatch,
|
||||
] = useReducer(exceptionsBuilderReducer(), {
|
||||
const [state, dispatch] = useReducer(exceptionsBuilderReducer(), {
|
||||
...initialState,
|
||||
disableAnd: isAndDisabled,
|
||||
disableNested: isNestedDisabled,
|
||||
disableOr: isOrDisabled,
|
||||
});
|
||||
|
||||
const {
|
||||
addNested,
|
||||
andLogicIncluded,
|
||||
disableAnd,
|
||||
disableNested,
|
||||
disableOr,
|
||||
warningExists,
|
||||
exceptions,
|
||||
exceptionsToDelete,
|
||||
} = state;
|
||||
|
||||
const errorExists = getTotalErrorExist(state);
|
||||
|
||||
const setErrorsExist = useCallback(
|
||||
(hasErrors: boolean): void => {
|
||||
(error: EntryFieldError): void => {
|
||||
dispatch({
|
||||
errorExists: hasErrors,
|
||||
error,
|
||||
type: 'setErrorsExist',
|
||||
});
|
||||
},
|
||||
|
|
|
@ -16,6 +16,8 @@ import {
|
|||
|
||||
export type ViewerModalName = 'addModal' | 'editModal' | null;
|
||||
|
||||
export type EntryFieldError = Record<string, boolean>;
|
||||
|
||||
export interface State {
|
||||
disableAnd: boolean;
|
||||
disableNested: boolean;
|
||||
|
@ -24,7 +26,7 @@ export interface State {
|
|||
addNested: boolean;
|
||||
exceptions: ExceptionsBuilderExceptionItem[];
|
||||
exceptionsToDelete: ExceptionListItemSchema[];
|
||||
errorExists: number;
|
||||
errors: EntryFieldError;
|
||||
warningExists: number;
|
||||
}
|
||||
|
||||
|
@ -56,7 +58,7 @@ export type Action =
|
|||
}
|
||||
| {
|
||||
type: 'setErrorsExist';
|
||||
errorExists: boolean;
|
||||
error: EntryFieldError;
|
||||
}
|
||||
| {
|
||||
type: 'setWarningsExist';
|
||||
|
@ -125,12 +127,14 @@ export const exceptionsBuilderReducer =
|
|||
};
|
||||
}
|
||||
case 'setErrorsExist': {
|
||||
const { errorExists } = state;
|
||||
const errTotal = action.errorExists ? errorExists + 1 : errorExists - 1;
|
||||
const newErrorsState = {
|
||||
...state.errors,
|
||||
...action.error,
|
||||
};
|
||||
|
||||
return {
|
||||
...state,
|
||||
errorExists: errTotal < 0 ? 0 : errTotal,
|
||||
errors: newErrorsState,
|
||||
};
|
||||
}
|
||||
case 'setWarningsExist': {
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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 type { State } from './reducer';
|
||||
|
||||
export const getTotalErrorExist = (state: State): number => {
|
||||
const { exceptions, errors } = state;
|
||||
const allEntryIds = exceptions
|
||||
.map((exception) => exception.entries.map((entry) => entry.id))
|
||||
.flat();
|
||||
const errTotal = Object.keys(errors).filter(
|
||||
(id) => allEntryIds.includes(id) && errors[id]
|
||||
).length;
|
||||
return errTotal;
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue