Skip to content

Commit

Permalink
[RAC] [Cases] All cases table column design updates (elastic#103544)
Browse files Browse the repository at this point in the history
  • Loading branch information
stephmilovic authored Jun 29, 2021
1 parent a5660fe commit c24318a
Show file tree
Hide file tree
Showing 13 changed files with 194 additions and 234 deletions.
85 changes: 11 additions & 74 deletions x-pack/plugins/cases/public/components/all_cases/actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,87 +5,24 @@
* 2.0.
*/

import { Dispatch } from 'react';
import { DefaultItemIconButtonAction } from '@elastic/eui/src/components/basic_table/action_types';

import { CaseStatuses } from '../../../common';
import { Case, SubCase } from '../../containers/types';
import { UpdateCase } from '../../containers/use_get_cases';
import { statuses } from '../status';
import { Case } from '../../containers/types';
import * as i18n from './translations';
import { isIndividual } from './helpers';

interface GetActions {
dispatchUpdate: Dispatch<Omit<UpdateCase, 'refetchCasesStatus'>>;
deleteCaseOnClick: (deleteCase: Case) => void;
}

export const getActions = ({
dispatchUpdate,
deleteCaseOnClick,
}: GetActions): Array<DefaultItemIconButtonAction<Case>> => {
const openCaseAction = {
available: (item: Case | SubCase) => item.status !== CaseStatuses.open,
enabled: (item: Case | SubCase) => isIndividual(item),
description: statuses[CaseStatuses.open].actions.single.title,
icon: statuses[CaseStatuses.open].icon,
name: statuses[CaseStatuses.open].actions.single.title,
onClick: (theCase: Case) =>
dispatchUpdate({
updateKey: 'status',
updateValue: CaseStatuses.open,
caseId: theCase.id,
version: theCase.version,
}),
type: 'icon' as const,
'data-test-subj': 'action-open',
};

const makeInProgressAction = {
available: (item: Case) => item.status !== CaseStatuses['in-progress'],
enabled: (item: Case | SubCase) => isIndividual(item),
description: statuses[CaseStatuses['in-progress']].actions.single.title,
icon: statuses[CaseStatuses['in-progress']].icon,
name: statuses[CaseStatuses['in-progress']].actions.single.title,
onClick: (theCase: Case) =>
dispatchUpdate({
updateKey: 'status',
updateValue: CaseStatuses['in-progress'],
caseId: theCase.id,
version: theCase.version,
}),
type: 'icon' as const,
'data-test-subj': 'action-in-progress',
};

const closeCaseAction = {
available: (item: Case | SubCase) => item.status !== CaseStatuses.closed,
enabled: (item: Case | SubCase) => isIndividual(item),
description: statuses[CaseStatuses.closed].actions.single.title,
icon: statuses[CaseStatuses.closed].icon,
name: statuses[CaseStatuses.closed].actions.single.title,
onClick: (theCase: Case) =>
dispatchUpdate({
updateKey: 'status',
updateValue: CaseStatuses.closed,
caseId: theCase.id,
version: theCase.version,
}),
type: 'icon' as const,
'data-test-subj': 'action-close',
};

return [
openCaseAction,
makeInProgressAction,
closeCaseAction,
{
description: i18n.DELETE_CASE(),
icon: 'trash',
name: i18n.DELETE_CASE(),
onClick: deleteCaseOnClick,
type: 'icon',
'data-test-subj': 'action-delete',
},
];
};
}: GetActions): Array<DefaultItemIconButtonAction<Case>> => [
{
description: i18n.DELETE_CASE(),
icon: 'trash',
name: i18n.DELETE_CASE(),
onClick: deleteCaseOnClick,
type: 'icon',
'data-test-subj': 'action-delete',
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,24 @@ const alertDataMock = {
alertId: 'alert-id',
owner: SECURITY_SOLUTION_OWNER,
};
jest.mock('../../common/lib/kibana', () => {
const originalModule = jest.requireActual('../../common/lib/kibana');
return {
...originalModule,
useKibana: () => ({
services: {
triggersActionsUi: {
actionTypeRegistry: {
get: jest.fn().mockReturnValue({
actionTypeTitle: '.jira',
iconClass: 'logoSecurity',
}),
},
},
},
}),
};
});

describe('AllCasesGeneric ', () => {
beforeEach(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ export const AllCasesGeneric = React.memo<AllCasesGenericProps>(
isLoadingCases: loading,
refreshCases,
showActions,
userCanCrud,
});

const itemIdToExpandedRowMap = useMemo(
Expand Down
19 changes: 19 additions & 0 deletions x-pack/plugins/cases/public/components/all_cases/columns.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,25 @@ import { ExternalServiceColumn } from './columns';

import { useGetCasesMockState } from '../../containers/mock';

jest.mock('../../common/lib/kibana', () => {
const originalModule = jest.requireActual('../../common/lib/kibana');
return {
...originalModule,
useKibana: () => ({
services: {
triggersActionsUi: {
actionTypeRegistry: {
get: jest.fn().mockReturnValue({
actionTypeTitle: '.jira',
iconClass: 'logoSecurity',
}),
},
},
},
}),
};
});

describe('ExternalServiceColumn ', () => {
it('Not pushed render', () => {
const wrapper = mount(
Expand Down
108 changes: 62 additions & 46 deletions x-pack/plugins/cases/public/components/all_cases/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
EuiTableFieldDataColumnType,
EuiFlexGroup,
EuiFlexItem,
EuiIcon,
} from '@elastic/eui';
import { RIGHT_ALIGNMENT } from '@elastic/eui/lib/services';
import styled from 'styled-components';
Expand All @@ -25,13 +26,14 @@ import { getEmptyTagValue } from '../empty_value';
import { FormattedRelativePreferenceDate } from '../formatted_date';
import { CaseDetailsHrefSchema, CaseDetailsLink, CasesNavigation } from '../links';
import * as i18n from './translations';
import { Status } from '../status';
import { getSubCasesStatusCountsBadges, isSubCase } from './helpers';
import { ALERTS } from '../../common/translations';
import { getActions } from './actions';
import { UpdateCase } from '../../containers/use_get_cases';
import { useDeleteCases } from '../../containers/use_delete_cases';
import { ConfirmDeleteCaseModal } from '../confirm_delete_case';
import { useKibana } from '../../common/lib/kibana';
import { StatusContextMenu } from '../case_action_bar/status_context_menu';

export type CasesColumns =
| EuiTableActionsColumnType<Case>
Expand Down Expand Up @@ -62,6 +64,7 @@ export interface GetCasesColumn {
isLoadingCases: string[];
refreshCases?: (a?: boolean) => void;
showActions: boolean;
userCanCrud: boolean;
}
export const useCasesColumns = ({
caseDetailsNavigation,
Expand All @@ -72,6 +75,7 @@ export const useCasesColumns = ({
isLoadingCases,
refreshCases,
showActions,
userCanCrud,
}: GetCasesColumn): CasesColumns[] => {
// Delete case
const {
Expand Down Expand Up @@ -113,9 +117,8 @@ export const useCasesColumns = ({
() =>
getActions({
deleteCaseOnClick: toggleDeleteModal,
dispatchUpdate: handleDispatchUpdate,
}),
[toggleDeleteModal, handleDispatchUpdate]
[toggleDeleteModal]
);

useEffect(() => {
Expand Down Expand Up @@ -267,26 +270,27 @@ export const useCasesColumns = ({
return getEmptyTagValue();
},
},
{
name: i18n.INCIDENT_MANAGEMENT_SYSTEM,
render: (theCase: Case) => {
if (theCase.externalService != null) {
return renderStringField(
`${theCase.externalService.connectorName}`,
`case-table-column-connector`
);
}
return getEmptyTagValue();
},
},
{
name: i18n.STATUS,
render: (theCase: Case) => {
if (theCase?.subCases == null || theCase.subCases.length === 0) {
if (theCase.status == null || theCase.type === CaseType.collection) {
return getEmptyTagValue();
}
return <Status type={theCase.status} />;
return (
<StatusContextMenu
currentStatus={theCase.status}
disabled={!userCanCrud || isLoadingCases.length > 0}
onStatusChanged={(status) =>
handleDispatchUpdate({
updateKey: 'status',
updateValue: status,
caseId: theCase.id,
version: theCase.version,
})
}
/>
);
}

const badges = getSubCasesStatusCountsBadges(theCase.subCases);
Expand Down Expand Up @@ -322,36 +326,48 @@ interface Props {
theCase: Case;
}

const IconWrapper = styled.span`
svg {
height: 20px !important;
position: relative;
top: 3px;
width: 20px !important;
}
`;
export const ExternalServiceColumn: React.FC<Props> = ({ theCase }) => {
const handleRenderDataToPush = useCallback(() => {
const lastCaseUpdate = theCase.updatedAt != null ? new Date(theCase.updatedAt) : null;
const lastCasePush =
theCase.externalService?.pushedAt != null
? new Date(theCase.externalService?.pushedAt)
: null;
const hasDataToPush =
lastCasePush === null ||
(lastCasePush != null &&
lastCaseUpdate != null &&
lastCasePush.getTime() < lastCaseUpdate?.getTime());
return (
<p>
<EuiLink
data-test-subj={`case-table-column-external`}
href={theCase.externalService?.externalUrl}
target="_blank"
aria-label={i18n.SERVICENOW_LINK_ARIA}
>
{theCase.externalService?.externalTitle}
</EuiLink>
{hasDataToPush
? renderStringField(i18n.REQUIRES_UPDATE, `case-table-column-external-requiresUpdate`)
: renderStringField(i18n.UP_TO_DATE, `case-table-column-external-upToDate`)}
</p>
);
}, [theCase]);
if (theCase.externalService !== null) {
return handleRenderDataToPush();
const { triggersActionsUi } = useKibana().services;

if (theCase.externalService == null) {
return renderStringField(i18n.NOT_PUSHED, `case-table-column-external-notPushed`);
}
return renderStringField(i18n.NOT_PUSHED, `case-table-column-external-notPushed`);

const lastCaseUpdate = theCase.updatedAt != null ? new Date(theCase.updatedAt) : null;
const lastCasePush =
theCase.externalService?.pushedAt != null ? new Date(theCase.externalService?.pushedAt) : null;
const hasDataToPush =
lastCasePush === null ||
(lastCaseUpdate != null && lastCasePush.getTime() < lastCaseUpdate?.getTime());
return (
<p>
<IconWrapper>
<EuiIcon
size="original"
title={theCase.externalService?.connectorName}
type={triggersActionsUi.actionTypeRegistry.get(theCase.connector.type)?.iconClass ?? ''}
/>
</IconWrapper>
<EuiLink
data-test-subj={`case-table-column-external`}
title={theCase.externalService?.connectorName}
href={theCase.externalService?.externalUrl}
target="_blank"
aria-label={i18n.PUSH_LINK_ARIA(theCase.externalService?.connectorName)}
>
{theCase.externalService?.externalTitle}
</EuiLink>
{hasDataToPush
? renderStringField(i18n.REQUIRES_UPDATE, `case-table-column-external-requiresUpdate`)
: renderStringField(i18n.UP_TO_DATE, `case-table-column-external-upToDate`)}
</p>
);
};
Loading

0 comments on commit c24318a

Please sign in to comment.