Sugerencias en la búsqueda

En este tutorial vamos a ver cómo agregar las sugerencias al campo de búsqueda con el que ya cuentan los themes. ¿Cómo funciona esto? Cuando un usuario está ingresando el texto para buscar, le aparecen abajo hasta 6 productos sugeridos para las letras o palabras que va escribiendo.

HTML

1. Lo primero que vamos a hacer es crear las carpeta header dentro de la carpeta snipplets, y luego agregar los siguientes snipplets con sus respectivos contenidos.

Es importante mantener las clases “js-...” para garantizar su funcionamiento cuando agreguemos el JavaScript.

header-search.tpl

<form class="js-search-container js-search-form" action="{{ store.search_url }}" method="get">
    <div class="form-group m-0">
        <input class="js-search-input form-control search-input" autocomplete="off" type="search" name="q" placeholder="{{ 'Buscar' | translate }}"/>
        <button type="submit" class="btn search-input-submit" value="">
            {% include "snipplets/svg/search.tpl" with {svg_custom_class: "icon-inline svg-icon-text"} %}
        </button>
    </div>
</form>
<div class="js-search-suggest search-suggest">
    {# AJAX container for search suggestions #}
</div>

header-search-results.tpl

<ul class="search-suggest-list">
    {% set search_suggestions = products | take(6) %}
    {% for product in search_suggestions %}
        <li class="search-suggest-item container-fluid">
            <a href="{{ product.url }}" class="search-suggest-link row justify-content-md-center">
                <div class="search-suggest-image-container col-xs-auto">
                    {{ product.featured_image | product_image_url("tiny") | img_tag(product.featured_image.alt, {class: 'search-suggest-image'}) }}
                </div>
                <div class="search-suggest-text col">
                    <p class="search-suggest-name">
                        {{ product.name | highlight(query) }}
                    </p>
                    {% if product.display_price %}
                        <p>
                            {{ product.price | money }}


                            {% set product_can_show_installments = product.show_installments and product.display_price and product.get_max_installments.installment > 1 %}
                            {% if product_can_show_installments %}
                                {% set max_installments_without_interests = product.get_max_installments(false) %}
                                {% if store.country == 'AR' %}
                                    {% set max_installments_with_interests = product.get_max_installments %}
                                    {% if max_installments_with_interests %}
                                        <span>| {{ "Hasta <strong class='installment-amount'>{1}</strong> cuotas" | t(max_installments_with_interests.installment, max_installments_with_interests.installment_data.installment_value_cents | money) }}</span>
                                    {% endif %}
                                {% else %}
                                    {% if max_installments_without_interests and max_installments_without_interests.installment > 1 %}
                                        <span>| {{ "<strong class='installment-amount'>{1}</strong> cuotas sin interés de <strong class='installment-price'>{2}</strong>" | t(max_installments_without_interests.installment, max_installments_without_interests.installment_data.installment_value_cents | money) }}</span>
                                    {% else %}
                                        {% set max_installments_with_interests = product.get_max_installments %}
                                        {% if max_installments_with_interests %}
                                            <span>| {{ "<strong class='installment-amount'>{1}</strong> cuotas de <strong class='installment-price'>{2}</strong>" | t(max_installments_with_interests.installment, max_installments_with_interests.installment_data.installment_value_cents | money) }}</span>
                                        {% endif %}
                                    {% endif %}
                                {% endif %}
                            {% endif %}
                        </p>
                    {% endif %}
                </div>
                <div class="col-xs-auto">
                    {% include "snipplets/svg/chevron-right.tpl" with {svg_custom_class: "icon-inline search-suggest-icon"} %}
                </div>
            </a>
        </li>
    {% endfor %}
    <a href="#" class="js-search-suggest-all-link btn btn-primary d-block">{{ 'Ver todos los resultados' | translate }}</a>
</ul>

Al comienzo del código se define la cantidad de productos sugeridos: {% set search_suggestions = products | take(6) %}. Si queres mostrar otra cantidad, podés hacerlo cambiando el número de “take(B)”

2. También, necesitamos agregar una carpeta SVG dentro de la carpeta snipplets. Acá vamos sumar los SVGs que usamos para los iconos en el calculador.

search.tpl

<svg class="{{ svg_custom_class }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M508.5 468.9L387.1 347.5c-2.3-2.3-5.3-3.5-8.5-3.5h-13.2c31.5-36.5 50.6-84 50.6-136C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c52 0 99.5-19.1 136-50.6v13.2c0 3.2 1.3 6.2 3.5 8.5l121.4 121.4c4.7 4.7 12.3 4.7 17 0l22.6-22.6c4.7-4.7 4.7-12.3 0-17zM208 368c-88.4 0-160-71.6-160-160S119.6 48 208 48s160 71.6 160 160-71.6 160-160 160z"/></svg>

chevron-right.tpl

<svg class="{{ svg_custom_class }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 512"><path d="M24.707 38.101L4.908 57.899c-4.686 4.686-4.686 12.284 0 16.971L185.607 256 4.908 437.13c-4.686 4.686-4.686 12.284 0 16.971L24.707 473.9c4.686 4.686 12.284 4.686 16.971 0l209.414-209.414c4.686-4.686 4.686-12.284 0-16.971L41.678 38.101c-4.687-4.687-12.285-4.687-16.971 0z"/></svg>

3. Por último necesitamos llamar al snipplet donde queramos mostrar el buscador, por lo general se encuentra en el header. En el theme Base lo ubicamos dentro de un modal.

{% snipplet "header/header-search.tpl" %}

CSS

Requisito:

Tener agregados en tu diseño las clases helpers. Podés seguir este este pequeño tutorial para hacerlo (simplemente es copiar y pegar algunas clases, no toma más de 1 minuto).

1. Agregar los estilos dentro del archivo static/style-critical.tpl 

Si en tu diseño usas una hoja de estilos para el CSS crítico, vamos a necesitar el siguiente código dentro de la misma, pero si no es el caso entonces podés unificar el CSS de los pasos 1 y 2 en un solo archivo.

{# /* // Forms */ #}

.form-group {
  position: relative;
  width: 100%;
}
.form-group .form-select-icon{
  position: absolute;
  bottom: 12px;
  right: 0;
  pointer-events: none;
}
.form-row {
  width: auto;
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -ms-flex-wrap: wrap;
  flex-wrap: wrap;
  margin-right: -5px;
  margin-left: -5px;
  clear: both;
}

.form-row > .col,
.form-row > [class*=col-]{
  padding-right: 5px;
  padding-left: 5px;
}

.form-label {
  display: block;
  font-size: 10px;
  text-transform: uppercase;
}

2. Agregar los estilos dentro del archivo static/style-async.tpl 

Si en tu diseño no usas una hoja de estilos para CSS asíncrono, agregalo al resto de tu código CSS.

{# /* // Search */ #}

.search-input {
  padding-right: 30px;
}

.search-input[type="search"]::-webkit-search-cancel-button {
  -webkit-appearance:none
}

.search-input-submit {
  position: absolute;
  top: 5px;
  right: 0;
  font-size: 18px;
  background: none;
  border: 0;
}

.search-suggest-list {
  margin: 0 0 10px 0;
  padding: 0;
}

.search-suggest-item {
  padding: 10px 15px;
  list-style: none;
}

.search-suggest-text,
.search-suggest-name {
  margin-bottom: 5px;
  line-height: 18px;
}
.search-suggest-icon {
  margin: 0 10px;
  font-size: 14px;
}

3. Agregamos el siguiente SASS de colores en style-colors.scss.tpl (o la hoja de tu diseño que tenga los colores y tipografías de la tienda). Recordá que las variables de colores y tipografías pueden variar respecto a tu diseño:

{# This mixin adds browser prefixes to a CSS property #}

@mixin prefix($property, $value, $prefixes: ()) {
    @each $prefix in $prefixes {
        #{'-' + $prefix + '-' + $property}: $value;
    }
       #{$property}: $value;
}

{# /* // Search */ #}

.search-suggest-item {
  border-bottom: 1px solid rgba($main-foreground, .1);
}

{# /* // Buttons */ #}

.btn{
  text-decoration: none;
  text-align: center;
  border: 0;
  cursor: pointer;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  text-transform: uppercase;
  background: none;
  @include prefix(transition, all 0.4s ease, webkit ms moz o);
  &:hover,
  &:focus{
    outline: 0;
    opacity: 0.8;
  }
  &[disabled],
  &[disabled]:hover{
    opacity: 0.5;
    cursor: not-allowed;
    outline: 0;
  }
  &-default{
    padding: 10px 15px; 
    background-color: rgba($main-foreground, .2);
    color: $main-foreground;
    fill: $main-foreground;
    font-weight: bold;
  }
  &-primary{
    padding: 15px;
    background-color: $primary-color;
    color: $main-background;
    fill: $main-background;
    letter-spacing: 4px;
    font-size: 12px;
    &:hover{
      color: $main-background;
      fill: $main-background;
    }
  }
}

JS

⚠️ A partir del día 30 de enero de 2023, la librería jQuery será removida del código de nuestras tiendas, por lo tanto la función "$" no podrá ser utilizada.

1. El JavaScript necesitamos agregarlo en el archivo store.js.tpl (o donde tengas tus funciones de JS). El código que necesitamos es el siguiente:

{# /* // Search suggestions */ #}

LS.search(jQueryNuvem(".js-search-input"), function (html, count) {
    $search_suggests = jQueryNuvem(this).closest(".js-search-container").next(".js-search-suggest");
    if (count > 0) {
        $search_suggests.html(html).show();
    } else {
        $search_suggests.hide();
    }
    if (jQueryNuvem(this).val().length == 0) {
        $search_suggests.hide();
    }
}, {
    snipplet: 'header/header-search-results.tpl'
});

if (window.innerWidth > 768) {

    {# Hide search suggestions if user click outside results #}

    jQueryNuvem("body").on("click", function () {
        jQueryNuvem(".js-search-suggest").hide();
    });

    {# Maintain search suggestions visibility if user click on links inside #}

    jQueryNuvem(document).on("click", ".js-search-suggest a", function () {
        jQueryNuvem(".js-search-suggest").show();
    });
}

jQueryNuvem(".js-search-suggest").on("click", ".js-search-suggest-all-link", function (e) {
    e.preventDefault();
    $this_closest_form = jQueryNuvem(this).closest(".js-search-suggest").prev(".js-search-form");
    $this_closest_form.submit();
});

Traducciones

En este paso agregamos los textos para las traducciones en el archivo config/translations.txt

es "Buscar"
pt "Buscar"
en "Search"
es_mx "Buscar"

es "Ver todos los resultados"
pt "Ver todos os resultados"
en "See all results"
es_mx "Ver todos los resultados"

es "Hasta 12 cuotas"
pt "Até 12x sem juros"
en "Upto 12x without interests"
es_mx "Hasta 12 cuotas"

es "Hasta <strong class='js-installment-amount installment-amount'>{1}</strong> cuotas"
pt "Até <strong class='js-installment-amount installment-amount'>{1}</strong>x"
en "Up to <strong class='js-installment-amount installment-amount'>{1}</strong> installments"
es_mx "Hasta <strong class='js-installment-amount installment-amount'>{1}</strong> meses"

es "<strong class='js-installment-amount installment-amount'>{1}</strong> cuotas de <strong class='js-installment-price installment-price'>{2}</strong>"
pt "<strong class='js-installment-amount installment-amount'>{1}</strong>x de <strong class='js-installment-price installment-price'>{2}</strong>"
en "In up to <strong class='js-installment-amount installment-amount'>{1}</strong> installments of <strong class='js-installment-price installment-price'>{2}</strong>"
es_mx "<strong class='js-installment-amount installment-amount'>{1}</strong> meses de <strong class='js-installment-price installment-price'>{2}</strong>"

es "<strong class='js-installment-amount installment-amount'>{1}</strong> cuotas sin interés de <strong class='js-installment-price installment-price'>{2}</strong>"
pt "<strong class='js-installment-amount installment-amount'>{1}</strong>x de <strong class='js-installment-price installment-price'>{2}</strong> sem juros"
en "In up to <strong class='js-installment-amount installment-amount'>{1}</strong> installments of <strong class='js-installment-price installment-price'>{2}</strong> without interest"
es_mx "<strong class='js-installment-amount installment-amount'>{1}</strong> meses sin intereses de <strong class='js-installment-price installment-price'>{2}</strong>"

es "En hasta <strong class='js-installment-amount installment-amount'>{1}</strong> cuotas"
pt "Em até <strong class='js-installment-amount installment-amount'>{1}</strong>x"
en "In up to <strong class='js-installment-amount installment-amount'>{1}</strong> installments"
es_mx "En hasta <strong class='js-installment-amount installment-amount'>{1}</strong> meses"