Skip to content

Commit 07539e1

Browse files
committed
feat: add Explorer stories for expanded dir and selected item
- ExplorerTabExpanded: shows expanded src folder with Collapse All button - ExplorerTabSelected: shows package.json with focus/selected blue background - Update mock to return subdirectory contents for src/
1 parent 272cbdb commit 07539e1

File tree

2 files changed

+137
-3
lines changed

2 files changed

+137
-3
lines changed

src/browser/stories/App.rightSidebar.stories.tsx

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,121 @@ export const ExplorerTab: AppStory = {
277277
},
278278
};
279279

280+
/**
281+
* Explorer tab with expanded directory showing Collapse All button
282+
*/
283+
export const ExplorerTabExpanded: AppStory = {
284+
render: () => (
285+
<AppWithMocks
286+
setup={() => {
287+
localStorage.setItem(RIGHT_SIDEBAR_TAB_KEY, JSON.stringify("explorer"));
288+
localStorage.setItem(RIGHT_SIDEBAR_WIDTH_KEY, "350");
289+
localStorage.removeItem(getRightSidebarLayoutKey("ws-explorer-expanded"));
290+
291+
const client = setupSimpleChatStory({
292+
workspaceId: "ws-explorer-expanded",
293+
workspaceName: "feature/files",
294+
projectName: "my-app",
295+
messages: [
296+
createUserMessage("msg-1", "Show me the project structure", { historySequence: 1 }),
297+
createAssistantMessage("msg-2", "Here is the project structure.", {
298+
historySequence: 2,
299+
}),
300+
],
301+
});
302+
expandRightSidebar();
303+
return client;
304+
}}
305+
/>
306+
),
307+
play: async ({ canvasElement }) => {
308+
const canvas = within(canvasElement);
309+
310+
// Wait for explorer tab and click it
311+
const explorerTab = await canvas.findByRole("tab", { name: /^explorer/i }, { timeout: 3000 });
312+
await userEvent.click(explorerTab);
313+
314+
// Wait for file tree to load
315+
await waitFor(
316+
() => {
317+
canvas.getByText("src");
318+
},
319+
{ timeout: 5000 }
320+
);
321+
322+
// Click on src folder to expand it
323+
const srcFolder = canvas.getByText("src");
324+
await userEvent.click(srcFolder);
325+
326+
// Wait for src contents to load and collapse all button to appear
327+
await waitFor(
328+
() => {
329+
canvas.getByText("App.tsx");
330+
canvas.getByText("components");
331+
},
332+
{ timeout: 5000 }
333+
);
334+
335+
// Verify collapse all button is visible (tooltip text)
336+
await waitFor(() => {
337+
canvas.getByRole("button", { name: /collapse all/i });
338+
});
339+
340+
// Blur to get clean screenshot
341+
blurActiveElement();
342+
},
343+
};
344+
345+
/**
346+
* Explorer tab with selected item showing blue background
347+
*/
348+
export const ExplorerTabSelected: AppStory = {
349+
render: () => (
350+
<AppWithMocks
351+
setup={() => {
352+
localStorage.setItem(RIGHT_SIDEBAR_TAB_KEY, JSON.stringify("explorer"));
353+
localStorage.setItem(RIGHT_SIDEBAR_WIDTH_KEY, "350");
354+
localStorage.removeItem(getRightSidebarLayoutKey("ws-explorer-selected"));
355+
356+
const client = setupSimpleChatStory({
357+
workspaceId: "ws-explorer-selected",
358+
workspaceName: "feature/files",
359+
projectName: "my-app",
360+
messages: [
361+
createUserMessage("msg-1", "Show me the project structure", { historySequence: 1 }),
362+
createAssistantMessage("msg-2", "Here is the project structure.", {
363+
historySequence: 2,
364+
}),
365+
],
366+
});
367+
expandRightSidebar();
368+
return client;
369+
}}
370+
/>
371+
),
372+
play: async ({ canvasElement }) => {
373+
const canvas = within(canvasElement);
374+
375+
// Wait for explorer tab and click it
376+
const explorerTab = await canvas.findByRole("tab", { name: /^explorer/i }, { timeout: 3000 });
377+
await userEvent.click(explorerTab);
378+
379+
// Wait for file tree to load
380+
await waitFor(
381+
() => {
382+
canvas.getByText("package.json");
383+
},
384+
{ timeout: 5000 }
385+
);
386+
387+
// Click on package.json to select it (will have focus/selected blue background)
388+
const packageJson = canvas.getByText("package.json");
389+
await userEvent.click(packageJson);
390+
391+
// Don't blur - keep the item selected/focused for the screenshot
392+
},
393+
};
394+
280395
/**
281396
* Stats tab when idle (no timing data) - shows placeholder message
282397
*/

src/browser/stories/mocks/orpc.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -428,8 +428,26 @@ export function createMockORPCClient(options: MockORPCClientOptions = {}): APICl
428428
},
429429
general: {
430430
listDirectory: () => Promise.resolve({ entries: [], hasMore: false }),
431-
listWorkspaceDirectory: () =>
432-
Promise.resolve({
431+
listWorkspaceDirectory: (input: { workspacePath: string; relativePath?: string }) => {
432+
// Return different contents based on the requested path
433+
if (input.relativePath === "src") {
434+
return Promise.resolve({
435+
success: true as const,
436+
data: [
437+
{
438+
name: "components",
439+
path: "src/components",
440+
isDirectory: true,
441+
children: [],
442+
},
443+
{ name: "utils", path: "src/utils", isDirectory: true, children: [] },
444+
{ name: "App.tsx", path: "src/App.tsx", isDirectory: false, children: [] },
445+
{ name: "index.ts", path: "src/index.ts", isDirectory: false, children: [] },
446+
],
447+
});
448+
}
449+
// Root directory
450+
return Promise.resolve({
433451
success: true as const,
434452
data: [
435453
{ name: "src", path: "src", isDirectory: true, children: [] },
@@ -445,7 +463,8 @@ export function createMockORPCClient(options: MockORPCClientOptions = {}): APICl
445463
{ name: "README.md", path: "README.md", isDirectory: false, children: [] },
446464
{ name: "tsconfig.json", path: "tsconfig.json", isDirectory: false, children: [] },
447465
],
448-
}),
466+
});
467+
},
449468
ping: (input: string) => Promise.resolve(`Pong: ${input}`),
450469
tick: async function* () {
451470
// No ticks in the mock, but keep the subscription open.

0 commit comments

Comments
 (0)