diff --git a/.changeset/forty-coins-wait.md b/.changeset/forty-coins-wait.md
new file mode 100644
index 00000000000..2a4786d0467
--- /dev/null
+++ b/.changeset/forty-coins-wait.md
@@ -0,0 +1,13 @@
+---
+'braid-design-system': patch
+---
+
+---
+updated:
+ - Autosuggest
+---
+
+**Autosuggest**: Improve handling of `suggestionHighlight` prop when set to `remaining`
+
+Fixes a bug in `Autosuggest` when using `suggestionHighlight` prop set to `remaining`.
+If the input contained multiple words, the highlighted portion would be appended to the end of matching suggestions.
diff --git a/packages/braid-design-system/src/lib/components/Autosuggest/Autosuggest.docs.tsx b/packages/braid-design-system/src/lib/components/Autosuggest/Autosuggest.docs.tsx
index 8d5be7a03ff..328818f3df1 100644
--- a/packages/braid-design-system/src/lib/components/Autosuggest/Autosuggest.docs.tsx
+++ b/packages/braid-design-system/src/lib/components/Autosuggest/Autosuggest.docs.tsx
@@ -14,7 +14,6 @@ import {
Column,
Columns,
TextField,
- Inline,
} from '../';
import { IconHelp, IconLanguage } from '../icons';
import { highlightSuggestions } from './Autosuggest';
@@ -478,7 +477,7 @@ const docs: ComponentDocs = {
source(
<>
{setDefaultState('textfield', 'App')}
- {setDefaultState('suggestion', 'Apples')}
+ {setDefaultState('suggestion', 'Apples and Bananas')}
Highlight {highlightType}
-
+
{parseHighlights(
getState('suggestion'),
highlightSuggestions(
@@ -504,15 +503,14 @@ const docs: ComponentDocs = {
? 'matching'
: 'remaining',
).map(({ start, end }) => [start, end]),
- ).map((part, index) => (
-
- {part.text}
-
- ))}
-
+ ).map((part, index) =>
+ part.highlight ? (
+ {part.text}
+ ) : (
+ part.text
+ ),
+ )}
+
))}
diff --git a/packages/braid-design-system/src/lib/components/Autosuggest/Autosuggest.tsx b/packages/braid-design-system/src/lib/components/Autosuggest/Autosuggest.tsx
index 96fe31f5713..e38f9b5d14f 100644
--- a/packages/braid-design-system/src/lib/components/Autosuggest/Autosuggest.tsx
+++ b/packages/braid-design-system/src/lib/components/Autosuggest/Autosuggest.tsx
@@ -44,6 +44,7 @@ import {
type AutosuggestTranslations,
autosuggest,
} from '../../translations/en';
+import { reverseMatches } from './reverseMatches';
import * as styles from './Autosuggest.css';
@@ -346,14 +347,13 @@ export function highlightSuggestions(
value: string,
variant: HighlightOptions = 'matching',
): SuggestionMatch {
- const matches = matchHighlights(suggestion, value);
+ const matchedHighlights = matchHighlights(suggestion, value);
+ const matches =
+ variant === 'matching'
+ ? matchedHighlights
+ : reverseMatches(suggestion, matchedHighlights);
- const formattedMatches =
- variant === 'remaining'
- ? matches.map(([_, end]) => ({ start: end, end: suggestion.length }))
- : matches.map(([start, end]) => ({ start, end }));
-
- return formattedMatches;
+ return matches.map(([start, end]) => ({ start, end }));
}
export const Autosuggest = forwardRef(function (
diff --git a/packages/braid-design-system/src/lib/components/Autosuggest/reverseMatches.test.ts b/packages/braid-design-system/src/lib/components/Autosuggest/reverseMatches.test.ts
new file mode 100644
index 00000000000..e46b087a02f
--- /dev/null
+++ b/packages/braid-design-system/src/lib/components/Autosuggest/reverseMatches.test.ts
@@ -0,0 +1,58 @@
+import { type Match, reverseMatches } from './reverseMatches';
+
+describe('reverseMatches', () => {
+ it('should return intervals for non-matching parts of the suggestion', () => {
+ const suggestion = 'Apples etc';
+ const matches: Match[] = [
+ [2, 4],
+ [6, 8],
+ ];
+ const expected: Match[] = [
+ [0, 2],
+ [4, 6],
+ [8, 10],
+ ];
+
+ expect(reverseMatches(suggestion, matches)).toEqual(expected);
+ });
+
+ it('should handle no matches', () => {
+ const suggestion = 'Apple';
+ const matches: Match[] = [];
+ const expected: Match[] = [[0, 5]];
+
+ expect(reverseMatches(suggestion, matches)).toEqual(expected);
+ });
+
+ it('should handle matches that cover the entire suggestion', () => {
+ const suggestion = 'Apple';
+ const matches: Match[] = [[0, 5]];
+ const expected: Match[] = [];
+
+ expect(reverseMatches(suggestion, matches)).toEqual(expected);
+ });
+
+ it('should handle matches at the start of the suggestion', () => {
+ const suggestion = 'Apple';
+ const matches: Match[] = [[0, 2]];
+ const expected: Match[] = [[2, 5]];
+
+ expect(reverseMatches(suggestion, matches)).toEqual(expected);
+ });
+
+ it('should handle matches at the end of the suggestion', () => {
+ const suggestion = 'Apple';
+ const matches: Match[] = [[3, 5]];
+ const expected: Match[] = [[0, 3]];
+
+ expect(reverseMatches(suggestion, matches)).toEqual(expected);
+ });
+
+ it('should handle matches for a single character suggestion', () => {
+ const suggestion = 'A';
+ const matches: Match[] = [[0, 1]];
+ const expected: Match[] = [];
+
+ expect(reverseMatches(suggestion, matches)).toEqual(expected);
+ });
+});
diff --git a/packages/braid-design-system/src/lib/components/Autosuggest/reverseMatches.ts b/packages/braid-design-system/src/lib/components/Autosuggest/reverseMatches.ts
new file mode 100644
index 00000000000..9e96d74d709
--- /dev/null
+++ b/packages/braid-design-system/src/lib/components/Autosuggest/reverseMatches.ts
@@ -0,0 +1,31 @@
+/*
+ In each Match,
+ - First number: index of the first highlighted character in the match
+ - Second number: index of the last highlighted character in the match *plus one*
+
+ This matches the format expected by the parse function
+ See https://github.com/moroshko/autosuggest-highlight?tab=readme-ov-file#parsetext-matches
+*/
+
+export type Match = [number, number];
+
+export function reverseMatches(suggestion: string, matches: Match[]): Match[] {
+ const suggestionLength = suggestion.length;
+ const reversedMatches: Match[] = [];
+
+ let currentStart = 0;
+
+ for (const [start, end] of matches) {
+ if (currentStart < start) {
+ reversedMatches.push([currentStart, start]);
+ }
+
+ currentStart = end;
+ }
+
+ if (currentStart < suggestionLength) {
+ reversedMatches.push([currentStart, suggestionLength]);
+ }
+
+ return reversedMatches;
+}