Display attribute errors in product view
This commit is contained in:
parent
bf295e4dce
commit
369fd09853
4 changed files with 81 additions and 60 deletions
|
@ -10,6 +10,7 @@ import { ProductErrorCode } from "./../../types/globalTypes";
|
|||
|
||||
export interface ProductErrorFragment {
|
||||
__typename: "ProductError";
|
||||
attributeId: string | null;
|
||||
code: ProductErrorCode;
|
||||
field: string | null;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -296,6 +296,7 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
|||
{attributes.length > 0 && (
|
||||
<ProductAttributes
|
||||
attributes={attributes}
|
||||
errors={errors}
|
||||
disabled={disabled}
|
||||
onChange={handleAttributeChange}
|
||||
onMultiChange={handleAttributeMultiChange}
|
||||
|
|
|
@ -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
|
||||
})
|
||||
)}
|
||||
/>
|
||||
));
|
||||
|
|
Loading…
Reference in a new issue