Skip to content
Open
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
83 changes: 25 additions & 58 deletions packages/super-editor/src/core/super-converter/styles.d.ts
Original file line number Diff line number Diff line change
@@ -1,66 +1,33 @@
/**
* Resolves run properties from styles chain
*/
export function resolveRunProperties(
params: unknown,
inlineRpr: unknown,
resolvedPpr: unknown,
isListNumber?: boolean,
numberingDefinedInline?: boolean,
): unknown;
import type { ParagraphProperties, ParagraphSpacing, RunProperties } from '@superdoc/style-engine/ooxml';

/**
* Resolves paragraph properties from styles chain
*/
export function resolveParagraphProperties(
params: unknown,
inlineProps: unknown,
insideTable?: boolean,
overrideInlineStyleId?: boolean,
tableStyleId?: string | null,
): unknown;
export { combineRunProperties, resolveParagraphProperties, resolveRunProperties } from '@superdoc/style-engine/ooxml';

/**
* Gets default properties for a translator
*/
export function getDefaultProperties(params: unknown, translator: unknown): unknown;
export interface ConverterMarkLike {
attrs: Record<string, unknown>;
type: string | { name?: string };
}

/**
* Gets style properties by style ID
*/
export function getStyleProperties(
params: unknown,
styleId: string,
translator: unknown,
): { properties: unknown; isDefault: boolean };
export interface ConverterMarkDefinition {
attrs: Record<string, unknown>;
type: string;
}

/**
* Gets numbering properties
*/
export function getNumberingProperties(
params: unknown,
ilvl: number,
numId: number,
translator: unknown,
tries?: number,
): unknown;
export function encodeMarksFromRPr(
runProperties: RunProperties,
docx: Record<string, unknown> | null | undefined,
): ConverterMarkDefinition[];

/**
* Encodes marks from run properties
*/
export function encodeMarksFromRPr(runProperties: unknown, docx: unknown): unknown;
export function encodeCSSFromPPr(
paragraphProperties: ParagraphProperties | null | undefined,
hasPreviousParagraph?: boolean,
nextParagraphProps?: ParagraphProperties | null,
): Record<string, string>;

/**
* Encodes CSS from paragraph properties
*/
export function encodeCSSFromPPr(paragraphProperties: unknown): unknown;
export function encodeCSSFromRPr(
runProperties: RunProperties | null | undefined,
docx: Record<string, unknown> | null | undefined,
): Record<string, string>;

/**
* Encodes CSS from run properties
*/
export function encodeCSSFromRPr(runProperties: unknown, docx: unknown): unknown;
export function decodeRPrFromMarks(marks: ConverterMarkLike[] | null | undefined): RunProperties;

/**
* Decodes run properties from marks
*/
export function decodeRPrFromMarks(marks: unknown): unknown;
export function getSpacingStyle(spacing: ParagraphSpacing, isListItem?: boolean): Record<string, string>;
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,11 @@ export const handleParagraphNode = (params) => {
const childParams = {
...params,
nodes: updatedElements,
extraParams: { ...params.extraParams, paragraphProperties: resolvedParagraphProperties },
extraParams: {
...params.extraParams,
paragraphProperties: resolvedParagraphProperties,
numberingDefinedInline: Boolean(inlineParagraphProperties.numberingProperties),
},
path: [...(params.path || []), node],
};
const translatedChildren = nodeListHandler.handler(childParams);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,65 @@ describe('r-translator integration with w:b (marks-only import, bold inline)', a
});
});
});

