Skip to content

Add grouped suggestions#222

Open
hbmartin wants to merge 2 commits into
masterfrom
Add-grouped-suggestions
Open

Add grouped suggestions#222
hbmartin wants to merge 2 commits into
masterfrom
Add-grouped-suggestions

Conversation

@hbmartin
Copy link
Copy Markdown
Owner

@hbmartin hbmartin commented Apr 22, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for grouped suggestions: providers can now return suggestions organized into labeled sections
    • Section headers display as non-selectable headers while maintaining keyboard navigation
    • Added example component demonstrating grouped async suggestions with filtering
  • Documentation

    • Updated README documenting grouped suggestions feature and migration information
  • Chores

    • Updated development dependencies

Note

Add grouped suggestions support to MentionsInput

  • Data providers can now return sections (grouped results with labels) instead of a flat items array via the updated MentionDataPage discriminated union type in types.ts.
  • SuggestionsOverlay renders non-focusable section headers between items using the new flattenSuggestionRenderEntries utility; keyboard navigation and selection index only the actual items.
  • Section class names (suggestionSection, suggestionSectionLabel) are added to MentionsInputClassNames and passed through from MentionsInput to the overlay for styling.
  • Query state and pagination logic in MentionsInputQueryState.ts and useSuggestionsQuery.ts merge and preserve section metadata across pages and loading states.
  • Behavioral Change: getSuggestionsLayoutKey now incorporates section structure, which may trigger additional overlay rerenders when sections change even if items are unchanged.

Macroscope summarized 341b750.

Key changes:

Added MentionDataSection and MentionDataPage.sections support in types.ts.
Normalized grouped pages into flat selectable items plus section metadata.
Preserved flat keyboard/focus indexes while rendering non-focusable section headers in SuggestionsOverlay.tsx.
Added pagination section merging by stable section identity.
Added class slots: suggestionSection, suggestionSectionLabel.
Added tests, README docs, and a new demo: GroupedSuggestions.tsx.
Copilot AI review requested due to automatic review settings April 22, 2026 15:25
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 22, 2026

📝 Walkthrough

Walkthrough

This PR adds support for grouped/sectioned mention suggestions. It introduces new types for section-based mention data, updates rendering and state management to display section headers while treating sections as non-focusable, and extends helper utilities to flatten and normalize sectioned results alongside pagination merging. Documentation and demo examples accompany the changes.

Changes

Cohort / File(s) Summary
Type System & Re-exports
src/types.ts, src/index.ts
Added new exported types MentionDataSection and SuggestionSection; updated MentionDataPage to a union type supporting either flat items or grouped sections; extended NormalizedMentionDataPage, SuggestionQueryState, SuggestionsMap with optional sections arrays; added suggestionSection and suggestionSectionLabel to MentionsInputClassNames.
Rendering & Layout
src/SuggestionsOverlay.tsx, src/SuggestionsOverlay.spec.tsx, src/Suggestion.tsx, src/MentionsInput.tsx
Updated SuggestionsOverlay to render section headers (non-focusable role="presentation") alongside items using new sectionClassName/sectionLabelClassName props; added data-suggestion-index attribute to suggestion items; modified focus/scroll lookup to use querySelector instead of children indexing; added test coverage verifying section rendering and focus behavior with grouped data.
Data Flattening & Normalization
src/utils/flattenSuggestions.ts, src/utils/flattenSuggestions.spec.tsx, src/utils/index.ts, src/MentionsInputSelectors.ts, src/MentionsInputSelectors.spec.tsx
Introduced flattenSuggestionRenderEntries to emit section header and item entries separately while preserving contiguous item indices; refactored flattenSuggestions to use shared ordering helper; extended normalizeMentionDataResult to detect and convert sections into normalized structures with stable section keys; added test coverage for section normalization and render entry flattening.
State Management & Query Handling
src/MentionsInputQueryState.ts, src/MentionsInputQueryState.spec.ts, src/useSuggestionsQuery.ts
Added section merging logic for paginated results: subsequent pages merge sections by key identity while concatenating matching section results; updated applySuccessfulQueryResult and applySuccessfulPageResult to preserve and merge sections; updated loading state and suggestion preservation to conditionally carry forward sections; added comprehensive test coverage for section merging and pagination.
Demo & Documentation
README.md, demo/src/examples/GroupedSuggestions.tsx, demo/src/examples/Examples.tsx, demo/src/examples/mentionsClassNames.ts
Added new grouped suggestions example component with static user/team datasets, async filtering provider using MentionDataPage with sections, and custom styling; updated README with grouped suggestions capability description and data shape example; added section styling configuration to demo class names.
Testing
tests/MentionsInput.spec.tsx
Added integration test verifying grouped async suggestion rendering, selection behavior via keyboard navigation, and correct onMentionsChange output for grouped items.
Dependencies
package.json
Updated Vitest tooling (@vitest/browser-*, @vitest/coverage-v8, vitest) from ^4.1.4^4.1.5 and knip from ^6.5.0^6.6.0.

