Skip to content

fix(tracked-changes): preserve format on export and import#2362

Open
palmer-cl wants to merge 3 commits intomainfrom
colep/sd-2023-bug-tracked-formatting-changes-are-dropped-on-export
Open

fix(tracked-changes): preserve format on export and import#2362
palmer-cl wants to merge 3 commits intomainfrom
colep/sd-2023-bug-tracked-formatting-changes-are-dropped-on-export

Conversation

@palmer-cl
Copy link
Collaborator

  • Preserve formatting changes on export
  • Add tests for multiple types of format preservation

@linear
Copy link

linear bot commented Mar 11, 2026

@github-actions
Copy link
Contributor

Status: FAIL

The core structural changes look correct — wrapping <w:rPrChange> children inside <w:rPr> as required by the spec (§17.13.5.31), and preserving <w:rPrChange> nodes during run property replacement. Two issues:


1. w:authorEmail is not a valid attribute on w:rPrChange

helpers.js:40 and asserted in r-translator.test.js:399:

// helpers.js
'w:authorEmail': trackStyleMark.attrs.authorEmail,

// r-translator.test.js
expect(runPropertyChange?.attributes).toMatchObject({
  'w:authorEmail': 'reviewer@example.com',
  ...
});

Per §17.13.5.31, w:rPrChange accepts exactly three attributes: w:id, w:author, and w:date. There is no w:authorEmail attribute defined on any revision/annotation element in the spec. Word will likely silently ignore this attribute, but it's still non-conformant XML. See https://ooxml.dev/spec?q=rPrChange for the full attribute table.


2. w:sz value type mismatch in test

r-translator.test.js:425:

expect(sizeNode?.attributes?.['w:val']).toBe(24);  // number

But the input fixture in markImporter.test.js:232 (and the OOXML spec) stores it as a string:

{ name: 'w:sz', attributes: { 'w:val': '24' } }

w:sz/@w:val is typed as ST_HpsMeasure (an unsigned integer serialized as a string in XML). If the implementation is emitting the attribute value as a JS number 24 rather than string '24', that's a round-trip fidelity problem — though an XML serializer would likely coerce it either way, the test asserting toBe(24) (strict equality, number) suggests the importer and exporter are inconsistently handling the type. Worth aligning these.

Copy link
Contributor

@caio-pizzol caio-pizzol left a comment

Choose a reason for hiding this comment

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

@colep approach is clean, no major issues.

two small DX suggestions inline — nothing blocking.

on tests: would be good to add coverage for (1) a text node with only a trackFormat mark and no other formatting, and (2) a run that already has a w:rPrChange surviving through replaceRunProps. both cover branches that would silently break without a test catching it. left inline comments.

if (runPropertiesNode) {
if (!Array.isArray(runPropertiesNode.elements)) runPropertiesNode.elements = [];

const existingChangeKeys = new Set(
Copy link
Contributor

Choose a reason for hiding this comment

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

the dedup logic here can't actually trigger — the template never contains w:rPrChange nodes, so the Set is always empty. could just append directly without the key check.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Would the preserved changes ever have duplicates we need to account for? or safe to just put them all in?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants