diff --git a/x-pack/plugins/security_solution/public/common/components/autocomplete/field.tsx b/x-pack/plugins/security_solution/public/common/components/autocomplete/field.tsx index fab2b1e4a74636..48b24a79bd7c0f 100644 --- a/x-pack/plugins/security_solution/public/common/components/autocomplete/field.tsx +++ b/x-pack/plugins/security_solution/public/common/components/autocomplete/field.tsx @@ -36,11 +36,11 @@ export const FieldComponent: React.FC = ({ onChange, }): JSX.Element => { const [touched, setIsTouched] = useState(false); - const getLabel = useCallback((field): string => field.name, []); + const getLabel = useCallback(({ name }): string => name, []); const optionsMemo = useMemo((): IFieldType[] => { if (indexPattern != null) { if (fieldTypeFilter.length > 0) { - return indexPattern.fields.filter((f) => fieldTypeFilter.includes(f.type)); + return indexPattern.fields.filter(({ type }) => fieldTypeFilter.includes(type)); } else { return indexPattern.fields; } @@ -68,6 +68,10 @@ export const FieldComponent: React.FC = ({ onChange(newValues); }; + const handleTouch = useCallback((): void => { + setIsTouched(true); + }, [setIsTouched]); + return ( = ({ isDisabled={isDisabled} isClearable={isClearable} isInvalid={isRequired ? touched && selectedField == null : false} - onFocus={() => setIsTouched(true)} + onFocus={handleTouch} singleSelection={{ asPlainText: true }} data-test-subj="fieldAutocompleteComboBox" style={{ width: `${fieldInputWidth}px` }} diff --git a/x-pack/plugins/security_solution/public/common/components/autocomplete/helpers.ts b/x-pack/plugins/security_solution/public/common/components/autocomplete/helpers.ts index 3dcaf612da649e..8bbc0221814752 100644 --- a/x-pack/plugins/security_solution/public/common/components/autocomplete/helpers.ts +++ b/x-pack/plugins/security_solution/public/common/components/autocomplete/helpers.ts @@ -69,11 +69,12 @@ export function getGenericComboBoxProps({ const newLabels = options.map(getLabel); const newComboOptions: EuiComboBoxOptionOption[] = newLabels.map((label) => ({ label })); const newSelectedComboOptions = selectedOptions + .map(getLabel) .filter((option) => { - return options.indexOf(option) !== -1; + return newLabels.indexOf(option) !== -1; }) .map((option) => { - return newComboOptions[options.indexOf(option)]; + return newComboOptions[newLabels.indexOf(option)]; }); return { diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/entry_item.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/entry_item.tsx index 3044f6d01b745c..450b48a793e4e8 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/entry_item.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/entry_item.tsx @@ -5,6 +5,7 @@ */ import React, { useCallback } from 'react'; import { EuiFormRow, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import styled from 'styled-components'; import { IFieldType, IIndexPattern } from '../../../../../../../../src/plugins/data/common'; import { FieldComponent } from '../../autocomplete/field'; @@ -29,6 +30,10 @@ import { } from './helpers'; import { EXCEPTION_OPERATORS_ONLY_LISTS } from '../../autocomplete/operators'; +const MyValuesInput = styled(EuiFlexItem)` + overflow: hidden; +`; + interface EntryItemProps { entry: FormattedBuilderEntry; indexPattern: IIndexPattern; @@ -257,12 +262,12 @@ export const BuilderEntryItem: React.FC = ({ > {renderFieldInput(showLabel)} {renderOperatorInput(showLabel)} - + {renderFieldValueInput( showLabel, entry.nested === 'parent' ? OperatorTypeEnum.EXISTS : entry.operator.type )} - + ); }; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/exception_item.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/exception_item.tsx index cd8b66acd223a2..49a159cdfe6232 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/exception_item.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/exception_item.tsx @@ -26,6 +26,11 @@ const MyBeautifulLine = styled(EuiFlexItem)` } `; +const MyOverflowContainer = styled(EuiFlexItem)` + overflow: hidden; + width: 100%; +`; + interface BuilderExceptionListItemProps { exceptionItem: ExceptionsBuilderExceptionItem; exceptionId: string; @@ -98,13 +103,13 @@ export const BuilderExceptionListItemComponent = React.memo )} - + {entries.map((item, index) => ( {item.nested === 'child' && } - + - + ))} - + ); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/helpers.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/helpers.test.tsx index a3c5d09a0fb645..04ab9ee7216f7e 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/helpers.test.tsx @@ -161,10 +161,7 @@ describe('Exception builder helpers', () => { const payloadItem: FormattedBuilderEntry = getMockNestedBuilderEntry(); const output = getFilteredIndexPatterns(payloadIndexPattern, payloadItem, 'detection'); const expected: IIndexPattern = { - fields: [ - { ...getField('nestedField.child') }, - { ...getField('nestedField.nestedChild.doublyNestedChild') }, - ], + fields: [{ ...getField('nestedField.child'), name: 'child' }], id: '1234', title: 'logstash-*', }; @@ -243,7 +240,7 @@ describe('Exception builder helpers', () => { }; const output = getFilteredIndexPatterns(payloadIndexPattern, payloadItem, 'endpoint'); const expected: IIndexPattern = { - fields: [getEndpointField('file.Ext.code_signature.status')], + fields: [{ ...getEndpointField('file.Ext.code_signature.status'), name: 'status' }], id: '1234', title: 'logstash-*', }; @@ -405,7 +402,7 @@ describe('Exception builder helpers', () => { aggregatable: false, count: 0, esTypes: ['text'], - name: 'nestedField.child', + name: 'child', readFromDocValues: false, scripted: false, searchable: true, @@ -600,7 +597,7 @@ describe('Exception builder helpers', () => { aggregatable: false, count: 0, esTypes: ['text'], - name: 'nestedField.child', + name: 'child', readFromDocValues: false, scripted: false, searchable: true, diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/helpers.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/helpers.tsx index bea1eb8f0e17ba..2fda14dfa04d79 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/helpers.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/helpers.tsx @@ -60,13 +60,20 @@ export const getFilteredIndexPatterns = ( // when user has selected a nested entry, only fields with the common parent are shown return { ...indexPatterns, - fields: indexPatterns.fields.filter( - (field) => - field.subType != null && - field.subType.nested != null && - item.parent != null && - field.subType.nested.path.startsWith(item.parent.parent.field) - ), + fields: indexPatterns.fields + .filter((indexField) => { + const fieldHasCommonParentPath = + indexField.subType != null && + indexField.subType.nested != null && + item.parent != null && + indexField.subType.nested.path === item.parent.parent.field; + + return fieldHasCommonParentPath; + }) + .map((f) => { + const fieldNameWithoutParentPath = f.name.split('.').slice(-1)[0]; + return { ...f, name: fieldNameWithoutParentPath }; + }), }; } else if (item.nested === 'parent' && item.field != null) { // when user has selected a nested entry, right above it we show the common parent @@ -145,7 +152,10 @@ export const getFormattedBuilderEntry = ( if (parent != null && parentIndex != null) { return { - field: foundField, + field: + foundField != null + ? { ...foundField, name: foundField.name.split('.').slice(-1)[0] } + : foundField, correspondingKeywordField, operator: getExceptionOperatorSelect(item), value: getEntryValue(item), diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.test.tsx index 84613d1c73536e..4c60f3ba5cccea 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.test.tsx @@ -17,6 +17,7 @@ import { useApi, } from '../../../../../public/lists_plugin_deps'; import { getExceptionListSchemaMock } from '../../../../../../lists/common/schemas/response/exception_list_schema.mock'; +import { getFoundExceptionListItemSchemaMock } from '../../../../../../lists/common/schemas/response/found_exception_list_item_schema.mock'; jest.mock('../../../../common/lib/kibana'); jest.mock('../../../../../public/lists_plugin_deps'); @@ -36,6 +37,7 @@ describe('ExceptionsViewer', () => { (useApi as jest.Mock).mockReturnValue({ deleteExceptionItem: jest.fn().mockResolvedValue(true), + getExceptionListsItems: jest.fn().mockResolvedValue(getFoundExceptionListItemSchemaMock()), }); (useExceptionList as jest.Mock).mockReturnValue([