feat(texteditor): add text alignment toggling buttons#5677
feat(texteditor): add text alignment toggling buttons#5677nucleogenesis merged 16 commits intolearningequality:unstablefrom
Conversation
|
👋 Thanks for contributing! We will assign a reviewer within the next two weeks. In the meantime, please ensure that:
We'll be in touch! 😊 |
00187e5 to
f2aa6e6
Compare
…gs with alignment dir
…hildren by calling serializeChildren directly
rtibblesbot
left a comment
There was a problem hiding this comment.
Good feature addition for RTL/LTR text alignment toggling. The serializer refactoring from mutation-based to return-based is a nice improvement. One bug in the overflow dropdown disabled logic.
CI passing.
Blocking:
- Inverted
:disabledlogic on overflow align button (see inline)
Suggestions:
- SVG icon dimension mismatch between align-left and align-right
- Peer dependency version mismatch for
@tiptap/extension-text-align
Nitpick:
- Double space in image markdown output when no dimensions present
@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly
How was this generated?
Reviewed the pull request diff checking for:
- Correctness: bugs, edge cases, undocumented behavior, resource leaks, hardcoded values
- Design: unnecessary complexity, naming, readability, comment accuracy, redundant state
- Architecture: duplicated concerns, minimal interfaces, composition over inheritance
- Testing: behavior-based assertions, mocks only at hard boundaries, accurate coverage
- Completeness: missing dependencies, unupdated usages, i18n, accessibility, security
- Principles: DRY (same reason to change), SRP, Rule of Three (no premature abstraction)
- Checked CI status and linked issue acceptance criteria
- For UI changes: inspected screenshots for layout, visual completeness, and consistency
...contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/EditorToolbar.vue
Outdated
Show resolved
Hide resolved
contentcuration/contentcuration/frontend/shared/views/TipTapEditor/assets/icon-alignRight.svg
Outdated
Show resolved
Hide resolved
...ntcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/utils/markdown.js
Outdated
Show resolved
Hide resolved
| } | ||
| } | ||
| break; | ||
| return parts.join('\n\n'); |
There was a problem hiding this comment.
praise: Nice refactoring of the serializer from mutation-based (result +=) to a return-based approach. This makes each case self-contained and much easier to reason about, especially now that alignment wrapping needs to inspect the serialized children before deciding the output format.
| type: 'paragraph', | ||
| content: [{ type: 'text', text: 'n', marks: [{ type: 'subscript' }] }], | ||
| }, | ||
| ]; |
There was a problem hiding this comment.
praise: Thorough test coverage for text alignment serialization — covers right/left/center/no-attr cases, headings, inline marks inside aligned paragraphs, and mixed alignment documents. Good edge case thinking.
...entcuration/frontend/shared/views/TipTapEditor/TipTapEditor/extensions/SmallTextExtension.js
Show resolved
Hide resolved
rtibblesbot
left a comment
There was a problem hiding this comment.
Solid iteration — the prior blocking issue (inverted disabled logic) and the SVG dimension mismatch are both fixed. 3 prior finding(s) resolved.
CI passing. New strings target the default branch (unstable) — appropriate.
Suggestions:
serializeNodeguard clause returnsundefined, which the return-based refactor now concatenates (see inline)- Double space in no-dimensions image markdown path (prior nitpick, still present — see inline)
Note: the reviewer thread from @nucleogenesis / @radinamatic / @marcellamaki about the alignment icon not reflecting the browser's automatic bidi direction (e.g., Arabic auto-aligning right while the icon still shows left) is a real UX concern worth addressing, though it may be out of scope for this PR.
@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly
How was this generated?
Compared the current PR state against findings from a prior review:
- Retrieved prior bot reviews deterministically via the GitHub API
- Classified each prior finding as RESOLVED, UNADDRESSED, ACKNOWLEDGED, or CONTESTED
- Only raised NEW findings for newly introduced code
- Reviewed the pull request diff checking for correctness, design, architecture, testing, completeness, and adherence to DRY/SRP principles
- Checked CI status and linked issue acceptance criteria
.../contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/utils/markdownSerializer.js
Outdated
Show resolved
Hide resolved
...ntcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/utils/markdown.js
Outdated
Show resolved
Hide resolved
...tion/contentcuration/frontend/shared/views/TipTapEditor/__tests__/markdownSerializer.spec.js
Show resolved
Hide resolved
.../contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/utils/markdownSerializer.js
Show resolved
Hide resolved
rtibblesbot
left a comment
There was a problem hiding this comment.
Good iteration — the bidi alignment detection via getEffectiveAlignment is a nice solution to the RTL concern raised by reviewers.
CI passing. New strings target unstable (default branch) — appropriate.
3 prior finding(s) resolved.
Suggestions:
serializeNodeguard clause still returnsundefined, which concatenates as the literal string"undefined"(UNADDRESSED — see inline)- Double space in no-dimensions image markdown path when alignment is present (UNADDRESSED — see inline)
@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly
How was this generated?
Compared the current PR state against findings from a prior review:
- Retrieved prior bot reviews deterministically via the GitHub API
- Classified each prior finding as RESOLVED, UNADDRESSED, ACKNOWLEDGED, or CONTESTED
- Only raised NEW findings for newly introduced code
- Reviewed the pull request diff checking for correctness, design, architecture, testing, completeness, and adherence to DRY/SRP principles
- Checked CI status and linked issue acceptance criteria
.../contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/utils/markdownSerializer.js
Outdated
Show resolved
Hide resolved
...ntcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/utils/markdown.js
Outdated
Show resolved
Hide resolved
| const dom = editorInstance.view.domAtPos(from).node; | ||
| const el = dom.nodeType === 1 ? dom : dom.parentElement; | ||
|
|
||
| return el ? window.getComputedStyle(el).textAlign : 'left'; |
There was a problem hiding this comment.
praise: Smart fallback to getComputedStyle for detecting browser-resolved bidi alignment — this handles the RTL auto-alignment case that pure attribute checking would miss.
rtibblesbot
left a comment
There was a problem hiding this comment.
Good iteration — all prior findings are resolved. The serializer refactoring to return-based is clean and the bidi alignment detection via getEffectiveAlignment is a solid approach.
CI passing. New strings target unstable (default branch) — appropriate.
5 prior finding(s) resolved.
Suggestions:
getEffectiveAlignmentmissing null guard when called fromalignActioncomputed (see inline)
@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly
How was this generated?
Compared the current PR state against findings from a prior review:
- Retrieved prior bot reviews deterministically via the GitHub API
- Classified each prior finding as RESOLVED, UNADDRESSED, ACKNOWLEDGED, or CONTESTED
- Only raised NEW findings for newly introduced code
- Reviewed the pull request diff checking for correctness, design, architecture, testing, completeness, and adherence to DRY/SRP principles
- Checked CI status and linked issue acceptance criteria
...entcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useToolbarActions.js
Show resolved
Hide resolved
|
|
||
| serializeNode(doc, null, 0); | ||
| return result.trim(); | ||
| return serializeNode(doc, null, 0).trim(); |
There was a problem hiding this comment.
praise: The refactor from mutation-based (result +=) to return-based serialization is a meaningful improvement — each node handler is now a pure function returning its output, which makes the control flow easier to reason about and eliminates the shared mutable state.
rtibblesbot
left a comment
There was a problem hiding this comment.
Good iteration — all prior findings are resolved. The bidi alignment detection via getEffectiveAlignment, the serializer alignment support, and the thorough test coverage make this a solid feature addition.
CI passing. PR targets unstable (default branch) — appropriate for new strings.
6 prior finding(s) resolved.
Suggestions:
- MobileFormattingBar align button missing
:is-availablebinding (see inline)
@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly
How was this generated?
Compared the current PR state against findings from a prior review:
- Retrieved prior bot reviews deterministically via the GitHub API
- Classified each prior finding as RESOLVED, UNADDRESSED, ACKNOWLEDGED, or CONTESTED
- Only raised NEW findings for newly introduced code
- Reviewed the pull request diff checking for correctness, design, architecture, testing, completeness, and adherence to DRY/SRP principles
- Checked CI status and linked issue acceptance criteria
| <ToolbarButton | ||
| :title="alignAction.title" | ||
| :icon="alignAction.icon" | ||
| :is-active="alignAction.isActive" |
There was a problem hiding this comment.
suggestion: The align button here doesn't bind :is-available="alignAction.isAvailable", unlike the desktop EditorToolbar.vue (line 77). This means the align button won't be disabled inside code blocks on mobile. Consider adding :is-available="alignAction.isAvailable" for parity.
| const editor = inject('editor', null); | ||
|
|
||
| // helper | ||
| const getEffectiveAlignment = editorInstance => { |
There was a problem hiding this comment.
praise: getEffectiveAlignment is a clean solution to the bidi/RTL concern — checking isActive first for explicit alignment, then falling back to getComputedStyle for browser-resolved direction. The null guard at line 10 keeps it consistent with the rest of the composable's defensive patterns.
nucleogenesis
left a comment
There was a problem hiding this comment.
The latest changes look great in manual testing! Thanks @habibayman !
rtibblesbot's suggestions were addressed
Summary
This PR includes the addition of a toggle alignment (Left/Right) button.
From the motives of making the RTL editor experience better and give users the ability to seamlessly mix RTL & LTR text in the same editing block.
It includes support for all nodes & Marks we have:
For this to work with our backward compatible dual markdown conversion pipeline; I have altered the markdown serializer to wrap non-default direction nodes with HTML tags containing the
alignattribute set.And of course, updated the unit tests accordingly.
References
Figma Design
currently this PR represents part of Update rich text editor to flexible, extensible rich text editing framework #5049
fixes [Rich Text Editor]: add a toggle text alignment (R/L) button #5385
Reviewer guidance
Tip
You can use the checklist I wrote in the description section
Note
Check the native behavior of the extension from the official TipTap documentation to see where its limitations -if any- exist.
https://tiptap.dev/docs/editor/extensions/functionality/textalign