Skip to content

Commit

Permalink
fix: block anchors attached to code blocks in publishing (#1267)
Browse files Browse the repository at this point in the history
* fix: block anchors attached to code blocks in publishing

* fix: wrap top-level anchor to avoid on hover of all content

Without wrapping, the top-level anchors we install for code blocks would always appear wherever the user was hovering over on the page. Now it will only appear when the user hovers over where the anchor should be. This makes them less discoverable on published pages, but I can't find a better solution.

* spike: fix bad imports
  • Loading branch information
Kaan Genç committed Sep 2, 2021
1 parent 6498972 commit 6b3c71c
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 11 deletions.
4 changes: 2 additions & 2 deletions packages/engine-server/src/markdown/remark/blockAnchors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,12 @@ function attachCompiler(proc: Unified.Processor, _opts?: PluginOpts) {
if (visitors) {
visitors.blockAnchor = function (node: BlockAnchor): string | Element {
const { dest } = MDUtilsV4.getDendronData(proc);
const fullId = node.id;
switch (dest) {
case DendronASTDest.MD_DENDRON:
case DendronASTDest.MD_REGULAR:
return `^${node.id}`;
return fullId;
case DendronASTDest.MD_ENHANCED_PREVIEW:
const fullId = node.id;
return `<a aria-hidden="true" class="block-anchor anchor-heading" id="${fullId}" href="#${fullId}">^${fullId}</a>`;
default:
throw new DendronError({ message: "Unable to render block anchor" });
Expand Down
34 changes: 27 additions & 7 deletions packages/engine-server/src/markdown/remark/dendronPub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
userTag2WikiLinkNoteV4,
} from "./utils";
import Unified, { Transformer } from "unified";
import { Node, Parent } from "unist";
import { Node } from "unist";
import u from "unist-builder";
import visitParents from "unist-util-visit-parents";
import { VFile } from "vfile";
Expand Down Expand Up @@ -283,7 +283,7 @@ function plugin(this: Unified.Processor, opts?: PluginOpts): Transformer {
node as BlockAnchor,
procOpts.blockAnchorsOpts
);
let target: Parent | undefined;
let target: Node | undefined;
const grandParent = ancestors[ancestors.length - 2];
if (
RemarkUtils.isParagraph(parent) &&
Expand All @@ -294,9 +294,13 @@ function plugin(this: Unified.Processor, opts?: PluginOpts): Transformer {
// If the block anchor is at the top level, then it references the block before it
const parentIndex = _.indexOf(grandParent.children, parent);
const previous = grandParent.children[parentIndex - 1];
if (_.isUndefined(previous) || !RemarkUtils.isParent(previous))
return; // invalid anchor, doesn't represent anything
target = previous;
if (_.isUndefined(previous)) {
// Block anchor at the very start of the note, just add anchor to the start
target = grandParent;
} else {
// There's an actual block before the anchor
target = previous;
}
} else if (RemarkUtils.isTableRow(grandParent)) {
// An anchor inside a table references the whole table.
const greatGrandParent = ancestors[ancestors.length - 3];
Expand All @@ -311,13 +315,29 @@ function plugin(this: Unified.Processor, opts?: PluginOpts): Transformer {
// Otherwise, it references the block it's inside
target = parent;
}

if (_.isUndefined(target)) return;
if (RemarkUtils.isList(target)) {
// Can't install as a child of the list, has to go into a list item
target = target.children[0];
}
// Install the block anchor at the target node
target.children.unshift(anchorHTML);

if (RemarkUtils.isParent(target)) {
// Install the block anchor at the target node
target.children.unshift(anchorHTML);
} else if (RemarkUtils.isRoot(target)) {
// If the anchor is the first thing in the note, anchorHTML goes to the start of the document
target.children.unshift(anchorHTML);
} else if (RemarkUtils.isParent(grandParent)) {
// For some elements (for example code blocks) we can't install the block anchor on them.
// In that case we at least put a link before the element so that the link will at least work.
const targetIndex = _.indexOf(grandParent.children, target);
const targetWrapper = paragraph([
anchorHTML,
grandParent.children[targetIndex],
]);
grandParent.children.splice(targetIndex, 1, targetWrapper);
}
// Remove the block anchor itself since we install the anchor at the target
const index = _.indexOf(parent.children, node);
parent!.children.splice(index, 1);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`blockAnchors rendering compile "HTML: after code block" 1`] = `
VFile {
"contents": "<h1 id=\\"root\\"><a aria-hidden=\\"true\\" class=\\"anchor-heading\\" href=\\"#root\\"><svg aria-hidden=\\"true\\" viewBox=\\"0 0 16 16\\"><use xlink:href=\\"#svg-link\\"></use></svg></a>Root</h1>
<p><a aria-hidden=\\"true\\" class=\\"block-anchor anchor-heading\\" id=\\"^my-block-anchor-0\\" href=\\"#^my-block-anchor-0\\"><svg viewBox=\\"0 0 16 16\\"><use xlink:href=\\"#svg-link\\"></use></svg></a></p><pre><code>const x = 1;
</code></pre><p></p>
<p></p>
<hr>
<h2 id=\\"children\\"><a aria-hidden=\\"true\\" class=\\"anchor-heading\\" href=\\"#children\\"><svg aria-hidden=\\"true\\" viewBox=\\"0 0 16 16\\"><use xlink:href=\\"#svg-link\\"></use></svg></a>Children</h2>
<ol>
<li><a href=\\"bar.html\\">Bar</a></li>
<li><a href=\\"foo.html\\">Foo</a></li>
</ol>",
"cwd": "<PROJECT_ROOT>",
"data": Object {},
"history": Array [],
"messages": Array [],
}
`;
exports[`blockAnchors rendering compile "HTML: end of paragraph" 1`] = `
VFile {
"contents": "<h1 id=\\"root\\"><a aria-hidden=\\"true\\" class=\\"anchor-heading\\" href=\\"#root\\"><svg aria-hidden=\\"true\\" viewBox=\\"0 0 16 16\\"><use xlink:href=\\"#svg-link\\"></use></svg></a>Root</h1>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,43 @@ describe("blockAnchors", () => {
preSetupHook: ENGINE_HOOKS.setupBasic,
});

const ALL_TEST_CASES = [...SIMPLE, ...END_OF_PARAGRAPH];
const AFTER_CODE_BLOCK = createProcTests({
name: "after code block",
setupFunc: async ({ engine, vaults, extra }) => {
const proc2 = createProcForTest({
engine,
dest: extra.dest,
vault: vaults[0],
});
const resp = await proc2.process(
["```", "const x = 1;", "```", "", anchor].join("\n")
);
return { resp };
},
verifyFuncDict: {
[DendronASTDest.HTML]: async ({ extra }) => {
const { resp } = extra;
expect(resp).toMatchSnapshot();
return [
{
actual: await AssertUtils.assertInString({
body: resp.toString(),
match: ["<a", `href="#${anchor}"`, `id="${anchor}"`, "</a>"],
nomatch: ["visibility: hidden"],
}),
expected: true,
},
];
},
},
preSetupHook: ENGINE_HOOKS.setupBasic,
});

const ALL_TEST_CASES = [
...SIMPLE,
...END_OF_PARAGRAPH,
...AFTER_CODE_BLOCK,
];
runAllTests({ name: "compile", testCases: ALL_TEST_CASES });
});
});
7 changes: 6 additions & 1 deletion test-workspace/vault/dendron.ref.links.block-anchors.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
id: links.block-anchors
title: Block Anchors
desc: ""
updated: 1608147857388
updated: 1630537995313
created: 1608147795766
---

Expand All @@ -23,6 +23,11 @@ Voluptatem ipsum et possimus aut. In modi quaerat temporibus. ^last-paragraph
| Laborum | libero |
| Ullam | optio | ^table

```js
const x = 0;
```
^code

## Mollitia ipsum et velit quia vel


Expand Down

0 comments on commit 6b3c71c

Please sign in to comment.