# Woo Recipes

> Practical Woo component structures backed by current E2E scenarios and demo-store patterns.

<!-- Sources: src/Testing/E2E/Woo/WooScenarioProvider.php; tests/e2e/components/woo/behavior.spec.ts; tests/e2e/components/woo/lifecycle.spec.ts; src/Testing/E2E/Woo/WooDemoStoreBuilder.php; src/Patterns/Testing/TestWooProductList/TestWooProductList.php; src/Patterns/Testing/TestSingleWooProductTemplate/TestSingleWooProductTemplate.php; src/Patterns/Testing/TestCartDemo/TestCartDemo.php; src/Patterns/Testing/TestCheckoutDemo/TestCheckoutDemo.php -->

# Woo Recipes

These recipes use scenario and demo-store structures that exist in the repo.

## Product Archive Card

Backed by `product_archive_simple_add` and `TestWooProductList`.

Use a product loop and bind each card with `{item.*}`:

<CodeExample title="Product archive purchase card" language="jsx">
{`{#loop products as item}
  <article class="product-card">
    <OmeWooAddToCartForm product='{{"product_id":"{item.id}","product_type":"{item.omewoo.product.type}"}}'>
      {#slot default}
        <OmeWooAddToCartButton behavior='{{"mode":"counter","trigger":"form_submit"}}' />
      {/slot}
    </OmeWooAddToCartForm>
  </article>
{/loop}`}
</CodeExample>

Why it works: the form owns product ID/type and form submission; the button is a submit control inside the form. The E2E archive scenario submits the form and verifies `CartCount` updates.

## Single Product Quantity

Backed by `product_single_simple_add`.

Use `{this.*}` on a single product template and set button quantity bounds:

<CodeExample title="Single product form with quantity bounds" language="jsx">
{`<OmeWooAddToCartForm product='{{"product_id":"{this.id}","product_type":"{this.omewoo.product.type}"}}'>
  {#slot default}
    <OmeWooAddToCartButton
      behavior='{{"mode":"counter","trigger":"form_submit"}}'
      quantity='{{"min":"1","max":"3","default":"2"}}'
    />
  {/slot}
</OmeWooAddToCartForm>`}
</CodeExample>

E2E fills `9`, dispatches change, and verifies the input clamps to `3` before the cart count updates.

## Variable Product Selector

Backed by `variable_product_requires_variation`, `variation_selector_button_radio`, and the demo single-product template.

Loop over product attributes and bind each `AttributeSelector` to the current attribute object:

<CodeExample title="Variation attribute selectors inside a purchase form" language="jsx">
{`<OmeWooAddToCartForm product='{{"product_id":"{this.id}","product_type":"{this.omewoo.product.type}"}}'>
  {#slot default}
    {#loop this.omewoo.attributes as attribute}
      <OmeWooAttributeSelector
        target='{{"attribute":{attribute}}}'
        ui='{{"mode":"radio","aria_label":"Choose {attribute.label}"}}'
      />
    {/loop}
    <OmeWooAddToCartButton behavior='{{"mode":"counter","trigger":"form_submit"}}' />
  {/slot}
</OmeWooAddToCartForm>`}
</CodeExample>

Why it works: the selector writes selected variation attribute state into the purchase form. The form blocks submit until a required variation option is selected, and unavailable options do not select.

## Cart Rows and Empty State

Backed by `cart_items_component_preview_contract`, `cart_page_update_and_remove`, and `cart_totals_and_empty_slot`.

Place cart item atoms inside the `CartItems` default slot and empty content inside the `empty` slot:

<CodeExample title="Cart item rows with empty fallback" language="jsx">
{`<OmeWooCartItems>
  {#slot default}
    <OmeWooCartItemImage />
    <OmeWooCartItemTitle />
    <OmeWooCartItemQuantity />
    <OmeWooCartItemRemove />
  {/slot}
  {#slot empty}
    Your cart is empty.
  {/slot}
</OmeWooCartItems>`}
</CodeExample>

E2E verifies row count, server-hydrated atom text, quantity updates, removal, totals refresh, and final empty state.

## Coupons

Backed by `cart_coupons`.

Use `CouponForm` to apply codes, `CouponAppliedList` to render current coupons, and `CouponRemoveButton` inside the coupon row:

<CodeExample title="Coupon apply and remove" language="jsx">
{`<OmeWooCouponForm />

<OmeWooCouponAppliedList>
  {#slot default}
    <OmeWooCouponRemoveButton />
  {/slot}
</OmeWooCouponAppliedList>`}
</CodeExample>

E2E applies `SAVE10`, verifies discount totals and coupon rows, then removes the coupon and verifies totals return.

## Shipping Methods

Backed by `shipping_selector`, `shipping_selector_button_radio`, `shipping_selector_select`, and `shipping_selector_select_content_props`.

Use `ShippingMethodSelector` wherever cart shipping selection should be available:

<CodeExample title="Shipping method selector in button-radio mode" language="jsx">
{`<OmeWooShippingMethodSelector
  ui='{{"mode":"button-radio","aria_label":"Choose a shipping method"}}'
  content='{{"show_price":true,"price_separator":" - "}}'
/>`}
</CodeExample>

The selector uses package-aware rate values. E2E verifies button-radio and select modes, hiding when no shipping is needed, and total/order summary refresh after selecting free shipping.

## Checkout Success

Backed by `checkout_success` and `TestCheckoutDemo`.

Use a provider, a form, address forms, selectors, terms, notices, and place order:

<CodeExample title="Checkout provider and form shell" language="jsx">
{`<OmeWooCheckoutProvider>
  {#slot default}
    <OmeWooCheckoutForm>
      {#slot default}
        <OmeWooBillingAddressForm />
        <OmeWooShippingAddressForm />
        <OmeWooPaymentMethodSelector />
        <OmeWooTermsCheckbox />
        <OmeWooCheckoutNotices />
        <OmeWooPlaceOrderButton />
      {/slot}
    </OmeWooCheckoutForm>
  {/slot}
</OmeWooCheckoutProvider>`}
</CodeExample>

E2E fills the required billing fields, selects a payment method, clicks place order, and waits for an order-received or order-pay URL.

## Native Hook Compatibility

Backed by `lifecycle_add_to_cart`, `shipping_selector` in pricing mode, and `lifecycle_checkout`.

Use the same components as normal. The important point is that Store API requests still run Woo hooks:

- Add-to-cart validation can block the Etch form and render a Woo notice.
- Add-to-cart hooks can inject cart item data.
- Pricing hooks can change totals rendered by cart and order summary components.
- Checkout hooks can update Store API orders and payment context.
