diff --git a/addons/web/static/src/core/select_menu/select_menu.js b/addons/web/static/src/core/select_menu/select_menu.js index da86db1ee3a..7312c264247 100644 --- a/addons/web/static/src/core/select_menu/select_menu.js +++ b/addons/web/static/src/core/select_menu/select_menu.js @@ -66,6 +66,7 @@ export class SelectMenu extends Component { searchPlaceholder: { type: String, optional: true }, value: { optional: true }, multiSelect: { type: Boolean, optional: true }, + onInput: { type: Function, optional: true }, onSelect: { type: Function, optional: true }, slots: { type: Object, optional: true }, }; @@ -93,6 +94,13 @@ export class SelectMenu extends Component { if (this.props.value !== nextProps.value) { this.selectedChoice = this.getSelectedChoice(nextProps); } + if ( + this.props.choices.length !== nextProps.choices.length || + this.props.groups.length !== nextProps.groups.length + ) { + const groups = [{ choices: nextProps.choices }, ...nextProps.groups]; + this.filterOptions(this.state.searchValue, groups); + } }); } @@ -156,6 +164,10 @@ export class SelectMenu extends Component { } } + async executeOnInput(searchString) { + await this.props.onInput(searchString); + } + onInput(searchString) { this.filterOptions(searchString); this.state.searchValue = searchString; @@ -165,6 +177,9 @@ export class SelectMenu extends Component { if (inputEl && inputEl.parentNode) { inputEl.parentNode.scrollTo(0, 0); } + if (this.props.onInput) { + this.executeOnInput(searchString); + } } onSearchKeydown(ev) { @@ -218,12 +233,12 @@ export class SelectMenu extends Component { * * @param {String} searchString */ - filterOptions(searchString = "") { - const groups = [{ choices: this.props.choices }, ...this.props.groups]; + filterOptions(searchString = "", groups) { + const groupsList = groups || [{ choices: this.props.choices }, ...this.props.groups]; this.state.choices = []; - for (const group of groups) { + for (const group of groupsList) { let filteredOptions = []; if (searchString) { diff --git a/addons/web/static/tests/core/select_menu_tests.js b/addons/web/static/tests/core/select_menu_tests.js index 87cbb346d7b..275c5b61124 100644 --- a/addons/web/static/tests/core/select_menu_tests.js +++ b/addons/web/static/tests/core/select_menu_tests.js @@ -867,4 +867,66 @@ QUnit.module("Web Components", (hooks) => { assert.verifySteps(["a"], "value has been selected after keyboard navigation"); } ); + + QUnit.test("Props onInput is executed when the search changes", async (assert) => { + class Parent extends Component { + setup() { + this.state = useState({ + choices: [{ label: "Hello", value: "hello" }], + value: "hello", + }); + } + + onInput() { + // This test adds items from the list of choices given by the parent. + // It can be used as a reference to fetch and load content dynamically to the SelectMenu + this.state.choices = [ + { label: "Hello", value: "hello" }, + { label: "Coucou", value: "hello2" }, + ]; + } + + onSelect(value) { + assert.step(value); + this.state.value = value; + } + } + Parent.components = { SelectMenu }; + Parent.template = xml` + + `; + + await mount(Parent, target, { env }); + assert.strictEqual(getValue(), "Hello"); + + await open(); + assert.strictEqual( + target.querySelector(".o_select_menu_menu").textContent, + "Hello", + "SelectMenu has only one choice available" + ); + + await editInput(target, "input.o_select_menu_sticky", "cou"); + assert.strictEqual( + target.querySelector(".o_select_menu_menu").textContent, + "Coucou", + "SelectMenu now has 'Coucou' available and search is filtered" + ); + + await click(target.querySelectorAll(".o_select_menu_item_label")[0]); + assert.verifySteps(["hello2"], "added item can be selected"); + assert.strictEqual(getValue(), "Coucou"); + + await open(); + assert.strictEqual( + target.querySelector(".o_select_menu_menu").textContent, + "CoucouHello", + "SelectMenu has two choices available" + ); + }); });