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

✨ Implement <Listbox /> component #76

Merged
merged 20 commits into from Sep 3, 2021
Merged

✨ Implement <Listbox /> component #76

merged 20 commits into from Sep 3, 2021

Conversation

dmcnamara-eng
Copy link
Collaborator

@dmcnamara-eng dmcnamara-eng commented Aug 31, 2021

 ✨ What Changed and Why

This PR is a functionally complete implementation of a <Listbox /> component based on https://headlessui.dev/react/listbox

Things that this PR implements:

  • Show/hide options on mouse click
  • Support for disabled component and individual options
  • Support for a optional label element
  • Full mouse support for focus and active elements
  • Full keyboard support (Enter/Space/ArrowUp/ArrowDown/ArrowLeft/ArrowRight/PageUp/PageDown/Home/End/Escape)
  • Support for searching the list using the keyboard
  • Click outside the listbox to close
  • Full accessibility implementation (aria-expanded/aria-labelledby/aria-controls/aria-haspopup/aria-active-descendent) and focus management
  • Sample implementation in dummy app

Things that this PR does not implement are:

  • static prop
  • Composition with transitions

I ported the React component tests for listbox as Ember integration tests. 90 integration tests were ported. Some tests were not ported because they are not applicable to Ember components. A test for transitions was not ported because transitions has not been implemented here yet.

😈 Before

Didn't exist.

😇 After

after

Closes #69

@dmcnamara-eng dmcnamara-eng changed the title ✨ Introduce <Listbox /> component ✨ Implement <Listbox /> component Aug 31, 2021
Copy link
Collaborator

@alexlafroscia alexlafroscia left a comment

Choose a reason for hiding this comment

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

I'm really excited to see this PR!

I took a first pass through and left some thoughts; there is a lot to review here so it might take me a few passes.

I'm generally going to assume the test implementation is fine and that the tests passing are enough of an indication that they're good-to-go, and will instead focus on the actual add-on source code for review purposes (at least, for now!)

addon/components/listbox.hbs Outdated Show resolved Hide resolved
addon/components/listbox.hbs Outdated Show resolved Hide resolved
addon/components/listbox.hbs Outdated Show resolved Hide resolved
addon/components/listbox.hbs Outdated Show resolved Hide resolved
addon/components/listbox.js Outdated Show resolved Hide resolved
addon/components/listbox/-button.hbs Outdated Show resolved Hide resolved
addon/components/listbox/-option.js Outdated Show resolved Hide resolved
addon/components/listbox/-options.hbs Outdated Show resolved Hide resolved
addon/components/listbox/-options.hbs Outdated Show resolved Hide resolved
tests/integration/components/listbox-test.js Outdated Show resolved Hide resolved
Use `isOpen` as the yielded property name

Use a `setter` for this property to ensure state is correct when property is changed externally.

Pass `openListbox` and `closeListbox` actions to components.
Use `focus-trap` to manage focus when opening/closing the listbox

Use `click-outside` to manage document click events
Now storing the index of each listbox option within the list.

This means we avoid an expensive `find` operation when setting the selected option on mouse click or key event.
@dmcnamara-eng
Copy link
Collaborator Author

I'm really excited to see this PR!

I took a first pass through and left some thoughts; there is a lot to review here so it might take me a few passes.

I'm generally going to assume the test implementation is fine and that the tests passing are enough of an indication that they're good-to-go, and will instead focus on the actual add-on source code for review purposes (at least, for now!)

Sounds good! Thanks for the quick feedback! I've replied to individual comments above.

addon/components/listbox.js Outdated Show resolved Hide resolved
addon/components/listbox.js Outdated Show resolved Hide resolved
addon/components/listbox/-button.hbs Outdated Show resolved Hide resolved
addon/components/listbox.hbs Outdated Show resolved Hide resolved
addon/components/listbox/-option.js Outdated Show resolved Hide resolved
addon/components/listbox.js Outdated Show resolved Hide resolved
@alexlafroscia alexlafroscia added the enhancement New feature or request label Sep 1, 2021
@alexlafroscia
Copy link
Collaborator

