From c28c2b9f1a510bb6b1656c6f72dbfb33b923c042 Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Mon, 29 Mar 2021 13:46:07 -0400 Subject: [PATCH 01/17] Pull out columns to be re-used for MetaEnginesTable --- .../components/engines/engines_table.tsx | 136 +++++++++--------- 1 file changed, 69 insertions(+), 67 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx index 3a65d9c449d6e..b6401a58b40a3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx @@ -14,6 +14,7 @@ import { EuiBasicTableColumn, CriteriaWithPagination, EuiTableActionsColumnType, + EuiTableFieldDataColumnType, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedNumber } from '@kbn/i18n/react'; @@ -29,6 +30,65 @@ import { generateEncodedPath } from '../../utils/encode_path_params'; import { FormattedDateTime } from '../../utils/formatted_date_time'; import { EngineDetails } from '../engine/types'; +export const NAME_COLUMN: EuiTableFieldDataColumnType = { + field: 'name', + name: i18n.translate('xpack.enterpriseSearch.appSearch.enginesOverview.table.column.name', { + defaultMessage: 'Name', + }), + width: '30%', + truncateText: true, + mobileOptions: { + header: true, + // Note: the below props are valid props per https://elastic.github.io/eui/#/tabular-content/tables (Responsive tables), but EUI's types have a bug reporting it as an error + // @ts-ignore + enlarge: true, + fullWidth: true, + truncateText: false, + }, +}; + +export const CREATED_AT_COLUMN: EuiTableFieldDataColumnType = { + field: 'created_at', + name: i18n.translate('xpack.enterpriseSearch.appSearch.enginesOverview.table.column.createdAt', { + defaultMessage: 'Created At', + }), + dataType: 'string', + render: (dateString: string) => , +}; + +export const LANGUAGE_COLUMN: EuiTableFieldDataColumnType = { + field: 'language', + name: i18n.translate('xpack.enterpriseSearch.appSearch.enginesOverview.table.column.language', { + defaultMessage: 'Language', + }), + dataType: 'string', + render: (language: string, engine: EngineDetails) => + engine.isMeta ? '' : language || UNIVERSAL_LANGUAGE, +}; + +export const DOCUMENT_COUNT_COLUMN: EuiTableFieldDataColumnType = { + field: 'document_count', + name: i18n.translate( + 'xpack.enterpriseSearch.appSearch.enginesOverview.table.column.documentCount', + { + defaultMessage: 'Document Count', + } + ), + dataType: 'number', + render: (number: number) => , + truncateText: true, +}; + +export const FIELD_COUNT_COLUMN: EuiTableFieldDataColumnType = { + field: 'field_count', + name: i18n.translate('xpack.enterpriseSearch.appSearch.enginesOverview.table.column.fieldCount', { + defaultMessage: 'Field Count', + }), + dataType: 'number', + render: (number: number) => , + truncateText: true, +}; + interface EnginesTableProps { items: EngineDetails[]; loading: boolean; @@ -57,8 +117,6 @@ export const EnginesTable: React.FC = ({ myRole: { canManageEngines }, } = useValues(AppLogic); - const generateEncodedEnginePath = (engineName: string) => - generateEncodedPath(ENGINE_PATH, { engineName }); const sendEngineTableLinkClickTelemetry = () => sendAppSearchTelemetry({ action: 'clicked', @@ -67,80 +125,24 @@ export const EnginesTable: React.FC = ({ const columns: Array> = [ { - field: 'name', - name: i18n.translate('xpack.enterpriseSearch.appSearch.enginesOverview.table.column.name', { - defaultMessage: 'Name', - }), + ...NAME_COLUMN, render: (name: string) => ( {name} ), - width: '30%', - truncateText: true, - mobileOptions: { - header: true, - // Note: the below props are valid props per https://elastic.github.io/eui/#/tabular-content/tables (Responsive tables), but EUI's types have a bug reporting it as an error - // @ts-ignore - enlarge: true, - fullWidth: true, - truncateText: false, - }, - }, - { - field: 'created_at', - name: i18n.translate( - 'xpack.enterpriseSearch.appSearch.enginesOverview.table.column.createdAt', - { - defaultMessage: 'Created At', - } - ), - dataType: 'string', - render: (dateString: string) => , - }, - { - field: 'language', - name: i18n.translate( - 'xpack.enterpriseSearch.appSearch.enginesOverview.table.column.language', - { - defaultMessage: 'Language', - } - ), - dataType: 'string', - render: (language: string, engine: EngineDetails) => - engine.isMeta ? '' : language || UNIVERSAL_LANGUAGE, - }, - { - field: 'document_count', - name: i18n.translate( - 'xpack.enterpriseSearch.appSearch.enginesOverview.table.column.documentCount', - { - defaultMessage: 'Document Count', - } - ), - dataType: 'number', - render: (number: number) => , - truncateText: true, - }, - { - field: 'field_count', - name: i18n.translate( - 'xpack.enterpriseSearch.appSearch.enginesOverview.table.column.fieldCount', - { - defaultMessage: 'Field Count', - } - ), - dataType: 'number', - render: (number: number) => , - truncateText: true, }, + CREATED_AT_COLUMN, + LANGUAGE_COLUMN, + DOCUMENT_COUNT_COLUMN, + FIELD_COUNT_COLUMN, ]; - const actionsColumn: EuiTableActionsColumnType = { + const ENGINE_ACTIONS_COLUMN: EuiTableActionsColumnType = { name: i18n.translate('xpack.enterpriseSearch.appSearch.enginesOverview.table.column.actions', { defaultMessage: 'Actions', }), @@ -157,7 +159,7 @@ export const EnginesTable: React.FC = ({ icon: 'eye', onClick: (engineDetails) => { sendEngineTableLinkClickTelemetry(); - navigateToUrl(generateEncodedEnginePath(engineDetails.name)); + navigateToUrl(generateEncodedPath(ENGINE_PATH, { engineName: engineDetails.name })); }, }, { @@ -194,7 +196,7 @@ export const EnginesTable: React.FC = ({ }; if (canManageEngines) { - columns.push(actionsColumn); + columns.push(ENGINE_ACTIONS_COLUMN); } return ( From 85e885767e201bb9f659c88f9c9da631afaab526 Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Mon, 29 Mar 2021 14:04:08 -0400 Subject: [PATCH 02/17] Add route to get source engines for meta engines --- .../server/routes/app_search/engines.test.ts | 43 +++++++++++++++++++ .../server/routes/app_search/engines.ts | 17 ++++++++ 2 files changed, 60 insertions(+) diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/engines.test.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/engines.test.ts index c653cad5c1c0d..bc4259fa37889 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/engines.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/engines.test.ts @@ -259,4 +259,47 @@ describe('engine routes', () => { }); }); }); + + describe('GET /api/app_search/engines/{name}/source_engines', () => { + let mockRouter: MockRouter; + + beforeEach(() => { + jest.clearAllMocks(); + mockRouter = new MockRouter({ + method: 'get', + path: '/api/app_search/engines/{name}/source_engines', + }); + + registerEnginesRoutes({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('validates correctly with name', () => { + const request = { params: { name: 'test-engine' } }; + mockRouter.shouldValidate(request); + }); + + it('fails validation without name', () => { + const request = { params: {} }; + mockRouter.shouldThrow(request); + }); + + it('fails validation with a non-string name', () => { + const request = { params: { name: 1 } }; + mockRouter.shouldThrow(request); + }); + + it('fails validation with missing query params', () => { + const request = { query: {} }; + mockRouter.shouldThrow(request); + }); + + it('creates a request to enterprise search', () => { + expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ + path: '/as/engines/:name/source_engines', + }); + }); + }); }); diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/engines.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/engines.ts index 77b055add7d79..f6e9d30dd0ade 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/engines.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/engines.ts @@ -95,4 +95,21 @@ export function registerEnginesRoutes({ path: '/as/engines/:name/overview_metrics', }) ); + router.get( + { + path: '/api/app_search/engines/{name}/source_engines', + validate: { + params: schema.object({ + name: schema.string(), + }), + query: schema.object({ + 'page[current]': schema.number(), + 'page[size]': schema.number(), + }), + }, + }, + enterpriseSearchRequestHandler.createRequest({ + path: '/as/engines/:name/source_engines', + }) + ); } From 34c8798533870882f86fa27f443456a1813c8741 Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Mon, 29 Mar 2021 14:05:54 -0400 Subject: [PATCH 03/17] New MetaEnginesTableLogic --- .../engines/meta_engines_table_logic.test.ts | 246 ++++++++++++++++++ .../engines/meta_engines_table_logic.ts | 130 +++++++++ .../components/engines/utils.test.ts | 48 ++++ .../app_search/components/engines/utils.ts | 29 +++ 4 files changed, 453 insertions(+) create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table_logic.test.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table_logic.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/utils.test.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/utils.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table_logic.test.ts new file mode 100644 index 0000000000000..f79881c2021c6 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table_logic.test.ts @@ -0,0 +1,246 @@ +/* + * 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 { LogicMounter, mockFlashMessageHelpers, mockHttpValues } from '../../../__mocks__'; + +import { nextTick } from '@kbn/test/jest'; + +import { EngineDetails } from '../engine/types'; + +import { MetaEnginesTableLogic } from './meta_engines_table_logic'; + +describe('MetaEnginesTableLogic', () => { + const DEFAULT_VALUES = { + expandedRows: {}, + sourceEngines: {}, + expandedSourceEngines: {}, + }; + + const SOURCE_ENGINES = [ + { + name: 'source-engine-1', + }, + { + name: 'source-engine-2', + }, + ] as EngineDetails[]; + + const META_ENGINES = [ + { + name: 'test-engine-1', + includedEngines: SOURCE_ENGINES, + }, + { + name: 'test-engine-2', + includedEngines: SOURCE_ENGINES, + }, + ] as EngineDetails[]; + + const DEFAULT_PROPS = { + metaEngines: [...SOURCE_ENGINES, ...META_ENGINES] as EngineDetails[], + }; + + const { http } = mockHttpValues; + const { mount } = new LogicMounter(MetaEnginesTableLogic); + const { flashAPIErrors } = mockFlashMessageHelpers; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('has expected default values', async () => { + mount({}, DEFAULT_PROPS); + expect(MetaEnginesTableLogic.values).toEqual(DEFAULT_VALUES); + }); + + describe('reducers', () => { + describe('expandedRows', () => { + it('displayRow adds an expanded row entry for provided itemId', () => { + mount(DEFAULT_VALUES, DEFAULT_PROPS); + MetaEnginesTableLogic.actions.displayRow('source-engine-1'); + + expect(MetaEnginesTableLogic.values.expandedRows).toEqual({ + 'source-engine-1': true, + }); + }); + + it('hideRow removes any expanded row entry for provided itemId', () => { + mount({ ...DEFAULT_VALUES, expandedRows: { 'source-engine-1': true } }, DEFAULT_PROPS); + + MetaEnginesTableLogic.actions.hideRow('source-engine-1'); + + expect(MetaEnginesTableLogic.values.expandedRows).toEqual({}); + }); + }); + + describe('sourceEngines is updated by addSourceEngines', () => { + mount({ + ...DEFAULT_VALUES, + sourceEngines: { + 'test-engine-1': [ + { name: 'source-engine-1' }, + { name: 'source-engine-2' }, + ] as EngineDetails[], + }, + }); + + MetaEnginesTableLogic.actions.addSourceEngines({ + 'test-engine-2': [ + { name: 'source-engine-1' }, + { name: 'source-engine-2' }, + ] as EngineDetails[], + }); + + expect(MetaEnginesTableLogic.values.sourceEngines).toEqual({ + 'test-engine-1': [{ name: 'source-engine-1' }, { name: 'source-engine-2' }], + 'test-engine-2': [{ name: 'source-engine-1' }, { name: 'source-engine-2' }], + }); + }); + }); + + describe('listeners', () => { + describe('fetchOrDisplayRow', () => { + it('calls displayRow when it already has data for the itemId', () => { + mount({ + ...DEFAULT_VALUES, + sourceEngines: { + 'test-engine-1': [ + { name: 'source-engine-1' }, + { name: 'source-engine-2' }, + ] as EngineDetails[], + }, + }); + jest.spyOn(MetaEnginesTableLogic.actions, 'displayRow'); + + MetaEnginesTableLogic.actions.fetchOrDisplayRow('test-engine-1'); + + expect(MetaEnginesTableLogic.actions.displayRow).toHaveBeenCalled(); + }); + + it('calls fetchSourceEngines when it needs to fetch data for the itemId', () => { + mount(); + jest.spyOn(MetaEnginesTableLogic.actions, 'fetchSourceEngines'); + + MetaEnginesTableLogic.actions.fetchOrDisplayRow('test-engine-1'); + + expect(MetaEnginesTableLogic.actions.fetchSourceEngines).toHaveBeenCalled(); + }); + }); + + describe('fetchSourceEngines', () => { + it('calls addSourceEngines and displayRow when it has retrieved all pages', async () => { + mount(); + http.get.mockReturnValueOnce( + Promise.resolve({ + meta: { + page: { + total_pages: 1, + }, + }, + results: [{ name: 'source-engine-1' }, { name: 'source-engine-2' }], + }) + ); + jest.spyOn(MetaEnginesTableLogic.actions, 'displayRow'); + jest.spyOn(MetaEnginesTableLogic.actions, 'addSourceEngines'); + + MetaEnginesTableLogic.actions.fetchSourceEngines('test-engine-1'); + await nextTick(); + + expect(http.get).toHaveBeenCalledWith( + '/api/app_search/engines/test-engine-1/source_engines', + { + query: { + 'page[current]': 1, + 'page[size]': 25, + }, + } + ); + expect(MetaEnginesTableLogic.actions.addSourceEngines).toHaveBeenCalledWith({ + 'test-engine-1': [{ name: 'source-engine-1' }, { name: 'source-engine-2' }], + }); + expect(MetaEnginesTableLogic.actions.displayRow).toHaveBeenCalledWith('test-engine-1'); + }); + + it('display a flash message on error', async () => { + http.get.mockReturnValueOnce(Promise.reject()); + mount(); + + MetaEnginesTableLogic.actions.fetchSourceEngines('test-engine-1'); + await nextTick(); + + expect(flashAPIErrors).toHaveBeenCalledTimes(1); + }); + + it('recursively fetches a number of pages', async () => { + mount(); + jest.spyOn(MetaEnginesTableLogic.actions, 'addSourceEngines'); + + // First page + http.get.mockReturnValueOnce( + Promise.resolve({ + meta: { + page: { + total_pages: 2, + }, + }, + results: [{ name: 'source-engine-1' }], + }) + ); + + // Second and final page + http.get.mockReturnValueOnce( + Promise.resolve({ + meta: { + page: { + total_pages: 2, + }, + }, + results: [{ name: 'source-engine-2' }], + }) + ); + + MetaEnginesTableLogic.actions.fetchSourceEngines('test-engine-1'); + await nextTick(); + await nextTick(); // I think I need this for multiple http.get calls + + expect(MetaEnginesTableLogic.actions.addSourceEngines).toHaveBeenCalledWith({ + 'test-engine-1': [ + // First page + { name: 'source-engine-1' }, + // Second and final page + { name: 'source-engine-2' }, + ], + }); + }); + }); + }); + + describe('selectors', () => { + it('expandedSourceEngines includes all source engines that have been expanded ', () => { + mount({ + ...DEFAULT_VALUES, + sourceEngines: { + 'test-engine-1': [ + { name: 'source-engine-1' }, + { name: 'source-engine-2' }, + ] as EngineDetails[], + 'test-engine-2': [ + { name: 'source-engine-1' }, + { name: 'source-engine-2' }, + ] as EngineDetails[], + }, + expandedRows: { + 'test-engine-1': true, + }, + }); + + expect(MetaEnginesTableLogic.values.expandedSourceEngines).toEqual({ + 'test-engine-1': [{ name: 'source-engine-1' }, { name: 'source-engine-2' }], + }); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table_logic.ts new file mode 100644 index 0000000000000..22a2325eeb0fc --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table_logic.ts @@ -0,0 +1,130 @@ +/* + * 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 { kea, MakeLogicType } from 'kea'; + +import { Meta } from '../../../../../common/types'; +import { flashAPIErrors } from '../../../shared/flash_messages'; + +import { HttpLogic } from '../../../shared/http'; + +import { EngineDetails } from '../engine/types'; + +interface MetaEnginesTableValues { + expandedRows: { [id: string]: boolean }; + sourceEngines: { [id: string]: EngineDetails[] }; + expandedSourceEngines: { [id: string]: EngineDetails[] }; +} + +interface MetaEnginesTableActions { + addSourceEngines( + sourceEngines: MetaEnginesTableValues['sourceEngines'] + ): MetaEnginesTableValues['sourceEngines']; + displayRow(itemId: string): string; + fetchOrDisplayRow(itemId: string): string; + fetchSourceEngines(engineName: string): string; + hideRow(itemId: string): string; +} + +interface EnginesAPIResponse { + results: EngineDetails[]; + meta: Meta; +} + +interface MetaEnginesTableProps { + metaEngines: EngineDetails[]; +} + +export const MetaEnginesTableLogic = kea< + MakeLogicType +>({ + path: ['enterprise_search', 'app_search', 'meta_engines_table_logic'], + actions: () => ({ + addSourceEngines: (sourceEngines: object) => sourceEngines, + displayRow: (itemId: string) => itemId, + hideRow: (itemId: string) => itemId, + fetchOrDisplayRow: (itemId) => itemId, + fetchSourceEngines: (engineName) => engineName, + }), + reducers: () => ({ + expandedRows: [ + {}, + { + displayRow: (expandedRows, itemId) => ({ + ...expandedRows, + [itemId]: true, + }), + hideRow: (expandedRows, itemId) => { + const newRows = { ...expandedRows }; + delete newRows[itemId]; + return newRows; + }, + }, + ], + sourceEngines: [ + {}, + { + addSourceEngines: (sourceEngines, newEngines) => ({ + ...sourceEngines, + ...newEngines, + }), + }, + ], + }), + selectors: { + expandedSourceEngines: [ + (selectors) => [selectors.sourceEngines, selectors.expandedRows], + (sourceEngines: MetaEnginesTableValues['sourceEngines'], expandedRows: string[]) => { + return Object.keys(expandedRows).reduce((expandedRowMap, engineName) => { + expandedRowMap[engineName] = sourceEngines[engineName]; + return expandedRowMap; + }, {} as MetaEnginesTableValues['sourceEngines']); + }, + ], + }, + listeners: ({ actions, values }) => ({ + fetchOrDisplayRow: (itemId) => { + const sourceEngines = values.sourceEngines; + if (sourceEngines[itemId]) { + actions.displayRow(itemId); + } else { + actions.fetchSourceEngines(itemId); + } + }, + fetchSourceEngines: (engineName) => { + const { http } = HttpLogic.values; + + let enginesAccumulator: EngineDetails[] = []; + + const recursiveFetchSourceEngines = async (page = 1) => { + try { + const { meta, results }: EnginesAPIResponse = await http.get( + `/api/app_search/engines/${engineName}/source_engines`, + { + query: { + 'page[current]': page, + 'page[size]': 25, + }, + } + ); + + enginesAccumulator = [...enginesAccumulator, ...results]; + + if (page >= meta.page.total_pages) { + actions.addSourceEngines({ [engineName]: enginesAccumulator }); + actions.displayRow(engineName); + } else { + recursiveFetchSourceEngines(page + 1); + } + } catch (e) { + flashAPIErrors(e); + } + }; + + recursiveFetchSourceEngines(); + }, + }), +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/utils.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/utils.test.ts new file mode 100644 index 0000000000000..73d1704430e7f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/utils.test.ts @@ -0,0 +1,48 @@ +/* + * 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 { SchemaConflicts } from '../../../shared/types'; +import { EngineDetails } from '../engine/types'; + +import { getConflictingEnginesSet } from './utils'; + +const DEFAULT_META_ENGINE_DETAILS = { + name: 'test-engine-1', + includedEngines: [ + { + name: 'source-engine-1', + }, + { + name: 'source-engine-2', + }, + { + name: 'source-engine-3', + }, + ] as EngineDetails[], + schemaConflicts: { + 'conflicting-field-1': { + text: ['source-engine-1'], + number: ['source-engine-2'], + geolocation: [], + date: [], + }, + 'conflicting-field-2': { + text: [], + number: [], + geolocation: ['source-engine-2'], + date: ['source-engine-3'], + }, + } as SchemaConflicts, +} as EngineDetails; + +describe('getConflictingEnginesSet', () => { + it('generates a set of engine names with any field conflicts for the meta-engine', () => { + expect(getConflictingEnginesSet(DEFAULT_META_ENGINE_DETAILS)).toEqual( + new Set(['source-engine-1', 'source-engine-2', 'source-engine-3']) + ); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/utils.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/utils.ts new file mode 100644 index 0000000000000..48410876b4de8 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/utils.ts @@ -0,0 +1,29 @@ +/* + * 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 { flatten } from 'lodash'; + +import { SchemaConflictFieldTypes, SchemaConflicts } from '../../../shared/types'; +import { EngineDetails } from '../engine/types'; + +const getConflictingEnginesFromConflictingField = ( + conflictingField: SchemaConflictFieldTypes +): string[] => flatten(Object.values(conflictingField)); + +const getConflictingEnginesFromSchemaConflicts = (schemaConflicts: SchemaConflicts): string[] => + flatten(Object.values(schemaConflicts).map(getConflictingEnginesFromConflictingField)); + +// Given a meta-engine (represented by IEngineDetails), generate a Set of all source engines +// who have schema conflicts in the context of that meta-engine +// +// A Set allows us to enforce uniqueness and has O(1) lookup time +export const getConflictingEnginesSet = (metaEngine: EngineDetails): Set => { + const conflictingEngines: string[] = metaEngine.schemaConflicts + ? getConflictingEnginesFromSchemaConflicts(metaEngine.schemaConflicts) + : []; + return new Set(conflictingEngines); +}; From 0cf74f5da04305d2c401f2a81efba4791b1624d3 Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Mon, 29 Mar 2021 14:06:03 -0400 Subject: [PATCH 04/17] New MetaEnginesTable component --- .../meta_engines_table_expanded_row.scss | 22 ++ .../meta_engines_table_expanded_row.test.tsx | 69 ++++++ .../meta_engines_table_expanded_row.tsx | 72 ++++++ ...engines_table_name_column_content.test.tsx | 196 +++++++++++++++ ...meta_engines_table_name_column_content.tsx | 61 +++++ .../engines/meta_engines_table.test.tsx | 225 ++++++++++++++++++ .../components/engines/meta_engines_table.tsx | 206 ++++++++++++++++ 7 files changed, 851 insertions(+) create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_expanded_row.scss create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_expanded_row.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_expanded_row.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_name_column_content.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_name_column_content.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table.tsx diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_expanded_row.scss b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_expanded_row.scss new file mode 100644 index 0000000000000..1f1d6d0d01537 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_expanded_row.scss @@ -0,0 +1,22 @@ + +.meta-engines__source-engines-table { + margin: -8px -8px 7px -8px; + + thead { + display: none; + } + + @include euiBreakpoint('l', 'xl') { + .euiTableRowCell { + border-top: none; + } + + .euiTitle { + display: none; + } + } + + .euiTableHeaderMobile { + display: none + } +} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_expanded_row.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_expanded_row.test.tsx new file mode 100644 index 0000000000000..abdbeaff4c1c0 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_expanded_row.test.tsx @@ -0,0 +1,69 @@ +/* + * 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 { mountWithIntl } from '../../../../__mocks__'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { EuiBasicTable, EuiHealth } from '@elastic/eui'; + +import { EngineDetails } from '../../engine/types'; + +import { MetaEnginesTableExpandedRow } from './meta_engines_table_expanded_row'; + +const SOURCE_ENGINES = [ + { + name: 'source-engine-1', + created_at: 'Fri, 1 Jan 1970 12:00:00 +0000', + language: 'English', + isMeta: true, + document_count: 99999, + field_count: 10, + }, + { + name: 'source-engine-2', + created_at: 'Fri, 1 Jan 1970 12:00:00 +0000', + language: 'English', + isMeta: true, + document_count: 55555, + field_count: 7, + }, +] as EngineDetails[]; + +describe('MetaEnginesTableExpandedRow', () => { + it('contains relevant source engine information', () => { + const wrapper = mountWithIntl( + + ); + const table = wrapper.find(EuiBasicTable); + + expect(table).toHaveLength(1); + + const tableContent = table.text(); + expect(tableContent).toContain('source-engine-1'); + expect(tableContent).toContain('99,999'); + expect(tableContent).toContain('10'); + + expect(tableContent).toContain('source-engine-2'); + expect(tableContent).toContain('55,555'); + expect(tableContent).toContain('7'); + }); + + it('indicates when a meta-engine has conflicts', () => { + const wrapper = shallow( + + ); + + const table = wrapper.find(EuiBasicTable); + expect(table.dive().find(EuiHealth)).toHaveLength(2); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_expanded_row.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_expanded_row.tsx new file mode 100644 index 0000000000000..d4064b06d1902 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_expanded_row.tsx @@ -0,0 +1,72 @@ +/* + * 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 { EuiBasicTable, EuiHealth, EuiTextAlign, EuiTitle } from '@elastic/eui'; + +import { EngineDetails } from '../../engine/types'; +import { + CREATED_AT_COLUMN, + DOCUMENT_COUNT_COLUMN, + FIELD_COUNT_COLUMN, + NAME_COLUMN, +} from '../engines_table'; + +import './meta_engines_table_expanded_row.scss'; + +interface MetaEnginesTableExpandedRowProps { + sourceEngines: EngineDetails[]; + conflictingEngines: Set; +} + +export const MetaEnginesTableExpandedRow: React.FC = ({ + sourceEngines, + conflictingEngines, +}) => ( +
+ + +

Source Engines

+
+
+ <>, // This is a blank column in place of the `Language` column + }, + DOCUMENT_COUNT_COLUMN, + { + ...FIELD_COUNT_COLUMN, + render: (_, engineDetails) => ( + <> + {conflictingEngines.has(engineDetails.name) ? ( + {engineDetails.field_count} + ) : ( + engineDetails.field_count + )} + + ), + }, + { + render: () => <>, // This is a blank column in place of the `Actions` column + }, + ]} + /> +
+); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_name_column_content.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_name_column_content.test.tsx new file mode 100644 index 0000000000000..dc3b3086b13b3 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_name_column_content.test.tsx @@ -0,0 +1,196 @@ +/* + * 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 { mountWithIntl } from '../../../../__mocks__'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { EuiHealth } from '@elastic/eui'; + +import { SchemaConflictFieldTypes, SchemaConflicts } from '../../../../shared/types'; +import { EngineDetails } from '../../engine/types'; + +import { MetaEnginesTableNameColumnContent } from './meta_engines_table_name_column_content'; + +describe('MetaEnginesTableNameColumnContent', () => { + it('includes the name of the engine', () => {}); + + describe('toggle button', () => { + it('displays expanded row when the row is currently hidden', () => { + const showRow = jest.fn(); + + const wrapper = shallow( + + ); + wrapper.find('.meta-engines__expand-row').at(0).simulate('click'); + + expect(showRow).toHaveBeenCalled(); + }); + + it('hides expanded row when the row is currently visible', () => { + const hideRow = jest.fn(); + + const wrapper = shallow( + + ); + wrapper.find('.meta-engines__expand-row').at(0).simulate('click'); + + expect(hideRow).toHaveBeenCalled(); + }); + }); + + describe('engine count', () => { + it('is included and labelled', () => { + const wrapper = mountWithIntl( + + ); + + const wrapperContent = wrapper.text(); + expect(wrapperContent).toContain('2 Engines'); + }); + it('defaults to 0', () => { + const wrapper = mountWithIntl( + + ); + + const wrapperContent = wrapper.text(); + expect(wrapperContent).toContain('0 Engines'); + }); + + it('label loses pluralization with only 1 source engine', () => { + const wrapper = mountWithIntl( + + ); + + const wrapperContent = wrapper.text(); + expect(wrapperContent).toContain('1 Engine'); + }); + }); + + it('indicates the precense of field-type conflicts', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find(EuiHealth)).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_name_column_content.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_name_column_content.tsx new file mode 100644 index 0000000000000..cac89c2e441ab --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_name_column_content.tsx @@ -0,0 +1,61 @@ +/* + * 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, EuiIcon, EuiHealth, EuiFlexItem } from '@elastic/eui'; + +import { EuiLinkTo } from '../../../../shared/react_router_helpers'; +import { ENGINE_PATH } from '../../../routes'; +import { generateEncodedPath } from '../../../utils/encode_path_params'; +import { EngineDetails } from '../../engine/types'; + +interface MetaEnginesTableNameContentProps { + name: string; + isExpanded: boolean; + item: EngineDetails; + hideRow: (name: string) => void; + sendEngineTableLinkClickTelemetry: () => void; + showRow: (name: string) => void; +} + +export const MetaEnginesTableNameColumnContent: React.FC = ({ + name, + item, + isExpanded, + sendEngineTableLinkClickTelemetry, + hideRow, + showRow, +}) => ( + + + {name} + + (isExpanded ? hideRow(name) : showRow(name))} + className="meta-engines__expand-row" + responsive={false} + > + + + + + {item.engine_count || '0'} Engine{item.engine_count !== 1 ? 's' : ''} + + {item.schemaConflicts && Object.keys(item.schemaConflicts).length > 0 && ( + + Field-type conflict + + )} + + +); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table.test.tsx new file mode 100644 index 0000000000000..97f1e587bf773 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table.test.tsx @@ -0,0 +1,225 @@ +/* + * 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 '../../../__mocks__/enterprise_search_url.mock'; +import { mockTelemetryActions, mountWithIntl, setMockValues } from '../../../__mocks__'; + +import React from 'react'; + +import { ReactWrapper, shallow } from 'enzyme'; + +import { EuiBasicTable, EuiPagination, EuiButtonEmpty, EuiIcon, EuiTableRow } from '@elastic/eui'; + +import { KibanaLogic } from '../../../shared/kibana'; +import { EuiLinkTo } from '../../../shared/react_router_helpers'; + +import { TelemetryLogic } from '../../../shared/telemetry'; +import { EngineDetails } from '../engine/types'; + +import { MetaEnginesTableExpandedRow } from './components/meta_engines_table_expanded_row'; +import { MetaEnginesTableNameColumnContent } from './components/meta_engines_table_name_column_content'; +import { EnginesLogic } from './engines_logic'; +import { MetaEnginesTable } from './meta_engines_table'; + +describe('MetaEnginesTable', () => { + const onChange = jest.fn(); + const onDeleteEngine = jest.fn(); + + const data = [ + { + name: 'test-engine', + created_at: 'Fri, 1 Jan 1970 12:00:00 +0000', + language: 'English', + isMeta: true, + document_count: 99999, + field_count: 10, + includedEngines: [ + { + name: 'source-engine-1', + }, + { name: 'source-engine-2' }, + ], + } as EngineDetails, + ]; + const pagination = { + pageIndex: 0, + pageSize: 10, + totalItemCount: 50, + hidePerPageOptions: true, + }; + const props = { + items: data, + loading: false, + pagination, + onChange, + onDeleteEngine, + }; + + const DEFAULT_VALUES = { + myRole: { + canManageMetaEngines: false, + }, + expandedSourceEngines: {}, + hideRow: jest.fn(), + fetchOrDisplayRow: jest.fn(), + }; + + const resetMocks = () => { + jest.clearAllMocks(); + setMockValues(DEFAULT_VALUES); + }; + + describe('basic table', () => { + let wrapper: ReactWrapper; + let table: ReactWrapper; + + beforeAll(() => { + resetMocks(); + wrapper = mountWithIntl(); + table = wrapper.find(EuiBasicTable); + }); + + it('renders', () => { + expect(table).toHaveLength(1); + expect(table.prop('pagination').totalItemCount).toEqual(50); + expect(table.find(MetaEnginesTableNameColumnContent)).toHaveLength(1); + + const tableContent = table.text(); + expect(tableContent).toContain('Jan 1, 1970'); + expect(tableContent).toContain('99,999'); + expect(tableContent).toContain('10'); + + expect(table.find(EuiPagination).find(EuiButtonEmpty)).toHaveLength(5); // Should display 5 pages at 10 engines per page + }); + + it('contains engine links which send telemetry', () => { + const engineLinks = wrapper.find(EuiLinkTo); + + engineLinks.forEach((link) => { + expect(link.prop('to')).toEqual('/engines/test-engine'); + link.simulate('click'); + + expect(mockTelemetryActions.sendAppSearchTelemetry).toHaveBeenCalledWith({ + action: 'clicked', + metric: 'engine_table_link', + }); + }); + }); + + it('triggers onPaginate', () => { + table.prop('onChange')({ page: { index: 4 } }); + expect(onChange).toHaveBeenCalledWith({ page: { index: 4 } }); + }); + }); + + describe('loading', () => { + it('passes the loading prop', () => { + resetMocks(); + const wrapper = mountWithIntl(); + + expect(wrapper.find(EuiBasicTable).prop('loading')).toEqual(true); + }); + }); + + describe('noItemsMessage', () => { + it('passes the noItemsMessage prop', () => { + resetMocks(); + const wrapper = mountWithIntl(); + expect(wrapper.find(EuiBasicTable).prop('noItemsMessage')).toEqual('No items.'); + }); + }); + + describe('actions', () => { + it('will hide the action buttons if the user cannot manage/delete engines', () => { + resetMocks(); + const wrapper = shallow(); + const tableRow = wrapper.find(EuiTableRow).first(); + + expect(tableRow.find(EuiIcon)).toHaveLength(0); + }); + + describe('when the user can manage/delete engines', () => { + let wrapper: ReactWrapper; + let tableRow: ReactWrapper; + let actions: ReactWrapper; + + beforeEach(() => { + resetMocks(); + setMockValues({ + ...DEFAULT_VALUES, + myRole: { + canManageMetaEngines: true, + }, + }); + + wrapper = mountWithIntl(); + tableRow = wrapper.find(EuiTableRow).first(); + actions = tableRow.find(EuiIcon); + EnginesLogic.mount(); + }); + + it('renders a manage action', () => { + jest.spyOn(TelemetryLogic.actions, 'sendAppSearchTelemetry'); + jest.spyOn(KibanaLogic.values, 'navigateToUrl'); + actions.at(1).simulate('click'); + + expect(TelemetryLogic.actions.sendAppSearchTelemetry).toHaveBeenCalled(); + expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith('/engines/test-engine'); + }); + + describe('delete action', () => { + it('shows the user a confirm message when the action is clicked', () => { + jest.spyOn(global, 'confirm' as any).mockReturnValueOnce(true); + actions.at(2).simulate('click'); + expect(global.confirm).toHaveBeenCalled(); + }); + + it('clicking the action and confirming deletes the engine', () => { + jest.spyOn(global, 'confirm' as any).mockReturnValueOnce(true); + jest.spyOn(EnginesLogic.actions, 'deleteEngine'); + + actions.at(2).simulate('click'); + + expect(onDeleteEngine).toHaveBeenCalled(); + }); + + it('clicking the action and not confirming does not delete the engine', () => { + jest.spyOn(global, 'confirm' as any).mockReturnValueOnce(false); + jest.spyOn(EnginesLogic.actions, 'deleteEngine'); + + actions.at(2).simulate('click'); + + expect(onDeleteEngine).toHaveBeenCalledTimes(0); + }); + }); + }); + }); + + describe('source engines', () => { + describe('source engine table', () => { + beforeEach(() => { + resetMocks(); + }); + + it('is hidden by default', () => { + const wrapper = shallow(); + const table = wrapper.find(EuiBasicTable); + expect(table.dive().find(MetaEnginesTableExpandedRow)).toHaveLength(0); + }); + + it('is visible when the row has been expanded', () => { + setMockValues({ + ...DEFAULT_VALUES, + expandedSourceEngines: { 'test-engine': true }, + }); + const wrapper = shallow(); + const table = wrapper.find(EuiBasicTable); + expect(table.dive().find(MetaEnginesTableExpandedRow)).toHaveLength(1); + }); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table.tsx new file mode 100644 index 0000000000000..43d20434ff76c --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table.tsx @@ -0,0 +1,206 @@ +/* + * 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, { ReactNode } from 'react'; + +import { useActions, useValues } from 'kea'; + +import { + EuiBasicTable, + EuiBasicTableColumn, + CriteriaWithPagination, + EuiTableActionsColumnType, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { KibanaLogic } from '../../../shared/kibana'; +import { TelemetryLogic } from '../../../shared/telemetry'; +import { AppLogic } from '../../app_logic'; +import { ENGINE_PATH } from '../../routes'; +import { generateEncodedPath } from '../../utils/encode_path_params'; +import { EngineDetails } from '../engine/types'; + +import { MetaEnginesTableExpandedRow } from './components/meta_engines_table_expanded_row'; +import { MetaEnginesTableNameColumnContent } from './components/meta_engines_table_name_column_content'; +import { + CREATED_AT_COLUMN, + DOCUMENT_COUNT_COLUMN, + FIELD_COUNT_COLUMN, + NAME_COLUMN, +} from './engines_table'; +import { MetaEnginesTableLogic } from './meta_engines_table_logic'; +import { getConflictingEnginesSet } from './utils'; + +interface EnginesTableProps { + items: EngineDetails[]; + loading: boolean; + noItemsMessage?: ReactNode; + pagination: { + pageIndex: number; + pageSize: number; + totalItemCount: number; + hidePerPageOptions: boolean; + }; + onChange(criteria: CriteriaWithPagination): void; + onDeleteEngine(engine: EngineDetails): void; +} + +interface IItemIdToExpandedRowMap { + [id: string]: ReactNode; +} + +export interface ConflictingEnginesSets { + [key: string]: Set; +} + +export const MetaEnginesTable: React.FC = ({ + items, + loading, + noItemsMessage, + pagination, + onChange, + onDeleteEngine, +}) => { + const metaEnginesTableLogic = MetaEnginesTableLogic({ metaEngines: items }); + const { expandedSourceEngines } = useValues(metaEnginesTableLogic); + const { hideRow, fetchOrDisplayRow } = useActions(metaEnginesTableLogic); + const { sendAppSearchTelemetry } = useActions(TelemetryLogic); + const { navigateToUrl } = useValues(KibanaLogic); + const { + myRole: { canManageMetaEngines }, + } = useValues(AppLogic); + + const conflictingEnginesSets: ConflictingEnginesSets = items.reduce((accumulator, metaEngine) => { + return { + ...accumulator, + [metaEngine.name]: getConflictingEnginesSet(metaEngine), + }; + }, {}); + + const itemIdToExpandedRowMap: IItemIdToExpandedRowMap = Object.keys(expandedSourceEngines).reduce( + (accumulator, engineName) => { + return { + ...accumulator, + [engineName]: ( + + ), + }; + }, + {} + ); + + const sendEngineTableLinkClickTelemetry = () => + sendAppSearchTelemetry({ + action: 'clicked', + metric: 'engine_table_link', + }); + + const columns: Array> = [ + { + ...NAME_COLUMN, + render: (name: string, item: EngineDetails) => ( + + ), + }, + CREATED_AT_COLUMN, + { + render: () => <>, // This is a blank column in place of the `Language` column + }, + DOCUMENT_COUNT_COLUMN, + FIELD_COUNT_COLUMN, + ]; + + const META_ENGINE_ACTIONS_COLUMN: EuiTableActionsColumnType = { + name: i18n.translate('xpack.enterpriseSearch.appSearch.enginesOverview.table.column.actions', { + defaultMessage: 'Actions', + }), + actions: [ + { + name: i18n.translate( + 'xpack.enterpriseSearch.appSearch.enginesOverview.table.action.manage', + { + defaultMessage: 'Manage', + } + ), + description: i18n.translate( + 'xpack.enterpriseSearch.appSearch.enginesOverview.metaEnginesTable.action.manage.buttonDescription', + { + defaultMessage: 'Manage this meta engine', + } + ), + type: 'icon', + icon: 'eye', + onClick: (engineDetails) => { + sendEngineTableLinkClickTelemetry(); + navigateToUrl(generateEncodedPath(ENGINE_PATH, { engineName: engineDetails.name })); + }, + }, + { + name: i18n.translate( + 'xpack.enterpriseSearch.appSearch.enginesOverview.table.action.delete.buttonLabel', + { + defaultMessage: 'Delete', + } + ), + description: i18n.translate( + 'xpack.enterpriseSearch.appSearch.enginesOverview.metaEnginesTable.action.delete.buttonDescription', + { + defaultMessage: 'Delete this meta engine', + } + ), + type: 'icon', + icon: 'trash', + color: 'danger', + onClick: (engine) => { + if ( + window.confirm( + i18n.translate( + 'xpack.enterpriseSearch.appSearch.enginesOverview.metaEnginesTable.action.delete.confirmationPopupMessage', + { + defaultMessage: + 'Are you sure you want to permanently delete "{engineName}" and all of its settings?', + values: { + engineName: engine.name, + }, + } + ) + ) + ) { + onDeleteEngine(engine); + } + }, + }, + ], + }; + + if (canManageMetaEngines) { + columns.push(META_ENGINE_ACTIONS_COLUMN); + } + + return ( + + ); +}; From 30dc40822bd2ca7c0bce7bc91dbb51e1aa595dca Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Tue, 30 Mar 2021 15:51:00 -0400 Subject: [PATCH 05/17] Remove isMeta prop from EnginesTable --- .../components/engines/engines_table.test.tsx | 17 ----------------- .../components/engines/engines_table.tsx | 5 ++--- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.test.tsx index fc37c3543af56..b43f120674533 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.test.tsx @@ -159,23 +159,6 @@ describe('EnginesTable', () => { const tableContent = wrapper.find(EuiBasicTable).text(); expect(tableContent).toContain('Universal'); }); - - it('renders no language text if the engine is a Meta Engine', () => { - const wrapper = mountWithIntl( - - ); - const tableContent = wrapper.find(EuiBasicTable).text(); - expect(tableContent).not.toContain('Universal'); - }); }); describe('actions', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx index b6401a58b40a3..7081010bac66f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx @@ -42,7 +42,7 @@ export const NAME_COLUMN: EuiTableFieldDataColumnType = { // Note: the below props are valid props per https://elastic.github.io/eui/#/tabular-content/tables (Responsive tables), but EUI's types have a bug reporting it as an error // @ts-ignore enlarge: true, - fullWidth: true, + width: '100%', truncateText: false, }, }; @@ -62,8 +62,7 @@ export const LANGUAGE_COLUMN: EuiTableFieldDataColumnType = { defaultMessage: 'Language', }), dataType: 'string', - render: (language: string, engine: EngineDetails) => - engine.isMeta ? '' : language || UNIVERSAL_LANGUAGE, + render: (language: string) => language || UNIVERSAL_LANGUAGE, }; export const DOCUMENT_COUNT_COLUMN: EuiTableFieldDataColumnType = { From fc882da25a7a4c50b8beb45dfd51dc204f6549a7 Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Mon, 29 Mar 2021 14:07:44 -0400 Subject: [PATCH 06/17] Swap EnginesTable with MetaEnginesTable in EnginesOverview for meta engines --- .../components/engines/engines_overview.test.tsx | 14 ++++++++++---- .../components/engines/engines_overview.tsx | 3 ++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.test.tsx index 3ca039907932e..7a00bdd46bff4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.test.tsx @@ -17,6 +17,8 @@ import { EuiEmptyPrompt } from '@elastic/eui'; import { LoadingState, EmptyState } from './components'; import { EnginesTable } from './engines_table'; +import { MetaEnginesTable } from './meta_engines_table'; + import { EnginesOverview } from './'; describe('EnginesOverview', () => { @@ -41,7 +43,11 @@ describe('EnginesOverview', () => { }, metaEnginesLoading: false, hasPlatinumLicense: false, + // AppLogic myRole: { canManageEngines: false }, + // MetaEnginesTableLogic + expandedSourceEngines: {}, + conflictingEnginesSets: {}, }; const actions = { loadEngines: jest.fn(), @@ -120,7 +126,7 @@ describe('EnginesOverview', () => { }); const wrapper = shallow(); - expect(wrapper.find(EnginesTable)).toHaveLength(2); + expect(wrapper.find(MetaEnginesTable)).toHaveLength(1); expect(actions.loadMetaEngines).toHaveBeenCalled(); }); @@ -147,7 +153,7 @@ describe('EnginesOverview', () => { metaEngines: [], }); const wrapper = shallow(); - const metaEnginesTable = wrapper.find(EnginesTable).last().dive(); + const metaEnginesTable = wrapper.find(MetaEnginesTable).dive(); const emptyPrompt = metaEnginesTable.dive().find(EuiEmptyPrompt).dive(); expect( @@ -199,10 +205,10 @@ describe('EnginesOverview', () => { const wrapper = shallow(); const pageEvent = { page: { index: 0 } }; - wrapper.find(EnginesTable).first().simulate('change', pageEvent); + wrapper.find(EnginesTable).simulate('change', pageEvent); expect(actions.onEnginesPagination).toHaveBeenCalledWith(1); - wrapper.find(EnginesTable).last().simulate('change', pageEvent); + wrapper.find(MetaEnginesTable).simulate('change', pageEvent); expect(actions.onMetaEnginesPagination).toHaveBeenCalledWith(1); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.tsx index a09f30035bafc..41cbcc87f1a47 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.tsx @@ -42,6 +42,7 @@ import { EnginesLogic } from './engines_logic'; import { EnginesTable } from './engines_table'; import './engines_overview.scss'; +import { MetaEnginesTable } from './meta_engines_table'; export const EnginesOverview: React.FC = () => { const { hasPlatinumLicense } = useValues(LicensingLogic); @@ -145,7 +146,7 @@ export const EnginesOverview: React.FC = () => { - Date: Mon, 5 Apr 2021 17:53:46 -0400 Subject: [PATCH 07/17] Missing test for MetaEnginesTableNameColumnContent --- ...engines_table_name_column_content.test.tsx | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_name_column_content.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_name_column_content.test.tsx index dc3b3086b13b3..2d78a95255783 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_name_column_content.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_name_column_content.test.tsx @@ -19,7 +19,30 @@ import { EngineDetails } from '../../engine/types'; import { MetaEnginesTableNameColumnContent } from './meta_engines_table_name_column_content'; describe('MetaEnginesTableNameColumnContent', () => { - it('includes the name of the engine', () => {}); + it('includes the name of the engine', () => { + const wrapper = mountWithIntl( + + ); + + expect(wrapper.text()).toContain('test-engine'); + }); describe('toggle button', () => { it('displays expanded row when the row is currently hidden', () => { From 6982ce0798535cd3fa913f4d4e1091ac0ce6220c Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Thu, 8 Apr 2021 13:19:34 -0400 Subject: [PATCH 08/17] Created new /app_search/components/engines/components/tables directory --- .../tables}/engines_table.test.tsx | 15 +++++++------- .../{ => components/tables}/engines_table.tsx | 20 +++++++++---------- .../tables}/meta_engines_table.test.tsx | 19 +++++++++--------- .../tables}/meta_engines_table.tsx | 18 ++++++++--------- .../meta_engines_table_expanded_row.scss | 0 .../meta_engines_table_expanded_row.test.tsx | 4 ++-- .../meta_engines_table_expanded_row.tsx | 5 +++-- .../tables}/meta_engines_table_logic.test.ts | 4 ++-- .../tables}/meta_engines_table_logic.ts | 8 ++++---- ...engines_table_name_column_content.test.tsx | 6 +++--- ...meta_engines_table_name_column_content.tsx | 8 ++++---- .../{ => components/tables}/utils.test.ts | 4 ++-- .../engines/{ => components/tables}/utils.ts | 4 ++-- .../engines/engines_overview.test.tsx | 5 ++--- .../components/engines/engines_overview.tsx | 4 ++-- 15 files changed, 61 insertions(+), 63 deletions(-) rename x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/{ => components/tables}/engines_table.test.tsx (94%) rename x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/{ => components/tables}/engines_table.tsx (90%) rename x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/{ => components/tables}/meta_engines_table.test.tsx (92%) rename x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/{ => components/tables}/meta_engines_table.tsx (91%) rename x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/{ => tables}/meta_engines_table_expanded_row.scss (100%) rename x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/{ => tables}/meta_engines_table_expanded_row.test.tsx (94%) rename x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/{ => tables}/meta_engines_table_expanded_row.tsx (96%) rename x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/{ => components/tables}/meta_engines_table_logic.test.ts (98%) rename x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/{ => components/tables}/meta_engines_table_logic.ts (93%) rename x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/{ => tables}/meta_engines_table_name_column_content.test.tsx (97%) rename x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/{ => tables}/meta_engines_table_name_column_content.tsx (87%) rename x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/{ => components/tables}/utils.test.ts (91%) rename x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/{ => components/tables}/utils.ts (93%) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engines_table.test.tsx similarity index 94% rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.test.tsx rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engines_table.test.tsx index b43f120674533..2b1dd58f74deb 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engines_table.test.tsx @@ -5,8 +5,8 @@ * 2.0. */ -import '../../../__mocks__/enterprise_search_url.mock'; -import { mockTelemetryActions, mountWithIntl, setMockValues } from '../../../__mocks__'; +import '../../../../../__mocks__/enterprise_search_url.mock'; +import { mockTelemetryActions, mountWithIntl, setMockValues } from '../../../../../__mocks__'; import React from 'react'; @@ -14,13 +14,12 @@ import { ReactWrapper, shallow } from 'enzyme'; import { EuiBasicTable, EuiPagination, EuiButtonEmpty, EuiIcon, EuiTableRow } from '@elastic/eui'; -import { KibanaLogic } from '../../../shared/kibana'; -import { EuiLinkTo } from '../../../shared/react_router_helpers'; +import { KibanaLogic } from '../../../../../shared/kibana'; +import { EuiLinkTo } from '../../../../../shared/react_router_helpers'; +import { TelemetryLogic } from '../../../../../shared/telemetry'; +import { EngineDetails } from '../../../engine/types'; +import { EnginesLogic } from '../../engines_logic'; -import { TelemetryLogic } from '../../../shared/telemetry'; -import { EngineDetails } from '../engine/types'; - -import { EnginesLogic } from './engines_logic'; import { EnginesTable } from './engines_table'; describe('EnginesTable', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engines_table.tsx similarity index 90% rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engines_table.tsx index 7081010bac66f..5ed896dd23aa8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engines_table.tsx @@ -19,16 +19,16 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedNumber } from '@kbn/i18n/react'; -import { MANAGE_BUTTON_LABEL, DELETE_BUTTON_LABEL } from '../../../shared/constants'; -import { KibanaLogic } from '../../../shared/kibana'; -import { EuiLinkTo } from '../../../shared/react_router_helpers'; -import { TelemetryLogic } from '../../../shared/telemetry'; -import { AppLogic } from '../../app_logic'; -import { UNIVERSAL_LANGUAGE } from '../../constants'; -import { ENGINE_PATH } from '../../routes'; -import { generateEncodedPath } from '../../utils/encode_path_params'; -import { FormattedDateTime } from '../../utils/formatted_date_time'; -import { EngineDetails } from '../engine/types'; +import { MANAGE_BUTTON_LABEL, DELETE_BUTTON_LABEL } from '../../../../../shared/constants'; +import { KibanaLogic } from '../../../../../shared/kibana'; +import { EuiLinkTo } from '../../../../../shared/react_router_helpers'; +import { TelemetryLogic } from '../../../../../shared/telemetry'; +import { AppLogic } from '../../../../app_logic'; +import { UNIVERSAL_LANGUAGE } from '../../../../constants'; +import { ENGINE_PATH } from '../../../../routes'; +import { generateEncodedPath } from '../../../../utils/encode_path_params'; +import { FormattedDateTime } from '../../../../utils/formatted_date_time'; +import { EngineDetails } from '../../../engine/types'; export const NAME_COLUMN: EuiTableFieldDataColumnType = { field: 'name', diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.test.tsx similarity index 92% rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table.test.tsx rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.test.tsx index 97f1e587bf773..0072f7fbca32e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.test.tsx @@ -5,8 +5,8 @@ * 2.0. */ -import '../../../__mocks__/enterprise_search_url.mock'; -import { mockTelemetryActions, mountWithIntl, setMockValues } from '../../../__mocks__'; +import '../../../../../__mocks__/enterprise_search_url.mock'; +import { mockTelemetryActions, mountWithIntl, setMockValues } from '../../../../../__mocks__'; import React from 'react'; @@ -14,16 +14,15 @@ import { ReactWrapper, shallow } from 'enzyme'; import { EuiBasicTable, EuiPagination, EuiButtonEmpty, EuiIcon, EuiTableRow } from '@elastic/eui'; -import { KibanaLogic } from '../../../shared/kibana'; -import { EuiLinkTo } from '../../../shared/react_router_helpers'; +import { KibanaLogic } from '../../../../../shared/kibana'; +import { EuiLinkTo } from '../../../../../shared/react_router_helpers'; +import { TelemetryLogic } from '../../../../../shared/telemetry'; +import { EngineDetails } from '../../../engine/types'; +import { EnginesLogic } from '../../engines_logic'; -import { TelemetryLogic } from '../../../shared/telemetry'; -import { EngineDetails } from '../engine/types'; - -import { MetaEnginesTableExpandedRow } from './components/meta_engines_table_expanded_row'; -import { MetaEnginesTableNameColumnContent } from './components/meta_engines_table_name_column_content'; -import { EnginesLogic } from './engines_logic'; import { MetaEnginesTable } from './meta_engines_table'; +import { MetaEnginesTableExpandedRow } from './meta_engines_table_expanded_row'; +import { MetaEnginesTableNameColumnContent } from './meta_engines_table_name_column_content'; describe('MetaEnginesTable', () => { const onChange = jest.fn(); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.tsx similarity index 91% rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table.tsx rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.tsx index 43d20434ff76c..1d5a5fa49ad5e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.tsx @@ -17,22 +17,22 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { KibanaLogic } from '../../../shared/kibana'; -import { TelemetryLogic } from '../../../shared/telemetry'; -import { AppLogic } from '../../app_logic'; -import { ENGINE_PATH } from '../../routes'; -import { generateEncodedPath } from '../../utils/encode_path_params'; -import { EngineDetails } from '../engine/types'; - -import { MetaEnginesTableExpandedRow } from './components/meta_engines_table_expanded_row'; -import { MetaEnginesTableNameColumnContent } from './components/meta_engines_table_name_column_content'; +import { KibanaLogic } from '../../../../../shared/kibana'; +import { TelemetryLogic } from '../../../../../shared/telemetry'; +import { AppLogic } from '../../../../app_logic'; +import { ENGINE_PATH } from '../../../../routes'; +import { generateEncodedPath } from '../../../../utils/encode_path_params'; +import { EngineDetails } from '../../../engine/types'; + import { CREATED_AT_COLUMN, DOCUMENT_COUNT_COLUMN, FIELD_COUNT_COLUMN, NAME_COLUMN, } from './engines_table'; +import { MetaEnginesTableExpandedRow } from './meta_engines_table_expanded_row'; import { MetaEnginesTableLogic } from './meta_engines_table_logic'; +import { MetaEnginesTableNameColumnContent } from './meta_engines_table_name_column_content'; import { getConflictingEnginesSet } from './utils'; interface EnginesTableProps { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_expanded_row.scss b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_expanded_row.scss similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_expanded_row.scss rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_expanded_row.scss diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_expanded_row.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_expanded_row.test.tsx similarity index 94% rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_expanded_row.test.tsx rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_expanded_row.test.tsx index abdbeaff4c1c0..dcaa1a2b7c246 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_expanded_row.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_expanded_row.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { mountWithIntl } from '../../../../__mocks__'; +import { mountWithIntl } from '../../../../../__mocks__'; import React from 'react'; @@ -13,7 +13,7 @@ import { shallow } from 'enzyme'; import { EuiBasicTable, EuiHealth } from '@elastic/eui'; -import { EngineDetails } from '../../engine/types'; +import { EngineDetails } from '../../../engine/types'; import { MetaEnginesTableExpandedRow } from './meta_engines_table_expanded_row'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_expanded_row.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_expanded_row.tsx similarity index 96% rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_expanded_row.tsx rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_expanded_row.tsx index d4064b06d1902..2544fcdf21de5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_expanded_row.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_expanded_row.tsx @@ -9,13 +9,14 @@ import React from 'react'; import { EuiBasicTable, EuiHealth, EuiTextAlign, EuiTitle } from '@elastic/eui'; -import { EngineDetails } from '../../engine/types'; +import { EngineDetails } from '../../../engine/types'; + import { CREATED_AT_COLUMN, DOCUMENT_COUNT_COLUMN, FIELD_COUNT_COLUMN, NAME_COLUMN, -} from '../engines_table'; +} from './engines_table'; import './meta_engines_table_expanded_row.scss'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_logic.test.ts similarity index 98% rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table_logic.test.ts rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_logic.test.ts index f79881c2021c6..c45691ee0192a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_logic.test.ts @@ -5,11 +5,11 @@ * 2.0. */ -import { LogicMounter, mockFlashMessageHelpers, mockHttpValues } from '../../../__mocks__'; +import { LogicMounter, mockFlashMessageHelpers, mockHttpValues } from '../../../../../__mocks__'; import { nextTick } from '@kbn/test/jest'; -import { EngineDetails } from '../engine/types'; +import { EngineDetails } from '../../../engine/types'; import { MetaEnginesTableLogic } from './meta_engines_table_logic'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_logic.ts similarity index 93% rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table_logic.ts rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_logic.ts index 22a2325eeb0fc..2cfdcc42d0b2e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/meta_engines_table_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_logic.ts @@ -6,12 +6,12 @@ */ import { kea, MakeLogicType } from 'kea'; -import { Meta } from '../../../../../common/types'; -import { flashAPIErrors } from '../../../shared/flash_messages'; +import { Meta } from '../../../../../../../common/types'; +import { flashAPIErrors } from '../../../../../shared/flash_messages'; -import { HttpLogic } from '../../../shared/http'; +import { HttpLogic } from '../../../../../shared/http'; -import { EngineDetails } from '../engine/types'; +import { EngineDetails } from '../../../engine/types'; interface MetaEnginesTableValues { expandedRows: { [id: string]: boolean }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_name_column_content.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_name_column_content.test.tsx similarity index 97% rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_name_column_content.test.tsx rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_name_column_content.test.tsx index 2d78a95255783..bded3051fb5c9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_name_column_content.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_name_column_content.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { mountWithIntl } from '../../../../__mocks__'; +import { mountWithIntl } from '../../../../../__mocks__'; import React from 'react'; @@ -13,8 +13,8 @@ import { shallow } from 'enzyme'; import { EuiHealth } from '@elastic/eui'; -import { SchemaConflictFieldTypes, SchemaConflicts } from '../../../../shared/types'; -import { EngineDetails } from '../../engine/types'; +import { SchemaConflictFieldTypes, SchemaConflicts } from '../../../../../shared/types'; +import { EngineDetails } from '../../../engine/types'; import { MetaEnginesTableNameColumnContent } from './meta_engines_table_name_column_content'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_name_column_content.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_name_column_content.tsx similarity index 87% rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_name_column_content.tsx rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_name_column_content.tsx index cac89c2e441ab..f5d50baa38552 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/meta_engines_table_name_column_content.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_name_column_content.tsx @@ -9,10 +9,10 @@ import React from 'react'; import { EuiFlexGroup, EuiIcon, EuiHealth, EuiFlexItem } from '@elastic/eui'; -import { EuiLinkTo } from '../../../../shared/react_router_helpers'; -import { ENGINE_PATH } from '../../../routes'; -import { generateEncodedPath } from '../../../utils/encode_path_params'; -import { EngineDetails } from '../../engine/types'; +import { EuiLinkTo } from '../../../../../shared/react_router_helpers'; +import { ENGINE_PATH } from '../../../../routes'; +import { generateEncodedPath } from '../../../../utils/encode_path_params'; +import { EngineDetails } from '../../../engine/types'; interface MetaEnginesTableNameContentProps { name: string; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/utils.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/utils.test.ts similarity index 91% rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/utils.test.ts rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/utils.test.ts index 73d1704430e7f..894656689f0a6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/utils.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/utils.test.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { SchemaConflicts } from '../../../shared/types'; -import { EngineDetails } from '../engine/types'; +import { SchemaConflicts } from '../../../../../shared/types'; +import { EngineDetails } from '../../../engine/types'; import { getConflictingEnginesSet } from './utils'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/utils.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/utils.ts similarity index 93% rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/utils.ts rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/utils.ts index 48410876b4de8..c21e01683c2ea 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/utils.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/utils.ts @@ -7,8 +7,8 @@ import { flatten } from 'lodash'; -import { SchemaConflictFieldTypes, SchemaConflicts } from '../../../shared/types'; -import { EngineDetails } from '../engine/types'; +import { SchemaConflictFieldTypes, SchemaConflicts } from '../../../../../shared/types'; +import { EngineDetails } from '../../../engine/types'; const getConflictingEnginesFromConflictingField = ( conflictingField: SchemaConflictFieldTypes diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.test.tsx index 7a00bdd46bff4..c47b169ede364 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.test.tsx @@ -15,9 +15,8 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { EuiEmptyPrompt } from '@elastic/eui'; import { LoadingState, EmptyState } from './components'; -import { EnginesTable } from './engines_table'; - -import { MetaEnginesTable } from './meta_engines_table'; +import { EnginesTable } from './components/tables/engines_table'; +import { MetaEnginesTable } from './components/tables/meta_engines_table'; import { EnginesOverview } from './'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.tsx index 41cbcc87f1a47..02e7694ba671f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.tsx @@ -30,6 +30,8 @@ import { EngineIcon, MetaEngineIcon } from '../../icons'; import { ENGINE_CREATION_PATH, META_ENGINE_CREATION_PATH } from '../../routes'; import { EnginesOverviewHeader, LoadingState, EmptyState } from './components'; +import { EnginesTable } from './components/tables/engines_table'; +import { MetaEnginesTable } from './components/tables/meta_engines_table'; import { CREATE_AN_ENGINE_BUTTON_LABEL, CREATE_A_META_ENGINE_BUTTON_LABEL, @@ -39,10 +41,8 @@ import { META_ENGINES_TITLE, } from './constants'; import { EnginesLogic } from './engines_logic'; -import { EnginesTable } from './engines_table'; import './engines_overview.scss'; -import { MetaEnginesTable } from './meta_engines_table'; export const EnginesOverview: React.FC = () => { const { hasPlatinumLicense } = useValues(LicensingLogic); From aabd29a47d3322ea90005720c24ba4985c1d8cf3 Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Thu, 8 Apr 2021 13:28:46 -0400 Subject: [PATCH 09/17] Moving columns to shared_columns.tsx file --- .../components/tables/engines_table.tsx | 56 ++------------- .../components/tables/meta_engines_table.tsx | 13 ++-- .../meta_engines_table_expanded_row.tsx | 14 ++-- .../components/tables/shared_columns.tsx | 69 +++++++++++++++++++ .../components/engines/constants.ts | 5 ++ 5 files changed, 92 insertions(+), 65 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/shared_columns.tsx diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engines_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engines_table.tsx index 5ed896dd23aa8..3e9cafc9934f7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engines_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engines_table.tsx @@ -17,7 +17,6 @@ import { EuiTableFieldDataColumnType, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FormattedNumber } from '@kbn/i18n/react'; import { MANAGE_BUTTON_LABEL, DELETE_BUTTON_LABEL } from '../../../../../shared/constants'; import { KibanaLogic } from '../../../../../shared/kibana'; @@ -27,34 +26,14 @@ import { AppLogic } from '../../../../app_logic'; import { UNIVERSAL_LANGUAGE } from '../../../../constants'; import { ENGINE_PATH } from '../../../../routes'; import { generateEncodedPath } from '../../../../utils/encode_path_params'; -import { FormattedDateTime } from '../../../../utils/formatted_date_time'; import { EngineDetails } from '../../../engine/types'; -export const NAME_COLUMN: EuiTableFieldDataColumnType = { - field: 'name', - name: i18n.translate('xpack.enterpriseSearch.appSearch.enginesOverview.table.column.name', { - defaultMessage: 'Name', - }), - width: '30%', - truncateText: true, - mobileOptions: { - header: true, - // Note: the below props are valid props per https://elastic.github.io/eui/#/tabular-content/tables (Responsive tables), but EUI's types have a bug reporting it as an error - // @ts-ignore - enlarge: true, - width: '100%', - truncateText: false, - }, -}; - -export const CREATED_AT_COLUMN: EuiTableFieldDataColumnType = { - field: 'created_at', - name: i18n.translate('xpack.enterpriseSearch.appSearch.enginesOverview.table.column.createdAt', { - defaultMessage: 'Created At', - }), - dataType: 'string', - render: (dateString: string) => , -}; +import { + CREATED_AT_COLUMN, + DOCUMENT_COUNT_COLUMN, + FIELD_COUNT_COLUMN, + NAME_COLUMN, +} from './shared_columns'; export const LANGUAGE_COLUMN: EuiTableFieldDataColumnType = { field: 'language', @@ -65,29 +44,6 @@ export const LANGUAGE_COLUMN: EuiTableFieldDataColumnType = { render: (language: string) => language || UNIVERSAL_LANGUAGE, }; -export const DOCUMENT_COUNT_COLUMN: EuiTableFieldDataColumnType = { - field: 'document_count', - name: i18n.translate( - 'xpack.enterpriseSearch.appSearch.enginesOverview.table.column.documentCount', - { - defaultMessage: 'Document Count', - } - ), - dataType: 'number', - render: (number: number) => , - truncateText: true, -}; - -export const FIELD_COUNT_COLUMN: EuiTableFieldDataColumnType = { - field: 'field_count', - name: i18n.translate('xpack.enterpriseSearch.appSearch.enginesOverview.table.column.fieldCount', { - defaultMessage: 'Field Count', - }), - dataType: 'number', - render: (number: number) => , - truncateText: true, -}; - interface EnginesTableProps { items: EngineDetails[]; loading: boolean; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.tsx index 1d5a5fa49ad5e..e54ab6a15a108 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.tsx @@ -24,15 +24,16 @@ import { ENGINE_PATH } from '../../../../routes'; import { generateEncodedPath } from '../../../../utils/encode_path_params'; import { EngineDetails } from '../../../engine/types'; +import { MetaEnginesTableExpandedRow } from './meta_engines_table_expanded_row'; +import { MetaEnginesTableLogic } from './meta_engines_table_logic'; +import { MetaEnginesTableNameColumnContent } from './meta_engines_table_name_column_content'; import { + BLANK_COLUMN, CREATED_AT_COLUMN, DOCUMENT_COUNT_COLUMN, FIELD_COUNT_COLUMN, NAME_COLUMN, -} from './engines_table'; -import { MetaEnginesTableExpandedRow } from './meta_engines_table_expanded_row'; -import { MetaEnginesTableLogic } from './meta_engines_table_logic'; -import { MetaEnginesTableNameColumnContent } from './meta_engines_table_name_column_content'; +} from './shared_columns'; import { getConflictingEnginesSet } from './utils'; interface EnginesTableProps { @@ -117,9 +118,7 @@ export const MetaEnginesTable: React.FC = ({ ), }, CREATED_AT_COLUMN, - { - render: () => <>, // This is a blank column in place of the `Language` column - }, + BLANK_COLUMN, DOCUMENT_COUNT_COLUMN, FIELD_COUNT_COLUMN, ]; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_expanded_row.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_expanded_row.tsx index 2544fcdf21de5..41bb62eece7a9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_expanded_row.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_expanded_row.tsx @@ -10,13 +10,15 @@ import React from 'react'; import { EuiBasicTable, EuiHealth, EuiTextAlign, EuiTitle } from '@elastic/eui'; import { EngineDetails } from '../../../engine/types'; +import { SOURCE_ENGINES_TITLE } from '../../constants'; import { + BLANK_COLUMN, CREATED_AT_COLUMN, DOCUMENT_COUNT_COLUMN, FIELD_COUNT_COLUMN, NAME_COLUMN, -} from './engines_table'; +} from './shared_columns'; import './meta_engines_table_expanded_row.scss'; @@ -32,7 +34,7 @@ export const MetaEnginesTableExpandedRow: React.FC -

Source Engines

+

{SOURCE_ENGINES_TITLE}

<>, // This is a blank column in place of the `Language` column - }, + BLANK_COLUMN, DOCUMENT_COUNT_COLUMN, { ...FIELD_COUNT_COLUMN, @@ -64,9 +64,7 @@ export const MetaEnginesTableExpandedRow: React.FC ), }, - { - render: () => <>, // This is a blank column in place of the `Actions` column - }, + BLANK_COLUMN, ]} /> diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/shared_columns.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/shared_columns.tsx new file mode 100644 index 0000000000000..3eaed7efe317d --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/shared_columns.tsx @@ -0,0 +1,69 @@ +/* + * 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 { EuiTableFieldDataColumnType, EuiTableComputedColumnType } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedNumber } from '@kbn/i18n/react'; + +import { FormattedDateTime } from '../../../../utils/formatted_date_time'; +import { EngineDetails } from '../../../engine/types'; + +export const BLANK_COLUMN: EuiTableComputedColumnType = { + render: () => <>, + 'aria-hidden': true, +}; + +export const NAME_COLUMN: EuiTableFieldDataColumnType = { + field: 'name', + name: i18n.translate('xpack.enterpriseSearch.appSearch.enginesOverview.table.column.name', { + defaultMessage: 'Name', + }), + width: '30%', + truncateText: true, + mobileOptions: { + header: true, + // Note: the below props are valid props per https://elastic.github.io/eui/#/tabular-content/tables (Responsive tables), but EUI's types have a bug reporting it as an error + // @ts-ignore + enlarge: true, + width: '100%', + truncateText: false, + }, +}; + +export const CREATED_AT_COLUMN: EuiTableFieldDataColumnType = { + field: 'created_at', + name: i18n.translate('xpack.enterpriseSearch.appSearch.enginesOverview.table.column.createdAt', { + defaultMessage: 'Created At', + }), + dataType: 'string', + render: (dateString: string) => , +}; + +export const DOCUMENT_COUNT_COLUMN: EuiTableFieldDataColumnType = { + field: 'document_count', + name: i18n.translate( + 'xpack.enterpriseSearch.appSearch.enginesOverview.table.column.documentCount', + { + defaultMessage: 'Document Count', + } + ), + dataType: 'number', + render: (number: number) => , + truncateText: true, +}; + +export const FIELD_COUNT_COLUMN: EuiTableFieldDataColumnType = { + field: 'field_count', + name: i18n.translate('xpack.enterpriseSearch.appSearch.enginesOverview.table.column.fieldCount', { + defaultMessage: 'Field Count', + }), + dataType: 'number', + render: (number: number) => , + truncateText: true, +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/constants.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/constants.ts index 1955084393e57..4a5d1d47ee8d7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/constants.ts @@ -16,6 +16,11 @@ export const META_ENGINES_TITLE = i18n.translate( { defaultMessage: 'Meta Engines' } ); +export const SOURCE_ENGINES_TITLE = i18n.translate( + 'xpack.enterpriseSearch.appSearch.metaEngines.title', + { defaultMessage: 'Source Engines' } +); + export const CREATE_AN_ENGINE_BUTTON_LABEL = i18n.translate( 'xpack.enterpriseSearch.appSearch.engines.createAnEngineButton.ButtonLabel', { From a364bf8da39e871755633ea24a87cc449c51443e Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Thu, 8 Apr 2021 13:33:25 -0400 Subject: [PATCH 10/17] Updates to MetaEnginesTableExpandedRow and MetaEnginesTableNameColumnContent --- .../meta_engines_table_expanded_row.scss | 3 +- ...meta_engines_table_name_column_content.tsx | 44 +++++++++++++------ 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_expanded_row.scss b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_expanded_row.scss index 1f1d6d0d01537..25d4bfd89ded4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_expanded_row.scss +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_expanded_row.scss @@ -1,6 +1,5 @@ - .meta-engines__source-engines-table { - margin: -8px -8px 7px -8px; + margin: -$euiSizeS -$euiSizeS $euiSizeS -$euiSizeS; thead { display: none; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_name_column_content.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_name_column_content.tsx index f5d50baa38552..fd32ed59966e6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_name_column_content.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_name_column_content.tsx @@ -9,6 +9,8 @@ import React from 'react'; import { EuiFlexGroup, EuiIcon, EuiHealth, EuiFlexItem } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + import { EuiLinkTo } from '../../../../../shared/react_router_helpers'; import { ENGINE_PATH } from '../../../../routes'; import { generateEncodedPath } from '../../../../utils/encode_path_params'; @@ -38,24 +40,38 @@ export const MetaEnginesTableNameColumnContent: React.FC {name} - (isExpanded ? hideRow(name) : showRow(name))} + aria-expanded={isExpanded} className="meta-engines__expand-row" - responsive={false} > - - - - - {item.engine_count || '0'} Engine{item.engine_count !== 1 ? 's' : ''} - - {item.schemaConflicts && Object.keys(item.schemaConflicts).length > 0 && ( + + + + - Field-type conflict + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.engines.metaEnginesTable.sourceEnginesCount', + { + defaultMessage: '{sourceEnginesCount, plural, one {# Engine} other {# Engines}}', + values: { sourceEnginesCount: item.engine_count || 0 }, + } + )} - )} - + {item.schemaConflicts && Object.keys(item.schemaConflicts).length > 0 && ( + + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.engines.metaEnginesTable.fieldTypeConflictWarning', + { + defaultMessage: 'Field-type conflict', + } + )} + + + )} + + ); From dbfcc8259fb169948d64dbeec3367e54fb2888b0 Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Thu, 8 Apr 2021 13:56:50 -0400 Subject: [PATCH 11/17] Fixes to EnginesTable, MetaEnginesTable, MetaEnginesTableLogic --- .../components/tables/engines_table.tsx | 18 +-- .../components/tables/meta_engines_table.tsx | 72 +++++------ .../tables/meta_engines_table_logic.ts | 41 +++---- .../engines/components/tables/types.ts | 26 ++++ .../engines/components/tables/utils.test.ts | 115 +++++++++++++----- .../engines/components/tables/utils.ts | 8 +- 6 files changed, 165 insertions(+), 115 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/types.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engines_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engines_table.tsx index 3e9cafc9934f7..cf2a8ca61c190 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engines_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engines_table.tsx @@ -5,14 +5,13 @@ * 2.0. */ -import React, { ReactNode } from 'react'; +import React from 'react'; import { useActions, useValues } from 'kea'; import { EuiBasicTable, EuiBasicTableColumn, - CriteriaWithPagination, EuiTableActionsColumnType, EuiTableFieldDataColumnType, } from '@elastic/eui'; @@ -34,6 +33,7 @@ import { FIELD_COUNT_COLUMN, NAME_COLUMN, } from './shared_columns'; +import { EnginesTableProps } from './types'; export const LANGUAGE_COLUMN: EuiTableFieldDataColumnType = { field: 'language', @@ -44,20 +44,6 @@ export const LANGUAGE_COLUMN: EuiTableFieldDataColumnType = { render: (language: string) => language || UNIVERSAL_LANGUAGE, }; -interface EnginesTableProps { - items: EngineDetails[]; - loading: boolean; - noItemsMessage?: ReactNode; - pagination: { - pageIndex: number; - pageSize: number; - totalItemCount: number; - hidePerPageOptions: boolean; - }; - onChange(criteria: CriteriaWithPagination): void; - onDeleteEngine(engine: EngineDetails): void; -} - export const EnginesTable: React.FC = ({ items, loading, diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.tsx index e54ab6a15a108..d244a9f517731 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.tsx @@ -5,16 +5,11 @@ * 2.0. */ -import React, { ReactNode } from 'react'; +import React, { ReactNode, useMemo } from 'react'; import { useActions, useValues } from 'kea'; -import { - EuiBasicTable, - EuiBasicTableColumn, - CriteriaWithPagination, - EuiTableActionsColumnType, -} from '@elastic/eui'; +import { EuiBasicTable, EuiBasicTableColumn, EuiTableActionsColumnType } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { KibanaLogic } from '../../../../../shared/kibana'; @@ -34,22 +29,9 @@ import { FIELD_COUNT_COLUMN, NAME_COLUMN, } from './shared_columns'; +import { EnginesTableProps } from './types'; import { getConflictingEnginesSet } from './utils'; -interface EnginesTableProps { - items: EngineDetails[]; - loading: boolean; - noItemsMessage?: ReactNode; - pagination: { - pageIndex: number; - pageSize: number; - totalItemCount: number; - hidePerPageOptions: boolean; - }; - onChange(criteria: CriteriaWithPagination): void; - onDeleteEngine(engine: EngineDetails): void; -} - interface IItemIdToExpandedRowMap { [id: string]: ReactNode; } @@ -66,35 +48,39 @@ export const MetaEnginesTable: React.FC = ({ onChange, onDeleteEngine, }) => { - const metaEnginesTableLogic = MetaEnginesTableLogic({ metaEngines: items }); - const { expandedSourceEngines } = useValues(metaEnginesTableLogic); - const { hideRow, fetchOrDisplayRow } = useActions(metaEnginesTableLogic); + const { expandedSourceEngines } = useValues(MetaEnginesTableLogic); + const { hideRow, fetchOrDisplayRow } = useActions(MetaEnginesTableLogic); const { sendAppSearchTelemetry } = useActions(TelemetryLogic); const { navigateToUrl } = useValues(KibanaLogic); const { myRole: { canManageMetaEngines }, } = useValues(AppLogic); - const conflictingEnginesSets: ConflictingEnginesSets = items.reduce((accumulator, metaEngine) => { - return { - ...accumulator, - [metaEngine.name]: getConflictingEnginesSet(metaEngine), - }; - }, {}); + const conflictingEnginesSets: ConflictingEnginesSets = useMemo( + () => + items.reduce((accumulator, metaEngine) => { + return { + ...accumulator, + [metaEngine.name]: getConflictingEnginesSet(metaEngine), + }; + }, {}), + [items] + ); - const itemIdToExpandedRowMap: IItemIdToExpandedRowMap = Object.keys(expandedSourceEngines).reduce( - (accumulator, engineName) => { - return { - ...accumulator, - [engineName]: ( - - ), - }; - }, - {} + const itemIdToExpandedRowMap: IItemIdToExpandedRowMap = useMemo( + () => + Object.keys(expandedSourceEngines).reduce((accumulator, engineName) => { + return { + ...accumulator, + [engineName]: ( + + ), + }; + }, {}), + [expandedSourceEngines, conflictingEnginesSets] ); const sendEngineTableLinkClickTelemetry = () => diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_logic.ts index 2cfdcc42d0b2e..04e1ee5c1b61a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_logic.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import { kea, MakeLogicType } from 'kea'; import { Meta } from '../../../../../../../common/types'; @@ -22,11 +23,11 @@ interface MetaEnginesTableValues { interface MetaEnginesTableActions { addSourceEngines( sourceEngines: MetaEnginesTableValues['sourceEngines'] - ): MetaEnginesTableValues['sourceEngines']; - displayRow(itemId: string): string; - fetchOrDisplayRow(itemId: string): string; - fetchSourceEngines(engineName: string): string; - hideRow(itemId: string): string; + ): { sourceEngines: MetaEnginesTableValues['sourceEngines'] }; + displayRow(itemId: string): { itemId: string }; + fetchOrDisplayRow(itemId: string): { itemId: string }; + fetchSourceEngines(engineName: string): { engineName: string }; + hideRow(itemId: string): { itemId: string }; } interface EnginesAPIResponse { @@ -34,30 +35,26 @@ interface EnginesAPIResponse { meta: Meta; } -interface MetaEnginesTableProps { - metaEngines: EngineDetails[]; -} - export const MetaEnginesTableLogic = kea< - MakeLogicType + MakeLogicType >({ path: ['enterprise_search', 'app_search', 'meta_engines_table_logic'], actions: () => ({ - addSourceEngines: (sourceEngines: object) => sourceEngines, - displayRow: (itemId: string) => itemId, - hideRow: (itemId: string) => itemId, - fetchOrDisplayRow: (itemId) => itemId, - fetchSourceEngines: (engineName) => engineName, + addSourceEngines: (sourceEngines) => ({ sourceEngines }), + displayRow: (itemId) => ({ itemId }), + hideRow: (itemId) => ({ itemId }), + fetchOrDisplayRow: (itemId) => ({ itemId }), + fetchSourceEngines: (engineName) => ({ engineName }), }), reducers: () => ({ expandedRows: [ {}, { - displayRow: (expandedRows, itemId) => ({ + displayRow: (expandedRows, { itemId }) => ({ ...expandedRows, [itemId]: true, }), - hideRow: (expandedRows, itemId) => { + hideRow: (expandedRows, { itemId }) => { const newRows = { ...expandedRows }; delete newRows[itemId]; return newRows; @@ -67,9 +64,9 @@ export const MetaEnginesTableLogic = kea< sourceEngines: [ {}, { - addSourceEngines: (sourceEngines, newEngines) => ({ - ...sourceEngines, - ...newEngines, + addSourceEngines: (currentSourceEngines, { sourceEngines: newSourceEngines }) => ({ + ...currentSourceEngines, + ...newSourceEngines, }), }, ], @@ -86,7 +83,7 @@ export const MetaEnginesTableLogic = kea< ], }, listeners: ({ actions, values }) => ({ - fetchOrDisplayRow: (itemId) => { + fetchOrDisplayRow: ({ itemId }) => { const sourceEngines = values.sourceEngines; if (sourceEngines[itemId]) { actions.displayRow(itemId); @@ -94,7 +91,7 @@ export const MetaEnginesTableLogic = kea< actions.fetchSourceEngines(itemId); } }, - fetchSourceEngines: (engineName) => { + fetchSourceEngines: ({ engineName }) => { const { http } = HttpLogic.values; let enginesAccumulator: EngineDetails[] = []; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/types.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/types.ts new file mode 100644 index 0000000000000..169294f059339 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/types.ts @@ -0,0 +1,26 @@ +/* + * 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 { ReactNode } from 'react'; + +import { CriteriaWithPagination } from '@elastic/eui'; + +import { EngineDetails } from '../../../engine/types'; + +export interface EnginesTableProps { + items: EngineDetails[]; + loading: boolean; + noItemsMessage?: ReactNode; + pagination: { + pageIndex: number; + pageSize: number; + totalItemCount: number; + hidePerPageOptions: boolean; + }; + onChange(criteria: CriteriaWithPagination): void; + onDeleteEngine(engine: EngineDetails): void; +} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/utils.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/utils.test.ts index 894656689f0a6..48ef706b6d3cd 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/utils.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/utils.test.ts @@ -5,41 +5,94 @@ * 2.0. */ -import { SchemaConflicts } from '../../../../../shared/types'; +import { SchemaConflictFieldTypes, SchemaConflicts } from '../../../../../shared/types'; import { EngineDetails } from '../../../engine/types'; -import { getConflictingEnginesSet } from './utils'; - -const DEFAULT_META_ENGINE_DETAILS = { - name: 'test-engine-1', - includedEngines: [ - { - name: 'source-engine-1', - }, - { - name: 'source-engine-2', - }, - { - name: 'source-engine-3', - }, - ] as EngineDetails[], - schemaConflicts: { - 'conflicting-field-1': { - text: ['source-engine-1'], - number: ['source-engine-2'], - geolocation: [], - date: [], - }, - 'conflicting-field-2': { - text: [], - number: [], - geolocation: ['source-engine-2'], - date: ['source-engine-3'], - }, - } as SchemaConflicts, -} as EngineDetails; +import { + getConflictingEnginesFromConflictingField, + getConflictingEnginesFromSchemaConflicts, + getConflictingEnginesSet, +} from './utils'; + +describe('getConflictingEnginesFromConflictingField', () => { + const CONFLICTING_FIELD: SchemaConflictFieldTypes = { + text: ['source-engine-1'], + number: ['source-engine-2', 'source-engine-3'], + geolocation: ['source-engine-4'], + date: ['source-engine-5', 'source-engine-6'], + }; + + it('returns a flat array of all engines with conflicts across different schema types, including duplicates', () => { + const result = getConflictingEnginesFromConflictingField(CONFLICTING_FIELD); + + // we can't guaruntee ordering + expect(result).toHaveLength(6); + expect(result).toContain('source-engine-1'); + expect(result).toContain('source-engine-2'); + expect(result).toContain('source-engine-3'); + expect(result).toContain('source-engine-4'); + expect(result).toContain('source-engine-5'); + expect(result).toContain('source-engine-6'); + }); +}); + +describe('getConflictingEnginesFromSchemaConflicts', () => { + it('returns a flat array of all engines with conflicts across all fields, including duplicates', () => { + const SCHEMA_CONFLICTS: SchemaConflicts = { + 'conflicting-field-1': { + text: ['source-engine-1'], + number: ['source-engine-2'], + geolocation: [], + date: [], + }, + 'conflicting-field-2': { + text: [], + number: [], + geolocation: ['source-engine-2'], + date: ['source-engine-3'], + }, + }; + + const result = getConflictingEnginesFromSchemaConflicts(SCHEMA_CONFLICTS); + + // we can't guaruntee ordering + expect(result).toHaveLength(4); + expect(result).toContain('source-engine-1'); + expect(result).toContain('source-engine-2'); + expect(result).toContain('source-engine-3'); + }); +}); describe('getConflictingEnginesSet', () => { + const DEFAULT_META_ENGINE_DETAILS = { + name: 'test-engine-1', + includedEngines: [ + { + name: 'source-engine-1', + }, + { + name: 'source-engine-2', + }, + { + name: 'source-engine-3', + }, + ] as EngineDetails[], + schemaConflicts: { + 'conflicting-field-1': { + text: ['source-engine-1'], + number: ['source-engine-2'], + geolocation: [], + date: [], + }, + 'conflicting-field-2': { + text: [], + number: [], + geolocation: ['source-engine-2'], + date: ['source-engine-3'], + }, + } as SchemaConflicts, + } as EngineDetails; + it('generates a set of engine names with any field conflicts for the meta-engine', () => { expect(getConflictingEnginesSet(DEFAULT_META_ENGINE_DETAILS)).toEqual( new Set(['source-engine-1', 'source-engine-2', 'source-engine-3']) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/utils.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/utils.ts index c21e01683c2ea..686cf6e7f9ae7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/utils.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/utils.ts @@ -10,11 +10,13 @@ import { flatten } from 'lodash'; import { SchemaConflictFieldTypes, SchemaConflicts } from '../../../../../shared/types'; import { EngineDetails } from '../../../engine/types'; -const getConflictingEnginesFromConflictingField = ( +export const getConflictingEnginesFromConflictingField = ( conflictingField: SchemaConflictFieldTypes -): string[] => flatten(Object.values(conflictingField)); +): string[] => Object.values(conflictingField).flat(); -const getConflictingEnginesFromSchemaConflicts = (schemaConflicts: SchemaConflicts): string[] => +export const getConflictingEnginesFromSchemaConflicts = ( + schemaConflicts: SchemaConflicts +): string[] => flatten(Object.values(schemaConflicts).map(getConflictingEnginesFromConflictingField)); // Given a meta-engine (represented by IEngineDetails), generate a Set of all source engines From a29c45aa50fa1d897c544c500f0c25bcdd938ca0 Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Mon, 12 Apr 2021 11:51:24 -0400 Subject: [PATCH 12/17] Remove flatten import --- .../app_search/components/engines/components/tables/utils.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/utils.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/utils.ts index 686cf6e7f9ae7..b1172237e3ad3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/utils.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/utils.ts @@ -5,8 +5,6 @@ * 2.0. */ -import { flatten } from 'lodash'; - import { SchemaConflictFieldTypes, SchemaConflicts } from '../../../../../shared/types'; import { EngineDetails } from '../../../engine/types'; @@ -16,8 +14,7 @@ export const getConflictingEnginesFromConflictingField = ( export const getConflictingEnginesFromSchemaConflicts = ( schemaConflicts: SchemaConflicts -): string[] => - flatten(Object.values(schemaConflicts).map(getConflictingEnginesFromConflictingField)); +): string[] => Object.values(schemaConflicts).flatMap(getConflictingEnginesFromConflictingField); // Given a meta-engine (represented by IEngineDetails), generate a Set of all source engines // who have schema conflicts in the context of that meta-engine From 39146c51f19701f77b8ea0ede9ff802bf44eaf2f Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Mon, 12 Apr 2021 12:59:40 -0400 Subject: [PATCH 13/17] Fix i18n --- .../applications/app_search/components/engines/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/constants.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/constants.ts index 4a5d1d47ee8d7..c6c077e984efe 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/constants.ts @@ -17,7 +17,7 @@ export const META_ENGINES_TITLE = i18n.translate( ); export const SOURCE_ENGINES_TITLE = i18n.translate( - 'xpack.enterpriseSearch.appSearch.metaEngines.title', + 'xpack.enterpriseSearch.appSearch.enginesOverview.metaEnginesTable.sourceEngines.title', { defaultMessage: 'Source Engines' } ); From 024b1f212fb682e39efd65a94943e4fdd1de6279 Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Mon, 12 Apr 2021 16:02:30 -0400 Subject: [PATCH 14/17] PR Feedback --- .../tables/meta_engines_table.test.tsx | 34 +++++---- .../components/tables/meta_engines_table.tsx | 5 +- .../meta_engines_table_expanded_row.scss | 4 +- .../meta_engines_table_expanded_row.tsx | 12 ++-- .../tables/meta_engines_table_logic.test.ts | 13 +++- ...engines_table_name_column_content.test.tsx | 72 ++----------------- ...meta_engines_table_name_column_content.tsx | 15 ++-- .../components/tables/shared_columns.tsx | 6 +- .../engines/components/tables/utils.test.ts | 4 +- 9 files changed, 54 insertions(+), 111 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.test.tsx index 0072f7fbca32e..a31d736f79d41 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.test.tsx @@ -198,27 +198,25 @@ describe('MetaEnginesTable', () => { }); }); - describe('source engines', () => { - describe('source engine table', () => { - beforeEach(() => { - resetMocks(); - }); + describe('expanded source engines', () => { + beforeEach(() => { + resetMocks(); + }); - it('is hidden by default', () => { - const wrapper = shallow(); - const table = wrapper.find(EuiBasicTable); - expect(table.dive().find(MetaEnginesTableExpandedRow)).toHaveLength(0); - }); + it('is hidden by default', () => { + const wrapper = shallow(); + const table = wrapper.find(EuiBasicTable); + expect(table.dive().find(MetaEnginesTableExpandedRow)).toHaveLength(0); + }); - it('is visible when the row has been expanded', () => { - setMockValues({ - ...DEFAULT_VALUES, - expandedSourceEngines: { 'test-engine': true }, - }); - const wrapper = shallow(); - const table = wrapper.find(EuiBasicTable); - expect(table.dive().find(MetaEnginesTableExpandedRow)).toHaveLength(1); + it('is visible when the row has been expanded', () => { + setMockValues({ + ...DEFAULT_VALUES, + expandedSourceEngines: { 'test-engine': true }, }); + const wrapper = shallow(); + const table = wrapper.find(EuiBasicTable); + expect(table.dive().find(MetaEnginesTableExpandedRow)).toHaveLength(1); }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.tsx index d244a9f517731..47ea1e8751744 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.tsx @@ -92,11 +92,10 @@ export const MetaEnginesTable: React.FC = ({ const columns: Array> = [ { ...NAME_COLUMN, - render: (name: string, item: EngineDetails) => ( + render: (_, item: EngineDetails) => ( ( -
- - -

{SOURCE_ENGINES_TITLE}

-
-
+
+ +

{SOURCE_ENGINES_TITLE}

+
{ }); }); - describe('sourceEngines is updated by addSourceEngines', () => { + it('sourceEngines is updated by addSourceEngines', () => { mount({ ...DEFAULT_VALUES, sourceEngines: { @@ -122,6 +122,16 @@ describe('MetaEnginesTableLogic', () => { }); it('calls fetchSourceEngines when it needs to fetch data for the itemId', () => { + http.get.mockReturnValueOnce( + Promise.resolve({ + meta: { + page: { + total_pages: 1, + }, + }, + results: [{ name: 'source-engine-1' }, { name: 'source-engine-2' }], + }) + ); mount(); jest.spyOn(MetaEnginesTableLogic.actions, 'fetchSourceEngines'); @@ -205,7 +215,6 @@ describe('MetaEnginesTableLogic', () => { MetaEnginesTableLogic.actions.fetchSourceEngines('test-engine-1'); await nextTick(); - await nextTick(); // I think I need this for multiple http.get calls expect(MetaEnginesTableLogic.actions.addSourceEngines).toHaveBeenCalledWith({ 'test-engine-1': [ diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_name_column_content.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_name_column_content.test.tsx index bded3051fb5c9..728ce16106f75 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_name_column_content.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_name_column_content.test.tsx @@ -5,8 +5,6 @@ * 2.0. */ -import { mountWithIntl } from '../../../../../__mocks__'; - import React from 'react'; import { shallow } from 'enzyme'; @@ -20,11 +18,10 @@ import { MetaEnginesTableNameColumnContent } from './meta_engines_table_name_col describe('MetaEnginesTableNameColumnContent', () => { it('includes the name of the engine', () => { - const wrapper = mountWithIntl( + const wrapper = shallow( { /> ); - expect(wrapper.text()).toContain('test-engine'); + expect(wrapper.find('[data-test-subj="EngineName"]')).toHaveLength(1); }); describe('toggle button', () => { @@ -52,7 +49,6 @@ describe('MetaEnginesTableNameColumnContent', () => { { sendEngineTableLinkClickTelemetry={jest.fn()} /> ); - wrapper.find('.meta-engines__expand-row').at(0).simulate('click'); + wrapper.find('[data-test-subj="ExpandRowButton"]').at(0).simulate('click'); expect(showRow).toHaveBeenCalled(); }); @@ -80,7 +76,6 @@ describe('MetaEnginesTableNameColumnContent', () => { { sendEngineTableLinkClickTelemetry={jest.fn()} /> ); - wrapper.find('.meta-engines__expand-row').at(0).simulate('click'); + wrapper.find('[data-test-subj="ExpandRowButton"]').at(0).simulate('click'); expect(hideRow).toHaveBeenCalled(); }); @@ -104,11 +99,10 @@ describe('MetaEnginesTableNameColumnContent', () => { describe('engine count', () => { it('is included and labelled', () => { - const wrapper = mountWithIntl( + const wrapper = shallow( { /> ); - const wrapperContent = wrapper.text(); - expect(wrapperContent).toContain('2 Engines'); - }); - it('defaults to 0', () => { - const wrapper = mountWithIntl( - - ); - - const wrapperContent = wrapper.text(); - expect(wrapperContent).toContain('0 Engines'); - }); - - it('label loses pluralization with only 1 source engine', () => { - const wrapper = mountWithIntl( - - ); - - const wrapperContent = wrapper.text(); - expect(wrapperContent).toContain('1 Engine'); + expect(wrapper.find('[data-test-subj="SourceEnginesCount"]')).toHaveLength(1); }); }); @@ -191,7 +132,6 @@ describe('MetaEnginesTableNameColumnContent', () => { void; @@ -26,8 +25,7 @@ interface MetaEnginesTableNameContentProps { } export const MetaEnginesTableNameColumnContent: React.FC = ({ - name, - item, + item: { name, schemaConflicts, engine_count: engineCount }, isExpanded, sendEngineTableLinkClickTelemetry, hideRow, @@ -37,6 +35,7 @@ export const MetaEnginesTableNameColumnContent: React.FC {name} @@ -44,22 +43,22 @@ export const MetaEnginesTableNameColumnContent: React.FC (isExpanded ? hideRow(name) : showRow(name))} aria-expanded={isExpanded} - className="meta-engines__expand-row" + data-test-subj="ExpandRowButton" > - + {i18n.translate( 'xpack.enterpriseSearch.appSearch.engines.metaEnginesTable.sourceEnginesCount', { - defaultMessage: '{sourceEnginesCount, plural, one {# Engine} other {# Engines}}', - values: { sourceEnginesCount: item.engine_count || 0 }, + defaultMessage: '{sourceEnginesCount, plural, one {# engine} other {# engines}}', + values: { sourceEnginesCount: engineCount || 0 }, } )} - {item.schemaConflicts && Object.keys(item.schemaConflicts).length > 0 && ( + {schemaConflicts && Object.keys(schemaConflicts).length > 0 && ( {i18n.translate( diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/shared_columns.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/shared_columns.tsx index 3eaed7efe317d..1d330ba599a6e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/shared_columns.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/shared_columns.tsx @@ -39,7 +39,7 @@ export const NAME_COLUMN: EuiTableFieldDataColumnType = { export const CREATED_AT_COLUMN: EuiTableFieldDataColumnType = { field: 'created_at', name: i18n.translate('xpack.enterpriseSearch.appSearch.enginesOverview.table.column.createdAt', { - defaultMessage: 'Created At', + defaultMessage: 'Created at', }), dataType: 'string', render: (dateString: string) => , @@ -50,7 +50,7 @@ export const DOCUMENT_COUNT_COLUMN: EuiTableFieldDataColumnType = name: i18n.translate( 'xpack.enterpriseSearch.appSearch.enginesOverview.table.column.documentCount', { - defaultMessage: 'Document Count', + defaultMessage: 'Document count', } ), dataType: 'number', @@ -61,7 +61,7 @@ export const DOCUMENT_COUNT_COLUMN: EuiTableFieldDataColumnType = export const FIELD_COUNT_COLUMN: EuiTableFieldDataColumnType = { field: 'field_count', name: i18n.translate('xpack.enterpriseSearch.appSearch.enginesOverview.table.column.fieldCount', { - defaultMessage: 'Field Count', + defaultMessage: 'Field count', }), dataType: 'number', render: (number: number) => , diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/utils.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/utils.test.ts index 48ef706b6d3cd..f65a2e52bae06 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/utils.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/utils.test.ts @@ -25,7 +25,7 @@ describe('getConflictingEnginesFromConflictingField', () => { it('returns a flat array of all engines with conflicts across different schema types, including duplicates', () => { const result = getConflictingEnginesFromConflictingField(CONFLICTING_FIELD); - // we can't guaruntee ordering + // we can't guarantee ordering expect(result).toHaveLength(6); expect(result).toContain('source-engine-1'); expect(result).toContain('source-engine-2'); @@ -55,7 +55,7 @@ describe('getConflictingEnginesFromSchemaConflicts', () => { const result = getConflictingEnginesFromSchemaConflicts(SCHEMA_CONFLICTS); - // we can't guaruntee ordering + // we can't guarantee ordering expect(result).toHaveLength(4); expect(result).toContain('source-engine-1'); expect(result).toContain('source-engine-2'); From abeda3cf110e84a2c138c5442d0a3111abfd7d7e Mon Sep 17 00:00:00 2001 From: Constance Chen Date: Tue, 13 Apr 2021 16:03:44 -0700 Subject: [PATCH 15/17] DRY out shared engine link helpers --- .../tables/engine_link_helpers.test.tsx | 47 +++++++++++++++++++ .../components/tables/engine_link_helpers.tsx | 36 ++++++++++++++ .../components/tables/engines_table.tsx | 30 ++---------- .../components/tables/meta_engines_table.tsx | 19 +------- ...engines_table_name_column_content.test.tsx | 5 -- ...meta_engines_table_name_column_content.tsx | 15 ++---- 6 files changed, 92 insertions(+), 60 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engine_link_helpers.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engine_link_helpers.tsx diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engine_link_helpers.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engine_link_helpers.test.tsx new file mode 100644 index 0000000000000..5d91c724068e7 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engine_link_helpers.test.tsx @@ -0,0 +1,47 @@ +/* + * 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 { mockKibanaValues, mockTelemetryActions } from '../../../../../__mocks__'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { EuiLinkTo } from '../../../../../shared/react_router_helpers'; + +import { navigateToEngine, renderEngineLink } from './engine_link_helpers'; + +describe('navigateToEngine', () => { + const { navigateToUrl } = mockKibanaValues; + const { sendAppSearchTelemetry } = mockTelemetryActions; + + it('sends the user to the engine page and triggers a telemetry event', () => { + navigateToEngine('engine-a'); + expect(navigateToUrl).toHaveBeenCalledWith('/engines/engine-a'); + expect(sendAppSearchTelemetry).toHaveBeenCalledWith({ + action: 'clicked', + metric: 'engine_table_link', + }); + }); +}); + +describe('renderEngineLink', () => { + const { sendAppSearchTelemetry } = mockTelemetryActions; + + it('renders a link to the engine with telemetry', () => { + const wrapper = shallow(
{renderEngineLink('engine-b')}
); + const link = wrapper.find(EuiLinkTo); + + expect(link.prop('to')).toEqual('/engines/engine-b'); + + link.simulate('click'); + expect(sendAppSearchTelemetry).toHaveBeenCalledWith({ + action: 'clicked', + metric: 'engine_table_link', + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engine_link_helpers.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engine_link_helpers.tsx new file mode 100644 index 0000000000000..a3350d1ef9939 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engine_link_helpers.tsx @@ -0,0 +1,36 @@ +/* + * 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 { KibanaLogic } from '../../../../../shared/kibana'; +import { EuiLinkTo } from '../../../../../shared/react_router_helpers'; +import { TelemetryLogic } from '../../../../../shared/telemetry'; +import { ENGINE_PATH } from '../../../../routes'; +import { generateEncodedPath } from '../../../../utils/encode_path_params'; + +const sendEngineTableLinkClickTelemetry = () => { + TelemetryLogic.actions.sendAppSearchTelemetry({ + action: 'clicked', + metric: 'engine_table_link', + }); +}; + +export const navigateToEngine = (engineName: string) => { + sendEngineTableLinkClickTelemetry(); + KibanaLogic.values.navigateToUrl(generateEncodedPath(ENGINE_PATH, { engineName })); +}; + +export const renderEngineLink = (engineName: string) => ( + + {engineName} + +); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engines_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engines_table.tsx index cf2a8ca61c190..5c0dff570efad 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engines_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engines_table.tsx @@ -7,7 +7,7 @@ import React from 'react'; -import { useActions, useValues } from 'kea'; +import { useValues } from 'kea'; import { EuiBasicTable, @@ -18,15 +18,11 @@ import { import { i18n } from '@kbn/i18n'; import { MANAGE_BUTTON_LABEL, DELETE_BUTTON_LABEL } from '../../../../../shared/constants'; -import { KibanaLogic } from '../../../../../shared/kibana'; -import { EuiLinkTo } from '../../../../../shared/react_router_helpers'; -import { TelemetryLogic } from '../../../../../shared/telemetry'; import { AppLogic } from '../../../../app_logic'; import { UNIVERSAL_LANGUAGE } from '../../../../constants'; -import { ENGINE_PATH } from '../../../../routes'; -import { generateEncodedPath } from '../../../../utils/encode_path_params'; import { EngineDetails } from '../../../engine/types'; +import { renderEngineLink, navigateToEngine } from './engine_link_helpers'; import { CREATED_AT_COLUMN, DOCUMENT_COUNT_COLUMN, @@ -52,30 +48,14 @@ export const EnginesTable: React.FC = ({ onChange, onDeleteEngine, }) => { - const { sendAppSearchTelemetry } = useActions(TelemetryLogic); - const { navigateToUrl } = useValues(KibanaLogic); const { myRole: { canManageEngines }, } = useValues(AppLogic); - const sendEngineTableLinkClickTelemetry = () => - sendAppSearchTelemetry({ - action: 'clicked', - metric: 'engine_table_link', - }); - const columns: Array> = [ { ...NAME_COLUMN, - render: (name: string) => ( - - {name} - - ), + render: (name: string) => renderEngineLink(name), }, CREATED_AT_COLUMN, LANGUAGE_COLUMN, @@ -98,9 +78,7 @@ export const EnginesTable: React.FC = ({ ), type: 'icon', icon: 'eye', - onClick: (engineDetails) => { - sendEngineTableLinkClickTelemetry(); - navigateToUrl(generateEncodedPath(ENGINE_PATH, { engineName: engineDetails.name })); + onClick: (engineDetails) => navigateToEngine(engineDetails.name), }, }, { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.tsx index 47ea1e8751744..8dc491bdbc481 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.tsx @@ -12,13 +12,10 @@ import { useActions, useValues } from 'kea'; import { EuiBasicTable, EuiBasicTableColumn, EuiTableActionsColumnType } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { KibanaLogic } from '../../../../../shared/kibana'; -import { TelemetryLogic } from '../../../../../shared/telemetry'; import { AppLogic } from '../../../../app_logic'; -import { ENGINE_PATH } from '../../../../routes'; -import { generateEncodedPath } from '../../../../utils/encode_path_params'; import { EngineDetails } from '../../../engine/types'; +import { navigateToEngine } from './engine_link_helpers'; import { MetaEnginesTableExpandedRow } from './meta_engines_table_expanded_row'; import { MetaEnginesTableLogic } from './meta_engines_table_logic'; import { MetaEnginesTableNameColumnContent } from './meta_engines_table_name_column_content'; @@ -50,8 +47,6 @@ export const MetaEnginesTable: React.FC = ({ }) => { const { expandedSourceEngines } = useValues(MetaEnginesTableLogic); const { hideRow, fetchOrDisplayRow } = useActions(MetaEnginesTableLogic); - const { sendAppSearchTelemetry } = useActions(TelemetryLogic); - const { navigateToUrl } = useValues(KibanaLogic); const { myRole: { canManageMetaEngines }, } = useValues(AppLogic); @@ -83,12 +78,6 @@ export const MetaEnginesTable: React.FC = ({ [expandedSourceEngines, conflictingEnginesSets] ); - const sendEngineTableLinkClickTelemetry = () => - sendAppSearchTelemetry({ - action: 'clicked', - metric: 'engine_table_link', - }); - const columns: Array> = [ { ...NAME_COLUMN, @@ -98,7 +87,6 @@ export const MetaEnginesTable: React.FC = ({ isExpanded={!!itemIdToExpandedRowMap[item.name]} hideRow={hideRow} showRow={fetchOrDisplayRow} - sendEngineTableLinkClickTelemetry={sendEngineTableLinkClickTelemetry} /> ), }, @@ -128,10 +116,7 @@ export const MetaEnginesTable: React.FC = ({ ), type: 'icon', icon: 'eye', - onClick: (engineDetails) => { - sendEngineTableLinkClickTelemetry(); - navigateToUrl(generateEncodedPath(ENGINE_PATH, { engineName: engineDetails.name })); - }, + onClick: (engineDetails) => navigateToEngine(engineDetails.name), }, { name: i18n.translate( diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_name_column_content.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_name_column_content.test.tsx index 728ce16106f75..df65f2f86e174 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_name_column_content.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_name_column_content.test.tsx @@ -34,7 +34,6 @@ describe('MetaEnginesTableNameColumnContent', () => { } as EngineDetails } isExpanded={false} - sendEngineTableLinkClickTelemetry={jest.fn()} /> ); @@ -61,7 +60,6 @@ describe('MetaEnginesTableNameColumnContent', () => { } as EngineDetails } isExpanded={false} - sendEngineTableLinkClickTelemetry={jest.fn()} /> ); wrapper.find('[data-test-subj="ExpandRowButton"]').at(0).simulate('click'); @@ -88,7 +86,6 @@ describe('MetaEnginesTableNameColumnContent', () => { } as EngineDetails } isExpanded - sendEngineTableLinkClickTelemetry={jest.fn()} /> ); wrapper.find('[data-test-subj="ExpandRowButton"]').at(0).simulate('click'); @@ -119,7 +116,6 @@ describe('MetaEnginesTableNameColumnContent', () => { } as EngineDetails } isExpanded - sendEngineTableLinkClickTelemetry={jest.fn()} /> ); @@ -150,7 +146,6 @@ describe('MetaEnginesTableNameColumnContent', () => { } as EngineDetails } isExpanded - sendEngineTableLinkClickTelemetry={jest.fn()} /> ); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_name_column_content.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_name_column_content.tsx index 81659d45246ab..e05246ab4d92c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_name_column_content.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table_name_column_content.tsx @@ -11,34 +11,25 @@ import { EuiFlexGroup, EuiIcon, EuiHealth, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { EuiLinkTo } from '../../../../../shared/react_router_helpers'; -import { ENGINE_PATH } from '../../../../routes'; -import { generateEncodedPath } from '../../../../utils/encode_path_params'; import { EngineDetails } from '../../../engine/types'; +import { renderEngineLink } from './engine_link_helpers'; + interface MetaEnginesTableNameContentProps { isExpanded: boolean; item: EngineDetails; hideRow: (name: string) => void; - sendEngineTableLinkClickTelemetry: () => void; showRow: (name: string) => void; } export const MetaEnginesTableNameColumnContent: React.FC = ({ item: { name, schemaConflicts, engine_count: engineCount }, isExpanded, - sendEngineTableLinkClickTelemetry, hideRow, showRow, }) => ( - - {name} - + {renderEngineLink(name)}