@cutting/material-react-table 4.0.3 → 4.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.esm.js +32 -2
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +32 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/MaterialReactTable.smoke.test.tsx +19 -0
- package/src/components/body/MRT_TableBodyCellValue.tsx +2 -2
- package/src/utils/highlight.utils.ts +52 -0
package/package.json
CHANGED
|
@@ -53,4 +53,23 @@ describe('MaterialReactTable smoke', () => {
|
|
|
53
53
|
);
|
|
54
54
|
expect(firstBodyRow?.textContent).toContain('Aaron');
|
|
55
55
|
});
|
|
56
|
+
|
|
57
|
+
test('wraps matching text in a highlight span when global filter is active', () => {
|
|
58
|
+
render(
|
|
59
|
+
<MaterialReactTable
|
|
60
|
+
columns={columns}
|
|
61
|
+
data={data}
|
|
62
|
+
enableFilterMatchHighlighting
|
|
63
|
+
enableGlobalFilter
|
|
64
|
+
initialState={{ globalFilter: 'Sar', showGlobalFilter: true }}
|
|
65
|
+
/>,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const highlightWrapper = screen.getByRole('note', { name: 'Sara' });
|
|
69
|
+
expect(highlightWrapper).toBeInTheDocument();
|
|
70
|
+
|
|
71
|
+
const matchSpans = highlightWrapper.querySelectorAll('span');
|
|
72
|
+
expect(matchSpans.length).toBeGreaterThan(1);
|
|
73
|
+
expect(highlightWrapper.textContent).toBe('Sara');
|
|
74
|
+
});
|
|
56
75
|
});
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Box } from '@mui/material';
|
|
2
|
-
import highlightWords from 'highlight-words';
|
|
3
2
|
import { type ReactNode, type RefObject } from 'react';
|
|
4
3
|
|
|
5
4
|
import {
|
|
@@ -7,6 +6,7 @@ import {
|
|
|
7
6
|
type MRT_RowData,
|
|
8
7
|
type MRT_TableInstance,
|
|
9
8
|
} from '../../types';
|
|
9
|
+
import { highlightWords } from '../../utils/highlight.utils';
|
|
10
10
|
|
|
11
11
|
const allowedTypes = ['string', 'number'];
|
|
12
12
|
|
|
@@ -78,7 +78,7 @@ export const MRT_TableBodyCellValue = <TData extends MRT_RowData>({
|
|
|
78
78
|
allowedTypes.includes(typeof globalFilter) &&
|
|
79
79
|
column.getCanGlobalFilter()))
|
|
80
80
|
) {
|
|
81
|
-
const chunks = highlightWords
|
|
81
|
+
const chunks = highlightWords({
|
|
82
82
|
matchExactly:
|
|
83
83
|
(filterValue ? columnDef._filterFn : globalFilterFn) !== 'fuzzy',
|
|
84
84
|
query: (filterValue ?? globalFilter ?? '').toString(),
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export interface HighlightChunk {
|
|
2
|
+
key: string;
|
|
3
|
+
match: boolean;
|
|
4
|
+
text: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface HighlightWordsArgs {
|
|
8
|
+
matchExactly?: boolean;
|
|
9
|
+
query: string;
|
|
10
|
+
text: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const escapeRegExp = (term: string): string =>
|
|
14
|
+
term.replace(/[|\\{}()[\]^$+*?.-]/g, (char) => `\\${char}`);
|
|
15
|
+
|
|
16
|
+
const termsToRegExpString = (terms: string): string =>
|
|
17
|
+
terms.replace(/\s{2,}/g, ' ').split(' ').join('|');
|
|
18
|
+
|
|
19
|
+
const buildRegExp = (terms: string, matchExactly: boolean): RegExp => {
|
|
20
|
+
const fromString = /^([/~@;%#'])(.*?)\1([gimsuy]*)$/.exec(terms);
|
|
21
|
+
if (fromString) {
|
|
22
|
+
return new RegExp(fromString[2], fromString[3]);
|
|
23
|
+
}
|
|
24
|
+
const escaped = escapeRegExp(terms.trim());
|
|
25
|
+
const pattern = matchExactly ? escaped : termsToRegExpString(escaped);
|
|
26
|
+
return new RegExp(`(${pattern})`, 'ig');
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
let uidCounter = 0;
|
|
30
|
+
const uid = (): string => `mrt-hl-${(uidCounter += 1).toString(36)}`;
|
|
31
|
+
|
|
32
|
+
export const highlightWords = ({
|
|
33
|
+
matchExactly = false,
|
|
34
|
+
query,
|
|
35
|
+
text,
|
|
36
|
+
}: HighlightWordsArgs): HighlightChunk[] => {
|
|
37
|
+
const safeQuery = typeof query === 'string' ? query.trim() : query;
|
|
38
|
+
if (safeQuery === '') {
|
|
39
|
+
return [{ key: uid(), match: false, text }];
|
|
40
|
+
}
|
|
41
|
+
const searchRegexp = buildRegExp(query, matchExactly);
|
|
42
|
+
return text
|
|
43
|
+
.split(searchRegexp)
|
|
44
|
+
.filter((str) => str.length > 0)
|
|
45
|
+
.map((str) => ({
|
|
46
|
+
key: uid(),
|
|
47
|
+
match: matchExactly
|
|
48
|
+
? str.toLowerCase() === safeQuery.toLowerCase()
|
|
49
|
+
: searchRegexp.test(str),
|
|
50
|
+
text: str,
|
|
51
|
+
}));
|
|
52
|
+
};
|