Skip to content

Commit

Permalink
Debounce the request (#8443)
Browse files Browse the repository at this point in the history
* work for #5791 Dropdown - Activate a list of options only after a user enters more than the specified number of characters

* work for #5791 Dropdown - Activate a list of options only after a user enters more than the specified number of characters

---------

Co-authored-by: OlgaLarina <[email protected]>
  • Loading branch information
OlgaLarina and OlgaLarina authored Jun 21, 2024
1 parent a4ee909 commit 73712f6
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 67 deletions.
47 changes: 30 additions & 17 deletions src/dropdownListModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { doKey2ClickBlur, doKey2ClickUp } from "./utils/utils";
export class DropdownListModel extends Base {
readonly minPageSize = 25;
readonly loadingItemHeight = 40;

timer: any = undefined;
private htmlCleanerElement: HTMLDivElement;

private _markdownMode = false;
Expand Down Expand Up @@ -57,28 +57,41 @@ export class DropdownListModel extends Base {
this.listModel.isAllDataLoaded = this.question.choicesLazyLoadEnabled && this.itemsSettings.items.length == this.itemsSettings.totalCount;
this.question.choices = this.itemsSettings.items;
}

private loadQuestionChoices(callbackAfterItemsLoaded?: () => void) {
this.isRunningLoadQuestionChoices = true;
this.question.survey.loadQuestionChoices({
question: this.question,
filter: this.filterString,
skip: this.itemsSettings.skip,
take: this.itemsSettings.take,
setItems: (items: Array<any>, totalCount: number) => {
this.isRunningLoadQuestionChoices = false;
this.setItems(items || [], totalCount || 0);
this.popupRecalculatePosition(this.itemsSettings.skip === this.itemsSettings.take);
if (!!callbackAfterItemsLoaded) {
callbackAfterItemsLoaded();
}
}
});
this.itemsSettings.skip += this.itemsSettings.take;
}
private updateQuestionChoices(callbackAfterItemsLoaded?: () => void): void {
if (this.isRunningLoadQuestionChoices) return;

const isUpdate = (this.itemsSettings.skip + 1) < this.itemsSettings.totalCount;
if (!this.itemsSettings.skip || isUpdate) {
this.isRunningLoadQuestionChoices = true;
this.question.survey.loadQuestionChoices({
question: this.question,
filter: this.filterString,
skip: this.itemsSettings.skip,
take: this.itemsSettings.take,
setItems: (items: Array<any>, totalCount: number) => {
this.isRunningLoadQuestionChoices = false;
this.setItems(items || [], totalCount || 0);
this.popupRecalculatePosition(this.itemsSettings.skip === this.itemsSettings.take);
if (!!callbackAfterItemsLoaded) {
callbackAfterItemsLoaded();
}

if (!!this.filterString && settings.dropdownSearchDelay > 0) {
if (!!this.timer) {
clearTimeout(this.timer);
this.timer = undefined;
}
});
this.itemsSettings.skip += this.itemsSettings.take;
this.timer = setTimeout(() => {
this.loadQuestionChoices(callbackAfterItemsLoaded);
}, settings.dropdownSearchDelay);
} else {
this.loadQuestionChoices(callbackAfterItemsLoaded);
}
}
}

Expand Down
1 change: 1 addition & 0 deletions src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ export var settings = {
* This setting applies to all TagBox questions on a page. You can use the [closeOnSelect](https://surveyjs.io/form-library/documentation/api-reference/dropdown-tag-box-model#closeOnSelect) property to specify the same setting for an individual TagBox question.
*/
tagboxCloseOnSelect: false,
dropdownSearchDelay: 500,
/**
* A function that activates a browser confirm dialog.
*
Expand Down
2 changes: 1 addition & 1 deletion tests/entries/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,4 @@ import "../../src/localization/german";
import { settings } from "../../src/settings";

settings.animationEnabled = false;

settings.dropdownSearchDelay = 0;
110 changes: 83 additions & 27 deletions tests/questionDropdownTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -739,7 +739,7 @@ QUnit.test("lazy loading: first loading", assert => {
assert.equal(question.choicesLazyLoadEnabled, true);
assert.equal(question.choices.length, 0);

question.dropdownListModel.popupModel.isVisible = true;
question.dropdownListModel.popupModel.show();
setTimeout(() => {
assert.equal(question.choices.length, 30);
assert.equal(question.choices[0].value, 1);
Expand All @@ -765,7 +765,7 @@ QUnit.test("lazy loading: first loading - default value", assert => {
const question = <QuestionDropdownModel>survey.getAllQuestions()[0];
assert.equal(question.choicesLazyLoadEnabled, true);
assert.equal(question.choices.length, 0);
question.dropdownListModel.popupModel.isVisible = true;
question.dropdownListModel.popupModel.show();
question.dropdownListModel.changeSelectionWithKeyboard(false);
setTimeout(() => {
assert.equal(question.choices.length, 30);
Expand All @@ -792,7 +792,7 @@ QUnit.test("lazy loading: several loading", assert => {
assert.equal(question.choicesLazyLoadEnabled, true);
assert.equal(question.choices.length, 0);

question.dropdownListModel.popupModel.toggleVisibility();
question.dropdownListModel.popupModel.show();
setTimeout(() => {
assert.equal(question.choices.length, 25);
assert.equal(question.choices[0].value, 1);
Expand Down Expand Up @@ -820,6 +820,61 @@ QUnit.test("lazy loading: several loading", assert => {
}, onChoicesLazyLoadCallbackTimeOut + callbackTimeOutDelta);
});

QUnit.test("lazy loading + change filter string + dropdownSearchDelay ", assert => {
const newValueDebouncedInputValue = 2 * onChoicesLazyLoadCallbackTimeOut;
const oldValueDebouncedInputValue = settings.dropdownSearchDelay;
settings.dropdownSearchDelay = newValueDebouncedInputValue;
const done1 = assert.async();
const done2 = assert.async();
const done3 = assert.async();
const done4 = assert.async();

const json = {
questions: [{
"type": "dropdown",
"name": "q1",
"choicesLazyLoadEnabled": true
}]
};
const survey = new SurveyModel(json);
survey.onChoicesLazyLoad.add(callback);

const question = <QuestionDropdownModel>survey.getAllQuestions()[0];
assert.equal(question.choicesLazyLoadEnabled, true);
assert.equal(question.choices.length, 0);

question.dropdownListModel.popupModel.show();
assert.equal(question.choices.length, 0, "show popup before request");

setTimeout(() => {
assert.equal(question.choices.length, 25, "show popup after request");
assert.equal(question.choices[0].value, 1, "show popup after request");

question.dropdownListModel.filterString = "2";
setTimeout(() => {
assert.equal(question.choices.length, 25, "filter is 2");
assert.equal(question.choices[0].value, 1, "filter is 2");

question.dropdownListModel.filterString = "22";
setTimeout(() => {
assert.equal(question.choices.length, 25, "filter is 22 before request");
assert.equal(question.choices[0].value, 1, "filter is 22 before request");

setTimeout(() => {
assert.equal(question.choices.length, 25, "filter is 22 after request");
assert.equal(question.choices[0].value, 22, "filter is 22 after request");

settings.dropdownSearchDelay = oldValueDebouncedInputValue;
done4();
}, onChoicesLazyLoadCallbackTimeOut + newValueDebouncedInputValue);
done3();
}, onChoicesLazyLoadCallbackTimeOut + callbackTimeOutDelta);
done2();
}, onChoicesLazyLoadCallbackTimeOut + callbackTimeOutDelta);
done1();
}, onChoicesLazyLoadCallbackTimeOut + callbackTimeOutDelta);
});

QUnit.test("The onGetChoiceDisplayValue callback fires multiple times, #6078", assert => {
let requestCount = 0;
let responseCount = 0;
Expand Down Expand Up @@ -930,7 +985,7 @@ QUnit.test("lazy loading: storeOthersAsComment is false", assert => {
assert.equal(question.visibleChoices[0].id, "other");
assert.equal(question.visibleChoices[0].value, "other");

question.dropdownListModel.popupModel.isVisible = true;
question.dropdownListModel.popupModel.show();
setTimeout(() => {
assert.equal(question.visibleChoices.length, 56);
assert.equal(question.visibleChoices[0].value, 1);
Expand Down Expand Up @@ -969,7 +1024,7 @@ QUnit.test("itemsSettings property", assert => {
assert.equal(itemsSettings.items.length, 0);
assert.equal(listModel.actions.length, 0, "listModel.actions");

question.dropdownListModel.popupModel.isVisible = true;
question.dropdownListModel.popupModel.show();

setTimeout(() => {
assert.equal(listModel.actions.length, 26, "listModel.actions");
Expand All @@ -978,7 +1033,7 @@ QUnit.test("itemsSettings property", assert => {
assert.equal(itemsSettings.totalCount, 55);
assert.equal(itemsSettings.items.length, 25);

question.dropdownListModel.popupModel.isVisible = false;
question.dropdownListModel.popupModel.hide();

setTimeout(() => {
assert.equal(listModel.actions.length, 26, "listModel.actions");
Expand All @@ -987,7 +1042,7 @@ QUnit.test("itemsSettings property", assert => {
assert.equal(itemsSettings.totalCount, 0);
assert.equal(itemsSettings.items.length, 0);

question.dropdownListModel.popupModel.isVisible = true;
question.dropdownListModel.popupModel.show();
assert.equal(listModel.actions.length, 0, "listModel.actions");

done2();
Expand Down Expand Up @@ -1039,7 +1094,7 @@ QUnit.test("min page size", assert => {
assert.equal(itemsSettings.items.length, 0);
assert.equal(listModel.actions.length, 0, "listModel.actions");

question.dropdownListModel.popupModel.isVisible = true;
question.dropdownListModel.popupModel.show();

setTimeout(() => {
assert.equal(listModel.actions.length, 26, "listModel.actions");
Expand Down Expand Up @@ -1072,7 +1127,7 @@ QUnit.test("selectedItem until all data is loaded", assert => {
assert.equal(listModel.actions.length, 0, "listModel.actions");
assert.equal(question.selectedItem, null);

question.dropdownListModel.popupModel.toggleVisibility();
question.dropdownListModel.popupModel.show();
setTimeout(() => {
assert.equal(question.choices.length, 30);
assert.equal(listModel.actions.length, 31, "listModel.actions");
Expand All @@ -1086,8 +1141,8 @@ QUnit.test("selectedItem until all data is loaded", assert => {
question.value = question.choices[54].value;
assert.equal(question.selectedItem.value, 55);

question.dropdownListModel.popupModel.isVisible = false;
question.dropdownListModel.popupModel.isVisible = true;
question.dropdownListModel.popupModel.hide();
question.dropdownListModel.popupModel.show();

setTimeout(() => {
assert.equal(question.choices.length, 30);
Expand Down Expand Up @@ -1157,7 +1212,7 @@ QUnit.test("lazy loading + onGetChoiceDisplayValue: defaultValue", assert => {
assert.equal(question.selectedItem.text, "DisplayText_55");
assert.equal(questionTitle.locTitle.textOrHtml, "DisplayText_55", "display text is correct");

question.dropdownListModel.popupModel.isVisible = true;
question.dropdownListModel.popupModel.show();
setTimeout(() => {
assert.equal(question.choices.length, 25);
assert.equal(question.choices[0].value, 1);
Expand Down Expand Up @@ -1230,7 +1285,7 @@ QUnit.test("lazy loading + onGetChoiceDisplayValue, selected last item", assert
assert.equal(question.value, undefined);
assert.equal(question.selectedItem, undefined);

question.dropdownListModel.popupModel.isVisible = true;
question.dropdownListModel.popupModel.show();
question.value = 54;
assert.equal(question.choices.length, 30);
assert.equal(question.value, 54);
Expand Down Expand Up @@ -1273,7 +1328,7 @@ QUnit.test("lazy loading + onGetChoiceDisplayValue: defaultValue is object", ass
question.dropdownListModel.onFocus(null);
assert.equal(question.dropdownListModel.inputString, "DisplayText_55");

question.dropdownListModel.popupModel.isVisible = true;
question.dropdownListModel.popupModel.show();
setTimeout(() => {
assert.equal(question.choices.length, 25);
assert.equal(question.choices[0].value, 1);
Expand Down Expand Up @@ -1321,7 +1376,7 @@ QUnit.test("lazy loading + onGetChoiceDisplayValue: set survey data", assert =>
assert.equal(question.selectedItem.value, 55);
assert.equal(question.selectedItem.text, "DisplayText_55");

question.dropdownListModel.popupModel.isVisible = true;
question.dropdownListModel.popupModel.show();
setTimeout(() => {
assert.equal(question.choices.length, 25);
assert.equal(question.choices[0].value, 1);
Expand Down Expand Up @@ -1368,7 +1423,7 @@ QUnit.test("lazy loading data is lost: defaultValue", assert => {
assert.equal(question.choices.length, 0);
assert.equal(question.value, 55);

question.dropdownListModel.popupModel.isVisible = true;
question.dropdownListModel.popupModel.show();
setTimeout(() => {
assert.equal(question.choices.length, 25);
assert.equal(question.value, 55);
Expand Down Expand Up @@ -1415,7 +1470,7 @@ QUnit.test("lazy loading data is lost: set survey data", assert => {
assert.equal(question.choices.length, 0);
assert.equal(question.value, 55);

question.dropdownListModel.popupModel.isVisible = true;
question.dropdownListModel.popupModel.show();
setTimeout(() => {
assert.equal(question.choices.length, 25);
assert.equal(question.value, 55);
Expand Down Expand Up @@ -1452,7 +1507,7 @@ QUnit.test("lazy loading + change filter string", assert => {
assert.equal(itemsSettings.totalCount, 0);
assert.equal(itemsSettings.items.length, 0);

question.dropdownListModel.popupModel.toggleVisibility();
question.dropdownListModel.popupModel.show();
setTimeout(() => {
assert.equal(question.choices.length, 25);
assert.equal(question.choices[0].value, 1);
Expand Down Expand Up @@ -1517,7 +1572,7 @@ QUnit.test("lazy loading + change listModel filter string", assert => {
assert.equal(itemsSettings.totalCount, 0);
assert.equal(itemsSettings.items.length, 0);

question.dropdownListModel.popupModel.toggleVisibility();
question.dropdownListModel.popupModel.show();
setTimeout(() => {
assert.equal(question.choices.length, 25);
assert.equal(question.choices[0].value, 1);
Expand Down Expand Up @@ -1629,7 +1684,7 @@ QUnit.test("lazy loading placeholder", assert => {
assert.equal(list.emptyMessage, "Loading...");
assert.equal(question.choices.length, 0);

question.dropdownListModel.popupModel.toggleVisibility();
question.dropdownListModel.popupModel.show();
setTimeout(() => {
assert.equal(list.actions.length, 0);
assert.equal(list.emptyMessage, "No data to display");
Expand Down Expand Up @@ -1769,7 +1824,7 @@ QUnit.test("lazy loading: change choicesLazyLoadEnabled on runtime", assert => {
assert.equal(question.choices.length, 0);
assert.equal(question.dropdownListModel["listModel"].visibleItems.length, 0, "#1");

question.dropdownListModel.popupModel.isVisible = true;
question.dropdownListModel.popupModel.show();
assert.equal(question.choices.length, 15);
assert.equal(question.dropdownListModel["listModel"].visibleItems.length, 16, "#2");
assert.equal(question.dropdownListModel["listModel"].visibleItems[15].id, "loadingIndicator");
Expand All @@ -1796,7 +1851,7 @@ QUnit.test("lazy loading: duplication of elements after blur", assert => {

assert.equal(question.choicesLazyLoadEnabled, true);
assert.equal(question.choices.length, 0, "question.choices.length #0");
question.dropdownListModel.popupModel.isVisible = true;
question.dropdownListModel.popupModel.show();
question.dropdownListModel.changeSelectionWithKeyboard(false);
setTimeout(() => {
assert.equal(question.choices.length, 25, "question.choices.length #1");
Expand Down Expand Up @@ -1828,7 +1883,7 @@ QUnit.test("lazy loading: duplication of elements after blur", assert => {
assert.equal(itemsSettings.take, 25, "take #3");
assert.equal(itemsSettings.totalCount, 0, "totalCount #3");
assert.equal(itemsSettings.items.length, 0, "items.length #3");
question.dropdownListModel.popupModel.isVisible = true;
question.dropdownListModel.popupModel.show();

setTimeout(() => {
assert.equal(question.choices.length, 25, "question.choices.length #4");
Expand Down Expand Up @@ -1864,11 +1919,12 @@ QUnit.test("lazy loading: check onChoicesLazyLoad callback count", assert => {
} else {
opt.setItems(getNumberArray(opt.skip + 1, total - opt.skip, opt.filter), total);
}
}, onChoicesLazyLoadCallbackTimeOut); });
}, onChoicesLazyLoadCallbackTimeOut);
});
const question = <QuestionDropdownModel>survey.getAllQuestions()[0];

assert.equal(callbackCount, 0);
question.dropdownListModel.popupModel.isVisible = true;
question.dropdownListModel.popupModel.show();
question.dropdownListModel.changeSelectionWithKeyboard(false);
setTimeout(() => {
assert.equal(callbackCount, 1);
Expand All @@ -1884,7 +1940,7 @@ QUnit.test("lazy loading: check onChoicesLazyLoad callback count", assert => {

setTimeout(() => {
assert.equal(callbackCount, 2);
question.dropdownListModel.popupModel.isVisible = true;
question.dropdownListModel.popupModel.show();

setTimeout(() => {
assert.equal(callbackCount, 3);
Expand Down Expand Up @@ -1964,7 +2020,7 @@ QUnit.test("Dropdown choicesLazyLoadEnabled into matrixdynamic", function (asser
assert.equal(itemsSettings.totalCount, 0);
assert.equal(itemsSettings.items.length, 0);

question.dropdownListModel.popupModel.isVisible = true;
question.dropdownListModel.popupModel.show();
setTimeout(() => {
assert.equal(question.choices.length, 25);
assert.equal(question.choices[0].value, 1);
Expand Down
Loading

0 comments on commit 73712f6

Please sign in to comment.