Skip to content
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

Add support for draft documentation pages #1613

Merged
merged 13 commits into from Apr 30, 2024
5 changes: 5 additions & 0 deletions .changeset/six-vans-trade.md
@@ -0,0 +1,5 @@
---
'@astrojs/starlight': minor
---

Adds new `draft` frontmatter option to exclude a page from production builds.
1 change: 1 addition & 0 deletions docs/src/content/docs/guides/pages.mdx
Expand Up @@ -105,6 +105,7 @@ The following properties differ from Markdown frontmatter:
- The [`slug`](/reference/frontmatter/#slug) property is not supported and is automatically set based on the custom page’s URL.
- The [`editUrl`](/reference/frontmatter/#editurl) option requires a URL to display an edit link.
- The [`sidebar`](/reference/frontmatter/#sidebar) frontmatter property for customizing how the page appears in [autogenerated link groups](/reference/configuration/#sidebar) is not available. Pages using the `<StarlightPage />` component are not part of a collection and cannot be added to an autogenerated sidebar group.
- The [`draft`](/reference/frontmatter/#draft) option only displays a [notice](/reference/overrides/#draftcontentnotice) that the page is a draft but does not automatically exclude it from production builds.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this correct or should it read like this:

Suggested change
- The [`draft`](/reference/frontmatter/#draft) option only displays a [notice](/reference/overrides/#draftcontentnotice) that the page is a draft but does not automatically exclude it from production builds.
- The [`draft`](/reference/frontmatter/#draft) option displays a [notice](/reference/overrides/#draftcontentnotice) that the page is a draft and will not be included in production builds.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually correct in this specific section as we are talking about custom pages automatically injected by a plugin for example so it would be up to the plugin to make sure the page is not injected when not needed.

Altho, this may also be poorly worded as I'm the one who wrote it so may need some tweaking ^^


##### `sidebar`

Expand Down
15 changes: 15 additions & 0 deletions docs/src/content/docs/reference/frontmatter.md
Expand Up @@ -268,6 +268,21 @@ pagefind: false
---
```

### `draft`

**type:** `boolean`
**default:** `false`

Set whether this page should be considered a draft and not be included in the site [production builds](https://docs.astro.build/en/reference/cli-reference/#astro-build) and [autogenerated link groups](/guides/sidebar/#autogenerated-groups). Set to `true` to mark a page as a draft and make it only visible during development.
delucis marked this conversation as resolved.
Show resolved Hide resolved

```md
---
# src/content/docs/example.md
# Exclude this page from production builds
draft: true
---
```

### `sidebar`

**type:** [`SidebarConfig`](#sidebarconfig)
Expand Down
6 changes: 6 additions & 0 deletions docs/src/content/docs/reference/overrides.md
Expand Up @@ -332,6 +332,12 @@ Component containing the `<h1>` element for the current page.

Implementations should ensure they set `id="_top"` on the `<h1>` element as in the default implementation.

#### `DraftContentNotice`

**Default component:** [`DraftContentNotice.astro`](https://github.com/withastro/starlight/blob/main/packages/starlight/components/DraftContentNotice.astro)

Notice displayed to users during development when the current page is marked as a draft.

#### `FallbackContentNotice`

**Default component:** [`FallbackContentNotice.astro`](https://github.com/withastro/starlight/blob/main/packages/starlight/components/FallbackContentNotice.astro)
Expand Down
1 change: 1 addition & 0 deletions packages/starlight/__tests__/basics/config-errors.test.ts
Expand Up @@ -17,6 +17,7 @@ test('parses valid config successfully', () => {
"components": {
"Banner": "@astrojs/starlight/components/Banner.astro",
"ContentPanel": "@astrojs/starlight/components/ContentPanel.astro",
"DraftContentNotice": "@astrojs/starlight/components/DraftContentNotice.astro",
"EditLink": "@astrojs/starlight/components/EditLink.astro",
"FallbackContentNotice": "@astrojs/starlight/components/FallbackContentNotice.astro",
"Footer": "@astrojs/starlight/components/Footer.astro",
Expand Down
20 changes: 19 additions & 1 deletion packages/starlight/__tests__/basics/routing.test.ts
Expand Up @@ -8,7 +8,7 @@ vi.mock('astro:content', async () =>
docs: [
['404.md', { title: 'Not found' }],
['index.mdx', { title: 'Home page' }],
['guides/authoring-content.md', { title: 'Authoring content' }],
['guides/authoring-content.md', { title: 'Authoring content', draft: true }],
],
})
);
Expand Down Expand Up @@ -41,3 +41,21 @@ test('routes have locale data added', () => {
expect(route.locale).toBeUndefined();
}
});

test('routes includes drafts except in production', async () => {
expect(routes.find((route) => route.id === 'guides/authoring-content.md')).toBeTruthy();

// Reset the modules registry so that re-importing `utils/routing.ts` re-evaluates the module and
// re-computes the routes. Re-importing the module is necessary because top-level imports cannot
// be re-evaluated.
vi.resetModules();
// Set the mode to production.
vi.stubEnv('MODE', 'production');
// Re-import the module to re-evaluate it.
const { routes: prodRoutes } = await import('../../utils/routing');

expect(prodRoutes.find((route) => route.id === 'guides/authoring-content.md')).toBeFalsy();

vi.unstubAllEnvs();
vi.resetModules();
});
8 changes: 7 additions & 1 deletion packages/starlight/__tests__/test-utils.ts
Expand Up @@ -53,7 +53,13 @@ export async function mockedAstroContent({
const mockDicts = i18n.map((dict) => mockDict(...dict));
return {
...mod,
getCollection: (collection: 'docs' | 'i18n') => (collection === 'i18n' ? mockDicts : mockDocs),
getCollection: (
collection: 'docs' | 'i18n',
filter?: (entry: ReturnType<typeof mockDoc> | ReturnType<typeof mockDict>) => unknown
) => {
const entries = collection === 'i18n' ? mockDicts : mockDocs;
return filter ? entries.filter(filter) : entries;
},
};
}

Expand Down
31 changes: 31 additions & 0 deletions packages/starlight/components/ContentNotice.astro
@@ -0,0 +1,31 @@
---
import { Icons } from '../components/Icons';
import Icon from '../user-components/Icon.astro';

interface Props {
icon: keyof typeof Icons;
label: string;
}

const { icon, label } = Astro.props;
---

<p class="sl-flex">
<Icon name={icon} size="1.5em" color="var(--sl-color-orange-high)" />
<span>{label}</span>
</p>

<style>
p {
border: 1px solid var(--sl-color-orange);
padding: 0.75em 1em;
background-color: var(--sl-color-orange-low);
color: var(--sl-color-orange-high);
width: max-content;
max-width: 100%;
align-items: center;
gap: 0.75em;
font-size: var(--sl-text-body-sm);
line-height: var(--sl-line-height-headings);
}
</style>
8 changes: 8 additions & 0 deletions packages/starlight/components/DraftContentNotice.astro
@@ -0,0 +1,8 @@
---
import ContentNotice from './ContentNotice.astro';
import type { Props } from '../props';

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

<ContentNotice icon="warning" label={labels['page.draft']} />
23 changes: 2 additions & 21 deletions packages/starlight/components/FallbackContentNotice.astro
@@ -1,27 +1,8 @@
---
import Icon from '../user-components/Icon.astro';
import ContentNotice from './ContentNotice.astro';
import type { Props } from '../props';

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

<p class="sl-flex">
<Icon name={'warning'} size="1.5em" color="var(--sl-color-orange-high)" /><span
>{labels['i18n.untranslatedContent']}</span
>
</p>

<style>
p {
border: 1px solid var(--sl-color-orange);
padding: 0.75em 1em;
background-color: var(--sl-color-orange-low);
color: var(--sl-color-orange-high);
width: max-content;
max-width: 100%;
align-items: center;
gap: 0.75em;
font-size: var(--sl-text-body-sm);
line-height: var(--sl-line-height-headings);
}
</style>
<ContentNotice icon="warning" label={labels['i18n.untranslatedContent']} />
2 changes: 2 additions & 0 deletions packages/starlight/components/Page.astro
Expand Up @@ -11,6 +11,7 @@ import '../style/util.css';
import Banner from 'virtual:starlight/components/Banner';
import ContentPanel from 'virtual:starlight/components/ContentPanel';
import FallbackContentNotice from 'virtual:starlight/components/FallbackContentNotice';
import DraftContentNotice from 'virtual:starlight/components/DraftContentNotice';
import Footer from 'virtual:starlight/components/Footer';
import Head from 'virtual:starlight/components/Head';
import Header from 'virtual:starlight/components/Header';
Expand Down Expand Up @@ -101,6 +102,7 @@ const pagefindEnabled =
<>
<ContentPanel {...Astro.props}>
<PageTitle {...Astro.props} />
{Astro.props.entry.data.draft && <DraftContentNotice {...Astro.props} />}
{Astro.props.isFallback && <FallbackContentNotice {...Astro.props} />}
</ContentPanel>
<ContentPanel {...Astro.props}>
Expand Down
4 changes: 4 additions & 0 deletions packages/starlight/package.json
Expand Up @@ -98,6 +98,10 @@
"types": "./components/FallbackContentNotice.astro.tsx",
"import": "./components/FallbackContentNotice.astro"
},
"./components/DraftContentNotice.astro": {
"types": "./components/DraftContentNotice.astro.tsx",
"import": "./components/DraftContentNotice.astro"
},
"./components/Page.astro": {
"types": "./components/Page.astro.tsx",
"import": "./components/Page.astro"
Expand Down
6 changes: 6 additions & 0 deletions packages/starlight/schema.ts
Expand Up @@ -103,6 +103,12 @@ const StarlightFrontmatterSchema = (context: SchemaContext) =>

/** Pagefind indexing for this page - set to false to disable. */
pagefind: z.boolean().default(true),

/**
* Indicates that this page is a draft and will not be included in production builds.
* Note that the page will still be available when running Astro in development mode.
*/
draft: z.boolean().default(false),
});
/** Type of Starlight’s default frontmatter schema. */
type DefaultSchema = ReturnType<typeof StarlightFrontmatterSchema>;
Expand Down
9 changes: 9 additions & 0 deletions packages/starlight/schemas/components.ts
Expand Up @@ -202,6 +202,15 @@ export function ComponentConfigSchema() {
.string()
.default('@astrojs/starlight/components/FallbackContentNotice.astro'),

/**
* Notice displayed to users on draft pages. Only used in development mode.
*
* @see {@link https://github.com/withastro/starlight/blob/main/packages/starlight/components/DraftContentNotice.astro `DraftContentNotice` default implementation}
*/
DraftContentNotice: z
.string()
.default('@astrojs/starlight/components/DraftContentNotice.astro'),

/**
* Component rendered at the top of the page when `hero` is set in frontmatter. The default
* implementation shows a large title, tagline, and call-to-action links alongside an optional image.
Expand Down
6 changes: 6 additions & 0 deletions packages/starlight/schemas/i18n.ts
Expand Up @@ -116,6 +116,12 @@ function starlightI18nSchema() {
.string()
.describe('Label shown on the “next page” pagination arrow in the page footer.'),

'page.draft': z
.string()
.describe(
'Development-only notice informing users they are on a page that is a draft which will not be included in production builds.'
),

'404.text': z.string().describe('Text shown on Starlight’s default 404 page'),
'aside.tip': z.string().describe('Text shown on the tip aside variant'),
'aside.note': z.string().describe('Text shown on the note aside variant'),
Expand Down
1 change: 1 addition & 0 deletions packages/starlight/translations/ar.json
Expand Up @@ -18,6 +18,7 @@
"page.lastUpdated": "اخر تحديث:",
"page.previousLink": "السابق",
"page.nextLink": "التالي",
"page.draft": "This content is a draft and will not be included in production builds.",
"404.text": "الصفحة غير موجودة. تأكد من الرابط أو ابحث بإستعمال شريط البحث.",
"aside.note": "ملحوظة",
"aside.tip": "نصيحة",
Expand Down
1 change: 1 addition & 0 deletions packages/starlight/translations/cs.json
Expand Up @@ -18,6 +18,7 @@
"page.lastUpdated": "Poslední aktualizace:",
"page.previousLink": "Předchozí",
"page.nextLink": "Další",
"page.draft": "This content is a draft and will not be included in production builds.",
"404.text": "Stránka nenalezena. Zkontrolujte adresu URL nebo zkuste použít vyhledávací pole.",
"aside.note": "Note",
"aside.tip": "Tip",
Expand Down
1 change: 1 addition & 0 deletions packages/starlight/translations/da.json
Expand Up @@ -18,6 +18,7 @@
"page.lastUpdated": "Sidst opdateret:",
"page.previousLink": "Forrige",
"page.nextLink": "Næste",
"page.draft": "This content is a draft and will not be included in production builds.",
"404.text": "Siden er ikke fundet. Tjek din URL eller prøv søgelinjen.",
"aside.note": "Note",
"aside.tip": "Tip",
Expand Down
1 change: 1 addition & 0 deletions packages/starlight/translations/de.json
Expand Up @@ -18,6 +18,7 @@
"page.lastUpdated": "Zuletzt bearbeitet:",
"page.previousLink": "Vorherige Seite",
"page.nextLink": "Nächste Seite",
"page.draft": "This content is a draft and will not be included in production builds.",
"404.text": "Seite nicht gefunden. Überprüfe die URL oder nutze die Suchleiste.",
"aside.note": "Hinweis",
"aside.tip": "Tipp",
Expand Down
1 change: 1 addition & 0 deletions packages/starlight/translations/en.json
Expand Up @@ -18,6 +18,7 @@
"page.lastUpdated": "Last updated:",
"page.previousLink": "Previous",
"page.nextLink": "Next",
"page.draft": "This content is a draft and will not be included in production builds.",
"404.text": "Page not found. Check the URL or try using the search bar.",
"aside.note": "Note",
"aside.tip": "Tip",
Expand Down
1 change: 1 addition & 0 deletions packages/starlight/translations/es.json
Expand Up @@ -18,6 +18,7 @@
"page.lastUpdated": "Última actualización:",
"page.previousLink": "Página anterior",
"page.nextLink": "Siguiente página",
"page.draft": "This content is a draft and will not be included in production builds.",
"404.text": "Página no encontrada. Verifica la URL o intenta usar la barra de búsqueda.",
"aside.note": "Nota",
"aside.tip": "Consejo",
Expand Down
1 change: 1 addition & 0 deletions packages/starlight/translations/fa.json
Expand Up @@ -18,6 +18,7 @@
"page.lastUpdated": "آخرین به روز رسانی:",
"page.previousLink": "قبلی",
"page.nextLink": "بعدی",
"page.draft": "This content is a draft and will not be included in production builds.",
"404.text": "صفحه یافت نشد. لطفاً URL را بررسی کنید یا از جستجو استفاده نمایید.",
"aside.note": "Note",
"aside.tip": "Tip",
Expand Down
1 change: 1 addition & 0 deletions packages/starlight/translations/fr.json
Expand Up @@ -18,6 +18,7 @@
"page.lastUpdated": "Dernière mise à jour :",
"page.previousLink": "Précédent",
"page.nextLink": "Suivant",
"page.draft": "Ce contenu est une ébauche et ne sera pas inclus dans la version de production.",
"404.text": "Page non trouvée. Vérifiez l’URL ou essayez d’utiliser la barre de recherche.",
"aside.note": "Note",
"aside.tip": "Astuce",
Expand Down
1 change: 1 addition & 0 deletions packages/starlight/translations/gl.json
Expand Up @@ -18,6 +18,7 @@
"page.lastUpdated": "Última actualización:",
"page.previousLink": "Anterior",
"page.nextLink": "Seguinte",
"page.draft": "This content is a draft and will not be included in production builds.",
"404.text": "Paxina non atopada. Comproba a URL ou intenta usar a barra de busca.",
"aside.note": "Note",
"aside.tip": "Tip",
Expand Down
1 change: 1 addition & 0 deletions packages/starlight/translations/he.json
Expand Up @@ -18,6 +18,7 @@
"page.lastUpdated": "עדכון אחרון:",
"page.previousLink": "הקודם",
"page.nextLink": "הבא",
"page.draft": "This content is a draft and will not be included in production builds.",
"404.text": "הדף לא נמצא. אנא בדקו את כתובת האתר או נסו להשתמש בסרגל החיפוש.",
"aside.note": "Note",
"aside.tip": "Tip",
Expand Down
1 change: 1 addition & 0 deletions packages/starlight/translations/hi.json
Expand Up @@ -18,6 +18,7 @@
"page.lastUpdated": "आखिरी अद्यतन:",
"page.previousLink": "पिछला",
"page.nextLink": "अगला",
"page.draft": "This content is a draft and will not be included in production builds.",
"404.text": "यह पृष्ठ नहीं मिला। URL जांचें या खोज बार का उपयोग करने का प्रयास करें।",
"aside.note": "टिप्पणी",
"aside.tip": "संकेत",
Expand Down
1 change: 1 addition & 0 deletions packages/starlight/translations/id.json
Expand Up @@ -18,6 +18,7 @@
"page.lastUpdated": "Terakhir diperbaharui:",
"page.previousLink": "Sebelumnya",
"page.nextLink": "Selanjutnya",
"page.draft": "This content is a draft and will not be included in production builds.",
"404.text": "Halaman tidak ditemukan. Cek kembali kolom URL atau gunakan fitur pencarian.",
"aside.note": "Catatan",
"aside.tip": "Tips",
Expand Down
1 change: 1 addition & 0 deletions packages/starlight/translations/it.json
Expand Up @@ -18,6 +18,7 @@
"page.lastUpdated": "Ultimo aggiornamento:",
"page.previousLink": "Indietro",
"page.nextLink": "Avanti",
"page.draft": "This content is a draft and will not be included in production builds.",
"404.text": "Pagina non trovata. Verifica l'URL o prova a utilizzare la barra di ricerca.",
"aside.note": "Nota",
"aside.tip": "Consiglio",
Expand Down
1 change: 1 addition & 0 deletions packages/starlight/translations/ja.json
Expand Up @@ -18,6 +18,7 @@
"page.lastUpdated": "最終更新日:",
"page.previousLink": "前へ",
"page.nextLink": "次へ",
"page.draft": "This content is a draft and will not be included in production builds.",
"404.text": "ページが見つかりません。 URL を確認するか、検索バーを使用してみてください。",
"aside.note": "ノート",
"aside.tip": "ヒント",
Expand Down
1 change: 1 addition & 0 deletions packages/starlight/translations/ko.json
Expand Up @@ -18,6 +18,7 @@
"page.lastUpdated": "마지막 업데이트:",
"page.previousLink": "이전",
"page.nextLink": "다음",
"page.draft": "This content is a draft and will not be included in production builds.",
"404.text": "페이지를 찾을 수 없습니다. URL을 확인하거나 검색 막대를 사용해 보세요.",
"aside.note": "노트",
"aside.tip": "팁",
Expand Down
1 change: 1 addition & 0 deletions packages/starlight/translations/nb.json
Expand Up @@ -18,6 +18,7 @@
"page.lastUpdated": "Sist oppdatert:",
"page.previousLink": "Forrige",
"page.nextLink": "Neste",
"page.draft": "This content is a draft and will not be included in production builds.",
"404.text": "Siden ble ikke funnet. Sjekk URL-en eller prøv å bruke søkefeltet.",
"aside.note": "Note",
"aside.tip": "Tip",
Expand Down
1 change: 1 addition & 0 deletions packages/starlight/translations/nl.json
Expand Up @@ -18,6 +18,7 @@
"page.lastUpdated": "Laatst bewerkt:",
"page.previousLink": "Vorige",
"page.nextLink": "Volgende",
"page.draft": "This content is a draft and will not be included in production builds.",
"404.text": "Pagina niet gevonden. Controleer de URL of probeer de zoekbalk.",
"aside.note": "Opmerking",
"aside.tip": "Tip",
Expand Down
1 change: 1 addition & 0 deletions packages/starlight/translations/pl.json
Expand Up @@ -18,6 +18,7 @@
"page.lastUpdated": "Ostatnia aktualizacja:",
"page.previousLink": "Poprzednia strona",
"page.nextLink": "Następna strona",
"page.draft": "This content is a draft and will not be included in production builds.",
"404.text": "Nie znaleziono. Sprawdź URL lub użyj wyszukiwarki.",
"aside.note": "Notatka",
"aside.tip": "Wskazówka",
Expand Down
1 change: 1 addition & 0 deletions packages/starlight/translations/pt.json
Expand Up @@ -18,6 +18,7 @@
"page.lastUpdated": "Última atualização:",
"page.previousLink": "Anterior",
"page.nextLink": "Próximo",
"page.draft": "Esse conteúdo é um rascunho e não será incluído em builds de produção.",
"404.text": "Página não encontrada. Verifique o URL ou tente usar a barra de pesquisa.",
"aside.note": "Nota",
"aside.tip": "Dica",
Expand Down