feat(ep-commerce): headless styling contract + EPSearchBox provider refactor (#305, #308)#309
Merged
Merged
Conversation
…efactor (#305, #308) PRD #305 - Headless styling contract for the nine catalog-search components: - New `headless-styling.ts` deep module: idempotent injection of a single `<style data-ep-headless-styles>` tag with `:where()` rules at zero specificity, encapsulating the structural CSS the components need (`position: relative` for the search box wrapper, `width: 100%; align-self: stretch` for the catalog provider). - New `assertHeadlessStylingContract` test helper asserts (a) className lands on the documented leaf, (b) no inline appearance styles, (c) editor and runtime renders produce structurally equivalent leaves. - Refactored EPCatalogSearchProvider: dropped DEFAULT_PROVIDER_WRAPPER_STYLE from six render sites; structural CSS now ships from the :where() block. - Refactored EPSearchHits: added data-ep-search-hits attribute for contract test consistency; the gridStyle inline pattern is preserved (justified workaround for Plasmic's className-strip on display/grid). - README documents the four-rule contract, leaf-element table, structural CSS pattern, and migration notes. PRD #308 - EPSearchBox slot composition refactor: EPSearchBox is now a render-children-only provider. It calls useSearchBox(), manages debounce, and exposes searchFieldData (value, displayValue, isEmpty) via DataProvider plus setValue/clear refActions. The visible chrome (input, clear button) is owned by the designer - they drop Plasmic-controlled `<input>` and `<button>` into the slot, which escape Plasmic's TPL_COMPONENT_PROPS appearance filter. - New SearchFieldData type in design-time-data.ts. - EPSearchBox forwardRef + useImperativeHandle exposes setValue (debounced) and clear (cancels pending refine, resets state). - Metadata change is additive: deprecated placeholder/autoFocus/showClear props preserved as hidden no-ops to keep existing project bundles valid (per pattern_bundle_invariant_orphans). - README "Composing EPSearchBox" section explains the new wiring contract. Tests: - 45 contract assertions (5 per component x 9 components, except EPSearchBox which has its own behavioural tests). - 8 new EPSearchBox provider tests: searchFieldData exposure, setValue updates value, setValue debounces refine, clear resets and calls useSearchBox().clear(), displayValue diverges from value during debounce, previewState='withData' forces mock outside editor, meta exposes refActions and providesData. - All 1522 EP commerce jest tests pass.
This was referenced May 7, 2026
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
Two layered PRDs delivered together because PRD #308 is the architectural completion of PRD #305:
TPL_COMPONENT_PROPS. The fix is to refactorEPSearchBoxfrom a chrome-rendering code component into a render-children-only provider, pushing the visible<input>and<button>into the slot as Plasmic-controlled tags (which escape the filter).Changes
New files
headless-styling.ts— deep module: idempotentinjectHeadlessStyles()+useHeadlessStyling()hook. Mounts a single<style data-ep-headless-styles>tag with:where()rules at zero specificity for[data-ep-search-box](legacy, still useful for non-provider use) and[data-ep-catalog-search-provider].__tests__/headless-styling-contract.tsx—describeHeadlessStylingContractshared test helper: asserts className lands on documented leaf, no inline appearance styles, editor + runtime structurally equivalent.Modified
EPSearchBox.tsx— provider-only, no DOM. forwardRef + useImperativeHandle exposessetValue(value)(debounced) andclear()ref-actions. Wraps children in<DataProvider name="searchFieldData" data={{value, displayValue, isEmpty}}>. Deprecated props (placeholder/autoFocus/showClear) preserved as hidden no-ops (perpattern_bundle_invariant_orphans) so existing project bundles stay valid.EPCatalogSearchProvider.tsx— droppedDEFAULT_PROVIDER_WRAPPER_STYLEfrom six render sites; structural CSS now ships via the:where()block.EPSearchHits.tsx— addeddata-ep-search-hitsattribute for contract-test consistency. The inlinegridStylepattern is preserved (the only justified inline-style workaround in the catalog-search codebase, due to Plasmic's className-strip ondisplay: grid).design-time-data.ts— newSearchFieldDatatype andMOCK_SEARCH_FIELD_DATA.__tests__/catalog-search-components.test.tsx— 45 contract assertions (5 per component × 9 components, except EPSearchBox which has its own behavioural tests). 8 new EPSearchBox provider tests covering ref-action behaviour, debounce, displayValue divergence during debounce, and meta correctness.README.md— new## Styling contractsection with the four-rule contract, leaf-element table per component,:where()pattern guidance,Composing EPSearchBoxwalkthrough, migration notes.Migration
<input>and<button>, then bindvalueand wire interactions per the README's "Composing EPSearchBox" section.9iSL9GyrJNp9ebWzxaHtM5) was re-authored via the Plasmic MCP during this work; the/productspage now shows a fully styled search input with all designer-set appearance properties reaching the live DOM.Test plan
yarn testinplasmicpkgs/commerce-providers/elastic-path— 1522 tests pass (no regressions, 8 new EPSearchBox tests, 45 contract assertions across 9 components).yarn buildclean (tsdx + server entry).http://localhost:3456/products:searchFieldDatapropagates to slot children via React context (verified via fiber inspection).($ctx.searchFieldData && $ctx.searchFieldData.value || "").Outstanding (not blocking this PR)
invokeRefActionaction type is not exposed by the MCP'sinteraction.addtool (filed as gap add enhanced container insights #81 in the EP MCP feedback tracker). The customFunction-with-$refsworkaround saves but doesn't fire ref-actions at runtime. To complete the example app's wiring: in Studio, click the input → addonChangeinteraction → "Run action" →EP Search Box→setValue→event.target.value. Same for the clear button'sonClick→clear.alwaysFresh: true).Closes #305.
Closes #308.