Compare commits

...

1 Commits

Author SHA1 Message Date
you
e837a9d416 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
2026-04-17 07:41:14 +00:00
2 changed files with 42 additions and 34 deletions

View File

@@ -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 = '<div class="node-fullscreen">' +
'<div class="node-full-header">' +
'<button class="detail-back-btn node-back-btn" id="nodeBackBtn" aria-label="Back to nodes">←</button>' +
'<span class="node-full-title">Loading…</span>' +
'</div>' +
'<div class="node-full-body" id="nodeFullBody">' +
'<div class="text-center text-muted" style="padding:40px">Loading…</div>' +
'</div>' +
'</div>';
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 = `<div class="node-fullscreen">
<div class="node-full-header">
<button class="detail-back-btn node-back-btn" id="nodeBackBtn" aria-label="Back to nodes">←</button>
<span class="node-full-title">Loading…</span>
</div>
<div class="node-full-body" id="nodeFullBody">
<div class="text-center text-muted" style="padding:40px">Loading…</div>
</div>
</div>`;
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')) {

View File

@@ -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)