1.0.27 • Published 8 months ago

@searchstax-inc/searchstudio-ux-react v1.0.27

Weekly downloads
-
License
-
Repository
-
Last release
8 months ago

sitesearch-ux-react

Library to build Site Search page

changelog

changelog is documented at CHANGELOG.md

Installation

npm install following package npm install --save @searchstax-inc/searchstudio-ux-react

Add following code to <head>

<script type="text/javascript">
      var _msq = _msq || []; //declare object
      var analyticsBaseUrl = 'https://analytics-us-east.searchstax.co';
      (function () {
        var ms = document.createElement('script');
        ms.type = 'text/javascript';
        ms.src = 'https://static.searchstax.co/studio-js/v3/js/studio-analytics.js';
        var s = document.getElementsByTagName('script')[0];
        s.parentNode.insertBefore(ms, s);
      })();
    </script>

Usage

After importing SearchstaxWrapper component needs to wrap all other components:

<SearchstaxWrapper
        searchURL={config.searchURL}
        suggesterURL={config.suggesterURL}
        trackApiKey={config.trackApiKey}
        searchAuth={config.searchAuth}
        initialized={initialized}
        beforeSearch={beforeSearch}
        afterSearch={afterSearch}
        authType="basic"
        language="en"
      >
  // other components will go there

  </SearchstaxWrapper>

Initialization

Initialization object needs to be of type: ISearchstaxConfig

Initialization example

