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 dropdown scrolling to top when the virtual scroll plugin fetch next page #821

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
14 changes: 13 additions & 1 deletion src/plugins/virtual_scroll/plugin.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down
30 changes: 15 additions & 15 deletions src/tom-select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
}

/**
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

/**
Expand All @@ -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);
}
Expand All @@ -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');
}

Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down
15 changes: 9 additions & 6 deletions test/tests/plugins/virtual_scroll.js
Original file line number Diff line number Diff line change
Expand Up @@ -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('<input>',{
const test = setup_test('<input>',{
plugins:['virtual_scroll'],
labelField: 'value',
valueField: 'value',
Expand All @@ -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);
Expand All @@ -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);
});
Expand Down