1862 - Add references field to attributes section (#923)

* Add references field to attributes section

* Update messagees and test shapshots

* Remove unused style in sortable chips component
This commit is contained in:
Dawid Tarasiuk 2021-01-07 12:01:24 +01:00 committed by Jakub Majorek
parent 55a330040e
commit ee05b090b8
19 changed files with 1468 additions and 176 deletions

View file

@ -1543,6 +1543,10 @@
"src_dot_components_dot_AttributeUnassignDialog_dot_2037985699": {
"string": "Are you sure you want to unassign {attributeName} from {itemTypeName}?"
},
"src_dot_components_dot_Attributes_dot_3824528779": {
"context": "button label",
"string": "Assign references"
},
"src_dot_components_dot_Attributes_dot_attributesNumber": {
"context": "number of attributes",
"string": "{number} Attributes"

View file

@ -393,6 +393,7 @@ type Attribute implements Node & ObjectWithMetadata {
privateMetadata: [MetadataItem]!
metadata: [MetadataItem]!
inputType: AttributeInputTypeEnum
entityType: AttributeEntityTypeEnum
name: String
slug: String
type: AttributeTypeEnum
@ -431,6 +432,7 @@ type AttributeCreate {
input AttributeCreateInput {
inputType: AttributeInputTypeEnum
entityType: AttributeEntityTypeEnum
name: String!
slug: String
type: AttributeTypeEnum!
@ -450,6 +452,10 @@ type AttributeDelete {
attribute: Attribute
}
enum AttributeEntityTypeEnum {
PAGE
}
type AttributeError {
field: String
message: String
@ -490,6 +496,7 @@ enum AttributeInputTypeEnum {
DROPDOWN
MULTISELECT
FILE
REFERENCE
}
type AttributeReorderValues {
@ -566,6 +573,7 @@ type AttributeValue implements Node {
type: AttributeValueType @deprecated(reason: "Use the `inputType` field to determine the type of attribute's value. This field will be removed after 2020-07-31.")
translation(languageCode: LanguageCodeEnum!): AttributeValueTranslation
inputType: AttributeInputTypeEnum
reference: ID
file: File
}
@ -598,6 +606,7 @@ input AttributeValueInput {
values: [String]
file: String
contentType: String
references: [ID!]
}
type AttributeValueTranslatableContent implements Node {
@ -643,6 +652,7 @@ type BulkProductError {
message: String
code: ProductErrorCode!
attributes: [ID!]
values: [ID!]
index: Int
warehouses: [ID!]
channels: [ID!]
@ -653,6 +663,7 @@ type BulkStockError {
message: String
code: ProductErrorCode!
attributes: [ID!]
values: [ID!]
index: Int
}
@ -1082,6 +1093,7 @@ type CollectionChannelListingError {
message: String
code: ProductErrorCode!
attributes: [ID!]
values: [ID!]
channels: [ID!]
}
@ -2596,6 +2608,7 @@ type Mutation {
productTypeBulkDelete(ids: [ID]!): ProductTypeBulkDelete
productTypeUpdate(id: ID!, input: ProductTypeInput!): ProductTypeUpdate
productTypeReorderAttributes(moves: [ReorderInput]!, productTypeId: ID!, type: ProductAttributeType!): ProductTypeReorderAttributes
productReorderAttributeValues(attributeId: ID!, moves: [ReorderInput]!, productId: ID!): ProductReorderAttributeValues
digitalContentCreate(input: DigitalContentUploadInput!, variantId: ID!): DigitalContentCreate
digitalContentDelete(variantId: ID!): DigitalContentDelete
digitalContentUpdate(input: DigitalContentInput!, variantId: ID!): DigitalContentUpdate
@ -2611,6 +2624,7 @@ type Mutation {
productVariantSetDefault(productId: ID!, variantId: ID!): ProductVariantSetDefault
productVariantTranslate(id: ID!, input: NameTranslationInput!, languageCode: LanguageCodeEnum!): ProductVariantTranslate
productVariantChannelListingUpdate(id: ID!, input: [ProductVariantChannelListingAddInput!]!): ProductVariantChannelListingUpdate
productVariantReorderAttributeValues(attributeId: ID!, moves: [ReorderInput]!, variantId: ID!): ProductVariantReorderAttributeValues
variantImageAssign(imageId: ID!, variantId: ID!): VariantImageAssign
variantImageUnassign(imageId: ID!, variantId: ID!): VariantImageUnassign
paymentCapture(amount: PositiveDecimal, paymentId: ID!): PaymentCapture
@ -2630,6 +2644,7 @@ type Mutation {
pageAttributeAssign(attributeIds: [ID!]!, pageTypeId: ID!): PageAttributeAssign
pageAttributeUnassign(attributeIds: [ID!]!, pageTypeId: ID!): PageAttributeUnassign
pageTypeReorderAttributes(moves: [ReorderInput!]!, pageTypeId: ID!): PageTypeReorderAttributes
pageReorderAttributeValues(attributeId: ID!, moves: [ReorderInput]!, pageId: ID!): PageReorderAttributeValues
draftOrderComplete(id: ID!): DraftOrderComplete
draftOrderCreate(input: DraftOrderCreateInput!): DraftOrderCreate
draftOrderDelete(id: ID!): DraftOrderDelete
@ -3290,6 +3305,7 @@ type PageError {
message: String
code: PageErrorCode!
attributes: [ID!]
values: [ID!]
}
enum PageErrorCode {
@ -3324,6 +3340,12 @@ input PageInput {
seo: SeoInput
}
type PageReorderAttributeValues {
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
page: Page
pageErrors: [PageError!]!
}
enum PageSortField {
TITLE
SLUG
@ -3821,6 +3843,7 @@ type ProductChannelListingError {
message: String
code: ProductErrorCode!
attributes: [ID!]
values: [ID!]
channels: [ID!]
}
@ -3879,6 +3902,7 @@ type ProductError {
message: String
code: ProductErrorCode!
attributes: [ID!]
values: [ID!]
}
enum ProductErrorCode {
@ -4026,6 +4050,12 @@ type ProductPricingInfo {
priceRangeLocalCurrency: TaxedMoneyRange
}
type ProductReorderAttributeValues {
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
product: Product
productErrors: [ProductError!]!
}
input ProductStockFilterInput {
warehouseIds: [ID!]
quantity: IntRangeInput
@ -4172,8 +4202,12 @@ type ProductVariant implements Node & ObjectWithMetadata {
weight: Weight
privateMetadata: [MetadataItem]!
metadata: [MetadataItem]!
quantity: Int! @deprecated(reason: "Use the stock field instead. This field will be removed after 2020-07-31.")
quantityAllocated: Int @deprecated(reason: "Use the stock field instead. This field will be removed after 2020-07-31.")
stockQuantity: Int! @deprecated(reason: "Use the quantityAvailable field instead. This field will be removed after 2020-07-31.")
channelListings: [ProductVariantChannelListing!]
pricing: VariantPricingInfo
isAvailable: Boolean @deprecated(reason: "Use the stock field instead. This field will be removed after 2020-07-31.")
attributes(variantSelection: VariantAttributeScope): [SelectedAttribute!]!
costPrice: Money
margin: Int
@ -4278,6 +4312,12 @@ type ProductVariantReorder {
productErrors: [ProductError!]!
}
type ProductVariantReorderAttributeValues {
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
productVariant: ProductVariant
productErrors: [ProductError!]!
}
type ProductVariantSetDefault {
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
product: Product

View file

@ -12,7 +12,10 @@ const props: AttributesProps = {
loading: false,
onChange: () => undefined,
onFileChange: () => undefined,
onMultiChange: () => undefined
onMultiChange: () => undefined,
onReferencesChange: () => undefined,
onReferencesChangeClick: () => undefined,
onReferencesReorder: () => undefined
};
storiesOf("Attributes / Attributes", module)

View file

@ -5,7 +5,6 @@ import makeStyles from "@material-ui/core/styles/makeStyles";
import Typography from "@material-ui/core/Typography";
import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown";
import CardTitle from "@saleor/components/CardTitle";
import Grid from "@saleor/components/Grid";
import Hr from "@saleor/components/Hr";
import MultiAutocompleteSelectField, {
MultiAutocompleteChoiceType
@ -17,6 +16,7 @@ import { AttributeValueFragment } from "@saleor/fragments/types/AttributeValueFr
import { PageErrorWithAttributesFragment } from "@saleor/fragments/types/PageErrorWithAttributesFragment";
import { ProductErrorWithAttributesFragment } from "@saleor/fragments/types/ProductErrorWithAttributesFragment";
import { FormsetAtomicData, FormsetChange } from "@saleor/hooks/useFormset";
import { ReorderAction } from "@saleor/types";
import { AttributeInputTypeEnum } from "@saleor/types/globalTypes";
import { getProductErrorMessage } from "@saleor/utils/errors";
import getPageErrorMessage from "@saleor/utils/errors/page";
@ -30,6 +30,9 @@ import {
} from "react-intl";
import FileUploadField, { FileChoiceType } from "../FileUploadField";
import SortableChipsField from "../SortableChipsField";
import BasicAttributeRow from "./BasicAttributeRow";
import ExtendedAttributeRow from "./ExtendedAttributeRow";
import { VariantAttributeScope } from "./types";
export interface AttributeInputData {
@ -51,7 +54,10 @@ export interface AttributesProps {
title?: React.ReactNode;
onChange: FormsetChange;
onMultiChange: FormsetChange;
onFileChange?: FormsetChange; // TODO: temporairy optional, should be changed to required, after all pages implement it
onFileChange: FormsetChange;
onReferencesChange?: FormsetChange; // TODO: temporairy optional, should be changed to required, after all pages implement it
onReferencesChangeClick?: () => void; // TODO: temporairy optional, should be changed to required, after all pages implement it
onReferencesReorder?: ReorderAction; // TODO: temporairy optional, should be changed to required, after all pages implement it
}
const useStyles = makeStyles(
@ -210,7 +216,10 @@ const Attributes: React.FC<AttributesProps> = ({
title,
onChange,
onMultiChange,
onFileChange
onFileChange,
onReferencesChange,
onReferencesChangeClick,
onReferencesReorder
}) => {
const intl = useIntl();
const classes = useStyles({});
@ -255,76 +264,91 @@ const Attributes: React.FC<AttributesProps> = ({
return (
<React.Fragment key={attribute.id}>
{attributeIndex > 0 && <Hr />}
<Grid className={classes.attributeSection} variant="uniform">
<div
className={classes.attributeSectionLabel}
data-test="attribute-label"
{attribute.data.inputType ===
AttributeInputTypeEnum.REFERENCE ? (
<ExtendedAttributeRow
label={attribute.label}
selectLabel={intl.formatMessage({
defaultMessage: "Assign references",
description: "button label"
})}
onSelect={onReferencesChangeClick}
disabled={disabled}
>
<Typography>{attribute.label}</Typography>
</div>
<div data-test="attribute-value">
{attribute.data.inputType ===
AttributeInputTypeEnum.FILE ? (
<FileUploadField
className={classes.fileField}
disabled={disabled}
loading={loading}
file={getFileChoice(attribute)}
onFileUpload={file =>
onFileChange(attribute.id, file)
}
onFileDelete={() =>
onFileChange(attribute.id, undefined)
}
error={!!error}
helperText={getErrorMessage(error, intl)}
inputProps={{
name: `attribute:${attribute.label}`
}}
/>
) : attribute.data.inputType ===
AttributeInputTypeEnum.DROPDOWN ? (
<SingleAutocompleteSelectField
choices={getSingleChoices(attribute.data.values)}
disabled={disabled}
displayValue={
attribute.data.values.find(
value => value.slug === attribute.value[0]
)?.name ||
attribute.value[0] ||
""
}
emptyOption={!attribute.data.isRequired}
error={!!error}
helperText={getErrorMessage(error, intl)}
name={`attribute:${attribute.label}`}
label={intl.formatMessage(messages.valueLabel)}
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)}
disabled={disabled}
error={!!error}
helperText={getErrorMessage(error, intl)}
label={intl.formatMessage(
messages.multipleValueLable
)}
name={`attribute:${attribute.label}`}
value={attribute.value}
onChange={event =>
onMultiChange(attribute.id, event.target.value)
}
allowCustomValues={!attribute.data.isRequired}
/>
)}
</div>
</Grid>
<SortableChipsField
values={getMultiDisplayValue(attribute)}
onValueDelete={value =>
onReferencesChange(
attribute.id,
attribute.value?.filter(id => id !== value)
)
}
onValueReorder={onReferencesReorder}
loading={loading}
/>
</ExtendedAttributeRow>
) : attribute.data.inputType ===
AttributeInputTypeEnum.FILE ? (
<BasicAttributeRow label={attribute.label}>
<FileUploadField
className={classes.fileField}
disabled={disabled}
loading={loading}
file={getFileChoice(attribute)}
onFileUpload={file => onFileChange(attribute.id, file)}
onFileDelete={() =>
onFileChange(attribute.id, undefined)
}
error={!!error}
helperText={getErrorMessage(error, intl)}
inputProps={{
name: `attribute:${attribute.label}`
}}
/>
</BasicAttributeRow>
) : attribute.data.inputType ===
AttributeInputTypeEnum.DROPDOWN ? (
<BasicAttributeRow label={attribute.label}>
<SingleAutocompleteSelectField
choices={getSingleChoices(attribute.data.values)}
disabled={disabled}
displayValue={
attribute.data.values.find(
value => value.slug === attribute.value[0]
)?.name ||
attribute.value[0] ||
""
}
emptyOption={!attribute.data.isRequired}
error={!!error}
helperText={getErrorMessage(error, intl)}
name={`attribute:${attribute.label}`}
label={intl.formatMessage(messages.valueLabel)}
value={attribute.value[0]}
onChange={event =>
onChange(attribute.id, event.target.value)
}
allowCustomValues={!attribute.data.isRequired}
/>
</BasicAttributeRow>
) : (
<BasicAttributeRow label={attribute.label}>
<MultiAutocompleteSelectField
choices={getMultiChoices(attribute.data.values)}
displayValues={getMultiDisplayValue(attribute)}
disabled={disabled}
error={!!error}
helperText={getErrorMessage(error, intl)}
label={intl.formatMessage(messages.multipleValueLable)}
name={`attribute:${attribute.label}`}
value={attribute.value}
onChange={event =>
onMultiChange(attribute.id, event.target.value)
}
allowCustomValues={!attribute.data.isRequired}
/>
</BasicAttributeRow>
)}
</React.Fragment>
);
})}

View file

@ -0,0 +1,44 @@
import { makeStyles } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography";
import Grid from "@saleor/components/Grid";
import React from "react";
const useStyles = makeStyles(
theme => ({
attributeSection: {
"&:last-of-type": {
paddingBottom: 0
},
padding: theme.spacing(2, 0)
},
attributeSectionLabel: {
alignItems: "center",
display: "flex"
}
}),
{ name: "BasicAttributeRow" }
);
interface BasicAttributeRowProps {
label: string;
}
const BasicAttributeRow: React.FC<BasicAttributeRowProps> = props => {
const { label, children } = props;
const classes = useStyles(props);
return (
<Grid className={classes.attributeSection} variant="uniform">
<div
className={classes.attributeSectionLabel}
data-test="attribute-label"
>
<Typography>{label}</Typography>
</div>
<div data-test="attribute-value">{children}</div>
</Grid>
);
};
BasicAttributeRow.displayName = "BasicAttributeRow";
export default BasicAttributeRow;

View file

@ -0,0 +1,65 @@
import Button from "@material-ui/core/Button";
import { makeStyles } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography";
import Grid from "@saleor/components/Grid";
import React from "react";
const useStyles = makeStyles(
theme => ({
attributeSection: {
"&:last-of-type": {
paddingBottom: 0
},
padding: theme.spacing(2, 0)
},
attributeSectionButton: {
float: "right"
},
attributeSectionLabel: {
alignItems: "center",
display: "flex"
}
}),
{ name: "ExtendedAttributeRow" }
);
interface ExtendedAttributeRowProps {
label: string;
selectLabel: string;
disabled: boolean;
onSelect: () => void;
}
const ExtendedAttributeRow: React.FC<ExtendedAttributeRowProps> = props => {
const { label, selectLabel, disabled, onSelect, children } = props;
const classes = useStyles(props);
return (
<>
<Grid className={classes.attributeSection} variant="uniform">
<div
className={classes.attributeSectionLabel}
data-test="attribute-label"
>
<Typography>{label}</Typography>
</div>
<div data-test="attribute-selector">
<Button
className={classes.attributeSectionButton}
disabled={disabled}
variant="text"
color="primary"
data-test="button-attribute-selector"
onClick={onSelect}
>
{selectLabel}
</Button>
</div>
</Grid>
<div data-test="attribute-value">{children}</div>
</>
);
};
ExtendedAttributeRow.displayName = "ExtendedAttributeRow";
export default ExtendedAttributeRow;

View file

@ -79,15 +79,49 @@ const FILE_ATTRIBUTE: AttributeInput = {
}
]
},
id: "ifudbgidfsb",
id: "fguygygugyu",
label: "File Attribute",
value: []
};
const REFERENCE_ATTRIBUTE: AttributeInput = {
data: {
inputType: AttributeInputTypeEnum.REFERENCE,
isRequired: true,
values: [
{
__typename: "AttributeValue",
file: null,
id: "vbnhgcvjhbvhj",
name: "References First Value",
slug: "references-first-value"
},
{
__typename: "AttributeValue",
file: null,
id: "gucngdfdfvdvd",
name: "References Second Value",
slug: "references-second-value"
},
{
__typename: "AttributeValue",
file: null,
id: "dfdfdsfdsfdse",
name: "References Third Value",
slug: "references-third-value"
}
]
},
id: "kclsmcdsmcs",
label: "References Attribute",
value: []
};
export const ATTRIBUTES: AttributeInput[] = [
DROPDOWN_ATTRIBUTE,
MULTISELECT_ATTRIBUTE,
FILE_ATTRIBUTE
FILE_ATTRIBUTE,
REFERENCE_ATTRIBUTE
];
export const ATTRIBUTES_SELECTED: AttributeInput[] = [
@ -105,5 +139,13 @@ export const ATTRIBUTES_SELECTED: AttributeInput[] = [
{
...FILE_ATTRIBUTE,
value: [FILE_ATTRIBUTE.data.values[0].slug]
},
{
...REFERENCE_ATTRIBUTE,
value: [
REFERENCE_ATTRIBUTE.data.values[0].slug,
REFERENCE_ATTRIBUTE.data.values[1].slug,
REFERENCE_ATTRIBUTE.data.values[2].slug
]
}
];

View file

@ -1,10 +1,9 @@
import Chip, { ChipProps } from "@saleor/components/Chip";
import CardDecorator from "@saleor/storybook/CardDecorator";
import Decorator from "@saleor/storybook/Decorator";
import { storiesOf } from "@storybook/react";
import React from "react";
import CardDecorator from "../../CardDecorator";
import Decorator from "../../Decorator";
const props: ChipProps = {
label: "Lorem Ipsum"
};

View file

@ -0,0 +1,29 @@
import SortableChip, {
SortableChipProps
} from "@saleor/components/SortableChip";
import CardDecorator from "@saleor/storybook/CardDecorator";
import Decorator from "@saleor/storybook/Decorator";
import { storiesOf } from "@storybook/react";
import React from "react";
import { SortableContainer } from "react-sortable-hoc";
const Container = SortableContainer(props => props.children);
const props: SortableChipProps = {
index: 0,
label: "Lorem Ipsum"
};
storiesOf("Generics / Sortable chip", module)
.addDecorator(CardDecorator)
.addDecorator(Decorator)
.add("default", () => (
<Container>
<SortableChip {...props} />
</Container>
))
.add("with x", () => (
<Container>
<SortableChip {...props} onClose={() => undefined} />
</Container>
));

View file

@ -0,0 +1,68 @@
import { makeStyles } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography";
import CloseIcon from "@material-ui/icons/Close";
import classNames from "classnames";
import React from "react";
import { SortableElement, SortableElementProps } from "react-sortable-hoc";
import SortableHandle from "./SortableHandle";
export interface SortableChipProps extends SortableElementProps {
className?: string;
label: React.ReactNode;
onClose?: () => void;
}
const useStyles = makeStyles(
theme => ({
closeIcon: {
cursor: "pointer",
fontSize: 16,
marginLeft: theme.spacing(),
verticalAlign: "middle"
},
content: {
alignItems: "center",
display: "flex"
},
root: {
border: `1px solid ${theme.palette.divider}`,
borderRadius: 18,
display: "inline-block",
marginRight: theme.spacing(2),
padding: "6px 12px"
},
sortableHandle: {
marginRight: theme.spacing(1)
}
}),
{ name: "SortableChip" }
);
const SortableChip = SortableElement<SortableChipProps>(props => {
const { className, label, onClose } = props;
const classes = useStyles(props);
return (
<div className={classNames(classes.root, className)}>
<div className={classes.content}>
<SortableHandle
className={classes.sortableHandle}
data-test="button-drag-handle"
/>
<Typography data-test="chip-label">{label}</Typography>
{onClose && (
<CloseIcon
className={classes.closeIcon}
onClick={onClose}
data-test="button-close"
/>
)}
</div>
</div>
);
});
SortableChip.displayName = "SortableChip";
export default SortableChip;

View file

@ -0,0 +1,29 @@
import { makeStyles } from "@material-ui/core/styles";
import Draggable from "@saleor/icons/Draggable";
import classNames from "classnames";
import React from "react";
import { SortableHandle as SortableHandleHoc } from "react-sortable-hoc";
const useStyles = makeStyles(
{
drag: {
cursor: "grab"
}
},
{ name: "SortableHandle" }
);
interface SortableHandle {
className?: string;
}
const SortableHandle = SortableHandleHoc(props => {
const { className, ...restProps } = props;
const classes = useStyles(props);
return (
<Draggable className={classNames(classes.drag, className)} {...restProps} />
);
});
export default SortableHandle;

View file

@ -0,0 +1,2 @@
export { default } from "./SortableChip";
export * from "./SortableChip";

View file

@ -0,0 +1,27 @@
import CardDecorator from "@saleor/storybook/CardDecorator";
import Decorator from "@saleor/storybook/Decorator";
import { storiesOf } from "@storybook/react";
import React from "react";
import SortableChipsField, {
SortableChipsFieldProps
} from "./SortableChipsField";
const props: SortableChipsFieldProps = {
onValueDelete: () => undefined,
onValueReorder: () => undefined,
values: [
{ label: "Item 1", value: "item-1" },
{ label: "Item 2", value: "item-2" },
{ label: "Item 3", value: "item-3" },
{ label: "Item 4", value: "item-4" },
{ label: "Item 5", value: "item-5" },
{ label: "Item 6", value: "item-6" }
]
};
storiesOf("Generics / Sortable chips field", module)
.addDecorator(CardDecorator)
.addDecorator(Decorator)
.add("default", () => <SortableChipsField {...props} />)
.add("loading", () => <SortableChipsField {...props} loading={true} />);

View file

@ -0,0 +1,66 @@
import { makeStyles } from "@material-ui/core/styles";
import { ReorderAction } from "@saleor/types";
import React from "react";
import { SortableContainerProps } from "react-sortable-hoc";
import Skeleton from "../Skeleton";
import DraggableChip from "../SortableChip";
import SortableContainer from "./SortableContainer";
const useStyles = makeStyles(
theme => ({
chip: {
background: "#fff",
color: theme.palette.primary.dark,
marginBottom: theme.spacing(1)
}
}),
{
name: "SortableChipsField"
}
);
interface SortableChipsFieldValueType {
label: string;
value: any;
}
export interface SortableChipsFieldProps extends SortableContainerProps {
loading?: boolean;
values: SortableChipsFieldValueType[];
onValueDelete: (id: string) => void;
onValueReorder: ReorderAction;
}
const SortableChipsField: React.FC<SortableChipsFieldProps> = props => {
const { loading, values, onValueDelete, onValueReorder } = props;
const classes = useStyles(props);
return (
<SortableContainer
axis="xy"
lockAxis="xy"
useDragHandle
onSortEnd={onValueReorder}
>
<div>
{loading ? (
<Skeleton />
) : (
values.map((value, valueIndex) => (
<DraggableChip
className={classes.chip}
key={valueIndex}
index={valueIndex}
label={value.label}
onClose={() => onValueDelete(value.value)}
/>
))
)}
</div>
</SortableContainer>
);
};
SortableChipsField.displayName = "SortableChipsField";
export default SortableChipsField;

View file

@ -0,0 +1,5 @@
import { SortableContainer as SortableContainerHoc } from "react-sortable-hoc";
const SortableContainer = SortableContainerHoc(({ children }) => children);
export default SortableContainer;

View file

@ -0,0 +1,2 @@
export { default } from "./SortableChipsField";
export * from "./SortableChipsField";

File diff suppressed because it is too large Load diff

View file

@ -16,7 +16,6 @@ function loadStories() {
require("./stories/components/AutocompleteSelectMenu");
require("./stories/components/CardMenu");
require("./stories/components/Checkbox");
require("./stories/components/Chip");
require("./stories/components/ColumnPicker");
require("./stories/components/Date");
require("./stories/components/DateTime");

View file

@ -70,6 +70,10 @@ export enum AppTypeEnum {
THIRDPARTY = "THIRDPARTY",
}
export enum AttributeEntityTypeEnum {
PAGE = "PAGE",
}
export enum AttributeErrorCode {
ALREADY_EXISTS = "ALREADY_EXISTS",
GRAPHQL_ERROR = "GRAPHQL_ERROR",
@ -83,6 +87,7 @@ export enum AttributeInputTypeEnum {
DROPDOWN = "DROPDOWN",
FILE = "FILE",
MULTISELECT = "MULTISELECT",
REFERENCE = "REFERENCE",
}
export enum AttributeSortField {
@ -1022,6 +1027,7 @@ export interface AppTokenInput {
export interface AttributeCreateInput {
inputType?: AttributeInputTypeEnum | null;
entityType?: AttributeEntityTypeEnum | null;
name: string;
slug?: string | null;
type: AttributeTypeEnum;
@ -1084,6 +1090,7 @@ export interface AttributeValueInput {
values?: (string | null)[] | null;
file?: string | null;
contentType?: string | null;
references?: string[] | null;
}
export interface BulkAttributeValueInput {