mirror of
https://github.com/elastic/kibana.git
synced 2025-04-26 02:37:44 -04:00
[Alerting UI]Changed rules table to support visual indication for disabled and muted alerts (#104190)
* [Alerting UI]Changed rules table to support visual indication for disabled or muted alerts * changed columns styles due to the mockup * added tests * fixed quotas * fixed popover * fixed due to the lates UI updates * fixed errors * moved enabled to a separate component * fixed tests * fixed due to comments * Update x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.scss Co-authored-by: Mike Côté <mikecote@users.noreply.github.com> * removed test code * fixed tests * fixed due to comments * fixed due to comments Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Mike Côté <mikecote@users.noreply.github.com>
This commit is contained in:
parent
05c29efab5
commit
d91c6d0cfb
15 changed files with 412 additions and 257 deletions
|
@ -22829,7 +22829,6 @@
|
||||||
"xpack.triggersActionsUI.sections.alertDetails.alertInstancesList.status.active": "アクティブ",
|
"xpack.triggersActionsUI.sections.alertDetails.alertInstancesList.status.active": "アクティブ",
|
||||||
"xpack.triggersActionsUI.sections.alertDetails.alertInstancesList.status.inactive": "OK",
|
"xpack.triggersActionsUI.sections.alertDetails.alertInstancesList.status.inactive": "OK",
|
||||||
"xpack.triggersActionsUI.sections.alertDetails.alerts.disabledRuleTitle": "無効なルール",
|
"xpack.triggersActionsUI.sections.alertDetails.alerts.disabledRuleTitle": "無効なルール",
|
||||||
"xpack.triggersActionsUI.sections.alertDetails.collapsedItemActons.disableTitle": "無効にする",
|
|
||||||
"xpack.triggersActionsUI.sections.alertDetails.collapsedItemActons.muteTitle": "ミュート",
|
"xpack.triggersActionsUI.sections.alertDetails.collapsedItemActons.muteTitle": "ミュート",
|
||||||
"xpack.triggersActionsUI.sections.alertDetails.dismissButtonTitle": "閉じる",
|
"xpack.triggersActionsUI.sections.alertDetails.dismissButtonTitle": "閉じる",
|
||||||
"xpack.triggersActionsUI.sections.alertDetails.editAlertButtonLabel": "編集",
|
"xpack.triggersActionsUI.sections.alertDetails.editAlertButtonLabel": "編集",
|
||||||
|
@ -22883,10 +22882,8 @@
|
||||||
"xpack.triggersActionsUI.sections.alertsList.alertErrorReasonReading": "ルールの読み取り中にエラーが発生しました。",
|
"xpack.triggersActionsUI.sections.alertsList.alertErrorReasonReading": "ルールの読み取り中にエラーが発生しました。",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.alertErrorReasonRunning": "ルールの実行中にエラーが発生しました。",
|
"xpack.triggersActionsUI.sections.alertsList.alertErrorReasonRunning": "ルールの実行中にエラーが発生しました。",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.alertErrorReasonUnknown": "不明な理由でエラーが発生しました。",
|
"xpack.triggersActionsUI.sections.alertsList.alertErrorReasonUnknown": "不明な理由でエラーが発生しました。",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.actionsCount": "アクション",
|
|
||||||
"xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.actionsTex": "アクション",
|
"xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.actionsTex": "アクション",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.alertTypeTitle": "型",
|
"xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.alertTypeTitle": "型",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.intervalTitle": "次の間隔で実行",
|
|
||||||
"xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.nameTitle": "名前",
|
"xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.nameTitle": "名前",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.statusTitle": "ステータス",
|
"xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.statusTitle": "ステータス",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.tagsText": "タグ",
|
"xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.tagsText": "タグ",
|
||||||
|
@ -22908,10 +22905,7 @@
|
||||||
"xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.failedToUnmuteRulesMessage": "ルールをミュート解除できませんでした",
|
"xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.failedToUnmuteRulesMessage": "ルールをミュート解除できませんでした",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.muteAllTitle": "ミュート",
|
"xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.muteAllTitle": "ミュート",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.unmuteAllTitle": "ミュート解除",
|
"xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.unmuteAllTitle": "ミュート解除",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.deleteTitle": "削除",
|
|
||||||
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.disableHelpText": "無効にすると、ルールは確認されません。",
|
|
||||||
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.disableTitle": "無効にする",
|
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.disableTitle": "無効にする",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.muteHelpText": "ミュートにすると、ルールは確認されますが、アクションは実行されません。",
|
|
||||||
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.muteTitle": "ミュート",
|
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.muteTitle": "ミュート",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.popoverButtonTitle": "アクション",
|
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.popoverButtonTitle": "アクション",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.dismissBunnerButtonLabel": "閉じる",
|
"xpack.triggersActionsUI.sections.alertsList.dismissBunnerButtonLabel": "閉じる",
|
||||||
|
|
|
@ -23185,7 +23185,6 @@
|
||||||
"xpack.triggersActionsUI.sections.alertDetails.alertInstancesList.status.active": "活动",
|
"xpack.triggersActionsUI.sections.alertDetails.alertInstancesList.status.active": "活动",
|
||||||
"xpack.triggersActionsUI.sections.alertDetails.alertInstancesList.status.inactive": "确定",
|
"xpack.triggersActionsUI.sections.alertDetails.alertInstancesList.status.inactive": "确定",
|
||||||
"xpack.triggersActionsUI.sections.alertDetails.alerts.disabledRuleTitle": "已禁用规则",
|
"xpack.triggersActionsUI.sections.alertDetails.alerts.disabledRuleTitle": "已禁用规则",
|
||||||
"xpack.triggersActionsUI.sections.alertDetails.collapsedItemActons.disableTitle": "禁用",
|
|
||||||
"xpack.triggersActionsUI.sections.alertDetails.collapsedItemActons.muteTitle": "静音",
|
"xpack.triggersActionsUI.sections.alertDetails.collapsedItemActons.muteTitle": "静音",
|
||||||
"xpack.triggersActionsUI.sections.alertDetails.dismissButtonTitle": "关闭",
|
"xpack.triggersActionsUI.sections.alertDetails.dismissButtonTitle": "关闭",
|
||||||
"xpack.triggersActionsUI.sections.alertDetails.editAlertButtonLabel": "编辑",
|
"xpack.triggersActionsUI.sections.alertDetails.editAlertButtonLabel": "编辑",
|
||||||
|
@ -23239,10 +23238,8 @@
|
||||||
"xpack.triggersActionsUI.sections.alertsList.alertErrorReasonReading": "读取规则时发生错误。",
|
"xpack.triggersActionsUI.sections.alertsList.alertErrorReasonReading": "读取规则时发生错误。",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.alertErrorReasonRunning": "运行规则时发生错误。",
|
"xpack.triggersActionsUI.sections.alertsList.alertErrorReasonRunning": "运行规则时发生错误。",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.alertErrorReasonUnknown": "由于未知原因发生错误。",
|
"xpack.triggersActionsUI.sections.alertsList.alertErrorReasonUnknown": "由于未知原因发生错误。",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.actionsCount": "操作",
|
|
||||||
"xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.actionsTex": "操作",
|
"xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.actionsTex": "操作",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.alertTypeTitle": "类型",
|
"xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.alertTypeTitle": "类型",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.intervalTitle": "运行间隔",
|
|
||||||
"xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.nameTitle": "名称",
|
"xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.nameTitle": "名称",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.statusTitle": "状态",
|
"xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.statusTitle": "状态",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.tagsText": "标签",
|
"xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.tagsText": "标签",
|
||||||
|
@ -23265,10 +23262,7 @@
|
||||||
"xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.failedToUnmuteRulesMessage": "无法取消静音规则",
|
"xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.failedToUnmuteRulesMessage": "无法取消静音规则",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.muteAllTitle": "静音",
|
"xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.muteAllTitle": "静音",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.unmuteAllTitle": "取消静音",
|
"xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.unmuteAllTitle": "取消静音",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.deleteTitle": "删除",
|
|
||||||
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.disableHelpText": "禁用后,将不检查规则。",
|
|
||||||
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.disableTitle": "禁用",
|
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.disableTitle": "禁用",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.muteHelpText": "静音后,将检查规则,但不执行操作。",
|
|
||||||
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.muteTitle": "静音",
|
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.muteTitle": "静音",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.popoverButtonTitle": "操作",
|
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.popoverButtonTitle": "操作",
|
||||||
"xpack.triggersActionsUI.sections.alertsList.dismissBunnerButtonLabel": "关闭",
|
"xpack.triggersActionsUI.sections.alertsList.dismissBunnerButtonLabel": "关闭",
|
||||||
|
|
|
@ -359,16 +359,16 @@ describe('disable button', () => {
|
||||||
<AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} />
|
<AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} />
|
||||||
)
|
)
|
||||||
.find(EuiSwitch)
|
.find(EuiSwitch)
|
||||||
.find('[name="disable"]')
|
.find('[name="enable"]')
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
expect(enableButton.props()).toMatchObject({
|
expect(enableButton.props()).toMatchObject({
|
||||||
checked: false,
|
checked: true,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render a disable button when alert is disabled', () => {
|
it('should render a enable button when alert is disabled', () => {
|
||||||
const alert = mockAlert({
|
const alert = mockAlert({
|
||||||
enabled: false,
|
enabled: false,
|
||||||
});
|
});
|
||||||
|
@ -390,11 +390,11 @@ describe('disable button', () => {
|
||||||
<AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} />
|
<AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} />
|
||||||
)
|
)
|
||||||
.find(EuiSwitch)
|
.find(EuiSwitch)
|
||||||
.find('[name="disable"]')
|
.find('[name="enable"]')
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
expect(enableButton.props()).toMatchObject({
|
expect(enableButton.props()).toMatchObject({
|
||||||
checked: true,
|
checked: false,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -428,7 +428,7 @@ describe('disable button', () => {
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
.find(EuiSwitch)
|
.find(EuiSwitch)
|
||||||
.find('[name="disable"]')
|
.find('[name="enable"]')
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
enableButton.simulate('click');
|
enableButton.simulate('click');
|
||||||
|
@ -468,7 +468,7 @@ describe('disable button', () => {
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
.find(EuiSwitch)
|
.find(EuiSwitch)
|
||||||
.find('[name="disable"]')
|
.find('[name="enable"]')
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
enableButton.simulate('click');
|
enableButton.simulate('click');
|
||||||
|
@ -531,14 +531,14 @@ describe('disable button', () => {
|
||||||
|
|
||||||
// Disable the alert
|
// Disable the alert
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper.find('[data-test-subj="disableSwitch"] .euiSwitch__button').first().simulate('click');
|
wrapper.find('[data-test-subj="enableSwitch"] .euiSwitch__button').first().simulate('click');
|
||||||
await nextTick();
|
await nextTick();
|
||||||
});
|
});
|
||||||
expect(disableAlert).toHaveBeenCalled();
|
expect(disableAlert).toHaveBeenCalled();
|
||||||
|
|
||||||
// Enable the alert
|
// Enable the alert
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper.find('[data-test-subj="disableSwitch"] .euiSwitch__button').first().simulate('click');
|
wrapper.find('[data-test-subj="enableSwitch"] .euiSwitch__button').first().simulate('click');
|
||||||
await nextTick();
|
await nextTick();
|
||||||
});
|
});
|
||||||
expect(enableAlert).toHaveBeenCalled();
|
expect(enableAlert).toHaveBeenCalled();
|
||||||
|
|
|
@ -219,10 +219,10 @@ export const AlertDetails: React.FunctionComponent<AlertDetailsProps> = ({
|
||||||
<EuiFlexGroup justifyContent="flexEnd" wrap responsive={false} gutterSize="m">
|
<EuiFlexGroup justifyContent="flexEnd" wrap responsive={false} gutterSize="m">
|
||||||
<EuiFlexItem grow={false}>
|
<EuiFlexItem grow={false}>
|
||||||
<EuiSwitch
|
<EuiSwitch
|
||||||
name="disable"
|
name="enable"
|
||||||
disabled={!canSaveAlert || !alertType.enabledInLicense}
|
disabled={!canSaveAlert || !alertType.enabledInLicense}
|
||||||
checked={!isEnabled}
|
checked={isEnabled}
|
||||||
data-test-subj="disableSwitch"
|
data-test-subj="enableSwitch"
|
||||||
onChange={async () => {
|
onChange={async () => {
|
||||||
if (isEnabled) {
|
if (isEnabled) {
|
||||||
setIsEnabled(false);
|
setIsEnabled(false);
|
||||||
|
@ -237,8 +237,8 @@ export const AlertDetails: React.FunctionComponent<AlertDetailsProps> = ({
|
||||||
}}
|
}}
|
||||||
label={
|
label={
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="xpack.triggersActionsUI.sections.alertDetails.collapsedItemActons.disableTitle"
|
id="xpack.triggersActionsUI.sections.alertDetails.collapsedItemActons.enableTitle"
|
||||||
defaultMessage="Disable"
|
defaultMessage="Enable"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -133,6 +133,7 @@ export const AlertEdit = ({
|
||||||
aria-labelledby="flyoutAlertEditTitle"
|
aria-labelledby="flyoutAlertEditTitle"
|
||||||
size="m"
|
size="m"
|
||||||
maxWidth={620}
|
maxWidth={620}
|
||||||
|
ownFocus={false}
|
||||||
>
|
>
|
||||||
<EuiFlyoutHeader hasBorder>
|
<EuiFlyoutHeader hasBorder>
|
||||||
<EuiTitle size="s" data-test-subj="editAlertFlyoutTitle">
|
<EuiTitle size="s" data-test-subj="editAlertFlyoutTitle">
|
||||||
|
|
|
@ -95,13 +95,13 @@ export const AlertStatusFilter: React.FunctionComponent<AlertStatusFilterProps>
|
||||||
export function getHealthColor(status: AlertExecutionStatuses) {
|
export function getHealthColor(status: AlertExecutionStatuses) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'active':
|
case 'active':
|
||||||
return 'primary';
|
return 'success';
|
||||||
case 'error':
|
case 'error':
|
||||||
return 'danger';
|
return 'danger';
|
||||||
case 'ok':
|
case 'ok':
|
||||||
return 'subdued';
|
return 'subdued';
|
||||||
case 'pending':
|
case 'pending':
|
||||||
return 'success';
|
return 'accent';
|
||||||
default:
|
default:
|
||||||
return 'warning';
|
return 'warning';
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,3 +5,29 @@
|
||||||
color: $euiColorDarkShade;
|
color: $euiColorDarkShade;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.euiTableRow {
|
||||||
|
&:hover,
|
||||||
|
&:focus-within,
|
||||||
|
&[class*='-isActive'] {
|
||||||
|
.alertSidebarItem__action {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Only visually hide the action, so that it's still accessible to screen readers.
|
||||||
|
* 2. When tabbed to, this element needs to be visible for keyboard accessibility.
|
||||||
|
*/
|
||||||
|
.alertSidebarItem__action {
|
||||||
|
opacity: 0; /* 1 */
|
||||||
|
|
||||||
|
&.alertSidebarItem__mobile {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
opacity: 1; /* 2 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -368,7 +368,7 @@ describe('alerts_list component with items', () => {
|
||||||
it('sorts alerts when clicking the name column', async () => {
|
it('sorts alerts when clicking the name column', async () => {
|
||||||
await setup();
|
await setup();
|
||||||
wrapper
|
wrapper
|
||||||
.find('[data-test-subj="tableHeaderCell_name_0"] .euiTableHeaderButton')
|
.find('[data-test-subj="tableHeaderCell_name_1"] .euiTableHeaderButton')
|
||||||
.first()
|
.first()
|
||||||
.simulate('click');
|
.simulate('click');
|
||||||
|
|
||||||
|
@ -386,6 +386,28 @@ describe('alerts_list component with items', () => {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sorts alerts when clicking the enabled column', async () => {
|
||||||
|
await setup();
|
||||||
|
wrapper
|
||||||
|
.find('[data-test-subj="tableHeaderCell_enabled_0"] .euiTableHeaderButton')
|
||||||
|
.first()
|
||||||
|
.simulate('click');
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
await nextTick();
|
||||||
|
wrapper.update();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(loadAlerts).toHaveBeenLastCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
sort: {
|
||||||
|
field: 'enabled',
|
||||||
|
direction: 'asc',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('alerts_list component empty with show only capability', () => {
|
describe('alerts_list component empty with show only capability', () => {
|
||||||
|
|
|
@ -28,12 +28,13 @@ import {
|
||||||
EuiText,
|
EuiText,
|
||||||
EuiToolTip,
|
EuiToolTip,
|
||||||
EuiTableSortingType,
|
EuiTableSortingType,
|
||||||
|
EuiButtonIcon,
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import { ActionType, Alert, AlertTableItem, AlertTypeIndex, Pagination } from '../../../../types';
|
import { ActionType, Alert, AlertTableItem, AlertTypeIndex, Pagination } from '../../../../types';
|
||||||
import { AlertAdd } from '../../alert_form';
|
import { AlertAdd, AlertEdit } from '../../alert_form';
|
||||||
import { BulkOperationPopover } from '../../common/components/bulk_operation_popover';
|
import { BulkOperationPopover } from '../../common/components/bulk_operation_popover';
|
||||||
import { AlertQuickEditButtonsWithApi as AlertQuickEditButtons } from '../../common/components/alert_quick_edit_buttons';
|
import { AlertQuickEditButtonsWithApi as AlertQuickEditButtons } from '../../common/components/alert_quick_edit_buttons';
|
||||||
import { CollapsedItemActionsWithApi as CollapsedItemActions } from './collapsed_item_actions';
|
import { CollapsedItemActionsWithApi as CollapsedItemActions } from './collapsed_item_actions';
|
||||||
|
@ -44,6 +45,8 @@ import {
|
||||||
loadAlerts,
|
loadAlerts,
|
||||||
loadAlertAggregations,
|
loadAlertAggregations,
|
||||||
loadAlertTypes,
|
loadAlertTypes,
|
||||||
|
disableAlert,
|
||||||
|
enableAlert,
|
||||||
deleteAlerts,
|
deleteAlerts,
|
||||||
} from '../../../lib/alert_api';
|
} from '../../../lib/alert_api';
|
||||||
import { loadActionTypes } from '../../../lib/action_connector_api';
|
import { loadActionTypes } from '../../../lib/action_connector_api';
|
||||||
|
@ -65,6 +68,7 @@ import './alerts_list.scss';
|
||||||
import { CenterJustifiedSpinner } from '../../../components/center_justified_spinner';
|
import { CenterJustifiedSpinner } from '../../../components/center_justified_spinner';
|
||||||
import { ManageLicenseModal } from './manage_license_modal';
|
import { ManageLicenseModal } from './manage_license_modal';
|
||||||
import { checkAlertTypeEnabled } from '../../../lib/check_alert_type_enabled';
|
import { checkAlertTypeEnabled } from '../../../lib/check_alert_type_enabled';
|
||||||
|
import { RuleEnabledSwitch } from './rule_enabled_switch';
|
||||||
|
|
||||||
const ENTER_KEY = 13;
|
const ENTER_KEY = 13;
|
||||||
|
|
||||||
|
@ -102,6 +106,9 @@ export const AlertsList: React.FunctionComponent = () => {
|
||||||
const [alertStatusesFilter, setAlertStatusesFilter] = useState<string[]>([]);
|
const [alertStatusesFilter, setAlertStatusesFilter] = useState<string[]>([]);
|
||||||
const [alertFlyoutVisible, setAlertFlyoutVisibility] = useState<boolean>(false);
|
const [alertFlyoutVisible, setAlertFlyoutVisibility] = useState<boolean>(false);
|
||||||
const [dismissAlertErrors, setDismissAlertErrors] = useState<boolean>(false);
|
const [dismissAlertErrors, setDismissAlertErrors] = useState<boolean>(false);
|
||||||
|
const [editFlyoutVisible, setEditFlyoutVisibility] = useState<boolean>(false);
|
||||||
|
const [currentRuleToEdit, setCurrentRuleToEdit] = useState<AlertTableItem | null>(null);
|
||||||
|
|
||||||
const [sort, setSort] = useState<EuiTableSortingType<AlertTableItem>['sort']>({
|
const [sort, setSort] = useState<EuiTableSortingType<AlertTableItem>['sort']>({
|
||||||
field: 'name',
|
field: 'name',
|
||||||
direction: 'asc',
|
direction: 'asc',
|
||||||
|
@ -131,6 +138,10 @@ export const AlertsList: React.FunctionComponent = () => {
|
||||||
totalItemCount: 0,
|
totalItemCount: 0,
|
||||||
});
|
});
|
||||||
const [alertsToDelete, setAlertsToDelete] = useState<string[]>([]);
|
const [alertsToDelete, setAlertsToDelete] = useState<string[]>([]);
|
||||||
|
const onRuleEdit = (ruleItem: AlertTableItem) => {
|
||||||
|
setEditFlyoutVisibility(true);
|
||||||
|
setCurrentRuleToEdit(ruleItem);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadAlertsData();
|
loadAlertsData();
|
||||||
|
@ -169,15 +180,14 @@ export const AlertsList: React.FunctionComponent = () => {
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
const result = await loadActionTypes({ http });
|
const result = await loadActionTypes({ http });
|
||||||
setActionTypes(
|
const sortedResult = result
|
||||||
result
|
|
||||||
.filter(
|
.filter(
|
||||||
// TODO: Remove "DEFAULT_HIDDEN_ACTION_TYPES" when cases connector is available across Kibana.
|
// TODO: Remove "DEFAULT_HIDDEN_ACTION_TYPES" when cases connector is available across Kibana.
|
||||||
// Issue: https://github.com/elastic/kibana/issues/82502.
|
// Issue: https://github.com/elastic/kibana/issues/82502.
|
||||||
({ id }) => actionTypeRegistry.has(id) && !DEFAULT_HIDDEN_ACTION_TYPES.includes(id)
|
({ id }) => actionTypeRegistry.has(id) && !DEFAULT_HIDDEN_ACTION_TYPES.includes(id)
|
||||||
)
|
)
|
||||||
.sort((a, b) => a.name.localeCompare(b.name))
|
.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
);
|
setActionTypes(sortedResult);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toasts.addDanger({
|
toasts.addDanger({
|
||||||
title: i18n.translate(
|
title: i18n.translate(
|
||||||
|
@ -309,6 +319,26 @@ export const AlertsList: React.FunctionComponent = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const alertsTableColumns = [
|
const alertsTableColumns = [
|
||||||
|
{
|
||||||
|
field: 'enabled',
|
||||||
|
name: i18n.translate(
|
||||||
|
'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.enabledTitle',
|
||||||
|
{ defaultMessage: 'Enabled' }
|
||||||
|
),
|
||||||
|
width: '90px',
|
||||||
|
render(_enabled: boolean | undefined, item: AlertTableItem) {
|
||||||
|
return (
|
||||||
|
<RuleEnabledSwitch
|
||||||
|
disableAlert={async () => await disableAlert({ http, id: item.id })}
|
||||||
|
enableAlert={async () => await enableAlert({ http, id: item.id })}
|
||||||
|
item={item}
|
||||||
|
onAlertChanged={() => loadAlertsData()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
sortable: true,
|
||||||
|
'data-test-subj': 'alertsTableCell-enabled',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
field: 'name',
|
field: 'name',
|
||||||
name: i18n.translate(
|
name: i18n.translate(
|
||||||
|
@ -317,7 +347,7 @@ export const AlertsList: React.FunctionComponent = () => {
|
||||||
),
|
),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
truncateText: true,
|
truncateText: true,
|
||||||
width: '35%',
|
width: '30%',
|
||||||
'data-test-subj': 'alertsTableCell-name',
|
'data-test-subj': 'alertsTableCell-name',
|
||||||
render: (name: string, alert: AlertTableItem) => {
|
render: (name: string, alert: AlertTableItem) => {
|
||||||
const ruleType = alertTypesState.data.get(alert.alertTypeId);
|
const ruleType = alertTypesState.data.get(alert.alertTypeId);
|
||||||
|
@ -357,37 +387,12 @@ export const AlertsList: React.FunctionComponent = () => {
|
||||||
),
|
),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
truncateText: false,
|
truncateText: false,
|
||||||
width: '150px',
|
width: '120px',
|
||||||
'data-test-subj': 'alertsTableCell-status',
|
'data-test-subj': 'alertsTableCell-status',
|
||||||
render: (executionStatus: AlertExecutionStatus, item: AlertTableItem) => {
|
render: (_executionStatus: AlertExecutionStatus, item: AlertTableItem) => {
|
||||||
return renderAlertExecutionStatus(item.executionStatus, item);
|
return renderAlertExecutionStatus(item.executionStatus, item);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
field: 'tagsText',
|
|
||||||
name: i18n.translate(
|
|
||||||
'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.tagsText',
|
|
||||||
{ defaultMessage: 'Tags' }
|
|
||||||
),
|
|
||||||
sortable: false,
|
|
||||||
'data-test-subj': 'alertsTableCell-tagsText',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'actionsCount',
|
|
||||||
name: i18n.translate(
|
|
||||||
'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.actionsCount',
|
|
||||||
{ defaultMessage: 'Actions' }
|
|
||||||
),
|
|
||||||
render: (count: number, item: AlertTableItem) => {
|
|
||||||
return (
|
|
||||||
<EuiBadge color="hollow" key={item.id}>
|
|
||||||
{count}
|
|
||||||
</EuiBadge>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
sortable: false,
|
|
||||||
'data-test-subj': 'alertsTableCell-actionsCount',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
field: 'alertType',
|
field: 'alertType',
|
||||||
name: i18n.translate(
|
name: i18n.translate(
|
||||||
|
@ -396,29 +401,115 @@ export const AlertsList: React.FunctionComponent = () => {
|
||||||
),
|
),
|
||||||
sortable: false,
|
sortable: false,
|
||||||
truncateText: true,
|
truncateText: true,
|
||||||
|
render: (_count: number, item: AlertTableItem) => (
|
||||||
|
<EuiBadge color="default">{item.alertType}</EuiBadge>
|
||||||
|
),
|
||||||
'data-test-subj': 'alertsTableCell-alertType',
|
'data-test-subj': 'alertsTableCell-alertType',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'schedule.interval',
|
field: 'tagsText',
|
||||||
name: i18n.translate(
|
name: i18n.translate(
|
||||||
'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.intervalTitle',
|
'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.tagsText',
|
||||||
|
{ defaultMessage: 'Tags' }
|
||||||
|
),
|
||||||
|
sortable: false,
|
||||||
|
'data-test-subj': 'alertsTableCell-tagsText',
|
||||||
|
render: (_count: number, item: AlertTableItem) => (
|
||||||
|
<div className="eui-textTruncate" title={item.tagsText}>
|
||||||
|
{item.tagsText}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'schedule.interval',
|
||||||
|
width: '6%',
|
||||||
|
name: i18n.translate(
|
||||||
|
'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.scheduleTitle',
|
||||||
{ defaultMessage: 'Runs every' }
|
{ defaultMessage: 'Runs every' }
|
||||||
),
|
),
|
||||||
sortable: false,
|
sortable: false,
|
||||||
truncateText: false,
|
truncateText: false,
|
||||||
'data-test-subj': 'alertsTableCell-interval',
|
'data-test-subj': 'alertsTableCell-interval',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
width: '9%',
|
||||||
|
name: i18n.translate(
|
||||||
|
'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.actionsTitle',
|
||||||
|
{ defaultMessage: 'Actions' }
|
||||||
|
),
|
||||||
|
render: (item: AlertTableItem) => {
|
||||||
|
return (
|
||||||
|
<EuiFlexGroup wrap responsive={false} gutterSize="s">
|
||||||
|
<EuiFlexItem grow={false}>{item.actionsCount}</EuiFlexItem>
|
||||||
|
<EuiFlexItem grow={false}>
|
||||||
|
<div>
|
||||||
|
{item.muteAll ? (
|
||||||
|
<EuiBadge data-test-subj="mutedActionsBadge" color="hollow">
|
||||||
|
<FormattedMessage
|
||||||
|
id="xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.mutedBadge"
|
||||||
|
defaultMessage="Muted"
|
||||||
|
/>
|
||||||
|
</EuiBadge>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</EuiFlexItem>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
'data-test-subj': 'alertsTableCell-actions',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: '',
|
name: '',
|
||||||
width: '40px',
|
width: '10%',
|
||||||
render(item: AlertTableItem) {
|
render(item: AlertTableItem) {
|
||||||
return (
|
return (
|
||||||
|
<EuiFlexGroup justifyContent="spaceBetween" gutterSize="s">
|
||||||
|
<EuiFlexItem grow={false} className="alertSidebarItem">
|
||||||
|
<EuiFlexGroup justifyContent="flexEnd" gutterSize="s">
|
||||||
|
<EuiFlexItem grow={false}>
|
||||||
|
<EuiButtonIcon
|
||||||
|
color={'primary'}
|
||||||
|
title={i18n.translate(
|
||||||
|
'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.editButtonTooltip',
|
||||||
|
{ defaultMessage: 'Edit' }
|
||||||
|
)}
|
||||||
|
className="alertSidebarItem__action"
|
||||||
|
onClick={() => onRuleEdit(item)}
|
||||||
|
iconType={'pencil'}
|
||||||
|
aria-label={i18n.translate(
|
||||||
|
'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.editAriaLabel',
|
||||||
|
{ defaultMessage: 'Edit' }
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</EuiFlexItem>
|
||||||
|
<EuiFlexItem grow={false}>
|
||||||
|
<EuiButtonIcon
|
||||||
|
color={'danger'}
|
||||||
|
title={i18n.translate(
|
||||||
|
'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.deleteButtonTooltip',
|
||||||
|
{ defaultMessage: 'Delete' }
|
||||||
|
)}
|
||||||
|
className="alertSidebarItem__action"
|
||||||
|
onClick={() => setAlertsToDelete([item.id])}
|
||||||
|
iconType={'trash'}
|
||||||
|
aria-label={i18n.translate(
|
||||||
|
'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.deleteAriaLabel',
|
||||||
|
{ defaultMessage: 'Delete' }
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</EuiFlexItem>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
</EuiFlexItem>
|
||||||
|
<EuiFlexItem grow={false}>
|
||||||
<CollapsedItemActions
|
<CollapsedItemActions
|
||||||
key={item.id}
|
key={item.id}
|
||||||
item={item}
|
item={item}
|
||||||
onAlertChanged={() => loadAlertsData()}
|
onAlertChanged={() => loadAlertsData()}
|
||||||
setAlertsToDelete={setAlertsToDelete}
|
setAlertsToDelete={setAlertsToDelete}
|
||||||
|
onEditAlert={() => onRuleEdit(item)}
|
||||||
/>
|
/>
|
||||||
|
</EuiFlexItem>
|
||||||
|
</EuiFlexGroup>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -621,7 +712,7 @@ export const AlertsList: React.FunctionComponent = () => {
|
||||||
</EuiText>
|
</EuiText>
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
<EuiFlexItem grow={false}>
|
<EuiFlexItem grow={false}>
|
||||||
<EuiHealth color="primary" data-test-subj="totalActiveAlertsCount">
|
<EuiHealth color="success" data-test-subj="totalActiveAlertsCount">
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="xpack.triggersActionsUI.sections.alertsList.totalStausesActiveDescription"
|
id="xpack.triggersActionsUI.sections.alertsList.totalStausesActiveDescription"
|
||||||
defaultMessage="Active: {totalStausesActive}"
|
defaultMessage="Active: {totalStausesActive}"
|
||||||
|
@ -650,7 +741,7 @@ export const AlertsList: React.FunctionComponent = () => {
|
||||||
</EuiHealth>
|
</EuiHealth>
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
<EuiFlexItem grow={false}>
|
<EuiFlexItem grow={false}>
|
||||||
<EuiHealth color="success" data-test-subj="totalPendingAlertsCount">
|
<EuiHealth color="accent" data-test-subj="totalPendingAlertsCount">
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="xpack.triggersActionsUI.sections.alertsList.totalStausesPendingDescription"
|
id="xpack.triggersActionsUI.sections.alertsList.totalStausesPendingDescription"
|
||||||
defaultMessage="Pending: {totalStausesPending}"
|
defaultMessage="Pending: {totalStausesPending}"
|
||||||
|
@ -802,6 +893,17 @@ export const AlertsList: React.FunctionComponent = () => {
|
||||||
onSave={loadAlertsData}
|
onSave={loadAlertsData}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{editFlyoutVisible && currentRuleToEdit && (
|
||||||
|
<AlertEdit
|
||||||
|
initialAlert={currentRuleToEdit}
|
||||||
|
onClose={() => {
|
||||||
|
setEditFlyoutVisibility(false);
|
||||||
|
}}
|
||||||
|
actionTypeRegistry={actionTypeRegistry}
|
||||||
|
alertTypeRegistry={alertTypeRegistry}
|
||||||
|
onSave={loadAlertsData}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,20 +4,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.actCollapsedItemActions__item {
|
button[data-test-subj='deleteAlert'] {
|
||||||
padding: $euiSizeM;
|
color: $euiColorDanger;
|
||||||
}
|
|
||||||
|
|
||||||
.actCollapsedItemActions__delete {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
.actCollapsedItemActions__deleteIcon {
|
|
||||||
width: $euiSwitchWidthCompressed;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actCollapsedItemActions__deleteLabel {
|
|
||||||
padding-left: $euiSizeS;
|
|
||||||
padding-top: $euiSizeXS * .5;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,18 +8,7 @@
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { asyncScheduler } from 'rxjs';
|
import { asyncScheduler } from 'rxjs';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { FormattedMessage } from '@kbn/i18n/react';
|
import { EuiButtonIcon, EuiPopover, EuiContextMenu } from '@elastic/eui';
|
||||||
import {
|
|
||||||
EuiButtonIcon,
|
|
||||||
EuiPopover,
|
|
||||||
EuiContextMenuPanel,
|
|
||||||
EuiContextMenuItem,
|
|
||||||
EuiSwitch,
|
|
||||||
EuiHorizontalRule,
|
|
||||||
EuiText,
|
|
||||||
EuiSpacer,
|
|
||||||
EuiIcon,
|
|
||||||
} from '@elastic/eui';
|
|
||||||
|
|
||||||
import { AlertTableItem } from '../../../../types';
|
import { AlertTableItem } from '../../../../types';
|
||||||
import {
|
import {
|
||||||
|
@ -32,6 +21,7 @@ export type ComponentOpts = {
|
||||||
item: AlertTableItem;
|
item: AlertTableItem;
|
||||||
onAlertChanged: () => void;
|
onAlertChanged: () => void;
|
||||||
setAlertsToDelete: React.Dispatch<React.SetStateAction<string[]>>;
|
setAlertsToDelete: React.Dispatch<React.SetStateAction<string[]>>;
|
||||||
|
onEditAlert: (item: AlertTableItem) => void;
|
||||||
} & BulkOperationsComponentOpts;
|
} & BulkOperationsComponentOpts;
|
||||||
|
|
||||||
export const CollapsedItemActions: React.FunctionComponent<ComponentOpts> = ({
|
export const CollapsedItemActions: React.FunctionComponent<ComponentOpts> = ({
|
||||||
|
@ -42,6 +32,7 @@ export const CollapsedItemActions: React.FunctionComponent<ComponentOpts> = ({
|
||||||
unmuteAlert,
|
unmuteAlert,
|
||||||
muteAlert,
|
muteAlert,
|
||||||
setAlertsToDelete,
|
setAlertsToDelete,
|
||||||
|
onEditAlert,
|
||||||
}: ComponentOpts) => {
|
}: ComponentOpts) => {
|
||||||
const [isPopoverOpen, setIsPopoverOpen] = useState<boolean>(false);
|
const [isPopoverOpen, setIsPopoverOpen] = useState<boolean>(false);
|
||||||
const [isDisabled, setIsDisabled] = useState<boolean>(!item.enabled);
|
const [isDisabled, setIsDisabled] = useState<boolean>(!item.enabled);
|
||||||
|
@ -54,7 +45,7 @@ export const CollapsedItemActions: React.FunctionComponent<ComponentOpts> = ({
|
||||||
const button = (
|
const button = (
|
||||||
<EuiButtonIcon
|
<EuiButtonIcon
|
||||||
disabled={!item.isEditable}
|
disabled={!item.isEditable}
|
||||||
iconType="boxesVertical"
|
iconType="boxesHorizontal"
|
||||||
onClick={() => setIsPopoverOpen(!isPopoverOpen)}
|
onClick={() => setIsPopoverOpen(!isPopoverOpen)}
|
||||||
aria-label={i18n.translate(
|
aria-label={i18n.translate(
|
||||||
'xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.popoverButtonTitle',
|
'xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.popoverButtonTitle',
|
||||||
|
@ -63,58 +54,15 @@ export const CollapsedItemActions: React.FunctionComponent<ComponentOpts> = ({
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
const panels = [
|
||||||
<EuiPopover
|
{
|
||||||
button={button}
|
id: 0,
|
||||||
isOpen={isPopoverOpen}
|
hasFocus: false,
|
||||||
closePopover={() => setIsPopoverOpen(false)}
|
items: [
|
||||||
ownFocus
|
{
|
||||||
panelPaddingSize="none"
|
disabled: !(item.isEditable && !isDisabled) || !item.enabledInLicense,
|
||||||
data-test-subj="collapsedItemActions"
|
'data-test-subj': 'muteButton',
|
||||||
>
|
onClick: async () => {
|
||||||
<EuiContextMenuPanel className="actCollapsedItemActions" hasFocus={false}>
|
|
||||||
<div className="actCollapsedItemActions__item">
|
|
||||||
<EuiSwitch
|
|
||||||
name="disable"
|
|
||||||
disabled={!item.isEditable || !item.enabledInLicense}
|
|
||||||
compressed
|
|
||||||
checked={isDisabled}
|
|
||||||
data-test-subj="disableSwitch"
|
|
||||||
onChange={async () => {
|
|
||||||
const enabled = !isDisabled;
|
|
||||||
asyncScheduler.schedule(async () => {
|
|
||||||
if (enabled) {
|
|
||||||
await disableAlert({ ...item, enabled });
|
|
||||||
} else {
|
|
||||||
await enableAlert({ ...item, enabled });
|
|
||||||
}
|
|
||||||
onAlertChanged();
|
|
||||||
}, 10);
|
|
||||||
setIsDisabled(!isDisabled);
|
|
||||||
}}
|
|
||||||
label={
|
|
||||||
<FormattedMessage
|
|
||||||
id="xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.disableTitle"
|
|
||||||
defaultMessage="Disable"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<EuiSpacer size="xs" />
|
|
||||||
<EuiText color="subdued" size="xs">
|
|
||||||
<FormattedMessage
|
|
||||||
id="xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.disableHelpText"
|
|
||||||
defaultMessage="When disabled, the rule is not checked."
|
|
||||||
/>
|
|
||||||
</EuiText>
|
|
||||||
</div>
|
|
||||||
<div className="actCollapsedItemActions__item">
|
|
||||||
<EuiSwitch
|
|
||||||
name="mute"
|
|
||||||
checked={isMuted}
|
|
||||||
disabled={!(item.isEditable && !isDisabled) || !item.enabledInLicense}
|
|
||||||
compressed
|
|
||||||
data-test-subj="muteSwitch"
|
|
||||||
onChange={async () => {
|
|
||||||
const muteAll = isMuted;
|
const muteAll = isMuted;
|
||||||
asyncScheduler.schedule(async () => {
|
asyncScheduler.schedule(async () => {
|
||||||
if (muteAll) {
|
if (muteAll) {
|
||||||
|
@ -125,45 +73,82 @@ export const CollapsedItemActions: React.FunctionComponent<ComponentOpts> = ({
|
||||||
onAlertChanged();
|
onAlertChanged();
|
||||||
}, 10);
|
}, 10);
|
||||||
setIsMuted(!isMuted);
|
setIsMuted(!isMuted);
|
||||||
}}
|
setIsPopoverOpen(!isPopoverOpen);
|
||||||
label={
|
},
|
||||||
<FormattedMessage
|
name: isMuted
|
||||||
id="xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.muteTitle"
|
? i18n.translate(
|
||||||
defaultMessage="Mute"
|
'xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.unmuteTitle',
|
||||||
/>
|
{ defaultMessage: 'Unmute' }
|
||||||
|
)
|
||||||
|
: i18n.translate(
|
||||||
|
'xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.muteTitle',
|
||||||
|
{ defaultMessage: 'Mute' }
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disabled: !item.isEditable || !item.enabledInLicense,
|
||||||
|
'data-test-subj': 'disableButton',
|
||||||
|
onClick: async () => {
|
||||||
|
const enabled = !isDisabled;
|
||||||
|
asyncScheduler.schedule(async () => {
|
||||||
|
if (enabled) {
|
||||||
|
await disableAlert({ ...item, enabled });
|
||||||
|
} else {
|
||||||
|
await enableAlert({ ...item, enabled });
|
||||||
}
|
}
|
||||||
/>
|
onAlertChanged();
|
||||||
<EuiSpacer size="xs" />
|
}, 10);
|
||||||
<EuiText color="subdued" size="xs">
|
setIsDisabled(!isDisabled);
|
||||||
<FormattedMessage
|
setIsPopoverOpen(!isPopoverOpen);
|
||||||
id="xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.muteHelpText"
|
},
|
||||||
defaultMessage="When muted, the rule is checked, but no action is performed."
|
name: isDisabled
|
||||||
/>
|
? i18n.translate(
|
||||||
</EuiText>
|
'xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.enableTitle',
|
||||||
</div>
|
{ defaultMessage: 'Enable' }
|
||||||
<EuiHorizontalRule margin="none" />
|
)
|
||||||
<EuiContextMenuItem
|
: i18n.translate(
|
||||||
disabled={!item.isEditable}
|
'xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.disableTitle',
|
||||||
data-test-subj="deleteAlert"
|
{ defaultMessage: 'Disable' }
|
||||||
onClick={() => setAlertsToDelete([item.id])}
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disabled: !item.isEditable,
|
||||||
|
'data-test-subj': 'editAlert',
|
||||||
|
onClick: () => {
|
||||||
|
setIsPopoverOpen(!isPopoverOpen);
|
||||||
|
onEditAlert(item);
|
||||||
|
},
|
||||||
|
name: i18n.translate(
|
||||||
|
'xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.editTitle',
|
||||||
|
{ defaultMessage: 'Edit rule' }
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disabled: !item.isEditable,
|
||||||
|
'data-test-subj': 'deleteAlert',
|
||||||
|
onClick: () => {
|
||||||
|
setIsPopoverOpen(!isPopoverOpen);
|
||||||
|
setAlertsToDelete([item.id]);
|
||||||
|
},
|
||||||
|
name: i18n.translate(
|
||||||
|
'xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.deleteRuleTitle',
|
||||||
|
{ defaultMessage: 'Delete rule' }
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EuiPopover
|
||||||
|
button={button}
|
||||||
|
isOpen={isPopoverOpen}
|
||||||
|
closePopover={() => setIsPopoverOpen(false)}
|
||||||
|
ownFocus
|
||||||
|
panelPaddingSize="none"
|
||||||
|
data-test-subj="collapsedItemActions"
|
||||||
>
|
>
|
||||||
<div className="actCollapsedItemActions__delete">
|
<EuiContextMenu initialPanelId={0} panels={panels} className="actCollapsedItemActions" />
|
||||||
<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>
|
</EuiPopover>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { asyncScheduler } from 'rxjs';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { EuiSwitch } from '@elastic/eui';
|
||||||
|
|
||||||
|
import { Alert, AlertTableItem } from '../../../../types';
|
||||||
|
|
||||||
|
interface ComponentOpts {
|
||||||
|
item: AlertTableItem;
|
||||||
|
onAlertChanged: () => void;
|
||||||
|
enableAlert: (alert: Alert) => Promise<void>;
|
||||||
|
disableAlert: (alert: Alert) => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RuleEnabledSwitch: React.FunctionComponent<ComponentOpts> = ({
|
||||||
|
item,
|
||||||
|
onAlertChanged,
|
||||||
|
disableAlert,
|
||||||
|
enableAlert,
|
||||||
|
}: ComponentOpts) => {
|
||||||
|
const [isEnabled, setIsEnabled] = useState<boolean>(!item.enabled);
|
||||||
|
useEffect(() => {
|
||||||
|
setIsEnabled(item.enabled);
|
||||||
|
}, [item.enabled]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EuiSwitch
|
||||||
|
name="enable"
|
||||||
|
disabled={!item.isEditable || !item.enabledInLicense}
|
||||||
|
compressed
|
||||||
|
checked={isEnabled}
|
||||||
|
data-test-subj="enableSwitch"
|
||||||
|
onChange={async () => {
|
||||||
|
const enabled = isEnabled;
|
||||||
|
asyncScheduler.schedule(async () => {
|
||||||
|
if (enabled) {
|
||||||
|
await disableAlert({ ...item, enabled });
|
||||||
|
} else {
|
||||||
|
await enableAlert({ ...item, enabled });
|
||||||
|
}
|
||||||
|
onAlertChanged();
|
||||||
|
}, 10);
|
||||||
|
setIsEnabled(!isEnabled);
|
||||||
|
}}
|
||||||
|
label=""
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
|
@ -177,11 +177,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
||||||
|
|
||||||
await testSubjects.click('collapsedItemActions');
|
await testSubjects.click('collapsedItemActions');
|
||||||
|
|
||||||
await pageObjects.triggersActionsUI.toggleSwitch('disableSwitch');
|
await testSubjects.click('disableButton');
|
||||||
|
|
||||||
await pageObjects.triggersActionsUI.ensureRuleActionToggleApplied(
|
await pageObjects.triggersActionsUI.ensureRuleActionToggleApplied(
|
||||||
createdAlert.name,
|
createdAlert.name,
|
||||||
'disableSwitch',
|
'enableSwitch',
|
||||||
'true'
|
'true'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -194,10 +194,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
||||||
|
|
||||||
await testSubjects.click('collapsedItemActions');
|
await testSubjects.click('collapsedItemActions');
|
||||||
|
|
||||||
await pageObjects.triggersActionsUI.toggleSwitch('disableSwitch');
|
await testSubjects.click('disableButton');
|
||||||
await pageObjects.triggersActionsUI.ensureRuleActionToggleApplied(
|
await pageObjects.triggersActionsUI.ensureRuleActionToggleApplied(
|
||||||
createdAlert.name,
|
createdAlert.name,
|
||||||
'disableSwitch',
|
'enableSwitch',
|
||||||
'false'
|
'false'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -209,12 +209,13 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
||||||
|
|
||||||
await testSubjects.click('collapsedItemActions');
|
await testSubjects.click('collapsedItemActions');
|
||||||
|
|
||||||
await pageObjects.triggersActionsUI.toggleSwitch('muteSwitch');
|
await testSubjects.click('muteButton');
|
||||||
await pageObjects.triggersActionsUI.ensureRuleActionToggleApplied(
|
|
||||||
createdAlert.name,
|
await retry.tryForTime(30000, async () => {
|
||||||
'muteSwitch',
|
await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name);
|
||||||
'true'
|
const muteBadge = await testSubjects.find('mutedActionsBadge');
|
||||||
);
|
expect(await muteBadge.isDisplayed()).to.eql(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should unmute single alert', async () => {
|
it('should unmute single alert', async () => {
|
||||||
|
@ -226,12 +227,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
||||||
|
|
||||||
await testSubjects.click('collapsedItemActions');
|
await testSubjects.click('collapsedItemActions');
|
||||||
|
|
||||||
await pageObjects.triggersActionsUI.toggleSwitch('muteSwitch');
|
await testSubjects.click('muteButton');
|
||||||
await pageObjects.triggersActionsUI.ensureRuleActionToggleApplied(
|
await retry.tryForTime(30000, async () => {
|
||||||
createdAlert.name,
|
await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name);
|
||||||
'muteSwitch',
|
await testSubjects.missingOrFail('mutedActionsBadge');
|
||||||
'false'
|
});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should delete single alert', async () => {
|
it('should delete single alert', async () => {
|
||||||
|
@ -273,11 +273,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
||||||
|
|
||||||
await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name);
|
await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name);
|
||||||
|
|
||||||
await testSubjects.click('collapsedItemActions');
|
await retry.tryForTime(30000, async () => {
|
||||||
|
await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name);
|
||||||
const muteSwitch = await testSubjects.find('muteSwitch');
|
const muteBadge = await testSubjects.find('mutedActionsBadge');
|
||||||
const isChecked = await muteSwitch.getAttribute('aria-checked');
|
expect(await muteBadge.isDisplayed()).to.eql(true);
|
||||||
expect(isChecked).to.eql('true');
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should unmute all selection', async () => {
|
it('should unmute all selection', async () => {
|
||||||
|
@ -296,13 +296,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
||||||
// Mute all button shows after clicking unmute all
|
// Mute all button shows after clicking unmute all
|
||||||
await testSubjects.existOrFail('muteAll');
|
await testSubjects.existOrFail('muteAll');
|
||||||
|
|
||||||
|
await retry.tryForTime(30000, async () => {
|
||||||
await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name);
|
await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name);
|
||||||
|
await testSubjects.missingOrFail('mutedActionsBadge');
|
||||||
await testSubjects.click('collapsedItemActions');
|
});
|
||||||
|
|
||||||
const muteSwitch = await testSubjects.find('muteSwitch');
|
|
||||||
const isChecked = await muteSwitch.getAttribute('aria-checked');
|
|
||||||
expect(isChecked).to.eql('false');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should disable all selection', async () => {
|
it('should disable all selection', async () => {
|
||||||
|
@ -319,13 +316,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
||||||
// Enable all button shows after clicking disable all
|
// Enable all button shows after clicking disable all
|
||||||
await testSubjects.existOrFail('enableAll');
|
await testSubjects.existOrFail('enableAll');
|
||||||
|
|
||||||
await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name);
|
await pageObjects.triggersActionsUI.ensureRuleActionToggleApplied(
|
||||||
|
createdAlert.name,
|
||||||
await testSubjects.click('collapsedItemActions');
|
'enableSwitch',
|
||||||
|
'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 () => {
|
it('should enable all selection', async () => {
|
||||||
|
@ -344,13 +339,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
||||||
// Disable all button shows after clicking enable all
|
// Disable all button shows after clicking enable all
|
||||||
await testSubjects.existOrFail('disableAll');
|
await testSubjects.existOrFail('disableAll');
|
||||||
|
|
||||||
await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name);
|
await pageObjects.triggersActionsUI.ensureRuleActionToggleApplied(
|
||||||
|
createdAlert.name,
|
||||||
await testSubjects.click('collapsedItemActions');
|
'enableSwitch',
|
||||||
|
'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 () => {
|
it('should delete all selection', async () => {
|
||||||
|
|
|
@ -146,23 +146,23 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should disable the alert', async () => {
|
it('should disable the alert', async () => {
|
||||||
const disableSwitch = await testSubjects.find('disableSwitch');
|
const enableSwitch = await testSubjects.find('enableSwitch');
|
||||||
|
|
||||||
const isChecked = await disableSwitch.getAttribute('aria-checked');
|
const isChecked = await enableSwitch.getAttribute('aria-checked');
|
||||||
expect(isChecked).to.eql('false');
|
expect(isChecked).to.eql('true');
|
||||||
|
|
||||||
await disableSwitch.click();
|
await enableSwitch.click();
|
||||||
|
|
||||||
const disableSwitchAfterDisabling = await testSubjects.find('disableSwitch');
|
const disableSwitchAfterDisabling = await testSubjects.find('enableSwitch');
|
||||||
const isCheckedAfterDisabling = await disableSwitchAfterDisabling.getAttribute(
|
const isCheckedAfterDisabling = await disableSwitchAfterDisabling.getAttribute(
|
||||||
'aria-checked'
|
'aria-checked'
|
||||||
);
|
);
|
||||||
expect(isCheckedAfterDisabling).to.eql('true');
|
expect(isCheckedAfterDisabling).to.eql('false');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shouldnt allow you to mute a disabled alert', async () => {
|
it('shouldnt allow you to mute a disabled alert', async () => {
|
||||||
const disabledDisableSwitch = await testSubjects.find('disableSwitch');
|
const disabledEnableSwitch = await testSubjects.find('enableSwitch');
|
||||||
expect(await disabledDisableSwitch.getAttribute('aria-checked')).to.eql('true');
|
expect(await disabledEnableSwitch.getAttribute('aria-checked')).to.eql('false');
|
||||||
|
|
||||||
const muteSwitch = await testSubjects.find('muteSwitch');
|
const muteSwitch = await testSubjects.find('muteSwitch');
|
||||||
expect(await muteSwitch.getAttribute('aria-checked')).to.eql('false');
|
expect(await muteSwitch.getAttribute('aria-checked')).to.eql('false');
|
||||||
|
@ -177,18 +177,18 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reenable a disabled the alert', async () => {
|
it('should reenable a disabled the alert', async () => {
|
||||||
const disableSwitch = await testSubjects.find('disableSwitch');
|
const enableSwitch = await testSubjects.find('enableSwitch');
|
||||||
|
|
||||||
const isChecked = await disableSwitch.getAttribute('aria-checked');
|
const isChecked = await enableSwitch.getAttribute('aria-checked');
|
||||||
expect(isChecked).to.eql('true');
|
expect(isChecked).to.eql('false');
|
||||||
|
|
||||||
await disableSwitch.click();
|
await enableSwitch.click();
|
||||||
|
|
||||||
const disableSwitchAfterReenabling = await testSubjects.find('disableSwitch');
|
const disableSwitchAfterReenabling = await testSubjects.find('enableSwitch');
|
||||||
const isCheckedAfterDisabling = await disableSwitchAfterReenabling.getAttribute(
|
const isCheckedAfterDisabling = await disableSwitchAfterReenabling.getAttribute(
|
||||||
'aria-checked'
|
'aria-checked'
|
||||||
);
|
);
|
||||||
expect(isCheckedAfterDisabling).to.eql('false');
|
expect(isCheckedAfterDisabling).to.eql('true');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should mute the alert', async () => {
|
it('should mute the alert', async () => {
|
||||||
|
|
|
@ -190,8 +190,6 @@ export function TriggersActionsPageProvider({ getService }: FtrProviderContext)
|
||||||
) {
|
) {
|
||||||
await retry.tryForTime(30000, async () => {
|
await retry.tryForTime(30000, async () => {
|
||||||
await this.searchAlerts(ruleName);
|
await this.searchAlerts(ruleName);
|
||||||
await testSubjects.click('collapsedItemActions');
|
|
||||||
|
|
||||||
const switchControl = await testSubjects.find(switchName);
|
const switchControl = await testSubjects.find(switchName);
|
||||||
const isChecked = await switchControl.getAttribute('aria-checked');
|
const isChecked = await switchControl.getAttribute('aria-checked');
|
||||||
expect(isChecked).to.eql(shouldBeCheckedAsString);
|
expect(isChecked).to.eql(shouldBeCheckedAsString);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue