From 2d0687a5d086de79cb3e138845132569fee3a00d Mon Sep 17 00:00:00 2001 From: lastHokage Date: Wed, 3 Jun 2026 12:18:47 +0100 Subject: [PATCH 1/6] feat: add anew validation keyword for mapping the content-tree to spark node --- SPEC.md | 10 ++++++++ build.bash | 2 +- content-tree.d.ts | 40 ++++++++++++++++++++++++++++++++ schemas/transit-tree.schema.json | 6 +++++ 4 files changed, 57 insertions(+), 1 deletion(-) diff --git a/SPEC.md b/SPEC.md index c8677e1..8ef4ee2 100644 --- a/SPEC.md +++ b/SPEC.md @@ -1018,18 +1018,22 @@ interface CarouselCard extends Node { id: string /** * @description Heading (60 characters recommended) + * @sparkMapNodeType textInput */ title: string /** * @description Image + * @sparkMapNodeType image */ children: [ImageSet] /** * @description Body text (200 characters recommended) + * @sparkMapNodeType textInput */ copy: string /** * @description Details (optional, 60 characters recommended) + * @sparkMapNodeType textInput */ additionalInfo?: string } @@ -1052,7 +1056,13 @@ type CarouselChildren = CarouselCard[] ```ts interface CarouselHeading extends Node { type: "carousel-heading" + /** + @sparkMapNodeType textInput + */ title: string + /** + @sparkMapNodeType textInput + */ standfirst?: string } ``` diff --git a/build.bash b/build.bash index 6455fb1..32f8687 100755 --- a/build.bash +++ b/build.bash @@ -2,7 +2,7 @@ node tools/maketypes content-tree.ts tsc -d content-tree.ts typescript-json-schema --noExtraProps --required content-tree.ts ContentTree.full.Root > schemas/content-tree.schema.json -typescript-json-schema --validationKeywords sparkGenerateStoryblock sparkRepeater minItem maxItem --noExtraProps --required content-tree.ts ContentTree.transit.Root > schemas/transit-tree.schema.json +typescript-json-schema --validationKeywords sparkMapNodeType sparkGenerateStoryblock sparkRepeater minItem maxItem --noExtraProps --required content-tree.ts ContentTree.transit.Root > schemas/transit-tree.schema.json typescript-json-schema --noExtraProps --required content-tree.ts ContentTree.transit.Body > schemas/body-tree.schema.json rm content-tree.ts rm content-tree.js diff --git a/content-tree.d.ts b/content-tree.d.ts index 06d96b8..7d929d8 100644 --- a/content-tree.d.ts +++ b/content-tree.d.ts @@ -447,18 +447,22 @@ export declare namespace ContentTree { id: string; /** * @description Heading (60 characters recommended) + * @sparkMapNodeType textInput */ title: string; /** * @description Image + * @sparkMapNodeType image */ children: [ImageSet]; /** * @description Body text (200 characters recommended) + * @sparkMapNodeType textInput */ copy: string; /** * @description Details (optional, 60 characters recommended) + * @sparkMapNodeType textInput */ additionalInfo?: string; } @@ -471,7 +475,13 @@ export declare namespace ContentTree { type CarouselChildren = CarouselCard[]; interface CarouselHeading extends Node { type: "carousel-heading"; + /** + @sparkMapNodeType textInput + */ title: string; + /** + @sparkMapNodeType textInput + */ standfirst?: string; } /** @@ -935,18 +945,22 @@ export declare namespace ContentTree { id: string; /** * @description Heading (60 characters recommended) + * @sparkMapNodeType textInput */ title: string; /** * @description Image + * @sparkMapNodeType image */ children: [ImageSet]; /** * @description Body text (200 characters recommended) + * @sparkMapNodeType textInput */ copy: string; /** * @description Details (optional, 60 characters recommended) + * @sparkMapNodeType textInput */ additionalInfo?: string; } @@ -959,7 +973,13 @@ export declare namespace ContentTree { type CarouselChildren = CarouselCard[]; interface CarouselHeading extends Node { type: "carousel-heading"; + /** + @sparkMapNodeType textInput + */ title: string; + /** + @sparkMapNodeType textInput + */ standfirst?: string; } /** @@ -1397,18 +1417,22 @@ export declare namespace ContentTree { id: string; /** * @description Heading (60 characters recommended) + * @sparkMapNodeType textInput */ title: string; /** * @description Image + * @sparkMapNodeType image */ children: [ImageSet]; /** * @description Body text (200 characters recommended) + * @sparkMapNodeType textInput */ copy: string; /** * @description Details (optional, 60 characters recommended) + * @sparkMapNodeType textInput */ additionalInfo?: string; } @@ -1421,7 +1445,13 @@ export declare namespace ContentTree { type CarouselChildren = CarouselCard[]; interface CarouselHeading extends Node { type: "carousel-heading"; + /** + @sparkMapNodeType textInput + */ title: string; + /** + @sparkMapNodeType textInput + */ standfirst?: string; } /** @@ -1886,18 +1916,22 @@ export declare namespace ContentTree { id: string; /** * @description Heading (60 characters recommended) + * @sparkMapNodeType textInput */ title: string; /** * @description Image + * @sparkMapNodeType image */ children: [ImageSet]; /** * @description Body text (200 characters recommended) + * @sparkMapNodeType textInput */ copy: string; /** * @description Details (optional, 60 characters recommended) + * @sparkMapNodeType textInput */ additionalInfo?: string; } @@ -1910,7 +1944,13 @@ export declare namespace ContentTree { type CarouselChildren = CarouselCard[]; interface CarouselHeading extends Node { type: "carousel-heading"; + /** + @sparkMapNodeType textInput + */ title: string; + /** + @sparkMapNodeType textInput + */ standfirst?: string; } /** diff --git a/schemas/transit-tree.schema.json b/schemas/transit-tree.schema.json index 5325b0b..b3100bb 100644 --- a/schemas/transit-tree.schema.json +++ b/schemas/transit-tree.schema.json @@ -313,6 +313,7 @@ "properties": { "additionalInfo": { "description": "Details (optional, 60 characters recommended)", + "sparkMapNodeType": "textInput", "type": "string" }, "children": { @@ -324,10 +325,12 @@ ], "maxItems": 1, "minItems": 1, + "sparkMapNodeType": "image", "type": "array" }, "copy": { "description": "Body text (200 characters recommended)", + "sparkMapNodeType": "textInput", "type": "string" }, "data": {}, @@ -337,6 +340,7 @@ }, "title": { "description": "Heading (60 characters recommended)", + "sparkMapNodeType": "textInput", "type": "string" }, "type": { @@ -358,9 +362,11 @@ "properties": { "data": {}, "standfirst": { + "sparkMapNodeType": "textInput", "type": "string" }, "title": { + "sparkMapNodeType": "textInput", "type": "string" }, "type": { From 21de3b3325018eeb57bc762c811a3c9eb8c295b8 Mon Sep 17 00:00:00 2001 From: lastHokage Date: Wed, 3 Jun 2026 12:57:52 +0100 Subject: [PATCH 2/6] feat: create a new build command which utilising propOrder flag to compile and new file just for spark to fix the oredering issue --- build.bash | 3 +- schemas/spark-transit-tree.schema.json | 2185 ++++++++++++++++++++++++ 2 files changed, 2187 insertions(+), 1 deletion(-) create mode 100644 schemas/spark-transit-tree.schema.json diff --git a/build.bash b/build.bash index 32f8687..1eb2a8c 100755 --- a/build.bash +++ b/build.bash @@ -2,7 +2,8 @@ node tools/maketypes content-tree.ts tsc -d content-tree.ts typescript-json-schema --noExtraProps --required content-tree.ts ContentTree.full.Root > schemas/content-tree.schema.json -typescript-json-schema --validationKeywords sparkMapNodeType sparkGenerateStoryblock sparkRepeater minItem maxItem --noExtraProps --required content-tree.ts ContentTree.transit.Root > schemas/transit-tree.schema.json +typescript-json-schema --noExtraProps --required content-tree.ts ContentTree.transit.Root > schemas/transit-tree.schema.json typescript-json-schema --noExtraProps --required content-tree.ts ContentTree.transit.Body > schemas/body-tree.schema.json +typescript-json-schema --validationKeywords sparkMapNodeType sparkGenerateStoryblock sparkRepeater minItem maxItem --propOrder --noExtraProps --required content-tree.ts ContentTree.transit.Root > schemas/spark-transit-tree.schema.json rm content-tree.ts rm content-tree.js diff --git a/schemas/spark-transit-tree.schema.json b/schemas/spark-transit-tree.schema.json new file mode 100644 index 0000000..2f7698a --- /dev/null +++ b/schemas/spark-transit-tree.schema.json @@ -0,0 +1,2185 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "definitions": { + "ContentTree.transit.AcastPodcast": { + "additionalProperties": false, + "properties": { + "data": {}, + "type": { + "const": "acast-podcast", + "type": "string" + }, + "url": { + "description": "Acast Podcast embed url e.g. https://embed.acast.com/*", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "url", + "data" + ], + "required": [ + "type", + "url" + ], + "type": "object" + }, + "ContentTree.transit.BigNumber": { + "additionalProperties": false, + "properties": { + "data": {}, + "description": { + "type": "string" + }, + "number": { + "type": "string" + }, + "type": { + "const": "big-number", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "number", + "description", + "data" + ], + "required": [ + "description", + "number", + "type" + ], + "type": "object" + }, + "ContentTree.transit.Blockquote": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/ContentTree.transit.Paragraph" + }, + { + "$ref": "#/definitions/ContentTree.transit.Text" + }, + { + "$ref": "#/definitions/ContentTree.transit.Break" + }, + { + "$ref": "#/definitions/ContentTree.transit.Strong" + }, + { + "$ref": "#/definitions/ContentTree.transit.Emphasis" + }, + { + "$ref": "#/definitions/ContentTree.transit.Strikethrough" + }, + { + "$ref": "#/definitions/ContentTree.transit.Subscript" + }, + { + "$ref": "#/definitions/ContentTree.transit.Superscript" + }, + { + "$ref": "#/definitions/ContentTree.transit.Link" + }, + { + "$ref": "#/definitions/ContentTree.transit.FindOutMoreLink" + } + ] + }, + "type": "array" + }, + "data": {}, + "type": { + "const": "blockquote", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "children", + "data" + ], + "required": [ + "children", + "type" + ], + "type": "object" + }, + "ContentTree.transit.Body": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.BodyBlock" + }, + "type": "array" + }, + "data": {}, + "type": { + "const": "body", + "type": "string" + }, + "version": { + "type": "number" + } + }, + "propertyOrder": [ + "type", + "version", + "children", + "data" + ], + "required": [ + "children", + "type", + "version" + ], + "type": "object" + }, + "ContentTree.transit.BodyBlock": { + "anyOf": [ + { + "$ref": "#/definitions/ContentTree.transit.Paragraph" + }, + { + "$ref": "#/definitions/ContentTree.transit.Heading" + }, + { + "$ref": "#/definitions/ContentTree.transit.List" + }, + { + "$ref": "#/definitions/ContentTree.transit.Blockquote" + }, + { + "$ref": "#/definitions/ContentTree.transit.ThematicBreak" + }, + { + "$ref": "#/definitions/ContentTree.transit.Text" + }, + { + "$ref": "#/definitions/ContentTree.transit.AcastPodcast" + }, + { + "$ref": "#/definitions/ContentTree.transit.BigNumber" + }, + { + "$ref": "#/definitions/ContentTree.transit.Carousel" + }, + { + "$ref": "#/definitions/ContentTree.transit.ClipSet" + }, + { + "$ref": "#/definitions/ContentTree.transit.CustomCodeComponent" + }, + { + "$ref": "#/definitions/ContentTree.transit.Definition" + }, + { + "$ref": "#/definitions/ContentTree.transit.Flourish" + }, + { + "$ref": "#/definitions/ContentTree.transit.ImagePair" + }, + { + "$ref": "#/definitions/ContentTree.transit.ImageSet" + }, + { + "$ref": "#/definitions/ContentTree.transit.InfoBox" + }, + { + "$ref": "#/definitions/ContentTree.transit.InfoPair" + }, + { + "$ref": "#/definitions/ContentTree.transit.InNumbers" + }, + { + "$ref": "#/definitions/ContentTree.transit.Layout" + }, + { + "$ref": "#/definitions/ContentTree.transit.Pullquote" + }, + { + "$ref": "#/definitions/ContentTree.transit.Recommended" + }, + { + "$ref": "#/definitions/ContentTree.transit.RecommendedList" + }, + { + "$ref": "#/definitions/ContentTree.transit.ScrollyBlock" + }, + { + "$ref": "#/definitions/ContentTree.transit.Table" + }, + { + "$ref": "#/definitions/ContentTree.transit.Timeline" + }, + { + "$ref": "#/definitions/ContentTree.transit.Tweet" + }, + { + "$ref": "#/definitions/ContentTree.transit.Video" + }, + { + "$ref": "#/definitions/ContentTree.transit.VimeoVideo" + }, + { + "$ref": "#/definitions/ContentTree.transit.YoutubeVideo" + } + ] + }, + "ContentTree.transit.Break": { + "additionalProperties": false, + "properties": { + "data": {}, + "type": { + "const": "break", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "data" + ], + "required": [ + "type" + ], + "type": "object" + }, + "ContentTree.transit.Card": { + "additionalProperties": false, + "description": "A card describes a subject with images and text", + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.CardChildren" + }, + "type": "array" + }, + "data": {}, + "title": { + "description": "The title of this card", + "type": "string" + }, + "type": { + "const": "card", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "title", + "children", + "data" + ], + "required": [ + "children", + "type" + ], + "type": "object" + }, + "ContentTree.transit.CardChildren": { + "anyOf": [ + { + "$ref": "#/definitions/ContentTree.transit.Paragraph" + }, + { + "$ref": "#/definitions/ContentTree.transit.List" + }, + { + "$ref": "#/definitions/ContentTree.transit.Blockquote" + }, + { + "$ref": "#/definitions/ContentTree.transit.ThematicBreak" + }, + { + "$ref": "#/definitions/ContentTree.transit.Text" + }, + { + "$ref": "#/definitions/ContentTree.transit.ImageSet" + } + ], + "description": "Allowed children for a card" + }, + "ContentTree.transit.Carousel": { + "additionalProperties": false, + "properties": { + "children": { + "description": "Carousel item", + "items": { + "$ref": "#/definitions/ContentTree.transit.CarouselCard" + }, + "maxItems": 10, + "minItems": 4, + "sparkRepeater": true, + "type": "array" + }, + "data": {}, + "heading": { + "$ref": "#/definitions/ContentTree.transit.CarouselHeading" + }, + "id": { + "description": "unique identifier required for component tracking", + "type": "string" + }, + "type": { + "const": "carousel", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "id", + "heading", + "children", + "data" + ], + "required": [ + "children", + "id", + "type" + ], + "sparkGenerateStoryblock": true, + "type": "object" + }, + "ContentTree.transit.CarouselCard": { + "additionalProperties": false, + "properties": { + "additionalInfo": { + "description": "Details (optional, 60 characters recommended)", + "sparkMapNodeType": "textInput", + "type": "string" + }, + "children": { + "description": "Image", + "items": [ + { + "$ref": "#/definitions/ContentTree.transit.ImageSet" + } + ], + "maxItems": 1, + "minItems": 1, + "sparkMapNodeType": "image", + "type": "array" + }, + "copy": { + "description": "Body text (200 characters recommended)", + "sparkMapNodeType": "textInput", + "type": "string" + }, + "data": {}, + "id": { + "description": "unique identifier required for component tracking", + "type": "string" + }, + "title": { + "description": "Heading (60 characters recommended)", + "sparkMapNodeType": "textInput", + "type": "string" + }, + "type": { + "const": "carousel-card", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "id", + "children", + "title", + "copy", + "additionalInfo", + "data" + ], + "required": [ + "children", + "copy", + "id", + "title", + "type" + ], + "type": "object" + }, + "ContentTree.transit.CarouselHeading": { + "additionalProperties": false, + "properties": { + "data": {}, + "standfirst": { + "sparkMapNodeType": "textInput", + "type": "string" + }, + "title": { + "sparkMapNodeType": "textInput", + "type": "string" + }, + "type": { + "const": "carousel-heading", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "title", + "standfirst", + "data" + ], + "required": [ + "title", + "type" + ], + "type": "object" + }, + "ContentTree.transit.ClipSet": { + "additionalProperties": false, + "properties": { + "autoplay": { + "type": "boolean" + }, + "data": {}, + "fragmentIdentifier": { + "type": "string" + }, + "id": { + "type": "string" + }, + "layoutWidth": { + "$ref": "#/definitions/ContentTree.transit.ClipSetLayoutWidth" + }, + "loop": { + "type": "boolean" + }, + "muted": { + "type": "boolean" + }, + "type": { + "const": "clip-set", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "id", + "layoutWidth", + "autoplay", + "fragmentIdentifier", + "loop", + "muted", + "data" + ], + "required": [ + "id", + "layoutWidth", + "type" + ], + "type": "object" + }, + "ContentTree.transit.ClipSetLayoutWidth": { + "enum": [ + "full-grid", + "in-line", + "mid-grid" + ], + "type": "string" + }, + "ContentTree.transit.CustomCodeComponent": { + "additionalProperties": false, + "properties": { + "data": {}, + "id": { + "description": "Id taken from the CAPI url", + "type": "string" + }, + "layoutWidth": { + "$ref": "#/definitions/ContentTree.transit.LayoutWidth", + "description": "How the component should be presented in the article page according to the column layout system" + }, + "type": { + "const": "custom-code-component", + "description": "Component type", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "id", + "layoutWidth", + "data" + ], + "required": [ + "id", + "layoutWidth", + "type" + ], + "type": "object" + }, + "ContentTree.transit.Definition": { + "additionalProperties": false, + "description": "A definition has a term and a related description. It is used to describe a term.", + "properties": { + "data": {}, + "description": { + "type": "string" + }, + "term": { + "type": "string" + }, + "type": { + "const": "definition", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "term", + "description", + "data" + ], + "required": [ + "description", + "term", + "type" + ], + "type": "object" + }, + "ContentTree.transit.Emphasis": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.Phrasing" + }, + "type": "array" + }, + "data": {}, + "type": { + "const": "emphasis", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "children", + "data" + ], + "required": [ + "children", + "type" + ], + "type": "object" + }, + "ContentTree.transit.FindOutMoreLink": { + "additionalProperties": false, + "properties": { + "children": { + "items": [ + { + "anyOf": [ + { + "$ref": "#/definitions/ContentTree.transit.Text" + }, + { + "$ref": "#/definitions/ContentTree.transit.Strong" + }, + { + "$ref": "#/definitions/ContentTree.transit.Emphasis" + } + ] + } + ], + "maxItems": 1, + "minItems": 1, + "type": "array" + }, + "data": {}, + "type": { + "const": "find-out-more-link", + "type": "string" + }, + "url": { + "type": "string" + } + }, + "propertyOrder": [ + "type", + "url", + "children", + "data" + ], + "required": [ + "children", + "type", + "url" + ], + "type": "object" + }, + "ContentTree.transit.Flourish": { + "additionalProperties": false, + "properties": { + "data": {}, + "description": { + "type": "string" + }, + "flourishType": { + "type": "string" + }, + "fragmentIdentifier": { + "type": "string" + }, + "id": { + "type": "string" + }, + "layoutWidth": { + "$ref": "#/definitions/ContentTree.transit.FlourishLayoutWidth" + }, + "timestamp": { + "type": "string" + }, + "type": { + "const": "flourish", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "id", + "layoutWidth", + "flourishType", + "description", + "timestamp", + "fragmentIdentifier", + "data" + ], + "required": [ + "flourishType", + "id", + "layoutWidth", + "type" + ], + "type": "object" + }, + "ContentTree.transit.FlourishLayoutWidth": { + "enum": [ + "full-grid", + "in-line" + ], + "type": "string" + }, + "ContentTree.transit.Heading": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.Text" + }, + "type": "array" + }, + "data": {}, + "fragmentIdentifier": { + "type": "string" + }, + "level": { + "enum": [ + "chapter", + "label", + "subheading" + ], + "type": "string" + }, + "type": { + "const": "heading", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "children", + "level", + "fragmentIdentifier", + "data" + ], + "required": [ + "children", + "level", + "type" + ], + "type": "object" + }, + "ContentTree.transit.ImagePair": { + "additionalProperties": false, + "properties": { + "children": { + "items": [ + { + "$ref": "#/definitions/ContentTree.transit.ImageSet" + }, + { + "$ref": "#/definitions/ContentTree.transit.ImageSet" + } + ], + "maxItems": 2, + "minItems": 2, + "type": "array" + }, + "data": {}, + "type": { + "const": "image-pair", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "children", + "data" + ], + "required": [ + "children", + "type" + ], + "type": "object" + }, + "ContentTree.transit.ImageSet": { + "additionalProperties": false, + "properties": { + "data": {}, + "fragmentIdentifier": { + "type": "string" + }, + "id": { + "type": "string" + }, + "type": { + "const": "image-set", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "id", + "fragmentIdentifier", + "data" + ], + "required": [ + "id", + "type" + ], + "type": "object" + }, + "ContentTree.transit.InNumbers": { + "additionalProperties": false, + "description": "InNumbers represents a set of numbers with related descriptions.", + "properties": { + "children": { + "items": [ + { + "$ref": "#/definitions/ContentTree.transit.Definition" + }, + { + "$ref": "#/definitions/ContentTree.transit.Definition" + }, + { + "$ref": "#/definitions/ContentTree.transit.Definition" + } + ], + "maxItems": 3, + "minItems": 3, + "type": "array" + }, + "data": {}, + "title": { + "description": "The title for the InNumbers", + "type": "string" + }, + "type": { + "const": "in-numbers", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "title", + "children", + "data" + ], + "required": [ + "children", + "type" + ], + "type": "object" + }, + "ContentTree.transit.InfoBox": { + "additionalProperties": false, + "description": "An info box describes a subject via a single card", + "properties": { + "children": { + "items": [ + { + "$ref": "#/definitions/ContentTree.transit.Card" + } + ], + "maxItems": 1, + "minItems": 1, + "type": "array" + }, + "data": {}, + "layoutWidth": { + "$ref": "#/definitions/ContentTree.transit.InfoBoxLayoutWidth", + "description": "The layout width supported by this node" + }, + "type": { + "const": "info-box", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "layoutWidth", + "children", + "data" + ], + "required": [ + "children", + "layoutWidth", + "type" + ], + "type": "object" + }, + "ContentTree.transit.InfoBoxLayoutWidth": { + "description": "Allowed layout widths for an InfoBox.", + "enum": [ + "in-line", + "inset-left" + ], + "type": "string" + }, + "ContentTree.transit.InfoPair": { + "additionalProperties": false, + "description": "InfoPair provides exactly two cards.", + "properties": { + "children": { + "items": [ + { + "$ref": "#/definitions/ContentTree.transit.Card" + }, + { + "$ref": "#/definitions/ContentTree.transit.Card" + } + ], + "maxItems": 2, + "minItems": 2, + "type": "array" + }, + "data": {}, + "title": { + "description": "The title of the info pair", + "type": "string" + }, + "type": { + "const": "info-pair", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "title", + "children", + "data" + ], + "required": [ + "children", + "type" + ], + "type": "object" + }, + "ContentTree.transit.Layout": { + "additionalProperties": false, + "properties": { + "children": { + "anyOf": [ + { + "additionalItems": { + "$ref": "#/definitions/ContentTree.transit.LayoutSlot" + }, + "items": [ + { + "$ref": "#/definitions/ContentTree.transit.Heading" + }, + { + "$ref": "#/definitions/ContentTree.transit.LayoutImage" + } + ], + "minItems": 2, + "type": "array" + }, + { + "additionalItems": { + "$ref": "#/definitions/ContentTree.transit.LayoutSlot" + }, + "items": [ + { + "$ref": "#/definitions/ContentTree.transit.Heading" + } + ], + "minItems": 1, + "type": "array" + }, + { + "items": { + "$ref": "#/definitions/ContentTree.transit.LayoutSlot" + }, + "type": "array" + } + ] + }, + "data": {}, + "layoutName": { + "enum": [ + "auto", + "card", + "timeline" + ], + "type": "string" + }, + "layoutWidth": { + "type": "string" + }, + "type": { + "const": "layout", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "layoutName", + "layoutWidth", + "children", + "data" + ], + "required": [ + "children", + "layoutName", + "layoutWidth", + "type" + ], + "type": "object" + }, + "ContentTree.transit.LayoutImage": { + "additionalProperties": false, + "properties": { + "alt": { + "type": "string" + }, + "caption": { + "type": "string" + }, + "credit": { + "type": "string" + }, + "data": {}, + "id": { + "type": "string" + }, + "type": { + "const": "layout-image", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "id", + "alt", + "caption", + "credit", + "data" + ], + "required": [ + "alt", + "caption", + "credit", + "id", + "type" + ], + "type": "object" + }, + "ContentTree.transit.LayoutSlot": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/ContentTree.transit.Paragraph" + }, + { + "$ref": "#/definitions/ContentTree.transit.Heading" + }, + { + "$ref": "#/definitions/ContentTree.transit.LayoutImage" + } + ] + }, + "type": "array" + }, + "data": {}, + "type": { + "const": "layout-slot", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "children", + "data" + ], + "required": [ + "children", + "type" + ], + "type": "object" + }, + "ContentTree.transit.LayoutWidth": { + "enum": [ + "auto", + "full-bleed", + "full-grid", + "full-width", + "in-line", + "inset-left", + "inset-right", + "mid-grid" + ], + "type": "string" + }, + "ContentTree.transit.Link": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.Phrasing" + }, + "type": "array" + }, + "data": {}, + "title": { + "type": "string" + }, + "type": { + "const": "link", + "type": "string" + }, + "url": { + "type": "string" + } + }, + "propertyOrder": [ + "type", + "url", + "title", + "children", + "data" + ], + "required": [ + "children", + "title", + "type", + "url" + ], + "type": "object" + }, + "ContentTree.transit.List": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.ListItem" + }, + "type": "array" + }, + "data": {}, + "ordered": { + "type": "boolean" + }, + "type": { + "const": "list", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "ordered", + "children", + "data" + ], + "required": [ + "children", + "ordered", + "type" + ], + "type": "object" + }, + "ContentTree.transit.ListItem": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/ContentTree.transit.Paragraph" + }, + { + "$ref": "#/definitions/ContentTree.transit.Text" + }, + { + "$ref": "#/definitions/ContentTree.transit.Break" + }, + { + "$ref": "#/definitions/ContentTree.transit.Strong" + }, + { + "$ref": "#/definitions/ContentTree.transit.Emphasis" + }, + { + "$ref": "#/definitions/ContentTree.transit.Strikethrough" + }, + { + "$ref": "#/definitions/ContentTree.transit.Subscript" + }, + { + "$ref": "#/definitions/ContentTree.transit.Superscript" + }, + { + "$ref": "#/definitions/ContentTree.transit.Link" + }, + { + "$ref": "#/definitions/ContentTree.transit.FindOutMoreLink" + } + ] + }, + "type": "array" + }, + "data": {}, + "type": { + "const": "list-item", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "children", + "data" + ], + "required": [ + "children", + "type" + ], + "type": "object" + }, + "ContentTree.transit.Paragraph": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.Phrasing" + }, + "type": "array" + }, + "data": {}, + "type": { + "const": "paragraph", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "children", + "data" + ], + "required": [ + "children", + "type" + ], + "type": "object" + }, + "ContentTree.transit.Phrasing": { + "anyOf": [ + { + "$ref": "#/definitions/ContentTree.transit.Text" + }, + { + "$ref": "#/definitions/ContentTree.transit.Break" + }, + { + "$ref": "#/definitions/ContentTree.transit.Strong" + }, + { + "$ref": "#/definitions/ContentTree.transit.Emphasis" + }, + { + "$ref": "#/definitions/ContentTree.transit.Strikethrough" + }, + { + "$ref": "#/definitions/ContentTree.transit.Subscript" + }, + { + "$ref": "#/definitions/ContentTree.transit.Superscript" + }, + { + "$ref": "#/definitions/ContentTree.transit.Link" + }, + { + "$ref": "#/definitions/ContentTree.transit.FindOutMoreLink" + } + ] + }, + "ContentTree.transit.Pullquote": { + "additionalProperties": false, + "properties": { + "data": {}, + "source": { + "type": "string" + }, + "text": { + "type": "string" + }, + "type": { + "const": "pullquote", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "text", + "source", + "data" + ], + "required": [ + "text", + "type" + ], + "type": "object" + }, + "ContentTree.transit.Recommended": { + "additionalProperties": false, + "properties": { + "data": {}, + "heading": { + "type": "string" + }, + "id": { + "type": "string" + }, + "teaserTitleOverride": { + "type": "string" + }, + "type": { + "const": "recommended", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "id", + "heading", + "teaserTitleOverride", + "data" + ], + "required": [ + "id", + "type" + ], + "type": "object" + }, + "ContentTree.transit.RecommendedList": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.Recommended" + }, + "type": "array" + }, + "data": {}, + "heading": { + "type": "string" + }, + "type": { + "const": "recommended-list", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "heading", + "children", + "data" + ], + "required": [ + "children", + "type" + ], + "type": "object" + }, + "ContentTree.transit.ScrollyBlock": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.ScrollySection" + }, + "type": "array" + }, + "data": {}, + "theme": { + "enum": [ + "sans", + "serif" + ], + "type": "string" + }, + "type": { + "const": "scrolly-block", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "theme", + "children", + "data" + ], + "required": [ + "children", + "theme", + "type" + ], + "type": "object" + }, + "ContentTree.transit.ScrollyCopy": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/ContentTree.transit.Paragraph" + }, + { + "$ref": "#/definitions/ContentTree.transit.ScrollyHeading" + } + ] + }, + "type": "array" + }, + "data": {}, + "type": { + "const": "scrolly-copy", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "children", + "data" + ], + "required": [ + "children", + "type" + ], + "type": "object" + }, + "ContentTree.transit.ScrollyHeading": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.Text" + }, + "type": "array" + }, + "data": {}, + "level": { + "enum": [ + "chapter", + "heading", + "subheading" + ], + "type": "string" + }, + "type": { + "const": "scrolly-heading", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "level", + "children", + "data" + ], + "required": [ + "children", + "level", + "type" + ], + "type": "object" + }, + "ContentTree.transit.ScrollyImage": { + "additionalProperties": false, + "properties": { + "data": {}, + "id": { + "type": "string" + }, + "type": { + "const": "scrolly-image", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "id", + "data" + ], + "required": [ + "id", + "type" + ], + "type": "object" + }, + "ContentTree.transit.ScrollySection": { + "additionalProperties": false, + "properties": { + "children": { + "additionalItems": { + "$ref": "#/definitions/ContentTree.transit.ScrollyCopy" + }, + "items": [ + { + "$ref": "#/definitions/ContentTree.transit.ScrollyImage" + } + ], + "minItems": 1, + "type": "array" + }, + "data": {}, + "display": { + "enum": [ + "dark-background", + "light-background" + ], + "type": "string" + }, + "noBox": { + "const": true, + "type": "boolean" + }, + "position": { + "enum": [ + "center", + "left", + "right" + ], + "type": "string" + }, + "transition": { + "enum": [ + "delay-after", + "delay-before" + ], + "type": "string" + }, + "type": { + "const": "scrolly-section", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "display", + "noBox", + "position", + "transition", + "children", + "data" + ], + "required": [ + "children", + "display", + "position", + "type" + ], + "type": "object" + }, + "ContentTree.transit.Strikethrough": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.Phrasing" + }, + "type": "array" + }, + "data": {}, + "type": { + "const": "strikethrough", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "children", + "data" + ], + "required": [ + "children", + "type" + ], + "type": "object" + }, + "ContentTree.transit.Strong": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.Phrasing" + }, + "type": "array" + }, + "data": {}, + "type": { + "const": "strong", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "children", + "data" + ], + "required": [ + "children", + "type" + ], + "type": "object" + }, + "ContentTree.transit.Subscript": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.Phrasing" + }, + "type": "array" + }, + "data": {}, + "type": { + "const": "subscript", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "children", + "data" + ], + "required": [ + "children", + "type" + ], + "type": "object" + }, + "ContentTree.transit.Superscript": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.Phrasing" + }, + "type": "array" + }, + "data": {}, + "type": { + "const": "superscript", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "children", + "data" + ], + "required": [ + "children", + "type" + ], + "type": "object" + }, + "ContentTree.transit.Table": { + "additionalProperties": false, + "properties": { + "children": { + "$ref": "#/definitions/ContentTree.transit.TableChildren" + }, + "collapseAfterHowManyRows": { + "type": "number" + }, + "columnSettings": { + "items": { + "additionalProperties": false, + "properties": { + "hideOnMobile": { + "type": "boolean" + }, + "sortType": { + "enum": [ + "currency", + "date", + "number", + "percent", + "text" + ], + "type": "string" + }, + "sortable": { + "type": "boolean" + } + }, + "propertyOrder": [ + "hideOnMobile", + "sortable", + "sortType" + ], + "required": [ + "hideOnMobile", + "sortable" + ], + "type": "object" + }, + "type": "array" + }, + "compact": { + "type": "boolean" + }, + "data": {}, + "layoutWidth": { + "$ref": "#/definitions/ContentTree.transit.TableLayoutWidth" + }, + "responsiveStyle": { + "enum": [ + "flat", + "overflow", + "scroll" + ], + "type": "string" + }, + "stripes": { + "type": "boolean" + }, + "type": { + "const": "table", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "stripes", + "compact", + "layoutWidth", + "collapseAfterHowManyRows", + "responsiveStyle", + "columnSettings", + "children", + "data" + ], + "required": [ + "children", + "columnSettings", + "compact", + "layoutWidth", + "responsiveStyle", + "stripes", + "type" + ], + "type": "object" + }, + "ContentTree.transit.TableBody": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.TableRow" + }, + "type": "array" + }, + "data": {}, + "type": { + "const": "table-body", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "children", + "data" + ], + "required": [ + "children", + "type" + ], + "type": "object" + }, + "ContentTree.transit.TableCaption": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.Phrasing" + }, + "type": "array" + }, + "data": {}, + "type": { + "const": "table-caption", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "children", + "data" + ], + "required": [ + "children", + "type" + ], + "type": "object" + }, + "ContentTree.transit.TableCell": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.Phrasing" + }, + "type": "array" + }, + "columnSpan": { + "type": "number" + }, + "data": {}, + "heading": { + "type": "boolean" + }, + "rowSpan": { + "type": "number" + }, + "type": { + "const": "table-cell", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "heading", + "columnSpan", + "rowSpan", + "children", + "data" + ], + "required": [ + "children", + "type" + ], + "type": "object" + }, + "ContentTree.transit.TableChildren": { + "anyOf": [ + { + "items": [ + { + "$ref": "#/definitions/ContentTree.transit.TableCaption" + }, + { + "$ref": "#/definitions/ContentTree.transit.TableBody" + }, + { + "$ref": "#/definitions/ContentTree.transit.TableFooter" + } + ], + "maxItems": 3, + "minItems": 2, + "type": "array" + }, + { + "items": [ + { + "$ref": "#/definitions/ContentTree.transit.TableBody" + }, + { + "$ref": "#/definitions/ContentTree.transit.TableFooter" + } + ], + "maxItems": 2, + "minItems": 1, + "type": "array" + }, + { + "items": [ + { + "$ref": "#/definitions/ContentTree.transit.TableCaption" + }, + { + "$ref": "#/definitions/ContentTree.transit.TableHeader" + }, + { + "$ref": "#/definitions/ContentTree.transit.TableBody" + }, + { + "$ref": "#/definitions/ContentTree.transit.TableFooter" + } + ], + "maxItems": 4, + "minItems": 3, + "type": "array" + }, + { + "items": [ + { + "$ref": "#/definitions/ContentTree.transit.TableHeader" + }, + { + "$ref": "#/definitions/ContentTree.transit.TableBody" + }, + { + "$ref": "#/definitions/ContentTree.transit.TableFooter" + } + ], + "maxItems": 3, + "minItems": 2, + "type": "array" + } + ] + }, + "ContentTree.transit.TableFooter": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.Phrasing" + }, + "type": "array" + }, + "data": {}, + "type": { + "const": "table-footer", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "children", + "data" + ], + "required": [ + "children", + "type" + ], + "type": "object" + }, + "ContentTree.transit.TableHeader": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.TableRow" + }, + "type": "array" + }, + "data": {}, + "type": { + "const": "table-header", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "children", + "data" + ], + "required": [ + "children", + "type" + ], + "type": "object" + }, + "ContentTree.transit.TableLayoutWidth": { + "enum": [ + "auto", + "full-bleed", + "full-grid", + "inset-left", + "inset-right" + ], + "type": "string" + }, + "ContentTree.transit.TableRow": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.TableCell" + }, + "type": "array" + }, + "data": {}, + "type": { + "const": "table-row", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "children", + "data" + ], + "required": [ + "children", + "type" + ], + "type": "object" + }, + "ContentTree.transit.Text": { + "additionalProperties": false, + "properties": { + "data": {}, + "type": { + "const": "text", + "type": "string" + }, + "value": { + "type": "string" + } + }, + "propertyOrder": [ + "type", + "value", + "data" + ], + "required": [ + "type", + "value" + ], + "type": "object" + }, + "ContentTree.transit.ThematicBreak": { + "additionalProperties": false, + "properties": { + "data": {}, + "type": { + "const": "thematic-break", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "data" + ], + "required": [ + "type" + ], + "type": "object" + }, + "ContentTree.transit.Timeline": { + "additionalProperties": false, + "description": "Timeline nodes display a timeline of events in arbitrary order.", + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.TimelineEvent" + }, + "type": "array" + }, + "data": {}, + "title": { + "description": "The title for the timeline", + "type": "string" + }, + "type": { + "const": "timeline", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "title", + "children", + "data" + ], + "required": [ + "children", + "title", + "type" + ], + "type": "object" + }, + "ContentTree.transit.TimelineEvent": { + "additionalProperties": false, + "description": "TimelineEvent is the representation of a single event in a Timeline.", + "properties": { + "children": { + "description": "Any combination of paragraphs and image sets", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/ContentTree.transit.Paragraph" + }, + { + "$ref": "#/definitions/ContentTree.transit.ImageSet" + } + ] + }, + "type": "array" + }, + "data": {}, + "title": { + "description": "The title of the event", + "type": "string" + }, + "type": { + "const": "timeline-event", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "title", + "children", + "data" + ], + "required": [ + "children", + "title", + "type" + ], + "type": "object" + }, + "ContentTree.transit.Tweet": { + "additionalProperties": false, + "properties": { + "data": {}, + "id": { + "type": "string" + }, + "type": { + "const": "tweet", + "type": "string" + } + }, + "propertyOrder": [ + "id", + "type", + "data" + ], + "required": [ + "id", + "type" + ], + "type": "object" + }, + "ContentTree.transit.Video": { + "additionalProperties": false, + "properties": { + "data": {}, + "id": { + "type": "string" + }, + "type": { + "const": "video", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "id", + "data" + ], + "required": [ + "id", + "type" + ], + "type": "object" + }, + "ContentTree.transit.VimeoVideo": { + "additionalProperties": false, + "properties": { + "data": {}, + "type": { + "const": "vimeo-video", + "type": "string" + }, + "url": { + "description": "Vimeo embed URL e.g. https://player.vimeo.com/player/ or https://vimeo.com/", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "url", + "data" + ], + "required": [ + "type", + "url" + ], + "type": "object" + }, + "ContentTree.transit.YoutubeVideo": { + "additionalProperties": false, + "properties": { + "data": {}, + "type": { + "const": "youtube-video", + "type": "string" + }, + "url": { + "type": "string" + } + }, + "propertyOrder": [ + "type", + "url", + "data" + ], + "required": [ + "type", + "url" + ], + "type": "object" + } + }, + "properties": { + "body": { + "$ref": "#/definitions/ContentTree.transit.Body" + }, + "data": {}, + "type": { + "const": "root", + "type": "string" + } + }, + "propertyOrder": [ + "type", + "body", + "data" + ], + "required": [ + "body", + "type" + ], + "type": "object" +} + From ad3c32cafe1e97ecdb1a53cd1908c7f771a14df1 Mon Sep 17 00:00:00 2001 From: lastHokage Date: Wed, 3 Jun 2026 12:59:27 +0100 Subject: [PATCH 3/6] fix: change the oreder of the carousel-card to match the spark design --- SPEC.md | 10 ++++---- content-tree.d.ts | 40 ++++++++++++++++---------------- schemas/transit-tree.schema.json | 8 ------- 3 files changed, 25 insertions(+), 33 deletions(-) diff --git a/SPEC.md b/SPEC.md index 8ef4ee2..f1a0bbc 100644 --- a/SPEC.md +++ b/SPEC.md @@ -1016,16 +1016,16 @@ interface CarouselCard extends Node { * @description unique identifier required for component tracking */ id: string - /** - * @description Heading (60 characters recommended) - * @sparkMapNodeType textInput - */ - title: string /** * @description Image * @sparkMapNodeType image */ children: [ImageSet] + /** + * @description Heading (60 characters recommended) + * @sparkMapNodeType textInput + */ + title: string /** * @description Body text (200 characters recommended) * @sparkMapNodeType textInput diff --git a/content-tree.d.ts b/content-tree.d.ts index 7d929d8..8acec1c 100644 --- a/content-tree.d.ts +++ b/content-tree.d.ts @@ -445,16 +445,16 @@ export declare namespace ContentTree { * @description unique identifier required for component tracking */ id: string; - /** - * @description Heading (60 characters recommended) - * @sparkMapNodeType textInput - */ - title: string; /** * @description Image * @sparkMapNodeType image */ children: [ImageSet]; + /** + * @description Heading (60 characters recommended) + * @sparkMapNodeType textInput + */ + title: string; /** * @description Body text (200 characters recommended) * @sparkMapNodeType textInput @@ -943,16 +943,16 @@ export declare namespace ContentTree { * @description unique identifier required for component tracking */ id: string; - /** - * @description Heading (60 characters recommended) - * @sparkMapNodeType textInput - */ - title: string; /** * @description Image * @sparkMapNodeType image */ children: [ImageSet]; + /** + * @description Heading (60 characters recommended) + * @sparkMapNodeType textInput + */ + title: string; /** * @description Body text (200 characters recommended) * @sparkMapNodeType textInput @@ -1415,16 +1415,16 @@ export declare namespace ContentTree { * @description unique identifier required for component tracking */ id: string; - /** - * @description Heading (60 characters recommended) - * @sparkMapNodeType textInput - */ - title: string; /** * @description Image * @sparkMapNodeType image */ children: [ImageSet]; + /** + * @description Heading (60 characters recommended) + * @sparkMapNodeType textInput + */ + title: string; /** * @description Body text (200 characters recommended) * @sparkMapNodeType textInput @@ -1914,16 +1914,16 @@ export declare namespace ContentTree { * @description unique identifier required for component tracking */ id: string; - /** - * @description Heading (60 characters recommended) - * @sparkMapNodeType textInput - */ - title: string; /** * @description Image * @sparkMapNodeType image */ children: [ImageSet]; + /** + * @description Heading (60 characters recommended) + * @sparkMapNodeType textInput + */ + title: string; /** * @description Body text (200 characters recommended) * @sparkMapNodeType textInput diff --git a/schemas/transit-tree.schema.json b/schemas/transit-tree.schema.json index b3100bb..c2b1e07 100644 --- a/schemas/transit-tree.schema.json +++ b/schemas/transit-tree.schema.json @@ -284,7 +284,6 @@ }, "maxItems": 10, "minItems": 4, - "sparkRepeater": true, "type": "array" }, "data": {}, @@ -305,7 +304,6 @@ "id", "type" ], - "sparkGenerateStoryblock": true, "type": "object" }, "ContentTree.transit.CarouselCard": { @@ -313,7 +311,6 @@ "properties": { "additionalInfo": { "description": "Details (optional, 60 characters recommended)", - "sparkMapNodeType": "textInput", "type": "string" }, "children": { @@ -325,12 +322,10 @@ ], "maxItems": 1, "minItems": 1, - "sparkMapNodeType": "image", "type": "array" }, "copy": { "description": "Body text (200 characters recommended)", - "sparkMapNodeType": "textInput", "type": "string" }, "data": {}, @@ -340,7 +335,6 @@ }, "title": { "description": "Heading (60 characters recommended)", - "sparkMapNodeType": "textInput", "type": "string" }, "type": { @@ -362,11 +356,9 @@ "properties": { "data": {}, "standfirst": { - "sparkMapNodeType": "textInput", "type": "string" }, "title": { - "sparkMapNodeType": "textInput", "type": "string" }, "type": { From 7175457242301bf0f6304a8f780d6558ef340257 Mon Sep 17 00:00:00 2001 From: Katie Fenn Date: Fri, 5 Jun 2026 13:39:45 +0100 Subject: [PATCH 4/6] feat: Add test coverage for spark-transit-tree.schema.json - The existing test suite had tests covering the existing schemas - content-tree, transit-tree and body-tree. However, the preceding commits also added a new schema - spark-transit-tree, which has more custom validation keywords and a requirement for property ordering. - This commit adds coverage for the new schema type and adds the supporting validation config for sparkMapNodeType and propertyOrder. --- test.bash | 11 +++++++++-- tests/schema/ajv-custom-keywords.js | 8 ++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/test.bash b/test.bash index 6a1bffc..c330dbf 100755 --- a/test.bash +++ b/test.bash @@ -7,9 +7,16 @@ AJV_CUSTOM_KEYWORDS=./tests/schema/ajv-custom-keywords.js for schema in body-tree content-tree transit-tree; do for datafile in tests/schema/"$schema"/valid/*; do - "$AJV" test --valid --allow-union-types -c "$AJV_CUSTOM_KEYWORDS" -s schemas/"$schema".schema.json -d "$datafile" + "$AJV" test --valid --allow-union-types -s schemas/"$schema".schema.json -d "$datafile" done for datafile in tests/schema/"$schema"/invalid/*; do - "$AJV" test --invalid --allow-union-types -c "$AJV_CUSTOM_KEYWORDS" -s schemas/"$schema".schema.json -d "$datafile" + "$AJV" test --invalid --allow-union-types -s schemas/"$schema".schema.json -d "$datafile" done done + +for datafile in tests/schema/"$schema"/valid/*; do + "$AJV" test --valid --allow-union-types -c "$AJV_CUSTOM_KEYWORDS" -s schemas/spark-transit-tree.schema.json -d "$datafile" +done +for datafile in tests/schema/"$schema"/invalid/*; do + "$AJV" test --invalid --allow-union-types -c "$AJV_CUSTOM_KEYWORDS" -s schemas/spark-transit-tree.schema.json -d "$datafile" +done diff --git a/tests/schema/ajv-custom-keywords.js b/tests/schema/ajv-custom-keywords.js index 8f9ceac..d118779 100644 --- a/tests/schema/ajv-custom-keywords.js +++ b/tests/schema/ajv-custom-keywords.js @@ -7,4 +7,12 @@ module.exports = function addCustomKeywords(ajv) { keyword: "sparkGenerateStoryblock", schemaType: "boolean", }) + ajv.addKeyword({ + keyword: "sparkMapNodeType", + schemaType: "string", + }) + ajv.addKeyword({ + keyword: "propertyOrder", + schemaType: "array", + }) } From 98ad6f24b22f7eaf1ba29b9ea521225a041b7666 Mon Sep 17 00:00:00 2001 From: Katie Fenn Date: Thu, 18 Jun 2026 13:51:30 +0100 Subject: [PATCH 5/6] Migrate Title and Standfirst properties to the Carousel node --- .DS_Store | Bin 0 -> 6148 bytes SPEC.md | 25 +++----- content-tree.d.ts | 80 ++++++++++--------------- schemas/body-tree.schema.json | 30 ++-------- schemas/content-tree.schema.json | 30 ++-------- schemas/spark-transit-tree.schema.json | 46 ++++---------- schemas/transit-tree.schema.json | 30 ++-------- 7 files changed, 71 insertions(+), 170 deletions(-) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..f3f76fd88efabced2cc37fe39c67c51f83dbb0de GIT binary patch literal 6148 zcmeHK%}T>S5T3QwrWBzE1-&hJEm&KBC|*LWFJMFuDmAgi2GeY5T6-vk-1UWg5ueAI z-3?gn!IOxcf!%L*es(h-WPboac$4k{KotNSY=nZ8B|_$DD)fy2p|oo=`f6! zO!OB`^z9``;Q{*4gHPYDLiD_a!#GOE_4>P5D-_o^N>0hCICp+uP5p5&o}^wdx}njf zQgN8;L3kDQrk(26nM%e%l=MbAA?o%p$+Plk zMrME+UcoZEP5iZH2&CRNxchA`=9mp0C|m>D$bAoR*Ok6k%`ya>HI+NBN$;Tq(g8DIt$87P=x zmCpZj{AHFt@)uKh#0)S4|BL}qZ2N5wi?V0yxAN$$mDujFkx*Pl3JU5AmjE2lKC+{o d#xF_7IM-rkkY*vfPDkX6fF^`HX5beX_y9^aOuYaA literal 0 HcmV?d00001 diff --git a/SPEC.md b/SPEC.md index 9decf4d..e5d4897 100644 --- a/SPEC.md +++ b/SPEC.md @@ -1052,22 +1052,6 @@ type CarouselChildren = CarouselCard[] ``` **CarouselChildren** the array of carousel cards that make up a carousel -#### `CarouselHeading` -```ts -interface CarouselHeading extends Node { - type: "carousel-heading" - /** - @sparkMapNodeType textInput - */ - title: string - /** - @sparkMapNodeType textInput - */ - standfirst?: string -} -``` -**CarouselHeading** is the main header for a carousel component - #### `Carousel` ```ts /** @@ -1079,7 +1063,14 @@ interface Carousel extends Parent { * @description unique identifier required for component tracking */ id: string - heading?: CarouselHeading + /** + @sparkMapNodeType textInput + */ + title?: string + /** + @sparkMapNodeType textInput + */ + standfirst?: string children: CarouselChildren } ``` diff --git a/content-tree.d.ts b/content-tree.d.ts index b67a769..9e03533 100644 --- a/content-tree.d.ts +++ b/content-tree.d.ts @@ -473,17 +473,6 @@ export declare namespace ContentTree { * @sparkRepeater true */ type CarouselChildren = CarouselCard[]; - interface CarouselHeading extends Node { - type: "carousel-heading"; - /** - @sparkMapNodeType textInput - */ - title: string; - /** - @sparkMapNodeType textInput - */ - standfirst?: string; - } /** * @sparkGenerateStoryblock true */ @@ -493,7 +482,14 @@ export declare namespace ContentTree { * @description unique identifier required for component tracking */ id: string; - heading?: CarouselHeading; + /** + @sparkMapNodeType textInput + */ + title?: string; + /** + @sparkMapNodeType textInput + */ + standfirst?: string; children: CarouselChildren; } namespace full { @@ -971,17 +967,6 @@ export declare namespace ContentTree { * @sparkRepeater true */ type CarouselChildren = CarouselCard[]; - interface CarouselHeading extends Node { - type: "carousel-heading"; - /** - @sparkMapNodeType textInput - */ - title: string; - /** - @sparkMapNodeType textInput - */ - standfirst?: string; - } /** * @sparkGenerateStoryblock true */ @@ -991,7 +976,14 @@ export declare namespace ContentTree { * @description unique identifier required for component tracking */ id: string; - heading?: CarouselHeading; + /** + @sparkMapNodeType textInput + */ + title?: string; + /** + @sparkMapNodeType textInput + */ + standfirst?: string; children: CarouselChildren; } } @@ -1443,17 +1435,6 @@ export declare namespace ContentTree { * @sparkRepeater true */ type CarouselChildren = CarouselCard[]; - interface CarouselHeading extends Node { - type: "carousel-heading"; - /** - @sparkMapNodeType textInput - */ - title: string; - /** - @sparkMapNodeType textInput - */ - standfirst?: string; - } /** * @sparkGenerateStoryblock true */ @@ -1463,7 +1444,14 @@ export declare namespace ContentTree { * @description unique identifier required for component tracking */ id: string; - heading?: CarouselHeading; + /** + @sparkMapNodeType textInput + */ + title?: string; + /** + @sparkMapNodeType textInput + */ + standfirst?: string; children: CarouselChildren; } } @@ -1942,17 +1930,6 @@ export declare namespace ContentTree { * @sparkRepeater true */ type CarouselChildren = CarouselCard[]; - interface CarouselHeading extends Node { - type: "carousel-heading"; - /** - @sparkMapNodeType textInput - */ - title: string; - /** - @sparkMapNodeType textInput - */ - standfirst?: string; - } /** * @sparkGenerateStoryblock true */ @@ -1962,7 +1939,14 @@ export declare namespace ContentTree { * @description unique identifier required for component tracking */ id: string; - heading?: CarouselHeading; + /** + @sparkMapNodeType textInput + */ + title?: string; + /** + @sparkMapNodeType textInput + */ + standfirst?: string; children: CarouselChildren; } } diff --git a/schemas/body-tree.schema.json b/schemas/body-tree.schema.json index 58e036a..ab2f001 100644 --- a/schemas/body-tree.schema.json +++ b/schemas/body-tree.schema.json @@ -265,13 +265,16 @@ "type": "array" }, "data": {}, - "heading": { - "$ref": "#/definitions/ContentTree.transit.CarouselHeading" - }, "id": { "description": "unique identifier required for component tracking", "type": "string" }, + "standfirst": { + "type": "string" + }, + "title": { + "type": "string" + }, "type": { "const": "carousel", "type": "string" @@ -329,27 +332,6 @@ ], "type": "object" }, - "ContentTree.transit.CarouselHeading": { - "additionalProperties": false, - "properties": { - "data": {}, - "standfirst": { - "type": "string" - }, - "title": { - "type": "string" - }, - "type": { - "const": "carousel-heading", - "type": "string" - } - }, - "required": [ - "title", - "type" - ], - "type": "object" - }, "ContentTree.transit.ClipSet": { "additionalProperties": false, "properties": { diff --git a/schemas/content-tree.schema.json b/schemas/content-tree.schema.json index 0ecf377..5b85867 100644 --- a/schemas/content-tree.schema.json +++ b/schemas/content-tree.schema.json @@ -302,13 +302,16 @@ "type": "array" }, "data": {}, - "heading": { - "$ref": "#/definitions/ContentTree.full.CarouselHeading" - }, "id": { "description": "unique identifier required for component tracking", "type": "string" }, + "standfirst": { + "type": "string" + }, + "title": { + "type": "string" + }, "type": { "const": "carousel", "type": "string" @@ -366,27 +369,6 @@ ], "type": "object" }, - "ContentTree.full.CarouselHeading": { - "additionalProperties": false, - "properties": { - "data": {}, - "standfirst": { - "type": "string" - }, - "title": { - "type": "string" - }, - "type": { - "const": "carousel-heading", - "type": "string" - } - }, - "required": [ - "title", - "type" - ], - "type": "object" - }, "ContentTree.full.ClipSet": { "additionalProperties": false, "properties": { diff --git a/schemas/spark-transit-tree.schema.json b/schemas/spark-transit-tree.schema.json index 2f7698a..e57e52f 100644 --- a/schemas/spark-transit-tree.schema.json +++ b/schemas/spark-transit-tree.schema.json @@ -288,6 +288,9 @@ { "$ref": "#/definitions/ContentTree.transit.Paragraph" }, + { + "$ref": "#/definitions/ContentTree.transit.Heading" + }, { "$ref": "#/definitions/ContentTree.transit.List" }, @@ -320,13 +323,18 @@ "type": "array" }, "data": {}, - "heading": { - "$ref": "#/definitions/ContentTree.transit.CarouselHeading" - }, "id": { "description": "unique identifier required for component tracking", "type": "string" }, + "standfirst": { + "sparkMapNodeType": "textInput", + "type": "string" + }, + "title": { + "sparkMapNodeType": "textInput", + "type": "string" + }, "type": { "const": "carousel", "type": "string" @@ -335,7 +343,8 @@ "propertyOrder": [ "type", "id", - "heading", + "title", + "standfirst", "children", "data" ], @@ -405,35 +414,6 @@ ], "type": "object" }, - "ContentTree.transit.CarouselHeading": { - "additionalProperties": false, - "properties": { - "data": {}, - "standfirst": { - "sparkMapNodeType": "textInput", - "type": "string" - }, - "title": { - "sparkMapNodeType": "textInput", - "type": "string" - }, - "type": { - "const": "carousel-heading", - "type": "string" - } - }, - "propertyOrder": [ - "type", - "title", - "standfirst", - "data" - ], - "required": [ - "title", - "type" - ], - "type": "object" - }, "ContentTree.transit.ClipSet": { "additionalProperties": false, "properties": { diff --git a/schemas/transit-tree.schema.json b/schemas/transit-tree.schema.json index 96ab7b8..f9f91cb 100644 --- a/schemas/transit-tree.schema.json +++ b/schemas/transit-tree.schema.json @@ -290,13 +290,16 @@ "type": "array" }, "data": {}, - "heading": { - "$ref": "#/definitions/ContentTree.transit.CarouselHeading" - }, "id": { "description": "unique identifier required for component tracking", "type": "string" }, + "standfirst": { + "type": "string" + }, + "title": { + "type": "string" + }, "type": { "const": "carousel", "type": "string" @@ -354,27 +357,6 @@ ], "type": "object" }, - "ContentTree.transit.CarouselHeading": { - "additionalProperties": false, - "properties": { - "data": {}, - "standfirst": { - "type": "string" - }, - "title": { - "type": "string" - }, - "type": { - "const": "carousel-heading", - "type": "string" - } - }, - "required": [ - "title", - "type" - ], - "type": "object" - }, "ContentTree.transit.ClipSet": { "additionalProperties": false, "properties": { From 752f49805f5621baa8549cece707b246d44dcb76 Mon Sep 17 00:00:00 2001 From: Katie Fenn Date: Thu, 18 Jun 2026 14:32:35 +0100 Subject: [PATCH 6/6] Bump the version number --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 70e5724..62948f2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@financial-times/content-tree", "description": "content tree format", - "version": "0.13.0", + "version": "0.14.0", "publishConfig": { "access": "public" },