@coveo/quantic 3.38.1 → 3.39.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.
- package/force-app/main/default/labels/CustomLabels.labels-meta.xml +14 -0
- package/force-app/main/default/lwc/quanticGeneratedAnswerFollowUpInput/__tests__/quanticGeneratedAnswerFollowUpInput.test.js +180 -0
- package/force-app/main/default/lwc/quanticGeneratedAnswerFollowUpInput/quanticGeneratedAnswerFollowUpInput.css +17 -0
- package/force-app/main/default/lwc/quanticGeneratedAnswerFollowUpInput/quanticGeneratedAnswerFollowUpInput.html +29 -0
- package/force-app/main/default/lwc/quanticGeneratedAnswerFollowUpInput/quanticGeneratedAnswerFollowUpInput.js +79 -0
- package/force-app/main/default/lwc/quanticGeneratedAnswerFollowUpInput/quanticGeneratedAnswerFollowUpInput.js-meta.xml +5 -0
- package/force-app/main/default/staticresources/coveoheadless/case-assist/headless.js +4 -4
- package/force-app/main/default/staticresources/coveoheadless/headless.js +5 -5
- package/force-app/main/default/staticresources/coveoheadless/insight/headless.js +4 -4
- package/force-app/main/default/staticresources/coveoheadless/recommendation/headless.js +14 -14
- package/package.json +4 -4
|
@@ -1624,4 +1624,18 @@
|
|
|
1624
1624
|
<protected>false</protected>
|
|
1625
1625
|
<shortDescription>No generated answer available.</shortDescription>
|
|
1626
1626
|
</labels>
|
|
1627
|
+
<labels>
|
|
1628
|
+
<fullName>quantic_AskFollowUp</fullName>
|
|
1629
|
+
<value>Ask follow-up</value>
|
|
1630
|
+
<language>en_US</language>
|
|
1631
|
+
<protected>false</protected>
|
|
1632
|
+
<shortDescription>Ask follow-up</shortDescription>
|
|
1633
|
+
</labels>
|
|
1634
|
+
<labels>
|
|
1635
|
+
<fullName>quantic_SubmitFollowUp</fullName>
|
|
1636
|
+
<value>Submit follow-up</value>
|
|
1637
|
+
<language>en_US</language>
|
|
1638
|
+
<protected>false</protected>
|
|
1639
|
+
<shortDescription>Submit follow-up</shortDescription>
|
|
1640
|
+
</labels>
|
|
1627
1641
|
</CustomLabels>
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import {buildCreateTestComponent, cleanup, flushPromises} from 'c/testUtils';
|
|
2
|
+
import QuanticGeneratedAnswerFollowUpInput from '../quanticGeneratedAnswerFollowUpInput';
|
|
3
|
+
|
|
4
|
+
jest.mock(
|
|
5
|
+
'@salesforce/label/c.quantic_SubmitFollowUp',
|
|
6
|
+
() => ({default: 'Submit follow-up'}),
|
|
7
|
+
{virtual: true}
|
|
8
|
+
);
|
|
9
|
+
jest.mock(
|
|
10
|
+
'@salesforce/label/c.quantic_AskFollowUp',
|
|
11
|
+
() => ({default: 'Ask follow-up'}),
|
|
12
|
+
{virtual: true}
|
|
13
|
+
);
|
|
14
|
+
jest.mock('c/quanticUtils', () => ({
|
|
15
|
+
keys: {ENTER: 'Enter'},
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
const selectors = {
|
|
19
|
+
input: 'lightning-input',
|
|
20
|
+
submitButton: 'lightning-button-icon',
|
|
21
|
+
inputContainer: '.follow-up-input__input-container',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const createTestComponent = buildCreateTestComponent(
|
|
25
|
+
QuanticGeneratedAnswerFollowUpInput,
|
|
26
|
+
'c-quantic-generated-answer-follow-up-input'
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
describe('c-quantic-generated-answer-follow-up-input', () => {
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
cleanup();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should render the input and submit button', async () => {
|
|
35
|
+
const element = createTestComponent();
|
|
36
|
+
await flushPromises();
|
|
37
|
+
|
|
38
|
+
const input = element.shadowRoot.querySelector(selectors.input);
|
|
39
|
+
const submitButton = element.shadowRoot.querySelector(
|
|
40
|
+
selectors.submitButton
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
expect(input).not.toBeNull();
|
|
44
|
+
expect(submitButton).not.toBeNull();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('submitting a follow up', () => {
|
|
48
|
+
it('should dispatch #quantic__submitfollowup when pressing Enter', async () => {
|
|
49
|
+
const element = createTestComponent();
|
|
50
|
+
await flushPromises();
|
|
51
|
+
|
|
52
|
+
const handler = jest.fn();
|
|
53
|
+
element.addEventListener('quantic__submitfollowup', handler);
|
|
54
|
+
|
|
55
|
+
const input = element.shadowRoot.querySelector(selectors.input);
|
|
56
|
+
input.value = 'follow up question';
|
|
57
|
+
input.dispatchEvent(new KeyboardEvent('keydown', {key: 'Enter'}));
|
|
58
|
+
|
|
59
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
60
|
+
expect(handler.mock.calls[0][0].detail.value).toBe('follow up question');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should dispatch #quantic__submitfollowup when clicking the submit button', async () => {
|
|
64
|
+
const element = createTestComponent();
|
|
65
|
+
await flushPromises();
|
|
66
|
+
|
|
67
|
+
const handler = jest.fn();
|
|
68
|
+
element.addEventListener('quantic__submitfollowup', handler);
|
|
69
|
+
|
|
70
|
+
const input = element.shadowRoot.querySelector(selectors.input);
|
|
71
|
+
input.value = 'another question';
|
|
72
|
+
|
|
73
|
+
const submitButton = element.shadowRoot.querySelector(
|
|
74
|
+
selectors.submitButton
|
|
75
|
+
);
|
|
76
|
+
submitButton.click();
|
|
77
|
+
|
|
78
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
79
|
+
expect(handler.mock.calls[0][0].detail.value).toBe('another question');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should clear the input and blur it after a successful submit', async () => {
|
|
83
|
+
const element = createTestComponent();
|
|
84
|
+
await flushPromises();
|
|
85
|
+
|
|
86
|
+
const input = element.shadowRoot.querySelector(selectors.input);
|
|
87
|
+
input.value = 'a question';
|
|
88
|
+
input.blur = jest.fn();
|
|
89
|
+
|
|
90
|
+
input.dispatchEvent(new KeyboardEvent('keydown', {key: 'Enter'}));
|
|
91
|
+
|
|
92
|
+
expect(input.value).toBe('');
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe('when submission should be blocked', () => {
|
|
97
|
+
it('should not dispatch the event when submitButtonDisabled is true', async () => {
|
|
98
|
+
const element = createTestComponent({submitButtonDisabled: true});
|
|
99
|
+
await flushPromises();
|
|
100
|
+
|
|
101
|
+
const handler = jest.fn();
|
|
102
|
+
element.addEventListener('quantic__submitfollowup', handler);
|
|
103
|
+
|
|
104
|
+
const input = element.shadowRoot.querySelector(selectors.input);
|
|
105
|
+
input.value = 'a question';
|
|
106
|
+
input.dispatchEvent(new KeyboardEvent('keydown', {key: 'Enter'}));
|
|
107
|
+
|
|
108
|
+
expect(handler).not.toHaveBeenCalled();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should not dispatch the event when input is whitespace only', async () => {
|
|
112
|
+
const element = createTestComponent();
|
|
113
|
+
await flushPromises();
|
|
114
|
+
|
|
115
|
+
const handler = jest.fn();
|
|
116
|
+
element.addEventListener('quantic__submitfollowup', handler);
|
|
117
|
+
|
|
118
|
+
const input = element.shadowRoot.querySelector(selectors.input);
|
|
119
|
+
input.value = ' ';
|
|
120
|
+
input.dispatchEvent(new KeyboardEvent('keydown', {key: 'Enter'}));
|
|
121
|
+
|
|
122
|
+
expect(handler).not.toHaveBeenCalled();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should not dispatch the event when input is empty', async () => {
|
|
126
|
+
const element = createTestComponent();
|
|
127
|
+
await flushPromises();
|
|
128
|
+
|
|
129
|
+
const handler = jest.fn();
|
|
130
|
+
element.addEventListener('quantic__submitfollowup', handler);
|
|
131
|
+
|
|
132
|
+
const input = element.shadowRoot.querySelector(selectors.input);
|
|
133
|
+
input.value = '';
|
|
134
|
+
|
|
135
|
+
const submitButton = element.shadowRoot.querySelector(
|
|
136
|
+
selectors.submitButton
|
|
137
|
+
);
|
|
138
|
+
submitButton.click();
|
|
139
|
+
|
|
140
|
+
expect(handler).not.toHaveBeenCalled();
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe('focus and blur CSS class toggling', () => {
|
|
145
|
+
it('should add the focused class when the input is focused', async () => {
|
|
146
|
+
const element = createTestComponent();
|
|
147
|
+
await flushPromises();
|
|
148
|
+
|
|
149
|
+
const input = element.shadowRoot.querySelector(selectors.input);
|
|
150
|
+
input.dispatchEvent(new CustomEvent('focus'));
|
|
151
|
+
await flushPromises();
|
|
152
|
+
|
|
153
|
+
const container = element.shadowRoot.querySelector(
|
|
154
|
+
selectors.inputContainer
|
|
155
|
+
);
|
|
156
|
+
expect(container.classList).toContain(
|
|
157
|
+
'follow-up-input__input-container--focused'
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('should remove the focused class when the input is blurred', async () => {
|
|
162
|
+
const element = createTestComponent();
|
|
163
|
+
await flushPromises();
|
|
164
|
+
|
|
165
|
+
const input = element.shadowRoot.querySelector(selectors.input);
|
|
166
|
+
input.dispatchEvent(new CustomEvent('focus'));
|
|
167
|
+
await flushPromises();
|
|
168
|
+
|
|
169
|
+
input.dispatchEvent(new CustomEvent('blur'));
|
|
170
|
+
await flushPromises();
|
|
171
|
+
|
|
172
|
+
const container = element.shadowRoot.querySelector(
|
|
173
|
+
selectors.inputContainer
|
|
174
|
+
);
|
|
175
|
+
expect(container.classList).not.toContain(
|
|
176
|
+
'follow-up-input__input-container--focused'
|
|
177
|
+
);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
--slds-c-input-shadow: none;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.follow-up-input__input {
|
|
6
|
+
border: none;
|
|
7
|
+
box-shadow: none;
|
|
8
|
+
outline: none;
|
|
9
|
+
width: 100%;
|
|
10
|
+
--slds-c-input-color-border: transparent;
|
|
11
|
+
--slds-c-input-shadow-focus: transparent;
|
|
12
|
+
--slds-c-input-spacing-border: 0px;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.follow-up-input__input-container.follow-up-input__input-container--focused {
|
|
16
|
+
border-color: var(--lwc-brandAccessible, #0176d3);
|
|
17
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class={inputContainerClasses}>
|
|
3
|
+
<lightning-input
|
|
4
|
+
class="slds-col slds-grow follow-up-input__input"
|
|
5
|
+
type="text"
|
|
6
|
+
variant="label-hidden"
|
|
7
|
+
label={labels.askFollowUp}
|
|
8
|
+
placeholder={labels.askFollowUp}
|
|
9
|
+
name="ask-followup"
|
|
10
|
+
lwc:ref="askFollowUpInput"
|
|
11
|
+
onfocus={handleFocus}
|
|
12
|
+
onblur={handleBlur}
|
|
13
|
+
onkeydown={handleKeyDown}
|
|
14
|
+
></lightning-input>
|
|
15
|
+
<lightning-button-icon
|
|
16
|
+
disabled={submitButtonDisabled}
|
|
17
|
+
onclick={handleSubmitFollowUp}
|
|
18
|
+
icon-name="utility:arrowup"
|
|
19
|
+
variant="brand"
|
|
20
|
+
class="slds-col slds-grow-none slds-align_absolute-center"
|
|
21
|
+
name="submit-follow-up"
|
|
22
|
+
title={labels.submitFollowUp}
|
|
23
|
+
aria-label={labels.submitFollowUp}
|
|
24
|
+
alternative-text={labels.submitFollowUp}
|
|
25
|
+
>
|
|
26
|
+
<label>{labels.submitFollowUp}</label>
|
|
27
|
+
</lightning-button-icon>
|
|
28
|
+
</div>
|
|
29
|
+
</template>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import submitFollowUp from '@salesforce/label/c.quantic_SubmitFollowUp';
|
|
2
|
+
import askFollowUp from '@salesforce/label/c.quantic_AskFollowUp';
|
|
3
|
+
import {keys} from 'c/quanticUtils';
|
|
4
|
+
import {api, LightningElement} from 'lwc';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The `QuanticGeneratedAnswerFollowUpInput` component allows users to submit follow-up questions related to a generated answer.
|
|
8
|
+
* @fires CustomEvent#quantic__submitfollowup
|
|
9
|
+
* @category Internal
|
|
10
|
+
* @example
|
|
11
|
+
* <c-quantic-generated-answer-follow-up-input></c-quantic-generated-answer-follow-up-input>
|
|
12
|
+
*/
|
|
13
|
+
export default class QuanticGeneratedAnswerFollowUpInput extends LightningElement {
|
|
14
|
+
labels = {
|
|
15
|
+
submitFollowUp,
|
|
16
|
+
askFollowUp,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Whether the submit button is disabled.
|
|
21
|
+
* @type {boolean}
|
|
22
|
+
* @defaultValue false
|
|
23
|
+
*/
|
|
24
|
+
@api
|
|
25
|
+
submitButtonDisabled = false;
|
|
26
|
+
|
|
27
|
+
/** @type {boolean} */
|
|
28
|
+
_focused = false;
|
|
29
|
+
|
|
30
|
+
handleKeyDown(event) {
|
|
31
|
+
// Let the browser commit IME text before handling shortcuts like Enter during composition.
|
|
32
|
+
if (event.isComposing || event.keyCode === 229) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (event.key === keys.ENTER) {
|
|
37
|
+
event.preventDefault();
|
|
38
|
+
this.handleSubmitFollowUp();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
handleSubmitFollowUp() {
|
|
43
|
+
if (
|
|
44
|
+
this.submitButtonDisabled ||
|
|
45
|
+
this.refs.askFollowUpInput.value.trim() === ''
|
|
46
|
+
) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
this.sendSubmitFollowUpEvent();
|
|
50
|
+
this.refs.askFollowUpInput.value = '';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
handleFocus() {
|
|
54
|
+
this._focused = true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
handleBlur() {
|
|
58
|
+
this._focused = false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Sends the "quantic__submitfollowup" event.
|
|
63
|
+
*/
|
|
64
|
+
sendSubmitFollowUpEvent() {
|
|
65
|
+
this.dispatchEvent(
|
|
66
|
+
new CustomEvent('quantic__submitfollowup', {
|
|
67
|
+
bubbles: true,
|
|
68
|
+
composed: true,
|
|
69
|
+
detail: {
|
|
70
|
+
value: this.refs.askFollowUpInput.value,
|
|
71
|
+
},
|
|
72
|
+
})
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
get inputContainerClasses() {
|
|
77
|
+
return `follow-up-input__input-container slds-box slds-grid slds-box_xx-small ${this._focused ? 'follow-up-input__input-container--focused' : ''}`;
|
|
78
|
+
}
|
|
79
|
+
}
|