@thoughtspot/visual-embed-sdk 1.39.2-alpha.1 → 1.39.2-alpha.2
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 +1 -1
- package/cjs/src/config.spec.js +9 -0
- package/cjs/src/config.spec.js.map +1 -1
- package/cjs/src/embed/app.d.ts +75 -15
- package/cjs/src/embed/app.d.ts.map +1 -1
- package/cjs/src/embed/app.js +68 -9
- package/cjs/src/embed/app.js.map +1 -1
- package/cjs/src/embed/app.spec.js +356 -4
- package/cjs/src/embed/app.spec.js.map +1 -1
- package/cjs/src/embed/bodyless-conversation.d.ts +19 -7
- package/cjs/src/embed/bodyless-conversation.d.ts.map +1 -1
- package/cjs/src/embed/bodyless-conversation.js +24 -4
- package/cjs/src/embed/bodyless-conversation.js.map +1 -1
- package/cjs/src/embed/bodyless-conversation.spec.js +8 -190
- package/cjs/src/embed/bodyless-conversation.spec.js.map +1 -1
- package/cjs/src/embed/conversation.d.ts +2 -60
- package/cjs/src/embed/conversation.d.ts.map +1 -1
- package/cjs/src/embed/conversation.js +1 -9
- package/cjs/src/embed/conversation.js.map +1 -1
- package/cjs/src/embed/conversation.spec.js +0 -102
- package/cjs/src/embed/conversation.spec.js.map +1 -1
- package/cjs/src/embed/liveboard.d.ts +56 -0
- package/cjs/src/embed/liveboard.d.ts.map +1 -1
- package/cjs/src/embed/liveboard.js +46 -0
- package/cjs/src/embed/liveboard.js.map +1 -1
- package/cjs/src/embed/liveboard.spec.js +203 -0
- package/cjs/src/embed/liveboard.spec.js.map +1 -1
- package/cjs/src/errors.d.ts +1 -0
- package/cjs/src/errors.d.ts.map +1 -1
- package/cjs/src/errors.js +1 -0
- package/cjs/src/errors.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 +3 -2
- package/cjs/src/react/all-types-export.js.map +1 -1
- package/cjs/src/react/index.d.ts +71 -20
- package/cjs/src/react/index.d.ts.map +1 -1
- package/cjs/src/react/index.js +79 -42
- package/cjs/src/react/index.js.map +1 -1
- package/cjs/src/react/index.spec.js +436 -100
- package/cjs/src/react/index.spec.js.map +1 -1
- package/cjs/src/types.d.ts +46 -4
- package/cjs/src/types.d.ts.map +1 -1
- package/cjs/src/types.js +28 -0
- package/cjs/src/types.js.map +1 -1
- package/cjs/src/utils/graphql/nlsService/conversation-service.d.ts.map +1 -1
- package/cjs/src/utils/graphql/nlsService/conversation-service.js +2 -0
- package/cjs/src/utils/graphql/nlsService/conversation-service.js.map +1 -1
- package/cjs/src/utils/processTrigger.js +2 -1
- package/cjs/src/utils/processTrigger.js.map +1 -1
- package/cjs/src/utils.d.ts +6 -0
- package/cjs/src/utils.d.ts.map +1 -1
- package/cjs/src/utils.js +23 -3
- package/cjs/src/utils.js.map +1 -1
- package/cjs/src/utils.spec.js +237 -1
- package/cjs/src/utils.spec.js.map +1 -1
- package/dist/index-e3Uw3YFO.js +7371 -0
- package/dist/src/embed/app.d.ts +75 -15
- package/dist/src/embed/app.d.ts.map +1 -1
- package/dist/src/embed/bodyless-conversation.d.ts +19 -7
- package/dist/src/embed/bodyless-conversation.d.ts.map +1 -1
- package/dist/src/embed/conversation.d.ts +2 -60
- package/dist/src/embed/conversation.d.ts.map +1 -1
- package/dist/src/embed/liveboard.d.ts +56 -0
- package/dist/src/embed/liveboard.d.ts.map +1 -1
- package/dist/src/errors.d.ts +1 -0
- package/dist/src/errors.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 +71 -20
- package/dist/src/react/index.d.ts.map +1 -1
- package/dist/src/types.d.ts +46 -4
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/graphql/nlsService/conversation-service.d.ts.map +1 -1
- package/dist/src/utils.d.ts +6 -0
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/tsembed-react.es.js +276 -74
- package/dist/tsembed-react.js +276 -72
- package/dist/tsembed.es.js +194 -27
- package/dist/tsembed.js +192 -25
- package/dist/visual-embed-sdk-react-full.d.ts +246 -105
- package/dist/visual-embed-sdk-react.d.ts +246 -105
- package/dist/visual-embed-sdk.d.ts +176 -86
- package/lib/package.json +1 -1
- package/lib/src/config.spec.js +9 -0
- package/lib/src/config.spec.js.map +1 -1
- package/lib/src/embed/app.d.ts +75 -15
- package/lib/src/embed/app.d.ts.map +1 -1
- package/lib/src/embed/app.js +68 -9
- package/lib/src/embed/app.js.map +1 -1
- package/lib/src/embed/app.spec.js +357 -5
- package/lib/src/embed/app.spec.js.map +1 -1
- package/lib/src/embed/bodyless-conversation.d.ts +19 -7
- package/lib/src/embed/bodyless-conversation.d.ts.map +1 -1
- package/lib/src/embed/bodyless-conversation.js +23 -4
- package/lib/src/embed/bodyless-conversation.js.map +1 -1
- package/lib/src/embed/bodyless-conversation.spec.js +9 -191
- package/lib/src/embed/bodyless-conversation.spec.js.map +1 -1
- package/lib/src/embed/conversation.d.ts +2 -60
- package/lib/src/embed/conversation.d.ts.map +1 -1
- package/lib/src/embed/conversation.js +2 -10
- package/lib/src/embed/conversation.js.map +1 -1
- package/lib/src/embed/conversation.spec.js +2 -104
- package/lib/src/embed/conversation.spec.js.map +1 -1
- package/lib/src/embed/liveboard.d.ts +56 -0
- package/lib/src/embed/liveboard.d.ts.map +1 -1
- package/lib/src/embed/liveboard.js +47 -1
- package/lib/src/embed/liveboard.js.map +1 -1
- package/lib/src/embed/liveboard.spec.js +203 -0
- package/lib/src/embed/liveboard.spec.js.map +1 -1
- package/lib/src/errors.d.ts +1 -0
- package/lib/src/errors.d.ts.map +1 -1
- package/lib/src/errors.js +1 -0
- package/lib/src/errors.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 +71 -20
- package/lib/src/react/index.d.ts.map +1 -1
- package/lib/src/react/index.js +79 -43
- package/lib/src/react/index.js.map +1 -1
- package/lib/src/react/index.spec.js +439 -103
- package/lib/src/react/index.spec.js.map +1 -1
- package/lib/src/types.d.ts +46 -4
- package/lib/src/types.d.ts.map +1 -1
- package/lib/src/types.js +28 -0
- package/lib/src/types.js.map +1 -1
- package/lib/src/utils/graphql/nlsService/conversation-service.d.ts.map +1 -1
- package/lib/src/utils/graphql/nlsService/conversation-service.js +2 -0
- package/lib/src/utils/graphql/nlsService/conversation-service.js.map +1 -1
- package/lib/src/utils/processTrigger.js +2 -1
- package/lib/src/utils/processTrigger.js.map +1 -1
- package/lib/src/utils.d.ts +6 -0
- package/lib/src/utils.d.ts.map +1 -1
- package/lib/src/utils.js +21 -2
- package/lib/src/utils.js.map +1 -1
- package/lib/src/utils.spec.js +238 -2
- package/lib/src/utils.spec.js.map +1 -1
- package/lib/src/visual-embed-sdk.d.ts +178 -88
- package/package.json +1 -1
- package/src/config.spec.ts +11 -0
- package/src/embed/app.spec.ts +444 -4
- package/src/embed/app.ts +131 -27
- package/src/embed/bodyless-conversation.spec.ts +9 -203
- package/src/embed/bodyless-conversation.ts +24 -10
- package/src/embed/conversation.spec.ts +5 -131
- package/src/embed/conversation.ts +10 -82
- package/src/embed/liveboard.spec.ts +251 -1
- package/src/embed/liveboard.ts +97 -5
- package/src/errors.ts +1 -0
- package/src/index.ts +2 -0
- package/src/react/all-types-export.ts +2 -1
- package/src/react/index.spec.tsx +556 -157
- package/src/react/index.tsx +117 -51
- package/src/types.ts +42 -0
- package/src/utils/graphql/nlsService/conversation-service.ts +2 -0
- package/src/utils/processTrigger.ts +1 -1
- package/src/utils.spec.ts +279 -2
- package/src/utils.ts +28 -2
package/src/react/index.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import React, { useRef } from 'react';
|
|
3
|
+
import React, { useRef, useCallback } from 'react';
|
|
4
4
|
import useDeepCompareEffect from 'use-deep-compare-effect';
|
|
5
5
|
import { AuthEventEmitter } from '../auth';
|
|
6
6
|
import { deepMerge } from '../utils';
|
|
@@ -10,12 +10,13 @@ import { SearchEmbed as _SearchEmbed, SearchViewConfig } from '../embed/search';
|
|
|
10
10
|
import { AppEmbed as _AppEmbed, AppViewConfig } from '../embed/app';
|
|
11
11
|
import { LiveboardEmbed as _LiveboardEmbed, LiveboardViewConfig } from '../embed/liveboard';
|
|
12
12
|
import { TsEmbed } from '../embed/ts-embed';
|
|
13
|
-
import { SpotterAgentEmbed as _SpotterAgentEmbed, SpotterAgentEmbedViewConfig } from '../embed/bodyless-conversation';
|
|
13
|
+
import { SpotterAgentEmbed as _SpotterAgentEmbed, SpotterAgentEmbedViewConfig, ConversationMessage as _ConversationMessage, SpotterAgentMessageViewConfig } from '../embed/bodyless-conversation';
|
|
14
14
|
|
|
15
15
|
import { EmbedConfig, EmbedEvent, AllEmbedViewConfig } from '../types';
|
|
16
16
|
import { EmbedProps, getViewPropsAndListeners } from './util';
|
|
17
17
|
import { SpotterEmbed as _SpotterEmbed, SpotterEmbedViewConfig, ConversationEmbed as _ConversationEmbed, ConversationViewConfig } from '../embed/conversation';
|
|
18
18
|
import { init } from '../embed/base';
|
|
19
|
+
import { ERROR_MESSAGE } from '../errors';
|
|
19
20
|
|
|
20
21
|
const componentFactory = <T extends typeof TsEmbed, U extends EmbedProps, V extends AllEmbedViewConfig>(
|
|
21
22
|
EmbedConstructor: T,
|
|
@@ -383,62 +384,61 @@ export const ConversationEmbed = componentFactory<
|
|
|
383
384
|
ConversationViewConfig
|
|
384
385
|
>(_ConversationEmbed);
|
|
385
386
|
|
|
386
|
-
|
|
387
|
+
/**
|
|
388
|
+
* React component for individual conversation messages from SpotterAgent.
|
|
389
|
+
* This component is used internally by the useSpotterAgent hook.
|
|
390
|
+
* @version SDK: 1.37.0 | ThoughtSpot: 10.9.0.cl
|
|
391
|
+
*/
|
|
392
|
+
interface ConversationMessageProps extends EmbedProps, SpotterAgentMessageViewConfig {}
|
|
393
|
+
|
|
394
|
+
export const ConversationMessage = componentFactory<
|
|
395
|
+
typeof _ConversationMessage,
|
|
396
|
+
ConversationMessageProps,
|
|
397
|
+
SpotterAgentMessageViewConfig
|
|
398
|
+
>(_ConversationMessage);
|
|
399
|
+
|
|
400
|
+
type SpotterMessageProps = {
|
|
401
|
+
message: SpotterAgentMessageViewConfig;
|
|
402
|
+
query?: string;
|
|
403
|
+
} & Omit<EmbedProps, keyof SpotterAgentMessageViewConfig>;
|
|
387
404
|
|
|
388
405
|
/**
|
|
389
|
-
* React component for
|
|
390
|
-
*
|
|
406
|
+
* React component for displaying individual conversation messages from SpotterAgent.
|
|
407
|
+
*
|
|
408
|
+
* This component renders a single message response from your ThoughtSpot conversation,
|
|
409
|
+
* showing charts, visualizations, or text responses based on the user's query.
|
|
410
|
+
*
|
|
391
411
|
* @example
|
|
392
412
|
* ```tsx
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
*
|
|
396
|
-
*
|
|
397
|
-
*
|
|
398
|
-
*
|
|
399
|
-
*
|
|
400
|
-
*
|
|
401
|
-
*
|
|
402
|
-
*
|
|
403
|
-
*
|
|
404
|
-
*
|
|
405
|
-
* <SpotterAgentEmbed ref={ref} worksheetId="worksheetId" />
|
|
406
|
-
* <button onClick={handleSendMessage}>Send Message</button>
|
|
407
|
-
* </div>
|
|
408
|
-
* );
|
|
413
|
+
* const { sendMessage } = useSpotterAgent({ worksheetId: 'worksheetId' });
|
|
414
|
+
* const result = await sendMessage('show me sales by region');
|
|
415
|
+
*
|
|
416
|
+
* if (!result.error) {
|
|
417
|
+
* // Simple usage - just pass the message data
|
|
418
|
+
* <SpotterMessage message={result.message} />
|
|
419
|
+
*
|
|
420
|
+
* // With optional query for context
|
|
421
|
+
* <SpotterMessage
|
|
422
|
+
* message={result.message}
|
|
423
|
+
* query={result.query}
|
|
424
|
+
* />
|
|
409
425
|
* }
|
|
410
426
|
* ```
|
|
427
|
+
* @version SDK: 1.39.0 | ThoughtSpot: 10.11.0.cl
|
|
411
428
|
*/
|
|
412
|
-
export const
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
if (serviceRef.current) {
|
|
418
|
-
serviceRef.current = null;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
const configProps = {
|
|
422
|
-
...restProps,
|
|
423
|
-
...(className ? { containerClassName: className } : {})
|
|
424
|
-
};
|
|
425
|
-
|
|
426
|
-
serviceRef.current = new _SpotterAgentEmbed(configProps);
|
|
429
|
+
export const SpotterMessage = React.forwardRef<
|
|
430
|
+
React.ComponentRef<typeof ConversationMessage>,
|
|
431
|
+
SpotterMessageProps
|
|
432
|
+
>((props, ref) => {
|
|
433
|
+
const { message, query: _, ...otherProps } = props;
|
|
427
434
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
return () => {
|
|
437
|
-
serviceRef.current = null;
|
|
438
|
-
};
|
|
439
|
-
}, [props]);
|
|
440
|
-
|
|
441
|
-
return null;
|
|
435
|
+
return (
|
|
436
|
+
<ConversationMessage
|
|
437
|
+
ref={ref}
|
|
438
|
+
{...message}
|
|
439
|
+
{...otherProps}
|
|
440
|
+
/>
|
|
441
|
+
);
|
|
442
442
|
});
|
|
443
443
|
|
|
444
444
|
/**
|
|
@@ -470,7 +470,8 @@ type EmbedComponent = typeof SearchEmbed
|
|
|
470
470
|
| typeof LiveboardEmbed
|
|
471
471
|
| typeof SearchBarEmbed
|
|
472
472
|
| typeof SageEmbed
|
|
473
|
-
| typeof
|
|
473
|
+
| typeof ConversationMessage
|
|
474
|
+
| typeof SpotterMessage
|
|
474
475
|
| typeof SpotterEmbed
|
|
475
476
|
| typeof ConversationEmbed;
|
|
476
477
|
|
|
@@ -518,6 +519,71 @@ export function useInit(config: EmbedConfig) {
|
|
|
518
519
|
return ref;
|
|
519
520
|
}
|
|
520
521
|
|
|
522
|
+
/**
|
|
523
|
+
* React hook for interacting with SpotterAgent AI conversations.
|
|
524
|
+
*
|
|
525
|
+
* This hook provides a sendMessage function that allows you to send natural language
|
|
526
|
+
* queries to your data and get back AI-generated responses with visualizations.
|
|
527
|
+
*
|
|
528
|
+
* @param config - Configuration object containing worksheetId and other options
|
|
529
|
+
* @returns Object with sendMessage function that returns conversation results
|
|
530
|
+
* @example
|
|
531
|
+
* ```tsx
|
|
532
|
+
* const { sendMessage } = useSpotterAgent({ worksheetId: 'worksheetId' });
|
|
533
|
+
*
|
|
534
|
+
* const handleQuery = async () => {
|
|
535
|
+
* const result = await sendMessage('show me sales by region');
|
|
536
|
+
*
|
|
537
|
+
* if (!result.error) {
|
|
538
|
+
* // Display the message response
|
|
539
|
+
* <SpotterMessage message={result.message} />
|
|
540
|
+
* } else {
|
|
541
|
+
* console.error('Error:', result.error);
|
|
542
|
+
* }
|
|
543
|
+
* };
|
|
544
|
+
* ```
|
|
545
|
+
* @version SDK: 1.39.0 | ThoughtSpot: 10.11.0.cl
|
|
546
|
+
*/
|
|
547
|
+
export function useSpotterAgent(config: SpotterAgentEmbedViewConfig) {
|
|
548
|
+
const serviceRef = useRef<_SpotterAgentEmbed | null>(null);
|
|
549
|
+
|
|
550
|
+
useDeepCompareEffect(() => {
|
|
551
|
+
if (serviceRef.current) {
|
|
552
|
+
serviceRef.current = null;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
serviceRef.current = new _SpotterAgentEmbed(config);
|
|
556
|
+
|
|
557
|
+
return () => {
|
|
558
|
+
serviceRef.current = null;
|
|
559
|
+
};
|
|
560
|
+
}, [config]);
|
|
561
|
+
|
|
562
|
+
const sendMessage = useCallback(async (query: string) => {
|
|
563
|
+
if (!serviceRef.current) {
|
|
564
|
+
return { error: new Error(ERROR_MESSAGE.SPOTTER_AGENT_NOT_INITIALIZED) };
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
const result = await serviceRef.current.sendMessageData(query);
|
|
568
|
+
|
|
569
|
+
if (result.error) {
|
|
570
|
+
return { error: result.error };
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return {
|
|
574
|
+
query: query,
|
|
575
|
+
message: {
|
|
576
|
+
...result.data,
|
|
577
|
+
worksheetId: config.worksheetId,
|
|
578
|
+
},
|
|
579
|
+
};
|
|
580
|
+
}, [config.worksheetId]);
|
|
581
|
+
|
|
582
|
+
return {
|
|
583
|
+
sendMessage,
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
|
|
521
587
|
export {
|
|
522
588
|
LiveboardViewConfig,
|
|
523
589
|
SearchViewConfig,
|
package/src/types.ts
CHANGED
|
@@ -217,6 +217,12 @@ export enum HomeLeftNavItem {
|
|
|
217
217
|
* @version SDK: 1.40.0 | ThoughtSpot: 10.11.0.cl
|
|
218
218
|
*/
|
|
219
219
|
Spotter = 'spotter',
|
|
220
|
+
/**
|
|
221
|
+
* Favorites option in the insights left navigation,
|
|
222
|
+
* available when new navigation V3 is enabled.
|
|
223
|
+
* @version SDK: 1.41.0 | ThoughtSpot: 10.12.0.cl
|
|
224
|
+
*/
|
|
225
|
+
Favorites = 'favorites',
|
|
220
226
|
}
|
|
221
227
|
export type DOMSelector = string | HTMLElement;
|
|
222
228
|
|
|
@@ -1010,6 +1016,20 @@ export interface BaseViewConfig {
|
|
|
1010
1016
|
* @private
|
|
1011
1017
|
*/
|
|
1012
1018
|
insertInToSlide?: boolean;
|
|
1019
|
+
/**
|
|
1020
|
+
* Show alert messages and toast messages in the embed.
|
|
1021
|
+
* Supported embed in all embed types.
|
|
1022
|
+
*
|
|
1023
|
+
* @version SDK: 1.11.0 | ThoughtSpot: 8.3.0.cl, 8.4.1.sw
|
|
1024
|
+
* @example
|
|
1025
|
+
* ```js
|
|
1026
|
+
* const embed = new AppEmbed('#tsEmbed', {
|
|
1027
|
+
* ... // other embed view config
|
|
1028
|
+
* showAlerts:true,
|
|
1029
|
+
* })
|
|
1030
|
+
* ```
|
|
1031
|
+
*/
|
|
1032
|
+
showAlerts?: boolean;
|
|
1013
1033
|
}
|
|
1014
1034
|
|
|
1015
1035
|
/**
|
|
@@ -2687,6 +2707,12 @@ export enum EmbedEvent {
|
|
|
2687
2707
|
* @version SDK : 1.40.0 | ThoughtSpot: 10.11.0.cl
|
|
2688
2708
|
*/
|
|
2689
2709
|
ExitPresentMode = 'exitPresentMode',
|
|
2710
|
+
/**
|
|
2711
|
+
* Emitted when a user requests the full height lazy load data.
|
|
2712
|
+
* @version SDK : 1.39.0 | ThoughtSpot : 10.10.0.cl
|
|
2713
|
+
* @hidden
|
|
2714
|
+
*/
|
|
2715
|
+
RequestVisibleEmbedCoordinates = 'requestVisibleEmbedCoordinates',
|
|
2690
2716
|
}
|
|
2691
2717
|
|
|
2692
2718
|
/**
|
|
@@ -3771,6 +3797,19 @@ export enum HostEvent {
|
|
|
3771
3797
|
* @version SDK: 1.40.0 | ThoughtSpot: 10.11.0.cl
|
|
3772
3798
|
*/
|
|
3773
3799
|
ExitPresentMode = 'exitPresentMode',
|
|
3800
|
+
/**
|
|
3801
|
+
* Triggers the full height lazy load data.
|
|
3802
|
+
* @example
|
|
3803
|
+
* ```js
|
|
3804
|
+
* liveboardEmbed.on(EmbedEvent.RequestVisibleEmbedCoordinates, (payload) => {
|
|
3805
|
+
* console.log(payload);
|
|
3806
|
+
* });
|
|
3807
|
+
* ```
|
|
3808
|
+
* @version SDK: 1.39.0 | ThoughtSpot: 10.10.0.cl
|
|
3809
|
+
*
|
|
3810
|
+
* @hidden
|
|
3811
|
+
*/
|
|
3812
|
+
VisibleEmbedCoordinates = 'visibleEmbedCoordinates',
|
|
3774
3813
|
}
|
|
3775
3814
|
|
|
3776
3815
|
/**
|
|
@@ -3876,6 +3915,7 @@ export enum Param {
|
|
|
3876
3915
|
Query = 'query',
|
|
3877
3916
|
HideHomepageLeftNav = 'hideHomepageLeftNav',
|
|
3878
3917
|
ModularHomeExperienceEnabled = 'modularHomeExperience',
|
|
3918
|
+
ListPageVersion = 'listpageVersion',
|
|
3879
3919
|
PendoTrackingKey = 'additionalPendoKey',
|
|
3880
3920
|
LiveboardHeaderSticky = 'isLiveboardHeaderSticky',
|
|
3881
3921
|
IsProductTour = 'isProductTour',
|
|
@@ -3914,6 +3954,8 @@ export enum Param {
|
|
|
3914
3954
|
PrimaryAction = 'primaryAction',
|
|
3915
3955
|
isSpotterAgentEmbed = 'isSpotterAgentEmbed',
|
|
3916
3956
|
IsLiveboardStylingAndGroupingEnabled = 'isLiveboardStylingAndGroupingEnabled',
|
|
3957
|
+
IsLazyLoadingForEmbedEnabled = 'isLazyLoadingForEmbedEnabled',
|
|
3958
|
+
RootMarginForLazyLoad = 'rootMarginForLazyLoad'
|
|
3917
3959
|
}
|
|
3918
3960
|
|
|
3919
3961
|
/**
|
|
@@ -31,7 +31,7 @@ function postIframeMessage(
|
|
|
31
31
|
thoughtSpotHost: string,
|
|
32
32
|
channel?: MessageChannel,
|
|
33
33
|
) {
|
|
34
|
-
return iFrame.contentWindow
|
|
34
|
+
return iFrame.contentWindow?.postMessage(message, thoughtSpotHost, [channel?.port2]);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
export const TRIGGER_TIMEOUT = 30000;
|
package/src/utils.spec.ts
CHANGED
|
@@ -15,6 +15,8 @@ import {
|
|
|
15
15
|
getValueFromWindow,
|
|
16
16
|
handlePresentEvent,
|
|
17
17
|
handleExitPresentMode,
|
|
18
|
+
getTypeFromValue,
|
|
19
|
+
calculateVisibleElementData,
|
|
18
20
|
} from './utils';
|
|
19
21
|
import { RuntimeFilterOp } from './types';
|
|
20
22
|
import { logger } from './utils/logger';
|
|
@@ -284,6 +286,27 @@ describe('unit test for utils', () => {
|
|
|
284
286
|
test('isUndefined', () => {
|
|
285
287
|
expect(isUndefined(undefined)).toBe(true);
|
|
286
288
|
expect(isUndefined({})).toBe(false);
|
|
289
|
+
expect(isUndefined(null)).toBe(false);
|
|
290
|
+
expect(isUndefined('')).toBe(false);
|
|
291
|
+
expect(isUndefined(0)).toBe(false);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test('removeTypename should handle edge cases', () => {
|
|
295
|
+
expect(removeTypename(null)).toBe(null);
|
|
296
|
+
expect(removeTypename(undefined)).toBe(undefined);
|
|
297
|
+
expect(removeTypename('string')).toBe('string');
|
|
298
|
+
expect(removeTypename(123)).toBe(123);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
test('getTypeFromValue should return correct types', () => {
|
|
302
|
+
expect(getTypeFromValue('test')).toEqual(['char', 'string']);
|
|
303
|
+
expect(getTypeFromValue(123)).toEqual(['double', 'double']);
|
|
304
|
+
expect(getTypeFromValue(true)).toEqual(['boolean', 'boolean']);
|
|
305
|
+
expect(getTypeFromValue(false)).toEqual(['boolean', 'boolean']);
|
|
306
|
+
expect(getTypeFromValue(null)).toEqual(['', '']);
|
|
307
|
+
expect(getTypeFromValue(undefined)).toEqual(['', '']);
|
|
308
|
+
expect(getTypeFromValue({})).toEqual(['', '']);
|
|
309
|
+
expect(getTypeFromValue([])).toEqual(['', '']);
|
|
287
310
|
});
|
|
288
311
|
|
|
289
312
|
describe('getValueFromWindow and storeValueInWindow', () => {
|
|
@@ -293,7 +316,7 @@ describe('unit test for utils', () => {
|
|
|
293
316
|
});
|
|
294
317
|
|
|
295
318
|
test('Object should be set if not', () => {
|
|
296
|
-
|
|
319
|
+
|
|
297
320
|
(window as any)._tsEmbedSDK = null;
|
|
298
321
|
|
|
299
322
|
storeValueInWindow('test', 'testValue');
|
|
@@ -303,6 +326,13 @@ describe('unit test for utils', () => {
|
|
|
303
326
|
test('Return undefined if key is not found', () => {
|
|
304
327
|
expect(getValueFromWindow('notFound')).toBe(undefined);
|
|
305
328
|
});
|
|
329
|
+
|
|
330
|
+
test('Store with ignoreIfAlreadyExists option', () => {
|
|
331
|
+
storeValueInWindow('test2', 'firstValue');
|
|
332
|
+
const result = storeValueInWindow('test2', 'secondValue', { ignoreIfAlreadyExists: true });
|
|
333
|
+
expect(result).toBe('firstValue');
|
|
334
|
+
expect(getValueFromWindow('test2')).toBe('firstValue');
|
|
335
|
+
});
|
|
306
336
|
});
|
|
307
337
|
});
|
|
308
338
|
|
|
@@ -312,7 +342,7 @@ describe('Fullscreen Utility Functions', () => {
|
|
|
312
342
|
|
|
313
343
|
beforeEach(() => {
|
|
314
344
|
jest.clearAllMocks();
|
|
315
|
-
|
|
345
|
+
|
|
316
346
|
// Store and mock exitFullscreen
|
|
317
347
|
originalExitFullscreen = document.exitFullscreen;
|
|
318
348
|
document.exitFullscreen = jest.fn();
|
|
@@ -407,3 +437,250 @@ describe('Fullscreen Utility Functions', () => {
|
|
|
407
437
|
});
|
|
408
438
|
});
|
|
409
439
|
});
|
|
440
|
+
|
|
441
|
+
describe('calculateVisibleElementData', () => {
|
|
442
|
+
let mockElement: HTMLElement;
|
|
443
|
+
let originalInnerHeight: number;
|
|
444
|
+
let originalInnerWidth: number;
|
|
445
|
+
|
|
446
|
+
beforeEach(() => {
|
|
447
|
+
// Store original window dimensions
|
|
448
|
+
originalInnerHeight = window.innerHeight;
|
|
449
|
+
originalInnerWidth = window.innerWidth;
|
|
450
|
+
|
|
451
|
+
// Mock window dimensions
|
|
452
|
+
Object.defineProperty(window, 'innerHeight', {
|
|
453
|
+
writable: true,
|
|
454
|
+
configurable: true,
|
|
455
|
+
value: 800,
|
|
456
|
+
});
|
|
457
|
+
Object.defineProperty(window, 'innerWidth', {
|
|
458
|
+
writable: true,
|
|
459
|
+
configurable: true,
|
|
460
|
+
value: 1200,
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
// Create mock element
|
|
464
|
+
mockElement = document.createElement('div');
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
afterEach(() => {
|
|
468
|
+
// Restore original window dimensions
|
|
469
|
+
Object.defineProperty(window, 'innerHeight', {
|
|
470
|
+
value: originalInnerHeight,
|
|
471
|
+
});
|
|
472
|
+
Object.defineProperty(window, 'innerWidth', {
|
|
473
|
+
value: originalInnerWidth,
|
|
474
|
+
});
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
it('should calculate data for fully visible element', () => {
|
|
478
|
+
// Mock getBoundingClientRect for element fully within viewport
|
|
479
|
+
jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({
|
|
480
|
+
top: 100,
|
|
481
|
+
left: 150,
|
|
482
|
+
bottom: 300,
|
|
483
|
+
right: 400,
|
|
484
|
+
width: 250,
|
|
485
|
+
height: 200,
|
|
486
|
+
} as DOMRect);
|
|
487
|
+
|
|
488
|
+
const result = calculateVisibleElementData(mockElement);
|
|
489
|
+
|
|
490
|
+
expect(result).toEqual({
|
|
491
|
+
top: 0, // Not clipped from top
|
|
492
|
+
height: 200, // Full height visible
|
|
493
|
+
left: 0, // Not clipped from left
|
|
494
|
+
width: 250, // Full width visible
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
it('should calculate data for element clipped from top', () => {
|
|
499
|
+
// Mock getBoundingClientRect for element partially above viewport
|
|
500
|
+
jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({
|
|
501
|
+
top: -50,
|
|
502
|
+
left: 100,
|
|
503
|
+
bottom: 150,
|
|
504
|
+
right: 400,
|
|
505
|
+
width: 300,
|
|
506
|
+
height: 200,
|
|
507
|
+
} as DOMRect);
|
|
508
|
+
|
|
509
|
+
const result = calculateVisibleElementData(mockElement);
|
|
510
|
+
|
|
511
|
+
expect(result).toEqual({
|
|
512
|
+
top: 50, // Clipped 50px from top
|
|
513
|
+
height: 150, // 150px visible height (0 to 150)
|
|
514
|
+
left: 0, // Not clipped from left
|
|
515
|
+
width: 300, // Full width visible
|
|
516
|
+
});
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
it('should calculate data for element clipped from left', () => {
|
|
520
|
+
// Mock getBoundingClientRect for element partially left of viewport
|
|
521
|
+
jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({
|
|
522
|
+
top: 100,
|
|
523
|
+
left: -80,
|
|
524
|
+
bottom: 300,
|
|
525
|
+
right: 200,
|
|
526
|
+
width: 280,
|
|
527
|
+
height: 200,
|
|
528
|
+
} as DOMRect);
|
|
529
|
+
|
|
530
|
+
const result = calculateVisibleElementData(mockElement);
|
|
531
|
+
|
|
532
|
+
expect(result).toEqual({
|
|
533
|
+
top: 0, // Not clipped from top
|
|
534
|
+
height: 200, // Full height visible
|
|
535
|
+
left: 80, // Clipped 80px from left
|
|
536
|
+
width: 200, // 200px visible width (0 to 200)
|
|
537
|
+
});
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
it('should calculate data for element clipped from bottom', () => {
|
|
541
|
+
// Mock getBoundingClientRect for element extending below viewport
|
|
542
|
+
jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({
|
|
543
|
+
top: 600,
|
|
544
|
+
left: 100,
|
|
545
|
+
bottom: 950, // Extends beyond window height of 800
|
|
546
|
+
right: 400,
|
|
547
|
+
width: 300,
|
|
548
|
+
height: 350,
|
|
549
|
+
} as DOMRect);
|
|
550
|
+
|
|
551
|
+
const result = calculateVisibleElementData(mockElement);
|
|
552
|
+
|
|
553
|
+
expect(result).toEqual({
|
|
554
|
+
top: 0, // Not clipped from top
|
|
555
|
+
height: 200, // Only 200px visible (600 to 800)
|
|
556
|
+
left: 0, // Not clipped from left
|
|
557
|
+
width: 300, // Full width visible
|
|
558
|
+
});
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
it('should calculate data for element clipped from right', () => {
|
|
562
|
+
// Mock getBoundingClientRect for element extending beyond right edge
|
|
563
|
+
jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({
|
|
564
|
+
top: 100,
|
|
565
|
+
left: 1000,
|
|
566
|
+
bottom: 300,
|
|
567
|
+
right: 1400, // Extends beyond window width of 1200
|
|
568
|
+
width: 400,
|
|
569
|
+
height: 200,
|
|
570
|
+
} as DOMRect);
|
|
571
|
+
|
|
572
|
+
const result = calculateVisibleElementData(mockElement);
|
|
573
|
+
|
|
574
|
+
expect(result).toEqual({
|
|
575
|
+
top: 0, // Not clipped from top
|
|
576
|
+
height: 200, // Full height visible
|
|
577
|
+
left: 0, // Not clipped from left
|
|
578
|
+
width: 200, // Only 200px visible width (1000 to 1200)
|
|
579
|
+
});
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
it('should calculate data for element clipped from multiple sides', () => {
|
|
583
|
+
// Mock getBoundingClientRect for element clipped from top and left
|
|
584
|
+
jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({
|
|
585
|
+
top: -100,
|
|
586
|
+
left: -50,
|
|
587
|
+
bottom: 200,
|
|
588
|
+
right: 300,
|
|
589
|
+
width: 350,
|
|
590
|
+
height: 300,
|
|
591
|
+
} as DOMRect);
|
|
592
|
+
|
|
593
|
+
const result = calculateVisibleElementData(mockElement);
|
|
594
|
+
|
|
595
|
+
expect(result).toEqual({
|
|
596
|
+
top: 100, // Clipped 100px from top
|
|
597
|
+
height: 200, // 200px visible height (0 to 200)
|
|
598
|
+
left: 50, // Clipped 50px from left
|
|
599
|
+
width: 300, // 300px visible width (0 to 300)
|
|
600
|
+
});
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
it('should handle element completely outside viewport (above)', () => {
|
|
604
|
+
// Mock getBoundingClientRect for element completely above viewport
|
|
605
|
+
jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({
|
|
606
|
+
top: -300,
|
|
607
|
+
left: 100,
|
|
608
|
+
bottom: -100,
|
|
609
|
+
right: 400,
|
|
610
|
+
width: 300,
|
|
611
|
+
height: 200,
|
|
612
|
+
} as DOMRect);
|
|
613
|
+
|
|
614
|
+
const result = calculateVisibleElementData(mockElement);
|
|
615
|
+
|
|
616
|
+
expect(result).toEqual({
|
|
617
|
+
top: 300, // Clipped 300px from top
|
|
618
|
+
height: 0, // No visible height (clamped from negative)
|
|
619
|
+
left: 0, // Not clipped from left
|
|
620
|
+
width: 300, // Full width would be visible if in viewport
|
|
621
|
+
});
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
it('should handle element completely outside viewport (left)', () => {
|
|
625
|
+
// Mock getBoundingClientRect for element completely left of viewport
|
|
626
|
+
jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({
|
|
627
|
+
top: 100,
|
|
628
|
+
left: -400,
|
|
629
|
+
bottom: 300,
|
|
630
|
+
right: -100,
|
|
631
|
+
width: 300,
|
|
632
|
+
height: 200,
|
|
633
|
+
} as DOMRect);
|
|
634
|
+
|
|
635
|
+
const result = calculateVisibleElementData(mockElement);
|
|
636
|
+
|
|
637
|
+
expect(result).toEqual({
|
|
638
|
+
top: 0, // Not clipped from top
|
|
639
|
+
height: 200, // Full height would be visible if in viewport
|
|
640
|
+
left: 400, // Clipped 400px from left
|
|
641
|
+
width: 0, // No visible width (min(1200, -100) - max(-400, 0) = -100 - 0 = -100, but clamped)
|
|
642
|
+
});
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
it('should handle element larger than viewport', () => {
|
|
646
|
+
// Mock getBoundingClientRect for element larger than viewport
|
|
647
|
+
jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({
|
|
648
|
+
top: -200,
|
|
649
|
+
left: -300,
|
|
650
|
+
bottom: 1000,
|
|
651
|
+
right: 1500,
|
|
652
|
+
width: 1800,
|
|
653
|
+
height: 1200,
|
|
654
|
+
} as DOMRect);
|
|
655
|
+
|
|
656
|
+
const result = calculateVisibleElementData(mockElement);
|
|
657
|
+
|
|
658
|
+
expect(result).toEqual({
|
|
659
|
+
top: 200, // Clipped 200px from top
|
|
660
|
+
height: 800, // Visible height equals window height
|
|
661
|
+
left: 300, // Clipped 300px from left
|
|
662
|
+
width: 1200, // Visible width equals window width
|
|
663
|
+
});
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
it('should handle element exactly at viewport boundaries', () => {
|
|
667
|
+
// Mock getBoundingClientRect for element at exact viewport boundaries
|
|
668
|
+
jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({
|
|
669
|
+
top: 0,
|
|
670
|
+
left: 0,
|
|
671
|
+
bottom: 800,
|
|
672
|
+
right: 1200,
|
|
673
|
+
width: 1200,
|
|
674
|
+
height: 800,
|
|
675
|
+
} as DOMRect);
|
|
676
|
+
|
|
677
|
+
const result = calculateVisibleElementData(mockElement);
|
|
678
|
+
|
|
679
|
+
expect(result).toEqual({
|
|
680
|
+
top: 0, // Not clipped from top
|
|
681
|
+
height: 800, // Full viewport height
|
|
682
|
+
left: 0, // Not clipped from left
|
|
683
|
+
width: 1200, // Full viewport width
|
|
684
|
+
});
|
|
685
|
+
});
|
|
686
|
+
});
|
package/src/utils.ts
CHANGED
|
@@ -269,7 +269,7 @@ export const getOperationNameFromQuery = (query: string) => {
|
|
|
269
269
|
export function removeTypename(obj: any) {
|
|
270
270
|
if (!obj || typeof obj !== 'object') return obj;
|
|
271
271
|
|
|
272
|
-
|
|
272
|
+
|
|
273
273
|
for (const key in obj) {
|
|
274
274
|
if (key === '__typename') {
|
|
275
275
|
delete obj[key];
|
|
@@ -300,7 +300,11 @@ export const setStyleProperties = (
|
|
|
300
300
|
): void => {
|
|
301
301
|
if (!element?.style) return;
|
|
302
302
|
Object.keys(styleProperties).forEach((styleProperty) => {
|
|
303
|
-
|
|
303
|
+
const styleKey = styleProperty as keyof CSSStyleDeclaration;
|
|
304
|
+
const value = styleProperties[styleKey];
|
|
305
|
+
if (value !== undefined) {
|
|
306
|
+
(element.style as any)[styleKey] = value.toString();
|
|
307
|
+
}
|
|
304
308
|
});
|
|
305
309
|
};
|
|
306
310
|
/**
|
|
@@ -463,3 +467,25 @@ export const handleExitPresentMode = async (): Promise<void> => {
|
|
|
463
467
|
|
|
464
468
|
logger.warn('Exit fullscreen API is not supported by this browser.');
|
|
465
469
|
};
|
|
470
|
+
|
|
471
|
+
export const calculateVisibleElementData = (element: HTMLElement) => {
|
|
472
|
+
const rect = element.getBoundingClientRect();
|
|
473
|
+
|
|
474
|
+
const windowHeight = window.innerHeight;
|
|
475
|
+
const windowWidth = window.innerWidth;
|
|
476
|
+
|
|
477
|
+
const frameRelativeTop = Math.max(rect.top, 0);
|
|
478
|
+
const frameRelativeLeft = Math.max(rect.left, 0);
|
|
479
|
+
|
|
480
|
+
const frameRelativeBottom = Math.min(windowHeight, rect.bottom);
|
|
481
|
+
const frameRelativeRight = Math.min(windowWidth, rect.right);
|
|
482
|
+
|
|
483
|
+
const data = {
|
|
484
|
+
top: Math.max(0, rect.top * -1),
|
|
485
|
+
height: Math.max(0, frameRelativeBottom - frameRelativeTop),
|
|
486
|
+
left: Math.max(0, rect.left * -1),
|
|
487
|
+
width: Math.max(0, frameRelativeRight - frameRelativeLeft),
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
return data;
|
|
491
|
+
}
|