diff --git a/packages/main/cypress/specs/Tokenizer.cy.tsx b/packages/main/cypress/specs/Tokenizer.cy.tsx index c30ae1259df1..310c7003a0ef 100755 --- a/packages/main/cypress/specs/Tokenizer.cy.tsx +++ b/packages/main/cypress/specs/Tokenizer.cy.tsx @@ -1566,3 +1566,80 @@ describe("Keyboard Handling", () => { .should("have.attr", "selected"); }); }); + +describe("Tokenizer - getFocusDomRef Method", () => { + it("should focus the last focused token on tokenizer focus if its visible", () => { + const onButtonClick = () => { + document.getElementById("tokenizer").focus(); + } + cy.mount( + <> + + + + + + + + + + ); + + cy.get("[ui5-token]") + .eq(1) + .realClick(); + + cy.get("[ui5-button]") + .eq(0) + .realClick(); + + cy.get("[ui5-button]") + .eq(1) + .realClick(); + + cy.get("[ui5-token]") + .eq(1) + .should("be.focused"); + }); + + it("should focus the first token if the previously focused token is not visible", () => { + const onButtonClick = () => { + document.getElementById("nmore-token").focus(); + } + cy.mount( + <> +
+ + + + + + + +
+ + + + ); + + cy.get("[ui5-button]") + .eq(1) + .realClick(); + + cy.get("[ui5-token]") + .eq(2) + .realClick(); + + cy.get("[ui5-button]") + .eq(0) + .realClick(); + + cy.get("[ui5-button]") + .eq(1) + .realClick(); + + cy.get("[ui5-token]") + .eq(0) + .should("be.focused"); + }); +}); diff --git a/packages/main/src/Tokenizer.ts b/packages/main/src/Tokenizer.ts index 2ee34d93796c..3fdd45f81874 100644 --- a/packages/main/src/Tokenizer.ts +++ b/packages/main/src/Tokenizer.ts @@ -364,6 +364,8 @@ class Tokenizer extends UI5Element implements IFormInputElement { _previousToken: Token | null = null; _focusedElementBeforeOpen?: HTMLElement | null; _deletedDialogItems!: Token[]; + _lastFocusedToken: Token | null = null; + _isFocusSetInternally: boolean = false; /** * Scroll to end when tokenizer is expanded * @private @@ -496,7 +498,7 @@ class Tokenizer extends UI5Element implements IFormInputElement { this._nMoreCount = this.overflownTokens.length; - if (firstToken && !this.disabled && !this.preventInitialFocus && !this._skipTabIndex) { + if (firstToken && !this.disabled && !this.preventInitialFocus && !this._skipTabIndex && !this._isFocusSetInternally) { firstToken.forcedTabIndex = "0"; } @@ -946,6 +948,7 @@ class Tokenizer extends UI5Element implements IFormInputElement { _onfocusin(e: FocusEvent) { const target = e.target as Token; + this._lastFocusedToken = target; if (target && target.toBeDeleted) { this._tokenDeleting = true; @@ -975,6 +978,7 @@ class Tokenizer extends UI5Element implements IFormInputElement { if (!this.contains(relatedTarget)) { this._tokens[0].forcedTabIndex = "0"; + this._isFocusSetInternally = false; this._skipTabIndex = false; } @@ -984,6 +988,22 @@ class Tokenizer extends UI5Element implements IFormInputElement { } } + /** + * Determines the DOM element to focus when the Tokenizer receives focus. + * If the last-focused token is not overflown, focus is restored to it. + * Otherwise, the focus defaults to the first visible token. + */ + getFocusDomRef(): HTMLElement | undefined { + if (this._lastFocusedToken && !this.overflownTokens.includes(this._lastFocusedToken)) { + this._itemNav._currentIndex = this.tokens.indexOf(this._lastFocusedToken); + this._isFocusSetInternally = true; + this.tokens[0].forcedTabIndex = "-1"; + } else { + this._itemNav._currentIndex = 0; + } + return this._itemNav._getCurrentItem(); + } + _toggleTokenSelection(tokens: Array) { if (!tokens || !tokens.length) { return;