Skip to content

Commit 2104fa0

Browse files
Added highlighting of result
- uses react-highlight-words to handle the highlighting - added SearchQueryContext to enable having the query available everywhere - also highlight stems (for example, if the query is "failed", it will also find the word "fails" as they share the same stem. ElasticLunr does not provide a way to show the full "fails" word, so currently we highlight the beginning of the word)
1 parent 432fb0f commit 2104fa0

File tree

16 files changed

+196
-20
lines changed

16 files changed

+196
-20
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,12 @@
2828
"@fortawesome/free-solid-svg-icons": "^5.13.0",
2929
"@fortawesome/react-fontawesome": "^0.1.9",
3030
"@types/elasticlunr": "^0.9.0",
31+
"@types/react-highlight-words": "^0.16.1",
3132
"color": "^3.1.2",
3233
"elasticlunr": "^0.9.5",
3334
"marked": "^0.8.2",
3435
"react-accessible-accordion": "^3.0.1",
36+
"react-highlight-words": "^0.16.0",
3537
"sanitize-html": "^1.23.0"
3638
},
3739
"peerDependencies": {

src/SearchQueryContext.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import React from 'react'
2+
3+
export default React.createContext({ query: '' })

src/components/app/FilteredResults.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { useState } from 'react'
22
import GherkinQueryContext from '../../GherkinQueryContext'
3+
34
import SearchBar from './SearchBar'
45
import { GherkinDocumentList } from '../..'
56

src/components/app/HighLight.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import React from 'react'
2+
import Highlighter from 'react-highlight-words'
3+
import SearchQueryContext from '../../SearchQueryContext'
4+
import elasticlunr from 'elasticlunr'
5+
6+
interface IProps {
7+
text: string
8+
}
9+
10+
const HighLight: React.FunctionComponent<IProps> = ({ text }) => {
11+
const searchQueryContext = React.useContext(SearchQueryContext)
12+
const queryWords = searchQueryContext.query
13+
? searchQueryContext.query.split(' ')
14+
: []
15+
const searchWords: string[] = []
16+
17+
for (const word of queryWords) {
18+
const stem = elasticlunr.stemmer(word)
19+
searchWords.push(word)
20+
21+
if (stem !== word) {
22+
searchWords.push(stem)
23+
}
24+
}
25+
26+
elasticlunr.stemmer
27+
28+
return (
29+
<Highlighter
30+
className="highlight"
31+
highlightClassName="YourHighlightClass"
32+
searchWords={searchWords}
33+
autoEscape={true}
34+
textToHighlight={text}
35+
/>
36+
)
37+
}
38+
39+
export default HighLight

src/components/app/QueriesWrapper.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,31 @@
11
import React from 'react'
22
import GherkinQueryContext from '../../GherkinQueryContext'
33
import CucumberQueryContext from '../../CucumberQueryContext'
4+
import SearchQueryContext from '../../SearchQueryContext'
45
import { Query as GherkinQuery } from '@cucumber/gherkin'
56
import { Query as CucumberQuery } from '@cucumber/query'
67

78
interface IProps {
89
cucumberQuery: CucumberQuery
910
gherkinQuery: GherkinQuery
11+
query?: string
1012
}
1113

1214
const QueriesWrapper: React.FunctionComponent<IProps> = ({
1315
gherkinQuery,
1416
cucumberQuery,
17+
query,
1518
children,
1619
}) => {
20+
const searchQuery = { query: query }
21+
1722
return (
1823
<div className="cucumber-react">
1924
<CucumberQueryContext.Provider value={cucumberQuery}>
2025
<GherkinQueryContext.Provider value={gherkinQuery}>
21-
{children}
26+
<SearchQueryContext.Provider value={searchQuery}>
27+
{children}
28+
</SearchQueryContext.Provider>
2229
</GherkinQueryContext.Provider>
2330
</CucumberQueryContext.Provider>
2431
</div>

src/components/app/SearchBar.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
import React, { useState } from 'react'
1+
import React from 'react'
22
import { faSearch } from '@fortawesome/free-solid-svg-icons'
33
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
4+
import SearchQueryContext from '../../SearchQueryContext'
45

56
interface IProps {
67
queryUpdated: (query: string) => any
78
}
89

910
const SearchBar: React.FunctionComponent<IProps> = ({ queryUpdated }) => {
10-
const [query, setQuery] = useState('')
11+
const searchQueryContext = React.useContext(SearchQueryContext)
1112

1213
const updateQueryOnEnter = (event: React.KeyboardEvent) => {
1314
if (event.key === 'Enter') {
14-
queryUpdated(query)
15+
queryUpdated(searchQueryContext.query)
1516
}
1617
}
1718

@@ -20,9 +21,13 @@ const SearchBar: React.FunctionComponent<IProps> = ({ queryUpdated }) => {
2021
<input
2122
type="text"
2223
onKeyPress={updateQueryOnEnter}
23-
onChange={(event) => setQuery(event.target.value)}
24+
onChange={(event) => (searchQueryContext.query = event.target.value)}
2425
/>
25-
<button type="submit" onClick={() => queryUpdated(query)} value="search">
26+
<button
27+
type="submit"
28+
onClick={() => queryUpdated(searchQueryContext.query)}
29+
value="search"
30+
>
2631
<FontAwesomeIcon icon={faSearch} />
2732
</button>
2833
</div>
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import React from 'react'
22
import { messages } from '@cucumber/messages'
33
import IDocString = messages.GherkinDocument.Feature.Step.IDocString
4+
import HighLight from '../app/HighLight'
45

56
interface IProps {
67
docString: IDocString
78
}
89

910
const DocString: React.FunctionComponent<IProps> = ({ docString }) => {
10-
return <pre>{docString.content}</pre>
11+
return (
12+
<pre>
13+
<HighLight text={docString.content} />
14+
</pre>
15+
)
1116
}
1217

1318
export default DocString

src/components/gherkin/Feature.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { messages } from '@cucumber/messages'
77
import Rule from './Rule'
88
import Background from './Background'
99
import IFeature = messages.GherkinDocument.IFeature
10+
import HighLight from '../app/HighLight'
1011

1112
interface IProps {
1213
feature: IFeature
@@ -18,7 +19,9 @@ const Feature: React.FunctionComponent<IProps> = ({ feature }) => {
1819
<Tags tags={feature.tags} />
1920
<h1>
2021
<Keyword>{feature.keyword}:</Keyword>{' '}
21-
<span className="step-text">{feature.name}</span>
22+
<span className="step-text">
23+
<HighLight text={feature.name} />
24+
</span>
2225
</h1>
2326
<div className="cucumberFeature__indent">
2427
{feature.description ? (

src/components/gherkin/Rule.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Scenario from './Scenario'
55
import { messages } from '@cucumber/messages'
66
import Background from './Background'
77
import IRule = messages.GherkinDocument.Feature.FeatureChild.IRule
8+
import HighLight from '../app/HighLight'
89

910
interface IProps {
1011
rule: IRule
@@ -15,7 +16,9 @@ const Rule: React.FunctionComponent<IProps> = ({ rule }) => {
1516
<section>
1617
<h2>
1718
<Keyword>{rule.keyword}:</Keyword>{' '}
18-
<span className="step-text">{rule.name}</span>
19+
<span className="step-text">
20+
<HighLight text={rule.name} />
21+
</span>
1922
</h2>
2023
<div className="indent">
2124
<Description description={rule.description} />

src/components/gherkin/Scenario.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import IScenario = messages.GherkinDocument.Feature.IScenario
1010
import CucumberQueryContext from '../../CucumberQueryContext'
1111
import GherkinQueryContext from '../../GherkinQueryContext'
1212
import UriContext from '../../UriContext'
13+
import HighLight from '../app/HighLight'
1314

1415
interface IProps {
1516
scenario: IScenario
@@ -31,7 +32,9 @@ const Scenario: React.FunctionComponent<IProps> = ({ scenario }) => {
3132
<Tags tags={scenario.tags} />
3233
<h2>
3334
<Keyword>{scenario.keyword}:</Keyword>{' '}
34-
<span className="step-text">{scenario.name}</span>
35+
<span className="step-text">
36+
<HighLight text={scenario.name} />
37+
</span>
3538
</h2>
3639
<Description description={scenario.description} />
3740
<HookList hookSteps={beforeHooks} />

0 commit comments

Comments
 (0)