···
cy.get('@input').should('have.focus');
168
+
it('supports nested dialogs', () => {
169
+
const InnerDialog = () => {
170
+
const ref = useRef<HTMLUListElement>(null);
171
+
useDialogFocus(ref);
174
+
<ul ref={ref} role="dialog">
175
+
<li tabIndex={0}>Inner #1</li>
176
+
<li tabIndex={0}>Inner #2</li>
181
+
const OuterDialog = () => {
182
+
const [visible, setVisible] = useState(false);
183
+
const [nested, setNested] = useState(false);
184
+
const ref = useRef<HTMLUListElement>(null);
186
+
useDialogFocus(ref, { disabled: !visible });
190
+
<input type="text" name="text" onFocus={() => setVisible(true)} />
192
+
<ul ref={ref} role="dialog">
193
+
<li tabIndex={0}>Outer #1</li>
194
+
<li tabIndex={0} onFocus={() => setNested(true)}>Outer #2</li>
195
+
{nested && <InnerDialog />}
198
+
<button>after</button>
203
+
mount(<OuterDialog />);
205
+
cy.get('input').first().as('input').focus();
206
+
cy.focused().should('have.property.name', 'text');
208
+
// select first dialog
209
+
cy.realPress('ArrowDown');
210
+
cy.focused().contains('Outer #1');
211
+
cy.realPress('ArrowDown');
212
+
cy.focused().contains('Outer #2');
214
+
// select second dialog
215
+
cy.realPress('ArrowDown');
216
+
cy.focused().contains('Inner #1');
217
+
cy.realPress('ArrowDown');
218
+
cy.focused().contains('Inner #2');
220
+
// remains in inner dialog
221
+
cy.realPress('ArrowDown');
222
+
cy.focused().contains('Inner #1');
224
+
// tabs to last dialog
225
+
cy.realPress(['Shift', 'Tab']);
226
+
cy.focused().contains('Outer #2');
228
+
// arrows bring us back to the inner dialog
229
+
cy.realPress('ArrowUp');
230
+
cy.focused().contains('Inner #2');
232
+
// tab out of dialogs
233
+
cy.realPress('Tab');
234
+
cy.focused().contains('after');
235
+
// we can't reenter the dialogs
236
+
cy.realPress(['Shift', 'Tab']);
237
+
cy.get('@input').should('have.focus');
240
+
it('supports nested dialogs', () => {
241
+
const InnerDialog = () => {
242
+
const ref = useRef<HTMLUListElement>(null);
243
+
useDialogFocus(ref);
246
+
<ul ref={ref} role="dialog">
247
+
<li tabIndex={0}>Inner #1</li>
248
+
<li tabIndex={0}>Inner #2</li>
253
+
const OuterDialog = () => {
254
+
const [visible, setVisible] = useState(false);
255
+
const [nested, setNested] = useState(false);
256
+
const ref = useRef<HTMLUListElement>(null);
258
+
useDialogFocus(ref, { disabled: !visible });
262
+
<input type="text" name="text" onFocus={() => setVisible(true)} />
264
+
<ul ref={ref} role="dialog">
265
+
<li tabIndex={0}>Outer #1</li>
266
+
<li tabIndex={0} onFocus={() => setNested(true)}>Outer #2</li>
267
+
{nested && <InnerDialog />}
270
+
<button>after</button>
275
+
mount(<OuterDialog />);
277
+
cy.get('input').first().as('input').focus();
278
+
cy.focused().should('have.property.name', 'text');
280
+
// select first dialog
281
+
cy.realPress('ArrowDown');
282
+
cy.focused().contains('Outer #1');
283
+
cy.realPress('ArrowDown');
284
+
cy.focused().contains('Outer #2');
286
+
// select second dialog
287
+
cy.realPress('ArrowDown');
288
+
cy.focused().contains('Inner #1');
289
+
cy.realPress('ArrowDown');
290
+
cy.focused().contains('Inner #2');
292
+
// remains in inner dialog
293
+
cy.realPress('ArrowDown');
294
+
cy.focused().contains('Inner #1');
296
+
// tabs to last dialog
297
+
cy.realPress(['Shift', 'Tab']);
298
+
cy.focused().contains('Outer #2');
300
+
// arrows bring us back to the inner dialog
301
+
cy.realPress('ArrowUp');
302
+
cy.focused().contains('Inner #2');
304
+
// tab out of dialogs
305
+
cy.realPress('Tab');
306
+
cy.focused().contains('after');
307
+
// we can't reenter the dialogs
308
+
cy.realPress(['Shift', 'Tab']);
309
+
cy.get('@input').should('have.focus');
312
+
it('allows dialogs in semantic order', () => {
313
+
const Dialog = ({ name }) => {
314
+
const ownerRef = useRef<HTMLInputElement>(null);
315
+
const ref = useRef<HTMLUListElement>(null);
317
+
useDialogFocus(ref, { ownerRef });
321
+
<input type="text" className={name} ref={ownerRef} tabIndex={-1} />
322
+
<ul ref={ref} role="dialog">
323
+
<li tabIndex={0}>{name} #1</li>
324
+
<li tabIndex={0}>{name} #2</li>
332
+
<Dialog name="First" />
333
+
<Dialog name="Second" />
334
+
<button>after</button>
338
+
cy.get('.First').first().as('first');
339
+
cy.get('.Second').first().as('second');
341
+
// focus first dialog
342
+
cy.get('@first').focus();
343
+
cy.get('.First').first().as('first').focus();
345
+
// tabs over both subsequent dialogs
346
+
cy.realPress('Tab');
347
+
cy.focused().contains('after');
349
+
// given a focused first input, doesn't allow the first dialog to be used
350
+
cy.get('@first').focus();
351
+
cy.realPress('ArrowDown');
352
+
cy.get('@first').should('have.focus');
354
+
// given a focused second input, does allow the second dialog to be used
355
+
cy.get('@second').focus();
356
+
cy.realPress('ArrowDown');
357
+
cy.focused().contains('Second #1');