@salesforcedevs/dx-components 1.3.246-canary.2 → 1.3.247
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/LICENSE +12 -0
- package/package.json +3 -2
- package/src/modules/dx/audio/audio.css +39 -20
- package/src/modules/dx/audio/audio.html +22 -23
- package/src/modules/dx/audio/audio.ts +65 -5
- package/src/modules/dx/cardBlogPost/cardBlogPost.ts +5 -0
- package/src/modules/dx/coveoRecommendations/coveoRecommendations.html +1 -0
- package/src/modules/dx/coveoRecommendations/coveoRecommendations.ts +58 -3
- package/src/modules/dx/popover/popover.ts +8 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Copyright (c) 2020, Salesforce.com, Inc.
|
|
2
|
+
All rights reserved.
|
|
3
|
+
|
|
4
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
5
|
+
|
|
6
|
+
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
7
|
+
|
|
8
|
+
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
9
|
+
|
|
10
|
+
* Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
|
11
|
+
|
|
12
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforcedevs/dx-components",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.247",
|
|
4
4
|
"description": "DX Lightning web components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"engines": {
|
|
@@ -44,5 +44,6 @@
|
|
|
44
44
|
},
|
|
45
45
|
"volta": {
|
|
46
46
|
"node": "16.19.1"
|
|
47
|
-
}
|
|
47
|
+
},
|
|
48
|
+
"gitHead": "fdadc22dcca148e26f1589d4de64b8445899f39d"
|
|
48
49
|
}
|
|
@@ -30,8 +30,10 @@
|
|
|
30
30
|
top: 1px;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
/*
|
|
34
|
-
|
|
33
|
+
/*
|
|
34
|
+
The "inner" container/main part of the player, inside the outer, colored border.
|
|
35
|
+
Current design goal of this element and all other "inner" elements is to closely match native controls as they appear in Chrome
|
|
36
|
+
*/
|
|
35
37
|
.player {
|
|
36
38
|
--dx-c-popover-border-radius: 0;
|
|
37
39
|
--dx-c-popover-padding: 0;
|
|
@@ -72,7 +74,6 @@
|
|
|
72
74
|
border-radius: 16px;
|
|
73
75
|
cursor: pointer;
|
|
74
76
|
height: 4px;
|
|
75
|
-
outline: none;
|
|
76
77
|
width: 100%;
|
|
77
78
|
}
|
|
78
79
|
|
|
@@ -123,24 +124,29 @@
|
|
|
123
124
|
.player-volume-container {
|
|
124
125
|
align-items: center;
|
|
125
126
|
border-radius: var(--dx-g-spacing-xs);
|
|
127
|
+
height: 24px;
|
|
126
128
|
display: flex;
|
|
127
|
-
padding-left:
|
|
129
|
+
padding-left: 6px;
|
|
130
|
+
position: relative;
|
|
128
131
|
transition: 0.2s ease-out;
|
|
129
132
|
width: 24px;
|
|
130
133
|
}
|
|
131
134
|
|
|
132
135
|
.player-volume-container .player-volume-slider {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
136
|
+
/* HTML5 boilerplate .visuallyhidden equivalent, for hiding an element but leaving it focusable */
|
|
137
|
+
border: 0;
|
|
138
|
+
clip: rect(0 0 0 0);
|
|
139
|
+
height: 1px;
|
|
140
|
+
margin: -1px;
|
|
141
|
+
overflow: hidden;
|
|
142
|
+
padding: 0;
|
|
143
|
+
position: absolute;
|
|
144
|
+
width: 1px;
|
|
140
145
|
}
|
|
141
146
|
|
|
142
|
-
.player-volume-container
|
|
143
|
-
|
|
147
|
+
.player-volume-container .player-volume-button {
|
|
148
|
+
position: absolute;
|
|
149
|
+
right: 0;
|
|
144
150
|
}
|
|
145
151
|
|
|
146
152
|
/* Three dot menu, for download and playback speed settings */
|
|
@@ -158,8 +164,8 @@
|
|
|
158
164
|
}
|
|
159
165
|
|
|
160
166
|
/* Speed selection items have a large left padding in Chrome, which we are mimicking */
|
|
161
|
-
.player-threedot-menu
|
|
162
|
-
padding-left: calc(var(--dx-c-threedot-menu-item-padding) *
|
|
167
|
+
.player-threedot-menu .player-speed-item {
|
|
168
|
+
padding-left: calc(var(--dx-c-threedot-menu-item-padding) * 3);
|
|
163
169
|
}
|
|
164
170
|
|
|
165
171
|
.player-threedot-menu a {
|
|
@@ -189,8 +195,10 @@
|
|
|
189
195
|
transform: translateY(-50%);
|
|
190
196
|
}
|
|
191
197
|
|
|
192
|
-
/*
|
|
193
|
-
|
|
198
|
+
/*
|
|
199
|
+
All clickable elements inside of the three dot menu are buttons, except for the download link
|
|
200
|
+
dx-button is not used here because these things barely look like buttons
|
|
201
|
+
*/
|
|
194
202
|
.player-threedot-menu button {
|
|
195
203
|
background: transparent;
|
|
196
204
|
border: 0;
|
|
@@ -223,16 +231,27 @@
|
|
|
223
231
|
opacity: 1;
|
|
224
232
|
}
|
|
225
233
|
|
|
234
|
+
.player-volume-container.focused-by-keyboard,
|
|
226
235
|
.player-volume-container:hover {
|
|
227
236
|
background: var(--sds-g-gray-4);
|
|
228
|
-
width:
|
|
237
|
+
width: max(12%, 150px);
|
|
229
238
|
}
|
|
230
239
|
|
|
240
|
+
.player-volume-container.focused-by-keyboard .player-volume-slider,
|
|
231
241
|
.player-volume-container:hover .player-volume-slider {
|
|
232
|
-
|
|
242
|
+
/* Undo .visuallyhidden */
|
|
243
|
+
clip: unset;
|
|
244
|
+
height: 4px;
|
|
245
|
+
margin-right: 24px; /* leave room for the button */
|
|
246
|
+
overflow: visible;
|
|
247
|
+
position: relative;
|
|
248
|
+
width: 100%;
|
|
233
249
|
}
|
|
234
250
|
|
|
235
|
-
.player-threedot-menu
|
|
251
|
+
.player-threedot-menu a:focus,
|
|
252
|
+
.player-threedot-menu a:hover,
|
|
253
|
+
.player-threedot-menu button:focus,
|
|
254
|
+
.player-threedot-menu button:hover {
|
|
236
255
|
background: var(--sds-g-gray-4);
|
|
237
256
|
}
|
|
238
257
|
}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div
|
|
3
|
+
aria-label={playerAriaLabel}
|
|
4
|
+
class="custom-audio-player"
|
|
5
|
+
lwc:ref="container"
|
|
6
|
+
onkeyup={handleContainerKeyUp}
|
|
7
|
+
tabindex="0"
|
|
8
|
+
>
|
|
3
9
|
<dx-icon
|
|
4
10
|
class="listen-icon"
|
|
5
11
|
size="xsmall"
|
|
@@ -8,24 +14,13 @@
|
|
|
8
14
|
></dx-icon>
|
|
9
15
|
<span class="listen-text">Listen to this article</span>
|
|
10
16
|
<div class="player">
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
></dx-button>
|
|
19
|
-
</template>
|
|
20
|
-
<template lwc:else>
|
|
21
|
-
<dx-button
|
|
22
|
-
aria-label="Play"
|
|
23
|
-
class="player-play-button"
|
|
24
|
-
icon-symbol="play"
|
|
25
|
-
variant="custom"
|
|
26
|
-
onclick={handleAudioPlay}
|
|
27
|
-
></dx-button>
|
|
28
|
-
</template>
|
|
17
|
+
<dx-button
|
|
18
|
+
aria-label={mainControlAriaLabel}
|
|
19
|
+
class="player-play-pause-button"
|
|
20
|
+
icon-symbol={mainControlIconSymbol}
|
|
21
|
+
variant="custom"
|
|
22
|
+
onclick={handleMainControlClick}
|
|
23
|
+
></dx-button>
|
|
29
24
|
<div class="player-time">
|
|
30
25
|
<span class="player-current-time">{formattedCurrentTime}</span>
|
|
31
26
|
/
|
|
@@ -41,13 +36,14 @@
|
|
|
41
36
|
onchange={handleSeekChange}
|
|
42
37
|
oninput={handleSeekInput}
|
|
43
38
|
/>
|
|
44
|
-
<div class="player-volume-container">
|
|
39
|
+
<div class="player-volume-container" lwc:ref="playerVolumeContainer" onfocusin={handleVolumeFocusIn} onfocusout={handleVolumeFocusOut}>
|
|
45
40
|
<input
|
|
46
41
|
aria-label="Volume Level"
|
|
47
42
|
class="player-volume-slider"
|
|
48
43
|
type="range"
|
|
49
44
|
max="100"
|
|
50
45
|
lwc:ref="playerVolumeSlider"
|
|
46
|
+
tabindex="0"
|
|
51
47
|
value={volume}
|
|
52
48
|
oninput={handleVolumeInput}
|
|
53
49
|
/>
|
|
@@ -70,7 +66,7 @@
|
|
|
70
66
|
slot="control"
|
|
71
67
|
variant="custom"
|
|
72
68
|
></dx-button>
|
|
73
|
-
<div class="player-threedot-menu" slot="content">
|
|
69
|
+
<div class="player-threedot-menu" lwc:ref="playbackSpeedMenu" slot="content" tabindex="0">
|
|
74
70
|
<template lwc:if={isSettingPlaybackSpeed}>
|
|
75
71
|
<div>
|
|
76
72
|
<ul>
|
|
@@ -91,9 +87,11 @@
|
|
|
91
87
|
>
|
|
92
88
|
<li
|
|
93
89
|
key={playbackSpeedDatum.value}
|
|
94
|
-
class="player-speed-item"
|
|
95
90
|
>
|
|
96
|
-
<button
|
|
91
|
+
<button
|
|
92
|
+
class="player-speed-item"
|
|
93
|
+
onclick={handleSetPlaybackSpeed}
|
|
94
|
+
>
|
|
97
95
|
{playbackSpeedDatum.value}
|
|
98
96
|
</button>
|
|
99
97
|
<dx-icon
|
|
@@ -113,6 +111,7 @@
|
|
|
113
111
|
<a
|
|
114
112
|
href={audioSrc}
|
|
115
113
|
download
|
|
114
|
+
tabindex="0"
|
|
116
115
|
target="_blank"
|
|
117
116
|
rel="noopener"
|
|
118
117
|
>
|
|
@@ -9,6 +9,7 @@ type TrackColors = {
|
|
|
9
9
|
buffer?: string;
|
|
10
10
|
after: string;
|
|
11
11
|
};
|
|
12
|
+
// eslint-disable-next-line no-use-before-define
|
|
12
13
|
type PlaybackSpeed = (typeof formattedPlaybackSpeeds)[number];
|
|
13
14
|
|
|
14
15
|
/*
|
|
@@ -46,12 +47,15 @@ const formattedPlaybackSpeeds = [
|
|
|
46
47
|
|
|
47
48
|
export default class Audio extends LightningElementWithTypedRefs<{
|
|
48
49
|
audioElement: HTMLAudioElement;
|
|
50
|
+
container: HTMLDivElement;
|
|
51
|
+
playbackSpeedMenu: HTMLDivElement;
|
|
49
52
|
playbackSpeedPopover: Popover;
|
|
50
53
|
playerSeekSlider: HTMLInputElement;
|
|
51
54
|
playerVolumeSlider: HTMLInputElement;
|
|
55
|
+
playerVolumeContainer: HTMLDivElement;
|
|
52
56
|
}> {
|
|
53
57
|
@api audioSrc!: string;
|
|
54
|
-
@api
|
|
58
|
+
@api audioTitle!: string;
|
|
55
59
|
|
|
56
60
|
private _bufferedTimeRanges?: TimeRanges;
|
|
57
61
|
private _currentTimeSeconds = 0;
|
|
@@ -60,6 +64,7 @@ export default class Audio extends LightningElementWithTypedRefs<{
|
|
|
60
64
|
private animationFrameId: number | null = null; // controls movement of the timeline when audio is playing
|
|
61
65
|
private currentPlaybackSpeed: PlaybackSpeed = "Normal";
|
|
62
66
|
private didRender = false;
|
|
67
|
+
private isAnimating = false;
|
|
63
68
|
private isPlaying = false;
|
|
64
69
|
private isSettingPlaybackSpeed = false; // popover menu has two panes; this manages that state
|
|
65
70
|
// `playbackSpeedData` tracks which value is selected, in a way useable by LWC for:each without requiring a separate sub-component
|
|
@@ -78,6 +83,18 @@ export default class Audio extends LightningElementWithTypedRefs<{
|
|
|
78
83
|
return this.getFormatted(this.durationSeconds);
|
|
79
84
|
}
|
|
80
85
|
|
|
86
|
+
private get mainControlAriaLabel() {
|
|
87
|
+
return this.isPlaying ? "Pause" : "Play";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private get mainControlIconSymbol() {
|
|
91
|
+
return this.isPlaying ? "pause" : "play";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private get playerAriaLabel() {
|
|
95
|
+
return `Audio: ${this.audioTitle}`;
|
|
96
|
+
}
|
|
97
|
+
|
|
81
98
|
// NOTE that values with getters and setters here have side effects that update the UI elements.
|
|
82
99
|
// This essentially makes these items the source of truth for the UI.
|
|
83
100
|
private get bufferedTimeRanges() {
|
|
@@ -176,6 +193,14 @@ export default class Audio extends LightningElementWithTypedRefs<{
|
|
|
176
193
|
this.bufferedTimeRanges = this.typedRefs.audioElement.buffered;
|
|
177
194
|
};
|
|
178
195
|
|
|
196
|
+
handleMainControlClick = (event: Event) => {
|
|
197
|
+
if (this.isPlaying) {
|
|
198
|
+
this.handleAudioPause(event);
|
|
199
|
+
} else {
|
|
200
|
+
this.handleAudioPlay(event);
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
|
|
179
204
|
handleAudioPlay = (event: Event) => {
|
|
180
205
|
this.typedRefs.audioElement.play();
|
|
181
206
|
this.syncTimeWithAudio();
|
|
@@ -253,9 +278,34 @@ export default class Audio extends LightningElementWithTypedRefs<{
|
|
|
253
278
|
}
|
|
254
279
|
};
|
|
255
280
|
|
|
281
|
+
handleVolumeFocusIn = () => {
|
|
282
|
+
this.typedRefs.playerVolumeContainer.classList.add(
|
|
283
|
+
"focused-by-keyboard"
|
|
284
|
+
);
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
handleVolumeFocusOut = (event: FocusEvent) => {
|
|
288
|
+
const { playerVolumeContainer } = this.typedRefs;
|
|
289
|
+
const isFocusingChildOfVolumeContainer = Array.from(
|
|
290
|
+
playerVolumeContainer.children
|
|
291
|
+
).some((childElement) => event.relatedTarget === childElement);
|
|
292
|
+
|
|
293
|
+
if (!isFocusingChildOfVolumeContainer) {
|
|
294
|
+
this.typedRefs.playerVolumeContainer.classList.remove(
|
|
295
|
+
"focused-by-keyboard"
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
256
300
|
// Set "pane" state for playback popover menu
|
|
257
301
|
handlePlaybackSpeedClick = () => {
|
|
258
302
|
this.isSettingPlaybackSpeed = true;
|
|
303
|
+
this.refocusPlaybackSpeedMenu();
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
// Sets "pane" state for popover back to the default
|
|
307
|
+
resetIsSettingPlaybackSpeed = () => {
|
|
308
|
+
this.isSettingPlaybackSpeed = false;
|
|
259
309
|
};
|
|
260
310
|
|
|
261
311
|
// Handle selection of a playback speed in the "three dot" menu
|
|
@@ -281,13 +331,23 @@ export default class Audio extends LightningElementWithTypedRefs<{
|
|
|
281
331
|
: parseFloat(selectedPlaybackSpeed);
|
|
282
332
|
};
|
|
283
333
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
334
|
+
handleContainerKeyUp = (event: KeyboardEvent) => {
|
|
335
|
+
if (event.key === " " && event.target === this.typedRefs.container) {
|
|
336
|
+
if (this.isPlaying) {
|
|
337
|
+
this.handleAudioPause(event);
|
|
338
|
+
} else {
|
|
339
|
+
this.handleAudioPlay(event);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
287
342
|
};
|
|
288
343
|
|
|
289
344
|
/* END event handlers, BEGIN private utility methods */
|
|
290
345
|
|
|
346
|
+
// Called when menu's inner pane changes, ensuring focus is on the newly-displayed menu
|
|
347
|
+
private refocusPlaybackSpeedMenu = () => {
|
|
348
|
+
Promise.resolve().then(() => this.typedRefs.playbackSpeedMenu.focus());
|
|
349
|
+
};
|
|
350
|
+
|
|
291
351
|
// Animates the seek slider (timeline) each frame so that it is N*Sync with current play
|
|
292
352
|
// time. We do this using requestAnimationFrame so that we can temporarily cancel the
|
|
293
353
|
// animating whenever the user is interacting with the slider.
|
|
@@ -378,7 +438,7 @@ export default class Audio extends LightningElementWithTypedRefs<{
|
|
|
378
438
|
const payload = {
|
|
379
439
|
event: eventType,
|
|
380
440
|
media_action: action,
|
|
381
|
-
media_name: this.
|
|
441
|
+
media_name: this.audioTitle,
|
|
382
442
|
media_percentage_played:
|
|
383
443
|
this.durationSeconds === 0
|
|
384
444
|
? this.durationSeconds
|
|
@@ -14,6 +14,7 @@ export default class CardBlogPost extends LightningElement {
|
|
|
14
14
|
@api title!: string;
|
|
15
15
|
@api authors?: Array<any> | null = null;
|
|
16
16
|
@api origin?: string = "wordpress";
|
|
17
|
+
@api clickEvent?: () => void;
|
|
17
18
|
|
|
18
19
|
// LWC is being compiled so that datetime is being interpreted as date-time
|
|
19
20
|
// ONLY from from the implementation in dx-card-blog-post-provider
|
|
@@ -22,6 +23,10 @@ export default class CardBlogPost extends LightningElement {
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
private onLinkClick(event: Event) {
|
|
26
|
+
if (this.clickEvent) {
|
|
27
|
+
this.clickEvent();
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
const payload = {
|
|
26
31
|
click_text: this.title,
|
|
27
32
|
click_url: `${window.location.origin}${this.href}`,
|
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
import { LightningElement, api } from "lwc";
|
|
2
2
|
|
|
3
3
|
const SEARCH_HUB = "developerWebsiteBlogs";
|
|
4
|
+
|
|
5
|
+
interface Recommendation {
|
|
6
|
+
originalCoveoData: {
|
|
7
|
+
uri: string;
|
|
8
|
+
title: string;
|
|
9
|
+
searchUid: string;
|
|
10
|
+
raw: {
|
|
11
|
+
permanentId: string;
|
|
12
|
+
};
|
|
13
|
+
"@source": string;
|
|
14
|
+
};
|
|
15
|
+
clickEvent: () => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
4
18
|
export default class CoveoRecommendations extends LightningElement {
|
|
5
19
|
@api coveoAuthToken!: string;
|
|
6
20
|
@api coveoOrganizationId!: string;
|
|
7
21
|
|
|
8
|
-
_recommendations = [] as
|
|
22
|
+
_recommendations = [] as Recommendation[];
|
|
9
23
|
|
|
10
24
|
private get recommendations(): any[] {
|
|
11
25
|
return this._recommendations || [];
|
|
@@ -46,10 +60,17 @@ export default class CoveoRecommendations extends LightningElement {
|
|
|
46
60
|
|
|
47
61
|
const blogDataLoadTasks = results
|
|
48
62
|
.slice(0, 3)
|
|
49
|
-
.map(async (rec: any) => {
|
|
63
|
+
.map(async (rec: any, index: number) => {
|
|
50
64
|
const slug = rec.uri.split("/").pop();
|
|
51
65
|
const blogDataUrl = `https://developer.salesforce.com/blogs/wp-json/wp/v2/posts?slug=${slug}&state=publish&_embed=wp:featuredmedia`;
|
|
52
|
-
|
|
66
|
+
const wordPressData = (
|
|
67
|
+
await (await fetch(blogDataUrl)).json()
|
|
68
|
+
)[0];
|
|
69
|
+
wordPressData.originalCoveoData = rec;
|
|
70
|
+
wordPressData.originalCoveoData.searchUid =
|
|
71
|
+
json.searchUid;
|
|
72
|
+
wordPressData.clickEvent =
|
|
73
|
+
this.buildClickAnalyticsLogger(index);
|
|
53
74
|
});
|
|
54
75
|
this._recommendations = await Promise.all(
|
|
55
76
|
blogDataLoadTasks
|
|
@@ -92,4 +113,38 @@ export default class CoveoRecommendations extends LightningElement {
|
|
|
92
113
|
}
|
|
93
114
|
);
|
|
94
115
|
};
|
|
116
|
+
|
|
117
|
+
buildClickAnalyticsLogger = (index: number) => () => {
|
|
118
|
+
const payload = {
|
|
119
|
+
anonymous: true,
|
|
120
|
+
documentPosition: index,
|
|
121
|
+
documentTitle: this._recommendations[index].originalCoveoData.title,
|
|
122
|
+
documentUrl: this._recommendations[index].originalCoveoData.uri,
|
|
123
|
+
language: "en",
|
|
124
|
+
originLevel1: SEARCH_HUB,
|
|
125
|
+
originLevel2: SEARCH_HUB,
|
|
126
|
+
actionCause: "recommendationOpen",
|
|
127
|
+
queryText: "", // This has to be included, but is empty, because recommendations use the 'search' endpoint with an empty query string
|
|
128
|
+
searchQueryUid: this._recommendations[index].originalCoveoData.uri,
|
|
129
|
+
sourceName:
|
|
130
|
+
this._recommendations[index].originalCoveoData["@source"],
|
|
131
|
+
customData: {
|
|
132
|
+
contentIDKey: "permanentId",
|
|
133
|
+
contentIDValue:
|
|
134
|
+
this._recommendations[index].originalCoveoData.raw
|
|
135
|
+
.permanentId
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
fetch(
|
|
139
|
+
`https://${this.coveoOrganizationId}.analytics.org.coveo.com/rest/ua/v15/analytics/click`,
|
|
140
|
+
{
|
|
141
|
+
headers: {
|
|
142
|
+
Authorization: `Bearer ${this.coveoAuthToken}`,
|
|
143
|
+
"Content-Type": "application/json"
|
|
144
|
+
},
|
|
145
|
+
method: "POST",
|
|
146
|
+
body: JSON.stringify(payload)
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
};
|
|
95
150
|
}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from "typings/custom";
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
|
+
autoUpdate,
|
|
9
10
|
computePosition,
|
|
10
11
|
flip,
|
|
11
12
|
size,
|
|
@@ -31,6 +32,8 @@ const isEventOutsideElements = (
|
|
|
31
32
|
);
|
|
32
33
|
|
|
33
34
|
export default class Popover extends LightningElement {
|
|
35
|
+
private autoUpdateCleanup?: () => void;
|
|
36
|
+
|
|
34
37
|
@api offset?: "small" | "medium";
|
|
35
38
|
@api pagePadding?: number = 16; // padding between dropdown and the edge of the page
|
|
36
39
|
@api placement?: PopperPlacement = "bottom-start";
|
|
@@ -76,6 +79,10 @@ export default class Popover extends LightningElement {
|
|
|
76
79
|
this._open = true;
|
|
77
80
|
this.control.setAttribute("aria-expanded", "true");
|
|
78
81
|
|
|
82
|
+
if (this.popover) {
|
|
83
|
+
this.autoUpdateCleanup = autoUpdate(this.control, this.popover, this.setPosition);
|
|
84
|
+
}
|
|
85
|
+
|
|
79
86
|
this.dispatchEvent(new CustomEvent("open"));
|
|
80
87
|
|
|
81
88
|
setTimeout(() => {
|
|
@@ -88,6 +95,7 @@ export default class Popover extends LightningElement {
|
|
|
88
95
|
@api
|
|
89
96
|
closePopover(focusControl: boolean = false) {
|
|
90
97
|
this._open = false;
|
|
98
|
+
this.autoUpdateCleanup?.();
|
|
91
99
|
if (focusControl && this.control && this.control.focus) {
|
|
92
100
|
this.control.focus();
|
|
93
101
|
}
|