Skip to content

✨(frontend) enable justified text alignment#2294

Open
ericboucher wants to merge 7 commits into
suitenumerique:mainfrom
ericboucher:add-justify
Open

✨(frontend) enable justified text alignment#2294
ericboucher wants to merge 7 commits into
suitenumerique:mainfrom
ericboucher:add-justify

Conversation

@ericboucher
Copy link
Copy Markdown

@ericboucher ericboucher commented May 14, 2026

Purpose

Expose justified text alignment in the editor so formal documents match common expectations (e.g. justified body text). Docs is gaining adoption in French administration contexts where official or formal content often expects full justification alongside left / center / right.

We are aware justified text is not always ideal for accessibility (word spacing) (#395). We believe usage will stay relatively uncommon for everyday notes; the control remains optional (per block, not a default). For the workflows that need it, having justify available is nonetheless important.

Screenshot 2026-05-14 at 3 40 11 PM Screenshot 2026-05-14 at 3 08 42 PM

Proposal

  • Extend the formatting toolbar with BlockNote’s existing TextAlignButton for justify, alongside left / center / right (minimal surface: reuses @blocknote/react + existing i18n).
  • Document / accept trade-offs:
    • Editor: BlockNote schema already supports justify; default toolbar omitted it—we only surface it.
    • DOCX: upstream @blocknote/xl-docx-exporter historically maps justify to OOXML distribute, which is not the same as standard justified paragraphs (both). We prefer upstream fix or typed overrides long-term over patching minified dist, which is brittle on exporter upgrades—scope any DOCx alignment tweak in this PR or as a follow-up as maintainers prefer.
    • A11y: no change to defaults; justify is opt-in; consider help text later.

External contributions

Thank you for your contribution! 🎉

Please ensure the following items are checked before submitting your pull request:

General requirements

Skip the checkbox below 👇 if you're fixing an issue or adding documentation

  • Before submitting a PR for a new feature I made sure to contact the product manager

CI requirements

  • I made sure that all existing tests are passing
  • I have signed off my commits with git commit --signoff (DCO compliance)
  • I have signed my commits with my SSH or GPG key (git commit -S)
  • My commit messages follow the required format: <gitmoji>(type) title description
  • I have added a changelog entry under ## [Unreleased] section (if noticeable change)

AI requirements

Skip the checkboxes below 👇 If you didn't use AI for your contribution

  • I used AI assistance to produce part or all of this contribution
  • I have read, reviewed, understood and can explain the code I am submitting
  • I can jump in a call or a chat to explain my work to a maintainer

@ericboucher ericboucher changed the title Add justify option to text block ✨(frontend) add justified text alignment May 14, 2026
@ericboucher ericboucher changed the title ✨(frontend) add justified text alignment ✨(frontend) enable justified text alignment May 14, 2026
@ericboucher ericboucher marked this pull request as ready for review May 22, 2026 08:13
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

Review Change Stack

Walkthrough

This PR adds justified text alignment support to the Impress document editor. The frontend toolbar now includes a justify button positioned after the right-align button. The DOCX export system is updated to handle justified alignment through a new shared styling converter function (blockNoteDocxBlockPropsToStyles) that maps BlockNote text alignment properties to DOCX paragraph options, with justify consistently mapped to OOXML both. Six DOCX block handlers (for paragraph, heading, and list item types) use this converter. The export schema configuration is updated to use these new handlers, and existing alignment mappings in utils and imageDocx are corrected from distribute to both for consistency.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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
Title check ✅ Passed The title clearly describes the main change: enabling justified text alignment in the frontend, which is the primary focus of this PR.
Description check ✅ Passed The description comprehensively explains the purpose, motivation (French administration formal documents), proposal details, accessibility trade-offs, and implementation approach, all directly related to the changeset.
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 unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

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: 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
`@src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/blockNoteDocxBlockProps.ts`:
- Around line 19-43: The current code conditionally omits the entire shading/run
objects when backgroundColor/textColor are 'default' or missing, but docx can
accept undefined subfields; simplify by always returning the shading object
(with type: ShadingType.CLEAR) and the run object while setting shading.fill to
colors[props.backgroundColor]?.background?.slice(1) (or undefined) and run.color
to colors[props.textColor]?.text?.slice(1) (or undefined), removing the outer
ternaries that produce undefined for the whole shading/run so invalid XML won’t
be produced and lookups that miss a color simply leave the subfield undefined;
update the code around the shading and run expressions (references: shading,
ShadingType.CLEAR, run, colors, props.backgroundColor, props.textColor)
accordingly.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5cbbcbed-9d82-41f8-9bae-662d0d78575f

📥 Commits

Reviewing files that changed from the base of the PR and between 24d58a1 and 8f3fe86.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolBar/BlockNoteToolbar.tsx
  • src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/blockNoteDocxBlockProps.ts
  • src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/defaultBlocksDocxJustify.tsx
  • src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/imageDocx.tsx
  • src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/index.ts
  • src/frontend/apps/impress/src/features/docs/doc-export/mappingDocx.tsx
  • src/frontend/apps/impress/src/features/docs/doc-export/utils.ts

Comment on lines +19 to +43
shading:
props.backgroundColor === 'default' || !props.backgroundColor
? undefined
: {
type: ShadingType.CLEAR,
fill: (() => {
const color = colors[props.backgroundColor]?.background;
if (!color) {
return undefined;
}
return color.slice(1);
})(),
},
run:
props.textColor === 'default' || !props.textColor
? undefined
: {
color: (() => {
const color = colors[props.textColor]?.text;
if (!color) {
return undefined;
}
return color.slice(1);
})(),
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In the docxnpm library, areParagraphoption fields likeshading.fillandrun.colorallowed to beundefined, or should the entire shading/run object be omitted when color is unresolved?

💡 Result:

Yes—within docx, option fields such as shading.fill and run.color are defined as optional, and passing them as undefined (i.e., leaving them undefined) is supported; docx omits the corresponding XML attribute when the value is undefined. Therefore, when a color is unresolved, you can set the individual option fields to undefined rather than needing to omit the entire shading/run object. In docx’s shading implementation, the IShadingAttributesProperties type defines fill/color/type as optional fields, and createShading maps undefined to “no attribute” by setting the XML attribute value to undefined when fill/color are undefined [1]. This means the library naturally supports “unresolved” colors by omitting only those attributes, not the entire parent object. Similarly for run-level color: docx’s public type definitions for run properties expose color?: string (optional) [2], meaning you should provide it only when you have a concrete value. If you omit/leave it undefined, the serialization layer can exclude it (and RunProperties.prepForXml is designed to exclude empty components from XML serialization) [3]. Practical guidance: - If you have a Paragraph/Run options object and you don’t know the color yet, omit the specific fields (or leave them as undefined). - Only omit the entire shading or run object if the whole component would be empty/unneeded for your desired formatting. Answering your exact question: you do not need to omit the entire shading/run object when shading.fill or run.color is unresolved; docx is designed to allow those individual fields to be undefined and will omit the underlying XML attributes [1][2].

Citations:


DOCX accepts undefined style fields; omitting the whole shading/run object isn’t required

docx’s types/serialization support optional style attributes like shading.fill and run.color; leaving those fields undefined simply omits the corresponding XML attributes. You can still choose to omit the entire shading/run block when the lookup fails to reduce emitted XML, but the current undefined fill/color values shouldn’t cause invalid output.

🤖 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/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/blockNoteDocxBlockProps.ts`
around lines 19 - 43, The current code conditionally omits the entire
shading/run objects when backgroundColor/textColor are 'default' or missing, but
docx can accept undefined subfields; simplify by always returning the shading
object (with type: ShadingType.CLEAR) and the run object while setting
shading.fill to colors[props.backgroundColor]?.background?.slice(1) (or
undefined) and run.color to colors[props.textColor]?.text?.slice(1) (or
undefined), removing the outer ternaries that produce undefined for the whole
shading/run so invalid XML won’t be produced and lookups that miss a color
simply leave the subfield undefined; update the code around the shading and run
expressions (references: shading, ShadingType.CLEAR, run, colors,
props.backgroundColor, props.textColor) accordingly.

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.

1 participant