Skip to content

Commit e93d11a

Browse files
authored
feat(astro): override components to support HeadTags (#375)
1 parent bc61229 commit e93d11a

File tree

10 files changed

+100
-8
lines changed

10 files changed

+100
-8
lines changed

docs/tutorialkit.dev/src/content/docs/guides/overriding-components.mdx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,22 @@ interface Props {
8989
/** Content of the dialog */
9090
children: ReactNode;
9191
}
92+
```
93+
94+
### HeadTags
95+
96+
Component for overriding title, links and metadata in the `<head>` tag.
97+
98+
When overriding `HeadTags` you can place TutorialKit's default components using following [Astro slots](https://docs.astro.build/en/basics/astro-components/#named-slots):
99+
100+
- `title`: The page title
101+
- `links`: Links for the favicon, fonts and other assets
102+
- `meta`: Metadata and Open Graph tags
103+
104+
```jsx title="src/components/CustomHeadLinks.astro"
105+
<slot name="title" />
106+
<slot name="links" />
107+
<slot name="meta" />
108+
{/** Add your own tags */}
109+
<link rel="sitemap" href="/sitemap-index.xml" />
92110
```

e2e/configs/override-components.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export default defineConfig({
1010
components: {
1111
Dialog: './src/components/Dialog.tsx',
1212
TopBar: './src/components/TopBar.astro',
13+
HeadTags: './src/components/CustomHeadTags.astro',
1314
},
1415
}),
1516
],
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<slot name="title" />
2+
<slot name="links" />
3+
<slot name="meta" />
4+
<meta name="e2e-test-custom-meta-tag" content="custom-content" />
5+
<link rel="sitemap" href="/sitemap-index.xml" />
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
test('developer can override HeadTags', async ({ page }) => {
4+
await page.goto('/');
5+
6+
const defaultElems = [
7+
page.locator('title'),
8+
page.locator('meta[name="og:title"]'),
9+
page.locator('link[rel="stylesheet"]').first(),
10+
];
11+
const customElems = [
12+
page.locator('meta[name="e2e-test-custom-meta-tag"][content="custom-content"]'),
13+
page.locator('link[rel="sitemap"]'),
14+
];
15+
16+
for (const e of defaultElems) {
17+
await expect(e).toBeAttached();
18+
}
19+
20+
for (const e of customElems) {
21+
await expect(e).toBeAttached();
22+
}
23+
});

packages/astro/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"./default/pages/index.astro": "./dist/default/pages/index.astro",
2121
"./default/pages/[...slug].astro": "./dist/default/pages/[...slug].astro",
2222
"./default/components/TopBar.astro": "./dist/default/components/TopBar.astro",
23+
"./default/components/HeadTags.astro": "./dist/default/components/HeadTags.astro",
2324
"./package.json": "./package.json"
2425
},
2526
"files": [
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<slot name="title" />
2+
<slot name="links" />
3+
<slot name="meta" />

packages/astro/src/default/env-default.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ interface WebContainerConfig {
99

1010
declare module 'tutorialkit:override-components' {
1111
const topBar: typeof import('./src/default/components/TopBar.astro').default;
12+
const headTags: typeof import('./src/default/components/HeadTags.astro').default;
1213
const dialog: typeof import('@tutorialkit/react/dialog').default;
1314

14-
export { topBar as TopBar, dialog as Dialog };
15+
export { topBar as TopBar, dialog as Dialog, headTags as HeadTags };
1516
}
1617

1718
declare const __ENTERPRISE__: boolean;

packages/astro/src/default/layouts/Layout.astro

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
---
2+
import { HeadTags } from 'tutorialkit:override-components';
23
import { ViewTransitions } from 'astro:transitions';
34
import type { MetaTagsConfig } from '@tutorialkit/types';
45
import MetaTags from '../components/MetaTags.astro';
@@ -15,13 +16,22 @@ const faviconUrl = readPublicAsset('favicon.svg');
1516
<!doctype html>
1617
<html lang="en" transition:animate="none" class="h-full overflow-hidden">
1718
<head>
18-
<title>{title}</title>
19-
{faviconUrl ? <link rel="icon" type="image/svg+xml" href={faviconUrl} /> : null}
20-
<MetaTags meta={meta} />
21-
<link rel="preconnect" href="https://fonts.googleapis.com" />
22-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
23-
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
24-
<link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@400;700&display=swap" rel="stylesheet" />
19+
<HeadTags>
20+
<title slot="title">{title}</title>
21+
22+
<Fragment slot="links">
23+
{faviconUrl ? <link rel="icon" type="image/svg+xml" href={faviconUrl} /> : null}
24+
<link rel="preconnect" href="https://fonts.googleapis.com" />
25+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
26+
<link
27+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
28+
rel="stylesheet"
29+
/>
30+
<link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@400;700&display=swap" rel="stylesheet" />
31+
</Fragment>
32+
33+
<MetaTags slot="meta" meta={meta} />
34+
</HeadTags>
2535
<ViewTransitions />
2636
<script is:inline>
2737
setTutorialKitTheme();

packages/astro/src/vite-plugins/override-components.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
* components: {
1919
* TopBar: './CustomTopBar.astro',
2020
* Dialog: './CustomDialog.tsx',
21+
* HeadTags: './CustomHeadLinks.astro',
2122
* },
2223
* }),
2324
* ],
@@ -54,6 +55,23 @@ export interface OverrideComponentsOptions {
5455
* It will receive same props that `@tutorialkit/react/dialog` supports.
5556
*/
5657
Dialog?: string;
58+
59+
/**
60+
* Component for overriding title, links and metadata in the `<head>` tag.
61+
*
62+
* This component has slots that are used to pass TutorialKit's default tags:
63+
*
64+
* - `title`: The page title
65+
* - `links`: Links for the favicon, fonts and other assets
66+
* - `meta`: Metadata and Open Graph tags
67+
*
68+
* ```jsx
69+
* <slot name="title" />
70+
* <slot name="links" />
71+
* <slot name="meta" />
72+
* ```
73+
*/
74+
HeadTags: string;
5775
}
5876

5977
interface Options {
@@ -77,11 +95,13 @@ export function overrideComponents({ components, defaultRoutes }: Options): Vite
7795
async load(id) {
7896
if (id === resolvedId) {
7997
const topBar = components?.TopBar || resolveDefaultTopBar(defaultRoutes);
98+
const headTags = components?.HeadTags || resolveDefaultHeadTags(defaultRoutes);
8099
const dialog = components?.Dialog || '@tutorialkit/react/dialog';
81100

82101
return `
83102
export { default as TopBar } from '${topBar}';
84103
export { default as Dialog } from '${dialog}';
104+
export { default as HeadTags } from '${headTags}';
85105
`;
86106
}
87107

@@ -98,3 +118,12 @@ function resolveDefaultTopBar(defaultRoutes: boolean) {
98118
// default `TopBar` is used from local file when `defaultRoutes` is disabled
99119
return './src/components/TopBar.astro';
100120
}
121+
122+
function resolveDefaultHeadTags(defaultRoutes: boolean) {
123+
if (defaultRoutes) {
124+
return '@tutorialkit/astro/default/components/HeadTags.astro';
125+
}
126+
127+
// default `HeadTags` is used from local file when `defaultRoutes` is disabled
128+
return './src/components/HeadTags.astro';
129+
}

packages/cli/tests/__snapshots__/create-tutorial.test.ts.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ exports[`create and eject a project 1`] = `
235235
"public/logo.svg",
236236
"src",
237237
"src/components",
238+
"src/components/HeadTags.astro",
238239
"src/components/LoginButton.tsx",
239240
"src/components/Logo.astro",
240241
"src/components/MainContainer.astro",

0 commit comments

Comments
 (0)