Skip to content

Feat: New components to show SKUs inside the bundle on the checkout summary. #456

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
"useNx": false,
"npmClient": "pnpm",
"version": "4.7.11",
"version": "4.8.0-beta.4",
"command": {
"version": {
"preid": "beta"
Expand Down
2 changes: 1 addition & 1 deletion packages/react-components/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@commercelayer/react-components",
"version": "4.7.11",
"version": "4.8.0-beta.4",
"description": "The Official Commerce Layer React Components",
"main": "lib/cjs/index.js",
"module": "lib/esm/index.js",
Expand Down
117 changes: 117 additions & 0 deletions packages/react-components/specs/addresses/invert-addresses.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import CommerceLayer from '#components/auth/CommerceLayer'
import getToken from '../utils/getToken'
import { render, screen } from '@testing-library/react'
import { type OrderContext } from '../utils/context'
import AddressesContainer from '#components/addresses/AddressesContainer'
import AddressInput from '#components/addresses/AddressInput'
import ShippingAddressForm from '#components/addresses/ShippingAddressForm'
import AddressCountrySelector from '#components/addresses/AddressCountrySelector'
import AddressStateSelector from '#components/addresses/AddressStateSelector'
import OrderContainer from '#components/orders/OrderContainer'
import OrderNumber from '#components/orders/OrderNumber'

describe('Billing info input', () => {
let token: string | undefined
let domain: string | undefined
beforeAll(async () => {
const { accessToken, endpoint } = await getToken()
if (accessToken !== undefined) {
token = accessToken
domain = endpoint
}
})
beforeEach<OrderContext>(async (ctx) => {
if (token != null && domain != null) {
ctx.accessToken = token
ctx.endpoint = domain
ctx.orderId = 'wxzYheVAAY'
}
})
it<OrderContext>('Use shipping address as billing address', async (ctx) => {
render(
<CommerceLayer accessToken={ctx.accessToken} endpoint={ctx.endpoint}>
<OrderContainer orderId={ctx.orderId}>
<OrderNumber />
<AddressesContainer invertAddresses>
<ShippingAddressForm>
<AddressInput
data-testid='first-name'
name='shipping_address_first_name'
/>
<AddressInput
data-testid='last-name'
name='shipping_address_last_name'
/>
<AddressInput
data-testid='line-1'
name='shipping_address_line_1'
/>
<AddressInput
data-testid='line-2'
name='shipping_address_line_2'
/>
<AddressInput data-testid='city' name='shipping_address_city' />
<AddressCountrySelector
data-testid='country-code'
name='shipping_address_country_code'
/>
<AddressStateSelector
data-testid='state-code'
name='shipping_address_state_code'
/>
<AddressInput
data-testid='zip-code'
name='shipping_address_zip_code'
/>
<AddressInput data-testid='phone' name='shipping_address_phone' />
<AddressInput
data-testid='billing-info'
name='shipping_address_billing_info'
/>
</ShippingAddressForm>
</AddressesContainer>
</OrderContainer>
</CommerceLayer>
)
await screen.findByText('2454728')
const firstName = screen.getByTestId('first-name')
const lastName = screen.getByTestId('last-name')
const line1 = screen.getByTestId('line-1')
const line2 = screen.getByTestId('line-2')
const city = screen.getByTestId('city')
const countryCode = screen.getByTestId('country-code')
const stateCode = screen.getByTestId('state-code')
const zipCode = screen.getByTestId('zip-code')
const phone = screen.getByTestId('phone')
const billingInfo = screen.getByTestId('billing-info')
expect(firstName).toBeDefined()
expect(lastName).toBeDefined()
expect(line1).toBeDefined()
expect(line2).toBeDefined()
expect(city).toBeDefined()
expect(countryCode).toBeDefined()
expect(stateCode).toBeDefined()
expect(zipCode).toBeDefined()
expect(phone).toBeDefined()
expect(billingInfo).toBeDefined()
})
// it<OrderContext>('Hide billing info if requires_billing_info is false and required is undefined', async (ctx) => {
// render(
// <CommerceLayer accessToken={ctx.accessToken}>
// <OrderContainer orderId={ctx.orderId}>
// <OrderNumber />
// <AddressesContainer>
// <BillingAddressForm>
// <AddressInput
// data-testid='billing-info'
// name='billing_address_billing_info'
// />
// </BillingAddressForm>
// </AddressesContainer>
// </OrderContainer>
// </CommerceLayer>
// )
// const billingInfo = screen.queryByTestId('billing-info')
// expect(billingInfo).toBeNull()
// })
})
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ interface Props {
* If true, the address will be considered a business address.
*/
isBusiness?: boolean
/**
* If true, the shipping address will be considered as primary address. Default is false.
*/
invertAddresses?: boolean
}

/**
Expand All @@ -50,7 +54,12 @@ interface Props {
* </span>
*/
export function AddressesContainer(props: Props): JSX.Element {
const { children, shipToDifferentAddress = false, isBusiness } = props
const {
children,
shipToDifferentAddress = false,
isBusiness,
invertAddresses = false
} = props
const [state, dispatch] = useReducer(addressReducer, addressInitialState)
const { order, orderId, updateOrder } = useContext(OrderContext)
const config = useContext(CommerceLayerContext)
Expand All @@ -59,7 +68,8 @@ export function AddressesContainer(props: Props): JSX.Element {
type: 'setShipToDifferentAddress',
payload: {
shipToDifferentAddress,
isBusiness
isBusiness,
invertAddresses
}
})
return () => {
Expand All @@ -68,7 +78,7 @@ export function AddressesContainer(props: Props): JSX.Element {
payload: {}
})
}
}, [shipToDifferentAddress, isBusiness])
}, [shipToDifferentAddress, isBusiness, invertAddresses])
const contextValue = {
...state,
setAddressErrors: (errors: BaseError[], resource: AddressResource) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ import Parent from '#components/utils/Parent'
import { type ChildrenFunction } from '#typings/index'
import AddressContext from '#context/AddressContext'
import {
shippingAddressController,
countryLockController,
billingAddressController
addressesController
} from '#utils/addressesManager'
import OrderContext from '#context/OrderContext'
import CustomerContext from '#context/CustomerContext'
Expand Down Expand Up @@ -46,7 +45,8 @@ export function SaveAddressesButton(props: Props): JSX.Element {
shipping_address: shippingAddress,
saveAddresses,
billingAddressId,
shippingAddressId
shippingAddressId,
invertAddresses
} = useContext(AddressContext)
const { order } = useContext(OrderContext)
const {
Expand All @@ -69,18 +69,27 @@ export function SaveAddressesButton(props: Props): JSX.Element {
)
customerEmail = Object.keys(isValidEmail).length > 0
}
const billingDisable = billingAddressController({

const shippingAddressCleaned: any = Object.keys(shippingAddress ?? {}).reduce(
(acc, key) => {
return {
...acc,
// @ts-expect-error type mismatch
[key.replace(`shipping_address_`, '')]: shippingAddress[key].value
}
},
{}
)

const { billingDisable, shippingDisable } = addressesController({
invertAddresses,
requiresBillingInfo: order?.requires_billing_info,
billing_address: billingAddress,
errors,
billingAddressId,
requiresBillingInfo: order?.requires_billing_info
})
const shippingDisable = shippingAddressController({
billingDisable,
errors,
shipping_address: shippingAddressCleaned,
shipToDifferentAddress,
shipping_address: shippingAddress,
shippingAddressId
shippingAddressId,
billingAddressId,
errors
})
const countryLockDisable = countryLockController({
countryCodeLock: order?.shipping_country_code_lock,
Expand Down
29 changes: 21 additions & 8 deletions packages/react-components/src/components/line_items/LineItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import LineItemChildrenContext, {
type InitialLineItemChildrenContext
} from '#context/LineItemChildrenContext'
import ShipmentChildrenContext from '#context/ShipmentChildrenContext'
import LineItemBundleChildrenContext, {
type InitialLineItemBundleChildrenContext
} from '#context/LineItemBundleChildrenContext'

export type TLineItem =
| 'gift_cards'
Expand All @@ -29,19 +32,29 @@ export function LineItem(props: Props): JSX.Element {
: lineItems
const components = items
?.filter((l) => l?.item_type === type)
.map((lineItem, k, check) => {
if (
lineItem?.item_type === 'bundles' &&
k > 0 &&
check[k - 1]?.bundle_code === lineItem.bundle_code
)
return null
.map((lineItem) => {
if (lineItem?.item_type === 'bundles') {
const skuListItems = lineItem?.bundle?.sku_list?.sku_list_items
const skuListItemsProps: InitialLineItemBundleChildrenContext = {
skuListItems,
lineItem
}
return (
<LineItemBundleChildrenContext.Provider
key={lineItem?.id}
value={skuListItemsProps}
>
{children}
</LineItemBundleChildrenContext.Provider>
)
}
if (
lineItem?.item_type === 'gift_cards' &&
lineItem?.total_amount_cents &&
lineItem?.total_amount_cents <= 0
)
) {
return null
}
const lineProps: InitialLineItemChildrenContext = {
lineItem
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import getAmount from '#utils/getAmount'
import LineItemChildrenContext from '#context/LineItemChildrenContext'
import Parent from '#components/utils/Parent'
import { type BaseAmountComponent, type BasePriceType } from '#typings/index'
import LineItemBundleChildrenContext from '#context/LineItemBundleChildrenContext'

type Props = BaseAmountComponent & {
type?: BasePriceType
Expand All @@ -11,21 +12,23 @@ type Props = BaseAmountComponent & {
export function LineItemAmount(props: Props): JSX.Element {
const { format = 'formatted', type = 'total', ...p } = props
const { lineItem } = useContext(LineItemChildrenContext)
const { lineItem: lineItemBundle } = useContext(LineItemBundleChildrenContext)
const [price, setPrice] = useState('')
const item = lineItem ?? lineItemBundle
useEffect(() => {
if (lineItem) {
if (item) {
const p = getAmount({
base: 'amount',
type,
format,
obj: lineItem
obj: item
})
setPrice(p)
}
return (): void => {
setPrice('')
}
}, [lineItem])
}, [item])
const parentProps = {
price,
...p
Expand Down
Loading