@@ -248,6 +248,191 @@ describe('VirtualizedList', () => {
248248 expect ( removeOwner ( component . toJSON ( ) ) ) . toMatchSnapshot ( ) ;
249249 } ) ;
250250
251+ it ( 'sets progressViewOffset on RefreshControl when inverted and layout is known' , async ( ) => {
252+ const ITEM_HEIGHT = 50 ;
253+ const layout = { width : 300 , height : 600 } ;
254+ let component ;
255+ await act ( ( ) => {
256+ component = create (
257+ < VirtualizedList
258+ data = { new Array ( 5 ) . fill ( ) . map ( ( _ , ii ) => ( { id : String ( ii ) } ) ) }
259+ getItem = { ( data , index ) => data [ index ] }
260+ getItemCount = { data => data . length }
261+ getItemLayout = { ( { index} ) => ( {
262+ length : ITEM_HEIGHT ,
263+ offset : index * ITEM_HEIGHT ,
264+ } ) }
265+ inverted = { true }
266+ keyExtractor = { ( item , index ) => item . id }
267+ onRefresh = { jest . fn ( ) }
268+ refreshing = { false }
269+ renderItem = { ( { item} ) => < item value = { item . id } /> }
270+ /> ,
271+ ) ;
272+ } ) ;
273+
274+ const instance = component . getInstance ( ) ;
275+
276+ // Simulate layout to set visibleLength
277+ await act ( ( ) => {
278+ instance . _onLayout ( { nativeEvent : { layout, zoomScale : 1 } } ) ;
279+ } ) ;
280+
281+ // Force re-render after layout
282+ await act ( ( ) => {
283+ instance . forceUpdate ( ) ;
284+ } ) ;
285+
286+ const tree = component . toJSON ( ) ;
287+ // The RefreshControl should have progressViewOffset equal to the visible height
288+ const refreshControl = tree . props . refreshControl ;
289+ expect ( refreshControl . props . progressViewOffset ) . toBe ( layout . height ) ;
290+ } ) ;
291+
292+ it ( 'does not set progressViewOffset when not inverted' , async ( ) => {
293+ const ITEM_HEIGHT = 50 ;
294+ const layout = { width : 300 , height : 600 } ;
295+ let component ;
296+ await act ( ( ) => {
297+ component = create (
298+ < VirtualizedList
299+ data = { new Array ( 5 ) . fill ( ) . map ( ( _ , ii ) => ( { id : String ( ii ) } ) ) }
300+ getItem = { ( data , index ) => data [ index ] }
301+ getItemCount = { data => data . length }
302+ getItemLayout = { ( { index} ) => ( {
303+ length : ITEM_HEIGHT ,
304+ offset : index * ITEM_HEIGHT ,
305+ } ) }
306+ inverted = { false }
307+ keyExtractor = { ( item , index ) => item . id }
308+ onRefresh = { jest . fn ( ) }
309+ refreshing = { false }
310+ renderItem = { ( { item} ) => < item value = { item . id } /> }
311+ /> ,
312+ ) ;
313+ } ) ;
314+
315+ const instance = component . getInstance ( ) ;
316+
317+ await act ( ( ) => {
318+ instance . _onLayout ( { nativeEvent : { layout, zoomScale : 1 } } ) ;
319+ } ) ;
320+
321+ await act ( ( ) => {
322+ instance . forceUpdate ( ) ;
323+ } ) ;
324+
325+ const tree = component . toJSON ( ) ;
326+ const refreshControl = tree . props . refreshControl ;
327+ // progressViewOffset should be undefined when not inverted
328+ expect ( refreshControl . props . progressViewOffset ) . toBeUndefined ( ) ;
329+ } ) ;
330+
331+ it ( 'respects user-provided progressViewOffset when inverted' , async ( ) => {
332+ const ITEM_HEIGHT = 50 ;
333+ const layout = { width : 300 , height : 600 } ;
334+ const customOffset = 100 ;
335+ let component ;
336+ await act ( ( ) => {
337+ component = create (
338+ < VirtualizedList
339+ data = { new Array ( 5 ) . fill ( ) . map ( ( _ , ii ) => ( { id : String ( ii ) } ) ) }
340+ getItem = { ( data , index ) => data [ index ] }
341+ getItemCount = { data => data . length }
342+ getItemLayout = { ( { index} ) => ( {
343+ length : ITEM_HEIGHT ,
344+ offset : index * ITEM_HEIGHT ,
345+ } ) }
346+ inverted = { true }
347+ keyExtractor = { ( item , index ) => item . id }
348+ onRefresh = { jest . fn ( ) }
349+ refreshing = { false }
350+ progressViewOffset = { customOffset }
351+ renderItem = { ( { item} ) => < item value = { item . id } /> }
352+ /> ,
353+ ) ;
354+ } ) ;
355+
356+ const instance = component . getInstance ( ) ;
357+
358+ await act ( ( ) => {
359+ instance . _onLayout ( { nativeEvent : { layout, zoomScale : 1 } } ) ;
360+ } ) ;
361+
362+ await act ( ( ) => {
363+ instance . forceUpdate ( ) ;
364+ } ) ;
365+
366+ const tree = component . toJSON ( ) ;
367+ const refreshControl = tree . props . refreshControl ;
368+ // User-provided progressViewOffset should be respected
369+ expect ( refreshControl . props . progressViewOffset ) . toBe ( customOffset ) ;
370+ } ) ;
371+
372+ it ( 'does not set progressViewOffset before layout when inverted' , async ( ) => {
373+ let component ;
374+ await act ( ( ) => {
375+ component = create (
376+ < VirtualizedList
377+ data = { new Array ( 5 ) . fill ( ) . map ( ( _ , ii ) => ( { id : String ( ii ) } ) ) }
378+ getItem = { ( data , index ) => data [ index ] }
379+ getItemCount = { data => data . length }
380+ getItemLayout = { ( { index} ) => ( { length : 50 , offset : index * 50 } ) }
381+ inverted = { true }
382+ keyExtractor = { ( item , index ) => item . id }
383+ onRefresh = { jest . fn ( ) }
384+ refreshing = { false }
385+ renderItem = { ( { item} ) => < item value = { item . id } /> }
386+ /> ,
387+ ) ;
388+ } ) ;
389+
390+ const tree = component . toJSON ( ) ;
391+ const refreshControl = tree . props . refreshControl ;
392+ // Before layout, visibleLength is 0, so offset should be undefined to avoid flicker
393+ expect ( refreshControl . props . progressViewOffset ) . toBeUndefined ( ) ;
394+ } ) ;
395+
396+ it ( 'clones custom refreshControl with offset when inverted' , async ( ) => {
397+ const ITEM_HEIGHT = 50 ;
398+ const layout = { width : 300 , height : 600 } ;
399+ const RefreshControl = require ( 'react-native' ) . RefreshControl ;
400+ let component ;
401+ await act ( ( ) => {
402+ component = create (
403+ < VirtualizedList
404+ data = { new Array ( 5 ) . fill ( ) . map ( ( _ , ii ) => ( { id : String ( ii ) } ) ) }
405+ getItem = { ( data , index ) => data [ index ] }
406+ getItemCount = { data => data . length }
407+ getItemLayout = { ( { index} ) => ( {
408+ length : ITEM_HEIGHT ,
409+ offset : index * ITEM_HEIGHT ,
410+ } ) }
411+ inverted = { true }
412+ keyExtractor = { ( item , index ) => item . id }
413+ onRefresh = { jest . fn ( ) }
414+ refreshing = { false }
415+ refreshControl = { < RefreshControl refreshing = { false } /> }
416+ renderItem = { ( { item} ) => < item value = { item . id } /> }
417+ /> ,
418+ ) ;
419+ } ) ;
420+
421+ const instance = component . getInstance ( ) ;
422+
423+ await act ( ( ) => {
424+ instance . _onLayout ( { nativeEvent : { layout, zoomScale : 1 } } ) ;
425+ } ) ;
426+
427+ await act ( ( ) => {
428+ instance . forceUpdate ( ) ;
429+ } ) ;
430+
431+ const tree = component . toJSON ( ) ;
432+ const refreshControl = tree . props . refreshControl ;
433+ expect ( refreshControl . props . progressViewOffset ) . toBe ( layout . height ) ;
434+ } ) ;
435+
251436 it ( 'test getItem functionality where data is not an Array' , async ( ) => {
252437 let component ;
253438 await act ( ( ) => {
0 commit comments