Skip to content

feat: drop down keyboard navigation#1985

Merged
acywatson merged 12 commits into
facebook:mainfrom
ivan-ngchakming:feat/dropdown-keyboard-nav
May 24, 2022
Merged

feat: drop down keyboard navigation#1985
acywatson merged 12 commits into
facebook:mainfrom
ivan-ngchakming:feat/dropdown-keyboard-nav

Conversation

@ivan-ngchakming

@ivan-ngchakming ivan-ngchakming commented Apr 26, 2022

Copy link
Copy Markdown
Contributor

Added keyboard navigation feature to drop down UI component

  • Set focus to first drop down item on open
  • Allow navigation using Tab
  • Allow navigation using arrow keys (ArrowUp, ArrowDown)
  • Allow closing drop down using Escape and return focus to the drop down button

closes #1923

Screens

tosDemo

@vercel

vercel Bot commented Apr 26, 2022

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated
lexical ✅ Ready (Inspect) Visit Preview May 20, 2022 at 5:15AM (UTC)
lexical-playground ✅ Ready (Inspect) Visit Preview May 20, 2022 at 5:15AM (UTC)

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Apr 26, 2022
@ivan-ngchakming ivan-ngchakming changed the title feat: dropdown keyboard nagivation feat: drop down keyboard navigation Apr 26, 2022
@ivan-ngchakming ivan-ngchakming marked this pull request as ready for review April 26, 2022 07:03
}
};

const contextValue = {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should be memoized to prevent React doing extra context updates.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I am not sure how to do this. Wrapping contextValue in useMemo with all its entries as dependencies doesnt make sense? Updates won't be reduced this way?

Anyway, I have moved the highlightedItem out of the contextValue object, and call the focus method from the parent instead of the within the item component, this should reduce the extra renders triggered by change in the highlighted item.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@ivan-ngchakming - useMemo will preserve the same object reference which means that Context.Provider will see as the same exact value and won't trigger further re-renders when that is the case.

Without useMemo, every time this components rerenders you're essentially generating a brand new object/value.

@always-maap

Copy link
Copy Markdown
Contributor

I think when the drop-down is open, pressing Tab should close and focus on the next element but currently, it cycles between items

@acywatson

acywatson commented May 14, 2022

Copy link
Copy Markdown
Contributor

I think when the drop-down is open, pressing Tab should close and focus on the next element but currently, it cycles between items

Is that correct? I'm not sure. @a11yHolli can your clarify the correct behavior for us here? Would love to get this one updated and merged. @ivan-ngchakming

@ivan-ngchakming

Copy link
Copy Markdown
Contributor Author

I think when the drop-down is open, pressing Tab should close and focus on the next element but currently, it cycles between items

I believe this would be consistent with the native select.

I am currently working on my finals, will work on this in a few days as soon as I am free.

@acywatson acywatson left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This looks good to me now - thanks!

}

// $FlowFixMe: I am not sure why this is wrong
highlightedItem?.current?.focus();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We typically avoid optional chaining - I don't think we have the transpilation in place necessary to support various targets we support.

Does it work if you do something likee:

if (highlightedItem !== null && highlightedItem.current instanceof HTMLElement) {
  highlightedItem.current.focus();
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

What if you did:

const highlightedItemElem: null | HTMLElement = highlightedItem.current
if (highlightedItemElem !== null) {
  highlightedItemElem.focus();
} 

@ivan-ngchakming ivan-ngchakming May 20, 2022

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I tried out a few ways

    // works
    if (highlightedItem && highlightedItem.current instanceof HTMLElement) {
      highlightedItem.current.focus();
    }

    // also works, since highlightedItem.current is either `HTMLElement` or `null`
    if (highlightedItem && highlightedItem.current) {
      highlightedItem.current.focus();
    }

    // type error: highlightedItem is possibly 'undefined' in `highlightedItem.current instanceof HTMLElement`
    if (highlightedItem !== null && highlightedItem.current instanceof HTMLElement) {
      highlightedItem.current.focus();
    }

    // type error: highlightedItem is possibly 'undefined'
    const highlightedItemElem: null | HTMLElement = highlightedItem.current
    if (highlightedItemElem !== null) {
      highlightedItemElem.focus();
    }

If its all the same, I will go with the second option as its more concise, but still satisfying all the type checks.

    if (highlightedItem && highlightedItem.current) {
      highlightedItem.current.focus();
    }

@always-maap always-maap left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

what exactly is the active class doing?
without it the button is not full width

Ordibehesht-30-1401 00-24-22

@ivan-ngchakming

Copy link
Copy Markdown
Contributor Author

what exactly is the active class doing? without it the button is not full width

It was behaving like this before this PR if I remember correctly. I am guessing the style that should be applied on it is missing, maybe there should be some kind of icon or indicator to show the style applied on the selected text? But the non-active buttons not being full width issue would still needs to be fixed.

image

@acywatson acywatson merged commit 584b846 into facebook:main May 24, 2022
@ivan-ngchakming ivan-ngchakming deleted the feat/dropdown-keyboard-nav branch May 25, 2022 08:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Toolbar Keyboard Accessibility

7 participants