From 4a3086209b9409d306b189898b5ad90e9809411a Mon Sep 17 00:00:00 2001 From: Julian Gernun Date: Fri, 22 Jul 2022 15:21:09 +0200 Subject: [PATCH 1/4] first commit --- .../alerts_table/alerts_table.test.tsx | 1 + .../sections/alerts_table/alerts_table.tsx | 4 +- .../alerts_table/alerts_table_state.tsx | 13 ++- .../bulk_actions/bulk_actions.test.tsx | 1 + .../components/last_updated_at/index.tsx | 82 +++++++++++++++++++ .../last_updated_at/translations.ts | 16 ++++ .../toolbar/toolbar_visibility.tsx | 27 +++++- .../triggers_actions_ui/public/types.ts | 1 + 8 files changed, 139 insertions(+), 6 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/index.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/translations.ts diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.test.tsx index 3a7d693ce6375..48ce22d7c1c11 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.test.tsx @@ -88,6 +88,7 @@ describe('AlertsTable', () => { useFetchAlertsData, visibleColumns: columns.map((c) => c.id), 'data-test-subj': 'testTable', + updatedAt: Date.now(), }; describe('Alerts table UI', () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx index 4bcc6afaa5a47..44429b472e60b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx @@ -72,8 +72,10 @@ const AlertsTable: React.FunctionComponent = (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, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.tsx index e53cb6251fe9f..c48b170f262f7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.tsx @@ -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, @@ -215,6 +222,7 @@ const AlertsTableState = ({ onSortChange, refresh, sort, + updatedAt, }; }, [ alerts, @@ -228,6 +236,7 @@ const AlertsTableState = ({ pagination.pageIndex, refresh, sort, + updatedAt, ]); const tableProps = useMemo( @@ -246,6 +255,7 @@ const AlertsTableState = ({ useFetchAlertsData, visibleColumns: storageAlertsTable.current.visibleColumns ?? [], 'data-test-subj': 'internalAlertsState', + updatedAt, }), [ alertsTableConfiguration, @@ -254,6 +264,7 @@ const AlertsTableState = ({ pagination.pageSize, showExpandToDetails, useFetchAlertsData, + updatedAt, ] ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/bulk_actions.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/bulk_actions.test.tsx index 7bcd56a332d87..ed6feb98f406b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/bulk_actions.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/bulk_actions.test.tsx @@ -92,6 +92,7 @@ describe('AlertsTable.BulkActions', () => { useFetchAlertsData: () => alertsData, visibleColumns: columns.map((c) => c.id), 'data-test-subj': 'testTable', + updatedAt: Date.now(), }; const tablePropsWithBulkActions = { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/index.tsx new file mode 100644 index 0000000000000..25b552d85927f --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/index.tsx @@ -0,0 +1,82 @@ +/* + * 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} + { + + } + + ) +); + +Updated.displayName = 'Updated'; + +const prefix = ` ${i18n.UPDATED} `; + +export const LastUpdatedAt = React.memo( + ({ 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 {i18n.UPDATING}; + } + + if (!compact) { + return ; + } + + return null; + }, [compact, date, showUpdating, updatedAt]); + + return ( + } + > + + {updateText} + + + ); + } +); + +LastUpdatedAt.displayName = 'LastUpdatedAt'; + +// eslint-disable-next-line import/no-default-export +export { LastUpdatedAt as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/translations.ts new file mode 100644 index 0000000000000..05f2786e27a89 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/translations.ts @@ -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.lastUpdated.updating', { + defaultMessage: 'Updating...', +}); + +export const UPDATED = i18n.translate('xpack.triggersActionsUI.lastUpdated.updated', { + defaultMessage: 'Updated', +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/toolbar_visibility.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/toolbar_visibility.tsx index ed6655c2abe07..65047afe05576 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/toolbar_visibility.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/toolbar_visibility.tsx @@ -11,29 +11,48 @@ import React, { lazy, Suspense } from 'react'; import { BulkActionsConfig } from '../../../../types'; const BulkActionsToolbar = lazy(() => import('../bulk_actions/components/toolbar')); +const LastUpdatedAt = lazy(() => import('./components/last_updated_at')); + +const getDefaultVisibility = (updatedAt: number) => { + return { + showColumnSelector: true, + showSortSelector: true, + additionalControls: { + right: ( + + + + ), + }, + }; +}; export const getToolbarVisibility = ({ bulkActions, alertsCount, rowSelection, alerts, + isLoading, + updatedAt, }: { bulkActions: BulkActionsConfig[]; alertsCount: number; rowSelection: Set; alerts: EcsFieldsResponse[]; + isLoading: boolean; + updatedAt: number; }): EuiDataGridToolBarVisibilityOptions => { const selectedRowsCount = rowSelection.size; + const defaultVisibility = getDefaultVisibility(updatedAt); + if (selectedRowsCount === 0 || selectedRowsCount === undefined || bulkActions.length === 0) - return { - showColumnSelector: true, - showSortSelector: true, - }; + return defaultVisibility; const options = { showColumnSelector: false, showSortSelector: false, additionalControls: { + ...defaultVisibility.additionalControls, left: { append: ( diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index fc169d1679389..389683bc2d0d0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -418,6 +418,7 @@ export interface AlertsTableProps { useFetchAlertsData: () => FetchAlertData; visibleColumns: string[]; 'data-test-subj': string; + updatedAt: number; } // TODO We need to create generic type between our plugin, right now we have different one because of the old alerts table From ec37638b4618e007f7ce8c1d00728fe419766cc4 Mon Sep 17 00:00:00 2001 From: Julian Gernun Date: Fri, 22 Jul 2022 16:28:37 +0200 Subject: [PATCH 2/4] fix and add test --- .../alerts_table/alerts_table.test.tsx | 28 +++++++++++++------ .../alerts_table/alerts_table_state.test.tsx | 19 +++++++++---- .../bulk_actions/bulk_actions.test.tsx | 21 ++++++++++---- .../components/last_updated_at/index.tsx | 7 ++--- .../toolbar/toolbar_visibility.tsx | 8 ++---- 5 files changed, 53 insertions(+), 30 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.test.tsx index 48ce22d7c1c11..acad34ac965f7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.test.tsx @@ -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'); @@ -91,9 +92,15 @@ describe('AlertsTable', () => { updatedAt: Date.now(), }; + const AlertsTableWithLocale: React.FunctionComponent = (props) => ( + + + + ); + describe('Alerts table UI', () => { it('should support sorting', async () => { - const renderResult = render(); + const renderResult = render(); userEvent.click(renderResult.container.querySelector('.euiDataGridHeaderCell__button')!); userEvent.click(renderResult.getByTestId(`dataGridHeaderCellActionGroup-${columns[0].id}`)); userEvent.click(renderResult.getByTitle('Sort A-Z')); @@ -103,14 +110,19 @@ describe('AlertsTable', () => { }); it('should support pagination', async () => { - const renderResult = render(); + const renderResult = render(); 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(); + 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(); + const wrapper = render(); expect(wrapper.getByTestId('expandColumnHeaderLabel').textContent).toBe('Actions'); }); @@ -126,7 +138,7 @@ describe('AlertsTable', () => { }, ], }; - const wrapper = render(); + const wrapper = render(); expect(wrapper.queryByTestId('testHeader')).not.toBe(null); expect(wrapper.queryByTestId('testCell')).not.toBe(null); }); @@ -169,7 +181,7 @@ describe('AlertsTable', () => { }, }; - const { queryByTestId } = render(); + const { queryByTestId } = render(); expect(queryByTestId('testActionColumn')).not.toBe(null); expect(queryByTestId('testActionColumn2')).not.toBe(null); expect(queryByTestId('expandColumnCellOpenFlyoutButton-0')).not.toBe(null); @@ -212,7 +224,7 @@ describe('AlertsTable', () => { }, }; - const { queryByTestId } = render(); + const { queryByTestId } = render(); expect(queryByTestId('testActionColumn')).not.toBe(null); expect(queryByTestId('testActionColumn2')).not.toBe(null); expect(queryByTestId('expandColumnCellOpenFlyoutButton-0')).toBe(null); @@ -224,7 +236,7 @@ describe('AlertsTable', () => { showExpandToDetails: false, }; - const { queryByTestId } = render(); + const { queryByTestId } = render(); expect(queryByTestId('expandColumnHeaderLabel')).toBe(null); expect(queryByTestId('expandColumnCellOpenFlyoutButton')).toBe(null); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.test.tsx index 267d467326fe5..3c1040a54b31e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.test.tsx @@ -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'); @@ -103,6 +104,12 @@ hookUseFetchAlerts.mockImplementation(() => [ }, ]); +const AlertsTableWithLocale: React.FunctionComponent = (props) => ( + + + +); + describe('AlertsTableState', () => { const tableProps = { alertsTableConfigurationRegistry: alertsTableConfigurationRegistryMock, @@ -120,14 +127,14 @@ describe('AlertsTableState', () => { describe('Alerts table configuration registry', () => { it('should read the configuration from the registry', async () => { - render(); + render(); 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(); + const result = render(); expect(result.getByTestId('alertsTableNoConfiguration')).toBeTruthy(); }); }); @@ -137,7 +144,7 @@ describe('AlertsTableState', () => { hookUseFetchAlerts.mockClear(); }); it('should show a flyout when selecting an alert', async () => { - const wrapper = render(); + const wrapper = render(); userEvent.click(wrapper.queryByTestId('expandColumnCellOpenFlyoutButton-0')!); const result = await wrapper.findAllByTestId('alertsFlyout'); @@ -158,7 +165,7 @@ describe('AlertsTableState', () => { it('should refetch data if flyout pagination exceeds the current page', async () => { const wrapper = render( - { }); it('should render an empty screen if there are no alerts', async () => { - const result = render(); + const result = render(); expect(result.getByTestId('alertsStateTableEmptyState')).toBeTruthy(); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/bulk_actions.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/bulk_actions.test.tsx index ed6feb98f406b..20a0d1a6bf087 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/bulk_actions.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/bulk_actions.test.tsx @@ -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', () => ({ @@ -128,22 +129,32 @@ describe('AlertsTable.BulkActions', () => { ); return ( - - - + + + + + ); }; describe('when the bulk action hook is not set', () => { it('should not show the bulk actions column', () => { - const { queryByTestId } = render(); + const { queryByTestId } = render( + + + + ); expect(queryByTestId('bulk-actions-header')).toBeNull(); }); }); describe('when the bulk action hook is set', () => { it('should show the bulk actions column', () => { - const { getByTestId } = render(); + const { getByTestId } = render( + + + + ); expect(getByTestId('bulk-actions-header')).toBeDefined(); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/index.tsx index 25b552d85927f..f2631b9fc0f26 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/index.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/index.tsx @@ -64,11 +64,8 @@ export const LastUpdatedAt = React.memo( }, [compact, date, showUpdating, updatedAt]); return ( - } - > - + }> + {updateText} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/toolbar_visibility.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/toolbar_visibility.tsx index 65047afe05576..df79c2723193b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/toolbar_visibility.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/toolbar_visibility.tsx @@ -9,20 +9,16 @@ import { EuiDataGridToolBarVisibilityOptions } from '@elastic/eui'; import { EcsFieldsResponse } from '@kbn/rule-registry-plugin/common/search_strategy'; import React, { lazy, Suspense } from 'react'; import { BulkActionsConfig } from '../../../../types'; +import { LastUpdatedAt } from './components/last_updated_at'; const BulkActionsToolbar = lazy(() => import('../bulk_actions/components/toolbar')); -const LastUpdatedAt = lazy(() => import('./components/last_updated_at')); const getDefaultVisibility = (updatedAt: number) => { return { showColumnSelector: true, showSortSelector: true, additionalControls: { - right: ( - - - - ), + right: , }, }; }; From ce5a44e08e15f17aed764209b8197e1db27746bc Mon Sep 17 00:00:00 2001 From: Julian Gernun Date: Mon, 25 Jul 2022 09:28:19 +0200 Subject: [PATCH 3/4] Update x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/translations.ts Co-authored-by: Xavier Mouligneau --- .../toolbar/components/last_updated_at/translations.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/translations.ts index 05f2786e27a89..ea3960f4165d6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/translations.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/translations.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; -export const UPDATING = i18n.translate('xpack.triggersActionsUI.lastUpdated.updating', { +export const UPDATING = i18n.translate('xpack.triggersActionsUI.alertsTable.lastUpdated.updating', { defaultMessage: 'Updating...', }); From 075d4cd040cb1867c7f9be6b20d4fac85160ef8d Mon Sep 17 00:00:00 2001 From: Julian Gernun Date: Mon, 25 Jul 2022 09:28:24 +0200 Subject: [PATCH 4/4] Update x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/translations.ts Co-authored-by: Xavier Mouligneau --- .../toolbar/components/last_updated_at/translations.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/translations.ts index ea3960f4165d6..ec68d475823b0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/translations.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/translations.ts @@ -11,6 +11,6 @@ export const UPDATING = i18n.translate('xpack.triggersActionsUI.alertsTable.last defaultMessage: 'Updating...', }); -export const UPDATED = i18n.translate('xpack.triggersActionsUI.lastUpdated.updated', { +export const UPDATED = i18n.translate('xpack.triggersActionsUI.alertsTable.lastUpdated.updated', { defaultMessage: 'Updated', });