npm package: @searchstax-inc/searchstudio-ux-js
Description: Library to build Site Search page
Latest version: 4.1.72

# sitesearch-ux-js

Library to build Site Search page

## Installation
npm install following package
`npm install --save @searchstax-inc/searchstudio-ux-js`


## Usage

After importing Searchstax class a new instance needs to be created:

```const searchstax = new Searchstax();```

## Initialization
Initialization object needs to be of type: `ISearchstaxConfig`
```
interface ISearchstaxConfig {
  sessionId?: string; // session id that is used for tracking. if not defined random value will be generated
  language: string; // language code. Example: 'en'
  model?: string; // Site Search model name.
  searchURL: string; // Site Search select endpoint
  suggesterURL: string; //Site Search suggest endpoint
  trackApiKey: string; // Api key used for tracking events
  searchAuth: string; // Authentication value. based on authType it's either a token value or basic auth value
  authType: "token" | "basic"; // Type of authentication
  autoCorrect?: boolean; // if set to true it will autoCorrect misspelled words. Default is false
  router?: IRouterConfig; // optional object containing router settings
  analyticsBaseUrl?: string; // url for analytics calls
  questionURL?: string; // url for AI answer widget
  hooks?: {
    // optional object that provides various hook options
    beforeSearch?: (props: ISearchObject) => ISearchObject | null; // this function gets called before firing search. searchProps 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.
    afterSearch?: (results: ISearchstaxParsedResult[]) => ISearchstaxParsedResult[]; // this function gets called after firing search and before rendering. It needs to return array of results that are either modified or untouched.
  };
}
```

Initialization example
```
searchstax.initialize({
    language: "en",
    model: "Main Profile",
    searchURL: "",
    suggesterURL: "",
    trackApiKey: "",
    searchAuth: "",
    sessionId: "yourSessionId",
    authType: "basic",
    questionURL: "",
    router: {
      enabled: true,
      routeName: "searchstax",
      title: (result: ISearchObject) => {
        return "Search results for: " + result.query;
      },
      ignoredKeys: [],
    },
    analyticsBaseUrl: 'https://analytics-us-east.searchstax.com',
    hooks: {
      beforeSearch: function (props: ISearchObject) {
        const propsCopy = { ...props };
        return propsCopy;
      },
      afterSearch: function (results: ISearchstaxParsedResult[]) {
        const copy = [...results];
        return copy;
      },
    }
  });
```

## Initial layout
Our base theme is designed with this layout in mind but it is optional as all widgets have id parameters and can be attached to any element.
```
<div class="searchstax-page-layout-container">
      <div id="searchstax-input-container"></div>

      <div class="search-details-container">
        <div id="search-feedback-container"></div>
        <div id="search-sorting-container"></div>
      </div>

      <div class="searchstax-page-layout-facet-result-container">
        <div class="searchstax-page-layout-facet-container">
          <div id="searchstax-facets-container"></div>
        </div>

        <div class="searchstax-page-layout-result-container">
          <div id="searchstax-external-promotions-layout-container"></div>
          <div id="searchstax-results-container"></div>
          <div id="searchstax-related-searches-container"></div>
          <div id="searchstax-pagination-container"></div>
        </div>
      </div>
</div>
```

### widgets
Following widgets are available:

