0.3.1 - 2026-10-05

← All releases

Change Who is affected What to do
Cancelling the confirmation dialog leaves filter/sort/quick-filter state untouched Code that relied on getState() reporting the new filter even after a cancel The call is now a no-op; read getState() / getQuickFilter() to see what was applied
A totalRows from fetchMeta that is not a non-negative integer is treated as 0 and logged Servers returning the row count as a string or a fraction Send an integer. Previously the tab froze as soon as the grid scrolled or repainted
printGrid() warns about server rows that were not loaded Tests that treat console warnings as failures Use printGrid({ full: true }) to print everything
Hidden columns are accepted by setFilter / setSort / setCellValue Code that relied on those calls throwing for a hidden column They now apply instead of throwing
Columns the data no longer has disappear from getHiddenColumns() Code that saves and restores the hidden list across changing column sets Stale column names are cleaned up automatically

Added

  • printGrid({ full: true }): fetches every row matching the current filters and sorts, slots the rows added with addRow() in where they sit on screen, and returns a Promise so you can await the filled print window. The default (full: false) prints only rows already loaded, as before, and now warns on the console about how many server rows were left out.
  • Behavior notes in the type declarations: when startEditing throws and when it returns false (checkbox / button columns), that setCellValue ignores editableCols, that getChanges throws without rowKey, that setHeaderCheckbox does not fire onHeaderCheckboxChange, that an empty data array needs columnDefs, and that filter/sort changes may show a confirmation dialog.

Fixed

  • A totalRows of 1.5, Infinity, NaN, a string, a negative number or undefined from fetchMeta made the browser tab stop responding. The value is now coerced to a non-negative integer and reported on the console, and row counts above two billion no longer stall.
  • With unsaved edits present, setFilter / setFilterValues / setSort / removeSort / clearSort / setQuickFilter changed the filter and sort state before the confirmation dialog, so after a cancel getState() reported a condition the rows did not have. setQuickFilter was worse: the cancelled value stayed recorded, so passing it again was skipped as “unchanged” and that search term could never be applied.
  • setState({ hiddenColumns }) on its own was ignored; hiding only took effect when columns was passed in the same call.
  • Passing a hidden column to setFilter / setFilterValues / setSort / setCellValue / setCellValues threw unknown field. Hiding a filtered column left that filter active but impossible to change, only to remove. setSelection / focusCell / startEditing still throw, since they need a column position, but the message now tells you to call showColumn() first.
  • A rowHighlighter that threw took down the whole frame. It is now isolated like cellBackground / cellDecorator: the error is logged and the row is drawn without a highlight.
  • An onRender that threw let the exception escape drawing. Since the first paint happens while the grid is being created, new JHGrid(...) itself failed. The error is now logged and the grid works.
  • Errors raised while laying out columns or drawing the first frame were reported as fetchMeta failed. The two phases are now separate and each reports its own cause.
  • A pagination.pageSize between 0 and 1 produced a page size of 0 and an Infinity page count. Values below 1 now fall back to the default of 50, like 0 already did.
  • exportCsv({ full: true }) requested pages forever against a fetchData that ignores the requested page range and always returns a full page. It now stops at the known row count.
  • printGrid({ full: true }) left out the rows added with addRow(). The default print includes them, so the same method produced a different set of rows depending on full. They are now printed among the server rows, where they sit on screen. Rows a plugin collected instead carry no positions, so in that case they print in the order they arrived.
  • exportCsv({ full: true }) / printGrid({ full: true }) still asked for the first page after the server reported a row count of 0, which was the one case a fetchData that ignores the page range could still loop on. Once the first load is through, only the reported row count is requested.
  • The full scan behind row reordering (rowReorder) and the color filter used fetchMeta’s totalRows unchecked, so a fraction, Infinity or NaN broke it with Invalid array length. It is coerced to a non-negative integer, like the first load.
  • After the data’s column set changed, columns that no longer exist stayed in getHiddenColumns() and could be brought back with showColumn().
  • destroy() left the loaded-row cache and failed-chunk retry timers in place; both are released now (100 cached rows became 0). Requests already sent are still not cancelled, and their responses are discarded as before.

Performance

  • Applying a saved row (acknowledgeSave) and clearing a row’s edits cost time proportional to the number of edits in the whole grid; it is now proportional to the edited fields of that row. Acknowledging 2000 rows with 40,000 edited cells went from 2755ms to 343ms, measured by running the pre-fix and post-fix bundles in the same session.
  • getState() no longer scans the full edit set once per added row. With 2000 added rows and 20,000 edited cells it went from 400ms to 2.3ms.
  • Clearing a deleted column’s edits uses the same index (16.9ms to 9.8ms with 40,000 edited cells), and leaves exactly the same edits behind as before.
  • Removed the rowHighlighter result cache: it was rebuilt every frame and never reused, so it only allocated. The number of calls is unchanged (once per visible row per frame).

To check for regressions the pre-fix and post-fix bundles were measured in the same session; the post-fix figure is in brackets. 1,000,000 rows (3 renderers, 1200x600): 37.3ms (39.5ms) to initialize, 0.4ms (0.5ms) per repaint, 0.2ms (0.2ms) per scrollTo. 500,000 local rows: 12.2ms (13.7ms) to initialize, 114ms (118ms) to sort, 49ms (44ms) to filter. 40,000-cell bulk edit 37.5ms (36.7ms), getChanges 16.1ms (14.6ms), undo 17.6ms (14.7ms). All within measurement noise.


This site uses Just the Docs, a documentation theme for Jekyll.