Skip to content

Redesign product filters#48

Merged
damianlegawiec merged 6 commits intomainfrom
better-filters-ui
Mar 9, 2026
Merged

Redesign product filters#48
damianlegawiec merged 6 commits intomainfrom
better-filters-ui

Conversation

@Cichorek
Copy link
Copy Markdown
Contributor

@Cichorek Cichorek commented Mar 9, 2026

Summary

  • Redesigned filter UI with dropdown-based filter bar (desktop) and full-screen drawer (mobile), inspired by Saleor storefront
  • Split monolithic ProductFilters.tsx (479 lines, 5 components) into 8 focused files under products/filters/ with barrel export
  • Extracted shared utilities (color-map.ts, price-buckets.ts, filters.ts) into lib/utils/ — DRY across filter components and VariantPicker
  • Fixed stale closure bug in infinite scroll loadMore callback by using refs instead of state
  • Added color swatch support for ~120 named colors with CSS keyword fallback
  • Removed all non-functional comments for cleaner codebase

Test plan

  • Verify filter dropdowns open/close on desktop (option types, price, availability, sort)
  • Verify mobile filter drawer opens from "Filters" button, applies filters, closes
  • Verify filter chips appear below filter bar and can be individually removed or cleared
  • Verify infinite scroll loads more products when scrolling down
  • Verify color swatches render correctly in both filters and variant picker
  • Verify price buckets generate correctly based on product price range
  • Run npm run check — passes ✅
  • Run npx tsc --noEmit — passes ✅

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Redesigned product filtering: unified FilterBar with dropdowns, FilterChips, and a mobile filter drawer; option, price and availability controls, clear/apply actions, and price buckets.
    • New availability labels and improved color swatches for variants.
    • Exposed active filter state to listing hook for consistent behavior.
  • Improvements

    • Improved infinite-scroll/load-more deduplication and loading logic.
  • Style

    • Added slide-in-left animation CSS variable and keyframes.

…cture

Split monolithic ProductFilters.tsx (479 lines) into 8 focused components
under products/filters/. Extract shared utilities (color-map, price-buckets,
filters) into lib/utils/. Fix stale closure bug in infinite scroll by using
refs. Remove all non-functional comments.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 9, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Modularizes product filtering: introduces FilterBar, dropdowns, chips, and mobile drawer; adds utilities for colors, filters, and price buckets; centralizes ActiveFilters type; updates listing/layout/hooks to use ActiveFilters; removes legacy ProductFilters file and adds a small globals.css animation. (≤50 words)

Changes

