]> git.proxmox.com Git - extjs.git/blame - extjs/classic/classic/test/specs/util/FocusableContainer.js
add extjs 6.0.1 sources
[extjs.git] / extjs / classic / classic / test / specs / util / FocusableContainer.js
CommitLineData
6527f429
DM
1describe("Ext.util.FocusableContainer", function() {\r
2 var forward = true,\r
3 backward = false,\r
4 autoId = 0,\r
5 focusAndWait = jasmine.focusAndWait,\r
6 pressTab = jasmine.pressTabKey,\r
7 pressArrow = jasmine.pressArrowKey,\r
8 waitForFocus = jasmine.waitForFocus,\r
9 expectFocused = jasmine.expectFocused,\r
10 expectAria = jasmine.expectAriaAttr,\r
11 expectNoAria = jasmine.expectNoAriaAttr,\r
12 Container, fc, fcEl;\r
13 \r
14 function makeButton(config) {\r
15 config = config || {};\r
16 \r
17 Ext.applyIf(config, {\r
18 // It is easier to troubleshoot test failures when error message\r
19 // says "expected fooBtn-xyz to be afterBtn-xyz", rather than\r
20 // "expected button-xyz to be button-zyx"; since these\r
21 // messages often display element id's, it's easier to set component\r
22 // id here than guesstimate later.\r
23 id: (config.text || 'button') + '-' + ++autoId,\r
24 renderTo: Ext.getBody()\r
25 });\r
26 \r
27 var btn = new Ext.button.Button(config);\r
28 \r
29 return btn;\r
30 }\r
31 \r
32 function makeContainer(config) {\r
33 var items, i, len, item;\r
34 \r
35 config = Ext.apply({\r
36 id: 'focusableContainer-' + ++autoId,\r
37 width: 1000,\r
38 height: 50,\r
39 style: {\r
40 'background-color': 'green'\r
41 },\r
42 layout: 'hbox',\r
43 defaults: {\r
44 xtype: 'button'\r
45 },\r
46 renderTo: Ext.getBody()\r
47 }, config);\r
48 \r
49 items = config.items;\r
50 \r
51 if (items) {\r
52 for (i = 0, len = items.length; i < len; i++) {\r
53 item = items[i];\r
54 \r
55 if (item.xtype === 'button') {\r
56 item.id = (item.text || 'button') + '-' + ++autoId;\r
57 };\r
58 }\r
59 }\r
60 \r
61 fc = new Container(config);\r
62 fcEl = fc.getFocusableContainerEl();\r
63 \r
64 return fc;\r
65 }\r
66 \r
67 beforeEach(function() {\r
68 Container = Ext.define('spec.FocusableContainer', {\r
69 extend: 'Ext.container.Container',\r
70 \r
71 mixins: [\r
72 'Ext.util.FocusableContainer'\r
73 ]\r
74 });\r
75 });\r
76 \r
77 afterEach(function() {\r
78 if (fc) {\r
79 fc.destroy();\r
80 }\r
81 \r
82 Ext.undefine('spec.FocusableContainer');\r
83 Container = fc = fcEl = null;\r
84 });\r
85 \r
86 describe("init/destroy", function() {\r
87 var proto, first, second, third;\r
88 \r
89 function setupContainer(config) {\r
90 config = Ext.apply({\r
91 activeChildTabIndex: 42,\r
92 items: [{\r
93 xtype: 'button',\r
94 itemId: 'first',\r
95 text: 'first'\r
96 }, {\r
97 xtype: 'button',\r
98 itemId: 'second',\r
99 text: 'second',\r
100 disabled: true\r
101 }, {\r
102 xtype: 'button',\r
103 itemId: 'third',\r
104 text: 'third',\r
105 tabIndex: -10\r
106 }]\r
107 }, config);\r
108 \r
109 makeContainer(config);\r
110 \r
111 first = fc.down('#first');\r
112 second = fc.down('#second');\r
113 third = fc.down('#third');\r
114 \r
115 return fc;\r
116 }\r
117 \r
118 beforeEach(function() {\r
119 proto = Container.prototype;\r
120 \r
121 spyOn(proto, 'doInitFocusableContainer').andCallThrough();\r
122 spyOn(proto, 'doDestroyFocusableContainer').andCallThrough();\r
123 });\r
124 \r
125 afterEach(function() {\r
126 proto = first = second = third = null;\r
127 });\r
128 \r
129 describe("enableFocusableContainer === true (default)", function() {\r
130 describe("enableFocusableContainer stays true", function() {\r
131 beforeEach(function() {\r
132 setupContainer();\r
133 });\r
134 \r
135 it("should call init", function() {\r
136 expect(fc.doInitFocusableContainer).toHaveBeenCalled();\r
137 });\r
138 \r
139 it("should place tabindex on container el", function() {\r
140 expectAria(fc, 'tabIndex', '42');\r
141 });\r
142 \r
143 it("should create keyNav", function() {\r
144 expect(fc.focusableKeyNav).toBeDefined();\r
145 });\r
146 \r
147 it("should set tabindex on the first child", function() {\r
148 expectAria(first, 'tabIndex', '-1');\r
149 });\r
150 \r
151 it("should NOT set tabindex on the second child", function() {\r
152 expectNoAria(second, 'tabIndex');\r
153 });\r
154 \r
155 it("should set tabindex on the third child", function() {\r
156 expectAria(third, 'tabIndex', '-1');\r
157 });\r
158 \r
159 it("should call destroy", function() {\r
160 fc.destroy();\r
161 \r
162 expect(fc.doDestroyFocusableContainer).toHaveBeenCalled();\r
163 });\r
164 });\r
165 \r
166 describe("enableFocusableContainer stays true with no enabled children", function() {\r
167 beforeEach(function() {\r
168 setupContainer({ renderTo: undefined });\r
169 \r
170 first.disable();\r
171 third.disable();\r
172 \r
173 fc.render(Ext.getBody());\r
174 });\r
175 \r
176 it("should call init", function() {\r
177 expect(fc.doInitFocusableContainer).toHaveBeenCalled();\r
178 });\r
179 \r
180 it("should NOT set tabindex on container el", function() {\r
181 expectNoAria(fc, 'tabIndex');\r
182 });\r
183 \r
184 it("should create keyNav", function() {\r
185 expect(fc.focusableKeyNav).toBeDefined();\r
186 });\r
187 \r
188 it("should not set tabindex on the first child", function() {\r
189 expectNoAria(first, 'tabIndex');\r
190 });\r
191 \r
192 it("should not set tabindex on the second child", function() {\r
193 expectNoAria(second, 'tabIndex');\r
194 });\r
195 \r
196 it("should not set tabindex on the third child", function() {\r
197 expectNoAria(third, 'tabIndex');\r
198 });\r
199 \r
200 it("should call destroy", function() {\r
201 fc.destroy();\r
202 \r
203 expect(fc.doDestroyFocusableContainer).toHaveBeenCalled();\r
204 });\r
205 });\r
206 \r
207 // This is common case when a toolbar needs to make a late decision to bail out\r
208 // of being a FocusableContainer because one or more of its children needs to handle\r
209 // arrow key presses. See https://sencha.jira.com/browse/EXTJS-17458\r
210 describe("enableFocusableContainer changes to false before rendering", function() {\r
211 beforeEach(function() {\r
212 setupContainer({ renderTo: undefined });\r
213 fc.enableFocusableContainer = false;\r
214 fc.render(Ext.getBody());\r
215 });\r
216 \r
217 it("should not call init", function() {\r
218 expect(fc.doInitFocusableContainer).not.toHaveBeenCalled();\r
219 });\r
220 \r
221 it("should not place tabindex on container el", function() {\r
222 expectNoAria(fc, 'tabIndex');\r
223 });\r
224 \r
225 it("should not create keyNav", function() {\r
226 expect(fc.focusableKeyNav).not.toBeDefined();\r
227 });\r
228 \r
229 it("should not add tabindex to second child", function() {\r
230 expectNoAria(second, 'tabIndex');\r
231 });\r
232 \r
233 it("should not alter tabindex on last child", function() {\r
234 expectAria(third, 'tabIndex', '-10');\r
235 });\r
236 \r
237 it("should not call destroy", function() {\r
238 fc.destroy();\r
239 \r
240 expect(fc.doDestroyFocusableContainer).not.toHaveBeenCalled();\r
241 });\r
242 });\r
243 });\r
244 \r
245 describe("enableFocusableContainer === false", function() {\r
246 beforeEach(function() {\r
247 setupContainer({ enableFocusableContainer: false });\r
248 });\r
249 \r
250 it("should not call init", function() {\r
251 expect(fc.doInitFocusableContainer).not.toHaveBeenCalled();\r
252 });\r
253 \r
254 it("should not place tabindex on container el", function() {\r
255 expectNoAria(fc, 'tabIndex');\r
256 });\r
257 \r
258 it("should not create keyNav", function() {\r
259 expect(fc.focusableKeyNav).not.toBeDefined();\r
260 });\r
261 \r
262 it("should not alter tabindex on first child", function() {\r
263 expectAria(first, 'tabIndex', '0');\r
264 });\r
265 \r
266 it("should not add tabindex to second child", function() {\r
267 expectNoAria(second, 'tabIndex');\r
268 });\r
269 \r
270 it("should not alter tabindex on last child", function() {\r
271 expectAria(third, 'tabIndex', '-10');\r
272 });\r
273 \r
274 it("should not call destroy", function() {\r
275 fc.destroy();\r
276 \r
277 expect(fc.doDestroyFocusableContainer).not.toHaveBeenCalled();\r
278 });\r
279 });\r
280 });\r
281 \r
282 describe("show", function() {\r
283 beforeEach(function() {\r
284 makeContainer({\r
285 items: [{\r
286 xtype: 'button',\r
287 text: 'OK'\r
288 }]\r
289 });\r
290 });\r
291 \r
292 it("should reactivate FC el upon show", function() {\r
293 fc.hide();\r
294 fc.el.set({ tabIndex: -1 });\r
295 fc.show();\r
296 \r
297 expect(fc.el.dom.getAttribute('tabIndex')).toBe('0');\r
298 });\r
299 });\r
300 \r
301 describe("predicates", function() {\r
302 describe("isFocusableContainerActive", function() {\r
303 var button;\r
304 \r
305 function expectActive(want) {\r
306 var have = fc.isFocusableContainerActive();\r
307 \r
308 expect(have).toBe(want);\r
309 }\r
310 \r
311 beforeEach(function() {\r
312 makeContainer({\r
313 items: [{ text: 'foo' }]\r
314 });\r
315 \r
316 button = fc.down('button');\r
317 });\r
318 \r
319 afterEach(function() {\r
320 button = null;\r
321 });\r
322 \r
323 it("should return true by default", function() {\r
324 expectActive(true);\r
325 });\r
326 \r
327 it("should return false when container el is not tabbable", function() {\r
328 fcEl.dom.removeAttribute('tabIndex');\r
329 \r
330 expectActive(false);\r
331 });\r
332 \r
333 describe("after activating a child", function() {\r
334 beforeEach(function() {\r
335 focusAndWait(button);\r
336 });\r
337 \r
338 it("should return true when child is focused", function() {\r
339 expectActive(true);\r
340 });\r
341 \r
342 it("should return false if active child is not tabbable", function() {\r
343 button.getFocusEl().dom.removeAttribute('tabIndex');\r
344 \r
345 expectActive(false);\r
346 });\r
347 });\r
348 });\r
349 });\r
350 \r
351 describe("child lookup", function() {\r
352 describe("first/last child", function() {\r
353 function makeSuite(name, config) {\r
354 describe(name, function() {\r
355 var fooBtn, barBtn;\r
356 \r
357 beforeEach(function() {\r
358 makeContainer(config);\r
359 \r
360 fooBtn = fc.down('button[text=fooBtn]');\r
361 barBtn = fc.down('button[text=barBtn]');\r
362 });\r
363 \r
364 it("finds foo going forward", function() {\r
365 var child = fc.findNextFocusableChild({ step: true });\r
366 \r
367 expect(child).toBe(fooBtn);\r
368 });\r
369 \r
370 it("finds bar going backward", function() {\r
371 var child = fc.findNextFocusableChild({ step: false });\r
372 \r
373 expect(child).toBe(barBtn);\r
374 });\r
375 });\r
376 }\r
377 \r
378 makeSuite('focusable child', {\r
379 items: [\r
380 { xtype: 'button', text: 'fooBtn' },\r
381 { xtype: 'button', text: 'barBtn' }\r
382 ]\r
383 });\r
384 \r
385 makeSuite('non-focusable child', {\r
386 items: [\r
387 { xtype: 'tbtext', text: 'text1' },\r
388 { xtype: 'button', text: 'fooBtn' },\r
389 { xtype: 'button', text: 'barBtn' },\r
390 { xtype: 'tbtext', text: 'text2' }\r
391 ]\r
392 });\r
393 \r
394 makeSuite('focusable but disabled child', {\r
395 items: [\r
396 { xtype: 'button', text: 'disabled1', disabled: true },\r
397 { xtype: 'button', text: 'fooBtn' },\r
398 { xtype: 'button', text: 'barBtn' },\r
399 { xtype: 'button', text: 'disabled2', disabled: true }\r
400 ]\r
401 });\r
402 \r
403 makeSuite('focusable/disabled AND non-focusable child', {\r
404 items: [\r
405 { xtype: 'tbtext', text: 'text1' },\r
406 { xtype: 'button', text: 'disabled1', disabled: true },\r
407 { xtype: 'button', text: 'fooBtn' },\r
408 { xtype: 'button', text: 'barBtn' },\r
409 { xtype: 'tbtext', text: 'text2' },\r
410 { xtype: 'button', text: 'disabled2', disabled: true }\r
411 ]\r
412 });\r
413 });\r
414 \r
415 describe("from existing child", function() {\r
416 var fooBtn, barBtn, fooInput, barInput, disabled1, disabled2;\r
417 \r
418 function expectToFind(whatNext, whereFrom, goingForward) {\r
419 var child = fc.findNextFocusableChild({ child: whereFrom, step: goingForward });\r
420 \r
421 expect(child).toBe(whatNext);\r
422 }\r
423 \r
424 beforeEach(function() {\r
425 makeContainer({\r
426 items: [\r
427 { xtype: 'tbtext', text: 'text1' },\r
428 { xtype: 'button', text: 'disabled1', disabled: true },\r
429 { xtype: 'button', text: 'fooBtn' },\r
430 { xtype: 'tbseparator' },\r
431 { xtype: 'textfield', fieldLabel: 'foo field' },\r
432 { xtype: 'button', text: 'disabled2', disabled: true },\r
433 { xtype: 'button', text: 'barBtn' },\r
434 { xtype: 'tbfill' },\r
435 { xtype: 'combobox', fieldLabel: 'bar combo' }\r
436 ]\r
437 });\r
438 \r
439 fooBtn = fc.down('button[text=fooBtn]');\r
440 barBtn = fc.down('button[text=barBtn]');\r
441 \r
442 disabled1 = fc.down('button[text=disabled1]');\r
443 disabled2 = fc.down('button[text=disabled2]');\r
444 \r
445 fooInput = fc.down('textfield');\r
446 barInput = fc.down('combobox');\r
447 });\r
448 \r
449 afterEach(function() {\r
450 fooBtn = barBtn = fooInput = barInput = disabled1 = disabled2 = null;\r
451 });\r
452 \r
453 describe("forward", function() {\r
454 describe("disabled buttons not changed", function() {\r
455 it("finds fooBtn as the first item", function() {\r
456 expectToFind(fooBtn, null, forward);\r
457 });\r
458 \r
459 it("finds fooInput from fooBtn", function() {\r
460 expectToFind(fooInput, fooBtn, forward);\r
461 });\r
462 \r
463 it("finds barBtn from fooInput", function() {\r
464 expectToFind(barBtn, fooInput, forward);\r
465 });\r
466 \r
467 it("finds barInput from barBtn", function() {\r
468 expectToFind(barInput, barBtn, forward);\r
469 });\r
470 \r
471 it("finds fooBtn from barInput (wraps over)", function() {\r
472 expectToFind(fooBtn, barInput, forward);\r
473 });\r
474 });\r
475 \r
476 describe("disabled1 state changed", function() {\r
477 beforeEach(function() {\r
478 disabled1.enable();\r
479 });\r
480 \r
481 it("finds disabled1 as the first item", function() {\r
482 expectToFind(disabled1, null, forward);\r
483 });\r
484 \r
485 it("finds fooBtn from disabled1", function() {\r
486 expectToFind(fooBtn, disabled1, forward);\r
487 });\r
488 \r
489 it("finds fooInput from fooBtn", function() {\r
490 expectToFind(fooInput, fooBtn, forward);\r
491 });\r
492 \r
493 it("finds barBtn from fooInput", function() {\r
494 expectToFind(barBtn, fooInput, forward);\r
495 });\r
496 \r
497 it("finds barInput from barBtn", function() {\r
498 expectToFind(barInput, barBtn, forward);\r
499 });\r
500 \r
501 it("finds disabled1 from barInput (wraps over)", function() {\r
502 expectToFind(disabled1, barInput, forward);\r
503 });\r
504 });\r
505 \r
506 describe("disabled2 state changed", function() {\r
507 beforeEach(function() {\r
508 disabled2.enable();\r
509 });\r
510 \r
511 it("finds fooBtn as the first item", function() {\r
512 expectToFind(fooBtn, null, forward);\r
513 });\r
514 \r
515 it("finds fooInput from fooBtn", function() {\r
516 expectToFind(fooInput, fooBtn, forward);\r
517 });\r
518 \r
519 it("finds disabled2 from fooInput", function() {\r
520 expectToFind(disabled2, fooInput, forward);\r
521 });\r
522 \r
523 it("finds barBtn from disabled2", function() {\r
524 expectToFind(barBtn, disabled2, forward);\r
525 });\r
526 \r
527 it("finds barInput from barBtn", function() {\r
528 expectToFind(barInput, barBtn, forward);\r
529 });\r
530 \r
531 it("finds fooBtn from barInput (wraps over)", function() {\r
532 expectToFind(fooBtn, barInput, forward);\r
533 });\r
534 });\r
535 });\r
536 \r
537 describe("backward", function() {\r
538 describe("disabled buttons not changed", function() {\r
539 it("finds barInput as the first item", function() {\r
540 expectToFind(barInput, null, backward);\r
541 });\r
542 \r
543 it("finds barBtn from barInput", function() {\r
544 expectToFind(barBtn, barInput, backward);\r
545 });\r
546 \r
547 it("finds fooInput from barBtn", function() {\r
548 expectToFind(fooInput, barBtn, backward);\r
549 });\r
550 \r
551 it("finds fooBtn from fooInput", function() {\r
552 expectToFind(fooBtn, fooInput, backward);\r
553 });\r
554 \r
555 it("finds barInput from fooBtn (wraps over)", function() {\r
556 expectToFind(barInput, fooBtn, backward);\r
557 });\r
558 });\r
559 \r
560 describe("disabled1 state changed", function() {\r
561 beforeEach(function() {\r
562 disabled1.enable();\r
563 });\r
564 \r
565 it("finds barInput as the first item", function() {\r
566 expectToFind(barInput, null, backward);\r
567 });\r
568 \r
569 it("finds barBtn from barInput", function() {\r
570 expectToFind(barBtn, barInput, backward);\r
571 });\r
572 \r
573 it("finds fooInput from barBtn", function() {\r
574 expectToFind(fooInput, barBtn, backward);\r
575 });\r
576 \r
577 it("finds fooBtn from fooInput", function() {\r
578 expectToFind(fooBtn, fooInput, backward);\r
579 });\r
580 \r
581 it("finds disabled1 from fooBtn", function() {\r
582 expectToFind(disabled1, fooBtn, backward);\r
583 });\r
584 \r
585 it("finds barInput from disabled1 (wraps over)", function() {\r
586 expectToFind(barInput, disabled1, backward);\r
587 });\r
588 });\r
589 \r
590 describe("disabled2 state changed", function() {\r
591 beforeEach(function() {\r
592 disabled2.enable();\r
593 });\r
594 \r
595 it("finds barInput as the first item", function() {\r
596 expectToFind(barInput, null, backward);\r
597 });\r
598 \r
599 it("finds barBtn from barInput", function() {\r
600 expectToFind(barBtn, barInput, backward);\r
601 });\r
602 \r
603 it("finds disabled2 from barBtn", function() {\r
604 expectToFind(disabled2, barBtn, backward);\r
605 });\r
606 \r
607 it("finds fooInput from disabled2", function() {\r
608 expectToFind(fooInput, disabled2, backward);\r
609 });\r
610 \r
611 it("finds fooBtn from fooInput", function() {\r
612 expectToFind(fooBtn, fooInput, backward);\r
613 });\r
614 \r
615 it("finds barInput from fooBtn (wraps over)", function() {\r
616 expectToFind(barInput, fooBtn, backward);\r
617 });\r
618 });\r
619 });\r
620 });\r
621 });\r
622 \r
623 describe("child state handling", function() {\r
624 var first, second;\r
625 \r
626 afterEach(function() {\r
627 first = second = null;\r
628 });\r
629 \r
630 describe("initially enabled children", function() {\r
631 beforeEach(function() {\r
632 makeContainer({\r
633 items: [{\r
634 itemId: 'first',\r
635 text: 'first'\r
636 }, {\r
637 itemId: 'second',\r
638 text: 'second'\r
639 }]\r
640 });\r
641 \r
642 first = fc.down('#first');\r
643 second = fc.down('#second');\r
644 });\r
645 \r
646 it("should activate container el", function() {\r
647 expectAria(fc, 'tabIndex', '0');\r
648 });\r
649 \r
650 it("should deactivate container el when all children become disabled", function() {\r
651 first.disable();\r
652 second.disable()\r
653 \r
654 expectNoAria(fc, 'tabIndex');\r
655 });\r
656 });\r
657 \r
658 describe("initially disabled children", function() {\r
659 beforeEach(function() {\r
660 makeContainer({\r
661 items: [{\r
662 itemId: 'first',\r
663 text: 'first',\r
664 disabled: true\r
665 }, {\r
666 itemId: 'second',\r
667 text: 'second',\r
668 disabled: true\r
669 }]\r
670 });\r
671 \r
672 first = fc.down('#first');\r
673 second = fc.down('#second');\r
674 });\r
675 \r
676 it("should not activate container el", function() {\r
677 expectNoAria(fc, 'tabIndex');\r
678 });\r
679 \r
680 it("should activate container el when one child becomes enabled", function() {\r
681 first.enable();\r
682 \r
683 expectAria(fc, 'tabIndex', '0');\r
684 });\r
685 });\r
686 \r
687 describe("child state changes", function() {\r
688 beforeEach(function() {\r
689 makeContainer({\r
690 items: [{\r
691 itemId: 'first',\r
692 text: 'first'\r
693 }, {\r
694 itemId: 'second',\r
695 text: 'second'\r
696 }]\r
697 });\r
698 \r
699 first = fc.down('#first');\r
700 second = fc.down('#second');\r
701 });\r
702 \r
703 it("should set lastFocusedChild when child is focused", function() {\r
704 focusAndWait(first);\r
705 \r
706 runs(function() {\r
707 expect(fc.lastFocusedChild).toBe(first);\r
708 });\r
709 });\r
710 \r
711 describe("children become disabled, none focused", function() {\r
712 beforeEach(function() {\r
713 first.disable();\r
714 second.disable();\r
715 });\r
716 \r
717 it("should deactivate container el", function() {\r
718 expectNoAria(fc, 'tabIndex');\r
719 });\r
720 \r
721 it("should not reset first child tabIndex", function() {\r
722 expectNoAria(first, 'tabIndex');\r
723 });\r
724 \r
725 it("should not reset second child tabIndex", function() {\r
726 expectNoAria(second, 'tabIndex');\r
727 });\r
728 \r
729 describe("one child becoming enabled", function() {\r
730 beforeEach(function() {\r
731 second.enable();\r
732 });\r
733 \r
734 it("should activate container el", function() {\r
735 expectAria(fc, 'tabIndex', '0');\r
736 });\r
737 \r
738 it("should not reset first child tabIndex", function() {\r
739 expectNoAria(first, 'tabIndex');\r
740 });\r
741 \r
742 it("should reset second child tabIndex", function() {\r
743 expectAria(second, 'tabIndex', '-1');\r
744 });\r
745 });\r
746 \r
747 describe("both children become enabled", function() {\r
748 beforeEach(function() {\r
749 first.enable();\r
750 second.enable();\r
751 });\r
752 \r
753 it("should activate container el", function() {\r
754 expectAria(fc, 'tabIndex', '0');\r
755 });\r
756 \r
757 it("should reset first child tabIndex", function() {\r
758 expectAria(first, 'tabIndex', '-1');\r
759 });\r
760 \r
761 it("should reset second child tabIndex", function() {\r
762 expectAria(second, 'tabIndex', '-1');\r
763 });\r
764 });\r
765 });\r
766 \r
767 describe("last focusable child becoming disabled", function() {\r
768 beforeEach(function() {\r
769 runs(function() {\r
770 first.disable();\r
771 });\r
772 \r
773 focusAndWait(second);\r
774 \r
775 runs(function() {\r
776 second.disable();\r
777 });\r
778 });\r
779 \r
780 it("should not reset lastFocusedChild when child is disabled", function() {\r
781 expect(fc.lastFocusedChild).toBe(second);\r
782 });\r
783 \r
784 it("should deactivate container el", function() {\r
785 expectNoAria(fc, 'tabIndex');\r
786 });\r
787 \r
788 it("should not reset tabIndex on the child", function() {\r
789 expectNoAria(second, 'tabIndex');\r
790 });\r
791 \r
792 describe("becoming enabled again", function() {\r
793 beforeEach(function() {\r
794 second.tabIndex = 42;\r
795 second.enable();\r
796 });\r
797 \r
798 it("should not activate container el", function() {\r
799 expectNoAria(fc, 'tabIndex');\r
800 });\r
801 \r
802 it("should not interfere with child tabIndex", function() {\r
803 expectAria(second, 'tabIndex', '42');\r
804 });\r
805 });\r
806 \r
807 describe("all children become enabled", function() {\r
808 beforeEach(function() {\r
809 first.tabIndex = 101;\r
810 second.tabIndex = 102;\r
811 second.enable();\r
812 first.enable();\r
813 });\r
814 \r
815 it("should not activate container el", function() {\r
816 expectNoAria(fc, 'tabIndex');\r
817 });\r
818 \r
819 it("should reset first child tabIndex", function() {\r
820 expectAria(first, 'tabIndex', '-1');\r
821 });\r
822 \r
823 it("should not interfere with second child tabIndex", function() {\r
824 expectAria(second, 'tabIndex', '102');\r
825 });\r
826 });\r
827 });\r
828 });\r
829 });\r
830 \r
831 describe("focus handling", function() {\r
832 var beforeBtn, fooBtn, barBtn;\r
833 \r
834 beforeEach(function() {\r
835 // Before button is outside of the container\r
836 beforeBtn = makeButton({ text: 'beforeBtn' });\r
837 });\r
838 \r
839 afterEach(function() {\r
840 if (beforeBtn) {\r
841 beforeBtn.destroy();\r
842 }\r
843 \r
844 beforeBtn = null;\r
845 });\r
846 \r
847 describe("have focusables", function() {\r
848 beforeEach(function() {\r
849 makeContainer({\r
850 items: [\r
851 { xtype: 'button', text: 'fooBtn' },\r
852 { xtype: 'button', text: 'barBtn' }\r
853 ]\r
854 });\r
855 \r
856 fooBtn = fc.down('button[text=fooBtn]');\r
857 barBtn = fc.down('button[text=barBtn]');\r
858 });\r
859 \r
860 describe("focusing container el", function() {\r
861 beforeEach(function() {\r
862 focusAndWait(fcEl, fooBtn);\r
863 });\r
864 \r
865 describe("in FocusableContainer", function() {\r
866 it("should focus first child", function() {\r
867 expectFocused(fooBtn);\r
868 });\r
869 \r
870 it("should make first child tabbable", function() {\r
871 expectAria(fooBtn, 'tabIndex', '0');\r
872 });\r
873 \r
874 it("should make itself untabbable", function() {\r
875 expectNoAria(fc, 'tabIndex');\r
876 });\r
877 });\r
878 \r
879 describe("out of FocusableContainer", function() {\r
880 beforeEach(function() {\r
881 focusAndWait(beforeBtn);\r
882 });\r
883 \r
884 it("should keep first child tabbable", function() {\r
885 expectAria(fooBtn, 'tabIndex', '0');\r
886 });\r
887 \r
888 it("should not make itself tabbable", function() {\r
889 expectNoAria(fc, 'tabIndex');\r
890 });\r
891 });\r
892 });\r
893 \r
894 describe("focusing children", function() {\r
895 beforeEach(function() {\r
896 focusAndWait(fooBtn);\r
897 });\r
898 \r
899 describe("into FocusableContainer", function() {\r
900 it("should not prevent the child from getting focus", function() {\r
901 expectFocused(fooBtn);\r
902 });\r
903 \r
904 it("should make the child tabbable", function() {\r
905 expectAria(fooBtn, 'tabIndex', '0');\r
906 });\r
907 \r
908 it("should make its el untabbable", function() {\r
909 expectNoAria(fc, 'tabIndex');\r
910 });\r
911 });\r
912 \r
913 describe("out of FocusableContainer", function() {\r
914 beforeEach(function() {\r
915 focusAndWait(beforeBtn);\r
916 });\r
917 \r
918 it("should not prevent focus from leaving", function() {\r
919 expectFocused(beforeBtn);\r
920 });\r
921 \r
922 it("should keep the child tabbable", function() {\r
923 expectAria(fooBtn, 'tabIndex', '0');\r
924 });\r
925 \r
926 it("should keep its el untabbable", function() {\r
927 expectNoAria(fc, 'tabIndex');\r
928 });\r
929 });\r
930 });\r
931 \r
932 describe("disabling currently focused child", function() {\r
933 beforeEach(function() {\r
934 focusAndWait(fooBtn);\r
935 });\r
936 \r
937 describe("when there are other focusable children remaining", function() {\r
938 beforeEach(function() {\r
939 fooBtn.disable();\r
940 });\r
941 \r
942 it("should focus next child", function() {\r
943 expectFocused(barBtn);\r
944 });\r
945 \r
946 it("should not make container el focusable", function() {\r
947 expectNoAria(fcEl, 'tabIndex');\r
948 });\r
949 \r
950 it("should update lastFocusedChild", function() {\r
951 expect(fc.lastFocusedChild).toBe(barBtn);\r
952 });\r
953 });\r
954 \r
955 describe("when there are no focusable children remaining", function() {\r
956 beforeEach(function() {\r
957 barBtn.disable();\r
958 \r
959 fooBtn.findFocusTarget = function() {\r
960 return beforeBtn;\r
961 };\r
962 \r
963 fooBtn.disable();\r
964 });\r
965 \r
966 it("should focus findFocusTarget result", function() {\r
967 expectFocused(beforeBtn);\r
968 });\r
969 \r
970 it("should deactivate container el", function() {\r
971 expectNoAria(fc, 'tabIndex');\r
972 });\r
973 \r
974 it("should not update lastFocusedChild", function() {\r
975 expect(fc.lastFocusedChild).toBe(fooBtn);\r
976 });\r
977 });\r
978 });\r
979 });\r
980 });\r
981 \r
982 describe("mouse event handling", function() {\r
983 var beforeBtn, text, fooBtn, input;\r
984 \r
985 beforeEach(function() {\r
986 beforeBtn = makeButton({ text: 'beforeBtn' });\r
987 \r
988 makeContainer({\r
989 style: {\r
990 'margin-left': '100px'\r
991 },\r
992 items: [\r
993 { xtype: 'tbtext', text: '****' },\r
994 { xtype: 'button', text: 'fooBtn' },\r
995 { xtype: 'textfield', fieldLabel: 'fooInput' }\r
996 ]\r
997 });\r
998 \r
999 text = fc.down('tbtext');\r
1000 fooBtn = fc.down('button');\r
1001 input = fc.down('textfield');\r
1002 });\r
1003 \r
1004 afterEach(function() {\r
1005 beforeBtn.destroy();\r
1006 \r
1007 beforeBtn = null;\r
1008 });\r
1009 \r
1010 it("should ignore left click on container body el", function() {\r
1011 focusAndWait(beforeBtn);\r
1012 \r
1013 runs(function() {\r
1014 jasmine.fireMouseEvent(fc.el, 'click');\r
1015 });\r
1016 \r
1017 expectFocused(beforeBtn);\r
1018 });\r
1019 \r
1020 it("should ignore right click on container body el", function() {\r
1021 focusAndWait(beforeBtn);\r
1022 \r
1023 runs(function() {\r
1024 jasmine.fireMouseEvent(fc.el, 'click', null, null, 1);\r
1025 });\r
1026 \r
1027 expectFocused(beforeBtn);\r
1028 });\r
1029 \r
1030 it("should not react to clicks in non-focusable children", function() {\r
1031 focusAndWait(beforeBtn);\r
1032 \r
1033 runs(function() {\r
1034 jasmine.fireMouseEvent(text.el, 'click');\r
1035 });\r
1036 \r
1037 expectFocused(beforeBtn);\r
1038 });\r
1039 \r
1040 describe("clicks on focusable child", function() {\r
1041 var spy;\r
1042 \r
1043 // We're listening to mousedown instead of click here because Ext 5/Touch\r
1044 // event system is doing crazy translation of touch/mouse/pointer events\r
1045 // that is browser specific. Click is translated from 'tap' in IE10+\r
1046 // but for some reason firing 'tap' event doesn't seem to be reaching\r
1047 // the proper event plumbing in the Button, so Button's click event never fires.\r
1048 // Mousedown on the element works, and that's good enough for this case.\r
1049 beforeEach(function() {\r
1050 spy = jasmine.createSpy('click');\r
1051 \r
1052 fooBtn.on('click', spy);\r
1053 \r
1054 // Right clicks are blocked by Button's code\r
1055 fooBtn.el.on('mousedown', spy);\r
1056 });\r
1057 \r
1058 it("should not block left click", function() {\r
1059 runs(function() {\r
1060 jasmine.fireMouseEvent(fooBtn.el, 'click');\r
1061 });\r
1062 \r
1063 waitsForSpy(spy, 'left click', 100);\r
1064 \r
1065 runs(function() {\r
1066 expect(spy).toHaveBeenCalled();\r
1067 });\r
1068 });\r
1069 \r
1070 it("should not block right click", function() {\r
1071 runs(function() {\r
1072 jasmine.fireMouseEvent(fooBtn.el, 'click', null, null, 1);\r
1073 });\r
1074 \r
1075 waitsForSpy(spy, 'right click', 100);\r
1076 \r
1077 runs(function() {\r
1078 expect(spy).toHaveBeenCalled();\r
1079 });\r
1080 });\r
1081 });\r
1082 });\r
1083 \r
1084 describe("keyboard event handling", function() {\r
1085 var forward = true,\r
1086 backward = false,\r
1087 beforeBtn, afterBtn, fooBtn, barBtn, fooInput, barInput, slider,\r
1088 disabledBtn1, disabledBtn2;\r
1089 \r
1090 function tabAndExpect(from, direction, to, debug) {\r
1091 pressTab(from, direction);\r
1092 \r
1093 expectFocused(to);\r
1094 }\r
1095 \r
1096 function arrowAndExpect(from, arrow, to) {\r
1097 pressArrow(from, arrow);\r
1098 \r
1099 expectFocused(to);\r
1100 }\r
1101 \r
1102 // Unfortunately we cannot test that the actual problem is solved,\r
1103 // which is scrolling the parent container caused by default action\r
1104 // on arrow keys. This is because synthetic injected events do not cause\r
1105 // default action. The best we can do is to check that event handlers\r
1106 // are calling preventDefault() on the events.\r
1107 // See https://sencha.jira.com/browse/EXTJS-18186\r
1108 describe("preventing parent scroll", function() {\r
1109 var upSpy, downSpy, rightSpy, leftSpy;\r
1110 \r
1111 beforeEach(function() {\r
1112 makeContainer({\r
1113 renderTo: undefined,\r
1114 items: [{\r
1115 xtype: 'button',\r
1116 text: 'fooBtn'\r
1117 }, {\r
1118 xtype: 'button',\r
1119 text: 'barBtn'\r
1120 }]\r
1121 });\r
1122 \r
1123 fooBtn = fc.down('button[text=fooBtn]');\r
1124 barBtn = fc.down('button[text=barBtn]');\r
1125 \r
1126 upSpy = spyOn(fc, 'onFocusableContainerUpKey').andCallThrough();\r
1127 downSpy = spyOn(fc, 'onFocusableContainerDownKey').andCallThrough();\r
1128 rightSpy = spyOn(fc, 'onFocusableContainerRightKey').andCallThrough();\r
1129 leftSpy = spyOn(fc, 'onFocusableContainerLeftKey').andCallThrough();\r
1130 \r
1131 fc.render(Ext.getBody());\r
1132 });\r
1133 \r
1134 afterEach(function() {\r
1135 fooBtn = barBtn = null;\r
1136 upSpy = downSpy = rightSpy = leftSpy = null;\r
1137 });\r
1138 \r
1139 it("should preventDefault on the Up arrow key", function() {\r
1140 pressArrow(barBtn, 'up');\r
1141 \r
1142 waitForFocus(fooBtn);\r
1143 \r
1144 runs(function() {\r
1145 expect(upSpy.mostRecentCall.args[0].defaultPrevented).toBe(true);\r
1146 });\r
1147 });\r
1148 \r
1149 it("should preventDefault on the Down arrow key", function() {\r
1150 pressArrow(fooBtn, 'down');\r
1151 \r
1152 waitForFocus(barBtn);\r
1153 \r
1154 runs(function() {\r
1155 expect(downSpy.mostRecentCall.args[0].defaultPrevented).toBe(true);\r
1156 });\r
1157 });\r
1158 \r
1159 it("should preventDefault on the Right arrow key", function() {\r
1160 pressArrow(fooBtn, 'right');\r
1161 \r
1162 waitForFocus(barBtn);\r
1163 \r
1164 runs(function() {\r
1165 expect(rightSpy.mostRecentCall.args[0].defaultPrevented).toBe(true);\r
1166 });\r
1167 });\r
1168 \r
1169 it("should preventDefault on the Left arrow key", function() {\r
1170 pressArrow(barBtn, 'left');\r
1171 \r
1172 waitForFocus(fooBtn);\r
1173 \r
1174 runs(function() {\r
1175 expect(leftSpy.mostRecentCall.args[0].defaultPrevented).toBe(true);\r
1176 });\r
1177 });\r
1178 });\r
1179 \r
1180 describe("enableFocusableContainer === true", function() {\r
1181 beforeEach(function() {\r
1182 runs(function() {\r
1183 beforeBtn = makeButton({ text: 'beforeBtn' });\r
1184 \r
1185 makeContainer({\r
1186 items: [\r
1187 { xtype: 'tbtext', text: '**' },\r
1188 { xtype: 'button', text: 'disabledBtn1', disabled: true },\r
1189 { xtype: 'button', text: 'fooBtn' },\r
1190 { xtype: 'tbseparator' },\r
1191 { xtype: 'textfield', id: 'fooInput-' + ++autoId },\r
1192 { xtype: 'tbseparator' },\r
1193 {\r
1194 xtype: 'slider',\r
1195 id: 'slider-' + ++autoId,\r
1196 value: 50,\r
1197 width: 100,\r
1198 animate: false\r
1199 },\r
1200 { xtype: 'tbseparator' },\r
1201 { xtype: 'tbfill' },\r
1202 { xtype: 'tbseparator' },\r
1203 { xtype: 'button', text: 'barBtn' },\r
1204 { xtype: 'button', text: 'disabledBtn2', disabled: true },\r
1205 { xtype: 'combobox', id: 'barInput-' + ++autoId },\r
1206 { xtype: 'tbtext', text: '***' }\r
1207 ]\r
1208 });\r
1209 \r
1210 fooBtn = fc.down('button[text=fooBtn]');\r
1211 barBtn = fc.down('button[text=barBtn]');\r
1212 \r
1213 fooInput = fc.down('textfield');\r
1214 barInput = fc.down('combobox');\r
1215 slider = fc.down('slider');\r
1216 \r
1217 disabledBtn1 = fc.down('button[text=disabledBtn1]');\r
1218 disabledBtn2 = fc.down('button[text=disabledBtn2]');\r
1219 \r
1220 afterBtn = makeButton({ text: 'afterBtn' });\r
1221 });\r
1222 \r
1223 jasmine.waitAWhile();\r
1224 });\r
1225 \r
1226 afterEach(function() {\r
1227 beforeBtn.destroy();\r
1228 afterBtn.destroy();\r
1229 });\r
1230 \r
1231 describe("tabbing", function() {\r
1232 describe("clean state in/out", function() {\r
1233 it("should tab from beforeBtn to fooBtn", function() {\r
1234 tabAndExpect(beforeBtn, forward, fooBtn);\r
1235 });\r
1236 \r
1237 it("should shift-tab from fooBtn fo beforeBtn", function() {\r
1238 tabAndExpect(fooBtn, backward, beforeBtn);\r
1239 });\r
1240 \r
1241 it("should tab from fooBtn to afterBtn", function() {\r
1242 tabAndExpect(fooBtn, forward, afterBtn);\r
1243 });\r
1244 \r
1245 it("should shift-tab from afterBtn to fooBtn", function() {\r
1246 tabAndExpect(afterBtn, backward, fooBtn);\r
1247 });\r
1248 });\r
1249 \r
1250 describe("needArrowKeys children", function() {\r
1251 it("should tab from fooInput to slider", function() {\r
1252 tabAndExpect(fooInput, forward, slider);\r
1253 });\r
1254 \r
1255 it("should tab from slider to barBtn", function() {\r
1256 tabAndExpect(slider, forward, barBtn);\r
1257 });\r
1258 \r
1259 it("should tab from barInput to afterBtn", function() {\r
1260 tabAndExpect(barInput, forward, afterBtn);\r
1261 });\r
1262 \r
1263 it("should shift-tab from barInput to barBtn", function() {\r
1264 tabAndExpect(barInput, backward, barBtn);\r
1265 });\r
1266 \r
1267 it("should shift-tab from slider to fooInput", function() {\r
1268 tabAndExpect(slider, backward, fooInput);\r
1269 });\r
1270 \r
1271 it("should shift-tab from fooInput to fooBtn", function() {\r
1272 tabAndExpect(fooInput, backward, fooBtn);\r
1273 });\r
1274 });\r
1275 \r
1276 describe("last focused child", function() {\r
1277 it("should shift-tab back into barInput from afterBtn", function() {\r
1278 tabAndExpect(barInput, forward, afterBtn);\r
1279 tabAndExpect(afterBtn, backward, barInput);\r
1280 });\r
1281 \r
1282 it("should shift-tab back to barBtn from afterBtn", function() {\r
1283 tabAndExpect(barBtn, forward, afterBtn);\r
1284 tabAndExpect(afterBtn, backward, barBtn);\r
1285 });\r
1286 \r
1287 describe("disabled state changes", function() {\r
1288 it("should choose fooBtn when shift-tabbing from afterBtn", function() {\r
1289 tabAndExpect(barBtn, forward, afterBtn);\r
1290 \r
1291 runs(function() {\r
1292 barBtn.disable();\r
1293 });\r
1294 \r
1295 tabAndExpect(afterBtn, backward, fooBtn);\r
1296 });\r
1297 \r
1298 it("should choose disabledBtn1 when tabbing from beforeBtn", function() {\r
1299 tabAndExpect(barBtn, backward, beforeBtn);\r
1300 \r
1301 runs(function() {\r
1302 barBtn.disable();\r
1303 disabledBtn1.enable();\r
1304 });\r
1305 \r
1306 tabAndExpect(beforeBtn, forward, disabledBtn1);\r
1307 });\r
1308 });\r
1309 });\r
1310 });\r
1311 \r
1312 describe("arrow keys", function() {\r
1313 describe("simple children (buttons, etc)", function() {\r
1314 it("should go right from fooBtn to fooInput", function() {\r
1315 arrowAndExpect(fooBtn, 'right', fooInput);\r
1316 });\r
1317 \r
1318 it("should go down from fooBtn to fooInput", function() {\r
1319 arrowAndExpect(fooBtn, 'down', fooInput);\r
1320 });\r
1321 \r
1322 it("should wrap over left from fooBtn to barInput", function() {\r
1323 arrowAndExpect(fooBtn, 'left', barInput);\r
1324 });\r
1325 \r
1326 it("should wrap over up from fooBtn to barInput", function() {\r
1327 arrowAndExpect(fooBtn, 'up', barInput);\r
1328 });\r
1329 \r
1330 it("should go left from barBtn to slider", function() {\r
1331 arrowAndExpect(barBtn, 'left', slider);\r
1332 });\r
1333 \r
1334 it("should go up from barBtn to slider", function() {\r
1335 arrowAndExpect(barBtn, 'up', slider);\r
1336 });\r
1337 });\r
1338 \r
1339 describe("needArrowKeys children", function() {\r
1340 describe("slider", function() {\r
1341 function makeSpec(key) {\r
1342 it("should not block " + key + " arrow key", function() {\r
1343 var changed = false;\r
1344 \r
1345 runs(function() {\r
1346 slider.on('change', function() { changed = true });\r
1347 });\r
1348 \r
1349 pressArrow(slider, key);\r
1350 \r
1351 runs(function() {\r
1352 expect(changed).toBeTruthy();\r
1353 });\r
1354 });\r
1355 }\r
1356 \r
1357 makeSpec('left');\r
1358 makeSpec('right');\r
1359 makeSpec('up');\r
1360 makeSpec('down');\r
1361 });\r
1362 \r
1363 describe("combo box", function() {\r
1364 beforeEach(function() {\r
1365 Ext.apply(barInput, {\r
1366 queryMode: 'local',\r
1367 displayField: 'name'\r
1368 });\r
1369 \r
1370 var store = new Ext.data.Store({\r
1371 fields: ['name'],\r
1372 data: [{ name: 'foo' }]\r
1373 });\r
1374 \r
1375 barInput.setStore(store);\r
1376 });\r
1377 \r
1378 it("should not block down arrow key", function() {\r
1379 pressArrow(barInput, 'down');\r
1380 \r
1381 runs(function() {\r
1382 expect(barInput.isExpanded).toBeTruthy();\r
1383 });\r
1384 });\r
1385 });\r
1386 });\r
1387 });\r
1388 });\r
1389 \r
1390 describe("enableFocusableContainer === false", function() {\r
1391 beforeEach(function() {\r
1392 runs(function() {\r
1393 beforeBtn = makeButton({ text: 'beforeBtn' });\r
1394 \r
1395 makeContainer({\r
1396 renderTo: undefined,\r
1397 items: [\r
1398 { xtype: 'tbtext', text: '**' },\r
1399 { xtype: 'button', text: 'disabledBtn1', disabled: true },\r
1400 { xtype: 'button', text: 'fooBtn' },\r
1401 { xtype: 'tbseparator' },\r
1402 { xtype: 'textfield', id: 'fooInput-' + ++autoId },\r
1403 { xtype: 'tbseparator' },\r
1404 {\r
1405 xtype: 'slider',\r
1406 id: 'slider-' + ++autoId,\r
1407 value: 50,\r
1408 width: 100,\r
1409 animate: false\r
1410 },\r
1411 { xtype: 'tbseparator' },\r
1412 { xtype: 'tbfill' },\r
1413 { xtype: 'tbseparator' },\r
1414 { xtype: 'button', text: 'barBtn' },\r
1415 { xtype: 'button', text: 'disabledBtn2', disabled: true },\r
1416 { xtype: 'combobox', id: 'barInput-' + ++autoId },\r
1417 { xtype: 'tbtext', text: '***' }\r
1418 ]\r
1419 });\r
1420 \r
1421 fooBtn = fc.down('button[text=fooBtn]');\r
1422 barBtn = fc.down('button[text=barBtn]');\r
1423 \r
1424 fooInput = fc.down('textfield');\r
1425 barInput = fc.down('combobox');\r
1426 slider = fc.down('slider');\r
1427 \r
1428 disabledBtn1 = fc.down('button[text=disabledBtn1]');\r
1429 disabledBtn2 = fc.down('button[text=disabledBtn2]');\r
1430 \r
1431 fc.enableFocusableContainer = false;\r
1432 fc.render(Ext.getBody());\r
1433 \r
1434 afterBtn = makeButton({ text: 'afterBtn' });\r
1435 });\r
1436 \r
1437 jasmine.waitAWhile();\r
1438 });\r
1439 \r
1440 afterEach(function() {\r
1441 beforeBtn.destroy();\r
1442 afterBtn.destroy();\r
1443 });\r
1444 \r
1445 describe("tabbing", function() {\r
1446 it("should tab from beforeBtn to fooBtn", function() {\r
1447 tabAndExpect(beforeBtn, forward, fooBtn);\r
1448 });\r
1449 \r
1450 it("should shift-tab from fooBtn to beforeBtn", function() {\r
1451 tabAndExpect(fooBtn, backward, beforeBtn);\r
1452 });\r
1453 \r
1454 it("should tab from fooBtn to fooInput", function() {\r
1455 tabAndExpect(fooBtn, forward, fooInput);\r
1456 });\r
1457 \r
1458 it("should shift-tab from fooInput to fooBtn", function() {\r
1459 tabAndExpect(fooInput, backward, fooBtn);\r
1460 });\r
1461 \r
1462 it("should tab from fooInput to slider", function() {\r
1463 tabAndExpect(fooInput, forward, slider);\r
1464 });\r
1465 \r
1466 it("should shift-tab from slider to fooInput", function() {\r
1467 tabAndExpect(slider, backward, fooInput);\r
1468 });\r
1469 \r
1470 it("should tab from slider to barBtn", function() {\r
1471 tabAndExpect(slider, forward, barBtn);\r
1472 });\r
1473 \r
1474 it("should shift-tab from barBtn to slider", function() {\r
1475 tabAndExpect(barBtn, backward, slider);\r
1476 });\r
1477 \r
1478 it("should tab from barBtn to barInput", function() {\r
1479 tabAndExpect(barBtn, forward, barInput);\r
1480 });\r
1481 \r
1482 it("should shift-tab from barInput to barBtn", function() {\r
1483 tabAndExpect(barInput, backward, barBtn);\r
1484 });\r
1485 \r
1486 it("should tab from barInput to afterBtn", function() {\r
1487 tabAndExpect(barInput, forward, afterBtn);\r
1488 });\r
1489 \r
1490 it("should shift-tab from afterBtn to barInput", function() {\r
1491 tabAndExpect(afterBtn, backward, barInput);\r
1492 });\r
1493 \r
1494 describe("disabled state changes", function() {\r
1495 beforeEach(function() {\r
1496 disabledBtn1.enable();\r
1497 disabledBtn2.enable();\r
1498 });\r
1499 \r
1500 it("should tab from beforeBtn to disabledBtn1", function() {\r
1501 tabAndExpect(beforeBtn, forward, disabledBtn1);\r
1502 });\r
1503 \r
1504 it("should shift-tab from disabledBtn1 to beforeBtn", function() {\r
1505 tabAndExpect(disabledBtn1, backward, beforeBtn);\r
1506 });\r
1507 \r
1508 it("should tab from disabledBtn1 to fooBtn", function() {\r
1509 tabAndExpect(disabledBtn1, forward, fooBtn);\r
1510 });\r
1511 \r
1512 it("should shift-tab from fooBtn to disabledBtn1", function() {\r
1513 tabAndExpect(fooBtn, backward, disabledBtn1);\r
1514 });\r
1515 \r
1516 it("should tab from barBtn to disabledBtn2", function() {\r
1517 tabAndExpect(barBtn, forward, disabledBtn2);\r
1518 });\r
1519 \r
1520 it("should shift-tab from disabledBtn2 to barBtn", function() {\r
1521 tabAndExpect(disabledBtn2, backward, barBtn);\r
1522 });\r
1523 \r
1524 it("should tab from disabledBtn2 to barInput", function() {\r
1525 tabAndExpect(disabledBtn2, forward, barInput);\r
1526 });\r
1527 \r
1528 it("should shift-tab from barInput to disabledBtn2", function() {\r
1529 tabAndExpect(barInput, backward, disabledBtn2);\r
1530 });\r
1531 });\r
1532 });\r
1533 \r
1534 // Arrow keys should not navigate when FocusableContainer is disabled;\r
1535 // we have to make sure of that!\r
1536 describe("arrow keys", function() {\r
1537 describe("fooBtn", function() {\r
1538 it("should stay focused on left arrow", function() {\r
1539 arrowAndExpect(fooBtn, 'left', fooBtn);\r
1540 });\r
1541 \r
1542 it("should stay focused on right arrow", function() {\r
1543 arrowAndExpect(fooBtn, 'right', fooBtn);\r
1544 });\r
1545 \r
1546 it("should stay focused on up arrow", function() {\r
1547 arrowAndExpect(fooBtn, 'up', fooBtn);\r
1548 });\r
1549 \r
1550 it("should stay focused on down arrow", function() {\r
1551 arrowAndExpect(fooBtn, 'down', fooBtn);\r
1552 });\r
1553 });\r
1554 \r
1555 describe("slider", function() {\r
1556 function makeSpec(key) {\r
1557 it("should not block " + key + " arrow key", function() {\r
1558 var changed = false;\r
1559 \r
1560 runs(function() {\r
1561 slider.on('change', function() { changed = true });\r
1562 });\r
1563 \r
1564 pressArrow(slider, key);\r
1565 \r
1566 runs(function() {\r
1567 expect(changed).toBeTruthy();\r
1568 });\r
1569 });\r
1570 }\r
1571 \r
1572 makeSpec('left');\r
1573 makeSpec('right');\r
1574 makeSpec('up');\r
1575 makeSpec('down');\r
1576 });\r
1577 \r
1578 describe("combo box", function() {\r
1579 beforeEach(function() {\r
1580 Ext.apply(barInput, {\r
1581 queryMode: 'local',\r
1582 displayField: 'name'\r
1583 });\r
1584 \r
1585 var store = new Ext.data.Store({\r
1586 fields: ['name'],\r
1587 data: [{ name: 'foo' }]\r
1588 });\r
1589 \r
1590 barInput.setStore(store);\r
1591 });\r
1592 \r
1593 it("should not block down arrow key", function() {\r
1594 pressArrow(barInput, 'down');\r
1595 \r
1596 runs(function() {\r
1597 expect(barInput.isExpanded).toBeTruthy();\r
1598 });\r
1599 });\r
1600 });\r
1601 });\r
1602 });\r
1603 });\r
1604});\r