[Alerting] "Create alert" and alert list design improvements (#63515)

* added header to actions section

* adjusted some spacing in Create Alert

* lighter text in Actions section headings

* fixed bulk actions dropdown

* improve alert collapsed item actions

* improve dropdown and adjust some buttos

* adjust font size of steps to match hierarchy

* need to check master

* improve collapsed actions menu

* added periods to help texts

* going back to EuiButtonEmpty to be able to use isLoading

* fix prop

* remove Fragment

* remove translations we're not using

* Fix functional tests

Co-authored-by: Mike Cote <mikecote@users.noreply.github.com>
This commit is contained in:
Andrea Del Rio 2020-04-17 12:15:04 -07:00 committed by GitHub
parent dc9bfb5d0f
commit c13a026b01
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 426 additions and 328 deletions

View file

@ -15960,7 +15960,7 @@
"xpack.triggersActionsUI.sections.alertAdd.saveButtonLabel": "保存",
"xpack.triggersActionsUI.sections.alertAdd.saveErrorNotificationText": "アラートを作成できません。",
"xpack.triggersActionsUI.sections.alertAdd.saveSuccessNotificationText": "「{alertName}」 を保存しました",
"xpack.triggersActionsUI.sections.alertAdd.selectIndex": "インデックスを選択してください",
"xpack.triggersActionsUI.sections.alertAdd.selectIndex": "インデックスを選択してください",
"xpack.triggersActionsUI.sections.alertAdd.threshold.closeIndexPopoverLabel": "閉じる",
"xpack.triggersActionsUI.sections.alertAdd.threshold.fixErrorInExpressionBelowValidationMessage": "下の表現のエラーを修正してください。",
"xpack.triggersActionsUI.sections.alertAdd.threshold.howToBroadenSearchQueryDescription": "* で検索クエリの範囲を広げます。",
@ -16044,7 +16044,6 @@
"xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.muteAllTitle": "ミュート",
"xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.unmuteAllTitle": "ミュート解除",
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.deleteTitle": "削除",
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.enableTitle": "有効にする",
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.muteTitle": "ミュート",
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.popoverButtonTitle": "アクション",
"xpack.triggersActionsUI.components.emptyPrompt.emptyButton": "アラートの作成",
@ -16757,4 +16756,4 @@
"xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "フィールドを選択してください。",
"xpack.watcher.watcherDescription": "アラートの作成、管理、監視によりデータへの変更を検知します。"
}
}
}

View file

@ -15957,7 +15957,7 @@
"xpack.triggersActionsUI.sections.addModalConnectorForm.updateSuccessNotificationText": "已创建“{connectorName}”",
"xpack.triggersActionsUI.sections.alertAdd.betaBadgeTooltipContent": "{pluginName} 为公测版,可能会进行更改。设计和代码相对于正式发行版功能还不够成熟,将按原样提供,且不提供任何保证。公测版功能不受正式发行版功能支持 SLA 的约束。",
"xpack.triggersActionsUI.sections.alertAdd.cancelButtonLabel": "取消",
"xpack.triggersActionsUI.sections.alertAdd.conditionPrompt": "定义条件",
"xpack.triggersActionsUI.sections.alertAdd.conditionPrompt": "定义条件",
"xpack.triggersActionsUI.sections.alertAdd.errorLoadingAlertVisualizationTitle": "无法加载告警可视化",
"xpack.triggersActionsUI.sections.alertAdd.flyoutTitle": "创建告警",
"xpack.triggersActionsUI.sections.alertAdd.loadingAlertVisualizationDescription": "正在加载告警可视化……",
@ -15965,7 +15965,7 @@
"xpack.triggersActionsUI.sections.alertAdd.saveButtonLabel": "保存",
"xpack.triggersActionsUI.sections.alertAdd.saveErrorNotificationText": "无法创建告警。",
"xpack.triggersActionsUI.sections.alertAdd.saveSuccessNotificationText": "已保存“{alertName}”",
"xpack.triggersActionsUI.sections.alertAdd.selectIndex": "选择索引",
"xpack.triggersActionsUI.sections.alertAdd.selectIndex": "选择索引",
"xpack.triggersActionsUI.sections.alertAdd.threshold.closeIndexPopoverLabel": "关闭",
"xpack.triggersActionsUI.sections.alertAdd.threshold.fixErrorInExpressionBelowValidationMessage": "表达式包含错误。",
"xpack.triggersActionsUI.sections.alertAdd.threshold.howToBroadenSearchQueryDescription": "使用 * 可扩大您的查询范围。",
@ -16049,7 +16049,6 @@
"xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.muteAllTitle": "静音",
"xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.unmuteAllTitle": "取消静音",
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.deleteTitle": "删除",
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.enableTitle": "启用",
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.muteTitle": "静音",
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.popoverButtonTitle": "操作",
"xpack.triggersActionsUI.components.emptyPrompt.emptyButton": "创建告警",
@ -16762,4 +16761,4 @@
"xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "此字段必填。",
"xpack.watcher.watcherDescription": "通过创建、管理和监测警报来检测数据中的更改。"
}
}
}

