Skip to content

Commit

Permalink
⚗️ Add infinite scroll in results table
Browse files Browse the repository at this point in the history
  • Loading branch information
baptisteArno committed Jan 4, 2022
1 parent 8ddf608 commit 72454c0
Show file tree
Hide file tree
Showing 9 changed files with 200 additions and 98 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const LoadingRows = ({ totalColumns }: LoadingRowsProps) => {
border="1px"
as="td"
borderColor="gray.200"
flex="0"
width="50px"
>
<Checkbox isDisabled />
</Flex>
Expand All @@ -30,7 +30,7 @@ export const LoadingRows = ({ totalColumns }: LoadingRowsProps) => {
border="1px"
as="td"
borderColor="gray.200"
flex="1"
width="180px"
align="center"
>
<Skeleton height="5px" w="full" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { Box, Checkbox, Flex } from '@chakra-ui/react'
import { Answer, Result } from 'bot-engine'
import { useTypebot } from 'contexts/TypebotContext'
import React, { useEffect } from 'react'
import React, { useEffect, useRef } from 'react'
import { Hooks, useFlexLayout, useRowSelect, useTable } from 'react-table'
import { parseSubmissionsColumns } from 'services/publicTypebot'
import { parseDateToReadable } from 'services/results'
Expand All @@ -13,12 +13,16 @@ const defaultCellWidth = 180

type SubmissionsTableProps = {
results?: (Result & { answers: Answer[] })[]
hasMore?: boolean
onNewSelection: (selection: string[]) => void
onScrollToBottom: () => void
}

export const SubmissionsTable = ({
results,
hasMore,
onNewSelection,
onScrollToBottom,
}: SubmissionsTableProps) => {
const { publishedTypebot } = useTypebot()
const columns: any = React.useMemo(
Expand All @@ -34,8 +38,11 @@ export const SubmissionsTable = ({
{}
),
})),
[results]
// eslint-disable-next-line react-hooks/exhaustive-deps
[results?.length]
)
const bottomElement = useRef<HTMLDivElement | null>(null)
const tableWrapper = useRef<HTMLDivElement | null>(null)

const {
getTableProps,
Expand All @@ -57,10 +64,35 @@ export const SubmissionsTable = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedFlatRows])

