From 71f6e74ad5250b8a6e3b9bea816cb69cbd435fbb Mon Sep 17 00:00:00 2001 From: Lisa-Ann B Date: Wed, 7 Aug 2024 18:11:41 -0400 Subject: [PATCH 01/19] Resolve server side pagination to work with exposed states - #1572 --- .gitmodules | 2 +- .../custom/TableResultsEntities.jsx | 11 +- src/components/custom/TableResultsFiles.jsx | 1 + .../custom/layout/CustomClearSearchBox.js | 4 +- src/components/custom/search/ResultsBlock.jsx | 27 +++-- .../custom/search/ResultsPerPage.jsx | 18 +-- src/config/search/entities.js | 7 +- src/config/search/files.js | 4 + src/config/search/metadata.js | 4 + src/context/TableResultsContext.jsx | 31 +++-- src/pages/search/entities.js | 19 ++- src/pages/search/files.js | 12 +- src/pages/search/index.js | 1 + src/pages/search/metadata.js | 10 +- src/public/js/main-plugins.js | 112 +++++++++--------- src/search-ui | 2 +- 16 files changed, 146 insertions(+), 119 deletions(-) diff --git a/.gitmodules b/.gitmodules index c97c17cb7..bceaea38f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "src/search-ui"] path = src/search-ui - url = https://github.com/dbmi-pitt/search-ui + url = git@github.com:dbmi-pitt/search-ui.git branch = main \ No newline at end of file diff --git a/src/components/custom/TableResultsEntities.jsx b/src/components/custom/TableResultsEntities.jsx index b358353b0..64668570b 100644 --- a/src/components/custom/TableResultsEntities.jsx +++ b/src/components/custom/TableResultsEntities.jsx @@ -12,15 +12,15 @@ import ClipboardCopy from "../ClipboardCopy"; import BulkExport, {handleCheckbox} from "./BulkExport"; import {getOptions} from "./search/ResultsPerPage"; import ResultsBlock from "./search/ResultsBlock"; -import {TableResultsProvider} from "../../context/TableResultsContext"; +import {TableResultsProvider} from "@/context/TableResultsContext"; import SenNetPopover from "../SenNetPopover"; import {Chip} from "@mui/material"; import MoreHorizIcon from "@mui/icons-material/MoreHoriz"; import AppModal from "../AppModal"; -import {parseJson} from "../../lib/services"; -import {COLS_ORDER_KEY} from "../../config/config"; +import {parseJson} from "@/lib/services"; +import {COLS_ORDER_KEY} from "@/config/config"; -function TableResultsEntities({children, filters, onRowClicked, currentColumns, forData = false, rowFn, inModal = false}) { +function TableResultsEntities({children, filters, onRowClicked, currentColumns, forData = false, rowFn, inModal = false, rawResponse}) { let hasMultipleEntityTypes = checkMultipleFilterType(filters); const {isLoggedIn, cache, getGroupName} = useContext(AppContext) @@ -268,7 +268,7 @@ function TableResultsEntities({children, filters, onRowClicked, currentColumns, } // Prepare opsDict - getOptions(children.length) + getOptions(rawResponse?.record_count || children.length) const getSearchContext = () => `entities.${tableContext.current}` @@ -276,6 +276,7 @@ function TableResultsEntities({children, filters, onRowClicked, currentColumns, <> /> { - - }, []) + const [hiddenColumns, setHiddenColumns] = useState(null) return ( <> @@ -42,10 +42,14 @@ function ResultsBlock({getTableColumns, disableRowClick, tableClassName, default {rows.length > 0 && } - + + { { currentColumns.current.current = cols @@ -63,9 +67,12 @@ function ResultsBlock({getTableColumns, disableRowClick, tableClassName, default onChangePage={handlePageChange} onChangeRowsPerPage={handleRowsPerPageChange} onRowClicked={!disableRowClick ? handleOnRowClicked : undefined} - paginationPerPage={resultsPerPage} + paginationPerPage={pageSize} paginationRowsPerPageOptions={Object.keys(opsDict)} pagination + paginationServer + paginationDefaultPage={pageNumber} + paginationTotalRows={rawResponse.record_count} progressPending={isLoading} progressComponent={} />} diff --git a/src/components/custom/search/ResultsPerPage.jsx b/src/components/custom/search/ResultsPerPage.jsx index 9f2bf6813..d98669536 100644 --- a/src/components/custom/search/ResultsPerPage.jsx +++ b/src/components/custom/search/ResultsPerPage.jsx @@ -1,19 +1,12 @@ -import {getCheckAll, handleCheckAll} from "../BulkExport"; -import $ from "jquery"; -import {RESULTS_PER_PAGE} from "../../../config/config"; +import {handleCheckAll} from "../BulkExport"; +import {RESULTS_PER_PAGE} from "@/config/config"; import React, {useState} from "react"; import Select from 'react-select' import {clearDownloadSizeLabel} from "../TableResultsFiles"; -export const handlePagingInfo = (page, resultsPerPage, totalRows) => { +export const handleTableControls = () => { try { handleCheckAll() - const $pgInfo = $('.sui-paging-info') - let from = (page - 1) * resultsPerPage + 1 - let to = page * resultsPerPage - to = to > totalRows ? totalRows : to - let txt = totalRows > 0 ? `${from} - ${to}` : '0 - 0' - $pgInfo.find('strong').eq(0).html(`${txt}`) clearDownloadSizeLabel() } catch (e) { console.error(e) @@ -35,7 +28,7 @@ export const getOptions = (totalRows) => { return result } -export function ResultsPerPage({resultsPerPage, setResultsPerPage, totalRows}) { +export function ResultsPerPage({resultsPerPage, setResultsPerPage, totalRows, updateTablePagination}) { const getDefaultValue = () => getOptions(totalRows).length > 1 ? getOptions(totalRows)[1] : getOptions(totalRows)[0] const [value, setValue] = useState(getDefaultValue()) @@ -43,7 +36,8 @@ export function ResultsPerPage({resultsPerPage, setResultsPerPage, totalRows}) { const handleChange = (e) => { setResultsPerPage(e.value) setValue(e) - handlePagingInfo(1, e.value, totalRows) + updateTablePagination(1, e.value) + handleTableControls() } const getCurrentValue = () => { diff --git a/src/config/search/entities.js b/src/config/search/entities.js index ee4fd9c15..e95a0ba86 100644 --- a/src/config/search/entities.js +++ b/src/config/search/entities.js @@ -240,9 +240,14 @@ export const SEARCH_ENTITIES = { 'title', 'description', ], + pagination: { + from: 1, + size: 20 + } }, initialState: { - resultsPerPage: 10000, + current: 1, + resultsPerPage: 20, sortList: [{ field: "last_modified_timestamp", direction: "desc" diff --git a/src/config/search/files.js b/src/config/search/files.js index db1abb32c..1b7a79d9e 100644 --- a/src/config/search/files.js +++ b/src/config/search/files.js @@ -75,6 +75,10 @@ export const SEARCH_FILES = { dataset_type: {type: 'value'} }, source_fields: sourceItems, + pagination: { + from: 1, + size: 20 + } }, initialState: { resultsPerPage: 10000, diff --git a/src/config/search/metadata.js b/src/config/search/metadata.js index eef23d018..efcf833dc 100644 --- a/src/config/search/metadata.js +++ b/src/config/search/metadata.js @@ -469,6 +469,10 @@ export const SEARCH_METADATA = { 'title', 'description', ], + pagination: { + from: 1, + size: 20 + } }, initialState: { resultsPerPage: 10000, diff --git a/src/context/TableResultsContext.jsx b/src/context/TableResultsContext.jsx index 64ce9481e..dd993c6bc 100644 --- a/src/context/TableResultsContext.jsx +++ b/src/context/TableResultsContext.jsx @@ -3,15 +3,16 @@ import $ from "jquery"; import AppContext from "./AppContext"; import {RESULTS_PER_PAGE} from "../config/config"; import {createTheme} from "react-data-table-component"; -import {handlePagingInfo} from "../components/custom/search/ResultsPerPage"; -import log from 'loglevel' import SearchUIContext from "search-ui/components/core/SearchUIContext"; +import {handleTableControls} from "@/components/custom/search/ResultsPerPage"; const TableResultsContext = createContext({}) -export const TableResultsProvider = ({ columnsRef, children, getHotLink, rows, filters, onRowClicked, forData = false, raw, getId, inModal = false }) => { +export const TableResultsProvider = ({ index, columnsRef, children, getHotLink, rows, filters, onRowClicked, forData = false, raw, getId, inModal = false }) => { const {isLoggedIn} = useContext(AppContext) - const {isLoading} = useContext(SearchUIContext) + const {isLoading, rawResponse, pageNumber, + setPageNumber, pageSize, setPageSize, sort, setSort} = useContext(SearchUIContext) + const hasLoaded = useRef(false) let pageData = [] const [resultsPerPage, setResultsPerPage] = useState(RESULTS_PER_PAGE[1]) @@ -50,21 +51,24 @@ export const TableResultsProvider = ({ columnsRef, children, getHotLink, rows, f } } + const updateTablePagination = (page, currentRowsPerPage) => { + setPageSize(currentRowsPerPage) + setPageNumber(page) + } + const handlePageChange = (page, totalRows) => { - handlePagingInfo(page, resultsPerPage, totalRows) + updateTablePagination(page, resultsPerPage) + handleTableControls() } const handleRowsPerPageChange = (currentRowsPerPage, currentPage) => { - handlePagingInfo(currentPage, currentRowsPerPage, rows.length) setResultsPerPage(currentRowsPerPage) + updateTablePagination(1, currentRowsPerPage) + handleTableControls() } useEffect(() => { hasLoaded.current = true setNoResultsMessage(getNoDataMessage()) - if (!forData) { - log.debug('Results', rows) - handlePageChange(1, rows.length) - } }, []) useEffect(() => { @@ -73,7 +77,7 @@ export const TableResultsProvider = ({ columnsRef, children, getHotLink, rows, f const getTableData = () => { pageData = [] - handlePageChange(1, rows.length) + //handlePageChange(1, rows.length) if (rows.length) { for (let row of rows) { let _row = row.props ? row.props.result : row @@ -93,6 +97,9 @@ export const TableResultsProvider = ({ columnsRef, children, getHotLink, rows, f pageData, resultsPerPage, setResultsPerPage, + setPageSize, sort, setSort, + pageSize, + pageNumber, currentColumns, getId, rows, @@ -102,6 +109,8 @@ export const TableResultsProvider = ({ columnsRef, children, getHotLink, rows, f handlePageChange, noResultsMessage, isLoading, + rawResponse, + updateTablePagination }}> { children } diff --git a/src/pages/search/entities.js b/src/pages/search/entities.js index b501bec2f..ae2fa2d46 100644 --- a/src/pages/search/entities.js +++ b/src/pages/search/entities.js @@ -2,16 +2,16 @@ import dynamic from "next/dynamic"; import React, {useContext} from "react"; import {ErrorBoundary, SearchBox} from "@elastic/react-search-ui"; import {Layout} from "@elastic/react-search-ui-views"; -import {APP_TITLE} from "../../config/config"; -import {SEARCH_ENTITIES} from "../../config/search/entities" +import {APP_TITLE} from "@/config/config"; +import {SEARCH_ENTITIES} from "@/config/search/entities" import CustomClearSearchBox from "../../components/custom/layout/CustomClearSearchBox"; import Button from 'react-bootstrap/Button'; import Form from 'react-bootstrap/Form'; import InputGroup from 'react-bootstrap/InputGroup'; import AppContext from "../../context/AppContext"; import SelectedFilters from "../../components/custom/layout/SelectedFilters"; -import {getUBKGFullName} from "../../components/custom/js/functions"; -import {TableResultsEntities} from "../../components/custom/TableResultsEntities"; +import {getUBKGFullName} from "@/components/custom/js/functions"; +import {TableResultsEntities} from "@/components/custom/TableResultsEntities"; const AppFooter = dynamic(() => import("../../components/custom/layout/AppFooter")) const AppNavbar = dynamic(() => import("../../components/custom/layout/AppNavbar")) @@ -21,12 +21,11 @@ const FacetsContent = dynamic(() => import("../../components/custom/search/Facet const Header = dynamic(() => import("../../components/custom/layout/Header")) const InvalidToken = dynamic(() => import("../../components/custom/layout/InvalidToken")) const SearchDropdown = dynamic(() => import("../../components/custom/search/SearchDropdown")) -const SearchUIContainer = dynamic(() => import("search-ui/components/core/SearchUIContainer")) +const SearchUIContainer = dynamic(() => import("@/search-ui/components/core/SearchUIContainer")) const SelectedFacets = dynamic(() => import("../../components/custom/search/SelectedFacets")) const SenNetBanner = dynamic(() => import("../../components/SenNetBanner")) const Spinner = dynamic(() => import("../../components/custom/Spinner")) - function SearchEntities() { const { _t, @@ -39,6 +38,10 @@ function SearchEntities() { hasAuthenticationCookie } = useContext(AppContext); + function handleSearchFormSubmit(event, onSubmit) { + onSubmit(event) + } + // Define here because we need auth state from AppContext SEARCH_ENTITIES['searchQuery']['conditionalFacets']['rui_location'] = ({filters}) => { return hasAuthenticationCookie() && !isUnauthorized() && @@ -50,10 +53,6 @@ function SearchEntities() { filters.some((filter) => filter.field === "entity_type" && filter.values.includes("Dataset")) } - function handleSearchFormSubmit(event, onSubmit) { - onSubmit(event) - } - if (validatingToken() || isAuthorizing()) { return } else if (hasInvalidToken()) { diff --git a/src/pages/search/files.js b/src/pages/search/files.js index 8e6b1ea96..ab0d8179b 100644 --- a/src/pages/search/files.js +++ b/src/pages/search/files.js @@ -2,16 +2,16 @@ import dynamic from "next/dynamic"; import React, {useContext} from "react"; import {ErrorBoundary, SearchBox} from "@elastic/react-search-ui"; import {Layout} from "@elastic/react-search-ui-views"; -import {TableResultsFiles} from '../../components/custom/TableResultsFiles' -import {APP_TITLE} from "../../config/config"; -import {SEARCH_FILES} from "../../config/search/files" +import {TableResultsFiles} from '@/components/custom/TableResultsFiles' +import {APP_TITLE} from "@/config/config"; +import {SEARCH_FILES} from "@/config/search/files" import CustomClearSearchBox from "../../components/custom/layout/CustomClearSearchBox"; import Button from 'react-bootstrap/Button'; import Form from 'react-bootstrap/Form'; import InputGroup from 'react-bootstrap/InputGroup'; import AppContext from "../../context/AppContext"; import SelectedFilters from "../../components/custom/layout/SelectedFilters"; -import {getUBKGFullName} from "../../components/custom/js/functions"; +import {getUBKGFullName} from "@/components/custom/js/functions"; const AppFooter = dynamic(() => import("../../components/custom/layout/AppFooter")) const AppNavbar = dynamic(() => import("../../components/custom/layout/AppNavbar")) @@ -19,7 +19,7 @@ const BodyContent = dynamic(() => import("../../components/custom/search/BodyCon const FacetsContent = dynamic(() => import("../../components/custom/search/FacetsContent")) const Header = dynamic(() => import("../../components/custom/layout/Header")) const SearchDropdown = dynamic(() => import("../../components/custom/search/SearchDropdown")) -const SearchUIContainer = dynamic(() => import("search-ui/components/core/SearchUIContainer")) +const SearchUIContainer = dynamic(() => import("@/search-ui/components/core/SearchUIContainer")) const SelectedFacets = dynamic(() => import("../../components/custom/search/SelectedFacets")) const Spinner = dynamic(() => import("../../components/custom/Spinner")) @@ -38,6 +38,7 @@ function SearchFiles() { onSubmit(event) } + if (isAuthorizing()) { return } else { @@ -49,7 +50,6 @@ function SearchFiles() { return ( <>
-