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