Delete CellActions from UI actions plugin (#149522)

## Summary
This PR only deletes the component from the UI action plugin.
@semd has already added the component to a new package here
https://github.com/elastic/kibana/pull/149057

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Pablo Machado 2023-01-27 10:37:04 +01:00 committed by GitHub
parent c4a0ed2c19
commit 482d1ced82
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 0 additions and 1596 deletions

View file

@ -42,7 +42,6 @@ const STORYBOOKS = [
'security_solution',
'shared_ux',
'triggers_actions_ui',
'ui_actions',
'ui_actions_enhanced',
'language_documentation_popover',
'unified_search',

View file

@ -47,6 +47,5 @@ export const storybookAliases = {
threat_intelligence: 'x-pack/plugins/threat_intelligence/.storybook',
triggers_actions_ui: 'x-pack/plugins/triggers_actions_ui/.storybook',
ui_actions_enhanced: 'src/plugins/ui_actions_enhanced/.storybook',
ui_actions: 'src/plugins/ui_actions/.storybook',
unified_search: 'src/plugins/unified_search/.storybook',
};

View file

@ -1,17 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
const defaultConfig = require('@kbn/storybook').defaultConfig;
module.exports = {
...defaultConfig,
stories: ['../**/*.stories.tsx'],
reactOptions: {
strictMode: true,
},
};

View file

@ -1,17 +0,0 @@
This package provides a uniform interface for displaying UI actions for a cell.
For the `CellActions` component to work, it must be wrapped by `CellActionsContextProvider`. Ideally, the wrapper should stay on the top of the rendering tree.
Example:
```JSX
<CellActionsContextProvider
// call uiActions.getTriggerCompatibleActions(triggerId, data)
getCompatibleActions={getCompatibleActions}>
...
<CellActions mode={CellActionsMode.HOVER_POPOVER} triggerId={MY_TRIGGER_ID} config={{ field: 'fieldName', value: 'fieldValue', fieldType: 'text' }}>
Hover me
</CellActions>
</CellActionsContextProvider>
```
`CellActions` component will display all compatible actions registered for the trigger id.

View file

@ -1,34 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { render } from '@testing-library/react';
import React from 'react';
import { makeAction } from '../mocks/helpers';
import { CellActionExecutionContext } from './cell_actions';
import { ActionItem } from './cell_action_item';
describe('ActionItem', () => {
it('renders', () => {
const action = makeAction('test-action');
const actionContext = {} as CellActionExecutionContext;
const { queryByTestId } = render(
<ActionItem action={action} actionContext={actionContext} showTooltip={false} />
);
expect(queryByTestId('actionItem-test-action')).toBeInTheDocument();
});
it('renders tooltip when showTooltip=true is received', () => {
const action = makeAction('test-action');
const actionContext = {} as CellActionExecutionContext;
const { container } = render(
<ActionItem action={action} actionContext={actionContext} showTooltip />
);
expect(container.querySelector('.euiToolTipAnchor')).not.toBeNull();
});
});

View file

@ -1,45 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import React, { useMemo } from 'react';
import { EuiButtonIcon, EuiToolTip, IconType } from '@elastic/eui';
import type { Action } from '../../actions';
import { CellActionExecutionContext } from './cell_actions';
export const ActionItem = ({
action,
actionContext,
showTooltip,
}: {
action: Action;
actionContext: CellActionExecutionContext;
showTooltip: boolean;
}) => {
const actionProps = useMemo(
() => ({
iconType: action.getIconType(actionContext) as IconType,
onClick: () => action.execute(actionContext),
'data-test-subj': `actionItem-${action.id}`,
'aria-label': action.getDisplayName(actionContext),
}),
[action, actionContext]
);
if (!actionProps.iconType) return null;
return showTooltip ? (
<EuiToolTip
content={action.getDisplayNameTooltip ? action.getDisplayNameTooltip(actionContext) : ''}
>
<EuiButtonIcon {...actionProps} iconSize="s" />
</EuiToolTip>
) : (
<EuiButtonIcon {...actionProps} iconSize="s" />
);
};

View file

@ -1,77 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import React from 'react';
import { ComponentStory } from '@storybook/react';
import { CellActionsContextProvider } from './cell_actions_context';
import { makeAction } from '../mocks/helpers';
import { CellActions, CellActionsMode, CellActionsProps } from './cell_actions';
const TRIGGER_ID = 'testTriggerId';
const FIELD = { name: 'name', value: '123', type: 'text' };
const getCompatibleActions = () =>
Promise.resolve([
makeAction('Filter in', 'plusInCircle', 2),
makeAction('Filter out', 'minusInCircle', 3),
makeAction('Minimize', 'minimize', 1),
makeAction('Send email', 'email', 4),
makeAction('Pin field', 'pin', 5),
]);
export default {
title: 'CellAction',
decorators: [
(storyFn: Function) => (
<CellActionsContextProvider
// call uiActions getTriggerCompatibleActions(triggerId, data)
getTriggerCompatibleActions={getCompatibleActions}
>
<div style={{ paddingTop: '70px' }} />
{storyFn()}
</CellActionsContextProvider>
),
],
};
const CellActionsTemplate: ComponentStory<React.FC<CellActionsProps>> = (args) => (
<CellActions {...args}>Field value</CellActions>
);
export const DefaultWithControls = CellActionsTemplate.bind({});
DefaultWithControls.argTypes = {
mode: {
options: [CellActionsMode.HOVER_POPOVER, CellActionsMode.ALWAYS_VISIBLE],
defaultValue: CellActionsMode.HOVER_POPOVER,
control: {
type: 'radio',
},
},
};
DefaultWithControls.args = {
showActionTooltips: true,
mode: CellActionsMode.ALWAYS_VISIBLE,
triggerId: TRIGGER_ID,
field: FIELD,
visibleCellActions: 3,
};
export const CellActionInline = ({}: {}) => (
<CellActions mode={CellActionsMode.ALWAYS_VISIBLE} triggerId={TRIGGER_ID} field={FIELD}>
Field value
</CellActions>
);
export const CellActionHoverPopup = ({}: {}) => (
<CellActions mode={CellActionsMode.HOVER_POPOVER} triggerId={TRIGGER_ID} field={FIELD}>
Hover me
</CellActions>
);

View file

@ -1,74 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { act, render } from '@testing-library/react';
import React from 'react';
import { CellActions, CellActionsMode } from './cell_actions';
import { CellActionsContextProvider } from './cell_actions_context';
const TRIGGER_ID = 'test-trigger-id';
const FIELD = { name: 'name', value: '123', type: 'text' };
describe('CellActions', () => {
it('renders', async () => {
const getActionsPromise = Promise.resolve([]);
const getActions = () => getActionsPromise;
const { queryByTestId } = render(
<CellActionsContextProvider getTriggerCompatibleActions={getActions}>
<CellActions mode={CellActionsMode.ALWAYS_VISIBLE} triggerId={TRIGGER_ID} field={FIELD}>
Field value
</CellActions>
</CellActionsContextProvider>
);
await act(async () => {
await getActionsPromise;
});
expect(queryByTestId('cellActions')).toBeInTheDocument();
});
it('renders InlineActions when mode is ALWAYS_VISIBLE', async () => {
const getActionsPromise = Promise.resolve([]);
const getActions = () => getActionsPromise;
const { queryByTestId } = render(
<CellActionsContextProvider getTriggerCompatibleActions={getActions}>
<CellActions mode={CellActionsMode.ALWAYS_VISIBLE} triggerId={TRIGGER_ID} field={FIELD}>
Field value
</CellActions>
</CellActionsContextProvider>
);
await act(async () => {
await getActionsPromise;
});
expect(queryByTestId('inlineActions')).toBeInTheDocument();
});
it('renders HoverActionsPopover when mode is HOVER_POPOVER', async () => {
const getActionsPromise = Promise.resolve([]);
const getActions = () => getActionsPromise;
const { queryByTestId } = render(
<CellActionsContextProvider getTriggerCompatibleActions={getActions}>
<CellActions mode={CellActionsMode.HOVER_POPOVER} triggerId={TRIGGER_ID} field={FIELD}>
Field value
</CellActions>
</CellActionsContextProvider>
);
await act(async () => {
await getActionsPromise;
});
expect(queryByTestId('hoverActionsPopover')).toBeInTheDocument();
});
});

View file

@ -1,140 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import React, { useMemo, useRef } from 'react';
import type { ActionExecutionContext } from '../../actions';
import { InlineActions } from './inline_actions';
import { HoverActionsPopover } from './hover_actions_popover';
export interface CellActionField {
/**
* Field name.
* Example: 'host.name'
*/
name: string;
/**
* Field type.
* Example: 'keyword'
*/
type: string;
/**
* Field value.
* Example: 'My-Laptop'
*/
value: string;
}
export interface CellActionExecutionContext extends ActionExecutionContext {
/**
* Ref to a DOM node where the action can add custom HTML.
*/
extraContentNodeRef: React.MutableRefObject<HTMLDivElement | null>;
/**
* Ref to the node where the cell action are rendered.
*/
nodeRef: React.MutableRefObject<HTMLDivElement | null>;
/**
* Extra configurations for actions.
*/
metadata?: Record<string, unknown>;
field: CellActionField;
}
export enum CellActionsMode {
HOVER_POPOVER = 'hover-popover',
ALWAYS_VISIBLE = 'always-visible',
}
export interface CellActionsProps {
/**
* Common set of properties used by most actions.
*/
field: CellActionField;
/**
* The trigger in which the actions are registered.
*/
triggerId: string;
/**
* UI configuration. Possible options are `HOVER_POPOVER` and `ALWAYS_VISIBLE`.
*
* `HOVER_POPOVER` shows the actions when the children component is hovered.
*
* `ALWAYS_VISIBLE` always shows the actions.
*/
mode: CellActionsMode;
/**
* It displays a tooltip for every action button when `true`.
*/
showActionTooltips?: boolean;
/**
* It shows 'more actions' button when the number of actions is bigger than this parameter.
*/
visibleCellActions?: number;
/**
* Custom set of properties used by some actions.
* An action might require a specific set of metadata properties to render.
* This data is sent directly to actions.
*/
metadata?: Record<string, unknown>;
}
export const CellActions: React.FC<CellActionsProps> = ({
field,
triggerId,
children,
mode,
showActionTooltips = true,
visibleCellActions = 3,
metadata,
}) => {
const extraContentNodeRef = useRef<HTMLDivElement | null>(null);
const nodeRef = useRef<HTMLDivElement | null>(null);
const actionContext: CellActionExecutionContext = useMemo(
() => ({
field,
trigger: { id: triggerId },
extraContentNodeRef,
nodeRef,
metadata,
}),
[field, triggerId, metadata]
);
if (mode === CellActionsMode.HOVER_POPOVER) {
return (
<div ref={nodeRef} data-test-subj={'cellActions'}>
<HoverActionsPopover
actionContext={actionContext}
showActionTooltips={showActionTooltips}
visibleCellActions={visibleCellActions}
>
{children}
</HoverActionsPopover>
<div ref={extraContentNodeRef} />
</div>
);
}
return (
<div ref={nodeRef} data-test-subj={'cellActions'}>
{children}
<InlineActions
actionContext={actionContext}
showActionTooltips={showActionTooltips}
visibleCellActions={visibleCellActions}
/>
<div ref={extraContentNodeRef} />
</div>
);
};

View file

@ -1,155 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { act, renderHook } from '@testing-library/react-hooks';
import React from 'react';
import { makeAction } from '../mocks/helpers';
import { CellActionExecutionContext } from './cell_actions';
import {
CellActionsContextProvider,
useLoadActions,
useLoadActionsFn,
} from './cell_actions_context';
describe('CellActionsContextProvider', () => {
const actionContext = { trigger: { id: 'triggerId' } } as CellActionExecutionContext;
it('loads actions when useLoadActionsFn callback is called', async () => {
const action = makeAction('action-1', 'icon', 1);
const getActionsPromise = Promise.resolve([action]);
const getActions = () => getActionsPromise;
const { result } = renderHook(
() => useLoadActionsFn(),
{
wrapper: ({ children }) => (
<CellActionsContextProvider getTriggerCompatibleActions={getActions}>
{children}
</CellActionsContextProvider>
),
}
);
const [{ value: valueBeforeFnCalled }, loadActions] = result.current;
// value is undefined before loadActions is called
expect(valueBeforeFnCalled).toBeUndefined();
await act(async () => {
loadActions(actionContext);
await getActionsPromise;
});
const [{ value: valueAfterFnCalled }] = result.current;
expect(valueAfterFnCalled).toEqual([action]);
});
it('loads actions when useLoadActions called', async () => {
const action = makeAction('action-1', 'icon', 1);
const getActionsPromise = Promise.resolve([action]);
const getActions = () => getActionsPromise;
const { result } = renderHook(
() => useLoadActions(actionContext),
{
wrapper: ({ children }) => (
<CellActionsContextProvider getTriggerCompatibleActions={getActions}>
{children}
</CellActionsContextProvider>
),
}
);
await act(async () => {
await getActionsPromise;
});
expect(result.current.value).toEqual([action]);
});
it('sorts actions by order', async () => {
const firstAction = makeAction('action-1', 'icon', 1);
const secondAction = makeAction('action-2', 'icon', 2);
const getActionsPromise = Promise.resolve([secondAction, firstAction]);
const getActions = () => getActionsPromise;
const { result } = renderHook(
() => useLoadActions(actionContext),
{
wrapper: ({ children }) => (
<CellActionsContextProvider getTriggerCompatibleActions={getActions}>
{children}
</CellActionsContextProvider>
),
}
);
await act(async () => {
await getActionsPromise;
});
expect(result.current.value).toEqual([firstAction, secondAction]);
});
it('sorts actions by id when order is undefined', async () => {
const firstAction = makeAction('action-1');
const secondAction = makeAction('action-2');
const getActionsPromise = Promise.resolve([secondAction, firstAction]);
const getActions = () => getActionsPromise;
const { result } = renderHook(
() => useLoadActions(actionContext),
{
wrapper: ({ children }) => (
<CellActionsContextProvider getTriggerCompatibleActions={getActions}>
{children}
</CellActionsContextProvider>
),
}
);
await act(async () => {
await getActionsPromise;
});
expect(result.current.value).toEqual([firstAction, secondAction]);
});
it('sorts actions by id and order', async () => {
const actionWithoutOrder = makeAction('action-1-no-order');
const secondAction = makeAction('action-2', 'icon', 2);
const thirdAction = makeAction('action-3', 'icon', 3);
const getActionsPromise = Promise.resolve([secondAction, actionWithoutOrder, thirdAction]);
const getActions = () => getActionsPromise;
const { result } = renderHook(
() => useLoadActions(actionContext),
{
wrapper: ({ children }) => (
<CellActionsContextProvider getTriggerCompatibleActions={getActions}>
{children}
</CellActionsContextProvider>
),
}
);
await act(async () => {
await getActionsPromise;
});
expect(result.current.value).toEqual([secondAction, thirdAction, actionWithoutOrder]);
});
});

View file

@ -1,69 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { orderBy } from 'lodash/fp';
import React, { createContext, FC, useCallback, useContext } from 'react';
import useAsync from 'react-use/lib/useAsync';
import useAsyncFn from 'react-use/lib/useAsyncFn';
import type { Action } from '../../actions';
import { CellActionExecutionContext } from './cell_actions';
// It must to match `UiActionsService.getTriggerCompatibleActions`
type GetTriggerCompatibleActionsType = (triggerId: string, context: object) => Promise<Action[]>;
type GetActionsType = (context: CellActionExecutionContext) => Promise<Action[]>;
const CellActionsContext = createContext<{ getActions: GetActionsType } | null>(null);
interface CellActionsContextProviderProps {
/**
* Please assign `uiActions.getTriggerCompatibleActions` function.
* This function should return a list of actions for a triggerId that are compatible with the provided context.
*/
getTriggerCompatibleActions: GetTriggerCompatibleActionsType;
}
export const CellActionsContextProvider: FC<CellActionsContextProviderProps> = ({
children,
getTriggerCompatibleActions,
}) => {
const getSortedCompatibleActions = useCallback<GetActionsType>(
(context) =>
getTriggerCompatibleActions(context.trigger.id, context).then((actions) =>
orderBy(['order', 'id'], ['asc', 'asc'], actions)
),
[getTriggerCompatibleActions]
);
return (
<CellActionsContext.Provider value={{ getActions: getSortedCompatibleActions }}>
{children}
</CellActionsContext.Provider>
);
};
const useCellActions = () => {
const context = useContext(CellActionsContext);
if (!context) {
throw new Error(
'No CellActionsContext found. Please wrap the application with CellActionsContextProvider'
);
}
return context;
};
export const useLoadActions = (context: CellActionExecutionContext) => {
const { getActions } = useCellActions();
return useAsync(() => getActions(context), []);
};
export const useLoadActionsFn = () => {
const { getActions } = useCellActions();
return useAsyncFn(getActions, []);
};

View file

@ -1,32 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { fireEvent, render } from '@testing-library/react';
import React from 'react';
import { ExtraActionsButton } from './extra_actions_button';
describe('ExtraActionsButton', () => {
it('renders', () => {
const { queryByTestId } = render(<ExtraActionsButton onClick={() => {}} showTooltip={false} />);
expect(queryByTestId('showExtraActionsButton')).toBeInTheDocument();
});
it('renders tooltip when showTooltip=true is received', () => {
const { container } = render(<ExtraActionsButton onClick={() => {}} showTooltip />);
expect(container.querySelector('.euiToolTipAnchor')).not.toBeNull();
});
it('calls onClick when button is clicked', () => {
const onClick = jest.fn();
const { getByTestId } = render(<ExtraActionsButton onClick={onClick} showTooltip />);
fireEvent.click(getByTestId('showExtraActionsButton'));
expect(onClick).toHaveBeenCalled();
});
});

View file

@ -1,35 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { EuiButtonIcon, EuiToolTip } from '@elastic/eui';
import React from 'react';
import { SHOW_MORE_ACTIONS } from './translations';
interface ExtraActionsButtonProps {
onClick: () => void;
showTooltip: boolean;
}
export const ExtraActionsButton: React.FC<ExtraActionsButtonProps> = ({ onClick, showTooltip }) =>
showTooltip ? (
<EuiToolTip content={SHOW_MORE_ACTIONS}>
<EuiButtonIcon
data-test-subj="showExtraActionsButton"
aria-label={SHOW_MORE_ACTIONS}
iconType="boxesHorizontal"
onClick={onClick}
/>
</EuiToolTip>
) : (
<EuiButtonIcon
data-test-subj="showExtraActionsButton"
aria-label={SHOW_MORE_ACTIONS}
iconType="boxesHorizontal"
onClick={onClick}
/>
);

View file

@ -1,91 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { act, fireEvent, render } from '@testing-library/react';
import React from 'react';
import { CellActionExecutionContext } from './cell_actions';
import { makeAction } from '../mocks/helpers';
import { ExtraActionsPopOver, ExtraActionsPopOverWithAnchor } from './extra_actions_popover';
const actionContext = { field: { name: 'fieldName' } } as CellActionExecutionContext;
describe('ExtraActionsPopOver', () => {
it('renders', () => {
const { queryByTestId } = render(
<ExtraActionsPopOver
actionContext={actionContext}
isOpen={false}
closePopOver={() => {}}
actions={[]}
button={<span />}
/>
);
expect(queryByTestId('extraActionsPopOver')).toBeInTheDocument();
});
it('executes action and close popover when menu item is clicked', async () => {
const executeAction = jest.fn();
const closePopOver = jest.fn();
const action = { ...makeAction('test-action'), execute: executeAction };
const { getByLabelText } = render(
<ExtraActionsPopOver
actionContext={actionContext}
isOpen={true}
closePopOver={closePopOver}
actions={[action]}
button={<span />}
/>
);
await act(async () => {
await fireEvent.click(getByLabelText('test-action'));
});
expect(executeAction).toHaveBeenCalled();
expect(closePopOver).toHaveBeenCalled();
});
});
describe('ExtraActionsPopOverWithAnchor', () => {
const anchorElement = document.createElement('span');
document.body.appendChild(anchorElement);
it('renders', () => {
const { queryByTestId } = render(
<ExtraActionsPopOverWithAnchor
actionContext={actionContext}
isOpen={false}
closePopOver={() => {}}
actions={[]}
anchorRef={{ current: anchorElement }}
/>
);
expect(queryByTestId('extraActionsPopOverWithAnchor')).toBeInTheDocument();
});
it('executes action and close popover when menu item is clicked', () => {
const executeAction = jest.fn();
const closePopOver = jest.fn();
const action = { ...makeAction('test-action'), execute: executeAction };
const { getByLabelText } = render(
<ExtraActionsPopOverWithAnchor
actionContext={actionContext}
isOpen={true}
closePopOver={closePopOver}
actions={[action]}
anchorRef={{ current: anchorElement }}
/>
);
fireEvent.click(getByLabelText('test-action'));
expect(executeAction).toHaveBeenCalled();
expect(closePopOver).toHaveBeenCalled();
});
});

View file

@ -1,133 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import {
EuiContextMenuItem,
EuiContextMenuPanel,
EuiPopover,
EuiScreenReaderOnly,
EuiWrappingPopover,
} from '@elastic/eui';
import React, { useMemo } from 'react';
import { euiThemeVars } from '@kbn/ui-theme';
import { css } from '@emotion/react';
import type { Action } from '../../actions';
import { EXTRA_ACTIONS_ARIA_LABEL, YOU_ARE_IN_A_DIALOG_CONTAINING_OPTIONS } from './translations';
import { CellActionExecutionContext } from './cell_actions';
const euiContextMenuItemCSS = css`
color: ${euiThemeVars.euiColorPrimaryText};
`;
interface ActionsPopOverProps {
actionContext: CellActionExecutionContext;
isOpen: boolean;
closePopOver: () => void;
actions: Action[];
button: JSX.Element;
}
export const ExtraActionsPopOver: React.FC<ActionsPopOverProps> = ({
actions,
actionContext,
isOpen,
closePopOver,
button,
}) => (
<EuiPopover
button={button}
isOpen={isOpen}
closePopover={closePopOver}
panelPaddingSize="xs"
anchorPosition={'downCenter'}
hasArrow
repositionOnScroll
ownFocus
data-test-subj="extraActionsPopOver"
aria-label={EXTRA_ACTIONS_ARIA_LABEL}
>
<ExtraActionsPopOverContent
actions={actions}
actionContext={actionContext}
closePopOver={closePopOver}
/>
</EuiPopover>
);
interface ExtraActionsPopOverWithAnchorProps
extends Pick<ActionsPopOverProps, 'actionContext' | 'closePopOver' | 'isOpen' | 'actions'> {
anchorRef: React.RefObject<HTMLElement>;
}
export const ExtraActionsPopOverWithAnchor = ({
anchorRef,
actionContext,
isOpen,
closePopOver,
actions,
}: ExtraActionsPopOverWithAnchorProps) => {
return anchorRef.current ? (
<EuiWrappingPopover
aria-label={EXTRA_ACTIONS_ARIA_LABEL}
button={anchorRef.current}
isOpen={isOpen}
closePopover={closePopOver}
panelPaddingSize="xs"
anchorPosition={'downCenter'}
hasArrow={false}
repositionOnScroll
ownFocus
attachToAnchor={false}
data-test-subj="extraActionsPopOverWithAnchor"
>
<ExtraActionsPopOverContent
actions={actions}
actionContext={actionContext}
closePopOver={closePopOver}
/>
</EuiWrappingPopover>
) : null;
};
type ExtraActionsPopOverContentProps = Pick<
ActionsPopOverProps,
'actionContext' | 'closePopOver' | 'actions'
>;
const ExtraActionsPopOverContent: React.FC<ExtraActionsPopOverContentProps> = ({
actionContext,
actions,
closePopOver,
}) => {
const items = useMemo(
() =>
actions.map((action) => (
<EuiContextMenuItem
css={euiContextMenuItemCSS}
key={action.id}
icon={action.getIconType(actionContext)}
aria-label={action.getDisplayName(actionContext)}
onClick={() => {
closePopOver();
action.execute(actionContext);
}}
>
{action.getDisplayName(actionContext)}
</EuiContextMenuItem>
)),
[actionContext, actions, closePopOver]
);
return (
<>
<EuiScreenReaderOnly>
<p>{YOU_ARE_IN_A_DIALOG_CONTAINING_OPTIONS(actionContext.field.name)}</p>
</EuiScreenReaderOnly>
<EuiContextMenuPanel size="s" items={items} />
</>
);
};

View file

@ -1,195 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { act, fireEvent, render } from '@testing-library/react';
import React from 'react';
import { CellActionExecutionContext } from './cell_actions';
import { makeAction } from '../mocks/helpers';
import { HoverActionsPopover } from './hover_actions_popover';
import { CellActionsContextProvider } from './cell_actions_context';
describe('HoverActionsPopover', () => {
const actionContext = {
trigger: { id: 'triggerId' },
field: { name: 'fieldName' },
} as CellActionExecutionContext;
const TestComponent = () => <span data-test-subj="test-component" />;
jest.useFakeTimers();
it('renders', () => {
const getActions = () => Promise.resolve([]);
const { queryByTestId } = render(
<CellActionsContextProvider getTriggerCompatibleActions={getActions}>
<HoverActionsPopover
children={null}
visibleCellActions={4}
actionContext={actionContext}
showActionTooltips={false}
/>
</CellActionsContextProvider>
);
expect(queryByTestId('hoverActionsPopover')).toBeInTheDocument();
});
it('renders actions when hovered', async () => {
const action = makeAction('test-action');
const getActionsPromise = Promise.resolve([action]);
const getActions = () => getActionsPromise;
const { queryByLabelText, getByTestId } = render(
<CellActionsContextProvider getTriggerCompatibleActions={getActions}>
<HoverActionsPopover
visibleCellActions={4}
actionContext={actionContext}
showActionTooltips={false}
>
<TestComponent />
</HoverActionsPopover>
</CellActionsContextProvider>
);
await hoverElement(getByTestId('test-component'), async () => {
await getActionsPromise;
jest.runAllTimers();
});
expect(queryByLabelText('test-action')).toBeInTheDocument();
});
it('hide actions when mouse stops hovering', async () => {
const action = makeAction('test-action');
const getActionsPromise = Promise.resolve([action]);
const getActions = () => getActionsPromise;
const { queryByLabelText, getByTestId } = render(
<CellActionsContextProvider getTriggerCompatibleActions={getActions}>
<HoverActionsPopover
visibleCellActions={4}
actionContext={actionContext}
showActionTooltips={false}
>
<TestComponent />
</HoverActionsPopover>
</CellActionsContextProvider>
);
await hoverElement(getByTestId('test-component'), async () => {
await getActionsPromise;
jest.runAllTimers();
});
// Mouse leaves hover state
await act(async () => {
fireEvent.mouseLeave(getByTestId('test-component'));
});
expect(queryByLabelText('test-action')).not.toBeInTheDocument();
});
it('renders extra actions button', async () => {
const actions = [makeAction('test-action-1'), makeAction('test-action-2')];
const getActionsPromise = Promise.resolve(actions);
const getActions = () => getActionsPromise;
const { getByTestId } = render(
<CellActionsContextProvider getTriggerCompatibleActions={getActions}>
<HoverActionsPopover
visibleCellActions={1}
actionContext={actionContext}
showActionTooltips={false}
>
<TestComponent />
</HoverActionsPopover>
</CellActionsContextProvider>
);
await hoverElement(getByTestId('test-component'), async () => {
await getActionsPromise;
jest.runAllTimers();
});
expect(getByTestId('showExtraActionsButton')).toBeInTheDocument();
});
it('shows extra actions when extra actions button is clicked', async () => {
const actions = [makeAction('test-action-1'), makeAction('test-action-2')];
const getActionsPromise = Promise.resolve(actions);
const getActions = () => getActionsPromise;
const { getByTestId, getByLabelText } = render(
<CellActionsContextProvider getTriggerCompatibleActions={getActions}>
<HoverActionsPopover
visibleCellActions={1}
actionContext={actionContext}
showActionTooltips={false}
>
<TestComponent />
</HoverActionsPopover>
</CellActionsContextProvider>
);
await hoverElement(getByTestId('test-component'), async () => {
await getActionsPromise;
jest.runAllTimers();
});
act(() => {
fireEvent.click(getByTestId('showExtraActionsButton'));
});
expect(getByLabelText('test-action-2')).toBeInTheDocument();
});
it('does not render visible actions if extra actions are already rendered', async () => {
const actions = [
makeAction('test-action-1'),
// extra actions
makeAction('test-action-2'),
makeAction('test-action-3'),
];
const getActionsPromise = Promise.resolve(actions);
const getActions = () => getActionsPromise;
const { getByTestId, queryByLabelText } = render(
<CellActionsContextProvider getTriggerCompatibleActions={getActions}>
<HoverActionsPopover
visibleCellActions={2}
actionContext={actionContext}
showActionTooltips={false}
>
<TestComponent />
</HoverActionsPopover>
</CellActionsContextProvider>
);
await hoverElement(getByTestId('test-component'), async () => {
await getActionsPromise;
jest.runAllTimers();
});
act(() => {
fireEvent.click(getByTestId('showExtraActionsButton'));
});
await hoverElement(getByTestId('test-component'), async () => {
await getActionsPromise;
jest.runAllTimers();
});
expect(queryByLabelText('test-action-1')).not.toBeInTheDocument();
expect(queryByLabelText('test-action-2')).toBeInTheDocument();
expect(queryByLabelText('test-action-3')).toBeInTheDocument();
});
});
const hoverElement = async (element: Element, waitForChange: () => Promise<unknown>) => {
await act(async () => {
fireEvent.mouseEnter(element);
await waitForChange();
});
};

View file

@ -1,168 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { EuiPopover, EuiScreenReaderOnly } from '@elastic/eui';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { euiThemeVars } from '@kbn/ui-theme';
import { css } from '@emotion/react';
import { debounce } from 'lodash';
import { ActionItem } from './cell_action_item';
import { ExtraActionsButton } from './extra_actions_button';
import { ACTIONS_AREA_LABEL, YOU_ARE_IN_A_DIALOG_CONTAINING_OPTIONS } from './translations';
import { partitionActions } from '../hooks/actions';
import { ExtraActionsPopOverWithAnchor } from './extra_actions_popover';
import { CellActionExecutionContext } from './cell_actions';
import { useLoadActionsFn } from './cell_actions_context';
/** This class is added to the document body while dragging */
export const IS_DRAGGING_CLASS_NAME = 'is-dragging';
// Overwrite Popover default minWidth to avoid displaying empty space
const PANEL_STYLE = { minWidth: `24px` };
const hoverContentWrapperCSS = css`
padding: 0 ${euiThemeVars.euiSizeS};
`;
/**
* To avoid expensive changes to the DOM, delay showing the popover menu
*/
const HOVER_INTENT_DELAY = 100; // ms
interface Props {
children: React.ReactNode;
visibleCellActions: number;
actionContext: CellActionExecutionContext;
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]);
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}
/>
</>
);
}
);

