diff --git a/src/client/cypress/e2e/mainPage/boreholeTable.cy.js b/src/client/cypress/e2e/mainPage/boreholeTable.cy.js index 8c6540a79..b39c8026a 100644 --- a/src/client/cypress/e2e/mainPage/boreholeTable.cy.js +++ b/src/client/cypress/e2e/mainPage/boreholeTable.cy.js @@ -1,4 +1,5 @@ import { + checkRowWithText, clickOnRowWithText, showTableAndWaitForData, sortBy, @@ -94,16 +95,37 @@ describe("Borehole editor table tests", () => { unCheckRowWithText("Aaliyah Casper"); cy.contains("1477 selected").should("be.visible"); + // navigate to next page + cy.get('[aria-label="next page"]').scrollIntoView().click(); + waitForTableData(); + cy.contains("1477 selected").should("be.visible"); + + // uncheck another row + unCheckRowWithText("Andres Miller"); + cy.contains("1476 selected").should("be.visible"); + // uncheck all rows cy.get('[data-cy="table-header-checkbox"]').click(); cy.get('[data-cy="boreholes-number-preview"]').should("have.text", "1'626"); - // verify select all rows with filtered data + // check one row + checkRowWithText("Andres Renner"); + cy.contains("1 selected").should("be.visible"); + + // navigate to previous page + cy.get('[aria-label="next page"]').scrollIntoView().click(); + waitForTableData(); + cy.contains("1 selected").should("be.visible"); + + // check all, then uncheck all from page where single selection is not visible + cy.get('[data-cy="table-header-checkbox"]').click(); + cy.get('[data-cy="table-header-checkbox"]').click(); + cy.get('[data-cy="boreholes-number-preview"]').should("have.text", "1'626"); + + // filter data cy.get('[data-cy="show-filter-button"]').click(); cy.contains("Registration").click(); cy.contains("Show all fields").children(".checkbox").click(); - - // input value cy.contains("Created by").next().find("input").type("v_ U%r"); cy.wait("@edit_list"); verifyPaginationText("1–100 of 329"); @@ -112,5 +134,10 @@ describe("Borehole editor table tests", () => { cy.get('[data-cy="table-header-checkbox"]').click(); cy.contains("1'626").should("not.exist"); cy.contains("298 selected").should("be.visible"); // does not select locked rows + + // navigate to next page + cy.get('[aria-label="next page"]').scrollIntoView().click(); + waitForTableData(); + cy.contains("298 selected").should("be.visible"); }); }); diff --git a/src/client/src/pages/overview/boreholeTable/boreholeTable.tsx b/src/client/src/pages/overview/boreholeTable/boreholeTable.tsx index 54fbb2321..4d0c146e0 100644 --- a/src/client/src/pages/overview/boreholeTable/boreholeTable.tsx +++ b/src/client/src/pages/overview/boreholeTable/boreholeTable.tsx @@ -1,4 +1,4 @@ -import React, { FC, useContext, useEffect, useMemo, useRef } from "react"; +import React, { FC, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { useSelector } from "react-redux"; import { useHistory } from "react-router-dom"; @@ -11,6 +11,7 @@ import { GridEventListener, GridHeaderCheckbox, GridPaginationModel, + GridRenderCellParams, GridRowParams, GridRowSelectionModel, GridScrollParams, @@ -23,6 +24,7 @@ import { useDomains } from "../../../api/fetchApiV2"; import { theme } from "../../../AppTheme.ts"; import { useAuth } from "../../../auth/useBdmsAuth.tsx"; import { muiLocales } from "../../../mui.locales.ts"; +import { areArraysEqual } from "../../../utils.ts"; import { OverViewContext } from "../overViewContext.tsx"; import { TablePaginationActions } from "./TablePaginationActions.tsx"; @@ -63,6 +65,19 @@ export const BoreholeTable: FC = ({ const scrollPositionRef = useRef(tableScrollPosition); const user: User = useSelector((state: ReduxRootState) => state.core_user); const userIsEditor = user.data.roles.includes("EDIT"); + const [filteredIds, setFilteredIds] = useState([]); + + // This useEffect makes sure that the table selection model is only updated when the + // filtered_borehole_ids have changed and not whenever the boreholes.data changes, + // which also happens on every pagination event (server side pagination). + useEffect(() => { + if (boreholes?.filtered_borehole_ids && filteredIds) { + if (!areArraysEqual(boreholes.filtered_borehole_ids as number[], filteredIds)) { + setFilteredIds(boreholes.filtered_borehole_ids as number[]); + setSelectionModel([]); + } + } + }, [boreholes.filtered_borehole_ids, filteredIds, setSelectionModel]); const rowCount = useMemo(() => { if (boreholes?.length > 0) { @@ -71,8 +86,8 @@ export const BoreholeTable: FC = ({ return rowCountRef.current; }, [boreholes?.length]); - const renderHeaderCheckbox = useMemo(() => { - return (params: GridColumnHeaderParams) => { + const renderHeaderCheckbox = useCallback( + (params: GridColumnHeaderParams) => { const handleHeaderCheckboxClick = (event: React.ChangeEvent) => { if (event.target.checked) { setSelectionModel(boreholes.filtered_borehole_ids); @@ -90,8 +105,32 @@ export const BoreholeTable: FC = ({ sx={{ m: 1 }} /> ); - }; - }, [boreholes.filtered_borehole_ids, setSelectionModel]); + }, + [boreholes.filtered_borehole_ids, setSelectionModel], + ); + + const renderCellCheckbox = useCallback( + (params: GridRenderCellParams) => { + const handleCheckBoxClick = (event: React.ChangeEvent) => { + const rowId = params.id as number; + if (event.target.checked) { + setSelectionModel([...new Set([...selectionModel, rowId])]); + } else { + setSelectionModel(selectionModel.filter(item => item !== rowId)); + } + }; + return ( + + + + ); + }, + [selectionModel, setSelectionModel], + ); const columns: GridColDef[] = [ { @@ -104,11 +143,7 @@ export const BoreholeTable: FC = ({ disableReorder: true, disableExport: true, renderHeader: renderHeaderCheckbox, - renderCell: params => ( - - - - ), + renderCell: renderCellCheckbox, }, { field: "alternate_name", headerName: t("name"), flex: 1 }, { @@ -339,7 +374,6 @@ export const BoreholeTable: FC = ({ isRowSelectable={(params: GridRowParams) => params.row.lock === null} disableRowSelectionOnClick rowSelectionModel={selectionModel} - onRowSelectionModelChange={setSelectionModel} hideFooterSelectedRowCount sortingMode="server" sortModel={sortModel} diff --git a/src/client/src/utils.ts b/src/client/src/utils.ts index e32c4a5c9..d63d8a6cb 100644 --- a/src/client/src/utils.ts +++ b/src/client/src/utils.ts @@ -2,3 +2,10 @@ export function capitalizeFirstLetter(text: string) { if (!text) return ""; return text.charAt(0).toUpperCase() + text.slice(1); } + +export function areArraysEqual(array1: number[], array2: number[]) { + if (array1?.length !== array2?.length) { + return false; + } + return [...array1].sort((a, b) => a - b).every((value, index) => value === [...array2].sort((a, b) => a - b)[index]); +}