loading

Universal Search Results Page

A Universal Search Results Page combines results from multiple verticals into a set of results. You can read more about each individual component referenced here in the Components section.

Component Description
SearchBar Where the user enters their Search
Navigation Navigate between Universal and Vertical Search
SpellCheck Show UI to correct user’s spelling
DirectAnswer Show a direct answer if one is found
UniversalResults Show the results
LocationBias Show where the search was run from

Code

Putting these components together the page looks like this:

<body>
  <div class="answers-layout">
    <div class="searchbar-container"></div>
    <div class="navigation-container"></div>
    <div class="spellcheck-container"></div>
    <div class="universal-container"></div>
    <div class="directanswer-container"></div>
    <div class="locationbias-container"></div>
  </div>

  <script>
    ANSWERS.init({
      apiKey: "3517add824e992916861b76e456724d9",
      experienceKey: "answers-js-docs",
      businessId: "3215760",
      experienceVersion: "PRODUCTION",

      onReady: function () {
        // init components
        this.addComponent("SearchBar", {
          container: ".searchbar-container",
          allowEmptySearch: true
        });
        this.addComponent("Navigation", {
          container: ".navigation-container",
        });
        this.addComponent("SpellCheck", {
          container: ".spellcheck-container",
        });
        this.addComponent("DirectAnswer", {
          container: ".directanswer-container",
        });
        this.addComponent("UniversalResults", {
          container: ".universal-container",
          config: {
            locations: {
              icon: "pin",
              card: {
                cardType: "Standard",
                dataMappings: {
                  title: (item) => item.name,
                  subtitle: (item) => item.address.line1,
                  details: (item) => item.description,
                  image: (item) =>
                    item.photoGallery ? item.photoGallery[0].image.url : null,
                  url: "#"
                },
                callsToAction: [
                  {
                    label: "Get Directions",
                    icon: "directions",
                    url: "#",
                    analyticsEventType: "GET_DIRECTIONS",
                    target: "_self"
                  },
                  {
                    label: "Call",
                    icon: "phone",
                    url: (item) => `tel:${item.mainPhone}`,
                    analyticsEventType: "TAP_TO_CALL"
                  }
                ]
              },
              appliedFilters: {
                show: true,
                showFieldNames: true,
                hiddenFields: ["builtin.entityType"],
                delimiter: "|",
                labelText: "Filters applied to this search:",
                removableLabelText: "Remove this filter"
              }
            },
            faqs: {
              icon: "support",
              title: "FAQs",
              card: {
                cardType: "Accordion",
                dataMappings: {
                  title: (item) => item.name,
                  details: (item) => item.answer,
                  url: "#"
                },
                callsToAction: [
                  {
                    label: "Learn More",
                    icon: "",
                    url: "#",
                    analyticsEventType: "CTA_CLICK",
                    target: "_self"
                  }
                ]
              }
            },
            jobs: {
              icon: "briefcase",
              card: {
                cardType: "Standard",
                dataMappings: {
                  title: (item) => item.name,
                  subtitle: (item) => `Date Posted: ${item.datePosted}`,
                  details: (item) => item.description,
                  url: "#"
                },
                callsToAction: [
                  {
                    label: "Learn More",
                    icon: "info",
                    url: "#",
                    analyticsEventType: "CTA_CLICK",
                    target: "_self"
                  }
                ]
              }
            }
          }
        });
        this.addComponent("LocationBias", {
          container: ".locationbias-container",
        });
      }
    });
  </script>
</body>

Example

Here is a fully working example:

Vertical Search Results Page

A Vertical Search Results Page displays results for a single vertical. You can read more about each individual component referenced here in the Components section.

light bulb
Tip
All the components you add here should be on a new, separate HTML page from the Universal Search Results Page.
Component Description
SearchBar Where the user enters their Search
Navigation Navigate between Universal and Vertical Search using tabs
SpellCheck Show UI to correct user’s spelling
Facets Show a list of facets to help the user filter
Pagination Show pagination controls the help the user navigate to subsequent pages
VerticalResults Show the results for the vertical
LocationBias Show where the search was run from

