SearchBar
The SearchBar
component is the main entry point for search querying.
It provides the input box where the user types their query, as well as the autocomplete behavior.
Search Bars can be used in both Vertical and Universal Search.
If you’re only adding a searchbar to the page (instead of the full experience), we recommend using our searchbar-only assets. They contain only the necessary JS/CSS/hbs for the searchbar component, and are therefore optimized for page speed. The JS component has the exact same API properties as the SearchBar
described here, with one exception: it requires a redirectUrl
.
Read more about this bundle here, and see our Hitchhiker guide for further integration instructions.
Default Styling
Here is the default styling for a search bar:
And here is the default styling for a search bar with autocomplete showing:
Universal Search
Here is an example of adding a search bar to Universal Search:
<div class="search-bar-container"></div>
ANSWERS.addComponent("SearchBar", {
container: ".search-bar-container",
});
Vertical Search
Here is an example of adding a search bar to Vertical Search.
It’s the same as Universal Search but requires an additional verticalKey
.
<div class="search-bar-container"></div>
ANSWERS.addComponent("SearchBar", {
container: ".search-bar-container",
verticalKey: "VERTICAL_KEY", // required if not specified in search config in initialization.
});
Redirecting Search
Often you want to add a Search Bar to a page but then redirect to a search results page.
This is accomplished via the redirectUrl
property. If this property is filled out,
the search will redirect when a user hits submit. This URL should be a search results page that includes
a UniversalResults
or VerticalResults
component.
Using HTML5 Geolocation
By default, when a user types a query with “near me” intent (IE “locations near me”), we’ll prompt them to grant access to HTML5 geolocation. If you do not want this behavior, you can set the promptForLocation
to false. If you notice consistently lethargic geolocation requests, you can reduce the timeout (geolocationOptions.timeout
) or increase the maximum age for a geolocation request (geolocationOptions.maximumAge
), and add an optional alert if the request fails (geolocationTimeoutAlert
).
Finally, if the accuracy of geolocation information is particularly important, you can enable “high accuracy”, which will provide more accurate geolocation results at the expense of response time and/or device power consumption.
More information on enableHighAccuracy
, timeout
and maximumAge
can be found in the MDN documentation.
ANSWERS.addComponent("SearchBar", {
container: ".search-bar-container",
redirectUrl: "https://www.domain.com",
geolocationOptions: {
timeout: 500,
maximumAge: 300000,
enableHighAccuracy: false
},
geolocationTimeoutAlert: {
enabled: true,
message: "We are unable to determine your location"
}
});
Multiple Search Bars
Often you will want to have multiple search bars on a single page. For example one in the header and one in the center
of the home page. To accomplish this make sure each search bar has a unique container
and name
.
ANSWERS.addComponent("SearchBar", {
container: ".search-bar-container",
name: "search-bar",
redirectUrl: "https://www.domain.com/search-results",
});
ANSWERS.addComponent("SearchBar", {
container: ".search-bar-container-2",
name: "search-bar-2",
redirectUrl: "https://www.domain.com/search-results",
});
Typing Placeholder
One nice UI trick to encourage more users to search is to show a user typing in the input. While the Answers JS library doesn’t support this by default, this can easily be added using Typed.js.
First, we will get the options via the /autocomplete
endpoint and then we feed those options into the Typed. This should happen after the
SearchBar
component is added to the page. We are using axios
to handle the API request.
var url = `https://liveapi-cached.yext.com/v2/accounts/me/answers/autocomplete`;
url += `?v=20190101`;
url += `&api_key=${apiKey}`;
url += `&sessionTrackingEnabled=false`;
url += `&experienceKey=${experienceKey}`;
url += `&input=`;
url += `&version=${version}`;
url += `&locale=${locale}`;
axios.get(url).then(function(response) {
// Get strings from response
const strings = response.data.response.results.map(function(r) {
return r.value;
});
// Set up Typed
var options = {
strings,
showCursor: true,
cursorChar: "|",
typeSpeed: 45,
backSpeed: 20,
smartBackspace: true,
loop: true,
startDelay: 500,
backDelay: 2000,
attr: "placeholder",
};
var typed = new Typed(".js-yext-query", options);
});
Here is a working example:
SearchBar API
Property | Type | Default | Description |
---|---|---|---|
verticalKey
|
string
|
The vertical for the search bar. Required if used in vertical search, and not included in the top level search configuration. | |
query
|
string
|
The initial search term in the search bar. Does not apply a query | |
labelText
|
string
|
Label for the search bar. Hidden by default. | |
submitText
|
string
|
The label of the submit button. Hidden by default. | |
submitIcon
|
string
|
Specify pre-built icon for the search bar. If none is specified, the animated search icon will be used. See icons. | |
customIconUrl
|
string
|
A custom icon URL to replace the default search icon. | |
promptHeader
|
string
|
Text label displayed above the autocomplete options. | |
placeholderText
|
string
|
Placeholder text of the search bar | |
allowEmptySearch
|
boolean
|
Allow a user to conduct an empty search. Should be set to true if the defaultInitialSearch in the top level search configuration is “”. | |
autoFocus
|
boolean
|
Auto-focus the search bar on page load. | |
autocompleteOnLoad
|
boolean
|
If autoFocus is true, show or display the autocomplete prompts.
|
|
searchCooldown
|
number
|
300
|
The number of miliseconds to wait before conducting new searches. Guards against someone rage-clicking the search bar. |
promptForLocation
|
boolean
|
true
|
Ask the user for their geolocation when “near me” intent is detected. |
clearButton
|
boolean
|
true
|
Display an “x” button to clear the current query. |
redirectUrl
|
string
|
true
|
Redirect the search query to url |
formSelector
|
string
|
form
|
Form Selector that is provided to the search bar template |
inputEl
|
string
|
js-yext-query
|
Input Element that is required if a custom template is used where the input varies. |
useForm
|
boolean
|
true
|
When true, a form is used as the query submission context. Note it’s WCAG best practice to set this to true .
|
geolocationOptions
|
object
|
Geolocation-related configuration. See Geolocation Documentation for more information. | |
enableHighAccuracy
|
boolean
|
Whether to improve accuracy at the cost of response time and/or power consumption, defaults to false. | |
timeout
|
number
|
1000
|
The maximum amount of time (in ms) a geolocation call is allowed to take before defaulting, defaults to 1 second. |
maximumAge
|
number
|
300000
|
The maximum amount of time (in ms) to cache a geolocation call, defaults to 5 minutes. |
geolocationTimeoutAlert
|
object
|
Configuration for a native alert, displayed when a call to the geolocation API fails. | |
enabled
|
boolean
|
Whether to display a window.alert() on the page | |
message
|
string
|
We are unable to determine your location
|
If enabled is true, the message in the alert.
|
voiceSearch
|
object
|
Configuration related to displaying a microphone icon for conducting a search with voice using the Web Speech API | |
enabled
|
boolean
|
Whether to display the voice search icon. | |
customMicIconUrl
|
string
|
A URL for an image that replaces the default microphone icon | |
customListeningIconUrl
|
string
|
A URL for an image that replaces the default listening icon |
Navigation
Background
The navigation component is made of up of tabs that link to universal and vertical search pages. You’ll typically see it appear just below the search bar.
When conducting a search from universal, the tabs will reorder based on the order of the search results. By default, tabs that do not fit in the container will be placed inside a dropdown menu. This can be configured for mobile (see mobileOverflowBehavior
in API Properties below).
The pages that appear in the navigation are configured in the ANSWERS.init’s verticalPages configuration.
We don’t recommend dynamically changing the width of the overflow button, as this can impact it’s behavior unintentionally (there is logic that checks for changes to Navigation width to update the More dropdown content). If you need to include a border on the button, we recommend using an
outline
instead.
Configuration
<div class="navigation-container"></div>
ANSWERS.addComponent('Navigation', {
container: '.navigation-container',
// mobileOverflowBehavior: 'COLLAPSE',
// ariaLabel: 'Search Page Navigation',
// overflowLabel: 'More',
// overflowIcon: null
})
Example
Navigation API
Property | Type | Default | Description |
---|---|---|---|
ariaLabel
|
string
|
Search Page Navigation
|
Optional, the aria-label to set on the navigation. |
mobileOverflowBehavior
|
string
|
COLLAPSE
|
Optional, controls if navigation collapses to a more tab, or uses inner scroll on mobile. Options are COLLAPSE or INNERSCROLL .
|
overflowLabel
|
string
|
More
|
Optional, the label to display on the dropdown menu button when it overflows. |
overflowIcon
|
string
|
three stacked dots
|
Optional, name of the icon to show on the dropdown button instead when it overflows. |
SpellCheck
Background
The SpellCheck
component helps correct a user’s spelling.
It is usually displayed above the results. It works on both Universal Search and Vertical Search and
is generally added directly above the
VerticalResults
or DirectAnswer
.
Configuration
The component is named SpellCheck
. There are no additional required properties.
<div class="spell-check-container></div>
ANSWERS.addComponent("SpellCheck", {
container: ".spell-check-container",
// suggestionHelpText: "Are you sure you didn't mean: ",
});
Example
SpellCheck API
Property | Type | Default | Description |
---|---|---|---|
suggestionHelpText
|
string
|
Did you mean
|
Did you mean text to displays before the query |
DirectAnswer
Background
The DirectAnswer
component shows a Direct Answer to a query. It is usually shown above the UniversalResults
component. DirectAnswers are only returned on Universal Search.
Default Styling
Here is the default styling for a Direct Answer:
Base Configuration
Here’s a basic example of adding a direct answer.
<div class="direct-answer-container"></div>
ANSWERS.addComponent("DirectAnswer", {
container: ".direct-answer-container",
});
Creating a Custom DirectAnswer Card
You can customize the look and behavior of your Direct Answer by creating a custom Direct Answer card.
A custom Direct Answer card is given the same data as the built-in card. That data will look something like the below:
{
type: "FIELD_VALUE",
answer: {
entityName: "Entity Name",
fieldName: "Phone Number",
fieldApiName: "mainPhone",
value: "+11234567890",
fieldType: "phone"
},
relatedItem: {
verticalConfigId: 'people',
data: {
id: "Employee-2116",
type: "ce_person",
fieldValues: {
description: "This is the description field.",
name: "First Last",
firstName: "First",
lastName: "Last",
mainPhone: "+1234567890",
}
}
}
}
A custom Direct Answer card needs a corresponding template. This can be added either inline by changing the component’s constructor to:
constructor(config, systemConfig) {
super(config, systemConfig);
this.setTemplate(`<div> your template here </div>`)
}
Or by including a custom template bundle, and adding:
static defaultTemplateName () {
return 'CustomDirectAnswerTemplate';
}
Where ‘CustomDirectAnswerTemplate’ is the name the template is registered under.
We will use the following template for our example card.
<div class="customDirectAnswer">
<div class="customDirectAnswer-type">
{{type}}
</div>
<div class="customDirectAnswer-value">
{{#each customValue}}
{{#if url}}
{{> valueLink }}
{{else}}
{{{this}}}
{{/if}}
{{/each}}
</div>
{{> feedback}}
</div>
{{#*inline 'feedback'}}
<span class="customDirectAnswer-thumbsUpIcon js-customDirectAnswer-thumbsUpIcon"
data-component="IconComponent"
data-opts='{"iconName": "thumb"}'
></span>
<span class="customDirectAnswer-thumbsDownIcon js-customDirectAnswer-thumbsDownIcon"
data-component="IconComponent"
data-opts='{"iconName": "thumb"}'
></span>
{{/inline}}
{{#*inline 'valueLink'}}
<a class="customDirectAnswer-fieldValueLink" href="{{{url}}}"
{{#if @root/eventType}}data-eventtype="{{@root/eventType}}"{{/if}}
{{#if @root/eventOptions}}data-eventoptions='{{{ json @root/eventOptions }}}'{{/if}}>
{{{displayText}}}
</a>
{{/inline}}
This specific example needs some css to flip the thumbs up icon the right way.
.customDirectAnswer-thumbsUpIcon svg {
transform: rotate(180deg);
}
This is the javascript class for our custom Direct Answer card. It applies custom formatting to the Direct Answer, registers analytics events to the thumbs up/down icons, and passes custom event options into the template.
class CustomDirectAnswerClass extends ANSWERS.Component {
constructor(config, systemConfig) {
// If you need to override the constructor, make sure to call super(config, systemConfig) first.
super(config, systemConfig);
// For simplicity's sake, we set this card's template using setTemplate(), as opposed to
// a custom template bundle.
this.setTemplate(`<div> your template here </div>`)
}
/**
* setState() lets you pass variables directly into your template.
* Here, data is the directAnswer data from the query.
* Below, we pass through a custom direct answers value, customValue.
* @param {Object} data
* @returns {Object}
*/
setState(data) {
const { type, answer, relatedItem } = data;
const associatedEntityId = data.relatedItem && data.relatedItem.data && data.relatedItem.data.id;
const verticalConfigId = data.relatedItem && data.relatedItem.verticalConfigId;
return super.setState({
...data,
customValue: this.getCustomValue(answer),
eventType: 'CUSTOM_EVENT',
eventOptions: {
searcher: 'UNIVERSAL',
verticalConfigId: verticalConfigId,
entityId: associatedEntityId,
}
});
}
/**
* onMount() lets you register event listeners. Here, we register the thumbs up and thumbs
* down buttons to fire an analytics event on click.
*/
onMount() {
const thumbsUpIcon = this._container.querySelector('.js-customDirectAnswer-thumbsUpIcon');
const thumbsDownIcon = this._container.querySelector('.js-customDirectAnswer-thumbsDownIcon');
thumbsUpIcon.addEventListener('click', () => this.reportQuality(true));
thumbsDownIcon.addEventListener('click', () => this.reportQuality(false));
}
/**
* reportQuality() sends an analytics event (either THUMBS_UP or THUMBS_DOWN).
* @param {boolean} isGood true if the answer is what you were looking for
*/
reportQuality(isGood) {
const eventType = isGood === true ? 'THUMBS_UP' : 'THUMBS_DOWN';
const event = new ANSWERS.AnalyticsEvent(eventType).addOptions({
directAnswer: true
});
this.analyticsReporter.report(event);
}
/**
* Formats a Direct Answer value based on its fieldType.
* @param {Object} answer the answer property in the directAnswer model
* @returns {string}
*/
formatValue(answer) {
const { fieldType, value } = answer;
switch (fieldType) {
case 'phone':
return {
url: 'http://myCustomWebsite.com/?mainPhone=' + value,
displayText: value,
};
case 'rich_text':
return ANSWERS.formatRichText(value);
case 'single_line_text':
case 'multi_line_text':
default:
return value;
}
}
/**
* Computes a custom Direct Answer. If answer.value is an array, this method
* formats every value in the array and returns it, otherwise it just formats the single
* given value.
* @param {Object} answer
* @returns {Array<string>}
*/
getCustomValue(answer) {
if (Array.isArray(answer.value)) {
return answer.value.map(value => this.formatValue(answer))
} else {
return [ this.formatValue(answer) ];
}
}
/**
* The name of your custom direct answer card. THIS is the value you will use in any config,
* such as defaultCard, when you want to specify this custom Direct Answer card.
* @returns {string}
*/
static get type() {
return 'MyCustomDirectAnswerCard';
}
}
// Don't forget to register your Direct Answer card within the SDK. Otherwise the SDK won't recognize your card name!
ANSWERS.registerComponentType(CustomDirectAnswerClass);
Using Cardoverrides
Now that we’ve created a custom direct answer card, you can specify which card should be used based on the type of direct answer that returns. There are two possible types: FIELD_VALUE
, which returns for NLP filters, and FEATURED_SNIPPET
, which returns for document search.
Here, we’ve specified that any FEATURED_SNIPPET
direct answers should use a custom card we’ve named MyCustomDirectAnswerCardDocSearch
. FIELD_VALUE
card types are a little more advanced, we’ll go into these below:
ANSWERS.addComponent("DirectAnswer", {
container: ".direct-answer-container",
types: {
'FEATURED_SNIPPET': {
cardType: "MyCustomDirectAnswerCardDocSearch",
},
'FIELD_VALUE': {
cardType: "MyCustomDirectAnswerCard",
cardOverrides: [
{
cardType: 'MyMenuCustomDirectAnswerCard',
fieldName: 'description',
entityType: 'ce_menuItem',
fieldType: 'rich_text'
}
]
}
}
});
FIELD_VALUE
is using cardOverrides
; with these, we can specify that a different card should be used based on the fieldName
, entityType
, and/or fieldType
combination. This DirectAnswer component will pick the first condition matched, so the ordering of the cardOverrides
is important. For example, if the following overrides were specified:
cardOverrides: [
{
cardType: 'MyCustomDirectAnswerCard',
entityType: 'ce_menuItem',
},
{
cardType: 'MyOtherCustomDirectAnswerCard',
fieldName: 'description',
entityType: 'ce_menuItem',
},
]
All menu item entities would receive the MyCustomDirectAnswerCard
, since it’s the first rule in the list that applies, even if a direct answer returned for the description
field.
Formatting Data in a Direct Answer
You can format data in a direct answer using the transformData
hook outlined here.
Example
In this example, we’ve overridden the viewDetailsText
and the footerTextOnSubmission
. We’re also using a Custom Data Transform to override the formatting for a phone number.
DirectAnswer API
Property | Type | Default | Description |
---|---|---|---|
formEl
|
string
|
.js-directAnswer-feedback-form
|
The selector for the form used for submitting the feedback |
thumbsUpSelector
|
string
|
.js-directAnswer-thumbUp
|
The selector to bind ui interaction to for the thumbs up button |
thumbsDownSelector
|
string
|
.js-directAnswer-thumbDown
|
The selector to bind ui interaction to for the thumbs down button |
positiveFeedbackSrText
|
string
|
This answered my question
|
The screen reader text for the thumbs up button. |
negativeFeedbackSrText
|
string
|
This did not answer my question
|
The screen reader text for the thumbs down button. |
viewDetailsText
|
string
|
View Details
|
The display text for the View Details click to action link, which is the website URL of the entity. |
footerTextOnSubmission
|
string
|
Thank you for your feedback!
|
The footer text to display on submission of feedback |
defaultCard
|
string
|
DEPRECATED Optionally specify a custom direct answer card to use, which is the default when there are no matching card overrides. | |
cardOverrides
|
|
DEPRECATED Formerly used to specify a specific direct answer card based on the fieldName, entityType or fieldType | |
types
|
object
|
Specify card types and overrides based on the direct answer type (FEATURED_SNIPPET or FIELD_VALUE ). Each property is nested under the direct answer type.
|
|
type.cardType
|
string
|
The name of the card to use for this direct answer type. | |
type.cardOverrides
|
array
|
Card overrides for this direct answer type. | |
fieldName
|
string
|
The field name for which this override should apply | |
entityType
|
string
|
The entity type name for which this override should apply | |
fieldType
|
string
|
The field type for which this override should apply | |
cardType
|
string
|
The card to use when this override is fulfilled |
UniversalResults
Background
The UniversalResults
component renders a set of verticals. Each vertical is rendered as a section. Each section includes a list of results, where each result is rendered as a card. Visually, there are lots of similarities between each vertical section and the Vertical Results component. That’s because, under the hood, Universal Results uses Vertical Results component to render the sections and their results.
To learn more about how to design a full Universal Results pages, check out the Universal Search Results.
The Configuration Object
The bulk of the Universal Results work is specifying the configuration for each vertical, under the larger config
object. All verticals that are specified in the backend Answers configuration will be displayed using the default standard
card (even if the config
is not specified, or if a vertical is not included in said config
).
Configuration
Here is an example configuration for UniversalResults
.
ANSWERS.addComponent('UniversalResults', {
container: '.universal-results-container',
appliedFilters: {
show: true,
showFieldNames: false,
hiddenFields: ['builtin.entityType'],
resultsCountSeparator: '|',
showChangeFilters: false,
changeFiltersText: 'change filters',
delimiter: '|',
labelText: 'Filters applied to this search:',
},
config: {
people: { // The verticalKey
title: 'People',
icon: 'star',
url: 'people.html',
viewMore: true,
viewMoreLabel: 'View More!',
showResultCount: true,
includeMap: true,
mapConfig: {
mapProvider: 'google',
apiKey: '<<< enter your api key here >>>',
},
}
},
})
Example
Here is a fully working example:
UniversalResults API
Property | Type | Default | Description |
---|---|---|---|
appliedFilters
|
object
|
Settings for the applied filters bar in the results header. These settings can be overriden in the “config” option below on a per-vertical basis. | |
show
|
boolean
|
true
|
If true, show any applied filters that were applied to the universal search. |
showFieldNames
|
boolean
|
If appliedFilters.show is true, whether to display the field name of an applied filter, e.g. “Location - Virginia” vs just “Virginia”. | |
hiddenFields
|
array
|
['builtin.entityType']
|
If appliedFilters.show is true, this is list of filters that should not be displayed. |
resultsCountSeparator
|
string
|
|
|
The character that separates the count of results (e.g. “1-6”) from the applied filter bar. |
showChangeFilters
|
boolean
|
Whether to display the change filters link that links out to the vertical search. | |
changeFiltersText
|
string
|
change filters
|
If showChangeFilters is true, the text for the change filters link. |
delimiter
|
string
|
|
|
The character that separates each field (and its associated filters) within the applied filter bar. |
labelText
|
string
|
Filters applied to this search:
|
The aria-label given to the applied filters bar. |
config
|
object
|
configuration for each vertical’s results. Each property is nested under the verticalKey. | |
card
|
object
|
Standard
|
The card used to display each individual result, see Result Cards. |
template
|
string
|
A custom Handlebars template for this section | |
title
|
string
|
The vertical key
|
The title of the vertical, displayed in the section heading. |
icon
|
string
|
star
|
Icon to display to the left of the title. Must be one of the SDK’s built-in icons |
url
|
string
|
VERTICAL_KEY.html
|
The url for both the viewMore link and the change-filters link. The VERTICAL_KEY used in the default is the key in the config object.
|
viewMore
|
boolean
|
true
|
Whether to display a view more link at the bottom of the universal results section for this vertical. Url , or the verticalPages .url initialization option must be populated. See more on verticalPages here.
|
viewMoreLabel
|
string
|
View More
|
The text for the view more link, if viewMore is true. |
appliedFilters
|
object
|
Same as appliedFilters settings above. Settings specified here will override any top level settings. | |
show
|
boolean
|
true
|
If true, show any applied filters that were applied to the universal search. |
showFieldNames
|
boolean
|
If appliedFilters.show is true, whether to display the field name of an applied filter, e.g. “Location - Virginia” vs just “Virginia”. | |
hiddenFields
|
array
|
['builtin.entityType']
|
If appliedFilters.show is true, this is list of filters that should not be displayed. |
resultsCountSeparator
|
string
|
|
|
The character that separates the count of results (e.g. “1-6”) from the applied filter bar. |
showChangeFilters
|
boolean
|
Whether to display the change filters link that links out to the vertical search. | |
changeFiltersText
|
string
|
change filters
|
If showChangeFilters is true, the text for the change filters link. |
delimiter
|
string
|
|
|
The character that separates each field (and its associated filters) within the applied filter bar. |
labelText
|
string
|
Filters applied to this search:
|
The aria-label given to the applied filters bar. |
useAccordion
|
boolean
|
DEPRECATED Use the accordion card instead. Whether to use the AccordionResults component instead of VerticalResults for this vertical. | |
includeMap
|
boolean
|
Whether to include a map in this vertical results section. | |
mapConfig
|
object
|
Configuration for the map. If includeMap is true, this is required. Additional configuration for this object described in the Map Component documentation. | |
mapProvider
|
string
|
REQUIRED Either ‘mapbox’ or ‘google’, not case sensitive | |
apiKey
|
string
|
REQUIRED API key for the map provider | |
viewAllText
|
string
|
DEPRECATED Use viewMoreLabel instead. viewAllText is a synonym for viewMoreLabel, where viewMoreLabel takes precedence over viewAllText. |
VerticalResults
Background
The VerticalResults
component is similar to UniversalResults
but just renders a
single vertical. It displays a list of results where each result is rendered as a card.
To learn more about how to design a full Vertical Results pages, check out the Vertical Search Results.
Specifying the Vertical
First, you should make sure to specify the vertical of the page.
To specify the vertical that is used on the page, the verticalKey
should be added to the Facets
, SearchBar
, and initial search
config.
The
VerticalResults
component does not accept averticalKey
. It is infererred based on the other components on the page.
Configuration
Here is an example configuration for VerticalResults
. In this example you can see the card is customized and we are choosing to display
all results if no results are found.
ANSWERS.addComponent("VerticalResults", {
container: ".vertical-results",
card: {
cardType: "Standard",
dataMappings: {
title: (item) => item.name,
subtitle: (item) => item.address.line1,
details: "",
image: (item) =>
!item.c_advisorPhoto ? null : item.c_advisorPhoto.image.url,
url: "#",
},
callsToAction: [
{
label: "Book Appointment",
icon: "calendar",
url: "#",
analyticsEventType: "CTA_CLICK",
target: "_self",
},
{
label: "Call",
icon: "phone",
url: (item) => `tel:${item.mainPhone}`,
analyticsEventType: "TAP_TO_CALL",
},
],
},
noResults: {
displayAllResults: true,
},
});
Customizing Result Cards
To customize the result card you should adjust the card
property. Specifically, use the dataMappings
and callsToAction
to customize
the look and the feel of the card. You can learn more in the Result Cards Guide.
No Results
With version 0.13.1, you can show all results when no results return, and link to other verticals that return results. See more in our Vertical No Results Guide
VerticalResults API
Property | Type | Default | Description |
---|---|---|---|
container
|
string
|
REQUIRED Selector for the component container | |
renderItem
|
function
|
string to give custom template to result item | |
itemTemplate
|
string
|
Handlebars template for a custom card | |
maxNumberOfColumns
|
number
|
1
|
Set a maximum number of columns that will display at the widest breakpoint. Possible values are 1,2,3 or 4. |
showResultCount
|
boolean
|
true
|
Whether to display the total number of results |
card
|
object
|
The card used to display each individual result, see the Cards section for more details | |
noResults
|
object
|
Configuration for what to display when no results are found | |
template
|
string
|
Handlebars template for the no results component | |
displayAllResults
|
boolean
|
Shows all results in the vertical when no results are found |
FilterSearch
Background
The FilterSearch
component provides a text input box for users to type characters and select a preset matching filter.
The potential filters displayed are based on fields in the Knowledge Graph. A single FilterSearch
can combine multiple filters into one interface. FilterSearch has typo tolerance built in but does not do any natural language processing and only supports selecting an explicit filter.
When a filter is selected, a vertical search is performed. If multiple FilterSearch components are on the page, the search will include all selected filters across all of the components.
Styling
FilterSearch
doesn’t come with much styling, so it’s recommended to write custom CSS to style the component.
The below demo includes some example custom CSS.
Configuration
The only required configuration option beyond container
and verticalKey
(if not specified in the top level search configuration initialization) is searchParameters
.
searchParameters
specifies which fields should be searchable. It takes in two properties:sectioned
andfields
.fields
takes in an array of field options (fieldId
,entityTypeId
).sectioned
determines if the multiple fields should be seperated by sections.
Using Single vs. Multiple Fields
Filtersearch
can filter on a single field, or more than one.
Single Field
Here is an example of FilterSearch
using a single field - name
ANSWERS.addComponent("FilterSearch", {
container: ".filter-search",
verticalKey: "locations",
searchText: "Find an Employee",
searchParameters: {
fields: [
{
fieldId: "name",
entityTypeId: "ce_person",
},
],
},
});
Multiple Fields
Here is an example of FilterSearch
combining two fields together - name
and builtin.location
.
builtin.location
will provide a location autocomplete experience.
ANSWERS.addComponent("FilterSearch", {
container: ".filter-search",
verticalKey: "people",
searchText: "Find an Employee or Location",
// highlight-start
searchParameters: {
sectioned: true,
fields: [
{
fieldId: "builtin.location",
entityTypeId: "ce_person",
},
{
fieldId: "name",
entityTypeId: "ce_person",
},
],
},
// highlight-end
});
Setting a Default Query
You might want to set a default query on load. To do this, set the query
and filter
attributes; the former controls the text that displays in the input, while the latter controls the actual filter applied on load.
this.addComponent("FilterSearch", {
container: ".filter-search-container",
verticalKey: "locations",
query: "Peaceful Coffee",
filter: {
name: {
$eq: "Peaceful Coffee"
}
},
searchParameters: {
sectioned: true,
fields: [
{
fieldId: "builtin.location",
entityTypeId: "location"
},
{
fieldId: "name",
entityTypeId: "location"
}
]
}
});
Example
Here’s a demo of FilterSearch
. In this example, we filter on two fields (name
and builtin.location
). We also pre-applied a query and a filter for the location name “Peaceful Cafe”. Finally, we’ve added custom CSS to style the component.
FilterSearch API
Property | Type | Default | Description |
---|---|---|---|
verticalKey
|
string
|
REQUIRED verticalKey | |
placeholderText
|
string
|
Placeholder text for the input | |
searchParameters
|
object
|
Params for the FilterSearch | |
fields
|
array
|
List of fileds to query | |
fields[].fieldId
|
string
|
Field ID of the field in KG | |
fields[].entityTypeIds
|
string
|
Entity type api name e.g. healthcareProfessional , ce_person
|
|
searchParameters.sectioned
|
boolean
|
if true sections search results by search filter | |
storeOnChange
|
boolean
|
If true the selected filter is saved and used for the next search but it does not trigger a new search. | |
title
|
string
|
A title above the FilterSearch box | |
searchText
|
string
|
What are you interested in?
|
The search text used for labeling the input box |
promptHeader
|
string
|
Text to show as the first item in autocomplete | |
autoFocus
|
boolean
|
Auto focuses the input box | |
redirectUrl
|
string
|
Redirect to search query to a new URL | |
query
|
string
|
Optional, the query displayed on load. Defaults to the query stored in the url (if any). Does not conduct a search. | |
filter
|
object
|
the filter for component to apply on load, defaults to the filter stored in the url (if any). For more information see the filter section of this API documentation |
FilterOptions
Background
The FilterOptions
component allows users to filter their search results using
a list of static, pre-defined filters. Static filters differ from
Facets in that they are defined client-side, which means that they will be
rendered pre-search and are not sensitive to the results of the query. Static filters are applied once a search is conducted.
If you are using static filters differently and need to trigger a new search as soon as a static filter changes, use
FilterOptions
within theFilterBox
component and toggle thesearchOnChange
attribute.
Here’s a basic example:
<div class="filter-container"></div>
ANSWERS.addComponent('FilterOptions', {
container: '.filter-container',
control: 'singleoption',
options: [
{
label: 'Pick Up',
field: 'pickupAndDeliveryServices',
value: "In-Store Pickup"
},
{
label: 'Delivery',
field: 'pickupAndDeliveryServices',
value: "Delivery"
}
]
});
Options
There are two different types of FilterOptions
: STATIC_FILTER
and RADIUS_FILTER
.
STATIC_FILTER
is used for standard filtering on booleans and string fields, whereas
RADIUS_FILTER
allows you to filter results based on their distance from the user.
STATIC_FILTER
STATIC_FILTER
options take few attributes
* field
: Corresponds to the field API name in the knowledge graph
* value
: If the field is a boolean, true
or false
, otherwise the display name of the field option.
* label
: The label to display for this option
* selected
: Whether this option should be selected on load.
If an option is marked as
selected: true
and you’ve configured adefaultInitial
search, it will be applied to the results on load. See the Vertical Search Results guide for more information.
{
options: [
{
// Optional, the label to show next to the filter option.
label: 'Pick Up',
// Required, the api field to filter on, configured on the Yext platform.
field: 'pickupAndDeliveryServices',
// Required, the value for the above field to filter by.
value: "In-Store Pickup",
// Optional, whether this option will be selected on page load. Selected options stored in the url
// take priority over this. Defaults to false.
selected: false
},
{
label: 'Delivery',
field: 'pickupAndDeliveryServices',
value: "Delivery"
}
]
});
Here’s an example:
RADIUS_FILTER
RADIUS_FILTER
takes a value
in meters, in addition to a label
and the selected
boolean.
{
options: [
{
// Required, the value of the radius to apply (in meters). If this value is 0, the SDK will not add explicit radius filtering to the request. The backend may still perform its own filtering depending on the query given.
value: 8046.72,
// Optional, the label to show next to the filter option.
label: '5 miles',
// Optional, whether this option will be selected on page load. Selected options stored in the url
// take priority over this. Defaults to false.
selected: false
},
{
value: 16093.4,
label: "10 miles",
selected: true
},
{
value: 40233.6,
label: "25 miles"
},
{
value: 80467.2,
label: "50 miles"
}
],
}
Here’s an example:
Advanced Configuration
After you’ve specified your options, you can also take advantage of several of the advanced configuration options. Here, we’ve added a reset button, removed the expand/collapse, and hidden all options beyond two behind a “show more!” button.
ANSWERS.addComponent('FilterOptions', {
container: ".filter-options-container",
control: "singleoption",
optionType: "STATIC_FILTER",
showReset: true,
resetLabel: "reset!", //defaults to "reset"
showMore: true,
showMoreLimit: 2,
showMoreLabel: "show more!",
options: [ ... ]
});
FilterOptions API
Property | Type | Default | Description |
---|---|---|---|
control
|
string
|
singleoption
|
singleoption or multioption . singleoption will show a radio control and multioption will show as checkboxes
|
options
|
array
|
The list of options a user can choose from | |
options[].label
|
string
|
Label to show on the filter option | |
options[].field
|
string
|
API Field name (e.g. c_openNow )
|
|
options[].value
|
any
|
The value to the filter the field by | |
showReset
|
boolean
|
Show a reset button | |
storeOnChange
|
boolean
|
Where the filter value is saved on change and sent with the next search. | |
optionSelector
|
string
|
.js-yext-filter-option
|
The selector used for options in the template, defaults to ‘.js-yext-filter-option’ |
showReset
|
boolean
|
Whether to show a reset button | |
resetLabel
|
string
|
Reset
|
The label to use for the reset button |
showMore
|
boolean
|
true
|
WWhether to allow collapsing of excess filter options after a limit |
showMoreLimit
|
number
|
5
|
The max number of filter options to show before collapsing extras |
FilterBox
Background
The FilterBox
component allows users to add multiple static filter components together, including FilterOptions. Configuration on the top-level FilterBox component carries through to the individual filter components. Use FilterBox
instead of individual filter components if:
- A single apply button should impact all filters
- The filters are grouped together visually
- A set of static filter components should share settings
Bug Alert: There’s currently an issue with filterbox’s searchOnChange functionality. For now, setting this to false will simply hide the apply button, but a new search is needed to the filters to be applied.
Here’s a basic example:
this.addComponent("FilterBox", {
container: ".filter-box-container",
searchOnChange: true,
filters: [
{
type: "FilterOptions",
control: "singleoption",
optionType: "STATIC_FILTER",
label: "Services",
options: [
{
label: "Pick Up",
field: "pickupAndDeliveryServices",
value: "In-Store Pickup"
},
{
label: "Delivery",
field: "pickupAndDeliveryServices",
value: "Delivery"
}
]
},
{
control: "singleoption",
type: "FilterOptions",
optionType: "RADIUS_FILTER",
showExpand: false,
label: "Within",
options: [
{
value: 8046.72,
label: "5 miles"
},
{
value: 16093.4,
label: "10 miles"
},
{
value: 40233.6,
label: "25 miles"
},
{
value: 80467.2,
label: "50 miles"
}
]
}
]
});
Advanced Configuration & Example
After you’ve specified the individual filter components, you can also take advantage of several of the configuration options that will apply to all included filter components. Here, we’ve added a reset button per filter, and a reset button for the group of filters. We’ve also updated the showMoreLimit
, which applies to both FilterOptions
components. Finally, we’ve set expand
to false for both FilterOptions
components, but have overridden it for the static FilterOptions
.
FilterBox API
Property | Type | Default | Description |
---|---|---|---|
filters
|
object
|
REQUIRED List of filter component configurations | |
verticalKey
|
string
|
The verticalKey , required if not specified in top level search configuration.
|
|
title
|
string
|
Filters
|
Title to display above the filter components. |
showCount
|
boolean
|
true
|
Show the number of results for each filter |
searchOnChange
|
boolean
|
Execute a new search whenever a filter selection changes. If true, the Apply and Reset buttons will not display. | |
resetFilter
|
boolean
|
Whether to show a reset button per filter | |
resetFilterLabel
|
string
|
Reset
|
The label to use for the reset button per filter |
resetFilters
|
boolean
|
Whether to show a reset button for the group of filters. Defaults to true if searchOnChange is true.
|
|
resetFiltersLabel
|
string
|
Reset
|
The label to use for the reset button for group of filters. |
showMore
|
boolean
|
true
|
Whether to allow collapsing of excess filter options after a limit |
showMoreLimit
|
number
|
5
|
The max number of filter options to show before collapsing extras |
showMoreLabel
|
string
|
show more
|
If showMoreLimit is true, the label to show for displaying more filter options. |
showLessLabel
|
string
|
show less
|
If showMoreLimit is true, the label to show for displaying fewer filter options. |
showNumberApplied
|
boolean
|
Show the number of applied filter when a group is collapsed. | |
expand
|
boolean
|
true
|
Allow expanding and collapsing entire groups of filters |
applyLabel
|
string
|
Apply
|
The label to show on the apply button. Will only display if searchOnChange is false.
|
Facets
Background
The Facets
component allows users to filter their search results using the
facets returned by the Answers API. Facets differ from static
filter components in that they are returned by the server, instead of being
defined by the client. This means that the server will decide which facets
are relevant to a particular query and what their options should be, as well
as how many of the results belong to each facet option.
For this reason, the facet component involves less configuration than other
filter components. Like other filter components, facets can only be used on a
VerticalSearch
page. They can be added to an experience like this:
<div class="facets"></div>
ANSWERS.addComponent('Facets', {
container: '.facets',
verticalKey: 'VERTICAL_KEY',
});
Basic Configuration
There are a few common configuration options you may want to tweak for the facets component. Here is a standard example:
ANSWERS.addComponent('Facets', {
container: '.facets',
verticalKey: 'VERTICAL_KEY',
searchOnChange: true,
showCount: false,
showMoreLimit: 2,
showMoreLabel: 'More',
showLessLabel: 'Less'
});
In this example…
- showCount: false
hides the number of results for each facet option
- searchOnChange: true
causes the search to automatically refresh as soon as
the user selects a facet
- showMoreLimit: 10
adds a “Show More” button to reveal more options when
there are more than 10 facet options
- showMoreLabel and showLessLabel
control the labels of the resulting buttons
The resulting component looks something like this:
Searchable Facets
Some facet fields will have dozens or hundreds of options, so it doesn’t make sense to display them all to the user at once. A better way is to make the facet searchable, which allows the user to search for the particular option they’re looking for. Facets can be made searchable like so:
ANSWERS.addComponent('Facets', {
container: '.facets',
verticalKey: 'VERTICAL_KEY',
searchable: true
});
The result looks like this:
Field-Specific Configuration
Often it doesn’t make sense to apply the same configuration to all facets in
an experience. For example, some facet fields may have hundreds of options and
should be made searchable
, but others might not.
To apply configuration for particular facet fields, use the transformFacets
option. The transformFacets
option of the Facets component allows facets data to be fully customized. The function takes in and returns an array of the answers-core DisplayableFacet which is described here. The function also has access to the Facets config as the second parameter.
Here’s an example of using this option to customize a boolean facet and make it searchable.
transformFacets: (facets, config) => {
console.log(config);
return facets.map((facet) => {
console.log(facet);
const options = facet.options.map((option) => {
let displayName = option.displayName;
if (facet.fieldId === "c_acceptingNewPatients") {
if (option.value === false) {
displayName = "Not Accepting Patients";
}
if (option.value === true) {
displayName = "Accepting Patients";
}
}
return Object.assign({}, option, { displayName });
});
let searchable = false;
if (facet.fieldId === "paymentOptions") {
searchable = true;
}
return Object.assign({}, facet, { options, searchable });
});
}
Facets API
Property | Type | Default | Description |
---|---|---|---|
verticalKey
|
string
|
the vertical for which these facets apply. Required if not included in the top level search configuration. | |
title
|
string
|
Filters
|
Title to display above the facets |
showCount
|
boolean
|
Whether to show the number of results for each facet option | |
searchOnChange
|
boolean
|
Whether to execute a new search whenever a facet selection changes | |
resetFacet
|
boolean
|
Show a reset button per facet group | |
resetFacetLabel
|
boolean
|
reset
|
If resetFacet is true, the label to use for the reset button per facet group
|
resetFacets
|
boolean
|
Show a reset-all button for the facets control. Defaults to true if searchOnChange is false. | |
resetFacetsLabel
|
string
|
reset-all
|
If resetFacets is true, the label to use to reset all.
|
showMore
|
boolean
|
true
|
Whether to allow collapsing of excess facet options after a limit |
showMoreLimit
|
boolean
|
true
|
The max number of filter options to show before collapsing extras |
showMoreLabel
|
string
|
show more
|
If showMore is true, the label for the button to display more facets
|
showLessLabel
|
string
|
show more
|
If showMore is true, the label for the button to display less facets
|
expand
|
boolean
|
true
|
Adds an expand/collapse per facet. |
showNumberApplied
|
boolean
|
true
|
When facet is collapsed, show the number of facet options applied. |
searchable
|
boolean
|
If true, make this facet searchable by displaying the filter option search input | |
placeholderText
|
string
|
Search here...
|
If searchable is true, the placeholder text used for the filter option search input
|
searchLabelText
|
string
|
Search for a filter option
|
The form label text for the search input |
fields
|
object
|
DEPRECATED Field-specific overrides | |
searchable
|
boolean
|
If true, make this facet searchable by displaying the filter option search input | |
placeholderText
|
string
|
If searchable is true, the placeholder text used for the filter option search input
|
|
control
|
string
|
singleopiton
|
Control type, either singleoption or multiopton |
transformFacets
|
function
|
Allows for custom transforms of facets as they return | |
applyLabel
|
string
|
apply
|
If searchOnChange is false, the label to show on the apply button
|
SortOptions
Background
The SortOptions
component shows optional sorting that a user can apply to a set of results. This component is only used on vertical search. SortOptions
can be configured to include sorting by a field, distance (automatically calculated from the entity’s address), and relevance. The default sort is the order specified by the serverside configuration. Notably, any sorting applied client-side via this component does not replace what’s in the serverside configuration, it’s simply layered on top. Learn more about how to fully configure sorting in this Hitchhiker module.
Basic Configuration
There are only a few required configuration options for SortOptions
; the verticalKey
and an options
array. Within the options
array, you must specify the type
of sorting option (either FIELD
, ENTITY_DISTANCE
or RELEVANCE
) and the label
for that sorting option.
<div class="sort-options-container"></div>
ANSWERS.addComponent('SortOptions', {
container: '.sort-options-container',
verticalKey: 'locations',
options: [
{
type: 'FIELD',
field: 'c_popularity',
direction: 'ASC',
label: 'Popularity',
},
{
type: "ENTITY_DISTANCE",
label: 'Distance'
},
{
type: 'RELEVANCE',
label: 'Relevance'
}
],
});
Advanced Configuration
Beyond the basic configuration options, you can also control various settings similar to the FilterOptions and Facets. These include showMore
, showMoreLimit
, showMoreLabel
, showLessLabel
, searchOnChange
and applyLabel
.
Finally, you can control the defaultSortLabel
, which is the label for the option that is selected on load and corresponds to the default sort. You can show a reset button via showReset
, and the label of that button via resetLabel
.
In the below example, we’ve updated the defaultSortLabel
and the label
for sorting. We’ve also added a reset button with the label “reset the sort”.
What Does Ascending and Descending Mean Per Field Type?
Field | ASC | DESC |
---|---|---|
Date |
Oldest to most recent, IE 1/1/1990, 1/1/2020 | Most recent to oldest, IE 1/1/2020, 1/1/1990 |
Number |
Smallest to largest | Largest to smallest |
Boolean |
False, True | True, False |
String |
Special characters, then A-Z | Z-A, then special characters |
SortOptions API
Property | Type | Default | Description |
---|---|---|---|
verticalKey
|
string
|
REQUIRED The vertical key for this vertical. | |
options
|
array
|
REQUIRED Array of sorting objects. Each object contains type, label, field and direction. | |
type
|
string
|
REQUIRED
The type of sorting option. Can be either FIELD (on a field),ENTITY_DISTANCE (distance), or RELEVANCE (relevance determined by answers algorithm and any sorting logic in the serverside configuration, same as the default sort order).
|
|
label
|
string
|
REQUIRED The label for this sort option | |
field
|
string
|
Required if type is FIELD . The API name of the field on which to sort.
|
|
direction
|
string
|
Required if type is FIELD . Can be either ASC (ascending) or DESC (descending). The direction in which to sort.
|
|
defaultSortLabel
|
string
|
Best Match
|
The label used for the “default” sort (determined by answers algorithm and any sorting logic in the serverside configuration), defaults to ‘Best Match’ |
optionSelector
|
array
|
.yxt-SortOptions-optionSelector
|
The selector used for options in the sort options template. |
searchOnChange
|
boolean
|
Whether or not changing an option triggers a search immediatley. If false the component also renders an apply button that applies the sort. If true, no apply button is shown. | |
showReset
|
boolean
|
Whether or not to show a “reset” in the component. If true, clicking reset will set the component back to the default label. | |
resetLabel
|
string
|
reset
|
The label to use for the reset button. |
showMore
|
boolean
|
true
|
Allow collapsing excess filter options after a limit, defaults to true. Note, screen readers will not read options hidden by this flag, without clicking show more first. |
showMoreLimit
|
string
|
5
|
The max number of filter options to show before collapsing extras. |
showMoreLabel
|
string
|
Show more
|
The label to show for displaying more options |
showLessLabel
|
string
|
Show less
|
The label to show for displaying fewer options. |
onChange
|
function
|
function() => {}
|
The callback function to call when changed. Runs before a search is triggered if searchOnChange is true.
|
label
|
string
|
Sorting
|
The label to be used in the legend above the sort options |
applyLabel
|
string
|
Apply
|
The label to be used on the apply button. Only appears if searchOnChange is false.
|
QASubmission
Background
To fully set up this module, we recommend going through our Hitchhiker Training, which covers how to add create an organization entity, enable the Q&A feature and answer questions.
QASubmission is a form that appears at the bottom of the Universal Search Page or vertical search page. It allows a user to submit a question, along with their name and email. If a user is unable to find what s/he is looking for, they can submit a question.
When a user submits a question, it creates an instance of first party Q&A within Yext. This is available in the listings tab, under Q&A. You can then answer questions within Yext. The answer is sent to the end user’s email (inputted when s/he submitted a question).
Basic Configuration
There are only two required attributes to add QASubmission – a privacyPolicyUrl
and an entityId
for the corresponding organization entityId
.
<div class="question-submission-container"></div>
ANSWERS.addComponent('QASubmission', {
container: ".question-submission-container",
entityId: 'org-1',
privacyPolicyUrl: 'https://mybiz.com/policy',
})
Example
In this more advanced example, we’ve updated the teaser
, the description
and the buttonLabel
.
QASubmission API
Property | Type | Default | Description |
---|---|---|---|
entityId
|
string
|
REQUIRED the Entity ID of the organization entity in the Knowledge Graph. | |
privacyPolicyUrl
|
string
|
REQUIRED URL for the privacy policy link. | |
formSelector
|
string
|
Native form node within container
|
Optional, the form selector to use in the component. |
sectionTitle
|
string
|
Ask a Question
|
Optional, title displayed in the heading for the form. |
teaser
|
string
|
Can't find what you’re looking for? Ask a question below.
|
Teaser displayed for the form, next to the title. |
expanded
|
boolean
|
true
|
whether or not the form is expanded by default when a user arrives on the page. |
description
|
string
|
Enter your question and contact information, and we'll get back to you with a response shortly.
|
Description that displays above the name and email, once the form is expanded |
nameLabel
|
string
|
Name
|
Optional, label for name input |
emailLabel
|
string
|
Email
|
Optional, label for email input. |
questionLabel
|
string
|
Question
|
Optional, label for question input. |
privacyPolicyText
|
string
|
By submitting my email address, I consent to being contacted via email at the address provided.
|
Text before the privacy policy link. |
privacyPolicyUrlLabel
|
string
|
Learn more here.
|
Display text for the privacy policy url. |
privacyPolicyErrorText
|
string
|
You must agree to the privacy policy to submit feedback.
|
Error message displayed when the privacy policy is not selected. |
emailFormatErrorText
|
string
|
Please enter a valid email address.
|
Error message displayed when an invalid email is not submitted |
requiredInputPlaceholder
|
string
|
(required)
|
Placeholder displayed in all required fields |
buttonLabel
|
string
|
Submit
|
Label displayed on the button to submit a question. |
questionSubmissionConfirmationText
|
string
|
Thank you for your question!
|
Confirmation displayed once a question is submitted. |
networkErrorText
|
string
|
We're sorry, an error occurred.
|
Error message displayed when there is an issue with the QA Submission request. |
Map
Background
The map component is displayed on both universal and vertical search pages. It’s added on a Vertical Search Page, but included as a subcomponent on a Universal Page, within Universal Results on a Universal Search Page.
Basic Configuration
The basic config for a map requires the apiKey
and the mapProvider
. All other configuration is optional. We also recommend adding the no results configuration, assuming you are displaying all results as outlined here
<div class="map-container"></div>
ANSWERS.addComponent('Map', {
container: '.map-container',
mapProvider: 'mapBox',
apiKey: '12345',
noResults: {
displayAllResults: false,
visible: false
},
});
Hooks
The Map component provides a few different hooks, where you can provide functions to trigger custom behavior.
Pin Hooks
The relevant item is passed to the call back
- onPinClick
fires when a pin is clicked
- onPinMouseOver
fires when a pin is hovered over.
- onPinMouseOut
fires when a pin is no longer hovered.
JS Hooks
onLoaded
fires when the JS for the map is loaded.
Example
In the below example, we fire various alerts when pins are clicked or hovered.
ANSWERS.addComponent('Map', {
container: '.map-container',
mapProvider: 'mapBox',
apiKey: '12345',
onPinClick: function (entity) { console.log("clicked " + JSON.stringify(entity));}
onPinMouseOver: function (entity) { console.log("hovering on " + JSON.stringify(entity));}
onPinMouseOut: function (entity) { console.log("leaving " + JSON.stringify(entity));}
onLoaded: function () { console.log("Loaded JS")},
});
Customizing the Pin
The Map
component also accepts a pin
function, that allows you to create an entirely custom pin if you’re using Google Maps. Said function should return an object with the following properities:
{
svg: null, //if you're using an svg
url: null, //if you're using an image hosted elsewhere
anchor: null, // e.g. { x: 1, y: 1 }, offsets the pin
scaledSize: null // e.g. { w: 20, h: 20 }, alters the size
};
Here’s an example using a custom svg (the pin label will automatically be added to the svg):
ANSWERS.addComponent('Map', {
container: '.map-container',
mapProvider: 'mapBox',
apiKey: '12345',
pin: function () {
return {
svg: `<svg
xmlns="http://www.w3.org/2000/svg"
width="20px"
height="20px"
viewBox="0 -1 22 28"
>
<g fill="none" fill-rule="evenodd">
<path
fill="#848484"
fill-rule="nonzero"
stroke="#848484"
d="M0 10.767c0 5.563 7.196 12.418 9.947 14.836.6.527 1.509.53 2.112.005C14.815 23.212 22 16.423 22 10.767 22 4.82 17.075 0 11 0S0 4.82 0 10.767"
/>
<text
fill="#848484"
font-family="Arial-BoldMT,Arial"
font-size="12"
font-weight="bold"
>
<tspan x="50%" y="15" text-anchor="middle">
</tspan>
</text>
</g>
</svg>`
};
}
});
Example
Map API
Property | Type | Default | Description |
---|---|---|---|
mapProvider
|
string
|
REQUIRED
Supported map providers include google or mapBox , not case-sensitive
|
|
apiKey
|
string
|
REQUIRED
The API Key used for interacting with the map provider, except for Google Maps if provided clientId
|
|
clientId
|
string
|
Can be used for Google Maps in place of the API key | |
collapsePins
|
boolean
|
Determines whether or not to collapse pins at the same lat/lng | |
zoom
|
number
|
14
|
the zoom level of the map |
defaultPosition
|
object
|
{lat: 37.0902, long: -95.7129}
|
The default coordinates to display if showEmptyMap is set to true. Takes an object with two properties, lat and lng .
|
showEmptyMap
|
boolean
|
Determines if an empty map should be shown on the initial page load. Also used in no results if specific no results config for the map is not provided. | |
onPinClick
|
function
|
Callback to invoke when a pin is clicked. The clicked item(s) are passed to the callback. | |
onPinMouseOver
|
function
|
Callback to invoke when a pin is hovered. The hovered item(s) are passed to the callback. | |
onPinMouseOut
|
function
|
Callback to invoke when a pin is no longer hovered after being hovered. The clicked item(s) are passed to the callback | |
onLoaded
|
function
|
Callback to invoke once the JavaScript is loaded | |
noResults
|
object
|
||
displayAllResults
|
boolean
|
Whether to display map pins for all possible results when no results are found. | |
visible
|
boolean
|
Whether to display the map when no results are found, taking priority over showEmptyMap. If unset, a map will be visible if showEmptyMap is true OR if displayAllResults is true and alternative results are returned. | |
pin
|
function
|
Custom configuration override to use for the map markers. Should return an object. See here |
Pagination
Background
The Pagination
component is used on Vertical Search to allow users to navigate to other pages.
The default page size is 20 results, and pagination is used to help users navigate to results
that are not on the first page.
Default Styling
Here is the default styling for the pagination component:
Basic Configuration
Here is a basic example of adding pagination to a vertical results page:
<div class="pagination"></div>
ANSWERS.addComponent("Pagination", {
container: ".pagination",
});
Configuring Page Size
By default, the page size is set to 20. To override the page size, edit the limit
property on the search
config in the init
. Here is an example of chaning the page size to 50:
ANSWERS.init({
// API Key, Experience Key, Business ID etc. go here
search: {
verticalKey: "advisors",
limit: 50,
defaultInitialSearch: "",
},
});
Hiding First and Last Page
By default, the pagination component will always show a button to easily navigate to the first or last page. This is represented by a double arrow in the search results.
To disable this use the showFirstAndLastButton
property. Here is an example or hiding first and last page controls:
ANSWERS.addComponent("Pagination", {
container: ".pagination",
showFirstAndLastButton: false
});
Show Multiple Pages
By default, one page is shown at a time. To show multiple page numbers (and allow a user to jump to a specific page), configure maxVisiblePagesMobile
and maxVisiblePagesDesktop
. The pageLabel
can also be removed.
ANSWERS.addComponent("Pagination", {
container: ".pagination",
maxVisiblePagesMobile: 5,
maxVisiblePagesDesktop: 1,
pageLabel: ""
});
Pin First and Last Page Number
A user might want to see how many pages are available when conducting a query, especially for those verticals that lend themselves to high-result queries. It’s not recommended to use this configuration option with showFirstAndLastButton
, since the frontend behavior is redundant.
To pin the first and last page, set the pinFirstAndLastPage
boolean to true
.
ANSWERS.addComponent("Pagination", {
container: ".pagination",
maxVisiblePagesMobile: 3,
maxVisiblePagesDesktop: 10,
pageLabel: ""
pinFirstAndLastPage: true,
});
Trigger Behavior On Page Change
The is a function invoked when a user clicks to change pages. It accepts the “destination” page number (newPageNumber
), the previous page number (oldPageNumber
), and the total number of pages (totalPages
).
ANSWERS.addComponent("Pagination", {
container: ".pagination",
maxVisiblePagesMobile: 3,
maxVisiblePagesDesktop: 10,
pageLabel: ""
pinFirstAndLastPage: true,
onPaginate: function(newPageNumber, oldPageNumber, totalPages) {
console.log(
"I'm going from " + oldPageNumber + " to " + newPageNumber +
" and there are " + totalPages + " pages.")
// Logs the following when paginating from page 1 to 6:
// "I'm going from 1 to 6 and there are 35 pages."
}
});
Example
Here’s a fully working example:
Pagination API
Property | Type | Default | Description |
---|---|---|---|
verticalKey
|
string
|
The verticalKey for this vertical. Required if not included in the top level search configuration.
|
|
maxVisiblePagesDesktop
|
number
|
1
|
The maximum number of pages visible to users above the mobile breakpoint. |
maxVisiblePagesMobile
|
number
|
1
|
The maximum number of pages visible to users below the mobile breakpoint. |
pinFirstAndLastPage
|
boolean
|
ensure that the page numbers for first and last page are always shown. Not recommended to use with showFirstAndLastButton. Defaults to false | |
showFirstAndLastButton
|
boolean
|
true
|
Optional, display double-arrows allowing users to jump to the first and last page of results. |
pageLabel
|
string
|
Page
|
Label for a page of results |
noResults
|
object
|
Configuration of no results | |
visible
|
boolean
|
Whether pagination should be visible when displaying no results. | |
onPaginate
|
function
|
document.documentElement.scrollTop = 0; document.body.scrollTop = 0;
|
Function invoked when a user clicks to change pages. Default scrolls user to the top of the page. (newPageNumber, oldPageNumber, totalPages) => {}
|
showFirst - DEPRECATED
|
boolean
|
true
|
Display a double arrow allowing users to jump to the first page of results. DEPRECATED, please use showFirstAndLastButton instead.
|
showLast - DEPRECATED
|
boolean
|
true
|
Display a double arrow allowing users to jump to the last page of results. DEPRECATED, please use showFirstAndLastButton instead.
|
LocationBias
Background
The location bias shows the user where we think they are located. The purpose of this component is to inform the user as well as to allow them to opt in for HTML5 geolocation. It is recommended to display this at the bottom of any search results page.
Note: If you want to allow a user to select a location check out the
GeoLocationFilter
component here.
Default Styling
If we are using IP address to locate the user, the component looks something like this:
If the user has opted in to HTML5 Gelocation, then the component looks something like this:
Basic Configuration
The only required option is verticalKey
when this is used on Vertical Search Results Page (if not included in top level search configuration).
<div class="location-bias-container"></div>
ANSWERS.addComponent("LocationBias", {
container: ".location-bias-container",
// verticalKey: "VERTICAL_KEY"
});
Advanced Configuration
HTML5 Geolocation
Similar to the SearchBar component, you can adjust the options passed to the Geolocation API.
You can reduce the timeout (geolocationOptions.timeout
) or increase the maximum age for a geolocation request (geolocationOptions.maximumAge
) and add an optional alert if the request fails (geolocationTimeoutAlert
).
Additionally, if the accuracy of geolocation accuracy is particularly important, you can enable “high accuracy”, which will provide more accurate geolocation results at the expense of response time and/or device power consumption.
ANSWERS.addComponent("LocationBias", {
container: ".location-bias-container",
geolocationOptions: {
timeout: 500,
maximumAge: 300000,
enableHighAccuracy: false
},
geolocationTimeoutAlert: {
enabled: true,
message: "We are unable to determine your location"
}
});
Overriding Strings
You can also override the default strings used in the component using the following attributes:
- ipAccuracyHelpText
: Text displayed when geolocation fails and IP address is used to determine location
- deviceAccuracyHelpText
: Text displayed when geolocation succeeds and HTML5 is used to determine location.
- updateLocationButtonText
: Button text used to re-request location information.
ANSWERS.addComponent("LocationBias", {
container: ".location-bias-container",
ipAccuracyHelpText: 'based on your internet address',
deviceAccuracyHelpText: 'based on your device',
updateLocationButtonText: 'Update your location',
});
Example
LocationBias API
Property | Type | Default | Description |
---|---|---|---|
verticalKey
|
string
|
If used on Vertical Search Page, the verticalKey for the vertical. Required if not included in top level search configuration.
|
|
updateLocationEl
|
string
|
.js-locationBias-update-location
|
Selector for button a user clicks to update their location |
ipAccuracyHelpText
|
string
|
based on your internet address
|
Text displayed when geolocation fails, and IP address is used to determine location. |
deviceAccuracyHelpText
|
string
|
based on your device
|
Text displayed when geolocation succeeds, and HTML5 is used to determine location. |
updateLocationButtonText
|
string
|
Update your location
|
Label of the button to update your location |
geolocationOptions
|
object
|
Geolocation-related configuration. See Geolocation Documentation for more information. | |
enableHighAccuracy
|
boolean
|
Whether to improve accuracy at the cost of response time and/or power consumption, defaults to false. | |
timeout
|
number
|
6000
|
The maximum amount of time (in ms) a geolocation call is allowed to take before defaulting, defaults to 1 second. |
maximumAge
|
number
|
300000
|
The maximum amount of time (in ms) to cache a geolocation call, defaults to 5 minutes. |
geolocationTimeoutAlert
|
object
|
Configuration for a native alert, displayed when a call to the geolocation API fails. | |
enabled
|
boolean
|
Whether to display a window.alert() on the page | |
message
|
string
|
We are unable to determine your location
|
If enabled is true, the message in the alert.
|
Icon
Background
The icon component is typically created by other components, but it can also be used as a standalone component.
Basic Configuration
<div class='icon-container'></div>
ANSWERS.addComponent('IconComponent', {
container: '.icon-container',
iconName: 'pantheon',
});
Built-In Icons
The following is a list of names for the icons that are supported by default. You can reference these names when a component has an attribute to set the icon (this includes the search bar, universal results and no results).
Name | Icon |
---|---|
briefcase | |
calendar | |
callout | |
chevron | |
close | |
directions | |
document | |
elements | |
gear | |
info | |
kabob | |
light_bulb | |
link | |
magnifying_glass | |
mic | |
office | |
pantheon | |
person | |
phone | |
pin | |
receipt | |
star | |
support | |
tag | |
thumb | |
window |
Icon API
Property | Type | Default | Description |
---|---|---|---|
iconName
|
string
|
Use one of the predefined icons in the Answers Search UI Library | |
iconUrl
|
string
|
Sets the icon to reference an image URL. Overrides icon name. | |
classNames
|
string
|
Adds class names to the icon. Multiple classnames should be space-delimited. |