-
Notifications
You must be signed in to change notification settings - Fork 23
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
fix(kselect, kmultiselect): filtered arrow navigation [KHCP-13956] #2573
base: main
Are you sure you want to change the base?
fix(kselect, kmultiselect): filtered arrow navigation [KHCP-13956] #2573
Conversation
✅ Deploy Preview for kongponents-sandbox ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
✅ Deploy Preview for kongponents ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
@@ -72,7 +72,7 @@ const getGroupItems = (group: string) => props.items?.filter(item => item.group | |||
const setFocus = (index: number = 0) => { | |||
if (kMultiselectItem.value) { | |||
if (!props.items[index].disabled) { | |||
kMultiselectItem.value[index]?.$el?.querySelector('button').focus() | |||
kMultiselectItem.value[index]?.$el?.querySelector('button')?.focus() |
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 we should move away from using the refs array. A straightforward alternative would be to use querySelectorAll
to select the .select-item button
elements, as this approach always reflects the correct order. This change also eliminates the need to add a key
to the K(Multi)SelectItems
component, which can cause unnecessary re-rendering on every item change.
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've refactored the implementation moving away from using the refs array to utilize querySelectorAll
like you suggested. Please review and let me know what you think
Preview package from this PR in consuming applicationIn consuming application project install preview version of kongponents generated by this PR:
|
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 left some comments on KMultiselect
, which I believe also apply to KSelect
.
<div | ||
ref="kMultiselectItemsContainer" | ||
aria-live="polite" | ||
class="multiselect-items-container" |
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.
How about moving this element into <KMultiselectItems>
?
const onKeyDown = (event: Event) => { | ||
const { target, key } = event as KeyboardEvent |
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.
const onKeyDown = (event: Event) => { | |
const { target, key } = event as KeyboardEvent | |
const onKeyDown = ({ target, key } : KeyboardEvent) => { |
const index = props.items.findIndex(item => item.key === key) | ||
if (key === 'ArrowDown' || key === 'ArrowUp') { | ||
// find the items container element | ||
const kSelectItemsContainer = (target as HTMLElement).closest('.multiselect-items-container') |
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 it might be better to move the wrapper into this component so that we don't query for elements outside this component.
} | ||
} | ||
|
||
const setArrowNavigationFocus = (): void => { |
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 should still provide a focus
method on <KMultiselectItems>
so that we don't need to query into another component.
} | ||
if (selectableItemsElements?.length) { | ||
// find the current element index in the array | ||
const currentElementIndex = Array.from(selectableItemsElements).findIndex(el => el === target) |
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.
const currentElementIndex = Array.from(selectableItemsElements).findIndex(el => el === target) | |
const currentElementIndex = [...selectableItemsElements].indexOf(target) |
// find the items container element | ||
const kSelectItemsContainer = (target as HTMLElement).closest('.multiselect-items-container') | ||
// all selectable items | ||
const selectableItemsElements = kSelectItemsContainer?.querySelectorAll('.multiselect-item button:not([disabled])') |
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.
const selectableItemsElements = kSelectItemsContainer?.querySelectorAll('.multiselect-item button:not([disabled])') | |
const selectableItemsElements = kSelectItemsContainer?.querySelectorAll<HTMLButtonElement>('.multiselect-item button:not(:disabled)') |
const currentElementIndex = Array.from(selectableItemsElements).findIndex(el => el === target) | ||
// move to the next or previous element | ||
const nextElementIndex = key === 'ArrowDown' ? currentElementIndex + 1 : currentElementIndex - 1 | ||
const nextElement = selectableItemsElements[nextElementIndex] as HTMLButtonElement |
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.
const nextElement = selectableItemsElements[nextElementIndex] as HTMLButtonElement | |
const nextElement = selectableItemsElements[nextElementIndex] |
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.
nit: Can we use less verbose variable names?
if (nextElement) { | ||
nextElement.focus() | ||
} |
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.
if (nextElement) { | |
nextElement.focus() | |
} | |
nextElement?.focus() |
Summary
Addresses: https://konghq.atlassian.net/browse/KHCP-13956
Fixes broken arrow navigation in KSelect and KMultiselect
Verification steps: