Display attribute errors in product view

This commit is contained in:
dominik-zeglen 2020-09-29 16:31:17 +02:00
parent bf295e4dce
commit 369fd09853
4 changed files with 81 additions and 60 deletions

View file

@ -10,6 +10,7 @@ import { ProductErrorCode } from "./../../types/globalTypes";
export interface ProductErrorFragment { export interface ProductErrorFragment {
__typename: "ProductError"; __typename: "ProductError";
attributeId: string | null;
code: ProductErrorCode; code: ProductErrorCode;
field: string | null; field: string | null;
} }

View file

@ -13,10 +13,12 @@ import MultiAutocompleteSelectField, {
import SingleAutocompleteSelectField, { import SingleAutocompleteSelectField, {
SingleAutocompleteChoiceType SingleAutocompleteChoiceType
} from "@saleor/components/SingleAutocompleteSelectField"; } from "@saleor/components/SingleAutocompleteSelectField";
import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment";
import { FormsetAtomicData, FormsetChange } from "@saleor/hooks/useFormset"; import { FormsetAtomicData, FormsetChange } from "@saleor/hooks/useFormset";
import { maybe } from "@saleor/misc"; import { maybe } from "@saleor/misc";
import { ProductDetails_product_attributes_attribute_values } from "@saleor/products/types/ProductDetails"; import { ProductDetails_product_attributes_attribute_values } from "@saleor/products/types/ProductDetails";
import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; import { AttributeInputTypeEnum } from "@saleor/types/globalTypes";
import { getProductErrorMessage } from "@saleor/utils/errors";
import classNames from "classnames"; import classNames from "classnames";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
@ -33,6 +35,7 @@ export type ProductAttributeInput = FormsetAtomicData<
export interface ProductAttributesProps { export interface ProductAttributesProps {
attributes: ProductAttributeInput[]; attributes: ProductAttributeInput[];
disabled: boolean; disabled: boolean;
errors: ProductErrorFragment[];
onChange: FormsetChange; onChange: FormsetChange;
onMultiChange: FormsetChange; onMultiChange: FormsetChange;
} }
@ -125,6 +128,7 @@ function getSingleChoices(
const ProductAttributes: React.FC<ProductAttributesProps> = ({ const ProductAttributes: React.FC<ProductAttributesProps> = ({
attributes, attributes,
disabled, disabled,
errors,
onChange, onChange,
onMultiChange onMultiChange
}) => { }) => {
@ -169,61 +173,71 @@ const ProductAttributes: React.FC<ProductAttributesProps> = ({
{expanded && attributes.length > 0 && ( {expanded && attributes.length > 0 && (
<> <>
<Hr /> <Hr />
{attributes.map((attribute, attributeIndex) => ( {attributes.map((attribute, attributeIndex) => {
<React.Fragment key={attribute.id}> const error = errors.find(
{attributeIndex > 0 && <Hr />} err => err.attributeId === attribute.id
<Grid className={classes.attributeSection} variant="uniform"> );
<div
className={classes.attributeSectionLabel} return (
data-test="product-attribute-label" <React.Fragment key={attribute.id}>
> {attributeIndex > 0 && <Hr />}
<Typography>{attribute.label}</Typography> <Grid className={classes.attributeSection} variant="uniform">
</div> <div
<div data-test="product-attribute-value"> className={classes.attributeSectionLabel}
{attribute.data.inputType === data-test="product-attribute-label"
AttributeInputTypeEnum.DROPDOWN ? ( >
<SingleAutocompleteSelectField <Typography>{attribute.label}</Typography>
choices={getSingleChoices(attribute.data.values)} </div>
disabled={disabled} <div data-test="product-attribute-value">
displayValue={maybe( {attribute.data.inputType ===
() => AttributeInputTypeEnum.DROPDOWN ? (
attribute.data.values.find( <SingleAutocompleteSelectField
value => value.slug === attribute.value[0] choices={getSingleChoices(attribute.data.values)}
).name, disabled={disabled}
attribute.value[0] displayValue={maybe(
)} () =>
emptyOption attribute.data.values.find(
name={`attribute:${attribute.label}`} value => value.slug === attribute.value[0]
label={intl.formatMessage({ ).name,
defaultMessage: "Value", attribute.value[0]
description: "attribute value" )}
})} emptyOption
value={attribute.value[0]} error={!!error}
onChange={event => helperText={getProductErrorMessage(error, intl)}
onChange(attribute.id, event.target.value) name={`attribute:${attribute.label}`}
} label={intl.formatMessage({
allowCustomValues={!attribute.data.isRequired} defaultMessage: "Value",
/> description: "attribute value"
) : ( })}
<MultiAutocompleteSelectField value={attribute.value[0]}
choices={getMultiChoices(attribute.data.values)} onChange={event =>
displayValues={getMultiDisplayValue(attribute)} onChange(attribute.id, event.target.value)
label={intl.formatMessage({ }
defaultMessage: "Values", allowCustomValues={!attribute.data.isRequired}
description: "attribute values" />
})} ) : (
name={`attribute:${attribute.label}`} <MultiAutocompleteSelectField
value={attribute.value} choices={getMultiChoices(attribute.data.values)}
onChange={event => displayValues={getMultiDisplayValue(attribute)}
onMultiChange(attribute.id, event.target.value) error={!!error}
} helperText={getProductErrorMessage(error, intl)}
allowCustomValues={!attribute.data.isRequired} label={intl.formatMessage({
/> defaultMessage: "Values",
)} description: "attribute values"
</div> })}
</Grid> name={`attribute:${attribute.label}`}
</React.Fragment> value={attribute.value}
))} onChange={event =>
onMultiChange(attribute.id, event.target.value)
}
allowCustomValues={!attribute.data.isRequired}
/>
)}
</div>
</Grid>
</React.Fragment>
);
})}
</> </>
)} )}
</CardContent> </CardContent>

View file

@ -296,6 +296,7 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
{attributes.length > 0 && ( {attributes.length > 0 && (
<ProductAttributes <ProductAttributes
attributes={attributes} attributes={attributes}
errors={errors}
disabled={disabled} disabled={disabled}
onChange={handleAttributeChange} onChange={handleAttributeChange}
onMultiChange={handleAttributeMultiChange} onMultiChange={handleAttributeMultiChange}

View file

@ -137,6 +137,7 @@ storiesOf("Views / Products / Product edit", module)
<ProductUpdatePage <ProductUpdatePage
{...props} {...props}
errors={([ errors={([
"attributes",
"basePrice", "basePrice",
"category", "category",
"chargeTaxes", "chargeTaxes",
@ -148,10 +149,14 @@ storiesOf("Views / Products / Product edit", module)
"seoTitle", "seoTitle",
"sku", "sku",
"stockQuantity" "stockQuantity"
] as Array<keyof ProductUpdatePageFormData>).map(field => ({ ] as Array<keyof ProductUpdatePageFormData | "attributes">).map(
__typename: "ProductError", field => ({
code: ProductErrorCode.INVALID, __typename: "ProductError",
field attributeId:
}))} field === "attributes" ? product.attributes[0].attribute.id : null,
code: ProductErrorCode.INVALID,
field
})
)}
/> />
)); ));