Reputation: 161
I'm trying to create a Filter/Search Table in react + material-ui. I'm using filter method to filter the table data by a input. Now I need to highlight the matching searched keywords in the table. I couldn't find any way to do this in my scenario.
I have state like this. Stored all table records and filter method. (I have simplify the code)
const [records, setRecords] = useState(tableService.getAllTableData)
const [filterFn, setFilterFn] = useState({ fn: items => { return items; } })
And the input field and filter methord
const handleSearch = e => {
let target = e.target;
const searchFields = ["fullName", "email", "mobileNumber"];
setFilterFn({
fn: items => {
if (target.value == "")
return items;
else
return items.filter(item => {
return searchFields.some(field => item[field].toLowerCase().includes(target.value.toLowerCase()))
})
}
})
}
<Input
label = "Search"
onChange = { handleSearch }
/>
And I'm rendering data this way.
const recordsAfterPagingAndSorting = () => {
return stableSort(filterFn.fn(records), getComparator(order, orderBy)).slice(page * rowsPerPage, (page + 1) * rowsPerPage)
}
<TableBody>
recordsAfterPagingAndSorting().map(record => (
<TableRow key={record.id}>
<TableCell size='small'>{record.fullName}</TableCell>
<TableCell size='small'>{record.email}</TableCell>
<TableCell size='small'>{record.mobileNumber}</TableCell>
<TableCell size='small'>{record.selectBoxTitle}</TableCell>
</TableRow>
)
</TableBody>
What is the proper way to do this? Can I handle the word highlighting logic inside the filter method?
Upvotes: 1
Views: 1429
Reputation: 1976
What you've written helps you identify what are the records that contain this search phrase. The part you're missing about highlighting is that you need the portion that matched and the portion around it.
For instance:
If someone searched for 'HAN' your name 'Eshan Rajapakshe' should be filtered but to highlight the filtered portion you need something like ['Es', 'han', ' Rajapakshe']
and to get the match info you can either compare it with the lowercased search phrase or add that logic to the array itself [{text:'Es', match:false}, {text:'han', match: true}, {text:' Rajapakshe', match: false}]
You can get the above using the package I have published
or
The first array can be obtain by a simple split using regex 'Eshan'.split(new RegExp(`(${search_phrase})`, 'gi'))
In your case you can call the module in your function. You'll have to change your render logic accordingly
import getParts from 'ec-highlighter-core'
...
const handleSearch = e => {
let target = e.target
const searchFields = ['fullName', 'email', 'mobileNumber']
setFilterFn({
fn: items => {
let filteredItems
// Get filtered items
if (target.value === '') filteredItems = items
else
filteredItems = items.filter(item => {
return searchFields.some(field =>
item[field].toLowerCase().includes(target.value.toLowerCase())
)
})
// Get the arrays for highlighting
return filteredItems.map(item => {
const out = item
searchFields.forEach(field => {
out[field] = getParts(item[field], target.value)
})
return out
})
}
})
}
<Input
label = "Search"
onChange = { handleSearch }
/>
Upvotes: 2
Reputation: 161
I found this alternative solution. Maybe not the react way. onKeyUp
event and select all dom element with querySelectorAll
//onKeyUp function
const searchedWordsHighlight = (event) => {
var searchedPara = document.querySelectorAll('.searchCell-body');
var words = event.target.value;
var regex = RegExp(words, 'gi') // case insensitive
var replacement = '<b>' + words + '</b>';
for (let i = 0; i < searchedPara.length; i++) {
const element = searchedPara[i].innerText;
if (words != "") {
let newHTML = element.replace(regex, replacement)
searchedPara[i].innerHTML = newHTML;
} else {
let newHTML = element.replace()
searchedPara[i].innerHTML = newHTML;
}
}
}
//Input field
<Input
label = "Search"
onChange = { handleSearch }
onKeyUp={searchedWordsHighlight}
/>
//Table
<TableRow key={record.id}>
<TableCell className="searchCell-body" size='small'>{record.fullName}</TableCell>
<TableCell className="searchCell-body" size='small'>{record.email}</TableCell>
<TableCell className="searchCell-body" size='small'>{record.mobileNumber}</TableCell>
<TableCell size='small'>{record.selectBoxTitle}</TableCell>
</TableRow>
Upvotes: 0