@searchstax-inc/searchstudio-ux-angular 1.2.51 → 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 (48) hide show
  1. package/CHANGELOG.md +8 -1
  2. package/README.md +1188 -686
  3. package/esm2022/lib/components/searchstax-input/searchstax-input.component.mjs +6 -40
  4. package/esm2022/lib/components/searchstax-results/searchstax-results.component.mjs +5 -3
  5. package/esm2022/lib/components/searchstax-search-answer/searchstax-search-answer.component.mjs +4 -3
  6. package/esm2022/lib/components/searchstax-search-external/searchstax-search-external.component.mjs +4 -3
  7. package/esm2022/lib/components/searchstax-search-facets/searchstax-search-facets.component.mjs +5 -3
  8. package/esm2022/lib/components/searchstax-search-location/searchstax-search-location.component.mjs +4 -4
  9. package/esm2022/lib/components/searchstax-search-overview/searchstax-search-overview.component.mjs +4 -3
  10. package/esm2022/lib/components/searchstax-search-pagination/searchstax-search-pagination.component.mjs +5 -3
  11. package/esm2022/lib/components/searchstax-search-related/searchstax-search-related.component.mjs +4 -3
  12. package/esm2022/lib/components/searchstax-search-sorting/searchstax-search-sorting.component.mjs +4 -4
  13. package/esm2022/lib/components/templates/SearchstaxLocationWidgetTemplateConfig.mjs +91 -0
  14. package/esm2022/lib/components/templates/searchstax-search-answer-template.mjs +119 -0
  15. package/esm2022/lib/components/templates/searchstax-search-external-template.mjs +107 -0
  16. package/esm2022/lib/components/templates/searchstax-search-facet-desktop-template.mjs +207 -0
  17. package/esm2022/lib/components/templates/searchstax-search-facet-mobile-template.mjs +343 -0
  18. package/esm2022/lib/components/templates/searchstax-search-input-template.mjs +126 -0
  19. package/esm2022/lib/components/templates/searchstax-search-location-template.mjs +152 -0
  20. package/esm2022/lib/components/templates/searchstax-search-no-results-template.mjs +85 -0
  21. package/esm2022/lib/components/templates/searchstax-search-overview-template.mjs +87 -0
  22. package/esm2022/lib/components/templates/searchstax-search-pagination-infinite-template.mjs +60 -0
  23. package/esm2022/lib/components/templates/searchstax-search-pagination-template.mjs +96 -0
  24. package/esm2022/lib/components/templates/searchstax-search-related-template.mjs +81 -0
  25. package/esm2022/lib/components/templates/searchstax-search-results-template.mjs +171 -0
  26. package/esm2022/lib/components/templates/searchstax-search-sorting-template.mjs +76 -0
  27. package/esm2022/lib/searchstudio-ux-angular.module.mjs +70 -5
  28. package/esm2022/public-api.mjs +14 -1
  29. package/fesm2022/searchstax-inc-searchstudio-ux-angular.mjs +1840 -64
  30. package/fesm2022/searchstax-inc-searchstudio-ux-angular.mjs.map +1 -1
  31. package/lib/components/searchstax-input/searchstax-input.component.d.ts +11 -1
  32. package/lib/components/templates/SearchstaxLocationWidgetTemplateConfig.d.ts +13 -0
  33. package/lib/components/templates/searchstax-search-answer-template.d.ts +7 -0
  34. package/lib/components/templates/searchstax-search-external-template.d.ts +7 -0
  35. package/lib/components/templates/searchstax-search-facet-desktop-template.d.ts +7 -0
  36. package/lib/components/templates/searchstax-search-facet-mobile-template.d.ts +7 -0
  37. package/lib/components/templates/searchstax-search-input-template.d.ts +7 -0
  38. package/lib/components/templates/searchstax-search-location-template.d.ts +7 -0
  39. package/lib/components/templates/searchstax-search-no-results-template.d.ts +7 -0
  40. package/lib/components/templates/searchstax-search-overview-template.d.ts +7 -0
  41. package/lib/components/templates/searchstax-search-pagination-infinite-template.d.ts +7 -0
  42. package/lib/components/templates/searchstax-search-pagination-template.d.ts +7 -0
  43. package/lib/components/templates/searchstax-search-related-template.d.ts +7 -0
  44. package/lib/components/templates/searchstax-search-results-template.d.ts +7 -0
  45. package/lib/components/templates/searchstax-search-sorting-template.d.ts +7 -0
  46. package/lib/searchstudio-ux-angular.module.d.ts +16 -3
  47. package/package.json +2 -2
  48. package/public-api.d.ts +13 -0
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
163
  <app-searchstax-answer [showShowMoreAfterWordCount]="100"></app-searchstax-answer>
161
164
  ```
162
165
 
166
+ **Props**
167
+
168
+ - showShowMoreAfterWordCount - 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 [showShowMoreAfterWordCount]="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>
285
224
  </div>
286
- </ng-template>
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>
235
+ </div>
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
+ - inputTemplate - template override. look at examples below
267
+
268
+
269
+ **inputWidgetTemplate**
270
+
271
+ The inputTemplate prop allows customizing the input UI.
301
272
 
302
- example of input widget initialization with template override
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,676 @@ 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
+
457
+ **Template Override**
458
+
459
+ The searchLocationTemplate prop allows customizing the location input UI.
460
+
461
+ It receives the following props:
462
+
463
+ - locationData – data containing info on when to show certain elements
464
+ - inputValue – value of location input
465
+ - locationBlur - Handler for location input blur
466
+ - radiusChange - Handler for location radius change
467
+ - selectValue - Value of radius select
468
+ - inputChange - location input change handler
469
+ - locationError - boolean stating if location has error
470
+ - getCurrentLocation - Handler for getting current location from browser
471
+
472
+
473
+ **Example**
474
+
377
475
  ```