useEffect(() => {
if (!bottomElement.current) return
const options: IntersectionObserverInit = {
root: tableWrapper.current,
threshold: 0,
}
const observer = new IntersectionObserver(handleObserver, options)
if (bottomElement.current) observer.observe(bottomElement.current)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [bottomElement.current])

const handleObserver = (entities: any[]) => {
const target = entities[0]
if (target.isIntersecting) onScrollToBottom()
}

return (
<Flex overflowX="scroll" maxW="full" className="table-wrapper" rounded="md">
<Box as="table" rounded="md" {...getTableProps()} w="full">
<Box as="thead">
<Flex
overflow="scroll"
maxW="full"
maxH="full"
className="table-wrapper"
rounded="md"
data-testid="table-wrapper"
pb="20"
ref={tableWrapper}
>
<Box as="table" rounded="md" {...getTableProps()} w="full" h="full">
<Box as="thead" pos="sticky" top="0" zIndex={2}>
{headerGroups.map((headerGroup: any) => {
return (
<Flex as="tr" {...headerGroup.getHeaderGroupProps()}>
Expand All @@ -75,6 +107,7 @@ export const SubmissionsTable = ({
color="gray.500"
fontWeight="normal"
textAlign="left"
bgColor={'white'}
{...column.getHeaderProps()}
style={{
width: idx === 0 ? '50px' : `${defaultCellWidth}px`,
Expand All @@ -90,13 +123,17 @@ export const SubmissionsTable = ({
</Box>

<Box as="tbody" {...getTableBodyProps()}>
{results === undefined && (
<LoadingRows totalColumns={columns.length} />
)}
{rows.map((row: any) => {
{rows.map((row: any, idx: number) => {
prepareRow(row)
return (
<Flex as="tr" {...row.getRowProps()}>
<Flex
as="tr"
{...row.getRowProps()}
ref={(ref) => {
if (results && idx === results.length - 10)
bottomElement.current = ref
}}
>
{row.cells.map((cell: any, idx: number) => {
return (
<Flex
Expand All @@ -105,6 +142,7 @@ export const SubmissionsTable = ({
border="1px"
as="td"
borderColor="gray.200"
bgColor={'white'}
{...cell.getCellProps()}
style={{
width: idx === 0 ? '50px' : `${defaultCellWidth}px`,
Expand All @@ -117,6 +155,9 @@ export const SubmissionsTable = ({
</Flex>
)
})}
{(results === undefined || hasMore === true) && (
<LoadingRows totalColumns={columns.length} />
)}
</Box>
</Box>
</Flex>
Expand Down
48 changes: 14 additions & 34 deletions apps/builder/cypress/plugins/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,49 +85,29 @@ const createTypebots = async () => {
const createResults = () => {
return prisma.result.createMany({
data: [
{
typebotId: 'typebot1',
},
{
typebotId: 'typebot1',
},
{
id: 'result1',
typebotId: 'typebot2',
},
{
id: 'result2',
typebotId: 'typebot2',
},
{
id: 'result3',
typebotId: 'typebot2',
},
...Array.from(Array(200)).map((_, idx) => {
const today = new Date()
return {
id: `result${idx}`,
typebotId: 'typebot2',
createdAt: new Date(
today.setTime(today.getTime() + 1000 * 60 * 60 * 24 * idx)
),
}
}),
],
})
}

const createAnswers = () => {
return prisma.answer.createMany({
data: [
{
resultId: 'result1',
content: 'content 1',
stepId: 'step1',
blockId: 'block1',
},
{
resultId: 'result2',
content: 'content 2',
...Array.from(Array(200)).map((_, idx) => ({
resultId: `result${idx}`,
content: `content${idx}`,
stepId: 'step1',
blockId: 'block1',
},
{
resultId: 'result3',
content: 'content 3',
stepId: 'step1',
blockId: 'block1',
},
})),
],
})
}
Expand Down
32 changes: 24 additions & 8 deletions apps/builder/cypress/tests/results.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
describe('ResultsPage', () => {
before(() => {
cy.intercept({ url: '/api/typebots/typebot2/results?', method: 'GET' }).as(
cy.intercept({ url: '/api/typebots/typebot2/results*', method: 'GET' }).as(
'getResults'
)
cy.intercept({ url: '/api/typebots/typebot2/results*', method: 'GET' }).as(
'getResults'
)
})
Expand All @@ -13,19 +16,32 @@ describe('ResultsPage', () => {
cy.signIn('test2@gmail.com')
cy.visit('/typebots/typebot2/results')
cy.wait('@getResults')
cy.findByText('content 2').should('exist')
cy.findByText('content 3').should('exist')
cy.findByText('content198').should('exist')
cy.findByText('content197').should('exist')
cy.findAllByRole('checkbox').eq(2).check({ force: true })
cy.findAllByRole('checkbox').eq(3).check({ force: true })
cy.findByRole('button', { name: 'Delete 2' }).click()
cy.findByRole('button', { name: 'Delete 2' }).click({ force: true })
cy.findByRole('button', { name: 'Delete' }).click()
cy.findByText('content 2').should('not.exist')
cy.findByText('content 3').should('not.exist')
cy.findByText('content198').should('not.exist')
cy.findByText('content197').should('not.exist')
cy.wait(200)
cy.findAllByRole('checkbox').first().check({ force: true })
cy.findByRole('button', { name: 'Delete 198' }).click({ force: true })
cy.findByRole('button', { name: 'Delete' }).click()
cy.findAllByRole('row').should('have.length', 1)
})

it.only('submissions table should have infinite scroll', () => {
it('submissions table should have infinite scroll', () => {
cy.signIn('test2@gmail.com')
cy.visit('/typebots/typebot2/results')
cy.wait('@getResults')
cy.findByText('content50').should('not.exist')
cy.findByText('content199').should('exist')
cy.findByTestId('table-wrapper').scrollTo('bottom')
cy.findByText('content149').should('exist')
cy.findByTestId('table-wrapper').scrollTo('bottom')
cy.findByText('content99').should('exist')
cy.findByTestId('table-wrapper').scrollTo('bottom')
cy.findByText('content50').should('exist')
cy.findByText('content0').should('exist')
})
})
11 changes: 10 additions & 1 deletion apps/builder/layouts/results/ResultsContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,18 @@ export const ResultsContent = () => {
status: 'error',
})

const { stats } = useStats({
const { stats, mutate } = useStats({
typebotId: typebot?.id,
onError: (err) => toast({ title: err.name, description: err.message }),
})

const handleDeletedResults = (total: number) => {
if (!stats) return
mutate({
stats: { ...stats, totalStarts: stats.totalStarts - total },
})
}

return (
<Flex h="full" w="full">
<Flex
Expand Down Expand Up @@ -66,6 +74,7 @@ export const ResultsContent = () => {
) : (
<SubmissionsContent
typebotId={typebot.id}
onDeleteResults={handleDeletedResults}
totalResults={stats?.totalStarts ?? 0}
/>
))}
Expand Down
Loading

0 comments on commit 72454c0

Please sign in to comment.