@thoughtspot/visual-embed-sdk 1.33.12 → 1.35.1

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.
Files changed (190) hide show
  1. package/cjs/package.json +15 -3
  2. package/cjs/src/embed/app.d.ts +1 -1
  3. package/cjs/src/embed/base.native.d.ts +1 -0
  4. package/cjs/src/embed/base.native.d.ts.map +1 -0
  5. package/cjs/src/embed/base.native.js +1 -0
  6. package/cjs/src/embed/base.native.js.map +1 -0
  7. package/cjs/src/embed/hello.d.ts +2 -0
  8. package/cjs/src/embed/hello.d.ts.map +1 -0
  9. package/cjs/src/embed/hello.js +6 -0
  10. package/cjs/src/embed/hello.js.map +1 -0
  11. package/cjs/src/embed/liveboard.d.ts +1 -1
  12. package/cjs/src/embed/searchEmbed-basic-auth.spec.d.ts +2 -0
  13. package/cjs/src/embed/searchEmbed-basic-auth.spec.d.ts.map +1 -0
  14. package/cjs/src/embed/searchEmbed-basic-auth.spec.js +104 -0
  15. package/cjs/src/embed/searchEmbed-basic-auth.spec.js.map +1 -0
  16. package/cjs/src/native/LiveboardEmbed.d.ts +13 -0
  17. package/cjs/src/native/LiveboardEmbed.d.ts.map +1 -0
  18. package/cjs/src/native/LiveboardEmbed.js +146 -0
  19. package/cjs/src/native/LiveboardEmbed.js.map +1 -0
  20. package/cjs/src/native/commonUtils.d.ts +20 -0
  21. package/cjs/src/native/commonUtils.d.ts.map +1 -0
  22. package/cjs/src/native/commonUtils.js +175 -0
  23. package/cjs/src/native/commonUtils.js.map +1 -0
  24. package/cjs/src/native/commonUtils.spec.d.ts +2 -0
  25. package/cjs/src/native/commonUtils.spec.d.ts.map +1 -0
  26. package/cjs/src/native/commonUtils.spec.js +127 -0
  27. package/cjs/src/native/commonUtils.spec.js.map +1 -0
  28. package/cjs/src/native/embedConfig.d.ts +10 -0
  29. package/cjs/src/native/embedConfig.d.ts.map +1 -0
  30. package/cjs/src/native/embedConfig.js +12 -0
  31. package/cjs/src/native/embedConfig.js.map +1 -0
  32. package/cjs/src/native/index.d.ts +2 -0
  33. package/cjs/src/native/index.d.ts.map +1 -0
  34. package/cjs/src/native/index.js +7 -0
  35. package/cjs/src/native/index.js.map +1 -0
  36. package/cjs/src/native/types.d.ts +10 -0
  37. package/cjs/src/native/types.d.ts.map +1 -0
  38. package/cjs/src/native/types.js +3 -0
  39. package/cjs/src/native/types.js.map +1 -0
  40. package/cjs/src/parsers/filtersToRuntimeFilters.d.ts +6 -0
  41. package/cjs/src/parsers/filtersToRuntimeFilters.d.ts.map +1 -0
  42. package/cjs/src/parsers/filtersToRuntimeFilters.js +60 -0
  43. package/cjs/src/parsers/filtersToRuntimeFilters.js.map +1 -0
  44. package/cjs/src/parsers/filtersToRuntimeFilters.spec.d.ts +2 -0
  45. package/cjs/src/parsers/filtersToRuntimeFilters.spec.d.ts.map +1 -0
  46. package/cjs/src/parsers/filtersToRuntimeFilters.spec.js +205 -0
  47. package/cjs/src/parsers/filtersToRuntimeFilters.spec.js.map +1 -0
  48. package/cjs/src/parsers/index.d.ts +2 -0
  49. package/cjs/src/parsers/index.d.ts.map +1 -0
  50. package/cjs/src/parsers/index.js +6 -0
  51. package/cjs/src/parsers/index.js.map +1 -0
  52. package/cjs/src/parsers/index.spec.d.ts +2 -0
  53. package/cjs/src/parsers/index.spec.d.ts.map +1 -0
  54. package/cjs/src/parsers/index.spec.js +9 -0
  55. package/cjs/src/parsers/index.spec.js.map +1 -0
  56. package/cjs/src/react-native/Liveboard.d.ts +1 -0
  57. package/cjs/src/react-native/Liveboard.d.ts.map +1 -0
  58. package/cjs/src/react-native/Liveboard.js +1 -0
  59. package/cjs/src/react-native/Liveboard.js.map +1 -0
  60. package/cjs/src/types.d.ts +4 -4
  61. package/cjs/src/types.js +4 -4
  62. package/cjs/src/utils/processData.spec.js +33 -0
  63. package/cjs/src/utils/processData.spec.js.map +1 -1
  64. package/cjs/src/utils.d.ts +15 -0
  65. package/cjs/src/utils.d.ts.map +1 -1
  66. package/cjs/src/utils.js +24 -3
  67. package/cjs/src/utils.js.map +1 -1
  68. package/cjs/src/utils.spec.js +132 -0
  69. package/cjs/src/utils.spec.js.map +1 -1
  70. package/dist/{index-DgCGXqKb.js → index-BXczdQc7.js} +1 -1
  71. package/dist/index-Bh8kjZh8.js +7370 -0
  72. package/dist/src/embed/app.d.ts +1 -1
  73. package/dist/src/embed/hello.d.ts +2 -0
  74. package/dist/src/embed/hello.d.ts.map +1 -0
  75. package/dist/src/embed/liveboard.d.ts +1 -1
  76. package/dist/src/embed/searchEmbed-basic-auth.spec.d.ts +2 -0
  77. package/dist/src/embed/searchEmbed-basic-auth.spec.d.ts.map +1 -0
  78. package/dist/src/native/LiveboardEmbed.d.ts +13 -0
  79. package/dist/src/native/LiveboardEmbed.d.ts.map +1 -0
  80. package/dist/src/native/commonUtils.d.ts +20 -0
  81. package/dist/src/native/commonUtils.d.ts.map +1 -0
  82. package/dist/src/native/commonUtils.spec.d.ts +2 -0
  83. package/dist/src/native/commonUtils.spec.d.ts.map +1 -0
  84. package/dist/src/native/embedConfig.d.ts +10 -0
  85. package/dist/src/native/embedConfig.d.ts.map +1 -0
  86. package/dist/src/native/index.d.ts +2 -0
  87. package/dist/src/native/index.d.ts.map +1 -0
  88. package/dist/src/native/types.d.ts +10 -0
  89. package/dist/src/native/types.d.ts.map +1 -0
  90. package/dist/src/parsers/filtersToRuntimeFilters.d.ts +6 -0
  91. package/dist/src/parsers/filtersToRuntimeFilters.d.ts.map +1 -0
  92. package/dist/src/parsers/filtersToRuntimeFilters.spec.d.ts +2 -0
  93. package/dist/src/parsers/filtersToRuntimeFilters.spec.d.ts.map +1 -0
  94. package/dist/src/parsers/index.d.ts +2 -0
  95. package/dist/src/parsers/index.d.ts.map +1 -0
  96. package/dist/src/parsers/index.spec.d.ts +2 -0
  97. package/dist/src/parsers/index.spec.d.ts.map +1 -0
  98. package/dist/src/types.d.ts +4 -4
  99. package/dist/src/utils.d.ts +15 -0
  100. package/dist/src/utils.d.ts.map +1 -1
  101. package/dist/tsembed-parsers.es.js +3831 -0
  102. package/dist/tsembed-parsers.js +3840 -0
  103. package/dist/tsembed-react.es.js +7 -7
  104. package/dist/tsembed-react.js +6 -6
  105. package/dist/tsembed.es.js +7 -7
  106. package/dist/tsembed.js +6 -6
  107. package/dist/visual-embed-sdk-react-full.d.ts +6 -6
  108. package/dist/visual-embed-sdk-react.d.ts +6 -6
  109. package/dist/visual-embed-sdk.d.ts +6 -6
  110. package/lib/package.json +15 -3
  111. package/lib/src/embed/app.d.ts +1 -1
  112. package/lib/src/embed/base.native.d.ts +1 -0
  113. package/lib/src/embed/base.native.d.ts.map +1 -0
  114. package/lib/src/embed/base.native.js +1 -0
  115. package/lib/src/embed/base.native.js.map +1 -0
  116. package/lib/src/embed/hello.d.ts +2 -0
  117. package/lib/src/embed/hello.d.ts.map +1 -0
  118. package/lib/src/embed/hello.js +2 -0
  119. package/lib/src/embed/hello.js.map +1 -0
  120. package/lib/src/embed/liveboard.d.ts +1 -1
  121. package/lib/src/embed/searchEmbed-basic-auth.spec.d.ts +2 -0
  122. package/lib/src/embed/searchEmbed-basic-auth.spec.d.ts.map +1 -0
  123. package/lib/src/embed/searchEmbed-basic-auth.spec.js +101 -0
  124. package/lib/src/embed/searchEmbed-basic-auth.spec.js.map +1 -0
  125. package/lib/src/native/LiveboardEmbed.d.ts +13 -0
  126. package/lib/src/native/LiveboardEmbed.d.ts.map +1 -0
  127. package/lib/src/native/LiveboardEmbed.js +141 -0
  128. package/lib/src/native/LiveboardEmbed.js.map +1 -0
  129. package/lib/src/native/commonUtils.d.ts +20 -0
  130. package/lib/src/native/commonUtils.d.ts.map +1 -0
  131. package/lib/src/native/commonUtils.js +169 -0
  132. package/lib/src/native/commonUtils.js.map +1 -0
  133. package/lib/src/native/commonUtils.spec.d.ts +2 -0
  134. package/lib/src/native/commonUtils.spec.d.ts.map +1 -0
  135. package/lib/src/native/commonUtils.spec.js +124 -0
  136. package/lib/src/native/commonUtils.spec.js.map +1 -0
  137. package/lib/src/native/embedConfig.d.ts +10 -0
  138. package/lib/src/native/embedConfig.d.ts.map +1 -0
  139. package/lib/src/native/embedConfig.js +7 -0
  140. package/lib/src/native/embedConfig.js.map +1 -0
  141. package/lib/src/native/index.d.ts +2 -0
  142. package/lib/src/native/index.d.ts.map +1 -0
  143. package/lib/src/native/index.js +2 -0
  144. package/lib/src/native/index.js.map +1 -0
  145. package/lib/src/native/types.d.ts +10 -0
  146. package/lib/src/native/types.d.ts.map +1 -0
  147. package/lib/src/native/types.js +2 -0
  148. package/lib/src/native/types.js.map +1 -0
  149. package/lib/src/parsers/filtersToRuntimeFilters.d.ts +6 -0
  150. package/lib/src/parsers/filtersToRuntimeFilters.d.ts.map +1 -0
  151. package/lib/src/parsers/filtersToRuntimeFilters.js +55 -0
  152. package/lib/src/parsers/filtersToRuntimeFilters.js.map +1 -0
  153. package/lib/src/parsers/filtersToRuntimeFilters.spec.d.ts +2 -0
  154. package/lib/src/parsers/filtersToRuntimeFilters.spec.d.ts.map +1 -0
  155. package/lib/src/parsers/filtersToRuntimeFilters.spec.js +203 -0
  156. package/lib/src/parsers/filtersToRuntimeFilters.spec.js.map +1 -0
  157. package/lib/src/parsers/index.d.ts +2 -0
  158. package/lib/src/parsers/index.d.ts.map +1 -0
  159. package/lib/src/parsers/index.js +2 -0
  160. package/lib/src/parsers/index.js.map +1 -0
  161. package/lib/src/parsers/index.spec.d.ts +2 -0
  162. package/lib/src/parsers/index.spec.d.ts.map +1 -0
  163. package/lib/src/parsers/index.spec.js +7 -0
  164. package/lib/src/parsers/index.spec.js.map +1 -0
  165. package/lib/src/react-native/Liveboard.d.ts +1 -0
  166. package/lib/src/react-native/Liveboard.d.ts.map +1 -0
  167. package/lib/src/react-native/Liveboard.js +1 -0
  168. package/lib/src/react-native/Liveboard.js.map +1 -0
  169. package/lib/src/types.d.ts +4 -4
  170. package/lib/src/types.js +4 -4
  171. package/lib/src/utils/processData.spec.js +33 -0
  172. package/lib/src/utils/processData.spec.js.map +1 -1
  173. package/lib/src/utils.d.ts +15 -0
  174. package/lib/src/utils.d.ts.map +1 -1
  175. package/lib/src/utils.js +20 -2
  176. package/lib/src/utils.js.map +1 -1
  177. package/lib/src/utils.spec.js +133 -1
  178. package/lib/src/utils.spec.js.map +1 -1
  179. package/lib/src/visual-embed-sdk.d.ts +6 -6
  180. package/package.json +15 -3
  181. package/src/embed/app.ts +1 -1
  182. package/src/embed/liveboard.ts +1 -1
  183. package/src/native/commonUtils.spec.tsx +161 -0
  184. package/src/native/commonUtils.ts +183 -0
  185. package/src/native/index.ts +1 -0
  186. package/src/native/types.ts +10 -0
  187. package/src/types.ts +4 -4
  188. package/src/utils/processData.spec.ts +42 -0
  189. package/src/utils.spec.ts +147 -1
  190. package/src/utils.ts +25 -2
@@ -0,0 +1,183 @@
1
+ import { getCustomisationsMobileEmbed, getQueryParamString } from '../utils';
2
+ import { Param } from '../types';
3
+ import pkgInfo from '../../package.json';
4
+ import { WebViewConfig } from './types';
5
+ import { logger } from '../utils/logger';
6
+
7
+ /**
8
+ * This method constructs the webview URL with given config.
9
+ * @param config To get the webviewURL pass the necessary config options.
10
+ * host: string;
11
+ * authType: AuthType;
12
+ * liveboardId: string;
13
+ * getAuthToken: () => Promise<string>;
14
+ * These four are necessary arguments.
15
+ * @returns The Promise for WebView URL.
16
+ */
17
+ export const getWebViewUrl = async (config: WebViewConfig): Promise<string> => {
18
+ if (typeof config.getAuthToken !== 'function') {
19
+ throw new Error('`getAuthToken` must be a function that returns a Promise.');
20
+ }
21
+
22
+ const authToken = await config.getAuthToken();
23
+ if (!authToken) {
24
+ throw new Error('Failed to fetch initial authentication token.');
25
+ }
26
+
27
+ const hostAppUrl = encodeURIComponent(
28
+ config.host.includes('localhost')
29
+ || config.host.includes('127.0.0.1')
30
+ || config.host.includes('10.0.2.2')
31
+ ? 'local-host'
32
+ : config.host,
33
+ );
34
+
35
+ const queryParams = {
36
+ [Param.EmbedApp]: true,
37
+ [Param.HostAppUrl]: hostAppUrl,
38
+ [Param.Version]: pkgInfo.version,
39
+ [Param.AuthType]: config.authType,
40
+ [Param.livedBoardEmbed]: true,
41
+ [Param.EnableFlipTooltipToContextMenu]: true,
42
+ [Param.ContextMenuTrigger]: true,
43
+ };
44
+
45
+ const queryString = getQueryParamString(queryParams);
46
+ const webViewUrl = `${config.host}/embed?${queryString}#/embed/viz/${encodeURIComponent(config.liveboardId)}`;
47
+ return webViewUrl;
48
+ };
49
+
50
+ /**
51
+ * setting up message handling for the message replies to TS instances.
52
+ * @param config The webview config
53
+ * @param event The message event from the WebView.
54
+ * @param WebViewRef Ref to use and inject javascript
55
+ */
56
+ export const setupWebViewMessageHandler = async (
57
+ config: WebViewConfig,
58
+ event: any,
59
+ webViewRef: any,
60
+ ) => {
61
+ const message = JSON.parse(event.nativeEvent.data);
62
+
63
+ const injectJavaScript = (codeSnip: string) => {
64
+ if (webViewRef?.current) {
65
+ webViewRef.current.injectJavaScript(codeSnip);
66
+ } else {
67
+ logger.error('Reference for Webview not found!!');
68
+ }
69
+ };
70
+
71
+ const defaultHandleMessage = async () => {
72
+ switch (message.type) {
73
+ case 'appInit': {
74
+ try {
75
+ const authToken = await config.getAuthToken();
76
+ const initPayload = {
77
+ type: 'appInit',
78
+ data: {
79
+ host: config.host,
80
+ authToken,
81
+ customisations: getCustomisationsMobileEmbed(config),
82
+ },
83
+ };
84
+ injectJavaScript(jsCodeToHandleInteractionsForContextMenu);
85
+ injectJavaScript(`window.postMessage(${JSON.stringify(initPayload)}, '*');`);
86
+ } catch (error) {
87
+ console.error('Error handling appInit:', error);
88
+ }
89
+ break;
90
+ }
91
+
92
+ case 'ThoughtspotAuthExpired': {
93
+ try {
94
+ const newAuthToken = await config.getAuthToken();
95
+ if (newAuthToken) {
96
+ const authExpirePayload = {
97
+ type: 'ThoughtspotAuthExpired',
98
+ data: { authToken: newAuthToken },
99
+ };
100
+ injectJavaScript(`window.postMessage(${JSON.stringify(authExpirePayload)}, '*');`);
101
+ }
102
+ } catch (error) {
103
+ console.error('Error refreshing token on expiry:', error);
104
+ }
105
+ break;
106
+ }
107
+
108
+ case 'ThoughtspotAuthFailure': {
109
+ try {
110
+ const newAuthToken = await config.getAuthToken();
111
+ if (newAuthToken) {
112
+ const authFailurePayload = {
113
+ type: 'ThoughtspotAuthFailure',
114
+ data: { authToken: newAuthToken },
115
+ };
116
+ injectJavaScript(`window.postMessage(${JSON.stringify(authFailurePayload)}, '*');`);
117
+ }
118
+ } catch (error) {
119
+ console.error('Error refreshing token on failure:', error);
120
+ }
121
+ break;
122
+ }
123
+
124
+ default:
125
+ console.warn('Unhandled message type:', message.type);
126
+ }
127
+ };
128
+
129
+ if (config.handleMessage) {
130
+ await config.handleMessage(event);
131
+ } else {
132
+ await defaultHandleMessage();
133
+ }
134
+ };
135
+
136
+ const jsCodeToHandleInteractionsForContextMenu = `
137
+ // Disabling auofocus
138
+ document.querySelectorAll('input[autofocus], textarea[autofocus]').forEach(el => el.removeAttribute('autofocus'));
139
+
140
+ // adding meta tag to keep fixed viewport scalign
141
+ const meta = document.createElement('meta');
142
+ meta.name = 'viewport';
143
+ meta.content = 'width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no';
144
+ document.head.appendChild(meta);
145
+
146
+ // input focus problem. -> we can just force it inside our view.
147
+ document.addEventListener('focusin', (event) => {
148
+ const target = event.target;
149
+
150
+ if (
151
+ target.tagName === 'INPUT' ||
152
+ target.tagName === 'TEXTAREA'
153
+ ) {
154
+ const rect = target.getBoundingClientRect();
155
+ if (
156
+ rect.top < 0 ||
157
+ rect.bottom > window.innerHeight ||
158
+ rect.left < 0 ||
159
+ rect.right > window.innerWidth
160
+ ) {
161
+ event.preventDefault();
162
+ // target.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'end' });
163
+ const horizontalPadding = 10;
164
+
165
+ let scrollX = 0;
166
+
167
+ if (rect.left < horizontalPadding) {
168
+ scrollX = rect.left - horizontalPadding;
169
+ }
170
+ if (rect.right > window.innerWidth - horizontalPadding) {
171
+ scrollX = rect.right - window.innerWidth + horizontalPadding;
172
+ }
173
+ const scrollY = rect.top - (window.innerHeight / 2 - rect.height / 2);
174
+
175
+ window.scrollBy({
176
+ top: scrollY,
177
+ left: scrollX,
178
+ behavior: 'smooth',
179
+ })
180
+ }
181
+ }
182
+ });
183
+ `;
@@ -0,0 +1 @@
1
+ export { getWebViewUrl, setupWebViewMessageHandler } from './commonUtils';
@@ -0,0 +1,10 @@
1
+ import { AuthType, CustomisationsInterface } from 'src/types';
2
+
3
+ export interface WebViewConfig {
4
+ host: string;
5
+ authType: AuthType;
6
+ liveboardId: string;
7
+ getAuthToken: () => Promise<string>;
8
+ handleMessage?: (event: any) => void;
9
+ customizations?: CustomisationsInterface;
10
+ }
package/src/types.ts CHANGED
@@ -2133,12 +2133,12 @@ export enum EmbedEvent {
2133
2133
  */
2134
2134
  FilterChanged = 'filterChanged',
2135
2135
  /**
2136
- * Emitted when a user clicks the **Go** button on the Search page
2136
+ * Emitted when a user clicks the **Go** button on the sage embed
2137
2137
  * @version SDK : 1.26.0 | Thoughtspot: 9.7.0.cl, 9.8.0.sw
2138
2138
  */
2139
2139
  SageEmbedQuery = 'sageEmbedQuery',
2140
2140
  /**
2141
- * Emitted when a user selects a data source.
2141
+ * Emitted when a user selects a data source on the sage embed
2142
2142
  * @version SDK : 1.26.0 | Thoughtspot: 9.7.0.cl, 9.8.0.sw
2143
2143
  */
2144
2144
  SageWorksheetUpdated = 'sageWorksheetUpdated',
@@ -4379,7 +4379,7 @@ export enum Action {
4379
4379
  * ```js
4380
4380
  * const disabledActions = [Action.DisableChipReorder]
4381
4381
  * ```
4382
- * @version SDK: 1.35.0 | Thoughtspot: 10.5.0.cl
4382
+ * @version SDK: 1.36.0 | Thoughtspot: 10.6.0.cl
4383
4383
  */
4384
4384
  DisableChipReorder = 'disableChipReorder',
4385
4385
 
@@ -4389,7 +4389,7 @@ export enum Action {
4389
4389
  * ```js
4390
4390
  * hiddenAction: [Action.ChangeFilterVisibilityInTab]
4391
4391
  * ```
4392
- * @version SDK: 1.35.0 | Thoughtspot: 10.5.0.cl
4392
+ * @version SDK: 1.36.0 | Thoughtspot: 10.6.0.cl
4393
4393
  */
4394
4394
  ChangeFilterVisibilityInTab = 'changeFilterVisibilityInTab',
4395
4395
 
@@ -188,4 +188,46 @@ describe('Unit test for process data', () => {
188
188
  expect(base.notifyAuthFailure).not.toBeCalled();
189
189
  expect(el.innerHTML).not.toBe('Hello');
190
190
  });
191
+
192
+ test('should return the event if type is unknown (default branch)', () => {
193
+ const unknownEvent = { type: 'SomeRandomEvent' };
194
+ const result = processDataInstance.processEventData(
195
+ 'SomeRandomEvent' as any,
196
+ unknownEvent,
197
+ thoughtSpotHost,
198
+ null,
199
+ );
200
+ expect(result).toBe(unknownEvent);
201
+ });
202
+
203
+ test('processAuthInit uses e.payload.userGUID if e.data.userGUID is missing', () => {
204
+ const e = {
205
+ type: EmbedEvent.AuthInit,
206
+ payload: { userGUID: 'payload-guid' },
207
+ data: {},
208
+ };
209
+ jest.spyOn(base, 'notifyAuthSuccess').mockImplementation(() => {});
210
+ const result = processDataInstance.processEventData(e.type, e, '', null);
211
+ expect(result.data.userGUID).toBe('payload-guid');
212
+ expect(base.notifyAuthSuccess).toHaveBeenCalled();
213
+ });
214
+
215
+ test('NoCookieAccess with suppressErrorAlerts=true does not alert', () => {
216
+ const e = { type: EmbedEvent.NoCookieAccess };
217
+ const el: any = {};
218
+ jest.spyOn(embedConfigInstance, 'getEmbedConfig').mockReturnValue({
219
+ loginFailedMessage: 'Cookie blocked!',
220
+ suppressNoCookieAccessAlert: false,
221
+ suppressErrorAlerts: true,
222
+ ignoreNoCookieAccess: false,
223
+ });
224
+ jest.spyOn(window, 'alert').mockImplementation(() => {});
225
+ jest.spyOn(base, 'notifyAuthFailure');
226
+
227
+ const result = processDataInstance.processEventData(e.type, e, '', el);
228
+ expect(result).toEqual({ type: e.type });
229
+ expect(window.alert).not.toHaveBeenCalled();
230
+ expect(el.innerHTML).toBe('Cookie blocked!');
231
+ expect(base.notifyAuthFailure).toHaveBeenCalled();
232
+ });
191
233
  });
package/src/utils.spec.ts CHANGED
@@ -11,8 +11,10 @@ import {
11
11
  removeStyleProperties,
12
12
  setStyleProperties,
13
13
  isUndefined,
14
+ getCustomisationsMobileEmbed,
14
15
  } from './utils';
15
- import { RuntimeFilterOp } from './types';
16
+ import { CustomisationsInterface, RuntimeFilterOp } from './types';
17
+ import { WebViewConfig } from './native/types';
16
18
 
17
19
  describe('unit test for utils', () => {
18
20
  test('getQueryParamString', () => {
@@ -273,3 +275,147 @@ describe('unit test for utils', () => {
273
275
  expect(isUndefined({})).toBe(false);
274
276
  });
275
277
  });
278
+
279
+ describe('getCustomisationsMobileEmbed', () => {
280
+ it('should return empty style and content if no customizations are defined', () => {
281
+ const embedConfig: Partial<WebViewConfig> = {};
282
+
283
+ const result = getCustomisationsMobileEmbed(embedConfig as WebViewConfig);
284
+ expect(result).toEqual({
285
+ style: {
286
+ customCSS: {},
287
+ customCSSUrl: undefined,
288
+ },
289
+ content: {},
290
+ });
291
+ });
292
+
293
+ it('should use embedConfig.customizations when present', () => {
294
+ const embedConfig: Partial<WebViewConfig> = {
295
+ customizations: {
296
+ style: {
297
+ customCSS: {
298
+ rules_UNSTABLE: {
299
+ '.title': { fontSize: '20px' },
300
+ }
301
+ },
302
+ customCSSUrl: 'http://example.com/styles.css',
303
+ },
304
+ content: {
305
+ headerText: 'Hello World',
306
+ },
307
+ },
308
+ };
309
+
310
+ const result = getCustomisationsMobileEmbed(embedConfig as WebViewConfig);
311
+ expect(result).toEqual({
312
+ style: {
313
+ customCSS: {
314
+ rules_UNSTABLE: {
315
+ '.title': { fontSize: '20px' },
316
+ }
317
+ },
318
+ customCSSUrl: 'http://example.com/styles.css',
319
+ },
320
+ content: {
321
+ headerText: 'Hello World',
322
+ },
323
+ });
324
+ });
325
+
326
+ it('should fallback to embedConfig.customisations if customizations is undefined', () => {
327
+ const embedConfig: Partial<WebViewConfig> = {
328
+ customizations: {
329
+ style: {
330
+ backgroundColor: 'green',
331
+ customCSS: { '.anotherClass': { color: 'white' } },
332
+ },
333
+ content: {
334
+ footerText: 'Footer content',
335
+ },
336
+ } as CustomisationsInterface,
337
+ };
338
+
339
+ const result = getCustomisationsMobileEmbed(embedConfig as WebViewConfig);
340
+ expect(result).toEqual({
341
+ style: {
342
+ backgroundColor: 'green',
343
+ customCSS: { '.anotherClass': { color: 'white' } },
344
+ customCSSUrl: undefined,
345
+ },
346
+ content: {
347
+ footerText: 'Footer content',
348
+ },
349
+ });
350
+ });
351
+
352
+ it('should merge style objects correctly (customCSS stays as an object)', () => {
353
+ const embedConfig: Partial<WebViewConfig> = {
354
+ customizations: {
355
+ style: {
356
+ customCSS: {
357
+ rules_UNSTABLE: {
358
+ '.title': { fontSize: '20px' },
359
+ }
360
+ },
361
+ customCSSUrl: 'http://example.com/old-styles.css',
362
+ },
363
+ },
364
+ };
365
+
366
+ const result = getCustomisationsMobileEmbed(embedConfig as WebViewConfig);
367
+ expect(result).toEqual({
368
+ style: {
369
+ customCSS: {
370
+ rules_UNSTABLE: {
371
+ '.title': { fontSize: '20px' },
372
+ }
373
+ },
374
+ customCSSUrl: 'http://example.com/old-styles.css',
375
+ },
376
+ content: {},
377
+ });
378
+ });
379
+
380
+ it('should handle missing style or content keys gracefully', () => {
381
+ const embedConfig: Partial<WebViewConfig> = {
382
+ customizations: {
383
+ style: undefined, // style is missing
384
+ content: undefined, // content is missing
385
+ },
386
+ };
387
+
388
+ const result = getCustomisationsMobileEmbed(embedConfig as WebViewConfig);
389
+ expect(result).toEqual({
390
+ style: {
391
+ customCSS: {},
392
+ customCSSUrl: undefined,
393
+ },
394
+ content: {},
395
+ });
396
+ });
397
+
398
+ it('should override customCSSUrl with embedConfig.customizations.style.customCSSUrl if defined', () => {
399
+ const embedConfig: Partial<WebViewConfig> = {
400
+ customizations: {
401
+ style: {
402
+ customCSSUrl: 'http://example.com/custom.css',
403
+ customCSS: {
404
+ rules_UNSTABLE: {
405
+ '.title': { fontSize: '20px' },
406
+ }
407
+ },
408
+ },
409
+ },
410
+ };
411
+
412
+ const result = getCustomisationsMobileEmbed(embedConfig as WebViewConfig);
413
+ expect(result.style?.customCSSUrl).toBe('http://example.com/custom.css');
414
+ expect(result.style?.customCSS).toEqual({
415
+ rules_UNSTABLE: {
416
+ '.title': { fontSize: '20px' },
417
+ }
418
+ });
419
+ });
420
+ });
421
+
package/src/utils.ts CHANGED
@@ -16,6 +16,7 @@ import {
16
16
  ViewConfig,
17
17
  RuntimeParameter,
18
18
  } from './types';
19
+ import { WebViewConfig } from './native/types';
19
20
 
20
21
  /**
21
22
  * Construct a runtime filters query string from the given filters.
@@ -73,7 +74,7 @@ export const getRuntimeParameters = (runtimeParameters: RuntimeParameter[]): str
73
74
  * parameter to the ThoughtSpot app.
74
75
  * @param value Any parameter value
75
76
  */
76
- const serializeParam = (value: any) => {
77
+ export const serializeParam = (value: any) => {
77
78
  // do not serialize primitive types
78
79
  if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
79
80
  return value;
@@ -88,7 +89,7 @@ const serializeParam = (value: any) => {
88
89
  * in case of any other type, we directly return the value.
89
90
  * @param value
90
91
  */
91
- const paramToString = (value: any) => (Array.isArray(value) ? value.join(',') : value);
92
+ export const paramToString = (value: any) => (Array.isArray(value) ? value.join(',') : value);
92
93
 
93
94
  /**
94
95
  * Return a query param string composed from the given params object
@@ -242,6 +243,28 @@ export const getCustomisations = (
242
243
  return customizations;
243
244
  };
244
245
 
246
+ export const getCustomisationsMobileEmbed = (
247
+ embedConfig: WebViewConfig,
248
+ ): CustomisationsInterface => {
249
+ const customizationsFromEmbedConfig = embedConfig.customizations
250
+ || ((embedConfig as any).customisations as CustomisationsInterface);
251
+
252
+ const customizations: CustomisationsInterface = {
253
+ style: {
254
+ ...customizationsFromEmbedConfig?.style,
255
+ customCSS: {
256
+ ...customizationsFromEmbedConfig?.style?.customCSS,
257
+ },
258
+ customCSSUrl:
259
+ customizationsFromEmbedConfig?.style?.customCSSUrl,
260
+ },
261
+ content: {
262
+ ...customizationsFromEmbedConfig?.content,
263
+ },
264
+ };
265
+ return customizations;
266
+ };
267
+
245
268
  export const getRuntimeFilters = (runtimefilters: any) => getFilterQuery(runtimefilters || []);
246
269
 
247
270
  /**