@thoughtspot/visual-embed-sdk 1.37.1 → 1.38.0-alpha.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 (114) hide show
  1. package/README.md +1 -1
  2. package/cjs/package.json +1 -1
  3. package/cjs/src/embed/app.js +1 -1
  4. package/cjs/src/embed/app.js.map +1 -1
  5. package/cjs/src/embed/bodyless-conversation.d.ts +44 -9
  6. package/cjs/src/embed/bodyless-conversation.d.ts.map +1 -1
  7. package/cjs/src/embed/bodyless-conversation.js +35 -5
  8. package/cjs/src/embed/bodyless-conversation.js.map +1 -1
  9. package/cjs/src/embed/bodyless-conversation.spec.js +290 -5
  10. package/cjs/src/embed/bodyless-conversation.spec.js.map +1 -1
  11. package/cjs/src/embed/conversation.d.ts +40 -11
  12. package/cjs/src/embed/conversation.d.ts.map +1 -1
  13. package/cjs/src/embed/conversation.js +29 -4
  14. package/cjs/src/embed/conversation.js.map +1 -1
  15. package/cjs/src/embed/conversation.spec.js +7 -7
  16. package/cjs/src/embed/conversation.spec.js.map +1 -1
  17. package/cjs/src/embed/liveboard.d.ts.map +1 -1
  18. package/cjs/src/embed/liveboard.js +21 -10
  19. package/cjs/src/embed/liveboard.js.map +1 -1
  20. package/cjs/src/embed/ts-embed.spec.d.ts.map +1 -1
  21. package/cjs/src/embed/ts-embed.spec.js.map +1 -1
  22. package/cjs/src/index.d.ts +4 -4
  23. package/cjs/src/index.d.ts.map +1 -1
  24. package/cjs/src/index.js +5 -1
  25. package/cjs/src/index.js.map +1 -1
  26. package/cjs/src/react/all-types-export.d.ts +1 -1
  27. package/cjs/src/react/all-types-export.d.ts.map +1 -1
  28. package/cjs/src/react/all-types-export.js +3 -1
  29. package/cjs/src/react/all-types-export.js.map +1 -1
  30. package/cjs/src/react/index.d.ts +53 -5
  31. package/cjs/src/react/index.d.ts.map +1 -1
  32. package/cjs/src/react/index.js +73 -4
  33. package/cjs/src/react/index.js.map +1 -1
  34. package/cjs/src/react/index.spec.js +94 -0
  35. package/cjs/src/react/index.spec.js.map +1 -1
  36. package/cjs/src/types.d.ts +2 -1
  37. package/cjs/src/types.d.ts.map +1 -1
  38. package/cjs/src/types.js +1 -0
  39. package/cjs/src/types.js.map +1 -1
  40. package/dist/{index-m9UtENc9.js → index-DeFzsyFF.js} +1 -1
  41. package/dist/index-NZYq1Tu3.js +7370 -0
  42. package/dist/src/embed/bodyless-conversation.d.ts +44 -9
  43. package/dist/src/embed/bodyless-conversation.d.ts.map +1 -1
  44. package/dist/src/embed/conversation.d.ts +40 -11
  45. package/dist/src/embed/conversation.d.ts.map +1 -1
  46. package/dist/src/embed/liveboard.d.ts.map +1 -1
  47. package/dist/src/embed/ts-embed.spec.d.ts.map +1 -1
  48. package/dist/src/index.d.ts +4 -4
  49. package/dist/src/index.d.ts.map +1 -1
  50. package/dist/src/react/all-types-export.d.ts +1 -1
  51. package/dist/src/react/all-types-export.d.ts.map +1 -1
  52. package/dist/src/react/index.d.ts +53 -5
  53. package/dist/src/react/index.d.ts.map +1 -1
  54. package/dist/src/types.d.ts +2 -1
  55. package/dist/src/types.d.ts.map +1 -1
  56. package/dist/tsembed-react.es.js +583 -21
  57. package/dist/tsembed-react.js +583 -19
  58. package/dist/tsembed.es.js +86 -22
  59. package/dist/tsembed.js +92 -26
  60. package/dist/visual-embed-sdk-react-full.d.ts +184 -72
  61. package/dist/visual-embed-sdk-react.d.ts +184 -72
  62. package/dist/visual-embed-sdk.d.ts +87 -22
  63. package/lib/package.json +1 -1
  64. package/lib/src/embed/app.js +1 -1
  65. package/lib/src/embed/app.js.map +1 -1
  66. package/lib/src/embed/bodyless-conversation.d.ts +44 -9
  67. package/lib/src/embed/bodyless-conversation.d.ts.map +1 -1
  68. package/lib/src/embed/bodyless-conversation.js +33 -4
  69. package/lib/src/embed/bodyless-conversation.js.map +1 -1
  70. package/lib/src/embed/bodyless-conversation.spec.js +292 -7
  71. package/lib/src/embed/bodyless-conversation.spec.js.map +1 -1
  72. package/lib/src/embed/conversation.d.ts +40 -11
  73. package/lib/src/embed/conversation.d.ts.map +1 -1
  74. package/lib/src/embed/conversation.js +27 -3
  75. package/lib/src/embed/conversation.js.map +1 -1
  76. package/lib/src/embed/conversation.spec.js +8 -8
  77. package/lib/src/embed/conversation.spec.js.map +1 -1
  78. package/lib/src/embed/liveboard.d.ts.map +1 -1
  79. package/lib/src/embed/liveboard.js +21 -10
  80. package/lib/src/embed/liveboard.js.map +1 -1
  81. package/lib/src/embed/ts-embed.spec.d.ts.map +1 -1
  82. package/lib/src/embed/ts-embed.spec.js.map +1 -1
  83. package/lib/src/index.d.ts +4 -4
  84. package/lib/src/index.d.ts.map +1 -1
  85. package/lib/src/index.js +4 -4
  86. package/lib/src/index.js.map +1 -1
  87. package/lib/src/react/all-types-export.d.ts +1 -1
  88. package/lib/src/react/all-types-export.d.ts.map +1 -1
  89. package/lib/src/react/all-types-export.js +1 -1
  90. package/lib/src/react/all-types-export.js.map +1 -1
  91. package/lib/src/react/index.d.ts +53 -5
  92. package/lib/src/react/index.d.ts.map +1 -1
  93. package/lib/src/react/index.js +73 -4
  94. package/lib/src/react/index.js.map +1 -1
  95. package/lib/src/react/index.spec.js +96 -2
  96. package/lib/src/react/index.spec.js.map +1 -1
  97. package/lib/src/types.d.ts +2 -1
  98. package/lib/src/types.d.ts.map +1 -1
  99. package/lib/src/types.js +1 -0
  100. package/lib/src/types.js.map +1 -1
  101. package/lib/src/visual-embed-sdk.d.ts +90 -25
  102. package/package.json +1 -1
  103. package/src/embed/app.ts +1 -1
  104. package/src/embed/bodyless-conversation.spec.ts +314 -7
  105. package/src/embed/bodyless-conversation.ts +53 -8
  106. package/src/embed/conversation.spec.ts +16 -16
  107. package/src/embed/conversation.ts +44 -11
  108. package/src/embed/liveboard.ts +25 -10
  109. package/src/embed/ts-embed.spec.ts +0 -2
  110. package/src/index.ts +16 -5
  111. package/src/react/all-types-export.ts +2 -0
  112. package/src/react/index.spec.tsx +157 -1
  113. package/src/react/index.tsx +98 -11
  114. package/src/types.ts +1 -0