View file

@ -1,10 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
export { CellActions, CellActionsMode } from './cell_actions';
export { CellActionsContextProvider } from './cell_actions_context';

View file

@ -1,63 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { act, render } from '@testing-library/react';
import React from 'react';
import { CellActionExecutionContext } from './cell_actions';
import { makeAction } from '../mocks/helpers';
import { InlineActions } from './inline_actions';
import { CellActionsContextProvider } from '.';
describe('InlineActions', () => {
const actionContext = { trigger: { id: 'triggerId' } } as CellActionExecutionContext;
it('renders', async () => {
const getActionsPromise = Promise.resolve([]);
const getActions = () => getActionsPromise;
const { queryByTestId } = render(
<CellActionsContextProvider getTriggerCompatibleActions={getActions}>
<InlineActions
visibleCellActions={5}
actionContext={actionContext}
showActionTooltips={false}
/>
</CellActionsContextProvider>
);
await act(async () => {
await getActionsPromise;
});
expect(queryByTestId('inlineActions')).toBeInTheDocument();
});
it('renders all actions', async () => {
const getActionsPromise = Promise.resolve([
makeAction('action-1'),
makeAction('action-2'),
makeAction('action-3'),
makeAction('action-4'),
makeAction('action-5'),
]);
const getActions = () => getActionsPromise;
const { queryAllByRole } = render(
<CellActionsContextProvider getTriggerCompatibleActions={getActions}>
<InlineActions
visibleCellActions={5}
actionContext={actionContext}
showActionTooltips={false}
/>
</CellActionsContextProvider>
);
await act(async () => {
await getActionsPromise;
});
expect(queryAllByRole('button').length).toBe(5);
});
});

