fix: responsivity of fields and order of signup form

This commit is contained in:
Christel Westerberg
2024-11-18 15:07:23 +01:00
parent 9094b08345
commit d5c6b6809c
17 changed files with 264 additions and 262 deletions

View File

@@ -3,12 +3,14 @@
align-self: flex-start;
display: grid;
gap: var(--Spacing-x2);
container-name: addressContainer;
container-type: inline-size;
}
.container {
display: grid;
gap: var(--Spacing-x2);
grid-template-columns: max(164px) 1fr;
grid-template-columns: minmax(100px, 164px) 1fr;
}
@media (min-width: 768px) {
@@ -16,3 +18,9 @@
display: none;
}
}
@container addressContainer (max-width: 350px) {
.container {
grid-template-columns: 1fr;
}
}

View File

@@ -48,7 +48,6 @@ export default function SignupForm({ link, subtitle, title }: SignUpFormProps) {
zipCode: "",
},
password: "",
termsAccepted: false,
},
mode: "all",
criteriaMode: "all",

View File

@@ -4,14 +4,8 @@ import { useEffect, useState } from "react"
import { useWatch } from "react-hook-form"
import { useIntl } from "react-intl"
import { privacyPolicy } from "@/constants/currentWebHrefs"
import Checkbox from "@/components/TempDesignSystem/Form/Checkbox"
import DateSelect from "@/components/TempDesignSystem/Form/Date"
import Input from "@/components/TempDesignSystem/Form/Input"
import JoinScandicFriendsCard from "@/components/TempDesignSystem/Form/JoinScandicFriendsCard"
import Link from "@/components/TempDesignSystem/Link"
import Body from "@/components/TempDesignSystem/Text/Body"
import Caption from "@/components/TempDesignSystem/Text/Caption"
import useLang from "@/hooks/useLang"
@@ -31,49 +25,27 @@ export default function Signup({ name }: { name: string }) {
setIsJoinChecked(joinValue)
}, [joinValue])
return (
<div className={styles.container}>
<JoinScandicFriendsCard
name={name}
difference={{ price: 1000, currency: "SEK" }}
/>
{isJoinChecked ? (
return isJoinChecked ? (
<div className={styles.additionalFormData}>
<Input
name="zipCode"
label={intl.formatMessage({ id: "Zip code" })}
registerOptions={{ required: true }}
/>
<div className={styles.dateField}>
<header>
<Caption type="bold">
{intl.formatMessage({ id: "Birth date" })} *
</Caption>
</header>
<DateSelect
name="dateOfBirth"
registerOptions={{ required: true }}
/>
<DateSelect name="dateOfBirth" registerOptions={{ required: true }} />
</div>
</div>
) : (
<Input
name="zipCode"
label={intl.formatMessage({ id: "Zip code" })}
registerOptions={{ required: true }}
label={intl.formatMessage({ id: "Membership no" })}
name="membershipNo"
type="tel"
/>
</div>
<div>
<Checkbox name="termsAccepted" registerOptions={{ required: true }}>
<Body>
{intl.formatMessage({
id: "Yes, I accept the Terms and conditions for Scandic Friends and understand that Scandic will process my personal data in accordance with",
})}{" "}
<Link
variant="underscored"
color="peach80"
target="_blank"
href={privacyPolicy[lang]}
>
{intl.formatMessage({ id: "Scandic's Privacy Policy." })}
</Link>
</Body>
</Checkbox>
</div>
</div>
) : null}
</div>
)
}

View File

