Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(select): clear select if no option matches value #2110

Merged
merged 1 commit into from
Dec 16, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/demo-app/select/select-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<p> Status: {{ foodControl.status }} </p>
<button md-button (click)="foodControl.setValue('pizza-1')">SET VALUE</button>
<button md-button (click)="toggleDisabled()">TOGGLE DISABLED</button>
<button md-button (click)="foodControl.reset()">RESET</button>
</md-card>
</div>

Expand All @@ -30,6 +31,7 @@
<button md-button (click)="currentDrink='sprite-1'">SET VALUE</button>
<button md-button (click)="isRequired=!isRequired">TOGGLE REQUIRED</button>
<button md-button (click)="isDisabled=!isDisabled">TOGGLE DISABLED</button>
<button md-button (click)="drinkControl.reset()">RESET</button>
</md-card>

</div>
Expand Down
65 changes: 54 additions & 11 deletions src/lib/select/select.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,19 +193,19 @@ describe('MdSelect', () => {
});

it('should focus the selected option if an option is selected', async(() => {
trigger.click();
fixture.detectChanges();

const options =
overlayContainerElement.querySelectorAll('md-option') as NodeListOf<HTMLElement>;
options[1].click();
fixture.detectChanges();
// must wait for initial writeValue promise to finish
fixture.whenStable().then(() => {
fixture.componentInstance.control.setValue('pizza-1');
fixture.detectChanges();

trigger.click();
fixture.detectChanges();
trigger.click();
fixture.detectChanges();

fixture.whenStable().then(() => {
expect(fixture.componentInstance.select._keyManager.focusedItemIndex).toEqual(1);
// must wait for animation to finish
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(fixture.componentInstance.select._keyManager.focusedItemIndex).toEqual(1);
});
});
}));

Expand Down Expand Up @@ -307,6 +307,49 @@ describe('MdSelect', () => {
.toEqual('steak-0', `Expected control's value to be set to the new option.`);
});

it('should clear the selection when a nonexistent option value is selected', () => {
fixture.componentInstance.control.setValue('pizza-1');
fixture.detectChanges();

fixture.componentInstance.control.setValue('gibberish');
fixture.detectChanges();

const value = fixture.debugElement.query(By.css('.md-select-value'));
expect(value).toBe(null, `Expected trigger to be cleared when option value is not found.`);
expect(trigger.textContent)
.not.toContain('Pizza', `Expected trigger to be cleared when option value is not found.`);

trigger.click();
fixture.detectChanges();

const options =
overlayContainerElement.querySelectorAll('md-option') as NodeListOf<HTMLElement>;
expect(options[1].classList)
.not.toContain('md-selected', `Expected option with the old value not to be selected.`);
});


it('should clear the selection when the control is reset', () => {
fixture.componentInstance.control.setValue('pizza-1');
fixture.detectChanges();

fixture.componentInstance.control.reset();
fixture.detectChanges();

const value = fixture.debugElement.query(By.css('.md-select-value'));
expect(value).toBe(null, `Expected trigger to be cleared when option value is not found.`);
expect(trigger.textContent)
.not.toContain('Pizza', `Expected trigger to be cleared when option value is not found.`);

trigger.click();
fixture.detectChanges();

const options =
overlayContainerElement.querySelectorAll('md-option') as NodeListOf<HTMLElement>;
expect(options[1].classList)
.not.toContain('md-selected', `Expected option with the old value not to be selected.`);
});

it('should set the control to touched when the select is touched', () => {
expect(fixture.componentInstance.control.touched)
.toEqual(false, `Expected the control to start off as untouched.`);
Expand Down
31 changes: 26 additions & 5 deletions src/lib/select/select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,11 +277,7 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
return;
}

this.options.forEach((option: MdOption) => {
if (option.value === value) {
option.select();
}
});
this._setSelectionByValue(value);
}

/**
Expand Down Expand Up @@ -378,6 +374,30 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
scrollContainer.scrollTop = this._scrollTop;
}

/**
* Sets the selected option based on a value. If no option can be
* found with the designated value, the select trigger is cleared.
*/
private _setSelectionByValue(value: any): void {
const options = this.options.toArray();

for (let i = 0; i < this.options.length; i++) {
if (options[i].value === value) {
options[i].select();
return;
}
}

// Clear selection if no item was selected.
this._clearSelection();
}

/** Clears the select trigger and deselects every option in the list. */
private _clearSelection(): void {
this._selected = null;
this._updateOptions();
}

private _getTriggerRect(): ClientRect {
return this.trigger.nativeElement.getBoundingClientRect();
}
Expand Down Expand Up @@ -426,6 +446,7 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
this._selected = option;
this._updateOptions();
this._setValueWidth();
this._placeholderState = '';
if (this.panelOpen) {
this.close();
}
Expand Down