@propeller-commerce/propeller-v2-react-ui 0.15.9 → 0.15.11

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
@@ -8,6 +8,44 @@ once it reaches 1.0. Until then (the `0.x` line) the public API may change
8
8
  between minor versions; breaking changes are called out below and in
9
9
  [MIGRATION.md](./MIGRATION.md).
10
10
 
11
+ ## [0.15.11] - 2026-08-11
12
+
13
+ ### Fixed
14
+
15
+ - **Checkout step 3 opened with nothing selected on a fresh cart (PWP-934).**
16
+ `CartPaymethods` and `CartCarriers` only ever adopted a value the cart already
17
+ stored, so a cart that had never reached step 3 — every first order, and every
18
+ order after the cart is recreated — rendered both grids blank and refused to
19
+ continue until the user clicked. They now preselect the cart's stored option
20
+ when there is one and otherwise the first option offered, so Continue works
21
+ straight away. A stored value the backend no longer offers also falls back
22
+ instead of leaving an invisible selection.
23
+
24
+ ### Changed
25
+
26
+ - The active option is now derived from the cart rather than mirrored into state
27
+ by an effect, so it is present in the first render (including SSR) instead of
28
+ appearing after hydration. `onPaymethodSelect` / `onCarrierSelect` still fire
29
+ once for the preselection, so hosts that persist the choice — and price the
30
+ transaction costs off it — keep working unchanged.
31
+
32
+ ## [0.15.10] - 2026-08-11
33
+
34
+ ### Fixed
35
+
36
+ - **The cart sidebar's "Bonus items" heading stayed English on a localized
37
+ page.** `CartIconAndSidebar` renders `CartBonusItems` itself but passed it no
38
+ `labels`, so the block always fell back to its English defaults — while the
39
+ same component on the cart and checkout pages, where the host passes `labels`
40
+ directly, translated correctly. Same shape as the `cartItemLabels` gap fixed
41
+ in 0.4.24.
42
+
43
+ ### Added
44
+
45
+ - `CartIconAndSidebar` accepts `cartBonusItemsLabels` (keys: `title`, `sku`),
46
+ forwarded to the embedded bonus-items block. Hosts already translating
47
+ `CartBonusItems` on the cart page can pass the same map.
48
+
11
49
  ## [0.15.9] - 2026-08-11
12
50
 
13
51
  ### Fixed
package/dist/index.cjs CHANGED
@@ -6427,6 +6427,12 @@ function CartBonusItems(rawProps) {
6427
6427
  ] });
6428
6428
  }
6429
6429
  var CartBonusItems_default = CartBonusItems;
6430
+
6431
+ // src/composables/shared/utils/preselect.ts
6432
+ function pickPreselected(items, stored, keyOf) {
6433
+ const list = items || [];
6434
+ return (stored ? list.find((item) => keyOf(item) === stored) : void 0) || list[0];
6435
+ }
6430
6436
  function CartCarriers(props) {
6431
6437
  const [selectedName, setSelectedName] = React6.useState("");
6432
6438
  const containerClass = props.carriersContainerClass || "cart-carriers";
@@ -6440,16 +6446,16 @@ function CartCarriers(props) {
6440
6446
  setSelectedName(carrier.name);
6441
6447
  if (props.onCarrierSelect) props.onCarrierSelect(carrier);
6442
6448
  }
6449
+ const preselected = pickPreselected(
6450
+ carriers,
6451
+ props.cart?.postageData?.carrier,
6452
+ (c) => c.name
6453
+ );
6454
+ const activeName = selectedName || preselected?.name || "";
6443
6455
  React6.useEffect(() => {
6444
- if (!selectedName && props.cart?.postageData?.carrier) {
6445
- const name = props.cart.postageData.carrier;
6446
- setSelectedName(name);
6447
- if (props.onCarrierSelect) {
6448
- const match = carriers.find((c) => c.name === name);
6449
- if (match) props.onCarrierSelect(match);
6450
- }
6451
- }
6452
- }, [props.cart, selectedName, props.onCarrierSelect, carriers, props]);
6456
+ if (selectedName || !preselected) return;
6457
+ if (props.onCarrierSelect) props.onCarrierSelect(preselected);
6458
+ }, [preselected?.name]);
6453
6459
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `propeller-cart-carriers ${containerClass}`, children: [
6454
6460
  carriers.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-cart-carriers__grid grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3", children: carriers.map((carrier, index) => {
6455
6461
  const logoUrl = showLogo ? carrier.logo || "" : "";
@@ -6457,8 +6463,8 @@ function CartCarriers(props) {
6457
6463
  "div",
6458
6464
  {
6459
6465
  onClick: () => handleSelect(carrier),
6460
- "data-selected": selectedName === carrier.name ? "true" : "false",
6461
- className: `propeller-cart-carriers__carrier relative cursor-pointer border rounded-container p-3 flex flex-col items-center justify-center gap-2 text-center aspect-square transition-all ${selectedName === carrier.name ? "border-secondary bg-secondary/5 shadow-sm" : "border-border hover:border-secondary/30"}`,
6466
+ "data-selected": activeName === carrier.name ? "true" : "false",
6467
+ className: `propeller-cart-carriers__carrier relative cursor-pointer border rounded-container p-3 flex flex-col items-center justify-center gap-2 text-center aspect-square transition-all ${activeName === carrier.name ? "border-secondary bg-secondary/5 shadow-sm" : "border-border hover:border-secondary/30"}`,
6462
6468
  children: [
6463
6469
  props.showPrice !== false ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "propeller-cart-carriers__carrier-price absolute top-2 right-2 text-xs bg-surface-hover text-muted-foreground px-2 py-0.5 rounded-full", children: formatCarrierPrice(carrier.price) }) : null,
6464
6470
  logoUrl ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "propeller-cart-carriers__carrier-logo-wrap flex items-center justify-center h-10 w-full", children: /* @__PURE__ */ jsxRuntime.jsx("img", { className: "propeller-cart-carriers__carrier-logo max-h-10 max-w-[80%] w-auto object-contain", src: logoUrl, alt: carrier.name }) }) : null,
