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
3 changes: 2 additions & 1 deletion apps/site/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ can also search OJS with their own OJS API key.
| What | Where it lives | How to change it |
| --- | --- | --- |
| Homepage, About and other section-built pages | site-content: `pages/*.yaml` | `/admin`, or edit the file |
| The journal's own section-built pages (For Authors) | site-content: `pages/journal/*.yaml` | `/admin` → Journal pages |
| Menus, footer, social links | site-content: `settings/navigation.yaml` | `/admin` → Settings → Menus |
| Site name, newsletter, analytics | site-content: `settings/site.yaml` | `/admin` → Settings → Site |
| Images and files uploaded in `/admin` | site-content: `uploads/` | `/admin` |
Expand Down Expand Up @@ -63,7 +64,7 @@ Dockerfile packages the built site, see docs/deploying.md
```

site-content holds `pages/` (section-built pages, `index.yaml` is the
homepage), `blog/` (posts, Markdown), `people/`, `categories/`, `settings/`
homepage, `journal/` the journal's own pages), `blog/` (posts, Markdown), `people/`, `categories/`, `settings/`
(`site.yaml`, `navigation.yaml`) and `uploads/`.

## What's where
Expand Down
24 changes: 23 additions & 1 deletion apps/site/docs/editing.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,35 @@ bottom. You can add, remove, reorder and edit sections:
- **Newsletter sign-up**
- **Buttons**

For pages that guide someone through steps, like the journal's For Authors
page, there are also:

- **On this page**: links to the sections below it that have an anchor and a
title. "Short name" gives a section a shorter label there.
- **Chooser**: cards to pick from, each opening its own panel beside them
("What are you submitting?"). A panel holds text, numbered parts, cards,
steps and "read more" boxes. Each card has an anchor, so
`/journal/for-authors#registered-report` opens that card's panel. A card
with no panel content is a plain card that opens nothing.
- **Cards**: boxes in a grid, optionally marked Required, Recommended or Not
needed, with a key above them if you tick it. Each card can be framed,
tinted, dashed, titled in a dark bar or have only a line on top, and can
have small tags and extra text that opens on click. A tag can be a link:
`[Methodology](https://credit.niso.org/contributor-roles/methodology/)`.
- **Steps**: a numbered process. Several routes (Standard, Perspective,
Registered Report) become tabs.

Text sections can have buttons too.

Text fields take Markdown: `**bold**`, `*italic*`, `[a link](https://...)`.

To make a new page, click **New Page** in `/admin`. Its address is its title
in lowercase with dashes (`Open Positions` becomes `/open-positions`).

The page called `journal` is the journal's front page at `/journal`, with the
journal's menu above it. All articles are at `/journal/articles`, as cards or
journal's menu above it. **Journal pages** in `/admin` are the journal's own
pages: `for-authors` is `/journal/for-authors`, under the journal's menu and in
its colours. A journal page replaces the PubPub page at the same address. All articles are at `/journal/articles`, as cards or
a compact list depending on Settings → Site → Journal.