@@ -8,14 +8,28 @@ import { getQueryParamString } from '../utils';
8
8
  * Configuration for bodyless conversation options.
9
9
  * @group Embed components
10
10
  */
11
- export interface BodylessConversationViewConfig extends ViewConfig {
11
+ export interface SpotterAgentEmbedViewConfig extends ViewConfig {
12
12
  /**
13
13
  * The ID of the worksheet to use for the conversation.
14
14
  */
15
15
  worksheetId: string;
16
+
17
+ /**
18
+ * Optional CSS class name to add to the container div.
19
+ */
20
+ containerClassName?: string;
16
21
  }
17
22
 
18
- interface ConversationMessageViewConfig extends BodylessConversationViewConfig {
23
+ /**
24
+ * Configuration for conversation options.
25
+ * @deprecated from SDK: 1.38.0 | ThoughtSpot: 10.10.0.cl
26
+ * Use {@link SpotterAgentEmbedViewConfig} instead
27
+ * @group Embed components
28
+ */
29
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
30
+ export interface BodylessConversationViewConfig extends SpotterAgentEmbedViewConfig {}
31
+
32
+ interface SpotterAgentMessageViewConfig extends SpotterAgentEmbedViewConfig {
19
33
  sessionId: string;
20
34
  genNo: number;
21
35
  acSessionId: string;
@@ -23,7 +37,7 @@ interface ConversationMessageViewConfig extends BodylessConversationViewConfig {
23
37
  }
24
38
 
25
39
  class ConversationMessage extends TsEmbed {
26
- constructor(container: HTMLElement, protected viewConfig: ConversationMessageViewConfig) {
40
+ constructor(container: HTMLElement, protected viewConfig: SpotterAgentMessageViewConfig) {
27
41
  viewConfig.embedComponentType = 'bodyless-conversation';
28
42
  super(container, viewConfig);
29
43
  }
@@ -39,6 +53,7 @@ class ConversationMessage extends TsEmbed {
39
53
  const queryParams = this.getBaseQueryParams();
40
54
 
41
55
  queryParams[Param.HideActions] = [...(queryParams[Param.HideActions] ?? [])];
56
+ queryParams[Param.isSpotterAgentEmbed] = true;
42
57
  let query = '';
43
58
  const queryParamsString = getQueryParamString(queryParams, true);
44
59
  if (queryParamsString) {
@@ -68,9 +83,9 @@ class ConversationMessage extends TsEmbed {
68
83
  * chatbots or other conversational interfaces.
69
84
  * @example
70
85
  * ```js
71
- * import { BodylessConversation } from '@thoughtspot/visual-embed-sdk';
86
+ * import { SpotterAgentEmbed } from '@thoughtspot/visual-embed-sdk';
72
87
  *
73
- * const conversation = new BodylessConversation({
88
+ * const conversation = new SpotterAgentEmbed({
74
89
  * worksheetId: 'worksheetId',
75
90
  * });
76
91
  *
@@ -80,12 +95,12 @@ class ConversationMessage extends TsEmbed {
80
95
  * document.body.appendChild(container); // or to any other element
81
96
  * ```
82
97
  * @group Embed components
83
- * @version SDK: 1.33.1 | ThoughtSpot: 10.5.0.cl
98
+ * @version SDK: 1.37.0 | ThoughtSpot: 10.9.0.cl
84
99
  */
85
- export class BodylessConversation {
100
+ export class SpotterAgentEmbed {
86
101
  private conversationService: ConversationService;
87
102
 
88
- constructor(private viewConfig: BodylessConversationViewConfig) {
103
+ constructor(private viewConfig: SpotterAgentEmbedViewConfig) {
89
104
  const embedConfig = getEmbedConfig();
90
105
 
91
106
  this.conversationService = new ConversationService(
@@ -101,6 +116,10 @@ export class BodylessConversation {
101
116
  }
102
117
 
103
118
  const container = document.createElement('div');
119
+ if (this.viewConfig.containerClassName) {
120
+ container.className = this.viewConfig.containerClassName;
121
+ }
122
+
104
123
  const embed = new ConversationMessage(container, {
105
124
  ...this.viewConfig,
106
125
  sessionId: data.sessionId,
@@ -112,3 +131,29 @@ export class BodylessConversation {
112
131
  return { container, viz: embed };
113
132
  }
114
133
  }
134
+
135
+ /**
136
+ * Create a conversation embed, which can be integrated inside
137
+ * chatbots or other conversational interfaces.
138
+ * @deprecated from SDK: 1.38.0 | ThoughtSpot: 10.10.0.cl
139
+ * Use {@link SpotterAgentEmbed} instead
140
+ * @example
141
+ * ```js
142
+ * import { SpotterAgentEmbed } from '@thoughtspot/visual-embed-sdk';
143
+ *
144
+ * const conversation = new SpotterAgentEmbed({
145
+ * worksheetId: 'worksheetId',
146
+ * });
147
+ *
148
+ * const { container, error } = await conversation.sendMessage('show me sales by region');
149
+ *
150
+ * // append the container to the DOM
151
+ * document.body.appendChild(container); // or to any other element
152
+ * ```
153
+ * @group Embed components
154
+ */
155
+ export class BodylessConversation extends SpotterAgentEmbed {
156
+ constructor(viewConfig: BodylessConversationViewConfig) {
157
+ super(viewConfig);
158
+ }
159
+ }
@@ -1,6 +1,6 @@
1
1
  import {
2
- ConversationEmbed,
3
- ConversationViewConfig,
2
+ SpotterEmbed,
3
+ SpotterEmbedViewConfig,
4
4
  } from './conversation';
5
5
  import * as authInstance from '../auth';
6
6
  import { init } from '../index';
@@ -28,14 +28,14 @@ beforeAll(() => {
28
28
 
29
29
  describe('ConversationEmbed', () => {
30
30
  it('should render the conversation embed', async () => {
31
- const viewConfig: ConversationViewConfig = {
31
+ const viewConfig: SpotterEmbedViewConfig = {
32
32
  worksheetId: 'worksheetId',
33
33
  searchOptions: {
34
34
  searchQuery: 'searchQuery',
35
35
  },
36
36
  };
37
37
 
38
- const conversationEmbed = new ConversationEmbed(getRootEl(), viewConfig);
38
+ const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig);
39
39
  await conversationEmbed.render();
40
40
  expectUrlMatchesWithParams(
41
41
  getIFrameSrc(),
@@ -44,7 +44,7 @@ describe('ConversationEmbed', () => {
44
44
  });
45
45
 
46
46
  it('should render the conversation embed with worksheets disabled', async () => {
47
- const viewConfig: ConversationViewConfig = {
47
+ const viewConfig: SpotterEmbedViewConfig = {
48
48
  worksheetId: 'worksheetId',
49
49
  searchOptions: {
50
50
  searchQuery: 'searchQuery',
@@ -52,7 +52,7 @@ describe('ConversationEmbed', () => {
52
52
  disableSourceSelection: true,
53
53
  };
54
54
 
55
- const conversationEmbed = new ConversationEmbed(getRootEl(), viewConfig);
55
+ const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig);
56
56
  await conversationEmbed.render();
57
57
  expectUrlMatchesWithParams(
58
58
  getIFrameSrc(),
@@ -61,7 +61,7 @@ describe('ConversationEmbed', () => {
61
61
  });
62
62
 
63
63
  it('should render the conversation embed with spotter limitations text if flag is set', async () => {
64
- const viewConfig: ConversationViewConfig = {
64
+ const viewConfig: SpotterEmbedViewConfig = {
65
65
  worksheetId: 'worksheetId',
66
66
  searchOptions: {
67
67
  searchQuery: 'searchQuery',
@@ -69,7 +69,7 @@ describe('ConversationEmbed', () => {
69
69
  showSpotterLimitations: true,
70
70
  };
71
71
 
72
- const conversationEmbed = new ConversationEmbed(getRootEl(), viewConfig);
72
+ const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig);
73
73
  await conversationEmbed.render();
74
74
  expectUrlMatchesWithParams(
75
75
  getIFrameSrc(),
@@ -78,7 +78,7 @@ describe('ConversationEmbed', () => {
78
78
  });
79
79
 
80
80
  it('should render the conversation embed with sample questions hidden', async () => {
81
- const viewConfig: ConversationViewConfig = {
81
+ const viewConfig: SpotterEmbedViewConfig = {
82
82
  worksheetId: 'worksheetId',
83
83
  searchOptions: {
84
84
  searchQuery: 'searchQuery',
@@ -86,7 +86,7 @@ describe('ConversationEmbed', () => {
86
86
  hideSampleQuestions: true,
87
87
  };
88
88
 
89
- const conversationEmbed = new ConversationEmbed(getRootEl(), viewConfig);
89
+ const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig);
90
90
  await conversationEmbed.render();
91
91
  expectUrlMatchesWithParams(
92
92
  getIFrameSrc(),
@@ -95,7 +95,7 @@ describe('ConversationEmbed', () => {
95
95
  });
96
96
 
97
97
  it('should render the conversation embed with worksheets hidden', async () => {
98
- const viewConfig: ConversationViewConfig = {
98
+ const viewConfig: SpotterEmbedViewConfig = {
99
99
  worksheetId: 'worksheetId',
100
100
  searchOptions: {
101
101
  searchQuery: 'searchQuery',
@@ -103,7 +103,7 @@ describe('ConversationEmbed', () => {
103
103
  hideSourceSelection: true,
104
104
  };
105
105
 
106
- const conversationEmbed = new ConversationEmbed(getRootEl(), viewConfig);
106
+ const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig);
107
107
  await conversationEmbed.render();
108
108
  expectUrlMatchesWithParams(
109
109
  getIFrameSrc(),
@@ -112,13 +112,13 @@ describe('ConversationEmbed', () => {
112
112
  });
113
113
 
114
114
  it('should handle error when worksheetId is not provided', async () => {
115
- const viewConfig: ConversationViewConfig = {
115
+ const viewConfig: SpotterEmbedViewConfig = {
116
116
  worksheetId: '',
117
117
  searchOptions: {
118
118
  searchQuery: 'searchQuery',
119
119
  },
120
120
  };
121
- const conversationEmbed = new ConversationEmbed(getRootEl(), viewConfig);
121
+ const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig);
122
122
  (conversationEmbed as any).handleError = jest.fn();
123
123
  await conversationEmbed.render();
124
124
  expect((conversationEmbed as any).handleError).toHaveBeenCalledWith(
@@ -127,7 +127,7 @@ describe('ConversationEmbed', () => {
127
127
  });
128
128
 
129
129
  it('should render the conversation embed if data panel v2 flag is true', async () => {
130
- const viewConfig: ConversationViewConfig = {
130
+ const viewConfig: SpotterEmbedViewConfig = {
131
131
  worksheetId: 'worksheetId',
132
132
  searchOptions: {
133
133
  searchQuery: 'searchQuery',
@@ -135,7 +135,7 @@ describe('ConversationEmbed', () => {
135
135
  dataPanelV2: true,
136
136
  };
137
137
 
138
- const conversationEmbed = new ConversationEmbed(getRootEl(), viewConfig);
138
+ const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig);
139
139
  await conversationEmbed.render();
140
140
  expectUrlMatchesWithParams(
141
141
  getIFrameSrc(),
@@ -15,10 +15,10 @@ export interface SearchOptions {
15
15
  }
16
16
 
17
17
  /**
18
- * The configuration for the embedded conversationEmbed options.
18
+ * The configuration for the embedded spotterEmbed options.
19
19
  * @group Embed components
20
20
  */
21
- export interface ConversationViewConfig extends ViewConfig {
21
+ export interface SpotterEmbedViewConfig extends ViewConfig {
22
22
  /**
23
23
  * The ID of the worksheet to use for the conversation.
24
24
  */
@@ -32,7 +32,7 @@ export interface ConversationViewConfig extends ViewConfig {
32
32
  * but still display the selected data source.
33
33
  * @example
34
34
  * ```js
35
- * const embed = new ConversationEmbed('#tsEmbed', {
35
+ * const embed = new SpotterEmbed('#tsEmbed', {
36
36
  * ... // other options
37
37
  * disableSourceSelection : true,
38
38
  * })
@@ -44,7 +44,7 @@ export interface ConversationViewConfig extends ViewConfig {
44
44
  * hideSourceSelection : Hide data source selection
45
45
  * @example
46
46
  * ```js
47
- * const embed = new ConversationEmbed('#tsEmbed', {
47
+ * const embed = new SpotterEmbed('#tsEmbed', {
48
48
  * ... // other options
49
49
  * hideSourceSelection : true,
50
50
  * })
@@ -71,7 +71,7 @@ export interface ConversationViewConfig extends ViewConfig {
71
71
  * default is false.
72
72
  * @example
73
73
  * ```js
74
- * const embed = new ConversationEmbed('#tsEmbed', {
74
+ * const embed = new SpotterEmbed('#tsEmbed', {
75
75
  * ... // other options
76
76
  * showSpotterLimitations : true,
77
77
  * })
@@ -84,7 +84,7 @@ export interface ConversationViewConfig extends ViewConfig {
84
84
  * the initial screen of the conversation.
85
85
  * @example
86
86
  * ```js
87
- * const embed = new ConversationEmbed('#tsEmbed', {
87
+ * const embed = new SpotterEmbed('#tsEmbed', {
88
88
  * ... // other options
89
89
  * hideSampleQuestions : true,
90
90
  * })
@@ -94,12 +94,21 @@ export interface ConversationViewConfig extends ViewConfig {
94
94
  hideSampleQuestions?: boolean;
95
95
  }
96
96
 
97
+ /**
98
+ * The configuration for the embedded spotterEmbed options.
99
+ * @deprecated from SDK: 1.38.0 | ThoughtSpot: 10.10.0.cl
100
+ * Use {@link SpotterEmbedViewConfig} instead
101
+ * @group Embed components
102
+ */
103
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
104
+ export interface ConversationViewConfig extends SpotterEmbedViewConfig {}
105
+
97
106
  /**
98
107
  * Embed ThoughtSpot AI Conversation.
99
108
  * @group Embed components
100
109
  * @example
101
110
  * ```js
102
- * const conversation = new ConversationEmbed('#tsEmbed', {
111
+ * const conversation = new SpotterEmbed('#tsEmbed', {
103
112
  * worksheetId: 'worksheetId',
104
113
  * searchOptions: {
105
114
  * searchQuery: 'searchQuery',
@@ -107,10 +116,10 @@ export interface ConversationViewConfig extends ViewConfig {
107
116
  * });
108
117
  * conversation.render();
109
118
  * ```
110
- * @version SDK: 1.33.1 | ThoughtSpot: 10.5.0.cl
119
+ * @version SDK: 1.37.0 | ThoughtSpot: 10.9.0.cl
111
120
  */
112
- export class ConversationEmbed extends TsEmbed {
113
- constructor(container: HTMLElement, protected viewConfig: ConversationViewConfig) {
121
+ export class SpotterEmbed extends TsEmbed {
122
+ constructor(container: HTMLElement, protected viewConfig: SpotterEmbedViewConfig) {
114
123
  viewConfig.embedComponentType = 'conversation';
115
124
  super(container, viewConfig);
116
125
  }
@@ -163,7 +172,7 @@ export class ConversationEmbed extends TsEmbed {
163
172
  return `${this.getEmbedBasePath(query)}/embed/${path}${tsPostHashParams}`;
164
173
  }
165
174
 
166
- public async render(): Promise<ConversationEmbed> {
175
+ public async render(): Promise<SpotterEmbed> {
167
176
  await super.render();
168
177
 
169
178
  const src = this.getIframeSrc();
@@ -171,3 +180,27 @@ export class ConversationEmbed extends TsEmbed {
171
180
  return this;
172
181
  }
173
182
  }
183
+
184
+ /**
185
+ * Embed ThoughtSpot AI Conversation.
186
+ * @deprecated from SDK: 1.38.0 | ThoughtSpot: 10.10.0.cl
187
+ * Use {@link SpotterEmbed} instead
188
+ * @group Embed components
189
+ * @example
190
+ * ```js
191
+ * const conversation = new SpotterEmbed('#tsEmbed', {
192
+ * worksheetId: 'worksheetId',
193
+ * searchOptions: {
194
+ * searchQuery: 'searchQuery',
195
+ * },
196
+ * });
197
+ * conversation.render();
198
+ * ```
199
+ * @version SDK: 1.37.0 | ThoughtSpot: 10.9.0.cl
200
+ */
201
+ export class ConversationEmbed extends SpotterEmbed {
202
+ constructor(container: HTMLElement, protected viewConfig: ConversationViewConfig) {
203
+ viewConfig.embedComponentType = 'conversation';
204
+ super(container, viewConfig);
205
+ }
206
+ }
@@ -24,13 +24,8 @@ import { getAuthPromise } from './base';
24
24
  import { TsEmbed, V1Embed } from './ts-embed';
25
25
  import { addPreviewStylesIfNotPresent } from '../utils/global-styles';
26
26
  import { TriggerPayload, TriggerResponse } from './hostEventClient/contracts';
27
+ import { logger } from '../utils/logger';
27
28
 
28
- const liveboardHeightWhitelistedRoutes = [
29
- '/embed/viz/',
30
- '/embed/insights/viz/',
31
- '/tsl-editor/PINBOARD_ANSWER_BOOK/',
32
- '/import-tsl/PINBOARD_ANSWER_BOOK/',
33
- ];
34
29
 
35
30
  /**
36
31
  * The configuration for the embedded Liveboard or visualization page view.
@@ -429,11 +424,16 @@ export class LiveboardEmbed extends V1Embed {
429
424
 
430
425
  private defaultHeight = 500;
431
426
 
432
- // eslint-disable-next-line no-useless-constructor
427
+
433
428
  constructor(domSelector: DOMSelector, viewConfig: LiveboardViewConfig) {
434
429
  viewConfig.embedComponentType = 'LiveboardEmbed';
435
430
  super(domSelector, viewConfig);
436
431
  if (this.viewConfig.fullHeight === true) {
432
+ if (this.viewConfig.vizId) {
433
+ logger.warn('Full height is currently only supported for Liveboard embeds.' +
434
+ 'Using full height with vizId might lead to unexpected behavior.');
435
+ }
436
+
437
437
  this.on(EmbedEvent.RouteChange, this.setIframeHeightForNonEmbedLiveboard);
438
438
  this.on(EmbedEvent.EmbedHeight, this.updateIFrameHeight);
439
439
  this.on(EmbedEvent.EmbedIframeCenter, this.embedIframeCenter);
@@ -592,11 +592,26 @@ export class LiveboardEmbed extends V1Embed {
592
592
  };
593
593
 
594
594
  private setIframeHeightForNonEmbedLiveboard = (data: MessagePayload) => {
595
- const routePath = data.data.currentPath;
596
- if (liveboardHeightWhitelistedRoutes.some((path) => routePath.startsWith(path))) {
595
+ const { height: frameHeight } = this.viewConfig.frameParams || {};
596
+
597
+ const liveboardRelatedRoutes = [
598
+ '/pinboard/',
599
+ '/insights/pinboard/',
600
+ '/schedules/',
601
+ '/embed/viz/',
602
+ '/embed/insights/viz/',
603
+ '/liveboard/',
604
+ '/insights/liveboard/',
605
+ '/tsl-editor/PINBOARD_ANSWER_BOOK/',
606
+ '/import-tsl/PINBOARD_ANSWER_BOOK/',
607
+ ];
608
+
609
+ if (liveboardRelatedRoutes.some((path) => data.data.currentPath.startsWith(path))) {
610
+ // Ignore the height reset of the frame, if the navigation is
611
+ // only within the liveboard page.
597
612
  return;
598
613
  }
599
- this.setIFrameHeight(this.defaultHeight);
614
+ this.setIFrameHeight(frameHeight || this.defaultHeight);
600
615
  };
601
616
 
602
617
  private setActiveTab(data: { tabId: string }) {
@@ -14,8 +14,6 @@ import {
14
14
  AppViewConfig,
15
15
  SageEmbed,
16
16
  SageViewConfig,
17
- ConversationViewConfig,
18
- ConversationEmbed,
19
17
  SearchViewConfig,
20
18
  AnswerService,
21
19
  } from '../index';
package/src/index.ts CHANGED
@@ -9,7 +9,12 @@
9
9
  * @author Ayon Ghosh <ayon.ghosh@thoughtspot.com>
10
10
  */
11
11
  import {
12
- AppEmbed, Page, AppViewConfig, HomePageSearchBarMode,
12
+ AppEmbed,
13
+ Page,
14
+ AppViewConfig,
15
+ HomePageSearchBarMode,
16
+ PrimaryNavbarVersion,
17
+ HomePage,
13
18
  } from './embed/app';
14
19
  import {
15
20
  init,
@@ -23,8 +28,8 @@ import {
23
28
  import { PinboardEmbed, LiveboardViewConfig, LiveboardEmbed } from './embed/liveboard';
24
29
  import { SearchEmbed, SearchViewConfig } from './embed/search';
25
30
  import { SearchBarEmbed, SearchBarViewConfig } from './embed/search-bar';
26
- import { BodylessConversation, BodylessConversationViewConfig } from './embed/bodyless-conversation';
27
- import { ConversationEmbed, ConversationViewConfig } from './embed/conversation';
31
+ import { SpotterAgentEmbed, SpotterAgentEmbedViewConfig, BodylessConversation, BodylessConversationViewConfig} from './embed/bodyless-conversation';
32
+ import { SpotterEmbed, SpotterEmbedViewConfig, ConversationEmbed, ConversationViewConfig } from './embed/conversation';
28
33
  import {
29
34
  AuthFailureType, AuthStatus, AuthEvent, AuthEventEmitter,
30
35
  } from './auth';
@@ -85,10 +90,14 @@ export {
85
90
  LiveboardEmbed,
86
91
  SageEmbed,
87
92
  AppEmbed,
88
- BodylessConversation,
93
+ SpotterAgentEmbed,
94
+ SpotterAgentEmbedViewConfig,
89
95
  BodylessConversationViewConfig,
90
- ConversationEmbed,
96
+ BodylessConversation,
97
+ SpotterEmbed,
98
+ SpotterEmbedViewConfig,
91
99
  ConversationViewConfig,
100
+ ConversationEmbed,
92
101
  AuthFailureType,
93
102
  AuthStatus,
94
103
  AuthEvent,
@@ -129,6 +138,8 @@ export {
129
138
  uploadMixpanelEvent,
130
139
  MIXPANEL_EVENT,
131
140
  HomePageSearchBarMode,
141
+ PrimaryNavbarVersion,
142
+ HomePage,
132
143
  VizPoint,
133
144
  CustomActionPayload,
134
145
  UIPassthroughEvent,
@@ -9,8 +9,10 @@ export {
9
9
  PreRenderedAppEmbed,
10
10
  SageEmbed,
11
11
  PreRenderedSageEmbed,
12
+ SpotterEmbed,
12
13
  ConversationEmbed,
13
14
  PreRenderedConversationEmbed,
15
+ SpotterAgentEmbed,
14
16
  useEmbedRef,
15
17
  useInit,
16
18
  } from './index';
@@ -16,6 +16,7 @@ import {
16
16
  } from '../test/test-utils';
17
17
  import {
18
18
  SearchEmbed, AppEmbed, LiveboardEmbed, useEmbedRef, SearchBarEmbed, PreRenderedLiveboardEmbed,
19
+ SpotterAgentEmbed
19
20
  } from './index';
20
21
  import * as allExports from './index';
21
22
  import {
@@ -235,6 +236,161 @@ describe('React Components', () => {
235
236
  });
236
237
  });
237
238
 
239
+ describe('SpotterAgentEmbed', () => {
240
+ it('Should work as a React component integrating BodylessConversation', async () => {
241
+ const mockDiv = document.createElement('div');
242
+
243
+ let conversationService: any = null;
244
+
245
+ render(
246
+ <SpotterAgentEmbed
247
+ ref={(instance: any) => {
248
+ conversationService = instance;
249
+
250
+ if (instance) {
251
+ instance.sendMessage = jest.fn().mockResolvedValue({
252
+ container: mockDiv
253
+ });
254
+ }
255
+ }}
256
+ worksheetId="test-worksheet-id"
257
+ />
258
+ );
259
+
260
+ expect(conversationService).not.toBeNull();
261
+
262
+ if (conversationService) {
263
+ expect(typeof conversationService.sendMessage).toBe('function');
264
+
265
+ const response = await conversationService.sendMessage("What are my sales this month?");
266
+ expect(response.container).toBe(mockDiv);
267
+ }
268
+ });
269
+
270
+ it('Should work as a React component with ref support', () => {
271
+ const mockSendMessage = jest.fn().mockResolvedValue({
272
+ container: document.createElement('div'),
273
+ viz: {}
274
+ });
275
+
276
+ const TestComponent = () => {
277
+ const conversationRef = React.useRef(null);
278
+
279
+ const handleClick = () => {
280
+ if (conversationRef.current) {
281
+ conversationRef.current.sendMessage = mockSendMessage;
282
+
283
+ conversationRef.current.sendMessage("Test message");
284
+ }
285
+ };
286
+
287
+ return (
288
+ <>
289
+ <SpotterAgentEmbed
290
+ ref={conversationRef}
291
+ worksheetId="test-worksheet-id"
292
+ />
293
+ <button data-testid="test-button" onClick={handleClick}>
294
+ Send Message
295
+ </button>
296
+ </>
297
+ );
298
+ };
299
+
300
+ const { getByTestId } = render(<TestComponent />);
301
+
302
+ fireEvent.click(getByTestId('test-button'));
303
+
304
+ expect(mockSendMessage).toHaveBeenCalledWith("Test message");
305
+ });
306
+
307
+ it('Should work with the useEmbedRef hook', () => {
308
+ const mockSendMessage = jest.fn().mockResolvedValue({
309
+ container: document.createElement('div'),
310
+ viz: {}
311
+ });
312
+
313
+ const TestComponent = () => {
314
+ const embedRef = useEmbedRef<typeof SpotterAgentEmbed>();
315
+
316
+ const handleClick = () => {
317
+ if (embedRef.current) {
318
+ const service = embedRef.current as unknown as {
319
+ sendMessage: typeof mockSendMessage
320
+ };
321
+
322
+ service.sendMessage = mockSendMessage;
323
+
324
+ service.sendMessage("Test with useEmbedRef");
325
+ }
326
+ };
327
+
328
+ return (
329
+ <>
330
+ <SpotterAgentEmbed
331
+ ref={embedRef}
332
+ worksheetId="test-worksheet-id"
333
+ />
334
+ <button data-testid="use-embed-ref-button" onClick={handleClick}>
335
+ Send with useEmbedRef
336
+ </button>
337
+ </>
338
+ );
339
+ };
340
+
341
+ const { getByTestId } = render(<TestComponent />);
342
+
343
+ fireEvent.click(getByTestId('use-embed-ref-button'));
344
+
345
+ expect(mockSendMessage).toHaveBeenCalledWith("Test with useEmbedRef");
346
+ });
347
+
348
+ it('Should work with the className prop', async () => {
349
+ let capturedInstance: any = null;
350
+
351
+ const TestComponent = () => {
352
+ const embedRef = useEmbedRef<typeof SpotterAgentEmbed>();
353
+
354
+ React.useEffect(() => {
355
+ capturedInstance = embedRef.current;
356
+
357
+ if (capturedInstance) {
358
+ const mockConversationService = {
359
+ sendMessage: jest.fn().mockResolvedValue({
360
+ data: {
361
+ sessionId: 'test-session',
362
+ genNo: 1,
363
+ stateKey: {
364
+ transactionId: 'test-transaction',
365
+ generationNumber: 1
366
+ }
367
+ }
368
+ })
369
+ };
370
+ (capturedInstance as any).conversationService = mockConversationService;
371
+ }
372
+ }, []);
373
+
374
+ return (
375
+ <SpotterAgentEmbed
376
+ ref={embedRef}
377
+ worksheetId="test-worksheet-id"
378
+ className="embedClass"
379
+ />
380
+ );
381
+ };
382
+
383
+ render(<TestComponent />);
384
+
385
+ expect(capturedInstance).not.toBeNull();
386
+
387
+ if (capturedInstance) {
388
+ const result = await capturedInstance.sendMessage("test");
389
+ expect(result.container.className).toBe("embedClass");
390
+ }
391
+ });
392
+ });
393
+
238
394
  describe('PreRenderedLiveboardEmbed', () => {
239
395
  it('should preRender the liveboard ', async () => {
240
396
  const preRenderId = 'tsEmbed-pre-render-wrapper-test';
@@ -285,7 +441,7 @@ describe('allExports', () => {
285
441
  it('should not have undefined exports', () => {
286
442
  Object.keys(allExports).forEach(
287
443
  (exportKey) => expect(
288
- Boolean(allExports[exportKey]),
444
+ Boolean(allExports[exportKey as keyof typeof allExports]),
289
445
  )
290
446
  .toBe(true),
291
447
  );