Skip to content

Commit

Permalink
[8.x] [Search][Onboarding] Default home to Global Empty State (elasti…
Browse files Browse the repository at this point in the history
…c#195142) (elastic#195431)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Search][Onboarding] Default home to Global Empty State
(elastic#195142)](elastic#195142)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Rodney
Norris","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-10-08T14:29:59Z","message":"[Search][Onboarding]
Default home to Global Empty State (elastic#195142)\n\n## Summary\r\nUpdated
the ES3 (Serverless Search) default home route to be the global\r\nempty
state, when `search_indices` is enabled.\r\nMoved the getting started
page, the current homepage, from\r\n`/app/elasticsearch` to
`/app/elasticsearch/getting_started`\r\nThis required adding a redirect
for `/app/elasticsearch` to\r\n`/app/elasticsearch/start`.\r\n\r\nAfter
we enabled `search_indices` by default for ES3, we can remove
the\r\nconditional logic added by this PR.\r\n\r\n### Screenshots\r\nES3
Home With search indices config FF
enabled\r\n\r\n![image](https://github.com/user-attachments/assets/9a2227c0-8ec3-4e98-ba5c-08cebf8d3df4)\r\nES3
Home with search indices config FF
disabled\r\n\r\n![image](https://github.com/user-attachments/assets/68bb6f36-f754-4f6b-9637-cf419ef21945)\r\n\r\n\r\n###
Checklist\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [ ] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests
changed","sha":"d573915dd30c62adb780728ab83668285f5bd64d","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","Team:Search","backport:prev-minor"],"title":"[Search][Onboarding]
Default home to Global Empty
State","number":195142,"url":"https://github.com/elastic/kibana/pull/195142","mergeCommit":{"message":"[Search][Onboarding]
Default home to Global Empty State (elastic#195142)\n\n## Summary\r\nUpdated
the ES3 (Serverless Search) default home route to be the global\r\nempty
state, when `search_indices` is enabled.\r\nMoved the getting started
page, the current homepage, from\r\n`/app/elasticsearch` to
`/app/elasticsearch/getting_started`\r\nThis required adding a redirect
for `/app/elasticsearch` to\r\n`/app/elasticsearch/start`.\r\n\r\nAfter
we enabled `search_indices` by default for ES3, we can remove
the\r\nconditional logic added by this PR.\r\n\r\n### Screenshots\r\nES3
Home With search indices config FF
enabled\r\n\r\n![image](https://github.com/user-attachments/assets/9a2227c0-8ec3-4e98-ba5c-08cebf8d3df4)\r\nES3
Home with search indices config FF
disabled\r\n\r\n![image](https://github.com/user-attachments/assets/68bb6f36-f754-4f6b-9637-cf419ef21945)\r\n\r\n\r\n###
Checklist\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [ ] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests
changed","sha":"d573915dd30c62adb780728ab83668285f5bd64d"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/195142","number":195142,"mergeCommit":{"message":"[Search][Onboarding]
Default home to Global Empty State (elastic#195142)\n\n## Summary\r\nUpdated
the ES3 (Serverless Search) default home route to be the global\r\nempty
state, when `search_indices` is enabled.\r\nMoved the getting started
page, the current homepage, from\r\n`/app/elasticsearch` to
`/app/elasticsearch/getting_started`\r\nThis required adding a redirect
for `/app/elasticsearch` to\r\n`/app/elasticsearch/start`.\r\n\r\nAfter
we enabled `search_indices` by default for ES3, we can remove
the\r\nconditional logic added by this PR.\r\n\r\n### Screenshots\r\nES3
Home With search indices config FF
enabled\r\n\r\n![image](https://github.com/user-attachments/assets/9a2227c0-8ec3-4e98-ba5c-08cebf8d3df4)\r\nES3
Home with search indices config FF
disabled\r\n\r\n![image](https://github.com/user-attachments/assets/68bb6f36-f754-4f6b-9637-cf419ef21945)\r\n\r\n\r\n###
Checklist\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [ ] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests
changed","sha":"d573915dd30c62adb780728ab83668285f5bd64d"}}]}]
BACKPORT-->

Co-authored-by: Rodney Norris <[email protected]>
  • Loading branch information
kibanamachine and TattdCodeMonkey authored Oct 8, 2024
1 parent 6854213 commit 9bd5789
Show file tree
Hide file tree
Showing 24 changed files with 236 additions and 84 deletions.
2 changes: 2 additions & 0 deletions packages/deeplinks/search/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ export const SERVERLESS_ES_CONNECTORS_ID = 'serverlessConnectors';
export const SERVERLESS_ES_SEARCH_PLAYGROUND_ID = 'searchPlayground';
export const SERVERLESS_ES_SEARCH_INFERENCE_ENDPOINTS_ID = 'searchInferenceEndpoints';
export const SEARCH_HOMEPAGE = 'searchHomepage';
export const SEARCH_INDICES_START = 'elasticsearchStart';
export const SEARCH_INDICES = 'elasticsearchIndices';
8 changes: 7 additions & 1 deletion packages/deeplinks/search/deep_links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import {
SERVERLESS_ES_SEARCH_PLAYGROUND_ID,
SERVERLESS_ES_SEARCH_INFERENCE_ENDPOINTS_ID,
SEARCH_HOMEPAGE,
SEARCH_INDICES_START,
SEARCH_INDICES,
} from './constants';

export type EnterpriseSearchApp = typeof ENTERPRISE_SEARCH_APP_ID;
Expand All @@ -34,6 +36,8 @@ export type ConnectorsId = typeof SERVERLESS_ES_CONNECTORS_ID;
export type SearchPlaygroundId = typeof SERVERLESS_ES_SEARCH_PLAYGROUND_ID;
export type SearchInferenceEndpointsId = typeof SERVERLESS_ES_SEARCH_INFERENCE_ENDPOINTS_ID;
export type SearchHomepage = typeof SEARCH_HOMEPAGE;
export type SearchStart = typeof SEARCH_INDICES_START;
export type SearchIndices = typeof SEARCH_INDICES;

export type ContentLinkId = 'searchIndices' | 'connectors' | 'webCrawlers';

Expand All @@ -59,4 +63,6 @@ export type DeepLinkId =
| `${EnterpriseSearchContentApp}:${ContentLinkId}`
| `${EnterpriseSearchApplicationsApp}:${ApplicationsLinkId}`
| `${EnterpriseSearchAppsearchApp}:${AppsearchLinkId}`
| `${EnterpriseSearchRelevanceApp}:${RelevanceLinkId}`;
| `${EnterpriseSearchRelevanceApp}:${RelevanceLinkId}`
| SearchStart
| SearchIndices;
4 changes: 4 additions & 0 deletions x-pack/plugins/search_indices/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
* 2.0.
*/

import type { SearchIndices, SearchStart } from '@kbn/deeplinks-search/deep_links';

export const PLUGIN_ID = 'searchIndices';
export const PLUGIN_NAME = 'searchIndices';
export const START_APP_ID: SearchStart = 'elasticsearchStart';
export const INDICES_APP_ID: SearchIndices = 'elasticsearchIndices';

export type { IndicesStatusResponse, UserStartPrivilegesResponse } from './types';
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,17 @@ export const CreateIndexForm = ({
const [indexNameHasError, setIndexNameHasError] = useState<boolean>(false);
const usageTracker = useUsageTracker();
const { createIndex, isLoading } = useCreateIndex();
const onCreateIndex = useCallback(() => {
if (!isValidIndexName(formState.indexName)) {
return;
}
usageTracker.click(AnalyticsEvents.startCreateIndexClick);
createIndex({ indexName: formState.indexName });
}, [usageTracker, createIndex, formState.indexName]);
const onCreateIndex = useCallback(
(e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!isValidIndexName(formState.indexName)) {
return;
}
usageTracker.click(AnalyticsEvents.startCreateIndexClick);
createIndex({ indexName: formState.indexName });
},
[usageTracker, createIndex, formState.indexName]
);
const onIndexNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newIndexName = e.target.value;
setFormState({ ...formState, indexName: e.target.value });
Expand All @@ -78,7 +82,12 @@ export const CreateIndexForm = ({

return (
<>
<EuiForm data-test-subj="createIndexUIView" fullWidth component="form">
<EuiForm
data-test-subj="createIndexUIView"
fullWidth
component="form"
onSubmit={onCreateIndex}
>
<EuiFormRow
label={i18n.translate('xpack.searchIndices.startPage.createIndex.name.label', {
defaultMessage: 'Name your index',
Expand Down Expand Up @@ -137,11 +146,9 @@ export const CreateIndexForm = ({
color="primary"
iconSide="left"
iconType="sparkles"
data-telemetry-id="searchIndices-start-createIndexBtn"
data-test-subj="createIndexBtn"
disabled={indexNameHasError || isLoading}
isLoading={isLoading}
onClick={onCreateIndex}
type="submit"
>
{CREATE_INDEX_CONTENT}
Expand Down Expand Up @@ -181,11 +188,7 @@ export const CreateIndexForm = ({
defaultMessage="Already have some data? {link}"
values={{
link: (
<EuiLink
data-telemetry-id="searchIndices-start-uploadFile"
data-test-subj="uploadFileLink"
onClick={onFileUpload}
>
<EuiLink data-test-subj="uploadFileLink" onClick={onFileUpload}>
{i18n.translate(
'xpack.searchIndices.startPage.createIndex.fileUpload.link',
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,6 @@ export const ElasticsearchStart = ({ userPrivileges }: ElasticsearchStartProps)
iconSide="right"
iconType="popout"
data-test-subj="analyzeLogsBtn"
data-telemetry-id="searchIndicesStartCollectLogsLink"
href={docLinks.analyzeLogs}
target="_blank"
>
Expand Down Expand Up @@ -249,7 +248,6 @@ export const ElasticsearchStart = ({ userPrivileges }: ElasticsearchStartProps)
iconSide="right"
iconType="popout"
data-test-subj="startO11yTrialBtn"
data-telemetry-id="searchIndicesStartO11yTrialLink"
href={o11yTrialLink}
target="_blank"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import { useEffect } from 'react';
import { useEffect, useState } from 'react';

import type { IndicesStatusResponse } from '../../../../common';

Expand All @@ -15,13 +15,30 @@ import { navigateToIndexDetails } from './utils';

export const useIndicesRedirect = (indicesStatus?: IndicesStatusResponse) => {
const { application, http } = useKibana().services;
const [lastStatus, setLastStatus] = useState<IndicesStatusResponse | undefined>(() => undefined);
const [hasDoneRedirect, setHasDoneRedirect] = useState(() => false);
return useEffect(() => {
if (!indicesStatus) return;
if (indicesStatus.indexNames.length === 0) return;
if (hasDoneRedirect) {
return;
}
if (!indicesStatus) {
return;
}
if (indicesStatus.indexNames.length === 0) {
setLastStatus(indicesStatus);
return;
}
if (lastStatus === undefined && indicesStatus.indexNames.length > 0) {
application.navigateToApp('management', { deepLinkId: 'index_management' });
setHasDoneRedirect(true);
return;
}
if (indicesStatus.indexNames.length === 1) {
navigateToIndexDetails(application, http, indicesStatus.indexNames[0]);
setHasDoneRedirect(true);
return;
}
application.navigateToApp('management', { deepLinkId: 'index_management' });
}, [application, http, indicesStatus]);
setHasDoneRedirect(true);
}, [application, http, indicesStatus, lastStatus, hasDoneRedirect]);
};
18 changes: 13 additions & 5 deletions x-pack/plugins/search_indices/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import type {
SearchIndicesServicesContextDeps,
} from './types';
import { initQueryClient } from './services/query_client';
import { INDICES_APP_ID, START_APP_ID } from '../common';
import { INDICES_APP_BASE, START_APP_BASE } from './routes';

export class SearchIndicesPlugin
implements Plugin<SearchIndicesPluginSetup, SearchIndicesPluginStart>
Expand All @@ -26,8 +28,8 @@ export class SearchIndicesPlugin
const queryClient = initQueryClient(core.notifications.toasts);

core.application.register({
id: 'elasticsearchStart',
appRoute: '/app/elasticsearch/start',
id: START_APP_ID,
appRoute: START_APP_BASE,
title: i18n.translate('xpack.searchIndices.elasticsearchStart.startAppTitle', {
defaultMessage: 'Elasticsearch Start',
}),
Expand All @@ -43,8 +45,8 @@ export class SearchIndicesPlugin
},
});
core.application.register({
id: 'elasticsearchIndices',
appRoute: '/app/elasticsearch/indices',
id: INDICES_APP_ID,
appRoute: INDICES_APP_BASE,
title: i18n.translate('xpack.searchIndices.elasticsearchIndices.startAppTitle', {
defaultMessage: 'Elasticsearch Indices',
}),
Expand All @@ -62,12 +64,18 @@ export class SearchIndicesPlugin

return {
enabled: true,
startAppId: START_APP_ID,
startRoute: START_APP_BASE,
};
}

public start(core: CoreStart): SearchIndicesPluginStart {
docLinks.setDocLinks(core.docLinks.links);
return {};
return {
enabled: true,
startAppId: START_APP_ID,
startRoute: START_APP_BASE,
};
}

public stop() {}
Expand Down
3 changes: 3 additions & 0 deletions x-pack/plugins/search_indices/public/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ export enum SearchIndexDetailsTabs {
MAPPINGS = 'mappings',
SETTINGS = 'settings',
}

export const START_APP_BASE = '/app/elasticsearch/start';
export const INDICES_APP_BASE = '/app/elasticsearch/indices';
13 changes: 10 additions & 3 deletions x-pack/plugins/search_indices/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,20 @@ import type {
MappingProperty,
MappingPropertyBase,
} from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { IndexManagementPluginStart } from '@kbn/index-management-shared-types';
import type { IndexManagementPluginStart } from '@kbn/index-management-shared-types';
import type { AppDeepLinkId } from '@kbn/core-chrome-browser';

export interface SearchIndicesPluginSetup {
enabled: boolean;
startAppId: string;
startRoute: string;
}

export interface SearchIndicesPluginStart {
enabled: boolean;
startAppId: AppDeepLinkId;
startRoute: string;
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface SearchIndicesPluginStart {}

export interface AppPluginStartDependencies {
navigation: NavigationPublicPluginStart;
Expand Down
4 changes: 3 additions & 1 deletion x-pack/plugins/search_indices/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@
"@kbn/es-types",
"@kbn/search-api-keys-server",
"@kbn/search-api-keys-components",
"@kbn/search-shared-ui"
"@kbn/search-shared-ui",
"@kbn/deeplinks-search",
"@kbn/core-chrome-browser"
],
"exclude": [
"target/**/*",
Expand Down
33 changes: 30 additions & 3 deletions x-pack/plugins/serverless_search/public/navigation_tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,27 @@
* 2.0.
*/

import type { NavigationTreeDefinition } from '@kbn/core-chrome-browser';
import type {
AppDeepLinkId,
NavigationTreeDefinition,
NodeDefinition,
} from '@kbn/core-chrome-browser';
import { i18n } from '@kbn/i18n';
import { CONNECTORS_LABEL } from '../common/i18n_string';

export const navigationTree = (): NavigationTreeDefinition => ({
const gettingStartedItem: NodeDefinition<AppDeepLinkId, string, string> = {
id: 'gettingStarted',
title: i18n.translate('xpack.serverlessSearch.nav.gettingStarted', {
defaultMessage: 'Getting Started',
}),
link: 'serverlessElasticsearch',
spaceBefore: 'm',
};

export const navigationTree = (
homeLink: AppDeepLinkId = 'serverlessElasticsearch' as AppDeepLinkId,
showGettingStarted: boolean
): NavigationTreeDefinition => ({
body: [
{
type: 'navGroup',
Expand All @@ -25,7 +41,7 @@ export const navigationTree = (): NavigationTreeDefinition => ({
title: i18n.translate('xpack.serverlessSearch.nav.home', {
defaultMessage: 'Home',
}),
link: 'serverlessElasticsearch',
link: homeLink,
spaceBefore: 'm',
},
{
Expand Down Expand Up @@ -70,6 +86,16 @@ export const navigationTree = (): NavigationTreeDefinition => ({
link: 'management:index_management',
breadcrumbStatus:
'hidden' /* management sub-pages set their breadcrumbs themselves */,
getIsActive: ({ pathNameSerialized, prepend }) => {
return (
pathNameSerialized.startsWith(
prepend('/app/management/data/index_management/')
) ||
pathNameSerialized.startsWith(
prepend('/app/elasticsearch/indices/index_details/')
)
);
},
},
{
title: CONNECTORS_LABEL,
Expand Down Expand Up @@ -112,6 +138,7 @@ export const navigationTree = (): NavigationTreeDefinition => ({
},
],
},
...(showGettingStarted ? [gettingStartedItem] : []),
],
},
],
Expand Down
37 changes: 32 additions & 5 deletions x-pack/plugins/serverless_search/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ export class ServerlessSearchPlugin
const homeTitle = i18n.translate('xpack.serverlessSearch.app.home.title', {
defaultMessage: 'Home',
});
const useGlobalEmptyState = setupDeps.searchIndices?.enabled ?? false;
const serverlessElasticsearchAppRoute = useGlobalEmptyState
? '/app/elasticsearch/getting_started'
: '/app/elasticsearch';

core.application.register({
id: 'serverlessElasticsearch',
Expand All @@ -81,7 +85,7 @@ export class ServerlessSearchPlugin
}),
euiIconType: 'logoElastic',
category: DEFAULT_APP_CATEGORIES.enterpriseSearch,
appRoute: '/app/elasticsearch',
appRoute: serverlessElasticsearchAppRoute,
async mount({ element, history }: AppMountParameters) {
const { renderApp } = await import('./application/elasticsearch');
const [coreStart, services] = await core.getStartServices();
Expand Down Expand Up @@ -120,6 +124,24 @@ export class ServerlessSearchPlugin
},
});

if (useGlobalEmptyState) {
const redirectApp = setupDeps.searchIndices!.startAppId;
core.application.register({
id: 'serverlessHomeRedirect',
title: homeTitle,
appRoute: '/app/elasticsearch',
euiIconType: 'logoElastic',
category: DEFAULT_APP_CATEGORIES.enterpriseSearch,
visibleIn: [],
async mount({}: AppMountParameters) {
const [coreStart] = await core.getStartServices();
coreStart.chrome.docTitle.change(homeTitle);
coreStart.application.navigateToApp(redirectApp);
return () => {};
},
});
}

setupDeps.discover.showInlineTopNav();

return {};
Expand All @@ -130,10 +152,15 @@ export class ServerlessSearchPlugin
services: ServerlessSearchPluginStartDependencies
): ServerlessSearchPluginStart {
const { serverless, management, indexManagement, security } = services;

serverless.setProjectHome('/app/elasticsearch');

const navigationTree$ = of(navigationTree());
const useGlobalEmptyState = services.searchIndices?.enabled ?? false;
const homeRoute = useGlobalEmptyState
? services.searchIndices!.startRoute
: '/app/elasticsearch';
serverless.setProjectHome(homeRoute);

const navigationTree$ = of(
navigationTree(services.searchIndices?.startAppId, useGlobalEmptyState)
);
serverless.initNavigation('search', navigationTree$, { dataTestSubj: 'svlSearchSideNav' });

const extendCardNavDefinitions = serverless.getNavigationCards(
Expand Down
Loading

0 comments on commit 9bd5789

Please sign in to comment.