From 001d44cb028df385afe7ff01d5ea3ca5e432efed Mon Sep 17 00:00:00 2001 From: Luke Gmys Date: Tue, 4 Oct 2022 16:41:56 +0200 Subject: [PATCH] [TIP] Add update status component (#142560) --- .../cypress/e2e/indicators.cy.ts | 4 +- .../cypress/screens/indicators.ts | 2 +- .../public/components/layout/layout.tsx | 16 ++++- .../public/components/update_status/index.ts | 8 +++ .../update_status/update_status.test.tsx | 63 +++++++++++++++++++ .../update_status/update_status.tsx | 43 +++++++++++++ .../indicators/hooks/use_indicators.test.tsx | 1 + .../indicators/hooks/use_indicators.ts | 5 +- .../indicators/indicators_page.test.tsx | 1 + .../modules/indicators/indicators_page.tsx | 8 ++- 10 files changed, 143 insertions(+), 8 deletions(-) create mode 100644 x-pack/plugins/threat_intelligence/public/components/update_status/index.ts create mode 100644 x-pack/plugins/threat_intelligence/public/components/update_status/update_status.test.tsx create mode 100644 x-pack/plugins/threat_intelligence/public/components/update_status/update_status.tsx diff --git a/x-pack/plugins/threat_intelligence/cypress/e2e/indicators.cy.ts b/x-pack/plugins/threat_intelligence/cypress/e2e/indicators.cy.ts index e52effa09ab3b..c5d67894aa0ff 100644 --- a/x-pack/plugins/threat_intelligence/cypress/e2e/indicators.cy.ts +++ b/x-pack/plugins/threat_intelligence/cypress/e2e/indicators.cy.ts @@ -185,9 +185,7 @@ describe('Indicators', () => { it('should render the inspector flyout', () => { cy.get(INSPECTOR_BUTTON).last().click({ force: true }); - cy.get(INSPECTOR_PANEL).should('be.visible'); - - cy.get(INSPECTOR_PANEL).contains('Index patterns'); + cy.get(INSPECTOR_PANEL).contains('Indicators search requests'); }); }); }); diff --git a/x-pack/plugins/threat_intelligence/cypress/screens/indicators.ts b/x-pack/plugins/threat_intelligence/cypress/screens/indicators.ts index 2bc1b704e8159..0464e57c6749b 100644 --- a/x-pack/plugins/threat_intelligence/cypress/screens/indicators.ts +++ b/x-pack/plugins/threat_intelligence/cypress/screens/indicators.ts @@ -31,7 +31,7 @@ export const FILTERS_GLOBAL_CONTAINER = '[data-test-subj="filters-global-contain export const TIME_RANGE_PICKER = `[data-test-subj="superDatePickerToggleQuickMenuButton"]`; -export const QUERY_INPUT = `[data-test-subj="iocListPageQueryInput"]`; +export const QUERY_INPUT = `[data-test-subj="queryInput"]`; export const EMPTY_STATE = '[data-test-subj="indicatorsTableEmptyState"]'; diff --git a/x-pack/plugins/threat_intelligence/public/components/layout/layout.tsx b/x-pack/plugins/threat_intelligence/public/components/layout/layout.tsx index 6c7621977b8dc..04ee12819d988 100644 --- a/x-pack/plugins/threat_intelligence/public/components/layout/layout.tsx +++ b/x-pack/plugins/threat_intelligence/public/components/layout/layout.tsx @@ -6,17 +6,23 @@ */ import { EuiPageHeader, EuiPageHeaderSection, EuiSpacer, EuiText } from '@elastic/eui'; -import React, { FC } from 'react'; +import React, { FC, ReactNode } from 'react'; import { SecuritySolutionPageWrapper } from '../../containers/security_solution_page_wrapper'; export interface LayoutProps { pageTitle?: string; border?: boolean; + subHeader?: ReactNode; } export const TITLE_TEST_ID = 'tiDefaultPageLayoutTitle'; -export const DefaultPageLayout: FC = ({ children, pageTitle, border = true }) => { +export const DefaultPageLayout: FC = ({ + children, + pageTitle, + border = true, + subHeader, +}) => { return ( @@ -26,6 +32,12 @@ export const DefaultPageLayout: FC = ({ children, pageTitle, border

{pageTitle}

)} + {subHeader ? ( + <> + + {subHeader} + + ) : null}
diff --git a/x-pack/plugins/threat_intelligence/public/components/update_status/index.ts b/x-pack/plugins/threat_intelligence/public/components/update_status/index.ts new file mode 100644 index 0000000000000..f83c0e64fda23 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/components/update_status/index.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export * from './update_status'; diff --git a/x-pack/plugins/threat_intelligence/public/components/update_status/update_status.test.tsx b/x-pack/plugins/threat_intelligence/public/components/update_status/update_status.test.tsx new file mode 100644 index 0000000000000..2ed1503d89a78 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/components/update_status/update_status.test.tsx @@ -0,0 +1,63 @@ +/* + * 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 { render } from '@testing-library/react'; +import React from 'react'; +import { TestProvidersComponent } from '../../common/mocks/test_providers'; +import { UpdateStatus } from './update_status'; + +describe('', () => { + it('should render Updated now', () => { + const result = render(, { + wrapper: TestProvidersComponent, + }); + + expect(result.asFragment()).toMatchInlineSnapshot(` + +
+
+
+ Updated now +
+
+
+
+ `); + }); + + it('should render Updating when isUpdating', () => { + const result = render(, { + wrapper: TestProvidersComponent, + }); + + expect(result.asFragment()).toMatchInlineSnapshot(` + +
+
+
+ Updating... +
+
+
+
+ `); + }); +}); diff --git a/x-pack/plugins/threat_intelligence/public/components/update_status/update_status.tsx b/x-pack/plugins/threat_intelligence/public/components/update_status/update_status.tsx new file mode 100644 index 0000000000000..02f43481186dd --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/components/update_status/update_status.tsx @@ -0,0 +1,43 @@ +/* + * 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 React from 'react'; + +import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedRelative } from '@kbn/i18n-react'; + +interface UpdateStatusProps { + updatedAt: number; + isUpdating: boolean; +} + +const UPDATING = i18n.translate('xpack.threatIntelligence.updateStatus.updating', { + defaultMessage: 'Updating...', +}); + +const UPDATED = i18n.translate('xpack.threatIntelligence.updateStatus.updated', { + defaultMessage: 'Updated', +}); + +export const UpdateStatus: React.FC = ({ isUpdating, updatedAt }) => ( + + + + {isUpdating ? ( + UPDATING + ) : ( + <> + {UPDATED} +   + + + )} + + + +); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.test.tsx index 42f6a4eb1fdb7..40d64636fa346 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.test.tsx @@ -105,6 +105,7 @@ describe('useIndicators()', () => { expect(hookResult.result.current).toMatchInlineSnapshot(` Object { + "dataUpdatedAt": 0, "handleRefresh": [Function], "indicatorCount": 0, "indicators": Array [], diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.ts index 2352f302a1d4d..e2e0aaddf07aa 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.ts +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.ts @@ -47,6 +47,8 @@ export interface UseIndicatorsValue { * Data loading is in progress (see docs on `isFetching` here: https://tanstack.com/query/v4/docs/guides/queries) */ isFetching: boolean; + + dataUpdatedAt: number; } export const useIndicators = ({ @@ -95,7 +97,7 @@ export const useIndicators = ({ [inspectorAdapters, searchService] ); - const { isLoading, isFetching, data, refetch } = useQuery( + const { isLoading, isFetching, data, refetch, dataUpdatedAt } = useQuery( [ 'indicatorsTable', { @@ -132,5 +134,6 @@ export const useIndicators = ({ isLoading, isFetching, handleRefresh, + dataUpdatedAt, }; }; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.test.tsx index e46c605d1a90a..7f4db9fa75262 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.test.tsx @@ -42,6 +42,7 @@ describe('', () => { onChangeItemsPerPage: stub, onChangePage: stub, handleRefresh: stub, + dataUpdatedAt: Date.now(), }); (useFilters as jest.MockedFunction).mockReturnValue({ diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.tsx index 511faaa73a7a0..fcf690631d740 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.tsx @@ -20,6 +20,7 @@ import { useColumnSettings } from './components/indicators_table/hooks/use_colum import { useAggregatedIndicators } from './hooks/use_aggregated_indicators'; import { IndicatorsFilters } from './containers/indicators_filters'; import { useSecurityContext } from '../../hooks/use_security_context'; +import { UpdateStatus } from '../../components/update_status'; const queryClient = new QueryClient(); @@ -48,6 +49,7 @@ const IndicatorsPageContent: VFC = () => { pagination, isLoading: isLoadingIndicators, isFetching: isFetchingIndicators, + dataUpdatedAt, } = useIndicators({ filters, filterQuery, @@ -72,10 +74,14 @@ const IndicatorsPageContent: VFC = () => { return ( - + } + > +