Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
| placeHolderRef.addEventListener("keydown", downHandler); | ||
| placeHolderRef.addEventListener("keyup", upHandler); | ||
| return () => { | ||
| placeHolderRef?.removeEventListener("keydown", downHandler); | ||
| placeHolderRef?.removeEventListener("keyup", upHandler); | ||
| }; | ||
| } else { | ||
| window.addEventListener("keydown", downHandler); | ||
| window.addEventListener("keyup", upHandler); | ||
| // Remove event listeners on cleanup | ||
| return () => { | ||
| window.removeEventListener("keydown", downHandler); | ||
| window.removeEventListener("keyup", upHandler); | ||
| }; |
There was a problem hiding this comment.
Handle keypress on the red or when in a window
Usefull if you only wanna do stuff when something is focused
| const isSelected = | ||
| (Array.isArray(selectedItems) && selectedItems?.some((selection) => selection.value === item.value)) || | ||
| (!Array.isArray(selectedItems) && selectedItems?.value === item.value); | ||
|
|
There was a problem hiding this comment.
Check if its selected based if we're using multiple or not
| <div | ||
| className={cn( | ||
| "flex h-4 w-4 items-center justify-center rounded-[4px] border ltr:mr-2 rtl:ml-2", | ||
| isSelected ? "bg-gray-800 text-gray-50" : "text-primary-600 border-gray-300 bg-gray-50" | ||
| )}> | ||
| {isSelected && <FiCheck className="h-3 w-3 text-current" />} | ||
| </div> | ||
| </li> |
There was a problem hiding this comment.
Fake checkbox as the input item would highjack focus and also have weird on event handlers attached
| searchBoxRef: React.RefObject<HTMLInputElement>; | ||
| } | ||
|
|
||
| type FlatternedOption = Option & { current: number; groupedIndex?: number }; |
There was a problem hiding this comment.
We flattern optional to include the count and also the index of the group it is attached to (If any)
| const flatternOptions = (options: Option[], groupCount?: number): FlatternedOption[] => { | ||
| return options.reduce((acc, option, current) => { | ||
| if (option.options) { | ||
| return [...acc, ...flatternOptions(option.options, current + (groupCount || 0))]; |
There was a problem hiding this comment.
Recursivly call this is we have options -> current + group count keeps track of where we are in the inital list
Needed to reference label and handle keyboard navigation correctly
| const upPress = useKeyPress("ArrowUp", searchBoxRef); | ||
| const enterPress = useKeyPress("Enter", searchBoxRef); | ||
|
|
||
| const flatternedList = useMemo(() => flatternOptions(list), [list]); |
There was a problem hiding this comment.
flattern the list on the inital list passed in
| const totalOptionsLength = useMemo(() => { | ||
| return flatternedList.length; | ||
| }, [flatternedList]); |
There was a problem hiding this comment.
Calculate the length of this - needed to get the min and max bounds of the array for keyboard navigation
| useEffect(() => { | ||
| if (downPress) { | ||
| // Cycle to start of list if at end | ||
| setKeyboardFocus((prev) => (prev + 1) % totalOptionsLength); |
There was a problem hiding this comment.
add one to previous take the % of it
e.g list size of 10
10+1 % 10 = 1
There was a problem hiding this comment.
Great use of the modular operator!
| useEffect(() => { | ||
| if (upPress) { | ||
| // Cycle to end of list if at start | ||
| setKeyboardFocus((prev) => (prev - 1 + totalOptionsLength) % totalOptionsLength); |
There was a problem hiding this comment.
Same as above but in reverse
| {filteredList?.map((item, index) => { | ||
| const focused = index === keyboardFocus; | ||
| return ( | ||
| <React.Fragment key={index}> | ||
| <div className="px-2.5"> | ||
| {item.current === 0 && item.groupedIndex !== undefined && ( | ||
| <Label>{list[item.groupedIndex].label}</Label> | ||
| )} | ||
| <Item item={item} index={index} focused={focused} /> | ||
| </div> | ||
| </React.Fragment> | ||
| ); |
There was a problem hiding this comment.
Render all items - if its the first item in the list render a label before it
| classNames?: { | ||
| menuButton?: ({ isDisabled }: { isDisabled: boolean }) => string; | ||
| menu?: string; | ||
| tagItem?: ({ isDisabled }: { isDisabled: boolean }) => string; | ||
| tagItemText?: string; | ||
| tagItemIconContainer?: string; | ||
| tagItemIcon?: string; | ||
| list?: string; | ||
| listGroupLabel?: string; | ||
| listItem?: ({ isSelected }: { isSelected: boolean }) => string; | ||
| listDisabledItem?: string; | ||
| ChevronIcon?: ({ open }: { open: boolean }) => string; | ||
| searchContainer?: string; | ||
| searchBox?: string; | ||
| searchIcon?: string; | ||
| closeIcon?: string; | ||
| }; |
There was a problem hiding this comment.
WIP styles override if needed
There was a problem hiding this comment.
Nice — I think this is a super simple approach of overriding the styles. Very easy to use, clear on how it works 👏
| const onPressEnterOrSpace = useCallback( | ||
| (e: React.KeyboardEvent<HTMLDivElement>) => { | ||
| e.preventDefault(); | ||
| if ((e.code === "Enter" || e.code === "Space") && !isDisabled) { | ||
| toggle(); | ||
| } | ||
| }, | ||
| [isDisabled, toggle] | ||
| ); |
There was a problem hiding this comment.
Handle open on space or enter if its not disabled
nevermind. routing forms have selects and darkmdoe |
Will pick this up tomorrow should be a quick win to implement :) |
| @PeerRich @JeroenReumkens Darkmode been commited :) |
|
Great work @sean-brydon! Just tested again and noticed two other small issues;
|
|
lemme put this on auto merge, @sean-brydon can you ping @JeroenReumkens once you addressed the changes and @JeroenReumkens can you approve after? |
|
@JeroenReumkens I have fixed the disabled state I can't replicate
At all - you got a demo of that happening? |
JeroenReumkens
left a comment
There was a problem hiding this comment.
Great work @sean-brydon , approved!

What does this PR do?
Code probably needs a bit of cleanup but thought i'd put it out here to see if anyone has any suggestions/improvements first
Full custom select component that matches 95% of WCAG a11y guidelines (I will work on improving in due course - react-select doesnt seem to even be 100%)
This is not used within the app anywhere as of right now. Just been implemented in isolation in storybook.
TODO (maybe in future PR)
Fixes #6593
Loom Video: https://www.loom.com/share/436b048792514d4b8e6e8907e90bd5b5
Environment: Staging(main branch) / Production
Type of change