-
Notifications
You must be signed in to change notification settings - Fork 867
Insert checkbox column in Facility > Users KTable to allow user selection #13479
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
Insert checkbox column in Facility > Users KTable to allow user selection #13479
Conversation
Build Artifacts
|
nucleogenesis
left a comment
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 Allan, I see the selection working in manual testing. I made some suggestions for clean-up and noted some necessary changes.
| /> | ||
| <KButton | ||
| appearance="basic-link" | ||
| text="filter" |
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 missed this in the strings file, can you add it to the file please?
|
|
||
| <KButton | ||
| appearance="basic-link" | ||
| text="clear " |
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.
CommonCoreStrings.clearAction will work here.
| return { | ||
| selectedUser: null, | ||
| modalShown: null, | ||
| selectedUsers: new Set(), |
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.
Let's move everything from the data() method into the setup() method and use refs for them - ie, const selectedUser = ref(null)
| tableHeaders() { | ||
| return [ | ||
| { | ||
| label: '', |
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 think we still want there to be a label on this which should be passed to the KCheckbox and w/ the :showLabel="false" we ensure there is an accessible label still present, even if it's not visible.
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.
The label for this should be "Select all" which I think at this point can be added to the common core strings file.
Once we merge this, we can make a follow-up issue to update the 4 other instances that define their own "Select all" messages (ie, quiz management, user table)
| if (selectedVisibleUsers.length === 0) { | ||
| return { checked: false, indeterminate: false }; | ||
| } else if (selectedVisibleUsers.length === visibleUserIds.length) { | ||
| return { checked: true, indeterminate: false }; | ||
| } else { | ||
| return { checked: false, indeterminate: true }; | ||
| } |
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 like the feel of this that you can check selectAllState.(checked|indeterminate) to get the current value in the component above.
The if/else/if/else though makes it a little hard to mentally map what conditions result in which values.
I think the core part of the logic can be written like:
checked: trueif the number of visible users equal to number of selected users, otherwisechecked: falseindeterminate: trueifchecked == falseand the number of selected users is greater than 0; otherwiseindeterminate: false
This is a bit easier to read than:
- Are no users selected? Then
checked: false, indeterminate: false - Are the same # of users selected than are visible? Then
checked: true, indeterminate: false - Otherwise,
checked:false, indeterminate: true
Could you try reworking this function to read a bit more like the first narrative? I think returning the object still would be great, but it might be nice to clean up the if/else chain. Happy to chat about this on Slack or a call if that'd be helpful.
| // Otherwise, only non-SUs can be edited. | ||
| return this.isSuperuser || !user.is_superuser; | ||
| }, | ||
| // manageUserOptions(userId) { |
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.
Are these no longer needed?
|
@radinamatic - Jacob has left some code changes for Allan, so I think we will wait to do the full manual QA feedback until the code review is ✅, but I'm adding you as a reviewer for a11y purposes. I'm going to do an initial pass as I do my code review/manual review now, just to catch anything that I can spot, and then I will ask for you to jump in and do the more thorough one :) |
nucleogenesis
left a comment
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 Allan! I've added a few comments and questions - the aria-label stuff is blocking but the other questions may not be.
One thing though that I was thinking about - particularly if you're running into reactivity issues where you do something like this.selectedUsers = new Set(this.selectedUsers) - I wonder if maybe these things you're adding to the methods: {} section could be written instead in the setup() method as functions.
Then you can just refer directly to selectedUsers.value for the getting/setting stuff.
I don't think we've explicitly decided to move away from the Vue Options API - but I do think that keeping it consistent within a single file is a good idea wherever we can.
| const isChecked = | ||
| selectedVisibleUsers.length === visibleUserIds.length && selectedVisibleUsers.length > 0; | ||
| const isIndeterminate = | ||
| selectedVisibleUsers.length > 0 && selectedVisibleUsers.length < visibleUserIds.length; |
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.
This could be simplified even further using !isChecked as the second half of your boolean expression here.
| <KIconButton icon="assignCoaches" /> | ||
| <KIconButton icon="add" /> | ||
| <KIconButton icon="remove" /> | ||
| <KIconButton icon="download" /> | ||
| <KIconButton icon="trash" /> |
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.
These should have an :ariaLabel and :tooltip attr added that use the strings here -- also you can remove the "download" icon as we won't be implementing that here.
| dataType: 'string', | ||
| minWidth: '300px', | ||
| width: '40%', | ||
| width: '35%', |
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.
Why did these need to be changed?
| this.selectedUsers.add(user.id); | ||
| } | ||
| this.selectedUsers = new Set(this.selectedUsers); |
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.
Is this necessary to do? Did you run into an issue with reactivity or something that led you to directly assign a new Set to this.selectedUsers?
| this.selectedUsers = new Set(this.selectedUsers); | ||
| }, | ||
| clearSelectedUsers() { | ||
| this.selectedUsers = new Set(); |
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.
There's a clear() method available to sets so should be able to do this.selectedUsers.clear() here.
| :ariaLabel="selectAllLabel$()" | ||
| @change="handleSelectAllToggle" | ||
| > | ||
| <span class="visuallyhidden">{{ selectAllLabel$() }}</span> |
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.
@radinamatic - it seems to me we don't need both the aria level and the visually hidden span here. Is there one or the other that we should prefer?
|
|
||
| <template #cell="{ content, colIndex, row }"> | ||
| <span v-if="colIndex === 0"> | ||
| <KCheckbox |
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.
We do need either a visually-hidden label or an aria-label (please follow whichever @radinamatic indicates is the preferred option based on the question above) for these checkboxes
Each row checkbox should announce its label by screen readers: e.g. “Select John Doe, coach.”
LianaHarris360
left a comment
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 just left a few clean-up comments, adding the checkbox column is a super important piece of the 0.19 feature work, thank you for working on this Allan!
| label: '', | ||
| dataType: 'undefined', | ||
| minWidth: '150px', | ||
| minWidth: '180px', |
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.
This is causing the last column to be wider than necessary. Because this minWidth property is optional, we can either remove it completely, or set it to a smaller value because the OPTIONS button that goes in this column takes up less space than it previously did.
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.
marcellamaki
left a comment
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.
Hi @AllanOXDi - really great work so far. Thanks for already addressing so much feedback. I left one small comment regarding using translated strings in aria labels, and I'm going to hand it over to @radinamatic for more robust accessibility testing.
One other (very small) issue I noticed that is mostly a pre-existing problem that has just been exacerbated here is that the tooltip for the identifier is covered by the table cell

It exists in Kolibri-demo as well, but because of the spacing there, the text is legible.

It's a small piece of cleanup, but I think worth doing now
@radinamatic - over to you for a11y testing! (Please note that the icon buttons at the top of the table are not intended to be interactive yet).
| <KCheckbox | ||
| :checked="isUserSelected(row[6])" | ||
| :showLabel="false" | ||
| :aria-label="row[1] + ',' + row[6].kind" |
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.
|
@AllanOXDi Good work so far, still a few rough edges to smoothen 🙂 The main thing, as you can see in the screenshot, is that the checkbox does not seem to have a label, and NVDA announces:
Seems that the strategy to add the
Another issue is that there is a superfluous (first) This seems to happen because of the |
Hi @marcellamaki , the issue seems to have come from https://github.com/BabyElias/kolibri-design-system/blob/0613a2163071512a86da80591101eb97ba50aa94/lib/KTable/index.vue#L341 |
d2aec1e to
0a83573
Compare
|
@AllanOXDi, apologies for the late review 🙏🏽 Checkbox for all the individual users in rows below the header is now properly displaying the wording However, the column header cell is still read out as The second line in the above screen reader output is correct, because it is for the actual checkbox inside the first cell, but the header cell itself (and consequently all the cells below in that column) must have as the label just
Technically, we should not even be putting the
|
|
Okay! Thanks @radinamatic let me fix that |
1037cf4 to
d33057b
Compare
1d38405 to
19baf44
Compare
19baf44 to
3d7af12
Compare
|
🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉 🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉 |
radinamatic
left a comment
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 @AllanOXDi , ready to roll! 💯 🎉 ![]()









Summary
This Add checkbox column User table to enable individual and multiple user selection
Screen.Recording.2025-06-11.at.23.51.58.mov
References
#13426
Reviewer guidance
Check if