# Woo Cross Sells Widget

> Renders cross-sell products from the current WC cart. 3 layout styles (style-1, style-2, style-3). Editor shows a help notice when cart is empty / no cross-sells configured.

**Class file:** `includes/Elements/Woo_Cross_Sells.php` (1,167 lines)
**Slug:** `woo-cross-sells` (widget id `eael-woo-cross-sells`)
**Public docs:** <https://essential-addons.com/elementor/docs/woocommerce-cross-sells/>
**Pro-shared:** Pro-only. **Requires WooCommerce active + cart context.**

## Overview

Reads cross-sell products from `WC()->cart->get_cross_sells()` and renders them in a card grid. 3 visual styles (style-1, style-2, style-3) — each a separate template file. Heading + per-product image / price / purchasable badge. Only meaningful on the cart / checkout page (or wherever `WC()->cart` is populated). Editor preview shows a helper message when cart has no cross-sells configured.

## Pro vs Lite

Pro-only.

## File Map

| File | Role |
| --- | --- |
| `includes/Elements/Woo_Cross_Sells.php` | Widget class (1,167 lines) |
| `includes/Template/Woo-Cross-Sells/style-1.php` | Style 1 product card |
| `includes/Template/Woo-Cross-Sells/style-2.php` | Style 2 product card |
| `includes/Template/Woo-Cross-Sells/style-3.php` | Style 3 product card |
| `src/css/view/woo-cross-sells.scss` → `assets/front-end/css/view/woo-cross-sells.min.css` | Per-style grid styling |
| `src/js/view/woo-cross-sells.js` → `assets/front-end/js/view/woo-cross-sells.min.js` | Frontend behaviour |
| `config.php` entry `'woo-cross-sells'` | Self CSS + self JS |

## Architecture

- **Cart-driven render** — `WC()->cart->get_cross_sells()` (line 1124). Returns IDs; mapped via `wc_get_product`, filtered to visible products. Empty cart / no cross-sells configured → render skipped.
- **Editor-mode empty-state notice** — `Plugin::instance()->editor->is_edit_mode()` branch (line 1130) emits a help message when no cross-sells available. Frontend silently renders nothing.
- **`wc_products_array_orderby` + `array_slice`** for sort + limit. Pro respects user-configured offset + limit.
- **Style template dispatch** — `eael_dynamic_template_layout` (note lowercase `l`) — `style-1` / `style-2` / `style-3`. `$this->get_template( ... )` resolves to the matching PHP file.
- **Composes Lite's `Helper` trait** (line 18) — standard pattern.
- **WC purchasable badge logic** — per-product check `is_purchasable() && is_in_stock() && !is_type('variable')` (line 1158). Variable products render without the Add-to-Cart button (would need variation picker).

## Render Output (style-1)

```html
[?] <h2 class="eael-woo-cross-sells-heading">{heading}</h2>
<div class="eael-cs-products-container style-1 [eael-custom-image-area]">
  <!-- One block per cross-sell product -->
  <div class="eael-cs-single-product">
    <div class="eael-cs-product-image">
      <a href="{product_url}"><img src="{thumb}" /></a>
    </div>
    <div class="eael-cs-product-content">
      <h3>{title}</h3>
      <div class="price">{price_html}</div>
      [?] <a class="eael-add-to-cart" href="?add-to-cart={ID}">Add to Cart</a>
    </div>
  </div>
</div>
```

## Controls Reference

| Control id | Tab → Section | Type | Purpose |
| --- | --- | --- | --- |
| `eael_woo_cross_sales_heading` | Content → Heading | TEXT | Section heading |
| `eael_woo_cross_sells_heading_tag` | Content → Heading | SELECT | h1–h6 |
| `eael_dynamic_template_layout` | Content → Layout | SELECT | `style-1` / `style-2` / `style-3` |
| `products_count` | Content → Settings | NUMBER | Max products shown |
| `product_offset` | Content → Settings | NUMBER | Skip-N |
| `orderby` / `order` | Content → Settings | SELECT | WC sort |
| `eael_cross_sales_image_size_size` | Content → Image | SELECT | WC image size token |
| `eael_cross_sales_custom_size_img` | Style → Image | SWITCHER | Custom-size toggle (adds `.eael-custom-image-area`) |

## Conditional Dependencies

```text
eael_dynamic_template_layout = 'style-1' | 'style-2' → shared style-1+style-2 controls
eael_dynamic_template_layout = 'style-3' → style-3 specific controls (e.g. eael-cs-single-product column-gap)
eael_dynamic_template_layout != 'style-2' → exposes some controls hidden in style-2
eael_cross_sales_custom_size_img = 'yes' → enables custom image-size controls
```

## JavaScript Lifecycle

`src/js/view/woo-cross-sells.js` — handles AJAX add-to-cart with cart-fragment refresh. Verify on cart page that WC's cart fragments update after the button click.

## Hooks & Filters

Standard WC hooks for cart access. No Pro-emitted hooks.

## Common Issues

| Symptom | Likely cause | Diagnose | Fix |
| --- | --- | --- | --- |
| Editor shows "must add products that have cross-sells" | Empty cart in editor session OR no cross-sell relations | Add product to cart with cross-sells in WC | Configure cross-sells on at least one product the cart contains |
| Variable products show no Add-to-Cart | Intentional — Pro skips button on variable | — | Direct user to product page for variations |
| Cart count doesn't update after click | Cart fragments JS missing | DevTools: check `wc-cart-fragments` script | Ensure WC's standard scripts loaded |
| Style-2 styling looks wrong | Image height calc depends on heading height | Inspect `.eael-cs-product-image` max-height (line 249: `calc(100% - 78px)`) | Adjust heading row height or use style-1 |
| Heading XSS | `eael_woo_cross_sales_heading` not escaped | Read `wp_kses( $heading, HelperClass::eael_allowed_tags() )` (line 1153) | Pro escapes via Lite's allowed-tags whitelist — safe |

## Known Limitations

- **`eael_dynamic_template_layout` lowercase `l`** — inconsistent with sibling widgets using capital `L`. Preserved (user data)
- **Empty cart = no render on frontend** — by design, but the user has no widget-config-level "show fallback content" option
- **Variable products lack Add-to-Cart** — design decision; not configurable
- **WC scripts dependency** is implicit — Pro doesn't enqueue `wc-cart-fragments`; relies on WC's own enqueue
- **Style-2 height calc is fragile** — `calc(100% - 78px)` assumes a specific heading layout

## Cross-References

- Sibling: [`woo-product-slider.md`](woo-product-slider.md), [`woo-thank-you.md`](woo-thank-you.md), [`woo-account-dashboard.md`](woo-account-dashboard.md)
- Shared patterns: [`_patterns.md`](_patterns.md)
