@coveo/quantic 3.37.10 → 3.38.0

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 (32) hide show
  1. package/force-app/main/default/lwc/quanticFeedback/__tests__/quanticFeedback.test.js +11 -0
  2. package/force-app/main/default/lwc/quanticFeedback/quanticFeedback.html +6 -4
  3. package/force-app/main/default/lwc/quanticFeedback/quanticFeedback.js +8 -0
  4. package/force-app/main/default/lwc/quanticGeneratedAnswer/__tests__/quanticGeneratedAnswer.test.js +32 -3
  5. package/force-app/main/default/lwc/quanticGeneratedAnswer/quanticGeneratedAnswer.css +1 -8
  6. package/force-app/main/default/lwc/quanticGeneratedAnswer/quanticGeneratedAnswer.js +6 -28
  7. package/force-app/main/default/lwc/quanticGeneratedAnswer/templates/generatedAnswer.css +0 -13
  8. package/force-app/main/default/lwc/quanticGeneratedAnswer/templates/generatedAnswer.html +120 -93
  9. package/force-app/main/default/lwc/quanticGeneratedAnswer/templates/retryPrompt.html +24 -15
  10. package/force-app/main/default/lwc/quanticGeneratedAnswerCopyToClipboard/__tests__/quanticGeneratedAnswerCopyToClipboard.test.js +125 -0
  11. package/force-app/main/default/lwc/quanticGeneratedAnswerCopyToClipboard/quanticGeneratedAnswerCopyToClipboard.html +1 -1
  12. package/force-app/main/default/lwc/quanticGeneratedAnswerCopyToClipboard/quanticGeneratedAnswerCopyToClipboard.js +20 -1
  13. package/force-app/main/default/lwc/quanticThreadItem/__tests__/quanticThreadItem.test.js +176 -0
  14. package/force-app/main/default/lwc/quanticThreadItem/quanticThreadItem.css +64 -0
  15. package/force-app/main/default/lwc/quanticThreadItem/quanticThreadItem.html +48 -0
  16. package/force-app/main/default/lwc/quanticThreadItem/quanticThreadItem.js +80 -0
  17. package/force-app/main/default/lwc/quanticThreadItem/quanticThreadItem.js-meta.xml +5 -0
  18. package/force-app/main/default/staticresources/coveoheadless/case-assist/headless.js +15 -15
  19. package/force-app/main/default/staticresources/coveoheadless/definitions/app/case-assist-engine/case-assist-engine-configuration.d.ts +1 -1
  20. package/force-app/main/default/staticresources/coveoheadless/definitions/app/insight-engine/insight-engine-configuration.d.ts +1 -1
  21. package/force-app/main/default/staticresources/coveoheadless/definitions/app/recommendation-engine/recommendation-engine-configuration.d.ts +1 -1
  22. package/force-app/main/default/staticresources/coveoheadless/definitions/app/search-engine/search-engine-configuration.d.ts +1 -1
  23. package/force-app/main/default/staticresources/coveoheadless/definitions/features/case-assist-configuration/case-assist-configuration-actions.d.ts +1 -1
  24. package/force-app/main/default/staticresources/coveoheadless/definitions/features/case-assist-configuration/case-assist-configuration-state.d.ts +1 -1
  25. package/force-app/main/default/staticresources/coveoheadless/definitions/features/configuration/configuration-actions.d.ts +1 -1
  26. package/force-app/main/default/staticresources/coveoheadless/definitions/features/configuration/configuration-state.d.ts +1 -1
  27. package/force-app/main/default/staticresources/coveoheadless/definitions/features/generated-answer/generated-answer-request.d.ts +2 -2
  28. package/force-app/main/default/staticresources/coveoheadless/headless.js +17 -17
  29. package/force-app/main/default/staticresources/coveoheadless/insight/headless.js +16 -16
  30. package/force-app/main/default/staticresources/coveoheadless/recommendation/headless.js +14 -14
  31. package/force-app/main/default/staticresources/dompurify/purify.min.js +2 -2
  32. package/package.json +4 -4
