Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix style being missed on patchDocument #2387

Merged
merged 2 commits into from
Nov 1, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Fix style being missed on patchDocument
There were scenarios in which patching a document would result in loss
of style for the template runs and, possibly, their right-adjacent run
as well (post-splitting). That was due to the run style elements not
being added to the newly created runs.

This commit addresses this issue by introducing a new, optional, flag
for the `patchDocument` function: `keepOriginalStyles`. It defaults to
`false` (current behavior) and, when `true`, ensures that there is no
loss of styling.

This should address #2293.
  • Loading branch information
wilkmaia committed Oct 18, 2023
commit a89ee463e68d132538e738e886bfa19ad26b37fb
2 changes: 2 additions & 0 deletions src/patcher/from-docx.ts
Original file line number Diff line number Diff line change
@@ -49,6 +49,7 @@ export type IPatch = ParagraphPatch | FilePatch;

export interface PatchDocumentOptions {
readonly patches: { readonly [key: string]: IPatch };
readonly keepOriginalStyles?: boolean;
}

const imageReplacer = new ImageReplacer();
@@ -128,6 +129,7 @@ export const patchDocument = async (data: InputDataType, options: PatchDocumentO
patchText,
renderedParagraphs,
context,
options.keepOriginalStyles,
);
}

109 changes: 109 additions & 0 deletions src/patcher/replacer.spec.ts
Original file line number Diff line number Diff line change
@@ -44,6 +44,28 @@ const MOCK_JSON = {
},
],
},
{
type: "element",
name: "w:p",
elements: [
{
type: "element",
name: "w:r",
elements: [
{
type: "element",
name: "w:rPr",
elements: [{ type: "element", name: "w:b", attributes: { "w:val": "1" } }],
},
{
type: "element",
name: "w:t",
elements: [{ type: "text", text: "What a {{bold}} text!" }],
},
],
},
],
},
],
},
],
@@ -115,6 +137,93 @@ describe("replacer", () => {
expect(JSON.stringify(output)).to.contain("Delightful Header");
});

it("should replace paragraph type keeping original styling if keepOriginalStyles is true", () => {
const output = replacer(
MOCK_JSON,
{
type: PatchType.PARAGRAPH,
children: [new TextRun("sweet")],
},
"{{bold}}",
[
{
text: "What a {{bold}} text!",
runs: [
{
text: "What a {{bold}} text!",
parts: [{ text: "What a {{bold}} text!", index: 1, start: 0, end: 21 }],
index: 0,
start: 0,
end: 21,
},
],
index: 0,
path: [0, 0, 1],
},
],
{
file: {} as unknown as File,
viewWrapper: {
Relationships: {},
} as unknown as IViewWrapper,
stack: [],
},
true,
);

expect(JSON.stringify(output)).to.contain("sweet");
expect(output.elements![0].elements![1].elements).toMatchObject([
{
type: "element",
name: "w:r",
elements: [
{
type: "element",
name: "w:rPr",
elements: [{ type: "element", name: "w:b", attributes: { "w:val": "1" } }],
},
{
type: "element",
name: "w:t",
elements: [{ type: "text", text: "What a " }],
},
],
},
{
type: "element",
name: "w:r",
elements: [
{
type: "element",
name: "w:rPr",
elements: [{ type: "element", name: "w:b", attributes: { "w:val": "1" } }],
},
{
type: "element",
name: "w:t",
elements: [{ type: "text", text: "sweet" }],
},
],
},
{
type: "element",
name: "w:r",
elements: [
{
type: "element",
name: "w:rPr",
elements: [{ type: "element", name: "w:b", attributes: { "w:val": "1" } }],
},
{
type: "element",
name: "w:t",
elements: [{ type: "text", text: " text!" }],
},
],
},
]);
});

it("should replace document type", () => {
const output = replacer(
MOCK_JSON,
25 changes: 23 additions & 2 deletions src/patcher/replacer.ts
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@ export const replacer = (
patchText: string,
renderedParagraphs: readonly IRenderedParagraphNode[],
context: IContext,
keepOriginalStyles: boolean = false,
): Element => {
for (const renderedParagraph of renderedParagraphs) {
const textJson = patch.children
@@ -47,9 +48,29 @@ export const replacer = (

const index = findRunElementIndexWithToken(paragraphElement, SPLIT_TOKEN);

const { left, right } = splitRunElement(paragraphElement.elements![index], SPLIT_TOKEN);
const runElementToBeReplaced = paragraphElement.elements![index];
const { left, right } = splitRunElement(runElementToBeReplaced, SPLIT_TOKEN);

let newRunElements = textJson;
let patchedRightElement = right;

if (keepOriginalStyles) {
const runElementNonTextualElements =
runElementToBeReplaced.elements?.filter((e) => e.type === "element" && e.name !== "w:t") ?? [];

newRunElements = textJson.map((e) => ({
...e,
elements: [...runElementNonTextualElements, ...(e.elements ?? [])],
}));

patchedRightElement = {
...right,
elements: [...runElementNonTextualElements, ...(right.elements ?? [])],
};
}

// eslint-disable-next-line functional/immutable-data
paragraphElement.elements!.splice(index, 1, left, ...textJson, right);
paragraphElement.elements!.splice(index, 1, left, ...newRunElements, patchedRightElement);
break;
}
}