Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Value lists modal window #179339

Merged
merged 60 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
ca305c4
wip
nkhristinin Mar 20, 2024
e1c1d5b
Add list item
nkhristinin Mar 21, 2024
b149787
Some refactor
nkhristinin Mar 21, 2024
fcf4599
add upload
nkhristinin Mar 22, 2024
0bd4fde
move hooks
nkhristinin Mar 22, 2024
03cc414
pass link to exception
nkhristinin Mar 22, 2024
b97ba75
extra fixes
nkhristinin Mar 26, 2024
239e18f
Merge branch 'main' into value_lists
kibanamachine Mar 26, 2024
624d0a6
Merge branch 'main' into value_lists
kibanamachine Apr 2, 2024
585be6d
Check for value list permissions
nkhristinin Apr 3, 2024
db3b98f
Check how value list component passed
nkhristinin Apr 3, 2024
aeb4a42
Fix some types
nkhristinin Apr 3, 2024
f9653df
add translations
nkhristinin Apr 3, 2024
b537ca1
Fix flex
nkhristinin Apr 3, 2024
08a4328
Merge branch 'main' into value_lists
kibanamachine Apr 3, 2024
8f37ed8
Add feaure flag
nkhristinin Apr 3, 2024
8f25972
export hooks
nkhristinin Apr 3, 2024
07cf360
fix some ts errors
nkhristinin Apr 4, 2024
527c02e
fix type
nkhristinin Apr 4, 2024
dca48b9
Merge branch 'main' into value_lists
kibanamachine Apr 4, 2024
cc2bdc0
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Apr 4, 2024
4a13eab
Add some tests
nkhristinin Apr 4, 2024
4b200f8
List item api tests
nkhristinin Apr 4, 2024
ead589a
Merge branch 'value_lists' of github.com:nkhristinin/kibana into valu…
nkhristinin Apr 4, 2024
9190c63
add cypress tests
nkhristinin Apr 4, 2024
d204d79
fix some lint
nkhristinin Apr 5, 2024
0a76ae7
add more cypress tests
nkhristinin Apr 5, 2024
e7593d1
fix selector
nkhristinin Apr 5, 2024
7d65667
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Apr 5, 2024
23b71db
fix unit tests
nkhristinin Apr 5, 2024
8a6893a
enableExperimental true for some time
nkhristinin Apr 5, 2024
ddb9d46
Merge branch 'value_lists' of github.com:nkhristinin/kibana into valu…
nkhristinin Apr 5, 2024
0a917f0
move text to translations
nkhristinin Apr 5, 2024
8ecaf9e
Merge branch 'main' into value_lists
kibanamachine Apr 5, 2024
c5a9217
Fix typo
nkhristinin Apr 5, 2024
aa149b1
Merge branch 'main' into value_lists
kibanamachine Apr 8, 2024
af57a88
Merge branch 'value_lists' of github.com:nkhristinin/kibana into valu…
nkhristinin Apr 8, 2024
a1abdd5
Fix types
nkhristinin Apr 8, 2024
e1fa6f6
value list modal mock
nkhristinin Apr 8, 2024
3f0a040
snake_case file
nkhristinin Apr 8, 2024
a1f925b
Merge branch 'main' into value_lists
kibanamachine Apr 8, 2024
4e20033
fix errors
nkhristinin Apr 8, 2024
abe68f8
Merge branch 'value_lists' of github.com:nkhristinin/kibana into valu…
nkhristinin Apr 8, 2024
9492564
ts types
nkhristinin Apr 8, 2024
3158861
cy tests fixed
nkhristinin Apr 8, 2024
72690ea
one mrore type fix
nkhristinin Apr 9, 2024
5535ed4
fix typo
nkhristinin Apr 9, 2024
661c0f4
Merge remote-tracking branch 'upstream/main' into value_lists
nkhristinin Apr 9, 2024
2481ff3
change refresh to support wait_for
nkhristinin Apr 9, 2024
edc9892
cypress tests
nkhristinin Apr 9, 2024
b5bcf2e
fix
nkhristinin Apr 9, 2024
5168e0c
fix cy tests
nkhristinin Apr 9, 2024
a1aa82f
Merge branch 'main' into value_lists
kibanamachine Apr 10, 2024
4455600
PR feedback
nkhristinin Apr 11, 2024
83df32a
Merge branch 'main' into value_lists
kibanamachine Apr 11, 2024
5da233d
Merge branch 'main' into value_lists
kibanamachine Apr 12, 2024
47eec14
change feature flag name and disable it by default
nkhristinin Apr 12, 2024
c9deda4
lint fix
nkhristinin Apr 12, 2024
9b030df
Merge branch 'main' into value_lists
kibanamachine Apr 15, 2024
a74fe9c
Merge branch 'main' into value_lists
kibanamachine Apr 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ import {
// const mockKibanaHttpService = coreMock.createStart().http;
// import { coreMock } from '../../../../../../../src/core/public/mocks';
const mockKibanaHttpService = jest.fn();

const mockShowValueListModal = jest.fn();
const MockedShowValueListModal = (props: unknown) => {
mockShowValueListModal(props);
return <></>;
};
const mockStart = jest.fn();
const mockKeywordList: ListSchema = {
...getListResponseMock(),
Expand Down Expand Up @@ -63,6 +67,7 @@ describe('AutocompleteFieldListsComponent', () => {
placeholder="Placeholder text"
selectedField={getField('ip')}
selectedValue="some-list-id"
showValueListModal={MockedShowValueListModal}
/>
);

Expand All @@ -84,6 +89,7 @@ describe('AutocompleteFieldListsComponent', () => {
placeholder="Placeholder text"
selectedField={getField('@tags')}
selectedValue=""
showValueListModal={MockedShowValueListModal}
/>
);

Expand Down Expand Up @@ -111,6 +117,7 @@ describe('AutocompleteFieldListsComponent', () => {
placeholder="Placeholder text"
selectedField={getField('ip')}
selectedValue=""
showValueListModal={MockedShowValueListModal}
/>
);
expect(
Expand All @@ -131,6 +138,7 @@ describe('AutocompleteFieldListsComponent', () => {
placeholder="Placeholder text"
selectedField={getField('@tags')}
selectedValue=""
showValueListModal={MockedShowValueListModal}
/>
);

Expand All @@ -154,6 +162,7 @@ describe('AutocompleteFieldListsComponent', () => {
placeholder="Placeholder text"
selectedField={getField('ip')}
selectedValue=""
showValueListModal={MockedShowValueListModal}
/>
);

Expand All @@ -177,6 +186,7 @@ describe('AutocompleteFieldListsComponent', () => {
placeholder="Placeholder text"
selectedField={getField('ip')}
selectedValue="some-list-id"
showValueListModal={MockedShowValueListModal}
/>
);

Expand All @@ -200,6 +210,7 @@ describe('AutocompleteFieldListsComponent', () => {
placeholder="Placeholder text"
selectedField={getField('ip')}
selectedValue=""
showValueListModal={MockedShowValueListModal}
/>
);

Expand Down Expand Up @@ -230,4 +241,29 @@ describe('AutocompleteFieldListsComponent', () => {
});
});
});

test('it render the value list modal', async () => {
mockShowValueListModal.mockReset();
mount(
<AutocompleteFieldListsComponent
httpService={mockKibanaHttpService}
isClearable={false}
isDisabled={false}
isLoading={false}
onChange={jest.fn()}
placeholder="Placeholder text"
selectedField={getField('ip')}
selectedValue="some-list-id"
showValueListModal={MockedShowValueListModal}
/>
);

expect(mockShowValueListModal).toHaveBeenCalledWith(
expect.objectContaining({
children: 'Show value list',
listId: 'some-list-id',
shouldShowContentIfModalNotAvailable: false,
})
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Side Public License, v 1.
*/

import React, { useCallback, useEffect, useMemo, useState } from 'react';
import React, { ElementType, useCallback, useEffect, useMemo, useState } from 'react';
import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow, EuiLink, EuiText } from '@elastic/eui';
import type { ListSchema } from '@kbn/securitysolution-io-ts-list-types';
import { useFindListsBySize } from '@kbn/securitysolution-list-hooks';
Expand Down Expand Up @@ -36,6 +36,7 @@ interface AutocompleteFieldListsProps {
selectedValue: string | undefined;
allowLargeValueLists?: boolean;
'aria-label'?: string;
showValueListModal: ElementType;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason that this is a prop of AutocompleteFieldListsComponent? The interface doesn't look very flexible (or typed correctly, since we pass very specific props to showValueListModal), and I don't see any data being reported back up... I would just delete lines 39, 59, and 123 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would also get rid of many of the changes on this branch 😉

}

export interface AutocompleteListsData {
Expand All @@ -55,6 +56,7 @@ export const AutocompleteFieldListsComponent: React.FC<AutocompleteFieldListsPro
selectedValue,
allowLargeValueLists = false,
'aria-label': ariaLabel,
showValueListModal,
}): JSX.Element => {
const [error, setError] = useState<string | undefined>(undefined);
const [listData, setListData] = useState<AutocompleteListsData>({
Expand Down Expand Up @@ -118,29 +120,36 @@ export const AutocompleteFieldListsComponent: React.FC<AutocompleteFieldListsPro
}, [selectedField, start, httpService]);

const isLoadingState = useMemo((): boolean => isLoading || loading, [isLoading, loading]);
const ShowValueListModal = showValueListModal;

const helpText = useMemo(() => {
return (
!allowLargeValueLists && (
<EuiText size="xs">
{i18n.LISTS_TOOLTIP_INFO}{' '}
<EuiLink
external
target="_blank"
href={
getDocLinks({
kibanaBranch: 'main',
buildFlavor: 'traditional',
}).securitySolution.exceptions.value_lists
}
>
{i18n.SEE_DOCUMENTATION}
</EuiLink>
</EuiText>
)
<>
{selectedValue && (
<ShowValueListModal shouldShowContentIfModalNotAvailable={false} listId={selectedValue}>
{i18n.SHOW_VALUE_LIST_MODAL}
</ShowValueListModal>
)}
{!allowLargeValueLists && (
<EuiText size="xs">
{i18n.LISTS_TOOLTIP_INFO}{' '}
<EuiLink
external
target="_blank"
href={
getDocLinks({
kibanaBranch: 'main',
buildFlavor: 'traditional',
}).securitySolution.exceptions.value_lists
}
>
{i18n.SEE_DOCUMENTATION}
</EuiLink>
</EuiText>
)}
</>
);
}, [allowLargeValueLists]);

}, [allowLargeValueLists, selectedValue, ShowValueListModal]);
return (
<EuiFormRow
label={rowLabel}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ export const CONFLICT_MULTIPLE_INDEX_DESCRIPTION = (name: string, count: number)
values: { count, name },
});

export const SHOW_VALUE_LIST_MODAL = i18n.translate('autocomplete.showValueListModal', {
defaultMessage: 'Show value list',
});

// eslint-disable-next-line import/no-default-export
export default {
LOADING,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { render } from '@testing-library/react';
import React from 'react';

import { ExceptionItemCardConditions } from '.';
import { MockedShowValueListModal } from '../../mocks/value_list_modal.mock';

interface TestEntry {
field: string;
Expand Down Expand Up @@ -94,6 +95,7 @@ describe('ExceptionItemCardConditions', () => {
},
]}
dataTestSubj="exceptionItemConditions"
showValueListModal={MockedShowValueListModal}
/>
);
expect(wrapper.getByTestId('exceptionItemConditionsOs')).toHaveTextContent('OSIS Linux');
Expand Down Expand Up @@ -243,6 +245,7 @@ describe('ExceptionItemCardConditions', () => {
type: 'nested',
},
]}
showValueListModal={MockedShowValueListModal}
dataTestSubj="exceptionItemConditions"
/>
);
Expand Down Expand Up @@ -331,6 +334,7 @@ describe('ExceptionItemCardConditions', () => {
type: 'list',
},
]}
showValueListModal={MockedShowValueListModal}
dataTestSubj="exceptionItemConditions"
/>
);
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ import {
includedListTypeEntry,
includedMatchTypeEntry,
} from '../../../mocks/entry.mock';
import {
MockedShowValueListModal,
mockShowValueListModal,
} from '../../../mocks/value_list_modal.mock';

describe('entry_content.helper', () => {
describe('getEntryOperator', () => {
Expand Down Expand Up @@ -62,36 +66,79 @@ describe('entry_content.helper', () => {
describe('getValueExpression', () => {
it('should render multiple values in badges when operator type is match_any and values is Array', () => {
const wrapper = render(
getValueExpression(ListOperatorTypeEnum.MATCH_ANY, 'included', ['value 1', 'value 2'])
getValueExpression(
ListOperatorTypeEnum.MATCH_ANY,
'included',
['value 1', 'value 2'],
MockedShowValueListModal
)
);
expect(wrapper.getByTestId('matchAnyBadge0')).toHaveTextContent('value 1');
expect(wrapper.getByTestId('matchAnyBadge1')).toHaveTextContent('value 2');
});
it('should return one value when operator type is match_any and values is not Array', () => {
const wrapper = render(
getValueExpression(ListOperatorTypeEnum.MATCH_ANY, 'included', 'value 1')
getValueExpression(
ListOperatorTypeEnum.MATCH_ANY,
'included',
'value 1',
MockedShowValueListModal
)
);
expect(wrapper.getByTestId('entryValueExpression')).toHaveTextContent('value 1');
});
it('should return one value when operator type is a single value', () => {
const wrapper = render(
getValueExpression(ListOperatorTypeEnum.EXISTS, 'included', 'value 1')
getValueExpression(
ListOperatorTypeEnum.EXISTS,
'included',
'value 1',
MockedShowValueListModal
)
);
expect(wrapper.getByTestId('entryValueExpression')).toHaveTextContent('value 1');
});
it('should return value with warning icon when the value contains a leading or trailing space', () => {
const wrapper = render(
getValueExpression(ListOperatorTypeEnum.EXISTS, 'included', ' value 1')
getValueExpression(
ListOperatorTypeEnum.EXISTS,
'included',
' value 1',
MockedShowValueListModal
)
);
expect(wrapper.getByTestId('entryValueExpression')).toHaveTextContent(' value 1');
expect(wrapper.getByTestId('valueWithSpaceWarningTooltip')).toBeInTheDocument();
});
it('should return value without warning icon when the value does not contain a leading or trailing space', () => {
const wrapper = render(
getValueExpression(ListOperatorTypeEnum.EXISTS, 'included', 'value 1')
getValueExpression(
ListOperatorTypeEnum.EXISTS,
'included',
'value 1',
MockedShowValueListModal
)
);
expect(wrapper.getByTestId('entryValueExpression')).toHaveTextContent(' value 1');
expect(wrapper.queryByTestId('valueWithSpaceWarningTooltip')).not.toBeInTheDocument();
});
it('should render value list modal when operator type is list', () => {
mockShowValueListModal.mockReset();
render(
getValueExpression(
ListOperatorTypeEnum.LIST,
'included',
'value 1',
MockedShowValueListModal
)
);
expect(mockShowValueListModal).toHaveBeenCalledWith(
expect.objectContaining({
children: 'value 1',
listId: 'value 1',
shouldShowContentIfModalNotAvailable: true,
})
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Side Public License, v 1.
*/

import React from 'react';
import React, { ElementType } from 'react';
import { css } from '@emotion/css';
import { EuiExpression, EuiBadge } from '@elastic/eui';
import type { ListOperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types';
Expand All @@ -21,13 +21,21 @@ const entryValueWrapStyle = css`
const EntryValueWrap = ({ children }: { children: React.ReactNode }) => (
<span className={entryValueWrapStyle}>{children}</span>
);
const getEntryValue = (type: string, value?: string | string[]) => {

const getEntryValue = (type: string, value: string | string[], showValueListModal: ElementType) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing here: I don't think this needs to be an argument to getEntryValue, you can just add the new logic branch with an explicit ShowValueListModal reference 👍 .

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I totally agree and I don't like this props drilling myself.

The problem that those Exceptiond and Autocomplete are - packages - and only way I found it's to pass this modal window as props.

We already do that, and have those comments

securityLinkAnchorComponent: ElementType; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common

Alternative solutions - can be to move this modal window to another package, and make as dependency for Exception and Autocomplete - But I don't see why a very specific business logic component should go to another package.

Please let me know if you have other ideas

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, after going through the code again, and getting better acquainted with the different packages we have, it does feel like these value list components should be their own package (so as to avoid the circular dependency between security_solution and the autocomplete package referenced above). Something like kbn-securitysolution-list-components that contains this new modal and also the existing value_lists_management_flyout.

It would be great to do that here and avoid the churn in this PR, but if you'd prefer to do that in a subsequent PR I could probably be convinced of that. Let me know what you think. cc @yctercero

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But: if you do end up sticking with the current props-based solution, we should at least make it an FC with the correctly-typed props.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is what I'm thinking:

  • ++ what @rylnd suggested of moving the value lists components out to avoid passing through the modal as a prop
  • I do wish we could have the modal not be nested so deeply within another modal (in the exceptions modal). We've observed bugs of unintended re-renders of the exception conditions and that could man we see the value list modal begin to unexpectedly re-render as well. Could we run this through the flakey test runner to see if we catch any of that behavior?
  • I would love to chat tomorrow during tech time to understand what options we have here. I think what Ryland suggested could be a step further. The exceptions/lists workflows are under review, so this is also a great time to discuss design patterns we'd like to move towards. We can certainly take an iterative approach here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After discussion today, we decided that we will not move Value list modal to another packages, as it already depends on a lot of things from security-solution plugin.

Otherwhise there was idea to move Exceptions component package back into security solution, which I will create issue for.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do wish we could have the modal not be nested so deeply within another modal

Agree, but don't know what other options we can have here. Can be expanded flyout, but exceptions flyout already to wide, it will be not enough place for that.

Copy link
Contributor Author

@nkhristinin nkhristinin Apr 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we run this through the flakey test runner to see if we catch any of that behavior?

would run some flaky test runner

https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/5657

const ShowValueListModal = showValueListModal;
if (type === 'match_any' && Array.isArray(value)) {
return value.map((currentValue, index) => (
<EuiBadge key={index} data-test-subj={`matchAnyBadge${index}`} color="hollow">
<EntryValueWrap>{currentValue}</EntryValueWrap>
</EuiBadge>
));
} else if (type === 'list' && value) {
return (
<ShowValueListModal shouldShowContentIfModalNotAvailable listId={value.toString()}>
{value}
</ShowValueListModal>
);
}
return <EntryValueWrap>{value}</EntryValueWrap> ?? '';
};
Expand All @@ -48,12 +56,13 @@ export const getValue = (entry: Entry) => {
export const getValueExpression = (
type: ListOperatorTypeEnum,
operator: string,
value: string | string[]
value: string | string[],
showValueListModal: ElementType
) => (
<>
<EuiExpression
description={getEntryOperator(type, operator)}
value={getEntryValue(type, value)}
value={getEntryValue(type, value, showValueListModal)}
data-test-subj="entryValueExpression"
/>
<ValueWithSpaceWarning value={value} />
Expand Down
Loading