Skip to content

feat: support overriding TopBar #112

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

Merged
merged 6 commits into from
Jul 9, 2024
Merged
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
4 changes: 4 additions & 0 deletions docs/tutorialkit.dev/astro.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ export default defineConfig({
label: 'User Interface Reference',
link: '/guides/ui/',
},
{
label: 'Overriding Components',
link: '/guides/overriding-components/',
},
],
},
{
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
title: 'Overriding Components'
description: "Override TutorialKit's default components to fit your needs."
---
Comment on lines +1 to +4
Copy link
Member Author

Choose a reason for hiding this comment

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

These docs are highly inspired by Starlight: https://starlight.astro.build/guides/overriding-components/

import { Image } from 'astro:assets';
import uiTopBar from './images/ui-top-bar.png';

TutorialKit's default components are customizable with [theming](/reference/theming/) options.
In cases where theming is not enough you can override some built-in components with your own ones.

## Configuration

Define `components` option in your `astro.config.ts`:

```ts
import tutorialkit from '@tutorialkit/astro';
import { defineConfig } from 'astro/config';

export default defineConfig({
integrations: [
tutorialkit({
components: {
// Component overrides go here
},
}),
],
});
```

For example to override `TopBar`, provide a path to the new Astro component:

```ts
tutorialkit({
components: {
TopBar: './src/components/CustomTopBar.astro',
},
}),
```

### Top Bar

<Image src={uiTopBar} alt="TutorialKit's Top bar" />

When overriding `TopBar` you can place TutorialKit's default components using following [Astro slots](https://docs.astro.build/en/basics/astro-components/#named-slots):

- `logo`: Logo of the application
- `theme-switch`: Switch for changing the theme
- `login-button`: For StackBlitz Enterprise user, the login button

```jsx title="src/components/CustomTopBar.astro"
<nav>
<slot name="logo" />

<a href="https://tutorialkit.dev/">
Home page
</a>

<slot name="theme-switch" />

<LanguageSelect />

<slot name="login-button" />
</nav>
```
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"demo": "pnpm run --filter=demo.tutorialkit.dev dev",
"demo:build": "pnpm run --filter=demo.tutorialkit.dev build",
"lint": "eslint \"{packages,docs}/**/*\"",
"test": "pnpm run --filter=@tutorialkit/* --filter=tutorialkit test"
"test": "pnpm run --stream --filter=@tutorialkit/* --filter=tutorialkit test --run"
},
"license": "MIT",
"packageManager": "[email protected]",
Expand Down
3 changes: 2 additions & 1 deletion packages/astro/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
".": "./dist/index.js",
"./types": "./types.d.ts",
"./default/pages/index.astro": "./dist/default/pages/index.astro",
"./default/pages/[...slug].astro": "./dist/default/pages/[...slug].astro"
"./default/pages/[...slug].astro": "./dist/default/pages/[...slug].astro",
"./default/components/TopBar.astro": "./dist/default/components/TopBar.astro"
},
"files": [
"dist"
Expand Down
26 changes: 0 additions & 26 deletions packages/astro/src/default/components/Header.astro

This file was deleted.

13 changes: 13 additions & 0 deletions packages/astro/src/default/components/TopBar.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<nav
class="bg-tk-elements-topBar-backgroundColor border-b border-tk-elements-app-borderColor flex max-w-full items-center p-3 px-4 min-h-[56px]"
>
<div class="flex flex-1">
<slot name="logo" />
</div>
<div>
<slot name="theme-switch" />
</div>
<div>
<slot name="login-button" />
</div>
</nav>
21 changes: 21 additions & 0 deletions packages/astro/src/default/components/TopBarWrapper.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
import { TopBar } from 'tutorialkit:override-components';
import { ThemeSwitch } from './ThemeSwitch';
import { LoginButton } from './LoginButton';
import Logo from './Logo.astro';
import { useAuth } from './setup';

interface Props {
logoLink: string;
}

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

<TopBar>
<Logo slot="logo" logoLink={logoLink ?? '/'} />

<ThemeSwitch client:load transition:persist slot="theme-switch" />

{useAuth && <LoginButton client:load transition:persist slot="login-button" />}
</TopBar>
6 changes: 6 additions & 0 deletions packages/astro/src/default/env-default.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,11 @@ interface WebContainerConfig {
scope: string;
}

declare module 'tutorialkit:override-components' {
const topBar: typeof import('./src/default/components/TopBar.astro').default;

export { topBar as TopBar };
}

declare const __ENTERPRISE__: boolean;
declare const __WC_CONFIG__: WebContainerConfig | undefined;
4 changes: 2 additions & 2 deletions packages/astro/src/default/pages/[...slug].astro
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
import type { InferGetStaticPropsType } from 'astro';
import Header from '../components/Header.astro';
import TopBarWrapper from '../components/TopBarWrapper.astro';
import MainContainer from '../components/MainContainer.astro';
import Layout from '../layouts/Layout.astro';
import '../styles/base.css';
Expand All @@ -19,7 +19,7 @@ const { lesson, logoLink, navList, title } = Astro.props as Props;
<Layout title={title}>
<div id="previews-container"></div>
<main class="max-w-full flex flex-col h-screen overflow-hidden" data-swap-root>
<Header logoLink={logoLink ?? '/'} />
<TopBarWrapper logoLink={logoLink ?? '/'} />
<MainContainer lesson={lesson} navList={navList} />
</main>
</Layout>
14 changes: 13 additions & 1 deletion packages/astro/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { updateMarkdownConfig } from './remark/index.js';
import { tutorialkitCore } from './vite-plugins/core.js';
import { userlandCSS, watchUserlandCSS } from './vite-plugins/css.js';
import { tutorialkitStore } from './vite-plugins/store.js';
import { overrideComponents, type OverrideComponentsOptions } from './vite-plugins/override-components.js';
import { WebContainerFiles } from './webcontainer-files/index.js';

export const unoCSSConfig = {
Expand All @@ -27,6 +28,11 @@ export interface Options {
*/
defaultRoutes?: boolean | 'tutorial-only';

/**
* Override components of TutorialKit.
*/
components?: OverrideComponentsOptions;

/**
* The value of the Cross-Origin-Embedder-Policy header for the dev server.
* This is required for webcontainer to works.
Expand Down Expand Up @@ -62,7 +68,12 @@ export interface Options {
};
}

export default function createPlugin({ defaultRoutes = true, isolation, enterprise }: Options = {}): AstroIntegration {
export default function createPlugin({
defaultRoutes = true,
components,
isolation,
enterprise,
}: Options = {}): AstroIntegration {
const webcontainerFiles = new WebContainerFiles();

let _config: AstroConfig;
Expand Down Expand Up @@ -96,6 +107,7 @@ export default function createPlugin({ defaultRoutes = true, isolation, enterpri
userlandCSS,
tutorialkitStore,
tutorialkitCore,
overrideComponents({ components, defaultRoutes: !!defaultRoutes }),
process.env.TUTORIALKIT_DEV ? (await import('vite-plugin-inspect')).default() : null,
],
},
Expand Down
87 changes: 87 additions & 0 deletions packages/astro/src/vite-plugins/override-components.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/**
* A plugin that lets users override TutorialKit's components.
*
* The virtual module can be imported as:
*
* ```ts
* import { TopBar } from 'tutorialkit:override-components';
*
* <TopBar />
* ```
*
* User can override the components in `astro.config.ts`:
*
* ```ts
* export default defineConfig({
* integrations: [
* tutorialkit({
* components: {
* TopBar: './CustomTopBar.astro',
* },
* }),
* ],
* });
* ```
*/
import type { VitePlugin } from '../types.js';

export interface OverrideComponentsOptions {
/**
* Component for overriding the top bar.
*
* This component has 3 slots that are used to pass TutorialKit's default components:
* - `logo`: Logo of the application
* - `theme-switch`: Switch for changing the theme
* - `login-button`: For StackBlitz Enterprise user, the login button
*
* Usage:
*
* ```jsx
* <slot name="logo" />
* <slot name="theme-switch" />
* <slot name="login-button" />
* ```
*/
TopBar?: string;
}

interface Options {
components?: OverrideComponentsOptions;
defaultRoutes: boolean;
}

const virtualModuleId = 'tutorialkit:override-components';
const resolvedId = `\0${virtualModuleId}`;

export function overrideComponents({ components, defaultRoutes }: Options): VitePlugin {
return {
name: 'tutorialkit-override-components-plugin',
resolveId(id) {
if (id === virtualModuleId) {
return resolvedId;
}

return undefined;
},
async load(id) {
if (id === resolvedId) {
const topBar = components?.TopBar || resolveDefaultTopBar(defaultRoutes);

return `
export { default as TopBar } from '${topBar}';
`;
}

return undefined;
},
};
}

function resolveDefaultTopBar(defaultRoutes: boolean) {
if (defaultRoutes) {
return '@tutorialkit/astro/default/components/TopBar.astro';
}

// default `TopBar` is used from local file when `defaultRoutes` is disabled
return './src/components/TopBar.astro';
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ exports[`create and eject a project 1`] = `
"public/logo.svg",
"src",
"src/components",
"src/components/Header.astro",
"src/components/LoginButton.tsx",
"src/components/Logo.astro",
"src/components/MainContainer.astro",
Expand All @@ -156,6 +155,8 @@ exports[`create and eject a project 1`] = `
"src/components/NavWrapper.tsx",
"src/components/ResizablePanel.astro",
"src/components/ThemeSwitch.tsx",
"src/components/TopBar.astro",
"src/components/TopBarWrapper.astro",
"src/components/TutorialContent.astro",
"src/components/WorkspacePanelWrapper.tsx",
"src/components/setup.ts",
Expand Down