diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cc494392d4..b50cf9ca34 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,7 +38,7 @@ jobs: uses: actions/cache@v2 with: path: ~/.cache/ms-playwright - key: pw-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }} + key: pw-new-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }} - name: Install Dependencies run: npm install diff --git a/packages/core/src/extensions/Blocks/BlockAttributes.ts b/packages/core/src/extensions/Blocks/BlockAttributes.ts index b31bc6bfc3..4109c7bb6e 100644 --- a/packages/core/src/extensions/Blocks/BlockAttributes.ts +++ b/packages/core/src/extensions/Blocks/BlockAttributes.ts @@ -1,12 +1,10 @@ // Object containing all possible block attributes. const BlockAttributes: Record = { - listType: "data-list-type", blockColor: "data-block-color", blockStyle: "data-block-style", - headingType: "data-heading-type", id: "data-id", depth: "data-depth", depthChange: "data-depth-change", }; -export default BlockAttributes; \ No newline at end of file +export default BlockAttributes; diff --git a/packages/core/src/extensions/Blocks/OrderedListPlugin.ts b/packages/core/src/extensions/Blocks/OrderedListPlugin.ts deleted file mode 100644 index d01ccbd31f..0000000000 --- a/packages/core/src/extensions/Blocks/OrderedListPlugin.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Plugin, PluginKey } from "prosemirror-state"; - -const PLUGIN_KEY = new PluginKey(`ordered-list`); - -export const OrderedListPlugin = () => { - return new Plugin({ - key: PLUGIN_KEY, - appendTransaction: (_transactions, _oldState, newState) => { - const newTr = newState.tr; - let modified = false; - let skip = 0; - - let countPerBlockGroup = new Map(); - - newState.doc.descendants((node, pos, parent) => { - if (node.type.name === "block" && !node.attrs.listType) { - // reset for non consecutive oli blocks - countPerBlockGroup.set(parent, 1); - } - if ( - skip === 0 && - node.type.name === "block" && - node.attrs.listType === "oli" - ) { - skip = node.content.childCount; - - // create count for block group if not exist - if (!countPerBlockGroup.has(parent)) { - countPerBlockGroup.set(parent, 1); - } - - let count = countPerBlockGroup.get(parent); - - // This assumes that the content node is always the first child of the oli block, - // as the content model grows this assumption may need to change - if (node.content.child(0).attrs.position !== `${count}.`) { - // TODO: @DAlperin currently sub-items just continue from the order of the parent, - // sub-items should be ordered separately with letters or roman numerals or some such - newTr.setNodeMarkup(pos + 1, undefined, { - ...node.attrs, - position: `${count}.`, - }); - modified = true; - } - - countPerBlockGroup.set(parent, count + 1); - } else if (skip > 0) { - skip--; - } - }); - if (modified) { - return newTr; - } - return null; - }, - }); -}; diff --git a/packages/core/src/extensions/Blocks/PreviousBlockTypePlugin.ts b/packages/core/src/extensions/Blocks/PreviousBlockTypePlugin.ts index 1db218a4e3..c5da942b3a 100644 --- a/packages/core/src/extensions/Blocks/PreviousBlockTypePlugin.ts +++ b/packages/core/src/extensions/Blocks/PreviousBlockTypePlugin.ts @@ -5,18 +5,22 @@ import { } from "@tiptap/core"; import { Plugin, PluginKey } from "prosemirror-state"; import { Decoration, DecorationSet } from "prosemirror-view"; -import BlockAttributes from "./BlockAttributes"; const PLUGIN_KEY = new PluginKey(`previous-blocks`); -// Inserts "prev-" string into an HTML attribute name with a "data-" prefix, e.g. "data-depth" -> "data-prev-depth". -// Assumes "data-" prefix is in the attribute name. -const insertPrev = (attr: string) => attr.slice(0, 5) + "prev-" + attr.slice(5); +const nodeAttributes: Record = { + listItemType: "list-item-type", + listItemIndex: "list-item-index", + headingLevel: "heading-level", + type: "type", + depth: "depth", + "depth-change": "depth-change", +}; /** * This plugin tracks transformation of Block node attributes, so we can support CSS transitions. * - * Problem it solves: Prosemirror recreates the DOM when transactions happen. So when a transaction changes an Node attribute, + * Problem it solves: ProseMirror recreates the DOM when transactions happen. So when a transaction changes a Node attribute, * it results in a completely new DOM element. This means CSS transitions don't work. * * Solution: When attributes change on a node, this plugin sets a data-* attribute with the "previous" value. This way we can still use CSS transitions. (See block.module.css) @@ -75,7 +79,6 @@ export const PreviousBlockTypePlugin = () => { changes.forEach(() => { const oldNodes = findChildren(oldState.doc, (node) => node.attrs.id); - const oldNodesById = new Map( oldNodes.map((node) => [node.node.attrs.id, node]) ); @@ -84,28 +87,68 @@ export const PreviousBlockTypePlugin = () => { for (let node of newNodes) { const oldNode = oldNodesById.get(node.node.attrs.id); - if (oldNode) { + const oldContentNode = oldNode?.node.firstChild; + const newContentNode = node.node.firstChild; + if (oldNode && oldContentNode && newContentNode) { const newAttrs = { - listType: node.node.attrs.listType, - blockColor: node.node.attrs.blockColor, - blockStyle: node.node.attrs.blockStyle, - headingType: node.node.attrs.headingType, + listItemType: newContentNode.attrs.listItemType, + listItemIndex: newContentNode.attrs.listItemIndex, + headingLevel: newContentNode.attrs.headingLevel, + type: newContentNode.type.name, depth: newState.doc.resolve(node.pos).depth, }; const oldAttrs = { - listType: oldNode.node.attrs.listType, - blockColor: oldNode.node.attrs.blockColor, - blockStyle: oldNode.node.attrs.blockStyle, - headingType: oldNode.node.attrs.headingType, + listItemType: oldContentNode.attrs.listItemType, + listItemIndex: oldContentNode.attrs.listItemIndex, + headingLevel: oldContentNode.attrs.headingLevel, + type: oldContentNode.type.name, depth: oldState.doc.resolve(oldNode.pos).depth, }; + // Hacky fix to avoid processing certain transactions created by ordered list indexing plugin. + + // True when an existing ordered list item is assigned an index for the first time, which happens + // immediately after it's created. Using this condition to start an animation ensures it's not + // immediately overridden by a different transaction created by the ordered list indexing plugin. + const indexInitialized = + oldAttrs.listItemIndex === null && + newAttrs.listItemIndex !== null; + + // True when an existing ordered list item changes nesting levels, before its index is updated by the + // ordered list indexing plugin. This condition ensures that animations for indentation still work with + // ordered list items, while preventing unnecessary animations being done when dragging/dropping them. + const depthChanged = + oldAttrs.listItemIndex !== null && + newAttrs.listItemIndex !== null && + oldAttrs.listItemIndex === newAttrs.listItemIndex; + + // Only false for transactions in which the block remains an ordered list item before & after, but neither + // of the previous conditions apply. + const shouldUpdate = + oldAttrs.listItemType === "ordered" && + newAttrs.listItemType === "ordered" + ? indexInitialized || depthChanged + : true; + if ( - JSON.stringify(oldAttrs) !== JSON.stringify(newAttrs) // TODO: faster deep equal? + JSON.stringify(oldAttrs) !== JSON.stringify(newAttrs) && // TODO: faster deep equal? + shouldUpdate ) { - (oldAttrs as any).depthChange = oldAttrs.depth - newAttrs.depth; + (oldAttrs as any)["depth-change"] = + oldAttrs.depth - newAttrs.depth; prev.prevBlockAttrs[node.node.attrs.id] = oldAttrs; + + // for debugging: + console.log( + "id:", + node.node.attrs.id, + "previousBlockTypePlugin changes detected, oldAttrs", + oldAttrs, + "new", + newAttrs + ); + prev.needsUpdate = true; } } @@ -119,6 +162,7 @@ export const PreviousBlockTypePlugin = () => { decorations(state) { const pluginState = (this as Plugin).getState(state); if (!pluginState.needsUpdate) { + // console.log("0"); return undefined; } @@ -126,18 +170,27 @@ export const PreviousBlockTypePlugin = () => { state.doc.descendants((node, pos) => { if (!node.attrs.id) { + // console.log("1"); return; } const prevAttrs = pluginState.prevBlockAttrs[node.attrs.id]; if (!prevAttrs) { + // console.log("2"); return; } const decorationAttributes: any = {}; for (let [nodeAttr, val] of Object.entries(prevAttrs)) { - decorationAttributes[insertPrev(BlockAttributes[nodeAttr])] = + decorationAttributes["data-prev-" + nodeAttributes[nodeAttr]] = val || "none"; } + + // for debugging: + console.log( + "previousBlockTypePlugin committing decorations", + decorationAttributes + ); + const decoration = Decoration.node(pos, pos + node.nodeSize, { ...decorationAttributes, }); diff --git a/packages/core/src/extensions/Blocks/commands/joinBackward.ts b/packages/core/src/extensions/Blocks/commands/joinBackward.ts deleted file mode 100644 index 8731e207d2..0000000000 --- a/packages/core/src/extensions/Blocks/commands/joinBackward.ts +++ /dev/null @@ -1,274 +0,0 @@ -import { Fragment, Node, ResolvedPos, Slice } from "prosemirror-model"; -import { EditorState, NodeSelection, Selection } from "prosemirror-state"; -import { - canJoin, - liftTarget, - ReplaceAroundStep, - replaceStep, -} from "prosemirror-transform"; - -import { Command, TextSelection, Transaction } from "prosemirror-state"; -import { ReplaceStep } from "prosemirror-transform"; - -/** - * Code taken from https://github.com/ProseMirror/prosemirror-commands/blob/97a8cb5fac25e697d4693ce343e2e3b020a7fa2f/src/commands.ts - * Reason for modification: https://github.com/YousefED/BlockNote/pull/11 - * - * BlockA - * BlockB - * Order of behavior has been switched to make first and second blocks content - * merge before trying to add second block as child of first - * - * behavior responsible for joining BlockB as A child of BlockA moved to (line 379 - 393 original file) after - * behavior responsible for joining content of BlockA and BlockB (line 402 - 422 original file) - */ -export const joinBackward: Command = (state, dispatch, view) => { - let { $cursor } = state.selection as TextSelection; - if ( - !$cursor || - (view ? !view.endOfTextblock("backward", state) : $cursor.parentOffset > 0) - ) { - return false; - } - - let $cut = findCutBefore($cursor); - - // If there is no node before this, try to lift - if (!$cut) { - let range = $cursor.blockRange(), - target = range && liftTarget(range); - if (target === null) { - return false; - } - if (dispatch) { - dispatch(state.tr.lift(range!, target).scrollIntoView()); - } - return true; - } - - let before = $cut.nodeBefore!; - // Apply the joining algorithm - if (!before.type.spec.isolating && deleteBarrier(state, $cut, dispatch)) { - return true; - } - - // If the node below has no content and the node above is - // selectable, delete the node below and select the one above. - if ( - $cursor.parent.content.size === 0 && - (textblockAt(before, "end") || NodeSelection.isSelectable(before)) - ) { - let delStep = replaceStep( - state.doc, - $cursor.before(), - $cursor.after(), - Slice.empty - ); - if ( - delStep && - (delStep as ReplaceStep).slice.size < - (delStep as ReplaceStep).to - (delStep as ReplaceStep).from - ) { - if (dispatch) { - let tr = state.tr.step(delStep); - tr.setSelection( - textblockAt(before, "end") - ? Selection.findFrom( - tr.doc.resolve(tr.mapping.map($cut.pos, -1)), - -1 - )! - : NodeSelection.create(tr.doc, $cut.pos - before.nodeSize) - ); - dispatch(tr.scrollIntoView()); - } - return true; - } - } - - // If the node before is an atom, delete it - if (before.isAtom && $cut.depth === $cursor.depth - 1) { - if (dispatch) { - dispatch( - state.tr.delete($cut.pos - before.nodeSize, $cut.pos).scrollIntoView() - ); - } - return true; - } - - return false; -}; - -function findCutBefore($pos: ResolvedPos): ResolvedPos | null { - if (!$pos.parent.type.spec.isolating) { - for (let i = $pos.depth - 1; i >= 0; i--) { - if ($pos.index(i) > 0) { - return $pos.doc.resolve($pos.before(i + 1)); - } - if ($pos.node(i).type.spec.isolating) { - break; - } - } - } - return null; -} - -function deleteBarrier( - state: EditorState, - $cut: ResolvedPos, - dispatch: ((tr: Transaction) => void) | undefined -) { - let before = $cut.nodeBefore!, - after = $cut.nodeAfter!, - conn, - match; - if (before.type.spec.isolating || after.type.spec.isolating) { - return false; - } - if (joinMaybeClear(state, $cut, dispatch)) { - return true; - } - - let canDelAfter = $cut.parent.canReplace($cut.index(), $cut.index() + 1); - - let selAfter = Selection.findFrom($cut, 1); - let range = selAfter && selAfter.$from.blockRange(selAfter.$to), - target = range && liftTarget(range); - if (target != null && target >= $cut.depth) { - if (dispatch) { - dispatch(state.tr.lift(range!, target).scrollIntoView()); - } - return true; - } - - if ( - canDelAfter && - textblockAt(after, "start", true) && - textblockAt(before, "end") - ) { - let at = before, - wrap = []; - for (;;) { - wrap.push(at); - if (at.isTextblock) { - break; - } - at = at.lastChild!; - } - let afterText = after, - afterDepth = 1; - for (; !afterText.isTextblock; afterText = afterText.firstChild!) { - afterDepth++; - } - if (at.canReplace(at.childCount, at.childCount, afterText.content)) { - if (dispatch) { - let end = Fragment.empty; - for (let i = wrap.length - 1; i >= 0; i--) { - end = Fragment.from(wrap[i].copy(end)); - } - let tr = state.tr.step( - new ReplaceAroundStep( - $cut.pos - wrap.length, - $cut.pos + after.nodeSize, - $cut.pos + afterDepth, - $cut.pos + after.nodeSize - afterDepth, - new Slice(end, wrap.length, 0), - 0, - true - ) - ); - dispatch(tr.scrollIntoView()); - } - return true; - } - } - - if ( - canDelAfter && - (conn = (match = before.contentMatchAt(before.childCount)).findWrapping( - after.type - )) && - match.matchType(conn[0] || after.type)!.validEnd - ) { - if (dispatch) { - let end = $cut.pos + after.nodeSize, - wrap = Fragment.empty; - for (let i = conn.length - 1; i >= 0; i--) { - wrap = Fragment.from(conn[i].create(null, wrap)); - } - wrap = Fragment.from(before.copy(wrap)); - let tr = state.tr.step( - new ReplaceAroundStep( - $cut.pos - 1, - end, - $cut.pos, - end, - new Slice(wrap, 1, 0), - conn.length, - true - ) - ); - let joinAt = end + 2 * conn.length; - if (canJoin(tr.doc, joinAt)) { - tr.join(joinAt); - } - dispatch(tr.scrollIntoView()); - } - return true; - } - return false; -} - -function textblockAt(node: Node, side: "start" | "end", only = false) { - for ( - let scan: Node | null = node; - scan; - scan = side === "start" ? scan.firstChild : scan.lastChild - ) { - if (scan.isTextblock) { - return true; - } - if (only && scan.childCount !== 1) { - return false; - } - } - return false; -} -function joinMaybeClear( - state: EditorState, - $pos: ResolvedPos, - dispatch: ((tr: Transaction) => void) | undefined -) { - let before = $pos.nodeBefore, - after = $pos.nodeAfter, - index = $pos.index(); - if (!before || !after || !before.type.compatibleContent(after.type)) { - return false; - } - if (!before.content.size && $pos.parent.canReplace(index - 1, index)) { - if (dispatch) { - dispatch( - state.tr.delete($pos.pos - before.nodeSize, $pos.pos).scrollIntoView() - ); - } - return true; - } - if ( - !$pos.parent.canReplace(index, index + 1) || - !(after.isTextblock || canJoin(state.doc, $pos.pos)) - ) { - return false; - } - if (dispatch) { - dispatch( - state.tr - .clearIncompatible( - $pos.pos, - before.type, - before.contentMatchAt(before.childCount) - ) - .join($pos.pos) - .scrollIntoView() - ); - } - return true; -} diff --git a/packages/core/src/extensions/Blocks/helpers/getBlockInfoFromPos.ts b/packages/core/src/extensions/Blocks/helpers/getBlockInfoFromPos.ts new file mode 100644 index 0000000000..a39b3d34c1 --- /dev/null +++ b/packages/core/src/extensions/Blocks/helpers/getBlockInfoFromPos.ts @@ -0,0 +1,66 @@ +import { Node, NodeType } from "prosemirror-model"; + +export type BlockInfo = { + id: string; + node: Node; + contentNode: Node; + contentType: NodeType; + numChildBlocks: number; + startPos: number; + endPos: number; + depth: number; +}; + +/** + * Retrieves information regarding the most nested block node in a ProseMirror doc, that a given position lies in. + * @param doc The ProseMirror doc. + * @param posInBlock A position somewhere within a block node. + * @returns A BlockInfo object for the block the given position is in, or undefined if the position is not in a block + * for the given doc. + */ +export function getBlockInfoFromPos( + doc: Node, + posInBlock: number +): BlockInfo | undefined { + if (posInBlock <= 0 || posInBlock > doc.nodeSize) { + return undefined; + } + + const $pos = doc.resolve(posInBlock); + + const maxDepth = $pos.depth; + let node = $pos.node(maxDepth); + let depth = maxDepth; + + while (depth >= 0) { + // If the outermost node is not a block, it means the position does not lie within a block. + if (depth === 0) { + return undefined; + } + if (node.type.name === "block") { + break; + } + + depth -= 1; + node = $pos.node(depth); + } + + const id = node.attrs["id"]; + const contentNode = node.firstChild!; + const contentType = contentNode.type; + const numChildBlocks = node.childCount === 2 ? node.lastChild!.childCount : 0; + + const startPos = $pos.start(depth); + const endPos = $pos.end(depth); + + return { + id, + node, + contentNode, + contentType, + numChildBlocks, + startPos, + endPos, + depth, + }; +} diff --git a/packages/core/src/extensions/Blocks/helpers/setBlockHeading.ts b/packages/core/src/extensions/Blocks/helpers/setBlockHeading.ts deleted file mode 100644 index e315bf71cb..0000000000 --- a/packages/core/src/extensions/Blocks/helpers/setBlockHeading.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Transaction } from "prosemirror-state"; -import { Level } from "../nodes/Block"; -import { findBlock } from "./findBlock"; - -type Dispatch = ((args?: any) => any) | undefined; - -export function setBlockHeading( - tr: Transaction, - dispatch: Dispatch, - level?: Level -) { - // Get parent of TextNode - const containingBlock = findBlock(tr.selection); - - // Should not be possible because schema dictates - // that each text node is nested in a BlockContent - // node which is nested inside a BlockNode - if (!containingBlock) { - return false; - } - - // Add heading attribute to Block - if (dispatch) { - tr.setNodeMarkup(containingBlock.pos, undefined, { - ...containingBlock.node.attrs, - headingType: level, - }); - } - return true; -} diff --git a/packages/core/src/extensions/Blocks/index.ts b/packages/core/src/extensions/Blocks/index.ts index 6de9405d72..1a807a2f95 100644 --- a/packages/core/src/extensions/Blocks/index.ts +++ b/packages/core/src/extensions/Blocks/index.ts @@ -1,15 +1,19 @@ import { Node } from "@tiptap/core"; import { Block } from "./nodes/Block"; import { BlockGroup } from "./nodes/BlockGroup"; -import { ContentBlock } from "./nodes/Content"; +import { TextContent } from "./nodes/BlockTypes/TextBlock/TextContent"; +import { HeadingContent } from "./nodes/BlockTypes/HeadingBlock/HeadingContent"; +import { ListItemContent } from "./nodes/BlockTypes/ListItemBlock/ListItemContent"; export const blocks: any[] = [ - ContentBlock, + TextContent, + HeadingContent, + ListItemContent, Block, BlockGroup, Node.create({ name: "doc", topNode: true, - content: "blockgroup", + content: "blockGroup", }), ]; diff --git a/packages/core/src/extensions/Blocks/nodes/Block.module.css b/packages/core/src/extensions/Blocks/nodes/Block.module.css index 140c8711f4..e41b3d256c 100644 --- a/packages/core/src/extensions/Blocks/nodes/Block.module.css +++ b/packages/core/src/extensions/Blocks/nodes/Block.module.css @@ -23,11 +23,29 @@ BASIC STYLES } .blockContent { + font-size: 1em; padding: 3px 0; transition: font-size 0.2s; /* display: inline-block; */ } +.blockContent::before { + /* content: ""; */ + transition: all 0.2s; + /*margin: 0px;*/ +} + +/* reset styles, they will be set on blockContent */ +.blockContent p, +.blockContent h1, +.blockContent h2, +.blockContent h3, +.blockContent li { + margin: 0; + padding: 0; + font-size: inherit; +} + /* NESTED BLOCKS */ @@ -54,7 +72,7 @@ NESTED BLOCKS height: 0; } -/* NESTED BLOCK ANIMATIONS */ +/* NESTED BLOCK ANIMATIONS (change in indent) */ [data-prev-depth-change="1"] { --x: 1; @@ -97,106 +115,128 @@ NESTED BLOCKS } /* HEADINGS*/ -.blockOuter[data-prev-heading-type="1"] > .block > div:first-child, -.blockOuter[data-heading-type="1"]:not([data-prev-heading-type]) - > .block - > div:first-child { - font-size: 3em; - font-weight: bold; +[data-heading-level="1"] { + --level: 3em; +} +[data-heading-level="2"] { + --level: 2em; +} +[data-heading-level="3"] { + --level: 1.3em; } -.blockOuter[data-prev-heading-type="2"] > .block > div:first-child, -.blockOuter[data-heading-type="2"]:not([data-prev-heading-type]) - > .block - > div:first-child { - font-size: 2em; +[data-prev-heading-level="1"] { + --prev-level: 3em; +} +[data-prev-heading-level="2"] { + --prev-level: 2em; +} +[data-prev-heading-level="3"] { + --prev-level: 1.3em; +} + +.blockOuter[data-prev-type="headingContent"] > .block > .blockContent { + font-size: var(--prev-level); font-weight: bold; } -.blockOuter[data-prev-heading-type="3"] > .block > div:first-child, -.blockOuter[data-heading-type="3"]:not([data-prev-heading-type]) +.blockOuter:not([data-prev-type]) > .block - > div:first-child { - font-size: 1.3em; + > .blockContent[data-content-type="headingContent"] { + font-size: var(--level); font-weight: bold; } /* LISTS */ -.block > div:first-child::before { +.blockContent::before { + margin-right: 0; content: ""; - transition: all 0.2s; - margin-left: 0px; } -.blockOuter[data-prev-list-type="oli"] > .block > div:first-child::before, -.blockOuter[data-list-type="oli"]:not([data-prev-list-type]) - > .block - > div:first-child::before { - content: attr(data-position); +/* Ordered */ +[data-list-item-type="ordered"] { + --index: attr(data-list-item-index) +} + +[data-prev-list-item-type="ordered"] { + --prev-index: attr(data-prev-list-item-index) +} + +.blockOuter[data-prev-list-item-type="ordered"]:not([data-prev-list-item-index="none"]) +> .block +> .blockContent::before { margin-right: 1.2em; - padding-left: 0px; + content: var(--prev-index)"."; } -.blockOuter[data-prev-list-type="li"] > .block > div:first-child::before, -.blockOuter[data-list-type="li"]:not([data-prev-list-type]) - > .block - > div:first-child::before { +.blockOuter:not([data-prev-type]) +> .block +> .blockContent[data-list-item-type="ordered"]::before { + margin-right: 1.2em; + content: var(--index)"."; +} + +/* Unordered */ +/* No list nesting */ +.blockOuter[data-prev-list-item-type="unordered"] +> .block +> .blockContent::before { + margin-right: 1.2em; content: "•"; +} + +.blockOuter:not([data-prev-type]) +> .block +> .blockContent[data-list-item-type="unordered"]::before { margin-right: 1.2em; - /* display: list-item; */ - /* list-style-type: circle; */ - /* list-style-position: inside; */ - padding-left: 0px; - /* margin-left: 0.2em; */ + content: "•"; } -.blockOuter[data-list-type="li"] - > .block - > .blockGroup - > .blockOuter[data-prev-list-type="li"] - > .block - > div:first-child::before, -.blockOuter[data-list-type="li"] - > .block - > .blockGroup - > .blockOuter[data-list-type="li"]:not([data-prev-list-type]) - > .block - > div:first-child::before { +/* 1 level of list nesting */ +[data-list-item-type="unordered"]~.blockGroup +> .blockOuter[data-prev-list-item-type="unordered"] +> .block +> .blockContent::before { + margin-right: 1.2em; content: "◦"; - /* font-size: 1.2em; */ - font-family: arial; } -.blockOuter[data-list-type="li"] - > .block - > .blockGroup - > .blockOuter[data-list-type="li"] - > .block - > .blockGroup - .blockOuter[data-prev-list-type="li"] - > .block - > div:first-child::before, -.blockOuter[data-list-type="li"] - > .block - > .blockGroup - > .blockOuter[data-list-type="li"] - > .block - > .blockGroup - .blockOuter[data-list-type="li"]:not([data-prev-list-type]) - > .block - > div:first-child::before { +[data-list-item-type="unordered"]~.blockGroup +> .blockOuter:not([data-prev-type]) +> .block +> .blockContent[data-list-item-type="unordered"]::before { + margin-right: 1.2em; + content: "◦"; +} + +/* 2 levels of list nesting */ +[data-list-item-type="unordered"]~.blockGroup +[data-list-item-type="unordered"]~.blockGroup +> .blockOuter[data-prev-list-item-type="unordered"] +> .block +> .blockContent::before { + margin-right: 1.2em; + content: "▪"; +} + +[data-list-item-type="unordered"]~.blockGroup +[data-list-item-type="unordered"]~.blockGroup +> .blockOuter:not([data-prev-type]) +> .block +> .blockContent[data-list-item-type="unordered"]::before { + margin-right: 1.2em; content: "▪"; } /* PLACEHOLDERS*/ -.blockContent > div { +.blockContent > :first-child { display: inline; } -.blockContent.isEmpty div::before, -.blockContent.isFilter div::before { +.blockContent.isEmpty > :first-child:before, +.blockContent.isFilter > :first-child:before { /*float: left; */ content: ""; color: #aeb8c2; @@ -209,18 +249,20 @@ NESTED BLOCKS /* TODO: would be nicer if defined from code */ -.blockContent.isEmpty.hasAnchor div::before { +.blockContent.isEmpty.hasAnchor > :first-child:before { content: "Enter text or type '/' for commands"; } -.blockContent.isFilter.hasAnchor div::before { +.blockContent.isFilter.hasAnchor > :first-child:before { content: "Type to filter"; } -[data-heading-type] > .blockContent.isEmpty div::before { +.blockContent[data-content-type="headingContent"].isEmpty + > :first-child::before { content: "Heading"; } -[data-list-type] > .blockContent.isEmpty div::before { +.blockContent[data-content-type="listItemContent"].isEmpty + > :first-child:before { content: "List"; -} \ No newline at end of file +} diff --git a/packages/core/src/extensions/Blocks/nodes/Block.ts b/packages/core/src/extensions/Blocks/nodes/Block.ts index a7444c241b..aee2aa93f4 100644 --- a/packages/core/src/extensions/Blocks/nodes/Block.ts +++ b/packages/core/src/extensions/Blocks/nodes/Block.ts @@ -1,41 +1,38 @@ import { mergeAttributes, Node } from "@tiptap/core"; -import { Selection, TextSelection } from "prosemirror-state"; -import { joinBackward } from "../commands/joinBackward"; -import { findBlock } from "../helpers/findBlock"; -import { setBlockHeading } from "../helpers/setBlockHeading"; -import { OrderedListPlugin } from "../OrderedListPlugin"; +import { Slice } from "prosemirror-model"; +import { TextSelection } from "prosemirror-state"; +import BlockAttributes from "../BlockAttributes"; +import { getBlockInfoFromPos } from "../helpers/getBlockInfoFromPos"; import { PreviousBlockTypePlugin } from "../PreviousBlockTypePlugin"; -import { textblockTypeInputRuleSameNodeType } from "../rule"; import styles from "./Block.module.css"; -import BlockAttributes from "../BlockAttributes"; +import { HeadingContentAttributes } from "./BlockTypes/HeadingBlock/HeadingContent"; +import { ListItemContentAttributes } from "./BlockTypes/ListItemBlock/ListItemContent"; export interface IBlock { HTMLAttributes: Record; } -export type Level = "1" | "2" | "3"; -export type ListType = "li" | "oli"; +export type BlockContentAttributes = + | HeadingContentAttributes + | ListItemContentAttributes; declare module "@tiptap/core" { interface Commands { - blockHeading: { - /** - * Set a heading node - */ - setBlockHeading: (attributes: { level: Level }) => ReturnType; - /** - * Unset a heading node - */ - unsetBlockHeading: () => ReturnType; - - unsetList: () => ReturnType; - - addNewBlockAsSibling: (attributes?: { - headingType?: Level; - listType?: ListType; - }) => ReturnType; - - setBlockList: (type: ListType) => ReturnType; + block: { + BNCreateBlock: (pos: number) => ReturnType; + BNDeleteBlock: (posInBlock: number) => ReturnType; + BNMergeBlocks: (posBetweenBlocks: number) => ReturnType; + BNSplitBlock: (posInBlock: number, keepType: boolean) => ReturnType; + BNSetContentType: ( + posInBlock: number, + type: string, + attributes?: BlockContentAttributes + ) => ReturnType; + BNCreateBlockOrSetContentType: ( + posInBlock: number, + type: string, + attributes?: BlockContentAttributes + ) => ReturnType; }; } } @@ -46,38 +43,31 @@ declare module "@tiptap/core" { export const Block = Node.create({ name: "block", group: "block", + // A block always contains content, and optionally a blockGroup which contains nested blocks + content: "blockContent blockGroup?", + // Ensures content-specific keyboard handlers trigger first. + priority: 50, + defining: true, + addOptions() { return { HTMLAttributes: {}, }; }, - // A block always contains content, and optionally a blockGroup which contains nested blocks - content: "content blockgroup?", - - defining: true, - addAttributes() { return { - listType: { - default: undefined, - }, blockColor: { default: undefined, }, blockStyle: { default: undefined, }, - headingType: { - default: undefined, - keepOnSplit: false, - }, }; }, parseHTML() { return [ - // For parsing blocks within the editor. { tag: "div", getAttrs: (element) => { @@ -96,49 +86,6 @@ export const Block = Node.create({ return attrs; } - return false; - }, - }, - // For parsing headings & paragraphs copied from outside the editor. - { - tag: "p", - priority: 100, - }, - { - tag: "h1", - attrs: { headingType: "1" }, - }, - { - tag: "h2", - attrs: { headingType: "2" }, - }, - { - tag: "h3", - attrs: { headingType: "3" }, - }, - // For parsing list items copied from outside the editor. - { - tag: "li", - getAttrs: (element) => { - if (typeof element === "string") { - return false; - } - - const parent = element.parentElement; - - if (parent === null) { - return false; - } - - // Gets type of list item (ordered/unordered) based on parent element's tag ("ol"/"ul"). - if (parent.tagName === "UL") { - return { listType: "li" }; - } - - if (parent.tagName === "OL") { - return { listType: "oli" }; - } - return false; }, }, @@ -148,7 +95,8 @@ export const Block = Node.create({ renderHTML({ HTMLAttributes }) { const attrs: Record = {}; for (let [nodeAttr, HTMLAttr] of Object.entries(BlockAttributes)) { - if (HTMLAttributes[nodeAttr]) { + // Ensure falsy values are not misinterpreted. + if (HTMLAttributes[nodeAttr] !== undefined) { attrs[HTMLAttr] = HTMLAttributes[nodeAttr]; } } @@ -162,275 +110,439 @@ export const Block = Node.create({ [ "div", mergeAttributes(attrs, { + // TODO: maybe remove html attributes from inner block class: styles.block, - "data-node-type": "block", + "data-node-type": this.name, }), 0, ], ]; }, - addInputRules() { - return [ - ...["1", "2", "3"].map((level) => { - // Create a heading when starting with "#", "##", or "###"" - return textblockTypeInputRuleSameNodeType({ - find: new RegExp(`^(#{1,${level}})\\s$`), - type: this.type, - getAttributes: { - headingType: level, - }, - }); - }), - // Create a list when starting with "-" - textblockTypeInputRuleSameNodeType({ - find: /^\s*([-+*])\s$/, - type: this.type, - getAttributes: { - listType: "li", - }, - }), - textblockTypeInputRuleSameNodeType({ - find: new RegExp(/^1.\s/), - type: this.type, - getAttributes: { - listType: "oli", - }, - }), - ]; - }, - addCommands() { return { - setBlockHeading: - (attributes) => - ({ tr, dispatch }) => { - return setBlockHeading(tr, dispatch, attributes.level); + // Creates a new text block at a given position. + BNCreateBlock: + (pos) => + ({ state, dispatch }) => { + const newBlock = state.schema.nodes["block"].createAndFill()!; + + if (dispatch) { + state.tr.insert(pos, newBlock); + } + + return true; }, - unsetBlockHeading: - () => - ({ tr, dispatch }) => { - return setBlockHeading(tr, dispatch, undefined); + // Deletes a block at a given position and sets the selection to where the block was. + BNDeleteBlock: + (posInBlock) => + ({ state, view, dispatch }) => { + const blockInfo = getBlockInfoFromPos(state.doc, posInBlock); + if (blockInfo === undefined) { + return false; + } + + const { startPos, endPos } = blockInfo; + + if (dispatch) { + state.tr.deleteRange(startPos, endPos); + state.tr.setSelection( + new TextSelection(state.doc.resolve(startPos + 1)) + ); + view.focus(); + } + + return true; }, - unsetList: - () => - ({ tr, dispatch }) => { - const node = tr.selection.$anchor.node(-1); - const nodePos = tr.selection.$anchor.posAtIndex(0, -1) - 1; + // Appends the text contents of a block to the nearest previous block, given a position between them. Children of + // the merged block are moved out of it first, rather than also being merged. + // + // In the example below, the position passed into the function is between Block1 and Block2. + // + // Block1 + // Block2 + // Block3 + // Block4 + // Block5 + // + // Becomes: + // + // Block1 + // Block2Block3 + // Block4 + // Block5 + BNMergeBlocks: + (posBetweenBlocks) => + ({ state, dispatch }) => { + const nextNodeIsBlock = + state.doc.resolve(posBetweenBlocks + 1).node().type.name === + "block"; + const prevNodeIsBlock = + state.doc.resolve(posBetweenBlocks - 1).node().type.name === + "block"; + + if (!nextNodeIsBlock || !prevNodeIsBlock) { + return false; + } + + const nextBlockInfo = getBlockInfoFromPos( + state.doc, + posBetweenBlocks + 1 + ); - // const node2 = tr.doc.nodeAt(nodePos); - if (node.type.name === "block" && node.attrs["listType"]) { + const { node, contentNode, startPos, endPos, depth } = nextBlockInfo!; + + // Removes a level of nesting all children of the next block by 1 level, if it contains both content and block + // group nodes. + if (node.childCount === 2) { + const childBlocksStart = state.doc.resolve( + startPos + contentNode.nodeSize + 1 + ); + const childBlocksEnd = state.doc.resolve(endPos - 1); + const childBlocksRange = + childBlocksStart.blockRange(childBlocksEnd); + + // Moves the block group node inside the block into the block group node that the current block is in. if (dispatch) { - tr.setNodeMarkup(nodePos, undefined, { - ...node.attrs, - listType: undefined, - }); - return true; + state.tr.lift(childBlocksRange!, depth - 1); } } - return false; - }, - addNewBlockAsSibling: - (attributes) => - ({ tr, dispatch, state }) => { - // Get current block - const currentBlock = findBlock(tr.selection); - if (!currentBlock) { + let prevBlockEndPos = posBetweenBlocks - 1; + let prevBlockInfo = getBlockInfoFromPos(state.doc, prevBlockEndPos); + + // Finds the nearest previous block, regardless of nesting level. + while (prevBlockInfo!.numChildBlocks > 0) { + prevBlockEndPos--; + prevBlockInfo = getBlockInfoFromPos(state.doc, prevBlockEndPos); + if (prevBlockInfo === undefined) { + return false; + } + } + + // Deletes next block and adds its text content to the nearest previous block. + // TODO: Is there any situation where we need the whole block content, not just text? Implementation for this + // is trickier. + if (dispatch) { + state.tr.deleteRange(startPos, startPos + contentNode.nodeSize); + state.tr.insertText(contentNode.textContent, prevBlockEndPos - 1); + state.tr.setSelection( + new TextSelection(state.doc.resolve(prevBlockEndPos - 1)) + ); + } + + return true; + }, + // Splits a block at a given position. Content after the position is moved to a new block below, at the same + // nesting level. + BNSplitBlock: + (posInBlock, keepType) => + ({ state, dispatch }) => { + const blockInfo = getBlockInfoFromPos(state.doc, posInBlock); + if (blockInfo === undefined) { return false; } - // If current blocks content is empty dont create a new block - if (currentBlock.node.firstChild?.textContent.length === 0) { - if (dispatch) { - tr.setNodeMarkup(currentBlock.pos, undefined, attributes); + const { contentNode, contentType, startPos, endPos, depth } = + blockInfo; + + const newBlockInsertionPos = endPos + 1; + + // Creates new block first, otherwise positions get changed due to the original block's content changing. + // Only text content is transferred to the new block. + const secondBlockContent = state.doc.textBetween(posInBlock, endPos); + + const newBlock = state.schema.nodes["block"].createAndFill()!; + const newBlockContentPos = newBlockInsertionPos + 2; + + if (dispatch) { + state.tr.insert(newBlockInsertionPos, newBlock); + state.tr.insertText(secondBlockContent, newBlockContentPos); + + if (keepType) { + state.tr.setBlockType( + newBlockContentPos, + newBlockContentPos, + state.schema.node(contentType).type, + contentNode.attrs + ); } - return true; } - // Create new block after current block - const endOfBlock = currentBlock.pos + currentBlock.node.nodeSize; - let newBlock = state.schema.nodes["block"].createAndFill(attributes)!; + // Updates content of original block. + const firstBlockContent = state.doc.content.cut(startPos, posInBlock); + if (dispatch) { - tr.insert(endOfBlock, newBlock); - tr.setSelection(new TextSelection(tr.doc.resolve(endOfBlock + 1))); + state.tr.replace( + startPos, + endPos, + new Slice(firstBlockContent, depth, depth) + ); } + return true; }, - setBlockList: - (type) => - ({ tr, dispatch }) => { - const node = tr.selection.$anchor.node(-1); - const nodePos = tr.selection.$anchor.posAtIndex(0, -1) - 1; - - // const node2 = tr.doc.nodeAt(nodePos); - if (node.type.name === "block") { - if (dispatch) { - tr.setNodeMarkup(nodePos, undefined, { - ...node.attrs, - listType: type, - }); - } - return true; + // Changes the block at a given position to a given content type. + BNSetContentType: + (posInBlock, type, attributes) => + ({ state, dispatch }) => { + const blockInfo = getBlockInfoFromPos(state.doc, posInBlock); + if (blockInfo === undefined) { + return false; + } + + const { startPos, endPos } = blockInfo; + + if (dispatch) { + state.tr.setBlockType( + startPos + 1, + endPos - 1, + state.schema.node(type).type, + attributes + ); + } + + return true; + }, + // Changes the block at a given position to a given content type if it's empty, otherwise creates a new block of + // that type below it. + BNCreateBlockOrSetContentType: + (posInBlock, type, attributes) => + ({ state, chain }) => { + const blockInfo = getBlockInfoFromPos(state.doc, posInBlock); + if (blockInfo === undefined) { + return false; + } + + const { node, startPos, endPos } = blockInfo; + + if (node.textContent.length === 0) { + const oldBlockContentPos = startPos + 1; + + return chain() + .BNSetContentType(posInBlock, type, attributes) + .setTextSelection(oldBlockContentPos) + .run(); + } else { + const newBlockInsertionPos = endPos + 1; + const newBlockContentPos = newBlockInsertionPos + 1; + + return chain() + .BNCreateBlock(newBlockInsertionPos) + .BNSetContentType(newBlockContentPos, type, attributes) + .setTextSelection(newBlockContentPos) + .run(); } - return false; }, - joinBackward: - () => - ({ view, dispatch, state }) => - joinBackward(state, dispatch, view), // Override default joinBackward with edited command }; }, + addProseMirrorPlugins() { - return [PreviousBlockTypePlugin(), OrderedListPlugin()]; + return [PreviousBlockTypePlugin()]; }, + addKeyboardShortcuts() { // handleBackspace is partially adapted from https://github.com/ueberdosis/tiptap/blob/ed56337470efb4fd277128ab7ef792b37cfae992/packages/core/src/extensions/keymap.ts const handleBackspace = () => this.editor.commands.first(({ commands }) => [ - // Maybe the user wants to undo an auto formatting input rule (e.g.: - or #, and then hit backspace) (source: tiptap) + // Deletes the selection if it's not empty. + () => commands.deleteSelection(), + // Undoes an input rule if one was triggered in the last editor state change. () => commands.undoInputRule(), - // maybe convert first text block node to default node (source: tiptap) + // Changes block type to a text block if it's not already, while the selection is at the start of the block. + () => + commands.command(({ state }) => { + const { contentType } = getBlockInfoFromPos( + state.doc, + state.selection.from + )!; + + const selectionAtBlockStart = + state.selection.$anchor.parentOffset === 0; + const isTextBlock = contentType.name === "textContent"; + + if (selectionAtBlockStart && !isTextBlock) { + return commands.BNSetContentType( + state.selection.from, + "textContent" + ); + } + + return false; + }), + // Removes a level of nesting if the block is indented if the selection is at the start of the block. () => - commands.command(({ tr }) => { - const { selection, doc } = tr; - const { empty, $anchor } = selection; - const { pos, parent } = $anchor; - const isAtStart = Selection.atStart(doc).from === pos; + commands.command(({ state }) => { + const selectionAtBlockStart = + state.selection.$anchor.parentOffset === 0; - if ( - !empty || - !isAtStart || - !parent.type.isTextblock || - parent.textContent.length - ) { - return false; + if (selectionAtBlockStart) { + return commands.liftListItem("block"); } - return commands.clearNodes(); + return false; }), - () => commands.deleteSelection(), // (source: tiptap) + // Merges block with the previous one if it isn't indented, isn't the first block in the doc, and the selection + // is at the start of the block. () => - commands.command(({ tr }) => { - const isAtStartOfNode = tr.selection.$anchor.parentOffset === 0; - const node = tr.selection.$anchor.node(-1); - if (isAtStartOfNode && node.type.name === "block") { - // we're at the start of the block, so we're trying to "backspace" the bullet or indentation - return commands.first([ - () => commands.unsetList(), // first try to remove the "list" property - () => commands.liftListItem("block"), // then try to remove a level of indentation - ]); + commands.command(({ state }) => { + const { depth, startPos } = getBlockInfoFromPos( + state.doc, + state.selection.from + )!; + + const selectionAtBlockStart = + state.selection.$anchor.parentOffset === 0; + const selectionEmpty = + state.selection.anchor === state.selection.head; + const blockAtDocStart = startPos === 2; + + const posBetweenBlocks = startPos - 1; + + if ( + !blockAtDocStart && + selectionAtBlockStart && + selectionEmpty && + depth === 2 + ) { + return commands.BNMergeBlocks(posBetweenBlocks); } + return false; }), - ({ chain }) => - // we are at the start of a block at the root level. The user hits backspace to "merge it" to the end of the block above - // - // BlockA - // BlockB - - // Becomes: - - // BlockABlockB - - chain() - .command(({ tr, state, dispatch }) => { - const isAtStartOfNode = tr.selection.$anchor.parentOffset === 0; - const anchor = tr.selection.$anchor; - const node = anchor.node(-1); - if (isAtStartOfNode && node.type.name === "block") { - if (node.childCount === 2) { - // BlockB has children. We want to go from this: - // - // BlockA - // BlockB - // BlockC - // BlockD - // - // to: - // - // BlockABlockB - // BlockC - // BlockD - - // This parts moves the children of BlockB to the top level - const startSecondChild = anchor.posAtIndex(1, -1) + 1; // start of blockgroup - const endSecondChild = anchor.posAtIndex(2, -1) - 1; - const range = state.doc - .resolve(startSecondChild) - .blockRange(state.doc.resolve(endSecondChild)); - - if (dispatch) { - tr.lift(range!, anchor.depth - 2); - } - } - return true; - } - return false; - }) - // use joinBackward to merge BlockB to BlockA (i.e.: turn it into BlockABlockB) - // The standard JoinBackward would break here, and would turn it into: - // BlockA - // BlockB - // - // joinBackward has been patched with our custom version to fix this (see commands/joinBackward) - .joinBackward() - .run(), - - () => commands.selectNodeBackward(), // (source: tiptap) ]); const handleEnter = () => this.editor.commands.first(({ commands }) => [ - // Try to split the current block into 2 items: - () => commands.splitListItem("block"), - // Otherwise, maybe we are in an empty list item. "Enter" should remove the list bullet - ({ tr, dispatch }) => { - const $from = tr.selection.$from; - if ($from.depth !== 3) { - // only needed at root level, at deeper levels it should be handled already by splitListItem + // Removes a level of nesting if the block is empty & indented, while the selection is also empty & at the start + // of the block. + () => + commands.command(({ state }) => { + const { node, depth } = getBlockInfoFromPos( + state.doc, + state.selection.from + )!; + + const selectionAtBlockStart = + state.selection.$anchor.parentOffset === 0; + const selectionEmpty = + state.selection.anchor === state.selection.head; + const blockEmpty = node.textContent.length === 0; + const blockIndented = depth > 2; + + if ( + selectionAtBlockStart && + selectionEmpty && + blockEmpty && + blockIndented + ) { + return commands.liftListItem("block"); + } + return false; - } - const node = tr.selection.$anchor.node(-1); - const nodePos = tr.selection.$anchor.posAtIndex(0, -1) - 1; + }), + // Creates a new block and moves the selection to it if the current one is empty, while the selection is also + // empty & at the start of the block. + () => + commands.command(({ state, chain }) => { + const { node, endPos } = getBlockInfoFromPos( + state.doc, + state.selection.from + )!; + + const selectionAtBlockStart = + state.selection.$anchor.parentOffset === 0; + const selectionEmpty = + state.selection.anchor === state.selection.head; + const blockEmpty = node.textContent.length === 0; + + if (selectionAtBlockStart && selectionEmpty && blockEmpty) { + const newBlockInsertionPos = endPos + 1; + const newBlockContentPos = newBlockInsertionPos + 2; + + chain() + .BNCreateBlock(newBlockInsertionPos) + .setTextSelection(newBlockContentPos) + .run(); - if (node.type.name === "block" && node.attrs["listType"]) { - if (dispatch) { - tr.setNodeMarkup(nodePos, undefined, { - ...node.attrs, - listType: undefined, - }); + return true; } - return true; - } - return false; - }, - // Otherwise, we might be on an empty line and hit "Enter" to create a new line: - ({ tr, dispatch }) => { - const $from = tr.selection.$from; - if (dispatch) { - tr.split($from.pos, 2).scrollIntoView(); - } - return true; - }, + return false; + }), + // Splits the current block, moving content inside that's after the cursor to a new text block below. Also + // deletes the selection beforehand, if it's not empty. + () => + commands.command(({ state, chain }) => { + const { node } = getBlockInfoFromPos( + state.doc, + state.selection.from + )!; + + const blockEmpty = node.textContent.length === 0; + + if (!blockEmpty) { + chain() + .deleteSelection() + .BNSplitBlock(state.selection.from, false) + .run(); + + return true; + } + + return false; + }), ]); return { Backspace: handleBackspace, Enter: handleEnter, Tab: () => this.editor.commands.sinkListItem("block"), - "Shift-Tab": () => { - return this.editor.commands.liftListItem("block"); - }, + "Shift-Tab": () => this.editor.commands.liftListItem("block"), "Mod-Alt-0": () => - this.editor.chain().unsetList().unsetBlockHeading().run(), - "Mod-Alt-1": () => this.editor.commands.setBlockHeading({ level: "1" }), - "Mod-Alt-2": () => this.editor.commands.setBlockHeading({ level: "2" }), - "Mod-Alt-3": () => this.editor.commands.setBlockHeading({ level: "3" }), - "Mod-Shift-7": () => this.editor.commands.setBlockList("li"), - "Mod-Shift-8": () => this.editor.commands.setBlockList("oli"), - // TODO: Add shortcuts for numbered and bullet list + this.editor.commands.BNCreateBlock( + this.editor.state.selection.anchor + 2 + ), + "Mod-Alt-1": () => + this.editor.commands.BNSetContentType( + this.editor.state.selection.anchor, + "headingContent", + { + headingLevel: "1", + } + ), + "Mod-Alt-2": () => + this.editor.commands.BNSetContentType( + this.editor.state.selection.anchor, + "headingContent", + { + headingLevel: "2", + } + ), + "Mod-Alt-3": () => + this.editor.commands.BNSetContentType( + this.editor.state.selection.anchor, + "headingContent", + { + headingLevel: "3", + } + ), + "Mod-Shift-7": () => + this.editor.commands.BNSetContentType( + this.editor.state.selection.anchor, + "listItemContent", + { + listItemType: "unordered", + } + ), + "Mod-Shift-8": () => + this.editor.commands.BNSetContentType( + this.editor.state.selection.anchor, + "listItemContent", + { + listItemType: "ordered", + } + ), }; }, }); diff --git a/packages/core/src/extensions/Blocks/nodes/BlockGroup.ts b/packages/core/src/extensions/Blocks/nodes/BlockGroup.ts index 2dcca64a91..044e4704c4 100644 --- a/packages/core/src/extensions/Blocks/nodes/BlockGroup.ts +++ b/packages/core/src/extensions/Blocks/nodes/BlockGroup.ts @@ -2,7 +2,7 @@ import { mergeAttributes, Node } from "@tiptap/core"; import styles from "./Block.module.css"; export const BlockGroup = Node.create({ - name: "blockgroup", + name: "blockGroup", addOptions() { return { @@ -17,18 +17,18 @@ export const BlockGroup = Node.create({ { tag: "div", getAttrs: (element) => { - if(typeof element === "string") { + if (typeof element === "string") { return false; } - if(element.getAttribute("data-node-type") === "block-group") { + if (element.getAttribute("data-node-type") === "block-group") { // Null means the element matches, but we don't want to add any attributes to the node. return null; } return false; - } - } + }, + }, ]; }, @@ -37,7 +37,7 @@ export const BlockGroup = Node.create({ "div", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, { class: styles.blockGroup, - "data-node-type": "block-group" + "data-node-type": "block-group", }), 0, ]; diff --git a/packages/core/src/extensions/Blocks/nodes/BlockTypes/HeadingBlock/HeadingContent.ts b/packages/core/src/extensions/Blocks/nodes/BlockTypes/HeadingBlock/HeadingContent.ts new file mode 100644 index 0000000000..94fadeae7f --- /dev/null +++ b/packages/core/src/extensions/Blocks/nodes/BlockTypes/HeadingBlock/HeadingContent.ts @@ -0,0 +1,78 @@ +import { InputRule, mergeAttributes, Node } from "@tiptap/core"; +import styles from "../../Block.module.css"; + +export type HeadingContentAttributes = { + headingLevel: string; +}; + +export const HeadingContent = Node.create({ + name: "headingContent", + group: "blockContent", + content: "inline*", + + addAttributes() { + return { + headingLevel: { + default: "1", + // instead of "level" attributes, use "data-level" + parseHTML: (element) => element.getAttribute("data-heading-level"), + renderHTML: (attributes) => { + return { + "data-heading-level": attributes.headingLevel, + }; + }, + }, + }; + }, + + addInputRules() { + return [ + ...["1", "2", "3"].map((level) => { + // Creates a heading of appropriate level when starting with "#", "##", or "###". + return new InputRule({ + find: new RegExp(`^(#{${parseInt(level)}})\\s$`), + handler: ({ state, chain, range }) => { + chain() + .BNSetContentType(state.selection.from, "headingContent", { + headingLevel: level, + }) + // Removes the "#" character(s) used to set the heading. + .deleteRange({ from: range.from, to: range.to }); + }, + }); + }), + ]; + }, + + parseHTML() { + return [ + { + tag: "h1", + attrs: { headingLevel: "1" }, + node: "block" + }, + { + tag: "h2", + attrs: { headingLevel: "2" }, + node: "block" + }, + { + tag: "h3", + attrs: { headingLevel: "3" }, + node: "block" + }, + ]; + }, + + renderHTML({ node, HTMLAttributes }) { + console.log(node.attrs); + return [ + "div", + mergeAttributes(HTMLAttributes, { + class: styles.blockContent, + "data-content-type": this.name, + }), + ["h" + node.attrs["headingLevel"], 0], + ]; + }, +}); diff --git a/packages/core/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/ListItemContent.ts b/packages/core/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/ListItemContent.ts new file mode 100644 index 0000000000..21cc0f1eea --- /dev/null +++ b/packages/core/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/ListItemContent.ts @@ -0,0 +1,169 @@ +import { InputRule, mergeAttributes, Node } from "@tiptap/core"; +import { OrderedListItemIndexPlugin } from "./OrderedListItemIndexPlugin"; +import { getBlockInfoFromPos } from "../../../helpers/getBlockInfoFromPos"; +import styles from "../../Block.module.css"; + +export type ListItemContentAttributes = { + listItemType: string; +}; + +export const ListItemContent = Node.create({ + name: "listItemContent", + group: "blockContent", + content: "inline*", + + addAttributes() { + return { + listItemType: { + default: "unordered", + parseHTML: (element) => element.getAttribute("data-list-item-type"), + renderHTML: (attributes) => { + return { + "data-list-item-type": attributes.listItemType, + }; + }, + }, + listItemIndex: { + default: null, + parseHTML: (element) => element.getAttribute("data-list-item-index"), + renderHTML: (attributes) => { + return { + "data-list-item-index": attributes.listItemIndex, + }; + }, + }, + }; + }, + + addInputRules() { + return [ + // Creates an unordered list when starting with "-", "+", or "*". + new InputRule({ + find: new RegExp(`^[-+*]\\s$`), + handler: ({ state, chain, range }) => { + chain() + .BNSetContentType(state.selection.from, "listItemContent", { + listItemType: "unordered", + }) + // Removes the "-", "+", or "*" character used to set the list. + .deleteRange({ from: range.from, to: range.to }); + }, + }), + // Creates an ordered list when starting with "1.". + new InputRule({ + find: new RegExp(`^1\\.\\s$`), + handler: ({ state, chain, range }) => { + chain() + .BNSetContentType(state.selection.from, "listItemContent", { + listItemType: "ordered", + }) + // Removes the "1." characters used to set the list. + .deleteRange({ from: range.from, to: range.to }); + }, + }), + ]; + }, + + addKeyboardShortcuts() { + const handleEnter = () => { + const { node, contentType } = getBlockInfoFromPos( + this.editor.state.doc, + this.editor.state.selection.from + )!; + + const selectionEmpty = + this.editor.state.selection.anchor === this.editor.state.selection.head; + + if (contentType.name !== "listItemContent" || !selectionEmpty) { + return false; + } + + return this.editor.commands.first(({ state, chain, commands }) => [ + () => + // Changes list item block to a text block if both the content is empty. + commands.command(() => { + if (node.textContent.length === 0) { + return commands.BNSetContentType( + state.selection.from, + "textContent" + ); + } + + return false; + }), + + () => + // Splits the current block, moving content inside that's after the cursor to a new block of the same type + // below. + commands.command(() => { + if (node.textContent.length > 0) { + chain() + .deleteSelection() + .BNSplitBlock(state.selection.from, true) + .run(); + + return true; + } + + return false; + }), + ]); + }; + + return { + Enter: handleEnter, + }; + }, + + addProseMirrorPlugins() { + return [OrderedListItemIndexPlugin()]; + }, + + parseHTML() { + return [ + { + tag: "li", + getAttrs: (element) => { + if (typeof element === "string") { + return false; + } + + const parent = element.parentElement; + + if (parent === null) { + return false; + } + + // Case for BlockNote list structure. + // Gets type of list item (ordered/unordered) based on the block content's listItemType attribute. + if (parent.getAttribute("data-content-type") === "listItemContent") { + return { listItemType: parent.getAttribute("data-list-item-type") }; + } + + // Case for regular HTML list structure. + // Gets type of list item (ordered/unordered) based on parent element's tag ("ol"/"ul"). + if (parent.tagName === "UL") { + return { listItemType: "unordered" }; + } + if (parent.tagName === "OL") { + return { listItemType: "ordered" }; + } + + return false; + }, + node: "block" + }, + ]; + }, + + renderHTML({ HTMLAttributes }) { + return [ + "div", + mergeAttributes(HTMLAttributes, { + class: styles.blockContent, + "data-content-type": this.name, + }), + ["li", 0], + ]; + }, +}); diff --git a/packages/core/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/OrderedListItemIndexPlugin.ts b/packages/core/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/OrderedListItemIndexPlugin.ts new file mode 100644 index 0000000000..79918a7432 --- /dev/null +++ b/packages/core/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/OrderedListItemIndexPlugin.ts @@ -0,0 +1,77 @@ +import { Plugin, PluginKey } from "prosemirror-state"; +import { getBlockInfoFromPos } from "../../../helpers/getBlockInfoFromPos"; + +// ProseMirror Plugin which automatically assigns indices to ordered list items per nesting level. +const PLUGIN_KEY = new PluginKey(`ordered-list-item-index`); +export const OrderedListItemIndexPlugin = () => { + return new Plugin({ + key: PLUGIN_KEY, + appendTransaction: (_transactions, _oldState, newState) => { + const tr = newState.tr; + tr.setMeta("orderedListIndexing", true); + + let modified = false; + + // Traverses each node the doc using DFS, so blocks which are on the same nesting level will be traversed in the + // same order they appear. This means the index of each list item block can be calculated by incrementing the + // index of the previous list item block. + newState.doc.descendants((node, pos) => { + if ( + node.type.name === "block" && + node.firstChild!.type.name === "listItemContent" && + node.firstChild!.attrs["listItemType"] === "ordered" + ) { + let newIndex = "1"; + const isFirstBlockInDoc = pos === 1; + + const blockInfo = getBlockInfoFromPos(tr.doc, pos + 1)!; + if (blockInfo === undefined) { + return; + } + + // Checks if this block is the start of a new ordered list, i.e. if it's the first block in the document, the + // first block in its nesting level, or the previous block is not an ordered list item. + if (!isFirstBlockInDoc) { + const prevBlockInfo = getBlockInfoFromPos(tr.doc, pos - 2)!; + if (prevBlockInfo === undefined) { + return; + } + + const isFirstBlockInNestingLevel = + blockInfo.depth !== prevBlockInfo.depth; + + if (!isFirstBlockInNestingLevel) { + const prevBlockContentNode = prevBlockInfo.contentNode; + const prevBlockContentType = prevBlockInfo.contentType; + + const isPrevBlockOrderedListItem = + prevBlockContentType.name === "listItemContent" && + prevBlockContentNode.attrs["listItemType"] === "ordered"; + + if (isPrevBlockOrderedListItem) { + const prevBlockIndex = + prevBlockContentNode.attrs["listItemIndex"]; + + newIndex = (parseInt(prevBlockIndex) + 1).toString(); + } + } + } + + const contentNode = blockInfo.contentNode; + const index = contentNode.attrs["listItemIndex"]; + + if (index !== newIndex) { + modified = true; + + tr.setNodeMarkup(pos + 1, undefined, { + listItemType: "ordered", + listItemIndex: newIndex, + }); + } + } + }); + + return modified ? tr : null; + }, + }); +}; diff --git a/packages/core/src/extensions/Blocks/nodes/BlockTypes/TextBlock/TextContent.ts b/packages/core/src/extensions/Blocks/nodes/BlockTypes/TextBlock/TextContent.ts new file mode 100644 index 0000000000..99dba95916 --- /dev/null +++ b/packages/core/src/extensions/Blocks/nodes/BlockTypes/TextBlock/TextContent.ts @@ -0,0 +1,29 @@ +import { Node } from "@tiptap/core"; +import styles from "../../Block.module.css"; + +export const TextContent = Node.create({ + name: "textContent", + group: "blockContent", + content: "inline*", + + parseHTML() { + return [ + { + tag: "p", + priority: 200, + node: "block", + }, + ]; + }, + + renderHTML() { + return [ + "div", + { + class: styles.blockContent, + "data-content-type": this.name, + }, + ["p", 0], + ]; + }, +}); diff --git a/packages/core/src/extensions/Blocks/nodes/Content.ts b/packages/core/src/extensions/Blocks/nodes/Content.ts deleted file mode 100644 index ed29285154..0000000000 --- a/packages/core/src/extensions/Blocks/nodes/Content.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { mergeAttributes, Node } from "@tiptap/core"; -import styles from "./Block.module.css"; -export interface IBlock { - HTMLAttributes: Record; -} - -export const ContentBlock = Node.create({ - name: "content", - - addOptions() { - return { - HTMLAttributes: {}, - }; - }, - addAttributes() { - return { - position: { - default: undefined, - renderHTML: (attributes) => { - return { - "data-position": attributes.position, - }; - }, - parseHTML: (element) => element.getAttribute("data-position"), - }, - }; - }, - - content: "inline*", - - parseHTML() { - return [ - { - tag: "div", - getAttrs: (element) => { - if(typeof element === "string") { - return false; - } - - if(element.getAttribute("data-node-type") === "block-content") { - // Null means the element matches, but we don't want to add any attributes to the node. - return null; - } - - return false; - } - } - ]; - }, - - renderHTML({ HTMLAttributes }) { - return [ - "div", - mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, { - class: styles.blockContent, - "data-node-type": "block-content" - }), - // TODO: The extra nested div is only needed for placeholders, different solution (without extra div) would be preferable - // We can't use the other div because the ::before attribute on that one is already reserved for list-bullets - ["div", 0], - ]; - }, -}); diff --git a/packages/core/src/extensions/Blocks/rule.ts b/packages/core/src/extensions/Blocks/rule.ts deleted file mode 100644 index 1e93bac973..0000000000 --- a/packages/core/src/extensions/Blocks/rule.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { - callOrReturn, - ExtendedRegExpMatchArray, - InputRule, - InputRuleFinder, -} from "@tiptap/core"; -import { NodeType } from "prosemirror-model"; - -/** - * Modified version of https://github.com/ueberdosis/tiptap/blob/6a813686f5e87cebac49a624936dbeadb5a29f95/packages/core/src/inputRules/textblockTypeInputRule.ts - * But instead of changing the type of a node, we use setNodeMarkup to change some of it's current attributes - * - * Build an input rule that changes the type of a textblock when the - * matched text is typed into it. When using a regular expresion you’ll - * probably want the regexp to start with `^`, so that the pattern can - * only occur at the start of a textblock. - */ -export function textblockTypeInputRuleSameNodeType(config: { - find: InputRuleFinder; - type: NodeType; - getAttributes?: - | Record - | ((match: ExtendedRegExpMatchArray) => Record) - | false - | null; -}) { - return new InputRule({ - find: config.find, - handler: ({ state, range, match }) => { - const $start = state.doc.resolve(range.from); - const attributes = - callOrReturn(config.getAttributes, undefined, match) || {}; - - const blockNode = $start.node(-1); - if (blockNode.type !== config.type) { - return null; - } - - state.tr - .setNodeMarkup(range.from - 2, undefined, { - ...blockNode.attrs, - ...attributes, - }) - .delete(range.from, range.to); - return; - }, - }); -} diff --git a/packages/core/src/extensions/BubbleMenu/component/BubbleMenu.tsx b/packages/core/src/extensions/BubbleMenu/component/BubbleMenu.tsx index ad22510c2a..315d6b967e 100644 --- a/packages/core/src/extensions/BubbleMenu/component/BubbleMenu.tsx +++ b/packages/core/src/extensions/BubbleMenu/component/BubbleMenu.tsx @@ -22,42 +22,32 @@ import { findBlock } from "../../Blocks/helpers/findBlock"; import { formatKeyboardShortcut } from "../../../utils"; import LinkToolbarButton from "./LinkToolbarButton"; import { IconType } from "react-icons"; +import { Node } from "prosemirror-model"; -type ListType = "li" | "oli"; - -function getBlockName( - currentBlockHeading: number | undefined, - currentBlockListType: ListType | undefined -) { - const headings = ["Heading 1", "Heading 2", "Heading 3"]; - const lists = { - li: "Bullet List", - oli: "Numbered List", - }; - // A heading that's also a list, should show as Heading - if (currentBlockHeading) { - return headings[currentBlockHeading - 1]; - } else if (currentBlockListType) { - return lists[currentBlockListType]; - } else { +function getBlockName(blockContentNode: Node) { + if (blockContentNode.type.name === "textContent") { return "Text"; } + + if (blockContentNode.type.name === "headingContent") { + return "Heading " + blockContentNode.attrs["headingLevel"]; + } + + if (blockContentNode.type.name === "listItemContent") { + return blockContentNode.attrs["listItemType"] === "unordered" + ? "Bullet List" + : "Numbered List"; + } + + return ""; } // TODO: add list options, indentation export const BubbleMenu = (props: { editor: Editor }) => { useEditorForceUpdate(props.editor); - const currentBlock = findBlock(props.editor.state.selection); - const currentBlockHeading: number | undefined = - currentBlock?.node.attrs.headingType; - const currentBlockListType: ListType | undefined = - currentBlock?.node.attrs.listType; - - const currentBlockName = getBlockName( - currentBlockHeading, - currentBlockListType - ); + const selectedNode = props.editor.state.selection.$from.node(); + const currentBlockName = getBlockName(selectedNode); const blockIconMap: Record = { Text: RiText, @@ -78,72 +68,99 @@ export const BubbleMenu = (props: { editor: Editor }) => { onClick: () => { // Setting editor focus using a chained command instead causes bubble menu to flicker on click. props.editor.view.focus(); - props.editor.chain().unsetBlockHeading().unsetList().run(); + props.editor.commands.BNSetContentType( + props.editor.state.selection.from, + "textContent" + ); }, text: "Text", icon: RiText, - isSelected: currentBlockName === "Text", + isSelected: selectedNode.type.name === "textContent", }, { onClick: () => { props.editor.view.focus(); - props.editor - .chain() - .unsetList() - .setBlockHeading({ level: "1" }) - .run(); + props.editor.commands.BNSetContentType( + props.editor.state.selection.from, + "headingContent", + { + headingLevel: "1", + } + ); }, text: "Heading 1", icon: RiH1, - isSelected: currentBlockName === "Heading 1", + isSelected: + selectedNode.type.name === "headingContent" && + selectedNode.attrs["headingLevel"] === "1", }, { onClick: () => { props.editor.view.focus(); - props.editor - .chain() - .unsetList() - .setBlockHeading({ level: "2" }) - .run(); + props.editor.commands.BNSetContentType( + props.editor.state.selection.from, + "headingContent", + { + headingLevel: "2", + } + ); }, text: "Heading 2", icon: RiH2, - isSelected: currentBlockName === "Heading 2", + isSelected: + selectedNode.type.name === "headingContent" && + selectedNode.attrs["headingLevel"] === "2", }, { onClick: () => { props.editor.view.focus(); - props.editor - .chain() - .unsetList() - .setBlockHeading({ level: "3" }) - .run(); + props.editor.commands.BNSetContentType( + props.editor.state.selection.from, + "headingContent", + { + headingLevel: "3", + } + ); }, text: "Heading 3", icon: RiH3, - isSelected: currentBlockName === "Heading 3", + isSelected: + selectedNode.type.name === "headingContent" && + selectedNode.attrs["headingLevel"] === "3", }, { onClick: () => { props.editor.view.focus(); - props.editor.chain().unsetBlockHeading().setBlockList("li").run(); + props.editor.commands.BNSetContentType( + props.editor.state.selection.from, + "listItemContent", + { + listItemType: "unordered", + } + ); }, text: "Bullet List", icon: RiListUnordered, - isSelected: currentBlockName === "Bullet List", + isSelected: + selectedNode.type.name === "listItemContent" && + selectedNode.attrs["listItemType"] === "unordered", }, { onClick: () => { props.editor.view.focus(); - props.editor - .chain() - .unsetBlockHeading() - .setBlockList("oli") - .run(); + props.editor.commands.BNSetContentType( + props.editor.state.selection.from, + "listItemContent", + { + listItemType: "ordered", + } + ); }, text: "Numbered List", icon: RiListOrdered, - isSelected: currentBlockName === "Numbered List", + isSelected: + selectedNode.type.name === "listItemContent" && + selectedNode.attrs["listItemType"] === "ordered", }, ]} /> diff --git a/packages/core/src/extensions/DraggableBlocks/DraggableBlocksExtension.ts b/packages/core/src/extensions/DraggableBlocks/DraggableBlocksExtension.ts index 790d4e6076..e6042402ca 100644 --- a/packages/core/src/extensions/DraggableBlocks/DraggableBlocksExtension.ts +++ b/packages/core/src/extensions/DraggableBlocks/DraggableBlocksExtension.ts @@ -10,6 +10,6 @@ export const DraggableBlocksExtension = Extension.create<{}>({ name: "DraggableBlocksExtension", priority: 1000, // Need to be high, in order to hide draghandle when typing slash addProseMirrorPlugins() { - return [createDraggableBlocksPlugin()]; + return [createDraggableBlocksPlugin(this.editor)]; }, }); diff --git a/packages/core/src/extensions/DraggableBlocks/DraggableBlocksPlugin.tsx b/packages/core/src/extensions/DraggableBlocks/DraggableBlocksPlugin.tsx index 6bc1be5508..7df590bdb9 100644 --- a/packages/core/src/extensions/DraggableBlocks/DraggableBlocksPlugin.tsx +++ b/packages/core/src/extensions/DraggableBlocks/DraggableBlocksPlugin.tsx @@ -7,6 +7,7 @@ import { DragHandle } from "./components/DragHandle"; import { MantineProvider } from "@mantine/core"; import { BlockNoteTheme } from "../../BlockNoteTheme"; import { MultipleNodeSelection } from "../Blocks/MultipleNodeSelection"; +import { Editor } from "@tiptap/core"; const serializeForClipboard = (pv as any).__serializeForClipboard; // code based on https://github.com/ueberdosis/tiptap/issues/323#issuecomment-506637799 @@ -91,10 +92,7 @@ function blockPositionFromCoords( return null; } -function blockPositionsFromSelection( - selection: Selection, - doc: Node -) { +function blockPositionsFromSelection(selection: Selection, doc: Node) { // Absolute positions just before the first block spanned by the selection, and just after the last block. Having the // selection start and end just before and just after the target blocks ensures no whitespace/line breaks are left // behind after dragging & dropping them. @@ -107,9 +105,9 @@ function blockPositionsFromSelection( // in. If the anchor should update but the head shouldn't and vice versa, it means the user selection is outside a // block content node, which should never happen. const selectionStartInBlockContent = - doc.resolve(selection.from).node().type.name === "content"; + doc.resolve(selection.from).node().type.spec.group === "blockContent"; const selectionEndInBlockContent = - doc.resolve(selection.to).node().type.name === "content"; + doc.resolve(selection.to).node().type.spec.group === "blockContent"; // Ensures that entire outermost nodes are selected if the selection spans multiple nesting levels. const minDepth = Math.min(selection.$anchor.depth, selection.$head.depth); @@ -220,7 +218,7 @@ function dragStart(e: DragEvent, view: EditorView) { } } -export const createDraggableBlocksPlugin = () => { +export const createDraggableBlocksPlugin = (editor: Editor) => { let dropElement: HTMLElement | undefined; const WIDTH = 48; @@ -358,12 +356,12 @@ export const createDraggableBlocksPlugin = () => { ReactDOM.render( , dropElement diff --git a/packages/core/src/extensions/DraggableBlocks/components/DragHandle.tsx b/packages/core/src/extensions/DraggableBlocks/components/DragHandle.tsx index 1df34b1098..c880b556b8 100644 --- a/packages/core/src/extensions/DraggableBlocks/components/DragHandle.tsx +++ b/packages/core/src/extensions/DraggableBlocks/components/DragHandle.tsx @@ -1,78 +1,60 @@ -import { TextSelection } from "prosemirror-state"; -import { EditorView } from "prosemirror-view"; -import { useState } from "react"; -import { AiOutlinePlus } from "react-icons/ai"; -import { findBlock } from "../../Blocks/helpers/findBlock"; -import { SlashMenuPluginKey } from "../../SlashMenu/SlashMenuExtension"; -import { Menu } from "@mantine/core"; -import { MdDragIndicator } from "react-icons/all"; -import { ActionIcon } from "@mantine/core"; +import { Editor } from "@tiptap/core"; +import { ActionIcon, Menu } from "@mantine/core"; +import { AiOutlinePlus, MdDragIndicator } from "react-icons/all"; import DragHandleMenu from "./DragHandleMenu"; +import { SlashMenuPluginKey } from "../../SlashMenu/SlashMenuExtension"; +import { getBlockInfoFromPos } from "../../Blocks/helpers/getBlockInfoFromPos"; export const DragHandle = (props: { - view: EditorView; + editor: Editor; coords: { left: number; top: number }; onShow?: () => void; onHide?: () => void; onAddClicked?: () => void; }) => { - const [clicked, setClicked] = useState(false); - const [deleted, setDeleted] = useState(false); - const onDelete = () => { - const pos = props.view.posAtCoords(props.coords); + const pos = props.editor.view.posAtCoords(props.coords); if (!pos) { return; } - const currentBlock = findBlock( - TextSelection.create(props.view.state.doc, pos.pos) - ); - if (currentBlock) { - if (props.view.dispatch) { - props.view.dispatch( - props.view.state.tr.deleteRange( - currentBlock.pos, - currentBlock.pos + currentBlock.node.nodeSize - ) - ); - } - setDeleted(true); - } + props.editor.commands.BNDeleteBlock(pos.pos); }; const onAddClick = () => { - setClicked(true); if (props.onAddClicked) { props.onAddClicked(); } - const pos = props.view.posAtCoords(props.coords); + + const pos = props.editor.view.posAtCoords(props.coords); if (!pos) { return; } - const currentBlock = findBlock( - TextSelection.create(props.view.state.doc, pos.pos) - ); - if (!currentBlock) { + + const blockInfo = getBlockInfoFromPos(props.editor.state.doc, pos.pos); + if (blockInfo === undefined) { return; } - // If current blocks content is empty dont create a new block - if (currentBlock.node.firstChild?.textContent.length !== 0) { - // Create new block after current block - const endOfBlock = currentBlock.pos + currentBlock.node.nodeSize; - let newBlock = props.view.state.schema.nodes["content"].createAndFill()!; - props.view.state.tr.insert(endOfBlock, newBlock); - props.view.dispatch(props.view.state.tr.insert(endOfBlock, newBlock)); - props.view.dispatch( - props.view.state.tr.setSelection( - new TextSelection(props.view.state.tr.doc.resolve(endOfBlock + 1)) - ) - ); + + const { contentNode, endPos } = blockInfo; + + // Creates a new block if current one is not empty for the suggestion menu to open in. + if (contentNode.textContent.length !== 0) { + const newBlockInsertionPos = endPos + 1; + const newBlockContentPos = newBlockInsertionPos + 2; + + props.editor + .chain() + .BNCreateBlock(newBlockInsertionPos) + .BNSetContentType(newBlockContentPos, "textContent") + .setTextSelection(newBlockContentPos) + .run(); } - // Focus and activate slash menu - props.view.focus(); - props.view.dispatch( - props.view.state.tr.scrollIntoView().setMeta(SlashMenuPluginKey, { + + // Focuses and activates the suggestion menu. + props.editor.view.focus(); + props.editor.view.dispatch( + props.editor.view.state.tr.scrollIntoView().setMeta(SlashMenuPluginKey, { // TODO import suggestion plugin key activate: true, type: "drag", @@ -80,10 +62,6 @@ export const DragHandle = (props: { ); }; - if (deleted || clicked) { - return null; - } - return (
diff --git a/packages/core/src/extensions/SlashMenu/defaultCommands.tsx b/packages/core/src/extensions/SlashMenu/defaultCommands.tsx index 045c311c96..f5024a56cb 100644 --- a/packages/core/src/extensions/SlashMenu/defaultCommands.tsx +++ b/packages/core/src/extensions/SlashMenu/defaultCommands.tsx @@ -22,7 +22,9 @@ const defaultCommands: { [key: string]: SlashMenuItem } = { .chain() .focus() .deleteRange(range) - .addNewBlockAsSibling({ headingType: "1" }) + .BNCreateBlockOrSetContentType(range.from, "headingContent", { + headingLevel: "1", + }) .run(); }, ["h", "heading1", "h1"], @@ -40,7 +42,9 @@ const defaultCommands: { [key: string]: SlashMenuItem } = { .chain() .focus() .deleteRange(range) - .addNewBlockAsSibling({ headingType: "2" }) + .BNCreateBlockOrSetContentType(range.from, "headingContent", { + headingLevel: "2", + }) .run(); }, ["h2", "heading2", "subheading"], @@ -58,7 +62,9 @@ const defaultCommands: { [key: string]: SlashMenuItem } = { .chain() .focus() .deleteRange(range) - .addNewBlockAsSibling({ headingType: "3" }) + .BNCreateBlockOrSetContentType(range.from, "headingContent", { + headingLevel: "3", + }) .run(); }, ["h3", "heading3", "subheading"], @@ -76,7 +82,9 @@ const defaultCommands: { [key: string]: SlashMenuItem } = { .chain() .focus() .deleteRange(range) - .addNewBlockAsSibling({ listType: "oli" }) + .BNCreateBlockOrSetContentType(range.from, "listItemContent", { + listItemType: "ordered", + }) .run(); }, ["li", "list", "numberedlist", "numbered list"], @@ -86,7 +94,7 @@ const defaultCommands: { [key: string]: SlashMenuItem } = { ), // Command for creating a bullet list - bulletlist: new SlashMenuItem( + bulletList: new SlashMenuItem( "Bullet List", SlashMenuGroups.BASIC_BLOCKS, (editor, range) => { @@ -94,7 +102,9 @@ const defaultCommands: { [key: string]: SlashMenuItem } = { .chain() .focus() .deleteRange(range) - .addNewBlockAsSibling({ listType: "li" }) + .BNCreateBlockOrSetContentType(range.from, "listItemContent", { + listItemType: "unordered", + }) .run(); }, ["ul", "list", "bulletlist", "bullet list"], @@ -112,7 +122,7 @@ const defaultCommands: { [key: string]: SlashMenuItem } = { .chain() .focus() .deleteRange(range) - .addNewBlockAsSibling() + .BNCreateBlockOrSetContentType(range.from, "textContent") .run(); }, ["p"], diff --git a/packages/core/src/extensions/TrailingNode/TrailingNodeExtension.ts b/packages/core/src/extensions/TrailingNode/TrailingNodeExtension.ts index c5d09826e6..1d3a00f466 100644 --- a/packages/core/src/extensions/TrailingNode/TrailingNodeExtension.ts +++ b/packages/core/src/extensions/TrailingNode/TrailingNodeExtension.ts @@ -33,14 +33,14 @@ export const TrailingNode = Extension.create({ const shouldInsertNodeAtEnd = plugin.getState(state); const endPosition = doc.content.size - 2; const type = schema.nodes["block"]; - const contenttype = schema.nodes["content"]; + const contentType = schema.nodes["textContent"]; if (!shouldInsertNodeAtEnd) { return; } return tr.insert( endPosition, - type.create(undefined, contenttype.create()) + type.create(undefined, contentType.create()) ); }, state: { @@ -55,8 +55,8 @@ export const TrailingNode = Extension.create({ let lastNode = tr.doc.lastChild; - if (!lastNode || lastNode.type.name !== "blockgroup") { - throw new Error("Expected blockgroup"); + if (!lastNode || lastNode.type.name !== "blockGroup") { + throw new Error("Expected blockGroup"); } lastNode = lastNode.lastChild; diff --git a/packages/core/src/extensions/UniqueID/UniqueID.ts b/packages/core/src/extensions/UniqueID/UniqueID.ts index 95028adf2e..10f8c8de50 100644 --- a/packages/core/src/extensions/UniqueID/UniqueID.ts +++ b/packages/core/src/extensions/UniqueID/UniqueID.ts @@ -51,7 +51,20 @@ const UniqueID = Extension.create({ return { attributeName: "id", types: [], - generateID: () => v4(), + generateID: () => { + // Use mock ID if tests are running. + if ((window as any).__TEST_OPTIONS) { + if ((window as any).__TEST_OPTIONS.mockID === undefined) { + (window as any).__TEST_OPTIONS.mockID = 0; + } else { + (window as any).__TEST_OPTIONS.mockID++; + } + + return parseInt((window as any).__TEST_OPTIONS.mockID); + } + + return v4(); + }, filterTransaction: null, }; }, diff --git a/packages/core/src/shared/plugins/suggestion/SuggestionPlugin.ts b/packages/core/src/shared/plugins/suggestion/SuggestionPlugin.ts index 15ff186ff4..67b9372c51 100644 --- a/packages/core/src/shared/plugins/suggestion/SuggestionPlugin.ts +++ b/packages/core/src/shared/plugins/suggestion/SuggestionPlugin.ts @@ -216,6 +216,11 @@ export function createSuggestionPlugin({ const { selection } = transaction; const next = { ...prev }; + // TODO: More clearly define which transactions should be ignored and which should deactivate the menu. + if (transaction.getMeta("orderedListIndexing") !== undefined) { + return next; + } + if ( // only show popup if selection is a blinking cursor selection.from === selection.to && diff --git a/tests/end-to-end/basics/basics.test.ts b/tests/end-to-end/basics/basics.test.ts index 4916314970..e835649b06 100644 --- a/tests/end-to-end/basics/basics.test.ts +++ b/tests/end-to-end/basics/basics.test.ts @@ -1,4 +1,5 @@ -import { test, expect, Page } from "@playwright/test"; +import { expect } from "@playwright/test"; +import { test } from "../../setup/setupScript"; import { BASE_URL } from "../../utils/const"; test.beforeEach(async ({ page }) => { diff --git a/tests/end-to-end/copypaste/copypaste.test.ts b/tests/end-to-end/copypaste/copypaste.test.ts index 9e8e37e248..04bea726ae 100644 --- a/tests/end-to-end/copypaste/copypaste.test.ts +++ b/tests/end-to-end/copypaste/copypaste.test.ts @@ -1,4 +1,4 @@ -import { test } from "@playwright/test"; +import { test } from "../../setup/setupScript"; import { BASE_URL } from "../../utils/const"; import { compareDocToSnapshot, focusOnEditor } from "../../utils/editor"; import { diff --git a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/headings-json-chromium-linux.json b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/headings-json-chromium-linux.json index 7f36021b39..cf38556fff 100644 --- a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/headings-json-chromium-linux.json +++ b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/headings-json-chromium-linux.json @@ -2,17 +2,19 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "1" + "id": 0 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, "content": [ { "type": "text", @@ -25,12 +27,14 @@ { "type": "block", "attrs": { - "headingType": "2" + "id": 1 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "2" + }, "content": [ { "type": "text", @@ -43,12 +47,14 @@ { "type": "block", "attrs": { - "headingType": "3" + "id": 2 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "3" + }, "content": [ { "type": "text", @@ -60,23 +66,26 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 3 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] }, { "type": "block", "attrs": { - "headingType": "1" + "id": 5 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, "content": [ { "type": "text", @@ -89,12 +98,14 @@ { "type": "block", "attrs": { - "headingType": "2" + "id": 6 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "2" + }, "content": [ { "type": "text", @@ -107,12 +118,14 @@ { "type": "block", "attrs": { - "headingType": "3" + "id": 7 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "3" + }, "content": [ { "type": "text", @@ -124,11 +137,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 8 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/headings-json-firefox-linux.json b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/headings-json-firefox-linux.json index 7f36021b39..cf38556fff 100644 --- a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/headings-json-firefox-linux.json +++ b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/headings-json-firefox-linux.json @@ -2,17 +2,19 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "1" + "id": 0 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, "content": [ { "type": "text", @@ -25,12 +27,14 @@ { "type": "block", "attrs": { - "headingType": "2" + "id": 1 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "2" + }, "content": [ { "type": "text", @@ -43,12 +47,14 @@ { "type": "block", "attrs": { - "headingType": "3" + "id": 2 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "3" + }, "content": [ { "type": "text", @@ -60,23 +66,26 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 3 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] }, { "type": "block", "attrs": { - "headingType": "1" + "id": 5 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, "content": [ { "type": "text", @@ -89,12 +98,14 @@ { "type": "block", "attrs": { - "headingType": "2" + "id": 6 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "2" + }, "content": [ { "type": "text", @@ -107,12 +118,14 @@ { "type": "block", "attrs": { - "headingType": "3" + "id": 7 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "3" + }, "content": [ { "type": "text", @@ -124,11 +137,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 8 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/headings-json-webkit-linux.json b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/headings-json-webkit-linux.json index 7f36021b39..cf38556fff 100644 --- a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/headings-json-webkit-linux.json +++ b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/headings-json-webkit-linux.json @@ -2,17 +2,19 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "1" + "id": 0 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, "content": [ { "type": "text", @@ -25,12 +27,14 @@ { "type": "block", "attrs": { - "headingType": "2" + "id": 1 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "2" + }, "content": [ { "type": "text", @@ -43,12 +47,14 @@ { "type": "block", "attrs": { - "headingType": "3" + "id": 2 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "3" + }, "content": [ { "type": "text", @@ -60,23 +66,26 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 3 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] }, { "type": "block", "attrs": { - "headingType": "1" + "id": 5 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, "content": [ { "type": "text", @@ -89,12 +98,14 @@ { "type": "block", "attrs": { - "headingType": "2" + "id": 6 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "2" + }, "content": [ { "type": "text", @@ -107,12 +118,14 @@ { "type": "block", "attrs": { - "headingType": "3" + "id": 7 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "3" + }, "content": [ { "type": "text", @@ -124,11 +137,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 8 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedOrderedLists-json-chromium-linux.json b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedOrderedLists-json-chromium-linux.json index a5a9666c72..6ed98572fd 100644 --- a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedOrderedLists-json-chromium-linux.json +++ b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedOrderedLists-json-chromium-linux.json @@ -2,18 +2,19 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "oli" + "id": 0 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "1." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -23,18 +24,19 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "oli" + "id": 2 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "2." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -44,18 +46,19 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "oli" + "id": 3 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "3." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -76,24 +79,26 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] }, { "type": "block", "attrs": { - "listType": "oli" + "id": 5 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "1." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -103,18 +108,19 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "oli" + "id": 6 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "2." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -124,18 +130,19 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "oli" + "id": 7 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "3." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -156,11 +163,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 8 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedOrderedLists-json-firefox-linux.json b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedOrderedLists-json-firefox-linux.json index a5a9666c72..6ed98572fd 100644 --- a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedOrderedLists-json-firefox-linux.json +++ b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedOrderedLists-json-firefox-linux.json @@ -2,18 +2,19 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "oli" + "id": 0 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "1." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -23,18 +24,19 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "oli" + "id": 2 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "2." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -44,18 +46,19 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "oli" + "id": 3 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "3." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -76,24 +79,26 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] }, { "type": "block", "attrs": { - "listType": "oli" + "id": 5 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "1." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -103,18 +108,19 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "oli" + "id": 6 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "2." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -124,18 +130,19 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "oli" + "id": 7 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "3." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -156,11 +163,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 8 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedOrderedLists-json-webkit-linux.json b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedOrderedLists-json-webkit-linux.json index a5a9666c72..6ed98572fd 100644 --- a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedOrderedLists-json-webkit-linux.json +++ b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedOrderedLists-json-webkit-linux.json @@ -2,18 +2,19 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "oli" + "id": 0 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "1." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -23,18 +24,19 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "oli" + "id": 2 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "2." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -44,18 +46,19 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "oli" + "id": 3 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "3." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -76,24 +79,26 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] }, { "type": "block", "attrs": { - "listType": "oli" + "id": 5 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "1." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -103,18 +108,19 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "oli" + "id": 6 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "2." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -124,18 +130,19 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "oli" + "id": 7 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "3." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -156,11 +163,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 8 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedParagraphs-json-chromium-linux.json b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedParagraphs-json-chromium-linux.json index 657d2fbe3b..228676beb2 100644 --- a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedParagraphs-json-chromium-linux.json +++ b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedParagraphs-json-chromium-linux.json @@ -2,15 +2,16 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", - "attrs": {}, + "attrs": { + "id": 0 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -19,15 +20,16 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -36,15 +38,16 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", - "attrs": {}, + "attrs": { + "id": 2 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -64,21 +67,23 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 3 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 5 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -87,15 +92,16 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", - "attrs": {}, + "attrs": { + "id": 6 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -104,15 +110,16 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", - "attrs": {}, + "attrs": { + "id": 7 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -132,11 +139,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 8 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedParagraphs-json-firefox-linux.json b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedParagraphs-json-firefox-linux.json index 657d2fbe3b..228676beb2 100644 --- a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedParagraphs-json-firefox-linux.json +++ b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedParagraphs-json-firefox-linux.json @@ -2,15 +2,16 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", - "attrs": {}, + "attrs": { + "id": 0 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -19,15 +20,16 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -36,15 +38,16 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", - "attrs": {}, + "attrs": { + "id": 2 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -64,21 +67,23 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 3 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 5 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -87,15 +92,16 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", - "attrs": {}, + "attrs": { + "id": 6 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -104,15 +110,16 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", - "attrs": {}, + "attrs": { + "id": 7 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -132,11 +139,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 8 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedParagraphs-json-webkit-linux.json b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedParagraphs-json-webkit-linux.json index 657d2fbe3b..228676beb2 100644 --- a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedParagraphs-json-webkit-linux.json +++ b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedParagraphs-json-webkit-linux.json @@ -2,15 +2,16 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", - "attrs": {}, + "attrs": { + "id": 0 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -19,15 +20,16 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -36,15 +38,16 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", - "attrs": {}, + "attrs": { + "id": 2 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -64,21 +67,23 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 3 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 5 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -87,15 +92,16 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", - "attrs": {}, + "attrs": { + "id": 6 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -104,15 +110,16 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", - "attrs": {}, + "attrs": { + "id": 7 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -132,11 +139,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 8 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedUnorderedLists-json-chromium-linux.json b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedUnorderedLists-json-chromium-linux.json index 4c8ed656a5..228d4862f9 100644 --- a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedUnorderedLists-json-chromium-linux.json +++ b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedUnorderedLists-json-chromium-linux.json @@ -2,17 +2,20 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "li" + "id": 0 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -21,17 +24,20 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "li" + "id": 2 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -40,17 +46,20 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "li" + "id": 3 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -70,23 +79,27 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] }, { "type": "block", "attrs": { - "listType": "li" + "id": 5 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -95,17 +108,20 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "li" + "id": 6 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -114,17 +130,20 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "li" + "id": 7 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -144,11 +163,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 8 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedUnorderedLists-json-firefox-linux.json b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedUnorderedLists-json-firefox-linux.json index 4c8ed656a5..228d4862f9 100644 --- a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedUnorderedLists-json-firefox-linux.json +++ b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedUnorderedLists-json-firefox-linux.json @@ -2,17 +2,20 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "li" + "id": 0 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -21,17 +24,20 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "li" + "id": 2 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -40,17 +46,20 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "li" + "id": 3 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -70,23 +79,27 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] }, { "type": "block", "attrs": { - "listType": "li" + "id": 5 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -95,17 +108,20 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "li" + "id": 6 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -114,17 +130,20 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "li" + "id": 7 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -144,11 +163,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 8 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedUnorderedLists-json-webkit-linux.json b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedUnorderedLists-json-webkit-linux.json index 4c8ed656a5..228d4862f9 100644 --- a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedUnorderedLists-json-webkit-linux.json +++ b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/nestedUnorderedLists-json-webkit-linux.json @@ -2,17 +2,20 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "li" + "id": 0 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -21,17 +24,20 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "li" + "id": 2 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -40,17 +46,20 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "li" + "id": 3 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -70,23 +79,27 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] }, { "type": "block", "attrs": { - "listType": "li" + "id": 5 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -95,17 +108,20 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "li" + "id": 6 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -114,17 +130,20 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "li" + "id": 7 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -144,11 +163,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 8 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/orderedLists-json-chromium-linux.json b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/orderedLists-json-chromium-linux.json index 355923c13b..63e9d42a84 100644 --- a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/orderedLists-json-chromium-linux.json +++ b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/orderedLists-json-chromium-linux.json @@ -2,18 +2,19 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "oli" + "id": 0 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "1." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -27,13 +28,14 @@ { "type": "block", "attrs": { - "listType": "oli" + "id": 2 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "2." + "listItemType": "ordered", + "listItemIndex": "2" }, "content": [ { @@ -47,13 +49,14 @@ { "type": "block", "attrs": { - "listType": "oli" + "id": 3 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "3." + "listItemType": "ordered", + "listItemIndex": "3" }, "content": [ { @@ -66,24 +69,26 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] }, { "type": "block", "attrs": { - "listType": "oli" + "id": 5 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "1." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -97,13 +102,14 @@ { "type": "block", "attrs": { - "listType": "oli" + "id": 6 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "2." + "listItemType": "ordered", + "listItemIndex": "2" }, "content": [ { @@ -117,13 +123,14 @@ { "type": "block", "attrs": { - "listType": "oli" + "id": 7 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "3." + "listItemType": "ordered", + "listItemIndex": "3" }, "content": [ { @@ -136,11 +143,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 8 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/orderedLists-json-firefox-linux.json b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/orderedLists-json-firefox-linux.json index 355923c13b..63e9d42a84 100644 --- a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/orderedLists-json-firefox-linux.json +++ b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/orderedLists-json-firefox-linux.json @@ -2,18 +2,19 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "oli" + "id": 0 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "1." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -27,13 +28,14 @@ { "type": "block", "attrs": { - "listType": "oli" + "id": 2 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "2." + "listItemType": "ordered", + "listItemIndex": "2" }, "content": [ { @@ -47,13 +49,14 @@ { "type": "block", "attrs": { - "listType": "oli" + "id": 3 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "3." + "listItemType": "ordered", + "listItemIndex": "3" }, "content": [ { @@ -66,24 +69,26 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] }, { "type": "block", "attrs": { - "listType": "oli" + "id": 5 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "1." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -97,13 +102,14 @@ { "type": "block", "attrs": { - "listType": "oli" + "id": 6 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "2." + "listItemType": "ordered", + "listItemIndex": "2" }, "content": [ { @@ -117,13 +123,14 @@ { "type": "block", "attrs": { - "listType": "oli" + "id": 7 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "3." + "listItemType": "ordered", + "listItemIndex": "3" }, "content": [ { @@ -136,11 +143,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 8 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/orderedLists-json-webkit-linux.json b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/orderedLists-json-webkit-linux.json index 355923c13b..63e9d42a84 100644 --- a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/orderedLists-json-webkit-linux.json +++ b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/orderedLists-json-webkit-linux.json @@ -2,18 +2,19 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "oli" + "id": 0 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "1." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -27,13 +28,14 @@ { "type": "block", "attrs": { - "listType": "oli" + "id": 2 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "2." + "listItemType": "ordered", + "listItemIndex": "2" }, "content": [ { @@ -47,13 +49,14 @@ { "type": "block", "attrs": { - "listType": "oli" + "id": 3 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "3." + "listItemType": "ordered", + "listItemIndex": "3" }, "content": [ { @@ -66,24 +69,26 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] }, { "type": "block", "attrs": { - "listType": "oli" + "id": 5 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "1." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -97,13 +102,14 @@ { "type": "block", "attrs": { - "listType": "oli" + "id": 6 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "2." + "listItemType": "ordered", + "listItemIndex": "2" }, "content": [ { @@ -117,13 +123,14 @@ { "type": "block", "attrs": { - "listType": "oli" + "id": 7 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "3." + "listItemType": "ordered", + "listItemIndex": "3" }, "content": [ { @@ -136,11 +143,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 8 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/paragraphs-json-chromium-linux.json b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/paragraphs-json-chromium-linux.json index c2a97c12d0..fe2b9a15f3 100644 --- a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/paragraphs-json-chromium-linux.json +++ b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/paragraphs-json-chromium-linux.json @@ -2,15 +2,16 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", - "attrs": {}, + "attrs": { + "id": 0 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -22,11 +23,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -38,11 +40,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 2 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -54,21 +57,23 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 3 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 5 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -80,11 +85,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 6 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -96,11 +102,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 7 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -112,11 +119,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 8 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/paragraphs-json-firefox-linux.json b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/paragraphs-json-firefox-linux.json index c2a97c12d0..fe2b9a15f3 100644 --- a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/paragraphs-json-firefox-linux.json +++ b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/paragraphs-json-firefox-linux.json @@ -2,15 +2,16 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", - "attrs": {}, + "attrs": { + "id": 0 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -22,11 +23,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -38,11 +40,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 2 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -54,21 +57,23 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 3 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 5 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -80,11 +85,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 6 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -96,11 +102,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 7 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -112,11 +119,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 8 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/paragraphs-json-webkit-linux.json b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/paragraphs-json-webkit-linux.json index c2a97c12d0..fe2b9a15f3 100644 --- a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/paragraphs-json-webkit-linux.json +++ b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/paragraphs-json-webkit-linux.json @@ -2,15 +2,16 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", - "attrs": {}, + "attrs": { + "id": 0 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -22,11 +23,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -38,11 +40,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 2 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -54,21 +57,23 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 3 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 5 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -80,11 +85,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 6 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -96,11 +102,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 7 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -112,11 +119,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 8 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/unorderedLists-json-chromium-linux.json b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/unorderedLists-json-chromium-linux.json index be056d087c..9692e65de9 100644 --- a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/unorderedLists-json-chromium-linux.json +++ b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/unorderedLists-json-chromium-linux.json @@ -2,17 +2,20 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "li" + "id": 0 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -25,12 +28,15 @@ { "type": "block", "attrs": { - "listType": "li" + "id": 2 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -43,12 +49,15 @@ { "type": "block", "attrs": { - "listType": "li" + "id": 3 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -60,23 +69,27 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] }, { "type": "block", "attrs": { - "listType": "li" + "id": 5 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -89,12 +102,15 @@ { "type": "block", "attrs": { - "listType": "li" + "id": 6 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -107,12 +123,15 @@ { "type": "block", "attrs": { - "listType": "li" + "id": 7 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -124,11 +143,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 8 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/unorderedLists-json-firefox-linux.json b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/unorderedLists-json-firefox-linux.json index be056d087c..9692e65de9 100644 --- a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/unorderedLists-json-firefox-linux.json +++ b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/unorderedLists-json-firefox-linux.json @@ -2,17 +2,20 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "li" + "id": 0 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -25,12 +28,15 @@ { "type": "block", "attrs": { - "listType": "li" + "id": 2 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -43,12 +49,15 @@ { "type": "block", "attrs": { - "listType": "li" + "id": 3 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -60,23 +69,27 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] }, { "type": "block", "attrs": { - "listType": "li" + "id": 5 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -89,12 +102,15 @@ { "type": "block", "attrs": { - "listType": "li" + "id": 6 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -107,12 +123,15 @@ { "type": "block", "attrs": { - "listType": "li" + "id": 7 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -124,11 +143,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 8 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/unorderedLists-json-webkit-linux.json b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/unorderedLists-json-webkit-linux.json index be056d087c..9692e65de9 100644 --- a/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/unorderedLists-json-webkit-linux.json +++ b/tests/end-to-end/copypaste/copypaste.test.ts-snapshots/unorderedLists-json-webkit-linux.json @@ -2,17 +2,20 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "listType": "li" + "id": 0 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -25,12 +28,15 @@ { "type": "block", "attrs": { - "listType": "li" + "id": 2 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -43,12 +49,15 @@ { "type": "block", "attrs": { - "listType": "li" + "id": 3 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -60,23 +69,27 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] }, { "type": "block", "attrs": { - "listType": "li" + "id": 5 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -89,12 +102,15 @@ { "type": "block", "attrs": { - "listType": "li" + "id": 6 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -107,12 +123,15 @@ { "type": "block", "attrs": { - "listType": "li" + "id": 7 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -124,11 +143,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 8 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/dragdrop/dragdrop.test.ts b/tests/end-to-end/dragdrop/dragdrop.test.ts index aea2a9d916..ac671cb08f 100644 --- a/tests/end-to-end/dragdrop/dragdrop.test.ts +++ b/tests/end-to-end/dragdrop/dragdrop.test.ts @@ -1,4 +1,4 @@ -import { test } from "@playwright/test"; +import { test } from "../../setup/setupScript"; import { BASE_URL, BLOCK_SELECTOR, diff --git a/tests/end-to-end/dragdrop/dragdrop.test.ts-snapshots/dragdropnested-chromium-linux.json b/tests/end-to-end/dragdrop/dragdrop.test.ts-snapshots/dragdropnested-chromium-linux.json index e7bd190c1b..cb71f421d7 100644 --- a/tests/end-to-end/dragdrop/dragdrop.test.ts-snapshots/dragdropnested-chromium-linux.json +++ b/tests/end-to-end/dragdrop/dragdrop.test.ts-snapshots/dragdropnested-chromium-linux.json @@ -2,15 +2,16 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -19,17 +20,19 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "1" + "id": 6 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, "content": [ { "type": "text", @@ -41,11 +44,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 3 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -54,17 +58,19 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "2" + "id": 7 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "2" + }, "content": [ { "type": "text", @@ -85,12 +91,14 @@ { "type": "block", "attrs": { - "headingType": "3" + "id": 8 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "3" + }, "content": [ { "type": "text", @@ -102,11 +110,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 5 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/dragdrop/dragdrop.test.ts-snapshots/dragdropnested-firefox-linux.json b/tests/end-to-end/dragdrop/dragdrop.test.ts-snapshots/dragdropnested-firefox-linux.json index ad6e7b192d..cb71f421d7 100644 --- a/tests/end-to-end/dragdrop/dragdrop.test.ts-snapshots/dragdropnested-firefox-linux.json +++ b/tests/end-to-end/dragdrop/dragdrop.test.ts-snapshots/dragdropnested-firefox-linux.json @@ -2,33 +2,16 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "1" + "id": 1 }, "content": [ { - "type": "content", - "attrs": {}, - "content": [ - { - "type": "text", - "text": "Heading" - } - ] - } - ] - }, - { - "type": "block", - "attrs": {}, - "content": [ - { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -37,21 +20,23 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "2" + "id": 6 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, "content": [ { "type": "text", - "text": "HeadingHeading" + "text": "Heading" } ] } @@ -59,11 +44,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 3 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -72,21 +58,23 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "3" + "id": 7 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "2" + }, "content": [ { "type": "text", - "text": "HeadingHeadingHeading" + "text": "Heading" } ] } @@ -102,15 +90,19 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 8 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "3" + }, "content": [ { "type": "text", - "text": "HeadingHeadingHeading" + "text": "Heading" } ] } @@ -118,11 +110,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 5 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/dragdrop/dragdrop.test.ts-snapshots/dragdropnested-webkit-linux.json b/tests/end-to-end/dragdrop/dragdrop.test.ts-snapshots/dragdropnested-webkit-linux.json index e7bd190c1b..cb71f421d7 100644 --- a/tests/end-to-end/dragdrop/dragdrop.test.ts-snapshots/dragdropnested-webkit-linux.json +++ b/tests/end-to-end/dragdrop/dragdrop.test.ts-snapshots/dragdropnested-webkit-linux.json @@ -2,15 +2,16 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -19,17 +20,19 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "1" + "id": 6 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, "content": [ { "type": "text", @@ -41,11 +44,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 3 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -54,17 +58,19 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "2" + "id": 7 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "2" + }, "content": [ { "type": "text", @@ -85,12 +91,14 @@ { "type": "block", "attrs": { - "headingType": "3" + "id": 8 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "3" + }, "content": [ { "type": "text", @@ -102,11 +110,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 5 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/dragdrop/dragdrop.test.ts-snapshots/dragdropsingle-chromium-linux.json b/tests/end-to-end/dragdrop/dragdrop.test.ts-snapshots/dragdropsingle-chromium-linux.json index 7e14488f90..19acdb438d 100644 --- a/tests/end-to-end/dragdrop/dragdrop.test.ts-snapshots/dragdropsingle-chromium-linux.json +++ b/tests/end-to-end/dragdrop/dragdrop.test.ts-snapshots/dragdropsingle-chromium-linux.json @@ -2,17 +2,19 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "2" + "id": 1 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "2" + }, "content": [ { "type": "text", @@ -25,12 +27,14 @@ { "type": "block", "attrs": { - "headingType": "1" + "id": 4 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, "content": [ { "type": "text", @@ -43,12 +47,14 @@ { "type": "block", "attrs": { - "headingType": "3" + "id": 2 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "3" + }, "content": [ { "type": "text", @@ -60,11 +66,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 3 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/dragdrop/dragdrop.test.ts-snapshots/dragdropsingle-firefox-linux.json b/tests/end-to-end/dragdrop/dragdrop.test.ts-snapshots/dragdropsingle-firefox-linux.json index fd11cf4be5..19acdb438d 100644 --- a/tests/end-to-end/dragdrop/dragdrop.test.ts-snapshots/dragdropsingle-firefox-linux.json +++ b/tests/end-to-end/dragdrop/dragdrop.test.ts-snapshots/dragdropsingle-firefox-linux.json @@ -2,17 +2,19 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "1" + "id": 1 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "2" + }, "content": [ { "type": "text", @@ -25,16 +27,18 @@ { "type": "block", "attrs": { - "headingType": "2" + "id": 4 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, "content": [ { "type": "text", - "text": "HeadingHeading" + "text": "Heading" } ] } @@ -43,12 +47,14 @@ { "type": "block", "attrs": { - "headingType": "3" + "id": 2 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "3" + }, "content": [ { "type": "text", @@ -60,11 +66,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 3 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/dragdrop/dragdrop.test.ts-snapshots/dragdropsingle-webkit-linux.json b/tests/end-to-end/dragdrop/dragdrop.test.ts-snapshots/dragdropsingle-webkit-linux.json index 7e14488f90..19acdb438d 100644 --- a/tests/end-to-end/dragdrop/dragdrop.test.ts-snapshots/dragdropsingle-webkit-linux.json +++ b/tests/end-to-end/dragdrop/dragdrop.test.ts-snapshots/dragdropsingle-webkit-linux.json @@ -2,17 +2,19 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "2" + "id": 1 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "2" + }, "content": [ { "type": "text", @@ -25,12 +27,14 @@ { "type": "block", "attrs": { - "headingType": "1" + "id": 4 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, "content": [ { "type": "text", @@ -43,12 +47,14 @@ { "type": "block", "attrs": { - "headingType": "3" + "id": 2 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "3" + }, "content": [ { "type": "text", @@ -60,11 +66,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 3 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/draghandle/draghandle.test.ts b/tests/end-to-end/draghandle/draghandle.test.ts index 003d18c817..f9c924ea35 100644 --- a/tests/end-to-end/draghandle/draghandle.test.ts +++ b/tests/end-to-end/draghandle/draghandle.test.ts @@ -1,14 +1,15 @@ -import { test, expect, Page } from "@playwright/test"; +import { expect, Page } from "@playwright/test"; +import { test } from "../../setup/setupScript"; import { BASE_URL, - BLOCK_CONTENT_SELECTOR, BLOCK_SELECTOR, - DRAG_HANDLE, - DRAG_HANDLE_ADD, + DRAG_HANDLE_SELECTOR, + DRAG_HANDLE_ADD_SELECTOR, DRAG_HANDLE_MENU_SELECTOR, H_ONE_BLOCK_SELECTOR, H_THREE_BLOCK_SELECTOR, H_TWO_BLOCK_SELECTOR, + PARAGRAPH_SELECTOR, SLASH_MENU_SELECTOR, } from "../../utils/const"; import { @@ -28,7 +29,7 @@ test.describe("Check Draghandle functionality", () => { }); test.beforeEach(async () => { - page.goto(BASE_URL, { waitUntil: "networkidle" }); + await page.goto(BASE_URL, { waitUntil: "networkidle" }); await focusOnEditor(page); }); @@ -41,7 +42,7 @@ test.describe("Check Draghandle functionality", () => { await page.keyboard.type("Hover over this text"); const heading = await page.locator(H_ONE_BLOCK_SELECTOR); await moveMouseOverElement(page, heading); - await page.waitForSelector(DRAG_HANDLE); + await page.waitForSelector(DRAG_HANDLE_SELECTOR); }); test("Draghandle should display next to correct block", async () => { @@ -65,11 +66,9 @@ test.describe("Check Draghandle functionality", () => { await insertHeading(page, 3); const h1y = await getDragHandleYCoord(page, H_ONE_BLOCK_SELECTOR); - await page.pause(); const h2y = await getDragHandleYCoord(page, H_TWO_BLOCK_SELECTOR); - await page.pause(); const h3y = await getDragHandleYCoord(page, H_THREE_BLOCK_SELECTOR); - await page.pause(); + expect(h1y < h2y && h1y < h3y && h2y < h3y).toBeTruthy(); }); @@ -78,9 +77,10 @@ test.describe("Check Draghandle functionality", () => { await page.keyboard.type("Hover over this text"); const heading = await page.locator(H_ONE_BLOCK_SELECTOR).first(); await moveMouseOverElement(page, heading); - await page.click(DRAG_HANDLE); + await page.click(DRAG_HANDLE_SELECTOR); await page.waitForSelector(DRAG_HANDLE_MENU_SELECTOR); // Compare editor screenshot + await page.waitForTimeout(1000); expect(await page.screenshot()).toMatchSnapshot("draghandlemenu.png"); }); @@ -99,14 +99,15 @@ test.describe("Check Draghandle functionality", () => { test("Clicking add button should show filter message", async () => { const block = await page.locator(BLOCK_SELECTOR); await moveMouseOverElement(page, block); - await page.click(DRAG_HANDLE_ADD); - const content = await page.waitForSelector(BLOCK_CONTENT_SELECTOR); + await page.click(DRAG_HANDLE_ADD_SELECTOR); + const content = await page.waitForSelector(PARAGRAPH_SELECTOR); // Get text in :before const text = await content.evaluate((el) => window .getComputedStyle(el.children[0], ":before") .getPropertyValue("content") ); + expect(text).toBe('"Type to filter"'); }); @@ -115,7 +116,7 @@ test.describe("Check Draghandle functionality", () => { await page.keyboard.type("Hover over this text"); const heading = await page.locator(H_ONE_BLOCK_SELECTOR); await moveMouseOverElement(page, heading); - await page.click(DRAG_HANDLE_ADD); + await page.click(DRAG_HANDLE_ADD_SELECTOR); await page.waitForSelector(SLASH_MENU_SELECTOR); }); @@ -123,18 +124,21 @@ test.describe("Check Draghandle functionality", () => { await executeSlashCommand(page, "h1"); await page.keyboard.type("Hover over this text"); await hoverAndAddBlockFromDragHandle(page, H_ONE_BLOCK_SELECTOR, "h2"); - await page.waitForSelector(DRAG_HANDLE, { state: "detached" }); - await page.waitForSelector(DRAG_HANDLE_ADD, { state: "detached" }); + await page.waitForSelector(DRAG_HANDLE_SELECTOR, { state: "detached" }); + await page.waitForSelector(DRAG_HANDLE_ADD_SELECTOR, { state: "detached" }); }); test("Clicking delete button should delete block", async () => { await executeSlashCommand(page, "h1"); await page.keyboard.type("Hover over this text"); + await page.hover(H_ONE_BLOCK_SELECTOR); - await page.click(DRAG_HANDLE); + await page.click(DRAG_HANDLE_SELECTOR); await page.waitForSelector(DRAG_HANDLE_MENU_SELECTOR); await page.click("text=Delete"); await page.locator(H_ONE_BLOCK_SELECTOR).waitFor({ state: "detached" }); + + await compareDocToSnapshot(page, "draghandledelete"); }); test("Delete button should delete correct block", async () => { @@ -144,12 +148,14 @@ test.describe("Check Draghandle functionality", () => { await page.keyboard.type("This is h2"); await executeSlashCommand(page, "h3"); await page.keyboard.type("This is h3"); + await page.hover(H_TWO_BLOCK_SELECTOR); - await page.click(DRAG_HANDLE); + await page.click(DRAG_HANDLE_SELECTOR); await page.click("text=Delete"); await page.waitForSelector(H_ONE_BLOCK_SELECTOR); await page.waitForSelector(H_TWO_BLOCK_SELECTOR, { state: "detached" }); await page.waitForSelector(H_THREE_BLOCK_SELECTOR); + // Compare doc object snapshot await compareDocToSnapshot(page, "dragHandleDocStructure"); }); @@ -167,11 +173,14 @@ test.describe("Check Draghandle functionality", () => { await page.keyboard.press("Tab", { delay: 10 }); await executeSlashCommand(page, "h3"); await page.keyboard.type("This is h3"); + await page.hover(H_TWO_BLOCK_SELECTOR); - await page.click(DRAG_HANDLE); + await page.click(DRAG_HANDLE_SELECTOR); await page.click("text=Delete"); await page.waitForSelector(H_ONE_BLOCK_SELECTOR); await page.waitForSelector(H_TWO_BLOCK_SELECTOR, { state: "detached" }); await page.waitForSelector(H_THREE_BLOCK_SELECTOR, { state: "detached" }); + + await compareDocToSnapshot(page, "draghandlenesteddelete"); }); }); diff --git a/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/dragHandleDocStructure-chromium-linux.json b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/dragHandleDocStructure-chromium-linux.json index bfb2811100..169d9474e9 100644 --- a/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/dragHandleDocStructure-chromium-linux.json +++ b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/dragHandleDocStructure-chromium-linux.json @@ -2,17 +2,19 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "1" + "id": 0 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, "content": [ { "type": "text", @@ -25,12 +27,14 @@ { "type": "block", "attrs": { - "headingType": "3" + "id": 3 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "3" + }, "content": [ { "type": "text", @@ -42,11 +46,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/dragHandleDocStructure-firefox-linux.json b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/dragHandleDocStructure-firefox-linux.json index bfb2811100..169d9474e9 100644 --- a/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/dragHandleDocStructure-firefox-linux.json +++ b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/dragHandleDocStructure-firefox-linux.json @@ -2,17 +2,19 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "1" + "id": 0 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, "content": [ { "type": "text", @@ -25,12 +27,14 @@ { "type": "block", "attrs": { - "headingType": "3" + "id": 3 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "3" + }, "content": [ { "type": "text", @@ -42,11 +46,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/dragHandleDocStructure-webkit-linux.json b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/dragHandleDocStructure-webkit-linux.json index bfb2811100..169d9474e9 100644 --- a/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/dragHandleDocStructure-webkit-linux.json +++ b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/dragHandleDocStructure-webkit-linux.json @@ -2,17 +2,19 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "1" + "id": 0 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, "content": [ { "type": "text", @@ -25,12 +27,14 @@ { "type": "block", "attrs": { - "headingType": "3" + "id": 3 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "3" + }, "content": [ { "type": "text", @@ -42,11 +46,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandleadd-chromium-linux.json b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandleadd-chromium-linux.json index dabd28bf5d..a625a26a48 100644 --- a/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandleadd-chromium-linux.json +++ b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandleadd-chromium-linux.json @@ -2,17 +2,19 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "1" + "id": 0 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, "content": [ { "type": "text", @@ -25,12 +27,14 @@ { "type": "block", "attrs": { - "headingType": "2" + "id": 2 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "2" + }, "content": [ { "type": "text", @@ -42,11 +46,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandleadd-firefox-linux.json b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandleadd-firefox-linux.json index 316432492f..a625a26a48 100644 --- a/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandleadd-firefox-linux.json +++ b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandleadd-firefox-linux.json @@ -2,17 +2,19 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "1" + "id": 0 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, "content": [ { "type": "text", @@ -25,43 +27,31 @@ { "type": "block", "attrs": { - "headingType": "2" + "id": 2 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "2" + }, "content": [ { "type": "text", "text": "This is an h2" } ] - }, - { - "type": "blockgroup", - "content": [ - { - "type": "block", - "attrs": {}, - "content": [ - { - "type": "content", - "attrs": {} - } - ] - } - ] } ] }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandleadd-webkit-linux.json b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandleadd-webkit-linux.json index dabd28bf5d..a625a26a48 100644 --- a/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandleadd-webkit-linux.json +++ b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandleadd-webkit-linux.json @@ -2,17 +2,19 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "1" + "id": 0 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, "content": [ { "type": "text", @@ -25,12 +27,14 @@ { "type": "block", "attrs": { - "headingType": "2" + "id": 2 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "2" + }, "content": [ { "type": "text", @@ -42,11 +46,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandledelete-chromium-linux.json b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandledelete-chromium-linux.json new file mode 100644 index 0000000000..7fe6b9c670 --- /dev/null +++ b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandledelete-chromium-linux.json @@ -0,0 +1,21 @@ +{ + "type": "doc", + "content": [ + { + "type": "blockGroup", + "content": [ + { + "type": "block", + "attrs": { + "id": 1 + }, + "content": [ + { + "type": "textContent" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandledelete-firefox-linux.json b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandledelete-firefox-linux.json new file mode 100644 index 0000000000..7fe6b9c670 --- /dev/null +++ b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandledelete-firefox-linux.json @@ -0,0 +1,21 @@ +{ + "type": "doc", + "content": [ + { + "type": "blockGroup", + "content": [ + { + "type": "block", + "attrs": { + "id": 1 + }, + "content": [ + { + "type": "textContent" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandledelete-webkit-linux.json b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandledelete-webkit-linux.json new file mode 100644 index 0000000000..7fe6b9c670 --- /dev/null +++ b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandledelete-webkit-linux.json @@ -0,0 +1,21 @@ +{ + "type": "doc", + "content": [ + { + "type": "blockGroup", + "content": [ + { + "type": "block", + "attrs": { + "id": 1 + }, + "content": [ + { + "type": "textContent" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandlemenu-firefox-linux.png b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandlemenu-firefox-linux.png index 3169b69ac7..3960ede100 100644 Binary files a/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandlemenu-firefox-linux.png and b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandlemenu-firefox-linux.png differ diff --git a/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandlenesteddelete-chromium-linux.json b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandlenesteddelete-chromium-linux.json new file mode 100644 index 0000000000..61108c1be4 --- /dev/null +++ b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandlenesteddelete-chromium-linux.json @@ -0,0 +1,41 @@ +{ + "type": "doc", + "content": [ + { + "type": "blockGroup", + "content": [ + { + "type": "block", + "attrs": { + "id": 0 + }, + "content": [ + { + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, + "content": [ + { + "type": "text", + "text": "This is h1" + } + ] + } + ] + }, + { + "type": "block", + "attrs": { + "id": 1 + }, + "content": [ + { + "type": "textContent" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandlenesteddelete-firefox-linux.json b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandlenesteddelete-firefox-linux.json new file mode 100644 index 0000000000..61108c1be4 --- /dev/null +++ b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandlenesteddelete-firefox-linux.json @@ -0,0 +1,41 @@ +{ + "type": "doc", + "content": [ + { + "type": "blockGroup", + "content": [ + { + "type": "block", + "attrs": { + "id": 0 + }, + "content": [ + { + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, + "content": [ + { + "type": "text", + "text": "This is h1" + } + ] + } + ] + }, + { + "type": "block", + "attrs": { + "id": 1 + }, + "content": [ + { + "type": "textContent" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandlenesteddelete-webkit-linux.json b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandlenesteddelete-webkit-linux.json new file mode 100644 index 0000000000..61108c1be4 --- /dev/null +++ b/tests/end-to-end/draghandle/draghandle.test.ts-snapshots/draghandlenesteddelete-webkit-linux.json @@ -0,0 +1,41 @@ +{ + "type": "doc", + "content": [ + { + "type": "blockGroup", + "content": [ + { + "type": "block", + "attrs": { + "id": 0 + }, + "content": [ + { + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, + "content": [ + { + "type": "text", + "text": "This is h1" + } + ] + } + ] + }, + { + "type": "block", + "attrs": { + "id": 1 + }, + "content": [ + { + "type": "textContent" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/end-to-end/keyboardhandlers/keyboardhandlers.test.ts b/tests/end-to-end/keyboardhandlers/keyboardhandlers.test.ts new file mode 100644 index 0000000000..36c24edc4f --- /dev/null +++ b/tests/end-to-end/keyboardhandlers/keyboardhandlers.test.ts @@ -0,0 +1,37 @@ +import { test } from "../../setup/setupScript"; +import { + BASE_URL, + H_ONE_BLOCK_SELECTOR, + H_TWO_BLOCK_SELECTOR, +} from "../../utils/const"; +import { compareDocToSnapshot, focusOnEditor } from "../../utils/editor"; +import { insertHeading } from "../../utils/copypaste"; + +test.beforeEach(async ({ page }) => { + await page.goto(BASE_URL); +}); + +test.describe.configure({ mode: "serial" }); +test.describe("Check Keyboard Handlers' Behaviour", () => { + test("Check Enter when selection is not empty", async ({ page }) => { + await focusOnEditor(page); + await insertHeading(page, 1); + await insertHeading(page, 2); + + const startElement = await page.locator(H_ONE_BLOCK_SELECTOR); + let boundingBox = await startElement.boundingBox(); + let { x, y, height } = boundingBox; + await page.mouse.move(x + 35, y + height / 2, { steps: 5 }); + await page.mouse.down(); + + const endElement = await page.locator(H_TWO_BLOCK_SELECTOR); + boundingBox = await endElement.boundingBox(); + ({ x, y, height } = boundingBox); + await page.mouse.move(x + 105, y + height / 2, { steps: 5 }); + await page.mouse.up(); + + await page.keyboard.press("Enter"); + + await compareDocToSnapshot(page, "enterSelectionNotEmpty.json"); + }); +}); diff --git a/tests/end-to-end/keyboardhandlers/keyboardhandlers.test.ts-snapshots/enterSelectionNotEmpty-json-chromium-linux.json b/tests/end-to-end/keyboardhandlers/keyboardhandlers.test.ts-snapshots/enterSelectionNotEmpty-json-chromium-linux.json new file mode 100644 index 0000000000..a02af6f293 --- /dev/null +++ b/tests/end-to-end/keyboardhandlers/keyboardhandlers.test.ts-snapshots/enterSelectionNotEmpty-json-chromium-linux.json @@ -0,0 +1,58 @@ +{ + "type": "doc", + "content": [ + { + "type": "blockGroup", + "content": [ + { + "type": "block", + "attrs": { + "id": 0 + }, + "content": [ + { + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, + "content": [ + { + "type": "text", + "text": "H" + } + ] + } + ] + }, + { + "type": "block", + "attrs": { + "id": 3 + }, + "content": [ + { + "type": "textContent", + "content": [ + { + "type": "text", + "text": "g" + } + ] + } + ] + }, + { + "type": "block", + "attrs": { + "id": 2 + }, + "content": [ + { + "type": "textContent" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/end-to-end/keyboardhandlers/keyboardhandlers.test.ts-snapshots/enterSelectionNotEmpty-json-firefox-linux.json b/tests/end-to-end/keyboardhandlers/keyboardhandlers.test.ts-snapshots/enterSelectionNotEmpty-json-firefox-linux.json new file mode 100644 index 0000000000..a02af6f293 --- /dev/null +++ b/tests/end-to-end/keyboardhandlers/keyboardhandlers.test.ts-snapshots/enterSelectionNotEmpty-json-firefox-linux.json @@ -0,0 +1,58 @@ +{ + "type": "doc", + "content": [ + { + "type": "blockGroup", + "content": [ + { + "type": "block", + "attrs": { + "id": 0 + }, + "content": [ + { + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, + "content": [ + { + "type": "text", + "text": "H" + } + ] + } + ] + }, + { + "type": "block", + "attrs": { + "id": 3 + }, + "content": [ + { + "type": "textContent", + "content": [ + { + "type": "text", + "text": "g" + } + ] + } + ] + }, + { + "type": "block", + "attrs": { + "id": 2 + }, + "content": [ + { + "type": "textContent" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/end-to-end/keyboardhandlers/keyboardhandlers.test.ts-snapshots/enterSelectionNotEmpty-json-webkit-linux.json b/tests/end-to-end/keyboardhandlers/keyboardhandlers.test.ts-snapshots/enterSelectionNotEmpty-json-webkit-linux.json new file mode 100644 index 0000000000..a02af6f293 --- /dev/null +++ b/tests/end-to-end/keyboardhandlers/keyboardhandlers.test.ts-snapshots/enterSelectionNotEmpty-json-webkit-linux.json @@ -0,0 +1,58 @@ +{ + "type": "doc", + "content": [ + { + "type": "blockGroup", + "content": [ + { + "type": "block", + "attrs": { + "id": 0 + }, + "content": [ + { + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, + "content": [ + { + "type": "text", + "text": "H" + } + ] + } + ] + }, + { + "type": "block", + "attrs": { + "id": 3 + }, + "content": [ + { + "type": "textContent", + "content": [ + { + "type": "text", + "text": "g" + } + ] + } + ] + }, + { + "type": "block", + "attrs": { + "id": 2 + }, + "content": [ + { + "type": "textContent" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/end-to-end/placeholder/placeholder.test.ts b/tests/end-to-end/placeholder/placeholder.test.ts index 2cfd420527..c326a34d65 100644 --- a/tests/end-to-end/placeholder/placeholder.test.ts +++ b/tests/end-to-end/placeholder/placeholder.test.ts @@ -1,4 +1,5 @@ -import { test, expect } from "@playwright/test"; +import { expect } from "@playwright/test"; +import { test } from "../../setup/setupScript"; import { BASE_URL } from "../../utils/const"; test.beforeEach(async ({ page }) => { diff --git a/tests/end-to-end/slashmenu/slashmenu.test.ts b/tests/end-to-end/slashmenu/slashmenu.test.ts index 78a807eb31..56ecc3e8aa 100644 --- a/tests/end-to-end/slashmenu/slashmenu.test.ts +++ b/tests/end-to-end/slashmenu/slashmenu.test.ts @@ -1,4 +1,5 @@ -import { test, expect } from "@playwright/test"; +import { expect } from "@playwright/test"; +import { test } from "../../setup/setupScript"; import { BASE_URL, BLOCK_GROUP_SELECTOR, diff --git a/tests/end-to-end/slashmenu/slashmenu.test.ts-snapshots/docStructureSnapshot-chromium-linux.json b/tests/end-to-end/slashmenu/slashmenu.test.ts-snapshots/docStructureSnapshot-chromium-linux.json index 8dffa78a81..2bb25c82e1 100644 --- a/tests/end-to-end/slashmenu/slashmenu.test.ts-snapshots/docStructureSnapshot-chromium-linux.json +++ b/tests/end-to-end/slashmenu/slashmenu.test.ts-snapshots/docStructureSnapshot-chromium-linux.json @@ -2,17 +2,19 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "1" + "id": 0 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, "content": [ { "type": "text", @@ -25,12 +27,14 @@ { "type": "block", "attrs": { - "headingType": "2" + "id": 2 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "2" + }, "content": [ { "type": "text", @@ -39,17 +43,19 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "3" + "id": 3 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "3" + }, "content": [ { "type": "text", @@ -58,15 +64,16 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", - "attrs": {}, + "attrs": { + "id": 4 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -83,13 +90,14 @@ { "type": "block", "attrs": { - "listType": "oli" + "id": 5 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "1." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -103,13 +111,14 @@ { "type": "block", "attrs": { - "listType": "oli" + "id": 6 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "2." + "listItemType": "ordered", + "listItemIndex": "2" }, "content": [ { @@ -123,12 +132,15 @@ { "type": "block", "attrs": { - "listType": "li" + "id": 7 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -144,11 +156,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/slashmenu/slashmenu.test.ts-snapshots/docStructureSnapshot-firefox-linux.json b/tests/end-to-end/slashmenu/slashmenu.test.ts-snapshots/docStructureSnapshot-firefox-linux.json index 8dffa78a81..2bb25c82e1 100644 --- a/tests/end-to-end/slashmenu/slashmenu.test.ts-snapshots/docStructureSnapshot-firefox-linux.json +++ b/tests/end-to-end/slashmenu/slashmenu.test.ts-snapshots/docStructureSnapshot-firefox-linux.json @@ -2,17 +2,19 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "1" + "id": 0 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, "content": [ { "type": "text", @@ -25,12 +27,14 @@ { "type": "block", "attrs": { - "headingType": "2" + "id": 2 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "2" + }, "content": [ { "type": "text", @@ -39,17 +43,19 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "3" + "id": 3 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "3" + }, "content": [ { "type": "text", @@ -58,15 +64,16 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", - "attrs": {}, + "attrs": { + "id": 4 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -83,13 +90,14 @@ { "type": "block", "attrs": { - "listType": "oli" + "id": 5 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "1." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -103,13 +111,14 @@ { "type": "block", "attrs": { - "listType": "oli" + "id": 6 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "2." + "listItemType": "ordered", + "listItemIndex": "2" }, "content": [ { @@ -123,12 +132,15 @@ { "type": "block", "attrs": { - "listType": "li" + "id": 7 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -144,11 +156,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/slashmenu/slashmenu.test.ts-snapshots/docStructureSnapshot-webkit-linux.json b/tests/end-to-end/slashmenu/slashmenu.test.ts-snapshots/docStructureSnapshot-webkit-linux.json index 8dffa78a81..2bb25c82e1 100644 --- a/tests/end-to-end/slashmenu/slashmenu.test.ts-snapshots/docStructureSnapshot-webkit-linux.json +++ b/tests/end-to-end/slashmenu/slashmenu.test.ts-snapshots/docStructureSnapshot-webkit-linux.json @@ -2,17 +2,19 @@ "type": "doc", "content": [ { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "1" + "id": 0 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "1" + }, "content": [ { "type": "text", @@ -25,12 +27,14 @@ { "type": "block", "attrs": { - "headingType": "2" + "id": 2 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "2" + }, "content": [ { "type": "text", @@ -39,17 +43,19 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", "attrs": { - "headingType": "3" + "id": 3 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "headingContent", + "attrs": { + "headingLevel": "3" + }, "content": [ { "type": "text", @@ -58,15 +64,16 @@ ] }, { - "type": "blockgroup", + "type": "blockGroup", "content": [ { "type": "block", - "attrs": {}, + "attrs": { + "id": 4 + }, "content": [ { - "type": "content", - "attrs": {}, + "type": "textContent", "content": [ { "type": "text", @@ -83,13 +90,14 @@ { "type": "block", "attrs": { - "listType": "oli" + "id": 5 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "1." + "listItemType": "ordered", + "listItemIndex": "1" }, "content": [ { @@ -103,13 +111,14 @@ { "type": "block", "attrs": { - "listType": "oli" + "id": 6 }, "content": [ { - "type": "content", + "type": "listItemContent", "attrs": { - "position": "2." + "listItemType": "ordered", + "listItemIndex": "2" }, "content": [ { @@ -123,12 +132,15 @@ { "type": "block", "attrs": { - "listType": "li" + "id": 7 }, "content": [ { - "type": "content", - "attrs": {}, + "type": "listItemContent", + "attrs": { + "listItemType": "unordered", + "listItemIndex": null + }, "content": [ { "type": "text", @@ -144,11 +156,12 @@ }, { "type": "block", - "attrs": {}, + "attrs": { + "id": 1 + }, "content": [ { - "type": "content", - "attrs": {} + "type": "textContent" } ] } diff --git a/tests/end-to-end/slashmenu/slashmenu.test.ts-snapshots/slash-menu-end-product-chromium-linux.png b/tests/end-to-end/slashmenu/slashmenu.test.ts-snapshots/slash-menu-end-product-chromium-linux.png index 5009466f49..82122e69cb 100644 Binary files a/tests/end-to-end/slashmenu/slashmenu.test.ts-snapshots/slash-menu-end-product-chromium-linux.png and b/tests/end-to-end/slashmenu/slashmenu.test.ts-snapshots/slash-menu-end-product-chromium-linux.png differ diff --git a/tests/setup/setupScript.ts b/tests/setup/setupScript.ts new file mode 100644 index 0000000000..3fa0a38de1 --- /dev/null +++ b/tests/setup/setupScript.ts @@ -0,0 +1,34 @@ +import { + BrowserContext, + BrowserContextOptions, + test as base, + expect, +} from "@playwright/test"; + +export type TestOptions = { + mockID: number | undefined; +}; + +export const test = base.extend({ + browser: async ({ browser }, use) => { + const oldFunc = browser.newContext; + browser.newContext = async (options?: BrowserContextOptions) => { + const ret: BrowserContext = await oldFunc.call(browser, options); + + await ret.addInitScript({ + content: `window.__TEST_OPTIONS = window.__TEST_OPTIONS || {}`, + }); + + return ret; + }; + await use(browser); + }, + context: async ({ context }, use) => { + await context.addInitScript({ + content: `window.__TEST_OPTIONS = window.__TEST_OPTIONS || {}`, + }); + await use(context); + }, +}); + +export { expect } from "@playwright/test"; diff --git a/tests/utils/const.ts b/tests/utils/const.ts index 233f057690..2a8e271e56 100644 --- a/tests/utils/const.ts +++ b/tests/utils/const.ts @@ -4,16 +4,20 @@ export const BASE_URL = !process.env.RUN_IN_DOCKER : `http://host.docker.internal:${PORT}`; export const EDITOR_SELECTOR = `[data-test="editor"]`; -export const SLASH_MENU_SELECTOR = `.mantine-SuggestionList-root`; -export const H_ONE_BLOCK_SELECTOR = `[data-heading-type="1"] > [data-heading-type="1"]`; -export const H_TWO_BLOCK_SELECTOR = `[data-heading-type="2"] > [data-heading-type="2"]`; -export const H_THREE_BLOCK_SELECTOR = `[data-heading-type="3"] > [data-heading-type="3"]`; -export const NUMBERED_LIST_SELECTOR = `[data-list-type="oli"] > [data-list-type="oli"]`; -export const BULLET_LIST_SELECTOR = `[data-list-type="li"] > [data-list-type="li"]`; export const BLOCK_GROUP_SELECTOR = `[data-node-type="block-group"]`; export const BLOCK_SELECTOR = `[data-node-type="block"]`; -export const BLOCK_CONTENT_SELECTOR = `[data-node-type="block-content"]`; -export const DRAG_HANDLE = `[data-test="dragHandle"]`; -export const DRAG_HANDLE_ADD = `[data-test="dragHandleAdd"]`; + +export const H_ONE_BLOCK_SELECTOR = `[data-content-type=headingContent][data-heading-level="1"]`; +export const H_TWO_BLOCK_SELECTOR = `[data-content-type=headingContent][data-heading-level="2"]`; +export const H_THREE_BLOCK_SELECTOR = `[data-content-type=headingContent][data-heading-level="3"]`; +export const NUMBERED_LIST_SELECTOR = `[data-content-type="listItemContent"][data-list-item-type="ordered"]`; +export const BULLET_LIST_SELECTOR = `[data-content-type="listItemContent"][data-list-item-type="unordered"]`; +export const PARAGRAPH_SELECTOR = `[data-content-type=textContent]`; + +export const DRAG_HANDLE_SELECTOR = `[data-test="dragHandle"]`; +export const DRAG_HANDLE_ADD_SELECTOR = `[data-test="dragHandleAdd"]`; + export const DRAG_HANDLE_MENU_SELECTOR = `.mantine-DragHandleMenu-root`; +export const SLASH_MENU_SELECTOR = `.mantine-SuggestionList-root`; + export const TYPE_DELAY = 10; diff --git a/tests/utils/draghandle.ts b/tests/utils/draghandle.ts index aa4b305d6d..a576070b85 100644 --- a/tests/utils/draghandle.ts +++ b/tests/utils/draghandle.ts @@ -1,9 +1,9 @@ import { Page } from "@playwright/test"; -import { DRAG_HANDLE, DRAG_HANDLE_ADD } from "./const"; +import { DRAG_HANDLE_SELECTOR, DRAG_HANDLE_ADD_SELECTOR } from "./const"; import { moveMouseOverElement } from "./mouse"; export async function addBlockFromDragHandle(page: Page, blockQuery: string) { - await page.click(DRAG_HANDLE_ADD); + await page.click(DRAG_HANDLE_ADD_SELECTOR); await page.keyboard.type(blockQuery, { delay: 10 }); await page.keyboard.press("Enter", { delay: 10 }); } @@ -21,7 +21,7 @@ export async function hoverAndAddBlockFromDragHandle( export async function getDragHandleYCoord(page: Page, selector: string) { const element = await page.locator(selector); await moveMouseOverElement(page, element); - await page.waitForSelector(DRAG_HANDLE); - const boundingBox = await page.locator(DRAG_HANDLE).boundingBox(); + await page.waitForSelector(DRAG_HANDLE_SELECTOR); + const boundingBox = await page.locator(DRAG_HANDLE_SELECTOR).boundingBox(); return boundingBox.y; } diff --git a/tests/utils/editor.ts b/tests/utils/editor.ts index a59f2169df..4885df016a 100644 --- a/tests/utils/editor.ts +++ b/tests/utils/editor.ts @@ -23,7 +23,7 @@ export async function getDoc(page: Page) { } export function removeAttFromDoc(doc: unknown, att: string) { - if (typeof doc !== "object") return; + if (typeof doc !== "object" || doc === null) return; if (Object.keys(doc).includes(att)) { delete doc[att]; } @@ -33,10 +33,6 @@ export function removeAttFromDoc(doc: unknown, att: string) { export async function compareDocToSnapshot(page: Page, name: string) { // Remove id from docs - const doc = JSON.stringify( - removeAttFromDoc(await getDoc(page), "id"), - null, - 2 - ); + const doc = JSON.stringify(await getDoc(page), null, 2); expect(doc).toMatchSnapshot(`${name}.json`); } diff --git a/tests/utils/mouse.ts b/tests/utils/mouse.ts index 63a981cc02..ba324446bc 100644 --- a/tests/utils/mouse.ts +++ b/tests/utils/mouse.ts @@ -1,5 +1,5 @@ import { Locator, Page } from "@playwright/test"; -import { DRAG_HANDLE } from "./const"; +import { DRAG_HANDLE_SELECTOR } from "./const"; async function getElementLeftCoords(page: Page, element: Locator) { const boundingBox = await element.boundingBox(); @@ -29,8 +29,8 @@ export async function dragAndDropBlock( ) { await moveMouseOverElement(page, dragTarget); - await page.waitForSelector(DRAG_HANDLE); - const dragHandle = await page.locator(DRAG_HANDLE); + await page.waitForSelector(DRAG_HANDLE_SELECTOR); + const dragHandle = await page.locator(DRAG_HANDLE_SELECTOR); await moveMouseOverElement(page, dragHandle); await page.mouse.down();