Skip to content
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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

portikM
Copy link
Member

@portikM portikM commented Jan 15, 2025

Summary

Addresses: https://konghq.atlassian.net/browse/KHCP-13956

Fixes broken arrow navigation in KSelect and KMultiselect

Verification steps:

  1. Find a sortable instance of KSelect and/or KMultiselect in sandbox or docs
  2. Type a query in and after the component shows the results, remove it
  3. Try navigating through items using up and down arrows - it should work correctly

@portikM portikM self-assigned this Jan 15, 2025
@portikM portikM requested review from adamdehaven, jillztom, Justineo and a team as code owners January 15, 2025 17:54
Copy link

netlify bot commented Jan 15, 2025

Deploy Preview for kongponents-sandbox ready!

Name Link
🔨 Latest commit 79745bf
🔍 Latest deploy log https://app.netlify.com/sites/kongponents-sandbox/deploys/67898e3e39e03000083fdc66
😎 Deploy Preview https://deploy-preview-2573--kongponents-sandbox.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link

netlify bot commented Jan 15, 2025

Deploy Preview for kongponents ready!

Name Link
🔨 Latest commit 79745bf
🔍 Latest deploy log https://app.netlify.com/sites/kongponents/deploys/67898e3e59b6240008c3342a
😎 Deploy Preview https://deploy-preview-2573--kongponents.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

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()
Copy link
Contributor

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.

Copy link
Member Author

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

@kongponents-bot
Copy link
Collaborator

Preview package from this PR in consuming application

In consuming application project install preview version of kongponents generated by this PR:

@kong/kongponents@pr-2573

Copy link
Contributor

@Justineo Justineo left a 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"
Copy link
Contributor

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>?

Comment on lines +66 to +67
const onKeyDown = (event: Event) => {
const { target, key } = event as KeyboardEvent
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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')
Copy link
Contributor

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 => {
Copy link
Contributor

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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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])')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const nextElement = selectableItemsElements[nextElementIndex] as HTMLButtonElement
const nextElement = selectableItemsElements[nextElementIndex]

Copy link
Contributor

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?

Comment on lines +82 to +84
if (nextElement) {
nextElement.focus()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (nextElement) {
nextElement.focus()
}
nextElement?.focus()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants