@propeller-commerce/propeller-v2-react-ui 0.15.11 → 0.18.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,113 @@ 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.18.0] - 2026-08-20
12
+
13
+ ### Added
14
+
15
+ - **After-hooks now say WHAT happened, not just that something did.** Three
16
+ callbacks reported success with no argument, which made them unusable for
17
+ anything that has to distinguish the outcome — analytics, audit trails, or
18
+ optimistic UI. All three arguments are optional, so existing zero-argument
19
+ callbacks keep working unchanged.
20
+ - `AddToFavorite.onFavoriteChanged(change?)` — `{ action: 'added' | 'removed',
21
+ listId, productId, clusterId }`. It fired identically for an add and a
22
+ removal, so a host could not report "added to wishlist" without inventing
23
+ the direction, and a wishlist metric that also counts removals is worse
24
+ than no metric.
25
+ - `FavoriteLists.onListChanged(change?)` / `useFavorites` — `{ action:
26
+ 'created' | 'updated' | 'deleted', listId, name, isDefault }`. Note the
27
+ existing `onCreate` / `onEdit` / `onDelete` props are *overrides* that
28
+ replace the default mutation, so they were never usable as notifications.
29
+ - `QuickOrder.onTemplateDownload()` — the template link was a bare `<a>`, so
30
+ a buyer fetching the spreadsheet left no trace. Navigation is untouched.
31
+
32
+ ### Notes
33
+
34
+ - `QuoteActions` deliberately gains no `afterReject`: the component has no
35
+ reject action to hook — only accept. A rejection callback would be dead API.
36
+ - `PurchaseAuthorizationRequests` needs no new prop either; the existing
37
+ `afterDeleteRequest` already covers the manager rejecting a request, which is
38
+ what "delete" means in that UI.
39
+
40
+ ## [0.17.0] - 2026-08-20
41
+
42
+ ### Fixed
43
+
44
+ - **A host `className` override could not beat the component's own utility
45
+ (PWP-942 #17).** Class lists were built by string-appending the override
46
+ after the package's defaults, which decides nothing — the cascade does, not
47
+ attribute order. `iconClassName="text-cocoa"` on `AccountIconAndMenu` (baked-in
48
+ `text-white`) produced an element carrying both and rendered white, while the
49
+ same override on `CartIconAndSidebar` appeared to work only because its
50
+ default happened to lose anyway. Every override site now merges through
51
+ `tailwind-merge`, so a conflicting utility is replaced rather than raced.
52
+ 59 sites across 47 components; BEM hooks and non-conflicting utilities are
53
+ untouched.
54
+
55
+ - **Anonymous catalog listings were scoped differently on the client than on
56
+ the server (PWP-942 #22).** Listing hooks derived `userId` only from a
57
+ logged-in user and omitted the key entirely for guests, while a server-side
58
+ seed scopes anonymous queries to the channel's `anonymousUserId`. The two
59
+ therefore asked different questions, and the client refetch quietly replaced
60
+ a correctly-scoped product list with a differently-scoped one — assortment
61
+ rules, negative order lists in particular, are applied per user. All five
62
+ listing hooks (`useProductSearch` ×2, `useProductSlider`, `useQuickOrder`,
63
+ `useSpareParts`) now resolve the id through one shared helper. Hosts that
64
+ supply no `anonymousUserId` behave exactly as before.
65
+
66
+ ### Added
67
+
68
+ - **`configuration.anonymousUserId`** — the channel's guest account, resolved
69
+ server-side and handed to the package so client-side listings can scope to
70
+ it. Same route `baseCategoryId` takes since PWP-913; no module guesses it.
71
+
72
+ ## [0.16.0] - 2026-08-12
73
+
74
+ ### Fixed
75
+
76
+ - **Localized names and slugs ignored the storefront language (PWP-936 /
77
+ PWP-940).** A localized array carries one entry per authored language in
78
+ catalog order, so `names[0]` / `slugs[0]` is the catalog's DEFAULT language,
79
+ not the storefront's. Thirteen sites read index 0 unconditionally while
80
+ eleven others resolved by language correctly. Now all of them go through
81
+ `getLocalizedValue`, which prefers the active language and falls back to any
82
+ translation that has a value:
83
+ - `ClusterOptions` — option group and option product names (PWP-936)
84
+ - `FavoriteListItem`, `FavoriteListDetails` — product and cluster names
85
+ - `ItemsOverview` — item, bundle-leader, bundle-item and child-option names
86
+ - `ProductBundles` — bundle item names
87
+ - `OrderItemCard` — item name, child-item names, and **the cluster/product
88
+ slugs used to build the item's link**
89
+ - `SearchBar` — **the slug used to build each autosuggest result's link**
90
+
91
+ The three slug sites are the ones that actually broke: they emitted
92
+ default-language URLs on every non-default locale.
93
+ - **`useCart` never adopted a cart id that arrived after mount (PWP-938 /
94
+ PWP-937).** The id was seeded once with `useState(options.cartId || '')` and
95
+ only reassigned from the hook's own cart-creation path, so a component that
96
+ mounted before the cart resolved held `''` permanently. `CartIconAndSidebar`
97
+ renders in the header on the first paint of every page, so its "Request
98
+ authorization" button rendered enabled, fired, and returned `err('No cart')`
99
+ with nothing sent — the reported "cannot submit purchase request". The id is
100
+ now derived from the prop on every render. `processCart` was affected the
101
+ same way.
102
+
103
+ ### Changed
104
+
105
+ - **BREAKING (behavioural, hosts should act):** `OrderItemCard` gains a
106
+ `language` prop. The component is exported from the RSC-safe `/pure` entry
107
+ and reads no context by design, so a host that renders it directly must pass
108
+ `language` or it keeps resolving in the catalog's default language.
109
+ `OrderBonusItems` forwards its own resolved language automatically.
110
+ - `ClusterOptions` resolves `language` from `<PropellerProvider>` via
111
+ `useInfraProps`, like the other catalog components. Passing it explicitly
112
+ still wins.
113
+ - The favorites list's add-product button default now reads "Add product to
114
+ favorite list" instead of "Add product directly to this wishlist" (PWP-939).
115
+ Hosts that translate `FavoriteListDetails.addProductDirectly` should update
116
+ their own copy — "favorite list" is the agreed term, not "wishlist".
117
+
11
118
  ## [0.15.11] - 2026-08-11
12
119
 
13
120
  ### Fixed
package/README.md CHANGED
@@ -307,9 +307,17 @@ and pass it once.
307
307
  | `language` | `string` | Locale code used to resolve localized content (e.g. `'NL'`, `'EN'`). |
308
308
  | `includeTax` | `boolean` | Whether displayed prices include tax. |
309
309
  | `currency` | `string` | Currency symbol for price formatting. Default: `'€'`. |
310
- | `configuration` | `unknown` | Free-form config bag forwarded to components — stuff your own settings in. |
310
+ | `configuration` | `unknown` | Config bag forwarded to components. Recognized keys: `baseCategoryId`, `anonymousUserId` (see below), image filters — plus anything of your own. |
311
311
  | `portalMode` | `string` | Storefront mode (e.g. `'open'`, `'closed'`). |
312
312
 
313
+ **`configuration.anonymousUserId`** — the channel's guest account. Listing
314
+ hooks scope logged-out catalog queries to it, so the client asks the same
315
+ question your server-rendered seed did. Resolve it server-side (only the
316
+ server can reach the channel) and pass it in; when it's absent, anonymous
317
+ queries send no `userId` and the backend falls back to the api key's own
318
+ scope. Getting this wrong is invisible — the grid renders, just with the
319
+ wrong assortment.
320
+
313
321
  The `value` object is reactive — when your auth/company/language state
314
322
  changes, recompute it (memoize on those dependencies) and the provider
315
323
  propagates the new value. Components re-render with fresh infra; service
@@ -584,15 +592,19 @@ cascade order — no `!important` needed:
584
592
  .propeller-breadcrumbs__separator { display: none; }
585
593
  ```
586
594
 
587
- **3. Per-instance `className`** — every component appends `props.className`
588
- on its root, so a one-off override is a regular prop:
595
+ **3. Per-instance `className`** — every component merges `props.className`
596
+ into its root classes, so a one-off override is a regular prop:
589
597
 
590
598
  ```tsx
591
599
  <ProductCard product={p} className="ring-2 ring-yellow-400" />
592
600
  ```
593
601
 
594
- `className` **adds to** the base classes — it doesn't replace them. To strip
595
- a default, use a BEM hook.
602
+ The merge runs through [`tailwind-merge`](https://github.com/dcastil/tailwind-merge)
603
+ (since 0.17.0), so an override **replaces** the base utility it conflicts with
604
+ rather than racing it in the cascade — `iconClassName="text-cocoa"` on a
605
+ component whose default is `text-white` now renders cocoa. Non-conflicting
606
+ utilities and the BEM hooks are left untouched. To remove a default outright
607
+ (rather than override it), use a BEM hook.
596
608
 
597
609
  See [STYLING.md](./STYLING.md) for the full token list, the complete BEM
598
610
  hook catalog, and the cascade rationale.