From 9e4a95d8a65fb263d81ed13ba041c4bc5564ce41 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 23 Jan 2026 09:35:29 +0000 Subject: [PATCH 1/3] Initial plan From 6df00ca7ee66f39023489f4dc454012c3aed526a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 23 Jan 2026 09:39:21 +0000 Subject: [PATCH 2/3] Enhance FormSectionSchema with flexible form layout capabilities Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- .../references/ui/view_config/FormSection.mdx | 7 +- packages/spec/json-schema/FormSection.json | 23 +++-- packages/spec/json-schema/FormView.json | 46 ++++++---- packages/spec/json-schema/View.json | 92 +++++++++++-------- packages/spec/src/ui/view.test.ts | 42 +++++++-- packages/spec/src/ui/view.zod.ts | 9 +- 6 files changed, 133 insertions(+), 86 deletions(-) diff --git a/content/docs/references/ui/view_config/FormSection.mdx b/content/docs/references/ui/view_config/FormSection.mdx index 801c98338b..dda7d15e00 100644 --- a/content/docs/references/ui/view_config/FormSection.mdx +++ b/content/docs/references/ui/view_config/FormSection.mdx @@ -7,8 +7,9 @@ description: FormSection Schema Reference | Property | Type | Required | Description | | :--- | :--- | :--- | :--- | -| **label** | `string` | optional | | +| **title** | `string` | optional | Section title | +| **label** | `string` | optional | Legacy: use title instead | | **collapsible** | `boolean` | optional | | | **collapsed** | `boolean` | optional | | -| **columns** | `Enum<'1' \| '2' \| '3' \| '4'>` | optional | | -| **fields** | `string[]` | ✅ | | +| **columns** | `number` | optional | Number of columns (1-4) | +| **fields** | `string[]` | ✅ | Field names to include in this section | diff --git a/packages/spec/json-schema/FormSection.json b/packages/spec/json-schema/FormSection.json index bfc9a6d937..73cef2e75e 100644 --- a/packages/spec/json-schema/FormSection.json +++ b/packages/spec/json-schema/FormSection.json @@ -4,8 +4,13 @@ "FormSection": { "type": "object", "properties": { + "title": { + "type": "string", + "description": "Section title" + }, "label": { - "type": "string" + "type": "string", + "description": "Legacy: use title instead" }, "collapsible": { "type": "boolean", @@ -16,20 +21,18 @@ "default": false }, "columns": { - "type": "string", - "enum": [ - "1", - "2", - "3", - "4" - ], - "default": "2" + "type": "number", + "minimum": 1, + "maximum": 4, + "default": 2, + "description": "Number of columns (1-4)" }, "fields": { "type": "array", "items": { "type": "string" - } + }, + "description": "Field names to include in this section" } }, "required": [ diff --git a/packages/spec/json-schema/FormView.json b/packages/spec/json-schema/FormView.json index 72e606494a..73d6594a1a 100644 --- a/packages/spec/json-schema/FormView.json +++ b/packages/spec/json-schema/FormView.json @@ -18,8 +18,13 @@ "items": { "type": "object", "properties": { + "title": { + "type": "string", + "description": "Section title" + }, "label": { - "type": "string" + "type": "string", + "description": "Legacy: use title instead" }, "collapsible": { "type": "boolean", @@ -30,20 +35,18 @@ "default": false }, "columns": { - "type": "string", - "enum": [ - "1", - "2", - "3", - "4" - ], - "default": "2" + "type": "number", + "minimum": 1, + "maximum": 4, + "default": 2, + "description": "Number of columns (1-4)" }, "fields": { "type": "array", "items": { "type": "string" - } + }, + "description": "Field names to include in this section" } }, "required": [ @@ -57,8 +60,13 @@ "items": { "type": "object", "properties": { + "title": { + "type": "string", + "description": "Section title" + }, "label": { - "type": "string" + "type": "string", + "description": "Legacy: use title instead" }, "collapsible": { "type": "boolean", @@ -69,20 +77,18 @@ "default": false }, "columns": { - "type": "string", - "enum": [ - "1", - "2", - "3", - "4" - ], - "default": "2" + "type": "number", + "minimum": 1, + "maximum": 4, + "default": 2, + "description": "Number of columns (1-4)" }, "fields": { "type": "array", "items": { "type": "string" - } + }, + "description": "Field names to include in this section" } }, "required": [ diff --git a/packages/spec/json-schema/View.json b/packages/spec/json-schema/View.json index 1a30b5fd76..90d3d23695 100644 --- a/packages/spec/json-schema/View.json +++ b/packages/spec/json-schema/View.json @@ -168,8 +168,13 @@ "items": { "type": "object", "properties": { + "title": { + "type": "string", + "description": "Section title" + }, "label": { - "type": "string" + "type": "string", + "description": "Legacy: use title instead" }, "collapsible": { "type": "boolean", @@ -180,20 +185,18 @@ "default": false }, "columns": { - "type": "string", - "enum": [ - "1", - "2", - "3", - "4" - ], - "default": "2" + "type": "number", + "minimum": 1, + "maximum": 4, + "default": 2, + "description": "Number of columns (1-4)" }, "fields": { "type": "array", "items": { "type": "string" - } + }, + "description": "Field names to include in this section" } }, "required": [ @@ -207,8 +210,13 @@ "items": { "type": "object", "properties": { + "title": { + "type": "string", + "description": "Section title" + }, "label": { - "type": "string" + "type": "string", + "description": "Legacy: use title instead" }, "collapsible": { "type": "boolean", @@ -219,20 +227,18 @@ "default": false }, "columns": { - "type": "string", - "enum": [ - "1", - "2", - "3", - "4" - ], - "default": "2" + "type": "number", + "minimum": 1, + "maximum": 4, + "default": 2, + "description": "Number of columns (1-4)" }, "fields": { "type": "array", "items": { "type": "string" - } + }, + "description": "Field names to include in this section" } }, "required": [ @@ -414,8 +420,13 @@ "items": { "type": "object", "properties": { + "title": { + "type": "string", + "description": "Section title" + }, "label": { - "type": "string" + "type": "string", + "description": "Legacy: use title instead" }, "collapsible": { "type": "boolean", @@ -426,20 +437,18 @@ "default": false }, "columns": { - "type": "string", - "enum": [ - "1", - "2", - "3", - "4" - ], - "default": "2" + "type": "number", + "minimum": 1, + "maximum": 4, + "default": 2, + "description": "Number of columns (1-4)" }, "fields": { "type": "array", "items": { "type": "string" - } + }, + "description": "Field names to include in this section" } }, "required": [ @@ -453,8 +462,13 @@ "items": { "type": "object", "properties": { + "title": { + "type": "string", + "description": "Section title" + }, "label": { - "type": "string" + "type": "string", + "description": "Legacy: use title instead" }, "collapsible": { "type": "boolean", @@ -465,20 +479,18 @@ "default": false }, "columns": { - "type": "string", - "enum": [ - "1", - "2", - "3", - "4" - ], - "default": "2" + "type": "number", + "minimum": 1, + "maximum": 4, + "default": 2, + "description": "Number of columns (1-4)" }, "fields": { "type": "array", "items": { "type": "string" - } + }, + "description": "Field names to include in this section" } }, "required": [ diff --git a/packages/spec/src/ui/view.test.ts b/packages/spec/src/ui/view.test.ts index 104b157437..b9aaac2fb5 100644 --- a/packages/spec/src/ui/view.test.ts +++ b/packages/spec/src/ui/view.test.ts @@ -211,7 +211,7 @@ describe('FormSectionSchema', () => { label: 'Contact Information', collapsible: true, collapsed: false, - columns: '3' as const, + columns: 3, fields: ['first_name', 'last_name', 'email', 'phone'], }; @@ -219,17 +219,41 @@ describe('FormSectionSchema', () => { expect(result.columns).toBe(3); }); - it('should transform column strings to numbers', () => { - const columnOptions = ['1', '2', '3', '4'] as const; + it('should accept form section with title instead of label', () => { + const section = { + title: 'Contact Information', + collapsible: true, + columns: 3, + fields: ['first_name', 'last_name', 'email', 'phone'], + }; + + const result = FormSectionSchema.parse(section); + expect(result.title).toBe('Contact Information'); + expect(result.columns).toBe(3); + }); + + it('should validate columns min and max constraints', () => { + const validColumns = [1, 2, 3, 4]; - columnOptions.forEach(cols => { + validColumns.forEach(cols => { const section = { columns: cols, fields: ['field1'], }; const result = FormSectionSchema.parse(section); - expect(result.columns).toBe(parseInt(cols)); + expect(result.columns).toBe(cols); }); + + // Test invalid values + expect(() => FormSectionSchema.parse({ + columns: 0, + fields: ['field1'], + })).toThrow(); + + expect(() => FormSectionSchema.parse({ + columns: 5, + fields: ['field1'], + })).toThrow(); }); }); @@ -434,19 +458,19 @@ describe('ViewSchema', () => { sections: [ { label: 'Opportunity Details', - columns: '2', + columns: 2, fields: ['name', 'account_id', 'amount', 'stage', 'close_date', 'probability'], }, { label: 'Contact Information', - columns: '2', + columns: 2, fields: ['primary_contact', 'email', 'phone'], }, { label: 'Additional Information', collapsible: true, collapsed: true, - columns: '2', + columns: 2, fields: ['description', 'next_step', 'lead_source'], }, ], @@ -492,7 +516,7 @@ describe('ViewSchema', () => { }, { label: 'Schedule', - columns: '2', + columns: 2, fields: ['start_date', 'due_date', 'estimated_hours'], }, { diff --git a/packages/spec/src/ui/view.zod.ts b/packages/spec/src/ui/view.zod.ts index 8c6c0b35bd..7b58d79136 100644 --- a/packages/spec/src/ui/view.zod.ts +++ b/packages/spec/src/ui/view.zod.ts @@ -62,11 +62,12 @@ export const ListViewSchema = z.object({ * Form Layout Section */ export const FormSectionSchema = z.object({ - label: z.string().optional(), - collapsible: z.boolean().default(false), + title: z.string().optional().describe('Section title'), + label: z.string().optional().describe('Legacy: use title instead'), + collapsible: z.boolean().optional().default(false), collapsed: z.boolean().default(false), - columns: z.enum(['1', '2', '3', '4']).default('2').transform(val => parseInt(val) as 1 | 2 | 3 | 4), - fields: z.array(z.string()), // or complex FieldConfig + columns: z.number().min(1).max(4).default(2).describe('Number of columns (1-4)'), + fields: z.array(z.string()).describe('Field names to include in this section'), }); /** From ab7706e4550c032cc42e56fbdf118bb8d9f9d840 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 23 Jan 2026 09:40:32 +0000 Subject: [PATCH 3/3] Fix redundant optional modifier on collapsible field Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- packages/spec/src/ui/view.zod.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/spec/src/ui/view.zod.ts b/packages/spec/src/ui/view.zod.ts index 7b58d79136..bd84f0e8bd 100644 --- a/packages/spec/src/ui/view.zod.ts +++ b/packages/spec/src/ui/view.zod.ts @@ -64,7 +64,7 @@ export const ListViewSchema = z.object({ export const FormSectionSchema = z.object({ title: z.string().optional().describe('Section title'), label: z.string().optional().describe('Legacy: use title instead'), - collapsible: z.boolean().optional().default(false), + collapsible: z.boolean().default(false), collapsed: z.boolean().default(false), columns: z.number().min(1).max(4).default(2).describe('Number of columns (1-4)'), fields: z.array(z.string()).describe('Field names to include in this section'),