From e837a9d416a8aa3d6b3b3148bc3795a93eee65fa Mon Sep 17 00:00:00 2001 From: you Date: Fri, 17 Apr 2026 07:41:14 +0000 Subject: [PATCH] fix: Details button opens full-screen node detail on desktop (#778) The previous fix (#779) used hash navigation for the Details button, which only re-selected the node in the side panel on desktop. Now the Details button calls showFullScreenNode() to render the same full-screen detail view that mobile users see, regardless of screen width. - Extract showFullScreenNode() from mobile-only init code - Reuse it for both mobile init and desktop Details button click - Analytics links still navigate via hash as before - Update Playwright test to verify full-screen view renders --- public/nodes.js | 58 +++++++++++++++++++++++++----------------- test-e2e-playwright.js | 18 +++++-------- 2 files changed, 42 insertions(+), 34 deletions(-) diff --git a/public/nodes.js b/public/nodes.js index 439c3677..37b4d89f 100644 --- a/public/nodes.js +++ b/public/nodes.js @@ -315,29 +315,34 @@ let regionChangeHandler = null; + // Show full-screen node detail view (works on any screen size) + function showFullScreenNode(pubkey) { + var app = document.getElementById('app'); + app.innerHTML = '
' + + '
' + + '' + + 'Loading…' + + '
' + + '
' + + '
Loading…
' + + '
' + + '
'; + document.getElementById('nodeBackBtn').addEventListener('click', function() { location.hash = '#/nodes'; }); + loadFullNode(pubkey); + document.addEventListener('keydown', function nodesEsc(e) { + if (e.key === 'Escape') { + document.removeEventListener('keydown', nodesEsc); + location.hash = '#/nodes'; + } + }); + } + function init(app, routeParam) { directNode = routeParam || null; if (directNode && window.innerWidth <= 640) { - // Full-screen single node view (mobile only) - app.innerHTML = `
-
- - Loading… -
-
-
Loading…
-
-
`; - document.getElementById('nodeBackBtn').addEventListener('click', () => { location.hash = '#/nodes'; }); - loadFullNode(directNode); - // Escape to go back to nodes list - document.addEventListener('keydown', function nodesEsc(e) { - if (e.key === 'Escape') { - document.removeEventListener('keydown', nodesEsc); - location.hash = '#/nodes'; - } - }); + // Full-screen single node view (mobile) + showFullScreenNode(directNode); return; } @@ -1045,10 +1050,17 @@ var link = e.target.closest('a.btn-primary[href^="#/nodes/"]'); if (link) { e.preventDefault(); - var target = link.getAttribute('href'); - // Always clear and reassign — hashchange won't fire if hash already matches - history.replaceState(null, '', '#/'); - location.hash = target.substring(1); + var href = link.getAttribute('href'); + var pubkey = decodeURIComponent(href.replace('#/nodes/', '').replace('/analytics', '')); + if (href.includes('/analytics')) { + // Navigate to analytics page + history.replaceState(null, '', '#/'); + location.hash = '/nodes/' + encodeURIComponent(pubkey) + '/analytics'; + } else { + // Show full-screen node detail view + showFullScreenNode(pubkey); + history.replaceState(null, '', '#/nodes/' + encodeURIComponent(pubkey)); + } return; } if (e.target.closest('.panel-close-btn')) { diff --git a/test-e2e-playwright.js b/test-e2e-playwright.js index 4e6f9a6c..e397c604 100644 --- a/test-e2e-playwright.js +++ b/test-e2e-playwright.js @@ -231,8 +231,8 @@ async function run() { assert(hasStatus, 'No status indicator found in node detail'); }); - // Test: Node side panel Details link navigates to full detail page (#778) - await test('Node side panel Details link navigates', async () => { + // Test: Node side panel Details link opens full-screen detail view (#778) + await test('Node side panel Details link opens full detail', async () => { await page.goto(`${BASE}/#/nodes`, { waitUntil: 'domcontentloaded' }); await page.waitForSelector('table tbody tr'); // Click first row to open side panel @@ -243,16 +243,12 @@ async function run() { // Find the Details link in the side panel const detailsLink = await page.$('#nodesRight a.btn-primary[href^="#/nodes/"]'); assert(detailsLink, 'Details link not found in side panel'); - const href = await detailsLink.getAttribute('href'); - // Click the Details link — this should navigate to the full detail page + // Click the Details link — should open full-screen node detail view await detailsLink.click(); - // Wait for navigation — the full detail page has sections like neighbors/packets - await page.waitForFunction((expectedHash) => { - return location.hash === expectedHash; - }, href, { timeout: 5000 }); - // Verify we're on the full detail page (should have section tabs or detail content) - const hash = await page.evaluate(() => location.hash); - assert(hash === href, `Expected hash "${href}" but got "${hash}"`); + // Wait for the full-screen node detail view to render + await page.waitForSelector('.node-fullscreen', { timeout: 5000 }); + const hasFullBody = await page.$('.node-full-body'); + assert(hasFullBody, 'Full-screen node detail body not found'); }); // Test: Nodes page has WebSocket auto-update listener (#131)