Paginate tax rates in the tax class view (#2997)

This commit is contained in:
poulch 2023-01-20 13:51:46 +01:00 committed by GitHub
parent 05b2da8082
commit 221adf25d6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 240 additions and 14 deletions

View file

@ -1,10 +1,11 @@
import { commonMessages } from "@dashboard/intl";
import { TableCell } from "@material-ui/core";
import {
Pagination,
PaginationProps as MacawPaginationProps,
} from "@saleor/macaw-ui";
import React from "react";
import { defineMessages, useIntl } from "react-intl";
import { useIntl } from "react-intl";
import { Link, LinkProps } from "react-router-dom";
import { ListSettings } from "../../types";
@ -14,14 +15,6 @@ export type ListSettingsUpdate = <T extends keyof ListSettings>(
value: ListSettings[T],
) => void;
const messages = defineMessages({
noOfRows: {
id: "2HfSiT",
defaultMessage: "No. of rows",
description: "pagination",
},
});
export interface PaginationProps
extends Omit<
MacawPaginationProps,
@ -57,7 +50,7 @@ export const TablePagination: React.FC<PaginationProps> = ({
hasNextPage={hasNextPage && !disabled}
hasPreviousPage={hasPreviousPage && !disabled}
labels={{
noOfRows: intl.formatMessage(messages.noOfRows),
noOfRows: intl.formatMessage(commonMessages.noOfRows),
}}
rowNumber={settings?.rowNumber}
onRowNumberUpdate={

View file

@ -0,0 +1 @@
export * from "./useClientPagination";

View file

@ -0,0 +1,94 @@
import { act } from "@testing-library/react";
import { renderHook } from "@testing-library/react-hooks";
import { useClientPagination } from "./useClientPagination";
describe("useClientPagination", () => {
test("should reset current page when row number change", () => {
// Arrange
const { result } = renderHook(() => useClientPagination());
// Act
act(() => result.current.changeCurrentPage(2));
act(() => result.current.changeRowNumber(20));
// Assert
expect(result.current.currentPage).toEqual(1);
expect(result.current.rowNumber).toEqual(20);
});
test("should reset current page and row number when call restartPagination", () => {
// Arrange
const { result } = renderHook(() => useClientPagination());
// Act
act(() => result.current.changeCurrentPage(2));
act(() => result.current.changeRowNumber(20));
act(() => result.current.restartPagination());
// Assert
expect(result.current.currentPage).toEqual(1);
expect(result.current.rowNumber).toEqual(10);
});
test("should change current page when call changeCurrentPage", () => {
// Arrange
const { result } = renderHook(() => useClientPagination());
// Act
act(() => result.current.changeCurrentPage(2));
// Assert
expect(result.current.currentPage).toEqual(2);
});
test("should change row number when call changeRowNumber", () => {
// Arrange
const { result } = renderHook(() => useClientPagination());
// Act
act(() => result.current.changeRowNumber(20));
// Assert
expect(result.current.rowNumber).toEqual(20);
});
test("should return paginated data slice to first 10", () => {
// Arrange & Act
const { result } = renderHook(() => useClientPagination());
const paginatedData = result.current.paginate(Array.from(Array(20).keys()));
// Assert
expect(paginatedData.hasNextPage).toEqual(true);
expect(paginatedData.hasPreviousPage).toEqual(false);
expect(paginatedData.data.length).toEqual(10);
});
test("should return paginated data with false hasNextPage", () => {
// Arrange
const { result } = renderHook(() => useClientPagination());
// Act
act(() => result.current.changeCurrentPage(2));
// Assert
const paginatedData = result.current.paginate(Array.from(Array(20).keys()));
expect(paginatedData.hasNextPage).toEqual(false);
expect(paginatedData.hasPreviousPage).toEqual(true);
expect(paginatedData.data.length).toEqual(10);
});
test("should return paginated data with false hasNextPage and hasPreviousPage", () => {
// Arrange
const { result } = renderHook(() => useClientPagination());
// Act
act(() => result.current.changeRowNumber(25));
// Assert
const paginatedData = result.current.paginate(Array.from(Array(20).keys()));
expect(paginatedData.hasNextPage).toEqual(false);
expect(paginatedData.hasPreviousPage).toEqual(false);
expect(paginatedData.data.length).toEqual(20);
});
});

View file

@ -0,0 +1,47 @@
import { useCallback, useEffect, useState } from "react";
const DEFAULT_ROWS_COUNT = 10;
const FIRST_PAGINATED_PAGE = 1;
export const useClientPagination = () => {
const [rowNumber, setRowNumber] = useState(DEFAULT_ROWS_COUNT);
const [currentPage, setCurrentPage] = useState(FIRST_PAGINATED_PAGE);
const indexOfLastElement = currentPage * rowNumber;
const indexOfFirstElement = indexOfLastElement - rowNumber;
useEffect(() => {
setCurrentPage(FIRST_PAGINATED_PAGE);
}, [rowNumber]);
const restartPagination = useCallback(() => {
setCurrentPage(FIRST_PAGINATED_PAGE);
setRowNumber(DEFAULT_ROWS_COUNT);
}, []);
const changeRowNumber = useCallback((rowNumber: number) => {
setRowNumber(rowNumber);
}, []);
const changeCurrentPage = useCallback((page: number) => {
setCurrentPage(page);
}, []);
const paginate = useCallback(
<T>(data: T[]) => ({
data: data.slice(indexOfFirstElement, indexOfLastElement),
hasNextPage: data.length / (rowNumber * currentPage) > 1,
hasPreviousPage: currentPage > 1,
}),
[currentPage, indexOfFirstElement, indexOfLastElement, rowNumber],
);
return {
rowNumber,
changeRowNumber,
changeCurrentPage,
currentPage,
restartPagination,
paginate,
};
};

View file

@ -190,6 +190,11 @@ export const commonMessages = defineMessages({
id: "D3idYv",
defaultMessage: "Settings",
},
noOfRows: {
id: "2HfSiT",
defaultMessage: "No. of rows",
description: "pagination",
},
});
export const errorMessages = defineMessages({

View file

@ -0,0 +1,58 @@
import { commonMessages } from "@dashboard/intl";
import { makeStyles, Pagination } from "@saleor/macaw-ui";
import React from "react";
import { useIntl } from "react-intl";
interface TaxPaginationProps {
rowNumber: number;
currentPage: number;
setRowNumber: (rowNumber: number) => void;
hasNextPage: boolean;
hasPrevPage: boolean;
setCurrentPage: (currentPage: number) => void;
}
const useStyles = makeStyles(
theme => ({
container: {
padding: theme.spacing(0, 4),
},
}),
{ name: "TaxPagination" },
);
export const TaxPagination = ({
rowNumber,
setRowNumber,
setCurrentPage,
hasNextPage,
hasPrevPage,
currentPage,
}: TaxPaginationProps) => {
const classes = useStyles();
const intl = useIntl();
const handleNextPage = () => {
setCurrentPage(currentPage + 1);
};
const handlePrevPage = () => {
setCurrentPage(currentPage - 1);
};
return (
<div className={classes.container}>
<Pagination
hasNextPage={hasNextPage}
hasPreviousPage={hasPrevPage}
labels={{
noOfRows: intl.formatMessage(commonMessages.noOfRows),
}}
rowNumber={rowNumber}
onRowNumberUpdate={setRowNumber}
onNextPage={handleNextPage}
onPreviousPage={handlePrevPage}
/>
</div>
);
};

View file

@ -0,0 +1 @@
export * from "./TaxPagination";

View file

@ -8,6 +8,7 @@ import Savebar from "@dashboard/components/Savebar";
import Skeleton from "@dashboard/components/Skeleton";
import { configurationMenuUrl } from "@dashboard/configuration";
import { TaxClassFragment } from "@dashboard/graphql";
import { useClientPagination } from "@dashboard/hooks/useClientPagination/useClientPagination";
import { SubmitPromise } from "@dashboard/hooks/useForm";
import useNavigator from "@dashboard/hooks/useNavigator";
import { getById } from "@dashboard/misc";
@ -36,10 +37,11 @@ import {
PageTabs,
SearchIcon,
} from "@saleor/macaw-ui";
import React from "react";
import React, { useEffect, useMemo, useState } from "react";
import { FormattedMessage, useIntl } from "react-intl";
import TaxInput from "../../components/TaxInput";
import { TaxPagination } from "../../components/TaxPagination";
import TaxClassesForm from "./form";
import { useStyles } from "./styles";
import TaxClassesMenu from "./TaxClassesMenu";
@ -72,9 +74,17 @@ export const TaxClassesPage: React.FC<TaxClassesPageProps> = props => {
const navigate = useNavigator();
const classes = useStyles();
const [query, setQuery] = React.useState("");
const [query, setQuery] = useState("");
const {
rowNumber,
currentPage,
paginate,
restartPagination,
changeCurrentPage,
changeRowNumber,
} = useClientPagination();
const currentTaxClass = React.useMemo(
const currentTaxClass = useMemo(
() => taxClasses?.find(getById(selectedTaxClassId)),
[selectedTaxClassId, taxClasses],
);
@ -83,6 +93,10 @@ export const TaxClassesPage: React.FC<TaxClassesPageProps> = props => {
currentTaxClass?.id,
]);
useEffect(() => {
restartPagination();
}, [query, restartPagination]);
return (
<TaxClassesForm
taxClass={currentTaxClass}
@ -95,6 +109,10 @@ export const TaxClassesPage: React.FC<TaxClassesPageProps> = props => {
rate => rate.label.search(new RegExp(parseQuery(query), "i")) >= 0,
);
const { data: paginatedRates, hasNextPage, hasPreviousPage } = paginate(
filteredRates,
);
const formErrors = getFormErrors(["name"], validationErrors);
return (
@ -204,7 +222,7 @@ export const TaxClassesPage: React.FC<TaxClassesPageProps> = props => {
</ListItem>
</ListHeader>
<Divider />
{filteredRates?.map(
{paginatedRates?.map(
(countryRate, countryRateIndex) => (
<React.Fragment key={countryRate.id}>
<ListItem
@ -238,6 +256,15 @@ export const TaxClassesPage: React.FC<TaxClassesPageProps> = props => {
<VerticalSpacer />
</>
)}
<TaxPagination
rowNumber={rowNumber}
setRowNumber={changeRowNumber}
hasNextPage={hasNextPage}
hasPrevPage={hasPreviousPage}
currentPage={currentPage}
setCurrentPage={changeCurrentPage}
/>
</List>
</>
)}