@searchstax-inc/searchstudio-ux-vue 1.0.48 → 2.0.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.
Files changed (50) hide show
  1. package/README.md +1084 -529
  2. package/README.mustache +644 -0
  3. package/dist/@searchstax-inc/components/SearchstaxAnswerWidget.vue.d.ts +1 -1
  4. package/dist/@searchstax-inc/components/SearchstaxAnswerWidget.vue.d.ts.map +1 -1
  5. package/dist/@searchstax-inc/components/SearchstaxExternalPromotionsWidget.vue.d.ts +1 -1
  6. package/dist/@searchstax-inc/components/SearchstaxExternalPromotionsWidget.vue.d.ts.map +1 -1
  7. package/dist/@searchstax-inc/components/SearchstaxFacetsWidget.vue.d.ts +16 -1
  8. package/dist/@searchstax-inc/components/SearchstaxFacetsWidget.vue.d.ts.map +1 -1
  9. package/dist/@searchstax-inc/components/SearchstaxInputWidget.vue.d.ts.map +1 -1
  10. package/dist/@searchstax-inc/components/SearchstaxLocationWidget.vue.d.ts +1 -1
  11. package/dist/@searchstax-inc/components/SearchstaxLocationWidget.vue.d.ts.map +1 -1
  12. package/dist/@searchstax-inc/components/SearchstaxPaginationWidget.vue.d.ts +1 -1
  13. package/dist/@searchstax-inc/components/SearchstaxPaginationWidget.vue.d.ts.map +1 -1
  14. package/dist/@searchstax-inc/components/SearchstaxRelatedSearchesWidget.vue.d.ts +1 -1
  15. package/dist/@searchstax-inc/components/SearchstaxRelatedSearchesWidget.vue.d.ts.map +1 -1
  16. package/dist/@searchstax-inc/components/SearchstaxResultWidget.vue.d.ts +10 -1
  17. package/dist/@searchstax-inc/components/SearchstaxResultWidget.vue.d.ts.map +1 -1
  18. package/dist/@searchstax-inc/components/SearchstaxSearchFeedbackWidget.vue.d.ts.map +1 -1
  19. package/dist/@searchstax-inc/components/SearchstaxSortingWidget.vue.d.ts.map +1 -1
  20. package/dist/@searchstax-inc/components/templates/SearchstaxAnswerWidgetTemplate.vue.d.ts +22 -0
  21. package/dist/@searchstax-inc/components/templates/SearchstaxAnswerWidgetTemplate.vue.d.ts.map +1 -0
  22. package/dist/@searchstax-inc/components/templates/SearchstaxExternalPromotionsWidgetTemplate.vue.d.ts +22 -0
  23. package/dist/@searchstax-inc/components/templates/SearchstaxExternalPromotionsWidgetTemplate.vue.d.ts.map +1 -0
  24. package/dist/@searchstax-inc/components/templates/SearchstaxFacetsWidgetDesktopTemplate.vue.d.ts +64 -0
  25. package/dist/@searchstax-inc/components/templates/SearchstaxFacetsWidgetDesktopTemplate.vue.d.ts.map +1 -0
  26. package/dist/@searchstax-inc/components/templates/SearchstaxFacetsWidgetMobileTemplate.vue.d.ts +112 -0
  27. package/dist/@searchstax-inc/components/templates/SearchstaxFacetsWidgetMobileTemplate.vue.d.ts.map +1 -0
  28. package/dist/@searchstax-inc/components/templates/SearchstaxInputWidgetTemplate.vue.d.ts +38 -0
  29. package/dist/@searchstax-inc/components/templates/SearchstaxInputWidgetTemplate.vue.d.ts.map +1 -0
  30. package/dist/@searchstax-inc/components/templates/SearchstaxLocationWidgetTemplate.vue.d.ts +70 -0
  31. package/dist/@searchstax-inc/components/templates/SearchstaxLocationWidgetTemplate.vue.d.ts.map +1 -0
  32. package/dist/@searchstax-inc/components/templates/SearchstaxPaginationWidgetDefaultTemplate.vue.d.ts +30 -0
  33. package/dist/@searchstax-inc/components/templates/SearchstaxPaginationWidgetDefaultTemplate.vue.d.ts.map +1 -0
  34. package/dist/@searchstax-inc/components/templates/SearchstaxPaginationWidgetInfiniteScrollTemplate.vue.d.ts +13 -0
  35. package/dist/@searchstax-inc/components/templates/SearchstaxPaginationWidgetInfiniteScrollTemplate.vue.d.ts.map +1 -0
  36. package/dist/@searchstax-inc/components/templates/SearchstaxRelatedSearchesWidgetTemplate.vue.d.ts +22 -0
  37. package/dist/@searchstax-inc/components/templates/SearchstaxRelatedSearchesWidgetTemplate.vue.d.ts.map +1 -0
  38. package/dist/@searchstax-inc/components/templates/SearchstaxResultWidgetDefaultTemplate.vue.d.ts +22 -0
  39. package/dist/@searchstax-inc/components/templates/SearchstaxResultWidgetDefaultTemplate.vue.d.ts.map +1 -0
  40. package/dist/@searchstax-inc/components/templates/SearchstaxResultWidgetNoResultsTemplate.vue.d.ts +58 -0
  41. package/dist/@searchstax-inc/components/templates/SearchstaxResultWidgetNoResultsTemplate.vue.d.ts.map +1 -0
  42. package/dist/@searchstax-inc/components/templates/SearchstaxSearchFeedbackWidgetTemplate.vue.d.ts +22 -0
  43. package/dist/@searchstax-inc/components/templates/SearchstaxSearchFeedbackWidgetTemplate.vue.d.ts.map +1 -0
  44. package/dist/@searchstax-inc/components/templates/SearchstaxSortingWidgetTemplate.vue.d.ts +30 -0
  45. package/dist/@searchstax-inc/components/templates/SearchstaxSortingWidgetTemplate.vue.d.ts.map +1 -0
  46. package/dist/@searchstax-inc/searchstudio-ux-vue.cjs +70 -68
  47. package/dist/@searchstax-inc/searchstudio-ux-vue.iife.js +65 -63
  48. package/dist/@searchstax-inc/searchstudio-ux-vue.mjs +3296 -2681
  49. package/dist/@searchstax-inc/stores/searchstaxStore.d.ts +9 -0
  50. package/package.json +4 -4
package/README.md CHANGED
@@ -55,7 +55,7 @@ Initialization example
55
55
  ```
56
56
  sampleConfig = {
57
57
  language: "en",
58
- model: "",
58
+ model: "Main Profile",
59
59
  searchURL: "",
60
60
  suggesterURL: "",
61
61
  trackApiKey: "",
@@ -156,124 +156,90 @@ Following widgets are available:
156
156
  [sorting Widget](#sorting-widget)
157
157
 
158
158
  ### Answer Widget ###
159
+ SearchStax Site Search solution offers Vue widgets to assist in building your custom search page.
160
+
161
+ The SearchstaxAnswerWidget component for Vue provides an AI answer widget for your searches.
162
+
163
+ **Usage**
159
164
 
160
- example of answer widget initialization with minimum options
161
165
  ```
162
- <SearchstaxAnswerWidget :showShowMoreAfterWordCount="10"></SearchstaxAnswerWidget>
166
+ <SearchstaxAnswerWidget :showShowMoreAfterWordCount="10"></SearchstaxAnswerWidget>
163
167
  ```
164
168
 
169
+ **Props**
170
+
171
+ - showShowMoreAfterWordCount - number(default 100) determining after how many symbols UI will show “Read More” view.
172
+ - feedbackWidget – an optional object that configures thumbs-up and thumbs-down feedback functionality.
165
173
 
166
- example of answer widget initialization with lightweight feedback widget
174
+ Example of feedbackWidget config:
167
175
  ```
