feat: tableur mobile — barres repliées par défaut, bascule unique #189
Sous 768 px, la barre de menus, le ruban et le groupe Recherche sont repliés par défaut : la barre de formule (fx), les onglets et Enregistrer restent visibles et la grille récupère la hauteur d'écran. Un bouton unique (#xlsx-tools-toggle) dans la barre des feuilles déplie/replie l'ensemble (aria-expanded, état actif) ; desktop inchangé. Ergonomie tactile : cibles ≥ 44 px, onglets en défilement horizontal, champs de la barre de formule en 16 px (anti-zoom iOS), barre de formule en 2 lignes quand les outils sont ouverts. Tests : JSDOM xlsx-viewer.test.mjs (+2, 165/165), E2E mobile (repli par défaut, dépliage, no-overflow ruban, cibles 44 px) ; openXlsx() déplie automatiquement ≤ 768 px pour les tests desktop.
This commit is contained in:
@@ -72,6 +72,14 @@ async function openXlsx(page, file) {
|
||||
}
|
||||
await treeItem.dblclick({ timeout: 5000 });
|
||||
await expect(page.locator('#content-area .xlsx-table')).toBeVisible({ timeout: 15000 });
|
||||
// #189 — sous 768px la barre de menus et le ruban sont repliés par défaut.
|
||||
// Les tests « desktop » qui les manipulent les déplient d'abord ; le test
|
||||
// mobile dédié ouvre lui-même la feuille pour vérifier l'état replié.
|
||||
const toggle = page.locator('#xlsx-tools-toggle');
|
||||
if (await toggle.isVisible().catch(() => false)) {
|
||||
await toggle.click();
|
||||
await expect(page.locator('#content-area .xlsx-menubar')).toBeVisible();
|
||||
}
|
||||
}
|
||||
|
||||
test.describe('Excel viewer — garde-fous d\'écriture et valeurs calculées (#153)', () => {
|
||||
@@ -513,12 +521,27 @@ test.describe('Excel viewer — menus à icônes et fermeture au focus (#179)',
|
||||
await expect(page.locator('#xlsx-minibar')).toBeVisible();
|
||||
});
|
||||
|
||||
test('mobile : ruban sans débordement et cibles tactiles 44px', async ({ page, viewport }) => {
|
||||
test('mobile : barres repliées par défaut, ruban sans débordement et cibles 44px (#189)', async ({ page, viewport }) => {
|
||||
test.skip((viewport?.width ?? 9999) > 768, 'Mobile viewport required');
|
||||
await login(page);
|
||||
await page.evaluate(() => window.TabManager.open('TestVault', FIXTURE));
|
||||
await page.evaluate((f) => window.TabManager.open('TestVault', f), FIXTURE);
|
||||
await expect(page.locator('#content-area .xlsx-table')).toBeVisible({ timeout: 15000 });
|
||||
|
||||
// #189 — par défaut la barre de menus et le ruban sont repliés : la grille
|
||||
// garde la hauteur d'écran. La barre de formule, elle, reste visible.
|
||||
await expect(page.locator('#content-area .xlsx-menubar')).toBeHidden();
|
||||
await expect(page.locator('#content-area .xlsx-cmdbar')).toBeHidden();
|
||||
await expect(page.locator('#content-area .xlsx-formula-bar')).toBeVisible();
|
||||
await expect(page.locator('#content-area .xlsx-sheetbar')).toBeVisible();
|
||||
const toggle = page.locator('#xlsx-tools-toggle');
|
||||
await expect(toggle).toBeVisible();
|
||||
|
||||
// Un clic déplie menus + ruban (état annoncé par aria-expanded).
|
||||
await toggle.click();
|
||||
await expect(page.locator('#content-area .xlsx-menubar')).toBeVisible();
|
||||
await expect(page.locator('#content-area .xlsx-cmdbar')).toBeVisible();
|
||||
await expect(toggle).toHaveAttribute('aria-expanded', 'true');
|
||||
|
||||
// Le ruban s'enroule au lieu de déborder horizontalement.
|
||||
const overflow = await page.evaluate(() => {
|
||||
const bar = document.querySelector('#content-area .xlsx-cmdbar');
|
||||
@@ -526,6 +549,17 @@ test.describe('Excel viewer — menus à icônes et fermeture au focus (#179)',
|
||||
});
|
||||
expect(overflow).toBeLessThanOrEqual(1);
|
||||
|
||||
// Cibles tactiles : le basculeur et chaque commande du ruban font 44px.
|
||||
const toggleBox = await toggle.boundingBox();
|
||||
expect(toggleBox.height).toBeGreaterThanOrEqual(44);
|
||||
const ribbonBtnBox = await page.locator('#xs-b').boundingBox();
|
||||
expect(ribbonBtnBox.height).toBeGreaterThanOrEqual(44);
|
||||
|
||||
// Repli : la grille retrouve toute sa hauteur.
|
||||
await toggle.click();
|
||||
await expect(page.locator('#content-area .xlsx-menubar')).toBeHidden();
|
||||
await expect(toggle).toHaveAttribute('aria-expanded', 'false');
|
||||
|
||||
// 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');
|
||||
|
||||
@@ -2775,6 +2775,54 @@ await test("a read-only workbook keeps the view menus but drops the editing ones
|
||||
assert.ok(area.querySelector('[data-xlsx-action="toggle-grid"]'), "Gridlines stay");
|
||||
});
|
||||
|
||||
// ── #189 — mise en page mobile : barres repliées derrière un basculeur ──────
|
||||
|
||||
await test("the sheet strip carries the tools toggle that flips the toolbar (#189)", () => {
|
||||
const area = mountGrid();
|
||||
const strip = area.querySelector(".xlsx-sheetbar");
|
||||
const toggle = area.querySelector("#xlsx-tools-toggle");
|
||||
assert.ok(toggle, "toggle rendered");
|
||||
assert.equal(toggle.closest(".xlsx-sheetbar"), strip, "it lives in the sheet strip");
|
||||
assert.equal(toggle.getAttribute("aria-expanded"), "false", "starts collapsed");
|
||||
const toolbar = area.querySelector(".xlsx-toolbar");
|
||||
assert.ok(!toolbar.classList.contains("xlsx-tools-open"), "toolbar starts closed");
|
||||
toggle.click();
|
||||
assert.ok(toolbar.classList.contains("xlsx-tools-open"), "one click opens the tools");
|
||||
assert.equal(toggle.getAttribute("aria-expanded"), "true");
|
||||
assert.ok(toggle.classList.contains("active"), "the toggle shows its own state");
|
||||
toggle.click();
|
||||
assert.ok(!toolbar.classList.contains("xlsx-tools-open"), "a second click closes them");
|
||||
assert.equal(toggle.getAttribute("aria-expanded"), "false");
|
||||
});
|
||||
|
||||
await test("style.css hides menus/ribbon/find by default under 768px and reveals them on .xlsx-tools-open (#189)", () => {
|
||||
const css = readFileSync(path.join(REPO_ROOT, "frontend", "style.css"), "utf8");
|
||||
// The desktop default: the toggle itself is out of the way.
|
||||
assert.match(css, /\.xlsx-tools-toggle\s*\{\s*display:\s*none/, "toggle hidden on desktop");
|
||||
// Mobile block: collapsed by default… (CRLF-safe slice, from the #189
|
||||
// comment up to the next top-level rule).
|
||||
const from = css.indexOf("/* #189");
|
||||
assert.ok(from > 0, "the #189 mobile block exists after the coarse-pointer rules");
|
||||
const to = css.indexOf(".xlsx-context-item", from);
|
||||
assert.ok(to > from, "the #189 block is followed by the next rule");
|
||||
const block = css.slice(from, to);
|
||||
for (const cls of [".xlsx-menubar", ".xlsx-cmdbar", ".xlsx-find-group"]) {
|
||||
const hidden = new RegExp(cls.replace(".", "\\.") + "[^}]*display:\\s*none").test(block)
|
||||
|| new RegExp(cls.replace(".", "\\.") + ",[\\s\\S]{0,120}?display:\\s*none").test(block);
|
||||
assert.ok(hidden, `${cls} hidden by default on mobile`);
|
||||
assert.ok(
|
||||
block.includes(`.xlsx-toolbar.xlsx-tools-open ${cls}`),
|
||||
`${cls} revealed by .xlsx-tools-open`,
|
||||
);
|
||||
}
|
||||
// The formula bar always stays, and the open state can wrap it (find group).
|
||||
assert.ok(block.includes(".xlsx-toolbar.xlsx-tools-open .xlsx-formula-bar"), "formula bar wraps when open");
|
||||
// Touch: 44px targets, tabs on one scrollable row, 16px inputs (no iOS zoom).
|
||||
assert.match(block, /min-width:\s*44px/, "44px touch targets");
|
||||
assert.match(block, /\.xlsx-sheetbar \.xlsx-tabs\s*\{[^}]*overflow-x:\s*auto/, "tabs scroll horizontally");
|
||||
assert.match(block, /font-size:\s*16px/, "inputs at 16px avoid the iOS focus zoom");
|
||||
});
|
||||
|
||||
// ── Report ──────────────────────────────────────────────────────────────────
|
||||
console.log(`\n${passCount}/${testCount} tests passed\n`);
|
||||
process.exit(passCount === testCount ? 0 : 1);
|
||||
|
||||
Reference in New Issue
Block a user