diff --git a/e2e/src/content/tutorial/tests/navigation/layout-change-from/content.md b/e2e/src/content/tutorial/tests/navigation/layout-change-from/content.md new file mode 100644 index 000000000..eb1748c7b --- /dev/null +++ b/e2e/src/content/tutorial/tests/navigation/layout-change-from/content.md @@ -0,0 +1,13 @@ +--- +type: lesson +title: Layout change from +template: default +mainCommand: node index.mjs +previews: + - title: "Custom preview" + port: 8000 +--- + +# Navigation test - Layout change from + +This page should show previw diff --git a/e2e/src/content/tutorial/tests/navigation/layout-change-to/content.md b/e2e/src/content/tutorial/tests/navigation/layout-change-to/content.md new file mode 100644 index 000000000..bfc246072 --- /dev/null +++ b/e2e/src/content/tutorial/tests/navigation/layout-change-to/content.md @@ -0,0 +1,12 @@ +--- +type: lesson +title: Layout change to +previews: false +terminal: + panels: + - ["terminal", "Custom Terminal"] +--- + +# Navigation test - Layout change to + +This page should not show previw. It should show terminal instead. \ No newline at end of file diff --git a/e2e/src/content/tutorial/tests/navigation/meta.md b/e2e/src/content/tutorial/tests/navigation/meta.md index 4842a08d5..b6de05d28 100644 --- a/e2e/src/content/tutorial/tests/navigation/meta.md +++ b/e2e/src/content/tutorial/tests/navigation/meta.md @@ -5,6 +5,8 @@ lessons: - page-one - page-two - page-three + - layout-change-from + - layout-change-to mainCommand: '' prepareCommands: [] --- diff --git a/e2e/test/navigation.test.ts b/e2e/test/navigation.test.ts index bf56be552..ac91090e6 100644 --- a/e2e/test/navigation.test.ts +++ b/e2e/test/navigation.test.ts @@ -1,9 +1,9 @@ import { test, expect } from '@playwright/test'; -const BASE_URL = '/tests/navigation/page-one'; +const BASE_URL = '/tests/navigation'; test('user can navigate between lessons using nav bar links', async ({ page }) => { - await page.goto(BASE_URL); + await page.goto(`${BASE_URL}/page-one`); await expect(page.getByRole('heading', { level: 1, name: 'Navigation test - Page one' })).toBeVisible(); // navigate forwards @@ -21,10 +21,32 @@ test('user can navigate between lessons using nav bar links', async ({ page }) = }); test('user can navigate between lessons using breadcrumbs', async ({ page }) => { - await page.goto(BASE_URL); + await page.goto(`${BASE_URL}/page-one`); await page.getByRole('button', { name: 'Tests / Navigation / Page one' }).click({ force: true }); await page.getByRole('region', { name: 'Navigation' }).getByRole('link', { name: 'Page three' }).click(); await expect(page.getByRole('heading', { level: 1, name: 'Navigation test - Page three' })).toBeVisible(); }); + +test("user should see metadata's layout changes after navigation (#318)", async ({ page }) => { + await page.goto(`${BASE_URL}/layout-change-from`); + + // first page should have preview visible + await expect(page.getByRole('heading', { level: 1, name: 'Navigation test - Layout change from' })).toBeVisible(); + await expect(page.getByText('Custom preview')).toBeVisible(); + + await page.getByRole('link', { name: 'Layout change to' }).click(); + await expect(page.getByRole('heading', { level: 1, name: 'Navigation test - Layout change to' })).toBeVisible(); + + // second page should have preview hidden, terminal visible + + /* eslint-disable multiline-comment-style */ + // TODO: Requires #245 + // await expect(page.getByText('Preparing Environment')).not.toBeVisible(); + + await expect(page.getByRole('tab', { name: 'Custom Terminal', selected: true })).toBeVisible(); + await expect(page.getByRole('tabpanel', { name: 'Custom Terminal' })).toContainText('~/tutorial', { + useInnerText: true, + }); +}); diff --git a/packages/react/src/Panels/WorkspacePanel.tsx b/packages/react/src/Panels/WorkspacePanel.tsx index 3badbba45..98b4b72fd 100644 --- a/packages/react/src/Panels/WorkspacePanel.tsx +++ b/packages/react/src/Panels/WorkspacePanel.tsx @@ -32,6 +32,13 @@ interface TerminalProps extends PanelProps { * This component is the orchestrator between various interactive components. */ export function WorkspacePanel({ tutorialStore, theme }: Props) { + /** + * Re-render when lesson changes. + * The `tutorialStore.hasEditor()` and other methods below access + * stale data as they are not reactive. + */ + useStore(tutorialStore.ref); + const hasEditor = tutorialStore.hasEditor(); const hasPreviews = tutorialStore.hasPreviews(); const hideTerminalPanel = !tutorialStore.hasTerminalPanel(); @@ -89,6 +96,7 @@ function EditorSection({ theme, tutorialStore, hasEditor }: PanelProps) { const selectedFile = useStore(tutorialStore.selectedFile); const currentDocument = useStore(tutorialStore.currentDocument); const lessonFullyLoaded = useStore(tutorialStore.lessonFullyLoaded); + const storeRef = useStore(tutorialStore.ref); const lesson = tutorialStore.lesson!; @@ -116,7 +124,7 @@ function EditorSection({ theme, tutorialStore, hasEditor }: PanelProps) { } else { setHelpAction('reset'); } - }, [tutorialStore.ref]); + }, [storeRef]); return ( (null); const lesson = tutorialStore.lesson!; const terminalConfig = useStore(tutorialStore.terminalConfig); + const storeRef = useStore(tutorialStore.ref); function showTerminal() { const { current: terminal } = terminalPanelRef; @@ -206,7 +215,7 @@ function PreviewsSection({ }); return () => unsubscribe(); - }, [tutorialStore.ref]); + }, [storeRef]); return ( | undefined; private _lesson: Lesson | undefined; - private _ref: number = 1; + private _ref = atom(1); private _themeRef = atom(1); private _lessonFiles: Files | undefined; @@ -135,7 +135,7 @@ export class TutorialStore { this._lessonTask?.cancel(); - this._ref += 1; + this._ref.set(1 + (this._ref.value || 0)); this._lesson = lesson; this.lessonFullyLoaded.set(false); @@ -219,7 +219,7 @@ export class TutorialStore { return this._lesson; } - get ref(): unknown { + get ref(): ReadableAtom { return this._ref; }