Example Code

Putting these components together the page looks like this:

<body>
  <div class="answers-layout">
    <div class="searchbar-layout">
      <div class="searchbar-container"></div>
      <div class="navigation-container"></div>
    </div>
    <div class="filters-and-results-layout">
      <div class="facets-container"></div>
      <div class="results-layout">
        <div class="spellcheck-container"></div>
        <div class="vertical-container"></div>
        <div class="locationbias-container"></div>
      </div>
    </div>
  </div>

  <script>
    ANSWERS.init({
      apiKey: "3517add824e992916861b76e456724d9",
      experienceKey: "answers-js-docs",
      businessId: "3215760",
      experienceVersion: "PRODUCTION",
      verticalKey: "locations",
      search: {
        verticalKey: "locations",
        defaultInitialSearch:
          "locations that take mastercard and offer free wifi",
        limit: 20
      },
      verticalPages: [
        {
          label: "All",
          url: "/index.html",
          isFirst: true
        },
        {
          verticalKey: "locations",
          label: "Locations",
          url: "/locations.html",
          isActive: true
        },
        {
          verticalKey: "jobs",
          label: "Jobs",
          url: "/jobs.html"
        },
        {
          verticalKey: "faqs",
          label: "FAQs",
          url: "/faqs.html"
        }
      ],
      onReady: function () {
        // init components
        this.addComponent("SearchBar", {
          container: ".searchbar-container",
          allowEmptySearch: true,
          verticalKey: "locations"
        });
        this.addComponent("Navigation", {
          container: ".navigation-container"
        });
        this.addComponent("SpellCheck", {
          container: ".spellcheck-container"
        });
        this.addComponent("Facets", {
          container: ".facets-container",
          verticalKey: "locations",
          searchOnChange: true
        });
        this.addComponent("VerticalResults", {
          container: ".vertical-container",
          verticalKey: "locations",
          card: {
            cardType: "Standard",
            dataMappings: {
              title: (item) => item.name,
              subtitle: (item) => item.address.line1,
              details: (item) => item.description,
              image: (item) =>
                item.photoGallery ? item.photoGallery[0].image.url : null,
              url: "#"
            },

            callsToAction: [
              {
                label: "Get Directions",
                icon: "directions",
                url: "#",
                analyticsEventType: "GET_DIRECTIONS",
                target: "_self"
              },
              {
                label: "Call",
                icon: "phone",
                url: (item) => `tel:${item.mainPhone}`,
                analyticsEventType: "TAP_TO_CALL"
              }
            ]
          },
          noResults: {
            displayAllResults: true
          },
          appliedFilters: {
            show: true,
            removable: true
          }
        });
        this.addComponent("LocationBias", {
          container: ".locationbias-container"
        });
      }
    });
  </script>
</body>

UX Recommendations

Vertical Result pages can come in all shapes and sizes. Here are some recommendations to consider when designing a Vertical Search Results page.

Show all results on load

Generally, you want to display ALL results for a vertical as soon as the page loads. To to this:

  • Set allowEmptySearch: true on the SearchBar component
  • Set defaultInitialSearch: "" on the search config

Display ALL Results on No Results

You never want your user to hit a dead end. To help solve this, you can easily display ALL results if the user would see no results. To do this set displayAllResults in the noResults configuration option in the VerticalSearch component.

For example:

noResults: {
	displayAllResults: true,
}

To learn more about no results check out the Vertical No Results Guide.

Example

Here is a fully working example:

No Results

With version 0.13.1, you can show all results when no results return, and link to other verticals that return results.

Basic Template

In version 0.13.1 and up, you can specify noResults in your verticalResults component:

ANSWERS.addComponent("VerticalResults", {
  container: ".vertical-results-container",
  card: {
    cardType: "Standard",
    dataMappings: () => {},
    callsToAction: () => [],
  },
  noResults: {
     displayAllResults: true, //controls whether all results appear below. defaults to false if not specified.  
  }
});

