Skip to content

fix: encode history term in autocomplete navigation links - #222

Merged
hiagolcm merged 5 commits into
masterfrom
bugfix/fix-autocomplete-history-link-encoding
May 21, 2026
Merged

fix: encode history term in autocomplete navigation links#222
hiagolcm merged 5 commits into
masterfrom
bugfix/fix-autocomplete-history-link-encoding

Conversation

@hiagolcm

@hiagolcm hiagolcm commented May 20, 2026

Copy link
Copy Markdown
Contributor

Problem

History links broken for terms with special characters: When shoppers clicked autocomplete history entries containing / (e.g., "12/3 Romex"), the URL was malformed and failed to load the PLP.

Root cause: History links used unencoded path segments (/12/3 Romex), which the router interpreted as multiple segments instead of a single search term. The search bar correctly encodes these (/12%2F3%20romex), but the autocomplete didn't match.

Additional issues discovered:

"See all" link inconsistency: Used legacy $2F encoding instead of standard %2F, diverging from the search bar
Cookie corruption bug: Search terms containing , (or other cookie separators like ;, %, =) would split or corrupt the history list
Solution
Three coordinated fixes in a single PR:

Standardized URL encoding (react/utils/term-encoding.ts):

Created encodeSearchTerm() helper using standard encodeURIComponent (/ → %2F, space → %20)
Idempotent design handles legacy cookie entries without double-encoding
Used by both history rows and "see all" link for consistency
Cookie robustness (react/utils/search-history.ts):

Extracted cookie persistence into dedicated module
Per-entry percent-encoding before storing (any character now survives round-trip)
Changed addTermToHistory from decodeURI → decodeURIComponent for canonical storage
Updated autocomplete component (react/components/Autocomplete/index.tsx):

History rows: value now uses encodeSearchTerm(item)
"See all" link: switched from encodeUrlString to encodeSearchTerm
Both now produce URLs identical to the search bar

Impact

✅ History links now work for terms with /, spaces, and other special characters
✅ "See all" matches search bar - no more $2F vs %2F inconsistency
✅ Cookie survives any character - terms with ,, ;, %, = no longer corrupt history
✅ Backward compatible - legacy cookies read correctly
✅ Test coverage - regression protection for future changes

Before/After

Search term: "12/3 Romex"

Entry point Before (broken) After (fixed)
Search bar /12%2F3%20romex /12%2F3%20romex
History link /12/3%20Romex /12%2F3%20romex
"See all" /12$2F3%20romex ⚠️ /12%2F3%20romex

Cookie: "shoes, size 10"

Before After
Corrupts into 2 entries ❌ Single entry preserved ✅

History rows in the `autocomplete-result-list.v2` block now apply the
codebase-internal `encodeUrlString` placeholder (`/` -> `$2F`) to the
navigation `value`, mirroring what the "see all" link in `TileList`
already does. Without this, terms containing `/` (e.g. `12/3 Romex`)
were interpolated verbatim into the route slug by `vtex.render-runtime`
`<Link>`, splitting the term into multiple path segments and breaking
the search route at click time. The visible label stays decoded
(`decodeUrlString`) and the `biggy-search-history` cookie format is
unchanged.

Also adds a Jest test setup via `@vtex/test-tools` (matching sibling IS
apps); `make test` now runs the suite and `make check` includes it.
Initial coverage targets the encoding helpers and the new
`buildHistoryItemValue` helper.

Spec: is-io-specs/specs/fix-autocomplete-history-link-encoding/spec.md
@hiagolcm
hiagolcm requested a review from a team as a code owner May 20, 2026 19:40
@vtex-io-ci-cd

vtex-io-ci-cd Bot commented May 20, 2026

Copy link
Copy Markdown

Hi! I'm VTEX IO CI/CD Bot and I'll be helping you to publish your app! 🤖

Please select which version do you want to release:

  • Patch (backwards-compatible bug fixes)

  • Minor (backwards-compatible functionality)

  • Major (incompatible API changes)

And then you just need to merge your PR when you are ready! There is no need to create a release commit/tag.

  • No thanks, I would rather do it manually 😞

@vtex-io-docs-bot

vtex-io-docs-bot Bot commented May 20, 2026

Copy link
Copy Markdown

Beep boop 🤖

I noticed you didn't make any changes at the docs/ folder

  • There's nothing new to document 🤔
  • I'll do it later 😞

In order to keep track, I'll create an issue if you decide now is not a good time

  • I just updated 🎉🎉

hiagolcm and others added 3 commits May 20, 2026 16:46
The platform builder's at-loader was type-checking the new test files
under react/utils/__tests__/ and failing with TS2593/TS2304 errors
because jest globals (`describe`, `it`, `expect`) aren't recognised
without the jest types in scope.

