-
Notifications
You must be signed in to change notification settings - Fork 30
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
feat(components): Autocomplete Custom Menu Rendering #2229
Conversation
Deploying atlantis with
|
Latest commit: |
3f80505
|
Status: | ✅ Deploy successful! |
Preview URL: | https://638c0714.atlantis.pages.dev |
Branch Preview URL: | https://job-111436-rhf-autocomplete.atlantis.pages.dev |
5176974
to
30167e5
Compare
export interface AutocompleteProps< | ||
GenericOption extends AnyOption = AnyOption, | ||
GenericOptionValue extends Option = Option, | ||
GenericGetOptionsValue extends AnyOption = AnyOption, |
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 generic on GetOptionsValue is because some people were passing an initialOptions value that would be a different type to what was returned by getOptions
e.g. Initial options would be a list of GroupOptions but getOptions would give Options based on another value in the form
@@ -28,11 +27,12 @@ | |||
} | |||
|
|||
.options.visible { | |||
display: block; | |||
position: absolute; |
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.
Changed the popper visibility to use visibility to match the recommendations in the docs
https://popper.js.org/docs/v2/modifiers/hide/
It also helped with an issue when clicking on customRendered options that didn't use the onMouseDown
callback
Published Pre-release for aae2a87 with versions:
To install the new version(s) for Web run:
|
); | ||
} | ||
|
||
export const AdvancedTemplateCode = ` |
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 tried various ways to get the Canvas to render the code as well, since the Canvas can't use any state and expects a single component to render I had to break it out into AdvancedCustomTemplate
but the show code button stopped working.
I tried using things like the source option but it would also not work.
When I tried using the Source
block and doing AdvancedCustomTemplate.toString()
it was giving minified code. I am fine removing this is we wish
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 went through basically the exact same process with my stuff, wrestled with it for a while, and wrote this ticket to come back and improve it later. So I think the way you did it is totally fine. I would maybe just suggest using a Disclosure
or <details>
directly to allow people to show/hide the long blocks of code as they need. Right now it is a bit of a long scroll if you aren't interested in one of the examples
@MichaelParadis something worth checking in-product - in Storybook, I can have multiple custom Autocompletes open at once: |
Move menu keyboard navigation to useKeyboardNavigation
Use utility functions in Menu.tsx
…in preparation for customMenu rendering
Support generic usages of BaseMenuOption for custom menu rendering
Move Menu to its own folder
Extract the MenuPopper into a separate component for reuse
const requestedIndex = options[highlightedIndex + direction]; | ||
|
||
return requestedIndex && isGroup(requestedIndex) | ||
? direction + direction |
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 it possible that direction + direction is also a group?
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.
No, the Autocomplete removes GroupOptions that have no other options here. So if requestedIndex is a group it will move to one of the following:
- The next option which will be inside of the requestedIndex's group
- The previous option, which will be inside the Group of options "above" in the menu.
There will be a scenario where two group options (with no nested options) next to each other
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.
Okay, thank you for clarifying that for me
attachTo: MenuProps["attachTo"], | ||
visible = false, | ||
) { | ||
const [menuRef, setMenuRef] = useState<HTMLElement | null>(); |
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 this should default to null based on the typing
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.
Updated in 59f6974
popper?.update?.(); | ||
}, [visible]); | ||
|
||
const targetWidth = attachTo.current?.clientWidth; |
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.
Should this also be wrapped in useSafeLayoutEffect
in case attachTo
is resized after this has already been called?
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.
Popper adjusts a lot of the styling automatically. This targetWidth
is helpful for initial positioning but once visible has changed react-popper
starts controlling the rendering
Update types to better support Options inside of GroupOptions.
… which allows onChange to be the OptionValueType or undefined while onOptionSelect didn't allow it to be undefined
…6/rhf-autocomplete
…will live in Autocomplete notes
@ZakaryH I updated storybook and the docs to include information on rendering Autocompletes with custom sections. I am just updating my pre-release branch right now |
The main reason for this is we could need to call default keyboard behaviour in the parent above and provide the "default" highlighted index as a prop to the
|
…ion` and `useCustomKeyboardNavigation`
@ZakaryH I too was confused by this. I pushed an update that also fixes this by adjusting the opacity too 3f80505 The position absolute wasn't needed as well so I cleaned that up. Ideally I use |
@MichaelParadis nice, yeah that looks much better! I haven't looked at the code too closely so I'll let @Aiden-Brine give the final approval, but for the things I was playing around with and all the comments I had made this LGTM |
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 for making those changes and discussing everything! LGTM
Motivations
As a part of our composability and customizabilty work we wanted to add support for rendering a custom menu in the Autocomplete. To achieve this I added a
customMenuRender
prop that will be used to replace the content when the Autocomplete menu is opened.To help achieve this I did the following:
useKeyboardNavigation
for handling the existing keyboard navigation behaviour. This works when the customization of the menu only renders MenuOptions and doesn't include custom elementsuseCustomKeyboardNavigation
this is used when the Autocomplete menu has elements that aren't just options. The reason for this is that the Autocomplete can not be certain of how people want keyboard navigation to work. This works to prevent issues with custom selectable items such as a footer to create another option.MenuOption
which is the default behaviour of menu options in AutocompleteMenuOptionContent
the default rendering of the content of aMenuOption
BaseMenuOption
the base wrapper of theMenuOption
this component handles the highlighted and separator logicUNSAFE_
props for overriding the styles of these atomsDecisions
I decided to give full access to the Autocomplete Menu including when it is rendered when using the
customMenuRender
. The reason for this is that the existingAutocomplete
wouldn't display a menu if no options were available. However, when custom rendering a Menu the consumer may want to show a "Create new X` option instead.I made the options type support generics, the reason for this is that if generics weren't provided then the
customMenuRender
wouldn't be aware of the properties of the options it is rendering which would require consumers to use typescript casting to be able to get the properties of an object for rendering.I removed the XOR of
Option
andGroup
option forOptionCollection
the reason for this is that I found it was causing headaches when introducing support for generic option types. I also found thatOptionCollection
wasn't actually enforcing thatinitialOptions
andgetOptions
was only an array ofOption
s orGroupOption
sChanges
Added
Testing
Autocomplete
stories work as the did previously (verify keyboard navigation and using a mouse, etc.)Custom Rendering
story can be used with both a keyboard and a mouseChanges can be
tested via Pre-release
In Atlantis we use Github's built in pull request reviews.