Skip to content

Commit

Permalink
[Onboarding] Add mappings component to index details page (elastic#19…
Browse files Browse the repository at this point in the history
…3314)

## Summary

This PR adds mappings component to index detail page. 

<img width="1719" alt="Screenshot 2024-09-18 at 11 26 55 AM"
src="https://github.com/user-attachments/assets/5d10f372-fdf5-4452-82ea-f6b2e29398af">

### 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
  • Loading branch information
saarikabhasi authored Sep 19, 2024
1 parent f32ba5c commit 0c222ad
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 40 deletions.
1 change: 1 addition & 0 deletions x-pack/plugins/search_indices/kibana.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
],
"requiredPlugins": [
"share",
"indexManagement",
],
"optionalPlugins": [
"cloud",
Expand Down
5 changes: 1 addition & 4 deletions x-pack/plugins/search_indices/public/application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -30,9 +29,7 @@ export const renderApp = async (
<UsageTrackerContextProvider usageCollection={services.usageCollection}>
<I18nProvider>
<QueryClientProvider client={queryClient}>
<Router history={services.history}>
<App />
</Router>
<App />
</QueryClientProvider>
</I18nProvider>
</UsageTrackerContextProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
EuiButton,
EuiPageTemplate,
EuiFlexItem,
EuiTabbedContent,
EuiFlexGroup,
EuiPopover,
EuiButtonIcon,
Expand All @@ -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';
Expand All @@ -33,18 +34,56 @@ 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,
isError: isMappingsError,
isInitialLoading: isMappingsInitialLoading,
} = useIndexMapping(indexName);

const SearchIndexDetailsTabs: EuiTabbedContentTab[] = useMemo(() => {
return [
{
id: SearchIndicesDetailsMappingsTabs.DATA,
name: i18n.translate('xpack.searchIndices.documentsTabLabel', {
defaultMessage: 'Data',
}),
content: <IndexDocuments indexName={indexName} />,
'data-test-subj': `${SearchIndicesDetailsMappingsTabs.DATA}Tab`,
},
{
id: SearchIndicesDetailsMappingsTabs.MAPPINGS,
name: i18n.translate('xpack.searchIndices.mappingsTabLabel', {
defaultMessage: 'Mappings',
}),
content: <SearchIndexDetailsMappings index={index} />,
'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 ? <consolePlugin.EmbeddableConsole /> : null),
[consolePlugin]
Expand Down Expand Up @@ -176,15 +215,9 @@ export const SearchIndexDetailsPage = () => {
<EuiFlexItem>
<EuiFlexItem>
<EuiTabbedContent
tabs={[
{
id: 'data',
name: i18n.translate('xpack.searchIndices.documentsTabLabel', {
defaultMessage: 'Data',
}),
content: <IndexDocuments indexName={indexName} />,
},
]}
tabs={SearchIndexDetailsTabs}
onTabClick={handleTabClick}
selectedTab={selectedTab}
/>
</EuiFlexItem>
</EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
@@ -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 (
<>
<EuiSpacer />
<IndexMappingComponent index={index} showAboutMappings={false} />
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<Routes>
<Route exact path={SEARCH_INDICES_DETAILS_PATH} component={SearchIndexDetailsPage} />
<Route render={() => application.navigateToApp('elasticsearchStart')} />
</Routes>
<Router history={history}>
<Routes>
<Route exact path={[SEARCH_INDICES_DETAILS_TABS_PATH, SEARCH_INDICES_DETAILS_PATH]}>
<Routes>
<Route path={SEARCH_INDICES_DETAILS_TABS_PATH} component={SearchIndexDetailsPage} />
<Redirect
exact
from={`${SEARCH_INDICES_DETAILS_PATH}/`}
to={`${SEARCH_INDICES_DETAILS_PATH}/${SearchIndicesDetailsMappingsTabs.DATA}`}
/>
</Routes>
</Route>
<Route render={() => application.navigateToApp('elasticsearchStart')} />
</Routes>
</Router>
);
};
5 changes: 5 additions & 0 deletions x-pack/plugins/search_indices/public/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
}
2 changes: 2 additions & 0 deletions x-pack/plugins/search_indices/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -37,6 +38,7 @@ export interface SearchIndicesServicesContextDeps {
export type SearchIndicesServicesContext = CoreStart &
SearchIndicesAppPluginStartDependencies & {
history: AppMountParameters['history'];
indexManagement: IndexManagementPluginStart;
};

export interface AppUsageTracker {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
},
Expand Down Expand Up @@ -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}`);
},
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down

0 comments on commit 0c222ad

Please sign in to comment.