Skip to content

Feat/zipcode soft refresh query allowlist - #28

Merged
hiagolcm merged 17 commits into
mainfrom
feat/zipcode-soft-refresh-query-allowlist
Jun 30, 2026
Merged

Feat/zipcode soft refresh query allowlist#28
hiagolcm merged 17 commits into
mainfrom
feat/zipcode-soft-refresh-query-allowlist

Conversation

@hiagolcm

@hiagolcm hiagolcm commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

What problem is this solving?

Jira Card

After updating location info (zipcode or shipping-method) the component triggers a full reload of the page.
This PR changes the behavior to a soft refresh. Instead of refreshing the whole page, only the search related queries are refreshed

How to test it?

workspace

Screenshots or example usage:

output2.mp4

Describe alternatives you've considered, if any.

Related to / Depends on

How does this PR make you feel? 🔗

![](put .gif link here - can be found under "advanced" on giphy)

@vtex-io-ci-cd

vtex-io-ci-cd Bot commented Jun 11, 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 Jun 11, 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 🎉🎉

@github-actions

github-actions Bot commented Jun 11, 2026

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 dc36d60

hiagolcm and others added 13 commits June 24, 2026 13:11
Replace `location.reload()` in the five `useDeliveryPromise` callsites
(`submitZipcode`, `selectPickup`, `selectDeliveryShippingOption`,
`RESET_FULFILLMENT_METHOD`, `CLEAR_ZIPCODE`) with
`apolloClient.reFetchObservableQueries(true)`. The `POST /api/sessions`
response already applies the new `vtex_segment` cookie (which carries the
delivery / pickup hashes) before our await resolves, so by the time we
refetch, every `@withSegment` observable query rides the new segment.

Behavior:

- Apollo client is read via `useApolloClient` from react-apollo. When the
  provider is missing or the refetch throws, we fall back to a hard
  reload so the storefront always converges on the post-session state.
- `setIsLoading` keeps a spinner during the refetch window now that the
  React tree no longer disappears.
- The `reload` arg on `UPDATE_ZIPCODE` preserves its current contract:
  when `false`, the caller (e.g. PLP facet navigation) owns navigation
  and we neither refetch nor reload.

Tests:

- Mock `react-apollo` so existing tests continue to pass and add a new
  describe block covering the five soft-refresh paths plus the
  reload-fallback (Apollo throws, no provider mounted).

Co-authored-by: Cursor <cursoragent@cursor.com>
Adds failing tests (observed red before implementation) for replacing the
broad reFetchObservableQueries with a targeted refetch of only the
allowlisted store-resources queries (facetsV2, productSearchV3, Products,
Product, sponsoredProducts, ProductRecommendations), resetting
productSearchV3 to page 1 (from: 0), and falling back to location.reload()
when Apollo is unavailable, internals are inaccessible, or a refetch throws.

Refs specs/zipcode-soft-refresh-query-allowlist/spec.md

Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the broad apolloClient.reFetchObservableQueries(true) in
refreshStorefront with a targeted refetch of only the allowlisted
store-resources queries that vary by @withSegment: facetsV2,
productSearchV3, Products, Product, sponsoredProducts, and
ProductRecommendations.

apollo-client 2.6 has no public subset-refetch API, so the new helper
refetchAllowlistedQueries enumerates the QueryManager's observable queries
and filters by operation name, settling each refetch independently.
productSearchV3 is reset to page 1 (from: 0) via QUERY_VARIABLE_OVERRIDES
while preserving its other variables. Falls back to location.reload() when
Apollo is unavailable, internals are inaccessible, or a refetch throws.

Refs specs/zipcode-soft-refresh-query-allowlist/spec.md

Co-authored-by: Cursor <cursoragent@cursor.com>
Update the Unreleased soft-refresh entry to reflect refetching only the
allowlisted store-resources queries (with productSearchV3 reset to page 1)
instead of the broad reFetchObservableQueries.

Co-authored-by: Cursor <cursoragent@cursor.com>
…refresh

Without a reload, selecting delivery/pickup or resetting the fulfillment
method must (1) optimistically update deliveryPromiseMethod / selectedPickup
in context state, and (2) signal the shipping-method modal to close.

Adds failing tests (observed red before implementation):
- SELECT_DELIVERY_SHIPPING_OPTION → method 'delivery', pickup cleared
- UPDATE_PICKUP → method 'pickup-in-point', selected pickup stored
- RESET_FULFILLMENT_METHOD → method/pickup cleared
- ShippingMethodSelector closes the modal when fulfillmentSelectionAppliedId
  increments

Refs specs/zipcode-soft-refresh-query-allowlist/spec.md

Co-authored-by: Cursor <cursoragent@cursor.com>
The soft refresh keeps the React tree alive, so two behaviors the page
reload used to provide for free are now handled explicitly:

1. State sync: selectPickup, SELECT_DELIVERY_SHIPPING_OPTION and
   RESET_FULFILLMENT_METHOD update deliveryPromiseMethod / selectedPickup
   optimistically (the values the reload re-derived from the segment),
   so the shipping-method selector reflects the chosen option immediately.

