filtering-system

📁 saleor/storefront 📅 9 days ago
3
总安装量
3
周安装量
#62469
全站排名
安装命令
npx skills add https://github.com/saleor/storefront --skill filtering-system

Agent 安装分布

amp 3
github-copilot 3
codex 3
kimi-cli 3
gemini-cli 3
cursor 3

Skill 文档

Product Filtering System

Source: Saleor API – ProductFilterInput – Available server-side filter options

When to Use

Use this skill when:

  • Modifying product list filtering or sorting
  • Adding new filter types
  • Working on category, collection, or products pages
  • Understanding server-side vs client-side filtering

Instructions

Filter Architecture

Filter Processing Why
Categories ✅ Server-side Uses Saleor’s ProductFilterInput.categories
Price ✅ Server-side Uses Saleor’s ProductFilterInput.price
Sort ✅ Server-side Uses Saleor’s ProductOrder
Colors ❌ Client-side Saleor needs attribute IDs
Sizes ❌ Client-side Same as colors

Key Files

File Purpose
src/ui/components/plp/filter-utils.ts All filter utilities (server + client)
src/ui/components/plp/FilterBar.tsx Filter UI (dropdowns, mobile sheet)
src/ui/components/plp/useProductFilters.ts Hook consolidating filter logic

Server-Side Filtering

Category slugs in URL are resolved to IDs:

// In page.tsx (server component)
import { resolveCategorySlugsToIds, buildFilterVariables } from "@/ui/components/plp/filter-utils";

const categorySlugs = searchParams.categories?.split(",") || [];
const categoryMap = await resolveCategorySlugsToIds(categorySlugs);
const categoryIds = Array.from(categoryMap.values()).map((c) => c.id);

const filter = buildFilterVariables({
	priceRange: searchParams.price,
	categoryIds,
});

// Pass to GraphQL query
const { products } = await executePublicGraphQL(ProductListDocument, {
	variables: { channel, filter },
});

Client-Side Filtering

Colors and sizes are filtered after fetch:

import { filterProducts, extractColorOptions } from "@/ui/components/plp/filter-utils";

// Extract available options
const colorOptions = extractColorOptions(products, selectedColors);

// Apply filters
const filtered = filterProducts(products, {
	colors: selectedColors,
	sizes: selectedSizes,
});

Using the Hook

The useProductFilters hook consolidates all filter logic:

"use client";
import { useProductFilters } from "@/ui/components/plp/useProductFilters";

function ProductsClient({ products, resolvedCategories }) {
	const {
		filteredProducts,
		colorOptions,
		sizeOptions,
		selectedColors,
		handleColorToggle,
		handleSortChange,
		activeFilters,
	} = useProductFilters({
		products,
		resolvedCategories,
		enableCategoryFilter: true,
	});

	return (
		<FilterBar
			colorOptions={colorOptions}
			selectedColors={selectedColors}
			onColorToggle={handleColorToggle}
			// ...
		/>
	);
}

Static Price Ranges

Price ranges are static to avoid UI flicker when filtering:

import { STATIC_PRICE_RANGES_WITH_COUNT } from "@/ui/components/plp/filter-utils";

// Returns: [
//   { label: "Under $50", value: "0-50", count: 0 },
//   { label: "$50 - $100", value: "50-100", count: 0 },
//   ...
// ]

Examples

Adding a New Server-Side Filter

  1. Update buildFilterVariables in filter-utils.ts:
export function buildFilterVariables(params: {
	priceRange?: string | null;
	categoryIds?: string[];
	inStock?: boolean; // New filter
}): ProductFilterInput | undefined {
	// ... existing code ...

	if (params.inStock) {
		filter.stockAvailability = "IN_STOCK";
		hasFilter = true;
	}
}
  1. Parse from URL in page.tsx and pass to the function.

Anti-patterns

❌ Don’t filter categories client-side – Use server-side with IDs
❌ Don’t generate dynamic price ranges – Use static ranges
❌ Don’t hide selected filters – Always show so users can deselect