476
+ const config = {
477
+ appId: "APP_ID",
478
+ relatedSearchesAPIKey: "KEY"
479
+ }
480
+
481
+ const locationWidgetConfig = {
482
+ locationDecode: (term: string): Promise<ISearchstaxLocation> => {
483
+ return new Promise((resolve) => {
484
+ // make a request to google geocoding API to retrieve lat, lon and address
485
+
486
+ const geocodingAPIKey = "GEOCODING_API_KEY";
487
+ const geocodingURL = `https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(
488
+ term
489
+ )}&key=${geocodingAPIKey}`;
490
+ fetch(geocodingURL)
491
+ .then((response) => response.json())
492
+ .then((data) => {
493
+ if (data.status === "OK" && data.results.length > 0) {
494
+ const result = data.results[0];
495
+ const location = {
496
+ lat: result.geometry.location.lat,
497
+ lon: result.geometry.location.lng,
498
+ address: result.formatted_address,
499
+ };
500
+ resolve(location);
501
+ } else {
502
+ resolve({
503
+ address: undefined,
504
+ lat: undefined,
505
+ lon: undefined,
506
+ error: true,
507
+ });
508
+ }
509
+ })
510
+ .catch(() => {
511
+ resolve({
512
+ address: undefined,
513
+ lat: undefined,
514
+ lon: undefined,
515
+ error: true,
516
+ });
517
+ });
518
+ });
519
+ },
520
+ locationDecodeCoordinatesToAddress: (lat: string, lon: string): Promise<ISearchstaxLocation> => {
521
+ return new Promise((resolve) => {
522
+ fetch(
523
+ `https://geocoding-staging.searchstax.co/reverse?location=${lat},${lon}&components=country:US&app_id=${config.appId}`,
524
+ {
525
+ method: "GET",
526
+ headers: {
527
+ Authorization: `Token ${config.relatedSearchesAPIKey}`,
528
+ },
529
+ }
530
+ )
531
+ .then((response) => response.json())
532
+ .then((data) => {
533
+ if (data.status === "OK" && data.results.length > 0) {
534
+ const result = data.results[0];
535
+ resolve({
536
+ address: result.formatted_address,
537
+ lat: lat,
538
+ lon: lon,
539
+ error: false,
540
+ });
541
+ } else {
542
+ resolve({
543
+ address: undefined,
544
+ lat: lat,
545
+ lon: lon,
546
+ error: true,
547
+ });
548
+ }
549
+ })
550
+ .catch(() => {
551
+ resolve({
552
+ address: undefined,
553
+ lat: lat,
554
+ lon: lon,
555
+ error: true,
556
+ });
557
+ });
558
+ });
559
+ },
560
+ locationSearchEnabled: false,
561
+ locationValuesOverride: {
562
+ locationDistanceEnabled: true,
563
+ filterValues: ["any", "1000"],
564
+ filterUnit: "miles",
565
+ locationFilterDefaultValue: "any"
566
+ },
567
+ }
378
568
  <app-searchstax-search-location
379
- [locationDecode]="locationDecode"
569
+ [locationDecode]="locationWidgetConfig.locationDecode"
380
570
  [templateOverride]="locationTemplate"
381
- [locationDecodeCoordinatesToAddress]="locationDecodeCoordinatesToAddress"
571
+ [locationValuesOverride]="locationWidgetConfig.locationValuesOverride"
572
+ [locationSearchEnabled]="locationWidgetConfig.locationSearchEnabled"
573
+ [locationDecodeCoordinatesToAddress]="locationWidgetConfig.locationDecodeCoordinatesToAddress"
574
+ >
575
+ <ng-template
576
+ #locationTemplate
577
+ let-context="context"
578
+ let-locationData="locationData"
579
+ let-inputValue="inputValue"
580
+ let-selectValue="selectValue"
581
+ let-radiusChange="radiusChange"
582
+ let-inputChange="inputChange"
583
+ let-locationBlur="locationBlur"
584
+ let-locationError="locationError"
585
+ let-getCurrentLocation="getCurrentLocation"
586
+ >
587
+ <div
588
+ class="searchstax-location-input-container"
589
+ data-test-id="searchstax-location-input-container"
590
+ *ngIf="locationData"
591
+ >
592
+ <div class="searchstax-location-input-wrapper">
593
+ <span class="searchstax-location-input-label">NEAR</span>
594
+ <div class="searchstax-location-input-wrapper-inner">
595
+ <input
596
+ type="text"
597
+ id="searchstax-location-input"
598
+ class="searchstax-location-input"
599
+ [ngClass]="{
600
+ 'searchstax-input-location-error': locationError
601
+ }"
602
+ placeholder="Zip, Postal Code or City..."
603
+ aria-label="Search Location Input"
604
+ data-test-id="searchstax-location-input"
605
+ [value]="inputValue"
606
+ (change)="inputChange($event, context)"
607
+ (blur)="locationBlur(context)"
608
+ />
609
+ <button
610
+ (click)="getCurrentLocation(context)"
611
+ class="searchstax-get-current-location-button"
612
+ >
613
+ Use my current location
614
+ </button>
615
+ </div>
616
+ <span
617
+ *ngIf="locationData.shouldShowLocationDistanceDropdown"
618
+ class="searchstax-location-input-label"
619
+ >WITHIN</span
620
+ >
621
+ <select
622
+ *ngIf="locationData.shouldShowLocationDistanceDropdown"
623
+ id="searchstax-location-radius-select"
624
+ class="searchstax-location-radius-select"
625
+ aria-label="Search Location Radius Select"
626
+ data-test-id="searchstax-location-radius-select"
627
+ (change)="radiusChange($event, context)"
628
+ [(ngModel)]="context.selectValue"
382
629
  >
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"
630
+ <option
631
+ *ngFor="let value of locationData.locationSearchDistanceValues"
632
+ [value]="value.value"
394
633
  >
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>
634
+ {{ value.label }}
635
+ </option>
636
+ </select>
637
+ </div>
638
+ </div>
639
+ </ng-template>
640
+
450
641
  </app-searchstax-search-location>
451
642
  ```
643
+
452
644
  ### Result Widget ###
453
645
 
454
- example of results widget initialization with minimum options
455
- ```
456
- <app-searchstax-results
457
- [afterLinkClick]="afterLinkClick"
458
- ></app-searchstax-results>
459
- ```
646
+ The SearchStax Site Search solution offers an Angular results widget to assist with your custom search page.
647
+
648
+ The SearchstaxResultsWidget for Angular component displays the search results.
649
+
650
+ **Usage**
460
651
 
461
- example of results widget initialization with minimum options and infinite scroll behavior
462
652
  ```
