@thoughtspot/visual-embed-sdk 1.38.0 → 1.38.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/README.md +1 -1
- package/cjs/package.json +1 -1
- package/cjs/src/embed/app.d.ts +96 -156
- package/cjs/src/embed/app.d.ts.map +1 -1
- package/cjs/src/embed/app.js +7 -4
- package/cjs/src/embed/app.js.map +1 -1
- package/cjs/src/embed/app.spec.js +10 -0
- package/cjs/src/embed/app.spec.js.map +1 -1
- package/cjs/src/embed/bodyless-conversation.d.ts +4 -4
- package/cjs/src/embed/bodyless-conversation.d.ts.map +1 -1
- package/cjs/src/embed/bodyless-conversation.js +1 -1
- package/cjs/src/embed/bodyless-conversation.js.map +1 -1
- package/cjs/src/embed/conversation.d.ts +22 -11
- package/cjs/src/embed/conversation.d.ts.map +1 -1
- package/cjs/src/embed/conversation.js +1 -1
- package/cjs/src/embed/conversation.js.map +1 -1
- package/cjs/src/embed/liveboard.d.ts +110 -167
- package/cjs/src/embed/liveboard.d.ts.map +1 -1
- package/cjs/src/embed/liveboard.js +25 -11
- package/cjs/src/embed/liveboard.js.map +1 -1
- package/cjs/src/embed/liveboard.spec.js +11 -0
- package/cjs/src/embed/liveboard.spec.js.map +1 -1
- package/cjs/src/embed/sage.d.ts +17 -2
- package/cjs/src/embed/sage.d.ts.map +1 -1
- package/cjs/src/embed/sage.js +2 -0
- package/cjs/src/embed/sage.js.map +1 -1
- package/cjs/src/embed/search-bar.d.ts +16 -6
- package/cjs/src/embed/search-bar.d.ts.map +1 -1
- package/cjs/src/embed/search-bar.js.map +1 -1
- package/cjs/src/embed/search.d.ts +46 -14
- package/cjs/src/embed/search.d.ts.map +1 -1
- package/cjs/src/embed/search.js.map +1 -1
- package/cjs/src/embed/ts-embed.d.ts +19 -2
- package/cjs/src/embed/ts-embed.d.ts.map +1 -1
- package/cjs/src/embed/ts-embed.js +49 -0
- package/cjs/src/embed/ts-embed.js.map +1 -1
- package/cjs/src/embed/ts-embed.spec.js +45 -4
- package/cjs/src/embed/ts-embed.spec.js.map +1 -1
- package/cjs/src/index.d.ts +2 -2
- package/cjs/src/index.d.ts.map +1 -1
- package/cjs/src/index.js +2 -1
- package/cjs/src/index.js.map +1 -1
- package/cjs/src/react/all-types-export.d.ts +1 -1
- package/cjs/src/react/all-types-export.d.ts.map +1 -1
- package/cjs/src/react/all-types-export.js +2 -1
- package/cjs/src/react/all-types-export.js.map +1 -1
- package/cjs/src/react/index.d.ts +1 -1
- package/cjs/src/react/index.d.ts.map +1 -1
- package/cjs/src/react/index.js +1 -1
- package/cjs/src/react/util.d.ts +7 -4
- package/cjs/src/react/util.d.ts.map +1 -1
- package/cjs/src/react/util.js.map +1 -1
- package/cjs/src/types.d.ts +482 -251
- package/cjs/src/types.d.ts.map +1 -1
- package/cjs/src/types.js +31 -14
- package/cjs/src/types.js.map +1 -1
- package/cjs/src/utils/graphql/answerService/answerService.d.ts +2 -1
- package/cjs/src/utils/graphql/answerService/answerService.d.ts.map +1 -1
- package/cjs/src/utils/graphql/answerService/answerService.js +2 -1
- package/cjs/src/utils/graphql/answerService/answerService.js.map +1 -1
- package/cjs/src/utils/processData.d.ts.map +1 -1
- package/cjs/src/utils/processData.js +15 -0
- package/cjs/src/utils/processData.js.map +1 -1
- package/cjs/src/utils/processData.spec.js +15 -0
- package/cjs/src/utils/processData.spec.js.map +1 -1
- package/cjs/src/utils/processTrigger.d.ts.map +1 -1
- package/cjs/src/utils/processTrigger.js +14 -0
- package/cjs/src/utils/processTrigger.js.map +1 -1
- package/cjs/src/utils/processTrigger.spec.js +54 -0
- package/cjs/src/utils/processTrigger.spec.js.map +1 -1
- package/cjs/src/utils.d.ts +11 -2
- package/cjs/src/utils.d.ts.map +1 -1
- package/cjs/src/utils.js +70 -1
- package/cjs/src/utils.js.map +1 -1
- package/cjs/src/utils.spec.js +85 -0
- package/cjs/src/utils.spec.js.map +1 -1
- package/dist/{index-BIcnpmMY.js → index-BKIWUvTr.js} +1 -1
- package/dist/src/embed/app.d.ts +96 -156
- package/dist/src/embed/app.d.ts.map +1 -1
- package/dist/src/embed/bodyless-conversation.d.ts +4 -4
- package/dist/src/embed/bodyless-conversation.d.ts.map +1 -1
- package/dist/src/embed/conversation.d.ts +22 -11
- package/dist/src/embed/conversation.d.ts.map +1 -1
- package/dist/src/embed/liveboard.d.ts +110 -167
- package/dist/src/embed/liveboard.d.ts.map +1 -1
- package/dist/src/embed/sage.d.ts +17 -2
- package/dist/src/embed/sage.d.ts.map +1 -1
- package/dist/src/embed/search-bar.d.ts +16 -6
- package/dist/src/embed/search-bar.d.ts.map +1 -1
- package/dist/src/embed/search.d.ts +46 -14
- package/dist/src/embed/search.d.ts.map +1 -1
- package/dist/src/embed/ts-embed.d.ts +19 -2
- package/dist/src/embed/ts-embed.d.ts.map +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/react/all-types-export.d.ts +1 -1
- package/dist/src/react/all-types-export.d.ts.map +1 -1
- package/dist/src/react/index.d.ts +1 -1
- package/dist/src/react/index.d.ts.map +1 -1
- package/dist/src/react/util.d.ts +7 -4
- package/dist/src/react/util.d.ts.map +1 -1
- package/dist/src/types.d.ts +482 -251
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/graphql/answerService/answerService.d.ts +2 -1
- package/dist/src/utils/graphql/answerService/answerService.d.ts.map +1 -1
- package/dist/src/utils/processData.d.ts.map +1 -1
- package/dist/src/utils/processTrigger.d.ts.map +1 -1
- package/dist/src/utils.d.ts +11 -2
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/tsembed-react.es.js +3498 -3322
- package/dist/tsembed-react.js +3497 -3321
- package/dist/tsembed.es.js +213 -37
- package/dist/tsembed.js +216 -40
- package/dist/visual-embed-sdk-react-full.d.ts +845 -655
- package/dist/visual-embed-sdk-react.d.ts +845 -655
- package/dist/visual-embed-sdk.d.ts +827 -640
- package/lib/package.json +1 -1
- package/lib/src/embed/app.d.ts +96 -156
- package/lib/src/embed/app.d.ts.map +1 -1
- package/lib/src/embed/app.js +7 -4
- package/lib/src/embed/app.js.map +1 -1
- package/lib/src/embed/app.spec.js +10 -0
- package/lib/src/embed/app.spec.js.map +1 -1
- package/lib/src/embed/bodyless-conversation.d.ts +4 -4
- package/lib/src/embed/bodyless-conversation.d.ts.map +1 -1
- package/lib/src/embed/bodyless-conversation.js +1 -1
- package/lib/src/embed/bodyless-conversation.js.map +1 -1
- package/lib/src/embed/conversation.d.ts +22 -11
- package/lib/src/embed/conversation.d.ts.map +1 -1
- package/lib/src/embed/conversation.js +1 -1
- package/lib/src/embed/conversation.js.map +1 -1
- package/lib/src/embed/liveboard.d.ts +110 -167
- package/lib/src/embed/liveboard.d.ts.map +1 -1
- package/lib/src/embed/liveboard.js +25 -11
- package/lib/src/embed/liveboard.js.map +1 -1
- package/lib/src/embed/liveboard.spec.js +11 -0
- package/lib/src/embed/liveboard.spec.js.map +1 -1
- package/lib/src/embed/sage.d.ts +17 -2
- package/lib/src/embed/sage.d.ts.map +1 -1
- package/lib/src/embed/sage.js +2 -0
- package/lib/src/embed/sage.js.map +1 -1
- package/lib/src/embed/search-bar.d.ts +16 -6
- package/lib/src/embed/search-bar.d.ts.map +1 -1
- package/lib/src/embed/search-bar.js.map +1 -1
- package/lib/src/embed/search.d.ts +46 -14
- package/lib/src/embed/search.d.ts.map +1 -1
- package/lib/src/embed/search.js.map +1 -1
- package/lib/src/embed/ts-embed.d.ts +19 -2
- package/lib/src/embed/ts-embed.d.ts.map +1 -1
- package/lib/src/embed/ts-embed.js +49 -0
- package/lib/src/embed/ts-embed.js.map +1 -1
- package/lib/src/embed/ts-embed.spec.js +45 -4
- package/lib/src/embed/ts-embed.spec.js.map +1 -1
- package/lib/src/index.d.ts +2 -2
- package/lib/src/index.d.ts.map +1 -1
- package/lib/src/index.js +2 -2
- package/lib/src/index.js.map +1 -1
- package/lib/src/react/all-types-export.d.ts +1 -1
- package/lib/src/react/all-types-export.d.ts.map +1 -1
- package/lib/src/react/all-types-export.js +1 -1
- package/lib/src/react/all-types-export.js.map +1 -1
- package/lib/src/react/index.d.ts +1 -1
- package/lib/src/react/index.d.ts.map +1 -1
- package/lib/src/react/index.js +1 -1
- package/lib/src/react/util.d.ts +7 -4
- package/lib/src/react/util.d.ts.map +1 -1
- package/lib/src/react/util.js.map +1 -1
- package/lib/src/types.d.ts +482 -251
- package/lib/src/types.d.ts.map +1 -1
- package/lib/src/types.js +31 -14
- package/lib/src/types.js.map +1 -1
- package/lib/src/utils/graphql/answerService/answerService.d.ts +2 -1
- package/lib/src/utils/graphql/answerService/answerService.d.ts.map +1 -1
- package/lib/src/utils/graphql/answerService/answerService.js +2 -1
- package/lib/src/utils/graphql/answerService/answerService.js.map +1 -1
- package/lib/src/utils/processData.d.ts.map +1 -1
- package/lib/src/utils/processData.js +15 -0
- package/lib/src/utils/processData.js.map +1 -1
- package/lib/src/utils/processData.spec.js +15 -0
- package/lib/src/utils/processData.spec.js.map +1 -1
- package/lib/src/utils/processTrigger.d.ts.map +1 -1
- package/lib/src/utils/processTrigger.js +14 -0
- package/lib/src/utils/processTrigger.js.map +1 -1
- package/lib/src/utils/processTrigger.spec.js +54 -0
- package/lib/src/utils/processTrigger.spec.js.map +1 -1
- package/lib/src/utils.d.ts +11 -2
- package/lib/src/utils.d.ts.map +1 -1
- package/lib/src/utils.js +67 -0
- package/lib/src/utils.js.map +1 -1
- package/lib/src/utils.spec.js +86 -1
- package/lib/src/utils.spec.js.map +1 -1
- package/lib/src/visual-embed-sdk.d.ts +836 -649
- package/package.json +1 -1
- package/src/embed/app.spec.ts +14 -0
- package/src/embed/app.ts +98 -153
- package/src/embed/bodyless-conversation.ts +4 -4
- package/src/embed/conversation.ts +23 -12
- package/src/embed/liveboard.spec.ts +15 -0
- package/src/embed/liveboard.ts +142 -182
- package/src/embed/sage.ts +17 -5
- package/src/embed/search-bar.tsx +16 -17
- package/src/embed/search.ts +47 -21
- package/src/embed/ts-embed.spec.ts +52 -4
- package/src/embed/ts-embed.ts +61 -6
- package/src/index.ts +2 -0
- package/src/react/all-types-export.ts +1 -0
- package/src/react/index.tsx +3 -3
- package/src/react/util.ts +6 -4
- package/src/types.ts +488 -253
- package/src/utils/graphql/answerService/answerService.ts +5 -4
- package/src/utils/processData.spec.ts +28 -0
- package/src/utils/processData.ts +17 -0
- package/src/utils/processTrigger.spec.ts +90 -0
- package/src/utils/processTrigger.ts +16 -1
- package/src/utils.spec.ts +113 -0
- package/src/utils.ts +78 -2
|
@@ -27,19 +27,20 @@ export interface UnderlyingDataPoint {
|
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* AnswerService provides a simple way to work with ThoughtSpot Answers.
|
|
30
|
-
*
|
|
30
|
+
*
|
|
31
31
|
* This service allows you to interact with ThoughtSpot Answers programmatically,
|
|
32
32
|
* making it easy to customize visualizations, filter data, and extract insights
|
|
33
33
|
* directly from your application.
|
|
34
|
-
*
|
|
34
|
+
*
|
|
35
35
|
* You can use this service to:
|
|
36
|
+
*
|
|
36
37
|
* - Add or remove columns from Answers (`addColumns`, `removeColumns`, `addColumnsByName`)
|
|
37
38
|
* - Apply filters to Answers (`addFilter`)
|
|
38
39
|
* - Get data from Answers in different formats (JSON, CSV, PNG) (`fetchData`, `fetchCSVBlob`, `fetchPNGBlob`)
|
|
39
40
|
* - Get data for specific points in visualizations (`getUnderlyingDataForPoint`)
|
|
40
41
|
* - Run custom queries (`executeQuery`)
|
|
41
|
-
* - Add visualizations to
|
|
42
|
-
*
|
|
42
|
+
* - Add visualizations to Liveboards (`addDisplayedVizToLiveboard`)
|
|
43
|
+
*
|
|
43
44
|
* @example
|
|
44
45
|
* ```js
|
|
45
46
|
* // Get the answer service
|
|
@@ -6,6 +6,8 @@ import * as base from '../embed/base';
|
|
|
6
6
|
import * as embedConfigInstance from '../embed/embedConfig';
|
|
7
7
|
import { EmbedEvent, AuthType } from '../types';
|
|
8
8
|
import * as sessionInfoService from './sessionInfoService';
|
|
9
|
+
import * as utilsModule from '../utils';
|
|
10
|
+
import { logger } from './logger';
|
|
9
11
|
|
|
10
12
|
describe('Unit test for process data', () => {
|
|
11
13
|
beforeAll(() => {
|
|
@@ -251,4 +253,30 @@ describe('Unit test for process data', () => {
|
|
|
251
253
|
expect(base.notifyAuthFailure).toBeCalledWith(auth.AuthFailureType.IDLE_SESSION_TIMEOUT);
|
|
252
254
|
expect(el.innerHTML).toBe('Hello');
|
|
253
255
|
});
|
|
256
|
+
|
|
257
|
+
test('should handle ExitPresentMode when disableFullscreenPresentation is false (enabled)', () => {
|
|
258
|
+
const mockConfig = {
|
|
259
|
+
disableFullscreenPresentation: false,
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const mockHandleExitPresentMode = jest.spyOn(utilsModule, 'handleExitPresentMode').mockImplementation(() => {});
|
|
263
|
+
|
|
264
|
+
jest.spyOn(embedConfigInstance, 'getEmbedConfig').mockReturnValue(mockConfig);
|
|
265
|
+
|
|
266
|
+
const processedData = {
|
|
267
|
+
type: EmbedEvent.ExitPresentMode,
|
|
268
|
+
data: {},
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
processDataInstance.processEventData(
|
|
272
|
+
EmbedEvent.ExitPresentMode,
|
|
273
|
+
processedData,
|
|
274
|
+
thoughtSpotHost,
|
|
275
|
+
null,
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
expect(mockHandleExitPresentMode).toHaveBeenCalled();
|
|
279
|
+
|
|
280
|
+
mockHandleExitPresentMode.mockReset();
|
|
281
|
+
});
|
|
254
282
|
});
|
package/src/utils/processData.ts
CHANGED
|
@@ -10,6 +10,21 @@ import { AuthType, CustomActionPayload, EmbedEvent } from '../types';
|
|
|
10
10
|
import { AnswerService } from './graphql/answerService/answerService';
|
|
11
11
|
import { resetCachedAuthToken } from '../authToken';
|
|
12
12
|
import { ERROR_MESSAGE } from '../errors';
|
|
13
|
+
import { logger } from '../utils/logger';
|
|
14
|
+
import { handleExitPresentMode } from '../utils';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Process the ExitPresentMode event and handle default fullscreen exit
|
|
18
|
+
* @param e - The event data
|
|
19
|
+
*/
|
|
20
|
+
function processExitPresentMode(e: any) {
|
|
21
|
+
const embedConfig = getEmbedConfig();
|
|
22
|
+
const disableFullscreenPresentation = embedConfig?.disableFullscreenPresentation ?? true;
|
|
23
|
+
|
|
24
|
+
if (!disableFullscreenPresentation) {
|
|
25
|
+
handleExitPresentMode();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
13
28
|
|
|
14
29
|
/**
|
|
15
30
|
*
|
|
@@ -136,6 +151,8 @@ export function processEventData(
|
|
|
136
151
|
return processAuthFailure(e, containerEl);
|
|
137
152
|
case EmbedEvent.AuthLogout:
|
|
138
153
|
return processAuthLogout(e, containerEl);
|
|
154
|
+
case EmbedEvent.ExitPresentMode:
|
|
155
|
+
return processExitPresentMode(e);
|
|
139
156
|
default:
|
|
140
157
|
}
|
|
141
158
|
return e;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import * as _processTriggerInstance from './processTrigger';
|
|
2
2
|
import { HostEvent } from '../types';
|
|
3
3
|
import { messageChannelMock, mockMessageChannel } from '../test/test-utils';
|
|
4
|
+
import { logger } from './logger';
|
|
5
|
+
import * as utilsModule from '../utils';
|
|
6
|
+
import * as embedConfigModule from '../embed/embedConfig';
|
|
4
7
|
|
|
5
8
|
describe('Unit test for processTrigger', () => {
|
|
6
9
|
const iFrame: any = {
|
|
@@ -8,6 +11,11 @@ describe('Unit test for processTrigger', () => {
|
|
|
8
11
|
postMessage: jest.fn(),
|
|
9
12
|
},
|
|
10
13
|
};
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
jest.clearAllMocks();
|
|
17
|
+
});
|
|
18
|
+
|
|
11
19
|
test('when hostevent is reload, reload function should be called with iFrame', async () => {
|
|
12
20
|
jest.useFakeTimers();
|
|
13
21
|
const iFrameElement = document.createElement('iframe');
|
|
@@ -86,4 +94,86 @@ describe('Unit test for processTrigger', () => {
|
|
|
86
94
|
expect(messageChannelMock.port1.close).toBeCalled();
|
|
87
95
|
await expect(triggerPromise).resolves.toBeInstanceOf(Error);
|
|
88
96
|
});
|
|
97
|
+
|
|
98
|
+
describe('Present event with fullscreen presentation flag', () => {
|
|
99
|
+
let mockHandlePresentEvent: any;
|
|
100
|
+
let mockLoggerWarn: any;
|
|
101
|
+
let mockGetEmbedConfig: any;
|
|
102
|
+
|
|
103
|
+
beforeEach(() => {
|
|
104
|
+
mockHandlePresentEvent = jest.spyOn(utilsModule, 'handlePresentEvent').mockImplementation(() => {});
|
|
105
|
+
mockLoggerWarn = jest.spyOn(logger, 'warn').mockImplementation(() => {});
|
|
106
|
+
mockGetEmbedConfig = jest.spyOn(embedConfigModule, 'getEmbedConfig').mockImplementation(() => ({}));
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
afterEach(() => {
|
|
110
|
+
mockHandlePresentEvent.mockReset();
|
|
111
|
+
mockLoggerWarn.mockReset();
|
|
112
|
+
mockGetEmbedConfig.mockReset();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('should handle Present event when disableFullscreenPresentation is false (enabled)', () => {
|
|
116
|
+
const mockConfig = {
|
|
117
|
+
disableFullscreenPresentation: false,
|
|
118
|
+
};
|
|
119
|
+
mockGetEmbedConfig.mockReturnValue(mockConfig);
|
|
120
|
+
|
|
121
|
+
const messageType = HostEvent.Present;
|
|
122
|
+
const thoughtSpotHost = 'https://example.thoughtspot.com';
|
|
123
|
+
const data = {};
|
|
124
|
+
|
|
125
|
+
_processTriggerInstance.processTrigger(
|
|
126
|
+
iFrame,
|
|
127
|
+
messageType,
|
|
128
|
+
thoughtSpotHost,
|
|
129
|
+
data,
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
expect(mockHandlePresentEvent).toHaveBeenCalledWith(iFrame);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test('should warn and not handle Present event when disableFullscreenPresentation is true (disabled)', () => {
|
|
136
|
+
const mockConfig = {
|
|
137
|
+
disableFullscreenPresentation: true,
|
|
138
|
+
};
|
|
139
|
+
mockGetEmbedConfig.mockReturnValue(mockConfig);
|
|
140
|
+
|
|
141
|
+
const messageType = HostEvent.Present;
|
|
142
|
+
const thoughtSpotHost = 'https://example.thoughtspot.com';
|
|
143
|
+
const data = {};
|
|
144
|
+
|
|
145
|
+
_processTriggerInstance.processTrigger(
|
|
146
|
+
iFrame,
|
|
147
|
+
messageType,
|
|
148
|
+
thoughtSpotHost,
|
|
149
|
+
data,
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
expect(mockHandlePresentEvent).not.toHaveBeenCalled();
|
|
153
|
+
expect(mockLoggerWarn).toHaveBeenCalledWith(
|
|
154
|
+
'Fullscreen presentation mode is disabled. Set disableFullscreenPresentation: false to enable this feature.',
|
|
155
|
+
);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test('should default to disabled when disableFullscreenPresentation is not provided', () => {
|
|
159
|
+
const mockConfig = {};
|
|
160
|
+
mockGetEmbedConfig.mockReturnValue(mockConfig);
|
|
161
|
+
|
|
162
|
+
const messageType = HostEvent.Present;
|
|
163
|
+
const thoughtSpotHost = 'https://example.thoughtspot.com';
|
|
164
|
+
const data = {};
|
|
165
|
+
|
|
166
|
+
_processTriggerInstance.processTrigger(
|
|
167
|
+
iFrame,
|
|
168
|
+
messageType,
|
|
169
|
+
thoughtSpotHost,
|
|
170
|
+
data,
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
expect(mockHandlePresentEvent).not.toHaveBeenCalled();
|
|
174
|
+
expect(mockLoggerWarn).toHaveBeenCalledWith(
|
|
175
|
+
'Fullscreen presentation mode is disabled. Set disableFullscreenPresentation: false to enable this feature.',
|
|
176
|
+
);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
89
179
|
});
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { ERROR_MESSAGE } from '../errors';
|
|
2
|
-
import { HostEvent } from '../types';
|
|
2
|
+
import { HostEvent, MessagePayload } from '../types';
|
|
3
|
+
import { logger } from '../utils/logger';
|
|
4
|
+
import { handlePresentEvent } from '../utils';
|
|
5
|
+
import { getEmbedConfig } from '../embed/embedConfig';
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
8
|
* Reloads the ThoughtSpot iframe.
|
|
@@ -51,6 +54,18 @@ export function processTrigger(
|
|
|
51
54
|
reload(iFrame);
|
|
52
55
|
return res(null);
|
|
53
56
|
}
|
|
57
|
+
|
|
58
|
+
if (messageType === HostEvent.Present) {
|
|
59
|
+
const embedConfig = getEmbedConfig();
|
|
60
|
+
const disableFullscreenPresentation = embedConfig?.disableFullscreenPresentation ?? true;
|
|
61
|
+
|
|
62
|
+
if (!disableFullscreenPresentation) {
|
|
63
|
+
handlePresentEvent(iFrame);
|
|
64
|
+
} else {
|
|
65
|
+
logger.warn('Fullscreen presentation mode is disabled. Set disableFullscreenPresentation: false to enable this feature.');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
54
69
|
const channel = new MessageChannel();
|
|
55
70
|
channel.port1.onmessage = ({ data: responseData }) => {
|
|
56
71
|
channel.port1.close();
|
package/src/utils.spec.ts
CHANGED
|
@@ -13,8 +13,19 @@ import {
|
|
|
13
13
|
isUndefined,
|
|
14
14
|
storeValueInWindow,
|
|
15
15
|
getValueFromWindow,
|
|
16
|
+
handlePresentEvent,
|
|
17
|
+
handleExitPresentMode,
|
|
16
18
|
} from './utils';
|
|
17
19
|
import { RuntimeFilterOp } from './types';
|
|
20
|
+
import { logger } from './utils/logger';
|
|
21
|
+
|
|
22
|
+
// Mock logger
|
|
23
|
+
jest.mock('./utils/logger', () => ({
|
|
24
|
+
logger: {
|
|
25
|
+
warn: jest.fn(),
|
|
26
|
+
error: jest.fn(),
|
|
27
|
+
},
|
|
28
|
+
}));
|
|
18
29
|
|
|
19
30
|
describe('unit test for utils', () => {
|
|
20
31
|
test('getQueryParamString', () => {
|
|
@@ -294,3 +305,105 @@ describe('unit test for utils', () => {
|
|
|
294
305
|
});
|
|
295
306
|
});
|
|
296
307
|
});
|
|
308
|
+
|
|
309
|
+
describe('Fullscreen Utility Functions', () => {
|
|
310
|
+
let originalExitFullscreen: any;
|
|
311
|
+
let mockIframe: HTMLIFrameElement;
|
|
312
|
+
|
|
313
|
+
beforeEach(() => {
|
|
314
|
+
jest.clearAllMocks();
|
|
315
|
+
|
|
316
|
+
// Store and mock exitFullscreen
|
|
317
|
+
originalExitFullscreen = document.exitFullscreen;
|
|
318
|
+
document.exitFullscreen = jest.fn();
|
|
319
|
+
|
|
320
|
+
// Mock iframe
|
|
321
|
+
mockIframe = {
|
|
322
|
+
requestFullscreen: jest.fn(),
|
|
323
|
+
} as any;
|
|
324
|
+
|
|
325
|
+
// Mock not in fullscreen initially
|
|
326
|
+
Object.defineProperty(document, 'fullscreenElement', {
|
|
327
|
+
writable: true,
|
|
328
|
+
value: null,
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
afterEach(() => {
|
|
333
|
+
// Restore original method
|
|
334
|
+
document.exitFullscreen = originalExitFullscreen;
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
describe('handlePresentEvent', () => {
|
|
338
|
+
it('should enter fullscreen when iframe is provided', () => {
|
|
339
|
+
const mockPromise = Promise.resolve();
|
|
340
|
+
(mockIframe.requestFullscreen as jest.Mock).mockReturnValue(mockPromise);
|
|
341
|
+
|
|
342
|
+
handlePresentEvent(mockIframe);
|
|
343
|
+
|
|
344
|
+
expect(mockIframe.requestFullscreen).toHaveBeenCalled();
|
|
345
|
+
expect(logger.error).not.toHaveBeenCalled();
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('should log error when fullscreen API is not supported', () => {
|
|
349
|
+
delete (mockIframe as any).requestFullscreen;
|
|
350
|
+
|
|
351
|
+
handlePresentEvent(mockIframe);
|
|
352
|
+
|
|
353
|
+
expect(logger.error).toHaveBeenCalledWith('Fullscreen API is not supported by this browser.');
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it('should not attempt fullscreen when already in fullscreen mode', () => {
|
|
357
|
+
Object.defineProperty(document, 'fullscreenElement', {
|
|
358
|
+
writable: true,
|
|
359
|
+
value: mockIframe,
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
handlePresentEvent(mockIframe);
|
|
363
|
+
|
|
364
|
+
expect(mockIframe.requestFullscreen).not.toHaveBeenCalled();
|
|
365
|
+
expect(logger.error).not.toHaveBeenCalled();
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
describe('handleExitPresentMode', () => {
|
|
370
|
+
beforeEach(() => {
|
|
371
|
+
// Mock being in fullscreen
|
|
372
|
+
Object.defineProperty(document, 'fullscreenElement', {
|
|
373
|
+
writable: true,
|
|
374
|
+
value: document.createElement('iframe'),
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it('should exit fullscreen when in fullscreen mode', () => {
|
|
379
|
+
const mockPromise = Promise.resolve();
|
|
380
|
+
(document.exitFullscreen as jest.Mock).mockReturnValue(mockPromise);
|
|
381
|
+
|
|
382
|
+
handleExitPresentMode();
|
|
383
|
+
|
|
384
|
+
expect(document.exitFullscreen).toHaveBeenCalled();
|
|
385
|
+
expect(logger.warn).not.toHaveBeenCalled();
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
it('should not attempt to exit when not in fullscreen mode', () => {
|
|
389
|
+
Object.defineProperty(document, 'fullscreenElement', {
|
|
390
|
+
writable: true,
|
|
391
|
+
value: null,
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
handleExitPresentMode();
|
|
395
|
+
|
|
396
|
+
expect(document.exitFullscreen).not.toHaveBeenCalled();
|
|
397
|
+
expect(logger.warn).not.toHaveBeenCalled();
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it('should log warning when exit fullscreen API is not supported', () => {
|
|
401
|
+
// Mock being in fullscreen but no exit methods available
|
|
402
|
+
document.exitFullscreen = undefined as any;
|
|
403
|
+
|
|
404
|
+
handleExitPresentMode();
|
|
405
|
+
|
|
406
|
+
expect(logger.warn).toHaveBeenCalledWith('Exit fullscreen API is not supported by this browser.');
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
});
|
package/src/utils.ts
CHANGED
|
@@ -13,9 +13,10 @@ import {
|
|
|
13
13
|
RuntimeFilter,
|
|
14
14
|
CustomisationsInterface,
|
|
15
15
|
DOMSelector,
|
|
16
|
-
ViewConfig,
|
|
17
16
|
RuntimeParameter,
|
|
17
|
+
AllEmbedViewConfig,
|
|
18
18
|
} from './types';
|
|
19
|
+
import { logger } from './utils/logger';
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Construct a runtime filters query string from the given filters.
|
|
@@ -216,7 +217,7 @@ export const checkReleaseVersionInBeta = (
|
|
|
216
217
|
|
|
217
218
|
export const getCustomisations = (
|
|
218
219
|
embedConfig: EmbedConfig,
|
|
219
|
-
viewConfig:
|
|
220
|
+
viewConfig: AllEmbedViewConfig,
|
|
220
221
|
): CustomisationsInterface => {
|
|
221
222
|
const customizationsFromViewConfig = viewConfig.customizations;
|
|
222
223
|
const customizationsFromEmbedConfig = embedConfig.customizations
|
|
@@ -387,3 +388,78 @@ export function resetValueFromWindow(key: string): boolean {
|
|
|
387
388
|
}
|
|
388
389
|
return false;
|
|
389
390
|
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Check if the document is currently in fullscreen mode
|
|
394
|
+
*/
|
|
395
|
+
const isInFullscreen = (): boolean => {
|
|
396
|
+
return !!(
|
|
397
|
+
document.fullscreenElement ||
|
|
398
|
+
(document as any).webkitFullscreenElement ||
|
|
399
|
+
(document as any).mozFullScreenElement ||
|
|
400
|
+
(document as any).msFullscreenElement
|
|
401
|
+
);
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Handle Present HostEvent by entering fullscreen mode
|
|
406
|
+
* @param iframe The iframe element to make fullscreen
|
|
407
|
+
*/
|
|
408
|
+
export const handlePresentEvent = async (iframe: HTMLIFrameElement): Promise<void> => {
|
|
409
|
+
if (isInFullscreen()) {
|
|
410
|
+
return; // Already in fullscreen
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Browser-specific methods to enter fullscreen mode
|
|
414
|
+
const fullscreenMethods = [
|
|
415
|
+
'requestFullscreen', // Standard API
|
|
416
|
+
'webkitRequestFullscreen', // WebKit browsers
|
|
417
|
+
'mozRequestFullScreen', // Firefox
|
|
418
|
+
'msRequestFullscreen' // IE/Edge
|
|
419
|
+
];
|
|
420
|
+
|
|
421
|
+
for (const method of fullscreenMethods) {
|
|
422
|
+
if (typeof (iframe as any)[method] === 'function') {
|
|
423
|
+
try {
|
|
424
|
+
const result = (iframe as any)[method]();
|
|
425
|
+
await Promise.resolve(result);
|
|
426
|
+
return;
|
|
427
|
+
} catch (error) {
|
|
428
|
+
logger.warn(`Failed to enter fullscreen using ${method}:`, error);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
logger.error('Fullscreen API is not supported by this browser.');
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Handle ExitPresentMode EmbedEvent by exiting fullscreen mode
|
|
438
|
+
*/
|
|
439
|
+
export const handleExitPresentMode = async (): Promise<void> => {
|
|
440
|
+
if (!isInFullscreen()) {
|
|
441
|
+
return; // Not in fullscreen
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const exitFullscreenMethods = [
|
|
445
|
+
'exitFullscreen', // Standard API
|
|
446
|
+
'webkitExitFullscreen', // WebKit browsers
|
|
447
|
+
'mozCancelFullScreen', // Firefox
|
|
448
|
+
'msExitFullscreen' // IE/Edge
|
|
449
|
+
];
|
|
450
|
+
|
|
451
|
+
// Try each method until one works
|
|
452
|
+
for (const method of exitFullscreenMethods) {
|
|
453
|
+
if (typeof (document as any)[method] === 'function') {
|
|
454
|
+
try {
|
|
455
|
+
const result = (document as any)[method]();
|
|
456
|
+
await Promise.resolve(result);
|
|
457
|
+
return;
|
|
458
|
+
} catch (error) {
|
|
459
|
+
logger.warn(`Failed to exit fullscreen using ${method}:`, error);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
logger.warn('Exit fullscreen API is not supported by this browser.');
|
|
465
|
+
};
|