The last piece of functionality we miss is the ability to filter bills by category.
We want to click a category name in the NavBar component, and the table and chart should only display that category’s bills.
And we’ll add an ‘All’ button that is the default and will let us go back to show all the bills instead of filtering by one category.
Let’s do it!
I’m going to first restructure the App
component a little bit, by:
- adding an
activeCategory
property to the state - creating an
activeBills
computed property that will only return the bills associated with the currently selected categories (or all if none is selected) - passing
activeBills
to the BillsTable and Chart components, instead ofbills
so they will show the filtered bills.
The first change is easy, in the App component:
const [activeCategory, setActiveCategory] = useState()
Let’s create the activeBills
function. We filter the bills by activeCategory
, and we sort them by date in reverse chronological order (newest first):
const activeBills = () => {
return bills
.filter(bill =>
activeCategory ? bill.category === activeCategory : true
)
.sort((a, b) => (new Date(a.date) < new Date(b.date) ? 1 : -1))
}
Now we pass that to the child components, instead of bills
:
<div className="container flex">
<div className="w-1/2">
<BillsTable
bills={activeBills()}
showAddBill={showAddBill}
removeBill={removeBill}
/>
</div>
<div className="w-1/2">
<Chart bills={activeBills()} />
</div>
</div>
Now we’re ready to go in the NavBar component and do our edits there. But first let’s add 2 new props to NavBar: activeCategory
and setNewActiveCategory
<NavBar
categories={categories}
showAddCategory={showAddCategory}
activeCategory={activeCategory}
setNewActiveCategory={setNewActiveCategory}
/>
setNewActiveCategory
points to this function which simply calls setActiveCategory
passing the category index:
const setNewActiveCategory = index => {
setActiveCategory(index)
}
Let’s get to the NavBar component. Here’s the NavBar component at the moment, before any change:
import React from 'react'
export default props => {
const triggerShowAddCategory = () => {
props.showAddCategory()
}
const liStyle =
'p-4 inline bg-grey-lighter hover:bg-grey-light uppercase font-black cursor-pointer'
return (
<ul className="list-reset inline flex justify-center border-b-4 mb-0">
{props.categories
? props.categories.map((value, index) => {
return (
<li className={liStyle} key={index}>
{value}
</li>
)
})
: '<li>No categories</li>'}
<li className={liStyle} onClick={triggerShowAddCategory}>
➕
</li>
</ul>
)
}
Let’s add a setNewActiveCategory()
function we’ll use to call the setNewActiveCategory()
prop passed by the App component:
const setNewActiveCategory = index => {
props.setNewActiveCategory(index)
}
We’ll use that inside the categories map() to apply the class bg-grey-dark
if a category is active. This class is part of Tailwind and will apply a darker background. The liStyle
variable is just a string, so we concatenate a new class using +
.
Also, when we click a category we call the setActiveCategory
method, passing the category name:
{props.categories.map((value, index) => {
return (
<li
className={
liStyle +
(props.activeCategory === index
? ' bg-grey-dark'
: ' bg-grey-lighter')
}
key={index}
onClick={() => setNewActiveCategory(index)}
>
{value}
</li>
)
})}
Before iterating on categories
, let’s add an “All” button. Clciking it will reset the active category:
<li
className={
liStyle +
(props.activeCategory === '' || props.activeCategory === undefined
? ' bg-grey-dark'
: ' bg-grey-lighter')
}
onClick={() => setNewActiveCategory('')}
>
All
</li>
Now whenever we click a category, we ask App to update the activeCategory state property, and that’s automatically passed down again to the NavBar component to let it highlight the category, and React is also updating the active bills, so BillsTable and Chart components only show the active category amounts.
See the app in the current state on CodeSandbox.