describe('r-translator integration with table style run properties', () => {
const runNode = {
name: 'w:r',
elements: [{ name: 'w:t', elements: [{ type: 'text', text: 'Cell text' }] }],
};

const nodeListHandler = defaultNodeListHandler();
const sharedParams = {
translatedNumbering: {},
translatedLinkedStyles: {
docDefaults: { runProperties: {} },
styles: {
TableBold: {
runProperties: { bold: true },
},
},
},
};

it('applies bold mark from table style when table context is provided', () => {
const encoded = r_translator.encode({
nodes: [runNode],
nodeListHandler,
docx: {},
...sharedParams,
extraParams: {
paragraphProperties: {},
rowIndex: 0,
columnIndex: 0,
tableProperties: { tableStyleId: 'TableBold' },
totalColumns: 1,
totalRows: 1,
},
});

const textChild = encoded?.content?.find((child) => child?.type === 'text');
expect(textChild).toBeTruthy();
expect((textChild.marks || []).some((mark) => mark.type === 'bold')).toBe(true);
});

it('does not apply table-style bold mark without complete table context', () => {
const encoded = r_translator.encode({
nodes: [runNode],
nodeListHandler,
docx: {},
...sharedParams,
extraParams: {
paragraphProperties: {},
rowIndex: 0,
columnIndex: 0,
tableProperties: { tableStyleId: 'TableBold' },
totalColumns: 1,
// totalRows missing on purpose
},
});

const textChild = encoded?.content?.find((child) => child?.type === 'text');
expect(textChild).toBeTruthy();
expect((textChild.marks || []).some((mark) => mark.type === 'bold')).toBe(false);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,30 @@ const encode = (params, encodedAttrs = {}) => {

// Resolving run properties following style hierarchy
const paragraphProperties = params?.extraParams?.paragraphProperties || {};
const resolvedRunProperties = resolveRunProperties(params, runProperties ?? {}, paragraphProperties);
let tableInfo = null;
if (
params?.extraParams?.rowIndex != null &&
params?.extraParams?.columnIndex != null &&
params?.extraParams?.tableProperties != null &&
params?.extraParams?.totalColumns != null &&
params?.extraParams?.totalRows != null
) {
tableInfo = {
rowIndex: params.extraParams.rowIndex,
cellIndex: params.extraParams.columnIndex,
tableProperties: params.extraParams.tableProperties,
numCells: params.extraParams.totalColumns,
numRows: params.extraParams.totalRows,
};
}
const resolvedRunProperties = resolveRunProperties(
params,
runProperties ?? {},
paragraphProperties,
tableInfo,
false,
params?.extraParams?.numberingDefinedInline,
);

// Parsing marks from run properties
const marksResult = encodeMarksFromRPr(resolvedRunProperties, params?.docx);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, it, expect, vi } from 'vitest';
import { translator, config } from './r-translator.js';
import * as converterStyles from '../../../../styles.js';

describe('w:r r-translator (node)', () => {
it('exposes correct metadata', () => {
Expand Down Expand Up @@ -153,6 +154,81 @@ describe('w:r r-translator (node)', () => {
expect(child.attrs).toEqual({ originalName: 'w:custom' });
});

it('passes tableInfo and numberingDefinedInline to resolveRunProperties when table context is available', () => {
const resolveRunPropertiesSpy = vi
.spyOn(converterStyles, 'resolveRunProperties')
.mockImplementation(() => ({ bold: true }));
const runNode = {
name: 'w:r',
elements: [{ name: 'w:t', elements: [{ type: 'text', text: 'Cell' }] }],
};

const params = {
nodes: [runNode],
nodeListHandler: { handler: vi.fn(() => [{ type: 'text', text: 'Cell', marks: [] }]) },
docx: {},
extraParams: {
paragraphProperties: { styleId: 'ListParagraph' },
rowIndex: 2,
columnIndex: 1,
tableProperties: { tableStyleId: 'TableGrid' },
totalColumns: 3,
totalRows: 4,
numberingDefinedInline: true,
},
};

translator.encode(params);

expect(resolveRunPropertiesSpy).toHaveBeenCalledTimes(1);
expect(resolveRunPropertiesSpy).toHaveBeenCalledWith(
params,
{},
{ styleId: 'ListParagraph' },
{
rowIndex: 2,
cellIndex: 1,
tableProperties: { tableStyleId: 'TableGrid' },
numCells: 3,
numRows: 4,
},
false,
true,
);

resolveRunPropertiesSpy.mockRestore();
});

it('passes null tableInfo to resolveRunProperties when table context is incomplete', () => {
const resolveRunPropertiesSpy = vi.spyOn(converterStyles, 'resolveRunProperties').mockImplementation(() => ({}));
const runNode = {
name: 'w:r',
elements: [{ name: 'w:t', elements: [{ type: 'text', text: 'No table context' }] }],
};

const params = {
nodes: [runNode],
nodeListHandler: { handler: vi.fn(() => [{ type: 'text', text: 'No table context', marks: [] }]) },
docx: {},
extraParams: {
paragraphProperties: { styleId: 'Normal' },
rowIndex: 0,
columnIndex: 0,
tableProperties: { tableStyleId: 'TableGrid' },
totalColumns: 2,
// totalRows missing on purpose
},
};

translator.encode(params);

expect(resolveRunPropertiesSpy).toHaveBeenCalledTimes(1);
expect(resolveRunPropertiesSpy.mock.calls[0][3]).toBeNull();
expect(resolveRunPropertiesSpy.mock.calls[0][5]).toBeUndefined();

resolveRunPropertiesSpy.mockRestore();
});

it('does not wrap a comment range start and end in a run node', () => {
const params = {
node: {
Expand Down
Loading
Loading