Skip to content

Commit

Permalink
feat: 🎸 add render complete dispatcher to embeddable
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich committed Aug 6, 2020
1 parent 0fc88a8 commit 3093538
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 49 deletions.
3 changes: 2 additions & 1 deletion src/plugins/embeddable/kibana.json
Expand Up @@ -12,6 +12,7 @@
],
"requiredBundles": [
"savedObjects",
"kibanaReact"
"kibanaReact",
"kibanaUtils"
]
}
7 changes: 6 additions & 1 deletion src/plugins/embeddable/public/lib/embeddables/embeddable.tsx
Expand Up @@ -19,6 +19,7 @@

import { cloneDeep, isEqual } from 'lodash';
import * as Rx from 'rxjs';
import { RenderCompleteDispatcher } from '../../../../kibana_utils/public';
import { Adapters, ViewMode } from '../types';
import { IContainer } from '../containers';
import { EmbeddableInput, EmbeddableOutput, IEmbeddable } from './i_embeddable';
Expand Down Expand Up @@ -47,6 +48,8 @@ export abstract class Embeddable<
private readonly input$: Rx.BehaviorSubject<TEmbeddableInput>;
private readonly output$: Rx.BehaviorSubject<TEmbeddableOutput>;

protected renderComplete = new RenderCompleteDispatcher();

// Listener to parent changes, if this embeddable exists in a parent, in order
// to update input when the parent changes.
private parentSubscription?: Rx.Subscription;
Expand Down Expand Up @@ -133,7 +136,9 @@ export abstract class Embeddable<
}
}

public render(domNode: HTMLElement | Element): void {
public render(domNode: HTMLElement): void {
this.renderComplete.setEl(domNode);

if (this.destroyed) {
throw new Error('Embeddable has been destroyed');
}
Expand Down
17 changes: 0 additions & 17 deletions src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts
Expand Up @@ -197,21 +197,4 @@ export interface IEmbeddable<
* List of triggers that this embeddable will execute.
*/
supportedTriggers(): Array<keyof TriggerContextMapping>;

/**
* Should emit `true` when embeddable has finished loading its data and has
* completely rendered. Should emit `false` when when embeddable is loading data
* again. At the start it is assumed that embeddable is has not completed
* rendering, so this embeddable has to emit `true` at least once.
*
* This is used for reporting to know that embeddable is ready, so
* it can take a screenshot. It is also used in functional tests to know that
* page has stabilized.
*/
renderComplete$?: Observable<boolean>;

/**
* Use this hook to report when error happened during rendering.
*/
renderError$?: Observable<Error>;
}
Expand Up @@ -29,25 +29,50 @@ export function dispatchRenderStart(el: HTMLElement) {
dispatchEvent(el, 'renderStart');
}

export class RenderComplete {
constructor(private readonly el: HTMLElement) {}
/**
* Should call `dispatchComplete()` when UI block has finished loading its data and has
* completely rendered. Should `dispatchInProgress()` every time UI block
* starts loading data again. At the start it is assumed that UI block is loading
* so it dispatches "in progress" automatically, so you need to call `setRenderComplete`
* at least once.
*
* This is used for reporting to know that UI block is ready, so
* it can take a screenshot. It is also used in functional tests to know that
* page has stabilized.
*/
export class RenderCompleteDispatcher {
private count: number = 0;
private el?: HTMLElement;

constructor(el?: HTMLElement) {
this.setEl(el);
}

public setEl(el?: HTMLElement) {
this.el = el;
this.count = 0;
if (el) this.dispatchInProgress();
}

public readonly dispatchInProgress = () => {
public dispatchInProgress() {
if (!this.el) return;
this.el.setAttribute('data-render-complete', 'false');
this.el.setAttribute('data-rendering-count', String(this.count));
dispatchRenderStart(this.el);
};
}

public readonly dispatchComplete = (count: number) => {
public dispatchComplete() {
if (!this.el) return;
this.count++;
this.el.setAttribute('data-render-complete', 'true');
this.el.setAttribute('data-rendering-count', count.toString());
this.el.setAttribute('data-rendering-count', String(this.count));
dispatchRenderComplete(this.el);
};
}

public readonly dispatchError = () => {
this.el.setAttribute(
'data-rendering-count',
String(Number(this.el.getAttribute('data-rendering-count')) + 1)
);
public dispatchError() {
if (!this.el) return;
this.count++;
this.el.setAttribute('data-render-complete', 'false');
};
this.el.setAttribute('data-rendering-count', String(this.count));
}
}
Expand Up @@ -36,7 +36,6 @@ import {
IContainer,
Adapters,
} from '../../../../plugins/embeddable/public';
import { dispatchRenderComplete } from '../../../../plugins/kibana_utils/public';
import {
IExpressionLoaderParams,
ExpressionsStart,
Expand Down Expand Up @@ -97,9 +96,6 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
private readonly deps: VisualizeEmbeddableFactoryDeps;
private readonly inspectorAdapters?: Adapters;

protected renderComplete$ = new Rx.Subject<boolean>();
protected renderError$ = new Rx.Subject<Error>();

constructor(
timefilter: TimefilterContract,
{ vis, editPath, editUrl, indexPatterns, editable, deps }: VisualizeEmbeddableConfiguration,
Expand Down Expand Up @@ -248,26 +244,20 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
hasInspector = () => Boolean(this.getInspectorAdapters());

onContainerLoading = () => {
this.domNode.setAttribute('data-render-complete', 'false');
this.renderComplete.dispatchInProgress();
this.updateOutput({ loading: true, error: undefined });
};

onContainerRender = (count: number) => {
this.domNode.setAttribute('data-render-complete', 'true');
this.domNode.setAttribute('data-rendering-count', count.toString());
onContainerRender = () => {
this.renderComplete.dispatchComplete();
this.updateOutput({ loading: false, error: undefined });
dispatchRenderComplete(this.domNode);
};

onContainerError = (error: ExpressionRenderError) => {
if (this.abortController) {
this.abortController.abort();
}
this.domNode.setAttribute(
'data-rendering-count',
this.domNode.getAttribute('data-rendering-count') + 1
);
this.domNode.setAttribute('data-render-complete', 'false');
this.renderComplete.dispatchError();
this.updateOutput({ loading: false, error });
};

Expand All @@ -287,6 +277,7 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
domNode.appendChild(div);

this.domNode = div;
this.renderComplete.setEl(div);

const expressions = getExpressions();
this.handler = new expressions.ExpressionLoader(this.domNode, undefined, {
Expand Down Expand Up @@ -334,11 +325,8 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut

div.setAttribute('data-test-subj', 'visualizationLoader');
div.setAttribute('data-shared-item', '');
div.setAttribute('data-rendering-count', '0');
div.setAttribute('data-render-complete', 'false');

this.subscriptions.push(this.handler.loading$.subscribe(this.onContainerLoading));

this.subscriptions.push(this.handler.render$.subscribe(this.onContainerRender));

this.updateHandler();
Expand Down

0 comments on commit 3093538

Please sign in to comment.