Skip to content

Commit bbcdf88

Browse files
authored
Merge pull request #7052 from IgniteUI/vmihalkov/splitter-keyboard
Splitter keyboard interactions
2 parents d6de681 + 5fd2317 commit bbcdf88

File tree

2 files changed

+191
-2
lines changed

2 files changed

+191
-2
lines changed

projects/igniteui-angular/src/lib/splitter/splitter-bar/splitter-bar.component.ts

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
import { Component, Input, HostBinding, EventEmitter, Output } from '@angular/core';
1+
import { Component, Input, HostBinding, EventEmitter, Output, HostListener } from '@angular/core';
22
import { SplitterType } from '../splitter.component';
33
import { IgxSplitterPaneComponent } from '../splitter-pane/splitter-pane.component';
44
import { IDragMoveEventArgs, IDragStartEventArgs, DragDirection } from '../../directives/drag-drop/drag-drop.directive';
55

6+
7+
export const SPLITTER_INTERACTION_KEYS = new Set('right down left up arrowright arrowdown arrowleft arrowup'.split(' '));
8+
69
/**
710
* Provides reference to `SplitBarComponent` component.
811
* Represents the draggable gripper that visually separates panes and allows for changing their sizes.
@@ -30,6 +33,12 @@ export class IgxSplitBarComponent {
3033
@Input()
3134
public order!: number;
3235

36+
/**
37+
* @hidden
38+
* @internal
39+
*/
40+
@HostBinding('attr.tabindex')
41+
public tabindex = 0;
3342

