From 431e5ac1b1b716ee4ea6a965653d564fac1427b0 Mon Sep 17 00:00:00 2001 From: Nikolay Deshev Date: Wed, 9 Jul 2025 18:37:45 +0300 Subject: [PATCH 1/7] fix(ui5-tokenizer): sync popover list items with token text changes - Add invalidateOnChildChange config to tokens slot to watch for text property changes to ensure popover list items automatically update when token text is modified fixes: #11825 --- packages/main/cypress/specs/Tokenizer.cy.tsx | 106 +++++++++++++++++++ packages/main/src/Tokenizer.ts | 4 + 2 files changed, 110 insertions(+) diff --git a/packages/main/cypress/specs/Tokenizer.cy.tsx b/packages/main/cypress/specs/Tokenizer.cy.tsx index dbc36b53f1ef..9959a24aa9c3 100644 --- a/packages/main/cypress/specs/Tokenizer.cy.tsx +++ b/packages/main/cypress/specs/Tokenizer.cy.tsx @@ -208,3 +208,109 @@ describe("Tokenizer - multi-line and Clear All", () => { }); }); +describe("Tokenizer - Popover List Item Text Updates", () => { + it("updates list item text in popover when token text changes", () => { + cy.mount( + + + + + + + + + + ); + + cy.get("#test-token-text-update") + .shadow() + .find(".ui5-tokenizer-more-text") + .realClick(); + + cy.get("#test-token-text-update") + .shadow() + .find("[ui5-responsive-popover]") + .should("be.visible"); + + cy.get("#test-token-text-update") + .shadow() + .find("[ui5-responsive-popover] [ui5-list] [ui5-li]") + .eq(0) + .should("contain.text", "Original Text"); + + cy.get("#token-to-modify").then($token => { + const token = $token.get(0) as Token; + token.text = "Updated Text"; + }); + + cy.get("#test-token-text-update") + .shadow() + .find("[ui5-responsive-popover] [ui5-list] [ui5-li]") + .eq(0) + .should("contain.text", "Updated Text"); + + cy.get("#test-token-text-update") + .shadow() + .find("[ui5-responsive-popover] [ui5-list] [ui5-li]") + .eq(0) + .should("not.contain.text", "Original Text"); + }); + + it("updates multiple list items when multiple token texts change", () => { + cy.mount( + + + + + + + + ); + + cy.get("#test-multiple-token-updates") + .shadow() + .find(".ui5-tokenizer-more-text") + .realClick(); + + cy.get("#test-multiple-token-updates") + .shadow() + .find("[ui5-responsive-popover] [ui5-list] [ui5-li]") + .eq(0) + .should("contain.text", "Token 1"); + + cy.get("#test-multiple-token-updates") + .shadow() + .find("[ui5-responsive-popover] [ui5-list] [ui5-li]") + .eq(1) + .should("contain.text", "Token 2"); + + cy.get("#token-1").then($token => { + const token = $token.get(0) as Token; + token.text = "Modified Token 1"; + }); + + cy.get("#token-2").then($token => { + const token = $token.get(0) as Token; + token.text = "Modified Token 2"; + }); + + cy.get("#test-multiple-token-updates") + .shadow() + .find("[ui5-responsive-popover] [ui5-list] [ui5-li]") + .eq(0) + .should("contain.text", "Modified Token 1"); + + cy.get("#test-multiple-token-updates") + .shadow() + .find("[ui5-responsive-popover] [ui5-list] [ui5-li]") + .eq(1) + .should("contain.text", "Modified Token 2"); + + // Verify unchanged token remains the same + cy.get("#test-multiple-token-updates") + .shadow() + .find("[ui5-responsive-popover] [ui5-list] [ui5-li]") + .eq(2) + .should("contain.text", "Token 3"); + }); +}); diff --git a/packages/main/src/Tokenizer.ts b/packages/main/src/Tokenizer.ts index da74d3d498bb..9cad4b2c9e98 100644 --- a/packages/main/src/Tokenizer.ts +++ b/packages/main/src/Tokenizer.ts @@ -333,6 +333,10 @@ class Tokenizer extends UI5Element { type: HTMLElement, "default": true, individualSlots: true, + invalidateOnChildChange: { + properties: ["text"], + slots: false, + }, }) tokens!: Array; From e9c8e091e7fb0561504f0c6877e3497ebf08a4f6 Mon Sep 17 00:00:00 2001 From: Nikolay Deshev Date: Sat, 5 Jul 2025 16:22:07 +0300 Subject: [PATCH 2/7] fix(ui5-tokenizer): sync popover list items with token text Changes use the StandardListItem 'text' property instead of slot to ensure correct text rendering in popover list items --- packages/main/src/TokenizerPopoverTemplate.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/main/src/TokenizerPopoverTemplate.tsx b/packages/main/src/TokenizerPopoverTemplate.tsx index 4fb138b0a7b9..58fa75b7b8d1 100644 --- a/packages/main/src/TokenizerPopoverTemplate.tsx +++ b/packages/main/src/TokenizerPopoverTemplate.tsx @@ -42,7 +42,7 @@ export default function TokenizerPopoverTemplate(this: Tokenizer) { onItemDelete={this.itemDelete} > {this._tokens - .map(token => {token.text})} + .map(token => )} {this._isPhone && From 53a76a8544f48fbe0b80d172966e6afd2f6fb892 Mon Sep 17 00:00:00 2001 From: Nikolay Deshev Date: Sat, 5 Jul 2025 16:42:28 +0300 Subject: [PATCH 3/7] fix(ui5-tokenizer): sync popover list items with token text changes update tests --- packages/main/cypress/specs/Tokenizer.cy.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/main/cypress/specs/Tokenizer.cy.tsx b/packages/main/cypress/specs/Tokenizer.cy.tsx index 9959a24aa9c3..266c36425315 100644 --- a/packages/main/cypress/specs/Tokenizer.cy.tsx +++ b/packages/main/cypress/specs/Tokenizer.cy.tsx @@ -236,7 +236,7 @@ describe("Tokenizer - Popover List Item Text Updates", () => { .shadow() .find("[ui5-responsive-popover] [ui5-list] [ui5-li]") .eq(0) - .should("contain.text", "Original Text"); + .should("have.attr", "text", "Original Text"); cy.get("#token-to-modify").then($token => { const token = $token.get(0) as Token; @@ -247,13 +247,13 @@ describe("Tokenizer - Popover List Item Text Updates", () => { .shadow() .find("[ui5-responsive-popover] [ui5-list] [ui5-li]") .eq(0) - .should("contain.text", "Updated Text"); + .should("have.attr", "text", "Updated Text"); cy.get("#test-token-text-update") .shadow() .find("[ui5-responsive-popover] [ui5-list] [ui5-li]") .eq(0) - .should("not.contain.text", "Original Text"); + .should("not.have.attr", "text", "Original Text"); }); it("updates multiple list items when multiple token texts change", () => { @@ -276,13 +276,13 @@ describe("Tokenizer - Popover List Item Text Updates", () => { .shadow() .find("[ui5-responsive-popover] [ui5-list] [ui5-li]") .eq(0) - .should("contain.text", "Token 1"); + .should("have.attr", "text", "Token 1"); cy.get("#test-multiple-token-updates") .shadow() .find("[ui5-responsive-popover] [ui5-list] [ui5-li]") .eq(1) - .should("contain.text", "Token 2"); + .should("have.attr", "text", "Token 2"); cy.get("#token-1").then($token => { const token = $token.get(0) as Token; @@ -298,19 +298,19 @@ describe("Tokenizer - Popover List Item Text Updates", () => { .shadow() .find("[ui5-responsive-popover] [ui5-list] [ui5-li]") .eq(0) - .should("contain.text", "Modified Token 1"); + .should("have.attr", "text", "Modified Token 1"); cy.get("#test-multiple-token-updates") .shadow() .find("[ui5-responsive-popover] [ui5-list] [ui5-li]") .eq(1) - .should("contain.text", "Modified Token 2"); + .should("have.attr", "text", "Modified Token 2"); // Verify unchanged token remains the same cy.get("#test-multiple-token-updates") .shadow() .find("[ui5-responsive-popover] [ui5-list] [ui5-li]") .eq(2) - .should("contain.text", "Token 3"); + .should("have.attr", "text", "Token 3"); }); }); From 4a9902f6f8fa3ddbf78b33e19ac16cfad58e2f8e Mon Sep 17 00:00:00 2001 From: Nikolay Deshev Date: Wed, 9 Jul 2025 13:04:42 +0300 Subject: [PATCH 4/7] fix(ui5-tokenizer): sync popover list items with token text changes apply code review comments --- packages/main/cypress/specs/Tokenizer.cy.tsx | 29 +++++++++----------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/packages/main/cypress/specs/Tokenizer.cy.tsx b/packages/main/cypress/specs/Tokenizer.cy.tsx index 266c36425315..9ba5abb985fb 100644 --- a/packages/main/cypress/specs/Tokenizer.cy.tsx +++ b/packages/main/cypress/specs/Tokenizer.cy.tsx @@ -222,28 +222,25 @@ describe("Tokenizer - Popover List Item Text Updates", () => { ); - cy.get("#test-token-text-update") + cy.get("[ui5-tokenizer]") .shadow() .find(".ui5-tokenizer-more-text") .realClick(); - cy.get("#test-token-text-update") + cy.get("[ui5-tokenizer]") .shadow() .find("[ui5-responsive-popover]") .should("be.visible"); - cy.get("#test-token-text-update") + cy.get("[ui5-tokenizer]") .shadow() .find("[ui5-responsive-popover] [ui5-list] [ui5-li]") .eq(0) .should("have.attr", "text", "Original Text"); - cy.get("#token-to-modify").then($token => { - const token = $token.get(0) as Token; - token.text = "Updated Text"; - }); + cy.get("#token-to-modify").invoke("prop", "text", "Updated Text"); - cy.get("#test-token-text-update") + cy.get("[ui5-tokenizer]") .shadow() .find("[ui5-responsive-popover] [ui5-list] [ui5-li]") .eq(0) @@ -267,47 +264,47 @@ describe("Tokenizer - Popover List Item Text Updates", () => { ); - cy.get("#test-multiple-token-updates") + cy.get("[ui5-tokenizer]") .shadow() .find(".ui5-tokenizer-more-text") .realClick(); - cy.get("#test-multiple-token-updates") + cy.get("[ui5-tokenizer]") .shadow() .find("[ui5-responsive-popover] [ui5-list] [ui5-li]") .eq(0) .should("have.attr", "text", "Token 1"); - cy.get("#test-multiple-token-updates") + cy.get("[ui5-tokenizer]") .shadow() .find("[ui5-responsive-popover] [ui5-list] [ui5-li]") .eq(1) .should("have.attr", "text", "Token 2"); - cy.get("#token-1").then($token => { + cy.get("[ui5-token]").then($token => { const token = $token.get(0) as Token; token.text = "Modified Token 1"; }); - cy.get("#token-2").then($token => { + cy.get("[ui5-token]").then($token => { const token = $token.get(0) as Token; token.text = "Modified Token 2"; }); - cy.get("#test-multiple-token-updates") + cy.get("[ui5-tokenizer]") .shadow() .find("[ui5-responsive-popover] [ui5-list] [ui5-li]") .eq(0) .should("have.attr", "text", "Modified Token 1"); - cy.get("#test-multiple-token-updates") + cy.get("[ui5-tokenizer]") .shadow() .find("[ui5-responsive-popover] [ui5-list] [ui5-li]") .eq(1) .should("have.attr", "text", "Modified Token 2"); // Verify unchanged token remains the same - cy.get("#test-multiple-token-updates") + cy.get("[ui5-tokenizer]") .shadow() .find("[ui5-responsive-popover] [ui5-list] [ui5-li]") .eq(2) From d3ca93f4a932473829b2250372ea400361dc4e71 Mon Sep 17 00:00:00 2001 From: Nikolay Deshev Date: Wed, 9 Jul 2025 17:00:18 +0300 Subject: [PATCH 5/7] fix(ui5-tokenizer): sync popover list items with token text changes fix test --- packages/main/cypress/specs/Tokenizer.cy.tsx | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/main/cypress/specs/Tokenizer.cy.tsx b/packages/main/cypress/specs/Tokenizer.cy.tsx index 9ba5abb985fb..1c46d56e94ec 100644 --- a/packages/main/cypress/specs/Tokenizer.cy.tsx +++ b/packages/main/cypress/specs/Tokenizer.cy.tsx @@ -281,15 +281,8 @@ describe("Tokenizer - Popover List Item Text Updates", () => { .eq(1) .should("have.attr", "text", "Token 2"); - cy.get("[ui5-token]").then($token => { - const token = $token.get(0) as Token; - token.text = "Modified Token 1"; - }); - - cy.get("[ui5-token]").then($token => { - const token = $token.get(0) as Token; - token.text = "Modified Token 2"; - }); + cy.get("[ui5-token]").eq(0).invoke("prop", "text", "Modified Token 1"); + cy.get("[ui5-token]").eq(1).invoke("prop", "text", "Modified Token 2"); cy.get("[ui5-tokenizer]") .shadow() From 5fe328d914efbd2e03ef66090dc8fdbc099a1280 Mon Sep 17 00:00:00 2001 From: Nikolay Deshev Date: Wed, 9 Jul 2025 18:45:08 +0300 Subject: [PATCH 6/7] fix(ui5-tokenizer): sync popover list items with token text changes modify unstable ui5-multi-combobox tests --- .../main/cypress/specs/MultiComboBox.cy.tsx | 67 +++++++++---------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/packages/main/cypress/specs/MultiComboBox.cy.tsx b/packages/main/cypress/specs/MultiComboBox.cy.tsx index 2d551bc09259..7d9d126779f9 100644 --- a/packages/main/cypress/specs/MultiComboBox.cy.tsx +++ b/packages/main/cypress/specs/MultiComboBox.cy.tsx @@ -219,8 +219,10 @@ describe("MultiComboBox RTL/LTR Arrow Navigation", () => { .find("[ui5-tokenizer]") .find("[ui5-token]") .last() - .should("be.visible") - .should("be.focused"); + .as ("lastToken"); + + cy.get("@lastToken").should("be.visible"); + cy.get("@lastToken").should("be.focused"); }); it("should focus last token on arrow left in LTR mode when input is at start", () => { @@ -263,8 +265,10 @@ describe("MultiComboBox RTL/LTR Arrow Navigation", () => { .find("[ui5-tokenizer]") .find("[ui5-token]") .last() - .should("be.visible") - .should("be.focused"); + .as ("lastToken"); + + cy.get("@lastToken").should("be.visible"); + cy.get("@lastToken").should("be.focused"); }); it("should not focus token when cursor is not at start of input in RTL mode", () => { @@ -283,7 +287,6 @@ describe("MultiComboBox RTL/LTR Arrow Navigation", () => { .realClick(); cy.get("@mcb").should("be.focused"); - cy.get("@mcb") .shadow() @@ -297,23 +300,14 @@ describe("MultiComboBox RTL/LTR Arrow Navigation", () => { cy.get("@mcb").realPress("ArrowRight"); - cy.get("@mcb") - .shadow() - .find("input") - .as("input") - .realClick(); - - cy.get("@input") - .should("be.focused") - .should(($input) => { - expect(($input[0] as HTMLInputElement).selectionStart).to.equal(3); - }); - cy.get("@mcb") .shadow() .find("[ui5-tokenizer]") .find("[ui5-token]") - .should("not.be.focused"); + .as ("lastToken"); + + cy.get("@lastToken").should("be.visible"); + cy.get("@lastToken").should("not.be.focused"); }); it("should not focus token when text is selected in RTL mode", () => { @@ -350,7 +344,11 @@ describe("MultiComboBox RTL/LTR Arrow Navigation", () => { .shadow() .find("[ui5-tokenizer]") .find("[ui5-token]") - .should("not.have.focus"); + .as ("lastToken"); + + cy.get("@lastToken").should("be.visible"); + cy.get("@lastToken").should("not.be.focused"); + }); it("should navigate from last token back to input with arrow left in RTL mode", () => { @@ -378,17 +376,13 @@ describe("MultiComboBox RTL/LTR Arrow Navigation", () => { .find("[ui5-tokenizer]") .find("[ui5-token]") .last() - .as("lastToken") - .should("have.focus"); + .as("lastToken"); - cy.get("@lastToken") - .should("be.focused") - .realPress("ArrowLeft"); + cy.get("@lastToken").should("be.visible"); + cy.get("@lastToken").should("be.focused"); + cy.get("@lastToken").realPress("ArrowLeft"); - cy.get("@mcb") - .shadow() - .find("input") - .should("be.focused"); + cy.get("@mcb").should("be.focused"); }); it("should navigate from last token back to input with arrow right in LTR mode", () => { @@ -416,15 +410,14 @@ describe("MultiComboBox RTL/LTR Arrow Navigation", () => { .find("[ui5-tokenizer]") .find("[ui5-token]") .last() - .as("lastToken") - .should("be.focused"); + .as("lastToken"); + cy.get("@lastToken").should("be.visible"); + cy.get("@lastToken").should("be.focused"); cy.get("@lastToken").realPress("ArrowRight"); - cy.get("@mcb") - .shadow() - .find("input") - .should("be.focused"); + cy.get("@mcb").should("be.visible"); + cy.get("@mcb").should("be.focused"); }); it("should handle empty input case in RTL mode", () => { @@ -464,7 +457,11 @@ describe("MultiComboBox RTL/LTR Arrow Navigation", () => { .find("[ui5-tokenizer]") .find("[ui5-token]") .last() - .should("have.focus"); + .as("lastToken"); + + cy.get("@lastToken").should("be.visible"); + cy.get("@lastToken").should("be.focused"); + }); }); From 80c21db08fcd53a0ac437b2e84780b0707365139 Mon Sep 17 00:00:00 2001 From: Nikolay Deshev Date: Wed, 9 Jul 2025 19:19:50 +0300 Subject: [PATCH 7/7] fix(ui5-tokenizer): sync popover list items with token text changes modify unstable mcb tests --- packages/main/cypress/specs/MultiComboBox.cy.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/main/cypress/specs/MultiComboBox.cy.tsx b/packages/main/cypress/specs/MultiComboBox.cy.tsx index 7d9d126779f9..4fd77461990f 100644 --- a/packages/main/cypress/specs/MultiComboBox.cy.tsx +++ b/packages/main/cypress/specs/MultiComboBox.cy.tsx @@ -348,7 +348,6 @@ describe("MultiComboBox RTL/LTR Arrow Navigation", () => { cy.get("@lastToken").should("be.visible"); cy.get("@lastToken").should("not.be.focused"); - }); it("should navigate from last token back to input with arrow left in RTL mode", () => { @@ -410,11 +409,7 @@ describe("MultiComboBox RTL/LTR Arrow Navigation", () => { .find("[ui5-tokenizer]") .find("[ui5-token]") .last() - .as("lastToken"); - - cy.get("@lastToken").should("be.visible"); - cy.get("@lastToken").should("be.focused"); - cy.get("@lastToken").realPress("ArrowRight"); + .realPress("ArrowRight"); cy.get("@mcb").should("be.visible"); cy.get("@mcb").should("be.focused");