@@ -92,6 +92,8 @@ const SCROLL_VIEW_CONTENT_CLASS = 'dx-scrollview-content';
9292const LIST_ITEMS_CLASS = 'dx-list-items' ;
9393const FOCUSED_CLASS = 'dx-state-focused' ;
9494
95+ const APPLY_BUTTON_SELECTOR = `.${ APPLY_BUTTON_CLASS } .dx-button` ;
96+ const CLEAR_BUTTON_SELECTOR = `.${ CLEAR_BUTTON_CLASS } .dx-button` ;
9597const CANCEL_BUTTON_SELECTOR = '.dx-popup-cancel.dx-button' ;
9698
9799const WINDOW_RATIO = 0.8 ;
@@ -2654,6 +2656,149 @@ QUnit.module('list options', {
26542656 } ) ;
26552657} ) ;
26562658
2659+ QUnit . module ( 'keyboard navigation - focus management' , {
2660+ beforeEach : function ( ) {
2661+ fx . off = true ;
2662+ this . clock = sinon . useFakeTimers ( ) ;
2663+ this . getList = ( instance ) => instance . _list ;
2664+ } ,
2665+ afterEach : function ( ) {
2666+ this . clock . restore ( ) ;
2667+ fx . off = false ;
2668+ }
2669+ } , ( ) => {
2670+ QUnit . test ( 'list should have tabIndex=-1 when searchEnabled is true' , function ( assert ) {
2671+ const instance = $ ( '#lookup' ) . dxLookup ( {
2672+ items : [ 1 , 2 , 3 ] ,
2673+ focusStateEnabled : true ,
2674+ searchEnabled : true ,
2675+ opened : true ,
2676+ } ) . dxLookup ( 'instance' ) ;
2677+
2678+ assert . strictEqual ( this . getList ( instance ) . option ( 'tabIndex' ) , - 1 , 'list tabIndex is -1 when searchEnabled is true' ) ;
2679+ } ) ;
2680+
2681+ QUnit . test ( 'list should have tabIndex=0 when searchEnabled is false' , function ( assert ) {
2682+ const instance = $ ( '#lookup' ) . dxLookup ( {
2683+ items : [ 1 , 2 , 3 ] ,
2684+ focusStateEnabled : true ,
2685+ searchEnabled : false ,
2686+ opened : true ,
2687+ } ) . dxLookup ( 'instance' ) ;
2688+
2689+ assert . strictEqual ( this . getList ( instance ) . option ( 'tabIndex' ) , 0 , 'list tabIndex is 0 when searchEnabled is false' ) ;
2690+ } ) ;
2691+
2692+ QUnit . test ( 'list tabIndex should update when searchEnabled changes at runtime' , function ( assert ) {
2693+ const instance = $ ( '#lookup' ) . dxLookup ( {
2694+ items : [ 1 , 2 , 3 ] ,
2695+ focusStateEnabled : true ,
2696+ searchEnabled : true ,
2697+ opened : true ,
2698+ } ) . dxLookup ( 'instance' ) ;
2699+
2700+ const list = this . getList ( instance ) ;
2701+
2702+ assert . strictEqual ( list . option ( 'tabIndex' ) , - 1 , 'tabIndex is -1 initially with searchEnabled=true' ) ;
2703+
2704+ instance . option ( 'searchEnabled' , false ) ;
2705+ assert . strictEqual ( list . option ( 'tabIndex' ) , 0 , 'tabIndex changed to 0 after searchEnabled set to false' ) ;
2706+
2707+ instance . option ( 'searchEnabled' , true ) ;
2708+ assert . strictEqual ( list . option ( 'tabIndex' ) , - 1 , 'tabIndex changed back to -1 after searchEnabled set to true' ) ;
2709+ } ) ;
2710+
2711+ QUnit . test ( 'list should receive focusStateEnabled from Lookup' , function ( assert ) {
2712+ const instance = $ ( '#lookup' ) . dxLookup ( {
2713+ items : [ 1 , 2 , 3 ] ,
2714+ focusStateEnabled : true ,
2715+ opened : true ,
2716+ } ) . dxLookup ( 'instance' ) ;
2717+
2718+ assert . ok ( this . getList ( instance ) . option ( 'focusStateEnabled' ) , 'list has focusStateEnabled=true when Lookup has focusStateEnabled=true' ) ;
2719+ } ) ;
2720+
2721+ QUnit . test ( 'list focusStateEnabled should update when parent focusStateEnabled changes at runtime' , function ( assert ) {
2722+ const instance = $ ( '#lookup' ) . dxLookup ( {
2723+ items : [ 1 , 2 , 3 ] ,
2724+ focusStateEnabled : true ,
2725+ opened : true ,
2726+ } ) . dxLookup ( 'instance' ) ;
2727+
2728+ const list = this . getList ( instance ) ;
2729+
2730+ instance . option ( 'focusStateEnabled' , false ) ;
2731+ assert . notOk ( list . option ( 'focusStateEnabled' ) , 'list focusStateEnabled updated to false' ) ;
2732+
2733+ instance . option ( 'focusStateEnabled' , true ) ;
2734+ assert . ok ( list . option ( 'focusStateEnabled' ) , 'list focusStateEnabled updated to true' ) ;
2735+ } ) ;
2736+
2737+ QUnit . test ( 'done button should receive focusStateEnabled from Lookup' , function ( assert ) {
2738+ const instance = $ ( '#lookup' ) . dxLookup ( {
2739+ items : [ 1 , 2 , 3 ] ,
2740+ focusStateEnabled : true ,
2741+ applyValueMode : 'useButtons' ,
2742+ opened : true ,
2743+ } ) . dxLookup ( 'instance' ) ;
2744+
2745+ const $doneButton = $ ( instance . content ( ) ) . parent ( ) . find ( APPLY_BUTTON_SELECTOR ) ;
2746+ assert . ok ( $doneButton . length , 'done button exists' ) ;
2747+
2748+ const doneButton = $doneButton . dxButton ( 'instance' ) ;
2749+ assert . ok ( doneButton . option ( 'focusStateEnabled' ) , 'done button has focusStateEnabled=true' ) ;
2750+ } ) ;
2751+
2752+ QUnit . test ( 'clear button should receive focusStateEnabled from Lookup' , function ( assert ) {
2753+ const instance = $ ( '#lookup' ) . dxLookup ( {
2754+ items : [ 1 , 2 , 3 ] ,
2755+ focusStateEnabled : true ,
2756+ showClearButton : true ,
2757+ opened : true ,
2758+ } ) . dxLookup ( 'instance' ) ;
2759+
2760+ const $clearButton = $ ( instance . content ( ) ) . parent ( ) . find ( CLEAR_BUTTON_SELECTOR ) ;
2761+ assert . ok ( $clearButton . length , 'clear button exists' ) ;
2762+
2763+ const clearButton = $clearButton . dxButton ( 'instance' ) ;
2764+ assert . ok ( clearButton . option ( 'focusStateEnabled' ) , 'clear button has focusStateEnabled=true' ) ;
2765+ } ) ;
2766+
2767+ QUnit . test ( 'cancel button should receive focusStateEnabled from Lookup' , function ( assert ) {
2768+ const instance = $ ( '#lookup' ) . dxLookup ( {
2769+ items : [ 1 , 2 , 3 ] ,
2770+ focusStateEnabled : true ,
2771+ showCancelButton : true ,
2772+ opened : true ,
2773+ } ) . dxLookup ( 'instance' ) ;
2774+
2775+ const $cancelButton = $ ( instance . content ( ) ) . parent ( ) . find ( CANCEL_BUTTON_SELECTOR ) ;
2776+ assert . ok ( $cancelButton . length , 'cancel button exists' ) ;
2777+
2778+ const cancelButton = $cancelButton . dxButton ( 'instance' ) ;
2779+ assert . ok ( cancelButton . option ( 'focusStateEnabled' ) , 'cancel button has focusStateEnabled=true' ) ;
2780+ } ) ;
2781+
2782+ QUnit . test ( 'toolbar buttons should not have focusStateEnabled when Lookup has focusStateEnabled=false' , function ( assert ) {
2783+ const instance = $ ( '#lookup' ) . dxLookup ( {
2784+ items : [ 1 , 2 , 3 ] ,
2785+ focusStateEnabled : false ,
2786+ showClearButton : true ,
2787+ showCancelButton : true ,
2788+ applyValueMode : 'useButtons' ,
2789+ opened : true ,
2790+ } ) . dxLookup ( 'instance' ) ;
2791+
2792+ const doneButton = $ ( instance . content ( ) ) . parent ( ) . find ( APPLY_BUTTON_SELECTOR ) . dxButton ( 'instance' ) ;
2793+ const clearButton = $ ( instance . content ( ) ) . parent ( ) . find ( CLEAR_BUTTON_SELECTOR ) . dxButton ( 'instance' ) ;
2794+ const cancelButton = $ ( instance . content ( ) ) . parent ( ) . find ( CANCEL_BUTTON_SELECTOR ) . dxButton ( 'instance' ) ;
2795+
2796+ assert . notOk ( doneButton . option ( 'focusStateEnabled' ) , 'done button has focusStateEnabled=false' ) ;
2797+ assert . notOk ( clearButton . option ( 'focusStateEnabled' ) , 'clear button has focusStateEnabled=false' ) ;
2798+ assert . notOk ( cancelButton . option ( 'focusStateEnabled' ) , 'cancel button has focusStateEnabled=false' ) ;
2799+ } ) ;
2800+ } ) ;
2801+
26572802QUnit . module ( 'Native scrolling' , ( ) => {
26582803 QUnit . test ( 'After load new page scrollTop should not be changed' , function ( assert ) {
26592804 const data = [ ] ;
@@ -2910,25 +3055,6 @@ QUnit.module('keyboard navigation', {
29103055 assert . ok ( instance . _$list . find ( '.dx-list-item' ) . first ( ) . hasClass ( FOCUSED_CLASS ) , 'list-item is focused after down key pressing' ) ;
29113056 } ) ;
29123057
2913- QUnit . testInActiveWindow ( 'lookup-list keyboard navigation should work after focusing on list' , function ( assert ) {
2914- const $element = $ ( '#widget' ) . dxLookup ( {
2915- opened : true ,
2916- items : [ 1 , 2 , 3 ] ,
2917- focusStateEnabled : true ,
2918- searchEnabled : true
2919- } ) ;
2920- const instance = $element . dxLookup ( 'instance' ) ;
2921-
2922- instance . _$list . dxList ( 'focus' ) ;
2923- assert . ok ( instance . _$list . find ( `.${ LIST_ITEM_CLASS } ` ) . eq ( 0 ) . hasClass ( FOCUSED_CLASS ) , 'list-item is focused after focusing on list' ) ;
2924-
2925- const $listItemContainer = instance . _$list . find ( `.${ LIST_ITEMS_CLASS } ` ) . parent ( ) ;
2926- const keyboard = keyboardMock ( $listItemContainer ) ;
2927- keyboard . keyDown ( 'down' ) ;
2928-
2929- assert . ok ( instance . _$list . find ( `.${ LIST_ITEM_CLASS } ` ) . eq ( 1 ) . hasClass ( FOCUSED_CLASS ) , 'second list-item is focused after down key pressing' ) ;
2930- } ) ;
2931-
29323058 [ true , false ] . forEach ( value => {
29333059 QUnit . test ( `focus from last Popover element should ${ value ? 'not' : '' } move to Lookup field while keeping Popup open when usePopover: true and _scrollToSelectedItemEnabled: ${ value } ` , function ( assert ) {
29343060 const $element = $ ( '#widget' ) . dxLookup ( {
@@ -3545,7 +3671,7 @@ if(devices.real().deviceType === 'desktop') {
35453671 QUnit . test ( `opened: true, searchEnabled: ${ searchEnabled } ` , function ( ) {
35463672 helper . createWidget ( {
35473673 opened : true ,
3548- searchEnabled
3674+ searchEnabled,
35493675 } ) ;
35503676
35513677 const localizedRoleDescription = messageLocalization . format ( 'dxList-ariaRoleDescription' ) ;
@@ -3561,7 +3687,7 @@ if(devices.real().deviceType === 'desktop') {
35613687 } ;
35623688
35633689 const listItemContainerAttributes = {
3564- tabindex : '0' ,
3690+ tabindex : searchEnabled ? '-1' : '0' ,
35653691 role : 'application' ,
35663692 } ;
35673693
@@ -3601,9 +3727,12 @@ if(devices.real().deviceType === 'desktop') {
36013727 helper . checkAttributes ( $input , expectedAttributes , 'input' ) ;
36023728 }
36033729
3604- helper . widget . option ( 'searchEnabled' , ! searchEnabled ) ;
3730+ const newSearchEnabled = ! searchEnabled ;
3731+
3732+ helper . widget . option ( 'searchEnabled' , newSearchEnabled ) ;
36053733
36063734 listAttributes . id = helper . widget . _listId ;
3735+ listItemContainerAttributes . tabindex = newSearchEnabled ? '-1' : '0' ;
36073736
36083737 fieldAttributes = {
36093738 role : 'combobox' ,
@@ -3621,8 +3750,10 @@ if(devices.real().deviceType === 'desktop') {
36213750 id : helper . widget . _popupContentId ,
36223751 } ;
36233752
3753+ const scrollView = $list . find ( `.${ SCROLL_VIEW_CONTENT_CLASS } ` ) ;
3754+
36243755 helper . checkAttributes ( $list , listAttributes , 'list' ) ;
3625- helper . checkAttributes ( $list . find ( `. ${ SCROLL_VIEW_CONTENT_CLASS } ` ) , listItemContainerAttributes , 'scrollview content' ) ;
3756+ helper . checkAttributes ( scrollView , listItemContainerAttributes , 'scrollview content' ) ;
36263757 helper . checkAttributes ( $field , fieldAttributes , 'field' ) ;
36273758 helper . checkAttributes ( helper . $widget , widgetAttributes , 'widget' ) ;
36283759 helper . checkAttributes ( helper . widget . _popup . $content ( ) , popupContentAttributes , 'popupContent' ) ;
@@ -3714,15 +3845,15 @@ if(devices.real().deviceType === 'desktop') {
37143845 const $scrollView = $list . find ( `.${ SCROLL_VIEW_CONTENT_CLASS } ` ) ;
37153846 const $itemsContainer = $list . find ( `.${ LIST_ITEMS_CLASS } ` ) ;
37163847
3717- helper . checkAttributes ( $scrollView , { tabindex : '0 ' , role : 'application' } ) ;
3848+ helper . checkAttributes ( $scrollView , { tabindex : '-1 ' , role : 'application' } ) ;
37183849 helper . checkAttributes ( $itemsContainer , { } ) ;
37193850
37203851 helper . widget . option ( dataSourcePropertyName , [ 1 , 2 , 3 ] ) ;
3721- helper . checkAttributes ( $scrollView , { tabindex : '0 ' , role : 'application' } ) ;
3852+ helper . checkAttributes ( $scrollView , { tabindex : '-1 ' , role : 'application' } ) ;
37223853 helper . checkAttributes ( $itemsContainer , { 'aria-label' : 'Items' , role : 'listbox' } ) ;
37233854
37243855 helper . widget . option ( dataSourcePropertyName , [ ] ) ;
3725- helper . checkAttributes ( $scrollView , { tabindex : '0 ' , role : 'application' } ) ;
3856+ helper . checkAttributes ( $scrollView , { tabindex : '-1 ' , role : 'application' } ) ;
37263857 helper . checkAttributes ( $itemsContainer , { } ) ;
37273858 } ) ;
37283859 } ) ;
0 commit comments