[Security Solution][Endpoint][Response Actions]fix tray collapse on history page (#165260)

## Summary

Fixes a bug on the response actions history page where the details tray
does not collapse on a single click.

**before** 
elastic/kibana/issues/165058

**after**

![response-actions-hisotry-collapse](71ee1663-c087-41ef-b7c0-3c9be65f7ef6)

### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
This commit is contained in:
Ash 2023-09-04 10:57:32 +02:00 committed by GitHub
parent a2e841e6e4
commit c9c7f076ba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 41 additions and 25 deletions

View file

@ -7,19 +7,19 @@
import React, { memo, useCallback, useEffect, useMemo, useState } from 'react'; import React, { memo, useCallback, useEffect, useMemo, useState } from 'react';
import { import {
EuiI18nNumber, type CriteriaWithPagination,
EuiAvatar, EuiAvatar,
EuiBasicTable, EuiBasicTable,
EuiButtonIcon, EuiButtonIcon,
EuiFacetButton, EuiFacetButton,
EuiHorizontalRule, EuiHorizontalRule,
RIGHT_ALIGNMENT, EuiI18nNumber,
EuiScreenReaderOnly, EuiScreenReaderOnly,
EuiSkeletonText,
EuiText, EuiText,
EuiToolTip, EuiToolTip,
type HorizontalAlignment, type HorizontalAlignment,
type CriteriaWithPagination, RIGHT_ALIGNMENT,
EuiSkeletonText,
} from '@elastic/eui'; } from '@elastic/eui';
import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { euiStyled } from '@kbn/kibana-react-plugin/common';
import { FormattedMessage } from '@kbn/i18n-react'; import { FormattedMessage } from '@kbn/i18n-react';
@ -30,14 +30,13 @@ import { SecuritySolutionLinkAnchor } from '../../../../common/components/links'
import type { ActionListApiResponse } from '../../../../../common/endpoint/types'; import type { ActionListApiResponse } from '../../../../../common/endpoint/types';
import type { EndpointActionListRequestQuery } from '../../../../../common/api/endpoint'; import type { EndpointActionListRequestQuery } from '../../../../../common/api/endpoint';
import { FormattedDate } from '../../../../common/components/formatted_date'; import { FormattedDate } from '../../../../common/components/formatted_date';
import { TABLE_COLUMN_NAMES, UX_MESSAGES, ARIA_LABELS } from '../translations'; import { ARIA_LABELS, TABLE_COLUMN_NAMES, UX_MESSAGES } from '../translations';
import { getActionStatus, getUiCommand } from './hooks'; import { getActionStatus, getUiCommand } from './hooks';
import { getEmptyValue } from '../../../../common/components/empty_value'; import { getEmptyValue } from '../../../../common/components/empty_value';
import { StatusBadge } from './status_badge'; import { StatusBadge } from './status_badge';
import { ActionsLogExpandedTray } from './action_log_expanded_tray'; import { ActionsLogExpandedTray } from './action_log_expanded_tray';
import { useTestIdGenerator } from '../../../hooks/use_test_id_generator'; import { useTestIdGenerator } from '../../../hooks/use_test_id_generator';
import { MANAGEMENT_PAGE_SIZE_OPTIONS } from '../../../common/constants'; import { MANAGEMENT_PAGE_SIZE_OPTIONS } from '../../../common/constants';
import { useActionHistoryUrlParams } from './use_action_history_url_params';
import { useUrlPagination } from '../../../hooks/use_url_pagination'; import { useUrlPagination } from '../../../hooks/use_url_pagination';
const emptyValue = getEmptyValue(); const emptyValue = getEmptyValue();
@ -292,22 +291,19 @@ export const ActionsLogTable = memo<ActionsLogTableProps>(
}) => { }) => {
const getTestId = useTestIdGenerator(dataTestSubj); const getTestId = useTestIdGenerator(dataTestSubj);
const { pagination: paginationFromUrlParams } = useUrlPagination(); const { pagination: paginationFromUrlParams } = useUrlPagination();
const { withOutputs: withOutputsFromUrl } = useActionHistoryUrlParams();
const [expandedRowMap, setExpandedRowMap] = useState<ExpandedRowMapType>({}); const [expandedRowMap, setExpandedRowMap] = useState<ExpandedRowMapType>({});
const actionIdsWithOpenTrays = useMemo((): string[] => { const actionIdsWithOpenTrays = useMemo(
// get the list of action ids from URL params on the history page (): string[] =>
if (!isFlyout) { // get the list of action ids from the query params for flyout view
return withOutputsFromUrl ?? []; queryParams.withOutputs
} ? typeof queryParams.withOutputs === 'string'
// get the list of action ids form the query params for flyout view ? [queryParams.withOutputs]
return queryParams.withOutputs : queryParams.withOutputs
? typeof queryParams.withOutputs === 'string' : [],
? [queryParams.withOutputs] [queryParams.withOutputs]
: queryParams.withOutputs );
: [];
}, [isFlyout, queryParams.withOutputs, withOutputsFromUrl]);
const redoOpenTrays = useCallback(() => { const redoOpenTrays = useCallback(() => {
if (actionIdsWithOpenTrays.length && items.length) { if (actionIdsWithOpenTrays.length && items.length) {

View file

@ -7,12 +7,13 @@
import React from 'react'; import React from 'react';
import * as reactTestingLibrary from '@testing-library/react'; import * as reactTestingLibrary from '@testing-library/react';
import { waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import { waitForEuiPopoverOpen } from '@elastic/eui/lib/test/rtl'; import { waitForEuiPopoverOpen } from '@elastic/eui/lib/test/rtl';
import type { IHttpFetchError } from '@kbn/core-http-browser'; import type { IHttpFetchError } from '@kbn/core-http-browser';
import { import {
createAppRootMockRenderer,
type AppContextTestRender, type AppContextTestRender,
createAppRootMockRenderer,
} from '../../../../common/mock/endpoint'; } from '../../../../common/mock/endpoint';
import { ResponseActionsLog } from '../response_actions_log'; import { ResponseActionsLog } from '../response_actions_log';
import type { import type {
@ -29,7 +30,6 @@ import { v4 as uuidv4 } from 'uuid';
import { RESPONSE_ACTION_API_COMMANDS_NAMES } from '../../../../../common/endpoint/service/response_actions/constants'; import { RESPONSE_ACTION_API_COMMANDS_NAMES } from '../../../../../common/endpoint/service/response_actions/constants';
import { useUserPrivileges as _useUserPrivileges } from '../../../../common/components/user_privileges'; import { useUserPrivileges as _useUserPrivileges } from '../../../../common/components/user_privileges';
import { responseActionsHttpMocks } from '../../../mocks/response_actions_http_mocks'; import { responseActionsHttpMocks } from '../../../mocks/response_actions_http_mocks';
import { waitFor } from '@testing-library/react';
import { getEndpointAuthzInitialStateMock } from '../../../../../common/endpoint/service/authz/mocks'; import { getEndpointAuthzInitialStateMock } from '../../../../../common/endpoint/service/authz/mocks';
import { useGetEndpointActionList as _useGetEndpointActionList } from '../../../hooks/response_actions/use_get_endpoint_action_list'; import { useGetEndpointActionList as _useGetEndpointActionList } from '../../../hooks/response_actions/use_get_endpoint_action_list';
import { OUTPUT_MESSAGES } from '../translations'; import { OUTPUT_MESSAGES } from '../translations';
@ -470,7 +470,7 @@ describe('Response actions history', () => {
); );
}); });
it('should expand each row to show details', async () => { it('should expand/collapse each row to show/hide details', async () => {
render(); render();
const { getAllByTestId, queryAllByTestId } = renderResult; const { getAllByTestId, queryAllByTestId } = renderResult;
@ -961,8 +961,7 @@ describe('Response actions history', () => {
const expandButtons = getAllByTestId(`${testPrefix}-expand-button`); const expandButtons = getAllByTestId(`${testPrefix}-expand-button`);
expandButtons.map((button) => userEvent.click(button)); expandButtons.map((button) => userEvent.click(button));
const outputs = getAllByTestId(`${testPrefix}-details-tray-output`); return getAllByTestId(`${testPrefix}-details-tray-output`);
return outputs;
}; };
it.each(RESPONSE_ACTION_API_COMMANDS_NAMES)( it.each(RESPONSE_ACTION_API_COMMANDS_NAMES)(

View file

@ -50,4 +50,25 @@ describe('Response actions history page', () => {
cy.getByTestSubj('pagination-button-0').click(); cy.getByTestSubj('pagination-button-0').click();
cy.getByTestSubj('response-actions-list-details-tray').should('exist'); cy.getByTestSubj('response-actions-list-details-tray').should('exist');
}); });
it('collapses expanded tray with a single click', () => {
loadPage(`/app/security/administration/response_actions_history`);
// 2nd row on 1st page
const row = cy.getByTestSubj('response-actions-list-expand-button').eq(1);
// expand the row
row.click();
cy.getByTestSubj('response-actions-list-details-tray').should('exist');
cy.url().should('include', 'withOutputs');
// collapse the row
cy.intercept('GET', '/api/endpoint/action*').as('getResponses');
row.click();
// wait for the API response to come back
// and then see if the tray is actually closed
cy.wait('@getResponses', { timeout: 500 }).then(() => {
cy.getByTestSubj('response-actions-list-details-tray').should('not.exist');
cy.url().should('not.include', 'withOutputs');
});
});
}); });

View file

@ -236,7 +236,7 @@ describe('Response actions history page', () => {
return { return {
id: `agent-id-${i}`, id: `agent-id-${i}`,
name: `Host-name-${i}`, name: `Host-name-${i}`,
selected: [0, 1, 3, 5].includes(i) ? true : false, selected: [0, 1, 3, 5].includes(i),
}; };
}), }),
page: 0, page: 0,