diff --git a/src/vaadin-combo-box-data-provider-mixin.html b/src/vaadin-combo-box-data-provider-mixin.html index d07ab9acb..603f63b74 100644 --- a/src/vaadin-combo-box-data-provider-mixin.html +++ b/src/vaadin-combo-box-data-provider-mixin.html @@ -82,7 +82,7 @@ _dataProviderClearFilter(dataProvider, opened, value) { // Can't depend on filter in this observer as we don't want // to clear the filter whenever it's set - if (dataProvider && this.filter) { + if (dataProvider && !this.loading && this.filter) { this.size = undefined; this._pendingRequests = {}; this.filter = ''; @@ -107,11 +107,24 @@ /** @private */ _dataProviderFilterChanged() { - if (this.dataProvider && this.opened) { - this.size = undefined; - this._pendingRequests = {}; - this.clearCache(); + if (!this._shouldFetchData()) { + return; } + + this.size = undefined; + this._pendingRequests = {}; + this.clearCache(); + } + + /** @private */ + _shouldFetchData() { + + if (!this.dataProvider) { + return false; + } + + return this.opened || + (this.filter && this.filter.length); } /** @private */ @@ -161,6 +174,9 @@ if (this._isValidValue(this.value) && this._getItemValue(this.selectedItem) !== this.value) { this._selectItemForValue(this.value); } + if (!this.opened && !this.hasAttribute('focused')) { + this._commitValue(); + } this.size = size; delete this._pendingRequests[page]; @@ -197,7 +213,7 @@ filteredItems.push(this.__placeHolder); } this.filteredItems = filteredItems; - if (this.opened) { + if (this._shouldFetchData()) { this._loadPage(0); } else { this._forceNextRequest = true; diff --git a/src/vaadin-combo-box-mixin.html b/src/vaadin-combo-box-mixin.html index caccbe1ba..1f29a8fb5 100644 --- a/src/vaadin-combo-box-mixin.html +++ b/src/vaadin-combo-box-mixin.html @@ -552,7 +552,7 @@ /** @private */ _closeOrCommit() { - if (this.autoOpenDisabled && !this.opened) { + if (!this.opened && !this.loading) { this._commitValue(); } else { this.close(); @@ -666,7 +666,9 @@ this.close(); } - this._commitValue(); + if (!this.loading) { + this._commitValue(); + } } /** @private */ @@ -685,9 +687,12 @@ this.value = ''; } } else { + const itemsMatchedByLabel = this.filteredItems + && this.filteredItems.filter(item => this._getItemLabel(item) === this._inputElementValue) + || []; if (this.allowCustomValue // to prevent a repetitive input value being saved after pressing ESC and Tab. - && !(this.filteredItems && this.filteredItems.filter(item => this._getItemLabel(item) === this._inputElementValue).length)) { + && !itemsMatchedByLabel.length) { const e = new CustomEvent('custom-value-set', {detail: this._inputElementValue, composed: true, cancelable: true, bubbles: true}); this.dispatchEvent(e); @@ -696,6 +701,8 @@ this._selectItemForValue(customValue); this.value = customValue; } + } else if (!this.allowCustomValue && !this.opened && itemsMatchedByLabel.length == 1) { + this.value = this._getItemValue(itemsMatchedByLabel[0]); } else { this._inputElementValue = this.selectedItem ? this._getItemLabel(this.selectedItem) : (this.value || ''); } diff --git a/test/lazy-loading.html b/test/lazy-loading.html index a6ae05644..f8eeab71a 100644 --- a/test/lazy-loading.html +++ b/test/lazy-loading.html @@ -854,6 +854,91 @@ }); }); + describe('using data provider, lost focus before data is returned', () => { + let returnedItems; + const bluringDataProvider = (params, callback) => { + comboBox.blur(); + callback(returnedItems, returnedItems.length); + }; + + beforeEach(() => { + returnedItems = ['item 12']; + comboBox.focus(); + comboBox.opened = true; + comboBox.dataProvider = bluringDataProvider; + comboBox.opened = false; + comboBox.focus(); + }); + + it('should set value without auto-open-disabled', () => { + comboBox.autoOpenDisabled = false; + expect(comboBox.autoOpenDisabled).to.be.false; + + comboBox._inputElementValue = 'item 12'; + comboBox.filter = 'item 12'; + expect(comboBox.opened).to.be.false; + expect(comboBox.hasAttribute('focused')).to.be.false; + expect(comboBox.value).to.equal('item 12'); + }); + + it('should set value with auto-open-disabled', () => { + comboBox.autoOpenDisabled = true; + expect(comboBox.autoOpenDisabled).to.be.true; + + comboBox._inputElementValue = 'item 12'; + comboBox.filter = 'item 12'; + expect(comboBox.opened).to.be.false; + expect(comboBox.hasAttribute('focused')).to.be.false; + expect(comboBox.value).to.equal('item 12'); + }); + + it('should keep empty value if it is not an exact match', () => { + comboBox._inputElementValue = 'item'; + comboBox.filter = 'item'; + expect(comboBox.opened).to.be.false; + expect(comboBox.hasAttribute('focused')).to.be.false; + expect(comboBox.value).to.equal(''); + }); + + it('should keep previous value if it is not an exact match', () => { + comboBox.filteredItems = ['other value', 'item 12']; + comboBox.value = 'other value'; + expect(comboBox.value).to.equal('other value'); + + returnedItems = ['item 12']; + comboBox._inputElementValue = 'item 1'; + comboBox.filter = 'item 1'; + expect(comboBox.opened).to.be.false; + expect(comboBox.hasAttribute('focused')).to.be.false; + expect(comboBox.value).to.equal('other value'); + }); + + it('should keep previous value if allow-custom-value is set', () => { + comboBox.allowCustomValue = true; + const setInputValue = value => { + if (comboBox.inputElement.tagName === 'IRON-INPUT') { + comboBox.inputElement._initSlottedInput(); + comboBox.inputElement.inputElement.value = value; + } else { + comboBox.inputElement.value = value; + } + }; + comboBox.open(); + setInputValue('other value'); + comboBox.inputElement.dispatchEvent(new CustomEvent('input')); + comboBox.close(); + expect(comboBox.value).to.eql('other value'); + + comboBox.focus(); + setInputValue('item 12'); + comboBox.filter = 'item 12'; + + expect(comboBox.value).to.eql('other value'); + expect(comboBox.inputElement.value).to.eql('other value'); + }); + + }); + describe('after empty data set loaded', () => { const emptyDataProvider = sinon.spy((params, callback) => callback([], 0));