168
- feedbackConfig = {
176
+ const feedbackConfig = {
169
177
  renderFeedbackWidget: true,
170
- emailOverride: () => '',
178
+ emailOverride: searchstaxEmailOverride,
171
179
  thumbsUpValue: 10,
172
- thumbsDownValue: 0,
173
- lightweightTemplateOverride: `
174
- <div class="searchstax-lightweight-widget-container">
175
- <div class="searchstax-lightweight-widget-thumbs-up {{#thumbsUpActive}}active{{/thumbsUpActive}}">
176
- <svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
177
- <path d="M4.85079 7.59996L7.83141 1C8.4243 1 8.9929 1.23178 9.41213 1.64436C9.83136 2.05694 10.0669 2.61651 10.0669 3.19999V6.1333H14.2845C14.5005 6.13089 14.7145 6.17474 14.9116 6.26179C15.1087 6.34885 15.2842 6.47704 15.426 6.63747C15.5677 6.79791 15.6723 6.98676 15.7326 7.19094C15.7928 7.39513 15.8072 7.60975 15.7748 7.81996L14.7465 14.4199C14.6926 14.7696 14.5121 15.0884 14.2382 15.3175C13.9643 15.5466 13.6156 15.6706 13.2562 15.6666H4.85079M4.85079 7.59996V15.6666M4.85079 7.59996H2.61531C2.22006 7.59996 1.84099 7.75448 1.5615 8.02953C1.28201 8.30458 1.125 8.67763 1.125 9.06661V14.1999C1.125 14.5889 1.28201 14.9619 1.5615 15.237C1.84099 15.512 2.22006 15.6666 2.61531 15.6666H4.85079" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
178
- </svg>
179
- </div>
180
- <div class="searchstax-lightweight-widget-separator"></div>
181
- <div class="searchstax-lightweight-widget-thumbs-down {{#thumbsDownActive}}active{{/thumbsDownActive}}">
182
- <svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
183
- <path d="M12.1492 9.06801L9.16859 15.668C8.5757 15.668 8.0071 15.4362 7.58787 15.0236C7.16864 14.611 6.93311 14.0515 6.93311 13.468V10.5347H2.71552C2.4995 10.5371 2.28552 10.4932 2.08842 10.4062C1.89132 10.3191 1.71581 10.1909 1.57405 10.0305C1.43229 9.87006 1.32766 9.6812 1.26743 9.47702C1.2072 9.27284 1.19279 9.05822 1.22521 8.84801L2.25353 2.24806C2.30742 1.89833 2.48793 1.57955 2.76179 1.35046C3.03566 1.12136 3.38443 0.997398 3.74384 1.0014H12.1492M12.1492 9.06801V1.0014M12.1492 9.06801H14.3847C14.7799 9.06801 15.159 8.91349 15.4385 8.63844C15.718 8.36339 15.875 7.99034 15.875 7.60135V2.46805C15.875 2.07907 15.718 1.70602 15.4385 1.43097C15.159 1.15592 14.7799 1.0014 14.3847 1.0014H12.1492" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
184
- </svg>
185
-
186
- </div>
187
- </div>
188
- `
180
+ thumbsDownValue: 0
189
181
  }
190
- <SearchstaxAnswerWidget :showShowMoreAfterWordCount="100" :feedbackwidget="feedbackConfig">
191
- <template #answer="{ answerData, showMore }">
192
- <div v-if="answerData && answerData?.searchExecuted && answerData.shouldShowAnswer">
193
- <div class="searchstax-answer-wrap">
194
- <div class="searchstax-answer-icon"></div>
195
- <div>
196
- <div
197
- :class="
198
- 'searchstax-answer-container ' + answerData.showMoreButtonVisible
199
- ? 'searchstax-answer-show-more'
200
- : ''
201
- "
202
- >
203
- <div class="searchstax-answer-title">Smart Answers</div>
204
- <div class="searchstax-answer-error" v-if="answerData.shouldShowAnswerError" v-html="answerData.answerErrorMessage"></div>
205
- <div
206
- class="searchstax-answer-description"
207
- v-html="answerData.fullAnswerFormatted"
208
- ></div>
209
- <div v-if="answerData.answerLoading" class="searchstax-answer-loading"></div>
210
- </div>
211
- <div
212
- v-if="answerData.showMoreButtonVisible"
213
- class="searchstax-answer-load-more-button-container"
214
- >
215
- <button class="searchstax-answer-load-more-button" @click="showMore">
216
- Read More
217
- </button>
218
- </div>
219
- </div>
220
- <div class="searchstax-answer-footer">
221
- <div id="feedbackWidgetContainer"></div>
222
- <div class="searchstax-lightweight-widget-separator-inline"></div>
223
- <p class="searchstax-disclaimer">Generative AI is Experimental</p>
224
- </div>
225
- </div>
226
- </div>
227
- </template>
228
- </SearchstaxAnswerWidget>
229
182
  ```
230
183
 
231
- example of answer widget initialization without feedback widget
184
+
185
+ **searchAnswerTemplate**
186
+
187
+ The templates prop allows customizing the answer UI.
188
+
189
+ It receives the following props:
190
+
191
+ - answerData – Data object of type: ISearchstaxAnswerData
192
+ - showMore - Handler for exiting the show more view
193
+
194
+ **Example**
195
+
232
196
  ```
233
- <SearchstaxAnswerWidget :showShowMoreAfterWordCount="10">
197
+ <SearchstaxAnswerWidget :showShowMoreAfterWordCount="100" :feedbackwidget="feedbackConfig">
234
198
  <template #answer="{ answerData, showMore }">
235
199
  <div v-if="answerData && answerData?.searchExecuted && answerData.shouldShowAnswer">
236
- <div class="searchstax-answer-wrap">
237
- <div class="searchstax-answer-icon"></div>
238
- <div>
239
- <div
240
- :class="
241
- 'searchstax-answer-container ' + answerData.showMoreButtonVisible
242
- ? 'searchstax-answer-show-more'
243
- : ''
244
- "
245
- >
246
- <div class="searchstax-answer-title">Smart Answers</div>
247
- <div class="searchstax-answer-error" v-if="answerData.shouldShowAnswerError" v-html="answerData.answerErrorMessage"></div>
248
- <div
249
- class="searchstax-answer-description"
250
- v-html="answerData.fullAnswerFormatted"
251
- ></div>
252
- <div v-if="answerData.answerLoading" class="searchstax-answer-loading"></div>
253
- </div>
254
- <div
255
- v-if="answerData.showMoreButtonVisible"
256
- class="searchstax-answer-load-more-button-container"
257
- >
258
- <button class="searchstax-answer-load-more-button" @click="showMore">
259
- Read More
260
- </button>
261
- </div>
262
- </div>
263
- <div class="searchstax-answer-footer">
264
- <div id="feedbackWidgetContainer"></div>
265
- <div class="searchstax-lightweight-widget-separator-inline"></div>
266
- <p class="searchstax-disclaimer">Generative AI is Experimental</p>
267
- </div>
268
- </div>
200
+ <div class="searchstax-answer-wrap">
201
+ <div class="searchstax-answer-icon"></div>
202
+ <div>
203
+ <div
204
+ :class="{
205
+ 'searchstax-answer-container': true,
206
+ 'searchstax-answer-show-more': answerData.showMoreButtonVisible
207
+ }"
208
+ >
209
+ <div class="searchstax-answer-title">Smart Answers</div>
210
+ <div
211
+ class="searchstax-answer-error"
212
+ v-if="answerData.shouldShowAnswerError"
213
+ v-html="answerData.answerErrorMessage"
214
+ ></div>
215
+ <div class="searchstax-answer-description" v-html="answerData.fullAnswerFormatted"></div>
216
+ <div v-if="answerData.answerLoading" class="searchstax-answer-loading"></div>
217
+ </div>
218
+ <div
219
+ v-if="answerData.showMoreButtonVisible"
220
+ class="searchstax-answer-load-more-button-container"
221
+ >
222
+ <button class="searchstax-answer-load-more-button" @click="showMore">Read More</button>
269
223
  </div>
224
+ </div>
225
+ <div class="searchstax-answer-footer">
226
+ <div id="feedbackWidgetContainer"></div>
227
+ <div class="searchstax-lightweight-widget-separator-inline"></div>
228
+ <p class="searchstax-disclaimer">Generative AI is Experimental</p>
229
+ </div>
230
+ </div>
231
+ </div>
270
232
  </template>
271
233
  </SearchstaxAnswerWidget>
272
234
  ```
273
235
 
274
236
  ### Input Widget ###
275
237
 
276
- example of input widget initialization with minimum options
238
+ SearchStax Site Search solution offers a Vue search-input widget to assist with your custom search page.
239
+
240
+ The SearchstaxInputWidget provides a search input box with autosuggest/autocomplete functionality.
241
+
242
+ **Usage**
277
243
  ```
278
244
  <SearchstaxInputWidget
279
245
  :afterAutosuggest="afterAutosuggest"
@@ -282,8 +248,26 @@ example of input widget initialization with minimum options
282
248
  ></SearchstaxInputWidget>
283
249
  ```
284
250
 
251
+ **Props**
252
+
253
+ - suggestAfterMinChars - default 3. Number of characters needed for autosuggest to start triggering
254
+ - beforeAutosuggest - callback function that gets called before firing autosuggest. autosuggestProps are being passed as a property and can be modified, if passed along further search will execute with modified properties, if null is returned then event gets canceled and search never fires.
255
+ - afterAutosuggest - callback function that gets called after autosuggest has values but before rendering. It needs to return same type of data but it can be modified.
256
+ - inputTemplate - template override. look at examples below
285
257
 
286
- example of input widget initialization with template override
258
+
259
+ **inputWidgetTemplate**
260
+
261
+ The inputTemplate prop allows customizing the input UI.
262
+
263
+ It receives the following props:
264
+
265
+ - suggestions – Array of autosuggestion results
266
+ - onMouseLeave - Handler for mouse leave event
267
+ - onMouseOver - Handler for mouse over event
268
+ - onMouseClick - Handler for mouse click event
269
+
270
+ **example**
287
271
  ```
288
272
  <SearchstaxInputWidget
289
273
  :afterAutosuggest="afterAutosuggest"
@@ -292,72 +276,312 @@ example of input widget initialization with template override
292
276
  >
293
277
  <template #input="{ suggestions, onMouseLeave, onMouseOver, onMouseClick }">
294
278
  <div class="searchstax-search-input-wrapper">
295
- <input
296
- type="text"
297
- id="searchstax-search-input"
298
- class="searchstax-search-input"
299
- placeholder="SEARCH FOR..."
300
- />
301
- <div
302
- class="searchstax-autosuggest-container"
303
- :class="{ 'hidden': suggestions.length === 0 }"
304
- @mouseleave="onMouseLeave"
305
- >
306
- <div
307
- class="searchstax-autosuggest-item"
308
- v-for="suggestion in suggestions"
309
- :key="suggestion.term"
310
- >
311
- <div
312
- class="searchstax-autosuggest-item-term-container"
313
- v-html="suggestion.term"
314
- @mouseover="onMouseOver(suggestion)"
315
- @click.stop="onMouseClick()"
316
- ></div>
317
- </div>
318
- </div>
319
- <button
320
- class="searchstax-spinner-icon"
321
- id="searchstax-search-input-action-button"
322
- >
323
- </button>
324
- </div>
279
+ <input
280
+ type="text"
281
+ id="searchstax-search-input"
282
+ class="searchstax-search-input"
283
+ placeholder="SEARCH FOR..."
284
+ aria-label="search"
285
+ />
286
+ <div
287
+ class="searchstax-autosuggest-container"
288
+ :class="{ hidden: suggestions.length === 0 }"
289
+ @mouseleave="onMouseLeave"
290
+ >
291
+ <div
292
+ class="searchstax-autosuggest-item"
293
+ v-for="suggestion in suggestions"
294
+ :key="suggestion.term"
295
+ >
296
+ <div
297
+ class="searchstax-autosuggest-item-term-container"
298
+ v-html="suggestion.term"
299
+ tabindex="0"
300
+ @mouseover="onMouseOver(suggestion)"
301
+ @click.stop="onMouseClick()"
302
+ ></div>
303
+ </div>
304
+ </div>
305
+ </div>
306
+ <SearchstaxLocationWidget />
307
+ <button
308
+ class="searchstax-spinner-icon"
309
+ id="searchstax-search-input-action-button"
310
+ aria-label="search"
311
+ role="button"
312
+ ></button>
325
313
  </template>