@@ -7497,7 +7503,15 @@ function CartIconAndSidebar(rawProps) {
7497
7503
  },
7498
7504
  item.itemId
7499
7505
  )) }),
7500
- /* @__PURE__ */ jsxRuntime.jsx(CartBonusItemsImpl, { cart: props.cart, includeTax: useTax, className: "mt-4" })
7506
+ /* @__PURE__ */ jsxRuntime.jsx(
7507
+ CartBonusItemsImpl,
7508
+ {
7509
+ cart: props.cart,
7510
+ includeTax: useTax,
7511
+ className: "mt-4",
7512
+ labels: props.cartBonusItemsLabels
7513
+ }
7514
+ )
7501
7515
  ] }),
7502
7516
  " ",
7503
7517
  items.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-cart-icon__sidebar-footer px-5 py-4 border-t border-border space-y-3 bg-surface-hover", children: [
@@ -7754,16 +7768,16 @@ function CartPaymethods(rawProps) {
7754
7768
  setSelectedCode(method.code);
7755
7769
  if (props.onPaymethodSelect) props.onPaymethodSelect(method);
7756
7770
  }
7771
+ const preselected = pickPreselected(
7772
+ payMethods,
7773
+ props.cart?.paymentData?.method,
7774
+ (m) => m.code
7775
+ );
7776
+ const activeCode = selectedCode || preselected?.code || "";
7757
7777
  React6.useEffect(() => {
7758
- if (!selectedCode && props.cart?.paymentData?.method) {
7759
- const code = props.cart.paymentData.method;
7760
- setSelectedCode(code);
7761
- if (props.onPaymethodSelect) {
7762
- const match = payMethods.find((m) => m.code === code);
7763
- if (match) props.onPaymethodSelect(match);
7764
- }
7765
- }
7766
- }, [props.cart, selectedCode, props.onPaymethodSelect, payMethods, props]);
7778
+ if (selectedCode || !preselected) return;
7779
+ if (props.onPaymethodSelect) props.onPaymethodSelect(preselected);
7780
+ }, [preselected?.code]);
7767
7781
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `propeller-cart-paymethods ${containerClass}`, children: [
7768
7782
  payMethods.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-cart-paymethods__grid grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3", children: payMethods.map((method, index) => {
7769
7783
  const logoUrl = showLogo ? getLogoUrl(method) : "";
@@ -7771,8 +7785,8 @@ function CartPaymethods(rawProps) {
7771
7785
  "div",
7772
7786
  {
7773
7787
  onClick: () => handleSelect(method),
7774
- "data-selected": selectedCode === method.code ? "true" : "false",
7775
- className: `propeller-cart-paymethods__method relative cursor-pointer border rounded-container p-3 flex flex-col items-center justify-center gap-2 text-center aspect-square transition-all ${selectedCode === method.code ? "border-secondary bg-secondary/5 shadow-sm" : "border-border hover:border-secondary/30"}`,
7788
+ "data-selected": activeCode === method.code ? "true" : "false",
7789
+ className: `propeller-cart-paymethods__method relative cursor-pointer border rounded-container p-3 flex flex-col items-center justify-center gap-2 text-center aspect-square transition-all ${activeCode === method.code ? "border-secondary bg-secondary/5 shadow-sm" : "border-border hover:border-secondary/30"}`,
7776
7790
  children: [
7777
7791
  method.price > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "propeller-cart-paymethods__method-price absolute top-2 right-2 text-xs bg-surface-hover text-muted-foreground px-2 py-0.5 rounded-full", children: formatMethodPrice(method.price) }) : null,
7778
7792
  logoUrl ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "propeller-cart-paymethods__method-logo-wrap flex items-center justify-center h-10 w-full", children: /* @__PURE__ */ jsxRuntime.jsx(