Skip to content

Commit 589629a

Browse files
committed
feat(react): <FileTree> to support file and folder editing
1 parent 65bde9a commit 589629a

File tree

16 files changed

+764
-31
lines changed

16 files changed

+764
-31
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default 'File in first level';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default 'File in second level';
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
type: lesson
3+
title: Allow Edits Disabled
4+
previews: false
5+
terminal:
6+
activePanel: 1
7+
panels: terminal
8+
---
9+
10+
# File Tree test - Allow Edits Disabled
11+
12+
Option `editor.fileTree.allowEdits` has default `false` value.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default 'File in first level';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default 'File in second level';
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
type: lesson
3+
title: Allow Edits Enabled
4+
previews: false
5+
editor:
6+
fileTree:
7+
allowEdits: true
8+
terminal:
9+
activePanel: 1
10+
panels: terminal
11+
---
12+
13+
# File Tree test - Allow Edits Enabled
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default 'Lesson file example.js content';
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
type: lesson
3+
title: Hidden
4+
editor:
5+
fileTree: false
6+
focus: /example.js
7+
---
8+
9+
# File Tree test - Hidden

e2e/test/file-tree.test.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,101 @@ test('user can see cannot click solve on lessons without solution files', async
5757
// reset-button should be immediately visible
5858
await expect(page.getByRole('button', { name: 'Reset' })).toBeVisible();
5959
});
60+
61+
// TODO: Requires #245
62+
test.skip('user should not see hidden file tree', async ({ page }) => {
63+
await page.goto(`${BASE_URL}/hidden`);
64+
await expect(page.getByRole('heading', { level: 1, name: 'File Tree test - Hidden' })).toBeVisible();
65+
66+
await expect(page.getByText('Files')).not.toBeVisible();
67+
await expect(page.getByRole('button', { name: 'example.js' })).not.toBeVisible();
68+
});
69+
70+
test('user cannot create files or folders when lesson is not configured via allowEdits', async ({ page }) => {
71+
await page.goto(`${BASE_URL}/allow-edits-disabled`);
72+
73+
await expect(page.getByTestId('file-tree-root-context-menu')).not.toBeVisible();
74+
75+
await page.getByRole('button', { name: 'first-level' }).click({ button: 'right' });
76+
await expect(page.getByRole('menuitem', { name: 'Create file' })).not.toBeVisible();
77+
});
78+
79+
test('user can create files', async ({ page }) => {
80+
await page.goto(`${BASE_URL}/allow-edits-enabled`);
81+
await expect(page.getByRole('heading', { level: 1, name: 'File Tree test - Allow Edits Enabled' })).toBeVisible();
82+
83+
// wait for terminal to start
84+
const terminal = page.getByRole('textbox', { name: 'Terminal input' });
85+
const terminalOutput = page.getByRole('tabpanel', { name: 'Terminal' });
86+
await expect(terminalOutput).toContainText('~/tutorial', { useInnerText: true });
87+
88+
for (const [locator, filename] of [
89+
[page.getByTestId('file-tree-root-context-menu'), 'file-in-root.js'],
90+
[page.getByRole('button', { name: 'first-level' }), 'file-in-first-level.js'],
91+
[page.getByRole('button', { name: 'second-level' }), 'file-in-second-level.js'],
92+
] as const) {
93+
await locator.click({ button: 'right' });
94+
await page.getByRole('menuitem', { name: 'Create file' }).click();
95+
96+
await page.locator('*:focus').fill(filename);
97+
await page.locator('*:focus').press('Enter');
98+
await expect(page.getByRole('button', { name: filename, pressed: true })).toBeVisible();
99+
}
100+
101+
// verify that all files are present on file tree after last creation
102+
await expect(page.getByRole('button', { name: 'file-in-root.js' })).toBeVisible();
103+
await expect(page.getByRole('button', { name: 'file-in-first-level' })).toBeVisible();
104+
await expect(page.getByRole('button', { name: 'file-in-second-level' })).toBeVisible();
105+
106+
// verify that files are present on file system via terminal
107+
for (const [directory, filename] of [
108+
['./', 'file-in-root.js'],
109+
['./first-level', 'file-in-first-level.js'],
110+
['./first-level/second-level', 'file-in-second-level.js'],
111+
]) {
112+
await terminal.fill(`clear; ls ${directory}`);
113+
await terminal.press('Enter');
114+
115+
await expect(terminalOutput).toContainText(filename, { useInnerText: true });
116+
}
117+
});
118+
119+
test('user can create folders', async ({ page }) => {
120+
await page.goto(`${BASE_URL}/allow-edits-enabled`);
121+
await expect(page.getByRole('heading', { level: 1, name: 'File Tree test - Allow Edits Enabled' })).toBeVisible();
122+
123+
// wait for terminal to start
124+
const terminal = page.getByRole('textbox', { name: 'Terminal input' });
125+
const terminalOutput = page.getByRole('tabpanel', { name: 'Terminal' });
126+
await expect(terminalOutput).toContainText('~/tutorial', { useInnerText: true });
127+
128+
for (const [locator, folder] of [
129+
[page.getByTestId('file-tree-root-context-menu'), 'folder-1'],
130+
[page.getByRole('button', { name: 'folder-1' }), 'folder-2'],
131+
[page.getByRole('button', { name: 'folder-2' }), 'folder-3'],
132+
] as const) {
133+
await locator.click({ button: 'right' });
134+
await page.getByRole('menuitem', { name: 'Create folder' }).click();
135+
136+
await page.locator('*:focus').fill(folder);
137+
await page.locator('*:focus').press('Enter');
138+
await expect(page.getByRole('button', { name: folder })).toBeVisible();
139+
}
140+
141+
// verify that all folders are present on file tree after last creation
142+
await expect(page.getByRole('button', { name: 'folder-1' })).toBeVisible();
143+
await expect(page.getByRole('button', { name: 'folder-2' })).toBeVisible();
144+
await expect(page.getByRole('button', { name: 'folder-3' })).toBeVisible();
145+
146+
// verify that files are present on file system via terminal
147+
for (const [directory, folder] of [
148+
['./', 'folder-1'],
149+
['./folder-1', 'folder-2'],
150+
['./folder-1/folder-2', 'folder-3'],
151+
]) {
152+
await terminal.fill(`clear; ls ${directory}`);
153+
await terminal.press('Enter');
154+
155+
await expect(terminalOutput).toContainText(folder, { useInnerText: true });
156+
}
157+
});

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ export const DEFAULT_LOCALIZATION = {
77
editPageText: 'Edit this page',
88
webcontainerLinkText: 'Powered by WebContainers',
99
filesTitleText: 'Files',
10+
fileTreeCreateFileText: 'Create file',
11+
fileTreeCreateFolderText: 'Create folder',
1012
prepareEnvironmentTitleText: 'Preparing Environment',
1113
defaultPreviewTitleText: 'Preview',
1214
reloadPreviewTitle: 'Reload Preview',
1315
toggleTerminalButtonText: 'Toggle Terminal',
1416
solveButtonText: 'Solve',
1517
resetButtonText: 'Reset',
16-
} satisfies Lesson['data']['i18n'];
18+
} satisfies Required<Lesson['data']['i18n']>;

0 commit comments

Comments
 (0)