diff --git a/.changeset/honest-seahorses-whisper.md b/.changeset/honest-seahorses-whisper.md new file mode 100644 index 000000000..26907e370 --- /dev/null +++ b/.changeset/honest-seahorses-whisper.md @@ -0,0 +1,5 @@ +--- +'@keystatic/core': patch +--- + +Support ordered lists that don't start at 1 in `fields.markdoc` and `fields.mdx` diff --git a/.github/workflows/publish_snapshot.yml b/.github/workflows/publish_snapshot.yml index 91a1f8168..8cfdb2fbe 100644 --- a/.github/workflows/publish_snapshot.yml +++ b/.github/workflows/publish_snapshot.yml @@ -22,7 +22,7 @@ jobs: - name: version packages run: pnpm changeset version --snapshot ${{ inputs.tag }} env: - GITHUB_TOKEN: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: git commit run: | diff --git a/.github/workflows/version_packages.yml b/.github/workflows/version_packages.yml index 72649ae93..f4880379e 100644 --- a/.github/workflows/version_packages.yml +++ b/.github/workflows/version_packages.yml @@ -22,5 +22,4 @@ jobs: with: version: pnpm run version-packages env: - # use a different GitHub account to have the CI run on push - GITHUB_TOKEN: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/packages/keystatic/src/form/fields/markdoc/editor/inputrules/rules.ts b/packages/keystatic/src/form/fields/markdoc/editor/inputrules/rules.ts index ba7d13f68..53669f647 100644 --- a/packages/keystatic/src/form/fields/markdoc/editor/inputrules/rules.ts +++ b/packages/keystatic/src/form/fields/markdoc/editor/inputrules/rules.ts @@ -28,8 +28,10 @@ export function inputRulesForSchema({ nodes, marks, config }: EditorSchema) { } if (nodes.ordered_list) { rules.push({ - pattern: /^\s*\d+(?:\.|\))\s$/, - handler: wrappingInputRuleHandler(nodes.ordered_list), + pattern: /^\s*(\d+)(?:\.|\))\s$/, + handler: wrappingInputRuleHandler(nodes.ordered_list, match => ({ + start: parseInt(match[1], 10), + })), }); } if (nodes.unordered_list) { diff --git a/packages/keystatic/src/form/fields/markdoc/editor/markdoc/parse.ts b/packages/keystatic/src/form/fields/markdoc/editor/markdoc/parse.ts index e0bcd2cba..6e6f67b25 100644 --- a/packages/keystatic/src/form/fields/markdoc/editor/markdoc/parse.ts +++ b/packages/keystatic/src/form/fields/markdoc/editor/markdoc/parse.ts @@ -297,7 +297,13 @@ function markdocNodeToProseMirrorNode( ? schema.nodes.ordered_list : schema.nodes.unordered_list; if (!listType) return notAllowed(node, parentType); - return createAndFill(node, listType, {}); + return createAndFill( + node, + listType, + node.attributes.ordered && node.attributes.start !== undefined + ? { start: node.attributes.start } + : {} + ); } if (node.type === 'table') { if (!schema.nodes.table) return notAllowed(node, parentType); diff --git a/packages/keystatic/src/form/fields/markdoc/editor/markdoc/serialize.ts b/packages/keystatic/src/form/fields/markdoc/editor/markdoc/serialize.ts index bec3f9d19..3831248fd 100644 --- a/packages/keystatic/src/form/fields/markdoc/editor/markdoc/serialize.ts +++ b/packages/keystatic/src/form/fields/markdoc/editor/markdoc/serialize.ts @@ -244,7 +244,11 @@ export function proseMirrorToMarkdoc( return new Ast.Node('item', {}, listItemContent); } if (node.type === schema.nodes.ordered_list) { - return new Ast.Node('list', { ordered: true }, blocks(node.content)); + return new Ast.Node( + 'list', + { ordered: true, start: node.attrs.start }, + blocks(node.content) + ); } if (node.type === schema.nodes.unordered_list) { return new Ast.Node('list', { ordered: false }, blocks(node.content)); diff --git a/packages/keystatic/src/form/fields/markdoc/editor/mdx/parse.ts b/packages/keystatic/src/form/fields/markdoc/editor/mdx/parse.ts index ebf38f1b6..13456fc96 100644 --- a/packages/keystatic/src/form/fields/markdoc/editor/mdx/parse.ts +++ b/packages/keystatic/src/form/fields/markdoc/editor/mdx/parse.ts @@ -318,7 +318,11 @@ function markdocNodeToProseMirrorNode( ? schema.nodes.ordered_list : schema.nodes.unordered_list; if (!listType) return notAllowed(node, parentType); - return createAndFill(node, listType, {}); + return createAndFill( + node, + listType, + node.ordered && node.start != null ? { start: node.start } : {} + ); } if (node.type === 'table') { if (!schema.nodes.table) return notAllowed(node, parentType); diff --git a/packages/keystatic/src/form/fields/markdoc/editor/mdx/serialize.ts b/packages/keystatic/src/form/fields/markdoc/editor/mdx/serialize.ts index 988aff370..b596fdcec 100644 --- a/packages/keystatic/src/form/fields/markdoc/editor/mdx/serialize.ts +++ b/packages/keystatic/src/form/fields/markdoc/editor/mdx/serialize.ts @@ -249,6 +249,7 @@ function proseMirrorToMDX( type: 'list', ordered: true, spread: false, + start: node.attrs.start, children: mapContent(node, node => convertListItem(blocks(node.content))), }; } diff --git a/packages/keystatic/src/form/fields/markdoc/editor/schema.tsx b/packages/keystatic/src/form/fields/markdoc/editor/schema.tsx index 7c69f981e..cf5fc8c08 100644 --- a/packages/keystatic/src/form/fields/markdoc/editor/schema.tsx +++ b/packages/keystatic/src/form/fields/markdoc/editor/schema.tsx @@ -221,9 +221,26 @@ const nodeSpecs = { ordered_list: { content: 'list_item+', group: 'block', - parseDOM: [{ tag: 'ol' }], - toDOM() { - return olDOM; + attrs: { + start: { default: 1 }, + }, + parseDOM: [ + { + tag: 'ol', + getAttrs: node => { + if (typeof node === 'string') { + return false; + } + if (!(node instanceof HTMLOListElement) || node.start < 0) { + return { start: 1 }; + } + return { start: node.start }; + }, + }, + ], + toDOM(node) { + if (node.attrs.start === 1) return olDOM; + return ['ol', { start: node.attrs.start }, 0]; }, insertMenu: { label: 'Ordered list', diff --git a/packages/keystatic/src/form/fields/markdoc/editor/tests/lists.test.tsx b/packages/keystatic/src/form/fields/markdoc/editor/tests/lists.test.tsx index a5e1a5e19..9491f7064 100644 --- a/packages/keystatic/src/form/fields/markdoc/editor/tests/lists.test.tsx +++ b/packages/keystatic/src/form/fields/markdoc/editor/tests/lists.test.tsx @@ -19,7 +19,37 @@ test('ordered list shortcut', async () => { await user.keyboard(' '); expect(state()).toMatchInlineSnapshot(` - + + + + + + + + + `); +}); + +test('ordered list shortcut with different start', async () => { + const { state, user } = renderEditor( + + + + 5. + + + + + ); + + await user.keyboard(' '); + expect(state()).toMatchInlineSnapshot(` + + @@ -190,7 +220,9 @@ test('toggle list on empty line', async () => { expect(state()).toMatchInlineSnapshot(` - + @@ -219,7 +251,9 @@ test('toggle list on line with text', async () => { expect(state()).toMatchInlineSnapshot(` - + @@ -252,7 +286,9 @@ test('toggle list on line with text with marks', async () => { expect(state()).toMatchInlineSnapshot(` - + @@ -382,7 +418,9 @@ test('toggle ordered_list inside of multi-item ordered_list', async () => { expect(state()).toMatchInlineSnapshot(` - + @@ -397,7 +435,9 @@ test('toggle ordered_list inside of multi-item ordered_list', async () => { - + @@ -572,7 +612,9 @@ test('backspace at start of list only unwraps the first item', async () => { some text - + @@ -788,7 +830,9 @@ test('changing the type of a nested list', async () => { some text - + @@ -846,14 +890,18 @@ test('changing the type of a nested list to something which it is nested inside' top text - + middle text - + diff --git a/packages/keystatic/src/form/fields/markdoc/editor/tests/markdoc.test.tsx b/packages/keystatic/src/form/fields/markdoc/editor/tests/markdoc.test.tsx index a794f3c61..519b7c389 100644 --- a/packages/keystatic/src/form/fields/markdoc/editor/tests/markdoc.test.tsx +++ b/packages/keystatic/src/form/fields/markdoc/editor/tests/markdoc.test.tsx @@ -297,7 +297,9 @@ test('link in list', () => { const editor = fromMarkdoc(markdoc); expect(editor).toMatchInlineSnapshot(` - + @@ -690,3 +692,16 @@ test('undefined in conditional value', () => { " `); }); + +test('ordered list with start', () => { + const markdoc = `5. a +1. b +1. c`; + const editor = fromMarkdoc(markdoc); + expect(toMarkdoc(editor)).toMatchInlineSnapshot(` + "5. a + 1. b + 1. c + " + `); +}); diff --git a/packages/keystatic/src/form/fields/markdoc/editor/tests/mdx.test.tsx b/packages/keystatic/src/form/fields/markdoc/editor/tests/mdx.test.tsx index 24e1c5140..541b01ca3 100644 --- a/packages/keystatic/src/form/fields/markdoc/editor/tests/mdx.test.tsx +++ b/packages/keystatic/src/form/fields/markdoc/editor/tests/mdx.test.tsx @@ -303,7 +303,9 @@ test('link in list', () => { const editor = fromMDX(mdx); expect(editor).toMatchInlineSnapshot(` - + @@ -1032,3 +1034,16 @@ test('loose list', () => { " `); }); + +test('ordered list with start', () => { + const mdx = `5. a +1. b +1. c`; + const editor = fromMDX(mdx); + expect(toMDX(editor)).toMatchInlineSnapshot(` + "5. a + 6. b + 7. c + " + `); +}); diff --git a/packages/keystatic/src/form/fields/markdoc/editor/tests/pasting/from-other-editors.test.tsx b/packages/keystatic/src/form/fields/markdoc/editor/tests/pasting/from-other-editors.test.tsx index 04e11fe7c..0a65d8a32 100644 --- a/packages/keystatic/src/form/fields/markdoc/editor/tests/pasting/from-other-editors.test.tsx +++ b/packages/keystatic/src/form/fields/markdoc/editor/tests/pasting/from-other-editors.test.tsx @@ -187,7 +187,9 @@ test('confluence', async () => { - + @@ -201,7 +203,9 @@ test('confluence', async () => { item - + @@ -435,7 +439,9 @@ there is a break before this

- + @@ -449,7 +455,9 @@ there is a break before this

item
- + @@ -604,7 +612,9 @@ test('dropbox paper', async () => { - + @@ -618,7 +628,9 @@ test('dropbox paper', async () => { item - + @@ -803,7 +815,9 @@ test('google docs', async () => { - + @@ -817,7 +831,9 @@ test('google docs', async () => { item - + diff --git a/packages/keystatic/src/form/fields/markdoc/editor/tests/pasting/markdown.test.tsx b/packages/keystatic/src/form/fields/markdoc/editor/tests/pasting/markdown.test.tsx index da4d26ba6..e55156019 100644 --- a/packages/keystatic/src/form/fields/markdoc/editor/tests/pasting/markdown.test.tsx +++ b/packages/keystatic/src/form/fields/markdoc/editor/tests/pasting/markdown.test.tsx @@ -242,7 +242,9 @@ there is a break before this - + @@ -256,7 +258,9 @@ there is a break before this item - +