diff --git a/README.md b/README.md index 066c433..5e6d193 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,59 @@ interface Root extends Node { **Root** can be used as the _[root][term-root]_ of a _[tree][term-tree]_. +### `Topper` + +```ts +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' + 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 + +### 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 + ### `Body` ```ts @@ -748,7 +802,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..3912095 100644 --- a/content-tree.d.ts +++ b/content-tree.d.ts @@ -11,8 +11,28 @@ export declare namespace ContentTree { } interface Root extends Node { type: "root"; + topper: Topper; body: Body; } + type TopperLayout = 'deep-portrait' | 'deep-landscape' | 'split-text-left' | 'full-bleed'; + interface Topper extends Node { + type: 'topper'; + suggestedTopperLayout: TopperLayout; + suggestedBackgroundColor: string; + headline: Headline; + intro: Intro; + visual: CustomCodeComponent | ImageSet; + displayConcept: TeaserConcept; + } + interface Headline extends Parent { + type: 'headline'; + children: Text[]; + isLarge: boolean; + } + interface Intro extends Parent { + type: 'intro'; + children: [Text] | (Paragraph | List)[]; + } interface Body extends Parent { type: "body"; version: number; @@ -284,8 +304,28 @@ export declare namespace ContentTree { } interface Root extends Node { type: "root"; + topper: Topper; body: Body; } + type TopperLayout = 'deep-portrait' | 'deep-landscape' | 'split-text-left' | 'full-bleed'; + interface Topper extends Node { + type: 'topper'; + suggestedTopperLayout: TopperLayout; + suggestedBackgroundColor: string; + headline: Headline; + intro: Intro; + visual: CustomCodeComponent | ImageSet; + displayConcept: TeaserConcept; + } + interface Headline extends Parent { + type: 'headline'; + children: Text[]; + isLarge: boolean; + } + interface Intro extends Parent { + type: 'intro'; + children: [Text] | (Paragraph | List)[]; + } interface Body extends Parent { type: "body"; version: number; @@ -558,8 +598,26 @@ export declare namespace ContentTree { } interface Root extends Node { type: "root"; + topper: Topper; body: Body; } + type TopperLayout = 'deep-portrait' | 'deep-landscape' | 'split-text-left' | 'full-bleed'; + interface Topper extends Node { + type: 'topper'; + suggestedTopperLayout: TopperLayout; + suggestedBackgroundColor: string; + headline: Headline; + intro: Intro; + visual: CustomCodeComponent | ImageSet; + } + interface Headline extends Parent { + type: 'headline'; + children: Text[]; + } + interface Intro extends Parent { + type: 'intro'; + children: [Text] | (Paragraph | List)[]; + } interface Body extends Parent { type: "body"; version: number; @@ -819,8 +877,28 @@ export declare namespace ContentTree { } interface Root extends Node { type: "root"; + topper: Topper; body: Body; } + type TopperLayout = 'deep-portrait' | 'deep-landscape' | 'split-text-left' | 'full-bleed'; + interface Topper extends Node { + type: 'topper'; + suggestedTopperLayout: TopperLayout; + suggestedBackgroundColor: string; + headline: Headline; + intro: Intro; + visual: CustomCodeComponent | ImageSet; + displayConcept?: TeaserConcept; + } + interface Headline extends Parent { + type: 'headline'; + children: Text[]; + isLarge?: boolean; + } + interface Intro extends Parent { + type: 'intro'; + children: [Text] | (Paragraph | List)[]; + } interface Body extends Parent { type: "body"; version: number; diff --git a/schemas/content-tree.schema.json b/schemas/content-tree.schema.json index 7a976f7..792539a 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,100 @@ ], "type": "object" }, + "ContentTree.full.Topper": { + "additionalProperties": false, + "properties": { + "data": {}, + "displayConcept": { + "additionalProperties": false, + "properties": { + "apiUrl": { + "type": "string" + }, + "directType": { + "type": "string" + }, + "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" + ], + "type": "object" + }, + "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" + }, + "visual": { + "anyOf": [ + { + "$ref": "#/definitions/ContentTree.full.ImageSet" + }, + { + "$ref": "#/definitions/ContentTree.full.CustomCodeComponent" + } + ] + } + }, + "required": [ + "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": { @@ -1907,6 +2068,9 @@ "$ref": "#/definitions/ContentTree.full.Body" }, "data": {}, + "topper": { + "$ref": "#/definitions/ContentTree.full.Topper" + }, "type": { "const": "root", "type": "string" @@ -1914,6 +2078,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..aa1da17 --- /dev/null +++ b/schemas/topper-tree.schema.json @@ -0,0 +1,408 @@ +{ + "$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.TopperLayout": { + "enum": [ + "deep-landscape", + "deep-portrait", + "full-bleed", + "split-text-left" + ], + "type": "string" + } + }, + "properties": { + "data": {}, + "headline": { + "$ref": "#/definitions/ContentTree.transit.Headline" + }, + "intro": { + "$ref": "#/definitions/ContentTree.transit.Intro" + }, + "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": [ + "headline", + "intro", + "suggestedBackgroundColor", + "suggestedTopperLayout", + "type", + "visual" + ], + "type": "object" +} + diff --git a/schemas/transit-tree.schema.json b/schemas/transit-tree.schema.json index 1152a2b..312e25c 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,56 @@ ], "type": "object" }, + "ContentTree.transit.Topper": { + "additionalProperties": false, + "properties": { + "data": {}, + "headline": { + "$ref": "#/definitions/ContentTree.transit.Headline" + }, + "intro": { + "$ref": "#/definitions/ContentTree.transit.Intro" + }, + "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": [ + "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": { @@ -1210,6 +1323,9 @@ "$ref": "#/definitions/ContentTree.transit.Body" }, "data": {}, + "topper": { + "$ref": "#/definitions/ContentTree.transit.Topper" + }, "type": { "const": "root", "type": "string" @@ -1217,6 +1333,7 @@ }, "required": [ "body", + "topper", "type" ], "type": "object"