@propeller-commerce/propeller-v2-react-ui 0.4.17 → 0.4.19

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,30 @@ 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.4.19] - 2026-07-10
12
+
13
+ ### Fixed
14
+
15
+ - `CartItem` / `AddToCart`: product, bundle, cluster-option, and crossupsell
16
+ names now resolve for the active `language` (via `getLocalizedValue`) instead
17
+ of always taking `names[0]`. Cart lines, the add-to-cart modal, and the
18
+ "added to cart" toast previously showed the first localised name regardless of
19
+ the cart/page language.
20
+
21
+ ## [0.4.18] - 2026-07-10
22
+
23
+ ### Added
24
+
25
+ - `ProductSpecifications`: new `beforeSpecs?` / `afterSpecs?` render-prop slots.
26
+ Each receives `{ layout: 'table' | 'list' }` and returns arbitrary content
27
+ injected at the start / end of the specifications — inside `<tbody>` for the
28
+ table layout (return a `<tr>`, e.g. a labelled "Unit of measure" row) or in
29
+ the list stack for the list layout (return a block). In grouped mode they
30
+ render once, above the first group / below the last. General replacement for
31
+ hardcoding extra rows; the existing `packageDescription` prop is unchanged.
32
+ - `ProductTabs`: `specificationsBeforeSpecs?` / `specificationsAfterSpecs?`
33
+ passthrough props that forward to the specifications section's new slots.
34
+
11
35
  ## [0.4.17] - 2026-07-09
12
36
 
13
37
  ### Added
package/dist/index.cjs CHANGED
@@ -3954,7 +3954,7 @@ function AddToCart(rawProps) {
3954
3954
  }
3955
3955
  function getModalName() {
3956
3956
  if (addedCartItem) {
3957
- return addedCartItem.product?.names?.[0]?.value || propellerV2CoreUi.getLocalizedValue(props.product?.names, props.language, "Product");
3957
+ return propellerV2CoreUi.getLocalizedValue(addedCartItem.product?.names, props.language, "") || propellerV2CoreUi.getLocalizedValue(props.product?.names, props.language, "Product");
3958
3958
  }
3959
3959
  return propellerV2CoreUi.getLocalizedValue(props.product?.names, props.language, "Product");
3960
3960
  }
@@ -4307,7 +4307,7 @@ function AddToCart(rawProps) {
4307
4307
  {
4308
4308
  className: "propeller-add-to-cart__modal-child flex justify-between items-center text-xs text-muted-foreground",
4309
4309
  children: [
4310
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "line-clamp-1", children: child.product?.names?.[0]?.value || "Option" }),
4310
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "line-clamp-1", children: propellerV2CoreUi.getLocalizedValue(child.product?.names, props.language, "Option") }),
4311
4311
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-foreground-subtle whitespace-nowrap ml-2", children: propellerV2CoreUi.formatPrice(
4312
4312
  ((props.includeTax !== void 0 ? !!props.includeTax : includeTax) ? child.totalSumNet : child.totalSum) ?? 0,
4313
4313
  { symbol: props.currency ?? "\u20AC" }