3443
/**
3544
* Sets/gets the `SplitPaneComponent` associated with the current `SplitBarComponent`.
@@ -77,6 +86,79 @@ export class IgxSplitBarComponent {
7786
return this.siblings[0].hidden && !this.siblings[1].hidden;
7887
}
7988

89+
/**
90+
* @hidden @internal
91+
*/
92+
@HostListener('keydown', ['$event'])
93+
keyEvent(event: KeyboardEvent) {
94+
const key = event.key.toLowerCase();
95+
const ctrl = event.ctrlKey;
96+
event.stopPropagation();
97+
if (SPLITTER_INTERACTION_KEYS.has(key)) {
98+
event.preventDefault();
99+
}
100+
switch (key) {
101+
case 'arrowup':
102+
case 'up':
103+
if (this.type === SplitterType.Vertical) {
104+
if (ctrl) {
105+
this.onCollapsing(false);
106+
break;
107+
}
108+
if (!this.resizeDisallowed) {
109+
event.preventDefault();
110+
this.moveStart.emit(this.pane);
111+
this.moving.emit(10);
112+
}
113+
}
114+
break;
115+
case 'arrowdown':
116+
case 'down':
117+
if (this.type === SplitterType.Vertical) {
118+
if (ctrl) {
119+
this.onCollapsing(true);
120+
break;
121+
}
122+
if (!this.resizeDisallowed) {
123+
event.preventDefault();
124+
this.moveStart.emit(this.pane);
125+
this.moving.emit(-10);
126+
}
127+
}
128+
break;
129+
case 'arrowleft':
130+
case 'left':
131+
if (this.type === SplitterType.Horizontal) {
132+
if (ctrl) {
133+
this.onCollapsing(false);
134+
break;
135+
}
136+
if (!this.resizeDisallowed) {
137+
event.preventDefault();
138+
this.moveStart.emit(this.pane);
139+
this.moving.emit(10);
140+
}
141+
}
142+
break;
143+
case 'arrowright':
144+
case 'right':
145+
if (this.type === SplitterType.Horizontal) {
146+
if (ctrl) {
147+
this.onCollapsing(true);
148+
break;
149+
}
150+
if (!this.resizeDisallowed) {
151+
event.preventDefault();
152+
this.moveStart.emit(this.pane);
153+
this.moving.emit(-10);
154+
}
155+
}
156+
break;
157+
default:
158+
break;
159+
}
160+
}
161+
80162
/**
81163
* @hidden @internal
82164
*/
@@ -90,6 +172,7 @@ export class IgxSplitBarComponent {
90172
public get nextButtonHidden() {
91173
return this.siblings[1].hidden && !this.siblings[0].hidden;
92174
}
175+
93176
public onDragStart(event: IDragStartEventArgs) {
94177
if (this.resizeDisallowed) {
95178
event.cancel = true;

projects/igniteui-angular/src/lib/splitter/splitter.component.spec.ts

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { IgxSplitterModule } from './splitter.module';
22
import { configureTestSuite } from '../test-utils/configure-suite';
33
import { TestBed, async } from '@angular/core/testing';
4-
import { Component, ViewChild } from '@angular/core';
4+
import { Component, ViewChild, DebugElement } from '@angular/core';
55
import { SplitterType, IgxSplitterComponent } from './splitter.component';
66
import { By } from '@angular/platform-browser';
7+
import { UIInteractions } from '../test-utils/ui-interactions.spec';
78

89

910
const SPLITTERBAR_CLASS = 'igx-splitter-bar';
@@ -131,6 +132,111 @@ describe('IgxSplitter', () => {
131132
splitterBarComponent.onDragStart(args);
132133
expect(args.cancel).toBeTruthy();
133134
});
135+
136+
it('should allow resizing with up/down arrow keys', () => {
137+
fixture.componentInstance.type = SplitterType.Vertical;
138+
fixture.detectChanges();
139+
const pane1 = splitter.panes.toArray()[0];
140+
const pane2 = splitter.panes.toArray()[1];
141+
expect(pane1.size).toBe('auto');
142+
expect(pane2.size).toBe('auto');
143+
const pane1_originalSize = pane1.element.offsetHeight;
144+
const pane2_originalSize = pane2.element.offsetHeight;
145+
const splitterBarComponent: DebugElement = fixture.debugElement.query(By.css(SPLITTERBAR_CLASS));
146+
splitterBarComponent.nativeElement.focus();
147+
UIInteractions.triggerEventHandlerKeyDown('ArrowUp', splitterBarComponent);
148+
fixture.detectChanges();
149+
expect(pane1.size).toBe(pane1_originalSize - 10 + 'px');
150+
expect(pane2.size).toBe(pane2_originalSize + 10 + 'px');
151+
152+
UIInteractions.triggerEventHandlerKeyDown('ArrowDown', splitterBarComponent);
153+
UIInteractions.triggerEventHandlerKeyDown('ArrowDown', splitterBarComponent);
154+
fixture.detectChanges();
155+
expect(pane1.size).toBe(pane1_originalSize + 10 + 'px');
156+
expect(pane2.size).toBe(pane2_originalSize - 10 + 'px');
157+
158+
pane2.resizable = false;
159+
UIInteractions.triggerEventHandlerKeyDown('ArrowDown', splitterBarComponent);
160+
fixture.detectChanges();
161+
expect(pane1.size).toBe(pane1_originalSize + 10 + 'px');
162+
expect(pane2.size).toBe(pane2_originalSize - 10 + 'px');
163+
});
164+
165+
it('should allow resizing with left/right arrow keys', () => {
166+
fixture.componentInstance.type = SplitterType.Horizontal;
167+
fixture.detectChanges();
168+
const pane1 = splitter.panes.toArray()[0];
169+
const pane2 = splitter.panes.toArray()[1];
170+
expect(pane1.size).toBe('auto');
171+
expect(pane2.size).toBe('auto');
172+
const pane1_originalSize = pane1.element.offsetWidth;
173+
const pane2_originalSize = pane2.element.offsetWidth;
174+
const splitterBarComponent: DebugElement = fixture.debugElement.query(By.css(SPLITTERBAR_CLASS));
175+
splitterBarComponent.nativeElement.focus();
176+
UIInteractions.triggerEventHandlerKeyDown('ArrowLeft', splitterBarComponent);
177+
fixture.detectChanges();
178+
expect(parseFloat(pane1.size)).toBeCloseTo(pane1_originalSize - 10, 0);
179+
expect(parseFloat(pane2.size)).toBeCloseTo(pane2_originalSize + 10, 0);
180+
181+
UIInteractions.triggerEventHandlerKeyDown('ArrowRight', splitterBarComponent);
182+
UIInteractions.triggerEventHandlerKeyDown('ArrowRight', splitterBarComponent);
183+
fixture.detectChanges();
184+
expect(parseFloat(pane1.size)).toBeCloseTo(pane1_originalSize + 10, 0);
185+
expect(parseFloat(pane2.size)).toBeCloseTo(pane2_originalSize - 10, 0);
186+
187+
pane1.resizable = false;
188+
UIInteractions.triggerEventHandlerKeyDown('ArrowRight', splitterBarComponent);
189+
fixture.detectChanges();
190+
expect(parseFloat(pane1.size)).toBeCloseTo(pane1_originalSize + 10, 0);
191+
expect(parseFloat(pane2.size)).toBeCloseTo(pane2_originalSize - 10, 0);
192+
});
193+
194+
it('should allow expand/collapse with Ctrl + up/down arrow keys', () => {
195+
fixture.componentInstance.type = SplitterType.Vertical;
196+
fixture.detectChanges();
197+
const pane1 = splitter.panes.toArray()[0];
198+
const pane2 = splitter.panes.toArray()[1];
199+
expect(pane1.size).toBe('auto');
200+
expect(pane2.size).toBe('auto');
201+
const splitterBarComponent: DebugElement = fixture.debugElement.query(By.css(SPLITTERBAR_CLASS));
202+
splitterBarComponent.nativeElement.focus();
203+
UIInteractions.triggerEventHandlerKeyDown('ArrowUp', splitterBarComponent, false, false, true);
204+
fixture.detectChanges();
205+
expect(pane1.hidden).toBeTruthy();
206+
UIInteractions.triggerEventHandlerKeyDown('ArrowDown', splitterBarComponent, false, false, true);
207+
fixture.detectChanges();
208+
expect(pane1.hidden).toBeFalsy();
209+
UIInteractions.triggerEventHandlerKeyDown('ArrowDown', splitterBarComponent, false, false, true);
210+
fixture.detectChanges();
211+
expect(pane2.hidden).toBeTruthy();
212+
UIInteractions.triggerEventHandlerKeyDown('ArrowUp', splitterBarComponent, false, false, true);
213+
fixture.detectChanges();
214+
expect(pane2.hidden).toBeFalsy();
215+
});
216+
217+
it('should allow expand/collapse with Ctrl + left/right arrow keys', () => {
218+
fixture.componentInstance.type = SplitterType.Horizontal;
219+
fixture.detectChanges();
220+
const pane1 = splitter.panes.toArray()[0];
221+
const pane2 = splitter.panes.toArray()[1];
222+
expect(pane1.size).toBe('auto');
223+
expect(pane2.size).toBe('auto');
224+
const splitterBarComponent: DebugElement = fixture.debugElement.query(By.css(SPLITTERBAR_CLASS));
225+
splitterBarComponent.nativeElement.focus();
226+
UIInteractions.triggerEventHandlerKeyDown('ArrowLeft', splitterBarComponent, false, false, true);
227+
fixture.detectChanges();
228+
expect(pane1.hidden).toBeTruthy();
229+
UIInteractions.triggerEventHandlerKeyDown('ArrowRight', splitterBarComponent, false, false, true);
230+
fixture.detectChanges();
231+
expect(pane1.hidden).toBeFalsy();
232+
UIInteractions.triggerEventHandlerKeyDown('ArrowRight', splitterBarComponent, false, false, true);
233+
fixture.detectChanges();
234+
expect(pane2.hidden).toBeTruthy();
235+
UIInteractions.triggerEventHandlerKeyDown('ArrowLeft', splitterBarComponent, false, false, true);
236+
fixture.detectChanges();
237+
expect(pane2.hidden).toBeFalsy();
238+
});
239+
134240
});
135241

136242
describe('IgxSplitter pane toggle', () => {

0 commit comments

Comments
 (0)