Sequence Diagram

sequenceDiagram
    participant User as User
    participant MentionsInput
    participant Provider as Async Provider
    participant Selectors as Normalization
    participant Overlay as SuggestionsOverlay
    participant Handler as Selection Handler

    User->>MentionsInput: Type `@query`
    MentionsInput->>Provider: fetchGroupedDirectory(query)
    Provider-->>MentionsInput: { sections: [{ label, items }] }
    MentionsInput->>Selectors: normalizeMentionDataResult(page)
    Selectors-->>MentionsInput: { items: [...flattened], sections: [...normalized] }
    MentionsInput->>Overlay: Pass suggestions with sections
    Overlay->>Overlay: flattenSuggestionRenderEntries(suggestions)
    Overlay-->>Overlay: [SectionHeader, Item0, Item1, SectionHeader, Item2]
    Overlay->>User: Render section labels (non-focusable)<br/>+ selectable items
    User->>Overlay: ArrowDown to Item1
    Overlay->>Handler: onMouseEnter(suggestionIndex: 1)
    User->>Overlay: Enter to select Item1
    Overlay->>Handler: onSelect(item, queryInfo)
    Handler->>MentionsInput: Update mention
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add grouped suggestions' directly and concisely describes the main feature addition in the changeset: support for grouped/sectioned mention suggestions.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch Add-grouped-suggestions

Comment @coderabbitai help to get the list of available commands and usage tips.

@socket-security
Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatedvitest@​4.1.4 ⏵ 4.1.596 +110079 +199 +2100
Updated@​vitest/​coverage-v8@​4.1.4 ⏵ 4.1.5991007999 +1100
Updated@​vitest/​browser-preview@​4.1.4 ⏵ 4.1.5951008499 +1100
Updated@​vitest/​browser-playwright@​4.1.4 ⏵ 4.1.5991008699 +2100
Updatedknip@​6.5.0 ⏵ 6.6.09910095 +196100

View full report

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 22, 2026

Codecov Report

❌ Patch coverage is 87.50000% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.45%. Comparing base (0e9f941) to head (341b750).
⚠️ Report is 21 commits behind head on master.

Files with missing lines Patch % Lines
src/MentionsInputQueryState.ts 77.77% 4 Missing and 2 partials ⚠️
src/MentionsInputSelectors.ts 84.00% 1 Missing and 3 partials ⚠️
src/useSuggestionsQuery.ts 33.33% 0 Missing and 2 partials ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #222      +/-   ##
==========================================
- Coverage   97.90%   97.45%   -0.45%     
==========================================
  Files          58       58              
  Lines        2191     2279      +88     
  Branches      568      599      +31     
==========================================
+ Hits         2145     2221      +76     
- Misses         10       15       +5     
- Partials       36       43       +7     
Files with missing lines Coverage Δ
src/MentionsInput.tsx 96.26% <ø> (ø)
src/Suggestion.tsx 97.22% <ø> (ø)
src/SuggestionsOverlay.tsx 100.00% <100.00%> (ø)
src/types.ts 100.00% <ø> (ø)
src/utils/flattenSuggestions.ts 100.00% <100.00%> (ø)
src/useSuggestionsQuery.ts 95.69% <33.33%> (-1.03%) ⬇️
src/MentionsInputSelectors.ts 96.81% <84.00%> (-1.65%) ⬇️
src/MentionsInputQueryState.ts 92.94% <77.77%> (-7.06%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for grouped suggestion sections, allowing data providers to return categorized lists (e.g., Users and Teams) under a single trigger. The implementation includes updates to the internal state management to handle section merging during pagination, a new flattening utility to interleave non-selectable section headers with selectable items, and enhanced DOM selection logic to ensure keyboard navigation remains accurate despite the non-flat list structure. Additionally, new styling properties for sections have been added to the component interface, accompanied by comprehensive unit and integration tests. I have no feedback to provide.

@hbmartin
Copy link
Copy Markdown
Owner Author

Check compatibility with signavio/react-mentions#787

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class support for grouped suggestions (section headers + items) across the provider API, normalization/query state, overlay rendering, demo, and documentation.

Changes:

  • Extend provider result types to allow returning sections (Users/Teams-style grouping) and carry section metadata through suggestion state.
  • Render section headers in SuggestionsOverlay while keeping keyboard navigation and selectable indexes counting only actual suggestion items.
  • Add/adjust tests, demo example, and docs; bump Vitest/Knip dev deps.

Reviewed changes

Copilot reviewed 20 out of 21 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/MentionsInput.spec.tsx Adds an integration test for grouped async suggestions and keyboard selection behavior.
src/utils/index.ts Re-exports flattenSuggestionRenderEntries for overlay rendering.
src/utils/flattenSuggestions.ts Adds section-aware “render entries” flattening (headers + items) while preserving selectable item indexing.
src/utils/flattenSuggestions.spec.tsx Tests section header emission and index sequencing.
src/useSuggestionsQuery.ts Preserves sections through loading/preserved suggestion states.
src/types.ts Introduces MentionDataSection / SuggestionSection and updates MentionDataPage / suggestion state types for grouping.
src/index.ts Exports the new public MentionDataSection type.
src/SuggestionsOverlay.tsx Renders section headers and updates scroll-to-focused logic to avoid counting headers as options.
src/SuggestionsOverlay.spec.tsx Tests grouped section header rendering and index stability.
src/Suggestion.tsx Adds data-suggestion-index attribute for testing/diagnostics.
src/MentionsInputSelectors.ts Normalizes provider sections into flat items + section metadata and includes sections in layout keys.
src/MentionsInputSelectors.spec.tsx Updates layout-key expectations and tests section normalization.
src/MentionsInputQueryState.ts Stores/merges section metadata across query + pagination results.
src/MentionsInputQueryState.spec.ts Adds tests for section persistence and section merging across pagination.
src/MentionsInput.tsx Wires new section classNames through to the overlay.
pnpm-lock.yaml Updates lockfile for bumped dev dependencies.
package.json Bumps Vitest-related packages and Knip.
demo/src/examples/mentionsClassNames.ts Adds section-related classNames for the demo styles.
demo/src/examples/GroupedSuggestions.tsx New demo example showing grouped suggestions provider usage.
demo/src/examples/Examples.tsx Registers the new grouped suggestions demo card.
README.md Documents grouped suggestions usage and adds it to the demo/features list.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 264 to 275
const previousResults = Object.hasOwn(currentSuggestions, childIndex)
? currentSuggestions[childIndex].results
: []
const previousSections = Object.hasOwn(currentSuggestions, childIndex)
? currentSuggestions[childIndex].sections
: undefined
const results = [...previousResults, ...page.items]
const sections = mergeSuggestionSections(previousSections, page.sections)
const suggestions: SuggestionsMap<Extra> = {
...currentSuggestions,
[childIndex]: {
queryInfo,
results,
},
[childIndex]: createSuggestionsEntry(queryInfo, results, sections),
}
Comment thread src/types.ts
Comment on lines +71 to +78
| (MentionDataPageBase & {
items: ReadonlyArray<MentionDataItem<Extra>>
sections?: never
})
| (MentionDataPageBase & {
items?: never
sections: ReadonlyArray<MentionDataSection<Extra>>
})
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/SuggestionsOverlay.tsx (1)

273-279: 🧹 Nitpick | 🔵 Trivial

Avoid flattening the suggestion tree twice in this hot path.

flattenSuggestions and flattenSuggestionRenderEntries both walk the same suggestion map, but the first result is only used for length > 0. Derive that boolean from the render entries instead so overlay rendering stays to a single traversal.

As per coding guidelines "Preserve render locality: move state and derived work into the smallest branch that consumes it, and do not reparse mention children/config in hot render paths".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/SuggestionsOverlay.tsx` around lines 273 - 279, The code currently calls
flattenSuggestions and flattenSuggestionRenderEntries, doing two traversals;
remove the flattenSuggestions use in this hot path and derive the "has
suggestions" boolean from flattenedSuggestionRenderEntries instead: eliminate
the useMemo that computes flattenedSuggestions (and any references to
flattenedSuggestions.length > 0), update the overlay rendering logic to use
flattenedSuggestionRenderEntries (from
flattenSuggestionRenderEntries(mentionChildren, suggestions)) to determine
emptiness and render accordingly, and keep flattenSuggestions only if elsewhere
referenced; adjust any variable names/usages so only one traversal over
mentionChildren/suggestions occurs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/MentionsInputSelectors.spec.tsx`:
- Around line 711-717: The test constructs a MentionDataSection object named
publicSection with an unused items property which trips
unicorn/no-unused-properties; either remove the unused items key when creating
publicSection or use it in an assertion—e.g., construct publicSection = { label:
'Public API section' } only, or add an assertion against publicSection.items
(like expect(publicSection.items).toEqual([])) so the items property is
referenced; update the test around the publicSection declaration/assertions
accordingly.

In `@src/MentionsInputSelectors.ts`:
- Around line 94-100: The fallback key for sections that lack `id` can collide
when two sections share the same string `label`; update the label branch in
MentionsInputSelectors to include a disambiguator (e.g., append the
`sectionIndex` or a stable hash) so keys become unique per-section: when
`section.id` is undefined, return a string that includes `section.label` plus
`sectionIndex` (or another stable unique token) instead of only
`label:${section.label}`; ensure you use `String(sectionIndex)` or similar to
produce a stable string key so downstream code in flattenSuggestions and
MentionsInputQueryState no longer sees duplicate keys.

In `@src/utils/flattenSuggestions.ts`:
- Around line 91-146: The flattenSuggestionRenderEntries function is too complex
for the linter; extract the inner "process sections" and "process unsectioned
results" logic into two small helpers (e.g., pushSectionEntries and
pushUnsectionedResults) to reduce cognitive complexity. Each helper should
accept the flattened array, the entry/queryInfo, the childIndex or sectionKey as
needed, the results or section.results, the shared handledResults Set, and the
current suggestionIndex and return the updated suggestionIndex (or mutate a
passed object) so suggestionIndex is preserved; ensure keys use the same formats
(sectionKey-item-... and childIndex-item-...), keep use of
getSuggestionId(result), and keep the original types
(FlattenedSuggestionRenderEntry, SuggestionDataItem, SuggestionsMap) so behavior
and types remain unchanged.

---

Outside diff comments:
In `@src/SuggestionsOverlay.tsx`:
- Around line 273-279: The code currently calls flattenSuggestions and
flattenSuggestionRenderEntries, doing two traversals; remove the
flattenSuggestions use in this hot path and derive the "has suggestions" boolean
from flattenedSuggestionRenderEntries instead: eliminate the useMemo that
computes flattenedSuggestions (and any references to flattenedSuggestions.length
> 0), update the overlay rendering logic to use flattenedSuggestionRenderEntries
(from flattenSuggestionRenderEntries(mentionChildren, suggestions)) to determine
emptiness and render accordingly, and keep flattenSuggestions only if elsewhere
referenced; adjust any variable names/usages so only one traversal over
mentionChildren/suggestions occurs.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5c545249-68e0-4029-b529-623047b4aec5

📥 Commits

Reviewing files that changed from the base of the PR and between 138399c and 341b750.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (20)
  • README.md
  • demo/src/examples/Examples.tsx
  • demo/src/examples/GroupedSuggestions.tsx
  • demo/src/examples/mentionsClassNames.ts
  • package.json
  • src/MentionsInput.tsx
  • src/MentionsInputQueryState.spec.ts
  • src/MentionsInputQueryState.ts
  • src/MentionsInputSelectors.spec.tsx
  • src/MentionsInputSelectors.ts
  • src/Suggestion.tsx
  • src/SuggestionsOverlay.spec.tsx
  • src/SuggestionsOverlay.tsx
  • src/index.ts
  • src/types.ts
  • src/useSuggestionsQuery.ts
  • src/utils/flattenSuggestions.spec.tsx
  • src/utils/flattenSuggestions.ts
  • src/utils/index.ts
  • tests/MentionsInput.spec.tsx

Comment on lines +711 to +717
const publicSection: MentionDataSection = {
label: 'Public API section',
items: [],
}

expect(publicSection.label).toBe('Public API section')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use publicSection.items or avoid constructing it.

Only label is asserted here, so items stays unused and trips unicorn/no-unused-properties. This will keep the test file failing lint.

Minimal fix
-    expect(publicSection.label).toBe('Public API section')
+    expect(publicSection).toEqual({
+      label: 'Public API section',
+      items: [],
+    })
📝 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.

