From a62f9ef72e290c1b1658b9ab2df0fa6085c9d647 Mon Sep 17 00:00:00 2001 From: Enrique Moreno Date: Fri, 21 Feb 2025 12:34:46 +0100 Subject: [PATCH 1/4] Modified HalTable to allow APIs not compatible with pagination --- README.md | 16 ++++++------- lib/src/components/HalTable.tsx | 41 +++++++++++++++++++++++++-------- lib/src/components/types.ts | 4 ++++ 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 2ca7aee..a07f442 100644 --- a/README.md +++ b/README.md @@ -33,14 +33,14 @@ Hooks #### HalTable Props -| Name | Default | Description | -| :------------------------------------------- | :-------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| collectionUrl: `string` | | The URL of the collection resource to be used for the table. `Required` | -| headers: `object` | | Contains the HTTP headers to be sent along with the HTTP requests to the collectionUrl. `Optional` | -| itemsPerPage: `number` | 5 | The amount of items to be displayed per page. Will be used to calculate the `_start` and `_num` query parameters that will be sent to the collection for pagination. `Optional` | -| asyncHeadersHandler: `() => Promise` | | Async function that will be executed right before every HTTP request in order to retrieve dynamic headers. It must return a promise that resolves into an object with the keys and values of the headers. These headers will be merged with the ones indicated in the `headers` prop. `Optional` | -| columns: `Column[]` | [] | Array of objects specifying the columns to be displayed in the table. Each Column object has:
- header: Column label to be place at the table header.
- displayProperty: The name of the property in the items summary to be rendered for this column in the table.
- sortProperty: The name of the property in the items summary to be used for sorting the table.
- onClickItemFunction: Callback function that will be executed when the user clicks an item in that column. The collection item will be passed to this function when executed.
- mapFunction: Callback function that must return the value to be rendered in that column for a specific item. The item will be passed to this function as a parameter. | -| mode: `'default'` \| `'reduced'` | `default` | The available table modes:
- default: Default table size.
- reduced: More compact table with less spacing for high density information. | +| Name | Default | Description | +| :------------------------------------------- | :------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| collectionUrl: `string` | | The URL of the collection resource to be used for the table. `Required` | +| headers: `object` | | Contains the HTTP headers to be sent along with the HTTP requests to the collectionUrl. `Optional` | +| itemsPerPage: `number` | 5 | The amount of items to be displayed per page. Will be used to calculate the `_start` and `_num` query parameters that will be sent to the collection for pagination. `Optional` | +| asyncHeadersHandler: `() => Promise` | | Async function that will be executed right before every HTTP request in order to retrieve dynamic headers. It must return a promise that resolves into an object with the keys and values of the headers. These headers will be merged with the ones indicated in the `headers` prop. `Optional` | +| columns: `Column[]` | [] | Array of objects specifying the columns to be displayed in the table. Each Column object has:
- header: Column label to be place at the table header.
- displayProperty: The name of the property in the items summary to be rendered for this column in the table.
- sortProperty: The name of the property in the items summary to be used for sorting the table.
- onClickItemFunction: Callback function that will be executed when the user clicks an item in that column. The collection item will be passed to this function when executed.
- mapFunction: Callback function that must return the value to be rendered in that column for a specific item. The item will be passed to this function as a parameter. | +| hidePaginator: `boolean` | false | If true, paginator will not be displayed. `Optional` | #### HalTable Example diff --git a/lib/src/components/HalTable.tsx b/lib/src/components/HalTable.tsx index 9dff8e5..22aa85b 100644 --- a/lib/src/components/HalTable.tsx +++ b/lib/src/components/HalTable.tsx @@ -5,24 +5,36 @@ import { DxcPaginator, DxcFlex, DxcTypography, - HalstackProvider, } from "@dxc-technology/halstack-react"; import { HalApiCaller } from "@dxc-technology/halstack-client"; import styled from "styled-components"; import icons from "./Icons"; import { HalTableProps } from "./types"; -const addPageParams = ({ collectionUrl, page, itemsPerPage, sortColumn }) => { - return `${collectionUrl}${collectionUrl.includes("?") ? "&" : "?"}_start=${ - (page - 1) * itemsPerPage + 1 - }&_num=${itemsPerPage}${sortColumn ? `&_sort=${sortColumn}` : ``}`; +const addPageParams = ({ collectionUrl, page, itemsPerPage, sortColumn, handlePagination }) => { + let url = collectionUrl; + if (handlePagination) { + url += `${url.includes("?") ? "&" : "?"}_start=${ + (page - 1) * itemsPerPage + 1 + }&_num=${itemsPerPage}`; + } + if (sortColumn) { + url += `&_sort=${sortColumn}`; + } + return url; }; type NavigationFunctions = { onPageChange: (newPage: number) => void; sort: (column: string) => void; }; -const useCollection = (collectionUrl, asyncHeadersHandler, headers, itemsPerPage) => { +const useCollection = ( + collectionUrl, + asyncHeadersHandler, + headers, + itemsPerPage, + handlePagination +) => { const [isLoading, changeIsLoading] = useState(true); const [navigationFunctions, changeNavigationFunctions] = useState({ onPageChange: () => {}, @@ -40,7 +52,7 @@ const useCollection = (collectionUrl, asyncHeadersHandler, headers, itemsPerPage try { const asyncHeadears = asyncHeadersHandler ? await asyncHeadersHandler() : {}; const response = await HalApiCaller.get({ - url: addPageParams({ collectionUrl, page, itemsPerPage, sortColumn }), + url: addPageParams({ collectionUrl, page, itemsPerPage, sortColumn, handlePagination }), headers: { ...headers, ...asyncHeadears }, }); changeIsLoading(false); @@ -67,7 +79,15 @@ const useCollection = (collectionUrl, asyncHeadersHandler, headers, itemsPerPage }; fetchList(); - }, [collectionUrl, asyncHeadersHandler, headers, page, itemsPerPage, sortColumn]); + }, [ + collectionUrl, + asyncHeadersHandler, + headers, + page, + itemsPerPage, + handlePagination, + sortColumn, + ]); return { isLoading, @@ -102,6 +122,7 @@ const HalTable = ({ collectionUrl, asyncHeadersHandler, headers, + hidePaginator = false, columns, itemsPerPage = 5, mode = "default", @@ -114,7 +135,7 @@ const HalTable = ({ totalCollectionItems, error, sortColumn, - } = useCollection(collectionUrl, asyncHeadersHandler, headers, itemsPerPage); + } = useCollection(collectionUrl, asyncHeadersHandler, headers, itemsPerPage, !hidePaginator); const { onPageChange, sort } = navigationFunctions; return ( @@ -184,7 +205,7 @@ const HalTable = ({ ) )} - {!error && totalCollectionItems > 0 && ( + {!error && !hidePaginator && totalCollectionItems > 0 && ( Date: Fri, 21 Feb 2025 13:12:56 +0100 Subject: [PATCH 2/4] Remove pagination when itemsPerPage is higher than total --- lib/src/components/HalTable.tsx | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/src/components/HalTable.tsx b/lib/src/components/HalTable.tsx index 22aa85b..902b126 100644 --- a/lib/src/components/HalTable.tsx +++ b/lib/src/components/HalTable.tsx @@ -205,15 +205,18 @@ const HalTable = ({ ) )} - {!error && !hidePaginator && totalCollectionItems > 0 && ( - - )} + {!error && + !hidePaginator && + totalCollectionItems > 0 && + totalCollectionItems > itemsPerPage && ( + + )} {error && ( {error} From 469ba41a825cfd92d7becd12a561e53c4d8639c8 Mon Sep 17 00:00:00 2001 From: Enrique Moreno Date: Mon, 24 Feb 2025 11:10:53 +0100 Subject: [PATCH 3/4] Added tests for new paginator scenarios --- lib/src/tests/halTable.test.jsx | 47 ++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/lib/src/tests/halTable.test.jsx b/lib/src/tests/halTable.test.jsx index 6df6273..df4f92f 100644 --- a/lib/src/tests/halTable.test.jsx +++ b/lib/src/tests/halTable.test.jsx @@ -1,4 +1,4 @@ -import { fireEvent, render, waitFor } from "@testing-library/react"; +import { fireEvent, queryByText, render, waitFor } from "@testing-library/react"; import HalTable from "../components/HalTable"; describe("HalTable component tests", () => { @@ -69,4 +69,49 @@ describe("HalTable component tests", () => { fireEvent.click(getByText("trewqasdfgyhujikolpyt")); expect(onCellClick).toHaveBeenCalledWith("trewqasdfgyhujikolpyt"); }); + test("HalTable is not including paginator when itemsPerPage exceeds total", async () => { + const onCellClick = jest.fn(); + const { queryByText } = render( + onCellClick(item.summary.identifier), + }, + { + header: "baseCompany", + displayProperty: "baseCompany", + sortProperty: "baseCompany", + }, + ]} + itemsPerPage={100} + /> + ); + await waitFor(() => expect(queryByText("Go to page:")).toBeFalsy()); + }); + test("HalTable is including paginator by default", async () => { + const onCellClick = jest.fn(); + const { getByText } = render( + onCellClick(item.summary.identifier), + }, + { + header: "baseCompany", + displayProperty: "baseCompany", + sortProperty: "baseCompany", + }, + ]} + /> + ); + await waitFor(() => expect(getByText("Go to page:")).toBeTruthy()); + }); }); From 6a12ccb56f9391b87c9396ca721e417551f96318 Mon Sep 17 00:00:00 2001 From: Enrique Moreno Date: Mon, 24 Feb 2025 11:19:44 +0100 Subject: [PATCH 4/4] Improved readme with the missing props info --- README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a07f442..b516b13 100644 --- a/README.md +++ b/README.md @@ -33,14 +33,15 @@ Hooks #### HalTable Props -| Name | Default | Description | -| :------------------------------------------- | :------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| collectionUrl: `string` | | The URL of the collection resource to be used for the table. `Required` | -| headers: `object` | | Contains the HTTP headers to be sent along with the HTTP requests to the collectionUrl. `Optional` | -| itemsPerPage: `number` | 5 | The amount of items to be displayed per page. Will be used to calculate the `_start` and `_num` query parameters that will be sent to the collection for pagination. `Optional` | -| asyncHeadersHandler: `() => Promise` | | Async function that will be executed right before every HTTP request in order to retrieve dynamic headers. It must return a promise that resolves into an object with the keys and values of the headers. These headers will be merged with the ones indicated in the `headers` prop. `Optional` | -| columns: `Column[]` | [] | Array of objects specifying the columns to be displayed in the table. Each Column object has:
- header: Column label to be place at the table header.
- displayProperty: The name of the property in the items summary to be rendered for this column in the table.
- sortProperty: The name of the property in the items summary to be used for sorting the table.
- onClickItemFunction: Callback function that will be executed when the user clicks an item in that column. The collection item will be passed to this function when executed.
- mapFunction: Callback function that must return the value to be rendered in that column for a specific item. The item will be passed to this function as a parameter. | -| hidePaginator: `boolean` | false | If true, paginator will not be displayed. `Optional` | +| Name | Default | Description | +| :------------------------------------------- | :---------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| collectionUrl: `string` | | The URL of the collection resource to be used for the table. `Required` | +| headers: `object` | | Contains the HTTP headers to be sent along with the HTTP requests to the collectionUrl. `Optional` | +| itemsPerPage: `number` | `5` | The amount of items to be displayed per page. Will be used to calculate the `_start` and `_num` query parameters that will be sent to the collection for pagination. `Optional` | +| asyncHeadersHandler: `() => Promise` | | Async function that will be executed right before every HTTP request in order to retrieve dynamic headers. It must return a promise that resolves into an object with the keys and values of the headers. These headers will be merged with the ones indicated in the `headers` prop. `Optional` | +| columns: `Column[]` | `[]` | Array of objects specifying the columns to be displayed in the table. Each Column object has:
- header: Column label to be placed at the table header.
- displayProperty: The name of the property in the items summary to be rendered for this column in the table.
- sortProperty: The name of the property in the items summary to be used for sorting the table.
- onClickItemFunction: Callback function that will be executed when the user clicks an item in that column. The collection item will be passed to this function when executed.
- mapFunction: Callback function that must return the value to be rendered in that column for a specific item. The item will be passed to this function as a parameter. | +| hidePaginator: `boolean` | `false` | If true, paginator will not be displayed. `Optional` | +| mode: `"default" \| "reduced"` | `"default"` | Determines the visual style and layout of the table:
- `"default"`: Standard table size.
- `"reduced"`: More compact table with less spacing, suitable for high-density information. `Optional` | #### HalTable Example