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(dropdown): SingleSelect with search filter #903
Conversation
Codecov Report
@@ Coverage Diff @@
## master #903 +/- ##
==========================================
+ Coverage 96.16% 96.19% +0.02%
==========================================
Files 139 139
Lines 2373 2391 +18
Branches 454 457 +3
==========================================
+ Hits 2282 2300 +18
Misses 85 85
Partials 6 6
Continue to review full report at Codecov.
|
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'm excited that you're back porting this functionality from webapp to wonder-blocks. At a high-level I think we need to decide if we want this functionality in all of our dropdown types. If we do, it shouldn't be too much work to push the logic down into dropdown-core.js.
Requesting the following changes:
- expand the testing to verify the interaction of selection and fitlering
- fix what appears to be a missing return in
getMenuItems
- enable screenshotting on a story with the dropdown open on page load (if possible)
There are some other nits in the comments which I leave to your discretion.
// Assert | ||
expect(wrapper.find(IconButton).exists()).toBe(true); |
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.
Asserting on the props of SearchTextInput
would be more robust since what SearchTextInput
renders may change. Also, it's possible that the OptionItem
s might change in the future to render an IconButton
.
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.
Now that you mention it, I think it makes more sense to move this test inside search-text-input.test.js
. The SingleSelect
shouldn't worry about these specific details, and we are checking if the search input is present in the previous test.
// Arrange | ||
// Arrange |
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.
nit
// Assert | ||
expect(wrapper).toHaveState({searchText: ""}); | ||
}); | ||
}); |
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.
It would be good to have some tests that cover the interaction between filtering and selection. If the selected item doesn't match the search filter doesn't it still appear? If so, where does it appear? If not, how doesn't navigation from the selected item work?
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.
@jandrade friendly ping.
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.
Oh I forgot this comment. Thanks for the reminder.
}, | ||
}); | ||
|
||
export const customStyles = () => <SingleSelectWithCustomStyles />; |
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 name of this story should have something about filter/search in the title.
Seeing, this I just realized that if we allow hooks in wonder-block at least, we wouldn't need a class component here.
Aside: it would be nice if we could reuse certain stories in the docs instead of having to duplicate the same code in two places.
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.
Good call, I'll change that. About hooks, I thought about that, but I'd prefer to keep using classes as examples here in case folks using Webapp can refer to these snippets in a way they can directly use in Webapp.
I'll gladly replace these examples once we fully enable hooks in Webapp.
Re: aside: I'll be reusing those stories in my next PR (Storybook Docs)... wait for it :)
import * as React from "react"; | ||
import {shallow} from "enzyme"; | ||
|
||
import SearchTextInput from "../search-text-input.js"; |
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.
naming suggestion: SearchField
. You using this terminology elsewhere in this PR, but beyond that I think it generalizes better, e.g.
TextField
PasswordField
SearchField
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 agree, but I'd prefer to keep it that way to avoid modifying the functionality we already have in place for MultiSelect
. I wanted to touch as less as I could here to avoid doing any unnecessary refactors on the original implementation. As you mentioned I'll add a new ticket/TODO to work on this in the future.
const wrapper = shallow( | ||
<SearchTextInput | ||
searchText="query" | ||
testId="search-text-input" |
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.
nit: is the testId
necessary?
describe("SearchTextInput", () => { | ||
it("text input container should be focused when focusing on the input", () => { | ||
// Arrange | ||
const wrapper = shallow( |
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.
Nice use of shallow
. 🎉
@@ -669,7 +669,7 @@ describe("wonder-blocks-dropdown", () => { | |||
darkBackgroundWrapper: { | |||
flexDirection: "row", | |||
justifyContent: "flex-end", | |||
backgroundColor: Color.darkBlue, | |||
background: Color.darkBlue, |
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.
Why the switch to background
?
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.
For some reason, the tests were throwing a warning about using background
and backgroundColor
, but I don't remember what was, and now I don't see it, so I 'll revert back to bgColor
.
/** | ||
* The text input to filter the items by their label. Defaults to an empty | ||
* string. | ||
*/ | ||
searchText: string, |
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 this something that we'll want to have on MultiSelect
and ActionMenu
as well? If so, it might make sense to push some of the logic you added to this component into dropdown-core.js.
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 see now that searchText
is already part of DropdownCore
.
* When this is true, the dropdown body shows a search text input at the | ||
* top. The items will be filtered by the input. | ||
*/ | ||
isFilterable?: boolean, |
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 wonder if we should allow the filter algorithm to be overridden?
There are some really handy filtering rules besides "contains case insensitive match" such as the filtering rules that VSCode has for finding files, like matching starts of words. For example, ABC would match "Always Be Closing".
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.
@lillialexis wrote a pretty awesome smart matching algorithm for our github CLI tools would be nice to use.
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 did write a filter function that does exactly that @somewhatabstract! https://github.com/Khan/our-lovely-cli/blob/develop/cli/pull-request/smart-suggest.js
@@ -13,7 +13,7 @@ jobs: | |||
- name: Use Node.js 12.x | |||
uses: actions/setup-node@v1 | |||
with: | |||
node-version: 12.x | |||
node-version: 12.16.x |
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 changed this b/c storybook/chromatic was failing to build: storybookjs/storybook#10477 (comment)
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.
Hopefully they'll fix the underlying issue in the future.
@@ -35,5 +35,5 @@ jobs: | |||
- uses: chromaui/action@v1 | |||
with: | |||
token: ${{ secrets.GITHUB_TOKEN }} | |||
appCode: ${{ secrets.CHROMATIC_APP_CODE }} | |||
projectToken: ${{ secrets.CHROMATIC_APP_CODE }} |
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 it to remove a Chromatic warning.
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.
LGTM. There are maybe a few additional tests that could be added, see my friendly ping, but nothing blocking. Thanks for adding filtering to SingleSelect. ❤️
Summary
Similar to the MultiSelect component (#479), these changes introduce the
isFilterable
prop to<SingleSelect />
.This allows us to filter options inside the single select component.
Test plan