@@ -106,6 +106,17 @@ describe('c-quantic-feedback', () => {
106
106
  expect(question.textContent).toBe(exampleQuestion);
107
107
  });
108
108
 
109
+ it('should not render the feedback question when question is empty', async () => {
110
+ const element = createTestComponent({...defaultOptions, question: ''});
111
+ await flushPromises();
112
+
113
+ const question = element.shadowRoot.querySelector(
114
+ selectors.feedbackQuestion
115
+ );
116
+
117
+ expect(question).toBeNull();
118
+ });
119
+
109
120
  describe('when the component is in a neutral state', () => {
110
121
  it('should display the feedback buttons in a neutral state', async () => {
111
122
  const element = createTestComponent();
@@ -1,7 +1,9 @@
1
1
  <template>
2
2
  <div class="slds-grid feedback__main-section">
3
- <span class="slds-var-p-right_small slds-text-color_weak feedback__question"
4
- id="feedback__question">{question}</span>
3
+ <template lwc:if={hasQuestion}>
4
+ <span class="slds-var-p-right_small slds-text-color_weak feedback__question"
5
+ id="feedback__question">{question}</span>
6
+ </template>
5
7
  <c-quantic-stateful-button selected={liked} data-testid="feedback__like-button" tooltip={likeLabel}
6
8
  icon-name={likeIconName} onquantic__select={handleLike} onquantic__deselect={handleLike} without-borders selected-state-color="#2e844a"
7
9
  label={likeButtonLabel} icon-size={size}></c-quantic-stateful-button>
@@ -9,10 +11,10 @@
9
11
  icon-name={dislikeIconName} onquantic__select={handleDislike} onquantic__deselect={handleDislike} without-borders selected-state-color="#ea001e"
10
12
  label={dislikeButtonLabel} icon-size={size}></c-quantic-stateful-button>
11
13
  </div>
12
- <template if:true={displaySuccessMessage}>
14
+ <template lwc:if={displaySuccessMessage}>
13
15
  <div class="slds-grid feedback__success-message">
14
16
  <span class="slds-var-p-vertical_x-small">{successMessage}</span>
15
- <template if:false={hideExplainWhyButton}>
17
+ <template lwc:if={shouldShowExplainWhyButton}>
16
18
  <lightning-button data-testid="feedback__explain-why-button" class="slds-var-m-left_x-small feedback__explain-why"
17
19
  variant="base" label={labels.explainWhy} onclick={handlePressExplainWhyButton}></lightning-button>
18
20
  </template>
@@ -168,4 +168,12 @@ export default class QuanticFeedback extends LightningElement {
168
168
  if (this.hideLabels) return '';
169
169
  return this.dislikeLabel;
170
170
  }
171
+
172
+ get hasQuestion() {
173
+ return !!this.question?.trim();
174
+ }
175
+
176
+ get shouldShowExplainWhyButton() {
177
+ return !this.hideExplainWhyButton;
178
+ }
171
179
  }
@@ -73,8 +73,11 @@ function createTestComponent(options = defaultOptions, assignedElements = []) {
73
73
  const selectors = {
74
74
  initializationError: 'c-quantic-component-error',
75
75
  generatedAnswerCard: '[data-testid="generated-answer__card"]',
76
+ generatedAnswerHeader: '[data-testid="generated-answer__header"]',
77
+ generatedAnswerBody: '[data-testid="generated-answer__body"]',
78
+ generatedAnswerFooter: '[data-testid="generated-answer__footer"]',
76
79
  generatedAnswer: '[data-testid="generated-answer__answer"]',
77
- generatedAnswerBadge: '[data-testid="generated-answer__badge"]',
80
+ generatedAnswerBadge: '[data-testid="generated-answer__header-title"]',
78
81
  generatedAnswerRetryButton: '[data-testid="generated-answer__retry-button"]',
79
82
  generatedAnswerActions: '[data-testid="generated-answer__actions"]',
80
83
  generatedAnswerToggleButton: 'c-quantic-generated-answer-toggle',
@@ -471,7 +474,7 @@ describe('c-quantic-generated-answer', () => {
471
474
  expect(generatedAnswerActions).toBeNull();
472
475
  });
473
476
 
474
- it('should not display the generated answer disclaimer', async () => {
477
+ it('should not display the generated answer disclaimer in the footer', async () => {
475
478
  const element = createTestComponent();
476
479
  await flushPromises();
477
480
 
@@ -727,24 +730,50 @@ describe('c-quantic-generated-answer', () => {
727
730
  const element = createTestComponent();
728
731
  await flushPromises();
729
732
 
733
+ const generatedAnswerBody = element.shadowRoot.querySelector(
734
+ selectors.generatedAnswerBody
735
+ );
730
736
  const generatedAnswerActions = element.shadowRoot.querySelector(
731
737
  selectors.generatedAnswerActions
732
738
  );
733
739
 
740
+ expect(generatedAnswerBody).not.toBeNull();
734
741
  expect(generatedAnswerActions).not.toBeNull();
742
+ expect(generatedAnswerActions.closest('section')).toBe(
743
+ generatedAnswerBody
744
+ );
735
745
  });
736
746
 
737
- it('should not display the generated answer disclaimer', async () => {
747
+ it('should display the generated answer disclaimer', async () => {
738
748
  const element = createTestComponent();
739
749
  await flushPromises();
740
750
 
751
+ const generatedAnswerFooter = element.shadowRoot.querySelector(
752
+ selectors.generatedAnswerFooter
753
+ );
741
754
  const generatedAnswerDisclaimer = element.shadowRoot.querySelector(
742
755
  selectors.generatedAnswerDisclaimer
743
756
  );
744
757
 
758
+ expect(generatedAnswerFooter).not.toBeNull();
745
759
  expect(generatedAnswerDisclaimer).not.toBeNull();
746
760
  });
747
761
 
762
+ it('should display the generated answer header content', async () => {
763
+ const element = createTestComponent();
764
+ await flushPromises();
765
+
766
+ const generatedAnswerHeader = element.shadowRoot.querySelector(
767
+ selectors.generatedAnswerHeader
768
+ );
769
+ const generatedAnswerBadge = element.shadowRoot.querySelector(
770
+ selectors.generatedAnswerBadge
771
+ );
772
+
773
+ expect(generatedAnswerHeader).not.toBeNull();
774
+ expect(generatedAnswerBadge).not.toBeNull();
775
+ });
776
+
748
777
  it('should pass the disableCitationAnchoring property to the source citations component', async () => {
749
778
  const element = createTestComponent();
750
779
  await flushPromises();
@@ -1,10 +1,3 @@
1
- .generated-answer__card-header {
2
- justify-content: space-between;
3
- align-items: center;
4
- }
5
-
6
- .generated-answer__badge {
7
- background-color: var(--lwc-colorBackgroundButtonDefaultHover, #eef4ff);
1
+ .generated-answer__header-title {
8
2
  color: var(--lwc-brandPrimary, #1b96ff);
9
- border-radius: var(--lwc-border-radius-medium, 0.25rem);
10
3
  }
@@ -535,12 +535,12 @@ export default class QuanticGeneratedAnswer extends LightningElement {
535
535
  ];
536
536
  }
537
537
 
538
- get generatedAnswerFooterCssClass() {
539
- return 'slds-grid slds-wrap slds-grid_align-spread generated-answer__footer';
540
- }
541
-
542
- get generatedAnswerFooterRowClass() {
543
- return 'generated-answer__footer-row slds-grid slds-col slds-size_1-of-1 slds-wrap slds-grid_align-spread';
538
+ get generatedAnswerHeaderClass() {
539
+ const headerBaseClass =
540
+ 'generated-answer__card-header slds-grid slds-grid_vertical-align-center slds-grid_align-spread slds-p-horizontal_large slds-p-vertical_small';
541
+ return this.isVisible
542
+ ? `${headerBaseClass} slds-border_bottom`
543
+ : headerBaseClass;
544
544
  }
545
545
 
546
546
  get citationFields() {
@@ -565,28 +565,6 @@ export default class QuanticGeneratedAnswer extends LightningElement {
565
565
  return this.isAnswerCollapsed ? 'utility:chevrondown' : 'utility:chevronup';
566
566
  }
567
567
 
568
- get shouldShowCollapseGeneratingMessage() {
569
- // If the answer is collapsed and is still streaming,
570
- // we should show a message letting the user know it's still generating.
571
- return (
572
- this.collapsible &&
573
- this.isVisible &&
574
- this.isStreaming &&
575
- this._exceedsMaximumHeight
576
- );
577
- }
578
-
579
- get shouldShowToggleCollapseAnswer() {
580
- // Only show the toggle collapse button if the answer is
581
- // collapsible, visible, not streaming, and exceeds the maximum height.
582
- return (
583
- this.collapsible &&
584
- this.isVisible &&
585
- !this.isStreaming &&
586
- this._exceedsMaximumHeight
587
- );
588
- }
589
-
590
568
  /**
591
569
  * Returns the label to display in the generated answer show more|show less button.
592
570
  * @returns {string}
@@ -1,10 +1,5 @@
1
1
  @import '../quanticGeneratedAnswer.css';
2
2
 
3
- .generated-answer__header-actions {
4
- align-items: center;
5
- flex-wrap: wrap-reverse;
6
- }
7
-
8
3
  .generated-answer__answer {
9
4
  overflow: hidden;
10
5
  position: relative;
@@ -60,14 +55,6 @@
60
55
  visibility: hidden;
61
56
  }
62
57
 
63
- .generated-answer__disclaimer {
64
- min-height: var(--lwc-lineHeightButton,1.875rem);
65
- }
66
-
67
- .flex-one {
68
- flex: 1;
69
- }
70
-
71
58
  @keyframes cursor-blink {
72
59
  100% {
73
60
  visibility: hidden;
@@ -1,49 +1,34 @@
1
1
  <template>
2
2
  <template lwc:if={shouldDisplayGeneratedAnswer}>
3
- <div data-testid="generated-answer__card" class="slds-box">
4
- <div class="generated-answer__card-header slds-grid">
5
- <span
6
- data-testid="generated-answer__badge"
7
- class="generated-answer__badge slds-var-p-around_x-small slds-text-title_bold"
8
- >{labels.generatedAnswerForYou}</span
9
- >
10
- <div class="generated-answer__header-actions slds-grid">
11
- <template lwc:if={shouldDisplayActions}>
12
- <div
13
- data-testid="generated-answer__actions"
14
- class="slds-grid flex-one slds-grid_align-end"
15
- >
16
- <c-quantic-feedback
17
- state={feedbackState}
18
- onquantic__like={handleLike}
19
- onquantic__dislike={handleDislike}
20
- like-icon-name="utility:like"
21
- like-label={labels.thisAnswerWasHelpful}
22
- dislike-icon-name="utility:dislike"
23
- dislike-label={labels.thisAnswerWasNotHelpful}
24
- size="xx-small"
25
- question=""
26
- hide-explain-why-button
27
- hide-labels
28
- ></c-quantic-feedback>
29
- <c-quantic-generated-answer-copy-to-clipboard
30
- data-testid="generated-answer__copy-to-clipboard"
31
- answer={answer}
32
- class="slds-var-m-horizontal_xx-small"
33
- ></c-quantic-generated-answer-copy-to-clipboard>
34
- </div>
35
- </template>
36
- <template lwc:if={withToggle}>
37
- <div class="slds-grid flex-one slds-grid_align-end">
38
- <c-quantic-generated-answer-toggle
39
- is-generated-answer-visible={isVisible}
40
- ></c-quantic-generated-answer-toggle>
41
- </div>
42
- </template>
3
+ <div data-testid="generated-answer__card" class="slds-box slds-p-around_none">
4
+ <header
5
+ data-testid="generated-answer__header"
6
+ class={generatedAnswerHeaderClass}
7
+ >
8
+ <div class="generated-answer__header-title slds-grid slds-grid_vertical-align-center">
9
+ <lightning-icon
10
+ icon-name="utility:sparkles"
11
+ size="x-small"
12
+ alternative-text={labels.generatedAnswerForYou}
13
+ class="slds-current-color slds-var-m-right_x-small"
14
+ ></lightning-icon>
15
+ <span
16
+ data-testid="generated-answer__header-title"
17
+ class="slds-text-title_bold slds-p-vertical_x-small"
18
+ >{labels.generatedAnswerForYou}</span
19
+ >
43
20
  </div>
44
- </div>
21
+ <template lwc:if={withToggle}>
22
+ <c-quantic-generated-answer-toggle
23
+ is-generated-answer-visible={isVisible}
24
+ ></c-quantic-generated-answer-toggle>
25
+ </template>
26
+ </header>
45
27
  <template lwc:if={isVisible}>
46
- <div class="generated-answer__content slds-var-m-top_medium">
28
+ <section
29
+ data-testid="generated-answer__body"
30
+ class="generated-answer__content slds-p-top_medium slds-p-horizontal_large"
31
+ >
47
32
  <div
48
33
  data-testid="generated-answer__answer"
49
34
  class={generatedAnswerClass}
@@ -56,63 +41,100 @@
56
41
  >
57
42
  </c-quantic-generated-answer-content>
58
43
  </div>
59
- <div class={generatedAnswerFooterCssClass}>
60
- <div class={generatedAnswerFooterRowClass}>
61
- <template lwc:if={shouldDisplayCitations}>
62
- <div class="slds-var-m-top_small">
63
- <c-quantic-source-citations
64
- engine-id={engineId}
65
- data-testid="generated-answer__citations"
66
- citations={citations}
67
- citation-hover-handler={handleCitationHover}
68
- disable-citation-anchoring={disableCitationAnchoring}
69
- ></c-quantic-source-citations>
70
- </div>
71
- </template>
72
- </div>
73
- <div class={generatedAnswerFooterRowClass}>
74
- <template lwc:if={shouldShowCollapseGeneratingMessage}>
44
+ <div class="slds-grid slds-grid_vertical">
45
+ <template lwc:if={shouldDisplayCitations}>
46
+ <div class="slds-size_1-of-1 slds-var-m-top_x-small">
47
+ <c-quantic-source-citations
48
+ engine-id={engineId}
49
+ data-testid="generated-answer__citations"
50
+ citations={citations}
51
+ citation-hover-handler={handleCitationHover}
52
+ disable-citation-anchoring={disableCitationAnchoring}
53
+ ></c-quantic-source-citations>
54
+ </div>
55
+ </template>
56
+ <template lwc:if={shouldDisplayActions}>
57
+ <div class="slds-size_1-of-1 slds-grid slds-grid_vertical-align-center slds-var-m-top_x-small slds-grid_align-start">
75
58
  <div
76
- data-testid="generated-answer__collapse-generating-message"
77
- class="generated-answer__collapse-generating-message"
59
+ data-testid="generated-answer__actions"
60
+ class="slds-grid slds-grid_vertical-align-center"
78
61
  >
79
- {labels.generatingAnswer}...
62
+ <c-quantic-feedback
63
+ state={feedbackState}
64
+ onquantic__like={handleLike}
65
+ onquantic__dislike={handleDislike}
66
+ like-icon-name="utility:like"
67
+ like-label={labels.thisAnswerWasHelpful}
68
+ dislike-icon-name="utility:dislike"
69
+ dislike-label={labels.thisAnswerWasNotHelpful}
70
+ size="x-small"
71
+ question=""
72
+ hide-explain-why-button
73
+ hide-labels
74
+ ></c-quantic-feedback>
75
+ <c-quantic-generated-answer-copy-to-clipboard
76
+ data-testid="generated-answer__copy-to-clipboard"
77
+ answer={answer}
78
+ size="x-small"
79
+ class="slds-var-m-horizontal_xx-small"
80
+ ></c-quantic-generated-answer-copy-to-clipboard>
80
81
  </div>
81
- </template>
82
- <template lwc:if={shouldShowToggleCollapseAnswer}>
83
- <lightning-button
84
- data-testid="generated-answer__answer-toggle"
85
- icon-name={toggleCollapseAnswerIcon}
86
- variant="base"
87
- label={toggleCollapseAnswerLabel}
88
- title={toggleCollapseAnswerLabel}
89
- onclick={handleToggleCollapseAnswer}
90
- icon-position="right"
91
- class="generated-answer__answer-toggle slds-shrink-none slds-var-m-right_large"
92
- ></lightning-button>
93
- </template>
94
- <template lwc:if={shouldShowDisclaimer}>
95
- <div
96
- class="generated-answer__disclaimer slds-grid_vertical-align-center slds-col slds-text-color_weak slds-text-body_small"
97
- data-testid="generated-answer__disclaimer"
98
- >
99
- <slot name="disclaimer"> {labels.rgaDisclaimer} </slot>
82
+ </div>
83
+ </template>
84
+ <template lwc:if={isAnswerCollapsed}>
85
+ <div class="slds-grid slds-size_1-of-1 slds-wrap slds-grid_vertical-align-center slds-var-m-top_x-small">
86
+ <div class="slds-grid slds-wrap slds-grid_vertical-align-center">
87
+ <template lwc:if={isStreaming}>
88
+ <div
89
+ data-testid="generated-answer__collapse-generating-message"
90
+ class="generated-answer__collapse-generating-message slds-var-m-right_small"
91
+ >
92
+ {labels.generatingAnswer}...
93
+ </div>
94
+ </template>
95
+ <template lwc:else>
96
+ <lightning-button
97
+ data-testid="generated-answer__answer-toggle"
98
+ icon-name={toggleCollapseAnswerIcon}
99
+ variant="base"
100
+ label={toggleCollapseAnswerLabel}
101
+ title={toggleCollapseAnswerLabel}
102
+ onclick={handleToggleCollapseAnswer}
103
+ icon-position="right"
104
+ class="generated-answer__answer-toggle slds-shrink-none slds-var-m-right_large"
105
+ ></lightning-button>
106
+ </template>
100
107
  </div>
101
- </template>
102
- </div>
108
+ </div>
109
+ </template>
103
110
  </div>
104
- </div>
111
+ </section>
112
+ <template lwc:if={shouldShowDisclaimer}>
113
+ <footer
114
+ data-testid="generated-answer__footer"
115
+ class="generated-answer__disclaimer slds-grid slds-grid_vertical-align-center slds-grid_align-end slds-text-color_weak slds-text-body_small slds-p-horizontal_large slds-p-bottom_medium"
116
+ >
117
+ <div data-testid="generated-answer__disclaimer">
118
+ <slot name="disclaimer"> {labels.rgaDisclaimer} </slot>
119
+ </div>
120
+ </footer>
121
+ </template>
105
122
  </template>
106
123
  </div>
107
124
  </template>
108
125
  <template lwc:if={cannotAnswer}>
109
126
  <template lwc:if={isManualAnswerGeneration}>
110
127
  <div data-testid="generated-answer__no-answer-card" class="slds-box">
111
- <div class="generated-answer__card-header slds-grid">
112
- <span
113
- class="generated-answer__badge slds-var-p-around_x-small slds-text-title_bold"
114
- >{labels.generatedAnswerForYou}</span
115
- >
128
+ <div class="generated-answer__card-header slds-grid slds-grid_vertical-align-center slds-border_bottom slds-p-around_x-small">
129
+ <div class="generated-answer__header-title slds-grid slds-grid_vertical-align-center">
130
+ <lightning-icon
131
+ icon-name="utility:sparkles"
132
+ size="x-small"
133
+ alternative-text={labels.generatedAnswerForYou}
134
+ class="slds-current-color slds-var-m-right_x-small"
135
+ ></lightning-icon>
136
+ <span class="slds-text-title_bold">{labels.generatedAnswerForYou}</span>
137
+ </div>
116
138
  </div>
117
139
  <div
118
140
  data-testid="generated-answer__no-answer-message"
@@ -127,11 +149,16 @@
127
149
  <template lwc:else>
128
150
  <template lwc:if={hasCustomNoAnswerMessage}>
129
151
  <div data-testid="generated-answer__no-answer-card" class="slds-box">
130
- <div class="generated-answer__card-header slds-grid">
131
- <span
132
- class="generated-answer__badge slds-var-p-around_x-small slds-text-title_bold"
133
- >{labels.generatedAnswerForYou}</span
134
- >
152
+ <div class="generated-answer__card-header slds-grid slds-grid_vertical-align-center slds-border_bottom slds-p-around_x-small">
153
+ <div class="generated-answer__header-title slds-grid slds-grid_vertical-align-center">
154
+ <lightning-icon
155
+ icon-name="utility:sparkles"
156
+ size="x-small"
157
+ alternative-text={labels.generatedAnswerForYou}
158
+ class="slds-current-color slds-var-m-right_x-small"
159
+ ></lightning-icon>
160
+ <span class="slds-text-title_bold">{labels.generatedAnswerForYou}</span>
161
+ </div>
135
162
  </div>
136
163
  <div class="generated-answer__content slds-var-m-top_medium">
137
164
  <div class={generatedAnswerClass}>
@@ -2,25 +2,34 @@
2
2
  <template lwc:if={shouldDisplayGeneratedAnswer}>
3
3
  <div
4
4
  data-testid="generated-answer__card"
5
- class="slds-box"
5
+ class="slds-box slds-p-around_none"
6
6
  >
7
7
  <div
8
- class="slds-var-m-bottom_medium slds-grid generated-answer__card-header"
8
+ class="generated-answer__card-header slds-grid slds-grid_vertical-align-center slds-border_bottom slds-p-vertical_small slds-p-horizontal_large"
9
9
  >
10
- <span
11
- class="generated-answer__badge slds-var-p-around_x-small slds-text-title_bold"
12
- >{labels.generatedAnswerForYou}</span
13
- >
10
+ <div class="generated-answer__header-title slds-grid slds-grid_vertical-align-center">
11
+ <lightning-icon
12
+ icon-name="utility:sparkles"
13
+ size="x-small"
14
+ alternative-text={labels.generatedAnswerForYou}
15
+ class="slds-current-color slds-var-m-right_x-small"
16
+ ></lightning-icon>
17
+ <span class="slds-text-title_bold slds-p-vertical_x-small"
18
+ >{labels.generatedAnswerForYou}</span
19
+ >
20
+ </div>
14
21
  </div>
15
- <div>{labels.couldNotGenerateAnAnswer}</div>
16
- <div class="slds-var-m-top_small">
17
- <lightning-button
18
- data-testid="generated-answer__retry-button"
19
- variant="brand-outline"
20
- label={labels.tryAgain}
21
- title={labels.tryAgain}
22
- onclick={handleRetry}
23
- ></lightning-button>
22
+ <div class="slds-p-vertical_medium slds-p-horizontal_large">
23
+ <div>{labels.couldNotGenerateAnAnswer}</div>
24
+ <div class="slds-var-m-top_medium">
25
+ <lightning-button
26
+ data-testid="generated-answer__retry-button"
27
+ variant="brand-outline"
28
+ label={labels.tryAgain}
29
+ title={labels.tryAgain}
30
+ onclick={handleRetry}
31
+ ></lightning-button>
32
+ </div>
24
33
  </div>
25
34
  </div>
26
35
  </template>
@@ -0,0 +1,125 @@
1
+ /* eslint-disable jest/no-focused-tests */
2
+ // @ts-ignore
3
+ import {createElement} from 'lwc';
4
+ import QuanticGeneratedAnswerCopyToClipboard from '../quanticGeneratedAnswerCopyToClipboard';
5
+ import {copyToClipboard} from 'c/quanticUtils';
6
+
7
+ const defaultCopyLabel = 'Copy';
8
+ const defaultCopiedLabel = 'Copied';
9
+ const defaultSize = 'xx-small';
10
+
11
+ jest.mock(
12
+ '@salesforce/label/c.quantic_Copy',
13
+ () => ({default: defaultCopyLabel}),
14
+ {
15
+ virtual: true,
16
+ }
17
+ );
18
+
19
+ jest.mock(
20
+ '@salesforce/label/c.quantic_Copied',
21
+ () => ({default: defaultCopiedLabel}),
22
+ {
23
+ virtual: true,
24
+ }
25
+ );
26
+
27
+ jest.mock('c/quanticUtils', () => ({
28
+ copyToClipboard: jest.fn(() => Promise.resolve()),
29
+ }));
30
+
31
+ const selectors = {
32
+ copyButton: 'c-quantic-stateful-button',
33
+ };
34
+
35
+ const defaultOptions = {
36
+ answer: 'Example generated answer',
37
+ size: defaultSize,
38
+ };
39
+
40
+ function createTestComponent(options = defaultOptions) {
41
+ const element = createElement(
42
+ 'c-quantic-generated-answer-copy-to-clipboard',
43
+ {
44
+ is: QuanticGeneratedAnswerCopyToClipboard,
45
+ }
46
+ );
47
+
48
+ for (const [key, value] of Object.entries(options)) {
49
+ element[key] = value;
50
+ }
51
+
52
+ document.body.appendChild(element);
53
+ return element;
54
+ }
55
+
56
+ // Helper function to wait until the microtask queue is empty.
57
+ function flushPromises() {
58
+ // eslint-disable-next-line @lwc/lwc/no-async-operation
59
+ return new Promise((resolve) => setTimeout(resolve, 0));
60
+ }
61
+
62
+ describe('c-quantic-generated-answer-copy-to-clipboard', () => {
63
+ function cleanup() {
64
+ while (document.body.firstChild) {
65
+ document.body.removeChild(document.body.firstChild);
66
+ }
67
+ jest.clearAllMocks();
68
+ }
69
+
70
+ afterEach(() => {
71
+ cleanup();
72
+ });
73
+
74
+ it('should render the copy button with default size', async () => {
75
+ const element = createTestComponent();
76
+ await flushPromises();
77
+
78
+ const copyButton = element.shadowRoot.querySelector(selectors.copyButton);
79
+
80
+ expect(copyButton).not.toBeNull();
81
+ expect(copyButton.iconName).toBe('utility:copy');
82
+ expect(copyButton.iconSize).toBe(defaultSize);
83
+ expect(copyButton.tooltip).toBe(defaultCopyLabel);
84
+ expect(copyButton.selected).toBe(false);
85
+ expect(copyButton.withoutBorders).toBe(true);
86
+ });
87
+
88
+ it('should apply a custom valid size', async () => {
89
+ const customSize = 'large';
90
+ const element = createTestComponent({...defaultOptions, size: customSize});
91
+ await flushPromises();
92
+
93
+ const copyButton = element.shadowRoot.querySelector(selectors.copyButton);
94
+
95
+ expect(copyButton).not.toBeNull();
96
+ expect(copyButton.iconSize).toBe(customSize);
97
+ });
98
+
99
+ it('should ignore an invalid size and keep the default value', async () => {
100
+ const element = createTestComponent({
101
+ ...defaultOptions,
102
+ size: 'oliphant',
103
+ });
104
+ await flushPromises();
105
+
106
+ const copyButton = element.shadowRoot.querySelector(selectors.copyButton);
107
+
108
+ expect(copyButton).not.toBeNull();
109
+ expect(copyButton.iconSize).toBe(defaultSize);
110
+ });
111
+
112
+ it('should copy the answer and dispatch the #quantic__generatedanswercopy event', async () => {
113
+ const element = createTestComponent();
114
+ const handler = jest.fn();
115
+ element.addEventListener('quantic__generatedanswercopy', handler);
116
+ await flushPromises();
117
+
118
+ const copyButton = element.shadowRoot.querySelector(selectors.copyButton);
119
+ copyButton.dispatchEvent(new CustomEvent('quantic__select'));
120
+ await flushPromises();
121
+
122
+ expect(copyToClipboard).toHaveBeenCalledWith(defaultOptions.answer);
123
+ expect(handler).toHaveBeenCalledTimes(1);
124
+ });
125
+ });
@@ -6,6 +6,6 @@
6
6
  onquantic__select={handleCopyToClipboard}
7
7
  without-borders
8
8
  selected-state-color="#2e844a"
9
- icon-size="xx-small"
9
+ icon-size={size}
10
10
  ></c-quantic-stateful-button>
11
11
  </template>