@weatherboard/cms 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -0
- package/dist/portable/sections.d.cts +136 -0
- package/dist/portable/sections.d.mts +136 -0
- package/dist/portable/sections.d.ts +136 -0
- package/dist/portable/site-content.d.cts +13 -0
- package/dist/portable/site-content.d.mts +13 -0
- package/dist/portable/site-content.d.ts +13 -0
- package/dist/site-content.cjs +3 -3
- package/dist/site-content.cjs.map +4 -4
- package/dist/site-content.d.cts +2 -0
- package/dist/site-content.d.mts +2 -0
- package/dist/site-content.d.ts +2 -0
- package/dist/site-content.mjs +3 -3
- package/dist/site-content.mjs.map +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -50,6 +50,57 @@ const resolved = parseSiteHero({
|
|
|
50
50
|
}, fallback);
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
+
## Page sections (0.3.0)
|
|
54
|
+
|
|
55
|
+
`parseContentPage` (and `contentPageQuery`, `contentPagesQuery`) read the
|
|
56
|
+
optional `page.sections[]` that `@weatherboard/sanity-schema-base` declares
|
|
57
|
+
(A-1889). Each entry maps to a `kind`, listed in `READER_SECTION_KINDS`:
|
|
58
|
+
|
|
59
|
+
| Sanity `_type` | `kind` | composition | default layout |
|
|
60
|
+
| --- | --- | --- | --- |
|
|
61
|
+
| `featureStoriesBlock`, `surfaceCardBlock`, `accentBandBlock`, `locationsBlock` | `feature_stories`, `cta`, `accent_band`, `locations` | none | none |
|
|
62
|
+
| `textMediaBlock` | `text_media` | Split | `media-end` |
|
|
63
|
+
| `faqBlock` (items `faqItem`) | `faq` | HeadingList | `beside` |
|
|
64
|
+
| `galleryBlock` | `gallery` | StaggerRow | `staggered` |
|
|
65
|
+
| `contactBlock` | `contact` | Split | `media-end` |
|
|
66
|
+
|
|
67
|
+
The four home kinds read exactly as they do on the home page. The new kinds
|
|
68
|
+
return every key, `null` where there is nothing, with images as
|
|
69
|
+
`SectionImageInput` (`asset` id, `alt`, `caption`, `hotspot` `{ x, y }`).
|
|
70
|
+
|
|
71
|
+
The layout values are the A-1876 variant names, exported as `SPLIT_LAYOUTS`
|
|
72
|
+
(`media-end`, `media-start`, `media-below`), `HEADING_LIST_LAYOUTS` (`beside`,
|
|
73
|
+
`stacked`) and `STAGGER_ROW_LAYOUTS` (`staggered`, `aligned`), with the defaults
|
|
74
|
+
in `SECTION_LAYOUT_DEFAULTS`. A later contract test can compare them with the UI
|
|
75
|
+
package's lists.
|
|
76
|
+
|
|
77
|
+
`PageContent.sections` and `PageContent.warnings` are present only when the
|
|
78
|
+
stored page has a `sections` list, so a page without one reads exactly as it did
|
|
79
|
+
in 0.2.0. Each warning is `{ code, where }`, a code from `SECTION_WARNINGS` and
|
|
80
|
+
the path in the stored document, never copy:
|
|
81
|
+
|
|
82
|
+
- `section.layout.defaulted`: missing or unknown `layout`; the composition
|
|
83
|
+
default renders. It never throws and never yields an empty section.
|
|
84
|
+
- `section.type.unknown`: a section `_type` this reader does not know is
|
|
85
|
+
skipped; the rest of the page reads.
|
|
86
|
+
- `section.image.missing-alt`: an image without alt text is dropped, never
|
|
87
|
+
returned without it. `text_media.image` is then `null`; a gallery keeps its
|
|
88
|
+
other photos.
|
|
89
|
+
- `section.image.missing-asset`: an image with no uploaded asset is dropped.
|
|
90
|
+
- `section.empty.skipped`: a gallery whose every photo was dropped, or a contact
|
|
91
|
+
section with email, phone, text and button all off, is skipped.
|
|
92
|
+
|
|
93
|
+
Everything else stays a named refusal, as for the rest of the reader: missing
|
|
94
|
+
required copy, a FAQ with no questions, a gallery outside 2 to 4 photos, a
|
|
95
|
+
repeated jump link or a button jumping to a section that is not on the page
|
|
96
|
+
throws `MalformedContentError` naming the path. Page fallbacks that carry
|
|
97
|
+
`sections` are validated against the same kinds.
|
|
98
|
+
|
|
99
|
+
The warnings travel in the page data, not in the server reader's
|
|
100
|
+
`CmsResolution.warnings`, which still names only the hero warning.
|
|
101
|
+
|
|
102
|
+
0.3.0 is an additive minor under the pilot semver policy: a new reader kind.
|
|
103
|
+
|
|
53
104
|
## Schema and release delivery
|
|
54
105
|
|
|
55
106
|
`@weatherboard/sanity-schema-base` derives the hero schema from these rules:
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A-1915 (A-1739 M1c). The reader's half of the shared section set that
|
|
3
|
+
* `@weatherboard/sanity-schema-base` declares in `src/sections.ts` (A-1889):
|
|
4
|
+
* `page.sections[]`, drawn from the four home section types plus
|
|
5
|
+
* `textMediaBlock`, `faqBlock` (items `faqItem`), `galleryBlock` and
|
|
6
|
+
* `contactBlock`.
|
|
7
|
+
*
|
|
8
|
+
* ===========================================================================
|
|
9
|
+
* LAYOUTS ARE THE A-1876 VARIANT NAMES, EXACTLY
|
|
10
|
+
* ===========================================================================
|
|
11
|
+
*
|
|
12
|
+
* The three lists below are the composition variant names, spelled as A-1876
|
|
13
|
+
* spells them, so a contract test can import them and compare them with the
|
|
14
|
+
* UI package's. A missing or unknown stored `layout` resolves to the
|
|
15
|
+
* composition's default and records `section.layout.defaulted`, the rule cms
|
|
16
|
+
* 0.2.0 set for `siteHero.layout`. It never throws and never yields an empty
|
|
17
|
+
* section.
|
|
18
|
+
*
|
|
19
|
+
* ===========================================================================
|
|
20
|
+
* WARNINGS, NOT REFUSALS, FOR THREE THINGS
|
|
21
|
+
* ===========================================================================
|
|
22
|
+
*
|
|
23
|
+
* The rest of this reader refuses a malformed document by name. Three things
|
|
24
|
+
* in a section are warnings instead, each recorded with its path:
|
|
25
|
+
*
|
|
26
|
+
* section.layout.defaulted missing or unknown layout: the default renders
|
|
27
|
+
* section.type.unknown a section `_type` this reader does not know is
|
|
28
|
+
* skipped, so a newer model never breaks a page
|
|
29
|
+
* section.image.missing-alt an image without alt text is DROPPED, never
|
|
30
|
+
* rendered without it
|
|
31
|
+
* section.image.missing-asset an image with no uploaded asset is dropped
|
|
32
|
+
* section.empty.skipped a section left with nothing to show (a gallery
|
|
33
|
+
* whose every photo was dropped, a contact
|
|
34
|
+
* section switched off and empty) is skipped
|
|
35
|
+
*/
|
|
36
|
+
import type { ActionInput, CompositionBlockInput, CopyInput } from "./site-content.cjs";
|
|
37
|
+
/** `Split`: text with media. */
|
|
38
|
+
export declare const SPLIT_LAYOUTS: readonly ["media-end", "media-start", "media-below"];
|
|
39
|
+
/** `HeadingList`: a heading column and a list column. */
|
|
40
|
+
export declare const HEADING_LIST_LAYOUTS: readonly ["beside", "stacked"];
|
|
41
|
+
/** `StaggerRow`: a row of two to four items. */
|
|
42
|
+
export declare const STAGGER_ROW_LAYOUTS: readonly ["staggered", "aligned"];
|
|
43
|
+
export type SplitLayout = (typeof SPLIT_LAYOUTS)[number];
|
|
44
|
+
export type HeadingListLayout = (typeof HEADING_LIST_LAYOUTS)[number];
|
|
45
|
+
export type StaggerRowLayout = (typeof STAGGER_ROW_LAYOUTS)[number];
|
|
46
|
+
/** What a missing or unknown `layout` resolves to, per composition. */
|
|
47
|
+
export declare const SECTION_LAYOUT_DEFAULTS: {
|
|
48
|
+
readonly Split: "media-end";
|
|
49
|
+
readonly HeadingList: "beside";
|
|
50
|
+
readonly StaggerRow: "staggered";
|
|
51
|
+
};
|
|
52
|
+
/** Sanity `_type` on the left, the reader's `kind` on the right; the same rows as schema-base's `SECTION_CONTRACTS`. */
|
|
53
|
+
export declare const READER_SECTION_KINDS: {
|
|
54
|
+
readonly featureStoriesBlock: "feature_stories";
|
|
55
|
+
readonly surfaceCardBlock: "cta";
|
|
56
|
+
readonly accentBandBlock: "accent_band";
|
|
57
|
+
readonly locationsBlock: "locations";
|
|
58
|
+
readonly textMediaBlock: "text_media";
|
|
59
|
+
readonly faqBlock: "faq";
|
|
60
|
+
readonly galleryBlock: "gallery";
|
|
61
|
+
readonly contactBlock: "contact";
|
|
62
|
+
};
|
|
63
|
+
/** A gallery is a `StaggerRow`, which holds two to four items. */
|
|
64
|
+
export declare const READER_GALLERY_PHOTO_MIN = 2;
|
|
65
|
+
export declare const READER_GALLERY_PHOTO_LIMIT = 4;
|
|
66
|
+
export declare const SECTION_WARNINGS: {
|
|
67
|
+
readonly layoutDefaulted: "section.layout.defaulted";
|
|
68
|
+
readonly unknownType: "section.type.unknown";
|
|
69
|
+
readonly imageMissingAlt: "section.image.missing-alt";
|
|
70
|
+
readonly imageMissingAsset: "section.image.missing-asset";
|
|
71
|
+
readonly emptySkipped: "section.empty.skipped";
|
|
72
|
+
};
|
|
73
|
+
export type SectionWarningCode = (typeof SECTION_WARNINGS)[keyof typeof SECTION_WARNINGS];
|
|
74
|
+
/** A named warning and the path in the stored document it is about. Never copy. */
|
|
75
|
+
export type SectionWarning = {
|
|
76
|
+
readonly code: SectionWarningCode;
|
|
77
|
+
readonly where: string;
|
|
78
|
+
};
|
|
79
|
+
/** A Sanity image as a site needs it. `asset` is the image asset's id; `alt` is never empty. */
|
|
80
|
+
export type SectionImageInput = {
|
|
81
|
+
readonly asset: string;
|
|
82
|
+
readonly alt: string;
|
|
83
|
+
readonly caption: string | null;
|
|
84
|
+
readonly hotspot: {
|
|
85
|
+
readonly x: number;
|
|
86
|
+
readonly y: number;
|
|
87
|
+
} | null;
|
|
88
|
+
};
|
|
89
|
+
export type TextMediaSectionInput = {
|
|
90
|
+
readonly kind: "text_media";
|
|
91
|
+
readonly anchor: string | null;
|
|
92
|
+
readonly layout: SplitLayout;
|
|
93
|
+
readonly heading: CopyInput;
|
|
94
|
+
readonly tail: CopyInput | null;
|
|
95
|
+
readonly body: CopyInput;
|
|
96
|
+
readonly action: ActionInput | null;
|
|
97
|
+
/** `null` only when the stored photo was dropped (no alt text or no asset); a warning names it. */
|
|
98
|
+
readonly image: SectionImageInput | null;
|
|
99
|
+
};
|
|
100
|
+
export type FaqSectionInput = {
|
|
101
|
+
readonly kind: "faq";
|
|
102
|
+
readonly anchor: string | null;
|
|
103
|
+
readonly layout: HeadingListLayout;
|
|
104
|
+
readonly heading: CopyInput;
|
|
105
|
+
readonly tail: CopyInput | null;
|
|
106
|
+
readonly intro: CopyInput | null;
|
|
107
|
+
readonly items: readonly {
|
|
108
|
+
readonly question: CopyInput;
|
|
109
|
+
readonly answer: CopyInput;
|
|
110
|
+
}[];
|
|
111
|
+
};
|
|
112
|
+
export type GallerySectionInput = {
|
|
113
|
+
readonly kind: "gallery";
|
|
114
|
+
readonly anchor: string | null;
|
|
115
|
+
readonly layout: StaggerRowLayout;
|
|
116
|
+
readonly heading: CopyInput | null;
|
|
117
|
+
readonly images: readonly SectionImageInput[];
|
|
118
|
+
};
|
|
119
|
+
export type ContactSectionInput = {
|
|
120
|
+
readonly kind: "contact";
|
|
121
|
+
readonly anchor: string | null;
|
|
122
|
+
readonly layout: SplitLayout;
|
|
123
|
+
readonly heading: CopyInput;
|
|
124
|
+
readonly body: CopyInput | null;
|
|
125
|
+
/** Show the site settings' email address. */
|
|
126
|
+
readonly showEmail: boolean;
|
|
127
|
+
/** Show the site settings' phone number. */
|
|
128
|
+
readonly showPhone: boolean;
|
|
129
|
+
readonly action: ActionInput | null;
|
|
130
|
+
readonly image: SectionImageInput | null;
|
|
131
|
+
};
|
|
132
|
+
export type NewSectionInput = TextMediaSectionInput | FaqSectionInput | GallerySectionInput | ContactSectionInput;
|
|
133
|
+
/** One entry of `PageContent.sections`: a home section kind or one of the new kinds. */
|
|
134
|
+
export type PageSectionInput = Exclude<CompositionBlockInput, {
|
|
135
|
+
readonly kind: "campaign";
|
|
136
|
+
}> | NewSectionInput;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A-1915 (A-1739 M1c). The reader's half of the shared section set that
|
|
3
|
+
* `@weatherboard/sanity-schema-base` declares in `src/sections.ts` (A-1889):
|
|
4
|
+
* `page.sections[]`, drawn from the four home section types plus
|
|
5
|
+
* `textMediaBlock`, `faqBlock` (items `faqItem`), `galleryBlock` and
|
|
6
|
+
* `contactBlock`.
|
|
7
|
+
*
|
|
8
|
+
* ===========================================================================
|
|
9
|
+
* LAYOUTS ARE THE A-1876 VARIANT NAMES, EXACTLY
|
|
10
|
+
* ===========================================================================
|
|
11
|
+
*
|
|
12
|
+
* The three lists below are the composition variant names, spelled as A-1876
|
|
13
|
+
* spells them, so a contract test can import them and compare them with the
|
|
14
|
+
* UI package's. A missing or unknown stored `layout` resolves to the
|
|
15
|
+
* composition's default and records `section.layout.defaulted`, the rule cms
|
|
16
|
+
* 0.2.0 set for `siteHero.layout`. It never throws and never yields an empty
|
|
17
|
+
* section.
|
|
18
|
+
*
|
|
19
|
+
* ===========================================================================
|
|
20
|
+
* WARNINGS, NOT REFUSALS, FOR THREE THINGS
|
|
21
|
+
* ===========================================================================
|
|
22
|
+
*
|
|
23
|
+
* The rest of this reader refuses a malformed document by name. Three things
|
|
24
|
+
* in a section are warnings instead, each recorded with its path:
|
|
25
|
+
*
|
|
26
|
+
* section.layout.defaulted missing or unknown layout: the default renders
|
|
27
|
+
* section.type.unknown a section `_type` this reader does not know is
|
|
28
|
+
* skipped, so a newer model never breaks a page
|
|
29
|
+
* section.image.missing-alt an image without alt text is DROPPED, never
|
|
30
|
+
* rendered without it
|
|
31
|
+
* section.image.missing-asset an image with no uploaded asset is dropped
|
|
32
|
+
* section.empty.skipped a section left with nothing to show (a gallery
|
|
33
|
+
* whose every photo was dropped, a contact
|
|
34
|
+
* section switched off and empty) is skipped
|
|
35
|
+
*/
|
|
36
|
+
import type { ActionInput, CompositionBlockInput, CopyInput } from "./site-content.mjs";
|
|
37
|
+
/** `Split`: text with media. */
|
|
38
|
+
export declare const SPLIT_LAYOUTS: readonly ["media-end", "media-start", "media-below"];
|
|
39
|
+
/** `HeadingList`: a heading column and a list column. */
|
|
40
|
+
export declare const HEADING_LIST_LAYOUTS: readonly ["beside", "stacked"];
|
|
41
|
+
/** `StaggerRow`: a row of two to four items. */
|
|
42
|
+
export declare const STAGGER_ROW_LAYOUTS: readonly ["staggered", "aligned"];
|
|
43
|
+
export type SplitLayout = (typeof SPLIT_LAYOUTS)[number];
|
|
44
|
+
export type HeadingListLayout = (typeof HEADING_LIST_LAYOUTS)[number];
|
|
45
|
+
export type StaggerRowLayout = (typeof STAGGER_ROW_LAYOUTS)[number];
|
|
46
|
+
/** What a missing or unknown `layout` resolves to, per composition. */
|
|
47
|
+
export declare const SECTION_LAYOUT_DEFAULTS: {
|
|
48
|
+
readonly Split: "media-end";
|
|
49
|
+
readonly HeadingList: "beside";
|
|
50
|
+
readonly StaggerRow: "staggered";
|
|
51
|
+
};
|
|
52
|
+
/** Sanity `_type` on the left, the reader's `kind` on the right; the same rows as schema-base's `SECTION_CONTRACTS`. */
|
|
53
|
+
export declare const READER_SECTION_KINDS: {
|
|
54
|
+
readonly featureStoriesBlock: "feature_stories";
|
|
55
|
+
readonly surfaceCardBlock: "cta";
|
|
56
|
+
readonly accentBandBlock: "accent_band";
|
|
57
|
+
readonly locationsBlock: "locations";
|
|
58
|
+
readonly textMediaBlock: "text_media";
|
|
59
|
+
readonly faqBlock: "faq";
|
|
60
|
+
readonly galleryBlock: "gallery";
|
|
61
|
+
readonly contactBlock: "contact";
|
|
62
|
+
};
|
|
63
|
+
/** A gallery is a `StaggerRow`, which holds two to four items. */
|
|
64
|
+
export declare const READER_GALLERY_PHOTO_MIN = 2;
|
|
65
|
+
export declare const READER_GALLERY_PHOTO_LIMIT = 4;
|
|
66
|
+
export declare const SECTION_WARNINGS: {
|
|
67
|
+
readonly layoutDefaulted: "section.layout.defaulted";
|
|
68
|
+
readonly unknownType: "section.type.unknown";
|
|
69
|
+
readonly imageMissingAlt: "section.image.missing-alt";
|
|
70
|
+
readonly imageMissingAsset: "section.image.missing-asset";
|
|
71
|
+
readonly emptySkipped: "section.empty.skipped";
|
|
72
|
+
};
|
|
73
|
+
export type SectionWarningCode = (typeof SECTION_WARNINGS)[keyof typeof SECTION_WARNINGS];
|
|
74
|
+
/** A named warning and the path in the stored document it is about. Never copy. */
|
|
75
|
+
export type SectionWarning = {
|
|
76
|
+
readonly code: SectionWarningCode;
|
|
77
|
+
readonly where: string;
|
|
78
|
+
};
|
|
79
|
+
/** A Sanity image as a site needs it. `asset` is the image asset's id; `alt` is never empty. */
|
|
80
|
+
export type SectionImageInput = {
|
|
81
|
+
readonly asset: string;
|
|
82
|
+
readonly alt: string;
|
|
83
|
+
readonly caption: string | null;
|
|
84
|
+
readonly hotspot: {
|
|
85
|
+
readonly x: number;
|
|
86
|
+
readonly y: number;
|
|
87
|
+
} | null;
|
|
88
|
+
};
|
|
89
|
+
export type TextMediaSectionInput = {
|
|
90
|
+
readonly kind: "text_media";
|
|
91
|
+
readonly anchor: string | null;
|
|
92
|
+
readonly layout: SplitLayout;
|
|
93
|
+
readonly heading: CopyInput;
|
|
94
|
+
readonly tail: CopyInput | null;
|
|
95
|
+
readonly body: CopyInput;
|
|
96
|
+
readonly action: ActionInput | null;
|
|
97
|
+
/** `null` only when the stored photo was dropped (no alt text or no asset); a warning names it. */
|
|
98
|
+
readonly image: SectionImageInput | null;
|
|
99
|
+
};
|
|
100
|
+
export type FaqSectionInput = {
|
|
101
|
+
readonly kind: "faq";
|
|
102
|
+
readonly anchor: string | null;
|
|
103
|
+
readonly layout: HeadingListLayout;
|
|
104
|
+
readonly heading: CopyInput;
|
|
105
|
+
readonly tail: CopyInput | null;
|
|
106
|
+
readonly intro: CopyInput | null;
|
|
107
|
+
readonly items: readonly {
|
|
108
|
+
readonly question: CopyInput;
|
|
109
|
+
readonly answer: CopyInput;
|
|
110
|
+
}[];
|
|
111
|
+
};
|
|
112
|
+
export type GallerySectionInput = {
|
|
113
|
+
readonly kind: "gallery";
|
|
114
|
+
readonly anchor: string | null;
|
|
115
|
+
readonly layout: StaggerRowLayout;
|
|
116
|
+
readonly heading: CopyInput | null;
|
|
117
|
+
readonly images: readonly SectionImageInput[];
|
|
118
|
+
};
|
|
119
|
+
export type ContactSectionInput = {
|
|
120
|
+
readonly kind: "contact";
|
|
121
|
+
readonly anchor: string | null;
|
|
122
|
+
readonly layout: SplitLayout;
|
|
123
|
+
readonly heading: CopyInput;
|
|
124
|
+
readonly body: CopyInput | null;
|
|
125
|
+
/** Show the site settings' email address. */
|
|
126
|
+
readonly showEmail: boolean;
|
|
127
|
+
/** Show the site settings' phone number. */
|
|
128
|
+
readonly showPhone: boolean;
|
|
129
|
+
readonly action: ActionInput | null;
|
|
130
|
+
readonly image: SectionImageInput | null;
|
|
131
|
+
};
|
|
132
|
+
export type NewSectionInput = TextMediaSectionInput | FaqSectionInput | GallerySectionInput | ContactSectionInput;
|
|
133
|
+
/** One entry of `PageContent.sections`: a home section kind or one of the new kinds. */
|
|
134
|
+
export type PageSectionInput = Exclude<CompositionBlockInput, {
|
|
135
|
+
readonly kind: "campaign";
|
|
136
|
+
}> | NewSectionInput;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A-1915 (A-1739 M1c). The reader's half of the shared section set that
|
|
3
|
+
* `@weatherboard/sanity-schema-base` declares in `src/sections.ts` (A-1889):
|
|
4
|
+
* `page.sections[]`, drawn from the four home section types plus
|
|
5
|
+
* `textMediaBlock`, `faqBlock` (items `faqItem`), `galleryBlock` and
|
|
6
|
+
* `contactBlock`.
|
|
7
|
+
*
|
|
8
|
+
* ===========================================================================
|
|
9
|
+
* LAYOUTS ARE THE A-1876 VARIANT NAMES, EXACTLY
|
|
10
|
+
* ===========================================================================
|
|
11
|
+
*
|
|
12
|
+
* The three lists below are the composition variant names, spelled as A-1876
|
|
13
|
+
* spells them, so a contract test can import them and compare them with the
|
|
14
|
+
* UI package's. A missing or unknown stored `layout` resolves to the
|
|
15
|
+
* composition's default and records `section.layout.defaulted`, the rule cms
|
|
16
|
+
* 0.2.0 set for `siteHero.layout`. It never throws and never yields an empty
|
|
17
|
+
* section.
|
|
18
|
+
*
|
|
19
|
+
* ===========================================================================
|
|
20
|
+
* WARNINGS, NOT REFUSALS, FOR THREE THINGS
|
|
21
|
+
* ===========================================================================
|
|
22
|
+
*
|
|
23
|
+
* The rest of this reader refuses a malformed document by name. Three things
|
|
24
|
+
* in a section are warnings instead, each recorded with its path:
|
|
25
|
+
*
|
|
26
|
+
* section.layout.defaulted missing or unknown layout: the default renders
|
|
27
|
+
* section.type.unknown a section `_type` this reader does not know is
|
|
28
|
+
* skipped, so a newer model never breaks a page
|
|
29
|
+
* section.image.missing-alt an image without alt text is DROPPED, never
|
|
30
|
+
* rendered without it
|
|
31
|
+
* section.image.missing-asset an image with no uploaded asset is dropped
|
|
32
|
+
* section.empty.skipped a section left with nothing to show (a gallery
|
|
33
|
+
* whose every photo was dropped, a contact
|
|
34
|
+
* section switched off and empty) is skipped
|
|
35
|
+
*/
|
|
36
|
+
import type { ActionInput, CompositionBlockInput, CopyInput } from "./site-content.js";
|
|
37
|
+
/** `Split`: text with media. */
|
|
38
|
+
export declare const SPLIT_LAYOUTS: readonly ["media-end", "media-start", "media-below"];
|
|
39
|
+
/** `HeadingList`: a heading column and a list column. */
|
|
40
|
+
export declare const HEADING_LIST_LAYOUTS: readonly ["beside", "stacked"];
|
|
41
|
+
/** `StaggerRow`: a row of two to four items. */
|
|
42
|
+
export declare const STAGGER_ROW_LAYOUTS: readonly ["staggered", "aligned"];
|
|
43
|
+
export type SplitLayout = (typeof SPLIT_LAYOUTS)[number];
|
|
44
|
+
export type HeadingListLayout = (typeof HEADING_LIST_LAYOUTS)[number];
|
|
45
|
+
export type StaggerRowLayout = (typeof STAGGER_ROW_LAYOUTS)[number];
|
|
46
|
+
/** What a missing or unknown `layout` resolves to, per composition. */
|
|
47
|
+
export declare const SECTION_LAYOUT_DEFAULTS: {
|
|
48
|
+
readonly Split: "media-end";
|
|
49
|
+
readonly HeadingList: "beside";
|
|
50
|
+
readonly StaggerRow: "staggered";
|
|
51
|
+
};
|
|
52
|
+
/** Sanity `_type` on the left, the reader's `kind` on the right; the same rows as schema-base's `SECTION_CONTRACTS`. */
|
|
53
|
+
export declare const READER_SECTION_KINDS: {
|
|
54
|
+
readonly featureStoriesBlock: "feature_stories";
|
|
55
|
+
readonly surfaceCardBlock: "cta";
|
|
56
|
+
readonly accentBandBlock: "accent_band";
|
|
57
|
+
readonly locationsBlock: "locations";
|
|
58
|
+
readonly textMediaBlock: "text_media";
|
|
59
|
+
readonly faqBlock: "faq";
|
|
60
|
+
readonly galleryBlock: "gallery";
|
|
61
|
+
readonly contactBlock: "contact";
|
|
62
|
+
};
|
|
63
|
+
/** A gallery is a `StaggerRow`, which holds two to four items. */
|
|
64
|
+
export declare const READER_GALLERY_PHOTO_MIN = 2;
|
|
65
|
+
export declare const READER_GALLERY_PHOTO_LIMIT = 4;
|
|
66
|
+
export declare const SECTION_WARNINGS: {
|
|
67
|
+
readonly layoutDefaulted: "section.layout.defaulted";
|
|
68
|
+
readonly unknownType: "section.type.unknown";
|
|
69
|
+
readonly imageMissingAlt: "section.image.missing-alt";
|
|
70
|
+
readonly imageMissingAsset: "section.image.missing-asset";
|
|
71
|
+
readonly emptySkipped: "section.empty.skipped";
|
|
72
|
+
};
|
|
73
|
+
export type SectionWarningCode = (typeof SECTION_WARNINGS)[keyof typeof SECTION_WARNINGS];
|
|
74
|
+
/** A named warning and the path in the stored document it is about. Never copy. */
|
|
75
|
+
export type SectionWarning = {
|
|
76
|
+
readonly code: SectionWarningCode;
|
|
77
|
+
readonly where: string;
|
|
78
|
+
};
|
|
79
|
+
/** A Sanity image as a site needs it. `asset` is the image asset's id; `alt` is never empty. */
|
|
80
|
+
export type SectionImageInput = {
|
|
81
|
+
readonly asset: string;
|
|
82
|
+
readonly alt: string;
|
|
83
|
+
readonly caption: string | null;
|
|
84
|
+
readonly hotspot: {
|
|
85
|
+
readonly x: number;
|
|
86
|
+
readonly y: number;
|
|
87
|
+
} | null;
|
|
88
|
+
};
|
|
89
|
+
export type TextMediaSectionInput = {
|
|
90
|
+
readonly kind: "text_media";
|
|
91
|
+
readonly anchor: string | null;
|
|
92
|
+
readonly layout: SplitLayout;
|
|
93
|
+
readonly heading: CopyInput;
|
|
94
|
+
readonly tail: CopyInput | null;
|
|
95
|
+
readonly body: CopyInput;
|
|
96
|
+
readonly action: ActionInput | null;
|
|
97
|
+
/** `null` only when the stored photo was dropped (no alt text or no asset); a warning names it. */
|
|
98
|
+
readonly image: SectionImageInput | null;
|
|
99
|
+
};
|
|
100
|
+
export type FaqSectionInput = {
|
|
101
|
+
readonly kind: "faq";
|
|
102
|
+
readonly anchor: string | null;
|
|
103
|
+
readonly layout: HeadingListLayout;
|
|
104
|
+
readonly heading: CopyInput;
|
|
105
|
+
readonly tail: CopyInput | null;
|
|
106
|
+
readonly intro: CopyInput | null;
|
|
107
|
+
readonly items: readonly {
|
|
108
|
+
readonly question: CopyInput;
|
|
109
|
+
readonly answer: CopyInput;
|
|
110
|
+
}[];
|
|
111
|
+
};
|
|
112
|
+
export type GallerySectionInput = {
|
|
113
|
+
readonly kind: "gallery";
|
|
114
|
+
readonly anchor: string | null;
|
|
115
|
+
readonly layout: StaggerRowLayout;
|
|
116
|
+
readonly heading: CopyInput | null;
|
|
117
|
+
readonly images: readonly SectionImageInput[];
|
|
118
|
+
};
|
|
119
|
+
export type ContactSectionInput = {
|
|
120
|
+
readonly kind: "contact";
|
|
121
|
+
readonly anchor: string | null;
|
|
122
|
+
readonly layout: SplitLayout;
|
|
123
|
+
readonly heading: CopyInput;
|
|
124
|
+
readonly body: CopyInput | null;
|
|
125
|
+
/** Show the site settings' email address. */
|
|
126
|
+
readonly showEmail: boolean;
|
|
127
|
+
/** Show the site settings' phone number. */
|
|
128
|
+
readonly showPhone: boolean;
|
|
129
|
+
readonly action: ActionInput | null;
|
|
130
|
+
readonly image: SectionImageInput | null;
|
|
131
|
+
};
|
|
132
|
+
export type NewSectionInput = TextMediaSectionInput | FaqSectionInput | GallerySectionInput | ContactSectionInput;
|
|
133
|
+
/** One entry of `PageContent.sections`: a home section kind or one of the new kinds. */
|
|
134
|
+
export type PageSectionInput = Exclude<CompositionBlockInput, {
|
|
135
|
+
readonly kind: "campaign";
|
|
136
|
+
}> | NewSectionInput;
|
|
@@ -54,6 +54,11 @@
|
|
|
54
54
|
* accentBandBlock kind `accent_band`
|
|
55
55
|
* locationsBlock kind `locations` (jumpLink → anchor)
|
|
56
56
|
*
|
|
57
|
+
* A content page's `sections[]` (A-1915) reads those four kinds plus
|
|
58
|
+
* `text_media`, `faq`, `gallery` and `contact` into `PageContent.sections`,
|
|
59
|
+
* with their named warnings in `PageContent.warnings`. `sections.ts` holds the
|
|
60
|
+
* kinds, the A-1876 layout names and the warning codes.
|
|
61
|
+
*
|
|
57
62
|
* Two places the composition cannot carry a proposed marker: a `cta` block's
|
|
58
63
|
* plain strings, and the campaign's title and body (which travel as Markdown).
|
|
59
64
|
* There the text is returned plain and the line is still listed in
|
|
@@ -61,6 +66,7 @@
|
|
|
61
66
|
* approval. Nothing is dropped silently.
|
|
62
67
|
*/
|
|
63
68
|
import { type CmsQuery } from "../contracts/query.cjs";
|
|
69
|
+
import { type PageSectionInput, type SectionWarning } from "./sections.cjs";
|
|
64
70
|
export type CopyInput = string | {
|
|
65
71
|
readonly text: string;
|
|
66
72
|
readonly copySource: "captured" | "proposed";
|
|
@@ -142,6 +148,13 @@ export type PageContent = {
|
|
|
142
148
|
readonly body: string;
|
|
143
149
|
readonly migrated: boolean;
|
|
144
150
|
readonly proposedCopy: readonly ProposedCopy[];
|
|
151
|
+
/**
|
|
152
|
+
* A-1915. Present only when the stored page has a `sections` list, so a page
|
|
153
|
+
* written before sections existed reads exactly as it did in 0.2.0.
|
|
154
|
+
*/
|
|
155
|
+
readonly sections?: readonly PageSectionInput[];
|
|
156
|
+
/** A-1915. Present with `sections`: every named section warning, with its path. */
|
|
157
|
+
readonly warnings?: readonly SectionWarning[];
|
|
145
158
|
};
|
|
146
159
|
export type SiteSettingsContent = {
|
|
147
160
|
readonly name: string;
|
|
@@ -54,6 +54,11 @@
|
|
|
54
54
|
* accentBandBlock kind `accent_band`
|
|
55
55
|
* locationsBlock kind `locations` (jumpLink → anchor)
|
|
56
56
|
*
|
|
57
|
+
* A content page's `sections[]` (A-1915) reads those four kinds plus
|
|
58
|
+
* `text_media`, `faq`, `gallery` and `contact` into `PageContent.sections`,
|
|
59
|
+
* with their named warnings in `PageContent.warnings`. `sections.ts` holds the
|
|
60
|
+
* kinds, the A-1876 layout names and the warning codes.
|
|
61
|
+
*
|
|
57
62
|
* Two places the composition cannot carry a proposed marker: a `cta` block's
|
|
58
63
|
* plain strings, and the campaign's title and body (which travel as Markdown).
|
|
59
64
|
* There the text is returned plain and the line is still listed in
|
|
@@ -61,6 +66,7 @@
|
|
|
61
66
|
* approval. Nothing is dropped silently.
|
|
62
67
|
*/
|
|
63
68
|
import { type CmsQuery } from "../contracts/query.mjs";
|
|
69
|
+
import { type PageSectionInput, type SectionWarning } from "./sections.mjs";
|
|
64
70
|
export type CopyInput = string | {
|
|
65
71
|
readonly text: string;
|
|
66
72
|
readonly copySource: "captured" | "proposed";
|
|
@@ -142,6 +148,13 @@ export type PageContent = {
|
|
|
142
148
|
readonly body: string;
|
|
143
149
|
readonly migrated: boolean;
|
|
144
150
|
readonly proposedCopy: readonly ProposedCopy[];
|
|
151
|
+
/**
|
|
152
|
+
* A-1915. Present only when the stored page has a `sections` list, so a page
|
|
153
|
+
* written before sections existed reads exactly as it did in 0.2.0.
|
|
154
|
+
*/
|
|
155
|
+
readonly sections?: readonly PageSectionInput[];
|
|
156
|
+
/** A-1915. Present with `sections`: every named section warning, with its path. */
|
|
157
|
+
readonly warnings?: readonly SectionWarning[];
|
|
145
158
|
};
|
|
146
159
|
export type SiteSettingsContent = {
|
|
147
160
|
readonly name: string;
|
|
@@ -54,6 +54,11 @@
|
|
|
54
54
|
* accentBandBlock kind `accent_band`
|
|
55
55
|
* locationsBlock kind `locations` (jumpLink → anchor)
|
|
56
56
|
*
|
|
57
|
+
* A content page's `sections[]` (A-1915) reads those four kinds plus
|
|
58
|
+
* `text_media`, `faq`, `gallery` and `contact` into `PageContent.sections`,
|
|
59
|
+
* with their named warnings in `PageContent.warnings`. `sections.ts` holds the
|
|
60
|
+
* kinds, the A-1876 layout names and the warning codes.
|
|
61
|
+
*
|
|
57
62
|
* Two places the composition cannot carry a proposed marker: a `cta` block's
|
|
58
63
|
* plain strings, and the campaign's title and body (which travel as Markdown).
|
|
59
64
|
* There the text is returned plain and the line is still listed in
|
|
@@ -61,6 +66,7 @@
|
|
|
61
66
|
* approval. Nothing is dropped silently.
|
|
62
67
|
*/
|
|
63
68
|
import { type CmsQuery } from "../contracts/query.js";
|
|
69
|
+
import { type PageSectionInput, type SectionWarning } from "./sections.js";
|
|
64
70
|
export type CopyInput = string | {
|
|
65
71
|
readonly text: string;
|
|
66
72
|
readonly copySource: "captured" | "proposed";
|
|
@@ -142,6 +148,13 @@ export type PageContent = {
|
|
|
142
148
|
readonly body: string;
|
|
143
149
|
readonly migrated: boolean;
|
|
144
150
|
readonly proposedCopy: readonly ProposedCopy[];
|
|
151
|
+
/**
|
|
152
|
+
* A-1915. Present only when the stored page has a `sections` list, so a page
|
|
153
|
+
* written before sections existed reads exactly as it did in 0.2.0.
|
|
154
|
+
*/
|
|
155
|
+
readonly sections?: readonly PageSectionInput[];
|
|
156
|
+
/** A-1915. Present with `sections`: every named section warning, with its path. */
|
|
157
|
+
readonly warnings?: readonly SectionWarning[];
|
|
145
158
|
};
|
|
146
159
|
export type SiteSettingsContent = {
|
|
147
160
|
readonly name: string;
|