Add infinite scroll to product selection

This commit is contained in:
dominik-zeglen 2020-10-26 11:29:41 +01:00
parent 1bfc13c8b5
commit 96d8aa101c
4 changed files with 85 additions and 41 deletions

View file

@ -60,8 +60,11 @@ export const CollectionDetails: React.FC<CollectionDetailsProps> = ({
); );
const paginate = usePaginator(); const paginate = usePaginator();
const intl = useIntl(); const intl = useIntl();
const { search, result } = useProductSearch({ const { search, loadMore, result } = useProductSearch({
variables: DEFAULT_INITIAL_SEARCH_DATA variables: {
...DEFAULT_INITIAL_SEARCH_DATA,
first: 100
}
}); });
const [updateMetadata] = useMetadataUpdate({}); const [updateMetadata] = useMetadataUpdate({});
const [updatePrivateMetadata] = usePrivateMetadataUpdate({}); const [updatePrivateMetadata] = usePrivateMetadataUpdate({});
@ -296,8 +299,10 @@ export const CollectionDetails: React.FC<CollectionDetailsProps> = ({
/> />
<AssignProductDialog <AssignProductDialog
confirmButtonState={assignProductOpts.status} confirmButtonState={assignProductOpts.status}
hasMore={result.data?.search?.pageInfo.hasNextPage}
open={params.action === "assign"} open={params.action === "assign"}
onFetch={search} onFetch={search}
onFetchMore={loadMore}
loading={result.loading} loading={result.loading}
onClose={closeModal} onClose={closeModal}
onSubmit={products => onSubmit={products =>

View file

@ -19,7 +19,9 @@ import useSearchQuery from "@saleor/hooks/useSearchQuery";
import { buttonMessages } from "@saleor/intl"; import { buttonMessages } from "@saleor/intl";
import { maybe } from "@saleor/misc"; import { maybe } from "@saleor/misc";
import { SearchProducts_search_edges_node } from "@saleor/searches/types/SearchProducts"; import { SearchProducts_search_edges_node } from "@saleor/searches/types/SearchProducts";
import { FetchMoreProps } from "@saleor/types";
import React from "react"; import React from "react";
import InfiniteScroll from "react-infinite-scroller";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import Checkbox from "../Checkbox"; import Checkbox from "../Checkbox";
@ -30,7 +32,7 @@ export interface FormData {
} }
const useStyles = makeStyles( const useStyles = makeStyles(
{ theme => ({
avatar: { avatar: {
"&&:first-child": { "&&:first-child": {
paddingLeft: 0 paddingLeft: 0
@ -44,17 +46,25 @@ const useStyles = makeStyles(
colName: { colName: {
paddingLeft: 0 paddingLeft: 0
}, },
loadMoreLoaderContainer: {
alignItems: "center",
display: "flex",
height: theme.spacing(3),
justifyContent: "center",
marginTop: theme.spacing(3)
},
overflow: { overflow: {
overflowY: "visible" overflowY: "visible"
}, },
scrollArea: { scrollArea: {
height: 500,
overflowY: "scroll" overflowY: "scroll"
} }
}, }),
{ name: "AssignProductDialog" } { name: "AssignProductDialog" }
); );
export interface AssignProductDialogProps { export interface AssignProductDialogProps extends FetchMoreProps {
confirmButtonState: ConfirmButtonTransitionState; confirmButtonState: ConfirmButtonTransitionState;
open: boolean; open: boolean;
products: SearchProducts_search_edges_node[]; products: SearchProducts_search_edges_node[];
@ -84,11 +94,13 @@ function handleProductAssign(
const AssignProductDialog: React.FC<AssignProductDialogProps> = props => { const AssignProductDialog: React.FC<AssignProductDialogProps> = props => {
const { const {
confirmButtonState, confirmButtonState,
hasMore,
open, open,
loading, loading,
products, products,
onClose, onClose,
onFetch, onFetch,
onFetchMore,
onSubmit onSubmit
} = props; } = props;
const classes = useStyles(props); const classes = useStyles(props);
@ -135,44 +147,57 @@ const AssignProductDialog: React.FC<AssignProductDialogProps> = props => {
/> />
<FormSpacer /> <FormSpacer />
<div className={classes.scrollArea}> <div className={classes.scrollArea}>
<ResponsiveTable> <InfiniteScroll
<TableBody> pageStart={0}
{products && loadMore={onFetchMore}
products.map(product => { hasMore={hasMore}
const isSelected = selectedProducts.some( useWindow={false}
selectedProduct => selectedProduct.id === product.id loader={
); <div className={classes.loadMoreLoaderContainer}>
<CircularProgress size={16} />
</div>
}
threshold={10}
>
<ResponsiveTable key="table">
<TableBody>
{products &&
products.map(product => {
const isSelected = selectedProducts.some(
selectedProduct => selectedProduct.id === product.id
);
return ( return (
<TableRow key={product.id}> <TableRow key={product.id}>
<TableCellAvatar <TableCellAvatar
className={classes.avatar} className={classes.avatar}
thumbnail={maybe(() => product.thumbnail.url)} thumbnail={maybe(() => product.thumbnail.url)}
/>
<TableCell className={classes.colName}>
{product.name}
</TableCell>
<TableCell
padding="checkbox"
className={classes.checkboxCell}
>
<Checkbox
checked={isSelected}
onChange={() =>
handleProductAssign(
product,
isSelected,
selectedProducts,
setSelectedProducts
)
}
/> />
</TableCell> <TableCell className={classes.colName}>
</TableRow> {product.name}
); </TableCell>
})} <TableCell
</TableBody> padding="checkbox"
</ResponsiveTable> className={classes.checkboxCell}
>
<Checkbox
checked={isSelected}
onChange={() =>
handleProductAssign(
product,
isSelected,
selectedProducts,
setSelectedProducts
)
}
/>
</TableCell>
</TableRow>
);
})}
</TableBody>
</ResponsiveTable>
</InfiniteScroll>
</div> </div>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>

View file

@ -68,18 +68,21 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
); );
const intl = useIntl(); const intl = useIntl();
const { const {
// loadMore: loadMoreCategories,
search: searchCategories, search: searchCategories,
result: searchCategoriesOpts result: searchCategoriesOpts
} = useCategorySearch({ } = useCategorySearch({
variables: DEFAULT_INITIAL_SEARCH_DATA variables: DEFAULT_INITIAL_SEARCH_DATA
}); });
const { const {
// loadMore: loadMoreCollections,
search: searchCollections, search: searchCollections,
result: searchCollectionsOpts result: searchCollectionsOpts
} = useCollectionSearch({ } = useCollectionSearch({
variables: DEFAULT_INITIAL_SEARCH_DATA variables: DEFAULT_INITIAL_SEARCH_DATA
}); });
const { const {
loadMore: loadMoreProducts,
search: searchProducts, search: searchProducts,
result: searchProductsOpts result: searchProductsOpts
} = useProductSearch({ } = useProductSearch({
@ -325,8 +328,13 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
/> />
<AssignProductDialog <AssignProductDialog
confirmButtonState={saleCataloguesAddOpts.status} confirmButtonState={saleCataloguesAddOpts.status}
hasMore={
searchProductsOpts.data?.search.pageInfo
.hasNextPage
}
open={params.action === "assign-product"} open={params.action === "assign-product"}
onFetch={searchProducts} onFetch={searchProducts}
onFetchMore={loadMoreProducts}
loading={searchProductsOpts.loading} loading={searchProductsOpts.loading}
onClose={closeModal} onClose={closeModal}
onSubmit={products => onSubmit={products =>

View file

@ -82,6 +82,7 @@ export const VoucherDetails: React.FC<VoucherDetailsProps> = ({
variables: DEFAULT_INITIAL_SEARCH_DATA variables: DEFAULT_INITIAL_SEARCH_DATA
}); });
const { const {
loadMore: loadMoreProducts,
search: searchProducts, search: searchProducts,
result: searchProductsOpts result: searchProductsOpts
} = useProductSearch({ } = useProductSearch({
@ -484,8 +485,13 @@ export const VoucherDetails: React.FC<VoucherDetailsProps> = ({
confirmButtonState={ confirmButtonState={
voucherCataloguesAddOpts.status voucherCataloguesAddOpts.status
} }
hasMore={
searchProductsOpts.data?.search.pageInfo
.hasNextPage
}
open={params.action === "assign-product"} open={params.action === "assign-product"}
onFetch={searchProducts} onFetch={searchProducts}
onFetchMore={loadMoreProducts}
loading={searchProductsOpts.loading} loading={searchProductsOpts.loading}
onClose={closeModal} onClose={closeModal}
onSubmit={products => onSubmit={products =>