Skip to content

Extend ​

Subclass a shipped element to change what it renders or how it behaves. The subclass needs a tag of its own, since the package defines the shipped tag. To write a new component, subclass BaseSearchElement, which receives the search context and the search methods.

ts
import {SearchResults} from 'drupal-search-web-components/dist/search-results/search-results.js';

customElements.define(
  'my-results',
  class extends SearchResults {
    override _getResults() {
      // Your markup here.
    }
  }
);

On a Drupal page, the subclass takes the class from window.SearchWebComponents instead of importing it. For the library and the template, see A subclass on a Drupal page.

Hooks and methods ​

  • Hooks. Methods with a leading underscore, e.g., _getResults() and _getOptions(), are the hooks a subclass overrides to change what an element renders or does.
  • Methods. Methods without an underscore are the ones a page or a subclass calls, e.g., applyFacet() and getResults(), and the data methods of DropdownMixin, e.g., getAllOptions() and getOptionLabel().

Each component's Storybook page lists its hooks and its methods under Hooks and methods, inherited ones included, with what each returns and its signature. Three groups of hooks are shared:

  • Focus hooks, _getFocusedControlKey() and _getFocusTargetAfterRender(), on every component except search-root and search-box. For where focus goes, see Focus.
  • Facet hooks, on the four facet elements, e.g., _getFacetData() and _configOrAttribute().
  • Dropdown hooks, on facet-dropdown-html, and on search-sort and search-results-per-page with type="html".

For the request hooks of search-root, see Searching another API.

Defaults in a subclass ​

To give a property a different default in a subclass, set it in the constructor:

ts
constructor() {
  super();
  this.labelText = 'Order by';
}

Do not declare it as a class field, e.g., labelText = 'Order by';. A class field over a reactive property hides the property, and the element then ignores later changes to the property, from an attribute or from code.

This search-sort subclass sets its label in the constructor. It marks the default option in _getSelectOptions(), copied from SearchSort with one change, to the option text: ${item.label}${item.default ? ' (default)' : ''}.

ts
import {html} from 'lit';
import {repeat} from 'lit/directives/repeat.js';
import {SearchSort} from 'drupal-search-web-components/dist/search-sort/search-sort.js';

class RecipeSort extends SearchSort {
  constructor() {
    super();
    this.labelText = 'Order by';
  }

  override _getSelectOptions() {
    const s = this.getOptionValue(this._getSelected());

    return html`${repeat(
      this.sorts,
      (item) => this.getOptionValue(item),
      (item) => {
        const v = this.getOptionValue(item);
        return html`<option value=${v} ?selected=${v === s}>
          ${item.label}${item.default ? ' (default)' : ''}
        </option>`;
      }
    )}`;
  }
}
customElements.define('recipe-sort', RecipeSort);

Custom facet options ​

Focus after a pick ​

After the search, the facet puts focus back on the control a keyboard user used: the same option, or the first option after the reset button. The shipped focus logic finds the control by its markup, so a subclass that renders its own options keeps it when:

  • each option is an input or a button inside an li, with the option's key in its value attribute
  • the reset button is a button with the class reset.

A subclass with other markup, e.g., options rendered as links, overrides the two focus hooks instead: _getFocusedControlKey(control) returns a key for the control the visitor used, and _getFocusTargetAfterRender(key) returns the control to focus for that key once the facet has rendered. For where focus goes on each component, see Focus.

Applying a value ​

An option applies its value with applyFacet(this.urlAlias, value). urlAlias is the facet's URL alias, from settings.url_alias in the response. The URL alias names the facet in the page URL. urlAlias is a property, not an attribute. The facet's key attribute is its machine name, which is a different value. For the difference, see What "key" means in code.

Writing to the context ​

A component can change view state by publishing a changed context with updateContext(context). The root merges it:

  • resultDisplay, dialogOpen, dialogBreakpoint, and thirdPartySettings are a component's to write. thirdPartySettings holds a site's own state.
  • loading, response, and responseReady from a component are ignored.
  • query from a component is merged, but only until the next search, which sets its own query.
  • url, additionalParams, defaultPerPage, and updateUrl are set from the root's attributes for every search, so a component's value lasts until the next search.

To change the URL state, ask for a search with getResults(query).

Custom result elements ​

search-results renders each result with the element its mappings name, from the mappings attribute or from swc_results in the response. For the shape of a mapping, see The mappings shape.

A mapping's keys hold values of the match field, which resultField names. For each result, the first key that matches is used. The keys are checked in this order:

CheckedKeyMatchesExample
1The result's value and the listing layout shownResults with that value, when results are shown as that listing layoutrecipe-grid
2default and the listing layout shownResults with any value, when results are shown as that listing layoutdefault-grid
3The result's valueResults with that valuerecipe
4defaultResults with any valuedefault

A result with no matching mapping is not shown. The browser console logs a warning for it. A mapping whose element is not defined on the page shows nothing either.

The element gets the result as its data property and the mapping's settings as its settings property. The element can be a plain HTMLElement, as in the Quickstart, or a subclass of BaseSearchElement to read the search context too.

Here the search for curry shows recipes as recipe-card-result, a bordered card with the summary, and the article as title-link-result, a link. Switch to Grid to see every result as recipe-card-result: