From e4dac5a93226cb723dfa100c27f65c0ec1913fcc Mon Sep 17 00:00:00 2001 From: Jeferson Estevo Date: Mon, 20 Mar 2017 19:52:25 -0300 Subject: [PATCH 1/2] bug(autocomplete): deselect the other options when the user select one from the autocomplete Fixes #3689 --- src/lib/autocomplete/autocomplete-trigger.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib/autocomplete/autocomplete-trigger.ts b/src/lib/autocomplete/autocomplete-trigger.ts index 6d3c550022fc..5823c5dfb15c 100644 --- a/src/lib/autocomplete/autocomplete-trigger.ts +++ b/src/lib/autocomplete/autocomplete-trigger.ts @@ -306,6 +306,12 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy { */ private _setValueAndClose(event: MdOptionSelectionChange | null): void { if (event) { + this.autocomplete.options.forEach((option) => { + if (option != event.source) { + option.deselect(); + } + }); + this._setTriggerValue(event.source.value); this._onChange(event.source.value); } From c5c178dd1a851edb186f9ebe180a16f6013b9da0 Mon Sep 17 00:00:00 2001 From: Jeferson Estevo Date: Tue, 21 Mar 2017 14:42:33 -0300 Subject: [PATCH 2/2] bug(autocomplete): only call deselect on the previous selected option. Unit tests. --- src/lib/autocomplete/autocomplete-trigger.ts | 18 ++++-- src/lib/autocomplete/autocomplete.spec.ts | 63 ++++++++++++++++++++ 2 files changed, 75 insertions(+), 6 deletions(-) diff --git a/src/lib/autocomplete/autocomplete-trigger.ts b/src/lib/autocomplete/autocomplete-trigger.ts index 5823c5dfb15c..228f047c5fc1 100644 --- a/src/lib/autocomplete/autocomplete-trigger.ts +++ b/src/lib/autocomplete/autocomplete-trigger.ts @@ -306,12 +306,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy { */ private _setValueAndClose(event: MdOptionSelectionChange | null): void { if (event) { - this.autocomplete.options.forEach((option) => { - if (option != event.source) { - option.deselect(); - } - }); - + this._clearPreviousSelectedOption(event.source); this._setTriggerValue(event.source.value); this._onChange(event.source.value); } @@ -319,6 +314,17 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy { this.closePanel(); } + /** + * Clear any previous selected option and emit a selection change event for this option + */ + private _clearPreviousSelectedOption(skip: MdOption) { + this.autocomplete.options.forEach((option) => { + if (option != skip && option.selected) { + option.deselect(); + } + }); + } + private _createOverlay(): void { this._portal = new TemplatePortal(this.autocomplete.template, this._viewContainerRef); this._overlayRef = this._overlay.create(this._getOverlayConfig()); diff --git a/src/lib/autocomplete/autocomplete.spec.ts b/src/lib/autocomplete/autocomplete.spec.ts index 7118f73ae3cb..87c63b3385f6 100644 --- a/src/lib/autocomplete/autocomplete.spec.ts +++ b/src/lib/autocomplete/autocomplete.spec.ts @@ -898,6 +898,69 @@ describe('MdAutocomplete', () => { }); + describe('Option selection', () => { + let fixture: ComponentFixture; + + beforeEach(() => { + fixture = TestBed.createComponent(SimpleAutocomplete); + fixture.detectChanges(); + + fixture.componentInstance.trigger.openPanel(); + fixture.detectChanges(); + }); + + it('should deselect any other selected option', async(() => { + let options = + overlayContainerElement.querySelectorAll('md-option') as NodeListOf; + options[0].click(); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + fixture.detectChanges(); + + let componentOptions = fixture.componentInstance.options.toArray(); + expect(componentOptions[0].selected) + .toBe(true, `Clicked option should be selected.`); + + options = + overlayContainerElement.querySelectorAll('md-option') as NodeListOf; + options[1].click(); + fixture.detectChanges(); + + expect(componentOptions[0].selected) + .toBe(false, `Previous option should not be selected.`); + expect(componentOptions[1].selected) + .toBe(true, `New Clicked option should be selected.`); + + }); + })); + + it('should call deselect only on the previous selected option', async(() => { + let options = + overlayContainerElement.querySelectorAll('md-option') as NodeListOf; + options[0].click(); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + fixture.detectChanges(); + + let componentOptions = fixture.componentInstance.options.toArray(); + componentOptions.forEach(option => spyOn(option, 'deselect')); + + expect(componentOptions[0].selected) + .toBe(true, `Clicked option should be selected.`); + + options = + overlayContainerElement.querySelectorAll('md-option') as NodeListOf; + options[1].click(); + fixture.detectChanges(); + + expect(componentOptions[0].deselect).toHaveBeenCalled(); + componentOptions.slice(1).forEach(option => expect(option.deselect).not.toHaveBeenCalled()); + }); + })); + }); + describe('misc', () => { it('should allow basic use without any forms directives', () => {