Cohort / File(s) Summary
Removed legacy component
src/components/products/ProductFilters.tsx
Deleted the monolithic ProductFilters component and its exported types/constants/handlers.
New filter UI (bar & barrel)
src/components/products/filters/ProductFilters.tsx, src/components/products/filters/index.ts
Added memoized FilterBar (desktop entry) and a barrel export for filters.
Dropdown primitives
src/components/products/filters/FilterDropdown.tsx, src/components/products/filters/FilterChips.tsx
Added generic dropdown wrapper and chips renderer for active filters with removal/clear actions.
Dropdown contents
src/components/products/filters/OptionDropdownContent.tsx, src/components/products/filters/PriceDropdownContent.tsx, src/components/products/filters/AvailabilityDropdownContent.tsx, src/components/products/filters/SortDropdownContent.tsx
Added specialized content components for option lists, price buckets, availability, and sort selection with selection callbacks and visual states.
Mobile UX
src/components/products/filters/MobileFilterDrawer.tsx
Added full-screen mobile filter drawer with stagedFilters, accessibility/focus handling, and apply/clear flows.
Layout & listing adjustments
src/app/[country]/[locale]/(storefront)/t/[...permalink]/CategoryProductsContent.tsx, src/components/products/ProductListingLayout.tsx
Stopped forwarding taxonId from CategoryProductsContent; ProductListingLayout props now use activeFilters and totalCount, removed mobile drawer booleans and taxonId; rendering adjusted to use FilterBar and simplified skeleton/empty/grid flows.
Hook & query updates
src/hooks/useProductListing.ts, src/lib/utils/product-query.ts
useProductListing now accepts options object, exposes activeFilters, removes showMobileFilters state, adds loadingMoreRef for load-more dedupe; product-query import path updated for ActiveFilters.
Utilities & types
src/lib/utils/color-map.ts, src/lib/utils/filters.ts, src/lib/utils/price-buckets.ts, src/types/filters.ts
Added color-map (resolveColor/isColorOption), filter helpers (filtersEqual, getActiveFilterCount, normalizeSortKey, getSortLabel, AVAILABILITY_LABELS), price-bucket utilities (PriceBucket, generatePriceBuckets, findMatchingBucket), and centralized ActiveFilters + availability types.
Variant color handling
src/components/products/VariantPicker.tsx
Replaced inline color detection with isColorOption and use resolveColor for swatch backgrounds.
Styling
src/app/globals.css
Added --animate-slide-in-left CSS variable and slide-in-left keyframes; removed a comment line.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant FilterBar
    participant DropdownContent
    participant API
    participant ProductListing

    User->>FilterBar: open/toggle dropdown or drawer
    FilterBar->>DropdownContent: render option/price/availability/sort UI
    User->>DropdownContent: select/toggle option or bucket or availability
    DropdownContent-->>FilterBar: emit change (activeFilters)
    FilterBar->>API: request products with activeFilters
    API-->>ProductListing: return filtered products
    ProductListing->>User: render updated product grid
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 I hopped through chips and drawers so bright,
Swatches gleamed and buckets lit the night,
Dropdowns sang and filters tapped the tune,
I nudged the grid — the products danced in tune,
A rabbit cheers — new UX feels just right! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Redesign product filters' accurately reflects the main objective and primary change in the changeset, which is a comprehensive redesign of the product filter UI architecture.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch better-filters-ui

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
src/lib/utils/filters.ts (1)

24-39: Consider consolidating duplicate sort label mappings.

SORT_LABELS contains duplicate human-readable labels for different API key formats (e.g., "price" and "price asc" both map to "Price: Low to High"). While this provides flexibility for different backend formats, it may lead to maintenance overhead.

If the backend consistently uses one format, consider removing the unused mappings to reduce duplication.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/utils/filters.ts` around lines 24 - 39, SORT_LABELS contains
duplicate human-readable labels for multiple key formats (e.g., "price" vs
"price asc", "available_on" vs "available_on asc/desc", "name" vs "name
asc/desc"); consolidate by picking a canonical key format or normalizing
incoming sort keys before lookup to avoid duplicate entries. Update SORT_LABELS
to only include the canonical keys you expect from the backend (for example
"price", "-price", "available_on", "-available_on", "name", "-name", "manual",
"best_selling") and add a small normalization step where sort keys are converted
from variants like "price asc"/"price desc" to the canonical form before
accessing SORT_LABELS (refer to SORT_LABELS and the normalization function you
add or update).
src/components/products/filters/AvailabilityDropdownContent.tsx (1)

29-29: Type assertion bypasses type safety.

The cast option.id as AvailabilityStatus assumes the SDK's AvailabilityFilter.options[].id values will always be "in_stock" or "out_of_stock". If the SDK returns unexpected values, this could propagate invalid data through the filter system.

Consider adding a runtime guard:

🛡️ Proposed safer implementation
+const isValidAvailability = (id: string): id is AvailabilityStatus =>
+  id === "in_stock" || id === "out_of_stock";

 onClick={() => {
   if (isSelected) {
     onChange(undefined);
   } else {
-    onChange(option.id as AvailabilityStatus);
+    if (isValidAvailability(option.id)) {
+      onChange(option.id);
+    }
   }
 }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/products/filters/AvailabilityDropdownContent.tsx` at line 29,
