@coveo/quantic 3.43.0 → 3.43.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (17) hide show
  1. package/force-app/main/default/lwc/quanticGeneratedAnswer/__tests__/quanticGeneratedAnswer.test.js +145 -60
  2. package/force-app/main/default/lwc/quanticGeneratedAnswer/e2e/fixture-agent.ts +3 -3
  3. package/force-app/main/default/lwc/quanticGeneratedAnswer/e2e/fixture.ts +12 -9
  4. package/force-app/main/default/lwc/quanticGeneratedAnswer/e2e/pageObject.ts +5 -8
  5. package/force-app/main/default/lwc/quanticGeneratedAnswer/quanticGeneratedAnswer.js +12 -108
  6. package/force-app/main/default/lwc/quanticGeneratedAnswer/templates/generatedAnswer.html +8 -47
  7. package/force-app/main/default/lwc/quanticGeneratedAnswerContent/__tests__/quanticGeneratedAnswerContent.test.js +27 -0
  8. package/force-app/main/default/lwc/quanticGeneratedAnswerContent/quanticGeneratedAnswerContent.js +8 -1
  9. package/force-app/main/default/lwc/quanticGeneratedAnswerThread/quanticGeneratedAnswerThread.css +16 -4
  10. package/force-app/main/default/lwc/quanticGeneratedAnswerThread/quanticGeneratedAnswerThread.html +12 -16
  11. package/force-app/main/default/lwc/quanticThreadItem/quanticThreadItem.css +29 -11
  12. package/force-app/main/default/lwc/quanticThreadItem/quanticThreadItem.html +11 -15
  13. package/force-app/main/default/staticresources/coveoheadless/case-assist/headless.js +2 -2
  14. package/force-app/main/default/staticresources/coveoheadless/headless.js +2 -2
  15. package/force-app/main/default/staticresources/coveoheadless/insight/headless.js +2 -2
  16. package/force-app/main/default/staticresources/coveoheadless/recommendation/headless.js +2 -2
  17. package/package.json +3 -2
@@ -89,9 +89,8 @@ const selectors = {
89
89
  generatedAnswer: '[data-testid="generated-answer__answer"]',
90
90
  generatedAnswerBadge: '[data-testid="generated-answer__header-title"]',
91
91
  generatedAnswerRetryButton: '[data-testid="generated-answer__retry-button"]',
92
- generatedAnswerActions: '[data-testid="generated-answer__actions"]',
93
92
  generatedAnswerToggleButton: 'c-quantic-generated-answer-toggle',
94
- generatedAnswerContent: 'c-quantic-generated-answer-content',
93
+ generatedAnswerBodyComponent: 'c-quantic-generated-answer-body',
95
94
  generatingMessageWhenAnswerCollapsed:
96
95
  '[data-testid="generated-answer__collapse-generating-message"]',
97
96
  generatedAnswerCollapseToggle:
@@ -101,10 +100,8 @@ const selectors = {
101
100
  '[data-testid="generated-answer__no-answer-card"]',
102
101
  generatedAnswerNoAnswerMessage:
103
102
  '[data-testid="generated-answer__no-answer-message"]',
104
- generatedAnswerCitations: 'c-quantic-source-citations',
105
103
  generatedAnswerThread: 'c-quantic-generated-answer-thread',
106
104
  generatedAnswerFollowUpInput: 'c-quantic-generated-answer-follow-up-input',
107
- generatedAnswerFeedback: 'c-quantic-feedback',
108
105
  loadingSpinner: 'lightning-spinner',
109
106
  };
110
107
 
@@ -128,6 +125,7 @@ const functionsMocks = {
128
125
  like: functionsMocks.like,
129
126
  dislike: functionsMocks.dislike,
130
127
  closeFeedbackModal: functionsMocks.closeFeedbackModal,
128
+ logCitationHover: functionsMocks.logCitationHover,
131
129
  })),
