Skip to content

Commit 1c38b60

Browse files
authored
fix(ui5-tokenizer): sync popover list items with token text changes (#11854)
* 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 - use the StandardListItem 'text' property instead of slot to ensure correct text rendering in popover list items
1 parent c08c371 commit 1c38b60

File tree

4 files changed

+130
-38
lines changed

4 files changed

+130
-38
lines changed

packages/main/cypress/specs/MultiComboBox.cy.tsx

Lines changed: 29 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,10 @@ describe("MultiComboBox RTL/LTR Arrow Navigation", () => {
219219
.find("[ui5-tokenizer]")
220220
.find("[ui5-token]")
221221
.last()
222-
.should("be.visible")
223-
.should("be.focused");
222+
.as ("lastToken");
223+
224+
cy.get("@lastToken").should("be.visible");
225+
cy.get("@lastToken").should("be.focused");
224226
});
225227

226228
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", () => {
263265
.find("[ui5-tokenizer]")
264266
.find("[ui5-token]")
265267
.last()
266-
.should("be.visible")
267-
.should("be.focused");
268+
.as ("lastToken");
269+
270+
cy.get("@lastToken").should("be.visible");
271+
cy.get("@lastToken").should("be.focused");
268272
});
269273

270274
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", () => {
283287
.realClick();
284288

285289
cy.get("@mcb").should("be.focused");
286-
287290

288291
cy.get("@mcb")
289292
.shadow()
@@ -297,23 +300,14 @@ describe("MultiComboBox RTL/LTR Arrow Navigation", () => {
297300

298301
cy.get("@mcb").realPress("ArrowRight");
299302

300-
cy.get("@mcb")
301-
.shadow()
302-
.find("input")
303-
.as("input")
304-
.realClick();
305-
306-
cy.get("@input")
307-
.should("be.focused")
308-
.should(($input) => {
309-
expect(($input[0] as HTMLInputElement).selectionStart).to.equal(3);
310-
});
311-
312303
cy.get("@mcb")
313304
.shadow()
314305
.find("[ui5-tokenizer]")
315306
.find("[ui5-token]")
316-
.should("not.be.focused");
307+
.as ("lastToken");
308+
309+
cy.get("@lastToken").should("be.visible");
310+
cy.get("@lastToken").should("not.be.focused");
317311
});
318312

