Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-highlight-words

Package Overview
Dependencies
Maintainers
1
Versions
31
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-highlight-words - npm Package Compare versions

Comparing version 0.1.1 to 0.1.2

src/test-utils.js

2

package.json
{
"name": "react-highlight-words",
"version": "0.1.1",
"version": "0.1.2",
"description": "React component to highlight words within a larger body of text",

@@ -5,0 +5,0 @@ "main": "dist/main.js",

@@ -1,2 +0,2 @@

# react-highlight-words
<img src="https://cloud.githubusercontent.com/assets/29597/11913937/0d2dcd78-a629-11e5-83e7-6a17b6d765a5.png" width="260" height="260">

@@ -7,2 +7,19 @@ React component to highlight words within a larger body of text.

## Usage
To use it, just provide it with an array of search terms and a body of text to highlight:
```html
<Highlighter
highlightClassName='YourHighlightClass'
searchWords={['and', 'or', 'the']}
textToHighlight="The dog is chasing the cat. Or perhaps they're just playing?"
/>
```
And the `Highlighter` will mark all occurrences of search terms within the text:
<img width="368" alt="screen shot 2015-12-19 at 8 23 43 am" src="https://cloud.githubusercontent.com/assets/29597/11914033/e3c319f6-a629-11e5-896d-1a5ce22c9ea2.png">
## Installation

@@ -9,0 +26,0 @@ ```

@@ -10,4 +10,4 @@ import React, { Component } from 'react'

this.state = {
searchText: 'is',
textToHighlight: 'This is some text.'
searchText: 'and or the',
textToHighlight: `When in the Course of human events it becomes necessary for one people to dissolve the political bands which have connected them with another and to assume among the powers of the earth, the separate and equal station to which the Laws of Nature and of Nature's God entitle them, a decent respect to the opinions of mankind requires that they should declare the causes which impel them to the separation.`
}

@@ -22,18 +22,36 @@ }

<div {...props}>
<div className={styles.LabelAndInputRow}>
<label className={styles.Label}>Search terms</label>
<input
name='searchTerms'
value={searchText}
onChange={event => this.setState({ searchText: event.target.value })}/>
<label className={styles.Label}>Text to highlight</label>
<input
name='textToHighlight'
value={textToHighlight}
onChange={event => this.setState({ textToHighlight: event.target.value })}/>
</div>
<h4 className={styles.Header}>
Search terms
</h4>
<input
className={styles.Input}
name='searchTerms'
value={searchText}
onChange={event => this.setState({ searchText: event.target.value })}/>
<h4 className={styles.Header}>
Body of Text
</h4>
<textarea
className={styles.Input}
name='textToHighlight'
value={textToHighlight}
onChange={event => this.setState({ textToHighlight: event.target.value })}/>
<h4 className={styles.Header}>
Output
</h4>
<Highlighter
highlightClassName={styles.Highlight}
highlightStyle={{ fontWeight: 'normal' }}
searchWords={searchWords}
textToHighlight={textToHighlight}/>
textToHighlight={textToHighlight}
/>
<p className={styles.Footer}>
<a href='https://github.com/bvaughn/react-highlight-words/blob/master/src/Highlighter.example.js'>
View the source
</a>
</p>
</div>

@@ -40,0 +58,0 @@ )

/* @flow */
import React, { PropTypes } from 'react'
import styles from './Highlighter.css'
import * as Chunks from './utils.js'
Highlighter.propTypes = {
highlightClassName: PropTypes.string,
highlightStyle: PropTypes.object,
searchWords: PropTypes.arrayOf(PropTypes.string).isRequired,

@@ -17,66 +19,32 @@ textToHighlight: PropTypes.string.isRequired

highlightClassName = '',
highlightStyle = {},
searchWords,
textToHighlight }
) {
const chunks = Chunks.findAll(textToHighlight, searchWords)
const highlightClassNames = `${styles.Term} ${highlightClassName}`
let uidCounter = 0
const content = searchWords
.filter(searchWord => searchWord) // Remove empty words
.reduce((substrings, searchWord) => {
const searchWordLength = searchWord.length
const regex = new RegExp(searchWord, 'gi')
// Step backwards so that changes to the Array won't cause us to visit the same text twice.
for (var i = substrings.length - 1; i >= 0; i--) {
let substring = substrings[i]
return (
<span>
{chunks.map((chunk, index) => {
const text = textToHighlight.substr(chunk.start, chunk.end - chunk.start)
// Examine the current substring for any marker-matches.
// If we find matches, replace the plain text string with a styled <span> wrapper.
// If this element of the array already contains a styled <span> we can ignore it.
// There is no benefit to highlighting text twice.
// (This means that only one of a pair of overlapping words will be highlighted, but that's okay.)
if (typeof substring === 'string') {
let substringReplacements = []
let startIndex
while ((startIndex = substring.search(regex)) >= 0) {
if (startIndex > 0) {
substringReplacements.push(substring.substring(0, startIndex))
}
let endIndex = startIndex + searchWordLength
substringReplacements.push(
<span
key={++uidCounter}
className={highlightClassNames}
>
{substring.substring(startIndex, endIndex)}
</span>
)
substring = substring.substring(endIndex)
}
// If we didn't find any matches then just leave the current substring as-is.
if (substringReplacements.length) {
// Add any remaining text
if (substring.length) {
substringReplacements.push(substring)
}
// Replace the existing string element with the new mix of plain and styled elements.
substrings.splice(i, 1, ...substringReplacements)
}
if (chunk.highlight) {
return (
<mark
className={highlightClassNames}
key={index}
style={highlightStyle}
>
{text}
</mark>
)
} else {
return (
<span key={index}>{text}</span>
)
}
}
return substrings
}, [textToHighlight])
return (
<span>
{content}
})}
</span>
)
}

@@ -0,64 +1,98 @@

import React from 'react'
import Highlighter from './Highlighter'
import { render } from './test-utils'
import expect from 'expect.js'
describe('Highlighter', () => {
function getHighlighterChildren (textToHighlight, searchWords, highlightClassName) {
const instance = Highlighter({ textToHighlight, searchWords, highlightClassName })
const HIGHLIGHT_CLASS = 'customHighlightClass'
const HIGHLIGHT_QUERY_SELECTOR = `.${HIGHLIGHT_CLASS}`
return instance.props.children
function getHighlighterChildren (textToHighlight, searchWords, highlightStyle) {
const node = render(
<div>
<Highlighter
highlightClassName={HIGHLIGHT_CLASS}
highlightStyle={highlightStyle}
searchWords={searchWords}
textToHighlight={textToHighlight}
/>
</div>
)
return node.children[0]
}
it('should properly handle empty searchText', () => {
expect(getHighlighterChildren('This is text', [])).to.eql(['This is text'])
expect(getHighlighterChildren('This is text', [''])).to.eql(['This is text'])
const emptyValues = [[], ['']]
emptyValues.forEach((emptyValue) => {
const node = getHighlighterChildren('This is text', emptyValue)
expect(node.children.length).to.equal(1)
expect(node.querySelectorAll(HIGHLIGHT_QUERY_SELECTOR).length).to.equal(0)
expect(node.textContent).to.eql('This is text')
})
})
it('should properly handle empty textToHighlight', () => {
expect(getHighlighterChildren('', ['search']).length).to.eql([])
const node = getHighlighterChildren('', ['search'])
expect(node.children.length).to.equal(0)
expect(node.querySelectorAll(HIGHLIGHT_QUERY_SELECTOR).length).to.equal(0)
expect(node.textContent).to.eql('')
})
it('should highlight searchText words that exactly match words in textToHighlight', () => {
const matches = getHighlighterChildren('This is text', ['text'])
expect(matches.length).to.equal(2)
expect(matches[0]).to.equal('This is ')
expect(matches[1].props.children).to.equal('text')
const node = getHighlighterChildren('This is text', ['text'])
expect(node.children.length).to.equal(2)
const matches = node.querySelectorAll(HIGHLIGHT_QUERY_SELECTOR)
expect(matches.length).to.equal(1)
expect(matches[0].textContent).to.eql('text')
})
it('should highlight searchText words that partial-match text in textToHighlight', () => {
const matches = getHighlighterChildren('This is text', ['Th'])
expect(matches.length).to.equal(2)
expect(matches[0].props.children).to.equal('Th')
expect(matches[1]).to.equal('is is text')
const node = getHighlighterChildren('This is text', ['Th'])
expect(node.children.length).to.equal(2)
const matches = node.querySelectorAll(HIGHLIGHT_QUERY_SELECTOR)
expect(matches.length).to.equal(1)
expect(matches[0].textContent).to.eql('Th')
expect(node.children[0].textContent).to.equal('Th')
expect(node.children[1].textContent).to.equal('is is text')
})
it('should highlight multiple occurrences of a searchText word', () => {
const matches = getHighlighterChildren('This is text', ['is'])
expect(matches.length).to.equal(5)
expect(matches[0]).to.equal('Th')
expect(matches[1].props.children).to.equal('is')
expect(matches[2]).to.equal(' ')
expect(matches[3].props.children).to.equal('is')
expect(matches[4]).to.equal(' text')
const node = getHighlighterChildren('This is text', ['is'])
expect(node.children.length).to.equal(5)
expect(node.querySelectorAll(HIGHLIGHT_QUERY_SELECTOR).length).to.equal(2)
expect(node.textContent).to.eql('This is text')
expect(node.children[0].textContent).to.equal('Th')
expect(node.children[1].textContent).to.equal('is')
expect(node.children[2].textContent).to.equal(' ')
expect(node.children[3].textContent).to.equal('is')
expect(node.children[4].textContent).to.equal(' text')
})
it('should highlight multiple searchText words', () => {
const matches = getHighlighterChildren('This is text', ['This', 'text'])
expect(matches.length).to.equal(3)
expect(matches[0].props.children).to.equal('This')
expect(matches[1]).to.equal(' is ')
expect(matches[2].props.children).to.equal('text')
const node = getHighlighterChildren('This is text', ['This', 'text'])
expect(node.children.length).to.equal(3)
expect(node.querySelectorAll(HIGHLIGHT_QUERY_SELECTOR).length).to.equal(2)
expect(node.textContent).to.eql('This is text')
expect(node.children[0].textContent).to.equal('This')
expect(node.children[1].textContent).to.equal(' is ')
expect(node.children[2].textContent).to.equal('text')
})
it('should match terms in a case insensitive way but show their case-sensitive representation', () => {
const matches = getHighlighterChildren('This is text', ['this'])
expect(matches.length).to.equal(2)
expect(matches[0].props.children).to.equal('This')
expect(matches[1]).to.equal(' is text')
const node = getHighlighterChildren('This is text', ['this'])
const matches = node.querySelectorAll(HIGHLIGHT_QUERY_SELECTOR)
expect(matches.length).to.equal(1)
expect(matches[0].textContent).to.equal('This')
})
it('should use the :highlightClassName if specified', () => {
const matches = getHighlighterChildren('This is text', ['text'], 'customClass')
expect(matches.length).to.equal(2)
expect(matches[1].props.className).to.contain('customClass')
const node = getHighlighterChildren('This is text', ['text'])
expect(node.querySelector('mark').className).to.contain(HIGHLIGHT_CLASS)
})
it('should use the :highlightStyle if specified', () => {
const node = getHighlighterChildren('This is text', ['text'], { color: 'red' })
expect(node.querySelector('mark').style.color).to.contain('red')
})
})

@@ -7,6 +7,21 @@ import React from 'react'

return (
<div className={styles.demo}>
<HighlighterExample/>
<div>
<div className={styles.headerRow}>
<img
className={styles.logo}
width='482'
height='278'
src='https://cloud.githubusercontent.com/assets/29597/11903349/6da3d9c0-a56d-11e5-806d-1c7b96289523.png'
/>
</div>
<div className={styles.body}>
<div className={styles.card}>
<HighlighterExample/>
</div>
</div>
<div className={styles.footer}>
react-highlight-words is available under the MIT license.
</div>
</div>
)
}

@@ -9,2 +9,3 @@ /**

import { render } from 'react-dom'
import './index.css'

@@ -11,0 +12,0 @@ render(

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc