Skip to content

Commit 4cf6d78

Browse files
feat(fields): add SECTIONPAGES field support with section-aware numbering (SD-3349 and SD-3027) (#3605)
1 parent 5c111fd commit 4cf6d78

100 files changed

Lines changed: 4788 additions & 186 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/docs/document-api/reference/_generated-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1078,5 +1078,5 @@
10781078
}
10791079
],
10801080
"marker": "{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */}",
1081-
"sourceHash": "5f439c4117bbb2e55f227e7711df415c1173f8c476954c6e412a4b8b45edd1a3"
1081+
"sourceHash": "fc2e513626b5bfc6383d968b9bffdcea0f086469bf867a588dd7319c8b75c7de"
10821082
}

apps/docs/document-api/reference/index.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ The tables below are grouped by namespace.
116116
| <span style={{ whiteSpace: 'nowrap', wordBreak: 'normal', overflowWrap: 'normal' }}><a href="/document-api/reference/sections/set-page-setup"><code>sections.setPageSetup</code></a></span> | <span style={{ whiteSpace: 'nowrap', wordBreak: 'normal', overflowWrap: 'normal' }}><code>editor.doc.sections.setPageSetup(...)</code></span> | Set page size/orientation properties for a section. |
117117
| <span style={{ whiteSpace: 'nowrap', wordBreak: 'normal', overflowWrap: 'normal' }}><a href="/document-api/reference/sections/set-columns"><code>sections.setColumns</code></a></span> | <span style={{ whiteSpace: 'nowrap', wordBreak: 'normal', overflowWrap: 'normal' }}><code>editor.doc.sections.setColumns(...)</code></span> | Set column configuration for a section. |
118118
| <span style={{ whiteSpace: 'nowrap', wordBreak: 'normal', overflowWrap: 'normal' }}><a href="/document-api/reference/sections/set-line-numbering"><code>sections.setLineNumbering</code></a></span> | <span style={{ whiteSpace: 'nowrap', wordBreak: 'normal', overflowWrap: 'normal' }}><code>editor.doc.sections.setLineNumbering(...)</code></span> | Enable or configure line numbering for a section. |
119-
| <span style={{ whiteSpace: 'nowrap', wordBreak: 'normal', overflowWrap: 'normal' }}><a href="/document-api/reference/sections/set-page-numbering"><code>sections.setPageNumbering</code></a></span> | <span style={{ whiteSpace: 'nowrap', wordBreak: 'normal', overflowWrap: 'normal' }}><code>editor.doc.sections.setPageNumbering(...)</code></span> | Set page numbering format/start for a section. |
119+
| <span style={{ whiteSpace: 'nowrap', wordBreak: 'normal', overflowWrap: 'normal' }}><a href="/document-api/reference/sections/set-page-numbering"><code>sections.setPageNumbering</code></a></span> | <span style={{ whiteSpace: 'nowrap', wordBreak: 'normal', overflowWrap: 'normal' }}><code>editor.doc.sections.setPageNumbering(...)</code></span> | Set page numbering format/start and chapter numbering settings for a section. |
120120
| <span style={{ whiteSpace: 'nowrap', wordBreak: 'normal', overflowWrap: 'normal' }}><a href="/document-api/reference/sections/set-title-page"><code>sections.setTitlePage</code></a></span> | <span style={{ whiteSpace: 'nowrap', wordBreak: 'normal', overflowWrap: 'normal' }}><code>editor.doc.sections.setTitlePage(...)</code></span> | Enable or disable title-page behavior for a section. |
121121
| <span style={{ whiteSpace: 'nowrap', wordBreak: 'normal', overflowWrap: 'normal' }}><a href="/document-api/reference/sections/set-odd-even-headers-footers"><code>sections.setOddEvenHeadersFooters</code></a></span> | <span style={{ whiteSpace: 'nowrap', wordBreak: 'normal', overflowWrap: 'normal' }}><code>editor.doc.sections.setOddEvenHeadersFooters(...)</code></span> | Enable or disable odd/even header-footer mode in document settings. |
122122
| <span style={{ whiteSpace: 'nowrap', wordBreak: 'normal', overflowWrap: 'normal' }}><a href="/document-api/reference/sections/set-vertical-align"><code>sections.setVerticalAlign</code></a></span> | <span style={{ whiteSpace: 'nowrap', wordBreak: 'normal', overflowWrap: 'normal' }}><code>editor.doc.sections.setVerticalAlign(...)</code></span> | Set vertical page alignment for a section. |