326
314
  </SearchstaxInputWidget>
327
315
  ```
328
-
329
316
  ### Location Widget ###
330
317
 
331
- example of location widget initialization with minimum options
332
- ```
333
- function locationDecodeFunction(term: string): Promise<ISearchstaxLocation>{
334
- return new Promise((resolve) => {
335
- // make a request to geocoding API to retrieve lat, lon and address and then resolve promise
336
- resolve({
337
- address:'Address',
338
- lat: lat,
339
- lon: lon,
340
- error: false
341
- });
342
- });
343
- }
344
- function locationDecodeCoordinatesToAddress(lat, lon): Promise<ISearchstaxLocation>{
318
+ SearchStax Site Search solution offers a Vue search-location widget to assist with your custom search page.
319
+
320
+ The SearchstaxLocationWidget provides a location search input with location-based search functionality.
321
+
322
+ **Usage**
323
+
324
+ ```
325
+ const config = {
326
+ appId: "APP_ID",
327
+ relatedSearchesAPIKey: "KEY"
328
+ }
329
+
330
+ const locationWidgetConfig = {
331
+ locationDecode: (term: string): Promise<ISearchstaxLocation> => {
345
332
  return new Promise((resolve) => {
346
- // make a request to geocoding API to convert lat, lon to address and then resolve promise
347
- resolve({
348
- address:'Address',
349
- lat: lat,
350
- lon: lon,
351
- error: false
352
- });
333
+ // make a request to google geocoding API to retrieve lat, lon and address
334
+
335
+ const geocodingAPIKey = "GEOCODING_API_KEY";
336
+ const geocodingURL = `https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(
337
+ term
338
+ )}&key=${geocodingAPIKey}`;
339
+ fetch(geocodingURL)
340
+ .then((response) => response.json())
341
+ .then((data) => {
342
+ if (data.status === "OK" && data.results.length > 0) {
343
+ const result = data.results[0];
344
+ const location = {
345
+ lat: result.geometry.location.lat,
346
+ lon: result.geometry.location.lng,
347
+ address: result.formatted_address,
348
+ };
349
+ resolve(location);
350
+ } else {
351
+ resolve({
352
+ address: undefined,
353
+ lat: undefined,
354
+ lon: undefined,
355
+ error: true,
356
+ });
357
+ }
358
+ })
359
+ .catch(() => {
360
+ resolve({
361
+ address: undefined,
362
+ lat: undefined,
363
+ lon: undefined,
364
+ error: true,
365
+ });
366
+ });
353
367
  });
354
- }
355
- <SearchstaxLocationWidget :locationDecode="locationDecodeFunction" :locationDecodeCoordinatesToAddress="locationDecodeCoordinatesToAddress">
368
+ },
369
+ locationDecodeCoordinatesToAddress: (lat: string, lon: string): Promise<ISearchstaxLocation> => {
370
+ return new Promise((resolve) => {
371
+ fetch(
372
+ `https://geocoding-staging.searchstax.co/reverse?location=${lat},${lon}&components=country:US&app_id=${config.appId}`,
373
+ {
374
+ method: "GET",
375
+ headers: {
376
+ Authorization: `Token ${config.relatedSearchesAPIKey}`,
377
+ },
378
+ }
379
+ )
380
+ .then((response) => response.json())
381
+ .then((data) => {
382
+ if (data.status === "OK" && data.results.length > 0) {
383
+ const result = data.results[0];
384
+ resolve({
385
+ address: result.formatted_address,
386
+ lat: lat,
387
+ lon: lon,
388
+ error: false,
389
+ });
390
+ } else {
391
+ resolve({
392
+ address: undefined,
393
+ lat: lat,
394
+ lon: lon,
395
+ error: true,
396
+ });
397
+ }
398
+ })
399
+ .catch(() => {
400
+ resolve({
401
+ address: undefined,
402
+ lat: lat,
403
+ lon: lon,
404
+ error: true,
405
+ });
406
+ });
407
+ });
408
+ },
409
+ locationSearchEnabled: false,
410
+ locationValuesOverride: {
411
+ locationDistanceEnabled: true,
412
+ filterValues: ["any", "1000"],
413
+ filterUnit: "miles",
414
+ locationFilterDefaultValue: "any"
415
+ },
416
+ }
417
+ <SearchstaxLocationWidget :locationDecode="locationWidgetConfig.locationDecodeFunction" :locationDecodeCoordinatesToAddress="locationWidgetConfig.locationDecodeCoordinatesToAddress" :locationValuesOverride="locationWidgetConfig.locationValuesOverride" :locationSearchEnabled="locationWidgetConfig.locationSearchEnabled">
356
418
  </SearchstaxLocationWidget>
357
419
  ```
358
- example of location widget initialization with template override
420
+
421
+ **Props**
422
+
423
+ - locationDecode - callback function to override location decoding
424
+ - locationDecodeCoordinatesToAddress - callback function to override location decoding
425
+
426
+ **Template Override**
427
+
428
+ The searchLocationTemplate prop allows customizing the location input UI.
429
+
430
+ It receives the following props:
431
+
432
+ - locationData – data containing info on when to show certain elements
433
+ - inputValue – value of location input
434
+ - locationBlur - Handler for location input blur
435
+ - radiusChange - Handler for location radius change
436
+ - selectValue - Value of radius select
437
+ - inputChange - location input change handler
438
+ - locationError - boolean stating if location has error
439
+ - getCurrentLocation - Handler for getting current location from browser
440
+
441
+
442
+ **Example**
443
+
359
444
  ```
360
- <SearchstaxLocationWidget :locationDecode="renderConfig.locationWidget.locationDecode" :locationDecodeCoordinatesToAddress="locationDecodeCoordinatesToAddress">
445
+ const config = {
446
+ appId: "APP_ID",
447
+ relatedSearchesAPIKey: "KEY"
448
+ }
449
+
450
+ const locationWidgetConfig = {
451
+ locationDecode: (term: string): Promise<ISearchstaxLocation> => {
452
+ return new Promise((resolve) => {
453
+ // make a request to google geocoding API to retrieve lat, lon and address
454
+
455
+ const geocodingAPIKey = "GEOCODING_API_KEY";
456
+ const geocodingURL = `https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(
457
+ term
458
+ )}&key=${geocodingAPIKey}`;
459
+ fetch(geocodingURL)
460
+ .then((response) => response.json())
461
+ .then((data) => {
462
+ if (data.status === "OK" && data.results.length > 0) {
463
+ const result = data.results[0];
464
+ const location = {
465
+ lat: result.geometry.location.lat,
466
+ lon: result.geometry.location.lng,
467
+ address: result.formatted_address,
468
+ };
469
+ resolve(location);
470
+ } else {
471
+ resolve({
472
+ address: undefined,
473
+ lat: undefined,
474
+ lon: undefined,
475
+ error: true,
476
+ });
477
+ }
478
+ })
479
+ .catch(() => {
480
+ resolve({
481
+ address: undefined,
482
+ lat: undefined,
483
+ lon: undefined,
484
+ error: true,
485
+ });
486
+ });
487
+ });
488
+ },
489
+ locationDecodeCoordinatesToAddress: (lat: string, lon: string): Promise<ISearchstaxLocation> => {
490
+ return new Promise((resolve) => {
491
+ fetch(
492
+ `https://geocoding-staging.searchstax.co/reverse?location=${lat},${lon}&components=country:US&app_id=${config.appId}`,
493
+ {
494
+ method: "GET",
495
+ headers: {
496
+ Authorization: `Token ${config.relatedSearchesAPIKey}`,
497
+ },
498
+ }
499
+ )
500
+ .then((response) => response.json())
501
+ .then((data) => {
502
+ if (data.status === "OK" && data.results.length > 0) {
503
+ const result = data.results[0];
504
+ resolve({
505
+ address: result.formatted_address,
506
+ lat: lat,
507
+ lon: lon,
508
+ error: false,
509
+ });
510
+ } else {
511
+ resolve({
512
+ address: undefined,
513
+ lat: lat,
514
+ lon: lon,
515
+ error: true,
516
+ });
517
+ }
518
+ })
519
+ .catch(() => {
520
+ resolve({
521
+ address: undefined,
522
+ lat: lat,
523
+ lon: lon,
524
+ error: true,
525
+ });
526
+ });
527
+ });
528
+ },
529
+ locationSearchEnabled: false,
530
+ locationValuesOverride: {
531
+ locationDistanceEnabled: true,
532
+ filterValues: ["any", "1000"],
533
+ filterUnit: "miles",
534
+ locationFilterDefaultValue: "any"
535
+ },
536
+ }
537
+ <div
538
+ class="searchstax-location-input-container"
539
+ data-test-id="searchstax-location-input-container"
540
+ >
541
+ <div class="searchstax-location-input-wrapper">
542
+ <span class="searchstax-location-input-label">NEAR</span>
543
+ <div class="searchstax-location-input-wrapper-inner">
544
+ <input
545
+ type="text"
546
+ id="searchstax-location-input"
547
+ class="searchstax-location-input"
548
+ :class="{ 'searchstax-input-location-error': locationError }"
549
+ placeholder="Zip, Postal Code or City..."
550
+ aria-label="Search Location Input"
551
+ data-test-id="searchstax-location-input"
552
+ @change="inputChange"
553
+ :value="inputValue"
554
+ @blur="locationBlur"
555
+ />
556
+ <button @click="getCurrentLocation" class="searchstax-get-current-location-button">
557
+ Use my current location
558
+ </button>
559
+ </div>
560
+ <span
561
+ v-if="locationData.shouldShowLocationDistanceDropdown"
562
+ class="searchstax-location-input-label"
563
+ >WITHIN</span
564
+ >
565
+ <select
566
+ v-if="locationData.shouldShowLocationDistanceDropdown"
567
+ id="searchstax-location-radius-select"
568
+ class="searchstax-location-radius-select"
569
+ aria-label="Search Location Radius Select"
570
+ data-test-id="searchstax-location-radius-select"
571
+ @change="radiusChange"
572
+ :value="selectValue"
573
+ >
574
+ <option
575
+ v-for="value in locationData.locationSearchDistanceValues"
576
+ :value="value.value"
577
+ :key="value.value"
578
+ >
579
+ {{ value.label }}
580
+ </option>
581
+ </select>
582
+ </div>
583
+ </div>
584
+ <SearchstaxLocationWidget :locationDecode="locationWidgetConfig.locationDecode" :locationDecodeCoordinatesToAddress="locationWidgetConfig.locationDecodeCoordinatesToAddress" :locationValuesOverride="locationWidgetConfig.locationValuesOverride" :locationSearchEnabled="locationWidgetConfig.locationSearchEnabled">
361
585
  <template
