Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RAM] Add the "updated at" feature in new alerts table #136949

Merged
merged 6 commits into from
Jul 25, 2022
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 @@ -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