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

[Security Solution][Exceptions] - Fix bug displaying empty view when no exception list search results found #151530

Merged
merged 8 commits into from
Feb 23, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('EmptyViewerState', () => {
<EmptyViewerState
isReadOnly={false}
viewerStatus={ViewerStatus.ERROR}
onCreateExceptionListItem={jest.fn()}
onEmptyButtonStateClick={jest.fn()}
/>
);

Expand All @@ -34,7 +34,7 @@ describe('EmptyViewerState', () => {
<EmptyViewerState
isReadOnly={false}
viewerStatus={ViewerStatus.ERROR}
onCreateExceptionListItem={jest.fn()}
onEmptyButtonStateClick={jest.fn()}
title="Error title"
body="Error body"
/>
Expand All @@ -49,7 +49,7 @@ describe('EmptyViewerState', () => {
<EmptyViewerState
isReadOnly={false}
viewerStatus={ViewerStatus.LOADING}
onCreateExceptionListItem={jest.fn()}
onEmptyButtonStateClick={jest.fn()}
/>
);

Expand All @@ -60,7 +60,7 @@ describe('EmptyViewerState', () => {
<EmptyViewerState
isReadOnly={false}
viewerStatus={ViewerStatus.EMPTY_SEARCH}
onCreateExceptionListItem={jest.fn()}
onEmptyButtonStateClick={jest.fn()}
/>
);

Expand All @@ -77,7 +77,7 @@ describe('EmptyViewerState', () => {
<EmptyViewerState
isReadOnly={false}
viewerStatus={ViewerStatus.EMPTY_SEARCH}
onCreateExceptionListItem={jest.fn()}
onEmptyButtonStateClick={jest.fn()}
title="Empty search title"
body="Empty search body"
/>
Expand All @@ -92,7 +92,7 @@ describe('EmptyViewerState', () => {
<EmptyViewerState
isReadOnly={false}
viewerStatus={ViewerStatus.EMPTY}
onCreateExceptionListItem={jest.fn()}
onEmptyButtonStateClick={jest.fn()}
body="There are no endpoint exceptions."
buttonText="Add endpoint exception"
/>
Expand All @@ -108,7 +108,7 @@ describe('EmptyViewerState', () => {
<EmptyViewerState
isReadOnly={false}
viewerStatus={ViewerStatus.EMPTY}
onCreateExceptionListItem={jest.fn()}
onEmptyButtonStateClick={jest.fn()}
/>
);

Expand All @@ -125,7 +125,7 @@ describe('EmptyViewerState', () => {
<EmptyViewerState
isReadOnly={false}
viewerStatus={ViewerStatus.EMPTY}
onCreateExceptionListItem={jest.fn()}
onEmptyButtonStateClick={jest.fn()}
listType={ListTypeText.ENDPOINT}
/>
);
Expand All @@ -143,7 +143,7 @@ describe('EmptyViewerState', () => {
<EmptyViewerState
isReadOnly={true}
viewerStatus={ViewerStatus.EMPTY}
onCreateExceptionListItem={jest.fn()}
onEmptyButtonStateClick={jest.fn()}
listType={ListTypeText.ENDPOINT}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ interface EmptyViewerStateProps {
listType?: ListTypeText;
isReadOnly: boolean;
viewerStatus: ViewerStatus;
onCreateExceptionListItem?: () => void | null;
onEmptyButtonStateClick?: () => void | null;
}

const panelCss = css`
Expand All @@ -45,7 +45,7 @@ const EmptyViewerStateComponent: FC<EmptyViewerStateProps> = ({
listType,
isReadOnly,
viewerStatus,
onCreateExceptionListItem,
onEmptyButtonStateClick,
}) => {
const { euiTheme } = useEuiTheme();

Expand Down Expand Up @@ -75,7 +75,7 @@ const EmptyViewerStateComponent: FC<EmptyViewerStateProps> = ({
actions: [
<EuiButton
data-test-subj="emptyStateButton"
onClick={onCreateExceptionListItem}
onClick={onEmptyButtonStateClick}
iconType="plusInCircle"
color="primary"
isDisabled={isReadOnly}
Expand Down Expand Up @@ -110,7 +110,7 @@ const EmptyViewerStateComponent: FC<EmptyViewerStateProps> = ({
euiTheme.colors.darkestShade,
title,
body,
onCreateExceptionListItem,
onEmptyButtonStateClick,
isReadOnly,
buttonText,
listType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ const ExceptionItemsComponent: FC<ExceptionItemsProps> = ({
viewerStatus={viewerStatus}
buttonText={emptyViewerButtonText}
body={emptyViewerBody}
onCreateExceptionListItem={onCreateExceptionListItem}
onEmptyButtonStateClick={onCreateExceptionListItem}
/>
);
return (
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/security_solution/jest.config.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module.exports = {
preset: '@kbn/test',
rootDir: '../../../',
projects: [
'<rootDir>/x-pack/plugins/security_solution/common/*/jest.config.js',
yctercero marked this conversation as resolved.
Show resolved Hide resolved
// '<rootDir>/x-pack/plugins/security_solution/common/*/jest.config.js',
'<rootDir>/x-pack/plugins/security_solution/server/*/jest.config.js',
'<rootDir>/x-pack/plugins/security_solution/public/*/jest.config.js',
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const ListWithSearchComponent: FC<ListWithSearchComponentProps> = ({
<EmptyViewerState
isReadOnly={isReadOnly}
viewerStatus={viewerStatus as ViewerStatus}
onCreateExceptionListItem={onAddExceptionClick}
onEmptyButtonStateClick={onAddExceptionClick}
title={i18n.EXCEPTION_LIST_EMPTY_VIEWER_TITLE}
body={i18n.EXCEPTION_LIST_EMPTY_VIEWER_BODY(listName)}
buttonText={i18n.EXCEPTION_LIST_EMPTY_VIEWER_BUTTON}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React, { useEffect, useCallback, useState } from 'react';
import React, { useMemo, useEffect, useCallback, useState } from 'react';
import type { EuiSearchBarProps } from '@elastic/eui';

import {
Expand All @@ -18,8 +18,6 @@ import {
EuiButton,
EuiFlexGroup,
EuiFlexItem,
EuiLoadingContent,
EuiProgress,
EuiSpacer,
EuiPageHeader,
EuiHorizontalRule,
Expand All @@ -28,9 +26,9 @@ import {
import type { NamespaceType, ExceptionListFilter } from '@kbn/securitysolution-io-ts-list-types';
import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types';
import { useApi, useExceptionLists } from '@kbn/securitysolution-list-hooks';
import { ViewerStatus, EmptyViewerState } from '@kbn/securitysolution-exception-list-components';

import { AutoDownload } from '../../../common/components/auto_download/auto_download';
import { Loader } from '../../../common/components/loader';
import { useKibana } from '../../../common/lib/kibana';
import { useAppToasts } from '../../../common/hooks/use_app_toasts';

Expand Down Expand Up @@ -102,6 +100,8 @@ export const SharedLists = React.memo(() => {
);
const [filters, setFilters] = useState<ExceptionListFilter | undefined>();

const [viewerStatus, setViewStatus] = useState<ViewerStatus | null>(ViewerStatus.LOADING);

const [
loadingExceptions,
exceptions,
Expand Down Expand Up @@ -151,7 +151,7 @@ export const SharedLists = React.memo(() => {
);

const handleDelete = useCallback(
({ id, listId, namespaceType }: { id: string; listId: string; namespaceType: NamespaceType }) =>
({ id, namespaceType }: { id: string; namespaceType: NamespaceType }) =>
async () => {
try {
if (exceptionsListsRef[id] != null) {
Expand Down Expand Up @@ -234,6 +234,7 @@ export const SharedLists = React.memo(() => {
query,
queryText,
}: Parameters<NonNullable<EuiSearchBarProps['onChange']>>[0]): Promise<void> => {
setViewStatus(ViewerStatus.SEARCHING);
const filterOptions = {
name: null,
list_id: null,
Expand Down Expand Up @@ -393,6 +394,44 @@ export const SharedLists = React.memo(() => {
setDisplayAddExceptionItemFlyout(false);
setIsCreatePopoverOpen(false);
};
const onCreateExceptionListOpenClick = () => setDisplayCreateSharedListFlyout(true);

const isReadOnly = useMemo(() => {
return (canUserREAD && !canUserCRUD) ?? true;
}, [canUserREAD, canUserCRUD]);

useEffect(() => {
yctercero marked this conversation as resolved.
Show resolved Hide resolved
// If current status is searching and results come up empty,
// show empty search screen
if (
viewerStatus === ViewerStatus.SEARCHING &&
!loadingExceptions &&
!exceptionListsWithRuleRefs.length
) {
setViewStatus(ViewerStatus.EMPTY_SEARCH);
// if loading exceptions or their refs, set to loading
} else if (loadingTableInfo || initLoading) {
setViewStatus(ViewerStatus.LOADING);
// to differentiate between no exception lists and
// no exception list search results, check for existing
// loading state
} else if (
viewerStatus === ViewerStatus.LOADING &&
!loadingTableInfo &&
!initLoading &&
!exceptionListsWithRuleRefs.length
) {
setViewStatus(ViewerStatus.EMPTY);
} else if (viewerStatus === ViewerStatus.LOADING && !loadingTableInfo && !initLoading) {
setViewStatus(null);
}
}, [
loadingTableInfo,
initLoading,
viewerStatus,
loadingExceptions,
exceptionListsWithRuleRefs.length,
]);

return (
<>
Expand Down Expand Up @@ -447,7 +486,7 @@ export const SharedLists = React.memo(() => {
key={'createList'}
onClick={() => {
onCloseCreatePopover();
setDisplayCreateSharedListFlyout(true);
onCreateExceptionListOpenClick();
}}
>
{i18n.CREATE_SHARED_LIST_BUTTON}
Expand Down Expand Up @@ -483,7 +522,7 @@ export const SharedLists = React.memo(() => {
isEndpointItem={false}
isBulkAction={false}
showAlertCloseOptions
onCancel={(didRuleChange: boolean) => setDisplayAddExceptionItemFlyout(false)}
onCancel={() => setDisplayAddExceptionItemFlyout(false)}
onConfirm={(didRuleChange: boolean) => {
setDisplayAddExceptionItemFlyout(false);
if (didRuleChange) handleRefresh();
Expand All @@ -504,23 +543,17 @@ export const SharedLists = React.memo(() => {

<EuiHorizontalRule />
<div data-test-subj="allExceptionListsPanel">
{loadingTableInfo && (
<EuiProgress
data-test-subj="loadingRulesInfoProgress"
size="xs"
position="absolute"
color="accent"
/>
)}
{!initLoading && <ListsSearchBar onSearch={handleSearch} />}
<EuiSpacer size="m" />

{loadingTableInfo && !initLoading && !showReferenceErrorModal && (
<Loader data-test-subj="loadingPanelAllRulesTable" overlay size="xl" />
)}

{initLoading || loadingTableInfo ? (
<EuiLoadingContent data-test-subj="initialLoadingPanelAllRulesTable" lines={10} />
{viewerStatus != null ? (
<EmptyViewerState
isReadOnly={isReadOnly}
title={i18n.NO_EXCEPTION_LISTS}
viewerStatus={viewerStatus}
buttonText={i18n.CREATE_SHARED_LIST_BUTTON}
body={i18n.NO_LISTS_BODY}
onEmptyButtonStateClick={onCreateExceptionListOpenClick}
/>
) : (
<>
<ExceptionsTableUtilityBar
Expand All @@ -530,13 +563,13 @@ export const SharedLists = React.memo(() => {
sort={sort}
sortFields={SORT_FIELDS}
/>
{exceptionListsWithRuleRefs.length > 0 && canUserCRUD !== null && canUserREAD !== null && (
{exceptionListsWithRuleRefs.length > 0 && (
<div data-test-subj="exceptionsTable">
{exceptionListsWithRuleRefs.map((excList) => (
<ExceptionsListCard
key={excList.list_id}
data-test-subj="exceptionsListCard"
readOnly={canUserREAD && !canUserCRUD}
readOnly={isReadOnly}
exceptionsList={excList}
handleDelete={handleDelete}
handleExport={handleExport}
Expand Down