@@ -6060,7 +6060,7 @@ function CartItem(rawProps) {
6060
6060
  const minQuantity = getMinQuantity(props.cartItem.product);
6061
6061
  const step = getStep(props.cartItem.product);
6062
6062
  function getProductName5() {
6063
- return props.cartItem.product?.names?.[0]?.value || "Product";
6063
+ return propellerV2CoreUi.getLocalizedValue(props.cartItem.product?.names, props.language, "Product");
6064
6064
  }
6065
6065
  function getProductUrl2() {
6066
6066
  if (props.configuration?.urls && props.cartItem.product) {
@@ -6111,7 +6111,7 @@ function CartItem(rawProps) {
6111
6111
  if (!items) return "";
6112
6112
  const leader = items.find((bi) => bi.isLeader === propellerSdkV2.YesNo.Y);
6113
6113
  if (!leader) return "";
6114
- return leader.product.names?.[0]?.value || "Product";
6114
+ return propellerV2CoreUi.getLocalizedValue(leader.product.names, props.language, "Product");
6115
6115
  }
6116
6116
  function getBundleLeaderPrice() {
6117
6117
  const items = props.cartItem.bundle?.items;
@@ -6128,7 +6128,7 @@ function CartItem(rawProps) {
6128
6128
  return items.filter((bi) => bi.isLeader !== propellerSdkV2.YesNo.Y);
6129
6129
  }
6130
6130
  function getBundleItemName(bundleItem) {
6131
- return bundleItem.product.names?.[0]?.value || "Product";
6131
+ return propellerV2CoreUi.getLocalizedValue(bundleItem.product.names, props.language, "Product");
6132
6132
  }
6133
6133
  function getBundleItemPrice(bundleItem) {
6134
6134
  const price = bundleItem.price?.net;
@@ -6137,7 +6137,7 @@ function CartItem(rawProps) {
6137
6137
  }
6138
6138
  function getCrossupsellName(item) {
6139
6139
  const product = item?.productTo || item?.clusterTo;
6140
- return product?.names?.[0]?.value || "Product";
6140
+ return propellerV2CoreUi.getLocalizedValue(product?.names, props.language, "Product");
6141
6141
  }
6142
6142
  function getCrossupsellImageUrl(item) {
6143
6143
  const product = item?.productTo || item?.clusterTo;
@@ -6462,7 +6462,7 @@ function CartItemChildItems(props = {}) {
6462
6462
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: props.className ?? "propeller-cart-item__options mt-3 space-y-1.5 border-l-2 border-border pl-3", children: [
6463
6463
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "propeller-cart-item__options-label text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-1", children: propellerV2CoreUi.getLabel(resolved.labels, "includedOptions", "Included Options:") }),
6464
6464
  (cartItem.childItems || []).map((child, idx) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-cart-item__option flex flex-wrap gap-x-2 text-sm text-muted-foreground", children: [
6465
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: child.product.names?.[0]?.value || "Option" }),
6465
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: propellerV2CoreUi.getLocalizedValue(child.product.names, resolved.language, "Option") }),
6466
6466
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-foreground-subtle hidden sm:inline", children: "-" }),
6467
6467
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-foreground-subtle text-xs self-center", children: child.product.sku }),
6468
6468
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 border-b border-dotted border-border mx-1 mb-1" }),
@@ -15115,7 +15115,11 @@ function ProductSpecifications(rawProps) {
15115
15115
  if (!props.productId || !props.graphqlClient) return;
15116
15116
  fetchSpecs(props.productId);
15117
15117
  }, [props.productId]);
15118
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: !loading && (hasPublicAttributes() || props.packageDescription) ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsxs(
15118
+ const layoutMode = props.layout === "list" ? "list" : "table";
15119
+ const before = props.beforeSpecs?.({ layout: layoutMode });
15120
+ const after = props.afterSpecs?.({ layout: layoutMode });
15121
+ const hasSlotContent = before != null || after != null;
15122
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: !loading && (hasPublicAttributes() || props.packageDescription || hasSlotContent) ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsxs(
15119
15123
  "div",
15120
15124
  {
15121
15125
  className: `propeller-product-specifications ${props.className || ""}`,
@@ -15124,26 +15128,38 @@ function ProductSpecifications(rawProps) {
15124
15128
  children: [
15125
15129
  props.packageDescription ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "propeller-product-specifications__package-description mb-4 text-sm text-muted-foreground", children: props.packageDescription }) : null,
15126
15130
  !props.grouping ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
15127
- props.layout !== "list" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-hidden rounded-lg border border-border", children: /* @__PURE__ */ jsxRuntime.jsx("table", { className: "w-full text-sm", children: /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-border", children: getAttributes()?.map((attr, i) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "propeller-product-specifications__row odd:bg-card even:bg-muted/20", children: [
15128
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-2 font-medium text-foreground w-1/2", children: getAttributeLabel(attr) }),
15129
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-2 text-muted-foreground", children: getAttributeValue(attr) })
15130
- ] }, i)) }) }) }) : null,
15131
- props.layout === "list" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: getAttributes()?.map((attr, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-0.5", children: [
15132
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wide", children: getAttributeLabel(attr) }),
15133
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-foreground", children: getAttributeValue(attr) })
15134
- ] }, i)) }) : null
15131
+ props.layout !== "list" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-hidden rounded-lg border border-border", children: /* @__PURE__ */ jsxRuntime.jsx("table", { className: "w-full text-sm", children: /* @__PURE__ */ jsxRuntime.jsxs("tbody", { className: "divide-y divide-border", children: [
15132
+ before,
15133
+ getAttributes()?.map((attr, i) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "propeller-product-specifications__row odd:bg-card even:bg-muted/20", children: [
15134
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-2 font-medium text-foreground w-1/2", children: getAttributeLabel(attr) }),
15135
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-2 text-muted-foreground", children: getAttributeValue(attr) })
15136
+ ] }, i)),
15137
+ after
15138
+ ] }) }) }) : null,
15139
+ props.layout === "list" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
15140
+ before,
15141
+ getAttributes()?.map((attr, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-0.5", children: [
15142
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wide", children: getAttributeLabel(attr) }),
15143
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-foreground", children: getAttributeValue(attr) })
15144
+ ] }, i)),
15145
+ after
15146
+ ] }) : null
15135
15147
  ] }) : null,
