diff --git a/.eslintrc b/.eslintrc index af116ee51..67e8152ff 100644 --- a/.eslintrc +++ b/.eslintrc @@ -12,7 +12,6 @@ "globals": { "Slick": true, "Slicker": true, - "jQuery": true, "$": true, "_": true }, diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 4993ff222..e2f335fde 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -14,15 +14,15 @@ // allow Node14/npm8 until its EOL June 2023 { packageNames: ['node'], - allowedVersions: '14.17.0', + allowedVersions: '16.15.0', }, { packageNames: ['npm'], - allowedVersions: '6.14.13', + allowedVersions: '8.5.0', }, { packageNames: ['rxjs'], - allowedVersions: '7.5.0', + allowedVersions: '7.8.1', } ], schedule: ['on friday and tuesday'], diff --git a/.vscode/launch.json b/.vscode/launch.json index 8cfde2b20..e4b2cd394 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,11 +8,11 @@ "type": "msedge", "request": "launch", "name": "Vite - Edge Debugger", - "url": "http://localhost:8888", - "resolveSourceMapLocations": [ - "${workspaceFolder}/", - "!/node_modules/**" - ] + "url": "http://localhost:8888" + // "resolveSourceMapLocations": [ + // "${workspaceFolder}/", + // "!/node_modules/**" + // ] }, { "type": "node", diff --git a/CHANGELOG.md b/CHANGELOG.md index f6f4344f3..53fe4c763 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [3.0.0-beta.0](https://github.com/ghiscoding/slickgrid-universal/compare/v2.6.4...v3.0.0-beta.0) (2023-05-20) + +### ⚠ BREAKING CHANGES + +* drop jQuery requirement (#962) +* **common:** migrate to multiple-select-vanilla (#919) + +### Features + +* **common:** migrate to multiple-select-vanilla ([#919](https://github.com/ghiscoding/slickgrid-universal/issues/919)) ([bc74207](https://github.com/ghiscoding/slickgrid-universal/commit/bc74207e9b2ec46209e87b126e1fcff596c162af)) - by @ghiscoding +* drop jQuery requirement ([#962](https://github.com/ghiscoding/slickgrid-universal/issues/962)) ([3da21da](https://github.com/ghiscoding/slickgrid-universal/commit/3da21daacc391a0fb309fcddd78442642c5269f6)) - by @ghiscoding + ## [2.6.4](https://github.com/ghiscoding/slickgrid-universal/compare/v2.6.3...v2.6.4) (2023-05-20) ### Bug Fixes diff --git a/examples/vite-demo-vanilla-bundle/CHANGELOG.md b/examples/vite-demo-vanilla-bundle/CHANGELOG.md index 989957557..3111cfedc 100644 --- a/examples/vite-demo-vanilla-bundle/CHANGELOG.md +++ b/examples/vite-demo-vanilla-bundle/CHANGELOG.md @@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [3.0.0-beta.0](https://github.com/ghiscoding/slickgrid-universal/compare/v2.6.4...v3.0.0-beta.0) (2023-05-20) + +### ⚠ BREAKING CHANGES + +* drop jQuery requirement (#962) +* **common:** migrate to multiple-select-vanilla (#919) + +### Features + +* **common:** migrate to multiple-select-vanilla ([#919](https://github.com/ghiscoding/slickgrid-universal/issues/919)) ([bc74207](https://github.com/ghiscoding/slickgrid-universal/commit/bc74207e9b2ec46209e87b126e1fcff596c162af)) - by @ghiscoding +* drop jQuery requirement ([#962](https://github.com/ghiscoding/slickgrid-universal/issues/962)) ([3da21da](https://github.com/ghiscoding/slickgrid-universal/commit/3da21daacc391a0fb309fcddd78442642c5269f6)) - by @ghiscoding + ## [2.6.4](https://github.com/ghiscoding/slickgrid-universal/compare/v2.6.3...v2.6.4) (2023-05-20) ### Bug Fixes diff --git a/examples/vite-demo-vanilla-bundle/package.json b/examples/vite-demo-vanilla-bundle/package.json index 2eadf2e1e..43d8b0e87 100644 --- a/examples/vite-demo-vanilla-bundle/package.json +++ b/examples/vite-demo-vanilla-bundle/package.json @@ -1,7 +1,7 @@ { "name": "slickgrid-universal-vite-demo", "private": true, - "version": "2.6.4", + "version": "3.0.0-beta.0", "scripts": { "dev": "vite --mode development", "build": "tsc && vite build", @@ -25,17 +25,16 @@ "@slickgrid-universal/vanilla-force-bundle": "workspace:~", "bulma": "^0.9.4", "dompurify": "^3.0.3", + "fetch-jsonp": "^1.2.3", "flatpickr": "^4.6.13", - "jquery": "^3.7.0", "moment-mini": "^2.29.4", - "multiple-select-modified": "^1.3.17", + "multiple-select-vanilla": "^0.3.1", "rxjs": "^7.8.1", "whatwg-fetch": "^3.6.2" }, "devDependencies": { "@rollup/plugin-dynamic-import-vars": "^2.0.3", "@types/fnando__sparkline": "^0.3.4", - "@types/jquery": "^3.5.16", "@types/moment": "^2.13.0", "@types/node": "^18.16.13", "@types/whatwg-fetch": "^0.0.33", diff --git a/examples/vite-demo-vanilla-bundle/public/assets/multiple-select.png b/examples/vite-demo-vanilla-bundle/public/assets/multiple-select.png deleted file mode 100644 index b1282ce42..000000000 Binary files a/examples/vite-demo-vanilla-bundle/public/assets/multiple-select.png and /dev/null differ diff --git a/examples/vite-demo-vanilla-bundle/public/i18n/en.json b/examples/vite-demo-vanilla-bundle/public/i18n/en.json index bff4917cf..c90558024 100644 --- a/examples/vite-demo-vanilla-bundle/public/i18n/en.json +++ b/examples/vite-demo-vanilla-bundle/public/i18n/en.json @@ -43,6 +43,7 @@ "NOT_CONTAINS": "Not contains", "NOT_EQUAL_TO": "Not equal to", "NOT_IN_COLLECTION_SEPERATED_BY_COMMA": "Search items not in a collection, must be separated by a comma (a,b)", + "NO_MATCHES_FOUND": "No matches found", "OF": "of", "OK": "OK", "PAGE": "Page", diff --git a/examples/vite-demo-vanilla-bundle/public/i18n/fr.json b/examples/vite-demo-vanilla-bundle/public/i18n/fr.json index e02cdab89..fa6361296 100644 --- a/examples/vite-demo-vanilla-bundle/public/i18n/fr.json +++ b/examples/vite-demo-vanilla-bundle/public/i18n/fr.json @@ -43,6 +43,7 @@ "NOT_CONTAINS": "Ne contient pas", "NOT_EQUAL_TO": "Non égal à", "NOT_IN_COLLECTION_SEPERATED_BY_COMMA": "Recherche excluant certain éléments d'une collection, doit être séparé par une virgule (a,b)", + "NO_MATCHES_FOUND": "Aucun résultat", "OF": "de", "OK": "Terminé", "PAGE": "Page", diff --git a/examples/vite-demo-vanilla-bundle/src/examples/example04.ts b/examples/vite-demo-vanilla-bundle/src/examples/example04.ts index 34adc41af..3e4a538f0 100644 --- a/examples/vite-demo-vanilla-bundle/src/examples/example04.ts +++ b/examples/vite-demo-vanilla-bundle/src/examples/example04.ts @@ -15,6 +15,7 @@ import { } from '@slickgrid-universal/common'; import { ExcelExportService } from '@slickgrid-universal/excel-export'; import { Slicker, SlickVanillaGridBundle } from '@slickgrid-universal/vanilla-bundle'; +import fetchJsonp from 'fetch-jsonp'; // import { fetch } from 'whatwg-fetch'; import { ExampleGridOptions } from './example-grid-options'; @@ -124,7 +125,7 @@ export default class Example4 { editorOptions: { filter: true // adds a filter on top of the multi-select dropdown }, - model: Editors.multipleSelect, + model: Editors.singleSelect, }, }, { @@ -244,22 +245,14 @@ export default class Example4 { placeholder: '🔎︎ search city', // We can use the autocomplete through 3 ways "collection", "collectionAsync" or with your own autocomplete options - // use your own autocomplete options, instead of $.ajax, use HttpClient or FetchClient - // here we use $.ajax just because I'm not sure how to configure HttpClient with JSONP and CORS + // use your own autocomplete options, instead of fetch-jsonp, use HttpClient or FetchClient editorOptions: { minLength: 3, fetch: (searchText, updateCallback) => { - $.ajax({ - url: 'http://gd.geobytes.com/AutoCompleteCity', - dataType: 'jsonp', - data: { - q: searchText - }, - success: (data) => { - const finalData = (data.length === 1 && data[0] === '') ? [] : data; // invalid result should be [] instead of ['' - updateCallback(finalData); - } - }); + fetchJsonp(`http://gd.geobytes.com/AutoCompleteCity?q=${searchText}`) + .then((response) => response.json()) + .then((json) => updateCallback(json)) + .catch((ex) => console.log('invalid JSONP response', ex)); }, } as AutocompleterOption, }, @@ -282,22 +275,15 @@ export default class Example4 { // We can use the autocomplete through 3 ways "collection", "collectionAsync" or with your own autocomplete options // collectionAsync: fetch(URL_COUNTRIES_COLLECTION), - // OR use your own autocomplete options, instead of $.ajax, use HttpClient or FetchClient - // here we use $.ajax just because I'm not sure how to configure HttpClient with JSONP and CORS + // OR use your own autocomplete options, instead of fetchJsonp, use HttpClient or FetchClient + // here we use fetchJsonp just because I'm not sure how to configure HttpClient with JSONP and CORS filterOptions: { minLength: 3, fetch: (searchText, updateCallback) => { - $.ajax({ - url: 'http://gd.geobytes.com/AutoCompleteCity', - dataType: 'jsonp', - data: { - q: searchText - }, - success: (data) => { - const finalData = (data.length === 1 && data[0] === '') ? [] : data; // invalid result should be [] instead of [''] - updateCallback(finalData); - } - }); + fetchJsonp(`http://gd.geobytes.com/AutoCompleteCity?q=${searchText}`) + .then((response) => response.json()) + .then((json) => updateCallback(json)) + .catch((ex) => console.log('invalid JSONP response', ex)); }, } as AutocompleterOption, } diff --git a/examples/vite-demo-vanilla-bundle/src/examples/example07.ts b/examples/vite-demo-vanilla-bundle/src/examples/example07.ts index 7c2317059..35aceaef2 100644 --- a/examples/vite-demo-vanilla-bundle/src/examples/example07.ts +++ b/examples/vite-demo-vanilla-bundle/src/examples/example07.ts @@ -345,16 +345,18 @@ export default class Example7 { `; - $(DOMPurify.sanitize(modalHtml)).appendTo('body'); + const elm = document.createElement('div'); + elm.innerHTML = DOMPurify.sanitize(modalHtml); + document.body.appendChild(elm.querySelector('div') as HTMLDivElement); - $('.btn-close').on('click', function () { + this._bindingEventService.bind(document.querySelectorAll('.modal-card-foot .btn-close'), 'click', () => { if (grid?.slickGrid?.getOptions().showHeaderRow) { grid?.showHeaderRow(true); } document.getElementById('modal-allFilter')?.remove(); }); - $('#btn-clear-all').on('click', function () { + this._bindingEventService.bind(document.querySelector('#btn-clear-all') as HTMLButtonElement, 'click', () => { document.getElementById('modal-allFilter')?.remove(); grid?.filterService.clearFilters(); }); @@ -362,13 +364,12 @@ export default class Example7 { for (const columnFilter of grid?.columnDefinitions) { if (columnFilter.filterable) { const filterElm = `modal-allfilter-${columnFilter.id}`; - $('#modal-allFilter-table') - .append( - `
+ const innerHtml = document.querySelector('#modal-allFilter-table')!.innerHTML; + document.querySelector('#modal-allFilter-table')!.innerHTML = innerHtml + + `
${columnFilter.name}
-
` - ); +
`; grid?.filterService.drawFilterTemplate(columnFilter, `#${filterElm}`); } } diff --git a/examples/vite-demo-vanilla-bundle/src/examples/example12.html b/examples/vite-demo-vanilla-bundle/src/examples/example12.html index e8ab6d7fa..da5dd1c2b 100644 --- a/examples/vite-demo-vanilla-bundle/src/examples/example12.html +++ b/examples/vite-demo-vanilla-bundle/src/examples/example12.html @@ -9,10 +9,6 @@

code -

