diff --git a/src/Highlight.js b/src/Highlight.js new file mode 100644 index 00000000..c306c69d --- /dev/null +++ b/src/Highlight.js @@ -0,0 +1,27 @@ +import React from 'react' +import { Typography } from '@material-ui/core' + +const escapeHtml = unsafe => + unsafe + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') + +const addHighlight = (query, text, className = '') => { + if (!text) return '' + return escapeHtml(text).replace( + new RegExp(query, 'gi'), + match => `${match}`, + ) +} + +export default function Highlight({ query, text, classes = {}, ...props }) { + return ( + + ) +} diff --git a/src/search/SearchProvider.js b/src/search/SearchProvider.js index 689f282e..d121b0d5 100644 --- a/src/search/SearchProvider.js +++ b/src/search/SearchProvider.js @@ -46,6 +46,7 @@ export default function SearchProvider({ children, query, initialGroups, active }, 250) const context = { + query, state, setState, fetchSuggestions, diff --git a/src/search/SearchSuggestionItem.js b/src/search/SearchSuggestionItem.js index a0c477ee..ea3ae266 100644 --- a/src/search/SearchSuggestionItem.js +++ b/src/search/SearchSuggestionItem.js @@ -1,9 +1,10 @@ -import React from 'react' +import React, { useContext } from 'react' import makeStyles from '@material-ui/core/styles/makeStyles' import Link from '../link/Link' -import { Typography } from '@material-ui/core' import PropTypes from 'prop-types' import Image from '../Image' +import SearchContext from './SearchContext' +import Highlight from '../Highlight' export const styles = theme => ({ root: { @@ -28,6 +29,12 @@ export const styles = theme => ({ }, }, }, + text: {}, + highlight: { + backgroundColor: 'rgba(0,0,0,0.05)', + borderRadius: '2px', + color: theme.palette.secondary.main, + }, }) const useStyles = makeStyles(styles, { name: 'RSFSearchSuggestionItem' }) @@ -42,6 +49,8 @@ export default function SearchSuggestionItem({ }) { classes = useStyles({ classes }) + const { query } = useContext(SearchContext) + return (
  • @@ -55,7 +64,12 @@ export default function SearchSuggestionItem({ {...thumbnailProps} {...item.thumbnail} /> - {item.text} + )} diff --git a/test/Highlight.test.js b/test/Highlight.test.js new file mode 100644 index 00000000..0bd89be7 --- /dev/null +++ b/test/Highlight.test.js @@ -0,0 +1,25 @@ +import React from 'react' +import { mount } from 'enzyme' +import Highlight from 'react-storefront/Highlight' + +describe('Highlight', () => { + it('should not blow up if empty props', () => { + const wrapper = mount() + expect(wrapper.text()).toBe('') + }) + it('should not add highlights if no matches', () => { + const wrapper = mount() + expect(wrapper.text()).toBe('the fox jumps over') + }) + it('should escape text', () => { + const wrapper = mount( 'bar' < zat`} />) + expect(wrapper.text()).toBe('"foo" > 'bar' < zat') + }) + it('should add highlights to matches', () => { + const wrapper = mount( + , + ) + const matches = wrapper.html().match(/ox<\/span>/g) + expect(matches.length).toBe(2) + }) +}) diff --git a/test/search/SearchSuggestionGroup.test.js b/test/search/SearchSuggestionGroup.test.js index 2f0c9e47..7a095aea 100644 --- a/test/search/SearchSuggestionGroup.test.js +++ b/test/search/SearchSuggestionGroup.test.js @@ -2,6 +2,7 @@ import React from 'react' import { mount } from 'enzyme' import SearchSuggestionGroup from 'react-storefront/search/SearchSuggestionGroup' import SearchSuggestionItem from 'react-storefront/search/SearchSuggestionItem' +import SearchProvider from 'react-storefront/search/SearchProvider' import PWAContext from 'react-storefront/PWAContext' describe('SearchSuggestionGroup', () => { @@ -13,9 +14,11 @@ describe('SearchSuggestionGroup', () => { it('should render children when provided', () => { wrapper = mount( - -
    child
    -
    , + + +
    child
    +
    +
    , ) expect(wrapper.find('#child').text()).toBe('child') @@ -24,7 +27,9 @@ describe('SearchSuggestionGroup', () => { it('should render suggested items when no children provided', () => { wrapper = mount( - + + + , ) @@ -32,7 +37,11 @@ describe('SearchSuggestionGroup', () => { }) it('should render provided caption', () => { - wrapper = mount() + wrapper = mount( + + + , + ) expect(wrapper.find(SearchSuggestionGroup).text()).toBe('test') }) diff --git a/test/search/SearchSuggestionItem.test.js b/test/search/SearchSuggestionItem.test.js index c968bc6d..b38455be 100644 --- a/test/search/SearchSuggestionItem.test.js +++ b/test/search/SearchSuggestionItem.test.js @@ -1,5 +1,6 @@ import React from 'react' import { mount } from 'enzyme' +import SearchProvider from 'react-storefront/search/SearchProvider' import SearchSuggestionItem from 'react-storefront/search/SearchSuggestionItem' import Image from 'react-storefront/Image' import PWAContext from 'react-storefront/PWAContext' @@ -14,9 +15,11 @@ describe('SearchSuggestionItem', () => { it('should render children when provided', () => { wrapper = mount( - -
    child
    -
    + + +
    child
    +
    +
    , ) @@ -26,7 +29,9 @@ describe('SearchSuggestionItem', () => { it('should render image with a text when no children provided', () => { wrapper = mount( - + + + , ) @@ -42,10 +47,12 @@ describe('SearchSuggestionItem', () => { it('should spread thumbnail props on image', () => { wrapper = mount( - + + + , )