@@ -14,6 +14,7 @@ jest.mock('@react-aria/live-announcer');
14
14
import { act , fireEvent , render , screen , waitFor , within } from '@testing-library/react' ;
15
15
import { announce } from '@react-aria/live-announcer' ;
16
16
import { Button } from '@react-spectrum/button' ;
17
+ import { chain } from '@react-aria/utils' ;
17
18
import { ComboBox , Item , Section } from '../' ;
18
19
import { Provider } from '@react-spectrum/provider' ;
19
20
import React from 'react' ;
@@ -37,6 +38,7 @@ let onInputChange = jest.fn();
37
38
let outerBlur = jest . fn ( ) ;
38
39
let onFocus = jest . fn ( ) ;
39
40
let onBlur = jest . fn ( ) ;
41
+ let onLoadMore = jest . fn ( ) ;
40
42
41
43
let defaultProps = {
42
44
label : 'Test' ,
@@ -193,7 +195,8 @@ let AsyncComboBox = () => {
193
195
inputValue = { list . filterText }
194
196
onInputChange = { list . setFilterText }
195
197
loadingState = { list . loadingState }
196
- onLoadMore = { list . loadMore } >
198
+ onLoadMore = { chain ( list . loadMore , onLoadMore ) }
199
+ onOpenChange = { onOpenChange } >
197
200
{ ( item ) => < Item > { item . name } </ Item > }
198
201
</ ComboBox >
199
202
) ;
@@ -1975,6 +1978,104 @@ describe('ComboBox', function () {
1975
1978
} ) ;
1976
1979
} ) ;
1977
1980
1981
+ describe ( 'async' , function ( ) {
1982
+ let clientHeightSpy ;
1983
+ let scrollHeightSpy ;
1984
+ beforeEach ( ( ) => {
1985
+ // clientHeight is needed for ScrollView's updateSize()
1986
+ clientHeightSpy = jest . spyOn ( window . HTMLElement . prototype , 'clientHeight' , 'get' ) . mockImplementationOnce ( ( ) => 0 ) . mockImplementation ( ( ) => 40 ) ;
1987
+ // scrollHeight is for useVirutalizerItem to mock its getSize()
1988
+ scrollHeightSpy = jest . spyOn ( window . HTMLElement . prototype , 'scrollHeight' , 'get' ) . mockImplementation ( ( ) => 32 ) ;
1989
+ } ) ;
1990
+ afterEach ( ( ) => {
1991
+ clientHeightSpy . mockRestore ( ) ;
1992
+ scrollHeightSpy . mockRestore ( ) ;
1993
+ // This returns this to the value used by all the other tests
1994
+ jest . spyOn ( window . HTMLElement . prototype , 'clientHeight' , 'get' ) . mockImplementation ( ( ) => 1000 ) ;
1995
+ } ) ;
1996
+ it ( 'onLoadMore is called on initial open' , async ( ) => {
1997
+ let { getByRole} = render (
1998
+ < Provider theme = { theme } >
1999
+ < AsyncComboBox />
2000
+ </ Provider >
2001
+ ) ;
2002
+
2003
+ let button = getByRole ( 'button' ) ;
2004
+
2005
+ await waitFor ( ( ) => expect ( load ) . toHaveBeenCalledTimes ( 1 ) ) ;
2006
+ expect ( onOpenChange ) . toHaveBeenCalledTimes ( 0 ) ;
2007
+ expect ( onLoadMore ) . toHaveBeenCalledTimes ( 0 ) ;
2008
+
2009
+ await act ( async ( ) => {
2010
+ triggerPress ( button ) ;
2011
+ jest . runAllTimers ( ) ;
2012
+ } ) ;
2013
+
2014
+ let listbox = getByRole ( 'listbox' ) ;
2015
+ expect ( listbox ) . toBeVisible ( ) ;
2016
+
2017
+ expect ( onOpenChange ) . toHaveBeenCalledTimes ( 1 ) ;
2018
+ expect ( onOpenChange ) . toHaveBeenCalledWith ( true , 'manual' ) ;
2019
+ expect ( onLoadMore ) . toHaveBeenCalledTimes ( 1 ) ;
2020
+ await waitFor ( ( ) => expect ( load ) . toHaveBeenCalledTimes ( 1 ) ) ;
2021
+ } ) ;
2022
+
2023
+ it ( 'onLoadMore is not called on when previously opened' , async ( ) => {
2024
+ let { getByRole} = render (
2025
+ < Provider theme = { theme } >
2026
+ < AsyncComboBox />
2027
+ </ Provider >
2028
+ ) ;
2029
+
2030
+ let button = getByRole ( 'button' ) ;
2031
+ let combobox = getByRole ( 'combobox' ) ;
2032
+ await waitFor ( ( ) => expect ( load ) . toHaveBeenCalledTimes ( 1 ) ) ;
2033
+ expect ( onOpenChange ) . toHaveBeenCalledTimes ( 0 ) ;
2034
+ expect ( onLoadMore ) . toHaveBeenCalledTimes ( 0 ) ;
2035
+
2036
+ // this call and the one below are more correct for how the code should
2037
+ // behave, the inital call would have a height of zero and after that a measureable height
2038
+ clientHeightSpy . mockRestore ( ) ;
2039
+ clientHeightSpy = jest . spyOn ( window . HTMLElement . prototype , 'clientHeight' , 'get' ) . mockImplementationOnce ( ( ) => 0 ) . mockImplementation ( ( ) => 40 ) ;
2040
+ // open menu
2041
+ await act ( async ( ) => {
2042
+ triggerPress ( button ) ;
2043
+ jest . runAllTimers ( ) ;
2044
+ } ) ;
2045
+
2046
+ expect ( onOpenChange ) . toHaveBeenCalledTimes ( 1 ) ;
2047
+ expect ( onOpenChange ) . toHaveBeenCalledWith ( true , 'manual' ) ;
2048
+ expect ( onLoadMore ) . toHaveBeenCalledTimes ( 1 ) ;
2049
+
2050
+ // close menu
2051
+ act ( ( ) => {
2052
+ combobox . blur ( ) ;
2053
+ jest . runAllTimers ( ) ;
2054
+ } ) ;
2055
+
2056
+ expect ( onOpenChange ) . toHaveBeenCalledTimes ( 2 ) ;
2057
+ expect ( onOpenChange ) . toHaveBeenLastCalledWith ( false , undefined ) ;
2058
+ expect ( onLoadMore ) . toHaveBeenCalledTimes ( 1 ) ;
2059
+
2060
+ clientHeightSpy . mockRestore ( ) ;
2061
+ clientHeightSpy = jest . spyOn ( window . HTMLElement . prototype , 'clientHeight' , 'get' ) . mockImplementationOnce ( ( ) => 0 ) . mockImplementation ( ( ) => 40 ) ;
2062
+ // reopen menu
2063
+ await act ( async ( ) => {
2064
+ triggerPress ( button ) ;
2065
+ jest . runAllTimers ( ) ;
2066
+ } ) ;
2067
+
2068
+ expect ( onOpenChange ) . toHaveBeenCalledTimes ( 3 ) ;
2069
+ expect ( onOpenChange ) . toHaveBeenLastCalledWith ( true , 'manual' ) ;
2070
+ // Please test this in storybook.
2071
+ // In virtualizer.tsx the onVisibleRectChange() causes this to be called twice
2072
+ // because the browser limits the popover height via calculatePosition(),
2073
+ // while the test doesn't, causing virtualizer to try to load more
2074
+ expect ( onLoadMore ) . toHaveBeenCalledTimes ( 2 ) ;
2075
+ await waitFor ( ( ) => expect ( load ) . toHaveBeenCalledTimes ( 1 ) ) ;
2076
+ } ) ;
2077
+ } ) ;
2078
+
1978
2079
describe ( 'controlled combobox' , function ( ) {
1979
2080
describe ( 'controlled by both selectedKey and inputValue' , function ( ) {
1980
2081
it ( 'does not update state' , function ( ) {
@@ -3387,7 +3488,7 @@ describe('ComboBox', function () {
3387
3488
</ ComboBox >
3388
3489
</ Provider >
3389
3490
) ;
3390
-
3491
+
3391
3492
let button = getByRole ( 'button' ) ;
3392
3493
3393
3494
act ( ( ) => {
@@ -4472,9 +4573,13 @@ describe('ComboBox', function () {
4472
4573
trayInput . focus ( ) ;
4473
4574
fireEvent . change ( trayInput , { target : { value : 'aard' } } ) ;
4474
4575
jest . advanceTimersByTime ( 500 ) ;
4475
- let trayInputProgress = within ( tray ) . getByRole ( 'progressbar' , { hidden : true } ) ;
4476
- expect ( trayInputProgress ) . toBeTruthy ( ) ;
4477
- expect ( within ( listbox ) . queryByRole ( 'progressbar' ) ) . toBeNull ( ) ;
4576
+ } ) ;
4577
+
4578
+ let trayInputProgress = within ( tray ) . getByRole ( 'progressbar' , { hidden : true } ) ;
4579
+ expect ( trayInputProgress ) . toBeTruthy ( ) ;
4580
+ expect ( within ( listbox ) . queryByRole ( 'progressbar' ) ) . toBeNull ( ) ;
4581
+
4582
+ await act ( async ( ) => {
4478
4583
jest . runAllTimers ( ) ;
4479
4584
} ) ;
4480
4585
0 commit comments