I approved the GitHub Actions run so we'll get lint/test results for the PR now. GitHub disables those for first-time contributors (people were abusing the system to mine cryptocurrency 🤦‍♂️) do you need a contributor to give the OK for CI to run on an individual's first PR!

@dmcnamara-eng
Copy link
Collaborator Author

Rather than using the click-outside modifier on the button, I think we should be able to use the onDeactivate option to close the menu and avoid needing to perform any focus-management-on-click-outside behavior ourselves.

660f5cf removes the click-outside modifier to use a pure focus-trap implementation.

I had to skip one test for this to pass. The value of that test seems dubious at this point.

@dmcnamara-eng
Copy link
Collaborator Author

I approved the GitHub Actions run so we'll get lint/test results for the PR now. GitHub disables those for first-time contributors (people were abusing the system to mine cryptocurrency 🤦‍♂️) do you need a contributor to give the OK for CI to run on an individual's first PR!

Those crazy cryptokids 😆

I've fixed the lint errors and all tests should be passing now, so please kick-off another CI and see how we are doing 👍

@dmcnamara-eng dmcnamara-eng marked this pull request as ready for review September 2, 2021 10:48
Copy link
Collaborator

@alexlafroscia alexlafroscia left a comment

Choose a reason for hiding this comment

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

This is looking really great! Once we get clarity on the nested conditional I commented on, I am feeling good about merging this PR!

return this._isOpen;
}

set isOpen(isOpen) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I really like using a "setter" like this; keeps the code for manipulating the value clean, but allows us to perform these side-effects easily as well!

addon/components/listbox/-option.hbs Outdated Show resolved Hide resolved
@@ -37,6 +61,12 @@ const DialogState = {
InvisibleUnmounted: 'InvisibleUnmounted',
};

const ListboxState = {
Visible: 'Visible',
InvisibleHidden: 'InvisibleHidden',
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't see use of this state in tests, I assume that we are just lack of some tests right ? Some assertions don't implement it. Do we have a plan to extend the test suite ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe would be nice to follow the same approach what @achambers did in dialog ? What I mean is copy test e.g. from react repo and change to todo example
I am willing to help with them after that and fill gaps.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah yes this refers to the alternative "hidden" or "unmount" render strategy, in which React will insert the listbox options into the DOM, but display as hidden.

I think it's safe to push this feature out for now.

b452c35 adds todos for these tests.

Copy link
Collaborator

@alexlafroscia alexlafroscia left a comment

Choose a reason for hiding this comment

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

This is looking for to me! I'm happy to merge it at this point if you're ready @dmcnamara-eng !

@dmcnamara-eng
Copy link
Collaborator Author

This is looking for to me! I'm happy to merge it at this point if you're ready @dmcnamara-eng !

🥳 I'm happy. Please merge!

@alexlafroscia alexlafroscia merged commit 653a407 into GavinJoyce:master Sep 3, 2021
@alexlafroscia
Copy link
Collaborator

0.9.0 released with this PR in it! Awesome work here @dmcnamara-eng 🎉 💯

@dmcnamara-eng
Copy link
Collaborator Author

0.9.0 released with this PR in it! Awesome work here @dmcnamara-eng 🎉 💯

Cheers. Happy days. Let's get this library to1.0.0!

@GavinJoyce
Copy link
Owner

❤️

@miguelcobain
Copy link

@dmcnamara-eng how difficult would it be to implement the multiple feature on headlessui? https://headlessui.com/react/listbox#selecting-multiple-values

@knownasilya
Copy link

This renders in place instead of absolutely positioned in the body?

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

Successfully merging this pull request may close these issues.

Implement <Listbox> component
6 participants