diff --git a/src/plugins/virtual_scroll/plugin.ts b/src/plugins/virtual_scroll/plugin.ts
index 3aadfebf..424ff536 100644
--- a/src/plugins/virtual_scroll/plugin.ts
+++ b/src/plugins/virtual_scroll/plugin.ts
@@ -1,5 +1,5 @@
/**
- * Plugin: "restore_on_backspace" (Tom Select)
+ * Plugin: "virtual_scroll" (Tom Select)
* Copyright (c) contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
@@ -151,6 +151,18 @@ export default function(this:TomSelect) {
});
+ // as the “loading_more” element will be removed from the dropdown,
+ // we activate the previous option if needed
+ // to avoid the dropdown being scrolled back to the first one
+ self.hook('before','refreshOptions',()=>{
+
+ if (self.activeOption && "option" !== self.activeOption.getAttribute("role")) {
+ self.setActiveOption(self.activeOption.previousElementSibling as HTMLElement);
+ }
+
+ });
+
+
// add templates to dropdown
// loading_more if we have another url in the queue
// no_more_results if we don't have another url in the queue
diff --git a/src/tom-select.ts b/src/tom-select.ts
index 697949d3..eea6ea5e 100644
--- a/src/tom-select.ts
+++ b/src/tom-select.ts
@@ -73,6 +73,7 @@ export default class TomSelect extends MicroPlugin(MicroEvent){
public isFocused : boolean = false;
public isInputHidden : boolean = false;
public isSetup : boolean = false;
+ public isDropdownContentStale : boolean = true;
public ignoreFocus : boolean = false;
public ignoreHover : boolean = false;
public hasOptions : boolean = false;
@@ -546,8 +547,6 @@ export default class TomSelect extends MicroPlugin(MicroEvent){
self.setupOptions(settings.options,settings.optgroups);
self.setValue(settings.items||[],true); // silent prevents recursion
-
- self.lastQuery = null; // so updated options will be displayed in dropdown
}
/**
@@ -891,7 +890,7 @@ export default class TomSelect extends MicroPlugin(MicroEvent){
} else {
value = option.dataset.value;
if (typeof value !== 'undefined') {
- self.lastQuery = null;
+ self.isDropdownContentStale = self.settings.hideSelected;
self.addItem(value);
if (self.settings.closeAfterSelect) {
self.close();
@@ -979,7 +978,7 @@ export default class TomSelect extends MicroPlugin(MicroEvent){
loadCallback( options:TomOption[], optgroups:TomOption[] ):void{
const self = this;
self.loading = Math.max(self.loading - 1, 0);
- self.lastQuery = null;
+ self.isDropdownContentStale = true;
self.clearActiveOption(); // when new results load, focus should be on first option
self.setupOptions(options,optgroups);
@@ -1356,7 +1355,7 @@ export default class TomSelect extends MicroPlugin(MicroEvent){
}
// perform search
- if (query !== self.lastQuery) {
+ if (self.isDropdownContentStale || query !== self.lastQuery) {
self.lastQuery = query;
result = self.sifter.search(query, Object.assign(options, {score: calculateScore}));
self.currentResults = result;
@@ -1531,6 +1530,7 @@ export default class TomSelect extends MicroPlugin(MicroEvent){
dropdown_content.innerHTML = '';
append( dropdown_content, html );
+ self.isDropdownContentStale = false;
// highlight matching terms inline
if (self.settings.highlight) {
@@ -1649,10 +1649,10 @@ export default class TomSelect extends MicroPlugin(MicroEvent){
return false;
}
- data.$order = data.$order || ++self.order;
- data.$id = self.inputId + '-opt-' + data.$order;
- self.options[key] = data;
- self.lastQuery = null;
+ data.$order = data.$order || ++self.order;
+ data.$id = self.inputId + '-opt-' + data.$order;
+ self.options[key] = data;
+ self.isDropdownContentStale = true;
if( user_created ){
self.userOptions[key] = user_created;
@@ -1793,8 +1793,8 @@ export default class TomSelect extends MicroPlugin(MicroEvent){
replaceNode( item, item_new);
}
- // invalidate last query because we might have updated the sortField
- self.lastQuery = null;
+ // we might have updated the sortField
+ self.isDropdownContentStale = true;
}
/**
@@ -1809,7 +1809,7 @@ export default class TomSelect extends MicroPlugin(MicroEvent){
delete self.userOptions[value];
delete self.options[value];
- self.lastQuery = null;
+ self.isDropdownContentStale = true;
self.trigger('option_remove', value);
self.removeItem(value, silent);
}
@@ -1833,7 +1833,7 @@ export default class TomSelect extends MicroPlugin(MicroEvent){
});
this.options = this.sifter.items = selected;
- this.lastQuery = null;
+ this.isDropdownContentStale = true;
this.trigger('option_clear');
}
@@ -2042,7 +2042,7 @@ export default class TomSelect extends MicroPlugin(MicroEvent){
}
self.items.splice(i, 1);
- self.lastQuery = null;
+ self.isDropdownContentStale = true;
if (!self.settings.persist && self.userOptions.hasOwnProperty(value)) {
self.removeOption(value, silent);
}
@@ -2128,7 +2128,7 @@ export default class TomSelect extends MicroPlugin(MicroEvent){
*/
refreshItems() {
var self = this;
- self.lastQuery = null;
+ self.isDropdownContentStale = true;
if (self.isSetup) {
self.addItems(self.items);
diff --git a/test/tests/plugins/virtual_scroll.js b/test/tests/plugins/virtual_scroll.js
index 76985010..814dca4f 100644
--- a/test/tests/plugins/virtual_scroll.js
+++ b/test/tests/plugins/virtual_scroll.js
@@ -27,9 +27,9 @@ describe('plugin: virtual_scroll', function() {
it_n('load more data while scrolling',async ()=>{
- var load_calls = 0;
+ let load_calls = 0;
- var test = setup_test('',{
+ const test = setup_test('',{
plugins:['virtual_scroll'],
labelField: 'value',
valueField: 'value',
@@ -53,18 +53,21 @@ describe('plugin: virtual_scroll', function() {
await asyncType('a');
await waitFor(100); // wait for data to load
assert.equal( Object.keys(test.instance.options).length,20,'should load first set of data');
- assert.equal( test.instance.dropdown_content.querySelectorAll('.loading-more-results').length, 1, 'should have loading-more-reuslts template');
+ const loadingMoreIndicator = test.instance.dropdown_content.querySelector('.loading-more-results');
+ assert.isNotNull( loadingMoreIndicator, 'should have loading_more template');
assert.equal( test.instance.dropdown_content.querySelectorAll('.no-more-results').length, 0 ,'should not have no-more-results template');
assert.equal( test.instance.dropdown_content.querySelectorAll('.option').length, 21 ,'Should display 20 options plus .loading-more-results');
assert.equal( load_calls, 1);
+ const lastOption = loadingMoreIndicator.previousElementSibling;
// load second set of data for "a"
- test.instance.scroll(1000,'auto'); // scroll to bottom
+ test.instance.setActiveOption(loadingMoreIndicator); // scroll to bottom
await waitFor(500); // wait for scroll + more data to load
assert.equal( Object.keys(test.instance.options).length, 40,'should load second set of data');
- assert.equal( test.instance.dropdown_content.querySelectorAll('.loading-more-results').length, 0, 'should not have loading-more-reuslts template');
+ assert.equal( lastOption, test.instance.activeOption, 'previous dataset’s last option should be active')
+ assert.equal( test.instance.dropdown_content.querySelectorAll('.loading-more-results').length, 0, 'should not have loading_more template');
assert.equal( test.instance.dropdown_content.querySelectorAll('.no-more-results').length, 1 ,'should have no-more-results template');
assert.equal( test.instance.dropdown_content.querySelectorAll('.option').length, 31 ,'Should display 30 options plus .no-more-results');
assert.equal( load_calls, 2);
@@ -79,7 +82,7 @@ describe('plugin: virtual_scroll', function() {
// load first set of data for "b"
await asyncType('\bb');
- await waitFor(100); // wait for data to load
+ await waitFor(500); // wait for data to load
assert.equal( Object.keys(test.instance.options).length,20,'should load new set of data for "b"');
assert.equal( load_calls, 3);
});