diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 544f28309..74e5c12a7 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -20,7 +20,7 @@ on: - 'src/**/*' workflow_dispatch: -concurrency: build-release-${{ github.ref }} +concurrency: build-release jobs: ## Gather configuration required by other jobs diff --git a/CHANGELOG.md b/CHANGELOG.md index 5938259e5..5ee44319a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,84 @@ +# v2.19.1 (Thu Oct 19 2023) + +#### 🐛 Bug Fix + +- chore(button-area): add build file and jsdoc [#412](https://github.com/tyler-technologies-oss/forge/pull/412) ([@samrichardsontylertech](https://github.com/samrichardsontylertech)) + +#### Authors: 1 + +- Sam Richardson ([@samrichardsontylertech](https://github.com/samrichardsontylertech)) + +--- + +# v2.19.0 (Thu Oct 12 2023) + +#### 🚀 Enhancement + +- feat(button-area): create button area component [#326](https://github.com/tyler-technologies-oss/forge/pull/326) ([@samrichardsontylertech](https://github.com/samrichardsontylertech)) + +#### 🐛 Bug Fix + +- fix(expansion-panel): fixed a bug where calling `setOpenImmediate()` while an expand/collapse animation is already running would not cancel the active animation [#406](https://github.com/tyler-technologies-oss/forge/pull/406) ([@DRiFTy17](https://github.com/DRiFTy17)) +- fix(text-field): fixed a bug where the label was not being initialized properly when toggling density dynamically [#404](https://github.com/tyler-technologies-oss/forge/pull/404) ([@DRiFTy17](https://github.com/DRiFTy17)) +- fix(paginator): fixed how `offset` updates `pageIndex` along with updates to focus management [#402](https://github.com/tyler-technologies-oss/forge/pull/402) ([@DRiFTy17](https://github.com/DRiFTy17)) + +#### Authors: 2 + +- Kieran Nichols ([@DRiFTy17](https://github.com/DRiFTy17)) +- Sam Richardson ([@samrichardsontylertech](https://github.com/samrichardsontylertech)) + +--- + +# v2.18.1 (Wed Oct 04 2023) + +#### 🐛 Bug Fix + +- fix(paginator): fixed a bug where focus was lost when disabling the page buttons dynamically [#398](https://github.com/tyler-technologies-oss/forge/pull/398) ([@DRiFTy17](https://github.com/DRiFTy17)) +- feat(icon): move aria-hidden from host element to svg [#396](https://github.com/tyler-technologies-oss/forge/pull/396) ([@samrichardsontylertech](https://github.com/samrichardsontylertech)) + +#### Authors: 2 + +- Kieran Nichols ([@DRiFTy17](https://github.com/DRiFTy17)) +- Sam Richardson ([@samrichardsontylertech](https://github.com/samrichardsontylertech)) + +--- + +# v2.18.0 (Wed Sep 27 2023) + +#### 🚀 Enhancement + +- feat(autocomplete): added new `forceFilter()` method to allow for dynamically updating the options of an autocomplete [#391](https://github.com/tyler-technologies-oss/forge/pull/391) ([@DRiFTy17](https://github.com/DRiFTy17)) +- feat(list-dropdown): added configuration for setting secondary labels and for providing additional configuration to leading/trailing icon component elements [#393](https://github.com/tyler-technologies-oss/forge/pull/393) ([@DRiFTy17](https://github.com/DRiFTy17)) +- feat(keyboard-shortcut): add activate callback [#367](https://github.com/tyler-technologies-oss/forge/pull/367) ([@samrichardsontylertech](https://github.com/samrichardsontylertech)) + +#### 🐛 Bug Fix + +- fix(autocomplete): fixed a bug where the filter text was not getting removed when the clear button is clicked [#390](https://github.com/tyler-technologies-oss/forge/pull/390) ([@DRiFTy17](https://github.com/DRiFTy17)) +- feat(calendar): fixed a bug where an exception was being thrown if min/max was set and caused the current month to have no selectable dates. The calendar will now automatically move to the closest month with a selectable date [#392](https://github.com/tyler-technologies-oss/forge/pull/392) ([@DRiFTy17](https://github.com/DRiFTy17)) +- fix(chip-field): don't wrap leading/trailing icons [#386](https://github.com/tyler-technologies-oss/forge/pull/386) ([@MikeMatusz](https://github.com/MikeMatusz)) +- fix(date-picker, time-picker, date-range-picker): select mask on focus if shown [#385](https://github.com/tyler-technologies-oss/forge/pull/385) ([@MikeMatusz](https://github.com/MikeMatusz)) +- fix(chip-field): don't propagate click if disabled [#384](https://github.com/tyler-technologies-oss/forge/pull/384) ([@MikeMatusz](https://github.com/MikeMatusz)) + +#### Authors: 3 + +- Kieran Nichols ([@DRiFTy17](https://github.com/DRiFTy17)) +- Mike Matuszak ([@MikeMatusz](https://github.com/MikeMatusz)) +- Sam Richardson ([@samrichardsontylertech](https://github.com/samrichardsontylertech)) + +--- + +# v2.17.0 (Tue Aug 29 2023) + +#### 🚀 Enhancement + +- feat(popup): add popup ref to close event, emit from host [#365](https://github.com/tyler-technologies-oss/forge/pull/365) ([@MikeMatusz](https://github.com/MikeMatusz)) + +#### Authors: 1 + +- Mike Matuszak ([@MikeMatusz](https://github.com/MikeMatusz)) + +--- + # v2.16.6 (Mon Aug 21 2023) #### 🐛 Bug Fix diff --git a/forge.json b/forge.json index 2520f99d5..38a2218c3 100644 --- a/forge.json +++ b/forge.json @@ -1,5 +1,5 @@ { - "$schema": "./node_modules/@tylertech/forge-cli/schema.json", + "$schema": "./node_modules/@tylertech/forge-cli/config/schema.json", "packageOrg": "@tylertech", "packageName": "forge", "registry": "https://registry.npmjs.org/", diff --git a/package-lock.json b/package-lock.json index e95fe22e9..74fc61215 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@tylertech/forge", - "version": "3.0.0-next.1", + "version": "3.0.0-next.11", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@tylertech/forge", - "version": "3.0.0-next.1", + "version": "3.0.0-next.11", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -45,7 +45,7 @@ "@esm-bundle/chai": "^4.3.4-fix.0", "@open-wc/testing": "^3.1.8", "@tylertech-eslint/eslint-plugin": "^1.0.9", - "@tylertech/forge-cli": "^3.0.0-next.3", + "@tylertech/forge-cli": "^3.0.0-next.4", "@tylertech/forge-testing": "^2.0.0", "@tylertech/stylelint-rules": "^4.3.4", "@types/jasmine": "^3.10.3", @@ -98,6 +98,15 @@ "node": ">=12.17" } }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -1082,9 +1091,9 @@ } }, "node_modules/@csstools/css-parser-algorithms": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.2.0.tgz", - "integrity": "sha512-9BoQ/jSrPq4vv3b9jjLW+PNNv56KlDH5JMx5yASSNrCtvq70FCNZUjXRvbCeR9hYj9ZyhURtqpU/RFIgg6kiOw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.3.1.tgz", + "integrity": "sha512-xrvsmVUtefWMWQsGgFffqWSK03pZ1vfDki4IVIIUxxDKnGBzqNgv0A7SB1oXtVNEkcVO8xi1ZrTL29HhSu5kGA==", "dev": true, "funding": [ { @@ -1100,26 +1109,32 @@ "node": "^14 || ^16 || >=18" }, "peerDependencies": { - "@csstools/css-tokenizer": "^2.1.1" + "@csstools/css-tokenizer": "^2.2.0" } }, "node_modules/@csstools/css-tokenizer": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.1.1.tgz", - "integrity": "sha512-GbrTj2Z8MCTUv+52GE0RbFGM527xuXZ0Xa5g0Z+YN573uveS4G0qi6WNOMyz3yrFM/jaILTTwJ0+umx81EzqfA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.2.0.tgz", + "integrity": "sha512-wErmsWCbsmig8sQKkM6pFhr/oPha1bHfvxsUY5CYSQxwyhA9Ulrs8EqCgClhg4Tgg2XapVstGqSVcz0xOYizZA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "engines": { "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" } }, "node_modules/@csstools/media-query-list-parser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.0.tgz", - "integrity": "sha512-MXkR+TeaS2q9IkpyO6jVCdtA/bfpABJxIrfkLswThFN8EZZgI2RfAHhm6sDNDuYV25d5+b8Lj1fpTccIcSLPsQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.4.tgz", + "integrity": "sha512-V/OUXYX91tAC1CDsiY+HotIcJR+vPtzrX8pCplCpT++i8ThZZsq5F5dzZh/bDM3WUOjrvC1ljed1oSJxMfjqhw==", "dev": true, "funding": [ { @@ -1135,8 +1150,8 @@ "node": "^14 || ^16 || >=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^2.1.1", - "@csstools/css-tokenizer": "^2.1.1" + "@csstools/css-parser-algorithms": "^2.3.1", + "@csstools/css-tokenizer": "^2.2.0" } }, "node_modules/@csstools/selector-specificity": { @@ -1611,23 +1626,23 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.0.tgz", + "integrity": "sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", - "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.2", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -1665,9 +1680,9 @@ "dev": true }, "node_modules/@eslint/js": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz", - "integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1708,9 +1723,9 @@ "dev": true }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", + "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", @@ -1887,9 +1902,9 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", - "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", @@ -3316,9 +3331,9 @@ } }, "node_modules/@tylertech/forge-build-tools": { - "version": "3.0.0-next.5", - "resolved": "https://registry.npmjs.org/@tylertech/forge-build-tools/-/forge-build-tools-3.0.0-next.5.tgz", - "integrity": "sha512-Co5BZs1g9nhUPtczqbpovJHVjMj8C3sYzk1CFN/koJR33hCJJ8ENBNnIhUq2aPPJkd4WwtD7ZbI17ILjUIU2Kg==", + "version": "3.0.0-next.6", + "resolved": "https://registry.npmjs.org/@tylertech/forge-build-tools/-/forge-build-tools-3.0.0-next.6.tgz", + "integrity": "sha512-0DuV79UdObX1DsZqkTo73MZGAnR/OIpPf/MH4cZWwp6nuA6i0m1HV6FmimBR/DjZB6BTNxvk9QbunsgADuxEBg==", "dev": true, "dependencies": { "autoprefixer": "10.4.14", @@ -3353,6 +3368,28 @@ "typescript": "^4.6.3" } }, + "node_modules/@tylertech/forge-build-tools/node_modules/@csstools/selector-specificity": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.0.0.tgz", + "integrity": "sha512-hBI9tfBtuPIi885ZsZ32IMEU/5nlZH/KOVYJCOh7gyMxaVLGmLedYqFN6Ui1LXkI8JlC8IsuC0rF0btcRZKd5g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^6.0.13" + } + }, "node_modules/@tylertech/forge-build-tools/node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -3362,6 +3399,39 @@ "node": ">=8" } }, + "node_modules/@tylertech/forge-build-tools/node_modules/autoprefixer": { + "version": "10.4.14", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", + "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + ], + "dependencies": { + "browserslist": "^4.21.5", + "caniuse-lite": "^1.0.30001464", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/@tylertech/forge-build-tools/node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -3383,10 +3453,40 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@tylertech/forge-build-tools/node_modules/camelcase-keys": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-7.0.2.tgz", + "integrity": "sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==", + "dev": true, + "dependencies": { + "camelcase": "^6.3.0", + "map-obj": "^4.1.0", + "quick-lru": "^5.1.1", + "type-fest": "^1.2.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@tylertech/forge-build-tools/node_modules/camelcase-keys/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@tylertech/forge-build-tools/node_modules/chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -3396,14 +3496,14 @@ } }, "node_modules/@tylertech/forge-build-tools/node_modules/cosmiconfig": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", - "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.4.tgz", + "integrity": "sha512-SF+2P8+o/PTV05rgsAjDzL4OFdVXAulSfC/L19VaeVT7+tpOOSscCt2QLxDZ+CLxF2WOiq6y1K5asvs8qUJT/Q==", "dev": true, "dependencies": { - "import-fresh": "^3.2.1", + "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", + "parse-json": "^5.2.0", "path-type": "^4.0.0" }, "engines": { @@ -3411,32 +3511,39 @@ }, "funding": { "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@tylertech/forge-build-tools/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/@tylertech/forge-build-tools/node_modules/decamelize": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-5.0.1.tgz", + "integrity": "sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==", "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@tylertech/forge-build-tools/node_modules/glob": { - "version": "10.2.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.7.tgz", - "integrity": "sha512-jTKehsravOJo8IJxUGfZILnkvVJM/MOfHRs8QcXolVef2zNI9Tqyy5+SeuOAZd3upViEZQLyFpQhYiHLrMUNmA==", + "version": "10.3.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.4.tgz", + "integrity": "sha512-6LFElP3A+i/Q8XQKEvZjkEWEOTgAIALR9AO2rwT8bgPhDd1anmqDJDZ6lLddI4ehxxxR1S5RIqKe1uapMQfYaQ==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^2.0.3", "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2", - "path-scurry": "^1.7.0" + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" }, "bin": { "glob": "dist/cjs/src/bin.js" @@ -3477,60 +3584,54 @@ "node": ">=8" } }, - "node_modules/@tylertech/forge-build-tools/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/@tylertech/forge-build-tools/node_modules/known-css-properties": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.27.0.tgz", - "integrity": "sha512-uMCj6+hZYDoffuvAJjFAPz56E9uoowFHmTkqRtRq5WyC5Q6Cu/fTZKNQpX/RbzChBYLLl3lo8CjFZBAZXq9qFg==", - "dev": true - }, - "node_modules/@tylertech/forge-build-tools/node_modules/locate-path": { + "node_modules/@tylertech/forge-build-tools/node_modules/indent-string": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@tylertech/forge-build-tools/node_modules/known-css-properties": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.28.0.tgz", + "integrity": "sha512-9pSL5XB4J+ifHP0e0jmmC98OGC1nL8/JjS+fi6mnTlIf//yt/MfVLtKg7S6nCtj/8KTcWX7nRlY0XywoYY1ISQ==", + "dev": true + }, "node_modules/@tylertech/forge-build-tools/node_modules/meow": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", - "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "version": "10.1.5", + "resolved": "https://registry.npmjs.org/meow/-/meow-10.1.5.tgz", + "integrity": "sha512-/d+PQ4GKmGvM9Bee/DPa8z3mXs/pkvJE2KEThngVNOqtmljC6K7NMPxtc2JeZYTmpWb9k/TmxjeL18ez3h7vCw==", "dev": true, "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize": "^1.2.0", + "@types/minimist": "^1.2.2", + "camelcase-keys": "^7.0.0", + "decamelize": "^5.0.0", "decamelize-keys": "^1.1.0", "hard-rejection": "^2.1.0", "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" + "normalize-package-data": "^3.0.2", + "read-pkg-up": "^8.0.0", + "redent": "^4.0.0", + "trim-newlines": "^4.0.2", + "type-fest": "^1.2.2", + "yargs-parser": "^20.2.9" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@tylertech/forge-build-tools/node_modules/minimatch": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", - "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -3543,118 +3644,75 @@ } }, "node_modules/@tylertech/forge-build-tools/node_modules/minipass": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz", - "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", + "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==", "dev": true, "engines": { "node": ">=16 || 14 >=14.17" } }, - "node_modules/@tylertech/forge-build-tools/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/@tylertech/forge-build-tools/node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, "engines": { - "node": ">=6" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@tylertech/forge-build-tools/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@tylertech/forge-build-tools/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-6.0.0.tgz", + "integrity": "sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q==", "dev": true, "dependencies": { "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" + "normalize-package-data": "^3.0.2", + "parse-json": "^5.2.0", + "type-fest": "^1.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@tylertech/forge-build-tools/node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-8.0.0.tgz", + "integrity": "sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ==", "dev": true, "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" + "find-up": "^5.0.0", + "read-pkg": "^6.0.0", + "type-fest": "^1.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@tylertech/forge-build-tools/node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@tylertech/forge-build-tools/node_modules/read-pkg/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/@tylertech/forge-build-tools/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@tylertech/forge-build-tools/node_modules/resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "node_modules/@tylertech/forge-build-tools/node_modules/redent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-4.0.0.tgz", + "integrity": "sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==", "dev": true, "dependencies": { - "is-core-module": "^2.11.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "indent-string": "^5.0.0", + "strip-indent": "^4.0.0" }, - "bin": { - "resolve": "bin/resolve" + "engines": { + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@tylertech/forge-build-tools/node_modules/rimraf": { @@ -3675,19 +3733,10 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@tylertech/forge-build-tools/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, "node_modules/@tylertech/forge-build-tools/node_modules/signal-exit": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", - "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, "engines": { "node": ">=14" @@ -3720,23 +3769,38 @@ "node": ">=8" } }, + "node_modules/@tylertech/forge-build-tools/node_modules/strip-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", + "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@tylertech/forge-build-tools/node_modules/stylelint": { - "version": "15.7.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.7.0.tgz", - "integrity": "sha512-fQRwHwWuZsDn4ENyE9AsKkOkV9WlD2CmYiVDbdZPdS3iZh0ceypOn1EuwTNuZ8xTrHF+jVeIEzLtFFSlD/nJHg==", + "version": "15.10.3", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.10.3.tgz", + "integrity": "sha512-aBQMMxYvFzJJwkmg+BUUg3YfPyeuCuKo2f+LOw7yYbU8AZMblibwzp9OV4srHVeQldxvSFdz0/Xu8blq2AesiA==", "dev": true, "dependencies": { - "@csstools/css-parser-algorithms": "^2.2.0", - "@csstools/css-tokenizer": "^2.1.1", - "@csstools/media-query-list-parser": "^2.1.0", - "@csstools/selector-specificity": "^2.2.0", + "@csstools/css-parser-algorithms": "^2.3.1", + "@csstools/css-tokenizer": "^2.2.0", + "@csstools/media-query-list-parser": "^2.1.4", + "@csstools/selector-specificity": "^3.0.0", "balanced-match": "^2.0.0", "colord": "^2.9.3", "cosmiconfig": "^8.2.0", - "css-functions-list": "^3.1.0", + "css-functions-list": "^3.2.0", "css-tree": "^2.3.1", "debug": "^4.3.4", - "fast-glob": "^3.2.12", + "fast-glob": "^3.3.1", "fastest-levenshtein": "^1.0.16", "file-entry-cache": "^6.0.1", "global-modules": "^2.0.0", @@ -3747,14 +3811,13 @@ "import-lazy": "^4.0.0", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", - "known-css-properties": "^0.27.0", + "known-css-properties": "^0.28.0", "mathml-tag-names": "^2.1.3", - "meow": "^9.0.0", + "meow": "^10.1.5", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "picocolors": "^1.0.0", - "postcss": "^8.4.24", - "postcss-media-query-parser": "^0.2.3", + "postcss": "^8.4.27", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^6.0.0", "postcss-selector-parser": "^6.0.13", @@ -3766,11 +3829,10 @@ "supports-hyperlinks": "^3.0.0", "svg-tags": "^1.0.0", "table": "^6.8.1", - "v8-compile-cache": "^2.3.0", "write-file-atomic": "^5.0.1" }, "bin": { - "stylelint": "bin/stylelint.js" + "stylelint": "bin/stylelint.mjs" }, "engines": { "node": "^14.13.1 || >=16.0.0" @@ -3799,10 +3861,22 @@ "node": ">=14.18" } }, + "node_modules/@tylertech/forge-build-tools/node_modules/trim-newlines": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.1.1.tgz", + "integrity": "sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@tylertech/forge-build-tools/node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", "dev": true, "engines": { "node": ">=10" @@ -3825,13 +3899,13 @@ } }, "node_modules/@tylertech/forge-cli": { - "version": "3.0.0-next.3", - "resolved": "https://registry.npmjs.org/@tylertech/forge-cli/-/forge-cli-3.0.0-next.3.tgz", - "integrity": "sha512-S0OzUuLc8komAtF43EqpGkBmx9/7cZhYl3Ct9PTwUTzImpwOVd8jlh6X3NU9VZL7AdsrLQrqMm3mRqDAQK/sXg==", + "version": "3.0.0-next.4", + "resolved": "https://registry.npmjs.org/@tylertech/forge-cli/-/forge-cli-3.0.0-next.4.tgz", + "integrity": "sha512-QLUVSDhomyWcJex3w0ffKz9gfjnhZxOzQO1Ud44GZab9z4PBhTjQbS1xhISFBQDnquxy+IOpHyc1hTQB1boBAQ==", "dev": true, "dependencies": { "@custom-elements-manifest/analyzer": "^0.8.3", - "@tylertech/forge-build-tools": "3.0.0-next.5", + "@tylertech/forge-build-tools": "3.0.0-next.6", "autoprefixer": "^10.4.14", "browser-sync": "^2.29.3", "canonical-path": "^1.0.0", @@ -4220,9 +4294,9 @@ "dev": true }, "node_modules/@types/eslint": { - "version": "8.40.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.40.2.tgz", - "integrity": "sha512-PRVjQ4Eh9z9pmmtaq8nTjZjQwKFk7YIHIud3lRoKRBgUQjgjRmoGxxGEPXQkF+lH7QkHJRNr5F4aBgYCW0lqpQ==", + "version": "8.44.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.2.tgz", + "integrity": "sha512-sdPRb9K6iL5XZOmBubg8yiFp5yS/JdUDQsq5e6h95km91MCYMuvp7mh1fjPEYUhvHepKpZOjnEaMBR4PxjWDzg==", "dev": true, "dependencies": { "@types/estree": "*", @@ -6047,9 +6121,9 @@ "dev": true }, "node_modules/autoprefixer": { - "version": "10.4.14", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", - "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "version": "10.4.15", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.15.tgz", + "integrity": "sha512-KCuPB8ZCIqFdA4HwKXsvz7j6gvSDNhDP7WnUjBleRkKjPdvCmHFuQ77ocavI8FT6NdvlBnE2UFr2H4Mycn8Vew==", "dev": true, "funding": [ { @@ -6059,11 +6133,15 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "browserslist": "^4.21.5", - "caniuse-lite": "^1.0.30001464", + "browserslist": "^4.21.10", + "caniuse-lite": "^1.0.30001520", "fraction.js": "^4.2.0", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", @@ -6739,9 +6817,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "version": "4.21.10", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", + "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", "dev": true, "funding": [ { @@ -6751,13 +6829,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "caniuse-lite": "^1.0.30001517", + "electron-to-chromium": "^1.4.477", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.11" }, "bin": { "browserslist": "cli.js" @@ -7039,9 +7121,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001487", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001487.tgz", - "integrity": "sha512-83564Z3yWGqXsh2vaH/mhXfEM0wX+NlBCm1jYHOb97TrTWJEmPTccZgeLTPBUUb0PNVo+oomb7wkimZBIERClA==", + "version": "1.0.30001527", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001527.tgz", + "integrity": "sha512-YkJi7RwPgWtXVSgK4lG9AHH57nSzvvOp9MesgXmw4Q7n0C3H04L0foHqfxcmSAm5AcWb8dW9AYj2tR7/5GnddQ==", "dev": true, "funding": [ { @@ -8186,9 +8268,9 @@ } }, "node_modules/css-functions-list": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.1.0.tgz", - "integrity": "sha512-/9lCvYZaUbBGvYUgYGFJ4dcYiyqdhSjG7IPVluoV8A1ILjkF7ilmhp1OGUz8n+nmBcu0RNrQAzgD8B6FJbrt2w==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.0.tgz", + "integrity": "sha512-d/jBMPyYybkkLVypgtGv12R+pIFw4/f/IHtCTxWpZc8ofTYOPigIgmA6vu5rMHartZC+WuXhBUHfnyNUIQSYrg==", "dev": true, "engines": { "node": ">=12.22" @@ -8502,9 +8584,9 @@ } }, "node_modules/del": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-7.0.0.tgz", - "integrity": "sha512-tQbV/4u5WVB8HMJr08pgw0b6nG4RGt/tj+7Numvq+zqcvUFeMaIWWOUFltiU+6go8BSO2/ogsB4EasDaj0y68Q==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/del/-/del-7.1.0.tgz", + "integrity": "sha512-v2KyNk7efxhlyHpjEvfyxaAihKKK0nWCuf6ZtqZcFFpQRG0bJ12Qsr0RpvsICMjAAZ8DOVCxrlqpxISlMHC4Kg==", "dev": true, "dependencies": { "globby": "^13.1.2", @@ -8567,14 +8649,14 @@ } }, "node_modules/del/node_modules/globby": { - "version": "13.1.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.4.tgz", - "integrity": "sha512-iui/IiiW+QrJ1X1hKH5qwlMQyv34wJAYwH1vrf8b9kBA4sNiif3gKsMHa+BrdnOpEudWjpotfa7LrTzB1ERS/g==", + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", "dev": true, "dependencies": { "dir-glob": "^3.0.1", - "fast-glob": "^3.2.11", - "ignore": "^5.2.0", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", "merge2": "^1.4.1", "slash": "^4.0.0" }, @@ -8877,9 +8959,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.396", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.396.tgz", - "integrity": "sha512-pqKTdqp/c5vsrc0xUPYXTDBo9ixZuGY8es4ZOjjd6HD6bFYbu5QA09VoW3fkY4LF1T0zYk86lN6bZnNlBuOpdQ==", + "version": "1.4.508", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.508.tgz", + "integrity": "sha512-FFa8QKjQK/A5QuFr2167myhMesGrhlOBD+3cYNxO9/S4XzHEXesyTD/1/xF644gC8buFPz3ca6G1LOQD0tZrrg==", "dev": true }, "node_modules/emoji-regex": { @@ -9586,27 +9668,27 @@ } }, "node_modules/eslint": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz", - "integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.42.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.48.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -9616,7 +9698,6 @@ "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", @@ -9626,9 +9707,8 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -9668,9 +9748,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -9705,9 +9785,9 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -9760,12 +9840,12 @@ } }, "node_modules/espree": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", - "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" }, @@ -9963,9 +10043,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -10294,16 +10374,16 @@ "dev": true }, "node_modules/fraction.js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", - "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.6.tgz", + "integrity": "sha512-n2aZ9tNfYDwaHhvFTkhFErqOMIb8uyzSQ+vGJBjZyanAKZVbGUQ1sngfk9FdkBw7G26O7AgNjLcecLffD1c7eg==", "dev": true, "engines": { "node": "*" }, "funding": { "type": "patreon", - "url": "https://www.patreon.com/infusion" + "url": "https://github.com/sponsors/rawify" } }, "node_modules/fresh": { @@ -10899,9 +10979,9 @@ } }, "node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -12041,9 +12121,9 @@ } }, "node_modules/is-core-module": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", - "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -15219,9 +15299,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, "node_modules/nopt": { @@ -15814,17 +15894,17 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" }, "engines": { "node": ">= 0.8.0" @@ -15866,9 +15946,9 @@ } }, "node_modules/ora/node_modules/chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -16253,13 +16333,13 @@ "dev": true }, "node_modules/path-scurry": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.9.1.tgz", - "integrity": "sha512-UgmoiySyjFxP6tscZDgWGEAgsW5ok8W3F5CJDnnH2pozwSTGE6eH7vwTotMwATWA2r5xqdkKdxYPkwlJjAI/3g==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", "dev": true, "dependencies": { - "lru-cache": "^9.1.1", - "minipass": "^5.0.0 || ^6.0.0" + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -16278,9 +16358,9 @@ } }, "node_modules/path-scurry/node_modules/minipass": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.1.tgz", - "integrity": "sha512-Tenl5QPpgozlOGBiveNYHg2f6y+VpxsXRoIHFUVJuSmTonXRAE6q9b8Mp/O46762/2AlW4ye4Nkyvx0fgWDKbw==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", + "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==", "dev": true, "engines": { "node": ">=16 || 14 >=14.17" @@ -16510,9 +16590,9 @@ } }, "node_modules/postcss": { - "version": "8.4.24", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz", - "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==", + "version": "8.4.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz", + "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==", "dev": true, "funding": [ { @@ -17783,9 +17863,9 @@ "dev": true }, "node_modules/sass": { - "version": "1.63.4", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.63.4.tgz", - "integrity": "sha512-Sx/+weUmK+oiIlI+9sdD0wZHsqpbgQg8wSwSnGBjwb5GwqFhYNwwnI+UWZtLjKvKyFlKkatRK235qQ3mokyPoQ==", + "version": "1.66.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.66.1.tgz", + "integrity": "sha512-50c+zTsZOJVgFfTgwwEzkjA3/QACgdNsKueWPyAR0mRINIvLAStVQBbPg14iuqEQ74NPDbXzJARJ/O4SI1zftA==", "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", @@ -17837,9 +17917,9 @@ } }, "node_modules/sass/node_modules/immutable": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.0.tgz", - "integrity": "sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz", + "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==", "dev": true }, "node_modules/schema-utils": { @@ -20624,9 +20704,9 @@ "dev": true }, "node_modules/webpack": { - "version": "5.87.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.87.0.tgz", - "integrity": "sha512-GOu1tNbQ7p1bDEoFRs2YPcfyGs8xq52yyPBZ3m2VGnXGtV9MxjrkABHm4V9Ia280OefsSLzvbVoXcfLxjKY/Iw==", + "version": "5.88.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", + "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", @@ -20863,15 +20943,6 @@ "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", "dev": true }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -21173,6 +21244,12 @@ } } }, + "@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true + }, "@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -21985,21 +22062,21 @@ } }, "@csstools/css-parser-algorithms": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.2.0.tgz", - "integrity": "sha512-9BoQ/jSrPq4vv3b9jjLW+PNNv56KlDH5JMx5yASSNrCtvq70FCNZUjXRvbCeR9hYj9ZyhURtqpU/RFIgg6kiOw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.3.1.tgz", + "integrity": "sha512-xrvsmVUtefWMWQsGgFffqWSK03pZ1vfDki4IVIIUxxDKnGBzqNgv0A7SB1oXtVNEkcVO8xi1ZrTL29HhSu5kGA==", "dev": true }, "@csstools/css-tokenizer": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.1.1.tgz", - "integrity": "sha512-GbrTj2Z8MCTUv+52GE0RbFGM527xuXZ0Xa5g0Z+YN573uveS4G0qi6WNOMyz3yrFM/jaILTTwJ0+umx81EzqfA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.2.0.tgz", + "integrity": "sha512-wErmsWCbsmig8sQKkM6pFhr/oPha1bHfvxsUY5CYSQxwyhA9Ulrs8EqCgClhg4Tgg2XapVstGqSVcz0xOYizZA==", "dev": true }, "@csstools/media-query-list-parser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.0.tgz", - "integrity": "sha512-MXkR+TeaS2q9IkpyO6jVCdtA/bfpABJxIrfkLswThFN8EZZgI2RfAHhm6sDNDuYV25d5+b8Lj1fpTccIcSLPsQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.4.tgz", + "integrity": "sha512-V/OUXYX91tAC1CDsiY+HotIcJR+vPtzrX8pCplCpT++i8ThZZsq5F5dzZh/bDM3WUOjrvC1ljed1oSJxMfjqhw==", "dev": true }, "@csstools/selector-specificity": { @@ -22235,20 +22312,20 @@ } }, "@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.0.tgz", + "integrity": "sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==", "dev": true }, "@eslint/eslintrc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", - "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.2", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -22278,9 +22355,9 @@ } }, "@eslint/js": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz", - "integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", "dev": true }, "@esm-bundle/chai": { @@ -22318,9 +22395,9 @@ "dev": true }, "@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", + "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", "dev": true, "requires": { "@humanwhocodes/object-schema": "^1.2.1", @@ -22441,9 +22518,9 @@ "dev": true }, "@jridgewell/source-map": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", - "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", "dev": true, "requires": { "@jridgewell/gen-mapping": "^0.3.0", @@ -23679,9 +23756,9 @@ } }, "@tylertech/forge-build-tools": { - "version": "3.0.0-next.5", - "resolved": "https://registry.npmjs.org/@tylertech/forge-build-tools/-/forge-build-tools-3.0.0-next.5.tgz", - "integrity": "sha512-Co5BZs1g9nhUPtczqbpovJHVjMj8C3sYzk1CFN/koJR33hCJJ8ENBNnIhUq2aPPJkd4WwtD7ZbI17ILjUIU2Kg==", + "version": "3.0.0-next.6", + "resolved": "https://registry.npmjs.org/@tylertech/forge-build-tools/-/forge-build-tools-3.0.0-next.6.tgz", + "integrity": "sha512-0DuV79UdObX1DsZqkTo73MZGAnR/OIpPf/MH4cZWwp6nuA6i0m1HV6FmimBR/DjZB6BTNxvk9QbunsgADuxEBg==", "dev": true, "requires": { "autoprefixer": "10.4.14", @@ -23710,12 +23787,32 @@ "webpack-merge": "^5.9.0" }, "dependencies": { + "@csstools/selector-specificity": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.0.0.tgz", + "integrity": "sha512-hBI9tfBtuPIi885ZsZ32IMEU/5nlZH/KOVYJCOh7gyMxaVLGmLedYqFN6Ui1LXkI8JlC8IsuC0rF0btcRZKd5g==", + "dev": true + }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, + "autoprefixer": { + "version": "10.4.14", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", + "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "dev": true, + "requires": { + "browserslist": "^4.21.5", + "caniuse-lite": "^1.0.30001464", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + } + }, "brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -23731,45 +23828,61 @@ "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", "dev": true }, + "camelcase-keys": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-7.0.2.tgz", + "integrity": "sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==", + "dev": true, + "requires": { + "camelcase": "^6.3.0", + "map-obj": "^4.1.0", + "quick-lru": "^5.1.1", + "type-fest": "^1.2.1" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + } + } + }, "chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true }, "cosmiconfig": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", - "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.4.tgz", + "integrity": "sha512-SF+2P8+o/PTV05rgsAjDzL4OFdVXAulSfC/L19VaeVT7+tpOOSscCt2QLxDZ+CLxF2WOiq6y1K5asvs8qUJT/Q==", "dev": true, "requires": { - "import-fresh": "^3.2.1", + "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", + "parse-json": "^5.2.0", "path-type": "^4.0.0" } }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } + "decamelize": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-5.0.1.tgz", + "integrity": "sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==", + "dev": true }, "glob": { - "version": "10.2.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.7.tgz", - "integrity": "sha512-jTKehsravOJo8IJxUGfZILnkvVJM/MOfHRs8QcXolVef2zNI9Tqyy5+SeuOAZd3upViEZQLyFpQhYiHLrMUNmA==", + "version": "10.3.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.4.tgz", + "integrity": "sha512-6LFElP3A+i/Q8XQKEvZjkEWEOTgAIALR9AO2rwT8bgPhDd1anmqDJDZ6lLddI4ehxxxR1S5RIqKe1uapMQfYaQ==", "dev": true, "requires": { "foreground-child": "^3.1.0", "jackspeak": "^2.0.3", "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2", - "path-scurry": "^1.7.0" + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" } }, "globby": { @@ -23794,140 +23907,90 @@ } } }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", "dev": true }, "known-css-properties": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.27.0.tgz", - "integrity": "sha512-uMCj6+hZYDoffuvAJjFAPz56E9uoowFHmTkqRtRq5WyC5Q6Cu/fTZKNQpX/RbzChBYLLl3lo8CjFZBAZXq9qFg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.28.0.tgz", + "integrity": "sha512-9pSL5XB4J+ifHP0e0jmmC98OGC1nL8/JjS+fi6mnTlIf//yt/MfVLtKg7S6nCtj/8KTcWX7nRlY0XywoYY1ISQ==", "dev": true }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, "meow": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", - "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "version": "10.1.5", + "resolved": "https://registry.npmjs.org/meow/-/meow-10.1.5.tgz", + "integrity": "sha512-/d+PQ4GKmGvM9Bee/DPa8z3mXs/pkvJE2KEThngVNOqtmljC6K7NMPxtc2JeZYTmpWb9k/TmxjeL18ez3h7vCw==", "dev": true, "requires": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize": "^1.2.0", + "@types/minimist": "^1.2.2", + "camelcase-keys": "^7.0.0", + "decamelize": "^5.0.0", "decamelize-keys": "^1.1.0", "hard-rejection": "^2.1.0", "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" + "normalize-package-data": "^3.0.2", + "read-pkg-up": "^8.0.0", + "redent": "^4.0.0", + "trim-newlines": "^4.0.2", + "type-fest": "^1.2.2", + "yargs-parser": "^20.2.9" } }, "minimatch": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", - "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "requires": { "brace-expansion": "^2.0.1" } }, "minipass": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz", - "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", + "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==", "dev": true }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true }, "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-6.0.0.tgz", + "integrity": "sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q==", "dev": true, "requires": { "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } + "normalize-package-data": "^3.0.2", + "parse-json": "^5.2.0", + "type-fest": "^1.0.1" } }, "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-8.0.0.tgz", + "integrity": "sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ==", "dev": true, "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "dependencies": { - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - } + "find-up": "^5.0.0", + "read-pkg": "^6.0.0", + "type-fest": "^1.0.1" } }, - "resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "redent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-4.0.0.tgz", + "integrity": "sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==", "dev": true, "requires": { - "is-core-module": "^2.11.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "indent-string": "^5.0.0", + "strip-indent": "^4.0.0" } }, "rimraf": { @@ -23939,16 +24002,10 @@ "glob": "^10.2.5" } }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, "signal-exit": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", - "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true }, "slash": { @@ -23966,23 +24023,32 @@ "ansi-regex": "^5.0.1" } }, + "strip-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", + "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==", + "dev": true, + "requires": { + "min-indent": "^1.0.1" + } + }, "stylelint": { - "version": "15.7.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.7.0.tgz", - "integrity": "sha512-fQRwHwWuZsDn4ENyE9AsKkOkV9WlD2CmYiVDbdZPdS3iZh0ceypOn1EuwTNuZ8xTrHF+jVeIEzLtFFSlD/nJHg==", + "version": "15.10.3", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.10.3.tgz", + "integrity": "sha512-aBQMMxYvFzJJwkmg+BUUg3YfPyeuCuKo2f+LOw7yYbU8AZMblibwzp9OV4srHVeQldxvSFdz0/Xu8blq2AesiA==", "dev": true, "requires": { - "@csstools/css-parser-algorithms": "^2.2.0", - "@csstools/css-tokenizer": "^2.1.1", - "@csstools/media-query-list-parser": "^2.1.0", - "@csstools/selector-specificity": "^2.2.0", + "@csstools/css-parser-algorithms": "^2.3.1", + "@csstools/css-tokenizer": "^2.2.0", + "@csstools/media-query-list-parser": "^2.1.4", + "@csstools/selector-specificity": "^3.0.0", "balanced-match": "^2.0.0", "colord": "^2.9.3", "cosmiconfig": "^8.2.0", - "css-functions-list": "^3.1.0", + "css-functions-list": "^3.2.0", "css-tree": "^2.3.1", "debug": "^4.3.4", - "fast-glob": "^3.2.12", + "fast-glob": "^3.3.1", "fastest-levenshtein": "^1.0.16", "file-entry-cache": "^6.0.1", "global-modules": "^2.0.0", @@ -23993,14 +24059,13 @@ "import-lazy": "^4.0.0", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", - "known-css-properties": "^0.27.0", + "known-css-properties": "^0.28.0", "mathml-tag-names": "^2.1.3", - "meow": "^9.0.0", + "meow": "^10.1.5", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "picocolors": "^1.0.0", - "postcss": "^8.4.24", - "postcss-media-query-parser": "^0.2.3", + "postcss": "^8.4.27", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^6.0.0", "postcss-selector-parser": "^6.0.13", @@ -24012,7 +24077,6 @@ "supports-hyperlinks": "^3.0.0", "svg-tags": "^1.0.0", "table": "^6.8.1", - "v8-compile-cache": "^2.3.0", "write-file-atomic": "^5.0.1" }, "dependencies": { @@ -24034,10 +24098,16 @@ "supports-color": "^7.0.0" } }, + "trim-newlines": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.1.1.tgz", + "integrity": "sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ==", + "dev": true + }, "type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", "dev": true }, "write-file-atomic": { @@ -24053,13 +24123,13 @@ } }, "@tylertech/forge-cli": { - "version": "3.0.0-next.3", - "resolved": "https://registry.npmjs.org/@tylertech/forge-cli/-/forge-cli-3.0.0-next.3.tgz", - "integrity": "sha512-S0OzUuLc8komAtF43EqpGkBmx9/7cZhYl3Ct9PTwUTzImpwOVd8jlh6X3NU9VZL7AdsrLQrqMm3mRqDAQK/sXg==", + "version": "3.0.0-next.4", + "resolved": "https://registry.npmjs.org/@tylertech/forge-cli/-/forge-cli-3.0.0-next.4.tgz", + "integrity": "sha512-QLUVSDhomyWcJex3w0ffKz9gfjnhZxOzQO1Ud44GZab9z4PBhTjQbS1xhISFBQDnquxy+IOpHyc1hTQB1boBAQ==", "dev": true, "requires": { "@custom-elements-manifest/analyzer": "^0.8.3", - "@tylertech/forge-build-tools": "3.0.0-next.5", + "@tylertech/forge-build-tools": "3.0.0-next.6", "autoprefixer": "^10.4.14", "browser-sync": "^2.29.3", "canonical-path": "^1.0.0", @@ -24377,9 +24447,9 @@ "dev": true }, "@types/eslint": { - "version": "8.40.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.40.2.tgz", - "integrity": "sha512-PRVjQ4Eh9z9pmmtaq8nTjZjQwKFk7YIHIud3lRoKRBgUQjgjRmoGxxGEPXQkF+lH7QkHJRNr5F4aBgYCW0lqpQ==", + "version": "8.44.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.2.tgz", + "integrity": "sha512-sdPRb9K6iL5XZOmBubg8yiFp5yS/JdUDQsq5e6h95km91MCYMuvp7mh1fjPEYUhvHepKpZOjnEaMBR4PxjWDzg==", "dev": true, "requires": { "@types/estree": "*", @@ -25836,13 +25906,13 @@ } }, "autoprefixer": { - "version": "10.4.14", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", - "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "version": "10.4.15", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.15.tgz", + "integrity": "sha512-KCuPB8ZCIqFdA4HwKXsvz7j6gvSDNhDP7WnUjBleRkKjPdvCmHFuQ77ocavI8FT6NdvlBnE2UFr2H4Mycn8Vew==", "dev": true, "requires": { - "browserslist": "^4.21.5", - "caniuse-lite": "^1.0.30001464", + "browserslist": "^4.21.10", + "caniuse-lite": "^1.0.30001520", "fraction.js": "^4.2.0", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", @@ -26374,15 +26444,15 @@ } }, "browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "version": "4.21.10", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", + "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "caniuse-lite": "^1.0.30001517", + "electron-to-chromium": "^1.4.477", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.11" } }, "bs-recipes": { @@ -26582,9 +26652,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001487", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001487.tgz", - "integrity": "sha512-83564Z3yWGqXsh2vaH/mhXfEM0wX+NlBCm1jYHOb97TrTWJEmPTccZgeLTPBUUb0PNVo+oomb7wkimZBIERClA==", + "version": "1.0.30001527", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001527.tgz", + "integrity": "sha512-YkJi7RwPgWtXVSgK4lG9AHH57nSzvvOp9MesgXmw4Q7n0C3H04L0foHqfxcmSAm5AcWb8dW9AYj2tR7/5GnddQ==", "dev": true }, "canonical-path": { @@ -27453,9 +27523,9 @@ } }, "css-functions-list": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.1.0.tgz", - "integrity": "sha512-/9lCvYZaUbBGvYUgYGFJ4dcYiyqdhSjG7IPVluoV8A1ILjkF7ilmhp1OGUz8n+nmBcu0RNrQAzgD8B6FJbrt2w==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.0.tgz", + "integrity": "sha512-d/jBMPyYybkkLVypgtGv12R+pIFw4/f/IHtCTxWpZc8ofTYOPigIgmA6vu5rMHartZC+WuXhBUHfnyNUIQSYrg==", "dev": true }, "css-loader": { @@ -27679,9 +27749,9 @@ } }, "del": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-7.0.0.tgz", - "integrity": "sha512-tQbV/4u5WVB8HMJr08pgw0b6nG4RGt/tj+7Numvq+zqcvUFeMaIWWOUFltiU+6go8BSO2/ogsB4EasDaj0y68Q==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/del/-/del-7.1.0.tgz", + "integrity": "sha512-v2KyNk7efxhlyHpjEvfyxaAihKKK0nWCuf6ZtqZcFFpQRG0bJ12Qsr0RpvsICMjAAZ8DOVCxrlqpxISlMHC4Kg==", "dev": true, "requires": { "globby": "^13.1.2", @@ -27720,14 +27790,14 @@ "dev": true }, "globby": { - "version": "13.1.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.4.tgz", - "integrity": "sha512-iui/IiiW+QrJ1X1hKH5qwlMQyv34wJAYwH1vrf8b9kBA4sNiif3gKsMHa+BrdnOpEudWjpotfa7LrTzB1ERS/g==", + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", "dev": true, "requires": { "dir-glob": "^3.0.1", - "fast-glob": "^3.2.11", - "ignore": "^5.2.0", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", "merge2": "^1.4.1", "slash": "^4.0.0" } @@ -27952,9 +28022,9 @@ } }, "electron-to-chromium": { - "version": "1.4.396", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.396.tgz", - "integrity": "sha512-pqKTdqp/c5vsrc0xUPYXTDBo9ixZuGY8es4ZOjjd6HD6bFYbu5QA09VoW3fkY4LF1T0zYk86lN6bZnNlBuOpdQ==", + "version": "1.4.508", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.508.tgz", + "integrity": "sha512-FFa8QKjQK/A5QuFr2167myhMesGrhlOBD+3cYNxO9/S4XzHEXesyTD/1/xF644gC8buFPz3ca6G1LOQD0tZrrg==", "dev": true }, "emoji-regex": { @@ -28406,27 +28476,27 @@ "dev": true }, "eslint": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz", - "integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.42.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.48.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -28436,7 +28506,6 @@ "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", @@ -28446,9 +28515,8 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "dependencies": { @@ -28471,9 +28539,9 @@ "dev": true }, "eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "requires": { "esrecurse": "^4.3.0", @@ -28533,18 +28601,18 @@ } }, "eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true }, "espree": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", - "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "requires": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } @@ -28694,9 +28762,9 @@ "dev": true }, "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -28952,9 +29020,9 @@ "dev": true }, "fraction.js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", - "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.6.tgz", + "integrity": "sha512-n2aZ9tNfYDwaHhvFTkhFErqOMIb8uyzSQ+vGJBjZyanAKZVbGUQ1sngfk9FdkBw7G26O7AgNjLcecLffD1c7eg==", "dev": true }, "fresh": { @@ -29409,9 +29477,9 @@ } }, "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -30246,9 +30314,9 @@ } }, "is-core-module": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", - "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", "dev": true, "requires": { "has": "^1.0.3" @@ -32727,9 +32795,9 @@ } }, "node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, "nopt": { @@ -33165,17 +33233,17 @@ } }, "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "requires": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" } }, "ora": { @@ -33202,9 +33270,9 @@ "dev": true }, "chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true }, "cli-cursor": { @@ -33478,13 +33546,13 @@ "dev": true }, "path-scurry": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.9.1.tgz", - "integrity": "sha512-UgmoiySyjFxP6tscZDgWGEAgsW5ok8W3F5CJDnnH2pozwSTGE6eH7vwTotMwATWA2r5xqdkKdxYPkwlJjAI/3g==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", "dev": true, "requires": { - "lru-cache": "^9.1.1", - "minipass": "^5.0.0 || ^6.0.0" + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "dependencies": { "lru-cache": { @@ -33494,9 +33562,9 @@ "dev": true }, "minipass": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.1.tgz", - "integrity": "sha512-Tenl5QPpgozlOGBiveNYHg2f6y+VpxsXRoIHFUVJuSmTonXRAE6q9b8Mp/O46762/2AlW4ye4Nkyvx0fgWDKbw==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", + "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==", "dev": true } } @@ -33672,9 +33740,9 @@ } }, "postcss": { - "version": "8.4.24", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz", - "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==", + "version": "8.4.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz", + "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==", "dev": true, "requires": { "nanoid": "^3.3.6", @@ -34590,9 +34658,9 @@ "dev": true }, "sass": { - "version": "1.63.4", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.63.4.tgz", - "integrity": "sha512-Sx/+weUmK+oiIlI+9sdD0wZHsqpbgQg8wSwSnGBjwb5GwqFhYNwwnI+UWZtLjKvKyFlKkatRK235qQ3mokyPoQ==", + "version": "1.66.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.66.1.tgz", + "integrity": "sha512-50c+zTsZOJVgFfTgwwEzkjA3/QACgdNsKueWPyAR0mRINIvLAStVQBbPg14iuqEQ74NPDbXzJARJ/O4SI1zftA==", "dev": true, "requires": { "chokidar": ">=3.0.0 <4.0.0", @@ -34601,9 +34669,9 @@ }, "dependencies": { "immutable": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.0.tgz", - "integrity": "sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz", + "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==", "dev": true } } @@ -36696,9 +36764,9 @@ "dev": true }, "webpack": { - "version": "5.87.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.87.0.tgz", - "integrity": "sha512-GOu1tNbQ7p1bDEoFRs2YPcfyGs8xq52yyPBZ3m2VGnXGtV9MxjrkABHm4V9Ia280OefsSLzvbVoXcfLxjKY/Iw==", + "version": "5.88.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", + "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", "dev": true, "requires": { "@types/eslint-scope": "^3.7.3", @@ -36875,12 +36943,6 @@ "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", "dev": true }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", diff --git a/package.json b/package.json index 4083842f4..2a6717a0d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@tylertech/forge", - "version": "3.0.0-next.1", + "version": "3.0.0-next.11", "description": "Tyler Forge™ Web Components library", "license": "Apache-2.0", "author": "Tyler Technologies, Inc.", @@ -72,7 +72,7 @@ "@esm-bundle/chai": "^4.3.4-fix.0", "@open-wc/testing": "^3.1.8", "@tylertech-eslint/eslint-plugin": "^1.0.9", - "@tylertech/forge-cli": "^3.0.0-next.3", + "@tylertech/forge-cli": "^3.0.0-next.4", "@tylertech/forge-testing": "^2.0.0", "@tylertech/stylelint-rules": "^4.3.4", "@types/jasmine": "^3.10.3", diff --git a/src/dev/pages/autocomplete/autocomplete.ejs b/src/dev/pages/autocomplete/autocomplete.ejs index e478a9cb6..b2c473917 100644 --- a/src/dev/pages/autocomplete/autocomplete.ejs +++ b/src/dev/pages/autocomplete/autocomplete.ejs @@ -4,6 +4,11 @@ + + + diff --git a/src/dev/pages/avatar/avatar.ejs b/src/dev/pages/avatar/avatar.ejs index 466dccc04..77eec2c6b 100644 --- a/src/dev/pages/avatar/avatar.ejs +++ b/src/dev/pages/avatar/avatar.ejs @@ -1,27 +1,27 @@
-

