@coveo/quantic 3.34.2 → 3.35.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/README.md +4 -4
- package/force-app/main/default/lwc/quanticCitation/__tests__/quanticCitation.test.js +15 -75
- package/force-app/main/default/lwc/quanticCitation/quanticCitation.js +73 -39
- package/force-app/main/default/lwc/quanticTabBar/quanticTabBar.css +9 -0
- package/force-app/main/default/lwc/quanticTabBar/quanticTabBar.html +1 -1
- package/force-app/main/default/staticresources/coveoheadless/case-assist/headless.js +3 -3
- package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/product-enrichment/headless-product-enrichment.d.ts +8 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/commerce/product-enrichment/product-enrichment-actions-loader.d.ts +9 -2
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/commerce/product-enrichment/product-enrichment-actions.d.ts +11 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/commerce/product-enrichment/product-enrichment-state.d.ts +8 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/insight.index.d.ts +1 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/ssr/commerce/controllers/product-enrichment/headless-product-enrichment.ssr.d.ts +13 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/ssr/commerce/types/controller-definitions.d.ts +2 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/ssr/commerce/types/controller-scopes.d.ts +6 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/ssr/commerce/types/kind.d.ts +2 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/ssr-commerce-next.index.d.ts +3 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/ssr-commerce.index.d.ts +3 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/ssr-next/commerce/controllers/product-enrichment/headless-product-enrichment.ssr.d.ts +13 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/ssr-next/commerce/types/controller-definitions.d.ts +2 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/ssr-next/commerce/types/controller-scopes.d.ts +6 -0
- package/force-app/main/default/staticresources/coveoheadless/headless.js +7 -7
- package/force-app/main/default/staticresources/coveoheadless/insight/headless.js +9 -9
- package/force-app/main/default/staticresources/coveoheadless/recommendation/headless.js +7 -7
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -110,15 +110,15 @@ https://your-salesforce-lws-disabled-scratch-org-instance.force.com/examples
|
|
|
110
110
|
Once the community has been deployed, you can deploy the `main` or `example` components to a specific org only when needed by running the corresponding commands:
|
|
111
111
|
|
|
112
112
|
```bash
|
|
113
|
-
pnpm run deploy:main
|
|
114
|
-
pnpm run deploy:examples
|
|
113
|
+
pnpm run deploy:main Quantic__LWS_enabled
|
|
114
|
+
pnpm run deploy:examples Quantic__LWS_enabled
|
|
115
115
|
```
|
|
116
116
|
|
|
117
117
|
You can replace Quantic\_\_LWS_enabled with your target org alias. For example:
|
|
118
118
|
|
|
119
119
|
```bash
|
|
120
|
-
pnpm run deploy:main
|
|
121
|
-
pnpm run deploy:examples
|
|
120
|
+
pnpm run deploy:main MyCustomOrg
|
|
121
|
+
pnpm run deploy:examples MyCustomOrg
|
|
122
122
|
```
|
|
123
123
|
|
|
124
124
|
### Run Playwright for Quantic Components
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
// @ts-ignore
|
|
6
6
|
import {createElement} from 'lwc';
|
|
7
7
|
import QuanticCitation from '../quanticCitation';
|
|
8
|
-
import {debounce} from '../quanticCitation';
|
|
9
8
|
|
|
10
9
|
const functionsMocks = {
|
|
11
10
|
eventHandler: jest.fn((event) => event),
|
|
@@ -113,67 +112,6 @@ describe('c-quantic-citation', () => {
|
|
|
113
112
|
cleanup();
|
|
114
113
|
});
|
|
115
114
|
|
|
116
|
-
describe('debounce function', () => {
|
|
117
|
-
let mockFnToDebounce;
|
|
118
|
-
const DEBOUNCE_DELAY = 200;
|
|
119
|
-
|
|
120
|
-
beforeEach(() => {
|
|
121
|
-
jest.useFakeTimers();
|
|
122
|
-
mockFnToDebounce = jest.fn();
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
afterEach(() => {
|
|
126
|
-
jest.useRealTimers();
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
// Ensures that rapid calls result in only one execution
|
|
130
|
-
test('should execute once after the delay and use the arguments from the last call', () => {
|
|
131
|
-
const debouncedFn = debounce(mockFnToDebounce, DEBOUNCE_DELAY);
|
|
132
|
-
|
|
133
|
-
debouncedFn('first');
|
|
134
|
-
expect(mockFnToDebounce).not.toHaveBeenCalled();
|
|
135
|
-
|
|
136
|
-
jest.advanceTimersByTime(DEBOUNCE_DELAY - 100);
|
|
137
|
-
debouncedFn('middle');
|
|
138
|
-
|
|
139
|
-
jest.advanceTimersByTime(DEBOUNCE_DELAY - 100);
|
|
140
|
-
debouncedFn('last');
|
|
141
|
-
|
|
142
|
-
expect(mockFnToDebounce).not.toHaveBeenCalled();
|
|
143
|
-
jest.advanceTimersByTime(DEBOUNCE_DELAY);
|
|
144
|
-
|
|
145
|
-
expect(mockFnToDebounce).toHaveBeenCalledTimes(1);
|
|
146
|
-
expect(mockFnToDebounce).toHaveBeenCalledWith('last');
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
// Ensures calls separated by more than the delay execute independently.
|
|
150
|
-
test('should execute multiple times if calls are spaced outside the delay', () => {
|
|
151
|
-
const debouncedFn = debounce(mockFnToDebounce, DEBOUNCE_DELAY);
|
|
152
|
-
|
|
153
|
-
debouncedFn(100);
|
|
154
|
-
jest.advanceTimersByTime(DEBOUNCE_DELAY);
|
|
155
|
-
expect(mockFnToDebounce).toHaveBeenCalledTimes(1);
|
|
156
|
-
expect(mockFnToDebounce).toHaveBeenCalledWith(100);
|
|
157
|
-
|
|
158
|
-
debouncedFn(200, {data: 'second'});
|
|
159
|
-
jest.advanceTimersByTime(DEBOUNCE_DELAY);
|
|
160
|
-
expect(mockFnToDebounce).toHaveBeenCalledTimes(2);
|
|
161
|
-
expect(mockFnToDebounce).toHaveBeenCalledWith(200, {data: 'second'});
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
// Ensures that the execution can be canceled before the delay expires.
|
|
165
|
-
test('should not execute the function if cancel is called before the delay', () => {
|
|
166
|
-
const debouncedFn = debounce(mockFnToDebounce, DEBOUNCE_DELAY);
|
|
167
|
-
|
|
168
|
-
debouncedFn('should not run');
|
|
169
|
-
jest.advanceTimersByTime(DEBOUNCE_DELAY / 2);
|
|
170
|
-
debouncedFn.cancel();
|
|
171
|
-
|
|
172
|
-
jest.advanceTimersByTime(DEBOUNCE_DELAY * 2);
|
|
173
|
-
expect(mockFnToDebounce).not.toHaveBeenCalled();
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
|
|
177
115
|
it('should properly display the citation', async () => {
|
|
178
116
|
const element = createTestComponent();
|
|
179
117
|
await flushPromises();
|
|
@@ -222,7 +160,7 @@ describe('c-quantic-citation', () => {
|
|
|
222
160
|
jest.useRealTimers();
|
|
223
161
|
});
|
|
224
162
|
|
|
225
|
-
it('should display the citation tooltip', async () => {
|
|
163
|
+
it('should display the citation tooltip after 200ms delay', async () => {
|
|
226
164
|
const element = createTestComponent();
|
|
227
165
|
await flushPromises();
|
|
228
166
|
|
|
@@ -236,18 +174,17 @@ describe('c-quantic-citation', () => {
|
|
|
236
174
|
expect(citationLink).not.toBeNull();
|
|
237
175
|
expect(citationTooltip).not.toBeNull();
|
|
238
176
|
|
|
239
|
-
// Spy on the tooltip's showTooltip method to verify it gets called
|
|
240
177
|
const showTooltipSpy = jest.spyOn(citationTooltip, 'showTooltip');
|
|
241
178
|
|
|
242
179
|
await citationLink.dispatchEvent(
|
|
243
180
|
new CustomEvent('mouseenter', {bubbles: true})
|
|
244
181
|
);
|
|
182
|
+
expect(showTooltipSpy).toHaveBeenCalledTimes(0);
|
|
245
183
|
jest.advanceTimersByTime(200);
|
|
246
|
-
|
|
247
184
|
expect(showTooltipSpy).toHaveBeenCalledTimes(1);
|
|
248
185
|
});
|
|
249
186
|
|
|
250
|
-
it('should dispatch a citation hover event after hovering over the the citation for more than 1200ms, 200ms
|
|
187
|
+
it('should dispatch a citation hover event after hovering over the the citation for more than 1200ms, 200ms delay before hover + 1000ms minimum hover duration', async () => {
|
|
251
188
|
const element = createTestComponent();
|
|
252
189
|
await flushPromises();
|
|
253
190
|
setupEventDispatchTest('quantic__citationhover');
|
|
@@ -260,16 +197,19 @@ describe('c-quantic-citation', () => {
|
|
|
260
197
|
await citationLink.dispatchEvent(
|
|
261
198
|
new CustomEvent('mouseenter', {bubbles: true})
|
|
262
199
|
);
|
|
200
|
+
// Wait for show delay - this triggers the tooltip to show and sets hoverStartTimestamp
|
|
201
|
+
jest.advanceTimersByTime(200);
|
|
202
|
+
// Now wait for minimum hover time
|
|
263
203
|
jest.advanceTimersByTime(1000);
|
|
264
204
|
await citationLink.dispatchEvent(
|
|
265
205
|
new CustomEvent('mouseleave', {bubbles: true})
|
|
266
206
|
);
|
|
267
|
-
// Additional 200ms delay for the
|
|
207
|
+
// Additional 200ms delay for the hide tooltip logic
|
|
268
208
|
jest.advanceTimersByTime(200);
|
|
269
209
|
|
|
270
210
|
expect(functionsMocks.eventHandler).toHaveBeenCalledTimes(1);
|
|
271
211
|
expect(functionsMocks.eventHandler).toHaveBeenCalledWith({
|
|
272
|
-
citationHoverTimeMs:
|
|
212
|
+
citationHoverTimeMs: 1100,
|
|
273
213
|
});
|
|
274
214
|
});
|
|
275
215
|
|
|
@@ -313,32 +253,32 @@ describe('c-quantic-citation', () => {
|
|
|
313
253
|
|
|
314
254
|
const hideTooltipSpy = jest.spyOn(citationTooltip, 'hideTooltip');
|
|
315
255
|
|
|
316
|
-
// Hover over the citation
|
|
256
|
+
// Hover over the citation - tooltip shows after 200ms delay
|
|
317
257
|
await citationLink.dispatchEvent(
|
|
318
258
|
new CustomEvent('mouseenter', {bubbles: true})
|
|
319
259
|
);
|
|
320
260
|
jest.advanceTimersByTime(200);
|
|
321
261
|
|
|
322
|
-
// Move cursor to tooltip
|
|
262
|
+
// Move cursor to tooltip quickly (before hide delay expires)
|
|
323
263
|
await citationLink.dispatchEvent(
|
|
324
264
|
new CustomEvent('mouseleave', {bubbles: true})
|
|
325
265
|
);
|
|
326
|
-
|
|
266
|
+
|
|
327
267
|
await citationTooltip.dispatchEvent(
|
|
328
268
|
new CustomEvent('mouseenter', {bubbles: true})
|
|
329
269
|
);
|
|
330
270
|
|
|
331
|
-
// Move cursor back to citation
|
|
271
|
+
// Move cursor back to citation quickly (before hide delay expires)
|
|
332
272
|
await citationTooltip.dispatchEvent(
|
|
333
273
|
new CustomEvent('mouseleave', {bubbles: true})
|
|
334
274
|
);
|
|
335
|
-
|
|
275
|
+
|
|
336
276
|
await citationLink.dispatchEvent(
|
|
337
277
|
new CustomEvent('mouseenter', {bubbles: true})
|
|
338
278
|
);
|
|
339
279
|
|
|
340
|
-
// Advance time beyond
|
|
341
|
-
jest.advanceTimersByTime(
|
|
280
|
+
// Advance time beyond 100ms to see if tooltip is hidden
|
|
281
|
+
jest.advanceTimersByTime(200);
|
|
342
282
|
|
|
343
283
|
expect(hideTooltipSpy).toHaveBeenCalledTimes(0);
|
|
344
284
|
});
|
|
@@ -5,28 +5,10 @@ import {LightningElement, api} from 'lwc';
|
|
|
5
5
|
/** @typedef {import("coveo").InteractiveCitation} InteractiveCitation */
|
|
6
6
|
|
|
7
7
|
const minimumTooltipDisplayDurationMs = 1000;
|
|
8
|
-
const
|
|
8
|
+
const tooltipDelayMsShow = 200;
|
|
9
|
+
const tooltipDelayMsHide = 100;
|
|
9
10
|
const supportedFileTypesForTextFragment = ['html', 'SalesforceItem'];
|
|
10
11
|
|
|
11
|
-
/**
|
|
12
|
-
* Debounce function that delays invoking func until after wait milliseconds
|
|
13
|
-
* have elapsed since the last time the debounced function was invoked.
|
|
14
|
-
* Includes a cancel method to clear any pending timeout.
|
|
15
|
-
* @param {Function} fn - The function to debounce
|
|
16
|
-
* @param {number} delay - The number of milliseconds to delay
|
|
17
|
-
* @returns {Function} The debounced function with cancel method
|
|
18
|
-
*/
|
|
19
|
-
export function debounce(fn, delay) {
|
|
20
|
-
let timeout;
|
|
21
|
-
const debounced = (...args) => {
|
|
22
|
-
clearTimeout(timeout);
|
|
23
|
-
// eslint-disable-next-line @lwc/lwc/no-async-operation
|
|
24
|
-
timeout = setTimeout(() => fn(...args), delay);
|
|
25
|
-
};
|
|
26
|
-
debounced.cancel = () => clearTimeout(timeout);
|
|
27
|
-
return debounced;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
12
|
/**
|
|
31
13
|
* The `QuanticCitation` component renders an individual citation.
|
|
32
14
|
* @fires CustomEvent#quantic__citationhover
|
|
@@ -69,8 +51,14 @@ export default class QuanticCitation extends NavigationMixin(LightningElement) {
|
|
|
69
51
|
salesforceRecordUrl;
|
|
70
52
|
/** @type {boolean} */
|
|
71
53
|
isHrefWithTextFragment = false;
|
|
54
|
+
/** @type {boolean} */
|
|
55
|
+
isCitationHovered = false;
|
|
56
|
+
/** @type {boolean} */
|
|
57
|
+
isTooltipHovered = false;
|
|
72
58
|
/** @type {Object} */
|
|
73
|
-
|
|
59
|
+
showTimer = null;
|
|
60
|
+
/** @type {Object} */
|
|
61
|
+
hideTimer = null;
|
|
74
62
|
|
|
75
63
|
connectedCallback() {
|
|
76
64
|
const fileType = this.citation?.fields?.filetype;
|
|
@@ -78,15 +66,6 @@ export default class QuanticCitation extends NavigationMixin(LightningElement) {
|
|
|
78
66
|
!this.disableCitationAnchoring &&
|
|
79
67
|
supportedFileTypesForTextFragment.includes(fileType) &&
|
|
80
68
|
!!this.text;
|
|
81
|
-
|
|
82
|
-
// Initialize the debounced hide tooltip function
|
|
83
|
-
this.hideTooltipDebounced = debounce(() => {
|
|
84
|
-
if (this.tooltipIsDisplayed) {
|
|
85
|
-
this.dispatchCitationHoverEvent();
|
|
86
|
-
}
|
|
87
|
-
this.tooltipIsDisplayed = false;
|
|
88
|
-
this.tooltipComponent?.hideTooltip();
|
|
89
|
-
}, tooltipHideDelayMs);
|
|
90
69
|
}
|
|
91
70
|
|
|
92
71
|
renderedCallback() {
|
|
@@ -111,30 +90,77 @@ export default class QuanticCitation extends NavigationMixin(LightningElement) {
|
|
|
111
90
|
disconnectedCallback() {
|
|
112
91
|
this.removeBindings?.();
|
|
113
92
|
clearTimeout(this.timeout);
|
|
114
|
-
this.
|
|
93
|
+
this.cancelShow();
|
|
94
|
+
this.cancelHide();
|
|
115
95
|
}
|
|
116
96
|
|
|
117
97
|
handleCitationMouseEnter() {
|
|
118
|
-
this.
|
|
98
|
+
this.isCitationHovered = true;
|
|
99
|
+
this.updateTooltipHideShow();
|
|
119
100
|
}
|
|
120
101
|
|
|
121
102
|
handleCitationMouseLeave() {
|
|
122
|
-
this.
|
|
103
|
+
this.isCitationHovered = false;
|
|
104
|
+
this.updateTooltipHideShow();
|
|
123
105
|
}
|
|
124
106
|
|
|
125
107
|
handleTooltipMouseEnter() {
|
|
126
|
-
this.
|
|
108
|
+
this.isTooltipHovered = true;
|
|
109
|
+
this.updateTooltipHideShow();
|
|
127
110
|
}
|
|
128
111
|
|
|
129
112
|
handleTooltipMouseLeave() {
|
|
130
|
-
this.
|
|
113
|
+
this.isTooltipHovered = false;
|
|
114
|
+
this.updateTooltipHideShow();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
isHovering() {
|
|
118
|
+
return this.isCitationHovered || this.isTooltipHovered;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
updateTooltipHideShow() {
|
|
122
|
+
if (this.isHovering()) {
|
|
123
|
+
this.cancelHide();
|
|
124
|
+
this.scheduleShow();
|
|
125
|
+
} else {
|
|
126
|
+
this.cancelShow();
|
|
127
|
+
this.scheduleHide();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
scheduleShow() {
|
|
132
|
+
if (this.showTimer !== null) return;
|
|
133
|
+
|
|
134
|
+
// eslint-disable-next-line @lwc/lwc/no-async-operation
|
|
135
|
+
this.showTimer = setTimeout(() => {
|
|
136
|
+
this.showTimer = null;
|
|
137
|
+
if (this.isHovering()) this.showTooltip();
|
|
138
|
+
}, tooltipDelayMsShow);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
scheduleHide() {
|
|
142
|
+
if (this.hideTimer !== null) return;
|
|
143
|
+
|
|
144
|
+
// eslint-disable-next-line @lwc/lwc/no-async-operation
|
|
145
|
+
this.hideTimer = setTimeout(() => {
|
|
146
|
+
this.hideTimer = null;
|
|
147
|
+
if (!this.isHovering()) this.hideTooltip();
|
|
148
|
+
}, tooltipDelayMsHide);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
cancelShow() {
|
|
152
|
+
if (this.showTimer === null) return;
|
|
153
|
+
clearTimeout(this.showTimer);
|
|
154
|
+
this.showTimer = null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
cancelHide() {
|
|
158
|
+
if (this.hideTimer === null) return;
|
|
159
|
+
clearTimeout(this.hideTimer);
|
|
160
|
+
this.hideTimer = null;
|
|
131
161
|
}
|
|
132
162
|
|
|
133
|
-
/**
|
|
134
|
-
* Shows the tooltip immediately and cancels any pending hide.
|
|
135
|
-
*/
|
|
136
163
|
showTooltip() {
|
|
137
|
-
this.hideTooltipDebounced.cancel();
|
|
138
164
|
if (!this.tooltipIsDisplayed) {
|
|
139
165
|
this.hoverStartTimestamp = Date.now();
|
|
140
166
|
this.tooltipIsDisplayed = true;
|
|
@@ -142,6 +168,14 @@ export default class QuanticCitation extends NavigationMixin(LightningElement) {
|
|
|
142
168
|
}
|
|
143
169
|
}
|
|
144
170
|
|
|
171
|
+
hideTooltip() {
|
|
172
|
+
if (this.tooltipIsDisplayed) {
|
|
173
|
+
this.dispatchCitationHoverEvent();
|
|
174
|
+
this.tooltipIsDisplayed = false;
|
|
175
|
+
this.tooltipComponent?.hideTooltip();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
145
179
|
/**
|
|
146
180
|
* Dispatches the citation hover analytics event if minimum display duration was met.
|
|
147
181
|
*/
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
.tab-bar_list-container {
|
|
2
|
+
/* Prevents horizontal scrollbar from appearing when tabs overflow */
|
|
3
|
+
overflow-x: clip;
|
|
4
|
+
}
|
|
5
|
+
|
|
1
6
|
.tab-bar_dropdown {
|
|
2
7
|
max-width: 150px;
|
|
3
8
|
}
|
|
@@ -5,3 +10,7 @@
|
|
|
5
10
|
.tab-bar__more-button:focus {
|
|
6
11
|
text-decoration: none;
|
|
7
12
|
}
|
|
13
|
+
|
|
14
|
+
.tab-bar__dropdown-tab {
|
|
15
|
+
display: inline;
|
|
16
|
+
}
|