diff --git a/debug.png b/debug.png new file mode 100644 index 0000000..8e90977 Binary files /dev/null and b/debug.png differ diff --git a/index.html b/index.html index dda1d88..c84bc6c 100644 --- a/index.html +++ b/index.html @@ -92,7 +92,7 @@

Ruh Al Tarikh

0 - diff --git a/js/app.js b/js/app.js index 1e6898a..5bb8526 100644 --- a/js/app.js +++ b/js/app.js @@ -100,20 +100,6 @@ const DOM = { themeMenu: document.getElementById('themeMenu'), episodesNavBtn: document.querySelector('[data-action="scroll-to-episodes"]'), episodesSection: document.getElementById('episodesSection'), - episodesNavBtn: document.querySelector('[data-action="scroll-to-episodes"]'), - episodesSection: document.getElementById('episodesSection'), - episodesNavBtn: document.querySelector('[data-action="scroll-to-episodes"]'), - episodesSection: document.getElementById('episodesSection'), - episodesNavBtn: document.querySelector('[data-action="scroll-to-episodes"]'), - episodesSection: document.getElementById('episodesSection'), - episodesNavBtn: document.querySelector('[data-action="scroll-to-episodes"]'), - episodesSection: document.getElementById('episodesSection'), - episodesNavBtn: document.querySelector('[data-action="scroll-to-episodes"]'), - episodesSection: document.getElementById('episodesSection'), - episodesNavBtn: document.querySelector('[data-action="scroll-to-episodes"]'), - episodesSection: document.getElementById('episodesSection'), - episodesNavBtn: document.querySelector('[data-action="scroll-to-episodes"]'), - episodesSection: document.getElementById('episodesSection'), menuToggle: document.getElementById('menuToggleBtn'), scrollToTop: document.getElementById('scrollToTop'), @@ -750,6 +736,7 @@ function openDashboard() { initAIAssistant(); DOM.dashboardModal.style.display = 'block'; DOM.dashboardModal.setAttribute('aria-hidden', 'false'); + if (DOM.dashboardBtn) DOM.dashboardBtn.setAttribute('aria-expanded', 'true'); Utils.trapFocus(DOM.dashboardModal); DOM.body.style.overflow = 'hidden'; DOM.body.classList.add('modal-open'); @@ -759,6 +746,7 @@ function closeDashboard() { if (!DOM.dashboardModal) return; DOM.dashboardModal.style.display = "none"; DOM.dashboardModal.setAttribute("aria-hidden", "true"); + if (DOM.dashboardBtn) DOM.dashboardBtn.setAttribute('aria-expanded', 'false'); DOM.body.style.overflow = ""; DOM.body.classList.remove("modal-open"); if (AppState.lastFocused) { AppState.lastFocused.focus(); AppState.lastFocused = null; } @@ -1042,83 +1030,6 @@ function bindEvents() { }); } - // Navbar Episodes Scroll - if (DOM.episodesNavBtn && DOM.episodesSection) { - DOM.episodesNavBtn.addEventListener('click', () => { - DOM.episodesSection.scrollIntoView({ behavior: 'smooth' }); - if (document.body.classList.contains('mobile-nav-active')) { - document.body.classList.remove('mobile-nav-active'); - if (DOM.menuToggle) DOM.menuToggle.setAttribute('aria-expanded', 'false'); - } - }); - } - - // Navbar Episodes Scroll - if (DOM.episodesNavBtn && DOM.episodesSection) { - DOM.episodesNavBtn.addEventListener('click', () => { - DOM.episodesSection.scrollIntoView({ behavior: 'smooth' }); - if (document.body.classList.contains('mobile-nav-active')) { - document.body.classList.remove('mobile-nav-active'); - if (DOM.menuToggle) DOM.menuToggle.setAttribute('aria-expanded', 'false'); - } - }); - } - - // Navbar Episodes Scroll - if (DOM.episodesNavBtn && DOM.episodesSection) { - DOM.episodesNavBtn.addEventListener('click', () => { - DOM.episodesSection.scrollIntoView({ behavior: 'smooth' }); - if (document.body.classList.contains('mobile-nav-active')) { - document.body.classList.remove('mobile-nav-active'); - if (DOM.menuToggle) DOM.menuToggle.setAttribute('aria-expanded', 'false'); - } - }); - } - - // Navbar Episodes Scroll - if (DOM.episodesNavBtn && DOM.episodesSection) { - DOM.episodesNavBtn.addEventListener('click', () => { - DOM.episodesSection.scrollIntoView({ behavior: 'smooth' }); - if (document.body.classList.contains('mobile-nav-active')) { - document.body.classList.remove('mobile-nav-active'); - if (DOM.menuToggle) DOM.menuToggle.setAttribute('aria-expanded', 'false'); - } - }); - } - - // Navbar Episodes Scroll - if (DOM.episodesNavBtn && DOM.episodesSection) { - DOM.episodesNavBtn.addEventListener('click', () => { - DOM.episodesSection.scrollIntoView({ behavior: 'smooth' }); - if (document.body.classList.contains('mobile-nav-active')) { - document.body.classList.remove('mobile-nav-active'); - if (DOM.menuToggle) DOM.menuToggle.setAttribute('aria-expanded', 'false'); - } - }); - } - - // Navbar Episodes Scroll - if (DOM.episodesNavBtn && DOM.episodesSection) { - DOM.episodesNavBtn.addEventListener('click', () => { - DOM.episodesSection.scrollIntoView({ behavior: 'smooth' }); - if (document.body.classList.contains('mobile-nav-active')) { - document.body.classList.remove('mobile-nav-active'); - if (DOM.menuToggle) DOM.menuToggle.setAttribute('aria-expanded', 'false'); - } - }); - } - - // Navbar Episodes Scroll - if (DOM.episodesNavBtn && DOM.episodesSection) { - DOM.episodesNavBtn.addEventListener('click', () => { - DOM.episodesSection.scrollIntoView({ behavior: 'smooth' }); - if (document.body.classList.contains('mobile-nav-active')) { - document.body.classList.remove('mobile-nav-active'); - if (DOM.menuToggle) DOM.menuToggle.setAttribute('aria-expanded', 'false'); - } - }); - } - if (DOM.heroBtn) DOM.heroBtn.addEventListener('click', () => AppState.hero && openVideo(AppState.hero)); if (DOM.heroSave) DOM.heroSave.addEventListener('click', () => AppState.hero && toggleWatchLater(AppState.hero)); @@ -1332,6 +1243,17 @@ function bindEvents() { toggleTheme(); } + // Dashboard toggle + if (key === 'd') { + if (DOM.dashboardModal) { + if (DOM.dashboardModal.style.display === 'block') { + closeDashboard(); + } else { + openDashboard(); + } + } + } + // Watch Later toggle if (key === 'b') { if (AppState.current) { diff --git a/tests/manual_verify.js b/tests/manual_verify.js new file mode 100644 index 0000000..dd464d1 --- /dev/null +++ b/tests/manual_verify.js @@ -0,0 +1,53 @@ +const { chromium } = require('playwright-core'); + +(async () => { + const browser = await chromium.launch({ executablePath: '/usr/bin/google-chrome' }); + const page = await browser.newPage(); + await page.goto('http://localhost:8000'); + + console.log('Testing accessibility attributes...'); + const btn = await page.$('#dashboardBtn'); + const ariaControls = await btn.getAttribute('aria-controls'); + const ariaExpanded = await btn.getAttribute('aria-expanded'); + const title = await btn.getAttribute('title'); + + console.log('aria-controls:', ariaControls); + console.log('aria-expanded:', ariaExpanded); + console.log('title:', title); + + if (ariaControls !== 'dashboardModal' || ariaExpanded !== 'false' || title !== 'Dashboard (D)') { + console.error('FAILED accessibility check'); + process.exit(1); + } + + console.log('Testing keyboard shortcut D to open...'); + await page.keyboard.press('d'); + const ariaExpandedOpen = await btn.getAttribute('aria-expanded'); + console.log('aria-expanded (open):', ariaExpandedOpen); + if (ariaExpandedOpen !== 'true') { + console.error('FAILED keyboard open'); + process.exit(1); + } + + console.log('Testing keyboard shortcut D to close...'); + await page.keyboard.press('d'); + const ariaExpandedClosed = await btn.getAttribute('aria-expanded'); + console.log('aria-expanded (closed):', ariaExpandedClosed); + if (ariaExpandedClosed !== 'false') { + console.error('FAILED keyboard close'); + process.exit(1); + } + + console.log('Testing Escape key to close...'); + await page.keyboard.press('d'); + await page.keyboard.press('Escape'); + const ariaExpandedEsc = await btn.getAttribute('aria-expanded'); + console.log('aria-expanded (esc):', ariaExpandedEsc); + if (ariaExpandedEsc !== 'false') { + console.error('FAILED escape close'); + process.exit(1); + } + + console.log('ALL MANUAL CHECKS PASSED'); + await browser.close(); +})(); diff --git a/tests/manual_verify.py b/tests/manual_verify.py new file mode 100644 index 0000000..42d0794 --- /dev/null +++ b/tests/manual_verify.py @@ -0,0 +1,41 @@ +import asyncio +from playwright.async_api import async_playwright + +async def main(): + async with async_playwright() as p: + browser = await p.chromium.launch() + page = await browser.new_page() + page.on("console", lambda msg: print(f"BROWSER CONSOLE: {msg.text}")) + + await page.goto('http://localhost:8000') + await asyncio.sleep(2) + + btn = page.locator('#dashboardBtn') + + print('Testing click to open...') + await btn.click() + await asyncio.sleep(1) + aria_expanded_open = await btn.get_attribute('aria-expanded') + print(f'aria-expanded (open) after click: {aria_expanded_open}') + + if aria_expanded_open == 'true': + await page.screenshot(path="/home/jules/verification/dashboard_open_click.png") + print("Successfully opened dashboard via click") + else: + print("Failed to open dashboard via click") + + print('Testing keyboard shortcut d...') + # Close it first if open + if aria_expanded_open == 'true': + await page.keyboard.press('Escape') + await asyncio.sleep(1) + + await page.keyboard.press('d') + await asyncio.sleep(1) + aria_expanded_d = await btn.get_attribute('aria-expanded') + print(f'aria-expanded after d: {aria_expanded_d}') + + await browser.close() + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/tests/palette_verify.spec.js b/tests/palette_verify.spec.js new file mode 100644 index 0000000..970007d --- /dev/null +++ b/tests/palette_verify.spec.js @@ -0,0 +1,67 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Dashboard UX Enhancement', () => { + test.beforeEach(async ({ page }) => { + // Point to the local index.html using the local server + await page.goto('http://localhost:8000'); + }); + + test('Dashboard button has correct accessibility attributes', async ({ page }) => { + const dashboardBtn = page.locator('#dashboardBtn'); + await expect(dashboardBtn).toHaveAttribute('aria-controls', 'dashboardModal'); + await expect(dashboardBtn).toHaveAttribute('aria-expanded', 'false'); + await expect(dashboardBtn).toHaveAttribute('title', 'Dashboard (D)'); + }); + + test('Dashboard toggles via button click', async ({ page }) => { + const dashboardBtn = page.locator('#dashboardBtn'); + const dashboardModal = page.locator('#dashboardModal'); + + // Initially closed + await expect(dashboardModal).not.toBeVisible(); + await expect(dashboardBtn).toHaveAttribute('aria-expanded', 'false'); + + // Click to open + await dashboardBtn.click(); + + // Check if dashboard is visible. + // Note: Side panels might be display: block but off-screen. + // However, app.js sets display to 'block' and aria-hidden to 'false'. + await expect(dashboardModal).toHaveAttribute('aria-hidden', 'false'); + await expect(dashboardBtn).toHaveAttribute('aria-expanded', 'true'); + + // Close via close button in panel + const closeBtn = page.locator('#closeDashboard'); + await closeBtn.click(); + await expect(dashboardBtn).toHaveAttribute('aria-expanded', 'false'); + }); + + test('Dashboard toggles via keyboard shortcut D', async ({ page }) => { + const dashboardBtn = page.locator('#dashboardBtn'); + const dashboardModal = page.locator('#dashboardModal'); + + // Initially closed + await expect(dashboardBtn).toHaveAttribute('aria-expanded', 'false'); + + // Press 'd' to open + await page.keyboard.press('d'); + await expect(dashboardBtn).toHaveAttribute('aria-expanded', 'true'); + await expect(dashboardModal).toHaveAttribute('aria-hidden', 'false'); + + // Press 'd' again to close + await page.keyboard.press('d'); + await expect(dashboardBtn).toHaveAttribute('aria-expanded', 'false'); + }); + + test('Dashboard closes via Escape key', async ({ page }) => { + const dashboardBtn = page.locator('#dashboardBtn'); + + // Open it + await page.keyboard.press('d'); + await expect(dashboardBtn).toHaveAttribute('aria-expanded', 'true'); + + // Press Escape + await page.keyboard.press('Escape'); + await expect(dashboardBtn).toHaveAttribute('aria-expanded', 'false'); + }); +});