w/Image URL

+

w/Image URL

-

w/Text

+

w/Text

-

w/Custom Radius (10px)

+

w/Custom Radius (10px)

-

w/Icon

+

w/Icon

@@ -30,14 +30,14 @@
-

w/Custom Slotted Content and CSS Variable

+

w/Custom Slotted Content and CSS Variable

A
-

w/Invalid url

+

w/Invalid url

diff --git a/src/dev/pages/badge/badge.ejs b/src/dev/pages/badge/badge.ejs index 80d5f4bdd..e9f4817e3 100644 --- a/src/dev/pages/badge/badge.ejs +++ b/src/dev/pages/badge/badge.ejs @@ -31,7 +31,7 @@
-

Theme

+

Theme

Default Danger Warning @@ -40,7 +40,7 @@ Info (secondary)
-

Strong theme

+

Strong theme

Default Danger Warning diff --git a/src/dev/pages/button-area/button-area.ejs b/src/dev/pages/button-area/button-area.ejs new file mode 100644 index 000000000..2d1b76b60 --- /dev/null +++ b/src/dev/pages/button-area/button-area.ejs @@ -0,0 +1,46 @@ + + + +
+
+
Heading
+
Content
+
+ + + Like + + +
+
+
+ +
+ + + + + +
+
+
Expansion panel
+
Subheading
+
+ + + Like + + +
+
+
+ Expansion panel content +
+
+
+ + \ No newline at end of file diff --git a/src/dev/pages/button-area/button-area.html b/src/dev/pages/button-area/button-area.html new file mode 100644 index 000000000..a4211cdf2 --- /dev/null +++ b/src/dev/pages/button-area/button-area.html @@ -0,0 +1,11 @@ +<%- +include('./src/partials/page.ejs', { + page: { + title: 'Button Area', + includePath: './pages/button-area/button-area.ejs', + options: [ + { type: 'switch', label: 'Disabled', id: 'disabled-switch' } + ] + } +}) +%> \ No newline at end of file diff --git a/src/dev/pages/button-area/button-area.scss b/src/dev/pages/button-area/button-area.scss new file mode 100644 index 000000000..aa61a89df --- /dev/null +++ b/src/dev/pages/button-area/button-area.scss @@ -0,0 +1,22 @@ +forge-card { + --forge-card-padding: 0 !important; +} + +.flex-wrapper { + display: flex; + align-items: center; + margin: 16px; + + &>*:first-child { + margin-inline-end: auto; + } +} + +forge-icon[name=chevron_right], +forge-open-icon { + margin-inline-start: 8px; +} + +.expansion-panel-content { + margin: 16px; +} \ No newline at end of file diff --git a/src/dev/pages/button-area/button-area.ts b/src/dev/pages/button-area/button-area.ts new file mode 100644 index 000000000..b2dbaab47 --- /dev/null +++ b/src/dev/pages/button-area/button-area.ts @@ -0,0 +1,28 @@ +import { IButtonAreaComponent, IExpansionPanelComponent, ISwitchComponent, IconRegistry } from '@tylertech/forge'; +import { tylIconChevronRight, tylIconFavorite } from '@tylertech/tyler-icons/standard'; + +import '$src/shared'; +import '@tylertech/forge/button-area'; +import './button-area.scss'; + +IconRegistry.define([ + tylIconChevronRight, + tylIconFavorite +]); + +const buttonArea = document.getElementById('button-area') as IButtonAreaComponent; +buttonArea.addEventListener('click', () => alert('Click')); + +const expansionPanel = document.getElementById('expansion-panel') as IExpansionPanelComponent; +const expansionPanelButtonArea = document.getElementById('expansion-panel-button-area') as IButtonAreaComponent; +const expansionPanelButton = document.getElementById('expansion-panel-button'); +expansionPanel.addEventListener('forge-expansion-panel-toggle', (event: CustomEvent) => { + expansionPanelButton.setAttribute('aria-expanded', event.detail.toString()); +}); + + +const disabledToggle = document.querySelector('#disabled-switch') as ISwitchComponent; +disabledToggle.addEventListener('forge-switch-change', ({ detail: selected }) => { + buttonArea.disabled = selected; + expansionPanelButtonArea.disabled = selected; +}); diff --git a/src/dev/pages/button-toggle/button-toggle.ejs b/src/dev/pages/button-toggle/button-toggle.ejs index cea7fd1a2..21b17a383 100644 --- a/src/dev/pages/button-toggle/button-toggle.ejs +++ b/src/dev/pages/button-toggle/button-toggle.ejs @@ -1,4 +1,4 @@ -

Static

+

Static

@@ -11,7 +11,7 @@ -

Dynamic

+

Dynamic

diff --git a/src/dev/pages/card/card.ejs b/src/dev/pages/card/card.ejs index bbe4435fb..450b50892 100644 --- a/src/dev/pages/card/card.ejs +++ b/src/dev/pages/card/card.ejs @@ -1,6 +1,6 @@ -

+

This is the card title

@@ -10,7 +10,7 @@
-

+

Lorem, ipsum dolor sit amet consectetur adipisicing elit. Molestias exercitationem doloremque dolorem ullam, nesciunt quia velit necessitatibus numquam quasi voluptates impedit earum dolores diff --git a/src/dev/pages/checkbox/checkbox.ejs b/src/dev/pages/checkbox/checkbox.ejs index 90ecc86fd..81e2662e3 100644 --- a/src/dev/pages/checkbox/checkbox.ejs +++ b/src/dev/pages/checkbox/checkbox.ejs @@ -1,38 +1,43 @@

-

Default

+

Default

Default
-

Exposed input

+

Exposed input

Exposed input
-

Dense

+

Dense

Dense
-

Readonly

+

Readonly

Readonly
-

Disabled

+

Disabled

Disabled
-

Start label

+

Start label

Start label
-

Form associated

+

Prevent default

+ Prevent default +
+ +
+

Form associated

