-
Notifications
You must be signed in to change notification settings - Fork 2
Preview Ideas in CMS (pt. 1/2) #112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| 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 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. non-blocking, but worth noting down: this comment made me go look at how |
||
| 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", | ||
|
Comment on lines
+45
to
+48
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: maybe add timeZone: "UTC" here to avoid cases where Aug 10 locally is already Aug 11 in UTC |
||
| timeZone: "UTC", | ||
| }) | ||
| : 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; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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, | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't love these isPreview checks, and kind of wish that the gatsby components didn't need to know anything about the Decap previews, but it's probably a worthwhile tradeoff, since designing to avoid it would force bigger changes (or make previews onerous to maintain). As far as gatsby is concerned this is just some visual noise since isPreview should never be true outside of Decap. |
||
| nextSteps, | ||
| onExpandDescription, | ||
| preliminaryFindings, | ||
|
|
@@ -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> | ||
| )} | ||
|
|
||
|
|
@@ -214,8 +223,16 @@ export const IdeaPostTemplate: React.FC< | |
| {relatedIdeas!.map((idea) => { | ||
| if (!idea.slug && !idea.title) return null; | ||
| return ( | ||
| <li key={idea.slug}> | ||
| <Link to={idea.slug}>{idea.title}</Link> | ||
| <li key={idea.slug || idea.title}> | ||
| {/* Gatsby's Link needs the app runtime | ||
| the Decap preview iframe lacks */} | ||
| {isPreview ? ( | ||
| <a href={idea.slug}>{idea.title}</a> | ||
| ) : ( | ||
| <Link to={idea.slug}> | ||
| {idea.title} | ||
| </Link> | ||
| )} | ||
| </li> | ||
| ); | ||
| })} | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice catch!