fix: use self-portaling DetailPanel from design system v0.1.5
Some checks failed
CI / build (push) Failing after 57s
CI / cleanup-branch (push) Has been skipped
CI / docker (push) Has been skipped
CI / deploy (push) Has been skipped
CI / deploy-feature (push) Has been skipped

DetailPanel now portals itself to #cameleer-detail-panel-root (a div
AppShell places as a sibling of .main in the top-level flex row).
Pages just render <DetailPanel> inline — no manual createPortal,
no context, no prop drilling.

Remove the old #detail-panel-portal div from LayoutShell and the
createPortal wrappers from Dashboard and AgentHealth.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-24 19:00:02 +01:00
parent 5d2eff4f73
commit a5bc7cf6d1
5 changed files with 50 additions and 59 deletions

61
ui/package-lock.json generated
View File

@@ -8,7 +8,7 @@
"name": "ui",
"version": "0.0.0",
"dependencies": {
"@cameleer/design-system": "^0.1.4",
"@cameleer/design-system": "file:../../design-system",
"@tanstack/react-query": "^5.90.21",
"openapi-fetch": "^0.17.0",
"react": "^19.2.4",
@@ -35,6 +35,34 @@
"vite": "^8.0.0"
}
},
"../../design-system": {
"name": "@cameleer/design-system",
"version": "0.1.5",
"dependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-router-dom": "^7.0.0"
},
"devDependencies": {
"@playwright/test": "^1.58.2",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@vitejs/plugin-react": "^4.3.0",
"happy-dom": "^20.8.4",
"typescript": "^5.6.0",
"vite": "^6.0.0",
"vite-plugin-dts": "^4.5.4",
"vitest": "^3.0.0"
},
"peerDependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-router-dom": "^7.0.0"
}
},
"node_modules/@babel/code-frame": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
@@ -276,19 +304,8 @@
}
},
"node_modules/@cameleer/design-system": {
"version": "0.1.4",
"resolved": "https://gitea.siegeln.net/api/packages/cameleer/npm/%40cameleer%2Fdesign-system/-/0.1.4/design-system-0.1.4.tgz",
"integrity": "sha512-DWyHv/1Nr8/h56T2ny1dsOZGy9YK64hfajTMOsl7gkWF3PwLPiB9Hpapa4O2Y5T/sspegxdlSZSddHF7muyFbw==",
"dependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-router-dom": "^7.0.0"
},
"peerDependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-router-dom": "^7.0.0"
}
"resolved": "../../design-system",
"link": true
},
"node_modules/@emnapi/core": {
"version": "1.9.1",
@@ -2955,22 +2972,6 @@
}
}
},
"node_modules/react-router-dom": {
"version": "7.13.2",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.2.tgz",
"integrity": "sha512-aR7SUORwTqAW0JDeiWF07e9SBE9qGpByR9I8kJT5h/FrBKxPMS6TiC7rmVO+gC0q52Bx7JnjWe8Z1sR9faN4YA==",
"license": "MIT",
"dependencies": {
"react-router": "7.13.2"
},
"engines": {
"node": ">=20.0.0"
},
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18"
}
},
"node_modules/require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",

View File

@@ -14,7 +14,7 @@
"generate-api:live": "curl -s http://localhost:8081/api/v1/api-docs -o src/api/openapi.json && openapi-typescript src/api/openapi.json -o src/api/schema.d.ts"
},
"dependencies": {
"@cameleer/design-system": "^0.1.4",
"@cameleer/design-system": "^0.1.5",
"@tanstack/react-query": "^5.90.21",
"openapi-fetch": "^0.17.0",
"react": "^19.2.4",

View File

@@ -137,8 +137,6 @@ function LayoutContent() {
<main style={{ flex: 1, overflow: 'auto', padding: '1.5rem' }}>
<Outlet />
</main>
{/* Portal target for DetailPanel — pages use createPortal to render here */}
<div id="detail-panel-portal" />
</AppShell>
);
}

View File

@@ -1,5 +1,4 @@
import { useState, useMemo } from 'react';
import { createPortal } from 'react-dom';
import { useParams, Link } from 'react-router';
import {
StatCard, StatusDot, Badge, MonoText, ProgressBar,
@@ -504,18 +503,15 @@ export default function AgentHealth() {
</div>
)}
{/* Detail panel — portaled to AppShell level for proper slide-in */}
{selectedInstance && document.getElementById('detail-panel-portal') &&
createPortal(
<DetailPanel
open={panelOpen}
onClose={() => { setPanelOpen(false); setSelectedInstance(null); }}
title={selectedInstance.name ?? selectedInstance.id}
tabs={detailTabs}
/>,
document.getElementById('detail-panel-portal')!,
)
}
{/* Detail panel — auto-portals to AppShell level via design system */}
{selectedInstance && (
<DetailPanel
open={panelOpen}
onClose={() => { setPanelOpen(false); setSelectedInstance(null); }}
title={selectedInstance.name ?? selectedInstance.id}
tabs={detailTabs}
/>
)}
</div>
);
}

View File

@@ -1,5 +1,4 @@
import { useState, useMemo, useCallback } from 'react'
import { createPortal } from 'react-dom'
import { useParams, useNavigate } from 'react-router'
import {
DataTable,
@@ -415,14 +414,13 @@ export default function Dashboard() {
{/* Shortcuts bar */}
<ShortcutsBar shortcuts={SHORTCUTS} />
{/* Detail panel — portaled to AppShell level for proper slide-in */}
{selectedRow && detail && document.getElementById('detail-panel-portal') &&
createPortal(
<DetailPanel
open={panelOpen}
onClose={() => setPanelOpen(false)}
title={`${detail.routeId} \u2014 ${selectedRow.executionId.slice(0, 12)}`}
>
{/* Detail panel — auto-portals to AppShell level via design system */}
{selectedRow && detail && (
<DetailPanel
open={panelOpen}
onClose={() => setPanelOpen(false)}
title={`${detail.routeId} \u2014 ${selectedRow.executionId.slice(0, 12)}`}
>
<div className={styles.panelSection}>
<button
className={styles.openDetailLink}
@@ -498,10 +496,8 @@ export default function Dashboard() {
<div style={{ color: 'var(--text-muted)', fontSize: 12 }}>No processor data</div>
)}
</div>
</DetailPanel>,
document.getElementById('detail-panel-portal')!,
)
}
</DetailPanel>
)}
</>
)
}