sampleConfig = {
    language: "en",
    searchURL: "",
    suggesterURL: "",
    trackApiKey: "",
    searchAuth: "",
    authType: "basic",
    router: {
      enabled: true,
      routeName: "searchstax",
      title: (result: ISearchObject) => {
        return "Search results for: " + result.query;
      },
      ignoredKeys: [],
    },
    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.

<SearchstaxWrapper
        searchURL={sampleConfig.searchURL}
        suggesterURL={sampleConfig.suggesterURL}
        trackApiKey={sampleConfig.trackApiKey}
        searchAuth={sampleConfig.searchAuth}
        beforeSearch={sampleConfig.hooks.beforeSearch}
        afterSearch={sampleConfig.hooks.afterSearch}
        authType="basic"
        language="en"
      >
        <div className="searchstax-page-layout-container">
          <SearchstaxInputWidget
            afterAutosuggest={afterAutosuggest}
            beforeAutosuggest={beforeAutosuggest}
            inputTemplate={InputTemplate}
          ></SearchstaxInputWidget>

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

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

            <div className="searchstax-page-layout-result-container">
              <div id="searchstax-external-promotions-layout-container"></div>
              <SearchstaxResultWidget
                afterLinkClick={afterLinkClick}
                noResultTemplate={noResultTemplate}
                resultsTemplate={resultsTemplate}
              ></SearchstaxResultWidget>
              <div id="searchstax-related-searches-container"></div>
              <div id="searchstax-pagination-container"></div>
            </div>
          </div>
        </div>
      </SearchstaxWrapper>

widgets

Following widgets are available:

Answer Widget

Input Widget

Result Widget

Facets Widget

Pagination Widget

SearchFeedback Widget

RelatedSearches Widget

ExternalPromotions Widget

sorting Widget

Answer Widget

example of answer widget initialization with minimum options

<SearchstaxAnswerWidget
              showShowMoreAfterWordCount={100}
            ></SearchstaxAnswerWidget>

example of answer widget initialization with lightweight feedback widget

export function answerTemplate(
  answerData: null | ISearchstaxAnswerData,
  showMore: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void
) {
  return (
    <>
      {answerData && (
        <div className="searchstax-answer-wrap">
        <div className="searchstax-answer-icon"></div>
        <div>
          <div
            className={
              "searchstax-answer-container " +
              (answerData.showMoreButtonVisible === true ? "searchstax-answer-show-more" : "")
            }
          >
            <div className="searchstax-answer-title">Smart Answers</div>
              {answerData.shouldShowAnswerError && (
                <div className="searchstax-answer-error" dangerouslySetInnerHTML={{__html:answerData.answerErrorMessage}}></div>
              )}
              <div className="searchstax-answer-description" dangerouslySetInnerHTML={{__html:answerData.fullAnswerFormatted}}>
              </div>
                {answerData.answerLoading && (
                  <div className="searchstax-answer-loading"></div>
                )}
          </div>

          {answerData.showMoreButtonVisible === true && (
            <div className="searchstax-answer-load-more-button-container">
              <button
                className="searchstax-answer-load-more-button"
                onClick={(e) => {
                  showMore(e);
                }}
              >
                Read More
              </button>
            </div>
          )}
        </div>
        <div className="searchstax-answer-footer">
            <div id="feedbackWidgetContainer"></div>
            <div className="searchstax-lightweight-widget-separator-inline"></div>
            <p className="searchstax-disclaimer">Generative AI is Experimental</p>
        </div>
        </div>
      )}
    </>
  );
}

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>
    `,
}

<SearchstaxAnswerWidget
              searchAnswerTemplate={answerTemplate}
              showShowMoreAfterWordCount={100}
              feedbackwidget={feedbackConfig}
            ></SearchstaxAnswerWidget>

example of answer widget initialization without feedback widget

<SearchstaxAnswerWidget
              searchAnswerTemplate={answerTemplate}
              showShowMoreAfterWordCount={100}
            ></SearchstaxAnswerWidget>

Input Widget

example of input widget initialization with minimum options

<SearchstaxInputWidget
            afterAutosuggest={afterAutosuggest}
            beforeAutosuggest={beforeAutosuggest}
          ></SearchstaxInputWidget>

example of input widget initialization with template override

 function InputTemplate(
    suggestions: ISearchstaxSuggestion[],
    onMouseLeave: () => void,
    onMouseOver: (suggestion: ISearchstaxSuggestion) => void,
    onMouseClick: () => void
  ): React.ReactElement {
    return (
    <div className="searchstax-search-input-wrapper">
        <input
          type="text"
          id="searchstax-search-input"
          className="searchstax-search-input"
          placeholder="SEARCH FOR..."
          aria-label="search"
        />
        <div
          className={`searchstax-autosuggest-container ${
            suggestions.length === 0 ? "hidden" : ""
          }`}
          onMouseLeave={onMouseLeave}
        >
          {suggestions.map(function (suggestion) {
            return (
              <div
                className="searchstax-autosuggest-item"
                key={suggestion.term}
              >
                <div
                  className="searchstax-autosuggest-item-term-container"
                  dangerouslySetInnerHTML={{ __html: suggestion.term }}
                  onMouseOver={() => {
                    onMouseOver(suggestion);
                  }}
                  onClick={() => {
                    onMouseClick();
                  }}
                ></div>
              </div>
            );
          })}
        </div>

        <button
          className="searchstax-spinner-icon"
          id="searchstax-search-input-action-button"
          aria-label="search"
        ></button>
      </div>
    );
  }


<SearchstaxInputWidget
            afterAutosuggest={afterAutosuggest}
            beforeAutosuggest={beforeAutosuggest}
            inputTemplate={InputTemplate}
          ></SearchstaxInputWidget>

Result Widget

example of results widget initialization with minimum options

<SearchstaxResultWidget
                afterLinkClick={afterLinkClick}
                renderMethod={'pagination'}
              ></SearchstaxResultWidget>

example of results widget initialization with minimum options for infinite scroll behavior

<SearchstaxResultWidget
                afterLinkClick={afterLinkClick}
                renderMethod={'infiniteScroll'}
              ></SearchstaxResultWidget>

example of result widget initialization with template override

function noResultTemplate(searchTerm: string, metaData: ISearchstaxSearchMetadata | null, executeSearch: (term:string) => void): React.ReactElement {
    return (
      <div>
        <div className="searchstax-no-results">
          {" "}
          Showing <strong>no results</strong> for <strong>"{searchTerm}"</strong>
          <br />
          {metaData?.spellingSuggestion && (
            <span>
              &nbsp;Did you mean{" "}
              <a href="#" className="searchstax-suggestion-term" onClick={(e) => {
                          e.preventDefault();
                          executeSearch(metaData?.spellingSuggestion);
                        }}>
                {metaData?.spellingSuggestion}
              </a>
              ?
            </span>
          )}
        </div>
        <ul>
          <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>
          <li>Lost? Click on the ‘X” in the Search Box to reset your search.</li>
        </ul>
      </div>
    );
  }

  function resultsTemplate(
    searchResults: ISearchstaxParsedResult[],
    resultClicked: (results: ISearchstaxParsedResult, event: any) => ISearchstaxParsedResult[]
  ): React.ReactElement {
    return (
     <>
        {searchResults && searchResults.length && (
          <div className="searchstax-search-results">
            {searchResults !== null &&
              searchResults.map(function (searchResult) {
                return (
                  <a href={searchResult.url ?? ''} key={searchResult.uniqueId} onClick={event => {
                    resultClicked(searchResult, event);
                  }} tabIndex={0} data-searchstax-unique-result-id={ searchResult.uniqueId} className="searchstax-result-item-link searchstax-result-item-link-wrapping">
                  <div
                    className={`searchstax-search-result ${
                      searchResult.thumbnail ? "has-thumbnail" : ""
                    }`}
                    key={searchResult.uniqueId}
                  >
                    {searchResult.promoted && (
                      <div className="searchstax-search-result-promoted"></div>
                    )}


                    {searchResult.ribbon && (
                      <div className="searchstax-search-result-ribbon">
                        {searchResult.ribbon}
                      </div>
                    )}

                    {searchResult.thumbnail && (
                      <img
                        src={searchResult.thumbnail}
                        className="searchstax-thumbnail"
                      />
                    )}

                    <div className="searchstax-search-result-title-container">
                      <span className="searchstax-search-result-title">
                        {searchResult.title}
                      </span>
                    </div>

                    {searchResult.paths && (
                      <p className="searchstax-search-result-common">
                        {searchResult.paths}
                      </p>
                    )}

                    {searchResult.description && (
                      <p className="searchstax-search-result-description searchstax-search-result-common">
                        {searchResult.description}
                      </p>
                    )}

                    {searchResult.unmappedFields.map(function (
                      unmappedField: any
                    ) {
                      return (
                        <div key={unmappedField.key}>
                          {unmappedField.isImage &&
                            typeof unmappedField.value === "string" && (
                              <div className="searchstax-search-result-image-container">
                                <img
                                  src={unmappedField.value}
                                  className="searchstax-result-image"
                                />
                              </div>
                            )}

                          {!unmappedField.isImage && (
                            <div>
                              <p className="searchstax-search-result-common">
                                {unmappedField.value}
                              </p>
                            </div>
                          )}
                        </div>
                      );
                    })}
                  </div>
                  </a>
                );
              })}
          </div>
        )}
      </>
    );
  }


<SearchstaxResultWidget
                afterLinkClick={afterLinkClick}
                noResultTemplate={noResultTemplate}
                resultsTemplate={resultsTemplate}
              ></SearchstaxResultWidget>

Pagination Widget

example of pagination widget initialization with minimum options

<SearchstaxPaginationWidget></SearchstaxPaginationWidget>

example of pagination widget initialization with various options

function paginationTemplate(
    paginationData: IPaginationData | null,
    nextPage: (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => void,
    previousPage: (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => void
  ) {
    return (
     <>
        {paginationData && paginationData?.totalResults !== 0 && (
          <div className="searchstax-pagination-container">
            <div className="searchstax-pagination-content">
              <a
                className="searchstax-pagination-previous"
                style={
                  paginationData?.isFirstPage ? { pointerEvents: "none" } : {}
                }
                onClick={(e) => {
                  previousPage(e);
                }}
                tabIndex={0}
                id="searchstax-pagination-previous"
              >
                {" "}
                &lt; Previous{" "}
              </a>
              <div className="searchstax-pagination-details">
                {" "}
                {paginationData?.startResultIndex} -{" "}
                {paginationData?.endResultIndex} of{" "}
                {paginationData?.totalResults}
              </div>
              <a
                className="searchstax-pagination-next"
                style={
                  paginationData?.isLastPage ? { pointerEvents: "none" } : {}
                }
                onClick={(e) => {
                  nextPage(e);
                }}
                tabIndex={0}
                id="searchstax-pagination-next"
              >
                Next &gt;
              </a>
            </div>
          </div>
        )}
      </>
    );
  }

  <SearchstaxPaginationWidget paginationTemplate={paginationTemplate}></SearchstaxPaginationWidget>

example of pagination widget for infinite scroll

    function infiniteScrollTemplate(
    paginationData: IPaginationData | null,
    nextPage: (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => void
  ) {
    return (
      <>
        {paginationData &&
          paginationData.isInfiniteScroll &&
          paginationData?.totalResults !== 0 &&
          !paginationData?.isLastPage && (
            <div className="searchstax-pagination-container">
              <a
                className="searchstax-pagination-load-more"
                onClick={(e) => {
                  nextPage(e);
                }}
                onKeyDown={(e) => {
                  if(e.key === 'Enter' || e.key === ' ') {
                    nextPage(e as any);
                  }
                }}
              >
                Show More &gt;
              </a>
            </div>
          )}
      </>
    );
  }

  <SearchstaxPaginationWidget infiniteScrollTemplate={infiniteScrollTemplate}></SearchstaxPaginationWidget>

Facets Widget

example of facets widget initialization with minimum options

<SearchstaxFacetsWidget
                facetingType="and"
                itemsPerPageDesktop={2}
                itemsPerPageMobile={3}
                specificFacets={undefined}
              ></SearchstaxFacetsWidget>

example of facets widget initialization with template overrides

function facetsTemplateDesktop(
    facetsTemplateDataDesktop: IFacetsTemplateData | null,
    facetContainers: {
      [key: string]: React.LegacyRef<HTMLDivElement> | undefined;
    },
    isNotDeactivated: (name: string) => boolean,
    toggleFacetGroup: (name: string) => void,
    selectFacet: (
      index: string,
      event: React.MouseEvent<HTMLDivElement, MouseEvent>,
      data: IFacetValueData,
      isInput: boolean
    ) => void,
    isChecked: (facetValue: IFacetValueData) => boolean | undefined,
    showMoreLessDesktop: (
      e: React.MouseEvent<HTMLDivElement, MouseEvent>,
      data: IFacetData
    ) => void
  ) {
    return (
      <div className="searchstax-facets-container-desktop">
        {facetsTemplateDataDesktop?.facets.map((facet) => {
          return (
            <div
              className={`searchstax-facet-container ${
                isNotDeactivated(facet.name) ? "active" : ""
              }`}
              key={facet.name + "desktop"}
            >
              <div>
                <div
                  className="searchstax-facet-title-container"
                  onClick={() => {
                    toggleFacetGroup(facet.name);
                  }}
                >
                  <div className="searchstax-facet-title"> {facet.label}</div>
                  <div className="searchstax-facet-title-arrow active"></div>
                </div>
                <div className="searchstax-facet-values-container">
                  {facet.values.map(
                    //@ts-ignore
                    (facetValue: IFacetValueData, key) => {
                      facetContainers[key + facet.name] = createRef();
                      return (
                        <div
                          key={facetValue.value + facetValue.parentName}
                          className={`searchstax-facet-value-container ${
                            facetValue.disabled
                              ? "searchstax-facet-value-disabled"
                              : ""
                          }`}
                          ref={facetContainers[key + facet.name]}
                        >
                          <div className={
                                    "searchstax-facet-input" +
                                    " desktop-" +
                                    key +
                                    facet.name
                                  }>
                            <input
                              type="checkbox"
                              className="searchstax-facet-input-checkbox"
                              checked={isChecked(facetValue)}
                              readOnly={true}
                              aria-label={facetValue.value + ' ' + facetValue.count}
                              disabled={facetValue.disabled}
                              onClick={(e) => {
                                selectFacet(
                                  key + facet.name,
                                  e,
                                  facetValue,
                                  true
                                );
                              }}
                            />
                          </div>
                          <div
                            className="searchstax-facet-value-label"
                            onClick={(e) => {
                              selectFacet(
                                key + facet.name,
                                e,
                                facetValue,
                                false
                              );
                            }}
                          >
                            {facetValue.value}
                          </div>
                          <div
                            className="searchstax-facet-value-count"
                            onClick={(e) => {
                              selectFacet(
                                key + facet.name,
                                e,
                                facetValue,
                                false
                              );
                            }}
                          >
                            ({facetValue.count})
                          </div>
                        </div>
                      );
                    }
                  )}

                  {facet.hasMoreFacets && (
                    <div className="searchstax-facet-show-more-container">
                      <div
                        className="searchstax-facet-show-more-container"
                        onClick={(e) => {
                          showMoreLessDesktop(e, facet);
                        }}
                      >
                        {facet.showingAllFacets && (
                          <div className="searchstax-facet-show-less-button searchstax-facet-show-button">
                            less
                          </div>
                        )}
                        {!facet.showingAllFacets && (
                          <div className="searchstax-facet-show-more-button  searchstax-facet-show-button">
                            more
                          </div>
                        )}
                      </div>
                    </div>
                  )}
                </div>
              </div>
            </div>
          );
        })}
      </div>
    );
  }

  function facetsTemplateMobile(
    facetsTemplateDataMobile: IFacetsTemplateData | null,
    selectedFacetsCheckboxes: IFacetValue[],
    facetContainers: {
      [key: string]: React.LegacyRef<HTMLDivElement> | undefined;
    },
    isNotDeactivated: (name: string) => boolean,
    toggleFacetGroup: (name: string) => void,
    selectFacet: (
      index: string,
      event: React.MouseEvent<HTMLDivElement, MouseEvent>,
      data: IFacetValueData,
      isInput: boolean
    ) => void,
    isChecked: (facetValue: IFacetValueData) => boolean | undefined,
    unselectFacet: (facet: IFacetValue) => void,
    showMoreLessMobile: (
      e: React.MouseEvent<HTMLDivElement, MouseEvent>,
      data: IFacetData
    ) => void,
    openOverlay: () => void,
    closeOverlay: () => void,
    unselectAll: () => void
  ) {
    return facetsTemplateDataMobile ? (
      <div className="searchstax-facets-container-mobile">
        <div className="searchstax-facets-pills-container">
          <div
            className="searchstax-facets-pill searchstax-facets-pill-filter-by"
            onClick={() => {
              openOverlay();
            }}
          >
            <div className="searchstax-facets-pill-label">Filter By</div>
          </div>
          <div className="searchstax-facets-pills-selected">
            {selectedFacetsCheckboxes.map((facet) => {
              return (
                <div
                  className="searchstax-facets-pill searchstax-facets-pill-facets"
                  key={facet.value}
                  onClick={() => {
                    unselectFacet(facet);
                  }}
                >
                  <div className="searchstax-facets-pill-label">
                    {facet.value} ({facet.count})
                  </div>
                  <div className="searchstax-facets-pill-icon-close"></div>
                </div>
              );
            })}
            {selectedFacetsCheckboxes.length !== 0 && (
              <div
                className="searchstax-facets-pill searchstax-clear-filters searchstax-facets-pill-clear-all"
                onClick={() => {
                  unselectAll();
                }}
              >
                <div className="searchstax-facets-pill-label">
                  Clear Filters
                </div>
              </div>
            )}
          </div>
          <div
            className={`searchstax-facets-mobile-overlay ${
              //@ts-ignore
              facetsTemplateDataMobile.overlayOpened ? "searchstax-show" : ""
            }`}
          >
            <div className="searchstax-facets-mobile-overlay-header">
              <div className="searchstax-facets-mobile-overlay-header-title">
                Filter By
              </div>
              <div
                className="searchstax-search-close"
                onClick={() => {
                  closeOverlay();
                }}
              ></div>
            </div>
            <div className="searchstax-facets-container-mobile">
              {facetsTemplateDataMobile?.facets.map((facet) => {
                return (
                  <div
                    key={facet.name + "mobile"}
                    className={`searchstax-facet-container ${
                      isNotDeactivated(facet.name) ? `active` : ``
                    }`}
                  >
                    <div>
                      <div
                        className="searchstax-facet-title-container"
                        onClick={() => {
                          toggleFacetGroup(facet.name);
                        }}
                      >
                        <div className="searchstax-facet-title">
                          {" "}
                          {facet.label}{" "}
                        </div>
                        <div className="searchstax-facet-title-arrow active"></div>
                      </div>
                      <div className="searchstax-facet-values-container">
                        {facet.values.map(
                          //@ts-ignore
                          (facetValue: IFacetValueData, key) => {
                            facetContainers[key + facet.name] = createRef();

                            return (
                              <div
                                key={facetValue.value + facetValue.parentName}
                                className={`searchstax-facet-value-container ${
                                  facetValue.disabled
                                    ? `searchstax-facet-value-disabled`
                                    : ``
                                }`}
                                ref={facetContainers[key + facet.name]}
                              >
                                <div className={
                                    "searchstax-facet-input" +
                                    " mobile-" +
                                    key +
                                    facet.name
                                  }>
                                  <input
                                    type="checkbox"
                                    className="searchstax-facet-input-checkbox"
                                    checked={isChecked(facetValue)}
                                    readOnly={true}
                                    aria-label={facetValue.value + ' ' + facetValue.count}
                                    disabled={facetValue.disabled}
                                    onClick={(e) => {
                                      selectFacet(
                                        key + facet.name,
                                        e,
                                        facetValue,
                                        true
                                      );
                                    }}
                                  />
                                </div>
                                <div
                                  className="searchstax-facet-value-label"
                                  onClick={(e) => {
                                    selectFacet(
                                      key + facet.name,
                                      e,
                                      facetValue,
                                      false
                                    );
                                  }}
                                >
                                  {facetValue.value}
                                </div>
                                <div
                                  className="searchstax-facet-value-count"
                                  onClick={(e) => {
                                    selectFacet(
                                      key + facet.name,
                                      e,
                                      facetValue,
                                      false
                                    );
                                  }}
                                >
                                  ({facetValue.count})
                                </div>
                              </div>
                            );
                          }
                        )}

                        <div
                          className="searchstax-facet-show-more-container"
                          v-if="facet.hasMoreFacets"
                        >
                          <div
                            className="searchstax-facet-show-more-container"
                            onClick={(e) => {
                              showMoreLessMobile(e, facet);
                            }}
                          >
                            {facet.showingAllFacets && (
                              <div className="searchstax-facet-show-less-button searchstax-facet-show-button">
                                less
                              </div>
                            )}
                            {!facet.showingAllFacets && (
                              <div className="searchstax-facet-show-more-button  searchstax-facet-show-button">
                                more
                              </div>
                            )}
                          </div>
                        </div>
                      </div>
                    </div>
                  </div>
                );
              })}
            </div>
            <button
              className="searchstax-facets-mobile-overlay-done"
              onClick={() => {
                closeOverlay();
              }}
            >
              Done
            </button>
          </div>
        </div>
      </div>
    ) : (<></>);
  }
<SearchstaxFacetsWidget
                facetingType="and"
                itemsPerPageDesktop={2}
                itemsPerPageMobile={3}
                specificFacets={undefined}
                facetsTemplateDesktop={facetsTemplateDesktop}
                facetsTemplateMobile={facetsTemplateMobile}
              ></SearchstaxFacetsWidget>

SearchFeedback Widget

example of search feedback widget initialization with minimum options

<SearchstaxOverviewWidget></SearchstaxOverviewWidget>

example of search feedback widget initialization with template overrides

function searchOverviewTemplate(
    searchFeedbackData: null | ISearchstaxSearchFeedbackData,
    onOriginalQueryClick: (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => void
  ) {
    return (
      <>
        {searchFeedbackData && searchFeedbackData?.searchExecuted && searchFeedbackData?.totalResults > 0 && (
          <>
            Showing{" "}
            <b>
              {searchFeedbackData.startResultIndex} - {searchFeedbackData.endResultIndex}
            </b>{" "}
            of
            <b> {searchFeedbackData.totalResults}</b> results
            {searchFeedbackData.searchTerm && (
              <span>
                &nbsp;for "<b>{searchFeedbackData.searchTerm}</b>"{" "}
              </span>
            )}
            <div className="searchstax-feedback-container-suggested">
              {searchFeedbackData.autoCorrectedQuery && (
                <div>
                  {" "}
                  Search instead for{" "}
                  <a
                    href="#"
                    onClick={(e) => {
                      onOriginalQueryClick(e);
                    }}
                    className="searchstax-feedback-original-query"
                  >
                    {searchFeedbackData.originalQuery}
                  </a>
                </div>
              )}
            </div>
          </>
        )}
      </>
    );
  }
  <SearchstaxOverviewWidget searchOverviewTemplate={searchOverviewTemplate}></SearchstaxOverviewWidget>

RelatedSearches widget

example of search feedback widget initialization with minimum options

<SearchstaxRelatedSearchesWidget
                relatedSearchesURL={config.relatedSearchesURL}
                relatedSearchesAPIKey={config.relatedSearchesAPIKey}
              ></SearchstaxRelatedSearchesWidget>

example of search feedback widget initialization with template overrides

  function searchRelatedSearchesTemplate(
    relatedData: null | ISearchstaxRelatedSearchesData,
    executeSearch: (result: ISearchstaxRelatedSearchResult) => void
  ) {
    return (
      <>
        {relatedData && relatedData?.searchExecuted && relatedData?.hasRelatedSearches && (
          <div className="searchstax-related-searches-container" id="searchstax-related-searches-container">
            {" "}
            Related searches: <span id="searchstax-related-searches"></span>
            {relatedData.relatedSearches && (
              <span className="searchstax-related-search">
                {relatedData.relatedSearches.map((related) => (
                  <span
                    key={related.related_search}
                    onClick={() => {
                      executeSearch(related);
                    }}
                    className="searchstax-related-search searchstax-related-search-item"
                  >
                    {" "}
                    {related.related_search}
                    {!related.last && <span>,</span>}
                  </span>
                ))}
              </span>
            )}
          </div>
        )}
      </>
    );
  }
  <SearchstaxRelatedSearchesWidget
                relatedSearchesURL={config.relatedSearchesURL}
                relatedSearchesAPIKey={config.relatedSearchesAPIKey}
                searchRelatedSearchesTemplate={searchRelatedSearchesTemplate}
              ></SearchstaxRelatedSearchesWidget>

ExternalPromotions widget

example of search feedback widget initialization with minimum options

<SearchstaxExternalPromotionsWidget></SearchstaxExternalPromotionsWidget>

example of search feedback widget initialization with template overrides

  function searchExternalPromotionsTemplate(
    externalPromotionsData: null | ISearchstaxExternalPromotionsData,
    trackClick: (externalPromotion: IExternalPromotion, event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => void
  ) {
    return (
      <>
        {externalPromotionsData &&
          externalPromotionsData?.searchExecuted &&
          externalPromotionsData?.hasExternalPromotions &&
          externalPromotionsData.externalPromotions.map((externalPromotion) => (
            <div className="searchstax-external-promotion searchstax-search-result" key={externalPromotion.id}>
              <div className="icon-elevated"></div>
              {externalPromotion.url && (
                <a
                  href={externalPromotion.url}
                  onClick={(event) => {
                    trackClick(externalPromotion, event);
                  }}
                  className="searchstax-result-item-link"
                ></a>
              )}
              <div className="searchstax-search-result-title-container">
                <span className="searchstax-search-result-title">{externalPromotion.name}</span>
              </div>
              {externalPromotion.description && (
                <p className="searchstax-search-result-description searchstax-search-result-common">
                  {" "}
                  {externalPromotion.description}{" "}
                </p>
              )}
              {externalPromotion.url && (
                <p className="searchstax-search-result-description searchstax-search-result-common">
                  {" "}
                  {externalPromotion.url}{" "}
                </p>
              )}
            </div>
          ))}
      </>
    );
  }
<SearchstaxExternalPromotionsWidget
                searchExternalPromotionsTemplate={searchExternalPromotionsTemplate}
              ></SearchstaxExternalPromotionsWidget>

Sorting Widget

example of sorting widget initialization with minimum options

<SearchstaxSortingWidget></SearchstaxSortingWidget>

example of sorting widget initialization with template override

 function searchSortingTemplate(
    sortingData: null | ISearchstaxSearchSortingData,
    orderChange: (value: string) => void,
    selectedSorting: string
  ) {
    return (
      <>
        {sortingData && sortingData?.searchExecuted && sortingData?.hasResultsOrExternalPromotions && (
          <div className="searchstax-sorting-container">
            <label className="searchstax-sorting-label" htmlFor="searchstax-search-order-select">
              Sort By
            </label>
            <select
              id="searchstax-search-order-select"
              className="searchstax-search-order-select"
              value={selectedSorting}
              onChange={(e) => {
                orderChange(e.target.value);
              }}
            >
              {sortingData.sortOptions.map(function (sortOption) {
                  return (
                    <option key={sortOption.key} value={sortOption.key}>
                      {sortOption.value}
                    </option>
                  );
                })}
            </select>
          </div>
        )}
      </>
    );
  }
  <SearchstaxSortingWidget searchSortingTemplate={searchSortingTemplate}></SearchstaxSortingWidget>

Template overrides

Templates use vue slots.

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
1.0.8

12 months ago

1.0.7

12 months ago

1.0.6

1 year ago

1.0.5

1 year ago

1.0.22

8 months ago

1.0.27

8 months ago

1.0.14

9 months ago

1.0.13

9 months ago

1.0.12

11 months ago

1.0.4

1 year ago

1.0.2

1 year ago

1.0.1

1 year ago

1.0.3

1 year ago

0.3.18

1 year ago

0.3.17

1 year ago

0.3.16

1 year ago

0.3.15

1 year ago

0.2.18

2 years ago

0.2.17

2 years ago

0.3.5

2 years ago

0.3.1

2 years ago

0.3.3

2 years ago

0.2.15

2 years ago

0.2.14

2 years ago

0.2.13

2 years ago

0.2.12

2 years ago

0.2.11

2 years ago

0.2.10

2 years ago

0.2.7

2 years ago

0.2.6

2 years ago

0.2.9

2 years ago

0.2.8

2 years ago

0.2.5

2 years ago

0.2.3

2 years ago

0.2.4

2 years ago

0.1.0

2 years ago

0.2.1

2 years ago

0.2.0

2 years ago

0.2.2

2 years ago

0.0.11

3 years ago

0.0.10

3 years ago

0.0.9

3 years ago

0.0.7

3 years ago

0.0.6

3 years ago

0.0.5

3 years ago

0.0.3

3 years ago

0.0.2

3 years ago

0.0.1

3 years ago