463
653
  <app-searchstax-results
464
654
  [afterLinkClick]="afterLinkClick"
465
- [renderMethod]="'infiniteScroll'"
466
655
  ></app-searchstax-results>
467
656
  ```
468
657
 
469
- example of result widget initialization with template override
658
+ **Result Template Override**
659
+
660
+ The resultsTemplate prop allows customizing the result UI.
661
+
662
+ It receives following props:
663
+ - searchResults - Array of result items
664
+ - resultClicked - Handler for result click events
665
+
666
+
667
+ **No Results Template Override**
668
+
669
+ The noResultTemplate prop allows customizing the result UI.
670
+
671
+ It receives following props:
672
+
673
+ - searchResults - Array of result items
674
+ - resultClicked - Handler for result click events
675
+ - searchTerm - Search input term
676
+ - metadata - Metadata object
677
+ - executeSearch - Handler for executing srearch
678
+ - store - Main store of app
679
+
680
+ **Example of default render method**
470
681
  ```
471
682
  <app-searchstax-results
472
683
  [afterLinkClick]="afterLinkClick"
473
684
  [resultsTemplate]="resultsTemplate"
474
685
  [noResultsTemplate]="noResultsTemplate"
475
686
  >
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
- >
687
+ <ng-template
688
+ #resultsTemplate
689
+ let-searchResults="searchResults"
690
+ let-resultClicked="resultClicked"
691
+ let-isImage="isImage"
692
+ let-context="context"
693
+ >
694
+ <div class="searchstax-search-results">
695
+ <a
696
+ *ngFor="let searchResult of searchResults"
697
+ [href]="searchResult.url"
698
+ [attr.data-searchstax-unique-result-id]="searchResult.uniqueId"
699
+ (click)="resultClicked(searchResult, $event, context)"
700
+ (keyup.enter)="resultClicked(searchResult, $event, context)"
701
+ (keyup.space)="resultClicked(searchResult, $event, context)"
702
+ tabindex="0"
703
+ class="searchstax-result-item-link searchstax-result-item-link-wrapping"
704
+ >
705
+ <div
706
+ class="searchstax-search-result"
707
+ [ngClass]="{ 'has-thumbnail': searchResult.thumbnail }"
708
+ >
709
+ <div
710
+ *ngIf="searchResult.promoted"
711
+ class="searchstax-search-result-promoted"
712
+ ></div>
713
+ <div
714
+ *ngIf="searchResult.ribbon"
715
+ [innerHTML]="searchResult.ribbon"
716
+ class="searchstax-search-result-ribbon"
717
+ ></div>
718
+ <img
719
+ alt=""
720
+ *ngIf="searchResult.thumbnail"
721
+ [src]="searchResult.thumbnail"
722
+ class="searchstax-thumbnail"
723
+ />
724
+ <div class="searchstax-search-result-title-container">
725
+ <span
726
+ class="searchstax-search-result-title"
727
+ [innerHTML]="searchResult.title"
728
+ ></span>
729
+ </div>
730
+ <p
731
+ *ngIf="searchResult.paths"
732
+ class="searchstax-search-result-common"
733
+ [innerHTML]="searchResult.paths"
734
+ ></p>
735
+ <p
736
+ *ngIf="searchResult.description"
737
+ [innerHTML]="searchResult.description"
738
+ class="searchstax-search-result-description searchstax-search-result-common"
739
+ ></p>
740
+ <div *ngFor="let unmappedField of searchResult.unmappedFields">
741
+ <div
742
+ *ngIf="isImage(unmappedField)"
743
+ class="searchstax-search-result-image-container"
744
+ >
745
+ <img
746
+ alt=""
747
+ [src]="unmappedField.value"
748
+ class="searchstax-result-image"
749
+ />
536
750
  </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>
751
+ <div *ngIf="!isImage(unmappedField)">
752
+ <p
753
+ class="searchstax-search-result-common"
754
+ [innerHTML]="unmappedField.value"
755
+ ></p>
543
756
  </div>
544
- </ng-template>
757
+ </div>
758
+ </div>
759
+ </a>
760
+ </div>
761
+ </ng-template>
762
+ <ng-template
763
+ #noResultsTemplate
764
+ let-metadata="metadata"
765
+ let-searchTerm="searchTerm"
766
+ let-executeSearch="executeSearch"
767
+ let-context="context"
768
+ >
769
+ <div class="searchstax-no-results">
770
+ Showing <strong>no results</strong> for
771
+ <strong>"{{ searchTerm }}"</strong>
772
+ <br />
773
+ <span *ngIf="metadata?.spellingSuggestion"
774
+ >&nbsp;Did you mean
775
+ <a
776
+ href="#"
777
+ [attr.aria-label]="'did you mean: ' + metadata?.spellingSuggestion"
778
+ class="searchstax-suggestion-term"
779
+ (click)="
780
+ executeSearch(metadata?.spellingSuggestion ?? '', $event, context)
781
+ "
782
+ >{{ metadata?.spellingSuggestion }}</a
783
+ >?</span
784
+ >
785
+ </div>
786
+ <ul>
787
+ <li>
788
+ Try searching for search related terms or topics. We offer a wide
789
+ variety of content to help you get the information you need.
790
+ </li>
791
+ <li>Lost? Click on the ‘X” in the Search Box to reset your search.</li>
792
+ </ul>
793
+ </ng-template>
794
+
545
795
  </app-searchstax-results>
546
796
  ```
547
797
 
798
+ **Example of infinite scroll and pagination render methods**
799
+ ```
800
+ <app-searchstax-results
801
+ [afterLinkClick]="afterLinkClick"
802
+ [renderMethod]="'infiniteScroll'"
803
+ ></app-searchstax-results>
804
+
805
+
806
+ ```
807
+
548
808
  ### Pagination Widget ###
549
809
 
810
+ The SearchStax Site Search solution offers an Angular pagination widget to assist with you custom search page.
811
+
812
+ The SearchstaxPaginationWidget for Angular displays pagination controls for search results.
813
+
814
+ **Usage**
550
815
 
551
- example of pagination widget initialization with minimum options
552
816
  ```
