diff --git a/src/categories/components/CategoryProductList/CategoryProductList.tsx b/src/categories/components/CategoryProductList/CategoryProductList.tsx new file mode 100644 index 000000000..2cd9c53d1 --- /dev/null +++ b/src/categories/components/CategoryProductList/CategoryProductList.tsx @@ -0,0 +1,229 @@ +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 TableCellAvatar, { + AVATAR_MARGIN +} from "@saleor/components/TableCellAvatar"; +import TableHead from "@saleor/components/TableHead"; +import TablePagination from "@saleor/components/TablePagination"; +import i18n from "@saleor/i18n"; +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 numberOfColumns = 5; + + return ( +
+ + + + + + + + + + + + {i18n.t("Name", { context: "object" })} + + + + {i18n.t("Type", { context: "object" })} + + + {i18n.t("Published", { context: "object" })} + + + {i18n.t("Price", { context: "object" })} + + + + + + + + + {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 ? ( + + ) : ( + + )} + + + ); + }, + () => ( + + + {i18n.t("No products found")} + + + ) + )} + +
+
+ ); + } +); +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..e69de29bb 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/ColumnPicker/ColumnPicker.tsx b/src/components/ColumnPicker/ColumnPicker.tsx index b2255e8b3..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,33 +42,39 @@ const ColumnPicker: React.FC = props => { const { className, columns, + defaultColumns, hasMore, - initial = false, + initialColumns, + initialOpen = false, loading, - selectedColumns, total, - onCancel, - onColumnToggle, onFetchMore, - onReset, 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 ( @@ -95,9 +110,9 @@ const ColumnPicker: React.FC = props => { selectedColumns={selectedColumns} total={total} onCancel={handleCancel} - onColumnToggle={onColumnToggle} + onColumnToggle={handleColumnToggle} onFetchMore={onFetchMore} - onReset={onReset} + onReset={handleReset} onSave={handleSave} /> diff --git a/src/components/ColumnPicker/ColumnPickerContent.tsx b/src/components/ColumnPicker/ColumnPickerContent.tsx index 8fbc42525..340221ea3 100644 --- a/src/components/ColumnPicker/ColumnPickerContent.tsx +++ b/src/components/ColumnPicker/ColumnPickerContent.tsx @@ -61,6 +61,9 @@ const useStyles = makeStyles((theme: Theme) => ({ gridColumnEnd: "span 3", height: theme.spacing.unit * 3, justifyContent: "center" + }, + root: { + boxShadow: "0px 4px 4px rgba(0, 0, 0, 0.25)" } })); @@ -87,7 +90,7 @@ const ColumnPickerContent: React.FC = props => { : false; return ( - + 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/fixtures.ts b/src/fixtures.ts index 10f3345cd..b55c9d863 100644 --- a/src/fixtures.ts +++ b/src/fixtures.ts @@ -134,6 +134,5 @@ export const filters: Filter[] = [ export const fetchMoreProps: FetchMoreProps = { hasMore: true, loading: false, - onFetch: () => undefined, onFetchMore: () => undefined }; diff --git a/src/products/components/ProductList/ProductList.tsx b/src/products/components/ProductList/ProductList.tsx new file mode 100644 index 000000000..677576ac9 --- /dev/null +++ b/src/products/components/ProductList/ProductList.tsx @@ -0,0 +1,318 @@ +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 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 i18n from "@saleor/i18n"; +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 DisplayColumn: React.FC<{ column: ProductListColumns }> = props => ( + + ); + const gridAttributesFromSettings = settings.columns.filter( + isAttributeColumnValue + ); + const numberOfColumns = 2 + settings.columns.length; + + return ( +
+
+ + + + + + + + + + {gridAttributesFromSettings.map(gridAttribute => ( + + ))} + + + + + + 4 + })} + > + + {i18n.t("Name", { context: "object" })} + + + + + {i18n.t("Type", { context: "object" })} + + + + + {i18n.t("Published", { context: "object" })} + + + {gridAttributesFromSettings.map(gridAttributeFromSettings => ( + + {maybe( + () => + gridAttributes.find( + gridAttribute => + getAttributeIdFromColumnValue( + gridAttributeFromSettings + ) === gridAttribute.id + ).name, + + )} + + ))} + + + {i18n.t("Price", { context: "object" })} + + + + + + + + + + {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 ? ( + + ) : ( + + )} + + + + ); + }, + () => ( + + + {i18n.t("No products found")} + + + ) + )} + +
+
+ ); + } +); +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 b099519b5..d89e8077d 100644 --- a/src/products/components/ProductListPage/ProductListPage.tsx +++ b/src/products/components/ProductListPage/ProductListPage.tsx @@ -6,25 +6,25 @@ 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 { AvailableInGridAttributes_attributes_edges_node } from "@saleor/products/types/AvailableInGridAttributes"; +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 { toggle } from "@saleor/utils/lists"; import { ProductListUrlFilters } from "../../urls"; +import ProductList from "../ProductList"; import ProductListFilter from "../ProductListFilter"; export interface ProductListPageProps @@ -32,10 +32,11 @@ export interface ProductListPageProps ListActions, FilterPageProps, FetchMoreProps { + availableInGridAttributes: AvailableInGridAttributes_availableInGrid_edges_node[]; currencySymbol: string; - gridAttributes: AvailableInGridAttributes_attributes_edges_node[]; + gridAttributes: AvailableInGridAttributes_grid_edges_node[]; totalGridAttributes: number; - products: CategoryDetails_category_products_edges_node[]; + products: ProductList_products_edges_node[]; } const useStyles = makeStyles((theme: Theme) => ({ @@ -52,6 +53,7 @@ export const ProductListPage: React.FC = props => { filtersList, filterTabs, gridAttributes, + availableInGridAttributes, hasMore, initialSearch, loading, @@ -70,23 +72,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[] = [ { @@ -110,7 +98,7 @@ export const ProductListPage: React.FC = props => { }), value: "productType" as ProductListColumns }, - ...gridAttributes.map(attribute => ({ + ...availableInGridAttributes.map(attribute => ({ label: attribute.name, value: `attribute:${attribute.id}` })) @@ -122,14 +110,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)} + + ) + }} /> - - - 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 + + + + productBulkPublish({ + variables: { + ids: params.ids, + isPublished: false + } + }) } - }) - } - title={intl.formatMessage({ - defaultMessage: "Publish Products", - description: "dialog header" - })} - > - - + + 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: 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/stories/categories/CategoryProducts.tsx b/src/storybook/stories/categories/CategoryProducts.tsx deleted file mode 100644 index 07a415b3a..000000000 --- a/src/storybook/stories/categories/CategoryProducts.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { storiesOf } from "@storybook/react"; -import React from "react"; - -import placeholder from "@assets/images/placeholder60x60.png"; -import CategoryProducts from "../../../categories/components/CategoryProducts"; -import Decorator from "../../Decorator"; - -const products = [ - { - id: "UHJvZHVjdDox", - name: "Gardner, Graham and King", - productType: { - id: "1", - name: "T-Shirt" - }, - thumbnail: { - url: placeholder - } - }, - { - id: "UHJvZHVjdDoy", - name: "Gardner, Graham and King", - productType: { - id: "1", - name: "T-Shirt" - }, - thumbnail: { - url: placeholder - } - }, - { - id: "UHJvZHVjdDoz", - name: "Gardner, Graham and King", - productType: { - id: "1", - name: "T-Shirt" - }, - thumbnail: { - url: placeholder - } - }, - { - id: "UHJvZHVjdDoa", - name: "Gardner, Graham and King", - productType: { - id: "1", - name: "T-Shirt" - }, - thumbnail: { - url: placeholder - } - } -]; - -storiesOf("Categories / CategoryProducts", module) - .addDecorator(Decorator) - .add("without initial data", () => ( - - )) - .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 f23ec4824..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) 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/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;