feat: MPS-24 — chip strip stays on every SERP page
Operator: 'leave the chits on screen for all serp pages.' The horizontal tag-chip-strip only rendered on the shop home; drilling into a category (tag-detail SERP) dropped it, so hopping categories meant going back. - Extracted the chip strip (duplicated verbatim in home.j2 + shop.j2) into a single _facet_nav.j2 chip_strip(...) macro — DRY, one source of truth — and added it to shop_tag.j2 under the header. - shop_tag_detail view already supplied home_chips / active_tag / sort / price, so this was a template-only gap. Active category chip highlights on the SERP and carries facet_qs (sort/price compose). - Search SERP renders home.j2 so it gets the macro for free. Test: +test_chip_strip_stays_on_tag_detail_serp. 1136 passed. Docs: mps-24.md Phase 2.8i.
This commit is contained in:
parent
3bb05e4b5d
commit
09f5a68428
6 changed files with 85 additions and 39 deletions
|
|
@ -363,6 +363,19 @@ Tests (`test_functional.py::TestProductTagsSpa`):
|
|||
Deferred (occasional click, not the hot path): AJAX-ifying the
|
||||
"Suggest categories" link — still a full navigation by design.
|
||||
|
||||
**Phase 2.8i — chip strip on every SERP page** (shipped 2026-05-16):
|
||||
operator: "leave the chits on screen for all serp pages." The
|
||||
horizontal `tag-chip-strip` rendered only on the shop home; drilling
|
||||
into a category (tag-detail SERP `shop_tag.j2`) dropped it, so hopping
|
||||
categories meant going back. Extracted the (duplicated) chip strip
|
||||
from `home.j2`/`shop.j2` into a single `_facet_nav.j2` `chip_strip(...)`
|
||||
macro and added it to `shop_tag.j2` under the header. The
|
||||
`shop_tag_detail` view already supplied `home_chips` / `active_tag` /
|
||||
sort / price, so this was a template-only gap; the active category
|
||||
chip highlights on the SERP and carries `facet_qs`. Search SERP
|
||||
already renders `home.j2` so it gets the macro for free. Test:
|
||||
`test_chip_strip_stays_on_tag_detail_serp`.
|
||||
|
||||
**Phase 2.8h — facets compose, not clobber** (shipped 2026-05-16):
|
||||
operator: "switching one breaks it" — picking a category reset the
|
||||
active Sort + Price. Cause: the facet category links / "All" link /
|
||||
|
|
|
|||
|
|
@ -118,3 +118,22 @@
|
|||
{{ facet_form(base_url, tag_base, sort_options, sort_key, price_min, price_max, facet_tags, active_tag) }}
|
||||
</details>
|
||||
{% endmacro %}
|
||||
|
||||
{# Horizontal category chip strip. Single source of truth — used by the
|
||||
shop home (home.j2 / shop.j2) AND every SERP page (shop_tag.j2) so
|
||||
the chips stay on screen as the shopper hops categories. Carries the
|
||||
active sort/price (facet_qs) so switching category composes. #}
|
||||
{% macro chip_strip(shop_url, tag_base, home_chips, active_tag, sort_key, price_min, price_max) %}
|
||||
{% if home_chips %}
|
||||
<nav class="tag-chip-strip" data-tag-strip aria-label="Browse by category">
|
||||
<a href="{{ shop_url }}{{ facet_qs(sort_key, price_min, price_max) }}"
|
||||
class="tag-chip{% if not active_tag %} tag-chip-active{% endif %}"
|
||||
data-tag-slug="" rel="nofollow">All</a>
|
||||
{% for chip in home_chips %}
|
||||
<a href="{{ tag_base }}/tag/{{ chip.slug }}{{ facet_qs(sort_key, price_min, price_max) }}"
|
||||
class="tag-chip{% if active_tag and active_tag.id == chip.id %} tag-chip-active{% endif %}"
|
||||
data-tag-slug="{{ chip.slug }}" rel="nofollow">{{ chip.name }}</a>
|
||||
{% endfor %}
|
||||
</nav>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
|
|
|||
|
|
@ -88,24 +88,11 @@
|
|||
Layout 0 (flat) keeps its existing simple grid with no nav. #}
|
||||
{% set show_facet_nav = request.shop.home_layout and request.shop.home_layout >= 1 %}
|
||||
|
||||
{# MPS-24: tag chip strip — shown on layout 1 (chips) and 2 (lanes).
|
||||
Chips and the facet left-nav point at the SAME targets (shop home
|
||||
for "All", {tag_base}/tag/{slug} per category) so the two stay in
|
||||
lock-step. tag_filter.js keeps full navigation for /tag/ hrefs. #}
|
||||
{% if home_chips %}
|
||||
<nav class="tag-chip-strip" data-tag-strip aria-label="Browse by category">
|
||||
<a href="{{ shop_url }}{{ facet.facet_qs(sort_key, price_min, price_max) }}"
|
||||
class="tag-chip{% if not active_tag %} tag-chip-active{% endif %}"
|
||||
data-tag-slug=""
|
||||
rel="nofollow">All</a>
|
||||
{% for chip in home_chips %}
|
||||
<a href="{{ tag_base }}/tag/{{ chip.slug }}{{ facet.facet_qs(sort_key, price_min, price_max) }}"
|
||||
class="tag-chip{% if active_tag and active_tag.id == chip.id %} tag-chip-active{% endif %}"
|
||||
data-tag-slug="{{ chip.slug }}"
|
||||
rel="nofollow">{{ chip.name }}</a>
|
||||
{% endfor %}
|
||||
</nav>
|
||||
{% endif %}
|
||||
{# MPS-24: tag chip strip — shared macro, shown on layout 1 (chips)
|
||||
and 2 (lanes) AND every SERP page (shop_tag.j2). Chips + facet
|
||||
left-nav hit the SAME targets; tag_filter.js keeps full navigation
|
||||
for /tag/ hrefs. #}
|
||||
{{ facet.chip_strip(shop_url, tag_base, home_chips, active_tag, sort_key, price_min, price_max) }}
|
||||
|
||||
{% if show_facet_nav %}
|
||||
{# Mobile/tablet: collapsed <details> facets above the grid. #}
|
||||
|
|
|
|||
|
|
@ -15,27 +15,12 @@
|
|||
{% set tag_base = request.shop.absolute_url(request, slug=False) %}
|
||||
{% set show_facet_nav = request.shop.home_layout and request.shop.home_layout >= 1 %}
|
||||
|
||||
{# MPS-24: tag chip strip — shown on layout 1 (chips) and 2 (lanes). #}
|
||||
{# Chip strip and the facet left-nav point at the SAME targets: the
|
||||
shop home for "All" and the canonical tag detail page
|
||||
({tag_base}/tag/{slug}) per category. tag_filter.js detects the
|
||||
/tag/ href and keeps full navigation, so a chip and its matching
|
||||
sidebar entry always land on the same page with the same active
|
||||
state. #}
|
||||
{% if home_chips %}
|
||||
<nav class="tag-chip-strip" data-tag-strip aria-label="Browse by category">
|
||||
<a href="{{ shop_url }}{{ facet.facet_qs(sort_key, price_min, price_max) }}"
|
||||
class="tag-chip{% if not active_tag %} tag-chip-active{% endif %}"
|
||||
data-tag-slug=""
|
||||
rel="nofollow">All</a>
|
||||
{% for chip in home_chips %}
|
||||
<a href="{{ tag_base }}/tag/{{ chip.slug }}{{ facet.facet_qs(sort_key, price_min, price_max) }}"
|
||||
class="tag-chip{% if active_tag and active_tag.id == chip.id %} tag-chip-active{% endif %}"
|
||||
data-tag-slug="{{ chip.slug }}"
|
||||
rel="nofollow">{{ chip.name }}</a>
|
||||
{% endfor %}
|
||||
</nav>
|
||||
{% endif %}
|
||||
{# MPS-24: tag chip strip — shared macro (single source of truth),
|
||||
shown on layout 1 (chips) and 2 (lanes) AND every SERP page. Chip
|
||||
strip + facet left-nav point at the SAME targets; tag_filter.js
|
||||
keeps full navigation for /tag/ hrefs so a chip and its sidebar
|
||||
entry land on the same SERP with the same active state. #}
|
||||
{{ facet.chip_strip(shop_url, tag_base, home_chips, active_tag, sort_key, price_min, price_max) }}
|
||||
|
||||
{% if show_facet_nav %}
|
||||
{# Mobile/tablet: collapsed <details> facets above the grid. #}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,11 @@
|
|||
<p class="type-body-sm"><a href="{{ shop_url }}" class="shop-theme-link-color">← All products</a></p>
|
||||
</section>
|
||||
|
||||
{# Chip strip stays on screen on every SERP page so the shopper can
|
||||
hop categories without going back. Same shared macro as the shop
|
||||
home; active_tag highlights the current category. #}
|
||||
{{ facet.chip_strip(shop_url, tag_base, home_chips, active_tag, sort_key, price_min, price_max) }}
|
||||
|
||||
{# Mobile/tablet: collapsed <details> facets above the grid (CSS hides
|
||||
it ≥800px where the sidebar takes over). #}
|
||||
{{ facet.details(shop_url, tag_base, sort_options, sort_key, price_min, price_max, facet_tags, active_tag) }}
|
||||
|
|
|
|||
|
|
@ -8947,6 +8947,43 @@ class TestHomeLayoutAndTags(_AuthenticatedBase):
|
|||
self.assertIn('tag-detail-header', body)
|
||||
self.assertIn('serp-list', body)
|
||||
|
||||
def test_chip_strip_stays_on_tag_detail_serp(self):
|
||||
"""Operator: 'leave the chits on screen for all serp pages.'
|
||||
The horizontal chip strip must render on the tag-detail SERP
|
||||
(not just the shop home) so the shopper can hop categories
|
||||
without going back. The current category chip is active."""
|
||||
shop, product = self._make_shop_with_product("chip-serp-shop")
|
||||
self.testapp.post(
|
||||
f"/s/{shop.id}/settings",
|
||||
{
|
||||
"form_section": "home-layout-settings",
|
||||
"home_layout": "2",
|
||||
"home_layout_tag_limit": "5",
|
||||
"home_layout_per_lane_limit": "10",
|
||||
"submit": "Save Home Layout",
|
||||
},
|
||||
)
|
||||
self.testapp.post(
|
||||
f"/p/{product.id}/edit",
|
||||
{
|
||||
"title": product.title,
|
||||
"description": "One. Two. Three. Four. Five. Six.",
|
||||
"price": str(product.price),
|
||||
"visibility": "1",
|
||||
"tags": "Math",
|
||||
},
|
||||
)
|
||||
res = self.testapp.get(f"/s/{shop.id}/tag/math")
|
||||
body = res.body.decode()
|
||||
# The shared chip strip is present on the SERP page.
|
||||
self.assertIn('class="tag-chip-strip"', body)
|
||||
self.assertIn('data-tag-strip', body)
|
||||
# The active category chip is highlighted on the SERP.
|
||||
self.assertIn('tag-chip tag-chip-active', body)
|
||||
# And it still has the facet sidebar (chip strip is additive,
|
||||
# not a replacement).
|
||||
self.assertIn('facet-nav', body)
|
||||
|
||||
def test_facet_links_preserve_sort_and_price(self):
|
||||
"""Operator report: 'switching one breaks it' — changing category
|
||||
reset sort/price. Every category link / All link / chip / lane
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue