diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..5008ddfc Binary files /dev/null and b/.DS_Store differ diff --git a/.sfdx/indexes/lwc/custom-components.json b/.sfdx/indexes/lwc/custom-components.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/.sfdx/indexes/lwc/custom-components.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/src/embed/bodyless-conversation.spec.ts b/src/embed/bodyless-conversation.spec.ts index 811ccb1b..a0577975 100644 --- a/src/embed/bodyless-conversation.spec.ts +++ b/src/embed/bodyless-conversation.spec.ts @@ -1,5 +1,5 @@ import 'jest-fetch-mock'; -import { BodylessConversation, BodylessConversationViewConfig } from './bodyless-conversation'; +import { SpotterAgentEmbed, SpotterAgentEmbedViewConfig } from './bodyless-conversation'; import * as authInstance from '../auth'; import { init } from '../index'; import { Action, AuthType, RuntimeFilterOp } from '../types'; @@ -14,7 +14,7 @@ import { expectUrlToHaveParamsWithValues, } from '../test/test-utils'; -describe('BodylessConversation', () => { +describe('SpotterAgentEmbed', () => { const thoughtSpotHost = 'tshost'; beforeAll(() => { @@ -117,12 +117,12 @@ describe('BodylessConversation', () => { }, }), ); - const viewConfig: BodylessConversationViewConfig = { + const viewConfig: SpotterAgentEmbedViewConfig = { worksheetId: 'worksheetId', }; - const conversationEmbed = new BodylessConversation(viewConfig); - const result = await conversationEmbed.sendMessage('userMessage'); + const spotterEmbed = new SpotterAgentEmbed(viewConfig); + const result = await spotterEmbed.sendMessage('userMessage'); const iframeSrc = getIFrameSrc(result.container); expectUrlToHaveParamsWithValues(iframeSrc, { sessionId: 'sessionId', @@ -134,7 +134,7 @@ describe('BodylessConversation', () => { fetchMock.mockRejectOnce( new Error('error'), ); - const errorResult = await conversationEmbed.sendMessage('userMessage'); + const errorResult = await spotterEmbed.sendMessage('userMessage'); expect(errorResult.error instanceof Error).toBeTruthy(); }); }); diff --git a/src/embed/bodyless-conversation.ts b/src/embed/bodyless-conversation.ts index af87a602..330631a8 100644 --- a/src/embed/bodyless-conversation.ts +++ b/src/embed/bodyless-conversation.ts @@ -8,14 +8,22 @@ import { getQueryParamString } from '../utils'; * Configuration for bodyless conversation options. * @group Embed components */ -export interface BodylessConversationViewConfig extends ViewConfig { +export interface SpotterAgentEmbedViewConfig extends ViewConfig { /** * The ID of the worksheet to use for the conversation. */ worksheetId: string; } -interface ConversationMessageViewConfig extends BodylessConversationViewConfig { +/** + * Configuration for conversation options. + * @deprecated Renamed to SpotterAgentEmbedViewConfig + * @group Embed components + */ +// eslint-disable-next-line @typescript-eslint/no-empty-object-type +export interface BodylessConversationViewConfig extends SpotterAgentEmbedViewConfig {} + +interface SpotterAgentMessageViewConfig extends SpotterAgentEmbedViewConfig { sessionId: string; genNo: number; acSessionId: string; @@ -23,7 +31,7 @@ interface ConversationMessageViewConfig extends BodylessConversationViewConfig { } class ConversationMessage extends TsEmbed { - constructor(container: HTMLElement, protected viewConfig: ConversationMessageViewConfig) { + constructor(container: HTMLElement, protected viewConfig: SpotterAgentMessageViewConfig) { viewConfig.embedComponentType = 'bodyless-conversation'; super(container, viewConfig); } @@ -39,6 +47,7 @@ class ConversationMessage extends TsEmbed { const queryParams = this.getBaseQueryParams(); queryParams[Param.HideActions] = [...(queryParams[Param.HideActions] ?? [])]; + queryParams[Param.isSpotterAgentEmbed] = true; let query = ''; const queryParamsString = getQueryParamString(queryParams, true); if (queryParamsString) { @@ -68,9 +77,9 @@ class ConversationMessage extends TsEmbed { * chatbots or other conversational interfaces. * @example * ```js - * import { BodylessConversation } from '@thoughtspot/visual-embed-sdk'; + * import { SpotterAgentEmbed } from '@thoughtspot/visual-embed-sdk'; * - * const conversation = new BodylessConversation({ + * const conversation = new SpotterAgentEmbed({ * worksheetId: 'worksheetId', * }); * @@ -80,12 +89,12 @@ class ConversationMessage extends TsEmbed { * document.body.appendChild(container); // or to any other element * ``` * @group Embed components - * @version SDK: 1.33.1 | ThoughtSpot: 10.5.0.cl + * @version SDK: 1.37.0 | ThoughtSpot: 10.9.0.cl */ -export class BodylessConversation { +export class SpotterAgentEmbed { private conversationService: ConversationService; - constructor(private viewConfig: BodylessConversationViewConfig) { + constructor(private viewConfig: SpotterAgentEmbedViewConfig) { const embedConfig = getEmbedConfig(); this.conversationService = new ConversationService( @@ -112,3 +121,29 @@ export class BodylessConversation { return { container, viz: embed }; } } + +/** + * Create a conversation embed, which can be integrated inside + * chatbots or other conversational interfaces. + * @deprecated Renamed to SpotterAgentEmbed + * @example + * ```js + * import { SpotterAgentEmbed } from '@thoughtspot/visual-embed-sdk'; + * + * const conversation = new SpotterAgentEmbed({ + * worksheetId: 'worksheetId', + * }); + * + * const { container, error } = await conversation.sendMessage('show me sales by region'); + * + * // append the container to the DOM + * document.body.appendChild(container); // or to any other element + * ``` + * @group Embed components + * @version SDK: 1.37.0 | ThoughtSpot: 10.9.0.cl + */ +export class BodylessConversation extends SpotterAgentEmbed { + constructor(viewConfig: BodylessConversationViewConfig) { + super(viewConfig); + } +} diff --git a/src/embed/conversation.spec.ts b/src/embed/conversation.spec.ts index c615a784..a4b37b5f 100644 --- a/src/embed/conversation.spec.ts +++ b/src/embed/conversation.spec.ts @@ -1,6 +1,6 @@ import { - ConversationEmbed, - ConversationViewConfig, + SpotterEmbed, + SpotterEmbedViewConfig, } from './conversation'; import * as authInstance from '../auth'; import { init } from '../index'; @@ -28,14 +28,14 @@ beforeAll(() => { describe('ConversationEmbed', () => { it('should render the conversation embed', async () => { - const viewConfig: ConversationViewConfig = { + const viewConfig: SpotterEmbedViewConfig = { worksheetId: 'worksheetId', searchOptions: { searchQuery: 'searchQuery', }, }; - const conversationEmbed = new ConversationEmbed(getRootEl(), viewConfig); + const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig); await conversationEmbed.render(); expectUrlMatchesWithParams( getIFrameSrc(), @@ -44,7 +44,7 @@ describe('ConversationEmbed', () => { }); it('should render the conversation embed with worksheets disabled', async () => { - const viewConfig: ConversationViewConfig = { + const viewConfig: SpotterEmbedViewConfig = { worksheetId: 'worksheetId', searchOptions: { searchQuery: 'searchQuery', @@ -52,7 +52,7 @@ describe('ConversationEmbed', () => { disableSourceSelection: true, }; - const conversationEmbed = new ConversationEmbed(getRootEl(), viewConfig); + const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig); await conversationEmbed.render(); expectUrlMatchesWithParams( getIFrameSrc(), @@ -61,7 +61,7 @@ describe('ConversationEmbed', () => { }); it('should render the conversation embed with spotter limitations text if flag is set', async () => { - const viewConfig: ConversationViewConfig = { + const viewConfig: SpotterEmbedViewConfig = { worksheetId: 'worksheetId', searchOptions: { searchQuery: 'searchQuery', @@ -69,7 +69,7 @@ describe('ConversationEmbed', () => { showSpotterLimitations: true, }; - const conversationEmbed = new ConversationEmbed(getRootEl(), viewConfig); + const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig); await conversationEmbed.render(); expectUrlMatchesWithParams( getIFrameSrc(), @@ -78,7 +78,7 @@ describe('ConversationEmbed', () => { }); it('should render the conversation embed with sample questions hidden', async () => { - const viewConfig: ConversationViewConfig = { + const viewConfig: SpotterEmbedViewConfig = { worksheetId: 'worksheetId', searchOptions: { searchQuery: 'searchQuery', @@ -86,7 +86,7 @@ describe('ConversationEmbed', () => { hideSampleQuestions: true, }; - const conversationEmbed = new ConversationEmbed(getRootEl(), viewConfig); + const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig); await conversationEmbed.render(); expectUrlMatchesWithParams( getIFrameSrc(), @@ -95,7 +95,7 @@ describe('ConversationEmbed', () => { }); it('should render the conversation embed with worksheets hidden', async () => { - const viewConfig: ConversationViewConfig = { + const viewConfig: SpotterEmbedViewConfig = { worksheetId: 'worksheetId', searchOptions: { searchQuery: 'searchQuery', @@ -103,7 +103,7 @@ describe('ConversationEmbed', () => { hideSourceSelection: true, }; - const conversationEmbed = new ConversationEmbed(getRootEl(), viewConfig); + const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig); await conversationEmbed.render(); expectUrlMatchesWithParams( getIFrameSrc(), @@ -112,13 +112,13 @@ describe('ConversationEmbed', () => { }); it('should handle error when worksheetId is not provided', async () => { - const viewConfig: ConversationViewConfig = { + const viewConfig: SpotterEmbedViewConfig = { worksheetId: '', searchOptions: { searchQuery: 'searchQuery', }, }; - const conversationEmbed = new ConversationEmbed(getRootEl(), viewConfig); + const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig); (conversationEmbed as any).handleError = jest.fn(); await conversationEmbed.render(); expect((conversationEmbed as any).handleError).toHaveBeenCalledWith( @@ -127,7 +127,7 @@ describe('ConversationEmbed', () => { }); it('should render the conversation embed if data panel v2 flag is true', async () => { - const viewConfig: ConversationViewConfig = { + const viewConfig: SpotterEmbedViewConfig = { worksheetId: 'worksheetId', searchOptions: { searchQuery: 'searchQuery', @@ -135,7 +135,7 @@ describe('ConversationEmbed', () => { dataPanelV2: true, }; - const conversationEmbed = new ConversationEmbed(getRootEl(), viewConfig); + const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig); await conversationEmbed.render(); expectUrlMatchesWithParams( getIFrameSrc(), diff --git a/src/embed/conversation.ts b/src/embed/conversation.ts index e396a95d..cf748b34 100644 --- a/src/embed/conversation.ts +++ b/src/embed/conversation.ts @@ -15,10 +15,10 @@ export interface SearchOptions { } /** - * The configuration for the embedded conversationEmbed options. + * The configuration for the embedded spotterEmbed options. * @group Embed components */ -export interface ConversationViewConfig extends ViewConfig { +export interface SpotterEmbedViewConfig extends ViewConfig { /** * The ID of the worksheet to use for the conversation. */ @@ -32,7 +32,7 @@ export interface ConversationViewConfig extends ViewConfig { * but still display the selected data source. * @example * ```js - * const embed = new ConversationEmbed('#tsEmbed', { + * const embed = new SpotterEmbed('#tsEmbed', { * ... // other options * disableSourceSelection : true, * }) @@ -44,7 +44,7 @@ export interface ConversationViewConfig extends ViewConfig { * hideSourceSelection : Hide data source selection * @example * ```js - * const embed = new ConversationEmbed('#tsEmbed', { + * const embed = new SpotterEmbed('#tsEmbed', { * ... // other options * hideSourceSelection : true, * }) @@ -71,7 +71,7 @@ export interface ConversationViewConfig extends ViewConfig { * default is false. * @example * ```js - * const embed = new ConversationEmbed('#tsEmbed', { + * const embed = new SpotterEmbed('#tsEmbed', { * ... // other options * showSpotterLimitations : true, * }) @@ -84,7 +84,7 @@ export interface ConversationViewConfig extends ViewConfig { * the initial screen of the conversation. * @example * ```js - * const embed = new ConversationEmbed('#tsEmbed', { + * const embed = new SpotterEmbed('#tsEmbed', { * ... // other options * hideSampleQuestions : true, * }) @@ -94,12 +94,20 @@ export interface ConversationViewConfig extends ViewConfig { hideSampleQuestions?: boolean; } +/** + * The configuration for the embedded spotterEmbed options. + * @deprecated Renamed to SpotterEmbedViewConfig + * @group Embed components + */ +// eslint-disable-next-line @typescript-eslint/no-empty-object-type +export interface ConversationViewConfig extends SpotterEmbedViewConfig {} + /** * Embed ThoughtSpot AI Conversation. * @group Embed components * @example * ```js - * const conversation = new ConversationEmbed('#tsEmbed', { + * const conversation = new SpotterEmbed('#tsEmbed', { * worksheetId: 'worksheetId', * searchOptions: { * searchQuery: 'searchQuery', @@ -107,10 +115,10 @@ export interface ConversationViewConfig extends ViewConfig { * }); * conversation.render(); * ``` - * @version SDK: 1.33.1 | ThoughtSpot: 10.5.0.cl + * @version SDK: 1.38.0 | ThoughtSpot: 10.10.0.cl */ -export class ConversationEmbed extends TsEmbed { - constructor(container: HTMLElement, protected viewConfig: ConversationViewConfig) { +export class SpotterEmbed extends TsEmbed { + constructor(container: HTMLElement, protected viewConfig: SpotterEmbedViewConfig) { viewConfig.embedComponentType = 'conversation'; super(container, viewConfig); } @@ -163,7 +171,7 @@ export class ConversationEmbed extends TsEmbed { return `${this.getEmbedBasePath(query)}/embed/${path}${tsPostHashParams}`; } - public async render(): Promise { + public async render(): Promise { await super.render(); const src = this.getIframeSrc(); @@ -171,3 +179,26 @@ export class ConversationEmbed extends TsEmbed { return this; } } + +/** + * Embed ThoughtSpot AI Conversation. + * @deprecated Renamed to SpotterEmbed + * @group Embed components + * @example + * ```js + * const conversation = new SpotterEmbed('#tsEmbed', { + * worksheetId: 'worksheetId', + * searchOptions: { + * searchQuery: 'searchQuery', + * }, + * }); + * conversation.render(); + * ``` + * @version SDK: 1.37.0 | ThoughtSpot: 10.9.0.cl + */ +export class ConversationEmbed extends SpotterEmbed { + constructor(container: HTMLElement, protected viewConfig: ConversationViewConfig) { + viewConfig.embedComponentType = 'conversation'; + super(container, viewConfig); + } +} diff --git a/src/embed/ts-embed.spec.ts b/src/embed/ts-embed.spec.ts index f8b68739..a1dd40bf 100644 --- a/src/embed/ts-embed.spec.ts +++ b/src/embed/ts-embed.spec.ts @@ -14,8 +14,6 @@ import { AppViewConfig, SageEmbed, SageViewConfig, - ConversationViewConfig, - ConversationEmbed, SearchViewConfig, AnswerService, } from '../index'; diff --git a/src/index.ts b/src/index.ts index a63784ff..b2768f4e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,8 +23,8 @@ import { import { PinboardEmbed, LiveboardViewConfig, LiveboardEmbed } from './embed/liveboard'; import { SearchEmbed, SearchViewConfig } from './embed/search'; import { SearchBarEmbed, SearchBarViewConfig } from './embed/search-bar'; -import { BodylessConversation, BodylessConversationViewConfig } from './embed/bodyless-conversation'; -import { ConversationEmbed, ConversationViewConfig } from './embed/conversation'; +import { SpotterAgentEmbed, SpotterAgentEmbedViewConfig, SpotterAgentEmbed as BodylessConversation, SpotterAgentEmbedViewConfig as BodylessConversationViewConfig} from './embed/bodyless-conversation'; +import { SpotterEmbed, SpotterEmbedViewConfig, SpotterEmbed as ConversationEmbed, SpotterEmbedViewConfig as ConversationViewConfig } from './embed/conversation'; import { AuthFailureType, AuthStatus, AuthEvent, AuthEventEmitter, } from './auth'; @@ -85,10 +85,14 @@ export { LiveboardEmbed, SageEmbed, AppEmbed, - BodylessConversation, + SpotterAgentEmbed, + SpotterAgentEmbedViewConfig, BodylessConversationViewConfig, - ConversationEmbed, + BodylessConversation, + SpotterEmbed, + SpotterEmbedViewConfig, ConversationViewConfig, + ConversationEmbed, AuthFailureType, AuthStatus, AuthEvent, diff --git a/src/react/all-types-export.ts b/src/react/all-types-export.ts index c93101f2..c817e0ba 100644 --- a/src/react/all-types-export.ts +++ b/src/react/all-types-export.ts @@ -9,6 +9,7 @@ export { PreRenderedAppEmbed, SageEmbed, PreRenderedSageEmbed, + SpotterEmbed, ConversationEmbed, PreRenderedConversationEmbed, useEmbedRef, diff --git a/src/react/index.tsx b/src/react/index.tsx index 5b476488..409b9507 100644 --- a/src/react/index.tsx +++ b/src/react/index.tsx @@ -13,7 +13,7 @@ import { TsEmbed } from '../embed/ts-embed'; import { EmbedConfig, EmbedEvent, ViewConfig } from '../types'; import { EmbedProps, getViewPropsAndListeners } from './util'; -import { ConversationEmbed as _ConversationEmbed, ConversationViewConfig } from '../embed/conversation'; +import { SpotterEmbed as _SpotterEmbed, SpotterEmbedViewConfig, ConversationEmbed as _ConversationEmbed, ConversationViewConfig as ConversationViewConfig } from '../embed/conversation'; import { init } from '../embed/base'; const componentFactory = ( @@ -334,6 +334,7 @@ export const PreRenderedSageEmbed = componentFactory< SageViewConfig >(_SageEmbed, true); +interface SpotterEmbedProps extends EmbedProps, SpotterEmbedViewConfig { } interface ConversationEmbedProps extends EmbedProps, ConversationViewConfig { } /** @@ -341,6 +342,29 @@ interface ConversationEmbedProps extends EmbedProps, ConversationViewConfig { } * @example * ```tsx * function Sage() { + * return " + * }} + * ... other view config props or event listeners. + * /> + * } + * ``` + */ +export const SpotterEmbed = componentFactory< + typeof _SpotterEmbed, + SpotterEmbedProps, + SpotterEmbedViewConfig +>(_SpotterEmbed); + + +/** + * React component for LLM based conversation BI. + * @deprecated This component is renamed to SpotterEmbed. Use {@link SpotterEmbed} instead. + * @example + * ```tsx + * function Sage() { * return ( - _ConversationEmbed, - ); + typeof _ConversationEmbed, + ConversationEmbedProps, + ConversationViewConfig +>(_ConversationEmbed); /** * React component for PreRendered Conversation embed. * - * PreRenderedConversationEmbed will preRender the ConversationEmbed and will be hidden by + * PreRenderedConversationEmbed will preRender the SpotterEmbed and will be hidden by * default. * * SageEmbed with preRenderId passed will call showPreRender on the embed. @@ -370,21 +395,22 @@ export const ConversationEmbed = componentFactory< * } * ``` * function MyComponent() { - * return + * return * } * ``` */ export const PreRenderedConversationEmbed = componentFactory< - typeof _ConversationEmbed, - SageEmbedProps & PreRenderProps, - ConversationViewConfig ->(_ConversationEmbed, true); + typeof _SpotterEmbed, + SpotterEmbedProps & PreRenderProps, + SpotterEmbedViewConfig +>(_SpotterEmbed, true); type EmbedComponent = typeof SearchEmbed | typeof AppEmbed | typeof LiveboardEmbed | typeof SearchBarEmbed | typeof SageEmbed + | typeof SpotterEmbed | typeof ConversationEmbed; /** diff --git a/src/types.ts b/src/types.ts index e506e8ef..3b947792 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3499,6 +3499,60 @@ export enum HostEvent { * @version SDK: 1.37.0 | ThoughtSpot: 10.8.0.cl */ TransformTableVizData = 'TransformTableVizData', + + /** + * Triggers the table visualization re-render with the updated data. + * Includes the following properties: + * @param - `columnDataLite` - an array of object containing the + * data value modifications retrieved from the `EmbedEvent.TableVizRendered` + * payload.For example, { columnDataLite: []}`. + */ + SpotterSearch = 'SpotterSearch', + + /** + * Triggers the table visualization re-render with the updated data. + * Includes the following properties: + * @param - `columnDataLite` - an array of object containing the + * data value modifications retrieved from the `EmbedEvent.TableVizRendered` + * payload.For example, { columnDataLite: []}`. + */ + EditLastPrompt = 'EditLastPrompt', + + /** + * Triggers the table visualization re-render with the updated data. + * Includes the following properties: + * @param - `columnDataLite` - an array of object containing the + * data value modifications retrieved from the `EmbedEvent.TableVizRendered` + * payload.For example, { columnDataLite: []}`. + */ + PreviewSpotterData = 'PreviewSpotterData', + + /** + * Triggers the table visualization re-render with the updated data. + * Includes the following properties: + * @param - `columnDataLite` - an array of object containing the + * data value modifications retrieved from the `EmbedEvent.TableVizRendered` + * payload.For example, { columnDataLite: []}`. + */ + ResetSpotterConversation = 'ResetSpotterConversation', + + /** + * Triggers the table visualization re-render with the updated data. + * Includes the following properties: + * @param - `columnDataLite` - an array of object containing the + * data value modifications retrieved from the `EmbedEvent.TableVizRendered` + * payload.For example, { columnDataLite: []}`. + */ + DeleteLastPrompt = 'DeleteLastPrompt', + + /** + * Triggers the table visualization re-render with the updated data. + * Includes the following properties: + * @param - `columnDataLite` - an array of object containing the + * data value modifications retrieved from the `EmbedEvent.TableVizRendered` + * payload.For example, { columnDataLite: []}`. + */ + AnswerChartSwitcher = 'AnswerChartSwitcher', } /** @@ -3640,6 +3694,7 @@ export enum Param { ShowSpotterLimitations = 'showSpotterLimitations', CoverAndFilterOptionInPDF = 'coverAndFilterOptionInPDF', PrimaryAction = 'primaryAction', + isSpotterAgentEmbed = 'isSpotterAgentEmbed', } /**