[Search] [Playground] SideNav: move playground to build (#181087)

Update Search nav to build and move playground from content to build


![image](8393a3e1-0d42-48c7-aa41-a9cc17ef48fa)

update the kibana side nav to feature Playground. This routes from
application to playground.


![image](22fe95df-e277-4c0b-8e65-edba8ba940cf)

---------

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Joe McElroy 2024-04-22 16:07:35 +01:00 committed by GitHub
parent a41177f32c
commit 019dd79096
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 118 additions and 56 deletions

View file

@ -26,9 +26,9 @@ export type EnterpriseSearchWorkplaceSearchApp = typeof ENTERPRISE_SEARCH_WORKPL
export type ServerlessSearchApp = typeof SERVERLESS_ES_APP_ID;
export type ConnectorsId = typeof SERVERLESS_ES_CONNECTORS_ID;
export type ContentLinkId = 'searchIndices' | 'connectors' | 'webCrawlers' | 'playground';
export type ContentLinkId = 'searchIndices' | 'connectors' | 'webCrawlers';
export type ApplicationsLinkId = 'searchApplications';
export type ApplicationsLinkId = 'searchApplications' | 'playground';
export type AppsearchLinkId = 'engines';

View file

@ -206,6 +206,7 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D
licenseManagement: `${ENTERPRISE_SEARCH_DOCS}license-management.html`,
machineLearningStart: `${ELASTICSEARCH_DOCS}nlp-example.html`,
mailService: `${ENTERPRISE_SEARCH_DOCS}mailer-configuration.html`,
playground: `${KIBANA_DOCS}playground.html`,
mlDocumentEnrichment: `${ELASTICSEARCH_DOCS}ingest-pipeline-search-inference.html`,
searchApplicationsTemplates: `${ELASTICSEARCH_DOCS}search-application-api.html`,
searchApplicationsSearchApi: `${ELASTICSEARCH_DOCS}search-application-security.html`,

View file

@ -170,6 +170,7 @@ export interface DocLinks {
readonly languageClients: string;
readonly licenseManagement: string;
readonly machineLearningStart: string;
readonly playground: string;
readonly mailService: string;
readonly mlDocumentEnrichment: string;
readonly searchApplicationsTemplates: string;

View file

@ -123,7 +123,7 @@ const navTree: NavigationTreeDefinition = {
}),
children: [
{
link: 'enterpriseSearchContent:playground',
link: 'enterpriseSearchApplications:playground',
},
{
title: i18n.translate('navigation.searchNav.build.searchApplications', {

View file

@ -153,7 +153,7 @@ export const APPLICATIONS_PLUGIN = {
defaultMessage: 'Applications',
}),
NAV_TITLE: i18n.translate('xpack.enterpriseSearch.applications.navTitle', {
defaultMessage: 'Search Applications',
defaultMessage: 'Build',
}),
SUPPORT_URL: 'https://discuss.elastic.co/c/enterprise-search/',
URL: '/app/enterprise_search/applications',

View file

@ -15,13 +15,16 @@ import { SetEnterpriseSearchApplicationsChrome } from '../../../shared/kibana_ch
import { EnterpriseSearchPageTemplateWrapper, PageTemplateProps } from '../../../shared/layout';
import { useEnterpriseSearchApplicationNav } from '../../../shared/layout';
import { SendEnterpriseSearchTelemetry } from '../../../shared/telemetry';
import { PlaygroundHeaderDocsAction } from '../playground/header_docs_action';
import { SearchApplicationHeaderDocsAction } from '../search_application/header_docs_action';
export type EnterpriseSearchApplicationsPageTemplateProps = Omit<
PageTemplateProps,
'useEndpointHeaderActions'
> & {
docLink?: 'search_application' | 'playground';
hasSchemaConflicts?: boolean;
restrictWidth?: boolean;
searchApplicationName?: string;
};
@ -33,6 +36,8 @@ export const EnterpriseSearchApplicationsPageTemplate: React.FC<
pageViewTelemetry,
searchApplicationName,
hasSchemaConflicts,
restrictWidth = true,
docLink = 'search_application',
...pageTemplateProps
}) => {
const navItems = useEnterpriseSearchApplicationNav(
@ -42,7 +47,11 @@ export const EnterpriseSearchApplicationsPageTemplate: React.FC<
);
const { renderHeaderActions } = useValues(KibanaLogic);
useLayoutEffect(() => {
renderHeaderActions(SearchApplicationHeaderDocsAction);
const docAction = {
playground: PlaygroundHeaderDocsAction,
search_application: SearchApplicationHeaderDocsAction,
}[docLink];
renderHeaderActions(docAction);
return () => {
renderHeaderActions();
@ -55,7 +64,7 @@ export const EnterpriseSearchApplicationsPageTemplate: React.FC<
items: navItems,
name: ENTERPRISE_SEARCH_CONTENT_PLUGIN.NAME,
}}
restrictWidth
restrictWidth={restrictWidth}
setPageChrome={pageChrome && <SetEnterpriseSearchApplicationsChrome trail={pageChrome} />}
useEndpointHeaderActions={false}
>

View file

@ -0,0 +1,31 @@
/*
* 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 { EuiButtonEmpty } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { docLinks } from '../../../shared/doc_links';
import { EndpointsHeaderAction } from '../../../shared/layout/endpoints_header_action';
export const PlaygroundHeaderDocsAction: React.FC = () => (
<EndpointsHeaderAction>
<EuiButtonEmpty
data-telemetry-id="entSearchApplications-playground-documentationLink"
data-test-subj="playground-documentation-link"
href={docLinks.playground}
target="_blank"
iconType="documents"
>
{i18n.translate('xpack.enterpriseSearch.content.playground.header.docLink', {
defaultMessage: 'Playground Docs',
})}
</EuiButtonEmpty>
</EndpointsHeaderAction>
);

View file

@ -14,7 +14,7 @@ import { useValues } from 'kea';
import { i18n } from '@kbn/i18n';
import { KibanaLogic } from '../../../shared/kibana';
import { EnterpriseSearchContentPageTemplate } from '../layout/page_template';
import { EnterpriseSearchApplicationsPageTemplate } from '../layout/page_template';
export const Playground: React.FC = () => {
const [searchParams] = useSearchParams();
@ -32,7 +32,7 @@ export const Playground: React.FC = () => {
indices: index ? [index] : [],
}}
>
<EnterpriseSearchContentPageTemplate
<EnterpriseSearchApplicationsPageTemplate
pageChrome={[
i18n.translate('xpack.enterpriseSearch.content.playground.breadcrumb', {
defaultMessage: 'Playground',
@ -48,9 +48,10 @@ export const Playground: React.FC = () => {
restrictWidth={false}
customPageSections
bottomBorder="extended"
docLink="playground"
>
<searchPlayground.Playground />
</EnterpriseSearchContentPageTemplate>
</EnterpriseSearchApplicationsPageTemplate>
</searchPlayground.PlaygroundProvider>
);
};

View file

@ -11,16 +11,20 @@ import { Redirect } from 'react-router-dom';
import { Routes, Route } from '@kbn/shared-ux-router';
import { NotFound } from './components/not_found';
import { Playground } from './components/playground/playground';
import { SearchApplicationsRouter } from './components/search_applications/search_applications_router';
import { ROOT_PATH, SEARCH_APPLICATIONS_PATH } from './routes';
import { PLAYGROUND_PATH, ROOT_PATH, SEARCH_APPLICATIONS_PATH } from './routes';
export const Applications = () => {
return (
<Routes>
<Redirect exact from={ROOT_PATH} to={SEARCH_APPLICATIONS_PATH} />
<Redirect exact from={ROOT_PATH} to={PLAYGROUND_PATH} />
<Route path={SEARCH_APPLICATIONS_PATH}>
<SearchApplicationsRouter />
</Route>
<Route path={PLAYGROUND_PATH}>
<Playground />
</Route>
<Route>
<NotFound />
</Route>

View file

@ -17,6 +17,8 @@ export enum SearchApplicationViewTabs {
export const SEARCH_APPLICATION_CREATION_PATH = `${SEARCH_APPLICATIONS_PATH}/new`;
export const SEARCH_APPLICATION_PATH = `${SEARCH_APPLICATIONS_PATH}/:searchApplicationName`;
export const SEARCH_APPLICATION_TAB_PATH = `${SEARCH_APPLICATION_PATH}/:tabId`;
export const PLAYGROUND_PATH = `${ROOT_PATH}playground`;
export const SEARCH_APPLICATION_CONNECT_PATH = `${SEARCH_APPLICATION_PATH}/${SearchApplicationViewTabs.CONNECT}/:connectTabId`;
export enum SearchApplicationConnectTabs {
SEARCHAPI = 'search_api',

View file

@ -17,7 +17,6 @@ export const EnterpriseSearchContentPageTemplate: React.FC<PageTemplateProps> =
children,
pageChrome,
pageViewTelemetry,
restrictWidth = true,
...pageTemplateProps
}) => {
return (
@ -27,7 +26,6 @@ export const EnterpriseSearchContentPageTemplate: React.FC<PageTemplateProps> =
items: useEnterpriseSearchNav(),
name: ENTERPRISE_SEARCH_CONTENT_PLUGIN.NAME,
}}
restrictWidth={restrictWidth}
setPageChrome={pageChrome && <SetEnterpriseSearchContentChrome trail={pageChrome} />}
>
{pageViewTelemetry && (

View file

@ -11,10 +11,9 @@ import { EuiButton } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { PLAYGROUND_PATH } from '../../../../../applications/routes';
import { KibanaLogic } from '../../../../../shared/kibana';
import { PLAYGROUND_PATH } from '../../../../routes';
export interface SearchPlaygroundPopoverProps {
indexName: string;
ingestionMethod: string;

View file

@ -23,7 +23,6 @@ import { VersionMismatchPage } from '../shared/version_mismatch';
import { ConnectorsRouter } from './components/connectors/connectors_router';
import { CrawlersRouter } from './components/connectors/crawlers_router';
import { NotFound } from './components/not_found';
import { Playground } from './components/playground/playground';
import { SearchIndicesRouter } from './components/search_indices';
import {
CONNECTORS_PATH,
@ -32,7 +31,6 @@ import {
ROOT_PATH,
SEARCH_INDICES_PATH,
SETUP_GUIDE_PATH,
PLAYGROUND_PATH,
} from './routes';
export const EnterpriseSearchContent: React.FC<InitialAppData> = (props) => {
@ -84,9 +82,6 @@ export const EnterpriseSearchContentConfigured: React.FC<Required<InitialAppData
<Route path={CRAWLERS_PATH}>
<CrawlersRouter />
</Route>
<Route path={PLAYGROUND_PATH}>
<Playground />
</Route>
<Route>
<NotFound />
</Route>

View file

@ -13,7 +13,6 @@ export const ERROR_STATE_PATH = '/error_state';
export const SEARCH_INDICES_PATH = `${ROOT_PATH}search_indices`;
export const CONNECTORS_PATH = `${ROOT_PATH}connectors`;
export const CRAWLERS_PATH = `${ROOT_PATH}crawlers`;
export const PLAYGROUND_PATH = `${ROOT_PATH}playground`;
export const SETTINGS_PATH = `${ROOT_PATH}settings`;
export const NEW_INDEX_PATH = `${SEARCH_INDICES_PATH}/new_index`;

View file

@ -124,6 +124,7 @@ class DocLinks {
public licenseManagement: string;
public machineLearningStart: string;
public mlDocumentEnrichment: string;
public playground: string;
public pluginsIngestAttachment: string;
public queryDsl: string;
public restApis: string;
@ -301,6 +302,7 @@ class DocLinks {
this.licenseManagement = '';
this.machineLearningStart = '';
this.mlDocumentEnrichment = '';
this.playground = '';
this.pluginsIngestAttachment = '';
this.queryDsl = '';
this.restApis = '';
@ -480,6 +482,7 @@ class DocLinks {
this.licenseManagement = docLinks.links.enterpriseSearch.licenseManagement;
this.machineLearningStart = docLinks.links.enterpriseSearch.machineLearningStart;
this.mlDocumentEnrichment = docLinks.links.enterpriseSearch.mlDocumentEnrichment;
this.playground = docLinks.links.enterpriseSearch.playground;
this.pluginsIngestAttachment = docLinks.links.plugins.ingestAttachment;
this.queryDsl = docLinks.links.query.queryDsl;
this.restApis = docLinks.links.apis.restApis;

View file

@ -57,18 +57,18 @@ const baseNavItems = [
items: undefined,
name: 'Web crawlers',
},
{
href: '/app/enterprise_search/content/playground',
id: 'playground',
items: undefined,
name: 'Playground',
},
],
name: 'Content',
},
{
id: 'applications',
id: 'build',
items: [
{
href: '/app/enterprise_search/applications/playground',
id: 'playground',
items: undefined,
name: 'Playground',
},
{
href: '/app/enterprise_search/applications/search_applications',
id: 'searchApplications',
@ -82,7 +82,7 @@ const baseNavItems = [
name: 'Behavioral Analytics',
},
],
name: 'Applications',
name: 'Build',
},
{
id: 'es_getting_started',
@ -243,11 +243,11 @@ describe('useEnterpriseSearchApplicationNav', () => {
expect(navItems![0].id).toEqual('home');
expect(navItems?.slice(1).map((ni) => ni.name)).toEqual([
'Content',
'Applications',
'Build',
'Getting started',
'Enterprise Search',
]);
const searchItem = navItems?.find((ni) => ni.id === 'applications');
const searchItem = navItems?.find((ni) => ni.id === 'build');
expect(searchItem).not.toBeUndefined();
expect(searchItem!.items).not.toBeUndefined();
// @ts-ignore
@ -300,11 +300,11 @@ describe('useEnterpriseSearchApplicationNav', () => {
expect(navItems![0].id).toEqual('home');
expect(navItems?.slice(1).map((ni) => ni.name)).toEqual([
'Content',
'Applications',
'Build',
'Getting started',
'Enterprise Search',
]);
const searchItem = navItems?.find((ni) => ni.id === 'applications');
const searchItem = navItems?.find((ni) => ni.id === 'build');
expect(searchItem).not.toBeUndefined();
expect(searchItem!.items).not.toBeUndefined();
// @ts-ignore
@ -330,7 +330,7 @@ describe('useEnterpriseSearchApplicationNav', () => {
// @ts-ignore
const engineItem = navItems
.find((ni: EuiSideNavItemType<unknown>) => ni.id === 'applications')
.find((ni: EuiSideNavItemType<unknown>) => ni.id === 'build')
.items.find((ni: EuiSideNavItemType<unknown>) => ni.id === 'searchApplications')
.items[0].items.find(
(ni: EuiSideNavItemType<unknown>) => ni.id === 'enterpriseSearchApplicationsContent'
@ -398,9 +398,9 @@ describe('useEnterpriseSearchAnalyticsNav', () => {
integration: '/integration-path',
overview: '/overview-path',
});
const applicationsNav = navItems?.find((item) => item.id === 'applications');
const applicationsNav = navItems?.find((item) => item.id === 'build');
expect(applicationsNav).not.toBeUndefined();
const analyticsNav = applicationsNav?.items?.[1];
const analyticsNav = applicationsNav?.items?.[2];
expect(analyticsNav).not.toBeUndefined();
expect(analyticsNav).toEqual({
href: '/app/enterprise_search/analytics',

View file

@ -23,10 +23,13 @@ import {
VECTOR_SEARCH_PLUGIN,
WORKPLACE_SEARCH_PLUGIN,
} from '../../../../common/constants';
import { SEARCH_APPLICATIONS_PATH, SearchApplicationViewTabs } from '../../applications/routes';
import {
SEARCH_APPLICATIONS_PATH,
SearchApplicationViewTabs,
PLAYGROUND_PATH,
} from '../../applications/routes';
import { useIndicesNav } from '../../enterprise_search_content/components/search_index/indices/indices_nav';
import {
PLAYGROUND_PATH,
CONNECTORS_PATH,
CRAWLERS_PATH,
SEARCH_INDICES_PATH,
@ -93,6 +96,14 @@ export const useEnterpriseSearchNav = () => {
to: ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL + CRAWLERS_PATH,
}),
},
],
name: i18n.translate('xpack.enterpriseSearch.nav.contentTitle', {
defaultMessage: 'Content',
}),
},
{
id: 'build',
items: [
{
id: 'playground',
name: i18n.translate('xpack.enterpriseSearch.nav.PlaygroundTitle', {
@ -101,17 +112,9 @@ export const useEnterpriseSearchNav = () => {
...generateNavLink({
shouldNotCreateHref: true,
shouldShowActiveForSubroutes: true,
to: ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL + PLAYGROUND_PATH,
to: APPLICATIONS_PLUGIN.URL + PLAYGROUND_PATH,
}),
},
],
name: i18n.translate('xpack.enterpriseSearch.nav.contentTitle', {
defaultMessage: 'Content',
}),
},
{
id: 'applications',
items: [
{
id: 'searchApplications',
name: i18n.translate('xpack.enterpriseSearch.nav.searchApplicationsTitle', {
@ -134,7 +137,7 @@ export const useEnterpriseSearchNav = () => {
},
],
name: i18n.translate('xpack.enterpriseSearch.nav.applicationsTitle', {
defaultMessage: 'Applications',
defaultMessage: 'Build',
}),
},
{
@ -226,7 +229,7 @@ export const useEnterpriseSearchApplicationNav = (
const navItems = useEnterpriseSearchNav();
if (!navItems) return undefined;
if (!searchApplicationName) return navItems;
const applicationsItem = navItems.find((item) => item.id === 'applications');
const applicationsItem = navItems.find((item) => item.id === 'build');
if (!applicationsItem || !applicationsItem.items) return navItems;
const searchApplicationsItem = applicationsItem.items?.find(
(item) => item.id === 'searchApplications'
@ -320,7 +323,7 @@ export const useEnterpriseSearchAnalyticsNav = (
if (!navItems) return undefined;
const applicationsNav = navItems.find((item) => item.id === 'applications');
const applicationsNav = navItems.find((item) => item.id === 'build');
const analyticsNav = applicationsNav?.items?.find((item) => item.id === 'analyticsCollections');
if (!name || !paths || !analyticsNav) return navItems;

View file

@ -55,12 +55,11 @@ import {
import { ClientConfigType, InitialAppData } from '../common/types';
import { ENGINES_PATH } from './applications/app_search/routes';
import { SEARCH_APPLICATIONS_PATH } from './applications/applications/routes';
import { SEARCH_APPLICATIONS_PATH, PLAYGROUND_PATH } from './applications/applications/routes';
import {
CONNECTORS_PATH,
SEARCH_INDICES_PATH,
CRAWLERS_PATH,
PLAYGROUND_PATH,
} from './applications/enterprise_search_content/routes';
import { docLinks } from './applications/shared/doc_links';
@ -122,16 +121,17 @@ const contentLinks: AppDeepLink[] = [
defaultMessage: 'Web crawlers',
}),
},
];
const applicationsLinks: AppDeepLink[] = [
{
id: 'playground',
path: `/${PLAYGROUND_PATH}`,
title: i18n.translate('xpack.enterpriseSearch.navigation.contentPlaygroundLinkLabel', {
defaultMessage: 'Playground',
}),
visibleIn: ['sideNav', 'globalSearch'],
},
];
const applicationsLinks: AppDeepLink[] = [
{
id: 'searchApplications',
path: `/${SEARCH_APPLICATIONS_PATH}`,
@ -141,6 +141,7 @@ const applicationsLinks: AppDeepLink[] = [
defaultMessage: 'Search Applications',
}
),
visibleIn: ['globalSearch'],
},
];
@ -359,6 +360,7 @@ export class EnterpriseSearchPlugin implements Plugin {
return renderApp(Applications, kibanaDeps, pluginData);
},
title: APPLICATIONS_PLUGIN.NAV_TITLE,
visibleIn: [],
});
core.application.register({

View file

@ -113,11 +113,25 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
});
describe('Search Applications', () => {
describe('Playground', () => {
before(async () => {
await common.navigateToApp('enterprise_search/applications');
});
it('loads playground', async function () {
await retry.waitFor(
'playground docs link',
async () => await testSubjects.exists('playground-documentation-link')
);
await a11y.testAppSnapshot();
});
});
describe('Search Applications', () => {
before(async () => {
await common.navigateToApp('enterprise_search/applications/search_applications');
});
it('loads search applications list', async function () {
await retry.waitFor(
'search apps docs link',