Skip to content

Commit

Permalink
[145663] Refactor explore pages to migrate HoverActions to CellActions (
Browse files Browse the repository at this point in the history
#148056)

Epic: #144943


## Summary
Update explore pages to use the new `CellActions` component instead of
`HoverActions `.

### What is included?
* Update the user, host, and network page tables.

<img width="1512" alt="Screenshot 2023-01-17 at 13 12 16"
src="https://user-images.githubusercontent.com/1490444/212896520-f41e9026-cef0-4a37-8bd1-35784a87ca09.png">
<img width="1482" alt="Screenshot 2023-01-17 at 13 19 34"
src="https://user-images.githubusercontent.com/1490444/212897411-cd3c3ef8-bca0-461b-a1ff-c7dd67159d1b.png">

* Fields rendered when clicking on "+{N} more" 
<img width="248" alt="Screenshot 2023-01-17 at 12 51 38"
src="https://user-images.githubusercontent.com/1490444/212892255-2ecd7050-75f6-4883-b331-1fed527de53f.png">


### What is NOT included?
* Visualizations
* Fields on details pages. They are also used by the Timeline and need
to be draggable.
* Timeline
* Datagrid tables - Events and Alerts
* Plugins



### Checklist

Delete any items that are not applicable to this PR.

- [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
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)

Co-authored-by: kibanamachine <[email protected]>
  • Loading branch information
machadoum and kibanamachine authored Jan 24, 2023
1 parent ded5f9a commit d313c95
Show file tree
Hide file tree
Showing 81 changed files with 943 additions and 1,503 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe('CellActions', () => {
await getActionsPromise;
});

expect(queryByTestId('cellActions')).toBeInTheDocument();
expect(queryByTestId(`cellActions-renderContent-${FIELD.name}`)).toBeInTheDocument();
});

