Skip to content

Keep RTE toolbar dropdowns from being clipped - #6280

Merged
rtibbles merged 2 commits into
learningequality:unstablefrom
AlexVelezLl:fix-rte-dropdown-clipping
Oct 6, 2026
Merged

rtibbles merged 2 commits into
learningequality:unstablefrom
AlexVelezLl:fix-rte-dropdown-clipping

Conversation

@AlexVelezLl

@AlexVelezLl AlexVelezLl commented Oct 2, 2026 •

Copy link
Copy Markdown
Member

Summary

  • The format and paste dropdowns in the rich text editor toolbar were cut off by any parent that hides overflow, most visibly the QTI question card on a free response question at the end of the list.
  • Both now use KDropdownMenu, like the toolbar's More menu. It renders the menu on document.body and flips it above the trigger when there's no room below.
  • constrainToScrollParent is off, so the menu flips against the viewport instead of being pushed inside the question card, which would make it cover its own trigger.
  • Format options still preview the editor's typography: the editor's heading/paragraph rules now also apply to .format-option.
  • The hand-written keyboard, focus and outside-click handling is replaced by KDropdownMenu's. Trigger ArrowUp/ArrowDown and in-menu Home/End are no longer supported, matching the More menu.
  • Second commit: removes useDropdowns. Paste actions move to useToolbarActions, and format tracking moves into FormatDropdown, its only consumer.
Before After
image image

References

None.

Reviewer guidance

  • Open an exercise's Questions tab, edit questions placed last, and open the format and paste menus near the bottom of the panel after scrolling to the end. They should show in full, opening upward when there's no room below.
  • Choosing an option should apply it without closing the editor.
  • Visual differences from the old menus come from KDS (square corners, KDS shadow, focus highlight on the first item).

AI usage

I used Claude Code to investigate the clipping, compare approaches, and implement and test the change. I reviewed the code, steered the approach toward KDropdownMenu, and verified the menus in the browser.

🤖 Generated with Claude Code

@AlexVelezLl
AlexVelezLl marked this pull request as ready for review October 2, 2026 20:40
@AlexVelezLl
AlexVelezLl requested review from a team, marcellamaki and rtibblesbot October 2, 2026 20:40
@rtibblesbot

rtibblesbot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

🟡 Waiting for changes

Last updated: 2026-10-02 23:24 UTC

@rtibbles rtibbles self-assigned this Oct 2, 2026
@AlexVelezLl
AlexVelezLl force-pushed the fix-rte-dropdown-clipping branch from 7e32704 to 37c8abc Compare October 2, 2026 20:53

@rtibblesbot rtibblesbot 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.

PR #6280: the format trigger label wraps for headings, and focus on the selected format option is invisible. Swapping both menus for KDropdownMenu does fix the clipping.

The head moved to 37c8abca0 after this review ran; those commits are unreviewed.

CI passing.

  • Blocking: label wrap (FormatDropdown.vue:8), invisible focus on selected option (FormatDropdown.vue:23).
  • Suggestions: off-screen menu on short viewports, RTL alignment, Home/End, screen-reader name, value-based selection, global class name, paste-select test, KDS menu-item role.

Manual QA:

  • Clipping is gone at the top and bottom of the panel.
  • All five formats apply and save.
  • The paste flows work from the chevron, the main button and More.
  • Click-outside dismisses the menu without minimising the editor.
  • Escape returns focus.
  • The QA server then hit a Redis MISCONF error. Later scenarios ran on a standalone TipTapEditor harness.
  • Not checked: cursor-tracking of the format label across blocks, undo/redo, and two RTEs on one card.

@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly

How was this generated?

Ran a phased review pipeline over the pull request diff:

  • Classified the diff to select review passes (core, frontend, backend) and whether manual QA was required
  • Core review pass checked correctness, design, architecture, testing, completeness, and DRY/SRP/Rule-of-Three principles
  • Specialized frontend/backend review passes applied framework-specific lenses where those files changed
  • For UI changes: manual QA and an accessibility audit against a live dev server, when available
  • Checked CI status and linked issue acceptance criteria
  • Synthesized one review from those passes and chose the verdict from the findings, CI status, and QA evidence

:tabindex="index === focusedIndex ? 0 : -1"
@click="handleOptionClick(option)"
@keydown="handleItemKeydown($event, index, option)"
<KDropdownMenu

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.

suggestion: Both menus now fail axe's aria-roles/aria-required-children checks. KDS 5.9.0 UiMenuOption renders the invalid role="menu-item". The removed menus used role="menuitem". Can a KDS issue be filed and linked here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, could you open an issue in KDS for this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

And here on KDS.

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.

Already covered by learningequality/kolibri-design-system#1333, which includes the invalid role="menu-item".

@click="handleOptionClick(option)"
@keydown="handleItemKeydown($event, index, option)"
<KDropdownMenu
:options="menuOptions"

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.

nitpick: The paste menu stays open when its chevron overflows out of the toolbar.

  • Repro: open the menu at 1200px, then narrow to 500px.
  • The menu stays visible, 25px past the viewport, with aria-expanded="true".
  • The overflowed chevron is hidden with visibility: hidden, which the body-portaled menu doesn't inherit.
  • Close the menu when the chevron overflows.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Could you open an issue on KDS to expose the closePopover method so that we can call it when it is squashed into the more button?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

