Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/cms/cms.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ CMS.registerWidget("url-image", UrlImageControl, UrlImagePreview);

CMS.registerPreviewTemplate("index", IndexPagePreview);
CMS.registerPreviewTemplate("about", AboutPagePreview);
CMS.registerPreviewTemplate("idea", IdeaPostPreview);
CMS.registerPreviewTemplate("ideas", IdeaPostPreview);

// Decap exposes a number of lifecycle stages we can hook into and register.
CMS.registerEventListener({
Expand Down
26 changes: 0 additions & 26 deletions src/cms/preview-templates/IdeaPostPreview.js

This file was deleted.

88 changes: 88 additions & 0 deletions src/cms/preview-templates/IdeaPostPreview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React from "react";

import {
IdeaPostTemplate,
IdeaPostTemplateProps,
} from "../../templates/idea-post";
import { ImmutableLike, fromImmutable } from "../utils/immutable";

interface PreviewProps {
entry?: ImmutableLike;
value?: unknown;
}

/**
* Normalize CMS form data into the shape IdeaPostTemplate expects.
* Decap gives us raw widget values which differ from resolved Gatsby data:
* - relation widgets return value_field strings, not resolved objects
* - single select widgets return a string, not an array
*/
function normalizeCmsData(
raw: Record<string, unknown>,
): Partial<IdeaPostTemplateProps> {
const v = raw as Partial<IdeaPostTemplateProps>;

// program: single select string → array
const program = v.program;
const normalizedProgram = program
? Array.isArray(program)
? program
: [String(program)]
: undefined;

// authors: relation gives ["name1", "name2"] → [{ name, contactId }]
const authors = v.authors
? (v.authors as unknown as string[]).map((a) =>
typeof a === "string" ? { name: a, contactId: "" } : a,
)
: undefined;

// date: datetime widget returns a Date/object, template expects a string
const rawDate = raw.date;
const date = rawDate
? typeof rawDate === "string"
? rawDate
: new Date(rawDate as string | number).toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "2-digit",
})
: undefined;

return {
...v,
authors,
date,
isPreview: true,
program: normalizedProgram,
};
}

const IdeaPostPreview: React.FC<PreviewProps> = ({ entry, value }) => {
const raw = value ?? (entry?.get("data") as ImmutableLike | undefined);
const v = fromImmutable<Record<string, unknown>>(raw) ?? {};
return (
<>
<div
style={{
background: "#fffbe6",
border: "1px solid #ffe58f",
borderRadius: 4,
color: "#874d00",
fontSize: 12,
margin: 8,
padding: "6px 12px",
}}
>
Previews are approximate/under development — content and styling
may differ from production, and not all functionality will be
available.
</div>
<IdeaPostTemplate
{...(normalizeCmsData(v) as IdeaPostTemplateProps)}
/>
</>
);
};

export default IdeaPostPreview;
82 changes: 50 additions & 32 deletions src/templates/idea-post.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,20 @@ const {
tagRowLabel,
} = require("../style/idea-post.module.css");

export const IdeaPostTemplate: React.FC<
IdeaPostNode & {
onExpandDescription?: (
content: string,
label: string,
sectionKey: string,
) => void;
}
> = ({
export type IdeaPostTemplateProps = IdeaPostNode & {
isPreview?: boolean;
onExpandDescription?: (
content: string,
label: string,
sectionKey: string,
) => void;
};

export const IdeaPostTemplate: React.FC<IdeaPostTemplateProps> = ({
authors,
date,
introduction,
isPreview,
nextSteps,
onExpandDescription,
preliminaryFindings,
Expand Down Expand Up @@ -121,27 +123,34 @@ export const IdeaPostTemplate: React.FC<
</Button>
</div>
</div>

<ContactModal
authors={authors}
primaryContact={primaryContact}
title={title}
open={contactModalOpen}
onClose={() => setContactModalOpen(false)}
/>
{!isPreview && (
<ContactModal
authors={authors}
primaryContact={primaryContact}
title={title}
open={contactModalOpen}
onClose={() => setContactModalOpen(false)}
/>
)}

{/* Tag row */}
{tags && tags.length > 0 && (
<div className={tagRow}>
<span className={tagRowLabel}>Topics</span>
{tags.map((t) => (
<TagPopover
key={t}
tag={t}
currentSlug={slug}
className={tag}
/>
))}
{tags.map((t) =>
isPreview ? (
<span key={t} className={tag}>
{t}
</span>
) : (
<TagPopover
key={t}
tag={t}
currentSlug={slug}
className={tag}
/>
),
)}
</div>
)}

Expand Down Expand Up @@ -198,16 +207,25 @@ export const IdeaPostTemplate: React.FC<
</ul>
</div>
)}
{resources && (
<MaterialsAndMethodsComponent
resources={[...resources]}
onExpandDescription={onExpandDescription}
/>
)}
{resources &&
(isPreview ? (
<ul className={resourceList}>
{(resources as unknown as string[]).map(
(slug) => (
<li key={slug}>{slug}</li>
),
)}
</ul>
) : (
<MaterialsAndMethodsComponent
resources={[...resources]}
onExpandDescription={onExpandDescription}
/>
))}
</div>
</div>

{hasRelatedIdeas && (
{hasRelatedIdeas && !isPreview && (
<div id="related-ideas">
<div className={sectionLabel}>Related Ideas</div>
<ul className={relatedList}>
Expand Down
Loading