View file

@ -1,3 +1,13 @@
.actAlertVisualization__chart {
height: $euiSize * 15;
height: $euiSize * 14;
}
.actAddAlertSteps {
.euiStep__titleWrapper {
align-items: center;
}
.euiStep__title {
@include euiTitle('xs');
}
}

View file

@ -283,7 +283,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent<IndexThr
const firstSetOfSteps = [
{
title: i18n.translate('xpack.triggersActionsUI.sections.alertAdd.selectIndex', {
defaultMessage: 'Select an index.',
defaultMessage: 'Select an index',
}),
children: (
<>
@ -396,7 +396,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent<IndexThr
},
{
title: i18n.translate('xpack.triggersActionsUI.sections.alertAdd.conditionPrompt', {
defaultMessage: 'Define the condition.',
defaultMessage: 'Define the condition',
}),
children: (
<>
@ -445,12 +445,10 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent<IndexThr
</Fragment>
) : null}
<EuiSpacer size="l" />
<EuiSteps steps={firstSetOfSteps} />
<EuiSpacer size="l" />
<EuiSteps className="actAddAlertSteps" steps={firstSetOfSteps} />
<div className="actAlertVisualization__chart">
{canShowVizualization ? (
<Fragment>
<EuiSpacer size="xl" />
<EuiEmptyPrompt
iconType="visBarVertical"
body={

View file

@ -33,8 +33,6 @@ export const EmptyPrompt = ({ onCTAClicked }: { onCTAClicked: () => void }) => (
data-test-subj="createFirstAlertButton"
key="create-action"
fill
iconType="plusInCircle"
iconSide="left"
onClick={onCTAClicked}
>
<FormattedMessage

View file

@ -25,6 +25,8 @@ import {
EuiIconTip,
EuiLink,
EuiCallOut,
EuiHorizontalRule,
EuiText,
} from '@elastic/eui';
import { HttpSetup, ToastsApi } from 'kibana/public';
import { loadActionTypes, loadAllActions } from '../../lib/action_connector_api';
@ -256,81 +258,87 @@ export const ActionForm = ({
);
return (
<EuiAccordion
initialIsOpen={true}
key={index}
id={index.toString()}
className="actAccordionActionForm"
buttonContentClassName="actAccordionActionForm__button"
data-test-subj={`alertActionAccordion-${defaultActionGroupId}`}
buttonContent={
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiIcon type={actionTypeRegistered.iconClass} size="m" />
</EuiFlexItem>
<EuiFlexItem>
<EuiTitle size="s">
<h5>
<EuiFlexGroup gutterSize="s">
<EuiFlexItem grow={false}>
<FormattedMessage
defaultMessage="{actionConnectorName}"
id="xpack.triggersActionsUI.sections.alertForm.selectAlertActionTypeEditTitle"
values={{
actionConnectorName: `${actionConnector.name} ${
actionConnector.isPreconfigured ? preconfiguredMessage : ''
}`,
}}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
{checkEnabledResult.isEnabled === false && (
<Fragment>
<EuiIconTip
type="alert"
color="danger"
content={i18n.translate(
'xpack.triggersActionsUI.sections.alertForm.actionDisabledTitle',
{
defaultMessage: 'This action is disabled',
}
)}
position="right"
/>
</Fragment>
)}
</EuiFlexItem>
</EuiFlexGroup>
</h5>
</EuiTitle>
</EuiFlexItem>
</EuiFlexGroup>
}
extraAction={
<EuiButtonIcon
iconType="cross"
color="danger"
className="actAccordionActionForm__extraAction"
aria-label={i18n.translate(
'xpack.triggersActionsUI.sections.alertForm.accordion.deleteIconAriaLabel',
{
defaultMessage: 'Delete',
}
)}
onClick={() => {
const updatedActions = actions.filter((_item: AlertAction, i: number) => i !== index);
setAlertProperty(updatedActions);
setIsAddActionPanelOpen(
updatedActions.filter((item: AlertAction) => item.id !== actionItem.id).length === 0
);
setActiveActionItem(undefined);
}}
/>
}
paddingSize="l"
>
{accordionContent}
</EuiAccordion>
<Fragment>
<EuiAccordion
initialIsOpen={true}
key={index}
id={index.toString()}
className="actAccordionActionForm"
buttonContentClassName="actAccordionActionForm__button"
data-test-subj={`alertActionAccordion-${defaultActionGroupId}`}
buttonContent={
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiIcon type={actionTypeRegistered.iconClass} size="m" />
</EuiFlexItem>
<EuiFlexItem>
<EuiText>
<p>
<EuiFlexGroup gutterSize="s">
<EuiFlexItem grow={false}>
<FormattedMessage
defaultMessage="{actionConnectorName}"
id="xpack.triggersActionsUI.sections.alertForm.selectAlertActionTypeEditTitle"
values={{
actionConnectorName: `${actionConnector.name} ${
actionConnector.isPreconfigured ? preconfiguredMessage : ''
}`,
}}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
{checkEnabledResult.isEnabled === false && (
<Fragment>
<EuiIconTip
type="alert"
color="danger"
content={i18n.translate(
'xpack.triggersActionsUI.sections.alertForm.actionDisabledTitle',
{
defaultMessage: 'This action is disabled',
}
)}
position="right"
/>
</Fragment>
)}
</EuiFlexItem>
</EuiFlexGroup>
</p>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
}
extraAction={
<EuiButtonIcon
iconType="cross"
color="danger"
className="actAccordionActionForm__extraAction"
aria-label={i18n.translate(
'xpack.triggersActionsUI.sections.alertForm.accordion.deleteIconAriaLabel',
{
defaultMessage: 'Delete',
}
)}
onClick={() => {
const updatedActions = actions.filter(
(_item: AlertAction, i: number) => i !== index
);
setAlertProperty(updatedActions);
setIsAddActionPanelOpen(
updatedActions.filter((item: AlertAction) => item.id !== actionItem.id).length ===
0
);
setActiveActionItem(undefined);
}}
/>
}
paddingSize="l"
>
{accordionContent}
</EuiAccordion>
<EuiSpacer size="xs" />
</Fragment>
);
};
@ -341,96 +349,103 @@ export const ActionForm = ({
const actionTypeRegistered = actionTypeRegistry.get(actionItem.actionTypeId);
if (!actionTypeRegistered || actionItem.group !== defaultActionGroupId) return null;
return (
<EuiAccordion
initialIsOpen={true}
key={index}
id={index.toString()}
className="actAccordionActionForm"
buttonContentClassName="actAccordionActionForm__button"
data-test-subj={`alertActionAccordion-${defaultActionGroupId}`}
buttonContent={
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiIcon type={actionTypeRegistered.iconClass} size="m" />
</EuiFlexItem>
<EuiFlexItem>
<EuiTitle size="s">
<h5>
<FormattedMessage
defaultMessage="{actionConnectorName}"
id="xpack.triggersActionsUI.sections.alertForm.selectAlertActionTypeEditTitle"
values={{
actionConnectorName: actionTypeRegistered.actionTypeTitle,
}}
/>
</h5>
</EuiTitle>
</EuiFlexItem>
</EuiFlexGroup>
}
extraAction={
<EuiButtonIcon
iconType="cross"
color="danger"
className="actAccordionActionForm__extraAction"
aria-label={i18n.translate(
'xpack.triggersActionsUI.sections.alertForm.accordion.deleteIconAriaLabel',
{
defaultMessage: 'Delete',
}
)}
onClick={() => {
const updatedActions = actions.filter((_item: AlertAction, i: number) => i !== index);
setAlertProperty(updatedActions);
setIsAddActionPanelOpen(
updatedActions.filter((item: AlertAction) => item.id !== actionItem.id).length === 0
);
setActiveActionItem(undefined);
}}
/>
}
paddingSize="l"
>
<EuiEmptyPrompt
title={
emptyActionsIds.find((emptyId: string) => actionItem.id === emptyId) ? (
<FormattedMessage
id="xpack.triggersActionsUI.sections.alertForm.emptyConnectorsLabel"
defaultMessage="No {actionTypeName} connectors."
values={{
actionTypeName,
}}
/>
) : (
<EuiCallOut
title={i18n.translate(
'xpack.triggersActionsUI.sections.alertForm.unableToLoadConnectorTitle',
{
defaultMessage: 'Unable to load connector.',
}
)}
color="warning"
/>
)
<Fragment>
<EuiAccordion
initialIsOpen={true}
key={index}
id={index.toString()}
className="actAccordionActionForm"
buttonContentClassName="actAccordionActionForm__button"
data-test-subj={`alertActionAccordion-${defaultActionGroupId}`}
buttonContent={
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiIcon type={actionTypeRegistered.iconClass} size="m" />
</EuiFlexItem>
<EuiFlexItem>
<EuiText>
<p>
<FormattedMessage
defaultMessage="{actionConnectorName}"
id="xpack.triggersActionsUI.sections.alertForm.selectAlertActionTypeEditTitle"
values={{
actionConnectorName: actionTypeRegistered.actionTypeTitle,
}}
/>
</p>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
}
actions={[
<EuiButton
color="primary"
fill
data-test-subj="createActionConnectorButton"
extraAction={
<EuiButtonIcon
iconType="cross"
color="danger"
className="actAccordionActionForm__extraAction"
aria-label={i18n.translate(
'xpack.triggersActionsUI.sections.alertForm.accordion.deleteIconAriaLabel',
{
defaultMessage: 'Delete',
}
)}
onClick={() => {
setActiveActionItem({ actionTypeId: actionItem.actionTypeId, index });
setAddModalVisibility(true);
const updatedActions = actions.filter(
(_item: AlertAction, i: number) => i !== index
);
setAlertProperty(updatedActions);
setIsAddActionPanelOpen(
updatedActions.filter((item: AlertAction) => item.id !== actionItem.id).length ===
0
);
setActiveActionItem(undefined);
}}
>
<FormattedMessage
id="xpack.triggersActionsUI.sections.alertForm.addConnectorButtonLabel"
defaultMessage="Create a connector"
/>
</EuiButton>,
]}
/>
</EuiAccordion>
/>
}
paddingSize="l"
>
<EuiEmptyPrompt
title={
emptyActionsIds.find((emptyId: string) => actionItem.id === emptyId) ? (
<FormattedMessage
id="xpack.triggersActionsUI.sections.alertForm.emptyConnectorsLabel"
defaultMessage="No {actionTypeName} connectors."
values={{
actionTypeName,
}}
/>
) : (
<EuiCallOut
title={i18n.translate(
'xpack.triggersActionsUI.sections.alertForm.unableToLoadConnectorTitle',
{
defaultMessage: 'Unable to load connector.',
}
)}
color="warning"
/>
)
}
actions={[
<EuiButton
color="primary"
fill
size="s"
data-test-subj="createActionConnectorButton"
onClick={() => {
setActiveActionItem({ actionTypeId: actionItem.actionTypeId, index });
setAddModalVisibility(true);
}}
>
<FormattedMessage
id="xpack.triggersActionsUI.sections.alertForm.addConnectorButtonLabel"
defaultMessage="Create a connector"
/>
</EuiButton>,
]}
/>
</EuiAccordion>
<EuiSpacer size="xs" />
</Fragment>
);
};
@ -537,19 +552,34 @@ export const ActionForm = ({
</SectionLoading>
) : (
<Fragment>
{alertActionsList}
<EuiSpacer size="m" />
{isAddActionPanelOpen === false ? (
<EuiButton
iconType="plusInCircle"
data-test-subj="addAlertActionButton"
onClick={() => setIsAddActionPanelOpen(true)}
>
<EuiTitle size="s">
<h4>
<FormattedMessage
id="xpack.triggersActionsUI.sections.alertForm.addActionButtonLabel"
defaultMessage="Add action"
defaultMessage="Actions"
id="xpack.triggersActionsUI.sections.alertForm.actionSectionsTitle"
/>
</EuiButton>
</h4>
</EuiTitle>
<EuiSpacer size="m" />
{alertActionsList}
{isAddActionPanelOpen === false ? (
<div>
<EuiHorizontalRule />
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiButton
size="s"
data-test-subj="addAlertActionButton"
onClick={() => setIsAddActionPanelOpen(true)}
>
<FormattedMessage
id="xpack.triggersActionsUI.sections.alertForm.addActionButtonLabel"
defaultMessage="Add action"
/>
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</div>
) : null}
{isAddActionPanelOpen ? (
<Fragment>
@ -558,7 +588,7 @@ export const ActionForm = ({
<EuiTitle size="xs">
<h5>
<FormattedMessage
defaultMessage="Actions: Select an action type"
defaultMessage="Select an action type"
id="xpack.triggersActionsUI.sections.alertForm.selectAlertActionTypeTitle"
/>
</h5>
@ -571,6 +601,7 @@ export const ActionForm = ({
<EuiLink
href={VIEW_LICENSE_OPTIONS_LINK}
target="_blank"
external
className="actActionForm__getMoreActionsLink"
>
<FormattedMessage
@ -584,7 +615,7 @@ export const ActionForm = ({
)}
</EuiFlexGroup>
<EuiSpacer />
<EuiFlexGroup gutterSize="s" wrap>
<EuiFlexGroup justifyContent="spaceBetween" gutterSize="s" wrap>
{isLoadingActionTypes ? (
<SectionLoading>
<FormattedMessage

View file

@ -338,8 +338,6 @@ export const ActionsConnectorsList: React.FunctionComponent = () => {
data-test-subj="createActionButton"
key="create-action"
fill
iconType="plusInCircle"
iconSide="left"
onClick={() => setAddFlyoutVisibility(true)}
>
<FormattedMessage

View file

@ -280,8 +280,6 @@ export const AlertsList: React.FunctionComponent = () => {
key="create-alert"
data-test-subj="createAlertButton"
fill
iconType="plusInCircle"
iconSide="left"
onClick={() => setAlertFlyoutVisibility(true)}
>
<FormattedMessage

View file

@ -0,0 +1,23 @@
.actCollapsedItemActions {
.euiContextMenuItem:hover {
text-decoration: none;
}
}
.actCollapsedItemActions__item {
padding: $euiSizeM;
}
.actCollapsedItemActions__delete {
display: flex;
.actCollapsedItemActions__deleteIcon {
width: $euiSwitchWidthCompressed;
text-align: center;
}
.actCollapsedItemActions__deleteLabel {
padding-left: $euiSizeS;
padding-top: $euiSizeXS * .5;
}
}

View file

@ -8,12 +8,15 @@ import { i18n } from '@kbn/i18n';
import React, { useState } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import {
EuiButtonEmpty,
EuiButtonIcon,
EuiFormRow,
EuiPopover,
EuiPopoverFooter,
EuiContextMenuPanel,
EuiContextMenuItem,
EuiSwitch,
EuiHorizontalRule,
EuiText,
EuiSpacer,
EuiIcon,
} from '@elastic/eui';
import { AlertTableItem } from '../../../../types';
@ -23,6 +26,7 @@ import {
ComponentOpts as BulkOperationsComponentOpts,
withBulkAlertOperations,
} from '../../common/components/with_bulk_alert_api_operations';
import './collapsed_item_actions.scss';
export type ComponentOpts = {
item: AlertTableItem;
@ -64,68 +68,93 @@ export const CollapsedItemActions: React.FunctionComponent<ComponentOpts> = ({
isOpen={isPopoverOpen}
closePopover={() => setIsPopoverOpen(false)}
ownFocus
panelPaddingSize="none"
data-test-subj="collapsedItemActions"
>
<EuiFormRow>
<EuiSwitch
name="enable"
disabled={!canSave}
checked={item.enabled}
data-test-subj="enableSwitch"
onChange={async () => {
if (item.enabled) {
await disableAlert(item);
} else {
await enableAlert(item);
<EuiContextMenuPanel className="actCollapsedItemActions" hasFocus={false}>
<div className="actCollapsedItemActions__item">
<EuiSwitch
name="disable"
disabled={!canSave}
compressed
checked={!item.enabled}
data-test-subj="disableSwitch"
onChange={async () => {
if (item.enabled) {
await disableAlert(item);
} else {
await enableAlert(item);
}
onAlertChanged();
}}
label={
<FormattedMessage
id="xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.disableTitle"
defaultMessage="Disable"
/>
}
onAlertChanged();
}}
label={
/>
<EuiSpacer size="xs" />
<EuiText color="subdued" size="xs">
<FormattedMessage
id="xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.enableTitle"
defaultMessage="Enable"
id="xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.disableHelpText"
defaultMessage="When disabled, the alert is not checked."
/>
}
/>
</EuiFormRow>
<EuiFormRow>
<EuiSwitch
name="mute"
checked={item.muteAll}
disabled={!(canSave && item.enabled)}
data-test-subj="muteSwitch"
onChange={async () => {
if (item.muteAll) {
await unmuteAlert(item);
} else {
await muteAlert(item);
</EuiText>
</div>
<div className="actCollapsedItemActions__item">
<EuiSwitch
name="mute"
checked={item.muteAll}
disabled={!(canSave && item.enabled)}
compressed
data-test-subj="muteSwitch"
onChange={async () => {
if (item.muteAll) {
await unmuteAlert(item);
} else {
await muteAlert(item);
}
onAlertChanged();
}}
label={
<FormattedMessage
id="xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.muteTitle"
defaultMessage="Mute"
/>
}
onAlertChanged();
}}
label={
/>
<EuiSpacer size="xs" />
<EuiText color="subdued" size="xs">
<FormattedMessage
id="xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.muteTitle"
defaultMessage="Mute"
id="xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.muteHelpText"
defaultMessage="When muted, the alert is checked, but no action is performed."
/>
}
/>
</EuiFormRow>
<EuiPopoverFooter>
<EuiFormRow>
<EuiButtonEmpty
isDisabled={!canDelete}
iconType="trash"
color="text"
data-test-subj="deleteAlert"
onClick={() => setAlertsToDelete([item.id])}
>
<FormattedMessage
id="xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.deleteTitle"
defaultMessage="Delete"
/>
</EuiButtonEmpty>
</EuiFormRow>
</EuiPopoverFooter>
</EuiText>
</div>
<EuiHorizontalRule margin="none" />
<EuiContextMenuItem
disabled={!canDelete}
data-test-subj="deleteAlert"
onClick={() => setAlertsToDelete([item.id])}
>
<div className="actCollapsedItemActions__delete">
<div className="actCollapsedItemActions__deleteIcon">
<EuiIcon color="danger" type="trash" />
</div>
<div className="actCollapsedItemActions__deleteLabel">
<EuiText size="s" color="danger">
<p>
<FormattedMessage
id="xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.deleteTitle"
defaultMessage="Delete"
/>
</p>
</EuiText>
</div>
</div>
</EuiContextMenuItem>
</EuiContextMenuPanel>
</EuiPopover>
);
};

View file

@ -0,0 +1,5 @@
.actBulkActionPopover__deleteAll {
.euiButtonEmpty__text {
padding-top: $euiSizeXS;
}
}

View file

@ -5,9 +5,9 @@
*/
import { i18n } from '@kbn/i18n';
import React, { useState, Fragment } from 'react';
import React, { useState } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiButtonEmpty } from '@elastic/eui';
import { EuiButtonEmpty, EuiFlexItem, EuiFlexGroup } from '@elastic/eui';
import { Alert } from '../../../../types';
import { useAppDependencies } from '../../../app_context';
@ -15,6 +15,7 @@ import {
withBulkAlertOperations,
ComponentOpts as BulkOperationsComponentOpts,
} from './with_bulk_alert_api_operations';
import './alert_quick_edit_buttons.scss';
export type ComponentOpts = {
selectedItems: Alert[];
@ -147,72 +148,84 @@ export const AlertQuickEditButtons: React.FunctionComponent<ComponentOpts> = ({
}
return (
<Fragment>
<EuiFlexGroup alignItems="baseline" direction="column" gutterSize="none">
{!allAlertsMuted && (
<EuiButtonEmpty
onClick={onmMuteAllClick}
isLoading={isMutingAlerts}
isDisabled={isPerformingAction}
data-test-subj="muteAll"
>
<FormattedMessage
id="xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.muteAllTitle"
defaultMessage="Mute"
/>
</EuiButtonEmpty>
<EuiFlexItem>
<EuiButtonEmpty
onClick={onmMuteAllClick}
isLoading={isMutingAlerts}
isDisabled={isPerformingAction}
data-test-subj="muteAll"
>
<FormattedMessage
id="xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.muteAllTitle"
defaultMessage="Mute"
/>
</EuiButtonEmpty>
</EuiFlexItem>
)}
{allAlertsMuted && (
<EuiButtonEmpty
onClick={onUnmuteAllClick}
isLoading={isUnmutingAlerts}
isDisabled={isPerformingAction}
data-test-subj="unmuteAll"
>
<FormattedMessage
id="xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.unmuteAllTitle"
defaultMessage="Unmute"
/>
</EuiButtonEmpty>
<EuiFlexItem>
<EuiButtonEmpty
onClick={onUnmuteAllClick}
isLoading={isUnmutingAlerts}
isDisabled={isPerformingAction}
data-test-subj="unmuteAll"
>
<FormattedMessage
id="xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.unmuteAllTitle"
defaultMessage="Unmute"
/>
</EuiButtonEmpty>
</EuiFlexItem>
)}
{allAlertsDisabled && (
<EuiButtonEmpty
onClick={onEnableAllClick}
isLoading={isEnablingAlerts}
isDisabled={isPerformingAction}
data-test-subj="enableAll"
>
<FormattedMessage
id="xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.enableAllTitle"
defaultMessage="Enable"
/>
</EuiButtonEmpty>
<EuiFlexItem>
<EuiButtonEmpty
onClick={onEnableAllClick}
isLoading={isEnablingAlerts}
isDisabled={isPerformingAction}
data-test-subj="enableAll"
>
<FormattedMessage
id="xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.enableAllTitle"
defaultMessage="Enable"
/>
</EuiButtonEmpty>
</EuiFlexItem>
)}
{!allAlertsDisabled && (
<EuiFlexItem>
<EuiButtonEmpty
onClick={onDisableAllClick}
isLoading={isDisablingAlerts}
isDisabled={isPerformingAction}
data-test-subj="disableAll"
>
<FormattedMessage
id="xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.disableAllTitle"
defaultMessage="Disable"
/>
</EuiButtonEmpty>
</EuiFlexItem>
)}
<EuiFlexItem>
<EuiButtonEmpty
onClick={onDisableAllClick}
isLoading={isDisablingAlerts}
onClick={deleteSelectedItems}
isLoading={isDeletingAlerts}
iconType="trash"
color="danger"
isDisabled={isPerformingAction}
data-test-subj="disableAll"
data-test-subj="deleteAll"
className="actBulkActionPopover__deleteAll"
>
<FormattedMessage
id="xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.disableAllTitle"
defaultMessage="Disable"
id="xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.deleteAllTitle"
defaultMessage="Delete"
/>
</EuiButtonEmpty>
)}
<EuiButtonEmpty
onClick={deleteSelectedItems}
isLoading={isDeletingAlerts}
isDisabled={isPerformingAction}
data-test-subj="deleteAll"
>
<FormattedMessage
id="xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.deleteAllTitle"
defaultMessage="Delete"
/>
</EuiButtonEmpty>
</Fragment>
</EuiFlexItem>
</EuiFlexGroup>
);
};

View file

@ -4,9 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { useState } from 'react';
import React, { useState, Fragment } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiButton, EuiFormRow, EuiPopover } from '@elastic/eui';
import { EuiButton, EuiPopover } from '@elastic/eui';
export const BulkOperationPopover: React.FunctionComponent = ({ children }) => {
const [isPopoverOpen, setIsPopoverOpen] = useState<boolean>(false);
@ -16,6 +16,7 @@ export const BulkOperationPopover: React.FunctionComponent = ({ children }) => {
isOpen={isPopoverOpen}
closePopover={() => setIsPopoverOpen(false)}
data-test-subj="bulkAction"
panelPaddingSize="s"
button={
<EuiButton
iconType="arrowDown"
@ -31,11 +32,7 @@ export const BulkOperationPopover: React.FunctionComponent = ({ children }) => {
>
{children &&
React.Children.map(children, child =>
React.isValidElement(child) ? (
<EuiFormRow>{React.cloneElement(child, {})}</EuiFormRow>
) : (
child
)
React.isValidElement(child) ? <Fragment>{React.cloneElement(child, {})}</Fragment> : child
)}
</EuiPopover>
);

View file

@ -343,15 +343,15 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
await testSubjects.click('collapsedItemActions');
await pageObjects.triggersActionsUI.toggleSwitch('enableSwitch');
await pageObjects.triggersActionsUI.toggleSwitch('disableSwitch');
await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name);
await testSubjects.click('collapsedItemActions');
const enableSwitchAfterDisable = await testSubjects.find('enableSwitch');
const isChecked = await enableSwitchAfterDisable.getAttribute('aria-checked');
expect(isChecked).to.eql('false');
const disableSwitchAfterDisable = await testSubjects.find('disableSwitch');
const isChecked = await disableSwitchAfterDisable.getAttribute('aria-checked');
expect(isChecked).to.eql('true');
});
it('should re-enable single alert', async () => {
@ -361,21 +361,21 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
await testSubjects.click('collapsedItemActions');
await pageObjects.triggersActionsUI.toggleSwitch('enableSwitch');
await pageObjects.triggersActionsUI.toggleSwitch('disableSwitch');
await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name);
await testSubjects.click('collapsedItemActions');
await pageObjects.triggersActionsUI.toggleSwitch('enableSwitch');
await pageObjects.triggersActionsUI.toggleSwitch('disableSwitch');
await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name);
await testSubjects.click('collapsedItemActions');
const enableSwitchAfterReEnable = await testSubjects.find('enableSwitch');
const isChecked = await enableSwitchAfterReEnable.getAttribute('aria-checked');
expect(isChecked).to.eql('true');
const disableSwitchAfterReEnable = await testSubjects.find('disableSwitch');
const isChecked = await disableSwitchAfterReEnable.getAttribute('aria-checked');
expect(isChecked).to.eql('false');
});
it('should mute single alert', async () => {
@ -507,9 +507,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
await testSubjects.click('collapsedItemActions');
const enableSwitch = await testSubjects.find('enableSwitch');
const isChecked = await enableSwitch.getAttribute('aria-checked');
expect(isChecked).to.eql('false');
const disableSwitch = await testSubjects.find('disableSwitch');
const isChecked = await disableSwitch.getAttribute('aria-checked');
expect(isChecked).to.eql('true');
});
it('should enable all selection', async () => {
@ -532,9 +532,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
await testSubjects.click('collapsedItemActions');
const enableSwitch = await testSubjects.find('enableSwitch');
const isChecked = await enableSwitch.getAttribute('aria-checked');
expect(isChecked).to.eql('true');
const disableSwitch = await testSubjects.find('disableSwitch');
const isChecked = await disableSwitch.getAttribute('aria-checked');
expect(isChecked).to.eql('false');
});
it('should delete all selection', async () => {