1425 - Support reference type attribute on attribute details page (#918)

* Support reference type attribute on attribute details page

* Trigger CI

* Prevent changing attribute entity type during attribute update

* Refactor attribute details components
This commit is contained in:
Dawid Tarasiuk 2021-01-12 12:11:15 +01:00 committed by Jakub Majorek
parent ee05b090b8
commit 8b0fe986b2
17 changed files with 650 additions and 480 deletions

View file

@ -733,37 +733,49 @@
"context": "dialog content", "context": "dialog content",
"string": "Are you sure you want to delete {attributeName}?" "string": "Are you sure you want to delete {attributeName}?"
}, },
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_1005562666": { "src_dot_attributes_dot_components_dot_AttributeDetails_dot_attributeLabel": {
"context": "attribute's editor component", "context": "attribute's label",
"string": "Catalog Input type for Store Owner" "string": "Default Label"
}, },
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_1336738461": { "src_dot_attributes_dot_components_dot_AttributeDetails_dot_attributeSlug": {
"context": "product attribute type",
"string": "Dropdown"
},
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_1376373679": {
"context": "file attribute type",
"string": "File"
},
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_2592224946": {
"context": "check to require attribute to have value",
"string": "Value Required"
},
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_3334509011": {
"context": "product attribute type",
"string": "Multiple Select"
},
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_3605174225": {
"context": "attribute's slug short code label", "context": "attribute's slug short code label",
"string": "Attribute Code" "string": "Attribute Code"
}, },
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_4107478955": { "src_dot_attributes_dot_components_dot_AttributeDetails_dot_attributeSlugHelperText": {
"context": "attribute slug input field helper text", "context": "attribute slug input field helper text",
"string": "This is used internally. Make sure you dont use spaces" "string": "This is used internally. Make sure you dont use spaces"
}, },
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_691600601": { "src_dot_attributes_dot_components_dot_AttributeDetails_dot_dropdown": {
"context": "attribute's label", "context": "product attribute type",
"string": "Default Label" "string": "Dropdown"
},
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_entityType": {
"context": "attribute's editor component entity",
"string": "Entity"
},
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_file": {
"context": "file attribute type",
"string": "File"
},
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_inputType": {
"context": "attribute's editor component",
"string": "Catalog Input type for Store Owner"
},
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_multiselect": {
"context": "product attribute type",
"string": "Multiple Select"
},
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_page": {
"context": "page attribute entity type",
"string": "Pages"
},
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_references": {
"context": "references attribute type",
"string": "References"
},
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_valueRequired": {
"context": "check to require attribute to have value",
"string": "Value Required"
}, },
"src_dot_attributes_dot_components_dot_AttributeListPage_dot_2417065806": { "src_dot_attributes_dot_components_dot_AttributeListPage_dot_2417065806": {
"context": "tab name", "context": "tab name",
@ -844,43 +856,46 @@
"context": "page title", "context": "page title",
"string": "Create New Attribute" "string": "Create New Attribute"
}, },
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_1318123158": { "src_dot_attributes_dot_components_dot_AttributeProperties_dot_availableInGrid": {
"context": "attribute is filterable in storefront",
"string": "Use in Faceted Navigation"
},
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_1877630205": {
"context": "attribute properties regarding storefront",
"string": "Storefront Properties"
},
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_26409543": {
"context": "attribute properties regarding dashboard",
"string": "Dashboard Properties"
},
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_3135366329": {
"context": "attribute visibility in storefront",
"string": "Public"
},
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_3590282519": {
"context": "attribute position in storefront filters",
"string": "Position in faceted navigation"
},
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_3758203740": {
"string": "If enabled, attribute will be accessible to customers."
},
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_4048785456": {
"string": "If enabled this attribute can be used as a column in product table."
},
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_673770329": {
"context": "add attribute as column in product list table", "context": "add attribute as column in product list table",
"string": "Add to Column Options" "string": "Add to Column Options"
}, },
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_714335445": { "src_dot_attributes_dot_components_dot_AttributeProperties_dot_availableInGridCaption": {
"context": "caption",
"string": "If enabled this attribute can be used as a column in product table."
},
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_dashboardPropertiesTitle": {
"context": "attribute properties regarding dashboard",
"string": "Dashboard Properties"
},
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_filterableInDashboard": {
"context": "use attribute in filtering", "context": "use attribute in filtering",
"string": "Use in Filtering" "string": "Use in Filtering"
}, },
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_787251583": { "src_dot_attributes_dot_components_dot_AttributeProperties_dot_filterableInDashboardCaption": {
"context": "caption",
"string": "If enabled, youll be able to use this attribute to filter products in product list." "string": "If enabled, youll be able to use this attribute to filter products in product list."
}, },
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_filterableInStorefront": {
"context": "attribute is filterable in storefront",
"string": "Use in Faceted Navigation"
},
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_storefrontPropertiesTitle": {
"context": "attribute properties regarding storefront",
"string": "Storefront Properties"
},
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_storefrontSearchPosition": {
"context": "attribute position in storefront filters",
"string": "Position in faceted navigation"
},
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_visibleInStorefront": {
"context": "attribute visibility in storefront",
"string": "Public"
},
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_visibleInStorefrontCaption": {
"context": "caption",
"string": "If enabled, attribute will be accessible to customers."
},
"src_dot_attributes_dot_components_dot_AttributeValueDeleteDialog_dot_1326420604": { "src_dot_attributes_dot_components_dot_AttributeValueDeleteDialog_dot_1326420604": {
"context": "delete attribute value", "context": "delete attribute value",
"string": "Are you sure you want to delete \"{name}\" value?" "string": "Are you sure you want to delete \"{name}\" value?"

View file

@ -1,5 +1,6 @@
import Card from "@material-ui/core/Card"; import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent"; import CardContent from "@material-ui/core/CardContent";
import { makeStyles } from "@material-ui/core/styles";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
@ -7,16 +8,86 @@ import FormSpacer from "@saleor/components/FormSpacer";
import SingleSelectField from "@saleor/components/SingleSelectField"; import SingleSelectField from "@saleor/components/SingleSelectField";
import { AttributeErrorFragment } from "@saleor/fragments/types/AttributeErrorFragment"; import { AttributeErrorFragment } from "@saleor/fragments/types/AttributeErrorFragment";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; import {
AttributeEntityTypeEnum,
AttributeInputTypeEnum
} from "@saleor/types/globalTypes";
import { getFormErrors } from "@saleor/utils/errors"; import { getFormErrors } from "@saleor/utils/errors";
import getAttributeErrorMessage from "@saleor/utils/errors/attribute"; import getAttributeErrorMessage from "@saleor/utils/errors/attribute";
import React from "react"; import React from "react";
import { useIntl } from "react-intl"; import { defineMessages, useIntl } from "react-intl";
import slugify from "slugify"; import slugify from "slugify";
import { getAttributeSlugErrorMessage } from "../../errors"; import { getAttributeSlugErrorMessage } from "../../errors";
import { AttributePageFormData } from "../AttributePage"; import { AttributePageFormData } from "../AttributePage";
const messages = defineMessages({
attributeLabel: {
defaultMessage: "Default Label",
description: "attribute's label"
},
attributeSlug: {
defaultMessage: "Attribute Code",
description: "attribute's slug short code label"
},
attributeSlugHelperText: {
defaultMessage: "This is used internally. Make sure you dont use spaces",
description: "attribute slug input field helper text"
},
entityType: {
defaultMessage: "Entity",
description: "attribute's editor component entity"
},
inputType: {
defaultMessage: "Catalog Input type for Store Owner",
description: "attribute's editor component"
},
valueRequired: {
defaultMessage: "Value Required",
description: "check to require attribute to have value"
}
});
const inputTypeMessages = defineMessages({
dropdown: {
defaultMessage: "Dropdown",
description: "product attribute type"
},
file: {
defaultMessage: "File",
description: "file attribute type"
},
multiselect: {
defaultMessage: "Multiple Select",
description: "product attribute type"
},
references: {
defaultMessage: "References",
description: "references attribute type"
}
});
const entityTypeMessages = defineMessages({
page: {
defaultMessage: "Pages",
description: "page attribute entity type"
}
});
const useStyles = makeStyles(
theme => ({
inputTypeSection: {
columnGap: theme.spacing(2) + "px",
display: "flex",
[theme.breakpoints.down("md")]: {
flexFlow: "wrap",
rowGap: theme.spacing(3) + "px"
}
}
}),
{ name: "AttributeDetails" }
);
export interface AttributeDetailsProps { export interface AttributeDetailsProps {
canChangeType: boolean; canChangeType: boolean;
data: AttributePageFormData; data: AttributePageFormData;
@ -25,39 +96,39 @@ export interface AttributeDetailsProps {
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
const AttributeDetails: React.FC<AttributeDetailsProps> = ({ const AttributeDetails: React.FC<AttributeDetailsProps> = props => {
canChangeType, const { canChangeType, data, disabled, errors, onChange } = props;
data, const classes = useStyles(props);
disabled,
errors,
onChange
}) => {
const intl = useIntl(); const intl = useIntl();
const inputTypeChoices = [ const inputTypeChoices = [
{ {
label: intl.formatMessage({ label: intl.formatMessage(inputTypeMessages.dropdown),
defaultMessage: "Dropdown",
description: "product attribute type"
}),
value: AttributeInputTypeEnum.DROPDOWN value: AttributeInputTypeEnum.DROPDOWN
}, },
{ {
label: intl.formatMessage({ label: intl.formatMessage(inputTypeMessages.multiselect),
defaultMessage: "Multiple Select",
description: "product attribute type"
}),
value: AttributeInputTypeEnum.MULTISELECT value: AttributeInputTypeEnum.MULTISELECT
}, },
{ {
label: intl.formatMessage({ label: intl.formatMessage(inputTypeMessages.file),
defaultMessage: "File",
description: "file attribute type"
}),
value: AttributeInputTypeEnum.FILE value: AttributeInputTypeEnum.FILE
},
{
label: intl.formatMessage(inputTypeMessages.references),
value: AttributeInputTypeEnum.REFERENCE
}
];
const entityTypeChoices = [
{
label: intl.formatMessage(entityTypeMessages.page),
value: AttributeEntityTypeEnum.PAGE
} }
]; ];
const formErrors = getFormErrors(["name", "slug", "inputType"], errors); const formErrors = getFormErrors(
["name", "slug", "inputType", "entityType"],
errors
);
return ( return (
<Card> <Card>
@ -68,10 +139,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!formErrors.name} error={!!formErrors.name}
label={intl.formatMessage({ label={intl.formatMessage(messages.attributeLabel)}
defaultMessage: "Default Label",
description: "attribute's label"
})}
name={"name" as keyof AttributePageFormData} name={"name" as keyof AttributePageFormData}
fullWidth fullWidth
helperText={getAttributeErrorMessage(formErrors.name, intl)} helperText={getAttributeErrorMessage(formErrors.name, intl)}
@ -82,45 +150,46 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!formErrors.slug} error={!!formErrors.slug}
label={intl.formatMessage({ label={intl.formatMessage(messages.attributeSlug)}
defaultMessage: "Attribute Code",
description: "attribute's slug short code label"
})}
name={"slug" as keyof AttributePageFormData} name={"slug" as keyof AttributePageFormData}
placeholder={slugify(data.name).toLowerCase()} placeholder={slugify(data.name).toLowerCase()}
fullWidth fullWidth
helperText={ helperText={
getAttributeSlugErrorMessage(formErrors.slug, intl) || getAttributeSlugErrorMessage(formErrors.slug, intl) ||
intl.formatMessage({ intl.formatMessage(messages.attributeSlugHelperText)
defaultMessage:
"This is used internally. Make sure you dont use spaces",
description: "attribute slug input field helper text"
})
} }
value={data.slug} value={data.slug}
onChange={onChange} onChange={onChange}
/> />
<FormSpacer /> <FormSpacer />
<SingleSelectField <div className={classes.inputTypeSection}>
choices={inputTypeChoices} <SingleSelectField
disabled={disabled || !canChangeType} choices={inputTypeChoices}
error={!!formErrors.inputType} disabled={disabled || !canChangeType}
hint={getAttributeErrorMessage(formErrors.inputType, intl)} error={!!formErrors.inputType}
label={intl.formatMessage({ hint={getAttributeErrorMessage(formErrors.inputType, intl)}
defaultMessage: "Catalog Input type for Store Owner", label={intl.formatMessage(messages.inputType)}
description: "attribute's editor component" name="inputType"
})} onChange={onChange}
name="inputType" value={data.inputType}
onChange={onChange} />
value={data.inputType} {data.inputType === AttributeInputTypeEnum.REFERENCE && (
/> <SingleSelectField
choices={entityTypeChoices}
disabled={disabled || !canChangeType}
error={!!formErrors.entityType}
hint={getAttributeErrorMessage(formErrors.entityType, intl)}
label={intl.formatMessage(messages.entityType)}
name="entityType"
onChange={onChange}
value={data.entityType}
/>
)}
</div>
<FormSpacer /> <FormSpacer />
<ControlledCheckbox <ControlledCheckbox
name={"valueRequired" as keyof AttributePageFormData} name={"valueRequired" as keyof AttributePageFormData}
label={intl.formatMessage({ label={intl.formatMessage(messages.valueRequired)}
defaultMessage: "Value Required",
description: "check to require attribute to have value"
})}
checked={data.valueRequired} checked={data.valueRequired}
onChange={onChange} onChange={onChange}
disabled={disabled} disabled={disabled}

View file

@ -1,3 +1,4 @@
import { ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES } from "@saleor/attributes/utils/data";
import AppHeader from "@saleor/components/AppHeader"; import AppHeader from "@saleor/components/AppHeader";
import CardSpacer from "@saleor/components/CardSpacer"; import CardSpacer from "@saleor/components/CardSpacer";
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
@ -17,6 +18,7 @@ import { sectionNames } from "@saleor/intl";
import { maybe } from "@saleor/misc"; import { maybe } from "@saleor/misc";
import { ReorderAction } from "@saleor/types"; import { ReorderAction } from "@saleor/types";
import { import {
AttributeEntityTypeEnum,
AttributeInputTypeEnum, AttributeInputTypeEnum,
AttributeTypeEnum AttributeTypeEnum
} from "@saleor/types/globalTypes"; } from "@saleor/types/globalTypes";
@ -51,6 +53,7 @@ export interface AttributePageFormData extends MetadataFormData {
availableInGrid: boolean; availableInGrid: boolean;
filterableInDashboard: boolean; filterableInDashboard: boolean;
inputType: AttributeInputTypeEnum; inputType: AttributeInputTypeEnum;
entityType: AttributeEntityTypeEnum;
filterableInStorefront: boolean; filterableInStorefront: boolean;
name: string; name: string;
slug: string; slug: string;
@ -84,6 +87,7 @@ const AttributePage: React.FC<AttributePageProps> = ({
attribute === null attribute === null
? { ? {
availableInGrid: true, availableInGrid: true,
entityType: null,
filterableInDashboard: true, filterableInDashboard: true,
filterableInStorefront: true, filterableInStorefront: true,
inputType: AttributeInputTypeEnum.DROPDOWN, inputType: AttributeInputTypeEnum.DROPDOWN,
@ -98,6 +102,7 @@ const AttributePage: React.FC<AttributePageProps> = ({
} }
: { : {
availableInGrid: maybe(() => attribute.availableInGrid, true), availableInGrid: maybe(() => attribute.availableInGrid, true),
entityType: attribute?.entityType ?? null,
filterableInDashboard: maybe( filterableInDashboard: maybe(
() => attribute.filterableInDashboard, () => attribute.filterableInDashboard,
true true
@ -172,7 +177,9 @@ const AttributePage: React.FC<AttributePageProps> = ({
errors={errors} errors={errors}
onChange={change} onChange={change}
/> />
{data.inputType !== AttributeInputTypeEnum.FILE && ( {ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES.includes(
data.inputType
) && (
<> <>
<CardSpacer /> <CardSpacer />
<AttributeValues <AttributeValues

View file

@ -2,6 +2,7 @@ import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent"; import CardContent from "@material-ui/core/CardContent";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import { ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES } from "@saleor/attributes/utils/data";
import CardSpacer from "@saleor/components/CardSpacer"; import CardSpacer from "@saleor/components/CardSpacer";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
@ -10,17 +11,59 @@ import FormSpacer from "@saleor/components/FormSpacer";
import Hr from "@saleor/components/Hr"; import Hr from "@saleor/components/Hr";
import { AttributeErrorFragment } from "@saleor/fragments/types/AttributeErrorFragment"; import { AttributeErrorFragment } from "@saleor/fragments/types/AttributeErrorFragment";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { import { AttributeTypeEnum } from "@saleor/types/globalTypes";
AttributeInputTypeEnum,
AttributeTypeEnum
} from "@saleor/types/globalTypes";
import { getFormErrors } from "@saleor/utils/errors"; import { getFormErrors } from "@saleor/utils/errors";
import getAttributeErrorMessage from "@saleor/utils/errors/attribute"; import getAttributeErrorMessage from "@saleor/utils/errors/attribute";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { defineMessages, FormattedMessage, useIntl } from "react-intl";
import { AttributePageFormData } from "../AttributePage"; import { AttributePageFormData } from "../AttributePage";
const messages = defineMessages({
availableInGrid: {
defaultMessage: "Add to Column Options",
description: "add attribute as column in product list table"
},
availableInGridCaption: {
defaultMessage:
"If enabled this attribute can be used as a column in product table.",
description: "caption"
},
dashboardPropertiesTitle: {
defaultMessage: "Dashboard Properties",
description: "attribute properties regarding dashboard"
},
filterableInDashboard: {
defaultMessage: "Use in Filtering",
description: "use attribute in filtering"
},
filterableInDashboardCaption: {
defaultMessage:
"If enabled, youll be able to use this attribute to filter products in product list.",
description: "caption"
},
filterableInStorefront: {
defaultMessage: "Use in Faceted Navigation",
description: "attribute is filterable in storefront"
},
storefrontPropertiesTitle: {
defaultMessage: "Storefront Properties",
description: "attribute properties regarding storefront"
},
storefrontSearchPosition: {
defaultMessage: "Position in faceted navigation",
description: "attribute position in storefront filters"
},
visibleInStorefront: {
defaultMessage: "Public",
description: "attribute visibility in storefront"
},
visibleInStorefrontCaption: {
defaultMessage: "If enabled, attribute will be accessible to customers.",
description: "caption"
}
});
export interface AttributePropertiesProps { export interface AttributePropertiesProps {
data: AttributePageFormData; data: AttributePageFormData;
disabled: boolean; disabled: boolean;
@ -38,6 +81,14 @@ const AttributeProperties: React.FC<AttributePropertiesProps> = ({
const formErrors = getFormErrors(["storefrontSearchPosition"], errors); const formErrors = getFormErrors(["storefrontSearchPosition"], errors);
const dashboardProperties = ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES.includes(
data.inputType
);
const storefrontFacetedNavigationProperties =
ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES.includes(data.inputType) &&
data.type === AttributeTypeEnum.PRODUCT_TYPE;
return ( return (
<Card> <Card>
<CardTitle title={intl.formatMessage(commonMessages.properties)} /> <CardTitle title={intl.formatMessage(commonMessages.properties)} />
@ -74,61 +125,48 @@ const AttributeProperties: React.FC<AttributePropertiesProps> = ({
/> */} /> */}
<Typography variant="subtitle1"> <Typography variant="subtitle1">
<FormattedMessage <FormattedMessage {...messages.storefrontPropertiesTitle} />
defaultMessage="Storefront Properties"
description="attribute properties regarding storefront"
/>
</Typography> </Typography>
<Hr /> <Hr />
{data.inputType !== AttributeInputTypeEnum.FILE && {storefrontFacetedNavigationProperties && (
data.type === AttributeTypeEnum.PRODUCT_TYPE && ( <>
<> <ControlledCheckbox
<ControlledCheckbox name={"filterableInStorefront" as keyof FormData}
name={"filterableInStorefront" as keyof FormData} label={intl.formatMessage(messages.filterableInStorefront)}
label={intl.formatMessage({ checked={data.filterableInStorefront}
defaultMessage: "Use in Faceted Navigation", onChange={onChange}
description: "attribute is filterable in storefront" disabled={disabled}
})} />
checked={data.filterableInStorefront} {data.filterableInStorefront && (
onChange={onChange} <>
disabled={disabled} <FormSpacer />
/> <TextField
{data.filterableInStorefront && ( disabled={disabled}
<> error={!!formErrors.storefrontSearchPosition}
<FormSpacer /> fullWidth
<TextField helperText={getAttributeErrorMessage(
disabled={disabled} formErrors.storefrontSearchPosition,
error={!!formErrors.storefrontSearchPosition} intl
fullWidth )}
helperText={getAttributeErrorMessage( name={
formErrors.storefrontSearchPosition, "storefrontSearchPosition" as keyof AttributePageFormData
intl }
)} label={intl.formatMessage(messages.storefrontSearchPosition)}
name={ value={data.storefrontSearchPosition}
"storefrontSearchPosition" as keyof AttributePageFormData onChange={onChange}
} />
label={intl.formatMessage({ </>
defaultMessage: "Position in faceted navigation", )}
description: "attribute position in storefront filters" </>
})} )}
value={data.storefrontSearchPosition}
onChange={onChange}
/>
</>
)}
</>
)}
<FormSpacer /> <FormSpacer />
<ControlledSwitch <ControlledSwitch
name={"visibleInStorefront" as keyof FormData} name={"visibleInStorefront" as keyof FormData}
label={ label={
<> <>
<FormattedMessage <FormattedMessage {...messages.visibleInStorefront} />
defaultMessage="Public"
description="attribute visibility in storefront"
/>
<Typography variant="caption"> <Typography variant="caption">
<FormattedMessage defaultMessage="If enabled, attribute will be accessible to customers." /> <FormattedMessage {...messages.visibleInStorefrontCaption} />
</Typography> </Typography>
</> </>
} }
@ -136,14 +174,11 @@ const AttributeProperties: React.FC<AttributePropertiesProps> = ({
onChange={onChange} onChange={onChange}
disabled={disabled} disabled={disabled}
/> />
{data.inputType !== AttributeInputTypeEnum.FILE && ( {dashboardProperties && (
<> <>
<CardSpacer /> <CardSpacer />
<Typography variant="subtitle1"> <Typography variant="subtitle1">
<FormattedMessage <FormattedMessage {...messages.dashboardPropertiesTitle} />
defaultMessage="Dashboard Properties"
description="attribute properties regarding dashboard"
/>
</Typography> </Typography>
<Hr /> <Hr />
<CardSpacer /> <CardSpacer />
@ -151,12 +186,11 @@ const AttributeProperties: React.FC<AttributePropertiesProps> = ({
name={"filterableInDashboard" as keyof FormData} name={"filterableInDashboard" as keyof FormData}
label={ label={
<> <>
<FormattedMessage <FormattedMessage {...messages.filterableInDashboard} />
defaultMessage="Use in Filtering"
description="use attribute in filtering"
/>
<Typography variant="caption"> <Typography variant="caption">
<FormattedMessage defaultMessage="If enabled, youll be able to use this attribute to filter products in product list." /> <FormattedMessage
{...messages.filterableInDashboardCaption}
/>
</Typography> </Typography>
</> </>
} }
@ -169,12 +203,9 @@ const AttributeProperties: React.FC<AttributePropertiesProps> = ({
name={"availableInGrid" as keyof FormData} name={"availableInGrid" as keyof FormData}
label={ label={
<> <>
<FormattedMessage <FormattedMessage {...messages.availableInGrid} />
defaultMessage="Add to Column Options"
description="add attribute as column in product list table"
/>
<Typography variant="caption"> <Typography variant="caption">
<FormattedMessage defaultMessage="If enabled this attribute can be used as a column in product table." /> <FormattedMessage {...messages.availableInGridCaption} />
</Typography> </Typography>
</> </>
} }

View file

@ -10,6 +10,7 @@ import { AttributeList_attributes_edges_node } from "./types/AttributeList";
export const attribute: AttributeDetailsFragment = { export const attribute: AttributeDetailsFragment = {
__typename: "Attribute" as "Attribute", __typename: "Attribute" as "Attribute",
availableInGrid: true, availableInGrid: true,
entityType: null,
filterableInDashboard: false, filterableInDashboard: false,
filterableInStorefront: true, filterableInStorefront: true,
id: "UHJvZHVjdEF0dHJpYnV0ZTo5", id: "UHJvZHVjdEF0dHJpYnV0ZTo5",

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { AttributeCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeErrorCode } from "./../../types/globalTypes"; import { AttributeCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeEntityTypeEnum, AttributeErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: AttributeCreate // GraphQL mutation operation: AttributeCreate
@ -47,6 +47,7 @@ export interface AttributeCreate_attributeCreate_attribute {
privateMetadata: (AttributeCreate_attributeCreate_attribute_privateMetadata | null)[]; privateMetadata: (AttributeCreate_attributeCreate_attribute_privateMetadata | null)[];
availableInGrid: boolean; availableInGrid: boolean;
inputType: AttributeInputTypeEnum | null; inputType: AttributeInputTypeEnum | null;
entityType: AttributeEntityTypeEnum | null;
storefrontSearchPosition: number; storefrontSearchPosition: number;
valueRequired: boolean; valueRequired: boolean;
values: (AttributeCreate_attributeCreate_attribute_values | null)[] | null; values: (AttributeCreate_attributeCreate_attribute_values | null)[] | null;

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { AttributeTypeEnum, AttributeInputTypeEnum } from "./../../types/globalTypes"; import { AttributeTypeEnum, AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL query operation: AttributeDetails // GraphQL query operation: AttributeDetails
@ -47,6 +47,7 @@ export interface AttributeDetails_attribute {
privateMetadata: (AttributeDetails_attribute_privateMetadata | null)[]; privateMetadata: (AttributeDetails_attribute_privateMetadata | null)[];
availableInGrid: boolean; availableInGrid: boolean;
inputType: AttributeInputTypeEnum | null; inputType: AttributeInputTypeEnum | null;
entityType: AttributeEntityTypeEnum | null;
storefrontSearchPosition: number; storefrontSearchPosition: number;
valueRequired: boolean; valueRequired: boolean;
values: (AttributeDetails_attribute_values | null)[] | null; values: (AttributeDetails_attribute_values | null)[] | null;

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { AttributeUpdateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeErrorCode } from "./../../types/globalTypes"; import { AttributeUpdateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeEntityTypeEnum, AttributeErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: AttributeUpdate // GraphQL mutation operation: AttributeUpdate
@ -47,6 +47,7 @@ export interface AttributeUpdate_attributeUpdate_attribute {
privateMetadata: (AttributeUpdate_attributeUpdate_attribute_privateMetadata | null)[]; privateMetadata: (AttributeUpdate_attributeUpdate_attribute_privateMetadata | null)[];
availableInGrid: boolean; availableInGrid: boolean;
inputType: AttributeInputTypeEnum | null; inputType: AttributeInputTypeEnum | null;
entityType: AttributeEntityTypeEnum | null;
storefrontSearchPosition: number; storefrontSearchPosition: number;
valueRequired: boolean; valueRequired: boolean;
values: (AttributeUpdate_attributeUpdate_attribute_values | null)[] | null; values: (AttributeUpdate_attributeUpdate_attribute_values | null)[] | null;

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { AttributeValueCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeErrorCode } from "./../../types/globalTypes"; import { AttributeValueCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeEntityTypeEnum, AttributeErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: AttributeValueCreate // GraphQL mutation operation: AttributeValueCreate
@ -47,6 +47,7 @@ export interface AttributeValueCreate_attributeValueCreate_attribute {
privateMetadata: (AttributeValueCreate_attributeValueCreate_attribute_privateMetadata | null)[]; privateMetadata: (AttributeValueCreate_attributeValueCreate_attribute_privateMetadata | null)[];
availableInGrid: boolean; availableInGrid: boolean;
inputType: AttributeInputTypeEnum | null; inputType: AttributeInputTypeEnum | null;
entityType: AttributeEntityTypeEnum | null;
storefrontSearchPosition: number; storefrontSearchPosition: number;
valueRequired: boolean; valueRequired: boolean;
values: (AttributeValueCreate_attributeValueCreate_attribute_values | null)[] | null; values: (AttributeValueCreate_attributeValueCreate_attribute_values | null)[] | null;

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { AttributeTypeEnum, AttributeInputTypeEnum, AttributeErrorCode } from "./../../types/globalTypes"; import { AttributeTypeEnum, AttributeInputTypeEnum, AttributeEntityTypeEnum, AttributeErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: AttributeValueDelete // GraphQL mutation operation: AttributeValueDelete
@ -47,6 +47,7 @@ export interface AttributeValueDelete_attributeValueDelete_attribute {
privateMetadata: (AttributeValueDelete_attributeValueDelete_attribute_privateMetadata | null)[]; privateMetadata: (AttributeValueDelete_attributeValueDelete_attribute_privateMetadata | null)[];
availableInGrid: boolean; availableInGrid: boolean;
inputType: AttributeInputTypeEnum | null; inputType: AttributeInputTypeEnum | null;
entityType: AttributeEntityTypeEnum | null;
storefrontSearchPosition: number; storefrontSearchPosition: number;
valueRequired: boolean; valueRequired: boolean;
values: (AttributeValueDelete_attributeValueDelete_attribute_values | null)[] | null; values: (AttributeValueDelete_attributeValueDelete_attribute_values | null)[] | null;

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { AttributeValueCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeErrorCode } from "./../../types/globalTypes"; import { AttributeValueCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeEntityTypeEnum, AttributeErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: AttributeValueUpdate // GraphQL mutation operation: AttributeValueUpdate
@ -47,6 +47,7 @@ export interface AttributeValueUpdate_attributeValueUpdate_attribute {
privateMetadata: (AttributeValueUpdate_attributeValueUpdate_attribute_privateMetadata | null)[]; privateMetadata: (AttributeValueUpdate_attributeValueUpdate_attribute_privateMetadata | null)[];
availableInGrid: boolean; availableInGrid: boolean;
inputType: AttributeInputTypeEnum | null; inputType: AttributeInputTypeEnum | null;
entityType: AttributeEntityTypeEnum | null;
storefrontSearchPosition: number; storefrontSearchPosition: number;
valueRequired: boolean; valueRequired: boolean;
values: (AttributeValueUpdate_attributeValueUpdate_attribute_values | null)[] | null; values: (AttributeValueUpdate_attributeValueUpdate_attribute_values | null)[] | null;

View file

@ -10,8 +10,53 @@ import {
} from "@saleor/types/globalTypes"; } from "@saleor/types/globalTypes";
import { MutationFetchResult } from "react-apollo"; import { MutationFetchResult } from "react-apollo";
import { AttributePageFormData } from "../components/AttributePage";
import { AttributeValueEditDialogFormData } from "../components/AttributeValueEditDialog";
import { AttributeValueDelete } from "../types/AttributeValueDelete"; import { AttributeValueDelete } from "../types/AttributeValueDelete";
export const ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES = [
AttributeInputTypeEnum.DROPDOWN,
AttributeInputTypeEnum.MULTISELECT
];
function getSimpleAttributeData(
data: AttributePageFormData,
values: AttributeValueEditDialogFormData[]
) {
return {
...data,
metadata: undefined,
privateMetadata: undefined,
storefrontSearchPosition: parseInt(data.storefrontSearchPosition, 10),
values: values.map(value => ({
name: value.name
}))
};
}
function getFileOrReferenceAttributeData(
data: AttributePageFormData,
values: AttributeValueEditDialogFormData[]
) {
return {
...getSimpleAttributeData(data, values),
availableInGrid: undefined,
filterableInDashboard: undefined,
filterableInStorefront: undefined
};
}
export function getAttributeData(
data: AttributePageFormData,
values: AttributeValueEditDialogFormData[]
) {
if (ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES.includes(data.inputType)) {
return getSimpleAttributeData(data, values);
} else {
return getFileOrReferenceAttributeData(data, values);
}
}
export const isFileValueUnused = ( export const isFileValueUnused = (
attributesWithNewFileValue: FormsetData<null, File>, attributesWithNewFileValue: FormsetData<null, File>,
existingAttribute: existingAttribute:

View file

@ -1,12 +1,10 @@
import { getAttributeData } from "@saleor/attributes/utils/data";
import { AttributeErrorFragment } from "@saleor/fragments/types/AttributeErrorFragment"; import { AttributeErrorFragment } from "@saleor/fragments/types/AttributeErrorFragment";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier"; import useNotifier from "@saleor/hooks/useNotifier";
import { getStringOrPlaceholder } from "@saleor/misc"; import { getStringOrPlaceholder } from "@saleor/misc";
import { ReorderEvent } from "@saleor/types"; import { ReorderEvent } from "@saleor/types";
import { import { AttributeErrorCode } from "@saleor/types/globalTypes";
AttributeErrorCode,
AttributeInputTypeEnum
} from "@saleor/types/globalTypes";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
import createMetadataCreateHandler from "@saleor/utils/handlers/metadataCreateHandler"; import createMetadataCreateHandler from "@saleor/utils/handlers/metadataCreateHandler";
import { import {
@ -57,33 +55,6 @@ function areValuesEqual(
return a.name === b.name; return a.name === b.name;
} }
function getSimpleAttributeData(
data: AttributePageFormData,
values: AttributeValueEditDialogFormData[]
) {
return {
...data,
metadata: undefined,
privateMetadata: undefined,
storefrontSearchPosition: parseInt(data.storefrontSearchPosition, 10),
values: values.map(value => ({
name: value.name
}))
};
}
function getFileAttributeData(
data: AttributePageFormData,
values: AttributeValueEditDialogFormData[]
) {
return {
...getSimpleAttributeData(data, values),
availableInGrid: undefined,
filterableInDashboard: undefined,
filterableInStorefront: undefined
};
}
const AttributeDetails: React.FC<AttributeDetailsProps> = ({ params }) => { const AttributeDetails: React.FC<AttributeDetailsProps> = ({ params }) => {
const navigate = useNavigator(); const navigate = useNavigator();
const notify = useNotifier(); const notify = useNotifier();
@ -145,10 +116,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ params }) => {
setValues(move(values[oldIndex], values, areValuesEqual, newIndex)); setValues(move(values[oldIndex], values, areValuesEqual, newIndex));
const handleCreate = async (data: AttributePageFormData) => { const handleCreate = async (data: AttributePageFormData) => {
const input = const input = getAttributeData(data, values);
data.inputType === AttributeInputTypeEnum.FILE
? getFileAttributeData(data, values)
: getSimpleAttributeData(data, values);
const result = await attributeCreate({ const result = await attributeCreate({
variables: { variables: {

View file

@ -178,6 +178,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
const handleUpdate = async (data: AttributePageFormData) => { const handleUpdate = async (data: AttributePageFormData) => {
const input = { const input = {
...data, ...data,
entityType: undefined,
inputType: undefined, inputType: undefined,
metadata: undefined, metadata: undefined,
privateMetadata: undefined, privateMetadata: undefined,

View file

@ -12,6 +12,7 @@ export const attributeValueFragment = gql`
file { file {
...FileFragment ...FileFragment
} }
reference
} }
`; `;
@ -36,6 +37,7 @@ export const attributeDetailsFragment = gql`
...MetadataFragment ...MetadataFragment
availableInGrid availableInGrid
inputType inputType
entityType
storefrontSearchPosition storefrontSearchPosition
valueRequired valueRequired
values { values {

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { AttributeTypeEnum, AttributeInputTypeEnum } from "./../../types/globalTypes"; import { AttributeTypeEnum, AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL fragment: AttributeDetailsFragment // GraphQL fragment: AttributeDetailsFragment
@ -47,6 +47,7 @@ export interface AttributeDetailsFragment {
privateMetadata: (AttributeDetailsFragment_privateMetadata | null)[]; privateMetadata: (AttributeDetailsFragment_privateMetadata | null)[];
availableInGrid: boolean; availableInGrid: boolean;
inputType: AttributeInputTypeEnum | null; inputType: AttributeInputTypeEnum | null;
entityType: AttributeEntityTypeEnum | null;
storefrontSearchPosition: number; storefrontSearchPosition: number;
valueRequired: boolean; valueRequired: boolean;
values: (AttributeDetailsFragment_values | null)[] | null; values: (AttributeDetailsFragment_values | null)[] | null;

View file

@ -28404,57 +28404,61 @@ exports[`Storyshots Views / Attributes / Attribute details create 1`] = `
class="FormSpacer-spacer-id" class="FormSpacer-spacer-id"
/> />
<div <div
class="MuiFormControl-root-id SingleSelectField-formControl-id" class="AttributeDetails-inputTypeSection-id"
> >
<label
class="MuiFormLabel-root-id MuiInputLabel-root-id SingleSelectField-label-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-shrink-id MuiInputLabel-filled-id MuiFormLabel-filled-id"
data-shrink="true"
>
Catalog Input type for Store Owner
</label>
<div <div
class="MuiInputBase-root-id MuiOutlinedInput-root-id MuiInputBase-fullWidth-id MuiInputBase-formControl-id" class="MuiFormControl-root-id SingleSelectField-formControl-id"
> >
<label
class="MuiFormLabel-root-id MuiInputLabel-root-id SingleSelectField-label-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-shrink-id MuiInputLabel-filled-id MuiFormLabel-filled-id"
data-shrink="true"
>
Catalog Input type for Store Owner
</label>
<div <div
aria-haspopup="listbox" class="MuiInputBase-root-id MuiOutlinedInput-root-id MuiInputBase-fullWidth-id MuiInputBase-formControl-id"
aria-labelledby=" mui-component-select-inputType"
class="MuiSelect-root-id MuiSelect-select-id MuiSelect-selectMenu-id MuiSelect-outlined-id MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-inputSelect-id MuiOutlinedInput-inputSelect-id"
id="mui-component-select-inputType"
role="button"
tabindex="0"
> >
Dropdown <div
</div> aria-haspopup="listbox"
<input aria-labelledby=" mui-component-select-inputType"
name="inputType" class="MuiSelect-root-id MuiSelect-select-id MuiSelect-selectMenu-id MuiSelect-outlined-id MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-inputSelect-id MuiOutlinedInput-inputSelect-id"
type="hidden" id="mui-component-select-inputType"
value="DROPDOWN" role="button"
/> tabindex="0"
<svg
aria-hidden="true"
class="MuiSvgIcon-root-id MuiSelect-icon-id MuiSelect-iconOutlined-id"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M7 10l5 5 5-5z"
/>
</svg>
<fieldset
aria-hidden="true"
class="PrivateNotchedOutline-root-id MuiOutlinedInput-notchedOutline-id"
style="padding-left:8px"
>
<legend
class="PrivateNotchedOutline-legend-id"
style="width:143px"
> >
<span> Dropdown
</div>
</span> <input
</legend> name="inputType"
</fieldset> type="hidden"
value="DROPDOWN"
/>
<svg
aria-hidden="true"
class="MuiSvgIcon-root-id MuiSelect-icon-id MuiSelect-iconOutlined-id"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M7 10l5 5 5-5z"
/>
</svg>
<fieldset
aria-hidden="true"
class="PrivateNotchedOutline-root-id MuiOutlinedInput-notchedOutline-id"
style="padding-left:8px"
>
<legend
class="PrivateNotchedOutline-legend-id"
style="width:143px"
>
<span>
</span>
</legend>
</fieldset>
</div>
</div> </div>
</div> </div>
<div <div
@ -29435,56 +29439,60 @@ exports[`Storyshots Views / Attributes / Attribute details default 1`] = `
class="FormSpacer-spacer-id" class="FormSpacer-spacer-id"
/> />
<div <div
class="MuiFormControl-root-id SingleSelectField-formControl-id" class="AttributeDetails-inputTypeSection-id"
> >
<label
class="MuiFormLabel-root-id MuiInputLabel-root-id SingleSelectField-label-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-shrink-id MuiInputLabel-filled-id MuiFormLabel-disabled-id MuiInputLabel-disabled-id MuiFormLabel-filled-id"
data-shrink="true"
>
Catalog Input type for Store Owner
</label>
<div <div
class="MuiInputBase-root-id MuiOutlinedInput-root-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id MuiInputBase-fullWidth-id MuiInputBase-formControl-id" class="MuiFormControl-root-id SingleSelectField-formControl-id"
> >
<label
class="MuiFormLabel-root-id MuiInputLabel-root-id SingleSelectField-label-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-shrink-id MuiInputLabel-filled-id MuiFormLabel-disabled-id MuiInputLabel-disabled-id MuiFormLabel-filled-id"
data-shrink="true"
>
Catalog Input type for Store Owner
</label>
<div <div
aria-haspopup="listbox" class="MuiInputBase-root-id MuiOutlinedInput-root-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id MuiInputBase-fullWidth-id MuiInputBase-formControl-id"
aria-labelledby=" mui-component-select-inputType"
class="MuiSelect-root-id MuiSelect-select-id MuiSelect-selectMenu-id MuiSelect-outlined-id MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id MuiInputBase-inputSelect-id MuiOutlinedInput-inputSelect-id MuiSelect-disabled-id"
id="mui-component-select-inputType"
role="button"
> >
Dropdown <div
</div> aria-haspopup="listbox"
<input aria-labelledby=" mui-component-select-inputType"
name="inputType" class="MuiSelect-root-id MuiSelect-select-id MuiSelect-selectMenu-id MuiSelect-outlined-id MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id MuiInputBase-inputSelect-id MuiOutlinedInput-inputSelect-id MuiSelect-disabled-id"
type="hidden" id="mui-component-select-inputType"
value="DROPDOWN" role="button"
/>
<svg
aria-hidden="true"
class="MuiSvgIcon-root-id MuiSelect-icon-id MuiSelect-iconOutlined-id"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M7 10l5 5 5-5z"
/>
</svg>
<fieldset
aria-hidden="true"
class="PrivateNotchedOutline-root-id MuiOutlinedInput-notchedOutline-id"
style="padding-left:8px"
>
<legend
class="PrivateNotchedOutline-legend-id"
style="width:143px"
> >
<span> Dropdown
</div>
</span> <input
</legend> name="inputType"
</fieldset> type="hidden"
value="DROPDOWN"
/>
<svg
aria-hidden="true"
class="MuiSvgIcon-root-id MuiSelect-icon-id MuiSelect-iconOutlined-id"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M7 10l5 5 5-5z"
/>
</svg>
<fieldset
aria-hidden="true"
class="PrivateNotchedOutline-root-id MuiOutlinedInput-notchedOutline-id"
style="padding-left:8px"
>
<legend
class="PrivateNotchedOutline-legend-id"
style="width:143px"
>
<span>
</span>
</legend>
</fieldset>
</div>
</div> </div>
</div> </div>
<div <div
@ -30511,56 +30519,60 @@ exports[`Storyshots Views / Attributes / Attribute details form errors 1`] = `
class="FormSpacer-spacer-id" class="FormSpacer-spacer-id"
/> />
<div <div
class="MuiFormControl-root-id SingleSelectField-formControl-id" class="AttributeDetails-inputTypeSection-id"
> >
<label
class="MuiFormLabel-root-id MuiInputLabel-root-id SingleSelectField-label-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-shrink-id MuiInputLabel-filled-id MuiFormLabel-disabled-id MuiInputLabel-disabled-id MuiFormLabel-filled-id"
data-shrink="true"
>
Catalog Input type for Store Owner
</label>
<div <div
class="MuiInputBase-root-id MuiOutlinedInput-root-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id MuiInputBase-fullWidth-id MuiInputBase-formControl-id" class="MuiFormControl-root-id SingleSelectField-formControl-id"
> >
<label
class="MuiFormLabel-root-id MuiInputLabel-root-id SingleSelectField-label-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-shrink-id MuiInputLabel-filled-id MuiFormLabel-disabled-id MuiInputLabel-disabled-id MuiFormLabel-filled-id"
data-shrink="true"
>
Catalog Input type for Store Owner
</label>
<div <div
aria-haspopup="listbox" class="MuiInputBase-root-id MuiOutlinedInput-root-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id MuiInputBase-fullWidth-id MuiInputBase-formControl-id"
aria-labelledby=" mui-component-select-inputType"
class="MuiSelect-root-id MuiSelect-select-id MuiSelect-selectMenu-id MuiSelect-outlined-id MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id MuiInputBase-inputSelect-id MuiOutlinedInput-inputSelect-id MuiSelect-disabled-id"
id="mui-component-select-inputType"
role="button"
> >
Dropdown <div
</div> aria-haspopup="listbox"
<input aria-labelledby=" mui-component-select-inputType"
name="inputType" class="MuiSelect-root-id MuiSelect-select-id MuiSelect-selectMenu-id MuiSelect-outlined-id MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id MuiInputBase-inputSelect-id MuiOutlinedInput-inputSelect-id MuiSelect-disabled-id"
type="hidden" id="mui-component-select-inputType"
value="DROPDOWN" role="button"
/>
<svg
aria-hidden="true"
class="MuiSvgIcon-root-id MuiSelect-icon-id MuiSelect-iconOutlined-id"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M7 10l5 5 5-5z"
/>
</svg>
<fieldset
aria-hidden="true"
class="PrivateNotchedOutline-root-id MuiOutlinedInput-notchedOutline-id"
style="padding-left:8px"
>
<legend
class="PrivateNotchedOutline-legend-id"
style="width:143px"
> >
<span> Dropdown
</div>
</span> <input
</legend> name="inputType"
</fieldset> type="hidden"
value="DROPDOWN"
/>
<svg
aria-hidden="true"
class="MuiSvgIcon-root-id MuiSelect-icon-id MuiSelect-iconOutlined-id"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M7 10l5 5 5-5z"
/>
</svg>
<fieldset
aria-hidden="true"
class="PrivateNotchedOutline-root-id MuiOutlinedInput-notchedOutline-id"
style="padding-left:8px"
>
<legend
class="PrivateNotchedOutline-legend-id"
style="width:143px"
>
<span>
</span>
</legend>
</fieldset>
</div>
</div> </div>
</div> </div>
<div <div
@ -31594,56 +31606,60 @@ exports[`Storyshots Views / Attributes / Attribute details loading 1`] = `
class="FormSpacer-spacer-id" class="FormSpacer-spacer-id"
/> />
<div <div
class="MuiFormControl-root-id SingleSelectField-formControl-id" class="AttributeDetails-inputTypeSection-id"
> >
<label
class="MuiFormLabel-root-id MuiInputLabel-root-id SingleSelectField-label-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-shrink-id MuiInputLabel-filled-id MuiFormLabel-disabled-id MuiInputLabel-disabled-id MuiFormLabel-filled-id"
data-shrink="true"
>
Catalog Input type for Store Owner
</label>
<div <div
class="MuiInputBase-root-id MuiOutlinedInput-root-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id MuiInputBase-fullWidth-id MuiInputBase-formControl-id" class="MuiFormControl-root-id SingleSelectField-formControl-id"
> >
<label
class="MuiFormLabel-root-id MuiInputLabel-root-id SingleSelectField-label-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-shrink-id MuiInputLabel-filled-id MuiFormLabel-disabled-id MuiInputLabel-disabled-id MuiFormLabel-filled-id"
data-shrink="true"
>
Catalog Input type for Store Owner
</label>
<div <div
aria-haspopup="listbox" class="MuiInputBase-root-id MuiOutlinedInput-root-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id MuiInputBase-fullWidth-id MuiInputBase-formControl-id"
aria-labelledby=" mui-component-select-inputType"
class="MuiSelect-root-id MuiSelect-select-id MuiSelect-selectMenu-id MuiSelect-outlined-id MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id MuiInputBase-inputSelect-id MuiOutlinedInput-inputSelect-id MuiSelect-disabled-id"
id="mui-component-select-inputType"
role="button"
> >
Dropdown <div
</div> aria-haspopup="listbox"
<input aria-labelledby=" mui-component-select-inputType"
name="inputType" class="MuiSelect-root-id MuiSelect-select-id MuiSelect-selectMenu-id MuiSelect-outlined-id MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id MuiInputBase-inputSelect-id MuiOutlinedInput-inputSelect-id MuiSelect-disabled-id"
type="hidden" id="mui-component-select-inputType"
value="DROPDOWN" role="button"
/>
<svg
aria-hidden="true"
class="MuiSvgIcon-root-id MuiSelect-icon-id MuiSelect-iconOutlined-id"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M7 10l5 5 5-5z"
/>
</svg>
<fieldset
aria-hidden="true"
class="PrivateNotchedOutline-root-id MuiOutlinedInput-notchedOutline-id"
style="padding-left:8px"
>
<legend
class="PrivateNotchedOutline-legend-id"
style="width:143px"
> >
<span> Dropdown
</div>
</span> <input
</legend> name="inputType"
</fieldset> type="hidden"
value="DROPDOWN"
/>
<svg
aria-hidden="true"
class="MuiSvgIcon-root-id MuiSelect-icon-id MuiSelect-iconOutlined-id"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M7 10l5 5 5-5z"
/>
</svg>
<fieldset
aria-hidden="true"
class="PrivateNotchedOutline-root-id MuiOutlinedInput-notchedOutline-id"
style="padding-left:8px"
>
<legend
class="PrivateNotchedOutline-legend-id"
style="width:143px"
>
<span>
</span>
</legend>
</fieldset>
</div>
</div> </div>
</div> </div>
<div <div
@ -32409,56 +32425,60 @@ exports[`Storyshots Views / Attributes / Attribute details multiple select input
class="FormSpacer-spacer-id" class="FormSpacer-spacer-id"
/> />
<div <div
class="MuiFormControl-root-id SingleSelectField-formControl-id" class="AttributeDetails-inputTypeSection-id"
> >
<label
class="MuiFormLabel-root-id MuiInputLabel-root-id SingleSelectField-label-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-shrink-id MuiInputLabel-filled-id MuiFormLabel-disabled-id MuiInputLabel-disabled-id MuiFormLabel-filled-id"
data-shrink="true"
>
Catalog Input type for Store Owner
</label>
<div <div
class="MuiInputBase-root-id MuiOutlinedInput-root-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id MuiInputBase-fullWidth-id MuiInputBase-formControl-id" class="MuiFormControl-root-id SingleSelectField-formControl-id"
> >
<label
class="MuiFormLabel-root-id MuiInputLabel-root-id SingleSelectField-label-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-shrink-id MuiInputLabel-filled-id MuiFormLabel-disabled-id MuiInputLabel-disabled-id MuiFormLabel-filled-id"
data-shrink="true"
>
Catalog Input type for Store Owner
</label>
<div <div
aria-haspopup="listbox" class="MuiInputBase-root-id MuiOutlinedInput-root-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id MuiInputBase-fullWidth-id MuiInputBase-formControl-id"
aria-labelledby=" mui-component-select-inputType"
class="MuiSelect-root-id MuiSelect-select-id MuiSelect-selectMenu-id MuiSelect-outlined-id MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id MuiInputBase-inputSelect-id MuiOutlinedInput-inputSelect-id MuiSelect-disabled-id"
id="mui-component-select-inputType"
role="button"
> >
Multiple Select <div
</div> aria-haspopup="listbox"
<input aria-labelledby=" mui-component-select-inputType"
name="inputType" class="MuiSelect-root-id MuiSelect-select-id MuiSelect-selectMenu-id MuiSelect-outlined-id MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id MuiInputBase-inputSelect-id MuiOutlinedInput-inputSelect-id MuiSelect-disabled-id"
type="hidden" id="mui-component-select-inputType"
value="MULTISELECT" role="button"
/>
<svg
aria-hidden="true"
class="MuiSvgIcon-root-id MuiSelect-icon-id MuiSelect-iconOutlined-id"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M7 10l5 5 5-5z"
/>
</svg>
<fieldset
aria-hidden="true"
class="PrivateNotchedOutline-root-id MuiOutlinedInput-notchedOutline-id"
style="padding-left:8px"
>
<legend
class="PrivateNotchedOutline-legend-id"
style="width:143px"
> >
<span> Multiple Select
</div>
</span> <input
</legend> name="inputType"
</fieldset> type="hidden"
value="MULTISELECT"
/>
<svg
aria-hidden="true"
class="MuiSvgIcon-root-id MuiSelect-icon-id MuiSelect-iconOutlined-id"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M7 10l5 5 5-5z"
/>
</svg>
<fieldset
aria-hidden="true"
class="PrivateNotchedOutline-root-id MuiOutlinedInput-notchedOutline-id"
style="padding-left:8px"
>
<legend
class="PrivateNotchedOutline-legend-id"
style="width:143px"
>
<span>
</span>
</legend>
</fieldset>
</div>
</div> </div>
</div> </div>
<div <div
@ -33480,56 +33500,60 @@ exports[`Storyshots Views / Attributes / Attribute details no values 1`] = `
class="FormSpacer-spacer-id" class="FormSpacer-spacer-id"
/> />
<div <div
class="MuiFormControl-root-id SingleSelectField-formControl-id" class="AttributeDetails-inputTypeSection-id"
> >
<label
class="MuiFormLabel-root-id MuiInputLabel-root-id SingleSelectField-label-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-shrink-id MuiInputLabel-filled-id MuiFormLabel-disabled-id MuiInputLabel-disabled-id MuiFormLabel-filled-id"
data-shrink="true"
>
Catalog Input type for Store Owner
</label>
<div <div
class="MuiInputBase-root-id MuiOutlinedInput-root-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id MuiInputBase-fullWidth-id MuiInputBase-formControl-id" class="MuiFormControl-root-id SingleSelectField-formControl-id"
> >
<label
class="MuiFormLabel-root-id MuiInputLabel-root-id SingleSelectField-label-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-shrink-id MuiInputLabel-filled-id MuiFormLabel-disabled-id MuiInputLabel-disabled-id MuiFormLabel-filled-id"
data-shrink="true"
>
Catalog Input type for Store Owner
</label>
<div <div
aria-haspopup="listbox" class="MuiInputBase-root-id MuiOutlinedInput-root-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id MuiInputBase-fullWidth-id MuiInputBase-formControl-id"
aria-labelledby=" mui-component-select-inputType"
class="MuiSelect-root-id MuiSelect-select-id MuiSelect-selectMenu-id MuiSelect-outlined-id MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id MuiInputBase-inputSelect-id MuiOutlinedInput-inputSelect-id MuiSelect-disabled-id"
id="mui-component-select-inputType"
role="button"
> >
Dropdown <div
</div> aria-haspopup="listbox"
<input aria-labelledby=" mui-component-select-inputType"
name="inputType" class="MuiSelect-root-id MuiSelect-select-id MuiSelect-selectMenu-id MuiSelect-outlined-id MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-disabled-id MuiOutlinedInput-disabled-id MuiInputBase-inputSelect-id MuiOutlinedInput-inputSelect-id MuiSelect-disabled-id"
type="hidden" id="mui-component-select-inputType"
value="DROPDOWN" role="button"
/>
<svg
aria-hidden="true"
class="MuiSvgIcon-root-id MuiSelect-icon-id MuiSelect-iconOutlined-id"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M7 10l5 5 5-5z"
/>
</svg>
<fieldset
aria-hidden="true"
class="PrivateNotchedOutline-root-id MuiOutlinedInput-notchedOutline-id"
style="padding-left:8px"
>
<legend
class="PrivateNotchedOutline-legend-id"
style="width:143px"
> >
<span> Dropdown
</div>
</span> <input
</legend> name="inputType"
</fieldset> type="hidden"
value="DROPDOWN"
/>
<svg
aria-hidden="true"
class="MuiSvgIcon-root-id MuiSelect-icon-id MuiSelect-iconOutlined-id"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M7 10l5 5 5-5z"
/>
</svg>
<fieldset
aria-hidden="true"
class="PrivateNotchedOutline-root-id MuiOutlinedInput-notchedOutline-id"
style="padding-left:8px"
>
<legend
class="PrivateNotchedOutline-legend-id"
style="width:143px"
>
<span>
</span>
</legend>
</fieldset>
</div>
</div> </div>
</div> </div>
<div <div