Skip to content

Commit

Permalink
Merge branch 'develop' into tommy/checkout-signin
Browse files Browse the repository at this point in the history
  • Loading branch information
dpatil-magento authored Dec 15, 2020
2 parents a46f86d + c90caee commit b3941dd
Show file tree
Hide file tree
Showing 62 changed files with 4,280 additions and 1,172 deletions.
9 changes: 1 addition & 8 deletions packages/peregrine/lib/store/reducers/catalog.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ const initialState = {
categories: {},
currentPage: 1,
pageSize: 6,
prevPageTotal: null,
rootCategoryId: 2
prevPageTotal: null
};

const reducerMap = {
Expand Down Expand Up @@ -71,12 +70,6 @@ const reducerMap = {
}
};
},
[actions.setRootCategory]: (state, { payload }) => {
return {
...state,
rootCategoryId: payload
};
},
[actions.setCurrentPage.receive]: (state, { payload, error }) => {
if (error) {
return state;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ jest.mock('@apollo/client', () => {
useQuery: jest.fn(() => ({
data: null,
loading: false
}))
})),
useMutation: jest.fn(() => [
jest.fn(),
{
error: false,
loading: false
}
])
};
});

Expand Down Expand Up @@ -65,7 +72,15 @@ test('it returns the proper shape', () => {
const expectedKeys = [
'countryDisplayNameMap',
'customerAddresses',
'formErrors',
'formProps',
'handleAddAddress',
'handleCancelDialog',
'handleConfirmDialog',
'handleEditAddress',
'isDialogBusy',
'isDialogEditMode',
'isDialogOpen',
'isLoading'
];
expect(actualKeys.sort()).toEqual(expectedKeys.sort());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { gql } from '@apollo/client';

export const CustomerAddressBookAddressFragment = gql`
fragment CustomerAddressBookAddressFragment on CustomerAddress {
__typename
id
city
country_code
default_billing
default_shipping
firstname
lastname
middlename
postcode
region {
region
region_code
region_id
}
street
telephone
}
`;
Original file line number Diff line number Diff line change
@@ -1,35 +1,58 @@
import { gql } from '@apollo/client';

import { CustomerAddressBookAddressFragment } from './addressBookFragments.gql';

export const GET_CUSTOMER_ADDRESSES = gql`
query GetCustomerAddressesForAddressBook {
customer {
id
addresses {
id
city
country_code
default_billing
default_shipping
firstname
lastname
postcode
region {
region
region_code
region_id
}
street
telephone
...CustomerAddressBookAddressFragment
}
}
countries {
id
full_name_locale
}
}
${CustomerAddressBookAddressFragment}
`;

/**
* We use the connection key directive here because Apollo will save
* this customer's PII in localStorage if not.
*/
export const ADD_NEW_CUSTOMER_ADDRESS = gql`
mutation AddNewCustomerAddressToAddressBook(
$address: CustomerAddressInput!
) {
createCustomerAddress(input: $address)
@connection(key: "createCustomerAddress") {
# We don't manually write to the cache to update the collection
# after adding a new address so there's no need to query for a bunch
# of address fields here. We use refetchQueries to refresh the list.
id
}
}
`;

export const UPDATE_CUSTOMER_ADDRESS = gql`
mutation UpdateCustomerAddressInAddressBook(
$addressId: Int!
$updated_address: CustomerAddressInput!
) {
updateCustomerAddress(id: $addressId, input: $updated_address)
@connection(key: "updateCustomerAddress") {
id
...CustomerAddressBookAddressFragment
}
}
${CustomerAddressBookAddressFragment}
`;

export default {
getCustomerAddressesQuery: GET_CUSTOMER_ADDRESSES
createCustomerAddressMutation: ADD_NEW_CUSTOMER_ADDRESS,
getCustomerAddressesQuery: GET_CUSTOMER_ADDRESSES,
updateCustomerAddressMutation: UPDATE_CUSTOMER_ADDRESS
};
174 changes: 158 additions & 16 deletions packages/peregrine/lib/talons/AddressBookPage/useAddressBookPage.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useMemo } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useHistory } from 'react-router-dom';
import { useQuery } from '@apollo/client';
import { useMutation, useQuery } from '@apollo/client';

