Skip to content

Commit

Permalink
fix: your- and program-dimensions list lazy loading (#453)
Browse files Browse the repository at this point in the history
* fix: increment based on correct property `pager.page` not `data.page`

* fix: ensure scrollbox actually scrolls instead of the container

* fix: make scrollbox scrollable without fixed height

* fix: ensure `loading` remains `false` when lazy loading additional items

* fix: return `fetching` from useProgramDataDimensions for lazy loading

* chore: add e2e test for lazy loading your dimensions list

* chore: disable redux logger in cypress test runs

* chore: tweak your dimensions e2e test details

* chore: add e2e test for program dimensions

* refactor: apply suggestions from code review

Co-authored-by: Martin <[email protected]>

* fix: ensure variable names matches function argument

---------

Co-authored-by: Martin <[email protected]>
  • Loading branch information
HendrikThePendric and martinkrulltott authored Nov 1, 2023
1 parent 1371606 commit a7341f8
Show file tree
Hide file tree
Showing 8 changed files with 1,225 additions and 13 deletions.
1,107 changes: 1,107 additions & 0 deletions cypress/fixtures/yourDimensionsLazyLoading.json

Large diffs are not rendered by default.

37 changes: 37 additions & 0 deletions cypress/integration/programDimensions.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
openInputSidebar,
openProgramDimensionsSidebar,
selectEnrollmentWithProgramDimensions,
selectEventWithProgram,
} from '../helpers/dimensions.js'
import { expectAxisToHaveDimension } from '../helpers/layout.js'
import { goToStartPage } from '../helpers/startScreen.js'
Expand Down Expand Up @@ -462,6 +463,42 @@ I.e. Scheduled date works like this:
})
})
})
describe('lazy loading', () => {
it('loads more pages when scrolling down until last one is found', () => {
const getList = () => cy.getBySel('program-dimensions-list')
const getListChildren = () => getList().find('div[role="button"]')
const shouldLoadMoreItems = (nextListLength) => {
cy.getBySel('dimensions-list-load-more').should('exist')
// The loader is appended below the "viewport" so we need another scroll
getList().scrollTo('bottom')
cy.getBySel('dimensions-list-load-more').should('be.visible')
getListChildren().should('have.length', nextListLength)
}

goToStartPage()
selectEventWithProgram({
programName:
'Malaria case diagnosis, treatment and investigation',
})
programDimensionsIsEnabled()
cy.getBySel('program-dimensions-button').click()

getListChildren().should('have.length', 50)

// Subsequent pages should be fetched when scrolling down
getList().scrollTo('bottom')
shouldLoadMoreItems(100)

// This is the last page with only 6 items
getList().scrollTo('bottom')
shouldLoadMoreItems(106)

// Nothing should happen once the end has been reached
getList().scrollTo('bottom')
cy.getBySel('dimensions-list-load-more').should('not.exist')
getListChildren().should('have.length', 106)
})
})
}

