diff --git a/CHANGELOG.md b/CHANGELOG.md index 90ec6d03a..77362c6c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,3 +12,4 @@ All notable, unreleased changes to this project will be documented in this file. - Use react-intl - #105 by @dominik-zeglen - Add dynamic dashboard settings - #135 by @benekex2 - Fix plugins page translations - #141 by @benekex2 +- Add attributes to column picker - #136 by @dominik-zeglen diff --git a/src/categories/components/CategoryProductList/CategoryProductList.tsx b/src/categories/components/CategoryProductList/CategoryProductList.tsx new file mode 100644 index 000000000..1d7a894a6 --- /dev/null +++ b/src/categories/components/CategoryProductList/CategoryProductList.tsx @@ -0,0 +1,243 @@ +import { + createStyles, + Theme, + withStyles, + WithStyles +} from "@material-ui/core/styles"; +import Table from "@material-ui/core/Table"; +import TableBody from "@material-ui/core/TableBody"; +import TableCell from "@material-ui/core/TableCell"; +import TableFooter from "@material-ui/core/TableFooter"; +import TableRow from "@material-ui/core/TableRow"; +import Checkbox from "@saleor/components/Checkbox"; +import Money from "@saleor/components/Money"; +import Skeleton from "@saleor/components/Skeleton"; +import StatusLabel from "@saleor/components/StatusLabel"; +import { FormattedMessage, useIntl } from "react-intl"; + +import TableCellAvatar, { + AVATAR_MARGIN +} from "@saleor/components/TableCellAvatar"; +import TableHead from "@saleor/components/TableHead"; +import TablePagination from "@saleor/components/TablePagination"; +import { maybe, renderCollection } from "@saleor/misc"; +import { ListActions, ListProps } from "@saleor/types"; +import React from "react"; +import { CategoryDetails_category_products_edges_node } from "../../types/CategoryDetails"; + +const styles = (theme: Theme) => + createStyles({ + [theme.breakpoints.up("lg")]: { + colName: { + width: "auto" + }, + colPrice: { + width: 200 + }, + colPublished: { + width: 200 + }, + colType: { + width: 200 + } + }, + colFill: { + padding: 0, + width: "100%" + }, + colName: {}, + colNameHeader: { + marginLeft: AVATAR_MARGIN + }, + colPrice: { + textAlign: "right" + }, + colPublished: {}, + colType: {}, + link: { + cursor: "pointer" + }, + table: { + tableLayout: "fixed" + }, + tableContainer: { + overflowX: "scroll" + }, + textLeft: { + textAlign: "left" + }, + textRight: { + textAlign: "right" + } + }); + +interface CategoryProductListProps + extends ListProps, + ListActions, + WithStyles { + products: CategoryDetails_category_products_edges_node[]; +} + +export const CategoryProductList = withStyles(styles, { + name: "CategoryProductList" +})( + ({ + classes, + disabled, + isChecked, + pageInfo, + products, + selected, + toggle, + toggleAll, + toolbar, + onNextPage, + onPreviousPage, + onRowClick + }: CategoryProductListProps) => { + const intl = useIntl(); + + const numberOfColumns = 5; + + return ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {renderCollection( + products, + product => { + const isSelected = product ? isChecked(product.id) : false; + + return ( + + + toggle(product.id)} + /> + + product.thumbnail.url)} + > + {product ? product.name : } + + + {product && product.productType ? ( + product.productType.name + ) : ( + + )} + + + {product && + maybe(() => product.isAvailable !== undefined) ? ( + + ) : ( + + )} + + + {maybe(() => product.basePrice) && + maybe(() => product.basePrice.amount) !== undefined && + maybe(() => product.basePrice.currency) !== undefined ? ( + + ) : ( + + )} + + + ); + }, + () => ( + + + + + + ) + )} + +
+
+ ); + } +); +CategoryProductList.displayName = "CategoryProductList"; +export default CategoryProductList; diff --git a/src/categories/components/CategoryProductList/index.ts b/src/categories/components/CategoryProductList/index.ts new file mode 100644 index 000000000..5bc658620 --- /dev/null +++ b/src/categories/components/CategoryProductList/index.ts @@ -0,0 +1,2 @@ +export { default } from "./CategoryProductList"; +export * from "./CategoryProductList"; diff --git a/src/categories/components/CategoryProducts/CategoryProducts.tsx b/src/categories/components/CategoryProducts/CategoryProducts.tsx index 3615351bd..dd170dc53 100644 --- a/src/categories/components/CategoryProducts/CategoryProducts.tsx +++ b/src/categories/components/CategoryProducts/CategoryProducts.tsx @@ -1,156 +1,72 @@ import Button from "@material-ui/core/Button"; import Card from "@material-ui/core/Card"; -import { - createStyles, - Theme, - withStyles, - WithStyles -} from "@material-ui/core/styles"; -import Table from "@material-ui/core/Table"; -import TableBody from "@material-ui/core/TableBody"; -import TableCell from "@material-ui/core/TableCell"; -import TableFooter from "@material-ui/core/TableFooter"; -import TableHead from "@material-ui/core/TableHead"; -import TableRow from "@material-ui/core/TableRow"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; import CardTitle from "@saleor/components/CardTitle"; -import Skeleton from "@saleor/components/Skeleton"; -import TableCellAvatar from "@saleor/components/TableCellAvatar"; -import TablePagination from "@saleor/components/TablePagination"; -import { maybe, renderCollection } from "@saleor/misc"; +import { ListActions, PageListProps } from "../../../types"; +import { CategoryDetails_category_products_edges_node } from "../../types/CategoryDetails"; +import CategoryProductList from "../CategoryProductList"; -const styles = (theme: Theme) => - createStyles({ - link: { - color: theme.palette.primary.main, - cursor: "pointer" - }, - textLeft: { - textAlign: "left" - } - }); - -interface ProductListProps extends WithStyles { - hasNextPage?: boolean; - hasPreviousPage?: boolean; - products?: Array<{ - id: string; - name: string; - productType: { - name: string; - }; - thumbnail: { - url: string; - }; - }>; - onAddProduct?(); - onNextPage?(); - onPreviousPage?(); - onRowClick?(id: string): () => void; +interface CategoryProductsProps extends PageListProps, ListActions { + products: CategoryDetails_category_products_edges_node[]; + categoryName: string; } -export const ProductList = withStyles(styles, { name: "ProductList" })( - ({ - classes, - hasNextPage, - hasPreviousPage, - products, - onAddProduct, - onNextPage, - onPreviousPage, - onRowClick - }: ProductListProps) => { - const intl = useIntl(); +export const CategoryProducts: React.StatelessComponent< + CategoryProductsProps +> = ({ + products, + disabled, + pageInfo, + onAdd, + onNextPage, + onPreviousPage, + onRowClick, + categoryName, + isChecked, + selected, + toggle, + toggleAll, + toolbar +}) => { + const intl = useIntl(); - return ( - - - - - } - /> - - - - {(products === undefined || products.length > 0) && } - - - - - - - - - - - - - - - {renderCollection( - products, - product => ( - - product.thumbnail.url)} - /> - - {product ? ( - - {product.name} - - ) : ( - - )} - - - {product && product.productType ? ( - product.productType.name - ) : ( - - )} - - - ), - () => ( - - - - - - ) - )} - -
-
- ); - } -); -ProductList.displayName = "CategoryProductList"; -export default ProductList; + return ( + + + + + } + /> + + + ); +}; + +CategoryProducts.displayName = "CategoryProducts"; +export default CategoryProducts; diff --git a/src/categories/components/CategoryProductsCard/CategoryProductsCard.tsx b/src/categories/components/CategoryProductsCard/CategoryProductsCard.tsx deleted file mode 100644 index 42124f661..000000000 --- a/src/categories/components/CategoryProductsCard/CategoryProductsCard.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import Button from "@material-ui/core/Button"; -import Card from "@material-ui/core/Card"; -import React from "react"; -import { FormattedMessage, useIntl } from "react-intl"; - -import CardTitle from "@saleor/components/CardTitle"; -import ProductList from "@saleor/components/ProductList"; -import { ListActions, PageListProps } from "../../../types"; -import { CategoryDetails_category_products_edges_node } from "../../types/CategoryDetails"; - -interface CategoryProductsCardProps extends PageListProps, ListActions { - products: CategoryDetails_category_products_edges_node[]; - categoryName: string; -} - -export const CategoryProductsCard: React.StatelessComponent< - CategoryProductsCardProps -> = ({ - products, - disabled, - pageInfo, - onAdd, - onNextPage, - onPreviousPage, - onRowClick, - categoryName, - isChecked, - selected, - toggle, - toggleAll, - toolbar -}) => { - const intl = useIntl(); - - return ( - - - - - } - /> - - - ); -}; - -CategoryProductsCard.displayName = "CategoryProductsCard"; -export default CategoryProductsCard; diff --git a/src/categories/components/CategoryProductsCard/index.ts b/src/categories/components/CategoryProductsCard/index.ts deleted file mode 100644 index 741d2b708..000000000 --- a/src/categories/components/CategoryProductsCard/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from "./CategoryProductsCard"; -export * from "./CategoryProductsCard"; diff --git a/src/categories/components/CategoryUpdatePage/CategoryUpdatePage.tsx b/src/categories/components/CategoryUpdatePage/CategoryUpdatePage.tsx index 05e02cdbb..0e33eef2c 100644 --- a/src/categories/components/CategoryUpdatePage/CategoryUpdatePage.tsx +++ b/src/categories/components/CategoryUpdatePage/CategoryUpdatePage.tsx @@ -22,7 +22,7 @@ import { CategoryDetails_category_products_edges_node } from "../../types/CategoryDetails"; import CategoryBackground from "../CategoryBackground"; -import CategoryProductsCard from "../CategoryProductsCard"; +import CategoryProducts from "../CategoryProducts"; export interface FormData { backgroundImageAlt: string; @@ -195,7 +195,7 @@ export const CategoryUpdatePage: React.StatelessComponent< /> )} {currentTab === CategoryPageTab.products && ( - category.name)} products={products} disabled={disabled} diff --git a/src/components/AppLayout/AppLayout.tsx b/src/components/AppLayout/AppLayout.tsx index 01651ebc4..683ade739 100644 --- a/src/components/AppLayout/AppLayout.tsx +++ b/src/components/AppLayout/AppLayout.tsx @@ -204,6 +204,7 @@ const styles = (theme: Theme) => menuSmall: { background: theme.palette.background.paper, height: "100vh", + overflow: "hidden", padding: 25 }, popover: { diff --git a/src/components/ColumnPicker/ColumnPicker.tsx b/src/components/ColumnPicker/ColumnPicker.tsx index a0d72cb09..cf5cfb217 100644 --- a/src/components/ColumnPicker/ColumnPicker.tsx +++ b/src/components/ColumnPicker/ColumnPicker.tsx @@ -6,14 +6,23 @@ import { fade } from "@material-ui/core/styles/colorManipulator"; import makeStyles from "@material-ui/styles/makeStyles"; import React from "react"; +import useStateFromProps from "@saleor/hooks/useStateFromProps"; +import { toggle } from "@saleor/utils/lists"; import ColumnPickerButton from "./ColumnPickerButton"; import ColumnPickerContent, { ColumnPickerContentProps } from "./ColumnPickerContent"; -export interface ColumnPickerProps extends ColumnPickerContentProps { +export interface ColumnPickerProps + extends Omit< + ColumnPickerContentProps, + "selectedColumns" | "onCancel" | "onColumnToggle" | "onReset" | "onSave" + > { className?: string; - initial?: boolean; + defaultColumns: string[]; + initialColumns: string[]; + initialOpen?: boolean; + onSave: (columns: string[]) => void; } const useStyles = makeStyles( @@ -33,29 +42,39 @@ const ColumnPicker: React.FC = props => { const { className, columns, - initial = false, - selectedColumns, - onCancel, - onColumnToggle, - onReset, + defaultColumns, + hasMore, + initialColumns, + initialOpen = false, + loading, + total, + onFetchMore, onSave } = props; const classes = useStyles(props); const anchor = React.useRef(); const [isExpanded, setExpansionState] = React.useState(false); + const [selectedColumns, setSelectedColumns] = useStateFromProps( + initialColumns + ); React.useEffect(() => { - setTimeout(() => setExpansionState(initial), 100); + setTimeout(() => setExpansionState(initialOpen), 100); }, []); const handleCancel = React.useCallback(() => { setExpansionState(false); - onCancel(); - }, []); + setSelectedColumns(columns.map(column => column.value)); + }, [columns]); + + const handleColumnToggle = (column: string) => + setSelectedColumns(toggle(column, selectedColumns, (a, b) => a === b)); + + const handleReset = () => setSelectedColumns(defaultColumns); const handleSave = () => { setExpansionState(false); - onSave(); + onSave(selectedColumns); }; return ( @@ -86,10 +105,14 @@ const ColumnPicker: React.FC = props => { > diff --git a/src/components/ColumnPicker/ColumnPickerContent.tsx b/src/components/ColumnPicker/ColumnPickerContent.tsx index 52473159d..340221ea3 100644 --- a/src/components/ColumnPicker/ColumnPickerContent.tsx +++ b/src/components/ColumnPicker/ColumnPickerContent.tsx @@ -1,15 +1,18 @@ import Button from "@material-ui/core/Button"; import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; +import CircularProgress from "@material-ui/core/CircularProgress"; import { Theme } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; import makeStyles from "@material-ui/styles/makeStyles"; import classNames from "classnames"; import React from "react"; +import InfiniteScroll from "react-infinite-scroller"; import { FormattedMessage } from "react-intl"; import useElementScroll from "@saleor/hooks/useElementScroll"; import { buttonMessages } from "@saleor/intl"; +import { FetchMoreProps } from "@saleor/types"; import { isSelected } from "@saleor/utils/lists"; import ControlledCheckbox from "../ControlledCheckbox"; import Hr from "../Hr"; @@ -18,9 +21,10 @@ export interface ColumnPickerChoice { label: string; value: string; } -export interface ColumnPickerContentProps { +export interface ColumnPickerContentProps extends Partial { columns: ColumnPickerChoice[]; selectedColumns: string[]; + total?: number; onCancel: () => void; onColumnToggle: (column: string) => void; onReset: () => void; @@ -50,15 +54,29 @@ const useStyles = makeStyles((theme: Theme) => ({ }, dropShadow: { boxShadow: `0px -5px 10px 0px ${theme.overrides.MuiCard.root.borderColor}` + }, + loadMoreLoaderContainer: { + alignItems: "center", + display: "flex", + gridColumnEnd: "span 3", + height: theme.spacing.unit * 3, + justifyContent: "center" + }, + root: { + boxShadow: "0px 4px 4px rgba(0, 0, 0, 0.25)" } })); const ColumnPickerContent: React.FC = props => { const { columns, + hasMore, + loading, selectedColumns, + total, onCancel, onColumnToggle, + onFetchMore, onReset, onSave } = props; @@ -72,7 +90,7 @@ const ColumnPickerContent: React.FC = props => { : false; return ( - + = props => { description="pick columns to display" values={{ numberOfSelected: selectedColumns.length, - numberOfTotal: columns.length + numberOfTotal: total || columns.length }} />
- -
- {columns.map(column => ( - a === b + {hasMore && onFetchMore ? ( + + +
+ {columns.map(column => ( + a === b + )} + name={column.value} + label={column.label} + onChange={() => onColumnToggle(column.value)} + /> + ))} + {loading && ( +
+ +
)} - name={column.value} - label={column.label} - onChange={() => onColumnToggle(column.value)} - /> - ))} -
-
+
+
+ + ) : ( + +
+ {columns.map(column => ( + a === b + )} + name={column.value} + label={column.label} + onChange={() => onColumnToggle(column.value)} + /> + ))} +
+
+ )}
width: 200 } }, + colAttribute: { + width: 150 + }, colFill: { padding: 0, width: "100%" }, - colName: {}, + colName: { + "&$colNameFixed": { + width: 250 + } + }, + colNameFixed: {}, colNameHeader: { marginLeft: AVATAR_MARGIN }, @@ -77,7 +91,8 @@ interface ProductListProps extends ListProps, ListActions, WithStyles { - products: CategoryDetails_category_products_edges_node[]; + gridAttributes: AvailableInGridAttributes_grid_edges_node[]; + products: ProductList_products_edges_node[]; } export const ProductList = withStyles(styles, { name: "ProductList" })( @@ -86,6 +101,7 @@ export const ProductList = withStyles(styles, { name: "ProductList" })( settings, disabled, isChecked, + gridAttributes, pageInfo, products, selected, @@ -98,23 +114,35 @@ export const ProductList = withStyles(styles, { name: "ProductList" })( onRowClick }: ProductListProps) => { const intl = useIntl(); - const displayColumn = React.useCallback( - (column: ProductListColumns) => - isSelected(column, settings.columns, (a, b) => a === b), - [settings.columns] + + const DisplayColumn: React.FC<{ column: ProductListColumns }> = props => ( + + ); + + const gridAttributesFromSettings = settings.columns.filter( + isAttributeColumnValue ); const numberOfColumns = 2 + settings.columns.length; return (
- - - {displayColumn("productType") && } - {displayColumn("isPublished") && ( - - )} - {displayColumn("price") && } + + + + + + + + + + {gridAttributesFromSettings.map(gridAttribute => ( + + ))} + + + + - + 4 + })} + > - {displayColumn("productType") && ( + - )} - {displayColumn("isPublished") && ( + + - )} - {displayColumn("price") && ( + + {gridAttributesFromSettings.map(gridAttributeFromSettings => ( + + {maybe( + () => + gridAttributes.find( + gridAttribute => + getAttributeIdFromColumnValue( + gridAttributeFromSettings + ) === gridAttribute.id + ).name, + + )} + + ))} + - )} + @@ -194,9 +243,9 @@ export const ProductList = withStyles(styles, { name: "ProductList" })( className={classes.colName} thumbnail={maybe(() => product.thumbnail.url)} > - {product ? product.name : } + {maybe(() => product.name, )} - {displayColumn("productType") && ( + {product && product.productType ? ( product.productType.name @@ -204,8 +253,8 @@ export const ProductList = withStyles(styles, { name: "ProductList" })( )} - )} - {displayColumn("isPublished") && ( + + {product && maybe(() => product.isAvailable !== undefined) ? ( @@ -227,8 +276,28 @@ export const ProductList = withStyles(styles, { name: "ProductList" })( )} - )} - {displayColumn("price") && ( + + {gridAttributesFromSettings.map(gridAttribute => ( + + {maybe(() => { + const attribute = product.attributes.find( + attribute => + attribute.attribute.id === + getAttributeIdFromColumnValue(gridAttribute) + ); + if (attribute) { + return attribute.values + .map(value => value.name) + .join(", "); + } + return "-"; + }, )} + + ))} + {maybe(() => product.basePrice) && maybe(() => product.basePrice.amount) !== undefined && @@ -239,7 +308,7 @@ export const ProductList = withStyles(styles, { name: "ProductList" })( )} - )} + ); }, diff --git a/src/components/TableCellAvatar/TableCellAvatar.tsx b/src/components/TableCellAvatar/TableCellAvatar.tsx index 19ce47f88..893ebcc8a 100644 --- a/src/components/TableCellAvatar/TableCellAvatar.tsx +++ b/src/components/TableCellAvatar/TableCellAvatar.tsx @@ -26,7 +26,8 @@ const styles = (theme: Theme) => }, children: { alignSelf: "center", - marginLeft: theme.spacing.unit * 2 + marginLeft: theme.spacing.unit * 2, + width: "100%" }, content: { alignItems: "center", @@ -69,7 +70,7 @@ const TableCellAvatar = withStyles(styles, { name: "TableCellAvatar" })( src={thumbnail} /> )} - {children} +
{children}
) diff --git a/src/components/TableHead/TableHead.tsx b/src/components/TableHead/TableHead.tsx index 0ee28e0ec..bc36b2fa5 100644 --- a/src/components/TableHead/TableHead.tsx +++ b/src/components/TableHead/TableHead.tsx @@ -101,26 +101,25 @@ const TableHead = withStyles(styles, { })} /> )} - {(items === undefined || items.length > 0) && - (selected && ( - 0) && ( + + selected && selected > 0 })} - > - selected && selected > 0 - })} - checked={selected === 0 ? false : true} - disabled={disabled} - onChange={() => toggleAll(items, selected)} - /> - - ))} + checked={selected === 0 ? false : true} + disabled={disabled} + onChange={() => toggleAll(items, selected)} + /> + + )} {selected ? ( <> undefined, onFetchMore: () => undefined }; diff --git a/src/icons/Plugins.tsx b/src/icons/Plugins.tsx index 4f67c5b5a..c5b518de5 100644 --- a/src/icons/Plugins.tsx +++ b/src/icons/Plugins.tsx @@ -5,8 +5,8 @@ export const Plugins = createSvgIcon( <> diff --git a/src/orders/components/OrderProductAddDialog/OrderProductAddDialog.tsx b/src/orders/components/OrderProductAddDialog/OrderProductAddDialog.tsx index 479e2a567..c68b19c2b 100644 --- a/src/orders/components/OrderProductAddDialog/OrderProductAddDialog.tsx +++ b/src/orders/components/OrderProductAddDialog/OrderProductAddDialog.tsx @@ -81,6 +81,7 @@ interface OrderProductAddDialogProps extends FetchMoreProps { open: boolean; products: SearchOrderVariant_products_edges_node[]; onClose: () => void; + onFetch: (query: string) => void; onSubmit: (data: SearchOrderVariant_products_edges_node_variants[]) => void; } diff --git a/src/plugins/views/PluginsDetails.tsx b/src/plugins/views/PluginsDetails.tsx index 4a72e57fd..69e136351 100644 --- a/src/plugins/views/PluginsDetails.tsx +++ b/src/plugins/views/PluginsDetails.tsx @@ -2,7 +2,7 @@ import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import React from "react"; -import { FormattedMessage, useIntl } from "react-intl"; +import { useIntl } from "react-intl"; import { getMutationState, maybe } from "../../misc"; import PluginsDetailsPage from "../components/PluginsDetailsPage"; diff --git a/src/productTypes/components/AssignAttributeDialog/AssignAttributeDialog.tsx b/src/productTypes/components/AssignAttributeDialog/AssignAttributeDialog.tsx index a8ba7c5d2..9c0b05463 100644 --- a/src/productTypes/components/AssignAttributeDialog/AssignAttributeDialog.tsx +++ b/src/productTypes/components/AssignAttributeDialog/AssignAttributeDialog.tsx @@ -54,6 +54,7 @@ export interface AssignAttributeDialogProps extends FetchMoreProps { attributes: SearchAttributes_productType_availableAttributes_edges_node[]; selected: string[]; onClose: () => void; + onFetch: (query: string) => void; onOpen: () => void; onSubmit: () => void; onToggle: (id: string) => void; diff --git a/src/products/components/ProductList/ProductList.tsx b/src/products/components/ProductList/ProductList.tsx new file mode 100644 index 000000000..88508d18a --- /dev/null +++ b/src/products/components/ProductList/ProductList.tsx @@ -0,0 +1,332 @@ +import { + createStyles, + Theme, + withStyles, + WithStyles +} from "@material-ui/core/styles"; +import Table from "@material-ui/core/Table"; +import TableBody from "@material-ui/core/TableBody"; +import TableCell from "@material-ui/core/TableCell"; +import TableFooter from "@material-ui/core/TableFooter"; +import TableRow from "@material-ui/core/TableRow"; +import classNames from "classnames"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +import Checkbox from "@saleor/components/Checkbox"; +import Money from "@saleor/components/Money"; +import Skeleton from "@saleor/components/Skeleton"; +import StatusLabel from "@saleor/components/StatusLabel"; +import TableCellAvatar, { + AVATAR_MARGIN +} from "@saleor/components/TableCellAvatar"; +import TableHead from "@saleor/components/TableHead"; +import TablePagination from "@saleor/components/TablePagination"; +import { ProductListColumns } from "@saleor/config"; +import { maybe, renderCollection } from "@saleor/misc"; +import { + getAttributeIdFromColumnValue, + isAttributeColumnValue +} from "@saleor/products/components/ProductListPage/utils"; +import { AvailableInGridAttributes_grid_edges_node } from "@saleor/products/types/AvailableInGridAttributes"; +import { ProductList_products_edges_node } from "@saleor/products/types/ProductList"; +import { ListActions, ListProps } from "@saleor/types"; +import TDisplayColumn from "@saleor/utils/columns/DisplayColumn"; + +const styles = (theme: Theme) => + createStyles({ + [theme.breakpoints.up("lg")]: { + colName: { + width: "auto" + }, + colPrice: { + width: 200 + }, + colPublished: { + width: 200 + }, + colType: { + width: 200 + } + }, + colAttribute: { + width: 150 + }, + colFill: { + padding: 0, + width: "100%" + }, + colName: { + "&$colNameFixed": { + width: 250 + } + }, + colNameFixed: {}, + colNameHeader: { + marginLeft: AVATAR_MARGIN + }, + colPrice: { + textAlign: "right" + }, + colPublished: {}, + colType: {}, + link: { + cursor: "pointer" + }, + table: { + tableLayout: "fixed" + }, + tableContainer: { + overflowX: "scroll" + }, + textLeft: { + textAlign: "left" + }, + textRight: { + textAlign: "right" + } + }); + +interface ProductListProps + extends ListProps, + ListActions, + WithStyles { + gridAttributes: AvailableInGridAttributes_grid_edges_node[]; + products: ProductList_products_edges_node[]; +} + +export const ProductList = withStyles(styles, { name: "ProductList" })( + ({ + classes, + settings, + disabled, + isChecked, + gridAttributes, + pageInfo, + products, + selected, + toggle, + toggleAll, + toolbar, + onNextPage, + onPreviousPage, + onUpdateListSettings, + onRowClick + }: ProductListProps) => { + const intl = useIntl(); + + const DisplayColumn: React.FC<{ column: ProductListColumns }> = props => ( + + ); + const gridAttributesFromSettings = settings.columns.filter( + isAttributeColumnValue + ); + const numberOfColumns = 2 + settings.columns.length; + + return ( +
+
+ + + + + + + + + + {gridAttributesFromSettings.map(gridAttribute => ( + + ))} + + + + + + 4 + })} + > + + + + + + + + + + + + + + + {gridAttributesFromSettings.map(gridAttributeFromSettings => ( + + {maybe( + () => + gridAttributes.find( + gridAttribute => + getAttributeIdFromColumnValue( + gridAttributeFromSettings + ) === gridAttribute.id + ).name, + + )} + + ))} + + + + + + + + + + + + + {renderCollection( + products, + product => { + const isSelected = product ? isChecked(product.id) : false; + + return ( + + + toggle(product.id)} + /> + + product.thumbnail.url)} + > + {maybe(() => product.name, )} + + + + {product && product.productType ? ( + product.productType.name + ) : ( + + )} + + + + + {product && + maybe(() => product.isAvailable !== undefined) ? ( + + ) : ( + + )} + + + {gridAttributesFromSettings.map(gridAttribute => ( + + {maybe(() => { + const attribute = product.attributes.find( + attribute => + attribute.attribute.id === + getAttributeIdFromColumnValue(gridAttribute) + ); + if (attribute) { + return attribute.values + .map(value => value.name) + .join(", "); + } + return "-"; + }, )} + + ))} + + + {maybe(() => product.basePrice) && + maybe(() => product.basePrice.amount) !== undefined && + maybe(() => product.basePrice.currency) !== + undefined ? ( + + ) : ( + + )} + + + + ); + }, + () => ( + + + + + + ) + )} + +
+
+ ); + } +); +ProductList.displayName = "ProductList"; +export default ProductList; diff --git a/src/products/components/ProductList/index.ts b/src/products/components/ProductList/index.ts new file mode 100644 index 000000000..17fd4319e --- /dev/null +++ b/src/products/components/ProductList/index.ts @@ -0,0 +1 @@ +export { default } from "./ProductList"; diff --git a/src/products/components/ProductListPage/ProductListPage.tsx b/src/products/components/ProductListPage/ProductListPage.tsx index 6096fb686..ec18734b2 100644 --- a/src/products/components/ProductListPage/ProductListPage.tsx +++ b/src/products/components/ProductListPage/ProductListPage.tsx @@ -6,27 +6,38 @@ import makeStyles from "@material-ui/styles/makeStyles"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; -import { CategoryDetails_category_products_edges_node } from "@saleor/categories/types/CategoryDetails"; import ColumnPicker, { ColumnPickerChoice } from "@saleor/components/ColumnPicker"; import Container from "@saleor/components/Container"; import PageHeader from "@saleor/components/PageHeader"; -import ProductList from "@saleor/components/ProductList"; import { ProductListColumns } from "@saleor/config"; -import useStateFromProps from "@saleor/hooks/useStateFromProps"; import { sectionNames } from "@saleor/intl"; -import { FilterPageProps, ListActions, PageListProps } from "@saleor/types"; -import { toggle } from "@saleor/utils/lists"; +import { + AvailableInGridAttributes_availableInGrid_edges_node, + AvailableInGridAttributes_grid_edges_node +} from "@saleor/products/types/AvailableInGridAttributes"; +import { ProductList_products_edges_node } from "@saleor/products/types/ProductList"; +import { + FetchMoreProps, + FilterPageProps, + ListActions, + PageListProps +} from "@saleor/types"; import { ProductListUrlFilters } from "../../urls"; +import ProductList from "../ProductList"; import ProductListFilter from "../ProductListFilter"; export interface ProductListPageProps extends PageListProps, ListActions, - FilterPageProps { + FilterPageProps, + FetchMoreProps { + availableInGridAttributes: AvailableInGridAttributes_availableInGrid_edges_node[]; currencySymbol: string; - products: CategoryDetails_category_products_edges_node[]; + gridAttributes: AvailableInGridAttributes_grid_edges_node[]; + totalGridAttributes: number; + products: ProductList_products_edges_node[]; } const useStyles = makeStyles((theme: Theme) => ({ @@ -42,10 +53,16 @@ export const ProductListPage: React.FC = props => { defaultSettings, filtersList, filterTabs, + gridAttributes, + availableInGridAttributes, + hasMore, initialSearch, + loading, settings, + totalGridAttributes, onAdd, onAll, + onFetchMore, onSearchChange, onFilterAdd, onFilterSave, @@ -56,23 +73,9 @@ export const ProductListPage: React.FC = props => { } = props; const intl = useIntl(); const classes = useStyles(props); - const [selectedColumns, setSelectedColumns] = useStateFromProps( - settings.columns - ); - const handleCancel = React.useCallback( - () => setSelectedColumns(settings.columns), - [settings.columns] - ); - - const handleColumnToggle = (column: ProductListColumns) => - setSelectedColumns(prevSelectedColumns => - toggle(column, prevSelectedColumns, (a, b) => a === b) - ); - - const handleReset = () => setSelectedColumns(defaultSettings.columns); - - const handleSave = () => onUpdateListSettings("columns", selectedColumns); + const handleSave = (columns: ProductListColumns[]) => + onUpdateListSettings("columns", columns); const columns: ColumnPickerChoice[] = [ { @@ -95,7 +98,11 @@ export const ProductListPage: React.FC = props => { description: "product type" }), value: "productType" as ProductListColumns - } + }, + ...availableInGridAttributes.map(attribute => ({ + label: attribute.name, + value: `attribute:${attribute.id}` + })) ]; return ( @@ -104,10 +111,16 @@ export const ProductListPage: React.FC = props => { - - openModal("delete", listElements)} - > - - - - } - isChecked={isSelected} - selected={listElements.length} - toggle={toggle} - toggleAll={toggleAll} - onSearchChange={query => changeFilterField({ query })} - onFilterAdd={filter => - changeFilterField(createFilter(filter)) - } - onFilterSave={() => openModal("save-search")} - onFilterDelete={() => openModal("delete-search")} - onTabChange={handleTabChange} - initialSearch={params.query || ""} - filterTabs={getFilterTabs()} - /> - - productBulkDelete({ - variables: { ids: params.ids } - }) - } - title={intl.formatMessage({ - defaultMessage: "Delete Products", - description: "dialog header" - })} - variant="delete" - > - - params.ids.length), - displayQuantity: ( - - {maybe(() => params.ids.length)} - - ) - }} - /> - - - - productBulkPublish({ - variables: { - ids: params.ids, - isPublished: true + return ( + <> + + attributes.data.availableInGrid.edges.map( + edge => edge.node + ), + [] + )} + currencySymbol={currencySymbol} + currentTab={currentTab} + defaultSettings={ + defaultListSettings[ListViews.PRODUCT_LIST] } - }) - } - title={intl.formatMessage({ - defaultMessage: "Publish Products", - description: "dialog header" - })} - > - - params.ids.length), - displayQuantity: ( - - {maybe(() => params.ids.length)} - + gridAttributes={maybe( + () => + attributes.data.grid.edges.map( + edge => edge.node + ), + [] + )} + totalGridAttributes={maybe( + () => attributes.data.availableInGrid.totalCount, + 0 + )} + settings={settings} + loading={attributes.loading} + hasMore={maybe( + () => + attributes.data.availableInGrid.pageInfo + .hasNextPage, + false + )} + filtersList={createFilterChips( + params, + { + currencySymbol, + locale + }, + changeFilterField, + intl + )} + onAdd={() => navigate(productAddUrl)} + disabled={loading} + products={maybe(() => + data.products.edges.map(edge => edge.node) + )} + onFetchMore={() => + attributes.loadMore( + (prev, next) => { + if ( + prev.availableInGrid.pageInfo.endCursor === + next.availableInGrid.pageInfo.endCursor + ) { + return prev; + } + return { + ...prev, + availableInGrid: { + ...prev.availableInGrid, + edges: [ + ...prev.availableInGrid.edges, + ...next.availableInGrid.edges + ], + pageInfo: next.availableInGrid.pageInfo + } + }; + }, + { + after: + attributes.data.availableInGrid.pageInfo + .endCursor + } ) - }} - /> - - - - productBulkPublish({ - variables: { - ids: params.ids, - isPublished: false } - }) - } - title={intl.formatMessage({ - defaultMessage: "Unpublish Products", - description: "dialog header" - })} - > - - + openModal("unpublish", listElements) + } + > + + + + + openModal("delete", listElements) + } + > + + + + } + isChecked={isSelected} + selected={listElements.length} + toggle={toggle} + toggleAll={toggleAll} + onSearchChange={query => + changeFilterField({ query }) + } + onFilterAdd={filter => + changeFilterField(createFilter(filter)) + } + onFilterSave={() => openModal("save-search")} + onFilterDelete={() => openModal("delete-search")} + onTabChange={handleTabChange} + initialSearch={params.query || ""} + filterTabs={getFilterTabs()} + /> + + productBulkDelete({ + variables: { ids: params.ids } + }) + } + title={intl.formatMessage({ + defaultMessage: "Delete Products", + description: "dialog header" + })} + variant="delete" + > + + params.ids.length), - displayQuantity: ( - - {maybe(() => params.ids.length)} - - ) - }} + description="dialog content" + values={{ + counter: maybe(() => params.ids.length), + displayQuantity: ( + + {maybe(() => params.ids.length)} + + ) + }} + /> + + + + productBulkPublish({ + variables: { + ids: params.ids, + isPublished: true + } + }) + } + title={intl.formatMessage({ + defaultMessage: "Publish Products", + description: "dialog header" + })} + > + + params.ids.length), + displayQuantity: ( + + {maybe(() => params.ids.length)} + + ) + }} + /> + + + + productBulkPublish({ + variables: { + ids: params.ids, + isPublished: false + } + }) + } + title={intl.formatMessage({ + defaultMessage: "Unpublish Products", + description: "dialog header" + })} + > + + params.ids.length), + displayQuantity: ( + + {maybe(() => params.ids.length)} + + ) + }} + /> + + + - - - - tabs[currentTab - 1].name, "...")} - /> - - ); - }} - - )} - - ); - }} - + tabs[currentTab - 1].name, + "..." + )} + /> + + ); + }} + + )} + + ); + }} + + )} + ); }; export default ProductList; diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index e4807b17f..484755290 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -1116,6 +1116,54 @@ exports[`Storyshots Generics / Column picker default 1`] = ` `; +exports[`Storyshots Generics / Column picker loading 1`] = ` +
+
+
+
+
+ +
+
+
+
+
+`; + exports[`Storyshots Generics / Date default 1`] = `
- 0 @@ -10739,6 +10786,22 @@ exports[`Storyshots Views / Attributes / Attribute list default 1`] = ` +
+ + - 0 @@ -11449,6 +11511,24 @@ exports[`Storyshots Views / Attributes / Attribute list loading 1`] = ` +
+ + - 0 @@ -12387,6 +12466,22 @@ exports[`Storyshots Views / Categories / Category list default 1`] = ` +
+ + - 0 @@ -13020,6 +13114,22 @@ exports[`Storyshots Views / Categories / Category list loading 1`] = ` +
+ + - 0 @@ -15687,6 +15796,24 @@ Ctrl + K" +
+ +
Name Type Published Price @@ -19704,25 +19831,24 @@ Ctrl + K" class="CardTitle-hr-id" />
- 0 + @@ -21251,7 +21383,6 @@ Ctrl + K"
- 0
+ + Name Type Published Price @@ -19841,8 +19983,7 @@ Ctrl + K" class="MuiTableBody-root-id" >
@@ -21261,6 +21392,22 @@ Ctrl + K" +
+ + - Murray Inc - + - Williams-Taylor - + - Hebert-Sherman - + - Estes, Johnson and Graham - + - 0 @@ -22394,6 +22540,24 @@ Ctrl + K" +
+ + - - + - 0 @@ -23995,6 +24158,22 @@ exports[`Storyshots Views / Collections / Collection list default 1`] = ` +
+ + - 0 @@ -24434,6 +24612,24 @@ exports[`Storyshots Views / Collections / Collection list loading 1`] = ` +
+ + - 0 @@ -34233,6 +34428,22 @@ exports[`Storyshots Views / Customers / Customer list default 1`] = ` +
+ + - 0 @@ -35260,6 +35470,24 @@ exports[`Storyshots Views / Customers / Customer list loading 1`] = ` +
+ + - 0 @@ -36822,6 +37049,22 @@ exports[`Storyshots Views / Discounts / Sale details collections 1`] = ` +
+ + - 0 @@ -37399,6 +37641,22 @@ exports[`Storyshots Views / Discounts / Sale details default 1`] = ` +
+ + - 0 @@ -37996,6 +38253,22 @@ exports[`Storyshots Views / Discounts / Sale details form errors 1`] = ` +
+ + - 0 @@ -38582,6 +38854,24 @@ exports[`Storyshots Views / Discounts / Sale details loading 1`] = ` +
+ + - 0 @@ -39187,6 +39476,22 @@ exports[`Storyshots Views / Discounts / Sale details products 1`] = ` +
+ + - Orange Juice - + - Carrot Juice - + - Bean Juice - + - Black Hoodie - + - 0 @@ -39783,6 +40087,22 @@ exports[`Storyshots Views / Discounts / Sale list default 1`] = ` +
+ + - 0 @@ -40224,6 +40543,22 @@ exports[`Storyshots Views / Discounts / Sale list loading 1`] = ` +
+ + - 0 @@ -45118,6 +45452,22 @@ exports[`Storyshots Views / Discounts / Voucher list default 1`] = ` +
+ + - 0 @@ -45456,6 +45805,22 @@ exports[`Storyshots Views / Discounts / Voucher list loading 1`] = ` +
+ + - @@ -47030,7 +47395,7 @@ exports[`Storyshots Views / HomePage loading 1`] = ` /> - @@ -48648,7 +49013,6 @@ exports[`Storyshots Views / Navigation / Menu list default 1`] = `
- 0 @@ -48658,6 +49022,22 @@ exports[`Storyshots Views / Navigation / Menu list default 1`] = ` +
+ + - 0 @@ -48979,6 +49358,24 @@ exports[`Storyshots Views / Navigation / Menu list loading 1`] = ` +
+ + - 0 @@ -49520,6 +49916,22 @@ exports[`Storyshots Views / Orders / Draft order list default 1`] = ` +
+ + - 0 @@ -50637,6 +51048,24 @@ exports[`Storyshots Views / Orders / Draft order list loading 1`] = ` +
+ + - Watkins-Gonzalez (Soft) - + - Williams, Garcia and Walker (XS) - + - Williams, Garcia and Walker (XS) - + - Watkins-Gonzalez (Soft) - + - Williams, Garcia and Walker (XS) - + - Williams, Garcia and Walker (XS) - + - Watkins-Gonzalez (Soft) - + - Williams, Garcia and Walker (XS) - + - Williams, Garcia and Walker (XS) - + - - + - Watkins-Gonzalez (Soft) - + - Williams, Garcia and Walker (XS) - + - Williams, Garcia and Walker (XS) - + - Watkins-Gonzalez (Soft) - + - Williams, Garcia and Walker (XS) - + - Williams, Garcia and Walker (XS) - + - Watkins-Gonzalez (Soft) - + - Williams, Garcia and Walker (XS) - + - Williams, Garcia and Walker (XS) - + - Watkins-Gonzalez (Soft) - + - Williams, Garcia and Walker (XS) - + - Williams, Garcia and Walker (XS) - + - Watkins-Gonzalez (Soft) - + - Williams, Garcia and Walker (XS) - + - Williams, Garcia and Walker (XS) - + - Watkins-Gonzalez (Soft) - + - Williams, Garcia and Walker (XS) - + - Williams, Garcia and Walker (XS) - + - Watkins-Gonzalez (Soft) - + - Williams, Garcia and Walker (XS) - + - Williams, Garcia and Walker (XS) - + - Watkins-Gonzalez (Soft) - + - Williams, Garcia and Walker (XS) - + - Williams, Garcia and Walker (XS) - + - Watkins-Gonzalez (Soft) - + - Williams, Garcia and Walker (XS) - + - Williams, Garcia and Walker (XS) - + - Watkins-Gonzalez (Soft) - + - Williams, Garcia and Walker (XS) - + - Williams, Garcia and Walker (XS) - + -