The preview next to the fields is the page as the site draws it, updated a
Expand Down
180 changes: 179 additions & 1 deletion apps/site/src/admin/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ collections:
- { name: title, label: Title, widget: string }
- { name: description, label: Description (for search engines and link previews), widget: text, required: false }
- { name: showTitle, label: Show the title as a big heading, widget: boolean, default: false }
- name: sections
- &sections
name: sections
label: Sections
label_singular: Section
widget: list
Expand Down Expand Up @@ -93,6 +94,7 @@ collections:
- { name: id, label: Anchor (for links like /about#mission), widget: string, required: false }
- { name: body, label: Text, widget: markdown }
- { name: background, label: Background, widget: select, options: [paper, accent, accent-soft, journal, journal-soft], default: paper }
- *buttons
- name: features
label: Columns
widget: object
Expand Down Expand Up @@ -151,6 +153,182 @@ collections:
widget: object
fields:
- *buttons
# Building blocks for structured pages like the journal's For Authors page
- name: contents
label: On this page (links to the sections below)
widget: object
summary: "{{title}}"
fields:
- { name: title, label: Title, widget: string, default: On this page }
- name: chooser
label: Chooser (cards that each open a panel)
widget: object
summary: "{{title}}"
fields:
- &eyebrow { name: eyebrow, label: Small text above the title (“Step 1”), widget: string, required: false }
- &title { name: title, label: Title, widget: string, required: false }
- &id { name: id, label: "Anchor (for links like #types)", widget: string, required: false }
- &short { name: short, label: Short name in “On this page”, widget: string, required: false }
- &intro { name: intro, label: Intro, widget: markdown, required: false }
- name: groups
label: Groups of cards
label_singular: Group
widget: list
summary: "{{title}}"
fields:
- { name: title, label: Title (“What are you submitting?”), widget: string, required: false }
- { name: note, label: Note beside the title, widget: string, required: false }
- name: options
label: Cards
label_singular: Card
widget: list
summary: "{{title}}"
fields:
- { name: title, label: Title, widget: string }
- { name: id, label: "Anchor (link straight to it with #anchor; made from the title if empty)", widget: string, required: false }
- { name: badge, label: Badge (“New”, “Pilot”), widget: string, required: false }
- { name: summary, label: One-line summary, widget: text, required: false }
- name: facts
label: Facts on the card
label_singular: Fact
widget: list
required: false
summary: "{{label}}: {{value}}"
fields:
- { name: label, label: Label (“Review”), widget: string }
- { name: value, label: Value (“standard open review”), widget: string }
- { name: tone, label: Colour when chosen, widget: select, options: [{ label: Black, value: ink }, { label: Journal blue, value: journal }], default: ink }
- { name: heading, label: Panel title (the card’s title if empty), widget: string, required: false }
- { name: note, label: Small text beside the panel title, widget: string, required: false }
- name: blocks
label: Panel content
label_singular: Block
hint: Leave empty for a card that only informs, with nothing to open
widget: list
typeKey: type
types:
- name: text
label: Text
widget: object
fields:
- { name: body, label: Text, widget: markdown }
- name: parts
label: Numbered parts
widget: object
fields:
- name: items
label: Parts
label_singular: Part
widget: list
summary: "{{title}}"
fields:
- { name: title, label: Title, widget: string }
- { name: text, label: Text, widget: markdown, required: false }
- name: cards
label: Cards
widget: object
fields:
- { name: columns, label: Most cards side by side, widget: number, value_type: int, min: 1, max: 4, required: false }
- &cards
name: items
label: Cards
label_singular: Card
widget: list
summary: "{{title}}"
fields:
- { name: title, label: Title, widget: string }
- { name: body, label: Text, widget: markdown, required: false }
- { name: status, label: Status, widget: select, required: false, options: [{ label: Required, value: required }, { label: Recommended, value: recommended }, { label: Not needed, value: not-needed }] }
- { name: badge, label: Badge text (instead of the status’s own), widget: string, required: false }
- name: look
label: Look
widget: select
default: box
options:
- { label: Framed, value: box }
- { label: Title in a dark bar, value: headed }
- { label: Tinted, value: soft }
- { label: Dashed frame, value: dashed }
- { label: Line on top, value: rule }
- { label: No frame, value: plain }
- { name: tags, label: Small boxed words under the text, widget: list, required: false, hint: "Separate with commas. For a link, write [Word](https://…)" }
- name: more
label: Extra text that opens on click
widget: object
required: false
fields:
- { name: label, label: Button label, widget: string }
- { name: body, label: Text, widget: markdown }
- name: steps
label: Steps
widget: object
fields:
- &steps
name: steps
label: Steps
label_singular: Step
widget: list
summary: "{{title}}"
fields:
- { name: title, label: Title, widget: string }
- { name: kicker, label: Small text above (“Stage 1 · You”; numbered if empty), widget: string, required: false }
- { name: text, label: Text, widget: markdown, required: false }
- { name: highlight, label: Highlight (a milestone), widget: boolean, default: false }
- name: more
label: Read more (opens on click)
widget: object
summary: "{{label}}"
fields:
- { name: label, label: Button label, widget: string }
- { name: body, label: Text, widget: markdown }
- name: cards
label: Cards
widget: object
summary: "{{title}}"
fields:
- *eyebrow
- *title
- *id
- *short
- *intro
- { name: legend, label: Explain the Required / Recommended / Not needed badges, widget: boolean, default: false }
- { name: columns, label: Most cards side by side, widget: number, value_type: int, min: 1, max: 4, required: false }
- *cards
- name: steps
label: Steps (a numbered process)
widget: object
summary: "{{title}}"
fields:
- *eyebrow
- *title
- *id
- *short
- *intro
- name: routes
label: Routes (more than one become tabs)
label_singular: Route
widget: list
summary: "{{label}}"
fields:
- { name: label, label: Tab label, widget: string, required: false }
- *steps
- { name: note, label: Note under the steps, widget: markdown, required: false }

- name: journal-pages
label: Journal pages
label_singular: Journal page
description: Pages under /journal, in the journal's colours and under its menu (for-authors is /journal/for-authors). They replace the PubPub page at the same address.
folder: pages/journal
extension: yaml
format: yaml
create: true
slug: "{{slug}}"
identifier_field: title
fields:
- { name: title, label: Title, widget: string }
- { name: description, label: Text under the title (also for search engines and link previews), widget: text, required: false }
- { name: showTitle, label: Show the title as a big heading, widget: boolean, default: true }
- *sections

- name: blog
label: Blog
Expand Down
9 changes: 6 additions & 3 deletions apps/site/src/admin/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,9 @@ const session = () => JSON.parse(localStorage.getItem("sveltia-cms.user") ?? "nu
* as the editor has it goes to /admin/preview (src/pages/admin/preview.astro),
* which answers with the page as it will look, shown in a frame.
*/
function PagePreview({ entry }: PreviewProps) {
function PagePreview({ entry, collection }: PreviewProps & { collection: string }) {
const [html, setHtml] = React.useState("<p style='font-family: sans-serif'>Loading the preview…</p>");
const body = JSON.stringify({ slug: entry.get("slug"), data: entry.get("data")?.toJS?.() ?? {} });
const body = JSON.stringify({ slug: entry.get("slug"), collection, data: entry.get("data")?.toJS?.() ?? {} });

React.useEffect(() => {
const controller = new AbortController();
Expand All @@ -147,4 +147,7 @@ function PagePreview({ entry }: PreviewProps) {
return h("iframe", { srcDoc: html, title: "Preview", style: { width: "100%", height: "100vh", border: 0, display: "block" } });
}

CMS.registerPreviewTemplate("pages", PagePreview);
// Journal pages are drawn under the journal's menu
for (const collection of ["pages", "journal-pages"]) {
CMS.registerPreviewTemplate(collection, (props: PreviewProps) => h(PagePreview, { ...props, collection }));
}
20 changes: 18 additions & 2 deletions apps/site/src/components/Sections.astro
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
---
import type { Section } from "~/content.config";
import Buttons from "./sections/Buttons.astro";
import CardsSection from "./sections/CardsSection.astro";
import Chooser from "./sections/Chooser.astro";
import Contents from "./sections/Contents.astro";
import Features from "./sections/Features.astro";
import Hero from "./sections/Hero.astro";
import Issues from "./sections/Issues.astro";
import LatestArticles from "./sections/LatestArticles.astro";
import LatestPosts from "./sections/LatestPosts.astro";
import Newsletter from "./sections/Newsletter.astro";
import StepsSection from "./sections/StepsSection.astro";
import Team from "./sections/Team.astro";
import Text from "./sections/Text.astro";

Expand All @@ -26,14 +30,26 @@ const components = {
team: Team,
newsletter: Newsletter,
buttons: Buttons,
contents: Contents,
chooser: Chooser,
cards: CardsSection,
steps: StepsSection,
} satisfies Record<Section["type"], unknown>;

const { sections } = Astro.props;

// "On this page" lists the sections after it that have an anchor and a title
const linksAfter = (index: number) =>
sections.slice(index + 1).flatMap((s) => {
if (!("id" in s) || !s.id || !("title" in s) || !s.title) return [];
const label = ("short" in s && s.short) || s.title;
return [{ id: s.id, label }];
});
---

{
sections.map((section) => {
sections.map((section, i) => {
const Component = components[section.type] as any;
return <Component {...section} />;
return section.type === "contents" ? <Contents title={section.title} links={linksAfter(i)} /> : <Component {...section} />;
})
}
41 changes: 41 additions & 0 deletions apps/site/src/components/blocks/Blocks.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
import type { Block } from "~/content.config";
import Markdown from "../Markdown.astro";
import Cards from "./Cards.astro";
import More from "./More.astro";
import Steps from "./Steps.astro";

interface Props {
blocks: Block[];
}

const { blocks } = Astro.props;
---

{
blocks.map((block) => {
switch (block.type) {
case "text":
return <Markdown text={block.body} class="card-text max-w-3xl" />;
case "parts":
return (
<ol class="border-[1.5px] border-ink">
{block.items.map((part, i) => (
<li class="flex flex-wrap gap-x-6 gap-y-1 border-ink px-4 py-3 not-last:border-b-[1.5px] even:bg-journal-soft">
<strong class="shrink-0 basis-48">
{i + 1} · {part.title}
</strong>
{part.text && <Markdown text={part.text} class="card-text min-w-0 flex-1 basis-64" />}
</li>
))}
</ol>
);
case "cards":
return <Cards items={block.items} min="14rem" columns={block.columns} />;
case "steps":
return <Steps steps={block.steps} />;
case "more":
return <More label={block.label} body={block.body} />;
}
})
}
Loading
Loading