Merge branch 'work/companion' into hare/develop
This commit is contained in:
@@ -812,6 +812,12 @@ Deno.test("Account UI owns browser passkey session state without workspace autho
|
|||||||
const globalSidebar = await Deno.readTextFile(
|
const globalSidebar = await Deno.readTextFile(
|
||||||
new URL("../sidebar/GlobalSidebar.svelte", import.meta.url),
|
new URL("../sidebar/GlobalSidebar.svelte", import.meta.url),
|
||||||
);
|
);
|
||||||
|
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(
|
const sidebarFrame = await Deno.readTextFile(
|
||||||
new URL("../sidebar/SidebarFrame.svelte", import.meta.url),
|
new URL("../sidebar/SidebarFrame.svelte", import.meta.url),
|
||||||
);
|
);
|
||||||
@@ -874,13 +880,24 @@ Deno.test("Account UI owns browser passkey session state without workspace autho
|
|||||||
"Root layout chrome should keep GlobalSidebar as the root slot owner while account navigation stays in the header",
|
"Root layout chrome should keep GlobalSidebar as the root slot owner while account navigation stays in the header",
|
||||||
);
|
);
|
||||||
assert(
|
assert(
|
||||||
globalSidebar.includes('aria-label="Global pages"') &&
|
globalSidebar.includes("GlobalNavSections") &&
|
||||||
!globalSidebar.includes('<p class="sidebar-section-label">') &&
|
globalNavSections.includes('aria-label="Global pages"') &&
|
||||||
globalSidebar.includes("/account") &&
|
!globalNavSections.includes('<p class="sidebar-section-label">') &&
|
||||||
globalSidebar.includes("/login/device") &&
|
globalNavSections.includes('"/account"') &&
|
||||||
!globalSidebar.includes("Tickets") &&
|
globalNavSections.includes('"/login/device"') &&
|
||||||
!globalSidebar.includes("Repositories"),
|
!globalNavSections.includes('label: "Workspaces"') &&
|
||||||
"Root default sidebar should contain only global navigation, not workspace-scoped sections",
|
globalNavSections.includes("sidebar-nav-section--category") &&
|
||||||
|
globalNavSections.includes("global-workspaces-heading") &&
|
||||||
|
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}") &&
|
||||||
|
!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",
|
||||||
);
|
);
|
||||||
assert(
|
assert(
|
||||||
workspaceLayout.includes("{#snippet workspaceSidebar()}") &&
|
workspaceLayout.includes("{#snippet workspaceSidebar()}") &&
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { WorkspaceCatalogRecord } from "$lib/workspace/api/workspace-catalog";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
currentPath: string;
|
||||||
|
workspaces?: WorkspaceCatalogRecord[] | null;
|
||||||
|
workspaceError?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
let {
|
||||||
|
currentPath,
|
||||||
|
workspaces = null,
|
||||||
|
workspaceError = null,
|
||||||
|
}: Props = $props();
|
||||||
|
|
||||||
|
const globalItems = [
|
||||||
|
{ href: "/#workspace-create-title", label: "Create Workspace" },
|
||||||
|
{ href: "/account", label: "Account" },
|
||||||
|
{ href: "/login/device", label: "Device Login" },
|
||||||
|
];
|
||||||
|
|
||||||
|
function workspaceHref(workspaceId: string): string {
|
||||||
|
return `/w/${encodeURIComponent(workspaceId)}`;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<nav class="sidebar-sections" aria-label="Global pages">
|
||||||
|
<section class="sidebar-nav-section">
|
||||||
|
<div class="sidebar-list">
|
||||||
|
{#each globalItems as item}
|
||||||
|
<a
|
||||||
|
class="sidebar-link"
|
||||||
|
class:active={currentPath === item.href}
|
||||||
|
href={item.href}
|
||||||
|
aria-current={currentPath === item.href ? "page" : undefined}
|
||||||
|
>
|
||||||
|
<span>{item.label}</span>
|
||||||
|
</a>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{#if workspaces !== null}
|
||||||
|
<section class="sidebar-nav-section sidebar-nav-section--category" aria-labelledby="global-workspaces-heading">
|
||||||
|
<h2 id="global-workspaces-heading" class="sidebar-nav-section__header">workspaces</h2>
|
||||||
|
|
||||||
|
{#if workspaceError}
|
||||||
|
<p class="workspace-status error">Workspace list unavailable.</p>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if workspaces.length > 0}
|
||||||
|
<div class="sidebar-list">
|
||||||
|
{#each workspaces as workspace (workspace.workspace_id)}
|
||||||
|
{@const href = workspaceHref(workspace.workspace_id)}
|
||||||
|
<a
|
||||||
|
class="sidebar-link"
|
||||||
|
class:active={currentPath === href}
|
||||||
|
{href}
|
||||||
|
aria-current={currentPath === href ? "page" : undefined}
|
||||||
|
>
|
||||||
|
<span>{workspace.display_name}</span>
|
||||||
|
</a>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{:else if !workspaceError}
|
||||||
|
<p class="workspace-status">No accessible Workspaces.</p>
|
||||||
|
{/if}
|
||||||
|
</section>
|
||||||
|
{/if}
|
||||||
|
</nav>
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { SidebarSnippet } from './context';
|
import type { SidebarSnippet } from './context';
|
||||||
|
import GlobalNavSections from './GlobalNavSections.svelte';
|
||||||
import './sidebar.css';
|
import './sidebar.css';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -8,13 +9,6 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const { currentPath, content = null }: Props = $props();
|
const { currentPath, content = null }: Props = $props();
|
||||||
|
|
||||||
const items = [
|
|
||||||
{ href: '/', label: 'Workspaces' },
|
|
||||||
{ href: '/#workspace-create-title', label: 'Create Workspace' },
|
|
||||||
{ href: '/account', label: 'Account' },
|
|
||||||
{ href: '/login/device', label: 'Device Login' },
|
|
||||||
];
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if content}
|
{#if content}
|
||||||
@@ -22,18 +16,7 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<div class="global-sidebar" aria-label="Global navigation">
|
<div class="global-sidebar" aria-label="Global navigation">
|
||||||
<div class="global-sidebar-section">
|
<div class="global-sidebar-section">
|
||||||
<nav class="sidebar-list" aria-label="Global pages">
|
<GlobalNavSections {currentPath} />
|
||||||
{#each items as item}
|
|
||||||
<a
|
|
||||||
class="sidebar-link"
|
|
||||||
class:active={currentPath === item.href}
|
|
||||||
href={item.href}
|
|
||||||
aria-current={currentPath === item.href ? 'page' : undefined}
|
|
||||||
>
|
|
||||||
<span>{item.label}</span>
|
|
||||||
</a>
|
|
||||||
{/each}
|
|
||||||
</nav>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
type CreateWorkspaceRequest,
|
type CreateWorkspaceRequest,
|
||||||
type WorkspaceCatalogItem,
|
type WorkspaceCatalogItem,
|
||||||
} from "$lib/workspace/api/workspace-catalog";
|
} 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";
|
import "$lib/workspace/styles/workspace-catalog.css";
|
||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
@@ -87,6 +89,16 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
{#snippet homeSidebar()}
|
||||||
|
<GlobalNavSections
|
||||||
|
currentPath="/"
|
||||||
|
{workspaces}
|
||||||
|
workspaceError={catalogError}
|
||||||
|
/>
|
||||||
|
{/snippet}
|
||||||
|
|
||||||
|
<SidebarOverride sidebar={homeSidebar} />
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>Workspaces · Yoi</title>
|
<title>Workspaces · Yoi</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|||||||
Reference in New Issue
Block a user