adding loading$ observable (#48748) (#48909)

# Conflicts:
#	src/legacy/core_plugins/expressions/public/np_ready/public/loader.test.ts
#	src/legacy/core_plugins/expressions/public/np_ready/public/loader.ts
This commit is contained in:
Peter Pisljar 2019-10-25 22:34:47 +02:00 committed by Wylie Conlon
parent 1f6fd545de
commit d5c1c12d78
7 changed files with 61 additions and 14 deletions

View file

@ -26,6 +26,7 @@ import { IExpressionLoader, ExpressionLoader } from './loader';
// dom element which is provided by the component itself
export interface ExpressionRendererProps extends IExpressionLoaderParams {
className: string;
dataAttrs?: string[];
expression: string | ExpressionAST;
/**
* If an element is specified, but the response of the expression run can't be rendered
@ -39,6 +40,7 @@ export type ExpressionRenderer = React.FC<ExpressionRendererProps>;
export const createRenderer = (loader: IExpressionLoader): ExpressionRenderer => ({
className,
dataAttrs,
expression,
onRenderFailure,
...options
@ -68,5 +70,5 @@ export const createRenderer = (loader: IExpressionLoader): ExpressionRenderer =>
mountpoint.current,
]);
return <div className={className} ref={mountpoint} />;
return <div {...dataAttrs} className={className} ref={mountpoint} />;
};

View file

@ -86,6 +86,22 @@ describe('ExpressionDataHandler', () => {
expect(response).toEqual({ type: 'render', as: 'test' });
});
it('emits on loading$ when starting to load', async () => {
const expressionLoader = new ExpressionLoader(element, expressionString, {});
let loadingPromise = expressionLoader.loading$.pipe(first()).toPromise();
expressionLoader.update('test');
let response = await loadingPromise;
expect(response).toBeUndefined();
loadingPromise = expressionLoader.loading$.pipe(first()).toPromise();
expressionLoader.update('');
response = await loadingPromise;
expect(response).toBeUndefined();
loadingPromise = expressionLoader.loading$.pipe(first()).toPromise();
expressionLoader.update();
response = await loadingPromise;
expect(response).toBeUndefined();
});
it('emits on render$ when rendering is done', async () => {
const expressionLoader = new ExpressionLoader(element, expressionString, {});
const response = await expressionLoader.render$.pipe(first()).toPromise();

View file

@ -30,11 +30,14 @@ export class ExpressionLoader {
update$: ExpressionRenderHandler['update$'];
render$: ExpressionRenderHandler['render$'];
events$: ExpressionRenderHandler['events$'];
loading$: Observable<void>;
private dataHandler!: ExpressionDataHandler;
private renderHandler: ExpressionRenderHandler;
private dataSubject: Subject<Data>;
private loadingSubject: Subject<void>;
private data: Data;
private params: IExpressionLoaderParams;
constructor(
element: HTMLElement,
@ -44,6 +47,9 @@ export class ExpressionLoader {
this.dataSubject = new Subject();
this.data$ = this.dataSubject.asObservable().pipe(share());
this.loadingSubject = new Subject();
this.loading$ = this.loadingSubject.asObservable().pipe(share());
this.renderHandler = new ExpressionRenderHandler(element);
this.render$ = this.renderHandler.render$;
this.update$ = this.renderHandler.update$;
@ -57,8 +63,14 @@ export class ExpressionLoader {
this.render(data);
});
this.params = {
searchContext: { type: 'kibana_context' },
extraHandlers: params.extraHandlers,
};
this.dataHandler = new ExpressionDataHandler(expression, params);
this.dataHandler.getData().then(data => {
this.data = data;
this.dataSubject.next(data);
});
}
@ -91,11 +103,19 @@ export class ExpressionLoader {
return this.dataHandler.inspect();
}
update(expression: string | ExpressionAST, params: IExpressionLoaderParams): Promise<RenderId> {
update(expression?: string | ExpressionAST, params?: IExpressionLoaderParams): Promise<RenderId> {
const promise = this.render$.pipe(first()).toPromise();
if (params && params.searchContext && this.params.searchContext) {
this.params.searchContext = _.defaults(
{},
params.searchContext,
this.params.searchContext
) as any;
}
if (expression !== null) {
this.execute(expression, params);
this.loadingSubject.next();
if (expression) {
this.execute(expression, this.params);
} else {
this.render(this.data);
}
@ -110,13 +130,13 @@ export class ExpressionLoader {
this.dataHandler.cancel();
}
this.dataHandler = new ExpressionDataHandler(expression, params);
const data = await this.dataHandler.getData();
this.dataSubject.next(data);
return data;
this.data = await this.dataHandler.getData();
this.dataSubject.next(this.data);
return this.data;
};
private async render(data: Data): Promise<RenderId> {
return this.renderHandler.render(data);
return this.renderHandler.render(data, this.params.extraHandlers);
}
}

View file

@ -48,6 +48,9 @@ function createExpressionsStartMock(): ExpressionsStart {
execute: jest.fn(),
loader: jest.fn(),
render: jest.fn(),
ExpressionRenderHandler: jest.fn(),
ExpressionDataHandler: jest.fn(),
ExpressionLoader: jest.fn(),
};
}

View file

@ -35,9 +35,9 @@ import {
import { IInterpreter } from './types';
import { setInterpreter, setInspector, setRenderersRegistry } from './services';
import { createRenderer } from './expression_renderer';
import { loader } from './loader';
import { execute } from './execute';
import { render } from './render';
import { ExpressionLoader, loader } from './loader';
import { ExpressionDataHandler, execute } from './execute';
import { ExpressionRenderHandler, render } from './render';
export interface ExpressionsSetupDeps {
inspector: InspectorSetup;
@ -84,6 +84,9 @@ export class ExpressionsPublicPlugin
execute,
render,
loader,
ExpressionDataHandler,
ExpressionRenderHandler,
ExpressionLoader,
ExpressionRenderer,
};
}

View file

@ -23,6 +23,8 @@ import { share, first } from 'rxjs/operators';
import { event, RenderId, Data, IInterpreterRenderHandlers } from './types';
import { getRenderersRegistry } from './services';
export type IExpressionRendererExtraHandlers = Record<string, any>;
export class ExpressionRenderHandler {
render$: Observable<RenderId>;
update$: Observable<any>;
@ -65,8 +67,8 @@ export class ExpressionRenderHandler {
};
}
render = async (data: Data) => {
if (data.type !== 'render' || !data.as) {
render = async (data: Data, extraHandlers: IExpressionRendererExtraHandlers = {}) => {
if (!data || data.type !== 'render' || !data.as) {
throw new Error('invalid data provided to expression renderer');
}
@ -78,7 +80,7 @@ export class ExpressionRenderHandler {
getRenderersRegistry()
.get(data.as)
.render(this.element, data.value, this.handlers);
.render(this.element, data.value, { ...this.handlers, ...extraHandlers });
return promise;
};

View file

@ -46,6 +46,7 @@ export interface IExpressionLoaderParams {
disableCaching?: boolean;
customFunctions?: [];
customRenderers?: [];
extraHandlers?: Record<string, any>;
}
export interface IInterpreterHandlers {