@thoughtspot/visual-embed-sdk 1.46.4 → 1.46.5
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 +3 -3
- package/cjs/src/auth.d.ts +2 -1
- package/cjs/src/auth.d.ts.map +1 -1
- package/cjs/src/auth.js +2 -1
- package/cjs/src/auth.js.map +1 -1
- package/cjs/src/authToken.d.ts.map +1 -1
- package/cjs/src/authToken.js.map +1 -1
- package/cjs/src/css-variables.d.ts +87 -17
- package/cjs/src/css-variables.d.ts.map +1 -1
- package/cjs/src/embed/app.d.ts +41 -1
- package/cjs/src/embed/app.d.ts.map +1 -1
- package/cjs/src/embed/app.js +25 -36
- package/cjs/src/embed/app.js.map +1 -1
- package/cjs/src/embed/app.spec.js +35 -23
- package/cjs/src/embed/app.spec.js.map +1 -1
- package/cjs/src/embed/base.d.ts.map +1 -1
- package/cjs/src/embed/base.js.map +1 -1
- package/cjs/src/embed/base.spec.js.map +1 -1
- package/cjs/src/embed/conversation.d.ts +23 -1
- package/cjs/src/embed/conversation.d.ts.map +1 -1
- package/cjs/src/embed/conversation.js +18 -33
- package/cjs/src/embed/conversation.js.map +1 -1
- package/cjs/src/embed/conversation.spec.js +129 -97
- package/cjs/src/embed/conversation.spec.js.map +1 -1
- package/cjs/src/embed/events.spec.js +72 -0
- package/cjs/src/embed/events.spec.js.map +1 -1
- package/cjs/src/embed/hostEventClient/contracts.d.ts +105 -3
- package/cjs/src/embed/hostEventClient/contracts.d.ts.map +1 -1
- package/cjs/src/embed/hostEventClient/contracts.js +9 -0
- package/cjs/src/embed/hostEventClient/contracts.js.map +1 -1
- package/cjs/src/embed/hostEventClient/host-event-client.d.ts +28 -0
- package/cjs/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
- package/cjs/src/embed/hostEventClient/host-event-client.js +106 -9
- package/cjs/src/embed/hostEventClient/host-event-client.js.map +1 -1
- package/cjs/src/embed/hostEventClient/host-event-client.spec.js +327 -6
- package/cjs/src/embed/hostEventClient/host-event-client.spec.js.map +1 -1
- package/cjs/src/embed/hostEventClient/utils.d.ts +22 -0
- package/cjs/src/embed/hostEventClient/utils.d.ts.map +1 -0
- package/cjs/src/embed/hostEventClient/utils.js +51 -0
- package/cjs/src/embed/hostEventClient/utils.js.map +1 -0
- package/cjs/src/embed/hostEventClient/utils.spec.d.ts +2 -0
- package/cjs/src/embed/hostEventClient/utils.spec.d.ts.map +1 -0
- package/cjs/src/embed/hostEventClient/utils.spec.js +115 -0
- package/cjs/src/embed/hostEventClient/utils.spec.js.map +1 -0
- package/cjs/src/embed/liveboard.d.ts +18 -0
- package/cjs/src/embed/liveboard.d.ts.map +1 -1
- package/cjs/src/embed/liveboard.js +10 -3
- package/cjs/src/embed/liveboard.js.map +1 -1
- package/cjs/src/embed/liveboard.spec.js +54 -2
- package/cjs/src/embed/liveboard.spec.js.map +1 -1
- package/cjs/src/embed/sage.d.ts.map +1 -1
- package/cjs/src/embed/sage.js.map +1 -1
- package/cjs/src/embed/search.spec.js.map +1 -1
- package/cjs/src/embed/spotter-utils.d.ts +20 -0
- package/cjs/src/embed/spotter-utils.d.ts.map +1 -0
- package/cjs/src/embed/spotter-utils.js +52 -0
- package/cjs/src/embed/spotter-utils.js.map +1 -0
- package/cjs/src/embed/spotter-utils.spec.d.ts +2 -0
- package/cjs/src/embed/spotter-utils.spec.d.ts.map +1 -0
- package/cjs/src/embed/spotter-utils.spec.js +54 -0
- package/cjs/src/embed/spotter-utils.spec.js.map +1 -0
- package/cjs/src/embed/ts-embed.d.ts +41 -2
- package/cjs/src/embed/ts-embed.d.ts.map +1 -1
- package/cjs/src/embed/ts-embed.js +54 -3
- package/cjs/src/embed/ts-embed.js.map +1 -1
- package/cjs/src/errors.d.ts +2 -0
- package/cjs/src/errors.d.ts.map +1 -1
- package/cjs/src/errors.js +2 -0
- package/cjs/src/errors.js.map +1 -1
- package/cjs/src/react/index.d.ts.map +1 -1
- package/cjs/src/react/index.js +58 -53
- package/cjs/src/react/index.js.map +1 -1
- package/cjs/src/types.d.ts +832 -33
- package/cjs/src/types.d.ts.map +1 -1
- package/cjs/src/types.js +835 -3
- package/cjs/src/types.js.map +1 -1
- package/cjs/src/utils/graphql/answerService/answerService.d.ts +4 -2
- package/cjs/src/utils/graphql/answerService/answerService.d.ts.map +1 -1
- package/cjs/src/utils/graphql/answerService/answerService.js +4 -2
- package/cjs/src/utils/graphql/answerService/answerService.js.map +1 -1
- package/cjs/src/utils/graphql/preview-service.d.ts.map +1 -1
- package/cjs/src/utils/graphql/preview-service.js.map +1 -1
- package/cjs/src/utils/processData.d.ts.map +1 -1
- package/cjs/src/utils/processData.js.map +1 -1
- package/cjs/src/utils.d.ts +0 -9
- package/cjs/src/utils.d.ts.map +1 -1
- package/cjs/src/utils.js +1 -10
- package/cjs/src/utils.js.map +1 -1
- package/dist/index-ChNydfIz.js +7371 -0
- package/dist/index-DW2wEHqy.js +7371 -0
- package/dist/src/auth.d.ts +2 -1
- package/dist/src/auth.d.ts.map +1 -1
- package/dist/src/authToken.d.ts.map +1 -1
- package/dist/src/css-variables.d.ts +87 -17
- package/dist/src/css-variables.d.ts.map +1 -1
- package/dist/src/embed/app.d.ts +41 -1
- package/dist/src/embed/app.d.ts.map +1 -1
- package/dist/src/embed/base.d.ts.map +1 -1
- package/dist/src/embed/conversation.d.ts +23 -1
- package/dist/src/embed/conversation.d.ts.map +1 -1
- package/dist/src/embed/hostEventClient/contracts.d.ts +105 -3
- package/dist/src/embed/hostEventClient/contracts.d.ts.map +1 -1
- package/dist/src/embed/hostEventClient/host-event-client.d.ts +28 -0
- package/dist/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
- package/dist/src/embed/hostEventClient/utils.d.ts +22 -0
- package/dist/src/embed/hostEventClient/utils.d.ts.map +1 -0
- package/dist/src/embed/hostEventClient/utils.spec.d.ts +2 -0
- package/dist/src/embed/hostEventClient/utils.spec.d.ts.map +1 -0
- package/dist/src/embed/liveboard.d.ts +18 -0
- package/dist/src/embed/liveboard.d.ts.map +1 -1
- package/dist/src/embed/sage.d.ts.map +1 -1
- package/dist/src/embed/spotter-utils.d.ts +20 -0
- package/dist/src/embed/spotter-utils.d.ts.map +1 -0
- package/dist/src/embed/spotter-utils.spec.d.ts +2 -0
- package/dist/src/embed/spotter-utils.spec.d.ts.map +1 -0
- package/dist/src/embed/ts-embed.d.ts +41 -2
- package/dist/src/embed/ts-embed.d.ts.map +1 -1
- package/dist/src/errors.d.ts +2 -0
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/react/index.d.ts.map +1 -1
- package/dist/src/types.d.ts +832 -33
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/graphql/answerService/answerService.d.ts +4 -2
- package/dist/src/utils/graphql/answerService/answerService.d.ts.map +1 -1
- package/dist/src/utils/graphql/preview-service.d.ts.map +1 -1
- package/dist/src/utils/processData.d.ts.map +1 -1
- package/dist/src/utils.d.ts +0 -9
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/tsembed-react.es.js +1208 -154
- package/dist/tsembed-react.js +1206 -152
- package/dist/tsembed.es.js +1150 -101
- package/dist/tsembed.js +1148 -99
- package/dist/visual-embed-sdk-react-full.d.ts +1156 -55
- package/dist/visual-embed-sdk-react.d.ts +1156 -55
- package/dist/visual-embed-sdk.d.ts +1179 -60
- package/lib/package.json +3 -3
- package/lib/src/auth.d.ts +2 -1
- package/lib/src/auth.d.ts.map +1 -1
- package/lib/src/auth.js +2 -1
- package/lib/src/auth.js.map +1 -1
- package/lib/src/authToken.d.ts.map +1 -1
- package/lib/src/authToken.js.map +1 -1
- package/lib/src/css-variables.d.ts +87 -17
- package/lib/src/css-variables.d.ts.map +1 -1
- package/lib/src/embed/app.d.ts +41 -1
- package/lib/src/embed/app.d.ts.map +1 -1
- package/lib/src/embed/app.js +27 -38
- package/lib/src/embed/app.js.map +1 -1
- package/lib/src/embed/app.spec.js +35 -23
- package/lib/src/embed/app.spec.js.map +1 -1
- package/lib/src/embed/base.d.ts.map +1 -1
- package/lib/src/embed/base.js.map +1 -1
- package/lib/src/embed/base.spec.js.map +1 -1
- package/lib/src/embed/conversation.d.ts +23 -1
- package/lib/src/embed/conversation.d.ts.map +1 -1
- package/lib/src/embed/conversation.js +19 -34
- package/lib/src/embed/conversation.js.map +1 -1
- package/lib/src/embed/conversation.spec.js +131 -99
- package/lib/src/embed/conversation.spec.js.map +1 -1
- package/lib/src/embed/events.spec.js +73 -1
- package/lib/src/embed/events.spec.js.map +1 -1
- package/lib/src/embed/hostEventClient/contracts.d.ts +105 -3
- package/lib/src/embed/hostEventClient/contracts.d.ts.map +1 -1
- package/lib/src/embed/hostEventClient/contracts.js +9 -0
- package/lib/src/embed/hostEventClient/contracts.js.map +1 -1
- package/lib/src/embed/hostEventClient/host-event-client.d.ts +28 -0
- package/lib/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
- package/lib/src/embed/hostEventClient/host-event-client.js +106 -9
- package/lib/src/embed/hostEventClient/host-event-client.js.map +1 -1
- package/lib/src/embed/hostEventClient/host-event-client.spec.js +327 -6
- package/lib/src/embed/hostEventClient/host-event-client.spec.js.map +1 -1
- package/lib/src/embed/hostEventClient/utils.d.ts +22 -0
- package/lib/src/embed/hostEventClient/utils.d.ts.map +1 -0
- package/lib/src/embed/hostEventClient/utils.js +43 -0
- package/lib/src/embed/hostEventClient/utils.js.map +1 -0
- package/lib/src/embed/hostEventClient/utils.spec.d.ts +2 -0
- package/lib/src/embed/hostEventClient/utils.spec.d.ts.map +1 -0
- package/lib/src/embed/hostEventClient/utils.spec.js +113 -0
- package/lib/src/embed/hostEventClient/utils.spec.js.map +1 -0
- package/lib/src/embed/liveboard.d.ts +18 -0
- package/lib/src/embed/liveboard.d.ts.map +1 -1
- package/lib/src/embed/liveboard.js +10 -3
- package/lib/src/embed/liveboard.js.map +1 -1
- package/lib/src/embed/liveboard.spec.js +54 -2
- package/lib/src/embed/liveboard.spec.js.map +1 -1
- package/lib/src/embed/sage.d.ts.map +1 -1
- package/lib/src/embed/sage.js.map +1 -1
- package/lib/src/embed/search.spec.js.map +1 -1
- package/lib/src/embed/spotter-utils.d.ts +20 -0
- package/lib/src/embed/spotter-utils.d.ts.map +1 -0
- package/lib/src/embed/spotter-utils.js +47 -0
- package/lib/src/embed/spotter-utils.js.map +1 -0
- package/lib/src/embed/spotter-utils.spec.d.ts +2 -0
- package/lib/src/embed/spotter-utils.spec.d.ts.map +1 -0
- package/lib/src/embed/spotter-utils.spec.js +52 -0
- package/lib/src/embed/spotter-utils.spec.js.map +1 -0
- package/lib/src/embed/ts-embed.d.ts +41 -2
- package/lib/src/embed/ts-embed.d.ts.map +1 -1
- package/lib/src/embed/ts-embed.js +55 -4
- package/lib/src/embed/ts-embed.js.map +1 -1
- package/lib/src/errors.d.ts +2 -0
- package/lib/src/errors.d.ts.map +1 -1
- package/lib/src/errors.js +2 -0
- package/lib/src/errors.js.map +1 -1
- package/lib/src/react/index.d.ts.map +1 -1
- package/lib/src/react/index.js +58 -53
- package/lib/src/react/index.js.map +1 -1
- package/lib/src/types.d.ts +832 -33
- package/lib/src/types.d.ts.map +1 -1
- package/lib/src/types.js +835 -3
- package/lib/src/types.js.map +1 -1
- package/lib/src/utils/graphql/answerService/answerService.d.ts +4 -2
- package/lib/src/utils/graphql/answerService/answerService.d.ts.map +1 -1
- package/lib/src/utils/graphql/answerService/answerService.js +4 -2
- package/lib/src/utils/graphql/answerService/answerService.js.map +1 -1
- package/lib/src/utils/graphql/preview-service.d.ts.map +1 -1
- package/lib/src/utils/graphql/preview-service.js.map +1 -1
- package/lib/src/utils/processData.d.ts.map +1 -1
- package/lib/src/utils/processData.js.map +1 -1
- package/lib/src/utils.d.ts +0 -9
- package/lib/src/utils.d.ts.map +1 -1
- package/lib/src/utils.js +0 -8
- package/lib/src/utils.js.map +1 -1
- package/lib/src/visual-embed-sdk.d.ts +1179 -60
- package/package.json +3 -3
- package/src/auth.spec.ts +1 -1
- package/src/auth.ts +2 -1
- package/src/authToken.ts +0 -1
- package/src/css-variables.ts +96 -17
- package/src/embed/app.spec.ts +48 -30
- package/src/embed/app.ts +59 -54
- package/src/embed/base.spec.ts +1 -2
- package/src/embed/base.ts +1 -4
- package/src/embed/conversation.spec.ts +150 -119
- package/src/embed/conversation.ts +30 -54
- package/src/embed/events.spec.ts +88 -0
- package/src/embed/hostEventClient/contracts.ts +105 -2
- package/src/embed/hostEventClient/host-event-client.spec.ts +504 -6
- package/src/embed/hostEventClient/host-event-client.ts +146 -15
- package/src/embed/hostEventClient/utils.spec.ts +137 -0
- package/src/embed/hostEventClient/utils.ts +61 -0
- package/src/embed/liveboard.spec.ts +71 -2
- package/src/embed/liveboard.ts +29 -2
- package/src/embed/sage.ts +0 -1
- package/src/embed/search.spec.ts +0 -2
- package/src/embed/spotter-utils.spec.ts +56 -0
- package/src/embed/spotter-utils.ts +65 -0
- package/src/embed/ts-embed.ts +58 -4
- package/src/errors.ts +2 -0
- package/src/react/index.tsx +76 -72
- package/src/types.ts +843 -31
- package/src/utils/graphql/answerService/answerService.ts +4 -5
- package/src/utils/graphql/preview-service.ts +0 -1
- package/src/utils/processData.ts +0 -5
- package/src/utils.ts +0 -14
|
@@ -1,20 +1,65 @@
|
|
|
1
1
|
import { ContextType, HostEvent } from '../../types';
|
|
2
2
|
import { processTrigger as processTriggerService } from '../../utils/processTrigger';
|
|
3
3
|
import { getEmbedConfig } from '../embedConfig';
|
|
4
|
+
import {
|
|
5
|
+
isValidUpdateFiltersPayload,
|
|
6
|
+
isValidDrillDownPayload,
|
|
7
|
+
throwUpdateFiltersValidationError,
|
|
8
|
+
throwDrillDownValidationError,
|
|
9
|
+
} from './utils';
|
|
4
10
|
import {
|
|
5
11
|
UIPassthroughArrayResponse,
|
|
6
|
-
UIPassthroughEvent,
|
|
12
|
+
UIPassthroughEvent,
|
|
13
|
+
HostEventRequest,
|
|
14
|
+
HostEventResponse,
|
|
7
15
|
UIPassthroughRequest,
|
|
8
16
|
UIPassthroughResponse,
|
|
9
17
|
TriggerPayload,
|
|
10
18
|
TriggerResponse,
|
|
11
19
|
} from './contracts';
|
|
12
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Maps HostEvent to its corresponding UIPassthroughEvent.
|
|
23
|
+
* Includes both custom-handler events (Pin, SaveAnswer, UpdateFilters, DrillDown)
|
|
24
|
+
* and getter events (GetAnswerSession, GetFilters, etc.) that use getDataWithPassthroughFallback.
|
|
25
|
+
*/
|
|
26
|
+
const PASSTHROUGH_MAP: Partial<Record<HostEvent, UIPassthroughEvent>> = {
|
|
27
|
+
// Custom handlers (setters with special logic)
|
|
28
|
+
[HostEvent.Pin]: UIPassthroughEvent.PinAnswerToLiveboard,
|
|
29
|
+
[HostEvent.SaveAnswer]: UIPassthroughEvent.SaveAnswer,
|
|
30
|
+
[HostEvent.UpdateFilters]: UIPassthroughEvent.UpdateFilters,
|
|
31
|
+
[HostEvent.DrillDown]: UIPassthroughEvent.Drilldown,
|
|
32
|
+
// Getters (use getDataWithPassthroughFallback)
|
|
33
|
+
[HostEvent.GetAnswerSession]: UIPassthroughEvent.GetAnswerSession,
|
|
34
|
+
[HostEvent.GetFilters]: UIPassthroughEvent.GetFilters,
|
|
35
|
+
[HostEvent.GetIframeUrl]: UIPassthroughEvent.GetIframeUrl,
|
|
36
|
+
[HostEvent.GetParameters]: UIPassthroughEvent.GetParameters,
|
|
37
|
+
[HostEvent.GetTML]: UIPassthroughEvent.GetTML,
|
|
38
|
+
[HostEvent.GetTabs]: UIPassthroughEvent.GetTabs,
|
|
39
|
+
[HostEvent.getExportRequestForCurrentPinboard]: UIPassthroughEvent.GetExportRequestForCurrentPinboard,
|
|
40
|
+
};
|
|
41
|
+
|
|
13
42
|
export class HostEventClient {
|
|
14
43
|
iFrame: HTMLIFrameElement;
|
|
15
44
|
|
|
45
|
+
/** Cached list of available UI passthrough keys from the embedded app */
|
|
46
|
+
private availablePassthroughKeysCache: string[] | null = null;
|
|
47
|
+
|
|
48
|
+
/** Host events with custom handlers
|
|
49
|
+
* (setters or special logic) -
|
|
50
|
+
* bound to instance for protected method access */
|
|
51
|
+
private readonly customHandlers: Partial<
|
|
52
|
+
Record<HostEvent, (payload: any, context?: ContextType) => Promise<any>>
|
|
53
|
+
>;
|
|
54
|
+
|
|
16
55
|
constructor(iFrame?: HTMLIFrameElement) {
|
|
17
56
|
this.iFrame = iFrame;
|
|
57
|
+
this.customHandlers = {
|
|
58
|
+
[HostEvent.Pin]: (p, c) => this.handlePinEvent(p, c),
|
|
59
|
+
[HostEvent.SaveAnswer]: (p, c) => this.handleSaveAnswerEvent(p, c),
|
|
60
|
+
[HostEvent.UpdateFilters]: (p, c) => this.handleUpdateFiltersEvent(p, c),
|
|
61
|
+
[HostEvent.DrillDown]: (p, c) => this.handleDrillDownEvent(p, c),
|
|
62
|
+
};
|
|
18
63
|
}
|
|
19
64
|
|
|
20
65
|
/**
|
|
@@ -44,11 +89,10 @@ export class HostEventClient {
|
|
|
44
89
|
context?: ContextType,
|
|
45
90
|
): Promise<UIPassthroughResponse<UIPassthroughEventT>> {
|
|
46
91
|
const response = (await this.triggerUIPassthroughApi(apiName, parameters, context))
|
|
47
|
-
?.
|
|
92
|
+
?.find?.((r) => r.error || r.value);
|
|
48
93
|
|
|
49
94
|
if (!response) {
|
|
50
95
|
const error = `No answer found${parameters.vizId ? ` for vizId: ${parameters.vizId}` : ''}.`;
|
|
51
|
-
|
|
52
96
|
throw { error };
|
|
53
97
|
}
|
|
54
98
|
|
|
@@ -57,8 +101,8 @@ export class HostEventClient {
|
|
|
57
101
|
|| (response.value as any)?.error;
|
|
58
102
|
|
|
59
103
|
if (errors) {
|
|
60
|
-
|
|
61
|
-
throw { error:
|
|
104
|
+
const message = typeof errors === 'string' ? errors : JSON.stringify(errors);
|
|
105
|
+
throw { error: message };
|
|
62
106
|
}
|
|
63
107
|
|
|
64
108
|
return { ...response.value };
|
|
@@ -72,6 +116,36 @@ export class HostEventClient {
|
|
|
72
116
|
return this.processTrigger(hostEvent, data, context);
|
|
73
117
|
}
|
|
74
118
|
|
|
119
|
+
/**
|
|
120
|
+
* For getter events that return data. Tries UI passthrough first;
|
|
121
|
+
* if the app doesn't support it (no response data), falls back to
|
|
122
|
+
* the legacy host event channel. Real errors are thrown as-is.
|
|
123
|
+
*/
|
|
124
|
+
private async getDataWithPassthroughFallback<UIPassthroughEventT extends UIPassthroughEvent>(
|
|
125
|
+
passthroughEvent: UIPassthroughEventT,
|
|
126
|
+
hostEvent: HostEvent,
|
|
127
|
+
payload: any,
|
|
128
|
+
context?: ContextType,
|
|
129
|
+
): Promise<UIPassthroughResponse<UIPassthroughEventT>> {
|
|
130
|
+
const response = await this.triggerUIPassthroughApi(
|
|
131
|
+
passthroughEvent, payload || {}, context,
|
|
132
|
+
);
|
|
133
|
+
const matched = response?.find?.((r) => r.error || r.value);
|
|
134
|
+
if (!matched) {
|
|
135
|
+
return this.hostEventFallback(hostEvent, payload, context);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const errors = matched.error
|
|
139
|
+
|| (matched.value as any)?.errors
|
|
140
|
+
|| (matched.value as any)?.error;
|
|
141
|
+
if (errors) {
|
|
142
|
+
const message = typeof errors === 'string' ? errors : JSON.stringify(errors);
|
|
143
|
+
throw new Error(message);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return { ...matched.value };
|
|
147
|
+
}
|
|
148
|
+
|
|
75
149
|
/**
|
|
76
150
|
* Setter for the iframe element used for host events
|
|
77
151
|
* @param {HTMLIFrameElement} iFrame - the iframe element to set
|
|
@@ -80,6 +154,27 @@ export class HostEventClient {
|
|
|
80
154
|
this.iFrame = iFrame;
|
|
81
155
|
}
|
|
82
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Fetches the list of available UI passthrough keys from the embedded app.
|
|
159
|
+
* Result is cached for the session. Returns empty array on failure.
|
|
160
|
+
*/
|
|
161
|
+
private async getAvailableUIPassthroughKeys(context?: ContextType): Promise<string[]> {
|
|
162
|
+
if (this.availablePassthroughKeysCache !== null) {
|
|
163
|
+
return this.availablePassthroughKeysCache;
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
const response = await this.triggerUIPassthroughApi(
|
|
167
|
+
UIPassthroughEvent.GetAvailableUIPassthroughs, {}, context,
|
|
168
|
+
);
|
|
169
|
+
const matched = response?.find?.((r) => r.value && !r.error);
|
|
170
|
+
const keys = matched?.value?.keys;
|
|
171
|
+
this.availablePassthroughKeysCache = Array.isArray(keys) ? keys : [];
|
|
172
|
+
return this.availablePassthroughKeysCache;
|
|
173
|
+
} catch {
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
83
178
|
public async triggerUIPassthroughApi<UIPassthroughEventT extends UIPassthroughEvent>(
|
|
84
179
|
apiName: UIPassthroughEventT,
|
|
85
180
|
parameters: UIPassthroughRequest<UIPassthroughEventT>,
|
|
@@ -137,6 +232,37 @@ export class HostEventClient {
|
|
|
137
232
|
};
|
|
138
233
|
}
|
|
139
234
|
|
|
235
|
+
protected handleUpdateFiltersEvent(
|
|
236
|
+
payload: HostEventRequest<HostEvent.UpdateFilters>,
|
|
237
|
+
context?: ContextType,
|
|
238
|
+
): Promise<any> {
|
|
239
|
+
if (!isValidUpdateFiltersPayload(payload)) {
|
|
240
|
+
throwUpdateFiltersValidationError();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return this.handleHostEventWithParam(UIPassthroughEvent.UpdateFilters, payload, context as ContextType);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
protected handleDrillDownEvent(
|
|
247
|
+
payload: HostEventRequest<HostEvent.DrillDown>,
|
|
248
|
+
context?: ContextType,
|
|
249
|
+
): Promise<any> {
|
|
250
|
+
if (!isValidDrillDownPayload(payload)) {
|
|
251
|
+
throwDrillDownValidationError();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return this.handleHostEventWithParam(UIPassthroughEvent.Drilldown, payload, context as ContextType);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Dispatches a host event using the appropriate channel:
|
|
259
|
+
* 1. If the embedded app supports UI passthrough for this event, use it (custom handler or getter).
|
|
260
|
+
* 2. Otherwise fall back to the legacy host event channel.
|
|
261
|
+
*
|
|
262
|
+
* @param hostEvent - The host event to trigger
|
|
263
|
+
* @param payload - Optional payload for the event
|
|
264
|
+
* @param context - Optional context (e.g. vizId) for scoped operations
|
|
265
|
+
*/
|
|
140
266
|
public async triggerHostEvent<
|
|
141
267
|
HostEventT extends HostEvent,
|
|
142
268
|
PayloadT,
|
|
@@ -146,16 +272,21 @@ export class HostEventClient {
|
|
|
146
272
|
payload?: TriggerPayload<PayloadT, HostEventT>,
|
|
147
273
|
context?: ContextT,
|
|
148
274
|
): Promise<TriggerResponse<PayloadT, HostEventT, ContextType>> {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
) as any;
|
|
157
|
-
default:
|
|
158
|
-
return this.hostEventFallback(hostEvent, payload, context);
|
|
275
|
+
const customHandler = this.customHandlers[hostEvent];
|
|
276
|
+
const passthroughEvent = PASSTHROUGH_MAP[hostEvent];
|
|
277
|
+
|
|
278
|
+
// If embedded app supports passthrough but not this event, use legacy channel
|
|
279
|
+
const keys = passthroughEvent ? await this.getAvailableUIPassthroughKeys(context as ContextType) : [];
|
|
280
|
+
if (passthroughEvent && keys.length > 0 && !keys.includes(passthroughEvent)) {
|
|
281
|
+
return this.hostEventFallback(hostEvent, payload, context) as any;
|
|
159
282
|
}
|
|
283
|
+
|
|
284
|
+
// Custom handler (setters) > getter passthrough > legacy fallback
|
|
285
|
+
return (customHandler
|
|
286
|
+
? customHandler(payload, context as ContextType)
|
|
287
|
+
: passthroughEvent
|
|
288
|
+
? this.getDataWithPassthroughFallback(passthroughEvent, hostEvent, payload, context as ContextType)
|
|
289
|
+
: this.hostEventFallback(hostEvent, payload, context)
|
|
290
|
+
) as any;
|
|
160
291
|
}
|
|
161
292
|
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isValidUpdateFiltersPayload,
|
|
3
|
+
isValidDrillDownPayload,
|
|
4
|
+
createValidationError,
|
|
5
|
+
throwUpdateFiltersValidationError,
|
|
6
|
+
throwDrillDownValidationError,
|
|
7
|
+
} from './utils';
|
|
8
|
+
import { ERROR_MESSAGE } from '../../errors';
|
|
9
|
+
import { EmbedEvent } from '../../types';
|
|
10
|
+
import { embedEventStatus } from '../../utils';
|
|
11
|
+
|
|
12
|
+
describe('hostEventClient utils', () => {
|
|
13
|
+
describe('isValidUpdateFiltersPayload', () => {
|
|
14
|
+
it('returns false for undefined', () => {
|
|
15
|
+
expect(isValidUpdateFiltersPayload(undefined)).toBe(false);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('returns false for empty payload', () => {
|
|
19
|
+
expect(isValidUpdateFiltersPayload({})).toBe(false);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('returns true for valid filter', () => {
|
|
23
|
+
expect(isValidUpdateFiltersPayload({
|
|
24
|
+
filter: { column: 'region', oper: 'EQ', values: ['North'] },
|
|
25
|
+
} as any)).toBe(true);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('returns true for valid filters array', () => {
|
|
29
|
+
expect(isValidUpdateFiltersPayload({
|
|
30
|
+
filters: [
|
|
31
|
+
{ column: 'x', oper: 'IN', values: ['a', 'b'] },
|
|
32
|
+
{ column: 'y', oper: 'EQ', values: ['c'] },
|
|
33
|
+
],
|
|
34
|
+
} as any)).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('returns false for filter with missing column', () => {
|
|
38
|
+
expect(isValidUpdateFiltersPayload({
|
|
39
|
+
filter: { oper: 'EQ', values: ['a'] },
|
|
40
|
+
} as any)).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('returns false for filter with missing oper', () => {
|
|
44
|
+
expect(isValidUpdateFiltersPayload({
|
|
45
|
+
filter: { column: 'x', values: ['a'] },
|
|
46
|
+
} as any)).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('returns false for filter with non-array values', () => {
|
|
50
|
+
expect(isValidUpdateFiltersPayload({
|
|
51
|
+
filter: { column: 'x', oper: 'EQ', values: 'a' },
|
|
52
|
+
} as any)).toBe(false);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('returns false for empty filters array', () => {
|
|
56
|
+
expect(isValidUpdateFiltersPayload({ filters: [] } as any)).toBe(false);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('isValidDrillDownPayload', () => {
|
|
61
|
+
it('returns false for undefined', () => {
|
|
62
|
+
expect(isValidDrillDownPayload(undefined)).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('returns false for empty payload', () => {
|
|
66
|
+
expect(isValidDrillDownPayload({})).toBe(false);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('returns false for empty points', () => {
|
|
70
|
+
expect(isValidDrillDownPayload({ points: {} } as any)).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('returns false for non-object points', () => {
|
|
74
|
+
expect(isValidDrillDownPayload({ points: 'invalid' } as any)).toBe(false);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('returns true for clickedPoint', () => {
|
|
78
|
+
expect(isValidDrillDownPayload({
|
|
79
|
+
points: { clickedPoint: 'point-1' },
|
|
80
|
+
} as any)).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('returns true for selectedPoints', () => {
|
|
84
|
+
expect(isValidDrillDownPayload({
|
|
85
|
+
points: { selectedPoints: ['p1', 'p2'] },
|
|
86
|
+
} as any)).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('returns true for both clickedPoint and selectedPoints', () => {
|
|
90
|
+
expect(isValidDrillDownPayload({
|
|
91
|
+
points: { clickedPoint: 'p1', selectedPoints: ['p2'] },
|
|
92
|
+
} as any)).toBe(true);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('returns false for selectedPoints empty array', () => {
|
|
96
|
+
expect(isValidDrillDownPayload({
|
|
97
|
+
points: { selectedPoints: [] },
|
|
98
|
+
} as any)).toBe(false);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('createValidationError', () => {
|
|
103
|
+
it('throws with message and embedErrorDetails', () => {
|
|
104
|
+
expect(() => createValidationError('test error')).toThrow('test error');
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
createValidationError('custom msg');
|
|
108
|
+
} catch (err: any) {
|
|
109
|
+
expect(err.isValidationError).toBe(true);
|
|
110
|
+
expect(err.embedErrorDetails).toMatchObject({
|
|
111
|
+
type: EmbedEvent.Error,
|
|
112
|
+
data: {
|
|
113
|
+
errorType: 'VALIDATION_ERROR',
|
|
114
|
+
message: 'custom msg',
|
|
115
|
+
code: 'HOST_EVENT_VALIDATION',
|
|
116
|
+
error: 'custom msg',
|
|
117
|
+
},
|
|
118
|
+
status: embedEventStatus.END,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe('throwUpdateFiltersValidationError', () => {
|
|
125
|
+
it('throws with UPDATEFILTERS_INVALID_PAYLOAD message', () => {
|
|
126
|
+
expect(() => throwUpdateFiltersValidationError())
|
|
127
|
+
.toThrow(ERROR_MESSAGE.UPDATEFILTERS_INVALID_PAYLOAD);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe('throwDrillDownValidationError', () => {
|
|
132
|
+
it('throws with DRILLDOWN_INVALID_PAYLOAD message', () => {
|
|
133
|
+
expect(() => throwDrillDownValidationError())
|
|
134
|
+
.toThrow(ERROR_MESSAGE.DRILLDOWN_INVALID_PAYLOAD);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { EmbedErrorCodes, EmbedEvent, ErrorDetailsTypes, HostEvent } from '../../types';
|
|
2
|
+
import { ERROR_MESSAGE } from '../../errors';
|
|
3
|
+
import { HostEventRequest } from './contracts';
|
|
4
|
+
import { embedEventStatus } from '../../utils';
|
|
5
|
+
|
|
6
|
+
export function isValidUpdateFiltersPayload(
|
|
7
|
+
payload: HostEventRequest<HostEvent.UpdateFilters> | undefined,
|
|
8
|
+
): boolean {
|
|
9
|
+
if (!payload) return false;
|
|
10
|
+
|
|
11
|
+
const isValidFilter = (f: { column?: string; oper?: string; values?: unknown[] }) =>
|
|
12
|
+
!!f && typeof f.column === 'string' && typeof f.oper === 'string' && Array.isArray(f.values);
|
|
13
|
+
|
|
14
|
+
const hasValidFilter = payload.filter && isValidFilter(payload.filter);
|
|
15
|
+
const hasValidFilters = Array.isArray(payload.filters) && payload.filters.length > 0 && payload.filters.every(isValidFilter);
|
|
16
|
+
|
|
17
|
+
return !!(hasValidFilter || hasValidFilters);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function isValidDrillDownPayload(
|
|
21
|
+
payload: HostEventRequest<HostEvent.DrillDown> | undefined,
|
|
22
|
+
): boolean {
|
|
23
|
+
if (!payload) return false;
|
|
24
|
+
|
|
25
|
+
const points = payload.points;
|
|
26
|
+
if (!points || typeof points !== 'object') return false;
|
|
27
|
+
|
|
28
|
+
const hasClickedPoint = 'clickedPoint' in points && points.clickedPoint != null;
|
|
29
|
+
const hasSelectedPoints = Array.isArray(points.selectedPoints) && points.selectedPoints.length > 0;
|
|
30
|
+
|
|
31
|
+
return hasClickedPoint || hasSelectedPoints;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type ValidationError = Error & {
|
|
35
|
+
isValidationError?: boolean;
|
|
36
|
+
embedErrorDetails?: { type: EmbedEvent.Error; data: { errorType: ErrorDetailsTypes; message: string; code: EmbedErrorCodes; error: string }; status: typeof embedEventStatus.END };
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export function createValidationError(message: string): never {
|
|
40
|
+
const err = new Error(message) as ValidationError;
|
|
41
|
+
err.isValidationError = true;
|
|
42
|
+
err.embedErrorDetails = {
|
|
43
|
+
type: EmbedEvent.Error,
|
|
44
|
+
data:{
|
|
45
|
+
errorType: ErrorDetailsTypes.VALIDATION_ERROR,
|
|
46
|
+
message,
|
|
47
|
+
code: EmbedErrorCodes.HOST_EVENT_VALIDATION,
|
|
48
|
+
error: message
|
|
49
|
+
},
|
|
50
|
+
status:embedEventStatus.END
|
|
51
|
+
};
|
|
52
|
+
throw err;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function throwUpdateFiltersValidationError(): never {
|
|
56
|
+
createValidationError(ERROR_MESSAGE.UPDATEFILTERS_INVALID_PAYLOAD);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function throwDrillDownValidationError(): never {
|
|
60
|
+
createValidationError(ERROR_MESSAGE.DRILLDOWN_INVALID_PAYLOAD);
|
|
61
|
+
}
|
|
@@ -94,6 +94,37 @@ describe('Liveboard/viz embed tests', () => {
|
|
|
94
94
|
});
|
|
95
95
|
});
|
|
96
96
|
|
|
97
|
+
test('should set disabled actions using PersonalizedViewsDropdown alias', async () => {
|
|
98
|
+
const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
|
|
99
|
+
disabledActions: [Action.PersonalizedViewsDropdown],
|
|
100
|
+
disabledActionReason: 'Action denied',
|
|
101
|
+
...defaultViewConfig,
|
|
102
|
+
liveboardId,
|
|
103
|
+
} as LiveboardViewConfig);
|
|
104
|
+
liveboardEmbed.render();
|
|
105
|
+
await executeAfterWait(() => {
|
|
106
|
+
expectUrlMatchesWithParams(
|
|
107
|
+
getIFrameSrc(),
|
|
108
|
+
`http://${thoughtSpotHost}/?embedApp=true&${defaultParamsWithoutHiddenActions}&disableAction=[%22${Action.PersonalisedViewsDropdown}%22]&disableHint=Action%20denied&hideAction=[%22${Action.ReportError}%22]${prefixParams}#/embed/viz/${liveboardId}`,
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('should set hidden actions using OrganizeFavorites alias', async () => {
|
|
114
|
+
const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
|
|
115
|
+
hiddenActions: [Action.OrganizeFavorites],
|
|
116
|
+
...defaultViewConfig,
|
|
117
|
+
liveboardId,
|
|
118
|
+
} as LiveboardViewConfig);
|
|
119
|
+
liveboardEmbed.render();
|
|
120
|
+
await executeAfterWait(() => {
|
|
121
|
+
expectUrlMatchesWithParams(
|
|
122
|
+
getIFrameSrc(),
|
|
123
|
+
`http://${thoughtSpotHost}/?embedApp=true&${defaultParamsWithoutHiddenActions}&hideAction=[%22${Action.ReportError}%22,%22${Action.OrganiseFavourites}%22]${prefixParams}#/embed/viz/${liveboardId}`,
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
97
128
|
test('should set disabled actions', async () => {
|
|
98
129
|
const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
|
|
99
130
|
disabledActions: [Action.DownloadAsCsv, Action.DownloadAsPdf, Action.DownloadAsXlsx],
|
|
@@ -229,6 +260,21 @@ describe('Liveboard/viz embed tests', () => {
|
|
|
229
260
|
});
|
|
230
261
|
});
|
|
231
262
|
|
|
263
|
+
test('should set isWYSIWYGLiveboardPDFEnabled to true in url', async () => {
|
|
264
|
+
const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
|
|
265
|
+
isContinuousLiveboardPDFEnabled: true,
|
|
266
|
+
...defaultViewConfig,
|
|
267
|
+
liveboardId,
|
|
268
|
+
} as LiveboardViewConfig);
|
|
269
|
+
liveboardEmbed.render();
|
|
270
|
+
await executeAfterWait(() => {
|
|
271
|
+
expectUrlMatchesWithParams(
|
|
272
|
+
getIFrameSrc(),
|
|
273
|
+
`http://${thoughtSpotHost}/?embedApp=true${defaultParams}&isWYSIWYGLiveboardPDFEnabled=true${prefixParams}#/embed/viz/${liveboardId}`,
|
|
274
|
+
);
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
232
278
|
test('should set isLiveboardXLSXCSVDownloadEnabled to true in url', async () => {
|
|
233
279
|
const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
|
|
234
280
|
isLiveboardXLSXCSVDownloadEnabled: true,
|
|
@@ -1011,8 +1057,8 @@ describe('Liveboard/viz embed tests', () => {
|
|
|
1011
1057
|
} as LiveboardViewConfig);
|
|
1012
1058
|
liveboardEmbed.render();
|
|
1013
1059
|
await executeAfterWait(() => {
|
|
1014
|
-
// URL: #/embed/viz/{id}/tab/{tabId}?view={viewId}
|
|
1015
|
-
// END, not middle)
|
|
1060
|
+
// URL: #/embed/viz/{id}/tab/{tabId}?view={viewId}
|
|
1061
|
+
// (view at END, not middle)
|
|
1016
1062
|
expect(getIFrameSrc()).toMatch(
|
|
1017
1063
|
new RegExp(
|
|
1018
1064
|
`#/embed/viz/${liveboardId}/tab/${activeTabId}\\?view=${workaroundViewId}`,
|
|
@@ -1624,6 +1670,29 @@ describe('Liveboard/viz embed tests', () => {
|
|
|
1624
1670
|
});
|
|
1625
1671
|
});
|
|
1626
1672
|
|
|
1673
|
+
test('should send correct visible data when RequestVisibleEmbedCoordinates is triggered', async () => {
|
|
1674
|
+
const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
|
|
1675
|
+
...defaultViewConfig,
|
|
1676
|
+
liveboardId,
|
|
1677
|
+
fullHeight: true,
|
|
1678
|
+
lazyLoadingMargin: '10px',
|
|
1679
|
+
} as LiveboardViewConfig);
|
|
1680
|
+
|
|
1681
|
+
const mockTrigger = jest.spyOn(liveboardEmbed, 'trigger');
|
|
1682
|
+
|
|
1683
|
+
await liveboardEmbed.render();
|
|
1684
|
+
|
|
1685
|
+
// Trigger the lazy load data calculation
|
|
1686
|
+
(liveboardEmbed as any).sendFullHeightLazyLoadData();
|
|
1687
|
+
|
|
1688
|
+
expect(mockTrigger).not.toHaveBeenCalledWith(HostEvent.VisibleEmbedCoordinates, {
|
|
1689
|
+
top: 0,
|
|
1690
|
+
height: 500,
|
|
1691
|
+
left: 0,
|
|
1692
|
+
width: 650,
|
|
1693
|
+
});
|
|
1694
|
+
});
|
|
1695
|
+
|
|
1627
1696
|
test('should calculate correct visible data for partially visible full height element', async () => {
|
|
1628
1697
|
mockIFrame.getBoundingClientRect = jest.fn().mockReturnValue({
|
|
1629
1698
|
top: -50,
|
package/src/embed/liveboard.ts
CHANGED
|
@@ -387,6 +387,24 @@ export interface LiveboardViewConfig extends BaseViewConfig, LiveboardOtherViewC
|
|
|
387
387
|
* ```
|
|
388
388
|
*/
|
|
389
389
|
isPNGInScheduledEmailsEnabled?: boolean;
|
|
390
|
+
/**
|
|
391
|
+
* Enables the 'what you see is what you get' PDF export for Liveboards. Each tab is rendered on a single page
|
|
392
|
+
* following the exact UI layout, instead of splitting visualizations across multiple A4 pages.
|
|
393
|
+
* This feature is GA from version 26.5.0.cl and is enabled by default on embed deployments.
|
|
394
|
+
*
|
|
395
|
+
* Supported embed types: `AppEmbed`, `LiveboardEmbed`
|
|
396
|
+
* @type {boolean}
|
|
397
|
+
* @version SDK: 1.48.0 | ThoughtSpot: 26.5.0.cl
|
|
398
|
+
* @example
|
|
399
|
+
* ```js
|
|
400
|
+
* // Replace <EmbedComponent> with embed component name. For example, AppEmbed or LiveboardEmbed
|
|
401
|
+
* const embed = new <EmbedComponent>('#tsEmbed', {
|
|
402
|
+
* ... // other embed view config
|
|
403
|
+
* isContinuousLiveboardPDFEnabled: true,
|
|
404
|
+
* })
|
|
405
|
+
* ```
|
|
406
|
+
*/
|
|
407
|
+
isContinuousLiveboardPDFEnabled?: boolean;
|
|
390
408
|
/**
|
|
391
409
|
* This flag is used to enable/disable the XLSX/CSV download option for Liveboards
|
|
392
410
|
*
|
|
@@ -598,6 +616,7 @@ export class LiveboardEmbed extends V1Embed {
|
|
|
598
616
|
updatedSpotterChatPrompt,
|
|
599
617
|
spotterChatConfig,
|
|
600
618
|
isThisPeriodInDateFiltersEnabled,
|
|
619
|
+
isContinuousLiveboardPDFEnabled,
|
|
601
620
|
} = this.viewConfig;
|
|
602
621
|
|
|
603
622
|
const preventLiveboardFilterRemoval = this.viewConfig.preventLiveboardFilterRemoval
|
|
@@ -708,6 +727,10 @@ export class LiveboardEmbed extends V1Embed {
|
|
|
708
727
|
params[Param.IsThisPeriodInDateFiltersEnabled] = isThisPeriodInDateFiltersEnabled;
|
|
709
728
|
}
|
|
710
729
|
|
|
730
|
+
if (isContinuousLiveboardPDFEnabled !== undefined) {
|
|
731
|
+
params[Param.IsWYSIWYGLiveboardPDFEnabled] = isContinuousLiveboardPDFEnabled;
|
|
732
|
+
}
|
|
733
|
+
|
|
711
734
|
params[Param.LiveboardHeaderSticky] = isLiveboardHeaderSticky;
|
|
712
735
|
params[Param.LiveboardHeaderV2] = isLiveboardCompactHeaderEnabled;
|
|
713
736
|
params[Param.ShowLiveboardVerifiedBadge] = showLiveboardVerifiedBadge;
|
|
@@ -732,7 +755,8 @@ export class LiveboardEmbed extends V1Embed {
|
|
|
732
755
|
personalizedViewId?: string,
|
|
733
756
|
) {
|
|
734
757
|
// Extract view from liveboardId if passed along with it (legacy
|
|
735
|
-
// approach)
|
|
758
|
+
// approach)
|
|
759
|
+
// View must be appended as query param at the end, not
|
|
736
760
|
// embedded in path
|
|
737
761
|
let liveboardGuid = liveboardId;
|
|
738
762
|
let legacyViewId: string | undefined;
|
|
@@ -766,7 +790,10 @@ export class LiveboardEmbed extends V1Embed {
|
|
|
766
790
|
|
|
767
791
|
private sendFullHeightLazyLoadData = () => {
|
|
768
792
|
const data = calculateVisibleElementData(this.iFrame);
|
|
769
|
-
this
|
|
793
|
+
// this should be fired only if the lazyLoadingForFullHeight and fullHeight are true
|
|
794
|
+
if(this.viewConfig.lazyLoadingForFullHeight && this.viewConfig.fullHeight){
|
|
795
|
+
this.trigger(HostEvent.VisibleEmbedCoordinates, data);
|
|
796
|
+
}
|
|
770
797
|
};
|
|
771
798
|
|
|
772
799
|
/**
|
package/src/embed/sage.ts
CHANGED
|
@@ -147,7 +147,6 @@ export class SageEmbed extends V1Embed {
|
|
|
147
147
|
*/
|
|
148
148
|
protected viewConfig: SageViewConfig;
|
|
149
149
|
|
|
150
|
-
|
|
151
150
|
constructor(domSelector: DOMSelector, viewConfig: SageViewConfig) {
|
|
152
151
|
viewConfig.embedComponentType = 'SageEmbed';
|
|
153
152
|
super(domSelector, viewConfig);
|
package/src/embed/search.spec.ts
CHANGED
|
@@ -527,7 +527,6 @@ describe('Search embed tests', () => {
|
|
|
527
527
|
test('should set dataPanelCustomGroupsAccordionInitialState to EXPAND_FIRST when passed', async () => {
|
|
528
528
|
const searchEmbed = new SearchBarEmbed(getRootEl() as any, {
|
|
529
529
|
...defaultViewConfig,
|
|
530
|
-
|
|
531
530
|
});
|
|
532
531
|
searchEmbed.render();
|
|
533
532
|
await executeAfterWait(() => {
|
|
@@ -541,7 +540,6 @@ describe('Search embed tests', () => {
|
|
|
541
540
|
test('should set dataPanelCustomGroupsAccordionInitialState to EXPAND_FIRST when passed', async () => {
|
|
542
541
|
const searchEmbed = new SearchEmbed(getRootEl(), {
|
|
543
542
|
...defaultViewConfig,
|
|
544
|
-
|
|
545
543
|
dataPanelCustomGroupsAccordionInitialState:
|
|
546
544
|
DataPanelCustomColumnGroupsAccordionState.EXPAND_FIRST,
|
|
547
545
|
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { resolveEnablePastConversationsSidebar, buildSpotterSidebarAppInitData } from './spotter-utils';
|
|
2
|
+
import { ErrorDetailsTypes, EmbedErrorCodes } from '../types';
|
|
3
|
+
import { ERROR_MESSAGE } from '../errors';
|
|
4
|
+
|
|
5
|
+
describe('resolveEnablePastConversationsSidebar', () => {
|
|
6
|
+
it('prefers spotterSidebarConfig value over standalone', () => {
|
|
7
|
+
expect(resolveEnablePastConversationsSidebar({ spotterSidebarConfigValue: true, standaloneValue: false })).toBe(true);
|
|
8
|
+
expect(resolveEnablePastConversationsSidebar({ spotterSidebarConfigValue: false, standaloneValue: true })).toBe(false);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('falls back to standalone when spotterSidebarConfig value is absent', () => {
|
|
12
|
+
expect(resolveEnablePastConversationsSidebar({ standaloneValue: true })).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('returns undefined when both are absent', () => {
|
|
16
|
+
expect(resolveEnablePastConversationsSidebar({})).toBeUndefined();
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('buildSpotterSidebarAppInitData', () => {
|
|
21
|
+
const base = { type: 'APP_INIT' } as any;
|
|
22
|
+
const noopError = jest.fn();
|
|
23
|
+
|
|
24
|
+
it('returns base unchanged when no sidebar config or standalone flag', () => {
|
|
25
|
+
const result = buildSpotterSidebarAppInitData(base, {}, noopError);
|
|
26
|
+
expect(result).toBe(base);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('nests spotterSidebarConfig under embedParams', () => {
|
|
30
|
+
const result = buildSpotterSidebarAppInitData(base, {
|
|
31
|
+
spotterSidebarConfig: { enablePastConversationsSidebar: true, spotterSidebarTitle: 'Chats' },
|
|
32
|
+
}, noopError);
|
|
33
|
+
expect(result.embedParams?.spotterSidebarConfig).toEqual({
|
|
34
|
+
enablePastConversationsSidebar: true,
|
|
35
|
+
spotterSidebarTitle: 'Chats',
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('promotes standalone flag into spotterSidebarConfig.enablePastConversationsSidebar', () => {
|
|
40
|
+
const result = buildSpotterSidebarAppInitData(base, { enablePastConversationsSidebar: true }, noopError);
|
|
41
|
+
expect(result.embedParams?.spotterSidebarConfig?.enablePastConversationsSidebar).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('calls handleError and strips spotterDocumentationUrl when invalid', () => {
|
|
45
|
+
const handleError = jest.fn();
|
|
46
|
+
const result = buildSpotterSidebarAppInitData(base, {
|
|
47
|
+
spotterSidebarConfig: { spotterDocumentationUrl: 'not-a-url' },
|
|
48
|
+
}, handleError);
|
|
49
|
+
expect(handleError).toHaveBeenCalledWith(expect.objectContaining({
|
|
50
|
+
errorType: ErrorDetailsTypes.VALIDATION_ERROR,
|
|
51
|
+
message: ERROR_MESSAGE.INVALID_SPOTTER_DOCUMENTATION_URL,
|
|
52
|
+
code: EmbedErrorCodes.INVALID_URL,
|
|
53
|
+
}));
|
|
54
|
+
expect(result.embedParams?.spotterSidebarConfig?.spotterDocumentationUrl).toBeUndefined();
|
|
55
|
+
});
|
|
56
|
+
});
|