@coveo/quantic 3.10.0-pre.faaf12d7a7 → 3.11.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/README.md +52 -12
- package/force-app/main/default/labels/CustomLabels.labels-meta.xml +7 -0
- package/force-app/main/default/lwc/quanticCaseAssistInterface/quanticCaseAssistInterface.js +8 -0
- package/force-app/main/default/lwc/quanticCategoryFacet/__tests__/quanticCategoryFacet.test.js +108 -1
- package/force-app/main/default/lwc/quanticCategoryFacet/quanticCategoryFacet.js +22 -0
- package/force-app/main/default/lwc/quanticDateFacet/__tests__/quanticDateFacet.test.js +107 -1
- package/force-app/main/default/lwc/quanticDateFacet/quanticDateFacet.js +22 -0
- package/force-app/main/default/lwc/quanticFacet/__tests__/quanticFacet.test.js +106 -1
- package/force-app/main/default/lwc/quanticFacet/quanticFacet.js +21 -1
- package/force-app/main/default/lwc/quanticFeedbackModalQna/__tests__/quanticFeedbackModalQna.test.js +1 -1
- package/force-app/main/default/lwc/quanticFeedbackModalQna/quanticFeedbackModalQna.js +1 -1
- package/force-app/main/default/lwc/quanticGeneratedAnswer/templates/generatedAnswer.css +0 -1
- package/force-app/main/default/lwc/quanticGeneratedAnswerContent/templates/generatedMarkdownContent.css +1 -0
- package/force-app/main/default/lwc/quanticInsightInterface/quanticInsightInterface.js +8 -0
- package/force-app/main/default/lwc/quanticNotifications/__tests__/quanticNotifications.test.js +112 -4
- package/force-app/main/default/lwc/quanticNotifications/quanticNotifications.css +8 -0
- package/force-app/main/default/lwc/quanticNotifications/quanticNotifications.html +20 -7
- package/force-app/main/default/lwc/quanticNotifications/quanticNotifications.js +57 -18
- package/force-app/main/default/lwc/quanticNumericFacet/__tests__/quanticNumericFacet.test.js +107 -1
- package/force-app/main/default/lwc/quanticNumericFacet/quanticNumericFacet.js +22 -0
- package/force-app/main/default/lwc/quanticRecommendationInterface/quanticRecommendationInterface.js +8 -0
- package/force-app/main/default/lwc/quanticSearchInterface/quanticSearchInterface.js +8 -0
- package/force-app/main/default/lwc/quanticTimeframeFacet/__tests__/quanticTimeframeFacet.test.js +107 -1
- package/force-app/main/default/lwc/quanticTimeframeFacet/quanticTimeframeFacet.js +22 -0
- package/force-app/main/default/lwc/quanticUserActionsToggle/__tests__/quanticUserActionsToggle.test.js +18 -0
- package/force-app/main/default/lwc/quanticUserActionsToggle/e2e/data.ts +32 -0
- package/force-app/main/default/lwc/quanticUserActionsToggle/e2e/fixture.ts +32 -0
- package/force-app/main/default/lwc/quanticUserActionsToggle/e2e/pageObject.ts +71 -0
- package/force-app/main/default/lwc/quanticUserActionsToggle/e2e/quanticUserActionsToggle.e2e.ts +39 -0
- package/force-app/main/default/staticresources/coveobueno/browser/bueno.js +3 -18
- package/force-app/main/default/staticresources/coveoheadless/case-assist/headless.js +9 -6
- package/force-app/main/default/staticresources/coveoheadless/definitions/api/commerce/common/request.d.ts +1 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/api/commerce/recommendations/recommendations-request.d.ts +1 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/api/commerce/search/query-suggest/query-suggest-request.d.ts +1 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/api/knowledge/stream-answer-api.d.ts +1 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/api/service/case-assist/get-case-classifications/get-case-classifications-request.d.ts +1 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/api/service/case-assist/get-document-suggestions/get-document-suggestions-request.d.ts +1 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/api/service/insight/get-interface/get-interface-config-request.d.ts +1 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/api/service/insight/query/query-request.d.ts +2 -2
- package/force-app/main/default/staticresources/coveoheadless/definitions/api/service/insight/user-actions/user-actions-request.d.ts +1 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/commerce-engine/commerce-engine.ssr.d.ts +4 -13
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/commerce-ssr-engine/common.d.ts +8 -3
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/commerce-ssr-engine/factories/build-factory.d.ts +19 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/commerce-ssr-engine/factories/hydrated-state-factory.d.ts +4 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/commerce-ssr-engine/factories/recommendation-hydrated-state-factory.d.ts +3 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/commerce-ssr-engine/factories/recommendation-static-state-factory.d.ts +3 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/commerce-ssr-engine/factories/static-state-factory.d.ts +4 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/commerce-ssr-engine/types/build.d.ts +13 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/commerce-ssr-engine/types/common.d.ts +180 -5
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/commerce-ssr-engine/types/core-engine.d.ts +29 -10
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/commerce-ssr-engine/types/fetch-static-state.d.ts +15 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/commerce-ssr-engine/types/from-build-result.d.ts +20 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/commerce-ssr-engine/types/hydrate-static-state.d.ts +18 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/logger.d.ts +1 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/navigatorContextProvider.d.ts +17 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/ssr-engine/types/common.d.ts +6 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/ssr-engine/types/from-build-result.d.ts +1 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/core/parameter-manager/headless-core-parameter-manager.d.ts +1 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/field-suggestions/headless-field-suggestions-generator.ssr.d.ts +2 -2
- package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/instant-products/headless-instant-products.ssr.d.ts +3 -3
- package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/product-view/headless-product-view.ssr.d.ts +2 -2
- package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/recent-queries-list/headless-recent-queries-list.ssr.d.ts +2 -2
- package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/recommendations/headless-recommendations.ssr.d.ts +10 -4
- package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/standalone-search-box/headless-standalone-search-box.ssr.d.ts +2 -2
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/analytics/search-action-cause.d.ts +0 -82
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/facets/facet-set/facet-set-analytics-actions.d.ts +0 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/history/history-analytics-actions.d.ts +0 -3
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/search/legacy/search-request.d.ts +1 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/search/search-actions.d.ts +2 -2
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/search/search-request.d.ts +1 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/static-filter-set/static-filter-set-actions.d.ts +0 -4
- package/force-app/main/default/staticresources/coveoheadless/definitions/ssr-commerce.index.d.ts +4 -4
- package/force-app/main/default/staticresources/coveoheadless/definitions/test/mock-engine-v2.d.ts +1 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/utils/facet-utils.d.ts +1 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/utils/utils.d.ts +4 -0
- package/force-app/main/default/staticresources/coveoheadless/headless.js +11 -8
- package/force-app/main/default/staticresources/coveoheadless/insight/headless.js +10 -7
- package/force-app/main/default/staticresources/coveoheadless/recommendation/headless.js +9 -6
- package/force-app/main/default/staticresources/dompurify/purify.min.js +1339 -2
- package/package.json +23 -19
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
initializeWithHeadless,
|
|
16
16
|
registerToStore,
|
|
17
17
|
getHeadlessBundle,
|
|
18
|
+
getBueno,
|
|
18
19
|
} from 'c/quanticHeadlessLoader';
|
|
19
20
|
import {
|
|
20
21
|
I18nUtils,
|
|
@@ -177,7 +178,6 @@ export default class QuanticFacet extends LightningElement {
|
|
|
177
178
|
* @type {DependsOn}
|
|
178
179
|
*/
|
|
179
180
|
@api dependsOn;
|
|
180
|
-
|
|
181
181
|
/**
|
|
182
182
|
* Whether the facet is collapsed.
|
|
183
183
|
* @api
|
|
@@ -315,6 +315,7 @@ export default class QuanticFacet extends LightningElement {
|
|
|
315
315
|
element: this.template.host,
|
|
316
316
|
});
|
|
317
317
|
if (this.dependsOn) {
|
|
318
|
+
this.validateDependsOnProperty();
|
|
318
319
|
this.initFacetConditionManager(engine);
|
|
319
320
|
}
|
|
320
321
|
};
|
|
@@ -342,6 +343,25 @@ export default class QuanticFacet extends LightningElement {
|
|
|
342
343
|
});
|
|
343
344
|
this.dispatchEvent(renderFacetEvent);
|
|
344
345
|
}
|
|
346
|
+
validateDependsOnProperty() {
|
|
347
|
+
if (this.dependsOn) {
|
|
348
|
+
getBueno(this).then(() => {
|
|
349
|
+
const {parentFacetId, expectedValue} = this.dependsOn;
|
|
350
|
+
if (!Bueno.isString(parentFacetId)) {
|
|
351
|
+
console.error(
|
|
352
|
+
`The ${this.field} ${this.template.host.localName} requires dependsOn.parentFacetId to be a valid string.`
|
|
353
|
+
);
|
|
354
|
+
this.setInitializationError();
|
|
355
|
+
}
|
|
356
|
+
if (expectedValue && !Bueno.isString(expectedValue)) {
|
|
357
|
+
console.error(
|
|
358
|
+
`The ${this.field} ${this.template.host.localName} requires dependsOn.expectedValue to be a valid string.`
|
|
359
|
+
);
|
|
360
|
+
this.setInitializationError();
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
}
|
|
345
365
|
|
|
346
366
|
initFacetConditionManager(engine) {
|
|
347
367
|
this.facetConditionsManager = this.headless.buildFacetConditionsManager(
|
package/force-app/main/default/lwc/quanticFeedbackModalQna/__tests__/quanticFeedbackModalQna.test.js
CHANGED
|
@@ -193,7 +193,7 @@ describe('c-quantic-feedback-modal-qna', () => {
|
|
|
193
193
|
|
|
194
194
|
expect(functionsMocks.handleSubmit).toHaveBeenCalledWith(
|
|
195
195
|
expect.objectContaining({
|
|
196
|
-
|
|
196
|
+
correctTopic: 'yes',
|
|
197
197
|
hallucinationFree: 'yes',
|
|
198
198
|
documented: 'yes',
|
|
199
199
|
readable: 'yes',
|
|
@@ -168,7 +168,7 @@ export default class QuanticFeedbackModalQna extends LightningModal {
|
|
|
168
168
|
return;
|
|
169
169
|
}
|
|
170
170
|
this.handleSubmit({
|
|
171
|
-
|
|
171
|
+
correctTopic: this.correctTopicValue,
|
|
172
172
|
hallucinationFree: this.hallucinationFreeValue,
|
|
173
173
|
documented: this.documentedValue,
|
|
174
174
|
readable: this.readableValue,
|
|
@@ -80,6 +80,14 @@ export default class QuanticInsightInterface extends LightningElement {
|
|
|
80
80
|
...(document.referrer && {
|
|
81
81
|
originLevel3: document.referrer.substring(0, 256),
|
|
82
82
|
}),
|
|
83
|
+
analyticsClientMiddleware: (_event, payload) => {
|
|
84
|
+
if (!payload.customData) {
|
|
85
|
+
payload.customData = {};
|
|
86
|
+
}
|
|
87
|
+
payload.customData.coveoQuanticVersion =
|
|
88
|
+
window.coveoQuanticVersion;
|
|
89
|
+
return payload;
|
|
90
|
+
},
|
|
83
91
|
},
|
|
84
92
|
...rest,
|
|
85
93
|
},
|
package/force-app/main/default/lwc/quanticNotifications/__tests__/quanticNotifications.test.js
CHANGED
|
@@ -20,17 +20,30 @@ const exampleEngine = {
|
|
|
20
20
|
id: 'mock engine',
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
+
const mockSearchStatusState = {
|
|
24
|
+
hasResults: true,
|
|
25
|
+
hasError: false,
|
|
26
|
+
};
|
|
27
|
+
|
|
23
28
|
const functionsMocks = {
|
|
24
29
|
buildNotifyTrigger: jest.fn(() => ({
|
|
25
30
|
state: notificationsState,
|
|
26
31
|
subscribe: functionsMocks.subscribe,
|
|
27
32
|
})),
|
|
28
33
|
dispatchMessage: jest.fn(() => {}),
|
|
29
|
-
|
|
30
|
-
|
|
34
|
+
buildSearchStatus: jest.fn(() => ({
|
|
35
|
+
state: mockSearchStatusState,
|
|
36
|
+
subscribe: jest.fn((callback) => {
|
|
37
|
+
functionsMocks.subscribe.callback = callback;
|
|
38
|
+
return functionsMocks.unsubscribeSearchStatus;
|
|
39
|
+
}),
|
|
40
|
+
})),
|
|
41
|
+
subscribe: jest.fn((callback) => {
|
|
42
|
+
callback();
|
|
31
43
|
return functionsMocks.unsubscribe;
|
|
32
44
|
}),
|
|
33
45
|
unsubscribe: jest.fn(() => {}),
|
|
46
|
+
unsubscribeSearchStatus: jest.fn(() => {}),
|
|
34
47
|
};
|
|
35
48
|
|
|
36
49
|
// @ts-ignore
|
|
@@ -43,6 +56,7 @@ AriaLiveRegion.mockImplementation(() => {
|
|
|
43
56
|
const selectors = {
|
|
44
57
|
notifications: '[data-test="notification"]',
|
|
45
58
|
initializationError: 'c-quantic-component-error',
|
|
59
|
+
notificationCloseButton: 'lightning-button-icon',
|
|
46
60
|
};
|
|
47
61
|
|
|
48
62
|
const defaultOptions = {
|
|
@@ -68,6 +82,7 @@ function prepareHeadlessState() {
|
|
|
68
82
|
mockHeadlessLoader.getHeadlessBundle = () => {
|
|
69
83
|
return {
|
|
70
84
|
buildNotifyTrigger: functionsMocks.buildNotifyTrigger,
|
|
85
|
+
buildSearchStatus: functionsMocks.buildSearchStatus,
|
|
71
86
|
};
|
|
72
87
|
};
|
|
73
88
|
}
|
|
@@ -145,7 +160,7 @@ describe('c-quantic-notifications', () => {
|
|
|
145
160
|
});
|
|
146
161
|
|
|
147
162
|
describe('component initialization', () => {
|
|
148
|
-
it('should build the
|
|
163
|
+
it('should build the notifyTrigger and searchStatus controllers with the proper parameters', async () => {
|
|
149
164
|
createTestComponent();
|
|
150
165
|
await flushPromises();
|
|
151
166
|
|
|
@@ -153,9 +168,13 @@ describe('c-quantic-notifications', () => {
|
|
|
153
168
|
expect(functionsMocks.buildNotifyTrigger).toHaveBeenCalledWith(
|
|
154
169
|
exampleEngine
|
|
155
170
|
);
|
|
171
|
+
expect(functionsMocks.buildSearchStatus).toHaveBeenCalledTimes(1);
|
|
172
|
+
expect(functionsMocks.buildSearchStatus).toHaveBeenCalledWith(
|
|
173
|
+
exampleEngine
|
|
174
|
+
);
|
|
156
175
|
});
|
|
157
176
|
|
|
158
|
-
it('should subscribe to the headless state changes', async () => {
|
|
177
|
+
it('should subscribe to the headless notify trigger state changes', async () => {
|
|
159
178
|
createTestComponent();
|
|
160
179
|
await flushPromises();
|
|
161
180
|
|
|
@@ -223,5 +242,94 @@ describe('c-quantic-notifications', () => {
|
|
|
223
242
|
expect(notifications.length).toEqual(0);
|
|
224
243
|
});
|
|
225
244
|
});
|
|
245
|
+
|
|
246
|
+
describe('when there is an error following a search', () => {
|
|
247
|
+
it('should not render the notifications component', async () => {
|
|
248
|
+
const element = createTestComponent();
|
|
249
|
+
mockSearchStatusState.hasError = true;
|
|
250
|
+
functionsMocks.subscribe.callback();
|
|
251
|
+
await flushPromises();
|
|
252
|
+
|
|
253
|
+
const notifications = element.shadowRoot.querySelectorAll(
|
|
254
|
+
selectors.notifications
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
expect(notifications.length).toEqual(0);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
describe('when clicking on a notification close button', () => {
|
|
263
|
+
it('should properly dismiss that notification', async () => {
|
|
264
|
+
const element = createTestComponent();
|
|
265
|
+
await flushPromises();
|
|
266
|
+
|
|
267
|
+
const notificationsBeforeClose = element.shadowRoot.querySelectorAll(
|
|
268
|
+
selectors.notifications
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
const firstNotificationCloseButton =
|
|
272
|
+
notificationsBeforeClose[0].querySelector(
|
|
273
|
+
selectors.notificationCloseButton
|
|
274
|
+
);
|
|
275
|
+
firstNotificationCloseButton.click();
|
|
276
|
+
await flushPromises();
|
|
277
|
+
|
|
278
|
+
const notificationsAfterClose = element.shadowRoot.querySelectorAll(
|
|
279
|
+
selectors.notifications
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
expect(notificationsAfterClose.length).toEqual(
|
|
283
|
+
exampleNotifications.length - 1
|
|
284
|
+
);
|
|
285
|
+
expect(notificationsAfterClose[0].textContent).toEqual(
|
|
286
|
+
exampleNotifications[1]
|
|
287
|
+
);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
describe('when triggering another search with the same query', () => {
|
|
291
|
+
it('should reset the visibility of the notifications', async () => {
|
|
292
|
+
const element = createTestComponent();
|
|
293
|
+
await flushPromises();
|
|
294
|
+
|
|
295
|
+
const notificationsBeforeClose = element.shadowRoot.querySelectorAll(
|
|
296
|
+
selectors.notifications
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
expect(notificationsBeforeClose.length).toEqual(
|
|
300
|
+
exampleNotifications.length
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
const firstNotificationCloseButton =
|
|
304
|
+
notificationsBeforeClose[0].querySelector(
|
|
305
|
+
selectors.notificationCloseButton
|
|
306
|
+
);
|
|
307
|
+
firstNotificationCloseButton.click();
|
|
308
|
+
await flushPromises();
|
|
309
|
+
|
|
310
|
+
const notificationsAfterClose = element.shadowRoot.querySelectorAll(
|
|
311
|
+
selectors.notifications
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
expect(notificationsAfterClose.length).toEqual(
|
|
315
|
+
exampleNotifications.length - 1
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
mockSearchStatusState.hasError = false;
|
|
319
|
+
functionsMocks.subscribe.callback();
|
|
320
|
+
await flushPromises();
|
|
321
|
+
|
|
322
|
+
const notificationsAfterSearch = element.shadowRoot.querySelectorAll(
|
|
323
|
+
selectors.notifications
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
expect(notificationsAfterSearch.length).toEqual(
|
|
327
|
+
exampleNotifications.length
|
|
328
|
+
);
|
|
329
|
+
notificationsAfterSearch.forEach((notification, index) => {
|
|
330
|
+
expect(notification.textContent).toEqual(exampleNotifications[index]);
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
});
|
|
226
334
|
});
|
|
227
335
|
});
|
|
@@ -1,7 +1,15 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
--slds-c-toast-sizing-min-width: 100%;
|
|
3
|
+
}
|
|
4
|
+
|
|
1
5
|
.notification {
|
|
2
6
|
background-color: var(---lwc-colorBackgroundAlt, white);
|
|
3
7
|
border: 1px solid var(--lwc-colorBackgroundToast, rgb(116, 116, 116));
|
|
4
8
|
margin-right: 0;
|
|
5
9
|
margin-left: 0;
|
|
6
10
|
width: 100%;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.notification-container {
|
|
14
|
+
z-index: 2;
|
|
7
15
|
}
|
|
@@ -5,15 +5,28 @@
|
|
|
5
5
|
</template>
|
|
6
6
|
<template lwc:else>
|
|
7
7
|
<template for:each={notifications} for:item="notification">
|
|
8
|
-
<
|
|
9
|
-
<div data-
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
<template lwc:if={notification.visible}>
|
|
9
|
+
<div key={notification.id} data-test="notification" class="slds-notify_container slds-is-relative notification-container">
|
|
10
|
+
<div data-cy="notification" class="slds-notify slds-notify_toast notification" role="status">
|
|
11
|
+
<lightning-icon icon-name="utility:info_alt" alternative-text="info" title="info"
|
|
12
|
+
size="small"></lightning-icon>
|
|
13
|
+
<div class="slds-notify__content slds-var-m-left_small">
|
|
14
|
+
{notification.value}
|
|
15
|
+
</div>
|
|
16
|
+
<div class="slds-notify__close">
|
|
17
|
+
<lightning-button-icon
|
|
18
|
+
icon-name="utility:close"
|
|
19
|
+
alternative-text={labels.closeNotification}
|
|
20
|
+
size="small"
|
|
21
|
+
variant="bare"
|
|
22
|
+
title={labels.closeNotification}
|
|
23
|
+
onclick={handleNotificationClose}
|
|
24
|
+
data-id={notification.id}
|
|
25
|
+
></lightning-button-icon>
|
|
26
|
+
</div>
|
|
14
27
|
</div>
|
|
15
28
|
</div>
|
|
16
|
-
</
|
|
29
|
+
</template>
|
|
17
30
|
</template>
|
|
18
31
|
</template>
|
|
19
32
|
</template>
|
|
@@ -3,12 +3,13 @@ import {
|
|
|
3
3
|
initializeWithHeadless,
|
|
4
4
|
getHeadlessBundle,
|
|
5
5
|
} from 'c/quanticHeadlessLoader';
|
|
6
|
+
import closeNotification from '@salesforce/label/c.quantic_CloseNotification';
|
|
6
7
|
import {AriaLiveRegion} from 'c/quanticUtils';
|
|
7
8
|
import {LightningElement, api} from 'lwc';
|
|
8
9
|
|
|
9
10
|
/** @typedef {import("coveo").SearchEngine} SearchEngine */
|
|
11
|
+
/** @typedef {import("coveo").SearchStatus} SearchStatus */
|
|
10
12
|
/** @typedef {import("coveo").NotifyTrigger} NotifyTrigger */
|
|
11
|
-
/** @typedef {import("coveo").NotifyTriggerState} NotifyTriggerState */
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* The `QuanticNotifications` component is responsible for displaying notifications generated by the Coveo Search API (see [Trigger](https://docs.coveo.com/en/1458)).
|
|
@@ -27,12 +28,22 @@ export default class QuanticNotifications extends LightningElement {
|
|
|
27
28
|
|
|
28
29
|
/** @type {NotifyTrigger} */
|
|
29
30
|
notifyTrigger;
|
|
30
|
-
/** @type {NotifyTriggerState} */
|
|
31
|
-
notifyTriggerState;
|
|
32
31
|
/** @type {boolean} */
|
|
33
32
|
hasInitializationError = false;
|
|
34
33
|
/** @type {import('c/quanticUtils').AriaLiveUtils} */
|
|
35
34
|
ariaLiveNotificationsRegion;
|
|
35
|
+
/** @type {Array} */
|
|
36
|
+
notifications = [];
|
|
37
|
+
/** @type {Function} */
|
|
38
|
+
unsubscribe;
|
|
39
|
+
/** @type {Function} */
|
|
40
|
+
unsubscribeSearchStatus;
|
|
41
|
+
/** @type {SearchStatus} */
|
|
42
|
+
searchStatus;
|
|
43
|
+
|
|
44
|
+
labels = {
|
|
45
|
+
closeNotification,
|
|
46
|
+
};
|
|
36
47
|
|
|
37
48
|
connectedCallback() {
|
|
38
49
|
registerComponentForInit(this, this.engineId);
|
|
@@ -48,32 +59,60 @@ export default class QuanticNotifications extends LightningElement {
|
|
|
48
59
|
initialize = (engine) => {
|
|
49
60
|
this.headless = getHeadlessBundle(this.engineId);
|
|
50
61
|
this.notifyTrigger = this.headless.buildNotifyTrigger(engine);
|
|
62
|
+
this.searchStatus = this.headless.buildSearchStatus(engine);
|
|
51
63
|
this.ariaLiveNotificationsRegion = AriaLiveRegion('notifications', this);
|
|
52
64
|
this.unsubscribe = this.notifyTrigger.subscribe(() => this.updateState());
|
|
65
|
+
this.unsubscribeSearchStatus = this.searchStatus.subscribe(() =>
|
|
66
|
+
this.handleSearchStatusChange()
|
|
67
|
+
);
|
|
53
68
|
};
|
|
54
69
|
|
|
55
70
|
disconnectedCallback() {
|
|
56
71
|
this.unsubscribe?.();
|
|
72
|
+
this.unsubscribeSearchStatus?.();
|
|
57
73
|
}
|
|
58
74
|
|
|
59
75
|
updateState() {
|
|
60
|
-
this.
|
|
61
|
-
|
|
62
|
-
this.notifyTriggerState?.notifications.reduce(
|
|
63
|
-
(value, notification, index) => {
|
|
64
|
-
return `${value} Notification ${index + 1}: ${notification}`;
|
|
65
|
-
},
|
|
66
|
-
''
|
|
67
|
-
)
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
get notifications() {
|
|
72
|
-
return (
|
|
73
|
-
this.notifyTriggerState?.notifications.map((notification, index) => ({
|
|
76
|
+
this.notifications =
|
|
77
|
+
this.notifyTrigger?.state?.notifications.map((notification, index) => ({
|
|
74
78
|
value: notification,
|
|
75
79
|
id: index,
|
|
76
|
-
|
|
80
|
+
visible: true,
|
|
81
|
+
})) ?? [];
|
|
82
|
+
|
|
83
|
+
this.dispatchAriaLiveNotificationsRegionMessage();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
handleSearchStatusChange() {
|
|
87
|
+
if (!this.searchStatus?.state.isLoading) {
|
|
88
|
+
if (this.searchStatus?.state?.hasError) {
|
|
89
|
+
this.notifications = [];
|
|
90
|
+
} else {
|
|
91
|
+
this.notifications =
|
|
92
|
+
this.notifications?.map((notification) => ({
|
|
93
|
+
...notification,
|
|
94
|
+
visible: true,
|
|
95
|
+
})) ?? [];
|
|
96
|
+
this.dispatchAriaLiveNotificationsRegionMessage();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
handleNotificationClose(event) {
|
|
102
|
+
const currentNotificationId = event.currentTarget.dataset.id;
|
|
103
|
+
this.notifications = this.notifications.map((notification) => {
|
|
104
|
+
if (notification.id.toString() === currentNotificationId) {
|
|
105
|
+
return {...notification, visible: false};
|
|
106
|
+
}
|
|
107
|
+
return notification;
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
dispatchAriaLiveNotificationsRegionMessage() {
|
|
112
|
+
this.ariaLiveNotificationsRegion.dispatchMessage(
|
|
113
|
+
this.notifications.reduce((value, notification) => {
|
|
114
|
+
return `${value} Notification ${notification.id + 1}: ${notification.value}`;
|
|
115
|
+
}, '')
|
|
77
116
|
);
|
|
78
117
|
}
|
|
79
118
|
|
package/force-app/main/default/lwc/quanticNumericFacet/__tests__/quanticNumericFacet.test.js
CHANGED
|
@@ -12,17 +12,22 @@ jest.mock('c/quanticUtils', () => ({
|
|
|
12
12
|
NUMERICFACETS: 'numericFacets',
|
|
13
13
|
},
|
|
14
14
|
},
|
|
15
|
+
I18nUtils: {
|
|
16
|
+
format: jest.fn(),
|
|
17
|
+
},
|
|
15
18
|
}));
|
|
16
19
|
jest.mock('c/quanticHeadlessLoader');
|
|
17
20
|
|
|
18
21
|
const selectors = {
|
|
19
22
|
facetContent: '[data-test="facet-content"]',
|
|
23
|
+
componentError: 'c-quantic-component-error',
|
|
20
24
|
};
|
|
21
25
|
|
|
22
26
|
const exampleFacetId = 'example facet id';
|
|
27
|
+
const exampleField = 'exampleField';
|
|
23
28
|
const exampleFilterId = `${exampleFacetId}_input`;
|
|
24
29
|
const defaultOptions = {
|
|
25
|
-
field:
|
|
30
|
+
field: exampleField,
|
|
26
31
|
};
|
|
27
32
|
const numericFacetControllerMock = {
|
|
28
33
|
subscribe: jest.fn((callback) => callback()),
|
|
@@ -31,6 +36,8 @@ const numericFacetControllerMock = {
|
|
|
31
36
|
values: [],
|
|
32
37
|
},
|
|
33
38
|
};
|
|
39
|
+
const parentFacetIdError = `The ${exampleField} c-quantic-numeric-facet requires dependsOn.parentFacetId to be a valid string.`;
|
|
40
|
+
const expectedValueError = `The ${exampleField} c-quantic-numeric-facet requires dependsOn.expectedValue to be a valid string.`;
|
|
34
41
|
|
|
35
42
|
function createTestComponent(options = defaultOptions) {
|
|
36
43
|
prepareHeadlessState();
|
|
@@ -87,6 +94,21 @@ const exampleEngine = {
|
|
|
87
94
|
};
|
|
88
95
|
let isInitialized = false;
|
|
89
96
|
|
|
97
|
+
function mockBueno() {
|
|
98
|
+
// @ts-ignore
|
|
99
|
+
mockHeadlessLoader.getBueno = () => {
|
|
100
|
+
// @ts-ignore
|
|
101
|
+
global.Bueno = {
|
|
102
|
+
isString: jest
|
|
103
|
+
.fn()
|
|
104
|
+
.mockImplementation(
|
|
105
|
+
(value) => Object.prototype.toString.call(value) === '[object String]'
|
|
106
|
+
),
|
|
107
|
+
};
|
|
108
|
+
return new Promise((resolve) => resolve());
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
90
112
|
function mockSuccessfulHeadlessInitialization() {
|
|
91
113
|
// @ts-ignore
|
|
92
114
|
mockHeadlessLoader.initializeWithHeadless = (element, _, initialize) => {
|
|
@@ -109,6 +131,7 @@ function cleanup() {
|
|
|
109
131
|
describe('c-quantic-numeric-facet', () => {
|
|
110
132
|
beforeAll(() => {
|
|
111
133
|
mockSuccessfulHeadlessInitialization();
|
|
134
|
+
mockBueno();
|
|
112
135
|
});
|
|
113
136
|
|
|
114
137
|
afterEach(() => {
|
|
@@ -214,6 +237,89 @@ describe('c-quantic-numeric-facet', () => {
|
|
|
214
237
|
});
|
|
215
238
|
});
|
|
216
239
|
|
|
240
|
+
describe('validation of the dependsOn property', () => {
|
|
241
|
+
let consoleError;
|
|
242
|
+
beforeAll(() => {
|
|
243
|
+
consoleError = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
244
|
+
});
|
|
245
|
+
describe('when dependsOn.parentFacetId is not provided', () => {
|
|
246
|
+
it('should display the error component', async () => {
|
|
247
|
+
const invalidFacetDependency = {
|
|
248
|
+
expectedValue: 'txt',
|
|
249
|
+
};
|
|
250
|
+
const element = createTestComponent({
|
|
251
|
+
...defaultOptions,
|
|
252
|
+
dependsOn: invalidFacetDependency,
|
|
253
|
+
});
|
|
254
|
+
await flushPromises();
|
|
255
|
+
|
|
256
|
+
const componentError = element.shadowRoot.querySelector(
|
|
257
|
+
selectors.componentError
|
|
258
|
+
);
|
|
259
|
+
const facetContent = element.shadowRoot.querySelector(
|
|
260
|
+
selectors.facetContent
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
expect(consoleError).toHaveBeenCalledTimes(1);
|
|
264
|
+
expect(consoleError).toHaveBeenCalledWith(parentFacetIdError);
|
|
265
|
+
expect(componentError).not.toBeNull();
|
|
266
|
+
expect(facetContent).toBeNull();
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
describe('when dependsOn.parentFacetId is not a string', () => {
|
|
271
|
+
it('should display the error component', async () => {
|
|
272
|
+
const invalidFacetDependency = {
|
|
273
|
+
parentFacetId: 1,
|
|
274
|
+
expectedValue: 'txt',
|
|
275
|
+
};
|
|
276
|
+
const element = createTestComponent({
|
|
277
|
+
...defaultOptions,
|
|
278
|
+
dependsOn: invalidFacetDependency,
|
|
279
|
+
});
|
|
280
|
+
await flushPromises();
|
|
281
|
+
|
|
282
|
+
const componentError = element.shadowRoot.querySelector(
|
|
283
|
+
selectors.componentError
|
|
284
|
+
);
|
|
285
|
+
const facetContent = element.shadowRoot.querySelector(
|
|
286
|
+
selectors.facetContent
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
expect(consoleError).toHaveBeenCalledTimes(1);
|
|
290
|
+
expect(consoleError).toHaveBeenCalledWith(parentFacetIdError);
|
|
291
|
+
expect(componentError).not.toBeNull();
|
|
292
|
+
expect(facetContent).toBeNull();
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
describe('when dependsOn.expectedValue is not a string', () => {
|
|
297
|
+
it('should display the error component', async () => {
|
|
298
|
+
const invalidFacetDependency = {
|
|
299
|
+
parentFacetId: 'filetype',
|
|
300
|
+
expectedValue: 2,
|
|
301
|
+
};
|
|
302
|
+
const element = createTestComponent({
|
|
303
|
+
...defaultOptions,
|
|
304
|
+
dependsOn: invalidFacetDependency,
|
|
305
|
+
});
|
|
306
|
+
await flushPromises();
|
|
307
|
+
|
|
308
|
+
const componentError = element.shadowRoot.querySelector(
|
|
309
|
+
selectors.componentError
|
|
310
|
+
);
|
|
311
|
+
const facetContent = element.shadowRoot.querySelector(
|
|
312
|
+
selectors.facetContent
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
expect(consoleError).toHaveBeenCalledTimes(1);
|
|
316
|
+
expect(consoleError).toHaveBeenCalledWith(expectedValueError);
|
|
317
|
+
expect(componentError).not.toBeNull();
|
|
318
|
+
expect(facetContent).toBeNull();
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
|
|
217
323
|
describe('when the component is disconnected', () => {
|
|
218
324
|
it('should make the condition manager stop watching the facet', async () => {
|
|
219
325
|
const exampleFacetDependency = {
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
getHeadlessBindings,
|
|
19
19
|
registerToStore,
|
|
20
20
|
getHeadlessBundle,
|
|
21
|
+
getBueno,
|
|
21
22
|
} from 'c/quanticHeadlessLoader';
|
|
22
23
|
import {
|
|
23
24
|
I18nUtils,
|
|
@@ -264,6 +265,7 @@ export default class QuanticNumericFacet extends LightningElement {
|
|
|
264
265
|
* @param {SearchEngine} engine
|
|
265
266
|
*/
|
|
266
267
|
initialize = (engine) => {
|
|
268
|
+
this.validateDependsOnProperty();
|
|
267
269
|
this.headless = getHeadlessBundle(this.engineId);
|
|
268
270
|
this.searchStatus = this.headless.buildSearchStatus(engine);
|
|
269
271
|
this.unsubscribeSearchStatus = this.searchStatus.subscribe(() =>
|
|
@@ -288,6 +290,26 @@ export default class QuanticNumericFacet extends LightningElement {
|
|
|
288
290
|
});
|
|
289
291
|
};
|
|
290
292
|
|
|
293
|
+
validateDependsOnProperty() {
|
|
294
|
+
if (this.dependsOn) {
|
|
295
|
+
getBueno(this).then(() => {
|
|
296
|
+
const {parentFacetId, expectedValue} = this.dependsOn;
|
|
297
|
+
if (!Bueno.isString(parentFacetId)) {
|
|
298
|
+
console.error(
|
|
299
|
+
`The ${this.field} ${this.template.host.localName} requires dependsOn.parentFacetId to be a valid string.`
|
|
300
|
+
);
|
|
301
|
+
this.setInitializationError();
|
|
302
|
+
}
|
|
303
|
+
if (expectedValue && !Bueno.isString(expectedValue)) {
|
|
304
|
+
console.error(
|
|
305
|
+
`The ${this.field} ${this.template.host.localName} requires dependsOn.expectedValue to be a valid string.`
|
|
306
|
+
);
|
|
307
|
+
this.setInitializationError();
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
291
313
|
/**
|
|
292
314
|
* @param {import("coveo").SearchEngine} engine
|
|
293
315
|
*/
|
package/force-app/main/default/lwc/quanticRecommendationInterface/quanticRecommendationInterface.js
CHANGED
|
@@ -82,6 +82,14 @@ export default class QuanticRecommendationInterface extends LightningElement {
|
|
|
82
82
|
...(document.referrer && {
|
|
83
83
|
originLevel3: document.referrer.substring(0, 256),
|
|
84
84
|
}),
|
|
85
|
+
analyticsClientMiddleware: (_event, payload) => {
|
|
86
|
+
if (!payload.customData) {
|
|
87
|
+
payload.customData = {};
|
|
88
|
+
}
|
|
89
|
+
payload.customData.coveoQuanticVersion =
|
|
90
|
+
window.coveoQuanticVersion;
|
|
91
|
+
return payload;
|
|
92
|
+
},
|
|
85
93
|
},
|
|
86
94
|
...rest,
|
|
87
95
|
},
|
|
@@ -107,6 +107,14 @@ export default class QuanticSearchInterface extends LightningElement {
|
|
|
107
107
|
...(document.referrer && {
|
|
108
108
|
originLevel3: document.referrer.substring(0, 256),
|
|
109
109
|
}),
|
|
110
|
+
analyticsClientMiddleware: (_event, payload) => {
|
|
111
|
+
if (!payload.customData) {
|
|
112
|
+
payload.customData = {};
|
|
113
|
+
}
|
|
114
|
+
payload.customData.coveoQuanticVersion =
|
|
115
|
+
window.coveoQuanticVersion;
|
|
116
|
+
return payload;
|
|
117
|
+
},
|
|
110
118
|
},
|
|
111
119
|
...rest,
|
|
112
120
|
},
|