Merge pull request #373 from Safelite/refactor/linting2

SSR-616
This commit is contained in:
DavidAtSafelite 2023-07-24 12:56:24 -04:00 committed by GitHub
commit a1b21c9e19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 869 additions and 685 deletions

View file

@ -3,6 +3,9 @@ module.exports = {
browser: true,
jest: true
},
parserOptions: {
ecmaVersion: 14
},
extends: [
'eslint-config-airbnb-base',
'plugin:vue/vue3-recommended'
@ -16,9 +19,10 @@ module.exports = {
'object-curly-newline': ['error', { consistent: true }],
'function-paren-newline': ['error', 'never'],
'operator-linebreak': ['error', 'before'],
'implicit-arrow-linebreak': ['error', 'below'],
'implicit-arrow-linebreak': ['error', 'beside'],
'comma-dangle': ['error', 'never'],
indent: ['error', 4],
'max-len': ['error', { code: 140 }],
'vue/html-indent': 'off',
'vue/html-closing-bracket-newline': ['error', {
singleline: 'never',
@ -33,7 +37,7 @@ module.exports = {
svg: 'always',
math: 'always'
}],
'import/extensions': ['error', 'always', { vue: 'never' }]
'import/extensions': ['error', 'always', { vue: 'never', js: 'ignorePackages' }]
},
settings: {
'import/resolver': {

586
package-lock.json generated
View file

@ -8,11 +8,8 @@
"name": "digitalconsumer.iss",
"version": "0.1.0",
"dependencies": {
"@pinia/testing": "0.0.14",
"@popperjs/core": "^2.10.2",
"axios": "^0.27.2",
"bootstrap": "^5.2.2",
"jest-junit": "^13.0.0",
"bootstrap": "^5.2.3",
"maska": "^1.5.0",
"pinia": "^2.0.22",
"pinia-plugin-persistedstate": "^2.2.0",
@ -22,6 +19,7 @@
"vue-router": "4.1.3"
},
"devDependencies": {
"@pinia/testing": "0.1.2",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/user-event": "14.4.3",
"@testing-library/vue": "6.6.1",
@ -36,8 +34,10 @@
"eslint-config-airbnb-base": "15.0.0",
"eslint-import-resolver-alias": "1.1.2",
"eslint-plugin-import": "2.26.0",
"eslint-plugin-vue": "9.8.0",
"eslint-plugin-vue": "^9.15.1",
"jest": "^27.0.5",
"jest-junit": "^13.0.0",
"jsdoc": "^4.0.2",
"sass": "^1.32.7",
"sass-loader": "^12.0.0",
"vitest": "^0.32.4"
@ -503,9 +503,9 @@
}
},
"node_modules/@babel/parser": {
"version": "7.19.1",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.1.tgz",
"integrity": "sha512-h7RCSorm1DdTVGJf3P2Mhj3kdnkmF/EiysUkzS2TdgAYqyjFdMQJbVuXOBej2SBJaXan/lIVtT6KkGbyyq753A==",
"version": "7.22.7",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz",
"integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==",
"bin": {
"parser": "bin/babel-parser.js"
},
@ -2127,6 +2127,21 @@
"node": ">=12"
}
},
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
"integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
"dev": true,
"dependencies": {
"eslint-visitor-keys": "^3.3.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"peerDependencies": {
"eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
}
},
"node_modules/@eslint/eslintrc": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz",
@ -2926,10 +2941,9 @@
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.14",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
"dev": true
"version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.15",
@ -2941,6 +2955,18 @@
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"node_modules/@jsdoc/salty": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.5.tgz",
"integrity": "sha512-TfRP53RqunNe2HBobVBJ0VLhK1HbfvBYeTC1ahnN64PWvyYyGebmMiPkuwvD9fpw2ZbkoPb8Q7mwy0aR8Z9rvw==",
"dev": true,
"dependencies": {
"lodash": "^4.17.21"
},
"engines": {
"node": ">=v12.0.0"
}
},
"node_modules/@leichtgewicht/ip-codec": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
@ -2995,17 +3021,44 @@
}
},
"node_modules/@pinia/testing": {
"version": "0.0.14",
"resolved": "https://registry.npmjs.org/@pinia/testing/-/testing-0.0.14.tgz",
"integrity": "sha512-ZmZwVNd/NnKYLIfjfuKl0zlJ3UdiXFpsHzSlL6wCeezSlyrqGMxsIQKv0J6fleu38gyCNTPBEipfxrt8V4+VIg==",
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/@pinia/testing/-/testing-0.1.2.tgz",
"integrity": "sha512-8EyBaVFtv682tBiF810u81vAJ1ykwnaldkVDU52oqtVbh5FUj5dSCSM9+MtRM/tZkq0AulwMK4BnBktqwsnEnw==",
"dev": true,
"dependencies": {
"vue-demi": "*"
"vue-demi": ">=0.14.5"
},
"funding": {
"url": "https://github.com/sponsors/posva"
},
"peerDependencies": {
"pinia": ">=2.0.19"
"pinia": ">=2.1.2"
}
},
"node_modules/@pinia/testing/node_modules/vue-demi": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.5.tgz",
"integrity": "sha512-o9NUVpl/YlsGJ7t+xuqJKx8EBGf1quRhCiT6D/J0pfwmk9zUwYkC7yrF4SZCe6fETvSM3UNL2edcbYrSyc4QHA==",
"dev": true,
"hasInstallScript": true,
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/@polka/url": {
@ -3015,9 +3068,10 @@
"dev": true
},
"node_modules/@popperjs/core": {
"version": "2.11.6",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
"integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==",
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
@ -3605,6 +3659,28 @@
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
"dev": true
},
"node_modules/@types/linkify-it": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz",
"integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==",
"dev": true
},
"node_modules/@types/markdown-it": {
"version": "12.2.3",
"resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz",
"integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==",
"dev": true,
"dependencies": {
"@types/linkify-it": "*",
"@types/mdurl": "*"
}
},
"node_modules/@types/mdurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz",
"integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==",
"dev": true
},
"node_modules/@types/mime": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz",
@ -3837,18 +3913,6 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/@vitest/snapshot/node_modules/magic-string": {
"version": "0.30.0",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz",
"integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==",
"dev": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.13"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@vitest/snapshot/node_modules/pretty-format": {
"version": "29.5.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz",
@ -4491,49 +4555,49 @@
}
},
"node_modules/@vue/compiler-core": {
"version": "3.2.38",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.38.tgz",
"integrity": "sha512-/FsvnSu7Z+lkd/8KXMa4yYNUiqQrI22135gfsQYVGuh5tqEgOB0XqrUdb/KnCLa5+TmQLPwvyUnKMyCpu+SX3Q==",
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.4.tgz",
"integrity": "sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==",
"dependencies": {
"@babel/parser": "^7.16.4",
"@vue/shared": "3.2.38",
"@babel/parser": "^7.21.3",
"@vue/shared": "3.3.4",
"estree-walker": "^2.0.2",
"source-map": "^0.6.1"
"source-map-js": "^1.0.2"
}
},
"node_modules/@vue/compiler-dom": {
"version": "3.2.38",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.38.tgz",
"integrity": "sha512-zqX4FgUbw56kzHlgYuEEJR8mefFiiyR3u96498+zWPsLeh1WKvgIReoNE+U7gG8bCUdvsrJ0JRmev0Ky6n2O0g==",
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.4.tgz",
"integrity": "sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==",
"dependencies": {
"@vue/compiler-core": "3.2.38",
"@vue/shared": "3.2.38"
"@vue/compiler-core": "3.3.4",
"@vue/shared": "3.3.4"
}
},
"node_modules/@vue/compiler-sfc": {
"version": "3.2.38",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.38.tgz",
"integrity": "sha512-KZjrW32KloMYtTcHAFuw3CqsyWc5X6seb8KbkANSWt3Cz9p2qA8c1GJpSkksFP9ABb6an0FLCFl46ZFXx3kKpg==",
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.4.tgz",
"integrity": "sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==",
"dependencies": {
"@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.38",
"@vue/compiler-dom": "3.2.38",
"@vue/compiler-ssr": "3.2.38",
"@vue/reactivity-transform": "3.2.38",
"@vue/shared": "3.2.38",
"@babel/parser": "^7.20.15",
"@vue/compiler-core": "3.3.4",
"@vue/compiler-dom": "3.3.4",
"@vue/compiler-ssr": "3.3.4",
"@vue/reactivity-transform": "3.3.4",
"@vue/shared": "3.3.4",
"estree-walker": "^2.0.2",
"magic-string": "^0.25.7",
"magic-string": "^0.30.0",
"postcss": "^8.1.10",
"source-map": "^0.6.1"
"source-map-js": "^1.0.2"
}
},
"node_modules/@vue/compiler-ssr": {
"version": "3.2.38",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.38.tgz",
"integrity": "sha512-bm9jOeyv1H3UskNm4S6IfueKjUNFmi2kRweFIGnqaGkkRePjwEcfCVqyS3roe7HvF4ugsEkhf4+kIvDhip6XzQ==",
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.4.tgz",
"integrity": "sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==",
"dependencies": {
"@vue/compiler-dom": "3.2.38",
"@vue/shared": "3.2.38"
"@vue/compiler-dom": "3.3.4",
"@vue/shared": "3.3.4"
}
},
"node_modules/@vue/component-compiler-utils": {
@ -4601,65 +4665,65 @@
"dev": true
},
"node_modules/@vue/devtools-api": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.2.1.tgz",
"integrity": "sha512-OEgAMeQXvCoJ+1x8WyQuVZzFo0wcyCmUR3baRVLmKBo1LmYZWMlRiXlux5jd0fqVJu6PfDbOrZItVqUEzLobeQ=="
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.5.0.tgz",
"integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q=="
},
"node_modules/@vue/reactivity": {
"version": "3.2.38",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.38.tgz",
"integrity": "sha512-6L4myYcH9HG2M25co7/BSo0skKFHpAN8PhkNPM4xRVkyGl1K5M3Jx4rp5bsYhvYze2K4+l+pioN4e6ZwFLUVtw==",
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.4.tgz",
"integrity": "sha512-kLTDLwd0B1jG08NBF3R5rqULtv/f8x3rOFByTDz4J53ttIQEDmALqKqXY0J+XQeN0aV2FBxY8nJDf88yvOPAqQ==",
"dependencies": {
"@vue/shared": "3.2.38"
"@vue/shared": "3.3.4"
}
},
"node_modules/@vue/reactivity-transform": {
"version": "3.2.38",
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.38.tgz",
"integrity": "sha512-3SD3Jmi1yXrDwiNJqQ6fs1x61WsDLqVk4NyKVz78mkaIRh6d3IqtRnptgRfXn+Fzf+m6B1KxBYWq1APj6h4qeA==",
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.4.tgz",
"integrity": "sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==",
"dependencies": {
"@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.38",
"@vue/shared": "3.2.38",
"@babel/parser": "^7.20.15",
"@vue/compiler-core": "3.3.4",
"@vue/shared": "3.3.4",
"estree-walker": "^2.0.2",
"magic-string": "^0.25.7"
"magic-string": "^0.30.0"
}
},
"node_modules/@vue/runtime-core": {
"version": "3.2.38",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.38.tgz",
"integrity": "sha512-kk0qiSiXUU/IKxZw31824rxmFzrLr3TL6ZcbrxWTKivadoKupdlzbQM4SlGo4MU6Zzrqv4fzyUasTU1jDoEnzg==",
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.4.tgz",
"integrity": "sha512-R+bqxMN6pWO7zGI4OMlmvePOdP2c93GsHFM/siJI7O2nxFRzj55pLwkpCedEY+bTMgp5miZ8CxfIZo3S+gFqvA==",
"dependencies": {
"@vue/reactivity": "3.2.38",
"@vue/shared": "3.2.38"
"@vue/reactivity": "3.3.4",
"@vue/shared": "3.3.4"
}
},
"node_modules/@vue/runtime-dom": {
"version": "3.2.38",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.38.tgz",
"integrity": "sha512-4PKAb/ck2TjxdMSzMsnHViOrrwpudk4/A56uZjhzvusoEU9xqa5dygksbzYepdZeB5NqtRw5fRhWIiQlRVK45A==",
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.4.tgz",
"integrity": "sha512-Aj5bTJ3u5sFsUckRghsNjVTtxZQ1OyMWCr5dZRAPijF/0Vy4xEoRCwLyHXcj4D0UFbJ4lbx3gPTgg06K/GnPnQ==",
"dependencies": {
"@vue/runtime-core": "3.2.38",
"@vue/shared": "3.2.38",
"csstype": "^2.6.8"
"@vue/runtime-core": "3.3.4",
"@vue/shared": "3.3.4",
"csstype": "^3.1.1"
}
},
"node_modules/@vue/server-renderer": {
"version": "3.2.38",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.38.tgz",
"integrity": "sha512-pg+JanpbOZ5kEfOZzO2bt02YHd+ELhYP8zPeLU1H0e7lg079NtuuSB8fjLdn58c4Ou8UQ6C1/P+528nXnLPAhA==",
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.4.tgz",
"integrity": "sha512-Q6jDDzR23ViIb67v+vM1Dqntu+HUexQcsWKhhQa4ARVzxOY2HbC7QRW/ggkDBd5BU+uM1sV6XOAP0b216o34JQ==",
"dependencies": {
"@vue/compiler-ssr": "3.2.38",
"@vue/shared": "3.2.38"
"@vue/compiler-ssr": "3.3.4",
"@vue/shared": "3.3.4"
},
"peerDependencies": {
"vue": "3.2.38"
"vue": "3.3.4"
}
},
"node_modules/@vue/shared": {
"version": "3.2.38",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.38.tgz",
"integrity": "sha512-dTyhTIRmGXBjxJE+skC8tTWCGLCVc4wQgRRLt8+O9p5ewBAjoBwtCAkLPrtToSr1xltoe3st21Pv953aOZ7alg=="
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz",
"integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ=="
},
"node_modules/@vue/test-utils": {
"version": "2.0.2",
@ -5104,6 +5168,7 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"engines": {
"node": ">=8"
}
@ -5723,9 +5788,9 @@
"dev": true
},
"node_modules/bootstrap": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.2.tgz",
"integrity": "sha512-dEtzMTV71n6Fhmbg4fYJzQsw1N29hJKO1js5ackCgIpDcGid2ETMGC6zwSYw09v05Y+oRdQ9loC54zB1La3hHQ==",
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.0.tgz",
"integrity": "sha512-UnBV3E3v4STVNQdms6jSGO2CvOkjUMdDAVR2V5N4uCMdaIkaQjbcEAMqRimDHIs4uqBYzDAKCQwCB+97tJgHQw==",
"funding": [
{
"type": "github",
@ -5737,7 +5802,7 @@
}
],
"peerDependencies": {
"@popperjs/core": "^2.11.6"
"@popperjs/core": "^2.11.7"
}
},
"node_modules/brace-expansion": {
@ -5931,6 +5996,18 @@
"node": ">=4"
}
},
"node_modules/catharsis": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz",
"integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==",
"dev": true,
"dependencies": {
"lodash": "^4.17.15"
},
"engines": {
"node": ">= 10"
}
},
"node_modules/chai": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz",
@ -6872,9 +6949,9 @@
"dev": true
},
"node_modules/csstype": {
"version": "2.6.20",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz",
"integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA=="
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
},
"node_modules/data-urls": {
"version": "2.0.0",
@ -7875,17 +7952,17 @@
"dev": true
},
"node_modules/eslint-plugin-vue": {
"version": "9.8.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.8.0.tgz",
"integrity": "sha512-E/AXwcTzunyzM83C2QqDHxepMzvI2y6x+mmeYHbVDQlKFqmKYvRrhaVixEeeG27uI44p9oKDFiyCRw4XxgtfHA==",
"version": "9.15.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.15.1.tgz",
"integrity": "sha512-CJE/oZOslvmAR9hf8SClTdQ9JLweghT6JCBQNrT2Iel1uVw0W0OLJxzvPd6CxmABKCvLrtyDnqGV37O7KQv6+A==",
"dev": true,
"dependencies": {
"eslint-utils": "^3.0.0",
"@eslint-community/eslint-utils": "^4.3.0",
"natural-compare": "^1.4.0",
"nth-check": "^2.0.1",
"postcss-selector-parser": "^6.0.9",
"semver": "^7.3.5",
"vue-eslint-parser": "^9.0.1",
"vue-eslint-parser": "^9.3.0",
"xml-name-validator": "^4.0.0"
},
"engines": {
@ -10951,6 +11028,7 @@
"version": "13.2.0",
"resolved": "https://registry.npmjs.org/jest-junit/-/jest-junit-13.2.0.tgz",
"integrity": "sha512-B0XNlotl1rdsvFZkFfoa19mc634+rrd8E4Sskb92Bb8MmSXeWV9XJGUyctunZS1W410uAxcyYuPUGVnbcOH8cg==",
"dev": true,
"dependencies": {
"mkdirp": "^1.0.4",
"strip-ansi": "^6.0.1",
@ -10965,6 +11043,7 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"dev": true,
"bin": {
"mkdirp": "bin/cmd.js"
},
@ -12562,6 +12641,65 @@
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/js2xmlparser": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz",
"integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==",
"dev": true,
"dependencies": {
"xmlcreate": "^2.0.4"
}
},
"node_modules/jsdoc": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz",
"integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==",
"dev": true,
"dependencies": {
"@babel/parser": "^7.20.15",
"@jsdoc/salty": "^0.2.1",
"@types/markdown-it": "^12.2.3",
"bluebird": "^3.7.2",
"catharsis": "^0.9.0",
"escape-string-regexp": "^2.0.0",
"js2xmlparser": "^4.0.2",
"klaw": "^3.0.0",
"markdown-it": "^12.3.2",
"markdown-it-anchor": "^8.4.1",
"marked": "^4.0.10",
"mkdirp": "^1.0.4",
"requizzle": "^0.2.3",
"strip-json-comments": "^3.1.0",
"underscore": "~1.13.2"
},
"bin": {
"jsdoc": "jsdoc.js"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/jsdoc/node_modules/escape-string-regexp": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
"integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/jsdoc/node_modules/mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"dev": true,
"bin": {
"mkdirp": "bin/cmd.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/jsdom": {
"version": "16.7.0",
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz",
@ -12692,6 +12830,15 @@
"node": ">=0.10.0"
}
},
"node_modules/klaw": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz",
"integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==",
"dev": true,
"dependencies": {
"graceful-fs": "^4.1.9"
}
},
"node_modules/kleur": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
@ -12766,6 +12913,15 @@
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"dev": true
},
"node_modules/linkify-it": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz",
"integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==",
"dev": true,
"dependencies": {
"uc.micro": "^1.0.1"
}
},
"node_modules/loader-runner": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
@ -13124,11 +13280,14 @@
}
},
"node_modules/magic-string": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
"version": "0.30.1",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.1.tgz",
"integrity": "sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA==",
"dependencies": {
"sourcemap-codec": "^1.4.8"
"@jridgewell/sourcemap-codec": "^1.4.15"
},
"engines": {
"node": ">=12"
}
},
"node_modules/make-dir": {
@ -13155,6 +13314,59 @@
"tmpl": "1.0.5"
}
},
"node_modules/markdown-it": {
"version": "12.3.2",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz",
"integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==",
"dev": true,
"dependencies": {
"argparse": "^2.0.1",
"entities": "~2.1.0",
"linkify-it": "^3.0.1",
"mdurl": "^1.0.1",
"uc.micro": "^1.0.5"
},
"bin": {
"markdown-it": "bin/markdown-it.js"
}
},
"node_modules/markdown-it-anchor": {
"version": "8.6.7",
"resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz",
"integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==",
"dev": true,
"peerDependencies": {
"@types/markdown-it": "*",
"markdown-it": "*"
}
},
"node_modules/markdown-it/node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
},
"node_modules/markdown-it/node_modules/entities": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz",
"integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==",
"dev": true,
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/marked": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz",
"integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==",
"dev": true,
"bin": {
"marked": "bin/marked.js"
},
"engines": {
"node": ">= 12"
}
},
"node_modules/maska": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/maska/-/maska-1.5.0.tgz",
@ -13166,6 +13378,12 @@
"integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
"dev": true
},
"node_modules/mdurl": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
"integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==",
"dev": true
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@ -14176,12 +14394,12 @@
}
},
"node_modules/pinia": {
"version": "2.0.22",
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.22.tgz",
"integrity": "sha512-u+b8/BC+tmvo3ACbYO2w5NfxHWFOjvvw9DQnyT0dW8aUMCPRQT5QnfZ5R5W2MzZBMTeZRMQI7V/QFbafmM9QHw==",
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.1.4.tgz",
"integrity": "sha512-vYlnDu+Y/FXxv1ABo1vhjC+IbqvzUdiUC3sfDRrRyY2CQSrqqaa+iiHmqtARFxJVqWQMCJfXx1PBvFs9aJVLXQ==",
"dependencies": {
"@vue/devtools-api": "^6.2.1",
"vue-demi": "*"
"@vue/devtools-api": "^6.5.0",
"vue-demi": ">=0.14.5"
},
"funding": {
"url": "https://github.com/sponsors/posva"
@ -14189,7 +14407,7 @@
"peerDependencies": {
"@vue/composition-api": "^1.4.0",
"typescript": ">=4.4.4",
"vue": "^2.6.14 || ^3.2.0"
"vue": "^2.6.14 || ^3.3.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
@ -14213,6 +14431,31 @@
}
}
},
"node_modules/pinia/node_modules/vue-demi": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.5.tgz",
"integrity": "sha512-o9NUVpl/YlsGJ7t+xuqJKx8EBGf1quRhCiT6D/J0pfwmk9zUwYkC7yrF4SZCe6fETvSM3UNL2edcbYrSyc4QHA==",
"hasInstallScript": true,
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/pirates": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz",
@ -15315,6 +15558,15 @@
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
"dev": true
},
"node_modules/requizzle": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz",
"integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==",
"dev": true,
"dependencies": {
"lodash": "^4.17.21"
}
},
"node_modules/resolve": {
"version": "1.22.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
@ -15863,6 +16115,7 @@
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@ -15885,12 +16138,6 @@
"source-map": "^0.6.0"
}
},
"node_modules/sourcemap-codec": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
"deprecated": "Please use @jridgewell/sourcemap-codec instead"
},
"node_modules/spdx-correct": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
@ -16114,6 +16361,7 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
@ -16702,6 +16950,12 @@
"is-typedarray": "^1.0.0"
}
},
"node_modules/uc.micro": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
"dev": true
},
"node_modules/ufo": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.1.2.tgz",
@ -16723,6 +16977,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/underscore": {
"version": "1.13.6",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz",
"integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==",
"dev": true
},
"node_modules/unicode-canonical-property-names-ecmascript": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
@ -16851,6 +17111,7 @@
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"dev": true,
"bin": {
"uuid": "dist/bin/uuid"
}
@ -17056,59 +17317,22 @@
}
}
},
"node_modules/vitest/node_modules/magic-string": {
"version": "0.30.0",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz",
"integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==",
"dev": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.13"
},
"engines": {
"node": ">=12"
}
},
"node_modules/vue": {
"version": "3.2.38",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.38.tgz",
"integrity": "sha512-hHrScEFSmDAWL0cwO4B6WO7D3sALZPbfuThDsGBebthrNlDxdJZpGR3WB87VbjpPh96mep1+KzukYEhpHDFa8Q==",
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.3.4.tgz",
"integrity": "sha512-VTyEYn3yvIeY1Py0WaYGZsXnz3y5UnGi62GjVEqvEGPl6nxbOrCXbVOTQWBEJUqAyTUk2uJ5JLVnYJ6ZzGbrSw==",
"dependencies": {
"@vue/compiler-dom": "3.2.38",
"@vue/compiler-sfc": "3.2.38",
"@vue/runtime-dom": "3.2.38",
"@vue/server-renderer": "3.2.38",
"@vue/shared": "3.2.38"
}
},
"node_modules/vue-demi": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
"integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
"hasInstallScript": true,
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
"@vue/compiler-dom": "3.3.4",
"@vue/compiler-sfc": "3.3.4",
"@vue/runtime-dom": "3.3.4",
"@vue/server-renderer": "3.3.4",
"@vue/shared": "3.3.4"
}
},
"node_modules/vue-eslint-parser": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.1.0.tgz",
"integrity": "sha512-NGn/iQy8/Wb7RrRa4aRkokyCZfOUWk19OP5HP6JEozQFX5AoS/t+Z0ZN7FY4LlmWc4FNI922V7cvX28zctN8dQ==",
"version": "9.3.1",
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.3.1.tgz",
"integrity": "sha512-Clr85iD2XFZ3lJ52/ppmUDG/spxQu6+MAeHXjjyI4I1NUYZ9xmenQp4N0oaHJhrA8OOxltCVxMRfANGa70vU0g==",
"dev": true,
"dependencies": {
"debug": "^4.3.4",
@ -17130,9 +17354,9 @@
}
},
"node_modules/vue-eslint-parser/node_modules/eslint-scope": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
"integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.1.tgz",
"integrity": "sha512-CvefSOsDdaYYvxChovdrPo/ZGt8d5lrJWleAc1diXRKhHGiTYEI26cvo8Kle/wGnsizoCJjK73FMg1/IkIwiNA==",
"dev": true,
"dependencies": {
"esrecurse": "^4.3.0",
@ -17140,12 +17364,15 @@
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/vue-eslint-parser/node_modules/semver": {
"version": "7.3.8",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
"integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
@ -18021,7 +18248,8 @@
"node_modules/xml": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz",
"integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw=="
"integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==",
"dev": true
},
"node_modules/xml-name-validator": {
"version": "3.0.0",
@ -18035,6 +18263,12 @@
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
"dev": true
},
"node_modules/xmlcreate": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz",
"integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==",
"dev": true
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",

