mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Search][Onboarding] Adding new index details page for search indices (#191313)
## Summary As part of improving onboarding initiative, adding new page & routes for index detail page. **Screenshot** <img width="1727" alt="Screenshot 2024-09-05 at 12 08 08 PM" src="https://github.com/user-attachments/assets/1dc19380-ef53-43f8-93b2-8894114970cd"> **Note to reviewers:** Features added in this PR 1. added new route - `/app/elasticsearch/indices/index_details/${indexName}` 2. Back to indices -> redirects to index list page 3. FTR tests **How to test:** 1. Enable searchIndices plugin in `kibana.dev.yml` as this plugin is behind Feature flag ``` xpack.searchIndices.enabled: true ``` 2. Create a new index 3. Navigate to `/app/elasticsearch/indices/index_details/${indexName}` ### Checklist Delete any items that are not applicable to this PR. - [ ] 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) --------- Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
9a187df02d
commit
7fe372ece2
12 changed files with 236 additions and 6 deletions
|
@ -13,14 +13,15 @@ import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
|
|||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import { QueryClientProvider } from '@tanstack/react-query';
|
||||
|
||||
import { Router } from '@kbn/shared-ux-router';
|
||||
import { UsageTrackerContextProvider } from './contexts/usage_tracker_context';
|
||||
import { initQueryClient } from './services/query_client';
|
||||
import { SearchIndicesServicesContext } from './types';
|
||||
import { SearchIndicesServicesContextDeps } from './types';
|
||||
|
||||
export const renderApp = async (
|
||||
App: React.FC<{}>,
|
||||
core: CoreStart,
|
||||
services: Partial<SearchIndicesServicesContext>,
|
||||
services: SearchIndicesServicesContextDeps,
|
||||
element: HTMLElement
|
||||
) => {
|
||||
const queryClient = initQueryClient(core.notifications.toasts);
|
||||
|
@ -30,7 +31,9 @@ export const renderApp = async (
|
|||
<UsageTrackerContextProvider usageCollection={services.usageCollection}>
|
||||
<I18nProvider>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<App />
|
||||
<Router history={services.history}>
|
||||
<App />
|
||||
</Router>
|
||||
</QueryClientProvider>
|
||||
</I18nProvider>
|
||||
</UsageTrackerContextProvider>
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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 { EuiPageSection, EuiSpacer, EuiButton, EuiPageTemplate } from '@elastic/eui';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useIndex } from '../../hooks/api/use_index';
|
||||
import { useKibana } from '../../hooks/use_kibana';
|
||||
|
||||
export const SearchIndexDetailsPage = () => {
|
||||
const indexName = decodeURIComponent(useParams<{ indexName: string }>().indexName);
|
||||
const { console: consolePlugin, application } = useKibana().services;
|
||||
|
||||
const { data: index } = useIndex(indexName);
|
||||
const embeddableConsole = useMemo(
|
||||
() => (consolePlugin?.EmbeddableConsole ? <consolePlugin.EmbeddableConsole /> : null),
|
||||
[consolePlugin]
|
||||
);
|
||||
const navigateToIndexListPage = useCallback(() => {
|
||||
application.navigateToApp('management', { deepLinkId: 'index_management' });
|
||||
}, [application]);
|
||||
return (
|
||||
<EuiPageTemplate
|
||||
offset={0}
|
||||
restrictWidth={false}
|
||||
data-test-subj="searchIndicesDetailsPage"
|
||||
grow={false}
|
||||
bottomBorder={false}
|
||||
>
|
||||
<EuiPageSection>
|
||||
<EuiButton
|
||||
data-test-subj="searchIndexDetailsBackToIndicesButton"
|
||||
color="text"
|
||||
iconType="arrowLeft"
|
||||
onClick={navigateToIndexListPage}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.searchIndices.backToIndicesButtonLabel"
|
||||
defaultMessage="Back to indices"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiPageSection>
|
||||
<EuiPageTemplate.Header
|
||||
data-test-subj="searchIndexDetailsHeader"
|
||||
pageTitle={index?.name}
|
||||
rightSideItems={[]}
|
||||
/>
|
||||
<EuiSpacer size="l" />
|
||||
|
||||
<div data-test-subj="searchIndexDetailsContent" />
|
||||
{embeddableConsole}
|
||||
</EuiPageTemplate>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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 { Route, Routes } from '@kbn/shared-ux-router';
|
||||
import { SEARCH_INDICES_DETAILS_PATH } from '../../routes';
|
||||
import { SearchIndexDetailsPage } from './details_page';
|
||||
import { useKibana } from '../../hooks/use_kibana';
|
||||
|
||||
export const SearchIndicesRouter: React.FC = () => {
|
||||
const { application } = useKibana().services;
|
||||
return (
|
||||
<Routes>
|
||||
<Route exact path={SEARCH_INDICES_DETAILS_PATH} component={SearchIndexDetailsPage} />
|
||||
<Route render={() => application.navigateToApp('elasticsearchStart')} />
|
||||
</Routes>
|
||||
);
|
||||
};
|
26
x-pack/plugins/search_indices/public/hooks/api/use_index.ts
Normal file
26
x-pack/plugins/search_indices/public/hooks/api/use_index.ts
Normal file
|
@ -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 type { Index } from '@kbn/index-management';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useKibana } from '../use_kibana';
|
||||
|
||||
const POLLING_INTERVAL = 15 * 1000;
|
||||
export const useIndex = (indexName: string) => {
|
||||
const { http } = useKibana().services;
|
||||
const queryKey = ['fetchIndex', indexName];
|
||||
const result = useQuery({
|
||||
queryKey,
|
||||
refetchInterval: POLLING_INTERVAL,
|
||||
refetchIntervalInBackground: true,
|
||||
refetchOnWindowFocus: 'always',
|
||||
retry: true,
|
||||
queryFn: () =>
|
||||
http.fetch<Index>(`/internal/index_management/indices/${encodeURIComponent(indexName)}`),
|
||||
});
|
||||
return { queryKey, ...result };
|
||||
};
|
|
@ -11,7 +11,7 @@ import type {
|
|||
SearchIndicesAppPluginStartDependencies,
|
||||
SearchIndicesPluginSetup,
|
||||
SearchIndicesPluginStart,
|
||||
SearchIndicesServicesContext,
|
||||
SearchIndicesServicesContextDeps,
|
||||
} from './types';
|
||||
|
||||
export class SearchIndicesPlugin
|
||||
|
@ -23,20 +23,37 @@ export class SearchIndicesPlugin
|
|||
core.application.register({
|
||||
id: 'elasticsearchStart',
|
||||
appRoute: '/app/elasticsearch/start',
|
||||
title: i18n.translate('xpack.searchIndices.startAppTitle', {
|
||||
title: i18n.translate('xpack.searchIndices.elasticsearchStart.startAppTitle', {
|
||||
defaultMessage: 'Elasticsearch Start',
|
||||
}),
|
||||
async mount({ element, history }) {
|
||||
const { renderApp } = await import('./application');
|
||||
const { ElasticsearchStartPage } = await import('./components/start/start_page');
|
||||
const [coreStart, depsStart] = await core.getStartServices();
|
||||
const startDeps: Partial<SearchIndicesServicesContext> = {
|
||||
const startDeps: SearchIndicesServicesContextDeps = {
|
||||
...depsStart,
|
||||
history,
|
||||
};
|
||||
return renderApp(ElasticsearchStartPage, coreStart, startDeps, element);
|
||||
},
|
||||
});
|
||||
core.application.register({
|
||||
id: 'elasticsearchIndices',
|
||||
appRoute: '/app/elasticsearch/indices',
|
||||
title: i18n.translate('xpack.searchIndices.elasticsearchIndices.startAppTitle', {
|
||||
defaultMessage: 'Elasticsearch Indices',
|
||||
}),
|
||||
async mount({ element, history }) {
|
||||
const { renderApp } = await import('./application');
|
||||
const { SearchIndicesRouter } = await import('./components/indices/indices_router');
|
||||
const [coreStart, depsStart] = await core.getStartServices();
|
||||
const startDeps: SearchIndicesServicesContextDeps = {
|
||||
...depsStart,
|
||||
history,
|
||||
};
|
||||
return renderApp(SearchIndicesRouter, coreStart, startDeps, element);
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
enabled: true,
|
||||
|
|
9
x-pack/plugins/search_indices/public/routes.ts
Normal file
9
x-pack/plugins/search_indices/public/routes.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export const ROOT_PATH = '/';
|
||||
export const SEARCH_INDICES_DETAILS_PATH = `${ROOT_PATH}index_details/:indexName`;
|
|
@ -27,6 +27,10 @@ export interface SearchIndicesAppPluginStartDependencies {
|
|||
usageCollection?: UsageCollectionStart;
|
||||
}
|
||||
|
||||
export interface SearchIndicesServicesContextDeps {
|
||||
history: AppMountParameters['history'];
|
||||
usageCollection?: UsageCollectionStart;
|
||||
}
|
||||
export type SearchIndicesServicesContext = CoreStart &
|
||||
SearchIndicesAppPluginStartDependencies & {
|
||||
history: AppMountParameters['history'];
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
"@kbn/console-plugin",
|
||||
"@kbn/share-plugin",
|
||||
"@kbn/kibana-utils-plugin",
|
||||
"@kbn/shared-ux-router",
|
||||
"@kbn/index-management",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -21,6 +21,7 @@ import { SvlSearchConnectorsPageProvider } from './svl_search_connectors_page';
|
|||
import { SvlManagementPageProvider } from './svl_management_page';
|
||||
import { SvlIngestPipelines } from './svl_ingest_pipelines';
|
||||
import { SvlSearchHomePageProvider } from './svl_search_homepage';
|
||||
import { SvlSearchIndexDetailPageProvider } from './svl_search_index_detail_page';
|
||||
|
||||
export const pageObjects = {
|
||||
...xpackFunctionalPageObjects,
|
||||
|
@ -39,4 +40,5 @@ export const pageObjects = {
|
|||
svlManagementPage: SvlManagementPageProvider,
|
||||
svlIngestPipelines: SvlIngestPipelines,
|
||||
svlSearchHomePage: SvlSearchHomePageProvider,
|
||||
svlSearchIndexDetailPage: SvlSearchIndexDetailPageProvider,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../ftr_provider_context';
|
||||
|
||||
export function SvlSearchIndexDetailPageProvider({ getService }: FtrProviderContext) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
const browser = getService('browser');
|
||||
|
||||
return {
|
||||
async expectToBeIndexDetailPage() {
|
||||
expect(await browser.getCurrentUrl()).contain('/index_details');
|
||||
},
|
||||
async expectIndexDetailPageHeader() {
|
||||
await testSubjects.existOrFail('searchIndexDetailsHeader', { timeout: 2000 });
|
||||
},
|
||||
async expectIndexDetailPage() {
|
||||
await testSubjects.existOrFail('searchIndicesDetailsPage', { timeout: 2000 });
|
||||
},
|
||||
async expectBackToIndicesButtonExists() {
|
||||
await testSubjects.existOrFail('searchIndexDetailsBackToIndicesButton', { timeout: 2000 });
|
||||
},
|
||||
async clickBackToIndicesButton() {
|
||||
await testSubjects.click('searchIndexDetailsBackToIndicesButton');
|
||||
},
|
||||
async expectBackToIndicesButtonRedirectsToListPage() {
|
||||
await testSubjects.existOrFail('indicesList');
|
||||
},
|
||||
};
|
||||
}
|
|
@ -11,6 +11,7 @@ export default function ({ loadTestFile }: FtrProviderContext) {
|
|||
describe('serverless search UI - feature flags', function () {
|
||||
// add tests that require feature flags, defined in config.feature_flags.ts
|
||||
loadTestFile(require.resolve('./elasticsearch_start.ts'));
|
||||
loadTestFile(require.resolve('./search_index_detail.ts'));
|
||||
loadTestFile(require.resolve('../common/platform_security/navigation/management_nav_cards.ts'));
|
||||
loadTestFile(require.resolve('../common/platform_security/roles.ts'));
|
||||
loadTestFile(require.resolve('../common/spaces/multiple_spaces_enabled.ts'));
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../../ftr_provider_context';
|
||||
import { testHasEmbeddedConsole } from './embedded_console';
|
||||
|
||||
export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
||||
const pageObjects = getPageObjects([
|
||||
'svlCommonPage',
|
||||
'embeddedConsole',
|
||||
'svlSearchIndexDetailPage',
|
||||
]);
|
||||
const es = getService('es');
|
||||
const retry = getService('retry');
|
||||
const PageObjects = getPageObjects(['common']);
|
||||
|
||||
const esDeleteAllIndices = getService('esDeleteAllIndices');
|
||||
const indexName = 'test-my-index';
|
||||
|
||||
describe('search index detail page', () => {
|
||||
before(async () => {
|
||||
await pageObjects.svlCommonPage.loginWithRole('developer');
|
||||
await es.indices.create({ index: indexName });
|
||||
await retry.tryForTime(60 * 1000, async () => {
|
||||
await PageObjects.common.navigateToApp(`elasticsearch/indices/index_details/${indexName}`, {
|
||||
shouldLoginIfPrompted: false,
|
||||
});
|
||||
await pageObjects.svlSearchIndexDetailPage.expectIndexDetailPage();
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await esDeleteAllIndices(indexName);
|
||||
});
|
||||
it('loads index detail page', async () => {
|
||||
await pageObjects.svlSearchIndexDetailPage.expectIndexDetailPageHeader();
|
||||
await pageObjects.svlSearchIndexDetailPage.expectIndexDetailPage();
|
||||
});
|
||||
it('should have embedded dev console', async () => {
|
||||
await testHasEmbeddedConsole(pageObjects);
|
||||
});
|
||||
it('should redirect to indices list page', async () => {
|
||||
await pageObjects.svlSearchIndexDetailPage.expectBackToIndicesButtonExists();
|
||||
await pageObjects.svlSearchIndexDetailPage.clickBackToIndicesButton();
|
||||
await pageObjects.svlSearchIndexDetailPage.expectBackToIndicesButtonRedirectsToListPage();
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue