diff --git a/packages/main/src/MultiInput.ts b/packages/main/src/MultiInput.ts index 9c24480ae5e5..27c36a7e333f 100644 --- a/packages/main/src/MultiInput.ts +++ b/packages/main/src/MultiInput.ts @@ -223,11 +223,16 @@ class MultiInput extends Input { return this._focusFirstToken(e); } - if (isLeft(e) || isBackSpace(e)) { + if (isLeft(e)) { this._skipOpenSuggestions = true; return this._handleLeft(e); } + if (isBackSpace(e)) { + this._skipOpenSuggestions = true; + return this._handleBackspace(e); + } + this._skipOpenSuggestions = false; if (isShow(e)) { @@ -299,6 +304,21 @@ class MultiInput extends Input { } } + _handleBackspace(e: KeyboardEvent) { + const cursorPosition = this.getDomRef()!.querySelector(`input`)!.selectionStart; + const selectionEnd = this.getDomRef()!.querySelector(`input`)!.selectionEnd; + const isValueSelected = cursorPosition === 0 && selectionEnd === this.value.length; + const tokens = this.tokens; + const lastToken = tokens.length && tokens[tokens.length - 1]; + + // selectionStart property applies only to inputs of types text, search, URL, tel, and password + if ((!this.value || (this.value && cursorPosition === 0 && !isValueSelected)) && lastToken) { + e.preventDefault(); + lastToken.focus(); + this.tokenizer._itemNav.setCurrentItem(lastToken); + } + } + _focusFirstToken(e: KeyboardEvent) { const tokens = this.tokens; const firstToken = tokens.length && tokens[0]; diff --git a/packages/main/test/specs/MultiInput.spec.js b/packages/main/test/specs/MultiInput.spec.js index 849bc41f490c..4c56359ad65c 100644 --- a/packages/main/test/specs/MultiInput.spec.js +++ b/packages/main/test/specs/MultiInput.spec.js @@ -9,6 +9,9 @@ const getTokenizerPopoverId = async (inputId) => { }, inputId); } +const isMacOS = process.platform === 'darwin'; +const keyCtrlToPress = isMacOS ? 'Command' : 'Control'; + describe("MultiInput general interaction", () => { before(async () => { await browser.url(`test/pages/MultiInput.html`); @@ -563,6 +566,40 @@ describe("Keyboard handling", () => { assert.ok(await lastToken.getProperty("focused"), "The last token is focused on Backspace"); }); + it("should focus token last token when caret is at the beginning of the value", async () => { + const input = await browser.$("#two-tokens"); + const innerInput = await input.shadow$("input"); + const lastToken = await browser.$("#two-tokens ui5-token#secondToken"); + + // Act + await innerInput.click(); + await browser.keys("ArrowLeft"); + await browser.keys("ArrowLeft"); + await browser.keys("ArrowLeft"); + await browser.keys("Backspace"); + + assert.ok(await lastToken.getProperty("focused"), "The last token is focused on Backspace"); + }); + + it("should delete value on backspace", async () => { + const input = await browser.$("#two-tokens"); + const innerInput = await input.shadow$("input"); + const lastToken = await browser.$("#two-tokens ui5-token#secondToken"); + + // Act + await innerInput.click(); + await browser.keys([keyCtrlToPress, "a"]); + await browser.keys("Backspace"); + + // Assert + assert.strictEqual(await input.getProperty("value"), "", "Value is deleted on Backspace"); + + await browser.keys("Backspace"); + + assert.notOk(await input.getProperty("focused"), "The input loses focus on Backspace"); + assert.ok(await lastToken.getProperty("focused"), "The last token is focused on Backspace"); + }); + it("should delete token on backspace", async () => { const input = await browser.$("#two-tokens"); const innerInput = await input.shadow$("input");