And here on KDS.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Still waiting on the KDS issue to be filed for this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

AlexVelezLl and others added 2 commits October 2, 2026 17:37
…r clipped

The format and paste menus rendered inside the editor, so any ancestor that
hides its overflow, such as the QTI question card, cut them off. KDropdownMenu
renders them on document.body and flips them when there is no room, the way
the toolbar's More menu already does. Format options keep previewing the
editor's typography, and the hand-written keyboard, focus and outside-click
handling is replaced by KDropdownMenu's.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… own state

With KDropdownMenu managing the menus, useDropdowns held only unrelated leftovers.
The paste actions join the other toolbar action lists in useToolbarActions, and the
format at the cursor is tracked by FormatDropdown, its only user, so the toolbar and
paste dropdown no longer each listen to the editor for it. Drops the unused
clipboardOptions and the redundant selectFormat.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@AlexVelezLl
AlexVelezLl force-pushed the fix-rte-dropdown-clipping branch from 37c8abc to 51bf1ea Compare October 2, 2026 22:44

@rtibblesbot rtibblesbot 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.

PR #6280: 3 of 13 prior findings still need action; the other 10 are resolved or acknowledged.

CI passes. Manual QA did not run.

Still open:

  • FormatDropdown.vue:6 — screen readers don't announce the current format. This is pre-existing on unstable. Please file the issue the maintainer asked for.
  • PasteDropdown.vue:35 — menus fail axe aria-roles/aria-required-children. Please file the requested KDS issue.
  • PasteDropdown.vue:36 — the paste menu stays open when overflowed. Please file the requested KDS issue to expose closePopover.

New:

  • One suggestion on the format test (inline).
Prior-finding status

ACKNOWLEDGED — FormatDropdown.vue:8 — "Header 1/2/3" wraps onto two lines
ACKNOWLEDGED — FormatDropdown.vue:23 — Keyboard focus on selected option invisible
ACKNOWLEDGED — FormatDropdown.vue:16 — 322px menu can run off-screen
ACKNOWLEDGED — FormatDropdown.vue:14 — RTL menu overflows editor border (kolibri-design-system#1344)
ACKNOWLEDGED — FormatDropdown.vue:15 — Home/End close the menu (kolibri-design-system#1345)
UNADDRESSED — FormatDropdown.vue:6 — Screen-reader users can't hear the current format
RESOLVED — FormatDropdown.vue — Selection matched by translated label
RESOLVED — TipTapEditor.vue — Unscoped .format-option heading styles
RESOLVED — PasteDropdown.vue:38 — No test selects a paste menu option
UNADDRESSED — PasteDropdown.vue:35 — Menus fail axe aria-roles checks
UNADDRESSED — PasteDropdown.vue:36 — Paste menu stays open when overflowed
ACKNOWLEDGED — PasteDropdown.vue:169 — Labels take KDS 13.125px font size
RESOLVED — EditorToolbar.spec.js:451 — Menu-outside-toolbar assertion (praise)


@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly

How was this generated?

Ran an automatic code-only delta review triggered by new commits on a previously reviewed PR:

  • Retrieved prior bot reviews via the GitHub API
  • Classified each prior finding as RESOLVED, UNADDRESSED, ACKNOWLEDGED, or CONTESTED
  • Only raised NEW findings for newly introduced code
  • Core review pass only — specialized frontend/backend lenses and manual QA run when a review is explicitly requested
  • Synthesized one review from the passes and chose the verdict from the findings, CI status, and QA evidence


await user.click(within(screen.getByRole('menu')).getByText(formatHeader1$()));

expect(editor.commands).toContainEqual(['toggleHeading', { level: 1 }]);

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.

suggestion: This test asserts the recorded command chain, not the resulting document.

  • An equivalent rewrite such as setHeading would break it with no behavior change.
  • renderWithClipboard (:353) already renders a real editor.
  • Please start from '<p>a</p>', pick "Header 1", and assert getHTML() is '<h1>a</h1>'.
  • The chain-recording Proxy can then be deleted.

@rtibblesbot
rtibblesbot dismissed their stale review October 2, 2026 23:24

Superseded by my review of 51bf1ea: no blocking findings remain.

@rtibbles rtibbles left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Code changes make sense, and local behaviour checks out. The only issues I could see are pre-existing and not introduced in this PR - or are underlying issues in KDS.

Will wait on filing of follow up issues before approving for merge.

@rtibbles rtibbles left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

All follow up issues filed, manual QA showed no other concerns to me. Ready to merge.

@rtibbles
rtibbles merged commit 8b8ef2c into learningequality:unstable Oct 6, 2026
63 checks passed
@AlexVelezLl
AlexVelezLl deleted the fix-rte-dropdown-clipping branch October 6, 2026 19:18
@rtibblesbot

Copy link
Copy Markdown
Contributor

Quick check on rtibblesbot's review of this PR: react 👍 helpful · 😕 mixed · 👎 not helpful. Reply with anything specific.

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.

3 participants