feat(ep-commerce): tiered designer data surface for catalog-search (ADR-0011)#356
Open
field123 wants to merge 1 commit into
Open
feat(ep-commerce): tiered designer data surface for catalog-search (ADR-0011)#356field123 wants to merge 1 commit into
field123 wants to merge 1 commit into
Conversation
…DR-0011) Bring the catalog-search components up to the ADR-0006/0007 insulation floor with one governing principle — progressive disclosure: the common case is a discoverable, zero-lookup, zero-wiring action, every advanced path reachable underneath, and no enhancement removes a capability. Slice 1 — discoverable, tiered hit data surface - D7: shared base Product contract. Extract the hit normalizer into utils/normalize-hit.ts (normalizeSearchHit → SearchHitProduct extends Product), built from the shared primitives the PDP path uses (formatCurrency, buildExtensionsMap, normalizeExtensions). Absent base fields read as absent, never throw. - D1: EPSearchHits publishes per hit currentProduct.fields.<key> (Tier 1, flat, slug-free, from a configurable primaryExtensionTemplate prop) and the slug-keyed $ctx.productExtensions Proxy map (Tier 2). Raw extensions/rawData retained (Tier 3). - D3 (fields): fold highlight (auto|on|off) and a titlecase format into EPProductField — one field-render surface across PDP and search; the html:true footgun is contained, not handed to designers. - D8/D9: EPSearchHits ships a working unstyled default card built from the field components and collapses to one render path (pure inner fed items, hook in the data wrapper) per the #305 contract. Slice 2 — zero-wiring facets and pagination - D2: auto-wire refinement and single-select items via the prop-getter pattern (click + keyboard + role + selection aria), a defaulted-on disableable prop; off is byte-for-byte the prior function-on-context behaviour. - D5/D6: new EPSingleSelectFacet (useMenu, replace semantics, radiogroup a11y) with an opt-in All pseudo-item; EPRefinementList loses singleSelect but its per-item context is unchanged so slot content ports. - D4: EPSearchPagination exposes a windowed pageItems model (ellipsis sentinels, first/last/current flags, per-item bound goTo, window-size prop); raw pages + goTo retained. Cross-cutting - D3 (provider): EPCatalogSearchProvider excludeVariationChildren (default on) emits meta.product_types:!=child, AND'd with baseFilter. - D8: group the family under one "EP Catalog Search" insert-menu section; batteries-included provider scaffold. All enhancements opt-out to prior behaviour; existing instances keep working. Closes #355.
edda7c1 to
eac0654
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Brings the catalog-search components up to the ADR-0006/0007 insulation floor under one governing principle — progressive disclosure: the common case is a discoverable, zero-lookup, zero-wiring action; every advanced path stays reachable underneath; and no enhancement removes a capability (every convenience opt-outs to the exact prior behaviour).
Implements ADR-0011 ("Catalog-search designer ergonomics: a tiered, progressively-disclosed data surface"). Closes #355 (under the #304 umbrella).
Slice 1 — discoverable, tiered hit data surface
Productcontract): extract the hit normalizer intoutils/normalize-hit.ts(normalizeSearchHit→SearchHitProduct extends Product), built from the same primitives the PDP path uses (formatCurrency,buildExtensionsMap,normalizeExtensions). A hit'scurrentProductis shape-identical to the PDP's, so the ADR-0006 field components and bindings work unchanged across PDP + search. Absent base fields read as absent (never throw).EPSearchHitspublishes per hit$ctx.currentProduct.fields.<key>: flat, slug-free, null-safe, flattening a configurableprimaryExtensionTemplateprop (autocompletes in the picker, no(… || {}), no slug brackets);$ctx.productExtensions["<slug>"].field: the ADR-0007 slug-keyed Proxy map, scoped per hit;extensions/rawDataretained.highlight: auto | on | offand atitlecaseformat intoEPProductField— one field-render surface across PDP + search. Only the backend's<mark>markup is ever rendered as HTML, never the raw value, so thehtml:truefootgun is removed rather than handed to designers.EPSearchHitsships a batteries-included-but-unstyled default card built from the field components, and collapses to a single render (pure inner feditems, the InstantSearch hook in the data wrapper) per the PRD: EP catalog-search components must honour designer styling (headless styling contract) #305 headless-styling contract.Slice 2 — zero-wiring facets & pagination
onClick+ keyboard +role+ selectionaria-*). A defaulted-on, disableableautoWireprop; OFF is byte-for-byte today's function-on-context behaviour, andtogglestays on context as the escape.EPSingleSelectFacet): new component (useMenu, replace semantics, radiogroup a11y) splittingsingleSelectout ofEPRefinementList. Per-item context is identical, so slot content ports across a mode change. Opt-inincludeAllOptionemits an "All" pseudo-item (refined when nothing is, clears on select) flaggedisAllOption.EPRefinementList+EPClearRefinementskeep working unchanged.EPSearchPaginationexposes a windowedpageItemsmodel — ellipsis sentinels,isFirst/isLast/isCurrentflags, a per-item pre-boundgoTo, and awindowSizeprop. Rawpages: number[]+goToretained.Cross-cutting
EPCatalogSearchProvidergainsexcludeVariationChildren(default on) emittingmeta.product_types:!=child, AND'd withbaseFilter. Deliberate non-additive default — variation children near-universally don't belong in hits, and the package has no external consumers depending on the old behaviour. Turn off to include them."EP Catalog Search"insert-menu section; the provider's default slot is a complete, working unstyled scaffold (search box + scope facet + hits + empty + pagination).Verification
catalog-search-componentsjest suite + new unit/acceptance tests:normalize-hit,pagination-window,product-field-highlight,format-valuetitlecase, providerexcludeVariationChildren,EPSingleSelectFacet).assertHeadlessStylingContractpasses for the newEPSingleSelectFacetand the refactoredEPRefinementList/EPSearchHits.tsc --noEmitclean across all touched files.Behaviour change to call out
excludeVariationChildrendefaults on — searches that previously returned PXM variation children will stop doing so. This is intentional and documented above; set the prop tofalseto restore the old behaviour.