forked from elastic/kibana
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[dashboard] fix time_to_data does not capture entire client-side rend…
…ering (elastic#200640) Closes elastic#194489 PR adds new `PublishesRendered` interface. Embeddables can implement this interface to provide feedback when rendering is complete. PR updates ReactEmbeddableRender phase tracking logic to include check for `PublishesRendered` value when interface is implemented. --------- Co-authored-by: Elastic Machine <[email protected]>
- Loading branch information
1 parent
b6586a9
commit cdeb1e9
Showing
7 changed files
with
179 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 20 additions & 0 deletions
20
packages/presentation/presentation_publishing/interfaces/publishes_rendered.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/* | ||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public | ||
* License v3.0 only", or the "Server Side Public License, v 1". | ||
*/ | ||
|
||
import { PublishingSubject } from '../publishing_subject'; | ||
|
||
export interface PublishesRendered { | ||
rendered$: PublishingSubject<boolean>; | ||
} | ||
|
||
export const apiPublishesRendered = ( | ||
unknownApi: null | unknown | ||
): unknownApi is PublishesRendered => { | ||
return Boolean(unknownApi && (unknownApi as PublishesRendered)?.rendered$ !== undefined); | ||
}; |
100 changes: 100 additions & 0 deletions
100
src/plugins/embeddable/public/react_embeddable_system/phase_tracker.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
/* | ||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public | ||
* License v3.0 only", or the "Server Side Public License, v 1". | ||
*/ | ||
|
||
import { BehaviorSubject, skip } from 'rxjs'; | ||
import { PhaseTracker } from './phase_tracker'; | ||
|
||
describe('PhaseTracker', () => { | ||
describe('api does not implement PublishesDataLoading or PublishesRendered', () => { | ||
test(`should emit 'rendered' event`, (done) => { | ||
const phaseTracker = new PhaseTracker(); | ||
phaseTracker | ||
.getPhase$() | ||
.pipe(skip(1)) | ||
.subscribe((phaseEvent) => { | ||
expect(phaseEvent?.status).toBe('rendered'); | ||
done(); | ||
}); | ||
phaseTracker.trackPhaseEvents('1', {}); | ||
}); | ||
}); | ||
|
||
describe('api implements PublishesDataLoading', () => { | ||
test(`should emit 'loading' event when dataLoading is true`, (done) => { | ||
const phaseTracker = new PhaseTracker(); | ||
phaseTracker | ||
.getPhase$() | ||
.pipe(skip(1)) | ||
.subscribe((phaseEvent) => { | ||
expect(phaseEvent?.status).toBe('loading'); | ||
done(); | ||
}); | ||
phaseTracker.trackPhaseEvents('1', { dataLoading: new BehaviorSubject(true) }); | ||
}); | ||
|
||
test(`should emit 'rendered' event when dataLoading is false`, (done) => { | ||
const phaseTracker = new PhaseTracker(); | ||
phaseTracker | ||
.getPhase$() | ||
.pipe(skip(1)) | ||
.subscribe((phaseEvent) => { | ||
expect(phaseEvent?.status).toBe('rendered'); | ||
done(); | ||
}); | ||
phaseTracker.trackPhaseEvents('1', { dataLoading: new BehaviorSubject(false) }); | ||
}); | ||
}); | ||
|
||
describe('api implements PublishesDataLoading and PublishesRendered', () => { | ||
test(`should emit 'loading' event when dataLoading is true`, (done) => { | ||
const phaseTracker = new PhaseTracker(); | ||
phaseTracker | ||
.getPhase$() | ||
.pipe(skip(1)) | ||
.subscribe((phaseEvent) => { | ||
expect(phaseEvent?.status).toBe('loading'); | ||
done(); | ||
}); | ||
phaseTracker.trackPhaseEvents('1', { | ||
dataLoading: new BehaviorSubject(true), | ||
rendered$: new BehaviorSubject(false), | ||
}); | ||
}); | ||
|
||
test(`should emit 'loading' event when dataLoading is false but rendered is false`, (done) => { | ||
const phaseTracker = new PhaseTracker(); | ||
phaseTracker | ||
.getPhase$() | ||
.pipe(skip(1)) | ||
.subscribe((phaseEvent) => { | ||
expect(phaseEvent?.status).toBe('loading'); | ||
done(); | ||
}); | ||
phaseTracker.trackPhaseEvents('1', { | ||
dataLoading: new BehaviorSubject(false), | ||
rendered$: new BehaviorSubject(false), | ||
}); | ||
}); | ||
|
||
test(`should emit 'rendered' event only when rendered is true`, (done) => { | ||
const phaseTracker = new PhaseTracker(); | ||
phaseTracker | ||
.getPhase$() | ||
.pipe(skip(1)) | ||
.subscribe((phaseEvent) => { | ||
expect(phaseEvent?.status).toBe('rendered'); | ||
done(); | ||
}); | ||
phaseTracker.trackPhaseEvents('1', { | ||
dataLoading: new BehaviorSubject(false), | ||
rendered$: new BehaviorSubject(true), | ||
}); | ||
}); | ||
}); | ||
}); |
48 changes: 48 additions & 0 deletions
48
src/plugins/embeddable/public/react_embeddable_system/phase_tracker.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/* | ||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public | ||
* License v3.0 only", or the "Server Side Public License, v 1". | ||
*/ | ||
|
||
import { | ||
PhaseEvent, | ||
apiPublishesDataLoading, | ||
apiPublishesRendered, | ||
} from '@kbn/presentation-publishing'; | ||
import { BehaviorSubject, Subscription, combineLatest } from 'rxjs'; | ||
|
||
export class PhaseTracker { | ||
private firstLoadCompleteTime: number | undefined; | ||
private embeddableStartTime = performance.now(); | ||
private subscriptions = new Subscription(); | ||
private phase$ = new BehaviorSubject<PhaseEvent | undefined>(undefined); | ||
|
||
getPhase$() { | ||
return this.phase$; | ||
} | ||
|
||
public trackPhaseEvents(uuid: string, api: unknown) { | ||
const dataLoading$ = apiPublishesDataLoading(api) | ||
? api.dataLoading | ||
: new BehaviorSubject(false); | ||
const rendered$ = apiPublishesRendered(api) ? api.rendered$ : new BehaviorSubject(true); | ||
|
||
this.subscriptions.add( | ||
combineLatest([dataLoading$, rendered$]).subscribe(([dataLoading, rendered]) => { | ||
if (!this.firstLoadCompleteTime) { | ||
this.firstLoadCompleteTime = performance.now(); | ||
} | ||
const duration = this.firstLoadCompleteTime - this.embeddableStartTime; | ||
const status = dataLoading || !rendered ? 'loading' : 'rendered'; | ||
this.phase$.next({ id: uuid, status, timeToEvent: duration }); | ||
}) | ||
); | ||
} | ||
|
||
public cleanup() { | ||
this.subscriptions.unsubscribe(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters