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:
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
Executable
+306
@@ -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();
|
||||
Reference in New Issue
Block a user