Skip to content

Commit 4233d76

Browse files
committed
Tests
1 parent 4e20f38 commit 4233d76

File tree

2 files changed

+125
-55
lines changed

2 files changed

+125
-55
lines changed

src/cdk-experimental/popover-edit/popover-edit.spec.ts

Lines changed: 121 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,13 @@ const NAME_EDIT_TEMPLATE = `
2828
</div>
2929
`;
3030

31-
const WEIGHT_EDIT_TEMPLATE = `<div> Just a placeholder </div>`;
31+
const WEIGHT_EDIT_TEMPLATE = `
32+
<div>
33+
<form #f="ngForm" cdkEditControl>
34+
<input>
35+
</form>
36+
</div>
37+
`;
3238

3339
const CELL_TEMPLATE = `
3440
{{element.name}}
@@ -40,7 +46,7 @@ const CELL_TEMPLATE = `
4046

4147
const POPOVER_EDIT_DIRECTIVE_NAME = `[cdkPopoverEdit]="nameEdit" [cdkPopoverEditColspan]="colspan"`;
4248

43-
const POPOVER_EDIT_DIRECTIVE_WEIGHT = `[cdkPopoverEdit]="weightEdit"`;
49+
const POPOVER_EDIT_DIRECTIVE_WEIGHT = `[cdkPopoverEdit]="weightEdit" cdkPopoverEditTabOut`;
4450

