Skip to content

feat: add price slider filter to Shop page - #11

Open
vedanshujain wants to merge 1 commit into
mainfrom
feat/price-slider
Open

feat: add price slider filter to Shop page#11
vedanshujain wants to merge 1 commit into
mainfrom
feat/price-slider

Conversation

@vedanshujain

Copy link
Copy Markdown
Contributor

Summary

  • Adds a dual-handle price range slider to the Shop page that auto-detects min/max prices from the catalog
  • Filters products via WooCommerce Store API min_price/max_price params with 300ms debounce
  • Pure React implementation — no new dependencies

Changes

  • New: PriceSlider.jsx — dual-handle range slider component with overlapping native <input type="range"> elements
  • New: PriceSlider.css — themed styles using design tokens (hairline track, circular thumbs)
  • Modified: products.js — added getPriceRange() (paginates all products), minPrice/maxPrice support in getProducts()
  • Modified: ProductList.jsx — accepts price filter props, contextual empty message
  • Modified: Shop.jsx — price range state, debounce via useRef, conditional slider rendering

Test plan

  • Verify slider appears on /shop but NOT on / (Home)
  • Drag handles to filter products — results update after ~300ms
  • Handles cannot cross each other
  • Reset button appears when range is narrowed; restores full range on click
  • "No products in this price range" shows when filter excludes all products
  • Works in demo mode (npm run dev without WordPress)
  • Keyboard navigation (Tab + arrows) works on both handles

🤖 Generated with Claude Code

Adds a dual-handle price range slider that auto-detects the min/max
prices from the catalog and filters products in real-time with 300ms
debounce. Pure React, no extra dependencies.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a dual-handle price range slider to the Shop page, allowing users to filter products by minimum and maximum prices. The changes include the new PriceSlider component, updated product API methods to handle price filtering and calculate the catalog's price range, and integration with the Shop page and ProductList component. Feedback on these changes highlights several critical improvements: resolving a potential crash in getPriceRange by using optional chaining, addressing a performance bottleneck caused by sequential pagination requests, implementing a 'push' behavior to prevent overlapping slider handles from getting stuck, and adding visible focus styles to ensure keyboard accessibility.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +118 to +119
const unit = allPriceData[0].prices.currency_minor_unit ?? 2;
const symbol = allPriceData[0].prices.currency_symbol ?? '$';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using direct property access on allPriceData[0].prices is unsafe here. If the first product in the fetched batch has missing or incomplete price data, this will throw a TypeError and crash the page. Use optional chaining to safely access these properties, matching the pattern used in the demo mode fallback.

Suggested change
const unit = allPriceData[0].prices.currency_minor_unit ?? 2;
const symbol = allPriceData[0].prices.currency_symbol ?? '$';
const unit = allPriceData[0].prices?.currency_minor_unit ?? 2;
const symbol = allPriceData[0].prices?.currency_symbol ?? '$';

Comment on lines +104 to +112
while (hasMore) {
const batch = await storeApiRequest('products', {
query: { _fields: 'prices', per_page: 100, page },
});
if (!Array.isArray(batch) || batch.length === 0) break;
allPriceData = allPriceData.concat(batch);
hasMore = batch.length === 100;
page += 1;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Paginating through all products sequentially in a while loop to find the min/max prices is a major performance bottleneck. For stores with many products, this will result in numerous sequential API requests on every mount of the Shop page, leading to slow load times and high server load.

Consider using the WooCommerce Store API's dedicated /products/collection-data endpoint, which returns the min/max prices of the collection in a single lightweight request, or at least caching the result.

Comment on lines +30 to +38
function handleMinChange(e) {
const value = Number(e.target.value);
onChange(Math.min(value, currentMax), currentMax);
}

function handleMaxChange(e) {
const value = Number(e.target.value);
onChange(currentMin, Math.max(value, currentMin));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

When the minimum and maximum thumbs meet or overlap, the thumb with the lower z-index becomes completely unreachable and cannot be dragged. This causes the slider to get stuck, preventing the user from expanding the range again.

To resolve this, we can implement a 'push' behavior where dragging the minimum handle past the maximum handle (or vice versa) automatically updates the other handle, allowing them to cross smoothly without getting stuck.

Suggested change
function handleMinChange(e) {
const value = Number(e.target.value);
onChange(Math.min(value, currentMax), currentMax);
}
function handleMaxChange(e) {
const value = Number(e.target.value);
onChange(currentMin, Math.max(value, currentMin));
}
function handleMinChange(e) {
const value = Number(e.target.value);
if (value > currentMax) {
onChange(currentMax, value);
} else {
onChange(value, currentMax);
}
}
function handleMaxChange(e) {
const value = Number(e.target.value);
if (value < currentMin) {
onChange(value, currentMin);
} else {
onChange(currentMin, value);
}
}

Comment on lines +59 to +60
outline: none;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The price slider inputs have outline: none without any alternative focus styles. This makes the slider completely inaccessible to keyboard users, as there is no visual indication of which handle is currently focused when tabbing through the page.

Please add visible focus styles (e.g., using :focus-visible) for the slider thumbs to ensure compliance with accessibility standards.

  outline: none;
}

.price-slider__input:focus-visible::-webkit-slider-thumb {
  box-shadow: 0 0 0 3px var(--color-accent-subtle);
  border-color: var(--color-accent);
}

.price-slider__input:focus-visible::-moz-range-thumb {
  box-shadow: 0 0 0 3px var(--color-accent-subtle);
  border-color: var(--color-accent);
}

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.

1 participant