Skip to content

Commit

Permalink
fix(module:tree-select): selector cannot focus (#6073)
Browse files Browse the repository at this point in the history
close #6063
  • Loading branch information
hsuanxyz committed Nov 19, 2020
1 parent 3204927 commit 032bd01
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 12 deletions.
55 changes: 47 additions & 8 deletions components/tree-select/tree-select.component.ts
Expand Up @@ -3,7 +3,8 @@
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

import { BACKSPACE } from '@angular/cdk/keycodes';
import { FocusMonitor } from '@angular/cdk/a11y';
import { BACKSPACE, ESCAPE, TAB } from '@angular/cdk/keycodes';
import { CdkConnectedOverlay, CdkOverlayOrigin, ConnectedOverlayPositionChange } from '@angular/cdk/overlay';
import {
ChangeDetectorRef,
Expand Down Expand Up @@ -150,14 +151,14 @@ const TREE_SELECT_DEFAULT_CLASS = 'ant-select-dropdown ant-select-tree-dropdown'
</ng-container>
<nz-select-search
*ngIf="nzShowSearch"
[showInput]="nzShowSearch"
(keydown)="onKeyDownInput($event)"
(isComposingChange)="isComposing = $event"
(valueChange)="setInputValue($event)"
[value]="inputValue"
[mirrorSync]="isMultiple"
[disabled]="nzDisabled"
[showInput]="nzOpen"
[focusTrigger]="nzOpen"
></nz-select-search>
<nz-select-placeholder
Expand Down Expand Up @@ -192,17 +193,19 @@ const TREE_SELECT_DEFAULT_CLASS = 'ant-select-dropdown ant-select-tree-dropdown'
}
],
host: {
'[class.ant-select]': 'true',
'[class.ant-select-lg]': 'nzSize==="large"',
'[class.ant-select-sm]': 'nzSize==="small"',
'[class.ant-select-enabled]': '!nzDisabled',
'[class.ant-select-disabled]': 'nzDisabled',
'[class.ant-select-single]': '!isMultiple',
'[class.ant-select-show-arrow]': '!isMultiple',
'[class.ant-select-show-search]': '!isMultiple',
'[class.ant-select-multiple]': 'isMultiple',
'[class.ant-select-allow-clear]': 'nzAllowClear',
'[class.ant-select-open]': 'nzOpen',
'(click)': 'trigger()'
'[class.ant-select-focused]': 'nzOpen || focused',
'(click)': 'trigger()',
'(keydown)': 'onKeydown($event)'
}
})
export class NzTreeSelectComponent extends NzTreeBase implements ControlValueAccessor, OnInit, OnDestroy, OnChanges {
Expand Down Expand Up @@ -281,9 +284,11 @@ export class NzTreeSelectComponent extends NzTreeBase implements ControlValueAcc
isComposing = false;
isDestroy = true;
isNotFound = false;
focused = false;
inputValue = '';
dropDownPosition: 'top' | 'center' | 'bottom' = 'bottom';
selectionChangeSubscription!: Subscription;
focusChangeSubscription!: Subscription;
selectedNodes: NzTreeNode[] = [];
expandedKeys: string[] = [];
value: string[] = [];
Expand All @@ -305,6 +310,7 @@ export class NzTreeSelectComponent extends NzTreeBase implements ControlValueAcc
private renderer: Renderer2,
private cdr: ChangeDetectorRef,
private elementRef: ElementRef,
private focusMonitor: FocusMonitor,
@Host() @Optional() public noAnimation?: NzNoAnimationDirective
) {
super(nzTreeService);
Expand All @@ -315,12 +321,25 @@ export class NzTreeSelectComponent extends NzTreeBase implements ControlValueAcc
ngOnInit(): void {
this.isDestroy = false;
this.selectionChangeSubscription = this.subscribeSelectionChange();
this.focusChangeSubscription = this.focusMonitor.monitor(this.elementRef, true).subscribe(focusOrigin => {
if (!focusOrigin) {
this.focused = false;
this.cdr.markForCheck();
Promise.resolve().then(() => {
this.onTouched();
});
} else {
this.focused = true;
this.cdr.markForCheck();
}
});
}

ngOnDestroy(): void {
this.isDestroy = true;
this.closeDropDown();
this.selectionChangeSubscription.unsubscribe();
this.focusChangeSubscription.unsubscribe();
}

isComposingChange(isComposing: boolean): void {
Expand Down Expand Up @@ -369,14 +388,31 @@ export class NzTreeSelectComponent extends NzTreeBase implements ControlValueAcc
this.onTouched = fn;
}

onKeydown(event: KeyboardEvent): void {
if (this.nzDisabled) {
return;
}
switch (event.keyCode) {
case ESCAPE:
/**
* Skip the ESCAPE processing, it will be handled in {@link onOverlayKeyDown}.
*/
break;
case TAB:
this.closeDropDown();
break;
default:
if (!this.nzOpen) {
this.openDropdown();
}
}
}

trigger(): void {
if (this.nzDisabled || (!this.nzDisabled && this.nzOpen)) {
this.closeDropDown();
} else {
this.openDropdown();
if (this.nzShowSearch || this.isMultiple) {
this.focusOnInput();
}
}
}

Expand All @@ -385,6 +421,9 @@ export class NzTreeSelectComponent extends NzTreeBase implements ControlValueAcc
this.nzOpen = true;
this.nzOpenChange.emit(this.nzOpen);
this.updateCdkConnectedOverlayStatus();
if (this.nzShowSearch || this.isMultiple) {
this.focusOnInput();
}
}
}

