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 {
__typename: "ProductError";
attributeId: string | null;
code: ProductErrorCode;
field: string | null;
}

View file

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

View file

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

View file

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