Skip to content

Commit

Permalink
feat(highlight): add case sensitive prop
Browse files Browse the repository at this point in the history
  • Loading branch information
arusahni committed Mar 22, 2024
1 parent b0a36db commit 9803e41
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 9 deletions.
6 changes: 6 additions & 0 deletions .changeset/dry-dolls-beam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@chakra-ui/react": minor
"@chakra-ui/props-docs": minor
---

add caseSensitive property to the Highlight component
16 changes: 12 additions & 4 deletions packages/components/src/highlight/highlight-words.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,32 @@ export interface Chunk {
export interface HighlightOptions {
text: string
query: string | string[]
caseSensitive?: boolean
}

const escapeRegexp = (term: string): string =>
term.replace(/[|\\{}()[\]^$+*?.-]/g, (char: string) => `\\${char}`)

function buildRegex(query: string[]) {
function buildRegex(query: string[], caseSensitive: boolean) {
const _query = query
.filter((text) => text.length !== 0)
.map((text) => escapeRegexp(text.trim()))
if (!_query.length) {
return null
}

return new RegExp(`(${_query.join("|")})`, "ig")
return new RegExp(`(${_query.join("|")})`, `${caseSensitive ? "" : "i"}g`)
}

export function highlightWords({ text, query }: HighlightOptions): Chunk[] {
const regex = buildRegex(Array.isArray(query) ? query : [query])
export function highlightWords({
text,
query,
caseSensitive = false,
}: HighlightOptions): Chunk[] {
const regex = buildRegex(
Array.isArray(query) ? query : [query],
caseSensitive,
)
if (!regex) {
return [{ text, match: false }]
}
Expand Down
13 changes: 13 additions & 0 deletions packages/components/src/highlight/highlight.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,16 @@ export const MarketingExample = () => {
</Heading>
)
}

export const CaseSensitivity = () => (
<Text fontWeight="semibold">
<Highlight
query={["Human", "Dogs"]}
styles={{ px: "2", py: "1", rounded: "full", bg: "green.100" }}
caseSensitive
>
Human beings have many kinds of pets, of which dogs are one. Dogs are
often called human's best friends.
</Highlight>
</Text>
)
5 changes: 3 additions & 2 deletions packages/components/src/highlight/highlight.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface HighlightProps {
query: string | string[]
children: string | ((props: Chunk[]) => React.ReactNode)
styles?: SystemStyleObject
caseSensitive?: boolean
}

/**
Expand All @@ -16,13 +17,13 @@ export interface HighlightProps {
* @see Docs https://chakra-ui.com/docs/components/highlight
*/
export function Highlight(props: HighlightProps): JSX.Element {
const { children, query, styles } = props
const { children, query, styles, caseSensitive } = props

if (typeof children !== "string") {
throw new Error("The children prop of Highlight must be a string")
}

const chunks = useHighlight({ query, text: children })
const chunks = useHighlight({ query, text: children, caseSensitive })

return (
<>
Expand Down
27 changes: 27 additions & 0 deletions packages/components/src/highlight/use-highlight.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,31 @@ describe("useHighlight", () => {
]
`)
})

test("useHighlight matches case-sensitively correctly", () => {
const query = ["", "text"]
const { result } = hooks.render(() =>
useHighlight({
query: query,
text: "Text: this is an ordinary text which should have one match ",
caseSensitive: true,
}),
)
expect(result.current).toMatchInlineSnapshot(`
[
{
"match": false,
"text": "Text: this is an ordinary ",
},
{
"match": true,
"text": "text",
},
{
"match": false,
"text": " which should have one match ",
},
]
`)
})
})
7 changes: 5 additions & 2 deletions packages/components/src/highlight/use-highlight.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { HighlightOptions, highlightWords } from "./highlight-words"
export interface UseHighlightProps extends HighlightOptions {}

export function useHighlight(props: UseHighlightProps) {
const { text, query } = props
return useMemo(() => highlightWords({ text, query }), [text, query])
const { text, query, caseSensitive } = props
return useMemo(
() => highlightWords({ text, query, caseSensitive }),
[text, query, caseSensitive],
)
}
4 changes: 3 additions & 1 deletion packages/props-docs/generated/highlight.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
{
"Highlight": {
"query": { "type": "string | string[]", "required": true },
"caseSensitive": { "type": "boolean", "required": false },
"styles": { "type": "SystemStyleObject", "required": false }
},
"Mark": {},
"UseHighlight": {
"query": { "type": "string | string[]", "required": true },
"text": { "type": "string", "required": true }
"text": { "type": "string", "required": true },
"caseSensitive": { "type": "boolean", "required": false }
}
}

0 comments on commit 9803e41

Please sign in to comment.