fix: show workspace list throughout global sidebar
This commit is contained in:
@@ -43,17 +43,21 @@ Deno.test("root layout leaves Workspace selection explicit", async () => {
|
||||
new URL("./../../../routes/+layout.ts", import.meta.url),
|
||||
);
|
||||
assert(
|
||||
!layout.includes("/api/workspace") &&
|
||||
!layout.includes('"/api/workspace"') &&
|
||||
!layout.includes("redirect(") &&
|
||||
layout.includes("Workspace selection is explicit"),
|
||||
"root layout must not infer or redirect to a singleton Workspace",
|
||||
layout.includes("listWorkspaces(fetch)") &&
|
||||
layout.includes("accessibleWorkspaces"),
|
||||
"root layout may list accessible Workspaces but must not infer or redirect to a singleton Workspace",
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test("Workspace route changes dispose old multiplexed subscription state", async () => {
|
||||
const [layout, multiplexer] = await Promise.all([
|
||||
Deno.readTextFile(
|
||||
new URL("./../../../routes/w/[workspaceId]/+layout.svelte", import.meta.url),
|
||||
new URL(
|
||||
"./../../../routes/w/[workspaceId]/+layout.svelte",
|
||||
import.meta.url,
|
||||
),
|
||||
),
|
||||
Deno.readTextFile(new URL("./../multiplexer.ts", import.meta.url)),
|
||||
]);
|
||||
|
||||
@@ -358,9 +358,10 @@ Deno.test("root layout keeps Workspace selection explicit", async () => {
|
||||
|
||||
assert(
|
||||
layoutLoad.includes("export const load") &&
|
||||
layoutLoad.includes("() => ({})") &&
|
||||
layoutLoad.includes("listWorkspaces(fetch)") &&
|
||||
layoutLoad.includes("accessibleWorkspaces") &&
|
||||
!layoutLoad.includes("scopedCompatibilityRoute") &&
|
||||
!layoutLoad.includes("/api/workspace") &&
|
||||
!layoutLoad.includes('"/api/workspace"') &&
|
||||
!layoutLoad.includes("workspaceRoute") &&
|
||||
!layoutLoad.includes("redirect("),
|
||||
"root layout should not infer, bootstrap, or redirect through a singleton Workspace",
|
||||
@@ -815,9 +816,6 @@ Deno.test("Account UI owns browser passkey session state without workspace autho
|
||||
const globalNavSections = await Deno.readTextFile(
|
||||
new URL("../sidebar/GlobalNavSections.svelte", import.meta.url),
|
||||
);
|
||||
const workspaceCatalogPage = await Deno.readTextFile(
|
||||
new URL("./../../../routes/+page.svelte", import.meta.url),
|
||||
);
|
||||
const sidebarFrame = await Deno.readTextFile(
|
||||
new URL("../sidebar/SidebarFrame.svelte", import.meta.url),
|
||||
);
|
||||
@@ -891,13 +889,12 @@ Deno.test("Account UI owns browser passkey session state without workspace autho
|
||||
globalNavSections.includes("workspaces") &&
|
||||
globalNavSections.includes("workspace.display_name") &&
|
||||
globalNavSections.includes("workspaceHref(workspace.workspace_id)") &&
|
||||
workspaceCatalogPage.includes("SidebarOverride") &&
|
||||
workspaceCatalogPage.includes("sidebar={homeSidebar}") &&
|
||||
workspaceCatalogPage.includes("GlobalNavSections") &&
|
||||
workspaceCatalogPage.includes("{workspaces}") &&
|
||||
rootLayout.includes("workspaces={data.accessibleWorkspaces}") &&
|
||||
rootLayout.includes("workspaceError={data.workspaceCatalogError}") &&
|
||||
rootLayoutLoad.includes("listWorkspaces(fetch)") &&
|
||||
!globalNavSections.includes("Tickets") &&
|
||||
!globalNavSections.includes("Repositories"),
|
||||
"Root page sidebar should replace the Workspaces button with a categorized accessible Workspace list below the remaining global navigation",
|
||||
"Top-level sidebar should replace the Workspaces button with a categorized accessible Workspace list below the remaining global navigation",
|
||||
);
|
||||
assert(
|
||||
workspaceLayout.includes("{#snippet workspaceSidebar()}") &&
|
||||
@@ -935,7 +932,8 @@ Deno.test("Account UI owns browser passkey session state without workspace autho
|
||||
);
|
||||
assert(
|
||||
rootLayoutLoad.includes("export const load") &&
|
||||
rootLayoutLoad.includes("() => ({})") &&
|
||||
rootLayoutLoad.includes("listWorkspaces(fetch)") &&
|
||||
rootLayoutLoad.includes("accessibleWorkspaces") &&
|
||||
!rootLayoutLoad.includes("workspaceRoute") &&
|
||||
!rootLayoutLoad.includes("redirect("),
|
||||
"Root layout should leave account and device-login routes public by avoiding Workspace redirects entirely",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import type { WorkspaceCatalogRecord } from '../api/workspace-catalog';
|
||||
import type { SidebarSnippet } from './context';
|
||||
import GlobalNavSections from './GlobalNavSections.svelte';
|
||||
import './sidebar.css';
|
||||
@@ -6,9 +7,16 @@
|
||||
type Props = {
|
||||
currentPath: string;
|
||||
content?: SidebarSnippet | null;
|
||||
workspaces?: WorkspaceCatalogRecord[];
|
||||
workspaceError?: string | null;
|
||||
};
|
||||
|
||||
const { currentPath, content = null }: Props = $props();
|
||||
const {
|
||||
currentPath,
|
||||
content = null,
|
||||
workspaces = [],
|
||||
workspaceError = null,
|
||||
}: Props = $props();
|
||||
</script>
|
||||
|
||||
{#if content}
|
||||
@@ -16,7 +24,7 @@
|
||||
{:else}
|
||||
<div class="global-sidebar" aria-label="Global navigation">
|
||||
<div class="global-sidebar-section">
|
||||
<GlobalNavSections {currentPath} />
|
||||
<GlobalNavSections {currentPath} {workspaces} {workspaceError} />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
import '../app.css';
|
||||
import type { LayoutProps } from './$types';
|
||||
|
||||
let { children }: LayoutProps = $props();
|
||||
let { children, data }: LayoutProps = $props();
|
||||
let sidebar = $state<SidebarSnippet | null>(null);
|
||||
const sidebarOverrides = createOverrideStack<SidebarSnippet>((activeSidebar) => {
|
||||
sidebar = activeSidebar;
|
||||
@@ -27,7 +27,12 @@
|
||||
|
||||
<div class="app-shell">
|
||||
<SidebarFrame>
|
||||
<GlobalSidebar currentPath={page.url.pathname} content={sidebar} />
|
||||
<GlobalSidebar
|
||||
currentPath={page.url.pathname}
|
||||
content={sidebar}
|
||||
workspaces={data.accessibleWorkspaces}
|
||||
workspaceError={data.workspaceCatalogError}
|
||||
/>
|
||||
</SidebarFrame>
|
||||
<header class="app-shell__topbar">
|
||||
<div class="app-shell__topbar-location">
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
import type { LayoutLoad } from './$types';
|
||||
import { listWorkspaces } from "$lib/workspace/api/workspace-catalog";
|
||||
import type { LayoutLoad } from "./$types";
|
||||
|
||||
// Workspace selection is explicit at `/`; the root layout must never infer a
|
||||
// singleton Workspace or redirect based on an unscoped compatibility endpoint.
|
||||
export const load: LayoutLoad = () => ({});
|
||||
export const load: LayoutLoad = async ({ fetch }) => {
|
||||
try {
|
||||
return {
|
||||
accessibleWorkspaces: await listWorkspaces(fetch),
|
||||
workspaceCatalogError: null,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
accessibleWorkspaces: [],
|
||||
workspaceCatalogError: error instanceof Error
|
||||
? error.message
|
||||
: "Unable to load Workspaces",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
type CreateWorkspaceRequest,
|
||||
type WorkspaceCatalogItem,
|
||||
} from "$lib/workspace/api/workspace-catalog";
|
||||
import GlobalNavSections from "$lib/workspace/sidebar/GlobalNavSections.svelte";
|
||||
import SidebarOverride from "$lib/workspace/sidebar/SidebarOverride.svelte";
|
||||
import "$lib/workspace/styles/workspace-catalog.css";
|
||||
|
||||
let { data } = $props();
|
||||
@@ -89,16 +87,6 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
{#snippet homeSidebar()}
|
||||
<GlobalNavSections
|
||||
currentPath="/"
|
||||
{workspaces}
|
||||
workspaceError={catalogError}
|
||||
/>
|
||||
{/snippet}
|
||||
|
||||
<SidebarOverride sidebar={homeSidebar} />
|
||||
|
||||
<svelte:head>
|
||||
<title>Workspaces · Yoi</title>
|
||||
</svelte:head>
|
||||
|
||||
Reference in New Issue
Block a user