@thoughtspot/visual-embed-sdk 1.53.0 → 1.53.2-beta
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/auth.d.ts.map +1 -1
- package/cjs/src/auth.js +25 -6
- package/cjs/src/auth.js.map +1 -1
- package/cjs/src/auth.spec.js +40 -8
- package/cjs/src/auth.spec.js.map +1 -1
- package/cjs/src/contracts/contracts.spec.js +169 -2
- package/cjs/src/contracts/contracts.spec.js.map +1 -1
- package/cjs/src/contracts/embed-event-payloads.d.ts +212 -14
- package/cjs/src/contracts/embed-event-payloads.d.ts.map +1 -1
- package/cjs/src/contracts/host-event-contracts.d.ts +11 -0
- package/cjs/src/contracts/host-event-contracts.d.ts.map +1 -1
- package/cjs/src/css-variables.d.ts +140 -0
- package/cjs/src/css-variables.d.ts.map +1 -1
- package/cjs/src/embed/app.d.ts +1 -0
- package/cjs/src/embed/app.d.ts.map +1 -1
- package/cjs/src/embed/app.js.map +1 -1
- package/cjs/src/embed/auto-frame-renderer.d.ts +14 -1
- package/cjs/src/embed/auto-frame-renderer.d.ts.map +1 -1
- package/cjs/src/embed/auto-frame-renderer.js +91 -2
- package/cjs/src/embed/auto-frame-renderer.js.map +1 -1
- package/cjs/src/embed/auto-frame-renderer.spec.js +112 -7
- package/cjs/src/embed/auto-frame-renderer.spec.js.map +1 -1
- package/cjs/src/embed/conversation.d.ts +14 -0
- package/cjs/src/embed/conversation.d.ts.map +1 -1
- package/cjs/src/embed/conversation.js.map +1 -1
- package/cjs/src/embed/conversation.spec.js +22 -0
- package/cjs/src/embed/conversation.spec.js.map +1 -1
- package/cjs/src/embed/host-events.spec.js +50 -0
- package/cjs/src/embed/host-events.spec.js.map +1 -1
- package/cjs/src/embed/liveboard.d.ts +2 -16
- package/cjs/src/embed/liveboard.d.ts.map +1 -1
- package/cjs/src/embed/liveboard.js +29 -10
- package/cjs/src/embed/liveboard.js.map +1 -1
- package/cjs/src/embed/liveboard.spec.js +139 -0
- package/cjs/src/embed/liveboard.spec.js.map +1 -1
- package/cjs/src/embed/spotter-utils.d.ts +1 -0
- package/cjs/src/embed/spotter-utils.d.ts.map +1 -1
- package/cjs/src/embed/spotter-utils.js +7 -1
- package/cjs/src/embed/spotter-utils.js.map +1 -1
- package/cjs/src/embed/spotter-utils.spec.js +19 -0
- package/cjs/src/embed/spotter-utils.spec.js.map +1 -1
- package/cjs/src/embed/ts-embed.d.ts +21 -16
- package/cjs/src/embed/ts-embed.d.ts.map +1 -1
- package/cjs/src/embed/ts-embed.js +77 -63
- package/cjs/src/embed/ts-embed.js.map +1 -1
- package/cjs/src/embed/ts-embed.spec.js +240 -7
- package/cjs/src/embed/ts-embed.spec.js.map +1 -1
- package/cjs/src/index.d.ts +2 -2
- package/cjs/src/index.d.ts.map +1 -1
- package/cjs/src/index.js.map +1 -1
- package/cjs/src/react/all-types-export.d.ts +1 -1
- package/cjs/src/react/all-types-export.d.ts.map +1 -1
- package/cjs/src/react/all-types-export.js.map +1 -1
- package/cjs/src/react/util.d.ts +8 -1
- package/cjs/src/react/util.d.ts.map +1 -1
- package/cjs/src/react/util.js.map +1 -1
- package/cjs/src/types.d.ts +249 -2
- package/cjs/src/types.d.ts.map +1 -1
- package/cjs/src/types.js +247 -0
- package/cjs/src/types.js.map +1 -1
- package/cjs/src/utils/runtimeTelemetry.d.ts +11 -0
- package/cjs/src/utils/runtimeTelemetry.d.ts.map +1 -0
- package/cjs/src/utils/runtimeTelemetry.js +34 -0
- package/cjs/src/utils/runtimeTelemetry.js.map +1 -0
- package/cjs/src/utils/runtimeTelemetry.spec.d.ts +2 -0
- package/cjs/src/utils/runtimeTelemetry.spec.d.ts.map +1 -0
- package/cjs/src/utils/runtimeTelemetry.spec.js +124 -0
- package/cjs/src/utils/runtimeTelemetry.spec.js.map +1 -0
- package/dist/{index-CyDR8j_0.js → index-DQ7r5f1z.js} +1 -1
- package/dist/src/auth.d.ts.map +1 -1
- package/dist/src/contracts/embed-event-payloads.d.ts +212 -14
- package/dist/src/contracts/embed-event-payloads.d.ts.map +1 -1
- package/dist/src/contracts/host-event-contracts.d.ts +11 -0
- package/dist/src/contracts/host-event-contracts.d.ts.map +1 -1
- package/dist/src/css-variables.d.ts +140 -0
- package/dist/src/css-variables.d.ts.map +1 -1
- package/dist/src/embed/app.d.ts +1 -0
- package/dist/src/embed/app.d.ts.map +1 -1
- package/dist/src/embed/auto-frame-renderer.d.ts +14 -1
- package/dist/src/embed/auto-frame-renderer.d.ts.map +1 -1
- package/dist/src/embed/conversation.d.ts +14 -0
- package/dist/src/embed/conversation.d.ts.map +1 -1
- package/dist/src/embed/liveboard.d.ts +2 -16
- package/dist/src/embed/liveboard.d.ts.map +1 -1
- package/dist/src/embed/spotter-utils.d.ts +1 -0
- package/dist/src/embed/spotter-utils.d.ts.map +1 -1
- package/dist/src/embed/ts-embed.d.ts +21 -16
- package/dist/src/embed/ts-embed.d.ts.map +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/react/all-types-export.d.ts +1 -1
- package/dist/src/react/all-types-export.d.ts.map +1 -1
- package/dist/src/react/util.d.ts +8 -1
- package/dist/src/react/util.d.ts.map +1 -1
- package/dist/src/types.d.ts +249 -2
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/runtimeTelemetry.d.ts +11 -0
- package/dist/src/utils/runtimeTelemetry.d.ts.map +1 -0
- package/dist/src/utils/runtimeTelemetry.spec.d.ts +2 -0
- package/dist/src/utils/runtimeTelemetry.spec.d.ts.map +1 -0
- package/dist/tsembed-react.es.js +474 -84
- package/dist/tsembed-react.js +473 -83
- package/dist/tsembed.es.js +542 -85
- package/dist/tsembed.js +541 -84
- package/dist/visual-embed-sdk-react-full.d.ts +649 -30
- package/dist/visual-embed-sdk-react.d.ts +646 -28
- package/dist/visual-embed-sdk.d.ts +642 -44
- package/lib/package.json +1 -1
- package/lib/src/auth.d.ts.map +1 -1
- package/lib/src/auth.js +25 -6
- package/lib/src/auth.js.map +1 -1
- package/lib/src/auth.spec.js +40 -8
- package/lib/src/auth.spec.js.map +1 -1
- package/lib/src/contracts/contracts.spec.js +169 -2
- package/lib/src/contracts/contracts.spec.js.map +1 -1
- package/lib/src/contracts/embed-event-payloads.d.ts +212 -14
- package/lib/src/contracts/embed-event-payloads.d.ts.map +1 -1
- package/lib/src/contracts/host-event-contracts.d.ts +11 -0
- package/lib/src/contracts/host-event-contracts.d.ts.map +1 -1
- package/lib/src/css-variables.d.ts +140 -0
- package/lib/src/css-variables.d.ts.map +1 -1
- package/lib/src/embed/app.d.ts +1 -0
- package/lib/src/embed/app.d.ts.map +1 -1
- package/lib/src/embed/app.js.map +1 -1
- package/lib/src/embed/auto-frame-renderer.d.ts +14 -1
- package/lib/src/embed/auto-frame-renderer.d.ts.map +1 -1
- package/lib/src/embed/auto-frame-renderer.js +91 -2
- package/lib/src/embed/auto-frame-renderer.js.map +1 -1
- package/lib/src/embed/auto-frame-renderer.spec.js +113 -8
- package/lib/src/embed/auto-frame-renderer.spec.js.map +1 -1
- package/lib/src/embed/conversation.d.ts +14 -0
- package/lib/src/embed/conversation.d.ts.map +1 -1
- package/lib/src/embed/conversation.js.map +1 -1
- package/lib/src/embed/conversation.spec.js +22 -0
- package/lib/src/embed/conversation.spec.js.map +1 -1
- package/lib/src/embed/host-events.spec.js +50 -0
- package/lib/src/embed/host-events.spec.js.map +1 -1
- package/lib/src/embed/liveboard.d.ts +2 -16
- package/lib/src/embed/liveboard.d.ts.map +1 -1
- package/lib/src/embed/liveboard.js +28 -10
- package/lib/src/embed/liveboard.js.map +1 -1
- package/lib/src/embed/liveboard.spec.js +139 -0
- package/lib/src/embed/liveboard.spec.js.map +1 -1
- package/lib/src/embed/spotter-utils.d.ts +1 -0
- package/lib/src/embed/spotter-utils.d.ts.map +1 -1
- package/lib/src/embed/spotter-utils.js +7 -1
- package/lib/src/embed/spotter-utils.js.map +1 -1
- package/lib/src/embed/spotter-utils.spec.js +19 -0
- package/lib/src/embed/spotter-utils.spec.js.map +1 -1
- package/lib/src/embed/ts-embed.d.ts +21 -16
- package/lib/src/embed/ts-embed.d.ts.map +1 -1
- package/lib/src/embed/ts-embed.js +78 -64
- package/lib/src/embed/ts-embed.js.map +1 -1
- package/lib/src/embed/ts-embed.spec.js +240 -7
- package/lib/src/embed/ts-embed.spec.js.map +1 -1
- package/lib/src/index.d.ts +2 -2
- package/lib/src/index.d.ts.map +1 -1
- package/lib/src/index.js.map +1 -1
- package/lib/src/react/all-types-export.d.ts +1 -1
- package/lib/src/react/all-types-export.d.ts.map +1 -1
- package/lib/src/react/all-types-export.js.map +1 -1
- package/lib/src/react/util.d.ts +8 -1
- package/lib/src/react/util.d.ts.map +1 -1
- package/lib/src/react/util.js.map +1 -1
- package/lib/src/types.d.ts +249 -2
- package/lib/src/types.d.ts.map +1 -1
- package/lib/src/types.js +247 -0
- package/lib/src/types.js.map +1 -1
- package/lib/src/utils/runtimeTelemetry.d.ts +11 -0
- package/lib/src/utils/runtimeTelemetry.d.ts.map +1 -0
- package/lib/src/utils/runtimeTelemetry.js +28 -0
- package/lib/src/utils/runtimeTelemetry.js.map +1 -0
- package/lib/src/utils/runtimeTelemetry.spec.d.ts +2 -0
- package/lib/src/utils/runtimeTelemetry.spec.d.ts.map +1 -0
- package/lib/src/utils/runtimeTelemetry.spec.js +122 -0
- package/lib/src/utils/runtimeTelemetry.spec.js.map +1 -0
- package/lib/src/visual-embed-sdk.d.ts +642 -44
- package/package.json +1 -1
- package/src/auth.spec.ts +47 -11
- package/src/auth.ts +35 -6
- package/src/contracts/contracts.spec.ts +186 -3
- package/src/contracts/embed-event-payloads.ts +281 -14
- package/src/contracts/host-event-contracts.ts +14 -0
- package/src/css-variables.ts +168 -0
- package/src/embed/app.ts +1 -0
- package/src/embed/auto-frame-renderer.spec.ts +134 -8
- package/src/embed/auto-frame-renderer.ts +103 -2
- package/src/embed/conversation.spec.ts +24 -0
- package/src/embed/conversation.ts +14 -0
- package/src/embed/host-events.spec.ts +68 -0
- package/src/embed/liveboard.spec.ts +156 -0
- package/src/embed/liveboard.ts +34 -10
- package/src/embed/spotter-utils.spec.ts +20 -0
- package/src/embed/spotter-utils.ts +12 -1
- package/src/embed/ts-embed.spec.ts +297 -8
- package/src/embed/ts-embed.ts +79 -64
- package/src/index.ts +2 -0
- package/src/react/all-types-export.ts +1 -0
- package/src/react/util.ts +13 -1
- package/src/types.ts +249 -0
- package/src/utils/runtimeTelemetry.spec.ts +148 -0
- package/src/utils/runtimeTelemetry.ts +50 -0
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 {
|
|
@@ -605,6 +606,14 @@ export interface LiveboardViewConfig
|
|
|
605
606
|
* })
|
|
606
607
|
* ```
|
|
607
608
|
*/
|
|
609
|
+
/**
|
|
610
|
+
* A shallow copy without the keys whose value is `undefined`, so a key that was
|
|
611
|
+
* assigned `undefined` compares equal to one that was never set.
|
|
612
|
+
*/
|
|
613
|
+
const definedEntries = (config: Record<string, any> = {}): Record<string, any> => Object.fromEntries(
|
|
614
|
+
Object.entries(config).filter(([, value]) => value !== undefined),
|
|
615
|
+
);
|
|
616
|
+
|
|
608
617
|
export class LiveboardEmbed extends V1Embed {
|
|
609
618
|
protected viewConfig: LiveboardViewConfig;
|
|
610
619
|
|
|
@@ -1004,17 +1013,19 @@ export class LiveboardEmbed extends V1Embed {
|
|
|
1004
1013
|
super.beforePrerenderVisible();
|
|
1005
1014
|
|
|
1006
1015
|
// Captured before showPreRender() hands the wrapper over to this
|
|
1007
|
-
// instance.
|
|
1008
|
-
// nothing stale to clear.
|
|
1016
|
+
// instance. React builds a new instance per mount, so an equal config
|
|
1017
|
+
// is a hide/show, not a hand-over: there is nothing stale to clear.
|
|
1009
1018
|
const previous = this.getPreRenderObj<LiveboardEmbed>();
|
|
1010
|
-
const
|
|
1019
|
+
const previousRoute = this.isSameEmbedConfig(previous)
|
|
1020
|
+
? undefined
|
|
1021
|
+
: previous?.currentLiveboardState;
|
|
1011
1022
|
|
|
1012
1023
|
this.executeAfterEmbedContainerLoaded(async () => {
|
|
1013
1024
|
// Without this the params callback suspends on its await and
|
|
1014
1025
|
// Navigate goes out first, so the container loads with the previous
|
|
1015
1026
|
// config's filters.
|
|
1016
1027
|
await this.preRenderParamsApplied;
|
|
1017
|
-
if (this.
|
|
1028
|
+
if (this.isAlreadyOnSameRoute(previousRoute)) {
|
|
1018
1029
|
// Navigate to the route we are already on is a no-op, so the
|
|
1019
1030
|
// liveboard keeps its state. Home unmounts the container, which
|
|
1020
1031
|
// clears it.
|
|
@@ -1041,18 +1052,31 @@ export class LiveboardEmbed extends V1Embed {
|
|
|
1041
1052
|
});
|
|
1042
1053
|
}
|
|
1043
1054
|
|
|
1055
|
+
// definedEntries drops undefined-valued keys: a shown instance carries
|
|
1056
|
+
// vizId/activeTabId/etc as undefined, so a raw isEqual never matches again.
|
|
1057
|
+
// Functions compare equal, since React passes a fresh closure per render.
|
|
1058
|
+
private isSameEmbedConfig(previous?: LiveboardEmbed): boolean {
|
|
1059
|
+
if (!previous) return false;
|
|
1060
|
+
if (previous === this) return true;
|
|
1061
|
+
return isEqualWith(
|
|
1062
|
+
definedEntries(previous.viewConfig),
|
|
1063
|
+
definedEntries(this.viewConfig),
|
|
1064
|
+
(a, b) => (typeof a === 'function' && typeof b === 'function' ? true : undefined),
|
|
1065
|
+
);
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1044
1068
|
// The whole route, not just the liveboard id: the path is built from the
|
|
1045
1069
|
// viz, tab and view too, so a same-liveboard show onto another tab is a
|
|
1046
1070
|
// real navigation.
|
|
1047
|
-
private
|
|
1048
|
-
if (!
|
|
1071
|
+
private isAlreadyOnSameRoute(previousRoute?: LiveboardEmbed['currentLiveboardState']): boolean {
|
|
1072
|
+
if (!previousRoute?.liveboardId) return false;
|
|
1049
1073
|
const {
|
|
1050
1074
|
liveboardId, vizId, activeTabId, personalizedViewId,
|
|
1051
1075
|
} = this.viewConfig;
|
|
1052
|
-
return
|
|
1053
|
-
&&
|
|
1054
|
-
&&
|
|
1055
|
-
&&
|
|
1076
|
+
return previousRoute.liveboardId === liveboardId
|
|
1077
|
+
&& previousRoute.vizId === vizId
|
|
1078
|
+
&& previousRoute.activeTabId === activeTabId
|
|
1079
|
+
&& previousRoute.personalizedViewId === personalizedViewId;
|
|
1056
1080
|
}
|
|
1057
1081
|
|
|
1058
1082
|
protected async handleRenderForPrerender(): Promise<TsEmbed> {
|
|
@@ -58,6 +58,26 @@ describe('buildSpotterSidebarAppInitData', () => {
|
|
|
58
58
|
.toEqual(spotterChatPinConfig);
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
+
it('lifts enableSpotterInstructionAndMoreSettings to the top level of embedParams', () => {
|
|
62
|
+
const result = buildSpotterSidebarAppInitData(base, {
|
|
63
|
+
spotterSidebarConfig: {
|
|
64
|
+
enablePastConversationsSidebar: true,
|
|
65
|
+
enableSpotterInstructionAndMoreSettings: false,
|
|
66
|
+
},
|
|
67
|
+
}, noopError);
|
|
68
|
+
expect(result.embedParams?.enableSpotterInstructionAndMoreSettings).toBe(false);
|
|
69
|
+
expect(result.embedParams?.spotterSidebarConfig).toEqual({
|
|
70
|
+
enablePastConversationsSidebar: true,
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('omits enableSpotterInstructionAndMoreSettings when it is not set', () => {
|
|
75
|
+
const result = buildSpotterSidebarAppInitData(base, {
|
|
76
|
+
spotterSidebarConfig: { enablePastConversationsSidebar: true },
|
|
77
|
+
}, noopError);
|
|
78
|
+
expect(result.embedParams).not.toHaveProperty('enableSpotterInstructionAndMoreSettings');
|
|
79
|
+
});
|
|
80
|
+
|
|
61
81
|
it('promotes standalone flag into spotterSidebarConfig.enablePastConversationsSidebar', () => {
|
|
62
82
|
const result = buildSpotterSidebarAppInitData(base, { enablePastConversationsSidebar: true }, noopError);
|
|
63
83
|
expect(result.embedParams?.spotterSidebarConfig?.enablePastConversationsSidebar).toBe(true);
|
|
@@ -36,6 +36,7 @@ export function buildSpotterSidebarAppInitData<T extends DefaultAppInitData>(
|
|
|
36
36
|
embedParams?: {
|
|
37
37
|
spotterSidebarConfig?: SpotterSidebarViewConfig;
|
|
38
38
|
visualOverridesParams?: VisualizationOverrides | null;
|
|
39
|
+
enableSpotterInstructionAndMoreSettings?: boolean;
|
|
39
40
|
};
|
|
40
41
|
} {
|
|
41
42
|
const { spotterSidebarConfig, enablePastConversationsSidebar, visualOverrides } = viewConfig;
|
|
@@ -56,8 +57,15 @@ export function buildSpotterSidebarAppInitData<T extends DefaultAppInitData>(
|
|
|
56
57
|
};
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
// The app reads enableSpotterInstructionAndMoreSettings from the top level
|
|
61
|
+
// of embedParams, so lift it out of the sidebar config.
|
|
62
|
+
const {
|
|
63
|
+
enableSpotterInstructionAndMoreSettings,
|
|
64
|
+
...sidebarConfigRest
|
|
65
|
+
} = spotterSidebarConfig || {};
|
|
66
|
+
|
|
59
67
|
const resolvedSidebarConfig: SpotterSidebarViewConfig = {
|
|
60
|
-
...
|
|
68
|
+
...sidebarConfigRest,
|
|
61
69
|
...(resolvedEnablePastConversations !== undefined && {
|
|
62
70
|
enablePastConversationsSidebar: resolvedEnablePastConversations,
|
|
63
71
|
}),
|
|
@@ -81,6 +89,9 @@ export function buildSpotterSidebarAppInitData<T extends DefaultAppInitData>(
|
|
|
81
89
|
embedParams: {
|
|
82
90
|
...((defaultAppInitData as any).embedParams || {}),
|
|
83
91
|
spotterSidebarConfig: resolvedSidebarConfig,
|
|
92
|
+
...(enableSpotterInstructionAndMoreSettings !== undefined
|
|
93
|
+
? { enableSpotterInstructionAndMoreSettings }
|
|
94
|
+
: {}),
|
|
84
95
|
...(visualOverrides !== undefined ? { visualOverridesParams: visualOverrides } : {}),
|
|
85
96
|
},
|
|
86
97
|
};
|
|
@@ -2728,12 +2728,11 @@ describe('Unit test case for ts embed', () => {
|
|
|
2728
2728
|
contentRect: { height: 297, width: 987 },
|
|
2729
2729
|
},
|
|
2730
2730
|
]);
|
|
2731
|
-
|
|
2732
2731
|
expect(preRenderWrapper.style.height).toEqual(`${297}px`);
|
|
2733
2732
|
expect(preRenderWrapper.style.width).toEqual(`${987}px`);
|
|
2734
2733
|
|
|
2735
2734
|
expect(preRenderWrapper.style.opacity).toBe('');
|
|
2736
|
-
expect(preRenderWrapper.style.pointerEvents).toBe('');
|
|
2735
|
+
expect(preRenderWrapper.style.pointerEvents).toBe('auto');
|
|
2737
2736
|
expect(preRenderWrapper.style.zIndex).toBe('');
|
|
2738
2737
|
|
|
2739
2738
|
libEmbed.hidePreRender();
|
|
@@ -2770,7 +2769,7 @@ describe('Unit test case for ts embed', () => {
|
|
|
2770
2769
|
const preRenderWrapper = document.getElementById(preRenderIds.wrapper);
|
|
2771
2770
|
|
|
2772
2771
|
expect(preRenderWrapper.style.opacity).toBe('');
|
|
2773
|
-
expect(preRenderWrapper.style.pointerEvents).toBe('');
|
|
2772
|
+
expect(preRenderWrapper.style.pointerEvents).toBe('auto');
|
|
2774
2773
|
expect(preRenderWrapper.style.zIndex).toBe('');
|
|
2775
2774
|
});
|
|
2776
2775
|
|
|
@@ -2943,6 +2942,216 @@ describe('Unit test case for ts embed', () => {
|
|
|
2943
2942
|
(logger.debug as any).mockRestore();
|
|
2944
2943
|
});
|
|
2945
2944
|
|
|
2945
|
+
describe('position tracking (SCAL-338563)', () => {
|
|
2946
|
+
/**
|
|
2947
|
+
* A host app whose page scrolls in an inner element rather than the
|
|
2948
|
+
* document — the shape both reported cases have. The placeholder's
|
|
2949
|
+
* rect is driven off that element's scrollTop, the way a real
|
|
2950
|
+
* layout would move it.
|
|
2951
|
+
*/
|
|
2952
|
+
const mountInNestedScroller = () => {
|
|
2953
|
+
createRootEleForEmbed();
|
|
2954
|
+
const scroller = document.createElement('div');
|
|
2955
|
+
scroller.id = 'inner-scroller';
|
|
2956
|
+
document.body.appendChild(scroller);
|
|
2957
|
+
scroller.appendChild(document.getElementById('tsEmbedDiv'));
|
|
2958
|
+
|
|
2959
|
+
let scrollTop = 0;
|
|
2960
|
+
Object.defineProperty(scroller, 'scrollTop', {
|
|
2961
|
+
configurable: true,
|
|
2962
|
+
get: () => scrollTop,
|
|
2963
|
+
set: (value) => {
|
|
2964
|
+
scrollTop = value;
|
|
2965
|
+
},
|
|
2966
|
+
});
|
|
2967
|
+
scroller.getBoundingClientRect = () =>
|
|
2968
|
+
({
|
|
2969
|
+
x: 0, y: 0, width: 800, height: 600,
|
|
2970
|
+
top: 0, left: 0, bottom: 600, right: 800,
|
|
2971
|
+
} as DOMRect);
|
|
2972
|
+
|
|
2973
|
+
const placeholderTopAtRest = 400;
|
|
2974
|
+
const readPlaceholderTop = () => placeholderTopAtRest - scroller.scrollTop;
|
|
2975
|
+
|
|
2976
|
+
return { scroller, readPlaceholderTop };
|
|
2977
|
+
};
|
|
2978
|
+
|
|
2979
|
+
const stubPlaceholderRect = (embed: any, readTop: () => number) => {
|
|
2980
|
+
const placeholder = embed.getPreRenderPlaceHolderElement() as HTMLElement;
|
|
2981
|
+
placeholder.getBoundingClientRect = () => {
|
|
2982
|
+
const top = readTop();
|
|
2983
|
+
return {
|
|
2984
|
+
x: 0, y: top, width: 800, height: 300,
|
|
2985
|
+
top, left: 0, bottom: top + 300, right: 800,
|
|
2986
|
+
} as DOMRect;
|
|
2987
|
+
};
|
|
2988
|
+
return placeholder;
|
|
2989
|
+
};
|
|
2990
|
+
|
|
2991
|
+
it('should stop observing the placeholder once the frame is hidden', async () => {
|
|
2992
|
+
const { scroller } = mountInNestedScroller();
|
|
2993
|
+
|
|
2994
|
+
const libEmbed = new LiveboardEmbed('#tsEmbedDiv', {
|
|
2995
|
+
preRenderId: 'observer-stops-on-hide',
|
|
2996
|
+
liveboardId: 'myLiveboardId',
|
|
2997
|
+
});
|
|
2998
|
+
libEmbed.preRender();
|
|
2999
|
+
await waitFor(() => !!getIFrameEl());
|
|
3000
|
+
await libEmbed.showPreRender();
|
|
3001
|
+
|
|
3002
|
+
// Size is the only thing the browser will not handle for us, so
|
|
3003
|
+
// the observer is the one live resource a hide has to release.
|
|
3004
|
+
const observer = (libEmbed as any).resizeObserver;
|
|
3005
|
+
expect(observer).toBeDefined();
|
|
3006
|
+
const disconnectSpy = jest.spyOn(observer, 'disconnect');
|
|
3007
|
+
|
|
3008
|
+
libEmbed.hidePreRender();
|
|
3009
|
+
|
|
3010
|
+
expect(disconnectSpy).toHaveBeenCalled();
|
|
3011
|
+
|
|
3012
|
+
disconnectSpy.mockRestore();
|
|
3013
|
+
libEmbed.destroy();
|
|
3014
|
+
scroller.remove();
|
|
3015
|
+
});
|
|
3016
|
+
|
|
3017
|
+
it('should default the container to the host scrolling ancestor', async () => {
|
|
3018
|
+
const { scroller } = mountInNestedScroller();
|
|
3019
|
+
scroller.style.overflowY = 'auto';
|
|
3020
|
+
|
|
3021
|
+
const libEmbed = new LiveboardEmbed('#tsEmbedDiv', {
|
|
3022
|
+
preRenderId: 'auto-scroll-container',
|
|
3023
|
+
liveboardId: 'myLiveboardId',
|
|
3024
|
+
});
|
|
3025
|
+
libEmbed.preRender();
|
|
3026
|
+
await waitFor(() => !!getIFrameEl());
|
|
3027
|
+
await libEmbed.showPreRender();
|
|
3028
|
+
|
|
3029
|
+
const wrapper = document.getElementById(libEmbed.getPreRenderIds().wrapper);
|
|
3030
|
+
// In the scroller, so the browser moves it — no repositioning.
|
|
3031
|
+
expect(wrapper.parentElement).toBe(scroller);
|
|
3032
|
+
expect((libEmbed as any).preRenderContainerEl).toBe(scroller);
|
|
3033
|
+
|
|
3034
|
+
libEmbed.destroy();
|
|
3035
|
+
scroller.remove();
|
|
3036
|
+
});
|
|
3037
|
+
|
|
3038
|
+
it('should still use document.body when nothing scrolls', async () => {
|
|
3039
|
+
createRootEleForEmbed();
|
|
3040
|
+
|
|
3041
|
+
const libEmbed = new LiveboardEmbed('#tsEmbedDiv', {
|
|
3042
|
+
preRenderId: 'no-scroller-body',
|
|
3043
|
+
liveboardId: 'myLiveboardId',
|
|
3044
|
+
});
|
|
3045
|
+
libEmbed.preRender();
|
|
3046
|
+
await waitFor(() => !!getIFrameEl());
|
|
3047
|
+
await libEmbed.showPreRender();
|
|
3048
|
+
|
|
3049
|
+
const wrapper = document.getElementById(libEmbed.getPreRenderIds().wrapper);
|
|
3050
|
+
expect(wrapper.parentElement).toBe(document.body);
|
|
3051
|
+
|
|
3052
|
+
libEmbed.destroy();
|
|
3053
|
+
});
|
|
3054
|
+
|
|
3055
|
+
it('should set pointer-events on show rather than dropping the property', async () => {
|
|
3056
|
+
createRootEleForEmbed();
|
|
3057
|
+
const container = document.createElement('div');
|
|
3058
|
+
container.id = 'parking-root';
|
|
3059
|
+
// Usual styling for a parking root: no swallowed clicks.
|
|
3060
|
+
container.style.pointerEvents = 'none';
|
|
3061
|
+
document.body.appendChild(container);
|
|
3062
|
+
|
|
3063
|
+
const libEmbed = new LiveboardEmbed('#tsEmbedDiv', {
|
|
3064
|
+
preRenderId: 'pointer-events-restored',
|
|
3065
|
+
liveboardId: 'myLiveboardId',
|
|
3066
|
+
preRenderContainer: '#parking-root',
|
|
3067
|
+
});
|
|
3068
|
+
libEmbed.preRender();
|
|
3069
|
+
await waitFor(() => !!getIFrameEl());
|
|
3070
|
+
await libEmbed.showPreRender();
|
|
3071
|
+
|
|
3072
|
+
const wrapper = document.getElementById(libEmbed.getPreRenderIds().wrapper);
|
|
3073
|
+
// Merely removing the property would leave the container's
|
|
3074
|
+
// inherited `none` in force, and the frame dead to input.
|
|
3075
|
+
expect(wrapper.style.pointerEvents).toBe('auto');
|
|
3076
|
+
|
|
3077
|
+
libEmbed.destroy();
|
|
3078
|
+
container.remove();
|
|
3079
|
+
});
|
|
3080
|
+
|
|
3081
|
+
it('should park a hidden wrapper out of the scrollable area', async () => {
|
|
3082
|
+
createRootEleForEmbed();
|
|
3083
|
+
|
|
3084
|
+
const libEmbed = new LiveboardEmbed('#tsEmbedDiv', {
|
|
3085
|
+
preRenderId: 'hidden-parked',
|
|
3086
|
+
liveboardId: 'myLiveboardId',
|
|
3087
|
+
});
|
|
3088
|
+
libEmbed.preRender();
|
|
3089
|
+
await waitFor(() => !!getIFrameEl());
|
|
3090
|
+
await libEmbed.showPreRender();
|
|
3091
|
+
|
|
3092
|
+
const wrapper = document.getElementById(libEmbed.getPreRenderIds().wrapper);
|
|
3093
|
+
expect(wrapper.style.transform).toBe('');
|
|
3094
|
+
|
|
3095
|
+
libEmbed.hidePreRender();
|
|
3096
|
+
// Detached from every scroll container (fixed, resolves to the
|
|
3097
|
+
// viewport), parked for the captured-fixed case, size
|
|
3098
|
+
// preserved.
|
|
3099
|
+
expect(wrapper.style.position).toBe('fixed');
|
|
3100
|
+
expect(wrapper.style.transform).toBe('translateY(-100%)');
|
|
3101
|
+
expect(wrapper.style.height).not.toBe('');
|
|
3102
|
+
|
|
3103
|
+
await libEmbed.showPreRender();
|
|
3104
|
+
expect(wrapper.style.transform).toBe('');
|
|
3105
|
+
|
|
3106
|
+
libEmbed.destroy();
|
|
3107
|
+
});
|
|
3108
|
+
|
|
3109
|
+
it('should not seed the placeholder with the unmeasured wrapper height', async () => {
|
|
3110
|
+
createRootEleForEmbed();
|
|
3111
|
+
|
|
3112
|
+
const libEmbed = new LiveboardEmbed('#tsEmbedDiv', {
|
|
3113
|
+
preRenderId: 'unmeasured-height',
|
|
3114
|
+
liveboardId: 'myLiveboardId',
|
|
3115
|
+
fullHeight: true,
|
|
3116
|
+
frameParams: { height: 420 },
|
|
3117
|
+
});
|
|
3118
|
+
libEmbed.preRender();
|
|
3119
|
+
await waitFor(() => !!getIFrameEl());
|
|
3120
|
+
|
|
3121
|
+
const wrapper = document.getElementById(libEmbed.getPreRenderIds().wrapper);
|
|
3122
|
+
// createPreRenderWrapper() seeds 100vh, not a measurement.
|
|
3123
|
+
expect(wrapper.style.height).toBe('100vh');
|
|
3124
|
+
|
|
3125
|
+
await libEmbed.showPreRender();
|
|
3126
|
+
|
|
3127
|
+
const placeholder = (libEmbed as any).getPreRenderPlaceHolderElement() as HTMLElement;
|
|
3128
|
+
expect(placeholder.style.height).toBe('420px');
|
|
3129
|
+
|
|
3130
|
+
libEmbed.destroy();
|
|
3131
|
+
});
|
|
3132
|
+
|
|
3133
|
+
it('should seed the placeholder with a height fullHeight has measured', async () => {
|
|
3134
|
+
createRootEleForEmbed();
|
|
3135
|
+
|
|
3136
|
+
const libEmbed = new LiveboardEmbed('#tsEmbedDiv', {
|
|
3137
|
+
preRenderId: 'measured-height',
|
|
3138
|
+
liveboardId: 'myLiveboardId',
|
|
3139
|
+
fullHeight: true,
|
|
3140
|
+
frameParams: { height: 420 },
|
|
3141
|
+
});
|
|
3142
|
+
libEmbed.preRender();
|
|
3143
|
+
await waitFor(() => !!getIFrameEl());
|
|
3144
|
+
|
|
3145
|
+
(libEmbed as any).setIFrameHeight(1180);
|
|
3146
|
+
await libEmbed.showPreRender();
|
|
3147
|
+
|
|
3148
|
+
const placeholder = (libEmbed as any).getPreRenderPlaceHolderElement() as HTMLElement;
|
|
3149
|
+
expect(placeholder.style.height).toBe('1180px');
|
|
3150
|
+
|
|
3151
|
+
libEmbed.destroy();
|
|
3152
|
+
});
|
|
3153
|
+
});
|
|
3154
|
+
|
|
2946
3155
|
describe('preRenderContainer', () => {
|
|
2947
3156
|
it('should append preRenderWrapper to document.body when preRenderContainer is not set', async () => {
|
|
2948
3157
|
createRootEleForEmbed();
|
|
@@ -3216,21 +3425,16 @@ describe('Unit test case for ts embed', () => {
|
|
|
3216
3425
|
|
|
3217
3426
|
// Simulate React remounting the container: the old node (with
|
|
3218
3427
|
// our wrapper) is detached and a fresh node takes its place.
|
|
3219
|
-
const removeSpy = jest.spyOn(oldContainer, 'removeEventListener');
|
|
3220
3428
|
oldContainer.remove();
|
|
3221
3429
|
const newContainer = document.createElement('div');
|
|
3222
3430
|
newContainer.id = 'remount-container';
|
|
3223
3431
|
document.body.appendChild(newContainer);
|
|
3224
|
-
const addSpy = jest.spyOn(newContainer, 'addEventListener');
|
|
3225
3432
|
|
|
3226
3433
|
// A reposition (scroll/resize/re-show) heals the stale ref.
|
|
3227
3434
|
libEmbed.syncPreRenderStyle();
|
|
3228
3435
|
|
|
3229
3436
|
expect(newContainer.contains(wrapper)).toBe(true);
|
|
3230
3437
|
expect(oldContainer.contains(wrapper)).toBe(false);
|
|
3231
|
-
// The scroll listener is migrated to the live container.
|
|
3232
|
-
expect(removeSpy).toHaveBeenCalledWith('scroll', expect.any(Function));
|
|
3233
|
-
expect(addSpy).toHaveBeenCalledWith('scroll', expect.any(Function));
|
|
3234
3438
|
|
|
3235
3439
|
libEmbed.destroy();
|
|
3236
3440
|
newContainer.remove();
|
|
@@ -7372,3 +7576,88 @@ describe('excludeConfigFromURL', () => {
|
|
|
7372
7576
|
});
|
|
7373
7577
|
});
|
|
7374
7578
|
});
|
|
7579
|
+
|
|
7580
|
+
describe('visual-sdk-embed-create does not upload runtime filter or parameter data', () => {
|
|
7581
|
+
// Spied per test: an earlier suite calls jest.restoreAllMocks(), which
|
|
7582
|
+
// would unwire a spy created at collection time.
|
|
7583
|
+
let mockMixPanelEvent: jest.SpyInstance;
|
|
7584
|
+
|
|
7585
|
+
const runtimeFilters: RuntimeFilter[] = [
|
|
7586
|
+
{
|
|
7587
|
+
columnName: 'Patient SSN',
|
|
7588
|
+
operator: RuntimeFilterOp.EQ,
|
|
7589
|
+
values: ['123-45-6789'],
|
|
7590
|
+
},
|
|
7591
|
+
];
|
|
7592
|
+
const runtimeParameters: RuntimeParameter[] = [
|
|
7593
|
+
{
|
|
7594
|
+
name: 'Sales Region',
|
|
7595
|
+
value: 'EMEA',
|
|
7596
|
+
},
|
|
7597
|
+
];
|
|
7598
|
+
|
|
7599
|
+
beforeAll(() => {
|
|
7600
|
+
init({
|
|
7601
|
+
thoughtSpotHost: 'tshost',
|
|
7602
|
+
authType: AuthType.None,
|
|
7603
|
+
});
|
|
7604
|
+
});
|
|
7605
|
+
|
|
7606
|
+
beforeEach(() => {
|
|
7607
|
+
document.body.innerHTML = getDocumentBody();
|
|
7608
|
+
mockMixPanelEvent = jest.spyOn(mixpanelInstance, 'uploadMixpanelEvent');
|
|
7609
|
+
});
|
|
7610
|
+
|
|
7611
|
+
afterEach(() => {
|
|
7612
|
+
mockMixPanelEvent.mockRestore();
|
|
7613
|
+
});
|
|
7614
|
+
|
|
7615
|
+
const getEmbedCreateProps = () => {
|
|
7616
|
+
const call = mockMixPanelEvent.mock.calls.find(
|
|
7617
|
+
([eventId]) => eventId === MIXPANEL_EVENT.VISUAL_SDK_EMBED_CREATE,
|
|
7618
|
+
);
|
|
7619
|
+
expect(call).toBeDefined();
|
|
7620
|
+
return call[1];
|
|
7621
|
+
};
|
|
7622
|
+
|
|
7623
|
+
test('uploads the summary instead of the filters and parameters', () => {
|
|
7624
|
+
new LiveboardEmbed(getRootEl(), {
|
|
7625
|
+
liveboardId: 'lb-guid',
|
|
7626
|
+
runtimeFilters,
|
|
7627
|
+
runtimeParameters,
|
|
7628
|
+
} as LiveboardViewConfig);
|
|
7629
|
+
|
|
7630
|
+
expect(getEmbedCreateProps()).toEqual(
|
|
7631
|
+
expect.objectContaining({
|
|
7632
|
+
liveboardId: 'lb-guid',
|
|
7633
|
+
runtimeFilters: { count: 1, operators: ['EQ'] },
|
|
7634
|
+
runtimeParameters: { count: 1, applicabilityLevels: [] },
|
|
7635
|
+
}),
|
|
7636
|
+
);
|
|
7637
|
+
});
|
|
7638
|
+
|
|
7639
|
+
test('no column name, parameter name or operand reaches mixpanel', () => {
|
|
7640
|
+
new LiveboardEmbed(getRootEl(), {
|
|
7641
|
+
liveboardId: 'lb-guid',
|
|
7642
|
+
runtimeFilters,
|
|
7643
|
+
runtimeParameters,
|
|
7644
|
+
} as LiveboardViewConfig);
|
|
7645
|
+
|
|
7646
|
+
const uploaded = JSON.stringify(getEmbedCreateProps());
|
|
7647
|
+
['Patient SSN', '123-45-6789', 'Sales Region', 'EMEA'].forEach((customerValue) => {
|
|
7648
|
+
expect(uploaded).not.toContain(customerValue);
|
|
7649
|
+
});
|
|
7650
|
+
});
|
|
7651
|
+
|
|
7652
|
+
test('an embed with no runtime filters is unchanged', () => {
|
|
7653
|
+
new LiveboardEmbed(getRootEl(), {
|
|
7654
|
+
liveboardId: 'lb-guid',
|
|
7655
|
+
} as LiveboardViewConfig);
|
|
7656
|
+
|
|
7657
|
+
const props = getEmbedCreateProps();
|
|
7658
|
+
expect(props).toEqual({
|
|
7659
|
+
liveboardId: 'lb-guid',
|
|
7660
|
+
embedComponentType: 'LiveboardEmbed',
|
|
7661
|
+
});
|
|
7662
|
+
});
|
|
7663
|
+
});
|