Skip to content

Commit

Permalink
Merge pull request #688 from hallieswan/PORTALS-2882
Browse files Browse the repository at this point in the history
PORTALS-2882: initially limit number of cards shown, but then show full limit on each click of 'View More'
  • Loading branch information
jay-hodgson authored Feb 7, 2024
2 parents e85ca2c + c89afc9 commit 9b2f6bb
Show file tree
Hide file tree
Showing 8 changed files with 208 additions and 36 deletions.
2 changes: 1 addition & 1 deletion apps/portals/src/configurations/nf/routesConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const routes: GenericRoute[] = [
outsideContainerClassName: 'home-spacer home-bg-dark',
link: '/Explore/Studies',
props: {
limit,
initialLimit: limit,
columnAliases,
sql: newStudiesSql,
...studyCardConfiguration,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,10 @@ export const organizationDetailsPageConfig: DetailsPageProps = {
sql: studiesSql,
visibleColumnCount: 7,
sqlOperator: ColumnSingleValueFilterOperator.LIKE,
cardConfiguration: studyCardConfiguration,
cardConfiguration: { ...studyCardConfiguration, initialLimit: 2 },
name: 'Funded Studies',
columnAliases,
searchConfiguration,
limit: 2,
},
tableSqlKeys: ['fundingAgency'],
columnName: 'fundingAgency',
Expand All @@ -46,7 +45,7 @@ export const organizationDetailsPageConfig: DetailsPageProps = {
name: 'CardContainerLogic',
props: {
sql: publicationsSql,
limit: 3,
initialLimit: 3,
...publicationsCardConfiguration,
sqlOperator: ColumnSingleValueFilterOperator.LIKE,
},
Expand Down Expand Up @@ -96,7 +95,7 @@ export const organizationDetailsPageConfig: DetailsPageProps = {
props: {
...datasetCardConfiguration,
sql: datasetsSql,
limit: 3,
initialLimit: 3,
},
columnName: 'fundingAgency',
title: 'Datasets',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ export const studiesDetailPage: DetailsPageProps = {
tableSqlKeys: ['studyId'],
props: {
sql: toolStudySql,
limit: 3,
initialLimit: 3,
...toolsCardConfiguration,
},
},
Expand Down
6 changes: 3 additions & 3 deletions apps/portals/src/configurations/nf/synapseConfigs/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ export const toolDetailsPageConfig: DetailsPageProps = {
title: 'Development Publication',
props: {
...publicationsV2CardConfiguration,
limit: 3,
initialLimit: 3,
columnAliases,
sql: developmentPublicationSql,
secondaryLabelLimit: 4,
Expand Down Expand Up @@ -221,7 +221,7 @@ export const toolDetailsPageConfig: DetailsPageProps = {
title: 'Publications',
props: {
...publicationsV2CardConfiguration,
limit: 3,
initialLimit: 3,
columnAliases,
sql: publicationsV2Sql,
},
Expand Down Expand Up @@ -272,7 +272,7 @@ export const toolDetailsPageConfig: DetailsPageProps = {
props: {
sql: `${observationsSql} WHERE observationTime IS NULL`,
type: SynapseConstants.OBSERVATION_CARD,
limit: 3,
initialLimit: 3,
},
title: 'Community Observations',
tableSqlKeys: ['resourceId'],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { render, screen, waitFor } from '@testing-library/react'
import { render, screen, waitFor, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import React from 'react'
import { SynapseConstants } from '../../utils'
import CardContainer, { CardContainerProps } from './CardContainer'
import { QueryVisualizationWrapper } from '../QueryVisualizationWrapper/QueryVisualizationWrapper'
import { createWrapper } from '../../testutils/TestingLibraryUtils'
import { QueryBundleRequest } from '@sage-bionetworks/synapse-types'
import syn16787123Json from '../../mocks/query/syn16787123'
import {
DEFAULT_PAGE_SIZE,
GENERIC_CARD,
MEDIUM_USER_CARD,
STUDY,
} from '../../utils/SynapseConstants'
import { mockTableEntity } from '../../mocks/entity/mockTableEntity'
import mockUserCardTableQueryResultBundle from '../../mocks/query/mockUserCardTableQueryResultBundle'
import { server } from '../../mocks/msw/server'
import { mockUserProfileData } from '../../mocks/user/mock_user_profile'
Expand All @@ -19,12 +21,22 @@ import { getHandlersForTableQuery } from '../../mocks/msw/handlers/tableQueryHan
import { cloneDeep } from 'lodash-es'
import { QueryContextConsumer } from '../QueryContext'
import { InfiniteQueryContextType } from '../QueryContext/QueryContext'
import SynapseClient from '../../synapse-client'

const sql = 'SELECT * FROM syn16787123'
const tableId = 'syn16787123'
const sql = `SELECT * FROM ${tableId}`

jest
.spyOn(SynapseClient, 'getEntity')
.mockResolvedValue({ ...mockTableEntity, id: tableId })
const getQueryTableAsyncJobResultsSpy = jest.spyOn(
SynapseClient,
'getQueryTableAsyncJobResults',
)

const lastQueryRequest: QueryBundleRequest = {
concreteType: 'org.sagebionetworks.repo.model.table.QueryBundleRequest',
entityId: 'syn16787123',
entityId: tableId,
partMask: 255,
query: {
sql,
Expand All @@ -38,13 +50,33 @@ dataWithOnePage.queryCount =
dataWithOnePage.queryResult!.queryResults.rows.length
dataWithOnePage.queryResult!.nextPageToken = undefined

const dataWithMultiplePages = cloneDeep(syn16787123Json)
dataWithMultiplePages.queryCount =
dataWithMultiplePages.queryResult!.queryResults.rows.length + 1
dataWithMultiplePages.queryResult!.nextPageToken = {
const dataWithMultiplePagesFirstPage = cloneDeep(syn16787123Json)
dataWithMultiplePagesFirstPage.queryCount =
dataWithMultiplePagesFirstPage.queryResult!.queryResults.rows.length * 2 + 1
dataWithMultiplePagesFirstPage.queryResult!.nextPageToken = {
token: 'abcd',
}

const dataWithMultiplePagesSecondPage = cloneDeep(
dataWithMultiplePagesFirstPage,
)
dataWithMultiplePagesSecondPage.queryResult!.nextPageToken!.token = 'efgh'
dataWithMultiplePagesSecondPage.queryResult!.queryResults.rows = [
...dataWithMultiplePagesFirstPage.queryResult!.queryResults.rows.map(row => ({
...row,
rowId: row.rowId! + DEFAULT_PAGE_SIZE,
})),
]

const createDataWithOnePageWithQueryCount = (queryCount: number) => {
const page = cloneDeep(dataWithOnePage)
page.queryCount = queryCount
page.queryResult!.queryResults.rows = cloneDeep(
dataWithOnePage.queryResult!.queryResults.rows.slice(0, queryCount),
)
return page
}

const unitDescription = 'study'

let capturedQueryContext: InfiniteQueryContextType | undefined
Expand All @@ -69,19 +101,36 @@ const renderComponent = (props: CardContainerProps) => {
)
}

const setUp = (props: CardContainerProps) => {
const user = userEvent.setup()
const component = renderComponent(props)
return { component, user }
}

const waitForCardCount = async (cardText: string, nCards: number) => {
await waitFor(() => {
expect(
within(screen.getByRole('list')).getAllByText(cardText),
).toHaveLength(nCards)
})
}

describe('CardContainer tests', () => {
beforeAll(() => server.listen())
beforeEach(() => {
server.use(...getHandlersForTableQuery(dataWithMultiplePages))
jest.clearAllMocks()
server.use(...getHandlersForTableQuery(dataWithMultiplePagesFirstPage))
})
afterEach(() => server.restoreHandlers())
afterAll(() => server.close())

const type = SynapseConstants.STUDY
// cast the data to ignore ts warning
const props: CardContainerProps = {
unitDescription,
type,
type: GENERIC_CARD,
genericCardSchema: {
type: STUDY,
title: 'title',
},
}

it('renders without crashing', () => {
Expand All @@ -103,8 +152,11 @@ describe('CardContainer tests', () => {
await screen.findByText(title)
})

it('handleViewMore works', async () => {
renderComponent(props)
it('ViewMore button shows full pages on each click when initialLimit is not set', async () => {
const { user } = setUp(props)
await waitForCardCount(STUDY, DEFAULT_PAGE_SIZE)
expect(getQueryTableAsyncJobResultsSpy).toHaveBeenCalledTimes(1)

// go through calling handle view more
const viewMoreButton = await screen.findByRole('button', {
name: 'View More',
Expand All @@ -115,25 +167,128 @@ describe('CardContainer tests', () => {
'appendNextPageToResults',
)

await userEvent.click(viewMoreButton)
server.use(...getHandlersForTableQuery(dataWithMultiplePagesSecondPage))
await user.click(viewMoreButton)

// Verify that the next page was appended
await waitFor(() => {
expect(appendNextPageSpy).toHaveBeenCalled()
})
await waitForCardCount(STUDY, DEFAULT_PAGE_SIZE * 2)
expect(getQueryTableAsyncJobResultsSpy).toHaveBeenCalledTimes(2)
})

it('show ViewMore does not render when hasNextPage is false', () => {
it('ViewMore does not render when hasNextPage is false and no initialLimit is not set', async () => {
server.use(...getHandlersForTableQuery(dataWithOnePage))
renderComponent(props)
await waitForCardCount(STUDY, DEFAULT_PAGE_SIZE)
expect(
screen.queryByRole('button', { name: 'View More' }),
).not.toBeInTheDocument()
})

it('ViewMore button shows rest of first page on first click and full page on next click when initialLimit is set', async () => {
const initialLimit = 3
const queryCount = dataWithMultiplePagesFirstPage.queryCount!
expect(initialLimit).toBeLessThan(queryCount)
expect(DEFAULT_PAGE_SIZE * 2).toBeLessThan(queryCount)

const { user } = setUp({
...props,
initialLimit: initialLimit,
unitDescription: undefined,
})

const viewMoreButton = await screen.findByRole('button', {
name: 'View More',
})

// initial load - limited to initialLimit
await waitForCardCount(STUDY, initialLimit)
expect(viewMoreButton).toBeInTheDocument()
expect(getQueryTableAsyncJobResultsSpy).toHaveBeenCalledTimes(1) // initial call to get data

server.use(...getHandlersForTableQuery(dataWithMultiplePagesSecondPage))
await user.click(viewMoreButton)

// first showMore click - full first page
await waitForCardCount(STUDY, DEFAULT_PAGE_SIZE)
expect(viewMoreButton).toBeInTheDocument()
expect(getQueryTableAsyncJobResultsSpy).toHaveBeenCalledTimes(1) // no call needed - data already loaded

await user.click(viewMoreButton)

// second showMore click - full second page
await waitForCardCount(STUDY, DEFAULT_PAGE_SIZE * 2)
expect(viewMoreButton).toBeInTheDocument()
expect(getQueryTableAsyncJobResultsSpy).toHaveBeenCalledTimes(2) // call to get second page
})

it('ViewMore button does not render when initialLimit is greater than or equal to queryCount', async () => {
const initialLimit = 4
const queryCount = 3
expect(initialLimit).toBeGreaterThanOrEqual(queryCount)

server.use(
...getHandlersForTableQuery(
createDataWithOnePageWithQueryCount(queryCount),
),
)

setUp({
...props,
initialLimit: initialLimit,
unitDescription: undefined,
})

// initial load - limited to queryCount
await waitForCardCount(STUDY, queryCount)
const viewMoreButton = screen.queryByRole('button', {
name: 'View More',
})
expect(viewMoreButton).not.toBeInTheDocument()
expect(getQueryTableAsyncJobResultsSpy).toHaveBeenCalledTimes(1) // initial call to get data
})

it('ViewMore button renders when hasNextPage is false, but initialLimit is less than queryCount', async () => {
const initialLimit = 3
const queryCount = 5
expect(initialLimit).toBeLessThan(queryCount)
server.use(
...getHandlersForTableQuery(
createDataWithOnePageWithQueryCount(queryCount),
),
)

const { user } = setUp({
...props,
initialLimit: initialLimit,
unitDescription: undefined,
})

// initial load - limited to initialLimit
const viewMoreButton = await screen.findByRole('button', {
name: 'View More',
})
expect(viewMoreButton).toBeInTheDocument()
await waitForCardCount(STUDY, initialLimit)
expect(getQueryTableAsyncJobResultsSpy).toHaveBeenCalledTimes(1) // initial call to get data

await user.click(viewMoreButton)

// first showMore click - remaining results
await waitForCardCount(STUDY, queryCount)
expect(viewMoreButton).not.toBeInTheDocument()
expect(getQueryTableAsyncJobResultsSpy).toHaveBeenCalledTimes(1) // no call needed - data already loaded
})

it('Does not filter null IDs when rendering user cards (PORTALS-2430)', async () => {
server.use(...getHandlersForTableQuery(mockUserCardTableQueryResultBundle))
renderComponent({ ...props, type: MEDIUM_USER_CARD })
renderComponent({
...props,
type: MEDIUM_USER_CARD,
genericCardSchema: undefined,
})

// Since the first user in the mock data has a user ID, their profile information will be fetched, ignoring the table data.
await screen.findByText(
Expand Down
Loading

0 comments on commit 9b2f6bb

Please sign in to comment.