- The Autocomplete Editor (City of Origin) might throw some console errors because it uses "http://gd.geobytes.com/" request (which is not using https). - You can allow "Insecure content" in your browser for this website only to test the Autocomplete -
diff --git a/examples/vite-demo-vanilla-bundle/src/examples/example12.ts b/examples/vite-demo-vanilla-bundle/src/examples/example12.ts index d3003fa6c..063572247 100644 --- a/examples/vite-demo-vanilla-bundle/src/examples/example12.ts +++ b/examples/vite-demo-vanilla-bundle/src/examples/example12.ts @@ -506,8 +506,10 @@ export default class Example12 { }; if (!(i % 8)) { - delete tmpArray[i].finish; // also test with undefined properties - delete tmpArray[i].percentComplete; // also test with undefined properties + // also test with undefined properties + delete tmpArray[i].finish; + delete tmpArray[i].percentComplete; + delete tmpArray[i].analysis.percentComplete; } } return tmpArray; diff --git a/examples/vite-demo-vanilla-bundle/src/examples/example15.ts b/examples/vite-demo-vanilla-bundle/src/examples/example15.ts index c78676edb..dfda377cd 100644 --- a/examples/vite-demo-vanilla-bundle/src/examples/example15.ts +++ b/examples/vite-demo-vanilla-bundle/src/examples/example15.ts @@ -66,6 +66,9 @@ export default class Example15 { id: 'name', name: 'Name', field: 'name', sortable: true, type: FieldType.string, filterable: true, + editor: { + model: Editors.text, + }, filter: { model: Filters.compoundInput }, diff --git a/examples/vite-demo-vanilla-bundle/src/jQuery.ts b/examples/vite-demo-vanilla-bundle/src/jQuery.ts deleted file mode 100644 index 00c75bff0..000000000 --- a/examples/vite-demo-vanilla-bundle/src/jQuery.ts +++ /dev/null @@ -1,4 +0,0 @@ -import jQuery from "jquery"; - -// define & and jQuery on the global window object -Object.assign(window, { $: jQuery, jQuery }) diff --git a/examples/vite-demo-vanilla-bundle/src/main.ts b/examples/vite-demo-vanilla-bundle/src/main.ts index 571c0fa16..82b6572a9 100644 --- a/examples/vite-demo-vanilla-bundle/src/main.ts +++ b/examples/vite-demo-vanilla-bundle/src/main.ts @@ -1,8 +1,4 @@ -import 'jquery'; -import './jQuery'; // define jQuery on the global window to make it available in Vite since @rollup/plugin-inject causes conflict with SASS variables prefixed with $var - // import all CSS required by Slickgrid-Universal -import 'multiple-select-modified/src/multiple-select.css'; import 'flatpickr/dist/flatpickr.min.css'; import './styles.scss'; diff --git a/examples/vite-demo-vanilla-bundle/tsconfig.json b/examples/vite-demo-vanilla-bundle/tsconfig.json index a59f8f367..e2074321d 100644 --- a/examples/vite-demo-vanilla-bundle/tsconfig.json +++ b/examples/vite-demo-vanilla-bundle/tsconfig.json @@ -21,7 +21,6 @@ "skipLibCheck": true, "strictPropertyInitialization": false, "types": [ - "jquery", "node" ], "typeRoots": [ diff --git a/examples/vite-demo-vanilla-bundle/vite.config.ts b/examples/vite-demo-vanilla-bundle/vite.config.ts index de575d642..ee7157260 100644 --- a/examples/vite-demo-vanilla-bundle/vite.config.ts +++ b/examples/vite-demo-vanilla-bundle/vite.config.ts @@ -17,9 +17,6 @@ export default defineConfig(() => { ], }, }, - optimizeDeps: { - include: ['jquery'], - }, preview: { port: 8888 }, @@ -27,6 +24,9 @@ export default defineConfig(() => { port: 8888, cors: true, host: 'localhost', + hmr: { + clientPort: 8888, + }, }, }; }); \ No newline at end of file diff --git a/lerna.json b/lerna.json index 2cc0bcef0..33a340fe1 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "$schema": "node_modules/@lerna-lite/cli/schemas/lerna-schema.json", - "version": "2.6.4", + "version": "3.0.0-beta.0", "npmClient": "pnpm", "loglevel": "info", "command": { @@ -20,6 +20,7 @@ "syncWorkspaceLock": true } }, + "changelogPreset": "conventional-changelog-conventionalcommits", "packages": [ "examples/*", "packages/*" diff --git a/package.json b/package.json index c4e045b4b..8c0f523d5 100644 --- a/package.json +++ b/package.json @@ -34,8 +34,8 @@ "preview:publish": "lerna publish from-package --dry-run", "preview:version": "lerna version --dry-run", "preview:roll-new-release": "pnpm bundle && pnpm new-version --dry-run && pnpm new-publish --dry-run", - "preview:alpha-release": "lerna publish 2.0.0-alpha.0 --dist-tag next --dry-run", - "preview:major-release": "lerna publish 2.0.0 --dry-run", + "beta-release": "lerna publish 3.0.0-beta.0 --dist-tag next", + "preview:major-release": "lerna publish 3.0.0 --dry-run", "new-version": "lerna version", "new-publish": "lerna publish from-package", "roll-new-release": "pnpm bundle && pnpm new-version && pnpm new-publish", @@ -49,7 +49,7 @@ }, "comments": { "new-version": "To create a new version with Lerna-Lite, simply run the following script (1) 'roll-new-release'.", - "devDependencies": "The dev deps 'jQuery', 'slickgrid', 'sortablejs' and 'whatwg-fetch' are simply installed for Jest unit tests." + "devDependencies": "The dev deps 'slickgrid', 'sortablejs' and 'whatwg-fetch' are simply installed for Jest unit tests." }, "engines": { "node": ">=16.15.0", @@ -83,7 +83,6 @@ "jest-cli": "^29.5.0", "jest-environment-jsdom": "^29.5.0", "jest-extended": "^3.2.4", - "jquery": "^3.7.0", "jsdom": "^22.0.0", "jsdom-global": "^3.0.2", "moment-mini": "^2.29.4", @@ -92,7 +91,7 @@ "rimraf": "^5.0.1", "rxjs": "^7.8.1", "servor": "^4.0.2", - "slickgrid": "^3.0.4", + "slickgrid": "^4.0.0", "sortablejs": "^1.15.0", "ts-jest": "^29.1.0", "ts-node": "^10.9.1", @@ -102,5 +101,8 @@ "funding": { "type": "ko_fi", "url": "https://ko-fi.com/ghiscoding" + }, + "dependencies": { + "conventional-changelog-conventionalcommits": "^5.0.0" } } \ No newline at end of file diff --git a/packages/binding/CHANGELOG.md b/packages/binding/CHANGELOG.md index 2ff1c1272..0e48dbb4e 100644 --- a/packages/binding/CHANGELOG.md +++ b/packages/binding/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [3.0.0-beta.0](https://github.com/ghiscoding/slickgrid-universal/compare/v2.6.4...v3.0.0-beta.0) (2023-05-20) + +**Note:** Version bump only for package @slickgrid-universal/binding + ## [2.6.4](https://github.com/ghiscoding/slickgrid-universal/compare/v2.6.3...v2.6.4) (2023-05-20) ### Bug Fixes diff --git a/packages/binding/package.json b/packages/binding/package.json index 125472267..bc58617cb 100644 --- a/packages/binding/package.json +++ b/packages/binding/package.json @@ -1,6 +1,6 @@ { "name": "@slickgrid-universal/binding", - "version": "2.6.4", + "version": "3.0.0-beta.0", "description": "Simple Vanilla Implementation of a Binding Engine & Helper to add properties/events 2 way bindings", "main": "dist/commonjs/index.js", "module": "dist/esm/index.js", diff --git a/packages/common/CHANGELOG.md b/packages/common/CHANGELOG.md index 7426d45dc..630b0b9fb 100644 --- a/packages/common/CHANGELOG.md +++ b/packages/common/CHANGELOG.md @@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [3.0.0-beta.0](https://github.com/ghiscoding/slickgrid-universal/compare/v2.6.4...v3.0.0-beta.0) (2023-05-20) + +### ⚠ BREAKING CHANGES + +* drop jQuery requirement (#962) +* **common:** migrate to multiple-select-vanilla (#919) + +### Features + +* **common:** migrate to multiple-select-vanilla ([#919](https://github.com/ghiscoding/slickgrid-universal/issues/919)) ([bc74207](https://github.com/ghiscoding/slickgrid-universal/commit/bc74207e9b2ec46209e87b126e1fcff596c162af)) - by @ghiscoding +* drop jQuery requirement ([#962](https://github.com/ghiscoding/slickgrid-universal/issues/962)) ([3da21da](https://github.com/ghiscoding/slickgrid-universal/commit/3da21daacc391a0fb309fcddd78442642c5269f6)) - by @ghiscoding + ## [2.6.4](https://github.com/ghiscoding/slickgrid-universal/compare/v2.6.3...v2.6.4) (2023-05-20) ### Bug Fixes diff --git a/packages/common/package.json b/packages/common/package.json index 0ccce7b41..ecfbe840a 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -1,6 +1,6 @@ { "name": "@slickgrid-universal/common", - "version": "2.6.4", + "version": "3.0.0-beta.0", "description": "SlickGrid-Universal Common Code", "main": "dist/commonjs/index.js", "module": "dist/esm/index.js", @@ -49,18 +49,16 @@ "bundle:commonjs": "tsc --project tsconfig.bundle.json --outDir dist/commonjs --module commonjs", "bundle:esm": "tsc --project tsconfig.bundle.json --outDir dist/esm --module esnext --target es2018", "bundle:types": "tsc --emitDeclarationOnly --declarationMap --outDir dist/types", - "sass-build-task:scss-compile:bootstrap": "sass src/styles/slickgrid-theme-bootstrap.scss dist/styles/css/slickgrid-theme-bootstrap.css --style=compressed --quiet-deps --no-source-map", - "sass-build-task:scss-compile:material": "sass src/styles/slickgrid-theme-material.scss dist/styles/css/slickgrid-theme-material.css --style=compressed --quiet-deps --no-source-map", - "sass-build-task:scss-compile:material-bare": "sass src/styles/slickgrid-theme-material.bare.scss dist/styles/css/slickgrid-theme-material.bare.css --style=compressed --quiet-deps --no-source-map", - "sass-build-task:scss-compile:material-lite": "sass src/styles/slickgrid-theme-material.lite.scss dist/styles/css/slickgrid-theme-material.lite.css --style=compressed --quiet-deps --no-source-map", - "sass-build-task:scss-compile:salesforce": "sass src/styles/slickgrid-theme-salesforce.scss dist/styles/css/slickgrid-theme-salesforce.css --style=compressed --quiet-deps --no-source-map", - "sass-build-task:scss-compile:salesforce-bare": "sass src/styles/slickgrid-theme-salesforce.bare.scss dist/styles/css/slickgrid-theme-salesforce.bare.css --style=compressed --quiet-deps --no-source-map", - "sass-build-task:scss-compile:salesforce-lite": "sass src/styles/slickgrid-theme-salesforce.lite.scss dist/styles/css/slickgrid-theme-salesforce.lite.css --style=compressed --quiet-deps --no-source-map", + "sass-build-task:scss-compile:bootstrap": "sass src/styles/slickgrid-theme-bootstrap.scss dist/styles/css/slickgrid-theme-bootstrap.css --style=compressed --quiet-deps --no-source-map --load-path=node_modules", + "sass-build-task:scss-compile:material": "sass src/styles/slickgrid-theme-material.scss dist/styles/css/slickgrid-theme-material.css --style=compressed --quiet-deps --no-source-map --load-path=node_modules", + "sass-build-task:scss-compile:material-bare": "sass src/styles/slickgrid-theme-material.bare.scss dist/styles/css/slickgrid-theme-material.bare.css --style=compressed --quiet-deps --no-source-map --load-path=node_modules", + "sass-build-task:scss-compile:material-lite": "sass src/styles/slickgrid-theme-material.lite.scss dist/styles/css/slickgrid-theme-material.lite.css --style=compressed --quiet-deps --no-source-map --load-path=node_modules", + "sass-build-task:scss-compile:salesforce": "sass src/styles/slickgrid-theme-salesforce.scss dist/styles/css/slickgrid-theme-salesforce.css --style=compressed --quiet-deps --no-source-map --load-path=node_modules", + "sass-build-task:scss-compile:salesforce-bare": "sass src/styles/slickgrid-theme-salesforce.bare.scss dist/styles/css/slickgrid-theme-salesforce.bare.css --style=compressed --quiet-deps --no-source-map --load-path=node_modules", + "sass-build-task:scss-compile:salesforce-lite": "sass src/styles/slickgrid-theme-salesforce.lite.scss dist/styles/css/slickgrid-theme-salesforce.lite.css --style=compressed --quiet-deps --no-source-map --load-path=node_modules", "sass:build": "run-p sass-build-task:scss-compile:*", "postsass:build": "postcss --no-map --use cssnano --use autoprefixer --dir dist/styles/css dist/styles/css --style=compressed --quiet-deps --no-source-map", - "presass:copy": "cross-env copyfiles -f src/styles/*.png dist/styles/css", "sass:copy": "cross-env copyfiles -f src/styles/*.scss dist/styles/sass", - "postsass:copy": "cross-env copyfiles -f src/styles/*.png dist/styles/sass", "sass:watch:bootstrap": "npm run sass-build-task:scss-compile:bootstrap -- --watch", "sass:watch:material": "npm run sass-build-task:scss-compile:material -- --watch", "sass:watch:salesforce": "npm run sass-build-task:scss-compile:salesforce -- --watch", @@ -79,16 +77,14 @@ "dequal": "^2.0.3", "dompurify": "^3.0.3", "flatpickr": "^4.6.13", - "jquery": "^3.7.0", "moment-mini": "^2.29.4", - "multiple-select-modified": "^1.3.17", - "slickgrid": "^3.0.4", + "multiple-select-vanilla": "^0.3.1", + "slickgrid": "^4.0.0", "sortablejs": "^1.15.0", "un-flatten-tree": "^2.0.12" }, "devDependencies": { "@types/dompurify": "^3.0.2", - "@types/jquery": "^3.5.16", "@types/sortablejs": "^1.15.1", "autoprefixer": "^10.4.14", "copyfiles": "^2.4.1", @@ -100,8 +96,8 @@ "sass": "^1.62.1" }, "engines": { - "node": ">=14.17.0", - "npm": ">=6.14.13" + "node": ">=16.15.0", + "npm": ">=8.5.0" }, "funding": { "type": "ko_fi", diff --git a/packages/common/src/editors/__tests__/autocompleterEditor.spec.ts b/packages/common/src/editors/__tests__/autocompleterEditor.spec.ts index ca471198a..f6c7be07d 100644 --- a/packages/common/src/editors/__tests__/autocompleterEditor.spec.ts +++ b/packages/common/src/editors/__tests__/autocompleterEditor.spec.ts @@ -765,7 +765,9 @@ describe('AutocompleterEditor', () => { it('should call "setValue" with value & apply value flag and expect the DOM element to have same value and also expect the value to be applied to the item object', () => { const activeCellMock = { row: 0, cell: 0 }; jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); editor = new AutocompleterEditor(editorArguments); editor.setValue({ value: 'male', label: 'Male' }, true); @@ -779,7 +781,9 @@ describe('AutocompleterEditor', () => { it('should call "show" and expect the DOM element to not be disabled when "onBeforeEditCell" is NOT returning false', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => undefined + }); editor = new AutocompleterEditor(editorArguments); const disableSpy = jest.spyOn(editor, 'disable'); @@ -793,8 +797,12 @@ describe('AutocompleterEditor', () => { it('should call "show" and expect the DOM element to become disabled with empty value set in the form values when "onBeforeEditCell" returns false', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(false); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => false + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); editor = new AutocompleterEditor(editorArguments); editor.loadValue(mockItemData); @@ -815,8 +823,12 @@ describe('AutocompleterEditor', () => { it('should call "show" and expect the DOM element to become disabled and empty when "onBeforeEditCell" returns false and also expect "onBeforeComposite" to not be called because the value is blank', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(false); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => false + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.compositeEditorOptions = { excludeDisabledFieldFormValues: true }; @@ -837,7 +849,9 @@ describe('AutocompleterEditor', () => { it('should call "disable" method and expect the DOM element to become disabled and have an empty formValues be passed in the onCompositeEditorChange event', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.compositeEditorOptions = { excludeDisabledFieldFormValues: true }; @@ -859,7 +873,9 @@ describe('AutocompleterEditor', () => { it('should call "reset" method and expect the DOM element to become blank & untouched and have an empty formValues be passed in the onCompositeEditorChange event', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.compositeEditorOptions = { excludeDisabledFieldFormValues: true }; @@ -881,8 +897,12 @@ describe('AutocompleterEditor', () => { it('should expect "setValue" to have been called and also "onCompositeEditorChange" to have been triggered with the new value showing up in its "formValues" object', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => undefined + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.autoCommitEdit = true; (mockColumn.internalColumnEditor as ColumnEditor).collection = ['male', 'female']; mockItemData = { id: 123, gender: 'female', isActive: true }; @@ -905,7 +925,9 @@ describe('AutocompleterEditor', () => { it('should create the editor and expect a different collection outputed when using the override', () => { const activeCellMock = { row: 0, cell: 0 }; jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); mockColumn.internalColumnEditor = { collection: ['Other', 'Male', 'Female'], collectionOverride: (inputCollection) => inputCollection.filter(item => item !== 'other') diff --git a/packages/common/src/editors/__tests__/checkboxEditor.spec.ts b/packages/common/src/editors/__tests__/checkboxEditor.spec.ts index 2233d3b0f..6f2028a1a 100644 --- a/packages/common/src/editors/__tests__/checkboxEditor.spec.ts +++ b/packages/common/src/editors/__tests__/checkboxEditor.spec.ts @@ -417,7 +417,9 @@ describe('CheckboxEditor', () => { it('should call "setValue" with value & apply value flag and expect the DOM element to have same value and also expect the value to be applied to the item object', () => { const activeCellMock = { row: 0, cell: 0 }; jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); editor = new CheckboxEditor(editorArguments); editor.setValue(true, true); @@ -431,7 +433,9 @@ describe('CheckboxEditor', () => { it('should call "show" and expect the DOM element to not be disabled when "onBeforeEditCell" is NOT returning false', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => undefined + }); editor = new CheckboxEditor(editorArguments); const disableSpy = jest.spyOn(editor, 'disable'); @@ -445,8 +449,12 @@ describe('CheckboxEditor', () => { it('should call "show" and expect the DOM element to become disabled with empty value set in the form values when "onBeforeEditCell" returns false', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(false); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => false + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); editor = new CheckboxEditor(editorArguments); editor.loadValue(mockItemData); @@ -467,8 +475,12 @@ describe('CheckboxEditor', () => { it('should call "show" and expect the DOM element to become disabled and empty when "onBeforeEditCell" returns false and also expect "onBeforeComposite" to not be called because the value is blank', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(false); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => false + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.compositeEditorOptions = { excludeDisabledFieldFormValues: true }; @@ -489,7 +501,9 @@ describe('CheckboxEditor', () => { it('should call "disable" method and expect the DOM element to become disabled and have an empty formValues be passed in the onCompositeEditorChange event', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.compositeEditorOptions = { excludeDisabledFieldFormValues: true }; @@ -512,8 +526,12 @@ describe('CheckboxEditor', () => { it('should expect "onCompositeEditorChange" to have been triggered with the new value showing up in its "formValues" object', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => undefined + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.autoCommitEdit = true; mockItemData = { id: 1, title: 'task 1', isActive: true }; diff --git a/packages/common/src/editors/__tests__/dateEditor.spec.ts b/packages/common/src/editors/__tests__/dateEditor.spec.ts index 0077acec8..69632315f 100644 --- a/packages/common/src/editors/__tests__/dateEditor.spec.ts +++ b/packages/common/src/editors/__tests__/dateEditor.spec.ts @@ -523,7 +523,9 @@ describe('DateEditor', () => { it('should call "setValue" with value & apply value flag and expect the DOM element to have same value and also expect the value to be applied to the item object', () => { const activeCellMock = { row: 0, cell: 0 }; jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); mockColumn.type = FieldType.dateIso; editor = new DateEditor(editorArguments); editor.setValue('2001-01-02', true); @@ -538,7 +540,9 @@ describe('DateEditor', () => { it('should call "show" and expect the DOM element to not be disabled when "onBeforeEditCell" is NOT returning false', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => undefined + }); editor = new DateEditor(editorArguments); const disableSpy = jest.spyOn(editor, 'disable'); @@ -552,8 +556,12 @@ describe('DateEditor', () => { it('should call "show" and expect the DOM element to become disabled with empty value set in the form values when "onBeforeEditCell" returns false', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(false); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => false + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); editor = new DateEditor(editorArguments); editor.loadValue(mockItemData); @@ -574,8 +582,12 @@ describe('DateEditor', () => { it('should call "show" and expect the DOM element to become disabled and empty when "onBeforeEditCell" returns false and also expect "onBeforeComposite" to not be called because the value is blank', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(false); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => false + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.compositeEditorOptions = { excludeDisabledFieldFormValues: true }; @@ -596,7 +608,9 @@ describe('DateEditor', () => { it('should call "disable" method and expect the DOM element to become disabled and have an empty formValues be passed in the onCompositeEditorChange event', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.compositeEditorOptions = { excludeDisabledFieldFormValues: true }; @@ -620,8 +634,12 @@ describe('DateEditor', () => { (mockColumn.internalColumnEditor as ColumnEditor).editorOptions = { allowInput: true, altInput: false }; mockColumn.type = FieldType.dateIso; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => undefined + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.autoCommitEdit = true; mockItemData = { id: 1, startDate: '2001-01-02', isActive: true }; diff --git a/packages/common/src/editors/__tests__/dualInputEditor.spec.ts b/packages/common/src/editors/__tests__/dualInputEditor.spec.ts index fe7911102..b9357fc91 100644 --- a/packages/common/src/editors/__tests__/dualInputEditor.spec.ts +++ b/packages/common/src/editors/__tests__/dualInputEditor.spec.ts @@ -864,7 +864,9 @@ describe('DualInputEditor', () => { it('should call "show" and expect the DOM element to not be disabled when "onBeforeEditCell" is NOT returning false', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => undefined + }); editor = new DualInputEditor(editorArguments); const disableSpy = jest.spyOn(editor, 'disable'); @@ -878,8 +880,12 @@ describe('DualInputEditor', () => { it('should call "show" and expect the DOM element to become disabled with empty value set in the form values when "onBeforeEditCell" returns false', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(false); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => false + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); editor = new DualInputEditor(editorArguments); editor.loadValue(mockItemData); @@ -902,8 +908,12 @@ describe('DualInputEditor', () => { it('should call "show" and expect the DOM element to become disabled and empty when "onBeforeEditCell" returns false', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(false); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => false + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.compositeEditorOptions = { excludeDisabledFieldFormValues: true }; @@ -930,8 +940,12 @@ describe('DualInputEditor', () => { jest.useFakeTimers(); const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => undefined + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.autoCommitEdit = true; mockItemData = { id: 1, from: 4, to: 5, isActive: true }; @@ -954,8 +968,12 @@ describe('DualInputEditor', () => { jest.useFakeTimers(); const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => undefined + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.autoCommitEdit = true; mockItemData = { id: 1, from: 4, to: 5, isActive: true }; diff --git a/packages/common/src/editors/__tests__/floatEditor.spec.ts b/packages/common/src/editors/__tests__/floatEditor.spec.ts index d4cfdd917..1f153c8ef 100644 --- a/packages/common/src/editors/__tests__/floatEditor.spec.ts +++ b/packages/common/src/editors/__tests__/floatEditor.spec.ts @@ -650,7 +650,9 @@ describe('FloatEditor', () => { it('should call "setValue" with value & apply value flag and expect the DOM element to have same value and also expect the value to be applied to the item object', () => { const activeCellMock = { row: 0, cell: 0 }; jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); editor = new FloatEditor(editorArguments); editor.setValue(123, true); @@ -664,7 +666,9 @@ describe('FloatEditor', () => { it('should call "show" and expect the DOM element to not be disabled when "onBeforeEditCell" is NOT returning false', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => undefined + }); editor = new FloatEditor(editorArguments); const disableSpy = jest.spyOn(editor, 'disable'); @@ -678,8 +682,12 @@ describe('FloatEditor', () => { it('should call "show" and expect the DOM element to become disabled with empty value set in the form values when "onBeforeEditCell" returns false', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(false); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => false + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); editor = new FloatEditor(editorArguments); editor.loadValue(mockItemData); @@ -700,8 +708,12 @@ describe('FloatEditor', () => { it('should call "show" and expect the DOM element to become disabled and empty when "onBeforeEditCell" returns false and also expect "onBeforeComposite" to not be called because the value is blank', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(false); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => false + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.compositeEditorOptions = { excludeDisabledFieldFormValues: true }; @@ -722,7 +734,9 @@ describe('FloatEditor', () => { it('should call "disable" method and expect the DOM element to become disabled and have an empty formValues be passed in the onCompositeEditorChange event', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.compositeEditorOptions = { excludeDisabledFieldFormValues: true }; @@ -745,8 +759,12 @@ describe('FloatEditor', () => { jest.useFakeTimers(); const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => undefined + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.autoCommitEdit = true; mockItemData = { id: 1, price: 35, isActive: true }; @@ -769,8 +787,12 @@ describe('FloatEditor', () => { jest.useFakeTimers(); const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => undefined + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.autoCommitEdit = true; mockItemData = { id: 1, price: 35, isActive: true }; @@ -794,8 +816,12 @@ describe('FloatEditor', () => { jest.useFakeTimers(); const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => undefined + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.autoCommitEdit = true; mockItemData = { id: 1, price: 35, isActive: true }; diff --git a/packages/common/src/editors/__tests__/inputEditor.spec.ts b/packages/common/src/editors/__tests__/inputEditor.spec.ts index 7c59e720a..03a0b7d32 100644 --- a/packages/common/src/editors/__tests__/inputEditor.spec.ts +++ b/packages/common/src/editors/__tests__/inputEditor.spec.ts @@ -545,7 +545,9 @@ describe('InputEditor (TextEditor)', () => { it('should call "setValue" with value & apply value flag and expect the DOM element to have same value and also expect the value to be applied to the item object', () => { const activeCellMock = { row: 0, cell: 0 }; jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); editor = new InputEditor(editorArguments, 'text'); editor.setValue('task 1', true); @@ -559,7 +561,9 @@ describe('InputEditor (TextEditor)', () => { it('should call "show" and expect the DOM element to not be disabled when "onBeforeEditCell" is NOT returning false', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => undefined + }); editor = new InputEditor(editorArguments, 'text'); const disableSpy = jest.spyOn(editor, 'disable'); @@ -573,8 +577,12 @@ describe('InputEditor (TextEditor)', () => { it('should call "show" and expect the DOM element to become disabled with empty value set in the form values when "onBeforeEditCell" returns false', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(false); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => false + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); editor = new InputEditor(editorArguments, 'text'); editor.loadValue(mockItemData); @@ -595,8 +603,12 @@ describe('InputEditor (TextEditor)', () => { it('should call "show" and expect the DOM element to become disabled and empty when "onBeforeEditCell" returns false and also expect "onBeforeComposite" to not be called because the value is blank', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(false); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => false + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.compositeEditorOptions = { excludeDisabledFieldFormValues: true }; @@ -617,7 +629,9 @@ describe('InputEditor (TextEditor)', () => { it('should call "disable" method and expect the DOM element to become disabled and have an empty formValues be passed in the onCompositeEditorChange event', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.compositeEditorOptions = { excludeDisabledFieldFormValues: true }; @@ -640,8 +654,12 @@ describe('InputEditor (TextEditor)', () => { jest.useFakeTimers(); const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => undefined + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.autoCommitEdit = true; mockItemData = { id: 1, title: 'task 2', isActive: true }; diff --git a/packages/common/src/editors/__tests__/inputPasswordEditor.spec.ts b/packages/common/src/editors/__tests__/inputPasswordEditor.spec.ts index f948888aa..d45f59d34 100644 --- a/packages/common/src/editors/__tests__/inputPasswordEditor.spec.ts +++ b/packages/common/src/editors/__tests__/inputPasswordEditor.spec.ts @@ -547,7 +547,9 @@ describe('InputPasswordEditor', () => { it('should call "setValue" with value & apply value flag and expect the DOM element to have same value and also expect the value to be applied to the item object', () => { const activeCellMock = { row: 0, cell: 0 }; jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); editor = new InputPasswordEditor(editorArguments); editor.setValue('task 1', true); @@ -561,7 +563,9 @@ describe('InputPasswordEditor', () => { it('should call "show" and expect the DOM element to not be disabled when "onBeforeEditCell" is NOT returning false', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => undefined + }); editor = new InputPasswordEditor(editorArguments); const disableSpy = jest.spyOn(editor, 'disable'); @@ -575,8 +579,12 @@ describe('InputPasswordEditor', () => { it('should call "show" and expect the DOM element to become disabled with empty value set in the form values when "onBeforeEditCell" returns false', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(false); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => false + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); editor = new InputPasswordEditor(editorArguments); editor.loadValue(mockItemData); @@ -597,8 +605,12 @@ describe('InputPasswordEditor', () => { it('should call "show" and expect the DOM element to become disabled and empty when "onBeforeEditCell" returns false and also expect "onBeforeComposite" to not be called because the value is blank', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(false); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => false + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.compositeEditorOptions = { excludeDisabledFieldFormValues: true }; @@ -619,7 +631,9 @@ describe('InputPasswordEditor', () => { it('should call "disable" method and expect the DOM element to become disabled and have an empty formValues be passed in the onCompositeEditorChange event', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.compositeEditorOptions = { excludeDisabledFieldFormValues: true }; @@ -642,8 +656,12 @@ describe('InputPasswordEditor', () => { jest.useFakeTimers(); const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => undefined + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.autoCommitEdit = true; mockItemData = { id: 1, title: 'task 2', isActive: true }; diff --git a/packages/common/src/editors/__tests__/integerEditor.spec.ts b/packages/common/src/editors/__tests__/integerEditor.spec.ts index 3a01e0b5a..a289b1a44 100644 --- a/packages/common/src/editors/__tests__/integerEditor.spec.ts +++ b/packages/common/src/editors/__tests__/integerEditor.spec.ts @@ -583,7 +583,9 @@ describe('IntegerEditor', () => { it('should call "setValue" with value & apply value flag and expect the DOM element to have same value and also expect the value to be applied to the item object', () => { const activeCellMock = { row: 0, cell: 0 }; jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); editor = new IntegerEditor(editorArguments); editor.setValue(123, true); @@ -597,7 +599,9 @@ describe('IntegerEditor', () => { it('should call "show" and expect the DOM element to not be disabled when "onBeforeEditCell" is NOT returning false', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => undefined + }); editor = new IntegerEditor(editorArguments); const disableSpy = jest.spyOn(editor, 'disable'); @@ -611,8 +615,12 @@ describe('IntegerEditor', () => { it('should call "show" and expect the DOM element to become disabled with empty value set in the form values when "onBeforeEditCell" returns false', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(false); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => false + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); editor = new IntegerEditor(editorArguments); editor.loadValue(mockItemData); @@ -633,7 +641,9 @@ describe('IntegerEditor', () => { it('should call "disable" method and expect the DOM element to become disabled and have an empty formValues be passed in the onCompositeEditorChange event', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.compositeEditorOptions = { excludeDisabledFieldFormValues: true }; @@ -655,7 +665,9 @@ describe('IntegerEditor', () => { it('should call "disable" method and expect the DOM element to become disabled and have an empty formValues be passed in the onCompositeEditorChange event', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.compositeEditorOptions = { excludeDisabledFieldFormValues: true }; @@ -678,8 +690,12 @@ describe('IntegerEditor', () => { jest.useFakeTimers(); const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => undefined + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.autoCommitEdit = true; mockItemData = { id: 1, price: 35, isActive: true }; @@ -703,8 +719,12 @@ describe('IntegerEditor', () => { jest.useFakeTimers(); const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => undefined + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.autoCommitEdit = true; mockItemData = { id: 1, price: 35, isActive: true }; diff --git a/packages/common/src/editors/__tests__/longTextEditor.spec.ts b/packages/common/src/editors/__tests__/longTextEditor.spec.ts index 9e660284f..1c64775d4 100644 --- a/packages/common/src/editors/__tests__/longTextEditor.spec.ts +++ b/packages/common/src/editors/__tests__/longTextEditor.spec.ts @@ -834,7 +834,9 @@ describe('LongTextEditor', () => { it('should call "setValue" with value & apply value flag and expect the DOM element to have same value and also expect the value to be applied to the item object', () => { const activeCellMock = { row: 0, cell: 0 }; jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); editor = new LongTextEditor(editorArguments); editor.setValue('task 2', true); @@ -848,7 +850,9 @@ describe('LongTextEditor', () => { it('should call "show" and expect the DOM element to not be disabled when "onBeforeEditCell" is NOT returning false', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => undefined + }); editor = new LongTextEditor(editorArguments); const disableSpy = jest.spyOn(editor, 'disable'); @@ -862,8 +866,12 @@ describe('LongTextEditor', () => { it('should call "show" and expect the DOM element to become disabled with empty value set in the form values when "onBeforeEditCell" returns false', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(false); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => false + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); editor = new LongTextEditor(editorArguments); editor.loadValue(mockItemData); @@ -884,8 +892,12 @@ describe('LongTextEditor', () => { it('should call "show" and expect the DOM element to become disabled and empty when "onBeforeEditCell" returns false and also expect "onBeforeComposite" to not be called because the value is blank', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(false); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => false + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.compositeEditorOptions = { excludeDisabledFieldFormValues: true }; @@ -906,7 +918,9 @@ describe('LongTextEditor', () => { it('should call "disable" method and expect the DOM element to become disabled and have an empty formValues be passed in the onCompositeEditorChange event', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.compositeEditorOptions = { excludeDisabledFieldFormValues: true }; @@ -929,8 +943,12 @@ describe('LongTextEditor', () => { jest.useFakeTimers(); const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => undefined + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.autoCommitEdit = true; mockItemData = { id: 1, title: 'task 2', isActive: true }; diff --git a/packages/common/src/editors/__tests__/multipleSelectEditor.spec.ts b/packages/common/src/editors/__tests__/multipleSelectEditor.spec.ts index 0be97e0cc..0de93314e 100644 --- a/packages/common/src/editors/__tests__/multipleSelectEditor.spec.ts +++ b/packages/common/src/editors/__tests__/multipleSelectEditor.spec.ts @@ -1,5 +1,5 @@ // import 3rd party lib multiple-select for the tests -import 'multiple-select-modified'; +import 'multiple-select-vanilla'; import { Editors } from '../index'; import { MultipleSelectEditor } from '../multipleSelectEditor'; @@ -85,7 +85,7 @@ describe('MultipleSelectEditor', () => { it('should initialize the editor', () => { (mockColumn.internalColumnEditor as ColumnEditor).collection = [{ value: 'male', label: 'male' }, { value: 'female', label: 'female' }]; gridOptionMock.translater = translateService; - editor = new MultipleSelectEditor(editorArguments); + editor = new MultipleSelectEditor(editorArguments, 0); const editorCount = document.body.querySelectorAll('select.ms-filter.editor-gender').length; const spy = jest.spyOn(editor, 'show'); jest.runAllTimers(); // fast-forward timer @@ -111,10 +111,11 @@ describe('MultipleSelectEditor', () => { it('should hide the DOM element div wrapper when the "hide" method is called', () => { editor = new MultipleSelectEditor(editorArguments); - const editorElm = document.body.querySelector('[name=editor-gender].ms-drop') as HTMLDivElement; + const editorElm = document.body.querySelector('[data-name=editor-gender].ms-drop') as HTMLDivElement; + expect(editorElm).toBeTruthy(); - editor.show(); - expect(editorElm.style.display).toBe(''); + editor.show(null); + expect(editorElm.style.display).toBe('block'); editor.hide(); expect(editorElm.style.display).toBe('none'); @@ -122,13 +123,14 @@ describe('MultipleSelectEditor', () => { it('should show the DOM element div wrapper when the "show" method is called', () => { editor = new MultipleSelectEditor(editorArguments); - const editorElm = document.body.querySelector('[name=editor-gender].ms-drop') as HTMLDivElement; + const editorElm = document.body.querySelector('[data-name=editor-gender].ms-drop') as HTMLDivElement; + expect(editorElm).toBeTruthy(); editor.hide(); expect(editorElm.style.display).toBe('none'); - editor.show(); - expect(editorElm.style.display).toBe(''); + editor.show(null); + expect(editorElm.style.display).toBe('block'); }); }); }); diff --git a/packages/common/src/editors/__tests__/selectEditor.spec.ts b/packages/common/src/editors/__tests__/selectEditor.spec.ts index b8a9a4b99..311750b7e 100644 --- a/packages/common/src/editors/__tests__/selectEditor.spec.ts +++ b/packages/common/src/editors/__tests__/selectEditor.spec.ts @@ -1,5 +1,5 @@ // import 3rd party lib multiple-select for the tests -import 'multiple-select-modified'; +import 'multiple-select-vanilla'; import { Editors } from '../index'; import { SelectEditor } from '../selectEditor'; @@ -54,6 +54,7 @@ describe('SelectEditor', () => { divContainer = document.createElement('div'); divContainer.innerHTML = template; + document.body.innerHTML = ''; document.body.appendChild(divContainer); mockColumn = { id: 'gender', field: 'gender', editable: true, editor: { model: Editors.multipleSelect }, internalColumnEditor: {} } as Column; @@ -168,7 +169,7 @@ describe('SelectEditor', () => { expect(disableSpy).toHaveBeenCalledWith(true); }); - it('should initialize the editor even when user define his own editor options', () => { + it('should initialize the editor even when user define its own editor options', () => { (mockColumn.internalColumnEditor as ColumnEditor).editorOptions = { minLength: 3 } as AutocompleterOption; editor = new SelectEditor(editorArguments, true); const editorCount = document.body.querySelectorAll('select.ms-filter.editor-gender').length; @@ -182,7 +183,7 @@ describe('SelectEditor', () => { (mockColumn.internalColumnEditor as ColumnEditor).collection = [{ value: 'male', label: 'male' }, { value: 'female', label: 'female' }]; editor = new SelectEditor(editorArguments, true); - const editorElm = divContainer.querySelector('.ms-filter.editor-gender .placeholder') as HTMLSpanElement; + const editorElm = divContainer.querySelector('.ms-filter.editor-gender .ms-placeholder') as HTMLSpanElement; expect(editorElm.innerHTML).toBe(testValue); }); @@ -217,17 +218,17 @@ describe('SelectEditor', () => { const editorElm = editor.editorDomElement; expect(editor.getValue()).toEqual(['male']); - expect(editorElm[0].value).toEqual('male'); + expect(editor.msInstance!.getSelects()).toEqual(['male']); }); it('should create the multi-select editor with a blank entry at the beginning of the collection when "addBlankEntry" is set in the "collectionOptions" property', () => { (mockColumn.internalColumnEditor as ColumnEditor).collection = [{ value: 'male', label: 'male' }, { value: 'female', label: 'female' }]; (mockColumn.internalColumnEditor as ColumnEditor).collectionOptions = { addBlankEntry: true }; - editor = new SelectEditor(editorArguments, true); + editor = new SelectEditor(editorArguments, true, 0); const editorBtnElm = divContainer.querySelector('.ms-parent.ms-filter.editor-gender button.ms-choice') as HTMLButtonElement; - const editorListElm = divContainer.querySelectorAll(`[name=editor-gender].ms-drop ul>li input[type=checkbox]`); - const editorOkElm = divContainer.querySelector(`[name=editor-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const editorListElm = divContainer.querySelectorAll(`[data-name=editor-gender].ms-drop ul>li input[type=checkbox]`); + const editorOkElm = divContainer.querySelector(`[data-name=editor-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; editorBtnElm.click(); editorOkElm.click(); @@ -240,10 +241,10 @@ describe('SelectEditor', () => { (mockColumn.internalColumnEditor as ColumnEditor).collection = [{ value: 'male', label: 'male' }, { value: 'female', label: 'female' }]; (mockColumn.internalColumnEditor as ColumnEditor).collectionOptions = { addCustomFirstEntry: { value: null as any, label: '' } }; - editor = new SelectEditor(editorArguments, true); + editor = new SelectEditor(editorArguments, true, 0); const editorBtnElm = divContainer.querySelector('.ms-parent.ms-filter.editor-gender button.ms-choice') as HTMLButtonElement; - const editorListElm = divContainer.querySelectorAll(`[name=editor-gender].ms-drop ul>li input[type=checkbox]`); - const editorOkElm = divContainer.querySelector(`[name=editor-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const editorListElm = divContainer.querySelectorAll(`[data-name=editor-gender].ms-drop ul>li input[type=checkbox]`); + const editorOkElm = divContainer.querySelector(`[data-name=editor-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; editorBtnElm.click(); editorOkElm.click(); @@ -256,10 +257,10 @@ describe('SelectEditor', () => { (mockColumn.internalColumnEditor as ColumnEditor).collection = [{ value: 'male', label: 'male' }, { value: 'female', label: 'female' }]; (mockColumn.internalColumnEditor as ColumnEditor).collectionOptions = { addCustomLastEntry: { value: null as any, label: '' } }; - editor = new SelectEditor(editorArguments, true); + editor = new SelectEditor(editorArguments, true, 0); const editorBtnElm = divContainer.querySelector('.ms-parent.ms-filter.editor-gender button.ms-choice') as HTMLButtonElement; - const editorListElm = divContainer.querySelectorAll(`[name=editor-gender].ms-drop ul>li input[type=checkbox]`); - const editorOkElm = divContainer.querySelector(`[name=editor-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const editorListElm = divContainer.querySelectorAll(`[data-name=editor-gender].ms-drop ul>li input[type=checkbox]`); + const editorOkElm = divContainer.querySelector(`[data-name=editor-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; editorBtnElm.click(); editorOkElm.click(); @@ -270,14 +271,17 @@ describe('SelectEditor', () => { describe('isValueChanged method', () => { it('should return True after doing a check of an option and clicking on the OK button', () => { - editor = new SelectEditor(editorArguments, true); + editor = new SelectEditor(editorArguments, true, 0); editor.reset(); const editorBtnElm = divContainer.querySelector('.ms-parent.ms-filter.editor-gender button.ms-choice') as HTMLButtonElement; - const editorListElm = divContainer.querySelectorAll(`[name=editor-gender].ms-drop ul>li input[type=checkbox]`); - const editorOkElm = divContainer.querySelector(`[name=editor-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const editorListElm = divContainer.querySelectorAll(`[data-name=editor-gender].ms-drop ul>li input[type=checkbox]`); + const editorOkElm = divContainer.querySelector(`[data-name=editor-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; editorBtnElm.click(); // we can use property "checked" or dispatch an event + // editor.msInstance?.setSelects(['female']); + // editor.msInstance?.close(); + editorListElm[1].checked = true; editorListElm[1].dispatchEvent(new CustomEvent('click')); editorOkElm.click(); @@ -288,11 +292,11 @@ describe('SelectEditor', () => { }); it('should return False after doing a check & uncheck of the same option and clicking on the OK button', () => { - editor = new SelectEditor(editorArguments, true); + editor = new SelectEditor(editorArguments, true, 0); editor.reset(); const editorBtnElm = divContainer.querySelector('.ms-parent.ms-filter.editor-gender button.ms-choice') as HTMLButtonElement; const editorListElm = divContainer.querySelectorAll(`.ms-drop ul>li input[type=checkbox]`); - const editorOkElm = divContainer.querySelector(`[name=editor-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const editorOkElm = divContainer.querySelector(`[data-name=editor-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; editorBtnElm.click(); // we can use property "checked" or dispatch an event @@ -309,21 +313,21 @@ describe('SelectEditor', () => { it('should call the "changeEditorOption" method and expect new option to be merged with the previous Editor options and also expect to call MultipleSelect "refreshOptions" setter method', () => { editor = new SelectEditor(editorArguments, true); - const multipleSelectSpy = jest.spyOn(editor.editorDomElement, 'multipleSelect'); + const refreshSpy = jest.spyOn(editor.msInstance!, 'refreshOptions'); editor.changeEditorOption('filter', true); - expect(multipleSelectSpy).toHaveBeenCalledWith('refreshOptions', { ...editor.editorElmOptions, filter: true }); + expect(refreshSpy).toHaveBeenCalledWith({ ...editor.editorElmOptions, filter: true }); }); }); describe('isValueTouched method', () => { it('should return True after triggering an Check All event', () => { - editor = new SelectEditor(editorArguments, true); + editor = new SelectEditor(editorArguments, true, 0); editor.reset(); const selectAllBtnElm = divContainer.querySelector('.ms-select-all') as HTMLButtonElement; const editorBtnElm = divContainer.querySelector('.ms-parent.ms-filter.editor-gender button.ms-choice') as HTMLButtonElement; const editorListElm = divContainer.querySelectorAll(`.ms-drop ul>li input[type=checkbox]`); - const editorOkElm = divContainer.querySelector(`[name=editor-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const editorOkElm = divContainer.querySelector(`[data-name=editor-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; editorBtnElm.click(); // we can use property "checked" or dispatch an event @@ -331,19 +335,19 @@ describe('SelectEditor', () => { selectAllBtnElm.dispatchEvent(new CustomEvent('onCheckAll')); selectAllBtnElm.click(); editorOkElm.click(); - editor.editorDomElement.multipleSelect('checkAll'); + editor.msInstance?.checkAll(); expect(editorListElm.length).toBe(3); expect(editor.isValueTouched()).toBe(true); }); it('should return True after triggering an UnCheck All event', () => { - editor = new SelectEditor(editorArguments, true); + editor = new SelectEditor(editorArguments, true, 0); editor.reset(); const selectAllBtnElm = divContainer.querySelector('.ms-select-all') as HTMLButtonElement; const editorBtnElm = divContainer.querySelector('.ms-parent.ms-filter.editor-gender button.ms-choice') as HTMLButtonElement; const editorListElm = divContainer.querySelectorAll(`.ms-drop ul>li input[type=checkbox]`); - const editorOkElm = divContainer.querySelector(`[name=editor-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const editorOkElm = divContainer.querySelector(`[data-name=editor-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; editorBtnElm.click(); // we can use property "checked" or dispatch an event @@ -351,7 +355,7 @@ describe('SelectEditor', () => { selectAllBtnElm.dispatchEvent(new CustomEvent('onCheckAll')); selectAllBtnElm.click(); editorOkElm.click(); - editor.editorDomElement.multipleSelect('uncheckAll'); + editor.msInstance?.uncheckAll(); expect(editorListElm.length).toBe(3); expect(editor.isValueTouched()).toBe(true); @@ -481,10 +485,33 @@ describe('SelectEditor', () => { editor = new SelectEditor(editorArguments, true); editor.loadValue(mockItemData); const output = editor.serializeValue(); - const currentValue = editor.currentValue; expect(output).toEqual([{ label: 'male', value: 'male' }, { label: 'other', value: 'other' }]); - expect(currentValue).toEqual({}); + expect(editor.currentValues).toEqual([{ label: 'male', value: 'male' }, { label: 'other', value: 'other' }]); + }); + + it('should return all object values when using a dot (.) notation for complex object with a collection of option/label pair and using "serializeComplexValueFormat" as "object"', () => { + mockColumn.field = 'employee.gender'; + mockItemData = { id: 1, employee: { id: 24, gender: ['male', 'other'] }, isActive: true }; + (mockColumn.internalColumnEditor as ColumnEditor).serializeComplexValueFormat = 'object'; + editor = new SelectEditor(editorArguments, true); + editor.loadValue(mockItemData); + const output = editor.serializeValue(); + + expect(output).toEqual([{ label: 'male', value: 'male' }, { label: 'other', value: 'other' }]); + expect(editor.currentValues).toEqual([{ label: 'male', value: 'male' }, { label: 'other', value: 'other' }]); + }); + + it('should return a single object value when using a dot (.) notation for complex object with a collection of option/label pair and using "serializeComplexValueFormat" as "object"', () => { + mockColumn.field = 'employee.gender'; + mockItemData = { id: 1, employee: { id: 24, gender: 'male' }, isActive: true }; + (mockColumn.internalColumnEditor as ColumnEditor).serializeComplexValueFormat = 'object'; + editor = new SelectEditor(editorArguments, false); + editor.loadValue(mockItemData); + const output = editor.serializeValue(); + + expect(output).toEqual({ label: 'male', value: 'male' }); + expect(editor.currentValue).toEqual({ label: 'male', value: 'male' }); }); it('should return flat value when using a dot (.) notation for complex object with a collection of option/label pair and using "serializeComplexValueFormat" as "flat"', () => { @@ -494,10 +521,9 @@ describe('SelectEditor', () => { editor = new SelectEditor(editorArguments, true); editor.loadValue(mockItemData); const output = editor.serializeValue(); - const currentValue = editor.currentValue; expect(output).toEqual(['male', 'other']); - expect(currentValue).toEqual(''); + expect(editor.currentValues).toEqual(['male', 'other']); }); it('should return object value when using a dot (.) notation and we override the object path using "complexObjectPath" to find correct values', () => { @@ -554,7 +580,7 @@ describe('SelectEditor', () => { const saveSpy = jest.spyOn(editor, 'save'); editor.loadValue(mockItemData); - editor.editorDomElement.multipleSelect('close'); + editor.msInstance?.close(); editor.destroy(); expect(saveSpy).toHaveBeenCalledTimes(1); @@ -569,7 +595,7 @@ describe('SelectEditor', () => { const saveSpy = jest.spyOn(editor, 'save'); editor.loadValue(mockItemData); - editor.editorDomElement.multipleSelect('close'); + editor.msInstance?.close(); editor.destroy(); expect(saveSpy).toHaveBeenCalledTimes(1); @@ -611,10 +637,10 @@ describe('SelectEditor', () => { it('should create the multi-select editor with a default search term when passed as a filter argument even with collection an array of strings', () => { (mockColumn.internalColumnEditor as ColumnEditor).collection = ['male', 'female']; - editor = new SelectEditor(editorArguments, true); + editor = new SelectEditor(editorArguments, true, 0); const editorBtnElm = divContainer.querySelector('.ms-parent.ms-filter.editor-gender button.ms-choice') as HTMLButtonElement; - const editorListElm = divContainer.querySelectorAll(`[name=editor-gender].ms-drop ul>li input[type=checkbox]`); - const editorOkElm = divContainer.querySelector(`[name=editor-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const editorListElm = divContainer.querySelectorAll(`[data-name=editor-gender].ms-drop ul>li input[type=checkbox]`); + const editorOkElm = divContainer.querySelector(`[data-name=editor-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; editorBtnElm.click(); editorOkElm.click(); @@ -634,9 +660,9 @@ describe('SelectEditor', () => { } }; - editor = new SelectEditor(editorArguments, true); + editor = new SelectEditor(editorArguments, true, 0); const editorBtnElm = divContainer.querySelector('.ms-parent.ms-filter.editor-gender button.ms-choice') as HTMLButtonElement; - const editorListElm = divContainer.querySelectorAll(`[name=editor-gender].ms-drop ul>li input[type=checkbox]`); + const editorListElm = divContainer.querySelectorAll(`[data-name=editor-gender].ms-drop ul>li input[type=checkbox]`); editorBtnElm.click(); expect(editorListElm.length).toBe(3); @@ -659,9 +685,9 @@ describe('SelectEditor', () => { }, }; - editor = new SelectEditor(editorArguments, true); + editor = new SelectEditor(editorArguments, true, 0); const editorBtnElm = divContainer.querySelector('.ms-parent.ms-filter.editor-gender button.ms-choice') as HTMLButtonElement; - const editorListElm = divContainer.querySelectorAll(`[name=editor-gender].ms-drop ul>li input[type=checkbox]`); + const editorListElm = divContainer.querySelectorAll(`[data-name=editor-gender].ms-drop ul>li input[type=checkbox]`); editorBtnElm.click(); expect(editorListElm.length).toBe(3); @@ -681,9 +707,9 @@ describe('SelectEditor', () => { } }; - editor = new SelectEditor(editorArguments, true); + editor = new SelectEditor(editorArguments, true, 0); const editorBtnElm = divContainer.querySelector('.ms-parent.ms-filter.editor-gender button.ms-choice') as HTMLButtonElement; - const editorListElm = divContainer.querySelectorAll(`[name=editor-gender].ms-drop ul>li input[type=checkbox]`); + const editorListElm = divContainer.querySelectorAll(`[data-name=editor-gender].ms-drop ul>li input[type=checkbox]`); editorBtnElm.click(); expect(editorListElm.length).toBe(1); @@ -703,9 +729,9 @@ describe('SelectEditor', () => { }, }; - editor = new SelectEditor(editorArguments, true); + editor = new SelectEditor(editorArguments, true, 0); const editorBtnElm = divContainer.querySelector('.ms-parent.ms-filter.editor-gender button.ms-choice') as HTMLButtonElement; - const editorListElm = divContainer.querySelectorAll(`[name=editor-gender].ms-drop ul>li input[type=checkbox]`); + const editorListElm = divContainer.querySelectorAll(`[data-name=editor-gender].ms-drop ul>li input[type=checkbox]`); editorBtnElm.click(); expect(editorListElm.length).toBe(1); @@ -728,9 +754,9 @@ describe('SelectEditor', () => { }, }; - editor = new SelectEditor(editorArguments, true); + editor = new SelectEditor(editorArguments, true, 0); const editorBtnElm = divContainer.querySelector('.ms-parent.ms-filter.editor-gender button.ms-choice') as HTMLButtonElement; - const editorListElm = divContainer.querySelectorAll(`[name=editor-gender].ms-drop ul>li input[type=checkbox]`); + const editorListElm = divContainer.querySelectorAll(`[data-name=editor-gender].ms-drop ul>li input[type=checkbox]`); editorBtnElm.click(); expect(editorListElm.length).toBe(2); @@ -746,9 +772,9 @@ describe('SelectEditor', () => { collectionOverride: (inputCollection) => inputCollection.filter(item => item !== 'other') }; - editor = new SelectEditor(editorArguments, true); + editor = new SelectEditor(editorArguments, true, 0); const editorBtnElm = divContainer.querySelector('.ms-parent.ms-filter.editor-gender button.ms-choice') as HTMLButtonElement; - const editorListElm = divContainer.querySelectorAll(`[name=editor-gender].ms-drop ul>li input[type=checkbox]`); + const editorListElm = divContainer.querySelectorAll(`[data-name=editor-gender].ms-drop ul>li input[type=checkbox]`); editorBtnElm.click(); expect(editorListElm.length).toBe(2); @@ -770,9 +796,9 @@ describe('SelectEditor', () => { }, }; - editor = new SelectEditor(editorArguments, true); + editor = new SelectEditor(editorArguments, true, 0); const editorBtnElm = divContainer.querySelector('.ms-parent.ms-filter.editor-gender button.ms-choice') as HTMLButtonElement; - const editorListElm = divContainer.querySelectorAll(`[name=editor-gender].ms-drop ul>li input[type=checkbox]`); + const editorListElm = divContainer.querySelectorAll(`[data-name=editor-gender].ms-drop ul>li input[type=checkbox]`); editorBtnElm.click(); expect(editorListElm.length).toBe(3); @@ -794,9 +820,9 @@ describe('SelectEditor', () => { }, }; - editor = new SelectEditor(editorArguments, true); + editor = new SelectEditor(editorArguments, true, 0); const editorBtnElm = divContainer.querySelector('.ms-parent.ms-filter.editor-gender button.ms-choice') as HTMLButtonElement; - const editorListElm = divContainer.querySelectorAll(`[name=editor-gender].ms-drop ul>li span`); + const editorListElm = divContainer.querySelectorAll(`[data-name=editor-gender].ms-drop ul>li span`); editorBtnElm.click(); expect(editorListElm.length).toBe(2); @@ -819,11 +845,11 @@ describe('SelectEditor', () => { }; mockItemData = { id: 1, gender: 'male', isEffort: false }; - editor = new SelectEditor(editorArguments, true); + editor = new SelectEditor(editorArguments, true, 0); editor.loadValue(mockItemData); editor.setValue([false]); const editorBtnElm = divContainer.querySelector('.ms-parent.ms-filter.editor-gender button.ms-choice') as HTMLButtonElement; - const editorListElm = divContainer.querySelectorAll(`[name=editor-gender].ms-drop ul>li span`); + const editorListElm = divContainer.querySelectorAll(`[data-name=editor-gender].ms-drop ul>li span`); editorBtnElm.click(); expect(editor.getValue()).toEqual(['']); @@ -848,11 +874,11 @@ describe('SelectEditor', () => { mockItemData = { id: 1, gender: 'male', isEffort: false }; gridOptionMock.sanitizer = (dirtyHtml) => dirtyHtml.replace(/( : true`); expect(editorListElm.length).toBe(2); expect(editorListElm[0].innerHTML).toBe(' : True'); diff --git a/packages/common/src/editors/__tests__/sliderEditor.spec.ts b/packages/common/src/editors/__tests__/sliderEditor.spec.ts index 11544aa95..24eee5583 100644 --- a/packages/common/src/editors/__tests__/sliderEditor.spec.ts +++ b/packages/common/src/editors/__tests__/sliderEditor.spec.ts @@ -479,7 +479,7 @@ describe('SliderEditor', () => { const editorElm = divContainer.querySelector('.slider-editor input.editor-price') as HTMLInputElement; editorElm.dispatchEvent(new Event('change')); - expect(consoleSpy).toHaveBeenCalledWith('[Slickgrid-Universal] All editor.params are moving to "editorOptions" for better typing support and "params" will be deprecated in future release.'); + expect(consoleSpy).toHaveBeenCalledWith('[Slickgrid-Universal] All editor.params from Slider Editor are moving to "editorOptions" for better typing support and "params" will be deprecated in future release.'); }); it('should enableSliderTrackColoring and trigger a change event and expect slider track to have background color', () => { @@ -531,7 +531,9 @@ describe('SliderEditor', () => { it('should call "setValue" with value & apply value flag and expect the DOM element to have same value and also expect the value to be applied to the item object', () => { const activeCellMock = { row: 0, cell: 0 }; jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); editor = new SliderEditor(editorArguments); editor.setValue(95, true); @@ -545,7 +547,9 @@ describe('SliderEditor', () => { it('should call "show" and expect the DOM element to not be disabled when "onBeforeEditCell" is NOT returning false', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => undefined + }); editor = new SliderEditor(editorArguments); const disableSpy = jest.spyOn(editor, 'disable'); @@ -559,8 +563,12 @@ describe('SliderEditor', () => { it('should call "show" and expect the DOM element to become disabled with empty value set in the form values when "onBeforeEditCell" returns false', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(false); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => false + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); editor = new SliderEditor(editorArguments); editor.loadValue(mockItemData); @@ -581,8 +589,12 @@ describe('SliderEditor', () => { it('should call "show" and expect the DOM element to become disabled and empty when "onBeforeEditCell" returns false', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(false); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => false + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.compositeEditorOptions = { excludeDisabledFieldFormValues: true }; @@ -606,8 +618,12 @@ describe('SliderEditor', () => { it('should expect "onCompositeEditorChange" to have been triggered with the new value showing up in its "formValues" object', () => { const activeCellMock = { row: 0, cell: 0 }; const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); - const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); - const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); + const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue({ + getReturnValue: () => undefined + }); + const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue({ + getReturnValue: () => false + }); gridOptionMock.autoCommitEdit = true; mockItemData = { id: 1, price: 93, isActive: true }; diff --git a/packages/common/src/editors/autocompleterEditor.ts b/packages/common/src/editors/autocompleterEditor.ts index 3e130ec39..66f8d6a53 100644 --- a/packages/common/src/editors/autocompleterEditor.ts +++ b/packages/common/src/editors/autocompleterEditor.ts @@ -2,7 +2,7 @@ import * as autocompleter_ from 'autocompleter'; const autocomplete = (autocompleter_ && autocompleter_['default'] || autocompleter_) as (settings: AutocompleteSettings) => AutocompleteResult; // patch for rollup import type { AutocompleteItem, AutocompleteResult, AutocompleteSettings } from 'autocompleter'; -import { isObject, isPrimmitive, setDeepValue, toKebabCase } from '@slickgrid-universal/utils'; +import { isObject, isPrimitiveValue, setDeepValue, toKebabCase } from '@slickgrid-universal/utils'; import { Constants } from './../constants'; import { FieldType, KeyCode, } from '../enums/index'; @@ -435,7 +435,7 @@ export class AutocompleterEditor implements Ed const activeCell = this.grid.getActiveCell(); const isCellEditable = this.grid.onBeforeEditCell.notify({ ...activeCell, item: this.dataContext, column: this.args.column, grid: this.grid, target: 'composite', compositeEditorOptions: this.args.compositeEditorOptions - }); + }).getReturnValue(); this.disable(isCellEditable === false); } @@ -552,7 +552,7 @@ export class AutocompleterEditor implements Ed this._editorInputGroupElm.appendChild(document.createElement('span')); // show clear date button (unless user specifically doesn't want it) - if (!getEditorOptionByName(this.columnEditor, 'hideClearButton')) { + if (!getEditorOptionByName(this.columnEditor, 'hideClearButton', undefined, 'autocomplete')) { closeButtonGroupElm.appendChild(this._clearButtonElm); this._editorInputGroupElm.appendChild(closeButtonGroupElm); this._bindEventService.bind(this._clearButtonElm, 'click', () => this.clear()); @@ -594,7 +594,7 @@ export class AutocompleterEditor implements Ed // the kradeen autocomplete lib only works with label/value pair, make sure that our array is in accordance if (Array.isArray(finalCollection)) { - if (this.collection.every(x => isPrimmitive(x))) { + if (this.collection.every(x => isPrimitiveValue(x))) { // when detecting an array of primitives, we have to remap it to an array of value/pair objects finalCollection = finalCollection.map(c => ({ label: c, value: c })); } else { diff --git a/packages/common/src/editors/checkboxEditor.ts b/packages/common/src/editors/checkboxEditor.ts index be6269c3c..adeb3cd9f 100644 --- a/packages/common/src/editors/checkboxEditor.ts +++ b/packages/common/src/editors/checkboxEditor.ts @@ -299,7 +299,7 @@ export class CheckboxEditor implements Editor { const activeCell = this.grid.getActiveCell(); const isCellEditable = this.grid.onBeforeEditCell.notify({ ...activeCell, item: this.dataContext, column: this.args.column, grid: this.grid, target: 'composite', compositeEditorOptions: this.args.compositeEditorOptions - }); + }).getReturnValue(); this.disable(isCellEditable === false); } diff --git a/packages/common/src/editors/dateEditor.ts b/packages/common/src/editors/dateEditor.ts index 5a85c44f3..fccdebdf5 100644 --- a/packages/common/src/editors/dateEditor.ts +++ b/packages/common/src/editors/dateEditor.ts @@ -165,7 +165,7 @@ export class DateEditor implements Editor { ); // show clear date button (unless user specifically doesn't want it) - if (!getEditorOptionByName(this.columnEditor, 'hideClearButton')) { + if (!getEditorOptionByName(this.columnEditor, 'hideClearButton', undefined, 'date')) { closeButtonGroupElm.appendChild(this._clearButtonElm); this._editorInputGroupElm.appendChild(closeButtonGroupElm); this._bindEventService.bind(this._clearButtonElm, 'click', () => this._lastTriggeredByClearDate = true); @@ -426,7 +426,7 @@ export class DateEditor implements Editor { const activeCell = this.grid.getActiveCell(); const isCellEditable = this.grid.onBeforeEditCell.notify({ ...activeCell, item: this.dataContext, column: this.args.column, grid: this.grid, target: 'composite', compositeEditorOptions: this.args.compositeEditorOptions - }); + }).getReturnValue(); this.disable(isCellEditable === false); } diff --git a/packages/common/src/editors/dualInputEditor.ts b/packages/common/src/editors/dualInputEditor.ts index d9094b950..d9e5c33fc 100644 --- a/packages/common/src/editors/dualInputEditor.ts +++ b/packages/common/src/editors/dualInputEditor.ts @@ -517,7 +517,7 @@ export class DualInputEditor implements Editor { const activeCell = this.grid.getActiveCell(); const isCellEditable = this.grid.onBeforeEditCell.notify({ ...activeCell, item: this.dataContext, column: this.args.column, grid: this.grid, target: 'composite', compositeEditorOptions: this.args.compositeEditorOptions - }); + }).getReturnValue(); this.disable(isCellEditable === false); } diff --git a/packages/common/src/editors/editorUtilities.ts b/packages/common/src/editors/editorUtilities.ts index 316eca21f..2d40fcd58 100644 --- a/packages/common/src/editors/editorUtilities.ts +++ b/packages/common/src/editors/editorUtilities.ts @@ -5,13 +5,13 @@ import type { ColumnEditor } from '../interfaces/index'; * Get option from editor.params PR editor.editorOptions * @deprecated this should be removed when slider editorParams are replaced by editorOptions */ -export function getEditorOptionByName(columnEditor: ColumnEditor, optionName: K, defaultValue?: any): T[K] | undefined { +export function getEditorOptionByName(columnEditor: ColumnEditor, optionName: K, defaultValue?: any, editorName = 'Slider'): T[K] | undefined { let outValue; if (columnEditor.editorOptions?.[optionName] !== undefined) { outValue = (columnEditor.editorOptions as T)[optionName]; } else if (columnEditor?.params?.[optionName] !== undefined) { - console.warn(`[Slickgrid-Universal] All editor.params are moving to "editorOptions" for better typing support and "params" will be deprecated in future release.`); + console.warn(`[Slickgrid-Universal] All editor.params from ${editorName} Editor are moving to "editorOptions" for better typing support and "params" will be deprecated in future release.`); outValue = columnEditor?.params?.[optionName]; } return outValue ?? defaultValue; diff --git a/packages/common/src/editors/inputEditor.ts b/packages/common/src/editors/inputEditor.ts index 78fc18fe2..6f369ea40 100644 --- a/packages/common/src/editors/inputEditor.ts +++ b/packages/common/src/editors/inputEditor.ts @@ -310,7 +310,7 @@ export class InputEditor implements Editor { const activeCell = this.grid.getActiveCell(); const isCellEditable = this.grid.onBeforeEditCell.notify({ ...activeCell, item: this.dataContext, column: this.args.column, grid: this.grid, target: 'composite', compositeEditorOptions: this.args.compositeEditorOptions - }); + }).getReturnValue(); this.disable(isCellEditable === false); } diff --git a/packages/common/src/editors/longTextEditor.ts b/packages/common/src/editors/longTextEditor.ts index df9f5dace..d418be899 100644 --- a/packages/common/src/editors/longTextEditor.ts +++ b/packages/common/src/editors/longTextEditor.ts @@ -407,7 +407,7 @@ export class LongTextEditor implements Editor { const activeCell = this.grid.getActiveCell(); const isCellEditable = this.grid.onBeforeEditCell.notify({ ...activeCell, item: this.dataContext, column: this.args.column, grid: this.grid, target: 'composite', compositeEditorOptions: this.args.compositeEditorOptions - }); + }).getReturnValue(); this.disable(isCellEditable === false); } diff --git a/packages/common/src/editors/multipleSelectEditor.ts b/packages/common/src/editors/multipleSelectEditor.ts index 8da99fd15..60514e054 100644 --- a/packages/common/src/editors/multipleSelectEditor.ts +++ b/packages/common/src/editors/multipleSelectEditor.ts @@ -5,7 +5,7 @@ export class MultipleSelectEditor extends SelectEditor { /** * Initialize the Editor */ - constructor(protected readonly args: EditorArguments) { - super(args, true); + constructor(protected readonly args: EditorArguments, public delayOpening = 0) { + super(args, true, delayOpening); } } diff --git a/packages/common/src/editors/selectEditor.ts b/packages/common/src/editors/selectEditor.ts index e98d1edf3..2a33ad921 100644 --- a/packages/common/src/editors/selectEditor.ts +++ b/packages/common/src/editors/selectEditor.ts @@ -1,3 +1,4 @@ +import { multipleSelect, MultipleSelectInstance, MultipleSelectOption, OptionRowData } from 'multiple-select-vanilla'; import { setDeepValue } from '@slickgrid-universal/utils'; import { dequal } from 'dequal/lite'; @@ -16,12 +17,11 @@ import type { EditorValidationResult, GridOption, Locale, - MultipleSelectOption, SelectOption, SlickGrid, SlickNamespace, } from './../interfaces/index'; -import { buildSelectEditorOrFilterDomElement, CollectionService, findOrDefault, type TranslaterService } from '../services/index'; +import { buildMultipleSelectDataCollection, CollectionService, emptyElement, findOrDefault, sanitizeTextByAvailableSanitizer, type TranslaterService } from '../services/index'; import { getDescendantProperty, getTranslationPrefix, } from '../services/utilities'; // using external non-typed js libraries @@ -46,20 +46,22 @@ export class SelectEditor implements Editor { /** The translate library */ protected _translaterService?: TranslaterService; - /** The JQuery DOM element */ - $editorElm: any; + protected _msInstance?: MultipleSelectInstance; + + /** Editor DOM element */ + editorElm?: HTMLElement; /** is the Editor disabled? */ disabled = false; /** Editor Multiple-Select options */ - editorElmOptions!: MultipleSelectOption; + editorElmOptions!: Partial; /** DOM Element Name, useful for auto-detecting positioning (dropup / dropdown) */ elementName: string; /** The multiple-select options for a multiple select list */ - defaultOptions: MultipleSelectOption; + defaultOptions: Partial; /** The original item values that are set at the beginning */ originalValue: any | any[]; @@ -91,7 +93,7 @@ export class SelectEditor implements Editor { /** Final collection displayed in the UI, that is after processing filter/sort/override */ finalCollection: any[] = []; - constructor(protected readonly args: EditorArguments, protected readonly isMultipleSelect: boolean) { + constructor(protected readonly args: EditorArguments, protected readonly isMultipleSelect: boolean, public delayOpening = -1) { if (!args) { throw new Error('[Slickgrid-Universal] Something is wrong with this grid, an Editor must always have valid arguments.'); } @@ -109,20 +111,18 @@ export class SelectEditor implements Editor { this.elementName = `editor-${columnId}`; const compositeEditorOptions = this.args.compositeEditorOptions; - const libOptions: MultipleSelectOption = { + const libOptions = { autoAdjustDropHeight: true, autoAdjustDropPosition: true, autoAdjustDropWidthByTextSize: true, container: 'body', filter: false, maxHeight: 275, + minHeight: 25, name: this.elementName, single: true, - textTemplate: ($elm) => { - // render HTML code or not, by default it is sanitized and won't be rendered - const isRenderHtmlEnabled = this.columnEditor?.enableRenderHtml ?? false; - return isRenderHtmlEnabled ? $elm.text() : $elm.html(); - }, + useSelectOptionLabelToHtml: this.columnEditor?.enableRenderHtml ?? false, + sanitizer: (dirtyHtml: string) => sanitizeTextByAvailableSanitizer(this.gridOptions, dirtyHtml), onClick: () => this._isValueTouched = true, onCheckAll: () => this._isValueTouched = true, onUncheckAll: () => this._isValueTouched = true, @@ -134,25 +134,26 @@ export class SelectEditor implements Editor { this.save(this.hasAutoCommitEdit); } }, - }; + } as Partial; if (isMultipleSelect) { libOptions.single = false; - libOptions.addTitle = true; - libOptions.okButton = true; - libOptions.selectAllDelimiter = ['', '']; + libOptions.displayTitle = true; + libOptions.showOkButton = true; - if (this._translaterService && this._translaterService.getCurrentLanguage && this._translaterService.getCurrentLanguage()) { + if (this._translaterService?.getCurrentLanguage()) { const translationPrefix = getTranslationPrefix(this.gridOptions); - libOptions.countSelected = this._translaterService.translate(`${translationPrefix}X_OF_Y_SELECTED`); - libOptions.allSelected = this._translaterService.translate(`${translationPrefix}ALL_SELECTED`); + libOptions.countSelectedText = this._translaterService.translate(`${translationPrefix}X_OF_Y_SELECTED`); + libOptions.allSelectedText = this._translaterService.translate(`${translationPrefix}ALL_SELECTED`); libOptions.selectAllText = this._translaterService.translate(`${translationPrefix}SELECT_ALL`); libOptions.okButtonText = this._translaterService.translate(`${translationPrefix}OK`); + libOptions.noMatchesFoundText = this._translaterService.translate(`${translationPrefix}NO_MATCHES_FOUND`); } else { - libOptions.countSelected = this._locales?.TEXT_X_OF_Y_SELECTED; - libOptions.allSelected = this._locales?.TEXT_ALL_SELECTED; + libOptions.countSelectedText = this._locales?.TEXT_X_OF_Y_SELECTED; + libOptions.allSelectedText = this._locales?.TEXT_ALL_SELECTED; libOptions.selectAllText = this._locales?.TEXT_SELECT_ALL; libOptions.okButtonText = this._locales?.TEXT_OK; + libOptions.noMatchesFoundText = this._locales?.TEXT_NO_MATCHES_FOUND; } } @@ -187,8 +188,8 @@ export class SelectEditor implements Editor { } /** Getter for the Editor DOM Element */ - get editorDomElement(): any { - return this.$editorElm; + get editorDomElement() { + return this.editorElm; } get isCompositeEditor(): boolean { @@ -204,15 +205,19 @@ export class SelectEditor implements Editor { return this.gridOptions.autoCommitEdit ?? false; } + get msInstance() { + return this._msInstance; + } + /** * The current selected values (multiple select) from the collection */ get currentValues(): any[] | null { - const elmValue = this.$editorElm?.val(); + const selectedValues = this._msInstance?.getSelects() ?? []; // collection of strings, just return the filtered string that are equals - if (this.collection.every(x => typeof x === 'string')) { - return this.collection.filter(c => elmValue.indexOf(c?.toString()) !== -1); + if (this.collection.every(x => typeof x === 'number' || typeof x === 'string')) { + return this.collection.filter(c => selectedValues?.some(val => `${val}` === c?.toString())); } // collection of label/value pair @@ -220,7 +225,7 @@ export class SelectEditor implements Editor { const isIncludingPrefixSuffix = this.collectionOptions?.includePrefixSuffixToSelectedValues ?? false; return this.collection - .filter(c => elmValue.indexOf(c.hasOwnProperty(this.valueName) && c[this.valueName]?.toString()) !== -1) + .filter(c => selectedValues.some(val => `${val}` === c?.[this.valueName]?.toString())) .map(c => { const labelText = c[this.valueName]; let prefixText = c[this.labelPrefixName] || ''; @@ -253,19 +258,20 @@ export class SelectEditor implements Editor { * The current selected values (single select) from the collection */ get currentValue(): number | string { - const elmValue = this.$editorElm.val(); - const fieldName = this.columnDef && this.columnDef.field; + const selectedValues = this._msInstance?.getSelects() ?? []; + const selectedValue = selectedValues.length ? selectedValues[0] : ''; + const fieldName = this.columnDef?.field; if (fieldName !== undefined) { // collection of strings, just return the filtered string that are equals - if (this.collection.every(x => typeof x === 'string')) { - return findOrDefault(this.collection, (c: any) => c?.toString?.() === elmValue); + if (this.collection.every(x => typeof x === 'number' || typeof x === 'string')) { + return findOrDefault(this.collection, (c: any) => c?.toString?.() === `${selectedValue}`); } // collection of label/value pair const separatorBetweenLabels = this.collectionOptions?.separatorBetweenTextLabels ?? ''; const isIncludingPrefixSuffix = this.collectionOptions?.includePrefixSuffixToSelectedValues ?? false; - const itemFound = findOrDefault(this.collection, (c: any) => c.hasOwnProperty(this.valueName) && c[this.valueName]?.toString() === elmValue); + const itemFound = findOrDefault(this.collection, (c: any) => c.hasOwnProperty(this.valueName) && c[this.valueName]?.toString() === `${selectedValue}`); // is the field a complex object, "address.streetNumber" const isComplexObject = fieldName?.indexOf('.') > 0; @@ -352,14 +358,14 @@ export class SelectEditor implements Editor { } hide() { - if (this.$editorElm && typeof this.$editorElm.multipleSelect === 'function') { - this.$editorElm.multipleSelect('close'); + if (this._msInstance) { + this._msInstance.close(); } } - show() { - if (!this.isCompositeEditor && this.$editorElm && typeof this.$editorElm.multipleSelect === 'function') { - this.$editorElm.multipleSelect('open'); + show(openDelay?: number | null) { + if (!this.isCompositeEditor && this._msInstance) { + this._msInstance.open(openDelay); } else if (this.isCompositeEditor) { // when it's a Composite Editor, we'll check if the Editor is editable (by checking onBeforeEditCell) and if not Editable we'll disable the Editor this.applyInputUsabilityState(); @@ -367,8 +373,8 @@ export class SelectEditor implements Editor { } applyValue(item: any, state: any): void { - const fieldName = this.columnDef && this.columnDef.field; - const fieldType = this.columnDef && this.columnDef.type; + const fieldName = this.columnDef?.field; + const fieldType = this.columnDef?.type; let newValue = state; if (fieldName !== undefined) { @@ -405,24 +411,19 @@ export class SelectEditor implements Editor { destroy() { // when autoCommitEdit is enabled, we might end up leaving an editor without it being saved, if so do call a save before destroying // this mainly happens doing a blur or focusing on another cell in the grid (it won't come here if we click outside the grid, in the body) - if (this.$editorElm && this.hasAutoCommitEdit && this.isValueChanged() && !this._isDisposingOrCallingSave && !this.isCompositeEditor) { + if (this._msInstance && this.hasAutoCommitEdit && this.isValueChanged() && !this._isDisposingOrCallingSave && !this.isCompositeEditor) { this._isDisposingOrCallingSave = true; // change destroying flag to avoid infinite loop this.save(true); } this._isDisposingOrCallingSave = true; - if (this.$editorElm && typeof this.$editorElm.multipleSelect === 'function') { - this.$editorElm.multipleSelect('destroy'); - this.$editorElm.remove(); - const elementClassName = this.elementName.toString().replace('.', '\\.'); // make sure to escape any dot "." from CSS class to avoid console error - $(`[name=${elementClassName}].ms-drop`).remove(); - this.$editorElm.remove(); - this.$editorElm = null; - } + this._msInstance?.destroy(); + this.editorElm?.remove(); + this._msInstance = undefined; } loadValue(item: any): void { - const fieldName = this.columnDef && this.columnDef.field; + const fieldName = this.columnDef?.field; // is the field a complex object, "address.streetNumber" const isComplexObject = fieldName !== undefined && fieldName?.indexOf('.') > 0; @@ -439,7 +440,6 @@ export class SelectEditor implements Editor { } else { this.loadSingleValue(value); } - this.refresh(); } } @@ -447,8 +447,8 @@ export class SelectEditor implements Editor { // convert to string because that is how the DOM will return these values if (Array.isArray(currentValues)) { // keep the default values in memory for references - this.originalValue = currentValues.map((i: any) => i); - this.$editorElm.multipleSelect('setSelects', currentValues); + this.originalValue = currentValues.map((i: any) => (typeof i === 'number' || typeof i === 'boolean') ? `${i}` : i); + this._msInstance?.setSelects(this.originalValue); // if it's set by a Composite Editor, then also trigger a change for it const compositeEditorOptions = this.args.compositeEditorOptions; @@ -460,9 +460,8 @@ export class SelectEditor implements Editor { loadSingleValue(currentValue: any) { // keep the default value in memory for references - this.originalValue = typeof currentValue === 'number' ? `${currentValue}` : currentValue; - this.$editorElm.val(currentValue); - this.$editorElm.multipleSelect('setSelects', [currentValue]); + this.originalValue = (typeof currentValue === 'number' || typeof currentValue === 'boolean') ? `${currentValue}` : currentValue; + this._msInstance?.setSelects([this.originalValue]); } serializeValue(): any | any[] { @@ -482,7 +481,7 @@ export class SelectEditor implements Editor { } this.columnEditor.editorOptions[optionName] = newValue; this.editorElmOptions = { ...this.editorElmOptions, [optionName]: newValue }; - this.$editorElm.multipleSelect('refreshOptions', this.editorElmOptions); + this._msInstance?.refreshOptions(this.editorElmOptions); } } @@ -490,9 +489,9 @@ export class SelectEditor implements Editor { const prevIsDisabled = this.disabled; this.disabled = isDisabled; - if (this.$editorElm) { + if (this._msInstance) { if (isDisabled) { - this.$editorElm.multipleSelect('disable'); + this._msInstance.disable(); // clear select when it's newly disabled and not yet empty const currentValues: string | number | Array = this.getValue(); @@ -501,7 +500,7 @@ export class SelectEditor implements Editor { this.reset('', true, true); } } else { - this.$editorElm.multipleSelect('enable'); + this._msInstance.enable(); } } } @@ -509,14 +508,11 @@ export class SelectEditor implements Editor { focus() { // always set focus on grid first so that plugin to copy range (SlickCellExternalCopyManager) would still be able to paste at that position this.grid.focus(); - - if (this.$editorElm && this.$editorElm.multipleSelect) { - this.$editorElm.multipleSelect('focus'); - } + this._msInstance?.focus(); } isValueChanged(): boolean { - const valueSelection = this.$editorElm?.multipleSelect('getSelects'); + const valueSelection = this._msInstance?.getSelects(); if (this.isMultipleSelect) { const isEqual = dequal(valueSelection, this.originalValue); return !isEqual; @@ -535,10 +531,10 @@ export class SelectEditor implements Editor { */ reset(value?: string, triggerCompositeEventWhenExist = true, clearByDisableCommand = false) { const inputValue = value ?? this.originalValue; - if (this.$editorElm) { + if (this._msInstance) { this.originalValue = this.isMultipleSelect ? (inputValue !== undefined ? [inputValue] : []) : inputValue; const selection = this.originalValue === undefined ? [] : [this.originalValue]; - this.$editorElm.multipleSelect('setSelects', selection); + this._msInstance.setSelects(selection); } this._isValueTouched = false; @@ -564,7 +560,7 @@ export class SelectEditor implements Editor { validate(_targetElm?: any, inputValue?: any): EditorValidationResult { const isRequired = this.isCompositeEditor ? false : this.columnEditor?.required; - const elmValue = (inputValue !== undefined) ? inputValue : this.$editorElm && this.$editorElm.val && this.$editorElm.val(); + const elmValue = (inputValue !== undefined) ? inputValue : this._msInstance?.getSelects(); // && this.$editorElm.val && this.$editorElm.val(); const errorMsg = this.columnEditor && this.columnEditor.errorMessage; // when using Composite Editor, we also want to recheck if the field if disabled/enabled since it might change depending on other inputs on the composite form @@ -605,7 +601,7 @@ export class SelectEditor implements Editor { const activeCell = this.grid.getActiveCell(); const isCellEditable = this.grid.onBeforeEditCell.notify({ ...activeCell, item: this.dataContext, column: this.args.column, grid: this.grid, target: 'composite', compositeEditorOptions: this.args.compositeEditorOptions - }); + }).getReturnValue(); this.disable(isCellEditable === false); } @@ -701,7 +697,7 @@ export class SelectEditor implements Editor { this.finalCollection = finalCollection; // step 1, create HTML string template - const selectBuildResult = buildSelectEditorOrFilterDomElement( + const selectBuildResult = buildMultipleSelectDataCollection( 'editor', finalCollection, this.columnDef, @@ -712,7 +708,7 @@ export class SelectEditor implements Editor { // step 2, create the DOM Element of the editor // we will later also subscribe to the onClose event to save the Editor whenever that event is triggered - this.createEditorElement(selectBuildResult.selectElement); + this.createDomElement(selectBuildResult.selectElement, selectBuildResult.dataCollection); } /** Create a blank entry that can be added to the collection. It will also reuse the same collection structure provided by the user */ @@ -731,28 +727,25 @@ export class SelectEditor implements Editor { } /** - * From the Select DOM Element created earlier, create a Multiple/Single Select Editor using the jQuery multiple-select.js lib + * From the Select DOM Element created earlier, create a Multiple/Single Select Editor using the multiple-select-vanilla.js lib * @param {Object} selectElement */ - protected createEditorElement(selectElement: HTMLSelectElement) { - this.$editorElm = $(selectElement); - - if (this.$editorElm && typeof this.$editorElm.appendTo === 'function') { - $(this.args.container).empty(); - this.$editorElm.appendTo(this.args.container); + protected createDomElement(selectElement: HTMLSelectElement, dataCollection: OptionRowData[]) { + const cellContainer = this.args.container; + if (selectElement && cellContainer && typeof cellContainer.appendChild === 'function') { + emptyElement(cellContainer); + cellContainer.appendChild(selectElement); } // add placeholder when found const placeholder = this.columnEditor?.placeholder ?? ''; this.defaultOptions.placeholder = placeholder || ''; - if (typeof this.$editorElm.multipleSelect === 'function') { - const editorOptions = (this.columnDef && this.columnDef.internalColumnEditor) ? this.columnDef.internalColumnEditor.editorOptions : {}; - this.editorElmOptions = { ...this.defaultOptions, ...editorOptions }; - this.$editorElm = this.$editorElm.multipleSelect(this.editorElmOptions); - if (!this.isCompositeEditor) { - setTimeout(() => this.show()); - } + const editorOptions = this.columnDef?.internalColumnEditor?.editorOptions ?? {}; + this.editorElmOptions = { ...this.defaultOptions, ...editorOptions, data: dataCollection }; + this._msInstance = multipleSelect(selectElement, this.editorElmOptions) as MultipleSelectInstance; + if (!this.isCompositeEditor) { + this.delayOpening >= 0 ? setTimeout(() => this.show()) : this.show(); } } @@ -779,12 +772,4 @@ export class SelectEditor implements Editor { new Slick.EventData() ); } - - // refresh the jquery object because the selected checkboxes were already set - // prior to this method being called - protected refresh() { - if (typeof this.$editorElm.multipleSelect === 'function') { - this.$editorElm.multipleSelect('refresh'); - } - } } diff --git a/packages/common/src/editors/singleSelectEditor.ts b/packages/common/src/editors/singleSelectEditor.ts index 9d968b63a..4d16f3bba 100644 --- a/packages/common/src/editors/singleSelectEditor.ts +++ b/packages/common/src/editors/singleSelectEditor.ts @@ -5,7 +5,7 @@ export class SingleSelectEditor extends SelectEditor { /** * Initialize the Editor */ - constructor(protected readonly args: EditorArguments) { - super(args, false); + constructor(protected readonly args: EditorArguments, public delayOpening = 0) { + super(args, false, delayOpening); } } diff --git a/packages/common/src/editors/sliderEditor.ts b/packages/common/src/editors/sliderEditor.ts index 8fa8194e2..85b4f7835 100644 --- a/packages/common/src/editors/sliderEditor.ts +++ b/packages/common/src/editors/sliderEditor.ts @@ -360,7 +360,7 @@ export class SliderEditor implements Editor { const activeCell = this.grid.getActiveCell(); const isCellEditable = this.grid.onBeforeEditCell.notify({ ...activeCell, item: this.dataContext, column: this.args.column, grid: this.grid, target: 'composite', compositeEditorOptions: this.args.compositeEditorOptions - }); + }).getReturnValue(); this.disable(isCellEditable === false); } diff --git a/packages/common/src/extensions/__tests__/slickCellExternalCopyManager.spec.ts b/packages/common/src/extensions/__tests__/slickCellExternalCopyManager.spec.ts index 69c9ce27b..d9569049b 100644 --- a/packages/common/src/extensions/__tests__/slickCellExternalCopyManager.spec.ts +++ b/packages/common/src/extensions/__tests__/slickCellExternalCopyManager.spec.ts @@ -231,8 +231,8 @@ describe('CellExternalCopyManager', () => { expect(clearSpy).toHaveBeenCalled(); expect(mockOnCopyInit).toHaveBeenCalled(); - expect(mockOnCopyCancelled).toHaveBeenCalledWith(keyDownEscEvent, { ranges: [{ fromCell: 1, fromRow: 0, toCell: 2, toRow: 2 }] }); - expect(mockOnCopyCells).toHaveBeenCalledWith(keyDownEscEvent, { ranges: expect.toBeArray() }); + expect(mockOnCopyCancelled).toHaveBeenCalledWith(expect.any(Object), { ranges: [{ fromCell: 1, fromRow: 0, toCell: 2, toRow: 2 }] }); + expect(mockOnCopyCells).toHaveBeenCalledWith(expect.any(Object), { ranges: expect.toBeArray() }); const getActiveCellSpy = jest.spyOn(gridStub, 'getActiveCell'); const keyDownCtrlPasteEvent = new Event('keydown'); diff --git a/packages/common/src/extensions/__tests__/slickCellRangeSelector.spec.ts b/packages/common/src/extensions/__tests__/slickCellRangeSelector.spec.ts index e2464315d..0be781c94 100644 --- a/packages/common/src/extensions/__tests__/slickCellRangeSelector.spec.ts +++ b/packages/common/src/extensions/__tests__/slickCellRangeSelector.spec.ts @@ -7,7 +7,7 @@ declare const Slick: SlickNamespace; const GRID_UID = 'slickgrid_12345'; jest.mock('flatpickr', () => { }); -const addJQueryEventPropagation = function (event) { +const addVanillaEventPropagation = function (event) { Object.defineProperty(event, 'isPropagationStopped', { writable: true, configurable: true, value: jest.fn() }); Object.defineProperty(event, 'isImmediatePropagationStopped', { writable: true, configurable: true, value: jest.fn() }); return event; @@ -120,25 +120,27 @@ describe('CellRangeSelector Plugin', () => { jest.spyOn(gridStub, 'canCellBeSelected').mockReturnValue(false); jest.spyOn(gridStub, 'getCellFromPoint').mockReturnValue({ cell: 4, row: 5 }); const focusSpy = jest.spyOn(gridStub, 'focus'); - jest.spyOn(plugin.onBeforeCellRangeSelected, 'notify').mockReturnValue(true); + jest.spyOn(plugin.onBeforeCellRangeSelected, 'notify').mockReturnValue({ + getReturnValue: () => true + }); plugin.init(gridStub); const decoratorHideSpy = jest.spyOn(plugin.getCellDecorator(), 'hide'); const decoratorShowSpy = jest.spyOn(plugin.getCellDecorator(), 'show'); - const scrollEvent = addJQueryEventPropagation(new Event('scroll')); + const scrollEvent = addVanillaEventPropagation(new Event('scroll')); gridStub.onScroll.notify({ scrollTop: 10, scrollLeft: 15, grid: gridStub }, scrollEvent, gridStub); - const dragEventInit = addJQueryEventPropagation(new Event('dragInit')); + const dragEventInit = addVanillaEventPropagation(new Event('dragInit')); gridStub.onDragInit.notify({ offsetX: 6, offsetY: 7, row: 1, startX: 3, startY: 4 } as any, dragEventInit, gridStub); - const dragEventStart = addJQueryEventPropagation(new Event('dragStart')); + const dragEventStart = addVanillaEventPropagation(new Event('dragStart')); gridStub.onDragStart.notify({ offsetX: 6, offsetY: 7, row: 1, startX: 3, startY: 4 } as any, dragEventStart, gridStub); - const dragEvent = addJQueryEventPropagation(new Event('drag')); + const dragEvent = addVanillaEventPropagation(new Event('drag')); gridStub.onDrag.notify({ startX: 3, startY: 4, range: { start: { cell: 2, row: 3 }, end: { cell: 4, row: 5 } }, grid: gridStub } as any, dragEvent, gridStub); - const dragEventEnd = addJQueryEventPropagation(new Event('dragEnd')); + const dragEventEnd = addVanillaEventPropagation(new Event('dragEnd')); gridStub.onDragEnd.notify({ startX: 3, startY: 4, range: { start: { cell: 2, row: 3 }, end: { cell: 4, row: 5 } }, grid: gridStub } as any, dragEventEnd, gridStub); expect(focusSpy).not.toHaveBeenCalled(); @@ -162,21 +164,23 @@ describe('CellRangeSelector Plugin', () => { jest.spyOn(gridStub, 'getCellFromPoint').mockReturnValue({ cell: 4, row: 5 }); const focusSpy = jest.spyOn(gridStub, 'focus'); const scrollSpy = jest.spyOn(gridStub, 'scrollCellIntoView'); - jest.spyOn(plugin.onBeforeCellRangeSelected, 'notify').mockReturnValue(true); + jest.spyOn(plugin.onBeforeCellRangeSelected, 'notify').mockReturnValue({ + getReturnValue: () => true + }); plugin.init(gridStub); const decoratorShowSpy = jest.spyOn(plugin.getCellDecorator(), 'show'); - const scrollEvent = addJQueryEventPropagation(new Event('scroll')); + const scrollEvent = addVanillaEventPropagation(new Event('scroll')); gridStub.onScroll.notify({ scrollTop: 10, scrollLeft: 15, grid: gridStub }, scrollEvent, gridStub); - const dragEventInit = addJQueryEventPropagation(new Event('dragInit')); + const dragEventInit = addVanillaEventPropagation(new Event('dragInit')); gridStub.onDragInit.notify({ offsetX: 6, offsetY: 7, row: 1, startX: 3, startY: 4 } as any, dragEventInit, gridStub); - const dragEventStart = addJQueryEventPropagation(new Event('dragStart')); + const dragEventStart = addVanillaEventPropagation(new Event('dragStart')); gridStub.onDragStart.notify({ offsetX: 6, offsetY: 7, row: 1, startX: 3, startY: 4 } as any, dragEventStart, gridStub); - const dragEvent = addJQueryEventPropagation(new Event('drag')); + const dragEvent = addVanillaEventPropagation(new Event('drag')); dragEvent.pageX = 0; dragEvent.pageY = 0; gridStub.onDrag.notify({ startX: 3, startY: 4, range: { start: { cell: 2, row: 3 }, end: { cell: 4, row: 5 } }, grid: gridStub } as any, dragEvent, gridStub); @@ -203,21 +207,23 @@ describe('CellRangeSelector Plugin', () => { jest.spyOn(gridStub, 'getCellFromPoint').mockReturnValue({ cell: 4, row: 5 }); const focusSpy = jest.spyOn(gridStub, 'focus'); const scrollSpy = jest.spyOn(gridStub, 'scrollCellIntoView'); - const onBeforeCellSpy = jest.spyOn(plugin.onBeforeCellRangeSelected, 'notify').mockReturnValue(true); + const onBeforeCellSpy = jest.spyOn(plugin.onBeforeCellRangeSelected, 'notify').mockReturnValue({ + getReturnValue: () => true + }); plugin.init(gridStub); const decoratorShowSpy = jest.spyOn(plugin.getCellDecorator(), 'show'); - const scrollEvent = addJQueryEventPropagation(new Event('scroll')); + const scrollEvent = addVanillaEventPropagation(new Event('scroll')); gridStub.onScroll.notify({ scrollTop: 10, scrollLeft: 15, grid: gridStub }, scrollEvent, gridStub); - const dragEventInit = addJQueryEventPropagation(new Event('dragInit')); + const dragEventInit = addVanillaEventPropagation(new Event('dragInit')); gridStub.onDragInit.notify({ offsetX: 6, offsetY: 7, row: 1, startX: 3, startY: 4 } as any, dragEventInit, gridStub); - const dragEventStart = addJQueryEventPropagation(new Event('dragStart')); + const dragEventStart = addVanillaEventPropagation(new Event('dragStart')); gridStub.onDragStart.notify({ offsetX: 6, offsetY: 7, row: 1, startX: 3, startY: 4 } as any, dragEventStart, gridStub); - const dragEvent = addJQueryEventPropagation(new Event('drag')); + const dragEvent = addVanillaEventPropagation(new Event('drag')); dragEvent.pageX = -2; dragEvent.pageY = -156; gridStub.onDrag.notify({ startX: 3, startY: 4, range: { start: { cell: 2, row: 3 }, end: { cell: 4, row: 5 } }, grid: gridStub } as any, dragEvent, gridStub); @@ -246,26 +252,30 @@ describe('CellRangeSelector Plugin', () => { jest.spyOn(gridStub, 'canCellBeSelected').mockReturnValue(true); jest.spyOn(gridStub, 'getCellFromPoint').mockReturnValue({ cell: 4, row: 5 }); const focusSpy = jest.spyOn(gridStub, 'focus'); - const onBeforeCellRangeSpy = jest.spyOn(plugin.onBeforeCellRangeSelected, 'notify').mockReturnValue(true); - const onCellRangeSpy = jest.spyOn(plugin.onCellRangeSelected, 'notify').mockReturnValue(true); + const onBeforeCellRangeSpy = jest.spyOn(plugin.onBeforeCellRangeSelected, 'notify').mockReturnValue({ + getReturnValue: () => true + }); + const onCellRangeSpy = jest.spyOn(plugin.onCellRangeSelected, 'notify').mockReturnValue({ + getReturnValue: () => true + }); plugin.init(gridStub); const decoratorHideSpy = jest.spyOn(plugin.getCellDecorator(), 'hide'); const decoratorShowSpy = jest.spyOn(plugin.getCellDecorator(), 'show'); - const scrollEvent = addJQueryEventPropagation(new Event('scroll')); + const scrollEvent = addVanillaEventPropagation(new Event('scroll')); gridStub.onScroll.notify({ scrollTop: 10, scrollLeft: 15, grid: gridStub }, scrollEvent, gridStub); - const dragEventInit = addJQueryEventPropagation(new Event('dragInit')); + const dragEventInit = addVanillaEventPropagation(new Event('dragInit')); gridStub.onDragInit.notify({ offsetX: 6, offsetY: 7, row: 1, startX: 3, startY: 4 } as any, dragEventInit, gridStub); - const dragEventStart = addJQueryEventPropagation(new Event('dragStart')); + const dragEventStart = addVanillaEventPropagation(new Event('dragStart')); gridStub.onDragStart.notify({ offsetX: 6, offsetY: 7, row: 1, startX: 3, startY: 4 } as any, dragEventStart, gridStub); - const dragEventEnd = addJQueryEventPropagation(new Event('dragEnd')); + const dragEventEnd = addVanillaEventPropagation(new Event('dragEnd')); gridStub.onDragEnd.notify({ startX: 3, startY: 4, range: { start: { cell: 2, row: 3 }, end: { cell: 4, row: 5 } }, grid: gridStub } as any, dragEventEnd, gridStub); - const dragEvent = addJQueryEventPropagation(new Event('drag')); + const dragEvent = addVanillaEventPropagation(new Event('drag')); gridStub.onDrag.notify({ startX: 3, startY: 4, range: { start: { cell: 2, row: 3 }, end: { cell: 4, row: 5 } }, grid: gridStub } as any, dragEvent, gridStub); expect(focusSpy).toHaveBeenCalled(); @@ -300,8 +310,12 @@ describe('CellRangeSelector Plugin', () => { jest.spyOn(gridStub, 'getCellFromPoint').mockReturnValue({ cell: 4, row: 5 }); jest.spyOn(gridStub, 'getCellNodeBox').mockReturnValue({ right: 2, bottom: 3, left: 4, top: 5, height: 20, width: 33, visible: true }); const focusSpy = jest.spyOn(gridStub, 'focus'); - const onBeforeCellRangeSpy = jest.spyOn(plugin.onBeforeCellRangeSelected, 'notify').mockReturnValue(true); - const onCellRangeSpy = jest.spyOn(plugin.onCellRangeSelected, 'notify').mockReturnValue(true); + const onBeforeCellRangeSpy = jest.spyOn(plugin.onBeforeCellRangeSelected, 'notify').mockReturnValue({ + getReturnValue: () => true + }); + const onCellRangeSpy = jest.spyOn(plugin.onCellRangeSelected, 'notify').mockReturnValue({ + getReturnValue: () => true + }); const scrollSpy = jest.spyOn(gridStub, 'scrollCellIntoView'); const onCellRangeSelectingSpy = jest.spyOn(plugin.onCellRangeSelecting, 'notify'); @@ -309,16 +323,16 @@ describe('CellRangeSelector Plugin', () => { const decoratorHideSpy = jest.spyOn(plugin.getCellDecorator(), 'hide'); const decoratorShowSpy = jest.spyOn(plugin.getCellDecorator(), 'show'); - const scrollEvent = addJQueryEventPropagation(new Event('scroll')); + const scrollEvent = addVanillaEventPropagation(new Event('scroll')); gridStub.onScroll.notify({ scrollTop: 10, scrollLeft: 15, grid: gridStub }, scrollEvent, gridStub); - const dragEventInit = addJQueryEventPropagation(new Event('dragInit')); + const dragEventInit = addVanillaEventPropagation(new Event('dragInit')); gridStub.onDragInit.notify({ offsetX: 6, offsetY: 7, row: 1, startX: 3, startY: 4 } as any, dragEventInit, gridStub); - const dragEventStart = addJQueryEventPropagation(new Event('dragStart')); + const dragEventStart = addVanillaEventPropagation(new Event('dragStart')); gridStub.onDragStart.notify({ offsetX: 6, offsetY: 7, row: 1, startX: 3, startY: 4 } as any, dragEventStart, gridStub); - const dragEvent = addJQueryEventPropagation(new Event('drag')); + const dragEvent = addVanillaEventPropagation(new Event('drag')); gridStub.onDrag.notify({ startX: 3, startY: 4, range: { start: { cell: 2, row: 3 }, end: { cell: 4, row: 5 } }, grid: gridStub } as any, dragEvent, gridStub); expect(focusSpy).toHaveBeenCalled(); @@ -350,8 +364,12 @@ describe('CellRangeSelector Plugin', () => { jest.spyOn(gridStub, 'getCellFromPoint').mockReturnValue({ cell: 4, row: 5 }); jest.spyOn(gridStub, 'getCellNodeBox').mockReturnValue({ right: 2, bottom: 3, left: 4, top: 5, height: 20, width: 33, visible: true }); const focusSpy = jest.spyOn(gridStub, 'focus'); - const onBeforeCellRangeSpy = jest.spyOn(plugin.onBeforeCellRangeSelected, 'notify').mockReturnValue(true); - const onCellRangeSpy = jest.spyOn(plugin.onCellRangeSelected, 'notify').mockReturnValue(true); + const onBeforeCellRangeSpy = jest.spyOn(plugin.onBeforeCellRangeSelected, 'notify').mockReturnValue({ + getReturnValue: () => true + }); + const onCellRangeSpy = jest.spyOn(plugin.onCellRangeSelected, 'notify').mockReturnValue({ + getReturnValue: () => true + }); const scrollSpy = jest.spyOn(gridStub, 'scrollCellIntoView'); const onCellRangeSelectingSpy = jest.spyOn(plugin.onCellRangeSelecting, 'notify'); @@ -359,16 +377,16 @@ describe('CellRangeSelector Plugin', () => { const decoratorHideSpy = jest.spyOn(plugin.getCellDecorator(), 'hide'); const decoratorShowSpy = jest.spyOn(plugin.getCellDecorator(), 'show'); - const scrollEvent = addJQueryEventPropagation(new Event('scroll')); + const scrollEvent = addVanillaEventPropagation(new Event('scroll')); gridStub.onScroll.notify({ scrollTop: 10, scrollLeft: 15, grid: gridStub }, scrollEvent, gridStub); - const dragEventInit = addJQueryEventPropagation(new Event('dragInit')); + const dragEventInit = addVanillaEventPropagation(new Event('dragInit')); gridStub.onDragInit.notify({ offsetX: 6, offsetY: 7, row: 1, startX: 3, startY: 4 } as any, dragEventInit, gridStub); - const dragEventStart = addJQueryEventPropagation(new Event('dragStart')); + const dragEventStart = addVanillaEventPropagation(new Event('dragStart')); gridStub.onDragStart.notify({ offsetX: 6, offsetY: 7, row: 1, startX: 3, startY: 4 } as any, dragEventStart, gridStub); - const dragEvent = addJQueryEventPropagation(new Event('drag')); + const dragEvent = addVanillaEventPropagation(new Event('drag')); gridStub.onDrag.notify({ startX: 3, startY: 4, range: { start: { cell: 2, row: 3 }, end: { cell: 4, row: 5 } }, grid: gridStub } as any, dragEvent, gridStub); expect(focusSpy).toHaveBeenCalled(); @@ -408,21 +426,23 @@ describe('CellRangeSelector Plugin', () => { jest.spyOn(gridStub, 'canCellBeSelected').mockReturnValue(true); jest.spyOn(gridStub, 'getCellFromPoint').mockReturnValue({ cell: 4, row: 0 }); const focusSpy = jest.spyOn(gridStub, 'focus'); - const onBeforeCellRangeSpy = jest.spyOn(plugin.onBeforeCellRangeSelected, 'notify').mockReturnValue(true); + const onBeforeCellRangeSpy = jest.spyOn(plugin.onBeforeCellRangeSelected, 'notify').mockReturnValue({ + getReturnValue: () => true + }); plugin.init(gridStub); const decoratorShowSpy = jest.spyOn(plugin.getCellDecorator(), 'show'); - const scrollEvent = addJQueryEventPropagation(new Event('scroll')); + const scrollEvent = addVanillaEventPropagation(new Event('scroll')); gridStub.onScroll.notify({ scrollTop: 10, scrollLeft: 15, grid: gridStub }, scrollEvent, gridStub); - const dragEventInit = addJQueryEventPropagation(new Event('dragInit')); + const dragEventInit = addVanillaEventPropagation(new Event('dragInit')); gridStub.onDragInit.notify({ offsetX: 6, offsetY: 7, row: 1, startX: 3, startY: 4 } as any, dragEventInit, gridStub); - const dragEventStart = addJQueryEventPropagation(new Event('dragStart')); + const dragEventStart = addVanillaEventPropagation(new Event('dragStart')); gridStub.onDragStart.notify({ offsetX: 6, offsetY: 7, row: 1, startX: 3, startY: 4 } as any, dragEventStart, gridStub); - const dragEvent = addJQueryEventPropagation(new Event('drag')); + const dragEvent = addVanillaEventPropagation(new Event('drag')); gridStub.onDrag.notify({ startX: 3, startY: 4, range: { start: { cell: 2, row: 3 }, end: { cell: 4, row: 5 } }, grid: gridStub } as any, dragEvent, gridStub); // expect(focusSpy).toHaveBeenCalled(); @@ -453,21 +473,23 @@ describe('CellRangeSelector Plugin', () => { jest.spyOn(gridStub, 'canCellBeSelected').mockReturnValue(true); jest.spyOn(gridStub, 'getCellFromPoint').mockReturnValue({ cell: 4, row: 0 }); const focusSpy = jest.spyOn(gridStub, 'focus'); - const onBeforeCellRangeSpy = jest.spyOn(plugin.onBeforeCellRangeSelected, 'notify').mockReturnValue(true); + const onBeforeCellRangeSpy = jest.spyOn(plugin.onBeforeCellRangeSelected, 'notify').mockReturnValue({ + getReturnValue: () => true + }); plugin.init(gridStub); const decoratorShowSpy = jest.spyOn(plugin.getCellDecorator(), 'show'); - const scrollEvent = addJQueryEventPropagation(new Event('scroll')); + const scrollEvent = addVanillaEventPropagation(new Event('scroll')); gridStub.onScroll.notify({ scrollTop: 10, scrollLeft: 15, grid: gridStub }, scrollEvent, gridStub); - const dragEventInit = addJQueryEventPropagation(new Event('dragInit')); + const dragEventInit = addVanillaEventPropagation(new Event('dragInit')); gridStub.onDragInit.notify({ offsetX: 6, offsetY: 7, row: 1, startX: 3, startY: 4 } as any, dragEventInit, gridStub); - const dragEventStart = addJQueryEventPropagation(new Event('dragStart')); + const dragEventStart = addVanillaEventPropagation(new Event('dragStart')); gridStub.onDragStart.notify({ offsetX: 6, offsetY: 7, row: 1, startX: 3, startY: 4 } as any, dragEventStart, gridStub); - const dragEvent = addJQueryEventPropagation(new Event('drag')); + const dragEvent = addVanillaEventPropagation(new Event('drag')); gridStub.onDrag.notify({ startX: 3, startY: 4, range: { start: { cell: 2, row: 3 }, end: { cell: 4, row: 5 } }, grid: gridStub } as any, dragEvent, gridStub); expect(focusSpy).toHaveBeenCalled(); @@ -503,7 +525,9 @@ describe('CellRangeSelector Plugin', () => { isOutsideViewport: true }); const focusSpy = jest.spyOn(gridStub, 'focus'); - jest.spyOn(plugin.onBeforeCellRangeSelected, 'notify').mockReturnValue(true); + jest.spyOn(plugin.onBeforeCellRangeSelected, 'notify').mockReturnValue({ + getReturnValue: () => true + }); const getCellFromPointSpy = jest.spyOn(gridStub, 'getCellFromPoint'); const onCellRangeSelectingSpy = jest.spyOn(plugin.onCellRangeSelecting, 'notify'); @@ -512,16 +536,16 @@ describe('CellRangeSelector Plugin', () => { plugin.addonOptions.maxIntervalToShowNextCell = 6; const decoratorShowSpy = jest.spyOn(plugin.getCellDecorator(), 'show'); - const scrollEvent = addJQueryEventPropagation(new Event('scroll')); + const scrollEvent = addVanillaEventPropagation(new Event('scroll')); gridStub.onScroll.notify({ scrollTop: 10, scrollLeft: 15, grid: gridStub }, scrollEvent, gridStub); - const dragEventInit = addJQueryEventPropagation(new Event('dragInit')); + const dragEventInit = addVanillaEventPropagation(new Event('dragInit')); gridStub.onDragInit.notify({ offsetX: 6, offsetY: 7, row: 1, startX: 3, startY: 4 } as any, dragEventInit, gridStub); - const dragEventStart = addJQueryEventPropagation(new Event('dragStart')); + const dragEventStart = addVanillaEventPropagation(new Event('dragStart')); gridStub.onDragStart.notify({ offsetX: 6, offsetY: 7, row: 1, startX: 3, startY: 4 } as any, dragEventStart, gridStub); - const dragEvent = addJQueryEventPropagation(new Event('drag')); + const dragEvent = addVanillaEventPropagation(new Event('drag')); gridStub.onDrag.notify({ startX: 3, startY: 4, range: { start: { cell: 2, row: 3 }, end: { cell: 4, row: 5 } }, grid: gridStub } as any, dragEvent, gridStub); expect(focusSpy).toHaveBeenCalled(); @@ -556,7 +580,9 @@ describe('CellRangeSelector Plugin', () => { isOutsideViewport: true }); const focusSpy = jest.spyOn(gridStub, 'focus'); - jest.spyOn(plugin.onBeforeCellRangeSelected, 'notify').mockReturnValue(true); + jest.spyOn(plugin.onBeforeCellRangeSelected, 'notify').mockReturnValue({ + getReturnValue: () => true + }); const getCellFromPointSpy = jest.spyOn(gridStub, 'getCellFromPoint'); const onCellRangeSelectingSpy = jest.spyOn(plugin.onCellRangeSelecting, 'notify'); @@ -565,16 +591,16 @@ describe('CellRangeSelector Plugin', () => { plugin.addonOptions.maxIntervalToShowNextCell = 6; const decoratorShowSpy = jest.spyOn(plugin.getCellDecorator(), 'show'); - const scrollEvent = addJQueryEventPropagation(new Event('scroll')); + const scrollEvent = addVanillaEventPropagation(new Event('scroll')); gridStub.onScroll.notify({ scrollTop: 10, scrollLeft: 15, grid: gridStub }, scrollEvent, gridStub); - const dragEventInit = addJQueryEventPropagation(new Event('dragInit')); + const dragEventInit = addVanillaEventPropagation(new Event('dragInit')); gridStub.onDragInit.notify({ offsetX: 6, offsetY: 7, row: 1, startX: 3, startY: 4 } as any, dragEventInit, gridStub); - const dragEventStart = addJQueryEventPropagation(new Event('dragStart')); + const dragEventStart = addVanillaEventPropagation(new Event('dragStart')); gridStub.onDragStart.notify({ offsetX: 6, offsetY: 7, row: 1, startX: 3, startY: 4 } as any, dragEventStart, gridStub); - const dragEvent = addJQueryEventPropagation(new Event('drag')); + const dragEvent = addVanillaEventPropagation(new Event('drag')); gridStub.onDrag.notify({ startX: 3, startY: 4, range: { start: { cell: 2, row: 3 }, end: { cell: 4, row: 5 } }, grid: gridStub } as any, dragEvent, gridStub); expect(focusSpy).toHaveBeenCalled(); @@ -617,7 +643,9 @@ describe('CellRangeSelector Plugin', () => { isOutsideViewport: true }); const focusSpy = jest.spyOn(gridStub, 'focus'); - jest.spyOn(plugin.onBeforeCellRangeSelected, 'notify').mockReturnValue(true); + jest.spyOn(plugin.onBeforeCellRangeSelected, 'notify').mockReturnValue({ + getReturnValue: () => true + }); const getCellFromPointSpy = jest.spyOn(gridStub, 'getCellFromPoint'); const onCellRangeSelectingSpy = jest.spyOn(plugin.onCellRangeSelecting, 'notify'); @@ -626,16 +654,16 @@ describe('CellRangeSelector Plugin', () => { plugin.addonOptions.maxIntervalToShowNextCell = 6; const decoratorShowSpy = jest.spyOn(plugin.getCellDecorator(), 'show'); - const scrollEvent = addJQueryEventPropagation(new Event('scroll')); + const scrollEvent = addVanillaEventPropagation(new Event('scroll')); gridStub.onScroll.notify({ scrollTop: 10, scrollLeft: 15, grid: gridStub }, scrollEvent, gridStub); - const dragEventInit = addJQueryEventPropagation(new Event('dragInit')); + const dragEventInit = addVanillaEventPropagation(new Event('dragInit')); gridStub.onDragInit.notify({ offsetX: 6, offsetY: 7, row: 1, startX: 3, startY: 4 } as any, dragEventInit, gridStub); - const dragEventStart = addJQueryEventPropagation(new Event('dragStart')); + const dragEventStart = addVanillaEventPropagation(new Event('dragStart')); gridStub.onDragStart.notify({ offsetX: 6, offsetY: 7, row: 1, startX: 3, startY: 4 } as any, dragEventStart, gridStub); - const dragEvent = addJQueryEventPropagation(new Event('drag')); + const dragEvent = addVanillaEventPropagation(new Event('drag')); gridStub.onDrag.notify({ startX: 3, startY: 4, range: { start: { cell: 2, row: 3 }, end: { cell: 4, row: 5 } }, grid: gridStub } as any, dragEvent, gridStub); expect(focusSpy).toHaveBeenCalled(); diff --git a/packages/common/src/extensions/__tests__/slickCellSelectionModel.spec.ts b/packages/common/src/extensions/__tests__/slickCellSelectionModel.spec.ts index 534bb0d44..78379135d 100644 --- a/packages/common/src/extensions/__tests__/slickCellSelectionModel.spec.ts +++ b/packages/common/src/extensions/__tests__/slickCellSelectionModel.spec.ts @@ -8,7 +8,7 @@ declare const Slick: SlickNamespace; const GRID_UID = 'slickgrid_12345'; jest.mock('flatpickr', () => { }); -const addJQueryEventPropagation = function (event, commandKey = '', keyName = '') { +const addVanillaEventPropagation = function (event, commandKey = '', keyName = '') { Object.defineProperty(event, 'isPropagationStopped', { writable: true, configurable: true, value: jest.fn() }); Object.defineProperty(event, 'isImmediatePropagationStopped', { writable: true, configurable: true, value: jest.fn() }); if (commandKey) { @@ -162,19 +162,19 @@ describe('CellSelectionModel Plugin', () => { }); it('should return False when onBeforeCellRangeSelected is called and getEditorLock returns False', () => { - const mouseEvent = addJQueryEventPropagation(new Event('mouseenter')); + const mouseEvent = addVanillaEventPropagation(new Event('mouseenter')); jest.spyOn(gridStub.getEditorLock(), 'isActive').mockReturnValue(true); const stopPropSpy = jest.spyOn(mouseEvent, 'stopPropagation'); plugin.init(gridStub); - const output = plugin.cellRangeSelector.onBeforeCellRangeSelected.notify({ cell: 2, row: 3 }, mouseEvent, gridStub); + const output = plugin.cellRangeSelector.onBeforeCellRangeSelected.notify({ cell: 2, row: 3 }, mouseEvent, gridStub).getReturnValue(); expect(output).toBeFalsy(); expect(stopPropSpy).toHaveBeenCalled(); }); it('should call "setSelectedRanges" when "onCellRangeSelected"', () => { - const mouseEvent = addJQueryEventPropagation(new Event('mouseenter')); + const mouseEvent = addVanillaEventPropagation(new Event('mouseenter')); jest.spyOn(gridStub.getEditorLock(), 'isActive').mockReturnValue(true); const setActiveCellSpy = jest.spyOn(gridStub, 'setActiveCell'); const setSelectRangeSpy = jest.spyOn(plugin, 'setSelectedRanges'); @@ -190,7 +190,7 @@ describe('CellSelectionModel Plugin', () => { plugin = new SlickCellSelectionModel({ selectActiveCell: true, cellRangeSelector: undefined as any }); plugin.init(gridStub); const setSelectRangeSpy = jest.spyOn(plugin, 'setSelectedRanges'); - const mouseEvent = addJQueryEventPropagation(new Event('mouseenter')); + const mouseEvent = addVanillaEventPropagation(new Event('mouseenter')); gridStub.onActiveCellChanged.notify({ cell: 2, row: 3, grid: gridStub }, mouseEvent, gridStub); expect(setSelectRangeSpy).toHaveBeenCalledWith([{ @@ -203,7 +203,7 @@ describe('CellSelectionModel Plugin', () => { plugin = new SlickCellSelectionModel({ selectActiveCell: false, cellRangeSelector: undefined as any }); plugin.init(gridStub); const setSelectRangeSpy = jest.spyOn(plugin, 'setSelectedRanges'); - const mouseEvent = addJQueryEventPropagation(new Event('mouseenter')); + const mouseEvent = addVanillaEventPropagation(new Event('mouseenter')); gridStub.onActiveCellChanged.notify({ cell: 2, row: 3, grid: gridStub }, mouseEvent, gridStub); expect(setSelectRangeSpy).toHaveBeenCalledWith([]); @@ -219,7 +219,7 @@ describe('CellSelectionModel Plugin', () => { plugin.setSelectedRanges(mockRanges); const setSelectRangeSpy = jest.spyOn(plugin, 'setSelectedRanges'); - const keyDownEvent = addJQueryEventPropagation(new Event('keydown'), 'shiftKey', 'ArrowLeft'); + const keyDownEvent = addVanillaEventPropagation(new Event('keydown'), 'shiftKey', 'ArrowLeft'); gridStub.onKeyDown.notify({ cell: 2, row: 3, grid: gridStub }, keyDownEvent, gridStub); expect(setSelectRangeSpy).toHaveBeenCalledWith([{ @@ -237,7 +237,7 @@ describe('CellSelectionModel Plugin', () => { { fromCell: 2, fromRow: 3, toCell: 3, toRow: 4 } ] as unknown as SlickRange[]); const setSelectRangeSpy = jest.spyOn(plugin, 'setSelectedRanges'); - const keyDownEvent = addJQueryEventPropagation(new Event('keydown'), 'shiftKey', 'ArrowRight'); + const keyDownEvent = addVanillaEventPropagation(new Event('keydown'), 'shiftKey', 'ArrowRight'); gridStub.onKeyDown.notify({ cell: 2, row: 3, grid: gridStub }, keyDownEvent, gridStub); expect(setSelectRangeSpy).toHaveBeenCalledWith([{ @@ -255,7 +255,7 @@ describe('CellSelectionModel Plugin', () => { { fromCell: 2, fromRow: 3, toCell: 3, toRow: 4 } ] as unknown as SlickRange[]); const setSelectRangeSpy = jest.spyOn(plugin, 'setSelectedRanges'); - const keyDownEvent = addJQueryEventPropagation(new Event('keydown'), 'shiftKey', 'ArrowUp'); + const keyDownEvent = addVanillaEventPropagation(new Event('keydown'), 'shiftKey', 'ArrowUp'); gridStub.onKeyDown.notify({ cell: 2, row: 3, grid: gridStub }, keyDownEvent, gridStub); expect(setSelectRangeSpy).toHaveBeenCalledWith([{ @@ -273,7 +273,7 @@ describe('CellSelectionModel Plugin', () => { { fromCell: 2, fromRow: 3, toCell: 3, toRow: 4, contains: () => false } ] as unknown as SlickRange[]); const setSelectRangeSpy = jest.spyOn(plugin, 'setSelectedRanges'); - const keyDownEvent = addJQueryEventPropagation(new Event('keydown'), 'shiftKey', 'ArrowDown'); + const keyDownEvent = addVanillaEventPropagation(new Event('keydown'), 'shiftKey', 'ArrowDown'); gridStub.onKeyDown.notify({ cell: 2, row: 3, grid: gridStub }, keyDownEvent, gridStub); expect(setSelectRangeSpy).toHaveBeenCalledWith([{ @@ -295,7 +295,7 @@ describe('CellSelectionModel Plugin', () => { { fromCell: 2, fromRow: 3, toCell: 3, toRow: 4, contains: () => false } ] as unknown as SlickRange[]); const setSelectRangeSpy = jest.spyOn(plugin, 'setSelectedRanges'); - const keyDownEvent = addJQueryEventPropagation(new Event('keydown'), 'shiftKey', 'ArrowDown'); + const keyDownEvent = addVanillaEventPropagation(new Event('keydown'), 'shiftKey', 'ArrowDown'); gridStub.onKeyDown.notify({ cell: 2, row: 3, grid: gridStub }, keyDownEvent, gridStub); const expectedRangeCalled = [ diff --git a/packages/common/src/extensions/__tests__/slickCheckboxSelectColumn.spec.ts b/packages/common/src/extensions/__tests__/slickCheckboxSelectColumn.spec.ts index 78a993b10..46c2f5f4a 100644 --- a/packages/common/src/extensions/__tests__/slickCheckboxSelectColumn.spec.ts +++ b/packages/common/src/extensions/__tests__/slickCheckboxSelectColumn.spec.ts @@ -8,7 +8,7 @@ import { KeyCode } from '../../enums'; declare const Slick: SlickNamespace; -const addJQueryEventPropagation = function (event, commandKey = '', keyName = '', target?: HTMLElement, which: string | number = '') { +const addVanillaEventPropagation = function (event, commandKey = '', keyName = '', target?: HTMLElement, which: string | number = '') { Object.defineProperty(event, 'isPropagationStopped', { writable: true, configurable: true, value: jest.fn() }); Object.defineProperty(event, 'isImmediatePropagationStopped', { writable: true, configurable: true, value: jest.fn() }); if (commandKey) { @@ -177,7 +177,7 @@ describe('SlickCheckboxSelectColumn Plugin', () => { const checkboxElm = document.createElement('input'); checkboxElm.type = 'checkbox'; - const clickEvent = addJQueryEventPropagation(new Event('click'), '', '', checkboxElm); + const clickEvent = addVanillaEventPropagation(new Event('click'), '', '', checkboxElm); const preventDefaultSpy = jest.spyOn(clickEvent, 'preventDefault'); const stopImmediatePropagationSpy = jest.spyOn(clickEvent, 'stopImmediatePropagation'); gridStub.onHeaderClick.notify({ column: { id: '_checkbox_selector', field: '_checkbox_selector' }, grid: gridStub }, clickEvent); @@ -212,7 +212,7 @@ describe('SlickCheckboxSelectColumn Plugin', () => { const checkboxElm = document.createElement('input'); checkboxElm.type = 'checkbox'; checkboxElm.checked = true; - const clickEvent = addJQueryEventPropagation(new Event('click'), '', '', checkboxElm); + const clickEvent = addVanillaEventPropagation(new Event('click'), '', '', checkboxElm); const stopPropagationSpy = jest.spyOn(clickEvent, 'stopPropagation'); const stopImmediatePropagationSpy = jest.spyOn(clickEvent, 'stopImmediatePropagation'); gridStub.onHeaderClick.notify({ column: { id: '_checkbox_selector', field: '_checkbox_selector' }, grid: gridStub }, clickEvent); @@ -220,7 +220,7 @@ describe('SlickCheckboxSelectColumn Plugin', () => { expect(plugin).toBeTruthy(); expect(stopPropagationSpy).toHaveBeenCalled(); expect(stopImmediatePropagationSpy).toHaveBeenCalled(); - expect(setSelectedRowSpy).toHaveBeenCalledWith([0, 1, 2], 'click.selectAll'); + expect(setSelectedRowSpy).toHaveBeenCalledWith([0, 2], 'click.selectAll'); expect(onToggleStartMock).toHaveBeenCalledWith(expect.anything(), { caller: 'click.selectAll', previousSelectedRows: undefined, }); expect(onToggleEndMock).toHaveBeenCalledWith(expect.anything(), { caller: 'click.selectAll', previousSelectedRows: undefined, rows: [0, 2] }); }); @@ -246,7 +246,7 @@ describe('SlickCheckboxSelectColumn Plugin', () => { const checkboxElm = document.createElement('input'); checkboxElm.type = 'checkbox'; checkboxElm.checked = true; - const clickEvent = addJQueryEventPropagation(new Event('click'), '', '', checkboxElm); + const clickEvent = addVanillaEventPropagation(new Event('click'), '', '', checkboxElm); const stopPropagationSpy = jest.spyOn(clickEvent, 'stopPropagation'); const stopImmediatePropagationSpy = jest.spyOn(clickEvent, 'stopImmediatePropagation'); gridStub.onHeaderClick.notify({ column: { id: '_checkbox_selector', field: '_checkbox_selector' }, grid: gridStub }, clickEvent); @@ -254,7 +254,7 @@ describe('SlickCheckboxSelectColumn Plugin', () => { expect(plugin).toBeTruthy(); expect(stopPropagationSpy).toHaveBeenCalled(); expect(stopImmediatePropagationSpy).toHaveBeenCalled(); - expect(setSelectedRowSpy).toHaveBeenCalledWith([0, 1, 2], 'click.selectAll'); + expect(setSelectedRowSpy).toHaveBeenCalledWith([0, 2], 'click.selectAll'); expect(onToggleStartMock).toHaveBeenCalledWith(expect.anything(), { caller: 'click.selectAll', previousSelectedRows: undefined, }); expect(onToggleEndMock).toHaveBeenCalledWith(expect.anything(), { caller: 'click.selectAll', previousSelectedRows: undefined, rows: [0, 2] }); expect(setSelectedIdsSpy).toHaveBeenCalledWith([22], { isRowBeingAdded: true }); @@ -498,7 +498,7 @@ describe('SlickCheckboxSelectColumn Plugin', () => { plugin.init(gridStub); const checkboxElm = document.createElement('input'); checkboxElm.type = 'checkbox'; - const clickEvent = addJQueryEventPropagation(new Event('click'), '', '', checkboxElm); + const clickEvent = addVanillaEventPropagation(new Event('click'), '', '', checkboxElm); const stopPropagationSpy = jest.spyOn(clickEvent, 'stopPropagation'); const stopImmediatePropagationSpy = jest.spyOn(clickEvent, 'stopImmediatePropagation'); gridStub.onClick.notify({ cell: 0, row: 2, grid: gridStub }, clickEvent); @@ -517,7 +517,7 @@ describe('SlickCheckboxSelectColumn Plugin', () => { plugin.setOptions({ onRowToggleStart: onToggleStartMock }); const checkboxElm = document.createElement('input'); checkboxElm.type = 'checkbox'; - const clickEvent = addJQueryEventPropagation(new Event('click'), '', '', checkboxElm); + const clickEvent = addVanillaEventPropagation(new Event('click'), '', '', checkboxElm); const stopPropagationSpy = jest.spyOn(clickEvent, 'stopPropagation'); const stopImmediatePropagationSpy = jest.spyOn(clickEvent, 'stopImmediatePropagation'); gridStub.onClick.notify({ cell: 0, row: 2, grid: gridStub }, clickEvent); @@ -537,7 +537,7 @@ describe('SlickCheckboxSelectColumn Plugin', () => { plugin.setOptions({ onRowToggleEnd: onToggleEndMock }); const checkboxElm = document.createElement('input'); checkboxElm.type = 'checkbox'; - const clickEvent = addJQueryEventPropagation(new Event('click'), '', '', checkboxElm); + const clickEvent = addVanillaEventPropagation(new Event('click'), '', '', checkboxElm); const stopPropagationSpy = jest.spyOn(clickEvent, 'stopPropagation'); const stopImmediatePropagationSpy = jest.spyOn(clickEvent, 'stopImmediatePropagation'); gridStub.onClick.notify({ cell: 0, row: 2, grid: gridStub }, clickEvent); @@ -557,7 +557,7 @@ describe('SlickCheckboxSelectColumn Plugin', () => { plugin.init(gridStub); const checkboxElm = document.createElement('input'); checkboxElm.type = 'checkbox'; - const clickEvent = addJQueryEventPropagation(new Event('click'), '', '', checkboxElm); + const clickEvent = addVanillaEventPropagation(new Event('click'), '', '', checkboxElm); const preventDefaultSpy = jest.spyOn(clickEvent, 'preventDefault'); const stopImmediatePropagationSpy = jest.spyOn(clickEvent, 'stopImmediatePropagation'); gridStub.onClick.notify({ cell: 0, row: 2, grid: gridStub }, clickEvent); @@ -575,7 +575,7 @@ describe('SlickCheckboxSelectColumn Plugin', () => { plugin.init(gridStub); const checkboxElm = document.createElement('input'); checkboxElm.type = 'checkbox'; - const keyboardEvent = addJQueryEventPropagation(new Event('keyDown'), '', ' ', checkboxElm); + const keyboardEvent = addVanillaEventPropagation(new Event('keyDown'), '', ' ', checkboxElm); const preventDefaultSpy = jest.spyOn(keyboardEvent, 'preventDefault'); const stopImmediatePropagationSpy = jest.spyOn(keyboardEvent, 'stopImmediatePropagation'); gridStub.onKeyDown.notify({ cell: 0, row: 2, grid: gridStub }, keyboardEvent); @@ -598,7 +598,7 @@ describe('SlickCheckboxSelectColumn Plugin', () => { plugin.init(gridStub); const checkboxElm = document.createElement('input'); checkboxElm.type = 'checkbox'; - const keyboardEvent = addJQueryEventPropagation(new Event('keyDown'), '', ' ', checkboxElm, KeyCode.SPACE); + const keyboardEvent = addVanillaEventPropagation(new Event('keyDown'), '', ' ', checkboxElm, KeyCode.SPACE); const preventDefaultSpy = jest.spyOn(keyboardEvent, 'preventDefault'); const stopImmediatePropagationSpy = jest.spyOn(keyboardEvent, 'stopImmediatePropagation'); gridStub.onKeyDown.notify({ cell: 0, row: 2, grid: gridStub }, keyboardEvent); @@ -620,7 +620,7 @@ describe('SlickCheckboxSelectColumn Plugin', () => { plugin.init(gridStub); const checkboxElm = document.createElement('input'); checkboxElm.type = 'checkbox'; - const clickEvent = addJQueryEventPropagation(new Event('keyDown'), '', ' ', checkboxElm); + const clickEvent = addVanillaEventPropagation(new Event('keyDown'), '', ' ', checkboxElm); gridStub.onSelectedRowsChanged.notify({ rows: [2, 3], previousSelectedRows: [0, 1], grid: gridStub } as OnSelectedRowsChangedEventArgs, clickEvent); expect(plugin).toBeTruthy(); @@ -646,7 +646,7 @@ describe('SlickCheckboxSelectColumn Plugin', () => { plugin.init(gridStub); const checkboxElm = document.createElement('input'); checkboxElm.type = 'checkbox'; - const clickEvent = addJQueryEventPropagation(new Event('keyDown'), '', ' ', checkboxElm); + const clickEvent = addVanillaEventPropagation(new Event('keyDown'), '', ' ', checkboxElm); gridStub.onSelectedRowsChanged.notify({ rows: [2, 3], previousSelectedRows: [0, 1], grid: gridStub } as OnSelectedRowsChangedEventArgs, clickEvent); expect(plugin).toBeTruthy(); @@ -677,7 +677,7 @@ describe('SlickCheckboxSelectColumn Plugin', () => { const checkboxElm = document.createElement('input'); checkboxElm.type = 'checkbox'; - const clickEvent = addJQueryEventPropagation(new Event('keyDown'), '', ' ', checkboxElm); + const clickEvent = addVanillaEventPropagation(new Event('keyDown'), '', ' ', checkboxElm); gridStub.onSelectedRowsChanged.notify({ rows: [2, 3], previousSelectedRows: [0, 1], grid: gridStub } as OnSelectedRowsChangedEventArgs, clickEvent); expect(plugin).toBeTruthy(); @@ -712,7 +712,7 @@ describe('SlickCheckboxSelectColumn Plugin', () => { const checkboxElm = document.createElement('input'); checkboxElm.type = 'checkbox'; - const clickEvent = addJQueryEventPropagation(new Event('keyDown'), '', ' ', checkboxElm); + const clickEvent = addVanillaEventPropagation(new Event('keyDown'), '', ' ', checkboxElm); dataViewStub.onSelectedRowIdsChanged.notify({ rows: [0, 1], filteredIds: [1, 2], ids: [1, 2], selectedRowIds: [1, 2], dataView: dataViewStub, grid: gridStub }, clickEvent); expect(plugin).toBeTruthy(); diff --git a/packages/common/src/extensions/__tests__/slickRowMoveManager.spec.ts b/packages/common/src/extensions/__tests__/slickRowMoveManager.spec.ts index 0f8154c4d..6b433ec3f 100644 --- a/packages/common/src/extensions/__tests__/slickRowMoveManager.spec.ts +++ b/packages/common/src/extensions/__tests__/slickRowMoveManager.spec.ts @@ -8,7 +8,7 @@ declare const Slick: SlickNamespace; const GRID_UID = 'slickgrid_12345'; jest.mock('flatpickr', () => { }); -const addJQueryEventPropagation = function (event, target?: HTMLElement) { +const addVanillaEventPropagation = function (event, target?: HTMLElement) { Object.defineProperty(event, 'isPropagationStopped', { writable: true, configurable: true, value: jest.fn() }); Object.defineProperty(event, 'isImmediatePropagationStopped', { writable: true, configurable: true, value: jest.fn() }); if (target) { @@ -243,7 +243,7 @@ describe('SlickRowMoveManager Plugin', () => { plugin.init(gridStub); const divElm = document.createElement('div'); - const mouseEvent = addJQueryEventPropagation(new Event('mouseenter'), divElm); + const mouseEvent = addVanillaEventPropagation(new Event('mouseenter'), divElm); const stopImmediatePropagationSpy = jest.spyOn(mouseEvent, 'stopImmediatePropagation'); gridStub.onDragInit.notify({ count: 1, deltaX: 0, deltaY: 1, offsetX: 2, offsetY: 3, proxy: document.createElement('div'), guide: document.createElement('div'), row: 2, rows: [2], @@ -256,7 +256,7 @@ describe('SlickRowMoveManager Plugin', () => { plugin.init(gridStub, { hideRowMoveShadow: false }); const divElm = document.createElement('div'); - const mouseEvent = addJQueryEventPropagation(new Event('mouseenter'), divElm); + const mouseEvent = addVanillaEventPropagation(new Event('mouseenter'), divElm); const stopImmediatePropagationSpy = jest.spyOn(mouseEvent, 'stopImmediatePropagation'); const mockArgs = { deltaX: 0, deltaY: 1, offsetX: 2, offsetY: 3, @@ -287,7 +287,7 @@ describe('SlickRowMoveManager Plugin', () => { const onMoveRowNotifySpy = jest.spyOn(plugin.onMoveRows, 'notify'); const divElm = document.createElement('div'); - const mouseEvent = addJQueryEventPropagation(new Event('mouseenter'), divElm); + const mouseEvent = addVanillaEventPropagation(new Event('mouseenter'), divElm); const stopImmediatePropagationSpy = jest.spyOn(mouseEvent, 'stopImmediatePropagation'); const mockArgs = { deltaX: 0, deltaY: 1, offsetX: 2, offsetY: 3, @@ -309,7 +309,7 @@ describe('SlickRowMoveManager Plugin', () => { gridStub.onDragEnd.notify(mockArgs, mouseEvent); expect(onMoveRowNotifySpy).toHaveBeenCalledWith({ insertBefore: -1, rows: [mockNewMovedRow], grid: gridStub, }); - expect(mockOnMoveRows).toHaveBeenCalledWith(mouseEvent, { insertBefore: -1, rows: [mockNewMovedRow], grid: gridStub, }); + expect(mockOnMoveRows).toHaveBeenCalledWith(expect.anything(), { insertBefore: -1, rows: [mockNewMovedRow], grid: gridStub, }); expect(stopImmediatePropagationSpy).toHaveBeenCalledTimes(2); }); @@ -324,13 +324,13 @@ describe('SlickRowMoveManager Plugin', () => { const cancelEditorSpy = jest.spyOn(gridStub.getEditorLock(), 'cancelCurrentEdit'); const divElm = document.createElement('div'); - const mouseEvent = addJQueryEventPropagation(new Event('mouseenter'), divElm); + const mouseEvent = addVanillaEventPropagation(new Event('mouseenter'), divElm); const stopImmediatePropagationSpy = jest.spyOn(mouseEvent, 'stopImmediatePropagation'); const mockArgs = { canMove: true, deltaX: 0, deltaY: 1, offsetX: 2, offsetY: 3, row: 2, rows: [2], selectedRows: [2], insertBefore: 4, } as any; - const output = gridStub.onDragStart.notify(mockArgs, mouseEvent); + const output = gridStub.onDragStart.notify(mockArgs, mouseEvent).getReturnValue(); expect(stopImmediatePropagationSpy).not.toHaveBeenCalled(); expect(cancelEditorSpy).toHaveBeenCalled(); @@ -347,7 +347,7 @@ describe('SlickRowMoveManager Plugin', () => { jest.spyOn(gridStub.getEditorLock(), 'isActive').mockReturnValue(true); const divElm = document.createElement('div'); - const mouseEvent = addJQueryEventPropagation(new Event('mouseenter'), divElm); + const mouseEvent = addVanillaEventPropagation(new Event('mouseenter'), divElm); const stopImmediatePropagationSpy = jest.spyOn(mouseEvent, 'stopImmediatePropagation'); const mockArgs = { canMove: true, deltaX: 0, deltaY: 1, offsetX: 2, offsetY: 3, @@ -376,7 +376,7 @@ describe('SlickRowMoveManager Plugin', () => { jest.spyOn(gridStub.getEditorLock(), 'isActive').mockReturnValue(false); const divElm = document.createElement('div'); - const mouseEvent = addJQueryEventPropagation(new Event('mouseenter'), divElm); + const mouseEvent = addVanillaEventPropagation(new Event('mouseenter'), divElm); const stopImmediatePropagationSpy = jest.spyOn(mouseEvent, 'stopImmediatePropagation'); const mockArgs = { deltaX: 0, deltaY: 1, offsetX: 2, offsetY: 3, @@ -424,7 +424,7 @@ describe('SlickRowMoveManager Plugin', () => { jest.spyOn(gridStub.getEditorLock(), 'isActive').mockReturnValue(false); const divElm = document.createElement('div'); - const mouseEvent = addJQueryEventPropagation(new Event('mouseenter'), divElm); + const mouseEvent = addVanillaEventPropagation(new Event('mouseenter'), divElm); const stopImmediatePropagationSpy = jest.spyOn(mouseEvent, 'stopImmediatePropagation'); const mockArgs = { deltaX: 0, deltaY: 1, offsetX: 2, offsetY: 3, diff --git a/packages/common/src/extensions/__tests__/slickRowSelectionModel.spec.ts b/packages/common/src/extensions/__tests__/slickRowSelectionModel.spec.ts index 4e2c72cef..d114391fc 100644 --- a/packages/common/src/extensions/__tests__/slickRowSelectionModel.spec.ts +++ b/packages/common/src/extensions/__tests__/slickRowSelectionModel.spec.ts @@ -8,7 +8,7 @@ declare const Slick: SlickNamespace; const GRID_UID = 'slickgrid_12345'; jest.mock('flatpickr', () => { }); -const addJQueryEventPropagation = function (event, commandKey = '', keyName = '') { +const addVanillaEventPropagation = function (event, commandKey = '', keyName = '') { Object.defineProperty(event, 'isPropagationStopped', { writable: true, configurable: true, value: jest.fn() }); Object.defineProperty(event, 'isImmediatePropagationStopped', { writable: true, configurable: true, value: jest.fn() }); if (commandKey) { @@ -202,7 +202,7 @@ describe('SlickRowSelectionModel Plugin', () => { plugin = new SlickRowSelectionModel({ selectActiveRow: true, }); plugin.init(gridStub); const setSelectRangeSpy = jest.spyOn(plugin, 'setSelectedRanges'); - const mouseEvent = addJQueryEventPropagation(new Event('mouseenter')); + const mouseEvent = addVanillaEventPropagation(new Event('mouseenter')); gridStub.onActiveCellChanged.notify({ cell: 2, row: 3, grid: gridStub }, mouseEvent, gridStub); expect(setSelectRangeSpy).toHaveBeenCalledWith([{ @@ -223,7 +223,7 @@ describe('SlickRowSelectionModel Plugin', () => { plugin.setSelectedRanges(mockRanges); const setSelectRangeSpy = jest.spyOn(plugin, 'setSelectedRanges'); - const keyDownEvent = addJQueryEventPropagation(new Event('keydown'), 'shiftKey', 'ArrowDown'); + const keyDownEvent = addVanillaEventPropagation(new Event('keydown'), 'shiftKey', 'ArrowDown'); gridStub.onKeyDown.notify({ cell: 2, row: 3, grid: gridStub }, keyDownEvent, gridStub); expect(setSelectRangeSpy).toHaveBeenCalledWith([{ @@ -253,7 +253,7 @@ describe('SlickRowSelectionModel Plugin', () => { plugin.setSelectedRanges(mockRanges); const setSelectRangeSpy = jest.spyOn(plugin, 'setSelectedRanges'); - const keyDownEvent = addJQueryEventPropagation(new Event('keydown'), 'shiftKey', 'ArrowUp'); + const keyDownEvent = addVanillaEventPropagation(new Event('keydown'), 'shiftKey', 'ArrowUp'); gridStub.onKeyDown.notify({ cell: 2, row: 3, grid: gridStub }, keyDownEvent, gridStub); expect(setSelectRangeSpy).toHaveBeenCalledWith([{ @@ -278,7 +278,7 @@ describe('SlickRowSelectionModel Plugin', () => { plugin.setSelectedRanges(mockRanges); const setSelectRangeSpy = jest.spyOn(plugin, 'setSelectedRanges'); - const keyDownEvent = addJQueryEventPropagation(new Event('keydown'), 'shiftKey', 'ArrowUp'); + const keyDownEvent = addVanillaEventPropagation(new Event('keydown'), 'shiftKey', 'ArrowUp'); gridStub.onKeyDown.notify({ cell: 2, row: 5, grid: gridStub }, keyDownEvent, gridStub); expect(setSelectRangeSpy).toHaveBeenCalledWith([{ @@ -300,7 +300,7 @@ describe('SlickRowSelectionModel Plugin', () => { plugin.setSelectedRanges(mockRanges); const setSelectRangeSpy = jest.spyOn(plugin, 'setSelectedRanges'); - const keyDownEvent = addJQueryEventPropagation(new Event('keydown'), 'shiftKey', 'ArrowUp'); + const keyDownEvent = addVanillaEventPropagation(new Event('keydown'), 'shiftKey', 'ArrowUp'); gridStub.onKeyDown.notify({ cell: 2, row: 3, grid: gridStub }, keyDownEvent, gridStub); expect(setSelectRangeSpy).toHaveBeenCalledWith([{ @@ -325,7 +325,7 @@ describe('SlickRowSelectionModel Plugin', () => { plugin.setSelectedRanges(mockRanges); const setSelectRangeSpy = jest.spyOn(plugin, 'setSelectedRanges'); - const keyDownEvent = addJQueryEventPropagation(new Event('click'), 'ctrlKey'); + const keyDownEvent = addVanillaEventPropagation(new Event('click'), 'ctrlKey'); gridStub.onClick.notify({ cell: 2, row: 3, grid: gridStub }, keyDownEvent, gridStub); expect(setSelectRangeSpy).not.toHaveBeenCalled(); @@ -345,7 +345,7 @@ describe('SlickRowSelectionModel Plugin', () => { plugin.setSelectedRanges(mockRanges); const setSelectRangeSpy = jest.spyOn(plugin, 'setSelectedRanges'); - const keyDownEvent = addJQueryEventPropagation(new Event('click'), 'ctrlKey'); + const keyDownEvent = addVanillaEventPropagation(new Event('click'), 'ctrlKey'); gridStub.onClick.notify({ cell: 2, row: 3, grid: gridStub }, keyDownEvent, gridStub); expect(setSelectRangeSpy).not.toHaveBeenCalled(); @@ -364,7 +364,7 @@ describe('SlickRowSelectionModel Plugin', () => { plugin.setSelectedRanges(mockRanges); const setSelectRangeSpy = jest.spyOn(plugin, 'setSelectedRanges'); - const keyDownEvent = addJQueryEventPropagation(new Event('click'), 'ctrlKey'); + const keyDownEvent = addVanillaEventPropagation(new Event('click'), 'ctrlKey'); gridStub.onClick.notify({ cell: 2, row: 3, grid: gridStub }, keyDownEvent, gridStub); expect(setSelectRangeSpy).toHaveBeenCalledWith([{ @@ -393,7 +393,7 @@ describe('SlickRowSelectionModel Plugin', () => { plugin.setSelectedRanges(mockRanges); const setSelectRangeSpy = jest.spyOn(plugin, 'setSelectedRanges'); - const keyDownEvent = addJQueryEventPropagation(new Event('click'), 'shiftKey'); + const keyDownEvent = addVanillaEventPropagation(new Event('click'), 'shiftKey'); gridStub.onClick.notify({ cell: 2, row: 3, grid: gridStub }, keyDownEvent, gridStub); expect(setActiveCellSpy).toHaveBeenCalledWith(3, 2); @@ -420,7 +420,7 @@ describe('SlickRowSelectionModel Plugin', () => { plugin.setSelectedRanges(mockRanges); const setSelectRangeSpy = jest.spyOn(plugin, 'setSelectedRanges'); - const keyDownEvent = addJQueryEventPropagation(new Event('click'), 'shiftKey'); + const keyDownEvent = addVanillaEventPropagation(new Event('click'), 'shiftKey'); gridStub.onClick.notify({ cell: 2, row: 3, grid: gridStub }, keyDownEvent, gridStub); expect(setActiveCellSpy).toHaveBeenCalledWith(1, 0); @@ -452,7 +452,7 @@ describe('SlickRowSelectionModel Plugin', () => { plugin.setSelectedRanges(mockRanges); const setSelectRangeSpy = jest.spyOn(plugin, 'setSelectedRanges'); - const keyDownEvent = addJQueryEventPropagation(new Event('click'), 'ctrlKey'); + const keyDownEvent = addVanillaEventPropagation(new Event('click'), 'ctrlKey'); gridStub.onClick.notify({ cell: 2, row: 3, grid: gridStub }, keyDownEvent, gridStub); expect(setSelectRangeSpy).toHaveBeenCalledWith([{ @@ -489,7 +489,7 @@ describe('SlickRowSelectionModel Plugin', () => { const setSelectedRangeSpy = jest.spyOn(plugin, 'setSelectedRanges'); plugin.init(gridStub); - const scrollEvent = addJQueryEventPropagation(new Event('scroll')); + const scrollEvent = addVanillaEventPropagation(new Event('scroll')); plugin.getCellRangeSelector()!.onCellRangeSelected.notify({ range: { fromCell: 2, fromRow: 3, toCell: 4, toRow: 5 } }, scrollEvent, gridStub); expect(setSelectedRangeSpy).toHaveBeenCalledWith([{ @@ -511,7 +511,7 @@ describe('SlickRowSelectionModel Plugin', () => { accelerateInterval: 5 }); plugin.init(gridStub); - const scrollEvent = addJQueryEventPropagation(new Event('scroll')); + const scrollEvent = addVanillaEventPropagation(new Event('scroll')); plugin.getCellRangeSelector()!.onCellRangeSelected.notify({ range: { fromCell: 2, fromRow: 3, toCell: 4, toRow: 5 } }, scrollEvent, gridStub); expect(setSelectedRangeSpy).toHaveBeenCalledWith([{ @@ -525,7 +525,7 @@ describe('SlickRowSelectionModel Plugin', () => { mockGridOptions.multiSelect = false; plugin.init(gridStub); - const scrollEvent = addJQueryEventPropagation(new Event('scroll')); + const scrollEvent = addVanillaEventPropagation(new Event('scroll')); plugin.getCellRangeSelector()!.onCellRangeSelected.notify({ range: { fromCell: 2, fromRow: 3, toCell: 4, toRow: 5 } }, scrollEvent, gridStub); expect(setSelectedRangeSpy).not.toHaveBeenCalled(); @@ -536,7 +536,7 @@ describe('SlickRowSelectionModel Plugin', () => { mockGridOptions.multiSelect = false; plugin.init(gridStub); - const scrollEvent = addJQueryEventPropagation(new Event('scroll')); + const scrollEvent = addVanillaEventPropagation(new Event('scroll')); plugin.getCellRangeSelector()!.onBeforeCellRangeSelected.notify({ row: 2, cell: 4 }, scrollEvent, gridStub); expect(setActiveCellSpy).toHaveBeenCalledWith(2, 4); @@ -548,7 +548,7 @@ describe('SlickRowSelectionModel Plugin', () => { mockGridOptions.multiSelect = false; plugin.init(gridStub); - const scrollEvent = addJQueryEventPropagation(new Event('scroll')); + const scrollEvent = addVanillaEventPropagation(new Event('scroll')); plugin.getCellRangeSelector()!.onBeforeCellRangeSelected.notify({ row: 2, cell: 4 }, scrollEvent, gridStub); expect(setActiveCellSpy).not.toHaveBeenCalled(); @@ -561,7 +561,7 @@ describe('SlickRowSelectionModel Plugin', () => { mockGridOptions.multiSelect = false; plugin.init(gridStub); - const scrollEvent = addJQueryEventPropagation(new Event('scroll')); + const scrollEvent = addVanillaEventPropagation(new Event('scroll')); plugin.getCellRangeSelector()!.onBeforeCellRangeSelected.notify({ row: 2, cell: 1 }, scrollEvent, gridStub); expect(setActiveCellSpy).toHaveBeenCalledWith(2, 1); @@ -575,7 +575,7 @@ describe('SlickRowSelectionModel Plugin', () => { mockColumns.unshift({ id: '_move', field: '_move', behavior: 'selectAndMove' }); plugin.init(gridStub); - const scrollEvent = addJQueryEventPropagation(new Event('scroll')); + const scrollEvent = addVanillaEventPropagation(new Event('scroll')); plugin.getCellRangeSelector()!.onBeforeCellRangeSelected.notify({ row: 2, cell: 0 }, scrollEvent, gridStub); expect(setActiveCellSpy).not.toHaveBeenCalled(); diff --git a/packages/common/src/extensions/slickCellRangeSelector.ts b/packages/common/src/extensions/slickCellRangeSelector.ts index 641ef958b..962837169 100644 --- a/packages/common/src/extensions/slickCellRangeSelector.ts +++ b/packages/common/src/extensions/slickCellRangeSelector.ts @@ -349,7 +349,7 @@ export class SlickCellRangeSelector { protected handleDragStart(e: DOMMouseOrTouchEvent, dd: DragPosition) { const cellObj = this._grid.getCellFromEvent(e); - if (cellObj && this.onBeforeCellRangeSelected.notify(cellObj) !== false && this._grid.canCellBeSelected(cellObj.row, cellObj.cell)) { + if (cellObj && this.onBeforeCellRangeSelected.notify(cellObj).getReturnValue() !== false && this._grid.canCellBeSelected(cellObj.row, cellObj.cell)) { this._dragging = true; e.stopImmediatePropagation(); } diff --git a/packages/common/src/extensions/slickGridMenu.ts b/packages/common/src/extensions/slickGridMenu.ts index 77fb2f928..7a6f1f262 100644 --- a/packages/common/src/extensions/slickGridMenu.ts +++ b/packages/common/src/extensions/slickGridMenu.ts @@ -279,7 +279,7 @@ export class SlickGridMenu extends MenuBaseClass { // execute optional callback method defined by the user, if it returns false then we won't go further neither close the menu this.pubSubService.publish('onGridMenuMenuClose', callbackArgs); - if ((typeof this._gridMenuOptions?.onMenuClose === 'function' && this._gridMenuOptions.onMenuClose(event, callbackArgs) === false) || this.onMenuClose.notify(callbackArgs, null, this) === false) { + if ((typeof this._gridMenuOptions?.onMenuClose === 'function' && this._gridMenuOptions.onMenuClose(event, callbackArgs) === false) || this.onMenuClose.notify(callbackArgs, null, this).getReturnValue() === false) { return; } @@ -396,7 +396,7 @@ export class SlickGridMenu extends MenuBaseClass { // execute optional callback method defined by the user, if it returns false then we won't go further and not open the grid menu if (typeof e.stopPropagation === 'function') { this.pubSubService.publish('onGridMenuBeforeMenuShow', callbackArgs); - if ((typeof addonOptions?.onBeforeMenuShow === 'function' && addonOptions.onBeforeMenuShow(e, callbackArgs) === false) || this.onBeforeMenuShow.notify(callbackArgs, null, this)) { + if ((typeof addonOptions?.onBeforeMenuShow === 'function' && addonOptions.onBeforeMenuShow(e, callbackArgs) === false) || this.onBeforeMenuShow.notify(callbackArgs, null, this).getReturnValue() === false) { return; } } diff --git a/packages/common/src/extensions/slickRowMoveManager.ts b/packages/common/src/extensions/slickRowMoveManager.ts index f3616ae5f..d686a92dc 100644 --- a/packages/common/src/extensions/slickRowMoveManager.ts +++ b/packages/common/src/extensions/slickRowMoveManager.ts @@ -217,7 +217,7 @@ export class SlickRowMoveManager { insertBefore, }; - if (this._addonOptions?.onBeforeMoveRows?.(e, eventData) === false || this.onBeforeMoveRows.notify(eventData) === false) { + if (this._addonOptions?.onBeforeMoveRows?.(e, eventData) === false || this.onBeforeMoveRows.notify(eventData).getReturnValue() === false) { dd.canMove = false; } else { dd.canMove = true; diff --git a/packages/common/src/filters/__tests__/multipleSelectFilter.spec.ts b/packages/common/src/filters/__tests__/multipleSelectFilter.spec.ts index 1e3757d70..2d92bae5d 100644 --- a/packages/common/src/filters/__tests__/multipleSelectFilter.spec.ts +++ b/packages/common/src/filters/__tests__/multipleSelectFilter.spec.ts @@ -1,5 +1,5 @@ // import 3rd party lib multiple-select for the tests -import 'multiple-select-modified'; +import 'multiple-select-vanilla'; import { Filters } from '../filters.index'; import { Column, FilterArguments, GridOption, SlickGrid } from '../../interfaces/index'; @@ -73,6 +73,6 @@ describe('SelectFilter', () => { expect(spyGetHeaderRow).toHaveBeenCalled(); expect(filterCount).toBe(1); expect(filter.isMultipleSelect).toBe(true); - expect(filter.columnDef.filter.emptySearchTermReturnAllValues).toBeFalse(); + expect(filter.columnDef.filter!.emptySearchTermReturnAllValues).toBeFalse(); }); }); diff --git a/packages/common/src/filters/__tests__/selectFilter.spec.ts b/packages/common/src/filters/__tests__/selectFilter.spec.ts index e74300e28..3b5de795e 100644 --- a/packages/common/src/filters/__tests__/selectFilter.spec.ts +++ b/packages/common/src/filters/__tests__/selectFilter.spec.ts @@ -1,5 +1,5 @@ // import 3rd party lib multiple-select for the tests -import 'multiple-select-modified'; +import 'multiple-select-vanilla'; import { of, Subject } from 'rxjs'; import { FieldType, OperatorType } from '../../enums/index'; @@ -46,6 +46,7 @@ describe('SelectFilter', () => { divContainer = document.createElement('div'); divContainer.innerHTML = template; + document.body.innerHTML = ''; document.body.appendChild(divContainer); spyGetHeaderRow = jest.spyOn(gridStub, 'getHeaderRowColumn').mockReturnValue(divContainer); @@ -143,7 +144,7 @@ describe('SelectFilter', () => { mockColumn.filter!.collection = [{ value: 'male', label: 'male' }, { value: 'female', label: 'female' }]; filter.init(filterArguments); - const filterElm = divContainer.querySelector('.ms-filter.search-filter.filter-gender .placeholder') as HTMLSpanElement; + const filterElm = divContainer.querySelector('.ms-filter.search-filter.filter-gender .ms-placeholder') as HTMLSpanElement; expect(filterElm.innerHTML).toBe(testValue); }); @@ -154,14 +155,13 @@ describe('SelectFilter', () => { filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li input[type=checkbox]`); - const filterOkElm = divContainer.querySelector(`[name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li input[type=checkbox]`); + const filterOkElm = divContainer.querySelector(`[data-name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; filterBtnElm.click(); - // we can use property "checked" or dispatch an event - filterListElm[0].checked = true; - filterListElm[0].dispatchEvent(new Event('click')); + filter.msInstance?.setSelects(['male']); filterOkElm.click(); + filter.msInstance?.close(); const filterFilledElms = divContainer.querySelectorAll('.ms-parent.ms-filter.search-filter.filter-gender.filled'); expect(filterListElm.length).toBe(2); @@ -175,10 +175,11 @@ describe('SelectFilter', () => { filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li input[type=checkbox]`); - const filterOkElm = divContainer.querySelector(`[name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li input[type=checkbox]`); + const filterOkElm = divContainer.querySelector(`[data-name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; filterBtnElm.click(); filterOkElm.click(); + filter.msInstance?.close(); const filterFilledElms = divContainer.querySelectorAll('.ms-parent.ms-filter.search-filter.filter-gender.filled'); expect(filterListElm.length).toBe(2); @@ -192,15 +193,15 @@ describe('SelectFilter', () => { mockColumn.filter!.collection = ['male', 'female']; filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li input[type=checkbox]`); - const filterOkElm = divContainer.querySelector(`[name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li input[type=checkbox]`); + const filterOkElm = divContainer.querySelector(`[data-name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; filterBtnElm.click(); - // here we use "checked" property instead of dispatching an event - filterListElm[0].checked = true; - filterOkElm.click(); + filter.msInstance?.setSelects(['male']); + filter.msInstance?.close(); const filterFilledElms = divContainer.querySelectorAll('.ms-parent.ms-filter.search-filter.filter-gender.filled'); + expect(filterOkElm).toBeTruthy(); expect(filterListElm.length).toBe(2); expect(filterFilledElms.length).toBe(1); expect(spyCallback).toHaveBeenCalledWith(undefined, { columnDef: mockColumn, operator: 'IN', searchTerms: ['male'], shouldTriggerQuery: true }); @@ -211,22 +212,23 @@ describe('SelectFilter', () => { mockColumn.filter!.collection = [{ value: 'male', label: 'male' }, { value: 'female', label: 'female' }]; const spyCallback = jest.spyOn(filterArguments, 'callback'); - filter.init(filterArguments); + filter.init({ ...filterArguments, columnDef: mockColumn }); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li input[type=checkbox]`); - const filterOkElm = divContainer.querySelector(`[name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li input[type=checkbox]`); + const filterOkElm = divContainer.querySelector(`[data-name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; filterBtnElm.click(); - filterListElm[0].checked = true; - filterOkElm.click(); + filter.msInstance?.setSelects(['male']); + filter.msInstance?.close(); const filterFilledElms = divContainer.querySelectorAll('.ms-parent.ms-filter.search-filter.filter-gender.filled'); + expect(filterOkElm).toBeTruthy(); expect(filterListElm.length).toBe(2); expect(filterFilledElms.length).toBe(1); expect(spyCallback).toHaveBeenCalledWith(undefined, { columnDef: mockColumn, operator: 'NIN', searchTerms: ['male'], shouldTriggerQuery: true }); }); - it('should have same value in "getValues" after being set in "setValues"', () => { + it('should have same value in "getValues" after being set in "setValues" a single string', () => { mockColumn.filter!.collection = [{ value: 'male', label: 'male' }, { value: 'female', label: 'female' }]; filter.init(filterArguments); filter.setValues('female'); @@ -236,6 +238,26 @@ describe('SelectFilter', () => { expect(values.length).toBe(1); }); + it('should have same value in "getValues" after being set in "setValues" with an array', () => { + mockColumn.filter!.collection = [{ value: 'male', label: 'male' }, { value: 'female', label: 'female' }]; + filter.init(filterArguments); + filter.setValues(['female']); + const values = filter.getValues(); + + expect(values).toEqual(['female']); + expect(values.length).toBe(1); + }); + + it('should provide boolean values and expect "getValues" to be converted to string', () => { + mockColumn.filter!.collection = [{ value: true, label: 'True' }, { value: false, label: 'False' }]; + filter.init(filterArguments); + filter.setValues([false]); + const values = filter.getValues(); + + expect(values).toEqual(['false']); + expect(values.length).toBe(1); + }); + it('should have empty array returned from "getValues" when nothing is set', () => { mockColumn.filter!.collection = [{ value: 'male', label: 'male' }, { value: 'female', label: 'female' }]; filter.init(filterArguments); @@ -259,11 +281,12 @@ describe('SelectFilter', () => { filterArguments.searchTerms = ['female']; filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li input[type=checkbox]`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li input[type=checkbox]`); const filterFilledElms = divContainer.querySelectorAll('.ms-parent.ms-filter.search-filter.filter-gender.filled'); - const filterOkElm = divContainer.querySelector(`[name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const filterOkElm = divContainer.querySelector(`[data-name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; filterBtnElm.click(); filterOkElm.click(); + filter.msInstance?.close(); expect(filterListElm.length).toBe(2); expect(filterFilledElms.length).toBe(1); @@ -278,11 +301,12 @@ describe('SelectFilter', () => { filterArguments.searchTerms = [false]; filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li input[type=checkbox]`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li input[type=checkbox]`); const filterFilledElms = divContainer.querySelectorAll('.ms-parent.ms-filter.search-filter.filter-gender.filled'); - const filterOkElm = divContainer.querySelector(`[name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const filterOkElm = divContainer.querySelector(`[data-name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; filterBtnElm.click(); filterOkElm.click(); + filter.msInstance?.close(); expect(filterListElm.length).toBe(2); expect(filterFilledElms.length).toBe(1); @@ -297,11 +321,12 @@ describe('SelectFilter', () => { filterArguments.searchTerms = [2]; filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li input[type=checkbox]`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li input[type=checkbox]`); const filterFilledElms = divContainer.querySelectorAll('.ms-parent.ms-filter.search-filter.filter-gender.filled'); - const filterOkElm = divContainer.querySelector(`[name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const filterOkElm = divContainer.querySelector(`[data-name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; filterBtnElm.click(); filterOkElm.click(); + filter.msInstance?.close(); expect(filterListElm.length).toBe(2); expect(filterFilledElms.length).toBe(1); @@ -316,11 +341,12 @@ describe('SelectFilter', () => { filterArguments.searchTerms = ['female']; filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li input[type=checkbox]`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li input[type=checkbox]`); const filterFilledElms = divContainer.querySelectorAll('.ms-parent.ms-filter.search-filter.filter-gender.filled'); - const filterOkElm = divContainer.querySelector(`[name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const filterOkElm = divContainer.querySelector(`[data-name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; filterBtnElm.click(); filterOkElm.click(); + filter.msInstance?.close(); expect(filterListElm.length).toBe(2); expect(filterFilledElms.length).toBe(1); @@ -339,8 +365,9 @@ describe('SelectFilter', () => { filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li span`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li span`); filterBtnElm.click(); + filter.msInstance?.close(); expect(filterListElm.length).toBe(3); expect(filterListElm[0].textContent).toBe('other'); @@ -364,7 +391,7 @@ describe('SelectFilter', () => { filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li span`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li span`); filterBtnElm.click(); expect(filterListElm.length).toBe(3); @@ -381,7 +408,7 @@ describe('SelectFilter', () => { filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li span`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li span`); filterBtnElm.click(); expect(filterListElm.length).toBe(1); @@ -400,7 +427,7 @@ describe('SelectFilter', () => { filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li span`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li span`); filterBtnElm.click(); expect(filterListElm.length).toBe(1); @@ -420,7 +447,7 @@ describe('SelectFilter', () => { filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li span`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li span`); filterBtnElm.click(); expect(filterListElm.length).toBe(2); @@ -437,7 +464,7 @@ describe('SelectFilter', () => { filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li span`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li span`); filterBtnElm.click(); expect(filterListElm.length).toBe(3); @@ -459,8 +486,9 @@ describe('SelectFilter', () => { filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li span`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li span`); filterBtnElm.click(); + filter.msInstance?.close(); expect(filterListElm.length).toBe(2); expect(filterListElm[0].innerHTML).toBe(' True'); @@ -479,7 +507,7 @@ describe('SelectFilter', () => { filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li span`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li span`); filterBtnElm.click(); expect(filterListElm.length).toBe(2); @@ -494,11 +522,12 @@ describe('SelectFilter', () => { filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li input[type=checkbox]`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li input[type=checkbox]`); const filterFilledElms = divContainer.querySelectorAll('.ms-parent.ms-filter.search-filter.filter-gender.filled'); - const filterOkElm = divContainer.querySelector(`[name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const filterOkElm = divContainer.querySelector(`[data-name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; filterBtnElm.click(); filterOkElm.click(); + filter.msInstance?.close(); expect(filterListElm.length).toBe(3); expect(filterFilledElms.length).toBe(1); @@ -515,11 +544,12 @@ describe('SelectFilter', () => { filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li input[type=checkbox]`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li input[type=checkbox]`); const filterFilledElms = divContainer.querySelectorAll('.ms-parent.ms-filter.search-filter.filter-gender.filled'); - const filterOkElm = divContainer.querySelector(`[name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const filterOkElm = divContainer.querySelector(`[data-name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; filterBtnElm.click(); filterOkElm.click(); + filter.msInstance?.close(); expect(filterListElm.length).toBe(3); expect(filterFilledElms.length).toBe(1); @@ -536,11 +566,12 @@ describe('SelectFilter', () => { filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li input[type=checkbox]`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li input[type=checkbox]`); const filterFilledElms = divContainer.querySelectorAll('.ms-parent.ms-filter.search-filter.filter-gender.filled'); - const filterOkElm = divContainer.querySelector(`[name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const filterOkElm = divContainer.querySelector(`[data-name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; filterBtnElm.click(); filterOkElm.click(); + filter.msInstance?.close(); expect(filterListElm.length).toBe(3); expect(filterFilledElms.length).toBe(1); @@ -596,8 +627,8 @@ describe('SelectFilter', () => { const filterSelectAllElm = divContainer.querySelector('.filter-gender .ms-select-all label span') as HTMLSpanElement; const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li span`); - const filterOkElm = divContainer.querySelector(`[name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li span`); + const filterOkElm = divContainer.querySelector(`[data-name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; const filterParentElm = divContainer.querySelector(`.ms-parent.ms-filter.search-filter.filter-gender button`) as HTMLButtonElement; filterBtnElm.click(); @@ -629,8 +660,8 @@ describe('SelectFilter', () => { const filterSelectAllElm = divContainer.querySelector('.filter-gender .ms-select-all label span') as HTMLSpanElement; const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li span`); - const filterOkElm = divContainer.querySelector(`[name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li span`); + const filterOkElm = divContainer.querySelector(`[data-name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; const filterParentElm = divContainer.querySelector(`.ms-parent.ms-filter.search-filter.filter-gender button`) as HTMLButtonElement; filterBtnElm.click(); @@ -653,11 +684,12 @@ describe('SelectFilter', () => { await filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li input[type=checkbox]`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li input[type=checkbox]`); const filterFilledElms = divContainer.querySelectorAll('.ms-parent.ms-filter.search-filter.filter-gender.filled'); - const filterOkElm = divContainer.querySelector(`[name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const filterOkElm = divContainer.querySelector(`[data-name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; filterBtnElm.click(); filterOkElm.click(); + filter.msInstance?.close(); expect(filterListElm.length).toBe(2); expect(filterFilledElms.length).toBe(1); @@ -675,11 +707,12 @@ describe('SelectFilter', () => { await filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li input[type=checkbox]`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li input[type=checkbox]`); const filterFilledElms = divContainer.querySelectorAll('.ms-parent.ms-filter.search-filter.filter-gender.filled'); - const filterOkElm = divContainer.querySelector(`[name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const filterOkElm = divContainer.querySelector(`[data-name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; filterBtnElm.click(); filterOkElm.click(); + filter.msInstance?.close(); expect(filterListElm.length).toBe(2); expect(filterFilledElms.length).toBe(1); @@ -702,11 +735,12 @@ describe('SelectFilter', () => { await filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li input[type=checkbox]`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li input[type=checkbox]`); const filterFilledElms = divContainer.querySelectorAll('.ms-parent.ms-filter.search-filter.filter-gender.filled'); - const filterOkElm = divContainer.querySelector(`[name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const filterOkElm = divContainer.querySelector(`[data-name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; filterBtnElm.click(); filterOkElm.click(); + filter.msInstance?.close(); expect(filterListElm.length).toBe(2); expect(filterFilledElms.length).toBe(1); @@ -725,7 +759,7 @@ describe('SelectFilter', () => { await filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li span`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li span`); filterBtnElm.click(); expect(filterListElm.length).toBe(3); @@ -755,7 +789,7 @@ describe('SelectFilter', () => { expect(renderSpy).toHaveBeenCalledWith(newCollection); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li span`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li span`); filterBtnElm.click(); expect(filterListElm.length).toBe(3); @@ -781,7 +815,7 @@ describe('SelectFilter', () => { expect(renderSpy).toHaveBeenCalledWith(mockColumn.filter!.collection); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li span`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li span`); filterBtnElm.click(); expect(filterListElm.length).toBe(3); @@ -868,7 +902,7 @@ describe('SelectFilter', () => { await filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li span`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li span`); filterBtnElm.click(); expect(filterListElm.length).toBe(3); @@ -887,11 +921,12 @@ describe('SelectFilter', () => { await filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li input[type=checkbox]`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li input[type=checkbox]`); const filterFilledElms = divContainer.querySelectorAll('.ms-parent.ms-filter.search-filter.filter-gender.filled'); - const filterOkElm = divContainer.querySelector(`[name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; + const filterOkElm = divContainer.querySelector(`[data-name=filter-gender].ms-drop .ms-ok-button`) as HTMLButtonElement; filterBtnElm.click(); filterOkElm.click(); + filter.msInstance?.close(); expect(filterListElm.length).toBe(2); expect(filterFilledElms.length).toBe(1); @@ -907,7 +942,7 @@ describe('SelectFilter', () => { await filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li input[type=checkbox]`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li input[type=checkbox]`); filterBtnElm.click(); expect(filterListElm.length).toBe(2); @@ -917,7 +952,7 @@ describe('SelectFilter', () => { mockCollection.push('other'); (mockColumn.filter!.collectionAsync as Subject).next(mockCollection); - const filterUpdatedListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li input[type=checkbox]`); + const filterUpdatedListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li input[type=checkbox]`); expect(filterUpdatedListElm.length).toBe(3); }); @@ -938,7 +973,7 @@ describe('SelectFilter', () => { await filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li input[type=checkbox]`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li input[type=checkbox]`); filterBtnElm.click(); expect(filterListElm.length).toBe(2); @@ -948,7 +983,7 @@ describe('SelectFilter', () => { mockCollection.deep.myCollection.push('other'); (mockColumn.filter!.collectionAsync as Subject).next(mockCollection.deep.myCollection); - const filterUpdatedListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li input[type=checkbox]`); + const filterUpdatedListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li input[type=checkbox]`); expect(filterUpdatedListElm.length).toBe(3); }); diff --git a/packages/common/src/filters/__tests__/selectFilterNoLibLoaded.spec.ts b/packages/common/src/filters/__tests__/selectFilterNoLibLoaded.spec.ts deleted file mode 100644 index e259219db..000000000 --- a/packages/common/src/filters/__tests__/selectFilterNoLibLoaded.spec.ts +++ /dev/null @@ -1,71 +0,0 @@ -// import 3rd party lib multiple-select for the tests -// import 'multiple-select-modified'; - -import { Column, FilterArguments, GridOption, SlickGrid } from '../../interfaces/index'; -import { CollectionService } from '../../services/collection.service'; -import { Filters } from '../filters.index'; -import { SelectFilter } from '../selectFilter'; -import { TranslateServiceStub } from '../../../../../test/translateServiceStub'; - -const containerId = 'demo-container'; - -// define a
container to simulate the grid container -const template = `
`; - -const gridOptionMock = { - enableFiltering: true, - enableFilterTrimWhiteSpace: true, -} as GridOption; - -const collectionServiceStub = { - -} as CollectionService; - -const gridStub = { - getOptions: () => gridOptionMock, - getColumns: jest.fn(), - getHeaderRowColumn: jest.fn(), - render: jest.fn(), -} as unknown as SlickGrid; - -describe('SelectFilter', () => { - let divContainer: HTMLDivElement; - let filter: SelectFilter; - let filterArguments: FilterArguments; - let mockColumn: Column; - let translateService: TranslateServiceStub; - - beforeEach(() => { - translateService = new TranslateServiceStub(); - - divContainer = document.createElement('div'); - divContainer.innerHTML = template; - document.body.appendChild(divContainer); - jest.spyOn(gridStub, 'getHeaderRowColumn').mockReturnValue(divContainer); - - mockColumn = { - id: 'gender', field: 'gender', filterable: true, - filter: { - model: Filters.select, - collection: [{ value: '', label: '' }, { value: 'male', label: 'male' }, { value: 'female', label: 'female' }] - } - }; - - filterArguments = { - grid: gridStub, - columnDef: mockColumn, - callback: jest.fn(), - filterContainerElm: gridStub.getHeaderRowColumn(mockColumn.id) - }; - - filter = new SelectFilter(translateService, collectionServiceStub); - }); - - afterEach(() => { - filter.destroy(); - }); - - it('should throw an error when multiple-select.js is not provided or imported', () => { - expect(() => filter.init(filterArguments)).toThrowError(`multiple-select.js was not found, make sure to read the HOWTO Wiki on how to install it.`); - }); -}); diff --git a/packages/common/src/filters/__tests__/singleSelectFilter.spec.ts b/packages/common/src/filters/__tests__/singleSelectFilter.spec.ts index bb35d3ab2..e0771e58e 100644 --- a/packages/common/src/filters/__tests__/singleSelectFilter.spec.ts +++ b/packages/common/src/filters/__tests__/singleSelectFilter.spec.ts @@ -1,5 +1,5 @@ // import 3rd party lib multiple-select for the tests -import 'multiple-select-modified'; +import 'multiple-select-vanilla'; import { Filters } from '../filters.index'; import { Column, FilterArguments, GridOption, SlickGrid } from '../../interfaces/index'; @@ -83,7 +83,7 @@ describe('SelectFilter', () => { filterArguments.searchTerms = ['']; filter.init(filterArguments); - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li input[type=radio]`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li input[type=radio]`); const filterFilledElms = divContainer.querySelectorAll('.ms-parent.ms-filter.search-filter.filter-gender.filled'); expect(filterListElm.length).toBe(3); @@ -96,11 +96,11 @@ describe('SelectFilter', () => { filter.init(filterArguments); const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li input[type=radio]`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li input[type=radio]`); filterBtnElm.click(); - filterListElm[1].checked = true; - filterListElm[1].dispatchEvent(new CustomEvent('click')); + filter.msInstance?.setSelects(['female']); + filter.msInstance?.close(); const filterFilledElms = divContainer.querySelectorAll('.ms-parent.ms-filter.search-filter.filter-gender.filled'); expect(filterListElm.length).toBe(2); @@ -126,8 +126,8 @@ describe('SelectFilter', () => { jest.runAllTimers(); // fast-forward timer const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li span`); - const filterOkElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop .ms-ok-button`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li span`); + const filterOkElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop .ms-ok-button`); const filterSelectAllElm = divContainer.querySelectorAll('.filter-gender .ms-select-all label span'); filterBtnElm.click(); @@ -157,7 +157,7 @@ describe('SelectFilter', () => { jest.runAllTimers(); // fast-forward timer const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement; - const filterListElm = divContainer.querySelectorAll(`[name=filter-gender].ms-drop ul>li span`); + const filterListElm = divContainer.querySelectorAll(`[data-name=filter-gender].ms-drop ul>li span`); filterBtnElm.click(); expect(filterListElm.length).toBe(3); diff --git a/packages/common/src/filters/__tests__/sliderRangeFilter.spec.ts b/packages/common/src/filters/__tests__/sliderRangeFilter.spec.ts index b6f16c145..a163657b1 100644 --- a/packages/common/src/filters/__tests__/sliderRangeFilter.spec.ts +++ b/packages/common/src/filters/__tests__/sliderRangeFilter.spec.ts @@ -260,7 +260,7 @@ describe('SliderRangeFilter', () => { const filterLowestElm = divContainer.querySelector('.lowest-range-duration') as HTMLInputElement; const filterHighestElm = divContainer.querySelector('.highest-range-duration') as HTMLInputElement; - expect(consoleSpy).toHaveBeenCalledWith('[Slickgrid-Universal] All filter.params are moving to "filterOptions" for better typing support and "params" will be deprecated in future release.'); + expect(consoleSpy).toHaveBeenCalledWith('[Slickgrid-Universal] All filter.params from Slider Filter are moving to "filterOptions" for better typing support and "params" will be deprecated in future release.'); expect(filterLowestElm.textContent).toBe('4'); expect(filterHighestElm.textContent).toBe('69'); expect(filter.currentValues).toEqual([4, 69]); diff --git a/packages/common/src/filters/autocompleterFilter.ts b/packages/common/src/filters/autocompleterFilter.ts index 70c55a814..fba936be2 100644 --- a/packages/common/src/filters/autocompleterFilter.ts +++ b/packages/common/src/filters/autocompleterFilter.ts @@ -2,7 +2,7 @@ import * as autocompleter_ from 'autocompleter'; const autocomplete = (autocompleter_ && autocompleter_['default'] || autocompleter_) as (settings: AutocompleteSettings) => AutocompleteResult; // patch for rollup import type { AutocompleteItem, AutocompleteResult, AutocompleteSettings } from 'autocompleter'; -import { isPrimmitive, toKebabCase, toSentenceCase } from '@slickgrid-universal/utils'; +import { isPrimitiveValue, toKebabCase, toSentenceCase } from '@slickgrid-universal/utils'; import { FieldType, @@ -402,7 +402,7 @@ export class AutocompleterFilter implements Fi // the kradeen autocomplete lib only works with label/value pair, make sure that our array is in accordance if (Array.isArray(collection)) { - if (collection.every(x => isPrimmitive(x))) { + if (collection.every(x => isPrimitiveValue(x))) { // when detecting an array of primitives, we have to remap it to an array of value/pair objects collection = collection.map(c => ({ label: c, value: c })); } else { diff --git a/packages/common/src/filters/filterUtilities.ts b/packages/common/src/filters/filterUtilities.ts index 556723473..3e3f0b12c 100644 --- a/packages/common/src/filters/filterUtilities.ts +++ b/packages/common/src/filters/filterUtilities.ts @@ -27,16 +27,16 @@ export function buildSelectOperator(optionValues: Array<{ operator: OperatorStri } /** - * Get option from filter.params PR filter.filterOptions + * Get option from filter.params OR filter.filterOptions * @deprecated this should be removed when slider filterParams are replaced by filterOptions */ -export function getFilterOptionByName(columnFilter: ColumnFilter, optionName: K, defaultValue?: any): T[K] | undefined { +export function getFilterOptionByName(columnFilter: ColumnFilter, optionName: K, defaultValue?: any, filterName = 'Slider'): T[K] | undefined { let outValue; if (columnFilter.filterOptions?.[optionName] !== undefined) { outValue = (columnFilter.filterOptions as T)[optionName]; } else if (columnFilter?.params?.[optionName] !== undefined) { - console.warn(`[Slickgrid-Universal] All filter.params are moving to "filterOptions" for better typing support and "params" will be deprecated in future release.`); + console.warn(`[Slickgrid-Universal] All filter.params from ${filterName} Filter are moving to "filterOptions" for better typing support and "params" will be deprecated in future release.`); outValue = columnFilter?.params?.[optionName]; } return outValue ?? defaultValue; diff --git a/packages/common/src/filters/selectFilter.ts b/packages/common/src/filters/selectFilter.ts index cab6fccd2..d630ac5bd 100644 --- a/packages/common/src/filters/selectFilter.ts +++ b/packages/common/src/filters/selectFilter.ts @@ -1,3 +1,6 @@ +import { multipleSelect, MultipleSelectInstance, MultipleSelectOption, OptionRowData } from 'multiple-select-vanilla'; +import { isPrimitiveValue } from '@slickgrid-universal/utils'; + import { Constants } from '../constants'; import { type OperatorString, OperatorType, type SearchTerm, } from '../enums/index'; import type { @@ -10,35 +13,35 @@ import type { FilterCallback, GridOption, Locale, - MultipleSelectOption, SlickGrid, } from './../interfaces/index'; import type { CollectionService } from '../services/collection.service'; import { collectionObserver, propertyObserver } from '../services/observers'; import { getDescendantProperty, getTranslationPrefix, unsubscribeAll } from '../services/utilities'; -import { buildSelectEditorOrFilterDomElement, type RxJsFacade, type Subscription, type TranslaterService } from '../services/index'; +import { buildMultipleSelectDataCollection, emptyElement, type RxJsFacade, sanitizeTextByAvailableSanitizer, type Subscription, type TranslaterService } from '../services/index'; import { renderCollectionOptionsAsync } from './filterUtilities'; export class SelectFilter implements Filter { protected _isMultipleSelect = true; protected _collectionLength = 0; protected _locales!: Locale; + protected _msInstance?: MultipleSelectInstance; protected _shouldTriggerQuery = true; /** DOM Element Name, useful for auto-detecting positioning (dropup / dropdown) */ elementName!: string; /** Filter Multiple-Select options */ - filterElmOptions!: MultipleSelectOption; + filterElmOptions!: Partial; - /** The JQuery DOM element */ - $filterElm: any; + /** The DOM element */ + filterElm?: HTMLElement; grid!: SlickGrid; searchTerms: SearchTerm[] | undefined; columnDef!: Column; callback!: FilterCallback; - defaultOptions!: MultipleSelectOption; + defaultOptions!: Partial; isFilled = false; labelName!: string; labelPrefixName!: string; @@ -90,6 +93,10 @@ export class SelectFilter implements Filter { return this._isMultipleSelect; } + get msInstance() { + return this._msInstance; + } + /** Getter for the Filter Operator */ get operator(): OperatorType | OperatorString { return this.columnFilter?.operator ?? this.defaultOperator; @@ -102,9 +109,7 @@ export class SelectFilter implements Filter { } } - /** - * Initialize the filter template - */ + /** Initialize the filter template */ init(args: FilterArguments): Promise { if (!args) { throw new Error('[Slickgrid-Universal] A filter must always have an "init()" with valid arguments.'); @@ -121,11 +126,11 @@ export class SelectFilter implements Filter { } this.enableTranslateLabel = this.columnFilter?.enableTranslateLabel ?? false; - this.labelName = this.customStructure && this.customStructure.label || 'label'; - this.labelPrefixName = this.customStructure && this.customStructure.labelPrefix || 'labelPrefix'; - this.labelSuffixName = this.customStructure && this.customStructure.labelSuffix || 'labelSuffix'; - this.optionLabel = this.customStructure && this.customStructure.optionLabel || 'value'; - this.valueName = this.customStructure && this.customStructure.value || 'value'; + this.labelName = this.customStructure?.label ?? 'label'; + this.labelPrefixName = this.customStructure?.labelPrefix ?? 'labelPrefix'; + this.labelSuffixName = this.customStructure?.labelSuffix ?? 'labelSuffix'; + this.optionLabel = this.customStructure?.optionLabel ?? 'value'; + this.valueName = this.customStructure?.value ?? 'value'; if (this.enableTranslateLabel && (!this.translaterService || typeof this.translaterService.translate !== 'function')) { throw new Error(`[select-filter] The Translate Service is required for the Select Filter to work correctly when "enableTranslateLabel" is set.`); @@ -138,8 +143,8 @@ export class SelectFilter implements Filter { this.initMultipleSelectTemplate(); // add placeholder when found - let placeholder = this.gridOptions && this.gridOptions.defaultFilterPlaceholder || ''; - if (this.columnFilter && this.columnFilter.placeholder) { + let placeholder = this.gridOptions?.defaultFilterPlaceholder || ''; + if (this.columnFilter?.placeholder) { placeholder = this.columnFilter.placeholder; } this.defaultOptions.placeholder = placeholder || ''; @@ -183,14 +188,13 @@ export class SelectFilter implements Filter { }); } - /** - * Clear the filter values - */ + /** Clear the filter values */ clear(shouldTriggerQuery = true) { - if (this.$filterElm && this.$filterElm.multipleSelect && this._collectionLength > 0) { + if (this._msInstance && this._collectionLength > 0) { // reload the filter element by it's id, to make sure it's still a valid element (because of some issue in the GraphQL example) - this.$filterElm.multipleSelect('setSelects', []); - this.$filterElm.removeClass('filled').siblings('div .search-filter').removeClass('filled'); + this._msInstance.setSelects([]); + this.filterElm?.classList.remove('filled'); + this._msInstance?.getParentElement()?.classList.remove('filled'); this.searchTerms = []; this._shouldTriggerQuery = shouldTriggerQuery; this.callback(undefined, { columnDef: this.columnDef, clearFilterTriggered: true, shouldTriggerQuery: this._shouldTriggerQuery }); @@ -198,17 +202,10 @@ export class SelectFilter implements Filter { } } - /** - * destroy the filter - */ + /** destroy the filter */ destroy() { - if (this.$filterElm && typeof this.$filterElm.multipleSelect === 'function') { - this.$filterElm.multipleSelect('destroy'); - this.$filterElm.remove(); - const elementClassName = this.elementName.toString().replace('.', '\\.'); // make sure to escape any dot "." from CSS class to avoid console error - $(`[name=${elementClassName}].ms-drop`).remove(); - } - this.$filterElm = null; + this._msInstance?.destroy(); + this.filterElm?.remove(); // unsubscribe all the possible Observables if RxJS was used unsubscribeAll(this.subscriptions); @@ -219,19 +216,19 @@ export class SelectFilter implements Filter { * @params selected items */ getValues(): any[] { - if (this.$filterElm && typeof this.$filterElm.multipleSelect === 'function') { - return this.$filterElm.multipleSelect('getSelects'); - } - return []; + return this._msInstance?.getSelects() ?? []; } /** Set value(s) on the DOM element */ setValues(values: SearchTerm | SearchTerm[], operator?: OperatorType | OperatorString) { - if (values && this.$filterElm && typeof this.$filterElm.multipleSelect === 'function') { - values = Array.isArray(values) ? values : [values]; - this.$filterElm.multipleSelect('setSelects', values); + if (values !== undefined && this._msInstance) { + values = Array.isArray(values) + ? values.every(x => isPrimitiveValue(x)) ? values.map(String) : values + : [values]; + this._msInstance.setSelects(values); } this.updateFilterStyle(this.getValues().length > 0); + // set the operator when defined this.operator = operator || this.defaultOperator; } @@ -251,7 +248,7 @@ export class SelectFilter implements Filter { // user might want to filter certain items of the collection if (this.columnFilter && this.columnFilter.collectionFilterBy) { const filterBy = this.columnFilter.collectionFilterBy; - const filterCollectionBy = this.columnFilter.collectionOptions && this.columnFilter.collectionOptions.filterResultAfterEachPass || null; + const filterCollectionBy = this.columnFilter.collectionOptions?.filterResultAfterEachPass || null; outputCollection = this.collectionService.filterCollection(outputCollection, filterBy, filterCollectionBy); } @@ -344,7 +341,7 @@ export class SelectFilter implements Filter { newCollection = this.sortCollection(newCollection); // step 1, create HTML DOM element - const selectBuildResult = buildSelectEditorOrFilterDomElement( + const selectBuildResult = buildMultipleSelectDataCollection( 'filter', newCollection, this.columnDef, @@ -357,7 +354,7 @@ export class SelectFilter implements Filter { // step 2, create the DOM Element of the filter & pre-load search terms // we will later also subscribe to the onClose event to filter the data whenever that event is triggered - this.createFilterElement(selectBuildResult.selectElement); + this.createFilterElement(selectBuildResult.selectElement, selectBuildResult.dataCollection); this._collectionLength = newCollection.length; } @@ -377,77 +374,70 @@ export class SelectFilter implements Filter { } /** - * From the Select DOM Element created earlier, create a Multiple/Single Select Filter using the jQuery multiple-select.js lib + * From the Select DOM Element created earlier, create a Multiple/Single Select Filter using the multiple-select-vanilla.js lib * @param {Object} selectElement */ - protected createFilterElement(selectElement: HTMLSelectElement) { + protected createFilterElement(selectElement: HTMLSelectElement, dataCollection: OptionRowData[]) { const columnId = this.columnDef?.id ?? ''; // provide the name attribute to the DOM element which will be needed to auto-adjust drop position (dropup / dropdown) this.elementName = `filter-${columnId}`; this.defaultOptions.name = this.elementName; - $(this.filterContainerElm).empty(); + emptyElement(this.filterContainerElm); // create the DOM element & add an ID and filter class - this.$filterElm = $(selectElement); - if (typeof this.$filterElm.multipleSelect !== 'function') { - throw new Error(`multiple-select.js was not found, make sure to read the HOWTO Wiki on how to install it.`); - } - this.$filterElm.data('columnId', columnId); + this.filterElm = selectElement; + this.filterElm.dataset.columnId = `${columnId}`; // if there's a search term, we will add the "filled" class for styling purposes this.updateFilterStyle(this.isFilled); // append the new DOM element to the header row - if (this.$filterElm && typeof this.$filterElm.appendTo === 'function') { - this.$filterElm.appendTo(this.filterContainerElm); - } + this.filterContainerElm.appendChild(selectElement); // merge options & attach multiSelect const filterOptions: MultipleSelectOption = (this.columnFilter) ? this.columnFilter.filterOptions : {}; - this.filterElmOptions = { ...this.defaultOptions, ...(filterOptions as MultipleSelectOption) }; - this.$filterElm = this.$filterElm.multipleSelect(this.filterElmOptions); + this.filterElmOptions = { ...this.defaultOptions, ...(filterOptions as MultipleSelectOption), data: dataCollection }; + this._msInstance = multipleSelect(selectElement, this.filterElmOptions) as MultipleSelectInstance; } protected initMultipleSelectTemplate() { const isTranslateEnabled = this.gridOptions?.enableTranslate ?? false; + const columnId = this.columnDef?.id ?? ''; // default options used by this Filter, user can overwrite any of these by passing "otions" - const options: MultipleSelectOption = { + const options = { autoAdjustDropHeight: true, autoAdjustDropPosition: true, autoAdjustDropWidthByTextSize: true, + name: `${columnId}`, container: 'body', filter: false, // input search term on top of the select option list maxHeight: 275, single: true, - textTemplate: ($elm) => { - // are we rendering HTML code? by default it is sanitized and won't be rendered - const isRenderHtmlEnabled = this.columnDef && this.columnDef.filter && this.columnDef.filter.enableRenderHtml || false; - return isRenderHtmlEnabled ? $elm.text() : $elm.html(); - }, + sanitizer: (dirtyHtml: string) => sanitizeTextByAvailableSanitizer(this.gridOptions, dirtyHtml), // we will subscribe to the onClose event for triggering our callback // also add/remove "filled" class for styling purposes onClose: () => this.onTriggerEvent() - }; + } as MultipleSelectOption; if (this._isMultipleSelect) { options.single = false; - options.okButton = true; - options.addTitle = true; // show tooltip of all selected items while hovering the filter + options.showOkButton = true; + options.displayTitle = true; // show tooltip of all selected items while hovering the filter const translationPrefix = getTranslationPrefix(this.gridOptions); - options.countSelected = (isTranslateEnabled && this.translaterService?.translate) ? this.translaterService.translate(`${translationPrefix}X_OF_Y_SELECTED`) : this._locales?.TEXT_X_OF_Y_SELECTED; - options.allSelected = (isTranslateEnabled && this.translaterService?.translate) ? this.translaterService.translate(`${translationPrefix}ALL_SELECTED`) : this._locales?.TEXT_ALL_SELECTED; + options.countSelectedText = (isTranslateEnabled && this.translaterService?.translate) ? this.translaterService.translate(`${translationPrefix}X_OF_Y_SELECTED`) : this._locales?.TEXT_X_OF_Y_SELECTED; + options.allSelectedText = (isTranslateEnabled && this.translaterService?.translate) ? this.translaterService.translate(`${translationPrefix}ALL_SELECTED`) : this._locales?.TEXT_ALL_SELECTED; + options.noMatchesFoundText = (isTranslateEnabled && this.translaterService?.translate) ? this.translaterService.translate(`${translationPrefix}NO_MATCHES_FOUND`) : this._locales?.TEXT_NO_MATCHES_FOUND; options.okButtonText = (isTranslateEnabled && this.translaterService?.translate) ? this.translaterService.translate(`${translationPrefix}OK`) : this._locales?.TEXT_OK; options.selectAllText = (isTranslateEnabled && this.translaterService?.translate) ? this.translaterService.translate(`${translationPrefix}SELECT_ALL`) : this._locales?.TEXT_SELECT_ALL; - options.selectAllDelimiter = ['', '']; // remove default square brackets of default text "[Select All]" => "Select All" } this.defaultOptions = options; } protected onTriggerEvent() { - if (this.$filterElm) { + if (this._msInstance) { const selectedItems = this.getValues(); this.updateFilterStyle(Array.isArray(selectedItems) && selectedItems.length > 1 || (selectedItems.length === 1 && selectedItems[0] !== '')); this.searchTerms = selectedItems; @@ -461,11 +451,12 @@ export class SelectFilter implements Filter { protected updateFilterStyle(isFilled: boolean) { if (isFilled) { this.isFilled = true; - this.$filterElm?.addClass('filled').siblings('div .search-filter').addClass('filled'); + this.filterElm?.classList.add('filled'); + this._msInstance?.getParentElement()?.classList.add('filled'); } else { this.isFilled = false; - this.$filterElm.removeClass('filled'); - this.$filterElm.siblings('div .search-filter').removeClass('filled'); + this.filterElm?.classList.remove('filled'); + this._msInstance?.getParentElement()?.classList.remove('filled'); } } } diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 7a4c60041..38949703a 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -1,5 +1,3 @@ -import 'multiple-select-modified'; - import * as BackendUtilities from './services/backendUtility.service'; import * as Observers from './services/observers'; import * as ServiceUtilities from './services/utilities'; @@ -44,3 +42,7 @@ export { Enums } from './enums/enums.index'; const Utilities = { ...BackendUtilities, ...Observers, ...ServiceUtilities, ...SortUtilities, ...Utils, deepAssign: Utils.deepMerge }; export { Utilities }; export { SlickgridConfig } from './slickgrid-config'; + +// re-export MultipleSelectOption to avoid breaking previous code implementation +export { MultipleSelectOption } from 'multiple-select-vanilla'; + diff --git a/packages/common/src/interfaces/index.ts b/packages/common/src/interfaces/index.ts index 96248fd07..987074f9c 100644 --- a/packages/common/src/interfaces/index.ts +++ b/packages/common/src/interfaces/index.ts @@ -115,7 +115,6 @@ export * from './metrics.interface'; export * from './metricTexts.interface'; export * from './mouseOffsetViewport.interface'; export * from './multiColumnSort.interface'; -export * from './multipleSelectOption.interface'; export * from './onEventArgs.interface'; export * from './onValidationErrorResult.interface'; export * from './operatorDetail.interface'; diff --git a/packages/common/src/interfaces/locale.interface.ts b/packages/common/src/interfaces/locale.interface.ts index 9170b271f..fde9f362c 100644 --- a/packages/common/src/interfaces/locale.interface.ts +++ b/packages/common/src/interfaces/locale.interface.ts @@ -134,9 +134,12 @@ export interface Locale { /** Text "Less than or equal to" shown in Compound Editors/Filters as an Operator */ TEXT_LESS_THAN_OR_EQUAL_TO: string; - /** Text "Notelements found" that could show when nothing is returned in the Autocomplete */ + /** Text "No elements found" that could show when nothing is returned in the Autocomplete */ TEXT_NO_ELEMENTS_FOUND?: string; + /** Text "No matches found" that could show when nothing is returned in the multiple-select */ + TEXT_NO_MATCHES_FOUND?: string; + /** Text "Not contains" shown in Compound Editors/Filters as an Operator */ TEXT_NOT_CONTAINS: string; diff --git a/packages/common/src/interfaces/multipleSelectOption.interface.ts b/packages/common/src/interfaces/multipleSelectOption.interface.ts deleted file mode 100644 index af38bc51e..000000000 --- a/packages/common/src/interfaces/multipleSelectOption.interface.ts +++ /dev/null @@ -1,217 +0,0 @@ -export interface MultipleSelectOption { - /** Add a title. By default this option is set to false. */ - addTitle?: boolean; - - /** defaults to 20, when using "autoAdjustDropHeight" we might want to add a bottom (or top) padding instead of taking the entire available space */ - adjustHeightPadding?: number; - - /** Use animation, options are ('none', 'fade', 'slide'). By default this option is set to 'none' */ - animate?: 'none' | 'fade' | 'slide'; - - /** The text displays when the select all selected.By default this option is set to "All selected". */ - allSelected?: boolean | string; - - /** Auto-adjust the Drop menu height to fit with available space */ - autoAdjustDropHeight?: boolean; - - /** Auto-adjust the Drop position on the side with the most available space (dropup / dropdown) */ - autoAdjustDropPosition?: boolean; - - /** Drop menu to automatically set same width as the input select element */ - autoDropWidth?: boolean; - - /** Drop menu to automatically set it's width as the maximum available width of text */ - autoAdjustDropWidthByTextSize?: boolean; - - /** HTML container to use for the drop menu, e.g. 'body' */ - container?: string; - - /** `#` is replaced with the count of selected items, `%` is replaces with total items.By default this option is set to # of % selected. */ - countSelected?: string; - - /** Delimiter to use when display the selected options. By default this option is set to `,` */ - delimiter?: string; - - /** Display selected values on the element. By default this option is set to false. */ - displayValues?: boolean; - - /** Defaults to 26 (as per CSS), that is the "OK" button element height in pixels inside the drop when using multiple-selection */ - domElmOkButtonHeight?: number; - - /** Defaults to 26 (as per CSS), that is the select DOM element padding in pixels (that is not the drop but the select itself, how tall is it) */ - domElmSelectSidePadding?: number; - - /** Defaults to 39 (as per CSS), that is the DOM element of the "Select All" text area */ - domElmSelectAllHeight?: number; - - /** Define the width of the dropdown list */ - dropWidth?: number; - - /** Add `…` after selected options if minimumCountSelected is set. Overrides countSelected option.By default this option is set to false. */ - ellipsis?: boolean; - - /** Whether or not Multiple Select show a search field to search through checkbox items.By default this option is set to false. */ - filter?: boolean; - - /** Accept a filter by typing Enter on the keyboard. By default this option is set to false. */ - filterAcceptOnEnter?: boolean; - - /** Hide the option groupd checkboses. By default this is set to false. */ - hideOptgroupCheckboxes?: boolean; - - /** Whether or not Multiple Select open the select dropdown. */ - isOpen?: boolean; - - /** Keep the select dropdown always open.By default this option is set to false. */ - keepOpen?: boolean; - - /** Defaults to 250, define the maximum height property of the dropdown list. */ - maxHeight?: number; - - /** Defaults to 500, define the maximum width of the drop when using the "autoAdjustDropWidthByTextSize: true" flag. */ - maxWidth?: number; - - /** Define the minimum width of the drop when using the "autoAdjustDropWidthByTextSize: true" flag. */ - minWidth?: number; - - /** countSelected will be shown only if more than X items where selected.By default this option is set to 3. */ - minimumCountSelected?: number; - - /** Whether or not Multiple Select show multiple items in a row.By default this option is set to false. */ - multiple?: boolean; - - /** Multiple Select show multiple items width.By default this option is set to 80. */ - multipleWidth?: number; - - /** Provide a name to the multiple select element. By default this option is set to ''. */ - name?: string; - - /** Text to display when nothing is found. By default the text is "No matches found" */ - noMatchesFound?: string; - - /** Add an offset to the dropdown menu list. By default this option is set to 0. */ - offsetLeft?: number; - - /** Display the OK button at bottom of the list. By default this is set to false. */ - okButton?: boolean; - - /** Text to display on the bottom OK button. By default the text is "OK" */ - okButtonText?: string; - - /** A placeholder value can be defined and will be displayed until you select a item. */ - placeholder?: string; - - /** Defines the position of select dropdown, can only be bottom or top.By default this option is set to bottom. */ - position?: string; - - /** Whether or not Multiple Select show select all checkbox. */ - selectAll?: boolean; - - /** Multiple Select select all checkbox delimiter. By default this option is set to ['[',']']. */ - selectAllDelimiter?: string[]; - - /** Multiple Select select all checkbox text. By default this option is set to "Select all" */ - selectAllText?: string; - - /** Whether or not Multiple Select allows you to select only one option.By default this option is set to false. */ - single?: boolean; - - /** Defaults to false, when set to True it will use the "title" that were defined in each select option */ - useSelectOptionTitle?: boolean; - - /** Defaults to False, when set to True it will use the