@vio-live/web-sdk 0.33.0 → 0.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -14,6 +14,96 @@ How to keep this file: see [CONTRIBUTING → Changelog](CONTRIBUTING.md#changelo
14
14
  The details of each feature live in the [README](README.md); this file says *what*
15
15
  and *when*, and links there.
16
16
 
17
+ ## 0.36.0 — 2026-10-29
18
+
19
+ ### Changed
20
+ - **The cart says where shipping is chosen.** Under «Frakt» / «Totalt» a note: «Frakt velger du i Vipps eller i
21
+ neste steg.» (`part="cart-shipping-hint"`). While no rate is chosen and the brand offers several, the shipping and
22
+ the total read «fra …» (the cheapest).
23
+
24
+ ### Added
25
+ - **The heading of the description fold is configurable**: `Vio.configureDetail({ descriptionLabel: 'Produktbeskrivelse' })`
26
+ for the page, or `description-label` on `<vio-product-detail>` (wins). Default «Beskrivelse». Feedback from Aller
27
+ (29/10): a folded «Produktbeskrivelse» right under the price — with the section order
28
+ `[..., 'price', 'description', 'options', 'purchase', ...]` and `detailDescriptionMode: 'folded'`.
29
+ - **The brand's promises in the cart** too, under the ways to pay (same strip as the product detail, from the
30
+ sponsor block's `highlights`). Hide them with `applyVioTheme({ cartHighlightsDisplay: 'none' })`.
31
+
32
+ ## 0.35.0 — 2026-10-29
33
+
34
+ > 0.34.0 was never published on its own: its changes (section order of the product detail and layout tracking,
35
+ > below) ship in this version too.
36
+
37
+ ### Added
38
+ - **`vippsCartPayment: 'express' | 'checkout'`** (theme key, `--vio-vipps-cart-payment`) and the
39
+ attribute **`vipps-cart-payment`** on `<vio-cart>` and `<vio-product-detail>`. `express` is the
40
+ default; `checkout` restores the 0.31–0.34 behaviour exactly. See
41
+ [README → The hand-over to Vipps](README.md#the-hand-over-to-vipps-028).
42
+
43
+ ### Changed
44
+ - **The Vipps buttons pay the sponsor's whole cart with Vipps Express** (Angelo, 2026-10-29: «el
45
+ botón negro va con el nuestro y el botón de Vipps paga el carrito entero»). The black «Til
46
+ kassen» is the way into our checkout; Vipps goes to Vipps.
47
+ - **`<vio-cart>`**: the Vipps button starts Express for the sponsor's whole cart — every line of
48
+ that sponsor's backend cart, never another sponsor's — the same Express as the detail: the
49
+ «Åpner Vipps …» overlay, `vio:payment-redirect-*` with `source: 'cart'`, `add_payment_info`
50
+ once, the return from Vipps and its confirmation. It no longer dispatches
51
+ `vio:checkout-open`. One payment at a time (the overlay takes every tap; a second tap while
52
+ one is starting does nothing).
53
+ - **`<vio-product-detail>`**: «Kjøp nå med Vipps» with lines already in the brand's cart adds the
54
+ product (size and quantity) and pays the **whole cart** with Express, instead of opening our
55
+ checkout. The line under the button says what will be paid before the tap: «Betaler hele
56
+ handlekurven · {n} varer · {total}» (n = every unit with this product at the chosen quantity,
57
+ total = the cart's subtotal with it; shipping is chosen in Vipps). It follows the cart and the
58
+ quantity. With an empty cart nothing changes.
59
+ - Express off for the brand, or a cart that cannot have it (several suppliers, no rate): the
60
+ checkout opens with Vipps preselected, as before.
61
+ - **A redirect payment (Vipps, Stripe link) reuses only its own sponsor's open checkout.** A
62
+ payment for one sponsor's cart started while another sponsor's checkout was open would have
63
+ gone out on that checkout; it now creates its own.
64
+
65
+ ### ⚠️ Upgrade notes
66
+ - **Hosts listening to `vio:checkout-open` from the cart's Vipps button no longer get it** by
67
+ default: the cart goes to Vipps directly. «Til kassen» still sends `vio:checkout-open`, and the
68
+ Vipps button sends it only when Express cannot start (then with `paymentMethod: 'vipps'`). To
69
+ keep the old flow, set `vipps-cart-payment="checkout"` or `applyVioTheme({ vippsCartPayment:
70
+ 'checkout' })`.
71
+ - **`vio:payment-redirect-start` / `-cancel` / `-error` with `source: 'cart'` are back** (gone
72
+ since 0.31). A host that draws its own loader (`preventDefault()` on the start) gets them from
73
+ the cart too.
74
+ - The same for the detail with lines in the cart: no `vio:checkout-open` any more, the hand-over
75
+ events (`source: 'detail'`) instead.
76
+ - **Backend:** nothing new. shopcart already builds Express on the checkout's cart, every line;
77
+ it refuses Express (`express: false`, no payment) for a cart of several suppliers, and the
78
+ checkout opens instead.
79
+
80
+ ## 0.34.0 — 2026-10-29
81
+
82
+ ### Added
83
+ - **Section order of `<vio-product-detail>` and layout tracking** (asked by Replit for Mote &
84
+ Livsstil). `Vio.configureDetail({ layout: { id, revision, sections, experimentId?, variantId? } })`
85
+ orders the sections below the gallery for the page (`{ layout: null }` resets; open details
86
+ re-render); the element's `layout` property / attribute (JSON) wins. Section ids: `brand`,
87
+ `title`, `stock`, `price`, `options`, `purchase`, `highlights`, `description`,
88
+ `shippingReturns`; unknown ids are ignored, duplicates keep the first, the essentials (`title`,
89
+ `price`, `options`, `purchase`) are appended when missing. The real DOM is reordered, the same
90
+ on every screen. `el.appliedLayout`, `Vio.detailLayout()` and the event
91
+ `vio:detail-layout-applied` say what was applied. See
92
+ [README → Section order and layout tracking](README.md#section-order-and-layout-tracking-034).
93
+ - **The layout on the funnel events.** `view_item` of a non-default layout carries `layout_id`,
94
+ `layout_revision`, `experiment_id`, `variant_id` in its `context`. The detail stores the layout
95
+ on the cart line origin (`CartOrigin.layoutId`, `layoutRevision`, `experimentId`, `variantId`),
96
+ and every item of `add_to_cart`, `remove_from_cart`, `view_cart`, `begin_checkout`,
97
+ `add_payment_info` and `purchase` carries `layout_id`, `layout_revision`, `experiment_id`,
98
+ `experiment_variant_id` (`""` when none; `items[].variant_id` stays the product variant). A line
99
+ with units from two layouts is two items. Nothing goes to Commerce.
100
+
101
+ ### ⚠️ Upgrade notes
102
+ - **No change without configuration:** the detail renders exactly as in 0.33 and `view_item`
103
+ sends nothing new. The only difference on the wire: the items of the cart events now always
104
+ carry the four layout keys, empty (`""`) when the units came from no layout — the collector
105
+ ignores keys it does not know yet.
106
+
17
107
  ## 0.33.0 — 2026-10-28
18
108
 
19
109
  ### Fixed
package/README.md CHANGED
@@ -712,6 +712,7 @@ or on the element (`vio-product-detail { --vio-detail-logo-height: 48px; }`).
712
712
  | `cartWidth` / `checkoutWidth` | `--vio-cart-width` / `--vio-checkout-width` | `440px` | Drawer widths (desktop) |
713
713
  | `confirmLogoHeight` / `confirmTitleSize` / `confirmCheckBg` | `--vio-confirm-logo-height` / `--vio-confirm-title-size` / `--vio-confirm-check-bg` | `40px` / `24px` / green | The confirmation |
714
714
  | `redirectBg` / `redirectSpinner` | `--vio-redirect-bg` / `--vio-redirect-spinner` | translucent white / Vipps orange | The «Åpner Vipps …» overlay |
715
+ | `vippsCartPayment` | `--vio-vipps-cart-payment` | `express` | What the Vipps button of `<vio-cart>` and `<vio-product-detail>` pays when the cart has lines: `express` = Vipps Express for the sponsor's whole cart; `checkout` = our checkout with Vipps preselected (0.31–0.34). See [The hand-over to Vipps](#the-hand-over-to-vipps-028) |
715
716
 
716
717
  Also settable as CSS only: `--vio-detail-logo-max-width` (`220px`), `--vio-detail-image-position`,
717
718
  `--vio-detail-image-radius`, `--vio-detail-thumb-fit` (`cover`), `--vio-detail-thumb-border-width`,
@@ -759,10 +760,13 @@ whole page through `applyVioTheme` (theme key → custom property). The element
759
760
  |---|---|---|---|
760
761
  | `gallery` | `detailGallery` | `thumbs` (default) · `carousel` | `carousel`: every photo in one swipeable row with a dot per photo, no thumbnails — less height. `detailCarouselPeek` (`--vio-detail-carousel-peek`) shows the edge of the next one |
761
762
  | `description-mode` | `detailDescriptionMode` | `preview` (default) · `folded` · `open` | «Beskrivelse»: the first lines fading out + «Les mer» (`detailDescriptionPreviewLines`, `3`); only the heading until a tap; or all of it |
763
+ | `description-label` | — (`Vio.configureDetail({ descriptionLabel })`) | text, default `Beskrivelse` | Heading of the description fold, e.g. `Produktbeskrivelse` |
762
764
  | `highlights` | — | `[{ icon, title, text }]` (JSON) | The brand's promises under the buy zone. By default the brand's own (dashboard Settings → Brand, sponsor block `highlights`); this list replaces them on the page. Icons: `plane heart shirt truck return leaf star shield mailbox home pin clock`, or an image URL. `detailHighlightsDisplay: 'none'` hides the strip |
763
765
  | `delivery-time` | — | text, e.g. `2-5 virkedager` | «Leveringstid …» in «Frakt & retur» for a brand whose rates do not say it (each rate's own text in Commerce — «Leveringstid 1-5 dager» — wins) |
764
766
  | `carrier-logos` | — | `{ "Bring": "https://…" }` (JSON) | Carrier logos by the name the rate starts with («Bring - Pakke i postkasse»), for rates without their own logo in Commerce (`logo_url`, which wins); otherwise a line icon by the kind of delivery |
767
+ | `vipps-cart-payment` | `vippsCartPayment` | `express` (default) · `checkout` | «Kjøp nå med Vipps» when the brand's cart already has lines: the product is added and the **whole cart** goes to Vipps Express, the line under the button saying «Betaler hele handlekurven · n varer · total»; or (`checkout`) the checkout opens first, Vipps preselected. Also on `<vio-cart>` (its Vipps button) |
765
768
  | — | `detailShippingReturnsDisplay` | `block` (default) · `none` | «Frakt & retur»: one row per carrier (logo or icon, carrier, how it delivers, price), the delivery time, the right of withdrawal and the brand's pages |
769
+ | — | `cartHighlightsDisplay` | `grid` (default) · `none` | The brand's promises in the cart, under the ways to pay |
766
770
 
767
771
  The sheet closes with a pull down from its top bar, or from anywhere while its content is at the top.
768
772
 
@@ -785,6 +789,111 @@ line above the name). CSS only: `--vio-card-border-hover-color`, `--vio-card-add
785
789
  `--vio-card-retailer-{display,size,tracking,weight,color,margin}`,
786
790
  `--vio-card-description-{size,color,gap}`, `--vio-card-accent-text`.
787
791
 
792
+ ### Section order and layout tracking (0.34)
793
+
794
+ The sections of `<vio-product-detail>` **below the gallery** can be put in any order, and the
795
+ layout the shopper really saw travels with every event of the funnel — so a host can run an
796
+ experiment on the product page and read its conversion. The header (selling brand + close) and
797
+ the gallery stay where they are. Without any configuration nothing changes.
798
+
799
+ ```js
800
+ // The whole page (every detail open now and later):
801
+ Vio.configureDetail({
802
+ layout: {
803
+ id: 'pdp-sizes-first', // required
804
+ revision: '3',
805
+ sections: ['title', 'stock', 'price', 'options', 'purchase', 'description', 'shippingReturns'],
806
+ experimentId: 'pdp-2026-11', // optional
807
+ variantId: 'B', // optional — the experiment variant
808
+ },
809
+ })
810
+ Vio.configureDetail({ layout: null }) // back to the default
811
+
812
+ // One element (wins over the page): the property, or the attribute as JSON.
813
+ detail.layout = { id: 'pdp-b', revision: '1', sections: ['price', 'title', 'options', 'purchase'] }
814
+ // <vio-product-detail layout='{"id":"pdp-b","revision":"1","sections":["price","title","options","purchase"]}'>
815
+
816
+ detail.appliedLayout // what this detail applies
817
+ Vio.detailLayout() // what a detail without its own layout applies (the page's or the default)
818
+ ```
819
+
820
+ **Section ids**
821
+
822
+ | Id | What |
823
+ |---|---|
824
+ | `brand` | The product's brand line (`.brand`) |
825
+ | `title` | The product name |
826
+ | `stock` | «På lager» / «Få igjen» / «Utsolgt» |
827
+ | `price` | Price + compare-at price |
828
+ | `options` | The size / option groups and their prompt («Velg størrelse») |
829
+ | `purchase` | The SDK's buy group, never split: «Frakt fra…», Vipps / Apple Pay / the other ways to pay, quantity + «Legg i handlekurv», «Du handler direkte hos …» and the terms links |
830
+ | `highlights` | The brand's promises |
831
+ | `description` | «Beskrivelse» |
832
+ | `shippingReturns` | «Frakt & retur» |
833
+
834
+ Default order — exactly the DOM before 0.34: `brand, title, stock, price, options, purchase,
835
+ highlights, description, shippingReturns` (title and stock on one row; the options inside the
836
+ buy zone; highlights, description and «Frakt & retur» after it).
837
+
838
+ **Rules**
839
+
840
+ - Ids, `revision`, `experimentId`, `variantId`: strings of at most 64 characters of
841
+ `[A-Za-z0-9._:-]`. An invalid `id` (or `sections` that is not an array) ignores the layout, with
842
+ one `console.warn`; an invalid `revision` / `experimentId` / `variantId` is treated as absent.
843
+ - Unknown section ids are ignored; a duplicate: the first wins.
844
+ - Essentials — `title`, `price`, `options`, `purchase` — are appended in that order when missing.
845
+ Any other section left out is not rendered.
846
+ - `title` immediately followed by `stock` keep today's single row; otherwise each is its own block.
847
+ A run of `title` / `stock` / `price` is one `.headline` block.
848
+ - `options` immediately before `purchase` render inside the buy zone (`.buy`), as today — so a
849
+ sticky buy zone still holds them. Anywhere else they are their own block
850
+ (`.buy-options.options-apart`, same `part="buy-options"`), and the buy buttons still require the
851
+ choice: the same «Velg størrelse» prompt, and the options are scrolled into view.
852
+ - The real DOM is reordered (no CSS `order`), the same on phone and desktop. Every section after
853
+ `purchase` goes inside `.purchase`, as the description does by default: a sticky buy zone
854
+ (`detailBuyPosition: 'sticky'`) may slide over them, never over what comes before it.
855
+ `detailHighlightsDisplay`, `detailShippingReturnsDisplay`, `description-mode` and `gallery`
856
+ apply as before.
857
+
858
+ **Applied layout** — `el.appliedLayout`, `Vio.detailLayout()` and the event
859
+ `vio:detail-layout-applied` (bubbles, composed; React: `onLayoutApplied`), dispatched each time a
860
+ detail renders a product with a layout it was not yet shown with (every open, every change while
861
+ open; the detail also carries `productId` and `sponsorId`):
862
+
863
+ ```ts
864
+ {
865
+ id: string, revision: string, experimentId?: string, variantId?: string,
866
+ sections: string[], // what is rendered, in order
867
+ adjusted: Array<{ section: string, reason: 'unknown' | 'duplicate' | 'added-essential' }>,
868
+ source: 'element' | 'global' | 'default',
869
+ }
870
+ // no configuration:
871
+ { id: 'default', revision: '0', sections: [/* the default order */], adjusted: [], source: 'default' }
872
+ ```
873
+
874
+ **Tracking contract** (shared with the collector). Only a non-default layout (`source`
875
+ `element` / `global`) adds anything; the default one sends nothing new on `view_item`.
876
+
877
+ | Where | Fields | Value |
878
+ |---|---|---|
879
+ | `view_item` — `context` (next to `campaign_id`, `location_id`) | `layout_id`, `layout_revision`, `experiment_id`, `variant_id` | The layout the detail is shown with. `variant_id` here is the **experiment** variant (`context` has no product variant). Absent for the default layout; `experiment_id` / `variant_id` absent when the layout has none |
880
+ | Every item of `add_to_cart`, `remove_from_cart`, `view_cart`, `begin_checkout`, `add_payment_info`, `purchase` — `commerce.items[]` | `layout_id`, `layout_revision`, `experiment_id`, `experiment_variant_id` | The layout the units were added under (their cart line origin). Always present on these items: `""` when the units came from no layout (a card, the default layout). `items[].variant_id` stays the **product** variant — hence `experiment_variant_id` |
881
+
882
+ - The detail stores the layout on the cart line origin when it adds to the cart (any buy button):
883
+ `CartOrigin.layoutId`, `layoutRevision`, `experimentId`, `variantId` (the experiment variant),
884
+ taken from the layout shown at that moment. They persist with the cart (`localStorage`), so they
885
+ survive the return from Vipps / Stripe, a reload and another tab; changing the page's layout
886
+ later never rewrites lines already in the cart.
887
+ - A line whose units came from several layouts is split per layout into several items (each with
888
+ its quantity), the same way the units are split per campaign / slot (`Vio.cartSlices()` items
889
+ carry `layoutId`, `layoutRevision`, `experimentId`, `experimentVariantId` when they have a
890
+ layout). A purchase is never credited whole to the last product opened.
891
+ - `vio:added-to-cart` from the detail carries `layout: { layoutId, layoutRevision, experimentId?,
892
+ variantId? }` for a non-default layout (`variantId` of the event itself stays the product's).
893
+ - Internal traffic stays excluded as before; campaign, sponsor, channel, content and slot are
894
+ unchanged. The layout is **not** sent to Commerce (the line origin's wire to shopcart has no
895
+ layout fields): the collector joins `order_paid` to these events by `checkout_id`.
896
+
788
897
  ### The hand-over to Vipps (0.28)
789
898
 
790
899
  Between the tap on Vipps (detail, cart or checkout) and the Vipps page, the SDK shows
@@ -800,24 +909,41 @@ tap, so a second payment cannot start. Events on `document`:
800
909
  `source` is `'detail'`, `'cart'` or `'checkout'`. The overlay is `.vio-redirect-overlay`
801
910
  in the light DOM.
802
911
 
803
- **The detail's Vipps button pays the whole cart.** «Kjøp nå med Vipps» on the product
804
- detail adds the product to the sponsor's cart and the checkout is built on that cart —
805
- every line in it. It goes straight to Vipps only when the cart was **empty**; with lines
806
- already in it, the product is added and the checkout opens first (`vio:checkout-open`
807
- with `paymentMethod: 'vipps'`, no hand-over events), every line and the total on screen,
808
- so what the Vipps page lists is what the shopper saw.
809
-
810
- **Two Vipps paths (2026-10-28).** The detail's «Kjøp nå med Vipps» is **Express**: the app
811
- fills in name and address. The **cart's** Vipps button opens the checkout with Vipps
812
- preselected, and the checkout's Vipps is always the **standard** payment, even when the
912
+ **The Vipps buttons pay the sponsor's whole cart with Express (2026-10-29).** The black
913
+ «Til kassen» in `<vio-cart>` is the way into our checkout; the Vipps buttons go to Vipps:
914
+
915
+ - **`<vio-cart>`**: the Vipps button starts **Vipps Express for the whole cart** of the
916
+ sponsor (every line of its backend cart, never another sponsor's) — the hand-over above with
917
+ `source: 'cart'`, `add_payment_info` once, and the return from Vipps and its confirmation
918
+ exactly as from the detail. No `vio:checkout-open`.
919
+ - **`<vio-product-detail>`**: «Kjøp nå med Vipps» adds the product (size, quantity) and starts
920
+ Express for the sponsor's cart. With an **empty** cart that is the product alone (the line
921
+ under the button: «Vipps fyller inn navn og adresse – du godkjenner og betaler i appen»). With
922
+ **lines already in it**, the whole cart is paid, and the line under the button says so before
923
+ the tap: «Betaler hele handlekurven · {n} varer · {total}» — n = every unit including this
924
+ product at the chosen quantity, total = the cart's subtotal with it (shipping is chosen in
925
+ Vipps).
926
+
927
+ When the brand has Express off, or the cart cannot have it (several suppliers, digital goods,
928
+ no rate for the country — shopcart answers `express: false` and creates nothing), the checkout
929
+ opens with Vipps preselected (`vio:payment-redirect-cancel` with `'express-off'` /
930
+ `'not-started'`, then `vio:checkout-open`).
931
+
932
+ **The option: `vipps-cart-payment` / `vippsCartPayment`.** `express` is the default.
933
+ `checkout` brings back 0.31–0.34 exactly: the cart's Vipps button opens the checkout with Vipps
934
+ preselected (`vio:checkout-open`, `paymentMethod: 'vipps'`, no hand-over), and the detail's
935
+ does the same when the cart already has lines (product added first); an empty cart is Express
936
+ either way.
937
+
938
+ **The checkout's Vipps is the standard payment** (2026-10-28), even when the
813
939
  brand has Express on. Our form collects name, e-mail, **Mobilnummer**, address and the
814
940
  delivery with its price before paying. The mobile is required for Vipps only and accepts any
815
941
  market's number: the `+` prefix or the shipping country sets the calling code. The delivery is
816
942
  saved on the cart first; if that fails, no payment starts. The payment goes out with
817
943
  `express: false`, and the address (with `phone` + `phone_code`) is on the checkout's shipping
818
- and billing address. **Apple Pay on the detail follows the same
819
- rule**: its sheet's amount is fixed before the tap, so it opens only on an empty cart, for the
820
- product × the chosen quantity; otherwise the checkout opens (`paymentMethod: 'apple-pay'`).
944
+ and billing address. **Apple Pay on the detail** has its sheet's amount fixed before the tap,
945
+ so it opens only on an empty cart, for the product × the chosen quantity; otherwise the
946
+ checkout opens (`paymentMethod: 'apple-pay'`).
821
947
 
822
948
  ### What the shopper pays is what the page shows — the cart, the server, the other tabs
823
949
 
@@ -0,0 +1,3 @@
1
+
2
+ //# sourceMappingURL=chunk-3YFJFDSN.js.map
3
+ //# sourceMappingURL=chunk-3YFJFDSN.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-QGJUBVH3.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-3YFJFDSN.js"}