@@ -1,34 +1,30 @@
.form {
display: grid;
gap: var(--Spacing-x1);
padding: var(--Spacing-x3) 0px;
gap: var(--Spacing-x3);
margin-bottom: var(--Spacing-x3);
}
.container {
display: grid;
gap: var(--Spacing-x1);
grid-template-columns: 1fr 1fr;
gap: var(--Spacing-x2);
width: min(100%, 600px);
}
.header,
.country,
.email,
.membershipNo,
.signup,
.phone {
grid-column: 1/-1;
}
.footer {
display: grid;
gap: var(--Spacing-x3);
justify-items: flex-start;
margin-top: var(--Spacing-x1);
}
@media screen and (min-width: 1367px) {
@media screen and (min-width: 768px) {
.form {
gap: var(--Spacing-x2);
padding: var(--Spacing-x3) 0px;
gap: var(--Spacing-x3);
}
.container {
@@ -36,18 +32,4 @@
grid-template-columns: 1fr 1fr;
width: min(100%, 600px);
}
.country,
.email,
.membershipNo,
.phone {
grid-column: 1/-1;
}
.footer {
display: grid;
gap: var(--Spacing-x3);
justify-items: flex-start;
margin-top: var(--Spacing-x1);
}
}

View File

@@ -10,6 +10,7 @@ import { useStepsStore } from "@/stores/steps"
import Button from "@/components/TempDesignSystem/Button"
import CountrySelect from "@/components/TempDesignSystem/Form/Country"
import Input from "@/components/TempDesignSystem/Form/Input"
import JoinScandicFriendsCard from "@/components/TempDesignSystem/Form/JoinScandicFriendsCard"
import Phone from "@/components/TempDesignSystem/Form/Phone"
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
@@ -35,7 +36,6 @@ export default function Details({ user }: DetailsProps) {
join: state.data.join,
dateOfBirth: state.data.dateOfBirth,
zipCode: state.data.zipCode,
termsAccepted: state.data.termsAccepted,
membershipNo: state.data.membershipNo,
}))
@@ -52,7 +52,6 @@ export default function Details({ user }: DetailsProps) {
join: initialData.join,
dateOfBirth: initialData.dateOfBirth,
zipCode: initialData.zipCode,
termsAccepted: initialData.termsAccepted,
membershipNo: initialData.membershipNo,
},
criteriaMode: "all",
@@ -69,6 +68,7 @@ export default function Details({ user }: DetailsProps) {
[completeStep, updateDetails]
)
const joinValue = methods.watch("join")
return (
<FormProvider {...methods}>
<form
@@ -76,15 +76,21 @@ export default function Details({ user }: DetailsProps) {
id={formID}
onSubmit={methods.handleSubmit(onSubmit)}
>
{user ? null : <Signup name="join" />}
{user ? null : (
<JoinScandicFriendsCard
name="join"
difference={{ price: 1000, currency: "SEK" }}
/>
)}
<div className={styles.container}>
<Footnote
color="uiTextHighContrast"
textTransform="uppercase"
type="label"
className={styles.header}
>
{intl.formatMessage({ id: "Guest information" })}
</Footnote>
<div className={styles.container}>
<Input
label={intl.formatMessage({ id: "First name" })}
name="firstName"
@@ -118,13 +124,10 @@ export default function Details({ user }: DetailsProps) {
readOnly={!!user}
registerOptions={{ required: true }}
/>
{user || methods.watch("join") ? null : (
<Input
className={styles.membershipNo}
label={intl.formatMessage({ id: "Membership no" })}
name="membershipNo"
type="tel"
/>
{user ? null : (
<div className={styles.signup}>
<Signup name="join" />
</div>
)}
</div>
<footer className={styles.footer}>

View File

@@ -15,7 +15,6 @@ export const notJoinDetailsSchema = baseDetailsSchema.merge(
join: z.literal<boolean>(false),
zipCode: z.string().optional(),
dateOfBirth: z.string().optional(),
termsAccepted: z.boolean().default(false),
membershipNo: z
.string()
.optional()
@@ -39,15 +38,6 @@ export const joinDetailsSchema = baseDetailsSchema.merge(
join: z.literal<boolean>(true),
zipCode: z.string().min(1, { message: "Zip code is required" }),
dateOfBirth: z.string().min(1, { message: "Date of birth is required" }),
termsAccepted: z.literal<boolean>(true, {
errorMap: (err, ctx) => {
switch (err.code) {
case "invalid_literal":
return { message: "You must accept the terms and conditions" }
}
return { message: ctx.defaultError }
},
}),
membershipNo: z.string().optional(),
})
)

View File

@@ -66,7 +66,7 @@ export default function SectionAccordion({
const textColor =
isComplete || isOpen ? "uiTextHighContrast" : "baseTextDisabled"
return (
<section className={styles.wrapper} data-open={isOpen} data-step={step}>
<div className={styles.main} data-open={isOpen} data-step={step}>
<div className={styles.iconWrapper}>
<div className={styles.circle} data-checked={isComplete}>
{isComplete ? (
@@ -74,8 +74,7 @@ export default function SectionAccordion({
) : null}
</div>
</div>
<div className={styles.main}>
<header>
<header className={styles.header}>
<button onClick={onModify} className={styles.modifyButton}>
<Footnote
className={styles.title}
@@ -95,10 +94,7 @@ export default function SectionAccordion({
)}
</button>
</header>
<div className={styles.content}>
<div className={styles.contentWrapper}>{children}</div>
<div className={styles.content}>{children}</div>
</div>
</div>
</section>
)
}

View File

@@ -1,15 +1,25 @@
.wrapper {
position: relative;
display: flex;
flex-direction: row;
gap: var(--Spacing-x-one-and-half);
.main {
gap: var(--Spacing-x3);
width: 100%;
padding-top: var(--Spacing-x3);
transition: 0.4s ease-out;
display: grid;
grid-template-areas: "circle header" "content content";
grid-template-columns: auto 1fr;
grid-template-rows: 2.4em 0fr;
column-gap: var(--Spacing-x-one-and-half);
}
.wrapper:last-child .main {
.main:last-child .main {
border-bottom: none;
}
.header {
grid-area: header;
}
.modifyButton {
display: grid;
grid-template-areas: "title button" "selection button";
@@ -17,6 +27,7 @@
background-color: transparent;
border: none;
width: 100%;
padding: 0;
}
.title {
@@ -29,15 +40,6 @@
justify-self: flex-end;
}
.main {
display: grid;
width: 100%;
border-bottom: 1px solid var(--Primary-Light-On-Surface-Divider-subtle);
padding-bottom: var(--Spacing-x3);
transition: 0.4s ease-out;
grid-template-rows: 2em 0fr;
}
.selection {
font-weight: 450;
font-size: var(--typography-Title-4-fontSize);
@@ -46,6 +48,7 @@
.iconWrapper {
position: relative;
grid-area: circle;
}
.circle {
@@ -63,41 +66,41 @@
background-color: var(--UI-Input-Controls-Fill-Selected);
}
.wrapper[data-open="true"] .circle[data-checked="false"] {
.main[data-open="true"] .circle[data-checked="false"] {
background-color: var(--UI-Text-Placeholder);
}
.wrapper[data-open="false"] .circle[data-checked="false"] {
.main[data-open="false"] .circle[data-checked="false"] {
background-color: var(--Base-Surface-Subtle-Hover);
}
.wrapper[data-open="true"] .main {
grid-template-rows: 2em 1fr;
.main[data-open="true"] {
grid-template-rows: 2.4em 1fr;
gap: var(--Spacing-x3);
}
.content {
overflow: hidden;
grid-area: content;
border-bottom: 1px solid var(--Primary-Light-On-Surface-Divider-subtle);
}
.contentWrapper {
padding-top: var(--Spacing-x3);
}
@media screen and (min-width: 1367px) {
.wrapper {
@media screen and (min-width: 768px) {
.main {
gap: var(--Spacing-x3);
grid-template-areas: "circle header" "circle content";
}
.iconWrapper {
top: var(--Spacing-x1);
}
.wrapper:not(:last-child)::after {
.main:not(:last-child) .iconWrapper::after {
position: absolute;
left: 12px;
bottom: 0;
top: var(--Spacing-x7);
height: 100%;
bottom: calc(0px - var(--Spacing-x7));
top: 24px;
content: "";
border-left: 1px solid var(--Primary-Light-On-Surface-Divider-subtle);
}

View File

@@ -63,7 +63,7 @@
justify-content: flex-start;
}
@media screen and (min-width: 1367px) {
@media screen and (min-width: 768px) {
.wrapper {
gap: var(--Spacing-x3);
padding-top: var(--Spacing-x3);

View File

@@ -9,6 +9,7 @@
padding: var(--Spacing-x-one-and-half) var(--Spacing-x2);
transition: all 200ms ease;
width: min(100%, 600px);
height: 100%;
}
.label:hover {

View File

@@ -1,4 +1,8 @@
/* Leaving, will most likely get deleted */
.datePicker {
container-name: datePickerContainer;
container-type: inline-size;
}
.container {
display: grid;
gap: var(--Spacing-x2);
@@ -27,3 +31,10 @@
.year.invalid > div > div {
border-color: var(--Scandic-Red-60);
}
@container datePickerContainer (max-width: 350px) {
.container {
display: flex;
flex-direction: column;
}
}

View File

@@ -115,6 +115,7 @@ export default function DateSelect({ name, registerOptions = {} }: DateProps) {
ref={field.ref}
value={dateValue}
data-testid={name}
className={styles.datePicker}
>
<Group>
<DateInput className={styles.container}>

View File

@@ -35,7 +35,6 @@ export default function JoinScandicFriendsCard({
return (
<div className={styles.cardContainer}>
<header className={styles.header}>
<Checkbox name={name} className={styles.checkBox}>
<div>
<Caption type="label" textTransform="uppercase" color="red">
@@ -59,7 +58,7 @@ export default function JoinScandicFriendsCard({
</div>
</Checkbox>
<Footnote color="uiTextHighContrast">
<Footnote color="uiTextHighContrast" className={styles.login}>
{intl.formatMessage({ id: "Already a friend?" })}{" "}
<LoginButton
color="burgundy"
@@ -71,7 +70,6 @@ export default function JoinScandicFriendsCard({
{intl.formatMessage({ id: "Log in" })}
</LoginButton>
</Footnote>
</header>
<div className={styles.list}>
{list.map((item) => (
@@ -84,7 +82,7 @@ export default function JoinScandicFriendsCard({
</Caption>
))}
</div>
<Footnote color="uiTextPlaceholder">
<Footnote color="uiTextPlaceholder" className={styles.terms}>
{intl.formatMessage<React.ReactNode>(
{
id: "signup.terms",

View File

@@ -4,26 +4,52 @@
border: 1px solid var(--Base-Border-Subtle);
border-radius: var(--Corner-radius-Large);
display: grid;
gap: var(--Spacing-x2);
gap: var(--Spacing-x-one-and-half);
padding: var(--Spacing-x-one-and-half) var(--Spacing-x2);
grid-template-areas:
"checkbox"
"list"
"login"
"terms";
width: min(100%, 600px);
}
.header {
display: grid;
gap: var(--Spacing-x-one-and-half);
grid-template-columns: 1fr auto;
.login {
grid-area: login;
}
.checkBox {
align-self: center;
grid-area: checkbox;
}
.list {
display: flex;
display: grid;
grid-area: list;
gap: var(--Spacing-x1);
}
.listItem {
display: flex;
}
.terms {
border-top: 1px solid var(--Base-Border-Normal);
grid-area: terms;
padding-top: var(--Spacing-x1);
}
@media screen and (min-width: 768px) {
.cardContainer {
grid-template-columns: 1fr auto;
gap: var(--Spacing-x2);
grid-template-areas:
"checkbox login"
"list list"
"terms terms";
}
.list {
display: flex;
gap: var(--Spacing-x1);
}
}

View File

@@ -78,7 +78,8 @@ export default function Phone({
}
return (
<div className={`${styles.phone} ${className}`}>
<div className={`${styles.wrapper} ${className}`}>
<div className={styles.phone}>
<CountrySelector
disabled={readOnly}
dropdownArrowClassName={styles.arrow}
@@ -140,5 +141,6 @@ export default function Phone({
<ErrorMessage errors={formState.errors} name={field.name} />
</TextField>
</div>
</div>
)
}

View File

@@ -1,3 +1,7 @@
.wrapper {
container-name: phoneContainer;
container-type: inline-size;
}
.phone {
display: grid;
gap: var(--Spacing-x2);
@@ -100,3 +104,10 @@
justify-self: flex-start;
padding: 0;
}
@container phoneContainer (max-width: 350px) {
.phone {
display: flex;
flex-direction: column;
}
}

View File

@@ -94,7 +94,6 @@ export function createDetailsStore(
state.data.membershipNo = data.membershipNo
}
state.data.phoneNumber = data.phoneNumber
state.data.termsAccepted = data.termsAccepted
state.data.zipCode = data.zipCode
})
)