Skip to content

Commit

Permalink
Merge pull request #6002 from guardian/generic-checkout-design
Browse files Browse the repository at this point in the history
Add legends and secure checkout SVG to checkout
  • Loading branch information
jamesgorrie committed May 15, 2024
2 parents ccb1661 + 61e92f9 commit 8261242
Showing 1 changed file with 163 additions and 143 deletions.
306 changes: 163 additions & 143 deletions support-frontend/assets/pages/[countryGroupId]/checkout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
space,
textSans,
until,
visuallyHidden,
} from '@guardian/source-foundations';
import {
Column,
Expand Down Expand Up @@ -133,24 +132,25 @@ const legend = css`
${from.tablet} {
font-size: 28px;
}
display: flex;
width: 100%;
justify-content: space-between;
`;

const personalDetailsFieldGroupStyles = (hideDetailsHeading?: boolean) => css`
const fieldset = css`
position: relative;
margin-top: ${hideDetailsHeading ? `${space[4]}px` : '0'};
& > *:not(:first-of-type) {
margin-top: ${space[3]}px;
}
${from.tablet} {
& > *:not(:first-of-type) {
margin-top: ${space[4]}px;
}
}
`;
const personalDetailsHeader = css`
${visuallyHidden};
`;

const paymentMethodSelected = css`
box-shadow: inset 0 0 0 2px ${textInputThemeDefault.textInput.borderActive};
Expand Down Expand Up @@ -666,6 +666,7 @@ function CheckoutComponent({ geoId }: Props) {
setIsProcessingPayment(false);
}
};

