-
Notifications
You must be signed in to change notification settings - Fork 13
feat: add feedback prop to combobox #153
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
base: next
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -61,6 +61,9 @@ export const Combobox = forwardRef<HTMLInputElement, ComboboxProps>( | |
| onFocus, | ||
| onBlur, | ||
| optional, | ||
| feedbackInfo, | ||
| feedbackWarn, | ||
| feedbackError, | ||
| ...rest | ||
| } = props; | ||
|
|
||
|
|
@@ -249,77 +252,100 @@ export const Combobox = forwardRef<HTMLInputElement, ComboboxProps>( | |
| {getAriaText(currentOptions, value)} | ||
| </span> | ||
|
|
||
| <div | ||
| hidden={!isOpen || !currentOptions.length} | ||
| className={classNames( | ||
| listClassName, | ||
| 'absolute left-0 right-0 bg-primary pb-8 rounded-8 bg-white shadow', | ||
| )} | ||
| style={{ | ||
| zIndex: 3, // Force popover above misc. page content (mobile safari issue) | ||
| }} | ||
| > | ||
| <ul | ||
| id={listboxId} | ||
| role="listbox" | ||
| className={classNames('m-0 p-0 select-none list-none', { | ||
| [MATCH_SEGMENTS_CLASS_NAME]: matchTextSegments, | ||
| })} | ||
| {feedbackInfo && ( | ||
| <div className="absolute pb-8 left-0 right-0 bg-primary rounded-8 bg-white shadow"> | ||
| <div id="static-text" className="block p-8 text-gray-500"> | ||
| {feedbackInfo} | ||
| </div> | ||
| </div> | ||
| )} | ||
| {feedbackWarn && ( | ||
| <div className="absolute pb-8 left-0 right-0 bg-primary rounded-8 bg-white shadow"> | ||
| <div id="static-text" className="block p-8 font-bold"> | ||
| {feedbackWarn} | ||
| </div> | ||
| </div> | ||
| )} | ||
| {feedbackError && ( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this sufficient for screen readers, or does it need some aria attributes to highlight the error?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Question for @martin-storsletten
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might also depend on whether this is actually an error. I might be off base with that. Perhaps its just emphasised feedback text. |
||
| <div className="absolute pb-8 left-0 right-0 bg-primary rounded-8 bg-white shadow"> | ||
| <div id="static-text" className="block p-8 font-bold"> | ||
| {feedbackError} | ||
| </div> | ||
| </div> | ||
| )} | ||
|
Comment on lines
+262
to
+275
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to have two different props if they end up with the same style?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm honestly pretty unsure. They seemed like 3 distinct use cases even though they look the same so I leaned toward keeping them semantically different. We could go for something that leans more into the category of style instead of a "level" approach. Eg. light and heavy or something.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could we go with "subtle" (as seen in "Søker...") and "emphasis" (as seen in "Ingen treff")? |
||
| {!feedbackInfo && !feedbackWarn && !feedbackError && ( | ||
| <div | ||
| hidden={!isOpen || !currentOptions.length} | ||
| className={classNames( | ||
| listClassName, | ||
| 'absolute left-0 right-0 bg-primary pb-8 rounded-8 bg-white shadow', | ||
| )} | ||
| style={{ | ||
| zIndex: 3, // Force popover above misc. page content (mobile safari issue) | ||
| }} | ||
| > | ||
| {currentOptions.map((option) => { | ||
| const display = option.label || option.value; | ||
| let match: ReactNode = []; | ||
|
|
||
| if (matchTextSegments && !highlightValueMatch) { | ||
| const startIdx = display | ||
| .toLowerCase() | ||
| .indexOf(option.currentInputValue.toLowerCase()); | ||
|
|
||
| if (startIdx !== -1) { | ||
| const endIdx = startIdx + option.currentInputValue.length; | ||
| match = ( | ||
| <> | ||
| {display.substring(0, startIdx)} | ||
| <span data-combobox-text-match className="font-bold"> | ||
| {display.substring(startIdx, endIdx)} | ||
| </span> | ||
| {display.substring(endIdx)} | ||
| </> | ||
| ); | ||
| } else { | ||
| match = <span>{display}</span>; | ||
| <ul | ||
| id={listboxId} | ||
| role="listbox" | ||
| className={classNames('m-0 p-0 select-none list-none', { | ||
| [MATCH_SEGMENTS_CLASS_NAME]: matchTextSegments, | ||
| })} | ||
| > | ||
| {currentOptions.map((option) => { | ||
| const display = option.label || option.value; | ||
| let match: ReactNode = []; | ||
|
|
||
| if (matchTextSegments && !highlightValueMatch) { | ||
| const startIdx = display | ||
| .toLowerCase() | ||
| .indexOf(option.currentInputValue.toLowerCase()); | ||
|
|
||
| if (startIdx !== -1) { | ||
| const endIdx = startIdx + option.currentInputValue.length; | ||
| match = ( | ||
| <> | ||
| {display.substring(0, startIdx)} | ||
| <span data-combobox-text-match className="font-bold"> | ||
| {display.substring(startIdx, endIdx)} | ||
| </span> | ||
| {display.substring(endIdx)} | ||
| </> | ||
| ); | ||
| } else { | ||
| match = <span>{display}</span>; | ||
| } | ||
| } else if (highlightValueMatch) { | ||
| match = highlightValueMatch(display, value); | ||
| } | ||
| } else if (highlightValueMatch) { | ||
| match = highlightValueMatch(display, value); | ||
| } | ||
|
|
||
| return ( | ||
| <li | ||
| key={option.id} | ||
| id={option.id} | ||
| role="option" | ||
| aria-selected={navigationOption?.id === option.id} | ||
| tabIndex={-1} | ||
| onClick={() => { | ||
| new Promise((res) => res(setNavigationOption(option))).then( | ||
| () => { | ||
|
|
||
| return ( | ||
| <li | ||
| key={option.id} | ||
| id={option.id} | ||
| role="option" | ||
| aria-selected={navigationOption?.id === option.id} | ||
| tabIndex={-1} | ||
| onClick={() => { | ||
| new Promise((res) => | ||
| res(setNavigationOption(option)), | ||
| ).then(() => { | ||
| handleSelect(option); | ||
| }, | ||
| ); | ||
| }} | ||
| className={classNames({ | ||
| [`block cursor-pointer p-8 hover:bg-${OPTION_HIGHLIGHT_COLOR} ${OPTION_CLASS_NAME}`]: | ||
| true, | ||
| [`bg-${OPTION_HIGHLIGHT_COLOR}`]: | ||
| navigationOption?.id === option.id, | ||
| })} | ||
| > | ||
| {matchTextSegments || highlightValueMatch ? match : display} | ||
| </li> | ||
| ); | ||
| })} | ||
| </ul> | ||
| </div> | ||
| }); | ||
| }} | ||
| className={classNames({ | ||
| [`block cursor-pointer p-8 hover:bg-${OPTION_HIGHLIGHT_COLOR} ${OPTION_CLASS_NAME}`]: | ||
| true, | ||
| [`bg-${OPTION_HIGHLIGHT_COLOR}`]: | ||
| navigationOption?.id === option.id, | ||
| })} | ||
| > | ||
| {matchTextSegments || highlightValueMatch ? match : display} | ||
| </li> | ||
| ); | ||
| })} | ||
| </ul> | ||
| </div> | ||
| )} | ||
| </div> | ||
| ); | ||
| }, | ||
|
|
||
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.
Just wondering what you think about having a
feedbackprop that accepts a text string andfeedbackTypeprop that acceptsFeedbackType, e.g."info" | "warning"(we can default to one that makes most sense). This would give us freedom to handle more feedback styles without adding more props or making breaking changes.We could also try typing feedback as
feedback?: { text: string; type?: "info" | "warning" }to not add more props to an already complexCombobox.What do you think?
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 considered this and am happy to be persuaded that this is the better way to go. The whole thing is a bit awkward generally as you need to juggle more than 1 property when changing the feedback message during an async operation such as a search.
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.
Hm. I guess you'd still need to change 2 properties, i.e. resetting the one that was there before and setting a new one.
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.
Or we could lean into React's ability to take objects as props and do something like this:
??
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 there a reason why we can't go with light & bold? I'm just wondering if users will understand what emphasis and subtle entail without testing them first.
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 discussion is quite related to what @martin-storsletten has to say regards accessibility. If they should include some semantics it won't be sufficient to use style ad an indicator.
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.
My only concern with "light" and "bold" is that its highly coupled to how the styling is meaning if we change the styling, we might then need to change the name of the props. That said, that may not be very likely at all and light and bold may be easy to understand.