diff --git a/packages/main/cypress/specs/ToolbarSelect.cy.tsx b/packages/main/cypress/specs/ToolbarSelect.cy.tsx index 1c07e3644ea1..5f10a2d145fe 100644 --- a/packages/main/cypress/specs/ToolbarSelect.cy.tsx +++ b/packages/main/cypress/specs/ToolbarSelect.cy.tsx @@ -605,6 +605,70 @@ describe("Toolbar general interaction", () => { cy.get("[ui5-toolbar-select-option]").eq(2).should("have.attr", "selected"); cy.get("[ui5-toolbar-select-option]").eq(0).should("not.have.attr", "selected"); cy.get("[ui5-toolbar-select-option]").eq(1).should("not.have.attr", "selected"); - cy.get("ui5-select", { includeShadowDom: true }).should("have.attr", "value", "3"); + cy.get("[ui5-toolbar]") + .find("[ui5-toolbar-select]") + .shadow() + .find("[ui5-select]") + .find("[ui5-option]") + .eq(2) + .should("have.attr", "selected"); + }); + + it("Should clear the inner select when value is set to empty string after render", () => { + cy.mount( + + + Option 1 + Option 2 + Option 3 + + + ); + + cy.document().then(doc => { + const select = doc.querySelector("ui5-toolbar-select") as ToolbarSelect; + select.value = ""; + }); + + cy.get("[ui5-toolbar]") + .find("[ui5-toolbar-select]") + .shadow() + .find("[ui5-select]") + .should("have.attr", "value", ""); + }); + + it("Should not let stale _value override a later programmatic selected change", () => { + cy.mount( + <> + + + Option 1 + Option 2 + Option 3 + + + + + ); + + cy.get("#prog-btn").then($btn => { + $btn.get(0).addEventListener("click", () => { + const options = document.querySelectorAll("ui5-toolbar-select-option"); + options.forEach(opt => { (opt as ToolbarSelectOption).selected = false; }); + (document.getElementById("prog-opt3") as ToolbarSelectOption).selected = true; + }); + }); + + cy.get("#prog-btn").realClick(); + + cy.get("[ui5-toolbar-select-option]").eq(2).should("have.attr", "selected"); + cy.get("[ui5-toolbar-select-option]").eq(0).should("not.have.attr", "selected"); + cy.get("[ui5-toolbar]") + .find("[ui5-toolbar-select]") + .shadow() + .find("[ui5-select]") + .find("[ui5-option]") + .eq(2) + .should("have.attr", "selected"); }); }); \ No newline at end of file diff --git a/packages/main/src/ToolbarSelect.ts b/packages/main/src/ToolbarSelect.ts index 74fde918b1ad..9049b4193b41 100644 --- a/packages/main/src/ToolbarSelect.ts +++ b/packages/main/src/ToolbarSelect.ts @@ -154,14 +154,22 @@ class ToolbarSelect extends ToolbarItemBase { */ @property() set value(newValue: string) { - if (this.select && this.select.value !== newValue) { - this.select.value = newValue; + if (this.select) { + if (this.select.value !== newValue) { + this.select.value = newValue; + } + this._value = ""; + } else { + this._value = newValue; } - this._value = newValue; } get value(): string | undefined { - return this.select ? this.select.value : this._value; + if (this._value) { + return this._value; + } + const selectedOption = this.options.find(o => o.selected); + return selectedOption?.textContent || this.select?.value || ""; } get select(): Select | null { @@ -195,6 +203,30 @@ class ToolbarSelect extends ToolbarItemBase { } } + onBeforeRendering(): void { + super.onBeforeRendering(); + let lastSelectedIndex = -1; + this.options.forEach((option, index) => { + if (option.selected) { + lastSelectedIndex = index; + } + }); + this.options.forEach((option, index) => { + const shouldBeSelected = index === lastSelectedIndex; + if (option.selected !== shouldBeSelected) { + option.selected = shouldBeSelected; + } + }); + } + + onAfterRendering(): void { + super.onAfterRendering(); + if (this._value && this.select) { + this.select.value = this._value; + this._value = ""; + } + } + onChange(e: CustomEvent): void { e.stopImmediatePropagation(); const selectedOptionIndex = Number(e.detail.selectedOption?.getAttribute("data-ui5-external-action-item-index")); @@ -208,6 +240,7 @@ class ToolbarSelect extends ToolbarItemBase { } _syncOptions(selectedOptionIndex: number): void { + this._value = ""; this.options.forEach((option: ToolbarSelectOption, index: number) => { option.selected = index === selectedOptionIndex; }); diff --git a/packages/main/src/ToolbarSelectOption.ts b/packages/main/src/ToolbarSelectOption.ts index 9c324c8fa70a..5443e6bebad6 100644 --- a/packages/main/src/ToolbarSelectOption.ts +++ b/packages/main/src/ToolbarSelectOption.ts @@ -3,7 +3,6 @@ import type { DefaultSlot } from "@ui5/webcomponents-base/dist/UI5Element.js"; import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; import slot from "@ui5/webcomponents-base/dist/decorators/slot-strict.js"; import property from "@ui5/webcomponents-base/dist/decorators/property.js"; -import type ToolbarSelect from "./ToolbarSelect.js"; /** * @class @@ -34,32 +33,7 @@ class ToolbarSelectOption extends UI5Element { * @public */ @property({ type: Boolean }) - set selected(value: boolean) { - if (value) { - this.setAttribute("selected", ""); - this._clearSiblingsAndSync(); - } else { - this.removeAttribute("selected"); - } - } - - get selected(): boolean { - return this.hasAttribute("selected"); - } - - _clearSiblingsAndSync(): void { - const parent = this.parentElement as ToolbarSelect; - if (parent) { - parent.options?.forEach(option => { - if (option !== this) { - option.removeAttribute("selected"); - } - }); - if (parent.select) { - parent.select.value = this.value !== undefined && this.value !== "" ? this.value : (this.textContent || ""); - } - } - } + selected = false; /** * Defines the text of the component. diff --git a/packages/main/src/ToolbarSelectTemplate.tsx b/packages/main/src/ToolbarSelectTemplate.tsx index b1ffef24f03b..1ac2d1384de4 100644 --- a/packages/main/src/ToolbarSelectTemplate.tsx +++ b/packages/main/src/ToolbarSelectTemplate.tsx @@ -7,7 +7,6 @@ export default function ToolbarSelectTemplate(this: ToolbarSelect) {