362
586
  #location="{
363
587
  locationData,
@@ -370,7 +594,7 @@ example of location widget initialization with template override
370
594
  getCurrentLocation
371
595
  }"
372
596
  >
373
- <div
597
+ <div
374
598
  class="searchstax-location-input-container"
375
599
  data-test-id="searchstax-location-input-container"
376
600
  >
@@ -420,135 +644,338 @@ example of location widget initialization with template override
420
644
  </template>
421
645
  </SearchstaxLocationWidget>
422
646
  ```
647
+
423
648
  ### Result Widget ###
424
649
 
425
- example of results widget initialization with minimum options
650
+ The SearchStax Site Search solution offers a Vue results widget to assist with your custom search page.
651
+
652
+ The SearchstaxResultsWidget component displays the search results.
653
+
654
+ **Usage**
655
+
426
656
  ```
427
657
  <SearchstaxResultWidget :afterLinkClick="afterLinkClick"></SearchstaxResultWidget>
428
658
  ```
429
659
 
430
- example of results widget initialization with infinite scroll behavior
660
+ **Result Template Override**
661
+
662
+ The resultsTemplate prop allows customizing the result UI.
663
+
664
+ It receives following props:
665
+ - searchResults - Array of result items
666
+ - resultClicked - Handler for result click events
667
+
668
+
669
+ **No Results Template Override**
670
+
671
+ The noResultTemplate prop allows customizing the result UI.
672
+
673
+ It receives following props:
674
+
675
+ - searchResults - Array of result items
676
+ - resultClicked - Handler for result click events
677
+ - searchTerm - Search input term
678
+ - metadata - Metadata object
679
+ - executeSearch - Handler for executing srearch
680
+ - store - Main store of app
681
+
682
+ **Example of default render method**
431
683
  ```
432
- <SearchstaxResultWidget :afterLinkClick="afterLinkClick" :renderMethod="'infiniteScroll'"></SearchstaxResultWidget>
684
+ <SearchstaxResultWidget :afterLinkClick="afterLinkClick">
685
+ <template #results="{ searchResults, resultClicked }">
686
+ <div class="searchstax-search-results" v-if="searchResults && searchResults.length">
687
+ <a
688
+ :key="searchResult.uniqueId"
689
+ v-for="searchResult in searchResults"
690
+ :href="searchResult.url"
691
+ :data-searchstax-unique-result-id="searchResult.uniqueId"
692
+ @click="resultClicked(searchResult, $event)"
693
+ @keyup.enter="resultClicked(searchResult, $event)"
694
+ @keyup.space="resultClicked(searchResult, $event)"
695
+ class="searchstax-result-item-link searchstax-result-item-link-wrapping"
696
+ tabindex="0"
697
+ >
698
+ <div
699
+ class="searchstax-search-result"
700
+ :class="{
701
+ 'has-thumbnail': searchResult.thumbnail,
702
+ promoted: searchResult.promoted
703
+ }"
704
+ >
705
+ <div v-if="searchResult.promoted" class="searchstax-search-result-promoted"></div>
706
+ <div
707
+ v-if="searchResult.ribbon"
708
+ v-html="searchResult.ribbon"
709
+ class="searchstax-search-result-ribbon"
710
+ ></div>
711
+ <img
712
+ alt=""
713
+ v-if="searchResult.thumbnail"
714
+ :src="searchResult.thumbnail"
715
+ class="searchstax-thumbnail"
716
+ />
717
+ <div class="searchstax-search-result-title-container">
718
+ <span class="searchstax-search-result-title" v-html="searchResult.title"></span>
719
+ </div>
720
+ <p
721
+ v-if="searchResult.paths"
722
+ v-html="searchResult.paths"
723
+ class="searchstax-search-result-common"
724
+ ></p>
725
+ <p
726
+ v-if="searchResult.description"
727
+ v-html="searchResult.description"
728
+ class="searchstax-search-result-description searchstax-search-result-common"
729
+ ></p>
730
+ <div :key="unmappedField.key" v-for="unmappedField in searchResult.unmappedFields">
731
+ <div
732
+ v-if="unmappedField.isImage && typeof unmappedField.value === 'string'"
733
+ class="searchstax-search-result-image-container"
734
+ >
735
+ <img alt="" :src="unmappedField.value" class="searchstax-result-image" />
736
+ </div>
737
+ <div v-else>
738
+ <p class="searchstax-search-result-common" v-html="unmappedField.value"></p>
739
+ </div>
740
+ </div>
741
+ </div>
742
+ </a>
743
+ </div>
744
+ </template>
745
+ <template #noResult="{ searchResults, resultClicked, searchTerm, metadata, executeSearch, store }">
746
+ <div
747
+ v-if="
748
+ searchResults &&
749
+ searchResults.length === 0 &&
750
+ searchTerm !== '' &&
751
+ searchTerm !== '*' &&
752
+ store.searchstax.dataLayer.parsedData.firstSearchHappened
753
+ "
754
+ >
755
+ <div class="searchstax-no-results">
756
+ Showing <strong>no results</strong> for <strong>"{{ searchTerm }}"</strong>
757
+ <br />
758
+ <span v-if="metadata?.spellingSuggestion"
759
+ >Did you mean
760
+ <a
761
+ href="#"
762
+ class="searchstax-suggestion-term"
763
+ @click="executeSearch(metadata?.spellingSuggestion, $event)"
764
+ >
765
+ {{ metadata?.spellingSuggestion }}</a
766
+ >?</span
767
+ >
768
+ </div>
769
+ <ul>
770
+ <li>
771
+ Try searching for search related terms or topics. We offer a wide variety of content to help
772
+ you get the information you need.
773
+ </li>
774
+ <li>Lost? Click on the ‘X” in the Search Box to reset your search.</li>
775
+ </ul>
776
+ </div>
777
+ </template>
778
+ </SearchstaxResultWidget>
433
779
  ```
434
780
 
435
- example of result widget initialization with template override
781
+ **Example of infinite scroll and pagination render methods**
436
782
  ```
437
- <SearchstaxResultWidget :afterLinkClick="afterLinkClick">
783
+
784
+
785
+ <SearchstaxResultWidget :afterLinkClick="afterLinkClick" :renderMethod="'infiniteScroll'">
438
786
  <template #results="{ searchResults, resultClicked }">
787
+ <div class="searchstax-search-results" v-if="searchResults && searchResults.length">
788
+ <a
789
+ :key="searchResult.uniqueId"
790
+ v-for="searchResult in searchResults"
791
+ :href="searchResult.url"
792
+ :data-searchstax-unique-result-id="searchResult.uniqueId"
793
+ @click="resultClicked(searchResult, $event)"
794
+ @keyup.enter="resultClicked(searchResult, $event)"
795
+ @keyup.space="resultClicked(searchResult, $event)"
796
+ class="searchstax-result-item-link searchstax-result-item-link-wrapping"
797
+ tabindex="0"
798
+ >
799
+ <div
800
+ class="searchstax-search-result"
801
+ :class="{
802
+ 'has-thumbnail': searchResult.thumbnail,
803
+ promoted: searchResult.promoted
804
+ }"
805
+ >
806
+ <div v-if="searchResult.promoted" class="searchstax-search-result-promoted"></div>
807
+ <div
808
+ v-if="searchResult.ribbon"
809
+ v-html="searchResult.ribbon"
810
+ class="searchstax-search-result-ribbon"
811
+ ></div>
812
+ <img
813
+ alt=""
814
+ v-if="searchResult.thumbnail"
815
+ :src="searchResult.thumbnail"
816
+ class="searchstax-thumbnail"
817
+ />
818
+ <div class="searchstax-search-result-title-container">
819
+ <span class="searchstax-search-result-title" v-html="searchResult.title"></span>
820
+ </div>
821
+ <p
822
+ v-if="searchResult.paths"
823
+ v-html="searchResult.paths"
824
+ class="searchstax-search-result-common"
825
+ ></p>
826
+ <p
827
+ v-if="searchResult.description"
828
+ v-html="searchResult.description"
829
+ class="searchstax-search-result-description searchstax-search-result-common"
830
+ ></p>
831
+ <div :key="unmappedField.key" v-for="unmappedField in searchResult.unmappedFields">
832
+ <div
833
+ v-if="unmappedField.isImage && typeof unmappedField.value === 'string'"
834
+ class="searchstax-search-result-image-container"
835
+ >
836
+ <img alt="" :src="unmappedField.value" class="searchstax-result-image" />
837
+ </div>
838
+ <div v-else>
839
+ <p class="searchstax-search-result-common" v-html="unmappedField.value"></p>
840
+ </div>
841
+ </div>
842
+ </div>
843
+ </a>
844
+ </div>
845
+ </template>
846
+ <template #noResult="{ searchResults, resultClicked, searchTerm, metadata, executeSearch, store }">
439
847
  <div
