@coveo/quantic 3.10.0 → 3.11.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 +52 -12
- package/docs/out/quantic-docs.json +27 -0
- package/force-app/main/default/labels/CustomLabels.labels-meta.xml +7 -0
- package/force-app/main/default/lwc/quanticBreadcrumbManager/__tests__/quanticBreadcrumbManager.test.js +341 -0
- package/force-app/main/default/lwc/quanticBreadcrumbManager/quanticBreadcrumbManager.html +8 -8
- package/force-app/main/default/lwc/quanticBreadcrumbManager/quanticBreadcrumbManager.js +8 -8
- package/force-app/main/default/lwc/quanticFeedbackModalQna/templates/quanticFeedbackModalQna.html +1 -0
- package/force-app/main/default/lwc/quanticGeneratedAnswer/__tests__/quanticGeneratedAnswer.test.js +538 -21
- package/force-app/main/default/lwc/quanticGeneratedAnswer/e2e/data.ts +56 -0
- package/force-app/main/default/lwc/quanticGeneratedAnswer/e2e/fixture.ts +87 -0
- package/force-app/main/default/lwc/quanticGeneratedAnswer/e2e/pageObject.ts +279 -0
- package/force-app/main/default/lwc/quanticGeneratedAnswer/e2e/quanticGeneratedAnswer.e2e.ts +178 -0
- package/force-app/main/default/lwc/quanticGeneratedAnswer/templates/generatedAnswer.html +24 -10
- package/force-app/main/default/lwc/quanticGeneratedAnswer/templates/retryPrompt.html +2 -2
- package/force-app/main/default/lwc/quanticGeneratedAnswerToggle/quanticGeneratedAnswerToggle.html +1 -1
- 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/quanticPager/e2e/pagerObject.ts +1 -1
- 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/lwc/quanticUserActionsToggle/quanticUserActionsToggle.js +4 -0
- package/force-app/main/default/staticresources/coveobueno/browser/bueno.js +4 -4
- package/force-app/main/default/staticresources/coveoheadless/case-assist/headless.js +8 -10
- package/force-app/main/default/staticresources/coveoheadless/definitions/api/analytics/instant-result-analytics.d.ts +1 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/api/analytics/search-analytics.d.ts +1 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/api/knowledge/stream-answer-api.d.ts +3 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/commerce-engine/commerce-engine.ssr.d.ts +2 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/commerce-ssr-engine/factories/build-factory.d.ts +2 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/commerce-ssr-engine/factories/utils/recommendation-filter.d.ts +11 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/commerce-ssr-engine/types/build.d.ts +4 -8
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/commerce-ssr-engine/types/common.d.ts +178 -7
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/commerce-ssr-engine/types/core-engine.d.ts +3 -3
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/commerce-ssr-engine/types/fetch-static-state.d.ts +4 -12
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/commerce-ssr-engine/types/from-build-result.d.ts +1 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/commerce-ssr-engine/types/hydrate-static-state.d.ts +4 -11
- package/force-app/main/default/staticresources/coveoheadless/definitions/app/commerce-ssr-engine/types/kind.d.ts +6 -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/commerce.index.d.ts +1 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/context/cart/headless-cart.ssr.d.ts +2 -2
- package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/context/headless-context.ssr.d.ts +2 -2
- package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/core/breadcrumb-manager/headless-core-breadcrumb-manager.ssr.d.ts +1 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/core/facets/generator/headless-commerce-facet-generator.ssr.d.ts +3 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/core/parameter-manager/headless-core-parameter-manager.d.ts +8 -7
- package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/core/parameter-manager/headless-core-parameter-manager.ssr.d.ts +3 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/core/sub-controller/headless-sub-controller.d.ts +1 -2
- package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/core/url-manager/headless-core-url-manager.d.ts +11 -2
- package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/product-list/headless-product-list.ssr.d.ts +1 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/product-view/headless-product-view.ssr.d.ts +1 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/recommendations/headless-recommendations.d.ts +1 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/recommendations/headless-recommendations.ssr.d.ts +4 -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/commerce/parameters/parameters-actions.d.ts +22 -6
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/commerce/parameters/parameters-schema.d.ts +4 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/commerce/parameters/parameters-selectors.d.ts +0 -11
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/commerce/parameters/parameters-serializer.d.ts +6 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/commerce/parameters/parameters-serializer.ssr.d.ts +44 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/commerce/parameters/parameters-slice.d.ts +4 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/commerce/parameters/parameters-state.d.ts +4 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/commerce/recommendations/recommendations-actions.d.ts +1 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/commerce/recommendations/recommendations-state.d.ts +1 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/commerce/search/search-selectors.d.ts +0 -12
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/debug/debug-selectors.d.ts +1 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/question-answering/question-answering-selectors.d.ts +1 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/search/search-selectors.d.ts +1 -1
- package/force-app/main/default/staticresources/coveoheadless/definitions/features/search-parameters/search-parameter-serializer.d.ts +5 -2
- package/force-app/main/default/staticresources/coveoheadless/definitions/ssr-commerce.index.d.ts +4 -3
- package/force-app/main/default/staticresources/coveoheadless/definitions/state/commerce-app-state.d.ts +2 -2
- package/force-app/main/default/staticresources/coveoheadless/definitions/state/state-sections.d.ts +4 -0
- package/force-app/main/default/staticresources/coveoheadless/definitions/utils/errors.d.ts +3 -0
- package/force-app/main/default/staticresources/coveoheadless/headless.js +10 -12
- package/force-app/main/default/staticresources/coveoheadless/insight/headless.js +9 -11
- package/force-app/main/default/staticresources/coveoheadless/recommendation/headless.js +8 -10
- package/force-app/main/default/staticresources/dompurify/purify.min.js +2 -1339
- package/package.json +23 -19
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
|
|
|
@@ -119,6 +119,24 @@ describe('c-quantic-user-actions-toggle', () => {
|
|
|
119
119
|
cleanup();
|
|
120
120
|
});
|
|
121
121
|
|
|
122
|
+
describe('controller initialization', () => {
|
|
123
|
+
it('should build the controller with the right parameters', async () => {
|
|
124
|
+
createTestComponent();
|
|
125
|
+
await flushPromises();
|
|
126
|
+
|
|
127
|
+
expect(functionsMocks.buildUserActions).toHaveBeenCalledTimes(1);
|
|
128
|
+
expect(functionsMocks.buildUserActions).toHaveBeenCalledWith(
|
|
129
|
+
exampleEngine,
|
|
130
|
+
{
|
|
131
|
+
options: {
|
|
132
|
+
ticketCreationDate: exampleTicketCreationDateTime,
|
|
133
|
+
excludedCustomActions: exampleExcludedCustomActions,
|
|
134
|
+
},
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
122
140
|
it('should display the user actions toggle button', async () => {
|
|
123
141
|
const element = createTestComponent();
|
|
124
142
|
await flushPromises();
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const followingSessionsActions = [
|
|
2
|
+
{
|
|
3
|
+
name: 'CUSTOM',
|
|
4
|
+
value:
|
|
5
|
+
'{"event_type":"example","event_value":"exampleCustomAction","origin_level_1":"default"}',
|
|
6
|
+
time: new Date('2024-09-01T15:30:00Z').valueOf(),
|
|
7
|
+
},
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
const ticketCreationSessionActions = [
|
|
11
|
+
{
|
|
12
|
+
name: 'CUSTOM',
|
|
13
|
+
value:
|
|
14
|
+
'{"event_type":"errors","event_value":"One","origin_level_1":"default","origin_level_2":"default"}',
|
|
15
|
+
time: new Date('2024-08-30T00:10:00Z').valueOf(),
|
|
16
|
+
},
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const precedingSessionsActions = [
|
|
20
|
+
{
|
|
21
|
+
name: 'CUSTOM',
|
|
22
|
+
value:
|
|
23
|
+
'{"event_type":"example","event_value":"exampleCustomAction","origin_level_1":"default"}',
|
|
24
|
+
time: new Date('2024-08-29T15:40:00Z').valueOf(),
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export const exampleUserActionsData = [
|
|
29
|
+
...followingSessionsActions,
|
|
30
|
+
...ticketCreationSessionActions,
|
|
31
|
+
...precedingSessionsActions,
|
|
32
|
+
];
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {quanticBase} from '../../../../../../playwright/fixtures/baseFixture';
|
|
2
|
+
import {UserActionsToggleObject} from './pageObject';
|
|
3
|
+
import {exampleUserActionsData} from './data';
|
|
4
|
+
|
|
5
|
+
const pageUrl = 's/quantic-user-actions-toggle';
|
|
6
|
+
|
|
7
|
+
interface UserActionsToggleOptions {
|
|
8
|
+
userId: string;
|
|
9
|
+
ticketCreationDateTime: string;
|
|
10
|
+
}
|
|
11
|
+
type QuanticUserActionsToggleE2EFixtures = {
|
|
12
|
+
userActionsToggle: UserActionsToggleObject;
|
|
13
|
+
options: UserActionsToggleOptions;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const testInsight =
|
|
17
|
+
quanticBase.extend<QuanticUserActionsToggleE2EFixtures>({
|
|
18
|
+
options: {
|
|
19
|
+
userId: 'someone@company.com',
|
|
20
|
+
ticketCreationDateTime: '2024-01-01T00:00:00Z',
|
|
21
|
+
},
|
|
22
|
+
userActionsToggle: async ({page, options, configuration}, use) => {
|
|
23
|
+
await page.goto(pageUrl);
|
|
24
|
+
configuration.configure({...options});
|
|
25
|
+
const userActionsToggleObject = new UserActionsToggleObject(page);
|
|
26
|
+
userActionsToggleObject.mockUserActions(exampleUserActionsData);
|
|
27
|
+
await userActionsToggleObject.waitForUserActionsResponse(options.userId);
|
|
28
|
+
await use(userActionsToggleObject);
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export {expect} from '@playwright/test';
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type {Locator, Page, Request, Response} from '@playwright/test';
|
|
2
|
+
import {isUaCustomEvent} from '../../../../../../playwright/utils/requests';
|
|
3
|
+
|
|
4
|
+
const userActionsRequestRegex =
|
|
5
|
+
/\/rest\/organizations\/.*\/machinelearning\/user\/actions$/;
|
|
6
|
+
|
|
7
|
+
export class UserActionsToggleObject {
|
|
8
|
+
constructor(public page: Page) {
|
|
9
|
+
this.page = page;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
get userActionsToggleButton(): Locator {
|
|
13
|
+
return this.page.getByRole('button', {name: /User actions/i});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get userActionsModal(): Locator {
|
|
17
|
+
return this.page.locator(
|
|
18
|
+
'c-quantic-user-actions-toggle > c-quantic-modal > div'
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
get userActionsModalCloseButton(): Locator {
|
|
23
|
+
return this.page
|
|
24
|
+
.locator('c-quantic-user-actions-toggle > c-quantic-modal')
|
|
25
|
+
.getByRole('button', {name: 'Close'});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async mockUserActions(
|
|
29
|
+
userActions: Array<{name: string; value: string; time: number}>
|
|
30
|
+
) {
|
|
31
|
+
await this.page.route(userActionsRequestRegex, async (route) => {
|
|
32
|
+
const body = {value: userActions};
|
|
33
|
+
|
|
34
|
+
await route.fulfill({
|
|
35
|
+
body: JSON.stringify(body),
|
|
36
|
+
status: 200,
|
|
37
|
+
headers: {
|
|
38
|
+
'content-type': 'text/html',
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async waitForUserActionsResponse(userId: string): Promise<Response> {
|
|
45
|
+
return this.page.waitForResponse((response) => {
|
|
46
|
+
const request = response.request();
|
|
47
|
+
if (request.url().match(userActionsRequestRegex)) {
|
|
48
|
+
const requestBody = request.postDataJSON?.();
|
|
49
|
+
return requestBody?.objectId === userId;
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async waitForOpenUserActionsUaAnalytics(): Promise<Request> {
|
|
56
|
+
const uaRequest = this.page.waitForRequest((request) => {
|
|
57
|
+
if (isUaCustomEvent(request)) {
|
|
58
|
+
const requestBody = request.postDataJSON?.();
|
|
59
|
+
const expectedFields = {
|
|
60
|
+
eventType: 'User Actions',
|
|
61
|
+
eventValue: 'openUserActions',
|
|
62
|
+
};
|
|
63
|
+
return Object.keys(expectedFields).every(
|
|
64
|
+
(key) => requestBody?.[key] === expectedFields[key]
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
});
|
|
69
|
+
return uaRequest;
|
|
70
|
+
}
|
|
71
|
+
}
|
package/force-app/main/default/lwc/quanticUserActionsToggle/e2e/quanticUserActionsToggle.e2e.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {testInsight, expect} from './fixture';
|
|
2
|
+
|
|
3
|
+
const exampleUserId = 'exampleUserId';
|
|
4
|
+
const exampleTicketCreateDateTime = encodeURIComponent('2024-08-30');
|
|
5
|
+
|
|
6
|
+
testInsight.use({
|
|
7
|
+
options: {
|
|
8
|
+
userId: exampleUserId,
|
|
9
|
+
ticketCreationDateTime: exampleTicketCreateDateTime,
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
testInsight.describe('quantic user actions toggle', () => {
|
|
14
|
+
testInsight.describe('when opening the user action modal', () => {
|
|
15
|
+
testInsight(
|
|
16
|
+
'should open the user actions timeline and log analytics',
|
|
17
|
+
async ({userActionsToggle}) => {
|
|
18
|
+
await expect(userActionsToggle.userActionsModal).not.toBeVisible();
|
|
19
|
+
const uaPromise = userActionsToggle.waitForOpenUserActionsUaAnalytics();
|
|
20
|
+
await userActionsToggle.userActionsToggleButton.click();
|
|
21
|
+
await expect(userActionsToggle.userActionsModal).toBeVisible();
|
|
22
|
+
await uaPromise;
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
testInsight.describe('when closing the user action modal', () => {
|
|
28
|
+
testInsight(
|
|
29
|
+
'should close the user actions timeline',
|
|
30
|
+
async ({userActionsToggle}) => {
|
|
31
|
+
await expect(userActionsToggle.userActionsModal).not.toBeVisible();
|
|
32
|
+
await userActionsToggle.userActionsToggleButton.click();
|
|
33
|
+
await expect(userActionsToggle.userActionsModal).toBeVisible();
|
|
34
|
+
await userActionsToggle.userActionsModalCloseButton.click();
|
|
35
|
+
await expect(userActionsToggle.userActionsModal).not.toBeVisible();
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -35,17 +35,21 @@ export default class QuanticUserActionsToggle extends LightningElement {
|
|
|
35
35
|
@api engineId;
|
|
36
36
|
/**
|
|
37
37
|
* The ID of the user whose actions are being displayed. For example in email format "someone@company.com".
|
|
38
|
+
* @api
|
|
38
39
|
* @type {string}
|
|
39
40
|
*/
|
|
40
41
|
@api userId;
|
|
41
42
|
/**
|
|
42
43
|
* The date and time when the ticket was created. For example "2024-01-01T00:00:00Z"
|
|
44
|
+
* @api
|
|
43
45
|
* @type {string}
|
|
44
46
|
*/
|
|
45
47
|
@api ticketCreationDateTime;
|
|
46
48
|
/**
|
|
47
49
|
* The names of custom events to exclude.
|
|
50
|
+
* @api
|
|
48
51
|
* @type {Array<string>}
|
|
52
|
+
* @default []
|
|
49
53
|
*/
|
|
50
54
|
@api excludedCustomActions = [];
|
|
51
55
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @license
|
|
3
3
|
*
|
|
4
|
-
* Copyright
|
|
4
|
+
* Copyright 2025 Coveo Solutions Inc.
|
|
5
5
|
*
|
|
6
6
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
7
|
* you may not use this file except in compliance with the License.
|
|
@@ -35,8 +35,8 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
35
35
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
36
36
|
|
|
37
37
|
// src/index.ts
|
|
38
|
-
var
|
|
39
|
-
__export(
|
|
38
|
+
var index_exports = {};
|
|
39
|
+
__export(index_exports, {
|
|
40
40
|
ArrayValue: () => ArrayValue,
|
|
41
41
|
BooleanValue: () => BooleanValue,
|
|
42
42
|
EnumValue: () => EnumValue,
|
|
@@ -57,7 +57,7 @@ __export(src_exports, {
|
|
|
57
57
|
isString: () => isString,
|
|
58
58
|
isUndefined: () => isUndefined
|
|
59
59
|
});
|
|
60
|
-
module.exports = __toCommonJS(
|
|
60
|
+
module.exports = __toCommonJS(index_exports);
|
|
61
61
|
|
|
62
62
|
// src/schema.ts
|
|
63
63
|
function buildSchemaValidationError(errors, context) {
|