Skip to content

Commit

Permalink
highlightedvalue: add transform function parameter
Browse files Browse the repository at this point in the history
Previously, in the SpellCheck component, if the suggested text contained
an escaped character (for example `'`), then we would escape the string
before applying our highlighting transformations (in this case, adding
<strong> markup in between the highlighted values).

This caused the indices to be misaligned (as, for example, ' would
become &#x27;). Thus the <strong> tags would interrupt the escaped
strings and they would not longer be escaped.

To fix this, we accept a transformFunction in the highlightedValue get
functions. This is a generalization of the escapeExpression function, so
any transformations can be used. We change the highlightValue hbs
partial to call the highlighted values with an escape function as the
transformFunction.

J=SLAP-696
TEST=manual

Test on a local HH Theme Jambo site using a local SDK, with a SpellCheck
component.

Try searching for "my locaton's", this should give you a string
Did you mean: my location's
And not
Did you mean: my location&#x27;s

You should also see the entire word "location's" be highlighted, not a
portion of the string.

Try searching for "my locaton&", this should give you a SpellCheck
string
Did you mean: my location&
Where location is highlighted, not the &

Tested on an npm run test, added a test case for the transformFn
  • Loading branch information
creotutar authored and tmeyer2115 committed Nov 16, 2020
1 parent e1b0826 commit 3668f5c
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 8 deletions.
45 changes: 41 additions & 4 deletions src/core/models/highlightedvalue.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ export default class HighlightedValue {
return this.buildHighlightedValue(this.value, this.matchedSubstrings);
}

/**
* get highlighted value string
* @param {Function} transformFunction takes a string and returns the transformed string
* @returns {string} The value interpolated with highlighting markup and transformed in between
*/
getWithTransformFunction (transformFunction) {
this._sortMatchedSubstrings();
return this.buildHighlightedValue(this.value, this.matchedSubstrings, transformFunction);
}

/**
* get inverted highlighted value string
* @returns {string}
Expand All @@ -28,6 +38,17 @@ export default class HighlightedValue {
return this.buildHighlightedValue(this.value, invertedSubstrings);
}

/**
* get inverted highlighted value string
* @param {Function} transformFunction takes a string and returns the transformed string
* @returns {string} The value interpolated with highlighting markup and transformed in between
*/
getInvertedWithTransformFunction (transformFunction) {
this._sortMatchedSubstrings();
const invertedSubstrings = this._getInvertedSubstrings(this.matchedSubstrings, this.value.length);
return this.buildHighlightedValue(this.value, invertedSubstrings, transformFunction);
}

/**
* introduces highlighting to input data according to highlighting specifiers
*
Expand Down Expand Up @@ -63,6 +84,13 @@ export default class HighlightedValue {
* }
* }
*
* @param {Function} transformFunction function to apply to strings in between highlighting markup
*
* example function :
* function (string) {
* return handlebars.escapeExpression(string);
* }
*
* @returns {string} copy of input value with highlighting applied
*
* example object :
Expand All @@ -74,22 +102,31 @@ export default class HighlightedValue {
* }
*
*/
buildHighlightedValue (val, highlightedSubstrings) {
buildHighlightedValue (
val,
highlightedSubstrings,
transformFunction = function (x) { return x; }
) {
let highlightedValue = '';
let nextStart = 0;

if (highlightedSubstrings.length === 0) {
return val;
return transformFunction(val);
}

for (let j = 0; j < highlightedSubstrings.length; j++) {
let start = Number(highlightedSubstrings[j].offset);
let end = start + highlightedSubstrings[j].length;

highlightedValue += [val.slice(nextStart, start), '<strong>', val.slice(start, end), '</strong>'].join('');
highlightedValue += [
transformFunction(val.slice(nextStart, start)),
'<strong>',
transformFunction(val.slice(start, end)),
'</strong>'
].join('');

if (j === highlightedSubstrings.length - 1 && end < val.length) {
highlightedValue += val.slice(end);
highlightedValue += transformFunction(val.slice(end));
}

nextStart = end;
Expand Down
10 changes: 6 additions & 4 deletions src/ui/rendering/handlebarsrenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,15 +206,17 @@ export default class HandlebarsRenderer extends Renderer {
});

self.registerHelper('highlightValue', function (value, getInverted) {
const escapedInput = self.escapeExpression(value.value || value.shortValue);
const input = value.value || value.shortValue;

const highlightedVal = new HighlightedValue({
value: escapedInput,
value: input,
matchedSubstrings: value.matchedSubstrings
});
const escapeFunction = (val) => self.escapeExpression(val);

return getInverted ? self.SafeString(highlightedVal.getInverted())
: self.SafeString(highlightedVal.get());
return getInverted
? self.SafeString(highlightedVal.getInvertedWithTransformFunction(escapeFunction))
: self.SafeString(highlightedVal.getWithTransformFunction(escapeFunction));
});
}
}
21 changes: 21 additions & 0 deletions tests/core/models/highlightedvalue.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,25 @@ describe('createing highlighted values', () => {
expect(result).toEqual(expectedResult);
expect(invertedResult).toEqual(expectedInvertedResult);
});

it('properly handles a transformFunction', () => {
const data = {
key: 'jesse',
value: 'Jes\'se Sharps',
matchedSubstrings: [ { offset: 8, length: 4 }, { offset: 1, length: 4 } ]
};

const expectedResult = 'J<strong>es%27s</strong>e S<strong>harp</strong>s';
const expectedInvertedResult = '<strong>J</strong>es%27s<strong>e S</strong>harp<strong>s</strong>';

let highlightedValue = new HighlightedValue(data);
const transformFn = (string) => {
return string.replace(/'/gi, '%27');
};
const result = highlightedValue.getWithTransformFunction(transformFn);
const invertedResult = highlightedValue.getInvertedWithTransformFunction(transformFn);

expect(result).toEqual(expectedResult);
expect(invertedResult).toEqual(expectedInvertedResult);
});
});

0 comments on commit 3668f5c

Please sign in to comment.