From f1d1c6e773cc9d021c2db5af1f043ea18119ce54 Mon Sep 17 00:00:00 2001 From: Richard van der Dys Date: Thu, 25 Jun 2020 17:56:03 +0300 Subject: [PATCH 1/3] Added highlight to search suggestions --- src/Highlight.js | 31 ++++++++++++++++++++++++++++++ src/search/SearchProvider.js | 1 + src/search/SearchSuggestionItem.js | 16 ++++++++++++--- 3 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 src/Highlight.js diff --git a/src/Highlight.js b/src/Highlight.js new file mode 100644 index 00000000..8e8ede76 --- /dev/null +++ b/src/Highlight.js @@ -0,0 +1,31 @@ +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) => + escapeHtml(text).replace( + new RegExp(query, 'gi'), + match => `${match}`, + ) + +function Highlight({ query, text, highlightClassName, ...props }) { + return ( + + ) +} + +Highlight.defaultProps = { + highlightClassName: 'highlight', +} + +export default Highlight 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..b1f74f0f 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,13 @@ 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 +50,8 @@ export default function SearchSuggestionItem({ }) { classes = useStyles({ classes }) + const { query } = useContext(SearchContext) + return (
  • @@ -55,7 +65,7 @@ export default function SearchSuggestionItem({ {...thumbnailProps} {...item.thumbnail} /> - {item.text} + )} From 026d625207ecba5b41535979775adb9572f09bda Mon Sep 17 00:00:00 2001 From: Richard van der Dys Date: Thu, 25 Jun 2020 22:18:01 +0300 Subject: [PATCH 2/3] Using JSS class for highlight instead --- src/Highlight.js | 10 ++-------- src/search/SearchSuggestionItem.js | 18 +++++++++++------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/Highlight.js b/src/Highlight.js index 8e8ede76..1bcac7fa 100644 --- a/src/Highlight.js +++ b/src/Highlight.js @@ -15,17 +15,11 @@ const addHighlight = (query, text, className) => match => `${match}`, ) -function Highlight({ query, text, highlightClassName, ...props }) { +export default function Highlight({ query, text, classes, ...props }) { return ( ) } - -Highlight.defaultProps = { - highlightClassName: 'highlight', -} - -export default Highlight diff --git a/src/search/SearchSuggestionItem.js b/src/search/SearchSuggestionItem.js index b1f74f0f..ea3ae266 100644 --- a/src/search/SearchSuggestionItem.js +++ b/src/search/SearchSuggestionItem.js @@ -29,12 +29,11 @@ export const styles = theme => ({ }, }, }, - text: { - '& .highlight': { - backgroundColor: 'rgba(0,0,0,0.05)', - borderRadius: '2px', - color: theme.palette.secondary.main, - }, + text: {}, + highlight: { + backgroundColor: 'rgba(0,0,0,0.05)', + borderRadius: '2px', + color: theme.palette.secondary.main, }, }) @@ -65,7 +64,12 @@ export default function SearchSuggestionItem({ {...thumbnailProps} {...item.thumbnail} /> - + )} From 3d286283cf5116326990f4b6ee7560aeed3eaf99 Mon Sep 17 00:00:00 2001 From: Richard van der Dys Date: Fri, 26 Jun 2020 09:27:11 +0300 Subject: [PATCH 3/3] Added highlight tests --- src/Highlight.js | 8 +++++--- test/Highlight.test.js | 25 +++++++++++++++++++++++ test/search/SearchSuggestionGroup.test.js | 19 ++++++++++++----- test/search/SearchSuggestionItem.test.js | 23 +++++++++++++-------- 4 files changed, 59 insertions(+), 16 deletions(-) create mode 100644 test/Highlight.test.js diff --git a/src/Highlight.js b/src/Highlight.js index 1bcac7fa..c306c69d 100644 --- a/src/Highlight.js +++ b/src/Highlight.js @@ -9,13 +9,15 @@ const escapeHtml = unsafe => .replace(/"/g, '"') .replace(/'/g, ''') -const addHighlight = (query, text, className) => - escapeHtml(text).replace( +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 }) { +export default function Highlight({ query, text, classes = {}, ...props }) { return ( { + 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( - + + + , )