import { useAppContext } from '@magento/peregrine/lib/context/app';
import { useUserContext } from '@magento/peregrine/lib/context/user';
Expand All @@ -11,37 +11,71 @@ import defaultOperations from './addressBookPage.gql';
/**
* A talon to support the functionality of the Address Book page.
*
* @function
*
* @param {Object} props
* @param {Object} props.operations - GraphQL operations to be run by the talon.
*
* @returns {AddressBookPageTalonProps}
*
* @returns {Object} talonProps
* @returns {Object} talonProps.data - The user's address book data.
* @returns {Boolean} talonProps.isLoading - Indicates whether the user's
* address book data is loading.
* @example <caption>Importing into your project</caption>
* import { useAddressBookPage } from '@magento/peregrine/lib/talons/AddressBookPage/useAddressBookPage';
*/
export const useAddressBookPage = (props = {}) => {
const operations = mergeOperations(defaultOperations, props.operations);
const { getCustomerAddressesQuery } = operations;
const {
createCustomerAddressMutation,
getCustomerAddressesQuery,
updateCustomerAddressMutation
} = operations;

const [
,
{
actions: { setPageLoading }
}
] = useAppContext();
const history = useHistory();
const [{ isSignedIn }] = useUserContext();

const history = useHistory();

const { data: customerAddressesData, loading } = useQuery(
getCustomerAddressesQuery,
{
fetchPolicy: 'cache-and-network',
skip: !isSignedIn
}
);

const isRefetching = !!customerAddressesData && loading;
const isLoadingWithoutData = !customerAddressesData && loading;
const customerAddresses =
(customerAddressesData &&
customerAddressesData.customer &&
customerAddressesData.customer.addresses) ||
[];

const [
createCustomerAddress,
{
error: createCustomerAddressError,
loading: isCreatingCustomerAddress
}
] = useMutation(createCustomerAddressMutation);
const [
updateCustomerAddress,
{
error: updateCustomerAddressError,
loading: isUpdatingCustomerAddress
}
] = useMutation(updateCustomerAddressMutation);

const [isDialogOpen, setIsDialogOpen] = useState(false);
const [isDialogEditMode, setIsDialogEditMode] = useState(false);
const [formAddress, setFormAddress] = useState({});

// Use local state to determine whether to display errors or not.
// Could be replaced by a "reset mutation" function from apollo client.
// https://github.com/apollographql/apollo-feature-requests/issues/170
const [displayError, setDisplayError] = useState(false);

// If the user is no longer signed in, redirect to the home page.
useEffect(() => {
Expand All @@ -56,14 +90,87 @@ export const useAddressBookPage = (props = {}) => {
}, [isRefetching, setPageLoading]);

const handleAddAddress = useCallback(() => {
alert('TODO!');
// Hide all previous errors when we open the dialog.
setDisplayError(false);

setIsDialogEditMode(false);
setFormAddress({ country_code: 'US' });
setIsDialogOpen(true);
}, []);

const customerAddresses =
(customerAddressesData &&
customerAddressesData.customer &&
customerAddressesData.customer.addresses) ||
[];
const handleEditAddress = useCallback(address => {
// Hide all previous errors when we open the dialog.
setDisplayError(false);

setIsDialogEditMode(true);
setFormAddress(address);
setIsDialogOpen(true);
}, []);

const handleCancelDialog = useCallback(() => {
setIsDialogOpen(false);
}, []);

const handleConfirmDialog = useCallback(
async formValues => {
if (isDialogEditMode) {
try {
await updateCustomerAddress({
variables: {
addressId: formAddress.id,
updated_address: formValues
},
refetchQueries: [{ query: getCustomerAddressesQuery }],
awaitRefetchQueries: true
});

setIsDialogOpen(false);
} catch {
// Make sure any errors from the mutations are displayed.
setDisplayError(true);

// we have an onError link that logs errors, and FormError
// already renders this error, so just return to avoid
// triggering the success callback
return;
}
} else {
try {
await createCustomerAddress({
variables: { address: formValues },
refetchQueries: [{ query: getCustomerAddressesQuery }],
awaitRefetchQueries: true
});

setIsDialogOpen(false);
} catch {
// Make sure any errors from the mutations are displayed.
setDisplayError(true);

// we have an onError link that logs errors, and FormError
// already renders this error, so just return to avoid
// triggering the success callback
return;
}
}
},
[
createCustomerAddress,
formAddress,
getCustomerAddressesQuery,
isDialogEditMode,
updateCustomerAddress
]
);

const formErrors = useMemo(() => {
if (displayError) {
return new Map([
['createCustomerAddressMutation', createCustomerAddressError],
['updateCustomerAddressMutation', updateCustomerAddressError]
]);
} else return new Map();
}, [createCustomerAddressError, displayError, updateCustomerAddressError]);

// use data from backend until Intl.DisplayNames is widely supported
const countryDisplayNameMap = useMemo(() => {
Expand All @@ -79,10 +186,45 @@ export const useAddressBookPage = (props = {}) => {
return countryMap;
}, [customerAddressesData]);

const isDialogBusy = isCreatingCustomerAddress || isUpdatingCustomerAddress;
const isLoadingWithoutData = !customerAddressesData && loading;

const formProps = {
initialValues: formAddress
};

return {
countryDisplayNameMap,
customerAddresses,
formErrors,
formProps,
handleAddAddress,
handleCancelDialog,
handleConfirmDialog,
handleEditAddress,
isDialogBusy,
isDialogEditMode,
isDialogOpen,
isLoading: isLoadingWithoutData
};
};

/**
* Object type returned by the {@link useAddressBookPage} talon.
* It provides props data to use when rendering the address book page component.
*
* @typedef {Object} AddressBookPageTalonProps
*
* @property {Map} countryDisplayNameMap - A Map of country id to its localized display name.
* @property {Array<Object>} customerAddresses - A list of customer addresses.
* @property {Map} formErrors - A Map of form errors.
* @property {Object} formProps - Properties to pass to the add/edit form.
* @property {Function} handleAddAdddress - Function to invoke when adding a new address.
* @property {Function} handleCancelDialog - Function to invoke when cancelling the add/edit dialog.
* @property {Function} handleConfirmDialog - Function to invoke when submitting the add/edit dialog.
* @property {Function} handleEditAddress - Function to invoke when editing an existing address.
* @property {Boolean} isDialogBusy - Whether actions inside the dialog should be disabled.
* @property {Boolean} isDialogEditMode - Whether the dialog is in edit mode (true) or add new mode (false).
* @property {Boolean} isDialogOpen - Whether the dialog should be open.
* @property {Boolean} isLoading - Whether the page is loading.
*/
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

exports[`returns correct shape 1`] = `
Object {
"handleClose": [MockFunction closeDrawer],
"isOpen": false,
"setVariantPrice": [Function],
"variantPrice": null,
}
Expand Down
Loading

0 comments on commit b3941dd

Please sign in to comment.