553
817
  <app-searchstax-search-pagination></app-searchstax-search-pagination>
554
818
  ```
555
819
 
556
- example of pagination widget initialization with various options
820
+ **Main Template Override**
821
+
822
+ Main template for the pagination controls.
823
+
824
+ It receives following props:
825
+
826
+ - paginationData – Pagination info object
827
+ - previousPage – Handler for previous page click
828
+ - nextPage - – Handler for next page click
829
+
830
+
831
+ **Infinite Scroll Template Override**
832
+
833
+ Main template for the pagination controls in infinite scroll mode.
834
+
835
+ It receives following props:
836
+ - isLastPage - boolean, true if its last page
837
+ - results - results.length can be used if there are results
838
+
839
+ **Example**
840
+
557
841
  ```
558
842
  <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
- ```
843
+ <ng-template
844
+ #paginationTemplate
845
+ let-paginationData="paginationData"
846
+ let-previousPage="previousPage"
847
+ let-nextPage="nextPage"
848
+ let-context="context"
849
+ >
850
+ <div class="searchstax-pagination-container">
851
+ <div class="searchstax-pagination-content">
852
+ <a
853
+ class="searchstax-pagination-previous"
854
+ [class]="paginationData.isFirstPage ? 'disabled' : ''"
855
+ (click)="previousPage($event, context)"
856
+ (keyup.enter)="previousPage($event, context)"
857
+ (keyup.space)="previousPage($event, context)"
858
+ tabindex="0"
859
+ id="searchstax-pagination-previous"
860
+ >
861
+ &lt; Previous
862
+ </a>
863
+ <div class="searchstax-pagination-details">
864
+ {{ paginationData?.startResultIndex }} -
865
+ {{ paginationData?.endResultIndex }} of
866
+ {{ paginationData?.totalResults }}
867
+ </div>
868
+ <a
869
+ class="searchstax-pagination-next"
870
+ [class]="paginationData.isLastPage ? 'disabled' : ''"
871
+ (click)="nextPage($event, context)"
872
+ (keyup.enter)="nextPage($event, context)"
873
+ (keyup.space)="nextPage($event, context)"
874
+ tabindex="0"
875
+ id="searchstax-pagination-next"
876
+ >Next ></a
877
+ >
878
+ </div>
879
+ </div>
880
+ </ng-template>
881
+ </app-searchstax-search-pagination>
882
+
883
+ // infinite scroll example
590
884
 
591
- example of pagination widget with infinite scroll
592
- ```
593
885
  <app-searchstax-search-pagination [templateOverride]="paginationTemplate"
