From fbcc5e3d795c72d680caac8c8ea1d165a82d0815 Mon Sep 17 00:00:00 2001 From: Jonas Jesus Date: Thu, 18 Jun 2026 16:46:22 -0300 Subject: [PATCH] feat(vtex/plp): rich typing + JSDoc on PLPProps for CMS admin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CMS admin generates edit forms from the loader's Props TypeScript types. PLPProps had weak typing (sort?: string, fuzzy?: string) and no JSDoc, so the admin rendered plain text inputs and raw field names ('Count') instead of dropdowns and labelled fields. - sort?: string → sort?: Sort (8-value union → dropdown) - fuzzy?: string → fuzzy?: LabelledFuzzy ('automatic'|'disabled'|'enabled' → dropdown) - add @title/@description to every field (e.g. count → 'Items per page') Also fixes a latent runtime bug: props.fuzzy (a friendly LabelledFuzzy like 'automatic') was passed raw to the IS API, which expects '0'|'1'|'auto'. Now mapped via mapLabelledFuzzyToFuzzy(); the raw URL ?fuzzy= param still passes through. Related: DECO-5296 (CMS Studio loader form parity) Co-Authored-By: Claude Sonnet 4.6 (1M context) --- vtex/inline-loaders/productListingPage.ts | 39 ++++++++++++++++++++--- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/vtex/inline-loaders/productListingPage.ts b/vtex/inline-loaders/productListingPage.ts index 6032317..0a921ac 100644 --- a/vtex/inline-loaders/productListingPage.ts +++ b/vtex/inline-loaders/productListingPage.ts @@ -7,7 +7,7 @@ import { toFacetPath, } from "../client"; import { pickSku, toProduct } from "../utils/transform"; -import type { Product as ProductVTEX } from "../utils/types"; +import type { Product as ProductVTEX, Sort } from "../utils/types"; export interface SelectedFacet { key: string; @@ -42,12 +42,40 @@ export const mapLabelledFuzzyToFuzzy = (label?: LabelledFuzzy): "0" | "1" | "aut }; export interface PLPProps { + /** + * @title Query + * @description Overrides the search term used to fetch the listing. + */ query?: string; + /** + * @title Items per page + * @description Number of products per page to display. + */ count?: number; - sort?: string; - fuzzy?: string; + /** + * @title Sorting + * @description Order in which products are returned. + */ + sort?: Sort; + /** + * @title Fuzzy + * @description Controls Intelligent Search typo tolerance. + */ + fuzzy?: LabelledFuzzy; + /** + * @title Page offset + * @description Starting page (0-indexed) for the listing query. + */ page?: number; + /** + * @title Selected Facets + * @description Override selected facets from the URL (e.g. force a collection). + */ selectedFacets?: SelectedFacet[]; + /** + * @title Hide Unavailable Items + * @description Do not return out-of-stock items. + */ hideUnavailableItems?: boolean; /** Injected by CMS resolve — the matched page path (e.g. "/pisos/piso-vinilico-clicado") */ __pagePath?: string; @@ -301,7 +329,10 @@ export default async function vtexProductListingPage(props: PLPProps): Promise 0 ? rawCount : 12; const sort = props.sort || pageUrl?.searchParams.get("sort") || ""; - const fuzzy = props.fuzzy ?? pageUrl?.searchParams.get("fuzzy") ?? undefined; + // props.fuzzy is a friendly LabelledFuzzy ("automatic"|…) — translate it to the + // raw IS API value. The URL param is already a raw value, so it passes through. + const fuzzy = + mapLabelledFuzzyToFuzzy(props.fuzzy) ?? pageUrl?.searchParams.get("fuzzy") ?? undefined; const pageFromUrl = pageUrl?.searchParams.get("page"); const rawPage = props.page ?? (pageFromUrl ? Number(pageFromUrl) - 1 : 0); const page = Number.isFinite(rawPage) && rawPage >= 0 ? Math.floor(rawPage) : 0;