From f76f222878d65c62f4b0e995108cdea47c65e658 Mon Sep 17 00:00:00 2001 From: Arjun Gadhia Date: Mon, 31 Mar 2025 11:56:57 +0100 Subject: [PATCH 1/2] wip: add demo topper definition --- README.md | 61 ++++- build.bash | 1 + content-tree.d.ts | 83 ++++++ schemas/content-tree.schema.json | 164 +++++++++++ schemas/topper-tree.schema.json | 451 +++++++++++++++++++++++++++++++ schemas/transit-tree.schema.json | 160 +++++++++++ 6 files changed, 919 insertions(+), 1 deletion(-) create mode 100644 schemas/topper-tree.schema.json diff --git a/README.md b/README.md index 066c433..ea1409c 100644 --- a/README.md +++ b/README.md @@ -168,6 +168,7 @@ Its content is limited to only other content-tree content. ```ts interface Root extends Node { type: "root" + topper: Topper body: Body } ``` @@ -176,6 +177,65 @@ interface Root extends Node { **Root** can be used as the _[root][term-root]_ of a _[tree][term-tree]_. +### `Topper` + +```ts +type TopperType = +| 'DeepPortraitTopper' +| 'DeepLandscapeTopper' +| 'SplitTextTopper' +| 'FullBleedTopper' +| 'PodcastTopper' +| 'OpinionTopper' +| 'BrandedTopper' +| 'BasicTopper' +| 'TopperWithFlourish' +| 'PartnerContentTopper' +``` + +```ts +interface Topper extends Parent { + type: 'topper' + topperType: TopperType + backgroundColor: string // maybe a type? is this external?? + children: [Headline, Intro, TopperVisual?] +} +``` +**Topper** represents the topper of an article + +### Headline + +```ts +interface Headline extends Parent { + type: 'headline' + children: Text[] + external isLarge: boolean //is this external? it's based on some business logic, partly derived by topper type? should it be external? +} +``` + +**Headline** represents the title of the article as displayed on the article page + + +### Intro + +```ts +interface Intro extends Parent { + type: 'intro' + children: [Text] | (Paragraph | List)[] +} +``` +The article **Intro** can be either a one-line standfirst, or a longer summary + +### TopperVisual + +```ts +interface TopperVisual extends Parent { + type: 'topper-visual' + children: [CustomCodeComponent] | [ImageSet] // | ClipSet +} +``` +**TopperVisual** contains the visual element of the topper, which can be an image, clip, or custom component + ### `Body` ```ts @@ -748,7 +808,6 @@ interface CustomCodeComponent extends Node { - The basic interface in Spark to make reference to this system above (eg. the git repo URL or a public S3 bucket), and provide some data for it if necessary. This will be the Custom Component storyblock. - The data Spark receives from entering a specific ID will be used to render dynamic fields (the `attributes`). - ## License This software is published by the Financial Times under the [MIT licence](mit). diff --git a/build.bash b/build.bash index 7f7860a..4e39b46 100755 --- a/build.bash +++ b/build.bash @@ -4,5 +4,6 @@ tsc -d content-tree.ts typescript-json-schema --noExtraProps --required content-tree.ts ContentTree.full.Root > schemas/content-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 --noExtraProps --required content-tree.ts ContentTree.transit.Topper > schemas/topper-tree.schema.json rm content-tree.ts rm content-tree.js diff --git a/content-tree.d.ts b/content-tree.d.ts index 27c8b7d..8f8b04f 100644 --- a/content-tree.d.ts +++ b/content-tree.d.ts @@ -11,8 +11,29 @@ export declare namespace ContentTree { } interface Root extends Node { type: "root"; + topper: Topper; body: Body; } + type TopperType = 'DeepPortraitTopper' | 'DeepLandscapeTopper' | 'SplitTextTopper' | 'FullBleedTopper' | 'PodcastTopper' | 'OpinionTopper' | 'BrandedTopper' | 'BasicTopper' | 'TopperWithFlourish' | 'PartnerContentTopper'; + interface Topper extends Parent { + type: 'topper'; + topperType: TopperType; + backgroundColor: string; + children: [Headline, Intro, TopperVisual?]; + } + interface Headline extends Parent { + type: 'headline'; + children: Text[]; + isLarge: boolean; + } + interface Intro extends Parent { + type: 'intro'; + children: [Text] | (Paragraph | List)[]; + } + interface TopperVisual extends Parent { + type: 'topper-visual'; + children: [CustomCodeComponent] | [ImageSet]; + } interface Body extends Parent { type: "body"; version: number; @@ -284,8 +305,29 @@ export declare namespace ContentTree { } interface Root extends Node { type: "root"; + topper: Topper; body: Body; } + type TopperType = 'DeepPortraitTopper' | 'DeepLandscapeTopper' | 'SplitTextTopper' | 'FullBleedTopper' | 'PodcastTopper' | 'OpinionTopper' | 'BrandedTopper' | 'BasicTopper' | 'TopperWithFlourish' | 'PartnerContentTopper'; + interface Topper extends Parent { + type: 'topper'; + topperType: TopperType; + backgroundColor: string; + children: [Headline, Intro, TopperVisual?]; + } + interface Headline extends Parent { + type: 'headline'; + children: Text[]; + isLarge: boolean; + } + interface Intro extends Parent { + type: 'intro'; + children: [Text] | (Paragraph | List)[]; + } + interface TopperVisual extends Parent { + type: 'topper-visual'; + children: [CustomCodeComponent] | [ImageSet]; + } interface Body extends Parent { type: "body"; version: number; @@ -558,8 +600,28 @@ export declare namespace ContentTree { } interface Root extends Node { type: "root"; + topper: Topper; body: Body; } + type TopperType = 'DeepPortraitTopper' | 'DeepLandscapeTopper' | 'SplitTextTopper' | 'FullBleedTopper' | 'PodcastTopper' | 'OpinionTopper' | 'BrandedTopper' | 'BasicTopper' | 'TopperWithFlourish' | 'PartnerContentTopper'; + interface Topper extends Parent { + type: 'topper'; + topperType: TopperType; + backgroundColor: string; + children: [Headline, Intro, TopperVisual?]; + } + interface Headline extends Parent { + type: 'headline'; + children: Text[]; + } + interface Intro extends Parent { + type: 'intro'; + children: [Text] | (Paragraph | List)[]; + } + interface TopperVisual extends Parent { + type: 'topper-visual'; + children: [CustomCodeComponent] | [ImageSet]; + } interface Body extends Parent { type: "body"; version: number; @@ -819,8 +881,29 @@ export declare namespace ContentTree { } interface Root extends Node { type: "root"; + topper: Topper; body: Body; } + type TopperType = 'DeepPortraitTopper' | 'DeepLandscapeTopper' | 'SplitTextTopper' | 'FullBleedTopper' | 'PodcastTopper' | 'OpinionTopper' | 'BrandedTopper' | 'BasicTopper' | 'TopperWithFlourish' | 'PartnerContentTopper'; + interface Topper extends Parent { + type: 'topper'; + topperType: TopperType; + backgroundColor: string; + children: [Headline, Intro, TopperVisual?]; + } + interface Headline extends Parent { + type: 'headline'; + children: Text[]; + isLarge?: boolean; + } + interface Intro extends Parent { + type: 'intro'; + children: [Text] | (Paragraph | List)[]; + } + interface TopperVisual extends Parent { + type: 'topper-visual'; + children: [CustomCodeComponent] | [ImageSet]; + } interface Body extends Parent { type: "body"; version: number; diff --git a/schemas/content-tree.schema.json b/schemas/content-tree.schema.json index 7a976f7..52d4067 100644 --- a/schemas/content-tree.schema.json +++ b/schemas/content-tree.schema.json @@ -355,6 +355,31 @@ ], "type": "object" }, + "ContentTree.full.Headline": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.full.Text" + }, + "type": "array" + }, + "data": {}, + "isLarge": { + "type": "boolean" + }, + "type": { + "const": "headline", + "type": "string" + } + }, + "required": [ + "children", + "isLarge", + "type" + ], + "type": "object" + }, "ContentTree.full.ImageSet": { "additionalProperties": false, "properties": { @@ -531,6 +556,48 @@ ], "type": "object" }, + "ContentTree.full.Intro": { + "additionalProperties": false, + "properties": { + "children": { + "anyOf": [ + { + "items": [ + { + "$ref": "#/definitions/ContentTree.full.Text" + } + ], + "maxItems": 1, + "minItems": 1, + "type": "array" + }, + { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/ContentTree.full.Paragraph" + }, + { + "$ref": "#/definitions/ContentTree.full.List" + } + ] + }, + "type": "array" + } + ] + }, + "data": {}, + "type": { + "const": "intro", + "type": "string" + } + }, + "required": [ + "children", + "type" + ], + "type": "object" + }, "ContentTree.full.Layout": { "additionalProperties": false, "properties": { @@ -1839,6 +1906,99 @@ ], "type": "object" }, + "ContentTree.full.Topper": { + "additionalProperties": false, + "properties": { + "backgroundColor": { + "type": "string" + }, + "children": { + "items": [ + { + "$ref": "#/definitions/ContentTree.full.Headline" + }, + { + "$ref": "#/definitions/ContentTree.full.Intro" + }, + { + "$ref": "#/definitions/ContentTree.full.TopperVisual" + } + ], + "maxItems": 3, + "minItems": 2, + "type": "array" + }, + "data": {}, + "topperType": { + "$ref": "#/definitions/ContentTree.full.TopperType" + }, + "type": { + "const": "topper", + "type": "string" + } + }, + "required": [ + "backgroundColor", + "children", + "topperType", + "type" + ], + "type": "object" + }, + "ContentTree.full.TopperType": { + "enum": [ + "BasicTopper", + "BrandedTopper", + "DeepLandscapeTopper", + "DeepPortraitTopper", + "FullBleedTopper", + "OpinionTopper", + "PartnerContentTopper", + "PodcastTopper", + "SplitTextTopper", + "TopperWithFlourish" + ], + "type": "string" + }, + "ContentTree.full.TopperVisual": { + "additionalProperties": false, + "properties": { + "children": { + "anyOf": [ + { + "items": [ + { + "$ref": "#/definitions/ContentTree.full.CustomCodeComponent" + } + ], + "maxItems": 1, + "minItems": 1, + "type": "array" + }, + { + "items": [ + { + "$ref": "#/definitions/ContentTree.full.ImageSet" + } + ], + "maxItems": 1, + "minItems": 1, + "type": "array" + } + ] + }, + "data": {}, + "type": { + "const": "topper-visual", + "type": "string" + } + }, + "required": [ + "children", + "type" + ], + "type": "object" + }, "ContentTree.full.Tweet": { "additionalProperties": false, "properties": { @@ -1907,6 +2067,9 @@ "$ref": "#/definitions/ContentTree.full.Body" }, "data": {}, + "topper": { + "$ref": "#/definitions/ContentTree.full.Topper" + }, "type": { "const": "root", "type": "string" @@ -1914,6 +2077,7 @@ }, "required": [ "body", + "topper", "type" ], "type": "object" diff --git a/schemas/topper-tree.schema.json b/schemas/topper-tree.schema.json new file mode 100644 index 0000000..4511b18 --- /dev/null +++ b/schemas/topper-tree.schema.json @@ -0,0 +1,451 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "definitions": { + "ContentTree.transit.Break": { + "additionalProperties": false, + "properties": { + "data": {}, + "type": { + "const": "break", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "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" + } + }, + "required": [ + "id", + "layoutWidth", + "type" + ], + "type": "object" + }, + "ContentTree.transit.Emphasis": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.Phrasing" + }, + "type": "array" + }, + "data": {}, + "type": { + "const": "emphasis", + "type": "string" + } + }, + "required": [ + "children", + "type" + ], + "type": "object" + }, + "ContentTree.transit.Headline": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.Text" + }, + "type": "array" + }, + "data": {}, + "type": { + "const": "headline", + "type": "string" + } + }, + "required": [ + "children", + "type" + ], + "type": "object" + }, + "ContentTree.transit.ImageSet": { + "additionalProperties": false, + "properties": { + "data": {}, + "id": { + "type": "string" + }, + "type": { + "const": "image-set", + "type": "string" + } + }, + "required": [ + "id", + "type" + ], + "type": "object" + }, + "ContentTree.transit.Intro": { + "additionalProperties": false, + "properties": { + "children": { + "anyOf": [ + { + "items": [ + { + "$ref": "#/definitions/ContentTree.transit.Text" + } + ], + "maxItems": 1, + "minItems": 1, + "type": "array" + }, + { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/ContentTree.transit.Paragraph" + }, + { + "$ref": "#/definitions/ContentTree.transit.List" + } + ] + }, + "type": "array" + } + ] + }, + "data": {}, + "type": { + "const": "intro", + "type": "string" + } + }, + "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" + } + }, + "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" + } + }, + "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.Link" + } + ] + }, + "type": "array" + }, + "data": {}, + "type": { + "const": "list-item", + "type": "string" + } + }, + "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" + } + }, + "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.Link" + } + ] + }, + "ContentTree.transit.Strikethrough": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.Phrasing" + }, + "type": "array" + }, + "data": {}, + "type": { + "const": "strikethrough", + "type": "string" + } + }, + "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" + } + }, + "required": [ + "children", + "type" + ], + "type": "object" + }, + "ContentTree.transit.Text": { + "additionalProperties": false, + "properties": { + "data": {}, + "type": { + "const": "text", + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [ + "type", + "value" + ], + "type": "object" + }, + "ContentTree.transit.TopperType": { + "enum": [ + "BasicTopper", + "BrandedTopper", + "DeepLandscapeTopper", + "DeepPortraitTopper", + "FullBleedTopper", + "OpinionTopper", + "PartnerContentTopper", + "PodcastTopper", + "SplitTextTopper", + "TopperWithFlourish" + ], + "type": "string" + }, + "ContentTree.transit.TopperVisual": { + "additionalProperties": false, + "properties": { + "children": { + "anyOf": [ + { + "items": [ + { + "$ref": "#/definitions/ContentTree.transit.CustomCodeComponent" + } + ], + "maxItems": 1, + "minItems": 1, + "type": "array" + }, + { + "items": [ + { + "$ref": "#/definitions/ContentTree.transit.ImageSet" + } + ], + "maxItems": 1, + "minItems": 1, + "type": "array" + } + ] + }, + "data": {}, + "type": { + "const": "topper-visual", + "type": "string" + } + }, + "required": [ + "children", + "type" + ], + "type": "object" + } + }, + "properties": { + "backgroundColor": { + "type": "string" + }, + "children": { + "items": [ + { + "$ref": "#/definitions/ContentTree.transit.Headline" + }, + { + "$ref": "#/definitions/ContentTree.transit.Intro" + }, + { + "$ref": "#/definitions/ContentTree.transit.TopperVisual" + } + ], + "maxItems": 3, + "minItems": 2, + "type": "array" + }, + "data": {}, + "topperType": { + "$ref": "#/definitions/ContentTree.transit.TopperType" + }, + "type": { + "const": "topper", + "type": "string" + } + }, + "required": [ + "backgroundColor", + "children", + "topperType", + "type" + ], + "type": "object" +} + diff --git a/schemas/transit-tree.schema.json b/schemas/transit-tree.schema.json index 1152a2b..1c564c5 100644 --- a/schemas/transit-tree.schema.json +++ b/schemas/transit-tree.schema.json @@ -329,6 +329,27 @@ ], "type": "object" }, + "ContentTree.transit.Headline": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.Text" + }, + "type": "array" + }, + "data": {}, + "type": { + "const": "headline", + "type": "string" + } + }, + "required": [ + "children", + "type" + ], + "type": "object" + }, "ContentTree.transit.ImageSet": { "additionalProperties": false, "properties": { @@ -347,6 +368,48 @@ ], "type": "object" }, + "ContentTree.transit.Intro": { + "additionalProperties": false, + "properties": { + "children": { + "anyOf": [ + { + "items": [ + { + "$ref": "#/definitions/ContentTree.transit.Text" + } + ], + "maxItems": 1, + "minItems": 1, + "type": "array" + }, + { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/ContentTree.transit.Paragraph" + }, + { + "$ref": "#/definitions/ContentTree.transit.List" + } + ] + }, + "type": "array" + } + ] + }, + "data": {}, + "type": { + "const": "intro", + "type": "string" + } + }, + "required": [ + "children", + "type" + ], + "type": "object" + }, "ContentTree.transit.Layout": { "additionalProperties": false, "properties": { @@ -1146,6 +1209,99 @@ ], "type": "object" }, + "ContentTree.transit.Topper": { + "additionalProperties": false, + "properties": { + "backgroundColor": { + "type": "string" + }, + "children": { + "items": [ + { + "$ref": "#/definitions/ContentTree.transit.Headline" + }, + { + "$ref": "#/definitions/ContentTree.transit.Intro" + }, + { + "$ref": "#/definitions/ContentTree.transit.TopperVisual" + } + ], + "maxItems": 3, + "minItems": 2, + "type": "array" + }, + "data": {}, + "topperType": { + "$ref": "#/definitions/ContentTree.transit.TopperType" + }, + "type": { + "const": "topper", + "type": "string" + } + }, + "required": [ + "backgroundColor", + "children", + "topperType", + "type" + ], + "type": "object" + }, + "ContentTree.transit.TopperType": { + "enum": [ + "BasicTopper", + "BrandedTopper", + "DeepLandscapeTopper", + "DeepPortraitTopper", + "FullBleedTopper", + "OpinionTopper", + "PartnerContentTopper", + "PodcastTopper", + "SplitTextTopper", + "TopperWithFlourish" + ], + "type": "string" + }, + "ContentTree.transit.TopperVisual": { + "additionalProperties": false, + "properties": { + "children": { + "anyOf": [ + { + "items": [ + { + "$ref": "#/definitions/ContentTree.transit.CustomCodeComponent" + } + ], + "maxItems": 1, + "minItems": 1, + "type": "array" + }, + { + "items": [ + { + "$ref": "#/definitions/ContentTree.transit.ImageSet" + } + ], + "maxItems": 1, + "minItems": 1, + "type": "array" + } + ] + }, + "data": {}, + "type": { + "const": "topper-visual", + "type": "string" + } + }, + "required": [ + "children", + "type" + ], + "type": "object" + }, "ContentTree.transit.Tweet": { "additionalProperties": false, "properties": { @@ -1210,6 +1366,9 @@ "$ref": "#/definitions/ContentTree.transit.Body" }, "data": {}, + "topper": { + "$ref": "#/definitions/ContentTree.transit.Topper" + }, "type": { "const": "root", "type": "string" @@ -1217,6 +1376,7 @@ }, "required": [ "body", + "topper", "type" ], "type": "object" From 93d8886d06f48a49fec8076f76ceeda8b98a2060 Mon Sep 17 00:00:00 2001 From: Arjun Gadhia Date: Wed, 2 Apr 2025 12:07:56 +0100 Subject: [PATCH 2/2] wip: try out having the topper fields as properties rather than children --- README.md | 50 +++++------ content-tree.d.ts | 67 +++++++-------- schemas/content-tree.schema.json | 139 ++++++++++++++++--------------- schemas/topper-tree.schema.json | 105 +++++++---------------- schemas/transit-tree.schema.json | 101 +++++++--------------- 5 files changed, 183 insertions(+), 279 deletions(-) diff --git a/README.md b/README.md index ea1409c..5e6d193 100644 --- a/README.md +++ b/README.md @@ -180,25 +180,29 @@ interface Root extends Node { ### `Topper` ```ts -type TopperType = -| 'DeepPortraitTopper' -| 'DeepLandscapeTopper' -| 'SplitTextTopper' -| 'FullBleedTopper' -| 'PodcastTopper' -| 'OpinionTopper' -| 'BrandedTopper' -| 'BasicTopper' -| 'TopperWithFlourish' -| 'PartnerContentTopper' -``` - -```ts -interface Topper extends Parent { +type TopperLayout = +| 'deep-portrait' +| 'deep-landscape' +| 'split-text-left' +| 'full-bleed' +// I think the things below are based on other factors, so maybe shouldn't be published?? +// | 'PodcastTopper' +// | 'OpinionTopper' +// | 'BrandedTopper' +// | 'BasicTopper' +// | 'TopperWithFlourish' +// | 'PartnerContentTopper' +``` + +```ts +interface Topper extends Node { type: 'topper' - topperType: TopperType - backgroundColor: string // maybe a type? is this external?? - children: [Headline, Intro, TopperVisual?] + suggestedTopperLayout: TopperLayout + suggestedBackgroundColor: string // this is what editorial select, but can be overridden based on other content properties + headline: Headline + intro: Intro + visual: CustomCodeComponent | ImageSet // | ClipSet + external displayConcept: TeaserConcept } ``` **Topper** represents the topper of an article @@ -226,16 +230,6 @@ interface Intro extends Parent { ``` The article **Intro** can be either a one-line standfirst, or a longer summary -### TopperVisual - -```ts -interface TopperVisual extends Parent { - type: 'topper-visual' - children: [CustomCodeComponent] | [ImageSet] // | ClipSet -} -``` -**TopperVisual** contains the visual element of the topper, which can be an image, clip, or custom component - ### `Body` ```ts diff --git a/content-tree.d.ts b/content-tree.d.ts index 8f8b04f..3912095 100644 --- a/content-tree.d.ts +++ b/content-tree.d.ts @@ -14,12 +14,15 @@ export declare namespace ContentTree { topper: Topper; body: Body; } - type TopperType = 'DeepPortraitTopper' | 'DeepLandscapeTopper' | 'SplitTextTopper' | 'FullBleedTopper' | 'PodcastTopper' | 'OpinionTopper' | 'BrandedTopper' | 'BasicTopper' | 'TopperWithFlourish' | 'PartnerContentTopper'; - interface Topper extends Parent { + type TopperLayout = 'deep-portrait' | 'deep-landscape' | 'split-text-left' | 'full-bleed'; + interface Topper extends Node { type: 'topper'; - topperType: TopperType; - backgroundColor: string; - children: [Headline, Intro, TopperVisual?]; + suggestedTopperLayout: TopperLayout; + suggestedBackgroundColor: string; + headline: Headline; + intro: Intro; + visual: CustomCodeComponent | ImageSet; + displayConcept: TeaserConcept; } interface Headline extends Parent { type: 'headline'; @@ -30,10 +33,6 @@ export declare namespace ContentTree { type: 'intro'; children: [Text] | (Paragraph | List)[]; } - interface TopperVisual extends Parent { - type: 'topper-visual'; - children: [CustomCodeComponent] | [ImageSet]; - } interface Body extends Parent { type: "body"; version: number; @@ -308,12 +307,15 @@ export declare namespace ContentTree { topper: Topper; body: Body; } - type TopperType = 'DeepPortraitTopper' | 'DeepLandscapeTopper' | 'SplitTextTopper' | 'FullBleedTopper' | 'PodcastTopper' | 'OpinionTopper' | 'BrandedTopper' | 'BasicTopper' | 'TopperWithFlourish' | 'PartnerContentTopper'; - interface Topper extends Parent { + type TopperLayout = 'deep-portrait' | 'deep-landscape' | 'split-text-left' | 'full-bleed'; + interface Topper extends Node { type: 'topper'; - topperType: TopperType; - backgroundColor: string; - children: [Headline, Intro, TopperVisual?]; + suggestedTopperLayout: TopperLayout; + suggestedBackgroundColor: string; + headline: Headline; + intro: Intro; + visual: CustomCodeComponent | ImageSet; + displayConcept: TeaserConcept; } interface Headline extends Parent { type: 'headline'; @@ -324,10 +326,6 @@ export declare namespace ContentTree { type: 'intro'; children: [Text] | (Paragraph | List)[]; } - interface TopperVisual extends Parent { - type: 'topper-visual'; - children: [CustomCodeComponent] | [ImageSet]; - } interface Body extends Parent { type: "body"; version: number; @@ -603,12 +601,14 @@ export declare namespace ContentTree { topper: Topper; body: Body; } - type TopperType = 'DeepPortraitTopper' | 'DeepLandscapeTopper' | 'SplitTextTopper' | 'FullBleedTopper' | 'PodcastTopper' | 'OpinionTopper' | 'BrandedTopper' | 'BasicTopper' | 'TopperWithFlourish' | 'PartnerContentTopper'; - interface Topper extends Parent { + type TopperLayout = 'deep-portrait' | 'deep-landscape' | 'split-text-left' | 'full-bleed'; + interface Topper extends Node { type: 'topper'; - topperType: TopperType; - backgroundColor: string; - children: [Headline, Intro, TopperVisual?]; + suggestedTopperLayout: TopperLayout; + suggestedBackgroundColor: string; + headline: Headline; + intro: Intro; + visual: CustomCodeComponent | ImageSet; } interface Headline extends Parent { type: 'headline'; @@ -618,10 +618,6 @@ export declare namespace ContentTree { type: 'intro'; children: [Text] | (Paragraph | List)[]; } - interface TopperVisual extends Parent { - type: 'topper-visual'; - children: [CustomCodeComponent] | [ImageSet]; - } interface Body extends Parent { type: "body"; version: number; @@ -884,12 +880,15 @@ export declare namespace ContentTree { topper: Topper; body: Body; } - type TopperType = 'DeepPortraitTopper' | 'DeepLandscapeTopper' | 'SplitTextTopper' | 'FullBleedTopper' | 'PodcastTopper' | 'OpinionTopper' | 'BrandedTopper' | 'BasicTopper' | 'TopperWithFlourish' | 'PartnerContentTopper'; - interface Topper extends Parent { + type TopperLayout = 'deep-portrait' | 'deep-landscape' | 'split-text-left' | 'full-bleed'; + interface Topper extends Node { type: 'topper'; - topperType: TopperType; - backgroundColor: string; - children: [Headline, Intro, TopperVisual?]; + suggestedTopperLayout: TopperLayout; + suggestedBackgroundColor: string; + headline: Headline; + intro: Intro; + visual: CustomCodeComponent | ImageSet; + displayConcept?: TeaserConcept; } interface Headline extends Parent { type: 'headline'; @@ -900,10 +899,6 @@ export declare namespace ContentTree { type: 'intro'; children: [Text] | (Paragraph | List)[]; } - interface TopperVisual extends Parent { - type: 'topper-visual'; - children: [CustomCodeComponent] | [ImageSet]; - } interface Body extends Parent { type: "body"; version: number; diff --git a/schemas/content-tree.schema.json b/schemas/content-tree.schema.json index 52d4067..792539a 100644 --- a/schemas/content-tree.schema.json +++ b/schemas/content-tree.schema.json @@ -1909,96 +1909,97 @@ "ContentTree.full.Topper": { "additionalProperties": false, "properties": { - "backgroundColor": { - "type": "string" - }, - "children": { - "items": [ - { - "$ref": "#/definitions/ContentTree.full.Headline" + "data": {}, + "displayConcept": { + "additionalProperties": false, + "properties": { + "apiUrl": { + "type": "string" }, - { - "$ref": "#/definitions/ContentTree.full.Intro" + "directType": { + "type": "string" }, - { - "$ref": "#/definitions/ContentTree.full.TopperVisual" + "id": { + "type": "string" + }, + "predicate": { + "type": "string" + }, + "prefLabel": { + "type": "string" + }, + "type": { + "type": "string" + }, + "types": { + "items": { + "type": "string" + }, + "type": "array" + }, + "url": { + "type": "string" } + }, + "required": [ + "apiUrl", + "directType", + "id", + "predicate", + "prefLabel", + "type", + "types", + "url" ], - "maxItems": 3, - "minItems": 2, - "type": "array" + "type": "object" }, - "data": {}, - "topperType": { - "$ref": "#/definitions/ContentTree.full.TopperType" + "headline": { + "$ref": "#/definitions/ContentTree.full.Headline" + }, + "intro": { + "$ref": "#/definitions/ContentTree.full.Intro" + }, + "suggestedBackgroundColor": { + "type": "string" + }, + "suggestedTopperLayout": { + "$ref": "#/definitions/ContentTree.full.TopperLayout" }, "type": { "const": "topper", "type": "string" - } - }, - "required": [ - "backgroundColor", - "children", - "topperType", - "type" - ], - "type": "object" - }, - "ContentTree.full.TopperType": { - "enum": [ - "BasicTopper", - "BrandedTopper", - "DeepLandscapeTopper", - "DeepPortraitTopper", - "FullBleedTopper", - "OpinionTopper", - "PartnerContentTopper", - "PodcastTopper", - "SplitTextTopper", - "TopperWithFlourish" - ], - "type": "string" - }, - "ContentTree.full.TopperVisual": { - "additionalProperties": false, - "properties": { - "children": { + }, + "visual": { "anyOf": [ { - "items": [ - { - "$ref": "#/definitions/ContentTree.full.CustomCodeComponent" - } - ], - "maxItems": 1, - "minItems": 1, - "type": "array" + "$ref": "#/definitions/ContentTree.full.ImageSet" }, { - "items": [ - { - "$ref": "#/definitions/ContentTree.full.ImageSet" - } - ], - "maxItems": 1, - "minItems": 1, - "type": "array" + "$ref": "#/definitions/ContentTree.full.CustomCodeComponent" } ] - }, - "data": {}, - "type": { - "const": "topper-visual", - "type": "string" } }, "required": [ - "children", - "type" + "displayConcept", + "headline", + "intro", + "suggestedBackgroundColor", + "suggestedTopperLayout", + "type", + "visual" ], "type": "object" }, + "ContentTree.full.TopperLayout": { + "enum": [ + "deep-landscape", + "deep-portrait", + "full-bleed", + "split-text-left" + ], + "type": "string" + }, "ContentTree.full.Tweet": { "additionalProperties": false, "properties": { diff --git a/schemas/topper-tree.schema.json b/schemas/topper-tree.schema.json index 4511b18..aa1da17 100644 --- a/schemas/topper-tree.schema.json +++ b/schemas/topper-tree.schema.json @@ -356,95 +356,52 @@ ], "type": "object" }, - "ContentTree.transit.TopperType": { + "ContentTree.transit.TopperLayout": { "enum": [ - "BasicTopper", - "BrandedTopper", - "DeepLandscapeTopper", - "DeepPortraitTopper", - "FullBleedTopper", - "OpinionTopper", - "PartnerContentTopper", - "PodcastTopper", - "SplitTextTopper", - "TopperWithFlourish" + "deep-landscape", + "deep-portrait", + "full-bleed", + "split-text-left" ], "type": "string" - }, - "ContentTree.transit.TopperVisual": { - "additionalProperties": false, - "properties": { - "children": { - "anyOf": [ - { - "items": [ - { - "$ref": "#/definitions/ContentTree.transit.CustomCodeComponent" - } - ], - "maxItems": 1, - "minItems": 1, - "type": "array" - }, - { - "items": [ - { - "$ref": "#/definitions/ContentTree.transit.ImageSet" - } - ], - "maxItems": 1, - "minItems": 1, - "type": "array" - } - ] - }, - "data": {}, - "type": { - "const": "topper-visual", - "type": "string" - } - }, - "required": [ - "children", - "type" - ], - "type": "object" } }, "properties": { - "backgroundColor": { - "type": "string" + "data": {}, + "headline": { + "$ref": "#/definitions/ContentTree.transit.Headline" }, - "children": { - "items": [ - { - "$ref": "#/definitions/ContentTree.transit.Headline" - }, - { - "$ref": "#/definitions/ContentTree.transit.Intro" - }, - { - "$ref": "#/definitions/ContentTree.transit.TopperVisual" - } - ], - "maxItems": 3, - "minItems": 2, - "type": "array" + "intro": { + "$ref": "#/definitions/ContentTree.transit.Intro" }, - "data": {}, - "topperType": { - "$ref": "#/definitions/ContentTree.transit.TopperType" + "suggestedBackgroundColor": { + "type": "string" + }, + "suggestedTopperLayout": { + "$ref": "#/definitions/ContentTree.transit.TopperLayout" }, "type": { "const": "topper", "type": "string" + }, + "visual": { + "anyOf": [ + { + "$ref": "#/definitions/ContentTree.transit.ImageSet" + }, + { + "$ref": "#/definitions/ContentTree.transit.CustomCodeComponent" + } + ] } }, "required": [ - "backgroundColor", - "children", - "topperType", - "type" + "headline", + "intro", + "suggestedBackgroundColor", + "suggestedTopperLayout", + "type", + "visual" ], "type": "object" } diff --git a/schemas/transit-tree.schema.json b/schemas/transit-tree.schema.json index 1c564c5..312e25c 100644 --- a/schemas/transit-tree.schema.json +++ b/schemas/transit-tree.schema.json @@ -1212,96 +1212,53 @@ "ContentTree.transit.Topper": { "additionalProperties": false, "properties": { - "backgroundColor": { - "type": "string" + "data": {}, + "headline": { + "$ref": "#/definitions/ContentTree.transit.Headline" }, - "children": { - "items": [ - { - "$ref": "#/definitions/ContentTree.transit.Headline" - }, - { - "$ref": "#/definitions/ContentTree.transit.Intro" - }, - { - "$ref": "#/definitions/ContentTree.transit.TopperVisual" - } - ], - "maxItems": 3, - "minItems": 2, - "type": "array" + "intro": { + "$ref": "#/definitions/ContentTree.transit.Intro" }, - "data": {}, - "topperType": { - "$ref": "#/definitions/ContentTree.transit.TopperType" + "suggestedBackgroundColor": { + "type": "string" + }, + "suggestedTopperLayout": { + "$ref": "#/definitions/ContentTree.transit.TopperLayout" }, "type": { "const": "topper", "type": "string" - } - }, - "required": [ - "backgroundColor", - "children", - "topperType", - "type" - ], - "type": "object" - }, - "ContentTree.transit.TopperType": { - "enum": [ - "BasicTopper", - "BrandedTopper", - "DeepLandscapeTopper", - "DeepPortraitTopper", - "FullBleedTopper", - "OpinionTopper", - "PartnerContentTopper", - "PodcastTopper", - "SplitTextTopper", - "TopperWithFlourish" - ], - "type": "string" - }, - "ContentTree.transit.TopperVisual": { - "additionalProperties": false, - "properties": { - "children": { + }, + "visual": { "anyOf": [ { - "items": [ - { - "$ref": "#/definitions/ContentTree.transit.CustomCodeComponent" - } - ], - "maxItems": 1, - "minItems": 1, - "type": "array" + "$ref": "#/definitions/ContentTree.transit.ImageSet" }, { - "items": [ - { - "$ref": "#/definitions/ContentTree.transit.ImageSet" - } - ], - "maxItems": 1, - "minItems": 1, - "type": "array" + "$ref": "#/definitions/ContentTree.transit.CustomCodeComponent" } ] - }, - "data": {}, - "type": { - "const": "topper-visual", - "type": "string" } }, "required": [ - "children", - "type" + "headline", + "intro", + "suggestedBackgroundColor", + "suggestedTopperLayout", + "type", + "visual" ], "type": "object" }, + "ContentTree.transit.TopperLayout": { + "enum": [ + "deep-landscape", + "deep-portrait", + "full-bleed", + "split-text-left" + ], + "type": "string" + }, "ContentTree.transit.Tweet": { "additionalProperties": false, "properties": {