Skip to content

Commit

Permalink
[RAM][Observability] Add alert fields table to Observability flyout (e…
Browse files Browse the repository at this point in the history
…lastic#174685)

## Summary

Adds the alert fields table from
elastic#172830 to Observability alert
flyouts (triggered by the `View alert details` action).


![image](https://github.com/elastic/kibana/assets/18363145/7e10517e-a5b6-497a-91c8-5bc2bc88ad69)

### Checklist

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

---------

Co-authored-by: Xavier Mouligneau <xavier.mouligneau@elastic.co>
  • Loading branch information
2 people authored and CoenWarmer committed Feb 15, 2024
1 parent ad485a1 commit 8590041
Show file tree
Hide file tree
Showing 12 changed files with 351 additions and 172 deletions.
41 changes: 35 additions & 6 deletions packages/kbn-alerts-ui-shared/src/alert_fields_table/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { css } from '@emotion/react';
import React, { memo, useCallback, useMemo, useState } from 'react';
import { Alert } from '@kbn/alerting-types';
import { euiThemeVars } from '@kbn/ui-theme';
import { EuiBasicTableColumn } from '@elastic/eui/src/components/basic_table/basic_table';

export const search = {
box: {
Expand All @@ -28,7 +29,7 @@ export const search = {
},
};

const columns = [
const columns: Array<EuiBasicTableColumn<AlertField>> = [
{
field: 'key',
name: i18n.translate('alertsUIShared.alertFieldsTable.field', {
Expand Down Expand Up @@ -86,18 +87,46 @@ const useFieldBrowserPagination = () => {
};
};

type AlertField = Exclude<
{
[K in keyof Alert]: { key: K; value: Alert[K] };
}[keyof Alert],
undefined
>;

export interface AlertFieldsTableProps {
/**
* The raw alert object
*/
alert: Alert;
/**
* A list of alert field keys to be shown in the table.
* When not defined, all the fields are shown.
*/
fields?: Array<keyof Alert>;
}

export const AlertFieldsTable = memo(({ alert }: AlertFieldsTableProps) => {
/**
* A paginated, filterable table to show alert object fields
*/
export const AlertFieldsTable = memo(({ alert, fields }: AlertFieldsTableProps) => {
const { onTableChange, paginationTableProp } = useFieldBrowserPagination();
const items = useMemo(() => {
let _items = Object.entries(alert).map(
([key, value]) =>
({
key,
value,
} as AlertField)
);
if (fields?.length) {
_items = _items.filter((f) => fields.includes(f.key));
}
return _items;
}, [alert, fields]);
return (
<EuiInMemoryTable
items={Object.entries(alert).map(([key, value]) => ({
key,
value: Array.isArray(value) ? value?.[0] : value,
}))}
items={items}
itemId="key"
columns={columns}
onTableChange={onTableChange}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
* 2.0.
*/

import React from 'react';
import React, { ComponentProps } from 'react';
import * as useUiSettingHook from '@kbn/kibana-react-plugin/public/ui_settings/use_ui_setting';
import { createObservabilityRuleTypeRegistryMock } from '../../rules/observability_rule_type_registry_mock';
import { render } from '../../utils/test_helper';
import { AlertsFlyout } from './alerts_flyout';
import type { TopAlert } from '../../typings/alerts';

const rawAlert = {} as ComponentProps<typeof AlertsFlyout>['rawAlert'];

describe('AlertsFlyout', () => {
jest
.spyOn(useUiSettingHook, 'useUiSetting')
Expand All @@ -22,6 +24,7 @@ describe('AlertsFlyout', () => {
const flyout = render(
<AlertsFlyout
alert={activeAlert}
rawAlert={rawAlert}
observabilityRuleTypeRegistry={observabilityRuleTypeRegistryMock}
onClose={jest.fn()}
/>
Expand All @@ -34,6 +37,7 @@ describe('AlertsFlyout', () => {
const flyout = render(
<AlertsFlyout
alert={recoveredAlert}
rawAlert={rawAlert}
observabilityRuleTypeRegistry={observabilityRuleTypeRegistryMock}
onClose={jest.fn()}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import React, { useMemo } from 'react';
import { EuiFlyout, EuiFlyoutHeader, EuiFlyoutProps } from '@elastic/eui';
import { ALERT_UUID } from '@kbn/rule-data-utils';

import { EcsFieldsResponse } from '@kbn/rule-registry-plugin/common/search_strategy';
import { AlertsFlyoutHeader } from './alerts_flyout_header';
import { AlertsFlyoutBody } from './alerts_flyout_body';
import { AlertsFlyoutFooter } from './alerts_flyout_footer';
Expand All @@ -18,6 +19,7 @@ import type { TopAlert } from '../../typings/alerts';

type AlertsFlyoutProps = {
alert?: TopAlert;
rawAlert?: EcsFieldsResponse;
alerts?: Array<Record<string, unknown>>;
isInApp?: boolean;
observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry;
Expand All @@ -26,6 +28,7 @@ type AlertsFlyoutProps = {

export function AlertsFlyout({
alert,
rawAlert,
alerts,
isInApp = false,
observabilityRuleTypeRegistry,
Expand All @@ -41,16 +44,16 @@ export function AlertsFlyout({
if (!alertData) {
alertData = decoratedAlerts?.find((a) => a.fields[ALERT_UUID] === selectedAlertId) as TopAlert;
}
if (!alertData) {
if (!alertData || !rawAlert) {
return null;
}

return (
<EuiFlyout className="oblt__flyout" onClose={onClose} size="s" data-test-subj="alertsFlyout">
<EuiFlyout className="oblt__flyout" onClose={onClose} size="m" data-test-subj="alertsFlyout">
<EuiFlyoutHeader hasBorder>
<AlertsFlyoutHeader alert={alertData} />
</EuiFlyoutHeader>
<AlertsFlyoutBody alert={alertData} />
<AlertsFlyoutBody alert={alertData} rawAlert={rawAlert} />
<AlertsFlyoutFooter alert={alertData} isInApp={isInApp} />
</EuiFlyout>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,69 @@ import { AlertsFlyoutBody } from './alerts_flyout_body';
import { inventoryThresholdAlertEs } from '../../rules/fixtures/example_alerts';
import { parseAlert } from '../../pages/alerts/helpers/parse_alert';
import { RULE_DETAILS_PAGE_ID } from '../../pages/rule_details/constants';
import { fireEvent } from '@testing-library/react';

const tabsData = [
{ name: 'Overview', subj: 'overviewTab' },
{ name: 'Table', subj: 'tableTab' },
];

describe('AlertsFlyoutBody', () => {
jest
.spyOn(useUiSettingHook, 'useUiSetting')
.mockImplementation(() => 'MMM D, YYYY @ HH:mm:ss.SSS');
const observabilityRuleTypeRegistryMock = createObservabilityRuleTypeRegistryMock();
let flyout: ReturnType<typeof render>;

const setup = (id: string) => {
const alert = parseAlert(observabilityRuleTypeRegistryMock)(inventoryThresholdAlertEs);
return render(<AlertsFlyoutBody alert={alert} id={id} />);
flyout = render(
<AlertsFlyoutBody rawAlert={inventoryThresholdAlertEs} alert={alert} id={id} />
);
};

it('should render View rule detail link', async () => {
const flyout = setup('test');
setup('test');
expect(flyout.getByTestId('viewRuleDetailsFlyout')).toBeInTheDocument();
});

it('should NOT render View rule detail link for RULE_DETAILS_PAGE_ID', async () => {
const flyout = setup(RULE_DETAILS_PAGE_ID);
setup(RULE_DETAILS_PAGE_ID);
expect(flyout.queryByTestId('viewRuleDetailsFlyout')).not.toBeInTheDocument();
});

describe('tabs', () => {
beforeEach(() => {
setup('test');
});

tabsData.forEach(({ name: tab }) => {
test(`should render the ${tab} tab`, () => {
flyout.debug();
expect(flyout.getByText(tab)).toBeTruthy();
});
});

test('the Overview tab should be selected by default', () => {
expect(
flyout.container.querySelector(
'[data-test-subj="defaultAlertFlyoutTabs"] .euiTab-isSelected .euiTab__content'
)!.innerHTML
).toContain('Overview');
});

tabsData.forEach(({ subj, name }) => {
test(`should render the ${name} tab panel`, () => {
const tab = flyout.container.querySelector(
`[data-test-subj="defaultAlertFlyoutTabs"] [role="tablist"] [data-test-subj="${subj}"]`
);
fireEvent.click(tab!);
expect(
flyout.container.querySelector(
`[data-test-subj="defaultAlertFlyoutTabs"] [role="tabpanel"] [data-test-subj="${subj}Panel"]`
)
).toBeTruthy();
});
});
});
});
Loading

0 comments on commit 8590041

Please sign in to comment.