From c4863ece60543da5bfe59518101ca4b23ca04ba4 Mon Sep 17 00:00:00 2001 From: dominik-zeglen Date: Mon, 24 Feb 2020 15:14:48 +0100 Subject: [PATCH] Do not store errors in form component --- .../AttributeDetails/AttributeDetails.tsx | 17 +- .../AttributePage/AttributePage.tsx | 8 +- .../AttributeProperties.tsx | 11 +- .../AttributeValueEditDialog.tsx | 9 +- .../CategoryCreatePage/CategoryCreatePage.tsx | 11 +- .../CategoryDetailsForm.tsx | 12 +- .../CategoryUpdatePage/CategoryUpdatePage.tsx | 11 +- .../CollectionCreatePage.tsx | 8 +- .../CollectionDetails/CollectionDetails.tsx | 13 +- .../CollectionDetailsPage.tsx | 10 +- src/collections/views/CollectionDetails.tsx | 3 + src/components/AddressEdit/AddressEdit.tsx | 45 +- .../EditableTableCell/EditableTableCell.tsx | 2 +- src/components/Form/Form.tsx | 6 +- .../VisibilityCard/VisibilityCard.tsx | 18 +- .../CustomerAddressDialog.tsx | 15 +- .../CustomerCreateAddress.tsx | 4 +- .../CustomerCreateDetails.tsx | 17 +- .../CustomerCreateNote/CustomerCreateNote.tsx | 10 +- .../CustomerCreatePage/CustomerCreatePage.tsx | 19 +- .../CustomerDetails/CustomerDetails.tsx | 9 +- .../CustomerDetailsPage.tsx | 7 +- .../components/CustomerInfo/CustomerInfo.tsx | 20 +- .../DiscountDates/DiscountDates.tsx | 21 +- .../SaleCreatePage/SaleCreatePage.tsx | 8 +- .../SaleDetailsPage/SaleDetailsPage.tsx | 10 +- .../components/SaleInfo/SaleInfo.tsx | 10 +- .../components/SaleValue/SaleValue.tsx | 9 +- .../VoucherCreatePage/VoucherCreatePage.tsx | 16 +- .../components/VoucherDates/VoucherDates.tsx | 21 +- .../VoucherDetailsPage/VoucherDetailsPage.tsx | 16 +- .../components/VoucherInfo/VoucherInfo.tsx | 9 +- .../VoucherLimits/VoucherLimits.tsx | 9 +- .../VoucherRequirements.tsx | 15 +- .../components/VoucherTypes/VoucherTypes.tsx | 9 +- .../components/VoucherValue/VoucherValue.tsx | 13 +- src/hooks/useForm.ts | 19 - .../MenuCreateDialog/MenuCreateDialog.tsx | 10 +- .../MenuItemDialog/MenuItemDialog.tsx | 4 +- src/navigation/views/MenuList/MenuList.tsx | 1 + .../OrderAddressEditDialog.tsx | 6 +- .../PageDetailsPage/PageDetailsPage.tsx | 10 +- src/pages/components/PageInfo/PageInfo.tsx | 13 +- src/pages/components/PageSlug/PageSlug.tsx | 8 +- .../PluginSettings/PluginSettings.tsx | 7 +- .../PluginsDetailsPage/PluginsDetailsPage.tsx | 4 +- .../ProductTypeAttributeEditDialog.tsx | 40 +- .../ProductTypeCreatePage.tsx | 11 +- .../ProductTypeDetails/ProductTypeDetails.tsx | 9 +- .../ProductTypeDetailsPage.tsx | 11 +- .../ProductCreatePage/ProductCreatePage.tsx | 19 +- .../ProductDetailsForm/ProductDetailsForm.tsx | 12 +- .../ProductOrganization.tsx | 14 +- .../components/ProductStock/ProductStock.tsx | 8 +- .../ProductUpdatePage/ProductUpdatePage.tsx | 19 +- .../ProductVariantCreatePage.tsx | 8 +- .../ProductVariantPage/ProductVariantPage.tsx | 13 +- .../ProductVariantPrice.tsx | 34 +- .../ProductVariantStock.tsx | 17 +- .../ServiceCreatePage/ServiceCreatePage.tsx | 11 +- .../ServiceDetailsPage/ServiceDetailsPage.tsx | 11 +- .../components/ServiceInfo/ServiceInfo.tsx | 9 +- .../ShippingZoneCreatePage.tsx | 6 +- .../ShippingZoneDetailsPage.tsx | 12 +- .../ShippingZoneInfo/ShippingZoneInfo.tsx | 9 +- .../ShippingZoneRateDialog.tsx | 453 +++-- .../SiteSettingsAddress.tsx | 38 +- .../SiteSettingsDetails.tsx | 20 +- .../SiteSettingsKeyDialog.tsx | 31 +- .../SiteSettingsMailing.tsx | 23 +- .../SiteSettingsPage/SiteSettingsPage.tsx | 12 +- .../StaffAddMemberDialog.tsx | 19 +- .../StaffPasswordResetDialog.tsx | 17 +- .../__snapshots__/Stories.test.ts.snap | 1506 +++++++++++++++++ .../collections/CollectionDetailsPage.tsx | 10 + .../stories/components/AddressEdit.tsx | 2 +- .../stories/navigation/MenuCreateDialog.tsx | 5 + .../ProductTypeAttributeEditDialog.tsx | 7 +- .../siteSettings/SiteSettingsKeyDialog.tsx | 1 + src/types.ts | 4 +- src/utils/errors.ts | 7 +- .../WebhookCreatePage/WebhookCreatePage.tsx | 11 +- .../components/WebhookInfo/WebhookInfo.tsx | 40 +- .../WebhooksDetailsPage.tsx | 7 +- 84 files changed, 2241 insertions(+), 778 deletions(-) diff --git a/src/attributes/components/AttributeDetails/AttributeDetails.tsx b/src/attributes/components/AttributeDetails/AttributeDetails.tsx index 9d873b5e3..a0eb97cfe 100644 --- a/src/attributes/components/AttributeDetails/AttributeDetails.tsx +++ b/src/attributes/components/AttributeDetails/AttributeDetails.tsx @@ -10,15 +10,16 @@ import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; import FormSpacer from "@saleor/components/FormSpacer"; import SingleSelectField from "@saleor/components/SingleSelectField"; import { commonMessages } from "@saleor/intl"; -import { FormErrors } from "@saleor/types"; +import { UserError } from "@saleor/types"; import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; +import { getFieldError } from "@saleor/utils/errors"; import { AttributePageFormData } from "../AttributePage"; export interface AttributeDetailsProps { canChangeType: boolean; data: AttributePageFormData; disabled: boolean; - errors: FormErrors<"name" | "slug" | "inputType">; + errors: UserError[]; onChange: (event: React.ChangeEvent) => void; } @@ -55,21 +56,21 @@ const AttributeDetails: React.FC = ({ = ({ placeholder={slugify(data.name).toLowerCase()} fullWidth helperText={ - errors.slug || + getFieldError(errors, "slug")?.message || intl.formatMessage({ defaultMessage: "This is used internally. Make sure you don’t use spaces", @@ -92,8 +93,8 @@ const AttributeDetails: React.FC = ({ = ({ }); return ( -
- {({ change, errors: formErrors, data, submit }) => ( + + {({ change, data, submit }) => ( {intl.formatMessage(sectionNames.attributes)} @@ -130,7 +130,7 @@ const AttributePage: React.FC = ({ canChangeType={attribute === null} data={data} disabled={disabled} - errors={formErrors} + errors={errors} onChange={change} /> @@ -146,7 +146,7 @@ const AttributePage: React.FC = ({
diff --git a/src/attributes/components/AttributeProperties/AttributeProperties.tsx b/src/attributes/components/AttributeProperties/AttributeProperties.tsx index 0e5d2d958..4e12f1d29 100644 --- a/src/attributes/components/AttributeProperties/AttributeProperties.tsx +++ b/src/attributes/components/AttributeProperties/AttributeProperties.tsx @@ -11,13 +11,14 @@ import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; import FormSpacer from "@saleor/components/FormSpacer"; import Hr from "@saleor/components/Hr"; import { commonMessages } from "@saleor/intl"; -import { FormErrors } from "@saleor/types"; +import { UserError } from "@saleor/types"; +import { getFieldError } from "@saleor/utils/errors"; import { AttributePageFormData } from "../AttributePage"; export interface AttributePropertiesProps { data: AttributePageFormData; disabled: boolean; - errors: FormErrors<"storefrontSearchPosition">; + errors: UserError[]; onChange: (event: React.ChangeEvent) => void; } @@ -85,9 +86,11 @@ const AttributeProperties: React.FC = ({ {data.filterableInStorefront && ( = ({ /> )} - - {({ change, data, errors: formErrors, submit }) => ( + + {({ change, data, submit }) => ( <> = ({ disabled, onSubmit, onBack, - errors: userErrors, + errors, saveButtonBarState }) => { const intl = useIntl(); return ( - - {({ data, change, errors, submit, hasChanged }) => ( + + {({ data, change, submit, hasChanged }) => ( {intl.formatMessage(sectionNames.categories)} diff --git a/src/categories/components/CategoryDetailsForm/CategoryDetailsForm.tsx b/src/categories/components/CategoryDetailsForm/CategoryDetailsForm.tsx index f7530a0b8..5ffcefab8 100644 --- a/src/categories/components/CategoryDetailsForm/CategoryDetailsForm.tsx +++ b/src/categories/components/CategoryDetailsForm/CategoryDetailsForm.tsx @@ -9,6 +9,8 @@ import CardTitle from "@saleor/components/CardTitle"; import FormSpacer from "@saleor/components/FormSpacer"; import RichTextEditor from "@saleor/components/RichTextEditor"; import { commonMessages } from "@saleor/intl"; +import { UserError } from "@saleor/types"; +import { getFieldError } from "@saleor/utils/errors"; import { maybe } from "../../../misc"; import { CategoryDetails_category } from "../../types/CategoryDetails"; @@ -19,7 +21,7 @@ interface CategoryDetailsFormProps { description: RawDraftContentState; }; disabled: boolean; - errors: { [key: string]: string }; + errors: UserError[]; onChange: (event: React.ChangeEvent) => void; } @@ -47,16 +49,16 @@ export const CategoryDetailsForm: React.FC = ({ disabled={disabled} value={data && data.name} onChange={onChange} - error={!!errors.name} - helperText={errors.name} + error={!!getFieldError(errors, "name")} + helperText={getFieldError(errors, "name")?.message} fullWidth />
= ({ currentTab, category, disabled, - errors: userErrors, + errors, pageInfo, products, saveButtonBarState, @@ -115,13 +115,8 @@ export const CategoryUpdatePage: React.FC = ({ seoTitle: "" }; return ( - - {({ data, change, errors, submit, hasChanged }) => ( + + {({ data, change, submit, hasChanged }) => ( {intl.formatMessage(sectionNames.categories)} diff --git a/src/collections/components/CollectionCreatePage/CollectionCreatePage.tsx b/src/collections/components/CollectionCreatePage/CollectionCreatePage.tsx index e30eda267..c7680bbcc 100644 --- a/src/collections/components/CollectionCreatePage/CollectionCreatePage.tsx +++ b/src/collections/components/CollectionCreatePage/CollectionCreatePage.tsx @@ -68,8 +68,8 @@ const CollectionCreatePage: React.FC = ({ const localizeDate = useDateLocalize(); return ( - - {({ change, data, errors: formErrors, hasChanged, submit }) => ( + + {({ change, data, hasChanged, submit }) => ( {intl.formatMessage(sectionNames.collections)} @@ -85,7 +85,7 @@ const CollectionCreatePage: React.FC = ({ @@ -147,7 +147,7 @@ const CollectionCreatePage: React.FC = ({ ; + errors: UserError[]; onChange: (event: React.ChangeEvent) => void; } @@ -48,14 +49,14 @@ const CollectionDetails: React.FC = ({ disabled={disabled} value={data.name} onChange={onChange} - error={!!errors.name} - helperText={errors.name} + error={!!getFieldError(errors, "name")} + helperText={getFieldError(errors, "name")?.message} fullWidth /> JSON.parse(collection.descriptionJson))} label={intl.formatMessage(commonMessages.description)} name="description" diff --git a/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx b/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx index 0f1398f38..3d81c31e4 100644 --- a/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx +++ b/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx @@ -18,7 +18,7 @@ import VisibilityCard from "@saleor/components/VisibilityCard"; import useDateLocalize from "@saleor/hooks/useDateLocalize"; import { sectionNames } from "@saleor/intl"; import { maybe } from "../../../misc"; -import { ListActions, PageListProps } from "../../../types"; +import { ListActions, PageListProps, UserError } from "../../../types"; import { CollectionDetails_collection } from "../../types/CollectionDetails"; import CollectionDetails from "../CollectionDetails/CollectionDetails"; import { CollectionImage } from "../CollectionImage/CollectionImage"; @@ -37,6 +37,7 @@ export interface CollectionDetailsPageFormData { export interface CollectionDetailsPageProps extends PageListProps, ListActions { collection: CollectionDetails_collection; + errors: UserError[]; isFeatured: boolean; saveButtonBarState: ConfirmButtonTransitionState; onBack: () => void; @@ -50,6 +51,7 @@ export interface CollectionDetailsPageProps extends PageListProps, ListActions { const CollectionDetailsPage: React.FC = ({ collection, disabled, + errors, isFeatured, saveButtonBarState, onBack, @@ -77,7 +79,7 @@ const CollectionDetailsPage: React.FC = ({ onSubmit={onSubmit} confirmLeave > - {({ change, data, errors: formErrors, hasChanged, submit }) => ( + {({ change, data, hasChanged, submit }) => ( {intl.formatMessage(sectionNames.collections)} @@ -89,7 +91,7 @@ const CollectionDetailsPage: React.FC = ({ collection={collection} data={data} disabled={disabled} - errors={formErrors} + errors={errors} onChange={change} /> @@ -124,7 +126,7 @@ const CollectionDetailsPage: React.FC = ({
= ({ onBack={handleBack} disabled={loading} collection={maybe(() => data.collection)} + errors={ + updateCollection.opts?.data?.collectionUpdate.errors || [] + } isFeatured={maybe( () => data.shop.homepageCollection.id === data.collection.id, diff --git a/src/components/AddressEdit/AddressEdit.tsx b/src/components/AddressEdit/AddressEdit.tsx index c4c63e358..96ce090c1 100644 --- a/src/components/AddressEdit/AddressEdit.tsx +++ b/src/components/AddressEdit/AddressEdit.tsx @@ -5,7 +5,8 @@ import { useIntl } from "react-intl"; import { AddressTypeInput } from "@saleor/customers/types"; import { commonMessages } from "@saleor/intl"; -import { FormErrors } from "@saleor/types"; +import { UserError } from "@saleor/types"; +import { getFieldError } from "@saleor/utils/errors"; import FormSpacer from "../FormSpacer"; import SingleAutocompleteSelectField, { SingleAutocompleteChoiceType @@ -27,7 +28,7 @@ interface AddressEditProps { countryDisplayValue: string; data: AddressTypeInput; disabled?: boolean; - errors: FormErrors; + errors: UserError[]; onChange(event: React.ChangeEvent); onCountryChange(event: React.ChangeEvent); } @@ -52,8 +53,8 @@ const AddressEdit: React.FC = props => {
= props => {
= props => {
= props => {
= props => { = props => { = props => {
= props => {
= props => { = props => {
= props => { }; const [opened, setOpenStatus] = React.useState(focused); - const { change, data } = useForm({ value }, [], handleConfirm); + const { change, data } = useForm({ value }, handleConfirm); const enable = () => setOpenStatus(true); const disable = () => setOpenStatus(false); diff --git a/src/components/Form/Form.tsx b/src/components/Form/Form.tsx index 25512cf57..43668380f 100644 --- a/src/components/Form/Form.tsx +++ b/src/components/Form/Form.tsx @@ -1,20 +1,18 @@ import React from "react"; import useForm, { UseFormResult } from "@saleor/hooks/useForm"; -import { UserError } from "@saleor/types"; export interface FormProps { children: (props: UseFormResult) => React.ReactNode; confirmLeave?: boolean; - errors?: UserError[]; initial?: T; resetOnSubmit?: boolean; onSubmit?: (data: T) => void; } function Form(props: FormProps) { - const { children, errors, initial, resetOnSubmit, onSubmit } = props; - const renderProps = useForm(initial, errors, onSubmit); + const { children, initial, resetOnSubmit, onSubmit } = props; + const renderProps = useForm(initial, onSubmit); function handleSubmit(event?: React.FormEvent, cb?: () => void) { const { reset, submit } = renderProps; diff --git a/src/components/VisibilityCard/VisibilityCard.tsx b/src/components/VisibilityCard/VisibilityCard.tsx index 061d00a66..d75166fc8 100644 --- a/src/components/VisibilityCard/VisibilityCard.tsx +++ b/src/components/VisibilityCard/VisibilityCard.tsx @@ -8,7 +8,8 @@ import { useIntl } from "react-intl"; import CardTitle from "@saleor/components/CardTitle"; import RadioSwitchField from "@saleor/components/RadioSwitchField"; -import { FormErrors } from "@saleor/types"; +import { UserError } from "@saleor/types"; +import { getFieldError } from "@saleor/utils/errors"; import { DateContext } from "../Date/DateContext"; import FormSpacer from "../FormSpacer"; @@ -52,7 +53,7 @@ interface VisibilityCardProps { isPublished: boolean; publicationDate: string; }; - errors: FormErrors<"isPublished" | "publicationDate">; + errors: UserError[]; disabled?: boolean; hiddenMessage: string; onChange: (event: React.ChangeEvent) => void; @@ -62,7 +63,6 @@ interface VisibilityCardProps { export const VisibilityCard: React.FC = props => { const { children, - data: { isPublished, publicationDate }, errors, disabled, @@ -101,7 +101,7 @@ export const VisibilityCard: React.FC = props => {

@@ -140,7 +140,7 @@ export const VisibilityCard: React.FC = props => { )} {isPublicationDate && ( = props => { name="publicationDate" type="date" fullWidth={true} - helperText={errors.publicationDate} + helperText={getFieldError(errors, "publicationDate")?.message} value={publicationDate ? publicationDate : ""} onChange={onChange} className={classes.date} @@ -160,10 +160,12 @@ export const VisibilityCard: React.FC = props => { )} )} - {errors.isPublished && ( + {getFieldError(errors, "isPublished") && ( <> - {errors.isPublished} + + {getFieldError(errors, "isPublished")?.message} + )}

{children}
diff --git a/src/customers/components/CustomerAddressDialog/CustomerAddressDialog.tsx b/src/customers/components/CustomerAddressDialog/CustomerAddressDialog.tsx index 3b7a77679..4c6dd92d3 100644 --- a/src/customers/components/CustomerAddressDialog/CustomerAddressDialog.tsx +++ b/src/customers/components/CustomerAddressDialog/CustomerAddressDialog.tsx @@ -43,7 +43,10 @@ const styles = createStyles({ } }); -const CustomerAddressDialog = withStyles(styles, {})( +const CustomerAddressDialog = withStyles( + styles, + {} +)( ({ address, classes, @@ -98,12 +101,8 @@ const CustomerAddressDialog = withStyles(styles, {})( fullWidth maxWidth="sm" > - - {({ change, data, errors: formErrors }) => { + + {({ change, data }) => { const handleCountrySelect = createSingleAutocompleteSelectHandler( change, setCountryDisplayName, @@ -130,7 +129,7 @@ const CustomerAddressDialog = withStyles(styles, {})( countries={countryChoices} data={data} countryDisplayValue={countryDisplayName} - errors={formErrors} + errors={dialogErrors} onChange={change} onCountryChange={handleCountrySelect} /> diff --git a/src/customers/components/CustomerCreateAddress/CustomerCreateAddress.tsx b/src/customers/components/CustomerCreateAddress/CustomerCreateAddress.tsx index 530099ddf..2f98fbe18 100644 --- a/src/customers/components/CustomerCreateAddress/CustomerCreateAddress.tsx +++ b/src/customers/components/CustomerCreateAddress/CustomerCreateAddress.tsx @@ -9,7 +9,7 @@ import AddressEdit from "@saleor/components/AddressEdit"; import CardTitle from "@saleor/components/CardTitle"; import { FormSpacer } from "@saleor/components/FormSpacer"; import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; -import { FormErrors } from "../../../types"; +import { UserError } from "../../../types"; import { AddressTypeInput } from "../../types"; const useStyles = makeStyles( @@ -26,7 +26,7 @@ export interface CustomerCreateAddressProps { countryDisplayName: string; data: AddressTypeInput; disabled: boolean; - errors: FormErrors; + errors: UserError[]; onChange(event: React.ChangeEvent); onCountryChange(event: React.ChangeEvent); } diff --git a/src/customers/components/CustomerCreateDetails/CustomerCreateDetails.tsx b/src/customers/components/CustomerCreateDetails/CustomerCreateDetails.tsx index 9d41fd203..f4d8f44ac 100644 --- a/src/customers/components/CustomerCreateDetails/CustomerCreateDetails.tsx +++ b/src/customers/components/CustomerCreateDetails/CustomerCreateDetails.tsx @@ -7,7 +7,8 @@ import { useIntl } from "react-intl"; import CardTitle from "@saleor/components/CardTitle"; import { commonMessages } from "@saleor/intl"; -import { FormErrors } from "../../../types"; +import { getFieldError } from "@saleor/utils/errors"; +import { UserError } from "../../../types"; import { CustomerCreatePageFormData } from "../CustomerCreatePage"; const useStyles = makeStyles( @@ -25,7 +26,7 @@ const useStyles = makeStyles( export interface CustomerCreateDetailsProps { data: CustomerCreatePageFormData; disabled: boolean; - errors: FormErrors<"customerFirstName" | "customerLastName" | "email">; + errors: UserError[]; onChange: (event: React.ChangeEvent) => void; } @@ -47,33 +48,33 @@ const CustomerCreateDetails: React.FC = props => {
; + errors: UserError[]; onChange: (event: React.ChangeEvent) => void; } @@ -42,11 +42,11 @@ const CustomerCreateNote: React.FC = ({ = ({ countries, disabled, - errors, + errors: apiErrors, saveButtonBar, onBack, onSubmit @@ -98,6 +98,8 @@ const CustomerCreatePage: React.FC = ({ }) ); + const errors = [...apiErrors, ...validationErrors]; + const handleSubmit = ( formData: CustomerCreatePageFormData & AddressTypeInput ) => { @@ -130,13 +132,8 @@ const CustomerCreatePage: React.FC = ({ }; return ( - - {({ change, data, errors: formErrors, hasChanged, submit }) => { + + {({ change, data, hasChanged, submit }) => { const handleCountrySelect = createSingleAutocompleteSelectHandler( change, setCountryDisplayName, @@ -159,7 +156,7 @@ const CustomerCreatePage: React.FC = ({ @@ -168,7 +165,7 @@ const CustomerCreatePage: React.FC = ({ countryDisplayName={countryDisplayName} data={data} disabled={disabled} - errors={formErrors} + errors={errors} onChange={change} onCountryChange={handleCountrySelect} /> @@ -176,7 +173,7 @@ const CustomerCreatePage: React.FC = ({
diff --git a/src/customers/components/CustomerDetails/CustomerDetails.tsx b/src/customers/components/CustomerDetails/CustomerDetails.tsx index 681aa6bf4..d9b180398 100644 --- a/src/customers/components/CustomerDetails/CustomerDetails.tsx +++ b/src/customers/components/CustomerDetails/CustomerDetails.tsx @@ -11,7 +11,8 @@ import CardTitle from "@saleor/components/CardTitle"; import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox"; import Skeleton from "@saleor/components/Skeleton"; import { maybe } from "@saleor/misc"; -import { FormErrors } from "@saleor/types"; +import { UserError } from "@saleor/types"; +import { getFieldError } from "@saleor/utils/errors"; import { CustomerDetails_user } from "../../types/CustomerDetails"; const useStyles = makeStyles( @@ -39,7 +40,7 @@ export interface CustomerDetailsProps { note: string; }; disabled: boolean; - errors: FormErrors<"isActive" | "note">; + errors: UserError[]; onChange: (event: React.ChangeEvent) => void; } @@ -90,10 +91,10 @@ const CustomerDetails: React.FC = props => { /> = ({ return ( customer.email, ""), firstName: maybe(() => customer.firstName, ""), @@ -67,7 +66,7 @@ const CustomerDetailsPage: React.FC = ({ onSubmit={onSubmit} confirmLeave > - {({ change, data, errors: formErrors, hasChanged, submit }) => ( + {({ change, data, hasChanged, submit }) => ( {intl.formatMessage(sectionNames.customers)} @@ -79,14 +78,14 @@ const CustomerDetailsPage: React.FC = ({ customer={customer} data={data} disabled={disabled} - errors={formErrors} + errors={errors} onChange={change} /> diff --git a/src/customers/components/CustomerInfo/CustomerInfo.tsx b/src/customers/components/CustomerInfo/CustomerInfo.tsx index e292bbbb6..9f8cdb596 100644 --- a/src/customers/components/CustomerInfo/CustomerInfo.tsx +++ b/src/customers/components/CustomerInfo/CustomerInfo.tsx @@ -10,6 +10,8 @@ import CardTitle from "@saleor/components/CardTitle"; import Grid from "@saleor/components/Grid"; import Hr from "@saleor/components/Hr"; import { commonMessages } from "@saleor/intl"; +import { UserError } from "@saleor/types"; +import { getFieldError } from "@saleor/utils/errors"; const useStyles = makeStyles( theme => ({ @@ -33,11 +35,7 @@ export interface CustomerInfoProps { email: string; }; disabled: boolean; - errors: { - firstName?: string; - lastName?: string; - email?: string; - }; + errors: UserError[]; onChange: (event: React.ChangeEvent) => void; } @@ -64,9 +62,9 @@ const CustomerInfo: React.FC = props => { = props => { /> = props => { ; + errors: UserError[]; onChange: (event: React.ChangeEvent) => void; } @@ -44,8 +45,8 @@ const DiscountDates = ({ = ({ value: "" }; return ( - - {({ change, data, errors: formErrors, hasChanged, submit }) => ( + + {({ change, data, hasChanged, submit }) => ( {intl.formatMessage(sectionNames.sales)} @@ -74,7 +74,7 @@ const SaleCreatePage: React.FC = ({ @@ -84,7 +84,7 @@ const SaleCreatePage: React.FC = ({ data={data} disabled={disabled} defaultCurrency={defaultCurrency} - errors={formErrors} + errors={errors} onChange={change} />
diff --git a/src/discounts/components/SaleDetailsPage/SaleDetailsPage.tsx b/src/discounts/components/SaleDetailsPage/SaleDetailsPage.tsx index 3d15d501a..2f8d660e6 100644 --- a/src/discounts/components/SaleDetailsPage/SaleDetailsPage.tsx +++ b/src/discounts/components/SaleDetailsPage/SaleDetailsPage.tsx @@ -121,8 +121,8 @@ const SaleDetailsPage: React.FC = ({ value: maybe(() => sale.value.toString(), "") }; return ( - - {({ change, data, errors: formErrors, hasChanged, submit }) => ( + + {({ change, data, hasChanged, submit }) => ( {intl.formatMessage(sectionNames.sales)} @@ -133,7 +133,7 @@ const SaleDetailsPage: React.FC = ({ @@ -143,7 +143,7 @@ const SaleDetailsPage: React.FC = ({ currencySymbol={defaultCurrency} data={data} disabled={disabled} - errors={formErrors} + errors={errors} onChange={change} /> @@ -258,7 +258,7 @@ const SaleDetailsPage: React.FC = ({ data={data} disabled={disabled} defaultCurrency={defaultCurrency} - errors={formErrors} + errors={errors} onChange={change} />
diff --git a/src/discounts/components/SaleInfo/SaleInfo.tsx b/src/discounts/components/SaleInfo/SaleInfo.tsx index 5195e8ec3..c1047cca2 100644 --- a/src/discounts/components/SaleInfo/SaleInfo.tsx +++ b/src/discounts/components/SaleInfo/SaleInfo.tsx @@ -6,14 +6,14 @@ import { useIntl } from "react-intl"; import CardTitle from "@saleor/components/CardTitle"; import { commonMessages } from "@saleor/intl"; +import { UserError } from "@saleor/types"; +import { getFieldError } from "@saleor/utils/errors"; import { FormData } from "../SaleDetailsPage"; export interface SaleInfoProps { data: FormData; disabled: boolean; - errors: { - name?: string; - }; + errors: UserError[]; onChange: (event: React.ChangeEvent) => void; } @@ -33,8 +33,8 @@ const SaleInfo: React.FC = ({ ; + errors: UserError[]; onChange: FormChange; } @@ -43,12 +44,12 @@ const SaleValue: React.FC = ({ defaultMessage: "Discount Value", description: "sale discount" })} - error={!!errors.value} + error={!!getFieldError(errors, "value")} name="value" InputProps={{ endAdornment: data.type === SaleType.FIXED ? currencySymbol : "%" }} - helperText={errors.value} + helperText={getFieldError(errors, "value")?.message} value={data.value} onChange={onChange} /> diff --git a/src/discounts/components/VoucherCreatePage/VoucherCreatePage.tsx b/src/discounts/components/VoucherCreatePage/VoucherCreatePage.tsx index 91880a072..83a3c4958 100644 --- a/src/discounts/components/VoucherCreatePage/VoucherCreatePage.tsx +++ b/src/discounts/components/VoucherCreatePage/VoucherCreatePage.tsx @@ -81,8 +81,8 @@ const VoucherCreatePage: React.FC = ({ }; return ( - - {({ change, data, errors: formErrors, hasChanged, submit }) => ( + + {({ change, data, hasChanged, submit }) => ( {intl.formatMessage(sectionNames.vouchers)} @@ -97,7 +97,7 @@ const VoucherCreatePage: React.FC = ({
= ({ {data.discountType.toString() !== "SHIPPING" ? ( @@ -114,7 +114,7 @@ const VoucherCreatePage: React.FC = ({ data={data} disabled={disabled} defaultCurrency={defaultCurrency} - errors={formErrors} + errors={errors} onChange={change} variant="create" /> @@ -124,7 +124,7 @@ const VoucherCreatePage: React.FC = ({ data={data} disabled={disabled} defaultCurrency={defaultCurrency} - errors={formErrors} + errors={errors} onChange={change} /> @@ -132,7 +132,7 @@ const VoucherCreatePage: React.FC = ({ data={data} disabled={disabled} defaultCurrency={defaultCurrency} - errors={formErrors} + errors={errors} onChange={change} /> @@ -140,7 +140,7 @@ const VoucherCreatePage: React.FC = ({ data={data} disabled={disabled} defaultCurrency={defaultCurrency} - errors={formErrors} + errors={errors} onChange={change} />
diff --git a/src/discounts/components/VoucherDates/VoucherDates.tsx b/src/discounts/components/VoucherDates/VoucherDates.tsx index 92d3bf149..f687e357a 100644 --- a/src/discounts/components/VoucherDates/VoucherDates.tsx +++ b/src/discounts/components/VoucherDates/VoucherDates.tsx @@ -8,14 +8,15 @@ import CardTitle from "@saleor/components/CardTitle"; import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox"; import Grid from "@saleor/components/Grid"; import { commonMessages } from "@saleor/intl"; -import { FormErrors } from "../../../types"; +import { getFieldError } from "@saleor/utils/errors"; +import { UserError } from "../../../types"; import { FormData } from "../VoucherDetailsPage"; interface VoucherDatesProps { data: FormData; defaultCurrency: string; disabled: boolean; - errors: FormErrors<"endDate" | "startDate">; + errors: UserError[]; onChange: (event: React.ChangeEvent) => void; } @@ -39,8 +40,8 @@ const VoucherDates = ({ = ({ }; return ( - - {({ change, data, errors: formErrors, hasChanged, submit }) => ( + + {({ change, data, hasChanged, submit }) => ( {intl.formatMessage(sectionNames.vouchers)} @@ -178,7 +178,7 @@ const VoucherDetailsPage: React.FC = ({ @@ -186,7 +186,7 @@ const VoucherDetailsPage: React.FC = ({ @@ -195,7 +195,7 @@ const VoucherDetailsPage: React.FC = ({ data={data} disabled={disabled} defaultCurrency={defaultCurrency} - errors={formErrors} + errors={errors} onChange={change} variant="update" /> @@ -337,7 +337,7 @@ const VoucherDetailsPage: React.FC = ({ data={data} disabled={disabled} defaultCurrency={defaultCurrency} - errors={formErrors} + errors={errors} onChange={change} /> @@ -345,7 +345,7 @@ const VoucherDetailsPage: React.FC = ({ data={data} disabled={disabled} defaultCurrency={defaultCurrency} - errors={formErrors} + errors={errors} onChange={change} /> @@ -353,7 +353,7 @@ const VoucherDetailsPage: React.FC = ({ data={data} disabled={disabled} defaultCurrency={defaultCurrency} - errors={formErrors} + errors={errors} onChange={change} />
diff --git a/src/discounts/components/VoucherInfo/VoucherInfo.tsx b/src/discounts/components/VoucherInfo/VoucherInfo.tsx index 0251afac7..942b9ca48 100644 --- a/src/discounts/components/VoucherInfo/VoucherInfo.tsx +++ b/src/discounts/components/VoucherInfo/VoucherInfo.tsx @@ -7,13 +7,14 @@ import { FormattedMessage, useIntl } from "react-intl"; import Button from "@material-ui/core/Button"; import CardTitle from "@saleor/components/CardTitle"; import { commonMessages } from "@saleor/intl"; +import { getFieldError } from "@saleor/utils/errors"; import { generateCode } from "../../../misc"; -import { FormErrors } from "../../../types"; +import { UserError } from "../../../types"; import { FormData } from "../VoucherDetailsPage"; interface VoucherInfoProps { data: FormData; - errors: FormErrors<"code">; + errors: UserError[]; disabled: boolean; variant: "create" | "update"; onChange: (event: any) => void; @@ -54,9 +55,9 @@ const VoucherInfo = ({ ; + errors: UserError[]; onChange: (event: React.ChangeEvent) => void; } @@ -46,8 +47,8 @@ const VoucherLimits = ({ {data.hasUsageLimit && ( ; + errors: UserError[]; onChange: (event: React.ChangeEvent) => void; } @@ -76,8 +77,8 @@ const VoucherRequirements = ({ {data.requirementsPicker === RequirementsPicker.ORDER ? ( ; + errors: UserError[]; disabled: boolean; onChange: (event: React.ChangeEvent) => void; } @@ -62,8 +63,8 @@ const VoucherTypes = ({ ; + errors: UserError[]; disabled: boolean; variant: string; onChange: (event: React.ChangeEvent) => void; @@ -64,7 +65,7 @@ const VoucherValue: React.FC = props => { = props => { name: "discountType" as keyof FormData, values: null }} - helperText={errors.discountValue} + helperText={getFieldError(errors, "discountValue")?.message} name={"value" as keyof FormData} onChange={onChange} label={intl.formatMessage({ @@ -93,8 +94,8 @@ const VoucherValue: React.FC = props => { void) => void; export interface UseFormResult { change: FormChange; data: T; - errors: Record; hasChanged: boolean; reset: () => void; set: (data: T) => void; @@ -26,21 +24,6 @@ export interface UseFormResult { toggleValue: FormChange; } -function parseErrors(errors: UserError[]): Record { - return errors - ? errors.reduce( - (acc, curr) => - curr.field - ? { - ...acc, - [curr.field.split(":")[0]]: curr.message - } - : acc, - {} - ) - : {}; -} - type FormData = Record; function merge(prevData: T, prevState: T, data: T): T { @@ -68,7 +51,6 @@ function handleRefresh( function useForm( initial: T, - errors: UserError[], onSubmit: (data: T) => void ): UseFormResult { const [hasChanged, setChanged] = useState(false); @@ -135,7 +117,6 @@ function useForm( return { change, data, - errors: parseErrors(errors), hasChanged, reset, set, diff --git a/src/navigation/components/MenuCreateDialog/MenuCreateDialog.tsx b/src/navigation/components/MenuCreateDialog/MenuCreateDialog.tsx index 2de0f1611..c9e2ea9f8 100644 --- a/src/navigation/components/MenuCreateDialog/MenuCreateDialog.tsx +++ b/src/navigation/components/MenuCreateDialog/MenuCreateDialog.tsx @@ -12,6 +12,8 @@ import ConfirmButton, { } from "@saleor/components/ConfirmButton"; import Form from "@saleor/components/Form"; import { buttonMessages } from "@saleor/intl"; +import { getFieldError } from "@saleor/utils/errors"; +import { UserError } from "@saleor/types"; export interface MenuCreateDialogFormData { name: string; @@ -20,6 +22,7 @@ export interface MenuCreateDialogFormData { export interface MenuCreateDialogProps { confirmButtonState: ConfirmButtonTransitionState; disabled: boolean; + errors: UserError[]; open: boolean; onClose: () => void; onConfirm: (data: MenuCreateDialogFormData) => void; @@ -32,6 +35,7 @@ const initialForm: MenuCreateDialogFormData = { const MenuCreateDialog: React.FC = ({ confirmButtonState, disabled, + errors, onClose, onConfirm, open @@ -48,14 +52,14 @@ const MenuCreateDialog: React.FC = ({ /> - {({ change, data, errors: formErrors, submit }) => ( + {({ change, data, submit }) => ( <> = ({ } name="name" error={!!getFieldError(errors, "name")} - helperText={getFieldError(errors, "name")} + helperText={getFieldError(errors, "name")?.message} /> = ({ loading={loading} options={options} error={!!idError} - helperText={idError} + helperText={idError?.message} placeholder={intl.formatMessage({ defaultMessage: "Start typing to begin search...", id: "menuItemDialogLinkPlaceholder" diff --git a/src/navigation/views/MenuList/MenuList.tsx b/src/navigation/views/MenuList/MenuList.tsx index a0f37c477..0b303d83d 100644 --- a/src/navigation/views/MenuList/MenuList.tsx +++ b/src/navigation/views/MenuList/MenuList.tsx @@ -174,6 +174,7 @@ const MenuList: React.FC = ({ params }) => { open={params.action === "add"} confirmButtonState={menuCreateOpts.status} disabled={menuCreateOpts.loading} + errors={menuCreateOpts?.data?.menuCreate.errors || []} onClose={closeModal} onConfirm={formData => menuCreate({ diff --git a/src/orders/components/OrderAddressEditDialog/OrderAddressEditDialog.tsx b/src/orders/components/OrderAddressEditDialog/OrderAddressEditDialog.tsx index 7e6b861f3..5e995e757 100644 --- a/src/orders/components/OrderAddressEditDialog/OrderAddressEditDialog.tsx +++ b/src/orders/components/OrderAddressEditDialog/OrderAddressEditDialog.tsx @@ -80,8 +80,8 @@ const OrderAddressEditDialog: React.FC = props => { return ( - - {({ change, data, errors, submit }) => { + + {({ change, data, submit }) => { const handleCountrySelect = createSingleAutocompleteSelectHandler( change, setCountryDisplayName, @@ -106,7 +106,7 @@ const OrderAddressEditDialog: React.FC = props => { countries={countryChoices} countryDisplayValue={countryDisplayName} data={data} - errors={errors} + errors={dialogErrors} onChange={change} onCountryChange={handleCountrySelect} /> diff --git a/src/pages/components/PageDetailsPage/PageDetailsPage.tsx b/src/pages/components/PageDetailsPage/PageDetailsPage.tsx index d59e96026..c00740470 100644 --- a/src/pages/components/PageDetailsPage/PageDetailsPage.tsx +++ b/src/pages/components/PageDetailsPage/PageDetailsPage.tsx @@ -70,8 +70,8 @@ const PageDetailsPage: React.FC = ({ title: maybe(() => page.title, "") }; return ( - - {({ change, data, errors: formErrors, hasChanged, submit }) => ( + + {({ change, data, hasChanged, submit }) => ( {intl.formatMessage(sectionNames.pages)} @@ -91,7 +91,7 @@ const PageDetailsPage: React.FC = ({ @@ -119,13 +119,13 @@ const PageDetailsPage: React.FC = ({ ; + errors: UserError[]; page: PageDetails_page; onChange: (event: React.ChangeEvent) => void; } @@ -45,9 +46,9 @@ const PageInfo: React.FC = props => { = props => { JSON.parse(page.contentJson))} label={intl.formatMessage({ defaultMessage: "Content", diff --git a/src/pages/components/PageSlug/PageSlug.tsx b/src/pages/components/PageSlug/PageSlug.tsx index fe879893b..660adadd9 100644 --- a/src/pages/components/PageSlug/PageSlug.tsx +++ b/src/pages/components/PageSlug/PageSlug.tsx @@ -6,12 +6,14 @@ import { useIntl } from "react-intl"; import slugify from "slugify"; import CardTitle from "@saleor/components/CardTitle"; +import { UserError } from "@saleor/types"; +import { getFieldError } from "@saleor/utils/errors"; import { FormData } from "../PageDetailsPage"; export interface PageSlugProps { data: FormData; disabled: boolean; - errors: Partial>; + errors: UserError[]; onChange: (event: React.ChangeEvent) => void; } @@ -34,13 +36,13 @@ const PageSlug: React.FC = ({ ; + errors: UserError[]; disabled: boolean; onChange: (event: React.ChangeEvent) => void; fields: Plugin_plugin_configuration[]; @@ -84,7 +85,7 @@ const PluginSettings: React.FC = ({ ) : ( = props => { }; return ( - - {({ data, errors, hasChanged, submit, set, triggerChange }) => { + + {({ data, hasChanged, submit, set, triggerChange }) => { const onChange = (event: ChangeEvent) => { const { name, value } = event.target; const newData = { diff --git a/src/productTypes/components/ProductTypeAttributeEditDialog/ProductTypeAttributeEditDialog.tsx b/src/productTypes/components/ProductTypeAttributeEditDialog/ProductTypeAttributeEditDialog.tsx index e92ac1bb8..6a444d2f8 100644 --- a/src/productTypes/components/ProductTypeAttributeEditDialog/ProductTypeAttributeEditDialog.tsx +++ b/src/productTypes/components/ProductTypeAttributeEditDialog/ProductTypeAttributeEditDialog.tsx @@ -11,6 +11,8 @@ import Form from "@saleor/components/Form"; import { FormSpacer } from "@saleor/components/FormSpacer"; import ListField from "@saleor/components/ListField"; import { buttonMessages } from "@saleor/intl"; +import { UserError } from "@saleor/types"; +import { getFieldError } from "@saleor/utils/errors"; export interface FormData { name: string; @@ -22,10 +24,7 @@ export interface FormData { export interface ProductTypeAttributeEditDialogProps { disabled: boolean; - errors: Array<{ - field: string; - message: string; - }>; + errors: UserError[]; name: string; opened: boolean; title: string; @@ -37,9 +36,16 @@ export interface ProductTypeAttributeEditDialogProps { onConfirm: (data: FormData) => void; } -const ProductTypeAttributeEditDialog: React.FC< - ProductTypeAttributeEditDialogProps -> = ({ disabled, errors, name, opened, title, values, onClose, onConfirm }) => { +const ProductTypeAttributeEditDialog: React.FC = ({ + disabled, + errors, + name, + opened, + title, + values, + onClose, + onConfirm +}) => { const intl = useIntl(); const initialForm: FormData = { @@ -48,19 +54,19 @@ const ProductTypeAttributeEditDialog: React.FC< }; return ( - - {({ change, data, errors: formErrors }) => ( + + {({ change, data }) => ( <> {title} = ({ const [taxTypeDisplayName, setTaxTypeDisplayName] = useStateFromProps(""); return ( - - {({ change, data, errors: formErrors, hasChanged, submit }) => ( + + {({ change, data, hasChanged, submit }) => ( {intl.formatMessage(sectionNames.productTypes)} @@ -87,7 +82,7 @@ const ProductTypeCreatePage: React.FC = ({ diff --git a/src/productTypes/components/ProductTypeDetails/ProductTypeDetails.tsx b/src/productTypes/components/ProductTypeDetails/ProductTypeDetails.tsx index ef59b0294..758fd36e6 100644 --- a/src/productTypes/components/ProductTypeDetails/ProductTypeDetails.tsx +++ b/src/productTypes/components/ProductTypeDetails/ProductTypeDetails.tsx @@ -7,7 +7,8 @@ import { useIntl } from "react-intl"; import CardTitle from "@saleor/components/CardTitle"; import { commonMessages } from "@saleor/intl"; -import { FormErrors } from "@saleor/types"; +import { UserError } from "@saleor/types"; +import { getFieldError } from "@saleor/utils/errors"; const useStyles = makeStyles( { @@ -23,7 +24,7 @@ interface ProductTypeDetailsProps { name: string; }; disabled: boolean; - errors: FormErrors<"name">; + errors: UserError[]; onChange: (event: React.ChangeEvent) => void; } @@ -41,9 +42,9 @@ const ProductTypeDetails: React.FC = props => { = ({ weight: maybe(() => productType.weight.value) }; return ( - - {({ change, data, errors: formErrors, hasChanged, submit }) => ( + + {({ change, data, hasChanged, submit }) => ( {intl.formatMessage(sectionNames.productTypes)} @@ -140,7 +135,7 @@ const ProductTypeDetailsPage: React.FC = ({ diff --git a/src/products/components/ProductCreatePage/ProductCreatePage.tsx b/src/products/components/ProductCreatePage/ProductCreatePage.tsx index af5293613..10c543699 100644 --- a/src/products/components/ProductCreatePage/ProductCreatePage.tsx +++ b/src/products/components/ProductCreatePage/ProductCreatePage.tsx @@ -90,7 +90,7 @@ export const ProductCreatePage: React.FC = ({ disabled, categories: categoryChoiceList, collections: collectionChoiceList, - errors: userErrors, + errors, fetchCategories, fetchCollections, fetchMoreCategories, @@ -162,21 +162,8 @@ export const ProductCreatePage: React.FC = ({ }); return ( - - {({ - change, - data, - errors, - hasChanged, - submit, - triggerChange, - toggleValue - }) => { + + {({ change, data, hasChanged, submit, triggerChange, toggleValue }) => { const handleCollectionSelect = createMultiAutocompleteSelectHandler( toggleValue, setSelectedCollections, diff --git a/src/products/components/ProductDetailsForm/ProductDetailsForm.tsx b/src/products/components/ProductDetailsForm/ProductDetailsForm.tsx index 60797e7a3..3ade54592 100644 --- a/src/products/components/ProductDetailsForm/ProductDetailsForm.tsx +++ b/src/products/components/ProductDetailsForm/ProductDetailsForm.tsx @@ -9,6 +9,8 @@ import CardTitle from "@saleor/components/CardTitle"; import FormSpacer from "@saleor/components/FormSpacer"; import RichTextEditor from "@saleor/components/RichTextEditor"; import { commonMessages } from "@saleor/intl"; +import { UserError } from "@saleor/types"; +import { getFieldError } from "@saleor/utils/errors"; interface ProductDetailsFormProps { data: { @@ -16,7 +18,7 @@ interface ProductDetailsFormProps { name: string; }; disabled?: boolean; - errors: { [key: string]: string }; + errors: UserError[]; // Draftail isn't controlled - it needs only initial input // because it's autosaving on its own. // Ref https://github.com/mirumee/saleor/issues/4470 @@ -40,8 +42,8 @@ export const ProductDetailsForm: React.FC = ({ /> = ({ ; + errors: UserError[]; productType?: ProductType; productTypeInputDisplayValue?: string; productTypes?: SingleAutocompleteChoiceType[]; @@ -73,7 +74,6 @@ const ProductOrganization: React.FC = props => { canChangeType, categories, categoryInputDisplayValue, - collections, collectionsInputDisplayValue, data, @@ -108,8 +108,8 @@ const ProductOrganization: React.FC = props => { {canChangeType ? ( = props => { ) => void; } @@ -56,8 +58,8 @@ const ProductStock: React.FC = props => { })} value={data.sku} onChange={onChange} - error={!!errors.sku} - helperText={errors.sku} + error={!!getFieldError(errors, "sku")} + helperText={getFieldError(errors, "sku")?.message} /> = ({ disabled, categories: categoryChoiceList, collections: collectionChoiceList, - errors: userErrors, + errors, fetchCategories, fetchCollections, fetchMoreCategories, @@ -152,21 +152,8 @@ export const ProductUpdatePage: React.FC = ({ }); return ( - - {({ - change, - data, - errors, - hasChanged, - submit, - triggerChange, - toggleValue - }) => { + + {({ change, data, hasChanged, submit, triggerChange, toggleValue }) => { const handleCollectionSelect = createMultiAutocompleteSelectHandler( toggleValue, setSelectedCollections, diff --git a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx index 68be2da0b..eb6449f09 100644 --- a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx +++ b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx @@ -51,7 +51,7 @@ interface ProductVariantCreatePageProps { const ProductVariantCreatePage: React.FC = ({ currencySymbol, - errors: apiErrors, + errors, loading, header, product, @@ -93,8 +93,8 @@ const ProductVariantCreatePage: React.FC = ({ }); return ( - - {({ change, data, errors, hasChanged, submit, triggerChange }) => { + + {({ change, data, hasChanged, submit, triggerChange }) => { const handleAttributeChange: FormsetChange = (id, value) => { changeAttributeData(id, value); triggerChange(); @@ -120,7 +120,7 @@ const ProductVariantCreatePage: React.FC = ({ diff --git a/src/products/components/ProductVariantPage/ProductVariantPage.tsx b/src/products/components/ProductVariantPage/ProductVariantPage.tsx index f083d4667..8f7ca76ed 100644 --- a/src/products/components/ProductVariantPage/ProductVariantPage.tsx +++ b/src/products/components/ProductVariantPage/ProductVariantPage.tsx @@ -53,7 +53,7 @@ interface ProductVariantPageProps { } const ProductVariantPage: React.FC = ({ - errors: apiErrors, + errors, loading, header, placeholderImage, @@ -109,13 +109,8 @@ const ProductVariantPage: React.FC = ({ {maybe(() => variant.product.name)} - - {({ change, data, errors, hasChanged, submit, triggerChange }) => { + + {({ change, data, hasChanged, submit, triggerChange }) => { const handleAttributeChange: FormsetChange = (id, value) => { changeAttributeData(id, value); triggerChange(); @@ -143,7 +138,7 @@ const ProductVariantPage: React.FC = ({ diff --git a/src/products/components/ProductVariantPrice/ProductVariantPrice.tsx b/src/products/components/ProductVariantPrice/ProductVariantPrice.tsx index 81de4559e..057985ddd 100644 --- a/src/products/components/ProductVariantPrice/ProductVariantPrice.tsx +++ b/src/products/components/ProductVariantPrice/ProductVariantPrice.tsx @@ -6,6 +6,8 @@ import { useIntl } from "react-intl"; import CardTitle from "@saleor/components/CardTitle"; import PriceField from "@saleor/components/PriceField"; +import { UserError } from "@saleor/types"; +import { getFieldError } from "@saleor/utils/errors"; const useStyles = makeStyles( theme => ({ @@ -22,7 +24,7 @@ interface ProductVariantPriceProps { currencySymbol?: string; priceOverride?: string; costPrice?: string; - errors: { [key: string]: string }; + errors: UserError[]; loading?: boolean; onChange(event: any); } @@ -52,19 +54,18 @@ const ProductVariantPrice: React.FC = props => {
= props => {
({ @@ -19,10 +21,7 @@ const useStyles = makeStyles( ); interface ProductVariantStockProps { - errors: { - quantity?: string; - sku?: string; - }; + errors: UserError[]; sku: string; quantity: string; stockAllocated?: number; @@ -48,7 +47,7 @@ const ProductVariantStock: React.FC = props => {
= props => { description: "product variant stock" })} helperText={ - errors.quantity - ? errors.quantity + getFieldError(errors, "quantity") + ? getFieldError(errors, "quantity") : !!stockAllocated ? intl.formatMessage( { @@ -77,8 +76,8 @@ const ProductVariantStock: React.FC = props => {
= props => { const { disabled, - errors: formErrors, + errors, permissions, saveButtonBarState, onBack, @@ -50,13 +50,8 @@ const ServiceCreatePage: React.FC = props => { permissions: [] }; return ( - - {({ data, change, errors, hasChanged, submit }) => ( + + {({ data, change, hasChanged, submit }) => ( {intl.formatMessage(sectionNames.serviceAccounts)} diff --git a/src/services/components/ServiceDetailsPage/ServiceDetailsPage.tsx b/src/services/components/ServiceDetailsPage/ServiceDetailsPage.tsx index e0db04a4b..f2f658356 100644 --- a/src/services/components/ServiceDetailsPage/ServiceDetailsPage.tsx +++ b/src/services/components/ServiceDetailsPage/ServiceDetailsPage.tsx @@ -48,7 +48,7 @@ const ServiceDetailsPage: React.FC = props => { const { apiUri, disabled, - errors: formErrors, + errors, permissions, saveButtonBarState, service, @@ -79,13 +79,8 @@ const ServiceDetailsPage: React.FC = props => { permissions: maybe(() => service.permissions, []).map(perm => perm.code) }; return ( - - {({ data, change, errors, hasChanged, submit }) => ( + + {({ data, change, hasChanged, submit }) => ( {intl.formatMessage(sectionNames.serviceAccounts)} diff --git a/src/services/components/ServiceInfo/ServiceInfo.tsx b/src/services/components/ServiceInfo/ServiceInfo.tsx index 88ad3ee51..2b43c5600 100644 --- a/src/services/components/ServiceInfo/ServiceInfo.tsx +++ b/src/services/components/ServiceInfo/ServiceInfo.tsx @@ -6,14 +6,15 @@ import { useIntl } from "react-intl"; import CardTitle from "@saleor/components/CardTitle"; import { FormChange } from "@saleor/hooks/useForm"; -import { FormErrors } from "@saleor/types"; +import { UserError } from "@saleor/types"; +import { getFieldError } from "@saleor/utils/errors"; export interface ServiceInfoProps { data: { name: string; }; disabled: boolean; - errors: FormErrors<"name">; + errors: UserError[]; onChange: FormChange; } @@ -32,12 +33,12 @@ const ServiceInfo: React.FC = props => { = ({ }; return ( - - {({ change, data, errors: formErrors, hasChanged, submit }) => ( + + {({ change, data, hasChanged, submit }) => ( <> @@ -67,7 +67,7 @@ const ShippingZoneCreatePage: React.FC = ({
diff --git a/src/shipping/components/ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx b/src/shipping/components/ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx index 7100f4d27..1ef691f76 100644 --- a/src/shipping/components/ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx +++ b/src/shipping/components/ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx @@ -57,11 +57,11 @@ const ShippingZoneDetailsPage: React.FC = ({ const intl = useIntl(); const initialForm: FormData = { - name: maybe(() => shippingZone.name, "") + name: shippingZone?.name || "" }; return ( - - {({ change, data, errors: formErrors, hasChanged, submit }) => ( + + {({ change, data, hasChanged, submit }) => ( @@ -69,11 +69,7 @@ const ShippingZoneDetailsPage: React.FC = ({ shippingZone.name)} />
- + shippingZone.countries)} diff --git a/src/shipping/components/ShippingZoneInfo/ShippingZoneInfo.tsx b/src/shipping/components/ShippingZoneInfo/ShippingZoneInfo.tsx index 7e75e4598..128e2b51c 100644 --- a/src/shipping/components/ShippingZoneInfo/ShippingZoneInfo.tsx +++ b/src/shipping/components/ShippingZoneInfo/ShippingZoneInfo.tsx @@ -6,12 +6,13 @@ import { useIntl } from "react-intl"; import CardTitle from "@saleor/components/CardTitle"; import { commonMessages } from "@saleor/intl"; -import { FormErrors } from "../../../types"; +import { getFieldError } from "@saleor/utils/errors"; +import { UserError } from "../../../types"; import { FormData } from "../ShippingZoneDetailsPage"; export interface ShippingZoneInfoProps { data: FormData; - errors: FormErrors<"name">; + errors: UserError[]; onChange: (event: React.ChangeEvent) => void; } @@ -29,9 +30,9 @@ const ShippingZoneInfo: React.FC = ({ /> = props => { return ( - - {({ change, data, errors: formErrors, hasChanged }) => { - const typedFormErrors: FormErrors< - | "minimumOrderPrice" - | "minimumOrderWeight" - | "maximumOrderPrice" - | "maximumOrderWeight" - | "price" - | "name" - > = formErrors; - return ( - <> - - {variant === ShippingMethodTypeEnum.PRICE - ? action === "create" - ? intl.formatMessage({ - defaultMessage: "Add Price Rate", - description: "dialog header" - }) - : intl.formatMessage({ - defaultMessage: "Edit Price Rate", - description: "dialog header" - }) - : action === "create" + + {({ change, data, hasChanged }) => ( + <> + + {variant === ShippingMethodTypeEnum.PRICE + ? action === "create" ? intl.formatMessage({ - defaultMessage: "Add Weight Rate", - description: - "add weight based shipping method, dialog header" + defaultMessage: "Add Price Rate", + description: "dialog header" }) : intl.formatMessage({ - defaultMessage: "Edit Weight Rate", - description: - "edit weight based shipping method, dialog header" - })} - - - - -
- - {!!variant ? ( - <> - - {variant === ShippingMethodTypeEnum.PRICE - ? intl.formatMessage({ - defaultMessage: "Value range", - description: "order price range" - }) - : intl.formatMessage({ - defaultMessage: "Weight range", - description: "order weight range" - })} - - - - - {variant === ShippingMethodTypeEnum.PRICE - ? intl.formatMessage({ - defaultMessage: - "This rate will apply to all orders of all prices" - }) - : intl.formatMessage({ - defaultMessage: - "This rate will apply to all orders of all weights" - })} - - - } - checked={data.noLimits} - onChange={change} - disabled={disabled} - /> - {!data.noLimits && ( - <> - -
- - -
- - )} - - ) : ( - - )} -
-
- - - - - - {!data.isFree && ( - <> - -
- + + + +
+ + {!!variant ? ( + <> + + {variant === ShippingMethodTypeEnum.PRICE + ? intl.formatMessage({ + defaultMessage: "Value range", + description: "order price range" + }) + : intl.formatMessage({ + defaultMessage: "Weight range", + description: "order weight range" })} - name={"price" as keyof FormData} - type="number" - value={data.price} - onChange={change} - InputProps={{ - endAdornment: defaultCurrency - }} - /> -
- - )} -
- - - - {action === "create" - ? intl.formatMessage({ - defaultMessage: "Create rate", - description: "button" - }) - : intl.formatMessage({ - defaultMessage: "Update rate", - description: "button" + + + + + {variant === ShippingMethodTypeEnum.PRICE + ? intl.formatMessage({ + defaultMessage: + "This rate will apply to all orders of all prices" + }) + : intl.formatMessage({ + defaultMessage: + "This rate will apply to all orders of all weights" + })} + + + } + checked={data.noLimits} + onChange={change} + disabled={disabled} + /> + {!data.noLimits && ( + <> + +
+ + +
+ + )} + + ) : ( + + )} + +
+ + + + + + {!data.isFree && ( + <> + +
+ - - - ); - }} + name={"price" as keyof FormData} + type="number" + value={data.price} + onChange={change} + InputProps={{ + endAdornment: defaultCurrency + }} + /> +
+ + )} +
+ + + + {action === "create" + ? intl.formatMessage({ + defaultMessage: "Create rate", + description: "button" + }) + : intl.formatMessage({ + defaultMessage: "Update rate", + description: "button" + })} + + + + )}
); diff --git a/src/siteSettings/components/SiteSettingsAddress/SiteSettingsAddress.tsx b/src/siteSettings/components/SiteSettingsAddress/SiteSettingsAddress.tsx index 0e8f9564f..8ab76f48a 100644 --- a/src/siteSettings/components/SiteSettingsAddress/SiteSettingsAddress.tsx +++ b/src/siteSettings/components/SiteSettingsAddress/SiteSettingsAddress.tsx @@ -11,16 +11,16 @@ import Grid from "@saleor/components/Grid"; import SingleAutocompleteSelectField, { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; -import { AddressTypeInput } from "@saleor/customers/types"; import { ChangeEvent } from "@saleor/hooks/useForm"; -import { FormErrors } from "@saleor/types"; +import { UserError } from "@saleor/types"; +import { getFieldError } from "@saleor/utils/errors"; import { SiteSettingsPageFormData } from "../SiteSettingsPage"; interface SiteSettingsAddressProps { countries: SingleAutocompleteChoiceType[]; data: SiteSettingsPageFormData; displayCountry: string; - errors: FormErrors; + errors: UserError[]; disabled: boolean; onChange: (event: ChangeEvent) => void; onCountryChange: (event: ChangeEvent) => void; @@ -60,8 +60,8 @@ const SiteSettingsAddress: React.FC = props => { = props => { = props => { = props => { = props => { /> = props => { = props => { /> = props => { ; + errors: UserError[]; disabled: boolean; onChange: (event: React.ChangeEvent) => void; } @@ -36,14 +34,14 @@ const SiteSettingsDetails: React.FC = ({ = ({ , - Exclude, "children"> - > { - open: boolean; - onClose: () => void; +export interface SiteSettingsKeyDialogProps extends DialogProps { + errors: UserError[]; + initial: SiteSettingsKeyDialogForm; + onSubmit: (data: SiteSettingsKeyDialogForm) => void; } const SiteSettingsKeyDialog: React.FC = ({ @@ -40,8 +39,8 @@ const SiteSettingsKeyDialog: React.FC = ({ return ( -
- {({ change, data, errors }) => ( + + {({ change, data }) => ( <> = ({ label: authorizationKeyTypes[key], value: key }))} - error={!!errors.keyType} + error={!!getFieldError(errors, "keyType")} label={intl.formatMessage({ defaultMessage: "Authentication type", description: "authentication provider name" })} - hint={errors.keyType} + hint={getFieldError(errors, "keyType")?.message} name="type" onChange={change} value={data.type} /> ; + errors: UserError[]; disabled: boolean; onChange: (event: React.ChangeEvent) => void; } @@ -68,27 +65,29 @@ const SiteSettingsMailing: React.FC = props => { = props => { = props => { defaultMessage: "URL address" })} helperText={ - errors.customerSetPasswordUrl || + getFieldError(errors, "customerSetPasswordUrl")?.message || intl.formatMessage({ defaultMessage: "This URL will be used as a main URL for password resets. It will be sent via email." diff --git a/src/siteSettings/components/SiteSettingsPage/SiteSettingsPage.tsx b/src/siteSettings/components/SiteSettingsPage/SiteSettingsPage.tsx index f668ac630..afa1f883e 100644 --- a/src/siteSettings/components/SiteSettingsPage/SiteSettingsPage.tsx +++ b/src/siteSettings/components/SiteSettingsPage/SiteSettingsPage.tsx @@ -127,9 +127,10 @@ const SiteSettingsPage: React.FC = props => { name: maybe(() => shop.name, "") }; + const formErrors = [...errors, ...validationErrors]; + return ( { const submitFunc = areAddressInputFieldsModified(data) @@ -139,8 +140,7 @@ const SiteSettingsPage: React.FC = props => { }} confirmLeave > - {({ change, data, errors: formErrors, hasChanged, submit }) => { - const siteFormErrors = { ...formErrors }; + {({ change, data, hasChanged, submit }) => { const countryChoices = mapCountriesToChoices( maybe(() => shop.countries, []) ); @@ -169,7 +169,7 @@ const SiteSettingsPage: React.FC = props => {
@@ -187,7 +187,7 @@ const SiteSettingsPage: React.FC = props => {
@@ -208,7 +208,7 @@ const SiteSettingsPage: React.FC = props => { data={data} displayCountry={displayCountry} countries={countryChoices} - errors={siteFormErrors} + errors={formErrors} disabled={disabled} onChange={change} onCountryChange={handleCountryChange} diff --git a/src/staff/components/StaffAddMemberDialog/StaffAddMemberDialog.tsx b/src/staff/components/StaffAddMemberDialog/StaffAddMemberDialog.tsx index 1a47685fc..0e694bf3c 100644 --- a/src/staff/components/StaffAddMemberDialog/StaffAddMemberDialog.tsx +++ b/src/staff/components/StaffAddMemberDialog/StaffAddMemberDialog.tsx @@ -16,6 +16,8 @@ import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox"; import Form from "@saleor/components/Form"; import FormSpacer from "@saleor/components/FormSpacer"; import { buttonMessages, commonMessages } from "@saleor/intl"; +import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors"; +import { getFieldError } from "@saleor/utils/errors"; import { UserError } from "../../../types"; export interface FormData { @@ -65,13 +67,14 @@ interface StaffAddMemberDialogProps { const StaffAddMemberDialog: React.FC = props => { const { confirmButtonState, errors, open, onClose, onConfirm } = props; const classes = useStyles(props); + const dialogErrors = useModalDialogErrors(errors, open); const intl = useIntl(); return ( - - {({ change, data, errors: formErrors, hasChanged }) => ( + + {({ change, data, hasChanged }) => ( <> = props => {
= props => { onChange={change} /> = props => {
= ({ confirmButtonState, - errors: apiErrors, + errors, open, onClose, onSubmit }) => { const intl = useIntl(); - const dialogErrors = useModalDialogErrors(apiErrors, open); + const dialogErrors = useModalDialogErrors(errors, open); return ( @@ -49,14 +50,14 @@ const StaffPasswordResetDialog: React.FC = ({ description="dialog header" />
- - {({ change, data, errors, submit }) => ( + + {({ change, data, submit }) => ( <> = ({ /> `; +exports[`Storyshots Navigation / Menu create form errors 1`] = ` +
+`; + exports[`Storyshots Navigation / Menu create loading 1`] = `
`; +exports[`Storyshots Views / Collections / Collection details form errors 1`] = ` +
+ +
+
+
+ Summer collection +
+
+
+
+
+
+
+
+
+ + General Informations + +
+
+
+
+
+
+ +
+ + +
+

+ Generic form error +

+
+
+
+
+
+ Description +
+
+ +
+
+
+
+
+
+ + + bold + + +
+
+
+
+ + + italic + + +
+
+
+
+ + + strikethrough + + +
+
+

+
+ + + h1 + + +
+

+

+
+ + + h2 + + +
+

+

+
+ + + h3 + + +
+

+
+
+ + + blockquote + + +
+
+
    +
  • +
    + + + ul + + +
    +
  • +
+
    +
  1. +
    + + + ol + + +
    +
  2. +
+
+
+ + + link + + +
+
+
+
+
+
+
+
+
+ Generic form error +
+
+
+
+
+
+
+ + Background Image (optional) + +
+ + +
+
+
+
+
+
+
+
+ +
+
+ Alt text +
+
+
+
+
+ +
+ +