Skip to content

Commit f851fe1

Browse files
committed
Add tests describing current sticky header realization behavior
See react-native-community/discussions-and-proposals#335 for extra context. A VirtualizedList may have sticky headers, forwarded on to ScrollView. These sticky headers are exempt from virtualization once realized for the first time. This change documents the behavior of keeping sticky header cells realized after scrolling away. This scenario performs the same behavior as creating an internal "realization window" for sticky headers with a single cell window size. Generalizing the concept of realization windows should be shaped to support the existing sticky header scenario.
1 parent b08362a commit f851fe1

File tree

2 files changed

+779
-0
lines changed

2 files changed

+779
-0
lines changed

Libraries/Lists/__tests__/VirtualizedList-test.js

+159
Original file line numberDiff line numberDiff line change
@@ -504,4 +504,163 @@ describe('VirtualizedList', () => {
504504
'scrollToIndex out of range: requested index 3 is out of 0 to 2',
505505
);
506506
});
507+
508+
it('forwards correct stickyHeaderIndices when all in initial render window', () => {
509+
const items = Array(10)
510+
.fill()
511+
.map((_, i) => (i % 3 === 0 ? {key: i, sticky: true} : {key: i}));
512+
const stickyIndices = items
513+
.filter(item => item.sticky)
514+
.map(item => item.key);
515+
516+
const ITEM_HEIGHT = 10;
517+
518+
const component = ReactTestRenderer.create(
519+
<VirtualizedList
520+
data={items}
521+
stickyHeaderIndices={stickyIndices}
522+
initialNumToRender={10}
523+
renderItem={({item}) => <item value={item.key} sticky={item.sticky} />}
524+
getItem={(data, index) => data[index]}
525+
getItemCount={data => data.length}
526+
getItemLayout={(_, index) => ({
527+
length: ITEM_HEIGHT,
528+
offset: ITEM_HEIGHT * index,
529+
index,
530+
})}
531+
/>,
532+
);
533+
534+
expect(component).toMatchSnapshot();
535+
});
536+
537+
it('forwards correct stickyHeaderIndices when partially in initial render window', () => {
538+
const items = Array(10)
539+
.fill()
540+
.map((_, i) => (i % 3 === 0 ? {key: i, sticky: true} : {key: i}));
541+
const stickyIndices = items
542+
.filter(item => item.sticky)
543+
.map(item => item.key);
544+
545+
const ITEM_HEIGHT = 10;
546+
547+
const component = ReactTestRenderer.create(
548+
<VirtualizedList
549+
data={items}
550+
stickyHeaderIndices={stickyIndices}
551+
initialNumToRender={5}
552+
renderItem={({item}) => <item value={item.key} sticky={item.sticky} />}
553+
getItem={(data, index) => data[index]}
554+
getItemCount={data => data.length}
555+
getItemLayout={(_, index) => ({
556+
length: ITEM_HEIGHT,
557+
offset: ITEM_HEIGHT * index,
558+
index,
559+
})}
560+
/>,
561+
);
562+
563+
expect(component).toMatchSnapshot();
564+
});
565+
566+
it('realizes sticky headers in viewport on batched render', () => {
567+
const items = Array(10)
568+
.fill()
569+
.map((_, i) => (i % 3 === 0 ? {key: i, sticky: true} : {key: i}));
570+
const stickyIndices = items
571+
.filter(item => item.sticky)
572+
.map(item => item.key);
573+
574+
const ITEM_HEIGHT = 10;
575+
576+
const virtualizedListProps = {
577+
data: items,
578+
stickyHeaderIndices: stickyIndices,
579+
initialNumToRender: 1,
580+
windowSize: 1,
581+
renderItem: ({item}) => <item value={item.key} sticky={item.sticky} />,
582+
getItem: (data, index) => data[index],
583+
getItemCount: data => data.length,
584+
getItemLayout: (_, index) => ({
585+
length: ITEM_HEIGHT,
586+
offset: ITEM_HEIGHT * index,
587+
index,
588+
}),
589+
};
590+
591+
let component;
592+
593+
ReactTestRenderer.act(() => {
594+
component = ReactTestRenderer.create(
595+
<VirtualizedList key={'A'} {...virtualizedListProps} />,
596+
);
597+
});
598+
599+
ReactTestRenderer.act(() => {
600+
component
601+
.getInstance()
602+
._onLayout({nativeEvent: {layout: {width: 10, height: 50}}});
603+
component.getInstance()._onContentSizeChange(10, 100);
604+
jest.runAllTimers();
605+
});
606+
607+
expect(component).toMatchSnapshot();
608+
});
609+
610+
it('keeps sticky headers realized after scrolled out of viewport', () => {
611+
const items = Array(20)
612+
.fill()
613+
.map((_, i) =>
614+
i % 3 === 0 ? {key: i, sticky: true} : {key: i, sticky: false},
615+
);
616+
const stickyIndices = items
617+
.filter(item => item.sticky)
618+
.map(item => item.key);
619+
620+
const ITEM_HEIGHT = 10;
621+
622+
const virtualizedListProps = {
623+
data: items,
624+
stickyHeaderIndices: stickyIndices,
625+
initialNumToRender: 1,
626+
windowSize: 1,
627+
renderItem: ({item}) => <item value={item.key} sticky={item.sticky} />,
628+
getItem: (data, index) => data[index],
629+
getItemCount: data => data.length,
630+
getItemLayout: (_, index) => ({
631+
length: ITEM_HEIGHT,
632+
offset: ITEM_HEIGHT * index,
633+
index,
634+
}),
635+
};
636+
637+
let component;
638+
639+
ReactTestRenderer.act(() => {
640+
component = ReactTestRenderer.create(
641+
<VirtualizedList key={'A'} {...virtualizedListProps} />,
642+
);
643+
});
644+
645+
ReactTestRenderer.act(() => {
646+
component
647+
.getInstance()
648+
._onLayout({nativeEvent: {layout: {width: 10, height: 50}}});
649+
component.getInstance()._onContentSizeChange(10, 200);
650+
jest.runAllTimers();
651+
});
652+
653+
ReactTestRenderer.act(() => {
654+
component.getInstance()._onScroll({
655+
nativeEvent: {
656+
contentOffset: {x: 0, y: 150},
657+
contentSize: {width: 10, height: 200},
658+
layoutMeasurement: {width: 10, height: 50},
659+
},
660+
});
661+
jest.runAllTimers();
662+
});
663+
664+
expect(component).toMatchSnapshot();
665+
});
507666
});

0 commit comments

Comments
 (0)