[Answer Widget](#answer-widget)

[Input Widget](#input-widget)

[Location Widget](#location-widget)

[Result Widget](#result-widget)

[Facets Widget](#facets-widget)

[Pagination Widget](#pagination-widget)

[SearchFeedback Widget](#searchfeedback-widget)

[RelatedSearches Widget](#relatedsearches-widget)

[ExternalPromotions Widget](#externalpromotions-widget)

[sorting Widget](#sorting-widget)

### advanced data flow subscriptions
Following widgets are available:

[Data flow subscriptions](#data-flow-subscriptions)

### Answer Widget ###
SearchStax Site Search solution offers Native JS widgets to assist in building your custom search page.

The SearchstaxAnswerWidget component for JS provides an AI answer widget for your searches.

**Usage**

```
searchstax.addAnswerWidget("searchstax-answer-container", {
  showMoreAfterWordCount: 100,
  feedbackwidget: feedbackConfig
});
```

**Props**

- showMoreAfterWordCount - number(default 100) determining after how many words UI will show “Show More” view.
- feedbackWidget – an optional object that configures thumbs-up and thumbs-down feedback functionality.
- templates - template override. look at examples below

Example of feedbackWidget config:
```
const feedbackConfig = {
    renderFeedbackWidget: true,
    emailOverride: searchstaxEmailOverride,
    thumbsUpValue: 10,
    thumbsDownValue: 0,
    lightweightTemplateOverride: `
     <div class="searchstax-lightweight-widget-container">
     <div class="searchstax-lightweight-widget-thumbs-up {{#thumbsUpActive}}active{{/thumbsUpActive}}">
     <svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
      <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"/>
      </svg>
</div>
     <div class="searchstax-lightweight-widget-separator"></div>
     <div class="searchstax-lightweight-widget-thumbs-down {{#thumbsDownActive}}active{{/thumbsDownActive}}">
      <svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
      <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"/>
      </svg>

     </div>
     </div>
      `,
  }
```


**searchAnswerTemplate**

The templates prop allows customizing the answer UI.

It receives the following props:

- shouldShowAnswer – boolean
- answerErrorMessage – string
- fullAnswerFormatted – string
- showMoreButtonVisible – boolean
- answerLoading – boolean

**Example**

This example is based on [sitesearch-ux-js](https://www.npmjs.com/package/@searchstax-inc/searchstudio-ux-js#answer-widget):

```

searchstax.addAnswerWidget("searchstax-answer-container", {
    showMoreAfterWordCount: 100,
    templates: {
      main: {
          template: `
        {{#shouldShowAnswer}}
        <div class="searchstax-answer-wrap">
            <div class="searchstax-answer-icon"></div>
            <div>
                <div class="searchstax-answer-container {{#showMoreButtonVisible}}searchstax-answer-show-more{{/showMoreButtonVisible}}">
                    <div class="searchstax-answer-title">Smart Answers</div>
                    {{#shouldShowAnswerError}}
                        <div class="searchstax-answer-error">{{{answerErrorMessage}}}</div>
                    {{/shouldShowAnswerError}}
                    <div class="searchstax-answer-description">
                        {{{fullAnswerFormatted}}}
                        {{^showMoreButtonVisible}}
                            {{#answerLoading}}
                                <div class="searchstax-answer-loading"></div>
                            {{/answerLoading}}
                        {{/showMoreButtonVisible}}
                    </div>

                </div>

                {{#showMoreButtonVisible}}
                    <div class="searchstax-answer-load-more-button-container">
                        {{#answerLoading}}
                            <div class="searchstax-answer-loading"></div>
                        {{/answerLoading}}
                        <button class="searchstax-answer-load-more-button">Show More</button>
                    </div>
                {{/showMoreButtonVisible}}
            </div>
            <div class="searchstax-answer-footer">
                <div id="feedbackWidgetContainer"></div>
                <div class="searchstax-lightweight-widget-separator-inline"></div>
                <p class="searchstax-disclaimer">Generative AI is Experimental</p>
            </div>
            </div>
        {{/shouldShowAnswer}}
        `,
          answerContainerId: ``,
      },
    },
    feedbackwidget: feedbackConfig
  });
```

### Input Widget ###

The SearchStax Site Search solution’s Input widget provides the search input field with autosuggest/autocomplete capabilities.

**Usage**
```
searchstax.addSearchInputWidget("searchstax-input-container", {
    suggestAfterMinChars: 3,
 });
```

**Props**

- suggestAfterMinChars - default 3. Number of characters needed for autosuggest to start triggering
- hooks.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.
- hooks.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.
- templates - template override. look at examples below


**inputWidgetTemplate**

The templates prop allows customizing the input UI.

It receives the following props:

- locationEnabled – boolean

**inputWidgetAutosuggestTemplate**

The templates prop allows customizing the input autosuggest UI.

It receives the following props:

- term – suggested term

**example**
```
 searchstax.addSearchInputWidget("searchstax-input-container", {
    suggestAfterMinChars: 3,
    hooks: {
      afterAutosuggest: function (result: ISearchstaxSuggestResponse) {
        const copy = { ...result };
        return copy;
      },
      beforeAutosuggest: function (props: ISearchstaxSuggestProps) {
        // gets suggestProps, if passed along further autosuggest will execute, if null then event gets canceled
        // props can be modified and passed along
        const propsCopy = { ...props };
        // propsCopy.term = propsCopy.term + '222';
        return propsCopy;
      },
    },
    templates: {
    mainTemplate: {
        template: `
        <div class="searchstax-search-input-container searchstax-search-input-container-new {{#locationEnabled}}searchstax-alternative-render{{/locationEnabled}}">
          <div class="searchstax-search-input-wrapper">
            <input type="text" id="searchstax-search-input" class="searchstax-search-input" placeholder="SEARCH FOR..." aria-label="Search" />
          </div>
          <div id="searchstax-location-container" class="searchstax-location-container"></div>
          <button class="searchstax-spinner-icon" id="searchstax-search-input-action-button" aria-label="search" role="button"></button>
        </div>
        `,
        searchInputId: "searchstax-search-input"
    }
    autosuggestItemTemplate: {
        template: `
        <div class="searchstax-autosuggest-item-term-container">{{{term}}}</div>
        `,
    }
    },
  });
```
### Location Widget ###

SearchStax Site Search solution offers a native JS search-location widget to assist with your custom search page.

The SearchstaxLocationWidget provides a location search input with location-based search functionality.

**Usage**

```
function locationDecodeFunction(term: string): Promise<ISearchstaxLocation>{
        return new Promise((resolve) => {
          // make a request to geocoding API to retrieve lat, lon and address and then resolve promise
      resolve({
                  address:'Address',
                  lat: lat,
                  lon: lon,
                  error: false
                });
        });
  }

  function locationDecodeCoordinatesToAddress(lat, lon): Promise<ISearchstaxLocation>{
      return new Promise((resolve) => {
        // make a request to geocoding API to convert lat, lon to address and then resolve promise
      resolve({
                  address:'Address',
                  lat: lat,
                  lon: lon,
                  error: false
                });
      });
    }
 searchstax.addSearchLocationWidget("searchstax-location-container", {
  hooks: {
    locationDecode: locationDecodeFunction,
    locationDecodeCoordinatesToAddress: locationDecodeCoordinatesToAddress
    },
});
```

**Props**

- hooks.locationDecode - callback function to override location decoding
- hooks.locationDecodeCoordinatesToAddress - callback function to override location decoding
- templates - see examples below

**Template Override**

The templates prop allows customizing the location input UI.

It receives the following props:

- locationSearchDistanceValues – location distance values
- shouldShowLocationDistanceDropdown – boolean determining if distance dropdown should be shown


**Example**

```
searchstax.addSearchLocationWidget("searchstax-location-container", {
  templates:{
    mainTemplate: {
      template: `
        <div class="searchstax-location-input-container" data-test-id="searchstax-location-input-container">
            <div class="searchstax-location-input-wrapper">
                <span class="searchstax-location-input-label">NEAR</span>
               <div class="searchstax-location-input-wrapper-inner">
                  <input type="text" id="searchstax-location-input" class="searchstax-location-input" placeholder="Zip, Postal Code or City..." aria-label="Search Location Input" data-test-id="searchstax-location-input" />
                  <button id="searchstax-location-get-current-location" class="searchstax-get-current-location-button">Use my current location</button>
                </div>
                {{#shouldShowLocationDistanceDropdown}}
                  <span class="searchstax-location-input-label">WITHIN</span>
                  <select id="searchstax-location-radius-select" class="searchstax-location-radius-select" aria-label="Search Location Radius Select" data-test-id="searchstax-location-radius-select">
                    {{#locationSearchDistanceValues}}
                      <option value="{{value}}" {{#isSelected}}selected{{/isSelected}}>{{label}}</option>
                    {{/locationSearchDistanceValues}}
                  </select>
                {{/shouldShowLocationDistanceDropdown}}
            </div>
        </div>
        `,
      locationInputId: "searchstax-location-input",
      radiusInputId: "searchstax-location-radius-select"
    },
  },
  hooks: {
    locationDecode: locationDecodeFunction,
    locationDecodeCoordinatesToAddress: locationDecodeCoordinatesToAddress
  },
});
```

### Result Widget ###

The SearchStax Site Search solution’s Results Widget for UX-JS displays the search results.

**Usage**

```
searchstax.addSearchResultsWidget("searchstax-results-container", {});
```

**Props**

- renderMethod – either “pagination” or “infiniteScroll”.
- resultsPerPage – number of results on a page.
- hooks.afterLinkClick – Callback function invoked when a result link is clicked. Allows modifying the result object.
- templates - see examples below

**Main Template Override**

The template prop allows customizing the result UI.

It receives no props:

**Search Result Template Override**

The template prop allows customizing the result UI.

It receives following props:

- custom?: any - custom properties that may be added through aftersearchHook
- ribbon: string | null
- paths: string | null;
- url: string | null;
- title: string | null;
- titleTracking: string | null;
- promoted: boolean | null;
- thumbnail: string | null;
- date: string | null;
- snippet: string | null;
- description: string | null;
- uniqueId: string;
- position: number;
- distance: number | null;
- unit: string | null;
- unmappedFields: {
   key: string;
   value: string | string[] | boolean;
   isImage?: boolean;
 }[] - fields that were not mapped.
- allFields: { key: string; value: string | string[] | boolean }[] - all fields

**No Results Template Override**

The template prop allows customizing the result UI.

It receives following props:

- spellingSuggestion - suggestion of corrected spelling
- searchExecuted - boolean if search was executed
- searchTerm - term that was searched

**Example of default render method**
```
 searchstax.addSearchResultsWidget("searchstax-results-container", {
    templates: {
      mainTemplate: {
      template: `
        <section aria-label="search results container" tabindex="0">
            <div class="searchstax-search-results-container" id="searchstax-search-results-container" data-test-id="searchstax-search-results-container">
                <div class="searchstax-search-results" id="searchstax-search-results"></div>
            </div>
        </section>
            `,
        searchResultsContainerId: "searchstax-search-results",
      },
      searchResultTemplate: {
        template: `
    <a href="{{url}}" data-searchstax-unique-result-id="{{uniqueId}}" data-test-id="searchstax-result-item-link" class="searchstax-result-item-link searchstax-result-item-link-wrapping" tabindex="0">
    <div class="searchstax-search-result searchstax-search-result-wrapping {{#thumbnail}} has-thumbnail {{/thumbnail}}">
        {{#promoted}}
            <div class="searchstax-search-result-promoted" data-test-id="searchstax-search-result-promoted"></div>
        {{/promoted}}

        {{#ribbon}}
            <div class="searchstax-search-result-ribbon" data-test-id="searchstax-search-result-ribbon">
            {{{ribbon}}}
            </div>
        {{/ribbon}}

        {{#thumbnail}}
            <img alt="" src="{{thumbnail}}" alt="image" class="searchstax-thumbnail" data-test-id="searchstax-thumbnail">
        {{/thumbnail}}
        <div class="searchstax-search-result-title-container" data-test-id="searchstax-search-result-title-container">
            <h3 class="searchstax-search-result-title" id="title-{{uniqueId}}">{{{title}}}</h3>
        </div>

        {{#paths}}
            <p class="searchstax-search-result-common" data-test-id="searchstax-search-result-common">
                {{{paths}}}
            </p>
        {{/paths}}

        {{#description}}
            <p class="searchstax-search-result-description searchstax-search-result-common" data-test-id="searchstax-search-result-description">
                {{{description}}}
            </p>
        {{/description}}

        {{#unmappedFields}}
            {{#isImage}}
                <div class="searchstax-search-result-image-container">
                <img alt="" src="{{value}}" alt="image" class="searchstax-result-image" data-test-id="searchstax-result-image">
                </div>
            {{/isImage}}
            {{^isImage}}
                <p class="searchstax-search-result-common">
                {{{value}}}
                </p>
            {{/isImage}}
        {{/unmappedFields}}
        {{#distance}}
            <p class="searchstax-search-result-distance searchstax-search-result-common" data-test-id="searchstax-search-result-distance">
                {{distance}} {{unit}}
            </p>
        {{/distance}}
        </div>
        </a>
            `,
        searchResultUniqueIdAttribute: "data-searchstax-unique-result-id"
      },
      noSearchResultTemplate: {
        template: `
    {{#searchExecuted}}
    <div class="searchstax-no-results-wrap" data-test-id="searchstax-no-results-wrap">
      <div class="searchstax-no-results" data-test-id="searchstax-no-results">
          Showing <strong>no results</strong> for <strong>"{{ searchTerm }}"</strong>
          <br>
          {{#spellingSuggestion}}
              <span>&nbsp;Did you mean <a href="#" aria-label="Did you mean: {{originalQuery}}" class="searchstax-suggestion-term" onclick="searchCallback('{{ spellingSuggestion }}')">{{ spellingSuggestion }}</a>?</span>
          {{/spellingSuggestion}}
      </div>
      <ul class="searchstax-no-results-list" data-test-id="searchstax-no-results-list">
          <li>Try searching for search related terms or topics. We offer a wide variety of content to help you get the information you need.</li>
      </ul>
    </div>
  {{/searchExecuted}}
            `
      }
    },
    hooks: {
      afterLinkClick: function (result: ISearchstaxParsedResult) {
        // gets result that was clicked, if passed along further functions will execute, if null then event gets canceled
        const propsCopy = { ...result };

        return propsCopy;
      },
    },
  });
```

**Example of infinite scroll render method**
```
 searchstax.addSearchResultsWidget("searchstax-results-container", {
    templates: {
      mainTemplate: {
      template: `
        <section aria-label="search results container" tabindex="0">
            <div class="searchstax-search-results-container" id="searchstax-search-results-container" data-test-id="searchstax-search-results-container">
                <div class="searchstax-search-results" id="searchstax-search-results"></div>
            </div>
        </section>
            `,
        searchResultsContainerId: "searchstax-search-results",
      },
      searchResultTemplate: {
        template: `
    <a href="{{url}}" data-searchstax-unique-result-id="{{uniqueId}}" data-test-id="searchstax-result-item-link" class="searchstax-result-item-link searchstax-result-item-link-wrapping" tabindex="0">
    <div class="searchstax-search-result searchstax-search-result-wrapping {{#thumbnail}} has-thumbnail {{/thumbnail}}">
        {{#promoted}}
            <div class="searchstax-search-result-promoted" data-test-id="searchstax-search-result-promoted"></div>
        {{/promoted}}

        {{#ribbon}}
            <div class="searchstax-search-result-ribbon" data-test-id="searchstax-search-result-ribbon">
            {{{ribbon}}}
            </div>
        {{/ribbon}}

        {{#thumbnail}}
            <img alt="" src="{{thumbnail}}" alt="image" class="searchstax-thumbnail" data-test-id="searchstax-thumbnail">
        {{/thumbnail}}
        <div class="searchstax-search-result-title-container" data-test-id="searchstax-search-result-title-container">
            <h3 class="searchstax-search-result-title" id="title-{{uniqueId}}">{{{title}}}</h3>
        </div>

        {{#paths}}
            <p class="searchstax-search-result-common" data-test-id="searchstax-search-result-common">
                {{{paths}}}
            </p>
        {{/paths}}

        {{#description}}
            <p class="searchstax-search-result-description searchstax-search-result-common" data-test-id="searchstax-search-result-description">
                {{{description}}}
            </p>
        {{/description}}

        {{#unmappedFields}}
            {{#isImage}}
                <div class="searchstax-search-result-image-container">
                <img alt="" src="{{value}}" alt="image" class="searchstax-result-image" data-test-id="searchstax-result-image">
                </div>
            {{/isImage}}
            {{^isImage}}
                <p class="searchstax-search-result-common">
                {{{value}}}
                </p>
            {{/isImage}}
        {{/unmappedFields}}
        {{#distance}}
            <p class="searchstax-search-result-distance searchstax-search-result-common" data-test-id="searchstax-search-result-distance">
                {{distance}} {{unit}}
            </p>
        {{/distance}}
        </div>
        </a>
            `,
        searchResultUniqueIdAttribute: "data-searchstax-unique-result-id"
      },
      noSearchResultTemplate: {
        template: `
    {{#searchExecuted}}
    <div class="searchstax-no-results-wrap" data-test-id="searchstax-no-results-wrap">
      <div class="searchstax-no-results" data-test-id="searchstax-no-results">
          Showing <strong>no results</strong> for <strong>"{{ searchTerm }}"</strong>
          <br>
          {{#spellingSuggestion}}
              <span>&nbsp;Did you mean <a href="#" aria-label="Did you mean: {{originalQuery}}" class="searchstax-suggestion-term" onclick="searchCallback('{{ spellingSuggestion }}')">{{ spellingSuggestion }}</a>?</span>
          {{/spellingSuggestion}}
      </div>
      <ul class="searchstax-no-results-list" data-test-id="searchstax-no-results-list">
          <li>Try searching for search related terms or topics. We offer a wide variety of content to help you get the information you need.</li>
      </ul>
    </div>
  {{/searchExecuted}}
            `
      }
    },
    renderMethod: 'infiniteScroll',
    resultsPerPage: 10,
    hooks: {
      afterLinkClick: function (result: ISearchstaxParsedResult) {
        // gets result that was clicked, if passed along further functions will execute, if null then event gets canceled
        const propsCopy = { ...result };

        return propsCopy;
      },
    },
  });
```

### Pagination Widget ###

The SearchStax Site Search solution’s Pagination Widget displays a pagination control for UX-JS.

**Usage**

```
searchstax.addPaginationWidget("searchstax-pagination-container", {});
```

**Props**
- templates - see examples below


**Multiple Instances**

Multiple instances of the Pagination widget can be added by calling the addPaginationWidget method multiple times with different target containers:

```
searchstax.addPaginationWidget('container1', options1);

searchstax.addPaginationWidget('container2', options2);

```

Each instance can be configured separately via its options object. This allows for the widget to be added to different locations on the page as needed.



**Main Template Override**

Main template for the pagination controls.

It receives following props:

- nextPageLink - link of next page;
- previousPageLink - link of previous page;


**Infinite Scroll Template Override**

Main template for the pagination controls in infinite scroll mode.

It receives following props:
- isLastPage - boolean, true if its last page
- results - results.length can be used if there are results

**Example**

```
 searchstax.addPaginationWidget("searchstax-pagination-container", {
    templates: {
        mainTemplate: {
            template: `
      {{#results.length}}
        <div class="searchstax-pagination-container" data-test-id="searchstax-pagination-container">
          <div class="searchstax-pagination-content">
            <a role="link" class="searchstax-pagination-previous {{#isFirstPage}}disabled{{/isFirstPage}}" aria-disabled="{{#isFirstPage}}true{{/isFirstPage}}{{^isFirstPage}}false{{/isFirstPage}}"  id="searchstax-pagination-previous" data-test-id="searchstax-pagination-previous" tabindex="0" aria-label="Previous Page">< Previous</a>
            <div class="searchstax-pagination-details" data-test-id="searchstax-pagination-details">
              {{startResultIndex}} - {{endResultIndex}} of {{totalResults}}
            </div>
              <a role="link" class="searchstax-pagination-next {{#isLastPage}}disabled{{/isLastPage}}" aria-disabled="{{#isLastPage}}true{{/isLastPage}}{{^isLastPage}}false{{/isLastPage}}" data-test-id="searchstax-pagination-next" id="searchstax-pagination-next" tabindex="0" aria-label="Next Page">Next ></a>
          </div>
        </div>
      {{/results.length}}
      `,
            previousButtonClass: "searchstax-pagination-previous",
            nextButtonClass: "searchstax-pagination-next"
        },
        infiniteScrollTemplate: {
            template: `
      {{#results.length}}
        <div class="searchstax-pagination-container" data-test-id="searchstax-pagination-container">
          <div class="searchstax-pagination-content">
            <a role="link" class="searchstax-pagination-previous {{#isFirstPage}}disabled{{/isFirstPage}}" aria-disabled="{{#isFirstPage}}true{{/isFirstPage}}{{^isFirstPage}}false{{/isFirstPage}}"  id="searchstax-pagination-previous" data-test-id="searchstax-pagination-previous" tabindex="0" aria-label="Previous Page">< Previous</a>
            <div class="searchstax-pagination-details" data-test-id="searchstax-pagination-details">
              {{startResultIndex}} - {{endResultIndex}} of {{totalResults}}
            </div>
              <a role="link" class="searchstax-pagination-next {{#isLastPage}}disabled{{/isLastPage}}" aria-disabled="{{#isLastPage}}true{{/isLastPage}}{{^isLastPage}}false{{/isLastPage}}" data-test-id="searchstax-pagination-next" id="searchstax-pagination-next" tabindex="0" aria-label="Next Page">Next ></a>
          </div>
        </div>
      {{/results.length}}
      `,
            loadMoreButtonClass: "searchstax-pagination-load-more"
        }
    },
  });
```

### Facets Widget ###

The SearchStax Site Search solution’s Facets Widget for UX-JS displays faceted filters for search.

**Facet Selection and Order**

Facet lists are configured and ordered on the Site Search [Faceting Tab](https://www.searchstax.com/docs/searchstudio/faceting-tab/).

**Usage**

```
 searchstax.addFacetsWidget("searchstax-facets-container", {
    facetingType: "and",
    itemsPerPageDesktop: 3,
    itemsPerPageMobile: 99,
 });
```

**Multiple Instances**

Multiple instances of the Facets widget can be added by calling the addFacetsWidget method multiple times with different target containers:

```
searchstax.addFacetsWidget('container1', options1);

searchstax.addFacetsWidget('container2', options2);

```

Each instance can be configured separately via its options object. This allows for the widget to be added to different locations on the page as needed.


**Props**

- facetingType: "and" | "or" | "showUnavailable" | "tabs"; // type that determines how facets will behave
- specificFacets?: string[]; // optional array of facet names that if provided will only render those facets
- itemsPerPageDesktop: number; // default expanded facets for desktop
- itemsPerPageMobile: number; // default expanded facets for mobile
- templates - see examples below
- hooks: Optional object of lifecycle hooks to customize facet data before rendering. Supported hook:
  - **beforeFacetsRender** `(facets: IFacetData[]) => IFacetData[]` — Called with the current facets array before the widget renders. Return the same or a modified array to filter, reorder, or transform facets (e.g., hide certain facets or change their order).


**Main Template Desktop Override**

Main wrapper template for desktop facets display.

It receives following props from [IFacetsTemplateData](https://www.searchstax.com/docs/searchstudio/interfaces/#h-facets-interfaces)


**Main Template Mobile Override**

Main wrapper template for mobile facets display.

It receives following props from [IFacetsTemplateData](https://www.searchstax.com/docs/searchstudio/interfaces/#h-facets-interfaces)

**Show More Button Container Override**

Template for show more button

It receives following props:

- showingAllFacets - boolean true if showing all facets

**Facet Item Container Template Override**

Template for facet item container.

It receives following props:

- label - label of facet


**Clear Facets Template Override**

Template for clear facets.

It receives following props:

- shouldShow - boolean, true if it should be displayed

**Facet Item Template Override**

Template for facet item.

It receives following props:

- disabled - boolean, true if disabled
- isChecked - boolean, true if checked
- value - string, value of facet
- count - number of items for that facet

**Filter By Template Override**

Template for filter by.

It receives no props

**Selected Facets Template Override**

Template for selected facets.

It receives following props:

- value - string, value of facet
- count - number of items for that facet

**Example**
```
 searchstax.addFacetsWidget("searchstax-facets-container", {
    facetingType: "and",
    itemsPerPageDesktop: 3,
    itemsPerPageMobile: 99,
    templates: {
        mainTemplateDesktop: {
            template: `
      {{#hasResultsOrExternalPromotions}}
        <div class="searchstax-facets-container-desktop"></div>
      {{/hasResultsOrExternalPromotions}}
      `,
            facetsContainerId: "",
        },
        mainTemplateMobile: {
            template: `
        <div class="searchstax-facets-pills-container">
          <div class="searchstax-facets-pills-selected">
          </div>
        </div>

        <div class="searchstax-facets-mobile-overlay {{#overlayOpened}} searchstax-show{{/overlayOpened}}" data-test-id="searchstax-facets-mobile-overlay">
          <div class="searchstax-facets-mobile-overlay-header">
            <div class="searchstax-facets-mobile-overlay-header-title">Filter By</div>
            <div class="searchstax-search-close" tabindex="0" aria-label="close overlay" role="button" data-test-id="searchstax-search-close"></div>
          </div>
          <div class="searchstax-facets-container-mobile"></div>
          <button class="searchstax-facets-mobile-overlay-done" data-test-id="searchstax-facets-mobile-overlay-done">Done</button>
        </div>
      `,
            facetsContainerClass: `searchstax-facets-container-mobile`,
            closeOverlayTriggerClasses: ["searchstax-facets-mobile-overlay-done","searchstax-search-close",],
            filterByContainerClass: `searchstax-facets-pills-container`,
            selectedFacetsContainerClass: `searchstax-facets-pills-selected`,
        },
        showMoreButtonContainerTemplate: {
            template: `
      <div class="searchstax-facet-show-more-container" data-test-id="searchstax-facet-show-more-container">
      {{#showingAllFacets}}
        <div class="searchstax-facet-show-less-button searchstax-facet-show-button" tabindex="0" data-test-id="searchstax-facet-show-less-button" data-focus="{{focusId}}" role="button">less</div>
      {{/showingAllFacets}}
      {{^showingAllFacets}}
        <div class="searchstax-facet-show-more-button  searchstax-facet-show-button" tabindex="0" data-test-id="searchstax-facet-show-more-button" data-focus="{{focusId}}" role="button">more {{onShowMoreLessClick}}</div>
      {{/showingAllFacets}}
    </div>
      `,
            showMoreButtonClass: `searchstax-facet-show-more-container`,
        },
        facetItemContainerTemplate: {
            template: `
      <div>
        <div class="searchstax-facet-title-container" data-test-id="searchstax-facet-title-container">
            <div class="searchstax-facet-title" aria-label="Facet group: {{label}}" tabindex="0" role="button">
            {{label}}
            </div>
            <div class="searchstax-facet-title-arrow active"></div>
        </div>
        <div class="searchstax-facet-values-container"></div>
      </div>
      `,
            facetListTitleContainerClass: `searchstax-facet-title-container`,
            facetListContainerClass: `searchstax-facet-values-container`,
        },
        clearFacetsTemplate: {
            template: `
      {{#shouldShow}}
        <div class="searchstax-facets-pill searchstax-clear-filters searchstax-facets-pill-clear-all" tabindex="0" role="button" data-test-id="searchstax-facets-pill-clear-all">
        <div class="searchstax-facets-pill-label">Clear Filters</div>
        </div>
      {{/shouldShow}}
      `,
            containerClass: `searchstax-facets-pill-clear-all`,
        },
        facetItemTemplate: {
            template: `
      <div class="searchstax-facet-input">
        <input type="checkbox" class="searchstax-facet-input-checkbox" data-test-id="searchstax-facet-input-checkbox" {{#disabled}}disabled{{/disabled}} {{#isChecked}}checked{{/isChecked}} aria-label="{{value}} {{count}}" tabindex="0"/>
      </div>
      <div class="searchstax-facet-value-label" data-test-id="searchstax-facet-value-label">{{value}}</div>
      <div class="searchstax-facet-value-count" data-test-id="searchstax-facet-value-count">({{count}})</div>
      `,
            inputCheckboxClass: `searchstax-facet-input-checkbox`,
            checkTriggerClasses: ["searchstax-facet-value-label","searchstax-facet-value-count",],
        },
        filterByTemplate: {
            template: `
      <div class="searchstax-facets-pill searchstax-facets-pill-filter-by" tabindex="0" role="button" data-test-id="searchstax-facets-pill-filter-by" >
        <div class="searchstax-facets-pill-label">Filter By</div>
      </div>
      `,
            containerClass: `searchstax-facets-pill-filter-by`,
        },
        selectedFacetsTemplate: {
            template: `
      <div class="searchstax-facets-pill searchstax-facets-pill-facets" tabindex="0" role="button" data-test-id="searchstax-facets-pill-facets">
        <div class="searchstax-facets-pill-label">{{value}} ({{count}})</div>
        <div class="searchstax-facets-pill-icon-close"></div>
      </div>
      `,
            containerClass: `searchstax-facets-pill-facets`,
        },

    },
  });
```

### SearchFeedback Widget ###

The SearchStax Site Search solution’s Feedback Widget displays search feedback and stats for UX-JS.

**Usage**

```
searchstax.addSearchFeedbackWidget("search-feedback-container", {});
```

**Multiple Instances**

Multiple instances of the SearchFeedback widget can be added by calling the addSearchFeedbackWidget method multiple times with different target containers:

```
searchstax.addSearchFeedbackWidget('container1', options1);

searchstax.addSearchFeedbackWidget('container2', options2);

```

Each instance can be configured separately via its options object. This allows for the widget to be added to different locations on the page as needed.

**Props**
- templates - see examples below

**Main Template Override**

Main template for the search feedback message.

It receives following props:
- searchExecuted - boolean, true if search was executed
- hasResults - boolean, true if search has results
- startResultIndex - number, start of page results
- endResultIndex - number, end of page results
- totalResults - number, total results
- searchTerm - term that was searched
- autoCorrectedQuery - query that search was autocorrected to
- originalQuery - original query

**Example**
```
searchstax.addSearchFeedbackWidget("search-feedback-container", {
    templates: {
      main: {
          template: `
        {{#searchExecuted}}
            <a href="#searchstax-search-results" data-test-id="searchstax-skip" class="searchstax-skip">Skip to results section</a>
            <h4 class="searchstax-feedback-container" data-test-id="searchstax-feedback-container">
              {{#hasResults}}
                 <span> Showing <b>{{startResultIndex}} - {{endResultIndex}}</b> </span> of <b>{{totalResults}}</b> results {{#searchTerm}} for "<b>{{searchTerm}}</b>" {{/searchTerm}}
                  <div class="searchstax-feedback-container-suggested" data-test-id="searchstax-feedback-container-suggested">
                    {{#autoCorrectedQuery}}
                      Search instead for <a href="#" aria-label="Search instead for: {{originalQuery}}" class="searchstax-feedback-original-query" data-test-id="searchstax-feedback-original-query">{{originalQuery}}</a>
                    {{/autoCorrectedQuery}}
                  </div>
                {{/hasResults}}
            </h4>
        {{/searchExecuted}}
        `,
          originalQueryClass: `searchstax-feedback-original-query`
      }
    },
  });
```

### RelatedSearches widget ###

The SearchStax Site Search solution’s Related Searches Widget displays related searches for UX-JS.

**Usage**
```
  searchstax.addRelatedSearchesWidget("searchstax-related-searches-container", {
     relatedSearchesURL: "URL",
     relatedSearchesAPIKey: "KEY"
  })
```

**Props**

- relatedSearchesURL: // : An endpoint from the Discovery tab of the App Settings > All APls screen.
- relatedSearchesAPIKey: The Discovery API key from the Discovery tab of the App Settings > All
- templates - see examples below

**Main Template Override**

Main template for related searches.

It receives following props:

- hasRelatedSearches - boolean, true if has related searches
- relatedSearches - array of related searches


**Related Search Template Override**

Related Search template.

It receives following props:

- related_search - string, related searc
- last - boolean, true if last related search

**Example**

```
searchstax.addRelatedSearchesWidget("searchstax-related-searches-container", {
     relatedSearchesURL: "URL",
     relatedSearchesAPIKey: "KEY",
    templates: {
      main: {
          template: `
        {{#hasRelatedSearches}}
            <div class="searchstax-related-searches-container" data-test-id="searchstax-related-searches-container" id="searchstax-related-searches-container">
                Related searches: <span id="searchstax-related-searches"></span>
                {{#relatedSearches}}
                <span class="searchstax-related-search">

                </span>
            {{/relatedSearches}}
            </div>
        {{/hasRelatedSearches}}
        `,
          relatedSearchesContainerClass: `searchstax-related-search`,
      },
      relatedSearch: {
          template: `
        <span role="link" class="searchstax-related-search searchstax-related-search-item" data-test-id="searchstax-related-search-item" aria-label="Related search: {{related_search}}" tabindex="0">
            {{ related_search }}{{^last}}<span>,</span>{{/last}}
        </span>
        `,
          relatedSearchContainerClass: `searchstax-related-search-item`,
      },
    },
  });
```

### ExternalPromotions widget ###

SearchStax Site Search solution’s External Promotions Widget displays external promotions for UX-JS.

**Usage**

```
  searchstax.addExternalPromotionsWidget("searchstax-external-promotions-layout-container", {})
```

**Props**
- templates - see examples below

**Main Template Override**

Main template for external promotions.

It receives following props:

- hasExternalPromotions - boolean, true if has external promotions

**External promotion Template Override**

Template for external promotion item.

It receives following props:

- url - url of external promotion
- uniqueId - unique id
- name - name of promotion
- description - description

**Example**
```
searchstax.addExternalPromotionsWidget("searchstax-external-promotions-layout-container",  {
    templates: {
      mainTemplate: {
          template: `
        {{#hasExternalPromotions}}
            <div class="searchstax-external-promotions-container" id="searchstax-external-promotions-container" data-test-id="searchstax-external-promotions-container">
                 External promotions go here
            </div>
        {{/hasExternalPromotions}}
    `,
          externalPromotionsContainerId: `searchstax-external-promotions-container`,
      },
      externalPromotion: {
          template: `
        <div class="searchstax-external-promotion searchstax-search-result" data-test-id="searchstax-external-promotion">
            <div class="icon-elevated"></div>
            {{#url}}
            <a href="{{url}}" data-searchstax-unique-result-id="{{uniqueId}}" class="searchstax-result-item-link" data-test-id="searchstax-result-item-link"></a>
            {{/url}}
            <div class="searchstax-search-result-title-container">
                <span class="searchstax-search-result-title" data-test-id="searchstax-search-result-title">{{name}}</span>
            </div>
            {{#description}}
            <p class="searchstax-search-result-description searchstax-search-result-common" data-test-id="searchstax-search-result-description">
            {{description}}
            </p>
            {{/description}}
            {{#url}}
            <p class="searchstax-search-result-description searchstax-search-result-common">
            {{url}}
            </p>
            {{/url}}
        </div>
        `,
      },
    },
  });
```


### Sorting Widget ###

The SearchStax Site Search solution’s Sorting Widget allows sorting of search results for UX-JS.

**Usage**

```
  searchstax.addSearchSortingWidget("search-sorting-container", {});
```

**Props**
- templates - see examples below

**Main Template Override**

Main template for sorting widget.

It receives following props:

- searchExecuted - boolean, true if search was executed
- hasResultsOrExternalPromotions - boolean, true if there are results or external promotions
- sortOptions - array of sort options, has key/value

**Example**

```
searchstax.addSearchSortingWidget("search-sorting-container", {
    templates: {
      main: {
          template: `
      {{#searchExecuted}}
        {{#hasResultsOrExternalPromotions}}
        {{#sortOptions.length}}
        <div class="searchstax-sorting-container" data-test-id="searchstax-sorting-container">
            <label class="searchstax-sorting-label" data-test-id="searchstax-sorting-label" for="searchstax-search-order-select">Sort By</label>
            <select id="searchstax-search-order-select" class="searchstax-search-order-select" data-test-id="searchstax-search-order-select" >
            {{#sortOptions}}
                <option value="{{key}}">
                {{value}}
                </option>
            {{/sortOptions}}
            </select>
        </div>
        {{/sortOptions.length}}
        {{/hasResultsOrExternalPromotions}}
      {{/searchExecuted}}
      `,
          selectId: `searchstax-search-order-select`
      }
    },
  });
```


## Template overrides
Templates use mustache templating. For more info see https://github.com/janl/mustache.js

## STYLING

scss styles can be imported from searchstudio-ux-js
```
 @import './../node_modules/@searchstax-inc/searchstudio-ux-js/dist/styles/scss/mainTheme.scss';
```
css can be taken from

```
./../node_modules/@searchstax-inc/searchstudio-ux-js/dist/styles/mainTheme.css
```

### Data Flow Subscriptions ###
All variables are observables that have these functions:
```
$observable.subscribe((var) => {})
$observable.setValue(value)
$observable.getValue()
```

## Loading change subscription

subscribing to loading event observable is available at

```
searchstax.dataLayer.$loadingChange.subscribe((data) => {})
searchstax.dataLayer.$loadingChange.setValue(data)
const data = searchstax.dataLayer.$loadingChange.getValue()
```

$loadingChange observable is of type: boolean

## Search metadata subscription

subscribing to search metadata event observable is available at

```
searchstax.dataLayer.$searchResultsMetadata.subscribe((data) => {})
searchstax.dataLayer.$searchResultsMetadata.setValue(data)
const data = searchstax.dataLayer.$searchResultsMetadata.getValue()
```

$searchResultsMetadata observable is of type: [ISearchstaxSearchMetadata](https://www.searchstax.com/docs/searchstudio/interfaces/#h-search-results-interfaces) and can be used for custom pagination implementation along with afterSearch hook

## Search pagination data subscription

subscribing to pagination data event observable is available at

```
searchstax.dataLayer.$paginationData.subscribe((data) => {})
searchstax.dataLayer.$paginationData.setValue(data)
const data = searchstax.dataLayer.$paginationData.getValue()
```

$paginationData observable is of type: [IPaginationData](https://www.searchstax.com/docs/searchstudio/interfaces/#h-pagination-interfaces) and can be used for custom pagination implementation along with afterSearch hook

### Data Calculations ###
At any moment data calculation object is accessible with various variables aimed to help build templates in a cleaner way.

## Data calculations

Data calculations can be accessed through

```
searchstax.dataLayer.parsedData.data
```

parsedData.data is of type: [ISearchstaxParsedData](https://www.searchstax.com/docs/searchstudio/interfaces/#h-facets-interfaces)