@coveo/quantic 3.42.1 → 3.43.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.
- package/force-app/main/default/labels/CustomLabels.labels-meta.xml +21 -0
- package/force-app/main/default/lwc/quanticGeneratedAnswer/__tests__/quanticGeneratedAnswer.test.js +145 -60
- package/force-app/main/default/lwc/quanticGeneratedAnswer/e2e/agentData.ts +248 -0
- package/force-app/main/default/lwc/quanticGeneratedAnswer/e2e/fixture-agent.ts +77 -0
- package/force-app/main/default/lwc/quanticGeneratedAnswer/e2e/fixture.ts +74 -65
- package/force-app/main/default/lwc/quanticGeneratedAnswer/e2e/pageObject.ts +157 -15
- package/force-app/main/default/lwc/quanticGeneratedAnswer/e2e/quanticGeneratedAnswer.e2e.ts +1 -5
- package/force-app/main/default/lwc/quanticGeneratedAnswer/e2e/quanticGeneratedAnswerAgent.e2e.ts +233 -0
- package/force-app/main/default/lwc/quanticGeneratedAnswer/quanticGeneratedAnswer.js +12 -108
- package/force-app/main/default/lwc/quanticGeneratedAnswer/templates/conversation.html +1 -1
- package/force-app/main/default/lwc/quanticGeneratedAnswer/templates/generatedAnswer.html +8 -47
- package/force-app/main/default/lwc/quanticGeneratedAnswerContent/__tests__/quanticGeneratedAnswerContent.test.js +27 -0
- package/force-app/main/default/lwc/quanticGeneratedAnswerContent/quanticGeneratedAnswerContent.js +8 -1
- package/force-app/main/default/lwc/quanticGeneratedAnswerFollowUpInput/__tests__/quanticGeneratedAnswerFollowUpInput.test.js +242 -10
- package/force-app/main/default/lwc/quanticGeneratedAnswerFollowUpInput/quanticGeneratedAnswerFollowUpInput.css +2 -2
- package/force-app/main/default/lwc/quanticGeneratedAnswerFollowUpInput/quanticGeneratedAnswerFollowUpInput.html +16 -1
- package/force-app/main/default/lwc/quanticGeneratedAnswerFollowUpInput/quanticGeneratedAnswerFollowUpInput.js +47 -4
- package/force-app/main/default/lwc/quanticGeneratedAnswerStreamOfThought/__tests__/quanticGeneratedAnswerStreamOfThought.test.js +95 -41
- package/force-app/main/default/lwc/quanticGeneratedAnswerStreamOfThought/__tests__/stepsUtils.test.js +118 -0
- package/force-app/main/default/lwc/quanticGeneratedAnswerStreamOfThought/quanticGeneratedAnswerStreamOfThought.js +21 -35
- package/force-app/main/default/lwc/quanticGeneratedAnswerStreamOfThought/stepsUtils.js +66 -0
- package/force-app/main/default/staticresources/coveoheadless/case-assist/headless.js +15 -15
- package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/product-listing/facets/headless-product-listing-facet-options.d.ts +4 -39
- package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/search/facets/headless-search-facet-options.d.ts +4 -39
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/commerce/facets/facet-set/facet-set-selector.d.ts +3 -9
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/facets/facet-search-set/category/category-facet-search-state-selector.d.ts +1 -27
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/facets/facet-search-set/specific/specific-facet-search-state-selector.d.ts +1 -27
- package/force-app/main/default/staticresources/coveoheadless/headless.js +17 -17
- package/force-app/main/default/staticresources/coveoheadless/insight/headless.js +16 -16
- package/force-app/main/default/staticresources/coveoheadless/recommendation/headless.js +14 -14
- package/force-app/main/default/staticresources/dompurify/purify.min.js +2 -2
- package/package.json +7 -6
package/force-app/main/default/lwc/quanticGeneratedAnswer/e2e/quanticGeneratedAnswerAgent.e2e.ts
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import {testAgent, expect} from './fixture-agent';
|
|
2
|
+
import agentData from './agentData';
|
|
3
|
+
import {
|
|
4
|
+
analyticsModeTest,
|
|
5
|
+
AnalyticsModeEnum,
|
|
6
|
+
} from '../../../../../../playwright/utils/analyticsMode';
|
|
7
|
+
|
|
8
|
+
const exampleAgentId = '5fd0b5ea-d368-488e-bdeb-f6221ec0fb98';
|
|
9
|
+
|
|
10
|
+
analyticsModeTest.forEach((analytics) => {
|
|
11
|
+
testAgent.describe(`quantic generated answer - agent - ${analytics.label}`, () => {
|
|
12
|
+
testAgent.use({
|
|
13
|
+
analyticsMode: analytics.mode,
|
|
14
|
+
options: {agentId: exampleAgentId},
|
|
15
|
+
permissions: ['clipboard-read', 'clipboard-write'],
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
if (analytics.mode !== AnalyticsModeEnum.legacy) {
|
|
19
|
+
testAgent.fixme(true, 'Agent next analytics mode not yet implemented');
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
testAgent(
|
|
24
|
+
'should handle multiple answer generation, feedback, clipboard, citation, and follow-up interactions',
|
|
25
|
+
async ({generatedAnswer, page}) => {
|
|
26
|
+
await testAgent.step(
|
|
27
|
+
'should send a stream end analytics event when the first answer is generated',
|
|
28
|
+
async () => {
|
|
29
|
+
await generatedAnswer.streamEndAnalyticRequestPromise;
|
|
30
|
+
}
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
await testAgent.step(
|
|
34
|
+
'should send like analytics event when liking the first answer',
|
|
35
|
+
async () => {
|
|
36
|
+
const likeAnalyticRequestPromise =
|
|
37
|
+
generatedAnswer.waitForLikeGeneratedAnswerAnalytics();
|
|
38
|
+
await generatedAnswer.clickLikeButton();
|
|
39
|
+
await likeAnalyticRequestPromise;
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
await testAgent.step(
|
|
44
|
+
'should send dislike analytics event when disliking the first answer',
|
|
45
|
+
async () => {
|
|
46
|
+
const dislikeAnalyticRequestPromise =
|
|
47
|
+
generatedAnswer.waitForDislikeGeneratedAnswerAnalytics();
|
|
48
|
+
await generatedAnswer.clickDislikeButton();
|
|
49
|
+
await dislikeAnalyticRequestPromise;
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
await testAgent.step(
|
|
54
|
+
'should send a copy to clipboard analytics event and update the clipboard with the first answer content',
|
|
55
|
+
async () => {
|
|
56
|
+
const analyticRequestPromise =
|
|
57
|
+
generatedAnswer.waitForCopyToClipboardAnalytics();
|
|
58
|
+
await generatedAnswer.clickCopyToClipboardButton();
|
|
59
|
+
await analyticRequestPromise;
|
|
60
|
+
|
|
61
|
+
const clipboardContent = await page.evaluate(() =>
|
|
62
|
+
navigator.clipboard.readText()
|
|
63
|
+
);
|
|
64
|
+
expect(clipboardContent).toContain('Testing in Coveo');
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
await testAgent.step(
|
|
69
|
+
'should log citation hover analytics when hovering over a citation from the first answer',
|
|
70
|
+
async () => {
|
|
71
|
+
const citationIndex = 0;
|
|
72
|
+
const {id, permanentid} = agentData.citations[citationIndex];
|
|
73
|
+
const citationHoverAnalyticRequestPromise =
|
|
74
|
+
generatedAnswer.waitForSourceHoverAnalytics({
|
|
75
|
+
citationId: id,
|
|
76
|
+
permanentId: permanentid,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
await generatedAnswer.hoverOverCitation(citationIndex);
|
|
80
|
+
await citationHoverAnalyticRequestPromise;
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
await testAgent.step(
|
|
85
|
+
'should log citation click analytics when clicking on a citation from the first answer',
|
|
86
|
+
async () => {
|
|
87
|
+
const citationIndex = 0;
|
|
88
|
+
const {id, title, source, uri, clickUri, permanentid} =
|
|
89
|
+
agentData.citations[citationIndex];
|
|
90
|
+
const citationClickAnalyticRequestPromise =
|
|
91
|
+
generatedAnswer.waitForCitationClickAnalytics({
|
|
92
|
+
documentTitle: title,
|
|
93
|
+
sourceName: source,
|
|
94
|
+
documentPosition: citationIndex + 1,
|
|
95
|
+
documentUri: uri,
|
|
96
|
+
documentUrl: clickUri,
|
|
97
|
+
citationId: id,
|
|
98
|
+
contentIDKey: 'permanentid',
|
|
99
|
+
contentIDValue: permanentid,
|
|
100
|
+
});
|
|
101
|
+
await generatedAnswer.clickOnCitation(citationIndex);
|
|
102
|
+
await citationClickAnalyticRequestPromise;
|
|
103
|
+
}
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
await testAgent.step(
|
|
107
|
+
'FIRST FOLLOW-UP: should send the follow-up request with the correct conversationId and conversationToken',
|
|
108
|
+
async () => {
|
|
109
|
+
const followUpRequestPromise =
|
|
110
|
+
generatedAnswer.waitForAgentFollowUpRequest();
|
|
111
|
+
|
|
112
|
+
await generatedAnswer.typeFollowUpQuestion('follow-up question');
|
|
113
|
+
await generatedAnswer.submitFollowUp();
|
|
114
|
+
|
|
115
|
+
const followUpRequest = await followUpRequestPromise;
|
|
116
|
+
const followUpRequestBody = followUpRequest.postDataJSON();
|
|
117
|
+
expect(followUpRequestBody.conversationId).toBe(
|
|
118
|
+
agentData.conversationId
|
|
119
|
+
);
|
|
120
|
+
expect(followUpRequestBody.conversationToken).toBe(
|
|
121
|
+
agentData.conversationToken
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
await testAgent.step(
|
|
127
|
+
'should send stream end analytics with the right answerId for the follow-up',
|
|
128
|
+
async () => {
|
|
129
|
+
const followUpStreamEndPromise =
|
|
130
|
+
generatedAnswer.waitForFollowUpStreamEndAnalytics(
|
|
131
|
+
agentData.answerId2
|
|
132
|
+
);
|
|
133
|
+
await followUpStreamEndPromise;
|
|
134
|
+
}
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
await testAgent.step(
|
|
138
|
+
'should send like analytics with the follow-up answerId when liking the first follow-up answer',
|
|
139
|
+
async () => {
|
|
140
|
+
const likeAnalyticRequestPromise =
|
|
141
|
+
generatedAnswer.waitForLikeGeneratedAnswerAnalyticsForId(
|
|
142
|
+
agentData.answerId2
|
|
143
|
+
);
|
|
144
|
+
// Should be the first like button, since the previous answers are collapsed and the follow-up answer is the only one visible.
|
|
145
|
+
await generatedAnswer.clickNthLikeButton(0);
|
|
146
|
+
await likeAnalyticRequestPromise;
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
await testAgent.step(
|
|
151
|
+
'SECOND FOLLOW-UP: should send the second follow-up request with the correct conversationId and conversationToken',
|
|
152
|
+
async () => {
|
|
153
|
+
const followUpRequestPromise =
|
|
154
|
+
generatedAnswer.waitForAgentFollowUpRequest();
|
|
155
|
+
|
|
156
|
+
await generatedAnswer.typeFollowUpQuestion(
|
|
157
|
+
'second follow-up question'
|
|
158
|
+
);
|
|
159
|
+
await generatedAnswer.submitFollowUp();
|
|
160
|
+
|
|
161
|
+
const followUpRequest = await followUpRequestPromise;
|
|
162
|
+
const followUpRequestBody = followUpRequest.postDataJSON();
|
|
163
|
+
expect(followUpRequestBody.conversationId).toBe(
|
|
164
|
+
agentData.conversationId
|
|
165
|
+
);
|
|
166
|
+
expect(followUpRequestBody.conversationToken).toBe(
|
|
167
|
+
agentData.conversationToken
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
await testAgent.step(
|
|
173
|
+
'should send stream end analytics with the right answerId for the second follow-up',
|
|
174
|
+
async () => {
|
|
175
|
+
const followUpStreamEndPromise =
|
|
176
|
+
generatedAnswer.waitForFollowUpStreamEndAnalytics(
|
|
177
|
+
agentData.answerId3
|
|
178
|
+
);
|
|
179
|
+
await followUpStreamEndPromise;
|
|
180
|
+
}
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
await testAgent.step(
|
|
184
|
+
'should send dislike analytics with the right answerId for the second follow-up when disliking the second follow-up answer',
|
|
185
|
+
async () => {
|
|
186
|
+
const dislikeAnalyticRequestPromise =
|
|
187
|
+
generatedAnswer.waitForDislikeGeneratedAnswerAnalyticsForId(
|
|
188
|
+
agentData.answerId3
|
|
189
|
+
);
|
|
190
|
+
// Should be the first dislike button, since the previous answers are collapsed and the second follow-up answer is the only one visible.
|
|
191
|
+
await generatedAnswer.clickNthDislikeButton(0);
|
|
192
|
+
await dislikeAnalyticRequestPromise;
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
await testAgent.step(
|
|
197
|
+
'should expand previous answers when clicking on the "Show previous answers" button',
|
|
198
|
+
async () => {
|
|
199
|
+
await generatedAnswer.clickShowPreviousAnswersButton();
|
|
200
|
+
await expect(generatedAnswer.threadItems).toHaveCount(3);
|
|
201
|
+
}
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
await testAgent.step(
|
|
205
|
+
'should expand the first answer when clicking on the toggle button of the first answer',
|
|
206
|
+
async () => {
|
|
207
|
+
await generatedAnswer.clickNthThreadItem(0);
|
|
208
|
+
await expect(generatedAnswer.generatedAnswerBody.nth(0)).toBeVisible();
|
|
209
|
+
await expect(generatedAnswer.generatedAnswerBody.nth(1)).not.toBeVisible();
|
|
210
|
+
await expect(generatedAnswer.generatedAnswerBody.nth(2)).toBeVisible();
|
|
211
|
+
}
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
await testAgent.step(
|
|
215
|
+
'should allow to like the first answer, sending the correct analytics and not affecting the other answers',
|
|
216
|
+
async () => {
|
|
217
|
+
const likeAnalyticRequestPromise =
|
|
218
|
+
generatedAnswer.waitForLikeGeneratedAnswerAnalyticsForId(
|
|
219
|
+
agentData.answerId1
|
|
220
|
+
);
|
|
221
|
+
await generatedAnswer.clickNthLikeButton(0);
|
|
222
|
+
await likeAnalyticRequestPromise;
|
|
223
|
+
await expect(generatedAnswer.likeButton.nth(0)).toHaveAttribute('aria-pressed', 'true');
|
|
224
|
+
await expect(generatedAnswer.dislikeButton.nth(0)).toHaveAttribute('aria-pressed', 'false');
|
|
225
|
+
// Only two answers are visible, so only 2 like/dislike buttons visible.
|
|
226
|
+
await expect(generatedAnswer.dislikeButton.nth(1)).toHaveAttribute('aria-pressed', 'true');
|
|
227
|
+
await expect(generatedAnswer.likeButton.nth(1)).toHaveAttribute('aria-pressed', 'false');
|
|
228
|
+
}
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
);
|
|
232
|
+
});
|
|
233
|
+
});
|
|
@@ -1,23 +1,14 @@
|
|
|
1
1
|
import answerGenerated from '@salesforce/label/c.quantic_AnswerGenerated';
|
|
2
2
|
import couldNotGenerateAnAnswer from '@salesforce/label/c.quantic_CouldNotGenerateAnAnswer';
|
|
3
|
-
import feedback from '@salesforce/label/c.quantic_Feedback';
|
|
4
3
|
import feedbackHelpUsImprove from '@salesforce/label/c.quantic_FeedbackHelpUsImprove';
|
|
5
4
|
import generatedAnswerForYou from '@salesforce/label/c.quantic_GeneratedAnswerForYou';
|
|
6
5
|
import generatedAnswerIsHidden from '@salesforce/label/c.quantic_GeneratedAnswerIsHidden';
|
|
7
6
|
import showLess from '@salesforce/label/c.quantic_GeneratedAnswerShowLess';
|
|
8
7
|
import showMore from '@salesforce/label/c.quantic_GeneratedAnswerShowMore';
|
|
9
8
|
import generatingAnswer from '@salesforce/label/c.quantic_GeneratingAnswer';
|
|
10
|
-
import harmful from '@salesforce/label/c.quantic_Harmful';
|
|
11
|
-
import inaccurate from '@salesforce/label/c.quantic_Inaccurate';
|
|
12
|
-
import irrelevant from '@salesforce/label/c.quantic_Irrelevant';
|
|
13
9
|
import loading from '@salesforce/label/c.quantic_Loading';
|
|
14
|
-
import other from '@salesforce/label/c.quantic_Other';
|
|
15
|
-
import outOfDate from '@salesforce/label/c.quantic_OutOfDate';
|
|
16
10
|
import rgaDisclaimer from '@salesforce/label/c.quantic_RGADisclaimer';
|
|
17
|
-
import thisAnswerWasHelpful from '@salesforce/label/c.quantic_ThisAnswerWasHelpful';
|
|
18
|
-
import thisAnswerWasNotHelpful from '@salesforce/label/c.quantic_ThisAnswerWasNotHelpful';
|
|
19
11
|
import tryAgain from '@salesforce/label/c.quantic_TryAgain';
|
|
20
|
-
import whyGeneratedAnswerWasNotHelpful from '@salesforce/label/c.quantic_WhyGeneratedAnswerWasNotHelpful';
|
|
21
12
|
import noGeneratedAnswer from '@salesforce/label/c.quantic_NoGeneratedAnswer';
|
|
22
13
|
import FeedbackModalQna from 'c/quanticFeedbackModalQna';
|
|
23
14
|
import {
|
|
@@ -25,7 +16,7 @@ import {
|
|
|
25
16
|
initializeWithHeadless,
|
|
26
17
|
getHeadlessBundle,
|
|
27
18
|
} from 'c/quanticHeadlessLoader';
|
|
28
|
-
import {AriaLiveRegion, I18nUtils
|
|
19
|
+
import {AriaLiveRegion, I18nUtils} from 'c/quanticUtils';
|
|
29
20
|
import {LightningElement, api} from 'lwc';
|
|
30
21
|
// @ts-ignore
|
|
31
22
|
import errorTemplate from './templates/errorTemplate.html';
|
|
@@ -42,15 +33,9 @@ import conversationTemplate from './templates/conversation.html';
|
|
|
42
33
|
/** @typedef {import("coveo").GeneratedAnswer} GeneratedAnswer */
|
|
43
34
|
/** @typedef {import("coveo").GeneratedAnswerWithFollowUps} GeneratedAnswerWithFollowUps */
|
|
44
35
|
/** @typedef {import("coveo").GeneratedAnswerState} GeneratedAnswerState */
|
|
45
|
-
/** @typedef {import("coveo").GeneratedAnswerCitation} GeneratedAnswerCitation */
|
|
46
|
-
/** @typedef { 'neutral' | 'liked' | 'disliked'} FeedbackState */
|
|
47
36
|
/** @typedef {import("coveo").SearchStatus} SearchStatus */
|
|
48
37
|
/** @typedef {import("coveo").SearchStatusState} SearchStatusState */
|
|
49
38
|
|
|
50
|
-
const FEEDBACK_LIKED_STATE = 'liked';
|
|
51
|
-
const FEEDBACK_DISLIKED_STATE = 'disliked';
|
|
52
|
-
const FEEDBACK_NEUTRAL_STATE = 'neutral';
|
|
53
|
-
|
|
54
39
|
const DEFAULT_COLLAPSED_HEIGHT = 250;
|
|
55
40
|
const MAX_VALID_COLLAPSED_HEIGHT = 500;
|
|
56
41
|
const MIN_VALID_COLLAPSED_HEIGHT = 150;
|
|
@@ -143,18 +128,9 @@ export default class QuanticGeneratedAnswer extends LightningElement {
|
|
|
143
128
|
|
|
144
129
|
labels = {
|
|
145
130
|
generatedAnswerForYou,
|
|
146
|
-
thisAnswerWasNotHelpful,
|
|
147
|
-
thisAnswerWasHelpful,
|
|
148
131
|
tryAgain,
|
|
149
132
|
couldNotGenerateAnAnswer,
|
|
150
|
-
other,
|
|
151
|
-
harmful,
|
|
152
|
-
irrelevant,
|
|
153
|
-
inaccurate,
|
|
154
|
-
outOfDate,
|
|
155
133
|
feedbackHelpUsImprove,
|
|
156
|
-
feedback,
|
|
157
|
-
whyGeneratedAnswerWasNotHelpful,
|
|
158
134
|
generatingAnswer,
|
|
159
135
|
generatedAnswerIsHidden,
|
|
160
136
|
answerGenerated,
|
|
@@ -169,8 +145,6 @@ export default class QuanticGeneratedAnswer extends LightningElement {
|
|
|
169
145
|
generatedAnswer;
|
|
170
146
|
/** @type {GeneratedAnswerState} */
|
|
171
147
|
state;
|
|
172
|
-
/** @type {FeedbackState} */
|
|
173
|
-
feedbackState = 'neutral';
|
|
174
148
|
/** @type {SearchStatus} */
|
|
175
149
|
searchStatus;
|
|
176
150
|
/** @type {SearchStatusState} */
|
|
@@ -185,6 +159,8 @@ export default class QuanticGeneratedAnswer extends LightningElement {
|
|
|
185
159
|
_exceedsMaximumHeight = false;
|
|
186
160
|
/** @type {number} */
|
|
187
161
|
_maxCollapsedHeight = DEFAULT_COLLAPSED_HEIGHT;
|
|
162
|
+
/** @type {number} */
|
|
163
|
+
generatedAnswerHeight = 0;
|
|
188
164
|
|
|
189
165
|
connectedCallback() {
|
|
190
166
|
registerComponentForInit(this, this.engineId);
|
|
@@ -266,7 +242,6 @@ export default class QuanticGeneratedAnswer extends LightningElement {
|
|
|
266
242
|
|
|
267
243
|
updateState() {
|
|
268
244
|
this.state = this.generatedAnswer.state;
|
|
269
|
-
this.updateFeedbackState();
|
|
270
245
|
this.ariaLiveMessage.dispatchMessage(this.getGeneratedAnswerStatus());
|
|
271
246
|
|
|
272
247
|
if (this.isCollapsibleEnabled) {
|
|
@@ -278,8 +253,8 @@ export default class QuanticGeneratedAnswer extends LightningElement {
|
|
|
278
253
|
// If we are still streaming add a little extra height to the answer element to account for the next answer chunk.
|
|
279
254
|
// This helps a lot with the jankyness of the answer fading out when the chunk is close but not yet over the max height.
|
|
280
255
|
const answerElementHeight = this.isStreaming
|
|
281
|
-
? this.
|
|
282
|
-
: this.
|
|
256
|
+
? this.generatedAnswerHeight + 50
|
|
257
|
+
: this.generatedAnswerHeight;
|
|
283
258
|
|
|
284
259
|
return answerElementHeight > this.maxCollapsedHeight;
|
|
285
260
|
}
|
|
@@ -309,25 +284,6 @@ export default class QuanticGeneratedAnswer extends LightningElement {
|
|
|
309
284
|
this.searchStatusState = this.searchStatus.state;
|
|
310
285
|
}
|
|
311
286
|
|
|
312
|
-
updateFeedbackState() {
|
|
313
|
-
if (this.state?.liked) {
|
|
314
|
-
this.feedbackState = FEEDBACK_LIKED_STATE;
|
|
315
|
-
} else if (this.state?.disliked) {
|
|
316
|
-
this.feedbackState = FEEDBACK_DISLIKED_STATE;
|
|
317
|
-
} else {
|
|
318
|
-
this.feedbackState = FEEDBACK_NEUTRAL_STATE;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
/**
|
|
323
|
-
* handles hovering over a citation.
|
|
324
|
-
* @param {string} id
|
|
325
|
-
* @param {number} citationHoverTimeMs
|
|
326
|
-
*/
|
|
327
|
-
logCitationHover = (id, citationHoverTimeMs) => {
|
|
328
|
-
this.generatedAnswer.logCitationHover(id, citationHoverTimeMs);
|
|
329
|
-
};
|
|
330
|
-
|
|
331
287
|
/**
|
|
332
288
|
* handles liking the generated answer.
|
|
333
289
|
* @param {CustomEvent} event
|
|
@@ -420,6 +376,7 @@ export default class QuanticGeneratedAnswer extends LightningElement {
|
|
|
420
376
|
|
|
421
377
|
handleAnswerContentUpdated = (event) => {
|
|
422
378
|
event.stopPropagation();
|
|
379
|
+
this.generatedAnswerHeight = event.detail?.height;
|
|
423
380
|
if (this.isCollapsibleEnabled) {
|
|
424
381
|
this._exceedsMaximumHeight = this.isMaximumHeightExceeded();
|
|
425
382
|
}
|
|
@@ -453,15 +410,6 @@ export default class QuanticGeneratedAnswer extends LightningElement {
|
|
|
453
410
|
return this.template.querySelector('.generated-answer__answer');
|
|
454
411
|
}
|
|
455
412
|
|
|
456
|
-
/**
|
|
457
|
-
* Returns the generated answer height.
|
|
458
|
-
* @returns {number}
|
|
459
|
-
*/
|
|
460
|
-
get generatedAnswerElementHeight() {
|
|
461
|
-
// @ts-ignore
|
|
462
|
-
return getAbsoluteHeight(this.generatedAnswerElement?.firstChild);
|
|
463
|
-
}
|
|
464
|
-
|
|
465
413
|
/**
|
|
466
414
|
* Sets the value of the CSS variable "--maxHeight" to the value of the maxCollapsedHeight property.
|
|
467
415
|
*/
|
|
@@ -476,31 +424,14 @@ export default class QuanticGeneratedAnswer extends LightningElement {
|
|
|
476
424
|
return this?.state?.answer;
|
|
477
425
|
}
|
|
478
426
|
|
|
479
|
-
get answerId() {
|
|
480
|
-
return this?.state?.answerId;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
427
|
get citations() {
|
|
484
428
|
return this?.state?.citations;
|
|
485
429
|
}
|
|
486
430
|
|
|
487
|
-
get answerContentFormat() {
|
|
488
|
-
return this?.state?.answerContentFormat;
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
get shouldDisplayCitations() {
|
|
492
|
-
const hasCitations = !!this.citations?.length;
|
|
493
|
-
return hasCitations && !this.isAnswerCollapsed;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
431
|
get isStreaming() {
|
|
497
432
|
return this?.state?.isStreaming;
|
|
498
433
|
}
|
|
499
434
|
|
|
500
|
-
get shouldDisplayActions() {
|
|
501
|
-
return this.isVisible && !this.isStreaming && !this.isAnswerCollapsed;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
435
|
get isVisible() {
|
|
505
436
|
return this.state.isVisible;
|
|
506
437
|
}
|
|
@@ -527,15 +458,18 @@ export default class QuanticGeneratedAnswer extends LightningElement {
|
|
|
527
458
|
);
|
|
528
459
|
}
|
|
529
460
|
|
|
530
|
-
get
|
|
531
|
-
|
|
461
|
+
get initialAnswer() {
|
|
462
|
+
return {
|
|
532
463
|
...this.state,
|
|
533
464
|
question: this.engine?.state.query?.q ?? '',
|
|
534
465
|
};
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
get allGeneratedAnswers() {
|
|
535
469
|
const followUpAnswers =
|
|
536
470
|
this.generateAnswerWithFollowUps?.state.followUpAnswers
|
|
537
471
|
?.followUpAnswers ?? [];
|
|
538
|
-
return [initialAnswer, ...followUpAnswers];
|
|
472
|
+
return [this.initialAnswer, ...followUpAnswers];
|
|
539
473
|
}
|
|
540
474
|
|
|
541
475
|
get isCollapsibleEnabled() {
|
|
@@ -566,36 +500,6 @@ export default class QuanticGeneratedAnswer extends LightningElement {
|
|
|
566
500
|
return !this?.searchStatusState?.hasError && this.state?.error?.isRetryable;
|
|
567
501
|
}
|
|
568
502
|
|
|
569
|
-
/**
|
|
570
|
-
* Returns the options displayed in the Quantic Feedback Modal.
|
|
571
|
-
*/
|
|
572
|
-
get options() {
|
|
573
|
-
return [
|
|
574
|
-
{
|
|
575
|
-
label: this.labels.irrelevant,
|
|
576
|
-
value: 'irrelevant',
|
|
577
|
-
},
|
|
578
|
-
{
|
|
579
|
-
label: this.labels.inaccurate,
|
|
580
|
-
value: 'notAccurate',
|
|
581
|
-
},
|
|
582
|
-
{
|
|
583
|
-
label: this.labels.outOfDate,
|
|
584
|
-
value: 'outOfDate',
|
|
585
|
-
},
|
|
586
|
-
{
|
|
587
|
-
label: this.labels.harmful,
|
|
588
|
-
value: 'harmful',
|
|
589
|
-
},
|
|
590
|
-
{
|
|
591
|
-
label: this.labels.other,
|
|
592
|
-
value: 'other',
|
|
593
|
-
withDetails: true,
|
|
594
|
-
detailsRequired: true,
|
|
595
|
-
},
|
|
596
|
-
];
|
|
597
|
-
}
|
|
598
|
-
|
|
599
503
|
get generatedAnswerHeaderClass() {
|
|
600
504
|
const headerBaseClass =
|
|
601
505
|
'generated-answer__card-header slds-grid slds-grid_vertical-align-center slds-grid_align-spread slds-p-horizontal_large slds-p-vertical_small';
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
>
|
|
44
44
|
</c-quantic-generated-answer-thread>
|
|
45
45
|
</div>
|
|
46
|
-
<div class="slds-p-horizontal_large slds-p-vertical_medium">
|
|
46
|
+
<div class="slds-border_top slds-p-horizontal_large slds-p-vertical_medium">
|
|
47
47
|
<c-quantic-generated-answer-follow-up-input
|
|
48
48
|
onquantic__submitfollowup={handleFollowUpSubmit}
|
|
49
49
|
submit-button-disabled={isAnswerGenerationOngoing}
|
|
@@ -33,63 +33,24 @@
|
|
|
33
33
|
data-testid="generated-answer__answer"
|
|
34
34
|
class={generatedAnswerClass}
|
|
35
35
|
>
|
|
36
|
-
<c-quantic-generated-answer-
|
|
36
|
+
<c-quantic-generated-answer-body
|
|
37
37
|
engine-id={engineId}
|
|
38
|
-
answer
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
generated-answer={initialAnswer}
|
|
39
|
+
disable-citation-anchoring={disableCitationAnchoring}
|
|
40
|
+
onquantic__generatedanswerlike={handleLike}
|
|
41
|
+
onquantic__generatedanswerdislike={handleDislike}
|
|
42
|
+
onquantic__citationhover={handleCitationHover}
|
|
42
43
|
onquantic__answercontentupdated={handleAnswerContentUpdated}
|
|
43
|
-
>
|
|
44
|
-
</c-quantic-generated-answer-content>
|
|
44
|
+
></c-quantic-generated-answer-body>
|
|
45
45
|
</div>
|
|
46
46
|
<div class="slds-grid slds-grid_vertical">
|
|
47
|
-
<template lwc:if={shouldDisplayCitations}>
|
|
48
|
-
<div class="slds-size_1-of-1 slds-var-m-top_x-small">
|
|
49
|
-
<c-quantic-source-citations
|
|
50
|
-
engine-id={engineId}
|
|
51
|
-
data-testid="generated-answer__citations"
|
|
52
|
-
citations={citations}
|
|
53
|
-
citation-hover-handler={logCitationHover}
|
|
54
|
-
disable-citation-anchoring={disableCitationAnchoring}
|
|
55
|
-
></c-quantic-source-citations>
|
|
56
|
-
</div>
|
|
57
|
-
</template>
|
|
58
|
-
<template lwc:if={shouldDisplayActions}>
|
|
59
|
-
<div class="slds-size_1-of-1 slds-grid slds-grid_vertical-align-center slds-var-m-top_x-small slds-grid_align-start">
|
|
60
|
-
<div
|
|
61
|
-
data-testid="generated-answer__actions"
|
|
62
|
-
class="slds-grid slds-grid_vertical-align-center"
|
|
63
|
-
>
|
|
64
|
-
<c-quantic-feedback
|
|
65
|
-
state={feedbackState}
|
|
66
|
-
onquantic__like={handleLike}
|
|
67
|
-
onquantic__dislike={handleDislike}
|
|
68
|
-
like-icon-name="utility:like"
|
|
69
|
-
like-label={labels.thisAnswerWasHelpful}
|
|
70
|
-
dislike-icon-name="utility:dislike"
|
|
71
|
-
dislike-label={labels.thisAnswerWasNotHelpful}
|
|
72
|
-
size="x-small"
|
|
73
|
-
question=""
|
|
74
|
-
hide-explain-why-button
|
|
75
|
-
hide-labels
|
|
76
|
-
></c-quantic-feedback>
|
|
77
|
-
<c-quantic-generated-answer-copy-to-clipboard
|
|
78
|
-
data-testid="generated-answer__copy-to-clipboard"
|
|
79
|
-
answer={answer}
|
|
80
|
-
size="x-small"
|
|
81
|
-
class="slds-var-m-horizontal_xx-small"
|
|
82
|
-
></c-quantic-generated-answer-copy-to-clipboard>
|
|
83
|
-
</div>
|
|
84
|
-
</div>
|
|
85
|
-
</template>
|
|
86
47
|
<template lwc:if={isAnswerCollapsed}>
|
|
87
48
|
<div class="slds-grid slds-size_1-of-1 slds-wrap slds-grid_vertical-align-center slds-var-m-top_x-small">
|
|
88
49
|
<div class="slds-grid slds-wrap slds-grid_vertical-align-center">
|
|
89
50
|
<template lwc:if={isStreaming}>
|
|
90
51
|
<div
|
|
91
52
|
data-testid="generated-answer__collapse-generating-message"
|
|
92
|
-
class="generated-answer__collapse-generating-message slds-var-m-right_small"
|
|
53
|
+
class="generated-answer__collapse-generating-message slds-var-m-right_small slds-var-m-bottom_medium"
|
|
93
54
|
>
|
|
94
55
|
{labels.generatingAnswer}...
|
|
95
56
|
</div>
|
|
@@ -5,6 +5,8 @@ import {createElement} from 'lwc';
|
|
|
5
5
|
import {loadMarkdownDependencies} from 'c/quanticUtils';
|
|
6
6
|
import * as mockHeadlessLoader from 'c/quanticHeadlessLoader';
|
|
7
7
|
|
|
8
|
+
const mockAnswerHeight = 250;
|
|
9
|
+
|
|
8
10
|
jest.mock('c/quanticHeadlessLoader');
|
|
9
11
|
jest.mock('c/quanticUtils', () => ({
|
|
10
12
|
loadMarkdownDependencies: jest.fn(
|
|
@@ -17,6 +19,7 @@ jest.mock('c/quanticUtils', () => ({
|
|
|
17
19
|
LinkUtils: {
|
|
18
20
|
bindAnalyticsToLink: jest.fn(() => jest.fn()),
|
|
19
21
|
},
|
|
22
|
+
getAbsoluteHeight: jest.fn(() => mockAnswerHeight),
|
|
20
23
|
}));
|
|
21
24
|
|
|
22
25
|
const mockMarkedUse = jest.fn();
|
|
@@ -417,4 +420,28 @@ describe('c-quantic-generated-answer-content', () => {
|
|
|
417
420
|
});
|
|
418
421
|
});
|
|
419
422
|
});
|
|
423
|
+
|
|
424
|
+
describe('the quantic__answercontentupdated event', () => {
|
|
425
|
+
beforeEach(() => {
|
|
426
|
+
mockSuccessfulHeadlessInitialization();
|
|
427
|
+
prepareHeadlessState();
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it('should dispatch the event with the measured height when the markdown content is updated', async () => {
|
|
431
|
+
const element = createTestComponent({
|
|
432
|
+
...defaultOptions,
|
|
433
|
+
answerContentFormat: 'text/markdown',
|
|
434
|
+
answer: 'Hello, world!',
|
|
435
|
+
});
|
|
436
|
+
const handler = jest.fn();
|
|
437
|
+
element.addEventListener('quantic__answercontentupdated', handler);
|
|
438
|
+
await flushPromises();
|
|
439
|
+
|
|
440
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
441
|
+
const event = handler.mock.calls[0][0];
|
|
442
|
+
expect(event.detail).toEqual({height: mockAnswerHeight});
|
|
443
|
+
expect(event.bubbles).toBe(true);
|
|
444
|
+
expect(event.composed).toBe(true);
|
|
445
|
+
});
|
|
446
|
+
});
|
|
420
447
|
});
|
package/force-app/main/default/lwc/quanticGeneratedAnswerContent/quanticGeneratedAnswerContent.js
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
transformMarkdownToHtml,
|
|
8
8
|
loadMarkdownDependencies,
|
|
9
9
|
LinkUtils,
|
|
10
|
+
getAbsoluteHeight,
|
|
10
11
|
} from 'c/quanticUtils';
|
|
11
12
|
import {LightningElement, api} from 'lwc';
|
|
12
13
|
// @ts-ignore
|
|
@@ -171,7 +172,13 @@ export default class QuanticGeneratedAnswerContent extends LightningElement {
|
|
|
171
172
|
else {
|
|
172
173
|
answerContainer.textContent = this.answer;
|
|
173
174
|
}
|
|
174
|
-
this.dispatchEvent(
|
|
175
|
+
this.dispatchEvent(
|
|
176
|
+
new CustomEvent('quantic__answercontentupdated', {
|
|
177
|
+
bubbles: true,
|
|
178
|
+
composed: true,
|
|
179
|
+
detail: {height: getAbsoluteHeight(answerContainer)},
|
|
180
|
+
})
|
|
181
|
+
);
|
|
175
182
|
}
|
|
176
183
|
|
|
177
184
|
/**
|