diff --git a/docs/tutorialkit.dev/src/content/docs/reference/configuration.mdx b/docs/tutorialkit.dev/src/content/docs/reference/configuration.mdx index 619a4b4ca..e22dfc681 100644 --- a/docs/tutorialkit.dev/src/content/docs/reference/configuration.mdx +++ b/docs/tutorialkit.dev/src/content/docs/reference/configuration.mdx @@ -404,6 +404,28 @@ type TemplateType = "html" | "node" | "angular-cli" | "create-react-app" | "java ``` +##### `custom` + +Assign custom fields to a chapter/part/lesson. + + +This is useful when you want to consume items for the default `tutorial` collection +in order to implement custom features. + +```yaml +custom: + publishedAt: 2024-16-10 + tags: tutorialkit,astro,vite +``` + +```ts +import { getCollection } from 'astro:content'; +const collection = await getCollection('tutorial'); +for (const entry of collection) { + console.log("This part was published at:", entry.data?.custom?.publishedAt) +} +``` + ## Configure the Tutorialkit Astro integration `@tutorialkit/astro` is an integration for Astro. You can configure the integration in your `astro.config.ts` file. diff --git a/e2e/src/components/CustomMetadata.astro b/e2e/src/components/CustomMetadata.astro new file mode 100644 index 000000000..8af5d7b4d --- /dev/null +++ b/e2e/src/components/CustomMetadata.astro @@ -0,0 +1,11 @@ +--- +import { getCollection } from 'astro:content'; +const collection = await getCollection('tutorial'); + +const lesson = collection.find((c) => c.data.type === 'lesson' && c.slug.startsWith(Astro.params.slug!))!; +const { custom } = lesson.data; +--- + +

Custom metadata

+ +
{JSON.stringify(custom, null,2)}
diff --git a/e2e/src/content/tutorial/tests/metadata/custom/content.mdx b/e2e/src/content/tutorial/tests/metadata/custom/content.mdx new file mode 100644 index 000000000..397dfdc4d --- /dev/null +++ b/e2e/src/content/tutorial/tests/metadata/custom/content.mdx @@ -0,0 +1,15 @@ +--- +type: lesson +title: Custom +terminal: + panels: terminal +custom: + custom-message: 'Hello world' + numeric-field: 5173 +--- + +import CustomMetaData from "@components/CustomMetadata.astro" + +# Metadata test - Custom + + \ No newline at end of file diff --git a/e2e/src/content/tutorial/tests/metadata/meta.md b/e2e/src/content/tutorial/tests/metadata/meta.md new file mode 100644 index 000000000..e13ff569b --- /dev/null +++ b/e2e/src/content/tutorial/tests/metadata/meta.md @@ -0,0 +1,4 @@ +--- +type: chapter +title: Metadata +--- diff --git a/e2e/test/metadata.test.ts b/e2e/test/metadata.test.ts new file mode 100644 index 000000000..541ebca5a --- /dev/null +++ b/e2e/test/metadata.test.ts @@ -0,0 +1,13 @@ +import { test, expect } from '@playwright/test'; + +const BASE_URL = '/tests/metadata'; + +test('developer can pass custom metadata to lesson', async ({ page }) => { + await page.goto(`${BASE_URL}/custom`); + await expect(page.getByRole('heading', { level: 1, name: 'Metadata test - Custom' })).toBeVisible(); + + await expect(page.getByRole('heading', { level: 2, name: 'Custom metadata' })).toBeVisible(); + + await expect(page.getByText('"custom-message": "Hello world"')).toBeVisible(); + await expect(page.getByText('"numeric-field": 5173')).toBeVisible(); +}); diff --git a/packages/template/src/content/tutorial/1-basics/1-introduction/1-welcome/content.md b/packages/template/src/content/tutorial/1-basics/1-introduction/1-welcome/content.md index cf48965a5..1acc9d03f 100644 --- a/packages/template/src/content/tutorial/1-basics/1-introduction/1-welcome/content.md +++ b/packages/template/src/content/tutorial/1-basics/1-introduction/1-welcome/content.md @@ -12,8 +12,11 @@ prepareCommands: terminal: panels: ['terminal', 'output'] meta: - description: "This is lesson 1" - image: "/logo.svg" + description: "This is lesson 1" + image: "/logo.svg" +custom: + publishedAt: "2024-10-16" + --- # Kitchen Sink [Heading 1] diff --git a/packages/types/src/entities/index.ts b/packages/types/src/entities/index.ts index 81c828701..3d0abf5f6 100644 --- a/packages/types/src/entities/index.ts +++ b/packages/types/src/entities/index.ts @@ -1,5 +1,5 @@ import type { I18nSchema } from '../schemas/i18n.js'; -import type { ChapterSchema, LessonSchema, PartSchema } from '../schemas/index.js'; +import type { ChapterSchema, CustomSchema, LessonSchema, PartSchema } from '../schemas/index.js'; import type { MetaTagsSchema } from '../schemas/metatags.js'; export type * from './nav.js'; @@ -60,6 +60,8 @@ export type I18n = Required>; export type MetaTagsConfig = MetaTagsSchema; +export type CustomConfig = CustomSchema; + export interface Tutorial { logoLink?: string; firstPartId?: string; diff --git a/packages/types/src/schemas/common.ts b/packages/types/src/schemas/common.ts index faa8be849..cd854b563 100644 --- a/packages/types/src/schemas/common.ts +++ b/packages/types/src/schemas/common.ts @@ -203,13 +203,18 @@ export const editorSchema = z.union([ }), ]); +const customSchema = z.record(z.string(), z.any()); + export type TerminalPanelType = z.infer; export type TerminalSchema = z.infer; export type EditorSchema = z.infer; +export type CustomSchema = z.infer; export const webcontainerSchema = commandsSchema.extend({ meta: metaTagsSchema.optional(), + custom: customSchema.optional().describe('Assign custom fields to a chapter/part/lesson in the Astro collection'), + previews: previewSchema .optional() .describe(