Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
23 changes: 20 additions & 3 deletions src/rich-text/commands/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { setBlockType, toggleMark, wrapIn } from "prosemirror-commands";
import { redo, undo } from "prosemirror-history";
import { Mark, MarkType, NodeType, Schema } from "prosemirror-model";
import { EditorState, TextSelection, Transaction } from "prosemirror-state";
import {
Command,
EditorState,
TextSelection,
Transaction,
} from "prosemirror-state";
import { liftTarget } from "prosemirror-transform";
import { EditorView } from "prosemirror-view";
import {
Expand Down Expand Up @@ -35,8 +40,20 @@ import { _t } from "../../shared/localization";

export * from "./tables";

//TODO
function toggleWrapIn(nodeType: NodeType) {
/**
* Builds a command which wraps/unwraps the current selection with the passed in node type
* @param nodeType the type of node to wrap the selection in
* @returns A command to toggle the wrapper node
* Commands are functions that take a state and an optional
* transaction dispatch function and...
*
* - determine whether they apply to this state
* - if not, return false
* - if `dispatch` was passed, perform their effect, possibly by
* passing a transaction to `dispatch`
* - return true
*/
export function toggleWrapIn(nodeType: NodeType): Command {
const nodeCheck = nodeTypeActive(nodeType);
const wrapInCommand = wrapIn(nodeType);

Expand Down
2 changes: 1 addition & 1 deletion src/rich-text/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ const nodes: {
},

list_item: {
content: "paragraph block*",
content: "block+",

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The only change to allow blockquotes in list items, is from forcing the content to have the first child be a paragraph with 0 or more block decadents, to having one or more of any block level element

@b-kelly b-kelly Jul 20, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Just double checked the commonmark spec for absolute correctness™, and it seems that list items allow basically any block element that isn't a horizontal rule, so this is good to go.

https://spec.commonmark.org/0.30/#list-items

defining: true,
parseDOM: [{ tag: "li" }],
toDOM() {
Expand Down
34 changes: 12 additions & 22 deletions test/matchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,33 +133,23 @@ function expectNodeTree(doc: ProsemirrorNode, tree: CompareTree): void {
* @returns a CompareTree to be used with `toMatchNodeTree`
*/
export function createBasicNodeTree(input: string): CompareTree {
const branches = input
.split(">")
.map((x) => x.trim())
.reverse();
const branches = input.split(">").map((x) => x.trim());

if (!branches.length) return {};

let branch = branches.pop();
const root: CompareTree = {
"type.name": branch,
"type.name": "doc",
};

branch = branches.pop();
let node: CompareTree = root;
while (branch) {
if (isNaN(Number(branch))) {
const child = {
"type.name": branch,
};
node.content = [child];

node = child;
branch = branches.pop();
} else {
node.childCount = Number(branch);
return root;
}
let tree = root;

for (const branch of branches) {
const child = {
"type.name": branch,
};
tree.content = [child];
tree = child;
}

return root;
}

Expand Down
65 changes: 65 additions & 0 deletions test/rich-text/commands/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
exitInclusiveMarkCommand,
insertHorizontalRuleCommand,
toggleHeadingLevel,
toggleWrapIn,
} from "../../../src/rich-text/commands";
import {
applySelection,
Expand Down Expand Up @@ -194,6 +195,70 @@ describe("commands", () => {
});
});

describe("toggleWrapIn", () => {
it("should apply blockquote within paragraph", () => {
const state = applySelection(createState("<p>quote</p>", []), 3);
expect(state.doc).toMatchNodeTreeString("paragraph>text");

const toggleBlockQuote = toggleWrapIn(
state.schema.nodes.blockquote
);
const { newState, isValid } = executeTransaction(
state,
toggleBlockQuote
);

expect(isValid).toBeTruthy();
expect(newState.doc).toMatchNodeTreeString(
"blockquote>paragraph>text"
);
});

it("should remove blockquote within blockquote", () => {
const state = applySelection(
createState("<blockquote>quote</blockquote>", []),
3
);
expect(state.doc).toMatchNodeTreeString(
"blockquote>paragraph>text"
);

const toggleBlockQuote = toggleWrapIn(
state.schema.nodes.blockquote
);
const { newState, isValid } = executeTransaction(
state,
toggleBlockQuote
);

expect(isValid).toBeTruthy();
expect(newState.doc).toMatchNodeTreeString("paragraph>text");
});

it("should toggle blockquote within list item", () => {
const state = applySelection(
createState("<ul><li>list</li></ul>", []),
3
);
const resolvedNode = state.selection.$from;
// default li child is paragraph
expect(resolvedNode.node().type.name).toBe("paragraph");

const toggleBlockQuote = toggleWrapIn(
state.schema.nodes.blockquote
);
const { newState, isValid } = executeTransaction(
state,
toggleBlockQuote
);

expect(isValid).toBeTruthy();
expect(newState.doc).toMatchNodeTreeString(
"bullet_list>list_item>blockquote>paragraph>text"
);
});
});

describe("insertHorizontalRuleCommand", () => {
it("should not insert while in a table", () => {
const state = applySelection(
Expand Down
17 changes: 17 additions & 0 deletions test/shared/markdown-parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,23 @@ console.log("test");
expect(doc.content[0].type).toContain("_list");
expect(doc.content[0].attrs.tight).toBe(isTight);
});

it.each([
[`- > blockquote in list`, "bullet_list>list_item>blockquote"],
[`- paragraph in list`, "bullet_list>list_item>paragraph"],
[`- # heading in list`, "bullet_list>list_item>heading"],
[
`- ~~~\n code in list\n ~~~`,
"bullet_list>list_item>code_block",
],
[`- - list in list`, "bullet_list>list_item>bullet_list"],
])(
"should parse lists with direct block children",
(input, expected) => {
const doc = markdownParser.parse(input);
expect(doc).toMatchNodeTreeString(expected);
}
);
});

describe("reference links", () => {
Expand Down
30 changes: 30 additions & 0 deletions test/shared/markdown-serializer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,36 @@ describe("markdown-serializer", () => {

it.todo("should serialize elements from complex html markup");

it.each([
[
`<ul><li><blockquote><p>blockquote in list</p></blockquote></li></ul>`,
`- > blockquote in list`,
],
[`<ul><li><p>paragraph in list</p></li></ul>`, `- paragraph in list`],
[`<ul><li><h1>heading in list</h1></li></ul>`, `- # heading in list`],
[
`<ul>
<li>
<pre class="hljs"><code>code in list</code></pre>
</li>
</ul>`,
`- code in list`,
],
[
`<ul>
<li>
<ul>
<li>list in list</li>
</ul>
</li>
</ul>`,
`- - list in list`,
],
])("should serialize lists containing block children", (input, output) => {
const view = nonMarkdownRichView(input);
expect(view.content).toBe(output);
});

// TODO lots of complicated cases in the spec
// see https://spec.commonmark.org/0.30/#reference-link
// see https://spec.commonmark.org/0.30/#link-reference-definitions
Expand Down