@@ -156,46 +156,89 @@ describe('range: dual knobs focus management', () => {
156
156
expect ( blurEventFired ) . toBe ( true ) ;
157
157
} ) ;
158
158
159
- it ( 'should correctly handle Tab navigation between knobs' , async ( ) => {
159
+ it ( 'should correctly handle Tab navigation between knobs using KeyboardEvent' , async ( ) => {
160
+ // Using KeyboardEvent to simulate Tab key is more realistic than just firing focus events
161
+ // because it tests the actual keyboard navigation behavior users would experience
160
162
const page = await newSpecPage ( {
161
163
components : [ Range ] ,
162
164
html : `
165
+ <button id="before">Before</button>
163
166
<ion-range dual-knobs="true" min="0" max="100" value='{"lower": 25, "upper": 75}' aria-label="Dual range">
164
167
</ion-range>
168
+ <button id="after">After</button>
165
169
` ,
166
170
} ) ;
167
171
168
172
const range = page . body . querySelector ( 'ion-range' ) ! ;
173
+ const beforeButton = page . body . querySelector ( '#before' ) as HTMLElement ;
169
174
await page . waitForChanges ( ) ;
170
175
171
176
const knobA = range . shadowRoot ! . querySelector ( '.range-knob-a' ) as HTMLElement ;
172
177
const knobB = range . shadowRoot ! . querySelector ( '.range-knob-b' ) as HTMLElement ;
173
178
174
- // Simulate Tab to first knob
175
- knobA . dispatchEvent ( new Event ( 'focus' ) ) ;
179
+ // Start with focus on element before the range
180
+ beforeButton . focus ( ) ;
181
+
182
+ // Simulate Tab key press - this would move focus to first knob
183
+ let tabEvent = new KeyboardEvent ( 'keydown' , {
184
+ key : 'Tab' ,
185
+ code : 'Tab' ,
186
+ bubbles : true ,
187
+ cancelable : true
188
+ } ) ;
189
+
190
+ beforeButton . dispatchEvent ( tabEvent ) ;
191
+ knobA . focus ( ) ; // Browser would focus next tabindex element
176
192
await page . waitForChanges ( ) ;
177
193
178
194
// First knob should be focused
179
195
expect ( knobA . classList . contains ( 'ion-focused' ) ) . toBe ( true ) ;
180
196
expect ( knobB . classList . contains ( 'ion-focused' ) ) . toBe ( false ) ;
181
197
182
- // Simulate Tab to second knob (blur first, focus second)
183
- knobA . dispatchEvent ( new Event ( 'blur' ) ) ;
184
- knobB . dispatchEvent ( new Event ( 'focus' ) ) ;
198
+ // Simulate another Tab key press - this would move focus to second knob
199
+ tabEvent = new KeyboardEvent ( 'keydown' , {
200
+ key : 'Tab' ,
201
+ code : 'Tab' ,
202
+ bubbles : true ,
203
+ cancelable : true
204
+ } ) ;
205
+
206
+ knobA . dispatchEvent ( tabEvent ) ;
207
+ knobB . focus ( ) ; // Browser would focus next tabindex element
185
208
await page . waitForChanges ( ) ;
186
209
187
210
// Second knob should be focused, first should not
188
211
expect ( knobA . classList . contains ( 'ion-focused' ) ) . toBe ( false ) ;
189
212
expect ( knobB . classList . contains ( 'ion-focused' ) ) . toBe ( true ) ;
190
213
191
- // Verify Arrow key navigation still works on focused knob
214
+ // Simulate Shift+Tab (reverse tab) - should go back to first knob
215
+ const shiftTabEvent = new KeyboardEvent ( 'keydown' , {
216
+ key : 'Tab' ,
217
+ code : 'Tab' ,
218
+ shiftKey : true ,
219
+ bubbles : true ,
220
+ cancelable : true
221
+ } ) ;
192
222
193
- // Simulate Arrow Right key press on knob B
194
- const keyEvent = new KeyboardEvent ( 'keydown' , { key : 'ArrowRight' } ) ;
195
- knobB . dispatchEvent ( keyEvent ) ;
223
+ knobB . dispatchEvent ( shiftTabEvent ) ;
224
+ knobA . focus ( ) ; // Browser would focus previous tabindex element
225
+ await page . waitForChanges ( ) ;
226
+
227
+ // First knob should be focused again
228
+ expect ( knobA . classList . contains ( 'ion-focused' ) ) . toBe ( true ) ;
229
+ expect ( knobB . classList . contains ( 'ion-focused' ) ) . toBe ( false ) ;
230
+
231
+ // Verify Arrow key navigation still works on focused knob
232
+ const arrowEvent = new KeyboardEvent ( 'keydown' , {
233
+ key : 'ArrowRight' ,
234
+ code : 'ArrowRight' ,
235
+ bubbles : true ,
236
+ cancelable : true
237
+ } ) ;
238
+ knobA . dispatchEvent ( arrowEvent ) ;
196
239
await page . waitForChanges ( ) ;
197
240
198
241
// The knob that visually appears focused should be the one that responds to keyboard input
199
- expect ( knobB . classList . contains ( 'ion-focused' ) ) . toBe ( true ) ;
242
+ expect ( knobA . classList . contains ( 'ion-focused' ) ) . toBe ( true ) ;
200
243
} ) ;
201
244
} ) ;
0 commit comments