@propeller-commerce/propeller-v2-react-ui 0.4.15 → 0.4.16

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,31 @@ 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.16] - 2026-07-09
12
+
13
+ ### Added
14
+
15
+ - `ProductGrid`, `SearchBar`, `ProductInfo`: new `orderlistIds?: number[]` and
16
+ `applyOrderlists?: boolean` props to scope the product/search fetch to
17
+ specific orderlists (e.g. a chosen B2B contract). When `orderlistIds` is
18
+ non-empty, orderlists are applied (unless `applyOrderlists` is `false`);
19
+ otherwise `applyOrderlists: false` is sent so an authenticated user without a
20
+ contract still sees the full catalogue. Threaded through `useProductSearch`
21
+ and `useProductInfo` (explicit ids override the composable's default
22
+ all-company-orderlists resolution).
23
+ - `Menu`: new `getUrl?: (category: Category) => string` prop — a custom URL
24
+ builder that overrides `menuLinkFormat` / `configuration.urls.getCategoryUrl`,
25
+ letting hosts inject dynamic query strings (e.g. `?contract=…`). Mirrors the
26
+ existing `Breadcrumbs.getUrl`.
27
+ - `GridToolbar`: new `hidePriceSort?: boolean` prop to hide the price sort
28
+ option entirely (closed B2B portals where prices are "by quotation").
29
+ - `ProductTabs` / `ProductSpecifications`: new
30
+ `specificationsPackageDescription?` (ProductTabs) → `packageDescription?`
31
+ (ProductSpecifications) passthrough, rendering an extra package-description
32
+ string above the specifications table.
33
+
34
+ All additions are additive and backward-compatible.
35
+
11
36
  ## [0.4.15] - 2026-07-08
12
37
 
13
38
  ### Changed
