Sign up
Allows a user to register as a customer.
The customer will enter their billing address during the signup process. The
customer may select their country from a select menu, which is derived from
the countries defined in the channel that the customer is viewing the store
from. The menu defaults to the currently-selected country channel. The
billing fields in the signup form and their validation rules are defined by
the countrySettings (Channel.settings.countrySettings.privateCustomerFields
in the API).
SignupFormProvider
This Provider contains all of the validation and form setup logic (using Formik). The component must wrap the components that make use of the signup form hooks.
Example usage:
function SignupPage() {
return (
<SignupFormProvider>
{/* Hooks are used inside SignupForm, or anywhere below it in the tree */}
<SignupForm>
</SignupFormProvider>
)
}
Hooks
useAddressFields
This hooks returns an object containing the following:
countries: Country[]
An array of countries to display in the signup form. These countries are determined by the currently selected channel. Each Country has a code, id, and name.
fields: CustomerField[]
An array of address fields that must be displayed as part of the signup form, determined by the selected Country
. Each field has an id, label, placeholder, and required (boolean), as well as an inputProps object that can be spread on each input.
setCountryByCode(countryCode: string): void
Used to re-initialise the signup form when the country is changed. Must receive a valid country code.
Example usage:
const { fields, setCountryByCode, countries } = useAddressFields();
return (
<>
{
// display fields
fields.map((field) => (
<Input {...field.inputProps} />
))
}
<select
name="country"
id="country"
onChange={(e) => setCountryByCode(e.currentTarget.value)}
>
{countries.map((country) => (
<option key={country.code} value={country.code}>
{country.name}
</option>
))}
</select>
</>
);
useLoginFields
This hook returns an object containing the fields that are used to identify the user when logging in.
fields: CustomerField[]
An array containing the email and password fields. Each field is an object containing an id, label, placeholder, type (email|password
), and required (boolean), as well as an inputProps object that can be spread on each input field.
Example usage:
const { fields } = useLoginFields();
return fields.map((field) => {
return <Input {...field.inputProps} />;
});
useSignupForm
This hook is useful for submitting the form and displaying any validation errors that may be returned by the API.
It returns an object containing:
isSignupError: boolean
Returns true if the form failed to submit.
validationDetails: undefined | string[]
An array of strings containing both field and validation error. Will be undefined
if no validation errors exist.
isSubmitting: boolean
Whether or not the form is in the process of being submitted. Useful for disabling the submit button to prevent double submission.
handleSubmit(): void
Can be called to submit the signup form. Note that this is not necessary if your signup form contains a button with type="submit"
.
Example usage
const { isSubmitting } = useSignupForm();
return (
<div>
<button type="submit" disabled={isSubmitting}>{isSubmitting ? 'Submitting…' : 'Sign up'}</buton>
</div>
)
Non default SignupInputs
The following input fields is not a part of the API response for the current country SignupInput, meaning the Flight framework won't render these fields by default.
- priceListAccessCode
- dynamicContent
- preferences
- externalAttributes
You can however still use this as a part of your signup mutation by adding a custom input inside the SignupFormProvider. For this to work the input name need to match the field name in SignupInput, and if you don't want the input state controll to change between renders you need to add the additional values inside the additionalInitialValues object. Have a look in StoreAPI schema for more information on exact types.
priceListAccessCode
Example usage:
function SignUpPage() {
return (
<SignupFormProvider additionalInitialValues={{ priceListAccessCode: "" }}>
<FormikInput name="priceListAccessCode" />
</SignupFormProvider>
);
}
preferences
Example usage:
import Checkbox from "@jetshop/ui/Checkbox";
import { Field } from "formik";
function FormikCheckbox({ name, label, ...props }) {
return (
<Field name={name}>
{({ field }) => (
<Checkbox {...props} {...field} name={name} label={label} />
)}
</Field>
);
}
function SignUpPage() {
return (
<SignupFormProvider
additionalInitialValues={{
preferences: {
type: {
acceptsEmail: false,
},
},
}}
>
<FormikCheckbox
name="preferences.type.acceptsEmail"
label="Do you want newsletters?"
/>
</SignupFormProvider>
);
}
externalAttributes
When using a customer loyalty system you may want to add external attributes to each customer, which is useful for segmentation later on. This requires the attributes to be available in for example Voyado and added in Norce by Product Support. The variable accepts an array of objects like so:
[
{
"name": "string",
"value": "string"
}
]
The value should be a string, stringified JSON object or array, the expected type of this is defined by Voyado and client. In order to meet the requirements you need to provide an additional formatter (there will always be one used internally in the framework too) to the SignupFormProvider component. Like so:
NB: You most likely don't want to manipulate any other values than externalAttributes, make sure to pass the other values as is.
import { Field } from "formik";
import Checkbox from "@jetshop/ui/Checkbox";
function formatSignupInput(values) {
const externalAttributes = Object.entries(values?.externalAttributes).map(
([name, value]) => {
if (name === "my_cool_string_value") {
return {
name,
value,
};
} else {
return {
name,
value: JSON.stringify(value),
};
}
}
);
return {
...values,
externalAttributes,
};
}
const interests = [
{
name: "externalAttributes.interest",
value: "X",
},
{
name: "externalAttributes.interest",
value: "Y",
},
{
name: "externalAttributes.interest",
value: "Z",
},
];
function SignUpPage() {
return (
<SignupFormProvider
additionalFormatter={formatSignupInput}
additionalInitialValues={{
externalAttributes: {
my_cool_string_value: "",
interest: [],
},
}}
>
{/* Rest of form */}
<FormikInput name="externalAttributes.interest" />
{interests.map(({ name, value }) => (
<Field name={name} key={value}>
{({ field }) => (
<Checkbox {...field} name={name} value={value} label={value} />
)}
</Field>
))}
{/* Rest of form */}
</SignupFormProvider>
);
}
In this small example Formik is handling all the logic of adding and removing values from the array.
dynamicContent
This is useful for adding any content on each customer that may be presented in the store after login. If you need more data than a string, you may want to stringify a JSON object, like so:
import { Field } from "formik";
function formatSignupInput(values) {
const dynamicContent = JSON.stringify({
preferredBrand: values.dynamicContent.preferredBrand,
some_cool_meta_data: values.dynamicContent.some_cool_meta_data,
created: Date.now().toString(),
});
return { ...values, dynamicContent };
}
function SignUpPage() {
return (
<SignupFormProvider
additionalFormatter={formatSignupInput}
additionalInitialValues={{
dynamicContent: {
preferredBrand: "",
some_cool_meta_data: "",
created: "",
},
}}
>
{/* Rest of form */}
<FormikInput
name="dynamicContent.preferredBrand"
label="I would prefer to get dressed in this cool brand"
/>
<FormikInput
name="dynamicContent.some_cool_meta_data"
label="Cool meta data"
/>
{/* Rest of form */}
</SignupFormProvider>
);
}
If a string is enough, have a look at priceListAccessCode implementation and change the input name accordingly.
useSignupValidation
This hook is included in the template for easy customization.
It returns a translated string with validation details.
Example usage:
const { validationDetails, isSignupError, isSubmitting } = useSignupForm();
const validationMessage = useSignupValidation(validationDetails);
return (
<div>
{isSignupError && <div>{validationMessage}</div>}
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? "Submitting…" : "Sign up"}
</button>
</div>
);
Enable more fields for validation
If needed the fields object (useSignupValidation.js
) can extended to include more form fields to validate.
Available fields are the same ones used as SignUpInput
but with PascalCased keys (EmailAddress, PostalCode, MobilePhone etc).
Available validations errors are
Unknown,
Required,
BadFormat,
InvalidValue,
MaxLength,
UniqueValueAlreadyExists,
AgeValueInvalid,
InvalidEmailAddress,
InvalidPhoneNumber,
InvalidSocialSecurityNumber,
NotEditable
but commonly used are Required, InvalidValues and UniqueValueAlreadyExists. If no field or validation is specified it returns a generic validation error.
import t from "@jetshop/intl";
const fields = {
InvalidValue: {
Pid: t("PID is invalid"),
EmailAddress: t("Email address is invalid."),
},
UniqueValueAlreadyExists: {
Pid: t("This PID is already registered"),
EmailAddress: t("Your email address is already registered."),
},
Required: {
Pid: t("PID is required"),
EmailAddress: t("Email address is required."),
},
};