594
886
  [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>
887
+ <ng-template
888
+ #infiniteScrollTemplate
889
+ let-paginationData="paginationData"
890
+ let-nextPage="nextPage"
891
+ let-context="context"
892
+ >
893
+ <div class="searchstax-pagination-container">
894
+ <div class="searchstax-pagination-content">
895
+ <a
896
+ class="searchstax-pagination-load-more"
897
+ (click)="nextPage($event, context)"
898
+ (keyup.enter)="nextPage($event, context)"
899
+ (keyup.space)="nextPage($event, context)"
900
+ tabindex="0"
901
+ >
902
+ Load More
903
+ </a>
904
+ </div>
905
+ </div>
906
+ </ng-template>
907
+ <ng-template
908
+ #paginationTemplate
909
+ let-paginationData="paginationData"
910
+ let-previousPage="previousPage"
911
+ let-nextPage="nextPage"
912
+ let-context="context"
913
+ >
914
+ <div class="searchstax-pagination-container">
915
+ <div class="searchstax-pagination-content">
916
+ <a
917
+ class="searchstax-pagination-previous"
918
+ [class]="paginationData.isFirstPage ? 'disabled' : ''"
919
+ (click)="previousPage($event, context)"
920
+ (keyup.enter)="previousPage($event, context)"
921
+ (keyup.space)="previousPage($event, context)"
922
+ tabindex="0"
923
+ id="searchstax-pagination-previous"
924
+ >
925
+ &lt; Previous
926
+ </a>
927
+ <div class="searchstax-pagination-details">
928
+ {{ paginationData?.startResultIndex }} -
929
+ {{ paginationData?.endResultIndex }} of
930
+ {{ paginationData?.totalResults }}
931
+ </div>
932
+ <a
933
+ class="searchstax-pagination-next"
934
+ [class]="paginationData.isLastPage ? 'disabled' : ''"
935
+ (click)="nextPage($event, context)"
936
+ (keyup.enter)="nextPage($event, context)"
937
+ (keyup.space)="nextPage($event, context)"
938
+ tabindex="0"
939
+ id="searchstax-pagination-next"
940
+ >Next ></a
941
+ >
942
+ </div>
943
+ </div>
944
+ </ng-template>
642
945
  </app-searchstax-search-pagination>
643
946
  ```
644
947
 
645
948
  ### Facets Widget ###
646
949
 
647
- example of facets widget initialization with minimum options
950
+ The SearchStax Site Search solution offers the SearchstaxFacetsWidget for Angular to display the search facets.
951
+
952
+ **Facet Selection and Order**
953
+
954
+ Facet lists are configured and ordered on the Site Search [Faceting Tab](https://www.searchstax.com/docs/searchstudio/faceting-tab/).
955
+
956
+ **Usage**
957
+
648
958
  ```
649
959
  <app-searchstax-search-facets
650
960
  [facetingType]="'showUnavailable'"
@@ -653,8 +963,50 @@ example of facets widget initialization with minimum options
653
963
  ></app-searchstax-search-facets>
654
964
  ```
655
965
 
966
+ **Props**
967
+
968
+ - facetingType: "and" | "or" | "showUnavailable" | "tabs"; // type that determines how facets will behave
969
+ - specificFacets?: string[]; // optional array of facet names that if provided will only render those facets
970
+ - itemsPerPageDesktop: number; // default expanded facets for desktop
971
+ - itemsPerPageMobile: number; // default expanded facets for mobile
972
+
973
+
974
+ **Main Template Desktop Override**
975
+
976
+ Main wrapper template for desktop facets display.
977
+
978
+ It receives following props:
979
+ - facetsTemplateDataDesktop - Facets data object
980
+ - isNotDeactivated - Check if facet group is active
981
+ - toggleFacetGroup - Toggle facet group active state
982
+ - isChecked - Check if facet value is selected
983
+ - selectFacet - Handler for facet select
984
+ - showMoreLessDesktop - Show more/less facets handler
985
+ - facetContainers - Object of facet DOM containers
986
+ - updateRefDesktop - Handler for keeping checkbox references
656
987
 
657
- example of facets widget initialization with template overrides
988
+
989
+ **Main Template Mobile Override**
990
+
991
+ Main wrapper template for mobile facets display.
992
+
993
+ It receives following props:
994
+ - facetsTemplateDataMobile - Facets data object
995
+ - selectedFacetsCheckboxes - Selected facet values
996
+ - isNotDeactivated - Check if facet group is active
997
+ - toggleFacetGroup - Toggle facet group active state
998
+ - isChecked - Check if facet value is selected
999
+ - selectFacet - Handler for facet select
1000
+ - showMoreLessDesktop - Show more/less facets handler
1001
+ - facetContainers - Object of facet DOM containers
1002
+ - openOverlay - Handler to open mobile overlay
1003
+ - unselectFacet - Handler to unselect specific facet
1004
+ - unselectAll - Handler to unselect all facets
1005
+ - closeOverlay - Handler to close mobile overlay
1006
+ - updateRefMobile - Handler for keeping mobile checkbox references
1007
+
1008
+
1009
+ **Example**
658
1010
  ```
659
1011
  <app-searchstax-search-facets
660
1012
  [facetingType]="'showUnavailable'"
@@ -663,371 +1015,521 @@ example of facets widget initialization with template overrides
663
1015
  [templateOverrideDesktop]="templateOverrideDesktop"
664
1016
  [templateOverrideMobile]="templateOverrideMobile"
665
1017
  >
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"
1018
+ <ng-template
1019
+ #templateOverrideDesktop
1020
+ let-facetsTemplateDataDesktop="facetsTemplateDataDesktop"
1021
+ let-isNotDeactivated="isNotDeactivated"
1022
+ let-context="context"
1023
+ let-toggleFacetGroup="toggleFacetGroup"
1024
+ let-facetValues="facetValues"
1025
+ let-isChecked="isChecked"
1026
+ let-selectFacet="selectFacet"
1027
+ let-showMoreLessDesktop="showMoreLessDesktop"
1028
+ >
1029
+ <div class="searchstax-facets-container-desktop">
1030
+ <div
1031
+ class="searchstax-facet-container"
1032
+ *ngFor="let facet of facetsTemplateDataDesktop?.facets"
1033
+ [class]="{ active: isNotDeactivated(facet.name, context) }"
1034
+ >
1035
+ <div>
1036
+ <div
1037
+ class="searchstax-facet-title-container"
1038
+ (click)="toggleFacetGroup(facet.name, context)"
676
1039
  >
677
- <div class="searchstax-facets-container-desktop">
1040
+ <div class="searchstax-facet-title">
1041
+ {{ facet.label }}
1042
+ </div>
1043
+ <div class="searchstax-facet-title-arrow active"></div>
1044
+ </div>
1045
+ <div class="searchstax-facet-values-container" aria-live="polite">
1046
+ <div
1047
+ *ngFor="let facetValue of facetValues(facet); index as key"
1048
+ class="searchstax-facet-value-container"
1049
+ [class]="{
1050
+ 'searchstax-facet-value-disabled': facetValue.disabled
1051
+ }"
1052
+ #ref
1053
+ >
1054
+ <div class="searchstax-facet-input">
1055
+ <input
1056
+ type="checkbox"
1057
+ class="searchstax-facet-input-checkbox"
1058
+ [checked]="isChecked(facetValue, context)"
1059
+ [attr.aria-label]="
1060
+ facetValue.value + ' ' + facetValue.count
1061
+ "
1062
+ [disabled]="facetValue.disabled"
1063
+ (click)="
1064
+ selectFacet(ref, $event, facetValue, true, context)
1065
+ "
1066
+ />
1067
+ </div>
678
1068
  <div
679
- class="searchstax-facet-container"
680
- *ngFor="let facet of facetsTemplateDataDesktop?.facets"
681
- [class]="{ active: isNotDeactivated(facet.name, context) }"
1069
+ class="searchstax-facet-value-label"
1070
+ (click)="selectFacet(ref, $event, facetValue, false, context)"
682
1071
  >
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>
1072
+ {{ facetValue.value }}
1073
+ </div>
1074
+ <div
1075
+ class="searchstax-facet-value-count"
1076
+ (click)="selectFacet(ref, $event, facetValue, false, context)"
1077
+ >
1078
+ ({{ facetValue.count }})
1079
+ </div>
1080
+ </div>
1081
+ <div
1082
+ class="searchstax-facet-show-more-container"
1083
+ *ngIf="facet.hasMoreFacets"
1084
+ >
1085
+ <div
1086
+ class="searchstax-facet-show-more-container"
1087
+ (click)="showMoreLessDesktop($event, facet, context)"
1088
+ (keyup.enter)="showMoreLessDesktop($event, facet, context)"
1089
+ (keyup.space)="showMoreLessDesktop($event, facet, context)"
1090
+ tabindex="0"
1091
+ >
1092
+ <div
1093
+ *ngIf="facet.showingAllFacets"
1094
+ class="searchstax-facet-show-less-button searchstax-facet-show-button"
1095
+ >
1096
+ less
1097
+ </div>
1098
+ <div
1099
+ *ngIf="!facet.showingAllFacets"
1100
+ class="searchstax-facet-show-more-button searchstax-facet-show-button"
1101
+ >
1102
+ more
737
1103
  </div>
738
1104
  </div>
739
1105
  </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"
