mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
fix: Improve patch merging of links in table cells (#12614)
* fix: Improve merging of links in table cells through patch * PR feedback
This commit is contained in:
@@ -3,6 +3,7 @@ import * as Y from "yjs";
|
|||||||
import { TextEditMode } from "@shared/types";
|
import { TextEditMode } from "@shared/types";
|
||||||
import { APIUpdateExtension } from "@server/collaboration/APIUpdateExtension";
|
import { APIUpdateExtension } from "@server/collaboration/APIUpdateExtension";
|
||||||
import { Event } from "@server/models";
|
import { Event } from "@server/models";
|
||||||
|
import { parser } from "@server/editor";
|
||||||
import { DocumentHelper } from "@server/models/helpers/DocumentHelper";
|
import { DocumentHelper } from "@server/models/helpers/DocumentHelper";
|
||||||
import { ProsemirrorHelper } from "@server/models/helpers/ProsemirrorHelper";
|
import { ProsemirrorHelper } from "@server/models/helpers/ProsemirrorHelper";
|
||||||
import { buildDocument, buildUser } from "@server/test/factories";
|
import { buildDocument, buildUser } from "@server/test/factories";
|
||||||
@@ -1481,5 +1482,82 @@ describe("documentUpdater", () => {
|
|||||||
"Second item"
|
"Second item"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should apply a link when wrapping existing table cell text", async () => {
|
||||||
|
const user = await buildUser();
|
||||||
|
const document = await buildDocument({ teamId: user.teamId });
|
||||||
|
|
||||||
|
document.content = parser
|
||||||
|
.parse("| Name | Notes |\n|------|-------|\n| Alpha | see |\n")
|
||||||
|
.toJSON();
|
||||||
|
await document.save();
|
||||||
|
|
||||||
|
const result = DocumentHelper.applyMarkdownToDocument(
|
||||||
|
document,
|
||||||
|
"[see](https://example.com/docs)",
|
||||||
|
TextEditMode.Patch,
|
||||||
|
"see"
|
||||||
|
);
|
||||||
|
|
||||||
|
// The cell text is unchanged but should now carry a link mark — it must
|
||||||
|
// not be silently dropped during the merge.
|
||||||
|
const cellText =
|
||||||
|
result.content!.content![0].content![1].content![1].content![0]
|
||||||
|
.content![0];
|
||||||
|
expect(cellText.text).toEqual("see");
|
||||||
|
expect(cellText.marks).toEqual([
|
||||||
|
expect.objectContaining({
|
||||||
|
type: "link",
|
||||||
|
attrs: expect.objectContaining({ href: "https://example.com/docs" }),
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should preserve other table cells when adding a link to one cell", async () => {
|
||||||
|
const user = await buildUser();
|
||||||
|
const document = await buildDocument({ teamId: user.teamId });
|
||||||
|
|
||||||
|
document.content = parser
|
||||||
|
.parse(
|
||||||
|
"| Name | Notes |\n|------|-------|\n| Alpha | see |\n| Beta | other |\n"
|
||||||
|
)
|
||||||
|
.toJSON();
|
||||||
|
await document.save();
|
||||||
|
|
||||||
|
// Capture the untouched (Beta) row BEFORE patching so we can assert its
|
||||||
|
// structure and attrs are preserved exactly, not just its text.
|
||||||
|
const beforeDoc = DocumentHelper.toProsemirror(document).toJSON();
|
||||||
|
const untouchedRow = beforeDoc.content[0].content[2];
|
||||||
|
|
||||||
|
const result = DocumentHelper.applyMarkdownToDocument(
|
||||||
|
document,
|
||||||
|
"see [docs](https://example.com/d)",
|
||||||
|
TextEditMode.Patch,
|
||||||
|
"see"
|
||||||
|
);
|
||||||
|
|
||||||
|
// The patched cell gained the link
|
||||||
|
expect(result.text).toContain("[docs](https://example.com/d)");
|
||||||
|
// The untouched row node must remain identical
|
||||||
|
expect(result.content!.content![0].content![2]).toEqual(untouchedRow);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should apply a mark when wrapping existing list item text", async () => {
|
||||||
|
const user = await buildUser();
|
||||||
|
const document = await buildDocument({ teamId: user.teamId });
|
||||||
|
|
||||||
|
document.content = parser.parse("- clickme\n- other\n").toJSON();
|
||||||
|
await document.save();
|
||||||
|
|
||||||
|
const result = DocumentHelper.applyMarkdownToDocument(
|
||||||
|
document,
|
||||||
|
"[clickme](https://example.com)",
|
||||||
|
TextEditMode.Patch,
|
||||||
|
"clickme"
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.text).toContain("[clickme](https://example.com)");
|
||||||
|
expect(result.text).toContain("other");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -817,8 +817,18 @@ export class DocumentHelper {
|
|||||||
const textSame = oldChild.textContent === newChild.textContent;
|
const textSame = oldChild.textContent === newChild.textContent;
|
||||||
|
|
||||||
if (textSame && oldChild.sameMarkup(newChild)) {
|
if (textSame && oldChild.sameMarkup(newChild)) {
|
||||||
// Fully unchanged — keep original with its rich content
|
// Compare against the round-tripped baseline: when the
|
||||||
merged.push(oldChild);
|
// updated child is identical to a plain round-trip of the original,
|
||||||
|
// the patch did not touch it
|
||||||
|
if (!rtChild || newChild.eq(rtChild)) {
|
||||||
|
merged.push(oldChild);
|
||||||
|
} else if (!oldChild.isTextblock && !oldChild.isLeaf) {
|
||||||
|
// Container child changed deeper down — recurse to preserve rich
|
||||||
|
// content in the parts that did not change.
|
||||||
|
merged.push(DocumentHelper.mergeNodes(oldChild, newChild, rtChild));
|
||||||
|
} else {
|
||||||
|
merged.push(newChild);
|
||||||
|
}
|
||||||
} else if (textSame) {
|
} else if (textSame) {
|
||||||
// Attrs changed (e.g. checked state) but content same — merge attrs
|
// Attrs changed (e.g. checked state) but content same — merge attrs
|
||||||
// so that non-markdown-representable values (colwidth, highlight
|
// so that non-markdown-representable values (colwidth, highlight
|
||||||
|
|||||||
Reference in New Issue
Block a user