Skip to content

Commit

Permalink
Make <CartProvider> use country code from <ShopifyProvider> (#980)
Browse files Browse the repository at this point in the history
  • Loading branch information
blittle committed Jun 5, 2023
1 parent 42683d0 commit b9ab8eb
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/olive-adults-eat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/hydrogen-react': patch
---

Fix the `<CartProvider>` to by default pull localization from `<ShopifyProvider>`. You can still override the countryCode by passing a prop directly to `<CartProvider>`. Resovles https://github.com/Shopify/hydrogen/issues/622
85 changes: 82 additions & 3 deletions packages/hydrogen-react/src/CartProvider.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {vi, beforeEach, describe, expect, it} from 'vitest';
import {ComponentProps, PropsWithChildren} from 'react';
import {renderHook, act} from '@testing-library/react';
import {getCartMock, getCartLineMock} from './CartProvider.test.helpers.js';
import {ShopifyProvider} from './ShopifyProvider.js';
import {ShopifyProvider, type ShopifyProviderProps} from './ShopifyProvider.js';
import {getShopifyConfig} from './ShopifyProvider.test.js';

const mockUseCartActions = vi.fn();
Expand All @@ -23,13 +23,15 @@ vi.mock('./cart-hooks.js', () => ({
import {CartProvider, useCart} from './CartProvider.js';
import {cartFromGraphQL} from './useCartAPIStateMachine.js';
import {CART_ID_STORAGE_KEY} from './cart-constants.js';
import {CountryCode} from './storefront-api-types.js';

function ShopifyCartProvider(
props: Omit<ComponentProps<typeof CartProvider>, 'children'> = {},
shopifyProviderProps?: Omit<ShopifyProviderProps, 'children'>,
) {
return function Wrapper({children}: PropsWithChildren) {
return (
<ShopifyProvider {...getShopifyConfig()}>
<ShopifyProvider {...getShopifyConfig()} {...shopifyProviderProps}>
<CartProvider {...props}>{children}</CartProvider>
</ShopifyProvider>
);
Expand All @@ -49,6 +51,82 @@ describe('<CartProvider />', () => {
vi.spyOn(window.localStorage, 'getItem').mockReturnValue('');
});

describe('localization', () => {
it('uses the default countryCode provided to the shopify provider', async () => {
const discountCodesUpdateSpy = vi.fn(() => ({
data: {cartDiscountCodesUpdate: {cart: cartMock}},
}));

const buyerIdentityUpdateSpy = vi.fn(() => ({
data: {cartBuyerIdentityUpdate: {cart: cartMock}},
}));

const result = await useCartWithInitializedCart(
{
discountCodesUpdate: discountCodesUpdateSpy,
buyerIdentityUpdate: buyerIdentityUpdateSpy,
},
undefined,
{
...getShopifyConfig(),
countryIsoCode: 'KG' as CountryCode,
},
);

void act(() => {
result.current.discountCodesUpdate(['DiscountCode']);
});

expect(result.current.status).toEqual('updating');

// wait till idle
await act(async () => {});

expect(buyerIdentityUpdateSpy).toHaveBeenCalledWith('abc', {
countryCode: 'KG',
customerAccessToken: undefined,
});
});

it('uses countryCode override to the CartProvider component', async () => {
const discountCodesUpdateSpy = vi.fn(() => ({
data: {cartDiscountCodesUpdate: {cart: cartMock}},
}));

const buyerIdentityUpdateSpy = vi.fn(() => ({
data: {cartBuyerIdentityUpdate: {cart: cartMock}},
}));

const result = await useCartWithInitializedCart(
{
discountCodesUpdate: discountCodesUpdateSpy,
buyerIdentityUpdate: buyerIdentityUpdateSpy,
},
{
countryCode: 'UK' as CountryCode,
},
{
...getShopifyConfig(),
countryIsoCode: 'KG' as CountryCode,
},
);

void act(() => {
result.current.discountCodesUpdate(['DiscountCode']);
});

expect(result.current.status).toEqual('updating');

// wait till idle
await act(async () => {});

expect(buyerIdentityUpdateSpy).toHaveBeenCalledWith('abc', {
countryCode: 'UK',
customerAccessToken: undefined,
});
});
});

describe('`data` prop', () => {
it('uses the `data` prop if provided to initialize the cart. Taking precedence over localStorage', () => {
const cartFetchSpy = vi.fn(() => ({
Expand Down Expand Up @@ -1458,6 +1536,7 @@ describe('<CartProvider />', () => {
async function useCartWithInitializedCart(
cartActionsMocks = {},
cartProviderProps: Omit<ComponentProps<typeof CartProvider>, 'children'> = {},
shopifyProviderProps?: Omit<ShopifyProviderProps, 'children'>,
) {
const cartCreateSpy = vi.fn(() => ({
data: {cartCreate: {cart: cartMock}},
Expand All @@ -1470,7 +1549,7 @@ async function useCartWithInitializedCart(

// eslint-disable-next-line react-hooks/rules-of-hooks
const {result} = renderHook(() => useCart(), {
wrapper: ShopifyCartProvider(cartProviderProps),
wrapper: ShopifyCartProvider(cartProviderProps, shopifyProviderProps),
});

// creates a cart and wait till idle
Expand Down
16 changes: 15 additions & 1 deletion packages/hydrogen-react/src/CartProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {useCartAPIStateMachine} from './useCartAPIStateMachine.js';
import {CART_ID_STORAGE_KEY} from './cart-constants.js';
import {PartialDeep} from 'type-fest';
import {defaultCartFragment} from './cart-queries.js';
import {useShop} from './ShopifyProvider.js';

export const CartContext = createContext<CartWithActions | null>(null);

Expand Down Expand Up @@ -128,8 +129,21 @@ export function CartProvider({
data: cart,
cartFragment = defaultCartFragment,
customerAccessToken,
countryCode = 'US',
countryCode,
}: CartProviderProps): JSX.Element {
const shop = useShop();

if (!shop)
throw new Error(
'<CartProvider> needs to be a descendant of <ShopifyProvider>',
);

countryCode = (
(countryCode as string) ??
shop.countryIsoCode ??
'US'
).toUpperCase() as CountryCode;

if (countryCode) countryCode = countryCode.toUpperCase() as CountryCode;
const [prevCountryCode, setPrevCountryCode] = useState(countryCode);
const [prevCustomerAccessToken, setPrevCustomerAccessToken] =
Expand Down

0 comments on commit b9ab8eb

Please sign in to comment.