From eca948c8d17d33e8cbe9fd77976423936e4dcb8f Mon Sep 17 00:00:00 2001 From: Shayan Khan Date: Fri, 29 Sep 2023 13:50:54 -0500 Subject: [PATCH] bug/WP-52 Jobs View Infinite Scroll Fix (#865) * fix for infinite scroll not working, fixed logic for getting more jobs * update getJobSearchList tapis command to use skip instead of startAfter * update to scroll value calculation logic * added testing for scroll functionality --- client/src/components/Jobs/Jobs.jsx | 43 +++++----- client/src/components/Jobs/Jobs.test.js | 33 ++++++- .../InfiniteScrollTable.jsx | 85 ++++++++++--------- .../InfiniteScrollTable.scss | 5 ++ server/portal/apps/workspace/api/views.py | 2 +- 5 files changed, 107 insertions(+), 61 deletions(-) diff --git a/client/src/components/Jobs/Jobs.jsx b/client/src/components/Jobs/Jobs.jsx index bbef04a96..1945d1bf7 100644 --- a/client/src/components/Jobs/Jobs.jsx +++ b/client/src/components/Jobs/Jobs.jsx @@ -75,11 +75,14 @@ function JobsView({ const infiniteScrollCallback = useCallback(() => { // TODOv3: dropV2Jobs const dispatchType = version === 'v3' ? 'GET_JOBS' : 'GET_V2_JOBS'; - dispatch({ - type: dispatchType, - params: { offset: jobs.length, queryString: query.query_string || '' }, - }); - }, [dispatch, jobs, query.query_string]); + + if (!isJobLoading) { + dispatch({ + type: dispatchType, + params: { offset: jobs.length, queryString: query.query_string || '' }, + }); + } + }, [dispatch, jobs, query.query_string, isJobLoading]); const jobDetailLink = useCallback( ({ @@ -217,22 +220,20 @@ function JobsView({ disabled={isJobLoading || isNotificationLoading} /> )} -
- - {noDataText} - - } - getRowProps={rowProps} - columnMemoProps={[version]} /* TODOv3: dropV2Jobs. */ - /> -
+ + {noDataText} + + } + getRowProps={rowProps} + columnMemoProps={[version]} /* TODOv3: dropV2Jobs. */ + /> ); } diff --git a/client/src/components/Jobs/Jobs.test.js b/client/src/components/Jobs/Jobs.test.js index ea30d1934..05dcb53f6 100644 --- a/client/src/components/Jobs/Jobs.test.js +++ b/client/src/components/Jobs/Jobs.test.js @@ -1,5 +1,5 @@ import React from 'react'; -import { render } from '@testing-library/react'; +import { fireEvent, render, waitFor } from '@testing-library/react'; import Jobs from './Jobs'; import { createMemoryHistory } from 'history'; import { default as jobsList } from './Jobs.fixture'; @@ -135,4 +135,35 @@ describe('Jobs View', () => { const { getByText } = renderJobsComponent(store, history); expect(getByText(/unable to retrieve your jobs/)).toBeDefined(); }); + + it('should dispatch another get jobs event on scroll with proper offset', async () => { + const store = mockStore({ + notifications, + jobs: { ...jobs, list: jobsList }, + workbench: { ...workbench, config: { hideDataFiles: false } }, + apps: { + appIcons: {}, + }, + }); + + const { container } = render( + + + + + + ); + + const scrollContainer = container.querySelector('.table-container'); + + fireEvent.scroll(scrollContainer, { target: { scrollTop: 1 } }); + + expect(store.getActions()).toEqual([ + { type: 'GET_JOBS', params: { offset: 0, queryString: '' } }, + { + type: 'GET_JOBS', + params: { offset: jobsList.length, queryString: '' }, + }, + ]); + }); }); diff --git a/client/src/components/_common/InfiniteScrollTable/InfiniteScrollTable.jsx b/client/src/components/_common/InfiniteScrollTable/InfiniteScrollTable.jsx index f8496eeb8..a3bbe85e5 100644 --- a/client/src/components/_common/InfiniteScrollTable/InfiniteScrollTable.jsx +++ b/client/src/components/_common/InfiniteScrollTable/InfiniteScrollTable.jsx @@ -62,51 +62,60 @@ const InfiniteScrollTable = ({ useTable({ columns, data }); const onScroll = ({ target }) => { - const bottom = - target.scrollHeight - target.scrollTop === target.clientHeight; + const scrollbarHeight = target.offsetHeight - target.clientHeight; + const clientRectHeight = target.getBoundingClientRect().height; + const clientCalcHeight = clientRectHeight - scrollbarHeight; + const difference = Math.floor(target.scrollHeight - target.scrollTop); + + const bottom = difference <= clientCalcHeight; + if (bottom && target.scrollTop > 0) { onInfiniteScroll(tableData.length); } }; return ( - - - {headerGroups.map((headerGroup) => ( - - {headerGroup.headers.map((column) => ( - - ))} - - ))} - - - {rows.map((row) => { - prepareRow(row); - return ( - - {row.cells.map((cell) => { - return ( - - ); - })} +
+
{column.render('Header')}
- {cell.render('Cell')} -
+ + {headerGroups.map((headerGroup) => ( + + {headerGroup.headers.map((column) => ( + + ))} - ); - })} - - - -
{column.render('Header')}
+ ))} + + + {rows.map((row) => { + prepareRow(row); + return ( + + {row.cells.map((cell) => { + return ( + + {cell.render('Cell')} + + ); + })} + + ); + })} + + + + + ); }; diff --git a/client/src/components/_common/InfiniteScrollTable/InfiniteScrollTable.scss b/client/src/components/_common/InfiniteScrollTable/InfiniteScrollTable.scss index 55b98d918..4361f7586 100644 --- a/client/src/components/_common/InfiniteScrollTable/InfiniteScrollTable.scss +++ b/client/src/components/_common/InfiniteScrollTable/InfiniteScrollTable.scss @@ -79,3 +79,8 @@ width: 100%; } } + +.table-container { + height: 100%; + overflow: scroll; +} diff --git a/server/portal/apps/workspace/api/views.py b/server/portal/apps/workspace/api/views.py index 9a94db829..f76b36402 100644 --- a/server/portal/apps/workspace/api/views.py +++ b/server/portal/apps/workspace/api/views.py @@ -179,7 +179,7 @@ def listing(self, client, request): data = client.jobs.getJobSearchList( limit=limit, - startAfter=offset, + skip=offset, orderBy='lastUpdated(desc),name(asc)', _tapis_query_parameters={'tags.contains': f'portalName: {portal_name}'}, select='allAttributes'