15136
- !!props.grouping ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: getGroups()?.map((group) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6", children: [
15137
- !!group ? /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-sm font-semibold text-muted-foreground uppercase tracking-wide mb-2", children: group }) : null,
15138
- props.layout !== "list" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-hidden rounded-lg border border-border", children: /* @__PURE__ */ jsxRuntime.jsx("table", { className: "w-full text-sm", children: /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-border", children: getAttributesByGroup(group)?.map((attr, i) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "propeller-product-specifications__row odd:bg-card even:bg-muted/20", children: [
15139
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-2 font-medium text-foreground w-1/2", children: getAttributeLabel(attr) }),
15140
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-2 text-muted-foreground", children: getAttributeValue(attr) })
15141
- ] }, i)) }) }) }) : null,
15142
- props.layout === "list" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: getAttributesByGroup(group)?.map((attr, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-0.5", children: [
15143
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wide", children: getAttributeLabel(attr) }),
15144
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-foreground", children: getAttributeValue(attr) })
15145
- ] }, i)) }) : null
15146
- ] }, group)) }) : null
15148
+ !!props.grouping ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
15149
+ before ? layoutMode === "table" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-6 overflow-hidden rounded-lg border border-border", children: /* @__PURE__ */ jsxRuntime.jsx("table", { className: "w-full text-sm", children: /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-border", children: before }) }) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-6 space-y-3", children: before }) : null,
15150
+ getGroups()?.map((group) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6", children: [
15151
+ !!group ? /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-sm font-semibold text-muted-foreground uppercase tracking-wide mb-2", children: group }) : null,
15152
+ props.layout !== "list" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-hidden rounded-lg border border-border", children: /* @__PURE__ */ jsxRuntime.jsx("table", { className: "w-full text-sm", children: /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-border", children: getAttributesByGroup(group)?.map((attr, i) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "propeller-product-specifications__row odd:bg-card even:bg-muted/20", children: [
15153
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-2 font-medium text-foreground w-1/2", children: getAttributeLabel(attr) }),
15154
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-2 text-muted-foreground", children: getAttributeValue(attr) })
15155
+ ] }, i)) }) }) }) : null,
15156
+ props.layout === "list" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: getAttributesByGroup(group)?.map((attr, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-0.5", children: [
15157
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wide", children: getAttributeLabel(attr) }),
15158
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-foreground", children: getAttributeValue(attr) })
15159
+ ] }, i)) }) : null
15160
+ ] }, group)),
15161
+ after ? layoutMode === "table" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-6 overflow-hidden rounded-lg border border-border", children: /* @__PURE__ */ jsxRuntime.jsx("table", { className: "w-full text-sm", children: /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-border", children: after }) }) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: after }) : null
15162
+ ] }) : null
15147
15163
  ]
15148
15164
  }
15149
15165
  ) }) : null });
@@ -15352,7 +15368,9 @@ function ProductTabs(rawProps) {
15352
15368
  language: props.language,
15353
15369
  layout: props.specificationsLayout,
15354
15370
  grouping: props.specificationsGrouping,
15355
- packageDescription: props.specificationsPackageDescription
15371
+ packageDescription: props.specificationsPackageDescription,
15372
+ beforeSpecs: props.specificationsBeforeSpecs,
15373
+ afterSpecs: props.specificationsAfterSpecs
15356
15374
  }
15357
15375
  ) }) : null,
15358
15376
  isActive("downloads") && isTabVisible("downloads") ? /* @__PURE__ */ jsxRuntime.jsx(