feat: undo/redo, chargement IntersectionObserver et ARIA pour la visionneuse XLSX #154
CI / lint (push) Successful in 2m30s
CI / security (push) Failing after 1m45s
CI / build (push) Canceled after 0s
CI / e2e (push) Canceled after 0s
CI / test (push) Canceled after 19s

This commit is contained in:
2026-09-29 15:01:00 -04:00
parent e94af0369b
commit 72da123a51
15 changed files with 189 additions and 28 deletions
+37
View File
@@ -842,6 +842,7 @@ await test("a read-only workbook shows the read-only pill and offers no editing"
assert.ok(pill.textContent.includes(FR["xlsx.readonly_badge"]), pill.textContent);
assert.equal(area.querySelector("#xlsx-tab-add"), null, "no + for read-only");
assert.equal(area.querySelector("#xlsx-structure-btn"), null, "no structure for read-only");
assert.equal(area.querySelector("#xlsx-undo-btn"), null, "no history for read-only");
assert.equal(area.querySelector('td[data-cell="A1"]').getAttribute("contenteditable"), null, "cells are not editable");
});
@@ -870,6 +871,42 @@ await test("the command bar exposes grouped actions with a primary save", () =>
assert.ok(area.querySelector(".xlsx-cmd-group[data-group='file']"));
});
// ── #154-A4 — undo/redo, ARIA ───────────────────────────────────────────────
await test("undo/redo restores cell edits from the command stack", () => {
const area = mount();
const td = area.querySelector('td[data-cell="A1"]');
const undoBtn = area.querySelector("#xlsx-undo-btn");
const redoBtn = area.querySelector("#xlsx-redo-btn");
assert.equal(undoBtn.disabled, true, "nothing to undo yet");
td.focus();
editCell(area, "A1", "42");
td.dispatchEvent(new w.Event("blur"));
assert.equal(undoBtn.disabled, false, "undo becomes available after a committed edit");
undoBtn.click();
assert.equal(td.textContent, "100", "undo restores the original value");
assert.equal(area.querySelectorAll("td.xlsx-dirty").length, 0, "the cell is clean again");
assert.equal(redoBtn.disabled, false, "redo becomes available");
redoBtn.click();
assert.equal(td.textContent, "42", "redo reapplies the edit");
assert.equal(area.querySelectorAll("td.xlsx-dirty").length, 1, "the edit is dirty again");
});
await test("a no-op undo/redo pair leaves the buttons disabled", () => {
const area = mount();
assert.equal(area.querySelector("#xlsx-undo-btn").disabled, true);
assert.equal(area.querySelector("#xlsx-redo-btn").disabled, true);
});
await test("the grid exposes ARIA grid semantics", () => {
const area = mount();
const table = area.querySelector(".xlsx-table");
assert.equal(table.getAttribute("role"), "grid");
assert.equal(area.querySelector(".xlsx-table thead th").getAttribute("role"), "columnheader");
assert.equal(area.querySelector('td[data-cell="A1"]').getAttribute("role"), "gridcell");
assert.equal(area.querySelector("th.xlsx-rownum").getAttribute("role"), "rowheader");
});
// ── Report ──────────────────────────────────────────────────────────────────
console.log(`\n${passCount}/${testCount} tests passed\n`);
process.exit(passCount === testCount ? 0 : 1);