fix(web): allow to set default keyboard to 'off' - #16524
Conversation
User Test ResultsTest specification and instructions User tests are not required Test Artifacts |
5d4a63b to
466b822
Compare
466b822 to
01921a2
Compare
01921a2 to
a21df46
Compare
96c527c to
9de509a
Compare
| } | ||
|
|
||
| test.describe.skip('First example from the guide', function () { | ||
| test.describe('First example from the guide', function () { |
There was a problem hiding this comment.
The guide-examples e2e tests are re-enabled; if it turns out that they're still not stable then we can disable them again.
9de509a to
f82c68d
Compare
|
|
||
| languageMenu.lgList.style.display='none'; //still allows blank menu momentarily on selection | ||
| languageMenu.keyman.contextManager.activateKeyboard(entry.kn, entry.kc,true); | ||
| languageMenu.keyman.contextManager.restoreLastActiveTextStore(); |
There was a problem hiding this comment.
This is already done in activateKeyboard, so there's no need to do it twice (especially since activateKeyboard is async, so this call could possibly work with outdated data...)
The parameters for `setKeyboardForControl` basically have three different modes: - an id of a keyboard/language to set that keyboard, enabling independent keyboard mode - empty string to set the system keyboard, enabling independent keyboard mode - `null` to disable independent keyboard mode. Our previous code didn't properly handle the last two modes. This change fixes the problems and also clarifies and updates the documentation. Also included is an improvement to the guide-examples e2e tests that now wait until all keyboards are loaded (which may fix #16167). Partially drafted by kilo.ai. Fixes: #16080 Build-bot: skip release:web
f82c68d to
4d7867e
Compare
| if (!elem.ownerDocument.defaultView) { | ||
| return; | ||
| } |
There was a problem hiding this comment.
What role does this new conditional play?
There was a problem hiding this comment.
Just to play safe. defaultView could theoretically be null.
There was a problem hiding this comment.
It seems unlikely? But if we want to really play safe, we should:
| if (!elem.ownerDocument.defaultView) { | |
| return; | |
| } | |
| if (!elem?.ownerDocument?.defaultView) { | |
| return; | |
| } |
That will cover cases where elem is not set.
In some ways, this may be misleading -- perhaps it would be better for an error to be raised, so the consumer knows that this failed. Either by returning false or by throw:
| if (!elem.ownerDocument.defaultView) { | |
| return; | |
| } | |
| if (!elem?.ownerDocument?.defaultView) { | |
| return false; | |
| } |
| if (!elem.ownerDocument.defaultView) { | |
| return; | |
| } | |
| if (!elem?.ownerDocument?.defaultView) { | |
| throw new Error('setKeyboardForControl: elem null or has no default View'); | |
| } |
However, my primary thought here is that there are so many things we could consider for all the API endpoints -- this is a piecemeal validation step which will probably become inconsistent over time with other API endpoints, so really we should be thinking about parameter validation across the whole API -- types, object shape, return values / exceptions / console.warn / console.error, and implement that consistently. That would be a better outcome than micro patches. We see a similar issue with lines 299-302 below, where we have a console warning raised which is not really visible to the API consumer at runtime -- so becomes an invisible log message in 99% of cases.
There was a problem hiding this comment.
ok, I'll undo this check.
Done.
| * @param langId | ||
| */ | ||
| public setKeyboardForTextStore(textStore: AbstractElementTextStore<any>, kbdId: string, langId: string): void { | ||
| public setKeyboardForTextStore(textStore: AbstractElementTextStore<any>, kbdId: string | null, langId: string | null): void { |
There was a problem hiding this comment.
Why not use kbdId?: string, langId?: string instead? That way, there's no need for the nullish fallbacksin line 317 of keymanEngine.ts.
There was a problem hiding this comment.
Since null is one of the possible values I'd like to keep it in the type to be explicit. But I can make the arguments optional so that the signature is similar to KeymanEngine.setKeyboardForControl.
Done.
| } | ||
|
|
||
| this.contextManager.setKeyboardForTextStore(elem._kmwAttachment.textStore, keyboard, languageCode); | ||
| this.contextManager.setKeyboardForTextStore(elem._kmwAttachment.textStore, keyboard ?? null, languageCode ?? null); |
There was a problem hiding this comment.
I would prefer changes that leave these two parameters completely pass-through. Why change this line when an equally-simple change avoids the need for it?
| constructInstance: (): null => null | ||
| }; | ||
|
|
||
| describe('KeymanEngine.getKeyboardForControl', () => { |
There was a problem hiding this comment.
No explicit .setKeyboardForControl tests here?
While I guess they're kinda tied, you should also be able to verify three things:
- A keystroke (either physical or via OSK) results in the correct output character
- Swapping to a second, still-global control swaps the current "active keyboard" reported by the engine to the global keyboard setting.
- Swapping back to the original control restores its setting and what the "current keyboard" reported by the engine is.
I thought we had some old automated tests that might have already been testing points 2 and 3, but I don't see them upon a search.
They did exist back in stable-16.0, but apparently they got erased at some point by accident during work toward stable-17.0. Here's a permalink to the relevant automated tests from before:
There was a problem hiding this comment.
Added e2e tests
Also add links to guide examples to test page.
| if (!elem.ownerDocument.defaultView) { | ||
| return; | ||
| } |
There was a problem hiding this comment.
It seems unlikely? But if we want to really play safe, we should:
| if (!elem.ownerDocument.defaultView) { | |
| return; | |
| } | |
| if (!elem?.ownerDocument?.defaultView) { | |
| return; | |
| } |
That will cover cases where elem is not set.
In some ways, this may be misleading -- perhaps it would be better for an error to be raised, so the consumer knows that this failed. Either by returning false or by throw:
| if (!elem.ownerDocument.defaultView) { | |
| return; | |
| } | |
| if (!elem?.ownerDocument?.defaultView) { | |
| return false; | |
| } |
| if (!elem.ownerDocument.defaultView) { | |
| return; | |
| } | |
| if (!elem?.ownerDocument?.defaultView) { | |
| throw new Error('setKeyboardForControl: elem null or has no default View'); | |
| } |
However, my primary thought here is that there are so many things we could consider for all the API endpoints -- this is a piecemeal validation step which will probably become inconsistent over time with other API endpoints, so really we should be thinking about parameter validation across the whole API -- types, object shape, return values / exceptions / console.warn / console.error, and implement that consistently. That would be a better outcome than micro patches. We see a similar issue with lines 299-302 below, where we have a console warning raised which is not really visible to the API consumer at runtime -- so becomes an invisible log message in 99% of cases.
| if(!elem || !this.contextManager.isElementInIndependentMode(elem)) { | ||
| return null; | ||
| } | ||
| const keyboard = elem._kmwAttachment.keyboard; |
There was a problem hiding this comment.
What if _kmwAttachment is not defined?
| const keyboard = elem._kmwAttachment.keyboard; | |
| const keyboard = elem._kmwAttachment?.keyboard ?? ''; |
There was a problem hiding this comment.
Also suggest that we use keyboardId for consistency. Assuming that is what it is?
There was a problem hiding this comment.
If _kmwAttachment is not defined then isElementInIndependentMode will return false, so at this point here we know that it is defined.
| if(!elem || !this.contextManager.isElementInIndependentMode(elem)) { | ||
| return null; | ||
| } | ||
| return elem._kmwAttachment.languageCode; |
There was a problem hiding this comment.
| return elem._kmwAttachment.languageCode; | |
| return elem._kmwAttachment?.languageCode ?? ''; |
Query: should this be returning '' for system-specified language (matching the shape of getKeyboardForControl) or null (which is simpler to reason on)?
There was a problem hiding this comment.
To make it symmetrical to the values we pass to setKeyboardForControl it should return ''.
Done.
| * @return {string|null} The independently-managed keyboard for the control, | ||
| * or null if it is following the global keyboard setting. |
There was a problem hiding this comment.
This may be '' to mean 'independently-managed but set to system keyboard', I think according to the intention of the spec. We should make sure that is clear
Co-authored-by: Marc Durdin <marc@durdin.net>
|
Changes in this pull request will be available for download in Keyman version 19.0.287-alpha |
The parameters for
setKeyboardForControlbasically have three different modes: - an id of a keyboard/language to set that keyboard, enabling independent keyboard mode - empty string to set the system keyboard, enabling independent keyboard mode -nullto disable independent keyboard mode.Our previous code didn't properly handle the last two modes. This change fixes the problems and also clarifies and updates the documentation.
Also included is an improvement to the guide-examples e2e tests that now wait until all keyboards are loaded (which may fix #16167).
Partially drafted by kilo.ai.
User tests will follow in an upcoming PR for #16522.
Fixes: #16080
Build-bot: skip release:web
Test-bot: skip