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

"Virtual" pages prototype #1175

Merged
merged 43 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
fdb96f3
feat: add basic virtual page prototype
HiDeoo Nov 30, 2023
1525ed3
Merge branch 'main' into hd-virtual-pages
HiDeoo Dec 27, 2023
62e3ffc
feat: add virtual frontmatter
HiDeoo Dec 27, 2023
9151563
feat: make `hasSidebar` prop optional
HiDeoo Dec 27, 2023
e49999f
feat: make `headings` prop optional
HiDeoo Dec 27, 2023
4577df5
feat: make `dir` & `lang` props optional
HiDeoo Dec 27, 2023
478ddc2
chore: add example with mininal set of virtual route props
HiDeoo Dec 27, 2023
6aa37e0
fix: virtual page hero
HiDeoo Dec 27, 2023
c45c45f
chore: add banner example
HiDeoo Dec 27, 2023
88455a7
feat: add `makeVirtualStaticPaths()` helper
HiDeoo Dec 29, 2023
5737021
docs: add `isFallback` documentation
HiDeoo Dec 29, 2023
fc20da3
docs: reorder virtual page props
HiDeoo Dec 29, 2023
162b23e
docs: rewrite virtual page code example
HiDeoo Dec 29, 2023
823b259
Merge branch 'main' into hd-virtual-pages
HiDeoo Jan 15, 2024
104ddd0
chore: update pr
HiDeoo Jan 15, 2024
7448aca
Merge branch 'main' into hd-virtual-pages
HiDeoo Jan 23, 2024
7a9db5a
feat: remove `makeVirtualStaticPaths()` helper
HiDeoo Jan 23, 2024
611976a
refactor: isolate virtual page code
HiDeoo Jan 23, 2024
482fb11
feat: add support for edit URLs
HiDeoo Jan 23, 2024
ef9df1f
refactor: move virtual page frontmatter props to a dedicated property
HiDeoo Jan 23, 2024
de6ef91
docs: fix invalid link
HiDeoo Jan 23, 2024
2f6905e
feat: WIP use user frontmatter schema for validation
HiDeoo Jan 30, 2024
ef16232
feat: more WIP to use the user frontmatter schema for validation
HiDeoo Jan 31, 2024
93eb508
feat: use user frontmatter schema for validation
HiDeoo Feb 1, 2024
a887c62
refactor: virtual page type helpers
HiDeoo Feb 1, 2024
2107f4a
Merge branch 'main' into hd-virtual-pages
HiDeoo Feb 10, 2024
b974a7d
chore: fix lockfile
HiDeoo Feb 10, 2024
36ddbe5
chore: bump development and starter templates astro version to `4.3.5`
HiDeoo Feb 10, 2024
3ff92d7
chore: rename virtual pages to Starlight pages
HiDeoo Feb 10, 2024
8678fda
feat: improve Starlight page frontmatter validation errors
HiDeoo Feb 10, 2024
e29eccd
chore: remove `virtual-pages-demo`
HiDeoo Feb 10, 2024
9a04f7d
Chris docs: first pass
delucis Feb 12, 2024
2d7973d
test: remove unused import
HiDeoo Feb 13, 2024
9665a9d
feat: infer slug from `Astro.url`
HiDeoo Feb 13, 2024
a7d66fc
test: add `urlToSlug` root url tests
HiDeoo Feb 14, 2024
8e0c9fa
Add docs link
delucis Feb 16, 2024
4bd9ee3
Fallback improvement
delucis Feb 16, 2024
0741800
Nice whether today
delucis Feb 16, 2024
3a6850c
Merge branch 'main' into pr/1175
delucis Feb 16, 2024
02ca942
Improve custom pages section based on feedback
delucis Feb 16, 2024
802acb0
Add link
delucis Feb 16, 2024
ba4c60d
Comment out `sidebar` docs for first release
delucis Feb 16, 2024
8871b4d
Merge branch 'main' into hd-virtual-pages
delucis Feb 16, 2024
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 docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"@astrojs/starlight": "workspace:*",
"@lunariajs/core": "^0.0.25",
"@types/culori": "^2.0.0",
"astro": "^4.3.4",
"astro": "^4.3.5",
"culori": "^3.2.0",
"sharp": "^0.32.5"
},
Expand Down
38 changes: 2 additions & 36 deletions docs/src/content/docs/getting-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -83,43 +83,9 @@ Open this URL to start browsing your site.

Starlight is ready for you to add new content, or bring your existing files!

#### File formats
Add new pages to your site by creating Markdown files in the `src/content/docs/` directory.