132
130
  buildSearchStatus: jest.fn(() => ({
133
131
  state: searchStatusState,
@@ -148,6 +146,7 @@ const functionsMocks = {
148
146
  like: jest.fn(),
149
147
  dislike: jest.fn(),
150
148
  closeFeedbackModal: jest.fn(),
149
+ logCitationHover: jest.fn(),
151
150
  };
152
151
 
153
152
  /**
@@ -504,33 +503,25 @@ describe('c-quantic-generated-answer', () => {
504
503
  });
505
504
  });
506
505
 
507
- it('should display the generated answer content', async () => {
506
+ it('should forward the streaming answer to the generated answer body', async () => {
508
507
  const element = createTestComponent();
509
508
  await flushPromises();
510
509
 
511
- const generatedAnswerContent = element.shadowRoot.querySelector(
512
- selectors.generatedAnswerContent
513
- );
514
-
515
- expect(generatedAnswerContent).not.toBeNull();
516
- expect(generatedAnswerContent.isStreaming).toBe(true);
517
- expect(generatedAnswerContent.answer).toBe(exampleAnswer);
518
- expect(generatedAnswerContent.answerContentFormat).toBe(
519
- exampleAnswerContentFormat
510
+ const generatedAnswerBody = element.shadowRoot.querySelector(
511
+ selectors.generatedAnswerBodyComponent
520
512
  );
521
- expect(generatedAnswerContent.engineId).toBe(exampleEngineId);
522
- expect(generatedAnswerContent.answerId).toBe(exampleAnswerId);
523
- });
524
-
525
- it('should not display the generated answer actions', async () => {
526
- const element = createTestComponent();
527
- await flushPromises();
528
513
 
529
- const generatedAnswerActions = element.shadowRoot.querySelector(
530
- selectors.generatedAnswerActions
514
+ expect(generatedAnswerBody).not.toBeNull();
515
+ expect(generatedAnswerBody.engineId).toBe(exampleEngineId);
516
+ expect(generatedAnswerBody.disableCitationAnchoring).toBe(false);
517
+ expect(generatedAnswerBody.generatedAnswer).toEqual(
518
+ expect.objectContaining({
519
+ answer: exampleAnswer,
520
+ answerContentFormat: exampleAnswerContentFormat,
521
+ answerId: exampleAnswerId,
522
+ isStreaming: true,
523
+ })
531
524
  );
532
-
533
- expect(generatedAnswerActions).toBeNull();
534
525
  });
535
526
 
536
527
  it('should not display the generated answer disclaimer in the footer', async () => {
@@ -769,38 +760,48 @@ describe('c-quantic-generated-answer', () => {
769
760
  });
770
761
  });
771
762
 
772
- it('should display the generated answer content', async () => {
763
+ it('should forward the ready answer with citations to the generated answer body', async () => {
773
764
  const element = createTestComponent();
774
765
  await flushPromises();
775
766
 
776
- const generatedAnswerContent = element.shadowRoot.querySelector(
777
- selectors.generatedAnswerContent
767
+ const generatedAnswerBody = element.shadowRoot.querySelector(
768
+ selectors.generatedAnswerBodyComponent
778
769
  );
779
770
 
780
- expect(generatedAnswerContent).not.toBeNull();
781
- expect(generatedAnswerContent.isStreaming).toBe(false);
782
- expect(generatedAnswerContent.answer).toBe(exampleAnswer);
783
- expect(generatedAnswerContent.answerContentFormat).toBe(
784
- exampleAnswerContentFormat
771
+ expect(generatedAnswerBody).not.toBeNull();
772
+ expect(generatedAnswerBody.engineId).toBe(exampleEngineId);
773
+ expect(generatedAnswerBody.disableCitationAnchoring).toBe(false);
774
+ expect(generatedAnswerBody.generatedAnswer).toEqual(
775
+ expect.objectContaining({
776
+ answer: exampleAnswer,
777
+ answerContentFormat: exampleAnswerContentFormat,
778
+ citations: exampleCitations,
779
+ isStreaming: false,
780
+ })
785
781
  );
786
782
  });
787
783
 
788
- it('should display the generated answer actions', async () => {
789
- const element = createTestComponent();
784
+ it('should collapse the answer when the body reports a content height exceeding the maximum collapsed height', async () => {
785
+ const element = createTestComponent({
786
+ ...defaultOptions,
787
+ collapsible: true,
788
+ });
790
789
  await flushPromises();
791
790
 
792
791
  const generatedAnswerBody = element.shadowRoot.querySelector(
793
- selectors.generatedAnswerBody
792
+ selectors.generatedAnswerBodyComponent
794
793
  );
795
- const generatedAnswerActions = element.shadowRoot.querySelector(
796
- selectors.generatedAnswerActions
794
+ generatedAnswerBody.dispatchEvent(
795
+ new CustomEvent('quantic__answercontentupdated', {
796
+ detail: {height: defaultAnswerHeight + 100},
797
+ })
797
798
  );
799
+ await flushPromises();
798
800
 
799
- expect(generatedAnswerBody).not.toBeNull();
800
- expect(generatedAnswerActions).not.toBeNull();
801
- expect(generatedAnswerActions.closest('section')).toBe(
802
- generatedAnswerBody
801
+ const generatedAnswerCollapseToggle = element.shadowRoot.querySelector(
802
+ selectors.generatedAnswerCollapseToggle
803
803
  );
804
+ expect(generatedAnswerCollapseToggle).not.toBeNull();
804
805
  });
805
806
 
806
807
  it('should display the generated answer disclaimer', async () => {
@@ -833,17 +834,6 @@ describe('c-quantic-generated-answer', () => {
833
834
  expect(generatedAnswerBadge).not.toBeNull();
834
835
  });
835
836
 
836
- it('should pass the disableCitationAnchoring property to the source citations component', async () => {
837
- const element = createTestComponent();
838
- await flushPromises();
839
-
840
- const generatedAnswerCitations = element.shadowRoot.querySelector(
841
- selectors.generatedAnswerCitations
842
- );
843
- expect(generatedAnswerCitations).not.toBeNull();
844
- expect(generatedAnswerCitations.disableCitationAnchoring).toBe(false);
845
- });
846
-
847
837
  describe('when follow-ups are enabled', () => {
848
838
  const exampleAgentId = 'example agent id';
849
839
  const exampleFollowUpAnswers = [
@@ -1385,6 +1375,103 @@ describe('c-quantic-generated-answer', () => {
1385
1375
  });
1386
1376
  });
1387
1377
 
1378
+ describe('the citation hover event', () => {
1379
+ const exampleAnswer = 'answer generated successfully';
1380
+ const exampleAgentId = 'example agent id';
1381
+ const exampleCitationId = 'citation-id-1';
1382
+ const exampleCitationHoverTimeMs = 1500;
1383
+
1384
+ describe('when follow-ups are not enabled', () => {
1385
+ beforeEach(() => {
1386
+ generatedAnswerState = {
1387
+ ...initialGeneratedAnswerState,
1388
+ isStreaming: false,
1389
+ answer: exampleAnswer,
1390
+ answerContentFormat: 'text/markdown',
1391
+ answerId: exampleAnswerId,
1392
+ citations: exampleCitations,
1393
+ };
1394
+ mockSuccessfulHeadlessInitialization();
1395
+ prepareHeadlessState();
1396
+ });
1397
+
1398
+ afterAll(() => {
1399
+ generatedAnswerState = initialGeneratedAnswerState;
1400
+ });
1401
+
1402
+ it('should call logCitationHover on the controller with the correct parameters', async () => {
1403
+ const element = createTestComponent();
1404
+ await flushPromises();
1405
+
1406
+ const generatedAnswerBody = element.shadowRoot.querySelector(
1407
+ selectors.generatedAnswerBodyComponent
1408
+ );
1409
+ generatedAnswerBody.dispatchEvent(
1410
+ new CustomEvent('quantic__citationhover', {
1411
+ detail: {
1412
+ citationId: exampleCitationId,
1413
+ citationHoverTimeMs: exampleCitationHoverTimeMs,
1414
+ answerId: exampleAnswerId,
1415
+ },
1416
+ })
1417
+ );
1418
+ await flushPromises();
1419
+
1420
+ expect(functionsMocks.logCitationHover).toHaveBeenCalledTimes(1);
1421
+ expect(functionsMocks.logCitationHover).toHaveBeenCalledWith(
1422
+ exampleCitationId,
1423
+ exampleCitationHoverTimeMs,
1424
+ exampleAnswerId
1425
+ );
1426
+ });
1427
+ });
1428
+
1429
+ describe('when follow-ups are enabled', () => {
1430
+ beforeEach(() => {
1431
+ generatedAnswerState = {
1432
+ ...initialGeneratedAnswerState,
1433
+ answer: exampleAnswer,
1434
+ followUpAnswers: {isEnabled: true, followUpAnswers: []},
1435
+ };
1436
+ mockSuccessfulHeadlessInitialization();
1437
+ prepareHeadlessState();
1438
+ });
1439
+
1440
+ afterAll(() => {
1441
+ generatedAnswerState = initialGeneratedAnswerState;
1442
+ });
1443
+
1444
+ it('should call logCitationHover on the controller with the correct parameters', async () => {
1445
+ const element = createTestComponent({
1446
+ ...defaultOptions,
1447
+ agentId: exampleAgentId,
1448
+ });
1449
+ await flushPromises();
1450
+
1451
+ const thread = element.shadowRoot.querySelector(
1452
+ selectors.generatedAnswerThread
1453
+ );
1454
+ thread.dispatchEvent(
1455
+ new CustomEvent('quantic__citationhover', {
1456
+ detail: {
1457
+ citationId: exampleCitationId,
1458
+ citationHoverTimeMs: exampleCitationHoverTimeMs,
1459
+ answerId: exampleAnswerId,
1460
+ },
1461
+ })
1462
+ );
1463
+ await flushPromises();
1464
+
1465
+ expect(functionsMocks.logCitationHover).toHaveBeenCalledTimes(1);
1466
+ expect(functionsMocks.logCitationHover).toHaveBeenCalledWith(
1467
+ exampleCitationId,
1468
+ exampleCitationHoverTimeMs,
1469
+ exampleAnswerId
1470
+ );
1471
+ });
1472
+ });
1473
+ });
1474
+
1388
1475
  describe('the generated answer feedback', () => {
1389
1476
  const exampleAnswer = 'answer generated successfully';
1390
1477
  const exampleAgentId = 'example agent id';
@@ -1392,19 +1479,17 @@ describe('c-quantic-generated-answer', () => {
1392
1479
  describe.each([
1393
1480
  {
1394
1481
  action: 'like',
1395
- feedbackEvent: 'quantic__like',
1396
1482
  threadEvent: 'quantic__generatedanswerlike',
1397
1483
  controllerMethod: 'like',
1398
1484
  },
1399
1485
  {
1400
1486
  action: 'dislike',
1401
- feedbackEvent: 'quantic__dislike',
1402
1487
  threadEvent: 'quantic__generatedanswerdislike',
1403
1488
  controllerMethod: 'dislike',
1404
1489
  },
1405
1490
  ])(
1406
1491
  'when the user clicks the $action button',
1407
- ({feedbackEvent, threadEvent, controllerMethod}) => {
1492
+ ({threadEvent, controllerMethod}) => {
1408
1493
  describe('when follow-ups are not enabled', () => {
1409
1494
  beforeEach(() => {
1410
1495
  generatedAnswerState = {
@@ -1427,11 +1512,11 @@ describe('c-quantic-generated-answer', () => {
1427
1512
  const element = createTestComponent();
1428
1513
  await flushPromises();
1429
1514
 
1430
- const feedback = element.shadowRoot.querySelector(
1431
- selectors.generatedAnswerFeedback
1515
+ const generatedAnswerBody = element.shadowRoot.querySelector(
1516
+ selectors.generatedAnswerBodyComponent
1432
1517
  );
1433
- feedback.dispatchEvent(
1434
- new CustomEvent(feedbackEvent, {
1518
+ generatedAnswerBody.dispatchEvent(
1519
+ new CustomEvent(threadEvent, {
1435
1520
  detail: {answerId: exampleAnswerId},
1436
1521
  })
1437
1522
  );
@@ -46,7 +46,6 @@ export const testAgent =
46
46
  {
47
47
  headAnswerRequestRegex: agentAnswerRequestRegex,
48
48
  agentFollowUpRequestRegex,
49
- isAgent: true,
50
49
  }
51
50
  );
52
51
 
@@ -56,7 +55,8 @@ export const testAgent =
56
55
  await generatedAnswerObject.mockAgentFollowUpResponse(
57
56
  data.followUpStreams
58
57
  );
59
-
58
+
59
+ await search.mockSearchWithBaseResponse();
60
60
  await page.goto(pageUrl);
61
61
  await configuration.configure(options);
62
62
  await search.waitForSearchResponse();
@@ -67,7 +67,7 @@ export const testAgent =
67
67
  generatedAnswerObject.waitForHeadAnswerRequest();
68
68
 
69
69
  await search.fillSearchInput(exampleQuery);
70
- await search.performSearch();
70
+ search.performSearch();
71
71
  await search.waitForSearchResponse();
72
72
 
73
73
  await use(generatedAnswerObject);
@@ -89,6 +89,7 @@ export const testSearch =
89
89
  data.streamId
90
90
  );
91
91
 
92
+ await search.mockSearchWithBaseResponse();
92
93
  await page.goto(pageUrl);
93
94
  if (withFacets) {
94
95
  await generatedAnswerObject.clickAddFacetsButton();
@@ -104,10 +105,12 @@ export const testSearch =
104
105
  generatedAnswerObject.waitForHeadAnswerRequest();
105
106
  }
106
107
 
107
- await search.fillSearchInput(exampleQuery);
108
108
  await search.mockSearchWithGenerativeQuestionAnsweringId(data.streamId);
109
- await search.performSearch();
110
- await search.waitForSearchResponse();
109
+ await search.fillSearchInput(exampleQuery);
110
+ await Promise.all([
111
+ search.waitForSearchResponse(),
112
+ search.performSearch(),
113
+ ]);
111
114
 
112
115
  await use(generatedAnswerObject);
113
116
  },
@@ -162,17 +165,14 @@ export const testInsight =
162
165
  data.streamId
163
166
  );
164
167
 
168
+ await search.mockSearchWithBaseResponse();
165
169
  await page.goto(pageUrl);
166
170
  if (withFacets) {
167
171
  await generatedAnswerObject.clickAddFacetsButton();
168
172
  }
169
173
  configuration.configure({...options, useCase: useCaseEnum.insight});
170
-
171
174
  await insightSetup.waitForInsightInterfaceInitialization();
172
- await search.performSearch();
173
- await search.waitForSearchResponse();
174
175
 
175
- await search.mockSearchWithGenerativeQuestionAnsweringId(data.streamId);
176
176
  generatedAnswerObject.streamEndAnalyticRequestPromise =
177
177
  generatedAnswerObject.waitForStreamEndAnalytics();
178
178
  if (options.answerConfigurationId) {
@@ -180,9 +180,12 @@ export const testInsight =
180
180
  generatedAnswerObject.waitForHeadAnswerRequest();
181
181
  }
182
182
 
183
+ await search.mockSearchWithGenerativeQuestionAnsweringId(data.streamId);
183
184
  await search.fillSearchInput(exampleQuery);
184
- await search.performSearch();
185
- await search.waitForSearchResponse();
185
+ await Promise.all([
186
+ search.waitForSearchResponse(),
187
+ search.performSearch(),
188
+ ]);
186
189
 
187
190
  await use(generatedAnswerObject);
188
191
  },
@@ -29,7 +29,6 @@ export interface GeneratedAnswerObjectConfig {
29
29
  answerApiEnabled?: boolean;
30
30
  headAnswerRequestRegex?: RegExp;
31
31
  agentFollowUpRequestRegex?: RegExp;
32
- isAgent?: boolean;
33
32
  withFacets?: boolean;
34
33
  }
35
34
 
@@ -39,7 +38,6 @@ export class GeneratedAnswerObject {
39
38
  private headAnswerRequestRegex?: RegExp;
40
39
  private agentFollowUpRequestRegex?: RegExp;
41
40
  private withFacets: boolean;
42
- private isAgent: boolean;
43
41
 
44
42
  constructor(
45
43
  private page: Page,
@@ -55,15 +53,14 @@ export class GeneratedAnswerObject {
55
53
  this.headAnswerRequestRegex = config.headAnswerRequestRegex;
56
54
  this.agentFollowUpRequestRegex = config.agentFollowUpRequestRegex;
57
55
  this.withFacets = config.withFacets ?? false;
58
- this.isAgent = config.isAgent ?? false;
59
56
  }
60
57
 
61
58
  get likeButton(): Locator {
62
- return this.page.getByRole('button', {name: this.isAgent ? 'Like' : 'This answer was helpful', exact: true});
59
+ return this.page.getByRole('button', {name: 'Like', exact: true});
63
60
  }
64
61
 
65
62
  get dislikeButton(): Locator {
66
- return this.page.getByRole('button', {name: this.isAgent ? 'Dislike' : 'This answer was not helpful', exact: true});
63
+ return this.page.getByRole('button', {name: 'Dislike', exact: true});
67
64
  }
68
65
 
69
66
  get copyToClipboardButton(): Locator {
@@ -132,7 +129,7 @@ export class GeneratedAnswerObject {
132
129
 
133
130
  get citationLink(): Locator {
134
131
  return this.page
135
- .getByTestId(this.isAgent ? 'generated-answer-body__citations' : 'generated-answer__citations')
132
+ .getByTestId('generated-answer-body__citations')
136
133
  .locator('.citation__link');
137
134
  }
138
135
 
@@ -157,8 +154,8 @@ export class GeneratedAnswerObject {
157
154
  }
158
155
 
159
156
  async hoverOverCitation(index: number): Promise<void> {
160
- // waiting 500ms to allow the component to render completely, cause any re-rendering abort the hover action.
161
- await this.page.waitForTimeout(500);
157
+ // waiting 1000ms to allow the component to render completely, cause any re-rendering abort the hover action.
158
+ await this.page.waitForTimeout(1000);
162
159
  await this.citationLink.nth(index).hover();
163
160
  await this.page.waitForTimeout(minimumCitationTooltipDisplayDurationMs);
164
161
  await this.page.mouse.move(0, 0);
@@ -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, getAbsoluteHeight} from 'c/quanticUtils';
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.generatedAnswerElementHeight + 50
282
- : this.generatedAnswerElementHeight;
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 allGeneratedAnswers() {
531
- const initialAnswer = {
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';