mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-23 23:46:35 +00:00
fix(admin): let the plugin consent, signature and dependency dialogs scroll
This commit is contained in:
@@ -281,6 +281,10 @@ describe('AdminPluginsPanel — a refused update', () => {
|
||||
expect(await screen.findByText(/key it was installed with/i)).toBeInTheDocument()
|
||||
expect(screen.getByText(/key it is offering now/i)).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: /trust the new key/i })).toBeInTheDocument()
|
||||
// The dialog body scrolls; the decision buttons sit outside it, so they never leave the viewport (#2159).
|
||||
const body = screen.getByText(/key it is offering now/i).closest('.overflow-y-auto')
|
||||
expect(body).not.toBeNull()
|
||||
expect(body).not.toContainElement(screen.getByRole('button', { name: /trust the new key/i }))
|
||||
})
|
||||
|
||||
// D2, at the UI. An invalid signature means the bytes are not what the author signed.
|
||||
@@ -947,7 +951,11 @@ describe('AdminPluginsPanel — enabling a plugin', () => {
|
||||
await screen.findByText('Gotify')
|
||||
|
||||
fireEvent.click(rowToggle('Gotify'))
|
||||
fireEvent.click(await screen.findByRole('button', { name: /^cancel$/i }))
|
||||
// The dependency list scrolls; Cancel sits outside it, so it never leaves the viewport (#2159).
|
||||
const body = (await screen.findByText('trek-core')).closest('.overflow-y-auto')
|
||||
expect(body).not.toBeNull()
|
||||
expect(body).not.toContainElement(screen.getByRole('button', { name: /^cancel$/i }))
|
||||
fireEvent.click(screen.getByRole('button', { name: /^cancel$/i }))
|
||||
|
||||
await waitFor(() => expect(screen.queryByText('Missing dependencies')).not.toBeInTheDocument())
|
||||
expect(installs).toBe(0)
|
||||
@@ -967,6 +975,11 @@ describe('AdminPluginsPanel — enabling a plugin', () => {
|
||||
// An unknown permission is still shown — as its raw code
|
||||
expect(screen.getByText('weird:perm')).toBeInTheDocument()
|
||||
expect(screen.getByText('api.acme.io')).toBeInTheDocument()
|
||||
// A long permission list scrolls inside the dialog; the decision buttons sit outside
|
||||
// the scroll container, so they never leave the viewport (#2159).
|
||||
const body = screen.getByText('Read trips the acting user can access').closest('.overflow-y-auto')
|
||||
expect(body).not.toBeNull()
|
||||
expect(body).not.toContainElement(screen.getByRole('button', { name: /keep off for now/i }))
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: /keep off for now/i }))
|
||||
expect(await screen.findByText(/left off until you approve/i)).toBeInTheDocument()
|
||||
|
||||
@@ -1793,15 +1793,15 @@ function SignatureBlockDialog({ data, entry, busy, t, onRetrust, onClose }: {
|
||||
|
||||
return (
|
||||
<div role="presentation" className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4" onClick={onClose}>
|
||||
<div role="presentation" className="bg-surface-card border border-edge rounded-2xl w-full max-w-md shadow-modal overflow-hidden" onClick={e => e.stopPropagation()}>
|
||||
<div className="px-5 py-4 border-b border-edge-secondary flex items-start gap-3">
|
||||
<div role="presentation" className="bg-surface-card border border-edge rounded-2xl w-full max-w-md max-h-[88vh] flex flex-col shadow-modal overflow-hidden" onClick={e => e.stopPropagation()}>
|
||||
<div className="shrink-0 px-5 py-4 border-b border-edge-secondary flex items-start gap-3">
|
||||
<div className="w-9 h-9 rounded-lg bg-warning-soft grid place-items-center shrink-0"><ShieldAlert size={18} className="text-warning" /></div>
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-content">{t('admin.plugins.sig.title', { name: data.subject.name })}</h3>
|
||||
<p className="text-xs text-content-muted mt-1">{t(bodyKey as never)}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-5 space-y-4">
|
||||
<div className="p-5 space-y-4 min-h-0 flex-1 overflow-y-auto">
|
||||
{/* Fingerprints, not full keys: these exist to be COMPARED by a human — read the
|
||||
new one back to the author over the phone. The full key travels in the request. */}
|
||||
{canRetrust && (
|
||||
@@ -1815,7 +1815,7 @@ function SignatureBlockDialog({ data, entry, busy, t, onRetrust, onClose }: {
|
||||
<p className="text-xs text-content-faint font-mono break-all bg-surface-tertiary rounded-lg px-3 py-2">{data.detail}</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="px-5 py-3.5 border-t border-edge-secondary bg-surface-secondary flex items-center justify-end gap-2">
|
||||
<div className="shrink-0 px-5 py-3.5 border-t border-edge-secondary bg-surface-secondary flex items-center justify-end gap-2">
|
||||
<button type="button" onClick={onClose}
|
||||
className="text-xs font-medium px-3.5 py-2 rounded-lg border border-edge text-content-muted hover:text-content hover:bg-surface-tertiary transition-colors">
|
||||
{offerRetrust ? t('admin.plugins.sig.cancel') : t('common.close')}
|
||||
@@ -1857,15 +1857,15 @@ function UpdateConsentDialog({ data, unsigned, t, onApprove, onLater }: {
|
||||
}) {
|
||||
return (
|
||||
<div role="presentation" className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4" onClick={onLater}>
|
||||
<div role="presentation" className="bg-surface-card border border-edge rounded-2xl w-full max-w-md shadow-modal overflow-hidden" onClick={e => e.stopPropagation()}>
|
||||
<div className="px-5 py-4 border-b border-edge-secondary flex items-start gap-3">
|
||||
<div role="presentation" className="bg-surface-card border border-edge rounded-2xl w-full max-w-md max-h-[88vh] flex flex-col shadow-modal overflow-hidden" onClick={e => e.stopPropagation()}>
|
||||
<div className="shrink-0 px-5 py-4 border-b border-edge-secondary flex items-start gap-3">
|
||||
<div className="w-9 h-9 rounded-lg bg-warning-soft grid place-items-center shrink-0"><ShieldCheck size={18} className="text-warning" /></div>
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-content">{t('admin.plugins.updateConsentTitle')}</h3>
|
||||
<p className="text-xs text-content-muted mt-1">{t('admin.plugins.updateConsentBody', { name: data.plugin.name, version: data.version })}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-5 space-y-4">
|
||||
<div className="p-5 space-y-4 min-h-0 flex-1 overflow-y-auto">
|
||||
{/* The admin is about to widen what this code may do — so say, right here, that
|
||||
nothing ties this code to its author. One line, no checkbox, no extra click:
|
||||
this informs, it does not block. */}
|
||||
@@ -1894,7 +1894,7 @@ function UpdateConsentDialog({ data, unsigned, t, onApprove, onLater }: {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="px-5 py-3.5 border-t border-edge-secondary bg-surface-secondary flex items-center justify-end gap-2">
|
||||
<div className="shrink-0 px-5 py-3.5 border-t border-edge-secondary bg-surface-secondary flex items-center justify-end gap-2">
|
||||
<button type="button" onClick={onLater} className="text-xs font-medium px-3.5 py-2 rounded-lg border border-edge text-content-muted hover:text-content hover:bg-surface-tertiary transition-colors">{t('admin.plugins.updateLater')}</button>
|
||||
<button type="button" onClick={onApprove} className="text-xs font-semibold px-4 py-2 rounded-lg bg-accent text-accent-text hover:bg-accent-hover transition-colors">{t('admin.plugins.updateApprove')}</button>
|
||||
</div>
|
||||
@@ -1917,15 +1917,15 @@ function DependencyResolveDialog({ data, t, busy, installedIds, onDownload, onCl
|
||||
]
|
||||
return (
|
||||
<div role="presentation" className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4" onClick={onClose}>
|
||||
<div role="presentation" className="bg-surface-card border border-edge rounded-2xl w-full max-w-md shadow-modal overflow-hidden" onClick={e => e.stopPropagation()}>
|
||||
<div className="px-5 py-4 border-b border-edge-secondary flex items-start gap-3">
|
||||
<div role="presentation" className="bg-surface-card border border-edge rounded-2xl w-full max-w-md max-h-[88vh] flex flex-col shadow-modal overflow-hidden" onClick={e => e.stopPropagation()}>
|
||||
<div className="shrink-0 px-5 py-4 border-b border-edge-secondary flex items-start gap-3">
|
||||
<div className="w-9 h-9 rounded-lg bg-warning-soft grid place-items-center shrink-0"><Puzzle size={18} className="text-warning" /></div>
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-content">{t('admin.plugins.dep.resolveTitle')}</h3>
|
||||
<p className="text-xs text-content-muted mt-1">{t('admin.plugins.dep.resolveBody', { name: data.plugin.name })}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-5 space-y-2.5">
|
||||
<div className="p-5 space-y-2.5 min-h-0 flex-1 overflow-y-auto">
|
||||
{rows.map(r => (
|
||||
<div key={r.id} className="flex items-center gap-3 p-3 rounded-xl border border-edge-secondary bg-surface-tertiary">
|
||||
<div className="min-w-0 flex-1">
|
||||
@@ -1946,7 +1946,7 @@ function DependencyResolveDialog({ data, t, busy, installedIds, onDownload, onCl
|
||||
<p className="text-[11.5px] text-content-faint pt-1">{t('admin.plugins.dep.resolveHint')}</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="px-5 py-3.5 border-t border-edge-secondary bg-surface-secondary flex items-center justify-end">
|
||||
<div className="shrink-0 px-5 py-3.5 border-t border-edge-secondary bg-surface-secondary flex items-center justify-end">
|
||||
<button type="button" onClick={onClose} className="text-xs font-medium px-3.5 py-2 rounded-lg border border-edge text-content-muted hover:text-content hover:bg-surface-tertiary transition-colors">{t('common.cancel')}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user