# Free gift slot events

> DOM events from the free gift line, andor:offer_* customer events for gift adds, and the Horizon cart:update after a gift is removed, plus its data attributes and states.

Source: https://backtrackhq.com/docs/widgets/free-gift/events/

This page is for theme developers. The free gift line on product pages,
`<andor-bundle data-andor-widget="gift">`, sends the shared widget
events. The line and the cart gift slot also publish upsell offer events
for custom pixels.

All DOM events are `CustomEvent`s that bubble, so you can listen on
`document`. The `andor:offer_*` events are not DOM events. Subscribe to
them in a custom pixel.

> **Note:** A custom pixel is optional. AndOr counts offers without
> one. Add a pixel only if you want the `andor:offer_*` events in your
> own tools.

## Events

| Event | Sent on | When |
|---|---|---|
| `andor:widget:mount` | the line | the first pass that sees it |
| `andor:widget:unmount` | the line and `document` | the first pass after it left the page |
| `andor:display:change` | the line | its state changed, for example it hid |
| `andor:offer_viewed` | customer events | the offer is first seen, once per offer and placement per visit |
| `andor:offer_clicked` | customer events | a shopper presses **Add gift**, **Add it back** or **Choose gift** (not **Remove gift**) |
| `andor:offer_added` | customer events | a gift was added to the cart |
| `cart:update` | `document` | Horizon only: the slot removed a gift in the cart drawer |

Rules worth knowing:

- **No event on first paint**, and an unchanged state sends nothing.
- **The cart gift slot is not a widget host.** It sends no
  `andor:widget` events.
- **An automatic add has no click before it.** It is published as
  `andor:offer_added` with `auto: true` and `valueCents: 0`.

## Payloads

`andor:widget:mount`, `andor:widget:unmount` and
`andor:display:change` carry `event.detail.resource`:

| Field | Value for this widget |
|---|---|
| `widgetType` | `"gift"` |
| `widgetId` | the block id, or `"pasted"` for a pasted tag |
| `productId` | `null` (the line carries no product id) |

`andor:display:change` adds `previous` and `current` state objects. On
this host the useful field is `visible`. The others are `null`. See the
full payload on [Events](https://backtrackhq.com/docs/developers/events/).

The `andor:offer_*` events carry `event.customData`:

| Field | Type | Meaning |
|---|---|---|
| `offerId` | string | the offer id (digits) |
| `placement` | string | `"product_page"`, `"cart_page"` or `"cart_drawer"` |
| `valueCents` | number | adds only: the value of what was added, in cents. `0` for a gift AndOr added for the shopper |
| `auto` | `true` | only on a gift AndOr added for the shopper |

No customer data, cart token or product ids are sent.

`cart:update` carries Horizon's own shape:

| Field | Value |
|---|---|
| `detail.resource` | the `/cart.js` response after the removal |
| `detail.sourceId` | `"andor"` |
| `detail.data` | `{ source: "andor", itemCount }` |

When the slot removes a gift in a cart drawer on Horizon, AndOr sends
`cart:update` so Horizon redraws its cart. On Dawn, AndOr redraws the
drawer itself. On the cart page, or on a theme that is neither Dawn nor
Horizon, the page reloads instead.

> **Caution:** `cart:update` is Horizon's contract, not an AndOr API.
> AndOr sends it only after the slot removes a gift. A gift add does not
> send it. Listen to it only as Horizon documents it.

## Examples

React when the gift line hides:

```js
document.addEventListener("andor:display:change", (event) => {
  const { widgetType, current } = event.detail.resource;
  if (widgetType === "gift" && !current.visible) {
    console.log("free gift line hid");
  }
});
```

Optional: count gift adds in a custom pixel:

```js
// Shopify admin: Settings > Customer events > Add custom pixel
analytics.subscribe("andor:offer_added", (event) => {
  const { offerId, placement, valueCents, auto } = event.customData;
  console.log(auto ? "gift auto-added" : "gift added", offerId, placement,
    valueCents);
});
```

## Events the widget listens for

- `shopify:cart:lines-update`: the cart changed. The slot reads it again.
- `shopify:cart:view`: the cart drawer opened. The drawer slot draws.
- `shopify:section:load`: the theme editor re-rendered a section.

## Data attributes and states

### Product page gift banner

The gift banner uses the same attributes as the bundle card. See
[Bundle card events](https://backtrackhq.com/docs/widgets/bundle/events/#data-attributes-and-states).

### Gift slot

`data-andor-state` on `.andor-claim__box--gift`:

| Value | Meaning |
|---|---|
| `locked` | not earned yet: greyed, no button |
| `adding` | an automatic add is running (`aria-busy="true"`) |
| `choose` | earned: add it, or choose one |
| `removed` | the shopper removed it: "Add it back" |
| `gone` | chosen, and the cart stopped qualifying |
| `won` | one line: checkout applied it |
| `off` | one line: in the cart, another discount won |

The button carries `data-andor-act`: `add`, `open` or `remove`.

### Cart claim rows

The gift slot sits in the cart claim host. Its host attributes are on
[Buy X get Y events](https://backtrackhq.com/docs/widgets/buy-x-get-y/events/#data-attributes-and-states).

For the attributes every widget shares, like `data-andor-state` and
`data-andor-progress`, see
[Data attributes and states](https://backtrackhq.com/docs/developers/data-attributes/).
