Skip to content

Commit

Permalink
[Canvas] Fix nav link behavior in Canvas (#65590)
Browse files Browse the repository at this point in the history
* wip

* Storing last page for canvas in session storage

* Fix bad path

* Fix bad merge

* Cleanup and adding some types

* Fixing types

* PR feedback and storage refactor

Co-authored-by: Corey Robertson <corey.robertson@elastic.co>
  • Loading branch information
poffdeluxe and Corey Robertson committed May 7, 2020
1 parent 50e0e2f commit 51fcdbb
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 30 deletions.
2 changes: 1 addition & 1 deletion x-pack/plugins/canvas/common/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const API_ROUTE_WORKPAD_STRUCTURES = `${API_ROUTE}/workpad-structures`;
export const API_ROUTE_CUSTOM_ELEMENT = `${API_ROUTE}/custom-element`;
export const LOCALSTORAGE_PREFIX = `kibana.canvas`;
export const LOCALSTORAGE_CLIPBOARD = `${LOCALSTORAGE_PREFIX}.clipboard`;
export const LOCALSTORAGE_LASTPAGE = 'canvas:lastpage';
export const SESSIONSTORAGE_LASTPATH = 'lastPath:canvas';
export const FETCH_TIMEOUT = 30000; // 30 seconds
export const CANVAS_USAGE_TYPE = 'canvas';
export const DEFAULT_WORKPAD_CSS = '.canvasPage {\n\n}';
Expand Down
8 changes: 5 additions & 3 deletions x-pack/plugins/canvas/public/application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import ReactDOM from 'react-dom';
import { I18nProvider } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { Provider } from 'react-redux';
import { BehaviorSubject } from 'rxjs';

import { AppMountParameters, CoreStart, CoreSetup } from 'kibana/public';
import { AppMountParameters, CoreStart, CoreSetup, AppUpdater } from 'kibana/public';

import { CanvasStartDeps, CanvasSetupDeps } from './plugin';
// @ts-ignore Untyped local
Expand Down Expand Up @@ -88,9 +89,10 @@ export const initializeCanvas = async (
coreStart: CoreStart,
setupPlugins: CanvasSetupDeps,
startPlugins: CanvasStartDeps,
registries: SetupRegistries
registries: SetupRegistries,
appUpdater: BehaviorSubject<AppUpdater>
) => {
startServices(coreSetup, coreStart, setupPlugins, startPlugins);
startServices(coreSetup, coreStart, setupPlugins, startPlugins, appUpdater);

// Create Store
const canvasStore = await createStore(coreSetup, setupPlugins);
Expand Down
6 changes: 4 additions & 2 deletions x-pack/plugins/canvas/public/components/app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { connect } from 'react-redux';
import { compose, withProps } from 'recompose';
import { getAppReady, getBasePath } from '../../state/selectors/app';
import { appReady, appError } from '../../state/actions/app';
import { withKibana } from '../../../../../../src/plugins/kibana_react/public';

import { App as Component } from './app';

Expand Down Expand Up @@ -44,7 +45,8 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {

export const App = compose(
connect(mapStateToProps, mapDispatchToProps, mergeProps),
withProps(() => ({
onRouteChange: () => undefined,
withKibana,
withProps(props => ({
onRouteChange: props.kibana.services.canvas.navLink.updatePath,
}))
)(Component);
17 changes: 3 additions & 14 deletions x-pack/plugins/canvas/public/lib/clipboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { Storage } from '../../../../../src/plugins/kibana_utils/public';
import { LOCALSTORAGE_CLIPBOARD } from '../../common/lib/constants';
import { getWindow } from './get_window';

let storage: Storage;

const getStorage = (): Storage => {
if (!storage) {
storage = new Storage(getWindow().localStorage);
}

return storage;
};
import { getLocalStorage } from './storage';

export const setClipboardData = (data: any) => {
getStorage().set(LOCALSTORAGE_CLIPBOARD, JSON.stringify(data));
getLocalStorage().set(LOCALSTORAGE_CLIPBOARD, JSON.stringify(data));
};

export const getClipboardData = () => getStorage().get(LOCALSTORAGE_CLIPBOARD);
export const getClipboardData = () => getLocalStorage().get(LOCALSTORAGE_CLIPBOARD);
12 changes: 10 additions & 2 deletions x-pack/plugins/canvas/public/lib/get_window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,18 @@
*/

// return window if it exists, otherwise just return an object literal
const windowObj = { location: null, localStorage: {} as Window['localStorage'] };
const windowObj = {
location: null,
localStorage: {} as Window['localStorage'],
sessionStorage: {} as Window['sessionStorage'],
};

export const getWindow = ():
| Window
| { location: Location | null; localStorage: Window['localStorage'] } => {
| {
location: Location | null;
localStorage: Window['localStorage'];
sessionStorage: Window['sessionStorage'];
} => {
return typeof window === 'undefined' ? windowObj : window;
};
35 changes: 35 additions & 0 deletions x-pack/plugins/canvas/public/lib/storage.ts
Original file line number Diff line number Diff line change
@@ -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;
* you may not use this file except in compliance with the Elastic License.
*/

import { Storage } from '../../../../../src/plugins/kibana_utils/public';
import { getWindow } from './get_window';

export enum StorageType {
Local = 'localStorage',
Session = 'sessionStorage',
}

const storages: {
[x in StorageType]: Storage | null;
} = {
[StorageType.Local]: null,
[StorageType.Session]: null,
};

const getStorage = (type: StorageType): Storage => {
const storage = storages[type] || new Storage(getWindow()[type]);
storages[type] = storage;

return storage;
};

export const getLocalStorage = (): Storage => {
return getStorage(StorageType.Local);
};

export const getSessionStorage = (): Storage => {
return getStorage(StorageType.Session);
};
23 changes: 22 additions & 1 deletion x-pack/plugins/canvas/public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { BehaviorSubject } from 'rxjs';
import {
CoreSetup,
CoreStart,
Plugin,
AppMountParameters,
AppUpdater,
DEFAULT_APP_CATEGORIES,
} from '../../../../src/core/public';
import { HomePublicPluginSetup } from '../../../../src/plugins/home/public';
import { initLoadingIndicator } from './lib/loading_indicator';
import { getSessionStorage } from './lib/storage';
import { SESSIONSTORAGE_LASTPATH } from '../common/lib/constants';
import { featureCatalogueEntry } from './feature_catalogue_entry';
import { ExpressionsSetup, ExpressionsStart } from '../../../../src/plugins/expressions/public';
import { DataPublicPluginSetup } from '../../../../src/plugins/data/public';
Expand Down Expand Up @@ -60,6 +64,7 @@ export type CanvasStart = void;
/** @internal */
export class CanvasPlugin
implements Plugin<CanvasSetup, CanvasStart, CanvasSetupDeps, CanvasStartDeps> {
private appUpdater = new BehaviorSubject<AppUpdater>(() => ({}));
// TODO: Do we want to completely move canvas_plugin_src into it's own plugin?
private srcPlugin = new CanvasSrcPlugin();

Expand All @@ -68,20 +73,36 @@ export class CanvasPlugin

this.srcPlugin.setup(core, { canvas: canvasApi });

// Set the nav link to the last saved url if we have one in storage
const lastUrl = getSessionStorage().get(SESSIONSTORAGE_LASTPATH);
if (lastUrl) {
this.appUpdater.next(() => ({
defaultPath: `#${lastUrl}`,
}));
}

core.application.register({
category: DEFAULT_APP_CATEGORIES.kibana,
id: 'canvas',
title: 'Canvas',
euiIconType: 'canvasApp',
order: 3000,
updater$: this.appUpdater,
mount: async (params: AppMountParameters) => {
// Load application bundle
const { renderApp, initializeCanvas, teardownCanvas } = await import('./application');

// Get start services
const [coreStart, depsStart] = await core.getStartServices();

const canvasStore = await initializeCanvas(core, coreStart, plugins, depsStart, registries);
const canvasStore = await initializeCanvas(
core,
coreStart,
plugins,
depsStart,
registries,
this.appUpdater
);

const unmount = renderApp(coreStart, depsStart, params, canvasStore);

Expand Down
32 changes: 25 additions & 7 deletions x-pack/plugins/canvas/public/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { CoreSetup, CoreStart } from '../../../../../src/core/public';
import { BehaviorSubject } from 'rxjs';
import { CoreSetup, CoreStart, AppUpdater } from '../../../../../src/core/public';
import { CanvasSetupDeps, CanvasStartDeps } from '../plugin';
import { notifyServiceFactory } from './notify';
import { platformServiceFactory } from './platform';
import { navLinkServiceFactory } from './nav_link';

export type CanvasServiceFactory<Service> = (
coreSetup: CoreSetup,
coreStart: CoreStart,
canvasSetupPlugins: CanvasSetupDeps,
canvasStartPlugins: CanvasStartDeps
canvasStartPlugins: CanvasStartDeps,
appUpdater: BehaviorSubject<AppUpdater>
) => Service;

class CanvasServiceProvider<Service> {
Expand All @@ -28,9 +31,16 @@ class CanvasServiceProvider<Service> {
coreSetup: CoreSetup,
coreStart: CoreStart,
canvasSetupPlugins: CanvasSetupDeps,
canvasStartPlugins: CanvasStartDeps
canvasStartPlugins: CanvasStartDeps,
appUpdater: BehaviorSubject<AppUpdater>
) {
this.service = this.factory(coreSetup, coreStart, canvasSetupPlugins, canvasStartPlugins);
this.service = this.factory(
coreSetup,
coreStart,
canvasSetupPlugins,
canvasStartPlugins,
appUpdater
);
}

getService(): Service {
Expand All @@ -51,25 +61,33 @@ export type ServiceFromProvider<P> = P extends CanvasServiceProvider<infer T> ?
export const services = {
notify: new CanvasServiceProvider(notifyServiceFactory),
platform: new CanvasServiceProvider(platformServiceFactory),
navLink: new CanvasServiceProvider(navLinkServiceFactory),
};

export interface CanvasServices {
notify: ServiceFromProvider<typeof services.notify>;
platform: ServiceFromProvider<typeof services.platform>;
navLink: ServiceFromProvider<typeof services.navLink>;
}

export const startServices = (
coreSetup: CoreSetup,
coreStart: CoreStart,
canvasSetupPlugins: CanvasSetupDeps,
canvasStartPlugins: CanvasStartDeps
canvasStartPlugins: CanvasStartDeps,
appUpdater: BehaviorSubject<AppUpdater>
) => {
Object.entries(services).forEach(([key, provider]) =>
provider.start(coreSetup, coreStart, canvasSetupPlugins, canvasStartPlugins)
provider.start(coreSetup, coreStart, canvasSetupPlugins, canvasStartPlugins, appUpdater)
);
};

export const stopServices = () => {
Object.entries(services).forEach(([key, provider]) => provider.stop());
};

export const { notify: notifyService, platform: platformService } = services;
export const {
notify: notifyService,
platform: platformService,
navLink: navLinkService,
} = services;
31 changes: 31 additions & 0 deletions x-pack/plugins/canvas/public/services/nav_link.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;
* you may not use this file except in compliance with the Elastic License.
*/

import { CanvasServiceFactory } from '.';
import { SESSIONSTORAGE_LASTPATH } from '../../common/lib/constants';
import { getSessionStorage } from '../lib/storage';

interface NavLinkService {
updatePath: (path: string) => void;
}

export const navLinkServiceFactory: CanvasServiceFactory<NavLinkService> = (
coreSetup,
coreStart,
setupPlugins,
startPlugins,
appUpdater
) => {
return {
updatePath: (path: string) => {
appUpdater.next(() => ({
defaultPath: `#${path}`,
}));

getSessionStorage().set(SESSIONSTORAGE_LASTPATH, path);
},
};
};

0 comments on commit 51fcdbb

Please sign in to comment.