@propeller-commerce/propeller-v2-react-ui 0.27.0 → 0.29.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,125 @@ 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.29.0] - 2026-10-05
12
+
13
+ ### Fixed
14
+
15
+ - **Listings no longer drop the company's orderlist on a client-side fetch.**
16
+ `useProductSearch` sent `applyOrderlists: false` whenever the caller passed
17
+ no explicit `orderlistIds` — which is every app — so the backend was told to
18
+ ignore the company's own orderlists. The server render omits the flag and is
19
+ correctly scoped, so a category page painted the right products and then the
20
+ first sort, filter, page or company change replaced them with the unscoped
21
+ catalogue, priced and orderable. The flag is now sent only when the caller
22
+ actually asks for it, in both the grid fetch and the search-bar preview.
23
+ `useQuickOrder` had the same default, which let a bulk paste resolve SKUs
24
+ outside the contract.
25
+
26
+ Callers that relied on the implicit `false` for an intentionally unscoped
27
+ listing must now pass `applyOrderlists={false}` explicitly.
28
+
29
+ - **`<ProductSlider>` is scoped to the viewer and priced for their company.**
30
+ `fetchProducts` sent no `userId`, no `companyId` and no
31
+ `priceCalculateProductInput`, and resolved its ids through the flat
32
+ `products` resolver, which does not apply orderlist scoping. A featured
33
+ slider therefore showed products outside the company's assortment at the
34
+ default company's prices. Its `filterToAssortment` step — whose only job is
35
+ dropping out-of-assortment crossupsells — asked the same resolver and so
36
+ could never filter anything. Both now go through `category.products` over
37
+ `configuration.baseCategoryId`, the path the grid and quick order already
38
+ use. Without a resolved base category the primary fetch returns nothing
39
+ rather than an unscoped list, while the crossupsell check falls through to
40
+ the unverified items so a slider is never blanked by a failed check.
41
+
42
+ - **Cart quantity steppers keep the line on the order grid.** `CartItem` read
43
+ `minimumQuantity` and the step but used them only to disable the decrement
44
+ button: the buttons added or subtracted the step from whatever was there, so
45
+ a line already off the grid stayed off it (min 2, step 3: 9 → 12). The typed
46
+ field committed only values at or above the minimum, so clearing it or typing
47
+ below the minimum left the old quantity stuck. Both steppers now resolve to
48
+ the grid first, and editing goes through a draft value that resolves on blur
49
+ — the same rule `<AddToCart>` and `<QuickOrder>` already followed.
50
+
51
+ - **Add to cart recovers when the remembered cart is gone.** A cart ordered in
52
+ another browser or deleted leaves its id behind in the host's storage, and
53
+ every later `cartAddItem` answered `CART_NOT_FOUND_ERROR`. Nothing forgot the
54
+ id, so each retry failed identically and the badge kept showing the dead
55
+ cart. `addItem` now forgets it and starts a fresh cart once, then re-adds;
56
+ the recovery is bounded because cart initialisation only ever returns an
57
+ `OPEN` cart, and it is skipped when the caller passed `createCart: false`.
58
+
59
+ - **`useProductInfo` no longer sends every company orderlist as an allowlist.**
60
+ It fetched all orderlists for the company and passed their ids to
61
+ `product(orderlistIds:)` with no filter on type, so a NEGATIVE (exclusion)
62
+ list was applied as an allowlist and inverted the filter. Scoping is left to
63
+ the backend, which merges positive and negative lists itself — one fewer
64
+ request per product page.
65
+
66
+ ### Changed
67
+
68
+ - **The min/step rule comes from `propeller-v2-core-ui`.** `AddToCart`,
69
+ `QuickOrder` and `CartItem` each carried their own copy, and they had
70
+ drifted — two of them divided by an unguarded step. All now call
71
+ `resolveOrderableQuantity` from core-ui 0.11.0, which is the new minimum for
72
+ this package.
73
+
74
+ ## [0.28.0] - 2026-10-02
75
+
76
+ ### Added
77
+
78
+ - **`imgComponent` on `<PropellerDepsProvider>` — one setting routes every
79
+ image through the host’s image component.** Components rendered a bare
80
+ `<img src="https://media.…">`, so catalog images were fetched straight from
81
+ the media CDN. That CDN sends no `X-Robots-Tag` and serves no `robots.txt`,
82
+ so those images sat outside the storefront’s crawler directives and got none
83
+ of the host framework’s image optimisation. The existing `imageComponent`
84
+ slot could not close this: it takes a `product`/`cluster` and resolves the
85
+ URL itself, so it only fitted five surfaces, and the other nineteen image
86
+ sites had already resolved a URL string by render time. Set `imgComponent`
87
+ once on the provider and images across the package route through it,
88
+ including components added later. Optional — omitting it keeps the previous
89
+ markup. (PWP-1001)
90
+ - **`<PropellerImg>` and `useImgComponent()` are exported.** Hosts composing
91
+ their own cards get the same seam; `<PropellerImg>` is also on the `/pure`
92
+ entry for Server Components.
93
+ - **`OrderItemCard` accepts `imgComponent` as a prop.** It is `@rsc-safe` and
94
+ reads no context, so the provider cannot reach it — same direct-prop
95
+ convention as its existing `priceComponent` / `stockComponent`.
96
+
97
+ ### Changed
98
+
99
+ - **Nineteen image sites now render `<PropellerImg>`** across `ProductCard`,
100
+ `ClusterCard`, `ProductGallery`, `CartItem`, `OrderItemCard`, `SearchBar`,
101
+ `ProductBundles`, `MachineCard`, `AddToCart`, `QuickOrder`, `ItemsOverview`,
102
+ `FavoriteListItem`, `FavoriteListDetails`, `ClusterOptions`,
103
+ `ClusterConfigurator`, `CartBonusItems` and `DefaultProductImage`. Markup is
104
+ unchanged when no component is injected.
105
+ - **Carrier and payment-provider logos stay plain `<img>`.** Their domains sit
106
+ outside the host’s image allowlist, so routing them through a framework
107
+ image component would stop them loading. They are catalog-external and not
108
+ crawlable.
109
+ - **`npm run check:img` fails the build on a raw `<img>`.** The seam only holds
110
+ if new components use it, and a raw `<img>` opts a surface out silently.
111
+ Wired into `validate` and `prepublishOnly`.
112
+
113
+ ### Fixed
114
+
115
+ - **The "no image" placeholder was two different icons.** Cards, favourites and
116
+ `DefaultProductImage` drew one glyph; cart rows, the cart drawer, bundles, the
117
+ add-to-cart modal and the checkout overview drew another, at a heavier stroke
118
+ weight — so the same missing-image product looked like two different states
119
+ depending on which surface showed it. All of them now use the card icon at a
120
+ consistent stroke weight. The machine card keeps its own glyph, which is
121
+ deliberate.
122
+ - **The cart drawer’s image box did not centre its contents.** It overrides
123
+ `CartItem.Image`’s class, and the override dropped the flex centring, so the
124
+ placeholder sat top-left at its natural size instead of filling the box.
125
+ - **Placeholder icons were exposed to screen readers.** They carry no
126
+ information the row does not already state in text, so they now set
127
+ `aria-hidden`, matching what `DefaultProductImage` already did.
128
+ ---
129
+
11
130
  ## [0.27.0] - 2026-09-28
12
131
 
13
132
  ### Fixed