1106
+ </div>
1107
+ </div>
1108
+ </div>
1109
+ </div>
1110
+ </ng-template>
1111
+ <ng-template
1112
+ #templateOverrideMobile
1113
+ let-isNotDeactivated="isNotDeactivated"
1114
+ let-context="context"
1115
+ let-toggleFacetGroup="toggleFacetGroup"
1116
+ let-facetValues="facetValues"
1117
+ let-isChecked="isChecked"
1118
+ let-selectFacet="selectFacet"
1119
+ let-openOverlay="openOverlay"
1120
+ let-selectedFacetsCheckboxes="selectedFacetsCheckboxes"
1121
+ let-unselectFacet="unselectFacet"
1122
+ let-unselectAll="unselectAll"
1123
+ let-facetMobileData="facetMobileData"
1124
+ let-facetsTemplateDataMobile="facetsTemplateDataMobile"
1125
+ let-closeOverlay="closeOverlay"
1126
+ let-showMoreLessDesktop="showMoreLessDesktop"
1127
+ let-showMoreLessMobile="showMoreLessMobile"
1128
+ >
1129
+ <div class="searchstax-facets-container-mobile">
1130
+ <div class="searchstax-facets-pills-container">
1131
+ <div
1132
+ class="searchstax-facets-pill searchstax-facets-pill-filter-by"
1133
+ (click)="openOverlay(context)"
1134
+ >
1135
+ <div class="searchstax-facets-pill-label">Filter By</div>
1136
+ </div>
1137
+ <div class="searchstax-facets-pills-selected">
1138
+ <div
1139
+ class="searchstax-facets-pill searchstax-facets-pill-facets"
1140
+ *ngFor="let facet of selectedFacetsCheckboxes"
1141
+ (click)="unselectFacet(facet, context)"
1142
+ >
1143
+ <div class="searchstax-facets-pill-label">
1144
+ {{ facet.value }} ({{ facet.count }})
1145
+ </div>
1146
+ <div class="searchstax-facets-pill-icon-close"></div>
1147
+ </div>
1148
+ </div>
1149
+ <div
1150
+ class="searchstax-facets-pill searchstax-clear-filters searchstax-facets-pill-clear-all"
1151
+ *ngIf="selectedFacetsCheckboxes.length"
1152
+ (click)="unselectAll(context)"
1153
+ >
1154
+ <div class="searchstax-facets-pill-label">Clear Filters</div>
1155
+ </div>
1156
+ </div>
1157
+ <div
1158
+ class="searchstax-facets-mobile-overlay"
1159
+ [class]="{
1160
+ 'searchstax-show': facetMobileData(facetsTemplateDataMobile)
1161
+ ?.overlayOpened
1162
+ }"
1163
+ >
1164
+ <div class="searchstax-facets-mobile-overlay-header">
1165
+ <div class="searchstax-facets-mobile-overlay-header-title">
1166
+ Filter By
1167
+ </div>
1168
+ <div
1169
+ class="searchstax-search-close"
1170
+ (click)="closeOverlay(context)"
1171
+ ></div>
1172
+ </div>
1173
+ <div class="searchstax-facets-container-mobile">
1174
+ <div
1175
+ *ngFor="let facet of facetsTemplateDataMobile?.facets"
1176
+ class="searchstax-facet-container"
1177
+ [class]="{
1178
+ active: isNotDeactivated(facet.name, context)
1179
+ }"
757
1180
  >
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>
1181
+ <div>
1182
+ <div
1183
+ class="searchstax-facet-title-container"
1184
+ (click)="toggleFacetGroup(facet.name, context)"
1185
+ >
1186
+ <div class="searchstax-facet-title">
1187
+ {{ facet.label }}
762
1188
  </div>
763
- <div class="searchstax-facets-pills-selected">
1189
+ <div class="searchstax-facet-title-arrow active"></div>
1190
+ </div>
1191
+ <div
1192
+ class="searchstax-facet-values-container"
1193
+ aria-live="polite"
1194
+ >
1195
+ <div
1196
+ *ngFor="let facetValue of facetValues(facet); index as key"
1197
+ class="searchstax-facet-value-container"
1198
+ [class]="{
1199
+ 'searchstax-facet-value-disabled': facetValue.disabled
1200
+ }"
1201
+ #ref
1202
+ >
1203
+ <div class="searchstax-facet-input">
1204
+ <input
1205
+ type="checkbox"
1206
+ class="searchstax-facet-input-checkbox"
1207
+ [checked]="isChecked(facetValue, context)"
1208
+ [attr.aria-label]="
1209
+ facetValue.value + ' ' + facetValue.count
1210
+ "
1211
+ [disabled]="facetValue.disabled"
1212
+ (click)="
1213
+ selectFacet(ref, $event, facetValue, true, context)
1214
+ "
1215
+ />
1216
+ </div>
1217
+ <div
1218
+ class="searchstax-facet-value-label"
1219
+ (click)="
1220
+ selectFacet(ref, $event, facetValue, false, context)
1221
+ "
1222
+ >
1223
+ {{ facetValue.value }}
1224
+ </div>
764
1225
  <div
765
- class="searchstax-facets-pill searchstax-facets-pill-facets"
766
- *ngFor="let facet of selectedFacetsCheckboxes"
767
- (click)="unselectFacet(facet, context)"
1226
+ class="searchstax-facet-value-count"
1227
+ (click)="
1228
+ selectFacet(ref, $event, facetValue, false, context)
1229
+ "
768
1230
  >
769
- <div class="searchstax-facets-pill-label">{{ facet.value }} ({{ facet.count }})</div>
770
- <div class="searchstax-facets-pill-icon-close"></div>
1231
+ ({{ facetValue.count }})
771
1232
  </div>
772
1233
  </div>
773
1234
  <div
774
- class="searchstax-facets-pill searchstax-clear-filters searchstax-facets-pill-clear-all"
775
- *ngIf="selectedFacetsCheckboxes.length"
776
- (click)="unselectAll(context)"
1235
+ class="searchstax-facet-show-more-container"
1236
+ *ngIf="facet.hasMoreFacets"
777
1237
  >
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
1238
  <div
