Skip to content

Commit d14c404

Browse files
authored
feat(react): add button to reload a preview (#305)
1 parent e40188e commit d14c404

File tree

17 files changed

+120
-26
lines changed

17 files changed

+120
-26
lines changed

docs/tutorialkit.dev/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
},
1313
"dependencies": {
1414
"@tutorialkit/react": "workspace:*",
15-
"@webcontainer/api": "1.2.0",
15+
"@webcontainer/api": "1.2.4",
1616
"classnames": "^2.5.1",
1717
"react": "^18.3.1",
1818
"react-dom": "^18.3.1"

docs/tutorialkit.dev/src/content/docs/reference/configuration.mdx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,13 @@ type I18nText = {
9898
*/
9999
defaultPreviewTitleText?: string,
100100

101+
/**
102+
* Title attribute for the preview reload button.
103+
*
104+
* @default 'Reload Preview'
105+
*/
106+
reloadPreviewTitle?: string,
107+
101108
/**
102109
* Text for the toggle terminal button.
103110
*

e2e/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"@tutorialkit/runtime": "workspace:*",
1919
"@tutorialkit/theme": "workspace:*",
2020
"@tutorialkit/types": "workspace:*",
21+
"@types/react": "^18.3.3",
2122
"@types/node": "^22.2.0",
2223
"@unocss/reset": "^0.59.4",
2324
"@unocss/transformer-directives": "^0.62.0",
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import tutorialStore from 'tutorialkit:store';
2+
3+
interface Props {
4+
filePath: string;
5+
newContent: string;
6+
testId?: string;
7+
}
8+
9+
export function ButtonWriteToFile({ filePath, newContent, testId = 'write-to-file' }: Props) {
10+
async function writeFile() {
11+
await new Promise<void>((resolve) => {
12+
tutorialStore.lessonFullyLoaded.subscribe((value) => {
13+
if (value) {
14+
resolve();
15+
}
16+
});
17+
});
18+
19+
tutorialStore.updateFile(filePath, newContent);
20+
}
21+
22+
return (
23+
<button data-testid={testId} onClick={writeFile}>
24+
Write to File
25+
</button>
26+
);
27+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Index page

e2e/src/content/tutorial/tests/preview/single/content.md

Lines changed: 0 additions & 8 deletions
This file was deleted.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
type: lesson
3+
title: Single
4+
template: file-server
5+
previews:
6+
- [8000, "Node Server"]
7+
---
8+
9+
import { ButtonWriteToFile } from '@components/ButtonWriteToFile';
10+
11+
# Preview test - Single
12+
13+
<ButtonWriteToFile client:load filePath="/index.html" newContent="New content" />

e2e/test/preview.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,22 @@ test('user can see single preview tab', async ({ page }) => {
1313
await expect(preview.getByText('Index page')).toBeVisible();
1414
});
1515

16+
test('user can reload a preview tab', async ({ page }) => {
17+
await page.goto(`${BASE_URL}/single`);
18+
19+
const preview = page.frameLocator('[title="Node Server"]');
20+
21+
await expect(preview.getByText('Index page')).toBeVisible();
22+
23+
await page.getByTestId('write-to-file').click();
24+
25+
await expect(preview.getByText('Index page')).toBeVisible();
26+
27+
await page.getByRole('button', { name: 'Reload Preview' }).click();
28+
29+
await expect(preview.getByText('New content')).toBeVisible();
30+
});
31+
1632
test('user can see multiple preview tabs', async ({ page }) => {
1733
await page.goto(`${BASE_URL}/multiple`);
1834

e2e/tsconfig.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"extends": "astro/tsconfigs/strict",
3+
"compilerOptions": {
4+
"jsx": "react-jsx",
5+
"baseUrl": "./",
6+
"jsxImportSource": "react",
7+
"paths": {
8+
"@*": ["src/*"]
9+
}
10+
},
11+
"include": ["src", "./*.ts"],
12+
"exclude": ["node_modules", "dist"]
13+
}

packages/astro/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
"@tutorialkit/types": "workspace:*",
4444
"@types/react": "^18.3.3",
4545
"@unocss/reset": "^0.62.2",
46-
"@webcontainer/api": "1.2.0",
46+
"@webcontainer/api": "1.2.4",
4747
"astro": "^4.15.0",
4848
"astro-expressive-code": "^0.35.3",
4949
"chokidar": "3.6.0",

packages/astro/src/default/utils/content/default-localization.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export const DEFAULT_LOCALIZATION = {
99
filesTitleText: 'Files',
1010
prepareEnvironmentTitleText: 'Preparing Environment',
1111
defaultPreviewTitleText: 'Preview',
12+
reloadPreviewTitle: 'Reload Preview',
1213
toggleTerminalButtonText: 'Toggle Terminal',
1314
solveButtonText: 'Solve',
1415
resetButtonText: 'Reset',

packages/react/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@
7777
"@replit/codemirror-lang-svelte": "^6.0.0",
7878
"@tutorialkit/runtime": "workspace:*",
7979
"@tutorialkit/theme": "workspace:*",
80-
"@webcontainer/api": "1.2.0",
80+
"@webcontainer/api": "1.2.4",
8181
"@xterm/addon-fit": "^0.10.0",
8282
"@xterm/addon-web-links": "^0.11.0",
8383
"@xterm/xterm": "^5.5.0",

packages/react/src/Panels/PreviewPanel.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useStore } from '@nanostores/react';
22
import type { PreviewInfo, TutorialStore } from '@tutorialkit/runtime';
33
import type { I18n } from '@tutorialkit/types';
4+
import { reloadPreview } from '@webcontainer/api/utils';
45
import { createElement, forwardRef, memo, useCallback, useEffect, useImperativeHandle, useRef } from 'react';
56
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
67
import { BootScreen } from '../BootScreen.js';
@@ -161,6 +162,12 @@ function Preview({ preview, iframe, previewCount, first, last, toggleTerminal, i
161162
}
162163
}, [preview.url, iframe.ref]);
163164

165+
function reload() {
166+
if (iframe.ref) {
167+
reloadPreview(iframe.ref);
168+
}
169+
}
170+
164171
return (
165172
<div className="panel-container">
166173
<div
@@ -169,7 +176,13 @@ function Preview({ preview, iframe, previewCount, first, last, toggleTerminal, i
169176
})}
170177
>
171178
<div className="panel-title">
172-
<div className="panel-icon i-ph-globe-duotone"></div>
179+
<button
180+
onClick={reload}
181+
title={i18n.reloadPreviewTitle}
182+
className="panel-button rounded-full p-1.5 -my-1.5 -ml-2"
183+
>
184+
<div className="panel-icon i-ph-arrow-clockwise"></div>
185+
</button>
173186
<span className="text-sm truncate">{previewTitle(preview, previewCount, i18n)}</span>
174187
</div>
175188
{last && (

packages/runtime/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
},
3535
"dependencies": {
3636
"@tutorialkit/types": "workspace:*",
37-
"@webcontainer/api": "1.2.0",
37+
"@webcontainer/api": "1.2.4",
3838
"nanostores": "^0.10.3"
3939
},
4040
"devDependencies": {

packages/test-utils/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"type": "module",
66
"private": true,
77
"devDependencies": {
8-
"@webcontainer/api": "1.2.0",
8+
"@webcontainer/api": "1.2.4",
99
"typescript": "^5.4.5",
1010
"vitest": "^1.6.0"
1111
}

packages/types/src/schemas/i18n.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,13 @@ export const i18nSchema = z.object({
6666
*/
6767
defaultPreviewTitleText: z.string().optional().describe('Text shown on top of the preview section.'),
6868

69+
/**
70+
* Title attribute for the preview reload button.
71+
*
72+
* @default 'Reload Preview'
73+
*/
74+
reloadPreviewTitle: z.string().optional().describe('Title attribute for the preview reload button.'),
75+
6976
/**
7077
* Text for the toggle terminal button.
7178
*

pnpm-lock.yaml

Lines changed: 15 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)