@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.
- package/cjs/package.json +15 -3
- package/cjs/src/embed/app.d.ts +1 -1
- package/cjs/src/embed/base.native.d.ts +1 -0
- package/cjs/src/embed/base.native.d.ts.map +1 -0
- package/cjs/src/embed/base.native.js +1 -0
- package/cjs/src/embed/base.native.js.map +1 -0
- package/cjs/src/embed/hello.d.ts +2 -0
- package/cjs/src/embed/hello.d.ts.map +1 -0
- package/cjs/src/embed/hello.js +6 -0
- package/cjs/src/embed/hello.js.map +1 -0
- package/cjs/src/embed/liveboard.d.ts +1 -1
- package/cjs/src/embed/searchEmbed-basic-auth.spec.d.ts +2 -0
- package/cjs/src/embed/searchEmbed-basic-auth.spec.d.ts.map +1 -0
- package/cjs/src/embed/searchEmbed-basic-auth.spec.js +104 -0
- package/cjs/src/embed/searchEmbed-basic-auth.spec.js.map +1 -0
- package/cjs/src/native/LiveboardEmbed.d.ts +13 -0
- package/cjs/src/native/LiveboardEmbed.d.ts.map +1 -0
- package/cjs/src/native/LiveboardEmbed.js +146 -0
- package/cjs/src/native/LiveboardEmbed.js.map +1 -0
- package/cjs/src/native/commonUtils.d.ts +20 -0
- package/cjs/src/native/commonUtils.d.ts.map +1 -0
- package/cjs/src/native/commonUtils.js +175 -0
- package/cjs/src/native/commonUtils.js.map +1 -0
- package/cjs/src/native/commonUtils.spec.d.ts +2 -0
- package/cjs/src/native/commonUtils.spec.d.ts.map +1 -0
- package/cjs/src/native/commonUtils.spec.js +127 -0
- package/cjs/src/native/commonUtils.spec.js.map +1 -0
- package/cjs/src/native/embedConfig.d.ts +10 -0
- package/cjs/src/native/embedConfig.d.ts.map +1 -0
- package/cjs/src/native/embedConfig.js +12 -0
- package/cjs/src/native/embedConfig.js.map +1 -0
- package/cjs/src/native/index.d.ts +2 -0
- package/cjs/src/native/index.d.ts.map +1 -0
- package/cjs/src/native/index.js +7 -0
- package/cjs/src/native/index.js.map +1 -0
- package/cjs/src/native/types.d.ts +10 -0
- package/cjs/src/native/types.d.ts.map +1 -0
- package/cjs/src/native/types.js +3 -0
- package/cjs/src/native/types.js.map +1 -0
- package/cjs/src/parsers/filtersToRuntimeFilters.d.ts +6 -0
- package/cjs/src/parsers/filtersToRuntimeFilters.d.ts.map +1 -0
- package/cjs/src/parsers/filtersToRuntimeFilters.js +60 -0
- package/cjs/src/parsers/filtersToRuntimeFilters.js.map +1 -0
- package/cjs/src/parsers/filtersToRuntimeFilters.spec.d.ts +2 -0
- package/cjs/src/parsers/filtersToRuntimeFilters.spec.d.ts.map +1 -0
- package/cjs/src/parsers/filtersToRuntimeFilters.spec.js +205 -0
- package/cjs/src/parsers/filtersToRuntimeFilters.spec.js.map +1 -0
- package/cjs/src/parsers/index.d.ts +2 -0
- package/cjs/src/parsers/index.d.ts.map +1 -0
- package/cjs/src/parsers/index.js +6 -0
- package/cjs/src/parsers/index.js.map +1 -0
- package/cjs/src/parsers/index.spec.d.ts +2 -0
- package/cjs/src/parsers/index.spec.d.ts.map +1 -0
- package/cjs/src/parsers/index.spec.js +9 -0
- package/cjs/src/parsers/index.spec.js.map +1 -0
- package/cjs/src/react-native/Liveboard.d.ts +1 -0
- package/cjs/src/react-native/Liveboard.d.ts.map +1 -0
- package/cjs/src/react-native/Liveboard.js +1 -0
- package/cjs/src/react-native/Liveboard.js.map +1 -0
- package/cjs/src/types.d.ts +4 -4
- package/cjs/src/types.js +4 -4
- package/cjs/src/utils/processData.spec.js +33 -0
- package/cjs/src/utils/processData.spec.js.map +1 -1
- package/cjs/src/utils.d.ts +15 -0
- package/cjs/src/utils.d.ts.map +1 -1
- package/cjs/src/utils.js +24 -3
- package/cjs/src/utils.js.map +1 -1
- package/cjs/src/utils.spec.js +132 -0
- package/cjs/src/utils.spec.js.map +1 -1
- package/dist/{index-DgCGXqKb.js → index-BXczdQc7.js} +1 -1
- package/dist/index-Bh8kjZh8.js +7370 -0
- package/dist/src/embed/app.d.ts +1 -1
- package/dist/src/embed/hello.d.ts +2 -0
- package/dist/src/embed/hello.d.ts.map +1 -0
- package/dist/src/embed/liveboard.d.ts +1 -1
- package/dist/src/embed/searchEmbed-basic-auth.spec.d.ts +2 -0
- package/dist/src/embed/searchEmbed-basic-auth.spec.d.ts.map +1 -0
- package/dist/src/native/LiveboardEmbed.d.ts +13 -0
- package/dist/src/native/LiveboardEmbed.d.ts.map +1 -0
- package/dist/src/native/commonUtils.d.ts +20 -0
- package/dist/src/native/commonUtils.d.ts.map +1 -0
- package/dist/src/native/commonUtils.spec.d.ts +2 -0
- package/dist/src/native/commonUtils.spec.d.ts.map +1 -0
- package/dist/src/native/embedConfig.d.ts +10 -0
- package/dist/src/native/embedConfig.d.ts.map +1 -0
- package/dist/src/native/index.d.ts +2 -0
- package/dist/src/native/index.d.ts.map +1 -0
- package/dist/src/native/types.d.ts +10 -0
- package/dist/src/native/types.d.ts.map +1 -0
- package/dist/src/parsers/filtersToRuntimeFilters.d.ts +6 -0
- package/dist/src/parsers/filtersToRuntimeFilters.d.ts.map +1 -0
- package/dist/src/parsers/filtersToRuntimeFilters.spec.d.ts +2 -0
- package/dist/src/parsers/filtersToRuntimeFilters.spec.d.ts.map +1 -0
- package/dist/src/parsers/index.d.ts +2 -0
- package/dist/src/parsers/index.d.ts.map +1 -0
- package/dist/src/parsers/index.spec.d.ts +2 -0
- package/dist/src/parsers/index.spec.d.ts.map +1 -0
- package/dist/src/types.d.ts +4 -4
- package/dist/src/utils.d.ts +15 -0
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/tsembed-parsers.es.js +3831 -0
- package/dist/tsembed-parsers.js +3840 -0
- package/dist/tsembed-react.es.js +7 -7
- package/dist/tsembed-react.js +6 -6
- package/dist/tsembed.es.js +7 -7
- package/dist/tsembed.js +6 -6
- package/dist/visual-embed-sdk-react-full.d.ts +6 -6
- package/dist/visual-embed-sdk-react.d.ts +6 -6
- package/dist/visual-embed-sdk.d.ts +6 -6
- package/lib/package.json +15 -3
- package/lib/src/embed/app.d.ts +1 -1
- package/lib/src/embed/base.native.d.ts +1 -0
- package/lib/src/embed/base.native.d.ts.map +1 -0
- package/lib/src/embed/base.native.js +1 -0
- package/lib/src/embed/base.native.js.map +1 -0
- package/lib/src/embed/hello.d.ts +2 -0
- package/lib/src/embed/hello.d.ts.map +1 -0
- package/lib/src/embed/hello.js +2 -0
- package/lib/src/embed/hello.js.map +1 -0
- package/lib/src/embed/liveboard.d.ts +1 -1
- package/lib/src/embed/searchEmbed-basic-auth.spec.d.ts +2 -0
- package/lib/src/embed/searchEmbed-basic-auth.spec.d.ts.map +1 -0
- package/lib/src/embed/searchEmbed-basic-auth.spec.js +101 -0
- package/lib/src/embed/searchEmbed-basic-auth.spec.js.map +1 -0
- package/lib/src/native/LiveboardEmbed.d.ts +13 -0
- package/lib/src/native/LiveboardEmbed.d.ts.map +1 -0
- package/lib/src/native/LiveboardEmbed.js +141 -0
- package/lib/src/native/LiveboardEmbed.js.map +1 -0
- package/lib/src/native/commonUtils.d.ts +20 -0
- package/lib/src/native/commonUtils.d.ts.map +1 -0
- package/lib/src/native/commonUtils.js +169 -0
- package/lib/src/native/commonUtils.js.map +1 -0
- package/lib/src/native/commonUtils.spec.d.ts +2 -0
- package/lib/src/native/commonUtils.spec.d.ts.map +1 -0
- package/lib/src/native/commonUtils.spec.js +124 -0
- package/lib/src/native/commonUtils.spec.js.map +1 -0
- package/lib/src/native/embedConfig.d.ts +10 -0
- package/lib/src/native/embedConfig.d.ts.map +1 -0
- package/lib/src/native/embedConfig.js +7 -0
- package/lib/src/native/embedConfig.js.map +1 -0
- package/lib/src/native/index.d.ts +2 -0
- package/lib/src/native/index.d.ts.map +1 -0
- package/lib/src/native/index.js +2 -0
- package/lib/src/native/index.js.map +1 -0
- package/lib/src/native/types.d.ts +10 -0
- package/lib/src/native/types.d.ts.map +1 -0
- package/lib/src/native/types.js +2 -0
- package/lib/src/native/types.js.map +1 -0
- package/lib/src/parsers/filtersToRuntimeFilters.d.ts +6 -0
- package/lib/src/parsers/filtersToRuntimeFilters.d.ts.map +1 -0
- package/lib/src/parsers/filtersToRuntimeFilters.js +55 -0
- package/lib/src/parsers/filtersToRuntimeFilters.js.map +1 -0
- package/lib/src/parsers/filtersToRuntimeFilters.spec.d.ts +2 -0
- package/lib/src/parsers/filtersToRuntimeFilters.spec.d.ts.map +1 -0
- package/lib/src/parsers/filtersToRuntimeFilters.spec.js +203 -0
- package/lib/src/parsers/filtersToRuntimeFilters.spec.js.map +1 -0
- package/lib/src/parsers/index.d.ts +2 -0
- package/lib/src/parsers/index.d.ts.map +1 -0
- package/lib/src/parsers/index.js +2 -0
- package/lib/src/parsers/index.js.map +1 -0
- package/lib/src/parsers/index.spec.d.ts +2 -0
- package/lib/src/parsers/index.spec.d.ts.map +1 -0
- package/lib/src/parsers/index.spec.js +7 -0
- package/lib/src/parsers/index.spec.js.map +1 -0
- package/lib/src/react-native/Liveboard.d.ts +1 -0
- package/lib/src/react-native/Liveboard.d.ts.map +1 -0
- package/lib/src/react-native/Liveboard.js +1 -0
- package/lib/src/react-native/Liveboard.js.map +1 -0
- package/lib/src/types.d.ts +4 -4
- package/lib/src/types.js +4 -4
- package/lib/src/utils/processData.spec.js +33 -0
- package/lib/src/utils/processData.spec.js.map +1 -1
- package/lib/src/utils.d.ts +15 -0
- package/lib/src/utils.d.ts.map +1 -1
- package/lib/src/utils.js +20 -2
- package/lib/src/utils.js.map +1 -1
- package/lib/src/utils.spec.js +133 -1
- package/lib/src/utils.spec.js.map +1 -1
- package/lib/src/visual-embed-sdk.d.ts +6 -6
- package/package.json +15 -3
- package/src/embed/app.ts +1 -1
- package/src/embed/liveboard.ts +1 -1
- package/src/native/commonUtils.spec.tsx +161 -0
- package/src/native/commonUtils.ts +183 -0
- package/src/native/index.ts +1 -0
- package/src/native/types.ts +10 -0
- package/src/types.ts +4 -4
- package/src/utils/processData.spec.ts +42 -0
- package/src/utils.spec.ts +147 -1
- 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
|
|
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.
|
|
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.
|
|
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
|
/**
|