fix(preview): bound rich-text run walks, fold format notices, match Excel number display

- richTextPrefix visits at most maxCellTextChars + 1 runs. An empty run adds
  no text, so a length check alone walked every run of a shared string for
  every cell referencing it, on every tile.
- Two or more unsupported number format warnings in a tile fold into one
  "N unsupported number formats" entry, and the notice bar has a max-height
  and scrolls.
- General-format and unsupported-format numbers render at 15 significant
  digits, as Excel does (0.1+0.2 shows 0.3).
- TIME_FORMAT accepts a trailing AM/PM, so h:mm AM/PM renders as 2:30 PM
  instead of falling back to a date.
- SPREADSHEET_ASSET_VERSION refreshed.
This commit is contained in:
Aamer Akhter
2026-10-05 09:51:04 -04:00
parent 0ae5ce017a
commit 51b6be3e7a
9 changed files with 142 additions and 11 deletions
+22
View File
@@ -986,3 +986,25 @@ describe('spreadsheet preview worker: number formats ExcelJS would rescan', () =
expect(tile.warnings).toEqual([`Unsupported number format: ${code}`]);
}, 60_000);
});
describe('spreadsheet preview worker: the notice bar stays bounded', () => {
// Each distinct unsupported code was its own notice entry; a 40 x 20 sheet
// with a code per cell grew the bar to thousands of pixels.
it('folds many distinct unsupported number formats in one tile into one counted warning', async () => {
const workbook = new ExcelJS.Workbook();
const sheet = workbook.addWorksheet('Formats');
for (let row = 1; row <= 40; row += 1) {
for (let col = 1; col <= 20; col += 1) {
const cell = sheet.getCell(row, col);
cell.value = row * col + 0.5;
cell.numFmt = `"c${row}-${col}"0.00E+00`;
}
}
const harness = createHarness();
const metadata = await loadMetadata(harness, await writeWorkbook(workbook));
const tile = await requestTile(harness, metadata.sheets[0].id, { r1: 1, c1: 1, r2: 40, c2: 20 });
expect(tile.type, JSON.stringify(tile).slice(0, 200)).toBe('tile');
expect(tile.cells).toHaveLength(800);
expect(tile.warnings).toEqual(['800 unsupported number formats']);
}, 60_000);
});