Skip to content

fix: expose first suggestion as active descendant - #2965

Merged
nperez0111 merged 2 commits into
TypeCellOS:mainfrom
pupuking723:fix/suggestion-menu-active-descendant-zero
Aug 12, 2026
Merged

fix: expose first suggestion as active descendant#2965
nperez0111 merged 2 commits into
TypeCellOS:mainfrom
pupuking723:fix/suggestion-menu-active-descendant-zero

Conversation

@pupuking723

@pupuking723 pupuking723 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Fix the suggestion menu's aria-activedescendant value when the first item is selected.

Fixes #2926.

Rationale

The wrappers previously used a truthiness check for selectedIndex, so index 0 was treated like no selection and omitted from the accessibility attributes.

Changes

  • Add a small shared helper for deriving the active suggestion item ID.
  • Use it in both the standard and grid suggestion menu wrappers.
  • Cover the first item, a later item, and no selection with unit tests.

Impact

This only changes the accessibility attribute for the existing first-item selection. Other selection behavior and the no-selection state are unchanged.

Testing

  • npx --yes pnpm@11.8.0 --filter @blocknote/react test
  • npx --yes pnpm@11.8.0 --filter @blocknote/react lint
  • vp fmt --check for the changed files
  • git diff --check

Screenshots/Video

Not applicable; this is an accessibility attribute fix with no visual change.

Checklist

  • Code follows the project's coding standards.
  • Unit tests covering the fix have been added.
  • All relevant existing tests pass.
  • No documentation update is needed because there is no API or visual change.

Additional Notes

The regression test failed before the implementation because the helper was not yet available, and passes with the fix.

Summary by CodeRabbit

  • Accessibility

    • Improved active suggestion tracking for screen readers across list and grid menus by consistently setting the appropriate active-descendant identifier.
    • Ensured the active identifier is cleared when no suggestion is selected or the menu is unmounted.
  • Tests

    • Added coverage for active suggestion identifiers, empty selection states, and cleanup behavior across both menu layouts.

@vercel

vercel Bot commented Aug 12, 2026

Copy link
Copy Markdown

@pupuking723 is attempting to deploy a commit to the TypeCell Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 247c57e3-a03c-4bd0-b0c6-0fad87ca2b3a

📥 Commits

Reviewing files that changed from the base of the PR and between 1215587 and 00624ef.

📒 Files selected for processing (1)
  • packages/react/src/components/SuggestionMenu/SuggestionMenuWrapper.test.tsx

📝 Walkthrough

Walkthrough

The change adds a shared helper for suggestion-menu item IDs. Both suggestion-menu wrappers use it for aria-activedescendant, including selected index 0. Tests cover indexed, undefined, cleared, and unmounted states.

Changes

Suggestion menu accessibility

Layer / File(s) Summary
Active item ID helper and tests
packages/react/src/components/SuggestionMenu/getSuggestionMenuItemId.ts, packages/react/src/components/SuggestionMenu/SuggestionMenu.test.tsx
The helper returns an ID for a defined index and undefined otherwise. Tests cover both cases.
Wrapper active descendant integration
packages/react/src/components/SuggestionMenu/SuggestionMenuWrapper.tsx, packages/react/src/components/SuggestionMenu/GridSuggestionMenu/GridSuggestionMenuWrapper.tsx
Both wrappers use the helper to set aria-activedescendant.
Wrapper active descendant tests
packages/react/src/components/SuggestionMenu/SuggestionMenuWrapper.test.tsx
Tests cover selected indices, clearing when no item is selected, and clearing on unmount for both wrapper variants.

Estimated code review effort: 2 (Simple) | ~10 minutes

Poem

A rabbit checks the active ID,
Zero is no longer pushed aside.
No choice clears the attribute,
List and grid now match the pattern.
Tests hop along with carrot cheer.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the accessibility fix for the first suggestion.
Description check ✅ Passed The description includes all template sections and explains the changes, impact, testing, and checklist status.
Linked Issues check ✅ Passed The changes address issue #2926 by fixing both wrappers and testing selected and unselected states.
Out of Scope Changes check ✅ Passed All changes support the linked issue and stated objectives, with no unrelated modifications identified.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 `@packages/react/src/components/SuggestionMenu/SuggestionMenu.test.tsx`:
- Around line 44-49: Extend the SuggestionMenu tests around
getSuggestionMenuItemId to cover wrapper-level aria-activedescendant behavior
for both wrapper components. Verify index 0, a later index, and undefined, and
confirm the attribute is removed during cleanup through each wrapper.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fbc24cb8-93bb-44e6-82d6-4b56e9f744a8