Suggested change
const publicSection: MentionDataSection = {
label: 'Public API section',
items: [],
}
expect(publicSection.label).toBe('Public API section')
const publicSection: MentionDataSection = {
label: 'Public API section',
items: [],
}
expect(publicSection).toEqual({
label: 'Public API section',
items: [],
})
🧰 Tools
🪛 ESLint

[error] 713-713: Property items is defined but never used.

(unicorn/no-unused-properties)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/MentionsInputSelectors.spec.tsx` around lines 711 - 717, The test
constructs a MentionDataSection object named publicSection with an unused items
property which trips unicorn/no-unused-properties; either remove the unused
items key when creating publicSection or use it in an assertion—e.g., construct
publicSection = { label: 'Public API section' } only, or add an assertion
against publicSection.items (like expect(publicSection.items).toEqual([])) so
the items property is referenced; update the test around the publicSection
declaration/assertions accordingly.

Comment on lines +94 to +100
if (section.id !== undefined) {
return `id:${typeof section.id}:${String(section.id)}`
}

return typeof section.label === 'string'
? `label:${section.label}`
: `index:${sectionIndex.toString()}`
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Disambiguate fallback section keys.

Two sections that omit id but share the same string label both get the same key here. That gives src/utils/flattenSuggestions.ts duplicate React keys and makes src/MentionsInputQueryState.ts merge unrelated sections together during pagination. Include a disambiguator in the label fallback at minimum.

Possible fix
   if (section.id !== undefined) {
     return `id:${typeof section.id}:${String(section.id)}`
   }

   return typeof section.label === 'string'
-    ? `label:${section.label}`
+    ? `label:${section.label}:${sectionIndex.toString()}`
     : `index:${sectionIndex.toString()}`

As per coding guidelines "Never define components inside components; preserve stable keys and element identity across suggestions, highlighter output, and other dynamic lists".

📝 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.

Suggested change
if (section.id !== undefined) {
return `id:${typeof section.id}:${String(section.id)}`
}
return typeof section.label === 'string'
? `label:${section.label}`
: `index:${sectionIndex.toString()}`
if (section.id !== undefined) {
return `id:${typeof section.id}:${String(section.id)}`
}
return typeof section.label === 'string'
? `label:${section.label}:${sectionIndex.toString()}`
: `index:${sectionIndex.toString()}`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/MentionsInputSelectors.ts` around lines 94 - 100, The fallback key for
sections that lack `id` can collide when two sections share the same string
`label`; update the label branch in MentionsInputSelectors to include a
disambiguator (e.g., append the `sectionIndex` or a stable hash) so keys become
unique per-section: when `section.id` is undefined, return a string that
includes `section.label` plus `sectionIndex` (or another stable unique token)
instead of only `label:${section.label}`; ensure you use `String(sectionIndex)`
or similar to produce a stable string key so downstream code in
flattenSuggestions and MentionsInputQueryState no longer sees duplicate keys.

