feat(ui): menus déroulants + fenêtres flottantes, barre unique et sélecteur de projet (#16)

Lot A du sprint UI rework : réorganisation de la navigation racine.

- shared/ui/MenuBar.tsx : barre de menus unique avec menus déroulants
- shared/ui/FloatingWindow.tsx : primitive de fenêtre flottante réutilisable
  (socle du lot C #17 fenêtre dédiée gestion tickets)
- shared/ui/zIndex.ts : échelle z-index étendue pour les fenêtres flottantes
- App : intègre la barre unique et le sélecteur de projet permanent
- ProjectsView : sélecteur de projet permanent

Tests : tsc --noEmit clean, suite complète 527/527, suites impactées 33/33.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-06 20:55:37 +02:00
parent 0218e3271f
commit c55f948d25
9 changed files with 933 additions and 353 deletions

View File

@ -110,16 +110,22 @@ function renderView(project: MockProjectGateway) {
);
}
/** Opens a menu-bar dropdown item (#16 chrome). */
function openMenuItem(menu: string, item: string) {
fireEvent.click(screen.getByRole("button", { name: menu }));
fireEvent.click(screen.getByRole("button", { name: item }));
}
async function openProjectAndWorkTab(label: string) {
await screen.findByText(label);
// Open the project whose root is `label`.
// Open the project whose root is `label` from the welcome projects list.
const li = screen
.getAllByRole("listitem")
.find((node) => within(node).queryByText(label));
fireEvent.click(within(li!).getByRole("button", { name: "Open" }));
await screen.findByRole("tab");
// Switch the sidebar to the Work panel.
fireEvent.click(screen.getByRole("button", { name: "Work" }));
// Open the Work panel window from the View menu.
openMenuItem("View", "Work");
}
describe("ProjectsView — LS7 conversation viewer integration", () => {
@ -165,8 +171,11 @@ describe("ProjectsView — LS7 conversation viewer integration", () => {
await project.createProject("beta", "/p/b");
renderView(project);
// Open BOTH projects as tabs (while the Projects sidebar list is visible).
// Open BOTH projects as tabs. Use the projects manager window so the list
// persists across activation (the inline welcome list is replaced by the
// grid once a project is active).
await screen.findByText("/p/a");
openMenuItem("File", "Projects…");
for (const root of ["/p/a", "/p/b"]) {
const li = screen
.getAllByRole("listitem")
@ -177,7 +186,7 @@ describe("ProjectsView — LS7 conversation viewer integration", () => {
// Make alpha the active tab, then open its conversation viewer.
fireEvent.click(screen.getByRole("tab", { name: "alpha" }));
fireEvent.click(screen.getByRole("button", { name: "Work" }));
openMenuItem("View", "Work");
fireEvent.click(
await screen.findByRole("button", { name: `open conversation ${CONV_ID}` }),
);