@@ -188,6 +188,111 @@ describe('Picker incremental interactions', () => {
188188 expect ( combobox . getAttribute ( 'aria-activedescendant' ) ) . toBe ( 'Banana' ) ;
189189 } ) ;
190190
191+ it ( 'scrolls the selected option into view when expanding the menu' , ( ) => {
192+ const items = Array . from ( { length : 50 } , ( _ , i ) => `Item ${ i + 1 } ` ) ;
193+
194+ const ITEM_HEIGHT = 20 ;
195+ const LIST_CLIENT_HEIGHT = 200 ;
196+
197+ const originalScrollTo = HTMLElement . prototype . scrollTo ;
198+
199+ HTMLElement . prototype . scrollTo = jest . fn ( ) as any ;
200+
201+ const getBoundingClientRectSpy = jest
202+ . spyOn ( Element . prototype , 'getBoundingClientRect' )
203+ . mockImplementation ( function ( this : HTMLElement ) {
204+ if ( this . getAttribute ( 'role' ) === 'option' ) {
205+ const top = items . indexOf ( this . id ) * ITEM_HEIGHT ;
206+
207+ return {
208+ bottom : top + ITEM_HEIGHT ,
209+ height : ITEM_HEIGHT ,
210+ left : 0 ,
211+ right : 0 ,
212+ toJSON : ( ) => ( { } ) ,
213+ top,
214+ width : 0 ,
215+ x : 0 ,
216+ y : top ,
217+ } ;
218+ }
219+
220+ if ( this . getAttribute ( 'role' ) === 'listbox' ) {
221+ return {
222+ bottom : LIST_CLIENT_HEIGHT ,
223+ height : LIST_CLIENT_HEIGHT ,
224+ left : 0 ,
225+ right : 0 ,
226+ toJSON : ( ) => ( { } ) ,
227+ top : 0 ,
228+ width : 0 ,
229+ x : 0 ,
230+ y : 0 ,
231+ } ;
232+ }
233+
234+ return {
235+ bottom : 0 ,
236+ height : 0 ,
237+ left : 0 ,
238+ right : 0 ,
239+ toJSON : ( ) => ( { } ) ,
240+ top : 0 ,
241+ width : 0 ,
242+ x : 0 ,
243+ y : 0 ,
244+ } ;
245+ } ) ;
246+
247+ const clientHeightSpy = jest
248+ . spyOn ( HTMLElement . prototype , 'clientHeight' , 'get' )
249+ . mockImplementation ( function ( this : HTMLElement ) {
250+ return this . getAttribute ( 'role' ) === 'listbox'
251+ ? LIST_CLIENT_HEIGHT
252+ : 0 ;
253+ } ) ;
254+
255+ const scrollHeightSpy = jest
256+ . spyOn ( HTMLElement . prototype , 'scrollHeight' , 'get' )
257+ . mockImplementation ( function ( this : HTMLElement ) {
258+ return this . getAttribute ( 'role' ) === 'listbox'
259+ ? items . length * ITEM_HEIGHT
260+ : 0 ;
261+ } ) ;
262+
263+ try {
264+ const { getByRole} = render (
265+ < Picker items = { items } selectedKey = "Item 40" >
266+ { ( item ) => < Option key = { item } > { item } </ Option > }
267+ </ Picker >
268+ ) ;
269+
270+ userEvent . click ( getByRole ( 'combobox' ) ) ;
271+
272+ const selectedIndex = items . indexOf ( 'Item 40' ) ;
273+
274+ const expected = Math . max (
275+ 0 ,
276+ Math . min (
277+ selectedIndex * ITEM_HEIGHT -
278+ ( LIST_CLIENT_HEIGHT - ITEM_HEIGHT ) / 2 ,
279+ items . length * ITEM_HEIGHT - LIST_CLIENT_HEIGHT
280+ )
281+ ) ;
282+
283+ expect ( getByRole ( 'listbox' ) . scrollTop ) . toBe ( expected ) ;
284+ }
285+ finally {
286+ getBoundingClientRectSpy . mockRestore ( ) ;
287+
288+ clientHeightSpy . mockRestore ( ) ;
289+
290+ scrollHeightSpy . mockRestore ( ) ;
291+
292+ HTMLElement . prototype . scrollTo = originalScrollTo ;
293+ }
294+ } ) ;
295+
191296 describe ( 'expanded menu' , ( ) => {
192297 it . concurrent . each ( [
193298 [ 'enter' , '[Enter]' ] ,
0 commit comments