319313
it("should not focus token when text is selected in RTL mode", () => {
@@ -350,7 +344,10 @@ describe("MultiComboBox RTL/LTR Arrow Navigation", () => {
350344
.shadow()
351345
.find("[ui5-tokenizer]")
352346
.find("[ui5-token]")
353-
.should("not.have.focus");
347+
.as ("lastToken");
348+
349+
cy.get("@lastToken").should("be.visible");
350+
cy.get("@lastToken").should("not.be.focused");
354351
});
355352

356353
it("should navigate from last token back to input with arrow left in RTL mode", () => {
@@ -378,17 +375,13 @@ describe("MultiComboBox RTL/LTR Arrow Navigation", () => {
378375
.find("[ui5-tokenizer]")
379376
.find("[ui5-token]")
380377
.last()
381-
.as("lastToken")
382-
.should("have.focus");
378+
.as("lastToken");
383379

384-
cy.get("@lastToken")
385-
.should("be.focused")
386-
.realPress("ArrowLeft");
380+
cy.get("@lastToken").should("be.visible");
381+
cy.get("@lastToken").should("be.focused");
382+
cy.get("@lastToken").realPress("ArrowLeft");
387383

388-
cy.get("@mcb")
389-
.shadow()
390-
.find("input")
391-
.should("be.focused");
384+
cy.get("@mcb").should("be.focused");
392385
});
393386

394387
it("should navigate from last token back to input with arrow right in LTR mode", () => {
@@ -416,15 +409,10 @@ describe("MultiComboBox RTL/LTR Arrow Navigation", () => {
416409
.find("[ui5-tokenizer]")
417410
.find("[ui5-token]")
418411
.last()
419-
.as("lastToken")
420-
.should("be.focused");
421-
422-
cy.get("@lastToken").realPress("ArrowRight");
412+
.realPress("ArrowRight");
423413

424-
cy.get("@mcb")
425-
.shadow()
426-
.find("input")
427-
.should("be.focused");
414+
cy.get("@mcb").should("be.visible");
415+
cy.get("@mcb").should("be.focused");
428416
});
429417

430418
it("should handle empty input case in RTL mode", () => {
@@ -464,7 +452,11 @@ describe("MultiComboBox RTL/LTR Arrow Navigation", () => {
464452
.find("[ui5-tokenizer]")
465453
.find("[ui5-token]")
466454
.last()
467-
.should("have.focus");
455+
.as("lastToken");
456+
457+
cy.get("@lastToken").should("be.visible");
458+
cy.get("@lastToken").should("be.focused");
459+
468460
});
469461
});
470462

packages/main/cypress/specs/Tokenizer.cy.tsx

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,3 +208,99 @@ describe("Tokenizer - multi-line and Clear All", () => {
208208
});
209209
});
210210

211+
describe("Tokenizer - Popover List Item Text Updates", () => {
212+
it("updates list item text in popover when token text changes", () => {
213+
cy.mount(
214+
<Tokenizer id="test-token-text-update" style={{ width: "100px" }}>
215+
<Token text="Original Text" id="token-to-modify"></Token>
216+
<Token text="Bulgaria"></Token>
217+
<Token text="Canada"></Token>
218+
<Token text="Denmark"></Token>
219+
<Token text="Estonia"></Token>
220+
<Token text="Finland"></Token>
221+
<Token text="Germany"></Token>
222+
</Tokenizer>
223+
);
224+
225+
cy.get<Tokenizer>("[ui5-tokenizer]")
226+
.shadow()
227+
.find(".ui5-tokenizer-more-text")
228+
.realClick();
229+
230+
cy.get<Tokenizer>("[ui5-tokenizer]")
231+
.shadow()
232+
.find("[ui5-responsive-popover]")
233+
.should("be.visible");
234+
235+
cy.get<Tokenizer>("[ui5-tokenizer]")
236+
.shadow()
237+
.find("[ui5-responsive-popover] [ui5-list] [ui5-li]")
238+
.eq(0)
239+
.should("have.attr", "text", "Original Text");
240+
241+
cy.get("#token-to-modify").invoke("prop", "text", "Updated Text");
242+
243+
cy.get<Tokenizer>("[ui5-tokenizer]")
244+
.shadow()
245+
.find("[ui5-responsive-popover] [ui5-list] [ui5-li]")
246+
.eq(0)
247+
.should("have.attr", "text", "Updated Text");
248+
249+
cy.get("#test-token-text-update")
250+
.shadow()
251+
.find("[ui5-responsive-popover] [ui5-list] [ui5-li]")
252+
.eq(0)
253+
.should("not.have.attr", "text", "Original Text");
254+
});
255+
256+
it("updates multiple list items when multiple token texts change", () => {
257+
cy.mount(
258+
<Tokenizer id="test-multiple-token-updates" style={{ width: "100px" }}>
259+
<Token text="Token 1" id="token-1"></Token>
260+
<Token text="Token 2" id="token-2"></Token>
261+
<Token text="Token 3" id="token-3"></Token>
262+
<Token text="Denmark"></Token>
263+
<Token text="Estonia"></Token>
264+
</Tokenizer>
265+
);
266+
267+
cy.get<Tokenizer>("[ui5-tokenizer]")
268+
.shadow()
269+
.find(".ui5-tokenizer-more-text")
270+
.realClick();
271+
272+
cy.get<Tokenizer>("[ui5-tokenizer]")
273+
.shadow()
274+
.find("[ui5-responsive-popover] [ui5-list] [ui5-li]")
275+
.eq(0)
276+
.should("have.attr", "text", "Token 1");
277+
278+
cy.get<Tokenizer>("[ui5-tokenizer]")
279+
.shadow()
280+
.find("[ui5-responsive-popover] [ui5-list] [ui5-li]")
281+
.eq(1)
282+
.should("have.attr", "text", "Token 2");
283+
284+
cy.get<Token>("[ui5-token]").eq(0).invoke("prop", "text", "Modified Token 1");
285+
cy.get<Token>("[ui5-token]").eq(1).invoke("prop", "text", "Modified Token 2");
286+
287+
cy.get<Tokenizer>("[ui5-tokenizer]")
288+
.shadow()
289+
.find("[ui5-responsive-popover] [ui5-list] [ui5-li]")
290+
.eq(0)
291+
.should("have.attr", "text", "Modified Token 1");
292+
293+
cy.get<Tokenizer>("[ui5-tokenizer]")
294+
.shadow()
295+
.find("[ui5-responsive-popover] [ui5-list] [ui5-li]")
296+
.eq(1)
297+
.should("have.attr", "text", "Modified Token 2");
298+
299+
// Verify unchanged token remains the same
300+
cy.get<Tokenizer>("[ui5-tokenizer]")
301+
.shadow()
302+
.find("[ui5-responsive-popover] [ui5-list] [ui5-li]")
303+
.eq(2)
304+
.should("have.attr", "text", "Token 3");
305+
});
306+
});

packages/main/src/Tokenizer.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,10 @@ class Tokenizer extends UI5Element {
333333
type: HTMLElement,
334334
"default": true,
335335
individualSlots: true,
336+
invalidateOnChildChange: {
337+
properties: ["text"],
338+
slots: false,
339+
},
336340
})
337341
tokens!: Array<Token>;
338342

packages/main/src/TokenizerPopoverTemplate.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export default function TokenizerPopoverTemplate(this: Tokenizer) {
4242
onItemDelete={this.itemDelete}
4343
>
4444
{this._tokens
45-
.map(token => <ListItemStandard key={String(token._id)} data-ui5-token-ref-id={token._id} wrappingType="Normal">{token.text}</ListItemStandard>)}
45+
.map(token => <ListItemStandard key={String(token._id)} data-ui5-token-ref-id={token._id} wrappingType="Normal" text={token.text}></ListItemStandard>)}
4646
</List>
4747

4848
{this._isPhone &&

0 commit comments

Comments
 (0)