📥 Commits

Reviewing files that changed from the base of the PR and between e0cce10 and 1215587.

📒 Files selected for processing (4)
  • packages/react/src/components/SuggestionMenu/GridSuggestionMenu/GridSuggestionMenuWrapper.tsx
  • packages/react/src/components/SuggestionMenu/SuggestionMenu.test.tsx
  • packages/react/src/components/SuggestionMenu/SuggestionMenuWrapper.tsx
  • packages/react/src/components/SuggestionMenu/getSuggestionMenuItemId.ts

Comment on lines +44 to +49

it("returns an active descendant id for the first suggestion", () => {
expect(getSuggestionMenuItemId(0)).toBe("bn-suggestion-menu-item-0");
expect(getSuggestionMenuItemId(2)).toBe("bn-suggestion-menu-item-2");
expect(getSuggestionMenuItemId(undefined)).toBeUndefined();
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

target="packages/react/src/components/SuggestionMenu/SuggestionMenu.test.tsx"

printf '%s\n' '--- target file map ---'
ast-grep outline "$target" --view expanded

printf '%s\n' '--- target test file ---'
cat -n "$target"

printf '%s\n' '--- related SuggestionMenu files ---'
fd -i 'SuggestionMenu' packages/react/src/components | sort

printf '%s\n' '--- wrapper definitions and usages ---'
rg -n -C 4 'SuggestionMenuWrapper|GridSuggestionMenuWrapper|aria-activedescendant|getSuggestionMenuItemId' packages/react/src

Repository: TypeCellOS/BlockNote

Length of output: 20042


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- all active-descendant tests and wrapper tests ---'
rg -n -C 5 'aria-activedescendant|SuggestionMenuWrapper|GridSuggestionMenuWrapper' \
  --glob '*.{test,spec}.{ts,tsx,js,jsx}' \
  --glob '!node_modules' .

printf '%s\n' '--- SuggestionMenuWrapper implementation ---'
cat -n packages/react/src/components/SuggestionMenu/SuggestionMenuWrapper.tsx

printf '%s\n' '--- GridSuggestionMenuWrapper implementation ---'
cat -n packages/react/src/components/SuggestionMenu/GridSuggestionMenu/GridSuggestionMenuWrapper.tsx

printf '%s\n' '--- test configuration and React test utilities ---'
rg -n -C 3 'vite-plus/test|vitest|testing-library|render\(' \
  --glob '*.{json,js,jsx,ts,tsx,mjs,cjs}' \
  packages/react package.json pnpm-workspace.yaml

Repository: TypeCellOS/BlockNote

Length of output: 212


🏁 Script executed:

#!/bin/bash

printf '%s\n' '--- all active-descendant tests and wrapper tests ---'
rg -n -C 5 'aria-activedescendant|SuggestionMenuWrapper|GridSuggestionMenuWrapper' \
  --glob '*.{test,spec}.{ts,tsx,js,jsx}' \
  --glob '!node_modules' . || true

printf '%s\n' '--- SuggestionMenuWrapper implementation ---'
cat -n packages/react/src/components/SuggestionMenu/SuggestionMenuWrapper.tsx

printf '%s\n' '--- GridSuggestionMenuWrapper implementation ---'
cat -n packages/react/src/components/SuggestionMenu/GridSuggestionMenu/GridSuggestionMenuWrapper.tsx

printf '%s\n' '--- test configuration and React test utilities ---'
rg -n -C 3 'vite-plus/test|vitest|testing-library|render\(' \
  --glob '*.{json,js,jsx,ts,tsx,mjs,cjs}' \
  packages/react package.json pnpm-workspace.yaml || true

Repository: TypeCellOS/BlockNote

Length of output: 11649


🏁 Script executed:

#!/bin/bash

printf '%s\n' '--- keyboard navigation hooks ---'
cat -n packages/react/src/components/SuggestionMenu/hooks/useSuggestionMenuKeyboardNavigation.ts
cat -n packages/react/src/components/SuggestionMenu/GridSuggestionMenu/hooks/useGridSuggestionMenuKeyboardNavigation.ts

printf '%s\n' '--- package test dependencies and scripts ---'
rg -n -C 3 '"(test|react-testing-library|testing-library|jsdom|vite-plus)"|scripts:' \
  packages/react/package.json package.json pnpm-workspace.yaml

printf '%s\n' '--- read-only coverage verifier ---'
python3 - <<'PY'
from pathlib import Path
import re

root = Path(".")
test_files = [
    p for p in root.rglob("*")
    if p.is_file()
    and p.suffix in {".ts", ".tsx", ".js", ".jsx"}
    and (".test." in p.name or ".spec." in p.name)
    and "node_modules" not in p.parts
]

terms = {
    "SuggestionMenuWrapper": re.compile(r"\bSuggestionMenuWrapper\b"),
    "GridSuggestionMenuWrapper": re.compile(r"\bGridSuggestionMenuWrapper\b"),
    "aria-activedescendant": re.compile(r"aria-activedescendant"),
}

for name, pattern in terms.items():
    matches = []
    for path in test_files:
        text = path.read_text(errors="replace")
        if pattern.search(text):
            matches.append(str(path))
    print(f"{name}: {len(matches)} matching test files")
    for path in matches:
        print(f"  {path}")
PY

Repository: TypeCellOS/BlockNote

Length of output: 6723


Add wrapper-level aria-activedescendant coverage

No test file covers either wrapper or this attribute. Add tests for index 0, a later index, undefined, and cleanup through both wrappers.

🤖 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 `@packages/react/src/components/SuggestionMenu/SuggestionMenu.test.tsx` around
lines 44 - 49, Extend the SuggestionMenu tests around getSuggestionMenuItemId to
cover wrapper-level aria-activedescendant behavior for both wrapper components.
Verify index 0, a later index, and undefined, and confirm the attribute is
removed during cleanup through each wrapper.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added direct wrapper coverage in 00624ef. The tests render both the list and grid wrappers, exercise index 0, a later index, and undefined, and verify unmount cleanup clears aria-activedescendant. The full @blocknote/react test and lint commands pass locally.

@pkg-pr-new

pkg-pr-new Bot commented Aug 12, 2026

Copy link
Copy Markdown

Open in StackBlitz

@blocknote/ariakit

npm i https://pkg.pr.new/@blocknote/ariakit@2965

@blocknote/code-block

npm i https://pkg.pr.new/@blocknote/code-block@2965

@blocknote/core

npm i https://pkg.pr.new/@blocknote/core@2965

@blocknote/mantine

npm i https://pkg.pr.new/@blocknote/mantine@2965

@blocknote/react

npm i https://pkg.pr.new/@blocknote/react@2965

@blocknote/server-util

npm i https://pkg.pr.new/@blocknote/server-util@2965

@blocknote/shadcn

npm i https://pkg.pr.new/@blocknote/shadcn@2965

@blocknote/xl-ai

npm i https://pkg.pr.new/@blocknote/xl-ai@2965

@blocknote/xl-docx-exporter

npm i https://pkg.pr.new/@blocknote/xl-docx-exporter@2965

@blocknote/xl-email-exporter

npm i https://pkg.pr.new/@blocknote/xl-email-exporter@2965

@blocknote/xl-multi-column

npm i https://pkg.pr.new/@blocknote/xl-multi-column@2965

@blocknote/xl-odt-exporter

npm i https://pkg.pr.new/@blocknote/xl-odt-exporter@2965

@blocknote/xl-pdf-exporter

npm i https://pkg.pr.new/@blocknote/xl-pdf-exporter@2965

commit: 00624ef

@nperez0111
nperez0111 merged commit 115d433 into TypeCellOS:main Aug 12, 2026
19 of 22 checks passed
@nperez0111

Copy link
Copy Markdown
Contributor

Thanks for the PR @pupuking723

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.

Suggestion menu omits aria-activedescendant for the first item

2 participants