Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import {
EuiPopover,
EuiToolTip,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import styled from 'styled-components';
import React, { Suspense, useMemo, useState, useCallback, useEffect } from 'react';
import usePrevious from 'react-use/lib/usePrevious';
Expand Down Expand Up @@ -66,6 +65,7 @@ import { getDefaultCellActions } from './default_cell_actions';
import { LazyAlertsFlyout } from '../..';
import { parseAlert } from './parse_alert';
import { CoreStart } from '../../../../../../src/core/public';
import { translations } from './translations';

const ALERT_DURATION: typeof ALERT_DURATION_TYPED = ALERT_DURATION_NON_TYPED;
const ALERT_REASON: typeof ALERT_REASON_TYPED = ALERT_REASON_NON_TYPED;
Expand Down Expand Up @@ -115,33 +115,25 @@ export const columns: Array<
> = [
{
columnHeaderType: 'not-filtered',
displayAsText: i18n.translate('xpack.observability.alertsTGrid.statusColumnDescription', {
defaultMessage: 'Alert Status',
}),
displayAsText: translations.statusColumnDescription,
id: ALERT_STATUS,
initialWidth: 110,
},
{
columnHeaderType: 'not-filtered',
displayAsText: i18n.translate('xpack.observability.alertsTGrid.lastUpdatedColumnDescription', {
defaultMessage: 'Last updated',
}),
displayAsText: translations.lastUpdatedColumnDescription,
id: TIMESTAMP,
initialWidth: 230,
},
{
columnHeaderType: 'not-filtered',
displayAsText: i18n.translate('xpack.observability.alertsTGrid.durationColumnDescription', {
defaultMessage: 'Duration',
}),
displayAsText: translations.durationColumnDescription,
id: ALERT_DURATION,
initialWidth: 116,
},
{
columnHeaderType: 'not-filtered',
displayAsText: i18n.translate('xpack.observability.alertsTGrid.reasonColumnDescription', {
defaultMessage: 'Reason',
}),
displayAsText: translations.reasonColumnDescription,
id: ALERT_REASON,
linkField: '*',
},
Expand Down Expand Up @@ -249,73 +241,61 @@ function ObservabilityActions({
];
}, [afterCaseSelection, casePermissions, timelines, event, statusActionItems, alertPermissions]);

const viewDetailsTextLabel = i18n.translate(
'xpack.observability.alertsTable.viewDetailsTextLabel',
{
defaultMessage: 'View details',
}
);
const viewInAppTextLabel = i18n.translate('xpack.observability.alertsTable.viewInAppTextLabel', {
defaultMessage: 'View in app',
});
const moreActionsTextLabel = i18n.translate(
'xpack.observability.alertsTable.moreActionsTextLabel',
{
defaultMessage: 'More actions',
}
);
const actionsToolTip =
actionsMenuItems.length <= 0
? translations.notEnoughPermissions
: translations.moreActionsTextLabel;

return (
<>
<EuiFlexGroup gutterSize="none" responsive={false}>
<EuiFlexItem>
<EuiToolTip content={viewDetailsTextLabel}>
<EuiToolTip content={translations.viewDetailsTextLabel}>
<EuiButtonIcon
size="s"
iconType="expand"
color="text"
onClick={() => setFlyoutAlert(alert)}
data-test-subj="openFlyoutButton"
aria-label={viewDetailsTextLabel}
aria-label={translations.viewDetailsTextLabel}
/>
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem>
<EuiToolTip content={viewInAppTextLabel}>
<EuiToolTip content={translations.viewInAppTextLabel}>
<EuiButtonIcon
size="s"
href={prepend(alert.link ?? '')}
iconType="eye"
color="text"
aria-label={viewInAppTextLabel}
aria-label={translations.viewInAppTextLabel}
/>
</EuiToolTip>
</EuiFlexItem>
{actionsMenuItems.length > 0 && (
<EuiFlexItem>
<EuiPopover
button={
<EuiToolTip content={moreActionsTextLabel}>
<EuiButtonIcon
display="empty"
size="s"
color="text"
iconType="boxesHorizontal"
aria-label={moreActionsTextLabel}
onClick={() => toggleActionsPopover(eventId)}
data-test-subj="alerts-table-row-action-more"
/>
</EuiToolTip>
}
isOpen={openActionsPopoverId === eventId}
closePopover={closeActionsPopover}
panelPaddingSize="none"
anchorPosition="downLeft"
>
<EuiContextMenuPanel size="s" items={actionsMenuItems} />
</EuiPopover>
</EuiFlexItem>
)}
<EuiFlexItem>
<EuiPopover
button={
<EuiToolTip content={actionsToolTip}>
<EuiButtonIcon
display="empty"
disabled={actionsMenuItems.length <= 0}
size="s"
color="text"
iconType="boxesHorizontal"
aria-label={actionsToolTip}
onClick={() => toggleActionsPopover(eventId)}
data-test-subj="alerts-table-row-action-more"
/>
</EuiToolTip>
}
isOpen={openActionsPopoverId === eventId}
closePopover={closeActionsPopover}
panelPaddingSize="none"
anchorPosition="downLeft"
>
<EuiContextMenuPanel size="s" items={actionsMenuItems} />
</EuiPopover>
</EuiFlexItem>
</EuiFlexGroup>
</>
);
Expand Down Expand Up @@ -363,13 +343,7 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) {
id: 'expand',
width: 120,
headerCellRender: () => {
return (
<EventsThContent>
{i18n.translate('xpack.observability.alertsTable.actionsTextLabel', {
defaultMessage: 'Actions',
})}
</EventsThContent>
);
return <EventsThContent>{translations.actionsTextLabel}</EventsThContent>;
},
rowCellRender: (actionProps: ActionProps) => {
return (
Expand Down Expand Up @@ -400,12 +374,8 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) {
hasAlertsCrudPermissions,
indexNames,
itemsPerPageOptions: [10, 25, 50],
loadingText: i18n.translate('xpack.observability.alertsTable.loadingTextLabel', {
defaultMessage: 'loading alerts',
}),
footerText: i18n.translate('xpack.observability.alertsTable.footerTextLabel', {
defaultMessage: 'alerts',
}),
loadingText: translations.loadingTextLabel,
footerText: translations.footerTextLabel,
query: {
query: `${ALERT_WORKFLOW_STATUS}: ${workflowStatus}${kuery !== '' ? ` and ${kuery}` : ''}`,
language: 'kuery',
Expand All @@ -424,11 +394,7 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) {
filterStatus: workflowStatus as AlertWorkflowStatus,
leadingControlColumns,
trailingControlColumns,
unit: (totalAlerts: number) =>
i18n.translate('xpack.observability.alertsTable.showingAlertsTitle', {
values: { totalAlerts },
defaultMessage: '{totalAlerts, plural, =1 {alert} other {alerts}}',
}),
unit: (totalAlerts: number) => translations.showingAlertsTitle(totalAlerts),
};
}, [
casePermissions,
Expand All @@ -443,6 +409,7 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) {
leadingControlColumns,
deletedEventIds,
]);

const handleFlyoutClose = () => setFlyoutAlert(undefined);
const { observabilityRuleTypeRegistry } = usePluginContext();

Expand Down
61 changes: 61 additions & 0 deletions x-pack/plugins/observability/public/pages/alerts/translations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 I like the idea of moving translation constants to a separate file. Not sure about committing to adopting it as a convention just yet. There's some advantages to keeping them all in-line in the file: sometimes we debug by grepping for certain text strings and it's nice to be taken right to the code, and there are cases where we have to use <FormattedMessage> that might be awkward to import from a separate module. But let's do it here and see how maintainability works out.

* 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 { i18n } from '@kbn/i18n';

export const translations = {
viewDetailsTextLabel: i18n.translate('xpack.observability.alertsTable.viewDetailsTextLabel', {
defaultMessage: 'View details',
}),
viewInAppTextLabel: i18n.translate('xpack.observability.alertsTable.viewInAppTextLabel', {
defaultMessage: 'View in app',
}),
moreActionsTextLabel: i18n.translate('xpack.observability.alertsTable.moreActionsTextLabel', {
defaultMessage: 'More actions',
}),
notEnoughPermissions: i18n.translate('xpack.observability.alertsTable.notEnoughPermissions', {
defaultMessage: 'Additional privileges required',
}),
statusColumnDescription: i18n.translate(
'xpack.observability.alertsTGrid.statusColumnDescription',
{
defaultMessage: 'Alert Status',
}
),
lastUpdatedColumnDescription: i18n.translate(
'xpack.observability.alertsTGrid.lastUpdatedColumnDescription',
{
defaultMessage: 'Last updated',
}
),
durationColumnDescription: i18n.translate(
'xpack.observability.alertsTGrid.durationColumnDescription',
{
defaultMessage: 'Duration',
}
),
reasonColumnDescription: i18n.translate(
'xpack.observability.alertsTGrid.reasonColumnDescription',
{
defaultMessage: 'Reason',
}
),
actionsTextLabel: i18n.translate('xpack.observability.alertsTable.actionsTextLabel', {
defaultMessage: 'Actions',
}),
loadingTextLabel: i18n.translate('xpack.observability.alertsTable.loadingTextLabel', {
defaultMessage: 'loading alerts',
}),
footerTextLabel: i18n.translate('xpack.observability.alertsTable.footerTextLabel', {
defaultMessage: 'alerts',
}),
showingAlertsTitle: (totalAlerts: number) =>
i18n.translate('xpack.observability.alertsTable.showingAlertsTitle', {
values: { totalAlerts },
defaultMessage: '{totalAlerts, plural, =1 {alert} other {alerts}}',
}),
};
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,13 @@ export function ObservabilityAlertsCommonProvider({
return buttonText.substring(0, buttonText.indexOf('\n'));
};

const getActionsButtonByIndex = async (index: number) => {
const actionsOverflowButtons = await find.allByCssSelector(
'[data-test-subj="alerts-table-row-action-more"]'
);
return actionsOverflowButtons[index] || null;
};

return {
getQueryBar,
clearQueryBar,
Expand Down Expand Up @@ -236,5 +243,6 @@ export function ObservabilityAlertsCommonProvider({
typeInQueryBar,
openActionsMenuForRow,
getTimeRange,
getActionsButtonByIndex,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -214,5 +214,28 @@ export default ({ getService }: FtrProviderContext) => {
});
});
});

describe('Actions Button', () => {
before(async () => {
await observability.users.setTestUserRole(
observability.users.defineBasicObservabilityRole({
observabilityCases: ['read'],
logs: ['read'],
})
);
await esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs');
await observability.alerts.common.navigateToTimeWithData();
});

after(async () => {
await observability.users.restoreDefaultTestUserRole();
await esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs');
});

it('Is disabled when a user has only read privilages', async () => {
const actionsButton = await observability.alerts.common.getActionsButtonByIndex(0);
expect(await actionsButton.getAttribute('disabled')).to.be('true');
});
});
});
};