Compare commits

..

319 Commits

Author SHA1 Message Date
semantic-release-bot 2dcae9060f chore(release): 7.10.1 [skip ci]
## [7.10.1](https://git.datacontroller.io/dc/dc/compare/v7.10.0...v7.10.1) (2026-07-15)

### Bug Fixes

* **editor:** order validation rules by COLHEADERS, not cols[].VARNUM ([4c2aca3](4c2aca3495))
2026-07-15 14:05:25 +00:00
allan f4525cc62a Merge pull request 'Fix headers alignment' (#262) from fix/colheaders-rule-alignment into main
Release / Build-production-and-ng-test (push) Successful in 4m43s
Release / Build-and-test-development (push) Successful in 14m11s
Release / release (push) Successful in 9m6s
Reviewed-on: #262
Reviewed-by: Yury <yury@4gl.io>
2026-07-15 13:42:31 +00:00
sead d4aa70ddbf test(editor): make CLS EDIT un-hide regression test actually fail
Build / Build-and-ng-test (pull_request) Successful in 5m20s
Build / Build-and-test-development (pull_request) Successful in 14m46s
Lighthouse Checks / lighthouse (pull_request) Successful in 21m51s
2026-07-15 15:08:31 +02:00
sead 4c2aca3495 fix(editor): order validation rules by COLHEADERS, not cols[].VARNUM 2026-07-15 14:30:26 +02:00
semantic-release-bot 36c9ef0e95 chore(release): 7.10.0 [skip ci]
# [7.10.0](https://git.datacontroller.io/dc/dc/compare/v7.9.1...v7.10.0) (2026-07-13)

### Bug Fixes

* adapt column validation to vertical-array COLTYPE ([#253](#253)) ([3016750](301675052f))
* backend updates to bring back labels under [#240](#240) ([fb94840](fb94840016))
* **editor:** normalise unpadded time strings on spreadsheet import ([7b4b4eb](7b4b4ebeeb))
* **hot:** row rendering on resize, resolve blank whitespace ([c7ba025](c7ba025d39))
* including datadictionary descs in labels, [#240](#240) ([e7453a9](e7453a9305))
* pass variable formats to front-end in a vertical array instead of horizontal list ([691d6f2](691d6f277e))
* remove coltype from sasparams table, add it to cols table. ([8101400](81014001ac))
* trim leading/trailing whitespace from Excel header cells on upload ([2728dac](2728dac873))
* **va:** date filters refactor, support more dates ([df5c975](df5c975869))
* **va:** enable readOnly and hold filtering while editing ([82a254d](82a254d22c))
* **va:** enable Upload button in va mode ([1beb3d4](1beb3d490d))
* **va:** guard deferred contextMenu toggle against destroyed instance ([b5e9b25](b5e9b25319))

### Features

* adding DC_MAXOBS_WEBVIEW config item, closes [#258](#258) ([ba7b610](ba7b61082d))
* viewer/editor column label display toggle (?labels=true) ([25c12f2](25c12f2b18))

### Reverts

* enable upload button in va mode ([0d7fd34](0d7fd34297))
2026-07-13 16:56:10 +00:00
allan 7511b64523 Merge pull request 'Issue 251' (#255) from issue-251 into main
Release / Build-production-and-ng-test (push) Successful in 4m52s
Release / Build-and-test-development (push) Successful in 14m15s
Release / release (push) Successful in 9m11s
Reviewed-on: #255
2026-07-13 16:33:04 +00:00
sead 7b4b4ebeeb fix(editor): normalise unpadded time strings on spreadsheet import
Build / Build-and-ng-test (pull_request) Successful in 5m24s
Build / Build-and-test-development (pull_request) Successful in 14m38s
Lighthouse Checks / lighthouse (pull_request) Successful in 21m38s
2026-07-13 15:59:52 +02:00
sead c7ba025d39 fix(hot): row rendering on resize, resolve blank whitespace 2026-07-13 15:28:11 +02:00
sead b5e9b25319 fix(va): guard deferred contextMenu toggle against destroyed instance
Build / Build-and-ng-test (pull_request) Successful in 4m1s
Build / Build-and-test-development (pull_request) Successful in 10m44s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m5s
2026-07-13 10:12:38 +02:00
sead 82a254d22c fix(va): enable readOnly and hold filtering while editing
Build / Build-and-ng-test (pull_request) Successful in 3m54s
Build / Build-and-test-development (pull_request) Successful in 10m37s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m0s
2026-07-12 21:16:51 +02:00
sead 0d7fd34297 revert: enable upload button in va mode
This reverts commit 1beb3d490d.
2026-07-12 20:33:11 +02:00
sead 1beb3d490d fix(va): enable Upload button in va mode
Build / Build-and-ng-test (pull_request) Successful in 3m55s
Build / Build-and-test-development (pull_request) Successful in 10m32s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m13s
2026-07-10 15:58:21 +02:00
allan 1fb5e8eb37 chore: fixing header
Lighthouse Checks / lighthouse (pull_request) Has been cancelled
Build / Build-and-ng-test (pull_request) Successful in 4m3s
Build / Build-and-test-development (pull_request) Successful in 10m31s
2026-07-10 12:53:08 +01:00
allan 78dd29ba78 Merge pull request 'feat: adding DC_MAXOBS_WEBVIEW config item, closes #258' (#259) from issue258 into issue-251
Build / Build-and-ng-test (pull_request) Successful in 4m1s
Lighthouse Checks / lighthouse (pull_request) Has been cancelled
Build / Build-and-test-development (pull_request) Has been cancelled
Reviewed-on: #259
2026-07-10 11:52:24 +00:00
allan ba7b61082d feat: adding DC_MAXOBS_WEBVIEW config item, closes #258
Build / Build-and-ng-test (pull_request) Successful in 4m1s
Build / Build-and-test-development (pull_request) Has been cancelled
Lighthouse Checks / lighthouse (pull_request) Successful in 18m17s
2026-07-10 12:45:09 +01:00
Yury 1a9231809e Merge pull request 'fix(va): date filters refactor, support more dates' (#257) from fix/va-date-parsing into issue-251
Build / Build-and-ng-test (pull_request) Successful in 3m56s
Build / Build-and-test-development (pull_request) Successful in 10m33s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m15s
Reviewed-on: #257
Reviewed-by: Yury <yury@4gl.io>
2026-07-10 11:06:19 +00:00
sead df5c975869 fix(va): date filters refactor, support more dates
Build / Build-and-ng-test (pull_request) Successful in 3m52s
Build / Build-and-test-development (pull_request) Successful in 10m36s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m16s
2026-07-10 10:33:32 +02:00
YuryShkoda 2728dac873 fix: trim leading/trailing whitespace from Excel header cells on upload
Build / Build-and-ng-test (pull_request) Successful in 4m2s
Build / Build-and-test-development (pull_request) Successful in 10m45s
Lighthouse Checks / lighthouse (pull_request) Successful in 17m58s
Header matching in searchDataInExcel() was case-insensitive but not
whitespace-tolerant, so a header like " SOME_CHAR" (an easy defect to
pick up via copy-paste from another spreadsheet/system) was reported as
a missing column and aborted the whole upload.

- trim() the cell value before matching, alongside the existing
  toLowerCase() normalization
- add spreadsheet-util.spec.ts (no prior spec file existed) covering
  the trimmed match and a regression guard that a genuinely different
  header is still correctly reported missing
- add a Cypress case + fixture in excel.cy.ts covering the same defect
  end to end
2026-07-10 10:07:54 +03:00
YuryShkoda 88f55b9d06 chore(merge): merge origin/issue-251 into issue-251
Build / Build-and-ng-test (pull_request) Successful in 3m52s
Build / Build-and-test-development (pull_request) Successful in 10m21s
Lighthouse Checks / lighthouse (pull_request) Successful in 17m45s
2026-07-09 17:51:49 +03:00
YuryShkoda 301675052f fix: adapt column validation to vertical-array COLTYPE (#253)
getdata.sas now sends one COLTYPE JSON-object string per cols[] row (via
a LEFT JOIN on the real dataset's columns) instead of one comma-joined
sasparams.COLTYPE string, avoiding the 32K cats() buffer overflow on
wide tables (#253). Adapt the client accordingly:

- parseColTypeRow replaces parseColType; DcValidator builds validation
  rules per cols[] entry instead of from sasparams.COLTYPE
- _____DELETE__THIS__RECORD_____'s rule is never present in cols[] (it's
  a client-only synthetic column %mp_getcols doesn't know about), so
  it's now hardcoded via deleteRecordColumnRule.ts
- cols[] isn't guaranteed to arrive in COLHEADERS/VARNUM order (backend
  can serialize it alphabetically by NAME) but editor.component.ts pairs
  rules with headers positionally, so DcValidator now sorts cols by
  VARNUM before building rules
- update sas/mocks/sasjs getdata.js to the new per-column COLTYPE shape
- add Cypress coverage for the delete-record dropdown and an ordinary
  dropdown column end to end
2026-07-09 17:48:33 +03:00
allan 45c434271d Merge pull request 'Display LABELS instead of column NAMEs #240' (#252) from issue240 into issue-251
Build / Build-and-ng-test (pull_request) Successful in 4m18s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m44s
Build / Build-and-test-development (pull_request) Successful in 10m34s
Reviewed-on: #252
2026-07-09 09:36:55 +00:00
allan f523c798c3 Merge branch 'issue-251' into issue240
Build / Build-and-ng-test (pull_request) Successful in 4m12s
Build / Build-and-test-development (pull_request) Successful in 10m44s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m14s
2026-07-09 09:36:38 +00:00
YuryShkoda 41fd618884 chore(merge): merge issue240 into issue-251
Brings in the SAS-side fixes for issue #253 (getdata.sas passing
variable formats via a vertical cols.COLTYPE array instead of a
single concatenated sasparams.COLTYPE string, which overflowed SAS's
32K buffer on wide datasets) plus related label/datadictionary fixes.
No client TypeScript files are touched by this merge.
2026-07-09 11:52:37 +03:00
YuryShkoda f368242cb0 test: fixed viewer-labels.cy.ts
Build / Build-and-ng-test (pull_request) Successful in 4m6s
Build / Build-and-test-development (pull_request) Successful in 10m32s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m34s
2026-07-09 10:11:36 +03:00
YuryShkoda 636e4e5b4a test: fixed viewer-labels.cy.ts
Build / Build-and-ng-test (pull_request) Successful in 4m1s
Lighthouse Checks / lighthouse (pull_request) Successful in 17m53s
Build / Build-and-test-development (pull_request) Failing after 13m57s
2026-07-09 09:26:03 +03:00
YuryShkoda 49710eb62d chore: fixed package-lock in client
Build / Build-and-ng-test (pull_request) Successful in 3m55s
Build / Build-and-test-development (pull_request) Failing after 13m51s
Lighthouse Checks / lighthouse (pull_request) Successful in 17m50s
2026-07-09 08:53:00 +03:00
YuryShkoda 25c12f2b18 feat: viewer/editor column label display toggle (?labels=true)
Build / Build-and-ng-test (pull_request) Failing after 39s
Build / Build-and-test-development (pull_request) Has been skipped
Lighthouse Checks / lighthouse (pull_request) Failing after 54s
Adds a URL-driven toggle (?labels=true, mirroring embed=va) that
swaps grid column headers between NAME and LABEL in both the Viewer
and Editor, with a "Show labels"/"Show names" item in each grid's
right-click context menu. The header-info popup now shows NAME first
regardless of display mode. Sorting/filtering/data binding stay
NAME-based throughout — only the displayed header text changes.

New shared, unit-tested utils (parse-labels-param, display-col-headers,
col-info-html) back both pages. Mock data (viewdata.js, getdata.js)
gains a couple of columns with LABEL != NAME plus a blank-LABEL case,
covered by a new viewer-labels.cy.ts e2e spec now wired into CI.

Also fixes a pre-existing gap in cypress/support/commands.ts: the
isLoggedIn/loginAndUpdateValidKey custom commands had no Cypress
Chainable type augmentation, and moment was imported as a namespace
import despite its `export =` typing making it uncallable that way.
2026-07-08 15:15:28 +03:00
allan 3ba8858260 Merge pull request 'fix: pass variable formats to front-end in a vertical array instead of horizontal list' (#254) from fix/high_column_count_exceeding_maxcharlen into issue240
Build / Build-and-ng-test (pull_request) Successful in 3m53s
Build / Build-and-test-development (pull_request) Successful in 9m19s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m9s
Reviewed-on: #254
2026-07-08 10:55:37 +00:00
Trevor Moody 81014001ac fix: remove coltype from sasparams table, add it to cols table.
Build / Build-and-ng-test (pull_request) Successful in 3m52s
Build / Build-and-test-development (pull_request) Successful in 9m22s
Lighthouse Checks / lighthouse (pull_request) Successful in 17m54s
2026-07-08 03:24:22 +01:00
Trevor Moody 691d6f277e fix: pass variable formats to front-end in a vertical array instead of horizontal list 2026-07-07 17:24:16 +01:00
allan e7453a9305 fix: including datadictionary descs in labels, #240
Build / Build-and-ng-test (pull_request) Successful in 3m55s
Build / Build-and-test-development (pull_request) Successful in 9m15s
Lighthouse Checks / lighthouse (pull_request) Successful in 17m48s
2026-07-07 15:30:14 +01:00
YuryShkoda 005b616adb chore(deps): upgrade Handsontable to v18
Bump handsontable and @handsontable/angular-wrapper 17.1 -> 18.0 and
adapt to its stricter TypeScript-core types: moved handsontable/common
and deep walkontable imports to their new public export paths, added
explicit generics/casts where getData()/getCellMeta()/getSelected() are
now typed unknown/nullable instead of the loose v17 shapes, and updated
the AutocompleteEditor override to the new open() signature.

Also allowlists @handsontable/angular-wrapper@18.0.0 and
handsontable@18.0.0 in licenseChecker.js — both versions report the
same non-SPDX "SEE LICENSE IN LICENSE.txt" field that license-checker
mis-resolves as a disallowed custom license, same as prior versions.
2026-07-06 15:44:27 +03:00
allan 0ae1781d28 Merge branch 'main' into issue240
Build / Build-and-ng-test (pull_request) Successful in 4m12s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m17s
Build / Build-and-test-development (pull_request) Successful in 9m23s
2026-07-05 10:51:11 +00:00
allan fb94840016 fix: backend updates to bring back labels under #240
Build / Build-and-ng-test (pull_request) Successful in 4m11s
Build / Build-and-test-development (pull_request) Successful in 9m31s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m31s
2026-07-05 11:49:36 +01:00
semantic-release-bot 6864c044dc chore(release): 7.9.1 [skip ci]
## [7.9.1](https://git.datacontroller.io/dc/dc/compare/v7.9.0...v7.9.1) (2026-06-30)

### Bug Fixes

* workflow file upload using action PAT ([a161670](a161670b86))
2026-06-30 07:29:21 +00:00
allan b30ba3e76b Merge pull request 'fix: workflow file upload using action PAT' (#250) from workflowfix into main
Release / Build-production-and-ng-test (push) Successful in 3m38s
Release / Build-and-test-development (push) Successful in 9m14s
Release / release (push) Successful in 7m4s
Reviewed-on: #250
Reviewed-by: sead <307+sead@noreply.git.datacontroller.io>
2026-06-30 07:12:54 +00:00
allan a4583f2db0 Merge branch 'main' into workflowfix
Build / Build-and-test-development (pull_request) Has been cancelled
Build / Build-and-ng-test (pull_request) Has been cancelled
Lighthouse Checks / lighthouse (pull_request) Has been cancelled
2026-06-30 07:12:40 +00:00
allan a161670b86 fix: workflow file upload using action PAT
Build / Build-and-test-development (pull_request) Has been cancelled
Build / Build-and-ng-test (pull_request) Has been cancelled
Lighthouse Checks / lighthouse (pull_request) Has been cancelled
2026-06-30 08:09:39 +01:00
semantic-release-bot e6cf5a8d25 chore(release): 7.9.0 [skip ci]
# [7.9.0](https://git.datacontroller.io/dc/dc/compare/v7.8.2...v7.9.0) (2026-06-29)

### Bug Fixes

* allow large retained key values.  Closes [#248](#248). ([6a30de3](6a30de37ca))
* build issues ([#245](#245)) ([77b4bb3](77b4bb310e))
* **ci:** bump Lighthouse to 13.4.0 ([47ba03e](47ba03ec63))
* **dc-validator:** display date/time/datetime cells as raw ISO, locale-independent ([9ca5719](9ca57193b4))
* **editor:** await dynamic validation on paste; defer spinner ([ea03bde](ea03bdecc5))
* **editor:** cancelEdit will reset cell's valid state ([51071b4](51071b463b))
* **editor:** preserve commas in dropdown source values ([6af49cf](6af49cf1fd))
* **editor:** retry initSetup when the Handsontable instance isn't ready ([53b7ee0](53b7ee0cb2))
* **handsontable:** add exceljs ([38cb1e2](38cb1e207b))
* **handsontable:** horizontal scrollbar in dropdown ([ac0bd10](ac0bd10212))
* **handsontable:** restore dark mode for v17 ([9d97bf7](9d97bf7ea1))
* **handsontable:** update licenseChecker exclude buffers - exceljs dep ([12c7d30](12c7d30894))
* incrementing rk val ([7444209](74442096ba))
* **licensing:** add protocol info ([3668a74](3668a7426f)), closes [#178](#178)
* migrate handsontables to v17 ([1b73e35](1b73e355b7))
* **mocks:** return dynamic cell values as OBJ to match new contract ([a15fdc4](a15fdc401e))
* removing display_value in SAS code ([9b18a45](9b18a45d35))
* rule_type ([adc6424](adc6424211))
* sending dynamic_values and dynamic_extended_values back as OBJ instead of ARR ([e859d33](e859d3354e))
* upcasing RAW_VALUE, bumping core, fixing tests ([982d507](982d507ae3))
* validate pasted values ([b661580](b661580c60))
* viewLibs now fires only once, libPromise shared between the calls ([dc4e07a](dc4e07a692))

### Features

* **edit-record:** use native date/time/datetime pickers ([2d29346](2d29346cbf))
* **editor:** add READONLY, HIDDEN, ROUND and NUMBER_FORMAT validation rules ([4ea604f](4ea604f9fb))
* **editor:** migrate date/time/datetime to HOT 17 Intl cell types ([f70ea2f](f70ea2fe71))
* **editor:** paste-validation overlay with cancel and confirm ([609731f](609731ff99))
* **editor:** SAS VA data-driven content embed mode (?embed=va) ([ffa3ff9](ffa3ff9c10))
* **editor:** VA embed filter modes with pending UX ([ebe6972](ebe6972dd7))
* **editor:** validate autofilled cells; fix paste validation lag ([11ee49a](11ee49a57a))
* **handsontable:** enable export plugin, add export menu items ([29aaa72](29aaa72c60))
* migration script for adding new validation rules under [#241](#241) ([9f84a58](9f84a580fb))
* new validation rules in mpe_selectbox.  [#241](#241) ([2f5d3df](2f5d3dfc38))

### Reverts

* eager-load feature modules instead of lazy-loading ([3429a7a](3429a7a2a6))
* **editor:** DISPLAY_VALUE change ([eb015d7](eb015d712b))
2026-06-29 18:11:12 +00:00
allan 7ebc26fc7f chore: pipeline updates for release
Release / Build-production-and-ng-test (push) Successful in 3m34s
Release / Build-and-test-development (push) Successful in 9m13s
Release / release (push) Successful in 7m3s
2026-06-29 18:55:19 +01:00
allan c9dd933cec chore: aligning release.yml with build.yml
Release / Build-production-and-ng-test (push) Successful in 3m38s
Release / Build-and-test-development (push) Successful in 9m18s
Release / release (push) Failing after 3m1s
2026-06-29 18:17:18 +01:00
allan 716eba621f Merge pull request 'Resolve multiple issues' (#236) from 127-235-issues into main
Release / Build-production-and-ng-test (push) Failing after 1m19s
Release / Build-and-test-development (push) Has been skipped
Release / release (push) Has been skipped
Reviewed-on: #236
2026-06-29 17:03:54 +00:00
allan 982d507ae3 fix: upcasing RAW_VALUE, bumping core, fixing tests
Build / Build-and-ng-test (pull_request) Successful in 3m52s
Build / Build-and-test-development (pull_request) Successful in 9m18s
Lighthouse Checks / lighthouse (pull_request) Successful in 17m41s
2026-06-29 17:42:05 +01:00
sead 27a12951e3 build(lint): add .prettierignore to client
Build / Build-and-ng-test (pull_request) Successful in 3m54s
Build / Build-and-test-development (pull_request) Successful in 9m22s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m7s
2026-06-29 16:01:35 +02:00
sead 4c8e4ee1b2 build(lint): pin prettier to 3.8.4 in client to stop CI version drift
Build / Build-and-ng-test (pull_request) Failing after 1m35s
Build / Build-and-test-development (pull_request) Has been skipped
Lighthouse Checks / lighthouse (pull_request) Successful in 17m35s
2026-06-29 15:08:24 +02:00
sead a15fdc401e fix(mocks): return dynamic cell values as OBJ to match new contract
Build / Build-and-ng-test (pull_request) Failing after 1m36s
Build / Build-and-test-development (pull_request) Has been skipped
Lighthouse Checks / lighthouse (pull_request) Successful in 17m44s
2026-06-29 14:53:42 +02:00
sead 7019e39318 test(excel): update excel.cy.ts tests to reflect YYYY-MM-DD format
Build / Build-and-ng-test (pull_request) Failing after 1m37s
Build / Build-and-test-development (pull_request) Has been skipped
Lighthouse Checks / lighthouse (pull_request) Successful in 17m36s
Ref 9ca5719
2026-06-29 14:40:17 +02:00
sead 9ca57193b4 fix(dc-validator): display date/time/datetime cells as raw ISO, locale-independent
Build / Build-and-ng-test (pull_request) Successful in 3m57s
Build / Build-and-test-development (pull_request) Failing after 9m21s
Lighthouse Checks / lighthouse (pull_request) Successful in 17m42s
2026-06-26 10:03:45 +02:00
sead 175eb9853a refactor: consume dynamic_values/dynamic_extended_values as typed objects 2026-06-26 09:37:05 +02:00
allan e859d3354e fix: sending dynamic_values and dynamic_extended_values back as OBJ instead of ARR
Build / Build-and-ng-test (pull_request) Successful in 3m54s
Build / Build-and-test-development (pull_request) Successful in 9m38s
Lighthouse Checks / lighthouse (pull_request) Successful in 17m44s
2026-06-25 19:17:22 +01:00
allan adc6424211 fix: rule_type
Build / Build-and-ng-test (pull_request) Successful in 4m1s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m26s
Build / Build-and-test-development (pull_request) Successful in 9m48s
2026-06-25 17:48:52 +01:00
allan 74442096ba fix: incrementing rk val
Build / Build-and-ng-test (pull_request) Successful in 4m6s
Lighthouse Checks / lighthouse (pull_request) Successful in 17m52s
Build / Build-and-test-development (pull_request) Successful in 10m11s
2026-06-25 17:47:22 +01:00
allan 9f84a580fb feat: migration script for adding new validation rules under #241
Build / Build-and-ng-test (pull_request) Successful in 4m5s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m31s
Build / Build-and-test-development (pull_request) Successful in 10m2s
2026-06-25 17:46:16 +01:00
allan 2f5d3dfc38 feat: new validation rules in mpe_selectbox. #241 2026-06-25 17:40:59 +01:00
allan 9b18a45d35 fix: removing display_value in SAS code
Build / Build-and-ng-test (pull_request) Successful in 3m54s
Build / Build-and-test-development (pull_request) Successful in 9m39s
Lighthouse Checks / lighthouse (pull_request) Successful in 17m49s
2026-06-25 17:24:35 +01:00
allan 6a30de37ca fix: allow large retained key values. Closes #248.
Build / Build-and-ng-test (pull_request) Successful in 3m56s
Build / Build-and-test-development (pull_request) Successful in 9m43s
Lighthouse Checks / lighthouse (pull_request) Successful in 17m52s
The fix was actually in the core macro (mp_retainedkey)
2026-06-25 17:00:07 +01:00
sead 31075a165f chore(git): merge pr 'SAS VA data-driven content embed mode (?embed=va)' (#249) from 243-embed-va into 127-235-issues
Build / Build-and-ng-test (pull_request) Successful in 3m55s
Build / Build-and-test-development (pull_request) Successful in 9m42s
Lighthouse Checks / lighthouse (pull_request) Successful in 17m41s
Reviewed-on: #249
2026-06-25 13:51:10 +00:00
sead 53b7ee0cb2 fix(editor): retry initSetup when the Handsontable instance isn't ready
Build / Build-and-ng-test (pull_request) Successful in 3m57s
Build / Build-and-test-development (pull_request) Successful in 9m44s
Lighthouse Checks / lighthouse (pull_request) Successful in 17m58s
2026-06-25 15:26:33 +02:00
sead ebe6972dd7 feat(editor): VA embed filter modes with pending UX
Build / Build-and-ng-test (pull_request) Successful in 4m19s
Build / Build-and-test-development (pull_request) Successful in 9m51s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m8s
2026-06-25 14:53:24 +02:00
sead 2acea64589 refactor(editor): extract VA filter building into VaFilterService 2026-06-25 14:53:24 +02:00
sead ffa3ff9c10 feat(editor): SAS VA data-driven content embed mode (?embed=va) 2026-06-25 14:53:24 +02:00
sead 0ac3ff4511 docs(skills): add Handsontable and HyperFormula skills 2026-06-25 14:53:24 +02:00
sead 3429a7a2a6 revert: eager-load feature modules instead of lazy-loading
This reverts commit d4ae9e9084
2026-06-25 14:53:24 +02:00
sead 56fb2c2a3a chore(git): merge pr 'Fix HOT v17 migration issues, new validations, deprecate display_value' (#244) from 237-241-242-issues into 127-235-issues
Build / Build-and-ng-test (pull_request) Successful in 3m41s
Build / Build-and-test-development (pull_request) Successful in 9m46s
Lighthouse Checks / lighthouse (pull_request) Successful in 17m48s
Reviewed-on: #244
2026-06-25 12:51:23 +00:00
sead 77b4bb310e fix: build issues (#245)
Build / Build-and-ng-test (pull_request) Successful in 3m40s
Build / Build-and-test-development (pull_request) Successful in 9m36s
Lighthouse Checks / lighthouse (pull_request) Successful in 16m45s
- resolve HOT v17 render loops caused by unstable settings bind
- tests: pin locale to en-GB
- improve accessibility
- improve initial bundle size, split and lazy-load feature modules

Reviewed-on: #245
Co-authored-by: s <sead@4gl.io>
Co-committed-by: s <sead@4gl.io>
2026-06-16 21:41:24 +00:00
sead 47ba03ec63 fix(ci): bump Lighthouse to 13.4.0
Build / Build-and-ng-test (pull_request) Successful in 4m16s
Lighthouse Checks / lighthouse (pull_request) Failing after 18m34s
Build / Build-and-test-development (pull_request) Failing after 2h32m47s
2026-06-15 14:42:18 +02:00
sead e331e1bb21 ci(lighthouse): skip Chrome install when already present
Build / Build-and-ng-test (pull_request) Successful in 3m42s
Lighthouse Checks / lighthouse (pull_request) Failing after 5m2s
Build / Build-and-test-development (pull_request) Has been cancelled
2026-06-15 13:42:57 +02:00
sead 40f285f295 test(mocks): pin e2e locale and add lazy MPE_X_NEW demo table 2026-06-15 13:40:38 +02:00
sead 2d29346cbf feat(edit-record): use native date/time/datetime pickers 2026-06-15 13:10:13 +02:00
sead f70ea2fe71 feat(editor): migrate date/time/datetime to HOT 17 Intl cell types 2026-06-15 11:30:17 +02:00
sead 29aaa72c60 feat(handsontable): enable export plugin, add export menu items
Build / Build-and-ng-test (pull_request) Successful in 4m10s
Lighthouse Checks / lighthouse (pull_request) Failing after 7m20s
Build / Build-and-test-development (pull_request) Failing after 21m27s
2026-06-10 11:19:34 +02:00
sead 12c7d30894 fix(handsontable): update licenseChecker exclude buffers - exceljs dep 2026-06-09 13:56:30 +02:00
sead 38cb1e207b fix(handsontable): add exceljs
override uuid version - exceljs dep
2026-06-09 13:55:42 +02:00
sead 4c879c614b refactor(editor): replace numbro with native Intl numeric formatting 2026-06-08 19:08:25 +02:00
sead 8e012f760a refactor(sas): drop DISPLAY_VALUE from getdynamiccolvals payload
Lighthouse Checks / lighthouse (pull_request) Failing after 5m44s
Build / Build-and-ng-test (pull_request) Failing after 14m0s
Build / Build-and-test-development (pull_request) Has been skipped
2026-06-08 13:10:46 +02:00
sead 8c09befd74 refactor(editor): drop unused DISPLAY_VALUE from dropdown payload 2026-06-08 13:10:19 +02:00
sead 6af49cf1fd fix(editor): preserve commas in dropdown source values 2026-06-08 11:45:35 +02:00
sead 4ea604f9fb feat(editor): add READONLY, HIDDEN, ROUND and NUMBER_FORMAT validation rules
Build / Build-and-ng-test (pull_request) Failing after 15m15s
Build / Build-and-test-development (pull_request) Has been skipped
Lighthouse Checks / lighthouse (pull_request) Failing after 20m15s
2026-06-05 21:25:13 +02:00
sead 9d97bf7ea1 fix(handsontable): restore dark mode for v17
Lighthouse Checks / lighthouse (pull_request) Failing after 17m59s
Build / Build-and-ng-test (pull_request) Failing after 17m59s
Build / Build-and-test-development (pull_request) Has been skipped
2026-06-05 14:12:11 +02:00
sead eb015d712b revert(editor): DISPLAY_VALUE change
Build / Build-and-ng-test (pull_request) Failing after 16m30s
Build / Build-and-test-development (pull_request) Has been skipped
Lighthouse Checks / lighthouse (pull_request) Failing after 31m28s
Partially revert changes from 51071b463b
2026-05-26 22:08:20 +02:00
sead 1d04f4a42c refactor(editor): add bulk validation only on HARDSELECT_HOOK; skip SOFTSELECT_HOOK
Build / Build-and-ng-test (pull_request) Failing after 17m35s
Build / Build-and-test-development (pull_request) Has been skipped
Lighthouse Checks / lighthouse (pull_request) Failing after 32m35s
2026-05-26 21:57:30 +02:00
sead 11ee49a57a feat(editor): validate autofilled cells; fix paste validation lag
Build / Build-and-ng-test (pull_request) Successful in 3m33s
Lighthouse Checks / lighthouse (pull_request) Failing after 27m56s
Build / Build-and-test-development (pull_request) Failing after 34m22s
2026-05-26 17:09:35 +02:00
sead 609731ff99 feat(editor): paste-validation overlay with cancel and confirm
Build / Build-and-ng-test (pull_request) Failing after 16m23s
Build / Build-and-test-development (pull_request) Has been skipped
Lighthouse Checks / lighthouse (pull_request) Failing after 31m23s
2026-05-26 14:38:41 +02:00
sead d6cb32ed25 refactor(licensing): expand protocl text
Build / Build-and-ng-test (pull_request) Failing after 17m14s
Build / Build-and-test-development (pull_request) Has been skipped
Lighthouse Checks / lighthouse (pull_request) Failing after 32m12s
2026-05-26 11:47:52 +02:00
sead 3668a7426f fix(licensing): add protocol info
Build / Build-and-ng-test (pull_request) Successful in 3m32s
Build / Build-and-test-development (pull_request) Failing after 20m50s
Lighthouse Checks / lighthouse (pull_request) Failing after 29m24s
Close #178
2026-05-26 11:40:40 +02:00
sead cc82dcaafe refactor(mocks): replace string webouts with JS objects
Build / Build-and-ng-test (pull_request) Failing after 17m42s
Build / Build-and-test-development (pull_request) Has been skipped
Lighthouse Checks / lighthouse (pull_request) Failing after 28m24s
2026-05-26 11:06:50 +02:00
sead ea03bdecc5 fix(editor): await dynamic validation on paste; defer spinner 2026-05-26 10:48:13 +02:00
sead 51071b463b fix(editor): cancelEdit will reset cell's valid state 2026-05-25 16:55:18 +02:00
sead ac0bd10212 fix(handsontable): horizontal scrollbar in dropdown 2026-05-25 13:59:24 +02:00
sead 1b73e355b7 fix: migrate handsontables to v17 2026-05-25 13:29:27 +02:00
sead b661580c60 fix: validate pasted values 2026-05-22 22:10:58 +02:00
sead dc4e07a692 fix: viewLibs now fires only once, libPromise shared between the calls 2026-05-22 21:57:53 +02:00
semantic-release-bot f2313b31f1 chore(release): 7.8.2 [skip ci]
## [7.8.2](https://git.datacontroller.io/dc/dc/compare/v7.8.1...v7.8.2) (2026-05-20)

### Bug Fixes

* bumping ws package ([2382a55](2382a559a5))
* enabling version restore for non admin users ([5d889d8](5d889d824c))
2026-05-20 13:20:34 +00:00
allan f8810ee7e9 Merge pull request 'fix: bumping ws package' (#234) from customerfeedback into main
Release / Build-production-and-ng-test (push) Successful in 3m17s
Release / Build-and-test-development (push) Successful in 8m27s
Release / release (push) Successful in 7m4s
Reviewed-on: #234
2026-05-20 13:05:56 +00:00
allan 8ab4af8397 Merge branch 'main' into customerfeedback
Build / Build-and-ng-test (pull_request) Successful in 3m29s
Build / Build-and-test-development (pull_request) Successful in 8m29s
Lighthouse Checks / lighthouse (pull_request) Successful in 17m59s
2026-05-20 12:45:51 +00:00
4gl 2382a559a5 fix: bumping ws package
Build / Build-and-ng-test (pull_request) Failing after 3m30s
Build / Build-and-test-development (pull_request) Has been skipped
Lighthouse Checks / lighthouse (pull_request) Failing after 3m5s
2026-05-20 13:11:13 +01:00
Unknown 5d889d824c fix: enabling version restore for non admin users
Release / Build-production-and-ng-test (push) Failing after 1m11s
Release / Build-and-test-development (push) Has been skipped
Release / release (push) Has been skipped
2026-05-20 12:38:21 +01:00
semantic-release-bot bed21122ce chore(release): 7.8.1 [skip ci]
## [7.8.1](https://git.datacontroller.io/dc/dc/compare/v7.8.0...v7.8.1) (2026-05-15)

### Bug Fixes

* **sasjs:** enable runAsTask ([f1a26e1](f1a26e132e))
2026-05-15 11:29:08 +00:00
allan ea8cf71101 Merge pull request 'fix(sasjs): enable runAsTask' (#233) from hotfix-sasjs-attributes into main
Release / Build-production-and-ng-test (push) Successful in 3m38s
Release / Build-and-test-development (push) Successful in 8m56s
Release / release (push) Successful in 8m0s
Reviewed-on: #233
2026-05-15 11:13:21 +00:00
sead f1a26e132e fix(sasjs): enable runAsTask
Build / Build-and-ng-test (pull_request) Successful in 3m57s
Build / Build-and-test-development (pull_request) Successful in 9m29s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m28s
2026-05-15 12:53:04 +02:00
semantic-release-bot 1db6984de3 chore(release): 7.8.0 [skip ci]
# [7.8.0](https://git.datacontroller.io/dc/dc/compare/v7.7.3...v7.8.0) (2026-05-15)

### Bug Fixes

* enabling DSN=*ALL* in MPE_SECURITY ([7d94cb2](7d94cb2ae4))
* providing default values for RULE_ACTIVE on MPE_VALIDATIONS ([f031b4e](f031b4eb89))
* switch away from api usage for CASLIB metadata ([ce921a0](ce921a032a))
* use correct debug param for runAsTask ([bb80476](bb80476767))

### Features

* add runAsTask config attribute parser ([1635bc9](1635bc9c45))
* enabling *ALL* option by default in MPE_SECURITY (DSN col) ([93d4ab6](93d4ab65ac))
2026-05-15 09:07:49 +00:00
allan 636ff237dd Merge pull request 'Updates following customer session' (#231) from customerfeedback into main
Release / Build-production-and-ng-test (push) Successful in 3m39s
Release / Build-and-test-development (push) Successful in 8m50s
Release / release (push) Successful in 7m39s
Reviewed-on: #231
2026-05-15 08:52:14 +00:00
sead 02963ab6d5 chore: bump adapter
Build / Build-and-ng-test (pull_request) Successful in 3m49s
Build / Build-and-test-development (pull_request) Successful in 9m2s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m12s
2026-05-15 10:29:54 +02:00
allan d40f61292a Merge pull request 'feat: add runAsTask config attribute parser' (#232) from feat/execution-tasks-flag into customerfeedback
Lighthouse Checks / lighthouse (pull_request) Failing after 3m31s
Build / Build-and-ng-test (pull_request) Failing after 3m51s
Build / Build-and-test-development (pull_request) Has been skipped
Reviewed-on: #232
2026-05-15 08:08:39 +00:00
4gl 7d94cb2ae4 fix: enabling DSN=*ALL* in MPE_SECURITY
Build / Build-and-ng-test (pull_request) Successful in 3m48s
Build / Build-and-test-development (pull_request) Successful in 8m59s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m8s
2026-05-14 22:47:17 +01:00
sead bb80476767 fix: use correct debug param for runAsTask
Lighthouse Checks / lighthouse (pull_request) Failing after 3m28s
Build / Build-and-ng-test (pull_request) Failing after 3m51s
Build / Build-and-test-development (pull_request) Has been skipped
2026-05-14 11:21:01 +02:00
sead 1635bc9c45 feat: add runAsTask config attribute parser 2026-05-14 11:19:32 +02:00
4gl f031b4eb89 fix: providing default values for RULE_ACTIVE on MPE_VALIDATIONS
Lighthouse Checks / lighthouse (pull_request) Successful in 18m12s
Build / Build-and-ng-test (pull_request) Successful in 3m40s
Build / Build-and-test-development (pull_request) Successful in 8m58s
2026-05-13 19:02:09 +01:00
4gl 93d4ab65ac feat: enabling *ALL* option by default in MPE_SECURITY (DSN col)
Build / Build-and-ng-test (pull_request) Successful in 3m53s
Build / Build-and-test-development (pull_request) Successful in 9m12s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m15s
2026-05-13 18:47:19 +01:00
4gl ce921a032a fix: switch away from api usage for CASLIB metadata 2026-05-13 18:46:35 +01:00
semantic-release-bot 322f904b4b chore(release): 7.7.3 [skip ci]
## [7.7.3](https://git.datacontroller.io/dc/dc/compare/v7.7.2...v7.7.3) (2026-05-12)

### Bug Fixes

* move cas session assign to settings.sas and abort when lib is unassigned ([65f0b97](65f0b979a4))
2026-05-12 18:43:04 +00:00
allan 982eeac58c Merge pull request 'fix: move cas session assign to settings.sas and abort when lib is unassigned' (#230) from viyaux into main
Release / Build-production-and-ng-test (push) Successful in 3m49s
Release / Build-and-test-development (push) Successful in 9m10s
Release / release (push) Successful in 8m2s
Reviewed-on: #230
2026-05-12 18:26:47 +00:00
allan 0ab9717556 Merge branch 'main' into viyaux
Build / Build-and-test-development (pull_request) Has been cancelled
Build / Build-and-ng-test (pull_request) Has been cancelled
Lighthouse Checks / lighthouse (pull_request) Successful in 18m27s
2026-05-12 18:26:35 +00:00
allan 24a85de8e1 Merge pull request 'chore(client): bump fast-uri' (#229) from audit-20260511 into main
Release / Build-production-and-ng-test (push) Successful in 3m40s
Release / Build-and-test-development (push) Successful in 8m50s
Release / release (push) Failing after 3m10s
Reviewed-on: #229
2026-05-11 17:38:14 +00:00
4gl 65f0b979a4 fix: move cas session assign to settings.sas and abort when lib is unassigned
Build / Build-and-ng-test (pull_request) Successful in 3m55s
Build / Build-and-test-development (pull_request) Successful in 9m13s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m48s
2026-05-11 15:08:20 +01:00
sead 947f34a0ad chore(client): bump fast-uri
Build / Build-and-ng-test (pull_request) Successful in 3m50s
Build / Build-and-test-development (pull_request) Successful in 9m6s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m19s
Resolve GHSA-q3j6-qgpj-74h6, GHSA-v39h-62p7-jpjc
2026-05-11 09:39:36 +02:00
semantic-release-bot 0f60fd7181 chore(release): 7.7.2 [skip ci]
## [7.7.2](https://git.datacontroller.io/dc/dc/compare/v7.7.1...v7.7.2) (2026-05-07)

### Bug Fixes

* **client:** bundle Metropolis font locally to satisfy CSP ([9546fcd](9546fcd631))
* **client:** clear angular build cache on font strip to avoid stale dist ([503cb08](503cb08b2f))
* **client:** postinstall removal of Metropolis [@font-face](https://git.datacontroller.io/font-face) from @clr/ui ([e6397ce](e6397cecc1))
* **client:** serve text-security-disc font locally ([80ce80e](80ce80ece4))
* **editor:** preserve numeric type for SAS num cols with static SOFTSELECT/HARDSELECT ([05a3289](05a328976e))
2026-05-07 15:18:44 +00:00
allan 251062e42e Merge pull request 'Multiple frontend client issues' (#228) from 227-csp-issues-20260507 into main
Release / Build-production-and-ng-test (push) Successful in 3m38s
Release / Build-and-test-development (push) Successful in 8m50s
Release / release (push) Successful in 7m35s
Reviewed-on: #228
2026-05-07 15:03:08 +00:00
sead 05a328976e fix(editor): preserve numeric type for SAS num cols with static SOFTSELECT/HARDSELECT
Build / Build-and-ng-test (pull_request) Successful in 3m43s
Build / Build-and-test-development (pull_request) Successful in 9m21s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m25s
2026-05-07 15:02:06 +02:00
sead 503cb08b2f fix(client): clear angular build cache on font strip to avoid stale dist 2026-05-07 13:43:57 +02:00
sead f71be20476 build(client): drop duplicate asset emits for fonts and CSS-referenced svgs 2026-05-07 13:43:49 +02:00
sead e6397cecc1 fix(client): postinstall removal of Metropolis @font-face from @clr/ui 2026-05-07 13:43:37 +02:00
sead 80ce80ece4 fix(client): serve text-security-disc font locally 2026-05-07 13:43:21 +02:00
sead 9546fcd631 fix(client): bundle Metropolis font locally to satisfy CSP 2026-05-07 13:43:05 +02:00
semantic-release-bot b79aaf4327 chore(release): 7.7.1 [skip ci]
## [7.7.1](https://git.datacontroller.io/dc/dc/compare/v7.7.0...v7.7.1) (2026-05-05)

### Bug Fixes

* **client:** bump adapter ([d26f7d2](d26f7d2511))
* **sas:** bump cli ([d60029d](d60029deae))
2026-05-05 20:04:33 +00:00
allan 76f9198f73 Merge pull request 'fix(client): bump adapter' (#226) from fix/adapter-20260505 into main
Release / Build-production-and-ng-test (push) Successful in 3m28s
Release / Build-and-test-development (push) Successful in 8m44s
Release / release (push) Successful in 7m33s
Reviewed-on: #226
2026-05-05 19:49:17 +00:00
4gl d60029deae fix(sas): bump cli
Build / Build-and-ng-test (pull_request) Successful in 3m57s
Build / Build-and-test-development (pull_request) Successful in 9m9s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m10s
2026-05-05 18:48:15 +01:00
sead d26f7d2511 fix(client): bump adapter
Build / Build-and-ng-test (pull_request) Successful in 4m21s
Build / Build-and-test-development (pull_request) Successful in 10m34s
Lighthouse Checks / lighthouse (pull_request) Successful in 19m37s
2026-05-05 17:16:55 +02:00
semantic-release-bot 33efe09b50 chore(release): 7.7.0 [skip ci]
# [7.7.0](https://git.datacontroller.io/dc/dc/compare/v7.6.0...v7.7.0) (2026-05-04)

### Bug Fixes

* bump adapter to 4.16.6 ([1707f38](1707f3802a))
* remove data:image/svg+xml CSP violation, use class instead changing style directly ([d66eb5d](d66eb5dfc2))
* remove WORK, SASUSER and CASUSER as library options.  [#224](#224) ([ec66631](ec66631a33))

### Features

* auto-save CAS tables [#224](#224) ([40d04a5](40d04a53c4))
* autoload CAS tables. [#224](#224) ([d5ebb01](d5ebb01ce3))
2026-05-04 23:24:36 +00:00
4gl e0aef9bf00 chore: pin got lib to enable release flow
Release / Build-production-and-ng-test (push) Successful in 11m47s
Release / Build-and-test-development (push) Successful in 17m37s
Release / release (push) Successful in 8m18s
2026-05-04 23:51:19 +01:00
allan 02d1a2e0b1 Merge pull request 'fix: resolve CSP violation and update dependancies' (#223) from fix/audit-20260413 into main
Release / Build-production-and-ng-test (push) Successful in 8m48s
Release / Build-and-test-development (push) Successful in 17m50s
Release / release (push) Failing after 5m52s
Reviewed-on: #223
Reviewed-by: allan <allan@4gl.io>
2026-05-04 16:50:52 +00:00
sead 4e3154e929 chore(cypress): enable e2e video, folder guards
Build / Build-and-ng-test (pull_request) Successful in 3m58s
Build / Build-and-test-development (pull_request) Successful in 9m6s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m21s
2026-05-04 12:57:31 +02:00
sead 32c0713256 chore: add hyperformula license exception
Build / Build-and-ng-test (pull_request) Successful in 3m44s
Build / Build-and-test-development (pull_request) Failing after 8m45s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m29s
2026-05-04 11:05:00 +02:00
sead defe15bcec chore: bump client eslint and sas sasjs packages
Build / Build-and-ng-test (pull_request) Failing after 1m34s
Build / Build-and-test-development (pull_request) Has been skipped
Lighthouse Checks / lighthouse (pull_request) Failing after 1m47s
2026-05-04 10:42:31 +02:00
4gl 6f8e471f16 chore: dep man
Build / Build-and-ng-test (pull_request) Failing after 40s
Build / Build-and-test-development (pull_request) Has been skipped
Lighthouse Checks / lighthouse (pull_request) Failing after 55s
2026-05-01 12:53:07 +01:00
4gl dc35abfd85 chore: dependency bumps
Build / Build-and-ng-test (pull_request) Failing after 42s
Build / Build-and-test-development (pull_request) Has been skipped
Lighthouse Checks / lighthouse (pull_request) Failing after 1m0s
2026-05-01 12:36:39 +01:00
4gl 04a8c5d52a chore: bumping node
Build / Build-and-ng-test (pull_request) Failing after 50s
Build / Build-and-test-development (pull_request) Has been skipped
Lighthouse Checks / lighthouse (pull_request) Failing after 57s
2026-05-01 11:46:10 +01:00
4gl 2cb370053d chore: rebuilt package lock
Build / Build-and-ng-test (pull_request) Failing after 42s
Build / Build-and-test-development (pull_request) Has been skipped
Lighthouse Checks / lighthouse (pull_request) Failing after 1m3s
2026-05-01 11:34:30 +01:00
4gl 1707f3802a fix: bump adapter to 4.16.6
Lighthouse Checks / lighthouse (pull_request) Failing after 1m7s
Build / Build-and-ng-test (pull_request) Failing after 1m44s
Build / Build-and-test-development (pull_request) Has been skipped
2026-05-01 10:59:59 +01:00
allan c87ba660ca Merge branch 'main' into fix/audit-20260413
Build / Build-and-ng-test (pull_request) Successful in 3m58s
Build / Build-and-test-development (pull_request) Successful in 9m56s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m30s
2026-04-30 16:44:46 +00:00
allan ef8a2dbc38 Merge pull request 'fix: remove WORK, SASUSER and CASUSER as library options, plus auto CAS table load' (#225) from issue224 into main
Release / Build-production-and-ng-test (push) Failing after 1m28s
Release / Build-and-test-development (push) Has been skipped
Release / release (push) Has been skipped
Reviewed-on: #225
2026-04-30 15:28:50 +00:00
4gl 40d04a53c4 feat: auto-save CAS tables #224
Build / Build-and-ng-test (pull_request) Successful in 4m2s
Build / Build-and-test-development (pull_request) Successful in 10m6s
Lighthouse Checks / lighthouse (pull_request) Successful in 19m5s
2026-04-30 16:04:31 +01:00
4gl d5ebb01ce3 feat: autoload CAS tables. #224
Build / Build-and-ng-test (pull_request) Successful in 4m6s
Build / Build-and-test-development (pull_request) Successful in 10m9s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m54s
2026-04-30 15:41:20 +01:00
allan ec66631a33 fix: remove WORK, SASUSER and CASUSER as library options. #224
Build / Build-and-ng-test (pull_request) Successful in 4m15s
Build / Build-and-test-development (pull_request) Successful in 10m28s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m47s
2026-04-17 14:21:24 +01:00
sead d66eb5dfc2 fix: remove data:image/svg+xml CSP violation, use class instead changing style directly
Build / Build-and-ng-test (pull_request) Failing after 1m16s
Build / Build-and-test-development (pull_request) Has been skipped
Lighthouse Checks / lighthouse (pull_request) Successful in 18m7s
2026-04-13 10:29:54 +02:00
sead 731b589ed8 chore: override ajv and regenrate lock file 2026-04-13 09:23:13 +02:00
sead fe92d5fc36 chore: bump angular to latest 19 2026-04-13 08:58:54 +02:00
sead a335b400f1 chore: bump @sasjs/adapter 2026-04-13 08:55:48 +02:00
semantic-release-bot f63e507ddf chore(release): 7.6.0 [skip ci]
# [7.6.0](https://git.datacontroller.io/dc/dc/compare/v7.5.0...v7.6.0) (2026-04-03)

### Bug Fixes

* add label and tooltip for libref download, sanitise input ([52d5803](52d58036a4))

### Features

* configurable email alerts.  Closes [#217](#217) ([2ccf0d1](2ccf0d1100))
2026-04-03 22:17:14 +00:00
allan 991cc0567d Merge pull request 'feat: configurable email alerts. Closes #217' (#222) from issue217 into main
Release / Build-production-and-ng-test (push) Successful in 3m42s
Release / Build-and-test-development (push) Successful in 10m10s
Release / release (push) Successful in 7m48s
Reviewed-on: #222
2026-04-03 21:09:11 +00:00
sead 52d58036a4 fix: add label and tooltip for libref download, sanitise input
Build / Build-and-ng-test (pull_request) Successful in 4m6s
Build / Build-and-test-development (pull_request) Successful in 10m13s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m37s
2026-04-03 19:55:42 +02:00
allan 26bff85792 chore: fix debug line
Build / Build-and-ng-test (pull_request) Successful in 4m47s
Build / Build-and-test-development (pull_request) Successful in 10m16s
Lighthouse Checks / lighthouse (pull_request) Successful in 19m41s
2026-04-03 18:35:48 +01:00
allan 2ccf0d1100 feat: configurable email alerts. Closes #217
Build / Build-and-ng-test (pull_request) Successful in 4m42s
Build / Build-and-test-development (pull_request) Has been cancelled
Lighthouse Checks / lighthouse (pull_request) Has been cancelled
2026-04-03 18:34:23 +01:00
semantic-release-bot 3be33186bc chore(release): 7.5.0 [skip ci]
# [7.5.0](https://git.datacontroller.io/dc/dc/compare/v7.4.1...v7.5.0) (2026-04-03)

### Bug Fixes

* add workflow audits, update deps ([66e98a9](66e98a96cb))
* allow CSV uploads with licence row limit ([5b260e4](5b260e4915)), closes [#213](#213)
* bumping cli and pinning versions in .npmrc ([80039f4](80039f4876))
* guard CSV upload with fileUpload licence flag ([ed40df6](ed40df6295))
* parse embed param from window.location.hash for hash router compatibility ([0269c24](0269c2421d))
* quote CSV char values.  Closes [#215](#215) ([d9980e8](d9980e866d))
* resolve outer promise in parseCsvFile for non-WLATIN1 path ([4ee15e1](4ee15e1b6e))
* use XLSX for CSV row truncation to handle new lines in values ([6d590c0](6d590c050d))

### Features

* add embed URL parameter to hide header and back button ([b0dc441](b0dc441d68)), closes [#214](#214)
* add target libref input to config download ([a89657b](a89657b0b8)), closes [#212](#212)
* export config service to allow dclib swapping.  Closes [#212](#212) ([326c26f](326c26fddf))
2026-04-03 11:06:36 +00:00
allan 1a7f950ae2 Merge pull request 'feat: enabling dclib switching when exporting config' (#220) from issue212 into main
Release / Build-production-and-ng-test (push) Successful in 3m39s
Release / Build-and-test-development (push) Successful in 9m55s
Release / release (push) Successful in 7m46s
Reviewed-on: #220
2026-04-03 10:49:43 +00:00
allan 8924dc8ab1 chore: merge buid.yaml
Build / Build-and-ng-test (pull_request) Successful in 3m58s
Build / Build-and-test-development (pull_request) Successful in 10m3s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m46s
2026-04-03 10:30:05 +00:00
sead 2c2901b537 chore: rever upload artifacts actions version
Build / Build-and-ng-test (pull_request) Successful in 4m3s
Build / Build-and-test-development (pull_request) Successful in 10m0s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m24s
2026-04-03 10:32:28 +02:00
sead 2cae7ea638 chore: improve CI workflows
Build / Build-and-ng-test (pull_request) Successful in 4m3s
Build / Build-and-test-development (pull_request) Failing after 10m17s
Lighthouse Checks / lighthouse (pull_request) Failing after 18m31s
2026-04-03 09:36:39 +02:00
sead 66e98a96cb fix: add workflow audits, update deps
Build / Build-and-ng-test (pull_request) Successful in 4m2s
Build / Build-and-test-development (pull_request) Successful in 10m19s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m53s
2026-04-03 09:10:49 +02:00
allan 0b0db1c543 chore: run audit check in build.yaml as well as release.yaml
Build / Build-and-ng-test (pull_request) Failing after 1m31s
Build / Build-and-test-development (pull_request) Successful in 10m23s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 19m32s
2026-04-03 01:18:54 +00:00
allan 80039f4876 fix: bumping cli and pinning versions in .npmrc
Build / Build-and-ng-test (pull_request) Successful in 3m51s
Build / Build-and-test-development (pull_request) Successful in 10m9s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 19m11s
2026-04-03 02:02:05 +01:00
allan 326c26fddf feat: export config service to allow dclib swapping. Closes #212 2026-04-03 02:01:44 +01:00
allan e7b2ead0e2 Merge pull request 'fix: allow CSV uploads with licence row limit' (#219) from fix/213-csv-license-row-limit into main
Release / Build-production-and-ng-test (push) Failing after 1m25s
Release / Build-and-test-development (push) Has been skipped
Release / release (push) Has been skipped
Reviewed-on: #219
2026-04-02 19:08:18 +00:00
sead a89657b0b8 feat: add target libref input to config download
Build / Build-and-ng-test (pull_request) Successful in 4m5s
Build / Build-and-test-development (pull_request) Successful in 10m16s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m30s
Closes #212
2026-04-02 19:37:55 +02:00
sead 4ee15e1b6e fix: resolve outer promise in parseCsvFile for non-WLATIN1 path
Build / Build-and-ng-test (pull_request) Successful in 3m55s
Build / Build-and-test-development (pull_request) Successful in 10m21s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m49s
2026-04-02 18:48:27 +02:00
sead ed40df6295 fix: guard CSV upload with fileUpload licence flag
Build / Build-and-ng-test (pull_request) Successful in 4m3s
Build / Build-and-test-development (pull_request) Failing after 11m54s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m35s
2026-04-02 17:40:16 +02:00
sead 6d590c050d fix: use XLSX for CSV row truncation to handle new lines in values
Build / Build-and-ng-test (pull_request) Successful in 3m53s
Build / Build-and-test-development (pull_request) Successful in 10m25s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m29s
2026-04-02 17:03:16 +02:00
allan 47f9a54f97 Merge pull request 'feat: add embed URL parameter to hide header and back button' (#218) from feat/214-hide-titlebar-embed into fix/213-csv-license-row-limit
Build / Build-and-ng-test (pull_request) Successful in 4m0s
Build / Build-and-test-development (pull_request) Successful in 10m18s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m56s
Reviewed-on: #218
2026-04-02 14:37:06 +00:00
sead 17b0d72fbf test: add csv-limited spec to cypress workflow
Build / Build-and-ng-test (pull_request) Successful in 4m1s
Build / Build-and-test-development (pull_request) Successful in 10m23s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 19m1s
2026-04-02 16:13:35 +02:00
sead 0269c2421d fix: parse embed param from window.location.hash for hash router compatibility
Build / Build-and-ng-test (pull_request) Successful in 4m9s
Build / Build-and-test-development (pull_request) Successful in 10m9s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 19m8s
2026-04-02 14:57:16 +02:00
sead 5b260e4915 fix: allow CSV uploads with licence row limit
Build / Build-and-ng-test (pull_request) Successful in 3m56s
Build / Build-and-test-development (pull_request) Successful in 10m3s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m49s
Fixes #213
2026-04-02 14:34:58 +02:00
allan 5290410a17 Merge branch 'main' into feat/214-hide-titlebar-embed
Build / Build-and-ng-test (pull_request) Successful in 3m56s
Build / Build-and-test-development (pull_request) Successful in 10m11s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m31s
2026-04-02 11:13:49 +00:00
allan dc9041aaec Merge pull request 'fix: quote CSV char values. Closes #215' (#216) from issue215 into main
Release / Build-production-and-ng-test (push) Failing after 1m25s
Release / Build-and-test-development (push) Has been skipped
Release / release (push) Has been skipped
Reviewed-on: #216
2026-04-02 11:12:38 +00:00
sead b0dc441d68 feat: add embed URL parameter to hide header and back button
Build / Build-and-ng-test (pull_request) Successful in 4m3s
Build / Build-and-test-development (pull_request) Successful in 10m6s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m39s
Closes #214
2026-04-02 11:26:28 +02:00
allan b0fc3eb5af chore: update comment
Build / Build-and-ng-test (pull_request) Successful in 4m23s
Build / Build-and-test-development (pull_request) Successful in 10m7s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 19m13s
2026-03-31 17:09:17 +01:00
allan d9980e866d fix: quote CSV char values. Closes #215
Build / Build-and-ng-test (pull_request) Successful in 4m7s
Build / Build-and-test-development (pull_request) Has been cancelled
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Has been cancelled
2026-03-31 17:04:46 +01:00
semantic-release-bot 52ae3404ee chore(release): 7.4.1 [skip ci]
## [7.4.1](https://git.datacontroller.io/dc/dc/compare/v7.4.0...v7.4.1) (2026-03-12)

### Bug Fixes

* support for SASIOSNF engine (SNOW alias) plus meta assignment ([7694d1b](7694d1b0fb))
2026-03-12 00:52:17 +00:00
allan eecb4f4f53 Merge pull request 'fix: support for SASIOSNF engine (SNOW alias) plus meta assignment' (#209) from snowfixes into main
Release / Build-production-and-ng-test (push) Successful in 3m41s
Release / Build-and-test-development (push) Successful in 9m52s
Release / release (push) Successful in 7m57s
Reviewed-on: #209
2026-03-12 00:35:16 +00:00
allan 744345af81 chore: bump sasjs/cli
Build / Build-and-ng-test (pull_request) Successful in 3m51s
Build / Build-and-test-development (pull_request) Successful in 9m58s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m48s
2026-03-12 00:16:10 +00:00
_ 7694d1b0fb fix: support for SASIOSNF engine (SNOW alias) plus meta assignment
Build / Build-and-ng-test (pull_request) Successful in 3m46s
Build / Build-and-test-development (pull_request) Successful in 9m38s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m18s
2026-03-10 23:50:57 +00:00
semantic-release-bot d8010d4c0c chore(release): 7.4.0 [skip ci]
# [7.4.0](https://git.datacontroller.io/dc/dc/compare/v7.3.0...v7.4.0) (2026-02-20)

### Bug Fixes

* cli bump for mf_getscheme support ([a84ba41](a84ba41ea9))
* missing upcase on SNOW section, plus local sasjs target ([dc20064](dc200646f7))

### Features

* SAS code changes for snowflake support ([e273e87](e273e870ef))
2026-02-20 18:53:31 +00:00
allan a57b49c936 Merge pull request 'feat: SAS code changes for snowflake support' (#208) from sf into main
Release / Build-production-and-ng-test (push) Successful in 4m0s
Release / Build-and-test-development (push) Successful in 9m57s
Release / release (push) Successful in 7m57s
Reviewed-on: #208
2026-02-20 18:36:00 +00:00
allan a84ba41ea9 fix: cli bump for mf_getscheme support
Build / Build-and-ng-test (pull_request) Successful in 4m10s
Build / Build-and-test-development (pull_request) Successful in 10m4s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m58s
2026-02-20 18:15:31 +00:00
allan dc200646f7 fix: missing upcase on SNOW section, plus local sasjs target
Build / Build-and-ng-test (pull_request) Successful in 4m11s
Build / Build-and-test-development (pull_request) Successful in 9m55s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m25s
2026-02-20 01:08:55 +00:00
allan e273e870ef feat: SAS code changes for snowflake support
Build / Build-and-ng-test (pull_request) Successful in 4m9s
Build / Build-and-test-development (pull_request) Successful in 10m1s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m57s
2026-02-20 00:15:23 +00:00
semantic-release-bot 6fc34aca00 chore(release): 7.3.0 [skip ci]
# [7.3.0](https://git.datacontroller.io/dc/dc/compare/v7.2.8...v7.3.0) (2026-02-10)

### Bug Fixes

* bump xlsx, add crypto-shim ([8dc18b1](8dc18b155a))
* correctly applying deletes on viya, also ([46cdeb0](46cdeb0bab))
* crypto module requirement for sheetjs/crypto package ([505d0af](505d0af2b3))
* disable parsing excel in web worker beacuse it breaks in the stream apps ([280bdee](280bdeeb1b))
* Display all contexts when installing DC on Viya ([d41f88f](d41f88f8bf))
* **edit:** use cellValidation keys and hotDataSchema to fill in defaults on add row ([4957548](495754816c))
* enabling closeouts for UPDATE in CAS tables ([8b8e8ae](8b8e8aec15))
* enabling rollback when the table has formatted values ([815d6e9](815d6e97a8))
* improvements to validations ([6ceb681](6ceb681463))
* remove IE checks and conditions ([ece6bd1](ece6bd1d78))
* updates to demodata to enable auto CAS promote ([7740d2a](7740d2ac86))
* upgrade angular core and compiler ([aecd597](aecd597687))
* using fcopy instead of binary copy for file upload, for Viya 2026 compatibility ([716ee6e](716ee6eba0))
* **viewer:** search causing blank Handsontable ([338c7a2](338c7a2e41)), closes [#206](#206)

### Features

* adding demo data job ([8c2aeac](8c2aeacc85))
* **dq rules:** notnull validation when invalid cell, will auto populate a default value ([96f2518](96f2518af9))
2026-02-10 19:17:29 +00:00
allan f97ac70678 Merge pull request 'demodata' (#203) from demodata into main
Release / Build-production-and-ng-test (push) Successful in 3m27s
Release / Build-and-test-development (push) Successful in 9m22s
Release / release (push) Successful in 7m53s
Reviewed-on: #203
2026-02-10 19:01:17 +00:00
allan 6ceb681463 fix: improvements to validations
Build / Build-and-ng-test (pull_request) Successful in 3m47s
Build / Build-and-test-development (pull_request) Successful in 9m38s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m21s
2026-02-10 18:26:02 +00:00
allan 716ee6eba0 fix: using fcopy instead of binary copy for file upload, for Viya 2026 compatibility
Build / Build-and-ng-test (pull_request) Successful in 3m42s
Build / Build-and-test-development (pull_request) Successful in 9m33s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m44s
2026-02-10 16:34:09 +00:00
allan f6b0f6b0cd Merge pull request 'fix(viewer): search causing blank Handsontable' (#207) from fix/206-search-issue into demodata
Build / Build-and-ng-test (pull_request) Successful in 3m39s
Build / Build-and-test-development (pull_request) Successful in 9m37s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m31s
Reviewed-on: #207
2026-02-10 15:50:42 +00:00
sead 737a652ff0 refactor(viewer): use drop instead of debounce
Build / Build-and-ng-test (pull_request) Successful in 3m40s
Build / Build-and-test-development (pull_request) Successful in 9m31s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m32s
2026-02-10 15:36:20 +01:00
sead 2995e5c9dc chore: restore comments and condition
Build / Build-and-ng-test (pull_request) Successful in 3m52s
Build / Build-and-test-development (pull_request) Successful in 9m39s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 19m8s
2026-02-10 14:37:56 +01:00
sead 338c7a2e41 fix(viewer): search causing blank Handsontable
Build / Build-and-ng-test (pull_request) Successful in 3m46s
Build / Build-and-test-development (pull_request) Successful in 9m26s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m52s
Closes #206
2026-02-10 14:24:06 +01:00
allan ad27358deb Merge pull request 'fix(edit): use cellValidation keys and hotDataSchema to fill in defaults on add row' (#205) from fix/204-default-value into demodata
Build / Build-and-ng-test (pull_request) Successful in 4m15s
Build / Build-and-test-development (pull_request) Successful in 10m18s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m50s
Reviewed-on: #205
2026-02-10 12:58:15 +00:00
sead 495754816c fix(edit): use cellValidation keys and hotDataSchema to fill in defaults on add row
Build / Build-and-ng-test (pull_request) Successful in 3m40s
Build / Build-and-test-development (pull_request) Successful in 10m33s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 26m51s
2026-02-10 13:53:29 +01:00
M 96f2518af9 feat(dq rules): notnull validation when invalid cell, will auto populate a default value
Build / Build-and-ng-test (pull_request) Successful in 3m41s
Build / Build-and-test-development (pull_request) Successful in 9m30s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m50s
2026-02-10 12:29:15 +01:00
M 280bdeeb1b fix: disable parsing excel in web worker beacuse it breaks in the stream apps
Build / Build-and-ng-test (pull_request) Successful in 3m41s
Build / Build-and-test-development (pull_request) Successful in 9m41s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m0s
2026-02-10 11:58:00 +01:00
allan 46cdeb0bab fix: correctly applying deletes on viya, also
Build / Build-and-ng-test (pull_request) Successful in 3m32s
Build / Build-and-test-development (pull_request) Successful in 9m14s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m18s
adding more info to the staged directory in relation to deletes.  Also
refactoring the demo data.
2026-02-09 23:23:08 +00:00
allan d41f88f8bf fix: Display all contexts when installing DC on Viya
Build / Build-and-ng-test (pull_request) Successful in 3m58s
Build / Build-and-test-development (pull_request) Successful in 9m48s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m29s
2026-02-09 18:22:06 +00:00
allan 815d6e97a8 fix: enabling rollback when the table has formatted values
Build / Build-and-ng-test (pull_request) Successful in 3m35s
Build / Build-and-test-development (pull_request) Successful in 9m16s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m4s
2026-02-08 02:40:07 +00:00
allan 4e35aefe41 Merge pull request 'Upgrade angular core and compiler' (#199) from fix/audit-20260112 into demodata
Build / Build-and-ng-test (pull_request) Successful in 3m59s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 19m16s
Build / Build-and-test-development (pull_request) Successful in 9m48s
Reviewed-on: #199
Reviewed-by: mihajlo <mihajlo@4gl.io>
2026-02-07 23:46:19 +00:00
allan ca84915e43 Merge branch 'demodata' into fix/audit-20260112
Build / Build-and-ng-test (pull_request) Successful in 4m15s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 19m43s
Build / Build-and-test-development (pull_request) Successful in 9m56s
2026-02-07 23:46:10 +00:00
zver 31cc7e9e4d chore: server release and more demodata
Build / Build-and-ng-test (pull_request) Successful in 4m3s
Build / Build-and-test-development (pull_request) Successful in 9m46s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 19m52s
2026-02-07 23:44:37 +00:00
allan 4ec107705e Merge branch 'main' into demodata
Build / Build-and-ng-test (pull_request) Successful in 3m57s
Build / Build-and-test-development (pull_request) Successful in 9m9s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Failing after 3h13m38s
2026-02-07 20:04:16 +00:00
zver 7740d2ac86 fix: updates to demodata to enable auto CAS promote
Build / Build-and-ng-test (pull_request) Successful in 3m36s
Build / Build-and-test-development (pull_request) Successful in 9m2s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Failing after 3h13m50s
2026-02-07 20:02:14 +00:00
zver 8c2aeacc85 feat: adding demo data job 2026-02-06 20:50:55 +00:00
zver 8b8e8aec15 fix: enabling closeouts for UPDATE in CAS tables 2026-02-06 20:50:38 +00:00
semantic-release-bot 6ac3f660e9 chore(release): 7.2.8 [skip ci]
## [7.2.8](https://git.datacontroller.io/dc/dc/compare/v7.2.7...v7.2.8) (2026-02-06)

### Bug Fixes

* bump adapter version ([f4c8699](f4c8699aaf))
2026-02-06 12:51:50 +00:00
sead 7ee576a9c1 Merge pull request 'Update @sasjs/adapter version' (#202) from fix/bump-adapter-20260206 into main
Release / Build-production-and-ng-test (push) Successful in 3m32s
Release / Build-and-test-development (push) Successful in 9m7s
Release / release (push) Successful in 7m39s
Reviewed-on: #202
2026-02-06 12:35:55 +00:00
sead f4c8699aaf fix: bump adapter version
Build / Build-and-ng-test (pull_request) Successful in 3m48s
Build / Build-and-test-development (pull_request) Successful in 9m12s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m14s
2026-02-06 13:12:51 +01:00
zver 4273ca6e5c chore: demo data job 2026-02-06 02:13:23 +00:00
semantic-release-bot 4ba043b77e chore(release): 7.2.7 [skip ci]
## [7.2.7](https://git.datacontroller.io/dc/dc/compare/v7.2.6...v7.2.7) (2026-02-05)

### Bug Fixes

* dclib not found error in getchangeinfo job ([86791db](86791dbaca))
2026-02-05 19:15:36 +00:00
allan 0169415ea2 Merge pull request 'fix: dclib not found error in getchangeinfo job' (#201) from dclibfix into main
Release / Build-production-and-ng-test (push) Successful in 3m32s
Release / Build-and-test-development (push) Successful in 9m15s
Release / release (push) Successful in 7m56s
Reviewed-on: #201
2026-02-05 18:59:19 +00:00
allan 86791dbaca fix: dclib not found error in getchangeinfo job
Build / Build-and-ng-test (pull_request) Successful in 3m48s
Build / Build-and-test-development (pull_request) Successful in 9m21s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m34s
2026-02-05 16:53:23 +00:00
sead d5b58a3cbd test(excel): password tests - click away overlay modal
Build / Build-and-ng-test (pull_request) Successful in 3m39s
Build / Build-and-test-development (pull_request) Successful in 9m28s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 17m38s
2026-01-14 17:38:36 +01:00
sead 3d8281d27e test(excel): fix attachFile upload for password protected tests
Build / Build-and-ng-test (pull_request) Successful in 3m43s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 19m3s
Build / Build-and-test-development (pull_request) Failing after 11m3s
2026-01-14 15:47:10 +01:00
sead b1a014c7bc test(excel): add password protected excels tests
Build / Build-and-ng-test (pull_request) Successful in 3m36s
Build / Build-and-test-development (pull_request) Failing after 10m17s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m22s
2026-01-13 18:50:17 +01:00
sead 505d0af2b3 fix: crypto module requirement for sheetjs/crypto package
Undid the crypto-shim changes from 8dc18b155a
2026-01-13 18:41:56 +01:00
sead ece6bd1d78 fix: remove IE checks and conditions 2026-01-13 17:00:56 +01:00
sead 8dc18b155a fix: bump xlsx, add crypto-shim
Build / Build-and-ng-test (pull_request) Successful in 3m29s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m24s
Build / Build-and-test-development (pull_request) Successful in 9m33s
crypto-shim fixes vulnerable crypto-browserify package used by sheetjs/crypto, shim is based on crypto-js
2026-01-13 15:04:17 +01:00
sead aecd597687 fix: upgrade angular core and compiler
Build / Build-and-ng-test (pull_request) Successful in 3m50s
Build / Build-and-test-development (pull_request) Successful in 9m8s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m27s
2026-01-13 08:38:29 +01:00
allan c99f106bae Merge pull request 'Fix lighthouse ci save path' (#198) from fix/lighthouse-20251230 into main
Release / Build-production-and-ng-test (push) Successful in 3m31s
Release / Build-and-test-development (push) Successful in 9m14s
Release / release (push) Failing after 3m16s
Reviewed-on: #198
Reviewed-by: mihajlo <mihajlo@4gl.io>
2026-01-08 11:38:32 +00:00
allan d2fc7ae6fe Merge branch 'main' into fix/lighthouse-20251230
Build / Build-and-ng-test (pull_request) Successful in 4m15s
Build / Build-and-test-development (pull_request) Successful in 9m42s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 19m16s
2026-01-08 11:38:25 +00:00
semantic-release-bot c1c1d0055a chore(release): 7.2.6 [skip ci]
## [7.2.6](https://git.datacontroller.io/dc/dc/compare/v7.2.5...v7.2.6) (2026-01-05)

### Bug Fixes

* **deps:** update angular and moment ([8c5b357](8c5b357dd2))
2026-01-05 13:56:25 +00:00
sead f37ec82d39 Merge branch 'main' into fix/lighthouse-20251230
Build / Build-and-ng-test (pull_request) Successful in 4m4s
Build / Build-and-test-development (pull_request) Successful in 10m9s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 19m4s
2026-01-05 13:39:20 +00:00
sead 6e9e30e0f0 Merge pull request 'npm audit vulnerabilities' (#197) from fix/audit-20251229 into main
Release / Build-production-and-ng-test (push) Successful in 3m54s
Release / Build-and-test-development (push) Successful in 10m5s
Release / release (push) Successful in 7m57s
Reviewed-on: #197
Reviewed-by: mihajlo <mihajlo@4gl.io>
2026-01-05 13:39:02 +00:00
sead 990ddb5cd3 chore: update .gitignore
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Has been cancelled
Build / Build-and-test-development (pull_request) Has been cancelled
Build / Build-and-ng-test (pull_request) Has been cancelled
2025-12-30 14:55:33 +01:00
sead c6ebbb48bb ci: fix lighthouse artifacts save path
Build / Build-and-ng-test (pull_request) Successful in 3m55s
Build / Build-and-test-development (pull_request) Successful in 8m9s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m30s
2025-12-30 14:49:01 +01:00
sead 95cc0b1c91 chore: undo unnecessary changes
Build / Build-and-ng-test (pull_request) Successful in 3m49s
Build / Build-and-test-development (pull_request) Successful in 9m19s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m34s
2025-12-30 13:42:20 +01:00
sead 81b282f1f1 chore: revert from esbuild back to webpack
Build / Build-and-ng-test (pull_request) Successful in 3m41s
Build / Build-and-test-development (pull_request) Successful in 9m3s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m31s
2025-12-29 23:14:51 +01:00
sead 8c5b357dd2 fix(deps): update angular and moment
Build / Build-and-ng-test (pull_request) Successful in 2m28s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Has been cancelled
Build / Build-and-test-development (pull_request) Has been cancelled
2025-12-29 22:15:47 +01:00
sead a13b2cbfd2 chore: angular update from v18 to v19 2025-12-29 18:23:21 +01:00
sead 19617c2285 chore: ng update cdk 2025-12-29 17:01:48 +01:00
sead f9794a973f chore: ng update cli, schematics, and core 2025-12-29 16:53:33 +01:00
sead 65efe62d19 chore: prettier 2025-12-29 16:46:40 +01:00
sead 683ddcaf53 chore: add ng command 2025-12-29 16:46:11 +01:00
semantic-release-bot 113e0bbc3c chore(release): 7.2.5 [skip ci]
## [7.2.5](https://git.datacontroller.io/dc/dc/compare/v7.2.4...v7.2.5) (2025-12-09)

### Bug Fixes

* (build) rebuilt package-lock files ([bfbfd55](bfbfd55fe7))
* (deps) bump @sasjs/cli and @sasjs/core ([d7c7302](d7c7302c12))
* (deps) bumped @sasjs/core, @sasjs/cli, @sasjs/utils and @sasjs/adapter ([af1657e](af1657e226))
* configurable audit table on restore check ([26ce95f](26ce95f7c1)), closes [#193](#193)
* improved testing ([fb3c49a](fb3c49aa8b))
* output values to intended macro variables ([43ae73c](43ae73c5f3))
2025-12-09 12:27:01 +00:00
allan 2af97e40bf Merge pull request 'fix: (build) bump node version for pipelines' (#196) from node_version_bump_for_pipelines into main
Release / Build-production-and-ng-test (push) Successful in 3m23s
Release / Build-and-test-development (push) Successful in 7m59s
Release / release (push) Successful in 7m3s
Reviewed-on: #196
2025-12-09 12:12:31 +00:00
Trevor Moody 83cbe3aece chore: (build) bump node version for pipelines
Build / Build-and-ng-test (pull_request) Successful in 3m36s
Build / Build-and-test-development (pull_request) Successful in 8m10s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 18m11s
2025-12-09 11:49:56 +00:00
allan ceac1ba614 Merge pull request 'bumpCoreAndCli_20251125' (#195) from bumpCoreAndCli_20251125 into main
Release / Build-production-and-ng-test (push) Successful in 3m39s
Release / Build-and-test-development (push) Successful in 8m11s
Release / release (push) Failing after 3m19s
Reviewed-on: #195
2025-12-08 19:02:14 +00:00
allan 765fdbdf9d chore: merge
Build / Build-and-ng-test (pull_request) Successful in 3m33s
Build / Build-and-test-development (pull_request) Successful in 8m16s
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Successful in 18m46s
2025-12-08 17:41:07 +00:00
Trevor Moody 43ae73c5f3 fix: output values to intended macro variables
Build / Build-and-ng-test (pull_request) Successful in 3m33s
Build / Build-and-test-development (pull_request) Successful in 8m19s
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Successful in 19m13s
2025-12-08 16:55:22 +00:00
Trevor Moody e57a0de8a9 chore: Re-basing file due to merge conflict 2025-12-08 16:53:03 +00:00
Trevor Moody 3de491105b chore: (build) amended licenseChecker.js exclusions
Build / Build-and-ng-test (pull_request) Successful in 3m35s
Build / Build-and-test-development (pull_request) Successful in 8m11s
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Successful in 19m7s
2025-12-08 16:04:41 +00:00
M 2fe690e962 chore: package-lock
Build / Build-and-ng-test (pull_request) Failing after 1m27s
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Failing after 1m46s
Build / Build-and-test-development (pull_request) Successful in 8m6s
2025-12-08 15:49:46 +01:00
M b826d37086 chore: package-lock
Build / Build-and-ng-test (pull_request) Failing after 39s
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Failing after 53s
Build / Build-and-test-development (pull_request) Failing after 42s
2025-12-08 15:42:03 +01:00
Trevor Moody bfbfd55fe7 fix: (build) rebuilt package-lock files
Build / Build-and-ng-test (pull_request) Failing after 38s
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Failing after 58s
Build / Build-and-test-development (pull_request) Failing after 44s
2025-12-08 14:16:56 +00:00
Trevor Moody 15f38efd52 chore: (build) use node 24.5.0
Build / Build-and-ng-test (pull_request) Failing after 45s
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Failing after 56s
Build / Build-and-test-development (pull_request) Failing after 41s
2025-12-08 13:02:50 +00:00
Trevor Moody 5d25681485 chore: lint fixes
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Failing after 57s
Build / Build-and-test-development (pull_request) Failing after 42s
Build / Build-and-ng-test (pull_request) Failing after 41s
2025-12-08 12:30:00 +00:00
Trevor Moody d26df376f8 chore: CRLF to LF
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Failing after 56s
Build / Build-and-test-development (pull_request) Failing after 41s
Build / Build-and-ng-test (pull_request) Failing after 37s
2025-12-08 11:41:12 +00:00
allan cff3fb3bad Merge pull request 'fix: configurable audit table on restore check' (#194) from issue193 into main
Release / Build-production-and-ng-test (push) Successful in 3m36s
Release / Build-and-test-development (push) Successful in 8m15s
Release / release (push) Failing after 3m20s
Reviewed-on: #194
2025-12-08 11:25:44 +00:00
Trevor Moody fb3c49aa8b fix: improved testing
Build / Build-and-ng-test (pull_request) Failing after 39s
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Failing after 57s
Build / Build-and-test-development (pull_request) Failing after 58s
2025-12-08 10:21:37 +00:00
Trevor Moody af1657e226 fix: (deps) bumped @sasjs/core, @sasjs/cli, @sasjs/utils and @sasjs/adapter 2025-11-28 04:11:46 +00:00
Trevor Moody d7c7302c12 fix: (deps) bump @sasjs/cli and @sasjs/core 2025-11-25 14:24:36 +00:00
allan 26ce95f7c1 fix: configurable audit table on restore check
Build / Build-and-ng-test (pull_request) Successful in 5m41s
Build / Build-and-test-development (pull_request) Successful in 9m29s
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Successful in 19m20s
closes #193
2025-10-27 20:13:49 +00:00
semantic-release-bot 4924df2ef3 chore(release): 7.2.4 [skip ci]
## [7.2.4](https://git.datacontroller.io/dc/dc/compare/v7.2.3...v7.2.4) (2025-10-14)

### Bug Fixes

* ensure reload after applying licence key ([cb1978b](cb1978bcaf))
* snyk report security patches ([387f512](387f5122f1))
2025-10-14 11:44:18 +00:00
sead 2e141a5d52 chore(git): merge pull request 'fix: snyk report security patches' (#192) from dc_snyk into main
Release / Build-production-and-ng-test (push) Successful in 3m42s
Release / Build-and-test-development (push) Successful in 8m52s
Release / release (push) Successful in 8m13s
Reviewed-on: #192
Reviewed-by: mihajlo <mihajlo@4gl.io>
Reviewed-by: sead <sead@noreply.git.datacontroller.io>
2025-10-14 11:28:05 +00:00
Trevor Moody cb1978bcaf fix: ensure reload after applying licence key
Build / Build-and-ng-test (pull_request) Successful in 4m2s
Build / Build-and-test-development (pull_request) Successful in 8m49s
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Successful in 18m48s
2025-10-14 12:05:27 +01:00
Trevor Moody 387f5122f1 fix: snyk report security patches
Build / Build-and-ng-test (pull_request) Successful in 4m0s
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Successful in 19m23s
Build / Build-and-test-development (pull_request) Failing after 11m1s
2025-10-10 12:54:22 +01:00
semantic-release-bot db5887de21 chore(release): 7.2.3 [skip ci]
## [7.2.3](https://git.datacontroller.io/dc/dc/compare/v7.2.2...v7.2.3) (2025-10-02)

### Bug Fixes

* opening second table in viewer throws an error ([6c6b1cb](6c6b1cbf46))
2025-10-02 15:10:30 +00:00
allan fe24d9bcbd Merge pull request 'opening second table in viewer throws an error' (#191) from issue-189 into main
Release / Build-production-and-ng-test (push) Successful in 3m47s
Release / Build-and-test-development (push) Successful in 8m52s
Release / release (push) Successful in 8m25s
Reviewed-on: #191
Reviewed-by: allan <allan@4gl.io>
2025-10-02 14:54:13 +00:00
M 6c6b1cbf46 fix: opening second table in viewer throws an error
Build / Build-and-ng-test (pull_request) Successful in 4m59s
Build / Build-and-test-development (pull_request) Successful in 9m5s
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Successful in 19m55s
2025-10-02 16:23:00 +02:00
semantic-release-bot 4d65c9c999 chore(release): 7.2.2 [skip ci]
## [7.2.2](https://git.datacontroller.io/dc/dc/compare/v7.2.1...v7.2.2) (2025-09-23)

### Bug Fixes

* jsrsasign,  @sasjs/cli bump ([365f129](365f12996d))
2025-09-23 21:50:46 +00:00
allan 4417279275 Merge pull request 'Addressing production vulnerabilities' (#188) from vulnerabilities into main
Release / Build-production-and-ng-test (push) Successful in 3m35s
Release / Build-and-test-development (push) Successful in 8m7s
Release / release (push) Successful in 7m48s
Reviewed-on: #188
2025-09-23 21:35:40 +00:00
M 365f12996d fix: jsrsasign, @sasjs/cli bump
Build / Build-and-ng-test (pull_request) Successful in 3m52s
Build / Build-and-test-development (pull_request) Successful in 8m14s
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Successful in 18m20s
2025-09-23 16:44:55 +02:00
semantic-release-bot ef1015f33b chore(release): 7.2.1 [skip ci]
## [7.2.1](https://git.datacontroller.io/dc/dc/compare/v7.2.0...v7.2.1) (2025-08-08)

### Bug Fixes

* removing localhost from index.html ([225e693](225e693d1f))
2025-08-08 17:35:02 +00:00
allan b43dfb5cf4 Merge pull request 'fix: removing localhost from index.html' (#187) from localhostfix into main
Release / Build-production-and-ng-test (push) Successful in 3m54s
Release / Build-and-test-development (push) Successful in 8m25s
Release / release (push) Successful in 7m50s
Reviewed-on: #187
2025-08-08 17:19:17 +00:00
allan 225e693d1f fix: removing localhost from index.html
Build / Build-and-ng-test (pull_request) Successful in 3m47s
Build / Build-and-test-development (pull_request) Successful in 8m27s
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Successful in 18m41s
2025-08-08 18:08:49 +01:00
semantic-release-bot fda91770be chore(release): 7.2.0 [skip ci]
# [7.2.0](https://git.datacontroller.io/dc/dc/compare/v7.1.1...v7.2.0) (2025-08-08)

### Bug Fixes

* **ci:** cypress dependency package not available anymore ([26cdd73](26cdd73331))
* **hot v16 migration:** multi dataset fixed issues, and cypress tests adapted ([712b384](712b384848))
* obsolete cypress deps ([2ba4b53](2ba4b5383e))
* remaining hot migrations - handsontable/angular-wrapper ([b419cd5](b419cd5078))

### Features

* lighthouse accessibility check pipeline ([670ec2c](670ec2c71c))
2025-08-08 11:51:38 +00:00
allan d512876e0b Merge pull request 'fix: obsolete cypress deps' (#186) from cypress-deps into main
Release / Build-production-and-ng-test (push) Successful in 3m42s
Release / Build-and-test-development (push) Successful in 8m24s
Release / release (push) Successful in 7m50s
Reviewed-on: #186
2025-08-08 11:36:06 +00:00
M 2ba4b5383e fix: obsolete cypress deps
Build / Build-and-ng-test (pull_request) Successful in 3m46s
Build / Build-and-test-development (pull_request) Successful in 8m17s
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Successful in 18m35s
2025-08-08 13:25:11 +02:00
allan ecc3184609 Merge pull request 'fix: remaining hot migrations - handsontable/angular-wrapper' (#185) from remaining-hot-migration into main
Release / Build-production-and-ng-test (push) Successful in 3m32s
Release / Build-and-test-development (push) Failing after 38s
Release / release (push) Has been skipped
Reviewed-on: #185
Reviewed-by: allan <allan@4gl.io>
2025-08-08 08:28:16 +00:00
M 712b384848 fix(hot v16 migration): multi dataset fixed issues, and cypress tests adapted
Build / Build-and-ng-test (pull_request) Successful in 3m44s
Build / Build-and-test-development (pull_request) Successful in 8m24s
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Successful in 18m5s
2025-08-07 16:58:53 +02:00
M 26cdd73331 fix(ci): cypress dependency package not available anymore
Build / Build-and-ng-test (pull_request) Successful in 3m46s
Build / Build-and-test-development (pull_request) Failing after 11m42s
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Successful in 18m1s
2025-08-07 11:15:00 +02:00
M 919aa6dcfe ci: lighthouse
Build / Build-and-ng-test (pull_request) Successful in 3m46s
Build / Build-and-test-development (pull_request) Failing after 33s
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Successful in 18m1s
2025-08-07 10:22:10 +02:00
M 3bb3093b49 ci: lighthouse
Build / Build-and-ng-test (pull_request) Successful in 3m54s
Build / Build-and-test-development (pull_request) Failing after 33s
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Has been cancelled
2025-08-07 10:14:24 +02:00
M 9c12250558 ci: lighthouse
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Failing after 3m32s
Build / Build-and-ng-test (pull_request) Successful in 3m49s
Build / Build-and-test-development (pull_request) Failing after 35s
2025-08-07 10:09:19 +02:00
M 6c843f64fb chore: install wait-on
Build / Build-and-ng-test (pull_request) Successful in 3m45s
Build / Build-and-test-development (pull_request) Failing after 33s
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Has been cancelled
2025-08-07 09:54:27 +02:00
M 3fda7dc5b0 chore: installed wait-on
Build / Build-and-ng-test (pull_request) Successful in 3m49s
Build / Build-and-test-development (pull_request) Failing after 37s
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Has been cancelled
2025-08-07 09:48:31 +02:00
M 22ec7f0340 ci: lighthouse
Build / Build-and-ng-test (pull_request) Successful in 4m30s
Build / Build-and-test-development (pull_request) Failing after 38s
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Failing after 3h12m40s
2025-08-06 16:15:45 +02:00
M 378461dcbb chore: licence checker
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Failing after 3m31s
Build / Build-and-ng-test (pull_request) Successful in 3m49s
Build / Build-and-test-development (pull_request) Failing after 54s
2025-08-06 16:08:50 +02:00
M 905c7b9d3c chore: remove doxy
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Failing after 1m26s
Build / Build-and-ng-test (pull_request) Failing after 2m6s
Build / Build-and-test-development (pull_request) Failing after 53s
2025-08-06 16:05:07 +02:00
M 670ec2c71c feat: lighthouse accessibility check pipeline
Build / Build-and-ng-test (pull_request) Failing after 1m20s
Build / Build-and-test-development (pull_request) Failing after 57s
Lighthouse Checks / lighthouse (20.15.1) (pull_request) Failing after 2m24s
2025-08-06 15:52:58 +02:00
M b419cd5078 fix: remaining hot migrations - handsontable/angular-wrapper
Build / Build-and-ng-test (pull_request) Failing after 2m3s
Build / Build-and-test-development (pull_request) Failing after 1m34s
2025-08-06 14:06:07 +02:00
semantic-release-bot b1db4ea590 chore(release): 7.1.1 [skip ci]
## [7.1.1](https://git.datacontroller.io/dc/dc/compare/v7.1.0...v7.1.1) (2025-07-24)

### Bug Fixes

* **viewboxes:** hot v16 fails to load because of relative height `100%` ([672dd6d](672dd6d4f1))
2025-07-24 11:46:09 +00:00
mihajlo 822ddb1274 Merge pull request 'fix(viewboxes): hot v16 fails to load because of relative height 100%' (#183) from hot16-viewboxes into main
Release / Build-production-and-ng-test (push) Successful in 3m50s
Release / Build-and-test-development (push) Successful in 9m17s
Release / release (push) Successful in 8m4s
Reviewed-on: #183
2025-07-24 11:29:33 +00:00
M 672dd6d4f1 fix(viewboxes): hot v16 fails to load because of relative height 100%
Build / Build-and-ng-test (pull_request) Successful in 3m55s
Build / Build-and-test-development (pull_request) Successful in 8m27s
2025-07-24 13:12:15 +02:00
semantic-release-bot b3ac73d903 chore(release): 7.1.0 [skip ci]
# [7.1.0](https://git.datacontroller.io/dc/dc/compare/v7.0.3...v7.1.0) (2025-07-23)

### Bug Fixes

* adapter bump ([b495c41](b495c41626))
* bumping CLI to 4.12.10 ([a08a717](a08a717ca8))
* bumping sasjs/core and sasjs/cli ([63e9af4](63e9af402e))

### Features

* improving accessibility score up to 100, hot update to v16.0.1 ([71c308d](71c308d052))
2025-07-23 19:46:56 +00:00
M f8554dd5e7 ci: cypress tests, ng server 'wait on' fix
Release / Build-production-and-ng-test (push) Successful in 3m36s
Release / Build-and-test-development (push) Successful in 8m12s
Release / release (push) Successful in 7m58s
2025-07-23 21:31:29 +02:00
allan 88679c0c9a Merge pull request 'fix: bumping CLI to 4.12.10' (#181) from clibump into main
Release / Build-production-and-ng-test (push) Successful in 3m35s
Release / Build-and-test-development (push) Failing after 3h10m30s
Release / release (push) Has been cancelled
Reviewed-on: #181
2025-07-23 15:04:30 +00:00
allan a08a717ca8 fix: bumping CLI to 4.12.10
Build / Build-and-ng-test (pull_request) Successful in 3m43s
Build / Build-and-test-development (pull_request) Successful in 8m11s
2025-07-23 16:03:57 +01:00
allan c8b6fdbfdb Merge pull request 'Improving accessibility score up to 100, hot update to v16.0.1' (#180) from accessibility-maxing into main
Release / Build-production-and-ng-test (push) Failing after 1m13s
Release / Build-and-test-development (push) Has been skipped
Release / release (push) Has been skipped
Reviewed-on: #180
2025-07-23 13:21:40 +00:00
M b495c41626 fix: adapter bump
Build / Build-and-ng-test (pull_request) Successful in 3m39s
Build / Build-and-test-development (pull_request) Successful in 8m9s
2025-07-23 14:59:45 +02:00
M 7f4be474c6 chore(git): Merge branch 'main' into accessibility-maxing
Build / Build-and-ng-test (pull_request) Successful in 3m48s
Build / Build-and-test-development (pull_request) Successful in 8m12s
2025-07-23 13:23:53 +02:00
allan 7f6f68fcbb Merge pull request 'fix: bumping sasjs/core and sasjs/cli' (#177) from issue157 into main
Release / Build-production-and-ng-test (push) Failing after 1m13s
Release / Build-and-test-development (push) Has been skipped
Release / release (push) Has been skipped
Reviewed-on: #177
2025-07-23 11:14:37 +00:00
M 03fd7db033 chore: licence checker
Build / Build-and-ng-test (pull_request) Successful in 3m48s
Build / Build-and-test-development (pull_request) Successful in 8m13s
2025-07-23 12:22:04 +02:00
M 9dc5c66f7b chore: package-lock
Build / Build-and-ng-test (pull_request) Failing after 1m18s
Build / Build-and-test-development (pull_request) Successful in 8m12s
2025-07-23 12:16:25 +02:00
M aa1b08632e chore: package-lock
Build / Build-and-ng-test (pull_request) Successful in 3m42s
Build / Build-and-test-development (pull_request) Successful in 7m59s
2025-07-23 11:52:56 +02:00
M 6bbe354c9e ci: fix
Build / Build-and-ng-test (pull_request) Failing after 45s
Build / Build-and-test-development (pull_request) Failing after 46s
2025-07-23 11:51:40 +02:00
M 8ff429793b style: lint
Build / Build-and-ng-test (pull_request) Failing after 45s
Build / Build-and-test-development (pull_request) Failing after 46s
2025-07-23 11:50:35 +02:00
M 70d010127a style: lint
Build / Build-and-ng-test (pull_request) Failing after 43s
Build / Build-and-test-development (pull_request) Failing after 44s
2025-07-23 11:49:34 +02:00
M 696717c509 ci: script
Build / Build-and-ng-test (pull_request) Failing after 41s
Build / Build-and-test-development (pull_request) Successful in 8m1s
2025-07-23 11:38:24 +02:00
M 71c308d052 feat: improving accessibility score up to 100, hot update to v16.0.1
Build / Build-and-ng-test (pull_request) Failing after 41s
Build / Build-and-test-development (pull_request) Has been cancelled
2025-07-23 11:17:57 +02:00
allan bed5b320ad chore: lint fix
Build / Build-and-ng-test (pull_request) Successful in 3m48s
Build / Build-and-test-development (pull_request) Has been cancelled
2025-07-07 14:51:04 +01:00
allan b0e827412e Merge branch 'main' into issue157
Build / Build-and-ng-test (pull_request) Failing after 45s
Build / Build-and-test-development (pull_request) Failing after 3h1m39s
2025-07-07 13:34:38 +00:00
allan 63e9af402e fix: bumping sasjs/core and sasjs/cli
Build / Build-and-ng-test (pull_request) Failing after 41s
Build / Build-and-test-development (pull_request) Failing after 3h11m34s
2025-07-07 14:34:03 +01:00
allan fd55105f62 chore(git): adding size check in precommit hook
Release / Build-production-and-ng-test (push) Successful in 4m20s
Release / release (push) Has been cancelled
Release / Build-and-test-development (push) Has been cancelled
2025-06-27 16:42:13 +01:00
semantic-release-bot c2e3b362e7 chore(release): 7.0.3 [skip ci]
## [7.0.3](https://git.datacontroller.io/dc/dc/compare/v7.0.2...v7.0.3) (2025-06-26)

### Bug Fixes

* makedata vars ([e7cb471](e7cb471c0b))
* viya deploy makedata missing params ([7a82316](7a8231615c))
2025-06-26 16:29:21 +00:00
Mihajlo Medjedovic 5d2d60d040 chore: npm audit fix for client folder, lint fix
Release / Build-production-and-ng-test (push) Successful in 4m5s
Release / Build-and-test-development (push) Successful in 9m9s
Release / release (push) Successful in 9m18s
2025-06-26 17:53:22 +02:00
allan 0e59f5406f Merge pull request 'fix: viya deploy makedata missing params' (#176) from viya-deploy-params into main
Release / Build-production-and-ng-test (push) Failing after 1m18s
Release / Build-and-test-development (push) Has been skipped
Release / release (push) Has been skipped
Reviewed-on: #176
2025-06-25 21:51:31 +00:00
Medjedovic e7cb471c0b fix: makedata vars
Build / Build-and-ng-test (pull_request) Successful in 4m5s
Build / Build-and-test-development (pull_request) Successful in 8m39s
2025-06-25 23:29:42 +02:00
Medjedovic 0465089207 style: lint
Build / Build-and-ng-test (pull_request) Successful in 4m4s
Build / Build-and-test-development (pull_request) Successful in 8m36s
2025-06-25 23:17:48 +02:00
Medjedovic 4f2f59907c Merge branch 'viya-deploy-params' of ssh://git.datacontroller.io:29419/dc/dc into viya-deploy-params
Build / Build-and-ng-test (pull_request) Failing after 47s
Build / Build-and-test-development (pull_request) Successful in 8m37s
2025-06-25 23:04:38 +02:00
Medjedovic 7d85328d41 style: lint 2025-06-25 23:04:19 +02:00
allan f2a9329196 chore: bumping node v
Build / Build-and-ng-test (pull_request) Failing after 49s
Build / Build-and-test-development (pull_request) Successful in 8m39s
2025-06-25 20:20:19 +01:00
Medjedovic 7a8231615c fix: viya deploy makedata missing params
Build / Build-and-ng-test (pull_request) Failing after 49s
Build / Build-and-test-development (pull_request) Successful in 9m5s
2025-06-25 15:41:43 +02:00
semantic-release-bot 0db6b25327 chore(release): 7.0.2 [skip ci]
## [7.0.2](https://git.datacontroller.io/dc/dc/compare/v7.0.1...v7.0.2) (2025-06-21)

### Bug Fixes

* **viya deploy:** run makedata in new window to ensure logs are available for the user ([0b4042a](0b4042af60))
2025-06-21 09:30:41 +00:00
allan e91f6f01a6 Merge pull request 'fix(viya deploy): run makedata in new window to ensure logs are available for the user' (#175) from issue154 into main
Release / Build-production-and-ng-test (push) Successful in 4m4s
Release / Build-and-test-development (push) Successful in 8m40s
Release / release (push) Successful in 8m30s
Reviewed-on: #175
2025-06-21 09:14:11 +00:00
Mihajlo Medjedovic 0b4042af60 fix(viya deploy): run makedata in new window to ensure logs are available for the user
Build / Build-and-ng-test (pull_request) Successful in 4m12s
Build / Build-and-test-development (pull_request) Successful in 8m42s
2025-06-17 15:33:11 +02:00
311 changed files with 37693 additions and 21501 deletions
+606
View File
@@ -0,0 +1,606 @@
---
name: handsontable
description: >
Use this skill whenever the user asks about Handsontable — a JavaScript data grid component with
spreadsheet-like UX. Triggers include: mentions of "handsontable", "HotTable", "data grid",
"@handsontable/react-wrapper", spreadsheet component in React/Angular/Vue, HyperFormula formulas
inside a grid, cell types (dropdown, checkbox, date, numeric) in a grid context, or questions
about Handsontable theming, plugins, hooks, configuration options, or column/row features. Also
trigger when the user wants to add an editable spreadsheet-like table to a web app, or asks about
copy/paste, sorting, filtering, frozen rows/columns, merged cells, or context menus in a grid.
This skill covers Handsontable used with any framework (React, Angular, Vue 3, vanilla JS) and
HyperFormula as the Formulas plugin engine inside Handsontable. Do NOT use this skill for
headless/standalone HyperFormula usage without Handsontable — that is covered by the separate
"hyperformula" skill.
---
# Handsontable
Handsontable is a JavaScript **data grid component** (not a full spreadsheet application) that
brings spreadsheet-like UX to web apps: cell editing, copy/paste, sorting, filtering, formulas,
keyboard navigation, context menus, merged cells, frozen rows/columns, conditional formatting, data
validation, pagination, and 400+ built-in formulas via HyperFormula.
- **Latest version:** 17.1.0 (May 2026)
- **Frameworks:** Vanilla JS/TS, React (`@handsontable/react-wrapper`), Angular (`@handsontable/angular-wrapper`), Vue 3 (`@handsontable/vue3`)
- **React wrapper requires:** React 18+
- **License:** Dual — free for non-commercial use (`licenseKey: 'non-commercial-and-evaluation'`), paid for commercial. Per-developer annual license, offline validation (no server connection). Tiers: Hobby (free, non-commercial), Trial (free 45 days), Standard (from $999/yr), Priority (from $1,299/yr), Enterprise (custom). See [Pricing](https://handsontable.com/pricing).
Always check `references/docs-map.md` (in this skill folder) for the full organized link directory
when you need to point the user to specific documentation or need to look up more info.
### What this skill does NOT cover
- **Standalone HyperFormula** (headless formula engine without a grid UI) — use the "hyperformula" skill instead.
- **Other data grid libraries** (AG Grid, TanStack Table, etc.).
- **Full spreadsheet applications** — Handsontable is a component you embed, not a standalone app like Google Sheets.
---
## React Quick Start (Recommended)
### Install
```bash
npm install handsontable @handsontable/react-wrapper
```
### Minimal working example
```jsx
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
import 'handsontable/styles/handsontable.min.css';
import 'handsontable/styles/ht-theme-main.min.css';
registerAllModules();
const MyGrid = () => (
<HotTable
themeName="ht-theme-main"
data={[
['', 'Tesla', 'Volvo', 'Toyota', 'Ford'],
['2019', 10, 11, 12, 13],
['2020', 20, 11, 14, 13],
['2021', 30, 15, 12, 13],
]}
colHeaders={true}
rowHeaders={true}
height="auto"
autoWrapRow={true}
autoWrapCol={true}
licenseKey="non-commercial-and-evaluation"
/>
);
export default MyGrid;
```
Key points:
- `registerAllModules()` registers every built-in plugin. To reduce bundle size, import only what
you need — see [Modules guide](https://handsontable.com/docs/react-data-grid/modules/).
- `height` is required (or the grid won't render). Use `"auto"`, a pixel number, or a CSS string.
- All Handsontable configuration options are passed as props on `<HotTable>`.
- Full installation guide: https://handsontable.com/docs/react-data-grid/installation/
---
## Vanilla JS Quick Start
### Install
```bash
npm install handsontable
```
### CDN (jsDelivr)
```html
<script src="https://cdn.jsdelivr.net/npm/handsontable/dist/handsontable.full.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/handsontable/styles/handsontable.min.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/handsontable/styles/ht-theme-main.min.css" />
```
To pin a specific version, add `@17.1` after `handsontable` in the URL (e.g.,
`handsontable@17.1/dist/handsontable.full.min.js`).
### Minimal working example
```js
import Handsontable from 'handsontable';
import 'handsontable/styles/handsontable.min.css';
import 'handsontable/styles/ht-theme-main.min.css';
const container = document.getElementById('grid');
new Handsontable(container, {
data: [
['', 'Tesla', 'Volvo', 'Toyota', 'Ford'],
['2019', 10, 11, 12, 13],
['2020', 20, 11, 14, 13],
['2021', 30, 15, 12, 13],
],
colHeaders: true,
rowHeaders: true,
height: 'auto',
themeName: 'ht-theme-main',
autoWrapRow: true,
autoWrapCol: true,
licenseKey: 'non-commercial-and-evaluation',
});
```
Full JS docs: https://handsontable.com/docs/javascript-data-grid/installation/
---
## Other Frameworks
### Angular (v1719)
```bash
npm install handsontable @handsontable/angular-wrapper
```
The Angular wrapper was modernized in Handsontable v17.1 to align with Angular 1719, simplifying setup and reducing dependencies. Earlier Angular versions are no longer the target — upgrade Angular first if you're below v17.
Docs: https://handsontable.com/docs/angular-data-grid/installation/
### Vue 3
```bash
npm install handsontable @handsontable/vue3
```
Docs: https://handsontable.com/docs/react-data-grid/vue3-installation/
All framework wrappers share the same version number as the core library and expose the same
configuration options. The React examples in this skill translate directly — just use the
framework's component syntax. Note: Vue 2 (`@handsontable/vue`) is deprecated — use Vue 3.
---
## Theming: Light / Dark / Auto
Handsontable v15+ includes a built-in theme system. Three themes ship out of the box: **main**
(default, spreadsheet-like), **horizon** (clean, analytics-focused), **classic** (legacy
replacement).
### CSS file approach (simplest)
Import the base stylesheet plus a theme:
```js
import 'handsontable/styles/handsontable.min.css';
import 'handsontable/styles/ht-theme-main.min.css';
```
Then set the theme on the grid:
```jsx
<HotTable themeName="ht-theme-main" /* ... */ />
```
**Dark mode:**
- System preference (auto): `themeName="ht-theme-main-dark-auto"`
- Forced dark: `themeName="ht-theme-main-dark"`
Replace `main` with `horizon` or `classic` for other themes.
### Theme API approach (runtime switching)
```js
import { mainTheme, registerTheme } from 'handsontable/themes';
const theme = registerTheme(mainTheme)
.setColorScheme('auto') // 'light' | 'dark' | 'auto'
.setDensityType('comfortable'); // 'compact' | 'default' | 'comfortable'
```
Then pass it as a prop:
```jsx
<HotTable theme={theme} /* ... */ />
```
### CDN theme files
```html
<!-- Theme CSS (includes light and dark mode support) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/handsontable/styles/ht-theme-main.min.css" />
```
In v17, dark mode is controlled via `themeName` or the Theme API — there are no separate `-dark` or
`-dark-auto` CSS files. Load the base theme CSS above and set the mode at runtime.
For CSS variable customization, the Theme Builder, Figma design system, and 200+ design tokens, see:
- [Themes guide](https://handsontable.com/docs/react-data-grid/themes/)
- [Theme customization](https://handsontable.com/docs/react-data-grid/theme-customization/)
- [Theme Builder tool](https://handsontable.com/theme-builder)
---
## Common Configuration Patterns
All options below are passed as props on `<HotTable>` (React) or in the config object (vanilla JS).
For the full options reference: https://handsontable.com/docs/react-data-grid/api/options/
### Data binding
```jsx
// Array of arrays
data={[['A1', 'B1'], ['A2', 'B2']]}
// Array of objects
data={[{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]}
columns={[{ data: 'id' }, { data: 'name' }]}
```
Docs: https://handsontable.com/docs/react-data-grid/binding-to-data/
### Column & row headers
```jsx
colHeaders={['ID', 'Name', 'Price']} // custom labels, or {true} for A, B, C...
rowHeaders={true} // 1, 2, 3... or pass an array
```
### Column types (cell types)
Set via the `columns` array or `cells` function. Built-in types:
- `text` (default), `numeric`, `date`, `time`, `checkbox`, `select`, `dropdown`,
`autocomplete`, `password`, `handsontable` (nested grid), `multiselect` (v17+)
```jsx
columns={[
{ data: 'name', type: 'text' },
{ data: 'price', type: 'numeric', numericFormat: { pattern: '$0,0.00' } },
{ data: 'active', type: 'checkbox' },
{ data: 'category', type: 'dropdown', source: ['A', 'B', 'C'] },
]}
```
Full cell types reference: https://handsontable.com/docs/react-data-grid/cell-type/
### Sorting & filtering
```jsx
columnSorting={true} // single-column sort
// or
multiColumnSorting={true} // multi-column sort
filters={true} // enable column filters
dropdownMenu={true} // column header menu with filter UI
```
### Frozen rows/columns
```jsx
fixedRowsTop={1}
fixedRowsBottom={1}
fixedColumnsStart={2}
```
### Other common options
```jsx
readOnly={true} // entire grid read-only (or per-cell/column)
contextMenu={true} // right-click menu
mergeCells={[{ row: 0, col: 0, rowspan: 2, colspan: 2 }]}
manualColumnResize={true}
manualRowResize={true}
stretchH="all" // stretch columns to fill width: 'none' | 'last' | 'all'
```
### Per-column config with HotColumn (React)
```jsx
import { HotTable, HotColumn } from '@handsontable/react-wrapper';
<HotTable data={data} licenseKey="non-commercial-and-evaluation">
<HotColumn title="Name" data="name" />
<HotColumn title="Active" data="active" type="checkbox" />
</HotTable>
```
Docs: https://handsontable.com/docs/react-data-grid/hot-column/
---
## HyperFormula Integration (Formulas Plugin)
HyperFormula powers Handsontable's Formulas plugin, providing 400+ spreadsheet functions (SUM,
AVERAGE, IF, VLOOKUP, etc.). This section covers using HyperFormula **inside** Handsontable only.
### Install
```bash
npm install hyperformula
```
> **Note:** Starting in Handsontable v18, HyperFormula will no longer be bundled. Install it
> separately and pass it to the Formulas plugin.
### CDN
```html
<script src="https://cdn.jsdelivr.net/npm/hyperformula/dist/hyperformula.full.min.js"></script>
```
### Simple setup (auto-created instance)
Pass the `HyperFormula` class directly — Handsontable creates an instance automatically:
```jsx
import { HyperFormula } from 'hyperformula';
<HotTable
data={[
['10', '20', '=SUM(A1:B1)'],
['30', '40', '=SUM(A2:B2)'],
]}
formulas={{ engine: HyperFormula }}
colHeaders={true}
licenseKey="non-commercial-and-evaluation"
/>
```
### External instance (for multi-sheet or shared engine)
Create a HyperFormula instance with the `'internal-use-in-handsontable'` license key:
```jsx
import { HyperFormula } from 'hyperformula';
const hfInstance = HyperFormula.buildEmpty({
licenseKey: 'internal-use-in-handsontable',
});
// Sheet 1
<HotTable
data={data1}
formulas={{ engine: hfInstance, sheetName: 'Sheet1' }}
licenseKey="non-commercial-and-evaluation"
/>
// Sheet 2 — shares the same engine, enabling cross-sheet references
<HotTable
data={data2}
formulas={{ engine: hfInstance, sheetName: 'Sheet2' }}
licenseKey="non-commercial-and-evaluation"
/>
```
Cross-sheet formula example: `=SUM(Sheet1!A:A)`
### Named expressions
```jsx
formulas={{
engine: HyperFormula,
namedExpressions: [
{ name: 'TAX_RATE', expression: 0.21 },
],
}}
```
Use in cells: `=A1 * TAX_RATE`
### Listening for formula changes
```jsx
<HotTable
formulas={{ engine: HyperFormula }}
afterFormulasValuesUpdate={(changes) => {
changes.forEach((c) => console.log(c.address, c.newValue));
}}
/>
```
### Known limitations
- Formulas don't work with nested object data.
- Moving rows/columns with formulas requires the ManualRowMove / ManualColumnMove plugins (not IndexMapper).
- `getSourceData()` operates on physical indexes; formulas use visual indexes.
Full guide: https://handsontable.com/docs/react-data-grid/formula-calculation/
HyperFormula functions list: https://hyperformula.handsontable.com/docs/guide/built-in-functions.html
HyperFormula version compatibility table: https://handsontable.com/docs/react-data-grid/formula-calculation/#hyperformula-version-support
---
## Server-side data with DataProvider (v17.1+)
The **DataProvider plugin** (new in v17.1) wires the grid up to a remote data source so rows are fetched, sorted, and mutated server-side instead of held in memory. Use it for datasets too large to load up front, or when the source of truth lives in a backend.
The `dataProvider` option is an object with five required keys: a row-id resolver, a paginated `fetchRows` callback, and three mutation callbacks. For a read-only grid, stub the mutation callbacks with `async () => {}` — they must still be present.
```jsx
<HotTable
colHeaders={['ID', 'Name', 'Email']}
pagination={{ pageSize: 25 }}
columnSorting={true}
emptyDataState={true}
height={360}
licenseKey="non-commercial-and-evaluation"
dataProvider={{
// Required: how to extract a stable id from each row
rowId: 'id',
// Required: paginated fetch. `sort` is null when no column is sorted.
// Second arg carries an AbortSignal — pass it to fetch() so the plugin
// can cancel superseded requests when the user pages/sorts quickly.
async fetchRows({ page, pageSize, sort }, { signal }) {
const params = new URLSearchParams({ page, pageSize });
if (sort) {
// sort = { prop: string, order: 'asc' | 'desc' }
params.set('sortBy', sort.prop);
params.set('order', sort.order);
}
const res = await fetch(`/api/users?${params}`, { signal });
const { rows, totalRows } = await res.json();
return { rows, totalRows };
},
// Required. Stub with async () => {} if your grid is read-only.
// The plugin auto-refetches the current page after each callback resolves.
// See the plugin API ref for the exact payload shapes.
onRowsCreate: async () => {},
onRowsUpdate: async () => {},
onRowsRemove: async () => {},
}}
/>
```
### Loading and error UI
The plugin fires three hooks you can wire up as `<HotTable>` props for loading indicators and error surfaces:
- `beforeDataProviderFetch(params)` — fires before each fetch. `params.skipLoading` is set when the plugin wants to suppress your loading indicator (e.g., during a quick refetch).
- `afterDataProviderFetch(result)` — fires after a successful fetch.
- `afterDataProviderFetchError(error)` — fires when `fetchRows` throws or returns a rejected promise.
### Companion options
The plugin is built to pair with `pagination` (paginates server-side), `columnSorting` (single-column server-side sort), and `emptyDataState` (loading + empty state UI). Enable all three when you use DataProvider.
### Authoritative references
- Guide: https://handsontable.com/docs/react-data-grid/server-side-data-fetching/
- Recipe (REST API): https://handsontable.com/docs/react-data-grid/recipes/data-management/load-data-rest-api/
- Plugin API: https://handsontable.com/docs/react-data-grid/api/data-provider/
## Notifications (v17.1+)
The **Notification plugin** (new in v17.1) shows non-blocking toast notifications anchored to the grid — useful for confirming saves, surfacing validation errors, or signaling background sync state. Enable with `notifications: true` and trigger via `hot.getPlugin('notifications').showMessage(...)`. See the plugin guide for placement, severity levels, and auto-dismiss timing: https://handsontable.com/docs/react-data-grid/notification/
---
## Events & Hooks
Handsontable hooks are passed as props on `<HotTable>`:
```jsx
<HotTable
afterChange={(changes, source) => {
if (source !== 'loadData') {
console.log('Cell changed:', changes);
}
}}
beforeChange={(changes, source) => {
// Return false to cancel the edit
}}
afterSelection={(row, col, row2, col2) => {
console.log('Selected:', row, col, 'to', row2, col2);
}}
/>
```
There are 100+ hooks available. Full reference: https://handsontable.com/docs/react-data-grid/api/hooks/
Guide: https://handsontable.com/docs/react-data-grid/events-and-hooks/
---
## Accessing the Instance (Ref)
Use a ref to call Handsontable's core methods:
```jsx
import { useRef } from 'react';
const MyGrid = () => {
const hotRef = useRef(null);
const handleClick = () => {
const hot = hotRef.current?.hotInstance;
if (hot) {
console.log(hot.getData()); // get all data
hot.selectCell(0, 0); // select a cell
}
};
return (
<>
<HotTable ref={hotRef} /* ...options */ />
<button onClick={handleClick}>Get Data</button>
</>
);
};
```
Core API reference: https://handsontable.com/docs/react-data-grid/api/core/
---
## Performance & Large Datasets
Handsontable virtualizes rendering automatically — only visible rows and columns are in the DOM, so
grids with 10k100k+ rows perform well out of the box. For bulk programmatic updates, wrap mutations
in `batch()` to defer re-rendering until all changes are applied:
```jsx
hotRef.current.hotInstance.batch(() => {
// set many cells, add rows, etc.
});
```
For further tuning (disabling auto-size, reducing plugin overhead), see:
- [Performance guide](https://handsontable.com/docs/react-data-grid/performance/)
- [Batch operations](https://handsontable.com/docs/react-data-grid/batch-operations/)
- [Bundle size optimization](https://handsontable.com/docs/react-data-grid/bundle-size/)
---
## Recipes (v17+)
Handsontable v17 introduced a Recipes section in the docs — ready-made patterns for common use cases
(data validation workflows, dynamic column generation, etc.). Check the recipes index before building
something from scratch: https://handsontable.com/docs/react-data-grid/recipes/
---
## Common Pitfalls
- **Forgetting `height`**: The grid won't render without a `height` prop. Use `"auto"`, a pixel value, or a CSS string.
- **Not filtering `loadData` in `afterChange`**: The `afterChange` hook fires on initial data load with `source === 'loadData'`. Always check the source to avoid infinite loops when syncing changes back to state.
- **Using the old wrapper packages**: v17 removed `@handsontable/react` and `@handsontable/angular`. Use `@handsontable/react-wrapper` and `@handsontable/angular-wrapper`.
- **Using legacy CSS imports**: `handsontable.full.min.css` was removed in v17. Use `handsontable/styles/handsontable.min.css` plus a theme file.
- **Formulas with nested object data**: HyperFormula formulas don't work when `data` is an array of nested objects — use flat objects or arrays of arrays.
- **ExportFile `columnHeaders` renamed**: In v17.1 the ExportFile plugin's `columnHeaders` option was renamed to `colHeaders` to match the table-level option. Update any `exportAsString` / `exportAsBlob` / `downloadFile` calls that pass `columnHeaders: ...`.
---
## Version Awareness
Handsontable docs are versioned. The latest docs live at `/docs/react-data-grid/` (which redirects
to the current version). To link to a specific version, use `/docs/17.0/react-data-grid/`.
When helping a user, check which version they are on — breaking changes between major versions are
common. Point them to the relevant migration guide if they're upgrading.
For the full organized directory of documentation links, read `references/docs-map.md` in this
skill's folder.
### v17.1 changes (latest, May 2026)
- **New plugins:** DataProvider (server-side row loading via `dataProvider` option), Notification (toast notifications).
- **NestedHeaders rowspan:** column headers can now span multiple header rows.
- **ExportFile:** XLSX export added; the `columnHeaders` option was renamed to `colHeaders` (see Common Pitfalls).
- **Angular wrapper:** modernized for Angular 1719; simpler setup, fewer deps.
- **Touch:** long-press now opens the context menu on touch devices.
- **TypeScript:** `dateFormat` option now accepts `Intl.DateTimeFormatOptions`.
- No removals or deprecations in v17.1.
### v17.0 Breaking Changes
- Removed legacy wrapper packages (`@handsontable/react`, `@handsontable/angular`). Use
`@handsontable/react-wrapper` and `@handsontable/angular-wrapper`.
- Removed legacy CSS (`handsontable.full.min.css`). Use `handsontable/styles/` imports.
- Removed core-js from dependencies.
- Removed the PersistentState plugin.
- Deprecated bundled HyperFormula (will require separate install in v18).
- Deprecated numbro.js, Pikaday, moment.js, DOMPurify — use native alternatives.
Migration guide: https://handsontable.com/docs/react-data-grid/migration-from-16.2-to-17.0/
@@ -0,0 +1,231 @@
# Handsontable Documentation Map
> Last verified: May 2026 · Aligned with Handsontable 17.1.0
Organized link directory for all Handsontable and HyperFormula (in-grid) documentation. Use these
to point users to the right page. Links default to the React docs; replace `react-data-grid` with
`javascript-data-grid`, `angular-data-grid`, or prefix with `vue3-` for other frameworks.
## Getting Started
- Installation: https://handsontable.com/docs/react-data-grid/installation/
- Demo: https://handsontable.com/docs/react-data-grid/demo/
- Binding to data: https://handsontable.com/docs/react-data-grid/binding-to-data/
- Server-side data fetching (DataProvider, v17.1+): https://handsontable.com/docs/react-data-grid/server-side-data-fetching/
- Saving data: https://handsontable.com/docs/react-data-grid/saving-data/
- Configuration options guide: https://handsontable.com/docs/react-data-grid/configuration-options/
- Grid size: https://handsontable.com/docs/react-data-grid/grid-size/
- Instance methods: https://handsontable.com/docs/react-data-grid/instance-methods/
- Events and hooks: https://handsontable.com/docs/react-data-grid/events-and-hooks/
- License key: https://handsontable.com/docs/react-data-grid/license-key/
- Redux integration: https://handsontable.com/docs/react-data-grid/redux/
## Styling & Themes
- Themes guide: https://handsontable.com/docs/react-data-grid/themes/
- Design system (Figma): https://handsontable.com/docs/react-data-grid/handsontable-design-system/
- Theme customization (CSS vars, tokens): https://handsontable.com/docs/react-data-grid/theme-customization/
- Legacy style (pre-v15): https://handsontable.com/docs/react-data-grid/legacy-style/
- Theme Builder tool: https://handsontable.com/theme-builder
- Figma Theme Generator: https://github.com/handsontable/handsontable-figma
## Columns
- HotColumn component (React): https://handsontable.com/docs/react-data-grid/hot-column/
- Column headers: https://handsontable.com/docs/react-data-grid/column-header/
- Column groups (nested headers): https://handsontable.com/docs/react-data-grid/column-groups/
- Column hiding: https://handsontable.com/docs/react-data-grid/column-hiding/
- Column moving: https://handsontable.com/docs/react-data-grid/column-moving/
- Column freezing: https://handsontable.com/docs/react-data-grid/column-freezing/
- Column widths: https://handsontable.com/docs/react-data-grid/column-width/
- Column summary: https://handsontable.com/docs/react-data-grid/column-summary/
- Column virtualization: https://handsontable.com/docs/react-data-grid/column-virtualization/
- Column menu (dropdown): https://handsontable.com/docs/react-data-grid/column-menu/
- Column filter: https://handsontable.com/docs/react-data-grid/column-filter/
## Rows
- Row headers: https://handsontable.com/docs/react-data-grid/row-header/
- Row parent-child (nested rows): https://handsontable.com/docs/react-data-grid/row-parent-child/
- Row hiding: https://handsontable.com/docs/react-data-grid/row-hiding/
- Row moving: https://handsontable.com/docs/react-data-grid/row-moving/
- Row freezing: https://handsontable.com/docs/react-data-grid/row-freezing/
- Row heights: https://handsontable.com/docs/react-data-grid/row-height/
- Row virtualization: https://handsontable.com/docs/react-data-grid/row-virtualization/
- Rows sorting: https://handsontable.com/docs/react-data-grid/rows-sorting/
- Rows pagination: https://handsontable.com/docs/react-data-grid/rows-pagination/
- Row trimming: https://handsontable.com/docs/react-data-grid/row-trimming/
- Row pre-populating: https://handsontable.com/docs/react-data-grid/row-prepopulating/
## Cell Features
- Clipboard (copy/paste): https://handsontable.com/docs/react-data-grid/basic-clipboard/
- Selection: https://handsontable.com/docs/react-data-grid/selection/
- Merge cells: https://handsontable.com/docs/react-data-grid/merge-cells/
- Conditional formatting: https://handsontable.com/docs/react-data-grid/conditional-formatting/
- Text alignment: https://handsontable.com/docs/react-data-grid/text-alignment/
- Disabled cells: https://handsontable.com/docs/react-data-grid/disabled-cells/
- Comments: https://handsontable.com/docs/react-data-grid/comments/
- Autofill values: https://handsontable.com/docs/react-data-grid/autofill-values/
- Formatting cells: https://handsontable.com/docs/react-data-grid/formatting-cells/
## Cell Functions
- Cell functions overview: https://handsontable.com/docs/react-data-grid/cell-function/
- Cell renderer: https://handsontable.com/docs/react-data-grid/cell-renderer/
- Cell editor: https://handsontable.com/docs/react-data-grid/cell-editor/
- Cell validator: https://handsontable.com/docs/react-data-grid/cell-validator/
- Custom cells (v17+): https://handsontable.com/docs/react-data-grid/custom-cells/
## Cell Types
- Cell type overview: https://handsontable.com/docs/react-data-grid/cell-type/
- Numeric: https://handsontable.com/docs/react-data-grid/numeric-cell-type/
- Date: https://handsontable.com/docs/react-data-grid/date-cell-type/
- Time: https://handsontable.com/docs/react-data-grid/time-cell-type/
- Checkbox: https://handsontable.com/docs/react-data-grid/checkbox-cell-type/
- Select: https://handsontable.com/docs/react-data-grid/select-cell-type/
- Dropdown: https://handsontable.com/docs/react-data-grid/dropdown-cell-type/
- Autocomplete: https://handsontable.com/docs/react-data-grid/autocomplete-cell-type/
- MultiSelect (v17+): https://handsontable.com/docs/react-data-grid/multiselect-cell-type/
- Password: https://handsontable.com/docs/react-data-grid/password-cell-type/
- Handsontable (nested grid): https://handsontable.com/docs/react-data-grid/handsontable-cell-type/
## Formulas (HyperFormula in Handsontable)
- Formula calculation guide: https://handsontable.com/docs/react-data-grid/formula-calculation/
- Formulas plugin API: https://handsontable.com/docs/react-data-grid/api/formulas/
- HyperFormula built-in functions list: https://hyperformula.handsontable.com/docs/guide/built-in-functions.html
- HyperFormula configuration options: https://hyperformula.handsontable.com/docs/guide/configuration-options.html
- HyperFormula custom functions: https://hyperformula.handsontable.com/docs/guide/custom-functions.html
- HyperFormula named expressions: https://hyperformula.handsontable.com/docs/guide/cell-references.html#relative-references
- HyperFormula license key: https://hyperformula.handsontable.com/docs/guide/license-key.html
## Navigation
- Keyboard shortcuts: https://handsontable.com/docs/react-data-grid/keyboard-shortcuts/
- Custom shortcuts: https://handsontable.com/docs/react-data-grid/custom-shortcuts/
- Focus scopes: https://handsontable.com/docs/react-data-grid/focus-scopes/
- Searching values: https://handsontable.com/docs/react-data-grid/searching-values/
## Accessibility
- Accessibility guide: https://handsontable.com/docs/react-data-grid/accessibility/
## Menus & Accessories
- Context menu: https://handsontable.com/docs/react-data-grid/context-menu/
- Undo and redo: https://handsontable.com/docs/react-data-grid/undo-redo/
- Icon pack: https://handsontable.com/docs/react-data-grid/icon-pack/
- Export to CSV: https://handsontable.com/docs/react-data-grid/export-to-csv/
- Export to Excel/XLSX (v17.1+): https://handsontable.com/docs/react-data-grid/export-to-excel/
- Notification (v17.1+): https://handsontable.com/docs/react-data-grid/notification/
- Empty data state: https://handsontable.com/docs/react-data-grid/empty-data-state/
## Dialog & Loading
- Dialog: https://handsontable.com/docs/react-data-grid/dialog/
- Loading indicator: https://handsontable.com/docs/react-data-grid/loading/
## Internationalization
- Language: https://handsontable.com/docs/react-data-grid/language/
- Locale: https://handsontable.com/docs/react-data-grid/locale/
- Layout direction (RTL/LTR): https://handsontable.com/docs/react-data-grid/layout-direction/
- IME support: https://handsontable.com/docs/react-data-grid/ime-support/
## Tools & Building
- Modules (tree-shaking): https://handsontable.com/docs/react-data-grid/modules/
- Custom plugins: https://handsontable.com/docs/react-data-grid/custom-plugins/
- Custom builds: https://handsontable.com/docs/react-data-grid/custom-builds/
- Testing: https://handsontable.com/docs/react-data-grid/testing/
## Optimization
- Batch operations: https://handsontable.com/docs/react-data-grid/batch-operations/
- Performance: https://handsontable.com/docs/react-data-grid/performance/
- Bundle size: https://handsontable.com/docs/react-data-grid/bundle-size/
## Security
- Security guide: https://handsontable.com/docs/react-data-grid/security/
## API Reference
- API overview: https://handsontable.com/docs/react-data-grid/api/
- Core (instance methods): https://handsontable.com/docs/react-data-grid/api/core/
- Hooks (all events): https://handsontable.com/docs/react-data-grid/api/hooks/
- Configuration options (all props): https://handsontable.com/docs/react-data-grid/api/options/
- Plugins index: https://handsontable.com/docs/react-data-grid/api/plugins/
### Individual Plugin APIs
- AutoColumnSize: https://handsontable.com/docs/react-data-grid/api/auto-column-size/
- AutoRowSize: https://handsontable.com/docs/react-data-grid/api/auto-row-size/
- Autofill: https://handsontable.com/docs/react-data-grid/api/autofill/
- BindRowsWithHeaders: https://handsontable.com/docs/react-data-grid/api/bind-rows-with-headers/
- CollapsibleColumns: https://handsontable.com/docs/react-data-grid/api/collapsible-columns/
- ColumnSorting: https://handsontable.com/docs/react-data-grid/api/column-sorting/
- ColumnSummary: https://handsontable.com/docs/react-data-grid/api/column-summary/
- Comments: https://handsontable.com/docs/react-data-grid/api/comments/
- ContextMenu: https://handsontable.com/docs/react-data-grid/api/context-menu/
- CopyPaste: https://handsontable.com/docs/react-data-grid/api/copy-paste/
- CustomBorders: https://handsontable.com/docs/react-data-grid/api/custom-borders/
- DataProvider (v17.1+): https://handsontable.com/docs/react-data-grid/api/data-provider/
- Dialog: https://handsontable.com/docs/react-data-grid/api/dialog/
- DragToScroll: https://handsontable.com/docs/react-data-grid/api/drag-to-scroll/
- DropdownMenu: https://handsontable.com/docs/react-data-grid/api/dropdown-menu/
- EmptyDataState: https://handsontable.com/docs/react-data-grid/api/empty-data-state/
- ExportFile: https://handsontable.com/docs/react-data-grid/api/export-file/
- Filters: https://handsontable.com/docs/react-data-grid/api/filters/
- Formulas: https://handsontable.com/docs/react-data-grid/api/formulas/
- HiddenColumns: https://handsontable.com/docs/react-data-grid/api/hidden-columns/
- HiddenRows: https://handsontable.com/docs/react-data-grid/api/hidden-rows/
- Loading: https://handsontable.com/docs/react-data-grid/api/loading/
- ManualColumnFreeze: https://handsontable.com/docs/react-data-grid/api/manual-column-freeze/
- ManualColumnMove: https://handsontable.com/docs/react-data-grid/api/manual-column-move/
- ManualColumnResize: https://handsontable.com/docs/react-data-grid/api/manual-column-resize/
- ManualRowMove: https://handsontable.com/docs/react-data-grid/api/manual-row-move/
- ManualRowResize: https://handsontable.com/docs/react-data-grid/api/manual-row-resize/
- MergeCells: https://handsontable.com/docs/react-data-grid/api/merge-cells/
- MultiColumnSorting: https://handsontable.com/docs/react-data-grid/api/multi-column-sorting/
- NestedHeaders: https://handsontable.com/docs/react-data-grid/api/nested-headers/
- NestedRows: https://handsontable.com/docs/react-data-grid/api/nested-rows/
- Notification (v17.1+): https://handsontable.com/docs/react-data-grid/api/notification/
- Pagination: https://handsontable.com/docs/react-data-grid/api/pagination/
- Search: https://handsontable.com/docs/react-data-grid/api/search/
- StretchColumns: https://handsontable.com/docs/react-data-grid/api/stretch-columns/
- TrimRows: https://handsontable.com/docs/react-data-grid/api/trim-rows/
- UndoRedo: https://handsontable.com/docs/react-data-grid/api/undo-redo/
## Recipes (new in v17)
- Recipes index: https://handsontable.com/docs/react-data-grid/recipes/
- Load data from a REST API (DataProvider, v17.1+): https://handsontable.com/docs/react-data-grid/recipes/data-management/load-data-rest-api/
## Upgrade & Migration
- Changelog: https://handsontable.com/docs/react-data-grid/changelog/
- Versioning policy: https://handsontable.com/docs/react-data-grid/versioning-policy/
- Deprecation policy: https://handsontable.com/docs/react-data-grid/deprecation-policy/
- Long Term Support (LTS): https://handsontable.com/docs/react-data-grid/long-term-support/
- 16.2 → 17.0 migration: https://handsontable.com/docs/react-data-grid/migration-from-16.2-to-17.0/
- 16.0 → 16.1 migration: https://handsontable.com/docs/react-data-grid/migration-from-16.0-to-16.1/
- 15.3 → 16.0 migration: https://handsontable.com/docs/react-data-grid/migration-from-15.3-to-16.0/
- 14.6 → 15.0 migration: https://handsontable.com/docs/react-data-grid/migration-from-14.6-to-15.0/
## Other Framework Installation
- JavaScript: https://handsontable.com/docs/javascript-data-grid/installation/
- Angular: https://handsontable.com/docs/angular-data-grid/installation/
- Vue 3: https://handsontable.com/docs/react-data-grid/vue3-installation/
## SSR Examples (CodeSandbox)
- Next.js: https://codesandbox.io/p/sandbox/kwnjph?file=https://handsontable.com/codesandbox-vm?example-dir=next.js&handsontable-version=17.1preview=true
- Astro: https://codesandbox.io/p/sandbox/gnqcwn?file=https://handsontable.com/codesandbox-vm?example-dir=astro&handsontable-version=17.1preview=true
- Remix: https://codesandbox.io/p/sandbox/njcjlq?file=https://handsontable.com/codesandbox-vm?example-dir=remix&handsontable-version=17.1preview=true
- Nuxt: https://codesandbox.io/p/sandbox/r7qsjc?file=https://handsontable.com/codesandbox-vm?example-dir=nuxt&handsontable-version=17.1preview=true
## CDN Links (jsDelivr) — latest
- JS (full bundle): https://cdn.jsdelivr.net/npm/handsontable/dist/handsontable.full.min.js
- Base CSS: https://cdn.jsdelivr.net/npm/handsontable/styles/handsontable.min.css
- Theme Main: https://cdn.jsdelivr.net/npm/handsontable/styles/ht-theme-main.min.css
- Theme Horizon: https://cdn.jsdelivr.net/npm/handsontable/styles/ht-theme-horizon.min.css
- Theme Classic: https://cdn.jsdelivr.net/npm/handsontable/styles/ht-theme-classic.min.css
- React wrapper: https://cdn.jsdelivr.net/npm/@handsontable/react-wrapper/dist/react-handsontable.min.js
- HyperFormula: https://cdn.jsdelivr.net/npm/hyperformula/dist/hyperformula.full.min.js
Pin versions by adding `@17.1` after the package name (e.g., `handsontable@17.1`).
## Package Registries
- npm: https://www.npmjs.com/package/handsontable
- NuGet: https://www.nuget.org/packages/handsontable
## Community & Support
- Developer Forum: https://forum.handsontable.com/
- GitHub Issues: https://github.com/handsontable/handsontable/issues
- Stack Overflow: https://stackoverflow.com/tags/handsontable
- Commercial support: support@handsontable.com
- Contact form: https://handsontable.com/contact
- Blog / Release Notes: https://handsontable.com/blog/categories/release-notes
- Download page: https://handsontable.com/download
+85
View File
@@ -0,0 +1,85 @@
# @handsontable/hyperformula-skill
An agent skill for [**HyperFormula**](https://hyperformula.handsontable.com) — the headless, open-source TypeScript spreadsheet calculation engine. It gives any AI coding agent deep, task-oriented knowledge of HyperFormula so the agent can help you integrate, configure, and debug the engine faster.
This package is the npm distribution of the `hyperformula` skill maintained in [`handsontable/handsontable-skills`](https://github.com/handsontable/handsontable-skills). The package version tracks the HyperFormula product version the skill targets (e.g. `3.3.0` targets HyperFormula 3.3.0).
## What's in the package
A self-contained skill — plain markdown, no runtime dependencies:
```
SKILL.md ← entry point: concepts, routing, and a docs map (YAML frontmatter + body)
references/ ← task-oriented deep-dives the agent reads on demand
```
`SKILL.md` follows the [Agent Skills](https://claude.com/blog/skills) layout (a YAML frontmatter block with `name`/`description`, followed by markdown instructions). Any agent that can load a skill — or simply read a folder of markdown instructions — can use it.
## Install
```bash
npm install @handsontable/hyperformula-skill
```
The installed package lives at `node_modules/@handsontable/hyperformula-skill`. Resolve that path programmatically with:
```bash
node -p "require.resolve('@handsontable/hyperformula-skill/SKILL.md')"
```
Then make the skill available to your agent using whichever of the following matches it:
### Claude Code
Copy the package into a skills directory. User scope (available in every project):
```bash
cp -r node_modules/@handsontable/hyperformula-skill ~/.claude/skills/hyperformula
```
Or project scope, from your project root:
```bash
cp -r node_modules/@handsontable/hyperformula-skill .claude/skills/hyperformula
```
### Claude API
Upload the package contents (`SKILL.md` + `references/`) to the [Skills API](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview).
### OpenAI Codex
Codex discovers [skills](https://developers.openai.com/codex/skills) as folders containing a `SKILL.md`, under `~/.agents/skills` (available in every repo) or `.agents/skills` in a repo (checked in for your team). Copy the package into one of those locations:
```bash
# User scope — available in every repo
cp -r node_modules/@handsontable/hyperformula-skill ~/.agents/skills/hyperformula
# Repo scope — from your project root
cp -r node_modules/@handsontable/hyperformula-skill .agents/skills/hyperformula
```
Codex also follows symlinked skill folders, so you can instead link the installed package and let `npm install` keep it current:
```bash
ln -s "$PWD/node_modules/@handsontable/hyperformula-skill" ~/.agents/skills/hyperformula
```
### Any other agent
Most coding agents support a skills, rules, or knowledge directory. Copy (or symlink) the package folder into it, for example:
```bash
# Replace <agent-skills-dir> with your agent's skills/knowledge directory
cp -r node_modules/@handsontable/hyperformula-skill <agent-skills-dir>/hyperformula
```
If your agent reads a single instruction file rather than a folder, point it at `SKILL.md`; it links the files under `references/` so the agent can pull them in as needed. For RAG / vector-store setups, ingest `SKILL.md` together with every file under `references/` (the repository also publishes a pre-flattened `hyperformula-rag.md` for this case).
## Other distribution formats
The same skill source is also shipped as a drag-and-drop `.zip` (Cowork / Claude.ai web), a flattened `-rag.md` doc (RAG / vector stores), and a Claude Code plugin marketplace entry. See the [repository README](https://github.com/handsontable/handsontable-skills#readme) for details.
## License
MIT — see the [repository](https://github.com/handsontable/handsontable-skills/blob/main/LICENSE.txt). HyperFormula itself is separately licensed (GPLv3 or commercial); see the [licensing guide](https://hyperformula.handsontable.com/docs/guide/licensing.html).
+134
View File
@@ -0,0 +1,134 @@
---
name: hyperformula
description: >
Use this skill when the user works with HyperFormula (HF) — a headless,
open-source TypeScript spreadsheet calculation engine. Use it when the user:
mentions HyperFormula or HF; wants to integrate Excel-like formulas into a
JS/TS app; evaluate formulas programmatically in a browser or Node.js;
simulate spreadsheet or Excel calculation behavior server-side; run
calculations over data parsed from XLSX files (e.g. via SheetJS) in code;
build a pricing engine, financial model, or what-if analysis in code; define
custom spreadsheet functions or named expressions outside of a UI context. Do
NOT use this skill for: general Excel questions unrelated to HF; general
Google Sheets questions; generic spreadsheet formula-syntax questions outside
an HF context; formulas used inside Handsontable (use the "handsontable"
skill instead).
---
# HyperFormula
HyperFormula is a **headless, open-source TypeScript spreadsheet calculation engine** for embedding spreadsheet logic in any JavaScript/TypeScript application (browser or Node.js). ~420 built-in functions, dependency graph, undo/redo, i18n (17 languages). Dual-licensed: GPLv3 or commercial.
- Docs: https://hyperformula.handsontable.com/
- npm: https://www.npmjs.com/package/hyperformula
- GitHub: https://github.com/handsontable/hyperformula
## Where to go next
Task-oriented references in `references/` (open the one that matches what the user is doing):
- [`getting-started.md`](references/getting-started.md) — how to install, create an instance (`buildFromArray` / `buildFromSheets` / `buildEmpty`), and work with cell addresses. Open this for onboarding questions and first-run errors.
- [`api-quickref.md`](references/api-quickref.md) — runnable examples for CRUD, rows/columns, sheets, exporting data, batching, named expressions, events, undo/redo, clipboard. Open this when implementing or debugging HF API calls.
- [`custom-functions.md`](references/custom-functions.md) — extending HyperFormula via `FunctionPlugin`: minimal plugin, argument types, volatile functions, range arguments, returning arrays, error handling, aliases, localized names. Open this when adding or debugging custom spreadsheet functions.
- [`configuration.md`](references/configuration.md) — `ConfigParams` options, Excel-compatibility preset, Google-Sheets preset, and config-related pitfalls (separator collisions, Node ICU, `precisionRounding` change in v3). Open this when tuning behavior or diagnosing locale issues.
- [`vue3.md`](references/vue3.md) — Vue 3 integration: `markRaw` requirement, Composition API, Pinia/Vuex stores, `destroy()` on unmount. Open this when embedding HF in a Vue 3 app.
- [`error-handling.md`](references/error-handling.md) — inspecting `CellError`, `ErrorType` enum, `getCellValueDetailedType`, common error causes (`#NAME?`, `#CYCLE!`, `#REF!`, …). Open this when a cell returns an error or you need to branch on result type.
- [`general-pitfalls.md`](references/general-pitfalls.md) — cross-cutting gotchas: `destroy()` lifecycle, forcing literal strings, Excel-parity caveats, hard limits. Open this when results look wrong or memory grows unboundedly.
Below this section is the **Documentation map** — the canonical directory of links to the official HyperFormula docs. Use it when pointing the user to authoritative material.
## Documentation map
All links resolve to `hyperformula.handsontable.com` unless noted.
### Introduction
- Welcome / Homepage: https://hyperformula.handsontable.com/
- Demo: https://hyperformula.handsontable.com/docs/guide/demo.html
### Overview
- Quality (test coverage, CI): https://hyperformula.handsontable.com/docs/guide/quality.html
- Supported browsers: https://hyperformula.handsontable.com/docs/guide/supported-browsers.html
- Dependencies: https://hyperformula.handsontable.com/docs/guide/dependencies.html
- Licensing (GPLv3 vs commercial): https://hyperformula.handsontable.com/docs/guide/licensing.html
- Support: https://hyperformula.handsontable.com/docs/guide/support.html
### Getting Started
- Client-side installation: https://hyperformula.handsontable.com/docs/guide/client-side-installation.html
- Server-side installation (Node.js): https://hyperformula.handsontable.com/docs/guide/server-side-installation.html
- Basic usage: https://hyperformula.handsontable.com/docs/guide/basic-usage.html
- Advanced usage: https://hyperformula.handsontable.com/docs/guide/advanced-usage.html
- Configuration options guide: https://hyperformula.handsontable.com/docs/guide/configuration-options.html
- License key setup: https://hyperformula.handsontable.com/docs/guide/license-key.html
### Framework Integration
- React: https://hyperformula.handsontable.com/docs/guide/integration-with-react.html
- Vue: https://hyperformula.handsontable.com/docs/guide/integration-with-vue.html
- Angular: https://hyperformula.handsontable.com/docs/guide/integration-with-angular.html
- Svelte: https://hyperformula.handsontable.com/docs/guide/integration-with-svelte.html
### Data Operations
- Basic operations (CRUD): https://hyperformula.handsontable.com/docs/guide/basic-operations.html
- Batch operations: https://hyperformula.handsontable.com/docs/guide/batch-operations.html
- Clipboard operations: https://hyperformula.handsontable.com/docs/guide/clipboard-operations.html
- Undo-redo: https://hyperformula.handsontable.com/docs/guide/undo-redo.html
- Sorting data: https://hyperformula.handsontable.com/docs/guide/sorting-data.html
### Formulas
- Specifications and limits: https://hyperformula.handsontable.com/docs/guide/specifications-and-limits.html
- Cell references (relative, absolute, mixed, cross-sheet): https://hyperformula.handsontable.com/docs/guide/cell-references.html
- Types of values: https://hyperformula.handsontable.com/docs/guide/types-of-values.html
- Types of errors: https://hyperformula.handsontable.com/docs/guide/types-of-errors.html
- Types of operators: https://hyperformula.handsontable.com/docs/guide/types-of-operators.html
- Order of precedence: https://hyperformula.handsontable.com/docs/guide/order-of-precendece.html
- Built-in functions (full list, ~400): https://hyperformula.handsontable.com/docs/guide/built-in-functions.html
- Volatile functions: https://hyperformula.handsontable.com/docs/guide/volatile-functions.html
- Named expressions: https://hyperformula.handsontable.com/docs/guide/named-expressions.html
- Array formulas / ARRAYFORMULA: https://hyperformula.handsontable.com/docs/guide/arrays.html
### Internationalization
- i18n features (17 languages): https://hyperformula.handsontable.com/docs/guide/i18n-features.html
- Localizing function names: https://hyperformula.handsontable.com/docs/guide/localizing-functions.html
- Date and time handling: https://hyperformula.handsontable.com/docs/guide/date-and-time-handling.html
### Compatibility
- Microsoft Excel compatibility: https://hyperformula.handsontable.com/docs/guide/compatibility-with-microsoft-excel.html
- Google Sheets compatibility: https://hyperformula.handsontable.com/docs/guide/compatibility-with-google-sheets.html
- Runtime differences (Excel & Sheets): https://hyperformula.handsontable.com/docs/guide/list-of-differences.html
### Advanced Topics
- Key concepts (AST, dependency graph, evaluation): https://hyperformula.handsontable.com/docs/guide/key-concepts.html
- Dependency graph: https://hyperformula.handsontable.com/docs/guide/dependency-graph.html
- Building & testing from source: https://hyperformula.handsontable.com/docs/guide/building.html
- Custom functions (FunctionPlugin): https://hyperformula.handsontable.com/docs/guide/custom-functions.html
- Performance: https://hyperformula.handsontable.com/docs/guide/performance.html
- Known limitations: https://hyperformula.handsontable.com/docs/guide/known-limitations.html
- File import: https://hyperformula.handsontable.com/docs/guide/file-import.html
### Upgrade & Migration
- Release notes / Changelog: https://hyperformula.handsontable.com/docs/guide/release-notes.html
- 0.6 → 1.0 migration: https://hyperformula.handsontable.com/docs/guide/migration-from-0.6-to-1.0.html
- 1.x → 2.0 migration: https://hyperformula.handsontable.com/docs/guide/migration-from-1.x-to-2.0.html
- 2.x → 3.0 migration: https://hyperformula.handsontable.com/docs/guide/migration-from-2.x-to-3.0.html
### API Reference
- API overview: https://hyperformula.handsontable.com/docs/api/
- HyperFormula class (all methods): https://hyperformula.handsontable.com/docs/api/classes/hyperformula.html
- ConfigParams interface (all options): https://hyperformula.handsontable.com/docs/api/interfaces/configparams.html
- Listeners interface (all events): https://hyperformula.handsontable.com/docs/api/interfaces/listeners.html
### npm
- npm page: https://www.npmjs.com/package/hyperformula
### Community & Support
- GitHub repo: https://github.com/handsontable/hyperformula
- GitHub Issues: https://github.com/handsontable/hyperformula/issues
- GitHub Discussions: https://github.com/handsontable/hyperformula/discussions
- Developer Forum: https://forum.handsontable.com/
- Commercial support: support@handsontable.com
- Sales (commercial license): sales@handsontable.com
- Contact form: https://handsontable.com/contact
---
> **Last updated: 2026-05-20 · Aligned with HyperFormula 3.3.0.**
> If the user is on a newer release, confirm API shape against the latest docs (see the **Release notes** link above) before relying on this file.
+34
View File
@@ -0,0 +1,34 @@
{
"name": "@handsontable/hyperformula-skill",
"version": "3.3.0",
"description": "Agent skill for HyperFormula — the headless, open-source TypeScript spreadsheet calculation engine. Teaches AI coding agents how to integrate, configure, and debug HyperFormula.",
"license": "MIT",
"author": "Handsontable <hello@handsontable.com> (https://handsontable.com)",
"homepage": "https://github.com/handsontable/handsontable-skills#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/handsontable/handsontable-skills.git",
"directory": "skills/hyperformula"
},
"bugs": {
"url": "https://github.com/handsontable/handsontable-skills/issues"
},
"keywords": [
"agent-skill",
"claude-skill",
"claude",
"anthropic",
"ai",
"hyperformula",
"spreadsheet",
"formula-engine",
"handsontable"
],
"files": [
"SKILL.md",
"references/"
],
"publishConfig": {
"access": "public"
}
}
@@ -0,0 +1,147 @@
# API Quick Reference
Runnable examples for the most-used HyperFormula APIs. Authoritative docs:
- Basic operations: https://hyperformula.handsontable.com/docs/guide/basic-operations.html
- Batch operations: https://hyperformula.handsontable.com/docs/guide/batch-operations.html
- Named expressions: https://hyperformula.handsontable.com/docs/guide/named-expressions.html
- Clipboard operations: https://hyperformula.handsontable.com/docs/guide/clipboard-operations.html
- Undo-redo: https://hyperformula.handsontable.com/docs/guide/undo-redo.html
- Sorting data: https://hyperformula.handsontable.com/docs/guide/sorting-data.html
- HyperFormula class (all methods): https://hyperformula.handsontable.com/docs/api/classes/hyperformula.html
- Listeners (all events): https://hyperformula.handsontable.com/docs/api/interfaces/listeners.html
## CRUD
```ts
// Write: string, number, or formula
hf.setCellContents({ sheet: 0, col: 0, row: 0 }, 'Hello');
hf.setCellContents({ sheet: 0, col: 1, row: 0 }, '=A1 & " World"');
// Read
hf.getCellValue({ sheet: 0, col: 1, row: 0 }); // computed result
hf.getCellFormula({ sheet: 0, col: 1, row: 0 }); // '=A1 & " World"'
hf.getCellType({ sheet: 0, col: 0, row: 0 }); // CellType.FORMULA | VALUE | EMPTY
```
## Rows and columns
Second arg is `[startIndex, count]`, not a list of indexes.
```ts
hf.addRows(sheetId, [rowIndex, numberOfRows]);
hf.removeRows(sheetId, [rowIndex, numberOfRows]);
hf.addColumns(sheetId, [colIndex, numberOfColumns]);
hf.removeColumns(sheetId, [colIndex, numberOfColumns]);
```
## Sheets
```ts
hf.addSheet('NewSheet');
hf.removeSheet(sheetId);
hf.renameSheet(sheetId, 'BetterName');
hf.countSheets();
hf.getSheetName(sheetId);
hf.getSheetId('SheetName');
hf.getSheetDimensions(sheetId); // → { width, height }
```
## Export data
```ts
hf.getSheetValues(sheetId); // computed values as 2D array
hf.getSheetSerialized(sheetId); // formulas/raw values as 2D array
hf.getAllSheetsValues(); // { SheetName: values[][] }
hf.getAllSheetsSerialized(); // { SheetName: formulas[][] }
```
## Batch operations
Every `setCellContents` call triggers a full dependency-graph recalculation. Always batch when writing more than one cell.
```ts
// Preferred: batch() — returns ExportedChange[]
const changes = hf.batch(() => {
hf.setCellContents({ sheet: 0, col: 0, row: 0 }, '100');
hf.setCellContents({ sheet: 0, col: 0, row: 1 }, '200');
hf.addRows(0, [2, 1]);
});
// `changes` contains all cells that were recalculated.
// For bulk imports (value writes only):
hf.suspendEvaluation();
// ... hundreds of setCellContents calls ...
hf.resumeEvaluation(); // single recalc
// PITFALL: structural ops (addRows/moveRows/...) during suspendEvaluation
// have degraded performance. Use batch() when mixing structural + value writes.
```
## Sorting
`setRowOrder` takes a **permutation array**, not a comparator. Compute the new index sequence externally, then pass it in.
```ts
// Sort rows by the value in column 0
const rowCount = hf.getSheetDimensions(0).height;
const indexes = Array.from({ length: rowCount }, (_, i) => i);
indexes.sort((a, b) => {
const va = hf.getCellValue({ sheet: 0, col: 0, row: a }) as number;
const vb = hf.getCellValue({ sheet: 0, col: 0, row: b }) as number;
return va - vb;
});
hf.setRowOrder(0, indexes); // e.g. [2, 0, 1, 3]
```
## Named expressions
Reusable names for values or formulas. Can be global or scoped to a sheet (local shadows global).
```ts
hf.addNamedExpression('TAX_RATE', '0.21');
hf.addNamedExpression('TOTAL_REVENUE', '=SUM(Revenue!A:A)');
hf.setCellContents({ sheet: 0, col: 0, row: 0 }, '=1000 * TAX_RATE');
hf.listNamedExpressions(); // all registered names
```
## Custom functions
See [custom-functions.md](custom-functions.md) — `FunctionPlugin`, argument types, volatile functions, range arguments, returning arrays, error handling, aliases, localized names.
## Events
Subscribe via `.on()` on the instance. Full list: https://hyperformula.handsontable.com/docs/api/interfaces/listeners.html
```ts
hf.on('valuesUpdated', (changes) => {
changes.forEach((c) => console.log(c.address, c.newValue));
});
hf.on('sheetAdded', (addedSheetName) => { /* ... */ });
hf.on('sheetRemoved', (removedSheetName, removedSheetData) => { /* ... */ });
hf.on('sheetRenamed', (oldName, newName) => { /* ... */ });
```
## Undo / redo
```ts
hf.undo();
hf.redo();
hf.isThereSomethingToUndo();
hf.isThereSomethingToRedo();
hf.clearUndoStack();
```
Configure depth via `undoLimit` in `ConfigParams` (see [configuration.md](configuration.md)).
## Clipboard
```ts
hf.copy({ start: { sheet: 0, col: 0, row: 0 }, end: { sheet: 0, col: 1, row: 1 } });
hf.cut ({ start: { sheet: 0, col: 0, row: 0 }, end: { sheet: 0, col: 1, row: 1 } });
hf.paste({ sheet: 0, col: 5, row: 5 });
hf.isClipboardEmpty();
hf.clearClipboard();
```
@@ -0,0 +1,92 @@
# Configuration
`ConfigParams` + compatibility presets + config-specific pitfalls. Authoritative docs:
- Configuration options guide: https://hyperformula.handsontable.com/docs/guide/configuration-options.html
- ConfigParams (all options): https://hyperformula.handsontable.com/docs/api/interfaces/configparams.html
- Excel compatibility: https://hyperformula.handsontable.com/docs/guide/compatibility-with-microsoft-excel.html
- Google Sheets compatibility: https://hyperformula.handsontable.com/docs/guide/compatibility-with-google-sheets.html
## ConfigParams highlights
Pass as the second argument to any factory method.
```ts
const hf = HyperFormula.buildEmpty({
licenseKey: 'gpl-v3', // required — 'gpl-v3' for open source
precisionRounding: 10, // decimal rounding precision (default 10; was 14 before v3)
functionArgSeparator: ',', // separator between function arguments
dateFormats: ['DD/MM/YYYY', 'DD-MM-YYYY'],
nullDate: { year: 1900, month: 1, day: 1 },
leapYear1900: false, // Excel bug compat: treat 1900 as leap year
maxRows: 40000, // max rows per sheet (default 40000)
maxColumns: 18278, // max columns per sheet (default 18278 = 'ZZZ')
undoLimit: 20, // undo history depth
maxPendingLazyTransformations: 1000, // v3.3+ — cap accumulated lazy transformations before cleanup
});
```
## Excel compatibility preset
```ts
const hf = HyperFormula.buildEmpty({
licenseKey: 'gpl-v3',
useArrayArithmetic: true,
useWildcards: true,
evaluateNullToZero: true,
leapYear1900: true, // Excel's intentional 1900 leap-year bug
ignoreWhiteSpace: 'any',
caseSensitive: false,
accentSensitive: true,
});
```
## Google Sheets compatibility preset
Defaults already match Google Sheets behavior (`leapYear1900: false`, `useArrayArithmetic: false`).
```ts
const hf = HyperFormula.buildEmpty({ licenseKey: 'gpl-v3' });
```
## Config-specific pitfalls
### Vue 3
Vue 3 requires wrapping the instance with `markRaw` — see [vue3.md](vue3.md) for the full integration guide (React / Angular / Svelte need no special handling).
### `functionArgSeparator` vs `thousandSeparator` collision
These two options **cannot share a character**. European locales that use `,` as thousand separator must change the argument separator:
```ts
const hf = HyperFormula.buildEmpty({
licenseKey: 'gpl-v3',
thousandSeparator: ',',
decimalSeparator: '.',
functionArgSeparator: ';', // must differ from thousandSeparator
});
```
### Node.js must have full ICU
String comparisons (used in `MATCH`, `VLOOKUP`, sorting) can silently produce wrong results on Node.js < 13 or builds without full ICU. Verify at startup:
```ts
// Must report 'full' (or a detailed ICU version), not 'small'.
console.log(process.versions.icu);
```
### Tuning `maxPendingLazyTransformations` (v3.3+)
HyperFormula defers some structural transformations (row/column insertions, moves) and applies them lazily. In long-lived instances with heavy mutation throughput — bulk imports, frequent undo/redo, scripted batch edits — the pending queue can grow before cleanup. v3.3 fixed the unbounded-growth leak; this option lets you cap the queue explicitly. Lower values trade memory for more frequent flush work; the default is suitable for typical UI workloads.
### `precisionRounding` default changed in v3
Before v3 the default was `14`. It is now `10`. Calculations relying on the old precision need an explicit override:
```ts
const hf = HyperFormula.buildEmpty({
licenseKey: 'gpl-v3',
precisionRounding: 14, // pre-v3 behavior
});
```
@@ -0,0 +1,174 @@
# Custom Functions
Extend HyperFormula with your own functions via `FunctionPlugin`. Authoritative doc:
- Custom functions guide: https://hyperformula.handsontable.com/docs/guide/custom-functions.html
## Minimal plugin
Register **before** creating any instance — functions registered after `buildFromArray` / `buildFromSheets` / `buildEmpty` won't be available.
```ts
import { HyperFormula, FunctionPlugin, FunctionArgumentType } from 'hyperformula';
class MyPlugin extends FunctionPlugin {
greet(ast, state) {
return this.runFunction(
ast.args,
state,
this.metadata('GREET'),
(name) => `Hello, ${name}!`
);
}
}
MyPlugin.implementedFunctions = {
GREET: {
method: 'greet',
parameters: [{ argumentType: FunctionArgumentType.STRING }],
},
};
const translations = { enGB: { GREET: 'GREET' }, enUS: { GREET: 'GREET' } };
// PITFALL: registration MUST happen before any instance is built.
HyperFormula.registerFunctionPlugin(MyPlugin, translations);
const hf = HyperFormula.buildFromArray(
[['World', '=GREET(A1)']],
{ licenseKey: 'gpl-v3' }
);
console.log(hf.getCellValue({ sheet: 0, col: 1, row: 0 })); // "Hello, World!"
```
## Volatile functions
Mark a function as volatile so it recalculates on every change (like `RAND` or `NOW`). Volatile functions are expensive in large sheets — use sparingly.
```ts
MyPlugin.implementedFunctions = {
RAND_ID: {
method: 'randId',
parameters: [],
isVolatile: true,
},
};
```
## Argument types
```ts
import { FunctionArgumentType } from 'hyperformula';
// FunctionArgumentType.NUMBER
// FunctionArgumentType.STRING
// FunctionArgumentType.BOOLEAN
// FunctionArgumentType.NOERROR
// FunctionArgumentType.SCALAR
// FunctionArgumentType.RANGE // accepts a cell range
// FunctionArgumentType.ANY
```
Use `optionalArg: true`, `defaultValue`, and `minValue` / `maxValue` to validate arguments declaratively:
```ts
MyPlugin.implementedFunctions = {
CLAMP: {
method: 'clamp',
parameters: [
{ argumentType: FunctionArgumentType.NUMBER },
{ argumentType: FunctionArgumentType.NUMBER, optionalArg: true, defaultValue: 0 },
{ argumentType: FunctionArgumentType.NUMBER, optionalArg: true, defaultValue: 100 },
],
},
};
```
## Range arguments
Use `RANGE` argument type when the function takes a cell area. The callback receives the range as a 2D array.
```ts
class SumPositivesPlugin extends FunctionPlugin {
sumPositives(ast, state) {
return this.runFunction(
ast.args,
state,
this.metadata('SUM_POSITIVES'),
(range) => {
let total = 0;
for (const row of range.data) {
for (const v of row) if (typeof v === 'number' && v > 0) total += v;
}
return total;
}
);
}
}
SumPositivesPlugin.implementedFunctions = {
SUM_POSITIVES: {
method: 'sumPositives',
parameters: [{ argumentType: FunctionArgumentType.RANGE }],
},
};
```
## Returning arrays
Custom functions can return 2D arrays. **PITFALL:** result arrays don't auto-resize when upstream dependencies change — the footprint is fixed at first evaluation.
```ts
class SplitPlugin extends FunctionPlugin {
splitToRow(ast, state) {
return this.runFunction(
ast.args,
state,
this.metadata('SPLIT_ROW'),
(text, sep) => [text.split(sep)] // 2D array: one row, N cols
);
}
}
```
## Error handling
Return a `CellError` to signal a formula error:
```ts
import { CellError, ErrorType } from 'hyperformula';
class DividePlugin extends FunctionPlugin {
safeDivide(ast, state) {
return this.runFunction(
ast.args,
state,
this.metadata('SAFE_DIVIDE'),
(a, b) => (b === 0 ? new CellError(ErrorType.DIV_BY_ZERO) : a / b)
);
}
}
```
## Aliases and localized names
Expose one method under multiple function names, or localize:
```ts
MyPlugin.aliases = {
HELLO: 'GREET', // calling =HELLO(x) runs the greet() method
};
const translations = {
enGB: { GREET: 'GREET' },
plPL: { GREET: 'POWITAJ' }, // =POWITAJ("Świat") in Polish
};
HyperFormula.registerFunctionPlugin(MyPlugin, translations);
```
## Unregister / introspect
```ts
HyperFormula.unregisterFunctionPlugin(MyPlugin);
HyperFormula.unregisterFunction('GREET');
HyperFormula.getRegisteredFunctionNames('enGB');
```
@@ -0,0 +1,69 @@
# Error Handling
Inspect cell values that may be errors, tell error types apart, and check value shape. Authoritative docs:
- Types of errors: https://hyperformula.handsontable.com/docs/guide/types-of-errors.html
- Types of values: https://hyperformula.handsontable.com/docs/guide/types-of-values.html
## Always check `CellError` before using a result
Cell values can be errors (`#DIV/0!`, `#VALUE!`, `#REF!`, etc.). Always test before using a result.
```ts
import { CellError, ErrorType } from 'hyperformula';
const value = hf.getCellValue({ sheet: 0, col: 0, row: 0 });
if (value instanceof CellError) {
// ErrorType enum: DIV_BY_ZERO, VALUE, REF, NAME, NUM, NA, CYCLE, ERROR
switch (value.type) {
case ErrorType.CYCLE:
console.log('Circular reference');
break;
case ErrorType.NAME:
// Usually: function not registered (check plugin registration or i18n language)
console.log('Unknown name:', value.message);
break;
default:
console.log('Error:', value.type, value.message);
}
} else {
console.log('Value:', value);
}
```
## `#CYCLE!` is HyperFormula-specific
Standard spreadsheet apps report cycles differently. HyperFormula's `IF` also reports cycles for all branches, even unreachable ones — this can produce `#CYCLE!` in formulas that Excel or Sheets would evaluate.
## Inspect value shape without catching errors
```ts
import { CellValueDetailedType } from 'hyperformula';
hf.getCellValueDetailedType({ sheet: 0, col: 0, row: 0 });
// → CellValueDetailedType.NUMBER | STRING | BOOLEAN | ERROR | EMPTY | ...
```
Use the detailed type when you need to distinguish e.g. number vs empty without touching the value.
## Returning errors from custom functions
Custom `FunctionPlugin` methods can return a `CellError` to surface a formula error. See [custom-functions.md](custom-functions.md) for the full pattern.
```ts
import { CellError, ErrorType } from 'hyperformula';
return new CellError(ErrorType.DIV_BY_ZERO);
```
## Common causes
| Error | Common cause |
|---|---|
| `#NAME?` | Function not registered (check `registerFunctionPlugin` order or `language` config) |
| `#CYCLE!` | Circular reference — or an `IF` branch that *could* produce one |
| `#REF!` | Deleted row/column broke a formula reference |
| `#VALUE!` | Type mismatch — e.g. passing a string where a number is expected |
| `#DIV/0!` | Division by zero, including empty cells coerced to 0 |
| `#NUM!` | Out-of-range numeric result (e.g. `SQRT(-1)`) |
| `#N/A` | Lookup miss (`MATCH`, `VLOOKUP`) or propagated from an upstream `#N/A` |
@@ -0,0 +1,48 @@
# General Pitfalls
Cross-cutting gotchas that aren't tied to a specific API or config option. Authoritative docs:
- Known limitations: https://hyperformula.handsontable.com/docs/guide/known-limitations.html
- Built-in functions (full list): https://hyperformula.handsontable.com/docs/guide/built-in-functions.html
- Runtime differences vs Excel/Sheets: https://hyperformula.handsontable.com/docs/guide/list-of-differences.html
## Error handling
See [error-handling.md](error-handling.md) — checking `CellError`, `ErrorType` enum, `getCellValueDetailedType`, common error causes.
## Always call `destroy()` in long-running apps
HyperFormula maintains internal data structures (dependency graph, address mapping) that are **not** garbage-collected until `destroy()` is called. Leaking instances in servers or SPAs accumulates memory.
```ts
hf.destroy();
// After destroy() the instance is unusable — create a new one if needed.
```
v3.3 fixed two longstanding leak sources inside live instances — pending lazy transformations and undo/redo history were not being trimmed. If you maintain very long-lived instances with heavy mutation throughput, also see `maxPendingLazyTransformations` in [configuration.md](configuration.md) to bound the lazy-transformation queue. `destroy()` is still mandatory at teardown.
## Force a string that looks like a formula
Prefix with `'` (apostrophe) to store the literal text instead of evaluating.
```ts
// Stored as the literal string "=SUM(1,2)", not a formula:
hf.setCellContents({ sheet: 0, col: 0, row: 0 }, "'=SUM(1,2)");
```
## Don't assume Excel parity
~68% of Excel functions are covered. Runtime differences exist even for implemented functions. Before relying on behavior, check:
- Full built-in list: https://hyperformula.handsontable.com/docs/guide/built-in-functions.html
- Runtime differences: https://hyperformula.handsontable.com/docs/guide/list-of-differences.html
## `licenseKey` is always required
Every factory method (`buildFromArray`, `buildFromSheets`, `buildEmpty`) requires `licenseKey`. Use `'gpl-v3'` for open-source use or your commercial key.
## Known hard limits
- **Single workbook per instance** — no multi-workbook support.
- No 3D references, dynamic arrays, async functions, structured references ("Tables"), or relative named expressions.
- `IF` reports cycles for all branches, even unreachable ones.
- Custom function result arrays don't auto-resize when dependencies change.
@@ -0,0 +1,82 @@
# Getting Started
Install, create an instance, read/write cells. Authoritative docs:
- Client-side install: https://hyperformula.handsontable.com/docs/guide/client-side-installation.html
- Server-side install: https://hyperformula.handsontable.com/docs/guide/server-side-installation.html
- Basic usage: https://hyperformula.handsontable.com/docs/guide/basic-usage.html
## Install
### npm
```bash
npm install hyperformula
```
### CDN (jsDelivr, pinned version)
```html
<script src="https://cdn.jsdelivr.net/npm/hyperformula@3.2.0/dist/hyperformula.full.min.js"></script>
```
### Node.js
Same npm install. Requires Node.js 13+ with full ICU for locale-aware string comparison.
## Create an instance
Every factory method requires `licenseKey`. Use `'gpl-v3'` for open-source use or your commercial key.
### `buildFromArray` — single sheet from a 2D array
```ts
import { HyperFormula } from 'hyperformula';
const hf = HyperFormula.buildFromArray(
[
['10', '20', '=SUM(A1:B1)'],
['30', '40', '=SUM(A2:B2)'],
],
{ licenseKey: 'gpl-v3' }
);
console.log(hf.getCellValue({ sheet: 0, col: 2, row: 0 })); // 30
```
### `buildFromSheets` — multi-sheet workbook
```ts
const hf = HyperFormula.buildFromSheets(
{
Revenue: [['100', '200', '=SUM(A1:B1)']],
Expenses: [['50', '=Revenue!C1 - A1']],
},
{ licenseKey: 'gpl-v3' }
);
```
Cross-sheet references use `SheetName!CellRef` syntax.
### `buildEmpty` — start empty, add sheets later
```ts
const hf = HyperFormula.buildEmpty({ licenseKey: 'gpl-v3' });
const sheetName = hf.addSheet('Data');
const sheetId = hf.getSheetId(sheetName);
hf.setCellContents({ sheet: sheetId, col: 0, row: 0 }, [
['10', '20', '=SUM(A1:B1)'],
]);
```
## Cell addresses
All addresses use zero-indexed `{ sheet, col, row }`. Convert to/from A1 notation with built-in helpers:
```ts
hf.simpleCellAddressFromString('B3', 0);
// → { sheet: 0, col: 1, row: 2 }
hf.simpleCellAddressToString({ sheet: 0, col: 1, row: 2 }, 0);
// → 'B3'
```
@@ -0,0 +1,72 @@
# Vue 3
HyperFormula + Vue 3 integration. Authoritative doc:
- Vue integration guide: https://hyperformula.handsontable.com/docs/guide/integration-with-vue.html
## Always wrap the instance with `markRaw`
Vue 3's Composition API wraps objects in a reactive Proxy that intercepts property access and **corrupts HyperFormula's internal state**, causing silent data corruption or crashes. React, Angular, and Svelte need no special handling.
```ts
import { markRaw } from 'vue';
import { HyperFormula } from 'hyperformula';
const hf = markRaw(
HyperFormula.buildEmpty({ licenseKey: 'gpl-v3' })
);
```
Use the same wrapper no matter where the instance is held — `ref`, `reactive`, component state, a store (Pinia/Vuex), or a composable. If the raw HyperFormula instance ever reaches Vue's reactivity system, it must be marked.
## Composition API example
```ts
import { onBeforeUnmount, ref } from 'vue';
import { markRaw } from 'vue';
import { HyperFormula } from 'hyperformula';
export function useHyperFormula() {
const hf = markRaw(
HyperFormula.buildEmpty({ licenseKey: 'gpl-v3' })
);
// Free internal data structures when the component unmounts.
onBeforeUnmount(() => hf.destroy());
const result = ref<unknown>(null);
function evaluate(formula: string) {
hf.setCellContents({ sheet: 0, col: 0, row: 0 }, formula);
result.value = hf.getCellValue({ sheet: 0, col: 0, row: 0 });
}
return { evaluate, result };
}
```
## Pinia / Vuex store
```ts
import { defineStore } from 'pinia';
import { markRaw } from 'vue';
import { HyperFormula } from 'hyperformula';
export const useCalcStore = defineStore('calc', {
state: () => ({
hf: markRaw(
HyperFormula.buildEmpty({ licenseKey: 'gpl-v3' })
),
}),
});
```
## Cleanup
HyperFormula's internal graph is **not** garbage-collected until `destroy()` is called. In SPAs this accumulates memory over time.
```ts
import { onBeforeUnmount } from 'vue';
onBeforeUnmount(() => hf.destroy());
// After destroy() the instance is unusable — create a new one if needed.
```
+13 -2
View File
@@ -3,10 +3,21 @@
# Using `--silent` helps for showing any errs in the first line of the response
# The first line is picked up by the VS Code GIT UI popup when rc is not 0
if npm run --silent lint:silent ; then
if npm run --silent lint:check:silent ; then
exit 0
else
npm run --silent lint:fix
npm run --silent lint:fix:silent
echo "❌ Prettier check failed! We ran lint:fix for you. Please add & commit again."
exit 1
fi
## Avoid large commits
# https://www.backblaze.com/blog/how-many-bytes-are-in-a-megabyte-really/
size_limit=$((2 * 2**20)) # 2mbs
# https://git-scm.com/docs/git-rev-list#Documentation/git-rev-list.txt---disk-usage
commit_size=$(git rev-list --disk-usage HEAD^..HEAD)
test "$commit_size" -lt "$size_limit" || (
echo "Commit size is too large: $commit_size > $size_limit"
echo "Force commit using --no-verify"
exit 1
)
+1
View File
@@ -0,0 +1 @@
* text=auto eol=lf
+58 -37
View File
@@ -2,39 +2,53 @@ name: Build
run-name: Running Lint Check and Licence checker on Pull Request
on: [pull_request]
env:
NODE_VERSION: '24.15.0'
jobs:
Build-and-ng-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20.14.0
node-version: ${{ env.NODE_VERSION }}
- name: Install Google Chrome
run: |
wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
echo "deb http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google.list
apt-get update
apt-get install -y google-chrome-stable xvfb
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
apt install -y ./google-chrome*.deb
- name: Write .npmrc file
run: echo "$NPMRC" > client/.npmrc
run: echo "$NPMRC" >> client/.npmrc
shell: bash
env:
NPMRC: ${{ secrets.NPMRC}}
- name: Lint check
run: npm run lint:check
- name: Install dependencies
run: |
cd client
# Decrypt and Install sheet
echo ${{ secrets.SHEET_PWD }} | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
echo "${{ secrets.SHEET_PWD }}" | \
gpg --batch --yes --passphrase-fd 0 \
--output ./libraries/sheet-crypto.tgz \
--decrypt ./libraries/sheet-crypto.tgz.gpg
npm ci
- name: Check audit
# Audit should fail and stop the CI if critical vulnerability found
run: |
npm audit --audit-level=critical --omit=dev
cd ./sas
npm audit --audit-level=critical --omit=dev
cd ../client
npm audit --audit-level=critical --omit=dev
- name: Lint check
run: npm run lint:check
- name: Licence checker
run: |
cd client
@@ -52,26 +66,33 @@ jobs:
Build-and-test-development:
runs-on: ubuntu-latest
needs: Build-production-and-ng-test
needs: Build-and-ng-test
env:
CHROME_BIN: /usr/bin/google-chrome
# Pin OS locale
LANG: en_GB.UTF-8
LC_ALL: en_GB.UTF-8
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20.14.0
node-version: ${{ env.NODE_VERSION }}
- name: Write .npmrc file
run: |
touch client/.npmrc
echo '${{ secrets.NPMRC}}' > client/.npmrc
- run: apt-get update
- run: wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
- run: apt install -y ./google-chrome*.deb;
- run: export CHROME_BIN=/usr/bin/google-chrome
- run: apt-get update -y
- run: apt-get -y install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb
- run: apt -y install jq
- name: Install system dependencies
run: |
apt-get update
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
apt install -y ./google-chrome*.deb
apt-get -y install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2t64 libxtst6 xauth xvfb jq zip locales
# Generate the en_GB.UTF-8 locale referenced by LANG/LC_ALL
locale-gen en_GB.UTF-8
update-locale LANG=en_GB.UTF-8 LC_ALL=en_GB.UTF-8
- name: Write cypress credentials
run: echo "$CYPRESS_CREDS" > ./client/cypress.env.json
@@ -86,17 +107,18 @@ jobs:
echo ${{ secrets.SHEET_PWD }} | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
npm ci
# Install pm2 and prepare SASJS server
- run: npm i -g pm2
- run: curl -L https://github.com/sasjs/server/releases/latest/download/linux.zip > linux.zip
- run: unzip linux.zip
- run: touch .env
- run: echo RUN_TIMES=js >> .env
- run: echo NODE_PATH=node >> .env
- run: echo CORS=enable >> .env
- run: echo WHITELIST=http://localhost:4200 >> .env
- run: cat .env
- run: pm2 start api-linux --wait-ready
- name: Setup and start SASjs server
run: |
npm i -g pm2
curl -L https://github.com/sasjs/server/releases/latest/download/linux.zip > linux.zip
unzip linux.zip
touch .env
echo RUN_TIMES=js >> .env
echo NODE_PATH=node >> .env
echo CORS=enable >> .env
echo WHITELIST=http://localhost:4200 >> .env
cat .env
pm2 start api-linux --wait-ready
- name: Deploy mocked services
run: |
@@ -106,12 +128,8 @@ jobs:
sasjs cbd -t server-ci
# sasjs request services/admin/makedata -t server-ci -d ./deploy/makeData4GL.json -c ./deploy/requestConfig.json -o ./output.json
- name: Install ZIP
run: |
apt-get update
apt-get install zip
- name: Prepare and run frontend and cypress
timeout-minutes: 35
run: |
cd ./client
mv ./cypress.env.example.json ./cypress.env.json
@@ -126,11 +144,14 @@ jobs:
replace-in-files --regex='"hosturl".*' --replacement='hosturl:"http://localhost:4200",' ./cypress.config.ts
cat ./cypress.config.ts
# Start frontend and run cypress
npm start & npx wait-on http://localhost:4200 && npx cypress run --browser chrome --spec "cypress/e2e/liveness.cy.ts,cypress/e2e/editor.cy.ts,cypress/e2e/excel-multi-load.cy.ts,cypress/e2e/excel.cy.ts,cypress/e2e/csv.cy.ts,cypress/e2e/filtering.cy.ts,cypress/e2e/licensing.cy.ts"
# timeout 1800: SIGTERM after 30 min so Cypress can flush video/screenshots
# before the outer timeout-minutes hard-kills the step (avoids silent multi-hour hangs)
npx ng serve --host 0.0.0.0 --port 4200 & npx wait-on http://localhost:4200 && timeout 1800 npx cypress run --browser chrome --spec "cypress/e2e/csv-limited.cy.ts,cypress/e2e/liveness.cy.ts,cypress/e2e/editor.cy.ts,cypress/e2e/excel-multi-load.cy.ts,cypress/e2e/excel.cy.ts,cypress/e2e/csv.cy.ts,cypress/e2e/filtering.cy.ts,cypress/e2e/licensing.cy.ts,cypress/e2e/viewer-labels.cy.ts"
- name: Zip Cypress videos
if: always()
run: |
mkdir -p ./client/cypress/videos
zip -r cypress-videos ./client/cypress/videos
- name: Add cypress videos artifacts
+91
View File
@@ -0,0 +1,91 @@
name: Lighthouse Checks
run-name: Running Lighthouse Performance and Accessibility Checks on Pull Request
on: [pull_request]
env:
NODE_VERSION: '24.15.0'
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install Google Chrome
run: |
apt-get update
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
apt install -y ./google-chrome*.deb
- name: Install global packages
run: npm i -g pm2 @sasjs/cli wait-on
- name: Setup and start SASjs server
run: |
touch .env
echo RUN_TIMES=js >> .env
echo NODE_PATH=node >> .env
echo CORS=enable >> .env
echo WHITELIST=http://localhost:4200 >> .env
cat .env
curl -L https://github.com/sasjs/server/releases/latest/download/linux.zip > linux.zip
unzip linux.zip
pm2 start api-linux --wait-ready
- name: Write .npmrc file
run: echo "$NPMRC" > client/.npmrc
shell: bash
env:
NPMRC: ${{ secrets.NPMRC}}
- name: Install npm dependencies
run: |
cd client
# Decrypt and Install sheet
echo "${{ secrets.SHEET_PWD }}" | \
gpg --batch --yes --passphrase-fd 0 \
--output ./libraries/sheet-crypto.tgz \
--decrypt ./libraries/sheet-crypto.tgz.gpg
npm ci
npm install -g replace-in-files-cli
- name: Update appLoc in index.html
run: |
cd client
replace-in-files --regex='appLoc=".*?"' --replacement='appLoc="/proj/sasjs/genesis-mocks"' ./src/index.html
- name: Build Frontend
run: |
cd client
npm run build
- name: Deploy JS mocked services and frontend to the local SASjs Server instance
run: |
cd sas/mocks
npm ci
sasjs cbd -t server-ci
- name: Start frontend server
run: |
cd client
npx ng serve --host 0.0.0.0 --port 4200 &
wait-on http://localhost:4200
- name: Run Lighthouse CI
run: |
cd client
npx lhci autorun
- name: Lighthouse Result Artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: Lighthouse results
path: client/lighthouse-reports
include-hidden-files: true
+126 -60
View File
@@ -5,27 +5,31 @@ on:
branches:
- main
env:
NODE_VERSION: '24.5.0'
jobs:
Build-production-and-ng-test:
runs-on: ubuntu-latest
env:
CHROME_BIN: /usr/bin/google-chrome
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20.14.0
node-version: ${{ env.NODE_VERSION }}
- name: Write .npmrc file
run: |
touch client/.npmrc
echo '${{ secrets.NPMRC}}' > client/.npmrc
touch client/.npmrc
echo '${{ secrets.NPMRC}}' > client/.npmrc
- name: Install Chrome for Angular tests
run: |
apt-get update
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
apt install -y ./google-chrome*.deb;
export CHROME_BIN=/usr/bin/google-chrome
apt install -y ./google-chrome*.deb
- name: Write cypress credentials
run: echo "$CYPRESS_CREDS" > ./client/cypress.env.json
@@ -41,7 +45,7 @@ jobs:
npm ci
- name: Check audit
# Audit should fail and stop the CI if critical vulnerability found
# Audit should fail and stop the CI if critical vulnerability found
run: |
npm audit --audit-level=critical --omit=dev
cd ./sas
@@ -63,25 +67,32 @@ jobs:
Build-and-test-development:
runs-on: ubuntu-latest
needs: Build-production-and-ng-test
env:
CHROME_BIN: /usr/bin/google-chrome
# Pin OS locale
LANG: en_GB.UTF-8
LC_ALL: en_GB.UTF-8
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20.14.0
node-version: ${{ env.NODE_VERSION }}
- name: Write .npmrc file
run: |
touch client/.npmrc
echo '${{ secrets.NPMRC}}' > client/.npmrc
touch client/.npmrc
echo '${{ secrets.NPMRC}}' > client/.npmrc
- run: apt-get update
- run: wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
- run: apt install -y ./google-chrome*.deb;
- run: export CHROME_BIN=/usr/bin/google-chrome
- run: apt-get update -y
- run: apt-get -y install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb
- run: apt -y install jq
- name: Install system dependencies
run: |
apt-get update
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
apt install -y ./google-chrome*.deb
apt-get -y install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2t64 libxtst6 xauth xvfb jq zip locales
# Generate the en_GB.UTF-8 locale referenced by LANG/LC_ALL
locale-gen en_GB.UTF-8
update-locale LANG=en_GB.UTF-8 LC_ALL=en_GB.UTF-8
- name: Write cypress credentials
run: echo "$CYPRESS_CREDS" > ./client/cypress.env.json
@@ -96,17 +107,18 @@ jobs:
echo ${{ secrets.SHEET_PWD }} | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
npm ci
# Install pm2 and prepare SASJS server
- run: npm i -g pm2
- run: curl -L https://github.com/sasjs/server/releases/latest/download/linux.zip > linux.zip
- run: unzip linux.zip
- run: touch .env
- run: echo RUN_TIMES=js >> .env
- run: echo NODE_PATH=node >> .env
- run: echo CORS=enable >> .env
- run: echo WHITELIST=http://localhost:4200 >> .env
- run: cat .env
- run: pm2 start api-linux --wait-ready
- name: Setup and start SASjs server
run: |
npm i -g pm2
curl -L https://github.com/sasjs/server/releases/latest/download/linux.zip > linux.zip
unzip linux.zip
touch .env
echo RUN_TIMES=js >> .env
echo NODE_PATH=node >> .env
echo CORS=enable >> .env
echo WHITELIST=http://localhost:4200 >> .env
cat .env
pm2 start api-linux --wait-ready
- name: Deploy mocked services
run: |
@@ -116,11 +128,6 @@ jobs:
sasjs cbd -t server-ci
# sasjs request services/admin/makedata -t server-ci -d ./deploy/makeData4GL.json -c ./deploy/requestConfig.json -o ./output.json
- name: Install ZIP
run: |
apt-get update
apt-get install zip
- name: Prepare and run frontend and cypress
run: |
cd ./client
@@ -136,11 +143,12 @@ jobs:
replace-in-files --regex='"hosturl".*' --replacement='hosturl:"http://localhost:4200",' ./cypress.config.ts
cat ./cypress.config.ts
# Start frontend and run cypress
npm start & npx wait-on http://localhost:4200 && npx cypress run --browser chrome --spec "cypress/e2e/liveness.cy.ts,cypress/e2e/editor.cy.ts,cypress/e2e/excel-multi-load.cy.ts,cypress/e2e/excel.cy.ts,cypress/e2e/csv.cy.ts,cypress/e2e/filtering.cy.ts,cypress/e2e/licensing.cy.ts"
npx ng serve --host 0.0.0.0 --port 4200 & npx wait-on http://localhost:4200 && npx cypress run --browser chrome --spec "cypress/e2e/csv-limited.cy.ts,cypress/e2e/liveness.cy.ts,cypress/e2e/editor.cy.ts,cypress/e2e/excel-multi-load.cy.ts,cypress/e2e/excel.cy.ts,cypress/e2e/csv.cy.ts,cypress/e2e/filtering.cy.ts,cypress/e2e/licensing.cy.ts,cypress/e2e/viewer-labels.cy.ts"
- name: Zip Cypress videos
if: always()
run: |
mkdir -p ./client/cypress/videos
zip -r cypress-videos ./client/cypress/videos
- name: Add cypress videos artifacts
@@ -153,12 +161,22 @@ jobs:
release:
runs-on: ubuntu-latest
needs: [Build-production-and-ng-test, Build-and-test-development]
# The semantic-release step below uses the per-job token injected by Gitea
# Actions (secrets.GITEA_TOKEN). That token is scoped to THIS repository
# and its permissions are derived from this block.
# contents: write -> push CHANGELOG.md / package.json commit + tag, and
# create the Release via the Gitea API.
# The TSDOC_TOKEN and CODE_DATACONTROLLER_IO secrets continue to be used
# because they target *different* servers (webdoc.datacontroller.io and
# code.datacontroller.io), not the Gitea API.
permissions:
contents: write
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20.14.0
node-version: ${{ env.NODE_VERSION }}
- name: Write .npmrc file
run: |
@@ -168,17 +186,11 @@ jobs:
env:
NPMRC: ${{ secrets.NPMRC}}
- name: Install packages
- name: Install system packages
run: |
apt-get update
apt-get install zip -y
# sasjs cli is used to compile & build the SAS services
apt-get install -y zip jq doxygen
npm i -g @sasjs/cli
# jq is used to parse the release JSON
apt-get install jq -y
# doxygen is used for the SASJS docs
apt-get update
apt-get install doxygen -y
- name: Frontend Preliminary Build
description: We want to prevent creating empty release if frontend fails
@@ -191,12 +203,41 @@ jobs:
npm run build
- name: Create Empty Release (assets are posted later)
env:
# Use the ephemeral per-job token injected by Gitea Actions instead
# of a manually-managed PAT. This token always reports push=true on
# its own repo via the Gitea API, which is what the
# @saithodev/semantic-release-gitea verifyConditions step checks.
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
GITEA_URL: ${{ gitea.server_url }}
run: |
set -euo pipefail
npm i
npm i -g semantic-release
# We do a semantic-release DRY RUN to make the job fail if there are no changes to release
GITEA_TOKEN=${{ secrets.RELEASE_TOKEN }} GITEA_URL=https://git.datacontroller.io semantic-release --dry-run | grep -q "There are no relevant changes, so no new version is released." && exit 1
GITEA_TOKEN=${{ secrets.RELEASE_TOKEN }} GITEA_URL=https://git.datacontroller.io semantic-release
# We do a semantic-release DRY RUN to make the job fail if there are
# no changes to release. The previous implementation piped --dry-run
# into `grep`, which swallowed semantic-release's non-zero exit codes
# (e.g. auth/verifyConditions failures) — the real error then only
# surfaced from the second invocation. Capture output and inspect it
# so authentication failures fail the step cleanly with context.
echo "::group::semantic-release --dry-run"
DRY_EXIT=0
DRY_OUTPUT=$(semantic-release --dry-run 2>&1) || DRY_EXIT=$?
echo "$DRY_OUTPUT"
echo "::endgroup::"
if [ "$DRY_EXIT" -ne 0 ]; then
echo "::error::semantic-release dry-run failed (exit $DRY_EXIT). See log above; common cause is the Gitea token lacking push permission on this repo (verifyConditions of @saithodev/semantic-release-gitea)."
exit "$DRY_EXIT"
fi
if echo "$DRY_OUTPUT" | grep -q "There are no relevant changes, so no new version is released."; then
echo "::error::No releasable changes since last tag - aborting release job."
exit 1
fi
semantic-release
- name: Frontend Build
description: Must be created AFTER the release as the version (git tag) is used in the interface
@@ -228,6 +269,8 @@ jobs:
cp sasjs/utils/favicon.ico ../client/dist/favicon.ico
sasjs c -t server
rm -rf sasjsbuild/tests
server_apploc="/Public/app/dc"
sed -i "s|apploc=\"[^\"]*\"|apploc=\"${server_apploc}\"|g" sasjsbuild/services/web/index.html
sasjs b -t server
cp sasjsbuild/server.json.zip ./sasjs_server.json.zip
@@ -268,17 +311,40 @@ jobs:
surfer put --token ${{ secrets.CODE_DATACONTROLLER_IO }} --server code.datacontroller.io sasjsbuild/sasdocs/* /
- name: Upload assets to release
env:
# Use the same ephemeral per-job Gitea Actions token that the
# "Create Empty Release" step uses for semantic-release. It is scoped
# to this repo and is granted write access via the workflow's
# `permissions:` block at the top of the file (see contents: write).
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
run: |
RELEASE_ID=`curl -k 'https://git.datacontroller.io/api/v1/repos/dc/dc/releases/latest?access_token=${{ secrets.RELEASE_TOKEN }}' | jq -r '.id'`
RELEASE_BODY=`curl -k 'https://git.datacontroller.io/api/v1/repos/dc/dc/releases/latest?access_token=${{ secrets.RELEASE_TOKEN }}' | jq -r '.body'`
# Update body
curl --data '{"draft": false,"body":"'"$RELEASE_BODY\n\nFor installation instructions, please visit https://docs.datacontroller.io/"'"}' -X PATCH --header 'Content-Type: application/json' -k https://git.datacontroller.io/api/v1/repos/dc/dc/releases/$RELEASE_ID?access_token=${{ secrets.RELEASE_TOKEN }}
set -euo pipefail
# Send the token via Authorization header rather than ?access_token=
# (the query-string form is deprecated and leaks into access logs).
AUTH_HEADER="Authorization: token ${GITEA_TOKEN}"
BASE="${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}"
RELEASE_JSON=$(curl -k --fail-with-body -sS -H "$AUTH_HEADER" "$BASE/releases/latest")
RELEASE_ID=$(echo "$RELEASE_JSON" | jq -r '.id')
RELEASE_BODY=$(echo "$RELEASE_JSON" | jq -r '.body')
# Update body (also confirms the token has contents:write on this repo)
curl -k --fail-with-body -sS -X PATCH \
-H "$AUTH_HEADER" \
-H 'Content-Type: application/json' \
--data "$(jq -n --arg body "$RELEASE_BODY"$'\n\nFor installation instructions, please visit https://docs.datacontroller.io/' \
'{draft:false, body:$body}')" \
"$BASE/releases/$RELEASE_ID"
# Upload assets
URL="https://git.datacontroller.io/api/v1/repos/dc/dc/releases/$RELEASE_ID/assets?access_token=${{ secrets.RELEASE_TOKEN }}"
curl -k $URL -F attachment=@frontend.zip
curl -k $URL -F attachment=@sas/demostream_sas9.sas
curl -k $URL -F attachment=@sas/viya.sas
curl -k $URL -F attachment=@sas/sasjs_server.json.zip
curl -k $URL -F attachment=@sas/sas9.sas
curl -k $URL -F attachment=@sas/viya_noweb.sas
curl -k $URL -F attachment=@sas/viya_noweb.json
URL="$BASE/releases/$RELEASE_ID/assets"
for f in frontend.zip \
sas/demostream_sas9.sas \
sas/viya.sas \
sas/sasjs_server.json.zip \
sas/sas9.sas \
sas/viya_noweb.sas \
sas/viya_noweb.json; do
echo "Uploading $f ..."
curl -k --fail-with-body -sS -H "$AUTH_HEADER" "$URL" -F "attachment=@$f"
done
+2
View File
@@ -14,6 +14,7 @@ client/documentation
client/**/sheet-crypto.tgz
client/.nx
client/libraries/sheet-crypto.tgz
client/lighthouse-reports
cypress.env.json
sasjsbuild
sasjsresults
@@ -21,3 +22,4 @@ sasjsresults
.sasjsrc
client/.npmrc
*~
.lighthouseci
+3
View File
@@ -1 +1,4 @@
legacy-peer-deps=true
ignore-scripts=true
save-exact=true
fund=false
+1
View File
@@ -1,5 +1,6 @@
{
"cSpell.words": [
"Handsontable",
"Licence",
"SYSERRORTEXT",
"SYSWARNINGTEXT",
+355
View File
@@ -1,3 +1,358 @@
## [7.10.1](https://git.datacontroller.io/dc/dc/compare/v7.10.0...v7.10.1) (2026-07-15)
### Bug Fixes
* **editor:** order validation rules by COLHEADERS, not cols[].VARNUM ([4c2aca3](https://git.datacontroller.io/dc/dc/commit/4c2aca34951d6d5c5cf2df69d5bf4256a9290d53))
# [7.10.0](https://git.datacontroller.io/dc/dc/compare/v7.9.1...v7.10.0) (2026-07-13)
### Bug Fixes
* adapt column validation to vertical-array COLTYPE ([#253](https://git.datacontroller.io/dc/dc/issues/253)) ([3016750](https://git.datacontroller.io/dc/dc/commit/301675052fb5cbcb11f54c370ad7eda1d60461fd))
* backend updates to bring back labels under [#240](https://git.datacontroller.io/dc/dc/issues/240) ([fb94840](https://git.datacontroller.io/dc/dc/commit/fb94840016f067fa3523a3539dd0616d8970eed0))
* **editor:** normalise unpadded time strings on spreadsheet import ([7b4b4eb](https://git.datacontroller.io/dc/dc/commit/7b4b4ebeeb0e34ec5fcd5c485741d7bbddb43b7e))
* **hot:** row rendering on resize, resolve blank whitespace ([c7ba025](https://git.datacontroller.io/dc/dc/commit/c7ba025d39aa9681ad51f239503e884fb26613e2))
* including datadictionary descs in labels, [#240](https://git.datacontroller.io/dc/dc/issues/240) ([e7453a9](https://git.datacontroller.io/dc/dc/commit/e7453a9305263faf4d4f288d50977121353040c7))
* pass variable formats to front-end in a vertical array instead of horizontal list ([691d6f2](https://git.datacontroller.io/dc/dc/commit/691d6f277ea87051f539681b34da2f39cade8fc3))
* remove coltype from sasparams table, add it to cols table. ([8101400](https://git.datacontroller.io/dc/dc/commit/81014001ac4da6b8d262c72c65e0c657f186ec84))
* trim leading/trailing whitespace from Excel header cells on upload ([2728dac](https://git.datacontroller.io/dc/dc/commit/2728dac873ab8c9fc44413f873e4730515ca0e21))
* **va:** date filters refactor, support more dates ([df5c975](https://git.datacontroller.io/dc/dc/commit/df5c9758697d7481e67d20ed7ff81e9ce4e92ec0))
* **va:** enable readOnly and hold filtering while editing ([82a254d](https://git.datacontroller.io/dc/dc/commit/82a254d22cb7aaf75aae7b332f0b07fc8bda3e44))
* **va:** enable Upload button in va mode ([1beb3d4](https://git.datacontroller.io/dc/dc/commit/1beb3d490d0e7b8d5fa7985f121495ed62740cfe))
* **va:** guard deferred contextMenu toggle against destroyed instance ([b5e9b25](https://git.datacontroller.io/dc/dc/commit/b5e9b2531924bec1a23c67d3ad0ca3f512a75655))
### Features
* adding DC_MAXOBS_WEBVIEW config item, closes [#258](https://git.datacontroller.io/dc/dc/issues/258) ([ba7b610](https://git.datacontroller.io/dc/dc/commit/ba7b61082d39767f9539a84de6742a1704573e81))
* viewer/editor column label display toggle (?labels=true) ([25c12f2](https://git.datacontroller.io/dc/dc/commit/25c12f2b18adda09e6edc93eba263bd5ea0f7409))
### Reverts
* enable upload button in va mode ([0d7fd34](https://git.datacontroller.io/dc/dc/commit/0d7fd342970744489a2e6c6fb08f239fe853cb81))
## [7.9.1](https://git.datacontroller.io/dc/dc/compare/v7.9.0...v7.9.1) (2026-06-30)
### Bug Fixes
* workflow file upload using action PAT ([a161670](https://git.datacontroller.io/dc/dc/commit/a161670b8656614f61d42ee673064ba8f010b71c))
# [7.9.0](https://git.datacontroller.io/dc/dc/compare/v7.8.2...v7.9.0) (2026-06-29)
### Bug Fixes
* allow large retained key values. Closes [#248](https://git.datacontroller.io/dc/dc/issues/248). ([6a30de3](https://git.datacontroller.io/dc/dc/commit/6a30de37cad21b7b94bf550ce7afbc804e6f0f90))
* build issues ([#245](https://git.datacontroller.io/dc/dc/issues/245)) ([77b4bb3](https://git.datacontroller.io/dc/dc/commit/77b4bb310e62adb450805488fc69a132435d0eb7))
* **ci:** bump Lighthouse to 13.4.0 ([47ba03e](https://git.datacontroller.io/dc/dc/commit/47ba03ec637113d8867372d64484927de9b645ff))
* **dc-validator:** display date/time/datetime cells as raw ISO, locale-independent ([9ca5719](https://git.datacontroller.io/dc/dc/commit/9ca57193b4aafa1e7bf216301cdf197cea6ec4c9))
* **editor:** await dynamic validation on paste; defer spinner ([ea03bde](https://git.datacontroller.io/dc/dc/commit/ea03bdecc5aaf917052add2c7e6fdc192b9974ce))
* **editor:** cancelEdit will reset cell's valid state ([51071b4](https://git.datacontroller.io/dc/dc/commit/51071b463bb311c6db48c301daa8cc7dfd92144a))
* **editor:** preserve commas in dropdown source values ([6af49cf](https://git.datacontroller.io/dc/dc/commit/6af49cf1fd8aa27cbf99f9514cca537339cdbf7e))
* **editor:** retry initSetup when the Handsontable instance isn't ready ([53b7ee0](https://git.datacontroller.io/dc/dc/commit/53b7ee0cb23329fa4f9d6211ffe6b102053fade9))
* **handsontable:** add exceljs ([38cb1e2](https://git.datacontroller.io/dc/dc/commit/38cb1e207b58822d71ef803e770a93bc7265bc1c))
* **handsontable:** horizontal scrollbar in dropdown ([ac0bd10](https://git.datacontroller.io/dc/dc/commit/ac0bd102127ced6b6a908a7244f35002f22cb669))
* **handsontable:** restore dark mode for v17 ([9d97bf7](https://git.datacontroller.io/dc/dc/commit/9d97bf7ea13199ab9dab10c5fc47e07b4751fb9a))
* **handsontable:** update licenseChecker exclude buffers - exceljs dep ([12c7d30](https://git.datacontroller.io/dc/dc/commit/12c7d30894b5c3709b683189da6e52255ad0a3e7))
* incrementing rk val ([7444209](https://git.datacontroller.io/dc/dc/commit/74442096bac1481a4992e5c97dac47c8d277cc3f))
* **licensing:** add protocol info ([3668a74](https://git.datacontroller.io/dc/dc/commit/3668a7426fec078ad7939b5bc815d7ad920c0d62)), closes [#178](https://git.datacontroller.io/dc/dc/issues/178)
* migrate handsontables to v17 ([1b73e35](https://git.datacontroller.io/dc/dc/commit/1b73e355b78a0039aa48f7a137095eb5470c08e2))
* **mocks:** return dynamic cell values as OBJ to match new contract ([a15fdc4](https://git.datacontroller.io/dc/dc/commit/a15fdc401e6cdcf934a5d599f8b7c4d197e9d2d4))
* removing display_value in SAS code ([9b18a45](https://git.datacontroller.io/dc/dc/commit/9b18a45d354ad617e590d62758ef761b01b3095a))
* rule_type ([adc6424](https://git.datacontroller.io/dc/dc/commit/adc6424211a83fb03b267bf8fa6b6d9fa72f185b))
* sending dynamic_values and dynamic_extended_values back as OBJ instead of ARR ([e859d33](https://git.datacontroller.io/dc/dc/commit/e859d3354e82f0faad53a0b8aa14ed66974cbffa))
* upcasing RAW_VALUE, bumping core, fixing tests ([982d507](https://git.datacontroller.io/dc/dc/commit/982d507ae3ad2ccc08e65678956199cd54b696f2))
* validate pasted values ([b661580](https://git.datacontroller.io/dc/dc/commit/b661580c604c2923b930911a1881aec7b06ecf23))
* viewLibs now fires only once, libPromise shared between the calls ([dc4e07a](https://git.datacontroller.io/dc/dc/commit/dc4e07a692a5e839986abb69b17e51024c4cdc03))
### Features
* **edit-record:** use native date/time/datetime pickers ([2d29346](https://git.datacontroller.io/dc/dc/commit/2d29346cbf09ad057743e6f5ebea12f3257f246e))
* **editor:** add READONLY, HIDDEN, ROUND and NUMBER_FORMAT validation rules ([4ea604f](https://git.datacontroller.io/dc/dc/commit/4ea604f9fbdd429108fe063b306ea376e756c107))
* **editor:** migrate date/time/datetime to HOT 17 Intl cell types ([f70ea2f](https://git.datacontroller.io/dc/dc/commit/f70ea2fe712ff9a42bf47387f9f0aaa6d1a1f511))
* **editor:** paste-validation overlay with cancel and confirm ([609731f](https://git.datacontroller.io/dc/dc/commit/609731ff99185a162614df13bde7afb1a2c3bbca))
* **editor:** SAS VA data-driven content embed mode (?embed=va) ([ffa3ff9](https://git.datacontroller.io/dc/dc/commit/ffa3ff9c103d6d80677f4f67e7d53418f201f491))
* **editor:** VA embed filter modes with pending UX ([ebe6972](https://git.datacontroller.io/dc/dc/commit/ebe6972dd7d38c5742ed7eb446e0448d98b2c8a7))
* **editor:** validate autofilled cells; fix paste validation lag ([11ee49a](https://git.datacontroller.io/dc/dc/commit/11ee49a57a0bc7ccd59e91d5fe37bb5db1bf7d69))
* **handsontable:** enable export plugin, add export menu items ([29aaa72](https://git.datacontroller.io/dc/dc/commit/29aaa72c6047bb309d325887ab602a9369a1ad74))
* migration script for adding new validation rules under [#241](https://git.datacontroller.io/dc/dc/issues/241) ([9f84a58](https://git.datacontroller.io/dc/dc/commit/9f84a580fb762da8c1c796a35eb4f531df1caf13))
* new validation rules in mpe_selectbox. [#241](https://git.datacontroller.io/dc/dc/issues/241) ([2f5d3df](https://git.datacontroller.io/dc/dc/commit/2f5d3dfc387f02353601aaa844aaeabde3e9f3e5))
### Reverts
* eager-load feature modules instead of lazy-loading ([3429a7a](https://git.datacontroller.io/dc/dc/commit/3429a7a2a6ecfa6115394e1c842061cc68bf2be3))
* **editor:** DISPLAY_VALUE change ([eb015d7](https://git.datacontroller.io/dc/dc/commit/eb015d712b01687f36ac92e38ab576a7c114b1ba))
## [7.8.2](https://git.datacontroller.io/dc/dc/compare/v7.8.1...v7.8.2) (2026-05-20)
### Bug Fixes
* bumping ws package ([2382a55](https://git.datacontroller.io/dc/dc/commit/2382a559a5ac32b0f815776a90207650d5809ba6))
* enabling version restore for non admin users ([5d889d8](https://git.datacontroller.io/dc/dc/commit/5d889d824cc2f8e4ea089cbb578453125dc4ba6c))
## [7.8.1](https://git.datacontroller.io/dc/dc/compare/v7.8.0...v7.8.1) (2026-05-15)
### Bug Fixes
* **sasjs:** enable runAsTask ([f1a26e1](https://git.datacontroller.io/dc/dc/commit/f1a26e132eba7fa2ac64754940b52ea46c6619b3))
# [7.8.0](https://git.datacontroller.io/dc/dc/compare/v7.7.3...v7.8.0) (2026-05-15)
### Bug Fixes
* enabling DSN=*ALL* in MPE_SECURITY ([7d94cb2](https://git.datacontroller.io/dc/dc/commit/7d94cb2ae4a3f6c1fa1011ae0fced7083a2f2793))
* providing default values for RULE_ACTIVE on MPE_VALIDATIONS ([f031b4e](https://git.datacontroller.io/dc/dc/commit/f031b4eb8925397e60dcc739a721cfbbb6da8dff))
* switch away from api usage for CASLIB metadata ([ce921a0](https://git.datacontroller.io/dc/dc/commit/ce921a032a8970b8078a463a41da884e1fa71bc3))
* use correct debug param for runAsTask ([bb80476](https://git.datacontroller.io/dc/dc/commit/bb8047676749814d3b86eea666726dbe4bf5f270))
### Features
* add runAsTask config attribute parser ([1635bc9](https://git.datacontroller.io/dc/dc/commit/1635bc9c451bc221f386241007f594096f114b4f))
* enabling *ALL* option by default in MPE_SECURITY (DSN col) ([93d4ab6](https://git.datacontroller.io/dc/dc/commit/93d4ab65acce7b5b35e448146f9893964ad2cca3))
## [7.7.3](https://git.datacontroller.io/dc/dc/compare/v7.7.2...v7.7.3) (2026-05-12)
### Bug Fixes
* move cas session assign to settings.sas and abort when lib is unassigned ([65f0b97](https://git.datacontroller.io/dc/dc/commit/65f0b979a401277b3e070d409659ae3fae2ff8c0))
## [7.7.2](https://git.datacontroller.io/dc/dc/compare/v7.7.1...v7.7.2) (2026-05-07)
### Bug Fixes
* **client:** bundle Metropolis font locally to satisfy CSP ([9546fcd](https://git.datacontroller.io/dc/dc/commit/9546fcd6312f3e81f746ef6e32ef398810ed434a))
* **client:** clear angular build cache on font strip to avoid stale dist ([503cb08](https://git.datacontroller.io/dc/dc/commit/503cb08b2fa40397434189f9c20eff3358eb7010))
* **client:** postinstall removal of Metropolis [@font-face](https://git.datacontroller.io/font-face) from @clr/ui ([e6397ce](https://git.datacontroller.io/dc/dc/commit/e6397cecc13afe2a9238bdfb2b4b9b81f38d055c))
* **client:** serve text-security-disc font locally ([80ce80e](https://git.datacontroller.io/dc/dc/commit/80ce80ece40012e59c7cd0340b4aa9a9aca46443))
* **editor:** preserve numeric type for SAS num cols with static SOFTSELECT/HARDSELECT ([05a3289](https://git.datacontroller.io/dc/dc/commit/05a328976ea3d1d6ef7559850369aa580f0d067f))
## [7.7.1](https://git.datacontroller.io/dc/dc/compare/v7.7.0...v7.7.1) (2026-05-05)
### Bug Fixes
* **client:** bump adapter ([d26f7d2](https://git.datacontroller.io/dc/dc/commit/d26f7d2511008634124c7d6fde115abb43db9c43))
* **sas:** bump cli ([d60029d](https://git.datacontroller.io/dc/dc/commit/d60029deae0ec21f3b8570461e2a4ca041d58f72))
# [7.7.0](https://git.datacontroller.io/dc/dc/compare/v7.6.0...v7.7.0) (2026-05-04)
### Bug Fixes
* bump adapter to 4.16.6 ([1707f38](https://git.datacontroller.io/dc/dc/commit/1707f3802a97de8c659f1a88c92fc917e8a30615))
* remove data:image/svg+xml CSP violation, use class instead changing style directly ([d66eb5d](https://git.datacontroller.io/dc/dc/commit/d66eb5dfc2dbb01f1e6c0c7d15fc2ad2a39dd829))
* remove WORK, SASUSER and CASUSER as library options. [#224](https://git.datacontroller.io/dc/dc/issues/224) ([ec66631](https://git.datacontroller.io/dc/dc/commit/ec66631a33aabb8ab2f92fe22c15440127085782))
### Features
* auto-save CAS tables [#224](https://git.datacontroller.io/dc/dc/issues/224) ([40d04a5](https://git.datacontroller.io/dc/dc/commit/40d04a53c4c00183116bdbd08397e0f2ffb1f578))
* autoload CAS tables. [#224](https://git.datacontroller.io/dc/dc/issues/224) ([d5ebb01](https://git.datacontroller.io/dc/dc/commit/d5ebb01ce381f5f4ec06de041f3ab9e632c02e43))
# [7.6.0](https://git.datacontroller.io/dc/dc/compare/v7.5.0...v7.6.0) (2026-04-03)
### Bug Fixes
* add label and tooltip for libref download, sanitise input ([52d5803](https://git.datacontroller.io/dc/dc/commit/52d58036a40e25847e900f9b04a77dbcc409c12b))
### Features
* configurable email alerts. Closes [#217](https://git.datacontroller.io/dc/dc/issues/217) ([2ccf0d1](https://git.datacontroller.io/dc/dc/commit/2ccf0d11000129629a0665421135b7530af9892f))
# [7.5.0](https://git.datacontroller.io/dc/dc/compare/v7.4.1...v7.5.0) (2026-04-03)
### Bug Fixes
* add workflow audits, update deps ([66e98a9](https://git.datacontroller.io/dc/dc/commit/66e98a96cbd092e762b94a04660f8e17ca003ceb))
* allow CSV uploads with licence row limit ([5b260e4](https://git.datacontroller.io/dc/dc/commit/5b260e49153dd85bc0023ad94d8a5f57b8ffa6dc)), closes [#213](https://git.datacontroller.io/dc/dc/issues/213)
* bumping cli and pinning versions in .npmrc ([80039f4](https://git.datacontroller.io/dc/dc/commit/80039f4876c8e09dc477678e1eff58329094c9e9))
* guard CSV upload with fileUpload licence flag ([ed40df6](https://git.datacontroller.io/dc/dc/commit/ed40df62953c3055770b5cbf50738f4a48b943cd))
* parse embed param from window.location.hash for hash router compatibility ([0269c24](https://git.datacontroller.io/dc/dc/commit/0269c2421db245f7f5405678605cb4d4587e2a67))
* quote CSV char values. Closes [#215](https://git.datacontroller.io/dc/dc/issues/215) ([d9980e8](https://git.datacontroller.io/dc/dc/commit/d9980e866d1a2fe7a731ff279d73accd35003e67))
* resolve outer promise in parseCsvFile for non-WLATIN1 path ([4ee15e1](https://git.datacontroller.io/dc/dc/commit/4ee15e1b6e83f27f279fc345e6998452a8f64d7e))
* use XLSX for CSV row truncation to handle new lines in values ([6d590c0](https://git.datacontroller.io/dc/dc/commit/6d590c050dcd593a73464fae5604f774f016b10d))
### Features
* add embed URL parameter to hide header and back button ([b0dc441](https://git.datacontroller.io/dc/dc/commit/b0dc441d681369e06eee58288dbdbb236f930bdc)), closes [#214](https://git.datacontroller.io/dc/dc/issues/214)
* add target libref input to config download ([a89657b](https://git.datacontroller.io/dc/dc/commit/a89657b0b81b9c531f64c0dda2714b4eb16c4bc9)), closes [#212](https://git.datacontroller.io/dc/dc/issues/212)
* export config service to allow dclib swapping. Closes [#212](https://git.datacontroller.io/dc/dc/issues/212) ([326c26f](https://git.datacontroller.io/dc/dc/commit/326c26fddfa88a0dc4ca79d3bd0c77c4d807f37c))
## [7.4.1](https://git.datacontroller.io/dc/dc/compare/v7.4.0...v7.4.1) (2026-03-12)
### Bug Fixes
* support for SASIOSNF engine (SNOW alias) plus meta assignment ([7694d1b](https://git.datacontroller.io/dc/dc/commit/7694d1b0fb2bd0407c8598147fbae87a00d889a8))
# [7.4.0](https://git.datacontroller.io/dc/dc/compare/v7.3.0...v7.4.0) (2026-02-20)
### Bug Fixes
* cli bump for mf_getscheme support ([a84ba41](https://git.datacontroller.io/dc/dc/commit/a84ba41ea9f0c97ae24f0a572b8cf5ec200f2132))
* missing upcase on SNOW section, plus local sasjs target ([dc20064](https://git.datacontroller.io/dc/dc/commit/dc200646f7df2fd1910841f392c314532aae7581))
### Features
* SAS code changes for snowflake support ([e273e87](https://git.datacontroller.io/dc/dc/commit/e273e870efbf7875db869b760f2c7b1f39d571ae))
# [7.3.0](https://git.datacontroller.io/dc/dc/compare/v7.2.8...v7.3.0) (2026-02-10)
### Bug Fixes
* bump xlsx, add crypto-shim ([8dc18b1](https://git.datacontroller.io/dc/dc/commit/8dc18b155abfc20fd0b043e0d70bbbc17e6b6811))
* correctly applying deletes on viya, also ([46cdeb0](https://git.datacontroller.io/dc/dc/commit/46cdeb0babee6870553a41877cbe85204e7099c4))
* crypto module requirement for sheetjs/crypto package ([505d0af](https://git.datacontroller.io/dc/dc/commit/505d0af2b3b3c1c79c65045dcaffc263e0f8e796))
* disable parsing excel in web worker beacuse it breaks in the stream apps ([280bdee](https://git.datacontroller.io/dc/dc/commit/280bdeeb1b82f00689f46c68a3cde3f2d24bc18f))
* Display all contexts when installing DC on Viya ([d41f88f](https://git.datacontroller.io/dc/dc/commit/d41f88f8bf5bb2c725ee3edba085e8961ab8c727))
* **edit:** use cellValidation keys and hotDataSchema to fill in defaults on add row ([4957548](https://git.datacontroller.io/dc/dc/commit/495754816c0e757b8f8b1c0ad51246dc7b65d957))
* enabling closeouts for UPDATE in CAS tables ([8b8e8ae](https://git.datacontroller.io/dc/dc/commit/8b8e8aec159ff2f50cfa4683bcd7a25aabb75bf8))
* enabling rollback when the table has formatted values ([815d6e9](https://git.datacontroller.io/dc/dc/commit/815d6e97a8e304d79d48cc949ba126e02a318dc1))
* improvements to validations ([6ceb681](https://git.datacontroller.io/dc/dc/commit/6ceb6814633691b6d4ac2cb898cfb75e9d609102))
* remove IE checks and conditions ([ece6bd1](https://git.datacontroller.io/dc/dc/commit/ece6bd1d787d722531334fc4f1396a94cf6d92ec))
* updates to demodata to enable auto CAS promote ([7740d2a](https://git.datacontroller.io/dc/dc/commit/7740d2ac8694295b33b40a30603d8239818896f5))
* upgrade angular core and compiler ([aecd597](https://git.datacontroller.io/dc/dc/commit/aecd5976875a7c01189248c5f5aa3478b28c1ab2))
* using fcopy instead of binary copy for file upload, for Viya 2026 compatibility ([716ee6e](https://git.datacontroller.io/dc/dc/commit/716ee6eba0a28f4f0a7a96b0719caf15da9b6e78))
* **viewer:** search causing blank Handsontable ([338c7a2](https://git.datacontroller.io/dc/dc/commit/338c7a2e418c47e34331bd04718cd816f978837c)), closes [#206](https://git.datacontroller.io/dc/dc/issues/206)
### Features
* adding demo data job ([8c2aeac](https://git.datacontroller.io/dc/dc/commit/8c2aeacc85da5c106c356709cefcb412ed0a71db))
* **dq rules:** notnull validation when invalid cell, will auto populate a default value ([96f2518](https://git.datacontroller.io/dc/dc/commit/96f2518af9e547956be5862a1322d9ab8e07369b))
## [7.2.8](https://git.datacontroller.io/dc/dc/compare/v7.2.7...v7.2.8) (2026-02-06)
### Bug Fixes
* bump adapter version ([f4c8699](https://git.datacontroller.io/dc/dc/commit/f4c8699aaf0b1e01b447296978a4f6dedc8903f9))
## [7.2.7](https://git.datacontroller.io/dc/dc/compare/v7.2.6...v7.2.7) (2026-02-05)
### Bug Fixes
* dclib not found error in getchangeinfo job ([86791db](https://git.datacontroller.io/dc/dc/commit/86791dbaca39034a19bf8f34efbddf898c57f2f7))
## [7.2.6](https://git.datacontroller.io/dc/dc/compare/v7.2.5...v7.2.6) (2026-01-05)
### Bug Fixes
* **deps:** update angular and moment ([8c5b357](https://git.datacontroller.io/dc/dc/commit/8c5b357dd286db331a6dcdeb3fd499fe3b634288))
## [7.2.5](https://git.datacontroller.io/dc/dc/compare/v7.2.4...v7.2.5) (2025-12-09)
### Bug Fixes
* (build) rebuilt package-lock files ([bfbfd55](https://git.datacontroller.io/dc/dc/commit/bfbfd55fe7e2dff3ce707763a2c7939ff365318b))
* (deps) bump @sasjs/cli and @sasjs/core ([d7c7302](https://git.datacontroller.io/dc/dc/commit/d7c7302c12ac60f355ab9b3b1b461fcf7d0719b8))
* (deps) bumped @sasjs/core, @sasjs/cli, @sasjs/utils and @sasjs/adapter ([af1657e](https://git.datacontroller.io/dc/dc/commit/af1657e226a4efd22cc87401a3850c4a665c2680))
* configurable audit table on restore check ([26ce95f](https://git.datacontroller.io/dc/dc/commit/26ce95f7c1d2260f81c240cd6b058db154d997e4)), closes [#193](https://git.datacontroller.io/dc/dc/issues/193)
* improved testing ([fb3c49a](https://git.datacontroller.io/dc/dc/commit/fb3c49aa8bfdc6acf2ae3034b885010dcdce32a6))
* output values to intended macro variables ([43ae73c](https://git.datacontroller.io/dc/dc/commit/43ae73c5f3ad919394201f54984b61bb2a52fcfe))
## [7.2.4](https://git.datacontroller.io/dc/dc/compare/v7.2.3...v7.2.4) (2025-10-14)
### Bug Fixes
* ensure reload after applying licence key ([cb1978b](https://git.datacontroller.io/dc/dc/commit/cb1978bcaf23b0bf45b5d3b78b9707fd4e48a5f4))
* snyk report security patches ([387f512](https://git.datacontroller.io/dc/dc/commit/387f5122f1ea6dff55d23c9223f17737283a94d3))
## [7.2.3](https://git.datacontroller.io/dc/dc/compare/v7.2.2...v7.2.3) (2025-10-02)
### Bug Fixes
* opening second table in viewer throws an error ([6c6b1cb](https://git.datacontroller.io/dc/dc/commit/6c6b1cbf460e5291ec746af017e764b894fff8d5))
## [7.2.2](https://git.datacontroller.io/dc/dc/compare/v7.2.1...v7.2.2) (2025-09-23)
### Bug Fixes
* jsrsasign, @sasjs/cli bump ([365f129](https://git.datacontroller.io/dc/dc/commit/365f12996db3ef50a4f4f099d5af15696c43bb42))
## [7.2.1](https://git.datacontroller.io/dc/dc/compare/v7.2.0...v7.2.1) (2025-08-08)
### Bug Fixes
* removing localhost from index.html ([225e693](https://git.datacontroller.io/dc/dc/commit/225e693d1fd4381f2b8ce42fecb508f0a9e9dad8))
# [7.2.0](https://git.datacontroller.io/dc/dc/compare/v7.1.1...v7.2.0) (2025-08-08)
### Bug Fixes
* **ci:** cypress dependency package not available anymore ([26cdd73](https://git.datacontroller.io/dc/dc/commit/26cdd733315ef8babe9498ce93f6eb29c587dabd))
* **hot v16 migration:** multi dataset fixed issues, and cypress tests adapted ([712b384](https://git.datacontroller.io/dc/dc/commit/712b3848480a8769d149e00b0d2de91396022b66))
* obsolete cypress deps ([2ba4b53](https://git.datacontroller.io/dc/dc/commit/2ba4b5383e23bff8dfeb82b0ef473e5871c94709))
* remaining hot migrations - handsontable/angular-wrapper ([b419cd5](https://git.datacontroller.io/dc/dc/commit/b419cd507837e846e9dfcc6b729254d56cc196e6))
### Features
* lighthouse accessibility check pipeline ([670ec2c](https://git.datacontroller.io/dc/dc/commit/670ec2c71cb2d24e9d79e297a8cbc6136aa315c8))
## [7.1.1](https://git.datacontroller.io/dc/dc/compare/v7.1.0...v7.1.1) (2025-07-24)
### Bug Fixes
* **viewboxes:** hot v16 fails to load because of relative height `100%` ([672dd6d](https://git.datacontroller.io/dc/dc/commit/672dd6d4f1fda27e3706dd7caa42b45922319497))
# [7.1.0](https://git.datacontroller.io/dc/dc/compare/v7.0.3...v7.1.0) (2025-07-23)
### Bug Fixes
* adapter bump ([b495c41](https://git.datacontroller.io/dc/dc/commit/b495c41626c85b7c4141d9361e4d3a826efd6c05))
* bumping CLI to 4.12.10 ([a08a717](https://git.datacontroller.io/dc/dc/commit/a08a717ca8d49e8a7d63f3fd91c6a7d42a1d6d8b))
* bumping sasjs/core and sasjs/cli ([63e9af4](https://git.datacontroller.io/dc/dc/commit/63e9af402ed65f6be4426e76ee1376a40e6ed097))
### Features
* improving accessibility score up to 100, hot update to v16.0.1 ([71c308d](https://git.datacontroller.io/dc/dc/commit/71c308d052400ecedc03f8020a5a69471ac6b116))
## [7.0.3](https://git.datacontroller.io/dc/dc/compare/v7.0.2...v7.0.3) (2025-06-26)
### Bug Fixes
* makedata vars ([e7cb471](https://git.datacontroller.io/dc/dc/commit/e7cb471c0b60058b03fe8cbed5e3e2e70dd72e26))
* viya deploy makedata missing params ([7a82316](https://git.datacontroller.io/dc/dc/commit/7a8231615cb56710351fae5868e8fdeed54d180c))
## [7.0.2](https://git.datacontroller.io/dc/dc/compare/v7.0.1...v7.0.2) (2025-06-21)
### Bug Fixes
* **viya deploy:** run makedata in new window to ensure logs are available for the user ([0b4042a](https://git.datacontroller.io/dc/dc/commit/0b4042af6011fdc65cfaaa5d4b1d8f48cd67f3b3))
## [7.0.1](https://git.datacontroller.io/dc/dc/compare/v7.0.0...v7.0.1) (2025-06-11)
+24 -1
View File
@@ -38,4 +38,27 @@ For further information:
* Docs: https://docs.datacontroller.io
* Code: https://code.datacontroller.io
For support, contact support@4gl.io or reach out on [Matrix](https://matrix.to/#/#dc:4gl.io)!
For support, contact support@4gl.io or reach out on [Matrix](https://matrix.to/#/#dc:4gl.io)!
## Development
### Lighthouse CI
This project includes automated Lighthouse performance and accessibility checks that run on pull requests. The checks ensure:
- **Accessibility Score**: Minimum 1.0 (100%) median score across all tested pages
The Lighthouse CI workflow:
1. Sets up the development environment with SASjs server and mocked services
2. Builds and serves the Angular frontend
3. Installs Chrome and runs `lhci autorun` (Lighthouse CI) against key pages
4. Uploads results as artifacts for review
To run Lighthouse checks locally:
```bash
cd client
npm install
npm run lighthouse
```
Configuration is in `client/lighthouserc.js` (URL list, `desktop` preset, Chrome flags, assertions).
+4
View File
@@ -0,0 +1,4 @@
# Auto-generated at build time (node ./src/version.ts) — gitignored, never linted.
# Formerly skipped via root .gitignore when prettier ran from the repo root;
# now that lint runs from client/, prettier reads this CWD-local ignore instead.
src/environments/version.ts
+23 -30
View File
@@ -25,6 +25,7 @@
"options": {
"allowedCommonJsDependencies": [
"handsontable",
"exceljs",
"core-js",
"pikaday",
"querystring",
@@ -41,6 +42,8 @@
"zone.js",
"text-encoding",
"crypto-js/md5",
"crypto-js/sha1",
"crypto-js/sha512",
"buffer",
"numbro",
"@clr/icons",
@@ -51,26 +54,30 @@
"base64-arraybuffer",
"@handsontable/formulajs"
],
"polyfills": [
"src/polyfills.ts",
"zone.js"
],
"polyfills": ["src/polyfills.ts", "zone.js"],
"outputPath": "dist",
"resourcesOutputPath": "images",
"index": "src/index.html",
"main": "src/main.ts",
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
"src/images"
],
"styles": [
"src/styles.scss"
{
"glob": "**/*",
"input": "src/images",
"output": "images",
"ignore": ["spinner.svg", "caret.svg"]
}
],
"styles": ["src/styles.scss"],
"scripts": [
"node_modules/marked/marked.min.js"
"node_modules/marked/marked.min.js",
{
"input": "src/assets/va-early.js",
"bundleName": "va-early",
"inject": true
}
],
"webWorkerTsConfig": "tsconfig.worker.json"
"webWorkerTsConfig": "tsconfig.worker.json",
"main": "src/main.ts"
},
"configurations": {
"production": {
@@ -103,9 +110,7 @@
}
},
"development": {
"vendorChunk": true,
"extractLicenses": false,
"buildOptimizer": false,
"sourceMap": true,
"optimization": false,
"namedChunks": true
@@ -134,20 +139,11 @@
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"polyfills": [
"src/polyfills.ts",
"zone.js",
"zone.js/testing"
],
"polyfills": ["src/polyfills.ts", "zone.js", "zone.js/testing"],
"tsConfig": "tsconfig.spec.json",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.scss"
],
"assets": ["src/favicon.ico", "src/assets"],
"styles": ["src/styles.scss"],
"scripts": [],
"karmaConfig": "karma.conf.js",
"webWorkerTsConfig": "tsconfig.worker.json"
@@ -156,10 +152,7 @@
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"lintFilePatterns": [
"src/**/*.ts",
"src/**/*.html"
]
"lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
}
}
}
+26 -18
View File
@@ -1,13 +1,13 @@
import { defineConfig } from "cypress";
import { defineConfig } from 'cypress'
export default defineConfig({
reporter: "mochawesome",
reporter: 'mochawesome',
reporterOptions: {
reportDir: "cypress/results",
reportDir: 'cypress/results',
overwrite: false,
html: true,
json: false,
json: false
},
viewportHeight: 900,
viewportWidth: 1600,
@@ -16,24 +16,32 @@ export default defineConfig({
defaultCommandTimeout: 30000,
env: {
hosturl: "http://localhost:4200",
appLocation: "",
site_id_SAS9: "70221618",
site_id_SASVIYA: "70253615",
site_id_SASJS: "123",
serverType: "SASJS",
libraryToOpenIncludes_SASVIYA: "viya",
libraryToOpenIncludes_SAS9: "dc",
libraryToOpenIncludes_SASJS: "dc",
hosturl: 'http://localhost:4200',
appLocation: '',
site_id_SAS9: '70221618',
site_id_SASVIYA: '70253615',
site_id_SASJS: '123',
serverType: 'SASJS',
libraryToOpenIncludes_SASVIYA: 'viya',
libraryToOpenIncludes_SAS9: 'dc',
libraryToOpenIncludes_SASJS: 'dc',
debug: false,
screenshotOnRunFailure: false,
longerCommandTimeout: 50000,
testLicenceUserLimits: false,
testLicenceUserLimits: false
},
e2e: {
video: true,
setupNodeEvents(on, config) {
// implement node event listeners here
},
},
});
// Pin the browser locale so locale-formatted cells (intl-date/time/datetime)
// render deterministically regardless of the runner's system locale.
on('before:browser:launch', (browser, launchOptions) => {
if (browser.family === 'chromium' && browser.name !== 'electron') {
launchOptions.args.push('--lang=en-GB')
}
return launchOptions
})
}
}
})
@@ -0,0 +1,132 @@
// Marks this file as an ES module (rather than a global script) so its
// top-level consts don't collide, under the TS type-checker, with the same
// names declared in other spec files — see viewer-labels.cy.ts for the same
// pattern.
export {}
const username = Cypress.env('username')
const password = Cypress.env('password')
const hostUrl = Cypress.env('hosturl')
const appLocation = Cypress.env('appLocation')
const longerCommandTimeout = Cypress.env('longerCommandTimeout')
const serverType = Cypress.env('serverType')
const libraryToOpenIncludes = Cypress.env(`libraryToOpenIncludes_${serverType}`)
// Regression coverage for issue #253's vertical-array COLTYPE fix — see
// getdata.sas now sends one COLTYPE JSON-object string per cols[] row (via
// a LEFT JOIN keyed off the real dataset's columns), instead of one
// comma-joined sasparams.COLTYPE string covering every column. Two things
// are specifically at risk from that change and aren't covered elsewhere:
// 1. `_____DELETE__THIS__RECORD_____` (the delete checkbox column) is a
// client-side-only concept — %mp_getcols never has a row for it, so
// it can no longer travel via cols[].COLTYPE at all. Its Yes/No
// dropdown rule is now hardcoded client-side (deleteRecordColumnRule.ts)
// — test 1 proves that hardcode actually renders end to end.
// 2. Ordinary columns (e.g. SOME_DROPDOWN) still get their rule via the
// new per-row cols[].COLTYPE path — test 2 proves the migration didn't
// silently drop real columns' rules either.
// Column positions below come from COLHEADERS in
// sas/mocks/sasjs/services/editors/getdata.js:
// "_____DELETE__THIS__RECORD_____,PRIMARY_KEY_FIELD,SOME_CHAR,SOME_DROPDOWN,..."
// — childNodes[0] on a body <tr> is the row-header <th>, so childNodes[N+1]
// is the Nth (0-indexed) column in that list.
context('coltype vertical-array regression tests: ', function () {
this.beforeAll(() => {
cy.visit(`${hostUrl}/SASLogon/logout`)
cy.loginAndUpdateValidKey(true)
})
this.beforeEach(() => {
cy.visit(hostUrl + appLocation)
visitPage('home')
})
it('1 | delete-record column renders its hardcoded Yes/No dropdown', () => {
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
clickOnEdit()
cy.get('.ht_master tbody tr', { timeout: longerCommandTimeout }).then(
(rows: any) => {
// childNodes[1] = _____DELETE__THIS__RECORD_____ (1st column)
cy.get(rows[1].childNodes[1])
.click({ force: true })
.then(($td) => {
cy.get('.htAutocompleteArrow', { withinSubject: $td })
.should('exist')
.click({ force: true })
cy.get('.autocompleteEditor .htCore tbody td').should(
($choices) => {
const texts = [...$choices].map((el) => el.innerText.trim())
expect(texts).to.deep.equal(['No', 'Yes'])
}
)
})
}
)
})
it('2 | an ordinary dropdown column (SOME_DROPDOWN) still gets its rule from cols[].COLTYPE', () => {
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
clickOnEdit()
cy.get('.ht_master tbody tr', { timeout: longerCommandTimeout }).then(
(rows: any) => {
// childNodes[4] = SOME_DROPDOWN (4th column, 0-indexed as 3). This
// only holds if DcValidator emits rules in VARNUM order — see the
// "orders rules by VARNUM" regression test in dc-validator.spec.ts.
cy.get(rows[1].childNodes[4])
.click({ force: true })
.then(($td) => {
cy.get('.htAutocompleteArrow', { withinSubject: $td }).should(
'exist'
)
})
}
)
})
})
const clickOnEdit = (callback?: any) => {
cy.get('.btnCtrl button.btn-primary', { timeout: longerCommandTimeout })
.click()
.then(() => {
if (callback) callback()
})
}
const openTableFromTree = (libNameIncludes: string, tablename: string) => {
cy.get('.app-loading', { timeout: longerCommandTimeout })
.should('not.exist')
.then(() => {
cy.get('.nav-tree clr-tree > clr-tree-node', {
timeout: longerCommandTimeout
}).then((treeNodes: any) => {
let viyaLib
for (let node of treeNodes) {
if (node.innerText.toLowerCase().includes(libNameIncludes)) {
viyaLib = node
break
}
}
cy.get(viyaLib).within(() => {
cy.get('.clr-tree-node-content-container > button').click()
cy.get('.clr-treenode-link').then((innerNodes: any) => {
for (let innerNode of innerNodes) {
if (innerNode.innerText.toLowerCase().includes(tablename)) {
innerNode.click()
break
}
}
})
})
})
})
}
const visitPage = (url: string) => {
cy.visit(`${hostUrl}${appLocation}/#/${url}`)
}
+95
View File
@@ -0,0 +1,95 @@
const username = Cypress.env('username')
const password = Cypress.env('password')
const hostUrl = Cypress.env('hosturl')
const appLocation = Cypress.env('appLocation')
const longerCommandTimeout = Cypress.env('longerCommandTimeout')
const serverType = Cypress.env('serverType')
const libraryToOpenIncludes = Cypress.env(`libraryToOpenIncludes_${serverType}`)
const fixturePath = 'csvs/'
context('csv file upload restriction (free tier): ', function () {
this.beforeEach(() => {
cy.visit(hostUrl + appLocation)
cy.get('body').then(($body) => {
const usernameInput = $body.find('input.username')[0]
if (usernameInput && !Cypress.dom.isHidden(usernameInput)) {
cy.get('input.username').type(username)
cy.get('input.password').type(password)
cy.get('.login-group button').click()
}
})
cy.get('.app-loading', { timeout: longerCommandTimeout }).should(
'not.exist'
)
// Skip licensing page if presented - continue with free tier
cy.url().then((url) => {
if (url.includes('licensing')) {
cy.get('button').contains('Continue with free tier').click()
}
})
visitPage('home')
})
it('1 | File upload is restricted on free tier', () => {
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
// Click upload button - should show feature locked modal
cy.get('.buttonBar button:last-child').should('exist').click()
cy.get('.modal-title').should('contain', 'Locked Feature (File Upload)')
})
})
const openTableFromTree = (libNameIncludes: string, tablename: string) => {
cy.get('.app-loading', { timeout: longerCommandTimeout })
.should('not.exist')
.then(() => {
cy.get('.nav-tree clr-tree > clr-tree-node', {
timeout: longerCommandTimeout
}).then((treeNodes: any) => {
let targetLib
for (let node of treeNodes) {
if (node.innerText.toLowerCase().includes(libNameIncludes)) {
targetLib = node
break
}
}
cy.get(targetLib).within(() => {
cy.get('.clr-tree-node-content-container > button').click()
cy.get('.clr-treenode-link').then((innerNodes: any) => {
for (let innerNode of innerNodes) {
if (innerNode.innerText.toLowerCase().includes(tablename)) {
innerNode.click()
break
}
}
})
})
})
})
}
const attachFile = (filename: string, callback?: any) => {
cy.get('.buttonBar button:last-child')
.should('exist')
.click()
.then(() => {
cy.get('input[type="file"]#file-upload')
.attachFile(`/${fixturePath}/${filename}`)
.then(() => {
if (callback) callback()
})
})
}
const visitPage = (url: string) => {
cy.visit(`${hostUrl}${appLocation}/#/${url}`)
}
+20 -21
View File
@@ -32,27 +32,26 @@ context('excel tests: ', function () {
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
attachExcelFile('regular.csv', () => {
cy.get('#approval-btn', { timeout: 60000 })
.should('be.visible')
// .then(() => {
// cy.get('#hotInstance', { timeout: 30000 })
// .find('div.ht_master.handsontable')
// .find('div.wtHolder')
// .find('div.wtHider')
// .find('div.wtSpreader')
// .find('table.htCore')
// .find('tbody')
// .then((data) => {
// let cell: any = data[0].children[0].children[1]
// expect(cell.innerText).to.equal('0')
// cell = data[0].children[0].children[2]
// expect(cell.innerText).to.equal('44')
// cell = data[0].children[0].children[3]
// expect(cell.innerText).to.equal('abc')
// cell = data[0].children[0].children[6]
// expect(cell.innerText).to.equal('Option abc')
// })
// })
cy.get('#approval-btn', { timeout: 60000 }).should('be.visible')
// .then(() => {
// cy.get('#hotInstance', { timeout: 30000 })
// .find('div.ht_master.handsontable')
// .find('div.wtHolder')
// .find('div.wtHider')
// .find('div.wtSpreader')
// .find('table.htCore')
// .find('tbody')
// .then((data) => {
// let cell: any = data[0].children[0].children[1]
// expect(cell.innerText).to.equal('0')
// cell = data[0].children[0].children[2]
// expect(cell.innerText).to.equal('44')
// cell = data[0].children[0].children[3]
// expect(cell.innerText).to.equal('abc')
// cell = data[0].children[0].children[6]
// expect(cell.innerText).to.equal('Option abc')
// })
// })
})
})
+1 -5
View File
@@ -217,11 +217,7 @@ const rejectExcel = (callback?: any) => {
.should('contain', 'Approve')
.then((allButtons: any) => {
for (let approvalButton of allButtons) {
if (
approvalButton.innerText
.toLowerCase()
.includes('approve')
) {
if (approvalButton.innerText.toLowerCase().includes('approve')) {
approvalButton.click()
break
}
+130
View File
@@ -0,0 +1,130 @@
const hostUrl = Cypress.env('hosturl')
const appLocation = Cypress.env('appLocation')
const longerCommandTimeout = Cypress.env('longerCommandTimeout')
const serverType = Cypress.env('serverType')
const libraryToOpenIncludes = Cypress.env(`libraryToOpenIncludes_${serverType}`)
// SAS Visual Analytics data-driven content embed mode (`?embed=va`):
// - chrome is hidden (same as embed=true),
// - the editor opens in edit mode immediately (not read-only),
// - the only action button is a single Submit below the grid (stubbed in v1),
// - the top button bar and Add Record are hidden.
//
// The detailed PK+label data-merge logic is covered by the VaMessagingService
// unit spec; here we only assert the deterministic UI layout and that pushing a
// VA postMessage does not break the page.
context('embed=va (VA data-driven content) tests: ', function () {
this.beforeAll(() => {
cy.visit(`${hostUrl}/SASLogon/logout`)
cy.loginAndUpdateValidKey()
})
this.beforeEach(() => {
cy.visit(hostUrl + appLocation)
visitPage('home')
})
it('1 | opens the editor in VA mode: chrome hidden, editable, single bottom Submit', () => {
openEditorInVaMode('mpe_x_test', () => {
// Chrome hidden (same as embed=true)
cy.get('header.app-header').should('not.exist')
// Grid present and in edit mode (read-only Filter/Edit/Upload bar absent)
cy.get('#hotTable', { timeout: longerCommandTimeout }).should('exist')
cy.get('.btnCtrl button').contains('Edit').should('not.exist')
cy.get('.btnCtrl button').contains('Filter').should('not.exist')
// Top edit-mode buttons hidden in VA mode
cy.get('.btnCtrl button.btn-outline-danger').should('not.exist') // Cancel
cy.get('.btnCtrl').contains('Add Row').should('not.exist')
// Bottom Add Record hidden
cy.contains('button', 'Add Record').should('not.exist')
// Exactly one Submit button, below the grid, disabled in v1
cy.get('button').filter(':contains("Submit")').should('have.length', 1)
cy.get('button').contains('Submit').should('be.disabled')
})
})
it('2 | accepts a VA postMessage and stays in VA mode', () => {
// A VA message drives column visibility + filtering; an empty/unmatched
// message reloads the editor (unfiltered) while preserving ?embed=va.
openEditorInVaMode('mpe_x_test', () => {
cy.window().then((win) => {
win.postMessage(
{
version: '1',
resultName: 'dd1',
rowCount: 0,
availableRowCount: 0,
data: [],
columns: [],
parameters: []
},
'*'
)
})
// Editor still loads in VA mode after the message (chrome hidden, grid up)
cy.get('#hotTable', { timeout: longerCommandTimeout }).should('exist')
cy.get('header.app-header').should('not.exist')
cy.get('button').contains('Submit').should('exist')
})
})
})
// Opens a table from the tree (which routes to #/editor/LIB.TABLE), then
// re-visits the same route with ?embed=va so the app parses VA embed mode.
const openEditorInVaMode = (tablename: string, callback?: any) => {
openTableFromTree(libraryToOpenIncludes, tablename)
cy.get('#hotTable', { timeout: longerCommandTimeout })
.should('exist')
.then(() => {
cy.url().then((url) => {
const separator = url.includes('?') ? '&' : '?'
cy.visit(`${url}${separator}embed=va`)
cy.get('.app-loading', { timeout: longerCommandTimeout }).should(
'not.exist'
)
if (callback) callback()
})
})
}
const openTableFromTree = (libNameIncludes: string, tablename: string) => {
cy.get('.app-loading', { timeout: longerCommandTimeout })
.should('not.exist')
.then(() => {
cy.get('.nav-tree clr-tree > clr-tree-node', {
timeout: longerCommandTimeout
}).then((treeNodes: any) => {
let viyaLib
for (let node of treeNodes) {
if (node.innerText.toLowerCase().includes(libNameIncludes)) {
viyaLib = node
break
}
}
cy.get(viyaLib).within(() => {
cy.get('.clr-tree-node-content-container > button').click()
cy.get('.clr-treenode-link').then((innerNodes: any) => {
for (let innerNode of innerNodes) {
if (innerNode.innerText.toLowerCase().includes(tablename)) {
innerNode.click()
break
}
}
})
})
})
})
}
const visitPage = (url: string) => {
cy.visit(`${hostUrl}${appLocation}/#/${url}`)
}
+187 -98
View File
@@ -34,93 +34,162 @@ context('excel multi load tests: ', function () {
it('1 | Uploads Excel file with multiple sheets, 3 sheets including data, 2 sheets matched with dataset', (done) => {
attachExcelFile('multi_load_test_2.xlsx', () => {
checkHotUserDatasetTable('hotTableUserDataset', [
[library, mpeXTestTable],
[library, mpeTablesTable]
], () => {
cy.get('#continue-btn').trigger('click').then(() => {
checkIfTreeHasTables([`${library}.${mpeXTestTable}`, `${library}.${mpeTablesTable}`], undefined, (includes: boolean) => {
if (includes) {
// MPE_TABLES sheet does not have data so 1 error image must be shown
hasErrorTables(1, (valid: boolean) => {
if (valid) done()
})
}
})
})
})
checkHotUserDatasetTable(
'hotTableUserDataset',
[
[library, mpeXTestTable],
[library, mpeTablesTable]
],
() => {
cy.get('#continue-btn')
.trigger('click')
.then(() => {
checkIfTreeHasTables(
[`${library}.${mpeXTestTable}`, `${library}.${mpeTablesTable}`],
undefined,
(includes: boolean) => {
if (includes) {
// MPE_TABLES sheet does not have data so 1 error image must be shown
hasErrorTables(1, (valid: boolean) => {
if (valid) done()
})
}
}
)
})
}
)
})
})
it('2 | Uploads Excel file with multiple sheets, 2 sheets matched with dataset, 1 matched sheet does not have data', (done) => {
attachExcelFile('multi_load_test_1.xlsx', () => {
checkHotUserDatasetTable('hotTableUserDataset', [
[library, mpeXTestTable],
[library, mpeTablesTable]
], () => {
cy.get('#continue-btn').trigger('click').then(() => {
checkIfTreeHasTables([`${library}.${mpeXTestTable}`, `${library}.${mpeTablesTable}`], `${library}.${mpeXTestTable}`, (includes: boolean) => {
if (includes) {
cy.get('#hotTable').should('be.visible').then(() => {
checkHotUserDatasetTable('hotTable', [
['No', '1', 'more dummy data'],
['No', '1', 'It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told:'],
['No', '1', 'if you can fill the unforgiving minute']
], () => {
submitTables()
checkHotUserDatasetTable(
'hotTableUserDataset',
[
[library, mpeXTestTable],
[library, mpeTablesTable]
],
() => {
cy.get('#continue-btn')
.trigger('click')
.then(() => {
checkIfTreeHasTables(
[`${library}.${mpeXTestTable}`, `${library}.${mpeTablesTable}`],
`${library}.${mpeXTestTable}`,
(includes: boolean) => {
if (includes) {
cy.get('#hotTable')
.should('be.visible')
.then(() => {
checkHotUserDatasetTable(
'hotTable',
[
['No', '1', 'more dummy data'],
[
'No',
'1',
'It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told:'
],
[
'No',
'1',
'if you can fill the unforgiving minute'
]
],
() => {
submitTables()
hasSuccessSubmits(2, (valid: boolean) => {
if (valid) done()
})
})
})
}
})
})
})
hasSuccessSubmits(2, (valid: boolean) => {
if (valid) done()
})
}
)
})
}
}
)
})
}
)
})
})
it('3 | Uploads Excel file with multiple sheets, 1 sheets has 2 tables', (done) => {
attachExcelFile('multi_load_test_1.xlsx', () => {
checkHotUserDatasetTable('hotTableUserDataset', [
[library, mpeXTestTable],
[library, mpeTablesTable]
], () => {
cy.get('#continue-btn').trigger('click').then(() => {
checkIfTreeHasTables([`${library}.${mpeXTestTable}`, `${library}.${mpeTablesTable}`], `${library}.${mpeXTestTable}`, (includes: boolean) => {
if (includes) {
cy.get('#hotTable').should('be.visible').then(() => {
checkHotUserDatasetTable('hotTable', [
['No', '1', 'more dummy data'],
['No', '1', 'It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told:'],
['No', '1', 'if you can fill the unforgiving minute']
], () => {
clickOnTreeNode('DC996664.MPE_TABLES', () => {
cy.wait(1000).then(() => {
cy.get('#hotTable').should('be.visible').then(() => {
checkHotUserDatasetTable('hotTable', [
['No', 'DC914286', 'MPE_COLUMN_LEVEL_SECURITY'],
['No', 'DC914286', 'MPE_XLMAP_INFO'],
['No', 'DC914286', 'MPE_XLMAP_RULES']
], () => {
submitTables()
checkHotUserDatasetTable(
'hotTableUserDataset',
[
[library, mpeXTestTable],
[library, mpeTablesTable]
],
() => {
cy.get('#continue-btn')
.trigger('click')
.then(() => {
checkIfTreeHasTables(
[`${library}.${mpeXTestTable}`, `${library}.${mpeTablesTable}`],
`${library}.${mpeXTestTable}`,
(includes: boolean) => {
if (includes) {
cy.get('#hotTable')
.should('be.visible')
.then(() => {
checkHotUserDatasetTable(
'hotTable',
[
['No', '1', 'more dummy data'],
[
'No',
'1',
'It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told:'
],
[
'No',
'1',
'if you can fill the unforgiving minute'
]
],
() => {
clickOnTreeNode('DC996664.MPE_TABLES', () => {
cy.wait(1000).then(() => {
cy.get('#hotTable')
.should('be.visible')
.then(() => {
checkHotUserDatasetTable(
'hotTable',
[
[
'No',
'DC914286',
'MPE_COLUMN_LEVEL_SECURITY'
],
['No', 'DC914286', 'MPE_XLMAP_INFO'],
['No', 'DC914286', 'MPE_XLMAP_RULES']
],
() => {
submitTables()
hasSuccessSubmits(2, (valid: boolean) => {
if (valid) done()
})
})
hasSuccessSubmits(
2,
(valid: boolean) => {
if (valid) done()
}
)
}
)
})
})
})
}
)
})
})
})
})
})
}
})
})
})
}
}
)
})
}
)
})
})
@@ -142,25 +211,31 @@ const attachExcelFile = (excelFilename: string, callback?: any) => {
})
}
const checkHotUserDatasetTable = (hotId: string, dataToContain: any[][], callback?: () => void) => {
const checkHotUserDatasetTable = (
hotId: string,
dataToContain: any[][],
callback?: () => void
) => {
cy.get(`#${hotId}`, { timeout: longerCommandTimeout })
.find('div.ht_master.handsontable')
.find('div.wtHolder')
.find('div.wtHider')
.find('div.wtSpreader')
.find('table.htCore')
.find('tbody')
.then((data) => {
cy.wait(2000).then(() => {
for (let rowI = 0; rowI < dataToContain.length; rowI++) {
for (let colI = 0; colI < dataToContain[rowI].length; colI++) {
expect(data[0].children[rowI].children[colI]).to.contain(dataToContain[rowI][colI])
.find('div.ht_master.handsontable')
.find('div.wtHolder')
.find('div.wtHider')
.find('div.wtSpreader')
.find('table.htCore')
.find('tbody')
.then((data) => {
cy.wait(2000).then(() => {
for (let rowI = 0; rowI < dataToContain.length; rowI++) {
for (let colI = 0; colI < dataToContain[rowI].length; colI++) {
expect(data[0].children[rowI].children[colI]).to.contain(
dataToContain[rowI][colI]
)
}
}
}
if (callback) callback()
if (callback) callback()
})
})
})
}
const clickOnTreeNode = (clickOnNode: string, callback?: () => void) => {
@@ -174,7 +249,11 @@ const clickOnTreeNode = (clickOnNode: string, callback?: () => void) => {
})
}
const checkIfTreeHasTables = (tables: string[], clickOnNode?: string, callback?: (includes: boolean) => void) => {
const checkIfTreeHasTables = (
tables: string[],
clickOnNode?: string,
callback?: (includes: boolean) => void
) => {
cy.get('.nav-tree clr-tree > clr-tree-node').then((treeNodes: any) => {
let datasets = tables
let nodesCorrect = true
@@ -207,16 +286,26 @@ const submitTables = () => {
cy.wait(1000)
}
const hasSuccessSubmits = (expectedNoOfSubmits: number, callback: (valid: boolean) => void) => {
cy.get('.nav-tree clr-tree > clr-tree-node cds-icon[status="success"]').should('be.visible').then(($nodes) => {
callback(expectedNoOfSubmits === $nodes.length)
})
const hasSuccessSubmits = (
expectedNoOfSubmits: number,
callback: (valid: boolean) => void
) => {
cy.get('.nav-tree clr-tree > clr-tree-node cds-icon[status="success"]')
.should('be.visible')
.then(($nodes) => {
callback(expectedNoOfSubmits === $nodes.length)
})
}
const hasErrorTables = (expectedNoOfErrors: number, callback: (valid: boolean) => void) => {
cy.get('.nav-tree clr-tree > clr-tree-node cds-icon[status="danger"]').should('be.visible').then(($nodes) => {
callback(expectedNoOfErrors === $nodes.length)
})
const hasErrorTables = (
expectedNoOfErrors: number,
callback: (valid: boolean) => void
) => {
cy.get('.nav-tree clr-tree > clr-tree-node cds-icon[status="danger"]')
.should('be.visible')
.then(($nodes) => {
callback(expectedNoOfErrors === $nodes.length)
})
}
const visitPage = (url: string) => {
+108 -23
View File
@@ -234,7 +234,7 @@ context('excel tests: ', function () {
cy.get('.btn-upload-preview', { timeout: 60000 })
.should('be.visible')
.then(() => {
cy.get('#hotInstance', { timeout: 30000 })
cy.get('#hotTable', { timeout: 30000 })
.find('div.ht_master.handsontable')
.find('div.wtHolder')
.find('div.wtHider')
@@ -283,7 +283,7 @@ context('excel tests: ', function () {
cy.get('.btn-upload-preview', { timeout: 60000 })
.should('be.visible')
.then(() => {
cy.get('#hotInstance', { timeout: 30000 })
cy.get('#hotTable', { timeout: 30000 })
.find('div.ht_master.handsontable')
.find('div.wtHolder')
.find('div.wtHider')
@@ -309,6 +309,96 @@ context('excel tests: ', function () {
})
})
it('22 | Uploads password protected Excel and unlocks with correct password', (done) => {
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
cy.get('.buttonBar button:last-child')
.should('exist')
.click()
.then(() => {
cy.get('input[type="file"]#file-upload')
.attachFile(`/${fixturePath}/regular_excel_password.xlsx`)
.then(() => {
// Wait for password modal to appear
cy.get('#filePasswordInput', { timeout: 10000 })
.should('be.visible')
.type('123123')
// Click Unlock button
cy.get('.btn.btn-success-outline').should('not.be.disabled').click()
// Click away the overlay
cy.get('.modal-footer .btn.btn-primary', { timeout: 5000 }).click()
// Verify file loads successfully
cy.get('.btn-upload-preview', { timeout: 60000 })
.should('be.visible')
.then(() => {
submitExcel()
rejectExcel(done)
})
})
})
})
it('23 | Uploads password protected Excel and handles wrong password', (done) => {
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
cy.get('.buttonBar button:last-child')
.should('exist')
.click()
.then(() => {
cy.get('input[type="file"]#file-upload')
.attachFile(`/${fixturePath}/regular_excel_password.xlsx`)
.then(() => {
// First attempt: Enter wrong password
cy.get('#filePasswordInput', { timeout: 10000 })
.should('be.visible')
.type('wrongpassword')
cy.get('.btn.btn-success-outline').should('not.be.disabled').click()
// Verify error message appears
cy.get('.modal-footer .color-red', { timeout: 10000 })
.should('be.visible')
.should('contain', "Sorry that didn't work, try again.")
// Modal should still be open for retry
cy.get('#filePasswordInput')
.should('be.visible')
.clear()
.type('123123')
// Second attempt: Enter correct password
cy.get('.btn.btn-success-outline').should('not.be.disabled').click()
// Click away the overlay
cy.get('.modal-footer .btn.btn-primary', { timeout: 5000 }).click()
// Verify file loads successfully
cy.get('.btn-upload-preview', { timeout: 60000 })
.should('be.visible')
.then(() => {
submitExcel()
rejectExcel(done)
})
})
})
})
it('24 | Uploads Excel with leading whitespace in header row (should still succeed)', (done) => {
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
// Every header except the first has a leading space, matching a
// real-world defect from copy-pasting between spreadsheets/systems.
// Header matching must tolerate
// this rather than reporting the columns missing and aborting.
attachExcelFile('leading_whitespace_header_excel.xlsx', () => {
submitExcel()
rejectExcel(done)
})
})
// Large files break Cypress
// it ('? | Uploads Excel with size of 5MB', (done) => {
@@ -399,11 +489,7 @@ const rejectExcel = (callback?: any) => {
.should('contain', 'Approve')
.then((allButtons: any) => {
for (let approvalButton of allButtons) {
if (
approvalButton.innerText
.toLowerCase()
.includes('approve')
) {
if (approvalButton.innerText.toLowerCase().includes('approve')) {
approvalButton.click()
break
}
@@ -432,11 +518,7 @@ const acceptExcel = (callback?: any) => {
.should('contain', 'Approve')
.then((allButtons: any) => {
for (let approvalButton of allButtons) {
if (
approvalButton.innerText
.toLowerCase()
.includes('approve')
) {
if (approvalButton.innerText.toLowerCase().includes('approve')) {
approvalButton.click()
break
}
@@ -455,7 +537,7 @@ const acceptExcel = (callback?: any) => {
}
const checkResultOfFormulaUpload = (callback?: any) => {
cy.get('#hotInstance', { timeout: longerCommandTimeout })
cy.get('#hotTable', { timeout: longerCommandTimeout })
.find('div.ht_master.handsontable')
.find('div.wtHolder')
.find('div.wtHider')
@@ -470,8 +552,10 @@ const checkResultOfFormulaUpload = (callback?: any) => {
}
const checkResultOfXLSUpload = (callback?: any) => {
cy.viewport(1280, 720)
cy.get('#hotInstance', { timeout: 30000 })
// Config-default width — wide enough that the date/datetime/time columns are
// not virtualized away (the old 1280 width hid the trailing time columns).
cy.viewport(1600, 900)
cy.get('#hotTable', { timeout: 30000 })
.find('div.ht_master.handsontable')
.find('div.wtHolder')
.find('div.wtHider')
@@ -488,19 +572,20 @@ const checkResultOfXLSUpload = (callback?: any) => {
expect(cell.innerText).to.equal('▼\nOption 1')
cell = data[0].children[0].children[5]
expect(cell.innerText).to.equal('42')
// Cells render the raw stored ISO string verbatim (locale-independent;
// see registerIntlCellTypes). date -> YYYY-MM-DD, datetime ->
// YYYY-MM-DD HH:mm:ss, time -> HH:mm:ss.
cell = data[0].children[0].children[6]
expect(cell.innerText).to.equal('▼\n1960-02-12')
// When CI detached browser screen is smaller, below cells are not visible so test fails
// Commenting it out now until we figure out workaround
// cell = data[0].children[0].children[7]
// expect(cell.innerText).to.equal('▼\n1960-01-01 00:00:42')
// cell = data[0].children[0].children[8]
// expect(cell.innerText).to.equal('00:00:42')
expect(cell.innerText).to.equal('1960-02-12')
cell = data[0].children[0].children[7]
expect(cell.innerText).to.equal('1960-01-01 00:00:42')
cell = data[0].children[0].children[8]
expect(cell.innerText).to.equal('00:00:42')
if (callback) callback()
})
cy.get('#hotInstance', { timeout: 30000 })
cy.get('#hotTable', { timeout: 30000 })
.find('div.ht_master.handsontable')
.find('div.wtHolder')
.scrollTo('right')
+8 -7
View File
@@ -16,7 +16,6 @@ context('filtering tests: ', function () {
this.beforeEach(() => {
cy.visit(hostUrl + appLocation, { timeout: longerCommandTimeout })
visitPage('home')
})
@@ -299,14 +298,16 @@ const setFilterWithValue = (
cy.get('.no-values')
.should('not.exist')
.then(() => {
cy.get('.in-values-modal clr-checkbox-wrapper input').then((inputs: any) => {
inputs[0].click()
cy.get('.in-values-modal .modal-footer button').click()
cy.get('.in-values-modal clr-checkbox-wrapper input').then(
(inputs: any) => {
inputs[0].click()
cy.get('.in-values-modal .modal-footer button').click()
cy.get('.modal-footer .btn-success-outline').click()
cy.get('.modal-footer .btn-success-outline').click()
if (callback) callback()
})
if (callback) callback()
}
)
})
})
+1 -8
View File
@@ -23,7 +23,6 @@ interface EditConfigTableCells {
context('licensing tests: ', function () {
this.beforeAll(() => {
cy.loginAndUpdateValidKey()
})
@@ -371,8 +370,6 @@ context('licensing tests: ', function () {
})
})
}
})
const logout = (callback?: any) => {
@@ -697,11 +694,7 @@ const approveTable = (callback?: any) => {
.should('contain', 'Approve')
.then((allButtons: any) => {
for (let approvalButton of allButtons) {
if (
approvalButton.innerText
.toLowerCase()
.includes('approve')
) {
if (approvalButton.innerText.toLowerCase().includes('approve')) {
approvalButton.click()
break
}
+1 -6
View File
@@ -18,7 +18,6 @@ context('liveness tests: ', function () {
this.beforeEach(() => {
cy.visit(hostUrl + appLocation)
visitPage('home')
})
@@ -125,11 +124,7 @@ const rejectExcel = (callback?: any) => {
.should('contain', 'Approve')
.then((allButtons: any) => {
for (let approvalButton of allButtons) {
if (
approvalButton.innerText
.toLowerCase()
.includes('approve')
) {
if (approvalButton.innerText.toLowerCase().includes('approve')) {
approvalButton.click()
break
}
+34 -29
View File
@@ -76,7 +76,8 @@ context('editor tests: ', function () {
cy.get('.viewbox-open').click()
openTableFromViewboxTree(
libraryToOpenIncludes,
viewboxes.map((viewbox) => viewbox.viewbox_table))
viewboxes.map((viewbox) => viewbox.viewbox_table)
)
cy.get('.open-viewbox').then((viewboxNodes: any) => {
let found = 0
@@ -91,32 +92,34 @@ context('editor tests: ', function () {
if (found < viewboxes.length) return
cy.get('.viewboxes-container .viewbox', { withinSubject: null }).then((viewboxNodes: any) => {
for (let viewboxNode of viewboxNodes) {
cy.get(viewboxNode).within(() => {
cy.get('.table-title').then((tableTitle) => {
const title = tableTitle[0].innerText
const viewbox = viewboxes.find((vb) =>
title.toLowerCase().includes(vb.viewbox_table)
)
if (viewbox) {
cy.get('.ht_master.handsontable .htCore thead tr').then(
(viewboxColNodes: any) => {
let allColsHtml = viewboxColNodes[0].innerHTML
for (let col of viewbox?.columns) {
if (!allColsHtml.includes(col)) return
}
done()
}
cy.get('.viewboxes-container .viewbox', { withinSubject: null }).then(
(viewboxNodes: any) => {
for (let viewboxNode of viewboxNodes) {
cy.get(viewboxNode).within(() => {
cy.get('.table-title').then((tableTitle) => {
const title = tableTitle[0].innerText
const viewbox = viewboxes.find((vb) =>
title.toLowerCase().includes(vb.viewbox_table)
)
}
if (viewbox) {
cy.get('.ht_master.handsontable .htCore thead tr').then(
(viewboxColNodes: any) => {
let allColsHtml = viewboxColNodes[0].innerHTML
for (let col of viewbox?.columns) {
if (!allColsHtml.includes(col)) return
}
done()
}
)
}
})
})
})
}
}
})
)
})
})
@@ -395,11 +398,13 @@ context('editor tests: ', function () {
})
const removeAllColumns = () => {
cy.get('.configuration-wrapper clr-icon[shape="trash"]').then(removeNodes => {
for (let removeNode of removeNodes) {
removeNode.click()
cy.get('.configuration-wrapper clr-icon[shape="trash"]').then(
(removeNodes) => {
for (let removeNode of removeNodes) {
removeNode.click()
}
}
})
)
}
const checkColumns = (columns: string[], callback: () => void) => {
@@ -412,7 +417,7 @@ const checkColumns = (columns: string[], callback: () => void) => {
console.log('viewboxColNode', viewboxColNodes)
console.log('columns', columns)
for (let i = 0; i < viewboxColNodes.length; i++) {
const col = columns[i]|| ''
const col = columns[i] || ''
const colNode = viewboxColNodes[i]
if (
+243
View File
@@ -0,0 +1,243 @@
// Marks this file as an ES module (rather than a global script) so its
// top-level consts don't collide, under the TS type-checker, with the same
// names declared in other spec files — see e.g. download.cy.ts, which gets
// this for free via a real import.
export {}
const username = Cypress.env('username')
const password = Cypress.env('password')
const hostUrl = Cypress.env('hosturl')
const appLocation = Cypress.env('appLocation')
const longerCommandTimeout = Cypress.env('longerCommandTimeout')
const serverType = Cypress.env('serverType')
const libraryToOpenIncludes = Cypress.env(`libraryToOpenIncludes_${serverType}`)
// Viewer column-label display toggle (`?labels=true`): clients surfacing DC in
// SAS Visual Analytics want column LABELs shown instead of NAMEs. Default
// behavior (no param) must stay unchanged; the right-click context menu's
// "Show labels"/"Show names" item is the in-app affordance, and it drives the
// same URL param so state is shareable/refreshable. See mock data in
// sas/mocks/sasjs/services/public/viewdata.js: MPE_X_TEST's SOME_CHAR/SOME_DATE
// have LABELs that differ from NAME, and SOME_NUM has a blank LABEL (fallback
// to NAME) — this spec exercises exactly that fixture.
context('viewer column labels toggle tests: ', function () {
this.beforeAll(() => {
cy.visit(`${hostUrl}/SASLogon/logout`)
// forceLicenceKey=true: without it, loginAndUpdateValidKey() only
// generates key data and logs back out if no valid license already
// exists — it doesn't actually apply one (see commands.ts). Several
// specs call it with no argument and rely on an *earlier* spec in the
// same CI run having already forced a valid license onto the shared
// server first (csv.cy.ts, excel.cy.ts, etc.). This spec shouldn't
// depend on run order, so force it explicitly.
cy.loginAndUpdateValidKey(true)
})
this.beforeEach(() => {
// No re-login here: beforeAll's loginAndUpdateValidKey() already
// authenticated the session, and it persists across tests in this file
// (see filtering.cy.ts/csv.cy.ts for the same pattern). Re-typing into
// the login form would fail — it's hidden once already logged in.
cy.visit(hostUrl + appLocation)
// Visit 'home' first, matching filtering.cy.ts/csv.cy.ts/download.cy.ts —
// going straight to 'view/data' skips whatever startup-data fetch 'home'
// triggers, which passed locally (fast round-trips) but left the
// nav-tree empty in CI's slower environment.
visitPage('home')
visitPage('view/data')
})
it('1 | default (no param): headers show NAMEs', () => {
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
cy.get('#hotTable .ht_clone_top .htCore thead tr', {
timeout: longerCommandTimeout
}).should(($headerRow) => {
expect($headerRow[0].innerHTML).to.include('SOME_CHAR')
expect($headerRow[0].innerHTML).to.not.include('Some Character Column')
})
})
it('2 | ?labels=true: headers show LABELs, blank LABEL falls back to NAME', () => {
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
appendUrlParam('labels=true')
cy.get('#hotTable .ht_clone_top .htCore thead tr', {
timeout: longerCommandTimeout
}).should(($headerRow) => {
const html = $headerRow[0].innerHTML
expect(html).to.include('Some Character Column')
expect(html).to.include('Some Date')
// SOME_NUM has a blank LABEL in the mock -> falls back to NAME
expect(html).to.include('SOME_NUM')
})
})
it('3 | context menu "Show labels"/"Show names" toggles headers and the URL', () => {
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
toggleColumnLabelsFromContextMenu('Show labels')
cy.url().should('include', 'labels=true')
cy.get('#hotTable .ht_clone_top .htCore thead tr', {
timeout: longerCommandTimeout
}).should(($headerRow) => {
expect($headerRow[0].innerHTML).to.include('Some Character Column')
})
toggleColumnLabelsFromContextMenu('Show names')
cy.url().should('not.include', 'labels=true')
cy.get('#hotTable .ht_clone_top .htCore thead tr', {
timeout: longerCommandTimeout
}).should(($headerRow) => {
expect($headerRow[0].innerHTML).to.include('SOME_CHAR')
})
})
it('4 | ?embed=va&labels=true: labels shown with embed chrome hidden', () => {
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
appendUrlParam('embed=va&labels=true')
cy.get('header.app-header').should('not.exist')
cy.get('#hotTable .ht_clone_top .htCore thead tr', {
timeout: longerCommandTimeout
}).should(($headerRow) => {
expect($headerRow[0].innerHTML).to.include('Some Character Column')
})
})
it('5 | info dropdown: first line is NAME (in both toggle states)', () => {
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
// No assertion on the item's label text here: `info` has a custom
// `renderer`, so Handsontable never renders its `name` ("test info") as
// text at all — the renderer's own output (checked below) is the only
// real content.
openColumnDropdown('SOME_CHAR')
cy.get('.htDropdownMenu').should(($menu) => {
expect($menu.text()).to.match(/NAME: SOME_CHAR/)
})
cy.get('body').click(0, 0) // close menu
appendUrlParam('labels=true')
openColumnDropdown('Some Character Column')
cy.get('.htDropdownMenu').should(($menu) => {
// NAME is always shown first, even while headers display as LABEL
expect($menu.text()).to.match(/NAME: SOME_CHAR/)
})
})
})
const visitPage = (url: string) => {
cy.visit(`${hostUrl}${appLocation}/#/${url}`)
}
// Re-visits the current route with extra hash-query params appended, then
// forces a real reload. Under hash routing (`useHash: true`), changing only
// the URL's hash fragment is a same-document navigation — Angular Router
// still reacts to it (which is enough for `useLabels`, read reactively from
// `route.queryParams`), but `embed` is parsed once from `window.location.hash`
// at app bootstrap (app.component.ts), so `?embed=va` only takes effect after
// an actual reload.
const appendUrlParam = (param: string) => {
cy.url().then((url) => {
const separator = url.includes('?') ? '&' : '?'
cy.visit(`${url}${separator}${param}`)
cy.reload()
cy.get('.app-loading', { timeout: longerCommandTimeout }).should(
'not.exist'
)
})
}
const openColumnDropdown = (headerText: string) => {
// Handsontable renders the sticky/frozen header via a separate clone pane
// (.ht_clone_top) for scroll behavior; the header row inside the main
// .ht_master pane is kept visibility:hidden (replaced visually by the
// clone). .ht_clone_top is the real, interactive one — confirmed via
// `document.querySelector('#hotTable .ht_clone_top thead').innerText` in
// a live browser session.
cy.get('#hotTable .ht_clone_top .htCore thead button.changeType', {
timeout: longerCommandTimeout
})
.parents('th')
.filter((_, th) => Cypress.$(th).text().includes(headerText))
.last()
.as('targetHeader')
// Click the header text first to select the column — the `info` item's
// renderer reads hot.getSelected() to decide which column to describe,
// so without an active selection it always shows "No info found".
cy.get('@targetHeader').click()
cy.get('@targetHeader').find('button.changeType').click({ force: true })
}
const toggleColumnLabelsFromContextMenu = (
menuItemText: 'Show labels' | 'Show names'
) => {
cy.get('#hotTable .ht_master.handsontable .htCore tbody tr td', {
timeout: longerCommandTimeout
})
.first()
// force: true — the first body row can sit under an overlay clone's
// header/sort-icon layer, which fails Cypress's actionability check
// even though the cell is the real rightclick target underneath.
.rightclick({ force: true })
cy.get('.htContextMenu').contains(menuItemText).click()
cy.get('.app-loading', { timeout: longerCommandTimeout }).should('not.exist')
}
const openTableFromTree = (libNameIncludes: string, tablename: string) => {
cy.get('.app-loading', { timeout: longerCommandTimeout })
.should('not.exist')
.then(() => {
cy.get('.nav-tree clr-tree > clr-tree-node', {
timeout: longerCommandTimeout
}).then((treeNodes: any) => {
let viyaLib
for (let node of treeNodes) {
if (node.innerText.toLowerCase().includes(libNameIncludes)) {
viyaLib = node
break
}
}
cy.get(viyaLib).within(() => {
// Small settle wait: right after a fresh visit/reload, the tree
// component can still be re-rendering, causing this node to be
// found then swapped out mid-click ("disappeared from the page").
cy.wait(300)
cy.get(
'.clr-tree-node-content-container .clr-treenode-content p'
).click()
cy.get('.clr-treenode-link').then((innerNodes: any) => {
for (let innerNode of innerNodes) {
if (innerNode.innerText.toLowerCase().includes(tablename)) {
innerNode.click()
break
}
}
})
})
})
})
// Selecting the table triggers async SPA routing + a viewdata fetch;
// wait for the grid to actually render before any subsequent action
// (reading the URL, right-clicking, opening a header dropdown) — without
// this, callers can act while still on the intermediate library-only
// route or a not-yet-rendered grid. Waiting specifically for a header's
// dropdown button (not just the <tr> shell) confirms Handsontable has
// finished populating header cell contents, not just their DOM rows.
cy.get('#hotTable .ht_clone_top .htCore thead button.changeType', {
timeout: longerCommandTimeout
}).should('exist')
}
+5 -1
View File
@@ -4,7 +4,11 @@ PRIMARY_KEY_FIELD,SOME_CHAR,SOME_DROPDOWN,SOME_NUM,SOME_DATE,SOME_DATETIME,SOME_
2,even more dummy data,Option 3,42,12FEB1960,01JAN1960:00:00:42,0:02:22,3,44
3,"It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told:",Option 2,1613.001,27FEB1961,01JAN1960:00:07:03,0:00:44,3,44
4,if you can fill the unforgiving minute,Option 1,1613.0011235,02AUG1971,29MAY1973:06:12:03,0:06:52,3,44
1010,10 bottles of beer on the wall,Option 1,0.9153696885,04MAR1962,01JAN1960:12:47:55,0:01:40,92,76
1010,"10 bottles of beer
on the wall",Option 1,0.9153696885,04MAR1962,01JAN1960:12:47:55,0:01:40,92,76
1011,11 bottles of beer on the wall,Option 1,0.3531217558,29MAR1960,01JAN1960:03:33:24,0:01:03,80,29
1012,12 bottles of beer on the wall,Option 1,0.6743748717,02AUG1962,01JAN1960:07:25:59,0:00:10,16,98
1013,13 bottles of beer on the wall,Option 1,0.1305445992,11SEP1960,01JAN1960:13:51:32,0:00:35,73,15
1 PRIMARY_KEY_FIELD SOME_CHAR SOME_DROPDOWN SOME_NUM SOME_DATE SOME_DATETIME SOME_TIME SOME_SHORTNUM SOME_BESTNUM
4 2 even more dummy data Option 3 42 12FEB1960 01JAN1960:00:00:42 0:02:22 3 44
5 3 It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: Option 2 1613.001 27FEB1961 01JAN1960:00:07:03 0:00:44 3 44
6 4 if you can fill the unforgiving minute Option 1 1613.0011235 02AUG1971 29MAY1973:06:12:03 0:06:52 3 44
7 1010 10 bottles of beer on the wall 10 bottles of beer on the wall Option 1 0.9153696885 04MAR1962 01JAN1960:12:47:55 0:01:40 92 76
8 1011 11 bottles of beer on the wall Option 1 0.3531217558 29MAR1960 01JAN1960:03:33:24 0:01:03 80 29
9 1012 12 bottles of beer on the wall Option 1 0.6743748717 02AUG1962 01JAN1960:07:25:59 0:00:10 16 98
10 1013 13 bottles of beer on the wall Option 1 0.1305445992 11SEP1960 01JAN1960:13:51:32 0:00:35 73 15
11 1014 14 bottles of beer on the wall Option 1 0.7409067949 26JUL1960 01JAN1960:05:18:10 0:00:41 30 89
12 1011 1015 11 bottles of beer on the wall 15 bottles of beer on the wall Option 1 0.3531217558 0.0869016028 29MAR1960 28FEB1961 01JAN1960:03:33:24 01JAN1960:13:23:45 0:01:03 0:00:44 80 29 3
13 1012 1016 12 bottles of beer on the wall 16 bottles of beer on the wall Option 1 0.6743748717 0.0462121419 02AUG1962 09AUG1962 01JAN1960:07:25:59 01JAN1960:07:42:38 0:00:10 0:01:17 16 62 98 2
14 1013 1017 13 bottles of beer on the wall 17 bottles of beer on the wall Option 1 0.1305445992 0.7501918947 11SEP1960 14MAY1962 01JAN1960:13:51:32 01JAN1960:04:40:20 0:00:35 0:00:15 73 53 15 65
@@ -1,255 +0,0 @@
const username = Cypress.env('username')
const password = Cypress.env('password')
const hostUrl = Cypress.env('hosturl')
const appLocation = Cypress.env('appLocation')
const longerCommandTimeout = Cypress.env('longerCommandTimeout')
const serverType = Cypress.env('serverType')
const libraryToOpenIncludes = Cypress.env(`libraryToOpenIncludes_${serverType}`)
const fixturePath = 'excels_general/'
const downloadsFolder = Cypress.config('downloadsFolder')
import { deleteDownloadsFolder } from '../util/deleteDownloadFolder'
context('download files test: ', function () {
this.beforeAll(() => {
cy.visit(`${hostUrl}/SASLogon/logout`)
cy.loginAndUpdateValidKey()
})
this.beforeEach(() => {
cy.visit(hostUrl + appLocation)
cy.get('input.username').type(username)
cy.get('input.password').type(password)
cy.get('.login-group button').click()
visitPage('home')
})
this.afterEach(() => {
deleteDownloadsFolder()
})
it('1 | downloads audit file', (done) => {
visitPage('approve/toapprove')
cy.get('.app-loading', { timeout: longerCommandTimeout })
.should('not.exist')
.then(() => {
cy.get('.btn.btn-success')
.should('be.visible')
.then((buttons) => {
buttons[0].click()
const id = buttons[0].id
checkForFileDownloaded(id, 'zip', () => done())
})
})
})
it('2 | downloads viewer csv', (done) => {
visitPage('view/data')
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
openDownloadModal(() => {
cy.get('select')
.select('CSV')
.then(() => {
cy.get('.btn.btn-sm.btn-success-outline').then((button) => {
button.trigger('click')
const id = button[0].id
checkForFileDownloaded(id, 'csv', () => done())
})
})
})
})
it('3 | downloads viewer excel', (done) => {
visitPage('view/data')
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
openDownloadModal(() => {
cy.get('select')
.select('Excel')
.then(() => {
cy.get('.btn.btn-sm.btn-success-outline').then((button) => {
button.trigger('click')
const id = button[0].id
checkForFileDownloaded(id, 'xlsx', () => done())
})
})
})
})
it('4 | downloads viewer SAS Datalines', (done) => {
visitPage('view/data')
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
openDownloadModal(() => {
cy.get('select')
.select('SAS Datalines')
.then(() => {
cy.get('.btn.btn-sm.btn-success-outline').then((button) => {
button.trigger('click')
const id = button[0].id
checkForFileDownloaded(id, 'sas', () => done())
})
})
})
})
it('5 | downloads viewer SAS DDL', (done) => {
visitPage('view/data')
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
openDownloadModal(() => {
cy.get('select')
.select('SAS DDL')
.then(() => {
cy.get('.btn.btn-sm.btn-success-outline').then((button) => {
button.trigger('click')
const id = button[0].id
checkForFileDownloaded(id, 'ddl', () => done(), '_')
})
})
})
})
it('6 | downloads viewer TSQL DDL', (done) => {
visitPage('view/data')
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
openDownloadModal(() => {
cy.get('select')
.select('TSQL DDL')
.then(() => {
cy.get('.btn.btn-sm.btn-success-outline').then((button) => {
button.trigger('click')
const id = button[0].id
checkForFileDownloaded(id, 'ddl', () => done(), '_')
})
})
})
})
it('7 | downloads viewer PGSQL DDL', (done) => {
visitPage('view/data')
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
openDownloadModal(() => {
cy.get('select')
.select('PGSQL DDL')
.then(() => {
cy.get('.btn.btn-sm.btn-success-outline').then((button) => {
button.trigger('click')
const id = button[0].id
checkForFileDownloaded(id, 'ddl', () => done(), '_')
})
})
})
})
this.afterEach(() => {
cy.visit(`${hostUrl}/SASLogon/logout`)
})
this.afterAll(() => {
cy.visit(`https://sas.4gl.io/mihmed/cypress_finish`)
})
})
const visitPage = (url: string) => {
cy.visit(`${hostUrl}${appLocation}/#/${url}`)
}
const checkForFileDownloaded = (
id: string,
extension: string,
callback?: any,
libDivider: string = '.'
) => {
cy.on('url:changed', (newUrl) => {
console.log('newUrl', newUrl)
})
id = id.replace('.', libDivider)
const filename = downloadsFolder + '/' + id + '.' + extension
// browser might take a while to download the file,
// so use "cy.readFile" to retry until the file exists
// and has length - and we assume that it has finished downloading then
cy.readFile(filename, { timeout: longerCommandTimeout })
.should('have.length.gt', 10)
.then((file) => {
if (callback) callback()
})
}
const openDownloadModal = (callback?: any) => {
cy.get('.btn.btn-sm.btn-outline.filterSide.dropdown-toggle')
.click()
.then(() => {
cy.get('clr-dropdown-menu button').then((buttons) => {
for (let button of buttons) {
if (button.innerText.toLowerCase().includes('download')) {
button.click()
if (callback) callback()
}
}
})
})
}
const openTableFromTree = (libNameIncludes: string, tablename: string) => {
cy.get('.app-loading', { timeout: longerCommandTimeout })
.should('not.exist')
.then(() => {
cy.get('.nav-tree clr-tree > clr-tree-node', {
timeout: longerCommandTimeout
}).then((treeNodes: any) => {
let viyaLib
for (let node of treeNodes) {
if (node.innerText.toLowerCase().includes(libNameIncludes)) {
viyaLib = node
break
}
}
console.log('viyaLib', viyaLib)
cy.get(viyaLib).within(() => {
cy.get(
'.clr-tree-node-content-container .clr-treenode-content p'
).click()
cy.get('.clr-treenode-link').then((innerNodes: any) => {
for (let innerNode of innerNodes) {
if (innerNode.innerText.toLowerCase().includes(tablename)) {
innerNode.click()
break
}
}
})
})
})
})
}
-246
View File
@@ -1,246 +0,0 @@
const username = Cypress.env('username')
const password = Cypress.env('password')
const hostUrl = Cypress.env('hosturl')
const appLocation = Cypress.env('appLocation')
const longerCommandTimeout = Cypress.env('longerCommandTimeout')
const serverType = Cypress.env('serverType')
const libraryToOpenIncludes = Cypress.env(`libraryToOpenIncludes_${serverType}`)
const fixturePath = 'excels_general/'
context('editor tests: ', function () {
this.beforeAll(() => {
cy.visit(`${hostUrl}/SASLogon/logout`)
cy.loginAndUpdateValidKey()
})
this.beforeEach(() => {
cy.visit(hostUrl + appLocation)
visitPage('home')
})
it('1 | Submits duplicate primary keys', (done) => {
openTableFromTree(libraryToOpenIncludes, 'mpe_datadictionary')
attachExcelFile('MPE_DATADICTIONARY_duplicate_keys.xlsx', () => {
clickOnUploadPreview(() => {
confirmEditPreviewFile(() => {
submitTable(() => {
cy.get('.modal-body').then((modalBody: any) => {
if (modalBody[0].innerText.includes(`Duplicates found:`)) {
done()
}
})
})
})
})
})
})
it('2 | Submits null cells which must not be null', (done) => {
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
clickOnEdit(() => {
cy.get('.btn.btn-sm.btn-icon.btn-outline-danger', {
timeout: longerCommandTimeout
}).then(() => {
cy.get('.ht_master tbody tr').then((rows: any) => {
cy.get(rows[1].childNodes[2])
.dblclick({ force: true })
.then(() => {
cy.focused()
.clear()
.type('{enter}')
.then(() => {
submitTable(() => {
cy.get('.modal-body').then((modalBody: any) => {
if (
modalBody[0].innerHTML
.toLowerCase()
.includes(`invalid values are present`)
) {
done()
}
})
})
})
})
})
})
})
})
it('3 | Gets basic dynamic cell validation', () => {
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
clickOnEdit(() => {
cy.get('.btn.btn-sm.btn-icon.btn-outline-danger', {
timeout: longerCommandTimeout
}).then(() => {
cy.get('.ht_master tbody tr').then((rows: any) => {
cy.get(rows[1].childNodes[5])
.click({ force: true })
.then(($td) => {
cy.get('.htAutocompleteArrow', { withinSubject: $td }).should(
'exist'
)
})
})
})
})
})
it('4 | Gets advanced dynamic cell validation', () => {
openTableFromTree(libraryToOpenIncludes, 'mpe_tables')
clickOnEdit(() => {
cy.get('.btn.btn-sm.btn-icon.btn-outline-danger', {
timeout: longerCommandTimeout
}).then(() => {
cy.get('.ht_master tbody tr').then((rows: any) => {
cy.get(rows[1].childNodes[3])
.click({ force: true })
.then(($td) => {
cy.get('.htAutocompleteArrow', { withinSubject: $td }).should(
'exist'
)
cy.get('.htAutocompleteArrow', {
withinSubject: rows[1].childNodes[7]
}).should('exist')
cy.get('.htAutocompleteArrow', {
withinSubject: rows[1].childNodes[8]
}).should('exist')
})
})
})
})
})
})
const clickOnEdit = (callback?: any) => {
cy.get('.btnCtrl button.btn-primary', { timeout: longerCommandTimeout })
.click()
.then(() => {
if (callback) callback()
})
}
const openTableFromTree = (libNameIncludes: string, tablename: string) => {
cy.get('.app-loading', { timeout: longerCommandTimeout })
.should('not.exist')
.then(() => {
cy.get('.nav-tree clr-tree > clr-tree-node', {
timeout: longerCommandTimeout
}).then((treeNodes: any) => {
let viyaLib
for (let node of treeNodes) {
if (node.innerText.toLowerCase().includes(libNameIncludes)) {
viyaLib = node
break
}
}
cy.get(viyaLib).within(() => {
cy.get('.clr-tree-node-content-container > button').click()
cy.get('.clr-treenode-link').then((innerNodes: any) => {
for (let innerNode of innerNodes) {
if (innerNode.innerText.toLowerCase().includes(tablename)) {
innerNode.click()
break
}
}
})
})
})
})
}
const attachExcelFile = (excelFilename: string, callback?: any) => {
cy.get('.buttonBar button:last-child')
.click()
.then(() => {
cy.get('input[type="file"]#file-upload')
.attachFile(`/${fixturePath}/${excelFilename}`)
.then(() => {
cy.get('.modal-footer .btn.btn-primary').then((modalBtn) => {
modalBtn.click()
if (callback) callback()
})
})
})
}
const clickOnUploadPreview = (callback?: any) => {
cy.get('.buttonBar button.btn-primary.btn-upload-preview')
.click()
.then(() => {
if (callback) callback()
})
}
const confirmEditPreviewFile = (callback?: any) => {
cy.get('.modal-footer button.btn-success-outline')
.click()
.then(() => {
if (callback) callback()
})
}
const submitTable = (callback?: any) => {
cy.get('.btnCtrl button.btn-primary')
.click()
.then(() => {
if (callback) callback()
})
}
const submitTableMessage = (callback?: any) => {
cy.get('.modal-footer .btn.btn-sm.btn-success-outline')
.click()
.then(() => {
if (callback) callback()
})
}
const submitExcel = (callback?: any) => {
cy.get('.buttonBar button.preview-submit')
.click()
.then(() => {
if (callback) callback()
})
}
const re