791
- *ngFor="let facet of facetsTemplateDataMobile?.facets"
792
- class="searchstax-facet-container"
793
- [class]="{ active: isNotDeactivated(facet.name, context) }"
1239
+ class="searchstax-facet-show-more-container"
1240
+ (click)="showMoreLessMobile($event, facet, context)"
1241
+ (keyup.enter)="showMoreLessMobile($event, facet, context)"
1242
+ (keyup.space)="showMoreLessMobile($event, facet, context)"
1243
+ tabindex="0"
794
1244
  >
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>
1245
+ <div
1246
+ *ngIf="facet.showingAllFacets"
1247
+ class="searchstax-facet-show-less-button searchstax-facet-show-button"
1248
+ >
1249
+ less
1250
+ </div>
1251
+ <div
1252
+ *ngIf="!facet.showingAllFacets"
1253
+ class="searchstax-facet-show-more-button searchstax-facet-show-button"
1254
+ >
1255
+ more
849
1256
  </div>
850
1257
  </div>
851
1258
  </div>
852
- <button class="searchstax-facets-mobile-overlay-done" (click)="closeOverlay(context)">Done</button>
853
1259
  </div>
854
1260
  </div>
855
- </ng-template>
1261
+ </div>
1262
+ </div>
1263
+ <button
1264
+ class="searchstax-facets-mobile-overlay-done"
1265
+ (click)="closeOverlay(context)"
1266
+ >
1267
+ Done
1268
+ </button>
1269
+ </div>
1270
+ </div>
1271
+ </ng-template>
856
1272
  </app-searchstax-search-facets>
857
1273
  ```
858
1274
 
859
1275
  ### SearchFeedback Widget ###
860
1276
 
861
- example of search feedback widget initialization with minimum options
1277
+ The SearchStax Site Search solution includes an Angular feedback widget for your custom search page.
1278
+
1279
+ The SearchstaxFeedbackWidget for Angular displays search feedback and stats.
1280
+
1281
+ **Usage**
1282
+
862
1283
  ```
863
1284
  <app-searchstax-search-feedback></app-searchstax-search-feedback>
864
1285
  ```
865
1286
 
1287
+ **Main Template Override**
1288
+
1289
+ Main template for the search feedback message.
1290
+
1291
+ It receives following props:
1292
+ - searchFeedbackData - Feedback data object
1293
+ - onOriginalQueryClick - Handler for clicking on original query suggestion
866
1294
 
867
- example of search feedback widget initialization with template overrides
1295
+ **Example**
868
1296
  ```
869
1297
  <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>
1298
+ <ng-template
1299
+ #feedbackTemplate
1300
+ let-searchFeedbackData="searchFeedbackData"
1301
+ let-onOriginalQueryClick="onOriginalQueryClick"
1302
+ let-context="context"
1303
+ >
1304
+ <h2 class="searchstax-feedback-container">
1305
+ Showing
1306
+ <b
1307
+ >{{ searchFeedbackData!.startResultIndex }} -
1308
+ {{ searchFeedbackData!.endResultIndex }}</b
1309
+ >
1310
+ of <b>{{ searchFeedbackData!.totalResults }}</b> results
1311
+ <span *ngIf="searchFeedbackData.searchTerm"
1312
+ >for "<b>{{ searchFeedbackData!.searchTerm }}</b
1313
+ >"
1314
+ </span>
1315
+ <div class="searchstax-feedback-container-suggested">
1316
+ <div *ngIf="searchFeedbackData.autoCorrectedQuery">
1317
+ Search instead for
1318
+ <a
1319
+ href="#"
1320
+ (click)="onOriginalQueryClick($event, context)"
1321
+ class="searchstax-feedback-original-query"
1322
+ [attr.aria-label]="
1323
+ 'Search instead for: ' + searchFeedbackData!.originalQuery
1324
+ "
1325
+ >{{ searchFeedbackData!.originalQuery }}</a
1326
+ >
1327
+ </div>
1328
+ </div>
1329
+ </h2>
1330
+ </ng-template>
1331
+ </app-searchstax-search-feedback>
898
1332
  ```
899
1333
 
900
- ## RelatedSearches widget
1334
+ ### RelatedSearches widget ###
901
1335
 
1336
+ The SearchStax Site Search solution offers an Angular related-searches widget for your custom search page.
902
1337
 
1338
+ The SearchstaxRelatedSearchesWidget displays related searches.
903
1339
 
904
- example of search RelatedSearches widget initialization with minimum options
1340
+ **Usage**
905
1341
  ```
906
- <app-searchstax-related-searches
1342
+ <app-searchstax-related-searches
907
1343
  [relatedSearchesURL]="config.relatedSearchesURL"
908
1344
  [relatedSearchesAPIKey]="config.relatedSearchesAPIKey"
909
1345
  ></app-searchstax-related-searches>
910
1346
  ```
911
1347
 
1348
+ **Props**
1349
+
1350
+ - relatedSearchesURL: API URL for fetching related searches
1351
+ - relatedSearchesAPIKey?: API key for related searches API
1352
+
1353
+ **Main Template Override**
1354
+
1355
+ Main template for related searches.
1356
+
1357
+ It receives following props:
1358
+
1359
+ - relatedData - Related searches data object
1360
+ - executeSearch - Handler to run new search from related term
1361
+
1362
+ **Example**
912
1363
 
913
- example of search RelatedSearches widget initialization with template overrides
914
1364
  ```
915
- <app-searchstax-related-searches
1365
+ <app-searchstax-related-searches
916
1366
  [relatedSearchesURL]="config.relatedSearchesURL"
917
1367
  [relatedSearchesAPIKey]="config.relatedSearchesAPIKey"
918
1368
  [templateOverride]="relatedTemplate"
