Skip to content

Commit 308afe1

Browse files
authored
Merge pull request #19 from usds/tomn-usds/start-projectpages-support
Start support for Project page templates
2 parents 38482d9 + 64f0c50 commit 308afe1

File tree

7 files changed

+80
-35
lines changed

7 files changed

+80
-35
lines changed

src/components/EditActionsToolbar.tsx

+8
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { Fragment, useRef} from "react";
1111

1212
export const EditActionsToolbar = (props: {
1313
mdxeditorref: React.RefObject<MDXEditorMethods>,
14+
reloadCallback?: (newMd:string) => void,
1415
}) => {
1516
const newFromTemplate = () => {
1617
if (!confirm("This will replace any work you may have unsaved.\n\nContinue?")) {
@@ -19,6 +20,9 @@ export const EditActionsToolbar = (props: {
1920
const mdtext = getBlogTemplateMarkdown();
2021
props.mdxeditorref?.current?.setMarkdown(mdtext);
2122
localStorage.setItem(MARKDOWN_LOCAL_STORAGE_KEY, mdtext);
23+
if (props.reloadCallback) {
24+
props.reloadCallback(mdtext);
25+
}
2226
}
2327

2428
const loadData = async (evt: React.ChangeEvent<HTMLInputElement>) => {
@@ -33,6 +37,10 @@ export const EditActionsToolbar = (props: {
3337
const cleanmdtext = mdtext.replace(`<[email protected]>`, '[email protected]');
3438
props.mdxeditorref?.current?.setMarkdown(cleanmdtext);
3539
localStorage.setItem(MARKDOWN_LOCAL_STORAGE_KEY, mdtext);
40+
if (props.reloadCallback) {
41+
props.reloadCallback(mdtext);
42+
}
43+
3644
// setTimeout(() => {
3745
// window.location.reload(); // needs some help loading cached images
3846
// }, 250);

src/mdxcomponents/ImageDialogCustom.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export const ImageDialogCustom: React.FC = () => {
7878
Click to select image from your device
7979
</label>
8080
<input type="file" {...register('file')} />
81-
{filename.length? `Current: "${filename}"`: null }
81+
{filename?.length ? `Current: "${filename}"`: null }
8282
</div>
8383

8484
{/*<div className={styles.formField}>*/}

src/mdxcomponents/frontmatterCustom/FrontmatterCustomEditor.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {useCellValues, usePublisher} from '@mdxeditor/gurx';
1313
import styles from '../../styles/mdxeditor.copy.module.css';
1414
import stylesCustom from '../../styles/mdxeditor.custom.module.css';
1515
import {Fieldset, Grid, GridContainer, Checkbox, FileInput, Label} from "@trussworks/react-uswds";
16-
import {BlogFrontMatterFields, CACHE_NAME, READING_WORDS_PER_MINUTE} from "../../types/commontypes.ts";
16+
import {FrontMatterFields, CACHE_NAME, READING_WORDS_PER_MINUTE} from "../../types/commontypes.ts";
1717
import {
1818
blogFieldsFixup,
1919
generateFields,
@@ -44,7 +44,7 @@ export const FrontmatterCustomEditor = ({yaml, onChange}: FrontmatterCustomEdito
4444
const getFrontMatterFields = () => {
4545
const fields = yamlToBlogFields(yaml);
4646
// we do some basic cleanup. We'll need to set it back?
47-
fields.carousel_image = getFilnamePartOfUrlPath(fields.carousel_image);
47+
fields.carousel_image = getFilnamePartOfUrlPath(fields.carousel_image) ?? "carousel_img.png";
4848
return fields;
4949
};
5050

@@ -76,7 +76,7 @@ export const FrontmatterCustomEditor = ({yaml, onChange}: FrontmatterCustomEdito
7676
}, []);
7777

7878
const onSubmit = React.useCallback(
79-
(frontMatterFields: BlogFrontMatterFields) => {
79+
(frontMatterFields: FrontMatterFields) => {
8080
const fixedUpFields = blogFieldsFixup(frontMatterFields);
8181
const yamlstr = getYamlBlogHeader(fixedUpFields);
8282
onChange(yamlstr);

src/mdxcomponents/frontmatterUtils.ts

+42-25
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// great primary https://404wolf.com/posts/blog/imageBlocks
22
import YamlParser from "js-yaml";
33
import {
4-
BLANK_BLOG_FRONTMATTER_FIELDS,
5-
BlogFrontMatterFields, CACHE_NAME,
4+
BLANK_FRONTMATTER_FIELDS,
5+
FrontMatterFields, CACHE_NAME,
66
DEFAULT_AUTHOR, STARTER_BLOG_FRONTMATTER_FIELDS,
77
toMarkdownOptions
88
} from "../types/commontypes.ts";
@@ -25,9 +25,9 @@ import {showToast} from "../components/showToast.tsx";
2525
import JSZip from "jszip";
2626

2727

28-
export const yamlToBlogFields = (yamlstr: string): BlogFrontMatterFields => {
28+
export const yamlToBlogFields = (yamlstr: string): FrontMatterFields => {
2929
if (yamlstr.trim().length === 0) {
30-
return BLANK_BLOG_FRONTMATTER_FIELDS;
30+
return BLANK_FRONTMATTER_FIELDS;
3131
}
3232
try {
3333
// sanity check that the yamlstr doesn't include syntax that will throw an error
@@ -44,33 +44,39 @@ export const yamlToBlogFields = (yamlstr: string): BlogFrontMatterFields => {
4444
const author = data?.author ?? data?.byline_str ?? ""; // fallback logic for old format
4545

4646
// if we make every field a string, then this becomes easy.
47-
// Might be some reflection way to do this? Going to be verbose the first time.
47+
// Might be some reflection way to do this? Going to be verbose for now.
4848
// maybe json.parse could be used more?
49-
const result: BlogFrontMatterFields = {
50-
title: data.title ?? BLANK_BLOG_FRONTMATTER_FIELDS.title,
51-
date: data.date ?? data.dateline_str ?? BLANK_BLOG_FRONTMATTER_FIELDS.date,
52-
readtime_minutes: parseInt(data.readtime_minutes ?? data.readtime_str ?? BLANK_BLOG_FRONTMATTER_FIELDS.date),
53-
author: author.length ? author : BLANK_BLOG_FRONTMATTER_FIELDS.author,
54-
permalink: data.permalink ?? BLANK_BLOG_FRONTMATTER_FIELDS.permalink,
49+
const result: FrontMatterFields = {
50+
title: data.title ?? BLANK_FRONTMATTER_FIELDS.title,
51+
date: data.date ?? data.dateline_str ?? BLANK_FRONTMATTER_FIELDS.date,
52+
readtime_minutes: parseInt(data.readtime_minutes ?? data.readtime_str ?? BLANK_FRONTMATTER_FIELDS.date),
53+
author: author.length ? author : BLANK_FRONTMATTER_FIELDS.author,
54+
permalink: data.permalink ?? BLANK_FRONTMATTER_FIELDS.permalink,
5555
tags: [], // todo: fix
56-
basename: data.basename ?? BLANK_BLOG_FRONTMATTER_FIELDS.basename,
56+
basename: data.basename ?? BLANK_FRONTMATTER_FIELDS.basename,
5757

58-
carousel_title: data.carousel_title ?? BLANK_BLOG_FRONTMATTER_FIELDS.carousel_title,
59-
carousel_summary: data.carousel_summary ?? BLANK_BLOG_FRONTMATTER_FIELDS.carousel_summary,
60-
carousel_image: data.carousel_image ?? BLANK_BLOG_FRONTMATTER_FIELDS.carousel_image,
61-
carousel_image_alt_text: data.carousel_image_alt_text ?? BLANK_BLOG_FRONTMATTER_FIELDS.carousel_image_alt_text,
62-
carousel_show: (data.carousel_show === "true" ? "true" : BLANK_BLOG_FRONTMATTER_FIELDS.carousel_show),
58+
agency: data.agency ?? BLANK_FRONTMATTER_FIELDS.agency,
59+
project_url: data.project_url ?? BLANK_FRONTMATTER_FIELDS.project_url,
60+
impact_statement: [], // todo: fix
61+
62+
carousel_title: data.carousel_title ?? BLANK_FRONTMATTER_FIELDS.carousel_title,
63+
carousel_summary: data.carousel_summary ?? BLANK_FRONTMATTER_FIELDS.carousel_summary,
64+
carousel_image: data.carousel_image ?? BLANK_FRONTMATTER_FIELDS.carousel_image,
65+
carousel_image_alt_text: data.carousel_image_alt_text ?? BLANK_FRONTMATTER_FIELDS.carousel_image_alt_text,
66+
carousel_show: (data.carousel_show === "true" ? "true" : BLANK_FRONTMATTER_FIELDS.carousel_show),
6367
};
6468
return result;
6569
} catch(err) {
6670
console.error(err);
67-
return BLANK_BLOG_FRONTMATTER_FIELDS;
71+
return BLANK_FRONTMATTER_FIELDS;
6872
}
6973
}
7074

7175
/** there's a pending change with the website to rename some fields */
72-
export const getYamlBlogHeaderNew = (fields: BlogFrontMatterFields): string => {
73-
const tags = '[' + fields.tags?.map(s => `'${s}'`).join(',') + ']';
76+
export const getYamlBlogHeader = (fields: FrontMatterFields): string => {
77+
const tags = YamlParser.dump([...fields.tags]);
78+
const impact_statement = YamlParser.dump(fields.impact_statement);
79+
7480
return `# Page template info (DO NOT EDIT)
7581
layout: default
7682
blog_page: true
@@ -92,11 +98,22 @@ readtime_minutes: "${fields.readtime_minutes}"
9298
author: "${fields.author}"
9399
permalink: ${fields.permalink}
94100
basename: "${fields.basename}"
95-
tags: [${tags}]
101+
tags: ${tags}
102+
103+
# These are in the process of being renamed and are here for the transition, they should be removed
104+
dateline_str: "${fields.date}"
105+
byline_str: "${fields.author}"
106+
107+
# Project details page specifics. (Edit this)
108+
agency: ${fields.agency}
109+
project_url: ${fields.project_url}
110+
111+
# Project Impact statement (Edit this)
112+
impact_statement: ${impact_statement}
96113
`;
97114
}
98115

99-
export const getYamlBlogHeader = (fields: BlogFrontMatterFields): string => {
116+
export const getYamlBlogHeaderOld = (fields: FrontMatterFields): string => {
100117
const tags = '[' + fields.tags?.map(s => `'${s}'`).join(',') + ']';
101118
const carousel_show = fields.carousel_show ? "true" : "false";
102119
return `# Page template info (DO NOT EDIT)
@@ -144,7 +161,7 @@ export const generateBasename = (title: string): string => {
144161
/**
145162
* Just trying to consolidate all the url building to a single location.
146163
*/
147-
export const generateFields = (fields: BlogFrontMatterFields, resetPermalink = false): {
164+
export const generateFields = (fields: FrontMatterFields, resetPermalink = false): {
148165
basename: string, // if this is undefined, then function failed
149166
imagedir: string, // references the -img/ directory
150167
carousel_imagepath_for_md: string, // starts with /news-and-blog, used in MD
@@ -174,7 +191,7 @@ export const generateFields = (fields: BlogFrontMatterFields, resetPermalink = f
174191
return {basename, imagedir, carousel_imagepath_for_md, mdfilename, datedbasename, permalink};
175192
};
176193

177-
export const blogFieldsFixup = (fields: BlogFrontMatterFields, resetPermalink = false): BlogFrontMatterFields => {
194+
export const blogFieldsFixup = (fields: FrontMatterFields, resetPermalink = false): FrontMatterFields => {
178195
if (fields.title.trim() === "") {
179196
showToast("Must have a title to continue.", "error");
180197
return fields;
@@ -223,7 +240,7 @@ function isParent(node: unknown): node is Mdast.Parent {
223240
}
224241

225242
interface SaveDataType {
226-
yamlFields: BlogFrontMatterFields;
243+
yamlFields: FrontMatterFields;
227244
imagesFromMd: string[];
228245
markdownFixedStr: string; // fixes the cache image links to use zip save director
229246
}

src/misc.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {MDXEditorMethods} from "@mdxeditor/editor";
33
import {CACHE_NAME} from "./types/commontypes.ts";
44

55

6-
export const getFilnamePartOfUrlPath = (pathstr?: string) => pathstr?.split("/").pop() ?? "";
6+
export const getFilnamePartOfUrlPath = (pathstr?: string) => pathstr?.split("/")?.pop()?.split("?")[0] ?? undefined;
77

88
export const forceTypeBoolean = (value: string | null | boolean): boolean | null =>
99
typeof value === 'boolean' ? value : value === 'true' ? true : value === 'false' ? false : null;

src/pages/BlogEditorPage.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export const BlogEditorPage = () => {
7474

7575
return (
7676
<Fragment>
77-
<EditActionsToolbar mdxeditorref={mdxeditorref}/>
77+
<EditActionsToolbar mdxeditorref={mdxeditorref} reloadCallback={(newMd) => setOldMarkdown(newMd)} />
7878
<MDXEditor
7979
ref={mdxeditorref}
8080
className={"grid-container"}

src/types/commontypes.ts

+24-4
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,25 @@ export const DEFAULT_AUTHOR = "U.S. Digital Service";
1212
export const CACHE_NAME = "mdedit-cache-v1";
1313
export const MARKDOWN_LOCAL_STORAGE_KEY = "savedMarkdown";
1414

15-
export interface BlogFrontMatterFields {
15+
export type ImpactStatementField = {
16+
figure: string;
17+
unit: string; // e.g. "%" "M"
18+
description: string;
19+
};
20+
21+
export interface FrontMatterFields {
1622
title: string;
1723
date: string;
1824
readtime_minutes: number;
1925
author: string;
2026
permalink: string;
2127
basename: string; // used for MD filename and image directory name
2228

29+
// project pages use these
30+
agency: string;
31+
project_url: string;
32+
impact_statement: ImpactStatementField[];
33+
2334
carousel_title: string;
2435
carousel_summary: string;
2536
carousel_image: string;
@@ -28,13 +39,19 @@ export interface BlogFrontMatterFields {
2839
tags: string[];
2940
}
3041

31-
export const BLANK_BLOG_FRONTMATTER_FIELDS: BlogFrontMatterFields = {
42+
export const BLANK_FRONTMATTER_FIELDS: FrontMatterFields = {
3243
title: "",
3344
date: "",
3445
readtime_minutes: 1,
3546
author: DEFAULT_AUTHOR,
3647
permalink: "",
37-
basename: "",
48+
basename: "basename",
49+
50+
agency: "",
51+
project_url: "",
52+
impact_statement: [],
53+
54+
3855
carousel_title: "",
3956
carousel_summary: "",
4057
carousel_image: "",
@@ -43,13 +60,16 @@ export const BLANK_BLOG_FRONTMATTER_FIELDS: BlogFrontMatterFields = {
4360
tags: [],
4461
};
4562

46-
export const STARTER_BLOG_FRONTMATTER_FIELDS: BlogFrontMatterFields = {
63+
export const STARTER_BLOG_FRONTMATTER_FIELDS: FrontMatterFields = {
4764
title: "New news and blog page",
4865
date: getShortDate(new Date().toDateString()),
4966
readtime_minutes: 1,
5067
author: DEFAULT_AUTHOR,
5168
permalink: "/news-and-blog/new-news-and-blog-page-tmpl7",
5269
basename: "new-news-and-blog-page",
70+
agency: "",
71+
project_url: "",
72+
impact_statement: [],
5373
carousel_title: "New news and blog page",
5474
carousel_summary: `This is a blank news-and-blog page template. Click the crab icon to edit metadata.
5575
Enter new content below this header preview`,

0 commit comments

Comments
 (0)