Prevent form submit if country is not selected (#1966)

* Prevent form submit if country is not selected

* Refactor error checking function

* Make address forms not invoke chrome's autofill

* Add a comment

* Update tests
This commit is contained in:
Wojciech Mista 2022-04-06 11:32:31 +02:00 committed by GitHub
parent 05c2efde6c
commit 168887ac3b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 83 additions and 60 deletions

View file

@ -93,7 +93,9 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
value={data.firstName}
fullWidth
InputProps={{
autoComplete: "given-name"
// Setting 'autoComplete: "new-password"' is the only way to
// disable Chrome's autofill on forms as of early 2022
autoComplete: "new-password"
}}
/>
</div>
@ -108,7 +110,7 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
value={data.lastName}
fullWidth
InputProps={{
autoComplete: "family-name"
autoComplete: "new-password"
}}
/>
</div>
@ -128,7 +130,7 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
value={data.companyName}
fullWidth
InputProps={{
autoComplete: "organization"
autoComplete: "new-password"
}}
/>
</div>
@ -145,7 +147,7 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
value={data.phone}
onChange={onChange}
InputProps={{
autoComplete: "tel"
autoComplete: "new-password"
}}
/>
</div>
@ -163,7 +165,7 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
value={data.streetAddress1}
fullWidth
InputProps={{
autoComplete: "address-line1"
autoComplete: "new-password"
}}
/>
<FormSpacer />
@ -179,7 +181,7 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
value={data.streetAddress2}
fullWidth
InputProps={{
autoComplete: "address-line2"
autoComplete: "new-password"
}}
/>
<FormSpacer />
@ -197,7 +199,7 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
value={data.city}
fullWidth
InputProps={{
autoComplete: "address-level2"
autoComplete: "new-password"
}}
/>
</div>
@ -214,7 +216,7 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
value={data.postalCode}
fullWidth
InputProps={{
autoComplete: "postal-code"
autoComplete: "new-password"
}}
/>
</div>
@ -251,7 +253,7 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
value={data.countryArea}
fullWidth
InputProps={{
autoComplete: "address-level1"
autoComplete: "new-password"
}}
/>
</div>

View file

@ -43,7 +43,11 @@ import {
OrderCustomerAddressesEditDialogOutput,
OrderCustomerSearchAddressState
} from "./types";
import { getAddressEditProps, validateDefaultAddress } from "./utils";
import {
getAddressEditProps,
hasPreSubmitErrors,
validateDefaultAddress
} from "./utils";
export interface OrderCustomerAddressesEditDialogProps {
open: boolean;
@ -131,20 +135,22 @@ const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialo
transformAddressToAddressInput(
customerAddresses.find(getById(selectedCustomerAddressID))
);
const handleAddressesSubmit = (data: OrderCustomerAddressesEditFormData) => {
// async because handleShippingSubmit can return a promise
const handleAddressesSubmit = async (
data: OrderCustomerAddressesEditFormData
) => {
const shippingAddress =
customerAddresses.length > 0 &&
data.shippingAddressInputOption ===
AddressInputOptionEnum.CUSTOMER_ADDRESS
? getCustomerAddress(data.customerShippingAddress.id)
: handleShippingSubmit(data.shippingAddress);
: await handleShippingSubmit(data.shippingAddress);
const billingAddress =
customerAddresses.length > 0 &&
data.billingAddressInputOption === AddressInputOptionEnum.CUSTOMER_ADDRESS
? getCustomerAddress(data.customerBillingAddress.id)
: handleBillingSubmit(data.billingAddress);
: await handleBillingSubmit(data.billingAddress);
if (variant === AddressEditDialogVariant.CHANGE_SHIPPING_ADDRESS) {
return {
@ -205,12 +211,11 @@ const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialo
handleSubmit(data);
};
const handleSubmit = async (data: OrderCustomerAddressesEditFormData) => {
const addressesInput = handleAddressesSubmit(data);
if (addressesInput) {
const addressesInput = await handleAddressesSubmit(data);
if (addressesInput && !hasPreSubmitErrors(addressesInput)) {
await onConfirm(addressesInput as OrderCustomerAddressesEditDialogOutput);
setAddressSearchState(defaultSearchState);
}
return Promise.resolve([
...shippingValidationErrors,
...billingValidationErrors

View file

@ -221,7 +221,11 @@ const OrderCustomerAddressesEditForm: React.FC<OrderCustomerAddressesEditFormPro
rest
);
return <form onSubmit={props.submit}>{children(props)}</form>;
return (
<form onSubmit={props.submit} autoComplete="off">
{children(props)}
</form>
);
};
OrderCustomerAddressesEditForm.displayName = "OrderCustomerAddressesEditForm";

View file

@ -2,6 +2,7 @@ import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompl
import {
AccountErrorFragment,
AddressFragment,
AddressInput,
AddressTypeEnum,
Node,
OrderErrorFragment
@ -48,6 +49,21 @@ export function validateDefaultAddress<T extends AddressFragment>(
return defaultAddress;
}
const filterAddressErrors = (
dialogErrors: Array<OrderErrorFragment | AccountErrorFragment>,
addressType: AddressTypeEnum
) => dialogErrors.filter(error => error.addressType === addressType);
interface ShippingAddresses {
shippingAddress: AccountErrorFragment[] | AddressInput;
billingAddress: AccountErrorFragment[] | AddressInput;
}
export const hasPreSubmitErrors = (input: ShippingAddresses) =>
Object.values(input)
.flat()
.some(el => "code" in el);
export const getAddressEditProps = (
variant: "shipping" | "billing",
data: OrderCustomerAddressesEditData,
@ -63,9 +79,7 @@ export const getAddressEditProps = (
return {
...addressEditCommonProps,
addressInputName: "shippingAddressInputOption",
formErrors: dialogErrors.filter(
error => error.addressType === AddressTypeEnum.SHIPPING
),
formErrors: filterAddressErrors(dialogErrors, AddressTypeEnum.SHIPPING),
onEdit: () =>
setAddressSearchState({
open: true,
@ -84,9 +98,7 @@ export const getAddressEditProps = (
return {
...addressEditCommonProps,
addressInputName: "billingAddressInputOption",
formErrors: dialogErrors.filter(
error => error.addressType === AddressTypeEnum.BILLING
),
formErrors: filterAddressErrors(dialogErrors, AddressTypeEnum.BILLING),
onEdit: () =>
setAddressSearchState({
open: true,

View file

@ -3727,7 +3727,7 @@ exports[`Storyshots Generics / AddressEdit default 1`] = `
>
<input
aria-invalid="false"
autocomplete="given-name"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="firstName"
type="text"
@ -3763,7 +3763,7 @@ exports[`Storyshots Generics / AddressEdit default 1`] = `
>
<input
aria-invalid="false"
autocomplete="family-name"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="lastName"
type="text"
@ -3806,7 +3806,7 @@ exports[`Storyshots Generics / AddressEdit default 1`] = `
>
<input
aria-invalid="false"
autocomplete="organization"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="companyName"
type="text"
@ -3842,7 +3842,7 @@ exports[`Storyshots Generics / AddressEdit default 1`] = `
>
<input
aria-invalid="false"
autocomplete="tel"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="phone"
type="text"
@ -3881,7 +3881,7 @@ exports[`Storyshots Generics / AddressEdit default 1`] = `
>
<input
aria-invalid="false"
autocomplete="address-line1"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="streetAddress1"
type="text"
@ -3918,7 +3918,7 @@ exports[`Storyshots Generics / AddressEdit default 1`] = `
>
<input
aria-invalid="false"
autocomplete="address-line2"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="streetAddress2"
type="text"
@ -3959,7 +3959,7 @@ exports[`Storyshots Generics / AddressEdit default 1`] = `
>
<input
aria-invalid="false"
autocomplete="address-level2"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="city"
type="text"
@ -3995,7 +3995,7 @@ exports[`Storyshots Generics / AddressEdit default 1`] = `
>
<input
aria-invalid="false"
autocomplete="postal-code"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="postalCode"
type="text"
@ -4109,7 +4109,7 @@ exports[`Storyshots Generics / AddressEdit default 1`] = `
>
<input
aria-invalid="false"
autocomplete="address-level1"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="countryArea"
type="text"
@ -64796,7 +64796,7 @@ exports[`Storyshots Views / Customers / Create customer default 1`] = `
>
<input
aria-invalid="false"
autocomplete="given-name"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="firstName"
type="text"
@ -64832,7 +64832,7 @@ exports[`Storyshots Views / Customers / Create customer default 1`] = `
>
<input
aria-invalid="false"
autocomplete="family-name"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="lastName"
type="text"
@ -64875,7 +64875,7 @@ exports[`Storyshots Views / Customers / Create customer default 1`] = `
>
<input
aria-invalid="false"
autocomplete="organization"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="companyName"
type="text"
@ -64911,7 +64911,7 @@ exports[`Storyshots Views / Customers / Create customer default 1`] = `
>
<input
aria-invalid="false"
autocomplete="tel"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="phone"
type="text"
@ -64950,7 +64950,7 @@ exports[`Storyshots Views / Customers / Create customer default 1`] = `
>
<input
aria-invalid="false"
autocomplete="address-line1"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="streetAddress1"
type="text"
@ -64987,7 +64987,7 @@ exports[`Storyshots Views / Customers / Create customer default 1`] = `
>
<input
aria-invalid="false"
autocomplete="address-line2"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="streetAddress2"
type="text"
@ -65028,7 +65028,7 @@ exports[`Storyshots Views / Customers / Create customer default 1`] = `
>
<input
aria-invalid="false"
autocomplete="address-level2"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="city"
type="text"
@ -65064,7 +65064,7 @@ exports[`Storyshots Views / Customers / Create customer default 1`] = `
>
<input
aria-invalid="false"
autocomplete="postal-code"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="postalCode"
type="text"
@ -65178,7 +65178,7 @@ exports[`Storyshots Views / Customers / Create customer default 1`] = `
>
<input
aria-invalid="false"
autocomplete="address-level1"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="countryArea"
type="text"
@ -65490,7 +65490,7 @@ exports[`Storyshots Views / Customers / Create customer form errors 1`] = `
>
<input
aria-invalid="true"
autocomplete="given-name"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="firstName"
type="text"
@ -65531,7 +65531,7 @@ exports[`Storyshots Views / Customers / Create customer form errors 1`] = `
>
<input
aria-invalid="true"
autocomplete="family-name"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="lastName"
type="text"
@ -65579,7 +65579,7 @@ exports[`Storyshots Views / Customers / Create customer form errors 1`] = `
>
<input
aria-invalid="true"
autocomplete="organization"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="companyName"
type="text"
@ -65620,7 +65620,7 @@ exports[`Storyshots Views / Customers / Create customer form errors 1`] = `
>
<input
aria-invalid="true"
autocomplete="tel"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="phone"
type="text"
@ -65664,7 +65664,7 @@ exports[`Storyshots Views / Customers / Create customer form errors 1`] = `
>
<input
aria-invalid="true"
autocomplete="address-line1"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="streetAddress1"
type="text"
@ -65706,7 +65706,7 @@ exports[`Storyshots Views / Customers / Create customer form errors 1`] = `
>
<input
aria-invalid="true"
autocomplete="address-line2"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="streetAddress2"
type="text"
@ -65752,7 +65752,7 @@ exports[`Storyshots Views / Customers / Create customer form errors 1`] = `
>
<input
aria-invalid="true"
autocomplete="address-level2"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="city"
type="text"
@ -65793,7 +65793,7 @@ exports[`Storyshots Views / Customers / Create customer form errors 1`] = `
>
<input
aria-invalid="true"
autocomplete="postal-code"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="postalCode"
type="text"
@ -65917,7 +65917,7 @@ exports[`Storyshots Views / Customers / Create customer form errors 1`] = `
>
<input
aria-invalid="true"
autocomplete="address-level1"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
name="countryArea"
type="text"
@ -66237,7 +66237,7 @@ exports[`Storyshots Views / Customers / Create customer loading 1`] = `
>
<input
aria-invalid="false"
autocomplete="given-name"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id"
disabled=""
name="firstName"
@ -66274,7 +66274,7 @@ exports[`Storyshots Views / Customers / Create customer loading 1`] = `
>
<input
aria-invalid="false"
autocomplete="family-name"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id"
disabled=""
name="lastName"
@ -66318,7 +66318,7 @@ exports[`Storyshots Views / Customers / Create customer loading 1`] = `
>
<input
aria-invalid="false"
autocomplete="organization"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id"
disabled=""
name="companyName"
@ -66355,7 +66355,7 @@ exports[`Storyshots Views / Customers / Create customer loading 1`] = `
>
<input
aria-invalid="false"
autocomplete="tel"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id"
disabled=""
name="phone"
@ -66395,7 +66395,7 @@ exports[`Storyshots Views / Customers / Create customer loading 1`] = `
>
<input
aria-invalid="false"
autocomplete="address-line1"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id"
disabled=""
name="streetAddress1"
@ -66433,7 +66433,7 @@ exports[`Storyshots Views / Customers / Create customer loading 1`] = `
>
<input
aria-invalid="false"
autocomplete="address-line2"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id"
disabled=""
name="streetAddress2"
@ -66475,7 +66475,7 @@ exports[`Storyshots Views / Customers / Create customer loading 1`] = `
>
<input
aria-invalid="false"
autocomplete="address-level2"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id"
disabled=""
name="city"
@ -66512,7 +66512,7 @@ exports[`Storyshots Views / Customers / Create customer loading 1`] = `
>
<input
aria-invalid="false"
autocomplete="postal-code"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id"
disabled=""
name="postalCode"
@ -66628,7 +66628,7 @@ exports[`Storyshots Views / Customers / Create customer loading 1`] = `
>
<input
aria-invalid="false"
autocomplete="address-level1"
autocomplete="new-password"
class="MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id"
disabled=""
name="countryArea"