Upgrades handsontable and @handsontable/angular-wrapper from 18.0.0 to 18.1.1, plus the two repo-owned places that pin the version: the excludePackages allow-list in client/licenseChecker.js and the generated client/src/_hot-icons.scss.
Base is feat/export-dc-library-ddl (#333) rather than main: the Check audit gate currently fails on main for the advisories #333 fixes, so this branch needs that base to go green. Retarget to main once #333 lands.
Why
1. It fixes two Handsontable bugs that this repo's own specs had pinned. Both were "demonstrate the failure mode" tests asserting that the bug happens. The upgrade removes the bug, so those tests now pin the fixed behaviour instead.
Sheet size limit exceeded (upstream #10672, fixed in 18.1.0). The formulas plugin pushed the grid's maxRows into the HyperFormula engine as its sheet-size limit, so a licensing cap below an existing table's row count made the engine reject the whole table. initSetup works around this with Math.max(dataSource.length, editor_rows_allowed); 18.1 no longer couples the two. See characterColumnFormula.integration.spec.ts.
Formula corruption when updateSettings runs while a sort is active (fixed in 18.1.0). Calling hot.updateSettings() - even with a bare {} - while sorted turned formula cells into #REF!. DC's updateSettingsSortSafe clears and restores the sort around every call; 18.1 keeps the formulas plugin's index translations across the update. See sortedGridRowSync.integration.spec.ts.
DC's own workarounds are still in place and still correct. They are not removed here - that is a separate decision.
2. It fixes a crash in the filters plugin on the path DC actually uses (upstream #13480, fixed in 18.1.1). Editing a cell in a filtered column, or replacing the data with a filter active, after updateSettings() was called with the filters option threw. The changelog notes that the React and Angular wrappers call updateSettings on every update, and that is what DC's grids do: filters: true in viewer.component.ts and viewboxes.component.ts, with updateSettings on refetch and on the labels toggle.
3. Large-dataset performance. 18.1.0 is mostly a performance release: cell metadata is released for rows scrolled out of the viewport, index translation and the hooks dispatch were reworked, the grid renders in a single pass, sorting/filtering/hiding and bulk operations were all reworked, and cell-meta storage no longer caches settings for every visited cell.
18.1.0 also adds selectionHandles / moveCells, the intl-datetime cell type, preserveNumericLiteral, colorScheme / density, formulas.hyperlinks and a public NestedRows API, and hardens pasted HTML. All opt-in, none enabled in DC - so there is nothing new to screenshot.
Measured performance
Local synthetic benchmark: the same page, the same Chrome build and the same dataset (50,000 rows x 12 columns, best of 3 runs), loading each version's own handsontable.full.min.js and timing each operation with a forced hot.render() inside the timed region.
operation (50k rows)
18.0.0
18.1.1
change
initial render
1,317 ms
1,232 ms
-6%
sort, one column
490 ms
21 ms
-96%
filter, contains
205 ms
148 ms
-28%
loadData with a filter active
1,699 ms
1,265 ms
-26%
updateSettings
1,042 ms
588 ms
-44%
edit a cell in a filtered column
1,027 ms
42 ms
-96%
200 scattered cell edits
108,519 ms
7,842 ms
-93%
clear the filter
1,001 ms
584 ms
-42%
scroll to the last row
540 ms
38 ms
-93%
scroll back to the first row
549 ms
38 ms
-93%
Synthetic, single machine, not a claim about any particular deployment - but consistent with the changelog's own list of what changed.
Measured performance, end to end
The same upgrade, but measured through the app rather than the library: mocked estate (SASjs Server JS mocks), the client served by ng serve from this branch, driven by Cypress/Electron, timed from the click that starts the fetch to the grid rendering its first row - so the service call, the transfer, the parse and the grid build are all inside the measurement.
Two shapes: narrow, the MPE_X_TEST column set (9 columns), and wide, WIDEBOY as built by sas/sasjs/tests/testsetup.sas (ROW_ID plus char1..char500 ($20) and num1..num500 - 1,001 columns).
Payloads, measured directly against the mock services:
table
rows
cols
VIEW (viewdata)
EDIT (getdata)
narrow_100
100
9
24,137
28,196
narrow_500
500
9
109,518
123,177
narrow_1000
1,000
9
216,242
241,899
narrow_2000
2,000
9
430,695
480,352
wideboy_100
100
1,001
2,474,981
2,490,971
wideboy_500
500
1,001
11,680,863
11,311,253
wideboy
1,000
1,001
23,188,366
22,336,759
~216 bytes/row at 9 columns; ~23.2 KB/row at 1,001 columns.
Load time (ms):
table
mode
rows
cols
18.0.0
18.1.1
change
narrow_100
VIEW
100
9
1,167
1,289
+10%
narrow_100
EDIT
100
9
4,776
4,193
-12%
narrow_500
VIEW
500
9
1,551
954
-38%
narrow_500
EDIT
500
9
7,020
4,809
-31%
narrow_1000
VIEW
1,000
9
1,452
1,036
-29%
narrow_1000
EDIT
1,000
9
8,242
5,265
-36%
narrow_2000
VIEW
2,000
9
1,552
1,048
-32%
narrow_2000
EDIT
2,000
9
8,779
6,234
-29%
wideboy_100
VIEW
100
1,001
2,217
1,491
-33%
wideboy_100
EDIT
100
1,001
16,604
17,267
+4%
wideboy_500
VIEW
500
1,001
5,715
2,875
-50%
wideboy_500
EDIT
500
1,001
55,108
52,921
-4%
wideboy
VIEW
1,000
1,001
9,448
7,890
-16%
wideboy
EDIT
1,000
1,001
131,046
138,013
+5%
Reading:
VIEW is cheap, and got cheaper. Narrow stays at ~1.0-1.3 s from 100 to 2,000 rows - the payload is not the constraint at these sizes. Wide scales with the payload: 1.5 s at 2.4 MB, 7.9 s at 22 MB.
EDIT carries a large fixed cost (~4.2 s at 100 rows narrow) and grows slowly with rows - 6.2 s at 2,000 rows narrow. The upgrade takes ~30% off that.
Wide EDIT is pathological, and the upgrade does not touch it. 17 s at 100 rows, 53 s at 500, 138 s at 1,000 - all within noise of 18.0.0. That cost is DC's own per-cell work (the editor's cells function, validators, renderers) over up to a million cells, not Handsontable's render, so it is not something a grid upgrade can fix. It is why the EDIT screen gained a cell limit rather than a bigger row limit - that change is #335.
Caveat: dev-mode ng serve bundle (unminified, ~25 MB) against a JS mock, so the absolute times are pessimistic compared to a production build on a real estate. The shape of the curve and the 18.0.0 -> 18.1.1 deltas are the reliable part.
Changes
client/package.json and client/package-lock.json - handsontable and @handsontable/angular-wrapper18.0.0 -> 18.1.1. The lock diff is only the two entries: neither package has dependencies, so nothing else in the tree moves.
client/licenseChecker.js - adds @handsontable/angular-wrapper@18.1.1 and handsontable@18.1.1 to excludePackages. The list already carries 16.0.1 / 17.1.0 / 18.0.0, and the check fails the build on the library's non-standard licence string, so the new version has to be listed. Without this, npm run build stops at the license-checker step.
client/src/_hot-icons.scss - regenerated by scripts/gen-hot-icons.mjs on install. It drops the two Pikaday rules (.pika-single .pika-prev / .pika-next) because 18.1 removed the leftover Pikaday styles from the themes. Committed because the file is tracked.
client/src/app/shared/dc-validator/tests/dc-validator.spec.ts - 18.1 tightened ColumnSettings['validator'] to its real type. 18.0 declared ColumnSettings as Omit<GridSettings, 'data'> over a GridSettings that carries [key: string]: any, which collapsed validator to any; 18.1 uses RemoveIndexSignature<GridSettings>, so validator is now string | RegExp | function and rule.validator!.call(...) no longer type-checks. The spec narrows it through a small validatorOf() helper, mirroring the guard the production code already uses.
the two integration specs above.
Testing
npm ci clean; npm ls handsontable @handsontable/angular-wrapper -> both 18.1.1
npm audit --omit=dev -> found 0 vulnerabilities
npm run build (production) passes
npx ng test --no-progress --watch=false --browsers ChromeHeadlessCI -> 548 SUCCESS
Cypress suite against the mocked estate -> 135/135 passing (csv-limited.cy.ts is the free-tier spec: it passes on a fresh estate, and locally it needed the licence state reset first because a screenshot run had already applied a key)
npx prettier --check clean on the changed files
Screenshots
The grid surfaces on 18.1.1 - mocked estate, licence applied, no licence banner:
Viewer grid (DC_JSLIB.MPE_X_TEST, 7 rows x 9 cols)
Viewer filter modal
Editor grid
## What
Upgrades `handsontable` and `@handsontable/angular-wrapper` from `18.0.0` to `18.1.1`, plus the two repo-owned places that pin the version: the `excludePackages` allow-list in `client/licenseChecker.js` and the generated `client/src/_hot-icons.scss`.
Base is `feat/export-dc-library-ddl` (#333) rather than `main`: the `Check audit` gate currently fails on `main` for the advisories #333 fixes, so this branch needs that base to go green. Retarget to `main` once #333 lands.
## Why
**1. It fixes two Handsontable bugs that this repo's own specs had pinned.** Both were "demonstrate the failure mode" tests asserting that the bug happens. The upgrade removes the bug, so those tests now pin the fixed behaviour instead.
- `Sheet size limit exceeded` (upstream #10672, fixed in 18.1.0). The formulas plugin pushed the grid's `maxRows` into the HyperFormula engine as its sheet-size limit, so a licensing cap below an existing table's row count made the engine reject the whole table. `initSetup` works around this with `Math.max(dataSource.length, editor_rows_allowed)`; 18.1 no longer couples the two. See `characterColumnFormula.integration.spec.ts`.
- Formula corruption when `updateSettings` runs while a sort is active (fixed in 18.1.0). Calling `hot.updateSettings()` - even with a bare `{}` - while sorted turned formula cells into `#REF!`. DC's `updateSettingsSortSafe` clears and restores the sort around every call; 18.1 keeps the formulas plugin's index translations across the update. See `sortedGridRowSync.integration.spec.ts`.
DC's own workarounds are still in place and still correct. They are not removed here - that is a separate decision.
**2. It fixes a crash in the filters plugin on the path DC actually uses** (upstream #13480, fixed in 18.1.1). Editing a cell in a filtered column, or replacing the data with a filter active, after `updateSettings()` was called with the `filters` option threw. The changelog notes that the React and Angular wrappers call `updateSettings` on every update, and that is what DC's grids do: `filters: true` in `viewer.component.ts` and `viewboxes.component.ts`, with `updateSettings` on refetch and on the labels toggle.
**3. Large-dataset performance.** 18.1.0 is mostly a performance release: cell metadata is released for rows scrolled out of the viewport, index translation and the hooks dispatch were reworked, the grid renders in a single pass, sorting/filtering/hiding and bulk operations were all reworked, and cell-meta storage no longer caches settings for every visited cell.
18.1.0 also adds `selectionHandles` / `moveCells`, the `intl-datetime` cell type, `preserveNumericLiteral`, `colorScheme` / `density`, `formulas.hyperlinks` and a public NestedRows API, and hardens pasted HTML. All opt-in, none enabled in DC - so there is nothing new to screenshot.
## Measured performance
Local synthetic benchmark: the same page, the same Chrome build and the same dataset (50,000 rows x 12 columns, best of 3 runs), loading each version's own `handsontable.full.min.js` and timing each operation with a forced `hot.render()` inside the timed region.
| operation (50k rows) | 18.0.0 | 18.1.1 | change |
|---|---|---|---|
| initial render | 1,317 ms | 1,232 ms | -6% |
| sort, one column | 490 ms | 21 ms | **-96%** |
| filter, contains | 205 ms | 148 ms | -28% |
| loadData with a filter active | 1,699 ms | 1,265 ms | -26% |
| updateSettings | 1,042 ms | 588 ms | -44% |
| edit a cell in a filtered column | 1,027 ms | 42 ms | **-96%** |
| 200 scattered cell edits | 108,519 ms | 7,842 ms | **-93%** |
| clear the filter | 1,001 ms | 584 ms | -42% |
| scroll to the last row | 540 ms | 38 ms | **-93%** |
| scroll back to the first row | 549 ms | 38 ms | **-93%** |
Synthetic, single machine, not a claim about any particular deployment - but consistent with the changelog's own list of what changed.
## Measured performance, end to end
The same upgrade, but measured through the app rather than the library: mocked estate (SASjs Server JS mocks), the client served by `ng serve` from this branch, driven by Cypress/Electron, timed from the click that starts the fetch to the grid rendering its first row - so the service call, the transfer, the parse and the grid build are all inside the measurement.
Two shapes: **narrow**, the `MPE_X_TEST` column set (9 columns), and **wide**, `WIDEBOY` as built by `sas/sasjs/tests/testsetup.sas` (`ROW_ID` plus `char1..char500` ($20) and `num1..num500` - 1,001 columns).
Payloads, measured directly against the mock services:
| table | rows | cols | VIEW (viewdata) | EDIT (getdata) |
|---|---|---|---|---|
| narrow_100 | 100 | 9 | 24,137 | 28,196 |
| narrow_500 | 500 | 9 | 109,518 | 123,177 |
| narrow_1000 | 1,000 | 9 | 216,242 | 241,899 |
| narrow_2000 | 2,000 | 9 | 430,695 | 480,352 |
| wideboy_100 | 100 | 1,001 | 2,474,981 | 2,490,971 |
| wideboy_500 | 500 | 1,001 | 11,680,863 | 11,311,253 |
| wideboy | 1,000 | 1,001 | 23,188,366 | 22,336,759 |
~216 bytes/row at 9 columns; ~23.2 KB/row at 1,001 columns.
Load time (ms):
| table | mode | rows | cols | 18.0.0 | 18.1.1 | change |
|---|---|---|---|---|---|---|
| narrow_100 | VIEW | 100 | 9 | 1,167 | 1,289 | +10% |
| narrow_100 | EDIT | 100 | 9 | 4,776 | 4,193 | -12% |
| narrow_500 | VIEW | 500 | 9 | 1,551 | 954 | -38% |
| narrow_500 | EDIT | 500 | 9 | 7,020 | 4,809 | -31% |
| narrow_1000 | VIEW | 1,000 | 9 | 1,452 | 1,036 | -29% |
| narrow_1000 | EDIT | 1,000 | 9 | 8,242 | 5,265 | -36% |
| narrow_2000 | VIEW | 2,000 | 9 | 1,552 | 1,048 | -32% |
| narrow_2000 | EDIT | 2,000 | 9 | 8,779 | 6,234 | -29% |
| wideboy_100 | VIEW | 100 | 1,001 | 2,217 | 1,491 | -33% |
| wideboy_100 | EDIT | 100 | 1,001 | 16,604 | 17,267 | +4% |
| wideboy_500 | VIEW | 500 | 1,001 | 5,715 | 2,875 | -50% |
| wideboy_500 | EDIT | 500 | 1,001 | 55,108 | 52,921 | -4% |
| wideboy | VIEW | 1,000 | 1,001 | 9,448 | 7,890 | -16% |
| wideboy | EDIT | 1,000 | 1,001 | 131,046 | 138,013 | +5% |
Reading:
- **VIEW is cheap, and got cheaper.** Narrow stays at ~1.0-1.3 s from 100 to 2,000 rows - the payload is not the constraint at these sizes. Wide scales with the payload: 1.5 s at 2.4 MB, 7.9 s at 22 MB.
- **EDIT carries a large fixed cost** (~4.2 s at 100 rows narrow) and grows slowly with rows - 6.2 s at 2,000 rows narrow. The upgrade takes ~30% off that.
- **Wide EDIT is pathological, and the upgrade does not touch it.** 17 s at 100 rows, 53 s at 500, 138 s at 1,000 - all within noise of 18.0.0. That cost is DC's own per-cell work (the editor's `cells` function, validators, renderers) over up to a million cells, not Handsontable's render, so it is not something a grid upgrade can fix. It is why the EDIT screen gained a cell limit rather than a bigger row limit - that change is #335.
- Caveat: dev-mode `ng serve` bundle (unminified, ~25 MB) against a JS mock, so the absolute times are pessimistic compared to a production build on a real estate. The shape of the curve and the 18.0.0 -> 18.1.1 deltas are the reliable part.
## Changes
- `client/package.json` and `client/package-lock.json` - `handsontable` and `@handsontable/angular-wrapper` `18.0.0` -> `18.1.1`. The lock diff is only the two entries: neither package has dependencies, so nothing else in the tree moves.
- `client/licenseChecker.js` - adds `@handsontable/angular-wrapper@18.1.1` and `handsontable@18.1.1` to `excludePackages`. The list already carries 16.0.1 / 17.1.0 / 18.0.0, and the check fails the build on the library's non-standard licence string, so the new version has to be listed. Without this, `npm run build` stops at the `license-checker` step.
- `client/src/_hot-icons.scss` - regenerated by `scripts/gen-hot-icons.mjs` on install. It drops the two Pikaday rules (`.pika-single .pika-prev` / `.pika-next`) because 18.1 removed the leftover Pikaday styles from the themes. Committed because the file is tracked.
- `client/src/app/shared/dc-validator/tests/dc-validator.spec.ts` - 18.1 tightened `ColumnSettings['validator']` to its real type. 18.0 declared `ColumnSettings` as `Omit<GridSettings, 'data'>` over a `GridSettings` that carries `[key: string]: any`, which collapsed `validator` to `any`; 18.1 uses `RemoveIndexSignature<GridSettings>`, so `validator` is now `string | RegExp | function` and `rule.validator!.call(...)` no longer type-checks. The spec narrows it through a small `validatorOf()` helper, mirroring the guard the production code already uses.
- the two integration specs above.
## Testing
- [x] `npm ci` clean; `npm ls handsontable @handsontable/angular-wrapper` -> both 18.1.1
- [x] `npm audit --omit=dev` -> `found 0 vulnerabilities`
- [x] `npm run build` (production) passes
- [x] `npx ng test --no-progress --watch=false --browsers ChromeHeadlessCI` -> 548 SUCCESS
- [x] Cypress suite against the mocked estate -> 135/135 passing (`csv-limited.cy.ts` is the free-tier spec: it passes on a fresh estate, and locally it needed the licence state reset first because a screenshot run had already applied a key)
- [x] `npx prettier --check` clean on the changed files
### Screenshots
The grid surfaces on 18.1.1 - mocked estate, licence applied, no licence banner:
**Viewer grid** (`DC_JSLIB.MPE_X_TEST`, 7 rows x 9 cols)

**Viewer filter modal**

**Editor grid**

handsontable and @handsontable/angular-wrapper 18.0.0 -> 18.1.1, plus the
two repo-owned places that pin the version.
18.1 fixes two bugs this repo's specs had pinned as failure modes:
- "Sheet size limit exceeded" (upstream #10672): the formulas plugin used
to push the grid's maxRows into the HyperFormula engine as its sheet
size limit, so a licensing cap below an existing table's row count made
the engine reject the whole table. initSetup works around it with
Math.max(dataSource.length, editor_rows_allowed).
- Formula corruption on updateSettings while a sort is active: a bare {}
settings object was enough to turn formula cells into #REF!.
updateSettingsSortSafe clears and restores the sort around every call.
Both specs now assert the fixed behaviour instead, so a future regression
shows up. DC's own workarounds are untouched.
18.1.1 also fixes a filters plugin crash that lands on DC's usage exactly
(upstream #13480): editing a cell in a filtered column, or replacing the
data with a filter active, after updateSettings() with the filters option.
The Angular wrapper calls updateSettings on every update, and DC's viewer
and viewboxes run filters: true.
18.1.0 is mostly a performance release (viewport cell-meta release, index
translation, single-pass layout, bulk operations).
Also:
- licenseChecker.js: add the 18.1.1 handsontable packages to
excludePackages, or the build stops at the license-checker step (the
library's licence string is non-standard).
- _hot-icons.scss: regenerated by scripts/gen-hot-icons.mjs; drops the
Pikaday rules because 18.1 removed the leftover Pikaday theme styles.
- dc-validator.spec.ts: 18.1 tightened ColumnSettings['validator'] to its
real union (RemoveIndexSignature), so narrow it the way the production
code already does.
Reviewed the full base...head diff (7 files: handsontable and @handsontable/angular-wrapper 18.0.0 -> 18.1.1, license exclusions, Pikaday SCSS removal, two flipped integration specs) plus repo hardening at this head. The SCSS removal matches Handsontable 18.1 dropping the @handsontable/pikaday dependency, and keeping DC's own updateSettingsSortSafe and maxRows guards while pinning the fixed upstream behaviour is the right shape.
.pre-commit-config.yaml (missing) - the gitleaks scan runs only from .git-hooks/pre-commit, wired by the root prepare script, which the repo's .npmrc ignore-scripts=true suppresses on a fresh clone. Add a .pre-commit-config.yaml with the gitleaks hook pinned to an exact release tag so staged content is scanned independently of npm script execution.
commit b6b00ab96 "chore(deps): upgrade Handsontable to 18.1.1" - the upgrade changes shipped editor behaviour: maxRows capped below the loaded row count no longer throws (client/src/app/editor/utils/characterColumnFormula.integration.spec.ts:427 flips from expecting 'Sheet size limit exceeded' to expecting no error) and updateSettings on a sorted grid no longer corrupts formula cells (client/src/app/editor/utils/sortedGridRowSync.integration.spec.ts:347 flips from expecting #REF! to expecting none). The repo releases from commit subjects (.releaserc semantic-release), and chore cuts no release, so these user-visible fixes would ship with no version bump and no changelog entry. Use fix(deps): as the subject.
2 findings above for review.
Reviewed the full base...head diff (7 files: handsontable and @handsontable/angular-wrapper 18.0.0 -> 18.1.1, license exclusions, Pikaday SCSS removal, two flipped integration specs) plus repo hardening at this head. The SCSS removal matches Handsontable 18.1 dropping the @handsontable/pikaday dependency, and keeping DC's own updateSettingsSortSafe and maxRows guards while pinning the fixed upstream behaviour is the right shape.
- .pre-commit-config.yaml (missing) - the gitleaks scan runs only from .git-hooks/pre-commit, wired by the root `prepare` script, which the repo's .npmrc ignore-scripts=true suppresses on a fresh clone. Add a .pre-commit-config.yaml with the gitleaks hook pinned to an exact release tag so staged content is scanned independently of npm script execution.
- commit b6b00ab96 "chore(deps): upgrade Handsontable to 18.1.1" - the upgrade changes shipped editor behaviour: maxRows capped below the loaded row count no longer throws (client/src/app/editor/utils/characterColumnFormula.integration.spec.ts:427 flips from expecting 'Sheet size limit exceeded' to expecting no error) and updateSettings on a sorted grid no longer corrupts formula cells (client/src/app/editor/utils/sortedGridRowSync.integration.spec.ts:347 flips from expecting #REF! to expecting none). The repo releases from commit subjects (.releaserc semantic-release), and chore cuts no release, so these user-visible fixes would ship with no version bump and no changelog entry. Use fix(deps): as the subject.
2 findings above for review.
VIEW default 500 -> 2000. EDIT default 100 -> 250, plus a new
DC_MAXCELLS_WEBEDIT (200000) so a wide selection is refused on cells as
well as rows, whichever is reached first. Measured end-to-end: 2000 rows
of a 9 column table is ~1s in VIEW / ~6s in EDIT, but 100 rows of a 1001
column table is already ~17s in EDIT - a row cap cannot bound that.
The EDIT guard also moves: it now checks the filtered row count before
the sort that caps work.out, and again after PRE_EDIT_HOOK, which can
replace work.out with a larger table.
Review follow-ups on the row/cell limits:
- getdata.sas checked the limits twice - once before the sort that caps
work.out, and again after PRE_EDIT_HOOK. It is now a single check,
after both, so the counts cover everything that could reach the
browser: the rows the selection matched, plus anything a hook has
added. The payload cap moves to where work.outdata is built, which is
the only place that needs it. Each mp_abort also names its option once
- the limit was repeated in the message text as well as the condition.
- The migration closed only var_active=1 rows but inserted active rows
unconditionally, so an option a site had switched off was silently
switched back on. It now leaves those alone and logs a note.
- row-cell-limits.cy.ts test 1 matched a regex against the grid's text,
which the fixture's own columns satisfy (DEPTH_M reaches 4293,
SAMPLE_COUNT 4210), so it did not pin the raised VIEW cap. It now
reads the PK column - the first td of a .ht_master row - and requires
values in the PK range 1001-2000 with a maximum above 1500, which the
old 500 row cap could never render.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What
Upgrades
handsontableand@handsontable/angular-wrapperfrom18.0.0to18.1.1, plus the two repo-owned places that pin the version: theexcludePackagesallow-list inclient/licenseChecker.jsand the generatedclient/src/_hot-icons.scss.Base is
feat/export-dc-library-ddl(#333) rather thanmain: theCheck auditgate currently fails onmainfor the advisories #333 fixes, so this branch needs that base to go green. Retarget tomainonce #333 lands.Why
1. It fixes two Handsontable bugs that this repo's own specs had pinned. Both were "demonstrate the failure mode" tests asserting that the bug happens. The upgrade removes the bug, so those tests now pin the fixed behaviour instead.
Sheet size limit exceeded(upstream #10672, fixed in 18.1.0). The formulas plugin pushed the grid'smaxRowsinto the HyperFormula engine as its sheet-size limit, so a licensing cap below an existing table's row count made the engine reject the whole table.initSetupworks around this withMath.max(dataSource.length, editor_rows_allowed); 18.1 no longer couples the two. SeecharacterColumnFormula.integration.spec.ts.updateSettingsruns while a sort is active (fixed in 18.1.0). Callinghot.updateSettings()- even with a bare{}- while sorted turned formula cells into#REF!. DC'supdateSettingsSortSafeclears and restores the sort around every call; 18.1 keeps the formulas plugin's index translations across the update. SeesortedGridRowSync.integration.spec.ts.DC's own workarounds are still in place and still correct. They are not removed here - that is a separate decision.
2. It fixes a crash in the filters plugin on the path DC actually uses (upstream #13480, fixed in 18.1.1). Editing a cell in a filtered column, or replacing the data with a filter active, after
updateSettings()was called with thefiltersoption threw. The changelog notes that the React and Angular wrappers callupdateSettingson every update, and that is what DC's grids do:filters: trueinviewer.component.tsandviewboxes.component.ts, withupdateSettingson refetch and on the labels toggle.3. Large-dataset performance. 18.1.0 is mostly a performance release: cell metadata is released for rows scrolled out of the viewport, index translation and the hooks dispatch were reworked, the grid renders in a single pass, sorting/filtering/hiding and bulk operations were all reworked, and cell-meta storage no longer caches settings for every visited cell.
18.1.0 also adds
selectionHandles/moveCells, theintl-datetimecell type,preserveNumericLiteral,colorScheme/density,formulas.hyperlinksand a public NestedRows API, and hardens pasted HTML. All opt-in, none enabled in DC - so there is nothing new to screenshot.Measured performance
Local synthetic benchmark: the same page, the same Chrome build and the same dataset (50,000 rows x 12 columns, best of 3 runs), loading each version's own
handsontable.full.min.jsand timing each operation with a forcedhot.render()inside the timed region.Synthetic, single machine, not a claim about any particular deployment - but consistent with the changelog's own list of what changed.
Measured performance, end to end
The same upgrade, but measured through the app rather than the library: mocked estate (SASjs Server JS mocks), the client served by
ng servefrom this branch, driven by Cypress/Electron, timed from the click that starts the fetch to the grid rendering its first row - so the service call, the transfer, the parse and the grid build are all inside the measurement.Two shapes: narrow, the
MPE_X_TESTcolumn set (9 columns), and wide,WIDEBOYas built bysas/sasjs/tests/testsetup.sas(ROW_IDpluschar1..char500($20) andnum1..num500- 1,001 columns).Payloads, measured directly against the mock services:
~216 bytes/row at 9 columns; ~23.2 KB/row at 1,001 columns.
Load time (ms):
Reading:
cellsfunction, validators, renderers) over up to a million cells, not Handsontable's render, so it is not something a grid upgrade can fix. It is why the EDIT screen gained a cell limit rather than a bigger row limit - that change is #335.ng servebundle (unminified, ~25 MB) against a JS mock, so the absolute times are pessimistic compared to a production build on a real estate. The shape of the curve and the 18.0.0 -> 18.1.1 deltas are the reliable part.Changes
client/package.jsonandclient/package-lock.json-handsontableand@handsontable/angular-wrapper18.0.0->18.1.1. The lock diff is only the two entries: neither package has dependencies, so nothing else in the tree moves.client/licenseChecker.js- adds@handsontable/angular-wrapper@18.1.1andhandsontable@18.1.1toexcludePackages. The list already carries 16.0.1 / 17.1.0 / 18.0.0, and the check fails the build on the library's non-standard licence string, so the new version has to be listed. Without this,npm run buildstops at thelicense-checkerstep.client/src/_hot-icons.scss- regenerated byscripts/gen-hot-icons.mjson install. It drops the two Pikaday rules (.pika-single .pika-prev/.pika-next) because 18.1 removed the leftover Pikaday styles from the themes. Committed because the file is tracked.client/src/app/shared/dc-validator/tests/dc-validator.spec.ts- 18.1 tightenedColumnSettings['validator']to its real type. 18.0 declaredColumnSettingsasOmit<GridSettings, 'data'>over aGridSettingsthat carries[key: string]: any, which collapsedvalidatortoany; 18.1 usesRemoveIndexSignature<GridSettings>, sovalidatoris nowstring | RegExp | functionandrule.validator!.call(...)no longer type-checks. The spec narrows it through a smallvalidatorOf()helper, mirroring the guard the production code already uses.Testing
npm ciclean;npm ls handsontable @handsontable/angular-wrapper-> both 18.1.1npm audit --omit=dev->found 0 vulnerabilitiesnpm run build(production) passesnpx ng test --no-progress --watch=false --browsers ChromeHeadlessCI-> 548 SUCCESScsv-limited.cy.tsis the free-tier spec: it passes on a fresh estate, and locally it needed the licence state reset first because a screenshot run had already applied a key)npx prettier --checkclean on the changed filesScreenshots
The grid surfaces on 18.1.1 - mocked estate, licence applied, no licence banner:
Viewer grid (
DC_JSLIB.MPE_X_TEST, 7 rows x 9 cols)Viewer filter modal
Editor grid
Reviewed the full base...head diff (7 files: handsontable and @handsontable/angular-wrapper 18.0.0 -> 18.1.1, license exclusions, Pikaday SCSS removal, two flipped integration specs) plus repo hardening at this head. The SCSS removal matches Handsontable 18.1 dropping the @handsontable/pikaday dependency, and keeping DC's own updateSettingsSortSafe and maxRows guards while pinning the fixed upstream behaviour is the right shape.
preparescript, which the repo's .npmrc ignore-scripts=true suppresses on a fresh clone. Add a .pre-commit-config.yaml with the gitleaks hook pinned to an exact release tag so staged content is scanned independently of npm script execution.b6b00ab96"chore(deps): upgrade Handsontable to 18.1.1" - the upgrade changes shipped editor behaviour: maxRows capped below the loaded row count no longer throws (client/src/app/editor/utils/characterColumnFormula.integration.spec.ts:427 flips from expecting 'Sheet size limit exceeded' to expecting no error) and updateSettings on a sorted grid no longer corrupts formula cells (client/src/app/editor/utils/sortedGridRowSync.integration.spec.ts:347 flips from expecting #REF! to expecting none). The repo releases from commit subjects (.releaserc semantic-release), and chore cuts no release, so these user-visible fixes would ship with no version bump and no changelog entry. Use fix(deps): as the subject.2 findings above for review.