it('renders InlineActions when mode is INLINE', async () => {
Expand Down
6 changes: 4 additions & 2 deletions packages/kbn-cell-actions/src/components/cell_actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ export const CellActions: React.FC<CellActionsProps> = ({
[field, triggerId, metadata]
);

const dataTestSubj = `cellActions-renderContent-${field.name}`;

if (mode === CellActionsMode.HOVER) {
return (
<div ref={nodeRef} data-test-subj={'cellActions'}>
<div ref={nodeRef} data-test-subj={dataTestSubj}>
<HoverActionsPopover
actionContext={actionContext}
showActionTooltips={showActionTooltips}
Expand All @@ -51,7 +53,7 @@ export const CellActions: React.FC<CellActionsProps> = ({
}

return (
<div ref={nodeRef} data-test-subj={'cellActions'}>
<div ref={nodeRef} data-test-subj={dataTestSubj}>
{children}
<InlineActions
actionContext={actionContext}
Expand Down
254 changes: 132 additions & 122 deletions packages/kbn-cell-actions/src/components/hover_actions_popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,127 +42,137 @@ interface Props {
showActionTooltips: boolean;
}

export const HoverActionsPopover = React.memo<Props>(
({ children, visibleCellActions, actionContext, showActionTooltips }) => {
const contentRef = useRef<HTMLDivElement>(null);
const [isExtraActionsPopoverOpen, setIsExtraActionsPopoverOpen] = useState(false);
const [showHoverContent, setShowHoverContent] = useState(false);
const popoverRef = useRef<EuiPopover>(null);

const [{ value: actions }, loadActions] = useLoadActionsFn();

const { visibleActions, extraActions } = useMemo(
() => partitionActions(actions ?? [], visibleCellActions),
[actions, visibleCellActions]
);

const closePopover = useCallback(() => {
setShowHoverContent(false);
}, []);

const closeExtraActions = useCallback(
() => setIsExtraActionsPopoverOpen(false),
[setIsExtraActionsPopoverOpen]
);

const onShowExtraActionsClick = useCallback(() => {
setIsExtraActionsPopoverOpen(true);
closePopover();
}, [closePopover, setIsExtraActionsPopoverOpen]);

const openPopOverDebounced = useMemo(
() =>
debounce(() => {
if (!document.body.classList.contains(IS_DRAGGING_CLASS_NAME)) {
setShowHoverContent(true);
}
}, HOVER_INTENT_DELAY),
[]
);

// prevent setState on an unMounted component
useEffect(() => {
return () => {
openPopOverDebounced.cancel();
};
}, [openPopOverDebounced]);

const onMouseEnter = useCallback(async () => {
// Do not open actions with extra action popover is open
if (isExtraActionsPopoverOpen) return;

// memoize actions after the first call
if (actions === undefined) {
loadActions(actionContext);
}

openPopOverDebounced();
}, [isExtraActionsPopoverOpen, actions, openPopOverDebounced, loadActions, actionContext]);

const onMouseLeave = useCallback(() => {
closePopover();
}, [closePopover]);

const content = useMemo(() => {
return (
// Hack - Forces extra actions popover to close when hover content is clicked.
// This hack is required because we anchor the popover to the hover content instead
// of anchoring it to the button that triggers the popover.
// eslint-disable-next-line jsx-a11y/click-events-have-key-events
<div ref={contentRef} onMouseEnter={onMouseEnter} onClick={closeExtraActions}>
{children}
</div>
);
}, [onMouseEnter, closeExtraActions, children]);

export const HoverActionsPopover: React.FC<Props> = ({
children,
visibleCellActions,
actionContext,
showActionTooltips,
}) => {
const contentRef = useRef<HTMLDivElement>(null);
const [isExtraActionsPopoverOpen, setIsExtraActionsPopoverOpen] = useState(false);
const [showHoverContent, setShowHoverContent] = useState(false);
const popoverRef = useRef<EuiPopover>(null);

const [{ value: actions, error }, loadActions] = useLoadActionsFn();

const { visibleActions, extraActions } = useMemo(
() => partitionActions(actions ?? [], visibleCellActions),
[actions, visibleCellActions]
);

const openPopOverDebounced = useMemo(
() =>
debounce(() => {
if (!document.body.classList.contains(IS_DRAGGING_CLASS_NAME)) {
setShowHoverContent(true);
}
}, HOVER_INTENT_DELAY),
[]
);

const closePopover = useCallback(() => {
openPopOverDebounced.cancel();
setShowHoverContent(false);
}, [openPopOverDebounced]);

const closeExtraActions = useCallback(
() => setIsExtraActionsPopoverOpen(false),
[setIsExtraActionsPopoverOpen]
);

const onShowExtraActionsClick = useCallback(() => {
setIsExtraActionsPopoverOpen(true);
closePopover();
}, [closePopover, setIsExtraActionsPopoverOpen]);

// prevent setState on an unMounted component
useEffect(() => {
return () => {
openPopOverDebounced.cancel();
};
}, [openPopOverDebounced]);

const onMouseEnter = useCallback(async () => {
// Do not open actions with extra action popover is open
if (isExtraActionsPopoverOpen) return;

// memoize actions after the first call
if (actions === undefined) {
loadActions(actionContext);
}

openPopOverDebounced();
}, [isExtraActionsPopoverOpen, actions, openPopOverDebounced, loadActions, actionContext]);

const onMouseLeave = useCallback(() => {
closePopover();
}, [closePopover]);

const content = useMemo(() => {
return (
<>
<div onMouseLeave={onMouseLeave}>
<EuiPopover
panelStyle={PANEL_STYLE}
ref={popoverRef}
anchorPosition={'downCenter'}
button={content}
closePopover={closePopover}
hasArrow={false}
isOpen={showHoverContent}
panelPaddingSize="none"
repositionOnScroll
ownFocus={false}
data-test-subj={'hoverActionsPopover'}
aria-label={ACTIONS_AREA_LABEL}
>
{showHoverContent ? (
<div css={hoverContentWrapperCSS}>
<EuiScreenReaderOnly>
<p>{YOU_ARE_IN_A_DIALOG_CONTAINING_OPTIONS(actionContext.field.name)}</p>
</EuiScreenReaderOnly>
{visibleActions.map((action) => (
<ActionItem
key={action.id}
action={action}
actionContext={actionContext}
showTooltip={showActionTooltips}
/>
))}
{extraActions.length > 0 ? (
<ExtraActionsButton
onClick={onShowExtraActionsClick}
showTooltip={showActionTooltips}
/>
) : null}
</div>
) : null}
</EuiPopover>
</div>
<ExtraActionsPopOverWithAnchor
actions={extraActions}
anchorRef={contentRef}
actionContext={actionContext}
closePopOver={closeExtraActions}
isOpen={isExtraActionsPopoverOpen}
/>
</>
// Hack - Forces extra actions popover to close when hover content is clicked.
// This hack is required because we anchor the popover to the hover content instead
// of anchoring it to the button that triggers the popover.
// eslint-disable-next-line jsx-a11y/click-events-have-key-events
<div ref={contentRef} onMouseEnter={onMouseEnter} onClick={closeExtraActions}>
{children}
</div>
);
}
);
}, [onMouseEnter, closeExtraActions, children]);

useEffect(() => {
if (error) {
throw error;
}
}, [error]);

return (
<>
<div onMouseLeave={onMouseLeave}>
<EuiPopover
panelStyle={PANEL_STYLE}
ref={popoverRef}
anchorPosition={'downCenter'}
button={content}
closePopover={closePopover}
hasArrow={false}
isOpen={showHoverContent}
panelPaddingSize="none"
repositionOnScroll
ownFocus={false}
data-test-subj={'hoverActionsPopover'}
aria-label={ACTIONS_AREA_LABEL}
>
{showHoverContent ? (
<div css={hoverContentWrapperCSS}>
<EuiScreenReaderOnly>
<p>{YOU_ARE_IN_A_DIALOG_CONTAINING_OPTIONS(actionContext.field.name)}</p>
</EuiScreenReaderOnly>
{visibleActions.map((action) => (
<ActionItem
key={action.id}
action={action}
actionContext={actionContext}
showTooltip={showActionTooltips}
/>
))}
{extraActions.length > 0 ? (
<ExtraActionsButton
onClick={onShowExtraActionsClick}
showTooltip={showActionTooltips}
/>
) : null}
</div>
) : null}
</EuiPopover>
</div>
<ExtraActionsPopOverWithAnchor
actions={extraActions}
anchorRef={contentRef}
actionContext={actionContext}
closePopOver={closeExtraActions}
isOpen={isExtraActionsPopoverOpen}
/>
</>
);
};
12 changes: 9 additions & 3 deletions packages/kbn-cell-actions/src/components/inline_actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
* Side Public License, v 1.
*/

import React, { useCallback, useMemo, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { ActionItem } from './cell_action_item';
import { usePartitionActions } from '../hooks/actions';
import { ExtraActionsPopOver } from './extra_actions_popover';
import { ExtraActionsButton } from './extra_actions_button';
import type { CellActionExecutionContext } from '../types';
import { CellActionExecutionContext } from '../types';
import { useLoadActions } from '../hooks/use_load_actions';

interface InlineActionsProps {
Expand All @@ -25,7 +25,7 @@ export const InlineActions: React.FC<InlineActionsProps> = ({
showActionTooltips,
visibleCellActions,
}) => {
const { value: allActions } = useLoadActions(actionContext);
const { value: allActions, error } = useLoadActions(actionContext);
const { extraActions, visibleActions } = usePartitionActions(
allActions ?? [],
visibleCellActions
Expand All @@ -38,6 +38,12 @@ export const InlineActions: React.FC<InlineActionsProps> = ({
[togglePopOver, showActionTooltips]
);

useEffect(() => {
if (error) {
throw error;
}
}, [error]);

return (
<span data-test-subj="inlineActions">
{visibleActions.map((action, index) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver';
import {
navigateToHostRiskDetailTab,
openRiskTableFilterAndSelectTheCriticalOption,
removeCritialFilter,
removeCriticalFilter,
selectFiveItemsPerPageOption,
} from '../../tasks/host_risk';
import {
Expand Down Expand Up @@ -51,7 +51,7 @@ describe('risk tab', () => {

cy.get(HOST_BY_RISK_TABLE_CELL).eq(3).should('not.have.text', 'siem-kibana');

removeCritialFilter();
removeCriticalFilter();
});

it('should be able to change items count per page', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@

export const ALL_HOSTS_TABLE = '[data-test-subj="table-allHosts-loading-false"]';

export const HOSTS_NAMES = '[data-test-subj="render-content-host.name"] a.euiLink';
export const HOSTS_NAMES = '[data-test-subj="cellActions-renderContent-host.name"] a.euiLink';
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ export const HOST_BY_RISK_TABLE_PERPAGE_OPTIONS =
export const HOST_BY_RISK_TABLE_NEXT_PAGE_BUTTON =
'[data-test-subj="numberedPagination"] [data-test-subj="pagination-button-next"]';

export const HOST_BY_RISK_TABLE_HOSTNAME_CELL = '[data-test-subj="render-content-host.name"]';
export const HOST_BY_RISK_TABLE_HOSTNAME_CELL =
'[data-test-subj="cellActions-renderContent-host.name"]';
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
* 2.0.
*/

export const PROCESS_NAME_FIELD = '[data-test-subj="render-content-process.name"]';
export const PROCESS_NAME_FIELD = '[data-test-subj="cellActions-renderContent-process.name"]';

export const UNCOMMON_PROCESSES_TABLE = '[data-test-subj="table-uncommonProcesses-loading-false"]';
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@ export const IPS_TABLE_LOADED = '[data-test-subj="table-topNFlowSource-loading-f

export const EXPAND_OVERFLOW_ITEMS = '[data-test-subj="overflow-button"]';

export const FILTER_IN = '[data-test-subj="hover-actions-filter-for"]';
export const FILTER_IN = '[data-test-subj="actionItem-security_filterIn"]';

export const FILTER_OUT = '[data-test-subj="hover-actions-filter-out"]';
export const FILTER_OUT = '[data-test-subj="actionItem-security_filterOut"]';

export const ADD_TO_TIMELINE = '[data-test-subj="add-to-timeline"]';
export const ADD_TO_TIMELINE = '[data-test-subj="actionItem-security_addToTimeline"]';

export const SHOW_TOP_FIELD = '[data-test-subj="show-top-field"]';
export const SHOW_TOP_FIELD = '[data-test-subj="actionItem-security_showTopN"]';

export const COPY = '[data-test-subj="clipboard"]';
export const COPY = '[data-test-subj="actionItem-security_copyToClipboard"]';

export const TOP_N_CONTAINER = '[data-test-subj="topN-container"]';

export const DESTINATION_DOMAIN = `[data-test-subj="more-container"] [data-test-subj="render-content-destination.domain"]`;
export const DESTINATION_DOMAIN = `[data-test-subj="more-container"] [data-test-subj="cellActions-renderContent-destination.domain"]`;

export const OVERFLOW_ITEM =
'[data-test-subj="more-container"] [data-test-subj="render-content-destination.domain"]';
'[data-test-subj="more-container"] [data-test-subj="cellActions-renderContent-destination.domain"]';
Loading

0 comments on commit d313c95

Please sign in to comment.