From 88b8950408733eb94ef93964912dbfe999377ce0 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Thu, 17 Sep 2020 13:31:09 +0200 Subject: [PATCH] Add drag-and-drop to allow variants reordering --- src/components/ImageUpload/ImageUpload.tsx | 38 ++++++------ .../ProductImages/ProductImages.tsx | 5 +- .../ProductUpdatePage/ProductUpdatePage.tsx | 5 +- .../ProductVariantCreatePage.tsx | 6 +- .../ProductVariantNavigation.tsx | 58 ++++++++++++------- .../ProductVariantPage/ProductVariantPage.tsx | 6 +- .../ProductVariants/ProductVariants.tsx | 24 +++++--- .../views/ProductUpdate/ProductUpdate.tsx | 1 + src/products/views/ProductVariant.tsx | 1 + src/products/views/ProductVariantCreate.tsx | 1 + .../stories/products/ProductUpdatePage.tsx | 1 + .../products/ProductVariantCreatePage.tsx | 4 ++ .../stories/products/ProductVariantPage.tsx | 3 + 13 files changed, 98 insertions(+), 55 deletions(-) diff --git a/src/components/ImageUpload/ImageUpload.tsx b/src/components/ImageUpload/ImageUpload.tsx index f0b1d18a2..65530d2fe 100644 --- a/src/components/ImageUpload/ImageUpload.tsx +++ b/src/components/ImageUpload/ImageUpload.tsx @@ -15,6 +15,7 @@ interface ImageUploadProps { isActiveClassName?: string; iconContainerClassName?: string; iconContainerActiveClassName?: string; + hideUploadIcon?: boolean; onImageUpload: (file: FileList) => void; } @@ -66,6 +67,7 @@ export const ImageUpload: React.FC = props => { iconContainerActiveClassName, iconContainerClassName, isActiveClassName, + hideUploadIcon, onImageUpload } = props; @@ -82,24 +84,26 @@ export const ImageUpload: React.FC = props => { [isActiveClassName]: isDragActive })} > -
- - - - + - -
+ + + + + + )} {children && children({ isDragActive })} diff --git a/src/products/components/ProductImages/ProductImages.tsx b/src/products/components/ProductImages/ProductImages.tsx index bc3c750ca..2890342cd 100644 --- a/src/products/components/ProductImages/ProductImages.tsx +++ b/src/products/components/ProductImages/ProductImages.tsx @@ -83,9 +83,6 @@ const useStyles = makeStyles( imageUploadActive: { zIndex: 1 }, - imageUploadIcon: { - display: "none" - }, imageUploadIconActive: { display: "block" }, @@ -253,7 +250,7 @@ const ProductImages: React.FC = props => { className={classes.imageUpload} isActiveClassName={classes.imageUploadActive} disableClick={true} - iconContainerClassName={classes.imageUploadIcon} + hideUploadIcon={true} iconContainerActiveClassName={classes.imageUploadIconActive} onImageUpload={handleImageUpload} > diff --git a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx index d3be9d79e..ad8f27270 100644 --- a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx +++ b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx @@ -18,7 +18,7 @@ import { sectionNames } from "@saleor/intl"; import { maybe } from "@saleor/misc"; import { SearchCategories_search_edges_node } from "@saleor/searches/types/SearchCategories"; import { SearchCollections_search_edges_node } from "@saleor/searches/types/SearchCollections"; -import { FetchMoreProps, ListActions } from "@saleor/types"; +import { FetchMoreProps, ListActions, ReorderAction } from "@saleor/types"; import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler"; import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger"; @@ -73,6 +73,7 @@ export interface ProductUpdatePageProps extends ListActions { fetchCollections: (query: string) => void; onVariantsAdd: () => void; onVariantShow: (id: string) => () => void; + onVariantReorder: ReorderAction; onImageDelete: (id: string) => () => void; onBack?(); onDelete(); @@ -120,6 +121,7 @@ export const ProductUpdatePage: React.FC = ({ onVariantAdd, onVariantsAdd, onVariantShow, + onVariantReorder, isChecked, selected, toggle, @@ -302,6 +304,7 @@ export const ProductUpdatePage: React.FC = ({ onRowClick={onVariantShow} onVariantAdd={onVariantAdd} onVariantsAdd={onVariantsAdd} + onVariantReorder={onVariantReorder} toolbar={toolbar} isChecked={isChecked} selected={selected} diff --git a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx index ac79fad42..1237d8b98 100644 --- a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx +++ b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx @@ -14,6 +14,7 @@ import useFormset, { } from "@saleor/hooks/useFormset"; import { getVariantAttributeInputFromProduct } from "@saleor/products/utils/data"; import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses"; +import { ReorderAction } from "@saleor/types"; import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger"; import React from "react"; import { useIntl } from "react-intl"; @@ -56,6 +57,7 @@ interface ProductVariantCreatePageProps { onBack: () => void; onSubmit: (data: ProductVariantCreatePageSubmitData) => void; onVariantClick: (variantId: string) => void; + onVariantReorder: ReorderAction; } const ProductVariantCreatePage: React.FC = ({ @@ -69,7 +71,8 @@ const ProductVariantCreatePage: React.FC = ({ weightUnit, onBack, onSubmit, - onVariantClick + onVariantClick, + onVariantReorder }) => { const intl = useIntl(); const attributeInput = React.useMemo( @@ -131,6 +134,7 @@ const ProductVariantCreatePage: React.FC = ({ return onVariantClick(variantId); } }} + onReorder={onVariantReorder} />
diff --git a/src/products/components/ProductVariantNavigation/ProductVariantNavigation.tsx b/src/products/components/ProductVariantNavigation/ProductVariantNavigation.tsx index 89f0e7d55..249f03af6 100644 --- a/src/products/components/ProductVariantNavigation/ProductVariantNavigation.tsx +++ b/src/products/components/ProductVariantNavigation/ProductVariantNavigation.tsx @@ -1,13 +1,17 @@ import Button from "@material-ui/core/Button"; import Card from "@material-ui/core/Card"; import { makeStyles } from "@material-ui/core/styles"; -import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; import TableRow from "@material-ui/core/TableRow"; import CardTitle from "@saleor/components/CardTitle"; import ResponsiveTable from "@saleor/components/ResponsiveTable"; import Skeleton from "@saleor/components/Skeleton"; +import { + SortableTableBody, + SortableTableRow +} from "@saleor/components/SortableTable"; import TableCellAvatar from "@saleor/components/TableCellAvatar"; +import { ReorderAction } from "@saleor/types"; import classNames from "classnames"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; @@ -26,16 +30,18 @@ const useStyles = makeStyles( cursor: "pointer" }, tabActive: { - "&:before": { - background: theme.palette.primary.main, - content: '""', - height: "100%", - left: 0, - position: "absolute", - top: 0, - width: 2 - }, - position: "relative" + "& > td:first-child": { + "&:before": { + background: theme.palette.primary.main, + content: '""', + height: "100%", + left: 0, + position: "absolute", + top: 0, + width: 2 + }, + position: "relative" + } } }), { name: "ProductVariantNavigation" } @@ -49,10 +55,18 @@ interface ProductVariantNavigationProps { | ProductVariantCreateData_product_variants[]; onAdd?: () => void; onRowClick: (variantId: string) => void; + onReorder: ReorderAction; } const ProductVariantNavigation: React.FC = props => { - const { current, fallbackThumbnail, variants, onAdd, onRowClick } = props; + const { + current, + fallbackThumbnail, + variants, + onAdd, + onRowClick, + onReorder + } = props; const classes = useStyles(props); const intl = useIntl(); @@ -66,18 +80,18 @@ const ProductVariantNavigation: React.FC = props })} /> - - {renderCollection(variants, variant => ( - + {renderCollection(variants, (variant, variantIndex) => ( + onRowClick(variant.id) : undefined} > variant.images[0].url, fallbackThumbnail @@ -86,11 +100,11 @@ const ProductVariantNavigation: React.FC = props {variant ? variant.name || variant.sku : } - + ))} {onAdd ? ( - +
diff --git a/src/products/components/ProductVariants/ProductVariants.tsx b/src/products/components/ProductVariants/ProductVariants.tsx index 86ee81aa4..90c798629 100644 --- a/src/products/components/ProductVariants/ProductVariants.tsx +++ b/src/products/components/ProductVariants/ProductVariants.tsx @@ -3,9 +3,7 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; import Hidden from "@material-ui/core/Hidden"; import { makeStyles } from "@material-ui/core/styles"; -import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; -import TableRow from "@material-ui/core/TableRow"; import Typography from "@material-ui/core/Typography"; import CardTitle from "@saleor/components/CardTitle"; import Checkbox from "@saleor/components/Checkbox"; @@ -14,13 +12,17 @@ import Money from "@saleor/components/Money"; import ResponsiveTable from "@saleor/components/ResponsiveTable"; import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; import Skeleton from "@saleor/components/Skeleton"; +import { + SortableTableBody, + SortableTableRow +} from "@saleor/components/SortableTable"; import TableHead from "@saleor/components/TableHead"; import { ProductVariant_costPrice } from "@saleor/fragments/types/ProductVariant"; import React from "react"; import { FormattedMessage, IntlShape, useIntl } from "react-intl"; import { maybe, renderCollection } from "../../../misc"; -import { ListActions } from "../../../types"; +import { ListActions, ReorderAction } from "../../../types"; import { ProductDetails_product_variants, ProductDetails_product_variants_stocks_warehouse @@ -171,12 +173,13 @@ interface ProductVariantsProps extends ListActions { disabled: boolean; variants: ProductDetails_product_variants[]; fallbackPrice?: ProductVariant_costPrice; + onVariantReorder: ReorderAction; onRowClick: (id: string) => () => void; onVariantAdd?(); onVariantsAdd?(); } -const numberOfColumns = 5; +const numberOfColumns = 6; export const ProductVariants: React.FC = props => { const { @@ -186,6 +189,7 @@ export const ProductVariants: React.FC = props => { onRowClick, onVariantAdd, onVariantsAdd, + onVariantReorder, isChecked, selected, toggle, @@ -266,6 +270,7 @@ export const ProductVariants: React.FC = props => { items={variants} toggleAll={toggleAll} toolbar={toolbar} + dragRows > = props => { /> - - {renderCollection(variants, variant => { + + {renderCollection(variants, (variant, variantIndex) => { const isSelected = variant ? isChecked(variant.id) : false; const numAvailable = variant && variant.stocks @@ -303,11 +308,12 @@ export const ProductVariants: React.FC = props => { : null; return ( - @@ -354,10 +360,10 @@ export const ProductVariants: React.FC = props => { ) )} - + ); })} - + )} diff --git a/src/products/views/ProductUpdate/ProductUpdate.tsx b/src/products/views/ProductUpdate/ProductUpdate.tsx index a5f9c4e57..33b12df25 100644 --- a/src/products/views/ProductUpdate/ProductUpdate.tsx +++ b/src/products/views/ProductUpdate/ProductUpdate.tsx @@ -289,6 +289,7 @@ export const ProductUpdate: React.FC = ({ id, params }) => { onVariantsAdd={() => navigate(productVariantCreatorUrl(id))} onVariantShow={variantId => () => navigate(productVariantEditUrl(product.id, variantId))} + onVariantReorder={() => undefined} // TODO: ... onImageUpload={handleImageUpload} onImageEdit={handleImageEdit} onImageDelete={handleImageDelete} diff --git a/src/products/views/ProductVariant.tsx b/src/products/views/ProductVariant.tsx index bce7b327b..36fac92c9 100644 --- a/src/products/views/ProductVariant.tsx +++ b/src/products/views/ProductVariant.tsx @@ -202,6 +202,7 @@ export const ProductVariant: React.FC = ({ onVariantClick={variantId => { navigate(productVariantEditUrl(productId, variantId)); }} + onVariantReorder={() => undefined} // TODO: ... /> = ({ onBack={handleBack} onSubmit={handleSubmit} onVariantClick={handleVariantClick} + onVariantReorder={() => undefined} // TODO: ... saveButtonBarState={variantCreateResult.status} warehouses={ warehouses.data?.warehouses.edges.map(edge => edge.node) || [] diff --git a/src/storybook/stories/products/ProductUpdatePage.tsx b/src/storybook/stories/products/ProductUpdatePage.tsx index 7c00bcf01..7682f8369 100644 --- a/src/storybook/stories/products/ProductUpdatePage.tsx +++ b/src/storybook/stories/products/ProductUpdatePage.tsx @@ -34,6 +34,7 @@ const props: ProductUpdatePageProps = { onImageUpload: () => undefined, onSubmit: () => undefined, onVariantAdd: () => undefined, + onVariantReorder: () => undefined, onVariantShow: () => undefined, onVariantsAdd: () => undefined, placeholderImage, diff --git a/src/storybook/stories/products/ProductVariantCreatePage.tsx b/src/storybook/stories/products/ProductVariantCreatePage.tsx index fee28f4a7..d8578724d 100644 --- a/src/storybook/stories/products/ProductVariantCreatePage.tsx +++ b/src/storybook/stories/products/ProductVariantCreatePage.tsx @@ -23,6 +23,7 @@ storiesOf("Views / Products / Create product variant", module) onBack={() => undefined} onSubmit={() => undefined} onVariantClick={undefined} + onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} /> @@ -54,6 +55,7 @@ storiesOf("Views / Products / Create product variant", module) onBack={() => undefined} onSubmit={() => undefined} onVariantClick={undefined} + onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} /> @@ -69,6 +71,7 @@ storiesOf("Views / Products / Create product variant", module) onBack={() => undefined} onSubmit={() => undefined} onVariantClick={undefined} + onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} /> @@ -87,6 +90,7 @@ storiesOf("Views / Products / Create product variant", module) onBack={() => undefined} onSubmit={() => undefined} onVariantClick={undefined} + onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} /> diff --git a/src/storybook/stories/products/ProductVariantPage.tsx b/src/storybook/stories/products/ProductVariantPage.tsx index f4d50e4ca..cca477a73 100644 --- a/src/storybook/stories/products/ProductVariantPage.tsx +++ b/src/storybook/stories/products/ProductVariantPage.tsx @@ -24,6 +24,7 @@ storiesOf("Views / Products / Product variant details", module) onImageSelect={() => undefined} onSubmit={() => undefined} onVariantClick={() => undefined} + onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} /> @@ -41,6 +42,7 @@ storiesOf("Views / Products / Product variant details", module) onImageSelect={() => undefined} onSubmit={() => undefined} onVariantClick={() => undefined} + onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} /> @@ -56,6 +58,7 @@ storiesOf("Views / Products / Product variant details", module) onImageSelect={() => undefined} onSubmit={() => undefined} onVariantClick={() => undefined} + onVariantReorder={() => undefined} saveButtonBarState="default" errors={[ {