apps/docs/document-api/reference/sections/get.mdx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ Returns a SectionInfo object with full section properties including margins, col
111111
| `pageBorders.top.style` | string | no | |
112112
| `pageBorders.zOrder` | enum | no | `"front"`, `"back"` |
113113
| `pageNumbering` | object | no | |
114+
| `pageNumbering.chapterSeparator` | enum | no | `"hyphen"`, `"period"`, `"colon"`, `"emDash"`, `"enDash"` |
115+
| `pageNumbering.chapterStyle` | integer | no | |
114116
| `pageNumbering.format` | enum | no | `"decimal"`, `"lowerLetter"`, `"upperLetter"`, `"lowerRoman"`, `"upperRoman"`, `"numberInDash"` |
115117
| `pageNumbering.start` | integer | no | |
116118
| `pageSetup` | object | no | |
@@ -614,6 +616,20 @@ Returns a SectionInfo object with full section properties including margins, col
614616
"pageNumbering": {
615617
"additionalProperties": false,
616618
"properties": {
619+
"chapterSeparator": {
620+
"enum": [
621+
"hyphen",
622+
"period",
623+
"colon",
624+
"emDash",
625+
"enDash"
626+
],
627+
"type": "string"
628+
},
629+
"chapterStyle": {
630+
"minimum": 1,
631+
"type": "integer"
632+
},
617633
"format": {
618634
"enum": [
619635
"decimal",

apps/docs/document-api/reference/sections/list.mdx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,20 @@ Returns a SectionsListResult with an ordered array of section summaries and thei
586586
"pageNumbering": {
587587
"additionalProperties": false,
588588
"properties": {
589+
"chapterSeparator": {
590+
"enum": [
591+
"hyphen",
592+
"period",
593+
"colon",
594+
"emDash",
595+
"enDash"
596+
],
597+
"type": "string"
598+
},
599+
"chapterStyle": {
600+
"minimum": 1,
601+
"type": "integer"
602+
},
589603
"format": {
590604
"enum": [
591605
"decimal",

apps/docs/document-api/reference/sections/set-page-numbering.mdx

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
---
22
title: sections.setPageNumbering
33
sidebarTitle: sections.setPageNumbering
4-
description: Set page numbering format/start for a section.
4+
description: Set page numbering format/start and chapter numbering settings for a section.
55
---
66

77
{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */}
88

99
## Summary
1010

11-
Set page numbering format/start for a section.
11+
Set page numbering format/start and chapter numbering settings for a section.
1212

1313
- Operation ID: `sections.setPageNumbering`
1414
- API member path: `editor.doc.sections.setPageNumbering(...)`
@@ -20,7 +20,7 @@ Set page numbering format/start for a section.
2020

2121
## Expected result
2222

23-
Returns a SectionMutationResult receipt; reports NO_OP if page numbering format already matches.
23+
Returns a SectionMutationResult receipt; reports NO_OP if page numbering settings already match.
2424

2525
## Input fields
2626

@@ -42,6 +42,24 @@ Returns a SectionMutationResult receipt; reports NO_OP if page numbering format
4242
| `target.kind` | `"section"` | yes | Constant: `"section"` |
4343
| `target.sectionId` | string | yes | |
4444

45+
### Variant 3 (target.kind="section")
46+
47+
| Field | Type | Required | Description |
48+
| --- | --- | --- | --- |
49+
| `chapterStyle` | integer | yes | |
50+
| `target` | SectionAddress | yes | SectionAddress |
51+
| `target.kind` | `"section"` | yes | Constant: `"section"` |
52+
| `target.sectionId` | string | yes | |
53+
54+
### Variant 4 (target.kind="section")
55+
56+
| Field | Type | Required | Description |
57+
| --- | --- | --- | --- |
58+
| `chapterSeparator` | enum | yes | `"hyphen"`, `"period"`, `"colon"`, `"emDash"`, `"enDash"` |
59+
| `target` | SectionAddress | yes | SectionAddress |
60+
| `target.kind` | `"section"` | yes | Constant: `"section"` |
61+
| `target.sectionId` | string | yes | |
62+
4563
### Example request
4664

4765
```json
@@ -107,7 +125,7 @@ Returns a SectionMutationResult receipt; reports NO_OP if page numbering format
107125
```json
108126
{
109127
"additionalProperties": false,
110-
"oneOf": [
128+
"anyOf": [
111129
{
112130
"required": [
113131
"target",
@@ -119,9 +137,35 @@ Returns a SectionMutationResult receipt; reports NO_OP if page numbering format
119137
"target",
120138
"format"
121139
]
140+
},
141+
{
142+
"required": [
143+
"target",
144+
"chapterStyle"
145+
]
146+
},
147+
{
148+
"required": [
149+
"target",
150+
"chapterSeparator"
151+
]
122152
}
123153
],
124154
"properties": {
155+
"chapterSeparator": {
156+
"enum": [
157+
"hyphen",
158+
"period",
159+
"colon",
160+
"emDash",
161+
"enDash"
162+
],
163+
"type": "string"
164+
},
165+
"chapterStyle": {
166+
"minimum": 1,
167+
"type": "integer"
168+
},
125169
"format": {
126170
"enum": [
127171
"decimal",

packages/document-api/src/contract/operation-definitions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,8 +1259,8 @@ export const OPERATION_DEFINITIONS = {
12591259
},
12601260
'sections.setPageNumbering': {
12611261
memberPath: 'sections.setPageNumbering',
1262-
description: 'Set page numbering format/start for a section.',
1263-
expectedResult: 'Returns a SectionMutationResult receipt; reports NO_OP if page numbering format already matches.',
1262+
description: 'Set page numbering format/start and chapter numbering settings for a section.',
1263+
expectedResult: 'Returns a SectionMutationResult receipt; reports NO_OP if page numbering settings already match.',
12641264
requiresDocumentContext: true,
12651265
metadata: mutationOperation({
12661266
idempotency: 'conditional',

packages/document-api/src/contract/schemas.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1281,6 +1281,8 @@ const sectionLineNumberingSchema = objectSchema(
12811281
const sectionPageNumberingSchema = objectSchema({
12821282
start: { type: 'integer', minimum: 1 },
12831283
format: sectionPageNumberFormatSchema,
1284+
chapterStyle: { type: 'integer', minimum: 1 },
1285+
chapterSeparator: { type: 'string', enum: ['hyphen', 'period', 'colon', 'emDash', 'enDash'] },
12841286
});
12851287

12861288
const sectionHeaderFooterRefsSchema = objectSchema({
@@ -4018,10 +4020,17 @@ const operationSchemas: Record<OperationId, OperationSchemaSet> = {
40184020
target: sectionAddressSchema,
40194021
start: { type: 'integer', minimum: 1 },
40204022
format: sectionPageNumberFormatSchema,
4023+
chapterStyle: { type: 'integer', minimum: 1 },
4024+
chapterSeparator: { type: 'string', enum: ['hyphen', 'period', 'colon', 'emDash', 'enDash'] },
40214025
},
40224026
['target'],
40234027
),
4024-
oneOf: [{ required: ['target', 'start'] }, { required: ['target', 'format'] }],
4028+
anyOf: [
4029+
{ required: ['target', 'start'] },
4030+
{ required: ['target', 'format'] },
4031+
{ required: ['target', 'chapterStyle'] },
4032+
{ required: ['target', 'chapterSeparator'] },
4033+
],
40254034
},
40264035
output: sectionMutationResultSchemaFor('sections.setPageNumbering'),
40274036
success: sectionMutationSuccessSchema,

packages/document-api/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1429,6 +1429,7 @@ export type {
14291429
SectionPageBorders,
14301430
SectionPageMargins,
14311431
SectionPageNumbering,
1432+
SectionPageNumberingChapterSeparator,
14321433
SectionPageNumberingFormat,
14331434
SectionPageSetup,
14341435
SectionRangeDomain,

packages/document-api/src/sections/sections.test.ts

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,57 @@ describe('sections API validation', () => {
105105
executeSectionsSetPageNumbering(adapter, {
106106
target: { kind: 'section', sectionId: 'section-0' },
107107
}),
108-
).toThrow(/requires at least one of start or format/i);
108+
).toThrow(/requires at least one of start, format, chapterStyle, or chapterSeparator/i);
109+
});
110+
111+
it('accepts chapterStyle for setPageNumbering', () => {
112+
const setPageNumbering = mock(makeAdapter().setPageNumbering);
113+
const adapter = makeAdapter({ setPageNumbering });
114+
115+
executeSectionsSetPageNumbering(adapter, {
116+
target: { kind: 'section', sectionId: 'section-0' },
117+
chapterStyle: 1,
118+
});
119+
120+
expect(setPageNumbering).toHaveBeenCalledWith(
121+
{ target: { kind: 'section', sectionId: 'section-0' }, chapterStyle: 1 },
122+
{ changeMode: 'direct', dryRun: false, expectedRevision: undefined },
123+
);
124+
});
125+
126+
it('accepts valid chapterSeparator for setPageNumbering', () => {
127+
const setPageNumbering = mock(makeAdapter().setPageNumbering);
128+
const adapter = makeAdapter({ setPageNumbering });
129+
130+
executeSectionsSetPageNumbering(adapter, {
131+
target: { kind: 'section', sectionId: 'section-0' },
132+
chapterSeparator: 'enDash',
133+
});
134+
135+
expect(setPageNumbering).toHaveBeenCalledWith(
136+
{ target: { kind: 'section', sectionId: 'section-0' }, chapterSeparator: 'enDash' },
137+
{ changeMode: 'direct', dryRun: false, expectedRevision: undefined },
138+
);
139+
});
140+
141+
it('rejects invalid chapterSeparator for setPageNumbering', () => {
142+
const adapter = makeAdapter();
143+
expect(() =>
144+
executeSectionsSetPageNumbering(adapter, {
145+
target: { kind: 'section', sectionId: 'section-0' },
146+
chapterSeparator: 'slash' as any,
147+
}),
148+
).toThrow(/chapterSeparator/i);
149+
});
150+
151+
it('rejects chapterStyle less than 1 for setPageNumbering', () => {
152+
const adapter = makeAdapter();
153+
expect(() =>
154+
executeSectionsSetPageNumbering(adapter, {
155+
target: { kind: 'section', sectionId: 'section-0' },
156+
chapterStyle: 0,
157+
}),
158+
).toThrow(/chapterStyle/i);
109159
});
110160

111161
it('requires at least one field for setPageBorders', () => {

packages/document-api/src/sections/sections.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
SectionHeaderFooterVariant,
1111
SectionDirection,
1212
SectionOrientation,
13+
SectionPageNumberingChapterSeparator,
1314
SectionVerticalAlign,
1415
SectionsClearHeaderFooterRefInput,
1516
SectionsClearPageBordersInput,
@@ -41,6 +42,7 @@ export type {
4142
SectionBreakType,
4243
SectionHeaderFooterKind,
4344
SectionHeaderFooterVariant,
45+
SectionPageNumberingChapterSeparator,
4446
SectionsClearHeaderFooterRefInput,
4547
SectionsClearPageBordersInput,
4648
SectionsGetInput,
@@ -81,6 +83,13 @@ const PAGE_NUMBER_FORMATS = [
8183
'upperRoman',
8284
'numberInDash',
8385
] as const;
86+
const PAGE_NUMBER_CHAPTER_SEPARATORS: readonly SectionPageNumberingChapterSeparator[] = [
87+
'hyphen',
88+
'period',
89+
'colon',
90+
'emDash',
91+
'enDash',
92+
] as const;
8493
const PAGE_BORDER_DISPLAYS = ['allPages', 'firstPage', 'notFirstPage'] as const;
8594
const PAGE_BORDER_OFFSET_FROM_VALUES = ['page', 'text'] as const;
8695
const PAGE_BORDER_Z_ORDER_VALUES = ['front', 'back'] as const;
@@ -390,17 +399,25 @@ export function executeSectionsSetPageNumbering(
390399
options?: MutationOptions,
391400
): SectionMutationResult {
392401
assertSectionTarget(input, 'sections.setPageNumbering');
393-
if (!hasAnyDefined(input as unknown as Record<string, unknown>, ['start', 'format'])) {
402+
if (
403+
!hasAnyDefined(input as unknown as Record<string, unknown>, ['start', 'format', 'chapterStyle', 'chapterSeparator'])
404+
) {
394405
throw new DocumentApiValidationError(
395406
'INVALID_INPUT',
396-
'sections.setPageNumbering requires at least one of start or format.',
407+
'sections.setPageNumbering requires at least one of start, format, chapterStyle, or chapterSeparator.',
397408
);
398409
}
399410

400411
if (input.start !== undefined) assertPositiveInteger(input.start, 'sections.setPageNumbering.start');
401412
if (input.format !== undefined) {
402413
assertOneOf(input.format, 'sections.setPageNumbering.format', PAGE_NUMBER_FORMATS);
403414
}
415+
if (input.chapterStyle !== undefined) {
416+
assertPositiveInteger(input.chapterStyle, 'sections.setPageNumbering.chapterStyle');
417+
}
418+
if (input.chapterSeparator !== undefined) {
419+
assertOneOf(input.chapterSeparator, 'sections.setPageNumbering.chapterSeparator', PAGE_NUMBER_CHAPTER_SEPARATORS);
420+
}
404421

405422
return adapter.setPageNumbering(input, normalizeMutationOptions(options));
406423
}

0 commit comments

Comments
 (0)