Skip to content

Commit 16cfde3

Browse files
committed
fix: link preview decorations not showing on the correct node
fixes: #37
1 parent 6254acf commit 16cfde3

2 files changed

Lines changed: 69 additions & 22 deletions

File tree

src/rich-text/plugins/link-preview.ts

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,16 @@ export interface LinkPreviewProvider {
2929
*/
3030
function getValidProvider(
3131
providers: LinkPreviewProvider[],
32-
node: ProsemirrorNode
33-
): { url: string; provider: LinkPreviewProvider } | null {
34-
const n = node.isText ? node : node.content.firstChild;
32+
node: ProsemirrorNode,
33+
parent: ProsemirrorNode
34+
): {
35+
url: string;
36+
provider: LinkPreviewProvider;
37+
} | null {
38+
if (!node?.isText || !parent) {
39+
return null;
40+
}
41+
const n = node; // TODO CLEANUP
3542
const url = n?.marks.find((m) => m.type.name === "link")?.attrs
3643
?.href as string;
3744

@@ -47,7 +54,7 @@ function getValidProvider(
4754
}
4855

4956
// full preview providers require links to be in a paragraph by themselves
50-
if (!provider.textOnly && !isStandalonePreviewableLink(node)) {
57+
if (!provider.textOnly && !isStandalonePreviewableLink(parent)) {
5158
continue;
5259
}
5360

@@ -93,8 +100,8 @@ function getValidNodes(
93100
}[] = [];
94101

95102
// iterate over current document structure
96-
currState.doc.descendants((node, pos) => {
97-
const provider = getValidProvider(providers, node);
103+
currState.doc.descendants((node, pos, parent) => {
104+
const provider = getValidProvider(providers, node, parent);
98105

99106
if (provider) {
100107
validNodes.push({ provider, pos, node });
@@ -143,14 +150,20 @@ function generateRecentPreviewDecorations(
143150
if (!n.isTextOnly && n.content) {
144151
// if the url is in the cache, insert the link preview
145152
linkPreviewDecorations.push(insertLinkPreview(n.pos, n.content));
146-
} else {
147-
const node = doc.nodeAt(n.pos);
153+
} else if (!n.content) {
148154
// otherwise, add the loading styles
155+
// attach the node decorations to the text's parent node
156+
const resolved = doc.resolve(n.pos);
157+
const parentPos = resolved.posAtIndex(0, resolved.depth - 1);
149158
linkPreviewDecorations.push(
150-
Decoration.node(n.pos, n.pos + node.nodeSize, {
151-
class: "is-loading js-link-preview-loading",
152-
title: "Loading...",
153-
})
159+
Decoration.node(
160+
parentPos,
161+
parentPos + resolved.parent.nodeSize,
162+
{
163+
class: "is-loading js-link-preview-loading",
164+
title: "Loading...",
165+
}
166+
)
154167
);
155168
}
156169
});
@@ -173,7 +186,9 @@ function insertLinkPreview(pos: number, content: Node | null) {
173186
placeholder.appendChild(content.cloneNode(true));
174187
}
175188

176-
return Decoration.widget(pos, placeholder);
189+
return Decoration.widget(pos, placeholder, {
190+
side: -1,
191+
});
177192
}
178193

179194
/**

test/rich-text/plugins/link-preview.test.ts

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -79,23 +79,22 @@ describe("link-preview", () => {
7979
textOnly: true,
8080
});
8181

82-
// TODO loading indicator broken - decoration is not valid to be added to node
8382
// check that the loading indicator is shown
84-
// let loadingIndicator = richEditorView.dom.querySelectorAll(
85-
// ".js-link-preview-loading"
86-
// );
87-
// expect(loadingIndicator).toHaveLength(1);
83+
let loadingIndicator = richEditorView.dom.querySelectorAll(
84+
".js-link-preview-loading"
85+
);
86+
expect(loadingIndicator).toHaveLength(1);
8887

8988
resolver(document.createTextNode(textOnlyPreviewText));
9089

9190
// wait for the promise to resolve (immediately) and check that the async content was pulled in
9291
await sleepAsync(0);
9392

9493
// check that the loading indicator is no longer showing
95-
// loadingIndicator = richEditorView.dom.querySelectorAll(
96-
// ".js-link-preview-loading"
97-
// );
98-
// expect(loadingIndicator).toHaveLength(0);
94+
loadingIndicator = richEditorView.dom.querySelectorAll(
95+
".js-link-preview-loading"
96+
);
97+
expect(loadingIndicator).toHaveLength(0);
9998

10099
expect(richEditorView.document).toMatchNodeTree({
101100
childCount: 1,
@@ -115,4 +114,37 @@ describe("link-preview", () => {
115114
],
116115
});
117116
});
117+
118+
it.each([
119+
[
120+
`| Column A | Column B |
121+
| --- | --- |
122+
| [Cell 1](https://example.com/foo/bar) | Cell 2 |
123+
| Cell 3 | Cell 4 |`,
124+
"TD",
125+
],
126+
[`# https://example.com/foo/bar`, "H1"],
127+
])(
128+
"should add rich previews directly before the link in the same parent node",
129+
async (markdown, parentNodeName) => {
130+
const richEditorView = richView(markdown, {
131+
domainTest: /example.com/,
132+
renderer: (url) => {
133+
const promiseContent = document.createElement("div");
134+
promiseContent.textContent = url;
135+
return Promise.resolve(promiseContent);
136+
},
137+
});
138+
139+
// wait for the promise to resolve (immediately) and check that the async content was pulled in
140+
await sleepAsync(0);
141+
142+
const oneboxDom = richEditorView.dom.querySelectorAll(
143+
".js-link-preview-decoration"
144+
);
145+
expect(oneboxDom).toHaveLength(1);
146+
expect(oneboxDom[0].parentElement.nodeName).toBe(parentNodeName);
147+
expect(oneboxDom[0].nextElementSibling.nodeName).toBe("A");
148+
}
149+
);
118150
});

0 commit comments

Comments
 (0)