Skip to content

Commit

Permalink
[Security Solution] [Endpoint] Expand/collapse all collapsible cards …
Browse files Browse the repository at this point in the history
…from list in one action (#119592)

* Expand/collapse all collapsible cards from list in one action

* Adds unit test

* Fix ts errors when using FormatedMessage component from kibana i18n wrong import

* Fix non truncated text with tooltip

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
dasansol92 and kibanamachine authored Nov 30, 2021
1 parent 37d7588 commit d5e199b
Show file tree
Hide file tree
Showing 5 changed files with 394 additions and 294 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ describe.each([

it.each([
['header', 'testGrid-header'],
['expand/collapse placeholder', 'testGrid-header-expandCollapsePlaceHolder'],
['expand/collapse button', 'testGrid-header-expandCollapseAllButton'],
['name column', 'testGrid-header-layout-titleHolder'],
['description column', 'testGrid-header-layout-descriptionHolder'],
['description column', 'testGrid-header-layout-cardActionsPlaceholder'],
Expand Down Expand Up @@ -128,4 +128,55 @@ describe.each([
expect(renderResult.getByTestId('card-1-criteriaConditions')).not.toBeNull();
});
});

describe('and when cards are expanded/collapsed all together', () => {
it('should call onExpandCollapse callback when expand all', () => {
render();
act(() => {
fireEvent.click(renderResult.getByTestId('testGrid-header-expandCollapseAllButton'));
});

expect(expandCollapseHandler).toHaveBeenCalledWith({
expanded: items,
collapsed: [],
});
});

it('should call onExpandCollapse callback when collapse all', () => {
cardComponentPropsProvider = jest.fn((item) => {
return {
'data-test-subj': `card-${items.indexOf(item as AnyArtifact)}`,
expanded: true,
};
});
render();
act(() => {
fireEvent.click(renderResult.getByTestId('testGrid-header-expandCollapseAllButton'));
});

expect(expandCollapseHandler).toHaveBeenCalledWith({
expanded: [],
collapsed: items,
});
});

it('should call onExpandCollapse callback when expand all if not all items are expanded', () => {
cardComponentPropsProvider = jest.fn((item) => {
const index = items.indexOf(item as AnyArtifact);
return {
'data-test-subj': `card-${index}`,
expanded: index === 0 ? false : true,
};
});
render();
act(() => {
fireEvent.click(renderResult.getByTestId('testGrid-header-expandCollapseAllButton'));
});

expect(expandCollapseHandler).toHaveBeenLastCalledWith({
expanded: items,
collapsed: [],
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,29 @@ export const ArtifactCardGrid = memo<ArtifactCardGridProps>(
[callerDefinedCardProps, onExpandCollapse]
);

const isEverythingExpanded = useMemo(() => {
for (const [_, currentCardProps] of callerDefinedCardProps) {
const currentExpandedState = Boolean(currentCardProps.expanded);
if (!currentExpandedState) {
return false;
}
}
return true;
}, [callerDefinedCardProps]);

const handleCardExpandCollapseAll = useCallback(() => {
let expanded: AnyArtifact[] = [];
let collapsed: AnyArtifact[] = [];

if (!isEverythingExpanded) {
expanded = Array.from(callerDefinedCardProps.keys());
} else {
collapsed = Array.from(callerDefinedCardProps.keys());
}

onExpandCollapse({ expanded, collapsed });
}, [callerDefinedCardProps, onExpandCollapse, isEverythingExpanded]);

// Full list of card props that includes the actual artifact and the callbacks
type FullCardProps = Map<AnyArtifact, ArtifactEntryCollapsibleCardProps>;
const fullCardProps = useMemo<FullCardProps>(() => {
Expand Down Expand Up @@ -127,7 +150,11 @@ export const ArtifactCardGrid = memo<ArtifactCardGridProps>(

return (
<>
<GridHeader data-test-subj={getTestId('header')} />
<GridHeader
expandAllIconType={isEverythingExpanded ? 'fold' : 'unfold'}
onExpandCollapseAll={handleCardExpandCollapseAll}
data-test-subj={getTestId('header')}
/>

<PaginatedContent
{...paginatedContentProps}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
*/

import React, { memo, useMemo } from 'react';
import { CommonProps, EuiText } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { CommonProps, EuiText, EuiButtonIcon } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import styled from 'styled-components';
import { CardCompressedHeaderLayout, CardSectionPanel } from '../../artifact_entry_card';
Expand All @@ -17,55 +18,76 @@ const GridHeaderContainer = styled(CardSectionPanel)`
padding-bottom: ${({ theme }) => theme.eui.paddingSizes.s};
`;

export type GridHeaderProps = Pick<CommonProps, 'data-test-subj'>;
export const GridHeader = memo<GridHeaderProps>(({ 'data-test-subj': dataTestSubj }) => {
const getTestId = useTestIdGenerator(dataTestSubj);
export type GridHeaderProps = Pick<CommonProps, 'data-test-subj'> & {
expandAllIconType: 'fold' | 'unfold';
onExpandCollapseAll(): void;
};
export const GridHeader = memo<GridHeaderProps>(
({ 'data-test-subj': dataTestSubj, expandAllIconType, onExpandCollapseAll }) => {
const getTestId = useTestIdGenerator(dataTestSubj);

const expandToggleElement = useMemo(
() => <div data-test-subj={getTestId('expandCollapsePlaceHolder')} style={{ width: '24px' }} />,
[getTestId]
);
const expandToggleElement = useMemo(
() => (
<EuiButtonIcon
data-test-subj={getTestId('expandCollapseAllButton')}
aria-label={i18n.translate(
'xpack.securitySolution.artifactCardGrid.expandCollapseLabel',
{
defaultMessage: '{action} all cards',
values: {
action: 'unfold' ? 'Expand' : 'Collapse',
},
}
)}
iconType={expandAllIconType}
onClick={() => onExpandCollapseAll()}
style={{ marginLeft: '-5px' }}
/>
),
[getTestId, expandAllIconType, onExpandCollapseAll]
);

return (
<GridHeaderContainer data-test-subj={dataTestSubj}>
<CardCompressedHeaderLayout
expanded={false}
expandToggle={expandToggleElement}
data-test-subj={getTestId('layout')}
flushTop={true}
name={
<EuiText size="xs" data-test-subj={getTestId('name')}>
<strong>
<FormattedMessage
id="xpack.securitySolution.artifactCardGrid.nameColumn"
defaultMessage="Name"
/>
</strong>
</EuiText>
}
description={
<EuiText size="xs" data-test-subj={getTestId('description')}>
<strong>
<FormattedMessage
id="xpack.securitySolution.artifactCardGrid.DescriptionColumn"
defaultMessage="Description"
/>
</strong>
</EuiText>
}
effectScope={
<EuiText size="xs" data-test-subj={getTestId('assignment')}>
<strong>
<FormattedMessage
id="xpack.securitySolution.artifactCardGrid.assignmentColumn"
defaultMessage="Assignment"
/>
</strong>
</EuiText>
}
actionMenu={true}
/>
</GridHeaderContainer>
);
});
return (
<GridHeaderContainer data-test-subj={dataTestSubj}>
<CardCompressedHeaderLayout
expanded={false}
expandToggle={expandToggleElement}
data-test-subj={getTestId('layout')}
flushTop={true}
name={
<EuiText size="xs" data-test-subj={getTestId('name')}>
<strong>
<FormattedMessage
id="xpack.securitySolution.artifactCardGrid.nameColumn"
defaultMessage="Name"
/>
</strong>
</EuiText>
}
description={
<EuiText size="xs" data-test-subj={getTestId('description')}>
<strong>
<FormattedMessage
id="xpack.securitySolution.artifactCardGrid.DescriptionColumn"
defaultMessage="Description"
/>
</strong>
</EuiText>
}
effectScope={
<EuiText size="xs" data-test-subj={getTestId('assignment')}>
<strong>
<FormattedMessage
id="xpack.securitySolution.artifactCardGrid.assignmentColumn"
defaultMessage="Assignment"
/>
</strong>
</EuiText>
}
actionMenu={true}
/>
</GridHeaderContainer>
);
}
);
GridHeader.displayName = 'GridHeader';
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ export const TextValueDisplay = memo<TextValueDisplayProps>(
}, [bold, children]);

return (
<EuiText size={size} className={cssClassNames} data-test-subj={dataTestSubj}>
<EuiText size={size} data-test-subj={dataTestSubj}>
{withTooltip &&
'string' === typeof children &&
children.length > 0 &&
children !== getEmptyValue() ? (
<EuiToolTip content={children} position="top">
<EuiToolTip anchorClassName={cssClassNames} content={children} position="top">
<>{textContent}</>
</EuiToolTip>
) : (
Expand Down
Loading

0 comments on commit d5e199b

Please sign in to comment.