Aligns the test-setup convention with sibling repos (notably
`vtex.delivery-promise-components`):

- `.vtexignore`: exclude `**/__tests__`, `**/__mocks__`, `**/*.test.*`
  from the platform build (matches sibling `.vtexignore`).
- `react/tsconfig.json`: add `"types": ["jest", "node"]` so editor /
  local tsc passes recognise jest globals (matches sibling tsconfig).

Tests still run via `make test` / `yarn test` (vtex-test-tools), all
14 unit tests green.

Spec: is-io-specs/specs/fix-autocomplete-history-link-encoding/spec.md
Manual verification showed the previous fix produced `12$2f3%20romex`
in the path slug — the codebase-internal `$2F` placeholder used by
`encodeUrlString` — instead of the `12%2f3%20romex` emitted by the
search bar. Although both forms decode to the same `term` route param,
the visible mismatch is the regression the spec's US-1 acceptance
criterion was designed to prevent.

Switches `buildHistoryItemValue` to standard `encodeURIComponent`,
wrapped in `safeEncodeURIComponent` to stay idempotent against legacy
cookie entries that may already contain percent-encoded triplets
(decode-then-encode round-trip; falls back to verbatim encoding when
the input contains a malformed `%XX`).

The legacy `encodeUrlString` placeholder remains in use by `TileList`'s
"see all" link and is not touched here. Harmonising both code paths is
logged as a follow-up in the spec.

Tests updated to assert the standard `%2F` form plus two extra cases:
lowercase `%2f` recognition (idempotency) and malformed-triplet
fallback. All 16 tests green.

Spec: is-io-specs/specs/fix-autocomplete-history-link-encoding/spec.md
…inks

Replaces legacy $2F encoding with standard percent-encoding (%2F) for
both history item links and the "see all" link in autocomplete dropdown,
matching the search bar's encoding behavior since vtex.store-components@0e9a0ee5.

Changes:
- Create react/utils/term-encoding.ts with buildHistoryItemValue and
  buildSearchTermRouteValue helpers (both backed by safeEncodeURIComponent)
- Update Autocomplete#updateHistory to use buildHistoryItemValue for
  history item values
- Update Autocomplete#contentWhenQueryIsNotEmpty to use
  buildSearchTermRouteValue for the "see all" link
- Remove react/utils/history-items.ts (functionality moved to term-encoding.ts)
- Add comprehensive test coverage in term-encoding.test.ts
- Mark encodeUrlString as deprecated (no live callers after this fix)
- Update CHANGELOG.md

Spec: is-io-specs/specs/fix-autocomplete-history-link-encoding/spec.md
Co-authored-by: Cursor <cursoragent@cursor.com>
@hiagolcm
hiagolcm force-pushed the bugfix/fix-autocomplete-history-link-encoding branch from 2bab39b to e21e6e7 Compare May 20, 2026 20:45
…h terms

The cookie stored entries as a raw comma-joined string, so any term
containing a `,` (e.g. `shoes, size 10`) would silently split into two
unrelated history entries on the next read. The same was true for `;`
and other cookie-significant characters.

Changes:
- Extract cookie persistence into a new react/utils/search-history.ts
  module. Each entry is percent-encoded before being joined and decoded
  after being split, so any character is now safe.
- BiggyClient.searchHistory / prependSearchHistory delegate to the new
  module (kept as thin pass-throughs for backwards compatibility).
- Autocomplete#addTermToHistory uses decodeURIComponent (instead of
  decodeURI) so the canonical, fully-decoded form is what gets persisted.
- Add comprehensive test coverage in search-history.test.ts (term with
  comma, percent, semicolon, equals, slash, plus de-duplication and
  limit behavior).
- Update CHANGELOG.

Spec: is-io-specs/specs/fix-autocomplete-history-link-encoding/spec.md
(Decision 4, US-4)

Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions

Copy link
Copy Markdown
Warnings
⚠️

👀 The size of this pull request seems relatively large (>420 modifications). Consider splitting it into smaller pull requests to help make reviews easier and faster.

Generated by 🚫 dangerJS against 5fe3699

@hiagolcm
hiagolcm merged commit 895616d into master May 21, 2026
12 of 14 checks passed
@hiagolcm
hiagolcm deleted the bugfix/fix-autocomplete-history-link-encoding branch May 21, 2026 13:15
@vtex-io-ci-cd

vtex-io-ci-cd Bot commented May 21, 2026

Copy link
Copy Markdown

Your PR has been merged! App is being published. 🚀
Version 2.18.8 → 2.18.9

After the publishing process has been completed (check #vtex-io-releases) and doing A/B tests with the new version, you can deploy your release by running:

vtex deploy vtex.search@2.18.9

After that your app will be updated on all accounts.

For more information on the deployment process check the docs. 📖

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants