Skip to content

Commit 3e30953

Browse files
authored
docs: add examples of tutorialkit:store and tutorialkit:core usage (#348)
1 parent c0b8f41 commit 3e30953

File tree

5 files changed

+666
-12
lines changed

5 files changed

+666
-12
lines changed

docs/tutorialkit.dev/astro.config.ts

+8
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ export default defineConfig({
6767
label: 'Overriding Components',
6868
link: '/guides/overriding-components/',
6969
},
70+
{
71+
label: 'How to use TutorialKit API',
72+
link: '/guides/how-to-use-tutorialkit-api/',
73+
},
7074
],
7175
},
7276
{
@@ -84,6 +88,10 @@ export default defineConfig({
8488
label: 'React Components',
8589
link: '/reference/react-components',
8690
},
91+
{
92+
label: 'TutorialKit API',
93+
link: '/reference/tutorialkit-api',
94+
},
8795
],
8896
},
8997
],
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
---
2+
title: How to use TutorialKit API
3+
description: "Examples showing how to utilize TutorialKit APIs"
4+
---
5+
6+
import { Tabs, TabItem } from '@astrojs/starlight/components';
7+
8+
TutorialKit's API can be used in custom components to provide highly custom experience in the tutorials.
9+
Below are listed few examples to solve real-world use cases. See [TutorialKit API](/reference/tutorialkit-api/) for API documentation.
10+
11+
## Access tutorial state
12+
13+
In this example we'll read contents of `index.js` using Tutorial Store APIs.
14+
15+
When user clicks `Help` button, we check the contents of `index.js` and provide them hints about next steps.
16+
17+
<a class="my-4 inline-block" href="https://stackblitz.com/edit/tutorialkit-api-use-helpbutton?file=src/components/HelpButton.tsx&file=src/content/tutorial/1-basics/1-introduction/1-welcome/content.mdx">
18+
<img alt="Open in StackBlitz" src="https://developer.stackblitz.com/img/open_in_stackblitz.svg" />
19+
</a>
20+
21+
<Tabs>
22+
<TabItem label="HelpButton.tsx">
23+
24+
```tsx title="src/components/HelpButton.tsx"
25+
import tutorialStore from "tutorialkit:store";
26+
import { Dialog } from "@tutorialkit/react";
27+
import { useState } from "react";
28+
import { parseModule } from "magicast";
29+
30+
export default function HelpButton() {
31+
const [message, setMessage] = useState<string | null>(null);
32+
33+
function onClick() {
34+
const files = tutorialStore.documents.value;
35+
const index = files["/index.js"].value as string;
36+
37+
const message = verifyIndexJs(index);
38+
setMessage(message);
39+
}
40+
41+
return (
42+
<>
43+
<button onClick={onClick} className="px-4 py-1 rounded-md bg-tk-elements-primaryButton-backgroundColor text-tk-elements-primaryButton-textColor">
44+
Help
45+
</button>
46+
47+
{message && (
48+
<Dialog title="Help" confirmText="OK" onClose={() => setMessage(null)}>
49+
{message}
50+
</Dialog>
51+
)}
52+
</>
53+
);
54+
}
55+
56+
function verifyIndexJs(code: string) {
57+
const mod = parseModule(code);
58+
59+
const hasSumFunction =
60+
mod.$ast.type === "Program" &&
61+
mod.$ast.body.some(
62+
(node) => node.type === "FunctionDeclaration" && node.id.name === "sum"
63+
);
64+
65+
if (!hasSumFunction) {
66+
return "Your index.js should have a sum function";
67+
}
68+
69+
if (mod.exports.default?.$name !== "sum") {
70+
return "Your index.js should export sum as default export";
71+
}
72+
73+
return "All good";
74+
}
75+
```
76+
77+
</TabItem>
78+
<TabItem label="content.mdx">
79+
80+
```mdx title="src/content/tutorial/chapter/part/lesson/content.mdx"
81+
---
82+
type: lesson
83+
title: TutorialKit API usage example
84+
focus: /index.js
85+
---
86+
87+
import HelpButton from '@components/HelpButton';
88+
89+
# TutorialKit API usage example
90+
91+
Click this button if you get stuck:
92+
93+
<HelpButton client:load />
94+
95+
```
96+
97+
</TabItem>
98+
</Tabs>
99+
100+
101+
## Write to terminal
102+
103+
In this example we'll write commands to the terminal using Tutorial Store APIs.
104+
105+
When user clicks `Run tests` button, we run `npm run test` command into the terminal.
106+
This command starts our `test` command defined in template's `package.json`.
107+
108+
<a class="my-4 inline-block" href="https://stackblitz.com/edit/stackblitz-starters-1qz1r8?file=src/components/TerminalWriter.tsx&file=src/content/tutorial/1-basics/1-introduction/1-welcome/content.mdx">
109+
<img alt="Open in StackBlitz" src="https://developer.stackblitz.com/img/open_in_stackblitz.svg" />
110+
</a>
111+
112+
<Tabs>
113+
<TabItem label="TerminalWriter.tsx">
114+
115+
```tsx title="src/components/TerminalWriter.tsx"
116+
import tutorialStore from 'tutorialkit:store';
117+
118+
export default function TerminalWriter() {
119+
async function onClick() {
120+
const terminal = tutorialStore.terminalConfig.value!.panels[0];
121+
terminal.input('npm run test\n');
122+
}
123+
124+
return (
125+
<button
126+
onClick={onClick}
127+
className="px-4 py-1 my-3 cursor-pointer border-1 border-tk-border-primary rounded-md bg-tk-elements-primaryButton-backgroundColor text-tk-elements-primaryButton-textColor"
128+
>
129+
Run tests
130+
</button>
131+
);
132+
}
133+
```
134+
135+
</TabItem>
136+
137+
<TabItem label="content.mdx">
138+
139+
```mdx title="src/content/tutorial/chapter/part/lesson/content.mdx"
140+
---
141+
type: lesson
142+
title: Write to Terminal example
143+
---
144+
145+
import TerminalWriter from "@components/TerminalWriter";
146+
147+
# Write to Terminal example
148+
149+
Fix <code>counter.js</code> and run the tests!
150+
151+
<TerminalWriter client:load />
152+
```
153+
154+
</TabItem>
155+
</Tabs>
156+
157+
## Provide feedback to user when lesson is solved
158+
159+
In this example we'll use the Tutorial Core APIs to congratulate user when they solve the lesson code.
160+
161+
Every time user edits the `math.js`, we run `node check-lesson.js` in the webcontainer and see if the process exits with non-erroneous exit code.
162+
Once the exit code indicates success, we inform user with message.
163+
164+
<a class="my-4 inline-block" href="https://stackblitz.com/edit/stackblitz-starters-b9ie5x?file=src/components/LessonChecker.tsx&file=src/templates/default/check-lesson.mjs&file=src/content/tutorial/1-basics/1-introduction/1-welcome/content.mdx">
165+
<img alt="Open in StackBlitz" src="https://developer.stackblitz.com/img/open_in_stackblitz.svg" />
166+
</a>
167+
168+
<Tabs>
169+
<TabItem label="LessonChecker.tsx">
170+
171+
```tsx title="src/components/LessonChecker.tsx"
172+
import { Dialog } from '@tutorialkit/react';
173+
import { useEffect, useState } from 'react';
174+
import { webcontainer } from 'tutorialkit:core';
175+
import tutorialStore from 'tutorialkit:store';
176+
177+
export default function LessonChecker() {
178+
const [success, setSuccess] = useState(false);
179+
180+
useEffect(() => {
181+
let timeout: ReturnType<typeof setTimeout> | undefined = undefined;
182+
183+
const unsubscribe = tutorialStore.onDocumentChanged('/math.js', () => {
184+
clearTimeout(timeout);
185+
186+
timeout = setTimeout(async () => {
187+
if (await checkLesson()) {
188+
setSuccess(true);
189+
unsubscribe();
190+
}
191+
}, 250);
192+
});
193+
194+
return function cleanup() {
195+
unsubscribe();
196+
clearTimeout(timeout);
197+
};
198+
}, []);
199+
200+
return (
201+
<>
202+
{success && (
203+
<Dialog title="Lesson complete" confirmText="OK" onClose={() => setSuccess(false)}>
204+
Lesson complete, congratulations! 🎉
205+
</Dialog>
206+
)}
207+
</>
208+
);
209+
}
210+
211+
async function checkLesson(): Promise<boolean> {
212+
const webcontainerInstance = await webcontainer;
213+
const process = await webcontainerInstance.spawn('node', ['./check-lesson.mjs']);
214+
215+
const exitCode = await process.exit;
216+
217+
return exitCode === 0;
218+
}
219+
```
220+
221+
</TabItem>
222+
223+
<TabItem label="check-lesson.mjs">
224+
225+
```js title="src/templates/default/check-lesson.mjs"
226+
import * as math from './math.js';
227+
228+
if (math.sum(25, 32) !== 57) {
229+
process.exit(1);
230+
}
231+
232+
if (math.multiply(3, 25) !== 75) {
233+
process.exit(1);
234+
}
235+
236+
process.exit(0);
237+
```
238+
239+
</TabItem>
240+
241+
<TabItem label="content.mdx">
242+
243+
```mdx title="src/content/tutorial/chapter/part/lesson/content.mdx"
244+
---
245+
type: lesson
246+
title: TutorialKit API usage example
247+
focus: /math.js
248+
---
249+
250+
import LessonChecker from '@components/LessonChecker';
251+
252+
# TutorialKit API usage example
253+
254+
Solve <code>math.js</code> and you'll see notification about completed lesson!
255+
256+
<LessonChecker client:load />
257+
```
258+
259+
</TabItem>
260+
</Tabs>

0 commit comments

Comments
 (0)