@thoughtspot/visual-embed-sdk 1.52.2-beta.0 → 1.52.2-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/package.json +1 -1
- package/cjs/src/embed/liveboard.d.ts +13 -15
- package/cjs/src/embed/liveboard.d.ts.map +1 -1
- package/cjs/src/embed/liveboard.js +31 -3
- package/cjs/src/embed/liveboard.js.map +1 -1
- package/cjs/src/embed/liveboard.spec.js +79 -0
- package/cjs/src/embed/liveboard.spec.js.map +1 -1
- package/cjs/src/embed/ts-embed.spec.d.ts.map +1 -1
- package/cjs/src/embed/ts-embed.spec.js +10 -15
- package/cjs/src/embed/ts-embed.spec.js.map +1 -1
- package/dist/{index-BrJMbt8r.js → index-CnHREfvN.js} +1 -1
- package/dist/src/embed/liveboard.d.ts +13 -15
- package/dist/src/embed/liveboard.d.ts.map +1 -1
- package/dist/src/embed/ts-embed.spec.d.ts.map +1 -1
- package/dist/tsembed-react.es.js +72 -6
- package/dist/tsembed-react.js +71 -5
- package/dist/tsembed.es.js +72 -6
- package/dist/tsembed.js +71 -5
- package/dist/visual-embed-sdk-react-full.d.ts +13 -0
- package/dist/visual-embed-sdk-react.d.ts +13 -0
- package/dist/visual-embed-sdk.d.ts +13 -15
- package/lib/package.json +1 -1
- package/lib/src/embed/liveboard.d.ts +13 -15
- package/lib/src/embed/liveboard.d.ts.map +1 -1
- package/lib/src/embed/liveboard.js +30 -3
- package/lib/src/embed/liveboard.js.map +1 -1
- package/lib/src/embed/liveboard.spec.js +79 -0
- package/lib/src/embed/liveboard.spec.js.map +1 -1
- package/lib/src/embed/ts-embed.spec.d.ts.map +1 -1
- package/lib/src/embed/ts-embed.spec.js +10 -15
- package/lib/src/embed/ts-embed.spec.js.map +1 -1
- package/lib/src/visual-embed-sdk.d.ts +13 -15
- package/package.json +1 -1
- package/src/embed/liveboard.spec.ts +92 -0
- package/src/embed/liveboard.ts +36 -3
- package/src/embed/ts-embed.spec.ts +11 -18
|
@@ -15672,21 +15672,6 @@ export interface LiveboardViewConfig extends BaseViewConfig, FullHeightViewConfi
|
|
|
15672
15672
|
*/
|
|
15673
15673
|
updatedSpotterExperience?: boolean;
|
|
15674
15674
|
}
|
|
15675
|
-
/**
|
|
15676
|
-
* Embed a ThoughtSpot Liveboard or visualization. When rendered it already
|
|
15677
|
-
* waits for the authentication to complete, so you need not wait for
|
|
15678
|
-
* `AuthStatus.SUCCESS`.
|
|
15679
|
-
* @group Embed components
|
|
15680
|
-
* @example
|
|
15681
|
-
* ```js
|
|
15682
|
-
* import { .. } from '@thoughtspot/visual-embed-sdk';
|
|
15683
|
-
* init({ ... });
|
|
15684
|
-
* const embed = new LiveboardEmbed("#container", {
|
|
15685
|
-
* liveboardId: <your-id-here>,
|
|
15686
|
-
* // .. other params here.
|
|
15687
|
-
* })
|
|
15688
|
-
* ```
|
|
15689
|
-
*/
|
|
15690
15675
|
export class LiveboardEmbed extends V1Embed {
|
|
15691
15676
|
protected viewConfig: LiveboardViewConfig;
|
|
15692
15677
|
private readonly fullHeightController?;
|
|
@@ -15718,6 +15703,19 @@ export class LiveboardEmbed extends V1Embed {
|
|
|
15718
15703
|
personalizedViewId: string;
|
|
15719
15704
|
};
|
|
15720
15705
|
protected beforePrerenderVisible(): void;
|
|
15706
|
+
/**
|
|
15707
|
+
* Whether `previous` was showing what this instance is about to show.
|
|
15708
|
+
*
|
|
15709
|
+
* Undefined-valued keys are dropped before comparing. `navigateToLiveboard`
|
|
15710
|
+
* assigns `vizId`, `activeTabId` and `personalizedViewId` unconditionally,
|
|
15711
|
+
* so an instance that has been shown once carries those keys as `undefined`
|
|
15712
|
+
* while a freshly constructed one does not have them at all. `isEqual`
|
|
15713
|
+
* counts own keys, so without this the comparison can never be true after
|
|
15714
|
+
* the first show — which is the whole case this exists for.
|
|
15715
|
+
*
|
|
15716
|
+
* Callbacks compare as equal: React passes a fresh closure per render.
|
|
15717
|
+
*/
|
|
15718
|
+
private isSameEmbedConfig;
|
|
15721
15719
|
private isShowingLiveboardRoute;
|
|
15722
15720
|
protected handleRenderForPrerender(): Promise<TsEmbed>;
|
|
15723
15721
|
/**
|
package/package.json
CHANGED
|
@@ -2748,6 +2748,98 @@ describe('Liveboard/viz embed tests', () => {
|
|
|
2748
2748
|
expect(navigateToLiveboardSpy).toHaveBeenCalled();
|
|
2749
2749
|
});
|
|
2750
2750
|
|
|
2751
|
+
test('should not route via home when a new instance shows an equal config', async () => {
|
|
2752
|
+
const viewConfig = {
|
|
2753
|
+
liveboardId,
|
|
2754
|
+
vizId,
|
|
2755
|
+
activeTabId,
|
|
2756
|
+
...defaultViewConfig,
|
|
2757
|
+
};
|
|
2758
|
+
const previous = new LiveboardEmbed(getRootEl(), { ...viewConfig, beforeRender: () => {} } as any);
|
|
2759
|
+
const liveboardEmbed = new LiveboardEmbed(getRootEl(), { ...viewConfig, beforeRender: () => {} } as any);
|
|
2760
|
+
|
|
2761
|
+
// React constructs a new embed on every mount.
|
|
2762
|
+
jest.spyOn(liveboardEmbed as any, 'getPreRenderObj').mockReturnValue(previous);
|
|
2763
|
+
const triggerSpy = jest
|
|
2764
|
+
.spyOn(liveboardEmbed, 'trigger')
|
|
2765
|
+
.mockImplementation(() => Promise.resolve(undefined as any));
|
|
2766
|
+
const navigateToLiveboardSpy = jest
|
|
2767
|
+
.spyOn(liveboardEmbed, 'navigateToLiveboard')
|
|
2768
|
+
.mockImplementation(() => Promise.resolve(undefined));
|
|
2769
|
+
|
|
2770
|
+
liveboardEmbed.isEmbedContainerLoaded = false;
|
|
2771
|
+
liveboardEmbed['beforePrerenderVisible']();
|
|
2772
|
+
liveboardEmbed.isEmbedContainerLoaded = true;
|
|
2773
|
+
liveboardEmbed['executeEmbedContainerReadyCallbacks']();
|
|
2774
|
+
await waitForPreRenderNavigate();
|
|
2775
|
+
|
|
2776
|
+
expect(triggerSpy).not.toHaveBeenCalledWith(HostEvent.Navigate, 'home');
|
|
2777
|
+
expect(navigateToLiveboardSpy).toHaveBeenCalled();
|
|
2778
|
+
});
|
|
2779
|
+
|
|
2780
|
+
test('should not route via home when the previous instance has been shown once', async () => {
|
|
2781
|
+
// navigateToLiveboard assigns vizId/activeTabId/personalizedViewId
|
|
2782
|
+
// unconditionally, so a shown instance carries them as undefined
|
|
2783
|
+
// while a fresh one lacks the keys entirely. Comparing raw objects
|
|
2784
|
+
// counts own keys, so it never matches again.
|
|
2785
|
+
const viewConfig = { liveboardId, ...defaultViewConfig };
|
|
2786
|
+
const previous = new LiveboardEmbed(getRootEl(), { ...viewConfig } as any);
|
|
2787
|
+
(previous as any).viewConfig.vizId = undefined;
|
|
2788
|
+
(previous as any).viewConfig.activeTabId = undefined;
|
|
2789
|
+
(previous as any).viewConfig.personalizedViewId = undefined;
|
|
2790
|
+
|
|
2791
|
+
const liveboardEmbed = new LiveboardEmbed(getRootEl(), { ...viewConfig } as any);
|
|
2792
|
+
|
|
2793
|
+
jest.spyOn(liveboardEmbed as any, 'getPreRenderObj').mockReturnValue(previous);
|
|
2794
|
+
const triggerSpy = jest
|
|
2795
|
+
.spyOn(liveboardEmbed, 'trigger')
|
|
2796
|
+
.mockImplementation(() => Promise.resolve(undefined as any));
|
|
2797
|
+
jest
|
|
2798
|
+
.spyOn(liveboardEmbed, 'navigateToLiveboard')
|
|
2799
|
+
.mockImplementation(() => Promise.resolve(undefined));
|
|
2800
|
+
|
|
2801
|
+
liveboardEmbed.isEmbedContainerLoaded = false;
|
|
2802
|
+
liveboardEmbed['beforePrerenderVisible']();
|
|
2803
|
+
liveboardEmbed.isEmbedContainerLoaded = true;
|
|
2804
|
+
liveboardEmbed['executeEmbedContainerReadyCallbacks']();
|
|
2805
|
+
await waitForPreRenderNavigate();
|
|
2806
|
+
|
|
2807
|
+
expect(triggerSpy).not.toHaveBeenCalledWith(HostEvent.Navigate, 'home');
|
|
2808
|
+
});
|
|
2809
|
+
|
|
2810
|
+
test('should route via home when a new instance shows the same route with other filters', async () => {
|
|
2811
|
+
const viewConfig = {
|
|
2812
|
+
liveboardId,
|
|
2813
|
+
vizId,
|
|
2814
|
+
activeTabId,
|
|
2815
|
+
...defaultViewConfig,
|
|
2816
|
+
};
|
|
2817
|
+
const previous = new LiveboardEmbed(getRootEl(), {
|
|
2818
|
+
...viewConfig,
|
|
2819
|
+
runtimeFilters: [{ columnName: 'region', operator: RuntimeFilterOp.EQ, values: ['east'] }],
|
|
2820
|
+
});
|
|
2821
|
+
const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
|
|
2822
|
+
...viewConfig,
|
|
2823
|
+
runtimeFilters: [{ columnName: 'region', operator: RuntimeFilterOp.EQ, values: ['west'] }],
|
|
2824
|
+
});
|
|
2825
|
+
|
|
2826
|
+
jest.spyOn(liveboardEmbed as any, 'getPreRenderObj').mockReturnValue(previous);
|
|
2827
|
+
const triggerSpy = jest
|
|
2828
|
+
.spyOn(liveboardEmbed, 'trigger')
|
|
2829
|
+
.mockImplementation(() => Promise.resolve(undefined as any));
|
|
2830
|
+
jest
|
|
2831
|
+
.spyOn(liveboardEmbed, 'navigateToLiveboard')
|
|
2832
|
+
.mockImplementation(() => Promise.resolve(undefined));
|
|
2833
|
+
|
|
2834
|
+
liveboardEmbed.isEmbedContainerLoaded = false;
|
|
2835
|
+
liveboardEmbed['beforePrerenderVisible']();
|
|
2836
|
+
liveboardEmbed.isEmbedContainerLoaded = true;
|
|
2837
|
+
liveboardEmbed['executeEmbedContainerReadyCallbacks']();
|
|
2838
|
+
await waitForHomeHopNavigate();
|
|
2839
|
+
|
|
2840
|
+
expect(triggerSpy).toHaveBeenCalledWith(HostEvent.Navigate, 'home');
|
|
2841
|
+
});
|
|
2842
|
+
|
|
2751
2843
|
test('should not route via home when the pre-render is on another liveboard', async () => {
|
|
2752
2844
|
const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
|
|
2753
2845
|
liveboardId,
|
package/src/embed/liveboard.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* @author Ayon Ghosh <ayon.ghosh@thoughtspot.com>
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import isEqualWith from 'lodash/isEqualWith';
|
|
11
12
|
import { getPreview } from '../utils/graphql/preview-service';
|
|
12
13
|
import { ERROR_MESSAGE } from '../errors';
|
|
13
14
|
import {
|
|
@@ -562,6 +563,14 @@ export interface LiveboardViewConfig
|
|
|
562
563
|
* })
|
|
563
564
|
* ```
|
|
564
565
|
*/
|
|
566
|
+
/**
|
|
567
|
+
* A shallow copy without the keys whose value is `undefined`, so a key that was
|
|
568
|
+
* assigned `undefined` compares equal to one that was never set.
|
|
569
|
+
*/
|
|
570
|
+
const definedEntries = (config: Record<string, any> = {}): Record<string, any> => Object.fromEntries(
|
|
571
|
+
Object.entries(config).filter(([, value]) => value !== undefined),
|
|
572
|
+
);
|
|
573
|
+
|
|
565
574
|
export class LiveboardEmbed extends V1Embed {
|
|
566
575
|
protected viewConfig: LiveboardViewConfig;
|
|
567
576
|
|
|
@@ -955,10 +964,12 @@ export class LiveboardEmbed extends V1Embed {
|
|
|
955
964
|
super.beforePrerenderVisible();
|
|
956
965
|
|
|
957
966
|
// Captured before showPreRender() hands the wrapper over to this
|
|
958
|
-
// instance.
|
|
959
|
-
// nothing stale to clear.
|
|
967
|
+
// instance. React builds a new instance per mount, so an equal config
|
|
968
|
+
// is a hide/show, not a hand-over: there is nothing stale to clear.
|
|
960
969
|
const previous = this.getPreRenderObj<LiveboardEmbed>();
|
|
961
|
-
const showing =
|
|
970
|
+
const showing = this.isSameEmbedConfig(previous)
|
|
971
|
+
? undefined
|
|
972
|
+
: previous?.currentLiveboardState;
|
|
962
973
|
|
|
963
974
|
this.executeAfterEmbedContainerLoaded(async () => {
|
|
964
975
|
// Without this the params callback suspends on its await and
|
|
@@ -992,6 +1003,28 @@ export class LiveboardEmbed extends V1Embed {
|
|
|
992
1003
|
});
|
|
993
1004
|
}
|
|
994
1005
|
|
|
1006
|
+
/**
|
|
1007
|
+
* Whether `previous` was showing what this instance is about to show.
|
|
1008
|
+
*
|
|
1009
|
+
* Undefined-valued keys are dropped before comparing. `navigateToLiveboard`
|
|
1010
|
+
* assigns `vizId`, `activeTabId` and `personalizedViewId` unconditionally,
|
|
1011
|
+
* so an instance that has been shown once carries those keys as `undefined`
|
|
1012
|
+
* while a freshly constructed one does not have them at all. `isEqual`
|
|
1013
|
+
* counts own keys, so without this the comparison can never be true after
|
|
1014
|
+
* the first show — which is the whole case this exists for.
|
|
1015
|
+
*
|
|
1016
|
+
* Callbacks compare as equal: React passes a fresh closure per render.
|
|
1017
|
+
*/
|
|
1018
|
+
private isSameEmbedConfig(previous?: LiveboardEmbed): boolean {
|
|
1019
|
+
if (!previous) return false;
|
|
1020
|
+
if (previous === this) return true;
|
|
1021
|
+
return isEqualWith(
|
|
1022
|
+
definedEntries(previous.viewConfig),
|
|
1023
|
+
definedEntries(this.viewConfig),
|
|
1024
|
+
(a, b) => (typeof a === 'function' && typeof b === 'function' ? true : undefined),
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1027
|
+
|
|
995
1028
|
// The whole route, not just the liveboard id: the path is built from the
|
|
996
1029
|
// viz, tab and view too, so a same-liveboard show onto another tab is a
|
|
997
1030
|
// real navigation.
|
|
@@ -71,10 +71,6 @@ import * as processData from '../utils/processData';
|
|
|
71
71
|
jest.mock('../utils/processTrigger');
|
|
72
72
|
|
|
73
73
|
const mockProcessTrigger = processTrigger as jest.Mock;
|
|
74
|
-
|
|
75
|
-
const flushAnimationFrame = () => new Promise<void>((resolve) => {
|
|
76
|
-
requestAnimationFrame(() => resolve());
|
|
77
|
-
});
|
|
78
74
|
const mockHandleInterceptEvent = jest.spyOn(apiIntercept, 'handleInterceptEvent');
|
|
79
75
|
const defaultViewConfig = {
|
|
80
76
|
frameParams: {
|
|
@@ -2624,9 +2620,6 @@ describe('Unit test case for ts embed', () => {
|
|
|
2624
2620
|
contentRect: { height: 297, width: 987 },
|
|
2625
2621
|
},
|
|
2626
2622
|
]);
|
|
2627
|
-
// Syncs are coalesced to one per frame.
|
|
2628
|
-
await flushAnimationFrame();
|
|
2629
|
-
|
|
2630
2623
|
expect(preRenderWrapper.style.height).toEqual(`${297}px`);
|
|
2631
2624
|
expect(preRenderWrapper.style.width).toEqual(`${987}px`);
|
|
2632
2625
|
|
|
@@ -2887,28 +2880,28 @@ describe('Unit test case for ts embed', () => {
|
|
|
2887
2880
|
return placeholder;
|
|
2888
2881
|
};
|
|
2889
2882
|
|
|
2890
|
-
it('should stop
|
|
2891
|
-
const { scroller
|
|
2883
|
+
it('should stop observing the placeholder once the frame is hidden', async () => {
|
|
2884
|
+
const { scroller } = mountInNestedScroller();
|
|
2892
2885
|
|
|
2893
2886
|
const libEmbed = new LiveboardEmbed('#tsEmbedDiv', {
|
|
2894
|
-
preRenderId: '
|
|
2887
|
+
preRenderId: 'observer-stops-on-hide',
|
|
2895
2888
|
liveboardId: 'myLiveboardId',
|
|
2896
2889
|
});
|
|
2897
2890
|
libEmbed.preRender();
|
|
2898
2891
|
await waitFor(() => !!getIFrameEl());
|
|
2899
2892
|
await libEmbed.showPreRender();
|
|
2900
2893
|
|
|
2901
|
-
|
|
2902
|
-
|
|
2894
|
+
// Size is the only thing the browser will not handle for us, so
|
|
2895
|
+
// the observer is the one live resource a hide has to release.
|
|
2896
|
+
const observer = (libEmbed as any).resizeObserver;
|
|
2897
|
+
expect(observer).toBeDefined();
|
|
2898
|
+
const disconnectSpy = jest.spyOn(observer, 'disconnect');
|
|
2903
2899
|
|
|
2904
|
-
|
|
2905
|
-
scroller.scrollTop = 250;
|
|
2906
|
-
scroller.dispatchEvent(new Event('scroll'));
|
|
2907
|
-
await flushAnimationFrame();
|
|
2900
|
+
libEmbed.hidePreRender();
|
|
2908
2901
|
|
|
2909
|
-
expect(
|
|
2902
|
+
expect(disconnectSpy).toHaveBeenCalled();
|
|
2910
2903
|
|
|
2911
|
-
|
|
2904
|
+
disconnectSpy.mockRestore();
|
|
2912
2905
|
libEmbed.destroy();
|
|
2913
2906
|
scroller.remove();
|
|
2914
2907
|
});
|