Add load more to category and collection selection

This commit is contained in:
dominik-zeglen 2020-10-26 12:20:52 +01:00
parent 96d8aa101c
commit fb0a503b91
5 changed files with 183 additions and 80 deletions

View file

@ -17,7 +17,10 @@ import ResponsiveTable from "@saleor/components/ResponsiveTable";
import useSearchQuery from "@saleor/hooks/useSearchQuery"; import useSearchQuery from "@saleor/hooks/useSearchQuery";
import { buttonMessages } from "@saleor/intl"; import { buttonMessages } from "@saleor/intl";
import { SearchCategories_search_edges_node } from "@saleor/searches/types/SearchCategories"; import { SearchCategories_search_edges_node } from "@saleor/searches/types/SearchCategories";
import useScrollableDialogStyle from "@saleor/styles/useScrollableDialogStyle";
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";
@ -37,9 +40,6 @@ const useStyles = makeStyles(
checkboxCell: { checkboxCell: {
paddingLeft: 0 paddingLeft: 0
}, },
overflow: {
overflowY: "visible"
},
wideCell: { wideCell: {
width: "100%" width: "100%"
} }
@ -47,7 +47,7 @@ const useStyles = makeStyles(
{ name: "AssignCategoryDialog" } { name: "AssignCategoryDialog" }
); );
interface AssignCategoriesDialogProps { interface AssignCategoriesDialogProps extends FetchMoreProps {
categories: SearchCategories_search_edges_node[]; categories: SearchCategories_search_edges_node[];
confirmButtonState: ConfirmButtonTransitionState; confirmButtonState: ConfirmButtonTransitionState;
open: boolean; open: boolean;
@ -80,25 +80,31 @@ const AssignCategoriesDialog: React.FC<AssignCategoriesDialogProps> = props => {
open, open,
loading, loading,
categories: categories, categories: categories,
hasMore,
onClose, onClose,
onFetch, onFetch,
onFetchMore,
onSubmit onSubmit
} = props; } = props;
const classes = useStyles(props); const classes = useStyles(props);
const scrollableDialogClasses = useScrollableDialogStyle({});
const intl = useIntl(); const intl = useIntl();
const [query, onQueryChange] = useSearchQuery(onFetch); const [query, onQueryChange] = useSearchQuery(onFetch);
const [selectedCategories, setSelectedCategories] = React.useState< const [selectedCategories, setSelectedCategories] = React.useState<
SearchCategories_search_edges_node[] SearchCategories_search_edges_node[]
>([]); >([]);
const container = React.useRef<HTMLDivElement>();
const handleSubmit = () => onSubmit(selectedCategories); const handleSubmit = () => onSubmit(selectedCategories);
const containerHeight = container.current?.scrollHeight - 130;
return ( return (
<Dialog <Dialog
classes={{ paper: scrollableDialogClasses.dialog }}
open={open} open={open}
onClose={onClose} onClose={onClose}
classes={{ paper: classes.overflow }}
fullWidth fullWidth
maxWidth="sm" maxWidth="sm"
> >
@ -108,7 +114,10 @@ const AssignCategoriesDialog: React.FC<AssignCategoriesDialogProps> = props => {
description="dialog header" description="dialog header"
/> />
</DialogTitle> </DialogTitle>
<DialogContent className={classes.overflow}> <DialogContent
className={scrollableDialogClasses.content}
ref={container}
>
<TextField <TextField
name="query" name="query"
value={query} value={query}
@ -126,40 +135,59 @@ const AssignCategoriesDialog: React.FC<AssignCategoriesDialogProps> = props => {
}} }}
/> />
<FormSpacer /> <FormSpacer />
<ResponsiveTable> <div
<TableBody> className={scrollableDialogClasses.scrollArea}
{categories && style={{ height: containerHeight }}
categories.map(category => { >
const isSelected = !!selectedCategories.find( <InfiniteScroll
selectedCategories => selectedCategories.id === category.id pageStart={0}
); loadMore={onFetchMore}
hasMore={hasMore}
useWindow={false}
loader={
<div className={scrollableDialogClasses.loadMoreLoaderContainer}>
<CircularProgress size={16} />
</div>
}
threshold={10}
>
<ResponsiveTable key="table">
<TableBody>
{categories &&
categories.map(category => {
const isSelected = !!selectedCategories.find(
selectedCategories =>
selectedCategories.id === category.id
);
return ( return (
<TableRow key={category.id}> <TableRow key={category.id}>
<TableCell <TableCell
padding="checkbox" padding="checkbox"
className={classes.checkboxCell} className={classes.checkboxCell}
> >
<Checkbox <Checkbox
checked={isSelected} checked={isSelected}
onChange={() => onChange={() =>
handleCategoryAssign( handleCategoryAssign(
category, category,
isSelected, isSelected,
selectedCategories, selectedCategories,
setSelectedCategories setSelectedCategories
) )
} }
/> />
</TableCell> </TableCell>
<TableCell className={classes.wideCell}> <TableCell className={classes.wideCell}>
{category.name} {category.name}
</TableCell> </TableCell>
</TableRow> </TableRow>
); );
})} })}
</TableBody> </TableBody>
</ResponsiveTable> </ResponsiveTable>
</InfiniteScroll>
</div>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={onClose}> <Button onClick={onClose}>

View file

@ -13,7 +13,10 @@ import ResponsiveTable from "@saleor/components/ResponsiveTable";
import useSearchQuery from "@saleor/hooks/useSearchQuery"; import useSearchQuery from "@saleor/hooks/useSearchQuery";
import { buttonMessages } from "@saleor/intl"; import { buttonMessages } from "@saleor/intl";
import { SearchCollections_search_edges_node } from "@saleor/searches/types/SearchCollections"; import { SearchCollections_search_edges_node } from "@saleor/searches/types/SearchCollections";
import useScrollableDialogStyle from "@saleor/styles/useScrollableDialogStyle";
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";
@ -37,9 +40,6 @@ const useStyles = makeStyles(
checkboxCell: { checkboxCell: {
paddingLeft: 0 paddingLeft: 0
}, },
overflow: {
overflowY: "visible"
},
wideCell: { wideCell: {
width: "100%" width: "100%"
} }
@ -47,7 +47,7 @@ const useStyles = makeStyles(
{ name: "AssignCollectionDialog" } { name: "AssignCollectionDialog" }
); );
interface AssignCollectionDialogProps { interface AssignCollectionDialogProps extends FetchMoreProps {
collections: SearchCollections_search_edges_node[]; collections: SearchCollections_search_edges_node[];
confirmButtonState: ConfirmButtonTransitionState; confirmButtonState: ConfirmButtonTransitionState;
open: boolean; open: boolean;
@ -77,28 +77,34 @@ function handleCollectionAssign(
const AssignCollectionDialog: React.FC<AssignCollectionDialogProps> = props => { const AssignCollectionDialog: React.FC<AssignCollectionDialogProps> = props => {
const { const {
confirmButtonState, confirmButtonState,
hasMore,
open, open,
loading, loading,
collections, collections,
onClose, onClose,
onFetch, onFetch,
onFetchMore,
onSubmit onSubmit
} = props; } = props;
const classes = useStyles(props); const classes = useStyles(props);
const scrollableDialogClasses = useScrollableDialogStyle({});
const intl = useIntl(); const intl = useIntl();
const [query, onQueryChange] = useSearchQuery(onFetch); const [query, onQueryChange] = useSearchQuery(onFetch);
const [selectedCollections, setSelectedCollections] = React.useState< const [selectedCollections, setSelectedCollections] = React.useState<
SearchCollections_search_edges_node[] SearchCollections_search_edges_node[]
>([]); >([]);
const container = React.useRef<HTMLDivElement>();
const handleSubmit = () => onSubmit(selectedCollections); const handleSubmit = () => onSubmit(selectedCollections);
const containerHeight = container.current?.scrollHeight - 130;
return ( return (
<Dialog <Dialog
onClose={onClose} onClose={onClose}
open={open} open={open}
classes={{ paper: classes.overflow }} classes={{ paper: scrollableDialogClasses.dialog }}
fullWidth fullWidth
maxWidth="sm" maxWidth="sm"
> >
@ -108,7 +114,7 @@ const AssignCollectionDialog: React.FC<AssignCollectionDialogProps> = props => {
description="dialog header" description="dialog header"
/> />
</DialogTitle> </DialogTitle>
<DialogContent className={classes.overflow}> <DialogContent className={scrollableDialogClasses.content}>
<TextField <TextField
name="query" name="query"
value={query} value={query}
@ -126,40 +132,59 @@ const AssignCollectionDialog: React.FC<AssignCollectionDialogProps> = props => {
}} }}
/> />
<FormSpacer /> <FormSpacer />
<ResponsiveTable> <div
<TableBody> className={scrollableDialogClasses.scrollArea}
{collections && style={{ height: containerHeight }}
collections.map(collection => { >
const isSelected = !!selectedCollections.find( <InfiniteScroll
selectedCollection => selectedCollection.id === collection.id pageStart={0}
); loadMore={onFetchMore}
hasMore={hasMore}
useWindow={false}
loader={
<div className={scrollableDialogClasses.loadMoreLoaderContainer}>
<CircularProgress size={16} />
</div>
}
threshold={10}
>
<ResponsiveTable>
<TableBody>
{collections &&
collections.map(collection => {
const isSelected = !!selectedCollections.find(
selectedCollection =>
selectedCollection.id === collection.id
);
return ( return (
<TableRow key={collection.id}> <TableRow key={collection.id}>
<TableCell <TableCell
padding="checkbox" padding="checkbox"
className={classes.checkboxCell} className={classes.checkboxCell}
> >
<Checkbox <Checkbox
checked={isSelected} checked={isSelected}
onChange={() => onChange={() =>
handleCollectionAssign( handleCollectionAssign(
collection, collection,
isSelected, isSelected,
selectedCollections, selectedCollections,
setSelectedCollections setSelectedCollections
) )
} }
/> />
</TableCell> </TableCell>
<TableCell className={classes.wideCell}> <TableCell className={classes.wideCell}>
{collection.name} {collection.name}
</TableCell> </TableCell>
</TableRow> </TableRow>
); );
})} })}
</TableBody> </TableBody>
</ResponsiveTable> </ResponsiveTable>
</InfiniteScroll>
</div>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={onClose}> <Button onClick={onClose}>

View file

@ -68,14 +68,14 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
); );
const intl = useIntl(); const intl = useIntl();
const { const {
// loadMore: loadMoreCategories, 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, loadMore: loadMoreCollections,
search: searchCollections, search: searchCollections,
result: searchCollectionsOpts result: searchCollectionsOpts
} = useCollectionSearch({ } = useCollectionSearch({
@ -367,8 +367,13 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
) )
)} )}
confirmButtonState={saleCataloguesAddOpts.status} confirmButtonState={saleCataloguesAddOpts.status}
hasMore={
searchCategoriesOpts.data?.search.pageInfo
.hasNextPage
}
open={params.action === "assign-category"} open={params.action === "assign-category"}
onFetch={searchCategories} onFetch={searchCategories}
onFetchMore={loadMoreCategories}
loading={searchCategoriesOpts.loading} loading={searchCategoriesOpts.loading}
onClose={closeModal} onClose={closeModal}
onSubmit={categories => onSubmit={categories =>
@ -394,8 +399,13 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
) )
)} )}
confirmButtonState={saleCataloguesAddOpts.status} confirmButtonState={saleCataloguesAddOpts.status}
hasMore={
searchCollectionsOpts.data?.search.pageInfo
.hasNextPage
}
open={params.action === "assign-collection"} open={params.action === "assign-collection"}
onFetch={searchCollections} onFetch={searchCollections}
onFetchMore={loadMoreCollections}
loading={searchCollectionsOpts.loading} loading={searchCollectionsOpts.loading}
onClose={closeModal} onClose={closeModal}
onSubmit={collections => onSubmit={collections =>

View file

@ -70,12 +70,14 @@ export const VoucherDetails: React.FC<VoucherDetailsProps> = ({
); );
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({
@ -411,8 +413,13 @@ export const VoucherDetails: React.FC<VoucherDetailsProps> = ({
confirmButtonState={ confirmButtonState={
voucherCataloguesAddOpts.status voucherCataloguesAddOpts.status
} }
hasMore={
searchCategoriesOpts.data?.search.pageInfo
.hasNextPage
}
open={params.action === "assign-category"} open={params.action === "assign-category"}
onFetch={searchCategories} onFetch={searchCategories}
onFetchMore={loadMoreCategories}
loading={searchCategoriesOpts.loading} loading={searchCategoriesOpts.loading}
onClose={closeModal} onClose={closeModal}
onSubmit={categories => onSubmit={categories =>
@ -440,8 +447,13 @@ export const VoucherDetails: React.FC<VoucherDetailsProps> = ({
confirmButtonState={ confirmButtonState={
voucherCataloguesAddOpts.status voucherCataloguesAddOpts.status
} }
hasMore={
searchCollectionsOpts.data?.search.pageInfo
.hasNextPage
}
open={params.action === "assign-collection"} open={params.action === "assign-collection"}
onFetch={searchCollections} onFetch={searchCollections}
onFetchMore={loadMoreCollections}
loading={searchCollectionsOpts.loading} loading={searchCollectionsOpts.loading}
onClose={closeModal} onClose={closeModal}
onSubmit={collections => onSubmit={collections =>

View file

@ -0,0 +1,28 @@
import makeStyles from "@material-ui/core/styles/makeStyles";
const useScrollableDialogStyle = makeStyles(
theme => ({
content: {
overflowY: "hidden"
},
dialog: {
height: "calc(100% - 64px)",
maxHeight: 700
},
loadMoreLoaderContainer: {
alignItems: "center",
display: "flex",
height: theme.spacing(3),
justifyContent: "center",
marginTop: theme.spacing(3)
},
scrollArea: {
overflowY: "scroll"
}
}),
{
name: "ScrollableDialog"
}
);
export default useScrollableDialogStyle;