@@ -15,16 +15,19 @@ import {
1515 expect ,
1616 fixture ,
1717 html ,
18+ oneEvent ,
1819 waitUntil ,
1920} from '@open-wc/testing' ;
2021import '@spectrum-web-components/field-label/sp-field-label.js' ;
2122import '@spectrum-web-components/menu/sp-menu-divider.js' ;
23+ import type { MenuItem } from '@spectrum-web-components/menu' ;
2224import '@spectrum-web-components/menu/sp-menu-item.js' ;
2325import { Picker } from '@spectrum-web-components/picker' ;
2426import '@spectrum-web-components/picker/sync/sp-picker.js' ;
2527import { spreadProps } from '../../../test/lit-helpers.js' ;
2628import { Popover } from '@spectrum-web-components/popover' ;
2729import { Tray } from '@spectrum-web-components/tray/src/Tray.js' ;
30+ import { spy } from 'sinon' ;
2831
2932describe ( 'Picker, responsive' , ( ) => {
3033 let el : Picker ;
@@ -172,4 +175,139 @@ describe('Picker, responsive', () => {
172175 expect ( popover , 'popover' ) . to . not . be . null ;
173176 } ) ;
174177 } ) ;
178+
179+ describe ( 'touch device detection' , ( ) => {
180+ afterEach ( ( ) => {
181+ // Reset touch device and mobile simulation.
182+ if ( el && el . isTouchDevice ) {
183+ el . isTouchDevice . matches = false ;
184+ }
185+ if ( el && el . isMobile ) {
186+ el . isMobile . matches = false ;
187+ }
188+ } ) ;
189+
190+ it ( 'sets shouldSupportDragAndSelect to false on touch devices' , async ( ) => {
191+ el = await pickerFixture ( ) ;
192+ await elementUpdated ( el ) ;
193+ /**
194+ * This is a hack to set the `isTouchDevice` property to true
195+ * so that we can test the touch device behavior.
196+ */
197+ el . isTouchDevice . matches = true ;
198+ await elementUpdated ( el ) ;
199+
200+ // Open the picker to initialize the menu.
201+ const opened = oneEvent ( el , 'sp-opened' ) ;
202+ el . open = true ;
203+ await opened ;
204+
205+ // Wait for menu to be ready.
206+ await waitUntil (
207+ ( ) => el . optionsMenu && el . optionsMenu . childItems . length > 0 ,
208+ 'Menu should be initialized'
209+ ) ;
210+
211+ // Check that shouldSupportDragAndSelect is false.
212+ expect ( el . optionsMenu . shouldSupportDragAndSelect ) . to . be . false ;
213+ } ) ;
214+
215+ it ( 'sets shouldSupportDragAndSelect to true on desktop devices' , async ( ) => {
216+ el = await pickerFixture ( ) ;
217+ await elementUpdated ( el ) ;
218+
219+ // Ensure we're not on a touch device.
220+ el . isTouchDevice . matches = false ;
221+ await elementUpdated ( el ) ;
222+
223+ // Open the picker to initialize the menu.
224+ const opened = oneEvent ( el , 'sp-opened' ) ;
225+ el . open = true ;
226+ await opened ;
227+
228+ // Wait for menu to be ready.
229+ await waitUntil (
230+ ( ) => el . optionsMenu && el . optionsMenu . childItems . length > 0 ,
231+ 'Menu should be initialized'
232+ ) ;
233+
234+ // Check that shouldSupportDragAndSelect is true.
235+ expect ( el . optionsMenu . shouldSupportDragAndSelect ) . to . be . true ;
236+ } ) ;
237+
238+ it ( 'dispatches change event when menu item is clicked on touch device' , async ( ) => {
239+ el = await pickerFixture ( ) ;
240+ await elementUpdated ( el ) ;
241+
242+ const changeSpy = spy ( ) ;
243+ el . addEventListener ( 'change' , changeSpy ) ;
244+
245+ /**
246+ * This is a hack to set the `isTouchDevice` property to true
247+ * so that we can test the iPad/tablet behavior.
248+ */
249+ el . isTouchDevice . matches = true ;
250+ await elementUpdated ( el ) ;
251+
252+ // Open the picker.
253+ const opened = oneEvent ( el , 'sp-opened' ) ;
254+ el . open = true ;
255+ await opened ;
256+ await elementUpdated ( el ) ;
257+
258+ // Wait for menu to be ready.
259+ await waitUntil (
260+ ( ) => el . optionsMenu && el . optionsMenu . childItems . length > 0 ,
261+ 'Menu should be initialized'
262+ ) ;
263+
264+ // Wait for menu to be fully updated.
265+ await el . optionsMenu . updateComplete ;
266+ await elementUpdated ( el . optionsMenu ) ;
267+
268+ // Verify shouldSupportDragAndSelect is false on touch devices.
269+ expect ( el . optionsMenu . shouldSupportDragAndSelect ) . to . be . false ;
270+
271+ // Get the second menu item (value="option-2") from childItems.
272+ const menuItem = el . optionsMenu . childItems [ 1 ] as MenuItem ;
273+ expect ( menuItem ) . to . not . be . null ;
274+ await elementUpdated ( menuItem ) ;
275+
276+ // Ensure menu is not in scrolling state (which would prevent selection).
277+ el . optionsMenu . isScrolling = false ;
278+
279+ // Click the menu item.
280+ const closed = oneEvent ( el , 'sp-closed' ) ;
281+ menuItem . click ( ) ;
282+ await closed ;
283+
284+ // Verify the change event was dispatched.
285+ expect ( changeSpy . callCount ) . to . equal ( 1 ) ;
286+ expect ( el . value ) . to . equal ( 'option-2' ) ;
287+ } ) ;
288+
289+ it ( 'uses isTouchDevice instead of isMobile for shouldSupportDragAndSelect' , async ( ) => {
290+ el = await pickerFixture ( ) ;
291+ await elementUpdated ( el ) ;
292+
293+ // Simulate iPad: isMobile is false but isTouchDevice is true.
294+ el . isMobile . matches = false ;
295+ el . isTouchDevice . matches = true ;
296+ await elementUpdated ( el ) ;
297+
298+ // Open the picker.
299+ const opened = oneEvent ( el , 'sp-opened' ) ;
300+ el . open = true ;
301+ await opened ;
302+
303+ // Wait for menu to be ready.
304+ await waitUntil (
305+ ( ) => el . optionsMenu && el . optionsMenu . childItems . length > 0 ,
306+ 'Menu should be initialized'
307+ ) ;
308+
309+ // The fix: shouldSupportDragAndSelect should be false based on isTouchDevice.
310+ expect ( el . optionsMenu . shouldSupportDragAndSelect ) . to . be . false ;
311+ } ) ;
312+ } ) ;
175313} ) ;
0 commit comments