From a540cafb85eb92640e4d3963ba0bef6ce95a8cb6 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Tue, 21 Jul 2020 07:04:40 -0700 Subject: [PATCH] Add doc titles to ES UI apps (#71045) * Add doc titles to CCR, ILM, Index Management, Ingest Node Pipelines, License Management, Remote Clusters, Rollup Jobs, Watcher, and Upgrade Assistant. Clear doc title when leaving Dev Tools. * Refactor Watcher boot file to follow index-oriented pattern of other plugins. --- src/plugins/dev_tools/public/application.tsx | 1 + .../public/plugin.ts | 10 +++++- .../public/plugin.tsx | 10 +++++- .../application/mount_management_section.ts | 18 +++++++++-- .../plugins/ingest_pipelines/public/plugin.ts | 24 +++++++++++--- .../license_management/public/plugin.ts | 17 +++++++--- .../plugins/remote_clusters/public/plugin.ts | 16 +++++++++- x-pack/plugins/rollup/public/plugin.ts | 28 ++++++++++------ .../application/mount_management_section.ts | 1 - .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../upgrade_assistant/public/plugin.ts | 27 +++++++++++++--- .../application/{boot.tsx => index.tsx} | 3 +- x-pack/plugins/watcher/public/plugin.ts | 32 ++++++++++++++----- 14 files changed, 149 insertions(+), 40 deletions(-) rename x-pack/plugins/watcher/public/application/{boot.tsx => index.tsx} (94%) diff --git a/src/plugins/dev_tools/public/application.tsx b/src/plugins/dev_tools/public/application.tsx index 788ec1f145e2a..46f09a8ebb879 100644 --- a/src/plugins/dev_tools/public/application.tsx +++ b/src/plugins/dev_tools/public/application.tsx @@ -203,6 +203,7 @@ export function renderApp( }); return () => { + chrome.docTitle.reset(); ReactDOM.unmountComponentAtNode(element); unlisten(); }; diff --git a/x-pack/plugins/cross_cluster_replication/public/plugin.ts b/x-pack/plugins/cross_cluster_replication/public/plugin.ts index 7aa0d19fa976f..80e67110c20c7 100644 --- a/x-pack/plugins/cross_cluster_replication/public/plugin.ts +++ b/x-pack/plugins/cross_cluster_replication/public/plugin.ts @@ -45,12 +45,15 @@ export class CrossClusterReplicationPlugin implements Plugin { const [coreStart] = await getStartServices(); const { + chrome: { docTitle }, i18n: { Context: I18nContext }, docLinks: { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION }, application: { getUrlForApp }, } = coreStart; - return mountApp({ + docTitle.change(PLUGIN.TITLE); + + const unmountAppCallback = await mountApp({ element, setBreadcrumbs, I18nContext, @@ -59,6 +62,11 @@ export class CrossClusterReplicationPlugin implements Plugin { history, getUrlForApp, }); + + return () => { + docTitle.reset(); + unmountAppCallback(); + }; }, }); diff --git a/x-pack/plugins/index_lifecycle_management/public/plugin.tsx b/x-pack/plugins/index_lifecycle_management/public/plugin.tsx index 832d066dfa33b..1d26aa53752a9 100644 --- a/x-pack/plugins/index_lifecycle_management/public/plugin.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/plugin.tsx @@ -44,18 +44,26 @@ export class IndexLifecycleManagementPlugin { mount: async ({ element, history }) => { const [coreStart] = await getStartServices(); const { + chrome: { docTitle }, i18n: { Context: I18nContext }, docLinks: { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION }, application: { navigateToApp }, } = coreStart; + docTitle.change(PLUGIN.TITLE); + // Initialize additional services. initDocumentation( `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}/` ); const { renderApp } = await import('./application'); - return renderApp(element, I18nContext, history, navigateToApp); + const unmountAppCallback = renderApp(element, I18nContext, history, navigateToApp); + + return () => { + docTitle.reset(); + unmountAppCallback(); + }; }, }); diff --git a/x-pack/plugins/index_management/public/application/mount_management_section.ts b/x-pack/plugins/index_management/public/application/mount_management_section.ts index 6145ea410b0e8..6257b68430cf0 100644 --- a/x-pack/plugins/index_management/public/application/mount_management_section.ts +++ b/x-pack/plugins/index_management/public/application/mount_management_section.ts @@ -4,11 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ +import { i18n } from '@kbn/i18n'; import { CoreSetup } from 'src/core/public'; import { ManagementAppMountParams } from 'src/plugins/management/public/'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; import { IngestManagerSetup } from '../../../ingest_manager/public'; +import { PLUGIN } from '../../common/constants'; import { ExtensionsService } from '../services'; import { IndexMgmtMetricsType } from '../types'; import { AppDependencies } from './app_context'; @@ -34,7 +36,14 @@ export async function mountManagementSection( ) { const { element, setBreadcrumbs, history } = params; const [core] = await coreSetup.getStartServices(); - const { docLinks, fatalErrors, application } = core; + const { + docLinks, + fatalErrors, + application, + chrome: { docTitle }, + } = core; + + docTitle.change(PLUGIN.getI18nName(i18n)); breadcrumbService.setup(setBreadcrumbs); documentationService.setup(docLinks); @@ -53,5 +62,10 @@ export async function mountManagementSection( setBreadcrumbs, }; - return renderApp(element, { core, dependencies: appDependencies }); + const unmountAppCallback = renderApp(element, { core, dependencies: appDependencies }); + + return () => { + docTitle.reset(); + unmountAppCallback(); + }; } diff --git a/x-pack/plugins/ingest_pipelines/public/plugin.ts b/x-pack/plugins/ingest_pipelines/public/plugin.ts index 945e825c88fbd..339068f185d1d 100644 --- a/x-pack/plugins/ingest_pipelines/public/plugin.ts +++ b/x-pack/plugins/ingest_pipelines/public/plugin.ts @@ -14,22 +14,36 @@ import { Dependencies } from './types'; export class IngestPipelinesPlugin implements Plugin { public setup(coreSetup: CoreSetup, plugins: Dependencies): void { const { management, usageCollection } = plugins; - const { http } = coreSetup; + const { http, getStartServices } = coreSetup; // Initialize services uiMetricService.setup(usageCollection); apiService.setup(http, uiMetricService); + const pluginName = i18n.translate('xpack.ingestPipelines.appTitle', { + defaultMessage: 'Ingest Node Pipelines', + }); + management.sections.section.ingest.registerApp({ id: PLUGIN_ID, order: 1, - title: i18n.translate('xpack.ingestPipelines.appTitle', { - defaultMessage: 'Ingest Node Pipelines', - }), + title: pluginName, mount: async (params) => { + const [coreStart] = await getStartServices(); + + const { + chrome: { docTitle }, + } = coreStart; + + docTitle.change(pluginName); + const { mountManagementSection } = await import('./application/mount_management_section'); + const unmountAppCallback = await mountManagementSection(coreSetup, params); - return await mountManagementSection(coreSetup, params); + return () => { + docTitle.reset(); + unmountAppCallback(); + }; }, }); } diff --git a/x-pack/plugins/license_management/public/plugin.ts b/x-pack/plugins/license_management/public/plugin.ts index b99ea387121ee..27e31726f9a19 100644 --- a/x-pack/plugins/license_management/public/plugin.ts +++ b/x-pack/plugins/license_management/public/plugin.ts @@ -55,22 +55,27 @@ export class LicenseManagementUIPlugin title: PLUGIN.title, order: 0, mount: async ({ element, setBreadcrumbs, history }) => { - const [core, { telemetry }] = await getStartServices(); + const [coreStart, { telemetry }] = await getStartServices(); const initialLicense = await plugins.licensing.license$.pipe(first()).toPromise(); // Setup documentation links - const { docLinks } = core; + const { + docLinks, + chrome: { docTitle }, + } = coreStart; const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = docLinks; const esBase = `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}`; const appDocLinks = { security: `${esBase}/security-settings.html`, }; + docTitle.change(PLUGIN.title); + // Setup services this.breadcrumbService.setup(setBreadcrumbs); const appDependencies: AppDependencies = { - core, + core: coreStart, config, plugins: { licensing, @@ -87,8 +92,12 @@ export class LicenseManagementUIPlugin }; const { renderApp } = await import('./application'); + const unmountAppCallback = renderApp(element, appDependencies); - return renderApp(element, appDependencies); + return () => { + docTitle.reset(); + unmountAppCallback(); + }; }, }); diff --git a/x-pack/plugins/remote_clusters/public/plugin.ts b/x-pack/plugins/remote_clusters/public/plugin.ts index 33222dd7052e9..24cb148d24d84 100644 --- a/x-pack/plugins/remote_clusters/public/plugin.ts +++ b/x-pack/plugins/remote_clusters/public/plugin.ts @@ -7,6 +7,7 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup, Plugin, CoreStart, PluginInitializerContext } from 'kibana/public'; +import { PLUGIN } from '../common/constants'; import { init as initBreadcrumbs } from './application/services/breadcrumb'; import { init as initDocumentation } from './application/services/documentation'; import { init as initHttp } from './application/services/http'; @@ -43,11 +44,14 @@ export class RemoteClustersUIPlugin mount: async ({ element, setBreadcrumbs, history }) => { const [core] = await getStartServices(); const { + chrome: { docTitle }, i18n: { Context: i18nContext }, docLinks, fatalErrors, } = core; + docTitle.change(PLUGIN.getI18nName()); + // Initialize services initBreadcrumbs(setBreadcrumbs); initDocumentation(docLinks); @@ -58,7 +62,17 @@ export class RemoteClustersUIPlugin const isCloudEnabled = Boolean(cloud?.isCloudEnabled); const { renderApp } = await import('./application'); - return renderApp(element, i18nContext, { isCloudEnabled }, history); + const unmountAppCallback = await renderApp( + element, + i18nContext, + { isCloudEnabled }, + history + ); + + return () => { + docTitle.reset(); + unmountAppCallback(); + }; }, }); } diff --git a/x-pack/plugins/rollup/public/plugin.ts b/x-pack/plugins/rollup/public/plugin.ts index 73ee675b089c8..49545f090d923 100644 --- a/x-pack/plugins/rollup/public/plugin.ts +++ b/x-pack/plugins/rollup/public/plugin.ts @@ -75,21 +75,31 @@ export class RollupPlugin implements Plugin { }); } + const pluginName = i18n.translate('xpack.rollupJobs.appTitle', { + defaultMessage: 'Rollup Jobs', + }); + management.sections.section.data.registerApp({ id: 'rollup_jobs', - title: i18n.translate('xpack.rollupJobs.appTitle', { defaultMessage: 'Rollup Jobs' }), + title: pluginName, order: 4, async mount(params) { - params.setBreadcrumbs([ - { - text: i18n.translate('xpack.rollupJobs.breadcrumbsTitle', { - defaultMessage: 'Rollup Jobs', - }), - }, - ]); + const [coreStart] = await core.getStartServices(); + + const { + chrome: { docTitle }, + } = coreStart; + + docTitle.change(pluginName); + params.setBreadcrumbs([{ text: pluginName }]); + const { renderApp } = await import('./application'); + const unmountAppCallback = await renderApp(core, params); - return renderApp(core, params); + return () => { + docTitle.reset(); + unmountAppCallback(); + }; }, }); } diff --git a/x-pack/plugins/snapshot_restore/public/application/mount_management_section.ts b/x-pack/plugins/snapshot_restore/public/application/mount_management_section.ts index b06a60a5c830f..eb2046f065e17 100644 --- a/x-pack/plugins/snapshot_restore/public/application/mount_management_section.ts +++ b/x-pack/plugins/snapshot_restore/public/application/mount_management_section.ts @@ -48,7 +48,6 @@ export async function mountManagementSection( const unmountAppCallback = renderApp(element, appDependencies); return () => { - // Change tab label back to Kibana. docTitle.reset(); unmountAppCallback(); }; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index c1f0dc4c0c60c..4175f76ad7ba8 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -12332,7 +12332,6 @@ "xpack.reporting.shareContextMenu.pdfReportsButtonLabel": "PDF レポート", "xpack.reporting.shareContextMenu.pngReportsButtonLabel": "PNG レポート", "xpack.rollupJobs.appTitle": "ロールアップジョブ", - "xpack.rollupJobs.breadcrumbsTitle": "ロールアップジョブ", "xpack.rollupJobs.create.backButton.label": "戻る", "xpack.rollupJobs.create.dateTypeField": "日付", "xpack.rollupJobs.create.errors.dateHistogramFieldMissing": "日付フィールドが必要です。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 0f2a51c8ff889..33d60dbd17700 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -12338,7 +12338,6 @@ "xpack.reporting.shareContextMenu.pdfReportsButtonLabel": "PDF 报告", "xpack.reporting.shareContextMenu.pngReportsButtonLabel": "PNG 报告", "xpack.rollupJobs.appTitle": "汇总/打包作业", - "xpack.rollupJobs.breadcrumbsTitle": "汇总/打包作业", "xpack.rollupJobs.create.backButton.label": "上一步", "xpack.rollupJobs.create.dateTypeField": "日期", "xpack.rollupJobs.create.errors.dateHistogramFieldMissing": "“日期”字段必填。", diff --git a/x-pack/plugins/upgrade_assistant/public/plugin.ts b/x-pack/plugins/upgrade_assistant/public/plugin.ts index 01c1a6a4659d5..98f1b8351b88b 100644 --- a/x-pack/plugins/upgrade_assistant/public/plugin.ts +++ b/x-pack/plugins/upgrade_assistant/public/plugin.ts @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ + import { i18n } from '@kbn/i18n'; import { Plugin, CoreSetup, PluginInitializerContext } from 'src/core/public'; @@ -21,22 +22,38 @@ export class UpgradeAssistantUIPlugin implements Plugin { constructor(private ctx: PluginInitializerContext) {} setup(coreSetup: CoreSetup, { cloud, management }: Dependencies) { const { enabled } = this.ctx.config.get(); + if (!enabled) { return; } + const appRegistrar = management.sections.section.stack; const isCloudEnabled = Boolean(cloud?.isCloudEnabled); + const pluginName = i18n.translate('xpack.upgradeAssistant.appTitle', { + defaultMessage: '{version} Upgrade Assistant', + values: { version: `${NEXT_MAJOR_VERSION}.0` }, + }); + appRegistrar.registerApp({ id: 'upgrade_assistant', - title: i18n.translate('xpack.upgradeAssistant.appTitle', { - defaultMessage: '{version} Upgrade Assistant', - values: { version: `${NEXT_MAJOR_VERSION}.0` }, - }), + title: pluginName, order: 1, async mount(params) { + const [coreStart] = await coreSetup.getStartServices(); + + const { + chrome: { docTitle }, + } = coreStart; + + docTitle.change(pluginName); const { mountManagementSection } = await import('./application/mount_management_section'); - return mountManagementSection(coreSetup, isCloudEnabled, params); + const unmountAppCallback = await mountManagementSection(coreSetup, isCloudEnabled, params); + + return () => { + docTitle.reset(); + unmountAppCallback(); + }; }, }); } diff --git a/x-pack/plugins/watcher/public/application/boot.tsx b/x-pack/plugins/watcher/public/application/index.tsx similarity index 94% rename from x-pack/plugins/watcher/public/application/boot.tsx rename to x-pack/plugins/watcher/public/application/index.tsx index 8461bd65bbd5e..70a63e6a04dd6 100644 --- a/x-pack/plugins/watcher/public/application/boot.tsx +++ b/x-pack/plugins/watcher/public/application/index.tsx @@ -17,7 +17,7 @@ interface BootDeps extends AppDeps { I18nContext: any; } -export const boot = (bootDeps: BootDeps) => { +export const renderApp = (bootDeps: BootDeps) => { const { I18nContext, element, savedObjects, ...appDeps } = bootDeps; setHttpClient(appDeps.http); @@ -29,6 +29,7 @@ export const boot = (bootDeps: BootDeps) => { , element ); + return () => { unmountComponentAtNode(element); }; diff --git a/x-pack/plugins/watcher/public/plugin.ts b/x-pack/plugins/watcher/public/plugin.ts index 6b66c341497b7..98b49af15019b 100644 --- a/x-pack/plugins/watcher/public/plugin.ts +++ b/x-pack/plugins/watcher/public/plugin.ts @@ -30,20 +30,31 @@ export class WatcherUIPlugin implements Plugin { ) { const esSection = management.sections.section.insightsAndAlerting; + const pluginName = i18n.translate( + 'xpack.watcher.sections.watchList.managementSection.watcherDisplayName', + { defaultMessage: 'Watcher' } + ); + const watcherESApp = esSection.registerApp({ id: 'watcher', - title: i18n.translate( - 'xpack.watcher.sections.watchList.managementSection.watcherDisplayName', - { defaultMessage: 'Watcher' } - ), + title: pluginName, order: 3, mount: async ({ element, setBreadcrumbs, history }) => { - const [core] = await getStartServices(); - const { i18n: i18nDep, docLinks, savedObjects, application } = core; - const { boot } = await import('./application/boot'); + const [coreStart] = await getStartServices(); + const { + chrome: { docTitle }, + i18n: i18nDep, + docLinks, + savedObjects, + application, + } = coreStart; + + docTitle.change(pluginName); + + const { renderApp } = await import('./application'); const { TimeBuckets } = await import('./legacy'); - return boot({ + const unmountAppCallback = renderApp({ // Skip the first license status, because that's already been used to determine // whether to include Watcher. licenseStatus$: licensing.license$.pipe(skip(1), map(licenseToLicenseStatus)), @@ -60,6 +71,11 @@ export class WatcherUIPlugin implements Plugin { history, getUrlForApp: application.getUrlForApp, }); + + return () => { + docTitle.reset(); + unmountAppCallback(); + }; }, });