diff --git a/packages/richtext-lexical/src/utilities/jsx/lexicalMarkdownCopy.ts b/packages/richtext-lexical/src/utilities/jsx/lexicalMarkdownCopy.ts index 682abcb45d..3313a74233 100644 --- a/packages/richtext-lexical/src/utilities/jsx/lexicalMarkdownCopy.ts +++ b/packages/richtext-lexical/src/utilities/jsx/lexicalMarkdownCopy.ts @@ -18,8 +18,10 @@ const UNORDERED_LIST_REGEX = /^(\s*)[-*+]\s/ const CHECK_LIST_REGEX = /^(\s*)(?:-\s)?\s?(\[(\s|x)?\])\s/i const HEADING_REGEX = /^(#{1,6})\s/ const QUOTE_REGEX = /^>\s/ -const CODE_START_REGEX = /^[ \t]*```(\w+)?/ -const CODE_END_REGEX = /[ \t]*```$/ +// Match start of ``` or escaped \`\`\` code blocks +const CODE_START_REGEX = /^[ \t]*(\\`\\`\\`|```)(\w+)?/ +// Match end of ``` or escaped \`\`\` code blocks +const CODE_END_REGEX = /[ \t]*(\\`\\`\\`|```)$/ const CODE_SINGLE_LINE_REGEX = /^[ \t]*```[^`]+(?:(?:`{1,2}|`{4,})[^`]+)*```(?:[^`]|$)/ const TABLE_ROW_REG_EXP = /^\|(.+)\|\s?$/ const TABLE_ROW_DIVIDER_REG_EXP = /^(\| ?:?-*:? ?)+\|\s?$/ @@ -41,15 +43,15 @@ export function normalizeMarkdown(input: string, shouldMergeAdjacentLines = fals continue } - if ( - (CODE_START_REGEX.test(line) && !inCodeBlock) || - (CODE_END_REGEX.test(line) && inCodeBlock) - ) { - inCodeBlock = !inCodeBlock + // Toggle inCodeBlock state when encountering start or end of a code block + if (CODE_START_REGEX.test(line)) { + inCodeBlock = true + sanitizedLines.push(line) + continue } - // Detect the start or end of a code block - if (CODE_START_REGEX.test(line) || CODE_END_REGEX.test(line)) { + if (CODE_END_REGEX.test(line)) { + inCodeBlock = false sanitizedLines.push(line) continue } diff --git a/test/lexical-mdx/collections/Posts/index.ts b/test/lexical-mdx/collections/Posts/index.ts index 895cc38906..5aa80dad98 100644 --- a/test/lexical-mdx/collections/Posts/index.ts +++ b/test/lexical-mdx/collections/Posts/index.ts @@ -15,6 +15,7 @@ import { InlineCodeBlock } from '../../mdx/jsxBlocks/inlineCode.js' import { PackageInstallOptions } from '../../mdx/jsxBlocks/packageInstallOptions.js' import { TextContainerBlock } from '../../mdx/jsxBlocks/TextContainer.js' import { TextContainerNoTrimBlock } from '../../mdx/jsxBlocks/TextContainerNoTrim.js' +import { RestExamplesBlock } from '../../mdx/jsxBlocks/restExamples/index.js' export const postsSlug = 'posts' @@ -72,6 +73,7 @@ export const PostsCollection: CollectionConfig = { PackageInstallOptions, TextContainerNoTrimBlock, TextContainerBlock, + RestExamplesBlock, ], inlineBlocks: [InlineCodeBlock], }), @@ -94,6 +96,7 @@ export const PostsCollection: CollectionConfig = { PackageInstallOptions, TextContainerNoTrimBlock, TextContainerBlock, + RestExamplesBlock, ], inlineBlocks: [InlineCodeBlock], }), diff --git a/test/lexical-mdx/int.spec.ts b/test/lexical-mdx/int.spec.ts index f8679aae1f..ea591dc5e3 100644 --- a/test/lexical-mdx/int.spec.ts +++ b/test/lexical-mdx/int.spec.ts @@ -14,8 +14,11 @@ import { fileURLToPath } from 'url' import { initPayloadInt } from '../helpers/initPayloadInt.js' import { postsSlug } from './collections/Posts/index.js' import { editorJSONToMDX, mdxToEditorJSON } from './mdx/hooks.js' -import { tableJson } from './tableJson.js' -import { textToRichText } from './textToRichText.js' +import { restExamplesTest1 } from './tests/restExamples.test.js' +import { restExamplesTest2 } from './tests/restExamples2.test.js' + +import { defaultTests } from './tests/default.test.js' +import { writeFileSync } from 'fs' let config: SanitizedConfig let editorConfig: SanitizedServerEditorConfig @@ -23,7 +26,7 @@ let editorConfig: SanitizedServerEditorConfig const filename = fileURLToPath(import.meta.url) const dirname = path.dirname(filename) -type Tests = Array<{ +export type Test = { blockNode?: { fields: Omit } & Omit< @@ -36,7 +39,10 @@ type Tests = Array<{ input: string inputAfterConvertFromEditorJSON?: string rootChildren?: any[] -}> + convertToEditorJSON?: boolean + convertFromEditorJSON?: boolean +} +type Tests = Array describe('Lexical MDX', () => { // --__--__--__--__--__--__--__--__--__ @@ -55,1376 +61,11 @@ describe('Lexical MDX', () => { editorConfig = (richTextField.editor as LexicalRichTextAdapter).editorConfig }) - const INPUT_AND_OUTPUTBase: Tests = [ - { - inputAfterConvertFromEditorJSON: ``, - input: ` - - `, - blockNode: { - fields: { - blockType: 'PackageInstallOptions', - packageId: '444', - update: true, - uniqueId: 'xxx', - }, - }, - }, - { - input: ` - - ignored - -`, - inputAfterConvertFromEditorJSON: ``, - blockNode: { - fields: { - blockType: 'PackageInstallOptions', - packageId: '444', - }, - }, - }, - { - input: ` - - ignored - -`, - inputAfterConvertFromEditorJSON: ``, - blockNode: { - fields: { - blockType: 'PackageInstallOptions', - packageId: '444', - update: true, - }, - }, - }, - { - input: ` - - ignored - -`, - inputAfterConvertFromEditorJSON: ``, - blockNode: { - fields: { - blockType: 'PackageInstallOptions', - packageId: '444', - update: true, - }, - }, - }, - { - inputAfterConvertFromEditorJSON: ``, - input: ` - - ignored - -`, - blockNode: { - fields: { - blockType: 'PackageInstallOptions', - packageId: '444', - update: true, - }, - }, - }, - { - inputAfterConvertFromEditorJSON: ``, // Not test - test is not part of the block - input: ` - - ignored - -`, - blockNode: { - fields: { - blockType: 'PackageInstallOptions', - packageId: '444', - update: true, - someNestedObject: { test: 'hello' }, - }, - }, - }, - { - inputAfterConvertFromEditorJSON: ``, + const INPUT_AND_OUTPUTBase: Tests = [...defaultTests, restExamplesTest1, restExamplesTest2] - input: ` - - ignored - - ignoredi - - -`, - blockNode: { - fields: { - blockType: 'PackageInstallOptions', - packageId: '444', - update: true, - }, - }, - }, - { - inputAfterConvertFromEditorJSON: ``, - - input: ` - - ignored - - ignoredi - - - - -`, - blockNode: { - fields: { - blockType: 'PackageInstallOptions', - packageId: '444', - update: true, - }, - }, - }, - // TODO: Write test for this: - /* - - not ignored - - not ignored - - not ignored - - */ - { - input: ` -\`\`\`ts -hello\`\`\` -`, - inputAfterConvertFromEditorJSON: ` -\`\`\`ts -hello -\`\`\` -`, - blockNode: { - fields: { - blockType: 'Code', - code: 'hello', - language: 'ts', - }, - }, - }, - { - input: ` -\`\`\`ts - hello\`\`\` -`, - inputAfterConvertFromEditorJSON: ` -\`\`\`ts - hello -\`\`\` -`, - blockNode: { - fields: { - blockType: 'Code', - code: ' hello', - language: 'ts', - }, - }, - }, - { - input: ` -\`\`\`ts x\n hello\`\`\` -`, - inputAfterConvertFromEditorJSON: ` -\`\`\`ts - x - hello -\`\`\` -`, - blockNode: { - fields: { - blockType: 'Code', - code: ' x\n hello', - language: 'ts', - }, - }, - }, - { - input: ` -\`\`\`ts hello\`\`\` -`, - blockNode: { - fields: { - blockType: 'Code', - code: 'ts hello', - language: '', - }, - }, - }, - { - input: ` -\`\`\`hello\`\`\` -`, - blockNode: { - fields: { - blockType: 'Code', - code: 'hello', - language: '', - }, - }, - }, - { - input: ` -\`\`\`ts -hello -\`\`\` -`, - blockNode: { - fields: { - blockType: 'Code', - code: 'hello', - language: 'ts', - }, - }, - }, - { - input: ` -\`\`\`ts hello -there1 -\`\`\` -`, - inputAfterConvertFromEditorJSON: ` -\`\`\`ts - hello -there1 -\`\`\` -`, - blockNode: { - fields: { - blockType: 'Code', - code: ' hello\nthere1', - language: 'ts', - }, - }, - }, - { - input: ` -\`\`\`ts hello -there2 -!!\`\`\` -`, - inputAfterConvertFromEditorJSON: ` -\`\`\`ts - hello -there2 -!! -\`\`\` -`, - blockNode: { - fields: { - blockType: 'Code', - code: ' hello\nthere2\n!!', - language: 'ts', - }, - }, - }, - { - input: ` -\`\`\`ts -Hello -there3\`\`\` -`, - inputAfterConvertFromEditorJSON: ` -\`\`\`ts -Hello -there3 -\`\`\` -`, - blockNode: { - fields: { - blockType: 'Code', - code: 'Hello\nthere3', - language: 'ts', - }, - }, - }, - { - input: ` -\`\`\`ts -Hello -\`\`\`ts -nested -\`\`\`! -there4\`\`\` -`, - inputAfterConvertFromEditorJSON: ` -\`\`\`ts -Hello -\`\`\`ts -nested -\`\`\`! -there4 -\`\`\` -`, - blockNode: { - fields: { - blockType: 'Code', - code: 'Hello\n```ts\nnested\n```!\nthere4', - language: 'ts', - }, - }, - }, - { - ignoreSpacesAndNewlines: true, - input: ` -| Option | Default route | Description | -| ----------------- | ----------------------- | ----------------------------------------------- | -| \`account\` | | The user's account page. | -| \`createFirstUser\` | \`/create-first-user\` | The page to create the first user. | -`, - inputAfterConvertFromEditorJSON: ` -| Option | Default route | Description | -|---|---|---| -| \`account\` | | The user's account page. | -| \`createFirstUser\` | \`/create-first-user\` | The page to create the first user. | -`, - rootChildren: [tableJson], - }, - { - input: ` - - children text - -`, - blockNode: { - fields: { - blockType: 'Banner', - content: textToRichText('children text'), - }, - }, - }, - { - input: `\`inline code\``, - rootChildren: [ - { - children: [ - { - detail: 0, - format: 16, // Format 16 => inline code - mode: 'normal', - style: '', - text: 'inline code', - type: 'text', - version: 1, - }, - ], - format: '', - indent: 0, - type: 'paragraph', - version: 1, - textFormat: 0, - textStyle: '', - }, - ], - }, - { - // This test ensures that the JSX within the code block is does not disrupt the main JSX parsing - input: ` - - \`https://.payloadcms.com/page\` - -`, - blockNode: { - fields: { - blockType: 'Banner', - content: { - root: { - children: [ - { - children: [ - { - detail: 0, - format: 16, // Format 16 => inline code - mode: 'normal', - style: '', - text: 'https://.payloadcms.com/page', - type: 'text', - version: 1, - }, - ], - format: '', - indent: 0, - type: 'paragraph', - version: 1, - textFormat: 0, - textStyle: '', - }, - ], - format: '', - indent: 0, - type: 'root', - version: 1, - }, - }, - }, - }, - }, - { - input: 'Hello inline code test.', - rootChildren: [ - { - children: [ - { - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Hello ', - type: 'text', - version: 1, - }, - { - type: 'inlineBlock', - - fields: { - code: 'inline code', - blockType: 'InlineCode', - }, - version: 1, - }, - - { - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: ' test.', - type: 'text', - version: 1, - }, - ], - format: '', - indent: 0, - type: 'paragraph', - version: 1, - textFormat: 0, - textStyle: '', - }, - ], - }, - { - input: ` - - Some text 1 code 1 some - - text 2 code 2 some text - - 3 code 3 some text 4code 4 - -`, - description: 'Banner with inline codes, each line a paragraph', - blockNode: { - fields: { - blockType: 'Banner', - content: { - root: { - children: [ - { - children: [ - { - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Some text 1 ', - type: 'text', - version: 1, - }, - - { - type: 'inlineBlock', - - fields: { - code: 'code 1', - blockType: 'InlineCode', - }, - version: 1, - }, - - { - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: ' some', - type: 'text', - version: 1, - }, - ], - format: '', - indent: 0, - type: 'paragraph', - version: 1, - textFormat: 0, - textStyle: '', - }, - - { - children: [ - { - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'text 2 ', - type: 'text', - version: 1, - }, - - { - type: 'inlineBlock', - - fields: { - code: 'code 2', - blockType: 'InlineCode', - }, - version: 1, - }, - - { - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: ' some text', - type: 'text', - version: 1, - }, - ], - format: '', - indent: 0, - type: 'paragraph', - version: 1, - textFormat: 0, - textStyle: '', - }, - - { - children: [ - { - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: '3 ', - type: 'text', - version: 1, - }, - - { - type: 'inlineBlock', - - fields: { - code: 'code 3', - blockType: 'InlineCode', - }, - version: 1, - }, - { - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: ' some text 4', - type: 'text', - version: 1, - }, - { - type: 'inlineBlock', - fields: { - code: 'code 4', - blockType: 'InlineCode', - }, - version: 1, - }, - ], - format: '', - indent: 0, - type: 'paragraph', - version: 1, - textFormat: 0, - textStyle: '', - }, - ], - format: '', - indent: 0, - type: 'root', - version: 1, - }, - }, - }, - }, - }, - { - input: ` - - Some text 1 code 1 some - - text 2 code 2 some text - - 3 code 3 some text 4code 4 - -`, - description: 'Banner with inline codes, three paragraphs', - - blockNode: { - fields: { - blockType: 'Banner', - content: { - root: { - children: [ - { - children: [ - { - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Some text 1 ', - type: 'text', - version: 1, - }, - { - type: 'inlineBlock', - fields: { - code: 'code 1', - blockType: 'InlineCode', - }, - version: 1, - }, - { - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: ' some', - type: 'text', - version: 1, - }, - ], - format: '', - indent: 0, - type: 'paragraph', - version: 1, - textFormat: 0, - textStyle: '', - }, - { - children: [ - { - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'text 2 ', - type: 'text', - version: 1, - }, - { - type: 'inlineBlock', - fields: { - code: 'code 2', - blockType: 'InlineCode', - }, - version: 1, - }, - { - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: ' some text', - type: 'text', - version: 1, - }, - ], - format: '', - indent: 0, - type: 'paragraph', - version: 1, - textFormat: 0, - textStyle: '', - }, - { - children: [ - { - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: '3 ', - type: 'text', - version: 1, - }, - { - type: 'inlineBlock', - fields: { - code: 'code 3', - blockType: 'InlineCode', - }, - version: 1, - }, - { - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: ' some text 4', - type: 'text', - version: 1, - }, - { - type: 'inlineBlock', - fields: { - code: 'code 4', - blockType: 'InlineCode', - }, - version: 1, - }, - ], - format: '', - indent: 0, - type: 'paragraph', - version: 1, - textFormat: 0, - textStyle: '', - }, - ], - format: '', - indent: 0, - type: 'root', - version: 1, - }, - }, - }, - }, - }, - { - input: ` -Text before banner - - - test - -`, - rootChildren: [ - { - children: [ - { - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Text before banner', - type: 'text', - version: 1, - }, - ], - format: '', - indent: 0, - type: 'paragraph', - version: 1, - textFormat: 0, - textStyle: '', - }, - { - type: 'block', - format: '', - fields: { - blockType: 'Banner', - content: { - root: { - children: [ - { - children: [ - { - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'test', - type: 'text', - version: 1, - }, - ], - format: '', - indent: 0, - textFormat: 0, - textStyle: '', - type: 'paragraph', - version: 1, - }, - ], - format: '', - indent: 0, - type: 'root', - version: 1, - }, - }, - }, - version: 2, - }, - ], - }, - { - description: 'TextContainerNoTrim with nested, no-leftpad content', - input: ` - -no indent - - indent 2 - - indent 4 - -no indent - -`, - blockNode: { - fields: { - blockType: 'TextContainerNoTrim', - text: `no indent - - indent 2 - - indent 4 - -no indent`, - }, - }, - }, - { - description: 'TextContainer with nested, no-leftpad content', - - input: ` - -no indent - - indent 2 - - indent 4 - -no indent - -`, - inputAfterConvertFromEditorJSON: ` - - no indent - - indent 2 - - indent 4 - - no indent - -`, - blockNode: { - fields: { - blockType: 'TextContainer', - text: `no indent - - indent 2 - - indent 4 - -no indent`, - }, - }, - }, - { - description: 'TextContainerNoTrim with nested, leftpad content', - input: ` - - indent 2 - - indent 4 - - indent 6 - - indent 2 - -`, - blockNode: { - fields: { - blockType: 'TextContainerNoTrim', - text: ` indent 2 - - indent 4 - - indent 6 - - indent 2`, - }, - }, - }, - { - description: 'TextContainer with nested, leftpad content', - input: ` - - indent 2 - - indent 4 - - indent 6 - - indent 2 - -`, - blockNode: { - fields: { - blockType: 'TextContainer', - text: `indent 2 - - indent 4 - - indent 6 - -indent 2`, - }, - }, - }, - { - input: ` -Some text 1 -code 2 -`, - description: 'InlineCode after text, split by linebreak', - rootChildren: [ - { - children: [ - { - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Some text 1', - type: 'text', - version: 1, - }, - { - type: 'linebreak', - version: 1, - }, - { - type: 'inlineBlock', - fields: { - code: 'code 2', - blockType: 'InlineCode', - }, - version: 1, - }, - ], - format: '', - indent: 0, - type: 'paragraph', - version: 1, - textFormat: 0, - textStyle: '', - }, - ], - }, - { - description: 'Code block with nested within Banner', - input: ` - - \`\`\`ts - indent 1; - \`\`\` - -`, - blockNode: { - fields: { - blockType: 'Banner', - content: { - root: { - children: [ - { - fields: { - blockType: 'Code', - code: ' indent 1;', - language: 'ts', - }, - format: '', - type: 'block', - version: 2, - }, - ], - format: '', - indent: 0, - type: 'root', - version: 1, - }, - }, - }, - }, - }, - { - description: 'Code block with nested within Banner 2', - input: ` - -\`\`\`ts - indent 1; -\`\`\` - -`, - inputAfterConvertFromEditorJSON: ` - - \`\`\`ts - indent 1; - \`\`\` - -`, - blockNode: { - fields: { - blockType: 'Banner', - content: { - root: { - children: [ - { - fields: { - blockType: 'Code', - code: ' indent 1;', - language: 'ts', - }, - format: '', - type: 'block', - version: 2, - }, - ], - format: '', - indent: 0, - type: 'root', - version: 1, - }, - }, - }, - }, - }, - { - description: 'One-line Banner', - input: ` -Hi -`, - inputAfterConvertFromEditorJSON: ` - - Hi - -`, - blockNode: { - fields: { - blockType: 'Banner', - content: { - root: { - children: [ - { - children: [ - { - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Hi', - type: 'text', - version: 1, - }, - ], - format: '', - indent: 0, - type: 'paragraph', - version: 1, - textFormat: 0, - textStyle: '', - }, - ], - format: '', - indent: 0, - type: 'root', - version: 1, - }, - }, - }, - }, - }, - { - description: 'Code block with nested within 2 Banners', - input: ` - - -\`\`\`ts - indent 1; -\`\`\` - - -`, - inputAfterConvertFromEditorJSON: ` - - - \`\`\`ts - indent 1; - \`\`\` - - -`, - blockNode: { - fields: { - blockType: 'Banner', - content: { - root: { - children: [ - { - type: 'block', - format: '', - fields: { - blockType: 'Banner', - content: { - root: { - children: [ - { - fields: { - blockType: 'Code', - code: ' indent 1;', - language: 'ts', - }, - format: '', - type: 'block', - version: 2, - }, - ], - format: '', - indent: 0, - type: 'root', - version: 1, - }, - }, - }, - version: 2, - }, - ], - format: '', - indent: 0, - type: 'root', - version: 1, - }, - }, - }, - }, - }, - { - inputAfterConvertFromEditorJSON: ` - - Some line [Start of link line2](/some/link) - -`, - input: ` - -Some line [Start of link - line2](/some/link) - -`, - blockNode: { - fields: { - blockType: 'Banner', - content: { - root: { - children: [ - { - children: [ - { - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Some line ', - type: 'text', - version: 1, - }, - { - children: [ - { - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Start of link line2', - type: 'text', - version: 1, - }, - ], - fields: { - linkType: 'custom', - newTab: false, - url: '/some/link', - }, - format: '', - indent: 0, - type: 'link', - version: 3, - }, - ], - direction: null, - format: '', - indent: 0, - textFormat: 0, - textStyle: '', - type: 'paragraph', - version: 1, - }, - ], - direction: null, - format: '', - indent: 0, - type: 'root', - version: 1, - }, - }, - }, - }, - }, - { - inputAfterConvertFromEditorJSON: ` - - Text text [ Link ](/some/link) . - -`, - input: ` - - Text text [ Link - ](/some/link) . - -`, - blockNode: { - fields: { - blockType: 'Banner', - content: { - root: { - children: [ - { - children: [ - { - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: 'Text text ', - type: 'text', - version: 1, - }, - { - children: [ - { - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: ' Link ', - type: 'text', - version: 1, - }, - ], - fields: { - linkType: 'custom', - newTab: false, - url: '/some/link', - }, - format: '', - indent: 0, - type: 'link', - version: 3, - }, - { - detail: 0, - format: 0, - mode: 'normal', - style: '', - text: ' .', - type: 'text', - version: 1, - }, - ], - direction: null, - format: '', - indent: 0, - textFormat: 0, - textStyle: '', - type: 'paragraph', - version: 1, - }, - ], - direction: null, - format: '', - indent: 0, - type: 'root', - version: 1, - }, - }, - }, - }, - }, - { - input: ` - - -`, - inputAfterConvertFromEditorJSON: ``, - blockNode: { - fields: { - blockType: 'PackageInstallOptions', - packageId: 'Line', - someObject: { test: 'Line 1\n\nLine 2' }, - update: true, - }, - }, - }, - ] - - const INPUT_AND_OUTPUT: Tests = INPUT_AND_OUTPUTBase //.filter((test) => test.debugFlag) + const INPUT_AND_OUTPUT: Tests = INPUT_AND_OUTPUTBase.find((test) => test.debugFlag) + ? [INPUT_AND_OUTPUTBase.find((test) => test.debugFlag)] + : INPUT_AND_OUTPUTBase for (const { input, @@ -1432,7 +73,10 @@ Line 2\`}} blockNode, ignoreSpacesAndNewlines, rootChildren, + debugFlag, description, + convertFromEditorJSON, + convertToEditorJSON, } of INPUT_AND_OUTPUT) { let sanitizedInput = input // Remove beginning and end newline of input if exists (since the input is a template string) @@ -1456,71 +100,79 @@ Line 2\`}} } } - it(`can convert to editor JSON: ${description ?? sanitizedInput}"`, () => { - const result = mdxToEditorJSON({ - mdxWithFrontmatter: sanitizedInput, - editorConfig, - }) + if (convertToEditorJSON !== false) { + it(`can convert to editor JSON: ${description ?? sanitizedInput}"`, () => { + const result = mdxToEditorJSON({ + mdxWithFrontmatter: sanitizedInput, + editorConfig, + }) - if (blockNode) { - const receivedBlockNode: SerializedBlockNode = result.editorState.root - .children[0] as unknown as SerializedBlockNode - expect(receivedBlockNode).not.toBeNull() - - // By doing it like this, the blockNode defined in the test does not need to have all the top-level properties. We only wanna compare keys that are defined in the test - const receivedBlockNodeToTest = {} - for (const key in blockNode) { - receivedBlockNodeToTest[key] = receivedBlockNode[key] + if (debugFlag) { + writeFileSync(path.resolve(dirname, 'result.json'), JSON.stringify(result, null, 2)) } - removeUndefinedAndIDRecursively(receivedBlockNodeToTest) - removeUndefinedAndIDRecursively(blockNode) + if (blockNode) { + const receivedBlockNode: SerializedBlockNode = result.editorState.root + .children[0] as unknown as SerializedBlockNode + expect(receivedBlockNode).not.toBeNull() - expect(receivedBlockNodeToTest).toStrictEqual(blockNode) - } else if (rootChildren) { - const receivedRootChildren = result.editorState.root.children - removeUndefinedAndIDRecursively(receivedRootChildren) - removeUndefinedAndIDRecursively(rootChildren) + // By doing it like this, the blockNode defined in the test does not need to have all the top-level properties. We only wanna compare keys that are defined in the test + const receivedBlockNodeToTest = {} + for (const key in blockNode) { + receivedBlockNodeToTest[key] = receivedBlockNode[key] + } - expect(receivedRootChildren).toStrictEqual(rootChildren) - } else { - throw new Error('Test not configured properly') - } - }) + removeUndefinedAndIDRecursively(receivedBlockNodeToTest) + removeUndefinedAndIDRecursively(blockNode) - it(`can convert from editor JSON: ${description ?? sanitizedInput}"`, () => { - const editorState = { - root: { - children: blockNode - ? [ - { - format: '', - type: 'block', - version: 2, - ...blockNode, - }, - ] - : rootChildren, - format: '', - indent: 0, - type: 'root', - version: 1, - }, - } - const result = editorJSONToMDX({ - editorConfig, - editorState, + expect(receivedBlockNodeToTest).toStrictEqual(blockNode) + } else if (rootChildren) { + const receivedRootChildren = result.editorState.root.children + removeUndefinedAndIDRecursively(receivedRootChildren) + removeUndefinedAndIDRecursively(rootChildren) + + expect(receivedRootChildren).toStrictEqual(rootChildren) + } else { + throw new Error('Test not configured properly') + } }) - // Remove all spaces and newlines - const resultNoSpace = ignoreSpacesAndNewlines ? result.replace(/\s/g, '') : result - const inputNoSpace = ignoreSpacesAndNewlines - ? (sanitizedInputAfterConvertFromEditorJSON ?? sanitizedInput).replace(/\s/g, '') - : (sanitizedInputAfterConvertFromEditorJSON ?? sanitizedInput) + } - console.log('resultNoSpace', resultNoSpace) - console.log('inputNoSpace', inputNoSpace) - expect(resultNoSpace).toBe(inputNoSpace) - }) + if (convertFromEditorJSON !== false) { + it(`can convert from editor JSON: ${description ?? sanitizedInput}"`, () => { + const editorState = { + root: { + children: blockNode + ? [ + { + format: '', + type: 'block', + version: 2, + ...blockNode, + }, + ] + : rootChildren, + format: '', + indent: 0, + type: 'root', + version: 1, + }, + } + const result = editorJSONToMDX({ + editorConfig, + editorState, + }) + // Remove all spaces and newlines + const resultNoSpace = ignoreSpacesAndNewlines ? result.replace(/\s/g, '') : result + const inputNoSpace = ignoreSpacesAndNewlines + ? (sanitizedInputAfterConvertFromEditorJSON ?? sanitizedInput).replace(/\s/g, '') + : (sanitizedInputAfterConvertFromEditorJSON ?? sanitizedInput) + + console.log('resultNoSpace', resultNoSpace) + console.log('inputNoSpace', inputNoSpace) + expect(resultNoSpace).toBe(inputNoSpace) + }) + } } }) diff --git a/test/lexical-mdx/mdx/jsxBlocks/restExamples/index.ts b/test/lexical-mdx/mdx/jsxBlocks/restExamples/index.ts new file mode 100644 index 0000000000..9d91835cb2 --- /dev/null +++ b/test/lexical-mdx/mdx/jsxBlocks/restExamples/index.ts @@ -0,0 +1,88 @@ +import type { Block } from 'payload' + +export const RestExamplesBlock: Block = { + slug: 'restExamples', + fields: [ + { + name: 'data', + type: 'array', + fields: [ + { + name: 'operation', + type: 'text', + }, + { + name: 'method', + type: 'text', + }, + { + name: 'path', + type: 'text', + }, + { + name: 'description', + type: 'text', + }, + { + name: 'example', + type: 'group', + fields: [ + { + name: 'slug', + type: 'text', + }, + { + name: 'req', + type: 'json', + }, + { + name: 'res', + type: 'json', + }, + { + name: 'drawerContent', + type: 'textarea', + }, + ], + }, + ], + }, + ], + interfaceName: 'RestExamplesBlock', + jsx: { + export: ({ fields, lexicalToMarkdown }) => { + return { + props: { + data: fields.data.map((item) => { + return { + ...item, + example: { + ...item.example, + drawerContent: + lexicalToMarkdown && item.example.drawerContent + ? item.example.drawerContent + : undefined, + }, + } + }), + }, + } + }, + import: ({ children, markdownToLexical, props }) => { + return { + data: props.data.map((item) => { + return { + ...item, + example: { + ...item.example, + drawerContent: + markdownToLexical && item.example.drawerContent + ? item.example.drawerContent + : undefined, + }, + } + }), + } + }, + }, +} diff --git a/test/lexical-mdx/result.json b/test/lexical-mdx/result.json new file mode 100644 index 0000000000..a6583c1b0f --- /dev/null +++ b/test/lexical-mdx/result.json @@ -0,0 +1,47 @@ +{ + "editorState": { + "root": { + "children": [ + { + "format": "", + "type": "block", + "version": 2, + "fields": { + "blockType": "restExamples", + "data": [ + { + "operation": "Find", + "method": "GET", + "path": "/api/{collection-slug}", + "description": "Find paginated documents", + "example": { + "slug": "getCollection", + "req": true, + "res": { + "paginated": true, + "data": { + "id": "644a5c24cc1383022535fc7c", + "title": "Home", + "content": "REST API examples", + "slug": "home", + "createdAt": "2023-04-27T11:27:32.419Z", + "updatedAt": "2023-04-27T11:27:32.419Z" + } + }, + "drawerContent": "\n#### Heading\n\nOne `two` three\n\n- [sort](/docs/queries/overview#sort) - sort by field\n- [where](/docs/queries/overview) - pass a where query to constrain returned documents\n\n```ts\nconst a = 1\nconst b = 2\nconst c = 3\nconst d = 4\n```\n" + } + } + ], + "id": "6771f28d009a850b8401645e" + } + } + ], + "direction": null, + "format": "", + "indent": 0, + "type": "root", + "version": 1 + } + }, + "frontMatter": [] +} \ No newline at end of file diff --git a/test/lexical-mdx/tests/default.test.ts b/test/lexical-mdx/tests/default.test.ts new file mode 100644 index 0000000000..0c349c81f3 --- /dev/null +++ b/test/lexical-mdx/tests/default.test.ts @@ -0,0 +1,1372 @@ +import { tableJson } from '../tableJson.js' +import type { Test } from '../int.spec.js' +import { textToRichText } from '../textToRichText.js' + +export const defaultTests: Test[] = [ + { + inputAfterConvertFromEditorJSON: ``, + input: ` + + `, + blockNode: { + fields: { + blockType: 'PackageInstallOptions', + packageId: '444', + update: true, + uniqueId: 'xxx', + }, + }, + }, + { + input: ` + + ignored + +`, + inputAfterConvertFromEditorJSON: ``, + blockNode: { + fields: { + blockType: 'PackageInstallOptions', + packageId: '444', + }, + }, + }, + { + input: ` + + ignored + +`, + inputAfterConvertFromEditorJSON: ``, + blockNode: { + fields: { + blockType: 'PackageInstallOptions', + packageId: '444', + update: true, + }, + }, + }, + { + input: ` + + ignored + +`, + inputAfterConvertFromEditorJSON: ``, + blockNode: { + fields: { + blockType: 'PackageInstallOptions', + packageId: '444', + update: true, + }, + }, + }, + { + inputAfterConvertFromEditorJSON: ``, + input: ` + + ignored + +`, + blockNode: { + fields: { + blockType: 'PackageInstallOptions', + packageId: '444', + update: true, + }, + }, + }, + { + inputAfterConvertFromEditorJSON: ``, // Not test - test is not part of the block + input: ` + + ignored + +`, + blockNode: { + fields: { + blockType: 'PackageInstallOptions', + packageId: '444', + update: true, + someNestedObject: { test: 'hello' }, + }, + }, + }, + { + inputAfterConvertFromEditorJSON: ``, + + input: ` + + ignored + + ignoredi + + +`, + blockNode: { + fields: { + blockType: 'PackageInstallOptions', + packageId: '444', + update: true, + }, + }, + }, + { + inputAfterConvertFromEditorJSON: ``, + + input: ` + + ignored + + ignoredi + + + + +`, + blockNode: { + fields: { + blockType: 'PackageInstallOptions', + packageId: '444', + update: true, + }, + }, + }, + // TODO: Write test for this: + /* + + not ignored + + not ignored + + not ignored + + */ + { + input: ` +\`\`\`ts +hello\`\`\` +`, + inputAfterConvertFromEditorJSON: ` +\`\`\`ts +hello +\`\`\` +`, + blockNode: { + fields: { + blockType: 'Code', + code: 'hello', + language: 'ts', + }, + }, + }, + { + input: ` +\`\`\`ts + hello\`\`\` +`, + inputAfterConvertFromEditorJSON: ` +\`\`\`ts + hello +\`\`\` +`, + blockNode: { + fields: { + blockType: 'Code', + code: ' hello', + language: 'ts', + }, + }, + }, + { + input: ` +\`\`\`ts x\n hello\`\`\` +`, + inputAfterConvertFromEditorJSON: ` +\`\`\`ts + x + hello +\`\`\` +`, + blockNode: { + fields: { + blockType: 'Code', + code: ' x\n hello', + language: 'ts', + }, + }, + }, + { + input: ` +\`\`\`ts hello\`\`\` +`, + blockNode: { + fields: { + blockType: 'Code', + code: 'ts hello', + language: '', + }, + }, + }, + { + input: ` +\`\`\`hello\`\`\` +`, + blockNode: { + fields: { + blockType: 'Code', + code: 'hello', + language: '', + }, + }, + }, + { + input: ` +\`\`\`ts +hello +\`\`\` +`, + blockNode: { + fields: { + blockType: 'Code', + code: 'hello', + language: 'ts', + }, + }, + }, + { + input: ` +\`\`\`ts hello +there1 +\`\`\` +`, + inputAfterConvertFromEditorJSON: ` +\`\`\`ts + hello +there1 +\`\`\` +`, + blockNode: { + fields: { + blockType: 'Code', + code: ' hello\nthere1', + language: 'ts', + }, + }, + }, + { + input: ` +\`\`\`ts hello +there2 +!!\`\`\` +`, + inputAfterConvertFromEditorJSON: ` +\`\`\`ts + hello +there2 +!! +\`\`\` +`, + blockNode: { + fields: { + blockType: 'Code', + code: ' hello\nthere2\n!!', + language: 'ts', + }, + }, + }, + { + input: ` +\`\`\`ts +Hello +there3\`\`\` +`, + inputAfterConvertFromEditorJSON: ` +\`\`\`ts +Hello +there3 +\`\`\` +`, + blockNode: { + fields: { + blockType: 'Code', + code: 'Hello\nthere3', + language: 'ts', + }, + }, + }, + { + input: ` +\`\`\`ts +Hello +\`\`\`ts +nested +\`\`\`! +there4\`\`\` +`, + inputAfterConvertFromEditorJSON: ` +\`\`\`ts +Hello +\`\`\`ts +nested +\`\`\`! +there4 +\`\`\` +`, + blockNode: { + fields: { + blockType: 'Code', + code: 'Hello\n```ts\nnested\n```!\nthere4', + language: 'ts', + }, + }, + }, + { + ignoreSpacesAndNewlines: true, + input: ` +| Option | Default route | Description | +| ----------------- | ----------------------- | ----------------------------------------------- | +| \`account\` | | The user's account page. | +| \`createFirstUser\` | \`/create-first-user\` | The page to create the first user. | +`, + inputAfterConvertFromEditorJSON: ` +| Option | Default route | Description | +|---|---|---| +| \`account\` | | The user's account page. | +| \`createFirstUser\` | \`/create-first-user\` | The page to create the first user. | +`, + rootChildren: [tableJson], + }, + { + input: ` + + children text + +`, + blockNode: { + fields: { + blockType: 'Banner', + content: textToRichText('children text'), + }, + }, + }, + { + input: `\`inline code\``, + rootChildren: [ + { + children: [ + { + detail: 0, + format: 16, // Format 16 => inline code + mode: 'normal', + style: '', + text: 'inline code', + type: 'text', + version: 1, + }, + ], + format: '', + indent: 0, + type: 'paragraph', + version: 1, + textFormat: 0, + textStyle: '', + }, + ], + }, + { + // This test ensures that the JSX within the code block is does not disrupt the main JSX parsing + input: ` + + \`https://.payloadcms.com/page\` + +`, + blockNode: { + fields: { + blockType: 'Banner', + content: { + root: { + children: [ + { + children: [ + { + detail: 0, + format: 16, // Format 16 => inline code + mode: 'normal', + style: '', + text: 'https://.payloadcms.com/page', + type: 'text', + version: 1, + }, + ], + format: '', + indent: 0, + type: 'paragraph', + version: 1, + textFormat: 0, + textStyle: '', + }, + ], + format: '', + indent: 0, + type: 'root', + version: 1, + }, + }, + }, + }, + }, + { + input: 'Hello inline code test.', + rootChildren: [ + { + children: [ + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: 'Hello ', + type: 'text', + version: 1, + }, + { + type: 'inlineBlock', + + fields: { + code: 'inline code', + blockType: 'InlineCode', + }, + version: 1, + }, + + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: ' test.', + type: 'text', + version: 1, + }, + ], + format: '', + indent: 0, + type: 'paragraph', + version: 1, + textFormat: 0, + textStyle: '', + }, + ], + }, + { + input: ` + + Some text 1 code 1 some + + text 2 code 2 some text + + 3 code 3 some text 4code 4 + +`, + description: 'Banner with inline codes, each line a paragraph', + blockNode: { + fields: { + blockType: 'Banner', + content: { + root: { + children: [ + { + children: [ + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: 'Some text 1 ', + type: 'text', + version: 1, + }, + + { + type: 'inlineBlock', + + fields: { + code: 'code 1', + blockType: 'InlineCode', + }, + version: 1, + }, + + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: ' some', + type: 'text', + version: 1, + }, + ], + format: '', + indent: 0, + type: 'paragraph', + version: 1, + textFormat: 0, + textStyle: '', + }, + + { + children: [ + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: 'text 2 ', + type: 'text', + version: 1, + }, + + { + type: 'inlineBlock', + + fields: { + code: 'code 2', + blockType: 'InlineCode', + }, + version: 1, + }, + + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: ' some text', + type: 'text', + version: 1, + }, + ], + format: '', + indent: 0, + type: 'paragraph', + version: 1, + textFormat: 0, + textStyle: '', + }, + + { + children: [ + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: '3 ', + type: 'text', + version: 1, + }, + + { + type: 'inlineBlock', + + fields: { + code: 'code 3', + blockType: 'InlineCode', + }, + version: 1, + }, + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: ' some text 4', + type: 'text', + version: 1, + }, + { + type: 'inlineBlock', + fields: { + code: 'code 4', + blockType: 'InlineCode', + }, + version: 1, + }, + ], + format: '', + indent: 0, + type: 'paragraph', + version: 1, + textFormat: 0, + textStyle: '', + }, + ], + format: '', + indent: 0, + type: 'root', + version: 1, + }, + }, + }, + }, + }, + { + input: ` + + Some text 1 code 1 some + + text 2 code 2 some text + + 3 code 3 some text 4code 4 + +`, + description: 'Banner with inline codes, three paragraphs', + + blockNode: { + fields: { + blockType: 'Banner', + content: { + root: { + children: [ + { + children: [ + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: 'Some text 1 ', + type: 'text', + version: 1, + }, + { + type: 'inlineBlock', + fields: { + code: 'code 1', + blockType: 'InlineCode', + }, + version: 1, + }, + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: ' some', + type: 'text', + version: 1, + }, + ], + format: '', + indent: 0, + type: 'paragraph', + version: 1, + textFormat: 0, + textStyle: '', + }, + { + children: [ + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: 'text 2 ', + type: 'text', + version: 1, + }, + { + type: 'inlineBlock', + fields: { + code: 'code 2', + blockType: 'InlineCode', + }, + version: 1, + }, + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: ' some text', + type: 'text', + version: 1, + }, + ], + format: '', + indent: 0, + type: 'paragraph', + version: 1, + textFormat: 0, + textStyle: '', + }, + { + children: [ + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: '3 ', + type: 'text', + version: 1, + }, + { + type: 'inlineBlock', + fields: { + code: 'code 3', + blockType: 'InlineCode', + }, + version: 1, + }, + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: ' some text 4', + type: 'text', + version: 1, + }, + { + type: 'inlineBlock', + fields: { + code: 'code 4', + blockType: 'InlineCode', + }, + version: 1, + }, + ], + format: '', + indent: 0, + type: 'paragraph', + version: 1, + textFormat: 0, + textStyle: '', + }, + ], + format: '', + indent: 0, + type: 'root', + version: 1, + }, + }, + }, + }, + }, + { + input: ` +Text before banner + + + test + +`, + rootChildren: [ + { + children: [ + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: 'Text before banner', + type: 'text', + version: 1, + }, + ], + format: '', + indent: 0, + type: 'paragraph', + version: 1, + textFormat: 0, + textStyle: '', + }, + { + type: 'block', + format: '', + fields: { + blockType: 'Banner', + content: { + root: { + children: [ + { + children: [ + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: 'test', + type: 'text', + version: 1, + }, + ], + format: '', + indent: 0, + textFormat: 0, + textStyle: '', + type: 'paragraph', + version: 1, + }, + ], + format: '', + indent: 0, + type: 'root', + version: 1, + }, + }, + }, + version: 2, + }, + ], + }, + { + description: 'TextContainerNoTrim with nested, no-leftpad content', + input: ` + +no indent + + indent 2 + + indent 4 + +no indent + +`, + blockNode: { + fields: { + blockType: 'TextContainerNoTrim', + text: `no indent + + indent 2 + + indent 4 + +no indent`, + }, + }, + }, + { + description: 'TextContainer with nested, no-leftpad content', + + input: ` + +no indent + + indent 2 + + indent 4 + +no indent + +`, + inputAfterConvertFromEditorJSON: ` + + no indent + + indent 2 + + indent 4 + + no indent + +`, + blockNode: { + fields: { + blockType: 'TextContainer', + text: `no indent + + indent 2 + + indent 4 + +no indent`, + }, + }, + }, + { + description: 'TextContainerNoTrim with nested, leftpad content', + input: ` + + indent 2 + + indent 4 + + indent 6 + + indent 2 + +`, + blockNode: { + fields: { + blockType: 'TextContainerNoTrim', + text: ` indent 2 + + indent 4 + + indent 6 + + indent 2`, + }, + }, + }, + { + description: 'TextContainer with nested, leftpad content', + input: ` + + indent 2 + + indent 4 + + indent 6 + + indent 2 + +`, + blockNode: { + fields: { + blockType: 'TextContainer', + text: `indent 2 + + indent 4 + + indent 6 + +indent 2`, + }, + }, + }, + { + input: ` +Some text 1 +code 2 +`, + description: 'InlineCode after text, split by linebreak', + rootChildren: [ + { + children: [ + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: 'Some text 1', + type: 'text', + version: 1, + }, + { + type: 'linebreak', + version: 1, + }, + { + type: 'inlineBlock', + fields: { + code: 'code 2', + blockType: 'InlineCode', + }, + version: 1, + }, + ], + format: '', + indent: 0, + type: 'paragraph', + version: 1, + textFormat: 0, + textStyle: '', + }, + ], + }, + { + description: 'Code block with nested within Banner', + input: ` + + \`\`\`ts + indent 1; + \`\`\` + +`, + blockNode: { + fields: { + blockType: 'Banner', + content: { + root: { + children: [ + { + fields: { + blockType: 'Code', + code: ' indent 1;', + language: 'ts', + }, + format: '', + type: 'block', + version: 2, + }, + ], + format: '', + indent: 0, + type: 'root', + version: 1, + }, + }, + }, + }, + }, + { + description: 'Code block with nested within Banner 2', + input: ` + +\`\`\`ts + indent 1; +\`\`\` + +`, + inputAfterConvertFromEditorJSON: ` + + \`\`\`ts + indent 1; + \`\`\` + +`, + blockNode: { + fields: { + blockType: 'Banner', + content: { + root: { + children: [ + { + fields: { + blockType: 'Code', + code: ' indent 1;', + language: 'ts', + }, + format: '', + type: 'block', + version: 2, + }, + ], + format: '', + indent: 0, + type: 'root', + version: 1, + }, + }, + }, + }, + }, + { + description: 'One-line Banner', + input: ` +Hi +`, + inputAfterConvertFromEditorJSON: ` + + Hi + +`, + blockNode: { + fields: { + blockType: 'Banner', + content: { + root: { + children: [ + { + children: [ + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: 'Hi', + type: 'text', + version: 1, + }, + ], + format: '', + indent: 0, + type: 'paragraph', + version: 1, + textFormat: 0, + textStyle: '', + }, + ], + format: '', + indent: 0, + type: 'root', + version: 1, + }, + }, + }, + }, + }, + { + description: 'Code block with nested within 2 Banners', + input: ` + + +\`\`\`ts + indent 1; +\`\`\` + + +`, + inputAfterConvertFromEditorJSON: ` + + + \`\`\`ts + indent 1; + \`\`\` + + +`, + blockNode: { + fields: { + blockType: 'Banner', + content: { + root: { + children: [ + { + type: 'block', + format: '', + fields: { + blockType: 'Banner', + content: { + root: { + children: [ + { + fields: { + blockType: 'Code', + code: ' indent 1;', + language: 'ts', + }, + format: '', + type: 'block', + version: 2, + }, + ], + format: '', + indent: 0, + type: 'root', + version: 1, + }, + }, + }, + version: 2, + }, + ], + format: '', + indent: 0, + type: 'root', + version: 1, + }, + }, + }, + }, + }, + { + inputAfterConvertFromEditorJSON: ` + + Some line [Start of link line2](/some/link) + +`, + input: ` + +Some line [Start of link + line2](/some/link) + +`, + blockNode: { + fields: { + blockType: 'Banner', + content: { + root: { + children: [ + { + children: [ + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: 'Some line ', + type: 'text', + version: 1, + }, + { + children: [ + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: 'Start of link line2', + type: 'text', + version: 1, + }, + ], + fields: { + linkType: 'custom', + newTab: false, + url: '/some/link', + }, + format: '', + indent: 0, + type: 'link', + version: 3, + }, + ], + direction: null, + format: '', + indent: 0, + textFormat: 0, + textStyle: '', + type: 'paragraph', + version: 1, + }, + ], + direction: null, + format: '', + indent: 0, + type: 'root', + version: 1, + }, + }, + }, + }, + }, + { + inputAfterConvertFromEditorJSON: ` + + Text text [ Link ](/some/link) . + +`, + input: ` + + Text text [ Link + ](/some/link) . + +`, + blockNode: { + fields: { + blockType: 'Banner', + content: { + root: { + children: [ + { + children: [ + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: 'Text text ', + type: 'text', + version: 1, + }, + { + children: [ + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: ' Link ', + type: 'text', + version: 1, + }, + ], + fields: { + linkType: 'custom', + newTab: false, + url: '/some/link', + }, + format: '', + indent: 0, + type: 'link', + version: 3, + }, + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: ' .', + type: 'text', + version: 1, + }, + ], + direction: null, + format: '', + indent: 0, + textFormat: 0, + textStyle: '', + type: 'paragraph', + version: 1, + }, + ], + direction: null, + format: '', + indent: 0, + type: 'root', + version: 1, + }, + }, + }, + }, + }, + { + input: ` + + +`, + inputAfterConvertFromEditorJSON: ``, + blockNode: { + fields: { + blockType: 'PackageInstallOptions', + packageId: 'Line', + someObject: { test: 'Line 1\n\nLine 2' }, + update: true, + }, + }, + }, +] diff --git a/test/lexical-mdx/tests/restExamples.input.mdx b/test/lexical-mdx/tests/restExamples.input.mdx new file mode 100644 index 0000000000..0267e50f17 --- /dev/null +++ b/test/lexical-mdx/tests/restExamples.input.mdx @@ -0,0 +1,54 @@ +#### Heading + +One `two` three + +- [sort](/docs/queries/overview#sort) - sort by field +- [where](/docs/queries/overview) - pass a where query to constrain returned documents + +```ts +const a = 1 +const b = 2 +const c = 3 +const d = 4 +``` + + diff --git a/test/lexical-mdx/tests/restExamples.output.json b/test/lexical-mdx/tests/restExamples.output.json new file mode 100644 index 0000000000..b5b30c3df0 --- /dev/null +++ b/test/lexical-mdx/tests/restExamples.output.json @@ -0,0 +1,215 @@ +{ + "editorState": { + "root": { + "children": [ + { + "children": [ + { + "detail": 0, + "format": 0, + "mode": "normal", + "style": "", + "text": "Heading", + "type": "text", + "version": 1 + } + ], + "direction": null, + "format": "", + "indent": 0, + "type": "heading", + "version": 1, + "tag": "h4" + }, + { + "children": [ + { + "detail": 0, + "format": 0, + "mode": "normal", + "style": "", + "text": "One ", + "type": "text", + "version": 1 + }, + { + "detail": 0, + "format": 16, + "mode": "normal", + "style": "", + "text": "two", + "type": "text", + "version": 1 + }, + { + "detail": 0, + "format": 0, + "mode": "normal", + "style": "", + "text": " three", + "type": "text", + "version": 1 + } + ], + "direction": null, + "format": "", + "indent": 0, + "type": "paragraph", + "version": 1, + "textFormat": 0, + "textStyle": "" + }, + { + "children": [ + { + "children": [ + { + "children": [ + { + "detail": 0, + "format": 0, + "mode": "normal", + "style": "", + "text": "sort", + "type": "text", + "version": 1 + } + ], + "direction": null, + "format": "", + "indent": 0, + "type": "link", + "version": 3, + "fields": { + "doc": null, + "linkType": "custom", + "newTab": false, + "url": "/docs/queries/overview#sort" + }, + "id": "67708768dd53bf162cb1ae6a" + }, + { + "detail": 0, + "format": 0, + "mode": "normal", + "style": "", + "text": " - sort by field", + "type": "text", + "version": 1 + } + ], + "direction": null, + "format": "", + "indent": 0, + "type": "listitem", + "version": 1, + "value": 1 + }, + { + "children": [ + { + "children": [ + { + "detail": 0, + "format": 0, + "mode": "normal", + "style": "", + "text": "where", + "type": "text", + "version": 1 + } + ], + "direction": null, + "format": "", + "indent": 0, + "type": "link", + "version": 3, + "fields": { + "doc": null, + "linkType": "custom", + "newTab": false, + "url": "/docs/queries/overview" + }, + "id": "67708768dd53bf162cb1ae6b" + }, + { + "detail": 0, + "format": 0, + "mode": "normal", + "style": "", + "text": " - pass a where query to constrain returned documents", + "type": "text", + "version": 1 + } + ], + "direction": null, + "format": "", + "indent": 0, + "type": "listitem", + "version": 1, + "value": 2 + } + ], + "direction": null, + "format": "", + "indent": 0, + "type": "list", + "version": 1, + "listType": "bullet", + "start": 1, + "tag": "ul" + }, + { + "format": "", + "type": "block", + "version": 2, + "fields": { + "blockType": "Code", + "language": "ts", + "code": "const a = 1\nconst b = 2\nconst c = 3\nconst d = 4", + "id": "67708768dd53bf162cb1ae6c" + } + }, + { + "format": "", + "type": "block", + "version": 2, + "fields": { + "blockType": "restExamples", + "data": [ + { + "operation": "Find", + "method": "GET", + "path": "/api/{collection-slug}", + "description": "Find paginated documents", + "example": { + "slug": "getCollection", + "req": true, + "res": { + "paginated": true, + "data": { + "id": "644a5c24cc1383022535fc7c", + "title": "Home", + "content": "REST API examples", + "slug": "home", + "createdAt": "2023-04-27T11:27:32.419Z", + "updatedAt": "2023-04-27T11:27:32.419Z" + } + }, + "drawerContent": "\n#### Heading\n\nOne `two` three\n\n- [sort](/docs/queries/overview#sort) - sort by field\n- [where](/docs/queries/overview) - pass a where query to constrain returned documents\n\n```ts\nconst a = 1\nconst b = 2\nconst c = 3\nconst d = 4\n```\n" + } + } + ], + "id": "67708768dd53bf162cb1ae6d" + } + } + ], + "direction": null, + "format": "", + "indent": 0, + "type": "root", + "version": 1 + } + }, + "frontMatter": [] +} diff --git a/test/lexical-mdx/tests/restExamples.test.ts b/test/lexical-mdx/tests/restExamples.test.ts new file mode 100644 index 0000000000..1b9935bd6b --- /dev/null +++ b/test/lexical-mdx/tests/restExamples.test.ts @@ -0,0 +1,14 @@ +import { readFileSync } from 'fs' +import type { Test } from '../int.spec.js' +import { fileURLToPath } from 'url' +import path from 'path' + +const filename = fileURLToPath(import.meta.url) +const dirname = path.dirname(filename) + +export const restExamplesTest1: Test = { + input: readFileSync(path.resolve(dirname, 'restExamples.input.mdx'), 'utf-8'), + rootChildren: JSON.parse(readFileSync(path.resolve(dirname, 'restExamples.output.json'), 'utf-8')) + .editorState.root.children, + convertFromEditorJSON: false, +} diff --git a/test/lexical-mdx/tests/restExamples2.input.mdx b/test/lexical-mdx/tests/restExamples2.input.mdx new file mode 100644 index 0000000000..05ce186e46 --- /dev/null +++ b/test/lexical-mdx/tests/restExamples2.input.mdx @@ -0,0 +1,40 @@ + diff --git a/test/lexical-mdx/tests/restExamples2.output.json b/test/lexical-mdx/tests/restExamples2.output.json new file mode 100644 index 0000000000..a72d140559 --- /dev/null +++ b/test/lexical-mdx/tests/restExamples2.output.json @@ -0,0 +1,47 @@ +{ + "editorState": { + "root": { + "children": [ + { + "format": "", + "type": "block", + "version": 2, + "fields": { + "blockType": "restExamples", + "data": [ + { + "operation": "Find", + "method": "GET", + "path": "/api/{collection-slug}", + "description": "Find paginated documents", + "example": { + "slug": "getCollection", + "req": true, + "res": { + "paginated": true, + "data": { + "id": "644a5c24cc1383022535fc7c", + "title": "Home", + "content": "REST API examples", + "slug": "home", + "createdAt": "2023-04-27T11:27:32.419Z", + "updatedAt": "2023-04-27T11:27:32.419Z" + } + }, + "drawerContent": "\n#### Heading\n\nOne `two` three\n\n- [sort](/docs/queries/overview#sort) - sort by field\n- [where](/docs/queries/overview) - pass a where query to constrain returned documents\n\n```ts\nconst a = 1\nconst b = 2\nconst c = 3\nconst d = 4\n```\n" + } + } + ], + "id": "67708768dd53bf162cb1ae6d" + } + } + ], + "direction": null, + "format": "", + "indent": 0, + "type": "root", + "version": 1 + } + }, + "frontMatter": [] +} diff --git a/test/lexical-mdx/tests/restExamples2.test.ts b/test/lexical-mdx/tests/restExamples2.test.ts new file mode 100644 index 0000000000..6a3236e80f --- /dev/null +++ b/test/lexical-mdx/tests/restExamples2.test.ts @@ -0,0 +1,15 @@ +import { readFileSync } from 'fs' +import type { Test } from '../int.spec.js' +import { fileURLToPath } from 'url' +import path from 'path' + +const filename = fileURLToPath(import.meta.url) +const dirname = path.dirname(filename) + +export const restExamplesTest2: Test = { + input: readFileSync(path.resolve(dirname, 'restExamples2.input.mdx'), 'utf-8'), + rootChildren: JSON.parse( + readFileSync(path.resolve(dirname, 'restExamples2.output.json'), 'utf-8'), + ).editorState.root.children, + convertFromEditorJSON: false, +}