Skip to content
This repository has been archived by the owner on Dec 30, 2022. It is now read-only.

Commit

Permalink
feat(Highlight): support array of strings (#715)
Browse files Browse the repository at this point in the history
* feat(highlightArray): plug new index to a story

* feat(highlightArray): add test for highlight

* feat(highlightArray): visually highlight the element in arrays

* feat(highlightArray): wrap separator with span

* feat(highlightArray): modify story

* feat(highlightArray): move tagName logic to Highlighter component

* feat(highlightArray): manage className with cx

* feat(highlightArray): change className to render style

* feat(highlightArray): move back to tagName

* feat(highlightArray): test Highlighter

* feat(highlightArray): merge two stories in Storybook

* feat(highlightArray): updating Highlighting results guide

* feat(highlightArray): update Highlight and Snippet Widgets documentation

* feat(highlightArray): fix linting

* feat(highlightArray): fix linting error

* feat(highlightArray): updates to address reviews on PR

* feat(highlightArray): add function to generate key for iterables

* feat(highlightArray): update documentation after feedback on PR
  • Loading branch information
marielaures authored and samouss committed Dec 19, 2017
1 parent 8aa79ff commit 8e93c6a
Show file tree
Hide file tree
Showing 12 changed files with 735 additions and 136 deletions.
6 changes: 3 additions & 3 deletions docgen/src/guide/Highlighting_results.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ like most of its features it comes in two flavors, depending on your use case:
## <Highlight> and <Snippet> widgets

Highlighting is based on the results and you will need to make a custom Hit in order
to use the Highlighter. The Highlight and the Snippet widgets takes two props:
- attributeName: the path to the highlighted attribute
to use the Highlighter. The Highlight and the Snippet widgets take two props:
- attributeName: the path to the highlighted attribute of the hit (which can be either a string or an array of strings)
- hit: a single result object

**Notes:**
Expand Down Expand Up @@ -69,7 +69,7 @@ from the results. This function takes a single parameter object with three
properties:
- attributeName: the highlighted attribute name
- hit: a single result object
- highlightProperty: the path to the structure containing the highlighted attribute. The value is either `_highlightResult` or `_snippetResult` depending if you want to make an Highlight or Snippet widget.
- highlightProperty: the path to the structure containing the highlighted attribute. The value is either `_highlightResult` or `_snippetResult` depending on whether you want to make a Highlight or a Snippet widget.

Those parameters are taken from the context in which the the custom component
is used, therefore it's reasonable to have them as props.
Expand Down
2 changes: 2 additions & 0 deletions packages/react-instantsearch/src/components/Highlight.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ Highlight.propTypes = {
attributeName: PropTypes.string.isRequired,
highlight: PropTypes.func.isRequired,
tagName: PropTypes.string,
nonHighlightedTagName: PropTypes.string,
separatorComponent: PropTypes.node,
};
86 changes: 69 additions & 17 deletions packages/react-instantsearch/src/components/Highlighter.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,79 @@
import PropTypes from 'prop-types';
import React from 'react';
import classNames from './classNames';

const cx = classNames('Highlight');

function generateKey(i, value) {
return `split-${i}-${value}`;
}

export const Highlight = ({
value,
highlightedTagName,
isHighlighted,
nonHighlightedTagName,
}) => {
const TagName = isHighlighted ? highlightedTagName : nonHighlightedTagName;
const className = isHighlighted ? 'highlighted' : 'nonHighlighted';
return <TagName {...cx(className)}>{value}</TagName>;
};

Highlight.propTypes = {
value: PropTypes.string.isRequired,
isHighlighted: PropTypes.bool.isRequired,
highlightedTagName: PropTypes.string.isRequired,
nonHighlightedTagName: PropTypes.string.isRequired,
};

export default function Highlighter({
hit,
attributeName,
highlight,
highlightProperty,
tagName,
nonHighlightedTagName,
separator,
}) {
const parsedHighlightedValue = highlight({
hit,
attributeName,
highlightProperty,
});
const reactHighlighted = parsedHighlightedValue.map((v, i) => {
const key = `split-${i}-${v.value}`;
if (!v.isHighlighted) {
return (
<span key={key} className="ais-Highlight__nonHighlighted">
{v.value}
</span>
);
}
const HighlightedTag = tagName ? tagName : 'em';
return (
<HighlightedTag key={key} className="ais-Highlight__highlighted">
{v.value}
</HighlightedTag>
);
});
return <span className="ais-Highlight">{reactHighlighted}</span>;

return (
<span className="ais-Highlight">
{parsedHighlightedValue.map((item, i) => {
if (Array.isArray(item)) {
const isLast = i === parsedHighlightedValue.length - 1;
return (
<span key={generateKey(i, hit[attributeName][i])}>
{item.map((element, index) => (
<Highlight
key={generateKey(index, element.value)}
value={element.value}
highlightedTagName={tagName}
nonHighlightedTagName={nonHighlightedTagName}
isHighlighted={element.isHighlighted}
/>
))}
{!isLast && <span {...cx('separator')}>{separator}</span>}
</span>
);
}

return (
<Highlight
key={generateKey(i, item.value)}
value={item.value}
highlightedTagName={tagName}
nonHighlightedTagName={nonHighlightedTagName}
isHighlighted={item.isHighlighted}
/>
);
})}
</span>
);
}

Highlighter.propTypes = {
Expand All @@ -38,4 +82,12 @@ Highlighter.propTypes = {
highlight: PropTypes.func.isRequired,
highlightProperty: PropTypes.string.isRequired,
tagName: PropTypes.string,
nonHighlightedTagName: PropTypes.string,
separator: PropTypes.node,
};

Highlighter.defaultProps = {
tagName: 'em',
nonHighlightedTagName: 'span',
separator: ', ',
};
Loading

0 comments on commit 8e93c6a

Please sign in to comment.