Comment on lines +91 to +146
export const flattenSuggestionRenderEntries = <
Extra extends Record<string, unknown> = Record<string, unknown>,
>(
children: ReactNode,
suggestions: SuggestionsMap<Extra> | undefined
): Array<FlattenedSuggestionRenderEntry<Extra>> => {
const flattened: Array<FlattenedSuggestionRenderEntry<Extra>> = []
let suggestionIndex = 0

for (const [childIndex, entry] of getOrderedSuggestionMapEntries(children, suggestions)) {
const { queryInfo, results, sections } = entry
const handledResults = new Set<SuggestionDataItem<Extra>>()

if (sections !== undefined && sections.length > 0) {
for (const section of sections) {
const sectionKey = `${childIndex.toString()}-section-${section.key}`
flattened.push({
type: 'section',
key: sectionKey,
label: section.label,
queryInfo,
})

for (const [sectionResultIndex, result] of section.results.entries()) {
handledResults.add(result)
flattened.push({
type: 'item',
key: `${sectionKey}-item-${getSuggestionId(result)}-${sectionResultIndex.toString()}`,
index: suggestionIndex,
result,
queryInfo,
sectionKey,
})
suggestionIndex += 1
}
}
}

for (const [resultIndex, result] of results.entries()) {
if (handledResults.has(result)) {
continue
}

flattened.push({
type: 'item',
key: `${childIndex.toString()}-item-${getSuggestionId(result)}-${resultIndex.toString()}`,
index: suggestionIndex,
result,
queryInfo,
})
suggestionIndex += 1
}
}

return flattened
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Split flattenSuggestionRenderEntries into smaller helpers so lint passes.

This now exceeds the repo's sonarjs/cognitive-complexity limit, so the changed file will fail lint. Extract the section pass and the trailing unsectioned-results pass into small helpers.

🧰 Tools
🪛 ESLint

[error] 96-96: Refactor this function to reduce its Cognitive Complexity from 16 to the 15 allowed.

(sonarjs/cognitive-complexity)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/flattenSuggestions.ts` around lines 91 - 146, The
flattenSuggestionRenderEntries function is too complex for the linter; extract
the inner "process sections" and "process unsectioned results" logic into two
small helpers (e.g., pushSectionEntries and pushUnsectionedResults) to reduce
cognitive complexity. Each helper should accept the flattened array, the
entry/queryInfo, the childIndex or sectionKey as needed, the results or
section.results, the shared handledResults Set, and the current suggestionIndex
and return the updated suggestionIndex (or mutate a passed object) so
suggestionIndex is preserved; ensure keys use the same formats
(sectionKey-item-... and childIndex-item-...), keep use of
getSuggestionId(result), and keep the original types
(FlattenedSuggestionRenderEntry, SuggestionDataItem, SuggestionsMap) so behavior
and types remain unchanged.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 21 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/SuggestionsOverlay.tsx">

<violation number="1" location="src/SuggestionsOverlay.tsx:112">
P2: Expose grouped suggestion labels with listbox semantics instead of rendering them as presentational items.</violation>
</file>

<file name="src/MentionsInputQueryState.ts">

<violation number="1" location="src/MentionsInputQueryState.ts:135">
P2: Merging an existing section key keeps the old label/id, so paginated section headers can go stale.</violation>
</file>

<file name="src/utils/flattenSuggestions.ts">

<violation number="1" location="src/utils/flattenSuggestions.ts:105">
P1: Rendering all sectioned items before leftover results can desync the visible order from keyboard focus order.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

const handledResults = new Set<SuggestionDataItem<Extra>>()

if (sections !== undefined && sections.length > 0) {
for (const section of sections) {
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Rendering all sectioned items before leftover results can desync the visible order from keyboard focus order.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/utils/flattenSuggestions.ts, line 105:

<comment>Rendering all sectioned items before leftover results can desync the visible order from keyboard focus order.</comment>

<file context>
@@ -49,4 +88,61 @@ const flattenSuggestions = <Extra extends Record<string, unknown> = Record<strin
+    const handledResults = new Set<SuggestionDataItem<Extra>>()
+
+    if (sections !== undefined && sections.length > 0) {
+      for (const section of sections) {
+        const sectionKey = `${childIndex.toString()}-section-${section.key}`
+        flattened.push({
</file context>
Fix with Cubic

className,
labelClassName,
}: SuggestionSectionHeaderProps) => (
<li role="presentation" className={cn(sectionStyles(), className)} data-slot="suggestion-section">
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Expose grouped suggestion labels with listbox semantics instead of rendering them as presentational items.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/SuggestionsOverlay.tsx, line 112:

<comment>Expose grouped suggestion labels with listbox semantics instead of rendering them as presentational items.</comment>

<file context>
@@ -89,6 +98,24 @@ const statusStyles = cva('px-4 py-2.5 text-left text-sm leading-relaxed', {
+  className,
+  labelClassName,
+}: SuggestionSectionHeaderProps) => (
+  <li role="presentation" className={cn(sectionStyles(), className)} data-slot="suggestion-section">
+    <span className={cn(sectionLabelStyles(), labelClassName)} data-slot="suggestion-section-label">
+      {label}
</file context>
Fix with Cubic

Comment on lines +135 to +136
...existingSection,
results: [...existingSection.results, ...section.results],
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Merging an existing section key keeps the old label/id, so paginated section headers can go stale.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/MentionsInputQueryState.ts, line 135:

<comment>Merging an existing section key keeps the old label/id, so paginated section headers can go stale.</comment>

<file context>
@@ -82,6 +83,63 @@ const clampFocusIndex = <Extra extends Record<string, unknown>>(
+
+    const existingSection = mergedSections[existingSectionIndex]
+    mergedSections[existingSectionIndex] = {
+      ...existingSection,
+      results: [...existingSection.results, ...section.results],
+    }
</file context>
Suggested change
...existingSection,
results: [...existingSection.results, ...section.results],
...section,
results: [...existingSection.results, ...section.results],
Fix with Cubic

@hbmartin hbmartin added this to the 6.1.0 milestone May 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants