fix: implement complete hover synchronization for PDF/Print buttons

CRITICAL BUG FIX: Hover states now sync between action bar and hamburger menu

Changes:
1. Added mouseenter/mouseleave handlers to menu PDF button
   - templates/partials/navigation/hamburger-menu.html:178-181
   - Added .menu-pdf-btn class for targeting
   - Added hyperscript hover sync events

2. Updated syncPdfHover() function
   - static/js/cv-functions.js:71-82
   - Now selects both .pdf-btn and .menu-pdf-btn
   - Both buttons get .pdf-hover-sync class on hover

3. Updated syncPrintHover() function
   - static/js/cv-functions.js:88-99
   - Now selects both .print-btn and .menu-print-btn
   - Both buttons get .print-hover-sync class on hover

4. Added CSS for menu PDF button hover sync
   - static/css/main.css:2690-2700
   - .menu-pdf-btn.pdf-hover-sync styling (white bg, red icon)
   - Matches action bar PDF button hover state

5. Created comprehensive hover sync test
   - tests/mjs/8-hover-sync.test.mjs
   - Tests all 4 hover scenarios (bar→menu, menu→bar for both buttons)
   - Validates event handlers and CSS class application
   - Manual verification instructions included

Behavior now correct:
 Hovering action bar PDF button highlights menu PDF button
 Hovering action bar Print button highlights menu Print button
 Hovering menu PDF button highlights action bar PDF button
 Hovering menu Print button highlights action bar Print button

Fixes documented bug from PROJECT-MEMORY.md Section 3.
This commit is contained in:
juanatsap
2025-11-17 14:35:32 +00:00
parent fba52727bc
commit f87a1a5c28
4 changed files with 326 additions and 3 deletions
+12
View File
@@ -2687,6 +2687,18 @@ text-align: center;
color: var(--accent-blue);
}
/* PDF button in menu - White bg with red icon on hover */
.menu-pdf-btn:hover,
.menu-pdf-btn.pdf-hover-sync {
background: white !important;
color: #e74c3c !important;
}
.menu-pdf-btn:hover iconify-icon,
.menu-pdf-btn.pdf-hover-sync iconify-icon {
color: #e74c3c !important;
}
/* Print button in menu - White bg with green icon on hover */
.menu-print-btn:hover,
.menu-print-btn.print-hover-sync {
+4 -2
View File
@@ -69,7 +69,8 @@ function toggleTheme(isClean) {
* @param {boolean} show - true to add hover class, false to remove
*/
function syncPdfHover(show) {
const pdfButtons = document.querySelectorAll('.pdf-btn');
// Select both action bar PDF button and menu PDF button
const pdfButtons = document.querySelectorAll('.pdf-btn, .menu-pdf-btn');
pdfButtons.forEach(button => {
if (show) {
@@ -85,7 +86,8 @@ function syncPdfHover(show) {
* @param {boolean} show - true to add hover class, false to remove
*/
function syncPrintHover(show) {
const printButtons = document.querySelectorAll('.print-btn');
// Select both action bar Print button and menu Print button
const printButtons = document.querySelectorAll('.print-btn, .menu-print-btn');
printButtons.forEach(button => {
if (show) {
@@ -175,7 +175,10 @@
<span>{{if eq .Lang "es"}}Acciones{{else}}Actions{{end}}</span>
</div>
<button class="menu-action-btn" onclick="document.getElementById('pdf-modal').showModal()">
<button class="menu-action-btn menu-pdf-btn"
onclick="document.getElementById('pdf-modal').showModal()"
_="on mouseenter call syncPdfHover(true)
on mouseleave call syncPdfHover(false)">
<iconify-icon icon="catppuccin:pdf" width="20" height="20"></iconify-icon>
<span>{{if eq .Lang "es"}}Descargar como PDF{{else}}Download as PDF{{end}}</span>
</button>
+306
View File
@@ -0,0 +1,306 @@
#!/usr/bin/env bun
/**
* HOVER SYNCHRONIZATION TEST
* ===========================
* Tests hover state synchronization between action bar and hamburger menu buttons
*
* CRITICAL BUG: PDF/Print button hover states don't sync between locations
*
* Expected behavior:
* - Hovering action bar PDF button should highlight menu PDF button
* - Hovering action bar Print button should highlight menu Print button
* - Hovering menu buttons should highlight action bar buttons
* - Classes added: .pdf-hover-sync and .print-hover-sync
*/
import { chromium } from 'playwright';
const URL = "http://localhost:1999";
async function testHoverSync() {
console.log('🔄 HOVER SYNCHRONIZATION TEST\n');
console.log('='.repeat(70));
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage({ viewport: { width: 1920, height: 1080 } });
const errors = [];
const testResults = [];
// Track errors
page.on('console', msg => {
if (msg.type() === 'error') {
errors.push(msg.text());
console.log(`❌ ERROR: ${msg.text()}`);
}
});
await page.goto(URL);
await page.waitForTimeout(2000);
// ========================================================================
// TEST 1: PDF Button Hover Sync (Action Bar → Menu)
// ========================================================================
console.log("\n1️⃣ Testing PDF Button Hover Sync (Action Bar → Menu)...");
// Open hamburger menu to make menu buttons visible
const hamburger = await page.$('.hamburger-btn');
if (hamburger) {
await hamburger.click();
await page.waitForTimeout(500);
}
const pdfTest1 = await page.evaluate(() => {
// Find buttons
const actionBarPdf = document.querySelector('#action-bar-pdf-btn');
const menuPdf = document.querySelector('.menu-action-btn'); // First menu-action-btn is PDF
if (!actionBarPdf || !menuPdf) {
return { found: false, actionBarPdf: !!actionBarPdf, menuPdf: !!menuPdf };
}
// Simulate hover on action bar button
const mouseEnter = new MouseEvent('mouseenter', { bubbles: true });
actionBarPdf.dispatchEvent(mouseEnter);
// Check if both have hover class
const actionBarHasClass = actionBarPdf.classList.contains('pdf-hover-sync');
const menuHasClass = menuPdf.classList.contains('pdf-hover-sync');
// Clean up
const mouseLeave = new MouseEvent('mouseleave', { bubbles: true });
actionBarPdf.dispatchEvent(mouseLeave);
return {
found: true,
actionBarHasClass,
menuHasClass,
actionBarClasses: Array.from(actionBarPdf.classList),
menuClasses: Array.from(menuPdf.classList)
};
});
if (!pdfTest1.found) {
console.log(` ❌ FAIL - Buttons not found`);
console.log(` Action bar PDF: ${pdfTest1.actionBarPdf ? '✅' : '❌'}`);
console.log(` Menu PDF: ${pdfTest1.menuPdf ? '✅' : '❌'}`);
testResults.push({ test: 'PDF Hover Sync (Bar→Menu)', passed: false });
} else {
console.log(` Action bar button adds class: ${pdfTest1.actionBarHasClass ? '✅' : '❌'}`);
console.log(` Menu button gets class: ${pdfTest1.menuHasClass ? '✅' : '❌'}`);
console.log(` Action bar classes: ${pdfTest1.actionBarClasses.join(', ')}`);
console.log(` Menu classes: ${pdfTest1.menuClasses.join(', ')}`);
const passed = pdfTest1.actionBarHasClass && pdfTest1.menuHasClass;
console.log(` ${passed ? '✅ PASS' : '❌ FAIL'} - PDF hover sync (bar→menu)`);
testResults.push({ test: 'PDF Hover Sync (Bar→Menu)', passed });
}
// ========================================================================
// TEST 2: Print Button Hover Sync (Action Bar → Menu)
// ========================================================================
console.log("\n2️⃣ Testing Print Button Hover Sync (Action Bar → Menu)...");
const printTest1 = await page.evaluate(() => {
const actionBarPrint = document.querySelector('.action-bar-print-btn');
const menuPrint = document.querySelector('.menu-print-btn');
if (!actionBarPrint || !menuPrint) {
return { found: false, actionBarPrint: !!actionBarPrint, menuPrint: !!menuPrint };
}
// Simulate hover on action bar button
const mouseEnter = new MouseEvent('mouseenter', { bubbles: true });
actionBarPrint.dispatchEvent(mouseEnter);
// Check if both have hover class
const actionBarHasClass = actionBarPrint.classList.contains('print-hover-sync');
const menuHasClass = menuPrint.classList.contains('print-hover-sync');
// Clean up
const mouseLeave = new MouseEvent('mouseleave', { bubbles: true });
actionBarPrint.dispatchEvent(mouseLeave);
return {
found: true,
actionBarHasClass,
menuHasClass,
actionBarClasses: Array.from(actionBarPrint.classList),
menuClasses: Array.from(menuPrint.classList)
};
});
if (!printTest1.found) {
console.log(` ❌ FAIL - Buttons not found`);
console.log(` Action bar Print: ${printTest1.actionBarPrint ? '✅' : '❌'}`);
console.log(` Menu Print: ${printTest1.menuPrint ? '✅' : '❌'}`);
testResults.push({ test: 'Print Hover Sync (Bar→Menu)', passed: false });
} else {
console.log(` Action bar button adds class: ${printTest1.actionBarHasClass ? '✅' : '❌'}`);
console.log(` Menu button gets class: ${printTest1.menuHasClass ? '✅' : '❌'}`);
console.log(` Action bar classes: ${printTest1.actionBarClasses.join(', ')}`);
console.log(` Menu classes: ${printTest1.menuClasses.join(', ')}`);
const passed = printTest1.actionBarHasClass && printTest1.menuHasClass;
console.log(` ${passed ? '✅ PASS' : '❌ FAIL'} - Print hover sync (bar→menu)`);
testResults.push({ test: 'Print Hover Sync (Bar→Menu)', passed });
}
// ========================================================================
// TEST 3: PDF Button Hover Sync (Menu → Action Bar)
// ========================================================================
console.log("\n3️⃣ Testing PDF Button Hover Sync (Menu → Action Bar)...");
const pdfTest2 = await page.evaluate(() => {
const actionBarPdf = document.querySelector('#action-bar-pdf-btn');
const menuPdf = document.querySelector('.menu-action-btn');
if (!actionBarPdf || !menuPdf) {
return { found: false };
}
// Check if menu button has event handlers
const hasMouseEnter = menuPdf.getAttribute('_')?.includes('mouseenter');
const hasMouseLeave = menuPdf.getAttribute('_')?.includes('mouseleave');
// Simulate hover on menu button (if it has handlers)
if (hasMouseEnter) {
const mouseEnter = new MouseEvent('mouseenter', { bubbles: true });
menuPdf.dispatchEvent(mouseEnter);
}
const actionBarHasClass = actionBarPdf.classList.contains('pdf-hover-sync');
const menuHasClass = menuPdf.classList.contains('pdf-hover-sync');
// Clean up
if (hasMouseLeave) {
const mouseLeave = new MouseEvent('mouseleave', { bubbles: true });
menuPdf.dispatchEvent(mouseLeave);
}
return {
found: true,
hasMouseEnter,
hasMouseLeave,
actionBarHasClass,
menuHasClass
};
});
if (!pdfTest2.found) {
console.log(` ❌ FAIL - Buttons not found`);
testResults.push({ test: 'PDF Hover Sync (Menu→Bar)', passed: false });
} else {
console.log(` Menu button has mouseenter handler: ${pdfTest2.hasMouseEnter ? '✅' : '❌'}`);
console.log(` Menu button has mouseleave handler: ${pdfTest2.hasMouseLeave ? '✅' : '❌'}`);
console.log(` Action bar button gets class: ${pdfTest2.actionBarHasClass ? '✅' : '❌'}`);
console.log(` Menu button adds class: ${pdfTest2.menuHasClass ? '✅' : '❌'}`);
const passed = pdfTest2.hasMouseEnter && pdfTest2.hasMouseLeave &&
pdfTest2.actionBarHasClass && pdfTest2.menuHasClass;
console.log(` ${passed ? '✅ PASS' : '❌ FAIL'} - PDF hover sync (menu→bar)`);
testResults.push({ test: 'PDF Hover Sync (Menu→Bar)', passed });
}
// ========================================================================
// TEST 4: Print Button Hover Sync (Menu → Action Bar)
// ========================================================================
console.log("\n4️⃣ Testing Print Button Hover Sync (Menu → Action Bar)...");
const printTest2 = await page.evaluate(() => {
const actionBarPrint = document.querySelector('.action-bar-print-btn');
const menuPrint = document.querySelector('.menu-print-btn');
if (!actionBarPrint || !menuPrint) {
return { found: false };
}
// Check if menu button has event handlers
const hasMouseEnter = menuPrint.getAttribute('_')?.includes('mouseenter');
const hasMouseLeave = menuPrint.getAttribute('_')?.includes('mouseleave');
// Simulate hover on menu button
const mouseEnter = new MouseEvent('mouseenter', { bubbles: true });
menuPrint.dispatchEvent(mouseEnter);
const actionBarHasClass = actionBarPrint.classList.contains('print-hover-sync');
const menuHasClass = menuPrint.classList.contains('print-hover-sync');
// Clean up
const mouseLeave = new MouseEvent('mouseleave', { bubbles: true });
menuPrint.dispatchEvent(mouseLeave);
return {
found: true,
hasMouseEnter,
hasMouseLeave,
actionBarHasClass,
menuHasClass
};
});
if (!printTest2.found) {
console.log(` ❌ FAIL - Buttons not found`);
testResults.push({ test: 'Print Hover Sync (Menu→Bar)', passed: false });
} else {
console.log(` Menu button has mouseenter handler: ${printTest2.hasMouseEnter ? '✅' : '❌'}`);
console.log(` Menu button has mouseleave handler: ${printTest2.hasMouseLeave ? '✅' : '❌'}`);
console.log(` Action bar button gets class: ${printTest2.actionBarHasClass ? '✅' : '❌'}`);
console.log(` Menu button adds class: ${printTest2.menuHasClass ? '✅' : '❌'}`);
const passed = printTest2.hasMouseEnter && printTest2.hasMouseLeave &&
printTest2.actionBarHasClass && printTest2.menuHasClass;
console.log(` ${passed ? '✅ PASS' : '❌ FAIL'} - Print hover sync (menu→bar)`);
testResults.push({ test: 'Print Hover Sync (Menu→Bar)', passed });
}
// ========================================================================
// TEST 5: Visual hover test (manual verification)
// ========================================================================
console.log("\n5️⃣ Manual Visual Test...");
console.log(" Please hover over the following and verify visual sync:");
console.log(" 1. Hover action bar PDF button → menu PDF should highlight");
console.log(" 2. Hover action bar Print button → menu Print should highlight");
console.log(" 3. Hover menu PDF button → action bar PDF should highlight");
console.log(" 4. Hover menu Print button → action bar Print should highlight");
// ========================================================================
// FINAL SUMMARY
// ========================================================================
console.log("\n" + "=".repeat(70));
console.log("📊 TEST SUMMARY\n");
const totalTests = testResults.length;
const passedTests = testResults.filter(r => r.passed).length;
const failedTests = totalTests - passedTests;
testResults.forEach(result => {
console.log(` ${result.passed ? '✅' : '❌'} ${result.test}`);
});
console.log(`\n Total: ${passedTests}/${totalTests} tests passed`);
if (errors.length === 0) {
console.log("\n✅ NO CONSOLE ERRORS");
} else {
console.log(`\n⚠️ ${errors.length} CONSOLE ERRORS`);
}
console.log("=".repeat(70) + "\n");
if (failedTests === 0) {
console.log("🎉 ALL HOVER SYNC TESTS PASSED!");
} else {
console.log("⚠️ SOME TESTS FAILED - Hover synchronization broken");
console.log("\nExpected bug locations:");
console.log(" - templates/partials/navigation/hamburger-menu.html (missing mouseenter/leave on PDF button)");
console.log(" - static/js/cv-functions.js (hover sync functions exist but not called properly)");
}
console.log("\nBrowser will stay open for manual verification.");
console.log("Press Ctrl+C when done.\n");
await new Promise(() => {}); // Keep browser open
}
await testHoverSync();