The code casts option.id to AvailabilityStatus which bypasses type safety; in
AvailabilityDropdownContent replace the direct cast before calling
onChange(option.id as AvailabilityStatus) with a runtime guard that checks
option.id against the allowed AvailabilityStatus values (e.g., "in_stock" and
"out_of_stock") or an AvailabilityStatus enum/const set, and only call onChange
with the validated value, otherwise handle the unexpected case (no-op, fallback,
or error/log) to avoid propagating invalid values from the SDK.
src/components/products/filters/FilterDropdown.tsx (1)

56-57: Minor: Verify aria-haspopup value matches dropdown content.

The aria-haspopup="listbox" is appropriate if the dropdown content contains selectable options. If the content varies (e.g., price range inputs vs. option lists), you might want the parent to pass the appropriate aria-haspopup value.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/products/filters/FilterDropdown.tsx` around lines 56 - 57, The
aria-haspopup value is hardcoded to "listbox" in FilterDropdown
(aria-expanded={isOpen} aria-haspopup="listbox"), which may be incorrect for
varying dropdown content; add a new prop (e.g., ariaHaspopup?: string or a union
of allowed values) to the FilterDropdown component, update its prop
types/interface to include ariaHaspopup, use aria-haspopup={ariaHaspopup ??
'listbox'} when rendering, and document the prop so callers can pass the correct
value for price inputs, dialogs, menus, etc.
src/components/products/filters/MobileFilterDrawer.tsx (1)

42-59: Consider adding focus trap for accessibility.

The drawer lacks focus management, which can cause keyboard users to tab outside the visible drawer while it's open. Consider:

  1. Trapping focus within the drawer when open
  2. Moving focus to the close button when the drawer opens
  3. Returning focus to the trigger element when the drawer closes

This is a recommended improvement for WCAG 2.1 compliance (Success Criterion 2.4.3: Focus Order).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/products/filters/MobileFilterDrawer.tsx` around lines 42 - 59,
The drawer component MobileFilterDrawer.tsx currently lacks focus management;
update it to trap focus inside the drawer while open, move focus to the close
button when opening, and restore focus to the element that opened the drawer
when closing. Implement this by capturing the trigger element (store
document.activeElement before opening), adding a ref to the close button (e.g.,
closeButtonRef) and calling focus on it in useEffect when the drawer opens, and
setting focus back to the saved trigger in the onClose handler; ensure
tab/shift-tab are constrained to elements inside the drawer (either via a small
focus-trap implementation using keydown handlers that cycle focus within the
drawer containerRef or by integrating a lightweight library like
focus-trap-react) and clean up any event listeners on unmount.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/products/filters/ProductFilters.tsx`:
- Around line 80-82: The clearFilters callback currently only resets
optionValues; update the clearFilters function to call onFilterChange with a
payload that clears all filter fields (e.g., set optionValues: [], priceMin:
undefined or null, priceMax: undefined or null, availability: undefined or null)
while intentionally leaving sortBy untouched; locate the clearFilters
useCallback and change its argument to include optionValues plus priceMin,
priceMax and availability resets so the "Clear All" action in FilterChips truly
clears filters.

---

Nitpick comments:
In `@src/components/products/filters/AvailabilityDropdownContent.tsx`:
- Line 29: The code casts option.id to AvailabilityStatus which bypasses type
safety; in AvailabilityDropdownContent replace the direct cast before calling
onChange(option.id as AvailabilityStatus) with a runtime guard that checks
option.id against the allowed AvailabilityStatus values (e.g., "in_stock" and
"out_of_stock") or an AvailabilityStatus enum/const set, and only call onChange
with the validated value, otherwise handle the unexpected case (no-op, fallback,
or error/log) to avoid propagating invalid values from the SDK.

In `@src/components/products/filters/FilterDropdown.tsx`:
- Around line 56-57: The aria-haspopup value is hardcoded to "listbox" in
FilterDropdown (aria-expanded={isOpen} aria-haspopup="listbox"), which may be
incorrect for varying dropdown content; add a new prop (e.g., ariaHaspopup?:
string or a union of allowed values) to the FilterDropdown component, update its
prop types/interface to include ariaHaspopup, use aria-haspopup={ariaHaspopup ??
'listbox'} when rendering, and document the prop so callers can pass the correct
value for price inputs, dialogs, menus, etc.

In `@src/components/products/filters/MobileFilterDrawer.tsx`:
- Around line 42-59: The drawer component MobileFilterDrawer.tsx currently lacks
focus management; update it to trap focus inside the drawer while open, move
focus to the close button when opening, and restore focus to the element that
opened the drawer when closing. Implement this by capturing the trigger element
(store document.activeElement before opening), adding a ref to the close button
(e.g., closeButtonRef) and calling focus on it in useEffect when the drawer
opens, and setting focus back to the saved trigger in the onClose handler;
ensure tab/shift-tab are constrained to elements inside the drawer (either via a
small focus-trap implementation using keydown handlers that cycle focus within
the drawer containerRef or by integrating a lightweight library like
focus-trap-react) and clean up any event listeners on unmount.

In `@src/lib/utils/filters.ts`:
- Around line 24-39: SORT_LABELS contains duplicate human-readable labels for
multiple key formats (e.g., "price" vs "price asc", "available_on" vs
"available_on asc/desc", "name" vs "name asc/desc"); consolidate by picking a
canonical key format or normalizing incoming sort keys before lookup to avoid
duplicate entries. Update SORT_LABELS to only include the canonical keys you
expect from the backend (for example "price", "-price", "available_on",
"-available_on", "name", "-name", "manual", "best_selling") and add a small
normalization step where sort keys are converted from variants like "price
asc"/"price desc" to the canonical form before accessing SORT_LABELS (refer to
SORT_LABELS and the normalization function you add or update).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 94a17d1e-9ce5-4e6e-8f79-e5308594713e

📥 Commits

Reviewing files that changed from the base of the PR and between 6b714fa and 6f7502d.

📒 Files selected for processing (20)
  • src/app/[country]/[locale]/(storefront)/t/[...permalink]/CategoryProductsContent.tsx
  • src/app/globals.css
  • src/components/products/ProductFilters.tsx
  • src/components/products/ProductListingLayout.tsx
  • src/components/products/VariantPicker.tsx
  • src/components/products/filters/AvailabilityDropdownContent.tsx
  • src/components/products/filters/FilterChips.tsx
  • src/components/products/filters/FilterDropdown.tsx
  • src/components/products/filters/MobileFilterDrawer.tsx
  • src/components/products/filters/OptionDropdownContent.tsx
  • src/components/products/filters/PriceDropdownContent.tsx
  • src/components/products/filters/ProductFilters.tsx
  • src/components/products/filters/SortDropdownContent.tsx
  • src/components/products/filters/index.ts
  • src/hooks/useProductListing.ts
  • src/lib/utils/color-map.ts
  • src/lib/utils/filters.ts
  • src/lib/utils/price-buckets.ts
  • src/lib/utils/product-query.ts
  • src/types/filters.ts
💤 Files with no reviewable changes (2)
  • src/components/products/ProductFilters.tsx
  • src/app/[country]/[locale]/(storefront)/t/[...permalink]/CategoryProductsContent.tsx

Comment thread src/components/products/filters/ProductFilters.tsx Outdated
- Fix clearFilters to reset all filter fields (price, availability) not just optionValues
- Replace unsafe AvailabilityStatus casts with runtime type guard
- Add focus trap and ARIA attributes to MobileFilterDrawer
- Normalize SORT_LABELS to remove duplicate entries

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Cichorek Cichorek changed the title Redesign product filters with Saleor-inspired UI Redesign product filters Mar 9, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
src/components/products/filters/ProductFilters.tsx (1)

14-20: Switch the sibling component imports to the @ alias.

This file still uses ./... imports for local filter components, which diverges from the repo import convention.

♻️ Suggested change
-import { AvailabilityDropdownContent } from "./AvailabilityDropdownContent";
-import { FilterChips } from "./FilterChips";
-import { FilterDropdown } from "./FilterDropdown";
-import { MobileFilterDrawer } from "./MobileFilterDrawer";
-import { OptionDropdownContent } from "./OptionDropdownContent";
-import { PriceDropdownContent } from "./PriceDropdownContent";
-import { SortDropdownContent } from "./SortDropdownContent";
+import { AvailabilityDropdownContent } from "@/components/products/filters/AvailabilityDropdownContent";
+import { FilterChips } from "@/components/products/filters/FilterChips";
+import { FilterDropdown } from "@/components/products/filters/FilterDropdown";
+import { MobileFilterDrawer } from "@/components/products/filters/MobileFilterDrawer";
+import { OptionDropdownContent } from "@/components/products/filters/OptionDropdownContent";
+import { PriceDropdownContent } from "@/components/products/filters/PriceDropdownContent";
+import { SortDropdownContent } from "@/components/products/filters/SortDropdownContent";

As per coding guidelines, "Use absolute imports with @ alias (e.g., @/components/..., @/lib/...) instead of relative imports".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/products/filters/ProductFilters.tsx` around lines 14 - 20,
Replace the relative sibling imports with the project’s @ alias: update the
import paths for AvailabilityDropdownContent, FilterChips, FilterDropdown,
MobileFilterDrawer, OptionDropdownContent, PriceDropdownContent, and
SortDropdownContent to use "@/components/..." (or the repo’s configured @ root)
instead of "./..."; keep the same exported symbols and import names so only the
module paths change and no other code needs modification.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/products/filters/AvailabilityDropdownContent.tsx`:
- Around line 24-40: The button only changes visual styling; add accessible
pressed semantics by setting an ARIA pressed state and a clear accessible label:
update the button in AvailabilityDropdownContent to include
aria-pressed={isSelected} (or role="menuitemradio" with
aria-checked={isSelected} if it's inside a single-select menu) and ensure the
visible label (AVAILABILITY_LABELS[option.id] || option.id) remains unchanged so
assistive tech announces which option is active; keep the onClick logic using
isSelected, onChange, and isAvailabilityStatus(option.id) but add the aria
attribute to the button element.

In `@src/components/products/filters/MobileFilterDrawer.tsx`:
- Around line 196-223: The buttons rendering filter options (the element keyed
by option.id and using onToggle, including the CheckIcon and resolveColor usage)
lack accessible selection semantics; update each interactive button to expose
selection state by adding aria-pressed={isSelected} (or
role="checkbox"/aria-checked={isSelected} if you prefer checkbox semantics) and
ensure the button label remains the accessible name (e.g., option.presentation)
so assistive tech can announce it; make the same change to the other button
groups referenced (the blocks around the other ranges: 231-241, 276-295,
321-343) so all four control groups consistently expose selection with either
aria-pressed or checkbox/radio roles and aria-checked attributes.

In `@src/components/products/filters/ProductFilters.tsx`:
- Around line 48-88: Current handlers (handleOptionValueToggle,
handlePriceChange, handleAvailabilityChange, clearFilters, and handleSortChange)
mutate global filters immediately and are passed into MobileFilterDrawer; change
them to update a drawer-local draft state (e.g., stagedFilters) instead of
calling onFilterChange, pass these staged handlers into MobileFilterDrawer, make
clearFilters reset stagedFilters (not the global state), and add/rename a commit
function (e.g., applyStagedFilters) that calls onFilterChange(stagedFilters)
from the drawer footer Confirm/Show results action; ensure closeDropdown/cancel
simply discards stagedFilters so accidental edits are cancelable.

In `@src/components/products/filters/SortDropdownContent.tsx`:
- Around line 20-31: The sort option buttons only indicate selection visually;
update the button in SortDropdownContent to expose selection semantics by adding
a type="button" and setting aria-pressed={isActive} (or aria-current="true" when
active) so assistive tech knows the active option; ensure the onClick handler
(onSortChange(option.id)), getSortLabel(option.id) label, and option.id remain
unchanged and no other behavior is altered.

---

Nitpick comments:
In `@src/components/products/filters/ProductFilters.tsx`:
- Around line 14-20: Replace the relative sibling imports with the project’s @
alias: update the import paths for AvailabilityDropdownContent, FilterChips,
FilterDropdown, MobileFilterDrawer, OptionDropdownContent, PriceDropdownContent,
and SortDropdownContent to use "@/components/..." (or the repo’s configured @
root) instead of "./..."; keep the same exported symbols and import names so
only the module paths change and no other code needs modification.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5720de0d-2d7d-441f-aef8-b7ecc4bf0bbb

📥 Commits

Reviewing files that changed from the base of the PR and between 6f7502d and df61178.

📒 Files selected for processing (6)
  • src/components/products/filters/AvailabilityDropdownContent.tsx
  • src/components/products/filters/MobileFilterDrawer.tsx
  • src/components/products/filters/ProductFilters.tsx
  • src/components/products/filters/SortDropdownContent.tsx
  • src/lib/utils/filters.ts
  • src/types/filters.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/types/filters.ts

Comment thread src/components/products/filters/AvailabilityDropdownContent.tsx
Comment thread src/components/products/filters/MobileFilterDrawer.tsx
Comment thread src/components/products/filters/ProductFilters.tsx
Comment thread src/components/products/filters/SortDropdownContent.tsx
- Add type="button" and aria-pressed to filter buttons in
  AvailabilityDropdownContent, SortDropdownContent, and all
  MobileFilterDrawer button groups (colors, sizes, prices, availability)
- Refactor MobileFilterDrawer to use staged/draft filter state so
  selections are only applied on "Show results" and discarded on close
- Replace relative imports with @/ alias in ProductFilters

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/components/products/filters/ProductFilters.tsx (1)

30-36: Add an explicit return type to the exported component.

FilterBar is part of the public component surface, but its return type is inferred. The repo guideline asks for explicit return types in TS/TSX, so this is worth annotating directly.

♻️ Proposed fix
+import type { ReactElement } from "react";
 import { memo, useCallback, useMemo, useState } from "react";
@@
 export const FilterBar = memo(function FilterBar({
   filtersData,
   filtersLoading,
   activeFilters,
   totalCount,
   onFilterChange,
-}: FilterBarProps) {
+}: FilterBarProps): ReactElement | null {

As per coding guidelines "Use strict TypeScript type checking; define explicit return types for functions and avoid 'any' type".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/products/filters/ProductFilters.tsx` around lines 30 - 36, The
exported memoized component FilterBar currently relies on an inferred return
type; update its declaration to include an explicit return type (e.g.,
React.ReactElement or JSX.Element) so it conforms to TypeScript strict typing
for public components—modify the function signature that uses memo(function
FilterBar({ ... }: FilterBarProps): JSX.Element { ... }) (or equivalent) to
annotate the return type while keeping the use of memo and FilterBarProps
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/products/filters/ProductFilters.tsx`:
- Around line 229-245: The mobile Filters trigger button currently lacks an
explicit type and can inadvertently submit ancestor forms; update the button
element used with onClick={() => setShowMobileDrawer(true)} (the element that
renders FilterIcon, hasActiveFilters, and totalActiveFilters) to include
type="button" so it does not act as a submit control.

---

Nitpick comments:
In `@src/components/products/filters/ProductFilters.tsx`:
- Around line 30-36: The exported memoized component FilterBar currently relies
on an inferred return type; update its declaration to include an explicit return
type (e.g., React.ReactElement or JSX.Element) so it conforms to TypeScript
strict typing for public components—modify the function signature that uses
memo(function FilterBar({ ... }: FilterBarProps): JSX.Element { ... }) (or
equivalent) to annotate the return type while keeping the use of memo and
FilterBarProps unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9a46fb0b-9eec-4c4a-a082-1df71c59cad9

📥 Commits

Reviewing files that changed from the base of the PR and between df61178 and 10bcd53.

📒 Files selected for processing (4)
  • src/components/products/filters/AvailabilityDropdownContent.tsx
  • src/components/products/filters/MobileFilterDrawer.tsx
  • src/components/products/filters/ProductFilters.tsx
  • src/components/products/filters/SortDropdownContent.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/components/products/filters/AvailabilityDropdownContent.tsx
  • src/components/products/filters/MobileFilterDrawer.tsx
  • src/components/products/filters/SortDropdownContent.tsx

Comment thread src/components/products/filters/ProductFilters.tsx
Cichorek and others added 2 commits March 9, 2026 14:58
…to FilterBar

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolve merge conflicts in VariantPicker and useProductListing
by adopting new SDK type names (StoreVariant → Variant, StoreProduct → Product).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
src/components/products/ProductListingLayout.tsx (1)

28-43: Consider adding explicit return type.

Per coding guidelines, functions should have explicit return types. While TypeScript infers the return type correctly here, adding it would improve maintainability.

✨ Suggested enhancement
+import type { ReactElement } from "react";
+
 export function ProductListingLayout({
   products,
   loading,
   ...
-}: ProductListingLayoutProps) {
+}: ProductListingLayoutProps): ReactElement {

As per coding guidelines: "Use strict TypeScript type checking; define explicit return types for functions."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/products/ProductListingLayout.tsx` around lines 28 - 43, The
ProductListingLayout component lacks an explicit return type; update its
signature (the export function ProductListingLayout(...) declaration) to include
an explicit React return type such as React.ReactElement or JSX.Element (e.g.,
export function ProductListingLayout(...): React.ReactElement) and, if your
project’s TSX setup requires it, add an import type { ReactElement } from
'react' and use that type to satisfy the linter and strict TypeScript rules.
src/hooks/useProductListing.ts (1)

135-136: Consider grouping loadingMoreRef with other refs.

The loadingMoreRef is declared separately from the other refs (lines 38-50). For consistency and readability, consider moving it alongside hasMoreRef and other pagination-related refs.

📍 Suggested placement

Move this declaration to approximately line 40, after hasMoreRef:

  const hasMoreRef = useRef(false);
+ const loadingMoreRef = useRef(false);
  const filtersRef = useRef<ActiveFilters>({ optionValues: [] });

Then remove lines 135-136.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/useProductListing.ts` around lines 135 - 136, Move the
loadingMoreRef declaration next to the other pagination refs for consistency:
locate the hasMoreRef and related refs in useProductListing and add the const
loadingMoreRef = useRef(false); directly after them, then remove the separate
declaration currently named loadingMoreRef declared later in the file; update
any references to loadingMoreRef to use the moved ref (no other logic changes).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/components/products/ProductListingLayout.tsx`:
- Around line 28-43: The ProductListingLayout component lacks an explicit return
type; update its signature (the export function ProductListingLayout(...)
declaration) to include an explicit React return type such as React.ReactElement
or JSX.Element (e.g., export function ProductListingLayout(...):
React.ReactElement) and, if your project’s TSX setup requires it, add an import
type { ReactElement } from 'react' and use that type to satisfy the linter and
strict TypeScript rules.

In `@src/hooks/useProductListing.ts`:
- Around line 135-136: Move the loadingMoreRef declaration next to the other
pagination refs for consistency: locate the hasMoreRef and related refs in
useProductListing and add the const loadingMoreRef = useRef(false); directly
after them, then remove the separate declaration currently named loadingMoreRef
declared later in the file; update any references to loadingMoreRef to use the
moved ref (no other logic changes).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 72ee9ea7-4653-49b5-97a6-c5362b12c500

📥 Commits

Reviewing files that changed from the base of the PR and between ec0af98 and 88c680f.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • src/components/products/ProductListingLayout.tsx
  • src/components/products/VariantPicker.tsx
  • src/hooks/useProductListing.ts

…ether

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Cichorek Cichorek requested a review from damianlegawiec March 9, 2026 14:48
@damianlegawiec damianlegawiec merged commit 99a48f9 into main Mar 9, 2026
4 checks passed
This was referenced Mar 9, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Mar 19, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Apr 7, 2026
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