4551
interface PeriodicElement {
4652
name: string;
@@ -74,13 +80,13 @@ abstract class BaseTestComponent {
7480
return getRows(this.table.nativeElement);
7581
}
7682

77-
getEditCell(rowIndex = 0) {
83+
getEditCell(rowIndex = 0, cellIndex = 1) {
7884
const row = this.getRows()[rowIndex];
79-
return getCells(row)[1];
85+
return getCells(row)[cellIndex];
8086
}
8187

82-
focusEditCell(rowIndex = 0) {
83-
this.getEditCell(rowIndex).focus();
88+
focusEditCell(rowIndex = 0, cellIndex = 1) {
89+
this.getEditCell(rowIndex, cellIndex).focus();
8490
}
8591

8692
getOpenButton(rowIndex = 0) {
@@ -91,17 +97,21 @@ abstract class BaseTestComponent {
9197
this.getOpenButton(rowIndex)!.click();
9298
}
9399

94-
openLens(rowIndex = 0) {
95-
this.focusEditCell(rowIndex);
96-
this.getEditCell(rowIndex).dispatchEvent(
97-
new KeyboardEvent('keyup', {bubbles: true, key: 'Enter'}));
100+
openLens(rowIndex = 0, cellIndex = 1) {
101+
this.focusEditCell(rowIndex, cellIndex);
102+
this.getEditCell(rowIndex, cellIndex)
103+
.dispatchEvent(new KeyboardEvent('keyup', {bubbles: true, key: 'Enter'}));
98104
flush();
99105
}
100106

101107
getEditPane() {
102108
return document.querySelector('.cdk-edit-pane');
103109
}
104110

111+
getEditBoundingBox() {
112+
return document.querySelector('.cdk-overlay-connected-position-bounding-box');
113+
}
114+
105115
getInput() {
106116
return document.querySelector('input') as HTMLInputElement|null;
107117
}
@@ -366,68 +376,125 @@ describe('CDK Popover Edit', () => {
366376
}));
367377
});
368378

369-
describe('arrow key focus manipulation', () => {
370-
const dispatchKey = (cell: HTMLElement, keyCode: number) =>
371-
dispatchKeyboardEvent(cell, 'keydown', keyCode, cell);
372-
379+
describe('focus manipulation', () => {
373380
const getRowCells = () => component.getRows().map(getCells);
374381

375-
it('moves focus up/down/left/right and prevents default', () => {
376-
const rowCells = getRowCells();
382+
describe('arrow keys', () => {
383+
const dispatchKey = (cell: HTMLElement, keyCode: number) =>
384+
dispatchKeyboardEvent(cell, 'keydown', keyCode, cell);
377385

378-
// Focus the upper-left editable cell.
379-
rowCells[0][1].focus();
386+
it('moves focus up/down/left/right and prevents default', () => {
387+
const rowCells = getRowCells();
380388

381-
const downEvent = dispatchKey(rowCells[0][1], DOWN_ARROW);
382-
expect(document.activeElement).toBe(rowCells[1][1]);
383-
expect(downEvent.defaultPrevented).toBe(true);
389+
// Focus the upper-left editable cell.
390+
rowCells[0][1].focus();
384391

385-
const rightEvent = dispatchKey(rowCells[1][1], RIGHT_ARROW);
386-
expect(document.activeElement).toBe(rowCells[1][2]);
387-
expect(rightEvent.defaultPrevented).toBe(true);
392+
const downEvent = dispatchKey(rowCells[0][1], DOWN_ARROW);
393+
expect(document.activeElement).toBe(rowCells[1][1]);
394+
expect(downEvent.defaultPrevented).toBe(true);
388395

389-
const upEvent = dispatchKey(rowCells[1][2], UP_ARROW);
390-
expect(document.activeElement).toBe(rowCells[0][2]);
391-
expect(upEvent.defaultPrevented).toBe(true);
396+
const rightEvent = dispatchKey(rowCells[1][1], RIGHT_ARROW);
397+
expect(document.activeElement).toBe(rowCells[1][2]);
398+
expect(rightEvent.defaultPrevented).toBe(true);
392399

393-
const leftEvent = dispatchKey(rowCells[0][2], LEFT_ARROW);
394-
expect(document.activeElement).toBe(rowCells[0][1]);
395-
expect(leftEvent.defaultPrevented).toBe(true);
396-
});
400+
const upEvent = dispatchKey(rowCells[1][2], UP_ARROW);
401+
expect(document.activeElement).toBe(rowCells[0][2]);
402+
expect(upEvent.defaultPrevented).toBe(true);
397403

398-
it('wraps around when reaching start or end of a row, skipping non-editable cells', () => {
399-
const rowCells = getRowCells();
404+
const leftEvent = dispatchKey(rowCells[0][2], LEFT_ARROW);
405+
expect(document.activeElement).toBe(rowCells[0][1]);
406+
expect(leftEvent.defaultPrevented).toBe(true);
407+
});
400408

401-
// Focus the upper-right editable cell.
402-
rowCells[0][2].focus();
409+
it('wraps around when reaching start or end of a row, skipping non-editable cells',
410+
() => {
411+
const rowCells = getRowCells();
403412

404-
dispatchKey(rowCells[0][2], RIGHT_ARROW);
405-
expect(document.activeElement).toBe(rowCells[1][1]);
413+
// Focus the upper-right editable cell.
414+
rowCells[0][2].focus();
406415

407-
dispatchKey(rowCells[1][1], LEFT_ARROW);
408-
expect(document.activeElement).toBe(rowCells[0][2]);
409-
});
416+
dispatchKey(rowCells[0][2], RIGHT_ARROW);
417+
expect(document.activeElement).toBe(rowCells[1][1]);
418+
419+
dispatchKey(rowCells[1][1], LEFT_ARROW);
420+
expect(document.activeElement).toBe(rowCells[0][2]);
421+
});
422+
423+
it('does not fall off top or bottom of the table', () => {
424+
const rowCells = getRowCells();
425+
426+
// Focus the upper-left editable cell.
427+
rowCells[0][1].focus();
410428

411-
it('does not fall off top or bottom of the table', () => {
412-
const rowCells = getRowCells();
429+
dispatchKey(rowCells[0][1], UP_ARROW);
430+
expect(document.activeElement).toBe(rowCells[0][1]);
413431

414-
// Focus the upper-left editable cell.
415-
rowCells[0][1].focus();
432+
// Focus the bottom-left editable cell.
433+
rowCells[4][1].focus();
434+
dispatchKey(rowCells[4][1], DOWN_ARROW);
435+
expect(document.activeElement).toBe(rowCells[4][1]);
436+
});
416437

417-
dispatchKey(rowCells[0][1], UP_ARROW);
418-
expect(document.activeElement).toBe(rowCells[0][1]);
438+
it('ignores non arrow key events', () => {
439+
component.focusEditCell();
440+
const cell = component.getEditCell();
419441

420-
// Focus the bottom-left editable cell.
421-
rowCells[4][1].focus();
422-
dispatchKey(rowCells[4][1], DOWN_ARROW);
423-
expect(document.activeElement).toBe(rowCells[4][1]);
442+
expect(dispatchKey(cell, TAB).defaultPrevented).toBe(false);
443+
});
424444
});
425445

426-
it('ignores non arrow key events', () => {
427-
component.focusEditCell();
428-
const cell = component.getEditCell();
446+
describe('lens focus trapping behavior', () => {
447+
const getFocusablePaneElements = () =>
448+
(Array.from(component.getEditBoundingBox()!.querySelectorAll('*')) as HTMLElement[])
449+
.filter(element => element.tabIndex >= 0);
450+
451+
it('keeps focus within the lens by default', fakeAsync(() => {
452+
// Open the name lens which has the default behavior.
453+
component.openLens();
454+
455+
const focusableElements = getFocusablePaneElements();
456+
457+
// Focus the last element (end focus trap anchor).
458+
console.log(focusableElements[focusableElements.length - 1]);
459+
focusableElements[focusableElements.length - 1].focus();
460+
flush();
461+
462+
// Focus should have moved to the top of the lens.
463+
expect(document.activeElement).toBe(focusableElements[1]);
464+
expect(component.lensIsOpen()).toBe(true);
465+
}));
466+
467+
it('moves focus to the next cell when focus leaves end of lens with cdkPopoverEditTabOut',
468+
fakeAsync(() => {
469+
// Open the weight lens which has tab out behavior.
470+
component.openLens(0, 2);
471+
472+
const focusableElements = getFocusablePaneElements();
473+
474+
// Focus the last element (end focus trap anchor).
475+
focusableElements[focusableElements.length - 1].focus();
476+
flush();
477+
478+
// Focus should have moved to the next editable cell.
479+
expect(document.activeElement).toBe(component.getEditCell(1, 1));
480+
expect(component.lensIsOpen()).toBe(false);
481+
}));
482+
483+
it('moves focus to the previous cell when focus leaves end of lens with cdkPopoverEditTabOut',
484+
fakeAsync(() => {
485+
// Open the weight lens which has tab out behavior.
486+
component.openLens(0, 2);
487+
488+
const focusableElements = getFocusablePaneElements();
489+
490+
// Focus the first (start focus trap anchor).
491+
focusableElements[0].focus();
492+
flush();
429493

430-
expect(dispatchKey(cell, TAB).defaultPrevented).toBe(false);
494+
// Focus should have moved to the next editable cell.
495+
expect(document.activeElement).toBe(component.getEditCell(0, 1));
496+
expect(component.lensIsOpen()).toBe(false);
497+
}));
431498
});
432499
});
433500

src/cdk-experimental/popover-edit/table-directives.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,10 @@ export class CdkPopoverEditTabOut<C> extends CdkPopoverEdit<C> {
321321
this.focusTrap = this.focusEscapeNotifierFactory.create(this.overlayRef!.overlayElement);
322322

323323
this.focusTrap.escapes().pipe(takeUntil(this.destroyed)).subscribe(direction => {
324-
this.editEventDispatcher.editRef!.blur();
324+
if (this.editEventDispatcher.editRef) {
325+
this.editEventDispatcher.editRef.blur()
326+
}
327+
325328
this.focusDispatcher.moveFocusHorizontally(
326329
closest(this.elementRef.nativeElement!, CELL_SELECTOR) as HTMLElement,
327330
direction === FocusEscapeNotifierDirection.START ? -1 : 1);

0 commit comments

Comments
 (0)