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 {
|
export interface ProductErrorFragment {
|
||||||
__typename: "ProductError";
|
__typename: "ProductError";
|
||||||
|
attributeId: string | null;
|
||||||
code: ProductErrorCode;
|
code: ProductErrorCode;
|
||||||
field: string | null;
|
field: string | null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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
|
||||||
|
})
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
Loading…
Reference in a new issue