diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index 74e67b150..57c44198f 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -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" diff --git a/schema.graphql b/schema.graphql index 2d320ab14..e72a6545a 100644 --- a/schema.graphql +++ b/schema.graphql @@ -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 diff --git a/src/components/Attributes/Attributes.stories.tsx b/src/components/Attributes/Attributes.stories.tsx index b2f222856..ba294cbd9 100644 --- a/src/components/Attributes/Attributes.stories.tsx +++ b/src/components/Attributes/Attributes.stories.tsx @@ -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) diff --git a/src/components/Attributes/Attributes.tsx b/src/components/Attributes/Attributes.tsx index 9364ec721..87a9077bf 100644 --- a/src/components/Attributes/Attributes.tsx +++ b/src/components/Attributes/Attributes.tsx @@ -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 = ({ title, onChange, onMultiChange, - onFileChange + onFileChange, + onReferencesChange, + onReferencesChangeClick, + onReferencesReorder }) => { const intl = useIntl(); const classes = useStyles({}); @@ -255,76 +264,91 @@ const Attributes: React.FC = ({ return ( {attributeIndex > 0 &&
} - -
- {attribute.label} -
-
- {attribute.data.inputType === - AttributeInputTypeEnum.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 ? ( - 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} - /> - ) : ( - - onMultiChange(attribute.id, event.target.value) - } - allowCustomValues={!attribute.data.isRequired} - /> - )} -
-
+ + onReferencesChange( + attribute.id, + attribute.value?.filter(id => id !== value) + ) + } + onValueReorder={onReferencesReorder} + loading={loading} + /> + + ) : attribute.data.inputType === + AttributeInputTypeEnum.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 ? ( + + 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} + /> + + ) : ( + + + onMultiChange(attribute.id, event.target.value) + } + allowCustomValues={!attribute.data.isRequired} + /> + + )}
); })} diff --git a/src/components/Attributes/BasicAttributeRow.tsx b/src/components/Attributes/BasicAttributeRow.tsx new file mode 100644 index 000000000..ee0bb61dc --- /dev/null +++ b/src/components/Attributes/BasicAttributeRow.tsx @@ -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 = props => { + const { label, children } = props; + const classes = useStyles(props); + + return ( + +
+ {label} +
+
{children}
+
+ ); +}; + +BasicAttributeRow.displayName = "BasicAttributeRow"; +export default BasicAttributeRow; diff --git a/src/components/Attributes/ExtendedAttributeRow.tsx b/src/components/Attributes/ExtendedAttributeRow.tsx new file mode 100644 index 000000000..1bc9ac70b --- /dev/null +++ b/src/components/Attributes/ExtendedAttributeRow.tsx @@ -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 = props => { + const { label, selectLabel, disabled, onSelect, children } = props; + const classes = useStyles(props); + + return ( + <> + +
+ {label} +
+
+ +
+
+
{children}
+ + ); +}; + +ExtendedAttributeRow.displayName = "ExtendedAttributeRow"; +export default ExtendedAttributeRow; diff --git a/src/components/Attributes/fixtures.ts b/src/components/Attributes/fixtures.ts index 6a62b561c..e6aff799d 100644 --- a/src/components/Attributes/fixtures.ts +++ b/src/components/Attributes/fixtures.ts @@ -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 + ] } ]; diff --git a/src/storybook/stories/components/Chip.tsx b/src/components/Chip/Chip.stories.tsx similarity index 77% rename from src/storybook/stories/components/Chip.tsx rename to src/components/Chip/Chip.stories.tsx index 99ad5c9bf..0f582b110 100644 --- a/src/storybook/stories/components/Chip.tsx +++ b/src/components/Chip/Chip.stories.tsx @@ -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" }; diff --git a/src/components/SortableChip/SortableChip.stories.tsx b/src/components/SortableChip/SortableChip.stories.tsx new file mode 100644 index 000000000..30b82ea4c --- /dev/null +++ b/src/components/SortableChip/SortableChip.stories.tsx @@ -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", () => ( + + + + )) + .add("with x", () => ( + + undefined} /> + + )); diff --git a/src/components/SortableChip/SortableChip.tsx b/src/components/SortableChip/SortableChip.tsx new file mode 100644 index 000000000..c78900d3c --- /dev/null +++ b/src/components/SortableChip/SortableChip.tsx @@ -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(props => { + const { className, label, onClose } = props; + + const classes = useStyles(props); + + return ( +
+
+ + {label} + {onClose && ( + + )} +
+
+ ); +}); + +SortableChip.displayName = "SortableChip"; +export default SortableChip; diff --git a/src/components/SortableChip/SortableHandle.tsx b/src/components/SortableChip/SortableHandle.tsx new file mode 100644 index 000000000..7bfad44d4 --- /dev/null +++ b/src/components/SortableChip/SortableHandle.tsx @@ -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 ( + + ); +}); + +export default SortableHandle; diff --git a/src/components/SortableChip/index.ts b/src/components/SortableChip/index.ts new file mode 100644 index 000000000..fabe8557d --- /dev/null +++ b/src/components/SortableChip/index.ts @@ -0,0 +1,2 @@ +export { default } from "./SortableChip"; +export * from "./SortableChip"; diff --git a/src/components/SortableChipsField/SortableChipsField.stories.tsx b/src/components/SortableChipsField/SortableChipsField.stories.tsx new file mode 100644 index 000000000..5f3f383da --- /dev/null +++ b/src/components/SortableChipsField/SortableChipsField.stories.tsx @@ -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", () => ) + .add("loading", () => ); diff --git a/src/components/SortableChipsField/SortableChipsField.tsx b/src/components/SortableChipsField/SortableChipsField.tsx new file mode 100644 index 000000000..ef44b8b4c --- /dev/null +++ b/src/components/SortableChipsField/SortableChipsField.tsx @@ -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 = props => { + const { loading, values, onValueDelete, onValueReorder } = props; + const classes = useStyles(props); + + return ( + +
+ {loading ? ( + + ) : ( + values.map((value, valueIndex) => ( + onValueDelete(value.value)} + /> + )) + )} +
+
+ ); +}; + +SortableChipsField.displayName = "SortableChipsField"; +export default SortableChipsField; diff --git a/src/components/SortableChipsField/SortableContainer.tsx b/src/components/SortableChipsField/SortableContainer.tsx new file mode 100644 index 000000000..01f88e250 --- /dev/null +++ b/src/components/SortableChipsField/SortableContainer.tsx @@ -0,0 +1,5 @@ +import { SortableContainer as SortableContainerHoc } from "react-sortable-hoc"; + +const SortableContainer = SortableContainerHoc(({ children }) => children); + +export default SortableContainer; diff --git a/src/components/SortableChipsField/index.ts b/src/components/SortableChipsField/index.ts new file mode 100644 index 000000000..80fc61c0a --- /dev/null +++ b/src/components/SortableChipsField/index.ts @@ -0,0 +1,2 @@ +export { default } from "./SortableChipsField"; +export * from "./SortableChipsField"; diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index 09ab12e52..eb5bd3e4c 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -61,7 +61,7 @@ exports[`Storyshots Attributes / Attributes default 1`] = `
- 3 Attributes + 4 Attributes
+ + +
+
+
@@ -338,7 +376,7 @@ exports[`Storyshots Attributes / Attributes disabled 1`] = `
- 3 Attributes + 4 Attributes
+ + +
+
+
@@ -618,7 +695,7 @@ exports[`Storyshots Attributes / Attributes selected 1`] = `
- 3 Attributes + 4 Attributes
+ + +
+
+
+
+ +
+ References First Value +
+ +
+
+
+
+ +
+ References Second Value +
+ +
+
+
+
+ +
+ References Third Value +
+ +
+
+
+
@@ -10397,6 +10693,547 @@ exports[`Storyshots Generics / Skeleton default 1`] = ` `; +exports[`Storyshots Generics / Sortable chip default 1`] = ` +
+
+
+
+
+ +
+ Lorem Ipsum +
+
+
+
+
+
+`; + +exports[`Storyshots Generics / Sortable chip with x 1`] = ` +
+
+
+
+
+ +
+ Lorem Ipsum +
+ +
+
+
+
+
+`; + +exports[`Storyshots Generics / Sortable chips field default 1`] = ` +
+
+
+
+
+
+ +
+ Item 1 +
+ +
+
+
+
+ +
+ Item 2 +
+ +
+
+
+
+ +
+ Item 3 +
+ +
+
+
+
+ +
+ Item 4 +
+ +
+
+
+
+ +
+ Item 5 +
+ +
+
+
+
+ +
+ Item 6 +
+ +
+
+
+
+
+
+`; + +exports[`Storyshots Generics / Sortable chips field loading 1`] = ` +
+
+
+
+ + ‌ + +
+
+
+
+`; + exports[`Storyshots Generics / Square Button default 1`] = `