Skip to content

Commit

Permalink
fix(): no jump with float item height and itmi
Browse files Browse the repository at this point in the history
Fixes #446

itmi = initialTopMostItemIndex prop
  • Loading branch information
petyosi committed Sep 8, 2021
1 parent 10b7031 commit 9127413
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 7 deletions.
3 changes: 2 additions & 1 deletion e2e/auto-prepend-items.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useCallback, useState } from 'react'
import { useCallback, useState } from 'react'
import * as React from 'react'
import { Virtuoso } from '../src/'
import faker from 'faker'

Expand Down
16 changes: 16 additions & 0 deletions e2e/test-case-446.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
describe('list with a long last item', () => {
beforeEach(async () => {
await page.goto('http://localhost:1234/test-case-446')
await page.waitForSelector('#test-root')
await page.waitForTimeout(300)
})

// the float height was causing a load of item 9
it('starts from item with index 10', async () => {
const firstItemIndex = await page.evaluate(() => {
const listContainer = document.querySelector('[data-test-id=virtuoso-item-list]')!
return (listContainer as HTMLElement).firstElementChild!.getAttribute('data-item-index')
})
expect(firstItemIndex).toBe('10')
})
})
44 changes: 44 additions & 0 deletions e2e/test-case-446.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import * as React from 'react'
import { useState } from 'react'
import { Virtuoso } from '../src'

const itemContent = (index: number, note: { content: string }) => (
<div style={{ height: index % 2 ? 70.3 : 30.6, background: 'white' }}>{note.content}</div>
)
// globalThis['VIRTUOSO_LOG_LEVEL'] = 0

const notes: Array<ReturnType<typeof note>> = []
function note(index: number) {
return {
index: index + 1,
content: `Note ${index}`,
}
}

export const getNote = (index: number) => {
if (!notes[index]) {
notes[index] = note(index)
}

return notes[index]
}

const generateNotes = (length: number, startIndex = 0) => {
return Array.from({ length }).map((_, i) => getNote(i + startIndex))
}

const START_INDEX = 10000
const INITIAL_ITEM_COUNT = 20

export default function App() {
const [topMostItemIndex, setTopMostItemIndex] = useState(10)
const [notes] = useState(() => generateNotes(INITIAL_ITEM_COUNT, START_INDEX))

return (
<div>
<button onClick={() => setTopMostItemIndex(() => 3)}>Topmost index = 3</button>
<button onClick={() => setTopMostItemIndex(() => 7)}>Topmost index = 7</button>
<Virtuoso data={notes} itemContent={itemContent} initialTopMostItemIndex={topMostItemIndex} style={{ height: 300 }} />
</div>
)
}
2 changes: 1 addition & 1 deletion src/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ export const Items = React.memo(function VirtuosoItems({ showTopList = false }:

return createElement(
ListComponent,
{ ref, style: containerStyle },
{ ref, style: containerStyle, 'data-test-id': showTopList ? 'virtuoso-top-item-list' : 'virtuoso-item-list' },
(showTopList ? listState.topItems : listState.items).map((item) => {
const index = item.originalIndex!
const key = computeItemKey(index + firstItemIndex, item.data)
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export type TopItemListProps = Pick<ComponentPropsWithRef<'div'>, 'style' | 'chi
/**
* Passed to the Components.List custom component
*/
export type ListProps = Pick<ComponentPropsWithRef<'div'>, 'style' | 'children' | 'ref'>
export type ListProps = Pick<ComponentPropsWithRef<'div'>, 'style' | 'children' | 'ref'> & { 'data-test-id': string }

/**
* Passed to the Components.List custom component
Expand Down
9 changes: 6 additions & 3 deletions src/upwardScrollFixSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { listStateSystem } from './listStateSystem'
import { sizeSystem } from './sizeSystem'
import { stateFlagsSystem } from './stateFlagsSystem'
import { ListItem } from './interfaces'
import { loggerSystem, LogLevel } from './loggerSystem'

/**
* Fixes upward scrolling by calculating and compensation from changed item heights, using scrollBy.
Expand All @@ -14,6 +15,7 @@ export const upwardScrollFixSystem = u.system(
{ isScrolling },
{ listState },
{ beforeUnshiftWith, sizes },
{ log },
]) => {
const deviationOffset = u.streamFromEmitter(
u.pipe(
Expand Down Expand Up @@ -53,11 +55,12 @@ export const upwardScrollFixSystem = u.system(
[0, []] as [number, ListItem<any>[]]
),
u.filter(([amount]) => amount !== 0),
u.withLatestFrom(scrollTop, scrollDirection, scrollingInProgress),
u.withLatestFrom(scrollTop, scrollDirection, scrollingInProgress, log),
u.filter(([, scrollTop, scrollDirection, scrollingInProgress]) => {
return !scrollingInProgress && scrollTop !== 0 && scrollDirection === UP
}),
u.map(([[amount]]) => {
u.map(([[amount], , , , log]) => {
log('Upwards crolling compensation', { amount }, LogLevel.DEBUG)
return amount
})
)
Expand Down Expand Up @@ -103,5 +106,5 @@ export const upwardScrollFixSystem = u.system(

return { deviation }
},
u.tup(domIOSystem, stateFlagsSystem, listStateSystem, sizeSystem)
u.tup(domIOSystem, stateFlagsSystem, listStateSystem, sizeSystem, loggerSystem)
)
2 changes: 1 addition & 1 deletion src/utils/correctItemSize.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export function correctItemSize(el: HTMLElement, dimension: 'height' | 'width') {
return el.getBoundingClientRect()[dimension]
return Math.round(el.getBoundingClientRect()[dimension])
}

0 comments on commit 9127413

Please sign in to comment.