return (
<PageScaffold
header={<Header></Header>}
Expand Down Expand Up @@ -730,8 +731,8 @@ function CheckoutComponent({ geoId }: Props) {
>
<Box cssOverrides={shorterBoxMargin}>
<BoxContents>
<div css={personalDetailsFieldGroupStyles(true)}>
<h2 css={personalDetailsHeader}>Your details</h2>
<fieldset css={fieldset}>
<legend css={legend}>1. Your details</legend>
<div>
<TextInput
id="email"
Expand Down Expand Up @@ -858,7 +859,7 @@ function CheckoutComponent({ geoId }: Props) {
/>
</div>
)}
</div>
</fieldset>

<CheckoutDivider spacing="loose" />

Expand All @@ -876,7 +877,9 @@ function CheckoutComponent({ geoId }: Props) {
{productDescription.deliverableTo && (
<>
<fieldset>
<h2 css={legend}>Where should we deliver to?</h2>
<legend css={legend}>
Where should we deliver to?
</legend>
<AddressFields
scope={'delivery'}
lineOne={deliveryLineOne}
Expand Down Expand Up @@ -928,7 +931,9 @@ function CheckoutComponent({ geoId }: Props) {
}}
/>
</fieldset>

<CheckoutDivider spacing="loose" />

<RadioGroup
label="Is the billing address the same as the delivery address?"
hideLabel
Expand Down Expand Up @@ -964,10 +969,12 @@ function CheckoutComponent({ geoId }: Props) {
}}
/>
</RadioGroup>

<CheckoutDivider spacing="loose" />

{!billingAddressMatchesDelivery && (
<fieldset>
<h2 css={legend}>Your billing address</h2>
<legend css={legend}>Your billing address</legend>
<AddressFields
scope={'billing'}
lineOne={billingLineOne}
Expand Down Expand Up @@ -1023,148 +1030,161 @@ function CheckoutComponent({ geoId }: Props) {
)}
</>
)}

<RadioGroup>
{validPaymentMethods.map((validPaymentMethod) => {
const selected = paymentMethod === validPaymentMethod;
const { label, icon } =
paymentMethodData[validPaymentMethod];
return (
<div
css={
selected
? paymentMethodSelected
: paymentMethodNotSelected
}
>
<fieldset css={fieldset}>
<legend css={legend}>
2. Payment method
<SecureTransactionIndicator
hideText={true}
cssOverrides={css``}
/>
</legend>

<RadioGroup>
{validPaymentMethods.map((validPaymentMethod) => {
const selected = paymentMethod === validPaymentMethod;
const { label, icon } =
paymentMethodData[validPaymentMethod];
return (
<div
css={[
paymentMethodRadioWithImage,
css={
selected
? paymentMethodRadioWithImageSelected
: undefined,
]}
? paymentMethodSelected
: paymentMethodNotSelected
}
>
<Radio
label={label}
name="paymentMethod"
value={validPaymentMethod}
onChange={() => {
setPaymentMethod(validPaymentMethod);
}}
/>
<div>{icon}</div>
</div>
{validPaymentMethod === 'Stripe' && selected && (
<div css={paymentMethodBody}>
<input
type="hidden"
name="recaptchaToken"
value={recaptchaToken}
/>
<StripeCardForm
onCardNumberChange={() => {
// no-op
}}
onExpiryChange={() => {
// no-op
}}
onCvcChange={() => {
// no-op
<div
css={[
paymentMethodRadioWithImage,
selected
? paymentMethodRadioWithImageSelected
: undefined,
]}
>
<Radio
label={label}
name="paymentMethod"
value={validPaymentMethod}
onChange={() => {
setPaymentMethod(validPaymentMethod);
}}
errors={{}}
recaptcha={
<Recaptcha
// We could change the parents type to Promise and uses await here, but that has
// a lot of refactoring with not too much gain
onRecaptchaCompleted={(token) => {
setStripeClientSecretInProgress(true);
setRecaptchaToken(token);
void fetch(
'/stripe/create-setup-intent/recaptcha',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
/>
<div>{icon}</div>
</div>
{validPaymentMethod === 'Stripe' && selected && (
<div css={paymentMethodBody}>
<input
type="hidden"
name="recaptchaToken"
value={recaptchaToken}
/>
<StripeCardForm
onCardNumberChange={() => {
// no-op
}}
onExpiryChange={() => {
// no-op
}}
onCvcChange={() => {
// no-op
}}
errors={{}}
recaptcha={
<Recaptcha
// We could change the parents type to Promise and uses await here, but that has
// a lot of refactoring with not too much gain
onRecaptchaCompleted={(token) => {
setStripeClientSecretInProgress(true);
setRecaptchaToken(token);
void fetch(
'/stripe/create-setup-intent/recaptcha',
{
method: 'POST',
headers: {
'Content-Type':
'application/json',
},
body: JSON.stringify({
isTestUser,
stripePublicKey,
token,
}),
},
body: JSON.stringify({
isTestUser,
stripePublicKey,
token,
}),
},
)
.then((resp) => resp.json())
.then((json) => {
setStripeClientSecret(
(json as Record<string, string>)
.client_secret,
);
setStripeClientSecretInProgress(
false,
);
});
)
.then((resp) => resp.json())
.then((json) => {
setStripeClientSecret(
(json as Record<string, string>)
.client_secret,
);
setStripeClientSecretInProgress(
false,
);
});
}}
onRecaptchaExpired={() => {
// no-op
}}
/>
}
/>
</div>
)}

{validPaymentMethod === 'DirectDebit' &&
selected && (
<div
css={css`
padding: ${space[5]}px ${space[3]}px
${space[6]}px;
`}
>
<DirectDebitForm
countryGroupId={countryGroupId}
accountHolderName={accountHolderName}
accountNumber={accountNumber}
accountHolderConfirmation={
accountHolderConfirmation
}
sortCode={sortCode}
recaptchaCompleted={false}
updateAccountHolderName={(name: string) => {
setAccountHolderName(name);
}}
onRecaptchaExpired={() => {
// no-op
updateAccountNumber={(number: string) => {
setAccountNumber(number);
}}
/>
}
/>
</div>
)}

{validPaymentMethod === 'DirectDebit' && selected && (
<div
css={css`
padding: ${space[5]}px ${space[3]}px
${space[6]}px;
`}
>
<DirectDebitForm
countryGroupId={countryGroupId}
accountHolderName={accountHolderName}
accountNumber={accountNumber}
accountHolderConfirmation={
accountHolderConfirmation
}
sortCode={sortCode}
recaptchaCompleted={false}
updateAccountHolderName={(name: string) => {
setAccountHolderName(name);
}}
updateAccountNumber={(number: string) => {
setAccountNumber(number);
}}
updateSortCode={(sortCode: string) => {
setSortCode(sortCode);
}}
updateAccountHolderConfirmation={(
confirmation: boolean,
) => {
setAccountHolderConfirmation(confirmation);
}}
recaptcha={
<Recaptcha
// We could change the parents type to Promise and uses await here, but that has
// a lot of refactoring with not too much gain
onRecaptchaCompleted={(token) => {
setRecaptchaToken(token);
updateSortCode={(sortCode: string) => {
setSortCode(sortCode);
}}
onRecaptchaExpired={() => {
// no-op
updateAccountHolderConfirmation={(
confirmation: boolean,
) => {
setAccountHolderConfirmation(
confirmation,
);
}}
recaptcha={
<Recaptcha
// We could change the parents type to Promise and uses await here, but that has
// a lot of refactoring with not too much gain
onRecaptchaCompleted={(token) => {
setRecaptchaToken(token);
}}
onRecaptchaExpired={() => {
// no-op
}}
/>
}
formError={''}
errors={{}}
/>
}
formError={''}
errors={{}}
/>
</div>
)}
</div>
);
})}
</RadioGroup>
</div>
)}
</div>
);
})}
</RadioGroup>
</fieldset>
</BoxContents>
</Box>
<div
Expand Down

0 comments on commit 8261242

Please sign in to comment.