Skip to content
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

New checkout #639

Merged
merged 217 commits into from May 4, 2020
Merged

New checkout #639

merged 217 commits into from May 4, 2020

Conversation

orzechdev
Copy link
Member

@orzechdev orzechdev commented Mar 4, 2020

I want to merge this change because... it adds stateful checkout and cart to SDK with new cart and checkout UI.

New checkout and cart UI

New checkout and cart UI with completely refactored components which uses new hooks within them.
Zrzut ekranu 2020-05-4 o 02 24 24
Zrzut ekranu 2020-05-4 o 02 35 42
Zrzut ekranu 2020-05-4 o 02 28 01
Zrzut ekranu 2020-05-4 o 02 28 23
Zrzut ekranu 2020-05-4 o 02 29 34
Zrzut ekranu 2020-05-4 o 02 25 31
Zrzut ekranu 2020-05-4 o 02 30 12

New hooks for checkout and cart

  • added new useCheckout and useCart hooks, which has all data and methods associated with checkout and cart to use within React components:
const {
  checkout,
  billingAsShipping,
  selectedShippingAddressId,
  selectedBillingAddressId,
  availableShippingMethods,
  payment,
  availablePaymentGateways,
  promoCodeDiscount,
  loaded,
  load,
  setShippingAddress,
  setBillingAddress,
  setBillingAsShippingAddress,
  setShippingMethod,
  addPromoCode,
  removePromoCode,
  createPayment,
  completeCheckout,
} = useCheckout();

const {
  items,
  discount,
  shippingPrice,
  subtotalPrice,
  totalPrice,
  loaded,
  load,
  addItem,
  subtractItem,
  updateItem,
  removeItem
} = useCart();

SDK changes

  • as illustrated below, new hooks are prepared based on SaleorCheckoutAPI and SaleorCartAPI classes aggregated in SaleorAPI class. State for those API classes is derived from new SaleorState object, which is altered based on results form GraphQL queries/mutations, local storage changes and job executions. All data from SaleorState is observable, what means that SaleorCheckoutAPI and SaleorCartAPI classes observes appropriate data from there and automatically recalculate its own state based on notified changes. After that, SaleorAPI is notified about changes, which in turn notifies react context provider about them. Context provider then recreates context object with references to SaleorCheckoutAPI and SaleorCartAPI classes, which are used by new hooks. Effectively, React triggers components tree update and all instances of new hooks are up to date.
    mermaid-diagram-20200504010214
  • end developer might use API straight from SDK using SaleorManager, i.e.:
const manager = new SaleorManager(apolloClient, optionalCustomConfig);
manager.connect(saleorAPI => /* do something with saleor API */)
  • or end developer might utilize SaleorProvider which provides SaleorAPI in context, which is used among all new React hooks, i.e.:
<SaleorProvider client={apolloClient} config={optionalCustomConfig}>
    {/* use new React hooks anywhere in the components tree */}
</SaleorProvider>

Error handling

There are two types of functions returned by useCart and useCheckout hooks:

  • immediately executed functions (e.g. completeCheckout()), which return promise with:
{
  data, // altered data by function, do not need to be user, as same data in hooks are automatically updated
  dataError, // errors returned by backend
  functionError, // errors performed on frontend within SDK
  pending, // always false, indicates if function finished its job
}
  • queued functions to be executed as soon as possible, when internet connection exists (e.g. addItem()):
{
  pending, // indicates if function finished its job
}

For queued functions, errors might be catched, by adding error listeners:

const {
  addOnErrorListener,
  removeOnErrorListener,
} = useCheckout();

useEffect(() => {
  addOnErrorListener(onErrorListener);
  return () => {
    removeOnErrorListener(onErrorListener);
  };
}, []);

const onErrorListener = (
  error: ApolloErrorWithUserInput,
  type: ErrorTypes
) => {
  const errors = error.extraInfo.userInputErrors;
  if (type === ErrorCartTypes.SET_CART_ITEM && errors) {
    // do something with errors
  }
};

Fixes

@netlify
Copy link

@netlify netlify bot commented Mar 4, 2020

Deploy preview for saleor-storefront-stage processing.

Building with commit 62d083a

https://app.netlify.com/sites/saleor-storefront-stage/deploys/5eafd4d6bc5ff0000906fb12

@orzechdev orzechdev marked this pull request as ready for review Mar 4, 2020
@lgtm-com

This comment has been minimized.

@lgtm-com

This comment has been minimized.

@lgtm-com

This comment has been minimized.

src/@next/components/molecules/InputSelect/InputSelect.tsx Outdated Show resolved Hide resolved
src/@next/components/organisms/CheckoutPayment/fixtures.ts Outdated Show resolved Hide resolved
export const Wrapper = styled.div``;

export const Section = styled.section``;

Copy link
Member

@dominik-zeglen dominik-zeglen Apr 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...why?

Copy link
Member Author

@orzechdev orzechdev Apr 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll fix it, but in some cases I see that it may facilitate creating new components, where you might not know in advance which html tags should be styled and which should not, so the assumption that we do all styled tags eliminates the need to change tags in JSX and move them to styles.ts file on a regular basis. Eventually every styled tag is rendered as single html tag, so it does not change a lot.

return (
<S.Section>
<S.Title data-cy="checkoutPageSubtitle">SHIPPING METHOD</S.Title>
<Formik
Copy link
Member

@dominik-zeglen dominik-zeglen Apr 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that Formik has useFormik hook now, we should probably use it

Copy link
Member Author

@orzechdev orzechdev Apr 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it might be much better, but I guess we may switch to useFormik in another PR as many components are already prepared with render props way

export interface IShippingMethodPrice {
/**
* Currency code.
*/
currency: string;
/**
* Amount of money.
*/
amount: number;
}
Copy link
Member

@dominik-zeglen dominik-zeglen Apr 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like it could be replaced by some gql fragment type. Actually, a lot of props in this PR can be written using some fragments or types coming from queries.

Copy link
Member Author

@orzechdev orzechdev Apr 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about it, because if we want to rely on SDK in a separate package in the future, it might hide from us those interfaces generated by Apollo in that package.

@lgtm-com

This comment has been minimized.

@lgtm-com

This comment has been minimized.

@lgtm-com

This comment has been minimized.

@lgtm-com

This comment has been minimized.

@lgtm-com

This comment has been minimized.

@lgtm-com
Copy link

@lgtm-com lgtm-com bot commented Apr 30, 2020

This pull request fixes 5 alerts when merging 1cf7c8a into 3cfdeef - view on LGTM.com

fixed alerts:

  • 2 for Unused variable, import, function or class
  • 1 for Useless conditional
  • 1 for Assignment to constant
  • 1 for Unused or undefined state property

@lgtm-com

This comment has been minimized.

@lgtm-com

This comment has been minimized.

@lgtm-com
Copy link

@lgtm-com lgtm-com bot commented May 4, 2020

This pull request fixes 5 alerts when merging 62d083a into 3cfdeef - view on LGTM.com

fixed alerts:

  • 2 for Unused variable, import, function or class
  • 1 for Useless conditional
  • 1 for Assignment to constant
  • 1 for Unused or undefined state property

@orzechdev orzechdev merged commit af9ba5a into master May 4, 2020
8 checks passed
@orzechdev orzechdev deleted the refactor/cart-view branch May 4, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment