Direct Answer | Yext Hitchhikers Platform
Background
The DirectAnswer
component shows a Direct Answer to a query. It is usually shown above the UniversalResults
component. Direct Answers 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 inferred 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.
Direct Answer 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 |