Form associated
diff --git a/src/dev/pages/checkbox/checkbox.ts b/src/dev/pages/checkbox/checkbox.ts index 712a9e673..3a22be329 100644 --- a/src/dev/pages/checkbox/checkbox.ts +++ b/src/dev/pages/checkbox/checkbox.ts @@ -33,3 +33,8 @@ exposeInputToggle.addEventListener('forge-switch-change', ({ detail: selected }) document.querySelectorAll('forge-checkbox').forEach(checkbox => { checkbox.addEventListener('change', evt => console.log(evt)); }); + +const preventCheckbox = document.getElementById('prevent-checkbox') as ICheckboxComponent; +preventCheckbox.addEventListener('change', (evt: Event) => { + evt.preventDefault(); +}); diff --git a/src/dev/pages/chips/chips.ejs b/src/dev/pages/chips/chips.ejs index f0443ab5b..51d8e50ca 100644 --- a/src/dev/pages/chips/chips.ejs +++ b/src/dev/pages/chips/chips.ejs @@ -1,6 +1,6 @@
-

Basic chips

+

Basic chips

Small Medium @@ -9,7 +9,7 @@
-

Choice chips

+

Choice chips

Small Medium @@ -18,7 +18,7 @@
-

Choice chips (vertical)

+

Choice chips (vertical)

Small Medium @@ -27,8 +27,8 @@
-

Filter chips

-
No leading icon
+

Filter chips

+

No leading icon

Tops Bottoms @@ -38,7 +38,7 @@
-
With leading icon
+

With leading icon

@@ -60,7 +60,7 @@
-

Action chips

+

Action chips

@@ -82,7 +82,7 @@
-

With leading avatar

+

With leading avatar

@@ -104,7 +104,7 @@
-

+

Input chips @@ -135,7 +135,7 @@

-

Invalid chips

+

Invalid chips

diff --git a/src/dev/pages/dialog/dialog-template-basic.ejs b/src/dev/pages/dialog/dialog-template-basic.ejs index 3fb385ef5..6ee7de002 100644 --- a/src/dev/pages/dialog/dialog-template-basic.ejs +++ b/src/dev/pages/dialog/dialog-template-basic.ejs @@ -1,7 +1,7 @@
-

Dialog title

+

Dialog title

-

Default (dense)

+

Default (dense)

-

Toggle

+

Toggle

-

Toggle (dense)

+

Toggle (dense)

-

w/Anchor link

+

w/Anchor link

diff --git a/src/dev/pages/icon/icon.ejs b/src/dev/pages/icon/icon.ejs index c2f1f90a4..af7ddcf12 100644 --- a/src/dev/pages/icon/icon.ejs +++ b/src/dev/pages/icon/icon.ejs @@ -1,19 +1,19 @@ -

External icon

+

External icon

-

External icon (lazy)

+

External icon (lazy)

-

Registry icon

+

Registry icon

-

Custom

+

Custom

-

Invalid icon

+

Invalid icon

-

Invalid icon (external)

+

Invalid icon (external)

diff --git a/src/dev/pages/list/list.ejs b/src/dev/pages/list/list.ejs index 5e83d051e..c673cc770 100644 --- a/src/dev/pages/list/list.ejs +++ b/src/dev/pages/list/list.ejs @@ -1,7 +1,31 @@ -
+
+ +
+

Non-interactive (static is deprecated)

+
+ + List Item One + List Item Two + List Item Three + +
+
+ + +
+

Disabled

+
+ + List Item One + List Item Two + List Item Three + +
+
+
-

Single-Line

+

Single-Line

List Item One @@ -10,10 +34,55 @@
+ + +
+

Two-Line

+
+ + + Primary Text + Secondary Text + + + Primary Text + Secondary Text + + + Primary Text + Secondary Text + + +
+
+ + +
+

Three-Line

+
+ + + Primary Text + Secondary Text + Tertiary Text + + + Primary Text + Secondary Text + Tertiary Text + + + Primary Text + Secondary Text + Tertiary Text + + +
+
-

Single-Line (long text)

+

Single-Line (long text)

Lorem ipsum dolor sit amet consectetur adipisicing elit. Dignissimos, libero nemo. Similique suscipit ipsam ab veritatis excepturi repellendus ipsa labore! Accusantium, sint ex. Illum architecto rerum, voluptates consectetur cum quaerat. @@ -22,43 +91,67 @@
- +
-

Single-Line (long text w/wrap)

+

Single-Line (long text w/wrap)

- - Lorem ipsum dolor sit amet consectetur adipisicing elit. Dignissimos, libero nemo. Similique suscipit ipsam ab veritatis excepturi repellendus ipsa labore! Accusantium, sint ex. Illum architecto rerum, voluptates consectetur cum quaerat. - Lorem ipsum dolor sit amet consectetur adipisicing elit. Dignissimos, libero nemo. Similique suscipit ipsam ab veritatis excepturi repellendus ipsa labore! Accusantium, sint ex. Illum architecto rerum, voluptates consectetur cum quaerat. - Lorem ipsum dolor sit amet consectetur adipisicing elit. Dignissimos, libero nemo. Similique suscipit ipsam ab veritatis excepturi repellendus ipsa labore! Accusantium, sint ex. Illum architecto rerum, voluptates consectetur cum quaerat. + + Lorem ipsum dolor sit amet consectetur adipisicing elit. Dignissimos, libero nemo. Similique suscipit ipsam ab veritatis excepturi repellendus ipsa labore! Accusantium, sint ex. Illum architecto rerum, voluptates consectetur cum quaerat. + Lorem ipsum dolor sit amet consectetur adipisicing elit. Dignissimos, libero nemo. Similique suscipit ipsam ab veritatis excepturi repellendus ipsa labore! Accusantium, sint ex. Illum architecto rerum, voluptates consectetur cum quaerat. + Lorem ipsum dolor sit amet consectetur adipisicing elit. Dignissimos, libero nemo. Similique suscipit ipsam ab veritatis excepturi repellendus ipsa labore! Accusantium, sint ex. Illum architecto rerum, voluptates consectetur cum quaerat.
- +
-

Two-Line (long text w/wrap)

+

Two-Line (long text w/wrap)

- - - List Item - Lorem ipsum dolor sit amet consectetur adipisicing elit. Dignissimos, libero nemo. Similique suscipit ipsam ab veritatis excepturi repellendus ipsa labore! Accusantium, sint ex. Illum architecto rerum, voluptates consectetur cum quaerat. + + + List Item + Lorem ipsum dolor sit amet consectetur adipisicing elit. Dignissimos, libero nemo. Similique suscipit ipsam ab veritatis excepturi repellendus ipsa labore! Accusantium, sint ex. Illum architecto rerum, voluptates consectetur cum quaerat. - - List Item - Lorem ipsum dolor sit amet consectetur adipisicing elit. Dignissimos, libero nemo. Similique suscipit ipsam ab veritatis excepturi repellendus ipsa labore! Accusantium, sint ex. Illum architecto rerum, voluptates consectetur cum quaerat. + + List Item + Lorem ipsum dolor sit amet consectetur adipisicing elit. Dignissimos, libero nemo. Similique suscipit ipsam ab veritatis excepturi repellendus ipsa labore! Accusantium, sint ex. Illum architecto rerum, voluptates consectetur cum quaerat. - - List Item - Lorem ipsum dolor sit amet consectetur adipisicing elit. Dignissimos, libero nemo. Similique suscipit ipsam ab veritatis excepturi repellendus ipsa labore! Accusantium, sint ex. Illum architecto rerum, voluptates consectetur cum quaerat. + + List Item + Lorem ipsum dolor sit amet consectetur adipisicing elit. Dignissimos, libero nemo. Similique suscipit ipsam ab veritatis excepturi repellendus ipsa labore! Accusantium, sint ex. Illum architecto rerum, voluptates consectetur cum quaerat.
- + + +
+

Three-Line (long text w/wrap)

+
+ + + List Item + Lorem ipsum dolor sit amet consectetur adipisicing elit. Dignissimos, libero nemo. Similique suscipit ipsam ab veritatis excepturi repellendus ipsa labore! Accusantium, sint ex. Illum architecto rerum, voluptates consectetur cum quaerat. + Lorem ipsum dolor sit amet consectetur adipisicing elit. Dignissimos, libero nemo. Similique suscipit ipsam ab veritatis excepturi repellendus ipsa labore! Accusantium, sint ex. Illum architecto rerum, voluptates consectetur cum quaerat. + + + List Item + Lorem ipsum dolor sit amet consectetur adipisicing elit. Dignissimos, libero nemo. Similique suscipit ipsam ab veritatis excepturi repellendus ipsa labore! Accusantium, sint ex. Illum architecto rerum, voluptates consectetur cum quaerat. + Lorem ipsum dolor sit amet consectetur adipisicing elit. Dignissimos, libero nemo. Similique suscipit ipsam ab veritatis excepturi repellendus ipsa labore! Accusantium, sint ex. Illum architecto rerum, voluptates consectetur cum quaerat. + + + List Item + Lorem ipsum dolor sit amet consectetur adipisicing elit. Dignissimos, libero nemo. Similique suscipit ipsam ab veritatis excepturi repellendus ipsa labore! Accusantium, sint ex. Illum architecto rerum, voluptates consectetur cum quaerat. + Lorem ipsum dolor sit amet consectetur adipisicing elit. Dignissimos, libero nemo. Similique suscipit ipsam ab veritatis excepturi repellendus ipsa labore! Accusantium, sint ex. Illum architecto rerum, voluptates consectetur cum quaerat. + + +
+
+
-

Single-Line (dense)

+

Single-Line (dense)

List Item One @@ -67,88 +160,106 @@
- - + +
-

Two-Line

+

Two-Line (dense)

- - - List Item - Secondary Text + + + Primary Text + Secondary Text - - List Item - Secondary Text + + Primary Text + Secondary Text - - List Item - Secondary Text + + Primary Text + Secondary Text
- - + +
-

Three-Line

+

Three-Line (dense)

- - - List Item - Secondary Text - Tertiary Text + + + Primary Text + Secondary Text + Tertiary Text - - List Item - Secondary Text - Tertiary Text + + Primary Text + Secondary Text + Tertiary Text - - List Item - Secondary Text - Tertiary Text + + Primary Text + Secondary Text + Tertiary Text
- +
-

List with Selected Item

+

List with Selected Item

- - List Item One - List Item Two - List Item Three + + + + Wifi + Secondary Text + Tertiary Text + + + + + Bluetooth + Secondary Text + Tertiary Text + + + + + Data Usage + Secondary Text + Tertiary Text + +
- +
-

Leading Icon

+

Leading Icon

- List Item + List Item - List Item + List Item - List Item + List Item
- +
-

Trailing Icon

+

Trailing Icon

@@ -166,56 +277,56 @@
- +
-

Two-Line with Leading and Trailing Icon and Divider

+

Two-Line with Leading and Trailing Icon and Divider

- - - - Dog Photos - 9 Jan 2018 + + + + Dog Photos + 9 Jan 2018 - + - Cat Photos - 22 Dec 2017 + Cat Photos + 22 Dec 2017 - + - Potatoes - 30 Nov 2017 + Potatoes + 30 Nov 2017 - + - Carrots - 17 Oct 2017 + Carrots + 17 Oct 2017
- +
-

List with Dividers and Expandable Items

+

List with Dividers and Expandable Items

List Item One - + - - List Item One - List Item Two - List Item Three + + List Item One + List Item Two + List Item Three @@ -223,7 +334,7 @@ List Item Two - + List Item One @@ -236,53 +347,53 @@
- +
-

List with Leading Checkbox

+

List with Leading Checkbox

- List Item + List Item - List Item + List Item - List Item + List Item
- +
-

List with Trailing Checkbox

+

List with Trailing Checkbox

- List Item + List Item - List Item + List Item - List Item + List Item @@ -290,53 +401,53 @@
- +
-

List with Leading Radio Buttons

+

List with Leading Radio Buttons

- List Item + List Item - List Item + List Item - List Item + List Item
- +
-

List with Trailing Radio Buttons

+

List with Trailing Radio Buttons

- List Item + List Item - List Item + List Item - List Item + List Item @@ -345,9 +456,31 @@
+ + +
+

List with Trailing Switch

+
+ + + List Item + + + + List Item + + + + List Item + + + +
+
+
-

List with multiple Checkboxes (requires use of forge-ignore attribute)

+

List with multiple Checkboxes (requires use of forge-ignore attribute)

@@ -357,7 +490,7 @@ - List Item + List Item @@ -366,7 +499,7 @@ - List Item + List Item @@ -375,27 +508,50 @@ - List Item + List Item
- +
-

List Inside of Drawer

+

List Inside of Drawer

- - List Item One - - List Item Two - - List Item Three - +
+ + +
+

List with anchor links

+
+ + + Google + + + + Bing + + + + Yahoo + + + +
+
diff --git a/src/dev/pages/list/list.scss b/src/dev/pages/list/list.scss index d0e830f4c..20e97db0d 100644 --- a/src/dev/pages/list/list.scss +++ b/src/dev/pages/list/list.scss @@ -1,4 +1,4 @@ .list-demo { max-width: 600px; border: 1px solid var(--forge-theme-border-color); -} \ No newline at end of file +} diff --git a/src/dev/pages/list/list.ts b/src/dev/pages/list/list.ts index 885cfff07..10522cdd1 100644 --- a/src/dev/pages/list/list.ts +++ b/src/dev/pages/list/list.ts @@ -8,7 +8,7 @@ import '@tylertech/forge/expansion-panel'; import '@tylertech/forge/drawer'; import './list.scss'; import { IconRegistry } from '@tylertech/forge/icon'; -import { tylIconBluetooth, tylIconCode, tylIconDataUsage, tylIconFace, tylIconInfo, tylIconWifi } from '@tylertech/tyler-icons/standard'; +import { tylIconBluetooth, tylIconCode, tylIconDataUsage, tylIconFace, tylIconInfo, tylIconWifi, tylIconOpenInNew } from '@tylertech/tyler-icons/standard'; IconRegistry.define([ tylIconWifi, @@ -16,5 +16,6 @@ IconRegistry.define([ tylIconDataUsage, tylIconInfo, tylIconCode, - tylIconFace + tylIconFace, + tylIconOpenInNew ]); diff --git a/src/dev/pages/popup/popup.ejs b/src/dev/pages/popup/popup.ejs index d382aedeb..f331cfc96 100644 --- a/src/dev/pages/popup/popup.ejs +++ b/src/dev/pages/popup/popup.ejs @@ -4,7 +4,7 @@ \ No newline at end of file + diff --git a/src/lib/list/list/list.scss b/src/lib/list/list/list.scss index e956eeb05..635e08787 100644 --- a/src/lib/list/list/list.scss +++ b/src/lib/list/list/list.scss @@ -1,11 +1,26 @@ -@use './mixins'; - -@include mixins.core-styles; +@use './configuration'; +@use './core'; +@use '../list-item'; :host { - @include mixins.host; + display: block; } :host([hidden]) { display: none; } + +:host([navlist]) { + @include list-item.provide-theme(( + height: 40px, + margin: 4px 8px, + shape: 4px, + supporting-text-font-size: 0.875rem, + supporting-text-font-weight: 500 + )); +} + +.forge-list { + @include configuration.configuration; + @include core.base; +} diff --git a/src/lib/list/list/list.ts b/src/lib/list/list/list.ts index 6580807ba..52ec4184f 100644 --- a/src/lib/list/list/list.ts +++ b/src/lib/list/list/list.ts @@ -1,19 +1,25 @@ -import { attachShadowTemplate, coerceBoolean, CustomElement, FoundationProperty } from '@tylertech/forge-core'; +import { CustomElement, attachShadowTemplate, FoundationProperty, coerceBoolean } from '@tylertech/forge-core'; import { BaseComponent, IBaseComponent } from '../../core/base/base-component'; -import { ListItemComponent } from '../list-item'; import { ListAdapter } from './list-adapter'; -import { LIST_CONSTANTS } from './list-constants'; import { ListFoundation } from './list-foundation'; +import { LIST_CONSTANTS } from './list-constants'; import template from './list.html'; import styles from './list.scss'; +import { ListItemComponent } from '../list-item'; export interface IListComponent extends IBaseComponent { + /** @deprecated Use nonInteractive instead. */ static: boolean; + nonInteractive: boolean; + disabled: boolean; dense: boolean; propagateClick: boolean; indented: boolean; selectedValue: any; + twoLine: boolean; + threeLine: boolean; + wrap: boolean; } declare global { @@ -23,22 +29,63 @@ declare global { } /** - * The custom element class behind the `` element. - * * @tag forge-list + * + * @summary Lists are vertical groupings of related content. + * + * @csspart root - The component's root container element. + * + * @slot - The default/unnamed slot for child list items. + * + * @cssproperty --forge-list-container-color - The background color of the list surface, + * @cssproperty --forge-list-block-padding - The block padding of the list before and after the list items. + * @cssproperty --forge-list-inline-padding - The inline padding of the list next to the list items. + * + * @property {string} role - The role of the list. Default is 'list'. Valid values are 'list', 'listbox', and 'menu'. + * @property {boolean} static - Whether the list has all static items or not. + * @property {boolean} nonInteractive - Whether the list has all non-interactive items or not. + * @property {boolean} disabled - Whether the list items are disabled or not. + * @property {boolean} dense - Whether the list has all dense items or not. + * @property {boolean} propagateClick - Whether the list items propagate click events or not. + * @property {boolean} indented - Whether the list items within this list are indented. Default is false. + * @property {unknown | unknown[]} selectedValue - The selected list item value(s). + * @property {boolean} twoLine - Whether the list has all two-line items or not. + * @property {boolean} threeLine - Whether the list has all three-line items or not. + * @property {boolean} wrap - Whether the list has all items that wrap their text or not. + * + * @attribute {string} role - The role of the list. Default is 'list'. Valid values are 'list', 'listbox', and 'menu'. + * @attribute {boolean} static - Whether the list has all static items or not. + * @attribute {boolean} non-interactive - Whether the list has all non-interactive items or not. + * @attribute {boolean} disabled - Whether the list items are disabled or not. + * @attribute {boolean} dense - Whether the list has all dense items or not. + * @attribute {boolean} propagate-click - Whether the list items propagate click events or not. + * @attribute {string} selected-value - The selected list item value(s). + * @attribute {boolean} indented - Whether the list items within this list are indented. Default is false. + * @attribute {boolean} two-line - Whether the list has all two-line items or not. + * @attribute {boolean} three-line - Whether the list has all three-line items or not. + * @attribute {boolean} wrap - Whether the list has all items that wrap their text or not. + * @attribute {boolean} navlist - Controls whether the list is styled a navigation list or not. */ @CustomElement({ name: LIST_CONSTANTS.elementName, - dependencies: [ListItemComponent] + dependencies: [ + ListItemComponent + ] }) export class ListComponent extends BaseComponent implements IListComponent { public static get observedAttributes(): string[] { return [ + LIST_CONSTANTS.attributes.ROLE, LIST_CONSTANTS.attributes.STATIC, + LIST_CONSTANTS.attributes.NON_INTERACTIVE, + LIST_CONSTANTS.attributes.DISABLED, LIST_CONSTANTS.attributes.DENSE, - LIST_CONSTANTS.attributes.SELECTED_VALUE, LIST_CONSTANTS.attributes.PROPAGATE_CLICK, - LIST_CONSTANTS.attributes.INDENTED + LIST_CONSTANTS.attributes.SELECTED_VALUE, + LIST_CONSTANTS.attributes.INDENTED, + LIST_CONSTANTS.attributes.TWO_LINE, + LIST_CONSTANTS.attributes.THREE_LINE, + LIST_CONSTANTS.attributes.WRAP ]; } @@ -50,14 +97,21 @@ export class ListComponent extends BaseComponent implements IListComponent { this._foundation = new ListFoundation(new ListAdapter(this)); } - public initializedCallback(): void { + public connectedCallback(): void { this._foundation.initialize(); } public attributeChangedCallback(name: string, oldValue: string, newValue: string): void { switch (name) { + case LIST_CONSTANTS.attributes.ROLE: + this._foundation.updateRole(); + break; case LIST_CONSTANTS.attributes.STATIC: - this.static = coerceBoolean(newValue); + case LIST_CONSTANTS.attributes.NON_INTERACTIVE: + this.nonInteractive = coerceBoolean(newValue); + break; + case LIST_CONSTANTS.attributes.DISABLED: + this.disabled = coerceBoolean(newValue); break; case LIST_CONSTANTS.attributes.DENSE: this.dense = coerceBoolean(newValue); @@ -71,26 +125,45 @@ export class ListComponent extends BaseComponent implements IListComponent { case LIST_CONSTANTS.attributes.SELECTED_VALUE: this.selectedValue = newValue; break; + case LIST_CONSTANTS.attributes.TWO_LINE: + this.twoLine = coerceBoolean(newValue); + break; + case LIST_CONSTANTS.attributes.THREE_LINE: + this.threeLine = coerceBoolean(newValue); + break; + case LIST_CONSTANTS.attributes.WRAP: + this.wrap = coerceBoolean(newValue); + break; } } - /** Gets/sets whether the list has all static items or not. */ @FoundationProperty() public declare static: boolean; - /** Gets/sets whether the list has all dense items or not. */ + @FoundationProperty() + public declare nonInteractive: boolean; + + @FoundationProperty() + public declare disabled: boolean; + @FoundationProperty() public declare dense: boolean; - /** Gets/sets whether the list items allow mousedown events through to their underlying list item elements. Default is true. */ @FoundationProperty() public declare propagateClick: boolean; - /** Gets/sets whether the list items within this list are indented. Default is false. */ @FoundationProperty() public declare indented: boolean; - /** Gets/sets the selected list item value(s) */ @FoundationProperty() public declare selectedValue: any; + + @FoundationProperty() + public declare twoLine: boolean; + + @FoundationProperty() + public declare threeLine: boolean; + + @FoundationProperty() + public declare wrap: boolean; } diff --git a/src/lib/menu/build.json b/src/lib/menu/build.json index d8638d477..07c79a465 100644 --- a/src/lib/menu/build.json +++ b/src/lib/menu/build.json @@ -1,4 +1,4 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json" } \ No newline at end of file diff --git a/src/lib/open-icon/build.json b/src/lib/open-icon/build.json index d8638d477..07c79a465 100644 --- a/src/lib/open-icon/build.json +++ b/src/lib/open-icon/build.json @@ -1,4 +1,4 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json" } \ No newline at end of file diff --git a/src/lib/page-state/build.json b/src/lib/page-state/build.json index d8638d477..07c79a465 100644 --- a/src/lib/page-state/build.json +++ b/src/lib/page-state/build.json @@ -1,4 +1,4 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json" } \ No newline at end of file diff --git a/src/lib/paginator/build.json b/src/lib/paginator/build.json index d8638d477..07c79a465 100644 --- a/src/lib/paginator/build.json +++ b/src/lib/paginator/build.json @@ -1,4 +1,4 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json" } \ No newline at end of file diff --git a/src/lib/paginator/paginator-adapter.ts b/src/lib/paginator/paginator-adapter.ts index 88dc137cf..20885d416 100644 --- a/src/lib/paginator/paginator-adapter.ts +++ b/src/lib/paginator/paginator-adapter.ts @@ -5,40 +5,43 @@ import { ISelectComponent, ISelectOption } from '../select'; import { IPaginatorComponent } from './paginator'; import { PaginatorAlternativeAlignment, PAGINATOR_CONSTANTS } from './paginator-constants'; +export type PaginatorFieldIdentifier = 'first' | 'last' | 'previous' | 'next' | 'page-size'; + export interface IPaginatorAdapter extends IBaseAdapter { - setLabel: (value: string) => void; - setPageSizeOptions: (options: ISelectOption[]) => void; - setPageSize: (value: number) => void; - setRangeLabel: (value: string) => void; + setLabel(value: string): void; + setPageSizeOptions(options: ISelectOption[]): void; + setPageSize(value: number): void; + setRangeLabel(value: string): void; hasFirstPageButton(): boolean; showFirstPageButton(): void; hideFirstPageButton(): void; hasLastPageButton(): boolean; showLastPageButton(): void; hideLastPageButton(): void; - attachPageSizeChangeListener: (listener: (evt: CustomEvent) => void) => void; - attachFirstPageListener: (listener: (evt: Event) => void) => void; - attachPreviousPageListener: (listener: (evt: Event) => void) => void; - attachNextPageListener: (listener: (evt: Event) => void) => void; - attachLastPageListener: (listener: (evt: Event) => void) => void; - detachPageSizeChangeListener: (listener: (evt: CustomEvent) => void) => void; - detachFirstPageListener: (listener: (evt: Event) => void) => void; - detachPreviousPageListener: (listener: (evt: Event) => void) => void; - detachNextPageListener: (listener: (evt: Event) => void) => void; - detachLastPageListener: (listener: (evt: Event) => void) => void; - disableFirstPageButton: () => void; - enableFirstPageButton: () => void; - disablePreviousPageButton: () => void; - enablePreviousPageButton: () => void; - disableNextPageButton: () => void; - enableNextPageButton: () => void; + attachPageSizeChangeListener(listener: (evt: CustomEvent) => void): void; + attachFirstPageListener(listener: (evt: Event) => void): void; + attachPreviousPageListener(listener: (evt: Event) => void): void; + attachNextPageListener(listener: (evt: Event) => void): void; + attachLastPageListener(listener: (evt: Event) => void): void; + detachPageSizeChangeListener(listener: (evt: CustomEvent) => void): void; + detachFirstPageListener(listener: (evt: Event) => void): void; + detachPreviousPageListener(listener: (evt: Event) => void): void; + detachNextPageListener(listener: (evt: Event) => void): void; + detachLastPageListener(listener: (evt: Event) => void): void; + disableFirstPageButton(): void; + enableFirstPageButton(): void; + disablePreviousPageButton(): void; + enablePreviousPageButton(): void; + disableNextPageButton(): void; + enableNextPageButton(): void; disablePageSizeSelect(): void; enablePageSizeSelect(): void; setPageSizeVisibility(visible: boolean): void; - disableLastPageButton: () => void; - enableLastPageButton: () => void; - setAlternative: (alternative: boolean) => void; - setAlignment: (alignment: PaginatorAlternativeAlignment) => void; + disableLastPageButton(): void; + enableLastPageButton(): void; + setAlternative(alternative: boolean): void; + setAlignment(alignment: PaginatorAlternativeAlignment): void; + handleFocusMove(from: PaginatorFieldIdentifier): void; } /** @@ -256,4 +259,62 @@ export class PaginatorAdapter extends BaseAdapter implement break; } } + + public handleFocusMove(from: PaginatorFieldIdentifier): void { + if (!this._component.matches(':focus')) { + return; // We can only move focus elsewhere within the element if the element already contains focus + } + + switch (from) { + case 'first': + this._tryFocus([ + this._nextPageButton, + this._lastPageButton, + this._previousPageButton, + this._pageSizeSelect + ]); + break; + case 'last': + this._tryFocus([ + this._previousPageButton, + this._firstPageButton, + this._nextPageButton, + this._pageSizeSelect + ]); + break; + case 'previous': + this._tryFocus([ + this._nextPageButton, + this._lastPageButton, + this._firstPageButton, + this._pageSizeSelect + ]); + break; + case 'next': + this._tryFocus([ + this._previousPageButton, + this._firstPageButton, + this._lastPageButton, + this._pageSizeSelect + ]); + break; + case 'page-size': + this._tryFocus([ + this._nextPageButton, + this._lastPageButton, + this._firstPageButton, + this._previousPageButton + ]); + break; + } + } + + private _tryFocus(elements: Array): void { + for (const el of elements) { + if (el && el.isConnected && !el.disabled) { + el.focus(); + return; + } + } + } } diff --git a/src/lib/paginator/paginator-foundation.ts b/src/lib/paginator/paginator-foundation.ts index c05a9a473..f0bd56144 100644 --- a/src/lib/paginator/paginator-foundation.ts +++ b/src/lib/paginator/paginator-foundation.ts @@ -1,8 +1,7 @@ import { coerceNumber, ICustomElementFoundation, isArray, isDefined } from '@tylertech/forge-core'; -import { IPaginatorAdapter } from './paginator-adapter'; +import { IPaginatorAdapter, PaginatorFieldIdentifier } from './paginator-adapter'; import { PaginatorAlternativeAlignment, PAGINATOR_CONSTANTS, IPaginatorChangeEvent } from './paginator-constants'; -import { ISelectOption, ISelectComponent } from '../select'; - +import { ISelectOption } from '../select'; export interface IPaginatorFoundation extends ICustomElementFoundation { pageIndex: number; @@ -11,229 +10,44 @@ export interface IPaginatorFoundation extends ICustomElementFoundation { total: number; pageSizeOptions: number[] | boolean; pageSizeLabel: string; - initialize(): void; } -/** - * The foundation class behind the `` component. - */ export class PaginatorFoundation { - // Backing models private _pageIndex = PAGINATOR_CONSTANTS.numbers.DEFAULT_PAGE_INDEX; private _pageSize = PAGINATOR_CONSTANTS.numbers.DEFAULT_PAGE_SIZE; + private _offset = 0; private _total = PAGINATOR_CONSTANTS.numbers.DEFAULT_TOTAL; - private _pageSizeOptions: ISelectOption[] = []; + private _pageSizeOptions: ISelectOption[] = PAGINATOR_CONSTANTS.numbers.DEFAULT_PAGE_SIZE_OPTIONS.map(o => ({ label: `${o}`, value: `${o}` })); private _label = PAGINATOR_CONSTANTS.strings.DEFAULT_LABEL; private _firstLast = false; private _first = false; private _disabled = false; private _alternative: boolean; private _alignment: PaginatorAlternativeAlignment = 'space-between'; + private _rangeLabel: string; - // Listeners private _firstPageListener: (evt: Event) => void; private _previousPageListener: (evt: Event) => void; private _nextPageListener: (evt: Event) => void; private _lastPageListener: (evt: Event) => void; private _pageSizeListener: (evt: Event) => void; - // State variables - private _rangeLabel: string; - constructor(private _adapter: IPaginatorAdapter) { - // Create listeners - this._pageSizeListener = (evt: CustomEvent) => this._onPageSizeChanged(evt); + this._pageSizeListener = (evt: CustomEvent) => this._onPageSizeChanged(evt); this._firstPageListener = (evt: Event) => this._onFirstPage(evt); this._previousPageListener = (evt: Event) => this._onPreviousPage(evt); this._nextPageListener = (evt: Event) => this._onNextPage(evt); this._lastPageListener = (evt: Event) => this._onLastPage(evt); - - this._pageSizeOptions = PAGINATOR_CONSTANTS.numbers.DEFAULT_PAGE_SIZE_OPTIONS.map(o => ({ label: o.toString(), value: o.toString() })); - } - - /** The zero-based page index. Default is 0. */ - public set pageIndex(value: number) { - if (this._pageIndex !== value) { - if (isDefined(value)) { - this._pageIndex = value; - this._update(); - this._adapter.setHostAttribute(PAGINATOR_CONSTANTS.attributes.PAGE_INDEX, this._pageIndex.toString()); - } else { - this._adapter.removeHostAttribute(PAGINATOR_CONSTANTS.attributes.PAGE_INDEX); - } - } - } - public get pageIndex(): number { - return this._pageIndex; - } - - /** Number of items to display on a page. By default set to 25. */ - public set pageSize(value: number) { - if (this._pageSize !== value) { - this._pageSize = value; - this._adapter.setPageSize(this._pageSize); - this._update(); - this._adapter.setHostAttribute(PAGINATOR_CONSTANTS.attributes.PAGE_SIZE, this._pageSize.toString()); - } - } - public get pageSize(): number { - return this._pageSize; - } - - /** Sets page index by providing the number of items to skip. */ - public set offset(value: number) { - if (value >= this._total) { - if (this._total >= this._pageSize) { - value = this._total - this._pageSize; - } else { - value = 0; - } - } - const clampedValue = Math.min(Math.max(value, 0), this._total); - this.pageIndex = Math.floor(clampedValue / this._pageSize); - } - public get offset(): number { - return this._pageIndex * this._pageSize; - } - - /** The total number of items to be paginated. Default is 0. */ - public set total(value: number) { - if (this._total !== value) { - this._total = value; - this._update(); - this._adapter.setHostAttribute(PAGINATOR_CONSTANTS.attributes.TOTAL, this._total.toString()); - } - } - public get total(): number { - return this._total; - } - - /** The set of provided page size options to display to the user. */ - public set pageSizeOptions(options: number[] | boolean) { - if (isArray(options)) { - this._pageSizeOptions = (options as number[]) - .map(o => ({ label: o.toString(), value: o.toString() })) - .sort((a, b) => coerceNumber(a.value) - coerceNumber(b.value)); - this._adapter.setPageSizeOptions(this._pageSizeOptions); - this._adapter.attachPageSizeChangeListener(this._pageSizeListener); - this._adapter.setPageSizeVisibility(true); - if (isDefined(this._pageSize) && this._pageSizeOptions.length && !this._pageSizeOptions.find(o => coerceNumber(o.value) === this._pageSize)) { - this.pageSize = coerceNumber(this._pageSizeOptions[0].value); - } - } else if (options.toString().toLowerCase() === 'false') { - this._adapter.detachPageSizeChangeListener(this._pageSizeListener); - this._adapter.setPageSizeVisibility(false); - } - } - public get pageSizeOptions(): number[] | boolean { - return this._pageSizeOptions.map(o => Number(o.value)); - } - - /** A label for the paginator. Default is "Rows per page:". */ - public set label(value: string) { - if (this._label !== value) { - this._label = value; - this._adapter.setLabel(this._label); - this._adapter.setHostAttribute(PAGINATOR_CONSTANTS.attributes.LABEL, isDefined(this._label) ? this._label.toString() : ''); - } - } - public get label(): string { - return this._label; - } - - /** Whether to show the first page and last page buttons. Default is false. */ - public set firstLast(value: boolean) { - if (this._firstLast !== value) { - this._firstLast = value; - this._toggleFirstLastButtons(); - - if (this._firstLast) { - this._adapter.setHostAttribute(PAGINATOR_CONSTANTS.attributes.FIRST_LAST); - } else { - this._adapter.removeHostAttribute(PAGINATOR_CONSTANTS.attributes.FIRST_LAST); - } - } - } - public get firstLast(): boolean { - return this._firstLast; - } - - /** Whether to show the first page button. Default is false. */ - public set first(value: boolean) { - if (this._first !== value) { - this._first = value; - this._toggleFirstButton(); - - if (this._first) { - this._adapter.setHostAttribute(PAGINATOR_CONSTANTS.attributes.FIRST); - } else { - this._adapter.removeHostAttribute(PAGINATOR_CONSTANTS.attributes.FIRST); - } - } } - public get first(): boolean { - return this._first; - } - - /** Whether the paginator is disabled. Default is false. */ - public set disabled(value: boolean) { - if (this._disabled !== value) { - this._disabled = value; - this._update(); - - if (this._disabled) { - this._adapter.setHostAttribute(PAGINATOR_CONSTANTS.attributes.DISABLED); - } else { - this._adapter.removeHostAttribute(PAGINATOR_CONSTANTS.attributes.DISABLED); - } - } - } - public get disabled(): boolean { - return this._disabled; - } - - public get alternative(): boolean { - return this._alternative; - } - public set alternative(value: boolean) { - if (value !== this._alternative) { - this._alternative = value; - this._applyAlternative(); - } - } - - public get alignment(): PaginatorAlternativeAlignment { - return this._alignment; - } - public set alignment(value: PaginatorAlternativeAlignment) { - if (value !== this._alignment) { - this._alignment = value; - this._applyAlternativeAlignment(); - } - } - - private _applyAlternativeAlignment(): void { - this._adapter.setHostAttribute(PAGINATOR_CONSTANTS.attributes.ALIGNMENT, this._alignment); - this._adapter.setAlignment(this._alignment); - } - - private _applyAlternative(): void { - this._adapter.toggleHostAttribute(PAGINATOR_CONSTANTS.attributes.ALTERNATIVE, this._alternative); - this._adapter.setAlternative(this._alternative); - this._applyAlternativeAlignment(); - } - - /** - * Intializes the internal state when the component loads. - */ public initialize(): void { - this._update(); + this._updateRangeLabel(); this._adapter.setLabel(this._label); this._adapter.setPageSizeOptions(this._pageSizeOptions); this._adapter.setPageSize(this._pageSize); this._attachListeners(); this._toggleFirstLastButtons(); + this._syncInteractionState(); } public disconnect(): void { @@ -256,10 +70,6 @@ export class PaginatorFoundation { this._adapter.detachLastPageListener(this._lastPageListener); } - /** - * Handles clicking the first page button. - * @param evt The click event. - */ private _onFirstPage(evt: Event): void { evt.stopPropagation(); @@ -270,14 +80,10 @@ export class PaginatorFoundation { const firstPage = 0; const canPage = this._emitChangeEvent(PAGINATOR_CONSTANTS.strings.FIRST_PAGE, { pageIndex: firstPage }); if (canPage) { - this.pageIndex = firstPage; + this._applyPageIndex(firstPage, { fromField: 'first' }); } } - /** - * Handles clicking the previous page button. - * @param evt The click event. - */ private _onPreviousPage(evt: Event): void { evt.stopPropagation(); @@ -288,14 +94,10 @@ export class PaginatorFoundation { const prevPage = this._pageIndex - 1; const canPage = this._emitChangeEvent(PAGINATOR_CONSTANTS.strings.PREVIOUS_PAGE, { pageIndex: prevPage }); if (canPage) { - this.pageIndex = prevPage; + this._applyPageIndex(prevPage, { fromField: 'previous' }); } } - /** - * Handles clicking the next page button. - * @param evt The click event. - */ private _onNextPage(evt: Event): void { evt.stopPropagation(); @@ -306,14 +108,10 @@ export class PaginatorFoundation { const nextPage = this._pageIndex + 1; const canPage = this._emitChangeEvent(PAGINATOR_CONSTANTS.strings.NEXT_PAGE, { pageIndex: nextPage }); if (canPage) { - this.pageIndex = nextPage; + this._applyPageIndex(nextPage, { fromField: 'next' }); } } - /** - * Handles clicking the last page button. - * @param evt The click event. - */ private _onLastPage(evt: Event): void { evt.stopPropagation(); @@ -324,22 +122,18 @@ export class PaginatorFoundation { const lastPage = this._getMaxPages(); const canPage = this._emitChangeEvent(PAGINATOR_CONSTANTS.strings.LAST_PAGE, { pageIndex: lastPage }); if (canPage) { - this.pageIndex = lastPage; + this._applyPageIndex(lastPage, { fromField: 'last' }); } } - /** - * Handles selecting a new item in the page size options. - * @param evt The select custom event. - */ private _onPageSizeChanged(evt: CustomEvent): void { evt.stopPropagation(); const pageSize = Number(evt.detail); const canPage = this._emitChangeEvent(PAGINATOR_CONSTANTS.strings.PAGE_SIZE, { pageIndex: 0, pageSize }); if (canPage) { - this.pageIndex = 0; - this.pageSize = pageSize; + this._applyPageIndex(0, { fromField: 'page-size' }); + this._applyPageSize(pageSize); } else { evt.preventDefault(); } @@ -351,72 +145,58 @@ export class PaginatorFoundation { return this._adapter.emitHostEvent(PAGINATOR_CONSTANTS.events.CHANGE, detail, true, true); } - /** - * Returns the max number of pages based on our current parameters. - */ private _getMaxPages(): number { return Math.ceil(this._total / this._pageSize) - 1; } - /** - * Updates our internal state as well as updating the UI. - */ - private _update(): void { - // Create and update the range label + private _updateRangeLabel(): void { if (this.pageSize > 1) { const startIndex = this._pageIndex * this._pageSize; const indexStart = Math.floor(startIndex / this._pageSize) || 0; const pageStart = (indexStart * this._pageSize) + 1; const pageEnd = startIndex < this._total ? Math.min(startIndex + this._pageSize, this._total) : startIndex + this._pageSize; - this._rangeLabel = `${pageStart}-${pageEnd} ${PAGINATOR_CONSTANTS.strings.RANGE_SEPARATOR_LABEL} ${this._total}`; } else { this._rangeLabel = `${this._pageIndex + 1} ${PAGINATOR_CONSTANTS.strings.RANGE_SEPARATOR_LABEL} ${this._total}`; } - this._adapter.setRangeLabel(this._rangeLabel); + } - if (this.disabled) { - this._adapter.disablePageSizeSelect(); - this._adapter.disableFirstPageButton(); - this._adapter.disablePreviousPageButton(); - this._adapter.disableNextPageButton(); - this._adapter.disableLastPageButton(); - } else { - this._adapter.enablePageSizeSelect(); - // Check if first page button needs to be enabled/disabled - if (this._hasFirstPage()) { - this._adapter.enableFirstPageButton(); - } else { - this._adapter.disableFirstPageButton(); + private _syncInteractionState(fromField: PaginatorFieldIdentifier | null = null): void { + this._adapter.enableFirstPageButton(); + this._adapter.enablePreviousPageButton(); + this._adapter.enableNextPageButton(); + this._adapter.enableLastPageButton(); + + if (!this._hasFirstPage()) { + if (fromField) { + this._adapter.handleFocusMove(fromField); } + this._adapter.disableFirstPageButton(); + } - // Check if previous page button needs to be enabled/disabled - if (this._hasPreviousPage()) { - this._adapter.enablePreviousPageButton(); - } else { - this._adapter.disablePreviousPageButton(); + if (!this._hasPreviousPage()) { + if (fromField) { + this._adapter.handleFocusMove(fromField); } + this._adapter.disablePreviousPageButton(); + } - // Check if next page button needs to be enabled/disabled - if (this._hasNextPage()) { - this._adapter.enableNextPageButton(); - } else { - this._adapter.disableNextPageButton(); + if (!this._hasNextPage()) { + if (fromField) { + this._adapter.handleFocusMove(fromField); } + this._adapter.disableNextPageButton(); + } - // Check if last page button needs to be enabled/disabled - if (this._hasLastPage()) { - this._adapter.enableLastPageButton(); - } else { - this._adapter.disableLastPageButton(); + if (!this._hasLastPage()) { + if (fromField) { + this._adapter.handleFocusMove(fromField); } + this._adapter.disableLastPageButton(); } } - /** - * Toggle showing/hiding first and last buttons based on the show first/last buttons flag. - */ private _toggleFirstLastButtons(): void { this._toggleFirstButton(); if (this._firstLast) { @@ -430,9 +210,6 @@ export class PaginatorFoundation { } } - /** - * Toggle showing/hiding first button based on the show first or first/last buttons flags. - */ private _toggleFirstButton(): void { if (this._first || this._firstLast) { if (!this._adapter.hasFirstPageButton()) { @@ -445,28 +222,217 @@ export class PaginatorFoundation { } } - /** Checks if a first page exists. */ private _hasFirstPage(): boolean { - // same as has previous page return this._hasPreviousPage(); } - /** - * Checks if a previous page exists. - */ private _hasPreviousPage(): boolean { - return this._pageIndex >= 1 && this._pageSize !== 0; + return this._pageIndex > 0 && this._pageSize > 0; } - /** Checks if a next page exists */ private _hasNextPage(): boolean { const maxPages = this._getMaxPages(); - return this._pageIndex < maxPages && this._pageSize !== 0; + return this._pageIndex < maxPages && this._pageSize > 0; } - /** Checks if a last page exists. */ private _hasLastPage(): boolean { - // same as has next page return this._hasNextPage(); } + + private _computePageIndexFromOffset(value: number): void { + if (value >= this._total) { + if (this._total >= this._pageSize) { + value = this._total - this._pageSize; + } else { + value = 0; + } + } + const clampedValue = Math.min(Math.max(value, 0), this._total); + const pageIndex = Math.floor(clampedValue / this._pageSize); + this._applyPageIndex(pageIndex); + } + + private _computeOffset(): void { + if (this._total > 0) { + this._offset = this._pageIndex * this._pageSize; + } + } + + private _applyPageIndex(value: number, { fromField = null }: { fromField?: PaginatorFieldIdentifier | null } = {}): void { + this._pageIndex = value; + this._computeOffset(); + this._updateRangeLabel(); + this._syncInteractionState(fromField); + this._adapter.toggleHostAttribute(PAGINATOR_CONSTANTS.attributes.PAGE_INDEX, this._pageIndex != null, this._pageIndex.toString()); + } + + private _applyPageSize(value: number): void { + this._pageSize = value; + this._adapter.setPageSize(this._pageSize); + this._computeOffset(); + this._updateRangeLabel(); + this._syncInteractionState(); + } + + private _applyTotal(value: number): void { + this._total = value; + this._updateRangeLabel(); + + if (this._offset > 0 && this._total > 0) { + this._computePageIndexFromOffset(this._offset); + } + + this._syncInteractionState(); + } + + private _applyAlternativeAlignment(): void { + this._adapter.setHostAttribute(PAGINATOR_CONSTANTS.attributes.ALIGNMENT, this._alignment); + this._adapter.setAlignment(this._alignment); + } + + private _applyDisabled(disabled: boolean): void { + this._disabled = disabled; + if (disabled) { + this._adapter.disablePageSizeSelect(); + this._adapter.disableFirstPageButton(); + this._adapter.disablePreviousPageButton(); + this._adapter.disableNextPageButton(); + this._adapter.disableLastPageButton(); + } else { + this._adapter.enablePageSizeSelect(); + this._syncInteractionState(); + } + } + + public get pageIndex(): number { + return this._pageIndex; + } + public set pageIndex(value: number) { + if (this._pageIndex !== value) { + if (isDefined(value)) { + this._applyPageIndex(value); + } else { + this._adapter.removeHostAttribute(PAGINATOR_CONSTANTS.attributes.PAGE_INDEX); + } + } + } + + public get pageSize(): number { + return this._pageSize; + } + public set pageSize(value: number) { + if (this._pageSize !== value) { + this._applyPageSize(value); + this._adapter.setHostAttribute(PAGINATOR_CONSTANTS.attributes.PAGE_SIZE, `${this._pageSize}`); + } + } + + public get offset(): number { + return this._offset; + } + public set offset(value: number) { + if (this._offset !== value) { + this._offset = value; + this._computePageIndexFromOffset(value); + } + } + + public get total(): number { + return this._total; + } + public set total(value: number) { + if (this._total !== value) { + this._applyTotal(value); + this._adapter.setHostAttribute(PAGINATOR_CONSTANTS.attributes.TOTAL, `${this._total}`); + } + } + + public get pageSizeOptions(): number[] | boolean { + return this._pageSizeOptions.map(o => Number(o.value)); + } + public set pageSizeOptions(options: number[] | boolean) { + if (isArray(options)) { + this._pageSizeOptions = (options as number[]) + .map(o => ({ label: o.toString(), value: o.toString() })) + .sort((a, b) => coerceNumber(a.value) - coerceNumber(b.value)); + this._adapter.setPageSizeOptions(this._pageSizeOptions); + this._adapter.attachPageSizeChangeListener(this._pageSizeListener); + this._adapter.setPageSizeVisibility(true); + if (isDefined(this._pageSize) && this._pageSizeOptions.length && !this._pageSizeOptions.find(o => coerceNumber(o.value) === this._pageSize)) { + const pageSize = coerceNumber(this._pageSizeOptions[0].value); + this._applyPageSize(pageSize); + } + } else if (options.toString().toLowerCase() === 'false') { + this._adapter.detachPageSizeChangeListener(this._pageSizeListener); + this._adapter.setPageSizeVisibility(false); + } + } + + public get label(): string { + return this._label; + } + public set label(value: string) { + if (this._label !== value) { + this._label = value; + this._adapter.setLabel(this._label); + this._adapter.setHostAttribute(PAGINATOR_CONSTANTS.attributes.LABEL, isDefined(this._label) ? this._label.toString() : ''); + } + } + + public get firstLast(): boolean { + return this._firstLast; + } + public set firstLast(value: boolean) { + value = Boolean(value); + if (this._firstLast !== value) { + this._firstLast = value; + this._toggleFirstLastButtons(); + this._adapter.toggleHostAttribute(PAGINATOR_CONSTANTS.attributes.FIRST_LAST, this._firstLast); + } + } + + public get first(): boolean { + return this._first; + } + public set first(value: boolean) { + value = Boolean(value); + if (this._first !== value) { + this._first = value; + this._toggleFirstButton(); + this._adapter.toggleHostAttribute(PAGINATOR_CONSTANTS.attributes.FIRST, this._first); + } + } + + public get disabled(): boolean { + return this._disabled; + } + public set disabled(value: boolean) { + value = Boolean(value); + if (this._disabled !== value) { + this._applyDisabled(value); + this._adapter.toggleHostAttribute(PAGINATOR_CONSTANTS.attributes.DISABLED, this._disabled); + } + } + + public get alternative(): boolean { + return this._alternative; + } + public set alternative(value: boolean) { + if (value !== this._alternative) { + this._alternative = value; + this._adapter.setAlternative(this._alternative); + this._applyAlternativeAlignment(); + this._adapter.toggleHostAttribute(PAGINATOR_CONSTANTS.attributes.ALTERNATIVE, this._alternative); + } + } + + public get alignment(): PaginatorAlternativeAlignment { + return this._alignment; + } + public set alignment(value: PaginatorAlternativeAlignment) { + if (value !== this._alignment) { + this._alignment = value; + this._applyAlternativeAlignment(); + } + } } diff --git a/src/lib/paginator/paginator.ts b/src/lib/paginator/paginator.ts index f6723d31c..836eb055d 100644 --- a/src/lib/paginator/paginator.ts +++ b/src/lib/paginator/paginator.ts @@ -37,8 +37,6 @@ declare global { } /** - * The custom element class behind the `` component. - * * @tag forge-paginator */ @CustomElement({ diff --git a/src/lib/popup/build.json b/src/lib/popup/build.json index fa91a7b63..1cc25c673 100644 --- a/src/lib/popup/build.json +++ b/src/lib/popup/build.json @@ -1,5 +1,5 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json", "stylesheets": [ "./forge-popup.scss" diff --git a/src/lib/popup/popup-adapter.ts b/src/lib/popup/popup-adapter.ts index f078f3d82..9d15ed6f6 100644 --- a/src/lib/popup/popup-adapter.ts +++ b/src/lib/popup/popup-adapter.ts @@ -1,6 +1,6 @@ import { addClass, closestElement, emitEvent, getShadowElement, IPositionElementConfig, notChildEventListener, positionElementAsync, removeClass, removeElement, deepQuerySelectorAll, getActiveElement } from '@tylertech/forge-core'; import { BaseAdapter, IBaseAdapter } from '../core/base/base-adapter'; -import { IPopupComponent } from './popup'; +import { IPopupComponent, IPopupCloseEventData } from './popup'; import { IPopupPositionEventData, POPUP_CONSTANTS, PopupPlacement } from './popup-constants'; export interface IPopupAdapter extends IBaseAdapter { @@ -17,6 +17,7 @@ export interface IPopupAdapter extends IBaseAdapter { removeEventListener(type: string, listener: (evt: Event) => void): void; setBlurListener(listener: () => void): () => void; trySetInitialFocus(): void; + getCloseEventData(): IPopupCloseEventData; } export class PopupAdapter extends BaseAdapter implements IPopupAdapter { @@ -130,6 +131,10 @@ export class PopupAdapter extends BaseAdapter implements IPopup return false; } + public getCloseEventData(): IPopupCloseEventData { + return { popup: this._component }; + } + public addClass(classes: string | string[]): void { addClass(classes, this._rootElement); } diff --git a/src/lib/popup/popup-foundation.ts b/src/lib/popup/popup-foundation.ts index 95877b276..3515f7fe7 100644 --- a/src/lib/popup/popup-foundation.ts +++ b/src/lib/popup/popup-foundation.ts @@ -1,6 +1,6 @@ import { ICustomElementFoundation, isElement } from '@tylertech/forge-core'; import { IPopupAdapter } from './popup-adapter'; -import { IPopupPosition, PopupAnimationType, PopupPlacement, PopupStateCallback, POPUP_CONSTANTS as constants, POPUP_CONSTANTS } from './popup-constants'; +import { IPopupPosition, PopupAnimationType, PopupPlacement, PopupStateCallback, POPUP_CONSTANTS } from './popup-constants'; export interface IPopupFoundation extends ICustomElementFoundation { targetElement: HTMLElement; @@ -66,7 +66,7 @@ export class PopupFoundation implements IPopupFoundation { } this._adapter.manageWindowEvents(true); - this._adapter.dispatchEvent(constants.events.OPEN); + this._adapter.dispatchEvent(POPUP_CONSTANTS.events.OPEN); } private _closePopup(): void { @@ -85,7 +85,9 @@ export class PopupFoundation implements IPopupFoundation { private _destroyPopup(): void { this._adapter.manageWindowEvents(false); this._adapter.removePopup(this._manageFocus); - this._adapter.dispatchEvent(constants.events.CLOSE); + const eventData = this._adapter.getCloseEventData(); + this._adapter.dispatchEvent(POPUP_CONSTANTS.events.CLOSE, eventData); + this._adapter.emitHostEvent(POPUP_CONSTANTS.events.CLOSE, eventData); this._adapter.removeAttribute(POPUP_CONSTANTS.attributes.OPEN); } diff --git a/src/lib/popup/popup.ts b/src/lib/popup/popup.ts index 23b0c0ea3..7cedc578e 100644 --- a/src/lib/popup/popup.ts +++ b/src/lib/popup/popup.ts @@ -23,6 +23,10 @@ export interface IPopupComponent extends IBaseComponent { closeCallback: PopupStateCallback; } +export interface IPopupCloseEventData { + popup: IPopupComponent; +} + declare global { interface HTMLElementTagNameMap { 'forge-popup': IPopupComponent; @@ -30,7 +34,7 @@ declare global { interface HTMLElementEventMap { 'forge-popup-open': CustomEvent; - 'forge-popup-close': CustomEvent; + 'forge-popup-close': CustomEvent; 'forge-popup-position': CustomEvent; 'forge-popup-blur': CustomEvent; } diff --git a/src/lib/product-icon/build.json b/src/lib/product-icon/build.json index d8638d477..07c79a465 100644 --- a/src/lib/product-icon/build.json +++ b/src/lib/product-icon/build.json @@ -1,4 +1,4 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json" } \ No newline at end of file diff --git a/src/lib/quantity-field/build.json b/src/lib/quantity-field/build.json index 75276503f..8c96a0641 100644 --- a/src/lib/quantity-field/build.json +++ b/src/lib/quantity-field/build.json @@ -1,5 +1,5 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json", "stylesheets": [ "./forge-quantity-field.scss" diff --git a/src/lib/radio/build.json b/src/lib/radio/build.json index d8638d477..07c79a465 100644 --- a/src/lib/radio/build.json +++ b/src/lib/radio/build.json @@ -1,4 +1,4 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json" } \ No newline at end of file diff --git a/src/lib/ripple/build.json b/src/lib/ripple/build.json index 7de02b258..0153874aa 100644 --- a/src/lib/ripple/build.json +++ b/src/lib/ripple/build.json @@ -1,5 +1,5 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json", "stylesheets": [ "./forge-ripple.scss" diff --git a/src/lib/scaffold/build.json b/src/lib/scaffold/build.json index bc8cf624f..69a8f23e3 100644 --- a/src/lib/scaffold/build.json +++ b/src/lib/scaffold/build.json @@ -1,5 +1,5 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json", "stylesheets": [ "./forge-scaffold.scss" diff --git a/src/lib/select/build.json b/src/lib/select/build.json index d8638d477..07c79a465 100644 --- a/src/lib/select/build.json +++ b/src/lib/select/build.json @@ -1,4 +1,4 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json" } \ No newline at end of file diff --git a/src/lib/select/core/base-select-adapter.ts b/src/lib/select/core/base-select-adapter.ts index 512edcbf6..71c36e53b 100644 --- a/src/lib/select/core/base-select-adapter.ts +++ b/src/lib/select/core/base-select-adapter.ts @@ -86,6 +86,7 @@ export abstract class BaseSelectAdapter extends BaseAdapter; private _trailingIcon: string; private _trailingIconClass: string; private _trailingIconType: ListDropdownIconType; + private _trailingIconComponentProps: Partial; private _leadingBuilder: () => HTMLElement; private _trailingBuilder: () => HTMLElement; @@ -45,6 +49,17 @@ export class OptionFoundation implements IOptionFoundation { } } + /** Gets/sets the secondary label of this option. */ + public get secondaryLabel(): string { + return this._secondaryLabel; + } + public set secondaryLabel(value: string) { + if (this._secondaryLabel !== value) { + this._secondaryLabel = value; + this._adapter.toggleHostAttribute(OPTION_CONSTANTS.attributes.SECONDARY_LABEL, !!this._secondaryLabel, this._secondaryLabel); + } + } + /** Gets/sets the disabled status of this option. */ public get disabled(): boolean { return this._disabled; @@ -119,6 +134,16 @@ export class OptionFoundation implements IOptionFoundation { } } + /** Gets/sets the props on the leading icon component. */ + public get leadingIconComponentProps(): Partial { + return this._leadingIconComponentProps; + } + public set leadingIconComponentProps(value: Partial) { + if (this._leadingIconComponentProps !== value) { + this._leadingIconComponentProps = value; + } + } + /** Gets/sets the trailing icon of this option. */ public get trailingIcon(): string { return this._trailingIcon; @@ -152,6 +177,16 @@ export class OptionFoundation implements IOptionFoundation { } } + /** Gets/sets the props on the trailing icon component. */ + public get trailingIconComponentProps(): Partial { + return this._trailingIconComponentProps; + } + public set trailingIconComponentProps(value: Partial) { + if (this._trailingIconComponentProps !== value) { + this._trailingIconComponentProps = value; + } + } + /** Gets/sets the leading builder of this option. */ public get leadingBuilder(): (() => HTMLElement) { return this._leadingBuilder; diff --git a/src/lib/select/option/option.ts b/src/lib/select/option/option.ts index 16fbebc02..fdbdb9ef0 100644 --- a/src/lib/select/option/option.ts +++ b/src/lib/select/option/option.ts @@ -1,4 +1,5 @@ import { coerceBoolean, CustomElement, FoundationProperty, ICustomElement } from '@tylertech/forge-core'; +import { IIconComponent } from '../../icon'; import { BaseComponent } from '../../core/base/base-component'; import { IBaseListDropdownOption, ListDropdownIconType } from '../../list-dropdown/list-dropdown-constants'; import { OptionAdapter } from './option-adapter'; @@ -26,6 +27,7 @@ export class OptionComponent extends BaseComponent implements IOptionComponent { return [ OPTION_CONSTANTS.attributes.VALUE, OPTION_CONSTANTS.attributes.LABEL, + OPTION_CONSTANTS.attributes.SECONDARY_LABEL, OPTION_CONSTANTS.attributes.DISABLED, OPTION_CONSTANTS.attributes.DIVIDER, OPTION_CONSTANTS.attributes.OPTION_CLASS, @@ -59,6 +61,9 @@ export class OptionComponent extends BaseComponent implements IOptionComponent { case OPTION_CONSTANTS.attributes.LABEL: this.label = newValue; break; + case OPTION_CONSTANTS.attributes.SECONDARY_LABEL: + this.secondaryLabel = newValue; + break; case OPTION_CONSTANTS.attributes.DISABLED: this.disabled = coerceBoolean(newValue); break; @@ -97,6 +102,10 @@ export class OptionComponent extends BaseComponent implements IOptionComponent { @FoundationProperty() public declare label: string; + /** Gets/sets the secondary label of this option. */ + @FoundationProperty() + public declare secondaryLabel: string; + /** Gets/sets the disabled status of this option. */ @FoundationProperty() public declare disabled: boolean; @@ -121,6 +130,10 @@ export class OptionComponent extends BaseComponent implements IOptionComponent { @FoundationProperty() public declare leadingIconType: ListDropdownIconType; + /** Gets/sets properties on leading icon component. */ + @FoundationProperty() + public declare leadingIconComponentProps: Partial; + /** Gets/sets the trailing icon of this option. */ @FoundationProperty() public declare trailingIcon: string; @@ -133,6 +146,10 @@ export class OptionComponent extends BaseComponent implements IOptionComponent { @FoundationProperty() public declare trailingIconType: ListDropdownIconType; + /** Gets/sets properties on trailing icon component. */ + @FoundationProperty() + public declare trailingIconComponentProps: Partial; + /** Gets/sets the leading builder of this option. */ @FoundationProperty() public declare leadingBuilder: () => HTMLElement; diff --git a/src/lib/skeleton/build.json b/src/lib/skeleton/build.json index d8638d477..07c79a465 100644 --- a/src/lib/skeleton/build.json +++ b/src/lib/skeleton/build.json @@ -1,4 +1,4 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json" } \ No newline at end of file diff --git a/src/lib/slider/_core.scss b/src/lib/slider/_core.scss index a2f0b1117..ed0df27c6 100644 --- a/src/lib/slider/_core.scss +++ b/src/lib/slider/_core.scss @@ -98,13 +98,7 @@ $_active-track-end-clip: calc($_active-track-end-offset + $_active-track-max-cli @mixin track-inactive { block-size: var(--_inactive-track-height); border-radius: var(--_inactive-track-shape); - - // TODO: remove this and the @supports check below when we're confident that color-mix is more widely supported - background-color: var(--_inactive-track-color-fallback); - - @supports (background-color: color-mix(in srgb, red 50%, transparent)) { - background-color: var(--_inactive-track-color); - } + background-color: var(--_inactive-track-color); } @mixin track-active-disabled { @@ -200,7 +194,7 @@ $_active-track-end-clip: calc($_active-track-end-offset + $_active-track-max-cli } @mixin handle-label { - @include typography.style(label, [color font-weight]); + @include typography.style(label, $exclude: [font-weight]); position: absolute; box-sizing: border-box; diff --git a/src/lib/slider/build.json b/src/lib/slider/build.json index d8638d477..07c79a465 100644 --- a/src/lib/slider/build.json +++ b/src/lib/slider/build.json @@ -1,4 +1,4 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json" } \ No newline at end of file diff --git a/src/lib/slider/slider.html b/src/lib/slider/slider.html index e87b06219..765abbc97 100644 --- a/src/lib/slider/slider.html +++ b/src/lib/slider/slider.html @@ -6,8 +6,8 @@
- - + +
diff --git a/src/lib/slider/slider.scss b/src/lib/slider/slider.scss index fa13af04a..2d43fc59a 100644 --- a/src/lib/slider/slider.scss +++ b/src/lib/slider/slider.scss @@ -131,11 +131,11 @@ &.tickmarks { &::before { - @include core.tickmarks-active-disabled; + @include core.tickmarks-inactive-disabled; } &::after { - @include core.tickmarks-inactive-disabled; + @include core.tickmarks-active-disabled; } } } diff --git a/src/lib/slider/slider.test.ts b/src/lib/slider/slider.test.ts index 47c1e859f..90a42f65f 100644 --- a/src/lib/slider/slider.test.ts +++ b/src/lib/slider/slider.test.ts @@ -9,7 +9,6 @@ import { SLIDER_CONSTANTS } from './slider-constants'; import type { IStateLayerComponent } from '../state-layer/state-layer'; import './slider'; -import { STATE_LAYER_CONSTANTS } from '../state-layer'; class SliderHarness extends TestHarness { public rootElement: HTMLElement; diff --git a/src/lib/split-view/build.json b/src/lib/split-view/build.json index d8638d477..07c79a465 100644 --- a/src/lib/split-view/build.json +++ b/src/lib/split-view/build.json @@ -1,4 +1,4 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json" } \ No newline at end of file diff --git a/src/lib/state-layer/build.json b/src/lib/state-layer/build.json index 79c49f7e5..d6e9c5322 100644 --- a/src/lib/state-layer/build.json +++ b/src/lib/state-layer/build.json @@ -1,4 +1,4 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json" } diff --git a/src/lib/stepper/build.json b/src/lib/stepper/build.json index d8638d477..07c79a465 100644 --- a/src/lib/stepper/build.json +++ b/src/lib/stepper/build.json @@ -1,4 +1,4 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json" } \ No newline at end of file diff --git a/src/lib/switch/build.json b/src/lib/switch/build.json index d8638d477..07c79a465 100644 --- a/src/lib/switch/build.json +++ b/src/lib/switch/build.json @@ -1,4 +1,4 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json" } \ No newline at end of file diff --git a/src/lib/switch/index.ts b/src/lib/switch/index.ts index df72d2bbc..c9dd8ef7d 100644 --- a/src/lib/switch/index.ts +++ b/src/lib/switch/index.ts @@ -2,7 +2,9 @@ import { defineCustomElement } from '@tylertech/forge-core'; import { SwitchComponent } from './switch'; +export * from './switch-adapter'; export * from './switch-constants'; +export * from './switch-foundation'; export * from './switch'; export * from './switch-component-delegate'; diff --git a/src/lib/switch/switch.scss b/src/lib/switch/switch.scss index 62f5df776..e477d0e98 100644 --- a/src/lib/switch/switch.scss +++ b/src/lib/switch/switch.scss @@ -22,20 +22,6 @@ .container { @include core.container; - - &:active { - .track { - @include core.track-active; - } - - .handle { - @include core.handle-active; - - .icon { - @include core.icon-active; - } - } - } } .input { @@ -78,22 +64,6 @@ // On :host([on]) { - .container { - &:active { - .track { - @include core.track-active-on; - } - - .handle { - @include core.handle-active-on; - - .icon { - @include core.icon-active-on; - } - } - } - } - .track { @include core.track-on; } @@ -120,6 +90,42 @@ } } +// Active +:host(:not([disabled]):not([readonly])) { + .forge-switch { + .container:active { + .track { + @include core.track-active; + } + + .handle { + @include core.handle-active; + + .icon { + @include core.icon-active; + } + } + } + } +} +:host(:not([disabled]):not([readonly])[on]) { + .forge-switch { + .container:active { + .track { + @include core.track-active-on; + } + + .handle { + @include core.handle-active-on; + + .icon { + @include core.icon-active-on; + } + } + } + } +} + // Dense :host([dense]) { .forge-switch { diff --git a/src/lib/table/build.json b/src/lib/table/build.json index 4bf4c9a2e..5a35b3ce0 100644 --- a/src/lib/table/build.json +++ b/src/lib/table/build.json @@ -1,5 +1,5 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json", "stylesheets": [ "./forge-table.scss" diff --git a/src/lib/tabs/build.json b/src/lib/tabs/build.json index d8638d477..07c79a465 100644 --- a/src/lib/tabs/build.json +++ b/src/lib/tabs/build.json @@ -1,4 +1,4 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json" } \ No newline at end of file diff --git a/src/lib/tabs/tab/_configuration.scss b/src/lib/tabs/tab/_configuration.scss index fb98b1626..b87b1e9ba 100644 --- a/src/lib/tabs/tab/_configuration.scss +++ b/src/lib/tabs/tab/_configuration.scss @@ -5,6 +5,10 @@ --_active-color: #{tab-tokens.get(active-color)}; --_inactive-color: #{tab-tokens.get(inactive-color)}; --_height: #{tab-tokens.get(height)}; + --_stacked-height: #{tab-tokens.get(stacked-height)}; + + // Disabled + --_disabled-opacity: #{tab-tokens.get(disabled-opacity)}; // Indicator --_active-indicator-color: #{tab-tokens.get(indicator-color)}; @@ -18,6 +22,7 @@ // Content --_content-height: #{tab-tokens.get(content-height)}; + --_content-padding: #{tab-tokens.get(content-padding)}; // Icon --_active-focus-icon-color: #{tab-tokens.get(active-focus-icon-color)}; diff --git a/src/lib/tabs/tab/_core.scss b/src/lib/tabs/tab/_core.scss index 056d3e32f..f6ffc49b1 100644 --- a/src/lib/tabs/tab/_core.scss +++ b/src/lib/tabs/tab/_core.scss @@ -1,7 +1,6 @@ @use '../../core/styles/utils'; @use '../../core/styles/tokens/tabs/tab/tokens'; @use '../../core/styles/typography'; -@use '../../core/styles/spacing'; @mixin provide-theme($theme) { @include utils.provide(tokens.$tokens, $theme, tab); @@ -16,7 +15,10 @@ @mixin host-disabled { cursor: not-allowed; - opacity: #{tokens.get(disabled-opacity)}; +} + +@mixin disabled { + opacity: var(--_disabled-opacity); } @mixin tab { @@ -118,12 +120,10 @@ justify-content: center; white-space: nowrap; transition: 150ms color linear; - - $_content-padding: spacing.variable('100'); - max-height: calc(var(--_content-height) + 2 * $_content-padding); + max-height: calc(var(--_content-height) + 2 * var(--_content-padding)); min-height: var(--_content-height); - padding: $_content-padding calc(2 * $_content-padding); - gap: spacing.variable('050'); + padding: var(--_content-padding) calc(2 * var(--_content-padding)); + gap: var(--_content-padding); } @mixin label { diff --git a/src/lib/tabs/tab/tab.html b/src/lib/tabs/tab/tab.html index f9587cc07..dde078b7e 100644 --- a/src/lib/tabs/tab/tab.html +++ b/src/lib/tabs/tab/tab.html @@ -8,7 +8,7 @@ - - + +
\ No newline at end of file diff --git a/src/lib/tabs/tab/tab.scss b/src/lib/tabs/tab/tab.scss index 8adce6796..25771efb9 100644 --- a/src/lib/tabs/tab/tab.scss +++ b/src/lib/tabs/tab/tab.scss @@ -1,6 +1,5 @@ @use './core'; @use './configuration'; -@use '../../core/styles/tokens/tabs/tab/tokens'; @use '../../focus-indicator'; @use '../../state-layer'; @@ -28,6 +27,8 @@ @include core.host-disabled; .forge-tab { + @include core.disabled; + pointer-events: none; } } @@ -80,7 +81,7 @@ :host([stacked]) { .forge-tab { - --_height: #{tokens.get(stacked-height)}; + --_height: var(--_stacked-height); .content { @include core.content-stacked; diff --git a/src/lib/text-field/build.json b/src/lib/text-field/build.json index d8638d477..07c79a465 100644 --- a/src/lib/text-field/build.json +++ b/src/lib/text-field/build.json @@ -1,4 +1,4 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json" } \ No newline at end of file diff --git a/src/lib/theme/_theme.scss b/src/lib/theme/_theme.scss index 214d25d4e..24a3a86c3 100644 --- a/src/lib/theme/_theme.scss +++ b/src/lib/theme/_theme.scss @@ -9,12 +9,7 @@ @use './keys'; @use './theme-values'; @use './theme-utils'; -@use '../core/styles/theme'; - -@mixin theme-styles($prefix: forge) { - @include theme.properties; - @include theme-utils.core-styles; -} +@use '../core/styles/scrollbar'; @function text-emphasis($emphasis) { @return map.get(theme-values.$text-emphasis, $emphasis); @@ -156,45 +151,8 @@ ), $important: $important); } -@mixin scrollbar() { - &::-webkit-scrollbar { - @include css-custom-property(width, --forge-scrollbar-width, theme-values.$scrollbar-width); - @include css-custom-property(height, --forge-scrollbar-height, theme-values.$scrollbar-height); - } - - &::-webkit-scrollbar-corner { - @include property(background-color, scrollbar-track); - } - - &::-webkit-scrollbar-track { - @include property(background-color, scrollbar-track); - - &:hover { - @include property(background-color, scrollbar-track-hover); - } - } - - &::-webkit-scrollbar-thumb { - @include property(background-color, scrollbar-thumb); - @include css-custom-property(height, --forge-scrollbar-min-height, theme-values.$scrollbar-thumb-min-height); - @include css-custom-property(width, --forge-scrollbar-min-width, theme-values.$scrollbar-thumb-min-width); - @include css-custom-property(border-radius, --forge-scrollbar-border-radius, 10px); - @include css-custom-property(border-width, --forge-scrollbar-border-width, 3px); - - border-style: solid; - border-color: transparent; - background-clip: content-box; - - &:hover { - @include property(background-color, scrollbar-thumb-hover); - } - } -} - -@mixin scrollbar-theme-styles() { - * { - @include scrollbar; - } +@mixin scrollbar { + @include scrollbar.base; } @function elevation($z-value, $color: elevation-theme.$baseline-color, $opacity-boost: 0) { diff --git a/src/lib/theme/build.json b/src/lib/theme/build.json index ef973ae2d..3ce83a65e 100644 --- a/src/lib/theme/build.json +++ b/src/lib/theme/build.json @@ -1,5 +1,5 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json", "stylesheets": [ "./forge-theme.scss" diff --git a/src/lib/theme/forge-theme.scss b/src/lib/theme/forge-theme.scss index f1c583395..5b111a9f4 100644 --- a/src/lib/theme/forge-theme.scss +++ b/src/lib/theme/forge-theme.scss @@ -1,4 +1,22 @@ -@use './theme'; +@use '../core/styles/theme'; +@use '../core/styles/scrollbar'; -@include theme.theme-styles; -@include theme.scrollbar-theme-styles; +// Temporary theme properties for compatibility with MDC theme tokens +// +// TODO: remove this once MDC dependency is removed +:root { + @include theme.properties-compat; +} + +// Root theme properties +:root { + @include theme.properties; +} + +// Custom scrollbar styles +* { + @include scrollbar.base; +} + +// Utility classes for all theme tokens +@include theme.classes; diff --git a/src/lib/time-picker/build.json b/src/lib/time-picker/build.json index d8638d477..07c79a465 100644 --- a/src/lib/time-picker/build.json +++ b/src/lib/time-picker/build.json @@ -1,4 +1,4 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json" } \ No newline at end of file diff --git a/src/lib/time-picker/time-picker-foundation.ts b/src/lib/time-picker/time-picker-foundation.ts index 8208713dd..b6257786d 100644 --- a/src/lib/time-picker/time-picker-foundation.ts +++ b/src/lib/time-picker/time-picker-foundation.ts @@ -287,13 +287,13 @@ export class TimePickerFoundation implements ITimePickerFoundation { } private _onInputFocus(evt: Event): void { - if (this._allowInput) { - this._adapter.selectInputText(); - } - if (this.masked && this._showMaskFormat) { this._applyMask(); } + + if (this._allowInput) { + this._adapter.selectInputText(); + } } private _onInputBlur(evt: Event): void { diff --git a/src/lib/toast/build.json b/src/lib/toast/build.json index d8638d477..07c79a465 100644 --- a/src/lib/toast/build.json +++ b/src/lib/toast/build.json @@ -1,4 +1,4 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json" } \ No newline at end of file diff --git a/src/lib/toolbar/build.json b/src/lib/toolbar/build.json index d8638d477..07c79a465 100644 --- a/src/lib/toolbar/build.json +++ b/src/lib/toolbar/build.json @@ -1,4 +1,4 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json" } \ No newline at end of file diff --git a/src/lib/tooltip/build.json b/src/lib/tooltip/build.json index d19085f92..f7224f76c 100644 --- a/src/lib/tooltip/build.json +++ b/src/lib/tooltip/build.json @@ -1,5 +1,5 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json", "stylesheets": [ "./forge-tooltip.scss" diff --git a/src/lib/tsconfig-build.json b/src/lib/tsconfig-build.json index bf791e930..f40d3b3ed 100644 --- a/src/lib/tsconfig-build.json +++ b/src/lib/tsconfig-build.json @@ -3,7 +3,7 @@ "target": "es2017", "module": "es2015", "moduleResolution": "node", - "lib": ["es2015", "dom", "es2017"], + "lib": ["es2015", "dom", "es2017", "dom.iterable"], "experimentalDecorators": true, "noImplicitAny": true, "strictNullChecks": true, diff --git a/src/lib/typography/_mixins.scss b/src/lib/typography/_mixins.scss index 5fc128b9f..2bd4e2155 100644 --- a/src/lib/typography/_mixins.scss +++ b/src/lib/typography/_mixins.scss @@ -9,26 +9,6 @@ @use '../theme/custom-properties'; @mixin core-styles() { - .forge-typography { - @include mdc-typography.base; - @include theme.css-custom-property(color, --mdc-theme-text-primary-on-background, mdc-theme-color.prop-value(text-primary-on-background)); - } - - // Create custom classes for built-in MDC typography styles - @each $style in map.keys(mdc-typography.$styles) { - .forge-typography--#{$style} { - @include mdc-typography.typography($style); - @include apply-additional-styles(variables.$mdc-typography-additional-styles, $style); - } - } - - // Create classes for forge-specific typography styles - @each $style in map.keys(variables.$styles) { - .forge-typography--#{$style} { - @include typography($style); - } - } - :root { // forge-specific MDC typography overrides @each $style, $properties in variables.$mdc-typography-overrides { diff --git a/src/lib/typography/build.json b/src/lib/typography/build.json index 4c518815e..8c44fd6d1 100644 --- a/src/lib/typography/build.json +++ b/src/lib/typography/build.json @@ -1,5 +1,5 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json", "compile": false, "stylesheets": [ diff --git a/src/lib/utils/build.json b/src/lib/utils/build.json index d9f75c4cf..989896bde 100644 --- a/src/lib/utils/build.json +++ b/src/lib/utils/build.json @@ -1,5 +1,5 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json", "stylesheets": [ "./forge-flex.scss", diff --git a/src/lib/view-switcher/build.json b/src/lib/view-switcher/build.json index d8638d477..07c79a465 100644 --- a/src/lib/view-switcher/build.json +++ b/src/lib/view-switcher/build.json @@ -1,4 +1,4 @@ { - "$schema": "../../../node_modules/@tylertech/forge-cli/build-schema.json", + "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json", "extends": "../build.json" } \ No newline at end of file diff --git a/src/stories/StorybookMdxProvider.tsx b/src/stories/StorybookMdxProvider.tsx index 0dccbf49b..a62fab9c1 100644 --- a/src/stories/StorybookMdxProvider.tsx +++ b/src/stories/StorybookMdxProvider.tsx @@ -16,9 +16,9 @@ import { LiveDemoStage } from './src/core/live-demo-stage'; export const StorybookMdxComponents: Record = { wrapper: ({ children }: any) => {children}, - h1: (props: any) =>

{props.children}

, - h2: (props: any) =>

{props.children}

, - h3: (props: any) =>

{props.children}

, + h1: (props: any) =>

{props.children}

, + h2: (props: any) =>

{props.children}

, + h3: (props: any) =>

{props.children}

, table: (props: any) => , section: (props: any) =>
{props.children}
, blockquote: (props: any) =>
, diff --git a/src/stories/src/components/autocomplete/autocomplete.mdx b/src/stories/src/components/autocomplete/autocomplete.mdx index 677fe6446..9696ac161 100644 --- a/src/stories/src/components/autocomplete/autocomplete.mdx +++ b/src/stories/src/components/autocomplete/autocomplete.mdx @@ -173,6 +173,14 @@ Closes the dropdown. + + +Forces the filter callback to be executed to update the current selection state with new options. + +Call this when you need to update the options of an autocomplete dynamically by forcing the filter callback to execute and clear any existing options. + + + --- @@ -285,15 +293,18 @@ enum AutocompleteMode { interface IAutocompleteOption { value: T; label: string; + secondaryLabel?: string; disabled?: boolean; divider?: boolean; optionClass?: string | string[]; leadingIcon?: string; leadingIconClass?: string; leadingIconType?: ListDropdownIconType; + leadingIconComponentProps?: Partial; trailingIcon?: string; trailingIconClass?: string; trailingIconType?: ListDropdownIconType; + trailingIconComponentProps?: Partial; leadingBuilder?: () => HTMLElement; trailingBuilder?: () => HTMLElement; options?: Array; @@ -326,4 +337,12 @@ interface IAutocompletePopupConfiguration { } ``` +### IAutocompleteForceFilterOptions + +```ts +interface IAutocompleteForceFilterOptions { + preserveValue: boolean; +} +``` + diff --git a/src/stories/src/components/backdrop/backdrop.stories.tsx b/src/stories/src/components/backdrop/backdrop.stories.tsx index 8a47cdaa5..b8fc5ca0a 100644 --- a/src/stories/src/components/backdrop/backdrop.stories.tsx +++ b/src/stories/src/components/backdrop/backdrop.stories.tsx @@ -40,7 +40,7 @@ export const Default: Story = ({ -

(When open, click backdrop to close)

+

(When open, click backdrop to close)

+ +# Button area + +The button area component wraps any arbitrary content with a ` +
+
+
Heading
+
Content
+
+ + + Favorite + + +
+ + + ) +}; +Default.argTypes = buttonAreaArgTypes; +Default.args = { + label: 'Label', + disabled: false, +} as IButtonAreaProps; + +export const InExpansionPanel: Story = ({ + label = 'Toggle panel', + disabled = false, +}) => { + useEffect(() => { + IconRegistry.define([tylIconChevronRight, tylIconFavorite]); + }, []); + + const buttonRef = useRef(null); + + function onToggleExpansionPanel({ detail }: CustomEvent): void { + buttonRef.current?.setAttribute('aria-expanded', detail.toString()); + } + + return ( + + + + +
+
+
Heading
+
Content
+
+ + + Favorite + + +
+
+
Content
+
+
+ ) +}; +InExpansionPanel.argTypes = buttonAreaArgTypes; +InExpansionPanel.args = { + label: 'Toggle panel', + disabled: false, +} as IButtonAreaProps; diff --git a/src/stories/src/components/button-area/code/button-area-default.ts b/src/stories/src/components/button-area/code/button-area-default.ts new file mode 100644 index 000000000..816534ef5 --- /dev/null +++ b/src/stories/src/components/button-area/code/button-area-default.ts @@ -0,0 +1,107 @@ +export const ButtonAreaDefaultCodeHtml = () => { + return ` + + + +
+
+ Heading + Content +
+ + + Favorite + + +
+ +
+ `; +}; + +export const ButtonAreaDefaultCodeCss = () => { + return ` +forge-card { + --forge-card-padding: 0; + + width: 320px; + max-width: 100%; +} + +.content { + display: flex; + align-items: center; + gap: 8px; + padding: 16px; +} + +.content::first-child() { + margin-inline-end: auto; +} + `; +}; + +export const ButtonAreaInExpansionPanelCodeHtml = () => { + return ` + + + + +
+
+ Heading + Content +
+ + + Favorite + + +
+ +
Content
+
+
+ `; +}; + +export const ButtonAreaInExpansionPanelCodeCss = () => { + return ` +forge-card { + --forge-card-padding: 0; + + width: 320px; + max-width: 100%; +} + +.header-content { + display: flex; + align-items: center; + gap: 8px; + padding: 16px; +} + +.header-content::first-child() { + margin-inline-end: auto; +} + +.content { + margin: 16px; +} + `; +}; + +export const ButtonAreaInExpansionPanelCodeTs = () => { + return ` +const expansionPanel = document.getElementById('expansion-panel'); +const button = document.getElementById('button'); + +expansionPanel.addEventListener('forge-expansion-panel-toggle', ({ detail: boolean }) => { + button.setAttribute('aria-expanded', detail.toString()); +}); + `; +}; \ No newline at end of file diff --git a/src/stories/src/components/card/card.stories.tsx b/src/stories/src/components/card/card.stories.tsx index 3c5763a20..91cf02c1f 100644 --- a/src/stories/src/components/card/card.stories.tsx +++ b/src/stories/src/components/card/card.stories.tsx @@ -63,7 +63,7 @@ export const Styled: Story = ({
-

This is the card title

+

This is the card title

-

{LOREM_IPSUM.p1}

+

{LOREM_IPSUM.p1}

@@ -98,10 +98,10 @@ export const WithScaffold: Story = ({ -

Lorem ipsum

+

Lorem ipsum

-

+

Lorem ipsum dolor sit amet consectetur adipisicing elit. Molestias quas sed aliquid cumque sunt iste ad, alias quod adipisci? Nulla, libero necessitatibus enim sint nesciunt provident excepturi dolorum pariatur illum? diff --git a/src/stories/src/components/card/code/card-scaffold.ts b/src/stories/src/components/card/code/card-scaffold.ts index a36523d67..4eccf747a 100644 --- a/src/stories/src/components/card/code/card-scaffold.ts +++ b/src/stories/src/components/card/code/card-scaffold.ts @@ -3,10 +3,10 @@ export const CardScaffoldCodeHtml = () => { -

Lorem ipsum

+

Lorem ipsum

-

+

Lorem ipsum dolor sit amet consectetur adipisicing elit. Molestias quas sed aliquid cumque sunt iste ad, alias quod adipisci? Nulla, libero necessitatibus enim sint nesciunt provident excepturi dolorum pariatur illum? diff --git a/src/stories/src/components/card/code/card-styled.ts b/src/stories/src/components/card/code/card-styled.ts index f2b87f205..cab4cc069 100644 --- a/src/stories/src/components/card/code/card-styled.ts +++ b/src/stories/src/components/card/code/card-styled.ts @@ -3,7 +3,7 @@ export const CardStyledCodeHtml = () => {

-

This is the card title

+

This is the card title

-

+

Lorem, ipsum dolor sit amet consectetur adipisicing elit. Molestias exercitationem doloremque dolorem ullam, nesciunt quia velit necessitatibus numquam quasi voluptates impedit earum dolores repudiandae facilis totam non quo labore itaque?

diff --git a/src/stories/src/components/chip-field/chip-field.mdx b/src/stories/src/components/chip-field/chip-field.mdx index 8c348c0f6..3b2450543 100644 --- a/src/stories/src/components/chip-field/chip-field.mdx +++ b/src/stories/src/components/chip-field/chip-field.mdx @@ -145,6 +145,8 @@ Controls the floating state of the label. Call this to manually float the label. | `label-input-container` | The container element around the label and input slot. | `input-container` | The container element around the input slot only. | `addon-end-container` | The container element for the `addon-end` slot. +| `leading-container` | The container element for the `leading` slot. +| `trailing-container` | The container element for the `trailing` slot. diff --git a/src/stories/src/components/keyboard-shortcut/keyboard-shortcut.mdx b/src/stories/src/components/keyboard-shortcut/keyboard-shortcut.mdx index a3db9e8ae..b19e90f62 100644 --- a/src/stories/src/components/keyboard-shortcut/keyboard-shortcut.mdx +++ b/src/stories/src/components/keyboard-shortcut/keyboard-shortcut.mdx @@ -127,6 +127,13 @@ target element. + + +Sets a function to call when the keyboard shortcut is activated. This provides an alternative to +binding to the `forge-keyboard-shortcut-activate` event in scenerios where that may be cumbersome. + + + --- @@ -161,4 +168,16 @@ pressed if it includes a submit button. > Even though the keyboard shortcut component exists in the DOM, the element itself doesn't affect > accessibility or layout due to having its `display` style property set to `none`. + + + + +## Types + +### KeyboardShortcutActivateCallback + +```ts +type KeyboardShortcutActivateCallback = (event: KeyboardEvent) => void; +``` + \ No newline at end of file diff --git a/src/stories/src/components/keyboard-shortcut/keyboard-shortcut.stories.tsx b/src/stories/src/components/keyboard-shortcut/keyboard-shortcut.stories.tsx index 906ab2038..0e269a5a4 100644 --- a/src/stories/src/components/keyboard-shortcut/keyboard-shortcut.stories.tsx +++ b/src/stories/src/components/keyboard-shortcut/keyboard-shortcut.stories.tsx @@ -38,7 +38,7 @@ export const Default: Story = ({ return (
-
Focus an element and press the key combination ({key})
+
Focus an element and press the key combination ({key})
diff --git a/src/stories/src/components/popup/popup.mdx b/src/stories/src/components/popup/popup.mdx index 6c3c09d4a..c203c22ae 100644 --- a/src/stories/src/components/popup/popup.mdx +++ b/src/stories/src/components/popup/popup.mdx @@ -185,14 +185,15 @@ Manually tells the popup to execute its positioning logic. | Name | Description | :-----------------------| :----------------- | `forge-popup-position` | Emits when the popup is re-positioned. +| `forge-popup-close` | Emits from both the host and target elements when the popup is closed. The following events are emitted from the targetElement, **NOT** the `` element. | Name | Description | :-----------------------| :----------------- | `forge-popup-open` | Emits when the popup is opened. -| `forge-popup-close` | Emits when the popup is closed. -| `forge-popup-blur` | Emits when the popup is closed. +| `forge-popup-close` | Emits from both the host and target elements when the popup is closed. +| `forge-popup-blur` | Emits when the popup is blurred. diff --git a/src/stories/src/components/popup/popup.stories.tsx b/src/stories/src/components/popup/popup.stories.tsx index 534b7f317..06b482074 100644 --- a/src/stories/src/components/popup/popup.stories.tsx +++ b/src/stories/src/components/popup/popup.stories.tsx @@ -39,7 +39,7 @@ export const Default: Story = ({ open={isOpen} onDismiss={() => setIsOpen(false)} options={{ placement, manageFocus, animationType, offset }}> -
+
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Voluptatum, est iste. Tempore iure iste molestias expedita, laboriosam magni a nostrum, ullam molestiae, obcaecati dicta ipsam provident aut praesentium eius dolore! diff --git a/src/stories/src/components/select/select.mdx b/src/stories/src/components/select/select.mdx index 44682b471..4fe082d3e 100644 --- a/src/stories/src/components/select/select.mdx +++ b/src/stories/src/components/select/select.mdx @@ -158,7 +158,7 @@ Gets/sets the invalid state. Setting this to `true` will style the component usi -Gets/sets whether this component is marked as required or not. Setting to `true` will display an asterisk in the label. +Gets/sets whether this component is marked as required or not. Setting to `true` will display an asterisk next to the label. diff --git a/src/stories/src/components/text-field/text-field.mdx b/src/stories/src/components/text-field/text-field.mdx index c9318880e..9dffc83f0 100644 --- a/src/stories/src/components/text-field/text-field.mdx +++ b/src/stories/src/components/text-field/text-field.mdx @@ -72,7 +72,7 @@ The `default` variant is intended to be used in desktop applications. - Controls the presence of an asterisk after the label to signify that the field is required. + Controls the presence of an asterisk next to the label to signify that the field is required. > If you are using this for form validation, be sure to also update the property on the embedded `` element. diff --git a/src/stories/src/components/view-switcher/view-switcher.stories.tsx b/src/stories/src/components/view-switcher/view-switcher.stories.tsx index c8b92afa2..0367fdfa3 100644 --- a/src/stories/src/components/view-switcher/view-switcher.stories.tsx +++ b/src/stories/src/components/view-switcher/view-switcher.stories.tsx @@ -42,13 +42,13 @@ export const Default: Story = ({ -
View one
+
View one
-
View two
+
View two
-
View three
+
View three
diff --git a/src/stories/src/guides/css-custom-properties.stories.mdx b/src/stories/src/guides/css-custom-properties.stories.mdx index 45e35d384..14ecc6575 100644 --- a/src/stories/src/guides/css-custom-properties.stories.mdx +++ b/src/stories/src/guides/css-custom-properties.stories.mdx @@ -79,7 +79,7 @@ for information about which typography styles are available. For example, to override the `font-size` of all "title" typography styles you could use this: ```scss -.forge-typography--title { +.forge-typography--subheading4 { --forge-typography-title-font-size: 72px; } ``` diff --git a/src/test/spec/autocomplete/autocomplete.spec.ts b/src/test/spec/autocomplete/autocomplete.spec.ts index fa42f3104..49fc667e3 100644 --- a/src/test/spec/autocomplete/autocomplete.spec.ts +++ b/src/test/spec/autocomplete/autocomplete.spec.ts @@ -1525,7 +1525,7 @@ describe('AutocompleteComponent', function(this: ITestContext) { expect(this.context.component.value).toBe(listItems[2].value); }); - it('should pass in to-be new value in beforeValueChange', async function(this: ITestContext) { + it('should pass in to-be new value in beforeValueChange', async function(this: ITestContext) { this.context = setupTestContext(true); let newValue = ''; this.context.component.filter = () => DEFAULT_FILTER_OPTIONS; @@ -1543,6 +1543,79 @@ describe('AutocompleteComponent', function(this: ITestContext) { expect(newValue).toBe(listItems[2].value); }); + + it('should force filter callback to execute and update selected value', async function(this: ITestContext) { + this.context = setupTestContext(true); + this.context.component.filter = () => DEFAULT_FILTER_OPTIONS; + + _triggerDropdownClick(this.context.input); + + await tick(); + const listItems = _getListItems(this.context.component.popupElement); + _clickListItem(2, this.context.component.popupElement); + await tick(); + + expect(this.context.component.value).toBe(listItems[2].value); + + this.context.component.filter = () => [ + { label: `${DEFAULT_FILTER_OPTIONS[2].label} UPDATED`, value: DEFAULT_FILTER_OPTIONS[2].value}, + { label: 'New', value: 'new' } + ]; + this.context.component.forceFilter(); + + await tick(); + + expect(this.context.input.value).toBe(`${DEFAULT_FILTER_OPTIONS[2].label} UPDATED`); + expect(this.context.component.value).toBe(DEFAULT_FILTER_OPTIONS[2].value); + }); + + it('should force filter callback to execute and remove selected value', async function(this: ITestContext) { + this.context = setupTestContext(true); + this.context.component.filter = () => DEFAULT_FILTER_OPTIONS; + + _triggerDropdownClick(this.context.input); + + await tick(); + const listItems = _getListItems(this.context.component.popupElement); + _clickListItem(2, this.context.component.popupElement); + await tick(); + + expect(this.context.component.value).toBe(listItems[2].value); + + this.context.component.filter = () => [ + { label: 'New', value: 'new' } + ]; + this.context.component.forceFilter(); + + await tick(); + + expect(this.context.input.value).toBe(''); + expect(this.context.component.value).toBe(DEFAULT_FILTER_OPTIONS[2].value); + }); + + it('should force filter callback to execute and preserve selected value if not present in new filtered options', async function(this: ITestContext) { + this.context = setupTestContext(true); + this.context.component.filter = () => DEFAULT_FILTER_OPTIONS; + + _triggerDropdownClick(this.context.input); + + await tick(); + const listItems = _getListItems(this.context.component.popupElement); + _clickListItem(2, this.context.component.popupElement); + await tick(); + + expect(this.context.component.value).toBe(listItems[2].value); + + this.context.component.filter = () => [ + { label: 'New', value: 'new' } + ]; + this.context.component.forceFilter({ preserveValue: true }); + + await tick(); + + expect(this.context.input.value).toBe(DEFAULT_FILTER_OPTIONS[2].label); + expect(this.context.component.value).toBe(DEFAULT_FILTER_OPTIONS[2].value); + }); }); }); @@ -1680,11 +1753,11 @@ describe('AutocompleteComponent', function(this: ITestContext) { return options.map(({ label, value }) => ({ label, value })); } - function _getListItems(popupElement: HTMLElement | null): IListItemComponent[] { + function _getListItems(popupElement: HTMLElement | null): IListItemComponent[] { if (!popupElement) { return []; } - return Array.from(popupElement.querySelectorAll(LIST_ITEM_CONSTANTS.elementName)) as IListItemComponent[]; + return Array.from(popupElement.querySelectorAll(LIST_ITEM_CONSTANTS.elementName)) as IListItemComponent[]; } function _triggerDropdownClick(input: HTMLInputElement): void { diff --git a/src/test/spec/button-area/button-area.spec.ts b/src/test/spec/button-area/button-area.spec.ts new file mode 100644 index 000000000..10e7a7248 --- /dev/null +++ b/src/test/spec/button-area/button-area.spec.ts @@ -0,0 +1,86 @@ +import { BUTTON_AREA_CONSTANTS, IButtonAreaComponent, defineButtonAreaComponent } from '@tylertech/forge'; +import { tick } from '@tylertech/forge-testing'; + +interface ITestContext { + context: IButtonAreaTestContext; +} + +interface IButtonAreaTestContext { + component: IButtonAreaComponent; + button: HTMLButtonElement; + destroy(): void; +} + +describe('ButtonAreaComponent', function(this: ITestContext) { + beforeAll(function(this: ITestContext) { + defineButtonAreaComponent(); + }); + + afterEach(function(this: ITestContext) { + this.context.destroy(); + }); + + it('should instantiate component instance', function(this: ITestContext) { + this.context = setupTextContext(); + + expect(this.context.component.shadowRoot).toBeTruthy(); + }); + + it('should handle click', function(this: ITestContext) { + this.context = setupTextContext(); + + const callback = jasmine.createSpy('callback'); + this.context.component.addEventListener('click', callback); + this.context.component.click(); + expect(callback).toHaveBeenCalled(); + }); + + it('should handle click from the button', function(this: ITestContext) { + this.context = setupTextContext(); + + const callback = jasmine.createSpy('callback'); + this.context.component.addEventListener('click', callback); + this.context.button.click(); + expect(callback).toHaveBeenCalled(); + }); + + it('should disable', function(this: ITestContext) { + this.context = setupTextContext(); + + this.context.component.disabled = true; + const callback = jasmine.createSpy('callback'); + this.context.component.addEventListener('click', callback); + this.context.button.click(); + expect(callback).not.toHaveBeenCalled(); + }); + + it('should not handle click from ignored children', function(this: ITestContext) { + this.context = setupTextContext(); + + const callback = jasmine.createSpy('callback'); + this.context.component.addEventListener('click', callback); + + const button = document.createElement('button'); + button.toggleAttribute('data-forge-ignore', true); + this.context.component.append(button); + button.click(); + expect(callback).not.toHaveBeenCalled(); + }); + + function setupTextContext(): IButtonAreaTestContext { + const component = document.createElement(BUTTON_AREA_CONSTANTS.elementName) as IButtonAreaComponent; + const button = document.createElement('button'); + button.setAttribute('type', 'button'); + button.setAttribute('slot', 'button'); + component.append(button); + document.body.appendChild(component); + + return { + component, + button, + destroy: () => { + document.body.removeChild(component); + } + }; + } +}); diff --git a/src/test/spec/chip-field/chip-field.spec.ts b/src/test/spec/chip-field/chip-field.spec.ts index 91bbb3312..6456ed26b 100644 --- a/src/test/spec/chip-field/chip-field.spec.ts +++ b/src/test/spec/chip-field/chip-field.spec.ts @@ -1121,6 +1121,37 @@ describe('ChipFieldComponent', function(this: ITestContext) { }); + describe('Click events', function (this: ITestContext) { + it('should emit a click event from the input if the container is clicked', function(this: ITestContext) { + this.context = setupTestContext(); + const clickSpy = jasmine.createSpy('inputClick'); + const inputEl = getNativeInput(this.context.component); + inputEl.addEventListener('click', clickSpy); + + const containerEl = getInputContainerElement(this.context.component); + containerEl.dispatchEvent(new MouseEvent('mousedown')); + + expect(clickSpy).toHaveBeenCalledTimes(1); + + inputEl.removeEventListener('click', clickSpy); + }); + + it('should not emit a click event from a disabled input if the container is clicked', function(this: ITestContext) { + this.context = setupTestContext(); + const clickSpy = jasmine.createSpy('inputClick'); + const inputEl = getNativeInput(this.context.component); + inputEl.addEventListener('click', clickSpy); + inputEl.disabled = true; + + const containerEl = getInputContainerElement(this.context.component); + containerEl.dispatchEvent(new MouseEvent('mousedown')); + + expect(clickSpy).not.toHaveBeenCalled(); + + inputEl.removeEventListener('click', clickSpy); + }); + }); + describe('Member navigation', function(this: ITestContext) { it('should focus the last element when the left arrow key is pressed while the input is focused', function(this: ITestContext) { this.context = setupTestContext(); @@ -1218,6 +1249,10 @@ describe('ChipFieldComponent', function(this: ITestContext) { return component.querySelector(CHIP_FIELD_CONSTANTS.selectors.HELPER_TEXT) as HTMLElement; } + function getInputContainerElement(component: IChipFieldComponent) { + return getShadowElement(component, CHIP_FIELD_CONSTANTS.selectors.INPUT_CONTAINER) as HTMLDivElement; + } + function dispatchKeydownEvent(ele: HTMLElement, key: string) { const keyboardEvent = new KeyboardEvent('keydown', { bubbles: true, diff --git a/src/test/spec/date-picker/date-picker.spec.ts b/src/test/spec/date-picker/date-picker.spec.ts index 4f5089bfc..b27a01cba 100644 --- a/src/test/spec/date-picker/date-picker.spec.ts +++ b/src/test/spec/date-picker/date-picker.spec.ts @@ -77,6 +77,30 @@ describe('DatePickerComponent', function(this: ITestContext) { expect((calendar.value).toDateString()).toEqual(date.toDateString()); }); + it('should open calendar in month of min date if min is after current month', function(this: ITestContext) { + this.context = setupTestContext(false); + const date = new Date(); + this.context.component.min = new Date(date.getFullYear(), date.getMonth() + 1, 1); + this.context.append(); + + openPopup(this.context.component); + const calendar = getCalendar(this.context.component); + + expect(calendar.month).toEqual(date.getMonth() + 1); + }); + + it('should open calendar in month of max date if max is before current month', function(this: ITestContext) { + this.context = setupTestContext(false); + const date = new Date(); + this.context.component.max = new Date(date.getFullYear(), date.getMonth() - 1, 1); + this.context.append(); + + openPopup(this.context.component); + const calendar = getCalendar(this.context.component); + + expect(calendar.month).toEqual(date.getMonth() - 1); + }); + it('should automatically render a toggle button with a Forge text-field component', function(this: ITestContext) { this.context = setupTestContext(false, true, false); @@ -838,6 +862,19 @@ describe('DatePickerComponent', function(this: ITestContext) { expect(getInputElement(this.context.component).value).toBe('__/__/____'); }); + it('should select mask when shown on focus', function(this: ITestContext) { + this.context = setupTestContext(true); + const inputElement = getInputElement(this.context.component); + this.context.component.setAttribute(BASE_DATE_PICKER_CONSTANTS.observedAttributes.MASKED, ''); + this.context.component.setAttribute(BASE_DATE_PICKER_CONSTANTS.observedAttributes.SHOW_MASK_FORMAT, ''); + + expect(this.context.component.showMaskFormat).toBe(true); + inputElement.focus(); + + expect(inputElement.selectionStart).toEqual(0); + expect(inputElement.selectionEnd).toEqual('__/__/____'.length); + }); + it('should clear mask format on blur', function(this: ITestContext) { this.context = setupTestContext(true); const inputElement = getInputElement(this.context.component); diff --git a/src/test/spec/date-range-picker/date-range-picker.spec.ts b/src/test/spec/date-range-picker/date-range-picker.spec.ts index b4279f384..a289ec240 100644 --- a/src/test/spec/date-range-picker/date-range-picker.spec.ts +++ b/src/test/spec/date-range-picker/date-range-picker.spec.ts @@ -919,6 +919,32 @@ describe('DateRangePickerComponent', function(this: ITestContext) { expect(inputElement.value).toBe('01/01/2020'); }); + it('should select mask in "from" input when shown on focus', function(this: ITestContext) { + this.context = setupTestContext(true); + const fromElement = getFromElement(this.context.component); + this.context.component.setAttribute(BASE_DATE_PICKER_CONSTANTS.observedAttributes.MASKED, ''); + this.context.component.setAttribute(BASE_DATE_PICKER_CONSTANTS.observedAttributes.SHOW_MASK_FORMAT, ''); + + expect(this.context.component.showMaskFormat).toBe(true); + fromElement.focus(); + + expect(fromElement.selectionStart).toEqual(0); + expect(fromElement.selectionEnd).toEqual('__/__/____'.length); + }); + + it('should select mask in "to" input when shown on focus', function(this: ITestContext) { + this.context = setupTestContext(true); + const toElement = getToElement(this.context.component); + this.context.component.setAttribute(BASE_DATE_PICKER_CONSTANTS.observedAttributes.MASKED, ''); + this.context.component.setAttribute(BASE_DATE_PICKER_CONSTANTS.observedAttributes.SHOW_MASK_FORMAT, ''); + + expect(this.context.component.showMaskFormat).toBe(true); + toElement.focus(); + + expect(toElement.selectionStart).toEqual(0); + expect(toElement.selectionEnd).toEqual('__/__/____'.length); + }); + it('should only show mask format in "from" input on focus', function(this: ITestContext) { this.context = setupTestContext(true); const fromElement = getFromElement(this.context.component); diff --git a/src/test/spec/expansion-panel/expansion-panel.spec.ts b/src/test/spec/expansion-panel/expansion-panel.spec.ts index 6dc969970..fef0e7e78 100644 --- a/src/test/spec/expansion-panel/expansion-panel.spec.ts +++ b/src/test/spec/expansion-panel/expansion-panel.spec.ts @@ -373,6 +373,22 @@ describe('ExpansionPanelComponent', function(this: ITestContext) { expect(getInternalPanelContent(this.context.component).clientHeight).toBe(0); }); + it('should close immediately if set while active animation is running', async function(this: ITestContext) { + this.context = setupTestContext(); + this.context.append(); + + await tick(); + + this.context.component.open = true; + this.context.component.setOpenImmediate(true); + + await tick(); + await tick(); + + expect(this.context.component.open).toBeTrue(); + expect(getInternalPanelContent(this.context.component).style.height).toBe(''); + }); + it('should close immediately horizontal', async function(this: ITestContext) { this.context = setupTestContext(); this.context.component.useAnimations = false; diff --git a/src/test/spec/icon/icon.spec.ts b/src/test/spec/icon/icon.spec.ts index 9b1c86b2c..1955863a1 100644 --- a/src/test/spec/icon/icon.spec.ts +++ b/src/test/spec/icon/icon.spec.ts @@ -273,6 +273,18 @@ describe('IconComponent', function(this: ITestContext) { expect(loadSpy).not.toHaveBeenCalled(); }); + it('should hide icon from assistive technology', async function(this: ITestContext) { + this.context = setupTestContext(); + IconRegistry.define(DEFAULT_ICON); + + this.context.component.name = 'action_launcher'; + await timer(); + + const svg = this.context.component.shadowRoot!.querySelector('svg') + expect(svg?.getAttribute('aria-hidden')).toBe('true'); + IconRegistry.remove(DEFAULT_ICON); + }); + function setupTestContext(config?: Partial): ITestIconContext { const fixture = document.createElement('div'); fixture.id = 'icon-test-fixture'; diff --git a/src/test/spec/keyboard-shortcut/keyboard-shortcut.spec.ts b/src/test/spec/keyboard-shortcut/keyboard-shortcut.spec.ts index aa8a3ecc2..5f5a96833 100644 --- a/src/test/spec/keyboard-shortcut/keyboard-shortcut.spec.ts +++ b/src/test/spec/keyboard-shortcut/keyboard-shortcut.spec.ts @@ -1,6 +1,6 @@ import { removeElement } from '@tylertech/forge-core'; import { tick } from '@tylertech/forge-testing'; -import { IKeyboardShortcutComponent, KEYBOARD_SHORTCUT_CONSTANTS, defineKeyboardShortcutComponent } from '@tylertech/forge/keyboard-shortcut'; +import { IKeyboardShortcutComponent, KEYBOARD_SHORTCUT_CONSTANTS, KeyboardShortcutActivateCallback, defineKeyboardShortcutComponent } from '@tylertech/forge/keyboard-shortcut'; interface ITestContext { context: IKeyboardShortcutTestContext @@ -40,6 +40,19 @@ describe('KeyboardShortcutComponent', function(this: ITestContext) { expect(spy).toHaveBeenCalled(); }); + it('should invoke the callback function on a matching target keydown event', function(this: ITestContext) { + const key = 'a'; + const callback = jasmine.createSpy(); + this.context = setupTestContext(); + this.context.component.key = key; + this.context.component.activateCallback = callback; + + this.context.attach(); + + this.context.dispatchKeydownEvent({key}); + expect(callback).toHaveBeenCalled(); + }); + describe('attributes', function(this: ITestContext) { it('should set key when a key attribute is provided', function(this: ITestContext) { @@ -526,6 +539,20 @@ describe('KeyboardShortcutComponent', function(this: ITestContext) { expect(spy).not.toHaveBeenCalled(); }); + it('should not invoke the callback when disabled is set to true', function(this: ITestContext) { + const key = 'a'; + const callback = jasmine.createSpy(); + this.context = setupTestContext(); + this.context.component.key = key; + this.context.component.activateCallback = callback; + this.context.component.disabled = true; + + this.context.attach(); + + this.context.dispatchKeydownEvent({key}); + expect(callback).not.toHaveBeenCalled(); + }); + it('should connect target element when disabled is set to false', function(this: ITestContext) { const key = 'a'; this.context = setupTestContext(); diff --git a/src/test/spec/list-dropdown/list-dropdown.spec.ts b/src/test/spec/list-dropdown/list-dropdown.spec.ts index a42ddca3d..4d0b1a2de 100644 --- a/src/test/spec/list-dropdown/list-dropdown.spec.ts +++ b/src/test/spec/list-dropdown/list-dropdown.spec.ts @@ -4,7 +4,7 @@ import { IListDropdownTestContext, createListDropdown, getListItems, getListDrop import { IListDropdownConfig, IListDropdownOption, ListDropdownAsyncStyle, ListDropdownHeaderBuilder, IListDropdownOptionGroup, LIST_DROPDOWN_CONSTANTS, ListDropdownFooterBuilder, ListDropdownType, ListDropdownOptionBuilder, ListDropdownTransformCallback } from '@tylertech/forge/list-dropdown'; import { defineOptionComponent, defineOptionGroupComponent } from '@tylertech/forge/select'; import { definePopupComponent, POPUP_CONSTANTS, IPopupComponent } from '@tylertech/forge/popup'; -import { defineListComponent, IListItemComponent } from '@tylertech/forge/list'; +import { defineListComponent, IListComponent, IListItemComponent, LIST_CONSTANTS } from '@tylertech/forge/list'; import { defineLinearProgressComponent, SKELETON_CONSTANTS, DIVIDER_CONSTANTS, IIconComponent, CIRCULAR_PROGRESS_CONSTANTS } from '@tylertech/forge'; import { tryCleanupPopups, isVisibleInScrollContainer } from '../../utils'; @@ -295,7 +295,7 @@ describe('ListDropdown', function(this: ITestContext) { expect(listItems[2].selected).toBeTrue(); }); - it('should toggle selection and active state in multiple mode of deselected option', async function(this: ITestContext) { + it('should toggle selection in multiple mode of deselected option', async function(this: ITestContext) { this.context = createListDropdown({ ...DEFAULT_CONFIG, multiple: true, selectedValues: [BASIC_OPTIONS[1].value] }); this.context.listDropdown.open(); await delayPopupAnimation(); @@ -310,7 +310,7 @@ describe('ListDropdown', function(this: ITestContext) { expect(listItems[1].selected).toBeTrue(); expect(listItems[1].active).toBeFalse(); expect(listItems[2].selected).toBeTrue(); - expect(listItems[2].active).toBeTrue(); + expect(listItems[2].active).toBeFalse(); }); it('should activate selected option', async function(this: ITestContext) { @@ -733,7 +733,8 @@ describe('ListDropdown', function(this: ITestContext) { this.context.listDropdown.open(); await delayPopupAnimation(); - expect(this.context.listDropdown.dropdownElement!.getAttribute('role')).toBe('menu'); + const listElement = this.context.listDropdown.dropdownElement!.querySelector(LIST_CONSTANTS.elementName) as IListComponent; + expect(listElement.getAttribute('role')).toBe('menu'); }); it('should set popup classes', async function(this: ITestContext) { @@ -953,4 +954,17 @@ describe('ListDropdown', function(this: ITestContext) { expect(attrValue).toBeTruthy(); expect(attrValue).toBe('test-value'); }); + + it('should display options with secondary label', async function(this: ITestContext) { + const opts: IListDropdownOption[] = [ + { label: 'Label', secondaryLabel: 'Secondary label', value: 'value' }, + ]; + this.context = createListDropdown({ ...DEFAULT_CONFIG, options: opts }); + this.context.listDropdown.open(); + await timer(POPUP_CONSTANTS.numbers.ANIMATION_DURATION); + await tick(); + + const listItems = getListItems(); + expect(listItems[0].querySelector('span[slot=subtitle]')?.textContent).toBe('Secondary label'); + }); }); diff --git a/src/test/spec/list/list-item/list-item.spec.ts b/src/test/spec/list/list-item/list-item.spec.ts deleted file mode 100644 index fb02935ea..000000000 --- a/src/test/spec/list/list-item/list-item.spec.ts +++ /dev/null @@ -1,675 +0,0 @@ -import { getShadowElement, removeElement, getActiveElement } from '@tylertech/forge-core'; -import { tick, timer } from '@tylertech/forge-testing'; -import { CHECKBOX_CONSTANTS, ICheckboxComponent } from '@tylertech/forge/checkbox'; -import { DRAWER_CONSTANTS, IDrawerComponent } from '@tylertech/forge/drawer'; -import { defineListComponent, IListComponent, LIST_CONSTANTS } from '@tylertech/forge/list'; -import { defineListItemComponent, IListItemComponent, LIST_ITEM_CONSTANTS } from '@tylertech/forge/list/list-item'; -import { IRadioComponent, RADIO_CONSTANTS } from '@tylertech/forge/radio'; - -interface ITestContext { - context: ITestListItemDefaultContext | ITestListItemDrawerContext | ITestListItemCheckboxContext | ITestListItemRadioContext; -} - -interface ITestListItemDefaultContext { - component: IListItemComponent; - listComponent: IListComponent; - getRootElement(): HTMLElement; - append(): void; - destroy(): void; - /** Simulates a user interaction with the button to instantiate the ripple */ - simulateInteraction(): Promise; -} - -interface ITestListItemDrawerContext { - component: IListItemComponent; - destroy(): void; -} - -interface ITestListItemCheckboxContext { - component: IListItemComponent; - listComponent: IListComponent; - getRootElement(): HTMLElement; - getInputElement(): HTMLInputElement; - append(): void; - destroy(): void; -} - -interface ITestListItemRadioContext { - component1: IListItemComponent; - component2: IListItemComponent; - listComponent: IListComponent; - getRootElement1(): HTMLElement; - getRootElement2(): HTMLElement; - getRadioInput1(): HTMLInputElement; - getRadioInput2(): HTMLInputElement; - destroy(): void; -} - -describe('ListItemComponent', function(this: ITestContext) { - beforeAll(function(this: ITestContext) { - defineListItemComponent(); - defineListComponent(); - }); - - afterEach(function(this: ITestContext) { - this.context.destroy(); - }); - - describe('individual list item', function(this: ITestContext) { - it('should have proper role', function(this: ITestContext) { - this.context = setupTestContext(true); - expect(this.context.component.getAttribute('role')).toBe(LIST_ITEM_CONSTANTS.roles.LIST_ITEM); - }); - - it('should not have drawer context attribute', function(this: ITestContext) { - this.context = setupTestContext(true); - expect(this.context.component.hasAttribute(LIST_ITEM_CONSTANTS.attributes.DRAWER_CONTEXT)).toBe(false); - }); - - it('should not be two-line by default', function(this: ITestContext) { - this.context = setupTestContext(true); - expect(this.context.component.twoLine).toBe(false); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.TWO_LINE)).toBe(false); - }); - - it('should not be three-line by default', function(this: ITestContext) { - this.context = setupTestContext(true); - expect(this.context.component.threeLine).toBe(false); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.THREE_LINE)).toBe(false); - }); - - it('should not be static by default', function(this: ITestContext) { - this.context = setupTestContext(true); - expect(this.context.component.static).toBe(false); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.STATIC)).toBe(false); - }); - - it('should not be active by default', function(this: ITestContext) { - this.context = setupTestContext(true); - expect(this.context.component.active).toBe(false); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.ACTIVE)).toBe(false); - }); - - it('should use ripple by default, but delay initialization until user interaction', async function(this: ITestContext) { - this.context = setupTestContext(true); - const context = this.context as ITestListItemDefaultContext ; - expect(context.component.ripple).withContext('ripple property').toBe(true); - expect(context.getRootElement().classList.contains('mdc-ripple-upgraded')).withContext('ripple before interaction').toBe(false); - await context.simulateInteraction(); - expect(context.getRootElement().classList.contains('mdc-ripple-upgraded')).withContext('ripple after interaction').toBe(true); - }); - - it('should not be selected by default', function(this: ITestContext) { - this.context = setupTestContext(true); - - expect(this.context.component.selected).toBe(false); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.SELECTED)).toBe(false); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.ACTIVATED)).toBe(false); - }); - - it('should not have value by default', function(this: ITestContext) { - this.context = setupTestContext(true); - expect(this.context.component.value).toBeUndefined(); - }); - - it('should not have href by default', function(this: ITestContext) { - this.context = setupTestContext(true); - expect(this.context.component.href).toBeUndefined(); - }); - - it('should not have target by default', function(this: ITestContext) { - this.context = setupTestContext(true); - expect(this.context.component.target).toBeUndefined(); - }); - - it('should not be disabled by default', function(this: ITestContext) { - this.context = setupTestContext(true); - expect(this.context.component.disabled).toBe(false); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.DISABLED)).toBe(false); - }); - - it('should not wrap by default', function(this: ITestContext) { - this.context = setupTestContext(true); - expect(this.context.component.wrap).toBeFalse(); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.WRAP)).toBeFalse(); - }); - - it('should not be dense by default', function(this: ITestContext) { - this.context = setupTestContext(true); - expect(this.context.component.dense).toBe(false); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.DENSE)).toBe(false); - }); - - it('should propagate click by default', function(this: ITestContext) { - this.context = setupTestContext(true); - expect(this.context.component.propagateClick).toBe(true); - }); - - it('should not be indented by default', function(this: ITestContext) { - this.context = setupTestContext(true); - expect(this.context.component.indented).toBe(false); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.INDENTED)).toBe(false); - }); - - it('should remove ripple', async function(this: ITestContext) { - this.context = setupTestContext(true); - await tick(); - this.context.component.ripple = false; - await tick(); - expect(this.context.component.ripple).toBe(false); - expect(this.context.component.hasAttribute(LIST_ITEM_CONSTANTS.attributes.RIPPLE)).toBe(true); - expect(this.context.component.getAttribute(LIST_ITEM_CONSTANTS.attributes.RIPPLE)).toBe('false'); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains('mdc-ripple-upgraded')).toBe(false); - }); - - it('should not set ripple if static', async function(this: ITestContext) { - this.context = setupTestContext(); - this.context.component.ripple = false; - (this.context as ITestListItemDefaultContext).append(); - await tick(); - this.context.component.static = true; - this.context.component.ripple = true; - await tick(); - expect(this.context.component.ripple).toBe(false); - expect(this.context.component.hasAttribute(LIST_ITEM_CONSTANTS.attributes.RIPPLE)).toBe(true); - expect(this.context.component.getAttribute(LIST_ITEM_CONSTANTS.attributes.RIPPLE)).toBe('false'); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains('mdc-ripple-upgraded')).toBe(false); - }); - - it('should set to static via property', function(this: ITestContext) { - this.context = setupTestContext(true); - this.context.component.static = true; - expect(this.context.component.static).toBe(true); - expect(this.context.component.hasAttribute(LIST_ITEM_CONSTANTS.attributes.STATIC)).toBe(true); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.STATIC)).toBe(true); - }); - - it('should set to static via attribute', function(this: ITestContext) { - this.context = setupTestContext(true); - this.context.component.setAttribute(LIST_ITEM_CONSTANTS.attributes.STATIC, 'true'); - expect(this.context.component.static).toBe(true); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.STATIC)).toBe(true); - }); - - it('should set to static via attribute by default', function(this: ITestContext) { - this.context = setupTestContext(); - this.context.component.setAttribute(LIST_ITEM_CONSTANTS.attributes.STATIC, 'true'); - (this.context as ITestListItemDefaultContext).append(); - expect(this.context.component.static).toBe(true); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.STATIC)).toBe(true); - }); - - it('should not emit selected event when static', function(this: ITestContext) { - this.context = setupTestContext(); - this.context.component.setAttribute(LIST_ITEM_CONSTANTS.attributes.STATIC, 'true'); - (this.context as ITestListItemDefaultContext).append(); - const listener = jasmine.createSpy('selected listener'); - this.context.component.addEventListener(LIST_ITEM_CONSTANTS.events.SELECT, listener); - (this.context as ITestListItemDefaultContext).getRootElement().click(); - expect(listener).not.toHaveBeenCalled(); - }); - - it('should emit selected event when not static', function(this: ITestContext) { - this.context = setupTestContext(true); - const listener = jasmine.createSpy('selected listener'); - this.context.component.addEventListener(LIST_ITEM_CONSTANTS.events.SELECT, listener); - (this.context as ITestListItemDefaultContext).getRootElement().click(); - expect(listener).toHaveBeenCalledTimes(1); - }); - - it('should emit selected event when enter key is pressed', function(this: ITestContext) { - this.context = setupTestContext(true); - const listener = jasmine.createSpy('selected listener'); - this.context.component.addEventListener(LIST_ITEM_CONSTANTS.events.SELECT, listener); - (this.context as ITestListItemDefaultContext).getRootElement().dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' })); - expect(listener).toHaveBeenCalledTimes(1); - }); - - it('should emit selected event when enter space is pressed', function(this: ITestContext) { - this.context = setupTestContext(true); - const listener = jasmine.createSpy('selected listener'); - this.context.component.addEventListener(LIST_ITEM_CONSTANTS.events.SELECT, listener); - (this.context as ITestListItemDefaultContext).getRootElement().dispatchEvent(new KeyboardEvent('keydown', { key: ' ' })); - expect(listener).toHaveBeenCalledTimes(1); - }); - - it('should set value via property', function(this: ITestContext) { - this.context = setupTestContext(true); - this.context.component.value = 1; - expect(this.context.component.value).toBe(1); - }); - - it('should set correct role based on href value', function(this: ITestContext) { - this.context = setupTestContext(); - const url = 'https://www.google.com/'; - (this.context as ITestListItemDefaultContext).append(); - this.context.component.href = url; - expect((this.context as ITestListItemDefaultContext).getRootElement().getAttribute('role')).toBe(LIST_ITEM_CONSTANTS.roles.LINK); - }); - - it('should toggle role based on href', async function(this: ITestContext) { - this.context = setupTestContext(); - const url = 'https://www.google.com/'; - this.context.component.setAttribute(LIST_ITEM_CONSTANTS.attributes.HREF, url); - (this.context as ITestListItemDefaultContext).append(); - expect((this.context as ITestListItemDefaultContext).getRootElement().getAttribute('role')).toBe(LIST_ITEM_CONSTANTS.roles.LINK); - this.context.component.href = ''; - await tick(); - expect((this.context as ITestListItemDefaultContext).getRootElement().getAttribute('role')).toBe(LIST_ITEM_CONSTANTS.roles.LIST_ITEM); - }); - - it('should set href via property', function(this: ITestContext) { - this.context = setupTestContext(); - const url = 'https://www.google.com/'; - (this.context as ITestListItemDefaultContext).append(); - this.context.component.href = url; - expect(this.context.component.href).toBe(url); - expect(this.context.component.getAttribute(LIST_ITEM_CONSTANTS.attributes.HREF)).toBe(url); - }); - - it('should not emit selected event when href is set', function(this: ITestContext) { - this.context = setupTestContext(); - this.context.component.setAttribute(LIST_ITEM_CONSTANTS.attributes.HREF, '#SomeHref'); - (this.context as ITestListItemDefaultContext).append(); - const listener = jasmine.createSpy('selected listener'); - (this.context as ITestListItemDefaultContext).listComponent.addEventListener(LIST_ITEM_CONSTANTS.events.SELECT, listener); - (this.context as ITestListItemDefaultContext).getRootElement().click(); - expect(listener).not.toHaveBeenCalled(); - }); - - it('should set target via property', function(this: ITestContext) { - this.context = setupTestContext(true); - this.context.component.target = '_blank'; - expect(this.context.component.target).toBe('_blank'); - }); - - it('should set target via attribute', function(this: ITestContext) { - this.context = setupTestContext(true); - this.context.component.setAttribute(LIST_ITEM_CONSTANTS.attributes.TARGET, '_blank'); - expect(this.context.component.target).toBe('_blank'); - }); - - it('should set disabled via property', function(this: ITestContext) { - this.context = setupTestContext(true); - this.context.component.disabled = true; - expect(this.context.component.disabled).toBe(true); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.DISABLED)).toBe(true); - this.context.component.disabled = false; - expect(this.context.component.disabled).toBe(false); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.DISABLED)).toBe(false); - }); - - it('should set disabled via attribute', function(this: ITestContext) { - this.context = setupTestContext(true); - this.context.component.setAttribute(LIST_ITEM_CONSTANTS.attributes.DISABLED, 'true'); - expect(this.context.component.disabled).toBe(true); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.DISABLED)).toBe(true); - }); - - it('should set wrap via property', function(this: ITestContext) { - this.context = setupTestContext(true); - this.context.component.wrap = true; - const rootElement = (this.context as ITestListItemDefaultContext).getRootElement(); - - expect(this.context.component.wrap).toBeTrue(); - expect(rootElement.classList.contains(LIST_ITEM_CONSTANTS.classes.WRAP)).toBeTrue(); - - this.context.component.wrap = false; - - expect(this.context.component.wrap).toBeFalse(); - expect(rootElement.classList.contains(LIST_ITEM_CONSTANTS.classes.WRAP)).toBeFalse(); - }); - - it('should set wrap via attribute', function(this: ITestContext) { - this.context = setupTestContext(true); - this.context.component.setAttribute(LIST_ITEM_CONSTANTS.attributes.WRAP, ''); - expect(this.context.component.wrap).toBeTrue(); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.WRAP)).toBeTrue(); - }); - - it('should set target via attribute', function(this: ITestContext) { - this.context = setupTestContext(true); - this.context.component.setAttribute(LIST_ITEM_CONSTANTS.attributes.TARGET, '_blank'); - expect(this.context.component.target).toBe('_blank'); - }); - - it('should focus list item', function(this: ITestContext) { - this.context = setupTestContext(true); - this.context.component.focus(); - expect(getActiveElement()).toBe((this.context as ITestListItemDefaultContext).getRootElement()); - }); - - it('should not set focus when list item is selected', async function(this: ITestContext) { - this.context = setupTestContext(true); - this.context.component.selected = true; - await tick(); - expect(getActiveElement()).not.toBe((this.context as ITestListItemDefaultContext).getRootElement()); - }); - - it('should render selected when set by default via attribute', function(this: ITestContext) { - this.context = setupTestContext(); - this.context.component.setAttribute(LIST_ITEM_CONSTANTS.attributes.SELECTED, ''); - (this.context as ITestListItemDefaultContext).append(); - - expect(this.context.component.selected).toBe(true); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.SELECTED)).toBe(true); - }); - - it('should set two-line via property', function(this: ITestContext) { - this.context = setupTestContext(true); - this.context.component.twoLine = true; - expect(this.context.component.twoLine).toBe(true); - expect(this.context.component.hasAttribute(LIST_ITEM_CONSTANTS.attributes.TWO_LINE)).toBe(true); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.TWO_LINE)).toBe(true); - this.context.component.twoLine = false; - expect(this.context.component.twoLine).toBe(false); - expect(this.context.component.hasAttribute(LIST_ITEM_CONSTANTS.attributes.TWO_LINE)).toBe(false); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.TWO_LINE)).toBe(false); - }); - - it('should set two-line via attribute', function(this: ITestContext) { - this.context = setupTestContext(true); - this.context.component.setAttribute(LIST_ITEM_CONSTANTS.attributes.TWO_LINE, 'true'); - expect(this.context.component.twoLine).toBe(true); - expect(this.context.component.hasAttribute(LIST_ITEM_CONSTANTS.attributes.TWO_LINE)).toBe(true); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.TWO_LINE)).toBe(true); - }); - - it('should set two-line via attribute by default', function(this: ITestContext) { - this.context = setupTestContext(); - this.context.component.setAttribute(LIST_ITEM_CONSTANTS.attributes.TWO_LINE, 'true'); - (this.context as ITestListItemDefaultContext).append(); - expect(this.context.component.twoLine).toBe(true); - expect(this.context.component.hasAttribute(LIST_ITEM_CONSTANTS.attributes.TWO_LINE)).toBe(true); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.TWO_LINE)).toBe(true); - }); - - it('should set three-line via property', function(this: ITestContext) { - this.context = setupTestContext(true); - this.context.component.threeLine = true; - expect(this.context.component.threeLine).toBe(true); - expect(this.context.component.hasAttribute(LIST_ITEM_CONSTANTS.attributes.THREE_LINE)).toBe(true); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.THREE_LINE)).toBe(true); - this.context.component.threeLine = false; - expect(this.context.component.threeLine).toBe(false); - expect(this.context.component.hasAttribute(LIST_ITEM_CONSTANTS.attributes.THREE_LINE)).toBe(false); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.THREE_LINE)).toBe(false); - }); - - it('should set three-line via attribute', function(this: ITestContext) { - this.context = setupTestContext(true); - this.context.component.setAttribute(LIST_ITEM_CONSTANTS.attributes.THREE_LINE, 'true'); - expect(this.context.component.threeLine).toBe(true); - expect(this.context.component.hasAttribute(LIST_ITEM_CONSTANTS.attributes.THREE_LINE)).toBe(true); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.THREE_LINE)).toBe(true); - }); - - it('should set three-line via attribute by default', function(this: ITestContext) { - this.context = setupTestContext(); - this.context.component.setAttribute(LIST_ITEM_CONSTANTS.attributes.THREE_LINE, 'true'); - (this.context as ITestListItemDefaultContext).append(); - expect(this.context.component.threeLine).toBe(true); - expect(this.context.component.hasAttribute(LIST_ITEM_CONSTANTS.attributes.THREE_LINE)).toBe(true); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.THREE_LINE)).toBe(true); - }); - - it('should set active via property', function(this: ITestContext) { - this.context = setupTestContext(true); - this.context.component.active = true; - expect(this.context.component.active).toBe(true); - expect(this.context.component.hasAttribute(LIST_ITEM_CONSTANTS.attributes.ACTIVE)).toBe(true); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.ACTIVE)).toBe(true); - }); - - it('should set active via attribute', function(this: ITestContext) { - this.context = setupTestContext(true); - this.context.component.setAttribute(LIST_ITEM_CONSTANTS.attributes.ACTIVE, 'true'); - expect(this.context.component.active).toBe(true); - expect(this.context.component.hasAttribute(LIST_ITEM_CONSTANTS.attributes.ACTIVE)).toBe(true); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.ACTIVE)).toBe(true); - }); - - it('should set active via attribute by default', function(this: ITestContext) { - this.context = setupTestContext(); - this.context.component.setAttribute(LIST_ITEM_CONSTANTS.attributes.ACTIVE, 'true'); - (this.context as ITestListItemDefaultContext).append(); - expect(this.context.component.active).toBe(true); - expect(this.context.component.hasAttribute(LIST_ITEM_CONSTANTS.attributes.ACTIVE)).toBe(true); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.ACTIVE)).toBe(true); - }); - - it('should set active via property by default', function(this: ITestContext) { - this.context = setupTestContext(); - this.context.component.setAttribute(LIST_ITEM_CONSTANTS.attributes.ACTIVE, 'true'); - (this.context as ITestListItemDefaultContext).append(); - expect(this.context.component.active).toBe(true); - expect(this.context.component.hasAttribute(LIST_ITEM_CONSTANTS.attributes.ACTIVE)).toBe(true); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.ACTIVE)).toBe(true); - }); - - it('should toggle active', async function(this: ITestContext) { - this.context = setupTestContext(); - this.context.component.active = true; - (this.context as ITestListItemDefaultContext).append(); - await tick(); - this.context.component.active = false; - expect(this.context.component.active).toBe(false); - expect(this.context.component.hasAttribute(LIST_ITEM_CONSTANTS.attributes.ACTIVE)).toBe(false); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.ACTIVE)).toBe(false); - }); - - it('should toggle selected', async function(this: ITestContext) { - this.context = setupTestContext(); - this.context.component.selected = true; - (this.context as ITestListItemDefaultContext).append(); - await tick(); - this.context.component.selected = false; - expect(this.context.component.selected).toBe(false); - expect(this.context.component.hasAttribute(LIST_ITEM_CONSTANTS.attributes.SELECTED)).toBe(false); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.SELECTED)).toBe(false); - expect((this.context as ITestListItemDefaultContext).getRootElement().classList.contains(LIST_ITEM_CONSTANTS.classes.ACTIVATED)).toBe(false); - }); - }); - - describe('child of drawer component', function(this: ITestContext) { - - it('should have drawer context attribute when a child of a drawer component', function(this: ITestContext) { - this.context = setupDrawerTestContext(); - expect(this.context.component.hasAttribute(LIST_ITEM_CONSTANTS.attributes.DRAWER_CONTEXT)).toBe(true); - expect(this.context.component.getAttribute(LIST_ITEM_CONSTANTS.attributes.DRAWER_CONTEXT)).toBe('true'); - }); - }); - - describe('with checkbox', function(this: ITestContext) { - it('should check checkbox when clicked', function(this: ITestContext) { - this.context = setupCheckboxTestContext(true); - (this.context as ITestListItemCheckboxContext).getRootElement().click(); - expect((this.context as ITestListItemCheckboxContext).getInputElement().checked).toBe(true); - }); - - it('should check checkbox when parent list has selectedValue', async function(this: ITestContext) { - this.context = setupCheckboxTestContext(); - - (this.context as ITestListItemCheckboxContext).listComponent.selectedValue = [1]; - this.context.component.value = 1; - - // append to the DOM to start the test - (this.context as ITestListItemCheckboxContext).append(); - - await tick(); - - expect((this.context as ITestListItemCheckboxContext).getInputElement().checked).toBe(true); - }); - - it('should not check checkbox with the forge-ignore attribute applied when list-item is clicked', function(this: ITestContext) { - this.context = setupCheckboxTestContext(true); - const checkboxInputElement = (this.context as ITestListItemCheckboxContext).getInputElement(); - checkboxInputElement.setAttribute(LIST_ITEM_CONSTANTS.attributes.IGNORE, ''); - (this.context as ITestListItemCheckboxContext).getRootElement().click(); - expect(checkboxInputElement.checked).toBe(false); - }); - - it('should emit change event toggling checked state', function(this: ITestContext) { - this.context = setupCheckboxTestContext(true); - const changeSpy = jasmine.createSpy('change spy'); - (this.context as ITestListItemCheckboxContext).getInputElement().addEventListener('change', changeSpy); - - (this.context as ITestListItemCheckboxContext).getRootElement().click(); - - expect(changeSpy).toHaveBeenCalledTimes(1); - }); - }); - - describe('with radio', function(this: ITestContext) { - it('should check radio when clicked', function(this: ITestContext) { - this.context = setupRadioTestContext(); - this.context.getRootElement1().click(); - expect(this.context.getRadioInput1().checked).toBe(true); - }); - - it('should not check radio with the "forge-ignore" attribute applied when list-item is clicked', function(this: ITestContext) { - this.context = setupRadioTestContext(); - const radioInput1 = this.context.getRadioInput1(); - radioInput1.setAttribute(LIST_ITEM_CONSTANTS.attributes.IGNORE, ''); - - this.context.getRootElement1().click(); - expect(radioInput1.checked).toBe(false); - }); - - it('should check radio button in another list item', function(this: ITestContext) { - this.context = setupRadioTestContext(); - this.context.getRootElement1().click(); - this.context.getRootElement2().click(); - expect(this.context.getRadioInput1().checked).toBe(false); - expect(this.context.getRadioInput2().checked).toBe(true); - }); - - it('should emit change event toggling checked state', function(this: ITestContext) { - this.context = setupRadioTestContext(); - const changeSpy = jasmine.createSpy('change spy'); - this.context.getRadioInput1().addEventListener('change', changeSpy); - - this.context.getRootElement1().click(); - - expect(changeSpy).toHaveBeenCalledTimes(1); - }); - }); - - function setupTestContext(append = false): ITestListItemDefaultContext { - const fixture = document.createElement('div'); - fixture.id = 'list-item-test-fixture'; - - const listComponent = document.createElement(LIST_CONSTANTS.elementName) as IListComponent; - const component = createListItem(); - listComponent.appendChild(component); - fixture.appendChild(listComponent); - - if (append) { - document.body.appendChild(fixture); - } - - return { - listComponent, - component, - getRootElement: () => getShadowElement(component, LIST_ITEM_CONSTANTS.selectors.LIST_ITEM), - append: () => document.body.appendChild(fixture), - destroy: () => removeElement(fixture), - simulateInteraction: async () => { - const listItem = getShadowElement(component, LIST_ITEM_CONSTANTS.selectors.LIST_ITEM); - if (listItem) { - listItem.dispatchEvent(new Event('pointerenter')); - await timer(); - await tick(); - } - } - }; - } - - function setupDrawerTestContext(): ITestListItemDrawerContext { - const fixture = document.createElement('div'); - fixture.id = 'list-item-drawer-test-fixture'; - const listComponent = document.createElement(LIST_CONSTANTS.elementName) as IListComponent; - const component = createListItem(); - listComponent.appendChild(component) - const drawerComponent = document.createElement(DRAWER_CONSTANTS.elementName) as IDrawerComponent; - drawerComponent.appendChild(listComponent); - fixture.appendChild(drawerComponent); - document.body.appendChild(fixture); - return { - component, - destroy: () => removeElement(fixture) - }; - } - - function setupCheckboxTestContext(append = false): ITestListItemCheckboxContext { - const fixture = document.createElement('div'); - fixture.id = 'list-item-test-fixture'; - const listComponent = document.createElement(LIST_CONSTANTS.elementName) as IListComponent; - const component = createListItem(); - const checkboxComponent = document.createElement(CHECKBOX_CONSTANTS.elementName) as ICheckboxComponent; - checkboxComponent.slot = 'trailing'; - const checkboxInputElement = document.createElement('input'); - checkboxInputElement.type = 'checkbox'; - checkboxComponent.appendChild(checkboxInputElement); - component.appendChild(checkboxComponent); - listComponent.appendChild(component) - fixture.appendChild(listComponent); - - if(append) { - document.body.appendChild(fixture); - } - - return { - listComponent, - component, - getRootElement: () => getShadowElement(component, LIST_ITEM_CONSTANTS.selectors.LIST_ITEM), - getInputElement: () => component.querySelector('input') as HTMLInputElement, - append: () => document.body.appendChild(fixture), - destroy: () => removeElement(fixture) - }; - } - - function setupRadioTestContext(): ITestListItemRadioContext { - const fixture = document.createElement('div'); - fixture.id = 'list-item-radio-test-fixture'; - const listComponent = document.createElement(LIST_CONSTANTS.elementName) as IListComponent; - const component1 = createListItem(); - const component2 = createListItem(); - const radio1Component = createRadio(); - component1.appendChild(radio1Component); - listComponent.appendChild(component1) - const radio2Component = createRadio(); - component2.appendChild(radio2Component); - listComponent.appendChild(component2); - fixture.appendChild(listComponent); - document.body.appendChild(fixture); - return { - component1, - component2, - listComponent, - getRootElement1: () => getShadowElement(component1, LIST_ITEM_CONSTANTS.selectors.LIST_ITEM), - getRootElement2: () => getShadowElement(component2, LIST_ITEM_CONSTANTS.selectors.LIST_ITEM), - getRadioInput1: () => component1.querySelector('input') as HTMLInputElement, - getRadioInput2: () => component2.querySelector('input') as HTMLInputElement, - destroy: () => removeElement(fixture) - }; - } - - function createListItem(): IListItemComponent { - const component = document.createElement(LIST_ITEM_CONSTANTS.elementName) as IListItemComponent; - const lineOne = document.createElement('span'); - lineOne.textContent = 'Line one'; - component.appendChild(lineOne); - return component; - } - - function createRadio(): IRadioComponent { - const component = document.createElement(RADIO_CONSTANTS.elementName) as IRadioComponent; - component.slot = 'trailing'; - const inputElement = document.createElement('input'); - inputElement.type = 'radio'; - inputElement.name = 'list-radio'; - component.appendChild(inputElement); - return component; - } -}); diff --git a/src/test/spec/list/list/list.spec.ts b/src/test/spec/list/list/list.spec.ts deleted file mode 100644 index 2808bce53..000000000 --- a/src/test/spec/list/list/list.spec.ts +++ /dev/null @@ -1,425 +0,0 @@ -import { defineListComponent, IListComponent, LIST_CONSTANTS, LIST_ITEM_CONSTANTS, IListItemComponent, ListComponent } from '@tylertech/forge/list'; -import { DIVIDER_CONSTANTS, IDividerComponent } from '@tylertech/forge/divider'; -import { removeElement, getShadowElement, getActiveElement } from '@tylertech/forge-core'; -import { tick, dispatchKeyEvent } from '@tylertech/forge-testing'; - -interface ITestListItemNumber { - id: number; - text: string; - value: number; -} - -interface ITestListItemString { - id: number; - text: string; - value: string; -} - -const listNumberItems = [ - { id: 1, text: 'one', value: 1 }, - { id: 2, text: 'two', value: 2 }, - { id: 3, text: 'three', value: 3 } -]; - -const listStringItems = [ - { id: 1, text: 'one', value: '1' }, - { id: 2, text: 'two', value: '2' }, - { id: 3, text: 'three', value: '3' } -]; - -interface ITestContext { - context: ITestListContext -} - -interface ITestListContext { - component: IListComponent; - getListItemComponents(): IListItemComponent[]; - append(): void; - destroy(): void; -} - -describe('ListComponent', function(this: ITestContext) { - beforeAll(function(this: ITestContext) { - defineListComponent(); - }); - - afterEach(function(this: ITestContext) { - this.context.destroy(); - }); - - describe('list with number values', function(this: ITestContext) { - it('should not be static by default', function(this: ITestContext) { - this.context = setupTestContext(true, listNumberItems); - expect(this.context.component.static).toBe(false); - }); - - it('should set static via property', async function(this: ITestContext) { - this.context = setupTestContext(true, listNumberItems); - await tick(); - this.context.component.static = true; - expect(this.context.component.hasAttribute(LIST_CONSTANTS.attributes.STATIC)).toBe(true); - expect(this.context.getListItemComponents().every(li => li.static)).toBe(true); - }); - - it('should set static via attribute', function(this: ITestContext) { - this.context = setupTestContext(true, listNumberItems); - this.context.component.setAttribute(LIST_CONSTANTS.attributes.STATIC, 'true'); - expect(this.context.component.static).toBe(true); - expect(this.context.getListItemComponents().every(li => li.static)).toBe(true); - }); - - it('should render list items static when set by default via attribute', function(this: ITestContext) { - this.context = setupTestContext(false, listNumberItems); - this.context.component.setAttribute(LIST_CONSTANTS.attributes.STATIC, 'true'); - this.context.append(); - expect(this.context.component.static).toBe(true); - expect(this.context.getListItemComponents().every(li => li.static)).toBe(true); - }); - - it('should render list items static when set by default via property', function(this: ITestContext) { - this.context = setupTestContext(false, listNumberItems); - this.context.component.static = true; - this.context.append(); - expect(this.context.component.static).toBe(true); - expect(this.context.getListItemComponents().every(li => li.static)).toBe(true); - }); - - it('should set list items to not be static', async function(this: ITestContext) { - this.context = setupTestContext(false, listNumberItems); - this.context.component.static = true; - this.context.append(); - await tick(); - this.context.component.static = false; - expect(this.context.component.hasAttribute(LIST_CONSTANTS.attributes.STATIC)).toBe(false); - expect(this.context.getListItemComponents().every(li => li.static)).toBe(false); - }); - - it('should not handle keydown events when static', async function(this: ITestContext) { - this.context = setupTestContext(false, listNumberItems); - this.context.component.static = true; - this.context.append(); - const keydownSpy = spyOn(this.context.component['_foundation'], '_onKeydown').and.callThrough(); - await tick(); - dispatchKeyEvent(this.context.getListItemComponents()[0], 'keydown', 'Enter'); - expect(keydownSpy).not.toHaveBeenCalled(); - }); - - it('should handle keydown events when not static', async function(this: ITestContext) { - this.context = setupTestContext(true, listNumberItems); - const keydownSpy = spyOn(this.context.component['_foundation'], '_onKeydown').and.callThrough(); - await tick(); - dispatchKeyEvent(this.context.getListItemComponents()[0], 'keydown', 'Enter'); - expect(keydownSpy).toHaveBeenCalledTimes(1); - }); - - it('should handle keydown events when not static', async function(this: ITestContext) { - this.context = setupTestContext(true, listNumberItems); - const keydownSpy = spyOn(this.context.component['_foundation'], '_onKeydown').and.callThrough(); - await tick(); - dispatchKeyEvent(this.context.getListItemComponents()[0], 'keydown', 'Enter'); - expect(keydownSpy).toHaveBeenCalledTimes(1); - }); - - it('should not be dense by default', function(this: ITestContext) { - this.context = setupTestContext(true, listNumberItems); - expect(this.context.component.dense).toBe(false); - }); - - it('should set dense via property', function(this: ITestContext) { - this.context = setupTestContext(true, listNumberItems); - this.context.component.dense = true; - expect(this.context.component.dense).toBe(true); - expect(this.context.component.hasAttribute(LIST_CONSTANTS.attributes.DENSE)).toBe(true); - this.context.component.dense = false; - expect(this.context.component.dense).toBe(false); - expect(this.context.component.hasAttribute(LIST_CONSTANTS.attributes.DENSE)).toBe(false); - }); - - it('should set dense via attribute', function(this: ITestContext) { - this.context = setupTestContext(true, listNumberItems); - this.context.component.setAttribute(LIST_CONSTANTS.attributes.DENSE, 'true'); - expect(this.context.component.dense).toBe(true); - expect(this.context.getListItemComponents().every(li => li.dense)).toBe(true); - }); - - it('should render list items dense when set by default via attribute', function(this: ITestContext) { - this.context = setupTestContext(false, listNumberItems); - this.context.component.setAttribute(LIST_CONSTANTS.attributes.DENSE, 'true'); - this.context.append(); - expect(this.context.component.dense).toBe(true); - expect(this.context.getListItemComponents().every(li => li.dense)).toBe(true); - }); - - it('should render list items dense when set by default via property', function(this: ITestContext) { - this.context = setupTestContext(false, listNumberItems); - this.context.component.dense = true; - this.context.append(); - expect(this.context.component.dense).toBe(true); - expect(this.context.getListItemComponents().every(li => li.dense)).toBe(true); - }); - - it('should set list items to not be dense', async function(this: ITestContext) { - this.context = setupTestContext(false, listNumberItems); - this.context.component.dense = true; - this.context.append(); - await tick(); - this.context.component.dense = false; - expect(this.context.component.hasAttribute(LIST_CONSTANTS.attributes.DENSE)).toBe(false); - expect(this.context.getListItemComponents().every(li => li.dense)).toBe(false); - }); - - it('should set propagate click by default', function(this: ITestContext) { - this.context = setupTestContext(true, listNumberItems); - expect(this.context.component.propagateClick).toBe(true); - }); - - it('should set propagate click via property', function(this: ITestContext) { - this.context = setupTestContext(true, listNumberItems); - this.context.component.propagateClick = false; - expect(this.context.component.propagateClick).toBe(false); - this.context.component.propagateClick = true; - expect(this.context.component.propagateClick).toBe(true); - }); - - it('should set propagate click via attribute', function(this: ITestContext) { - this.context = setupTestContext(true, listNumberItems); - this.context.component.setAttribute(LIST_CONSTANTS.attributes.PROPAGATE_CLICK, 'false'); - expect(this.context.component.propagateClick).toBe(false); - expect(this.context.getListItemComponents().every(li => li.propagateClick)).toBe(false); - this.context.component.setAttribute(LIST_CONSTANTS.attributes.PROPAGATE_CLICK, 'true'); - expect(this.context.component.propagateClick).toBe(true); - expect(this.context.getListItemComponents().every(li => li.propagateClick)).toBe(true); - }); - - it('should set list items to not propagate click when set by default via attribute', function(this: ITestContext) { - this.context = setupTestContext(false, listNumberItems); - this.context.component.setAttribute(LIST_CONSTANTS.attributes.PROPAGATE_CLICK, 'false'); - this.context.append(); - expect(this.context.component.propagateClick).toBe(false); - expect(this.context.getListItemComponents().every(li => li.propagateClick)).toBe(false); - }); - - it('should set list items to not propagate click when set by default via property', function(this: ITestContext) { - this.context = setupTestContext(false, listNumberItems); - this.context.component.propagateClick = false; - this.context.append(); - expect(this.context.component.propagateClick).toBe(false); - expect(this.context.getListItemComponents().every(li => li.propagateClick)).toBe(false); - }); - - it('should not be indented by default', function(this: ITestContext) { - this.context = setupTestContext(true, listNumberItems); - expect(this.context.component.indented).toBe(false); - }); - - it('should set indented via property', function(this: ITestContext) { - this.context = setupTestContext(true, listNumberItems); - this.context.component.indented = true; - expect(this.context.component.indented).toBe(true); - expect(this.context.component.hasAttribute(LIST_CONSTANTS.attributes.INDENTED)).toBe(true); - this.context.component.indented = false; - expect(this.context.component.indented).toBe(false); - expect(this.context.component.hasAttribute(LIST_CONSTANTS.attributes.INDENTED)).toBe(false); - }); - - it('should set indented via attribute', function(this: ITestContext) { - this.context = setupTestContext(true, listNumberItems); - this.context.component.setAttribute(LIST_CONSTANTS.attributes.INDENTED, 'true'); - expect(this.context.component.indented).toBe(true); - expect(this.context.getListItemComponents().every(li => li.indented)).toBe(true); - }); - - it('should render list items indented when set by default via attribute', function(this: ITestContext) { - this.context = setupTestContext(false, listNumberItems); - this.context.component.setAttribute(LIST_CONSTANTS.attributes.INDENTED, 'true'); - this.context.append(); - expect(this.context.component.indented).toBe(true); - expect(this.context.getListItemComponents().every(li => li.indented)).toBe(true); - }); - - it('should render list items indented when set by default via property', function(this: ITestContext) { - this.context = setupTestContext(false, listNumberItems); - this.context.component.indented = true; - this.context.append(); - expect(this.context.component.indented).toBe(true); - expect(this.context.getListItemComponents().every(li => li.indented)).toBe(true); - }); - - it('should set list items to not be indented', async function(this: ITestContext) { - this.context = setupTestContext(false, listNumberItems); - this.context.component.indented = true; - this.context.append(); - await tick(); - this.context.component.indented = false; - expect(this.context.component.hasAttribute(LIST_CONSTANTS.attributes.INDENTED)).toBe(false); - expect(this.context.getListItemComponents().every(li => li.indented)).toBe(false); - }); - - it('should initialize with selected value via property', function(this: ITestContext) { - this.context = setupTestContext(false, listNumberItems); - this.context.component.selectedValue = [1, 2]; - this.context.append(); - expect(this.context.component.selectedValue.length).toBe(2); - const listItemComponents = this.context.getListItemComponents(); - expect(listItemComponents[0].selected).toBe(true); - expect(listItemComponents[1].selected).toBe(true); - }); - - it('should set selected value with via property', async function(this: ITestContext) { - this.context = setupTestContext(true, listNumberItems); - this.context.component.selectedValue = [1, 2]; - expect(this.context.component.selectedValue.length).toBe(2); - let listItemComponents = this.context.getListItemComponents(); - expect(listItemComponents[0].selected).toBe(true); - expect(listItemComponents[1].selected).toBe(true); - - await tick(); - - this.context.component.selectedValue = 1; - expect(this.context.component.selectedValue).toBe(1); - listItemComponents = this.context.getListItemComponents(); - expect(listItemComponents[0].selected).toBe(true); - expect(listItemComponents[1].selected).toBe(false); - }); - - it('should emit selected event when enter is pressed on focused list item', async function(this: ITestContext) { - this.context = setupTestContext(true, listNumberItems); - const listener = jasmine.createSpy('selected listener'); - this.context.component.addEventListener(LIST_ITEM_CONSTANTS.events.SELECT, listener); - await tick(); - - const listItemEl = getShadowElement(this.context.getListItemComponents()[0], LIST_ITEM_CONSTANTS.selectors.LIST_ITEM); - listItemEl.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' })); - - expect(listener).toHaveBeenCalledTimes(1); - }); - - it('should emit selected event when space is pressed on focused list item', async function(this: ITestContext) { - this.context = setupTestContext(true, listNumberItems); - const listener = jasmine.createSpy('selected listener'); - this.context.component.addEventListener(LIST_ITEM_CONSTANTS.events.SELECT, listener); - await tick(); - - const listItemEl = getShadowElement(this.context.getListItemComponents()[0], LIST_ITEM_CONSTANTS.selectors.LIST_ITEM); - listItemEl.dispatchEvent(new KeyboardEvent('keydown', { key: ' ' })); - - expect(listener).toHaveBeenCalledTimes(1); - }); - - it('should not ignore modifier keys when pressed on focused list item', async function(this: ITestContext) { - this.context = setupTestContext(true, listNumberItems); - const listener = jasmine.createSpy('selected listener'); - this.context.component.addEventListener(LIST_ITEM_CONSTANTS.events.SELECT, listener); - const listItemComponents = this.context.getListItemComponents(); - const firstItem = listItemComponents[0]; - const secondItem = listItemComponents[1]; - this.context.component.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' ,ctrlKey: true})); - this.context.component.focus(); - this.context.component.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' ,altKey: true})); - this.context.component.focus(); - this.context.component.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' ,shiftKey: true})); - this.context.component.focus(); - this.context.component.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' ,metaKey: true})); - expect(listener).toHaveBeenCalledTimes(0); - }) - - it('should focus first list item when home is pressed', async function(this: ITestContext) { - this.context = setupTestContext(true, listNumberItems); - await tick(); - const listItemComponents = this.context.getListItemComponents(); - const firstListItem = getShadowElement(listItemComponents[0], LIST_ITEM_CONSTANTS.selectors.LIST_ITEM); - const secondListItem = getShadowElement(listItemComponents[1], LIST_ITEM_CONSTANTS.selectors.LIST_ITEM); - secondListItem.focus(); - dispatchKeyEvent(listItemComponents[1], 'keydown', 'Home', true); - expect(getActiveElement()).toBe(firstListItem); - }); - - it('should focus last list item when end is pressed', async function(this: ITestContext) { - this.context = setupTestContext(true, listNumberItems); - await tick(); - const listItemComponents = this.context.getListItemComponents(); - const lastListItem = getShadowElement(listItemComponents[2], LIST_ITEM_CONSTANTS.selectors.LIST_ITEM); - const secondListItem = getShadowElement(listItemComponents[1], LIST_ITEM_CONSTANTS.selectors.LIST_ITEM); - secondListItem.focus(); - dispatchKeyEvent(listItemComponents[1], 'keydown', 'End', true); - expect(getActiveElement()).toBe(lastListItem); - }); - - it('should focus next list item when down arrow is pressed', async function(this: ITestContext) { - this.context = setupTestContext(true, listNumberItems); - await tick(); - const listItemComponents = this.context.getListItemComponents(); - const firstListItem = getShadowElement(listItemComponents[0], LIST_ITEM_CONSTANTS.selectors.LIST_ITEM); - const secondListItem = getShadowElement(listItemComponents[1], LIST_ITEM_CONSTANTS.selectors.LIST_ITEM); - firstListItem.focus(); - dispatchKeyEvent(listItemComponents[0], 'keydown', 'ArrowDown', true); - expect(getActiveElement()).toBe(secondListItem); - }); - - it('should focus previous list item when up arrow is pressed', async function(this: ITestContext) { - this.context = setupTestContext(true, listNumberItems); - await tick(); - const listItemComponents = this.context.getListItemComponents(); - const firstListItem = getShadowElement(listItemComponents[0], LIST_ITEM_CONSTANTS.selectors.LIST_ITEM); - const secondListItem = getShadowElement(listItemComponents[1], LIST_ITEM_CONSTANTS.selectors.LIST_ITEM); - secondListItem.focus(); - dispatchKeyEvent(listItemComponents[1], 'keydown', 'ArrowUp', true); - expect(getActiveElement()).toBe(firstListItem); - }); - - it('should wrap focus when up arrow is pressed on first list item', async function(this: ITestContext) { - this.context = setupTestContext(true, listNumberItems); - await tick(); - const listItemComponents = this.context.getListItemComponents(); - const firstListItem = getShadowElement(listItemComponents[0], LIST_ITEM_CONSTANTS.selectors.LIST_ITEM); - const lastListItem = getShadowElement(listItemComponents[2], LIST_ITEM_CONSTANTS.selectors.LIST_ITEM); - firstListItem.focus(); - dispatchKeyEvent(listItemComponents[1], 'keydown', 'ArrowUp', true); - expect(getActiveElement()).toBe(lastListItem); - }); - - it('should wrap focus when down arrow is pressed on last list item', async function(this: ITestContext) { - this.context = setupTestContext(true, listNumberItems); - await tick(); - const listItemComponents = this.context.getListItemComponents(); - const firstListItem = getShadowElement(listItemComponents[0], LIST_ITEM_CONSTANTS.selectors.LIST_ITEM); - const lastListItem = getShadowElement(listItemComponents[2], LIST_ITEM_CONSTANTS.selectors.LIST_ITEM); - lastListItem.focus(); - dispatchKeyEvent(listItemComponents[1], 'keydown', 'ArrowDown', true); - expect(getActiveElement()).toBe(firstListItem); - }); - }); - - describe('list with string values', function(this: ITestContext) { - it('should set a single selected string value via attribute', function(this: ITestContext) { - this.context = setupTestContext(true, listStringItems); - const selectedValue = '1'; - this.context.component.setAttribute(LIST_CONSTANTS.attributes.SELECTED_VALUE, selectedValue); - const listItemComponents = this.context.getListItemComponents(); - expect(listItemComponents[0].selected).toBe(true); - expect(listItemComponents[1].selected).toBe(false); - }); - }); - - function setupTestContext(append = false, listItems: ITestListItemNumber[] | ITestListItemString[]): ITestListContext { - const fixture = document.createElement('div'); - fixture.id = 'list-test-fixture'; - const component = document.createElement(LIST_CONSTANTS.elementName) as IListComponent; - for (const item of listItems) { - const listItem = document.createElement(LIST_ITEM_CONSTANTS.elementName) as IListItemComponent; - listItem.value = item.value; - listItem.textContent = item.text; - component.appendChild(listItem); - const divider = document.createElement(DIVIDER_CONSTANTS.elementName) as IDividerComponent; - component.appendChild(divider); - } - fixture.appendChild(component); - if (append) document.body.appendChild(fixture); - return { - component, - getListItemComponents: () => Array.from(component.querySelectorAll(LIST_ITEM_CONSTANTS.elementName)), - append: () => document.body.appendChild(fixture), - destroy: () => removeElement(fixture) - }; - } -}); diff --git a/src/test/spec/menu/menu.spec.ts b/src/test/spec/menu/menu.spec.ts index 8a656e506..0551a19d6 100644 --- a/src/test/spec/menu/menu.spec.ts +++ b/src/test/spec/menu/menu.spec.ts @@ -332,8 +332,8 @@ describe('MenuComponent', function(this: ITestContext) { this.context.component.options = options; this.context.component.open = true; await timer(300); - const listItemHost = getShadowElement(getPopupListItem(5), LIST_ITEM_CONSTANTS.selectors.LIST_ITEM); - expect(listItemHost.classList.contains(LIST_ITEM_CONSTANTS.classes.DISABLED)).toBe(true); + + expect(getPopupListItem(5).hasAttribute(LIST_ITEM_CONSTANTS.attributes.DISABLED)).toBe(true); }); it(`should have selected class when option is set to selected and persistSelection is true`, async function(this: ITestContext) { @@ -344,8 +344,8 @@ describe('MenuComponent', function(this: ITestContext) { this.context.component.options = options; this.context.component.open = true; await timer(300); - const listItemHost = getShadowElement(getPopupListItem(5), LIST_ITEM_CONSTANTS.selectors.LIST_ITEM); - expect(listItemHost.classList.contains(LIST_ITEM_CONSTANTS.classes.SELECTED)).toBe(true); + + expect(getPopupListItem(5).hasAttribute(LIST_ITEM_CONSTANTS.attributes.SELECTED)).toBe(true); }); it(`should not have selected class when option is set to selected and persistSelection is false`, async function(this: ITestContext) { @@ -356,8 +356,8 @@ describe('MenuComponent', function(this: ITestContext) { this.context.component.options = options; this.context.component.open = true; await timer(300); - const listItemHost = getShadowElement(getPopupListItem(5), LIST_ITEM_CONSTANTS.selectors.LIST_ITEM); - expect(listItemHost.classList.contains(LIST_ITEM_CONSTANTS.classes.SELECTED)).toBe(false); + + expect(getPopupListItem(5).hasAttribute(LIST_ITEM_CONSTANTS.attributes.SELECTED)).toBe(false); }); it(`should not have selected class when option is set to selected and persistSelection is switched from true to false`, async function(this: ITestContext) { @@ -370,8 +370,8 @@ describe('MenuComponent', function(this: ITestContext) { this.context.component.persistSelection = false; this.context.component.open = true; await timer(300); - const listItemHost = getShadowElement(getPopupListItem(5), LIST_ITEM_CONSTANTS.selectors.LIST_ITEM); - expect(listItemHost.classList.contains(LIST_ITEM_CONSTANTS.classes.SELECTED)).toBe(false); + + expect(getPopupListItem(5).hasAttribute(LIST_ITEM_CONSTANTS.attributes.SELECTED)).toBe(false); }); it('should use option builder', async function(this: ITestContext) { @@ -447,8 +447,7 @@ describe('MenuComponent', function(this: ITestContext) { await timer(300); this.context.getToggleElement().dispatchEvent(new KeyboardEvent('keydown', { code: 'ArrowDown' })); - const listItem = getShadowElement(getPopupListItem(0), LIST_ITEM_CONSTANTS.selectors.LIST_ITEM); - expect(listItem.classList.contains(LIST_ITEM_CONSTANTS.classes.ACTIVE)).toBe(true); + expect(getPopupListItem(0).hasAttribute(LIST_ITEM_CONSTANTS.attributes.ACTIVE)).toBe(true); }); it('arrow up from the start should activate the last list element', async function(this: ITestContext) { @@ -459,8 +458,7 @@ describe('MenuComponent', function(this: ITestContext) { await timer(300); this.context.getToggleElement().dispatchEvent(new KeyboardEvent('keydown', { code: 'ArrowUp' })); - const listItem = getShadowElement(getPopupListItem(6), LIST_ITEM_CONSTANTS.selectors.LIST_ITEM); - expect(listItem.classList.contains(LIST_ITEM_CONSTANTS.classes.ACTIVE)).toBe(true); + expect(getPopupListItem(6).hasAttribute(LIST_ITEM_CONSTANTS.attributes.ACTIVE)).toBe(true); }); it('enter should select the list element when persistSelection is true', async function(this: ITestContext) { @@ -571,7 +569,7 @@ describe('MenuComponent', function(this: ITestContext) { this.context.component.open = true; await timer(300); - getShadowElement(getPopupListItem(0), LIST_ITEM_CONSTANTS.selectors.LIST_ITEM).dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' })); + getPopupListItem(0).dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' })); await timer(300); @@ -684,7 +682,7 @@ describe('MenuComponent', function(this: ITestContext) { await tick(); const childMenuListItems = getChildMenuListItems(getPopupElement()); - childMenuListItems[1].shadowRoot!.querySelector(LIST_ITEM_CONSTANTS.selectors.LIST_ITEM)!.dispatchEvent(new MouseEvent('click')); + childMenuListItems[1].dispatchEvent(new MouseEvent('click')); expect(selectSpy).toHaveBeenCalledTimes(1); expect(selectSpy).toHaveBeenCalledWith(jasmine.objectContaining({ detail: { index: 1, value: EXPETED_SELECTION_VALUE, parentValue: options[1].value }})); diff --git a/src/test/spec/paginator/paginator.spec.ts b/src/test/spec/paginator/paginator.spec.ts index 3c583e46e..c3c40044f 100644 --- a/src/test/spec/paginator/paginator.spec.ts +++ b/src/test/spec/paginator/paginator.spec.ts @@ -114,6 +114,50 @@ describe('PaginatorComponent', function(this: ITestContext) { expect(this.context.nextPageButton.hasAttribute('disabled')).toBe(true); }); + it('should move focus to previous page button when next page button is disabled', function(this: ITestContext) { + this.context = setupTestContext(); + this.context.paginator.total = 100; + this.context.paginator.pageIndex = 2; + this.context.nextPageButton.focus(); + this.context.nextPageButton.click(); + + expect(this.context.nextPageButton.hasAttribute('disabled')).toBe(true); + expect(this.context.previousPageButton.matches(':focus')).toBe(true); + }); + + it('should move focus to next page button when previous page button is disabled', function(this: ITestContext) { + this.context = setupTestContext(); + this.context.paginator.total = 100; + this.context.paginator.pageIndex = 1; + this.context.previousPageButton.focus(); + this.context.previousPageButton.click(); + + expect(this.context.previousPageButton.hasAttribute('disabled')).toBe(true); + expect(this.context.nextPageButton.matches(':focus')).toBe(true); + }); + + it('should move focus to next page button when first page button is disabled', function(this: ITestContext) { + this.context = setupTestContext(true, true); + this.context.paginator.total = 100; + this.context.paginator.pageIndex = 1; + this.context.firstPageButton.focus(); + this.context.firstPageButton.click(); + + expect(this.context.firstPageButton.hasAttribute('disabled')).toBe(true); + expect(this.context.nextPageButton.matches(':focus')).toBe(true); + }); + + it('should move focus to previous page button when last page button is disabled', function(this: ITestContext) { + this.context = setupTestContext(true, true); + this.context.paginator.total = 100; + this.context.paginator.pageIndex = 2; + this.context.lastPageButton.focus(); + this.context.lastPageButton.click(); + + expect(this.context.lastPageButton.hasAttribute('disabled')).toBe(true); + expect(this.context.previousPageButton.matches(':focus')).toBe(true); + }); + it('should emit change event when clicking next page button', function(this: ITestContext) { this.context = setupTestContext(); this.context.paginator.total = 100; @@ -248,8 +292,7 @@ describe('PaginatorComponent', function(this: ITestContext) { this.context.pageSizeSelect.open = true; const listItem = this.context.pageSizeSelect.popupElement?.querySelector(LIST_ITEM_CONSTANTS.elementName) as IListItemComponent; - const listItemRoot = getShadowElement(listItem, LIST_ITEM_CONSTANTS.selectors.LIST_ITEM); - listItemRoot.click(); + listItem.click(); await tick(); expect(listItem.value).toBe('5'); @@ -267,8 +310,7 @@ describe('PaginatorComponent', function(this: ITestContext) { this.context.pageSizeSelect.open = true; const listItem = this.context.pageSizeSelect.popupElement?.querySelector(LIST_ITEM_CONSTANTS.elementName) as IListItemComponent; - const listItemRoot = getShadowElement(listItem, LIST_ITEM_CONSTANTS.selectors.LIST_ITEM); - listItemRoot.click(); + listItem.click(); await tick(); expect(this.context.paginator.pageSize).toBe(Number(originalPageSize)); @@ -378,6 +420,45 @@ describe('PaginatorComponent', function(this: ITestContext) { expect(this.context.paginator.pageIndex).toBe(0); }); + it('should set page index via offset property if total is not > 0 initially', function(this: ITestContext) { + this.context = setupTestContext(); + const pageSizeOptions = [5, 10, 25]; + this.context.paginator.pageSizeOptions = pageSizeOptions; + this.context.paginator.pageSize = 25; + + expect(this.context.paginator.total).toBe(0); + + this.context.paginator.offset = 25; + expect(this.context.paginator.pageIndex).toBe(0); + + this.context.paginator.total = 100; + expect(this.context.paginator.pageIndex).toBe(1); + }); + + it('should update offset when page index changes', function(this: ITestContext) { + this.context = setupTestContext(); + const pageSizeOptions = [5, 10, 25]; + this.context.paginator.pageSizeOptions = pageSizeOptions; + this.context.paginator.pageSize = 25; + this.context.paginator.total = 100; + expect(this.context.paginator.pageIndex).toBe(0); + expect(this.context.paginator.offset).toBe(0); + + this.context.paginator.pageIndex = 1; + + expect(this.context.paginator.pageIndex).toBe(1); + expect(this.context.paginator.offset).toBe(25); + + this.context.paginator.pageIndex = 3; + expect(this.context.paginator.pageIndex).toBe(3); + expect(this.context.paginator.offset).toBe(75); + + this.context.paginator.pageIndex = 0; + + expect(this.context.paginator.pageIndex).toBe(0); + expect(this.context.paginator.offset).toBe(0); + }); + it('should get offset', function(this: ITestContext) { this.context = setupTestContext(); const pageSizeOptions = [5, 10, 25]; diff --git a/src/test/spec/popup/popup.spec.ts b/src/test/spec/popup/popup.spec.ts index fcd0a8648..0c0e53c23 100644 --- a/src/test/spec/popup/popup.spec.ts +++ b/src/test/spec/popup/popup.spec.ts @@ -128,6 +128,18 @@ describe('Popup component', function(this: ITestContext) { targetElement.removeEventListener(POPUP_CONSTANTS.events.CLOSE, callback); }); + it('should also emit close event from the host element', function(this: ITestContext) { + this.context = setupTestContext(); + const { component, targetElement } = this.context; + component.targetElement = targetElement; + const callback = jasmine.createSpy('callback'); + component.addEventListener(POPUP_CONSTANTS.events.CLOSE, callback); + component.open = true; + component.open = false; + expect(callback).toHaveBeenCalledTimes(1); + component.removeEventListener(POPUP_CONSTANTS.events.CLOSE, callback); + }); + it('should accept and return focus on open and close', async function(this: ITestContext) { this.context = setupTestContext(); const { component, targetElement } = this.context; diff --git a/src/test/spec/select/select.spec.ts b/src/test/spec/select/select.spec.ts index 9558eb721..149423584 100644 --- a/src/test/spec/select/select.spec.ts +++ b/src/test/spec/select/select.spec.ts @@ -1487,8 +1487,8 @@ describe('SelectComponent', function(this: ITestContext) { _openDropdown(this.context.component); const listItems = Array.from(this.context.component.popupElement!.querySelectorAll(LIST_ITEM_CONSTANTS.elementName)) as IListItemComponent[]; - getShadowElement(listItems[0], LIST_ITEM_CONSTANTS.selectors.LIST_ITEM).click(); - getShadowElement(listItems[1], LIST_ITEM_CONSTANTS.selectors.LIST_ITEM).click(); + listItems[0].click(); + listItems[1].click(); expect(this.context.component.value).toEqual(['one', 'two']); expect(this.context.component.selectedIndex).toEqual([0, 1]); expect(listItems[0].selected).toBe(true); diff --git a/src/test/spec/text-field/text-field.spec.ts b/src/test/spec/text-field/text-field.spec.ts index b0f226709..80bd9ca4e 100644 --- a/src/test/spec/text-field/text-field.spec.ts +++ b/src/test/spec/text-field/text-field.spec.ts @@ -184,6 +184,34 @@ describe('TextFieldComponent', function(this: ITestContext) { expectFloatingLabelState(this.context, true); }); + it('should float label when value is set after changing from dense to roomy', async function(this: ITestContext) { + this.context = setupTestContext(true, { density: 'dense' }); + + await tick(); + this.context.input.value = 'test'; + await floatTick(); + + this.context.component.density = 'roomy'; + await floatTick(); + + expect(this.context.component.hasAttribute(FIELD_CONSTANTS.attributes.HOST_LABEL_FLOATING)).toBeTrue(); + expectFloatingLabelState(this.context, true); + }); + + it('should float label when value is set after changing from dense to default', async function(this: ITestContext) { + this.context = setupTestContext(true, { density: 'dense' }); + + await tick(); + this.context.input.value = 'test'; + await floatTick(); + + this.context.component.density = 'default'; + await floatTick(); + + expect(this.context.component.hasAttribute(FIELD_CONSTANTS.attributes.HOST_LABEL_FLOATING)).toBeTrue(); + expectFloatingLabelState(this.context, true); + }); + it('should float label when invoked programmatically', async function(this: ITestContext) { this.context = setupTestContext(); await tick(); diff --git a/src/test/spec/time-picker/time-picker.spec.ts b/src/test/spec/time-picker/time-picker.spec.ts index 2b1c7eccd..8f00e6c58 100644 --- a/src/test/spec/time-picker/time-picker.spec.ts +++ b/src/test/spec/time-picker/time-picker.spec.ts @@ -622,7 +622,7 @@ describe('TimePickerComponent', function(this: ITestContext) { await timer(POPUP_CONSTANTS.numbers.ANIMATION_DURATION); const listItems = this.context.getListItems(); - const matchingListItem = listItems.find(li => li.selected) as IListItemComponent; + const matchingListItem = listItems.find(li => li.selected) as IListItemComponent; expect(matchingListItem.value.time).toBe(timeMillis); }); @@ -667,7 +667,7 @@ describe('TimePickerComponent', function(this: ITestContext) { await timer(POPUP_CONSTANTS.numbers.ANIMATION_DURATION); const listItems = this.context.getListItems(); - const activeListItem = listItems.find(li => li.selected) as IListItemComponent; + const activeListItem = listItems.find(li => li.selected) as IListItemComponent; expect(activeListItem.value.time).toBe(timeMillis); }); @@ -701,7 +701,7 @@ describe('TimePickerComponent', function(this: ITestContext) { await timer(POPUP_CONSTANTS.numbers.ANIMATION_DURATION); const listItems = this.context.getListItems(); - const restrictedListItem = listItems.find(li => li.value.time === firstRestrictedTimeMillis) as IListItemComponent; + const restrictedListItem = listItems.find((li: IListItemComponent) => li.value.time === firstRestrictedTimeMillis) as IListItemComponent; expect(restrictedListItem.disabled).toBeTrue(); @@ -872,7 +872,7 @@ describe('TimePickerComponent', function(this: ITestContext) { this.context.inputElement.dispatchEvent(new KeyboardEvent('keydown', { code: 'End' })); this.context.inputElement.dispatchEvent(new KeyboardEvent('keydown', { code: 'Enter' })); - const selectedListItem = listItems[listItems.length - 1]; + const selectedListItem = listItems[listItems.length - 1] as IListItemComponent; const selectedTimeString = millisToTimeString(selectedListItem.value.time, true, false); expect(changeSpy).toHaveBeenCalledOnceWith(jasmine.objectContaining({ detail: selectedTimeString })); @@ -923,10 +923,11 @@ describe('TimePickerComponent', function(this: ITestContext) { await timer(POPUP_CONSTANTS.numbers.ANIMATION_DURATION); const listItems = this.context.getListItems(); + const listItem = listItems[0] as IListItemComponent; - expect(listItems[0].value.time).toBeNull(); - expect(listItems[0].innerText).toBe('Now'); - expect(listItems[0].value.metadata).toBe('now'); + expect(listItem.value.time).toBeNull(); + expect(listItem.innerText).toBe('Now'); + expect(listItem.value.metadata).toBe('now'); }); it('should show "now" as the only option in the dropdown when showHourOptions is false and showNow is true and customOptions is empty', async function(this: ITestContext) { @@ -939,11 +940,12 @@ describe('TimePickerComponent', function(this: ITestContext) { await timer(POPUP_CONSTANTS.numbers.ANIMATION_DURATION); const listItems = this.context.getListItems(); + const firstListItem = listItems[0] as IListItemComponent; expect(listItems.length).toBe(1); - expect(listItems[0].value.time).toBeNull(); - expect(listItems[0].innerText).toBe('Now'); - expect(listItems[0].value.metadata).toBe('now'); + expect(firstListItem.value.time).toBeNull(); + expect(firstListItem.innerText).toBe('Now'); + expect(firstListItem.value.metadata).toBe('now'); }); it('should should not show dropdown when showNow is false and showHourOptions is false and customOptions is empty', async function(this: ITestContext) { @@ -973,15 +975,17 @@ describe('TimePickerComponent', function(this: ITestContext) { expect(this.context.component.customOptions).toEqual(customOptions); - expect(listItems[0].innerText).toBe(customOptions[0].label); - expect(listItems[0].value.time).toBeNull(); - expect(listItems[0].value.metadata).toBe(customOptions[0].value); - expect(listItems[0].value.isCustom).toBeTrue(); + const firstListItem = listItems[0] as IListItemComponent; + expect(firstListItem.innerText).toBe(customOptions[0].label); + expect(firstListItem.value.time).toBeNull(); + expect(firstListItem.value.metadata).toBe(customOptions[0].value); + expect(firstListItem.value.isCustom).toBeTrue(); - expect(listItems[1].innerText).toBe(customOptions[1].label); - expect(listItems[1].value.time).toBeNull(); - expect(listItems[1].value.metadata).toBe(customOptions[1].value); - expect(listItems[1].value.isCustom).toBeTrue(); + const secondListItem = listItems[1] as IListItemComponent; + expect(secondListItem.innerText).toBe(customOptions[1].label); + expect(secondListItem.value.time).toBeNull(); + expect(secondListItem.value.metadata).toBe(customOptions[1].value); + expect(secondListItem.value.isCustom).toBeTrue(); }); it('should call toMilliseconds function when selecting custom options', async function(this: ITestContext) { @@ -1014,7 +1018,7 @@ describe('TimePickerComponent', function(this: ITestContext) { const firstListItemMillis = timeStringToMillis(min, true, false); const lastListItemMillis = timeStringToMillis(max, true, false); - const listItems = this.context.getListItems(); + const listItems = this.context.getListItems() as IListItemComponent[]; expect(listItems[0].value.time).toBe(firstListItemMillis); expect(listItems[listItems.length - 1].value.time).toBe(lastListItemMillis); @@ -1047,6 +1051,20 @@ describe('TimePickerComponent', function(this: ITestContext) { expect(inputElement.value).toBe('01:01'); }); + it('should select mask when shown on focus', function(this: ITestContext) { + this.context = _createTimePickerContext(); + const inputElement = this.context.inputElement; + this.context.component.setAttribute(TIME_PICKER_CONSTANTS.attributes.MASKED, ''); + this.context.component.setAttribute(TIME_PICKER_CONSTANTS.attributes.SHOW_MASK_FORMAT, ''); + + expect(this.context.component.masked).toBe(true); + expect(this.context.component.showMaskFormat).toBe(true); + inputElement.focus(); + + expect(inputElement.selectionStart).toEqual(0); + expect(inputElement.selectionEnd).toEqual('__:__ __'.length); + }); + it('should only show default mask format on focus', function(this: ITestContext) { this.context = _createTimePickerContext(); const inputElement = this.context.inputElement; diff --git a/src/test/utils/floating-label-utils.ts b/src/test/utils/floating-label-utils.ts index 006d8aa55..abf7d051a 100644 --- a/src/test/utils/floating-label-utils.ts +++ b/src/test/utils/floating-label-utils.ts @@ -6,14 +6,14 @@ export interface IFloatingLabelContext { } export function expectFloatingLabelState( - instance: IFloatingLabelContext, + instance: IFloatingLabelContext, isFloating: boolean ): void { testFloatingLabelState(instance.label, isFloating); } export function testFloatingLabelState( - labelElement: HTMLLabelElement, + labelElement: HTMLLabelElement, isFloating: boolean ): void { expect(labelElement.classList.contains(FLOATING_LABEL_CONSTANTS.classes.FLOAT_ABOVE)).toBe(isFloating); @@ -22,4 +22,4 @@ export function testFloatingLabelState( export async function floatTick(): Promise { await tick(); await tick(); -} \ No newline at end of file +} diff --git a/tsconfig.json b/tsconfig.json index 569be885d..a24f6bcf6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ "target": "es2015", "module": "commonjs", "moduleResolution": "node", - "lib": ["es2015", "dom", "es2017", "DOM.Iterable"], + "lib": ["es2015", "dom", "es2017", "dom.iterable"], "noImplicitAny": true, "strictNullChecks": true, "experimentalDecorators": true,