Before getting started you’ll definitely want to read about the origination of this package… you can do that here.
All right, now that you’re back, this is the issue we’re going to be tackling today. In the first iteration of the package, only string values were supported as filter conditions. Booleans and numbers require a little extra work to normalize and I didn’t want them to slow down getting the initial version of the package published.
As an extra bonus, I’ll be writing this guide as I work through adding this functionality! You’ll be able to keep track of my thought process the entire time and we’ll be able to pair the work in progress to specific commits in the associated pull request.
Overall this should be a rather simple addition, but it’s a good exercise to work through all of your requirements before you start. In this case:
- By their very nature, boolean values aren’t display-friendly strings. Instead of simply allowing the value of the item into our
availableConditionsas a raw boolean we need to format it for display.
- Once we’ve encountered a boolean property there’s no need to revisit that for every source item… the only available options for that property across all items will either be
- We’re currently sorting all of the available conditions for a property alphabetically, but one generally expects
Trueto come before
Falsein a list. We’ll want to ensure the default sorting isn’t applied to any field that contains a boolean value.
- When an active boolean filter condition is encountered, it should properly filter the item list.
What we already know
In looking through the package there are several obvious places requiring modification in order to add boolean support to filter conditions:
- Parsing the source items to assemble available filter conditions
- Adding and removing filter conditions
- Matching boolean item properties to active boolean properties when generating the item list
Parsing available filter conditions
While one could technically add an active filter condition without it being parsed into
availableConditions first, the main use-case will likely be having these boolean properties in the filtering UI.
At the moment we’re explicitly looking for non-empty string values. We’ll still want to check string values this way, but we’ll now want a separate path for parsing boolean values in order to meet our requirements.
Sorting is also applied to every property by default, so we’ll need to amend this path as well.
Let’s start with some tests!
The first test is the most important as we’ll establish that having multiple items (which will be the most common state of the list) should limit the
availableConditions to just
False without repeating, and in that order. It looks like this:
The second test covers the edge case in which only one item exists in the source list. Essentially we want the same
availableConditions any time a boolean property is encountered. The test is nearly identical, it simply reduces the supplied items to 1:
The failing commit can be found here.
Let’s start by adjusting our expectation that we only want to operate on non-empty string values. We can start by adding a conditional check for boolean properties and manually adding the values we want to display:
We’re still looping through every item, then every supplied
filterProperty on that item… we can save ourselves a little work by only acting on boolean properties once. Funny enough, the above implementation also helps us with our requirement to not sort the provided values for boolean properties.
An alternative approach would have been to check
if (value is bool && !booleanProperties.contains(property)), then check again in the loop that is responsible for sorting the filter conditions. I ultimately decided that the one-time hit of removing any boolean property keys from our base set of filter conditions would put us in better shape when dealing with large lists.
Adding and removing active conditions
Toggling filter conditions on and off is a core tenant of managing lists in this package. My assumption here is that since we’re treating boolean values as strings once they enter the conditions pipeline, we won’t have to actually modify anything. Let’s see if that holds true!
We already have a test in place for adding and removing filter conditions, so we can just modify it to also validate that boolean properties and values work as expected.
Here’s the commit for that test.
Turns out that no modification is required!
Matching items to filter
The last piece of the puzzle is actually filtering against boolean values. This will be a little trickier since we’re storing the conditions as formatted strings, but the items will still have them as booleans. Shouldn’t be too bad, though.
We could simply modify the existing tests, but since we’re dealing with a brand new value type I’m going to add one new test and adjust one of the existing tests.
For our new test, we want to ensure that the
True value we’ve generated for our conditions (and that would display in our filtering UI) can actually affect the list.
In addition to the new test above, I modified the test directly below as well. I chose that test for two reasons:
- It was already testing how multiple active conditions stack to refine the resulting list
- By replacing one of the active string conditions with a boolean condition (that would effectively result in the same list) we’re now also testing for items that have the exact same value on a given property… which is something we didn’t actually have covered before
That commit (with tests failing) is right here.
We know that items will still have their values stored as booleans, which is good, as that will be our trigger to enter into our boolean filtering path. We also know that we’ve stored the filter condition as a string and capitalized the first character so it’s more display-friendly.
What all of that means is that we’ll have to adjust both sides of the comparison to get an accurate match. The
itemValue will need to be converted to a string and the
targetValue will need to be lowercased.
Note: the initial temptation would be to just lowercase the
targetValue and always compare that to the stringified version of
itemValue. That’s actually not a bad idea but comes with some caveats and a few additional questions (especially since we’ve already set up the precedent of case-sensitive filtering).
For now, the above will work just nicely and can be found here.
Let’s take a look at those requirements one more time to make sure we’ve covered everything:
- Display-friendly labels — check
- Avoid redundant property looping — check
- Separate sorting for boolean values — check
- Actually filtering the list — check
Just a few documentation updates, and it looks like we’re in good shape!
Hopefully following along with this process in real-time has been a good look at how to take an issue, break it down into requirements, assess what code will need to be modified (and why), and then actually write the tests with the implementation. Even with what turned out to be a relatively simple addition we learned a few new things about the package and identified an area for improvement.
Take care until next time!