Expand Down
51 changes: 47 additions & 4 deletions components/tree-select/tree-select.spec.ts
@@ -1,5 +1,7 @@
import { BACKSPACE } from '@angular/cdk/keycodes';
import { OverlayContainer } from '@angular/cdk/overlay';
import { TestKey } from '@angular/cdk/testing';
import { UnitTestElement } from '@angular/cdk/testing/testbed';
import { Component, DebugElement, NgZone, ViewChild } from '@angular/core';
import { ComponentFixture, fakeAsync, flush, inject, TestBed, tick, waitForAsync } from '@angular/core/testing';
import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
Expand Down Expand Up @@ -109,10 +111,8 @@ describe('tree-select component', () => {
fixture.detectChanges();
});
it('should disabled work', fakeAsync(() => {
expect(treeSelect.nativeElement.classList).toContain('ant-select-enabled');
testComponent.disabled = true;
fixture.detectChanges();
expect(treeSelect.nativeElement.classList).not.toContain('ant-select-enabled');
expect(treeSelect.nativeElement.classList).toContain('ant-select-disabled');
expect(treeSelectComponent.nzOpen).toBe(false);
treeSelect.nativeElement.click();
Expand Down Expand Up @@ -196,18 +196,61 @@ describe('tree-select component', () => {
flush();
expect(treeSelectComponent.nzOpen).toBe(false);
}));

it('should be focusable', fakeAsync(() => {
const focusTrigger = treeSelect.query(By.css('.ant-select-selection-search-input')).nativeElement;
expect(treeSelect.nativeElement.classList).not.toContain('ant-select-focused');
dispatchFakeEvent(focusTrigger, 'focus');
fixture.detectChanges();
flush();
fixture.detectChanges();
expect(treeSelect.nativeElement.classList).toContain('ant-select-focused');
}));

it('should open dropdown when keydown', fakeAsync(async () => {
const testElement = new UnitTestElement(treeSelect.nativeElement, async () => {
fixture.detectChanges();
flush();
fixture.detectChanges();
});
expect(treeSelectComponent.nzOpen).toBe(false);
await testElement.sendKeys(TestKey.ESCAPE);
expect(treeSelectComponent.nzOpen).toBe(false);

await testElement.sendKeys(TestKey.ENTER);
expect(treeSelectComponent.nzOpen).toBe(true);
}));

it('should close dropdown when TAB keydown', fakeAsync(async () => {
const testElement = new UnitTestElement(treeSelect.nativeElement, async () => {
fixture.detectChanges();
flush();
fixture.detectChanges();
});

treeSelectComponent.nzOpen = true;
fixture.detectChanges();
flush();
fixture.detectChanges();

await testElement.sendKeys(TestKey.TAB);
expect(treeSelectComponent.nzOpen).toBe(false);
}));

it('should showSearch work', fakeAsync(() => {
treeSelectComponent.updateSelectedNodes();
fixture.detectChanges();
testComponent.showSearch = true;
fixture.detectChanges();
treeSelect.nativeElement.click();
fixture.detectChanges();
expect(treeSelect.nativeElement.querySelector('nz-select-search')).toBeTruthy();
const searchInput = treeSelect.nativeElement.querySelector('nz-select-search .ant-select-selection-search-input');
expect(searchInput).toBeTruthy();
expect(searchInput.style.opacity).toBe('');
testComponent.showSearch = false;
fixture.detectChanges();
tick();
expect(treeSelect.nativeElement.querySelector('nz-select-search')).toBeNull();
expect(searchInput.style.opacity).toBe('0');
flush();
fixture.detectChanges();
}));
Expand Down

0 comments on commit 032bd01

Please sign in to comment.