@propeller-commerce/propeller-v2-react-ui 0.11.4 → 0.12.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
@@ -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.12.0] - 2026-07-31
12
+
13
+ ### Added
14
+
15
+ - **`productTrackAttributes` on `<ProductGrid>`, `<SearchBar>` and
16
+ `useProductSearch`.** Pass the attribute names a card renders — e.g.
17
+ `productTrackAttributes={['MPN']}` — and they arrive on
18
+ `product.attributes` for every item in the result, ready to read from a
19
+ card slot like `belowName`. `useProductInfo` has had an option of this name
20
+ since the PDP work; the grid side simply never forwarded one, so hosts that
21
+ set it in their portal config saw it silently ignored on listings and had
22
+ to fetch attributes themselves in a second request.
23
+
24
+ Requires `propeller-sdk-v2` >= 0.16.0, where the `category` and `products`
25
+ operations gained `$attributeResultSearchInput` and `ProductGridFields`
26
+ gained the matching `attributes(input:)` selection.
27
+
28
+ Naming the attributes you render is not just a payload optimisation — it is
29
+ the correct behaviour. With no input the server returns the *first page* of
30
+ a product's attributes (12 per product), so on a product carrying more than
31
+ 12 the ones on later pages are missing with no error. Filtering to the names
32
+ you need also cut a 24-product category response from ~168 KB to ~124 KB in
33
+ our testing.
34
+
11
35
  ## [0.11.4] - 2026-07-31
12
36
 
13
37
  ### Fixed
package/dist/index.cjs CHANGED
@@ -2090,7 +2090,7 @@ function useProductInfo(options) {
2090
2090
  if (options.user && "customerId" in options.user) input.customerId = options.user.customerId;
2091
2091
  return input;
2092
2092
  }
2093
- function buildAttributeInput() {
2093
+ function buildAttributeInput2() {
2094
2094
  const names = options.productTrackAttributes;
2095
2095
  if (!names || names.length === 0) return void 0;
2096
2096
  return { attributeDescription: { names } };
@@ -2113,7 +2113,7 @@ function useProductInfo(options) {
2113
2113
  orderlistIds = (orderlists?.items ?? []).map((ol) => ol.id);
2114
2114
  }
2115
2115
  const service = propellerV2CoreUi.createServices(graphqlClient).product;
2116
- const attributeInput = buildAttributeInput();
2116
+ const attributeInput = buildAttributeInput2();
2117
2117
  const variables = {
2118
2118
  productId,
2119
2119
  language,
@@ -2203,6 +2203,10 @@ function useProductInfo(options) {
2203
2203
  };
2204
2204
  }
2205
2205
  var inflightFetches2 = /* @__PURE__ */ new Set();
2206
+ function buildAttributeInput(names) {
2207
+ if (!names || names.length === 0) return void 0;
2208
+ return { attributeDescription: { names } };
2209
+ }
2206
2210
  function useProductSearch(options) {
2207
2211
  const { graphqlClient, configuration } = options;
2208
2212
  const isControlled = options.products !== void 0;
@@ -2335,6 +2339,7 @@ function useProductSearch(options) {
2335
2339
  const filterAvailableAttributeInput = {
2336
2340
  isSearchable: true
2337
2341
  };
2342
+ const attributeInput = buildAttributeInput(options.productTrackAttributes);
2338
2343
  const variables = {
2339
2344
  categoryId: catId,
2340
2345
  language: lang,
@@ -2342,7 +2347,8 @@ function useProductSearch(options) {
2342
2347
  priceCalculateProductInput,
2343
2348
  filterAvailableAttributeInput,
2344
2349
  imageSearchFilters: configuration?.imageSearchFiltersGrid,
2345
- imageVariantFilters: configuration?.imageVariantFiltersMedium
2350
+ imageVariantFilters: configuration?.imageVariantFiltersMedium,
2351
+ ...attributeInput && { attributeResultSearchInput: attributeInput }
2346
2352
  };
2347
2353
  const response = await service.getCategory(variables);
2348
2354
  if (thisId !== fetchIdRef.current) return;
@@ -2403,7 +2409,9 @@ function useProductSearch(options) {
2403
2409
  taxZone,
2404
2410
  pageSize,
2405
2411
  pagination.currentPage,
2406
- configuration
2412
+ configuration,
2413
+ // Stringified: callers pass an array literal, so identity changes each render.
2414
+ JSON.stringify(options.productTrackAttributes ?? [])
2407
2415
  ]);
2408
2416
  const search = React6.useCallback(
2409
2417
  (term) => {
@@ -2482,13 +2490,15 @@ function useProductSearch(options) {
2482
2490
  ...contactId !== void 0 && { contactId },
2483
2491
  ...customerId !== void 0 && { customerId }
2484
2492
  };
2493
+ const attributeInput = buildAttributeInput(options.productTrackAttributes);
2485
2494
  const variables = {
2486
2495
  categoryId: catId,
2487
2496
  language,
2488
2497
  categoryProductSearchInput,
2489
2498
  priceCalculateProductInput,
2490
2499
  imageSearchFilters: configuration?.imageSearchFiltersGrid,
2491
- imageVariantFilters: configuration?.imageVariantFiltersMedium
2500
+ imageVariantFilters: configuration?.imageVariantFiltersMedium,
2501
+ ...attributeInput && { attributeResultSearchInput: attributeInput }
2492
2502
  };
2493
2503
  const response = await service.getCategory(variables);
2494
2504
  const productsResponse = response?.products;
@@ -2507,7 +2517,7 @@ function useProductSearch(options) {
2507
2517
  }, 300);
2508
2518
  },
2509
2519
  // eslint-disable-next-line react-hooks/exhaustive-deps
2510
- [graphqlClient, language, configuration, userKey, JSON.stringify(options.orderlistIds ?? []), options.applyOrderlists]
2520
+ [graphqlClient, language, configuration, userKey, JSON.stringify(options.orderlistIds ?? []), options.applyOrderlists, JSON.stringify(options.productTrackAttributes ?? [])]
2511
2521
  );
2512
2522
  React6.useEffect(() => {
2513
2523
  if (!isControlled) fetchProducts();
@@ -9034,6 +9044,7 @@ function ProductGrid(rawProps) {
9034
9044
  brand: props.brand,
9035
9045
  orderlistIds: props.orderlistIds,
9036
9046
  applyOrderlists: props.applyOrderlists,
9047
+ productTrackAttributes: props.productTrackAttributes,
9037
9048
  language: props.language,
9038
9049
  taxZone: props.taxZone,
9039
9050
  user: props.user,
@@ -18396,7 +18407,8 @@ function SearchBar(rawProps) {
18396
18407
  user: props.user,
18397
18408
  companyId: props.companyId,
18398
18409
  orderlistIds: props.orderlistIds,
18399
- applyOrderlists: props.applyOrderlists
18410
+ applyOrderlists: props.applyOrderlists,
18411
+ productTrackAttributes: props.productTrackAttributes
18400
18412
  });
18401
18413
  const results = searchResults.slice(0, maxResults).map((item) => mapToSearchBarResult(item, props.language));
18402
18414
  const itemsFound = searchItemsFound;