CIF Live Search Componente De Póver De live-search-popover

La ventana emergente Live Search es el elemento que contiene los resultados Live Search mientras escribe en el campo de búsqueda.
AEM En este tema se describe cómo integrar este componente en el sitio de.

Estructura de archivos file-strucure

CIF Para habilitar el componente de, los archivos deben editarse y crearse.

  • ui.apps/src/main/content/jcr_root/apps/venia/components/commerce/searchbar/clientlibs/.content.xml

    Crear el archivo .content.xml:

    code language-xml
    <?xml version="1.0" encoding="UTF-8"?>
    <jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
      jcr:primaryType="cq:ClientLibraryFolder"
      allowProxy="{Boolean}true"
      categories="[venia.cif]"
      jsProcessor="[default:none,min:none]"/>
    
  • ui.apps/src/main/content/jcr_root/apps/venia/components/commerce/searchbar/clientlibs/css.txt

    Crear el archivo css.txt:

    code language-text
    #base=css
    
    searchbar.css
    
  • ui.apps/src/main/content/jcr_root/apps/venia/components/commerce/searchbar/clientlibs/css/searchbar.css

    Crear el archivo searchbar.css:

    code language-css
    .searchbar__root .action.search:before {
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      font-size: 16px;
      line-height: 32px;
      color: #757575;
      /* content: "\e615"; */
      font-family: "luma-icons";
      margin: 0;
      vertical-align: top;
      display: inline-block;
      font-weight: normal;
      overflow: hidden;
      speak: none;
      text-align: center;
    }
    .searchbar__label {
      display: none;
    }
    
    .searchbar__root .action.search > span {
      border: 0;
      clip: rect(0, 0, 0, 0);
      height: 1px;
      margin: -1px;
      overflow: hidden;
      padding: 0;
      position: absolute;
      width: 1px;
    }
    input.searchbar__input::placeholder {
      font-size: 14px;
    }
    input.searchbar__input {
      background: #fff;
      background-clip: padding-box;
      border: 1px solid #c2c2c2;
      border-radius: 1px;
      font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
      font-size: 16px;
      height: 32px;
      line-height: 1.42857143;
      padding: 0 9px;
      vertical-align: baseline;
      width: 100%;
      max-width: 200px;
      box-sizing: border-box;
      cursor: text;
    }
    .search-autocomplete {
      position: absolute;
    }
    div.searchbar {
      width: 100% !important;
    }
    div.searchbar__fields.search {
      display: flex;
      justify-content: center;
    }
    .searchbar__form {
      justify-items: center !important;
    }
    @media all and (min-width: 769px) {
      .searchbar__form {
        justify-items: stretch !important;
      }
      div.searchbar {
        width: 8.333333% !important;
        margin: 0;
      }
      .searchbar__root {
        position: relative;
        z-index: 4;
      }
      .searchbar__root .searchbar__control {
        border-top: 0;
        margin: 0;
        padding: 0.75em 0;
      }
      .searchbar__root .searchbar__input {
        margin: 0;
        padding-right: 12px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
      }
    }
    
  • ui.apps/src/main/content/jcr_root/apps/venia/components/commerce/searchbar/clientlibs/js.txt

    Crear el archivo js.txt:

    code language-text
    js/searchbar.js
    
  • ui.apps/src/main/content/jcr_root/apps/venia/components/commerce/searchbar/clientlibs/js/searchbar.js

    Crear el archivo searchbar.js:

    code language-javascript
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ~ Copyright 2023 Adobe
    ~
    ~ Licensed under the Apache License, Version 2.0 (the "License");
    ~ you may not use this file except in compliance with the License.
    ~ You may obtain a copy of the License at
    ~
    ~     http://www.apache.org/licenses/LICENSE-2.0
    ~
    ~ Unless required by applicable law or agreed to in writing, software
    ~ distributed under the License is distributed on an "AS IS" BASIS,
    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    ~ See the License for the specific language governing permissions and
    ~ limitations under the License.
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    "use strict";
    
    const dataServicesStorefrontInstanceContextQuery = `
        query DataServicesStorefrontInstanceContext {
          dataServicesStorefrontInstanceContext {
            customer_group
            environment_id
            environment
            store_id
            store_view_id
            store_code
            store_view_code
            website_id
            website_name
            website_code
            store_url
            api_key
            store_name
            store_view_name
            base_currency_code
            store_view_currency_code
            catalog_extension_version
          }
          storeConfig {
            base_currency_code
            store_code
          }
        }
      `;
    
    const dataServicesMagentoExtensionContextQuery = `
        query DataServicesStorefrontInstanceContext {
          dataServicesMagentoExtensionContext {
            magento_extension_version
          }
        }
      `;
    
    const dataServicesStoreConfigurationContextQuery = `
        query DataServicesStoreConfigurationContext {
          dataServicesStoreConfigurationContext {
            currency_symbol
            currency_rate
            page_size
            page_size_options
            default_page_size_option
            display_out_of_stock
            allow_all_products
            locale
            min_query_length
          }
        }
      `;
    
    const getCookie = (cookieName) => {
      const cookie = document.cookie.match(
        `(^|[^;]+)\\s*${cookieName}\\s*=\\s*([^;]+)`
      );
      return cookie ? cookie.pop() : "";
    };
    
    const getLoginToken = () => {
      const key = "M2_VENIA_BROWSER_PERSISTENCE__signin_token";
      let token = getCookie("cif.userToken") || "";
    
      try {
        const lsToken = JSON.parse(localStorage.getItem(key));
        if (lsToken && lsToken.value) {
          const timestamp = new Date().getTime();
          if (timestamp - lsToken.timeStored < lsToken.ttl * 1000) {
            token = lsToken.value.replace(/"/g, "");
          }
        }
      } catch (e) {
        console.error(`Login token at ${key} is not valid JSON.`);
      }
      return token;
    };
    
    async function getGraphQLQuery(query, variables = {}) {
      const graphqlEndpoint = `/api/graphql`;
      const headers = {
        "Content-Type": "application/json",
      };
    
      const loginToken = getLoginToken();
      if (loginToken) {
        headers["Authorization"] = `Bearer ${loginToken}`;
      }
    
      const response = await fetch(graphqlEndpoint, {
        method: "POST",
        headers,
        body: JSON.stringify({
          query,
          variables,
        }),
      }).then((res) => res.json());
    
      return response.data;
    }
    
    class SearchBar {
      constructor() {
        const stateObject = {
          dataServicesStorefrontInstanceContext: null,
          dataServicesStoreConfigurationContext: null,
          magentoExtensionVersion: null,
          storeConfig: null,
        };
        this._state = stateObject;
        this._init();
      }
      _init() {
        this._initLiveSearch();
      }
    
      _injectStoreScript(src) {
        const script = document.createElement("script");
        script.type = "text/javascript";
        script.src = src;
    
        document.head.appendChild(script);
      }
    
      async _getStoreData() {
        const { dataServicesStorefrontInstanceContext, storeConfig } =
          (await getGraphQLQuery(dataServicesStorefrontInstanceContextQuery)) || {};
        const { dataServicesStoreConfigurationContext } =
          (await getGraphQLQuery(dataServicesStoreConfigurationContextQuery)) || {};
        this._state.dataServicesStorefrontInstanceContext =
          dataServicesStorefrontInstanceContext;
        this._state.dataServicesStoreConfigurationContext =
          dataServicesStoreConfigurationContext;
        this._state.storeConfig = storeConfig;
    
        if (!dataServicesStorefrontInstanceContext) {
          console.log("no dataServicesStorefrontInstanceContext");
          return;
        }
        // set session storage to expose for widget
        sessionStorage.setItem(
          "WIDGET_STOREFRONT_INSTANCE_CONTEXT",
          JSON.stringify({
            ...dataServicesStorefrontInstanceContext,
            ...dataServicesStoreConfigurationContext,
          })
        );
      }
    
      async _getMagentoExtensionVersion() {
        const { dataServicesMagentoExtensionContext } =
          (await getGraphQLQuery(dataServicesMagentoExtensionContextQuery)) || {};
        this._state.magentoExtensionVersion =
          dataServicesMagentoExtensionContext?.magento_extension_version;
        if (!dataServicesMagentoExtensionContext) {
          console.log("no magentoExtensionVersion");
          return;
        }
      }
    
      getStoreConfigMetadata() {
        const storeConfig = JSON.parse(
          document
            .querySelector("meta[name='store-config']")
            .getAttribute("content")
        );
    
        const { storeRootUrl } = storeConfig;
        const redirectUrl = storeRootUrl.split(".html")[0];
        return { storeConfig, redirectUrl };
      }
    
      async _initLiveSearch() {
        await Promise.all([
          this._getStoreData(),
          this._getMagentoExtensionVersion(),
        ]);
        if (!window.LiveSearchAutocomplete) {
          const liveSearchSrc =
            "https://livesearch-autocomplete.magento-ds.com/v0/LiveSearchAutocomplete.js";
    
          this._injectStoreScript(liveSearchSrc);
          // wait until script is loaded
          await new Promise((resolve) => {
            const interval = setInterval(() => {
              if (window.LiveSearchAutocomplete) {
                clearInterval(interval);
                resolve();
              }
            }, 200);
          });
        }
    
        const {
          dataServicesStorefrontInstanceContext,
          dataServicesStoreConfigurationContext,
        } = this._state;
        if (!dataServicesStorefrontInstanceContext) {
          console.log("no dataServicesStorefrontInstanceContext");
          return;
        }
    
        // initialize live-search
        new window.LiveSearchAutocomplete({
          environmentId: dataServicesStorefrontInstanceContext.environment_id,
          websiteCode: dataServicesStorefrontInstanceContext.website_code,
          storeCode: dataServicesStorefrontInstanceContext.store_code,
          storeViewCode: dataServicesStorefrontInstanceContext.store_view_code,
          config: {
            pageSize: dataServicesStoreConfigurationContext.page_size,
            minQueryLength: dataServicesStoreConfigurationContext.min_query_length,
            currencySymbol: dataServicesStoreConfigurationContext.currency_symbol,
            currencyRate: dataServicesStoreConfigurationContext.currency_rate,
            displayOutOfStock:
              dataServicesStoreConfigurationContext.display_out_of_stock,
            allowAllProducts:
              dataServicesStoreConfigurationContext.allow_all_products,
          },
          context: {
            customerGroup: dataServicesStorefrontInstanceContext.customer_group,
          },
          route: ({ sku }) => {
            return `${
              this.getStoreConfigMetadata().redirectUrl
            }.cifproductredirect.html/${sku}`;
          },
          searchRoute: {
            route: `${this.getStoreConfigMetadata().redirectUrl}/search.html`,
            query: "search_query",
          },
        });
    
        const formEle = document.getElementById("search_mini_form");
    
        formEle.setAttribute(
          "action",
          `${dataServicesStorefrontInstanceContext.store_url}catalogsearch/result`
        );
        // initialize store event after live-search
        this._initMetrics();
      }
    
      async _initMetrics() {
        //  Magento Store event
    
        // wait until script is magentoStorefrontEvents is found
        await new Promise((resolve) => {
          const interval = setInterval(() => {
            if (window.magentoStorefrontEvents) {
              clearInterval(interval);
              resolve();
            }
          }, 200);
        });
    
        const mse = window.magentoStorefrontEvents;
    
        const { dataServicesStorefrontInstanceContext, storeConfig } = this._state;
    
        const {
          base_currency_code,
          catalog_extension_version,
          environment,
          environment_id,
          store_code,
          store_id,
          store_name,
          store_url,
          store_view_code,
          store_view_id,
          store_view_name,
          store_view_currency_code,
          website_code,
          website_id,
          website_name,
        } = dataServicesStorefrontInstanceContext;
    
        console.log("initializing magento extension");
        mse.context.setMagentoExtension({
          magentoExtensionVersion: this._state.magentoExtensionVersion,
        });
    
        mse.context.setPage({
          pageType: "pdp",
          maxXOffset: 0,
          maxYOffset: 0,
          minXOffset: 0,
          minYOffset: 0,
          ping_interval: 5,
          pings: 1,
        });
    
        mse.context.setStorefrontInstance({
          environmentId: environment_id,
          environment: environment,
          storeUrl: store_url,
          websiteId: website_id,
          websiteCode: website_code,
          storeId: store_id,
          storeCode: store_code,
          storeViewId: store_view_id,
          storeViewCode: store_view_code,
          websiteName: website_name,
          storeName: store_name,
          storeViewName: store_view_name,
          baseCurrencyCode: base_currency_code,
          storeViewCurrencyCode: store_view_currency_code,
          catalogExtensionVersion: catalog_extension_version,
        });
      }
    }
    
    (function () {
      function onDocumentReady() {
        new SearchBar({});
      }
    
      if (document.readyState !== "loading") {
        onDocumentReady();
      } else {
        document.addEventListener("DOMContentLoaded", onDocumentReady);
      }
    })();
    
  • ui.apps/src/main/content/jcr_root/apps/venia/components/commerce/searchbar/searchbar.html

    Crear el archivo searchbar.html:

    code language-html
    <!-- Livesearch popover -->
    <div
      data-sly-use.storeconfig="com.adobe.cq.commerce.core.components.models.storeconfigexporter.StoreConfigExporter"
      class="searchbar__root widget-search"
    >
      <div class="block-title" style="display: none">
        <strong>Search</strong>
      </div>
      <div class="live-search-popover block-content">
        <form
          class="searchbar__form"
          id="search_mini_form"
          method="get"
          style="width: auto"
        >
          <div class="searchbar__fields">
            <label
              class="searchbar__label"
              for="search"
              data-role="minisearch-label"
            >
              <span>Search</span>
            </label>
            <div class="searchbar__control">
              <input
                id="search"
                type="text"
                name="q"
                value=""
                placeholder="Search entire store here..."
                class="searchbar__input"
                maxlength="128"
                role="combobox"
                aria-haspopup="false"
                aria-autocomplete="both"
                autocomplete="off"
                aria-expanded="false"
                onchange=""
              />
              <div id="search_autocomplete" class="search-autocomplete"></div>
            </div>
          </div>
          <div class="actions" style="display: none">
            <button
              type="submit"
              title="Search"
              class="action search"
              aria-label="Search"
            >
              <span>Search</span>
            </button>
          </div>
        </form>
      </div>
    </div>
    
  • ui.config/src/main/content/jcr_root/apps/venia/osgiconfig/config/com.adobe.cq.commerce.core.components.internal.servlets.ProductPageRedirectServlet.cfg.json

    Crear el archivo com.adobe.cq.commerce.core.components.internal.servlets.ProductPageRedirectServlet.cfg.json:

    code language-json
    {
      "sling.servlet.resourceTypes": [
          "core/cif/components/structure/page/v1/page",
          "core/cif/components/structure/page/v2/page",
          "core/cif/components/structure/page/v3/page"
      ]
    }
    
    • ui.tests/test-module/specs/venia/searchbar.js

      Edite el archivo searchbar.js, línea 19-20, cambiando describe a describe.skip:

      code language-javascript
      describe.skip('Venia Searchbar Component', () => {
      
recommendation-more-help
fbcff2a9-b6fe-4574-b04a-21e75df764ab