diff --git a/src/sidebar/components/tag-editor.js b/src/sidebar/components/tag-editor.js
index 0280cef66cc..b470ecf24c8 100644
--- a/src/sidebar/components/tag-editor.js
+++ b/src/sidebar/components/tag-editor.js
@@ -244,15 +244,32 @@ function TagEditor({
* @param {string} item - Suggested tag
* @return {JSXElement} - Formatted tag for use in list
*/
- const formatSuggestItem = item => {
- const curVal = pendingTag();
- const prefix = item.slice(0, item.indexOf(curVal));
- const suffix = item.slice(item.indexOf(curVal) + curVal.length);
+ const formatSuggestedItem = item => {
+ // filtering of tags is case-insensitive
+ const curVal = pendingTag().toLowerCase();
+ const suggestedTag = item.toLowerCase();
+ const matchIndex = suggestedTag.indexOf(curVal);
+
+ // If the current input doesn't seem to match the suggested tag,
+ // just render the tag as-is.
+ if (matchIndex === -1) {
+ return {item};
+ }
+
+ // Break the suggested tag into three parts:
+ // 1. Substring of the suggested tag that occurs before the match
+ // with the current input
+ const prefix = item.slice(0, matchIndex);
+ // 2. Substring of the suggested tag that matches the input text. NB:
+ // This may be in a different case than the input text.
+ const matchString = item.slice(matchIndex, matchIndex + curVal.length);
+ // 3. Substring of the suggested tag that occurs after the matched input
+ const suffix = item.slice(matchIndex + curVal.length);
return (
{prefix}
- {curVal}
+ {matchString}
{suffix}
);
@@ -324,7 +341,7 @@ function TagEditor({
{
+ fakeTagsService.filter.returns(['fine AArdvark', 'AAArgh']);
+ const wrapper = createComponent();
+ wrapper.find('input').instance().value = 'aa';
+ typeInput(wrapper);
+
+ const formattingFn = wrapper.find('AutocompleteList').prop('listFormatter');
+ const tagList = wrapper.find('AutocompleteList').prop('list');
+
+ const firstSuggestedTag = mount(formattingFn(tagList[0]))
+ .find('span')
+ .text();
+ const secondSuggestedTag = mount(formattingFn(tagList[1]))
+ .find('span')
+ .text();
+
+ // Even though the entered text was lower case ('aa'), the suggested tag
+ // should be rendered with its original casing (upper-case here)
+ assert.equal(firstSuggestedTag, 'AAArgh');
+ assert.equal(secondSuggestedTag, 'fine AArdvark');
+ });
+
+ it('shows suggested tags as-is if they do not seem to match the input', () => {
+ // This case addresses a situation in which a substring match isn't found
+ // for the current input text against a given suggested tag. This should not
+ // happen in practice—i.e. filtered tags should match the current input—
+ // but there is no contract that the tags service filtering uses the same
+ // "matching" as the component, so we should be able to handle cases where
+ // there doesn't "seem" to be a match by just rendering the suggested tag
+ // as-is.
+ fakeTagsService.filter.returns(['fine AArdvark', 'AAArgh']);
+ const wrapper = createComponent();
+ wrapper.find('input').instance().value = 'bb';
+ typeInput(wrapper);
+
+ const formattingFn = wrapper.find('AutocompleteList').prop('listFormatter');
+ const tagList = wrapper.find('AutocompleteList').prop('list');
+
+ const firstSuggestedTag = mount(formattingFn(tagList[0]))
+ .find('span')
+ .text();
+ const secondSuggestedTag = mount(formattingFn(tagList[1]))
+ .find('span')
+ .text();
+
+ // Obviously, these don't have a `bb` substring; we'll just render them...
+ assert.equal(firstSuggestedTag, 'AAArgh');
+ assert.equal(secondSuggestedTag, 'fine AArdvark');
+ });
+
it('passes the text value to filter() after receiving input', () => {
const wrapper = createComponent();
wrapper.find('input').instance().value = 'tag3';