View file

@ -2,6 +2,12 @@
"name": "digitalconsumer.iss",
"version": "0.1.0",
"private": true,
"eslintConfig": {
"env": {
"browser": true,
"es2023": true
}
},
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
@ -9,11 +15,8 @@
"test:unit:lite": "vue-cli-service test:unit --ci"
},
"dependencies": {
"@pinia/testing": "0.0.14",
"@popperjs/core": "^2.10.2",
"axios": "^0.27.2",
"bootstrap": "^5.2.2",
"jest-junit": "^13.0.0",
"bootstrap": "^5.2.3",
"maska": "^1.5.0",
"pinia": "^2.0.22",
"pinia-plugin-persistedstate": "^2.2.0",
@ -23,6 +26,7 @@
"vue-router": "4.1.3"
},
"devDependencies": {
"@pinia/testing": "0.1.2",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/user-event": "14.4.3",
"@testing-library/vue": "6.6.1",
@ -37,8 +41,10 @@
"eslint-config-airbnb-base": "15.0.0",
"eslint-import-resolver-alias": "1.1.2",
"eslint-plugin-import": "2.26.0",
"eslint-plugin-vue": "9.8.0",
"eslint-plugin-vue": "^9.15.1",
"jest": "^27.0.5",
"jest-junit": "^13.0.0",
"jsdoc": "^4.0.2",
"sass": "^1.32.7",
"sass-loader": "^12.0.0",
"vitest": "^0.32.4"

View file

@ -27,7 +27,7 @@ const customMappings = {
// If the value isn't found, return the original dynamic string without replacement
export function getCustomTransformValue(dynamicString, key) {
// Get array key from the dynamic string
const regexExp = new RegExp('{(.*?):(.*?)}', 'g');
const regexExp = /{(.*?):(.*?)}/g;
const matches = [...dynamicString.matchAll(regexExp)];
if (matches.length === 0) {

View file

@ -86,9 +86,8 @@ export function getTintImage(glassLocation, colorString) {
return '';
}
const tintImageSource = tintMap[glassLocation.toLowerCase()].find(
(item) => item.name.toLowerCase() == colorString.toLowerCase()
);
const tintImageSource = tintMap[glassLocation.toLowerCase()]
.find((item) => item.name.toLowerCase() === colorString.toLowerCase());
return tintImageSource;
}

View file

@ -7,9 +7,9 @@
@focusout="handleBlur"
@mousedown.left="handleEventAction(eventTypes.CLICK, $event)">
<input
:type="inputType"
:id="buttonId"
:key="buttonId"
:type="inputType"
:name="groupName"
:class="inputClasses"
:aria-required="isRequired"
@ -39,11 +39,61 @@ export default {
buttonWrapperClasses: [String, Array, Object],
inputClasses: [String, Array, Object]
},
emits: ['update:modelValue'],
setup(props) {
const inputType = props.isMultiSelect ? 'checkbox' : 'radio';
const fieldOptions = {
type: inputType,
validateOnValueUpdate: false,
validateOnMount: false
};
const { handleChange, meta, errors }
= useField(toRef(props, 'groupName'),
toRef(props, 'validationRules'),
fieldOptions);
return {
handleChange,
errors,
meta,
fieldOptions // only need to expose this for unit test purposes
};
},
data() {
return {
valueToEmit: null
};
},
computed: {
isChecked() {
if (this.isMultiSelect && this.modelValue instanceof Array) {
return this.modelValue.includes(this.value);
}
if (!this.isMultiSelect) {
return this.modelValue === this.value;
}
return false;
},
inputType() {
return this.isMultiSelect ? 'checkbox' : 'radio';
},
buttonId() {
return `${this.groupName}-${this.value}`.replaceAll(' ', '-');
},
isValueSelectedOnClick() {
return this.isMultiSelect || this.selectingInitiatesLoad;
},
eventTypes() {
return {
CHANGE: 'change',
ENTER: 'enter',
SPACE: 'space',
CLICK: 'click'
};
}
},
mounted() {
if (this.isChecked) {
this.handleChange(this.modelValue);
@ -56,6 +106,7 @@ export default {
case this.eventTypes.CHANGE:
this.handleClick(e);
break;
default:
}
} else {
switch (eventType) {
@ -65,19 +116,21 @@ export default {
this.handleClick(e);
break;
case this.eventTypes.CHANGE:
// eslint-disable-next-line no-unused-expressions
this.selectingInitiatesLoad
? this.handleSelectionChange(e)
: this.handleClick(e);
break;
default:
}
}
},
handleSelectionChange(e) {
handleSelectionChange() {
if (
this.isMultiSelect &&
(this.modelValue instanceof Array || this.modelValue == null)
this.isMultiSelect
&& (this.modelValue instanceof Array || this.modelValue == null)
) {
let newValue = this.modelValue ? [...this.modelValue] : [];
const newValue = this.modelValue ? [...this.modelValue] : [];
if (!newValue.includes(this.value)) {
newValue.push(this.value);
} else {
@ -106,56 +159,6 @@ export default {
onButtonQuestionLostFocusCallback: this.handlePushClickEventToGACheck
});
}
},
computed: {
isChecked() {
if (this.isMultiSelect && this.modelValue instanceof Array) {
return this.modelValue.includes(this.value);
} else if (!this.isMultiSelect) {
return this.modelValue == this.value;
} else {
return false;
}
},
inputType() {
return this.isMultiSelect ? 'checkbox' : 'radio';
},
buttonId() {
return `${this.groupName}-${this.value}`.replaceAll(' ', '-');
},
isValueSelectedOnClick() {
return this.isMultiSelect || this.selectingInitiatesLoad;
},
eventTypes() {
return {
CHANGE: 'change',
ENTER: 'enter',
SPACE: 'space',
CLICK: 'click'
};
}
},
setup(props) {
const inputType = props.isMultiSelect ? 'checkbox' : 'radio';
const fieldOptions = {
type: inputType,
validateOnValueUpdate: false,
validateOnMount: false
};
const { handleChange, meta, errors } = useField(
toRef(props, 'groupName'),
toRef(props, 'validationRules'),
fieldOptions
);
return {
handleChange,
errors,
meta,
fieldOptions // only need to expose this for unit test purposes
};
}
};
</script>

View file

@ -5,13 +5,13 @@ export const inputButtonProps = {
},
modelValue: {
required: true,
validator: function(prop) {
validator(prop) {
return (
prop === null
|| Array.isArray(prop)
|| typeof prop === 'string'
|| (typeof prop === 'number' && !Number.isNaN(prop))
)
);
}
},
isMultiSelect: Boolean,

View file

@ -51,10 +51,8 @@ describe('buttonQuestion.vue', () => {
describe('selectedValues', () => {
test('is radio => should emit captured value', async () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
setupMocks({ propsData: { groupName: 'group-name' } })
);
const wrapper = shallowMount(buttonQuestion,
setupMocks({ propsData: { groupName: 'group-name' } }));
await wrapper.setProps({
answers: ['2022', '2021', '2020'],
isMultiSelect: false,
@ -69,10 +67,8 @@ describe('buttonQuestion.vue', () => {
});
test('is checkbox => should emit captured value', async () => {
const wrapper = shallowMount(
buttonQuestion,
setupMocks({ propsData: { groupName: 'group-name' } })
);
const wrapper = shallowMount(buttonQuestion,
setupMocks({ propsData: { groupName: 'group-name' } }));
await wrapper.setProps({
answers: ['2022', '2021', '2020'],
isMultiSelect: false,
@ -97,8 +93,7 @@ describe('buttonQuestion.vue', () => {
describe('buttonLabel', () => {
test('answers have buttonLabel properties => buttonsInfo buttonsLabel properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
answers: [
@ -110,11 +105,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].buttonLabel).toEqual('Label 1');
@ -123,8 +117,7 @@ describe('buttonQuestion.vue', () => {
test('answers have Text properties, no buttonLabel properties => buttonsInfo buttonsLabel properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
answers: [
@ -136,11 +129,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].buttonLabel).toEqual('Label 1');
@ -149,8 +141,7 @@ describe('buttonQuestion.vue', () => {
test('answers have buttonLabel and Text properties => buttonsInfo buttonsLabel properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
answers: [
@ -164,11 +155,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].buttonLabel).toEqual('buttonLabel 1');
@ -177,17 +167,15 @@ describe('buttonQuestion.vue', () => {
test('answers is an array of strings => buttonLabel is answer values', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
answers: ['answer 1', 'answer 2']
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].buttonLabel).toEqual('answer 1');
@ -198,8 +186,7 @@ describe('buttonQuestion.vue', () => {
describe('altText', () => {
test('answers have altText properties => buttonsInfo altText properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
answers: [
@ -211,11 +198,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].altText).toEqual('altText 1');
@ -224,8 +210,7 @@ describe('buttonQuestion.vue', () => {
test('answers have Name properties, no buttonLabel properties => buttonsInfo altText properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
answers: [
@ -237,11 +222,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].altText).toEqual('Name 1');
@ -250,8 +234,7 @@ describe('buttonQuestion.vue', () => {
test('answers have altText and Name properties => buttonsInfo altText properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
answers: [
@ -265,11 +248,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].altText).toEqual('altText 1');
@ -278,17 +260,15 @@ describe('buttonQuestion.vue', () => {
test('answers is an array of strings => altText is answer values', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
answers: ['answer 1', 'answer 2']
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].altText).toEqual('answer 1');
@ -299,8 +279,7 @@ describe('buttonQuestion.vue', () => {
describe('buttonLabelSubCopy', () => {
test('answers have buttonLabelSubCopy properties => buttonsInfo buttonLabelSubCopy properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
answers: [
@ -312,11 +291,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].buttonLabelSubCopy).toEqual('buttonLabelSubCopy 1');
@ -325,8 +303,7 @@ describe('buttonQuestion.vue', () => {
test('answers have SubText properties, no buttonLabelSubCopy properties => buttonsInfo buttonLabelSubCopy properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
answers: [
@ -338,11 +315,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].buttonLabelSubCopy).toEqual('SubText 1');
@ -351,8 +327,7 @@ describe('buttonQuestion.vue', () => {
test('answers have buttonLabelSubCopy and SubText properties => buttonsInfo buttonLabelSubCopy properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
answers: [
@ -366,11 +341,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].buttonLabelSubCopy).toEqual('buttonLabelSubCopy 1');
@ -379,17 +353,15 @@ describe('buttonQuestion.vue', () => {
test('answers is an array of strings => there are no buttonLabelSubCopy properties', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
answers: ['answer 1', 'answer 2']
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].buttonLabelSubCopy).toBe(undefined);
@ -400,8 +372,7 @@ describe('buttonQuestion.vue', () => {
describe('buttonImage', () => {
test('answers have buttonImage properties => buttonsInfo buttonImage properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
answers: [
@ -413,11 +384,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].buttonImage).toEqual('buttonImage 1');
@ -426,8 +396,7 @@ describe('buttonQuestion.vue', () => {
test('answers have AnswerImageUrl properties, no buttonImage properties => buttonsInfo buttonImage properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
answers: [
@ -439,11 +408,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].buttonImage).toEqual('AnswerImageUrl 1');
@ -452,8 +420,7 @@ describe('buttonQuestion.vue', () => {
test('answers have buttonImage and AnswerImageUrl properties => buttonsInfo buttonImage properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
answers: [
@ -467,11 +434,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].buttonImage).toEqual('buttonImage 1');
@ -480,17 +446,15 @@ describe('buttonQuestion.vue', () => {
test('answers is an array of strings => there are no buttonImage properties', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
answers: ['answer 1', 'answer 2']
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].buttonImage).toBe(undefined);
@ -501,8 +465,7 @@ describe('buttonQuestion.vue', () => {
describe('buttonImageId', () => {
test('answers have buttonImageId properties => buttonsInfo buttonImageId properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
answers: [
@ -514,11 +477,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].buttonImageId).toEqual('buttonImageId 1');
@ -527,8 +489,7 @@ describe('buttonQuestion.vue', () => {
test('answers have ImageId properties, no buttonImageId properties => buttonsInfo buttonImage properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
answers: [
@ -540,11 +501,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].buttonImageId).toEqual('ImageId 1');
@ -553,8 +513,7 @@ describe('buttonQuestion.vue', () => {
test('answers have buttonImageId and ImageId properties => buttonsInfo buttonImageId properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
answers: [
@ -568,11 +527,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].buttonImageId).toEqual('buttonImageId 1');
@ -581,17 +539,15 @@ describe('buttonQuestion.vue', () => {
test('answers is an array of strings => there are no buttonImageId properties', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
answers: ['answer 1', 'answer 2']
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].buttonImageId).toBe(undefined);
@ -601,59 +557,50 @@ describe('buttonQuestion.vue', () => {
describe('groupName', () => {
const answers = [[['answer 1', 'answer 2']], [[{ value: 1 }, { value: 2 }]]];
test.each(answers)(
'answers have groupName properties with spaces => buttonsInfo groupName properties are correct',
test.each(answers)('answers have groupName properties with spaces => buttonsInfo groupName properties are correct',
(answerGroup) => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
answers: answerGroup,
groupName: 'this is my group name'
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].groupName).toEqual('this-is-my-group-name');
expect(buttonsInfo[1].groupName).toEqual('this-is-my-group-name');
}
);
});
test.each(answers)(
'answers have groupName properties with no spaces => buttonsInfo groupName properties are correct',
test.each(answers)('answers have groupName properties with no spaces => buttonsInfo groupName properties are correct',
(answerGroup) => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
answers: answerGroup,
groupName: 'this-is-my-group-name'
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].groupName).toEqual('this-is-my-group-name');
expect(buttonsInfo[1].groupName).toEqual('this-is-my-group-name');
}
);
});
});
describe('value', () => {
describe('useTextForValue is true', () => {
test('answers have value properties => buttonsInfo value properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
useTextForValue: true,
@ -666,11 +613,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].value).toEqual('value 1');
@ -679,8 +625,7 @@ describe('buttonQuestion.vue', () => {
test('answers have Text properties => buttonsInfo value properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
useTextForValue: true,
@ -693,11 +638,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].value).toEqual('Text 1');
@ -706,8 +650,7 @@ describe('buttonQuestion.vue', () => {
test('answers have Name properties => buttonsInfo value properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
useTextForValue: true,
@ -720,11 +663,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].value).toEqual('Name 1');
@ -733,8 +675,7 @@ describe('buttonQuestion.vue', () => {
test('answers have value and Text properties, no Name properties => buttonsInfo value properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
useTextForValue: true,
@ -749,11 +690,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].value).toEqual('value 1');
@ -762,8 +702,7 @@ describe('buttonQuestion.vue', () => {
test('answers have value and Name properties, no Text properties => buttonsInfo value properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
useTextForValue: true,
@ -778,11 +717,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].value).toEqual('value 1');
@ -791,8 +729,7 @@ describe('buttonQuestion.vue', () => {
test('answers have Text and Name properties, no value properties => buttonsInfo value properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
useTextForValue: true,
@ -807,11 +744,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].value).toEqual('Text 1');
@ -820,8 +756,7 @@ describe('buttonQuestion.vue', () => {
test('answers have value, Text, and Name properties => buttonsInfo value properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
useTextForValue: true,
@ -838,11 +773,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].value).toEqual('value 1');
@ -851,18 +785,16 @@ describe('buttonQuestion.vue', () => {
test('answers is an array of strings => buttonInfo value property values are values from array', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
useTextForValue: true,
answers: ['answer 1', 'answer 2']
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].value).toEqual('answer 1');
@ -873,8 +805,7 @@ describe('buttonQuestion.vue', () => {
describe('useTextForValue is false', () => {
test('answers have value properties => buttonsInfo value properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
useTextForValue: false,
@ -887,11 +818,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].value).toEqual('value 1');
@ -900,8 +830,7 @@ describe('buttonQuestion.vue', () => {
test('answers have Text properties => buttonsInfo value properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
useTextForValue: false,
@ -914,11 +843,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].value).toBe(null);
@ -927,8 +855,7 @@ describe('buttonQuestion.vue', () => {
test('answers have Name properties => buttonsInfo value properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
useTextForValue: false,
@ -941,11 +868,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].value).toEqual('Name 1');
@ -954,8 +880,7 @@ describe('buttonQuestion.vue', () => {
test('answers have value and Text properties, no Name properties => buttonsInfo value properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
useTextForValue: false,
@ -970,11 +895,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].value).toEqual('value 1');
@ -983,8 +907,7 @@ describe('buttonQuestion.vue', () => {
test('answers have value and Name properties, no Text properties => buttonsInfo value properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
useTextForValue: false,
@ -999,11 +922,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].value).toEqual('value 1');
@ -1012,8 +934,7 @@ describe('buttonQuestion.vue', () => {
test('answers have Text and Name properties, no value properties => buttonsInfo value properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
useTextForValue: false,
@ -1028,11 +949,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].value).toEqual('Name 1');
@ -1041,8 +961,7 @@ describe('buttonQuestion.vue', () => {
test('answers have value, Text, and Name properties => buttonsInfo value properties are correct', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
useTextForValue: false,
@ -1059,11 +978,10 @@ describe('buttonQuestion.vue', () => {
}
]
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].value).toEqual('value 1');
@ -1072,18 +990,16 @@ describe('buttonQuestion.vue', () => {
test('answers is an array of strings => buttonInfo value property values are values from array', () => {
// Arrange
const wrapper = shallowMount(
buttonQuestion,
const wrapper = shallowMount(buttonQuestion,
setupMocks({
propsData: {
useTextForValue: false,
answers: ['answer 1', 'answer 2']
}
})
);
}));
// Act
const buttonsInfo = wrapper.vm.buttonsInfo;
const { buttonsInfo } = wrapper.vm;
// Assert
expect(buttonsInfo[0].value).toEqual('answer 1');
@ -1096,9 +1012,8 @@ describe('buttonQuestion.vue', () => {
function setupMocks(mountOptionsMockData = {}) {
const defaultMountOptions = { route: { query: { issPage: 'page-name' } } };
const baseMountOptions = getMountOptions(
Object.assign(defaultMountOptions, mountOptionsMockData)
);
const baseMountOptions
= getMountOptions(Object.assign(defaultMountOptions, mountOptionsMockData));
const allMountOptions = Object.assign(defaultMountOptions, baseMountOptions);
return allMountOptions;

View file

@ -1,14 +1,15 @@
<!-- Documented in confluence https://safelite.atlassian.net/wiki/spaces/DC/pages/76644418/Button+Question+Component -->
<template>
<div
:class="
isOverflowScrollable ? 'button-question button-question-overflow' : 'button-question'
">
<div v-if="questionText && answers && answers.length > 0" class="question-text d-flex"
:class="isOverflowScrollable
? 'button-question button-question-overflow'
: 'button-question'">
<div
v-if="questionText && answers && answers.length > 0"
class="question-text d-flex"
:class="{'small-question-text': isSmallQuestionText,'small-question-label-text':isSmallQuestionLabelText}">
<span class="fw-bold w-100">{{ questionText }}</span>
</div>
<div class="w-100 d-flex justify-content-center">
<fieldset
class="w-100"
@ -16,24 +17,23 @@
:role="isMultiSelect ? 'group' : 'radiogroup'"
:aria-labelledby="formatString(groupName)">
<legend
:id="formatString(groupName)"
class="sr-only"
:data-focus-target="formatString(groupName)"
tabindex="-1"
:id="formatString(groupName)">
tabindex="-1">
{{ questionText }}
{{
isMultiSelect && answers && answers.length > 1
{{ isMultiSelect && answers && answers.length > 1
? "Select one or more options below."
: "Select an option below."
}}
: "Select an option below." }}
</legend>
<div :class="getComponentLoopWrapperClasses">
<div
:class="getComponentWrapperClasses"
v-for="answer in buttonsInfo"
:key="answer.value ?? answer">
:key="answer.value ?? answer"
:class="getComponentWrapperClasses">
<component
:is="buttonTypeString"
v-model="selectedValues"
:buttonLabel="answer.buttonLabel"
:buttonLabelSubCopy="answer.buttonLabelSubCopy"
:buttonBodyCopy="answer.buttonBodyCopy"
@ -51,15 +51,14 @@
:additionalButtonStyling="additionalButtonStyling"
:lastValuePushedToGa="lastValuePushedToGa"
:setLastValuePushedToGa="setLastValuePushedToGa"
:suppressError="suppressError"
v-model="selectedValues" />
:suppressError="suppressError" />
<!-- For nested questions -->
<transition name="fade" mode="out-in">
<transition
name="fade"
mode="out-in">
<div
v-if="
typeof selectedValues == 'string' &&
selectedValues == answer.value
">
v-if="typeof selectedValues == 'string' &&
selectedValues == answer.value">
<slot></slot>
</div>
</transition>
@ -67,10 +66,11 @@
</div>
</fieldset>
</div>
<div v-if="!suppressError" class="row form-test-error mt-1">
<div
v-if="!suppressError"
class="row form-test-error mt-1">
<error-message
:name="formatString(groupName)"
></error-message>
:name="formatString(groupName)"></error-message>
</div>
</div>
</template>
@ -81,10 +81,18 @@ import listButtonHorizontal from '@/ux-components/list-button-horizontal/list-bu
import listCard from '@/ux-components/list-card/list-card';
import radio from '@/ux-components/radio/radio';
import { ErrorMessage } from 'vee-validate';
import providerPrefRadio from '@/layouts/provider-preference/provider-pref-radio/provider-pref-radio.vue'
import providerPrefRadio from '@/layouts/provider-preference/provider-pref-radio/provider-pref-radio.vue';
export default {
name: 'buttonQuestion',
name: 'button-question',
components: {
listButton,
listButtonHorizontal,
listCard,
ErrorMessage,
radio,
providerPrefRadio
},
props: {
buttonTypeString: {
type: String,
@ -125,11 +133,7 @@ export default {
isSmallQuestionText: Boolean,
isSmallQuestionLabelText: Boolean
},
beforeMount() {
if (this.buttonTypeObject) {
this.$options.components[this.buttonTypeString] = this.buttonTypeObject;
}
},
emits: ['update:modelValue'],
data() {
return {
lastValuePushedToGa: null
@ -137,18 +141,19 @@ export default {
},
computed: {
getFieldSetClasses() {
// eslint-disable-next-line no-nested-ternary
const baseClasses = this.isOverflowScrollable
? 'container-fluid overflow-scroll position-absolute px-5 pt-1 py-0'
: this.buttonTypeString == 'listCard'
: this.buttonTypeString === 'listCard'
? 'w-100'
: '';
const SmallQuestionTextClass = this.isSmallQuestionText
? baseClasses + 'small-question-text'
? `${baseClasses}small-question-text`
: baseClasses;
const SmallQuestionLabelText = this.isSmallQuestionLabelText
? SmallQuestionTextClass + 'small-question-label-text'
? `${SmallQuestionTextClass}small-question-label-text`
: SmallQuestionTextClass;
return SmallQuestionLabelText;
},
@ -176,6 +181,7 @@ export default {
case 'providerPrefRadio':
classes = 'option-main';
break;
default:
}
return classes;
},
@ -211,9 +217,9 @@ export default {
buttonImageId: answer.buttonImageId ?? answer.ImageId,
groupName: this.formatString(this.groupName),
value:
answer.value ??
(this.useTextForValue && answer.Text ? answer.Text : answer.Name) ??
(typeof answer !== 'object' ? answer : null)
answer.value
?? (this.useTextForValue && answer.Text ? answer.Text : answer.Name)
?? (typeof answer !== 'object' ? answer : null)
}));
},
selectedValues: {
@ -225,6 +231,11 @@ export default {
}
}
},
beforeMount() {
if (this.buttonTypeObject) {
this.$options.components[this.buttonTypeString] = this.buttonTypeObject;
}
},
methods: {
formatString(str) {
return String(str).replaceAll(' ', '-');
@ -232,19 +243,11 @@ export default {
setLastValuePushedToGa(lastValuePushedToGa) {
this.lastValuePushedToGa = lastValuePushedToGa;
}
},
components: {
listButton,
listButtonHorizontal,
listCard,
ErrorMessage,
radio,
providerPrefRadio
}
};
</script>
<style lang="scss">
<style lang="scss" scoped>
.button-question {
color: $black;

View file

@ -5,9 +5,7 @@ import dropdownQuestion from './dropdown-question';
const questionText = 'Question Text';
const mockMixin = {
methods: {
getCmsContent: jest.fn().mockImplementation(() => {
return questionText;
})
getCmsContent: jest.fn().mockImplementation(() => questionText)
}
}
@ -59,8 +57,10 @@ describe('dropdownQuestion.vue', () => {
});
// Mock CMS content ...
// Trust me, the below instance of the string "Question Text" actually has the &NoBreak; in it. You just can't see it
// Don't believe me? Copy and paste it into Google. Then inspect the search field element in Dev Tools,
// Trust me, the below instance of the string "Question Text"
// actually has the & NoBreak; in it.You just can't see it
// Don't believe me? Copy and paste it into Google.
// Then inspect the search field element in Dev Tools,
// you will see "Q&NoBreak;uestion T&NoBreak;ext"
const expectedQuestionText = 'Question Text';
@ -135,7 +135,7 @@ describe('dropdownQuestion.vue', () => {
await wrapper.find('select').setValue('val2');
// Assert
expect(wrapper.emitted()).toHaveProperty('change')
expect(wrapper.emitted()).toHaveProperty('change');
});
it('Should call this.handleChange with new value when selectedOption is changed', async () => {

View file

@ -1,26 +1,38 @@
<template>
<div class="dropdown-question" :class="(errors && errors.length) || hasError ? 'has-error' : ''">
<div
class="dropdown-question"
:class="(errors && errors.length) || hasError ? 'has-error' : ''">
<label
:for="inputId"
:aria-label="questionText"
class="form-label"
v-html="labelText"></label>
<select
:id="inputId"
v-model="selectedOption"
class="form-select"
:id="inputId"
:name="inputId"
:aria-disabled="isDisabled"
:disabled="isDisabled"
:aria-required="isRequired"
:validationRules="validationRules"
:placeHolderText="placeHolderText">
<option v-if="placeHolderText" value="" selected>{{ placeHolderText }}</option>
<option v-for="(value, name, index) in options" :value="name" :key="index">
<option
v-if="placeHolderText"
value=""
selected>
{{ placeHolderText }}
</option>
<option
v-for="(value, name, index) in options"
:key="index"
:value="name">
{{ value }}
</option>
</select>
<div v-show="errorMessage" class="row mt-1 form-test-error">
<div
v-show="errorMessage"
class="row mt-1 form-test-error">
<span role="alert">{{ errorMessage }}</span>
</div>
</div>
@ -47,8 +59,8 @@ export default {
placeHolderText: String
},
setup(props) {
const propsClone = Object.assign({}, props);
const modelValue = propsClone.modelValue;
const propsClone = { ...props };
const { modelValue } = propsClone;
let initialValue;
switch (typeof modelValue) {
@ -63,14 +75,12 @@ export default {
const fieldOptions = {
type: 'select',
value: props.modelValue,
initialValue: initialValue
initialValue
};
const {errorMessage, handleBlur, handleChange, meta, errors } = useField(
props.inputId,
const { errorMessage, handleBlur, handleChange, meta, errors } = useField(props.inputId,
props.validationRules,
fieldOptions
);
fieldOptions);
return {
errorMessage,
@ -85,20 +95,20 @@ export default {
return this.getCmsContent(this.cmsWidgetName, 'QuestionText');
},
selectedOption: {
get: function () {
get() {
return !this.modelValue ? '' : this.modelValue;
},
set: function (newValue) {
set(newValue) {
this.$emit('update:modelValue', newValue);
}
},
labelText: {
get: function () {
get() {
const noBreakChar = '&NoBreak;';
var questionText = '';
let questionText = '';
if (this.disableAutoFill) {
var words = this.questionText.toString().split(/[ ]+/);
words.forEach(function (word) {
const words = this.questionText.toString().split(/[ ]+/);
words.forEach((word) => {
const position = 1;
word = [
word.toString().slice(0, position),
@ -125,7 +135,7 @@ export default {
};
</script>
<style lang="scss">
<style lang="scss" scoped>
.dropdown-question {
label {
color: $black;

View file

@ -1,3 +1,9 @@
import { shallowMount } from '@vue/test-utils';
import crypto from 'crypto';
import { useForm } from 'vee-validate';
import { Modal } from 'bootstrap';
import modal from './modal';
jest.mock('vee-validate', () => ({
useForm: jest.fn()
}));
@ -5,12 +11,6 @@ jest.mock('vee-validate', () => ({
const mockValidate = (returnValue) => jest.fn(async () => Promise.resolve({ valid: returnValue }));
const mockMeta = (returnValue) => jest.fn(async () => Promise.resolve(returnValue));
import { shallowMount } from '@vue/test-utils';
import modal from './modal';
import crypto from 'crypto';
import { useForm } from 'vee-validate';
import { Modal } from 'bootstrap';
const footerButtonText = 'Sample footer text here.';
const headerText = 'Sample header text here.';
@ -33,8 +33,8 @@ describe('modal.vue', () => {
const wrapper = shallowMount(modal, {
props: {
headerText: headerText,
footerButtonText: footerButtonText
headerText,
footerButtonText
},
attachTo: document.body
});
@ -58,7 +58,7 @@ describe('modal.vue', () => {
});
const wrapper = shallowMount(modal, {
props: {
footerButtonText: footerButtonText
footerButtonText
},
attachTo: document.body
});
@ -84,8 +84,8 @@ describe('modal.vue', () => {
const resetButtonStyle = jest.fn();
const wrapper = shallowMount(modal, {
props: {
footerButtonText: footerButtonText,
headerText: headerText
footerButtonText,
headerText
},
attachTo: document.body
});
@ -116,8 +116,8 @@ describe('modal.vue', () => {
const resetButtonStyle = jest.fn();
const wrapper = shallowMount(modal, {
props: {
footerButtonText: footerButtonText,
headerText: headerText
footerButtonText,
headerText
},
attachTo: document.body
});
@ -148,8 +148,8 @@ describe('modal.vue', () => {
const resetButtonStyle = jest.fn();
const wrapper = shallowMount(modal, {
props: {
footerButtonText: footerButtonText,
headerText: headerText
footerButtonText,
headerText
},
attachTo: document.body
});
@ -176,8 +176,8 @@ describe('modal.vue', () => {
const resetButtonStyle = jest.fn();
const wrapper = shallowMount(modal, {
props: {
footerButtonText: footerButtonText,
headerText: headerText
footerButtonText,
headerText
},
attachTo: document.body
});
@ -206,8 +206,8 @@ describe('modal.vue', () => {
const resetButtonStyle = jest.fn();
const wrapper = shallowMount(modal, {
props: {
footerButtonText: footerButtonText,
headerText: headerText
footerButtonText,
headerText
},
attachTo: document.body
});
@ -238,8 +238,8 @@ describe('modal.vue', () => {
const resetButtonStyle = jest.fn();
const wrapper = shallowMount(modal, {
props: {
footerButtonText: footerButtonText,
headerText: headerText
footerButtonText,
headerText
},
attachTo: document.body
});

View file

@ -1,12 +1,12 @@
<template>
<!-- Modal -->
<div
class="modal fade modal-component"
v-on="{ 'hidden.bs.modal': onModalClosed, 'shown.bs.modal': onModalOpened }"
:id="modalId"
class="modal fade modal-component"
tabindex="-1"
aria-labelledby="ModalComponentLabel"
aria-hidden="true">
aria-hidden="true"
v-on="{ 'hidden.bs.modal': onModalClosed, 'shown.bs.modal': onModalOpened }">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header mb-2 mt-2">
@ -19,22 +19,22 @@
ref="closeButton"
type="button"
class="btn-close"
aria-label="Close"
@mousedown="closeModal"
@keypress.space="closeModal"
aria-label="Close"></button>
@keypress.space="closeModal"></button>
</div>
<div class="modal-body ps-5 pe-5 pb-5 pt-0">
<slot></slot>
</div>
<div class="modal-footer px-5 py-4">
<modalButtonMain
ref="modalButtonMain"
isPrimary
class="w-100"
ref="modalButtonMain"
loaderColor="white"
:buttonText="footerButtonText"
@click-event="validateAndEmit"
:class="(isButtonDisabled || isFooterButtonDisabled) && 'form-test-invalid'" />
:class="(isButtonDisabled || isFooterButtonDisabled) && 'form-test-invalid'"
@click-event="validateAndEmit" />
</div>
</div>
</div>
@ -48,6 +48,9 @@ import { useForm } from 'vee-validate';
export default {
name: 'modal',
components: {
modalButtonMain
},
props: {
modalId: String,
headerText: String,
@ -60,6 +63,7 @@ export default {
},
isButtonDisabled: Boolean
},
emits: ['footer-button-event'],
setup(props) {
const modalId = props.modalId ? props.modalId : `modal-${crypto.randomUUID()}`;
@ -72,6 +76,14 @@ export default {
resetForm
};
},
computed: {
isFooterButtonDisabled() {
if (!this.meta.touched) {
return !this.meta.valid;
}
return !this.meta.dirty || !this.meta.valid;
}
},
methods: {
async validateAndEmit() {
const validationResult = await this.validate();
@ -99,22 +111,11 @@ export default {
const modal = Modal.getInstance(document.getElementById(this.modalId));
modal?.hide();
}
},
computed: {
isFooterButtonDisabled() {
if (!this.meta.touched) {
return !this.meta.valid;
}
return !this.meta.dirty || !this.meta.valid;
}
},
components: {
modalButtonMain
}
};
</script>
<style lang="scss">
<style lang="scss" scoped>
.modal {
overflow: hidden;
top: auto;

View file

@ -1,5 +1,5 @@
import { shallowMount } from '@vue/test-utils';
import questionChain from '@/digital-components/question-chain/question-chain.vue';
import questionChain from '@/digital-components/question-chain/question-chain';
import { getMountOptions } from '@/helpers/unit-test-helper.js';
import { nextTick } from 'vue';
@ -306,7 +306,7 @@ function setupMocks({
mountOptions.propsData = {
questionData: questionDataProp
};
mountOptions['attachTo'] = document.body;
mountOptions.attachTo = document.body;
const wrapper = shallowMount(questionChain, mountOptions);

View file

@ -1,6 +1,12 @@
<template>
<div v-for="(q, i) in questions" :key="i" class="questionBlock" >
<transition appear name="fade" mode="out-in">
<div
v-for="(q, i) in questions"
:key="i"
class="questionBlock">
<transition
appear
name="fade"
mode="out-in">
<buttonQuestion
v-if="q.answerSelected || q.questionSequence === currentQuestionNum"
class="radioQuestion"
@ -9,9 +15,9 @@
:answers="q.answers"
:groupName="`question-${index}-${q.questionSequence}`"
:modelValue="q.answerSelected"
@update:modelValue="handleAnswer(q, $event)"
isRequired
:validationRules="validationRules" />
:validationRules="validationRules"
@update:modelValue="handleAnswer(q, $event)" />
</transition>
</div>
</template>
@ -21,12 +27,9 @@ import buttonQuestion from '@/digital-components/button-question/button-question
import { useValidateForm } from 'vee-validate';
export default {
name: 'questionChain',
data() {
return {
currentQuestionNum: 0,
questions: []
};
name: 'question-chain',
components: {
buttonQuestion
},
props: {
questionData: Array,
@ -35,16 +38,23 @@ export default {
index: Number,
answerKey: String
},
emits: ['update:modelValue'],
data() {
return {
currentQuestionNum: 0,
questions: []
};
},
async created() {
// do a test validation check upon create to prevent out of sync / incorrect valid states
await useValidateForm(); // NOTE: needs to have async/await here; tested and won't work without it
// NOTE: needs to have async/await here; tested and won't work without it
await useValidateForm();
this.questionData.map((q, i) => {
const question = {
questionText: q.questionText,
questionSequence: q.questionSequence,
answers: q.answers.map((a) => {
return {
answers: q.answers.map((a) => ({
buttonLabel: a.answerText,
// Name will either be nextQuestionSequence or answerResult
// Name will be used by list-button as the input value.
@ -52,18 +62,17 @@ export default {
// 4 pieces of data separated by pipe characters:
// question number|type of answer|answer value|answer text
value: a.nextQuestionSequence
? q.questionSequence +
'|nextQuestion|' +
a.nextQuestionSequence +
'|' +
a.answerText
: q.questionSequence + '|answer|' + a.answerResult + '|' + a.answerText,
? `${q.questionSequence
}|nextQuestion|${
a.nextQuestionSequence
}|${
a.answerText}`
: `${q.questionSequence}|answer|${a.answerResult}|${a.answerText}`,
nextQuestionSequence: a.nextQuestionSequence,
answerResult: a.answerResult,
questionSequence: q.questionSequence,
questionType: a.nextQuestionSequence ? 'nextQuestion' : 'answer'
};
}),
})),
answerSelected: q.answerSelected || ''
};
if (!q.suppressThisQuestion) {
@ -105,7 +114,7 @@ export default {
// "5|answer|DW02104|Yes"
const returnedAnswerArray = returnedAnswer.split('|');
const questionNum = parseInt(returnedAnswerArray[0]);
const questionNum = parseInt(returnedAnswerArray[0], 10);
const questionType = returnedAnswerArray[1];
const questionAnswer = returnedAnswerArray[2];
const questionAnswerText = returnedAnswerArray[3];
@ -132,10 +141,12 @@ export default {
}
});
// return false if there's a nextQuestion... or return an object with final answers (truthy)
// return false if there's a nextQuestion...
// or return an object with final answers(truthy)
if (questionType === 'nextQuestion') {
// update to next question index
this.currentQuestionNum = parseInt(questionAnswer); // update count to display next question
// update count to display next question
this.currentQuestionNum = parseInt(questionAnswer, 10);
// scroll the next question into view
this.$nextTick(() => {
document
@ -143,25 +154,21 @@ export default {
.scrollIntoView({ behavior: 'smooth' });
});
return false;
} else {
}
// reset current question index (removes .current-question class)
this.currentQuestionNum = 0; // reset count
// return an object with the part answer, all the answered questions, and the part index
return {
answerResult: questionAnswer,
answeredQuestions: answeredQuestions,
answeredQuestions,
index: this.index
};
}
}
},
components: {
buttonQuestion
}
};
</script>
<style lang="scss">
<style lang="scss" scoped>
.questionBlock:not(:nth-of-type(1)){
.button-question.radioQuestion >div {

View file

@ -42,9 +42,7 @@ describe('modal.vue', () => {
const mockMixin = {
methods: {
getCmsContent: jest.fn((widgetName, cmsFieldName) => {
return mockCmsContent[cmsFieldName];
})
getCmsContent: jest.fn((widgetName, cmsFieldName) => mockCmsContent[cmsFieldName])
}
};

View file

@ -10,7 +10,8 @@ export default {
name: 'textBlock',
props: {
justifyText: String, // left, right, center
typeStyle: String, // h1-h6, body, small, label, caption (see Figma or Confluence documentation)
typeStyle: String, // h1-h6, body, small, label, caption
// (see Figma or Confluence documentation)
fontWeight: String, // bold=500, default is 400
cmsWidgetName: String
},

View file

@ -11,8 +11,7 @@ import { required } from '@/helpers/validation-rules.js';
describe('textarea-question.vue', () => {
const mockMixin = {
methods: {
getCmsContent: jest.fn().mockImplementation(() =>
getRandomString(10, 20)),
getCmsContent: jest.fn().mockImplementation(() => getRandomString(10, 20)),
setCmsContent: jest.fn()
}
};
@ -22,8 +21,7 @@ describe('textarea-question.vue', () => {
const questionText = getRandomString(10, 20);
const mixinWithQuestion = {
methods: {
getCmsContent: jest.fn().mockImplementation(() =>
questionText),
getCmsContent: jest.fn().mockImplementation(() => questionText),
setCmsContent: jest.fn()
}
};

View file

@ -143,7 +143,7 @@ export default {
</script>
<style lang="scss">
<style lang="scss" scoped>
.textarea-question {
label {
color: $black;

View file

@ -5,11 +5,9 @@ import textboxQuestion from './textbox-question';
const questionText = 'Question Text';
const mockMixin = {
methods: {
getCmsContent: jest.fn().mockImplementation(() => {
return questionText;
})
}
getCmsContent: jest.fn().mockImplementation(() => questionText)
}
};
const maska = jest.fn();
describe('textboxQuestion.vue', () => {
@ -18,7 +16,7 @@ describe('textboxQuestion.vue', () => {
const wrapper = shallowMount(textboxQuestion, {
global: {
directives: {
maska: maska,
maska
}
},
mixins: [mockMixin]
@ -38,7 +36,7 @@ describe('textboxQuestion.vue', () => {
const wrapper = shallowMount(textboxQuestion, {
global: {
directives: {
maska: maska,
maska
}
},
mixins: [mockMixin]
@ -56,7 +54,7 @@ describe('textboxQuestion.vue', () => {
const wrapper = shallowMount(textboxQuestion, {
global: {
directives: {
maska: maska,
maska
}
},
propsData: {
@ -77,7 +75,7 @@ describe('textboxQuestion.vue', () => {
const wrapper = shallowMount(textboxQuestion, {
global: {
directives: {
maska: maska,
maska
}
},
mixins: [mockMixin]
@ -95,7 +93,7 @@ describe('textboxQuestion.vue', () => {
const wrapper = shallowMount(textboxQuestion, {
global: {
directives: {
maska: maska,
maska
}
},
propsData: {
@ -116,7 +114,7 @@ describe('textboxQuestion.vue', () => {
const wrapper = shallowMount(textboxQuestion, {
global: {
directives: {
maska: maska,
maska
}
},
propsData: {
@ -128,7 +126,7 @@ describe('textboxQuestion.vue', () => {
await wrapper.find('input').setValue('val2');
// Assert
expect(wrapper.emitted()).toHaveProperty('change')
expect(wrapper.emitted()).toHaveProperty('change');
});
it('Should call this.handleChange with new value when the value is changed and the new value is valid', async () => {
@ -136,7 +134,7 @@ describe('textboxQuestion.vue', () => {
const wrapper = shallowMount(textboxQuestion, {
global: {
directives: {
maska: maska,
maska
}
},
propsData: {
@ -147,9 +145,7 @@ describe('textboxQuestion.vue', () => {
});
wrapper.vm.handleChange = jest.fn().mockImplementation(() => {});
wrapper.vm.validate = jest.fn().mockImplementation(() => {
return true;
});
wrapper.vm.validate = jest.fn().mockImplementation(() => true);
// Act
wrapper.vm.$options.watch.value.call(wrapper.vm, 'bar');

View file

@ -1,5 +1,7 @@
<template>
<div class="textbox-question" :class="(errors && errors.length) || hasError ? 'has-error' : ''">
<div
class="textbox-question"
:class="(errors && errors.length) || hasError ? 'has-error' : ''">
<label
v-if="displayQuestionText"
:for="inputId"
@ -14,12 +16,12 @@
includeSelectIcon ? 'has-select-icon' : '',
]">
<input
class="form-control"
:id="inputId"
:ref="inputId"
v-model.trim="value"
v-maska="mask"
class="form-control"
:type="type"
:ref="inputId"
:id="inputId"
:name="inputId"
:placeholder="placeholderText"
:aria-disabled="isDisabled"
@ -35,24 +37,31 @@
cornerStyle === 'rounded' ? 'rounded-pill' : '',
]"
:validationRules="validationRules"
:maxlength="maxLength ? maxLength : '999'"
:data-bs-toggle="includeSelectIcon ? 'modal' : ''"
:data-bs-target="'#' + cmsWidgetName"
@change="validationRules ? handleChange : () => {}"
@blur="validationRules ? handleChange : () => {}"
:maxlength="maxLength ? maxLength : '999'"
@focus="$emit('focus', $event.target.value)"
:data-bs-toggle="includeSelectIcon ? 'modal' : ''"
:data-bs-target="'#' + this.cmsWidgetName"
@paste="trimOnPaste"
@drop="trimOnPaste" />
<button v-if="includeSearchIcon" type="submit" aria-label="Search button" />
<button
v-if="includeSearchIcon"
type="submit"
aria-label="Search button" />
<button
v-if="includeSelectIcon"
type="submit"
data-bs-toggle="modal"
:data-bs-target="'#' + this.cmsWidgetName"
:data-bs-target="'#' + cmsWidgetName"
aria-label="Select button" />
</div>
<div v-show="errorMessage" class="row my-1 form-test-error">
<span class="d-inline-flex mt-0" role="alert">{{ errorMessage }}</span>
<div
v-show="errorMessage"
class="row my-1 form-test-error">
<span
class="d-inline-flex mt-0"
role="alert">{{ errorMessage }}</span>
</div>
</div>
</template>
@ -100,9 +109,10 @@ export default {
max: String,
disableAutoFill: Boolean
},
emits: ['focus', 'update:modelValue', 'textboxQuestionEvent.inputIdAssigned'],
setup(props) {
const propsClone = Object.assign({}, props);
const modelValue = propsClone.modelValue;
const propsClone = { ...props };
const { modelValue } = propsClone;
let initialValue;
switch (typeof modelValue) {
@ -117,14 +127,13 @@ export default {
const fieldOptions = {
type: 'text',
value: modelValue,
initialValue: initialValue
initialValue
};
const { errorMessage, handleBlur, handleChange, meta, validate, errors } = useField(
props.inputId,
// eslint-disable-next-line no-shadow
const { errorMessage, handleBlur, handleChange, meta, validate, errors } = useField(props.inputId,
props.validationRules,
fieldOptions
);
fieldOptions);
return {
errorMessage,
@ -135,42 +144,25 @@ export default {
errors
};
},
methods: {
trimOnPaste(evt) {
evt.stopPropagation();
var data = null;
if (evt.type === 'paste') {
data = evt.clipboardData || window.clipboardData;
} else {
evt.preventDefault();
data = evt.dataTransfer;
}
const value = data.getData('Text')?.trim();
this.$emit('update:modelValue', value);
}
},
computed: {
questionText() {
return this.getCmsContent(this.cmsWidgetName, 'QuestionText');
},
value: {
get: function () {
get() {
return this.modelValue;
},
set: function (newValue) {
set(newValue) {
this.$emit('update:modelValue', newValue);
}
},
labelText: {
get: function () {
get() {
const noBreakChar = '&NoBreak;';
var questionText = '';
let questionText = '';
if (this.disableAutoFill) {
var words = this.questionText.toString().split(/[ ]+/);
words.forEach(function (word) {
const words = this.questionText.toString().split(/[ ]+/);
words.forEach((word) => {
const position = 1;
word = [
word.toString().slice(0, position),
@ -189,9 +181,6 @@ export default {
}
}
},
mounted() {
this.$emit('textboxQuestionEvent.inputIdAssigned', this.inputId);
},
watch: {
async value(newValue) {
const result = await validate(newValue, this.validationRules); // do a test validation check, without triggering full validation
@ -199,11 +188,31 @@ export default {
this.handleChange(newValue); // trigger full validation on this field only
}
}
},
mounted() {
this.$emit('textboxQuestionEvent.inputIdAssigned', this.inputId);
},
methods: {
trimOnPaste(evt) {
evt.stopPropagation();
let data = null;
if (evt.type === 'paste') {
data = evt.clipboardData || window.clipboardData;
} else {
evt.preventDefault();
data = evt.dataTransfer;
}
const value = data.getData('Text')?.trim();
this.$emit('update:modelValue', value);
}
}
};
</script>
<style lang="scss">
<style lang="scss" scoped>
input[type="date"]::-webkit-inner-spin-button {
display: none;
}