From 4d7ef8b7efba116166dd2dcf43249694db5fcbfc Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Mon, 13 Apr 2020 18:32:09 -0400 Subject: [PATCH 01/35] Move domain to review step pull purchase deats out of checkout wrapper --- .../components/wp-checkout-order-review.js | 12 ++ .../components/wp-checkout-order-summary.js | 29 +--- .../wpcom/components/wp-checkout.js | 156 +++++++++--------- .../components/wp-order-review-line-items.js | 15 +- 4 files changed, 99 insertions(+), 113 deletions(-) diff --git a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-review.js b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-review.js index ce1bb14424dd0..a6bb8ff1f2b1b 100644 --- a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-review.js +++ b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-review.js @@ -12,6 +12,7 @@ import { useLineItems, useFormStatus } from '@automattic/composite-checkout'; import joinClasses from './join-classes'; import Coupon from './coupon'; import { WPOrderReviewLineItems, WPOrderReviewSection } from './wp-order-review-line-items'; +import { isLineItemADomain } from '../hooks/has-domains'; export default function WPCheckoutOrderReview( { className, @@ -23,13 +24,19 @@ export default function WPCheckoutOrderReview( { variantSelectOverride, getItemVariants, onChangePlanLength, + siteUrl, } ) { const [ items, total ] = useLineItems(); const { formStatus } = useFormStatus(); const isPurchaseFree = total.amount.value === 0; + const firstDomainItem = items.find( isLineItemADomain ); + const domainUrl = firstDomainItem ? firstDomainItem.sublabel : siteUrl; + return (
+ { domainUrl && { domainUrl } } + { domainUrl && { domainUrl } }; +export default function WPCheckoutOrderSummary() { + return ( + + + + ); } -export function WPCheckoutOrderSummaryTitle() { +function WPCheckoutOrderSummaryTitle() { const translate = useTranslate(); const total = useTotal(); return ( - { translate( 'You are all set to check out' ) } + { translate( 'Purchase Details' ) } { renderDisplayValueMarkdown( total.amount.displayValue ) } @@ -41,8 +35,3 @@ const CheckoutSummaryTitle = styled.span` const CheckoutSummaryTotal = styled.span` font-weight: ${( props ) => props.theme.weights.bold}; `; - -const DomainURL = styled.div` - margin-top: -12px; - word-break: break-word; -`; diff --git a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout.js b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout.js index d336ad6d0f23d..5b6ce97959a06 100644 --- a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout.js +++ b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout.js @@ -6,7 +6,6 @@ import { useTranslate } from 'i18n-calypso'; import styled from '@emotion/styled'; import { Checkout, - CheckoutStepBody, CheckoutSteps, CheckoutStep, getDefaultPaymentMethodStep, @@ -25,7 +24,7 @@ import { import { areDomainsInLineItems, isLineItemADomain } from '../hooks/has-domains'; import useCouponFieldState from '../hooks/use-coupon-field-state'; import WPCheckoutOrderReview from './wp-checkout-order-review'; -import WPCheckoutOrderSummary, { WPCheckoutOrderSummaryTitle } from './wp-checkout-order-summary'; +import WPCheckoutOrderSummary from './wp-checkout-order-summary'; import WPContactForm from './wp-contact-form'; import { isCompleteAndValid } from '../types'; import { WPOrderReviewTotal, WPOrderReviewSection, LineItemUI } from './wp-order-review-line-items'; @@ -129,94 +128,89 @@ export default function WPCheckout( { }; return ( - - } - titleContent={ } - errorMessage={ translate( 'There was an error with the summary step.' ) } - isStepActive={ false } - isStepComplete={ true } - stepNumber={ 1 } - totalSteps={ 1 } - stepId={ 'order-summary' } - /> - - true } - activeStepContent={ - - } - titleContent={ } - completeStepContent={ } - editButtonText={ translate( 'Edit' ) } - editButtonAriaLabel={ translate( 'Edit the payment method' ) } - nextStepButtonText={ translate( 'Continue' ) } - nextStepButtonAriaLabel={ translate( 'Continue with the selected payment method' ) } - validatingButtonText={ translate( 'Please wait…' ) } - validatingButtonAriaLabel={ translate( 'Please wait…' ) } - /> - { shouldShowContactStep && ( + <> + + + { - setShouldShowContactDetailsValidationErrors( true ); - return contactValidationCallback(); - } } + stepId="review-order-step" + isCompleteCallback={ () => true } activeStepContent={ - } - completeStepContent={ } - titleContent={ } + titleContent={ } + completeStepContent={ } editButtonText={ translate( 'Edit' ) } - editButtonAriaLabel={ translate( 'Edit the contact details' ) } + editButtonAriaLabel={ translate( 'Edit the payment method' ) } nextStepButtonText={ translate( 'Continue' ) } - nextStepButtonAriaLabel={ translate( 'Continue with the entered contact details' ) } + nextStepButtonAriaLabel={ translate( 'Continue with the selected payment method' ) } validatingButtonText={ translate( 'Please wait…' ) } validatingButtonAriaLabel={ translate( 'Please wait…' ) } /> - ) } - { + setShouldShowContactDetailsValidationErrors( true ); + return contactValidationCallback(); + } } + activeStepContent={ + + } + completeStepContent={ + + } + titleContent={ } + editButtonText={ translate( 'Edit' ) } + editButtonAriaLabel={ translate( 'Edit the contact details' ) } + nextStepButtonText={ translate( 'Continue' ) } + nextStepButtonAriaLabel={ translate( 'Continue with the entered contact details' ) } + validatingButtonText={ translate( 'Please wait…' ) } + validatingButtonAriaLabel={ translate( 'Please wait…' ) } /> - } - completeStepContent={ paymentMethodStep.completeStepContent } - titleContent={ paymentMethodStep.titleContent } - editButtonText={ translate( 'Edit' ) } - editButtonAriaLabel={ translate( 'Edit the payment method' ) } - nextStepButtonText={ translate( 'Continue' ) } - nextStepButtonAriaLabel={ translate( 'Continue with the selected payment method' ) } - validatingButtonText={ translate( 'Please wait…' ) } - validatingButtonAriaLabel={ translate( 'Please wait…' ) } - /> - - + ) } + + } + completeStepContent={ paymentMethodStep.completeStepContent } + titleContent={ paymentMethodStep.titleContent } + editButtonText={ translate( 'Edit' ) } + editButtonAriaLabel={ translate( 'Edit the payment method' ) } + nextStepButtonText={ translate( 'Continue' ) } + nextStepButtonAriaLabel={ translate( 'Continue with the selected payment method' ) } + validatingButtonText={ translate( 'Please wait…' ) } + validatingButtonAriaLabel={ translate( 'Please wait…' ) } + /> + + + ); } @@ -228,7 +222,7 @@ function PaymentMethodStep( { CheckoutTerms, responseCart, subtotal } ) { const [ items, total ] = useLineItems(); const taxes = items.filter( ( item ) => item.type === 'tax' ); return ( - + <> { paymentMethodStep.activeStepContent } @@ -242,7 +236,7 @@ function PaymentMethodStep( { CheckoutTerms, responseCart, subtotal } ) { ) ) } - + ); } diff --git a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-order-review-line-items.js b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-order-review-line-items.js index 864520adb0c40..9ae588e2ab115 100644 --- a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-order-review-line-items.js +++ b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-order-review-line-items.js @@ -57,7 +57,7 @@ function WPLineItem( { { hasDeleteButton && formStatus === 'ready' && ( - + <> - + ) } { shouldShowVariantSelector && ( @@ -310,8 +310,7 @@ WPOrderReviewLineItems.propTypes = { }; const WPOrderReviewList = styled.ul` - margin: -10px 0 10px 0; - padding: 0; + margin: 10px 0; `; const WPOrderReviewListItems = styled.li` @@ -319,14 +318,6 @@ const WPOrderReviewListItems = styled.li` padding: 0; display: block; list-style: none; - - :first-of-type .checkout-line-item { - padding-top: 10px; - } - - :first-of-type button { - top: -3px; - } `; function returnModalCopy( product, translate, hasDomainsInCart ) { From 37b3788f8abda985d0617fee0eb3aae849f8e6a9 Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Mon, 13 Apr 2020 23:11:48 -0400 Subject: [PATCH 02/35] Add useTax --- packages/composite-checkout/src/lib/line-items.js | 5 +++++ packages/composite-checkout/src/public-api.js | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/composite-checkout/src/lib/line-items.js b/packages/composite-checkout/src/lib/line-items.js index f8885b8ec90c2..c84ecc614a036 100644 --- a/packages/composite-checkout/src/lib/line-items.js +++ b/packages/composite-checkout/src/lib/line-items.js @@ -25,3 +25,8 @@ export function useTotal() { const [ , total ] = useLineItems(); return total; } + +export function useTax() { + const [ items ] = useLineItems(); + return items.find( item => item.type === 'tax' ); +} diff --git a/packages/composite-checkout/src/public-api.js b/packages/composite-checkout/src/public-api.js index 3e91893e615b3..8459e8474aaae 100644 --- a/packages/composite-checkout/src/public-api.js +++ b/packages/composite-checkout/src/public-api.js @@ -19,7 +19,7 @@ import { import CheckoutModal from './components/checkout-modal'; import { renderDisplayValueMarkdown } from './lib/render'; import { usePaymentMethod, usePaymentMethodId, useAllPaymentMethods } from './lib/payment-methods'; -import { useLineItems, useTotal } from './lib/line-items'; +import { useLineItems, useTotal, useTax } from './lib/line-items'; import { createRegistry, defaultRegistry, @@ -89,5 +89,6 @@ export { useRegisterStore, useRegistry, useSelect, + useTax, useTotal, }; From 7baa996f339c55e139fe77ac91099a1be941259c Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Mon, 13 Apr 2020 23:12:31 -0400 Subject: [PATCH 03/35] Add tax and basic styling to order summary --- .../components/wp-checkout-order-summary.js | 53 ++++++++++++++++--- packages/composite-checkout/src/theme.js | 1 + 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js index e3234b9270173..eab3a5773c437 100644 --- a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js +++ b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js @@ -3,35 +3,74 @@ */ import React from 'react'; import styled from '@emotion/styled'; -import { useTotal, renderDisplayValueMarkdown } from '@automattic/composite-checkout'; +import { useTax, useTotal, renderDisplayValueMarkdown } from '@automattic/composite-checkout'; import { useTranslate } from 'i18n-calypso'; export default function WPCheckoutOrderSummary() { + const translate = useTranslate(); + const tax = useTax(); + const total = useTotal(); + return ( - + - + + { tax && ( + <> + { translate( 'Taxes' ) } + + { renderDisplayValueMarkdown( tax.amount.displayValue ) } + + + ) } + + { translate( 'Total' ) } + + { renderDisplayValueMarkdown( total.amount.displayValue ) } + + + + ); } function WPCheckoutOrderSummaryTitle() { const translate = useTranslate(); - const total = useTotal(); return ( { translate( 'Purchase Details' ) } - - { renderDisplayValueMarkdown( total.amount.displayValue ) } - ); } +const CheckoutSummaryWrapper = styled.div` + background: ${props => props.theme.colors.surface}; + border: 1px solid ${props => props.theme.colors.borderColorLight}; + + @media ( ${props => props.theme.breakpoints.desktopUp} ) { + float: right; + margin-left: 16px; + width: 326px; + } +`; + const CheckoutSummaryTitle = styled.span` display: flex; justify-content: space-between; `; +const CheckoutSummaryAmountWrapper = styled.div` + border-top: 1px solid ${props => props.theme.colors.borderColorLight}; + display: flex; + flex-wrap: wrap; + justify-content: space-between; + padding: 16px; +`; + +const CheckoutSummaryLabel = styled.span``; + +const CheckoutSummaryAmount = styled.span``; + const CheckoutSummaryTotal = styled.span` font-weight: ${( props ) => props.theme.weights.bold}; `; diff --git a/packages/composite-checkout/src/theme.js b/packages/composite-checkout/src/theme.js index 373be9edad1c7..220a54f4158fd 100644 --- a/packages/composite-checkout/src/theme.js +++ b/packages/composite-checkout/src/theme.js @@ -40,6 +40,7 @@ const theme = { placeHolderTextColor: swatches.gray30, }, breakpoints: { + desktopUp: 'min-width: 960px', tabletUp: 'min-width: 700px', smallPhoneUp: 'min-width: 400px', }, From 084834e77dadc17ae03555d6db59d9f85e9f54fc Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Mon, 13 Apr 2020 23:22:10 -0400 Subject: [PATCH 04/35] Adjust summary amount styles --- .../components/wp-checkout-order-summary.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js index eab3a5773c437..9bba20940f591 100644 --- a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js +++ b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js @@ -16,12 +16,12 @@ export default function WPCheckoutOrderSummary() { { tax && ( - <> + { translate( 'Taxes' ) } { renderDisplayValueMarkdown( tax.amount.displayValue ) } - + ) } { translate( 'Total' ) } @@ -61,9 +61,6 @@ const CheckoutSummaryTitle = styled.span` const CheckoutSummaryAmountWrapper = styled.div` border-top: 1px solid ${props => props.theme.colors.borderColorLight}; - display: flex; - flex-wrap: wrap; - justify-content: space-between; padding: 16px; `; @@ -71,6 +68,12 @@ const CheckoutSummaryLabel = styled.span``; const CheckoutSummaryAmount = styled.span``; -const CheckoutSummaryTotal = styled.span` - font-weight: ${( props ) => props.theme.weights.bold}; +const CheckoutSummaryLineItem = styled.div` + display: flex; + flex-wrap: wrap; + justify-content: space-between; +`; + +const CheckoutSummaryTotal = styled( CheckoutSummaryLineItem )` + font-weight: ${props => props.theme.weights.bold}; `; From a9141423845e324feffef8bade39d0652a8c1714 Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Wed, 15 Apr 2020 15:20:20 -0400 Subject: [PATCH 05/35] Remove WPCheckoutOrderSummaryTitle for now. --- .../components/wp-checkout-order-summary.js | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js index 9bba20940f591..949c972c28004 100644 --- a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js +++ b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js @@ -13,7 +13,9 @@ export default function WPCheckoutOrderSummary() { return ( - + + { translate( 'Purchase Details' ) } + { tax && ( @@ -34,15 +36,6 @@ export default function WPCheckoutOrderSummary() { ); } -function WPCheckoutOrderSummaryTitle() { - const translate = useTranslate(); - return ( - - { translate( 'Purchase Details' ) } - - ); -} - const CheckoutSummaryWrapper = styled.div` background: ${props => props.theme.colors.surface}; border: 1px solid ${props => props.theme.colors.borderColorLight}; @@ -54,9 +47,10 @@ const CheckoutSummaryWrapper = styled.div` } `; -const CheckoutSummaryTitle = styled.span` - display: flex; - justify-content: space-between; +const CheckoutSummaryTitle = styled.h2` + color: ${props => props.theme.colors.textColor}; + font-weight: ${props => props.theme.weights.bold}; + padding: 16px; `; const CheckoutSummaryAmountWrapper = styled.div` From 9cc0a13b94ca6a29f351644957c9953131deff6a Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Wed, 15 Apr 2020 15:30:30 -0400 Subject: [PATCH 06/35] Missing propType --- .../wpcom/components/wp-checkout-order-review.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-review.js b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-review.js index a6bb8ff1f2b1b..108c8c37dda0f 100644 --- a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-review.js +++ b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-review.js @@ -68,6 +68,7 @@ WPCheckoutOrderReview.propTypes = { removeCoupon: PropTypes.func.isRequired, getItemVariants: PropTypes.func, onChangePlanLength: PropTypes.func, + siteUrl: PropTypes.string, }; const DomainURL = styled.div` From b12be53fc6acc7448033d6b80f2321da4d30a820 Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Wed, 15 Apr 2020 15:40:41 -0400 Subject: [PATCH 07/35] Switch useTax to useFirstLineItemOfType hook --- .../wpcom/components/wp-checkout-order-summary.js | 10 +++++++--- packages/composite-checkout/src/lib/line-items.js | 7 +++++-- packages/composite-checkout/src/public-api.js | 4 ++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js index 949c972c28004..8c3b428edffe9 100644 --- a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js +++ b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js @@ -3,12 +3,16 @@ */ import React from 'react'; import styled from '@emotion/styled'; -import { useTax, useTotal, renderDisplayValueMarkdown } from '@automattic/composite-checkout'; +import { + useFirstLineItemOfType, + useTotal, + renderDisplayValueMarkdown, +} from '@automattic/composite-checkout'; import { useTranslate } from 'i18n-calypso'; export default function WPCheckoutOrderSummary() { const translate = useTranslate(); - const tax = useTax(); + const tax = useFirstLineItemOfType( 'tax' ); const total = useTotal(); return ( @@ -19,7 +23,7 @@ export default function WPCheckoutOrderSummary() { { tax && ( - { translate( 'Taxes' ) } + { translate( 'Tax' ) } { renderDisplayValueMarkdown( tax.amount.displayValue ) } diff --git a/packages/composite-checkout/src/lib/line-items.js b/packages/composite-checkout/src/lib/line-items.js index c84ecc614a036..5a511e489ed39 100644 --- a/packages/composite-checkout/src/lib/line-items.js +++ b/packages/composite-checkout/src/lib/line-items.js @@ -26,7 +26,10 @@ export function useTotal() { return total; } -export function useTax() { +export function useFirstLineItemOfType( itemType = null ) { + if ( ! itemType ) { + throw new Error( 'missing itemType for useFirstLineItemOfType' ); + } const [ items ] = useLineItems(); - return items.find( item => item.type === 'tax' ); + return items.find( item => item.type === itemType ); } diff --git a/packages/composite-checkout/src/public-api.js b/packages/composite-checkout/src/public-api.js index 8459e8474aaae..fc26880a3242a 100644 --- a/packages/composite-checkout/src/public-api.js +++ b/packages/composite-checkout/src/public-api.js @@ -19,7 +19,7 @@ import { import CheckoutModal from './components/checkout-modal'; import { renderDisplayValueMarkdown } from './lib/render'; import { usePaymentMethod, usePaymentMethodId, useAllPaymentMethods } from './lib/payment-methods'; -import { useLineItems, useTotal, useTax } from './lib/line-items'; +import { useLineItems, useTotal, useFirstLineItemOfType } from './lib/line-items'; import { createRegistry, defaultRegistry, @@ -89,6 +89,6 @@ export { useRegisterStore, useRegistry, useSelect, - useTax, + useFirstLineItemOfType, useTotal, }; From 4ce26eaf2d88e07728729755d4de05a18080a66b Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Wed, 15 Apr 2020 15:48:57 -0400 Subject: [PATCH 08/35] Remove unnecessary span --- .../wpcom/components/wp-checkout-order-summary.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js index 8c3b428edffe9..29a47d1272783 100644 --- a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js +++ b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js @@ -17,9 +17,7 @@ export default function WPCheckoutOrderSummary() { return ( - - { translate( 'Purchase Details' ) } - + { translate( 'Purchase Details' ) } { tax && ( From 946ce9ece83d5d73973c4a3e56ca93e735ba969d Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Wed, 15 Apr 2020 16:45:30 -0400 Subject: [PATCH 09/35] Return all line items of a certain type. --- .../wpcom/components/wp-checkout-order-summary.js | 12 ++++++------ packages/composite-checkout/src/lib/line-items.js | 6 +++--- packages/composite-checkout/src/public-api.js | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js index 29a47d1272783..0e2bc71a2957f 100644 --- a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js +++ b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js @@ -4,7 +4,7 @@ import React from 'react'; import styled from '@emotion/styled'; import { - useFirstLineItemOfType, + useLineItemsOfType, useTotal, renderDisplayValueMarkdown, } from '@automattic/composite-checkout'; @@ -12,21 +12,21 @@ import { useTranslate } from 'i18n-calypso'; export default function WPCheckoutOrderSummary() { const translate = useTranslate(); - const tax = useFirstLineItemOfType( 'tax' ); + const taxes = useLineItemsOfType( 'tax' ); const total = useTotal(); return ( { translate( 'Purchase Details' ) } - { tax && ( - - { translate( 'Tax' ) } + { taxes.map( tax => ( + + { tax.label } { renderDisplayValueMarkdown( tax.amount.displayValue ) } - ) } + ) ) } { translate( 'Total' ) } diff --git a/packages/composite-checkout/src/lib/line-items.js b/packages/composite-checkout/src/lib/line-items.js index 5a511e489ed39..dbf5e8c6af566 100644 --- a/packages/composite-checkout/src/lib/line-items.js +++ b/packages/composite-checkout/src/lib/line-items.js @@ -26,10 +26,10 @@ export function useTotal() { return total; } -export function useFirstLineItemOfType( itemType = null ) { +export function useLineItemsOfType( itemType = null ) { if ( ! itemType ) { - throw new Error( 'missing itemType for useFirstLineItemOfType' ); + throw new Error( 'missing itemType for useLineItemsOfType' ); } const [ items ] = useLineItems(); - return items.find( item => item.type === itemType ); + return items.filter( item => item.type === itemType ); } diff --git a/packages/composite-checkout/src/public-api.js b/packages/composite-checkout/src/public-api.js index fc26880a3242a..f84eb1f40348f 100644 --- a/packages/composite-checkout/src/public-api.js +++ b/packages/composite-checkout/src/public-api.js @@ -19,7 +19,7 @@ import { import CheckoutModal from './components/checkout-modal'; import { renderDisplayValueMarkdown } from './lib/render'; import { usePaymentMethod, usePaymentMethodId, useAllPaymentMethods } from './lib/payment-methods'; -import { useLineItems, useTotal, useFirstLineItemOfType } from './lib/line-items'; +import { useLineItems, useTotal, useLineItemsOfType } from './lib/line-items'; import { createRegistry, defaultRegistry, @@ -89,6 +89,6 @@ export { useRegisterStore, useRegistry, useSelect, - useFirstLineItemOfType, + useLineItemsOfType, useTotal, }; From ee8104a716a92f6a74ad91c387e407ddb01d9cb5 Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Wed, 15 Apr 2020 17:39:15 -0400 Subject: [PATCH 10/35] Move MainContentUI to CheckoutSteps component --- .../src/components/checkout-steps.js | 78 ++++++++++--------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/packages/composite-checkout/src/components/checkout-steps.js b/packages/composite-checkout/src/components/checkout-steps.js index ca2e183ccd6d0..18ba27215e852 100644 --- a/packages/composite-checkout/src/components/checkout-steps.js +++ b/packages/composite-checkout/src/components/checkout-steps.js @@ -33,13 +33,11 @@ const CheckoutSingleStepDataContext = React.createContext(); export function Checkout( { children, className } ) { const { formStatus } = useFormStatus(); - const localize = useLocalize(); const [ activeStepNumber, setActiveStepNumber ] = useState( 1 ); const [ stepCompleteStatus, setStepCompleteStatus ] = useState( {} ); const [ totalSteps, setTotalSteps ] = useState( 0 ); const actualActiveStepNumber = activeStepNumber > totalSteps && totalSteps > 0 ? totalSteps : activeStepNumber; - const isThereAnotherNumberedStep = actualActiveStepNumber < totalSteps; // Change the step if the url changes useChangeStepNumberForUrl( setActiveStepNumber ); @@ -71,22 +69,7 @@ export function Checkout( { children, className } ) { setTotalSteps, } } > - - { children || getDefaultCheckoutSteps() } - - - - - - - + { children || getDefaultCheckoutSteps() } ); @@ -131,7 +114,10 @@ function DefaultCheckoutSteps() { ); } -export function CheckoutSteps( { children } ) { +export function CheckoutSteps( { children, className } ) { + const localize = useLocalize(); + const { formStatus } = useFormStatus(); + let stepNumber = 0; let nextStepNumber = 1; const steps = React.Children.toArray( children ).filter( ( child ) => child ); @@ -139,6 +125,7 @@ export function CheckoutSteps( { children } ) { const { activeStepNumber, stepCompleteStatus, setTotalSteps } = useContext( CheckoutStepDataContext ); + const isThereAnotherNumberedStep = activeStepNumber < totalSteps; useEffect( () => { setTotalSteps( totalSteps ); @@ -153,25 +140,40 @@ export function CheckoutSteps( { children } ) { totalSteps ); - return steps.map( ( child ) => { - stepNumber = nextStepNumber; - nextStepNumber = stepNumber === totalSteps ? null : stepNumber + 1; - const isStepActive = activeStepNumber === stepNumber; - const isStepComplete = !! stepCompleteStatus[ stepNumber ]; - return ( - - { child } - - ); - } ); + return ( + + { steps.map( child => { + stepNumber = nextStepNumber; + nextStepNumber = stepNumber === totalSteps ? null : stepNumber + 1; + const isStepActive = activeStepNumber === stepNumber; + const isStepComplete = !! stepCompleteStatus[ stepNumber ]; + return ( + + { child } + + ); + } ) } + + + + + + + + ); } export function CheckoutStep( { From b2085e4cac9ea519226b7b7a297ce49c0e80e85b Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Thu, 16 Apr 2020 10:35:28 -0400 Subject: [PATCH 11/35] Restructure Checkout and CheckoutSteps to allow for CheckoutSummary - Move CheckoutStepBody inside CheckoutSteps - Split real steps from not - Move `checkout-content` back to Checkout component - Update loading - Update demo --- packages/composite-checkout/demo/index.js | 22 +++--- .../src/components/checkout-steps.js | 67 +++++++++++-------- .../src/components/loading-content.js | 18 ++++- 3 files changed, 65 insertions(+), 42 deletions(-) diff --git a/packages/composite-checkout/demo/index.js b/packages/composite-checkout/demo/index.js index 4c1bfe8dbb864..e047b6652d773 100644 --- a/packages/composite-checkout/demo/index.js +++ b/packages/composite-checkout/demo/index.js @@ -394,18 +394,18 @@ function MyCheckoutBody() { return ( - + true } diff --git a/packages/composite-checkout/src/components/checkout-steps.js b/packages/composite-checkout/src/components/checkout-steps.js index 18ba27215e852..52608e526adc6 100644 --- a/packages/composite-checkout/src/components/checkout-steps.js +++ b/packages/composite-checkout/src/components/checkout-steps.js @@ -59,18 +59,20 @@ export function Checkout( { children, className } ) { return ( - - { children || getDefaultCheckoutSteps() } - + + + { children || getDefaultCheckoutSteps() } + + ); } @@ -81,19 +83,19 @@ function DefaultCheckoutSteps() { const reviewOrderStep = getDefaultOrderReviewStep(); return ( - + true } @@ -120,7 +122,9 @@ export function CheckoutSteps( { children, className } ) { let stepNumber = 0; let nextStepNumber = 1; - const steps = React.Children.toArray( children ).filter( ( child ) => child ); + const componentChildren = React.Children.toArray( children ).filter( child => child ); + const nonSteps = componentChildren.filter( child => child.type?.name !== 'CheckoutStep' ); + const steps = componentChildren.filter( child => child.type?.name === 'CheckoutStep' ); const totalSteps = steps.length; const { activeStepNumber, stepCompleteStatus, setTotalSteps } = useContext( CheckoutStepDataContext @@ -141,10 +145,11 @@ export function CheckoutSteps( { children, className } ) { ); return ( - + { nonSteps } { steps.map( child => { stepNumber = nextStepNumber; nextStepNumber = stepNumber === totalSteps ? null : stepNumber + 1; @@ -172,7 +177,7 @@ export function CheckoutSteps( { children, className } ) { - + ); } @@ -368,7 +373,11 @@ const ContainerUI = styled.div` `; const MainContentUI = styled.div` - background: ${( props ) => props.theme.colors.surface}; + display: flex; +`; + +const CheckoutStepsWrapperUI = styled.div` + background: ${props => props.theme.colors.surface}; width: 100%; box-sizing: border-box; margin-bottom: ${( props ) => ( props.isLastStepActive ? '100px' : 0) }; diff --git a/packages/composite-checkout/src/components/loading-content.js b/packages/composite-checkout/src/components/loading-content.js index c674638d5c9e3..20b4e1f2ade07 100644 --- a/packages/composite-checkout/src/components/loading-content.js +++ b/packages/composite-checkout/src/components/loading-content.js @@ -14,7 +14,7 @@ export default function LoadingContent() { const localize = useLocalize(); return ( -
+ { localize( 'Loading checkout' ) } @@ -32,10 +32,24 @@ export default function LoadingContent() { -
+ ); } +const LoadingContentWrapperUI = styled.div` + background: ${props => props.theme.colors.surface}; + width: 100%; + box-sizing: border-box; + margin-bottom: ${props => ( props.isLastStepActive ? '100px' : 0 )}; + + @media ( ${props => props.theme.breakpoints.tabletUp} ) { + border: 1px solid ${props => props.theme.colors.borderColorLight}; + margin: 32px auto; + box-sizing: border-box; + max-width: 556px; + } +`; + const LoadingCard = styled.div` padding: 24px; border-top: 1px solid ${( props ) => props.theme.colors.borderColorLight}; From a4150feb36036ad23e9ff8801ba3013634368db7 Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Thu, 16 Apr 2020 14:50:07 -0400 Subject: [PATCH 12/35] Add CheckoutSummary component skeleton with defaults and demo --- packages/composite-checkout/demo/index.js | 9 +++ .../src/components/checkout-order-summary.js | 6 +- .../src/components/checkout-steps.js | 61 +++++++++++++++++-- .../src/components/default-steps.js | 18 +++++- packages/composite-checkout/src/public-api.js | 8 ++- 5 files changed, 91 insertions(+), 11 deletions(-) diff --git a/packages/composite-checkout/demo/index.js b/packages/composite-checkout/demo/index.js index e047b6652d773..9a152cdb15c06 100644 --- a/packages/composite-checkout/demo/index.js +++ b/packages/composite-checkout/demo/index.js @@ -9,6 +9,7 @@ import styled from '@emotion/styled'; import ReactDOM from 'react-dom'; import { Checkout, + CheckoutSummary, CheckoutSteps, CheckoutStep, CheckoutStepBody, @@ -18,6 +19,7 @@ import { createStripeMethod, createStripePaymentMethodStore, defaultRegistry, + getDefaultOrderSummary, getDefaultOrderReviewStep, getDefaultOrderSummaryStep, getDefaultPaymentMethodStep, @@ -258,6 +260,7 @@ function ContactForm( { summary } ) { ); } +const orderSummary = getDefaultOrderSummary(); const orderSummaryStep = getDefaultOrderSummaryStep(); const paymentMethodStep = getDefaultPaymentMethodStep(); const reviewOrderStep = getDefaultOrderReviewStep(); @@ -394,6 +397,12 @@ function MyCheckoutBody() { return ( + ; +} + export function CheckoutOrderSummaryTitle() { const localize = useLocalize(); const total = useTotal(); diff --git a/packages/composite-checkout/src/components/checkout-steps.js b/packages/composite-checkout/src/components/checkout-steps.js index 52608e526adc6..f2cda0da96cf6 100644 --- a/packages/composite-checkout/src/components/checkout-steps.js +++ b/packages/composite-checkout/src/components/checkout-steps.js @@ -20,6 +20,7 @@ import { CheckIcon } from './shared-icons'; import CheckoutNextStepButton from './checkout-next-step-button'; import { getDefaultOrderReviewStep, + getDefaultOrderSummary, getDefaultOrderSummaryStep, getDefaultPaymentMethodStep, usePaymentMethod, @@ -78,11 +79,18 @@ export function Checkout( { children, className } ) { } function DefaultCheckoutSteps() { + const orderSummary = getDefaultOrderSummary(); const orderSummaryStep = getDefaultOrderSummaryStep(); const paymentMethodStep = getDefaultPaymentMethodStep(); const reviewOrderStep = getDefaultOrderReviewStep(); return ( + + { children } + + ); +} + export function CheckoutSteps( { children, className } ) { const localize = useLocalize(); const { formStatus } = useFormStatus(); @@ -374,20 +390,53 @@ const ContainerUI = styled.div` const MainContentUI = styled.div` display: flex; + flex-direction: column; + + @media ( ${props => props.theme.breakpoints.tabletUp} ) { + margin: 32px auto; + } + + @media ( ${props => props.theme.breakpoints.desktopUp} ) { + width: 882px; + flex-direction: row; + } +`; + +const CheckoutSummaryUI = styled.div` + background: ${props => props.theme.colors.surface}; + + @media ( ${props => props.theme.breakpoints.tabletUp} ) { + border: 1px solid ${props => props.theme.colors.borderColorLight}; + box-sizing: border-box; + max-width: 556px; + } + + @media ( ${props => props.theme.breakpoints.desktopUp} ) { + border: 1px solid ${props => props.theme.colors.borderColorLight}; + box-sizing: border-box; + margin-left: 16px; + margin-right: 0; + order: 2; + width: 326px; + } `; const CheckoutStepsWrapperUI = styled.div` background: ${props => props.theme.colors.surface}; - width: 100%; box-sizing: border-box; - margin-bottom: ${( props ) => ( props.isLastStepActive ? '100px' : 0) }; + margin-bottom: ${props => ( props.isLastStepActive ? '100px' : 0 )}; + width: 100%; - @media ( ${( props ) => props.theme.breakpoints.tabletUp} ) { - border: 1px solid ${( props ) => props.theme.colors.borderColorLight}; - margin: 32px auto; + @media ( ${props => props.theme.breakpoints.tabletUp} ) { + border: 1px solid ${props => props.theme.colors.borderColorLight}; box-sizing: border-box; max-width: 556px; } + + @media ( ${props => props.theme.breakpoints.desktopUp} ) { + width: 556px; + order: 1; + } `; const SubmitButtonWrapperUI = styled.div` diff --git a/packages/composite-checkout/src/components/default-steps.js b/packages/composite-checkout/src/components/default-steps.js index f1cf486318685..e333c5735131c 100644 --- a/packages/composite-checkout/src/components/default-steps.js +++ b/packages/composite-checkout/src/components/default-steps.js @@ -6,19 +6,31 @@ import React from 'react'; /** * Internal dependencies */ -import CheckoutOrderSummary, { CheckoutOrderSummaryTitle } from './checkout-order-summary'; +import CheckoutOrderSummaryStep, { + CheckoutOrderSummaryTitle, + CheckoutOrderSummary, +} from './checkout-order-summary'; import CheckoutReviewOrder, { CheckoutReviewOrderTitle } from './checkout-review-order'; import CheckoutPaymentMethods, { CheckoutPaymentMethodsTitle } from './checkout-payment-methods'; -export function getDefaultOrderSummaryStep() { +export function getDefaultOrderSummary() { return { id: 'order-summary', + className: 'checkout__order-summary', + titleContent: , + summaryContent: , + }; +} + +export function getDefaultOrderSummaryStep() { + return { + id: 'order-summary-step', className: 'checkout__order-summary-step', hasStepNumber: false, titleContent: , activeStepContent: null, incompleteStepContent: null, - completeStepContent: , + completeStepContent: , isCompleteCallback: () => true, }; } diff --git a/packages/composite-checkout/src/public-api.js b/packages/composite-checkout/src/public-api.js index f84eb1f40348f..0cb8b43997e06 100644 --- a/packages/composite-checkout/src/public-api.js +++ b/packages/composite-checkout/src/public-api.js @@ -7,6 +7,7 @@ import { Checkout, CheckoutStep, CheckoutStepBody, + CheckoutSummary, useIsStepActive, useIsStepComplete, } from './components/checkout-steps'; @@ -38,10 +39,12 @@ import { import { createApplePayMethod } from './lib/payment-methods/apple-pay'; import { createPayPalMethod } from './lib/payment-methods/paypal'; import { createExistingCardMethod } from './lib/payment-methods/existing-credit-card'; -import CheckoutOrderSummary, { +import CheckoutOrderSummaryStep, { + CheckoutOrderSummary, CheckoutOrderSummaryTitle, } from './components/checkout-order-summary'; import { + getDefaultOrderSummary, getDefaultOrderSummaryStep, getDefaultPaymentMethodStep, getDefaultOrderReviewStep, @@ -52,6 +55,7 @@ import { useFormStatus } from './lib/form-status'; export { Checkout, CheckoutModal, + CheckoutOrderSummaryStep, CheckoutOrderSummary, CheckoutOrderSummaryTitle, CheckoutPaymentMethods, @@ -59,6 +63,7 @@ export { CheckoutStep, CheckoutStepBody, CheckoutSteps, + CheckoutSummary, OrderReviewLineItems, OrderReviewSection, OrderReviewTotal, @@ -71,6 +76,7 @@ export { createStripeMethod, createStripePaymentMethodStore, defaultRegistry, + getDefaultOrderSummary, getDefaultOrderReviewStep, getDefaultOrderSummaryStep, getDefaultPaymentMethodStep, From 8a554ad1b96648032c2c375d5ea8e666b5ce0758 Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Thu, 16 Apr 2020 15:00:31 -0400 Subject: [PATCH 13/35] Cleanup CheckoutSummary - Remove props from Checkout Summary so that component just renders children - Update demo and defaults - Fix summary height when split into columns --- packages/composite-checkout/demo/index.js | 9 +++------ .../src/components/checkout-steps.js | 11 +++++------ .../src/components/default-steps.js | 1 - 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/packages/composite-checkout/demo/index.js b/packages/composite-checkout/demo/index.js index 9a152cdb15c06..84c4ec67e5100 100644 --- a/packages/composite-checkout/demo/index.js +++ b/packages/composite-checkout/demo/index.js @@ -397,12 +397,9 @@ function MyCheckoutBody() { return ( - + + { orderSummary.summaryContent } + - + + { orderSummary.summaryContent } + props.theme.breakpoints.desktopUp} ) { + align-items: flex-start; width: 882px; flex-direction: row; } @@ -404,6 +402,7 @@ const MainContentUI = styled.div` const CheckoutSummaryUI = styled.div` background: ${props => props.theme.colors.surface}; + padding: 16px; @media ( ${props => props.theme.breakpoints.tabletUp} ) { border: 1px solid ${props => props.theme.colors.borderColorLight}; diff --git a/packages/composite-checkout/src/components/default-steps.js b/packages/composite-checkout/src/components/default-steps.js index e333c5735131c..da381ad6305e5 100644 --- a/packages/composite-checkout/src/components/default-steps.js +++ b/packages/composite-checkout/src/components/default-steps.js @@ -17,7 +17,6 @@ export function getDefaultOrderSummary() { return { id: 'order-summary', className: 'checkout__order-summary', - titleContent: , summaryContent: , }; } From b4555b655331275622d000a4e8964e42b960b86f Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Thu, 16 Apr 2020 15:10:21 -0400 Subject: [PATCH 14/35] Cleanup styles up to this point --- .../src/components/checkout-steps.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/composite-checkout/src/components/checkout-steps.js b/packages/composite-checkout/src/components/checkout-steps.js index 316ea3c3b443e..5c17b012930e1 100644 --- a/packages/composite-checkout/src/components/checkout-steps.js +++ b/packages/composite-checkout/src/components/checkout-steps.js @@ -388,26 +388,29 @@ const ContainerUI = styled.div` const MainContentUI = styled.div` display: flex; flex-direction: column; + width: 100%; @media ( ${props => props.theme.breakpoints.tabletUp} ) { margin: 32px auto; + max-width: 556px; } @media ( ${props => props.theme.breakpoints.desktopUp} ) { align-items: flex-start; - width: 882px; flex-direction: row; + max-width: none; + width: 882px; } `; const CheckoutSummaryUI = styled.div` background: ${props => props.theme.colors.surface}; + box-sizing: border-box; padding: 16px; + width: 100%; @media ( ${props => props.theme.breakpoints.tabletUp} ) { border: 1px solid ${props => props.theme.colors.borderColorLight}; - box-sizing: border-box; - max-width: 556px; } @media ( ${props => props.theme.breakpoints.desktopUp} ) { @@ -428,8 +431,6 @@ const CheckoutStepsWrapperUI = styled.div` @media ( ${props => props.theme.breakpoints.tabletUp} ) { border: 1px solid ${props => props.theme.colors.borderColorLight}; - box-sizing: border-box; - max-width: 556px; } @media ( ${props => props.theme.breakpoints.desktopUp} ) { From e9ed68baf149ee22e022485ea1be3c1ea92b8778 Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Thu, 16 Apr 2020 15:27:13 -0400 Subject: [PATCH 15/35] Make CheckoutSummary default content unique from CheckoutSummaryStep --- .../src/components/checkout-order-summary.js | 80 ++++++++++++++++--- .../src/components/checkout-steps.js | 2 +- .../src/components/default-steps.js | 4 +- packages/composite-checkout/src/public-api.js | 4 +- 4 files changed, 72 insertions(+), 18 deletions(-) diff --git a/packages/composite-checkout/src/components/checkout-order-summary.js b/packages/composite-checkout/src/components/checkout-order-summary.js index 5c9bcd5e333bb..87c2500b1f3e5 100644 --- a/packages/composite-checkout/src/components/checkout-order-summary.js +++ b/packages/composite-checkout/src/components/checkout-order-summary.js @@ -7,8 +7,14 @@ import styled from '@emotion/styled'; /** * Internal dependencies */ -import { useLineItems, useTotal, renderDisplayValueMarkdown } from '../public-api'; +import { + useLineItems, + useLineItemsOfType, + useTotal, + renderDisplayValueMarkdown, +} from '../public-api'; import { useLocalize } from '../lib/localize'; +import { useTranslate } from 'i18n-calypso'; export default function CheckoutOrderSummaryStep() { const [ items ] = useLineItems(); @@ -33,28 +39,76 @@ const ProductListItem = styled.li` list-style-type: none; `; -export function CheckoutOrderSummary() { - return ; -} - -export function CheckoutOrderSummaryTitle() { +export function CheckoutOrderSummaryStepTitle() { const localize = useLocalize(); const total = useTotal(); return ( - + { localize( 'You are all set to check out' ) } - + { renderDisplayValueMarkdown( total.amount.displayValue ) } - - + + + ); +} + +const CheckoutSummaryStepTitle = styled.span` + display: flex; + justify-content: space-between; +`; + +const CheckoutSummaryStepTotal = styled.span` + font-weight: ${props => props.theme.weights.bold}; +`; + +export function CheckoutOrderSummary() { + const translate = useTranslate(); + const taxes = useLineItemsOfType( 'tax' ); + const total = useTotal(); + + return ( + <> + { translate( 'Purchase Details' ) } + + { taxes.map( tax => ( + + { tax.label } + + { renderDisplayValueMarkdown( tax.amount.displayValue ) } + + + ) ) } + + { translate( 'Total' ) } + + { renderDisplayValueMarkdown( total.amount.displayValue ) } + + + + ); } -const CheckoutSummaryTitle = styled.span` +const CheckoutSummaryTitle = styled.div` + color: ${props => props.theme.colors.textColor}; + font-weight: ${props => props.theme.weights.bold}; + padding: 16px; +`; + +const CheckoutSummaryAmountWrapper = styled.div` + border-top: 1px solid ${props => props.theme.colors.borderColorLight}; + padding: 16px; +`; + +const CheckoutSummaryLabel = styled.span``; + +const CheckoutSummaryAmount = styled.span``; + +const CheckoutSummaryLineItem = styled.div` display: flex; justify-content: space-between; `; -const CheckoutSummaryTotal = styled.span` - font-weight: ${( props ) => props.theme.weights.bold}; +const CheckoutSummaryTotal = styled( CheckoutSummaryLineItem )` + font-weight: ${props => props.theme.weights.bold}; `; diff --git a/packages/composite-checkout/src/components/checkout-steps.js b/packages/composite-checkout/src/components/checkout-steps.js index 5c17b012930e1..d638c29149c80 100644 --- a/packages/composite-checkout/src/components/checkout-steps.js +++ b/packages/composite-checkout/src/components/checkout-steps.js @@ -406,11 +406,11 @@ const MainContentUI = styled.div` const CheckoutSummaryUI = styled.div` background: ${props => props.theme.colors.surface}; box-sizing: border-box; - padding: 16px; width: 100%; @media ( ${props => props.theme.breakpoints.tabletUp} ) { border: 1px solid ${props => props.theme.colors.borderColorLight}; + border-bottom: none 0; } @media ( ${props => props.theme.breakpoints.desktopUp} ) { diff --git a/packages/composite-checkout/src/components/default-steps.js b/packages/composite-checkout/src/components/default-steps.js index da381ad6305e5..78917170b08eb 100644 --- a/packages/composite-checkout/src/components/default-steps.js +++ b/packages/composite-checkout/src/components/default-steps.js @@ -7,7 +7,7 @@ import React from 'react'; * Internal dependencies */ import CheckoutOrderSummaryStep, { - CheckoutOrderSummaryTitle, + CheckoutOrderSummaryStepTitle, CheckoutOrderSummary, } from './checkout-order-summary'; import CheckoutReviewOrder, { CheckoutReviewOrderTitle } from './checkout-review-order'; @@ -26,7 +26,7 @@ export function getDefaultOrderSummaryStep() { id: 'order-summary-step', className: 'checkout__order-summary-step', hasStepNumber: false, - titleContent: , + titleContent: , activeStepContent: null, incompleteStepContent: null, completeStepContent: , diff --git a/packages/composite-checkout/src/public-api.js b/packages/composite-checkout/src/public-api.js index 0cb8b43997e06..e0aa1f89b3178 100644 --- a/packages/composite-checkout/src/public-api.js +++ b/packages/composite-checkout/src/public-api.js @@ -41,7 +41,7 @@ import { createPayPalMethod } from './lib/payment-methods/paypal'; import { createExistingCardMethod } from './lib/payment-methods/existing-credit-card'; import CheckoutOrderSummaryStep, { CheckoutOrderSummary, - CheckoutOrderSummaryTitle, + CheckoutOrderSummaryStepTitle, } from './components/checkout-order-summary'; import { getDefaultOrderSummary, @@ -57,7 +57,7 @@ export { CheckoutModal, CheckoutOrderSummaryStep, CheckoutOrderSummary, - CheckoutOrderSummaryTitle, + CheckoutOrderSummaryStepTitle, CheckoutPaymentMethods, CheckoutProvider, CheckoutStep, From d1f734c05b67d50db11f71a3d8d58c1fb6aac301 Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Thu, 16 Apr 2020 15:33:36 -0400 Subject: [PATCH 16/35] Remove unused styled components from default summary --- .../src/components/checkout-order-summary.js | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/packages/composite-checkout/src/components/checkout-order-summary.js b/packages/composite-checkout/src/components/checkout-order-summary.js index 87c2500b1f3e5..7a4f11562492c 100644 --- a/packages/composite-checkout/src/components/checkout-order-summary.js +++ b/packages/composite-checkout/src/components/checkout-order-summary.js @@ -72,17 +72,13 @@ export function CheckoutOrderSummary() { { taxes.map( tax => ( - { tax.label } - - { renderDisplayValueMarkdown( tax.amount.displayValue ) } - + { tax.label } + { renderDisplayValueMarkdown( tax.amount.displayValue ) } ) ) } - { translate( 'Total' ) } - - { renderDisplayValueMarkdown( total.amount.displayValue ) } - + { translate( 'Total' ) } + { renderDisplayValueMarkdown( total.amount.displayValue ) } @@ -100,10 +96,6 @@ const CheckoutSummaryAmountWrapper = styled.div` padding: 16px; `; -const CheckoutSummaryLabel = styled.span``; - -const CheckoutSummaryAmount = styled.span``; - const CheckoutSummaryLineItem = styled.div` display: flex; justify-content: space-between; From 3dd40e48d5823c8b0dbfc15b3972529bb989dfbd Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Thu, 16 Apr 2020 15:55:02 -0400 Subject: [PATCH 17/35] Use CheckoutSummary in WPcom Checkout --- .../components/wp-checkout-order-summary.js | 31 +--- .../wpcom/components/wp-checkout.js | 143 +++++++++--------- .../src/components/checkout-steps.js | 1 + 3 files changed, 78 insertions(+), 97 deletions(-) diff --git a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js index 0e2bc71a2957f..7ff7a7ecfadb7 100644 --- a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js +++ b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js @@ -16,39 +16,24 @@ export default function WPCheckoutOrderSummary() { const total = useTotal(); return ( - + <> { translate( 'Purchase Details' ) } { taxes.map( tax => ( - { tax.label } - - { renderDisplayValueMarkdown( tax.amount.displayValue ) } - + { tax.label } + { renderDisplayValueMarkdown( tax.amount.displayValue ) } ) ) } - { translate( 'Total' ) } - - { renderDisplayValueMarkdown( total.amount.displayValue ) } - + { translate( 'Total' ) } + { renderDisplayValueMarkdown( total.amount.displayValue ) } - + ); } -const CheckoutSummaryWrapper = styled.div` - background: ${props => props.theme.colors.surface}; - border: 1px solid ${props => props.theme.colors.borderColorLight}; - - @media ( ${props => props.theme.breakpoints.desktopUp} ) { - float: right; - margin-left: 16px; - width: 326px; - } -`; - const CheckoutSummaryTitle = styled.h2` color: ${props => props.theme.colors.textColor}; font-weight: ${props => props.theme.weights.bold}; @@ -60,10 +45,6 @@ const CheckoutSummaryAmountWrapper = styled.div` padding: 16px; `; -const CheckoutSummaryLabel = styled.span``; - -const CheckoutSummaryAmount = styled.span``; - const CheckoutSummaryLineItem = styled.div` display: flex; flex-wrap: wrap; diff --git a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout.js b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout.js index 5b6ce97959a06..73a6e1d4c5168 100644 --- a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout.js +++ b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout.js @@ -8,6 +8,7 @@ import { Checkout, CheckoutSteps, CheckoutStep, + CheckoutSummary, getDefaultPaymentMethodStep, useIsStepActive, useIsStepComplete, @@ -128,89 +129,87 @@ export default function WPCheckout( { }; return ( - <> - - - + + + + + + true } + activeStepContent={ + + } + titleContent={ } + completeStepContent={ } + editButtonText={ translate( 'Edit' ) } + editButtonAriaLabel={ translate( 'Edit the payment method' ) } + nextStepButtonText={ translate( 'Continue' ) } + nextStepButtonAriaLabel={ translate( 'Continue with the selected payment method' ) } + validatingButtonText={ translate( 'Please wait…' ) } + validatingButtonAriaLabel={ translate( 'Please wait…' ) } + /> + { shouldShowContactStep && ( true } + stepId={ 'contact-form' } + isCompleteCallback={ () => { + setShouldShowContactDetailsValidationErrors( true ); + return contactValidationCallback(); + } } activeStepContent={ - } - titleContent={ } - completeStepContent={ } + completeStepContent={ } + titleContent={ } editButtonText={ translate( 'Edit' ) } - editButtonAriaLabel={ translate( 'Edit the payment method' ) } + editButtonAriaLabel={ translate( 'Edit the contact details' ) } nextStepButtonText={ translate( 'Continue' ) } - nextStepButtonAriaLabel={ translate( 'Continue with the selected payment method' ) } + nextStepButtonAriaLabel={ translate( 'Continue with the entered contact details' ) } validatingButtonText={ translate( 'Please wait…' ) } validatingButtonAriaLabel={ translate( 'Please wait…' ) } /> - { shouldShowContactStep && ( - { - setShouldShowContactDetailsValidationErrors( true ); - return contactValidationCallback(); - } } - activeStepContent={ - - } - completeStepContent={ - - } - titleContent={ } - editButtonText={ translate( 'Edit' ) } - editButtonAriaLabel={ translate( 'Edit the contact details' ) } - nextStepButtonText={ translate( 'Continue' ) } - nextStepButtonAriaLabel={ translate( 'Continue with the entered contact details' ) } - validatingButtonText={ translate( 'Please wait…' ) } - validatingButtonAriaLabel={ translate( 'Please wait…' ) } + ) } + - ) } - - } - completeStepContent={ paymentMethodStep.completeStepContent } - titleContent={ paymentMethodStep.titleContent } - editButtonText={ translate( 'Edit' ) } - editButtonAriaLabel={ translate( 'Edit the payment method' ) } - nextStepButtonText={ translate( 'Continue' ) } - nextStepButtonAriaLabel={ translate( 'Continue with the selected payment method' ) } - validatingButtonText={ translate( 'Please wait…' ) } - validatingButtonAriaLabel={ translate( 'Please wait…' ) } - /> - - - + } + completeStepContent={ paymentMethodStep.completeStepContent } + titleContent={ paymentMethodStep.titleContent } + editButtonText={ translate( 'Edit' ) } + editButtonAriaLabel={ translate( 'Edit the payment method' ) } + nextStepButtonText={ translate( 'Continue' ) } + nextStepButtonAriaLabel={ translate( 'Continue with the selected payment method' ) } + validatingButtonText={ translate( 'Please wait…' ) } + validatingButtonAriaLabel={ translate( 'Please wait…' ) } + /> + + ); } diff --git a/packages/composite-checkout/src/components/checkout-steps.js b/packages/composite-checkout/src/components/checkout-steps.js index d638c29149c80..2cd5829c4bbd8 100644 --- a/packages/composite-checkout/src/components/checkout-steps.js +++ b/packages/composite-checkout/src/components/checkout-steps.js @@ -405,6 +405,7 @@ const MainContentUI = styled.div` const CheckoutSummaryUI = styled.div` background: ${props => props.theme.colors.surface}; + border-bottom: 1px solid ${props => props.theme.colors.borderColorLight}; box-sizing: border-box; width: 100%; From 7fe28da1969dd4277830174d6b0cec8c885d7941 Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Thu, 16 Apr 2020 17:54:22 -0400 Subject: [PATCH 18/35] Remove widths from MainContentUI to allow for optional CheckoutSummary --- packages/composite-checkout/src/components/checkout-steps.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/composite-checkout/src/components/checkout-steps.js b/packages/composite-checkout/src/components/checkout-steps.js index 2cd5829c4bbd8..39bb0151ca9f7 100644 --- a/packages/composite-checkout/src/components/checkout-steps.js +++ b/packages/composite-checkout/src/components/checkout-steps.js @@ -392,14 +392,13 @@ const MainContentUI = styled.div` @media ( ${props => props.theme.breakpoints.tabletUp} ) { margin: 32px auto; - max-width: 556px; } @media ( ${props => props.theme.breakpoints.desktopUp} ) { align-items: flex-start; flex-direction: row; + justify-content: center; max-width: none; - width: 882px; } `; @@ -412,6 +411,7 @@ const CheckoutSummaryUI = styled.div` @media ( ${props => props.theme.breakpoints.tabletUp} ) { border: 1px solid ${props => props.theme.colors.borderColorLight}; border-bottom: none 0; + max-width: 556px; } @media ( ${props => props.theme.breakpoints.desktopUp} ) { @@ -432,6 +432,7 @@ const CheckoutStepsWrapperUI = styled.div` @media ( ${props => props.theme.breakpoints.tabletUp} ) { border: 1px solid ${props => props.theme.colors.borderColorLight}; + max-width: 556px; } @media ( ${props => props.theme.breakpoints.desktopUp} ) { From 840da4a0f5163f5f84b0e336aae9be963d238108 Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Mon, 20 Apr 2020 11:37:49 -0400 Subject: [PATCH 19/35] Update documentation to match current state --- packages/composite-checkout/README.md | 47 ++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/packages/composite-checkout/README.md b/packages/composite-checkout/README.md index 164e801ae4e03..b125982be63e8 100644 --- a/packages/composite-checkout/README.md +++ b/packages/composite-checkout/README.md @@ -25,13 +25,34 @@ It's also possible to build an entirely custom form using the other components e ## How to use this package -Most components of this package require being inside a [CheckoutProvider](#checkoutprovider). That component requires an array of [Payment Method objects](#payment-methods) which define the available payment methods (stripe credit cards, apple pay, paypal, credits, etc.) that will be displayed in the form. While you can create these objects manually, the package provides many pre-defined payment method objects that can be created by using the functions [createStripeMethod](#createstripemethod), [createApplePayMethod](#createapplepaymethod), [createPayPalMethod](#createpaypalmethod), [createFullCreditsMethod](#createFullCreditsMethod), and [createExistingCardMethod](#createExistingCardMethod). - -Any component which is a child of `CheckoutProvider` gets access to the custom hooks [useAllPaymentMethods](#useAllPaymentMethods), [useEvents](#useEvents), [useFormStatus](#useFormStatus), [useMessages](#useMessages), [useDispatch](#useDispatch), [useLineItems](#useLineItems), [usePaymentMethod](#usePaymentMethodId), [usePaymentMethodId](#usePaymentMethodId), [useRegisterStore](#useRegisterStore), [useRegistry](#useRegistry), [useSelect](#useSelect), and [useTotal](#useTotal). - -The [Checkout](#checkout) component creates the form itself. Within the component's children, you can render elements to create the checkout experience. Any child is allowed but the [CheckoutStepBody](#CheckoutStepBody) component can be used to render something that looks like a checkout step. A series of these can be used to create a semantic form. If you would like to have a series of steps that are joined by "Continue" buttons which are hidden and displayed as needed, you can instead use the [CheckoutSteps](#CheckoutSteps) component with [CheckoutStep](#CheckoutStep) children that will take care of the logic for you. - -Each `CheckoutStep` has a `isCompleteCallback` prop, which will be called when the "Continue" button is pressed. It can perform validation on that step's contents to determine if the form should continue to the next step. If the function returns true, the form continues to the next step, otherwise it remains on the same step. If the function returns a `Promise`, then the "Continue" button will change to "Please wait…" until the Promise resolves allowing for async operations. The value resolved by the Promise must be a boolean; true to continue, false to stay on the current step. +Most components of this package require being inside a [CheckoutProvider](#checkoutprovider). That component requires an array of [Payment Method objects](#payment-methods) which define the available payment methods (stripe credit cards, apple pay, paypal, credits, etc.) that will be displayed in the form. While you can create these objects manually, the package provides many pre-defined payment method objects that can be created by using the following functions: + - [createApplePayMethod](#createapplepaymethod) + - [createExistingCardMethod](#createExistingCardMethod) + - [createFullCreditsMethod](#createFullCreditsMethod) + - [createPayPalMethod](#createpaypalmethod) + - [createStripeMethod](#createstripemethod) + +Any component which is a child of `CheckoutProvider` gets access to the following custom hooks: + - [useAllPaymentMethods](#useAllPaymentMethods) + - [useEvents](#useEvents) + - [useFormStatus](#useFormStatus) + - [useMessages](#useMessages) + - [useDispatch](#useDispatch) + - [useLineItems](#useLineItems) + - [useLineItemsOfType](#useLineItemsOfType) + - [usePaymentMethod](#usePaymentMethodId) + - [usePaymentMethodId](#usePaymentMethodId) + - [useRegisterStore](#useRegisterStore) + - [useRegistry](#useRegistry) + - [useSelect](#useSelect) + - [useTotal](#useTotal) + +The [Checkout](#checkout) component creates the form itself. Within the component you can render any children to create the checkout experience, but a few components are provided to make this easier: + - [CheckoutSummary](#CheckoutSummary) can be used to render a summary that, by default, floats beside the checkout steps on larger screens and collapses behind a toggle at the top of smaller screens. + - [CheckoutStepBody](#CheckoutStepBody) can be used to render something that looks like a checkout step. A series of these can be used to create a semantic form. + - [CheckoutSteps](#CheckoutSteps) with [CheckoutStep](#CheckoutStep) children can be used to create a series of steps that are joined by "Continue" buttons which are hidden and displayed as needed. + +Each `CheckoutStep` has an `isCompleteCallback` prop, which will be called when the "Continue" button is pressed. It can perform validation on that step's contents to determine if the form should continue to the next step. If the function returns true, the form continues to the next step, otherwise it remains on the same step. If the function returns a `Promise`, then the "Continue" button will change to "Please wait…" until the Promise resolves allowing for async operations. The value resolved by the Promise must be a boolean; true to continue, false to stay on the current step. Any component within a `CheckoutStep` gets access to the custom hooks above as well as [useIsStepActive](#useIsStepActive) and [useIsStepComplete](#useIsStepComplete). @@ -98,6 +119,12 @@ The main wrapper component for the checkout form. It has the following props. - `className?: string`. The className for the component. +### CheckoutSummary + +Renders its `children` prop and acts as a wrapper to flow outside of the [`CheckoutSteps`](#CehckoutSteps) wrapper (floated on desktop, collapsed on mobile). It has the following props. + +- `className?: string`. The className for the component. + ### CheckoutProvider Renders its `children` prop and acts as a React Context provider. All of checkout should be wrapped in this. @@ -165,7 +192,7 @@ A component that looks like a checkout step. Normally you don't need to use this ## CheckoutSteps -A wrapper for [CheckoutStep](#CheckoutStep) objects that will connect the steps and provide a way to switch between them. Should be a direct child of [Checkout](#Checkout). +A wrapper for [CheckoutStep](#CheckoutStep) and [CheckoutStepBody](#CheckoutStepBody) objects that will connect the steps and provide a way to switch between them. Should be a direct child of [Checkout](#Checkout). ### CheckoutReviewOrder @@ -315,6 +342,10 @@ A React Hook that will return true if the current step is complete as defined by A React Hook that will return a two element array where the first element is the current array of line items (matching the `items` prop on `Checkout`), and the second element is the current total (matching the `total` prop). Only works within [CheckoutProvider](#CheckoutProvider). +### useLineItemsOfType + +A React Hook that will return an array of line items matching a specific 'type' (i.e. 'tax'). Only works within [CheckoutProvider](#CheckoutProvider). + ### useMessages A React Hook that will return an object containing the `showErrorMessage`, `showInfoMessage`, and `showSuccessMessage` callbacks as passed to `CheckoutProvider`. Only works within [CheckoutProvider](#CheckoutProvider). From 55452824d00653b6ffc5cad5a2eb9d3fb0d64daa Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Mon, 20 Apr 2020 12:41:52 -0400 Subject: [PATCH 20/35] Update tests to reflect order summary step > checkout summary change --- .../test/composite-checkout.js | 4 +-- .../components/wp-checkout-order-summary.js | 10 +++--- .../src/components/checkout-order-summary.js | 12 +++---- .../src/components/checkout-steps.js | 34 +++++++++---------- .../src/components/loading-content.js | 8 ++--- .../composite-checkout/src/lib/line-items.js | 2 +- 6 files changed, 35 insertions(+), 35 deletions(-) diff --git a/client/my-sites/checkout/composite-checkout/test/composite-checkout.js b/client/my-sites/checkout/composite-checkout/test/composite-checkout.js index 600d162337d91..e8371956cae3f 100644 --- a/client/my-sites/checkout/composite-checkout/test/composite-checkout.js +++ b/client/my-sites/checkout/composite-checkout/test/composite-checkout.js @@ -431,13 +431,13 @@ describe( 'CompositeCheckout', () => { expect( getByText( 'ZIP code' ) ).toBeInTheDocument(); } ); - it( 'renders the checkout greeting header', async () => { + it( 'renders the checkout summary', async () => { let renderResult; await act( async () => { renderResult = render( , container ); } ); const { getByText } = renderResult; - expect( getByText( 'You are all set to check out' ) ).toBeInTheDocument(); + expect( getByText( 'Purchase Details' ) ).toBeInTheDocument(); expect( page.redirect ).not.toHaveBeenCalled(); } ); diff --git a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js index 7ff7a7ecfadb7..bb1741cdc95e5 100644 --- a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js +++ b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js @@ -19,7 +19,7 @@ export default function WPCheckoutOrderSummary() { <> { translate( 'Purchase Details' ) } - { taxes.map( tax => ( + { taxes.map( ( tax ) => ( { tax.label } { renderDisplayValueMarkdown( tax.amount.displayValue ) } @@ -35,13 +35,13 @@ export default function WPCheckoutOrderSummary() { } const CheckoutSummaryTitle = styled.h2` - color: ${props => props.theme.colors.textColor}; - font-weight: ${props => props.theme.weights.bold}; + color: ${( props ) => props.theme.colors.textColor}; + font-weight: ${( props ) => props.theme.weights.bold}; padding: 16px; `; const CheckoutSummaryAmountWrapper = styled.div` - border-top: 1px solid ${props => props.theme.colors.borderColorLight}; + border-top: 1px solid ${( props ) => props.theme.colors.borderColorLight}; padding: 16px; `; @@ -52,5 +52,5 @@ const CheckoutSummaryLineItem = styled.div` `; const CheckoutSummaryTotal = styled( CheckoutSummaryLineItem )` - font-weight: ${props => props.theme.weights.bold}; + font-weight: ${( props ) => props.theme.weights.bold}; `; diff --git a/packages/composite-checkout/src/components/checkout-order-summary.js b/packages/composite-checkout/src/components/checkout-order-summary.js index 7a4f11562492c..db484e42f096a 100644 --- a/packages/composite-checkout/src/components/checkout-order-summary.js +++ b/packages/composite-checkout/src/components/checkout-order-summary.js @@ -58,7 +58,7 @@ const CheckoutSummaryStepTitle = styled.span` `; const CheckoutSummaryStepTotal = styled.span` - font-weight: ${props => props.theme.weights.bold}; + font-weight: ${( props ) => props.theme.weights.bold}; `; export function CheckoutOrderSummary() { @@ -70,7 +70,7 @@ export function CheckoutOrderSummary() { <> { translate( 'Purchase Details' ) } - { taxes.map( tax => ( + { taxes.map( ( tax ) => ( { tax.label } { renderDisplayValueMarkdown( tax.amount.displayValue ) } @@ -86,13 +86,13 @@ export function CheckoutOrderSummary() { } const CheckoutSummaryTitle = styled.div` - color: ${props => props.theme.colors.textColor}; - font-weight: ${props => props.theme.weights.bold}; + color: ${( props ) => props.theme.colors.textColor}; + font-weight: ${( props ) => props.theme.weights.bold}; padding: 16px; `; const CheckoutSummaryAmountWrapper = styled.div` - border-top: 1px solid ${props => props.theme.colors.borderColorLight}; + border-top: 1px solid ${( props ) => props.theme.colors.borderColorLight}; padding: 16px; `; @@ -102,5 +102,5 @@ const CheckoutSummaryLineItem = styled.div` `; const CheckoutSummaryTotal = styled( CheckoutSummaryLineItem )` - font-weight: ${props => props.theme.weights.bold}; + font-weight: ${( props ) => props.theme.weights.bold}; `; diff --git a/packages/composite-checkout/src/components/checkout-steps.js b/packages/composite-checkout/src/components/checkout-steps.js index 39bb0151ca9f7..cfc5c753b1388 100644 --- a/packages/composite-checkout/src/components/checkout-steps.js +++ b/packages/composite-checkout/src/components/checkout-steps.js @@ -135,9 +135,9 @@ export function CheckoutSteps( { children, className } ) { let stepNumber = 0; let nextStepNumber = 1; - const componentChildren = React.Children.toArray( children ).filter( child => child ); - const nonSteps = componentChildren.filter( child => child.type?.name !== 'CheckoutStep' ); - const steps = componentChildren.filter( child => child.type?.name === 'CheckoutStep' ); + const componentChildren = React.Children.toArray( children ).filter( ( child ) => child ); + const nonSteps = componentChildren.filter( ( child ) => child.type?.name !== 'CheckoutStep' ); + const steps = componentChildren.filter( ( child ) => child.type?.name === 'CheckoutStep' ); const totalSteps = steps.length; const { activeStepNumber, stepCompleteStatus, setTotalSteps } = useContext( CheckoutStepDataContext @@ -163,7 +163,7 @@ export function CheckoutSteps( { children, className } ) { isLastStepActive={ ! isThereAnotherNumberedStep } > { nonSteps } - { steps.map( child => { + { steps.map( ( child ) => { stepNumber = nextStepNumber; nextStepNumber = stepNumber === totalSteps ? null : stepNumber + 1; const isStepActive = activeStepNumber === stepNumber; @@ -390,11 +390,11 @@ const MainContentUI = styled.div` flex-direction: column; width: 100%; - @media ( ${props => props.theme.breakpoints.tabletUp} ) { + @media ( ${( props ) => props.theme.breakpoints.tabletUp} ) { margin: 32px auto; } - @media ( ${props => props.theme.breakpoints.desktopUp} ) { + @media ( ${( props ) => props.theme.breakpoints.desktopUp} ) { align-items: flex-start; flex-direction: row; justify-content: center; @@ -403,19 +403,19 @@ const MainContentUI = styled.div` `; const CheckoutSummaryUI = styled.div` - background: ${props => props.theme.colors.surface}; - border-bottom: 1px solid ${props => props.theme.colors.borderColorLight}; + background: ${( props ) => props.theme.colors.surface}; + border-bottom: 1px solid ${( props ) => props.theme.colors.borderColorLight}; box-sizing: border-box; width: 100%; - @media ( ${props => props.theme.breakpoints.tabletUp} ) { - border: 1px solid ${props => props.theme.colors.borderColorLight}; + @media ( ${( props ) => props.theme.breakpoints.tabletUp} ) { + border: 1px solid ${( props ) => props.theme.colors.borderColorLight}; border-bottom: none 0; max-width: 556px; } - @media ( ${props => props.theme.breakpoints.desktopUp} ) { - border: 1px solid ${props => props.theme.colors.borderColorLight}; + @media ( ${( props ) => props.theme.breakpoints.desktopUp} ) { + border: 1px solid ${( props ) => props.theme.colors.borderColorLight}; box-sizing: border-box; margin-left: 16px; margin-right: 0; @@ -425,17 +425,17 @@ const CheckoutSummaryUI = styled.div` `; const CheckoutStepsWrapperUI = styled.div` - background: ${props => props.theme.colors.surface}; + background: ${( props ) => props.theme.colors.surface}; box-sizing: border-box; - margin-bottom: ${props => ( props.isLastStepActive ? '100px' : 0 )}; + margin-bottom: ${( props ) => ( props.isLastStepActive ? '100px' : 0) }; width: 100%; - @media ( ${props => props.theme.breakpoints.tabletUp} ) { - border: 1px solid ${props => props.theme.colors.borderColorLight}; + @media ( ${( props ) => props.theme.breakpoints.tabletUp} ) { + border: 1px solid ${( props ) => props.theme.colors.borderColorLight}; max-width: 556px; } - @media ( ${props => props.theme.breakpoints.desktopUp} ) { + @media ( ${( props ) => props.theme.breakpoints.desktopUp} ) { width: 556px; order: 1; } diff --git a/packages/composite-checkout/src/components/loading-content.js b/packages/composite-checkout/src/components/loading-content.js index 20b4e1f2ade07..c31dcd2698c7f 100644 --- a/packages/composite-checkout/src/components/loading-content.js +++ b/packages/composite-checkout/src/components/loading-content.js @@ -37,13 +37,13 @@ export default function LoadingContent() { } const LoadingContentWrapperUI = styled.div` - background: ${props => props.theme.colors.surface}; + background: ${( props ) => props.theme.colors.surface}; width: 100%; box-sizing: border-box; - margin-bottom: ${props => ( props.isLastStepActive ? '100px' : 0 )}; + margin-bottom: ${( props ) => ( props.isLastStepActive ? '100px' : 0) }; - @media ( ${props => props.theme.breakpoints.tabletUp} ) { - border: 1px solid ${props => props.theme.colors.borderColorLight}; + @media ( ${( props ) => props.theme.breakpoints.tabletUp} ) { + border: 1px solid ${( props ) => props.theme.colors.borderColorLight}; margin: 32px auto; box-sizing: border-box; max-width: 556px; diff --git a/packages/composite-checkout/src/lib/line-items.js b/packages/composite-checkout/src/lib/line-items.js index dbf5e8c6af566..68814bb79b96c 100644 --- a/packages/composite-checkout/src/lib/line-items.js +++ b/packages/composite-checkout/src/lib/line-items.js @@ -31,5 +31,5 @@ export function useLineItemsOfType( itemType = null ) { throw new Error( 'missing itemType for useLineItemsOfType' ); } const [ items ] = useLineItems(); - return items.filter( item => item.type === itemType ); + return items.filter( ( item ) => item.type === itemType ); } From 8a3bc9804aa204c49c21c35c84038aea5ca4846d Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Tue, 21 Apr 2020 09:22:49 -0400 Subject: [PATCH 21/35] Center checkout for single-column layout --- .../composite-checkout/src/components/checkout-steps.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/composite-checkout/src/components/checkout-steps.js b/packages/composite-checkout/src/components/checkout-steps.js index cfc5c753b1388..3e551b74de1af 100644 --- a/packages/composite-checkout/src/components/checkout-steps.js +++ b/packages/composite-checkout/src/components/checkout-steps.js @@ -406,6 +406,7 @@ const CheckoutSummaryUI = styled.div` background: ${( props ) => props.theme.colors.surface}; border-bottom: 1px solid ${( props ) => props.theme.colors.borderColorLight}; box-sizing: border-box; + margin: 0 auto; width: 100%; @media ( ${( props ) => props.theme.breakpoints.tabletUp} ) { @@ -427,7 +428,7 @@ const CheckoutSummaryUI = styled.div` const CheckoutStepsWrapperUI = styled.div` background: ${( props ) => props.theme.colors.surface}; box-sizing: border-box; - margin-bottom: ${( props ) => ( props.isLastStepActive ? '100px' : 0) }; + margin: 0 auto ${( props ) => ( props.isLastStepActive ? '100px' : 0) }; width: 100%; @media ( ${( props ) => props.theme.breakpoints.tabletUp} ) { @@ -436,8 +437,9 @@ const CheckoutStepsWrapperUI = styled.div` } @media ( ${( props ) => props.theme.breakpoints.desktopUp} ) { - width: 556px; + margin: 0; order: 1; + width: 556px; } `; From c37dbb3305d608d73fd79d436733810048fb1254 Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Tue, 21 Apr 2020 10:28:36 -0400 Subject: [PATCH 22/35] Tighten up order review line item styles to match design --- .../wpcom/components/wp-checkout-order-review.js | 12 ++++++++---- .../components/wp-order-review-line-items.js | 15 ++++++++------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-review.js b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-review.js index 108c8c37dda0f..f5f106aa7db4c 100644 --- a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-review.js +++ b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-review.js @@ -5,6 +5,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import styled from '@emotion/styled'; import { useLineItems, useFormStatus } from '@automattic/composite-checkout'; +import { useTranslate } from 'i18n-calypso'; /** * Internal dependencies @@ -26,6 +27,7 @@ export default function WPCheckoutOrderReview( { onChangePlanLength, siteUrl, } ) { + const translate = useTranslate(); const [ items, total ] = useLineItems(); const { formStatus } = useFormStatus(); const isPurchaseFree = total.amount.value === 0; @@ -35,7 +37,7 @@ export default function WPCheckoutOrderReview( { return (
- { domainUrl && { domainUrl } } + { domainUrl && { translate( 'Site' ) + ': ' + domainUrl } } props.theme.colors.textColorLight}; + font-size: 14px; + margin-top: -10px; word-break: break-word; `; const CouponField = styled( Coupon )` - margin: 24px 30px 24px 0; - padding-bottom: 24px; + margin: 20px 30px 20px 0; + padding-bottom: 20px; border-bottom: 1px solid ${( props ) => props.theme.colors.borderColorLight}; `; diff --git a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-order-review-line-items.js b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-order-review-line-items.js index 9ae588e2ab115..8e5fe16d46e3b 100644 --- a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-order-review-line-items.js +++ b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-order-review-line-items.js @@ -179,11 +179,10 @@ export const LineItemUI = styled( WPLineItem )` color: ${( { theme, total } ) => ( total ? theme.colors.textColorDark : theme.colors.textColor) }; font-size: ${( { total } ) => ( total ? '1.2em' : '1em') }; padding: ${( { total, isSummaryVisible, tax, subtotal } ) => - isSummaryVisible || total || subtotal || tax ? '10px 0' : '24px 0'}; + isSummaryVisible || total || subtotal || tax ? '10px 0' : '20px 0'}; border-bottom: ${( { theme, total, isSummaryVisible } ) => isSummaryVisible || total ? 0 : '1px solid ' + theme.colors.borderColorLight}; position: relative; - margin-right: ${( { total, tax, subtotal } ) => ( subtotal || total || tax ? '0' : '30px') }; `; const LineItemTitleUI = styled.div` @@ -207,7 +206,7 @@ const DeleteButton = styled( Button )` position: absolute; padding: 10px; right: -50px; - top: 10px; + top: 8px; :hover rect { fill: ${( props ) => props.theme.colors.error}; @@ -275,7 +274,7 @@ export function WPOrderReviewLineItems( { return ( { items.map( ( item ) => ( - + - + ) ) } ); @@ -310,10 +309,12 @@ WPOrderReviewLineItems.propTypes = { }; const WPOrderReviewList = styled.ul` - margin: 10px 0; + border-top: 1px solid ${( props ) => props.theme.colors.borderColorLight}; + box-sizing: border-box; + margin: 20px 30px 20px 0; `; -const WPOrderReviewListItems = styled.li` +const WPOrderReviewListItem = styled.li` margin: 0; padding: 0; display: block; From 1567c2c3d4ac121b006fdf043371f3b03d952735 Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Tue, 21 Apr 2020 14:22:57 -0400 Subject: [PATCH 23/35] Export CheckIcon and allow it to be styled --- .../src/components/shared-icons.js | 17 +++++++++-------- packages/composite-checkout/src/public-api.js | 2 ++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/composite-checkout/src/components/shared-icons.js b/packages/composite-checkout/src/components/shared-icons.js index a04dda7823973..0878d834a6411 100644 --- a/packages/composite-checkout/src/components/shared-icons.js +++ b/packages/composite-checkout/src/components/shared-icons.js @@ -3,14 +3,14 @@ */ import React from 'react'; import PropTypes from 'prop-types'; +import styled from '@emotion/styled'; export function CheckIcon( { className, id } ) { return ( - + ); } @@ -41,6 +38,10 @@ CheckIcon.propTypes = { id: PropTypes.string, }; +const CheckIconUI = styled.svg` + fill: #fff; +`; + export function ErrorIcon( { className } ) { return ( Date: Tue, 21 Apr 2020 14:24:23 -0400 Subject: [PATCH 24/35] Stub out CheckoutSummaryFeatures for initial styling --- .../components/wp-checkout-order-summary.js | 61 ++++++++++++++++++- .../src/components/checkout-order-summary.js | 4 +- .../src/components/checkout-steps.js | 5 +- 3 files changed, 62 insertions(+), 8 deletions(-) diff --git a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js index bb1741cdc95e5..30efd0ebba5f3 100644 --- a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js +++ b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js @@ -4,9 +4,10 @@ import React from 'react'; import styled from '@emotion/styled'; import { + CheckoutCheckIcon, + renderDisplayValueMarkdown, useLineItemsOfType, useTotal, - renderDisplayValueMarkdown, } from '@automattic/composite-checkout'; import { useTranslate } from 'i18n-calypso'; @@ -18,6 +19,29 @@ export default function WPCheckoutOrderSummary() { return ( <> { translate( 'Purchase Details' ) } + + + { translate( 'Included with your purchase' ) } + + + + + { translate( 'Live chat and email support' ) } + + + + { translate( 'Free custom domain for a year' ) } + + + + { translate( 'Dozens of free themes' ) } + + + + { translate( 'Money back guarantee' ) } + + + { taxes.map( ( tax ) => ( @@ -36,13 +60,44 @@ export default function WPCheckoutOrderSummary() { const CheckoutSummaryTitle = styled.h2` color: ${( props ) => props.theme.colors.textColor}; + display: none; font-weight: ${( props ) => props.theme.weights.bold}; - padding: 16px; + padding: 20px 20px 0; + + @media ( ${( props ) => props.theme.breakpoints.desktopUp} ) { + display: none; + } +`; + +const CheckoutSummaryFeatures = styled.div` + padding: 20px; +`; + +const CheckoutSummaryFeaturesTitle = styled.h3` + font-size: 16px; + font-weight: ${( props ) => props.theme.weights.normal}; + margin-bottom: 4px; +`; + +const CheckoutSummaryFeaturesList = styled.ul` + margin: 0; + list-style: none; + font-size: 14px; +`; + +const WPCheckoutCheckIcon = styled( CheckoutCheckIcon )` + fill: ${( props ) => props.theme.colors.success}; + margin-right: 4px; + vertical-align: bottom; +`; + +const CheckoutSummaryFeaturesListItem = styled.li` + margin-bottom: 4px; `; const CheckoutSummaryAmountWrapper = styled.div` border-top: 1px solid ${( props ) => props.theme.colors.borderColorLight}; - padding: 16px; + padding: 20px; `; const CheckoutSummaryLineItem = styled.div` diff --git a/packages/composite-checkout/src/components/checkout-order-summary.js b/packages/composite-checkout/src/components/checkout-order-summary.js index db484e42f096a..bcefb54d15013 100644 --- a/packages/composite-checkout/src/components/checkout-order-summary.js +++ b/packages/composite-checkout/src/components/checkout-order-summary.js @@ -88,12 +88,12 @@ export function CheckoutOrderSummary() { const CheckoutSummaryTitle = styled.div` color: ${( props ) => props.theme.colors.textColor}; font-weight: ${( props ) => props.theme.weights.bold}; - padding: 16px; + padding: 24px 20px; `; const CheckoutSummaryAmountWrapper = styled.div` border-top: 1px solid ${( props ) => props.theme.colors.borderColorLight}; - padding: 16px; + padding: 24px 20px; `; const CheckoutSummaryLineItem = styled.div` diff --git a/packages/composite-checkout/src/components/checkout-steps.js b/packages/composite-checkout/src/components/checkout-steps.js index 3e551b74de1af..987e7807bd4b8 100644 --- a/packages/composite-checkout/src/components/checkout-steps.js +++ b/packages/composite-checkout/src/components/checkout-steps.js @@ -417,11 +417,10 @@ const CheckoutSummaryUI = styled.div` @media ( ${( props ) => props.theme.breakpoints.desktopUp} ) { border: 1px solid ${( props ) => props.theme.colors.borderColorLight}; - box-sizing: border-box; - margin-left: 16px; + margin-left: 24px; margin-right: 0; order: 2; - width: 326px; + width: 328px; } `; From 2cf0b5a46c80531676a2092e5696df24aab1657c Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Tue, 21 Apr 2020 15:16:43 -0400 Subject: [PATCH 25/35] Hide summary on mobile --- .../wpcom/components/wp-checkout.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout.js b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout.js index 73a6e1d4c5168..a042bfa6be674 100644 --- a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout.js +++ b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout.js @@ -130,9 +130,9 @@ export default function WPCheckout( { return ( - + - + props.theme.breakpoints.desktopUp} ) { + display: block; + } +`; + function setActiveStepNumber( stepNumber ) { window.location.hash = '#step' + stepNumber; } From 28059bbaaeec53e92243866900ff44c066302565 Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Tue, 21 Apr 2020 17:18:04 -0400 Subject: [PATCH 26/35] Alphabetize --- packages/composite-checkout/README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/composite-checkout/README.md b/packages/composite-checkout/README.md index b125982be63e8..3842e66b15887 100644 --- a/packages/composite-checkout/README.md +++ b/packages/composite-checkout/README.md @@ -119,12 +119,6 @@ The main wrapper component for the checkout form. It has the following props. - `className?: string`. The className for the component. -### CheckoutSummary - -Renders its `children` prop and acts as a wrapper to flow outside of the [`CheckoutSteps`](#CehckoutSteps) wrapper (floated on desktop, collapsed on mobile). It has the following props. - -- `className?: string`. The className for the component. - ### CheckoutProvider Renders its `children` prop and acts as a React Context provider. All of checkout should be wrapped in this. @@ -146,6 +140,10 @@ It has the following props. The line items are for display purposes only. They should also include subtotals, discounts, and taxes. No math will be performed on the line items. Instead, the amount to be charged will be specified by the required prop `total`, which is another line item. +### CheckoutReviewOrder + +Renders a list of the line items and their `displayValue` properties followed by the `total` line item, and whatever `submitButton` is in the current payment method. + ## CheckoutStep A checkout step. This should be a direct child of [CheckoutSteps](#CheckoutSteps) and is itself a wrapper for [CheckoutStepBody](#CheckoutStepBody). If you want to make something that looks like a step but is not connected to other steps, use a [CheckoutStepBody](#CheckoutStepBody) instead. @@ -194,9 +192,11 @@ A component that looks like a checkout step. Normally you don't need to use this A wrapper for [CheckoutStep](#CheckoutStep) and [CheckoutStepBody](#CheckoutStepBody) objects that will connect the steps and provide a way to switch between them. Should be a direct child of [Checkout](#Checkout). -### CheckoutReviewOrder +### CheckoutSummary -Renders a list of the line items and their `displayValue` properties followed by the `total` line item, and whatever `submitButton` is in the current payment method. +Renders its `children` prop and acts as a wrapper to flow outside of the [`CheckoutSteps`](#CehckoutSteps) wrapper (floated on desktop, collapsed on mobile). It has the following props. + +- `className?: string`. The className for the component. ### OrderReviewLineItems From ad0d7e9eff7e97f98f8a2e8aad27c21515ba891d Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Tue, 21 Apr 2020 17:19:33 -0400 Subject: [PATCH 27/35] Improve docs for useLineItemsOfType --- packages/composite-checkout/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/composite-checkout/README.md b/packages/composite-checkout/README.md index 3842e66b15887..e1a2db1c390ed 100644 --- a/packages/composite-checkout/README.md +++ b/packages/composite-checkout/README.md @@ -344,7 +344,7 @@ A React Hook that will return a two element array where the first element is the ### useLineItemsOfType -A React Hook that will return an array of line items matching a specific 'type' (i.e. 'tax'). Only works within [CheckoutProvider](#CheckoutProvider). +A React Hook taking one string argument that will return an array of [line items](#line-items) from the cart (derived from the same data returned by [useLineItems](#useLineItems)) whose `type` property matches that string. Only works within [CheckoutProvider](#CheckoutProvider). ### useMessages From b1891e45d3a8a92d8867c68eae51d30c75d9e813 Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Tue, 21 Apr 2020 17:22:41 -0400 Subject: [PATCH 28/35] Provide domainUrl as replacement string in translation --- .../wpcom/components/wp-checkout-order-review.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-review.js b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-review.js index f5f106aa7db4c..0ef15662ea7de 100644 --- a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-review.js +++ b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-review.js @@ -37,7 +37,7 @@ export default function WPCheckoutOrderReview( { return (
- { domainUrl && { translate( 'Site' ) + ': ' + domainUrl } } + { domainUrl && { translate( 'Site: %s', { args: domainUrl } ) } } Date: Tue, 21 Apr 2020 17:24:01 -0400 Subject: [PATCH 29/35] Remove default value on useLineItemsOfType hook --- packages/composite-checkout/src/lib/line-items.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/composite-checkout/src/lib/line-items.js b/packages/composite-checkout/src/lib/line-items.js index 68814bb79b96c..24326506d34b7 100644 --- a/packages/composite-checkout/src/lib/line-items.js +++ b/packages/composite-checkout/src/lib/line-items.js @@ -26,7 +26,7 @@ export function useTotal() { return total; } -export function useLineItemsOfType( itemType = null ) { +export function useLineItemsOfType( itemType ) { if ( ! itemType ) { throw new Error( 'missing itemType for useLineItemsOfType' ); } From a53b0337c1cc7f049932b1b2991b9d6ae174f300 Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Tue, 21 Apr 2020 17:44:34 -0400 Subject: [PATCH 30/35] Update docs to reflect moving the form structure to CheckoutSteps --- packages/composite-checkout/README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/composite-checkout/README.md b/packages/composite-checkout/README.md index e1a2db1c390ed..3c3fe8cdc4364 100644 --- a/packages/composite-checkout/README.md +++ b/packages/composite-checkout/README.md @@ -12,7 +12,7 @@ Once published, you'll be able to install this package using npm with: ## Description -This package provides a context provider, `CheckoutProvider`, and a default component, `Checkout`, which creates a checkout form. +This package provides a context provider, `CheckoutProvider`, a default component, `Checkout`, and the `CheckoutSteps` component which creates a checkout form. The form has two default steps: @@ -47,10 +47,11 @@ Any component which is a child of `CheckoutProvider` gets access to the followin - [useSelect](#useSelect) - [useTotal](#useTotal) -The [Checkout](#checkout) component creates the form itself. Within the component you can render any children to create the checkout experience, but a few components are provided to make this easier: - - [CheckoutSummary](#CheckoutSummary) can be used to render a summary that, by default, floats beside the checkout steps on larger screens and collapses behind a toggle at the top of smaller screens. - - [CheckoutStepBody](#CheckoutStepBody) can be used to render something that looks like a checkout step. A series of these can be used to create a semantic form. - - [CheckoutSteps](#CheckoutSteps) with [CheckoutStep](#CheckoutStep) children can be used to create a series of steps that are joined by "Continue" buttons which are hidden and displayed as needed. +The [Checkout](#checkout) component creates a wrapper for Checkout. Within the component you can render any children to create the checkout experience, but a few components are provided to make this easier: + - [CheckoutSummary](#CheckoutSummary) (optional) can be used to render a summary that, by default, floats beside the checkout steps on larger screens and collapses behind a toggle at the top of smaller screens. + - [CheckoutSteps](#CheckoutSteps) (required) creates the Checkout form itself. This includes the Checkout submit button and logic to handle step progression. + - [CheckoutStep](#CheckoutStep) (optional) children of `CheckoutSteps` can be used to create a series of steps that are joined by "Continue" buttons which are hidden and displayed as needed. + - [CheckoutStepBody](#CheckoutStepBody) (optional) can be used to render something that looks like a checkout step. A series of these can be used to create a semantic form. Each `CheckoutStep` has an `isCompleteCallback` prop, which will be called when the "Continue" button is pressed. It can perform validation on that step's contents to determine if the form should continue to the next step. If the function returns true, the form continues to the next step, otherwise it remains on the same step. If the function returns a `Promise`, then the "Continue" button will change to "Please wait…" until the Promise resolves allowing for async operations. The value resolved by the Promise must be a boolean; true to continue, false to stay on the current step. @@ -115,7 +116,7 @@ While the `Checkout` component takes care of most everything, there are many sit ### Checkout -The main wrapper component for the checkout form. It has the following props. +The main wrapper component for Checkout. It has the following props. - `className?: string`. The className for the component. @@ -190,7 +191,7 @@ A component that looks like a checkout step. Normally you don't need to use this ## CheckoutSteps -A wrapper for [CheckoutStep](#CheckoutStep) and [CheckoutStepBody](#CheckoutStepBody) objects that will connect the steps and provide a way to switch between them. Should be a direct child of [Checkout](#Checkout). +Creates the Checkout form and provides a wrapper for [CheckoutStep](#CheckoutStep) and [CheckoutStepBody](#CheckoutStepBody) objects that will connect the steps and provide a way to switch between them. Should be a direct child of [Checkout](#Checkout). ### CheckoutSummary @@ -340,7 +341,7 @@ A React Hook that will return true if the current step is complete as defined by ### useLineItems -A React Hook that will return a two element array where the first element is the current array of line items (matching the `items` prop on `Checkout`), and the second element is the current total (matching the `total` prop). Only works within [CheckoutProvider](#CheckoutProvider). +A React Hook that will return a two element array where the first element is the current array of line items (matching the `items` from the `CheckoutProvider`), and the second element is the current total (matching the `total` from the `CheckoutProvider`). Only works within [CheckoutProvider](#CheckoutProvider). ### useLineItemsOfType From 0ba584f247254404ab03ba65424c6000067cbdbd Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Wed, 22 Apr 2020 12:24:57 -0400 Subject: [PATCH 31/35] Add CheckoutStepArea component - adds the component and shifts the step wrapper styles to it - removes CheckoutSteps children splitting logic - moves CheckoutSteps (and CheckoutStep children) and CheckoutStepBody to new component in default Checkout --- .../wpcom/components/wp-checkout.js | 137 +++++++++--------- .../src/components/checkout-steps.js | 121 ++++++++-------- packages/composite-checkout/src/public-api.js | 4 +- 3 files changed, 137 insertions(+), 125 deletions(-) diff --git a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout.js b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout.js index a042bfa6be674..cc39770baa8e5 100644 --- a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout.js +++ b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout.js @@ -6,8 +6,9 @@ import { useTranslate } from 'i18n-calypso'; import styled from '@emotion/styled'; import { Checkout, - CheckoutSteps, CheckoutStep, + CheckoutStepArea, + CheckoutSteps, CheckoutSummary, getDefaultPaymentMethodStep, useIsStepActive, @@ -133,82 +134,86 @@ export default function WPCheckout( { - - true } - activeStepContent={ - - } - titleContent={ } - completeStepContent={ } - editButtonText={ translate( 'Edit' ) } - editButtonAriaLabel={ translate( 'Edit the payment method' ) } - nextStepButtonText={ translate( 'Continue' ) } - nextStepButtonAriaLabel={ translate( 'Continue with the selected payment method' ) } - validatingButtonText={ translate( 'Please wait…' ) } - validatingButtonAriaLabel={ translate( 'Please wait…' ) } - /> - { shouldShowContactStep && ( + + { - setShouldShowContactDetailsValidationErrors( true ); - return contactValidationCallback(); - } } + stepId="review-order-step" + isCompleteCallback={ () => true } activeStepContent={ - } - completeStepContent={ } - titleContent={ } + titleContent={ } + completeStepContent={ } editButtonText={ translate( 'Edit' ) } - editButtonAriaLabel={ translate( 'Edit the contact details' ) } + editButtonAriaLabel={ translate( 'Edit the payment method' ) } nextStepButtonText={ translate( 'Continue' ) } - nextStepButtonAriaLabel={ translate( 'Continue with the entered contact details' ) } + nextStepButtonAriaLabel={ translate( 'Continue with the selected payment method' ) } validatingButtonText={ translate( 'Please wait…' ) } validatingButtonAriaLabel={ translate( 'Please wait…' ) } /> - ) } - { + setShouldShowContactDetailsValidationErrors( true ); + return contactValidationCallback(); + } } + activeStepContent={ + + } + completeStepContent={ + + } + titleContent={ } + editButtonText={ translate( 'Edit' ) } + editButtonAriaLabel={ translate( 'Edit the contact details' ) } + nextStepButtonText={ translate( 'Continue' ) } + nextStepButtonAriaLabel={ translate( 'Continue with the entered contact details' ) } + validatingButtonText={ translate( 'Please wait…' ) } + validatingButtonAriaLabel={ translate( 'Please wait…' ) } /> - } - completeStepContent={ paymentMethodStep.completeStepContent } - titleContent={ paymentMethodStep.titleContent } - editButtonText={ translate( 'Edit' ) } - editButtonAriaLabel={ translate( 'Edit the payment method' ) } - nextStepButtonText={ translate( 'Continue' ) } - nextStepButtonAriaLabel={ translate( 'Continue with the selected payment method' ) } - validatingButtonText={ translate( 'Please wait…' ) } - validatingButtonAriaLabel={ translate( 'Please wait…' ) } - /> - + ) } + + } + completeStepContent={ paymentMethodStep.completeStepContent } + titleContent={ paymentMethodStep.titleContent } + editButtonText={ translate( 'Edit' ) } + editButtonAriaLabel={ translate( 'Edit the payment method' ) } + nextStepButtonText={ translate( 'Continue' ) } + nextStepButtonAriaLabel={ translate( 'Continue with the selected payment method' ) } + validatingButtonText={ translate( 'Please wait…' ) } + validatingButtonAriaLabel={ translate( 'Please wait…' ) } + /> + + ); } diff --git a/packages/composite-checkout/src/components/checkout-steps.js b/packages/composite-checkout/src/components/checkout-steps.js index 987e7807bd4b8..b1a3f5111a96b 100644 --- a/packages/composite-checkout/src/components/checkout-steps.js +++ b/packages/composite-checkout/src/components/checkout-steps.js @@ -88,7 +88,7 @@ function DefaultCheckoutSteps() { { orderSummary.summaryContent } - + - true } - activeStepContent={ reviewOrderStep.activeStepContent } - completeStepContent={ reviewOrderStep.completeStepContent } - titleContent={ reviewOrderStep.titleContent } - className={ reviewOrderStep.className } - /> - - + + true } + activeStepContent={ reviewOrderStep.activeStepContent } + completeStepContent={ reviewOrderStep.completeStepContent } + titleContent={ reviewOrderStep.titleContent } + className={ reviewOrderStep.className } + /> + + + ); } @@ -129,20 +131,39 @@ export function CheckoutSummary( { children, className } ) { ); } -export function CheckoutSteps( { children, className } ) { +export function CheckoutStepArea( { children, className } ) { const localize = useLocalize(); const { formStatus } = useFormStatus(); + const { activeStepNumber, totalSteps } = useContext( CheckoutStepDataContext ); + const actualActiveStepNumber = + activeStepNumber > totalSteps && totalSteps > 0 ? totalSteps : activeStepNumber; + const isThereAnotherNumberedStep = actualActiveStepNumber < totalSteps; + + return ( + + { children } + + + + + + + + ); +} + +export function CheckoutSteps( { children } ) { let stepNumber = 0; let nextStepNumber = 1; - const componentChildren = React.Children.toArray( children ).filter( ( child ) => child ); - const nonSteps = componentChildren.filter( ( child ) => child.type?.name !== 'CheckoutStep' ); - const steps = componentChildren.filter( ( child ) => child.type?.name === 'CheckoutStep' ); + + const steps = React.Children.toArray( children ).filter( ( child ) => child ); const totalSteps = steps.length; const { activeStepNumber, stepCompleteStatus, setTotalSteps } = useContext( CheckoutStepDataContext ); - const isThereAnotherNumberedStep = activeStepNumber < totalSteps; useEffect( () => { setTotalSteps( totalSteps ); @@ -157,41 +178,25 @@ export function CheckoutSteps( { children, className } ) { totalSteps ); - return ( - - { nonSteps } - { steps.map( ( child ) => { - stepNumber = nextStepNumber; - nextStepNumber = stepNumber === totalSteps ? null : stepNumber + 1; - const isStepActive = activeStepNumber === stepNumber; - const isStepComplete = !! stepCompleteStatus[ stepNumber ]; - return ( - - { child } - - ); - } ) } - - - - - - - - ); + return steps.map( ( child ) => { + stepNumber = nextStepNumber; + nextStepNumber = stepNumber === totalSteps ? null : stepNumber + 1; + const isStepActive = activeStepNumber === stepNumber; + const isStepComplete = !! stepCompleteStatus[ stepNumber ]; + return ( + + { child } + + ); + } ); } export function CheckoutStep( { @@ -424,7 +429,7 @@ const CheckoutSummaryUI = styled.div` } `; -const CheckoutStepsWrapperUI = styled.div` +const CheckoutStepAreaUI = styled.div` background: ${( props ) => props.theme.colors.surface}; box-sizing: border-box; margin: 0 auto ${( props ) => ( props.isLastStepActive ? '100px' : 0) }; diff --git a/packages/composite-checkout/src/public-api.js b/packages/composite-checkout/src/public-api.js index f3ba7ffc28d76..36eb3f8d82283 100644 --- a/packages/composite-checkout/src/public-api.js +++ b/packages/composite-checkout/src/public-api.js @@ -3,10 +3,11 @@ */ import { CheckoutProvider, useEvents, useMessages } from './components/checkout-provider'; import { - CheckoutSteps, Checkout, CheckoutStep, + CheckoutStepArea, CheckoutStepBody, + CheckoutSteps, CheckoutSummary, useIsStepActive, useIsStepComplete, @@ -63,6 +64,7 @@ export { CheckoutPaymentMethods, CheckoutProvider, CheckoutStep, + CheckoutStepArea, CheckoutStepBody, CheckoutSteps, CheckoutSummary, From a7ac92c70754582c7a0d4a12a3fcefdee31a11d4 Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Wed, 22 Apr 2020 12:41:28 -0400 Subject: [PATCH 32/35] Update docs and demo --- packages/composite-checkout/README.md | 13 ++-- packages/composite-checkout/demo/index.js | 73 ++++++++++++----------- 2 files changed, 47 insertions(+), 39 deletions(-) diff --git a/packages/composite-checkout/README.md b/packages/composite-checkout/README.md index 3c3fe8cdc4364..f6965eb582dc4 100644 --- a/packages/composite-checkout/README.md +++ b/packages/composite-checkout/README.md @@ -12,7 +12,7 @@ Once published, you'll be able to install this package using npm with: ## Description -This package provides a context provider, `CheckoutProvider`, a default component, `Checkout`, and the `CheckoutSteps` component which creates a checkout form. +This package provides a context provider, `CheckoutProvider`, a default component, `Checkout`, and the `CheckoutStepArea` component which creates a checkout form. The form has two default steps: @@ -49,9 +49,10 @@ Any component which is a child of `CheckoutProvider` gets access to the followin The [Checkout](#checkout) component creates a wrapper for Checkout. Within the component you can render any children to create the checkout experience, but a few components are provided to make this easier: - [CheckoutSummary](#CheckoutSummary) (optional) can be used to render a summary that, by default, floats beside the checkout steps on larger screens and collapses behind a toggle at the top of smaller screens. - - [CheckoutSteps](#CheckoutSteps) (required) creates the Checkout form itself. This includes the Checkout submit button and logic to handle step progression. - - [CheckoutStep](#CheckoutStep) (optional) children of `CheckoutSteps` can be used to create a series of steps that are joined by "Continue" buttons which are hidden and displayed as needed. + - [CheckoutStepArea](#CheckoutStepArea) (required) supplies a styled wrapper for the CheckoutStepBody and CheckoutStep components, and creates the Checkout form itself with a submit button. - [CheckoutStepBody](#CheckoutStepBody) (optional) can be used to render something that looks like a checkout step. A series of these can be used to create a semantic form. + - [CheckoutSteps](#CheckoutSteps) (with [CheckoutStep](#CheckoutStep) children) can be used to create a series of steps that are joined by "Continue" buttons which are hidden and displayed as needed. + - [CheckoutStep](#CheckoutStep) (optional) children of `CheckoutSteps` can be used to create a series of steps that are joined by "Continue" buttons which are hidden and displayed as needed. Each `CheckoutStep` has an `isCompleteCallback` prop, which will be called when the "Continue" button is pressed. It can perform validation on that step's contents to determine if the form should continue to the next step. If the function returns true, the form continues to the next step, otherwise it remains on the same step. If the function returns a `Promise`, then the "Continue" button will change to "Please wait…" until the Promise resolves allowing for async operations. The value resolved by the Promise must be a boolean; true to continue, false to stay on the current step. @@ -164,6 +165,10 @@ This component's props are: - `validatingButtonText?: string`. Used in place of "Please wait…" on the next step button when `isCompleteCallback` returns an unresolved Promise. - `validatingButtonAriaLabel:? string`. Used for the `aria-label` attribute on the next step button when `isCompleteCallback` returns an unresolved Promise. +## CheckoutArea + +Creates the Checkout form and provides a wrapper for [CheckoutStep](#CheckoutStep) and [CheckoutStepBody](#CheckoutStepBody) objects. Should be a direct child of [Checkout](#Checkout). + ## CheckoutStepBody A component that looks like a checkout step. Normally you don't need to use this directly, since [CheckoutStep](#CheckoutStep) creates this for you, but you can use it manually if you wish. @@ -191,7 +196,7 @@ A component that looks like a checkout step. Normally you don't need to use this ## CheckoutSteps -Creates the Checkout form and provides a wrapper for [CheckoutStep](#CheckoutStep) and [CheckoutStepBody](#CheckoutStepBody) objects that will connect the steps and provide a way to switch between them. Should be a direct child of [Checkout](#Checkout). +A wrapper for [CheckoutStep](#CheckoutStep) objects that will connect the steps and provide a way to switch between them. Should be a direct child of [Checkout](#Checkout). ### CheckoutSummary diff --git a/packages/composite-checkout/demo/index.js b/packages/composite-checkout/demo/index.js index 84c4ec67e5100..c7fe8049a311e 100644 --- a/packages/composite-checkout/demo/index.js +++ b/packages/composite-checkout/demo/index.js @@ -9,10 +9,11 @@ import styled from '@emotion/styled'; import ReactDOM from 'react-dom'; import { Checkout, - CheckoutSummary, - CheckoutSteps, + CheckoutStepArea, CheckoutStep, CheckoutStepBody, + CheckoutSteps, + CheckoutSummary, CheckoutProvider, createApplePayMethod, createPayPalMethod, @@ -400,7 +401,7 @@ function MyCheckoutBody() { { orderSummary.summaryContent } - + - true } - activeStepContent={ reviewOrderStep.activeStepContent } - completeStepContent={ reviewOrderStep.completeStepContent } - titleContent={ reviewOrderStep.titleContent } - /> - - new Promise( ( resolve ) => - setTimeout( () => { - if ( country.length === 0 ) { - showError( 'The country field is required' ); - resolve( false ); - return; - } - resolve( true ); - }, 1500 ) - ) - } - activeStepContent={ contactFormStep.activeStepContent } - completeStepContent={ contactFormStep.completeStepContent } - titleContent={ contactFormStep.titleContent } - /> - - + + true } + activeStepContent={ reviewOrderStep.activeStepContent } + completeStepContent={ reviewOrderStep.completeStepContent } + titleContent={ reviewOrderStep.titleContent } + /> + + new Promise( ( resolve ) => + setTimeout( () => { + if ( country.length === 0 ) { + showError( 'The country field is required' ); + resolve( false ); + return; + } + resolve( true ); + }, 1500 ) + ) + } + activeStepContent={ contactFormStep.activeStepContent } + completeStepContent={ contactFormStep.completeStepContent } + titleContent={ contactFormStep.titleContent } + /> + + + ); } From e3cd44b68b88cbd84ca15464f65ab72c77661f50 Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Wed, 22 Apr 2020 14:07:02 -0400 Subject: [PATCH 33/35] Update package tests --- packages/composite-checkout/test/checkout.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/composite-checkout/test/checkout.js b/packages/composite-checkout/test/checkout.js index 2b8644aa60930..5a4106a54afd6 100644 --- a/packages/composite-checkout/test/checkout.js +++ b/packages/composite-checkout/test/checkout.js @@ -18,9 +18,10 @@ import '@testing-library/jest-dom/extend-expect'; import { Checkout, CheckoutProvider, - CheckoutSteps, CheckoutStep, + CheckoutStepArea, CheckoutStepBody, + CheckoutSteps, useSelect, useDispatch, useFormStatus, @@ -640,7 +641,9 @@ function createStepsFromStepObjects( stepObjects, paymentData ) { return ( { stepObjectsWithoutStepNumber.map( createStepFromStepObject ) } - { stepObjectsWithStepNumber.map( createStepFromStepObject ) } + + { stepObjectsWithStepNumber.map( createStepFromStepObject ) } + ); } From 8c82195509f345dfc3d89c5745a4eaa6b64694b7 Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Wed, 22 Apr 2020 16:28:51 -0400 Subject: [PATCH 34/35] Update product features defaults --- .../wpcom/components/wp-checkout-order-summary.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js index 30efd0ebba5f3..81cf48691f9ea 100644 --- a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js +++ b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js @@ -28,14 +28,6 @@ export default function WPCheckoutOrderSummary() { { translate( 'Live chat and email support' ) } - - - { translate( 'Free custom domain for a year' ) } - - - - { translate( 'Dozens of free themes' ) } - { translate( 'Money back guarantee' ) } From 7a4d9bb3465d5ae207e3161120ab94afd2fc54a3 Mon Sep 17 00:00:00 2001 From: Michael Cain Date: Wed, 22 Apr 2020 16:42:28 -0400 Subject: [PATCH 35/35] Use translated string --- .../wpcom/components/wp-checkout-order-summary.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js index 81cf48691f9ea..6cc3ccdfcdae1 100644 --- a/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js +++ b/client/my-sites/checkout/composite-checkout/wpcom/components/wp-checkout-order-summary.js @@ -26,7 +26,7 @@ export default function WPCheckoutOrderSummary() { - { translate( 'Live chat and email support' ) } + { translate( 'Email and live chat support' ) }