From 1bc59f8383d1d369227d9c717ab305d926d4f3a9 Mon Sep 17 00:00:00 2001 From: Luccas Correa Date: Wed, 4 Feb 2026 16:41:05 -0300 Subject: [PATCH 1/4] fix: issue updating paragraph properties --- .../run/calculateInlineRunPropertiesPlugin.js | 2 +- ...calculateInlineRunPropertiesPlugin.test.js | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/super-editor/src/extensions/run/calculateInlineRunPropertiesPlugin.js b/packages/super-editor/src/extensions/run/calculateInlineRunPropertiesPlugin.js index 064a268623..145e968d47 100644 --- a/packages/super-editor/src/extensions/run/calculateInlineRunPropertiesPlugin.js +++ b/packages/super-editor/src/extensions/run/calculateInlineRunPropertiesPlugin.js @@ -84,7 +84,7 @@ export const calculateInlineRunPropertiesPlugin = (editor) => const inlineRunProperties = getInlineRunProperties(runPropertiesFromMarks, runPropertiesFromStyles); const runProperties = Object.keys(inlineRunProperties).length ? inlineRunProperties : null; - const isFirstInParagraph = $pos.parent.firstChild === runNode; + const isFirstInParagraph = $pos.parent.firstChild === runNode && $pos.parent.type === paragraphNode.type; if (isFirstInParagraph) { // Keep paragraph's default runProperties in sync for the first run diff --git a/packages/super-editor/src/extensions/run/calculateInlineRunPropertiesPlugin.test.js b/packages/super-editor/src/extensions/run/calculateInlineRunPropertiesPlugin.test.js index f7c1a2261b..51a78f5dd7 100644 --- a/packages/super-editor/src/extensions/run/calculateInlineRunPropertiesPlugin.test.js +++ b/packages/super-editor/src/extensions/run/calculateInlineRunPropertiesPlugin.test.js @@ -173,6 +173,25 @@ describe('calculateInlineRunPropertiesPlugin', () => { expect(paragraph.attrs.paragraphProperties).toEqual({ runProperties: { bold: true } }); }); + it('does not treat nested runs as the paragraph first run', () => { + const schema = makeSchema(); + const doc = schema.node('doc', null, [ + schema.node('paragraph', null, [schema.node('run', null, [schema.node('run', null, schema.text('Nested'))])]), + ]); + const state = createState(schema, doc); + const [, innerRunPos] = runPositions(state.doc); + const from = innerRunPos + 1; + const to = innerRunPos + 3; + + const tr = state.tr.addMark(from, to, schema.marks.bold.create()); + const { state: nextState } = state.applyTransaction(tr); + + const paragraph = nextState.doc.firstChild; + expect(paragraph.attrs.paragraphProperties?.runProperties?.bold).toBeUndefined(); + const innerRun = nextState.doc.nodeAt(innerRunPos); + expect(innerRun?.attrs.runProperties).toEqual({ bold: true }); + }); + it('does not update paragraph runProperties when a non-first run changes', () => { const schema = makeSchema(); const doc = schema.node('doc', null, [ From 7a65ec85a080d9e514117d9b06bd09badf5235cd Mon Sep 17 00:00:00 2001 From: Luccas Correa Date: Thu, 5 Feb 2026 10:13:55 -0300 Subject: [PATCH 2/4] fix: account for nested runs when updating paragraph props --- .../run/calculateInlineRunPropertiesPlugin.js | 10 ++++++--- ...calculateInlineRunPropertiesPlugin.test.js | 21 ++++++++++++------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/packages/super-editor/src/extensions/run/calculateInlineRunPropertiesPlugin.js b/packages/super-editor/src/extensions/run/calculateInlineRunPropertiesPlugin.js index 145e968d47..08a5057668 100644 --- a/packages/super-editor/src/extensions/run/calculateInlineRunPropertiesPlugin.js +++ b/packages/super-editor/src/extensions/run/calculateInlineRunPropertiesPlugin.js @@ -57,12 +57,18 @@ export const calculateInlineRunPropertiesPlugin = (editor) => const $pos = tr.doc.resolve(pos); let paragraphNode = null; + let child = runNode; + let isFirstInParagraph = $pos.parent.firstChild === child; + let paragraphPos; for (let depth = $pos.depth; depth >= 0; depth--) { const node = $pos.node(depth); + isFirstInParagraph = node.firstChild === child && isFirstInParagraph; if (node.type.name === 'paragraph') { paragraphNode = node; + paragraphPos = $pos.before(depth); break; } + child = node; } if (!paragraphNode) return; @@ -84,13 +90,11 @@ export const calculateInlineRunPropertiesPlugin = (editor) => const inlineRunProperties = getInlineRunProperties(runPropertiesFromMarks, runPropertiesFromStyles); const runProperties = Object.keys(inlineRunProperties).length ? inlineRunProperties : null; - const isFirstInParagraph = $pos.parent.firstChild === runNode && $pos.parent.type === paragraphNode.type; - if (isFirstInParagraph) { // Keep paragraph's default runProperties in sync for the first run const inlineParagraphProperties = carbonCopy(paragraphNode.attrs.paragraphProperties) || {}; inlineParagraphProperties.runProperties = runProperties; - tr.setNodeMarkup($pos.before(), paragraphNode.type, { + tr.setNodeMarkup(paragraphPos, paragraphNode.type, { ...paragraphNode.attrs, paragraphProperties: inlineParagraphProperties, }); diff --git a/packages/super-editor/src/extensions/run/calculateInlineRunPropertiesPlugin.test.js b/packages/super-editor/src/extensions/run/calculateInlineRunPropertiesPlugin.test.js index 51a78f5dd7..6bdb7c32f7 100644 --- a/packages/super-editor/src/extensions/run/calculateInlineRunPropertiesPlugin.test.js +++ b/packages/super-editor/src/extensions/run/calculateInlineRunPropertiesPlugin.test.js @@ -40,6 +40,11 @@ const makeSchema = () => runProperties: { default: null }, }, }, + bookmarkStart: { + inline: true, + group: 'inline', + content: 'inline*', + }, text: { group: 'inline' }, }, marks: { @@ -173,23 +178,23 @@ describe('calculateInlineRunPropertiesPlugin', () => { expect(paragraph.attrs.paragraphProperties).toEqual({ runProperties: { bold: true } }); }); - it('does not treat nested runs as the paragraph first run', () => { + it('treats the first run inside inline wrappers as the paragraph first run', () => { const schema = makeSchema(); const doc = schema.node('doc', null, [ - schema.node('paragraph', null, [schema.node('run', null, [schema.node('run', null, schema.text('Nested'))])]), + schema.node('paragraph', null, [ + schema.node('bookmarkStart', null, [schema.node('run', null, schema.text('Wrapped'))]), + ]), ]); const state = createState(schema, doc); - const [, innerRunPos] = runPositions(state.doc); - const from = innerRunPos + 1; - const to = innerRunPos + 3; + const [wrappedRunPos] = runPositions(state.doc); + const from = wrappedRunPos + 1; + const to = wrappedRunPos + 3; const tr = state.tr.addMark(from, to, schema.marks.bold.create()); const { state: nextState } = state.applyTransaction(tr); const paragraph = nextState.doc.firstChild; - expect(paragraph.attrs.paragraphProperties?.runProperties?.bold).toBeUndefined(); - const innerRun = nextState.doc.nodeAt(innerRunPos); - expect(innerRun?.attrs.runProperties).toEqual({ bold: true }); + expect(paragraph.attrs.paragraphProperties).toEqual({ runProperties: { bold: true } }); }); it('does not update paragraph runProperties when a non-first run changes', () => { From c63432367802d196b7ac5dd26a97753fbdc75719 Mon Sep 17 00:00:00 2001 From: Luccas Correa Date: Thu, 5 Feb 2026 11:17:55 -0300 Subject: [PATCH 3/4] fix: dependency configuration for visual tests --- devtools/visual-testing/packages/test-helpers/package.json | 3 +++ pnpm-lock.yaml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/devtools/visual-testing/packages/test-helpers/package.json b/devtools/visual-testing/packages/test-helpers/package.json index ad526fa6f7..bfba8ad21b 100644 --- a/devtools/visual-testing/packages/test-helpers/package.json +++ b/devtools/visual-testing/packages/test-helpers/package.json @@ -16,5 +16,8 @@ }, "devDependencies": { "eslint_d": "^14.3.0" + }, + "dependencies": { + "@superdoc-testing/harness": "workspace:*" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 19a1579a28..97f72a4e86 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -510,6 +510,9 @@ importers: '@playwright/test': specifier: '>=1.40.0' version: 1.58.1 + '@superdoc-testing/harness': + specifier: workspace:* + version: link:../harness devDependencies: eslint_d: specifier: ^14.3.0 From 674b4168e4971a87edda338bbfb4036f1a7a75f5 Mon Sep 17 00:00:00 2001 From: Luccas Correa Date: Thu, 5 Feb 2026 11:20:47 -0300 Subject: [PATCH 4/4] test: add visual test for applying font to text --- .../stories/formatting/apply-font.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 devtools/visual-testing/tests/interactions/stories/formatting/apply-font.ts diff --git a/devtools/visual-testing/tests/interactions/stories/formatting/apply-font.ts b/devtools/visual-testing/tests/interactions/stories/formatting/apply-font.ts new file mode 100644 index 0000000000..b210b450f4 --- /dev/null +++ b/devtools/visual-testing/tests/interactions/stories/formatting/apply-font.ts @@ -0,0 +1,28 @@ +import { defineStory } from '@superdoc-testing/helpers'; + +const WAIT_MS = 300; +const START_DOC = 'other/sd-1778-apply-font.docx'; +const FONT_NAME = 'Courier New'; + +export default defineStory({ + name: 'apply-font', + description: 'Select all content and apply the Courier New font.', + tickets: ['SD-1778'], + startDocument: START_DOC, + layout: true, + toolbar: 'full', + waitForFonts: true, + + async run(_, helpers): Promise { + const { selectAll, focus, executeCommand, waitForStable, milestone } = helpers; + await waitForStable(WAIT_MS); + await focus(); + + await selectAll(); + + await waitForStable(WAIT_MS); + await executeCommand('setFontFamily', FONT_NAME); + await waitForStable(WAIT_MS); + await milestone('font-applied', `Applied ${FONT_NAME} to document.`); + }, +});