@lime-bundles/react 7.3.1 → 7.4.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/README.md CHANGED
@@ -124,10 +124,14 @@ Full CSS variable reference: [css-variables.md](./docs/css-variables.md).
124
124
 
125
125
  `<FixedBundle>`, `<VolumeBundle>`, `<MixMatchBundle>`, and `useBundleData` accept four optional props for Shopify Markets and B2B catalogs:
126
126
 
127
- - **`country`** — ISO-3166 alpha-2 (e.g. `"US"`). Passed as Storefront `@inContext(country:)`. Drives Markets pricing and currency.
127
+ - **`country`** — ISO-3166 alpha-2 (e.g. `"US"`). Passed as Storefront `@inContext(country:)`. Drives Markets pricing and currency, and matches the bundle's projected country codes when it is restricted to specific markets.
128
128
  - **`language`** — ISO-639-1 (e.g. `"EN"`). Passed as `@inContext(language:)`.
129
- - **`buyer`** — B2B buyer identity. `BuyerInput` object or `() => Promise<BuyerInput>` callback.
130
- - **`marketId`** — Current visitor's Market GID. When the bundle is `marketVisibility: "specific"`, it's hidden unless this market is in its allow-list.
129
+ - **`buyer`** — B2B buyer identity. `BuyerInput` object or `() => Promise<BuyerInput>` callback. Its `companyLocationId` also matches the bundle's projected company locations when it is restricted to a B2B market.
130
+ - **`marketId`** — Current visitor's Market GID. Legacy market-gate signal; prefer `country` + `buyer` Shopify has deprecated market IDs for buyer targeting, and a B2B buyer's company-location market has no storefront market context.
131
+
132
+ A market-restricted bundle is shown when **any** signal matches: buyer country vs the bundle's projected `countryCodes`, buyer company location vs its projected `companyLocationIds`, or the legacy market-id vs `marketIds`. With no matching signal the fetcher returns `bundle: null` plus a `BundleVisibilityWarning` describing both sides of the mismatch.
133
+
134
+ **`currencyRate`** — store→presentment currency rate, accepted by the same components and by `fetchBundleData` / `useBundleData` / `useBundlesForProduct`. Merchant-configured amounts (fixed-amount discounts, flat prices, volume tier amounts) are stored in the shop's store currency, while `@inContext(country:)` prices arrive in the buyer's presentment currency; the checkout-side discount function converts configured amounts by `presentmentCurrencyRate`, so pass the same rate here (Hydrogen: the cart's `presentmentCurrencyRate`; Liquid-adjacent pages: `window.Shopify.currency.rate`) or the quoted sale price drifts from what checkout charges. Defaults to `1` (no conversion); invalid or non-positive values fall back to `1`. Percentages are unaffected.
131
135
 
132
136
  ```tsx
133
137
  <FixedBundle
package/dist/index.cjs CHANGED
@@ -93,6 +93,7 @@ async function fetchBundleData(options) {
93
93
  return result.bundle;
94
94
  }
95
95
  async function fetchBundleDataWithWarnings(options) {
96
+ const buyer = typeof options.buyer === "function" ? await options.buyer() : options.buyer;
96
97
  const config = {
97
98
  shopDomain: options.shopDomain,
98
99
  accessToken: options.storefrontAccessToken,
@@ -100,7 +101,7 @@ async function fetchBundleDataWithWarnings(options) {
100
101
  apiVersion: options.apiVersion,
101
102
  country: options.country,
102
103
  language: options.language,
103
- buyer: options.buyer
104
+ buyer
104
105
  };
105
106
  const client = (0, import_core.createStorefrontClient)(config);
106
107
  const query = (0, import_core.hasInContext)(config) ? (0, import_core.withInContext)(import_core.BUNDLE_METAOBJECT_QUERY) : import_core.BUNDLE_METAOBJECT_QUERY;
@@ -115,11 +116,16 @@ async function fetchBundleDataWithWarnings(options) {
115
116
  "not_found"
116
117
  );
117
118
  }
118
- const bundle = (0, import_core.parseMetaobjectBundleStrict)(
119
- data.metaobject.id,
120
- data.metaobject.fields
119
+ const bundle = (0, import_core.convertBundleToPresentment)(
120
+ (0, import_core.parseMetaobjectBundleStrict)(data.metaobject.id, data.metaobject.fields),
121
+ options.currencyRate
121
122
  );
122
- if (!(0, import_core.isVisibleInMarket)(bundle, options.marketId)) {
123
+ const buyerContext = {
124
+ marketId: options.marketId,
125
+ countryCode: options.country,
126
+ companyLocationId: buyer?.companyLocationId
127
+ };
128
+ if (!(0, import_core.isVisibleToBuyer)(bundle, buyerContext)) {
123
129
  return {
124
130
  bundle: null,
125
131
  warnings: [
@@ -127,7 +133,11 @@ async function fetchBundleDataWithWarnings(options) {
127
133
  bundleGid: options.bundleGid,
128
134
  reason: "market_mismatch",
129
135
  currentMarketId: options.marketId ?? null,
130
- allowedMarketIds: bundle.marketIds
136
+ currentCountryCode: options.country ?? null,
137
+ currentCompanyLocationId: buyer?.companyLocationId ?? null,
138
+ allowedMarketIds: bundle.marketIds,
139
+ allowedCountryCodes: bundle.countryCodes,
140
+ allowedCompanyLocationIds: bundle.companyLocationIds
131
141
  }
132
142
  ]
133
143
  };
@@ -194,7 +204,8 @@ function useBundleData(options) {
194
204
  country: options.country,
195
205
  language: options.language,
196
206
  buyer: options.buyer,
197
- marketId: options.marketId
207
+ marketId: options.marketId,
208
+ currencyRate: options.currencyRate
198
209
  }).then((bundle) => {
199
210
  if (controller.signal.aborted) return;
200
211
  setState({ status: "success", bundle, error: null });
@@ -225,7 +236,8 @@ function useBundleData(options) {
225
236
  options.country,
226
237
  options.language,
227
238
  options.buyer,
228
- options.marketId
239
+ options.marketId,
240
+ options.currencyRate
229
241
  ]);
230
242
  return state;
231
243
  }
@@ -749,12 +761,22 @@ function FixedBundle(props) {
749
761
  analyticsEnabled,
750
762
  onAddToCart,
751
763
  onError,
752
- className
764
+ className,
765
+ country,
766
+ language,
767
+ buyer,
768
+ marketId,
769
+ currencyRate
753
770
  } = props;
754
771
  const result = useBundleData({
755
772
  shopDomain,
756
773
  storefrontAccessToken,
757
- bundleGid
774
+ bundleGid,
775
+ country,
776
+ language,
777
+ buyer,
778
+ marketId,
779
+ currencyRate
758
780
  });
759
781
  const { elementRef, trackAddToCart } = useAnalytics({
760
782
  shopDomain,
@@ -1810,12 +1832,22 @@ function MixMatchBundle(props) {
1810
1832
  onAddToCart,
1811
1833
  onError,
1812
1834
  className,
1813
- productHandle
1835
+ productHandle,
1836
+ country,
1837
+ language,
1838
+ buyer,
1839
+ marketId,
1840
+ currencyRate
1814
1841
  } = props;
1815
1842
  const result = useBundleData({
1816
1843
  shopDomain,
1817
1844
  storefrontAccessToken,
1818
- bundleGid
1845
+ bundleGid,
1846
+ country,
1847
+ language,
1848
+ buyer,
1849
+ marketId,
1850
+ currencyRate
1819
1851
  });
1820
1852
  const { elementRef, trackAddToCart } = useAnalytics({
1821
1853
  shopDomain,
@@ -2341,12 +2373,22 @@ function VolumeBundle(props) {
2341
2373
  onAddToCart,
2342
2374
  onError,
2343
2375
  className,
2344
- productId
2376
+ productId,
2377
+ country,
2378
+ language,
2379
+ buyer,
2380
+ marketId,
2381
+ currencyRate
2345
2382
  } = props;
2346
2383
  const result = useBundleData({
2347
2384
  shopDomain,
2348
2385
  storefrontAccessToken,
2349
- bundleGid
2386
+ bundleGid,
2387
+ country,
2388
+ language,
2389
+ buyer,
2390
+ marketId,
2391
+ currencyRate
2350
2392
  });
2351
2393
  const { elementRef, trackAddToCart } = useAnalytics({
2352
2394
  shopDomain,
@@ -2722,12 +2764,22 @@ function BogoBundle(props) {
2722
2764
  analyticsEnabled,
2723
2765
  onAddToCart,
2724
2766
  onError,
2725
- className
2767
+ className,
2768
+ country,
2769
+ language,
2770
+ buyer,
2771
+ marketId,
2772
+ currencyRate
2726
2773
  } = props;
2727
2774
  const result = useBundleData({
2728
2775
  shopDomain,
2729
2776
  storefrontAccessToken,
2730
- bundleGid
2777
+ bundleGid,
2778
+ country,
2779
+ language,
2780
+ buyer,
2781
+ marketId,
2782
+ currencyRate
2731
2783
  });
2732
2784
  const { elementRef, trackAddToCart } = useAnalytics({
2733
2785
  shopDomain,
@@ -3778,12 +3830,22 @@ function MultiStepBundle(props) {
3778
3830
  analyticsEnabled,
3779
3831
  onAddToCart,
3780
3832
  onError,
3781
- className
3833
+ className,
3834
+ country,
3835
+ language,
3836
+ buyer,
3837
+ marketId,
3838
+ currencyRate
3782
3839
  } = props;
3783
3840
  const result = useBundleData({
3784
3841
  shopDomain,
3785
3842
  storefrontAccessToken,
3786
- bundleGid
3843
+ bundleGid,
3844
+ country,
3845
+ language,
3846
+ buyer,
3847
+ marketId,
3848
+ currencyRate
3787
3849
  });
3788
3850
  const { elementRef, trackAddToCart } = useAnalytics({
3789
3851
  shopDomain,
@@ -4389,7 +4451,8 @@ function useBundlesForProduct(options) {
4389
4451
  storefrontAccessToken: options.storefrontAccessToken,
4390
4452
  productHandle: options.productHandle,
4391
4453
  buyerIp: options.buyerIp,
4392
- signal: controller.signal
4454
+ signal: controller.signal,
4455
+ currencyRate: options.currencyRate
4393
4456
  }).then((bundles) => {
4394
4457
  if (controller.signal.aborted) return;
4395
4458
  setState({ status: "success", bundles, error: null });
@@ -4417,7 +4480,8 @@ function useBundlesForProduct(options) {
4417
4480
  options.shopDomain,
4418
4481
  options.storefrontAccessToken,
4419
4482
  options.productHandle,
4420
- options.buyerIp
4483
+ options.buyerIp,
4484
+ options.currencyRate
4421
4485
  ]);
4422
4486
  return state;
4423
4487
  }