440
- class="searchstax-search-results"
441
- v-if="searchResults && searchResults.length"
442
- >
443
- <div
444
- class="searchstax-search-result"
445
- :class="{ 'has-thumbnail': searchResult.thumbnail }"
446
- :key="searchResult.uniqueId"
447
- v-for="searchResult in searchResults"
448
- >
449
- <div
450
- v-if="searchResult.promoted"
451
- class="searchstax-search-result-promoted"
452
- ></div>
453
- <a
454
- v-if="searchResult.url"
455
- :href="searchResult.url"
456
- :data-searchstax-unique-result-id="searchResult.uniqueId"
457
- @click="resultClicked(searchResult, $event)"
458
- class="searchstax-result-item-link"
459
- ></a>
460
- <div
461
- v-if="searchResult.ribbon"
462
- class="searchstax-search-result-ribbon"
463
- > {{ searchResult.ribbon }} </div>
464
- <img
465
- v-if="searchResult.thumbnail"
466
- :src="searchResult.thumbnail"
467
- class="searchstax-thumbnail"
468
- />
469
- <div class="searchstax-search-result-title-container">
470
- <span class="searchstax-search-result-title">{{ searchResult.title }}</span>
471
- </div>
472
- <p
473
- v-if="searchResult.paths"
474
- class="searchstax-search-result-common"
475
- > {{ searchResult.paths }} </p>
476
- <p
477
- v-if="searchResult.description"
478
- class="searchstax-search-result-description searchstax-search-result-common"
479
- > {{ searchResult.description }} </p>
480
- <div
481
- :key="unmappedField.key"
482
- v-for="unmappedField in searchResult.unmappedFields"
483
- >
484
- <div
485
- v-if="unmappedField.isImage && typeof unmappedField.value === 'string'"
486
- class="searchstax-search-result-image-container"
487
- >
488
- <img
489
- :src="unmappedField.value"
490
- class="searchstax-result-image"
491
- />
492
- </div>
493
- <div v-else>
494
- <p class="searchstax-search-result-common"> {{ unmappedField.value }} </p>
495
- </div>
496
- </div>
497
- </div>
498
- </div>
848
+ v-if="
849
+ searchResults &&
850
+ searchResults.length === 0 &&
851
+ searchTerm !== '' &&
852
+ searchTerm !== '*' &&
853
+ store.searchstax.dataLayer.parsedData.firstSearchHappened
854
+ "
855
+ >
856
+ <div class="searchstax-no-results">
857
+ Showing <strong>no results</strong> for <strong>"{{ searchTerm }}"</strong>
858
+ <br />
859
+ <span v-if="metadata?.spellingSuggestion"
860
+ >Did you mean
861
+ <a
862
+ href="#"
863
+ class="searchstax-suggestion-term"
864
+ @click="executeSearch(metadata?.spellingSuggestion, $event)"
865
+ >
866
+ {{ metadata?.spellingSuggestion }}</a
867
+ >?</span
868
+ >
869
+ </div>
870
+ <ul>
871
+ <li>
872
+ Try searching for search related terms or topics. We offer a wide variety of content to help
873
+ you get the information you need.
874
+ </li>
875
+ <li>Lost? Click on the ‘X” in the Search Box to reset your search.</li>
876
+ </ul>
877
+ </div>
499
878
  </template>
500
879
  </SearchstaxResultWidget>
880
+
501
881
  ```
502
882
 
503
883
  ### Pagination Widget ###
504
884
 
885
+ The SearchStax Site Search solution offers a Vue pagination widget to assist with you custom search page.
886
+
887
+ The SearchstaxPaginationWidget component displays pagination controls for search results.
888
+
889
+ **Usage**
505
890
 
506
- example of pagination widget initialization with minimum options
507
891
  ```
508
892
  <SearchstaxPaginationWidget></SearchstaxPaginationWidget>
509
893
  ```
510
894
 
511
- example of pagination widget initialization with various options
512
- ```
513
- <template #pagination="{ paginationData, previousPage, nextPage }">
514
- <div
515
- class="searchstax-pagination-container"
516
- v-if="paginationData"
517
- >
518
- <div class="searchstax-pagination-content">
519
- <a
520
- class="searchstax-pagination-previous"
521
- :disabled="paginationData?.isFirstPage"
522
- @click="previousPage"
523
- id="searchstax-pagination-previous"
524
- > &lt; Previous </a>
525
- <div class="searchstax-pagination-details"> {{ paginationData?.startResultIndex }} - {{
526
- paginationData?.endResultIndex }} of {{ paginationData?.totalResults }} </div>
527
- <a
528
- class="searchstax-pagination-next"
529
- :disabled="paginationData?.isLastPage"
530
- @click="nextPage"
531
- id="searchstax-pagination-next"
532
- >Next ></a>
533
- </div>
534
- </div>
535
- </template>
536
- ```
537
- example of pagination widget initialization with infinite scroll template override
895
+ **Main Template Override**
896
+
897
+ Main template for the pagination controls.
898
+
899
+ It receives following props:
900
+
901
+ - paginationData – Pagination info object
902
+ - previousPage – Handler for previous page click
903
+ - nextPage - – Handler for next page click
904
+
905
+
906
+ **Infinite Scroll Template Override**
907
+
908
+ Main template for the pagination controls in infinite scroll mode.
909
+
910
+ It receives following props:
911
+ - isLastPage - boolean, true if its last page
912
+ - results - results.length can be used if there are results
913
+
914
+ **Example**
915
+
538
916
  ```
539
- <template #infiniteScroll="{ nextPage }">
540
- <div className="searchstax-pagination-container"
541
- <a
542
- class="searchstax-pagination-load-more"
543
- @click="nextPage"
544
- >Load More override</a>
545
- </div>
546
- </template>
917
+ <SearchstaxPaginationWidget>
918
+ <template #pagination="{ paginationData, previousPage, nextPage }">
919
+ <div class="searchstax-pagination-container" v-if="paginationData">
920
+ <div class="searchstax-pagination-content">
921
+ <a
922
+ class="searchstax-pagination-previous"
923
+ :class="paginationData.isFirstPage ? 'disabled' : ''"
924
+ :disabled="paginationData?.isFirstPage"
925
+ @click="previousPage"
926
+ @keyup.enter="previousPage"
927
+ @keyup.space="previousPage"
928
+ tabindex="0"
929
+ id="searchstax-pagination-previous"
930
+ >
931
+ &lt; Previous
932
+ </a>
933
+ <div class="searchstax-pagination-details">
934
+ {{ paginationData?.startResultIndex }} - {{ paginationData?.endResultIndex }} of
935
+ {{ paginationData?.totalResults }}
936
+ </div>
937
+ <a
938
+ class="searchstax-pagination-next"
939
+ :class="paginationData.isLastPage ? 'disabled' : ''"
940
+ :disabled="paginationData?.isLastPage"
941
+ tabindex="0"
942
+ @click="nextPage"
943
+ @keyup.enter="nextPage"
944
+ @keyup.space="nextPage"
945
+ id="searchstax-pagination-next"
946
+ >Next ></a
947
+ >
948
+ </div>
949
+ </div>
950
+ </template>
951
+ </SearchstaxPaginationWidget>
952
+ // infinite scroll example
953
+ <SearchstaxPaginationWidget>
954
+ <template #infiniteScroll="{ nextPage }">
955
+ <div className="searchstax-pagination-container">
956
+ <a
957
+ class="searchstax-pagination-load-more"
958
+ tabindex="0"
959
+ @click="nextPage"
960
+ @keyup.enter="nextPage"
961
+ @keyup.space="nextPage"
962
+ >Load More</a
963
+ >
964
+ </div>
965
+ </template>
966
+ </SearchstaxPaginationWidget>
547
967
  ```
548
968
 
549
969
  ### Facets Widget ###
550
970
 
551
- example of facets widget initialization with minimum options
971
+ The SearchStax Site Search solution offers a Vue SearchstaxFacetsWidget component to display facets on your custom search page.
972
+
973
+ **Facet Selection and Order**
974
+
975
+ Facet lists are configured and ordered on the Site Search [Faceting Tab](https://www.searchstax.com/docs/searchstudio/faceting-tab/).
976
+
977
+ **Usage**
978
+
552
979
  ```
553
980
  <SearchstaxFacetsWidget
554
981
  :facetingType="'or'"
@@ -557,316 +984,428 @@ example of facets widget initialization with minimum options
557
984
  ></SearchstaxFacetsWidget>
558
985
  ```
559
986
 
987
+ **Props**
988
+
989
+ - facetingType: "and" | "or" | "showUnavailable" | "tabs"; // type that determines how facets will behave
990
+ - specificFacets?: string[]; // optional array of facet names that if provided will only render those facets
991
+ - itemsPerPageDesktop: number; // default expanded facets for desktop
992
+ - itemsPerPageMobile: number; // default expanded facets for mobile
993
+
994
+
995
+ **Main Template Desktop Override**
996
+
997
+ Main wrapper template for desktop facets display.
998
+
999
+ It receives following props:
1000
+ - facetsTemplateDataDesktop - Facets data object
1001
+ - isNotDeactivated - Check if facet group is active
1002
+ - toggleFacetGroup - Toggle facet group active state
1003
+ - isChecked - Check if facet value is selected
1004
+ - selectFacet - Handler for facet select
1005
+ - showMoreLessDesktop - Show more/less facets handler
1006
+ - facetContainers - Object of facet DOM containers
1007
+ - updateRefDesktop - Handler for keeping checkbox references
1008
+
560
1009
 
561
- example of facets widget initialization with template overrides
1010
+ **Main Template Mobile Override**
1011
+
1012
+ Main wrapper template for mobile facets display.
1013
+
1014
+ It receives following props:
1015
+ - facetsTemplateDataMobile - Facets data object
1016
+ - selectedFacetsCheckboxes - Selected facet values
1017
+ - isNotDeactivated - Check if facet group is active
1018
+ - toggleFacetGroup - Toggle facet group active state
1019
+ - isChecked - Check if facet value is selected
1020
+ - selectFacet - Handler for facet select
1021
+ - showMoreLessDesktop - Show more/less facets handler
1022
+ - facetContainers - Object of facet DOM containers
1023
+ - openOverlay - Handler to open mobile overlay
1024
+ - unselectFacet - Handler to unselect specific facet
1025
+ - unselectAll - Handler to unselect all facets
1026
+ - closeOverlay - Handler to close mobile overlay
1027
+ - updateRefMobile - Handler for keeping mobile checkbox references
1028
+
1029
+
1030
+ **Example**
562
1031
  ```
