Skip to content

Commit

Permalink
[Security Solution] Standardize actions in Alerts KPI visualizations (e…
Browse files Browse the repository at this point in the history
…lastic#206340)

### Background

The initial intent of the PR was to address customer feedback regarding
hover actions in the kpi charts. Because the hover actions wrap around
the label, some users find the trigger sensitive, especially when screen
resolution is high and the text labels are small.

Upon exploring options and reviewing with UIUX team, it was decided that
we should follow the take action format in Lens charts (inline actions
inline, vertical 3 dots icon, and black color), to ensure that we have a
standard experience in charts.

### Before ###
**Non-Lens charts: overview charts, treemap**
- Popover actions upon hover
- Popover content is blue (default color)

![image](https://github.com/user-attachments/assets/20091b16-4408-4f55-ace8-95cbac25ff2e)

![image](https://github.com/user-attachments/assets/06b97ad8-fe41-4508-95ff-cc0ee5a73338)

**Lens charts: Trend graph, Count table**
- Actions are inline, with the vertical 3 dots icon
- Icon and menu item are in black color

![image](https://github.com/user-attachments/assets/df0dd709-ec02-4549-bf35-60ca5fe57179)

### After

All the non-Lens charts have inline actions in black

![image](https://github.com/user-attachments/assets/29c4e9e9-f458-4520-b90f-e4b16a5e1318)

![image](https://github.com/user-attachments/assets/ba904202-338c-4154-b645-128729010d1d)

### Changes to `CellActions` package

This PR focuses on making the inline option flexible, by taking
additional styling options in metadata

```
metadata={{
          extraActionsIconType: 'boxesVertical',
          extraActionsColor: 'text',
        }}
```
The styling does not impact hover options

![image](https://github.com/user-attachments/assets/07e59cd1-0d0b-472f-9ef1-a8a185d8dd3c)

### 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
- [x] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

(cherry picked from commit fc6e1d6)

# Conflicts:
#	x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_progress_bar_panel/alerts_progress_bar.tsx
#	x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/severity_level_panel/columns.tsx
  • Loading branch information
christineweng committed Feb 11, 2025
1 parent f5383e7 commit 4784089
Show file tree
Hide file tree
Showing 23 changed files with 403 additions and 214 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,23 @@ export const CellActionInline = () => (
</CellActions>
);

export const CellActionInlineCustomStyle = () => (
<CellActions
mode={CellActionsMode.INLINE}
triggerId={TRIGGER_ID}
data={[
{
field: FIELD,
value: VALUE,
},
]}
extraActionsIconType="boxesVertical"
extraActionsColor="text"
>
{'Field value'}
</CellActions>
);

export const CellActionHoverPopoverDown = () => (
<CellActions
mode={CellActionsMode.HOVER_DOWN}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export const CellActions: React.FC<CellActionsProps> = ({
disabledActionTypes = [],
metadata,
className,
extraActionsIconType,
extraActionsColor,
}) => {
const nodeRef = useRef<HTMLDivElement | null>(null);

Expand Down Expand Up @@ -83,6 +85,8 @@ export const CellActions: React.FC<CellActionsProps> = ({
showActionTooltips={showActionTooltips}
visibleCellActions={visibleCellActions}
disabledActionTypes={disabledActionTypes}
extraActionsIconType={extraActionsIconType}
extraActionsColor={extraActionsColor}
/>
</EuiFlexItem>
</EuiFlexGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ import { ExtraActionsButton } from './extra_actions_button';

describe('ExtraActionsButton', () => {
it('renders', () => {
const { queryByTestId } = render(<ExtraActionsButton onClick={() => {}} showTooltip={false} />);
const { queryByTestId, container } = render(
<ExtraActionsButton onClick={() => {}} showTooltip={false} />
);

expect(queryByTestId('showExtraActionsButton')).toBeInTheDocument();
expect(container.querySelector('[data-euiicon-type="boxesHorizontal"]')).toBeInTheDocument();
});

it('renders tooltip when showTooltip=true is received', () => {
Expand All @@ -30,4 +33,18 @@ describe('ExtraActionsButton', () => {
fireEvent.click(getByTestId('showExtraActionsButton'));
expect(onClick).toHaveBeenCalled();
});

it('renders with correct icon when it is specified in the actionContext', () => {
const { queryByTestId, container } = render(
<ExtraActionsButton
onClick={() => {}}
showTooltip={false}
extraActionsIconType="boxesVertical"
extraActionsColor="text"
/>
);

expect(queryByTestId('showExtraActionsButton')).toBeInTheDocument();
expect(container.querySelector('[data-euiicon-type="boxesVertical"]')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,40 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { EuiButtonIcon, EuiToolTip } from '@elastic/eui';
import { EuiButtonIcon, EuiToolTip, type EuiButtonIconProps } from '@elastic/eui';
import React from 'react';
import { SHOW_MORE_ACTIONS } from './translations';

interface ExtraActionsButtonProps {
onClick: () => void;
showTooltip: boolean;
extraActionsIconType?: EuiButtonIconProps['iconType'];
extraActionsColor?: EuiButtonIconProps['color'];
}

export const ExtraActionsButton: React.FC<ExtraActionsButtonProps> = ({ onClick, showTooltip }) =>
showTooltip ? (
export const ExtraActionsButton: React.FC<ExtraActionsButtonProps> = ({
onClick,
showTooltip,
extraActionsIconType,
extraActionsColor,
}) => {
return showTooltip ? (
<EuiToolTip content={SHOW_MORE_ACTIONS}>
<EuiButtonIcon
data-test-subj="showExtraActionsButton"
aria-label={SHOW_MORE_ACTIONS}
iconType="boxesHorizontal"
iconType={extraActionsIconType ?? 'boxesHorizontal'}
color={extraActionsColor}
onClick={onClick}
/>
</EuiToolTip>
) : (
<EuiButtonIcon
data-test-subj="showExtraActionsButton"
aria-label={SHOW_MORE_ACTIONS}
iconType="boxesHorizontal"
iconType={extraActionsIconType ?? 'boxesHorizontal'}
color={extraActionsColor}
onClick={onClick}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,22 @@ import {
EuiPopover,
EuiScreenReaderOnly,
EuiWrappingPopover,
type EuiButtonIconProps,
} from '@elastic/eui';
import React, { useMemo } from 'react';
import { euiThemeVars } from '@kbn/ui-theme';
import { css } from '@emotion/react';
import { EXTRA_ACTIONS_ARIA_LABEL, YOU_ARE_IN_A_DIALOG_CONTAINING_OPTIONS } from './translations';
import type { CellAction, CellActionExecutionContext } from '../types';

const euiContextMenuItemCSS = css`
color: ${euiThemeVars.euiColorPrimaryText};
`;
const getEuiContextMenuItemCSS = (extraActionsColor?: EuiButtonIconProps['color']) => {
if (extraActionsColor && extraActionsColor === 'text') {
return undefined;
}
return css`
color: ${euiThemeVars.euiColorPrimaryText};
`;
};

interface ActionsPopOverProps {
anchorPosition: 'rightCenter' | 'downCenter';
Expand All @@ -31,6 +37,7 @@ interface ActionsPopOverProps {
closePopOver: () => void;
actions: CellAction[];
button: JSX.Element;
extraActionsColor?: EuiButtonIconProps['color'];
}

export const ExtraActionsPopOver: React.FC<ActionsPopOverProps> = ({
Expand All @@ -40,6 +47,7 @@ export const ExtraActionsPopOver: React.FC<ActionsPopOverProps> = ({
isOpen,
closePopOver,
button,
extraActionsColor,
}) => (
<EuiPopover
button={button}
Expand All @@ -57,14 +65,15 @@ export const ExtraActionsPopOver: React.FC<ActionsPopOverProps> = ({
actions={actions}
actionContext={actionContext}
closePopOver={closePopOver}
extraActionsColor={extraActionsColor}
/>
</EuiPopover>
);

interface ExtraActionsPopOverWithAnchorProps
extends Pick<
ActionsPopOverProps,
'anchorPosition' | 'actionContext' | 'closePopOver' | 'isOpen' | 'actions'
'anchorPosition' | 'actionContext' | 'closePopOver' | 'isOpen' | 'actions' | 'extraActionsColor'
> {
anchorRef: React.RefObject<HTMLElement>;
}
Expand All @@ -76,6 +85,7 @@ export const ExtraActionsPopOverWithAnchor = ({
isOpen,
closePopOver,
actions,
extraActionsColor,
}: ExtraActionsPopOverWithAnchorProps) => {
return anchorRef.current ? (
<EuiWrappingPopover
Expand All @@ -95,26 +105,28 @@ export const ExtraActionsPopOverWithAnchor = ({
actions={actions}
actionContext={actionContext}
closePopOver={closePopOver}
extraActionsColor={extraActionsColor}
/>
</EuiWrappingPopover>
) : null;
};

type ExtraActionsPopOverContentProps = Pick<
ActionsPopOverProps,
'actionContext' | 'closePopOver' | 'actions'
'actionContext' | 'closePopOver' | 'actions' | 'extraActionsColor'
>;

const ExtraActionsPopOverContent: React.FC<ExtraActionsPopOverContentProps> = ({
actionContext,
actions,
closePopOver,
extraActionsColor,
}) => {
const items = useMemo(
() =>
actions.map((action) => (
<EuiContextMenuItem
css={euiContextMenuItemCSS}
css={getEuiContextMenuItemCSS(extraActionsColor)}
key={action.id}
icon={action.getIconType(actionContext)}
aria-label={action.getDisplayName(actionContext)}
Expand All @@ -127,7 +139,7 @@ const ExtraActionsPopOverContent: React.FC<ExtraActionsPopOverContentProps> = ({
{action.getDisplayName(actionContext)}
</EuiContextMenuItem>
)),
[actionContext, actions, closePopOver]
[actionContext, actions, closePopOver, extraActionsColor]
);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { EuiPopover, EuiScreenReaderOnly } from '@elastic/eui';

import { EuiPopover, EuiScreenReaderOnly, type EuiButtonIconProps } from '@elastic/eui';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { euiThemeVars } from '@kbn/ui-theme';
import { css } from '@emotion/react';
Expand Down Expand Up @@ -43,6 +42,8 @@ interface Props {
actionContext: CellActionExecutionContext;
showActionTooltips: boolean;
disabledActionTypes: string[];
extraActionsIconType?: EuiButtonIconProps['iconType'];
extraActionsColor?: EuiButtonIconProps['color'];
}

export const HoverActionsPopover: React.FC<Props> = ({
Expand All @@ -52,6 +53,8 @@ export const HoverActionsPopover: React.FC<Props> = ({
actionContext,
showActionTooltips,
disabledActionTypes,
extraActionsIconType,
extraActionsColor,
}) => {
const contentRef = useRef<HTMLDivElement>(null);
const [isExtraActionsPopoverOpen, setIsExtraActionsPopoverOpen] = useState(false);
Expand Down Expand Up @@ -161,6 +164,8 @@ export const HoverActionsPopover: React.FC<Props> = ({
<ExtraActionsButton
onClick={onShowExtraActionsClick}
showTooltip={showActionTooltips}
extraActionsIconType={extraActionsIconType}
extraActionsColor={extraActionsColor}
/>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*/

import React, { useCallback, useMemo, useState } from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { EuiFlexGroup, EuiFlexItem, type EuiButtonIconProps } from '@elastic/eui';
import { ActionItem } from './cell_action_item';
import { usePartitionActions } from '../hooks/actions';
import { ExtraActionsPopOver } from './extra_actions_popover';
Expand All @@ -22,6 +22,8 @@ interface InlineActionsProps {
showActionTooltips: boolean;
visibleCellActions: number;
disabledActionTypes: string[];
extraActionsIconType?: EuiButtonIconProps['iconType'];
extraActionsColor?: EuiButtonIconProps['color'];
}

export const InlineActions: React.FC<InlineActionsProps> = ({
Expand All @@ -30,6 +32,8 @@ export const InlineActions: React.FC<InlineActionsProps> = ({
showActionTooltips,
visibleCellActions,
disabledActionTypes,
extraActionsIconType,
extraActionsColor,
}) => {
const { value: actions } = useLoadActions(actionContext, { disabledActionTypes });
const { extraActions, visibleActions } = usePartitionActions(actions ?? [], visibleCellActions);
Expand All @@ -38,8 +42,15 @@ export const InlineActions: React.FC<InlineActionsProps> = ({
const togglePopOver = useCallback(() => setIsPopoverOpen((isOpen) => !isOpen), []);
const closePopOver = useCallback(() => setIsPopoverOpen(false), []);
const button = useMemo(
() => <ExtraActionsButton onClick={togglePopOver} showTooltip={showActionTooltips} />,
[togglePopOver, showActionTooltips]
() => (
<ExtraActionsButton
onClick={togglePopOver}
showTooltip={showActionTooltips}
extraActionsIconType={extraActionsIconType}
extraActionsColor={extraActionsColor}
/>
),
[togglePopOver, showActionTooltips, extraActionsIconType, extraActionsColor]
);

return (
Expand Down Expand Up @@ -68,6 +79,7 @@ export const InlineActions: React.FC<InlineActionsProps> = ({
button={button}
closePopOver={closePopOver}
isOpen={isPopoverOpen}
extraActionsColor={extraActionsColor}
/>
</EuiFlexItem>
) : null}
Expand Down
13 changes: 12 additions & 1 deletion src/platform/packages/shared/kbn-cell-actions/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {
} from '@kbn/ui-actions-plugin/public';
import type { FieldSpec } from '@kbn/data-views-plugin/common';
import type { Serializable } from '@kbn/utility-types';
import type { EuiButtonIconProps } from '@elastic/eui';
import type { CellActionsMode } from './constants';

export * from './actions/types';
Expand Down Expand Up @@ -85,8 +86,18 @@ export type CellActionsProps = PropsWithChildren<{
* This data is sent directly to actions.
*/
metadata?: Metadata;

/**
* The class name for the cell actions.
*/
className?: string;
/**
* The icon type for the extra actions button.
*/
extraActionsIconType?: EuiButtonIconProps['iconType'];
/**
* The color for the extra actions button.
*/
extraActionsColor?: EuiButtonIconProps['color'];
}>;

export interface CellActionExecutionContext extends ActionExecutionContext {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {
} from '@kbn/cell-actions';
import React, { useMemo } from 'react';
import type { CellActionFieldValue, CellActionsData } from '@kbn/cell-actions/src/types';
import type { EuiButtonIconProps } from '@elastic/eui';
import type { SecurityCellActionMetadata } from '../../../app/actions/types';
import { SecurityCellActionsTrigger, SecurityCellActionType } from '../../../app/actions/constants';
import { SourcererScopeName } from '../../../sourcerer/store/model';
Expand Down Expand Up @@ -42,6 +43,8 @@ export interface SecurityCellActionsProps
triggerId: SecurityCellActionsTrigger;
disabledActionTypes?: SecurityCellActionType[];
metadata?: SecurityCellActionMetadata;
extraActionsIconType?: EuiButtonIconProps['iconType'];
extraActionsColor?: EuiButtonIconProps['color'];
}

export interface UseDataGridColumnsSecurityCellActionsProps
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const ChartSettingsPopoverComponent: React.FC<Props> = ({
<EuiButtonIcon
aria-label={i18n.CHART_SETTINGS_POPOVER_ARIA_LABEL}
color="text"
iconType="boxesHorizontal"
iconType="boxesVertical"
onClick={onButtonClick}
size="xs"
/>
Expand Down
Loading

0 comments on commit 4784089

Please sign in to comment.