2. Modal close: a new context counter (fulfillmentSelectionAppliedId)
   increments when a fulfillment method is applied; shipping-method-selector
   watches it and closes the modal. The unavailable-items abort flow is
   preserved (the modal reopens only when the selection is not applied).

Refs specs/zipcode-soft-refresh-query-allowlist/spec.md

Co-authored-by: Cursor <cursoragent@cursor.com>
…efresh

Adds failing tests (observed red before implementation):
- selecting a pickup toggles loading exactly once (no validate→idle→select
  double spinner)
- productSearchV3 refetch resets to the first page on BOTH from and to
  (from: 0, to: page size - 1), preserving other variables
- removePageQueryParam drops the page query string without a reload

Refs specs/zipcode-soft-refresh-query-allowlist/spec.md

Co-authored-by: Cursor <cursoragent@cursor.com>
Issue 1 — double spinner: validateCartItems used to turn the loading flag
off on its success path, then the selection (selectPickup /
selectDeliveryShippingOption) turned it on again, producing a
loading→idle→loading→idle flicker. validateCartItems now keeps loading on
when the action will proceed and only turns it off when it surfaces the
unavailable-items modal, so the continuation owns a single loading→idle
cycle. Defensive early-return guards in the selection handlers clear loading
so it can never get stuck on.

Issue 2 — pagination reset: refetching productSearchV3 only reset `from`.
It now resets to the first page on both `from` and `to` (from: 0,
to: to - from, preserving the page size the way search-result derives it),
and drops the `page` query string from the URL via history.replaceState (no
reload) so the reset survives a later re-render or reload. The per-query
override is now a function of the query's current variables.

Refs specs/zipcode-soft-refresh-query-allowlist/spec.md

Co-authored-by: Cursor <cursoragent@cursor.com>
Assert that a location change on page >= 2 reloads (resetting search-result's
useFetchMore counter) instead of soft-refetching, and that
removePageQueryParam reports the previous page number.

Co-authored-by: Cursor <cursoragent@cursor.com>
search-result's useFetchMore pagination counter is seeded at mount and only
reset on query/map/orderBy/priceRange changes, so a soft refetch after a
segment change leaves the next "load more" jumping to the stale page N+1.
When the shopper is on page >= 2, refreshStorefront now reloads (after
stripping the page param so it lands on page 1), re-initializing that counter;
page 1 keeps the soft refresh. removePageQueryParam returns the previous page
to drive the decision.

Co-authored-by: Cursor <cursoragent@cursor.com>
…ses loading

Co-authored-by: Cursor <cursoragent@cursor.com>
Wrap `updateSession` in `selectPickup`, `selectDeliveryShippingOption`,
and `RESET_FULFILLMENT_METHOD` with try/catch. On failure the previous
`deliveryPromiseMethod` / `selectedPickup` values are restored, the
loading flag is released (no stuck spinner), the failure is logged, and
the soft refresh is skipped — the UI never claims a selection the session
does not hold. `persistPickupPreference` is also moved to run only after
a successful write so a failed write cannot poison the stored pickup.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@hiagolcm
hiagolcm force-pushed the feat/zipcode-soft-refresh-query-allowlist branch from 61503b6 to 43f33bd Compare June 24, 2026 16:18
hiagolcm and others added 4 commits June 24, 2026 13:58
…ock lifecycle

Drive the Apollo-client scenario through a module-level override the
react-apollo mock factory reads, instead of reassigning the imported
useApolloClient binding (unreliable across compilers that bind named
imports at import time). Reset runtime query / re-spy render-runtime and
the override in beforeEach for full isolation, and use mockRejectedValue
so the throwing-refetch case is robust to repeated calls.

Co-authored-by: Cursor <cursoragent@cursor.com>
Use the explicit window.location reference instead of the bare global
`location` so the hard-reload fallback always resolves to the current
window.location. The bare global binding can desync from a redefined
window.location in some jsdom environments, which made the reload
fallback no-op under CI. Functionally identical in the browser.

Co-authored-by: Cursor <cursoragent@cursor.com>
The three soft-refresh reload-fallback tests reassigned a module-level
override binding from the test body, which the CI test runner did not
honor through the hoisted jest.mock factory closure (the binding read
stale, so the hook always got the healthy in-memory client and never hit
the location.reload fallback). Read the client through a useApolloClient
spy re-established in beforeEach (mirroring the render-runtime query mock)
and build each failure scenario as a fully-constructed value assigned to
the live binding before render.

Co-authored-by: Cursor <cursoragent@cursor.com>
@hiagolcm
hiagolcm merged commit f7de206 into main Jun 30, 2026
5 checks passed
@vtex-io-ci-cd

vtex-io-ci-cd Bot commented Jun 30, 2026

Copy link
Copy Markdown

Your PR has been merged! App is being published. 🚀
Version 1.1.4 → 1.2.0

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.delivery-promise-components@1.2.0

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