search box component with example
Learning how to develop components via TDD can be daunting – so let’s do a quick TDD implementation, red-green refactor, talk about implementation progress, some problems, and we’ll take them with real-world examples How to solve with . As always, we use React, Jest, react-testing-library, TypeScript and Node.
First, let’s consider a fairly intuitive request for a React component, similar to the Google search bar. We’ll have less number of static selects, so we won’t need to link it to the backend API or do API-side filtering.
In our case, we want a simple input that allows you to start typing text and pops up a dropdown list of relevant options from a list of values returned from the backend API. To make this something, the user should be able to click on one of them to “select” and then emit an event that can be used to affect the rest of the app. We will use caching for this implementation and build it from failed tests to implementation. If we were going to get a user story for this component, it might look like this:
I am a user
I want to see a list of options for country as I type in to the search bar
So that I can select the correct country for megiven I am filling out a form
when I click on the country textbox and type "Un"
then a list of filtered countries should appear with "United Arab Emirates", "United Kingdom", and "United States" as optionsgiven I am filling out the form, have typed in "Un", and am seeing the list of filtered countries
when I click on a country
then it should lock in my selectiongiven I am filling out the form and have locked in a country selection
when I click on the locked in selection
then it should reset the selection and allow me to search again
First things first, we need to create our test file and component file to start working on this code. we will call it
FilteredSearch.tsx and have a friend
FilteredSearch.test.tsx in the same folder. I’ll add here a basic test that fails to get us started (it’s ‘red’ in the red-green refactor).
Alright, we’re off to a good start. we have a
FilteredSearch A test file with components and a failing test, but wait, why would this be a good start when our tests fail? This is a major approach to TDD – the red-green refactor is a way to verify the expectation before we write the code.
So, let’s get to the refactoring part – the next step is relatively simple to add something that has an Area role for “TextBox” to get the test we passed:
Now that we have a passed test, we should probably start thinking about the next incremental change we can make to our component and how to test for it. Since we know that our implementation will filter an array of data and allow the user to type in partial text, we should probably have something that appears after the user types the text along with a list of something and Disappears when there is no text. Let’s update our tests:
OK, so we’ve developed some failsafe tests (mostly because the option prop isn’t implemented yet) that test a filtered list that should appear after the user inputs it. When the option prop is invoked, one of these tests will succeed immediately – the one that confirms that the elements do not exist when nothing is typed.
To save space and promote reuse, I have already moved it
options List definition at the top of the file. Now we need to redo the component for the test to be successful:
OK, so to facilitate our new
options List, we’ve made quite a few changes. I’ll talk about them in the lines that begin with the disastrous props of the new
- we need this
optionsresort to taking a list of strings to be filtered and displayed as the user types in their search
- then we need to store something
- Be able to manage position when user is typing in text
- We may also store some memorabilia; a variable that indicates whether some search text is present so that we can display
optionsList when it’s there, and an array of important divs for option text post-filtering
- After that, we control the input by setting the value on our
onChangeEvent to capture typed text
optionsList div when search text is detected.
great progress! Our search lists options that can be sent as props from an API call (or a static resource) and filtered as the user types in the text. But, we’ve seen that the experience is a terrible UX for lack of a better word. As the user types in the search box, the div we’ve added expands in height and pushes all other content down on the page, and the flashing as options are added/removed is very distracting.
We’ve found something here – it seems like this search component has a lot of responsibility if we also have management of the hovering element. It looks like we need another component to handle these requirements, something that anchors an absolutely positioned component below another component and also removes it from the flow of the DOM so that it sits on top of other elements be visible.
We also know from other convoys about the system that this floating content will be needed elsewhere. Looks like we’re experiencing pain early! Let’s find a solution that saves us some time down the road.
Enter hook and component design!
Let’s get to the responsibility of determining whether an element on the page is bound to a hook that takes some child element and a ref. After that, it will set the position for us. Let’s write a test that checks to see if our hook can retrieve the proper x and y of an element
We mocked a ref for all the important implementation details above and would place the element at the top + height of the ref element, then just the left border. The implementation flows very smoothly from there:
Let’s now add a suite of tests to require here. We’ll borrow one from the current implementation of
<FilteredSearch /> In that we want to render a div and check the ‘document’ role. We will also check that it renders the child sent in it. Finally, it is located correctly (thanks
Specifically, we’re going to mock the hook first so that we don’t worry about the underlying implementation details. We’re going to test for anchor rendering in a position where the ref is present, and the anchor is showing, but also that it doesn’t render
show is false or the target ref is null. We will also check that the children are rendered and that the ‘top’ and ‘left’ style properties are set to the correct values based on the mock
Now, we need to implement the component itself:
With the test passing, we can fix it
<FilteredSearch /> current wrapping component
options list in
<FloatingAnchor /> Component:
And now with the tests passed, we can finally be sure that our new set of components works as expected in our current site! Running the code produces fairly intuitive input that can be typed in to reveal an anchored, floating list of countries that satisfy the typed filter.
Next, we should be able to interact with the filtered options list. Let’s implement this with a simple test for clicking one of the options in the list:
Here we have added an event spy on a new event handler prop called
fireEvent To type the starting text, we find the target option text in the filtered search and click on it. it will fail because we haven’t set the event handler prop
onOptionSelectednor have we wired
onClick event to use it. Let’s implement this in our main component:
Once saved, we’re back to business with the tests to emit the selected option event into the containing response code, what to do with it. Most likely, this data is stored somewhere in the state value and is used to confirm that the “form” has been completed, but we will not make any assumptions there.
So we have only one final functionality to build upon: SELECT ‘LOCK IN’. When I use this control to find and click on an option, it should display my selected value, and if the selected value is clicked again, I should be able to change it. This should be a very simple implementation, so let’s start the red-green refactor with an additional test we’re adding here:
This test now looks for an element with the role “note” and the text of the selected element. If both are present, we are good to go! Now to fix the implementation of the component:
One final act to implement. When we click on the newly added “Lock In” option, we should be able to select something new. Let’s implement it now:
And the final fix:
Now our component meets all requirements! Another can be added to clear the search text when a locked-in selection is clicked or other UX improvements such as CSS styles, to make it less rough and ready. But since we’re only covering the TDD process and use case for a red-green refactor, we’ll stop there.
Thanks for reading. Stay tuned for more.