describe(['>=39'], 'program dimensions', () => {
Expand Down
71 changes: 66 additions & 5 deletions cypress/integration/yourDimensions.cy.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { DIMENSION_ID_EVENT_DATE } from '../../src/modules/dimensionConstants.js'
import { E2E_PROGRAM, TEST_REL_PE_LAST_YEAR } from '../data/index.js'
import yourDimensionsFixture from '../fixtures/yourDimensionsLazyLoading.json'
import { typeInput } from '../helpers/common.js'
import {
openProgramDimensionsSidebar,
Expand All @@ -24,12 +25,12 @@ import { EXTENDED_TIMEOUT } from '../support/util.js'
const trackerProgram = E2E_PROGRAM
const periodLabel = trackerProgram[DIMENSION_ID_EVENT_DATE]

describe('event', () => {
it('Your dimensions can be used and filtered by', () => {
const dimensionName = 'Facility Type'
const filteredOutItemName = 'MCHP'
const filteredItemName = 'CHC'
describe('Your dimensions', () => {
const dimensionName = 'Facility Type'
const filteredOutItemName = 'MCHP'
const filteredItemName = 'CHC'

const openYourDimensionsPanel = () => {
goToStartPage()

selectEventWithProgram(trackerProgram)
Expand All @@ -43,6 +44,10 @@ describe('event', () => {

// open the your dimensions sidebar
cy.getBySel('main-sidebar').contains('Your dimensions').click()
}

it('can be used and filtered', () => {
openYourDimensionsPanel()

cy.getBySel('your-dimensions-list').contains(
dimensionName,
Expand Down Expand Up @@ -118,4 +123,60 @@ describe('event', () => {
`${getPreviousYearStr()}-12-11`,
])
})
it('list lazy loads', () => {
const getList = () => cy.getBySel('your-dimensions-list')
const getListChildren = () => getList().find('div[role="button"]')
const shouldLoadNextPage = (nextPage, nextListLength) => {
cy.getBySel('dimensions-list-load-more').should('exist')
// The loader is appended below the "viewport" so we need another scroll
getList().scrollTo('bottom')
cy.getBySel('dimensions-list-load-more').should('be.visible')
cy.wait('@getDimensions')
.its('request.query.page')
.should('eq', nextPage.toString())
getListChildren().should('have.length', nextListLength)
}

cy.intercept(
{
pathname: '**/api/*/dimensions**',
query: {
fields: 'id,dimensionType,valueType,optionSet,displayName~rename(name)',
},
},
(req) => req.reply(yourDimensionsFixture[`page_${req.query.page}`])
).as('getDimensions')

openYourDimensionsPanel()

// Page 1 should be fetched without scrolling
cy.wait('@getDimensions').its('request.query.page').should('eq', '1')
getListChildren().should('have.length', 50)

// Subsequent pages should be fetched when scrolling down
getList().scrollTo('bottom')
shouldLoadNextPage(2, 100)

getList().scrollTo('bottom')
shouldLoadNextPage(3, 150)

getList().scrollTo('bottom')
shouldLoadNextPage(4, 200)

// This is the last page with only 10 items
getList().scrollTo('bottom')
shouldLoadNextPage(5, 210)

// Nothing should happen once the end has been reached
getList().scrollTo('bottom')
cy.getBySel('dimensions-list-load-more').should('not.exist')
getListChildren().should('have.length', 210)
cy.get('@getDimensions.all').then((interceptions) => {
const hasRequestedPageSix = interceptions.some(
({ request }) => request.query.page === '6'
)
expect(interceptions).to.have.length(5)
expect(hasRequestedPageSix).to.be.false
})
})
})
5 changes: 4 additions & 1 deletion src/components/MainSidebar/DimensionsList/DimensionsList.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,10 @@ const DimensionsList = ({
/>
))}
{fetching && (
<div className={styles.loadMoreWrap}>
<div
className={styles.loadMoreWrap}
data-test="dimensions-list-load-more"
>
<CircularLoader small />
</div>
)}
Expand Down
2 changes: 2 additions & 0 deletions src/components/MainSidebar/MainSidebar.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
transform: translateX(0);
transition: transform 200ms ease-out;
flex-shrink: 0;
display: flex;
flex-direction: column;
}
.accessory.hidden .accessoryInner {
transform: translateX(-260px);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ const useProgramDataDimensions = ({

return {
loading,
fetching,
error,
dimensions,
setIsListEndVisible,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const query = {

const useYourDimensions = ({ visible, searchTerm, nameProp }) => {
const [isListEndVisible, setIsListEndVisible] = useState(false)
const [dimensions, setDimensions] = useState([])
const [dimensions, setDimensions] = useState(null)
const { data, error, loading, fetching, called, refetch } = useDataQuery(
query,
{
Expand All @@ -53,7 +53,7 @@ const useYourDimensions = ({ visible, searchTerm, nameProp }) => {
})
}
// Reset when filter changes
setDimensions([])
setDimensions(null)
}, [searchTerm, nameProp])

useEffect(() => {
Expand All @@ -63,7 +63,7 @@ const useYourDimensions = ({ visible, searchTerm, nameProp }) => {

if (isListEndVisible && !isLastPage && !fetching) {
refetch({
page: data.page + 1,
page: pager.page + 1,
searchTerm,
nameProp,
})
Expand All @@ -74,17 +74,17 @@ const useYourDimensions = ({ visible, searchTerm, nameProp }) => {
useEffect(() => {
if (data) {
setDimensions((currDimensions) => [
...currDimensions,
...(currDimensions ?? []),
...data.dimensions.dimensions,
])
}
}, [data])

return {
loading,
loading: dimensions ? false : loading,
fetching,
error,
dimensions,
dimensions: dimensions ?? [],
setIsListEndVisible,
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/configureStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ const configureStore = (middleware) => {

if (
!window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ &&
process.env.NODE_ENV !== 'production'
process.env.NODE_ENV !== 'production' &&
!window?.Cypress
) {
middleware.push(createLogger())
}
Expand Down

0 comments on commit a7341f8

Please sign in to comment.