Skip to content

Commit

Permalink
feat(select): select search cosmetic and behavior fixes (#136)
Browse files Browse the repository at this point in the history
  • Loading branch information
roll314 authored and pimenovoleg committed Jun 11, 2019
1 parent 930669d commit a25a60b
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ describe('OverlayKeyboardDispatcher', () => {
expect(overlayTwoSpy).toHaveBeenCalled();
});

it('should dispatch keyboard events when propagation is stopped', () => {
it('should not dispatch keyboard events when propagation is stopped', () => {
const overlayRef = overlay.create();
const spy = jasmine.createSpy('keyboard event spy');
const button = document.createElement('button');
Expand All @@ -84,7 +84,7 @@ describe('OverlayKeyboardDispatcher', () => {
keyboardDispatcher.add(overlayRef);
dispatchKeyboardEvent(button, 'keydown', ESCAPE);

expect(spy).toHaveBeenCalled();
expect(spy).not.toHaveBeenCalled();

button.parentNode!.removeChild(button); //tslint:disable-line
});
Expand Down Expand Up @@ -140,10 +140,10 @@ describe('OverlayKeyboardDispatcher', () => {
spyOn(body, 'removeEventListener');

keyboardDispatcher.add(overlayRef);
expect(body.addEventListener).toHaveBeenCalledWith('keydown', jasmine.any(Function), true);
expect(body.addEventListener).toHaveBeenCalledWith('keydown', jasmine.any(Function));

overlayRef.dispose();
expect(body.removeEventListener).toHaveBeenCalledWith('keydown', jasmine.any(Function), true);
expect(body.removeEventListener).toHaveBeenCalledWith('keydown', jasmine.any(Function));
});

});
Expand Down
4 changes: 2 additions & 2 deletions packages/cdk/overlay/keyboard/overlay-keyboard-dispatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class OverlayKeyboardDispatcher implements OnDestroy {
add(overlayRef: OverlayRef): void {
// Lazily start dispatcher once first overlay is added
if (!this._isAttached) {
this._document.body.addEventListener('keydown', this._keydownListener, true);
this._document.body.addEventListener('keydown', this._keydownListener);
this._isAttached = true;
}

Expand All @@ -61,7 +61,7 @@ export class OverlayKeyboardDispatcher implements OnDestroy {
/** Detaches the global keyboard event listener. */
private _detach() {
if (this._isAttached) {
this._document.body.removeEventListener('keydown', this._keydownListener, true);
this._document.body.removeEventListener('keydown', this._keydownListener);
this._isAttached = false;
}
}
Expand Down
3 changes: 1 addition & 2 deletions packages/mosaic-dev/select/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,9 @@ export class DemoComponent implements OnInit {
multipleSearchCtrl: FormControl = new FormControl();
filteredMultipleOptions: Observable<string[]>;

allOptions = OPTIONS;
optionCounter = 0;

private options: string[] = OPTIONS;
options: string[] = OPTIONS.sort();

ngOnInit(): void {
this.filteredOptions = merge(
Expand Down
4 changes: 1 addition & 3 deletions packages/mosaic-dev/select/template.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,13 @@
<mc-cleaner></mc-cleaner>
</mc-form-field>

<mc-option value="StickedOption">Sticked Option</mc-option>
<mc-option *ngFor="let option of filteredOptions | async" [value]="option">{{ option }}</mc-option>
</mc-select>
</mc-form-field>

<br><br>

<button mc-button (click)="optionCounter = optionCounter + 1;allOptions.push(optionCounter.toString())">add option</button>
<button mc-button (click)="optionCounter = optionCounter + 1;options.push(optionCounter.toString())">add option</button>

<br><br>

Expand All @@ -60,7 +59,6 @@
<mc-cleaner></mc-cleaner>
</mc-form-field>

<mc-option value="StickedOption">Sticked Option</mc-option>
<mc-option *ngFor="let option of filteredMultipleOptions | async" [value]="option">{{ option }}</mc-option>
</mc-select>
</mc-form-field>
Expand Down
4 changes: 3 additions & 1 deletion packages/mosaic/form-field/form-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ export class McFormField extends _McFormFieldMixinBase implements

hovered: boolean = false;

canCleanerClearByEsc: boolean = true;

constructor(public _elementRef: ElementRef, private _changeDetectorRef: ChangeDetectorRef) {
super(_elementRef);
}
Expand Down Expand Up @@ -155,7 +157,7 @@ export class McFormField extends _McFormFieldMixinBase implements

onKeyDown(event: KeyboardEvent): void {
// tslint:disable-next-line:deprecation
if (event.keyCode === ESCAPE && this._control.focused && this.hasCleaner) {
if (this.canCleanerClearByEsc && event.keyCode === ESCAPE && this._control.focused && this.hasCleaner) {
if (this._control && this._control.ngControl) {
this._control.ngControl.reset();
}
Expand Down
4 changes: 4 additions & 0 deletions packages/mosaic/select/_select-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
background-color: if($is-dark, map-get($second, 700), map-get($background, background));
}

.mc-select__no-options-message {
color: mc-color($second, 400);
}

.mc-select__search-container {
border-bottom: {
width: 1px;
Expand Down
39 changes: 37 additions & 2 deletions packages/mosaic/select/select.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import {
SPACE,
TAB,
UP_ARROW,
A
A, ESCAPE
} from '@ptsecurity/cdk/keycodes';
import { OverlayContainer } from '@ptsecurity/cdk/overlay';
import { Platform } from '@ptsecurity/cdk/platform';
Expand Down Expand Up @@ -235,7 +235,7 @@ class SelectWithChangeEvent {
selector: 'select-with-search',
template: `
<mc-form-field>
<mc-select [(value)]="singleSelectedWithSearch">
<mc-select #select [(value)]="singleSelectedWithSearch">
<mc-form-field mcSelectSearch>
<input
mcInput
Expand All @@ -249,6 +249,8 @@ class SelectWithChangeEvent {
`
})
class SelectWithSearch {
@ViewChild(McSelect, {static: false}) select: McSelect;

singleSelectedWithSearch = 'Moscow';

searchCtrl: FormControl = new FormControl();
Expand Down Expand Up @@ -2294,6 +2296,39 @@ describe('McSelect', () => {

expect(optionsTexts).toEqual(['Kaluga', 'Luga']);
}));

it('should clear search by esc', (() => {
trigger.click();
fixture.detectChanges();

const inputElementDebug = fixture.debugElement.query(By.css('input'));

inputElementDebug.nativeElement.value = 'lu';

inputElementDebug.triggerEventHandler('input', { target: inputElementDebug.nativeElement });
fixture.detectChanges();

dispatchKeyboardEvent(inputElementDebug.nativeElement, 'keydown', ESCAPE);

fixture.detectChanges();

expect(inputElementDebug.nativeElement.value).toBe('');
}));

it('should close list by esc if input is empty', () => {
trigger.click();
fixture.detectChanges();

const inputElementDebug = fixture.debugElement.query(By.css('input'));

dispatchKeyboardEvent(inputElementDebug.nativeElement, 'keydown', ESCAPE);

fixture.detectChanges();

const selectInstance = fixture.componentInstance.select;

expect(selectInstance.panelOpen).toBe(false);
});
});

describe('with a selectionChange event handler', () => {
Expand Down
50 changes: 42 additions & 8 deletions packages/mosaic/select/select.component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/* tslint:disable:no-empty */

import {
AfterContentInit, AfterViewInit,
AfterContentInit,
AfterViewInit,
Attribute,
ChangeDetectionStrategy,
ChangeDetectorRef,
Expand All @@ -21,10 +22,12 @@ import {
OnInit,
Optional,
Output,
QueryList, Renderer2,
QueryList,
Renderer2,
Self,
SimpleChanges,
ViewChild, ViewChildren,
ViewChild,
ViewChildren,
ViewEncapsulation
} from '@angular/core';
import { ControlValueAccessor, FormGroupDirective, NgControl, NgForm } from '@angular/forms';
Expand All @@ -42,6 +45,7 @@ import {
SPACE,
UP_ARROW,
A,
ESCAPE,
PAGE_UP,
PAGE_DOWN
} from '@ptsecurity/cdk/keycodes';
Expand Down Expand Up @@ -118,7 +122,10 @@ const McSelectMixinBase: CanDisableCtor & HasTabIndexCtor & CanUpdateErrorStateC

@Directive({
selector: '[mcSelectSearch]',
exportAs: 'mcSelectSearch'
exportAs: 'mcSelectSearch',
host: {
'(keydown)': 'handleKeydown($event)'
}
})
export class McSelectSearch implements AfterContentInit, OnDestroy {
@ContentChild(McInput, {static: false}) input: McInput;
Expand All @@ -127,6 +134,10 @@ export class McSelectSearch implements AfterContentInit, OnDestroy {

isSearchChanged: boolean = false;

constructor(formField: McFormField) {
formField.canCleanerClearByEsc = false;
}

focus(): void {
this.input.focus();
}
Expand All @@ -137,7 +148,7 @@ export class McSelectSearch implements AfterContentInit, OnDestroy {

ngAfterContentInit(): void {
if (!this.input) {
throw Error('McSelectSearch does not work without input');
throw Error('McSelectSearch does not work without mcInput');
}

if (!this.input.ngControl) {
Expand All @@ -152,8 +163,17 @@ export class McSelectSearch implements AfterContentInit, OnDestroy {
ngOnDestroy(): void {
this.searchChangesSubscription.unsubscribe();
}
}

handleKeydown(event: KeyboardEvent) {
// tslint:disable-next-line:deprecation
if (event.keyCode === ESCAPE) {
if (this.input.value) {
this.reset();
event.stopPropagation();
}
}
}
}


@Directive({ selector: 'mc-select-trigger' })
Expand Down Expand Up @@ -197,7 +217,9 @@ export class McSelect extends McSelectMixinBase implements
controlType = 'mc-select';

hiddenItems: number = 0;
// todo localization
oneMoreText: string = '...ещё';
noOptionsText: string = 'Ничего не найдено';

/** The last measured value for the trigger's client bounding rect. */
triggerRect: ClientRect;
Expand Down Expand Up @@ -435,6 +457,12 @@ export class McSelect extends McSelectMixinBase implements
return this._panelOpen;
}

get isEmptySearchResult(): boolean {
return this.search &&
this.options.length === 0 &&
!!this.search.input.value;
}

private _panelOpen = false;

/** The scroll position of the overlay panel, calculated to center the selected option. */
Expand Down Expand Up @@ -549,6 +577,12 @@ export class McSelect extends McSelectMixinBase implements
resetSearch(): void {
if (this.search) {
this.search.reset();
/*
todo the incorrect behaviour of keyManager is possible here
to avoid first item selection (to provide correct options flipping on closed select)
we should process options update like it is the first options appearance
*/
this.search.isSearchChanged = false;
}
}

Expand Down Expand Up @@ -584,13 +618,13 @@ export class McSelect extends McSelectMixinBase implements
this.overlayDir.overlayRef.overlayElement.style.fontSize = `${this.triggerFontSize}px`;
}
});

this.resetSearch();
}

/** Closes the overlay panel and focuses the host element. */
close(): void {
if (this._panelOpen) {
// the order of calls is important
this.resetSearch();
this._panelOpen = false;
this.keyManager.withHorizontalOrientation(this.isRtl() ? 'rtl' : 'ltr');

Expand Down
1 change: 1 addition & 0 deletions packages/mosaic/select/select.html
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
[@fadeInContent]="'showing'"
(@fadeInContent.done)="onFadeInDone()">

<div *ngIf="isEmptySearchResult" class="mc-select__no-options-message">{{noOptionsText}}</div>
<ng-content select="mc-option,mc-optgroup"></ng-content>
</div>
</div>
Expand Down
18 changes: 17 additions & 1 deletion packages/mosaic/select/select.scss
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,19 @@ $mc-select-placeholder-arrow-space: 2 * ($mc-select-arrow-size + $mc-select-arro
}
}

.mc-select__no-options-message {
display: flex;
flex-direction: row;
align-items: center;
box-sizing: border-box;
position: relative;
max-width: 100%;
height: 32px;
cursor: default;
outline: none;
padding: 0 16px;
}

.mc-select__trigger {
display: flex;
box-sizing: border-box;
Expand All @@ -58,6 +71,9 @@ $mc-select-placeholder-arrow-space: 2 * ($mc-select-arrow-size + $mc-select-arro
overflow: hidden;
text-overflow: ellipsis;

// compensate borders
margin-top: -2px;

white-space: nowrap;

& > span {
Expand All @@ -71,7 +87,7 @@ $mc-select-placeholder-arrow-space: 2 * ($mc-select-arrow-size + $mc-select-arro

overflow: hidden;

max-height: 24px;
max-height: 26px;

margin: 0;
padding-left: 0;
Expand Down

0 comments on commit a25a60b

Please sign in to comment.