Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions e2e-tests/fixtures/Workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,11 @@ export class Workspace {
await this.searchInput.clear();
}

async clickFile(name: string): Promise<void> {
await this.workspaceFileGrid.getByRole('row', { name }).click();
async clickFile(name: string, options?: { force?: boolean }): Promise<void> {
// `force: true` is for callers that expect the click to immediately summon a modal
// (e.g. unsaved-changes confirmation) — Playwright's hit-test would otherwise see the
// modal backdrop and retry until timeout, even though the original click registered.
await this.workspaceFileGrid.getByRole('row', { name }).click(options);
}

async createFolder(folderPath?: string): Promise<string> {
Expand Down Expand Up @@ -222,12 +225,15 @@ export class Workspace {

/**
* Open the right-side metadata panel by clicking the metadata tab icon.
* If the panel is already open on the metadata tab, this is a no-op.
* If the panel is already open on the metadata tab, this is a no-op — clicking again
* would toggle the panel closed.
*/
async openMetadataPanel(): Promise<void> {
// Click the Metadata tab button in the right icon rail
await this.metadataTabButton.click();
// Wait for the metadata panel content to appear
// The icon rail button sets data-active="true" iff metadata tab is active AND panel is open.
const isAlreadyOpen = (await this.metadataTabButton.getAttribute('data-active')) === 'true';
if (!isAlreadyOpen) {
await this.metadataTabButton.click();
}
await this.page.getByText('User metadata', { exact: true }).waitFor({ state: 'visible', timeout: 5000 });
}

Expand Down Expand Up @@ -279,7 +285,7 @@ export class Workspace {
this.metadataCancelButton = page.locator('.user-metadata-editor + div').getByRole('button', { name: 'Cancel' });
this.metadataPanel = page.locator('.user-metadata-editor').first();
this.metadataSaveButton = page.locator('.user-metadata-editor + div').getByRole('button', { name: 'Save' });
this.metadataTabButton = page.getByRole('button', { name: 'Metadata' });
this.metadataTabButton = page.getByRole('button', { exact: true, name: 'Metadata' });
this.navButtonSequences = page.locator('.nav-button:has-text("Sequences")');
this.navButtonSequencesMenu = this.navButtonSequences.getByRole('menu');
this.page = page;
Expand Down
10 changes: 9 additions & 1 deletion e2e-tests/tests/workspace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ test.describe.serial('Workspace', () => {
await workspace.pageLoadingLocatorWithData.waitFor({ state: 'detached' });
});

test('Right icon rail shows sequence tabs when workspace loads with no file selected', async () => {
// With no file in the URL the page defaults to a SequenceEditor, so the right
// icon rail should expose Selected Command and Command Dictionary tabs alongside Metadata.
await expect(setup.page.getByRole('button', { exact: true, name: 'Metadata' })).toBeVisible();
await expect(setup.page.getByRole('button', { exact: true, name: 'Selected Command' })).toBeVisible();
await expect(setup.page.getByRole('button', { exact: true, name: 'Command Dictionary' })).toBeVisible();
});

test('Workspace header menu should be accessible', async () => {
await expect(workspace.workspaceContextMenuButton).toBeVisible();
await workspace.openWorkspaceContextMenu();
Expand Down Expand Up @@ -279,7 +287,7 @@ test.describe.serial('Workspace', () => {

// Try to navigate to second file
await workspace.searchForFileAndWait(file2);
await workspace.clickFile(file2);
await workspace.clickFile(file2, { force: true });

// Should show confirmation modal
const modal = setup.page.locator('#modal-container');
Expand Down
17 changes: 8 additions & 9 deletions src/routes/workspaces/[workspaceId]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@
const resizableHandleClass =
'w-[3px] hover:after:bg-neutral-300 hover:after:transition-all hover:after:delay-[400ms] data-[active]:after:bg-neutral-300 data-[active]:after:transition-all';

let activeFileIsSequence: boolean = false;
let actionDetailIsDirty: boolean = false;
let availableActionsForActiveFile: ActionParameterPair[] = [];
let panelsReady: boolean = false;
let allActionsForWorkspace: ActionDefinition[] = [];
Expand Down Expand Up @@ -180,7 +182,6 @@
let workspaceTree: WorkspaceTreeNode | null = null;
let workspaceTreeMap: WorkspaceTreeMap = {};
let workspaceFileList: WorkspaceTreeNodeWithFullPath[] = [];
let actionDetailIsDirty: boolean = false;

if (initialActionRunIdParam) {
const runId = parseInt(initialActionRunIdParam, 10);
Expand Down Expand Up @@ -277,13 +278,14 @@

$: activeFileMetadata = ($activeDocumentPath && workspaceTreeMap[$activeDocumentPath]?.metadata) || null;
$: activeFileIsSequence =
$activeDocumentPath !== null &&
$activeDocument.type !== null &&
$activeDocument.type === WorkspaceContentType.Sequence;
$activeDocumentPath === null ||
($activeDocument.type !== null && $activeDocument.type === WorkspaceContentType.Sequence);
$: commandInfoMapper = $sequenceAdaptation.input.commandInfoMapper;
$: isFileReadOnly = activeFileMetadata?.readOnly ?? false;

// Switch right panel tab when file type changes
// Auto-switch the right-panel tab only when the editor crosses between sequence mode and
// non-sequence mode. Switching between two sequence files (or between blank and a sequence
// file) preserves whatever tab the user last chose.
let previousActiveFileIsSequence: boolean = activeFileIsSequence;
$: if (activeFileIsSequence !== previousActiveFileIsSequence) {
previousActiveFileIsSequence = activeFileIsSequence;
Expand Down Expand Up @@ -1410,9 +1412,6 @@
{:else}
{@const isTextOrEmpty =
$activeDocumentPath === null || isTextFile(workspaceTreeMap[$activeDocumentPath]?.type)}
{@const isSequenceFile =
$activeDocumentPath === null ||
($activeDocument.type !== null && $activeDocument.type === WorkspaceContentType.Sequence)}
<div class="relative grid h-full grid-cols-1 grid-rows-1">
{#if showLoadingSpinner && isTextOrEmpty}
<div
Expand All @@ -1421,7 +1420,7 @@
<LoaderCircle size={32} class="animate-spin text-muted-foreground" />
</div>
{/if}
{#if isTextOrEmpty && isSequenceFile}
{#if isTextOrEmpty && activeFileIsSequence}
<div class="flex h-full">
<SequenceEditor
bind:this={sequenceEditorRef}
Expand Down
Loading