[EuiSuggest][EuiSelectable] Allow controlled search value prop#5658
[EuiSuggest][EuiSelectable] Allow controlled search value prop#5658cee-chen merged 23 commits intoelastic:mainfrom
value prop#5658Conversation
f04927a to
b2b0e90
Compare
| onFocusBadge: false, | ||
| paddingSize: 'none', | ||
| isVirtualized, | ||
| ...rest, |
There was a problem hiding this comment.
Note: EuiSuggest previously passed ...rest to its suggest input
eui/src/components/suggest/suggest.tsx
Lines 54 to 61 in c85913b
so this PR moves ...rest from listProps to searchProps to maintain previous parity
There was a problem hiding this comment.
👍 Makes much more sense. Can we also then add a listProps prop that can be passed down?
There was a problem hiding this comment.
We definitely could! My only hesitation would be increasing the scope of this PR. As far as I can tell the EuiSuggest component didn't have a way to significantly customize the dropdown list before the EuiSelectable refactor. I'm good with adding it though if we're all OK with it and don't have any other larger API concerns/refactors
There was a problem hiding this comment.
@thompsongl just wanted to check in with you on this before I added it in, are you good with adding listProps as a new prop to EuiSuggest? (or does it feel weird that there's a listProps but not a searchProps? Am I overthinking this? 🧠)
There was a problem hiding this comment.
or does it feel weird that there's a listProps but not a searchProps? Am I overthinking this?
I think this the right question. I'd prefer opening an issue to propose adding both, rather than introducing the change in this PR. As we've learned, smaller changesets are better for these components 🙈
There was a problem hiding this comment.
I'm good with that - I think that enhancement can wait until later, whereas a few folks now have been directly affected by the value bug, so I'd rather get this PR in sooner rather than later.
| className={classes} | ||
| placeholder={placeholder} | ||
| value={value} | ||
| onChange={onChange} |
There was a problem hiding this comment.
This switch from onSearch to onChange is necessary because for whatever reason, EuiFieldSearch's underlying onSearch does not trigger correctly when value is passed, but onChange does.
I figured this change is mostly safe and not breaking because EuiSelectable (and its consuming applications) primarily rely on incremental search and are already overriding the Enter key presses to toggle list items. Additionally, EuiFieldSearch's docs themselves say to prefer onChange over onSearch if not using the enter key.
|
Preview documentation changes for this PR: https://eui.elastic.co/pr_5658/ |
baf0eeb to
d5ec22c
Compare
|
Preview documentation changes for this PR: https://eui.elastic.co/pr_5658/ |
- to make it easier to test missing props
…st example - to make it easier to test behavior - could also be left in to serve as a controlled example 🤷
Some of these were confusing to me when they showed up in the playground, some of these straight up don't work or break EuiSelectable behavior when passed, and some should be removed for API consistency
- to match previous behavior as well as handle EuiFieldSearch props
…hild ID - we should be passing the direct ID prop to the parent wrapper, not to the `ul` listbox, which previously had a `_listbox` suffix
- While still supporting `defaultValue` or when consumers do not pass `value` - This happens by retaining our passed `onSearchChange`->`onChange` state callback while adding `getDerivedStateFromProps` logic for the `value` prop + clean up EuiSelectableSearch to not also repetitively maintain its own internal searchValue state - since we're storing it at the top EuiSelectable level, we can just maintain it in 1 place and pass it down to children + remove `defaultValue` from being passed - React will throw an error if you attempt to pass both `value` and `defaultValue` + switch EuiFieldSearch prop `onSearch` to `onChange` - unfortunately onSearch doesn't work with uncontrolled values, so we must use `onChange` instead and ignore enter key presses
+ convert EuiSelectableSearch from class to function component, because why not
d5ec22c to
e883d8c
Compare
|
Preview documentation changes for this PR: https://eui.elastic.co/pr_5658/ |
| onFocusBadge: false, | ||
| paddingSize: 'none', | ||
| isVirtualized, | ||
| ...rest, |
There was a problem hiding this comment.
👍 Makes much more sense. Can we also then add a listProps prop that can be passed down?
- that can be reused by other components repeating `searchProps` - while keeping internal `_` type for props passed only by EuiSelectable parent + update changelog to note removed `incremental` prop, but look into fixing `inputRef` prop
- search value isn't changing when options are clicked, so there's no need to call `searchProps.onChange`
…Props naming - except for onSearch which becomes transmogged to onChange - not sure if this is too confusing
…lectable functionality + this fixes the EuiSelectableTemplateSitewide demo to not error on Cmd+K keypress, + fixes Cmd+K keypress on Firefox going to the URL bar
|
Preview documentation changes for this PR: https://eui.elastic.co/pr_5658/ |
|
Preview documentation changes for this PR: https://eui.elastic.co/pr_5658/ |
|
Preview documentation changes for this PR: https://eui.elastic.co/pr_5658/ |
| return ( | ||
| <> | ||
| <EuiSelectable<EuiSuggestionProps> | ||
| id={id} |
There was a problem hiding this comment.
It's not relevant for a11y, but a consumer-provided id is no longer used throughout the component. That is, the listbox gets one id and the input gets another.
Previously, this id would be the basis for rootId and used in all child ids. rootId now always gets the fallback case.
So a consumer can't use the id for any testing/DOM operations (maybe that's not terrible?).
There was a problem hiding this comment.
I made this change in EuiSuggest because it matched the component's behavior prior to the refactor (id and all other props were passed directly to the search input). For EuiSelectable itself I fixed this in 809ec87 - the wrapping div now receives the consumer id instead of the listbox receiving that id (I'm not quite sure why that change was made).
There was a problem hiding this comment.
Yeah that makes sense. What I'm getting at is: why not use the consumer-provided id as the basis for all generated ids via rootId. As is, rootId no longer does much because props.id will always be undefined:
eui/src/components/selectable/selectable.tsx
Lines 188 to 190 in 50d3f02
There was a problem hiding this comment.
Ah gotcha. I can do that, I was kinda in "restore all the props to the search input" mode and wasn't sure if that constituted a breaking change. I guess we already changed it as of the refactor though so it makes sense to keep the id on the root EuiSelectable 👍
There was a problem hiding this comment.
Actually sorry after looking at this again I don't think it's a 1:1 change especially from how EuiSuggest was previously. In the underlying EuiSelectable we don't actually automatically generate an id for the search input, just for the dropdown list and the message contents.
Until we add the option for listProps and searchProps (so users can manually pass in searchProps: { id: 'whatever' }) I'm really tempted to say we should leave the ID on the search input to maintain parity with how the component previously was.
There was a problem hiding this comment.
I want to add that it also feels really weird that an <EuiSuggest id="foo" data-test-subj="bar" /> would have the id go on the parent wrapper but the data-test-subj go on the search input. For now/for consistency I think we should keep properties on the search input 🤷♀️
There was a problem hiding this comment.
Until we add the option for listProps and searchProps
I'm fine waiting for this. Like I said, the id isn't relevant to a11y, just to potential consumer ease of knowing that id="foo" will result in <input id="foo" /> and other elements having predictable ids: foo_listbox, foo_instructions, foo_messageContent, etc.
|
Preview documentation changes for this PR: https://eui.elastic.co/pr_5658/ |
thompsongl
left a comment
There was a problem hiding this comment.
LGTM
Don't forget about your [REVERT] commits
|
Preview documentation changes for this PR: https://eui.elastic.co/pr_5658/ |
|
Latest merge was only a changelog conflict fix, so I'm going to go ahead and force-merge this without waiting for CI to finish, since the next release is waiting on this PR |
|
Preview documentation changes for this PR: https://eui.elastic.co/pr_5658/ |
Summary
closes #5644
closes #5651
closes #4341 - value, disabled, and placeholder can now all be passed without Typescript issues
EuiSelectableandEuiSuggestto accept controlledvaluepropsEuiSuggestnot correctly passing props to the search input (53c328e)EuiSelectableincorrectly rendering the passedidprop on the listbox instead of the parent wrapper (809ec87)EuiSelectableto no longer callsearchProps.onChangewhen list items are clicked (b969621)EuiSelectablenot respectingsearchProps.inputRef(514b8f9)EuiSelectable'ssearchProps.onSearchprop (since Enter keypresses do not trigger a search callback) - usesearchProps.onChangeinsteadEuiSuggest'sonInputChangeandonSearchChangecallbacks toonInput/onSearchrespectively, for consistency with our existing callback naming conventionsEuiSuggest'sisLoadingprop - usestatus.loadinginsteadincrementalprop fromEuiSuggestandEuiSelectable'ssearchPropsEuiSelectableSearchwas converted from a class component to a functional component, and no longer maintains its own internalsearchValuestate (but inherits it from the parentEuiSelectable, which already maintains said state)If possible, I recommend following along by commit.
QA
fieldvalue, but still works as normal/expected when the user types or backspaces into the search inputvalueprop) and type into the search input. Confirm that still works as normal/expectedSearchableexample loads in with atvalue, but still works as normal/expected when the user types or backspaces into the search inputvalueprop) and type into the search input. Confirm that still works as normal/expectedChecklist
- [ ] Check against all themes for compatibility in both light and dark modes- [ ] Checked in mobile- [ ] Checked in Chrome, Safari, Edge, and Firefox- [ ] Checked for accessibility including keyboard-only and screenreader modes- [ ] Added documentation- [ ] Checked Code Sandbox works for any docs examples