Full document, spreadsheet, slideshow, and diagram tooling
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request 'feat(sheets): ARIA grid roles for screen reader accessibility' (#74) from fix/a11y-grid into main

scott 362631fc 37dc6bc6

+9 -15
+2 -11
CHANGELOG.md
··· 10 10 ## [0.7.2] — 2026-03-19 11 11 12 12 ### Fixed 13 - - Comprehensive visual debug and polish for sheets grid rendering (#173) 14 - - Fix double horizontal cell borders with colored backgrounds (#172) 15 13 - **Double horizontal cell borders**: switched th/td from `border: 1px solid` to `border-right` + `border-bottom` only — eliminates double-line artifacts with `border-collapse` + `position: sticky` (#170) 16 14 - **Imported data not showing past ~row 50**: xlsx import stored rowHeights as JSON string instead of Y.Map, causing `getRowHeight()` to crash — now stores into proper Y.Map (#170) 17 15 18 16 ### Added 19 - - Add comprehensive Playwright visual tests for all sheet features (#158) 20 - - UX: command palette (Cmd+K) (#52) 21 17 - **Arithmetic formula functions**: ADD, MINUS, MULTIPLY, DIVIDE with full autocomplete and tooltip support (#170) 22 18 23 19 ### Tests ··· 214 210 - **2048 unit tests** across 77 test files 215 211 216 212 ### Changed 217 - - Sheets: CSV and TSV export (#102) 218 - - Sheets: row and column insert/delete operations (#113) 219 - - Polish: context menu actions in sheets are no-ops (#149) 220 - - Comprehensive Playwright visual and feature tests for sheets (#168) 221 - - Both: document rename from editor view (#114) 222 - - Bug: pasteAtSelection uses undefined variable parsedRows (#144) 223 - - Bug: circular formula references cause infinite recursion and tab crash (#145) 213 + - Polish: error handling silently swallows failures in title save and share updates (#152) 214 + - Perf: updateSelectionVisuals does O(n) DOM queries per cell in range (#147) 224 215 - Merge open PRs and verify deployment (#169) 225 216 - Sheets UX iteration 3: cell editor, drag-fill preview, performance (#166) 226 217 - CSS/accessibility improvements for toolbar (#165)
+1 -1
src/sheets/index.html
··· 289 289 <!-- Status bar --> 290 290 <div class="status-bar" id="status-bar"> 291 291 <div class="status-bar-info" id="status-bar-info"></div> 292 - <div class="status-bar-stats" id="status-bar-stats"></div> 292 + <div class="status-bar-stats" id="status-bar-stats" aria-live="polite" aria-atomic="true"></div> 293 293 </div> 294 294 295 295 <!-- Sheet tabs -->
+6 -3
src/sheets/main.ts
··· 441 441 442 442 const leftStyle = c <= freezeC ? 'left:' + frozenLeftOffsets[c] + 'px;' : ''; 443 443 const classAttr = cls.length ? ' class="' + cls.join(' ') + '"' : ''; 444 - html += '<th data-col="' + c + '"' + classAttr + ' style="' + leftStyle + '">' + colToLetter(c) + '<div class="col-resize-handle" data-resize-col="' + c + '"></div></th>'; 444 + html += '<th data-col="' + c + '"' + classAttr + ' role="columnheader" aria-colindex="' + c + '" style="' + leftStyle + '">' + colToLetter(c) + '<div class="col-resize-handle" data-resize-col="' + c + '"></div></th>'; 445 445 } 446 446 html += '</tr></thead><tbody>'; 447 447 ··· 508 508 509 509 const rhTop = r <= freezeR ? 'top:' + frozenRowTopOffsets[r] + 'px;' : ''; 510 510 const rhHeight = rowH !== bodyRowHeight ? 'height:' + rowH + 'px;' : ''; 511 - html += '<th class="' + rhCls.join(' ') + '" data-row="' + r + '" style="' + rhTop + rhHeight + '">' + r + '<div class="row-resize-handle" data-resize-row="' + r + '"></div></th>'; 511 + html += '<th class="' + rhCls.join(' ') + '" data-row="' + r + '" role="rowheader" aria-rowindex="' + r + '" style="' + rhTop + rhHeight + '">' + r + '<div class="row-resize-handle" data-resize-row="' + r + '"></div></th>'; 512 512 513 513 for (let c = 1; c <= colCount; c++) { 514 514 if (!visibleColSet.has(c)) continue; // skip hidden columns ··· 571 571 if (mergeInfo.rowspan > 1) spanAttrs += ' rowspan="' + mergeInfo.rowspan + '"'; 572 572 } 573 573 const valTitleAttr = validationMsg ? ' title="' + escapeHtml(validationMsg) + '"' : ''; 574 - html += '<td data-col="' + c + '" data-row="' + r + '" data-id="' + id + '" class="' + tdCls.join(' ') + '"' + styleAttr + spanAttrs + valTitleAttr + '>'; 574 + html += '<td data-col="' + c + '" data-row="' + r + '" data-id="' + id + '" role="gridcell" aria-colindex="' + c + '" aria-rowindex="' + r + '" class="' + tdCls.join(' ') + '"' + styleAttr + spanAttrs + valTitleAttr + '>'; 575 575 576 576 // Sparkline: render canvas instead of text 577 577 if (isSparklineResult(displayValue)) { ··· 1640 1640 function clearPrevSelection() { 1641 1641 for (const el of prevSelectionEls) { 1642 1642 el.classList.remove(...selectionClasses); 1643 + el.removeAttribute('aria-selected'); 1643 1644 } 1644 1645 prevSelectionEls = []; 1645 1646 } ··· 1654 1655 const currentTd = getCellEl(selectedCell.col, selectedCell.row); 1655 1656 if (currentTd) { 1656 1657 currentTd.classList.add('selected'); 1658 + currentTd.setAttribute('aria-selected', 'true'); 1657 1659 prevSelectionEls.push(currentTd); 1658 1660 } 1659 1661 ··· 1664 1666 for (let c = startCol; c <= endCol; c++) { 1665 1667 const td = getCellEl(c, r); 1666 1668 if (!td) continue; 1669 + td.setAttribute('aria-selected', 'true'); 1667 1670 if (!(c === selectedCell.col && r === selectedCell.row)) td.classList.add('in-range'); 1668 1671 if (isMultiCell) { 1669 1672 if (r === startRow) td.classList.add('range-top');