From 0c222addbb417e0a1f08091108d5eba24f543764 Mon Sep 17 00:00:00 2001 From: Saarika Bhasi <55930906+saarikabhasi@users.noreply.github.com> Date: Thu, 19 Sep 2024 12:50:19 -0400 Subject: [PATCH] [Onboarding] Add mappings component to index details page (#193314) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary This PR adds mappings component to index detail page. Screenshot 2024-09-18 at 11 26 55 AM ### Checklist Delete any items that are not applicable to this PR. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed --- x-pack/plugins/search_indices/kibana.jsonc | 1 + .../search_indices/public/application.tsx | 5 +- .../components/indices/details_page.tsx | 57 +++++++++++++++---- .../indices/details_page_mappings.tsx | 30 ++++++++++ .../components/indices/indices_router.tsx | 33 ++++++++--- .../plugins/search_indices/public/routes.ts | 5 ++ x-pack/plugins/search_indices/public/types.ts | 2 + .../svl_search_index_detail_page.ts | 16 +++++- .../test_suites/search/search_index_detail.ts | 35 ++++++++---- 9 files changed, 144 insertions(+), 40 deletions(-) create mode 100644 x-pack/plugins/search_indices/public/components/indices/details_page_mappings.tsx diff --git a/x-pack/plugins/search_indices/kibana.jsonc b/x-pack/plugins/search_indices/kibana.jsonc index 303a264d2bafd..63fbf63609dff 100644 --- a/x-pack/plugins/search_indices/kibana.jsonc +++ b/x-pack/plugins/search_indices/kibana.jsonc @@ -12,6 +12,7 @@ ], "requiredPlugins": [ "share", + "indexManagement", ], "optionalPlugins": [ "cloud", diff --git a/x-pack/plugins/search_indices/public/application.tsx b/x-pack/plugins/search_indices/public/application.tsx index 62c4edb42d02c..c196020319589 100644 --- a/x-pack/plugins/search_indices/public/application.tsx +++ b/x-pack/plugins/search_indices/public/application.tsx @@ -13,7 +13,6 @@ import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { I18nProvider } from '@kbn/i18n-react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { Router } from '@kbn/shared-ux-router'; import { UsageTrackerContextProvider } from './contexts/usage_tracker_context'; import { SearchIndicesServicesContextDeps } from './types'; @@ -30,9 +29,7 @@ export const renderApp = async ( - - - + diff --git a/x-pack/plugins/search_indices/public/components/indices/details_page.tsx b/x-pack/plugins/search_indices/public/components/indices/details_page.tsx index ed498a777d712..bca02f1ef1264 100644 --- a/x-pack/plugins/search_indices/public/components/indices/details_page.tsx +++ b/x-pack/plugins/search_indices/public/components/indices/details_page.tsx @@ -10,7 +10,6 @@ import { EuiButton, EuiPageTemplate, EuiFlexItem, - EuiTabbedContent, EuiFlexGroup, EuiPopover, EuiButtonIcon, @@ -19,8 +18,10 @@ import { EuiText, EuiIcon, EuiButtonEmpty, + EuiTabbedContent, + EuiTabbedContentTab, } from '@elastic/eui'; -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useParams } from 'react-router-dom'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; @@ -33,11 +34,14 @@ import { useIndexMapping } from '../../hooks/api/use_index_mappings'; import { IndexDocuments } from '../index_documents/index_documents'; import { DeleteIndexModal } from './delete_index_modal'; import { IndexloadingError } from './details_page_loading_error'; +import { SearchIndicesDetailsMappingsTabs } from '../../routes'; +import { SearchIndexDetailsMappings } from './details_page_mappings'; export const SearchIndexDetailsPage = () => { const indexName = decodeURIComponent(useParams<{ indexName: string }>().indexName); - const { console: consolePlugin, docLinks, application } = useKibana().services; + const tabId = decodeURIComponent(useParams<{ tabId: string }>().tabId); + const { console: consolePlugin, docLinks, application, history } = useKibana().services; const { data: index, refetch, isError: isIndexError, isInitialLoading } = useIndex(indexName); const { data: mappings, @@ -45,6 +49,41 @@ export const SearchIndexDetailsPage = () => { isInitialLoading: isMappingsInitialLoading, } = useIndexMapping(indexName); + const SearchIndexDetailsTabs: EuiTabbedContentTab[] = useMemo(() => { + return [ + { + id: SearchIndicesDetailsMappingsTabs.DATA, + name: i18n.translate('xpack.searchIndices.documentsTabLabel', { + defaultMessage: 'Data', + }), + content: , + 'data-test-subj': `${SearchIndicesDetailsMappingsTabs.DATA}Tab`, + }, + { + id: SearchIndicesDetailsMappingsTabs.MAPPINGS, + name: i18n.translate('xpack.searchIndices.mappingsTabLabel', { + defaultMessage: 'Mappings', + }), + content: , + 'data-test-subj': `${SearchIndicesDetailsMappingsTabs.MAPPINGS}Tab`, + }, + ]; + }, [index, indexName]); + + const [selectedTab, setSelectedTab] = useState(SearchIndexDetailsTabs[0]); + + useEffect(() => { + const newTab = SearchIndexDetailsTabs.find((tab) => tab.id === tabId); + if (newTab) setSelectedTab(newTab); + }, [SearchIndexDetailsTabs, tabId]); + + const handleTabClick = useCallback( + (tab) => { + history.push(`index_details/${indexName}/${tab.id}`); + }, + + [history, indexName] + ); const embeddableConsole = useMemo( () => (consolePlugin?.EmbeddableConsole ? : null), [consolePlugin] @@ -176,15 +215,9 @@ export const SearchIndexDetailsPage = () => { , - }, - ]} + tabs={SearchIndexDetailsTabs} + onTabClick={handleTabClick} + selectedTab={selectedTab} /> diff --git a/x-pack/plugins/search_indices/public/components/indices/details_page_mappings.tsx b/x-pack/plugins/search_indices/public/components/indices/details_page_mappings.tsx new file mode 100644 index 0000000000000..c90d5cad94c83 --- /dev/null +++ b/x-pack/plugins/search_indices/public/components/indices/details_page_mappings.tsx @@ -0,0 +1,30 @@ +/* + * 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 { EuiSpacer } from '@elastic/eui'; +import { Index } from '@kbn/index-management-shared-types'; +import React from 'react'; +import { useMemo } from 'react'; +import { useKibana } from '../../hooks/use_kibana'; +export interface SearchIndexDetailsMappingsProps { + index?: Index; +} +export const SearchIndexDetailsMappings = ({ index }: SearchIndexDetailsMappingsProps) => { + const { indexManagement, history } = useKibana().services; + + const IndexMappingComponent = useMemo( + () => indexManagement.getIndexMappingComponent({ history }), + [indexManagement, history] + ); + + return ( + <> + + + + ); +}; diff --git a/x-pack/plugins/search_indices/public/components/indices/indices_router.tsx b/x-pack/plugins/search_indices/public/components/indices/indices_router.tsx index fda9a9344f658..73aec33f80b5c 100644 --- a/x-pack/plugins/search_indices/public/components/indices/indices_router.tsx +++ b/x-pack/plugins/search_indices/public/components/indices/indices_router.tsx @@ -5,17 +5,32 @@ * 2.0. */ import React from 'react'; -import { Route, Routes } from '@kbn/shared-ux-router'; -import { SEARCH_INDICES_DETAILS_PATH } from '../../routes'; -import { SearchIndexDetailsPage } from './details_page'; +import { Route, Router, Routes } from '@kbn/shared-ux-router'; +import { Redirect } from 'react-router-dom'; import { useKibana } from '../../hooks/use_kibana'; - +import { + SearchIndicesDetailsMappingsTabs, + SEARCH_INDICES_DETAILS_PATH, + SEARCH_INDICES_DETAILS_TABS_PATH, +} from '../../routes'; +import { SearchIndexDetailsPage } from './details_page'; export const SearchIndicesRouter: React.FC = () => { - const { application } = useKibana().services; + const { application, history } = useKibana().services; return ( - - - application.navigateToApp('elasticsearchStart')} /> - + + + + + + + + + application.navigateToApp('elasticsearchStart')} /> + + ); }; diff --git a/x-pack/plugins/search_indices/public/routes.ts b/x-pack/plugins/search_indices/public/routes.ts index 9687b11ad1692..3e347881d1219 100644 --- a/x-pack/plugins/search_indices/public/routes.ts +++ b/x-pack/plugins/search_indices/public/routes.ts @@ -7,3 +7,8 @@ export const ROOT_PATH = '/'; export const SEARCH_INDICES_DETAILS_PATH = `${ROOT_PATH}index_details/:indexName`; +export const SEARCH_INDICES_DETAILS_TABS_PATH = `${SEARCH_INDICES_DETAILS_PATH}/:tabId`; +export enum SearchIndicesDetailsMappingsTabs { + DATA = 'data', + MAPPINGS = 'mappings', +} diff --git a/x-pack/plugins/search_indices/public/types.ts b/x-pack/plugins/search_indices/public/types.ts index 6e0192e34f87c..0f68863fe72f8 100644 --- a/x-pack/plugins/search_indices/public/types.ts +++ b/x-pack/plugins/search_indices/public/types.ts @@ -12,6 +12,7 @@ import type { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public' import type { SharePluginStart } from '@kbn/share-plugin/public'; import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; import type { MappingPropertyBase } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { IndexManagementPluginStart } from '@kbn/index-management-shared-types'; export interface SearchIndicesPluginSetup { enabled: boolean; @@ -37,6 +38,7 @@ export interface SearchIndicesServicesContextDeps { export type SearchIndicesServicesContext = CoreStart & SearchIndicesAppPluginStartDependencies & { history: AppMountParameters['history']; + indexManagement: IndexManagementPluginStart; }; export interface AppUsageTracker { diff --git a/x-pack/test_serverless/functional/page_objects/svl_search_index_detail_page.ts b/x-pack/test_serverless/functional/page_objects/svl_search_index_detail_page.ts index 4c5a53a9a30e5..8ffbfd2bcb8c1 100644 --- a/x-pack/test_serverless/functional/page_objects/svl_search_index_detail_page.ts +++ b/x-pack/test_serverless/functional/page_objects/svl_search_index_detail_page.ts @@ -14,9 +14,6 @@ export function SvlSearchIndexDetailPageProvider({ getService }: FtrProviderCont const retry = getService('retry'); return { - async expectToBeIndexDetailPage() { - expect(await browser.getCurrentUrl()).contain('/index_details'); - }, async expectIndexDetailPageHeader() { await testSubjects.existOrFail('searchIndexDetailsHeader', { timeout: 2000 }); }, @@ -111,5 +108,18 @@ export function SvlSearchIndexDetailPageProvider({ getService }: FtrProviderCont await testSubjects.click('reloadButton', 2000); }); }, + async expectWithDataTabsExists() { + await testSubjects.existOrFail('mappingsTab', { timeout: 2000 }); + await testSubjects.existOrFail('dataTab', { timeout: 2000 }); + }, + async expectShouldDefaultToDataTab() { + expect(await browser.getCurrentUrl()).contain('/data'); + }, + async withDataChangeTabs(tab: 'dataTab' | 'mappingsTab') { + await testSubjects.click(tab); + }, + async expectUrlShouldChangeTo(tab: 'data' | 'mappings') { + expect(await browser.getCurrentUrl()).contain(`/${tab}`); + }, }; } diff --git a/x-pack/test_serverless/functional/test_suites/search/search_index_detail.ts b/x-pack/test_serverless/functional/test_suites/search/search_index_detail.ts index da707ca55ac8b..7f40ec9127c6c 100644 --- a/x-pack/test_serverless/functional/test_suites/search/search_index_detail.ts +++ b/x-pack/test_serverless/functional/test_suites/search/search_index_detail.ts @@ -68,23 +68,34 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await pageObjects.svlSearchIndexDetailPage.expectAddDocumentCodeExamples(); }); - it('should have index documents', async () => { - await es.index({ - index: indexName, - body: { - my_field: [1, 0, 1], - }, - }); - - await svlSearchNavigation.navigateToIndexDetailPage(indexName); - await pageObjects.svlSearchIndexDetailPage.expectHasIndexDocuments(); - }); - it('back to indices button should redirect to list page', async () => { await pageObjects.svlSearchIndexDetailPage.expectBackToIndicesButtonExists(); await pageObjects.svlSearchIndexDetailPage.clickBackToIndicesButton(); await pageObjects.svlSearchIndexDetailPage.expectBackToIndicesButtonRedirectsToListPage(); }); + describe('With data', () => { + before(async () => { + await svlSearchNavigation.navigateToIndexDetailPage(indexName); + await es.index({ + index: indexName, + body: { + my_field: [1, 0, 1], + }, + }); + }); + it('should have index documents', async () => { + await svlSearchNavigation.navigateToIndexDetailPage(indexName); + await pageObjects.svlSearchIndexDetailPage.expectHasIndexDocuments(); + }); + it('should have with data tabs', async () => { + await pageObjects.svlSearchIndexDetailPage.expectWithDataTabsExists(); + await pageObjects.svlSearchIndexDetailPage.expectShouldDefaultToDataTab(); + }); + it('should be able to change tabs', async () => { + await pageObjects.svlSearchIndexDetailPage.withDataChangeTabs('mappingsTab'); + await pageObjects.svlSearchIndexDetailPage.expectUrlShouldChangeTo('mappings'); + }); + }); describe('page loading error', () => { before(async () => {