Skip to content

Commit fc018a7

Browse files
Shibani Basavashibbas
authored andcommitted
feat: update follow up questions for DI
1 parent c383062 commit fc018a7

File tree

7 files changed

+93
-48
lines changed

7 files changed

+93
-48
lines changed

packages/chat-component/src/components/chat-component.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ export class ChatComponent extends LitElement {
120120
@lazyMultiInject(ControllerType.ChatSection)
121121
chatSectionControllers: ChatSectionController[] | undefined;
122122

123+
public constructor() {
124+
super();
125+
this.setQuestionInputValue = this.setQuestionInputValue.bind(this);
126+
}
127+
123128
// Lifecycle method that runs when the component is first connected to the DOM
124129
override connectedCallback(): void {
125130
super.connectedCallback();
@@ -328,7 +333,7 @@ export class ChatComponent extends LitElement {
328333
.svgIcon="${iconLogo}"
329334
.context="${this.chatContext}"
330335
@on-citation-click="${this.handleCitationClick}"
331-
@on-followup-click="${this.handleInput}"
336+
@on-input="${this.handleInput}"
332337
>
333338
</chat-thread-component>`;
334339
}
@@ -338,7 +343,7 @@ export class ChatComponent extends LitElement {
338343
? ''
339344
: this.chatInputComponents
340345
.filter((component) => component.position === position)
341-
.map((component) => component.render(this.handleInput));
346+
.map((component) => component.render(this.setQuestionInputValue));
342347
}
343348

344349
// Render the chat component as a web component

packages/chat-component/src/components/chat-thread-component.ts

Lines changed: 25 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import { customElement, property, query, state } from 'lit/decorators.js';
44
import { styles } from '../styles/chat-thread-component.js';
55

66
import { globalConfig } from '../config/global-config.js';
7-
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
87
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
98

10-
import iconQuestion from '../../public/svg/bubblequestion-icon.svg?raw';
11-
129
import { type ChatActionButton } from './chat-action-button.js';
13-
import { type ChatEntryActionController, ControllerType, lazyMultiInject } from './composable.js';
10+
import {
11+
type ChatEntryActionController,
12+
type ChatEntryInlineInputController,
13+
ControllerType,
14+
lazyMultiInject,
15+
} from './composable.js';
1416
import { type ChatContextController } from './chat-context.js';
1517

1618
@customElement('chat-thread-component')
@@ -41,13 +43,27 @@ export class ChatThreadComponent extends LitElement {
4143
@lazyMultiInject(ControllerType.ChatEntryAction)
4244
actionCompontents: ChatEntryActionController[] | undefined;
4345

46+
@lazyMultiInject(ControllerType.ChatEntryInlineInput)
47+
inlineInputComponents: ChatEntryInlineInputController[] | undefined;
48+
49+
public constructor() {
50+
super();
51+
this.handleInput = this.handleInput.bind(this);
52+
}
53+
4454
connectedCallback() {
4555
super.connectedCallback();
4656
if (this.actionCompontents) {
4757
for (const component of this.actionCompontents) {
4858
component.attach(this, this.context);
4959
}
5060
}
61+
62+
if (this.inlineInputComponents) {
63+
for (const component of this.inlineInputComponents) {
64+
component.attach(this, this.context);
65+
}
66+
}
5167
}
5268

5369
actionButtonClicked(actionButton: ChatActionButton, entry: ChatThreadEntry, event: Event) {
@@ -75,12 +91,10 @@ export class ChatThreadComponent extends LitElement {
7591
}, 500);
7692
}
7793

78-
handleFollowupQuestionClick(question: string, entry: ChatThreadEntry, event: Event) {
79-
event.preventDefault();
80-
const followUpClickEvent = new CustomEvent('on-followup-click', {
94+
handleInput(input: string) {
95+
const followUpClickEvent = new CustomEvent('on-input', {
8196
detail: {
82-
value: question,
83-
chatThreadEntry: entry,
97+
value: input,
8498
},
8599
bubbles: true,
86100
composed: true,
@@ -150,36 +164,6 @@ export class ChatThreadComponent extends LitElement {
150164
return '';
151165
}
152166

153-
renderFollowupQuestions(entry: ChatThreadEntry) {
154-
const followupQuestions = entry.followupQuestions;
155-
// render followup questions
156-
// need to fix first after decoupling of teaserlist
157-
if (followupQuestions && followupQuestions.length > 0) {
158-
return html`
159-
<div class="items__listWrapper">
160-
${unsafeSVG(iconQuestion)}
161-
<ul class="items__list followup">
162-
${followupQuestions.map(
163-
(followupQuestion) => html`
164-
<li class="items__listItem--followup">
165-
<a
166-
class="items__link"
167-
href="#"
168-
data-testid="followUpQuestion"
169-
@click="${(event) => this.handleFollowupQuestionClick(followupQuestion, entry, event)}"
170-
>${followupQuestion}</a
171-
>
172-
</li>
173-
`,
174-
)}
175-
</ul>
176-
</div>
177-
`;
178-
}
179-
180-
return '';
181-
}
182-
183167
renderError(error: { message: string }) {
184168
return html`<p class="chat__txt error">${error.message}</p>`;
185169
}
@@ -193,7 +177,8 @@ export class ChatThreadComponent extends LitElement {
193177
<div class="chat__txt ${message.isUserMessage ? 'user-message' : ''}">
194178
${message.isUserMessage ? '' : this.renderResponseActions(message)}
195179
${message.text.map((textEntry) => this.renderTextEntry(textEntry))} ${this.renderCitation(message)}
196-
${this.renderFollowupQuestions(message)} ${message.error ? this.renderError(message.error) : ''}
180+
${this.inlineInputComponents?.map((component) => component.render(message, this.handleInput))}
181+
${message.error ? this.renderError(message.error) : ''}
197182
</div>
198183
<p class="chat__txt--info">
199184
<span class="timestamp">${message.timestamp}</span>,

packages/chat-component/src/components/composable.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export const ControllerType = {
1212
ChatSection: Symbol.for('ChatSectionController'),
1313
ChatEntryAction: Symbol.for('ChatEntryActionController'),
1414
Citation: Symbol.for('CitationController'),
15+
ChatEntryInlineInput: Symbol.for('ChatEntryInlineInputController'),
1516
};
1617

1718
export interface ComposableReactiveController extends ReactiveController {
@@ -24,7 +25,7 @@ export abstract class ComposableReactiveControllerBase implements ComposableReac
2425
protected context: ChatContextController;
2526

2627
attach(host: ReactiveControllerHost, context: ChatContextController) {
27-
this.host = host;
28+
(this.host = host).addController(this);
2829
this.context = context;
2930
}
3031

@@ -34,7 +35,7 @@ export abstract class ComposableReactiveControllerBase implements ComposableReac
3435

3536
export interface ChatInputController extends ComposableReactiveController {
3637
position: 'left' | 'right' | 'top';
37-
render: (handleInput: (event: CustomEvent<InputValue>) => void) => TemplateResult;
38+
render: (handleInput: (input: string) => void) => TemplateResult;
3839
}
3940

4041
export interface ChatInputFooterController extends ComposableReactiveController {
@@ -51,6 +52,10 @@ export interface ChatEntryActionController extends ComposableReactiveController
5152
render: (entry: ChatThreadEntry, isDisabled: boolean) => TemplateResult;
5253
}
5354

55+
export interface ChatEntryInlineInputController extends ComposableReactiveController {
56+
render: (entry: ChatThreadEntry, handleInput: (event: CustomEvent<InputValue>) => void) => TemplateResult;
57+
}
58+
5459
export interface CitationController extends ComposableReactiveController {
5560
render: (citation: Citation, url: string) => TemplateResult;
5661
}
@@ -80,3 +85,4 @@ container.bind<ChatInputFooterController>(ControllerType.ChatInputFooter).to(Def
8085
container.bind<ChatSectionController>(ControllerType.ChatSection).to(DefaultChatSectionController);
8186
container.bind<ChatEntryActionController>(ControllerType.ChatEntryAction).to(DefaultController);
8287
container.bind<CitationController>(ControllerType.Citation).to(DefaultController);
88+
container.bind<ChatEntryInlineInputController>(ControllerType.ChatEntryInlineInput).to(DefaultController);

packages/chat-component/src/components/default-questions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ import { html } from 'lit';
1313
export class DefaultQuestionsInputController extends ComposableReactiveControllerBase implements ChatInputController {
1414
position = 'top';
1515

16-
render(handleInput: (event: CustomEvent<InputValue>) => void) {
16+
render(handleInput: (input: string) => void) {
1717
const promptTemplate = html`
1818
<teaser-list-component
1919
.heading="${this.context.interactionModel === 'chat'
2020
? teaserListTexts.HEADING_CHAT
2121
: teaserListTexts.HEADING_ASK}"
2222
.clickable="${true}"
2323
.actionLabel="${teaserListTexts.TEASER_CTA_LABEL}"
24-
@teaser-click="${handleInput}"
24+
@teaser-click="${(event) => handleInput(event?.detail?.value)}"
2525
.teasers="${teaserListTexts.DEFAULT_PROMPTS}"
2626
></teaser-list-component>
2727
`;

packages/chat-component/src/components/features.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ import './teaser-list-component.js';
1212
import './document-previewer.js';
1313
import './citation-previewer.js';
1414

15+
import './follow-up-questions.js';
1516
// [COMPOSE COMPONENTS END]
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { injectable } from 'inversify';
2+
import {
3+
container,
4+
type ChatEntryInlineInputController,
5+
ControllerType,
6+
ComposableReactiveControllerBase,
7+
} from './composable.js';
8+
import { html } from 'lit';
9+
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
10+
import iconQuestion from '../../public/svg/bubblequestion-icon.svg?raw';
11+
12+
@injectable()
13+
export class FollowupQuestionsController
14+
extends ComposableReactiveControllerBase
15+
implements ChatEntryInlineInputController
16+
{
17+
render(entry: ChatThreadEntry, handleInput: (input: string) => void) {
18+
const followupQuestions = entry.followupQuestions;
19+
// render followup questions
20+
// need to fix first after decoupling of teaserlist
21+
if (followupQuestions && followupQuestions.length > 0) {
22+
return html`
23+
<div class="items__listWrapper">
24+
${unsafeSVG(iconQuestion)}
25+
<ul class="items__list followup">
26+
${followupQuestions.map(
27+
(followupQuestion) => html`
28+
<li class="items__listItem--followup">
29+
<a
30+
class="items__link"
31+
href="#"
32+
data-testid="followUpQuestion"
33+
@click="${() => handleInput(followupQuestion)}"
34+
>${followupQuestion}</a
35+
>
36+
</li>
37+
`,
38+
)}
39+
</ul>
40+
</div>
41+
`;
42+
}
43+
44+
return '';
45+
}
46+
}
47+
48+
container.bind<ChatEntryInlineInputController>(ControllerType.ChatEntryInlineInput).to(FollowupQuestionsController);

packages/chat-component/src/components/voice-input.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import { html } from 'lit';
66
export class VoiceInputController extends ComposableReactiveControllerBase implements ChatInputController {
77
position = 'right';
88

9-
render(handleInput: (event: CustomEvent<InputValue>) => void) {
10-
return html`<voice-input-button @on-voice-input="${handleInput}" />`;
9+
render(handleInput: (input: string) => void) {
10+
return html`<voice-input-button @on-voice-input="${(event) => handleInput(event?.detail?.value)}" />`;
1111
}
1212
}
1313

0 commit comments

Comments
 (0)