View file

@ -1,62 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import React, { useCallback, 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 { CellActionExecutionContext } from './cell_actions';
import { useLoadActions } from './cell_actions_context';
interface InlineActionsProps {
actionContext: CellActionExecutionContext;
showActionTooltips: boolean;
visibleCellActions: number;
}
export const InlineActions: React.FC<InlineActionsProps> = ({
actionContext,
showActionTooltips,
visibleCellActions,
}) => {
const { value: allActions } = useLoadActions(actionContext);
const { extraActions, visibleActions } = usePartitionActions(
allActions ?? [],
visibleCellActions
);
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const togglePopOver = useCallback(() => setIsPopoverOpen((isOpen) => !isOpen), []);
const closePopOver = useCallback(() => setIsPopoverOpen(false), []);
const button = useMemo(
() => <ExtraActionsButton onClick={togglePopOver} showTooltip={showActionTooltips} />,
[togglePopOver, showActionTooltips]
);
return (
<span data-test-subj="inlineActions">
{visibleActions.map((action, index) => (
<ActionItem
key={`action-item-${index}`}
action={action}
actionContext={actionContext}
showTooltip={showActionTooltips}
/>
))}
{extraActions.length > 0 ? (
<ExtraActionsPopOver
actions={extraActions}
actionContext={actionContext}
button={button}
closePopOver={closePopOver}
isOpen={isPopoverOpen}
/>
) : null}
</span>
);
};

View file

@ -1,29 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { i18n } from '@kbn/i18n';
export const YOU_ARE_IN_A_DIALOG_CONTAINING_OPTIONS = (fieldName: string) =>
i18n.translate('uiActions.cellActions.youAreInADialogContainingOptionsScreenReaderOnly', {
values: { fieldName },
defaultMessage: `You are in a dialog, containing options for field {fieldName}. Press tab to navigate options. Press escape to exit.`,
});
export const EXTRA_ACTIONS_ARIA_LABEL = i18n.translate(
'uiActions.cellActions.extraActionsAriaLabel',
{
defaultMessage: 'Extra actions',
}
);
export const SHOW_MORE_ACTIONS = i18n.translate('uiActions.showMoreActionsLabel', {
defaultMessage: 'More actions',
});
export const ACTIONS_AREA_LABEL = i18n.translate('uiActions.cellActions.actionsAriaLabel', {
defaultMessage: 'Actions',
});

View file

@ -1,85 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { makeAction } from '../mocks/helpers';
import { partitionActions } from './actions';
describe('InlineActions', () => {
it('returns an empty array when actions is an empty array', async () => {
const { extraActions, visibleActions } = partitionActions([], 5);
expect(visibleActions).toEqual([]);
expect(extraActions).toEqual([]);
});
it('returns only visible actions when visibleCellActions > actions.length', async () => {
const actions = [makeAction('action-1'), makeAction('action-2'), makeAction('action-3')];
const { extraActions, visibleActions } = partitionActions(actions, 4);
expect(visibleActions.length).toEqual(actions.length);
expect(extraActions).toEqual([]);
});
it('returns only extra actions when visibleCellActions is 1', async () => {
const actions = [makeAction('action-1'), makeAction('action-2'), makeAction('action-3')];
const { extraActions, visibleActions } = partitionActions(actions, 1);
expect(visibleActions).toEqual([]);
expect(extraActions.length).toEqual(actions.length);
});
it('returns only extra actions when visibleCellActions is 0', async () => {
const actions = [makeAction('action-1'), makeAction('action-2'), makeAction('action-3')];
const { extraActions, visibleActions } = partitionActions(actions, 0);
expect(visibleActions).toEqual([]);
expect(extraActions.length).toEqual(actions.length);
});
it('returns only extra actions when visibleCellActions is negative', async () => {
const actions = [makeAction('action-1'), makeAction('action-2'), makeAction('action-3')];
const { extraActions, visibleActions } = partitionActions(actions, -6);
expect(visibleActions).toEqual([]);
expect(extraActions.length).toEqual(actions.length);
});
it('returns only one visible action when visibleCellActionss 2 and action.length is 3', async () => {
const { extraActions, visibleActions } = partitionActions(
[makeAction('action-1'), makeAction('action-2'), makeAction('action-3')],
2
);
expect(visibleActions.length).toEqual(1);
expect(extraActions.length).toEqual(2);
});
it('returns two visible actions when visibleCellActions is 3 and action.length is 5', async () => {
const { extraActions, visibleActions } = partitionActions(
[
makeAction('action-1'),
makeAction('action-2'),
makeAction('action-3'),
makeAction('action-4'),
makeAction('action-5'),
],
3
);
expect(visibleActions.length).toEqual(2);
expect(extraActions.length).toEqual(3);
});
it('returns three visible actions when visibleCellActions is 3 and action.length is 3', async () => {
const { extraActions, visibleActions } = partitionActions(
[makeAction('action-1'), makeAction('action-2'), makeAction('action-3')],
3
);
expect(visibleActions.length).toEqual(3);
expect(extraActions.length).toEqual(0);
});
});

View file

@ -1,34 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { useMemo } from 'react';
import { Action } from '../../actions';
export const partitionActions = (actions: Action[], visibleCellActions: number) => {
if (visibleCellActions <= 1) return { extraActions: actions, visibleActions: [] };
if (actions.length <= visibleCellActions) return { extraActions: [], visibleActions: actions };
return {
visibleActions: actions.slice(0, visibleCellActions - 1),
extraActions: actions.slice(visibleCellActions - 1, actions.length),
};
};
export interface PartitionedActions {
extraActions: Array<Action<object>>;
visibleActions: Array<Action<object>>;
}
export const usePartitionActions = (
allActions: Action[],
visibleCellActions: number
): PartitionedActions => {
return useMemo(() => {
return partitionActions(allActions ?? [], visibleCellActions);
}, [allActions, visibleCellActions]);
};

View file

@ -1,21 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
export const makeAction = (actionsName: string, icon: string = 'icon', order?: number) => ({
id: actionsName,
type: actionsName,
order,
getIconType: () => icon,
getDisplayName: () => actionsName,
getDisplayNameTooltip: () => actionsName,
isCompatible: () => Promise.resolve(true),
execute: () => {
alert(actionsName);
return Promise.resolve();
},
});

View file

@ -39,10 +39,3 @@ export {
ACTION_VISUALIZE_LENS_FIELD,
} from './types';
export type { ActionExecutionContext, ActionExecutionMeta, ActionMenuItemProps } from './actions';
export {
CellActions,
CellActionsMode,
CellActionsContextProvider,
} from './cell_actions/components';
export type { CellActionExecutionContext } from './cell_actions/components/cell_actions';

View file

@ -13,7 +13,6 @@
"@kbn/utility-types",
"@kbn/i18n",
"@kbn/es-query",
"@kbn/ui-theme",
"@kbn/ui-actions-browser",
],
"exclude": [