Shopify / Shopify/dawn

Clear all discards the search term when the term contains an ampersand

Open
#3,977 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Liquid
Stars
3.1k
Forks
4.5k
PR merge metrics
No merged PRs in 30d

Description

Describe the current behavior

On a search results page, "Remove all" / "Clear all" silently discards the search
term when the term contains an ampersand (or <, >, " or '), returning the entire
catalogue instead of the filtered search.

snippets/facets.liquid:26 builds the clear-all URL from the search term with
escape, an HTML escaper, rather than a URL encoder:

assign terms = results.terms | escape
assign results_url = '?q=' | append: terms | append: '&options%5Bprefix%5D=last&sort_by=' | append: sort_by

escape turns & into &, which the browser decodes back to a real & when it
resolves the href, splitting the query string.

Steps to reproduce, on a clean Dawn install with the sample catalogue:

  1. Search for a & b -> /search?q=a+%26+b&options%5Bprefix%5D=last (3 results)
  2. Apply any filter, e.g. Availability: In stock (2 results)
  3. Click "Remove all"

Result: the URL becomes

?q=a%20&%20b&options%5Bprefix%5D=last&sort_by=relevance

Note the bare & mid-term: q is now a and b is a separate valueless
parameter. The page returns 45 results - the whole catalogue - and the filter row
switches to catalogue-wide facets (Region, Colour, Volume) instead of the
search's own.

Two details worth flagging:

  • The search input still displays a & b throughout, so the UI asserts a term
    the result set does not reflect. There is no error state; the shopper simply
    gets the wrong products under a correct-looking query.
  • On the broken URL, search.terms and the result set disagree, and what the
    box renders varies by theme. The reliable test is the result count, not the
    displayed term.

Describe the expected behavior

"Remove all" clears the filters and preserves the search term:

?q=a+%26+b&options%5Bprefix%5D=last&sort_by=relevance

returning the same 3 results as step 1.

Version information (Dawn, browsers and operating systems)

  • Dawn Version: 16.0.0
  • Edge Version: 152.0.4191.66 (Official build) (64-bit)
  • Windows 11

Possible solution

url_encode alone is not sufficient, because search.terms is returned already
HTML-escaped and Liquid's escape is escape-once. Evidence:
{{ search.terms | size }} returns 16 for a typed tweed & wool (12 characters),
and the term a&b<c>d"e'f comes back as a&amp;b&lt;c&gt;d&quot;e&#39;f. So
url_encode on its own yields a%26amp%3Bb... - the entity encoded rather than
the character - and repeated clicks compound it (&amp; -> &amp;amp;).

Unwinding the entities before encoding, with the ampersand last so earlier
replacements cannot be re-read, does round-trip correctly:

assign terms = results.terms | replace: '&lt;', '<' | replace: '&gt;', '>' | replace: '&quot;', '"' | replace: '&#39;', "'" | replace: '&amp;', '&' | url_encode

That is a workaround rather than a fix. The underlying issue is that the clear-all
URL has to be rebuilt by hand at all: Liquid exposes no request query string, and
a search has no equivalent of a collection's results.url. Horizon already avoids
hand-built URLs everywhere else by using the platform-generated
value.url_to_remove / filter.url_to_remove; a url_to_remove equivalent for
"clear everything", or an exposed search URL, would remove the need for any
escaping logic here.

Additional context/screenshots

The same construction is present verbatim in Horizon
(blocks/filters.liquid:59-63), where results_url feeds clear-all in five
places, so the fix likely wants applying in both themes. Horizon has issues
disabled, hence reporting here.

Related but distinct: #2198 covered the drawer 'Clear' action clearing all
filters rather than one group, and was closed in January 2023. This is the
query-string encoding of the clear-all URL, not the scope of the action.
Reproduced on Dawn 16.0.0 (current release, v16.0.0 / theme_version 16.0.0).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with snippets/facets.liquid:26 and compare the matching construction in Horizon at blocks/filters.liquid:59-63. Reproduce the clear-all flow with a search term containing an ampersand and verify the generated URL preserves the term. Done means clearing filters keeps the same search results in both themes without corrupting the query string.

Written by the indexing model from the issue text.

Assessment

Domain
frontend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.