feat: add enhanced mentions#3189
Conversation
# Conflicts: # src/components/Message/__tests__/__snapshots__/MessageText.test.tsx.snap # src/components/Message/renderText/rehypePlugins/mentionsMarkdownPlugin.ts # src/components/Message/renderText/renderText.tsx
b154ab1 to
8bc5ceb
Compare
f59965d to
70241a5
Compare
# Conflicts: # src/components/Message/__tests__/__snapshots__/MessageText.test.tsx.snap
π WalkthroughWalkthroughThis PR introduces a reusable ListItemLayout component for polymorphic list rendering, expands the mention system to support channels, roles, here notifications, and user groups alongside users, and refactors suggestion list mention rendering with dedicated components for each mention type. Message rendering now passes enriched mention metadata to support more expressive mention display and matching. ChangesListItemLayout Component and Icon Infrastructure
Mention System Refactor and Message Integration
Suggestion List Component Refactoring
Internationalization Updates
π― 4 (Complex) | β±οΈ ~60 minutes Suggested reviewers
Poem
π₯ Pre-merge checks | β 4 | β 1β Failed checks (1 warning)
β Passed checks (4 passed)
βοΈ Tip: You can configure your own custom pre-merge checks in the settings. β¨ Finishing Touchesπ§ͺ Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 11
Caution
Some comments are outside the diff and canβt be posted inline due to platform limitations.
β οΈ Outside diff range comments (1)
src/components/Message/MessageText.tsx (1)
81-83:β οΈ Potential issue | π Major | β‘ Quick winGate mention keyboard interaction on all rendered mention entities, not just users.
renderTextMentionEntitiesnow includes channel/here/role/user-group mentions, butisMentionsInteractionEnabledstill keys offmessage.mentioned_usersonly. A message that contains only@channel,@here, a role, or a user group will render mention spans, yet the inner wrapper stays unfocusable and Enter/Space never reachesonMentionsClickMessage.Suggested change
- const hasMentionedUsers = Boolean(message.mentioned_users?.length); + const hasMentions = renderTextMentionEntities.length > 0; const isMentionsInteractionEnabled = - hasMentionedUsers && typeof onMentionsClickMessage === 'function'; + hasMentions && typeof onMentionsClickMessage === 'function';π€ Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/Message/MessageText.tsx` around lines 81 - 83, The keyboard-interaction gating currently uses hasMentionedUsers and thus misses non-user mentions; replace that check with a broader detection (e.g., hasMentionEntities) that returns true when any mention entity arrays exist or when renderTextMentionEntities would produce mention spans β check fields like message.mentioned_users, message.mentioned_roles, message.mentioned_groups, message.mentioned_channels, and any "everyone"/"here"/"channel" indicator (or fallback to examining the rendered entities output) and use that in isMentionsInteractionEnabled alongside typeof onMentionsClickMessage to ensure Enter/Space are focusable and routed to onMentionsClickMessage for all mention types referenced by renderTextMentionEntities.
π§Ή Nitpick comments (5)
src/components/TextareaComposer/SuggestionList/MentionItem/UserGroupItem.tsx (1)
17-17: β‘ Quick winRemove unnecessary void statement.
The
void focused;statement appears to be dead code sincefocusedis actually used on line 34 (selected={focused}). This statement serves no purpose and should be removed for clarity.β»οΈ Proposed fix
...buttonProps }: UserGroupItemProps) => { - void focused; - return (π€ Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/TextareaComposer/SuggestionList/MentionItem/UserGroupItem.tsx` at line 17, Remove the stray "void focused;" dead statement in UserGroupItem.tsx; the variable focused is actually used later (e.g., passed to selected={focused}) so simply delete that line to avoid the no-op and keep the code clear while leaving the focused variable usage intact.src/components/TextareaComposer/SuggestionList/TokenizedSuggestionParts.tsx (1)
13-31: β‘ Quick winConsider memoizing this component for better autocomplete performance.
TokenizedSuggestionPartswill re-render whenever its parent suggestion item re-renders during autocomplete typing. Since this is in the hot path of the suggestion dropdown, wrapping it withReact.memowould prevent unnecessary re-renders whentokenizedDisplayNamehasn't changed.β»οΈ Suggested memoization
-export const TokenizedSuggestionParts = ({ +export const TokenizedSuggestionParts = React.memo(({ tokenizedDisplayName, -}: TokenizedSuggestionPartsProps) => +}: TokenizedSuggestionPartsProps) => { + return ( + <> + {tokenizedDisplayName.parts.map((part, i) => { - tokenizedDisplayName.parts.map((part, i) => { const matches = part.toLowerCase() === tokenizedDisplayName.token; const partWithHTMLSpacesAround = part.replace(/^\s+|\s+$/g, '\u00A0'); return ( <span className={clsx({ 'str-chat__emoji-item-part': !matches, 'str-chat__suggestion-item-part--match': matches, })} key={`part-${i}`} > {partWithHTMLSpacesAround} </span> ); - }); + })} + </> + ); +});π€ Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/TextareaComposer/SuggestionList/TokenizedSuggestionParts.tsx` around lines 13 - 31, Wrap the TokenizedSuggestionParts functional component with React.memo so it only re-renders when its props change; specifically memoize the exported TokenizedSuggestionParts (which takes tokenizedDisplayName: TokenizedSuggestionPartsProps) to avoid re-rendering on parent updates during autocomplete and ensure the memo key uses the default shallow prop comparison (or provide a custom comparison that deeply compares tokenizedDisplayName if necessary).src/components/MessageComposer/__tests__/MessageInput.test.tsx (1)
1311-1379: β‘ Quick winCover the actual wraparound branch.
This test sets
hasNext: true, but it never moves past the last loaded suggestion, so it does not exercise the newnextIndex >= loadedItems.lengthpath insrc/components/TextareaComposer/TextareaComposer.tsxLine 209. Add one moreArrowDownfrom@hereand assert that selection wraps back to@channel; otherwise the changed boundary behavior can regress unnoticed.π€ Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/MessageComposer/__tests__/MessageInput.test.tsx` around lines 1311 - 1379, Test does not exercise the wraparound branch (nextIndex >= loadedItems.length) in TextareaComposer.tsx; update the MessageInput.test.tsx test so after selecting '`@here`' you fire one more ArrowDown key event to move past the last loaded suggestion, then assert the selection wraps to '`@channel`' (e.g., fire another fireEvent.keyDown(formElement, { key: 'ArrowDown' }) before confirming with Enter and expect formElement toHaveValue('`@channel` ')). Reference the suggestions/searchSource usage in this test and the nextIndex >= loadedItems.length behavior in TextareaComposer.tsx when adding the extra ArrowDown + assertion.src/components/MessageComposer/QuotedMessagePreview.tsx (1)
409-411: π€ Low valueConsider removing redundant
mentioned_usersparameter.The
renderTextcall passes bothquotedMessage?.mentioned_users(deprecated parameter) andmessageMentionEntities: quotedMessageMentionEntities(new option), wherequotedMessageMentionEntitiesalready includes the mentioned users. SincerenderTextderives entities frommessageMentionEntitieswhen present (line 149-151 in renderText.tsx), the deprecated parameter is unused here.β»οΈ Simplify by removing redundant parameter
- renderedText = renderText(quotedMessageText, quotedMessage?.mentioned_users, { + renderedText = renderText(quotedMessageText, undefined, { messageMentionEntities: quotedMessageMentionEntities, });π€ Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/MessageComposer/QuotedMessagePreview.tsx` around lines 409 - 411, The renderText call is passing a deprecated second argument quotedMessage?.mentioned_users while also supplying messageMentionEntities: quotedMessageMentionEntities in the options; because renderText prefers messageMentionEntities (see renderText), remove the redundant quotedMessage?.mentioned_users argument from the renderedText invocation and rely solely on the options object (messageMentionEntities: quotedMessageMentionEntities) so entities are derived from quotedMessageMentionEntities; update the call site in QuotedMessagePreview.tsx where renderedText = renderText(quotedMessageText, quotedMessage?.mentioned_users, { messageMentionEntities: quotedMessageMentionEntities }) to the single-argument form using the options object.src/components/Message/types.ts (1)
73-74: β‘ Quick winUpdate the
renderTextprop docs to match the new contract.This prop now exposes
RenderTextFunction, but the JSDoc still points to the old utils location and doesn't mention the newoptions.messageMentionEntitiespath or the deprecatedmentionedUsersargument. Please refresh the inline docs here, and any matching guide page if this prop is documented externally. As per coding guidelines,src/**/*.{ts,tsx}: Ensure public API changes include documentation updates.π€ Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/Message/types.ts` around lines 73 - 74, Update the JSDoc for the renderText?: RenderTextFunction prop to reflect the new contract: remove the old utils link, describe that the function receives (text, options) where mention data is now available at options.messageMentionEntities (and note the mentionedUsers argument is deprecated), and briefly document expected return values/behavior; update any matching external docs or guide pages that reference the old signature to use RenderTextFunction and the new options.messageMentionEntities path.
π€ Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/Avatar/__tests__/Avatar.test.tsx`:
- Around line 69-74: The test in Avatar.test.tsx expects the wrong CSS class for
custom fallback icons; update the assertion in the 'should render a custom
fallback icon when provided and no initials are available' case to look for the
class produced by createIcon (use 'str-chaticon--megaphone' for IconMegaphone)
instead of '.str-chat__icon--megaphone' so the check on the rendered Avatar (use
AVATAR_ROOT_TEST_ID and the FallbackIcon=IconMegaphone usage) matches the
createIcon naming convention.
In `@src/components/ListItemLayout/styling/ListItemLayout.scss`:
- Around line 3-10: The stylesheet is failing stylelint's
declaration-empty-line-before rule for the custom property and the later
declarations; add a blank line immediately before the flagged declarations to
satisfy the rule. Specifically, insert an empty line before the
--list-item-padding declaration in the .str-chat__list-item-layout selector and
likewise add an empty line before the other flagged declaration(s) (e.g., the
padding declaration around the same selector or at the later block near lines
38β40) so the rule no longer reports errors.
In `@src/components/Message/__tests__/MessageText.test.tsx`:
- Around line 377-405: Add assertions to the existing test ("renders built-in,
role, and user-group mentions with mention styling") to verify keyboard
accessibility: for each mention found via getByText('`@channel`'),
getByText('`@here`'), getByText('`@admin`'), and getByText('`@Backend` Team') locate
the surrounding mention wrapper (e.g.,
element.closest('.str-chat__message-mention') or the element returned by
renderMessageText) and assert it is focusable by checking it has tabindex="0"
(or element.tabIndex === 0); ensure these extra expect checks are added after
the current attribute assertions and before the axe accessibility check so the
regression for focus/Enter on non-user mentions is covered.
In `@src/components/Message/MessageText.tsx`:
- Around line 55-57: In MessageText.tsx, replace the incorrect use of
message.mentioned_groups with message.mentioned_group_ids when calling
getRenderTextMentionEntities and update the helper/types used by
getRenderTextMentionEntities to accept mentioned_group_ids (or map
mentioned_group_ids -> expected shape) so TypeScript no longer reports TS2551;
also ensure isMentionsInteractionEnabledβs gating logic is updated so mention
interaction is enabled for group mentions (not just mentioned_users) when those
entities are rendered.
In `@src/components/TextareaComposer/styling/SuggestionList.scss`:
- Around line 42-60: The SCSS violates stylelint's declaration-empty-line-before
rule: in .str-chat__suggestion-list__item-title and
.str-chat__suggestion-list__item-details, add a blank line before any property
declaration that immediately follows an `@include` (i.e., ensure there is an empty
line between the `@include` utils.ellipsis-text; and the next declaration such as
text-align: start; or color: ...), so update those selectors (and similarly
.str-chat__list-item-layout__title.str-chat__suggestion-list__mention-item-title
if needed) to insert the required empty lines after mixins to satisfy linting.
In `@src/components/TextareaComposer/SuggestionList/SuggestionList.tsx`:
- Around line 223-235: The current compatibility check uses the translated
string value via legacyUserSuggestionsLabel which relies on locale bundles;
instead update the mentions branch in the suggestionMenuLabel computation to
resolve the new key with the old key as a fallback by calling t('aria/Mention
Suggestions', { defaultValue: t('aria/User Suggestions') }), remove the runtime
comparison to legacyUserSuggestionsLabel, and keep the other branches that use
t('aria/Command Suggestions') and t('aria/Emoji Suggestions') unchanged so
suggestions.searchSource.type still drives selection.
In `@src/i18n/ja.json`:
- Line 144: Replace the English value for the JSON key "aria/User Suggestions"
with the correct Japanese translation (e.g., "γ¦γΌγΆγΌεθ£") so the value is in
Japanese (not "aria/User Suggestions"); update the entry for "aria/User
Suggestions" in the ja.json translations and then run yarn validate-translations
to ensure the file passes validation.
In `@src/i18n/ko.json`:
- Line 144: The translation for the JSON key "aria/User Suggestions" currently
uses the English key as its value; update the value to the correct Korean
translation (e.g., "μ¬μ©μ μ μ") for the key "aria/User Suggestions" in
src/i18n/ko.json, ensure the value is a non-empty Korean string consistent with
other aria entries (see "aria/Mention Suggestions"), and run yarn
validate-translations after the change.
In `@src/i18n/nl.json`:
- Line 145: The JSON entry for the translation key "aria/User Suggestions"
currently uses the English key as its value; replace that value with the correct
Dutch translation (e.g., "Gebruikerssuggesties") for the "aria/User Suggestions"
key in the nl.json translations so Dutch users see localized text, then run yarn
validate-translations to ensure all translations are valid.
In `@src/i18n/pt.json`:
- Line 153: Update the translation value for the JSON key "aria/User
Suggestions" so it is a Portuguese string (e.g., "SugestΓ΅es de usuΓ‘rios")
instead of the English key; locate the "aria/User Suggestions" entry in the
pt.json translations and replace the value with the correct Portuguese
translation, ensure the string is non-empty and matches style of other entries
like "aria/Mention Suggestions", then run `yarn validate-translations` to
confirm no validation errors.
In `@src/i18n/ru.json`:
- Line 162: The JSON key "aria/User Suggestions" has an untranslated value;
update its value to the correct Russian string (e.g., the localized aria label
for "User Suggestions") in the ru.json entry for "aria/User Suggestions", then
search other src/i18n/*.json files for any identical untranslated key/value
pairs and correct them as needed; finally run yarn validate-translations to
ensure all locale files pass validation.
---
Outside diff comments:
In `@src/components/Message/MessageText.tsx`:
- Around line 81-83: The keyboard-interaction gating currently uses
hasMentionedUsers and thus misses non-user mentions; replace that check with a
broader detection (e.g., hasMentionEntities) that returns true when any mention
entity arrays exist or when renderTextMentionEntities would produce mention
spans β check fields like message.mentioned_users, message.mentioned_roles,
message.mentioned_groups, message.mentioned_channels, and any
"everyone"/"here"/"channel" indicator (or fallback to examining the rendered
entities output) and use that in isMentionsInteractionEnabled alongside typeof
onMentionsClickMessage to ensure Enter/Space are focusable and routed to
onMentionsClickMessage for all mention types referenced by
renderTextMentionEntities.
---
Nitpick comments:
In `@src/components/Message/types.ts`:
- Around line 73-74: Update the JSDoc for the renderText?: RenderTextFunction
prop to reflect the new contract: remove the old utils link, describe that the
function receives (text, options) where mention data is now available at
options.messageMentionEntities (and note the mentionedUsers argument is
deprecated), and briefly document expected return values/behavior; update any
matching external docs or guide pages that reference the old signature to use
RenderTextFunction and the new options.messageMentionEntities path.
In `@src/components/MessageComposer/__tests__/MessageInput.test.tsx`:
- Around line 1311-1379: Test does not exercise the wraparound branch (nextIndex
>= loadedItems.length) in TextareaComposer.tsx; update the MessageInput.test.tsx
test so after selecting '`@here`' you fire one more ArrowDown key event to move
past the last loaded suggestion, then assert the selection wraps to '`@channel`'
(e.g., fire another fireEvent.keyDown(formElement, { key: 'ArrowDown' }) before
confirming with Enter and expect formElement toHaveValue('`@channel` ')).
Reference the suggestions/searchSource usage in this test and the nextIndex >=
loadedItems.length behavior in TextareaComposer.tsx when adding the extra
ArrowDown + assertion.
In `@src/components/MessageComposer/QuotedMessagePreview.tsx`:
- Around line 409-411: The renderText call is passing a deprecated second
argument quotedMessage?.mentioned_users while also supplying
messageMentionEntities: quotedMessageMentionEntities in the options; because
renderText prefers messageMentionEntities (see renderText), remove the redundant
quotedMessage?.mentioned_users argument from the renderedText invocation and
rely solely on the options object (messageMentionEntities:
quotedMessageMentionEntities) so entities are derived from
quotedMessageMentionEntities; update the call site in QuotedMessagePreview.tsx
where renderedText = renderText(quotedMessageText,
quotedMessage?.mentioned_users, { messageMentionEntities:
quotedMessageMentionEntities }) to the single-argument form using the options
object.
In
`@src/components/TextareaComposer/SuggestionList/MentionItem/UserGroupItem.tsx`:
- Line 17: Remove the stray "void focused;" dead statement in UserGroupItem.tsx;
the variable focused is actually used later (e.g., passed to selected={focused})
so simply delete that line to avoid the no-op and keep the code clear while
leaving the focused variable usage intact.
In `@src/components/TextareaComposer/SuggestionList/TokenizedSuggestionParts.tsx`:
- Around line 13-31: Wrap the TokenizedSuggestionParts functional component with
React.memo so it only re-renders when its props change; specifically memoize the
exported TokenizedSuggestionParts (which takes tokenizedDisplayName:
TokenizedSuggestionPartsProps) to avoid re-rendering on parent updates during
autocomplete and ensure the memo key uses the default shallow prop comparison
(or provide a custom comparison that deeply compares tokenizedDisplayName if
necessary).
πͺ Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
βΉοΈ Review info
βοΈ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: aea911be-dcf9-4f80-a8da-7398c0d0a4b0
β Files ignored due to path filters (1)
src/components/Message/renderText/__tests__/__snapshots__/renderText.test.tsx.snapis excluded by!**/*.snap
π Files selected for processing (51)
src/components/Avatar/Avatar.tsxsrc/components/Avatar/__tests__/Avatar.test.tsxsrc/components/Button/styling/Button.scsssrc/components/Icons/icons.tsxsrc/components/ListItemLayout/ListItemLayout.tsxsrc/components/ListItemLayout/__tests__/ListItemLayout.test.tsxsrc/components/ListItemLayout/index.tssrc/components/ListItemLayout/styling/ListItemLayout.scsssrc/components/ListItemLayout/styling/index.scsssrc/components/Message/MessageText.tsxsrc/components/Message/__tests__/MessageText.test.tsxsrc/components/Message/__tests__/QuotedMessage.test.tsxsrc/components/Message/renderText/__tests__/renderText.test.tsxsrc/components/Message/renderText/componentRenderers/Mention.tsxsrc/components/Message/renderText/rehypePlugins/mentionsMarkdownPlugin.tssrc/components/Message/renderText/renderText.tsxsrc/components/Message/types.tssrc/components/MessageComposer/QuotedMessagePreview.tsxsrc/components/MessageComposer/__tests__/MessageInput.test.tsxsrc/components/TextareaComposer/SuggestionList/MentionItem/BroadcastMentionItem.tsxsrc/components/TextareaComposer/SuggestionList/MentionItem/MentionItem.tsxsrc/components/TextareaComposer/SuggestionList/MentionItem/MentionSuggestionTitle.tsxsrc/components/TextareaComposer/SuggestionList/MentionItem/RoleItem.tsxsrc/components/TextareaComposer/SuggestionList/MentionItem/SpecialMentionItem.tsxsrc/components/TextareaComposer/SuggestionList/MentionItem/UserGroupItem.tsxsrc/components/TextareaComposer/SuggestionList/MentionItem/UserItem.tsxsrc/components/TextareaComposer/SuggestionList/MentionItem/index.tssrc/components/TextareaComposer/SuggestionList/MentionItem/types.tssrc/components/TextareaComposer/SuggestionList/SuggestionList.tsxsrc/components/TextareaComposer/SuggestionList/SuggestionListItem.tsxsrc/components/TextareaComposer/SuggestionList/TokenizedSuggestionParts.tsxsrc/components/TextareaComposer/SuggestionList/UserItem.tsxsrc/components/TextareaComposer/SuggestionList/index.tssrc/components/TextareaComposer/TextareaComposer.tsxsrc/components/TextareaComposer/__tests__/MentionItem.test.tsxsrc/components/TextareaComposer/styling/SuggestionList.scsssrc/components/index.tssrc/context/MessageContext.tsxsrc/i18n/de.jsonsrc/i18n/en.jsonsrc/i18n/es.jsonsrc/i18n/fr.jsonsrc/i18n/hi.jsonsrc/i18n/it.jsonsrc/i18n/ja.jsonsrc/i18n/ko.jsonsrc/i18n/nl.jsonsrc/i18n/pt.jsonsrc/i18n/ru.jsonsrc/i18n/tr.jsonsrc/styling/index.scss
π€ Files with no reviewable changes (1)
- src/components/TextareaComposer/SuggestionList/UserItem.tsx
| .str-chat__list-item-layout { | ||
| --list-item-padding: var(--str-chat__spacing-xs) var(--str-chat__spacing-sm); | ||
| display: flex; | ||
| align-items: center; | ||
| gap: var(--str-chat__spacing-sm); | ||
| text-align: start; | ||
| padding: var(--list-item-padding); | ||
| width: 100%; |
There was a problem hiding this comment.
Fix the current Stylelint failures in this file.
Lines 5 and 40 are already flagged by declaration-empty-line-before, so this stylesheet will not pass lint as written.
Suggested change
.str-chat__list-item-layout {
--list-item-padding: var(--str-chat__spacing-xs) var(--str-chat__spacing-sm);
+
display: flex;
align-items: center;
gap: var(--str-chat__spacing-sm);
@@
&:is(button) {
`@include` utils.button-reset;
+
padding: var(--list-item-padding);
cursor: pointer;Also applies to: 38-40
π§° Tools
πͺ Stylelint (17.12.0)
[error] 5-5: Expected empty line before declaration (declaration-empty-line-before)
(declaration-empty-line-before)
π€ Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/ListItemLayout/styling/ListItemLayout.scss` around lines 3 -
10, The stylesheet is failing stylelint's declaration-empty-line-before rule for
the custom property and the later declarations; add a blank line immediately
before the flagged declarations to satisfy the rule. Specifically, insert an
empty line before the --list-item-padding declaration in the
.str-chat__list-item-layout selector and likewise add an empty line before the
other flagged declaration(s) (e.g., the padding declaration around the same
selector or at the later block near lines 38β40) so the rule no longer reports
errors.
| it('renders built-in, role, and user-group mentions with mention styling', async () => { | ||
| const text = 'Hello @channel @here @admin @Backend Team'; | ||
| const message = generateAliceMessage({ | ||
| mentioned_channel: true, | ||
| mentioned_groups: [ | ||
| fromPartial({ | ||
| created_at: '2026-05-28T00:00:00.000Z', | ||
| id: 'backend-team', | ||
| name: 'Backend Team', | ||
| updated_at: '2026-05-28T00:00:00.000Z', | ||
| }), | ||
| ], | ||
| mentioned_here: true, | ||
| mentioned_roles: ['admin'], | ||
| text, | ||
| }); | ||
| const { container, getByText } = await renderMessageText({ | ||
| customProps: { message }, | ||
| }); | ||
|
|
||
| expect(getByText('@channel')).toHaveAttribute('data-mention-type', 'channel'); | ||
| expect(getByText('@here')).toHaveAttribute('data-mention-type', 'here'); | ||
| expect(getByText('@admin')).toHaveAttribute('data-mention-type', 'role'); | ||
| expect(getByText('@Backend Team')).toHaveAttribute('data-mention-type', 'user_group'); | ||
| expect(container.querySelectorAll('.str-chat__message-mention')).toHaveLength(4); | ||
|
|
||
| const results = await axe(container); | ||
| expect(results).toHaveNoViolations(); | ||
| }); |
There was a problem hiding this comment.
Extend this regression to cover keyboard accessibility for non-user mentions.
This test proves the spans render, but it won't catch the current focus/Enter regression for messages that only contain @channel/role/group mentions. Please assert the inner wrapper becomes focusable for this payload so the accessibility path stays covered.
π€ Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/Message/__tests__/MessageText.test.tsx` around lines 377 -
405, Add assertions to the existing test ("renders built-in, role, and
user-group mentions with mention styling") to verify keyboard accessibility: for
each mention found via getByText('`@channel`'), getByText('`@here`'),
getByText('`@admin`'), and getByText('`@Backend` Team') locate the surrounding
mention wrapper (e.g., element.closest('.str-chat__message-mention') or the
element returned by renderMessageText) and assert it is focusable by checking it
has tabindex="0" (or element.tabIndex === 0); ensure these extra expect checks
are added after the current attribute assertions and before the axe
accessibility check so the regression for focus/Enter on non-user mentions is
covered.
| .str-chat__suggestion-list__item-title { | ||
| @include fonts.text-caption-default; | ||
| @include utils.ellipsis-text; | ||
| text-align: start; | ||
| min-width: 0; | ||
| } | ||
|
|
||
| .str-chat__list-item-layout__title.str-chat__suggestion-list__mention-item-title { | ||
| font: var(--str-chat__font-body-default); | ||
| } | ||
|
|
||
| .str-chat__suggestion-list__item-details { | ||
| @include fonts.text-metadata-default; | ||
| @include utils.ellipsis-text; | ||
| color: var(--str-chat__text-tertiary); | ||
| text-align: start; | ||
| min-width: 0; | ||
| } | ||
| } |
There was a problem hiding this comment.
Fix stylelint violations: add empty lines before declarations following mixins.
The declaration-empty-line-before rule requires an empty line before declarations that follow @include statements. Lines 45 and 56 violate this rule.
π¨ Proposed fix
.str-chat__suggestion-list__item-title {
`@include` fonts.text-caption-default;
`@include` utils.ellipsis-text;
+
text-align: start;
min-width: 0;
}
.str-chat__list-item-layout__title.str-chat__suggestion-list__mention-item-title {
font: var(--str-chat__font-body-default);
}
.str-chat__suggestion-list__item-details {
`@include` fonts.text-metadata-default;
`@include` utils.ellipsis-text;
+
color: var(--str-chat__text-tertiary);
text-align: start;
min-width: 0;
}As per coding guidelines, the project follows a 'zero warnings' policyβfix new warnings and avoid introducing any.
π§° Tools
πͺ Stylelint (17.12.0)
[error] 45-45: Expected empty line before declaration (declaration-empty-line-before)
(declaration-empty-line-before)
[error] 56-56: Expected empty line before declaration (declaration-empty-line-before)
(declaration-empty-line-before)
π€ Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/TextareaComposer/styling/SuggestionList.scss` around lines 42
- 60, The SCSS violates stylelint's declaration-empty-line-before rule: in
.str-chat__suggestion-list__item-title and
.str-chat__suggestion-list__item-details, add a blank line before any property
declaration that immediately follows an `@include` (i.e., ensure there is an empty
line between the `@include` utils.ellipsis-text; and the next declaration such as
text-align: start; or color: ...), so update those selectors (and similarly
.str-chat__list-item-layout__title.str-chat__suggestion-list__mention-item-title
if needed) to insert the required empty lines after mixins to satisfy linting.
| "aria/Unmute User": "η‘ι³γθ§£ι€γγ", | ||
| "aria/Unpin Message": "γγ³γθ§£ι€", | ||
| "aria/User Suggestions": "γ¦γΌγΆγΌεθ£", | ||
| "aria/User Suggestions": "aria/User Suggestions", |
There was a problem hiding this comment.
Translation value must be in Japanese, not the English key.
The value for aria/User Suggestions should be the Japanese translation (likely γ¦γΌγΆγΌεθ£), not the literal string aria/User Suggestions. This will cause English text to be displayed to Japanese users, breaking the i18n contract. Compare with line 97 where aria/Mention Suggestions is properly translated to γ‘γ³γ·γ§γ³εθ£.
π Proposed fix
- "aria/User Suggestions": "aria/User Suggestions",
+ "aria/User Suggestions": "γ¦γΌγΆγΌεθ£",As per coding guidelines, ensure all translation values are non-empty strings in the target language and run yarn validate-translations after fixing.
π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "aria/User Suggestions": "aria/User Suggestions", | |
| "aria/User Suggestions": "γ¦γΌγΆγΌεθ£", |
π€ Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/i18n/ja.json` at line 144, Replace the English value for the JSON key
"aria/User Suggestions" with the correct Japanese translation (e.g., "γ¦γΌγΆγΌεθ£")
so the value is in Japanese (not "aria/User Suggestions"); update the entry for
"aria/User Suggestions" in the ja.json translations and then run yarn
validate-translations to ensure the file passes validation.
| "aria/Unmute User": "μμκ±° ν΄μ ", | ||
| "aria/Unpin Message": "ν ν΄μ ", | ||
| "aria/User Suggestions": "μ¬μ©μ μ μ", | ||
| "aria/User Suggestions": "aria/User Suggestions", |
There was a problem hiding this comment.
Translation value must be in Korean, not the English key.
The value for aria/User Suggestions should be the Korean translation (likely μ¬μ©μ μ μ), not the literal string aria/User Suggestions. This will cause English text to be displayed to Korean users. Compare with line 97 where aria/Mention Suggestions is properly translated to λ©μ
μ μ.
π Proposed fix
- "aria/User Suggestions": "aria/User Suggestions",
+ "aria/User Suggestions": "μ¬μ©μ μ μ",As per coding guidelines, ensure all translation values are non-empty strings in the target language and run yarn validate-translations after fixing.
π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "aria/User Suggestions": "aria/User Suggestions", | |
| "aria/User Suggestions": "μ¬μ©μ μ μ", |
π€ Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/i18n/ko.json` at line 144, The translation for the JSON key "aria/User
Suggestions" currently uses the English key as its value; update the value to
the correct Korean translation (e.g., "μ¬μ©μ μ μ") for the key "aria/User
Suggestions" in src/i18n/ko.json, ensure the value is a non-empty Korean string
consistent with other aria entries (see "aria/Mention Suggestions"), and run
yarn validate-translations after the change.
| "aria/Unmute User": "Dempen opheffen", | ||
| "aria/Unpin Message": "Losmaken", | ||
| "aria/User Suggestions": "Gebruikerssuggesties", | ||
| "aria/User Suggestions": "aria/User Suggestions", |
There was a problem hiding this comment.
Translation value must be in Dutch, not the English key.
The value for aria/User Suggestions should be the Dutch translation (likely Gebruikerssuggesties), not the literal string aria/User Suggestions. This will cause English text to be displayed to Dutch users. Compare with line 98 where aria/Mention Suggestions is properly translated to Vermeldingssuggesties.
π Proposed fix
- "aria/User Suggestions": "aria/User Suggestions",
+ "aria/User Suggestions": "Gebruikerssuggesties",As per coding guidelines, ensure all translation values are non-empty strings in the target language and run yarn validate-translations after fixing.
π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "aria/User Suggestions": "aria/User Suggestions", | |
| "aria/User Suggestions": "Gebruikerssuggesties", |
π€ Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/i18n/nl.json` at line 145, The JSON entry for the translation key
"aria/User Suggestions" currently uses the English key as its value; replace
that value with the correct Dutch translation (e.g., "Gebruikerssuggesties") for
the "aria/User Suggestions" key in the nl.json translations so Dutch users see
localized text, then run yarn validate-translations to ensure all translations
are valid.
| "aria/Unmute User": "Ativar som", | ||
| "aria/Unpin Message": "Desfixar mensagem", | ||
| "aria/User Suggestions": "SugestΓ΅es de usuΓ‘rios", | ||
| "aria/User Suggestions": "aria/User Suggestions", |
There was a problem hiding this comment.
Translation value must be in Portuguese, not the English key.
The value for aria/User Suggestions should be the Portuguese translation (likely SugestΓ΅es de usuΓ‘rios), not the literal string aria/User Suggestions. This will cause English text to be displayed to Portuguese users. Compare with line 106 where aria/Mention Suggestions is properly translated to SugestΓ΅es de menΓ§Γ΅es.
π Proposed fix
- "aria/User Suggestions": "aria/User Suggestions",
+ "aria/User Suggestions": "SugestΓ΅es de usuΓ‘rios",As per coding guidelines, ensure all translation values are non-empty strings in the target language and run yarn validate-translations after fixing.
π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "aria/User Suggestions": "aria/User Suggestions", | |
| "aria/User Suggestions": "SugestΓ΅es de usuΓ‘rios", |
π€ Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/i18n/pt.json` at line 153, Update the translation value for the JSON key
"aria/User Suggestions" so it is a Portuguese string (e.g., "SugestΓ΅es de
usuΓ‘rios") instead of the English key; locate the "aria/User Suggestions" entry
in the pt.json translations and replace the value with the correct Portuguese
translation, ensure the string is non-empty and matches style of other entries
like "aria/Mention Suggestions", then run `yarn validate-translations` to
confirm no validation errors.
| "aria/Unmute User": "ΠΠΊΠ»ΡΡΠΈΡΡ ΡΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΡ", | ||
| "aria/Unpin Message": "ΠΡΠΊΡΠ΅ΠΏΠΈΡΡ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅", | ||
| "aria/User Suggestions": "ΠΠΎΠ΄ΡΠΊΠ°Π·ΠΊΠΈ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Π΅ΠΉ", | ||
| "aria/User Suggestions": "aria/User Suggestions", |
There was a problem hiding this comment.
π§© Analysis chain
π Script executed:
#!/bin/bash
# Description: Check all locale files for aria/User Suggestions values
rg -n '"aria/User Suggestions":\s*"aria/User Suggestions"' 'src/i18n/*.json'Repository: GetStream/stream-chat-react
Length of output: 1923
π Script executed:
rg -n '"aria/User Suggestions":\s*"aria/User Suggestions"' src/i18n/*.json
yarn validate-translationsRepository: GetStream/stream-chat-react
Length of output: 1942
π Script executed:
rg -n '"aria/User Suggestions":\s*"aria/User Suggestions"' src/i18n/*.json
yarn validate-translationsRepository: GetStream/stream-chat-react
Length of output: 1942
π Script executed:
rg -n '"aria/User Suggestions":\s*"aria/User Suggestions"' src/i18n/*.json
yarn validate-translationsRepository: GetStream/stream-chat-react
Length of output: 1942
Restore proper Russian translation for "aria/User Suggestions" (src/i18n/ru.json:162)
"aria/User Suggestions" maps to the literal string "aria/User Suggestions", so Russian users will hear an untranslated aria label instead of localized text.
Replace the value with the intended Russian string, then check other src/i18n/*.json locales for the same pattern and run yarn validate-translations.
π€ Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/i18n/ru.json` at line 162, The JSON key "aria/User Suggestions" has an
untranslated value; update its value to the correct Russian string (e.g., the
localized aria label for "User Suggestions") in the ru.json entry for "aria/User
Suggestions", then search other src/i18n/*.json files for any identical
untranslated key/value pairs and correct them as needed; finally run yarn
validate-translations to ensure all locale files pass validation.
There was a problem hiding this comment.
Actionable comments posted: 1
π€ Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@package.json`:
- Line 105: Update the unsatisfiable "stream-chat" version in package.json (both
the peerDependencies and devDependencies entries) to a published release (e.g.,
change "^9.45.0" to a known published version such as "^9.44.2"), then run
npm/yarn install to verify resolution and re-check that the chosen published
version contains the enhanced mentions API required by the dependent
stream-chat-js change; update both occurrences of the "stream-chat" dependency
to the same valid version.
πͺ Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
βΉοΈ Review info
βοΈ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 96584f4f-5683-48f0-ab89-86029b2f3b5b
β Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
π Files selected for processing (2)
.yarnrc.ymlpackage.json
| "react": "^19.0.0 || ^18.0.0 || ^17.0.0", | ||
| "react-dom": "^19.0.0 || ^18.0.0 || ^17.0.0", | ||
| "stream-chat": "^9.44.2" | ||
| "stream-chat": "^9.45.0" |
There was a problem hiding this comment.
π§© Analysis chain
π Web query:
stream-chat npm package version 9.45.0 release notes
π‘ Result:
As of June 4, 2026, version 9.45.0 of the stream-chat npm package has not been released. The latest available versions in the stream-chat series are 9.44.0 through 9.44.2, which were published in May 2026 [1][2]. You can monitor the official GitHub repository for future release notes and version updates [3][4].
Citations:
- 1: https://registry.npmjs.org/stream-chat
- 2: https://www.npmjs.com/package/stream-chat?activeTab=code
- 3: https://github.com/GetStream/stream-chat-js/releases
- 4: https://github.com/GetStream/stream-chat-js/blob/master/CHANGELOG.md
Fix stream-chat dependency version: ^9.45.0 isnβt published.
In package.json (peerDependencies/devDependencies, lines 105 and 175), stream-chat is set to ^9.45.0, but the npm registry shows 9.45.0 has not been released (latest is 9.44.0β9.44.2). This will make the dependency unsatisfiable; update to a published version and then re-check whether it includes the enhanced mentions API from the dependent stream-chat-js PR.
π€ Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@package.json` at line 105, Update the unsatisfiable "stream-chat" version in
package.json (both the peerDependencies and devDependencies entries) to a
published release (e.g., change "^9.45.0" to a known published version such as
"^9.44.2"), then run npm/yarn install to verify resolution and re-check that the
chosen published version contains the enhanced mentions API required by the
dependent stream-chat-js change; update both occurrences of the "stream-chat"
dependency to the same valid version.
π― Goal
Depends on: GetStream/stream-chat-js#1743
Closes REACT-979
This PR brings enhanced mentions support to
stream-chat-reactacross both the composer UI and rendered message text.It updates the
@suggestion flow to render a mixed mention set fromstream-chat-js, including direct users, built-in mentions like@channeland@here, roles, and user groups. The suggestion list now uses dedicated row components for each mention type, keeps keyboard navigation working across non-user items, and preserves accessibility and i18n behavior for the mixed result set.It also extends the message text rendering pipeline so enhanced mentions are highlighted the same way direct user mentions are. The
renderTextpath now accepts additive mention metadata, routes all mention kinds through a unifiedmentionrenderer contract vianode.mentionedEntity, and keeps backward compatibility for older user-only mention consumers through the deprecatednode.mentionedUseralias and the existingrenderText(text, mentioned_users, options)signature.Finally, the PR adds focused regression coverage for render-text behavior, quoted-message rendering, backward-compatible plugin usage, email-like mention edge cases, and multi-word mention names.
Highlights
@suggestion results fromstream-chat-js:@channel@hereMentionItemSpecialMentionItemRoleItemUserGroupItemUserItembackward compatibility for existing consumersrenderTextto support additive mention metadata throughoptions.messageMentionEntitiesmention+node.mentionedEntitynode.mentionedUsermentionsMarkdownPlugin(UserResponse[])renderText(text, mentioned_users, options)Summary by CodeRabbit
New Features
@here), roles, and user groups alongside user mentionsStyling
Localization