Skip to content

Commit

Permalink
[RAM] Add the "updated at" feature in new alerts table (#136949)
Browse files Browse the repository at this point in the history
* first commit

* fix and add test

* Update x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/translations.ts

Co-authored-by: Xavier Mouligneau <[email protected]>

* Update x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/translations.ts

Co-authored-by: Xavier Mouligneau <[email protected]>

Co-authored-by: Xavier Mouligneau <[email protected]>
Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
3 people authored Jul 25, 2022
1 parent 1377ef2 commit 8b21e25
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import userEvent from '@testing-library/user-event';
import { EcsFieldsResponse } from '@kbn/rule-registry-plugin/common/search_strategy';

import { AlertsTable } from './alerts_table';
import { AlertsField } from '../../../types';
import { AlertsField, AlertsTableProps } from '../../../types';
import { EuiButtonIcon, EuiFlexItem } from '@elastic/eui';
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';

jest.mock('@kbn/data-plugin/public');

Expand Down Expand Up @@ -88,11 +89,18 @@ describe('AlertsTable', () => {
useFetchAlertsData,
visibleColumns: columns.map((c) => c.id),
'data-test-subj': 'testTable',
updatedAt: Date.now(),
};

const AlertsTableWithLocale: React.FunctionComponent<AlertsTableProps> = (props) => (
<IntlProvider locale="en">
<AlertsTable {...props} />
</IntlProvider>
);

describe('Alerts table UI', () => {
it('should support sorting', async () => {
const renderResult = render(<AlertsTable {...tableProps} />);
const renderResult = render(<AlertsTableWithLocale {...tableProps} />);
userEvent.click(renderResult.container.querySelector('.euiDataGridHeaderCell__button')!);
userEvent.click(renderResult.getByTestId(`dataGridHeaderCellActionGroup-${columns[0].id}`));
userEvent.click(renderResult.getByTitle('Sort A-Z'));
Expand All @@ -102,14 +110,19 @@ describe('AlertsTable', () => {
});

it('should support pagination', async () => {
const renderResult = render(<AlertsTable {...tableProps} />);
const renderResult = render(<AlertsTableWithLocale {...tableProps} />);
userEvent.click(renderResult.getByTestId('pagination-button-1'));
expect(fetchAlertsData.onPageChange).toHaveBeenCalledWith({ pageIndex: 1, pageSize: 1 });
});

it('should show when it was updated', () => {
const { getByTestId } = render(<AlertsTableWithLocale {...tableProps} />);
expect(getByTestId('toolbar-updated-at')).not.toBe(null);
});

describe('leading control columns', () => {
it('should return at least the flyout action control', async () => {
const wrapper = render(<AlertsTable {...tableProps} />);
const wrapper = render(<AlertsTableWithLocale {...tableProps} />);
expect(wrapper.getByTestId('expandColumnHeaderLabel').textContent).toBe('Actions');
});

Expand All @@ -125,7 +138,7 @@ describe('AlertsTable', () => {
},
],
};
const wrapper = render(<AlertsTable {...customTableProps} />);
const wrapper = render(<AlertsTableWithLocale {...customTableProps} />);
expect(wrapper.queryByTestId('testHeader')).not.toBe(null);
expect(wrapper.queryByTestId('testCell')).not.toBe(null);
});
Expand Down Expand Up @@ -168,7 +181,7 @@ describe('AlertsTable', () => {
},
};

const { queryByTestId } = render(<AlertsTable {...customTableProps} />);
const { queryByTestId } = render(<AlertsTableWithLocale {...customTableProps} />);
expect(queryByTestId('testActionColumn')).not.toBe(null);
expect(queryByTestId('testActionColumn2')).not.toBe(null);
expect(queryByTestId('expandColumnCellOpenFlyoutButton-0')).not.toBe(null);
Expand Down Expand Up @@ -211,7 +224,7 @@ describe('AlertsTable', () => {
},
};

const { queryByTestId } = render(<AlertsTable {...customTableProps} />);
const { queryByTestId } = render(<AlertsTableWithLocale {...customTableProps} />);
expect(queryByTestId('testActionColumn')).not.toBe(null);
expect(queryByTestId('testActionColumn2')).not.toBe(null);
expect(queryByTestId('expandColumnCellOpenFlyoutButton-0')).toBe(null);
Expand All @@ -223,7 +236,7 @@ describe('AlertsTable', () => {
showExpandToDetails: false,
};

const { queryByTestId } = render(<AlertsTable {...customTableProps} />);
const { queryByTestId } = render(<AlertsTableWithLocale {...customTableProps} />);
expect(queryByTestId('expandColumnHeaderLabel')).toBe(null);
expect(queryByTestId('expandColumnCellOpenFlyoutButton')).toBe(null);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,10 @@ const AlertsTable: React.FunctionComponent<AlertsTableProps> = (props: AlertsTab
alertsCount,
rowSelection,
alerts: alertsData.alerts,
updatedAt: props.updatedAt,
isLoading,
});
}, [bulkActionsState, bulkActions, alertsCount, alertsData.alerts])();
}, [bulkActionsState, bulkActions, alertsCount, alertsData.alerts, props.updatedAt, isLoading])();

const {
pagination,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ import {
} from '../../../types';
import { PLUGIN_ID } from '../../../common/constants';
import { TypeRegistry } from '../../type_registry';
import AlertsTableState from './alerts_table_state';
import AlertsTableState, { AlertsTableStateProps } from './alerts_table_state';
import { useFetchAlerts } from './hooks/use_fetch_alerts';
import { DefaultSort } from './hooks';
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';

jest.mock('./hooks/use_fetch_alerts');
jest.mock('@kbn/kibana-utils-plugin/public');
Expand Down Expand Up @@ -103,6 +104,12 @@ hookUseFetchAlerts.mockImplementation(() => [
},
]);

const AlertsTableWithLocale: React.FunctionComponent<AlertsTableStateProps> = (props) => (
<IntlProvider locale="en">
<AlertsTableState {...props} />
</IntlProvider>
);

describe('AlertsTableState', () => {
const tableProps = {
alertsTableConfigurationRegistry: alertsTableConfigurationRegistryMock,
Expand All @@ -120,14 +127,14 @@ describe('AlertsTableState', () => {

describe('Alerts table configuration registry', () => {
it('should read the configuration from the registry', async () => {
render(<AlertsTableState {...tableProps} />);
render(<AlertsTableWithLocale {...tableProps} />);
expect(hasMock).toHaveBeenCalledWith(PLUGIN_ID);
expect(getMock).toHaveBeenCalledWith(PLUGIN_ID);
});

it('should render an empty error state when the plugin id owner is not registered', async () => {
const props = { ...tableProps, configurationId: 'none' };
const result = render(<AlertsTableState {...props} />);
const result = render(<AlertsTableWithLocale {...props} />);
expect(result.getByTestId('alertsTableNoConfiguration')).toBeTruthy();
});
});
Expand All @@ -137,7 +144,7 @@ describe('AlertsTableState', () => {
hookUseFetchAlerts.mockClear();
});
it('should show a flyout when selecting an alert', async () => {
const wrapper = render(<AlertsTableState {...tableProps} />);
const wrapper = render(<AlertsTableWithLocale {...tableProps} />);
userEvent.click(wrapper.queryByTestId('expandColumnCellOpenFlyoutButton-0')!);

const result = await wrapper.findAllByTestId('alertsFlyout');
Expand All @@ -158,7 +165,7 @@ describe('AlertsTableState', () => {

it('should refetch data if flyout pagination exceeds the current page', async () => {
const wrapper = render(
<AlertsTableState
<AlertsTableWithLocale
{...{
...tableProps,
pageSize: 1,
Expand Down Expand Up @@ -210,7 +217,7 @@ describe('AlertsTableState', () => {
});

it('should render an empty screen if there are no alerts', async () => {
const result = render(<AlertsTableState {...tableProps} />);
const result = render(<AlertsTableWithLocale {...tableProps} />);
expect(result.getByTestId('alertsStateTableEmptyState')).toBeTruthy();
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,14 @@ const AlertsTableState = ({

const [
isLoading,
{ alerts, isInitializing, getInspectQuery, refetch: refresh, totalAlerts: alertsCount },
{
alerts,
isInitializing,
getInspectQuery,
refetch: refresh,
totalAlerts: alertsCount,
updatedAt,
},
] = useFetchAlerts({
fields: columns.map((col) => ({ field: col.id, include_unmapped: true })),
featureIds,
Expand Down Expand Up @@ -215,6 +222,7 @@ const AlertsTableState = ({
onSortChange,
refresh,
sort,
updatedAt,
};
}, [
alerts,
Expand All @@ -228,6 +236,7 @@ const AlertsTableState = ({
pagination.pageIndex,
refresh,
sort,
updatedAt,
]);

const tableProps = useMemo(
Expand All @@ -246,6 +255,7 @@ const AlertsTableState = ({
useFetchAlertsData,
visibleColumns: storageAlertsTable.current.visibleColumns ?? [],
'data-test-subj': 'internalAlertsState',
updatedAt,
}),
[
alertsTableConfiguration,
Expand All @@ -254,6 +264,7 @@ const AlertsTableState = ({
pagination.pageSize,
showExpandToDetails,
useFetchAlertsData,
updatedAt,
]
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { BulkActionsContext } from './context';
import { AlertsTable } from '../alerts_table';
import { AlertsField, AlertsTableProps, BulkActionsState } from '../../../../types';
import { bulkActionsReducer } from './reducer';
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';

jest.mock('@kbn/data-plugin/public');
jest.mock('@kbn/kibana-react-plugin/public/ui_settings/use_ui_setting', () => ({
Expand Down Expand Up @@ -92,6 +93,7 @@ describe('AlertsTable.BulkActions', () => {
useFetchAlertsData: () => alertsData,
visibleColumns: columns.map((c) => c.id),
'data-test-subj': 'testTable',
updatedAt: Date.now(),
};

const tablePropsWithBulkActions = {
Expand Down Expand Up @@ -127,22 +129,32 @@ describe('AlertsTable.BulkActions', () => {
);

return (
<BulkActionsContext.Provider value={initialBulkActionsState}>
<AlertsTable {...props} />
</BulkActionsContext.Provider>
<IntlProvider locale="en">
<BulkActionsContext.Provider value={initialBulkActionsState}>
<AlertsTable {...props} />
</BulkActionsContext.Provider>
</IntlProvider>
);
};

describe('when the bulk action hook is not set', () => {
it('should not show the bulk actions column', () => {
const { queryByTestId } = render(<AlertsTable {...tableProps} />);
const { queryByTestId } = render(
<IntlProvider locale="en">
<AlertsTable {...tableProps} />
</IntlProvider>
);
expect(queryByTestId('bulk-actions-header')).toBeNull();
});
});

describe('when the bulk action hook is set', () => {
it('should show the bulk actions column', () => {
const { getByTestId } = render(<AlertsTable {...tablePropsWithBulkActions} />);
const { getByTestId } = render(
<IntlProvider locale="en">
<AlertsTable {...tablePropsWithBulkActions} />
</IntlProvider>
);
expect(getByTestId('bulk-actions-header')).toBeDefined();
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* 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 { EuiText, EuiToolTip } from '@elastic/eui';
import { FormattedRelative } from '@kbn/i18n-react';
import React, { useEffect, useMemo, useState } from 'react';

import * as i18n from './translations';

export interface LastUpdatedAtProps {
compact?: boolean;
updatedAt: number;
showUpdating?: boolean;
}

const Updated = React.memo<{ date: number; prefix: string; updatedAt: number }>(
({ date, prefix, updatedAt }) => (
<>
{prefix}
{
<FormattedRelative
data-test-subj="last-updated-at-date"
key={`formatedRelative-${date}`}
value={new Date(updatedAt)}
/>
}
</>
)
);

Updated.displayName = 'Updated';

const prefix = ` ${i18n.UPDATED} `;

export const LastUpdatedAt = React.memo<LastUpdatedAtProps>(
({ compact = false, updatedAt, showUpdating = false }) => {
const [date, setDate] = useState(Date.now());

function tick() {
setDate(Date.now());
}

useEffect(() => {
const timerID = setInterval(() => tick(), 10000);
return () => {
clearInterval(timerID);
};
}, []);

const updateText = useMemo(() => {
if (showUpdating) {
return <span> {i18n.UPDATING}</span>;
}

if (!compact) {
return <Updated date={date} prefix={prefix} updatedAt={updatedAt} />;
}

return null;
}, [compact, date, showUpdating, updatedAt]);

return (
<EuiToolTip content={<Updated date={date} prefix={prefix} updatedAt={updatedAt} />}>
<EuiText color="subdued" size="xs" data-test-subj="toolbar-updated-at">
{updateText}
</EuiText>
</EuiToolTip>
);
}
);

LastUpdatedAt.displayName = 'LastUpdatedAt';

// eslint-disable-next-line import/no-default-export
export { LastUpdatedAt as default };
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* 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 UPDATING = i18n.translate('xpack.triggersActionsUI.alertsTable.lastUpdated.updating', {
defaultMessage: 'Updating...',
});

export const UPDATED = i18n.translate('xpack.triggersActionsUI.alertsTable.lastUpdated.updated', {
defaultMessage: 'Updated',
});
Loading

0 comments on commit 8b21e25

Please sign in to comment.