563
- <SearchstaxFacetsWidget
564
- :facetingType="'or'"
565
- :itemsPerPageDesktop="3"
566
- :itemsPerPageMobile="99"
1032
+ <SearchstaxFacetsWidget
1033
+ :facetingType="'or'"
1034
+ :itemsPerPageDesktop="3"
1035
+ :itemsPerPageMobile="99"
1036
+ >
1037
+ <template
1038
+ #desktopFacets="{ facetsTemplateDataDesktop, isNotDeactivated, toggleFacetGroup, isChecked, selectFacet, showMoreLessDesktop, facetContainers, updateRefDesktop }"
1039
+ >
1040
+ <div
1041
+ v-if="facetsTemplateDataDesktop?.hasResultsOrExternalPromotions"
1042
+ class="searchstax-facets-container-desktop"
1043
+ >
1044
+ <div
1045
+ v-for="facet in facetsTemplateDataDesktop.facets"
1046
+ :key="facet.name + 'desktop'"
1047
+ class="searchstax-facet-container"
1048
+ :class="{ active: isNotDeactivated(facet.name) }"
1049
+ >
1050
+ <div>
1051
+ <div class="searchstax-facet-title-container" @click="toggleFacetGroup(facet.name)">
1052
+ <div class="searchstax-facet-title">{{ facet.label }}</div>
1053
+ <div class="searchstax-facet-title-arrow active"></div>
1054
+ </div>
1055
+ <div class="searchstax-facet-values-container" aria-live="polite">
1056
+ <div
1057
+ v-for="(facetValue) in (facet.values as IFacetValueData[])"
1058
+ :key="facetValue.value + facetValue.parentName"
1059
+ class="searchstax-facet-value-container"
1060
+ :class="{ 'searchstax-facet-value-disabled': facetValue.disabled }"
1061
+ :ref="(el) => updateRefDesktop(el, facetValue)"
1062
+ >
1063
+ <div class="searchstax-facet-input">
1064
+ <input
1065
+ type="checkbox"
1066
+ class="searchstax-facet-input-checkbox"
1067
+ :checked="isChecked(facetValue)"
1068
+ :disabled="facetValue.disabled"
1069
+ :aria-label="facetValue.value + ' ' + facetValue.count"
1070
+ @click="selectFacet(facetValue.value + '-desktop', $event, facetValue, true)"
1071
+ />
1072
+ </div>
1073
+ <div
1074
+ class="searchstax-facet-value-label"
1075
+ @click="selectFacet(facetValue.value + '-desktop', $event, facetValue, false)"
1076
+ >
1077
+ {{ facetValue.value }}
1078
+ </div>
1079
+ <div
1080
+ class="searchstax-facet-value-count"
1081
+ @click="selectFacet(facetValue.value + '-desktop', $event, facetValue, false)"
567
1082
  >
568
- <template
569
- #desktopFacets="{ facetsTemplateDataDesktop, isNotDeactivated, toggleFacetGroup, isChecked, selectFacet, showMoreLessDesktop, facetContainers }"
1083
+ ({{ facetValue.count }})
1084
+ </div>
1085
+ </div>
1086
+ <div class="searchstax-facet-show-more-container" v-if="facet.hasMoreFacets">
1087
+ <div
1088
+ class="searchstax-facet-show-more-container"
1089
+ @click="showMoreLessDesktop($event, facet)"
1090
+ @keyup.space="
1091
+ $event.preventDefault();
1092
+ showMoreLessDesktop($event, facet)
1093
+ "
1094
+ @keyup.enter="showMoreLessDesktop($event, facet)"
1095
+ tabindex="0"
1096
+ >
1097
+ <div
1098
+ v-if="facet.showingAllFacets"
1099
+ class="searchstax-facet-show-less-button searchstax-facet-show-button"
570
1100
  >
1101
+ less
1102
+ </div>
1103
+ <div v-else class="searchstax-facet-show-more-button searchstax-facet-show-button">
1104
+ more
1105
+ </div>
1106
+ </div>
1107
+ </div>
1108
+ </div>
1109
+ </div>
1110
+ </div>
1111
+ </div>
1112
+ </template>
1113
+ <template
1114
+ #mobileFacets="{ facetsTemplateDataMobile, selectedFacetsCheckboxes, isNotDeactivated, toggleFacetGroup, isChecked, selectFacet, showMoreLessDesktop, facetContainers, openOverlay, unselectFacet, unselectAll, closeOverlay, updateRefMobile }"
1115
+ >
1116
+ <div
1117
+ class="searchstax-facets-container-mobile"
1118
+ v-if="facetsTemplateDataMobile?.hasResultsOrExternalPromotions"
1119
+ >
1120
+ <div class="searchstax-facets-pills-container">
1121
+ <div class="searchstax-facets-pill searchstax-facets-pill-filter-by" @click="openOverlay">
1122
+ <div class="searchstax-facets-pill-label">Filter By</div>
1123
+ </div>
1124
+ <div class="searchstax-facets-pills-selected">
1125
+ <div
1126
+ class="searchstax-facets-pill searchstax-facets-pill-facets"
1127
+ v-for="facet in selectedFacetsCheckboxes"
1128
+ :key="facet.value"
1129
+ @click="unselectFacet(facet)"
1130
+ >
1131
+ <div class="searchstax-facets-pill-label">{{ facet.value }} ({{ facet.count }})</div>
1132
+ <div class="searchstax-facets-pill-icon-close"></div>
1133
+ </div>
1134
+ </div>
1135
+ <div
1136
+ class="searchstax-facets-pill searchstax-clear-filters searchstax-facets-pill-clear-all"
1137
+ v-if="selectedFacetsCheckboxes.length"
1138
+ @click="unselectAll"
1139
+ >
1140
+ <div class="searchstax-facets-pill-label">Clear Filters</div>
1141
+ </div>
1142
+ </div>
1143
+ <div
1144
+ class="searchstax-facets-mobile-overlay"
1145
+ :class="{ 'searchstax-show': (facetsTemplateDataMobile as any)?.overlayOpened }"
1146
+ >
1147
+ <div class="searchstax-facets-mobile-overlay-header">
1148
+ <div class="searchstax-facets-mobile-overlay-header-title">Filter By</div>
1149
+ <div class="searchstax-search-close" @click="closeOverlay"></div>
1150
+ </div>
1151
+ <div class="searchstax-facets-container-mobile">
1152
+ <div
1153
+ v-for="facet in facetsTemplateDataMobile?.facets"
1154
+ :key="facet.name + 'mobile'"
1155
+ class="searchstax-facet-container"
1156
+ :class="{ active: isNotDeactivated(facet.name) }"
1157
+ >
1158
+ <div>
1159
+ <div class="searchstax-facet-title-container" @click="toggleFacetGroup(facet.name)">
1160
+ <div class="searchstax-facet-title">{{ facet.label }}</div>
1161
+ <div class="searchstax-facet-title-arrow active"></div>
1162
+ </div>
1163
+ <div class="searchstax-facet-values-container" aria-live="polite">
1164
+ <div
1165
+ v-for="(facetValue) in facet.values as IFacetValueData[]"
1166
+ :key="facetValue.value + facetValue.parentName"
1167
+ class="searchstax-facet-value-container"
1168
+ :class="{ 'searchstax-facet-value-disabled': facetValue.disabled }"
1169
+ :ref="
1170
+ (el) => {
1171
+ updateRefMobile(el, facetValue)
1172
+ }
1173
+ "
1174
+ >
1175
+ <div class="searchstax-facet-input">
1176
+ <input
1177
+ type="checkbox"
1178
+ class="searchstax-facet-input-checkbox"
1179
+ :checked="isChecked(facetValue)"
1180
+ :disabled="facetValue.disabled"
1181
+ :aria-label="facetValue.value + ' ' + facetValue.count"
1182
+ @click="selectFacet(facetValue.value + '-mobile', $event, facetValue, true)"
1183
+ />
1184
+ </div>
571
1185
  <div
572
- v-if="facetsTemplateDataDesktop?.hasResultsOrExternalPromotions"
573
- class="searchstax-facets-container-desktop"
1186
+ class="searchstax-facet-value-label"
1187
+ @click="selectFacet(facetValue.value + '-mobile', $event, facetValue, false)"
574
1188
  >
575
- <div
576
- v-for="facet in facetsTemplateDataDesktop.facets"
577
- :key="facet"
578
- class="searchstax-facet-container"
579
- :class="{ 'active': isNotDeactivated(facet.name) }"
580
- >
581
- <div>
582
- <div
583
- class="searchstax-facet-title-container"
584
- @click="toggleFacetGroup(facet.name)"
585
- >
586
- <div class="searchstax-facet-title"> {{ facet.label }} </div>
587
- <div class="searchstax-facet-title-arrow active"></div>
588
- </div>
589
- <div class="searchstax-facet-values-container">
590
- <div
591
- v-for="(facetValue, key) in facet.values"
592
- :key="facetValue.value + facetValue.parentName"
593
- class="searchstax-facet-value-container"
594
- :class="{ 'searchstax-facet-value-disabled': facetValue.disabled }"
595
- :ref="el => { facetContainers[key + facet.name] = el }"
596
- >
597
- <div class="searchstax-facet-input">
598
- <input
599
- type="checkbox"
600
- class="searchstax-facet-input-checkbox"
601
- :checked="isChecked(facetValue)"
602
- :disabled="facetValue.disabled"
603
- @click="selectFacet(key + facet.name, $event, facetValue, true)"
604
- />
605
- </div>
606
- <div
607
- class="searchstax-facet-value-label"
608
- @click="selectFacet(key + facet.name, $event, facetValue, false)"
609
- >{{ facetValue.value }}</div>
610
- <div
611
- class="searchstax-facet-value-count"
612
- @click="selectFacet(key + facet.name, $event, facetValue, false)"
613
- >({{ facetValue.count }})</div>
614
- </div>
615
- <div
616
- class="searchstax-facet-show-more-container"
617
- v-if="facet.hasMoreFacets"
618
- >
619
- <div
620
- class="searchstax-facet-show-more-container"
621
- @click="showMoreLessDesktop($event, facet)"
622
- >
623
- <div
624
- v-if="facet.showingAllFacets"
625
- class="searchstax-facet-show-less-button searchstax-facet-show-button"
626
- >less</div>
627
- <div
628
- v-else
629
- class="searchstax-facet-show-more-button searchstax-facet-show-button"
630
- >more</div>
631
- </div>
632
- </div>
633
- </div>
634
- </div>
635
- </div>
1189
+ {{ facetValue.value }}
636
1190
  </div>
