feat: menus tableur a icones, fermeture au focus et polish mobile #179
This commit is contained in:
@@ -20,6 +20,10 @@
|
||||
* - `GET …/xlsx/sheet?offset=500` serves the rows the caps used to hide,
|
||||
* with the real A1 coordinates (#153 A9).
|
||||
*
|
||||
* Third block (#179) : menus carry one Lucide icon per entry and close on
|
||||
* focus loss (outside click, Escape); mobile viewport checks the ribbon
|
||||
* wrapping and 44px touch targets.
|
||||
*
|
||||
* The fixture is restored byte-for-byte in `afterAll` so a local run never
|
||||
* dirties the working copy.
|
||||
*
|
||||
@@ -314,3 +318,87 @@ test.describe('Excel viewer — presse-papiers, clavier et zone Nom (#156 P1)',
|
||||
expect(await textOf('C10')).toBe(src.c10);
|
||||
});
|
||||
});
|
||||
|
||||
// ── #179 — icônes des menus, fermeture au focus, polish mobile ──────────────
|
||||
|
||||
test.describe('Excel viewer — menus à icônes et fermeture au focus (#179)', () => {
|
||||
test('chaque entrée du menu contextuel porte une icône', async ({ page }) => {
|
||||
await login(page);
|
||||
await openFixture(page);
|
||||
|
||||
await page.locator('#content-area td[data-cell="A1"]').click({ button: 'right' });
|
||||
const menu = page.locator('.xlsx-context-menu');
|
||||
await expect(menu).toBeVisible();
|
||||
// Icône rendue par Lucide (svg) ou à rendre (<i data-lucide>) si le CDN
|
||||
// est injoignable — dans les deux cas l'entrée est iconisée.
|
||||
for (const label of ['Couper', 'Copier', 'Coller']) {
|
||||
const entry = menu.locator('.xlsx-context-item', { hasText: label });
|
||||
await expect(entry.locator('i[data-lucide], svg')).toHaveCount(1);
|
||||
}
|
||||
await expect(menu.locator('.xlsx-context-item i[data-lucide="trash-2"], .xlsx-context-item svg.lucide-trash-2')).not.toHaveCount(0);
|
||||
});
|
||||
|
||||
test('le menu contextuel se ferme au clic extérieur et à Échap', async ({ page }) => {
|
||||
await login(page);
|
||||
await openFixture(page);
|
||||
|
||||
const cell = page.locator('#content-area td[data-cell="A1"]');
|
||||
await cell.click({ button: 'right' });
|
||||
await expect(page.locator('.xlsx-context-menu')).toBeVisible();
|
||||
await page.locator('#content-area .xlsx-table').click({ position: { x: 5, y: 5 } });
|
||||
await expect(page.locator('.xlsx-context-menu')).toHaveCount(0);
|
||||
|
||||
await cell.click({ button: 'right' });
|
||||
await expect(page.locator('.xlsx-context-menu')).toBeVisible();
|
||||
await page.keyboard.press('Escape');
|
||||
await expect(page.locator('.xlsx-context-menu')).toHaveCount(0);
|
||||
});
|
||||
|
||||
test('le menu Structure se ferme quand le focus le quitte', async ({ page }) => {
|
||||
await login(page);
|
||||
await openFixture(page);
|
||||
|
||||
await page.locator('#xlsx-structure-btn').click();
|
||||
const menu = page.locator('.xlsx-structure-menu');
|
||||
await expect(menu).toBeVisible();
|
||||
await expect(menu.locator('.xlsx-structure-item i[data-lucide], .xlsx-structure-item svg').first()).toBeAttached();
|
||||
// Clic dans la grille = le focus quitte le menu → fermeture.
|
||||
await page.locator('#content-area td[data-cell="A1"]').click();
|
||||
await expect(page.locator('.xlsx-structure-menu')).toHaveCount(0);
|
||||
await expect(page.locator('#xlsx-structure-btn')).toHaveAttribute('aria-expanded', 'false');
|
||||
});
|
||||
|
||||
test('le menu Export se ferme au clic extérieur', async ({ page }) => {
|
||||
await login(page);
|
||||
await openFixture(page);
|
||||
|
||||
await page.locator('#xlsx-export-btn').click();
|
||||
const menu = page.locator('#xlsx-export-menu');
|
||||
await expect(menu).toBeVisible();
|
||||
await page.locator('#content-area td[data-cell="A1"]').click();
|
||||
await expect(menu).toBeHidden();
|
||||
});
|
||||
|
||||
test('mobile : ruban sans débordement et cibles tactiles 44px', async ({ page, viewport }) => {
|
||||
test.skip((viewport?.width ?? 9999) > 768, 'Mobile viewport required');
|
||||
await login(page);
|
||||
await page.evaluate(() => window.TabManager.open('TestVault', FIXTURE));
|
||||
await expect(page.locator('#content-area .xlsx-table')).toBeVisible({ timeout: 15000 });
|
||||
|
||||
// Le ruban s'enroule au lieu de déborder horizontalement.
|
||||
const overflow = await page.evaluate(() => {
|
||||
const bar = document.querySelector('#content-area .xlsx-cmdbar');
|
||||
return bar ? bar.scrollWidth - bar.clientWidth : -1;
|
||||
});
|
||||
expect(overflow).toBeLessThanOrEqual(1);
|
||||
|
||||
// Menu contextuel (clic droit émulé) : tient dans le viewport, entrées ≥ 44px.
|
||||
await page.locator('#content-area td[data-cell="A1"]').click({ button: 'right' });
|
||||
const menu = page.locator('.xlsx-context-menu');
|
||||
await expect(menu).toBeVisible();
|
||||
const box = await menu.boundingBox();
|
||||
expect(box.width).toBeLessThanOrEqual(viewport.width - 8);
|
||||
const itemBox = await menu.locator('.xlsx-context-item').first().boundingBox();
|
||||
expect(itemBox.height).toBeGreaterThanOrEqual(44);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user