58-1338 - +

-

15-1337 - +

- - + - 0 @@ -66285,6 +66713,22 @@ exports[`Storyshots Views / Orders / Order list default 1`] = ` +
+ + - 0 @@ -67895,6 +68338,24 @@ exports[`Storyshots Views / Orders / Order list loading 1`] = ` +
+ + - 0 @@ -69140,6 +69600,22 @@ exports[`Storyshots Views / Orders / Order list with custom filters 1`] = ` +
+ + - 0 @@ -72958,6 +73433,22 @@ exports[`Storyshots Views / Pages / Page list default 1`] = ` +
+ + - 0 @@ -73321,6 +73811,24 @@ exports[`Storyshots Views / Pages / Page list loading 1`] = ` +
+ + + + + + + + - 0 @@ -76154,6 +76695,22 @@ exports[`Storyshots Views / Product types / Product type details default 1`] = ` class="MuiTableCell-root-id MuiTableCell-head-id" scope="col" /> +
+ + - 0 @@ -76775,6 +77331,22 @@ exports[`Storyshots Views / Product types / Product type details form errors 1`] class="MuiTableCell-root-id MuiTableCell-head-id" scope="col" /> +
+ + - 0 @@ -77397,6 +77968,24 @@ exports[`Storyshots Views / Product types / Product type details loading 1`] = ` class="MuiTableCell-root-id MuiTableCell-head-id" scope="col" /> +
+ + - 0 @@ -78009,6 +78597,22 @@ exports[`Storyshots Views / Product types / Product types list default 1`] = ` +
+ + - 0 @@ -78375,6 +78978,24 @@ exports[`Storyshots Views / Product types / Product types list loading 1`] = ` +
+ + - @@ -82315,7 +82936,7 @@ exports[`Storyshots Views / Products / Create product variant default 1`] = ` src="placeholder255x255.png" /> - @@ -82343,7 +82964,7 @@ exports[`Storyshots Views / Products / Create product variant default 1`] = ` src="placeholder255x255.png" /> - @@ -82397,7 +83018,7 @@ exports[`Storyshots Views / Products / Create product variant default 1`] = ` - @@ -82835,7 +83456,7 @@ exports[`Storyshots Views / Products / Create product variant when loading data /> - @@ -82893,7 +83514,7 @@ exports[`Storyshots Views / Products / Create product variant when loading data - @@ -83263,7 +83884,7 @@ exports[`Storyshots Views / Products / Create product variant with errors 1`] = src="placeholder255x255.png" /> - @@ -83291,7 +83912,7 @@ exports[`Storyshots Views / Products / Create product variant with errors 1`] = src="placeholder255x255.png" /> - @@ -83345,7 +83966,7 @@ exports[`Storyshots Views / Products / Create product variant with errors 1`] = - @@ -84937,7 +85558,6 @@ Ctrl + K" Use variants for products that come in a variety of versions for example different sizes or colors

- 0 @@ -84947,6 +85567,22 @@ Ctrl + K" +
+ + - 0 @@ -87668,6 +88303,22 @@ Ctrl + K" +
+ + - 0 @@ -90453,6 +91103,22 @@ exports[`Storyshots Views / Products / Product list default 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -91420,7 +92666,6 @@ exports[`Storyshots Views / Products / Product list loading 1`] = `
- 0
+ +
@@ -91445,6 +92690,24 @@ exports[`Storyshots Views / Products / Product list loading 1`] = ` +
+ + - - + - 0 @@ -92783,6 +94045,22 @@ exports[`Storyshots Views / Products / Product list with custom filters 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -93613,7 +95471,7 @@ exports[`Storyshots Views / Products / Product variant details attribute errors src="placeholder60x60.png" /> - @@ -93641,7 +95499,7 @@ exports[`Storyshots Views / Products / Product variant details attribute errors src="placeholder60x60.png" /> - @@ -93669,7 +95527,7 @@ exports[`Storyshots Views / Products / Product variant details attribute errors src="placeholder60x60.png" /> - @@ -93697,7 +95555,7 @@ exports[`Storyshots Views / Products / Product variant details attribute errors src="placeholder60x60.png" /> - @@ -94296,7 +96154,7 @@ exports[`Storyshots Views / Products / Product variant details when loaded data src="placeholder60x60.png" /> - @@ -94324,7 +96182,7 @@ exports[`Storyshots Views / Products / Product variant details when loaded data src="placeholder60x60.png" /> - @@ -94352,7 +96210,7 @@ exports[`Storyshots Views / Products / Product variant details when loaded data src="placeholder60x60.png" /> - @@ -94380,7 +96238,7 @@ exports[`Storyshots Views / Products / Product variant details when loaded data src="placeholder60x60.png" /> - @@ -94985,7 +96843,7 @@ exports[`Storyshots Views / Products / Product variant details when loading data /> - @@ -97604,7 +99462,6 @@ exports[`Storyshots Views / Shipping / Shipping zones list default 1`] = `
- 0
+ +
@@ -97614,6 +99471,22 @@ exports[`Storyshots Views / Shipping / Shipping zones list default 1`] = ` +
+ + - 0 @@ -98218,6 +100090,24 @@ exports[`Storyshots Views / Shipping / Shipping zones list loading 1`] = ` +
+ + ( - - )) - .add("with initial data", () => ( - - )) - .add("with clickable rows", () => ( - undefined} - /> - )) - .add("when loading data", () => ( - - )); diff --git a/src/storybook/stories/components/AssignAttributeDialog.tsx b/src/storybook/stories/components/AssignAttributeDialog.tsx index d438dcc9a..9cb0462e5 100644 --- a/src/storybook/stories/components/AssignAttributeDialog.tsx +++ b/src/storybook/stories/components/AssignAttributeDialog.tsx @@ -15,6 +15,7 @@ const props: AssignAttributeDialogProps = { confirmButtonState: "default", errors: [], onClose: () => undefined, + onFetch: () => undefined, onOpen: () => undefined, onSubmit: () => undefined, onToggle: () => undefined, diff --git a/src/storybook/stories/components/ColumnPicker.tsx b/src/storybook/stories/components/ColumnPicker.tsx index 9da2bc9cc..18eadb037 100644 --- a/src/storybook/stories/components/ColumnPicker.tsx +++ b/src/storybook/stories/components/ColumnPicker.tsx @@ -26,12 +26,10 @@ const columns: ColumnPickerChoice[] = [ const props: ColumnPickerProps = { columns, - initial: true, - onCancel: () => undefined, - onColumnToggle: () => undefined, - onReset: () => undefined, - onSave: () => undefined, - selectedColumns: [1, 3, 4, 6].map(index => columns[index].value) + defaultColumns: [1, 3].map(index => columns[index].value), + initialColumns: [1, 3, 4, 6].map(index => columns[index].value), + initialOpen: true, + onSave: () => undefined }; storiesOf("Generics / Column picker", module) @@ -42,4 +40,12 @@ storiesOf("Generics / Column picker", module) )) .addDecorator(CardDecorator) .addDecorator(Decorator) - .add("default", () => ); + .add("default", () => ) + .add("loading", () => ( + undefined} + /> + )); diff --git a/src/storybook/stories/products/ProductListPage.tsx b/src/storybook/stories/products/ProductListPage.tsx index 1985d778a..d5f792e97 100644 --- a/src/storybook/stories/products/ProductListPage.tsx +++ b/src/storybook/stories/products/ProductListPage.tsx @@ -3,9 +3,11 @@ import React from "react"; import placeholderImage from "@assets/images/placeholder255x255.png"; import { defaultListSettings } from "@saleor/config"; +import { products as productListFixture } from "@saleor/products/fixtures"; +import { attributes } from "@saleor/productTypes/fixtures"; import { ListViews } from "@saleor/types"; -import { category as categoryFixture } from "../../../categories/fixtures"; import { + fetchMoreProps, filterPageProps, filters, listActionsProps, @@ -16,20 +18,22 @@ import ProductListPage, { } from "../../../products/components/ProductListPage"; import Decorator from "../../Decorator"; -const products = categoryFixture(placeholderImage).products.edges.map( - edge => edge.node -); +const products = productListFixture(placeholderImage); const props: ProductListPageProps = { ...listActionsProps, ...pageListProps.default, ...filterPageProps, + ...fetchMoreProps, + availableInGridAttributes: attributes, defaultSettings: defaultListSettings[ListViews.PRODUCT_LIST], + gridAttributes: attributes, products, settings: { ...pageListProps.default.settings, columns: ["isPublished", "productType", "price"] - } + }, + totalGridAttributes: attributes.length }; storiesOf("Views / Products / Product list", module) diff --git a/src/types.ts b/src/types.ts index 8a6b01353..12e09862d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -125,9 +125,8 @@ export interface ReorderEvent { } export type ReorderAction = (event: ReorderEvent) => void; -export interface FetchMoreProps { +export interface FetchMoreProps { loading: boolean; hasMore: boolean; - onFetch: (value: TData) => void; onFetchMore: () => void; } diff --git a/src/utils/columns/DisplayColumn.tsx b/src/utils/columns/DisplayColumn.tsx new file mode 100644 index 000000000..72dd32898 --- /dev/null +++ b/src/utils/columns/DisplayColumn.tsx @@ -0,0 +1,24 @@ +import React from "react"; + +import { isSelected } from "../lists"; + +export interface DisplayColumnProps { + displayColumns: TColumn[]; + column: TColumn; +} + +const DisplayColumn: React.FC = ({ + displayColumns, + children, + column +}) => { + const displayColumn = React.useCallback( + (column: string) => isSelected(column, displayColumns, (a, b) => a === b), + [displayColumns] + ); + + return <>{displayColumn(column) && children}; +}; + +DisplayColumn.displayName = "DisplayColumn"; +export default DisplayColumn;