Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions packages/core/src/blocks/ListItem/NumberedListItem/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,9 @@ export const createNumberedListItemBlockSpec = createBlockSpec(

const defaultProps = parseDefaultProps(element);

if (element.previousElementSibling || startIndex === 1) {
return defaultProps;
}

return {
...defaultProps,
start: startIndex,
start: element.previousElementSibling || startIndex === 1 ? undefined : startIndex,
};
}

Expand Down
101 changes: 101 additions & 0 deletions tests/src/unit/core/blocks/NumberedListItem.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { describe, expect, it } from "vitest";
import { createNumberedListItemBlockSpec } from "../../../../../packages/core/src/blocks/ListItem/NumberedListItem/block.js";

describe("NumberedListItem parse() method", () => {
const blockSpec = createNumberedListItemBlockSpec();
const parseFunc = blockSpec.implementation.parse;

if (!parseFunc) {
throw new Error("parse function not found");
}

it("should always return an object with 'start' property - first item, startIndex=1", () => {
// Create mock DOM elements
const li = document.createElement("li");
const ol = document.createElement("ol");
ol.setAttribute("start", "1");
ol.appendChild(li);

const result = parseFunc(li);

// The parse function should return an object with 'start' property
expect(result).toBeDefined();
expect(result).toHaveProperty("start");
expect(result?.start).toBeUndefined(); // Should be undefined for first item at index 1
});

it("should always return an object with 'start' property - first item, startIndex=5", () => {
const li = document.createElement("li");
const ol = document.createElement("ol");
ol.setAttribute("start", "5");
ol.appendChild(li);

const result = parseFunc(li);

expect(result).toBeDefined();
expect(result).toHaveProperty("start");
expect(result?.start).toBe(5); // Should be 5 for first item at non-standard index
});

it("should always return an object with 'start' property - subsequent item", () => {
const li1 = document.createElement("li");
const li2 = document.createElement("li");
const ol = document.createElement("ol");
ol.setAttribute("start", "1");
ol.appendChild(li1);
ol.appendChild(li2);

const result = parseFunc(li2);

expect(result).toBeDefined();
expect(result).toHaveProperty("start");
expect(result?.start).toBeUndefined(); // Subsequent items don't need explicit start
});

it("should always return an object with 'start' property - subsequent item in list starting at 5", () => {
const li1 = document.createElement("li");
const li2 = document.createElement("li");
const ol = document.createElement("ol");
ol.setAttribute("start", "5");
ol.appendChild(li1);
ol.appendChild(li2);

const result = parseFunc(li2);

expect(result).toBeDefined();
expect(result).toHaveProperty("start");
expect(result?.start).toBeUndefined(); // Subsequent items get undefined
});

it("regression test for issue #2241 - ensures 'start' property is always present", () => {
// This test verifies the fix for issue #2241
// The old code would return defaultProps (without 'start') for certain conditions
// The new code always includes 'start' in the returned object

const testCases = [
{ startAttr: "1", hasPreSibling: false, expectedStart: undefined },
{ startAttr: "1", hasPreSibling: true, expectedStart: undefined },
{ startAttr: "5", hasPreSibling: false, expectedStart: 5 },
{ startAttr: "5", hasPreSibling: true, expectedStart: undefined },
];

testCases.forEach(({ startAttr, hasPreSibling, expectedStart }) => {
const li = document.createElement("li");
const ol = document.createElement("ol");
ol.setAttribute("start", startAttr);

if (hasPreSibling) {
const li1 = document.createElement("li");
ol.appendChild(li1);
}

ol.appendChild(li);

const result = parseFunc(li);

// Critical assertion: 'start' property must ALWAYS be present
expect(result).toHaveProperty("start");
expect(result?.start).toBe(expectedStart);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
[
{
"children": [],
"content": [
{
"styles": {},
"text": "First item",
"type": "text",
},
],
"id": "1",
"props": {
"backgroundColor": "default",
"textAlignment": "left",
"textColor": "default",
},
"type": "numberedListItem",
},
{
"children": [],
"content": [
{
"styles": {},
"text": "Second item",
"type": "text",
},
],
"id": "2",
"props": {
"backgroundColor": "default",
"textAlignment": "left",
"textColor": "default",
},
"type": "numberedListItem",
},
{
"children": [],
"content": [
{
"styles": {},
"text": "Third item",
"type": "text",
},
],
"id": "3",
"props": {
"backgroundColor": "default",
"textAlignment": "left",
"textColor": "default",
},
"type": "numberedListItem",
},
{
"children": [],
"content": [
{
"styles": {},
"text": "List starting at 5",
"type": "text",
},
],
"id": "4",
"props": {
"backgroundColor": "default",
"textAlignment": "left",
"textColor": "default",
},
"type": "numberedListItem",
},
{
"children": [],
"content": [
{
"styles": {},
"text": "Second item",
"type": "text",
},
],
"id": "5",
"props": {
"backgroundColor": "default",
"textAlignment": "left",
"textColor": "default",
},
"type": "numberedListItem",
},
{
"children": [],
"content": [
{
"styles": {},
"text": "Third item",
"type": "text",
},
],
"id": "6",
"props": {
"backgroundColor": "default",
"textAlignment": "left",
"textColor": "default",
},
"type": "numberedListItem",
},
]
13 changes: 13 additions & 0 deletions tests/src/unit/core/formatConversion/parse/parseTestInstances.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1111,4 +1111,17 @@ Regular paragraph`,
},
executeTest: testParseMarkdown,
},
{
testCase: {
name: "issue2241NumberedListStartProperty",
content: `1. First item
2. Second item
3. Third item

5. List starting at 5
6. Second item
7. Third item`,
},
executeTest: testParseMarkdown,
},
];
Loading