package/dist/index.cjs CHANGED
@@ -2045,7 +2045,13 @@ function useProductInfo(options) {
2045
2045
  setError(null);
2046
2046
  try {
2047
2047
  let orderlistIds = [];
2048
- if (options.user && options.companyId) {
2048
+ let applyOrderlists = true;
2049
+ if (options.orderlistIds && options.orderlistIds.length > 0) {
2050
+ orderlistIds = options.orderlistIds;
2051
+ applyOrderlists = options.applyOrderlists !== false;
2052
+ } else if (options.applyOrderlists === false) {
2053
+ applyOrderlists = false;
2054
+ } else if (options.user && options.companyId) {
2049
2055
  const orderlistService = propellerV2CoreUi.createServices(graphqlClient).orderlist;
2050
2056
  const searchInput = { companyIds: [options.companyId] };
2051
2057
  const orderlists = await orderlistService.getOrderlists(searchInput);
@@ -2056,7 +2062,7 @@ function useProductInfo(options) {
2056
2062
  const variables = {
2057
2063
  productId,
2058
2064
  language,
2059
- applyOrderlists: true,
2065
+ applyOrderlists,
2060
2066
  orderlistIds,
2061
2067
  imageSearchFilters: imageSearchFilters ?? configuration.imageSearchFiltersGrid,
2062
2068
  imageVariantFilters: imageVariantFilters ?? configuration.imageVariantFiltersLarge ?? configuration.imageVariantFiltersSmall,
@@ -2240,6 +2246,10 @@ function useProductSearch(options) {
2240
2246
  const userId = options.user && "contactId" in options.user ? options.user.contactId : options.user && "customerId" in options.user ? options.user.customerId : void 0;
2241
2247
  const contactId = options.user && "contactId" in options.user ? options.user.contactId : void 0;
2242
2248
  const customerId = options.user && "customerId" in options.user ? options.user.customerId : void 0;
2249
+ const orderlistScope = options.orderlistIds && options.orderlistIds.length > 0 ? {
2250
+ applyOrderlists: options.applyOrderlists !== false,
2251
+ orderlistIds: options.orderlistIds
2252
+ } : { applyOrderlists: false };
2243
2253
  const categoryProductSearchInput = {
2244
2254
  language: lang,
2245
2255
  page: pagination.currentPage,
@@ -2257,7 +2267,8 @@ function useProductSearch(options) {
2257
2267
  ...priceFilter && { price: priceFilter },
2258
2268
  ...sortInputs.length && { sortInputs },
2259
2269
  ...options.companyId && { companyId: options.companyId },
2260
- ...userId !== void 0 && { userId }
2270
+ ...userId !== void 0 && { userId },
2271
+ ...orderlistScope
2261
2272
  };
2262
2273
  const priceCalculateProductInput = {
2263
2274
  taxZone,
@@ -7430,7 +7441,7 @@ function CategoryDescription(props) {
7430
7441
  const plain = html.replace(/<[^>]*>/g, "");
7431
7442
  if (plain.length <= maxLen) return html;
7432
7443
  const truncated = plain.substring(0, maxLen);
7433
- return truncated.substring(0, truncated.lastIndexOf(" ")) + "\xE2\u20AC\xA6";
7444
+ return truncated.substring(0, truncated.lastIndexOf(" ")) + "\u2026";
7434
7445
  }
7435
7446
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: !!html ? /* @__PURE__ */ jsxRuntime.jsxs(
7436
7447
  "div",
@@ -10697,7 +10708,8 @@ function GridToolbar(rawProps) {
10697
10708
  }
10698
10709
  function getSortOptions() {
10699
10710
  const opts = props.sortOptions || [];
10700
- return opts.length > 0 ? opts : ALL_SORT_FIELDS;
10711
+ const base = opts.length > 0 ? opts : ALL_SORT_FIELDS;
10712
+ return props.hidePriceSort ? base.filter((f) => f !== propellerSdkV2.ProductSortField.PRICE) : base;
10701
10713
  }
10702
10714
  function getOffsetOptions() {
10703
10715
  const opts = props.offset || [];
@@ -11120,10 +11132,12 @@ function Menu(rawProps) {
11120
11132
  }
11121
11133
  function getCategoryUrl(cat) {
11122
11134
  const lang = props.language || "NL";
11123
- return props.configuration.urls.getCategoryUrl(
11124
- { categoryId: cat.categoryId, slug: [{ value: cat.slug, language: lang }] },
11125
- lang
11126
- );
11135
+ const category = {
11136
+ categoryId: cat.categoryId,
11137
+ slug: [{ value: cat.slug, language: lang }]
11138
+ };
11139
+ if (props.getUrl) return props.getUrl(category);
11140
+ return props.configuration.urls.getCategoryUrl(category, lang);
11127
11141
  }
11128
11142
  function getSubCategories(cat) {
11129
11143
  return (cat.children || []).filter((sub) => sub.name && sub.slug);
@@ -13938,7 +13952,7 @@ function useProductGrid() {
13938
13952
  const ctx = React6.useContext(ProductGridSearchContext);
13939
13953
  if (!ctx) {
13940
13954
  throw new Error(
13941
- "<ProductGrid.X> must be rendered inside <ProductGrid>. Got null context \xE2\u20AC\u201D wrap the subcomponents in <ProductGrid categoryId={...}>...</ProductGrid>."
13955
+ "<ProductGrid.X> must be rendered inside <ProductGrid>. Got null context \u2014 wrap the subcomponents in <ProductGrid categoryId={...}>...</ProductGrid>."
13942
13956
  );
13943
13957
  }
13944
13958
  return ctx;
@@ -13959,6 +13973,8 @@ function ProductGrid(rawProps) {
13959
13973
  categoryId: props.categoryId,
13960
13974
  term: props.term,
13961
13975
  brand: props.brand,
13976
+ orderlistIds: props.orderlistIds,
13977
+ applyOrderlists: props.applyOrderlists,
13962
13978
  language: props.language,
13963
13979
  taxZone: props.taxZone,
13964
13980
  user: props.user,
@@ -14361,9 +14377,15 @@ function useProductInfoContext() {
14361
14377
  }
14362
14378
  function ProductInfo(rawProps) {
14363
14379
  const props = useInfraProps(rawProps);
14364
- const { product: fetchedProduct, loading, fetchProduct } = useProductInfo(
14365
- props.graphqlClient ? { graphqlClient: props.graphqlClient, language: props.language, configuration: props.configuration } : { graphqlClient: {}, language: props.language, configuration: props.configuration }
14366
- );
14380
+ const { product: fetchedProduct, loading, fetchProduct } = useProductInfo({
14381
+ graphqlClient: props.graphqlClient ?? {},
14382
+ language: props.language,
14383
+ configuration: props.configuration,
14384
+ user: props.user,
14385
+ companyId: props.companyId,
14386
+ orderlistIds: props.orderlistIds,
14387
+ applyOrderlists: props.applyOrderlists
14388
+ });
14367
14389
  const activeProduct = props.product || fetchedProduct;
14368
14390
  function getProductName5() {
14369
14391
  if (!activeProduct) return "";
@@ -15081,13 +15103,14 @@ function ProductSpecifications(rawProps) {
15081
15103
  if (!props.productId || !props.graphqlClient) return;
15082
15104
  fetchSpecs(props.productId);
15083
15105
  }, [props.productId]);
15084
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: !loading && hasPublicAttributes() ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsxs(
15106
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: !loading && (hasPublicAttributes() || props.packageDescription) ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsxs(
15085
15107
  "div",
15086
15108
  {
15087
15109
  className: `propeller-product-specifications ${props.className || ""}`,
15088
15110
  "data-layout": props.layout === "list" ? "list" : "table",
15089
15111
  "data-grouped": props.grouping ? "true" : "false",
15090
15112
  children: [
15113
+ props.packageDescription ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "propeller-product-specifications__package-description mb-4 text-sm text-muted-foreground", children: props.packageDescription }) : null,
15091
15114
  !props.grouping ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
15092
15115
  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: [
15093
15116
  /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-2 font-medium text-foreground w-1/2", children: getAttributeLabel(attr) }),
@@ -15316,7 +15339,8 @@ function ProductTabs(rawProps) {
15316
15339
  attributes: getSpecsAttributes(),
15317
15340
  language: props.language,
15318
15341
  layout: props.specificationsLayout,
15319
- grouping: props.specificationsGrouping
15342
+ grouping: props.specificationsGrouping,
15343
+ packageDescription: props.specificationsPackageDescription
15320
15344
  }
15321
15345
  ) }) : null,
15322
15346
  isActive("downloads") && isTabVisible("downloads") ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -15419,7 +15443,8 @@ function ProductTabs(rawProps) {
15419
15443
  attributes: getSpecsAttributes(),
15420
15444
  language: props.language,
15421
15445
  layout: props.specificationsLayout,
15422
- grouping: props.specificationsGrouping
15446
+ grouping: props.specificationsGrouping,
15447
+ packageDescription: props.specificationsPackageDescription
15423
15448
  }
15424
15449
  ) }) : null
15425
15450
  ] }) : null,
@@ -16206,7 +16231,7 @@ function RegisterForm(rawProps) {
16206
16231
  const contactLabel = props.labels?.contactLabel || "Company";
16207
16232
  const customerLabel = props.labels?.customerLabel || "Consumer";
16208
16233
  const emailPlaceholder = props.labels?.emailPlaceholder || "name@example.com";
16209
- const passwordPlaceholder = props.labels?.passwordPlaceholder || "\xE2\u20AC\xA2\xE2\u20AC\xA2\xE2\u20AC\xA2\xE2\u20AC\xA2\xE2\u20AC\xA2\xE2\u20AC\xA2\xE2\u20AC\xA2\xE2\u20AC\xA2";
16234
+ const passwordPlaceholder = props.labels?.passwordPlaceholder || "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
16210
16235
  const passwordMismatchText = props.labels?.passwordMismatch || "Passwords do not match";
16211
16236
  const loginText = props.labels?.loginText || "Already have an account?";
16212
16237
  const loginLinkText = props.labels?.loginLink || "Log in";
@@ -17110,7 +17135,9 @@ function SearchBar(rawProps) {
17110
17135
  graphqlClient: props.graphqlClient,
17111
17136
  language: props.language,
17112
17137
  configuration: props.configuration || {},
17113
- companyId: props.companyId
17138
+ companyId: props.companyId,
17139
+ orderlistIds: props.orderlistIds,
17140
+ applyOrderlists: props.applyOrderlists
17114
17141
  });
17115
17142
  const results = searchResults.slice(0, maxResults).map((item) => mapToSearchBarResult(item));
17116
17143
  const itemsFound = searchItemsFound;