637
- </template>
638
- <template
639
- #mobileFacets="{ facetsTemplateDataMobile, selectedFacetsCheckboxes, isNotDeactivated, toggleFacetGroup, isChecked, selectFacet, showMoreLessDesktop, facetContainers, openOverlay, unselectFacet, unselectAll, closeOverlay }"
640
- >
641
1191
  <div
642
- class="searchstax-facets-container-mobile"
643
- v-if="facetsTemplateDataMobile?.hasResultsOrExternalPromotions"
1192
+ class="searchstax-facet-value-count"
1193
+ @click="selectFacet(facetValue.value + '-mobile', $event, facetValue, false)"
644
1194
  >
645
- <div class="searchstax-facets-pills-container">
646
- <div
647
- class="searchstax-facets-pill searchstax-facets-pill-filter-by"
648
- @click="openOverlay"
649
- >
650
- <div class="searchstax-facets-pill-label">Filter By</div>
651
- </div>
652
- <div class="searchstax-facets-pills-selected">
653
- <div
654
- class="searchstax-facets-pill searchstax-facets-pill-facets"
655
- v-for="facet in selectedFacetsCheckboxes"
656
- :key="facet.value"
657
- @click="unselectFacet(facet)"
658
- >
659
- <div class="searchstax-facets-pill-label">{{ facet.value }} ({{ facet.count }})</div>
660
- <div class="searchstax-facets-pill-icon-close"></div>
661
- </div>
662
- </div>
663
- <div
664
- class="searchstax-facets-pill searchstax-clear-filters searchstax-facets-pill-clear-all"
665
- v-if="selectedFacetsCheckboxes.length"
666
- @click="unselectAll"
667
- >
668
- <div class="searchstax-facets-pill-label">Clear Filters</div>
669
- </div>
1195
+ ({{ facetValue.count }})
1196
+ </div>
1197
+ </div>
1198
+ <div class="searchstax-facet-show-more-container" v-if="facet.hasMoreFacets">
1199
+ <div
1200
+ class="searchstax-facet-show-more-container"
1201
+ @click="showMoreLessMobile($event, facet)"
1202
+ @keyup.space="showMoreLessMobile($event, facet)"
1203
+ @keyup.enter="showMoreLessMobile($event, facet)"
1204
+ tabindex="0"
1205
+ >
1206
+ <div
1207
+ v-if="facet.showingAllFacets"
1208
+ class="searchstax-facet-show-less-button searchstax-facet-show-button"
1209
+ >
1210
+ less
670
1211
  </div>
671
1212
  <div
672
- class="searchstax-facets-mobile-overlay"
673
- :class="{ 'searchstax-show': facetsTemplateDataMobile?.overlayOpened }"
1213
+ v-else
1214
+ class="searchstax-facet-show-more-button searchstax-facet-show-button"
674
1215
  >
675
- <div class="searchstax-facets-mobile-overlay-header">
676
- <div class="searchstax-facets-mobile-overlay-header-title">Filter By</div>
677
- <div
678
- class="searchstax-search-close"
679
- @click="closeOverlay"
680
- ></div>
681
- </div>
682
- <div class="searchstax-facets-container-mobile">
683
- <div
684
- v-for="facet in facetsTemplateDataMobile?.facets"
685
- :key="facet"
686
- class="searchstax-facet-container"
687
- :class="{ 'active': isNotDeactivated(facet.name) }"
688
- >
689
- <div>
690
- <div
691
- class="searchstax-facet-title-container"
692
- @click="toggleFacetGroup(facet.name)"
693
- >
694
- <div class="searchstax-facet-title"> {{ facet.label }} </div>
695
- <div class="searchstax-facet-title-arrow active"></div>
696
- </div>
697
- <div class="searchstax-facet-values-container">
698
- <div
699
- v-for="(facetValue, key) in facet.values"
700
- :key="facetValue.value + facetValue.parentName"
701
- class="searchstax-facet-value-container"
702
- :class="{ 'searchstax-facet-value-disabled': facetValue.disabled }"
703
- :ref="el => { facetContainers[key + facet.name] = el }"
704
- >
705
- <div class="searchstax-facet-input">
706
- <input
707
- type="checkbox"
708
- class="searchstax-facet-input-checkbox"
709
- :checked="isChecked(facetValue)"
710
- :disabled="facetValue.disabled"
711
- @click="selectFacet(key + facet.name, $event, facetValue, true)"
712
- />
713
- </div>
714
- <div
715
- class="searchstax-facet-value-label"
716
- @click="selectFacet(key + facet.name, $event, facetValue, false)"
717
- >{{ facetValue.value }}</div>
718
- <div
719
- class="searchstax-facet-value-count"
720
- @click="selectFacet(key + facet.name, $event, facetValue, false)"
721
- >({{ facetValue.count }})</div>
722
- </div>
723
- <div
724
- class="searchstax-facet-show-more-container"
725
- v-if="facet.hasMoreFacets"
726
- >
727
- <div
728
- class="searchstax-facet-show-more-container"
729
- @click="showMoreLessDesktop($event, facet)"
730
- >
731
- <div
732
- v-if="facet.showingAllFacets"
733
- class="searchstax-facet-show-less-button searchstax-facet-show-button"
734
- >less</div>
735
- <div
736
- v-else
737
- class="searchstax-facet-show-more-button searchstax-facet-show-button"
738
- >more</div>
739
- </div>
740
- </div>
741
- </div>
742
- </div>
743
- </div>
744
- </div>
745
- <button
746
- class="searchstax-facets-mobile-overlay-done"
747
- @click="closeOverlay"
748
- >Done</button>
1216
+ more
749
1217
  </div>
750
1218
  </div>
751
- </template>
752
- </SearchstaxFacetsWidget>
1219
+ </div>
1220
+ </div>
1221
+ </div>
1222
+ </div>
1223
+ </div>
1224
+ <button class="searchstax-facets-mobile-overlay-done" @click="closeOverlay">Done</button>
1225
+ </div>
1226
+ </div>
1227
+ </template>
1228
+ </SearchstaxFacetsWidget>
753
1229
  ```
754
1230
 
755
1231
  ### SearchFeedback Widget ###
756
1232
 
757
- example of search feedback widget initialization with minimum options
1233
+ The SearchStax Site Search solution provides a search feedback widget to support your Vue search pages.
1234
+
1235
+ The SearchstaxSearchFeedbackWidget displays search feedback and stats.
1236
+
1237
+ **Usage**
1238
+
758
1239
  ```
759
1240
  <SearchstaxSearchFeedbackWidget></SearchstaxSearchFeedbackWidget>
760
1241
  ```
761
1242
 
1243
+ **Main Template Override**
1244
+
1245
+ Main template for the search feedback message.
762
1246
 
763
- example of search feedback widget initialization with template overrides
1247
+ It receives following props:
1248
+ - searchFeedbackData - Feedback data object
1249
+ - onOriginalQueryClick - Handler for clicking on original query suggestion
1250
+
1251
+ **Example**
764
1252
  ```
765
1253
  <SearchstaxSearchFeedbackWidget>
766
1254
  <template #searchFeedback="{ searchFeedbackData, onOriginalQueryClick }">
767
- <div
768
- class="searchstax-feedback-container"
769
- v-if="searchFeedbackData && searchFeedbackData?.searchExecuted && searchFeedbackData?.totalResults"
770
- > Showing <b>{{ searchFeedbackData.startResultIndex }} - {{ searchFeedbackData.endResultIndex }}</b> of
771
- <b>{{ searchFeedbackData.totalResults }}</b> results <span v-if="searchFeedbackData.searchTerm">for "<b>{{
772
- searchFeedbackData.searchTerm }}</b>" </span>
773
- <div class="searchstax-feedback-container-suggested">
774
- <div v-if="searchFeedbackData.autoCorrectedQuery"> Search instead for <a
775
- href="#"
776
- @click.prevent="onOriginalQueryClick($event)"
777
- class="searchstax-feedback-original-query"
778
- >{{ searchFeedbackData.originalQuery }}</a>
779
- </div>
780
- </div>
781
- </div>
1255
+ <h2
1256
+ class="searchstax-feedback-container"
1257
+ v-if="
1258
+ searchFeedbackData && searchFeedbackData?.searchExecuted && searchFeedbackData?.totalResults
1259
+ "
1260
+ >
1261
+ Showing
1262
+ <b>{{ searchFeedbackData.startResultIndex }} - {{ searchFeedbackData.endResultIndex }}</b> of
1263
+ <b>{{ searchFeedbackData.totalResults }}</b> results
1264
+ <span v-if="searchFeedbackData.searchTerm"
1265
+ >for "<b>{{ searchFeedbackData.searchTerm }}</b
1266
+ >"
1267
+ </span>
1268
+ <div class="searchstax-feedback-container-suggested">
1269
+ <div v-if="searchFeedbackData.autoCorrectedQuery">
1270
+ Search instead for
1271
+ <a
1272
+ href="#"
1273
+ @click.prevent="onOriginalQueryClick($event)"
1274
+ class="searchstax-feedback-original-query"
1275
+ :aria-label="'Search instead for ' + searchFeedbackData.originalQuery"
1276
+ >{{ searchFeedbackData.originalQuery }}</a
1277
+ >
1278
+ </div>
1279
+ </div>
1280
+ </h2>
782
1281
  </template>
783
1282
  </SearchstaxSearchFeedbackWidget>
784
1283
  ```
785
1284
 
786
- ## RelatedSearches widget
1285
+ ### RelatedSearches widget ###
787
1286
 
1287
+ The SearchStax Site Search solution offers a Vue widget for displaying related searches.
788
1288
 
1289
+ The SearchstaxRelatedSearchesWidget for Vue component displays related searches.
789
1290
 
790
- example of search feedback widget initialization with minimum options
1291
+ **Usage**
791
1292
  ```
