@searchstax-inc/searchstudio-ux-angular
v4.1.72
Published
Library to build searchstudio search page
Keywords
Readme
searchstudio-ux-angular
Library to build searchstudio search page
changelog
changelog is documented at CHANGELOG.md
Installation
npm install following package
npm install --save @searchstax-inc/searchstudio-ux-angular
Usage
After importing SearchstaxWrapper component needs to wrap all other components.
Make sure your module imports SearchstudioUxAngularModule:
imports: [ SearchstudioUxAngularModule ],
Initialization
Initialization object needs to be of type: ISearchstaxConfig
Initialization example
Add following code to your ts file and fill config
config = {
searchURL: '',
suggesterURL: '',
trackApiKey: '',
searchAuth: '',
authType: 'basic',
relatedSearchesAPIKey: '',
relatedSearchesURL: '',
analyticsBaseUrl: 'https://analytics-us-east.searchstax.com'
}
beforeSearch(props: ISearchObject) {
// gets searchProps, if passed along further search will execute, if null then event gets canceled
// props can be modified and passed along
const propsCopy = { ...props }
// propsCopy.term = propsCopy.term;
return propsCopy
}
afterSearch(results: ISearchstaxParsedResult[]) {
const copy = [...results]
// copy.splice(0, 1);
return copy
}
afterAutosuggest(result: ISearchstaxSuggestResponse) {
const copy = { ...result }
return copy
}
beforeAutosuggest(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
}
afterLinkClick(results: ISearchstaxParsedResult): ISearchstaxParsedResult {
// gets result that was clicked, if passed along further functions will execute, if null then event gets canceled
const copy = { ...results }
return copy
}
initialized(searchstax: Searchstax) {
console.log('searchstax', searchstax);
}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>
<app-searchstax-wrapper
[searchURL]="config.searchURL"
[suggesterURL]="config.suggesterURL"
[trackApiKey]="config.trackApiKey"
[searchAuth]="config.searchAuth"
[beforeSearch]="beforeSearch"
[afterSearch]="afterSearch"
(initialized)="initialized($event)"
>
<div class="searchstax-page-layout-container">
<app-searchstax-input
[afterAutosuggest]="afterAutosuggest"
[beforeAutosuggest]="beforeAutosuggest"
[suggestAfterMinChars]="3"
></app-searchstax-input>
<div class="search-details-container">
<app-searchstax-search-feedback></app-searchstax-search-feedback>
<app-searchstax-search-sorting></app-searchstax-search-sorting>
</div>
<div class="searchstax-page-layout-facet-result-container">
<div class="searchstax-page-layout-facet-container">
<app-searchstax-search-facets
[facetingType]="'showUnavailable'"
[itemsPerPageDesktop]="2"
[itemsPerPageMobile]="99"
></app-searchstax-search-facets>
</div>
<div class="searchstax-page-layout-result-container">
<app-searchstax-external-promotions></app-searchstax-external-promotions>
<app-searchstax-results
[afterLinkClick]="afterLinkClick"
></app-searchstax-results>
<app-searchstax-related-searches
[relatedSearchesURL]="config.relatedSearchesURL"
[relatedSearchesAPIKey]="config.relatedSearchesAPIKey"
></app-searchstax-related-searches>
<app-searchstax-search-pagination></app-searchstax-search-pagination>
</div>
</div>
</div>
</app-searchstax-wrapper>
</div>
widgets
Following widgets are available:
Answer Widget
SearchStax Site Search solution offers Angular widgets to assist in building your custom search page.
The AppSearchstaxAnswer component for Angular provides an AI answer widget for your searches.
Usage
<app-searchstax-answer [showMoreAfterWordCount]="100"></app-searchstax-answer>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.
Example of feedbackWidget config:
const feedbackConfig = {
renderFeedbackWidget: true,
emailOverride: searchstaxEmailOverride,
thumbsUpValue: 10,
thumbsDownValue: 0
}searchAnswerTemplate
The templates prop allows customizing the answer UI.
It receives the following props:
- answerData – Data object of type: ISearchstaxAnswerData
- showMore - Handler for exiting the show more view
Example
<app-searchstax-answer [showMoreAfterWordCount]="100" [templateOverride]="answerTemplate" [feedbackwidget]="feedbackConfig">
<ng-template
#answerTemplate
let-answerData="answerData"
let-showMore="showMore"
let-context="context"
>
<div class="searchstax-answer-wrap">
<div class="searchstax-answer-icon"></div>
<div>
<div
[class]="{
'searchstax-answer-container': true,
'searchstax-answer-show-more': answerData.showMoreButtonVisible
}"
>
<div class="searchstax-answer-title">Smart Answers</div>
<div
class="searchstax-answer-error"
*ngIf="answerData.shouldShowAnswerError"
[innerHTML]="answerData.answerErrorMessage"
></div>
<div
class="searchstax-answer-description"
[innerHTML]="answerData.fullAnswerFormatted"
></div>
<div
*ngIf="answerData.answerLoading"
class="searchstax-answer-loading"
></div>
</div>
<div
*ngIf="answerData.showMoreButtonVisible"
class="searchstax-answer-load-more-button-container"
>
<button
class="searchstax-answer-load-more-button"
(click)="showMore($event, context)"
>
Show More
</button>
</div>
</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>
</ng-template>
</app-searchstax-answer>Input Widget
The SearchStax Site Search solution offers an Angular input widget to assist with your custom search page.
The SearchstaxInputWidget component for Angular provides a search input with autosuggest/autocomplete functionality. Usage
<app-searchstax-input
[afterAutosuggest]="afterAutosuggest"
[beforeAutosuggest]="beforeAutosuggest"
[suggestAfterMinChars]="3"
></app-searchstax-input>Props
- suggestAfterMinChars - default 3. Number of characters needed for autosuggest to start triggering
- 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.
- 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.
- templateOverride - template override. look at examples below
inputWidgetTemplate
The templateOverride prop allows customizing the input UI.
It receives the following props:
- suggestions – Array of autosuggestion results
- onMouseLeave - Handler for mouse leave event
- onMouseOver - Handler for mouse over event
- onMouseClick - Handler for mouse click event
example
<app-searchstax-input
[afterAutosuggest]="afterAutosuggest"
[beforeAutosuggest]="beforeAutosuggest"
[suggestAfterMinChars]="3"
[templateOverride]="inputTemplate"
>
<ng-template
#inputTemplate
let-suggestions="suggestions"
let-onMouseLeave="onMouseLeave"
let-onMouseOver="onMouseOver"
let-onMouseClick="onMouseClick"
let-context="context"
let-locationWidgetConfig="locationWidgetConfig"
>
<div class="searchstax-search-input-wrapper">
<input
type="text"
id="searchstax-search-input"
class="searchstax-search-input"
placeholder="SEARCH FOR..."
aria-label="search"
/>
<div
class="searchstax-autosuggest-container"
[ngClass]="{ hidden: suggestions.length === 0 }"
(mouseleave)="onMouseLeave(context)"
>
<div
class="searchstax-autosuggest-item"
*ngFor="let suggestion of suggestions"
>
<div
class="searchstax-autosuggest-item-term-container"
[innerHtml]="suggestion.term"
(mouseover)="onMouseOver(suggestion, context)"
tabindex="0"
(click)="onMouseClick($event, context)"
></div>
</div>
</div>
</div>
<app-searchstax-search-location
[locationDecode]="locationWidgetConfig.locationDecode"
[locationDecodeCoordinatesToAddress]="
locationWidgetConfig.locationDecodeCoordinatesToAddress
"
[locationSearchEnabled]="locationWidgetConfig.locationSearchEnabled"
[locationValuesOverride]="locationWidgetConfig.locationValuesOverride"
>
</app-searchstax-search-location>
<button
class="searchstax-spinner-icon"
id="searchstax-search-input-action-button"
role="button"
aria-label="search"
></button>
</ng-template>
</app-searchstax-input>Location Widget
SearchStax Site Search solution offers a Angular search-location widget to assist with your custom search page.
The SearchstaxLocationWidget provides a location search input with location-based search functionality.
Usage
const config = {
appId: "APP_ID",
relatedSearchesAPIKey: "KEY"
}
const locationWidgetConfig = {
locationDecode: (term: string): Promise<ISearchstaxLocation> => {
return new Promise((resolve) => {
// make a request to google geocoding API to retrieve lat, lon and address
const geocodingAPIKey = "GEOCODING_API_KEY";
const geocodingURL = `https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(
term
)}&key=${geocodingAPIKey}`;
fetch(geocodingURL)
.then((response) => response.json())
.then((data) => {
if (data.status === "OK" && data.results.length > 0) {
const result = data.results[0];
const location = {
lat: result.geometry.location.lat,
lon: result.geometry.location.lng,
address: result.formatted_address,
};
resolve(location);
} else {
resolve({
address: undefined,
lat: undefined,
lon: undefined,
error: true,
});
}
})
.catch(() => {
resolve({
address: undefined,
lat: undefined,
lon: undefined,
error: true,
});
});
});
},
locationDecodeCoordinatesToAddress: (lat: string, lon: string): Promise<ISearchstaxLocation> => {
return new Promise((resolve) => {
fetch(
`https://geocoding-staging.searchstax.co/reverse?location=${lat},${lon}&components=country:US&app_id=${config.appId}`,
{
method: "GET",
headers: {
Authorization: `Token ${config.relatedSearchesAPIKey}`,
},
}
)
.then((response) => response.json())
.then((data) => {
if (data.status === "OK" && data.results.length > 0) {
const result = data.results[0];
resolve({
address: result.formatted_address,
lat: lat,
lon: lon,
error: false,
});
} else {
resolve({
address: undefined,
lat: lat,
lon: lon,
error: true,
});
}
})
.catch(() => {
resolve({
address: undefined,
lat: lat,
lon: lon,
error: true,
});
});
});
},
locationSearchEnabled: false,
locationValuesOverride: {
locationDistanceEnabled: true,
filterValues: ["any", "1000"],
filterUnit: "miles",
locationFilterDefaultValue: "any"
},
}
<app-searchstax-search-location
[locationDecode]="locationWidgetConfig.locationDecodeFunction"
[locationDecodeCoordinatesToAddress]="locationWidgetConfig.locationDecodeCoordinatesToAddress"
[locationValuesOverride]="locationWidgetConfig.locationValuesOverride"
[locationSearchEnabled]="locationWidgetConfig.locationSearchEnabled"
>
</app-searchstax-search-location>Props
- locationDecode - callback function to override location decoding
- locationDecodeCoordinatesToAddress - callback function to override location decoding
- locationValuesOverride - location widget values
- locationSearchEnabled - boolean stating if locationSearch is locationSearchEnabled
- templateOverride - template override. look at examples below
Template Override
The searchLocationTemplate prop allows customizing the location input UI.
It receives the following props:
- locationData – data containing info on when to show certain elements
- inputValue – value of location input
- locationBlur - Handler for location input blur
- radiusChange - Handler for location radius change
- selectValue - Value of radius select
- inputChange - location input change handler
- locationError - boolean stating if location has error
- getCurrentLocation - Handler for getting current location from browser
Example
const config = {
appId: "APP_ID",
relatedSearchesAPIKey: "KEY"
}
const locationWidgetConfig = {
locationDecode: (term: string): Promise<ISearchstaxLocation> => {
return new Promise((resolve) => {
// make a request to google geocoding API to retrieve lat, lon and address
const geocodingAPIKey = "GEOCODING_API_KEY";
const geocodingURL = `https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(
term
)}&key=${geocodingAPIKey}`;
fetch(geocodingURL)
.then((response) => response.json())
.then((data) => {
if (data.status === "OK" && data.results.length > 0) {
const result = data.results[0];
const location = {
lat: result.geometry.location.lat,
lon: result.geometry.location.lng,
address: result.formatted_address,
};
resolve(location);
} else {
resolve({
address: undefined,
lat: undefined,
lon: undefined,
error: true,
});
}
})
.catch(() => {
resolve({
address: undefined,
lat: undefined,
lon: undefined,
error: true,
});
});
});
},
locationDecodeCoordinatesToAddress: (lat: string, lon: string): Promise<ISearchstaxLocation> => {
return new Promise((resolve) => {
fetch(
`https://geocoding-staging.searchstax.co/reverse?location=${lat},${lon}&components=country:US&app_id=${config.appId}`,
{
method: "GET",
headers: {
Authorization: `Token ${config.relatedSearchesAPIKey}`,
},
}
)
.then((response) => response.json())
.then((data) => {
if (data.status === "OK" && data.results.length > 0) {
const result = data.results[0];
resolve({
address: result.formatted_address,
lat: lat,
lon: lon,
error: false,
});
} else {
resolve({
address: undefined,
lat: lat,
lon: lon,
error: true,
});
}
})
.catch(() => {
resolve({
address: undefined,
lat: lat,
lon: lon,
error: true,
});
});
});
},
locationSearchEnabled: false,
locationValuesOverride: {
locationDistanceEnabled: true,
filterValues: ["any", "1000"],
filterUnit: "miles",
locationFilterDefaultValue: "any"
},
}
<app-searchstax-search-location
[locationDecode]="locationWidgetConfig.locationDecode"
[templateOverride]="locationTemplate"
[locationValuesOverride]="locationWidgetConfig.locationValuesOverride"
[locationSearchEnabled]="locationWidgetConfig.locationSearchEnabled"
[locationDecodeCoordinatesToAddress]="locationWidgetConfig.locationDecodeCoordinatesToAddress"
[templateOverride]="locationTemplate"
>
<ng-template
#locationTemplate
let-context="context"
let-locationData="locationData"
let-inputValue="inputValue"
let-selectValue="selectValue"
let-radiusChange="radiusChange"
let-inputChange="inputChange"
let-locationBlur="locationBlur"
let-locationError="locationError"
let-getCurrentLocation="getCurrentLocation"
>
<div
class="searchstax-location-input-container"
data-test-id="searchstax-location-input-container"
*ngIf="locationData"
>
<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"
[ngClass]="{
'searchstax-input-location-error': locationError
}"
placeholder="Zip, Postal Code or City..."
aria-label="Search Location Input"
data-test-id="searchstax-location-input"
[value]="inputValue"
(change)="inputChange($event, context)"
(blur)="locationBlur(context)"
/>
<button
(click)="getCurrentLocation(context)"
class="searchstax-get-current-location-button"
>
Use my current location
</button>
</div>
<span
*ngIf="locationData.shouldShowLocationDistanceDropdown"
class="searchstax-location-input-label"
>WITHIN</span
>
<select
*ngIf="locationData.shouldShowLocationDistanceDropdown"
id="searchstax-location-radius-select"
class="searchstax-location-radius-select"
aria-label="Search Location Radius Select"
data-test-id="searchstax-location-radius-select"
(change)="radiusChange($event, context)"
[(ngModel)]="context.selectValue"
>
<option
*ngFor="let value of locationData.locationSearchDistanceValues"
[value]="value.value"
>
{{ value.label }}
</option>
</select>
</div>
</div>
</ng-template>
</app-searchstax-search-location>Result Widget
The SearchStax Site Search solution offers an Angular results widget to assist with your custom search page.
The SearchstaxResultsWidget for Angular component displays the search results.
Usage
<app-searchstax-results
[afterLinkClick]="afterLinkClick"
[renderMethod]="'pagination'"
[resultsPerPage]="10"
></app-searchstax-results>Props
- renderMethod – either “pagination” or “infiniteScroll”.
- resultsPerPage – number of results on a page.
- afterLinkClick – Callback function invoked when a result link is clicked. Allows modifying the result object.
- noResultTemplate - template override for no results view
- resultsTemplate - template override for result item
Result Template Override
The resultsTemplate prop allows customizing the result UI.
It receives following props:
- searchResults - Array of result items
- resultClicked - Handler for result click events
No Results Template Override
The noResultTemplate prop allows customizing the result UI.
It receives following props:
- searchResults - Array of result items
- resultClicked - Handler for result click events
- searchTerm - Search input term
- metadata - Metadata object
- executeSearch - Handler for executing srearch
- store - Main store of app
Example of default render method
<app-searchstax-results
[afterLinkClick]="afterLinkClick"
[resultsTemplate]="resultsTemplate"
[noResultsTemplate]="noResultsTemplate"
>
<ng-template
#resultsTemplate
let-searchResults="searchResults"
let-resultClicked="resultClicked"
let-isImage="isImage"
let-context="context"
>
<div class="searchstax-search-results">
<a
*ngFor="let searchResult of searchResults"
[href]="searchResult.url"
[attr.data-searchstax-unique-result-id]="searchResult.uniqueId"
(click)="resultClicked(searchResult, $event, context)"
(keyup.enter)="resultClicked(searchResult, $event, context)"
(keyup.space)="resultClicked(searchResult, $event, context)"
[attr.aria-labelledby]="'title-' + searchResult.uniqueId"
tabindex="0"
class="searchstax-result-item-link searchstax-result-item-link-wrapping"
>
<div
class="searchstax-search-result"
[ngClass]="{ 'has-thumbnail': searchResult.thumbnail }"
>
<div
*ngIf="searchResult.promoted"
class="searchstax-search-result-promoted"
></div>
<div
*ngIf="searchResult.ribbon"
[innerHTML]="searchResult.ribbon"
class="searchstax-search-result-ribbon"
></div>
<img
alt=""
*ngIf="searchResult.thumbnail"
[src]="searchResult.thumbnail"
class="searchstax-thumbnail"
/>
<div class="searchstax-search-result-title-container">
<span
class="searchstax-search-result-title"
[id]="'title-' + searchResult.uniqueId"
[innerHTML]="searchResult.title"
></span>
</div>
<p
tabindex="0"
*ngIf="searchResult.paths"
class="searchstax-search-result-common"
[innerHTML]="searchResult.paths"
></p>
<p
tabindex="0"
*ngIf="searchResult.description"
[innerHTML]="searchResult.description"
class="searchstax-search-result-description searchstax-search-result-common"
></p>
<div *ngFor="let unmappedField of searchResult.unmappedFields">
<div
*ngIf="isImage(unmappedField)"
class="searchstax-search-result-image-container"
>
<img
alt=""
[src]="unmappedField.value"
class="searchstax-result-image"
/>
</div>
<div *ngIf="!isImage(unmappedField)">
<p
tabindex="0"
class="searchstax-search-result-common"
[innerHTML]="unmappedField.value"
></p>
</div>
</div>
</div>
</a>
</div>
</ng-template>
<ng-template
#noResultsTemplate
let-metadata="metadata"
let-searchTerm="searchTerm"
let-executeSearch="executeSearch"
let-context="context"
>
<div class="searchstax-no-results">
Showing <strong>no results</strong> for
<strong>"{{ searchTerm }}"</strong>
<br />
<span *ngIf="metadata?.spellingSuggestion"
> Did you mean
<a
href="#"
[attr.aria-label]="'did you mean: ' + metadata?.spellingSuggestion"
class="searchstax-suggestion-term"
(click)="
executeSearch(metadata?.spellingSuggestion ?? '', $event, context)
"
>{{ 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>
</ul>
</ng-template>
</app-searchstax-results>Example of infinite scroll and pagination render methods
<app-searchstax-results
[afterLinkClick]="afterLinkClick"
[renderMethod]="'infiniteScroll'"
></app-searchstax-results>
Pagination Widget
The SearchStax Site Search solution offers an Angular pagination widget to assist with you custom search page.
The SearchstaxPaginationWidget for Angular displays pagination controls for search results.
Usage
<app-searchstax-search-pagination></app-searchstax-search-pagination>Props
- infiniteScrollTemplateOverride - template override for infinite scroll view pagination
- templateOverride - template override for standard view pagination
Main Template Override
Main template for the pagination controls.
It receives following props:
- paginationData – Pagination info object
- previousPage – Handler for previous page click
- nextPage - – Handler for next page click
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
<app-searchstax-search-pagination [templateOverride]="paginationTemplate">
<ng-template
#paginationTemplate
let-paginationData="paginationData"
let-previousPage="previousPage"
let-nextPage="nextPage"
let-context="context"
>
<div class="searchstax-pagination-container">
<div class="searchstax-pagination-content">
<a
class="searchstax-pagination-previous"
[class]="paginationData.isFirstPage ? 'disabled' : ''"
(click)="previousPage($event, context)"
(keyup.enter)="previousPage($event, context)"
(keyup.space)="previousPage($event, context)"
tabindex="0"
id="searchstax-pagination-previous"
>
< Previous
</a>
<div class="searchstax-pagination-details">
{{ paginationData?.startResultIndex }} -
{{ paginationData?.endResultIndex }} of
{{ paginationData?.totalResults }}
</div>
<a
class="searchstax-pagination-next"
[class]="paginationData.isLastPage ? 'disabled' : ''"
(click)="nextPage($event, context)"
(keyup.enter)="nextPage($event, context)"
(keyup.space)="nextPage($event, context)"
tabindex="0"
id="searchstax-pagination-next"
>Next ></a
>
</div>
</div>
</ng-template>
</app-searchstax-search-pagination>
// infinite scroll example
<app-searchstax-search-pagination [templateOverride]="paginationTemplate"
[infiniteScrollTemplateOverride]="infiniteScrollTemplate">
<ng-template
#infiniteScrollTemplate
let-paginationData="paginationData"
let-nextPage="nextPage"
let-context="context"
>
<div class="searchstax-pagination-container">
<div class="searchstax-pagination-content">
<a
class="searchstax-pagination-load-more"
(click)="nextPage($event, context)"
(keyup.enter)="nextPage($event, context)"
(keyup.space)="nextPage($event, context)"
tabindex="0"
>
Load More
</a>
</div>
</div>
</ng-template>
<ng-template
#paginationTemplate
let-paginationData="paginationData"
let-previousPage="previousPage"
let-nextPage="nextPage"
let-context="context"
>
<div class="searchstax-pagination-container">
<div class="searchstax-pagination-content">
<a
class="searchstax-pagination-previous"
[class]="paginationData.isFirstPage ? 'disabled' : ''"
(click)="previousPage($event, context)"
(keyup.enter)="previousPage($event, context)"
(keyup.space)="previousPage($event, context)"
tabindex="0"
id="searchstax-pagination-previous"
>
< Previous
</a>
<div class="searchstax-pagination-details">
{{ paginationData?.startResultIndex }} -
{{ paginationData?.endResultIndex }} of
{{ paginationData?.totalResults }}
</div>
<a
class="searchstax-pagination-next"
[class]="paginationData.isLastPage ? 'disabled' : ''"
(click)="nextPage($event, context)"
(keyup.enter)="nextPage($event, context)"
(keyup.space)="nextPage($event, context)"
tabindex="0"
id="searchstax-pagination-next"
>Next ></a
>
</div>
</div>
</ng-template>
</app-searchstax-search-pagination>Facets Widget
The SearchStax Site Search solution offers the SearchstaxFacetsWidget for Angular to display the search facets.
Facet Selection and Order
Facet lists are configured and ordered on the Site Search Faceting Tab.
Usage
<app-searchstax-search-facets
[facetingType]="'showUnavailable'"
[itemsPerPageDesktop]="2"
[itemsPerPageMobile]="99"
></app-searchstax-search-facets>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
- templateOverrideMobile - template override for mobile view
- templateOverrideDesktop - template override for desktop view
- 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:
- facetsTemplateDataDesktop - Facets data object
- isNotDeactivated - Check if facet group is active
- toggleFacetGroup - Toggle facet group active state
- isChecked - Check if facet value is selected
- selectFacet - Handler for facet select
- showMoreLessDesktop - Show more/less facets handler
- facetContainers - Object of facet DOM containers
- updateRefDesktop - Handler for keeping checkbox references
Main Template Mobile Override
Main wrapper template for mobile facets display.
It receives following props:
- facetsTemplateDataMobile - Facets data object
- selectedFacetsCheckboxes - Selected facet values
- isNotDeactivated - Check if facet group is active
- toggleFacetGroup - Toggle facet group active state
- isChecked - Check if facet value is selected
- selectFacet - Handler for facet select
- showMoreLessDesktop - Show more/less facets handler
- facetContainers - Object of facet DOM containers
- openOverlay - Handler to open mobile overlay
- unselectFacet - Handler to unselect specific facet
- unselectAll - Handler to unselect all facets
- closeOverlay - Handler to close mobile overlay
- updateRefMobile - Handler for keeping mobile checkbox references
Example
<app-searchstax-search-facets
[facetingType]="'showUnavailable'"
[itemsPerPageDesktop]="2"
[itemsPerPageMobile]="99"
[templateOverrideDesktop]="templateOverrideDesktop"
[templateOverrideMobile]="templateOverrideMobile"
>
<ng-template
#templateOverrideDesktop
let-facetsTemplateDataDesktop="facetsTemplateDataDesktop"
let-isNotDeactivated="isNotDeactivated"
let-context="context"
let-toggleFacetGroup="toggleFacetGroup"
let-facetValues="facetValues"
let-isChecked="isChecked"
let-selectFacet="selectFacet"
let-showMoreLessDesktop="showMoreLessDesktop"
>
<div class="searchstax-facets-container-desktop">
<div
class="searchstax-facet-container"
*ngFor="let facet of facetsTemplateDataDesktop?.facets"
[class]="{ active: isNotDeactivated(facet.name, context) }"
>
<div>
<div
class="searchstax-facet-title-container"
(click)="toggleFacetGroup(facet.name, context)"
>
<div class="searchstax-facet-title">
{{ facet.label }}
</div>
<div class="searchstax-facet-title-arrow active"></div>
</div>
<div class="searchstax-facet-values-container" aria-live="polite">
<div
*ngFor="let facetValue of facetValues(facet); index as key"
class="searchstax-facet-value-container"
[class]="{
'searchstax-facet-value-disabled': facetValue.disabled
}"
#ref
>
<div class="searchstax-facet-input">
<input
type="checkbox"
class="searchstax-facet-input-checkbox"
[checked]="isChecked(facetValue, context)"
[attr.aria-label]="
facetValue.value + ' ' + facetValue.count
"
[disabled]="facetValue.disabled"
(click)="
selectFacet(ref, $event, facetValue, true, context)
"
/>
</div>
<div
class="searchstax-facet-value-label"
(click)="selectFacet(ref, $event, facetValue, false, context)"
>
{{ facetValue.value }}
</div>
<div
class="searchstax-facet-value-count"
(click)="selectFacet(ref, $event, facetValue, false, context)"
>
({{ facetValue.count }})
</div>
</div>
<div
class="searchstax-facet-show-more-container"
*ngIf="facet.hasMoreFacets"
>
<div
class="searchstax-facet-show-more-container"
(click)="showMoreLessDesktop($event, facet, context)"
(keyup.enter)="showMoreLessDesktop($event, facet, context)"
(keyup.space)="showMoreLessDesktop($event, facet, context)"
tabindex="0"
role="button"
>
<div
*ngIf="facet.showingAllFacets"
class="searchstax-facet-show-less-button searchstax-facet-show-button"
>
less
</div>
<div
*ngIf="!facet.showingAllFacets"
class="searchstax-facet-show-more-button searchstax-facet-show-button"
>
more
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</ng-template>
<ng-template
#templateOverrideMobile
let-isNotDeactivated="isNotDeactivated"
let-context="context"
let-toggleFacetGroup="toggleFacetGroup"
let-facetValues="facetValues"
let-isChecked="isChecked"
let-selectFacet="selectFacet"
let-openOverlay="openOverlay"
let-selectedFacetsCheckboxes="selectedFacetsCheckboxes"
let-unselectFacet="unselectFacet"
let-unselectAll="unselectAll"
let-facetMobileData="facetMobileData"
let-facetsTemplateDataMobile="facetsTemplateDataMobile"
let-closeOverlay="closeOverlay"
let-showMoreLessDesktop="showMoreLessDesktop"
let-showMoreLessMobile="showMoreLessMobile"
>
<div class="searchstax-facets-container-mobile">
<div class="searchstax-facets-pills-container">
<div
class="searchstax-facets-pill searchstax-facets-pill-filter-by"
(click)="openOverlay(context)"
>
<div class="searchstax-facets-pill-label">Filter By</div>
</div>
<div class="searchstax-facets-pills-selected">
<div
class="searchstax-facets-pill searchstax-facets-pill-facets"
*ngFor="let facet of selectedFacetsCheckboxes"
(click)="unselectFacet(facet, context)"
>
<div class="searchstax-facets-pill-label">
{{ facet.value }} ({{ facet.count }})
</div>
<div class="searchstax-facets-pill-icon-close"></div>
</div>
</div>
<div
class="searchstax-facets-pill searchstax-clear-filters searchstax-facets-pill-clear-all"
*ngIf="selectedFacetsCheckboxes.length"
(click)="unselectAll(context)"
>
<div class="searchstax-facets-pill-label">Clear Filters</div>
</div>
</div>
<div
class="searchstax-facets-mobile-overlay"
[class]="{
'searchstax-show': facetMobileData(facetsTemplateDataMobile)
?.overlayOpened
}"
>
<div class="searchstax-facets-mobile-overlay-header">
<div class="searchstax-facets-mobile-overlay-header-title">
Filter By
</div>
<div
class="searchstax-search-close"
(click)="closeOverlay(context)"
></div>
</div>
<div class="searchstax-facets-container-mobile">
<div
*ngFor="let facet of facetsTemplateDataMobile?.facets"
class="searchstax-facet-container"
[class]="{
active: isNotDeactivated(facet.name, context)
}"
>
<div>
<div
class="searchstax-facet-title-container"
(click)="toggleFacetGroup(facet.name, context)"
>
<div class="searchstax-facet-title">
{{ facet.label }}
</div>
<div class="searchstax-facet-title-arrow active"></div>
</div>
<div
class="searchstax-facet-values-container"
aria-live="polite"
>
<div
*ngFor="let facetValue of facetValues(facet); index as key"
class="searchstax-facet-value-container"
[class]="{
'searchstax-facet-value-disabled': facetValue.disabled
}"
#ref
>
<div class="searchstax-facet-input">
<input
type="checkbox"
class="searchstax-facet-input-checkbox"
[checked]="isChecked(facetValue, context)"
[attr.aria-label]="
facetValue.value + ' ' + facetValue.count
"
[disabled]="facetValue.disabled"
(click)="
selectFacet(ref, $event, facetValue, true, context)
"
/>
</div>
<div
class="searchstax-facet-value-label"
(click)="
selectFacet(ref, $event, facetValue, false, context)
"
>
{{ facetValue.value }}
</div>
<div
class="searchstax-facet-value-count"
(click)="
selectFacet(ref, $event, facetValue, false, context)
"
>
({{ facetValue.count }})
</div>
</div>
<div
class="searchstax-facet-show-more-container"
*ngIf="facet.hasMoreFacets"
>
<div
class="searchstax-facet-show-more-container"
(click)="showMoreLessMobile($event, facet, context)"
(keyup.enter)="showMoreLessMobile($event, facet, context)"
(keyup.space)="showMoreLessMobile($event, facet, context)"
tabindex="0"
>
<div
*ngIf="facet.showingAllFacets"
class="searchstax-facet-show-less-button searchstax-facet-show-button"
>
less
</div>
<div
*ngIf="!facet.showingAllFacets"
class="searchstax-facet-show-more-button searchstax-facet-show-button"
>
more
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<button
class="searchstax-facets-mobile-overlay-done"
(click)="closeOverlay(context)"
>
Done
</button>
</div>
</div>
</ng-template>
</app-searchstax-search-facets>SearchFeedback Widget
The SearchStax Site Search solution includes an Angular feedback widget for your custom search page.
The SearchstaxFeedbackWidget for Angular displays search feedback and stats.
Usage
<app-searchstax-search-feedback></app-searchstax-search-feedback>Props
- templateOverride - template override for search feedback
Main Template Override
Main template for the search feedback message.
It receives following props:
- searchFeedbackData - Feedback data object
- onOriginalQueryClick - Handler for clicking on original query suggestion
Example
<app-searchstax-search-feedback [templateOverride]="feedbackTemplate">
<ng-template
#feedbackTemplate
let-searchFeedbackData="searchFeedbackData"
let-onOriginalQueryClick="onOriginalQueryClick"
let-context="context"
>
<h2 class="searchstax-feedback-container">
Showing
<b
>{{ searchFeedbackData!.startResultIndex }} -
{{ searchFeedbackData!.endResultIndex }}</b
>
of <b>{{ searchFeedbackData!.totalResults }}</b> results
<span *ngIf="searchFeedbackData.searchTerm"
>for "<b>{{ searchFeedbackData!.searchTerm }}</b
>"
</span>
<div class="searchstax-feedback-container-suggested">
<div *ngIf="searchFeedbackData.autoCorrectedQuery">
Search instead for
<a
href="#"
(click)="onOriginalQueryClick($event, context)"
class="searchstax-feedback-original-query"
[attr.aria-label]="
'Search instead for: ' + searchFeedbackData!.originalQuery
"
>{{ searchFeedbackData!.originalQuery }}</a
>
</div>
</div>
</h2>
</ng-template>
</app-searchstax-search-feedback>RelatedSearches widget
The SearchStax Site Search solution offers an Angular related-searches widget for your custom search page.
The SearchstaxRelatedSearchesWidget displays related searches.
Usage
<app-searchstax-related-searches
[relatedSearchesURL]="config.relatedSearchesURL"
[relatedSearchesAPIKey]="config.relatedSearchesAPIKey"
></app-searchstax-related-searches>Props
- relatedSearchesURL: API URL for fetching related searches
- relatedSearchesAPIKey?: API key for related searches API
- templateOverride - template override for related search
Main Template Override
Main template for related searches.
It receives following props:
- relatedData - Related searches data object
- executeSearch - Handler to run new search from related term
Example
<app-searchstax-related-searches
[relatedSearchesURL]="config.relatedSearchesURL"
[relatedSearchesAPIKey]="config.relatedSearchesAPIKey"
[templateOverride]="relatedTemplate"
>
<ng-template
#relatedTemplate
let-rand="rand"
let-relatedData="relatedData"
let-executeSearch="executeSearch"
let-context="context"
>
<div
class="searchstax-related-searches-container"
[id]="'searchstax-related-searches-container' + rand"
>
Related searches: <span id="searchstax-related-searches"></span>
<span
class="searchstax-related-search"
*ngIf="relatedData.relatedSearches"
>
<span
*ngFor="let related of relatedData.relatedSearches"
(click)="executeSearch(related, context)"
(keyup.enter)="executeSearch(related, context)"
(keyup.space)="executeSearch(related, context)"
tabindex="0"
class="searchstax-related-search searchstax-related-search-item"
[attr.aria-label]="'Related search: ' + related.related_search"
>
{{ related.related_search }}<span *ngIf="!related.last">,</span>
</span>
</span>
</div>
</ng-template>
</app-searchstax-related-searches>ExternalPromotions widget
The SearchStax Site Search solution offers an Angular external-promotions widget for your custom search page.
The SearchstaxExternalPromotionsWidget displays external promotions fetched from the API.
Usage
<app-searchstax-external-promotions></app-searchstax-external-promotions>Props
- templateOverride - template override for external promotion
Main Template Override
Main template for external promotions.
It receives following props:
- externalPromotionsData – External promotions data object
- trackClick – Handler for tracking link clicks
Example
<app-searchstax-external-promotions [templateOverride]="externalPromotionsTemplate">
<ng-template
#externalPromotionsTemplate
let-context="context"
let-externalPromotionsData="externalPromotionsData"
let-trackClick="trackClick"
>
<div
class="searchstax-external-promotions-container"
id="searchstax-external-promotions-container"
>
<div
class="searchstax-external-promotion searchstax-search-result"
*ngFor="
let externalPromotion of externalPromotionsData.externalPromotions
"
>
<div class="icon-elevated"></div>
<a
*ngIf="externalPromotion.url"
[href]="externalPromotion.url"
(click)="trackClick(externalPromotion, $event, context)"
class="searchstax-result-item-link"
></a>
<div class="searchstax-search-result-title-container">
<span class="searchstax-search-result-title">{{
externalPromotion.name
}}</span>
</div>
<p
*ngIf="externalPromotion.description"
class="searchstax-search-result-description searchstax-search-result-common"
>
{{ externalPromotion.description }}
</p>
<p
*ngIf="externalPromotion.url"
class="searchstax-search-result-description searchstax-search-result-common"
>
{{ externalPromotion.url }}
</p>
</div>
</div>
</ng-template>
</app-searchstax-external-promotions>Sorting Widget
The SearchStax Site Search solution provides an Angular sorting widget for your custom search page.
The SearchstaxSortingWidget displays sorting options for search results.
Usage
<app-searchstax-search-sorting></app-searchstax-search-sorting>Props
- templateOverride - template override for sorting widget
Main Template Override
Main template for sorting widget.
It receives following props:
- sortingData – Sorting data object
- orderChange – Handler for sorting change
- selectedSorting – Current selected sorting
- templateOverride - template override for sorting widget
Example
<app-searchstax-search-sorting [templateOverride]="sortingTemplateOverride">
<ng-template
#sortingTemplateOverride
let-selectedSorting="selectedSorting"
let-orderChange="orderChange"
let-context="context"
>
<div class="searchstax-sorting-container">
<label
class="searchstax-sorting-label"
for="searchstax-search-order-select"
>Sort By</label
>
<select
id="searchstax-search-order-select"
class="searchstax-search-order-select"
[(ngModel)]="context.selectedSorting"
(change)="orderChange($event, context)"
>
<option
*ngFor="let sortOption of context.sortingData.sortOptions"
[value]="sortOption.key"
>
{{ sortOption.value }}
</option>
</select>
</div>
</ng-template>
</app-searchstax-search-sorting>Template overrides
Templates use vue templating.
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