919
1369
  >
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>
1370
+ <ng-template
1371
+ #relatedTemplate
1372
+ let-rand="rand"
1373
+ let-relatedData="relatedData"
1374
+ let-executeSearch="executeSearch"
1375
+ let-context="context"
1376
+ >
1377
+ <div
1378
+ class="searchstax-related-searches-container"
1379
+ [id]="'searchstax-related-searches-container' + rand"
1380
+ >
1381
+ Related searches: <span id="searchstax-related-searches"></span>
1382
+ <span
1383
+ class="searchstax-related-search"
1384
+ *ngIf="relatedData.relatedSearches"
1385
+ >
1386
+ <span
1387
+ *ngFor="let related of relatedData.relatedSearches"
1388
+ (click)="executeSearch(related, context)"
1389
+ (keyup.enter)="executeSearch(related, context)"
1390
+ (keyup.space)="executeSearch(related, context)"
1391
+ tabindex="0"
1392
+ class="searchstax-related-search searchstax-related-search-item"
1393
+ [attr.aria-label]="'Related search: ' + related.related_search"
1394
+ >
1395
+ {{ related.related_search }}<span *ngIf="!related.last">,</span>
1396
+ </span>
1397
+ </span>
1398
+ </div>
1399
+ </ng-template>
1400
+ </app-searchstax-related-searches>
941
1401
  ```
942
1402
 
943
1403
  ### ExternalPromotions widget ###
944
1404
 
945
- example of search ExternalPromotions widget initialization with minimum options
1405
+ The SearchStax Site Search solution offers an Angular external-promotions widget for your custom search page.
1406
+
1407
+ The SearchstaxExternalPromotionsWidget displays external promotions fetched from the API.
1408
+
1409
+ **Usage**
1410
+
946
1411
  ```
947
1412
  <app-searchstax-external-promotions></app-searchstax-external-promotions>
948
1413
  ```
949
1414
 
1415
+ **Main Template Override**
1416
+
1417
+ Main template for external promotions.
950
1418
 
951
- example of search ExternalPromotions widget initialization with template overrides
1419
+ It receives following props:
1420
+
1421
+ - externalPromotionsData – External promotions data object
1422
+ - trackClick – Handler for tracking link clicks
1423
+
1424
+ **Example**
952
1425
  ```
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>
1426
+ <app-searchstax-external-promotions [templateOverride]="externalPromotionsTemplate">
1427
+ <ng-template
1428
+ #externalPromotionsTemplate
1429
+ let-context="context"
1430
+ let-externalPromotionsData="externalPromotionsData"
1431
+ let-trackClick="trackClick"
1432
+ >
1433
+ <div
1434
+ class="searchstax-external-promotions-container"
1435
+ id="searchstax-external-promotions-container"
1436
+ >
1437
+ <div
1438
+ class="searchstax-external-promotion searchstax-search-result"
1439
+ *ngFor="
1440
+ let externalPromotion of externalPromotionsData.externalPromotions
1441
+ "
1442
+ >
1443
+ <div class="icon-elevated"></div>
1444
+ <a
1445
+ *ngIf="externalPromotion.url"
1446
+ [href]="externalPromotion.url"
1447
+ (click)="trackClick(externalPromotion, $event, context)"
1448
+ class="searchstax-result-item-link"
1449
+ ></a>
1450
+ <div class="searchstax-search-result-title-container">
1451
+ <span class="searchstax-search-result-title">{{
1452
+ externalPromotion.name
1453
+ }}</span>
1454
+ </div>
1455
+ <p
1456
+ *ngIf="externalPromotion.description"
1457
+ class="searchstax-search-result-description searchstax-search-result-common"
1458
+ >
1459
+ {{ externalPromotion.description }}
1460
+ </p>
1461
+ <p
1462
+ *ngIf="externalPromotion.url"
1463
+ class="searchstax-search-result-description searchstax-search-result-common"
1464
+ >
1465
+ {{ externalPromotion.url }}
1466
+ </p>
1467
+ </div>
1468
+ </div>
1469
+ </ng-template>
990
1470
  </app-searchstax-external-promotions>
991
1471
  ```
992
1472
 
993
1473
 
994
1474
  ### Sorting Widget ###
995
1475
 
996
- example of sorting widget initialization with minimum options
1476
+ The SearchStax Site Search solution provides an Angular sorting widget for your custom search page.
1477
+
1478
+ The SearchstaxSortingWidget displays sorting options for search results.
1479
+
1480
+ **Usage**
1481
+
997
1482
  ```
998
1483
  <app-searchstax-search-sorting></app-searchstax-search-sorting>
999
1484
  ```
1000
1485
 
1486
+ **Main Template Override**
1487
+
1488
+ Main template for sorting widget.
1489
+
1490
+ It receives following props:
1491
+
1492
+ - sortingData – Sorting data object
1493
+ - orderChange – Handler for sorting change
1494
+ - selectedSorting – Current selected sorting
1495
+
1496
+ **Example**
1001
1497
 
1002
- example of sorting widget initialization with template override
1003
1498
  ```
1004
1499
  <app-searchstax-search-sorting [templateOverride]="sortingTemplateOverride">
1005
- <ng-template
1006
- #sortingTemplateOverride
1007
- let-selectedSorting="selectedSorting"
1008
- let-orderChange="orderChange"
1009
- let-context="context"
1500
+ <ng-template
1501
+ #sortingTemplateOverride
1502
+ let-selectedSorting="selectedSorting"
1503
+ let-orderChange="orderChange"
1504
+ let-context="context"
1505
+ >
1506
+ <div class="searchstax-sorting-container">
1507
+ <label
1508
+ class="searchstax-sorting-label"
1509
+ for="searchstax-search-order-select"
1510
+ >Sort By</label
1511
+ >
1512
+ <select
1513
+ id="searchstax-search-order-select"
1514
+ class="searchstax-search-order-select"
1515
+ [(ngModel)]="context.selectedSorting"
1516
+ (change)="orderChange($event, context)"
1517
+ >
1518
+ <option
1519
+ *ngFor="let sortOption of context.sortingData.sortOptions"
1520
+ [value]="sortOption.key"
1010
1521
  >
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>
1522
+ {{ sortOption.value }}
1523
+ </option>
1524
+ </select>
1525
+ </div>
1526
+ </ng-template>
1025
1527
  </app-searchstax-search-sorting>
1026
1528
  ```
1027
1529
 
1028
1530
 
1029
1531
  ## Template overrides
1030
- Templates are passed to components via props.
1532
+ Templates use vue templating.
1031
1533
 
1032
1534
  ## STYLING
1033
1535
 
@@ -1039,4 +1541,4 @@ css can be taken from
1039
1541
 
1040
1542
  ```
1041
1543
  ./../node_modules/@searchstax-inc/searchstudio-ux-js/dist/styles/mainTheme.css
1042
- ```
1544
+ ```