mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Search Sessions] Fixes management actions not accessible (#105940)
This commit is contained in:
parent
1c4818e0db
commit
00d8f05f2e
8 changed files with 130 additions and 249 deletions
|
@ -12,20 +12,18 @@ import React, { useState } from 'react';
|
|||
import { CoreStart } from 'kibana/public';
|
||||
import { toMountPoint } from '../../../../../../../../src/plugins/kibana_react/public';
|
||||
import { SearchSessionsMgmtAPI } from '../../lib/api';
|
||||
import { TableText } from '../';
|
||||
import { OnActionClick, OnActionComplete, OnActionDismiss } from './types';
|
||||
import { IClickActionDescriptor } from '../';
|
||||
import { OnActionDismiss } from './types';
|
||||
import { UISession } from '../../types';
|
||||
|
||||
interface DeleteButtonProps {
|
||||
id: string;
|
||||
name: string;
|
||||
api: SearchSessionsMgmtAPI;
|
||||
onActionComplete: OnActionComplete;
|
||||
overlays: CoreStart['overlays'];
|
||||
onActionClick: OnActionClick;
|
||||
searchSession: UISession;
|
||||
}
|
||||
|
||||
const DeleteConfirm = (props: DeleteButtonProps & { onActionDismiss: OnActionDismiss }) => {
|
||||
const { id, name, api, onActionComplete, onActionDismiss } = props;
|
||||
const { searchSession, api, onActionDismiss } = props;
|
||||
const { name, id } = searchSession;
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const title = i18n.translate('xpack.data.mgmt.searchSessions.cancelModal.title', {
|
||||
|
@ -51,7 +49,6 @@ const DeleteConfirm = (props: DeleteButtonProps & { onActionDismiss: OnActionDis
|
|||
onConfirm={async () => {
|
||||
setIsLoading(true);
|
||||
await api.sendCancel(id);
|
||||
onActionComplete();
|
||||
onActionDismiss();
|
||||
}}
|
||||
confirmButtonText={confirm}
|
||||
|
@ -65,24 +62,21 @@ const DeleteConfirm = (props: DeleteButtonProps & { onActionDismiss: OnActionDis
|
|||
);
|
||||
};
|
||||
|
||||
export const DeleteButton = (props: DeleteButtonProps) => {
|
||||
const { overlays, onActionClick } = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableText
|
||||
onClick={() => {
|
||||
onActionClick();
|
||||
const ref = overlays.openModal(
|
||||
toMountPoint(<DeleteConfirm onActionDismiss={() => ref?.close()} {...props} />)
|
||||
);
|
||||
}}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.data.mgmt.searchSessions.actionDelete"
|
||||
defaultMessage="Delete"
|
||||
/>
|
||||
</TableText>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export const createDeleteActionDescriptor = (
|
||||
api: SearchSessionsMgmtAPI,
|
||||
uiSession: UISession,
|
||||
core: CoreStart
|
||||
): IClickActionDescriptor => ({
|
||||
iconType: 'crossInACircleFilled',
|
||||
label: (
|
||||
<FormattedMessage id="xpack.data.mgmt.searchSessions.actionDelete" defaultMessage="Delete" />
|
||||
),
|
||||
onClick: async () => {
|
||||
const ref = core.overlays.openModal(
|
||||
toMountPoint(
|
||||
<DeleteConfirm onActionDismiss={() => ref?.close()} searchSession={uiSession} api={api} />
|
||||
)
|
||||
);
|
||||
await ref.onClose;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -9,29 +9,25 @@ import { EuiConfirmModal } from '@elastic/eui';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React, { useState } from 'react';
|
||||
import { Duration } from 'moment';
|
||||
import moment from 'moment';
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import { toMountPoint } from '../../../../../../../../src/plugins/kibana_react/public';
|
||||
import { SearchSessionsMgmtAPI } from '../../lib/api';
|
||||
import { TableText } from '../';
|
||||
import { OnActionClick, OnActionComplete, OnActionDismiss } from './types';
|
||||
import { IClickActionDescriptor } from '../';
|
||||
import { OnActionDismiss } from './types';
|
||||
import { UISession } from '../../types';
|
||||
import extendSessionIcon from '../../icons/extend_session.svg';
|
||||
|
||||
interface ExtendButtonProps {
|
||||
id: string;
|
||||
name: string;
|
||||
expires: string | null;
|
||||
extendBy: Duration;
|
||||
searchSession: UISession;
|
||||
api: SearchSessionsMgmtAPI;
|
||||
overlays: CoreStart['overlays'];
|
||||
onActionClick: OnActionClick;
|
||||
onActionComplete: OnActionComplete;
|
||||
}
|
||||
|
||||
const ExtendConfirm = ({ ...props }: ExtendButtonProps & { onActionDismiss: OnActionDismiss }) => {
|
||||
const { id, name, expires, api, extendBy, onActionComplete, onActionDismiss } = props;
|
||||
const { searchSession, api, onActionDismiss } = props;
|
||||
const { id, name, expires } = searchSession;
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const extendByDuration = moment.duration(extendBy);
|
||||
const extendByDuration = moment.duration(api.getExtendByDuration());
|
||||
|
||||
const newExpiration = moment(expires).add(extendByDuration);
|
||||
|
||||
|
@ -61,7 +57,6 @@ const ExtendConfirm = ({ ...props }: ExtendButtonProps & { onActionDismiss: OnAc
|
|||
await api.sendExtend(id, `${newExpiration.toISOString()}`);
|
||||
setIsLoading(false);
|
||||
onActionDismiss();
|
||||
onActionComplete();
|
||||
}}
|
||||
confirmButtonText={confirm}
|
||||
confirmButtonDisabled={isLoading}
|
||||
|
@ -74,24 +69,21 @@ const ExtendConfirm = ({ ...props }: ExtendButtonProps & { onActionDismiss: OnAc
|
|||
);
|
||||
};
|
||||
|
||||
export const ExtendButton = (props: ExtendButtonProps) => {
|
||||
const { overlays, onActionClick } = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableText
|
||||
onClick={() => {
|
||||
onActionClick();
|
||||
const ref = overlays.openModal(
|
||||
toMountPoint(<ExtendConfirm onActionDismiss={() => ref?.close()} {...props} />)
|
||||
);
|
||||
}}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.data.mgmt.searchSessions.actionExtend"
|
||||
defaultMessage="Extend"
|
||||
/>
|
||||
</TableText>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export const createExtendActionDescriptor = (
|
||||
api: SearchSessionsMgmtAPI,
|
||||
uiSession: UISession,
|
||||
core: CoreStart
|
||||
): IClickActionDescriptor => ({
|
||||
iconType: extendSessionIcon,
|
||||
label: (
|
||||
<FormattedMessage id="xpack.data.mgmt.searchSessions.actionExtend" defaultMessage="Extend" />
|
||||
),
|
||||
onClick: async () => {
|
||||
const ref = core.overlays.openModal(
|
||||
toMountPoint(
|
||||
<ExtendConfirm onActionDismiss={() => ref?.close()} searchSession={uiSession} api={api} />
|
||||
)
|
||||
);
|
||||
await ref.onClose;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -5,93 +5,31 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import { IClickActionDescriptor } from '../';
|
||||
import extendSessionIcon from '../../icons/extend_session.svg';
|
||||
import { SearchSessionsMgmtAPI } from '../../lib/api';
|
||||
import { UISession } from '../../types';
|
||||
import { DeleteButton } from './delete_button';
|
||||
import { ExtendButton } from './extend_button';
|
||||
import { InspectButton } from './inspect_button';
|
||||
import { ACTION, OnActionClick, OnActionComplete } from './types';
|
||||
import { RenameButton } from './rename_button';
|
||||
import { createDeleteActionDescriptor } from './delete_button';
|
||||
import { createExtendActionDescriptor } from './extend_button';
|
||||
import { createInspectActionDescriptor } from './inspect_button';
|
||||
import { ACTION } from './types';
|
||||
import { createRenameActionDescriptor } from './rename_button';
|
||||
|
||||
export const getAction = (
|
||||
api: SearchSessionsMgmtAPI,
|
||||
actionType: string,
|
||||
uiSession: UISession,
|
||||
core: CoreStart,
|
||||
onActionClick: OnActionClick,
|
||||
onActionComplete: OnActionComplete
|
||||
core: CoreStart
|
||||
): IClickActionDescriptor | null => {
|
||||
const { id, name, expires } = uiSession;
|
||||
const { overlays, uiSettings } = core;
|
||||
switch (actionType) {
|
||||
case ACTION.INSPECT:
|
||||
return {
|
||||
iconType: 'document',
|
||||
textColor: 'default',
|
||||
label: (
|
||||
<InspectButton
|
||||
overlays={overlays}
|
||||
searchSession={uiSession}
|
||||
uiSettings={uiSettings}
|
||||
onActionClick={onActionClick}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
return createInspectActionDescriptor(api, uiSession, core);
|
||||
case ACTION.DELETE:
|
||||
return {
|
||||
iconType: 'crossInACircleFilled',
|
||||
textColor: 'default',
|
||||
label: (
|
||||
<DeleteButton
|
||||
api={api}
|
||||
id={id}
|
||||
name={name}
|
||||
overlays={overlays}
|
||||
onActionComplete={onActionComplete}
|
||||
onActionClick={onActionClick}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
return createDeleteActionDescriptor(api, uiSession, core);
|
||||
case ACTION.EXTEND:
|
||||
return {
|
||||
iconType: extendSessionIcon,
|
||||
textColor: 'default',
|
||||
label: (
|
||||
<ExtendButton
|
||||
api={api}
|
||||
id={id}
|
||||
name={name}
|
||||
expires={expires}
|
||||
overlays={overlays}
|
||||
extendBy={api.getExtendByDuration()}
|
||||
onActionComplete={onActionComplete}
|
||||
onActionClick={onActionClick}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
return createExtendActionDescriptor(api, uiSession, core);
|
||||
case ACTION.RENAME:
|
||||
return {
|
||||
iconType: 'pencil',
|
||||
textColor: 'default',
|
||||
label: (
|
||||
<RenameButton
|
||||
api={api}
|
||||
id={id}
|
||||
name={name}
|
||||
overlays={overlays}
|
||||
onActionComplete={onActionComplete}
|
||||
onActionClick={onActionClick}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
return createRenameActionDescriptor(api, uiSession, core);
|
||||
default:
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`Unknown action: ${actionType}`);
|
||||
|
|
|
@ -10,20 +10,18 @@ import { FormattedMessage } from '@kbn/i18n/react';
|
|||
import React, { Fragment } from 'react';
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import { UISession } from '../../types';
|
||||
import { TableText } from '..';
|
||||
import { IClickActionDescriptor } from '..';
|
||||
import {
|
||||
CodeEditor,
|
||||
createKibanaReactContext,
|
||||
toMountPoint,
|
||||
} from '../../../../../../../../src/plugins/kibana_react/public';
|
||||
import './inspect_button.scss';
|
||||
import { OnActionClick } from './types';
|
||||
import { SearchSessionsMgmtAPI } from '../../lib/api';
|
||||
|
||||
interface InspectFlyoutProps {
|
||||
searchSession: UISession;
|
||||
overlays: CoreStart['overlays'];
|
||||
uiSettings: CoreStart['uiSettings'];
|
||||
onActionClick: OnActionClick;
|
||||
}
|
||||
|
||||
const InspectFlyout = ({ uiSettings, searchSession }: InspectFlyoutProps) => {
|
||||
|
@ -84,24 +82,23 @@ const InspectFlyout = ({ uiSettings, searchSession }: InspectFlyoutProps) => {
|
|||
</KibanaReactContextProvider>
|
||||
);
|
||||
};
|
||||
export const InspectButton = (props: InspectFlyoutProps) => {
|
||||
const { overlays, onActionClick } = props;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<TableText
|
||||
onClick={() => {
|
||||
onActionClick();
|
||||
const flyout = <InspectFlyout {...props} />;
|
||||
overlays.openFlyout(toMountPoint(flyout));
|
||||
}}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.data.mgmt.searchSessions.flyoutTitle"
|
||||
aria-label="Inspect"
|
||||
defaultMessage="Inspect"
|
||||
/>
|
||||
</TableText>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
export const createInspectActionDescriptor = (
|
||||
api: SearchSessionsMgmtAPI,
|
||||
uiSession: UISession,
|
||||
core: CoreStart
|
||||
): IClickActionDescriptor => ({
|
||||
iconType: 'document',
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id="xpack.data.mgmt.searchSessions.flyoutTitle"
|
||||
aria-label="Inspect"
|
||||
defaultMessage="Inspect"
|
||||
/>
|
||||
),
|
||||
onClick: async () => {
|
||||
const flyout = <InspectFlyout uiSettings={core.uiSettings} searchSession={uiSession} />;
|
||||
const overlay = core.overlays.openFlyout(toMountPoint(flyout));
|
||||
await overlay.onClose;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -9,11 +9,7 @@ import {
|
|||
EuiButtonIcon,
|
||||
EuiContextMenu,
|
||||
EuiContextMenuPanelDescriptor,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiIcon,
|
||||
EuiPopover,
|
||||
EuiTextProps,
|
||||
EuiToolTip,
|
||||
} from '@elastic/eui';
|
||||
import {
|
||||
|
@ -22,20 +18,12 @@ import {
|
|||
} from '@elastic/eui/src/components/context_menu/context_menu';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import React, { ReactElement, useState } from 'react';
|
||||
import { TableText } from '../';
|
||||
import React, { useState } from 'react';
|
||||
import { SearchSessionsMgmtAPI } from '../../lib/api';
|
||||
import { UISession } from '../../types';
|
||||
import { getAction } from './get_action';
|
||||
import { ACTION, OnActionComplete } from './types';
|
||||
|
||||
// interfaces
|
||||
interface PopoverActionProps {
|
||||
textColor?: EuiTextProps['color'];
|
||||
iconType: string;
|
||||
children: string | ReactElement;
|
||||
}
|
||||
|
||||
interface PopoverActionItemsProps {
|
||||
session: UISession;
|
||||
api: SearchSessionsMgmtAPI;
|
||||
|
@ -43,18 +31,6 @@ interface PopoverActionItemsProps {
|
|||
core: CoreStart;
|
||||
}
|
||||
|
||||
// helper
|
||||
const PopoverAction = ({ textColor, iconType, children, ...props }: PopoverActionProps) => (
|
||||
<EuiFlexGroup gutterSize="s" alignItems="center" component="span" {...props}>
|
||||
<EuiFlexItem grow={false} component="span">
|
||||
<EuiIcon color={textColor} type={iconType} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={true} component="span">
|
||||
<TableText color={textColor}>{children}</TableText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
|
||||
export const PopoverActionsMenu = ({
|
||||
api,
|
||||
onActionComplete,
|
||||
|
@ -71,10 +47,6 @@ export const PopoverActionsMenu = ({
|
|||
setPopover(false);
|
||||
};
|
||||
|
||||
const onActionClick = () => {
|
||||
closePopover();
|
||||
};
|
||||
|
||||
const renderPopoverButton = () => (
|
||||
<EuiToolTip
|
||||
content={i18n.translate('xpack.data.mgmt.searchSessions.actions.tooltip.moreActions', {
|
||||
|
@ -95,9 +67,9 @@ export const PopoverActionsMenu = ({
|
|||
const actions = session.actions || [];
|
||||
// Generic set of actions - up to the API to return what is available
|
||||
const items = actions.reduce((itemSet, actionType) => {
|
||||
const actionDef = getAction(api, actionType, session, core, onActionClick, onActionComplete);
|
||||
const actionDef = getAction(api, actionType, session, core);
|
||||
if (actionDef) {
|
||||
const { label, textColor, iconType } = actionDef;
|
||||
const { label, iconType, onClick } = actionDef;
|
||||
|
||||
// add a line above the delete action (when there are multiple)
|
||||
// NOTE: Delete action MUST be the final action[] item
|
||||
|
@ -109,16 +81,15 @@ export const PopoverActionsMenu = ({
|
|||
...itemSet,
|
||||
{
|
||||
key: `action-${actionType}`,
|
||||
name: (
|
||||
<PopoverAction
|
||||
textColor={textColor}
|
||||
iconType={iconType}
|
||||
data-test-subj={`sessionManagementPopoverAction-${actionType}`}
|
||||
>
|
||||
{label}
|
||||
</PopoverAction>
|
||||
),
|
||||
},
|
||||
name: label,
|
||||
icon: iconType,
|
||||
'data-test-subj': `sessionManagementPopoverAction-${actionType}`,
|
||||
onClick: async () => {
|
||||
closePopover();
|
||||
await onClick();
|
||||
onActionComplete();
|
||||
},
|
||||
} as EuiContextMenuPanelItemDescriptorEntry,
|
||||
];
|
||||
}
|
||||
return itemSet;
|
||||
|
@ -126,20 +97,16 @@ export const PopoverActionsMenu = ({
|
|||
|
||||
const panels: EuiContextMenuPanelDescriptor[] = [{ id: 0, items }];
|
||||
|
||||
return (
|
||||
<>
|
||||
{actions.length ? (
|
||||
<EuiPopover
|
||||
id={`popover-${session.id}`}
|
||||
button={renderPopoverButton()}
|
||||
isOpen={isPopoverOpen}
|
||||
closePopover={closePopover}
|
||||
anchorPosition="downLeft"
|
||||
panelPaddingSize={'s'}
|
||||
>
|
||||
<EuiContextMenu initialPanelId={0} panels={panels} />
|
||||
</EuiPopover>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
return actions.length ? (
|
||||
<EuiPopover
|
||||
id={`popover-${session.id}`}
|
||||
button={renderPopoverButton()}
|
||||
isOpen={isPopoverOpen}
|
||||
closePopover={closePopover}
|
||||
anchorPosition="downLeft"
|
||||
panelPaddingSize={'s'}
|
||||
>
|
||||
<EuiContextMenu initialPanelId={0} panels={panels} />
|
||||
</EuiPopover>
|
||||
) : null;
|
||||
};
|
||||
|
|
|
@ -22,24 +22,22 @@ import { FormattedMessage } from '@kbn/i18n/react';
|
|||
import React, { useState } from 'react';
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import { SearchSessionsMgmtAPI } from '../../lib/api';
|
||||
import { TableText } from '../';
|
||||
import { IClickActionDescriptor } from '../';
|
||||
import { toMountPoint } from '../../../../../../../../src/plugins/kibana_react/public';
|
||||
import { OnActionClick, OnActionComplete, OnActionDismiss } from './types';
|
||||
import { OnActionDismiss } from './types';
|
||||
import { UISession } from '../../types';
|
||||
|
||||
interface RenameButtonProps {
|
||||
id: string;
|
||||
name: string;
|
||||
searchSession: UISession;
|
||||
api: SearchSessionsMgmtAPI;
|
||||
overlays: CoreStart['overlays'];
|
||||
onActionComplete: OnActionComplete;
|
||||
onActionClick: OnActionClick;
|
||||
}
|
||||
|
||||
const RenameDialog = ({
|
||||
onActionDismiss,
|
||||
...props
|
||||
}: RenameButtonProps & { onActionDismiss: OnActionDismiss }) => {
|
||||
const { id, name: originalName, api, onActionComplete } = props;
|
||||
const { api, searchSession } = props;
|
||||
const { id, name: originalName } = searchSession;
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [newName, setNewName] = useState(originalName);
|
||||
|
||||
|
@ -92,7 +90,6 @@ const RenameDialog = ({
|
|||
await api.sendRename(id, newName);
|
||||
setIsLoading(false);
|
||||
onActionDismiss();
|
||||
onActionComplete();
|
||||
}}
|
||||
fill
|
||||
isLoading={isLoading}
|
||||
|
@ -104,24 +101,21 @@ const RenameDialog = ({
|
|||
);
|
||||
};
|
||||
|
||||
export const RenameButton = (props: RenameButtonProps) => {
|
||||
const { overlays, onActionClick } = props;
|
||||
|
||||
const onClick = () => {
|
||||
onActionClick();
|
||||
const ref = overlays.openModal(
|
||||
toMountPoint(<RenameDialog onActionDismiss={() => ref?.close()} {...props} />)
|
||||
export const createRenameActionDescriptor = (
|
||||
api: SearchSessionsMgmtAPI,
|
||||
uiSession: UISession,
|
||||
core: CoreStart
|
||||
): IClickActionDescriptor => ({
|
||||
iconType: 'pencil',
|
||||
label: (
|
||||
<FormattedMessage id="xpack.data.mgmt.searchSessions.actionRename" defaultMessage="Edit name" />
|
||||
),
|
||||
onClick: async () => {
|
||||
const ref = core.overlays.openModal(
|
||||
toMountPoint(
|
||||
<RenameDialog onActionDismiss={() => ref?.close()} api={api} searchSession={uiSession} />
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableText onClick={onClick}>
|
||||
<FormattedMessage
|
||||
id="xpack.data.mgmt.searchSessions.actionRename"
|
||||
defaultMessage="Edit name"
|
||||
/>
|
||||
</TableText>
|
||||
</>
|
||||
);
|
||||
};
|
||||
await ref.onClose;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
export type OnActionComplete = () => void;
|
||||
export type OnActionClick = () => void;
|
||||
export type OnActionDismiss = () => void;
|
||||
|
||||
export enum ACTION {
|
||||
|
|
|
@ -20,9 +20,9 @@ export const TableText = ({ children, ...props }: EuiTextProps) => {
|
|||
};
|
||||
|
||||
export interface IClickActionDescriptor {
|
||||
label: string | React.ReactElement;
|
||||
label: React.ReactNode;
|
||||
iconType: 'trash' | 'cancel' | typeof extendSessionIcon;
|
||||
textColor: EuiTextProps['color'];
|
||||
onClick: () => Promise<void> | void;
|
||||
}
|
||||
|
||||
export interface IHrefActionDescriptor {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue