Skip to content

Commit

Permalink
[Dev Tools] Change breadcrumbs and docTitle when toggling between apps (
Browse files Browse the repository at this point in the history
  • Loading branch information
sabarasaba authored Nov 18, 2021
1 parent 4d3d95a commit 617be51
Show file tree
Hide file tree
Showing 15 changed files with 228 additions and 67 deletions.
64 changes: 39 additions & 25 deletions src/plugins/dev_tools/public/application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,27 @@ import React, { useEffect, useRef } from 'react';
import { Observable } from 'rxjs';
import ReactDOM from 'react-dom';
import { HashRouter as Router, Switch, Route, Redirect } from 'react-router-dom';
import { EuiTab, EuiTabs, EuiToolTip } from '@elastic/eui';
import { EuiTab, EuiTabs, EuiToolTip, EuiBetaBadge } from '@elastic/eui';
import { I18nProvider } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { euiThemeVars } from '@kbn/ui-shared-deps-src/theme';

import { ApplicationStart, ChromeStart, ScopedHistory, CoreTheme } from 'src/core/public';
import type { DocTitleService, BreadcrumbService } from './services';

import { DevToolApp } from './dev_tool';

export interface AppServices {
docTitleService: DocTitleService;
breadcrumbService: BreadcrumbService;
}

interface DevToolsWrapperProps {
devTools: readonly DevToolApp[];
activeDevTool: DevToolApp;
updateRoute: (newRoute: string) => void;
theme$: Observable<CoreTheme>;
appServices: AppServices;
}

interface MountedDevToolDescriptor {
Expand All @@ -32,7 +39,14 @@ interface MountedDevToolDescriptor {
unmountHandler: () => void;
}

function DevToolsWrapper({ devTools, activeDevTool, updateRoute, theme$ }: DevToolsWrapperProps) {
function DevToolsWrapper({
devTools,
activeDevTool,
updateRoute,
theme$,
appServices,
}: DevToolsWrapperProps) {
const { docTitleService, breadcrumbService } = appServices;
const mountedTool = useRef<MountedDevToolDescriptor | null>(null);

useEffect(
Expand All @@ -44,6 +58,11 @@ function DevToolsWrapper({ devTools, activeDevTool, updateRoute, theme$ }: DevTo
[]
);

useEffect(() => {
docTitleService.setTitle(activeDevTool.title);
breadcrumbService.setBreadcrumbs(activeDevTool.title);
}, [activeDevTool, docTitleService, breadcrumbService]);

return (
<main className="devApp">
<EuiTabs style={{ paddingLeft: euiThemeVars.euiSizeS }} size="l">
Expand All @@ -59,7 +78,21 @@ function DevToolsWrapper({ devTools, activeDevTool, updateRoute, theme$ }: DevTo
}}
>
<EuiToolTip content={currentDevTool.tooltipContent}>
<span>{currentDevTool.title}</span>
<span>
{currentDevTool.title}{' '}
{currentDevTool.isBeta && (
<EuiBetaBadge
size="s"
className="devApp__tabBeta"
label={i18n.translate('devTools.badge.betaLabel', {
defaultMessage: 'Beta',
})}
tooltipContent={i18n.translate('devTools.badge.betaTooltipText', {
defaultMessage: 'This feature might change drastically in future releases',
})}
/>
)}
</span>
</EuiToolTip>
</EuiTab>
))}
Expand Down Expand Up @@ -127,40 +160,20 @@ function setBadge(application: ApplicationStart, chrome: ChromeStart) {
});
}

function setTitle(chrome: ChromeStart) {
chrome.docTitle.change(
i18n.translate('devTools.pageTitle', {
defaultMessage: 'Dev Tools',
})
);
}

function setBreadcrumbs(chrome: ChromeStart) {
chrome.setBreadcrumbs([
{
text: i18n.translate('devTools.k7BreadcrumbsDevToolsLabel', {
defaultMessage: 'Dev Tools',
}),
href: '#/',
},
]);
}

export function renderApp(
element: HTMLElement,
application: ApplicationStart,
chrome: ChromeStart,
history: ScopedHistory,
theme$: Observable<CoreTheme>,
devTools: readonly DevToolApp[]
devTools: readonly DevToolApp[],
appServices: AppServices
) {
if (redirectOnMissingCapabilities(application)) {
return () => {};
}

setBadge(application, chrome);
setBreadcrumbs(chrome);
setTitle(chrome);

ReactDOM.render(
<I18nProvider>
Expand All @@ -180,6 +193,7 @@ export function renderApp(
activeDevTool={devTool}
devTools={devTools}
theme$={theme$}
appServices={appServices}
/>
)}
/>
Expand Down
17 changes: 17 additions & 0 deletions src/plugins/dev_tools/public/constants/texts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { i18n } from '@kbn/i18n';

export const i18Texts = {
breadcrumbs: {
home: i18n.translate('devTools.breadcrumb.homeLabel', {
defaultMessage: 'Dev Tools',
}),
},
};
17 changes: 12 additions & 5 deletions src/plugins/dev_tools/public/dev_tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
* Side Public License, v 1.
*/

import { ReactNode } from 'react';
import { AppMount } from 'src/core/public';

/**
Expand All @@ -29,9 +28,14 @@ export class DevToolApp {
* This will be used as a label in the tab above the actual tool.
* May also be a ReactNode.
*/
public readonly title: ReactNode;
public readonly title: string;
public readonly mount: AppMount;

/**
* Mark the navigation tab as beta.
*/
public readonly isBeta?: boolean;

/**
* Flag indicating to disable the tab of this dev tool. Navigating to a
* disabled dev tool will be treated as the navigation to an unknown route
Expand All @@ -57,12 +61,13 @@ export class DevToolApp {

constructor(
id: string,
title: ReactNode,
title: string,
mount: AppMount,
enableRouting: boolean,
order: number,
toolTipContent = '',
disabled = false
disabled = false,
isBeta?: boolean
) {
this.id = id;
this.title = title;
Expand All @@ -71,6 +76,7 @@ export class DevToolApp {
this.order = order;
this.tooltipContent = toolTipContent;
this.disabled = disabled;
this.isBeta = isBeta;
}

public enable() {
Expand All @@ -94,5 +100,6 @@ export const createDevToolApp = ({
order,
tooltipContent,
disabled,
isBeta,
}: CreateDevToolArgs) =>
new DevToolApp(id, title, mount, enableRouting, order, tooltipContent, disabled);
new DevToolApp(id, title, mount, enableRouting, order, tooltipContent, disabled, isBeta);
4 changes: 4 additions & 0 deletions src/plugins/dev_tools/public/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@
flex-direction: column;
flex-grow: 1;
}

.devApp__tabBeta {
vertical-align: middle;
}
22 changes: 21 additions & 1 deletion src/plugins/dev_tools/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { sortBy } from 'lodash';
import { AppNavLinkStatus, DEFAULT_APP_CATEGORIES } from '../../../core/public';
import { UrlForwardingSetup } from '../../url_forwarding/public';
import { CreateDevToolArgs, DevToolApp, createDevToolApp } from './dev_tool';
import { DocTitleService, BreadcrumbService } from './services';

import './index.scss';

Expand All @@ -36,6 +37,9 @@ export class DevToolsPlugin implements Plugin<DevToolsSetup, void> {
private readonly devTools = new Map<string, DevToolApp>();
private appStateUpdater = new BehaviorSubject<AppUpdater>(() => ({}));

private breadcrumbService = new BreadcrumbService();
private docTitleService = new DocTitleService();

private getSortedDevTools(): readonly DevToolApp[] {
return sortBy([...this.devTools.values()], 'order');
}
Expand All @@ -59,8 +63,24 @@ export class DevToolsPlugin implements Plugin<DevToolsSetup, void> {
const [core] = await getStartServices();
const { application, chrome } = core;

this.docTitleService.setup(chrome.docTitle.change);
this.breadcrumbService.setup(chrome.setBreadcrumbs);

const appServices = {
breadcrumbService: this.breadcrumbService,
docTitleService: this.docTitleService,
};

const { renderApp } = await import('./application');
return renderApp(element, application, chrome, history, theme$, this.getSortedDevTools());
return renderApp(
element,
application,
chrome,
history,
theme$,
this.getSortedDevTools(),
appServices
);
},
});

Expand Down
32 changes: 32 additions & 0 deletions src/plugins/dev_tools/public/services/breadcrumb.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { ManagementAppMountParams } from '../../../management/public';
import { i18Texts } from '../constants/texts';

export type SetBreadcrumbs = ManagementAppMountParams['setBreadcrumbs'];

export class BreadcrumbService {
private setBreadcrumbsHandler?: SetBreadcrumbs;

public setup(setBreadcrumbsHandler: SetBreadcrumbs): void {
this.setBreadcrumbsHandler = setBreadcrumbsHandler;
}

public setBreadcrumbs(page: string): void {
if (!this.setBreadcrumbsHandler) {
throw new Error('Breadcrumb service has not been initialized');
}

if (!page || page === 'home') {
this.setBreadcrumbsHandler([{ text: i18Texts.breadcrumbs.home }]);
} else {
this.setBreadcrumbsHandler([{ text: i18Texts.breadcrumbs.home, href: '#/' }, { text: page }]);
}
}
}
31 changes: 31 additions & 0 deletions src/plugins/dev_tools/public/services/doc_title.ts
Original file line number Diff line number Diff line change
@@ -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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { i18Texts } from '../constants/texts';

type ChangeDocTitleHandler = (newTitle: string | string[]) => void;

export class DocTitleService {
private changeDocTitleHandler: ChangeDocTitleHandler = () => {};

public setup(_changeDocTitleHandler: ChangeDocTitleHandler): void {
this.changeDocTitleHandler = _changeDocTitleHandler;
}

public setTitle(page: string): void {
if (!this.changeDocTitleHandler) {
throw new Error('DocTitle service has not been initialized');
}

if (!page || page === 'home') {
this.changeDocTitleHandler(i18Texts.breadcrumbs.home);
} else {
this.changeDocTitleHandler(`${page} - ${i18Texts.breadcrumbs.home}`);
}
}
}
10 changes: 10 additions & 0 deletions src/plugins/dev_tools/public/services/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

export { DocTitleService } from './doc_title';
export { BreadcrumbService } from './breadcrumb';
3 changes: 2 additions & 1 deletion src/plugins/dev_tools/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"include": ["public/**/*"],
"references": [
{ "path": "../../core/tsconfig.json" },
{ "path": "../url_forwarding/tsconfig.json" }
{ "path": "../url_forwarding/tsconfig.json" },
{ "path": "../../../src/plugins/management/tsconfig.json" }
]
}
26 changes: 4 additions & 22 deletions x-pack/plugins/painless_lab/public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
* 2.0.
*/

import React from 'react';
import { first } from 'rxjs/operators';
import { EuiBetaBadge, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { Plugin, CoreSetup } from 'src/core/public';

Expand Down Expand Up @@ -46,26 +44,10 @@ export class PainlessLabUIPlugin implements Plugin<void, void, PluginDependencie
const devTool = devTools.register({
id: 'painless_lab',
order: 7,
title: (
<EuiFlexGroup gutterSize="s" alignItems="center" responsive={false}>
<EuiFlexItem grow={false}>
{i18n.translate('xpack.painlessLab.displayName', {
defaultMessage: 'Painless Lab',
})}
</EuiFlexItem>

<EuiFlexItem grow={false} className="painlessLab__betaLabelContainer">
<EuiBetaBadge
label={i18n.translate('xpack.painlessLab.displayNameBetaLabel', {
defaultMessage: 'Beta',
})}
tooltipContent={i18n.translate('xpack.painlessLab.displayNameBetaTooltipText', {
defaultMessage: 'This feature might change drastically in future releases',
})}
/>
</EuiFlexItem>
</EuiFlexGroup>
) as any,
isBeta: true,
title: i18n.translate('xpack.painlessLab.displayName', {
defaultMessage: 'Painless Lab',
}),
enableRouting: false,
disabled: false,
mount: async ({ element }) => {
Expand Down
4 changes: 0 additions & 4 deletions x-pack/plugins/painless_lab/public/styles/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,6 @@ $bottomBarHeight: $euiSize * 3;
}
}

.painlessLab__betaLabelContainer {
line-height: 0;
}

// adding dev tool top bar + bottom bar height to the body offset
$bodyOffset: $euiHeaderHeightCompensation + $bottomBarHeight;

Expand Down
Loading

0 comments on commit 617be51

Please sign in to comment.