@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.
Files changed (216) hide show
  1. package/README.md +1 -1
  2. package/cjs/package.json +1 -1
  3. package/cjs/src/embed/app.d.ts +96 -156
  4. package/cjs/src/embed/app.d.ts.map +1 -1
  5. package/cjs/src/embed/app.js +7 -4
  6. package/cjs/src/embed/app.js.map +1 -1
  7. package/cjs/src/embed/app.spec.js +10 -0
  8. package/cjs/src/embed/app.spec.js.map +1 -1
  9. package/cjs/src/embed/bodyless-conversation.d.ts +4 -4
  10. package/cjs/src/embed/bodyless-conversation.d.ts.map +1 -1
  11. package/cjs/src/embed/bodyless-conversation.js +1 -1
  12. package/cjs/src/embed/bodyless-conversation.js.map +1 -1
  13. package/cjs/src/embed/conversation.d.ts +22 -11
  14. package/cjs/src/embed/conversation.d.ts.map +1 -1
  15. package/cjs/src/embed/conversation.js +1 -1
  16. package/cjs/src/embed/conversation.js.map +1 -1
  17. package/cjs/src/embed/liveboard.d.ts +110 -167
  18. package/cjs/src/embed/liveboard.d.ts.map +1 -1
  19. package/cjs/src/embed/liveboard.js +25 -11
  20. package/cjs/src/embed/liveboard.js.map +1 -1
  21. package/cjs/src/embed/liveboard.spec.js +11 -0
  22. package/cjs/src/embed/liveboard.spec.js.map +1 -1
  23. package/cjs/src/embed/sage.d.ts +17 -2
  24. package/cjs/src/embed/sage.d.ts.map +1 -1
  25. package/cjs/src/embed/sage.js +2 -0
  26. package/cjs/src/embed/sage.js.map +1 -1
  27. package/cjs/src/embed/search-bar.d.ts +16 -6
  28. package/cjs/src/embed/search-bar.d.ts.map +1 -1
  29. package/cjs/src/embed/search-bar.js.map +1 -1
  30. package/cjs/src/embed/search.d.ts +46 -14
  31. package/cjs/src/embed/search.d.ts.map +1 -1
  32. package/cjs/src/embed/search.js.map +1 -1
  33. package/cjs/src/embed/ts-embed.d.ts +19 -2
  34. package/cjs/src/embed/ts-embed.d.ts.map +1 -1
  35. package/cjs/src/embed/ts-embed.js +49 -0
  36. package/cjs/src/embed/ts-embed.js.map +1 -1
  37. package/cjs/src/embed/ts-embed.spec.js +45 -4
  38. package/cjs/src/embed/ts-embed.spec.js.map +1 -1
  39. package/cjs/src/index.d.ts +2 -2
  40. package/cjs/src/index.d.ts.map +1 -1
  41. package/cjs/src/index.js +2 -1
  42. package/cjs/src/index.js.map +1 -1
  43. package/cjs/src/react/all-types-export.d.ts +1 -1
  44. package/cjs/src/react/all-types-export.d.ts.map +1 -1
  45. package/cjs/src/react/all-types-export.js +2 -1
  46. package/cjs/src/react/all-types-export.js.map +1 -1
  47. package/cjs/src/react/index.d.ts +1 -1
  48. package/cjs/src/react/index.d.ts.map +1 -1
  49. package/cjs/src/react/index.js +1 -1
  50. package/cjs/src/react/util.d.ts +7 -4
  51. package/cjs/src/react/util.d.ts.map +1 -1
  52. package/cjs/src/react/util.js.map +1 -1
  53. package/cjs/src/types.d.ts +482 -251
  54. package/cjs/src/types.d.ts.map +1 -1
  55. package/cjs/src/types.js +31 -14
  56. package/cjs/src/types.js.map +1 -1
  57. package/cjs/src/utils/graphql/answerService/answerService.d.ts +2 -1
  58. package/cjs/src/utils/graphql/answerService/answerService.d.ts.map +1 -1
  59. package/cjs/src/utils/graphql/answerService/answerService.js +2 -1
  60. package/cjs/src/utils/graphql/answerService/answerService.js.map +1 -1
  61. package/cjs/src/utils/processData.d.ts.map +1 -1
  62. package/cjs/src/utils/processData.js +15 -0
  63. package/cjs/src/utils/processData.js.map +1 -1
  64. package/cjs/src/utils/processData.spec.js +15 -0
  65. package/cjs/src/utils/processData.spec.js.map +1 -1
  66. package/cjs/src/utils/processTrigger.d.ts.map +1 -1
  67. package/cjs/src/utils/processTrigger.js +14 -0
  68. package/cjs/src/utils/processTrigger.js.map +1 -1
  69. package/cjs/src/utils/processTrigger.spec.js +54 -0
  70. package/cjs/src/utils/processTrigger.spec.js.map +1 -1
  71. package/cjs/src/utils.d.ts +11 -2
  72. package/cjs/src/utils.d.ts.map +1 -1
  73. package/cjs/src/utils.js +70 -1
  74. package/cjs/src/utils.js.map +1 -1
  75. package/cjs/src/utils.spec.js +85 -0
  76. package/cjs/src/utils.spec.js.map +1 -1
  77. package/dist/{index-BIcnpmMY.js → index-BKIWUvTr.js} +1 -1
  78. package/dist/src/embed/app.d.ts +96 -156
  79. package/dist/src/embed/app.d.ts.map +1 -1
  80. package/dist/src/embed/bodyless-conversation.d.ts +4 -4
  81. package/dist/src/embed/bodyless-conversation.d.ts.map +1 -1
  82. package/dist/src/embed/conversation.d.ts +22 -11
  83. package/dist/src/embed/conversation.d.ts.map +1 -1
  84. package/dist/src/embed/liveboard.d.ts +110 -167
  85. package/dist/src/embed/liveboard.d.ts.map +1 -1
  86. package/dist/src/embed/sage.d.ts +17 -2
  87. package/dist/src/embed/sage.d.ts.map +1 -1
  88. package/dist/src/embed/search-bar.d.ts +16 -6
  89. package/dist/src/embed/search-bar.d.ts.map +1 -1
  90. package/dist/src/embed/search.d.ts +46 -14
  91. package/dist/src/embed/search.d.ts.map +1 -1
  92. package/dist/src/embed/ts-embed.d.ts +19 -2
  93. package/dist/src/embed/ts-embed.d.ts.map +1 -1
  94. package/dist/src/index.d.ts +2 -2
  95. package/dist/src/index.d.ts.map +1 -1
  96. package/dist/src/react/all-types-export.d.ts +1 -1
  97. package/dist/src/react/all-types-export.d.ts.map +1 -1
  98. package/dist/src/react/index.d.ts +1 -1
  99. package/dist/src/react/index.d.ts.map +1 -1
  100. package/dist/src/react/util.d.ts +7 -4
  101. package/dist/src/react/util.d.ts.map +1 -1
  102. package/dist/src/types.d.ts +482 -251
  103. package/dist/src/types.d.ts.map +1 -1
  104. package/dist/src/utils/graphql/answerService/answerService.d.ts +2 -1
  105. package/dist/src/utils/graphql/answerService/answerService.d.ts.map +1 -1
  106. package/dist/src/utils/processData.d.ts.map +1 -1
  107. package/dist/src/utils/processTrigger.d.ts.map +1 -1
  108. package/dist/src/utils.d.ts +11 -2
  109. package/dist/src/utils.d.ts.map +1 -1
  110. package/dist/tsembed-react.es.js +3498 -3322
  111. package/dist/tsembed-react.js +3497 -3321
  112. package/dist/tsembed.es.js +213 -37
  113. package/dist/tsembed.js +216 -40
  114. package/dist/visual-embed-sdk-react-full.d.ts +845 -655
  115. package/dist/visual-embed-sdk-react.d.ts +845 -655
  116. package/dist/visual-embed-sdk.d.ts +827 -640
  117. package/lib/package.json +1 -1
  118. package/lib/src/embed/app.d.ts +96 -156
  119. package/lib/src/embed/app.d.ts.map +1 -1
  120. package/lib/src/embed/app.js +7 -4
  121. package/lib/src/embed/app.js.map +1 -1
  122. package/lib/src/embed/app.spec.js +10 -0
  123. package/lib/src/embed/app.spec.js.map +1 -1
  124. package/lib/src/embed/bodyless-conversation.d.ts +4 -4
  125. package/lib/src/embed/bodyless-conversation.d.ts.map +1 -1
  126. package/lib/src/embed/bodyless-conversation.js +1 -1
  127. package/lib/src/embed/bodyless-conversation.js.map +1 -1
  128. package/lib/src/embed/conversation.d.ts +22 -11
  129. package/lib/src/embed/conversation.d.ts.map +1 -1
  130. package/lib/src/embed/conversation.js +1 -1
  131. package/lib/src/embed/conversation.js.map +1 -1
  132. package/lib/src/embed/liveboard.d.ts +110 -167
  133. package/lib/src/embed/liveboard.d.ts.map +1 -1
  134. package/lib/src/embed/liveboard.js +25 -11
  135. package/lib/src/embed/liveboard.js.map +1 -1
  136. package/lib/src/embed/liveboard.spec.js +11 -0
  137. package/lib/src/embed/liveboard.spec.js.map +1 -1
  138. package/lib/src/embed/sage.d.ts +17 -2
  139. package/lib/src/embed/sage.d.ts.map +1 -1
  140. package/lib/src/embed/sage.js +2 -0
  141. package/lib/src/embed/sage.js.map +1 -1
  142. package/lib/src/embed/search-bar.d.ts +16 -6
  143. package/lib/src/embed/search-bar.d.ts.map +1 -1
  144. package/lib/src/embed/search-bar.js.map +1 -1
  145. package/lib/src/embed/search.d.ts +46 -14
  146. package/lib/src/embed/search.d.ts.map +1 -1
  147. package/lib/src/embed/search.js.map +1 -1
  148. package/lib/src/embed/ts-embed.d.ts +19 -2
  149. package/lib/src/embed/ts-embed.d.ts.map +1 -1
  150. package/lib/src/embed/ts-embed.js +49 -0
  151. package/lib/src/embed/ts-embed.js.map +1 -1
  152. package/lib/src/embed/ts-embed.spec.js +45 -4
  153. package/lib/src/embed/ts-embed.spec.js.map +1 -1
  154. package/lib/src/index.d.ts +2 -2
  155. package/lib/src/index.d.ts.map +1 -1
  156. package/lib/src/index.js +2 -2
  157. package/lib/src/index.js.map +1 -1
  158. package/lib/src/react/all-types-export.d.ts +1 -1
  159. package/lib/src/react/all-types-export.d.ts.map +1 -1
  160. package/lib/src/react/all-types-export.js +1 -1
  161. package/lib/src/react/all-types-export.js.map +1 -1
  162. package/lib/src/react/index.d.ts +1 -1
  163. package/lib/src/react/index.d.ts.map +1 -1
  164. package/lib/src/react/index.js +1 -1
  165. package/lib/src/react/util.d.ts +7 -4
  166. package/lib/src/react/util.d.ts.map +1 -1
  167. package/lib/src/react/util.js.map +1 -1
  168. package/lib/src/types.d.ts +482 -251
  169. package/lib/src/types.d.ts.map +1 -1
  170. package/lib/src/types.js +31 -14
  171. package/lib/src/types.js.map +1 -1
  172. package/lib/src/utils/graphql/answerService/answerService.d.ts +2 -1
  173. package/lib/src/utils/graphql/answerService/answerService.d.ts.map +1 -1
  174. package/lib/src/utils/graphql/answerService/answerService.js +2 -1
  175. package/lib/src/utils/graphql/answerService/answerService.js.map +1 -1
  176. package/lib/src/utils/processData.d.ts.map +1 -1
  177. package/lib/src/utils/processData.js +15 -0
  178. package/lib/src/utils/processData.js.map +1 -1
  179. package/lib/src/utils/processData.spec.js +15 -0
  180. package/lib/src/utils/processData.spec.js.map +1 -1
  181. package/lib/src/utils/processTrigger.d.ts.map +1 -1
  182. package/lib/src/utils/processTrigger.js +14 -0
  183. package/lib/src/utils/processTrigger.js.map +1 -1
  184. package/lib/src/utils/processTrigger.spec.js +54 -0
  185. package/lib/src/utils/processTrigger.spec.js.map +1 -1
  186. package/lib/src/utils.d.ts +11 -2
  187. package/lib/src/utils.d.ts.map +1 -1
  188. package/lib/src/utils.js +67 -0
  189. package/lib/src/utils.js.map +1 -1
  190. package/lib/src/utils.spec.js +86 -1
  191. package/lib/src/utils.spec.js.map +1 -1
  192. package/lib/src/visual-embed-sdk.d.ts +836 -649
  193. package/package.json +1 -1
  194. package/src/embed/app.spec.ts +14 -0
  195. package/src/embed/app.ts +98 -153
  196. package/src/embed/bodyless-conversation.ts +4 -4
  197. package/src/embed/conversation.ts +23 -12
  198. package/src/embed/liveboard.spec.ts +15 -0
  199. package/src/embed/liveboard.ts +142 -182
  200. package/src/embed/sage.ts +17 -5
  201. package/src/embed/search-bar.tsx +16 -17
  202. package/src/embed/search.ts +47 -21
  203. package/src/embed/ts-embed.spec.ts +52 -4
  204. package/src/embed/ts-embed.ts +61 -6
  205. package/src/index.ts +2 -0
  206. package/src/react/all-types-export.ts +1 -0
  207. package/src/react/index.tsx +3 -3
  208. package/src/react/util.ts +6 -4
  209. package/src/types.ts +488 -253
  210. package/src/utils/graphql/answerService/answerService.ts +5 -4
  211. package/src/utils/processData.spec.ts +28 -0
  212. package/src/utils/processData.ts +17 -0
  213. package/src/utils/processTrigger.spec.ts +90 -0
  214. package/src/utils/processTrigger.ts +16 -1
  215. package/src/utils.spec.ts +113 -0
  216. 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 liveboards (`addDisplayedVizToLiveboard`)
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
  });
@@ -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: 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
+ };