Overwriting the Template

It’s very easy to override the no results template. Note: We did this because the logic is a little bit complicated (like iterating through the list of all alternative verticals, and deciding when to use plural vs. singular for “result” and “category”). We wanted you to still have access to those fancy things, even if you just wanted to customize part of the string.

You have access to the following Handlebars variables in the no results template:

  • universalUrl- The url for universal search
  • currentVerticalLabel - The current vertical
  • query - The query that was created
  • resultsCount - Results count for all results
  • verticalSuggestions - The set of all alternative verticals that returned results

You can see these in the following code sandbox: https://codesandbox.io/embed/blazing-breeze-uj9m7?fontsize=14&hidenavigation=1&module=%2Ftemplates.js&theme=dark

ANSWERS.addComponent("VerticalResults", {
  container: ".vertical-results-container",
  card: {
    cardType: "Standard",
    dataMappings: () => {},
    callsToAction: () => [],
  },
  noResults: {
     displayAllResults: true, //controls whether all results appear below. defaults to false if not specified.  
     template:  `
      <div class="yxt-AlternativeVerticals{{#unless isShowingResults}} yxt-AlternativeVerticals--notShowingResults{{/unless}}">
      <div class="yxt-AlternativeVerticals-noResultsInfo">
        <em class="yxt-AlternativeVerticals-noResultsInfo--emphasized">
          No results found
        </em>
        in {{currentVerticalLabel}}.
        {{#if isShowingResults}}Showing<em class="yxt-AlternativeVerticals-noResultsInfo--emphasized">all {{currentVerticalLabel}}</em>instead.{{/if}}
      </div>
      {{#if verticalSuggestions}}
        <div class="yxt-AlternativeVerticals-suggestionsWrapper">
          <div class="yxt-AlternativeVerticals-details">
            The following search
            {{plural verticalSuggestions.length 'category' 'categories'}}
            yielded results
            {{#if query}}
              for
              <span class="yxt-AlternativeVerticals-details--query">
                "{{query}}"</span>:
            {{/if}}
          </div>
          <ul class="yxt-AlternativeVerticals-suggestionsList">
            {{#each verticalSuggestions}}
              <li class="yxt-AlternativeVerticals-suggestion">
                <a class="yxt-AlternativeVerticals-suggestionLink"
                    href="{{url}}">
                  {{#if iconName}}
                    <div class="yxt-AlternativeVerticals-verticalIconWrapper"
                          data-component="IconComponent"
                          data-opts='{
                            "iconName": "{{iconName}}"
                          }'>
                    </div>
                  {{/if}}
                  <div class="yxt-AlternativeVerticals-suggestionLink--copy">
                    <span class="yxt-AlternativeVerticals-suggestionLink--copyLabel">
                      {{label}}
                    </span>
                    <span class="yxt-AlternativeVerticals-suggestionLink--copyResults">
                      ({{resultsCount}} {{plural resultsCount 'result' 'results'}})
                    </span>
                  </div>
                  <div class="yxt-AlternativeVerticals-arrowIconWrapper"
                        data-component="IconComponent"
                        data-opts='{
                          "iconName": "chevron"
                        }'>
                  </div>
                </a>
              </li>
            {{/each}}
          </ul>
          {{#if universalUrl}}
            <div class="yxt-AlternativeVerticals-universalDetails">
              Alternatively, you can
              <a class="yxt-AlternativeVerticals-universalLink"
                 href={{universalUrl}}>
                view results across all search categories</a>.
            </div>
          {{/if}}
        </div>
      {{/if}}
    </div>
      `
  }
});

FAQs

1. Can this apply to universal?

Not yet but this is on the roadmap. – the construct is going to be a little different.

2. How does this work for third party backends?

Third party backends do not get the no results functionality.

3. How do I change the color of the background or the styling?

Overwrite any of the scss here: https://github.com/yext/answers/blob/master/src/ui/sass/modules/_AlternativeVerticals.scss