-
Notifications
You must be signed in to change notification settings - Fork 61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Made a new KRadioButtonGroup component to fix Firefox radio button movement with arrow keys issue #650
base: develop
Are you sure you want to change the base?
Conversation
…-v1.5.x Release 1.5.x into develop
- This component enables focus movenment on radio buttons with arrow keys on firefox
- Add KRadioButton method description - Add KRadioButtonGroup slot and props description
Hi @MisRob Could you please review the PR? |
Hi @muditchoudhary, thanks a lot! I think as first step, we will QA in Kolibri. Would you be able to open a Kolibri PR for testing that would reference the latest commit of this KDS PR and use |
Sure @MisRob I had tested on Kolibri and I have the changes in stash, so I will have no problem in making a PR. I will send a PR soon. |
- This component enables focus movenment on radio buttons with arrow keys on firefox
- Add KRadioButton method description - Add KRadioButtonGroup slot and props description
…o KRadioButtonGroup-Component
- Used ._componentTag instead of ._name - Changed onkeydown with onkeyup listener
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks so much @muditchoudhary, firstly for researching the issue and approaches to solve it, and then in this PR for finalizing it. Also thanks for taking care of docs and tests. Overall it's great work and feels solid.
I'm leaving a couple of comments. The most important is the one related to many event listeners and it's the only change I request before proceeding to merging. I'd also like to clarify the enable
logic.
I'd appreciate if we could have the improvement regarding the dependency to KGridItem
, however it's also something that I could open a follow-up issue for. Same applies to the remaining comments that are rather minor. It really depends on your bandwith - let me know what works for you.
await wrapper.vm.$nextTick(); | ||
const radioButtons = wrapper.findAllComponents(KRadioButton); | ||
await radioButtons.at(2).trigger('click'); | ||
expect(wrapper.vm.focusedRadioIdx).toBe(2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd be great if we could test that this radio button is focused without dependency on its internal implementation, that is looking at vm.focusedRadioIdx
. Imagine a situation when we'd rename/remove focusedRadioIdx
, but in a way that focus would still work properly for users. In such a situation, this test would give negative result, even though there would be no problem for users. So for maintenance and clarity benefits, whenever possible, we to implement tests in a way that's similar to how the user interact with elements.
I think this technique should do https://stackoverflow.com/a/53042010 (but with attachTo
as attachToDocument
is deprecated)
await radioButtons.at(2).trigger('click'); | ||
expect(wrapper.vm.focusedRadioIdx).toBe(2); | ||
expect(radioButtons.at(2).vm.tabIndex).toBe(0); | ||
expect(radioButtons.at(1).vm.tabIndex).toBe(-1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the same reason as mentioned above, here I'd recommend looking into attributes
of the DOM node rather than into vm
's private data
. I assume it could look something like expect(radioButtons.at(1).attributes('tab-index')).toBe(0)
.
* Specifies whether the radio button group is enabled or disabled. | ||
* Used to make the first radio button active when the radio group becomes enabled again after being disabled. | ||
*/ | ||
enable: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't quite understand yet in what case we'd need to use enable
functionality, would you explain on an example?
mounted() { | ||
this.$nextTick(() => { | ||
if (this.isFirefox) { | ||
if (this.$children[0].$options._componentTag === 'KGridItem') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of checking specifically for KGridItem
, could we recursively search through $children
and their $children
etc. until we find the level of radio buttons?
This would make KRadioButtonGroup
work with any other component that wraps its KRadioButton
s, such as KFixedGridItem
or even HTML elements such as div
.
The alternative would be to properly document that only this single use-case is supported, however it seems to me that this suggestion shouldn't hopefully be complicated and I believe it would also help with clarity and deduplicated some of the code.
gridItem.$children.forEach(fixedGridItem => { | ||
fixedGridItem.$children.forEach(radioBtn => { | ||
this.radioButtons.push(radioBtn); | ||
radioBtn.$el.addEventListener('keyup', this.onKeyUp); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In larger groups of radio buttons, such as the language selection, adding an event listener to each one of them could potentially have negative performance impact, particularly in combination with looping through many elements. A general recommendation we follow in our codebases is to delegate listeners to a container element higher in the structure, so that we have as less listeners as possible.
In this case, KRadioButtonGroup
element when mounted
(and only in Firefox) would attach a single keyup
event listener. This event's handler function would then determine whether event.target
was a radio button, and if yes, it would run the logic needed.
I would also recommend doing the very same thing for the input
event (line 71)
So you'd have something like
mounted() {
this.$el.addEventListener('keyup', onKeyUp);
this.$el.addEventListener('input', onInput);
...
and these two would be the only event listeners in KRadioButtonGroup
.
Related logic would need to be adjusted to work in this updated way.
}, | ||
mounted() { | ||
this.$nextTick(() => { | ||
if (this.isFirefox) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a minor suggestion that could help with code readability. This would avoid one level of nesting:
mounted() {
if (!this.isFirefox) {
return;
}
this.$nextTick(() => {
...
...
...
}, | ||
watch: { | ||
// To focus on the first radio button when radio group enables again | ||
enable(newVal, oldVal) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that before running the code below, we'd also need isFirefox
check here
}, | ||
focusRadio(radioIdx, event) { | ||
if (radioIdx !== undefined && radioIdx !== null) { | ||
this.radioButtons[radioIdx].toggleCheck(event); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we're calling KRadioButton
's (currently private) toggleCheck
method, could you please add @public
JSDoc comment to toggleCheck
, similarly to how you did for setTabIndex
? It will ensure that if refactoring KRadioButton
, we will know that the method is being used from another component and don't remove/rename it accidentally.
@@ -220,6 +222,13 @@ | |||
*/ | |||
this.$emit('blur'); | |||
}, | |||
/** | |||
* @public | |||
* Set the tabIndex value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for making sure its documented. Just a little improvement for clarity:
* Set the tabIndex value | |
* Sets `tab-index` on input |
Thanks a lot, @MisRob for giving the detailed review and suggesting changes. I will send the changes soon. |
Description
KRadioButtonGroup
component that fixes radio button movement with arrow keys issues in Firefox.KRadioButtonGroup
.KRadioButtonGroup
Issue addressed
Fixes: 10491
Addresses #PR# HERE
Before/after screenshots
Before
Before.mp4
After
After.mp4
Changelog
[#PR no]: PR link
Steps to test
Kolibri
with KDS. How? given: hereKRadioButtonGroup
component and wrapKRadioButton
components insideKRadioButtonGroup
component, in these files:2.1 kolibri/plugins/device/assets/src/views/DeviceSettingsPage/index.vue
2.2 kolibri/core/assets/src/views/language-switcher/LanguageSwitcherModal.vue
3.1 http://127.0.0.1:8000/en/device/#/settings
3.2 Choose Language
Second way
(optional) Implementation notes
At a high level, how did you implement this?
Does this introduce any tech-debt items?
Testing checklist
Reviewer guidance
After review
CHANGELOG.md
Comments
For the change language model
KGrid
is used, which creates twoKRadioButtonGroup
components as the array is split.Although the arrow keys are working fine but when we shift tab it puts focus on the last focus radio button on the first list.
I want to know your thoughts. Is that acceptable?