792
- <SearchstaxRelatedSearchesWidget
1293
+ <SearchstaxRelatedSearchesWidget
793
1294
  :relatedSearchesURL="config.relatedSearchesURL"
794
1295
  :relatedSearchesAPIKey="config.relatedSearchesAPIKey"
795
1296
  ></SearchstaxRelatedSearchesWidget>
796
1297
  ```
797
1298
 
1299
+ **Props**
1300
+
1301
+ - relatedSearchesURL: API URL for fetching related searches
1302
+ - relatedSearchesAPIKey?: API key for related searches API
1303
+
1304
+ **Main Template Override**
1305
+
1306
+ Main template for related searches.
1307
+
1308
+ It receives following props:
1309
+
1310
+ - relatedData - Related searches data object
1311
+ - executeSearch - Handler to run new search from related term
1312
+
1313
+ **Example**
798
1314
 
799
- example of search feedback widget initialization with template overrides
800
1315
  ```
801
1316
  <SearchstaxRelatedSearchesWidget
802
1317
  :relatedSearchesURL="config.relatedSearchesURL"
803
1318
  :relatedSearchesAPIKey="config.relatedSearchesAPIKey"
804
1319
  >
805
1320
  <template #related="{ relatedData, executeSearch }">
806
- <div
807
- class="searchstax-related-searches-container"
808
- id="searchstax-related-searches-container"
809
- v-if="relatedData && relatedData?.searchExecuted && relatedData?.hasRelatedSearches"
810
- > Related searches: <span id="searchstax-related-searches"></span>
811
- <span
812
- class="searchstax-related-search"
813
- v-if="relatedData.relatedSearches"
814
- >
815
- <span
816
- v-for="related in relatedData.relatedSearches"
817
- :key="related.related_search"
818
- @click="executeSearch(related)"
819
- class="searchstax-related-search searchstax-related-search-item"
820
- > {{ related.related_search }}<span v-if="!related.last">,</span>
821
- </span>
822
- </span>
823
- </div>
1321
+ <div
1322
+ class="searchstax-related-searches-container"
1323
+ id="searchstax-related-searches-container"
1324
+ v-if="relatedData && relatedData?.searchExecuted && relatedData?.hasRelatedSearches"
1325
+ >
1326
+ Related searches: <span id="searchstax-related-searches"></span>
1327
+ <span class="searchstax-related-search" v-if="relatedData.relatedSearches">
1328
+ <span
1329
+ v-for="related in relatedData.relatedSearches"
1330
+ :key="related.related_search"
1331
+ @click="executeSearch(related)"
1332
+ @keyup.enter="executeSearch(related)"
1333
+ @keyup.space="executeSearch(related)"
1334
+ tabindex="0"
1335
+ class="searchstax-related-search searchstax-related-search-item"
1336
+ :aria-label="'Related search: ' + related.related_search"
1337
+ >
1338
+ {{ related.related_search }}<span v-if="!related.last">,</span>
1339
+ </span>
1340
+ </span>
1341
+ </div>
824
1342
  </template>
825
1343
  </SearchstaxRelatedSearchesWidget>
826
1344
  ```
827
1345
 
828
1346
  ### ExternalPromotions widget ###
829
1347
 
830
- example of search feedback widget initialization with minimum options
1348
+ The SearchStax Site Search solution offers a Vue external-promotions widget for your custom search page.
1349
+
1350
+ The SearchstaxExternalPromotionsWidget component displays external promotions fetched from the API.
1351
+
1352
+ **Usage**
1353
+
831
1354
  ```
832
1355
  <SearchstaxExternalPromotionsWidget></SearchstaxExternalPromotionsWidget>
833
1356
  ```
834
1357
 
1358
+ **Main Template Override**
1359
+
1360
+ Main template for external promotions.
1361
+
1362
+ It receives following props:
1363
+
1364
+ - externalPromotionsData – External promotions data object
1365
+ - trackClick – Handler for tracking link clicks
835
1366
 
836
- example of search feedback widget initialization with template overrides
1367
+ **Example**
837
1368
  ```
838
1369
  <SearchstaxExternalPromotionsWidget>
839
1370
  <template #externalPromotions="{ externalPromotionsData, trackClick }">
840
1371
  <div
841
- class="searchstax-external-promotions-container"
842
- id="searchstax-external-promotions-container"
843
- v-if="externalPromotionsData && externalPromotionsData?.searchExecuted && externalPromotionsData?.hasExternalPromotions"
844
- >
845
- <div
846
- class="searchstax-external-promotion searchstax-search-result"
847
- v-for="externalPromotion in externalPromotionsData.externalPromotions"
848
- :key="externalPromotion.id"
849
- >
850
- <div class="icon-elevated"></div>
851
- <a
852
- v-if="externalPromotion.url"
853
- href="{{externalPromotion.url}}"
854
- @click="trackClick(externalPromotion, $event)"
855
- class="searchstax-result-item-link"
856
- ></a>
857
- <div class="searchstax-search-result-title-container">
858
- <span class="searchstax-search-result-title">{{ externalPromotion.name }}</span>
859
- </div>
860
- <p
861
- v-if="externalPromotion.description"
862
- class="searchstax-search-result-description searchstax-search-result-common"
863
- > {{ externalPromotion.description }} </p>
864
- <p
865
- v-if="externalPromotion.url"
866
- class="searchstax-search-result-description searchstax-search-result-common"
867
- > {{ externalPromotion.url }} </p>
868
- </div>
869
- </div>
1372
+ class="searchstax-external-promotions-container"
1373
+ id="searchstax-external-promotions-container"
1374
+ v-if="
1375
+ externalPromotionsData &&
1376
+ externalPromotionsData?.searchExecuted &&
1377
+ externalPromotionsData?.hasExternalPromotions
1378
+ "
1379
+ >
1380
+ <div
1381
+ class="searchstax-external-promotion searchstax-search-result"
1382
+ v-for="externalPromotion in externalPromotionsData.externalPromotions"
1383
+ :key="externalPromotion.id"
1384
+ >
1385
+ <div class="icon-elevated"></div>
1386
+ <a
1387
+ v-if="externalPromotion.url"
1388
+ href="{{externalPromotion.url}}"
1389
+ @click="trackClick(externalPromotion, $event)"
1390
+ class="searchstax-result-item-link"
1391
+ ></a>
1392
+ <div class="searchstax-search-result-title-container">
1393
+ <span class="searchstax-search-result-title">{{ externalPromotion.name }}</span>
1394
+ </div>
1395
+ <p
1396
+ v-if="externalPromotion.description"
1397
+ class="searchstax-search-result-description searchstax-search-result-common"
1398
+ >
1399
+ {{ externalPromotion.description }}
1400
+ </p>
1401
+ <p
1402
+ v-if="externalPromotion.url"
1403
+ class="searchstax-search-result-description searchstax-search-result-common"
1404
+ >
1405
+ {{ externalPromotion.url }}
1406
+ </p>
1407
+ </div>
1408
+ </div>
870
1409
  </template>
871
1410
  </SearchstaxExternalPromotionsWidget>
872
1411
  ```
@@ -874,42 +1413,58 @@ example of search feedback widget initialization with template overrides
874
1413
 
875
1414
  ### Sorting Widget ###
876
1415
 
877
- example of sorting widget initialization with minimum options
1416
+ The SearchStax Site Search solution offers a Vue sorting widget for your custom search page.
1417
+
1418
+ The SearchstaxSortingWidget component displays sorting options for search results.
1419
+
1420
+ **Usage**
1421
+
878
1422
  ```
879
1423
  <SearchstaxSortingWidget></SearchstaxSortingWidget>
880
1424
  ```
881
1425
 
1426
+ **Main Template Override**
1427
+
1428
+ Main template for sorting widget.
1429
+
1430
+ It receives following props:
1431
+
1432
+ - sortingData – Sorting data object
1433
+ - orderChange – Handler for sorting change
1434
+ - selectedSorting – Current selected sorting
1435
+
1436
+ **Example**
882
1437
 
883
- example of sorting widget initialization with template override
884
1438
  ```
885
1439
  <SearchstaxSortingWidget>
886
1440
  <template #sorting="{ sortingData, orderChange, selectedSorting }">
887
- <div
888
- class="searchstax-sorting-container"
889
- v-if="sortingData && sortingData?.searchExecuted && sortingData?.hasResultsOrExternalPromotions"
890
- >
891
- <label
892
- class="searchstax-sorting-label"
893
- for="sort-by"
894
- >Sort By</label>
895
- <select
896
- id="searchstax-search-order-select"
897
- class="searchstax-search-order-select"
898
- :value="selectedSorting"
899
- @change="orderChange($event)"
900
- >
901
- <option value=""> Relevance </option>
902
- <option value="date desc"> Newest Content </option>
903
- <option value="date asc"> Oldest Content </option>
904
- </select>
905
- </div>
1441
+ <div
1442
+ class="searchstax-sorting-container"
1443
+ v-if="sortingData && sortingData?.searchExecuted && sortingData?.hasResultsOrExternalPromotions"
1444
+ >
1445
+ <label class="searchstax-sorting-label" for="searchstax-search-order-select">Sort By</label>
1446
+ <select
1447
+ id="searchstax-search-order-select"
1448
+ class="searchstax-search-order-select"
1449
+ :value="selectedSorting"
1450
+ @change="orderChange($event)"
1451
+ >
1452
+ <option
1453
+ v-for="sortOption in sortingData.sortOptions"
1454
+ :key="sortOption.key"
1455
+ :value="sortOption.key"
1456
+ >
1457
+ {{ sortOption.value }}
1458
+ </option>
1459
+ </select>
1460
+ </div>
906
1461
  </template>
907
1462
  </SearchstaxSortingWidget>
908
1463
  ```
909
1464
 
910
1465
 
911
1466
  ## Template overrides
912
- Templates use vue slots.
1467
+ Templates use vue templating.
913
1468
 
914
1469
  ## STYLING
915
1470
 
@@ -921,4 +1476,4 @@ css can be taken from
921
1476
 
922
1477
  ```
923
1478
  ./../node_modules/@searchstax-inc/searchstudio-ux-js/dist/styles/mainTheme.css
924
- ```
1479
+ ```