From defbdaaf696cf2d8fc96f5cd27b7cafe2a3535fb Mon Sep 17 00:00:00 2001 From: Herbert Lin Date: Mon, 13 May 2024 23:19:44 -0700 Subject: [PATCH 1/5] style: Fix indentation in CandidateBox.test.js --- src/lib/components/tests/CandidateBox.test.js | 168 +++++++++--------- 1 file changed, 84 insertions(+), 84 deletions(-) diff --git a/src/lib/components/tests/CandidateBox.test.js b/src/lib/components/tests/CandidateBox.test.js index 390991a0..62bc4d53 100644 --- a/src/lib/components/tests/CandidateBox.test.js +++ b/src/lib/components/tests/CandidateBox.test.js @@ -332,98 +332,98 @@ it('CandidateBox show not be called if keyboard.candidateBox is undefined upon s }); it('CandidateBox selection should trigger onChange', () => { - const keyboard = new Keyboard({ - layout: { - default: [ - "a b {bksp}" - ] - }, - layoutCandidates: { - a: "1 2 3 4 5 6" - }, - onChange: jest.fn(), - onChangeAll: jest.fn() - }); - - let candidateBoxOnItemSelected; - - const onSelect = jest.fn().mockImplementation((selectedCandidate) => { - candidateBoxOnItemSelected(selectedCandidate); - keyboard.candidateBox.destroy(); - }); - - const candidateBoxRenderFn = keyboard.candidateBox.renderPage; - - jest.spyOn(keyboard.candidateBox, "renderPage").mockImplementation((params) => { - candidateBoxOnItemSelected = params.onItemSelected; - params.onItemSelected = onSelect; - candidateBoxRenderFn(params); - }); - - keyboard.getButtonElement("a").click(); - keyboard.candidateBox.candidateBoxElement.querySelector("li").click(); - - expect(keyboard.options.onChange.mock.calls[0][0]).toBe("a"); - expect(keyboard.options.onChangeAll.mock.calls[0][0]).toMatchObject({"default": "a"}); - - expect(keyboard.options.onChange.mock.calls[1][0]).toBe("1"); - expect(keyboard.options.onChangeAll.mock.calls[1][0]).toMatchObject({"default": "1"}); - keyboard.destroy(); + const keyboard = new Keyboard({ + layout: { + default: [ + "a b {bksp}" + ] + }, + layoutCandidates: { + a: "1 2 3 4 5 6" + }, + onChange: jest.fn(), + onChangeAll: jest.fn() }); - it('CandidateBox normalization will work', () => { - const keyboard = new Keyboard({ - layout: { - default: [ - "a b {bksp}" - ] - }, - layoutCandidates: { - a: "신" - }, - onChange: jest.fn(), - onChangeAll: jest.fn() - }); + let candidateBoxOnItemSelected; - let candidateBoxOnItemSelected; - - const onSelect = jest.fn().mockImplementation((selectedCandidate) => { - candidateBoxOnItemSelected(selectedCandidate); - keyboard.candidateBox.destroy(); - }); + const onSelect = jest.fn().mockImplementation((selectedCandidate) => { + candidateBoxOnItemSelected(selectedCandidate); + keyboard.candidateBox.destroy(); + }); + + const candidateBoxRenderFn = keyboard.candidateBox.renderPage; - const candidateBoxRenderFn = keyboard.candidateBox.renderPage; - - jest.spyOn(keyboard.candidateBox, "renderPage").mockImplementation((params) => { - candidateBoxOnItemSelected = params.onItemSelected; - params.onItemSelected = onSelect; - candidateBoxRenderFn(params); - }); + jest.spyOn(keyboard.candidateBox, "renderPage").mockImplementation((params) => { + candidateBoxOnItemSelected = params.onItemSelected; + params.onItemSelected = onSelect; + candidateBoxRenderFn(params); + }); + + keyboard.getButtonElement("a").click(); + keyboard.candidateBox.candidateBoxElement.querySelector("li").click(); + + expect(keyboard.options.onChange.mock.calls[0][0]).toBe("a"); + expect(keyboard.options.onChangeAll.mock.calls[0][0]).toMatchObject({"default": "a"}); + + expect(keyboard.options.onChange.mock.calls[1][0]).toBe("1"); + expect(keyboard.options.onChangeAll.mock.calls[1][0]).toMatchObject({"default": "1"}); + keyboard.destroy(); +}); + +it('CandidateBox normalization will work', () => { + const keyboard = new Keyboard({ + layout: { + default: [ + "a b {bksp}" + ] + }, + layoutCandidates: { + a: "신" + }, + onChange: jest.fn(), + onChangeAll: jest.fn() + }); + + let candidateBoxOnItemSelected; - keyboard.getButtonElement("a").click(); - keyboard.candidateBox.candidateBoxElement.querySelector("li").click(); + const onSelect = jest.fn().mockImplementation((selectedCandidate) => { + candidateBoxOnItemSelected(selectedCandidate); + keyboard.candidateBox.destroy(); + }); + + const candidateBoxRenderFn = keyboard.candidateBox.renderPage; - expect(keyboard.options.onChange.mock.calls[0][0]).toBe("a"); - expect(keyboard.options.onChangeAll.mock.calls[0][0]).toMatchObject({"default": "a"}); + jest.spyOn(keyboard.candidateBox, "renderPage").mockImplementation((params) => { + candidateBoxOnItemSelected = params.onItemSelected; + params.onItemSelected = onSelect; + candidateBoxRenderFn(params); + }); - // Selected candidate will be normalized - expect(keyboard.options.onChange.mock.calls[1][0]).toBe("신"); - expect(keyboard.options.onChange.mock.calls[1][0].length).toBe(3); - expect(keyboard.options.onChangeAll.mock.calls[1][0]).toMatchObject({"default": "신"}); + keyboard.getButtonElement("a").click(); + keyboard.candidateBox.candidateBoxElement.querySelector("li").click(); - // Selected candidate will not be normalized - keyboard.clearInput(); - keyboard.setOptions({ disableCandidateNormalization: true }); + expect(keyboard.options.onChange.mock.calls[0][0]).toBe("a"); + expect(keyboard.options.onChangeAll.mock.calls[0][0]).toMatchObject({"default": "a"}); - keyboard.getButtonElement("a").click(); - keyboard.candidateBox.candidateBoxElement.querySelector("li").click(); - - expect(keyboard.options.onChange.mock.calls[2][0]).toBe("a"); - expect(keyboard.options.onChangeAll.mock.calls[2][0]).toMatchObject({"default": "a"}); + // Selected candidate will be normalized + expect(keyboard.options.onChange.mock.calls[1][0]).toBe("신"); + expect(keyboard.options.onChange.mock.calls[1][0].length).toBe(3); + expect(keyboard.options.onChangeAll.mock.calls[1][0]).toMatchObject({"default": "신"}); - expect(keyboard.options.onChange.mock.calls[3][0]).toBe("신"); - expect(keyboard.options.onChange.mock.calls[3][0].length).toBe(1); - expect(keyboard.options.onChangeAll.mock.calls[3][0]).toMatchObject({"default": "신"}); + // Selected candidate will not be normalized + keyboard.clearInput(); + keyboard.setOptions({ disableCandidateNormalization: true }); - keyboard.destroy(); - }); \ No newline at end of file + keyboard.getButtonElement("a").click(); + keyboard.candidateBox.candidateBoxElement.querySelector("li").click(); + + expect(keyboard.options.onChange.mock.calls[2][0]).toBe("a"); + expect(keyboard.options.onChangeAll.mock.calls[2][0]).toMatchObject({"default": "a"}); + + expect(keyboard.options.onChange.mock.calls[3][0]).toBe("신"); + expect(keyboard.options.onChange.mock.calls[3][0].length).toBe(1); + expect(keyboard.options.onChangeAll.mock.calls[3][0]).toMatchObject({"default": "신"}); + + keyboard.destroy(); +}); \ No newline at end of file From aa9f98e73eac1a76ee22f5e04d68e22e884e2d3b Mon Sep 17 00:00:00 2001 From: Herbert Lin Date: Mon, 13 May 2024 23:28:10 -0700 Subject: [PATCH 2/5] feat: Add definition for beforeInputUpdate --- src/lib/interfaces.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/interfaces.ts b/src/lib/interfaces.ts index cba5aa9f..a7b735d0 100644 --- a/src/lib/interfaces.ts +++ b/src/lib/interfaces.ts @@ -291,6 +291,11 @@ export interface KeyboardOptions { */ onKeyReleased?: (button: string, e?: MouseEvent) => any; + /** + * Executes the callback function before an input is about to be updated + */ + beforeInputUpdate?: (instance: SimpleKeyboard) => void; + /** * Module options can have any format */ From 9f917fea47fc796d9ecf33265a9c02c17310dc03 Mon Sep 17 00:00:00 2001 From: Herbert Lin Date: Mon, 13 May 2024 23:30:35 -0700 Subject: [PATCH 3/5] feat: Trigger beforeInputUpdate when a candidate is selected in candidate box --- src/lib/components/Keyboard.ts | 7 ++++ src/lib/components/tests/CandidateBox.test.js | 35 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/lib/components/Keyboard.ts b/src/lib/components/Keyboard.ts index cc5479dd..c86d0f2e 100644 --- a/src/lib/components/Keyboard.ts +++ b/src/lib/components/Keyboard.ts @@ -393,6 +393,13 @@ class SimpleKeyboard { candidateStr = selectedCandidate.normalize("NFD"); } + /** + * Perform an action before any input change + */ + if (typeof this.options.beforeInputUpdate === "function") { + this.options.beforeInputUpdate(this); + } + const currentInput = this.getInput(this.options.inputName, true); const initialCaretPosition = this.getCaretPositionEnd() || 0; const inputSubstr = diff --git a/src/lib/components/tests/CandidateBox.test.js b/src/lib/components/tests/CandidateBox.test.js index 62bc4d53..98e89d1b 100644 --- a/src/lib/components/tests/CandidateBox.test.js +++ b/src/lib/components/tests/CandidateBox.test.js @@ -371,6 +371,41 @@ it('CandidateBox selection should trigger onChange', () => { keyboard.destroy(); }); +it('CandidateBox selection should trigger beforeInputChange', () => { + const keyboard = new Keyboard({ + layout: { + default: [ + "a b {bksp}" + ] + }, + layoutCandidates: { + a: "1 2 3 4 5 6" + }, + beforeInputUpdate: jest.fn(), + }); + + let candidateBoxOnItemSelected; + + const onSelect = jest.fn().mockImplementation((selectedCandidate) => { + candidateBoxOnItemSelected(selectedCandidate); + keyboard.candidateBox.destroy(); + }); + + const candidateBoxRenderFn = keyboard.candidateBox.renderPage; + + jest.spyOn(keyboard.candidateBox, "renderPage").mockImplementation((params) => { + candidateBoxOnItemSelected = params.onItemSelected; + params.onItemSelected = onSelect; + candidateBoxRenderFn(params); + }); + + keyboard.getButtonElement("a").click(); + keyboard.candidateBox.candidateBoxElement.querySelector("li").click(); + + expect(keyboard.options.beforeInputUpdate.mock.calls[0][0]).toMatchObject(keyboard); + keyboard.destroy(); +}); + it('CandidateBox normalization will work', () => { const keyboard = new Keyboard({ layout: { From ea35f102e7b6b13ee687ae77f4663be64814cf18 Mon Sep 17 00:00:00 2001 From: Herbert Lin Date: Mon, 13 May 2024 23:33:00 -0700 Subject: [PATCH 4/5] feat: Trigger beforeInputUpdate when handleButtonClicked is called --- src/lib/components/Keyboard.ts | 7 +++++++ src/lib/components/tests/Keyboard.test.js | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/lib/components/Keyboard.ts b/src/lib/components/Keyboard.ts index c86d0f2e..76cddcba 100644 --- a/src/lib/components/Keyboard.ts +++ b/src/lib/components/Keyboard.ts @@ -465,6 +465,13 @@ class SimpleKeyboard { */ if (!this.input[inputName]) this.input[inputName] = ""; + /** + * Perform an action before any input change + */ + if (typeof this.options.beforeInputUpdate === "function") { + this.options.beforeInputUpdate(this); + } + /** * Calculating new input */ diff --git a/src/lib/components/tests/Keyboard.test.js b/src/lib/components/tests/Keyboard.test.js index bd372dff..ece2828b 100644 --- a/src/lib/components/tests/Keyboard.test.js +++ b/src/lib/components/tests/Keyboard.test.js @@ -218,6 +218,19 @@ it('Keyboard onChange will work', () => { expect(output).toBe("q"); }); +it('Keyboard beforeInputChange will work', () => { + const mockBeforeInputUpdate = jest.fn(); + + const keyboard = new Keyboard({ + beforeInputUpdate: mockBeforeInputUpdate, + useMouseEvents: true + }); + + keyboard.getButtonElement("q").onclick(); + + expect(mockBeforeInputUpdate).toHaveBeenCalledWith(keyboard); +}); + it('Keyboard onChangeAll will work', () => { let output; From dcc9d5d3021aaf53ae2fada4b978a2c17e03972d Mon Sep 17 00:00:00 2001 From: Francisco Hodge Date: Mon, 20 May 2024 17:29:22 -0400 Subject: [PATCH 5/5] Add beforeInputUpdate doc --- src/lib/components/Keyboard.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/components/Keyboard.ts b/src/lib/components/Keyboard.ts index 76cddcba..76abcfdd 100644 --- a/src/lib/components/Keyboard.ts +++ b/src/lib/components/Keyboard.ts @@ -118,6 +118,7 @@ class SimpleKeyboard { * @property {function(input: string):string} onChange Executes the callback function on input change. Returns the current input’s string. * @property {function} onRender Executes the callback function every time simple-keyboard is rendered (e.g: when you change layouts). * @property {function} onInit Executes the callback function once simple-keyboard is rendered for the first time (on initialization). + * @property {function(keyboard: Keyboard):void} beforeInputUpdate Perform an action before any input change * @property {function(inputs: object):object} onChangeAll Executes the callback function on input change. Returns the input object with all defined inputs. * @property {boolean} useButtonTag Render buttons as a button element instead of a div element. * @property {boolean} disableCaretPositioning A prop to ensure characters are always be added/removed at the end of the string.