Starlight supports authoring content in Markdown and MDX with no configuration required.
You can add support for Markdoc by installing the experimental [Astro Markdoc integration](https://docs.astro.build/en/guides/integrations-guide/markdoc/).

#### Add pages

Add new pages to your site by creating `.md` or `.mdx` files in `src/content/docs/`.
Use sub-folders to organize your files and to create multiple path segments.

For example, the following file structure will generate pages at `example.com/hello-world` and `example.com/guides/faq`:

import FileTree from '~/components/file-tree.astro';

<FileTree>

- src/
- content/
- docs/
- guides/
- faq.md
- hello-world.md

</FileTree>

#### Type-safe frontmatter

All Starlight pages share a customizable [common set of frontmatter properties](/reference/frontmatter/) to control how the page appears:

```md
---
title: Hello, World!
description: This is a page in my Starlight-powered site
---
```

If you forget anything important, Starlight will let you know.
Read more about file-based routing and support for MDX and Markdoc files in the [“Pages”](/guides/pages/) guide.

### Next steps

Expand Down
137 changes: 137 additions & 0 deletions docs/src/content/docs/guides/pages.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
---
title: Pages
description: Learn how to create and manage your documentation site’s pages with Starlight.
sidebar:
order: 1
---

Starlight generates your site’s HTML pages based on your content, with flexible options provided via Markdown frontmatter.
In addition, Starlight projects have full access to Astro’s powerful page generation tools.
delucis marked this conversation as resolved.
Show resolved Hide resolved
This guide shows how page generation works in Starlight.

## Content pages
Copy link
Member

Choose a reason for hiding this comment

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

Just wondering aloud whether another term like "Docs pages" or Documentation pages conveys more info here. Or even, "Autogenerated Pages" ... something that might stand a bit more in contrast to "Custom" pages.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah the naming was hard here. My thinking was that these are in src/content/, but yeah it’s not perfect. They’d probably both be “docs pages”, but maybe something like “autogenerated” could work… 🤔


### File formats

Starlight supports authoring content in Markdown and MDX with no configuration required.
You can add support for Markdoc by installing the experimental [Astro Markdoc integration](https://docs.astro.build/en/guides/integrations-guide/markdoc/).

### Add pages

Add new pages to your site by creating `.md` or `.mdx` files in `src/content/docs/`.
Use sub-folders to organize your files and to create multiple path segments.

For example, the following file structure will generate pages at `example.com/hello-world` and `example.com/reference/faq`:

import FileTree from '~/components/file-tree.astro';

<FileTree>

- src/
- content/
- docs/
- hello-world.md
- reference/
- faq.md

</FileTree>

### Type-safe frontmatter

All Starlight pages share a customizable [common set of frontmatter properties](/reference/frontmatter/) to control how the page appears:

```md
---
title: Hello, World!
description: This is a page in my Starlight-powered site
---
```

If you forget anything important, Starlight will let you know.

## Custom pages

For advanced use cases, you can create custom pages in Astro’s `src/pages/` directory.
delucis marked this conversation as resolved.
Show resolved Hide resolved
This is helpful if you need to build pages with a completely custom layout or generate a page from an alternative data source.
The `src/pages/` directory uses file-based routing and includes support for `.astro` files.
delucis marked this conversation as resolved.
Show resolved Hide resolved
delucis marked this conversation as resolved.
Show resolved Hide resolved

Read more in the [“Pages” guide in the Astro docs](https://docs.astro.build/en/basics/astro-pages/).

### Using Starlight’s design in custom pages

To use the Starlight layout in custom pages, wrap your page content with the `<StarlightPage />` component.
This can be helpful if you are generating content dynamically but still want to use Starlight’s design.

```astro
---
// src/pages/custom-page/example.astro
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
import CustomComponent from './CustomComponent.astro';
---

<StarlightPage frontmatter={{ title: 'My custom page' }}>
<p>This is a custom page with a custom component:</p>
<CustomComponent />
</StarlightPage>
```

#### Props

The `<StarlightPage />` component accepts the following props.

##### `frontmatter` (required)

**type:** `StarlightPageFrontmatter`

Set the [frontmatter properties](/reference/frontmatter/) for this page, similar to frontmatter in Markdown pages.
The [`title`](/reference/frontmatter/#title-required) property is required and all other properties are optional.

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) property is not supported. In Markdown frontmatter, this option allows customization of [autogenerated link groups](/reference/configuration/#sidebar), which is not applicable to pages using the `<StarlightPage />` component.

##### `sidebar`

**type:** `SidebarEntry[] | undefined`
**default:** the sidebar generated based on the [global `sidebar` config](/reference/configuration/#sidebar)

Provide a custom site navigation sidebar for this page.
delucis marked this conversation as resolved.
Show resolved Hide resolved
If not set, the page will use the default global sidebar.

##### `hasSidebar`

**type:** `boolean`
**default:** `false` if [`frontmatter.template`](/reference/frontmatter/#template) is `'splash'`, otherwise `true`

Set if the sidebar should be displayed on this page.
delucis marked this conversation as resolved.
Show resolved Hide resolved

##### `headings`

**type:** `{ depth: number; slug: string; text: string }[]`
**default:** `[]`

Provide an array of all the headings on this page.
Starlight will generate the page table of contents from these headings if provided.

##### `dir`

**type:** `'ltr' | 'rtl'`
**default:** the writing direction for the current locale

Set the writing direction for this page’s content.

##### `lang`

**type:** `string`
**default:** the language of the current locale

Set the BCP-47 language tag for this page’s content, e.g. `en`, `zh-CN`, or `pt-BR`.

##### `isFallback`

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

Indicate if this page is untranslated in the current language and using [fallback content](/guides/i18n/#fallback-content).
delucis marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion examples/basics/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
},
"dependencies": {
"@astrojs/starlight": "^0.18.0",
"astro": "^4.3.4",
"astro": "^4.3.5",
"sharp": "^0.32.5"
}
}
2 changes: 1 addition & 1 deletion examples/tailwind/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"@astrojs/starlight": "^0.18.0",
"@astrojs/starlight-tailwind": "^2.0.1",
"@astrojs/tailwind": "^5.1.0",
"astro": "^4.3.4",
"astro": "^4.3.5",
"sharp": "^0.32.5",
"tailwindcss": "^3.4.1"
}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"@changesets/changelog-github": "^0.4.8",
"@changesets/cli": "^2.26.1",
"@size-limit/file": "^8.2.4",
"astro": "^4.3.4",
"astro": "^4.3.5",
"prettier": "^3.0.0",
"prettier-plugin-astro": "^0.13.0",
"size-limit": "^8.2.4"
Expand Down
35 changes: 34 additions & 1 deletion packages/starlight/__tests__/basics/slugs.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { describe, expect, test } from 'vitest';
import { describe, expect, test, vi } from 'vitest';
import {
localeToLang,
localizedId,
localizedSlug,
slugToLocaleData,
slugToParam,
slugToPathname,
urlToSlug,
} from '../../utils/slugs';

describe('slugToLocaleData', () => {
Expand Down Expand Up @@ -76,3 +77,35 @@ describe('localizedSlug', () => {
expect(localizedSlug('test', undefined)).toBe('test');
});
});

describe('urlToSlug', () => {
test('returns slugs with `build.output: "directory"`', () => {
expect(urlToSlug(new URL('https://example.com'))).toBe('');
expect(urlToSlug(new URL('https://example.com/slug'))).toBe('slug');
expect(urlToSlug(new URL('https://example.com/dir/page/'))).toBe('dir/page');
expect(urlToSlug(new URL('https://example.com/dir/sub-dir/page/'))).toBe('dir/sub-dir/page');
});

test('returns slugs with `build.output: "file"`', () => {
expect(urlToSlug(new URL('https://example.com/index.html'))).toBe('');
expect(urlToSlug(new URL('https://example.com/slug.html'))).toBe('slug');
expect(urlToSlug(new URL('https://example.com/dir/page/index.html'))).toBe('dir/page');
expect(urlToSlug(new URL('https://example.com/dir/sub-dir/page.html'))).toBe(
'dir/sub-dir/page'
);
});

// It is currently not possible to test this as stubbing BASE_URL is not supported due to
// `vite-plugin-env` controlling it and the lack of a way to pass in an Astro config using
// `getViteConfig()` from `astro/config`.
test.todo('returns slugs with a custom `base` option', () => {
vi.stubEnv('BASE_URL', '/base/');
expect(urlToSlug(new URL('https://example.com/base'))).toBe('');
expect(urlToSlug(new URL('https://example.com/base/slug'))).toBe('slug');
expect(urlToSlug(new URL('https://example.com/base/dir/page/'))).toBe('dir/page');
expect(urlToSlug(new URL('https://example.com/base/dir/sub-dir/page/'))).toBe(
'dir/sub-dir/page'
);
vi.unstubAllEnvs();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { assert, expect, test, vi } from 'vitest';
import {
generateStarlightPageRouteData,
type StarlightPageProps,
} from '../../utils/starlight-page';

vi.mock('virtual:starlight/collection-config', async () => {
const { z } = await vi.importActual<typeof import('astro:content')>('astro:content');
return (await import('../test-utils')).mockedCollectionConfig({
extend: z.object({
// Make the built-in description field required.
description: z.string(),
// Add a new optional field.
category: z.string().optional(),
}),
});
});

const starlightPageProps: StarlightPageProps = {
frontmatter: { title: 'This is a test title' },
};

test('throws a validation error if a built-in field required by the user schema is not passed down', async () => {
expect.assertions(3);

try {
await generateStarlightPageRouteData({
props: starlightPageProps,
url: new URL('https://example.com/test-slug'),
});
} catch (error) {
assert(error instanceof Error);
const lines = error.message.split('\n');
// The first line should be a user-friendly error message describing the exact issue and the second line should be
// the missing description field.
expect(lines).toHaveLength(2);
const [message, missingField] = lines;
expect(message).toMatchInlineSnapshot(
`"Invalid frontmatter props passed to the \`<StarlightPage/>\` component."`
);
expect(missingField).toMatchInlineSnapshot(`"**description**: Required"`);
}
});

test('returns new field defined in the user schema', async () => {
const category = 'test category';
const data = await generateStarlightPageRouteData({
props: {
...starlightPageProps,
frontmatter: {
...starlightPageProps.frontmatter,
description: 'test description',
// @ts-expect-error - Custom field defined in the user schema.
category,
},
},
url: new URL('https://example.com/test-slug'),
});
// @ts-expect-error - Custom field defined in the user schema.
expect(data.entry.data.category).toBe(category);
});
Loading
Loading