-
Notifications
You must be signed in to change notification settings - Fork 683
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[PWA-245] Shipping Information (Authenticated) (#2380)
* Automatically set default shipping address if available * Automatically set default shipping address if available * Handle use case for customer entering address for first time * Stub out functionality that will toggle between two views on checkout page * - Complete UI for Address Book - Wire up mutation for address book selection * Complete changes to address edit to support editing customer address * Complete the rest of the new address flow and cleanup * Run linter * Big decomp of customer logic into dedicated components * - Select the current address on inital load of address book - Animate the address card when new address is selected * Address feedback from UX * Complete rest of PR feedback in prep for UX review * Fix the GraphQL validation errors * - Remove Customer data from cache during login/create actions - Address some more UX feedback * Different approach to list position and disabled checkbox * [PWA-562] Checkout (auth): Stored Address Follow Up (#2403) * - Refactor Region component to support other value keys - Change Customer form to use region id instead of code * Remove fragment from update response so we dont get flash of null data * Add some comments clarifying temporary work arounds * Address PR feedback and start working on getting existing tests to pass. * Switched modifier class for checkout page * Cast our regionId to string so isRequired validation works * Prettier and fix existing failing test * Cover ui components with tests * - Re-factor useShippingInformation to use skipped getQuery calls - Cover talon in tests * Cover the rest of the newly added talons with tests. * Address QA/PR feedback * Update tests and snaps with new click handler * Remove box-shadow transition from add new address card also * Update snaps * Refactor to use skip instead of lazy query to fix bug with effect order on signin. Co-authored-by: Devagouda <[email protected]>
- Loading branch information
1 parent
841e669
commit 74ed0ec
Showing
67 changed files
with
4,717 additions
and
229 deletions.
There are no files selected for viewing
14 changes: 14 additions & 0 deletions
14
packages/peregrine/lib/Apollo/clearCustomerDataFromCache.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { deleteCacheEntry } from './deleteCacheEntry'; | ||
|
||
/** | ||
* Deletes all references to Customer from the apollo cache including entries | ||
* that start with "$" which were automatically created by Apollo InMemoryCache. | ||
* By coincidence this rule additionally clears CustomerAddress entries, but | ||
* we'll need to keep this in mind by adding additional patterns as MyAccount | ||
* features are completed. | ||
* | ||
* @param {ApolloClient} client | ||
*/ | ||
export const clearCustomerDataFromCache = async client => { | ||
await deleteCacheEntry(client, key => key.match(/^\$?Customer/)); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
49 changes: 49 additions & 0 deletions
49
...e/lib/talons/CheckoutPage/AddressBook/__tests__/__snapshots__/useAddressBook.spec.js.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`callbacks update and return state handleApplyAddress 1`] = ` | ||
Object { | ||
"variables": Object { | ||
"addressId": 2, | ||
"cartId": "cart123", | ||
}, | ||
} | ||
`; | ||
|
||
exports[`returns the correct shape 1`] = ` | ||
Object { | ||
"activeAddress": undefined, | ||
"customerAddresses": Array [ | ||
Object { | ||
"firstname": "Philip", | ||
"id": 1, | ||
"lastname": "Fry", | ||
"street": Array [ | ||
"3000 57th Street", | ||
], | ||
}, | ||
Object { | ||
"firstname": "Bender", | ||
"id": 2, | ||
"lastname": "Rodríguez", | ||
"street": Array [ | ||
"3000 57th Street", | ||
], | ||
}, | ||
Object { | ||
"firstname": "John", | ||
"id": 3, | ||
"lastname": "Zoidberg", | ||
"street": Array [ | ||
"1 Dumpster Alley", | ||
], | ||
}, | ||
], | ||
"handleAddAddress": [Function], | ||
"handleApplyAddress": [Function], | ||
"handleCancel": [Function], | ||
"handleEditAddress": [Function], | ||
"handleSelectAddress": [Function], | ||
"isLoading": true, | ||
"selectedAddress": 2, | ||
} | ||
`; |
24 changes: 24 additions & 0 deletions
24
...e/lib/talons/CheckoutPage/AddressBook/__tests__/__snapshots__/useAddressCard.spec.js.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`event handlers fire callbacks handleEditAddress 1`] = ` | ||
Object { | ||
"country": Object { | ||
"code": "US", | ||
}, | ||
"email": "[email protected]", | ||
"firstname": "Philip", | ||
"id": 66, | ||
"region": Object { | ||
"id": 22, | ||
}, | ||
} | ||
`; | ||
|
||
exports[`returns correct shape 1`] = ` | ||
Object { | ||
"handleClick": [Function], | ||
"handleEditAddress": [Function], | ||
"handleKeyPress": [Function], | ||
"hasUpdate": false, | ||
} | ||
`; |
198 changes: 198 additions & 0 deletions
198
packages/peregrine/lib/talons/CheckoutPage/AddressBook/__tests__/useAddressBook.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
import React from 'react'; | ||
import { act } from 'react-test-renderer'; | ||
|
||
import { useAddressBook } from '../useAddressBook'; | ||
import createTestInstance from '../../../../util/createTestInstance'; | ||
import { useAppContext } from '../../../../context/app'; | ||
|
||
const mockGetCustomerAddresses = jest.fn().mockReturnValue({ | ||
data: { | ||
customer: { | ||
addresses: [ | ||
{ | ||
firstname: 'Philip', | ||
id: 1, | ||
lastname: 'Fry', | ||
street: ['3000 57th Street'] | ||
}, | ||
{ | ||
firstname: 'Bender', | ||
id: 2, | ||
lastname: 'Rodríguez', | ||
street: ['3000 57th Street'] | ||
}, | ||
{ | ||
firstname: 'John', | ||
id: 3, | ||
lastname: 'Zoidberg', | ||
street: ['1 Dumpster Alley'] | ||
} | ||
] | ||
} | ||
}, | ||
error: false, | ||
loading: false | ||
}); | ||
|
||
const mockGetCustomerCartAddress = jest.fn().mockReturnValue({ | ||
data: { | ||
customerCart: { | ||
shipping_addresses: [ | ||
{ | ||
firstname: 'Bender', | ||
lastname: 'Rodríguez', | ||
street: ['3000 57th Street'] | ||
} | ||
] | ||
} | ||
}, | ||
error: false, | ||
loading: false | ||
}); | ||
|
||
const mockSetCustomerAddressOnCart = jest.fn(); | ||
|
||
jest.mock('@apollo/react-hooks', () => ({ | ||
useQuery: jest.fn().mockImplementation(query => { | ||
if (query === 'getCustomerAddressesQuery') | ||
return mockGetCustomerAddresses(); | ||
|
||
if (query === 'getCustomerCartAddressQuery') | ||
return mockGetCustomerCartAddress(); | ||
|
||
return; | ||
}), | ||
useMutation: jest.fn(() => [ | ||
mockSetCustomerAddressOnCart, | ||
{ loading: true } | ||
]) | ||
})); | ||
|
||
jest.mock('../../../../context/app', () => { | ||
const state = {}; | ||
const api = { | ||
toggleDrawer: jest.fn() | ||
}; | ||
const useAppContext = jest.fn(() => [state, api]); | ||
|
||
return { useAppContext }; | ||
}); | ||
|
||
jest.mock('../../../../context/cart', () => { | ||
const state = { | ||
cartId: 'cart123' | ||
}; | ||
const api = {}; | ||
const useCartContext = jest.fn(() => [state, api]); | ||
|
||
return { useCartContext }; | ||
}); | ||
|
||
const Component = props => { | ||
const talonProps = useAddressBook(props); | ||
return <i talonProps={talonProps} />; | ||
}; | ||
|
||
const toggleActiveContent = jest.fn(); | ||
const mockProps = { | ||
mutations: {}, | ||
queries: { | ||
getCustomerAddressesQuery: 'getCustomerAddressesQuery', | ||
getCustomerCartAddressQuery: 'getCustomerCartAddressQuery' | ||
}, | ||
toggleActiveContent | ||
}; | ||
|
||
test('returns the correct shape', () => { | ||
const tree = createTestInstance(<Component {...mockProps} />); | ||
const { root } = tree; | ||
const { talonProps } = root.findByType('i').props; | ||
|
||
expect(talonProps).toMatchSnapshot(); | ||
}); | ||
|
||
test('auto selects new address', () => { | ||
mockGetCustomerAddresses.mockReturnValueOnce({ | ||
data: { | ||
customer: { | ||
addresses: [ | ||
{ | ||
firstname: 'Flexo', | ||
id: 44, | ||
lastname: 'Rodríguez', | ||
street: ['3000 57th Street'] | ||
} | ||
] | ||
} | ||
}, | ||
error: false, | ||
loading: false | ||
}); | ||
|
||
const tree = createTestInstance(<Component {...mockProps} />); | ||
|
||
act(() => { | ||
tree.update(<Component {...mockProps} />); | ||
}); | ||
|
||
const { root } = tree; | ||
const { talonProps } = root.findByType('i').props; | ||
expect(talonProps.selectedAddress).toBe(3); | ||
}); | ||
|
||
describe('callbacks update and return state', () => { | ||
const tree = createTestInstance(<Component {...mockProps} />); | ||
const { root } = tree; | ||
const { talonProps } = root.findByType('i').props; | ||
|
||
test('handleEditAddress', () => { | ||
const [, { toggleDrawer }] = useAppContext(); | ||
const { handleEditAddress } = talonProps; | ||
|
||
act(() => { | ||
handleEditAddress('activeAddress'); | ||
}); | ||
|
||
const { talonProps: newTalonProps } = root.findByType('i').props; | ||
|
||
expect(toggleDrawer).toHaveBeenCalled(); | ||
expect(newTalonProps.activeAddress).toBe('activeAddress'); | ||
}); | ||
|
||
test('handleAddAddress', () => { | ||
const [, { toggleDrawer }] = useAppContext(); | ||
const { handleAddAddress } = talonProps; | ||
|
||
act(() => { | ||
handleAddAddress(); | ||
}); | ||
|
||
const { talonProps: newTalonProps } = root.findByType('i').props; | ||
|
||
expect(toggleDrawer).toHaveBeenCalled(); | ||
expect(newTalonProps.activeAddress).toBeUndefined(); | ||
}); | ||
|
||
test('handleSelectAddress', () => { | ||
const { handleSelectAddress } = talonProps; | ||
|
||
act(() => { | ||
handleSelectAddress(318); | ||
}); | ||
|
||
const { talonProps: newTalonProps } = root.findByType('i').props; | ||
expect(newTalonProps.selectedAddress).toBe(318); | ||
}); | ||
|
||
test('handleApplyAddress', async () => { | ||
const { handleApplyAddress } = talonProps; | ||
|
||
await act(() => { | ||
handleApplyAddress(); | ||
}); | ||
|
||
expect(mockSetCustomerAddressOnCart).toHaveBeenCalled(); | ||
expect(mockSetCustomerAddressOnCart.mock.calls[0][0]).toMatchSnapshot(); | ||
expect(toggleActiveContent).toHaveBeenCalled(); | ||
}); | ||
}); |
86 changes: 86 additions & 0 deletions
86
packages/peregrine/lib/talons/CheckoutPage/AddressBook/__tests__/useAddressCard.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import React from 'react'; | ||
import { act } from 'react-test-renderer'; | ||
|
||
import createTestInstance from '../../../../util/createTestInstance'; | ||
import { useAddressCard } from '../useAddressCard'; | ||
|
||
const address = { | ||
country_code: 'US', | ||
email: '[email protected]', | ||
firstname: 'Philip', | ||
id: 66, | ||
region: { | ||
region_id: 22 | ||
} | ||
}; | ||
const onEdit = jest.fn(); | ||
const onSelection = jest.fn(); | ||
|
||
const mockProps = { | ||
address, | ||
onEdit, | ||
onSelection | ||
}; | ||
|
||
const Component = props => { | ||
const talonProps = useAddressCard(props); | ||
return <i talonProps={talonProps} />; | ||
}; | ||
|
||
test('returns correct shape', () => { | ||
const tree = createTestInstance(<Component {...mockProps} />); | ||
const { root } = tree; | ||
const { talonProps } = root.findByType('i').props; | ||
|
||
expect(talonProps).toMatchSnapshot(); | ||
}); | ||
|
||
test('returns correct value for update animation', () => { | ||
const tree = createTestInstance(<Component {...mockProps} />); | ||
const { root } = tree; | ||
const { talonProps } = root.findByType('i').props; | ||
|
||
expect(talonProps.hasUpdate).toBe(false); | ||
|
||
act(() => { | ||
tree.update( | ||
<Component | ||
{...mockProps} | ||
address={{ ...address, firstname: 'Bender' }} | ||
/> | ||
); | ||
}); | ||
|
||
const { talonProps: newTalonProps } = root.findByType('i').props; | ||
|
||
expect(newTalonProps.hasUpdate).toBe(true); | ||
}); | ||
|
||
describe('event handlers fire callbacks', () => { | ||
const tree = createTestInstance(<Component {...mockProps} />); | ||
const { root } = tree; | ||
const { talonProps } = root.findByType('i').props; | ||
|
||
test('handleClick', () => { | ||
const { handleClick } = talonProps; | ||
handleClick(); | ||
expect(onSelection).toHaveBeenCalledWith(66); | ||
}); | ||
|
||
test('handleKeyPress', () => { | ||
const { handleKeyPress } = talonProps; | ||
|
||
handleKeyPress({ key: 'Tab' }); | ||
expect(onSelection).not.toBeCalled(); | ||
|
||
handleKeyPress({ key: 'Enter' }); | ||
expect(onSelection).toHaveBeenCalledWith(66); | ||
}); | ||
|
||
test('handleEditAddress', () => { | ||
const { handleEditAddress } = talonProps; | ||
handleEditAddress(); | ||
expect(onEdit).toHaveBeenCalled(); | ||
expect(onEdit.mock.calls[0][0]).toMatchSnapshot(); | ||
}); | ||
}); |
Oops, something went wrong.