From 6ae4bf7d35751ffdb62c195134cd42dadb61c171 Mon Sep 17 00:00:00 2001 From: ayangster Date: Fri, 15 Sep 2023 14:45:28 -0700 Subject: [PATCH 01/21] Add multiselect code in clean branch --- .parcelrc | 4 + package.json | 1 + src/calculator-form.ts | 24 ++- src/calculator.ts | 6 + src/select.ts | 75 +++++++ static/assets/icons/caret-down-fill.svg | 3 + yarn.lock | 267 +++++++++++++++++++++++- 7 files changed, 378 insertions(+), 2 deletions(-) create mode 100644 .parcelrc create mode 100644 static/assets/icons/caret-down-fill.svg diff --git a/.parcelrc b/.parcelrc new file mode 100644 index 0000000..792968a --- /dev/null +++ b/.parcelrc @@ -0,0 +1,4 @@ +{ + "extends": ["@parcel/config-default"], + "reporters": ["...", "parcel-reporter-static-files-copy"] +} \ No newline at end of file diff --git a/package.json b/package.json index 1849ba9..1147dc0 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "cypress": "^12.6.0", "eslint": "^8.45.0", "parcel": "v2.8.3", + "parcel-reporter-static-files-copy": "^1.5.2", "postcss": "^8.4.21", "postcss-modules": "^6.0.0", "posthtml": "^0.16.6", diff --git a/src/calculator-form.ts b/src/calculator-form.ts index 3f2f1a0..bde549e 100644 --- a/src/calculator-form.ts +++ b/src/calculator-form.ts @@ -1,6 +1,6 @@ import { html, css } from 'lit'; import { downIcon, questionIcon } from './icons'; -import { select, selectStyles, OptionParam } from './select'; +import { select, multiselect, selectStyles, OptionParam } from './select'; import { inputStyles } from './styles/input'; import './currency-input'; import '@shoelace-style/shoelace/dist/themes/light.css'; @@ -57,6 +57,11 @@ const OWNER_STATUS_OPTIONS: OptionParam[] = [ { value: 'renter', label: 'Renter' }, ]; +const PROJECT_OPTIONS: OptionParam[] = [ + { value: 'heating', label: 'Heating' }, + { value: 'electrical', label: 'Electrical wiring' }, +]; + const TAX_FILING_OPTIONS: OptionParam[] = [ { value: 'single', label: 'Single' }, { value: 'joint', label: 'Married Filing Jointly' }, @@ -75,6 +80,7 @@ const HOUSEHOLD_SIZE_OPTIONS: OptionParam[] = [1, 2, 3, 4, 5, 6, 7, 8].map( export const formTemplate = ( [zip, ownerStatus, householdIncome, taxFiling, householdSize]: Array, + projects: Array, onSubmit: (e: SubmitEvent) => void, ) => html`
@@ -119,6 +125,22 @@ export const formTemplate = ( })} +
+ +
diff --git a/src/select.ts b/src/select.ts index bc51c6a..2f746dc 100644 --- a/src/select.ts +++ b/src/select.ts @@ -1,5 +1,8 @@ import { html, css } from 'lit'; import { ifDefined } from 'lit/directives/if-defined.js'; +import '@shoelace-style/shoelace/dist/themes/light.css'; +import '@shoelace-style/shoelace/dist/components/option/option.js'; +import '@shoelace-style/shoelace/dist/components/select/select.js'; export interface OptionParam { label: string; @@ -14,9 +17,22 @@ export interface SelectParam { tabIndex?: number; } +export interface MultiSelectParam { + id: string; + label?: string; + currentValues: string[]; + options: OptionParam[]; + helpText?: string; + placeholder?: string; + maxOptionsVisible?: number; +} + export const option = ({ label, value }: OptionParam, selected: boolean) => html` `; +export const multioption = ({ label, value }: OptionParam) => + html` ${label} `; + export const select = ({ id, required, @@ -39,6 +55,36 @@ export const select = ({ `; }; +export const multiselect = ({ + id, + label, + currentValues, + options, + helpText, + placeholder, + maxOptionsVisible, +}: MultiSelectParam) => { + return html` +
+ + + ${options.map(o => multioption(o))} + + +
+ `; +}; + export const selectStyles = css` /* // @link https://moderncss.dev/custom-select-styles-with-pure-css/ */ @@ -161,4 +207,33 @@ export const selectStyles = css` background-color: #eee; background-image: linear-gradient(to top, #ddd, #eee 33%); } + + sl-select { + --sl-input-height-medium: 3rem; + } + // parts go largest to smallest + // wraps label, input and helptext + sl-select::parts(form-control) { + } + // don't need we are using html label + sl-select::parts(form-control-label) { + } + // select wrapper + sl-select::parts(form-control-input) { + } + // help text + sl-select::parts(form-control-help-text) { + } + // The container the wraps the prefix, combobox, clear icon, and expand button. + sl-select::parts(combobox) { + } + // The container that wraps the prefix slot (geat icon?) + sl-select::parts(prefix) { + } + // selected option label + sl-select::parts(display-input) { + } + // the container where options are listed + sl-select::parts(listbox) { + } `; diff --git a/static/assets/icons/caret-down-fill.svg b/static/assets/icons/caret-down-fill.svg new file mode 100644 index 0000000..d7c3990 --- /dev/null +++ b/static/assets/icons/caret-down-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index ddef129..cd82969 100644 --- a/yarn.lock +++ b/yarn.lock @@ -214,31 +214,61 @@ resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.5.2.tgz#bc66fa43286b5c082e8fee0eacc17995806b6fbe" integrity sha512-+F8ioQIUN68B4UFiIBYu0QQvgb9FmlKw2ctQMSBfW2QBrZIxz9vD9jCGqTCPqZBRbPHAS/vG1zSXnKqnS2ch/A== +"@lmdb/lmdb-darwin-arm64@2.7.11": + version "2.7.11" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.7.11.tgz#b717e72f023d4215d14e4c57433c711a53c782cf" + integrity sha512-r6+vYq2vKzE+vgj/rNVRMwAevq0+ZR9IeMFIqcSga+wMtMdXQ27KqQ7uS99/yXASg29bos7yHP3yk4x6Iio0lw== + "@lmdb/lmdb-darwin-x64@2.5.2": version "2.5.2" resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.5.2.tgz#89d8390041bce6bab24a82a20392be22faf54ffc" integrity sha512-KvPH56KRLLx4KSfKBx0m1r7GGGUMXm0jrKmNE7plbHlesZMuPJICtn07HYgQhj1LNsK7Yqwuvnqh1QxhJnF1EA== +"@lmdb/lmdb-darwin-x64@2.7.11": + version "2.7.11" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.7.11.tgz#b42898b0742b4a82b8224b742b2d174c449cd170" + integrity sha512-jhj1aB4K8ycRL1HOQT5OtzlqOq70jxUQEWRN9Gqh3TIDN30dxXtiHi6EWF516tzw6v2+3QqhDMJh8O6DtTGG8Q== + "@lmdb/lmdb-linux-arm64@2.5.2": version "2.5.2" resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.5.2.tgz#14fe4c96c2bb1285f93797f45915fa35ee047268" integrity sha512-aLl89VHL/wjhievEOlPocoefUyWdvzVrcQ/MHQYZm2JfV1jUsrbr/ZfkPPUFvZBf+VSE+Q0clWs9l29PCX1hTQ== +"@lmdb/lmdb-linux-arm64@2.7.11": + version "2.7.11" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.7.11.tgz#a8dc8e386d27006cfccbf2a8598290b63d03a9ec" + integrity sha512-7xGEfPPbmVJWcY2Nzqo11B9Nfxs+BAsiiaY/OcT4aaTDdykKeCjvKMQJA3KXCtZ1AtiC9ljyGLi+BfUwdulY5A== + "@lmdb/lmdb-linux-arm@2.5.2": version "2.5.2" resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.5.2.tgz#05bde4573ab10cf21827339fe687148f2590cfa1" integrity sha512-5kQAP21hAkfW5Bl+e0P57dV4dGYnkNIpR7f/GAh6QHlgXx+vp/teVj4PGRZaKAvt0GX6++N6hF8NnGElLDuIDw== +"@lmdb/lmdb-linux-arm@2.7.11": + version "2.7.11" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.7.11.tgz#2103f48af28336efccaac008fe882dfce33e4ac5" + integrity sha512-dHfLFVSrw/v5X5lkwp0Vl7+NFpEeEYKfMG2DpdFJnnG1RgHQZngZxCaBagFoaJGykRpd2DYF1AeuXBFrAUAXfw== + "@lmdb/lmdb-linux-x64@2.5.2": version "2.5.2" resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.5.2.tgz#d2f85afd857d2c33d2caa5b057944574edafcfee" integrity sha512-xUdUfwDJLGjOUPH3BuPBt0NlIrR7f/QHKgu3GZIXswMMIihAekj2i97oI0iWG5Bok/b+OBjHPfa8IU9velnP/Q== +"@lmdb/lmdb-linux-x64@2.7.11": + version "2.7.11" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.7.11.tgz#d21ac368022a662610540f2ba8bb6ff0b96a9940" + integrity sha512-vUKI3JrREMQsXX8q0Eq5zX2FlYCKWMmLiCyyJNfZK0Uyf14RBg9VtB3ObQ41b4swYh2EWaltasWVe93Y8+KDng== + "@lmdb/lmdb-win32-x64@2.5.2": version "2.5.2" resolved "https://registry.yarnpkg.com/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.5.2.tgz#28f643fbc0bec30b07fbe95b137879b6b4d1c9c5" integrity sha512-zrBczSbXKxEyK2ijtbRdICDygRqWSRPpZMN5dD1T8VMEW5RIhIbwFWw2phDRXuBQdVDpSjalCIUMWMV2h3JaZA== +"@lmdb/lmdb-win32-x64@2.7.11": + version "2.7.11" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.7.11.tgz#af2cb4ae6d3a92ecdeb1503b73079417525476d2" + integrity sha512-BJwkHlSUgtB+Ei52Ai32M1AOMerSlzyIGA/KC4dAGL+GGwVMdwG8HGCOA2TxP3KjhbgDPMYkv7bt/NmOmRIFng== + "@mischnic/json-sourcemap@^0.1.0": version "0.1.0" resolved "https://registry.yarnpkg.com/@mischnic/json-sourcemap/-/json-sourcemap-0.1.0.tgz#38af657be4108140a548638267d02a2ea3336507" @@ -253,31 +283,61 @@ resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.0.tgz#d31a238c943ffc34bab73ad6ce7a6466d65888ef" integrity sha512-5qpnNHUyyEj9H3sm/4Um/bnx1lrQGhe8iqry/1d+cQYCRd/gzYA0YLeq0ezlk4hKx4vO+dsEsNyeowqRqslwQA== +"@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.2.tgz#44d752c1a2dc113f15f781b7cc4f53a307e3fa38" + integrity sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ== + "@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.0.tgz#2f6fbbec3d3f0bbe9c6678c899f1c1a6e25ed980" integrity sha512-ZphTFFd6SFweNAMKD+QJCrWpgkjf4qBuHltiMkKkD6FFrB3NOTRVmetAGTkJ57pa+s6J0yCH06LujWB9rZe94g== +"@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.2.tgz#f954f34355712212a8e06c465bc06c40852c6bb3" + integrity sha512-lwriRAHm1Yg4iDf23Oxm9n/t5Zpw1lVnxYU3HnJPTi2lJRkKTrps1KVgvL6m7WvmhYVt/FIsssWay+k45QHeuw== + "@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.0.tgz#19875441da50b9aa8f8e726eb097a4cead435a3f" integrity sha512-NEX6hdSvP4BmVyegaIbrGxvHzHvTzzsPaxXCsUt0mbLbPpEftsvNwaEVKOowXnLoeuGeD4MaqSwL3BUK2elsUA== +"@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.2.tgz#45c63037f045c2b15c44f80f0393fa24f9655367" + integrity sha512-FU20Bo66/f7He9Fp9sP2zaJ1Q8L9uLPZQDub/WlUip78JlPeMbVL8546HbZfcW9LNciEXc8d+tThSJjSC+tmsg== + "@msgpackr-extract/msgpackr-extract-linux-arm@3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.0.tgz#3b855ac72cc16e89db2f72adf47ddc964c20a53d" integrity sha512-ztKVV1dO/sSZyGse0PBCq3Pk1PkYjsA/dsEWE7lfrGoAK3i9HpS2o7XjGQ7V4va6nX+xPPOiuYpQwa4Bi6vlww== +"@msgpackr-extract/msgpackr-extract-linux-arm@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.2.tgz#35707efeafe6d22b3f373caf9e8775e8920d1399" + integrity sha512-MOI9Dlfrpi2Cuc7i5dXdxPbFIgbDBGgKR5F2yWEa6FVEtSWncfVNKW5AKjImAQ6CZlBK9tympdsZJ2xThBiWWA== + "@msgpackr-extract/msgpackr-extract-linux-x64@3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.0.tgz#455f1d5bb00e87f78c67711f26e7bff9f1457684" integrity sha512-9uvdAkZMOPCY7SPRxZLW8XGqBOVNVEhqlgffenN8shA1XR9FWVsSM13nr/oHtNgXg6iVyML7RwWPyqUeThlwxg== +"@msgpackr-extract/msgpackr-extract-linux-x64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.2.tgz#091b1218b66c341f532611477ef89e83f25fae4f" + integrity sha512-gsWNDCklNy7Ajk0vBBf9jEx04RUxuDQfBse918Ww+Qb9HCPoGzS+XJTLe96iN3BVK7grnLiYghP/M4L8VsaHeA== + "@msgpackr-extract/msgpackr-extract-win32-x64@3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.0.tgz#03c6bfcd3acb179ea69546c20d50895b9d623ada" integrity sha512-Wg0+9615kHKlr9iLVcG5I+/CHnf6w3x5UADRv8Ad16yA0Bu5l9eVOROjV7aHPG6uC8ZPFIVVaoSjDChD+Y0pzg== +"@msgpackr-extract/msgpackr-extract-win32-x64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.2.tgz#0f164b726869f71da3c594171df5ebc1c4b0a407" + integrity sha512-O+6Gs8UeDbyFpbSh2CPEz/UOrrdWPTBYNblZK5CxxLisYt4kGX3Sc+czffFonyjiGSq3jWLwJS/CCJc7tBr4sQ== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -321,6 +381,16 @@ "@parcel/utils" "2.8.3" lmdb "2.5.2" +"@parcel/cache@2.9.3": + version "2.9.3" + resolved "https://registry.yarnpkg.com/@parcel/cache/-/cache-2.9.3.tgz#3ed40b79858fcb7c2c73c0ed4c9807cf2388c8b4" + integrity sha512-Bj/H2uAJJSXtysG7E/x4EgTrE2hXmm7td/bc97K8M9N7+vQjxf7xb0ebgqe84ePVMkj4MVQSMEJkEucXVx4b0Q== + dependencies: + "@parcel/fs" "2.9.3" + "@parcel/logger" "2.9.3" + "@parcel/utils" "2.9.3" + lmdb "2.7.11" + "@parcel/codeframe@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/codeframe/-/codeframe-2.8.3.tgz#84fb529ef70def7f5bc64f6c59b18d24826f5fcc" @@ -328,6 +398,13 @@ dependencies: chalk "^4.1.0" +"@parcel/codeframe@2.9.3": + version "2.9.3" + resolved "https://registry.yarnpkg.com/@parcel/codeframe/-/codeframe-2.9.3.tgz#056cacaeedae9318878bdee8ffc584178b10ba42" + integrity sha512-z7yTyD6h3dvduaFoHpNqur74/2yDWL++33rjQjIjCaXREBN6dKHoMGMizzo/i4vbiI1p9dDox2FIDEHCMQxqdA== + dependencies: + chalk "^4.1.0" + "@parcel/compressor-raw@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/compressor-raw/-/compressor-raw-2.8.3.tgz#301753df8c6de967553149639e8a4179b88f0c95" @@ -409,11 +486,24 @@ "@mischnic/json-sourcemap" "^0.1.0" nullthrows "^1.1.1" +"@parcel/diagnostic@2.9.3": + version "2.9.3" + resolved "https://registry.yarnpkg.com/@parcel/diagnostic/-/diagnostic-2.9.3.tgz#23befe6c3b78440fe1e3635086e637da1529b4db" + integrity sha512-6jxBdyB3D7gP4iE66ghUGntWt2v64E6EbD4AetZk+hNJpgudOOPsKTovcMi/i7I4V0qD7WXSF4tvkZUoac0jwA== + dependencies: + "@mischnic/json-sourcemap" "^0.1.0" + nullthrows "^1.1.1" + "@parcel/events@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/events/-/events-2.8.3.tgz#205f8d874e6ecc2cbdb941bf8d54bae669e571af" integrity sha512-hoIS4tAxWp8FJk3628bsgKxEvR7bq2scCVYHSqZ4fTi/s0+VymEATrRCUqf+12e5H47uw1/ZjoqrGtBI02pz4w== +"@parcel/events@2.9.3": + version "2.9.3" + resolved "https://registry.yarnpkg.com/@parcel/events/-/events-2.9.3.tgz#b71253384c21f53fd3cced983cd2b287f7330e89" + integrity sha512-K0Scx+Bx9f9p1vuShMzNwIgiaZUkxEnexaKYHYemJrM7pMAqxIuIqhnvwurRCsZOVLUJPDDNJ626cWTc5vIq+A== + "@parcel/fs-search@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/fs-search/-/fs-search-2.8.3.tgz#1c7d812c110b808758f44c56e61dfffdb09e9451" @@ -421,6 +511,11 @@ dependencies: detect-libc "^1.0.3" +"@parcel/fs-search@2.9.3": + version "2.9.3" + resolved "https://registry.yarnpkg.com/@parcel/fs-search/-/fs-search-2.9.3.tgz#4993d68478b15db404149a271bb0084382dd2040" + integrity sha512-nsNz3bsOpwS+jphcd+XjZL3F3PDq9lik0O8HPm5f6LYkqKWT+u/kgQzA8OkAHCR3q96LGiHxUywHPEBc27vI4Q== + "@parcel/fs@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/fs/-/fs-2.8.3.tgz#80536afe877fc8a2bd26be5576b9ba27bb4c5754" @@ -432,6 +527,17 @@ "@parcel/watcher" "^2.0.7" "@parcel/workers" "2.8.3" +"@parcel/fs@2.9.3": + version "2.9.3" + resolved "https://registry.yarnpkg.com/@parcel/fs/-/fs-2.9.3.tgz#39abd0f71561efccaac3ba6e4b8227705b73e906" + integrity sha512-/PrRKgCRw22G7rNPSpgN3Q+i2nIkZWuvIOAdMG4KWXC4XLp8C9jarNaWd5QEQ75amjhQSl3oUzABzkdCtkKrgg== + dependencies: + "@parcel/fs-search" "2.9.3" + "@parcel/types" "2.9.3" + "@parcel/utils" "2.9.3" + "@parcel/watcher" "^2.0.7" + "@parcel/workers" "2.9.3" + "@parcel/graph@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/graph/-/graph-2.8.3.tgz#00ffe8ec032e74fee57199e54529f1da7322571d" @@ -447,6 +553,13 @@ detect-libc "^1.0.3" xxhash-wasm "^0.4.2" +"@parcel/hash@2.9.3": + version "2.9.3" + resolved "https://registry.yarnpkg.com/@parcel/hash/-/hash-2.9.3.tgz#bc7727939b1211b0a5d67fd00a9a55b8393c644a" + integrity sha512-qlH5B85XLzVAeijgKPjm1gQu35LoRYX/8igsjnN8vOlbc3O8BYAUIutU58fbHbtE8MJPbxQQUw7tkTjeoujcQQ== + dependencies: + xxhash-wasm "^0.4.2" + "@parcel/logger@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/logger/-/logger-2.8.3.tgz#e14e4debafb3ca9e87c07c06780f9afc38b2712c" @@ -455,6 +568,14 @@ "@parcel/diagnostic" "2.8.3" "@parcel/events" "2.8.3" +"@parcel/logger@2.9.3": + version "2.9.3" + resolved "https://registry.yarnpkg.com/@parcel/logger/-/logger-2.9.3.tgz#04362704d7af93d213de6587ff71a1a6d5f714ac" + integrity sha512-5FNBszcV6ilGFcijEOvoNVG6IUJGsnMiaEnGQs7Fvc1dktTjEddnoQbIYhcSZL63wEmzBZOgkT5yDMajJ/41jw== + dependencies: + "@parcel/diagnostic" "2.9.3" + "@parcel/events" "2.9.3" + "@parcel/markdown-ansi@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/markdown-ansi/-/markdown-ansi-2.8.3.tgz#1337d421bb1133ad178f386a8e1b746631bba4a1" @@ -462,6 +583,13 @@ dependencies: chalk "^4.1.0" +"@parcel/markdown-ansi@2.9.3": + version "2.9.3" + resolved "https://registry.yarnpkg.com/@parcel/markdown-ansi/-/markdown-ansi-2.9.3.tgz#b4de64eb252ce13e27f6e24e420b607db51097a5" + integrity sha512-/Q4X8F2aN8UNjAJrQ5NfK2OmZf6shry9DqetUSEndQ0fHonk78WKt6LT0zSKEBEW/bB/bXk6mNMsCup6L8ibjQ== + dependencies: + chalk "^4.1.0" + "@parcel/namer-default@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/namer-default/-/namer-default-2.8.3.tgz#5304bee74beb4b9c1880781bdbe35be0656372f4" @@ -481,6 +609,18 @@ nullthrows "^1.1.1" semver "^5.7.1" +"@parcel/node-resolver-core@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@parcel/node-resolver-core/-/node-resolver-core-3.0.3.tgz#cc890e66695b6d28745415106565499af9cb3c47" + integrity sha512-AjxNcZVHHJoNT/A99PKIdFtwvoze8PAiC3yz8E/dRggrDIOboUEodeQYV5Aq++aK76uz/iOP0tST2T8A5rhb1A== + dependencies: + "@mischnic/json-sourcemap" "^0.1.0" + "@parcel/diagnostic" "2.9.3" + "@parcel/fs" "2.9.3" + "@parcel/utils" "2.9.3" + nullthrows "^1.1.1" + semver "^7.5.2" + "@parcel/optimizer-css@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/optimizer-css/-/optimizer-css-2.8.3.tgz#420a333f4b78f7ff15e69217dfed34421b1143ee" @@ -551,6 +691,20 @@ "@parcel/workers" "2.8.3" semver "^5.7.1" +"@parcel/package-manager@2.9.3": + version "2.9.3" + resolved "https://registry.yarnpkg.com/@parcel/package-manager/-/package-manager-2.9.3.tgz#e8522671ba6c4f0a07b518957d22a038a7698b24" + integrity sha512-NH6omcNTEupDmW4Lm1e4NUYBjdqkURxgZ4CNESESInHJe6tblVhNB8Rpr1ar7zDar7cly9ILr8P6N3Ei7bTEjg== + dependencies: + "@parcel/diagnostic" "2.9.3" + "@parcel/fs" "2.9.3" + "@parcel/logger" "2.9.3" + "@parcel/node-resolver-core" "3.0.3" + "@parcel/types" "2.9.3" + "@parcel/utils" "2.9.3" + "@parcel/workers" "2.9.3" + semver "^7.5.2" + "@parcel/packager-css@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/packager-css/-/packager-css-2.8.3.tgz#0eff34268cb4f5dfb53c1bbca85f5567aeb1835a" @@ -609,6 +763,22 @@ dependencies: "@parcel/types" "2.8.3" +"@parcel/plugin@^2.0.0-beta.1": + version "2.9.3" + resolved "https://registry.yarnpkg.com/@parcel/plugin/-/plugin-2.9.3.tgz#90e9a9482fa27735494372f5643db01abcf3fdb6" + integrity sha512-qN85Gqr2GMuxX1dT1mnuO9hOcvlEv1lrYrCxn7CJN2nUhbwcfG+LEvcrCzCOJ6XtIHm+ZBV9h9p7FfoPLvpw+g== + dependencies: + "@parcel/types" "2.9.3" + +"@parcel/profiler@2.9.3": + version "2.9.3" + resolved "https://registry.yarnpkg.com/@parcel/profiler/-/profiler-2.9.3.tgz#6575ed6dc4275c0161dce74bd719961236673ce1" + integrity sha512-pyHc9lw8VZDfgZoeZWZU9J0CVEv1Zw9O5+e0DJPDPHuXJYr72ZAOhbljtU3owWKAeW+++Q2AZWkbUGEOjI/e6g== + dependencies: + "@parcel/diagnostic" "2.9.3" + "@parcel/events" "2.9.3" + chrome-trace-event "^1.0.2" + "@parcel/reporter-cli@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/reporter-cli/-/reporter-cli-2.8.3.tgz#12a4743b51b8fe6837f53c20e01bbf1f7336e8e4" @@ -826,6 +996,19 @@ "@parcel/workers" "2.8.3" utility-types "^3.10.0" +"@parcel/types@2.9.3": + version "2.9.3" + resolved "https://registry.yarnpkg.com/@parcel/types/-/types-2.9.3.tgz#170a26203b9088a306862b2dc914c27375d77bbc" + integrity sha512-NSNY8sYtRhvF1SqhnIGgGvJocyWt1K8Tnw5cVepm0g38ywtX6mwkBvMkmeehXkII4mSUn+frD9wGsydTunezvA== + dependencies: + "@parcel/cache" "2.9.3" + "@parcel/diagnostic" "2.9.3" + "@parcel/fs" "2.9.3" + "@parcel/package-manager" "2.9.3" + "@parcel/source-map" "^2.1.1" + "@parcel/workers" "2.9.3" + utility-types "^3.10.0" + "@parcel/utils@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/utils/-/utils-2.8.3.tgz#0d56c9e8e22c119590a5e044a0e01031965da40e" @@ -839,6 +1022,20 @@ "@parcel/source-map" "^2.1.1" chalk "^4.1.0" +"@parcel/utils@2.9.3": + version "2.9.3" + resolved "https://registry.yarnpkg.com/@parcel/utils/-/utils-2.9.3.tgz#d4df6837658f773c725a4934967ab1128a05fdd7" + integrity sha512-cesanjtj/oLehW8Waq9JFPmAImhoiHX03ihc3JTWkrvJYSbD7wYKCDgPAM3JiRAqvh1LZ6P699uITrYWNoRLUg== + dependencies: + "@parcel/codeframe" "2.9.3" + "@parcel/diagnostic" "2.9.3" + "@parcel/hash" "2.9.3" + "@parcel/logger" "2.9.3" + "@parcel/markdown-ansi" "2.9.3" + "@parcel/source-map" "^2.1.1" + chalk "^4.1.0" + nullthrows "^1.1.1" + "@parcel/watcher@^2.0.7": version "2.1.0" resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.1.0.tgz#5f32969362db4893922c526a842d8af7a8538545" @@ -861,6 +1058,18 @@ chrome-trace-event "^1.0.2" nullthrows "^1.1.1" +"@parcel/workers@2.9.3": + version "2.9.3" + resolved "https://registry.yarnpkg.com/@parcel/workers/-/workers-2.9.3.tgz#d1d84d3c767b840d0ed7123a03ab7e0f4a2c0731" + integrity sha512-zRrDuZJzTevrrwElYosFztgldhqW6G9q5zOeQXfVQFkkEJCNfg36ixeiofKRU8uu2x+j+T6216mhMNB6HiuY+w== + dependencies: + "@parcel/diagnostic" "2.9.3" + "@parcel/logger" "2.9.3" + "@parcel/profiler" "2.9.3" + "@parcel/types" "2.9.3" + "@parcel/utils" "2.9.3" + nullthrows "^1.1.1" + "@shoelace-style/animations@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@shoelace-style/animations/-/animations-1.1.0.tgz#17539abafd6dcbf2a79e089e1593175e9f7835b5" @@ -2422,6 +2631,24 @@ lmdb@2.5.2: "@lmdb/lmdb-linux-x64" "2.5.2" "@lmdb/lmdb-win32-x64" "2.5.2" +lmdb@2.7.11: + version "2.7.11" + resolved "https://registry.yarnpkg.com/lmdb/-/lmdb-2.7.11.tgz#a24b6d36b5c7ed9889cc2d9e103fdd3f5e144d7e" + integrity sha512-x9bD4hVp7PFLUoELL8RglbNXhAMt5CYhkmss+CEau9KlNoilsTzNi9QDsPZb3KMpOGZXG6jmXhW3bBxE2XVztw== + dependencies: + msgpackr "1.8.5" + node-addon-api "^4.3.0" + node-gyp-build-optional-packages "5.0.6" + ordered-binary "^1.4.0" + weak-lru-cache "^1.2.2" + optionalDependencies: + "@lmdb/lmdb-darwin-arm64" "2.7.11" + "@lmdb/lmdb-darwin-x64" "2.7.11" + "@lmdb/lmdb-linux-arm" "2.7.11" + "@lmdb/lmdb-linux-arm64" "2.7.11" + "@lmdb/lmdb-linux-x64" "2.7.11" + "@lmdb/lmdb-win32-x64" "2.7.11" + loader-utils@^3.2.0: version "3.2.1" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.2.1.tgz#4fb104b599daafd82ef3e1a41fb9265f87e1f576" @@ -2555,6 +2782,27 @@ msgpackr-extract@^3.0.0: "@msgpackr-extract/msgpackr-extract-linux-x64" "3.0.0" "@msgpackr-extract/msgpackr-extract-win32-x64" "3.0.0" +msgpackr-extract@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-3.0.2.tgz#e05ec1bb4453ddf020551bcd5daaf0092a2c279d" + integrity sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A== + dependencies: + node-gyp-build-optional-packages "5.0.7" + optionalDependencies: + "@msgpackr-extract/msgpackr-extract-darwin-arm64" "3.0.2" + "@msgpackr-extract/msgpackr-extract-darwin-x64" "3.0.2" + "@msgpackr-extract/msgpackr-extract-linux-arm" "3.0.2" + "@msgpackr-extract/msgpackr-extract-linux-arm64" "3.0.2" + "@msgpackr-extract/msgpackr-extract-linux-x64" "3.0.2" + "@msgpackr-extract/msgpackr-extract-win32-x64" "3.0.2" + +msgpackr@1.8.5: + version "1.8.5" + resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.8.5.tgz#8cadfb935357680648f33699d0e833c9179dbfeb" + integrity sha512-mpPs3qqTug6ahbblkThoUY2DQdNXcm4IapwOS3Vm/87vmpzLVelvp9h3It1y9l1VPpiFLV11vfOXnmeEwiIXwg== + optionalDependencies: + msgpackr-extract "^3.0.1" + msgpackr@^1.5.4: version "1.8.3" resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.8.3.tgz#78c1b91359f72707f4abeaca40cc423bd2d75185" @@ -2592,6 +2840,11 @@ node-gyp-build-optional-packages@5.0.3: resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.3.tgz#92a89d400352c44ad3975010368072b41ad66c17" integrity sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA== +node-gyp-build-optional-packages@5.0.6: + version "5.0.6" + resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.6.tgz#2949f5cc7dace3ac470fa2ff1a37456907120a1d" + integrity sha512-2ZJErHG4du9G3/8IWl/l9Bp5BBFy63rno5GVmjQijvTuUZKsl6g8RB4KH/x3NLcV5ZBb4GsXmAuTYr6dRml3Gw== + node-gyp-build-optional-packages@5.0.7: version "5.0.7" resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.7.tgz#5d2632bbde0ab2f6e22f1bbac2199b07244ae0b3" @@ -2662,6 +2915,11 @@ ordered-binary@^1.2.4: resolved "https://registry.yarnpkg.com/ordered-binary/-/ordered-binary-1.4.0.tgz#6bb53d44925f3b8afc33d1eed0fa15693b211389" integrity sha512-EHQ/jk4/a9hLupIKxTfUsQRej1Yd/0QLQs3vGvIqg5ZtCYSzNhkzHoZc7Zf4e4kUlDaC3Uw8Q/1opOLNN2OKRQ== +ordered-binary@^1.4.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/ordered-binary/-/ordered-binary-1.4.1.tgz#205cb6efd6c27fa0ef4eced994a023e081cdc911" + integrity sha512-9LtiGlPy982CsgxZvJGNNp2/NnrgEr6EAyN3iIEP3/8vd3YLgAZQHbQ75ZrkfBRGrNg37Dk3U6tuVb+B4Xfslg== + ospath@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" @@ -2688,6 +2946,13 @@ p-map@^4.0.0: dependencies: aggregate-error "^3.0.0" +parcel-reporter-static-files-copy@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/parcel-reporter-static-files-copy/-/parcel-reporter-static-files-copy-1.5.2.tgz#aed0370717ea5d5185d6b09aea9cf9e6f479366c" + integrity sha512-NyIP8dOBj4WiZYAfKKTFWrMh8/kjrP72673G3hs7TOoT/30QWwUQpRA7oW4IxbjY2ICOo2sV06Z1lzPKCDbrGA== + dependencies: + "@parcel/plugin" "^2.0.0-beta.1" + parcel@v2.8.3: version "2.8.3" resolved "https://registry.yarnpkg.com/parcel/-/parcel-2.8.3.tgz#1ff71d7317274fd367379bc7310a52c6b75d30c2" @@ -3022,7 +3287,7 @@ semver@^7.3.2: dependencies: lru-cache "^6.0.0" -semver@^7.5.4: +semver@^7.5.2, semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== From 731e705b37411ebc22f24a8434509b3fde03d9ba Mon Sep 17 00:00:00 2001 From: Owen Yamauchi Date: Fri, 4 Aug 2023 19:00:49 -0400 Subject: [PATCH 02/21] New state-specific calculator design ## Description This implements most of the spec'd design. There are some significant pieces still missing. They're tracked in Asana, but just as a summary: - Multiple selection of projects. - Adding authority logos to the bottom of the page. - Handling the case of entering a location outside RI. - Adding an optional email field for people to join the mailing list. In addition, there are some more pieces missing before this frontend is ready to replace the existing IRA calculator wholesale: - Spanish localization - Analytics - Appearance customizability via CSS ### Guide to new code - New .html page for the RI calculator, which pretty much just has the embed. It's included in the Parcel targets, which means it will be built and deployed. It's not ready for the public yet, so it uses a meta tag to disable search engine indexing. - `state-calculator.ts` is the custom HTML element. This contains all the state management. - `state-incentive-details.ts` is the view layer. - A couple of sub-parts are factored out: `utility-selector.ts` and `icon-tab-bar.ts`. - `calculator-types-v1.ts` corresponds to the shape of the API responses. This is manually maintained right now, not generated from the API in any way; eventually it'd be good to change that. - `projects.ts` has icons and labels for the various projects. ## Test Plan - Enter a RI zip code. 02807 for Block Island, 02861 for Providence (RI Energy only), 02814 for Pascoag (RIE and PUD available). - Fill in the rest of the form, click Calculate. Look at results. In the 02814 case, switch to the other utility and make sure the incentives reload. - Click the pill buttons to switch projects. - Click the "Learn more" / "Visit site" buttons. Resize the window to look at the three possible layouts. (1, 2, or 3 columns of incentive cards.) --- package.json | 3 +- src/api/calculator-types-v1.ts | 63 +++++ src/calculator-form.ts | 236 ++++++++++-------- src/calculator.ts | 2 + src/icon-tab-bar.ts | 124 ++++++++++ src/icons.ts | 42 +++- src/projects.ts | 205 ++++++++++++++++ src/rhode-island.html | 75 ++++++ src/select.ts | 13 + src/state-calculator.ts | 263 ++++++++++++++++++++ src/state-incentive-details.ts | 436 +++++++++++++++++++++++++++++++++ src/states.ts | 22 ++ src/utility-selector.ts | 132 ++++++++++ 13 files changed, 1515 insertions(+), 101 deletions(-) create mode 100644 src/api/calculator-types-v1.ts create mode 100644 src/icon-tab-bar.ts create mode 100644 src/projects.ts create mode 100644 src/rhode-island.html create mode 100644 src/state-calculator.ts create mode 100644 src/state-incentive-details.ts create mode 100644 src/states.ts create mode 100644 src/utility-selector.ts diff --git a/package.json b/package.json index 1849ba9..fa4c954 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,8 @@ "source": [ "src/calculator.ts", "src/rewiring-fonts.css", - "src/index.html" + "src/index.html", + "src/rhode-island.html" ], "outputFormat": "global", "context": "browser", diff --git a/src/api/calculator-types-v1.ts b/src/api/calculator-types-v1.ts new file mode 100644 index 0000000..866e4d1 --- /dev/null +++ b/src/api/calculator-types-v1.ts @@ -0,0 +1,63 @@ +export type IncentiveType = + | 'tax_credit' + | 'pos_rebate' + | 'rebate' + | 'account_credit' + | 'performance_rebate'; +export type AuthorityType = 'federal' | 'state' | 'utility'; + +export type AmountType = 'dollar_amount' | 'percent' | 'dollars_per_unit'; +export interface Amount { + type: AmountType; + number: number; + maximum?: number; + representative?: number; + unit?: string; +} + +export type ItemType = + | 'battery_storage_installation' + | 'efficiency_rebates' + | 'electric_panel' + | 'electric_stove' + | 'electric_vehicle_charger' + | 'electric_wiring' + | 'geothermal_heating_installation' + | 'heat_pump_air_conditioner_heater' + | 'heat_pump_clothes_dryer' + | 'heat_pump_water_heater' + | 'new_electric_vehicle' + | 'rooftop_solar_installation' + | 'used_electric_vehicle' + | 'weatherization'; + +export interface Item { + type: ItemType; + name: string; + url: string; +} + +export interface Incentive { + type: IncentiveType; + authority_type: AuthorityType; + authority_name: string | null; + program: string; + program_url?: string; + item: Item; + amount: Amount; + start_date: number; + end_date: number; + short_description?: string; + + eligible: boolean; +} + +export interface APIResponse { + savings: { + tax_credit: number; + pos_rebate: number; + rebate: number; + account_credit: number; + }; + incentives: Incentive[]; +} diff --git a/src/calculator-form.ts b/src/calculator-form.ts index 3f2f1a0..8f542ce 100644 --- a/src/calculator-form.ts +++ b/src/calculator-form.ts @@ -1,13 +1,14 @@ -import { html, css } from 'lit'; +import { html, css, nothing } from 'lit'; import { downIcon, questionIcon } from './icons'; import { select, selectStyles, OptionParam } from './select'; import { inputStyles } from './styles/input'; import './currency-input'; import '@shoelace-style/shoelace/dist/themes/light.css'; import '@shoelace-style/shoelace/dist/components/tooltip/tooltip.js'; +import { PROJECTS } from './projects'; const buttonStyles = css` - button { + button.calculate { appearance: none; font-family: inherit; font-size: 16px; @@ -24,7 +25,7 @@ const buttonStyles = css` width: 100%; } - button:hover { + button.calculate:hover { background-color: var(--ra-embed-primary-button-background-hover-color); } @@ -74,110 +75,147 @@ const HOUSEHOLD_SIZE_OPTIONS: OptionParam[] = [1, 2, 3, 4, 5, 6, 7, 8].map( ); export const formTemplate = ( - [zip, ownerStatus, householdIncome, taxFiling, householdSize]: Array, + [ + project, + zip, + ownerStatus, + householdIncome, + taxFiling, + householdSize, + ]: Array, + showProjectField: boolean, onSubmit: (e: SubmitEvent) => void, -) => html` - -
-
-
- -`; + + `; +}; diff --git a/src/calculator.ts b/src/calculator.ts index edaf7e5..02fd9e5 100644 --- a/src/calculator.ts +++ b/src/calculator.ts @@ -150,12 +150,14 @@ export class RewiringAmericaCalculator extends LitElement { ? nothing : formTemplate( [ + '', this.zip, this.ownerStatus, this.householdIncome, this.taxFiling, this.householdSize, ], + false, (event: SubmitEvent) => this.submit(event), )}
diff --git a/src/icon-tab-bar.ts b/src/icon-tab-bar.ts new file mode 100644 index 0000000..de04605 --- /dev/null +++ b/src/icon-tab-bar.ts @@ -0,0 +1,124 @@ +import { css, html } from 'lit'; +import { PROJECTS, Project, shortLabel } from './projects'; +import { select } from './select'; + +export const iconTabBarStyles = css` + .icon-tab-bar { + display: flex; + gap: 1rem; + flex-wrap: wrap; + justify-content: center; + + width: 83.3%; + margin-left: auto; + margin-right: auto; + margin-bottom: 1.5rem; + } + + button.icon-tab { + /* Override default button styles */ + background-color: transparent; + font-family: inherit; + cursor: pointer; + + min-width: 6rem; + + height: 3rem; + border: 1px solid #9b9b9b; + border-radius: 1.5rem; + padding: 0.75rem 1rem; + + /* Manage the gap between the icon and the caption */ + display: flex; + flex-direction: row; + gap: 0.5rem; + align-items: center; + justify-content: center; + + & .caption { + color: var(--color-purple-500, #4a00c3); + font-size: 1rem; + font-weight: 500; + line-height: 125%; + white-space: nowrap; + } + } + + button.icon-tab--selected { + cursor: default; + + background: var(--color-purple-500, #4a00c3); + border-color: var(--color-purple-500, #4a00c3); + + & .caption { + color: white; + } + } + + .icon-dropdown { + margin-bottom: 1.5rem; + } + + /* + * Only show the tabs/pills on medium and large layout. + * Only show dropdown on small layout. + */ + @media only screen and (max-width: 640px) { + .icon-tab-bar { + display: none; + } + } + + @media only screen and (min-width: 641px) { + .icon-dropdown { + display: none; + } + } +`; + +/** + * On medium and large layouts, this is a horizontally flowed row of pill-shaped + * buttons, with icon and text, representing projects. Clicking one selects that + * single project. The pills can flow onto multiple lines. + * + * On small layouts, this is a single-select dropdown. + */ +export const iconTabBarTemplate = ( + tabs: Project[], + selectedTab: Project, + onTabSelected: (newSelection: Project) => void, +) => { + const iconTabs = tabs.map(project => { + const isSelected = project === selectedTab; + const selectedClass = isSelected ? 'icon-tab--selected' : ''; + return html` + + `; + }); + + const options = tabs.map(project => ({ + value: project, + label: PROJECTS[project].label, + })); + + return html` +
${iconTabs}
+
+ ${select({ + id: 'project-selector', + required: true, + currentValue: selectedTab, + options, + onChange: event => + onTabSelected((event.target as HTMLInputElement).value as Project), + ariaLabel: 'Project', + })} +
+ `; +}; diff --git a/src/icons.ts b/src/icons.ts index 85fbd31..ef23a1f 100644 --- a/src/icons.ts +++ b/src/icons.ts @@ -1,4 +1,4 @@ -import { html } from 'lit'; +import { html, svg } from 'lit'; // FIXME: does this need to be nested like this? export const downIcon = (w: number, h: number) => html` html` `; + +export const exclamationPoint = (w: number = 16, h: number = 16) => html` + + + +`; + +export const upRightArrow = ( + w: number = 20, + h: number = 20, +) => svg` + + +`; diff --git a/src/projects.ts b/src/projects.ts new file mode 100644 index 0000000..bbc3fea --- /dev/null +++ b/src/projects.ts @@ -0,0 +1,205 @@ +import { TemplateResult, svg } from 'lit'; +import { ItemType } from './api/calculator-types-v1'; + +const RA_PURPLE = '#4a00c3'; +const color = (selected: boolean) => (selected ? 'white' : RA_PURPLE); + +const CLOTHES_DRYER_ICON = ( + selected: boolean, + w: number = 20, + h: number = 20, +) => + svg` + + + `; + +const COOKING_ICON = (selected: boolean, w: number = 20, h: number = 20) => + svg` + + + + + + `; + +const EV_ICON = (selected: boolean, w: number = 20, h: number = 20) => + svg` + + + + `; + +const ELECTRICAL_WIRING_ICON = ( + selected: boolean, + w: number = 20, + h: number = 20, +) => svg` + + + +`; + +const HVAC_ICON = (selected: boolean, w: number = 20, h: number = 20) => + svg` + + + `; + +const BATTERY_ICON = (selected: boolean, w: number = 20, h: number = 20) => + svg` + + + + + `; + +const SOLAR_ICON = (selected: boolean, w: number = 20, h: number = 20) => + svg` + + + + + + `; + +const WATER_HEATER_ICON = (selected: boolean, w: number = 20, h: number = 20) => + svg` + + + + + + `; + +type ProjectInfo = { + label: string; + shortLabel?: string; + icon: (selected: boolean, w?: number, h?: number) => TemplateResult<2>; + items: ItemType[]; +}; + +export type Project = + | 'heat_pump_clothes_dryer' + | 'hvac' + | 'ev' + | 'solar' + | 'battery' + | 'heat_pump_water_heater' + | 'cooking' + | 'wiring'; + +export const shortLabel = (p: Project) => + PROJECTS[p].shortLabel ?? PROJECTS[p].label; + +/** + * Icons, labels, and API `item` values for the various projects for which we + * show incentives. + */ +export const PROJECTS: Record = { + heat_pump_clothes_dryer: { + items: ['heat_pump_clothes_dryer'], + label: 'Clothes dryer', + icon: CLOTHES_DRYER_ICON, + }, + hvac: { + items: [ + 'heat_pump_air_conditioner_heater', + 'geothermal_heating_installation', + ], + label: 'Heating, ventilation & cooling', + shortLabel: 'HVAC', + icon: HVAC_ICON, + }, + ev: { + items: [ + 'new_electric_vehicle', + 'used_electric_vehicle', + 'electric_vehicle_charger', + ], + label: 'Electric vehicle', + shortLabel: 'EV', + icon: EV_ICON, + }, + solar: { + items: ['rooftop_solar_installation'], + label: 'Solar', + icon: SOLAR_ICON, + }, + battery: { + items: ['battery_storage_installation'], + label: 'Battery storage', + icon: BATTERY_ICON, + }, + heat_pump_water_heater: { + items: ['heat_pump_water_heater'], + label: 'Water heater', + icon: WATER_HEATER_ICON, + }, + cooking: { + items: ['electric_stove'], + label: 'Cooking stove/range', + shortLabel: 'Cooking', + icon: COOKING_ICON, + }, + wiring: { + items: ['electric_panel', 'electric_wiring'], + label: 'Electrical wiring', + shortLabel: 'Electrical', + icon: ELECTRICAL_WIRING_ICON, + }, +}; diff --git a/src/rhode-island.html b/src/rhode-island.html new file mode 100644 index 0000000..cecdd4d --- /dev/null +++ b/src/rhode-island.html @@ -0,0 +1,75 @@ + + + + + + + + Rewiring America Incentives Calculator - Rhode Island + + + + + + +
+

Rewiring America Incentives Calculator

+ +
+ + diff --git a/src/select.ts b/src/select.ts index bc51c6a..41d55ac 100644 --- a/src/select.ts +++ b/src/select.ts @@ -12,6 +12,9 @@ export interface SelectParam { options: OptionParam[]; currentValue: string; tabIndex?: number; + onChange?: (event: InputEvent) => void; + ariaLabel?: string; + disabled?: boolean; } export const option = ({ label, value }: OptionParam, selected: boolean) => @@ -23,6 +26,9 @@ export const select = ({ options, currentValue, tabIndex, + onChange, + ariaLabel, + disabled, }: SelectParam) => { return html`
@@ -30,7 +36,10 @@ export const select = ({ id="${id}" name="${id}" ?required=${required} + ?disabled=${disabled} tabindex="${ifDefined(tabIndex)}" + aria-label="${ifDefined(ariaLabel)}" + @change=${onChange} > ${options.map(o => option(o, o.value === currentValue))} @@ -94,6 +103,10 @@ export const selectStyles = css` margin-top: 4px; } + .select select[disabled] { + cursor: default; + } + .select select, .select::after { grid-area: select; diff --git a/src/state-calculator.ts b/src/state-calculator.ts new file mode 100644 index 0000000..178a1d4 --- /dev/null +++ b/src/state-calculator.ts @@ -0,0 +1,263 @@ +import { LitElement, html, nothing } from 'lit'; +import { customElement, property } from 'lit/decorators.js'; +import { Task, TaskStatus, initialState } from '@lit-labs/task'; +import { baseStyles } from './styles'; +import { formTemplate, formStyles } from './calculator-form'; +import { FilingStatus, OwnerStatus } from './calculator-types'; +import { CALCULATOR_FOOTER } from './calculator-footer'; +import { fetchApi } from './api/fetch'; +import { + stateIncentivesTemplate, + stateIncentivesStyles, + cardStyles, + separatorStyles, +} from './state-incentive-details'; +import { Project } from './projects'; +import { + utilitySelectorStyles, + utilitySelectorTemplate, +} from './utility-selector'; +import { iconTabBarStyles } from './icon-tab-bar'; + +import '@shoelace-style/shoelace/dist/components/spinner/spinner'; +import { STATES } from './states'; + +const loadingTemplate = () => html` +
+
+ +
+
+`; + +const errorTemplate = (error: unknown) => html` +
+ ${typeof error === 'object' && error && 'message' in error && error.message + ? error.message + : 'Error loading incentives.'} +
+`; + +const DEFAULT_CALCULATOR_API_HOST: string = 'https://api.rewiringamerica.org'; + +@customElement('rewiring-america-state-calculator') +export class RewiringAmericaStateCalculator extends LitElement { + static override styles = [ + baseStyles, + cardStyles, + ...formStyles, + stateIncentivesStyles, + utilitySelectorStyles, + separatorStyles, + iconTabBarStyles, + ]; + + /* supported properties to control showing/hiding of each card in the widget */ + + @property({ type: Boolean, attribute: 'hide-form' }) + hideForm: boolean = false; + + @property({ type: Boolean, attribute: 'hide-details' }) + hideDetails: boolean = false; + + /* supported properties to control which API path and key is used to load the calculator results */ + + @property({ type: String, attribute: 'api-key' }) + apiKey: string = ''; + + @property({ type: String, attribute: 'api-host' }) + apiHost: string = DEFAULT_CALCULATOR_API_HOST; + + /** + * Property to customize the calculator for a particular state. Must be the + * two-letter code, uppercase (example: "NY"). + * + * Currently the only customization is to display the name of the state. + * TODO: Have a nice error message if you enter a zip/address outside this + * state, if it's defined. + */ + @property({ type: String, attribute: 'state' }) + state: string = ''; + + /* supported properties to allow pre-filling the form */ + + @property({ type: String, attribute: 'zip' }) + zip: string = ''; + + @property({ type: String, attribute: 'owner-status' }) + ownerStatus: OwnerStatus = 'homeowner'; + + @property({ type: String, attribute: 'household-income' }) + householdIncome: string = '0'; + + @property({ type: String, attribute: 'tax-filing' }) + taxFiling: FilingStatus = 'single'; + + @property({ type: String, attribute: 'household-size' }) + householdSize: string = '1'; + + @property({ type: String }) + utility: string = ''; + + @property({ type: String }) + selectedProject: Project = 'hvac'; + + @property({ type: String }) + selectedOtherTab: Project = 'heat_pump_clothes_dryer'; + + submit(e: SubmitEvent) { + e.preventDefault(); + const formData = new FormData(e.target as HTMLFormElement); + const prevZip = this.zip; + this.zip = (formData.get('zip') as string) || ''; + this.ownerStatus = (formData.get('owner_status') as OwnerStatus) || ''; + this.householdIncome = (formData.get('household_income') as string) || ''; + this.taxFiling = (formData.get('tax_filing') as FilingStatus) || ''; + this.householdSize = (formData.get('household_size') as string) || ''; + this.selectedProject = (formData.get('project') as Project) || ''; + + // Zip is the only thing that determines what utilities are available, so + // only fetch utilities if zip has changed since last calculation. + const zipChanged = this.zip !== prevZip; + if (zipChanged) { + // This will run _task when it's done. + this._utilitiesTask.run(); + } else { + this._task.run(); + } + } + + isFormComplete() { + return !!( + this.zip && + this.ownerStatus && + this.taxFiling && + this.householdIncome && + this.householdSize && + this.selectedProject + ); + } + + private _utilitiesTask = new Task(this, { + autoRun: false, + task: async () => { + const query = new URLSearchParams({ + 'location[zip]': this.zip, + }); + const utilityMap = await fetchApi( + this.apiKey, + this.apiHost, + '/api/v1/utilities', + query, + ); + + return Object.keys(utilityMap).map(id => ({ + value: id, + label: utilityMap[id].name, + })); + }, + onComplete: options => { + // Preserve the previous utility selection if it's still available. + if (!options.map(o => o.value).includes(this.utility)) { + this.utility = options[0].value; + } + this._task.run(); + }, + }); + + private _task = new Task(this, { + autoRun: false, + task: async () => { + if (!this.isFormComplete()) { + // this is a special response type provided by Task to keep it in the INITIAL state + return initialState; + } + + const query = new URLSearchParams({ + 'location[zip]': this.zip, + owner_status: this.ownerStatus, + household_income: this.householdIncome, + tax_filing: this.taxFiling, + household_size: this.householdSize, + }); + query.append('authority_types', 'federal'); + query.append('authority_types', 'state'); + query.append('authority_types', 'utility'); + query.set('utility', this.utility); + + return await fetchApi( + this.apiKey, + this.apiHost, + '/api/v1/calculator', + query, + ); + }, + }); + + override render() { + return html` +
+
+

Your household info

+ ${this.hideForm + ? nothing + : formTemplate( + [ + this.selectedProject, + this.zip, + this.ownerStatus, + this.householdIncome, + this.taxFiling, + this.householdSize, + ], + true, + (event: SubmitEvent) => this.submit(event), + 'grid-3-2-1', + )} +
+ ${this._utilitiesTask.render({ + pending: loadingTemplate, + complete: options => + utilitySelectorTemplate( + STATES[this.state], + this.utility, + options, + newUtility => { + this.utility = newUtility; + this._task.run(); + }, + ), + error: errorTemplate, + })} + ${this._task.status !== TaskStatus.INITIAL && + this._utilitiesTask.status === TaskStatus.COMPLETE + ? html`
` + : nothing} + ${this._task.render({ + pending: loadingTemplate, + complete: results => + this._utilitiesTask.status !== TaskStatus.COMPLETE + ? nothing + : stateIncentivesTemplate( + results, + this.selectedProject, + this.selectedOtherTab, + newSelection => (this.selectedOtherTab = newSelection), + ), + error: errorTemplate, + })} + ${CALCULATOR_FOOTER} +
+ `; + } +} + +/** + * Tell TypeScript that the HTML tag's type signature corresponds to the + * class's type signature. + */ +declare global { + interface HTMLElementTagNameMap { + 'rewiring-america-state-calculator': RewiringAmericaStateCalculator; + } +} diff --git a/src/state-incentive-details.ts b/src/state-incentive-details.ts new file mode 100644 index 0000000..31d11ef --- /dev/null +++ b/src/state-incentive-details.ts @@ -0,0 +1,436 @@ +import { css, html, nothing } from 'lit'; +import { APIResponse, Incentive, ItemType } from './api/calculator-types-v1'; +import { exclamationPoint, questionIcon, upRightArrow } from './icons'; +import { PROJECTS, Project, shortLabel } from './projects'; +import { iconTabBarTemplate } from './icon-tab-bar'; + +export const stateIncentivesStyles = css` + .loading { + text-align: center; + font-size: 2rem; + } + + .incentive { + display: flex; + flex-direction: column; + gap: 1rem; + height: 100%; + } + + .incentive__chip { + display: flex; + gap: 0.625rem; + justify-content: center; + align-items: center; + + background-color: #f0edf8; + border-radius: 0.25rem; + font-weight: 700; + font-size: 0.6875rem; + letter-spacing: 0.03438rem; + line-height: 125%; + padding: 0.25rem 0.625rem; + color: #111; + text-transform: uppercase; + width: fit-content; + } + + .incentive__chip--warning { + background-color: #fef2ca; + padding: 0.1875rem 0.625rem 0.1875rem 0.1875rem; + color: #846f24; + } + + .incentive__subtitle { + color: #111; + font-weight: 500; + line-height: 125%; + } + + .incentive__blurb { + color: #757575; + line-height: 150%; + } + + .incentive__title { + color: #111; + font-size: 1.5rem; + line-height: 150%; + } + + .incentive__link-button { + display: flex; + gap: 0.5rem; + justify-content: center; + align-items: center; + align-self: stretch; + + height: 2.25rem; + padding: 0.375rem 0.875rem; + + border-radius: 0.25rem; + border: 1px solid #9b9b9b; + + color: var(--rewiring-purple); + font-size: 1rem; + font-weight: 500; + line-height: 125%; + text-decoration: none; + } + + .nowrap { + white-space: nowrap; + } + + .grid-3-2-1, + .grid-4-2-1 { + display: grid; + gap: 1rem; + align-items: end; + } + .grid-3-2-1--align-start, + .grid-4-2-1--align-start { + align-items: start; + } + + @media only screen and (max-width: 640px) { + .grid-3-2-1, + .grid-4-2-1 { + grid-template-columns: 1fr; + } + } + + @media only screen and (min-width: 641px) and (max-width: 768px) { + .grid-3-2-1, + .grid-4-2-1 { + grid-template-columns: 1fr 1fr; + } + } + + @media only screen and (min-width: 769px) { + .grid-3-2-1 { + grid-template-columns: 1fr 1fr 1fr; + } + .grid-4-2-1 { + grid-template-columns: 1fr 1fr 1fr 1fr; + } + } + + .grid-section { + & .card { + margin: 0; + } + } + + @media only screen and (max-width: 640px) { + .grid-section { + margin: 0 1rem; + min-width: 200px; + } + } + + .grid-section__header { + margin-bottom: 1.5rem; + + color: #111; + text-align: center; + + font-size: 2rem; + font-weight: 500; + line-height: 125%; + } + + .summary { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 0.75rem; + flex: 1 0 0; + padding: 0.75rem; + } + + .summary__title { + width: 100%; + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + } + + .summary__caption { + color: #111; + + font-size: 0.6875rem; + font-weight: 700; + line-height: 125%; + letter-spacing: 0.03438rem; + text-transform: uppercase; + } + + .summary__body { + color: #111; + + font-size: 1.5rem; + font-weight: 400; + line-height: 165%; + } +`; + +export const cardStyles = css` + .card { + margin: 0; + + border: var(--ra-embed-card-border); + border-radius: 0.5rem; + box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, 0.08); + background-color: var(--ra-embed-card-background); + overflow: clip; + } + + /* Extra small devices */ + @media only screen and (max-width: 640px) { + .card { + margin: 0 1rem; + min-width: 200px; + } + } + + .card-content { + padding: 1rem; + display: grid; + grid-template-rows: min-content; + gap: 1rem; + } +`; + +export const separatorStyles = css` + .separator { + background: #e2e2e2; + width: 100%; + height: 1px; + } + + @media only screen and (max-width: 640px) { + .separator { + display: none; + } + } +`; + +const titleTemplate = (incentive: Incentive) => { + const item = itemName(incentive.item.type); + const amount = incentive.amount; + if (amount.type === 'dollar_amount') { + return amount.maximum + ? `Up to $${amount.maximum.toLocaleString()} off ${item}` + : `$${amount.number.toLocaleString()} off ${item}`; + } else if (amount.type === 'percent') { + const percentStr = `${Math.round(amount.number * 100)}%`; + return amount.maximum + ? `${percentStr} of cost of ${item}, up to $${amount.maximum.toLocaleString()}` + : `${percentStr} of cost of ${item}`; + } else if (amount.type === 'dollars_per_unit') { + const perUnitStr = `$${amount.number.toLocaleString()}/${amount.unit}`; + return amount.maximum + ? `${perUnitStr} off ${item}, up to $${amount.maximum.toLocaleString()}` + : `${perUnitStr} off ${item}`; + } else { + return nothing; + } +}; + +/** + * TODO this is an internationalization sin. Figure out something better! + */ +const itemName = (itemType: ItemType) => + itemType === 'battery_storage_installation' + ? 'battery storage' + : itemType === 'electric_panel' + ? 'an electric panel' + : itemType === 'electric_stove' + ? 'an electric/induction stove' + : itemType === 'electric_vehicle_charger' + ? 'an EV charger' + : itemType === 'electric_wiring' + ? 'electric wiring' + : itemType === 'geothermal_heating_installation' + ? 'geothermal heating installation' + : itemType === 'heat_pump_air_conditioner_heater' + ? 'a heat pump' + : itemType === 'heat_pump_clothes_dryer' + ? 'a heat pump clothes dryer' + : itemType === 'heat_pump_water_heater' + ? 'a heat pump water heater' + : itemType === 'new_electric_vehicle' + ? 'a new electric vehicle' + : itemType === 'rooftop_solar_installation' + ? 'rooftop solar' + : itemType === 'used_electric_vehicle' + ? 'a used electric vehicle' + : itemType === 'weatherization' + ? 'weatherization' + : null; + +const formatIncentiveType = (incentive: Incentive) => + incentive.type === 'tax_credit' + ? 'Tax credit' + : incentive.type === 'pos_rebate' + ? 'Upfront discount' + : incentive.type === 'rebate' + ? 'Rebate' + : incentive.type === 'account_credit' + ? 'Account credit' + : incentive.type === 'performance_rebate' + ? 'Performance rebate' + : 'Incentive'; + +/** TODO get real dates in the data! */ +const startDateTemplate = (incentive: Incentive) => + incentive.type === 'pos_rebate' + ? html`
+ ${exclamationPoint()} Available early 2024 +
` + : nothing; + +const incentiveCardTemplate = (incentive: Incentive) => html` +
+
+
+
${formatIncentiveType(incentive)}
+
${titleTemplate(incentive)}
+
${incentive.program}
+
+
${incentive.short_description}
+ ${startDateTemplate(incentive)} + + ${incentive.program_url ? 'Visit site' : 'Learn more'} + ${incentive.program_url ? upRightArrow() : nothing} + +
+
+
+`; + +const summaryBoxTemplate = ( + caption: string, + body: string, + tooltip: string, +) => html` +
+
+
+ ${caption} + + ${questionIcon(18, 18)} + +
+
${body}
+
+
+`; + +const atAGlanceTemplate = (response: APIResponse) => { + return html` +
+

Incentives at a glance

+
+ ${summaryBoxTemplate( + 'Upfront discounts', + `$${response.savings.pos_rebate.toLocaleString()}`, + "Money saved on a project's upfront costs.", + )} + ${summaryBoxTemplate( + 'Rebates', + `$${response.savings.rebate.toLocaleString()}`, + 'Money paid back to you after a project is completed.', + )} + ${summaryBoxTemplate( + 'Account credits', + `$${response.savings.account_credit.toLocaleString()}`, + 'Money credited to your utility account, going towards paying your next bills.', + )} + ${summaryBoxTemplate( + 'Tax credits', + `$${response.savings.tax_credit.toLocaleString()}`, + 'Your taxes may be reduced by up to this amount.', + )} +
+
+ `; +}; + +const gridTemplate = ( + heading: string, + incentives: Incentive[], + tabs: Project[], + selectedTab: Project, + onTabSelected: (newSelection: Project) => void, +) => + incentives.length > 0 + ? html` +
+

${heading}

+ ${iconTabBarTemplate(tabs, selectedTab, onTabSelected)} +
+ ${incentives.map(incentiveCardTemplate)} +
+
+ ` + : nothing; +/** + * Renders the "at a glance" summary section, a grid of incentive cards about + * the project you selected in the main form, then a grid of tab-bar switchable + * incentive cards about other projects. + * + * @param selectedProject The project whose incentives should get hoisted into + * their own section above all the others. + * @param selectedOtherTab The project among the "others" section whose tab is + * currently selected. + */ +export const stateIncentivesTemplate = ( + response: APIResponse, + selectedProject: Project, + selectedOtherTab: Project, + onTabSelected: (newSelection: Project) => void, +) => { + const allEligible = response.incentives.filter(i => i.eligible); + + const incentivesByProject = Object.fromEntries( + Object.entries(PROJECTS).map(([project, info]) => [ + project, + allEligible.filter(i => info.items.includes(i.item.type)), + ]), + ) as Record; + + // Only offer "other" tabs if there are incentives for that project. + const otherTabs = ( + Object.entries(incentivesByProject) as [Project, Incentive[]][] + ) + .filter( + ([project, incentives]) => + project !== selectedProject && incentives.length > 0, + ) + .sort(([a], [b]) => shortLabel(a).localeCompare(shortLabel(b))) + .map(([project]) => project); + + return html` ${atAGlanceTemplate(response)} + ${gridTemplate( + "Incentives you're interested in", + incentivesByProject[selectedProject] ?? [], + [selectedProject], + selectedProject, + () => {}, + )} + ${gridTemplate( + 'Other incentives available to you', + incentivesByProject[selectedOtherTab] ?? [], + otherTabs, + // If a nonexistent tab is selected, pretend the first one is selected. + otherTabs.includes(selectedOtherTab) ? selectedOtherTab : otherTabs[0], + onTabSelected, + )}`; +}; diff --git a/src/states.ts b/src/states.ts new file mode 100644 index 0000000..061eb42 --- /dev/null +++ b/src/states.ts @@ -0,0 +1,22 @@ +import { TemplateResult, svg } from 'lit'; + +const RI_ICON = + () => svg` + + + `; + +export type StateInfo = { + name: string; + icon: () => TemplateResult<2>; +}; + +/** + * States that are supported for customization. + */ +export const STATES: Record = { + RI: { + name: 'Rhode Island', + icon: RI_ICON, + }, +}; diff --git a/src/utility-selector.ts b/src/utility-selector.ts new file mode 100644 index 0000000..9100ed9 --- /dev/null +++ b/src/utility-selector.ts @@ -0,0 +1,132 @@ +import { css, html } from 'lit'; +import { questionIcon } from './icons'; +import { OptionParam, select } from './select'; +import { StateInfo } from './states'; + +export const utilitySelectorStyles = css` + .utility-selector { + display: grid; + align-items: center; + + & .map { + position: relative; + text-align: center; + + & svg { + vertical-align: top; + } + } + + & h1 { + position: absolute; + top: 50%; + transform: translate(0, -50%); + + font-weight: 700; + line-height: 125%; + + text-align: center; + font-size: 1.75rem; + } + + & .spacer { + /* Only relevant on small layout */ + height: 1.5rem; + } + + & .selector { + height: min-content; + } + } + + /* Extra small devices: map above selector */ + @media only screen and (max-width: 640px) { + .utility-selector { + grid-template-columns: 1fr; + + margin-left: 1rem; + margin-right: 1rem; + min-width: 200px; + + & .card { + /* Margin is provided by the outer element */ + margin: 0; + } + } + } + + /* Medium and large: map and selector side by side */ + @media only screen and (min-width: 641px) { + .utility-selector { + grid-template-columns: 5fr 2fr 5fr; + } + } + + /* Large: bigger text, left-aligned */ + @media only screen and (min-width: 769px) { + .utility-selector { + & h1 { + text-align: left; + font-size: 2.25rem; + } + } + } +`; + +const utilityFormTemplate = ( + utilityId: string, + utilityOptions: OptionParam[], + onChange: (utilityId: string) => void, +) => { + return html` +
+
+ +
+
+ `; +}; + +/** + * The state map + utility selector section. They're displayed side-by-side on + * large and medium layouts, and map above selector on small. + * + * Because the utility selector is outside the main calculator form, changing + * the selection should immediately reload incentives (as opposed to waiting for + * a button press), so there's a callback for it here. + */ +export const utilitySelectorTemplate = ( + stateInfo: StateInfo, + utilityId: string, + utilityOptions: OptionParam[], + onChange: (utilityId: string) => void, +) => + html`
+
+ ${stateInfo.icon()} +

Incentives available to you in ${stateInfo.name}

+
+
+
+ ${utilityFormTemplate(utilityId, utilityOptions, onChange)} +
+
`; From 00a59a5b3beb47627a0faf1688e573a9ef7d8c5e Mon Sep 17 00:00:00 2001 From: Owen Yamauchi Date: Mon, 18 Sep 2023 18:24:35 -0400 Subject: [PATCH 03/21] minor fix --- src/state-calculator.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/state-calculator.ts b/src/state-calculator.ts index 178a1d4..df23cc7 100644 --- a/src/state-calculator.ts +++ b/src/state-calculator.ts @@ -117,9 +117,12 @@ export class RewiringAmericaStateCalculator extends LitElement { this.selectedProject = (formData.get('project') as Project) || ''; // Zip is the only thing that determines what utilities are available, so - // only fetch utilities if zip has changed since last calculation. - const zipChanged = this.zip !== prevZip; - if (zipChanged) { + // only fetch utilities if zip has changed since last calculation, or if + // utilities haven't been fetched yet at all. + if ( + this.zip !== prevZip || + this._utilitiesTask.status !== TaskStatus.COMPLETE + ) { // This will run _task when it's done. this._utilitiesTask.run(); } else { From 7c160df86393a8d12205d3c79622ac4ca2c35561 Mon Sep 17 00:00:00 2001 From: ayangster Date: Tue, 19 Sep 2023 09:27:11 -0700 Subject: [PATCH 04/21] Run prettier and remove errant brace --- src/calculator-form.ts | 248 ++++++++++++++++++++--------------------- src/calculator.ts | 6 +- src/select.ts | 2 +- 3 files changed, 127 insertions(+), 129 deletions(-) diff --git a/src/calculator-form.ts b/src/calculator-form.ts index 1df32ed..7a034e1 100644 --- a/src/calculator-form.ts +++ b/src/calculator-form.ts @@ -85,129 +85,127 @@ export const formTemplate = ( onSubmit: (e: SubmitEvent) => void, gridClass: string = 'grid-3-2', ) => html` -
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
+ +
+
+
- - `; -}; +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +`; diff --git a/src/calculator.ts b/src/calculator.ts index 756bcfc..1bebb5a 100644 --- a/src/calculator.ts +++ b/src/calculator.ts @@ -74,8 +74,8 @@ export class RewiringAmericaCalculator extends LitElement { @property({ type: String, attribute: 'owner-status' }) ownerStatus: OwnerStatus = 'homeowner'; - @property({ type: Array, attribute: 'projects'}) - projects: string[] = ['heating'] + @property({ type: Array, attribute: 'projects' }) + projects: string[] = ['heating']; @property({ type: String, attribute: 'household-income' }) householdIncome: string = '0'; @@ -101,7 +101,7 @@ export class RewiringAmericaCalculator extends LitElement { return !( this.zip && this.ownerStatus && - this.projects && + this.projects && this.taxFiling && this.householdIncome && this.householdSize diff --git a/src/select.ts b/src/select.ts index 8bd395a..924abe5 100644 --- a/src/select.ts +++ b/src/select.ts @@ -34,7 +34,7 @@ export const option = ({ label, value }: OptionParam, selected: boolean) => html` `; export const multioption = ({ label, value }: OptionParam) => - html` ${label} `; + html` ${label} `; export const select = ({ id, From 2bb9b8ead958dc4674f7646ac4668d2a4067dfc5 Mon Sep 17 00:00:00 2001 From: ayangster Date: Tue, 19 Sep 2023 11:27:27 -0700 Subject: [PATCH 05/21] Update incentive functionality to expect multiple selected projects --- src/calculator-form.ts | 237 +++++++++++++++++---------------- src/calculator.ts | 9 +- src/state-calculator.ts | 19 ++- src/state-incentive-details.ts | 18 +-- 4 files changed, 144 insertions(+), 139 deletions(-) diff --git a/src/calculator-form.ts b/src/calculator-form.ts index 7a034e1..c074310 100644 --- a/src/calculator-form.ts +++ b/src/calculator-form.ts @@ -1,4 +1,4 @@ -import { html, css } from 'lit'; +import { html, css, nothing } from 'lit'; import { downIcon, questionIcon } from './icons'; import { select, multiselect, selectStyles, OptionParam } from './select'; import { inputStyles } from './styles/input'; @@ -58,11 +58,6 @@ const OWNER_STATUS_OPTIONS: OptionParam[] = [ { value: 'renter', label: 'Renter' }, ]; -const PROJECT_OPTIONS: OptionParam[] = [ - { value: 'heating', label: 'Heating' }, - { value: 'electrical', label: 'Electrical wiring' }, -]; - const TAX_FILING_OPTIONS: OptionParam[] = [ { value: 'single', label: 'Single' }, { value: 'joint', label: 'Married Filing Jointly' }, @@ -82,16 +77,16 @@ const HOUSEHOLD_SIZE_OPTIONS: OptionParam[] = [1, 2, 3, 4, 5, 6, 7, 8].map( export const formTemplate = ( [zip, ownerStatus, householdIncome, taxFiling, householdSize]: Array, projects: Array, + showProjectField: boolean, onSubmit: (e: SubmitEvent) => void, gridClass: string = 'grid-3-2', -) => html` -
-
-
-
- -`; + + `; +}; diff --git a/src/calculator.ts b/src/calculator.ts index 1bebb5a..2c62ed1 100644 --- a/src/calculator.ts +++ b/src/calculator.ts @@ -74,9 +74,6 @@ export class RewiringAmericaCalculator extends LitElement { @property({ type: String, attribute: 'owner-status' }) ownerStatus: OwnerStatus = 'homeowner'; - @property({ type: Array, attribute: 'projects' }) - projects: string[] = ['heating']; - @property({ type: String, attribute: 'household-income' }) householdIncome: string = '0'; @@ -91,7 +88,6 @@ export class RewiringAmericaCalculator extends LitElement { const formData = new FormData(e.target as HTMLFormElement); this.zip = (formData.get('zip') as string) || ''; this.ownerStatus = (formData.get('owner_status') as OwnerStatus) || ''; - this.projects = (formData.get('projects') as string) || ''; this.householdIncome = (formData.get('household_income') as string) || ''; this.taxFiling = (formData.get('tax_filing') as FilingStatus) || ''; this.householdSize = (formData.get('household_size') as string) || ''; @@ -101,7 +97,6 @@ export class RewiringAmericaCalculator extends LitElement { return !( this.zip && this.ownerStatus && - this.projects && this.taxFiling && this.householdIncome && this.householdSize @@ -155,14 +150,14 @@ export class RewiringAmericaCalculator extends LitElement { ? nothing : formTemplate( [ - '', this.zip, this.ownerStatus, this.householdIncome, this.taxFiling, this.householdSize, ], - this.projects, + [], + false, (event: SubmitEvent) => this.submit(event), )}
diff --git a/src/state-calculator.ts b/src/state-calculator.ts index df23cc7..b13b1f7 100644 --- a/src/state-calculator.ts +++ b/src/state-calculator.ts @@ -99,11 +99,14 @@ export class RewiringAmericaStateCalculator extends LitElement { @property({ type: String }) utility: string = ''; + @property({ type: Array }) + projects: Project[] = ['battery']; + @property({ type: String }) - selectedProject: Project = 'hvac'; + selectedProjectTab: Project = 'battery'; @property({ type: String }) - selectedOtherTab: Project = 'heat_pump_clothes_dryer'; + selectedOtherTab: Project = 'battery'; submit(e: SubmitEvent) { e.preventDefault(); @@ -114,7 +117,7 @@ export class RewiringAmericaStateCalculator extends LitElement { this.householdIncome = (formData.get('household_income') as string) || ''; this.taxFiling = (formData.get('tax_filing') as FilingStatus) || ''; this.householdSize = (formData.get('household_size') as string) || ''; - this.selectedProject = (formData.get('project') as Project) || ''; + this.projects = (formData.getAll('projects') as Project[]) || ''; // Zip is the only thing that determines what utilities are available, so // only fetch utilities if zip has changed since last calculation, or if @@ -137,7 +140,7 @@ export class RewiringAmericaStateCalculator extends LitElement { this.taxFiling && this.householdIncome && this.householdSize && - this.selectedProject + this.projects ); } @@ -206,13 +209,13 @@ export class RewiringAmericaStateCalculator extends LitElement { ? nothing : formTemplate( [ - this.selectedProject, this.zip, this.ownerStatus, this.householdIncome, this.taxFiling, this.householdSize, ], + this.projects, true, (event: SubmitEvent) => this.submit(event), 'grid-3-2-1', @@ -243,9 +246,11 @@ export class RewiringAmericaStateCalculator extends LitElement { ? nothing : stateIncentivesTemplate( results, - this.selectedProject, + this.projects, + this.selectedProjectTab, this.selectedOtherTab, - newSelection => (this.selectedOtherTab = newSelection), + newSelection => (this.selectedProjectTab = newSelection), + newOtherSelection => (this.selectedOtherTab = newOtherSelection), ), error: errorTemplate, })} diff --git a/src/state-incentive-details.ts b/src/state-incentive-details.ts index 31d11ef..a332b4f 100644 --- a/src/state-incentive-details.ts +++ b/src/state-incentive-details.ts @@ -370,7 +370,7 @@ const gridTemplate = ( selectedTab: Project, onTabSelected: (newSelection: Project) => void, ) => - incentives.length > 0 + tabs.length > 0 && incentives.length > 0 ? html`

${heading}

@@ -393,9 +393,11 @@ const gridTemplate = ( */ export const stateIncentivesTemplate = ( response: APIResponse, - selectedProject: Project, + selectedProjects: Project[], + selectedProjectTab: Project, selectedOtherTab: Project, onTabSelected: (newSelection: Project) => void, + onOtherTabSelected: (newOtherSelection: Project) => void, ) => { const allEligible = response.incentives.filter(i => i.eligible); @@ -412,7 +414,7 @@ export const stateIncentivesTemplate = ( ) .filter( ([project, incentives]) => - project !== selectedProject && incentives.length > 0, + !selectedProjects.includes(project) && incentives.length > 0, ) .sort(([a], [b]) => shortLabel(a).localeCompare(shortLabel(b))) .map(([project]) => project); @@ -420,10 +422,10 @@ export const stateIncentivesTemplate = ( return html` ${atAGlanceTemplate(response)} ${gridTemplate( "Incentives you're interested in", - incentivesByProject[selectedProject] ?? [], - [selectedProject], - selectedProject, - () => {}, + incentivesByProject[selectedProjectTab] ?? [], + selectedProjects, + selectedProjects.includes(selectedProjectTab) ? selectedProjectTab : selectedProjects[0], + onTabSelected, )} ${gridTemplate( 'Other incentives available to you', @@ -431,6 +433,6 @@ export const stateIncentivesTemplate = ( otherTabs, // If a nonexistent tab is selected, pretend the first one is selected. otherTabs.includes(selectedOtherTab) ? selectedOtherTab : otherTabs[0], - onTabSelected, + onOtherTabSelected, )}`; }; From 007016f265c45db5879f00ef1e7293458c600e01 Mon Sep 17 00:00:00 2001 From: ayangster Date: Tue, 19 Sep 2023 15:25:19 -0700 Subject: [PATCH 06/21] Stylistic changes to multiselect --- src/select.ts | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/select.ts b/src/select.ts index 924abe5..1e64440 100644 --- a/src/select.ts +++ b/src/select.ts @@ -222,31 +222,45 @@ export const selectStyles = css` } sl-select { - --sl-input-height-medium: 3rem; + --sl-input-height-medium: 2.8215rem; + --sl-input-focus-ring-color: var(--select-focus); + --sl-input-focus-ring-style: solid; + --sl-focus-ring-width: 1px; + + --sl-input-border-width: 1px; + --sl-input-border-color-focus: var(--select-focus); + + margin-top: 4px; } // parts go largest to smallest + // wraps label, input and helptext - sl-select::parts(form-control) { + sl-select::part(form-control) { } // don't need we are using html label - sl-select::parts(form-control-label) { + sl-select::part(form-control-label) { } // select wrapper - sl-select::parts(form-control-input) { + sl-select::part(form-control-input) { } // help text - sl-select::parts(form-control-help-text) { + sl-select::part(form-control-help-text) { } // The container the wraps the prefix, combobox, clear icon, and expand button. - sl-select::parts(combobox) { + sl-select::part(combobox) { } // The container that wraps the prefix slot (geat icon?) - sl-select::parts(prefix) { + sl-select::part(prefix) { } // selected option label - sl-select::parts(display-input) { + sl-select::part(display-input) { } // the container where options are listed - sl-select::parts(listbox) { + sl-select::part(listbox) { + } + + sl-select::part(expand-icon) { + width: 0.75em; + height: 0.5em; } `; From 95021b1cac55a604283dbe55baf70793b32f98e1 Mon Sep 17 00:00:00 2001 From: ayangster Date: Wed, 20 Sep 2023 15:05:18 -0700 Subject: [PATCH 07/21] Finalize integration of multiselect into state calculator --- .parcelrc | 4 - package.json | 1 - src/calculator-form.ts | 2 + src/select.ts | 31 ++- src/state-calculator.ts | 11 +- src/state-incentive-details.ts | 19 +- static/assets/icons/caret-down-fill.svg | 3 - yarn.lock | 267 +----------------------- 8 files changed, 48 insertions(+), 290 deletions(-) delete mode 100644 .parcelrc delete mode 100644 static/assets/icons/caret-down-fill.svg diff --git a/.parcelrc b/.parcelrc deleted file mode 100644 index 792968a..0000000 --- a/.parcelrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": ["@parcel/config-default"], - "reporters": ["...", "parcel-reporter-static-files-copy"] -} \ No newline at end of file diff --git a/package.json b/package.json index 9785e8d..fa4c954 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,6 @@ "cypress": "^12.6.0", "eslint": "^8.45.0", "parcel": "v2.8.3", - "parcel-reporter-static-files-copy": "^1.5.2", "postcss": "^8.4.21", "postcss-modules": "^6.0.0", "posthtml": "^0.16.6", diff --git a/src/calculator-form.ts b/src/calculator-form.ts index c074310..4a77141 100644 --- a/src/calculator-form.ts +++ b/src/calculator-form.ts @@ -96,7 +96,9 @@ export const formTemplate = ( .map(([value, data]) => ({ value, label: data.label })) .sort((a, b) => a.label.localeCompare(b.label)), currentValues: projects, + placeholder: 'None selected', maxOptionsVisible: 1, + placement: 'top', })}
` diff --git a/src/select.ts b/src/select.ts index 1e64440..2271ceb 100644 --- a/src/select.ts +++ b/src/select.ts @@ -20,13 +20,24 @@ export interface SelectParam { disabled?: boolean; } -export interface MultiSelectParam { +export interface SLSelectParam { id: string; label?: string; - currentValues: string[]; options: OptionParam[]; helpText?: string; placeholder?: string; + placement?: string; + required?: boolean; + ariaLabel?: string; +} + +export interface SingleSelectParam extends SLSelectParam { + currentValue: string; + onChange?: (event: SlChangeEvent) => void; +} + +export interface MultiSelectParam extends SLSelectParam { + currentValues: string[]; maxOptionsVisible?: number; } @@ -72,6 +83,7 @@ export const multiselect = ({ helpText, placeholder, maxOptionsVisible, + placement, }: MultiSelectParam) => { return html`
@@ -83,10 +95,12 @@ export const multiselect = ({ help-text="${ifDefined(helpText)}" placeholder="${ifDefined(placeholder)}" max-options-visible="${ifDefined(maxOptionsVisible)}" + placement="${ifDefined(placement)}" + hoist multiple clearable > - + ${options.map(o => multioption(o))} @@ -223,6 +237,9 @@ export const selectStyles = css` sl-select { --sl-input-height-medium: 2.8215rem; + + --sl-input-font-family: var(--ra-embed-font-family); + --sl-input-focus-ring-color: var(--select-focus); --sl-input-focus-ring-style: solid; --sl-focus-ring-width: 1px; @@ -260,7 +277,11 @@ export const selectStyles = css` } sl-select::part(expand-icon) { - width: 0.75em; - height: 0.5em; + content: ''; + justify-self: end; + width: 0.6em; + height: 0.4em; + background-color: var(--select-arrow); + clip-path: polygon(100% 0%, 0 0%, 50% 100%); } `; diff --git a/src/state-calculator.ts b/src/state-calculator.ts index b13b1f7..98cca7a 100644 --- a/src/state-calculator.ts +++ b/src/state-calculator.ts @@ -100,10 +100,10 @@ export class RewiringAmericaStateCalculator extends LitElement { utility: string = ''; @property({ type: Array }) - projects: Project[] = ['battery']; + projects: Project[] = []; @property({ type: String }) - selectedProjectTab: Project = 'battery'; + selectedProjectTab: Project | undefined; @property({ type: String }) selectedOtherTab: Project = 'battery'; @@ -247,10 +247,11 @@ export class RewiringAmericaStateCalculator extends LitElement { : stateIncentivesTemplate( results, this.projects, - this.selectedProjectTab, - this.selectedOtherTab, + newOtherSelection => + (this.selectedOtherTab = newOtherSelection), newSelection => (this.selectedProjectTab = newSelection), - newOtherSelection => (this.selectedOtherTab = newOtherSelection), + this.selectedOtherTab, + this.selectedProjectTab, ), error: errorTemplate, })} diff --git a/src/state-incentive-details.ts b/src/state-incentive-details.ts index a332b4f..9ee3d49 100644 --- a/src/state-incentive-details.ts +++ b/src/state-incentive-details.ts @@ -394,10 +394,10 @@ const gridTemplate = ( export const stateIncentivesTemplate = ( response: APIResponse, selectedProjects: Project[], - selectedProjectTab: Project, - selectedOtherTab: Project, - onTabSelected: (newSelection: Project) => void, onOtherTabSelected: (newOtherSelection: Project) => void, + onTabSelected: (newSelection: Project) => void, + selectedOtherTab: Project, + selectedProjectTab?: Project, ) => { const allEligible = response.incentives.filter(i => i.eligible); @@ -419,16 +419,23 @@ export const stateIncentivesTemplate = ( .sort(([a], [b]) => shortLabel(a).localeCompare(shortLabel(b))) .map(([project]) => project); + const projectTab = selectedProjectTab ?? selectedProjects[0]; + const selectedIncentives = incentivesByProject[projectTab] ?? []; + const otherIncentivesLabel = + selectedIncentives.length == 0 + ? 'Incentives available to you' + : 'Other incentives available to you'; + return html` ${atAGlanceTemplate(response)} ${gridTemplate( "Incentives you're interested in", - incentivesByProject[selectedProjectTab] ?? [], + selectedIncentives, selectedProjects, - selectedProjects.includes(selectedProjectTab) ? selectedProjectTab : selectedProjects[0], + selectedProjects.includes(projectTab) ? projectTab : selectedProjects[0], onTabSelected, )} ${gridTemplate( - 'Other incentives available to you', + otherIncentivesLabel, incentivesByProject[selectedOtherTab] ?? [], otherTabs, // If a nonexistent tab is selected, pretend the first one is selected. diff --git a/static/assets/icons/caret-down-fill.svg b/static/assets/icons/caret-down-fill.svg deleted file mode 100644 index d7c3990..0000000 --- a/static/assets/icons/caret-down-fill.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index cd82969..ddef129 100644 --- a/yarn.lock +++ b/yarn.lock @@ -214,61 +214,31 @@ resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.5.2.tgz#bc66fa43286b5c082e8fee0eacc17995806b6fbe" integrity sha512-+F8ioQIUN68B4UFiIBYu0QQvgb9FmlKw2ctQMSBfW2QBrZIxz9vD9jCGqTCPqZBRbPHAS/vG1zSXnKqnS2ch/A== -"@lmdb/lmdb-darwin-arm64@2.7.11": - version "2.7.11" - resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.7.11.tgz#b717e72f023d4215d14e4c57433c711a53c782cf" - integrity sha512-r6+vYq2vKzE+vgj/rNVRMwAevq0+ZR9IeMFIqcSga+wMtMdXQ27KqQ7uS99/yXASg29bos7yHP3yk4x6Iio0lw== - "@lmdb/lmdb-darwin-x64@2.5.2": version "2.5.2" resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.5.2.tgz#89d8390041bce6bab24a82a20392be22faf54ffc" integrity sha512-KvPH56KRLLx4KSfKBx0m1r7GGGUMXm0jrKmNE7plbHlesZMuPJICtn07HYgQhj1LNsK7Yqwuvnqh1QxhJnF1EA== -"@lmdb/lmdb-darwin-x64@2.7.11": - version "2.7.11" - resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.7.11.tgz#b42898b0742b4a82b8224b742b2d174c449cd170" - integrity sha512-jhj1aB4K8ycRL1HOQT5OtzlqOq70jxUQEWRN9Gqh3TIDN30dxXtiHi6EWF516tzw6v2+3QqhDMJh8O6DtTGG8Q== - "@lmdb/lmdb-linux-arm64@2.5.2": version "2.5.2" resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.5.2.tgz#14fe4c96c2bb1285f93797f45915fa35ee047268" integrity sha512-aLl89VHL/wjhievEOlPocoefUyWdvzVrcQ/MHQYZm2JfV1jUsrbr/ZfkPPUFvZBf+VSE+Q0clWs9l29PCX1hTQ== -"@lmdb/lmdb-linux-arm64@2.7.11": - version "2.7.11" - resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.7.11.tgz#a8dc8e386d27006cfccbf2a8598290b63d03a9ec" - integrity sha512-7xGEfPPbmVJWcY2Nzqo11B9Nfxs+BAsiiaY/OcT4aaTDdykKeCjvKMQJA3KXCtZ1AtiC9ljyGLi+BfUwdulY5A== - "@lmdb/lmdb-linux-arm@2.5.2": version "2.5.2" resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.5.2.tgz#05bde4573ab10cf21827339fe687148f2590cfa1" integrity sha512-5kQAP21hAkfW5Bl+e0P57dV4dGYnkNIpR7f/GAh6QHlgXx+vp/teVj4PGRZaKAvt0GX6++N6hF8NnGElLDuIDw== -"@lmdb/lmdb-linux-arm@2.7.11": - version "2.7.11" - resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.7.11.tgz#2103f48af28336efccaac008fe882dfce33e4ac5" - integrity sha512-dHfLFVSrw/v5X5lkwp0Vl7+NFpEeEYKfMG2DpdFJnnG1RgHQZngZxCaBagFoaJGykRpd2DYF1AeuXBFrAUAXfw== - "@lmdb/lmdb-linux-x64@2.5.2": version "2.5.2" resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.5.2.tgz#d2f85afd857d2c33d2caa5b057944574edafcfee" integrity sha512-xUdUfwDJLGjOUPH3BuPBt0NlIrR7f/QHKgu3GZIXswMMIihAekj2i97oI0iWG5Bok/b+OBjHPfa8IU9velnP/Q== -"@lmdb/lmdb-linux-x64@2.7.11": - version "2.7.11" - resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.7.11.tgz#d21ac368022a662610540f2ba8bb6ff0b96a9940" - integrity sha512-vUKI3JrREMQsXX8q0Eq5zX2FlYCKWMmLiCyyJNfZK0Uyf14RBg9VtB3ObQ41b4swYh2EWaltasWVe93Y8+KDng== - "@lmdb/lmdb-win32-x64@2.5.2": version "2.5.2" resolved "https://registry.yarnpkg.com/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.5.2.tgz#28f643fbc0bec30b07fbe95b137879b6b4d1c9c5" integrity sha512-zrBczSbXKxEyK2ijtbRdICDygRqWSRPpZMN5dD1T8VMEW5RIhIbwFWw2phDRXuBQdVDpSjalCIUMWMV2h3JaZA== -"@lmdb/lmdb-win32-x64@2.7.11": - version "2.7.11" - resolved "https://registry.yarnpkg.com/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.7.11.tgz#af2cb4ae6d3a92ecdeb1503b73079417525476d2" - integrity sha512-BJwkHlSUgtB+Ei52Ai32M1AOMerSlzyIGA/KC4dAGL+GGwVMdwG8HGCOA2TxP3KjhbgDPMYkv7bt/NmOmRIFng== - "@mischnic/json-sourcemap@^0.1.0": version "0.1.0" resolved "https://registry.yarnpkg.com/@mischnic/json-sourcemap/-/json-sourcemap-0.1.0.tgz#38af657be4108140a548638267d02a2ea3336507" @@ -283,61 +253,31 @@ resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.0.tgz#d31a238c943ffc34bab73ad6ce7a6466d65888ef" integrity sha512-5qpnNHUyyEj9H3sm/4Um/bnx1lrQGhe8iqry/1d+cQYCRd/gzYA0YLeq0ezlk4hKx4vO+dsEsNyeowqRqslwQA== -"@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.2.tgz#44d752c1a2dc113f15f781b7cc4f53a307e3fa38" - integrity sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ== - "@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.0.tgz#2f6fbbec3d3f0bbe9c6678c899f1c1a6e25ed980" integrity sha512-ZphTFFd6SFweNAMKD+QJCrWpgkjf4qBuHltiMkKkD6FFrB3NOTRVmetAGTkJ57pa+s6J0yCH06LujWB9rZe94g== -"@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.2.tgz#f954f34355712212a8e06c465bc06c40852c6bb3" - integrity sha512-lwriRAHm1Yg4iDf23Oxm9n/t5Zpw1lVnxYU3HnJPTi2lJRkKTrps1KVgvL6m7WvmhYVt/FIsssWay+k45QHeuw== - "@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.0.tgz#19875441da50b9aa8f8e726eb097a4cead435a3f" integrity sha512-NEX6hdSvP4BmVyegaIbrGxvHzHvTzzsPaxXCsUt0mbLbPpEftsvNwaEVKOowXnLoeuGeD4MaqSwL3BUK2elsUA== -"@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.2.tgz#45c63037f045c2b15c44f80f0393fa24f9655367" - integrity sha512-FU20Bo66/f7He9Fp9sP2zaJ1Q8L9uLPZQDub/WlUip78JlPeMbVL8546HbZfcW9LNciEXc8d+tThSJjSC+tmsg== - "@msgpackr-extract/msgpackr-extract-linux-arm@3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.0.tgz#3b855ac72cc16e89db2f72adf47ddc964c20a53d" integrity sha512-ztKVV1dO/sSZyGse0PBCq3Pk1PkYjsA/dsEWE7lfrGoAK3i9HpS2o7XjGQ7V4va6nX+xPPOiuYpQwa4Bi6vlww== -"@msgpackr-extract/msgpackr-extract-linux-arm@3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.2.tgz#35707efeafe6d22b3f373caf9e8775e8920d1399" - integrity sha512-MOI9Dlfrpi2Cuc7i5dXdxPbFIgbDBGgKR5F2yWEa6FVEtSWncfVNKW5AKjImAQ6CZlBK9tympdsZJ2xThBiWWA== - "@msgpackr-extract/msgpackr-extract-linux-x64@3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.0.tgz#455f1d5bb00e87f78c67711f26e7bff9f1457684" integrity sha512-9uvdAkZMOPCY7SPRxZLW8XGqBOVNVEhqlgffenN8shA1XR9FWVsSM13nr/oHtNgXg6iVyML7RwWPyqUeThlwxg== -"@msgpackr-extract/msgpackr-extract-linux-x64@3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.2.tgz#091b1218b66c341f532611477ef89e83f25fae4f" - integrity sha512-gsWNDCklNy7Ajk0vBBf9jEx04RUxuDQfBse918Ww+Qb9HCPoGzS+XJTLe96iN3BVK7grnLiYghP/M4L8VsaHeA== - "@msgpackr-extract/msgpackr-extract-win32-x64@3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.0.tgz#03c6bfcd3acb179ea69546c20d50895b9d623ada" integrity sha512-Wg0+9615kHKlr9iLVcG5I+/CHnf6w3x5UADRv8Ad16yA0Bu5l9eVOROjV7aHPG6uC8ZPFIVVaoSjDChD+Y0pzg== -"@msgpackr-extract/msgpackr-extract-win32-x64@3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.2.tgz#0f164b726869f71da3c594171df5ebc1c4b0a407" - integrity sha512-O+6Gs8UeDbyFpbSh2CPEz/UOrrdWPTBYNblZK5CxxLisYt4kGX3Sc+czffFonyjiGSq3jWLwJS/CCJc7tBr4sQ== - "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -381,16 +321,6 @@ "@parcel/utils" "2.8.3" lmdb "2.5.2" -"@parcel/cache@2.9.3": - version "2.9.3" - resolved "https://registry.yarnpkg.com/@parcel/cache/-/cache-2.9.3.tgz#3ed40b79858fcb7c2c73c0ed4c9807cf2388c8b4" - integrity sha512-Bj/H2uAJJSXtysG7E/x4EgTrE2hXmm7td/bc97K8M9N7+vQjxf7xb0ebgqe84ePVMkj4MVQSMEJkEucXVx4b0Q== - dependencies: - "@parcel/fs" "2.9.3" - "@parcel/logger" "2.9.3" - "@parcel/utils" "2.9.3" - lmdb "2.7.11" - "@parcel/codeframe@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/codeframe/-/codeframe-2.8.3.tgz#84fb529ef70def7f5bc64f6c59b18d24826f5fcc" @@ -398,13 +328,6 @@ dependencies: chalk "^4.1.0" -"@parcel/codeframe@2.9.3": - version "2.9.3" - resolved "https://registry.yarnpkg.com/@parcel/codeframe/-/codeframe-2.9.3.tgz#056cacaeedae9318878bdee8ffc584178b10ba42" - integrity sha512-z7yTyD6h3dvduaFoHpNqur74/2yDWL++33rjQjIjCaXREBN6dKHoMGMizzo/i4vbiI1p9dDox2FIDEHCMQxqdA== - dependencies: - chalk "^4.1.0" - "@parcel/compressor-raw@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/compressor-raw/-/compressor-raw-2.8.3.tgz#301753df8c6de967553149639e8a4179b88f0c95" @@ -486,24 +409,11 @@ "@mischnic/json-sourcemap" "^0.1.0" nullthrows "^1.1.1" -"@parcel/diagnostic@2.9.3": - version "2.9.3" - resolved "https://registry.yarnpkg.com/@parcel/diagnostic/-/diagnostic-2.9.3.tgz#23befe6c3b78440fe1e3635086e637da1529b4db" - integrity sha512-6jxBdyB3D7gP4iE66ghUGntWt2v64E6EbD4AetZk+hNJpgudOOPsKTovcMi/i7I4V0qD7WXSF4tvkZUoac0jwA== - dependencies: - "@mischnic/json-sourcemap" "^0.1.0" - nullthrows "^1.1.1" - "@parcel/events@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/events/-/events-2.8.3.tgz#205f8d874e6ecc2cbdb941bf8d54bae669e571af" integrity sha512-hoIS4tAxWp8FJk3628bsgKxEvR7bq2scCVYHSqZ4fTi/s0+VymEATrRCUqf+12e5H47uw1/ZjoqrGtBI02pz4w== -"@parcel/events@2.9.3": - version "2.9.3" - resolved "https://registry.yarnpkg.com/@parcel/events/-/events-2.9.3.tgz#b71253384c21f53fd3cced983cd2b287f7330e89" - integrity sha512-K0Scx+Bx9f9p1vuShMzNwIgiaZUkxEnexaKYHYemJrM7pMAqxIuIqhnvwurRCsZOVLUJPDDNJ626cWTc5vIq+A== - "@parcel/fs-search@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/fs-search/-/fs-search-2.8.3.tgz#1c7d812c110b808758f44c56e61dfffdb09e9451" @@ -511,11 +421,6 @@ dependencies: detect-libc "^1.0.3" -"@parcel/fs-search@2.9.3": - version "2.9.3" - resolved "https://registry.yarnpkg.com/@parcel/fs-search/-/fs-search-2.9.3.tgz#4993d68478b15db404149a271bb0084382dd2040" - integrity sha512-nsNz3bsOpwS+jphcd+XjZL3F3PDq9lik0O8HPm5f6LYkqKWT+u/kgQzA8OkAHCR3q96LGiHxUywHPEBc27vI4Q== - "@parcel/fs@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/fs/-/fs-2.8.3.tgz#80536afe877fc8a2bd26be5576b9ba27bb4c5754" @@ -527,17 +432,6 @@ "@parcel/watcher" "^2.0.7" "@parcel/workers" "2.8.3" -"@parcel/fs@2.9.3": - version "2.9.3" - resolved "https://registry.yarnpkg.com/@parcel/fs/-/fs-2.9.3.tgz#39abd0f71561efccaac3ba6e4b8227705b73e906" - integrity sha512-/PrRKgCRw22G7rNPSpgN3Q+i2nIkZWuvIOAdMG4KWXC4XLp8C9jarNaWd5QEQ75amjhQSl3oUzABzkdCtkKrgg== - dependencies: - "@parcel/fs-search" "2.9.3" - "@parcel/types" "2.9.3" - "@parcel/utils" "2.9.3" - "@parcel/watcher" "^2.0.7" - "@parcel/workers" "2.9.3" - "@parcel/graph@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/graph/-/graph-2.8.3.tgz#00ffe8ec032e74fee57199e54529f1da7322571d" @@ -553,13 +447,6 @@ detect-libc "^1.0.3" xxhash-wasm "^0.4.2" -"@parcel/hash@2.9.3": - version "2.9.3" - resolved "https://registry.yarnpkg.com/@parcel/hash/-/hash-2.9.3.tgz#bc7727939b1211b0a5d67fd00a9a55b8393c644a" - integrity sha512-qlH5B85XLzVAeijgKPjm1gQu35LoRYX/8igsjnN8vOlbc3O8BYAUIutU58fbHbtE8MJPbxQQUw7tkTjeoujcQQ== - dependencies: - xxhash-wasm "^0.4.2" - "@parcel/logger@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/logger/-/logger-2.8.3.tgz#e14e4debafb3ca9e87c07c06780f9afc38b2712c" @@ -568,14 +455,6 @@ "@parcel/diagnostic" "2.8.3" "@parcel/events" "2.8.3" -"@parcel/logger@2.9.3": - version "2.9.3" - resolved "https://registry.yarnpkg.com/@parcel/logger/-/logger-2.9.3.tgz#04362704d7af93d213de6587ff71a1a6d5f714ac" - integrity sha512-5FNBszcV6ilGFcijEOvoNVG6IUJGsnMiaEnGQs7Fvc1dktTjEddnoQbIYhcSZL63wEmzBZOgkT5yDMajJ/41jw== - dependencies: - "@parcel/diagnostic" "2.9.3" - "@parcel/events" "2.9.3" - "@parcel/markdown-ansi@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/markdown-ansi/-/markdown-ansi-2.8.3.tgz#1337d421bb1133ad178f386a8e1b746631bba4a1" @@ -583,13 +462,6 @@ dependencies: chalk "^4.1.0" -"@parcel/markdown-ansi@2.9.3": - version "2.9.3" - resolved "https://registry.yarnpkg.com/@parcel/markdown-ansi/-/markdown-ansi-2.9.3.tgz#b4de64eb252ce13e27f6e24e420b607db51097a5" - integrity sha512-/Q4X8F2aN8UNjAJrQ5NfK2OmZf6shry9DqetUSEndQ0fHonk78WKt6LT0zSKEBEW/bB/bXk6mNMsCup6L8ibjQ== - dependencies: - chalk "^4.1.0" - "@parcel/namer-default@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/namer-default/-/namer-default-2.8.3.tgz#5304bee74beb4b9c1880781bdbe35be0656372f4" @@ -609,18 +481,6 @@ nullthrows "^1.1.1" semver "^5.7.1" -"@parcel/node-resolver-core@3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@parcel/node-resolver-core/-/node-resolver-core-3.0.3.tgz#cc890e66695b6d28745415106565499af9cb3c47" - integrity sha512-AjxNcZVHHJoNT/A99PKIdFtwvoze8PAiC3yz8E/dRggrDIOboUEodeQYV5Aq++aK76uz/iOP0tST2T8A5rhb1A== - dependencies: - "@mischnic/json-sourcemap" "^0.1.0" - "@parcel/diagnostic" "2.9.3" - "@parcel/fs" "2.9.3" - "@parcel/utils" "2.9.3" - nullthrows "^1.1.1" - semver "^7.5.2" - "@parcel/optimizer-css@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/optimizer-css/-/optimizer-css-2.8.3.tgz#420a333f4b78f7ff15e69217dfed34421b1143ee" @@ -691,20 +551,6 @@ "@parcel/workers" "2.8.3" semver "^5.7.1" -"@parcel/package-manager@2.9.3": - version "2.9.3" - resolved "https://registry.yarnpkg.com/@parcel/package-manager/-/package-manager-2.9.3.tgz#e8522671ba6c4f0a07b518957d22a038a7698b24" - integrity sha512-NH6omcNTEupDmW4Lm1e4NUYBjdqkURxgZ4CNESESInHJe6tblVhNB8Rpr1ar7zDar7cly9ILr8P6N3Ei7bTEjg== - dependencies: - "@parcel/diagnostic" "2.9.3" - "@parcel/fs" "2.9.3" - "@parcel/logger" "2.9.3" - "@parcel/node-resolver-core" "3.0.3" - "@parcel/types" "2.9.3" - "@parcel/utils" "2.9.3" - "@parcel/workers" "2.9.3" - semver "^7.5.2" - "@parcel/packager-css@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/packager-css/-/packager-css-2.8.3.tgz#0eff34268cb4f5dfb53c1bbca85f5567aeb1835a" @@ -763,22 +609,6 @@ dependencies: "@parcel/types" "2.8.3" -"@parcel/plugin@^2.0.0-beta.1": - version "2.9.3" - resolved "https://registry.yarnpkg.com/@parcel/plugin/-/plugin-2.9.3.tgz#90e9a9482fa27735494372f5643db01abcf3fdb6" - integrity sha512-qN85Gqr2GMuxX1dT1mnuO9hOcvlEv1lrYrCxn7CJN2nUhbwcfG+LEvcrCzCOJ6XtIHm+ZBV9h9p7FfoPLvpw+g== - dependencies: - "@parcel/types" "2.9.3" - -"@parcel/profiler@2.9.3": - version "2.9.3" - resolved "https://registry.yarnpkg.com/@parcel/profiler/-/profiler-2.9.3.tgz#6575ed6dc4275c0161dce74bd719961236673ce1" - integrity sha512-pyHc9lw8VZDfgZoeZWZU9J0CVEv1Zw9O5+e0DJPDPHuXJYr72ZAOhbljtU3owWKAeW+++Q2AZWkbUGEOjI/e6g== - dependencies: - "@parcel/diagnostic" "2.9.3" - "@parcel/events" "2.9.3" - chrome-trace-event "^1.0.2" - "@parcel/reporter-cli@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/reporter-cli/-/reporter-cli-2.8.3.tgz#12a4743b51b8fe6837f53c20e01bbf1f7336e8e4" @@ -996,19 +826,6 @@ "@parcel/workers" "2.8.3" utility-types "^3.10.0" -"@parcel/types@2.9.3": - version "2.9.3" - resolved "https://registry.yarnpkg.com/@parcel/types/-/types-2.9.3.tgz#170a26203b9088a306862b2dc914c27375d77bbc" - integrity sha512-NSNY8sYtRhvF1SqhnIGgGvJocyWt1K8Tnw5cVepm0g38ywtX6mwkBvMkmeehXkII4mSUn+frD9wGsydTunezvA== - dependencies: - "@parcel/cache" "2.9.3" - "@parcel/diagnostic" "2.9.3" - "@parcel/fs" "2.9.3" - "@parcel/package-manager" "2.9.3" - "@parcel/source-map" "^2.1.1" - "@parcel/workers" "2.9.3" - utility-types "^3.10.0" - "@parcel/utils@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/utils/-/utils-2.8.3.tgz#0d56c9e8e22c119590a5e044a0e01031965da40e" @@ -1022,20 +839,6 @@ "@parcel/source-map" "^2.1.1" chalk "^4.1.0" -"@parcel/utils@2.9.3": - version "2.9.3" - resolved "https://registry.yarnpkg.com/@parcel/utils/-/utils-2.9.3.tgz#d4df6837658f773c725a4934967ab1128a05fdd7" - integrity sha512-cesanjtj/oLehW8Waq9JFPmAImhoiHX03ihc3JTWkrvJYSbD7wYKCDgPAM3JiRAqvh1LZ6P699uITrYWNoRLUg== - dependencies: - "@parcel/codeframe" "2.9.3" - "@parcel/diagnostic" "2.9.3" - "@parcel/hash" "2.9.3" - "@parcel/logger" "2.9.3" - "@parcel/markdown-ansi" "2.9.3" - "@parcel/source-map" "^2.1.1" - chalk "^4.1.0" - nullthrows "^1.1.1" - "@parcel/watcher@^2.0.7": version "2.1.0" resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.1.0.tgz#5f32969362db4893922c526a842d8af7a8538545" @@ -1058,18 +861,6 @@ chrome-trace-event "^1.0.2" nullthrows "^1.1.1" -"@parcel/workers@2.9.3": - version "2.9.3" - resolved "https://registry.yarnpkg.com/@parcel/workers/-/workers-2.9.3.tgz#d1d84d3c767b840d0ed7123a03ab7e0f4a2c0731" - integrity sha512-zRrDuZJzTevrrwElYosFztgldhqW6G9q5zOeQXfVQFkkEJCNfg36ixeiofKRU8uu2x+j+T6216mhMNB6HiuY+w== - dependencies: - "@parcel/diagnostic" "2.9.3" - "@parcel/logger" "2.9.3" - "@parcel/profiler" "2.9.3" - "@parcel/types" "2.9.3" - "@parcel/utils" "2.9.3" - nullthrows "^1.1.1" - "@shoelace-style/animations@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@shoelace-style/animations/-/animations-1.1.0.tgz#17539abafd6dcbf2a79e089e1593175e9f7835b5" @@ -2631,24 +2422,6 @@ lmdb@2.5.2: "@lmdb/lmdb-linux-x64" "2.5.2" "@lmdb/lmdb-win32-x64" "2.5.2" -lmdb@2.7.11: - version "2.7.11" - resolved "https://registry.yarnpkg.com/lmdb/-/lmdb-2.7.11.tgz#a24b6d36b5c7ed9889cc2d9e103fdd3f5e144d7e" - integrity sha512-x9bD4hVp7PFLUoELL8RglbNXhAMt5CYhkmss+CEau9KlNoilsTzNi9QDsPZb3KMpOGZXG6jmXhW3bBxE2XVztw== - dependencies: - msgpackr "1.8.5" - node-addon-api "^4.3.0" - node-gyp-build-optional-packages "5.0.6" - ordered-binary "^1.4.0" - weak-lru-cache "^1.2.2" - optionalDependencies: - "@lmdb/lmdb-darwin-arm64" "2.7.11" - "@lmdb/lmdb-darwin-x64" "2.7.11" - "@lmdb/lmdb-linux-arm" "2.7.11" - "@lmdb/lmdb-linux-arm64" "2.7.11" - "@lmdb/lmdb-linux-x64" "2.7.11" - "@lmdb/lmdb-win32-x64" "2.7.11" - loader-utils@^3.2.0: version "3.2.1" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.2.1.tgz#4fb104b599daafd82ef3e1a41fb9265f87e1f576" @@ -2782,27 +2555,6 @@ msgpackr-extract@^3.0.0: "@msgpackr-extract/msgpackr-extract-linux-x64" "3.0.0" "@msgpackr-extract/msgpackr-extract-win32-x64" "3.0.0" -msgpackr-extract@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-3.0.2.tgz#e05ec1bb4453ddf020551bcd5daaf0092a2c279d" - integrity sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A== - dependencies: - node-gyp-build-optional-packages "5.0.7" - optionalDependencies: - "@msgpackr-extract/msgpackr-extract-darwin-arm64" "3.0.2" - "@msgpackr-extract/msgpackr-extract-darwin-x64" "3.0.2" - "@msgpackr-extract/msgpackr-extract-linux-arm" "3.0.2" - "@msgpackr-extract/msgpackr-extract-linux-arm64" "3.0.2" - "@msgpackr-extract/msgpackr-extract-linux-x64" "3.0.2" - "@msgpackr-extract/msgpackr-extract-win32-x64" "3.0.2" - -msgpackr@1.8.5: - version "1.8.5" - resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.8.5.tgz#8cadfb935357680648f33699d0e833c9179dbfeb" - integrity sha512-mpPs3qqTug6ahbblkThoUY2DQdNXcm4IapwOS3Vm/87vmpzLVelvp9h3It1y9l1VPpiFLV11vfOXnmeEwiIXwg== - optionalDependencies: - msgpackr-extract "^3.0.1" - msgpackr@^1.5.4: version "1.8.3" resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.8.3.tgz#78c1b91359f72707f4abeaca40cc423bd2d75185" @@ -2840,11 +2592,6 @@ node-gyp-build-optional-packages@5.0.3: resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.3.tgz#92a89d400352c44ad3975010368072b41ad66c17" integrity sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA== -node-gyp-build-optional-packages@5.0.6: - version "5.0.6" - resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.6.tgz#2949f5cc7dace3ac470fa2ff1a37456907120a1d" - integrity sha512-2ZJErHG4du9G3/8IWl/l9Bp5BBFy63rno5GVmjQijvTuUZKsl6g8RB4KH/x3NLcV5ZBb4GsXmAuTYr6dRml3Gw== - node-gyp-build-optional-packages@5.0.7: version "5.0.7" resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.7.tgz#5d2632bbde0ab2f6e22f1bbac2199b07244ae0b3" @@ -2915,11 +2662,6 @@ ordered-binary@^1.2.4: resolved "https://registry.yarnpkg.com/ordered-binary/-/ordered-binary-1.4.0.tgz#6bb53d44925f3b8afc33d1eed0fa15693b211389" integrity sha512-EHQ/jk4/a9hLupIKxTfUsQRej1Yd/0QLQs3vGvIqg5ZtCYSzNhkzHoZc7Zf4e4kUlDaC3Uw8Q/1opOLNN2OKRQ== -ordered-binary@^1.4.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/ordered-binary/-/ordered-binary-1.4.1.tgz#205cb6efd6c27fa0ef4eced994a023e081cdc911" - integrity sha512-9LtiGlPy982CsgxZvJGNNp2/NnrgEr6EAyN3iIEP3/8vd3YLgAZQHbQ75ZrkfBRGrNg37Dk3U6tuVb+B4Xfslg== - ospath@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" @@ -2946,13 +2688,6 @@ p-map@^4.0.0: dependencies: aggregate-error "^3.0.0" -parcel-reporter-static-files-copy@^1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/parcel-reporter-static-files-copy/-/parcel-reporter-static-files-copy-1.5.2.tgz#aed0370717ea5d5185d6b09aea9cf9e6f479366c" - integrity sha512-NyIP8dOBj4WiZYAfKKTFWrMh8/kjrP72673G3hs7TOoT/30QWwUQpRA7oW4IxbjY2ICOo2sV06Z1lzPKCDbrGA== - dependencies: - "@parcel/plugin" "^2.0.0-beta.1" - parcel@v2.8.3: version "2.8.3" resolved "https://registry.yarnpkg.com/parcel/-/parcel-2.8.3.tgz#1ff71d7317274fd367379bc7310a52c6b75d30c2" @@ -3287,7 +3022,7 @@ semver@^7.3.2: dependencies: lru-cache "^6.0.0" -semver@^7.5.2, semver@^7.5.4: +semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== From 2c8ffa89dac400f7a6e26a7f4471710a7f098c21 Mon Sep 17 00:00:00 2001 From: ayangster Date: Wed, 20 Sep 2023 15:08:29 -0700 Subject: [PATCH 08/21] Minor cleanups --- src/select.ts | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/src/select.ts b/src/select.ts index 2271ceb..32444a8 100644 --- a/src/select.ts +++ b/src/select.ts @@ -31,11 +31,6 @@ export interface SLSelectParam { ariaLabel?: string; } -export interface SingleSelectParam extends SLSelectParam { - currentValue: string; - onChange?: (event: SlChangeEvent) => void; -} - export interface MultiSelectParam extends SLSelectParam { currentValues: string[]; maxOptionsVisible?: number; @@ -249,32 +244,6 @@ export const selectStyles = css` margin-top: 4px; } - // parts go largest to smallest - - // wraps label, input and helptext - sl-select::part(form-control) { - } - // don't need we are using html label - sl-select::part(form-control-label) { - } - // select wrapper - sl-select::part(form-control-input) { - } - // help text - sl-select::part(form-control-help-text) { - } - // The container the wraps the prefix, combobox, clear icon, and expand button. - sl-select::part(combobox) { - } - // The container that wraps the prefix slot (geat icon?) - sl-select::part(prefix) { - } - // selected option label - sl-select::part(display-input) { - } - // the container where options are listed - sl-select::part(listbox) { - } sl-select::part(expand-icon) { content: ''; From 8f544d8fad21ca99d95a39617c7082460015fa94 Mon Sep 17 00:00:00 2001 From: ayangster Date: Thu, 21 Sep 2023 08:59:56 -0700 Subject: [PATCH 09/21] Remove clearable button - this allows more room for the option text --- src/select.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/select.ts b/src/select.ts index 32444a8..2e7640b 100644 --- a/src/select.ts +++ b/src/select.ts @@ -93,7 +93,6 @@ export const multiselect = ({ placement="${ifDefined(placement)}" hoist multiple - clearable > ${options.map(o => multioption(o))} From 0a6040df64500361458a138200ccde910f9128e0 Mon Sep 17 00:00:00 2001 From: ayangster Date: Thu, 21 Sep 2023 09:11:26 -0700 Subject: [PATCH 10/21] Remove unused ariaLabel --- src/select.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/select.ts b/src/select.ts index 2e7640b..d71df37 100644 --- a/src/select.ts +++ b/src/select.ts @@ -28,7 +28,6 @@ export interface SLSelectParam { placeholder?: string; placement?: string; required?: boolean; - ariaLabel?: string; } export interface MultiSelectParam extends SLSelectParam { From 324325915b611851b3dd25f5e8db04efb5de0345 Mon Sep 17 00:00:00 2001 From: ayangster Date: Wed, 27 Sep 2023 10:41:03 -0700 Subject: [PATCH 11/21] Merge cleanup --- src/calculator-form.ts | 10 ++++++++-- src/icons.ts | 2 +- src/state-calculator.ts | 16 ++++++++-------- src/state-incentive-details.ts | 3 ++- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/calculator-form.ts b/src/calculator-form.ts index 45aa0a0..68dbe38 100644 --- a/src/calculator-form.ts +++ b/src/calculator-form.ts @@ -75,7 +75,13 @@ const HOUSEHOLD_SIZE_OPTIONS: OptionParam[] = [1, 2, 3, 4, 5, 6, 7, 8].map( ); export const formTemplate = ( - [zip, ownerStatus, householdIncome, taxFiling, householdSize]: Array, + [ + zip, + ownerStatus, + householdIncome, + taxFiling, + householdSize, + ]: Array, projects: Array, showProjectField: boolean, onSubmit: (e: SubmitEvent) => void, @@ -206,7 +212,7 @@ export const formTemplate = (
-
diff --git a/src/icons.ts b/src/icons.ts index d71b449..6160a0d 100644 --- a/src/icons.ts +++ b/src/icons.ts @@ -1,4 +1,4 @@ -import { html, svg } from 'lit'; +import { html } from 'lit'; // FIXME: does this need to be nested like this? export const downIcon = (w: number, h: number) => html` - (this.selectedOtherTab = newOtherSelection), - newSelection => (this.selectedProjectTab = newSelection), - this.selectedOtherTab, - this.selectedProjectTab, - ), + results, + this.projects, + newOtherSelection => + (this.selectedOtherTab = newOtherSelection), + newSelection => (this.selectedProjectTab = newSelection), + this.selectedOtherTab, + this.selectedProjectTab, + ), error: errorTemplate, })} ${CALCULATOR_FOOTER} diff --git a/src/state-incentive-details.ts b/src/state-incentive-details.ts index f95cea7..abe5957 100644 --- a/src/state-incentive-details.ts +++ b/src/state-incentive-details.ts @@ -440,5 +440,6 @@ export const stateIncentivesTemplate = ( // If a nonexistent tab is selected, pretend the first one is selected. otherTabs.includes(selectedOtherTab) ? selectedOtherTab : otherTabs[0], onOtherTabSelected, - )}`; + )} + ${authorityLogosTemplate(response)}`; }; From 16e77a957c040bebe4d869d720ca015812e8d599 Mon Sep 17 00:00:00 2001 From: ayangster Date: Wed, 27 Sep 2023 10:51:54 -0700 Subject: [PATCH 12/21] Remove extraneous project field --- src/state-calculator.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/state-calculator.ts b/src/state-calculator.ts index 8c9d285..b1b81fb 100644 --- a/src/state-calculator.ts +++ b/src/state-calculator.ts @@ -211,7 +211,6 @@ export class RewiringAmericaStateCalculator extends LitElement { ? nothing : formTemplate( [ - this.selectedProject, this.zip, this.ownerStatus, this.householdIncome, From dcc7a0e74ae7639f4f7c289294b703565830c629 Mon Sep 17 00:00:00 2001 From: ayangster Date: Wed, 27 Sep 2023 11:01:46 -0700 Subject: [PATCH 13/21] Remove other extraneous project field --- src/calculator.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/calculator.ts b/src/calculator.ts index 387361e..2c62ed1 100644 --- a/src/calculator.ts +++ b/src/calculator.ts @@ -12,7 +12,6 @@ import { } from './calculator-types'; import { CALCULATOR_FOOTER } from './calculator-footer'; import { fetchApi } from './api/fetch'; -import { NO_PROJECT } from './projects'; const loadedTemplate = ( results: ICalculatedIncentiveResults, @@ -151,7 +150,6 @@ export class RewiringAmericaCalculator extends LitElement { ? nothing : formTemplate( [ - NO_PROJECT, this.zip, this.ownerStatus, this.householdIncome, From b43b40ef1272fbad941b38f626f326847b81724c Mon Sep 17 00:00:00 2001 From: ayangster Date: Thu, 28 Sep 2023 15:58:44 -0700 Subject: [PATCH 14/21] Playing around with select --- src/custom-select.ts | 16 ++++++++++++++++ src/select.ts | 1 + 2 files changed, 17 insertions(+) create mode 100644 src/custom-select.ts diff --git a/src/custom-select.ts b/src/custom-select.ts new file mode 100644 index 0000000..4fc1dd8 --- /dev/null +++ b/src/custom-select.ts @@ -0,0 +1,16 @@ +import { customElement } from 'lit/decorators.js'; +import '@shoelace-style/shoelace/dist/themes/light.css'; +import '@shoelace-style/shoelace/dist/components/option/option.js'; +import '@shoelace-style/shoelace/dist/components/select/select.js'; +import '@shoelace-style/shoelace/dist/components/menu/menu.js'; +import SlSelect from '@shoelace-style/shoelace/dist/components/select/select.js'; + +@customElement('ra-sl-select') +export class CustomSelect extends SlSelect { +} + +declare global { + interface HTMLElementTagNameMap { + 'ra-sl-select': CustomSelect; + } +} diff --git a/src/select.ts b/src/select.ts index d71df37..de132bb 100644 --- a/src/select.ts +++ b/src/select.ts @@ -3,6 +3,7 @@ import { ifDefined } from 'lit/directives/if-defined.js'; import '@shoelace-style/shoelace/dist/themes/light.css'; import '@shoelace-style/shoelace/dist/components/option/option.js'; import '@shoelace-style/shoelace/dist/components/select/select.js'; +import '@shoelace-style/shoelace/dist/components/dropdown/dropdown.js'; export interface OptionParam { label: string; From a356ffe41735bc90bbab3711aa33e21dc56dc136 Mon Sep 17 00:00:00 2001 From: ayangster Date: Mon, 2 Oct 2023 09:31:27 -0700 Subject: [PATCH 15/21] Fix tabbing from multiselect --- src/custom-select.ts | 16 ---------------- src/state-calculator.ts | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 16 deletions(-) delete mode 100644 src/custom-select.ts diff --git a/src/custom-select.ts b/src/custom-select.ts deleted file mode 100644 index 4fc1dd8..0000000 --- a/src/custom-select.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { customElement } from 'lit/decorators.js'; -import '@shoelace-style/shoelace/dist/themes/light.css'; -import '@shoelace-style/shoelace/dist/components/option/option.js'; -import '@shoelace-style/shoelace/dist/components/select/select.js'; -import '@shoelace-style/shoelace/dist/components/menu/menu.js'; -import SlSelect from '@shoelace-style/shoelace/dist/components/select/select.js'; - -@customElement('ra-sl-select') -export class CustomSelect extends SlSelect { -} - -declare global { - interface HTMLElementTagNameMap { - 'ra-sl-select': CustomSelect; - } -} diff --git a/src/state-calculator.ts b/src/state-calculator.ts index b1b81fb..9ce8e55 100644 --- a/src/state-calculator.ts +++ b/src/state-calculator.ts @@ -135,6 +135,31 @@ export class RewiringAmericaStateCalculator extends LitElement { } } + override async firstUpdated() { + // Give the browser a chance to paint + await new Promise((r) => setTimeout(r, 0)); + const select = this.renderRoot.querySelector('sl-select'); + const combobox = this.renderRoot.querySelector('sl-select')?.renderRoot.querySelector('div.select__combobox'); + + select?.addEventListener('keydown', event => { + if (event.key === "Tab" && select.open) { + event.preventDefault(); + event.stopPropagation(); + select.hide(); + select.displayInput.focus({ preventScroll: true }); + } + }); + + combobox?.addEventListener('keydown', event => { + if (event.key === "Tab" && select?.open) { + event.preventDefault(); + event.stopPropagation(); + select.hide(); + select.displayInput.focus({ preventScroll: true }); + } + }); + } + isFormComplete() { return !!( this.zip && From 668f536642eb8702cb1db03c1992d26e270332a9 Mon Sep 17 00:00:00 2001 From: ayangster Date: Mon, 2 Oct 2023 09:36:24 -0700 Subject: [PATCH 16/21] Run prettier --- cypress/e2e/state-calculator.cy.ts | 4 ++-- src/calculator-form.ts | 8 +------- src/state-calculator.ts | 10 ++++++---- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/cypress/e2e/state-calculator.cy.ts b/cypress/e2e/state-calculator.cy.ts index a0d17aa..908fe9b 100644 --- a/cypress/e2e/state-calculator.cy.ts +++ b/cypress/e2e/state-calculator.cy.ts @@ -21,7 +21,7 @@ describe('template spec', () => { cy.get('rewiring-america-state-calculator') .shadow() .find('select#utility') - .should("exist"); + .should('exist'); cy.get('rewiring-america-state-calculator') .shadow() @@ -49,6 +49,6 @@ describe('template spec', () => { cy.get('rewiring-america-state-calculator') .shadow() - .contains("Other incentives available to you"); + .contains('Other incentives available to you'); }); }); diff --git a/src/calculator-form.ts b/src/calculator-form.ts index 68dbe38..494f2de 100644 --- a/src/calculator-form.ts +++ b/src/calculator-form.ts @@ -75,13 +75,7 @@ const HOUSEHOLD_SIZE_OPTIONS: OptionParam[] = [1, 2, 3, 4, 5, 6, 7, 8].map( ); export const formTemplate = ( - [ - zip, - ownerStatus, - householdIncome, - taxFiling, - householdSize, - ]: Array, + [zip, ownerStatus, householdIncome, taxFiling, householdSize]: Array, projects: Array, showProjectField: boolean, onSubmit: (e: SubmitEvent) => void, diff --git a/src/state-calculator.ts b/src/state-calculator.ts index c216590..96791d0 100644 --- a/src/state-calculator.ts +++ b/src/state-calculator.ts @@ -145,12 +145,14 @@ export class RewiringAmericaStateCalculator extends LitElement { override async firstUpdated() { // Give the browser a chance to paint - await new Promise((r) => setTimeout(r, 0)); + await new Promise(r => setTimeout(r, 0)); const select = this.renderRoot.querySelector('sl-select'); - const combobox = this.renderRoot.querySelector('sl-select')?.renderRoot.querySelector('div.select__combobox'); + const combobox = this.renderRoot + .querySelector('sl-select') + ?.renderRoot.querySelector('div.select__combobox'); select?.addEventListener('keydown', event => { - if (event.key === "Tab" && select.open) { + if (event.key === 'Tab' && select.open) { event.preventDefault(); event.stopPropagation(); select.hide(); @@ -159,7 +161,7 @@ export class RewiringAmericaStateCalculator extends LitElement { }); combobox?.addEventListener('keydown', event => { - if (event.key === "Tab" && select?.open) { + if (event.key === 'Tab' && select?.open) { event.preventDefault(); event.stopPropagation(); select.hide(); From 2a5191234cb3cef33a7d159c576eb724aadd6bf4 Mon Sep 17 00:00:00 2001 From: ayangster Date: Mon, 2 Oct 2023 09:37:56 -0700 Subject: [PATCH 17/21] Remove unnecessary import --- src/select.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/select.ts b/src/select.ts index de132bb..d71df37 100644 --- a/src/select.ts +++ b/src/select.ts @@ -3,7 +3,6 @@ import { ifDefined } from 'lit/directives/if-defined.js'; import '@shoelace-style/shoelace/dist/themes/light.css'; import '@shoelace-style/shoelace/dist/components/option/option.js'; import '@shoelace-style/shoelace/dist/components/select/select.js'; -import '@shoelace-style/shoelace/dist/components/dropdown/dropdown.js'; export interface OptionParam { label: string; From fa68539d95a01cb221cfa305e3687312d875ab91 Mon Sep 17 00:00:00 2001 From: ayangster Date: Mon, 2 Oct 2023 09:47:52 -0700 Subject: [PATCH 18/21] Add cypress project selection --- cypress/e2e/state-calculator.cy.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cypress/e2e/state-calculator.cy.ts b/cypress/e2e/state-calculator.cy.ts index 908fe9b..3b1d6a1 100644 --- a/cypress/e2e/state-calculator.cy.ts +++ b/cypress/e2e/state-calculator.cy.ts @@ -9,6 +9,11 @@ describe('template spec', () => { .shadow() .contains('Your household info'); + cy.get('rewiring-america-state-calculator') + .shadow() + .find('sl-select#projects') + .select('battery'); + cy.get('rewiring-america-state-calculator') .shadow() .find('input#zip') From 004f9688eafa90d8ec070e63578f54fa84af2cdc Mon Sep 17 00:00:00 2001 From: ayangster Date: Mon, 2 Oct 2023 09:52:06 -0700 Subject: [PATCH 19/21] Modify cypress test --- cypress/e2e/state-calculator.cy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/e2e/state-calculator.cy.ts b/cypress/e2e/state-calculator.cy.ts index 3b1d6a1..f09a9ab 100644 --- a/cypress/e2e/state-calculator.cy.ts +++ b/cypress/e2e/state-calculator.cy.ts @@ -12,7 +12,7 @@ describe('template spec', () => { cy.get('rewiring-america-state-calculator') .shadow() .find('sl-select#projects') - .select('battery'); + .invoke('attr', 'value', 'battery'); cy.get('rewiring-america-state-calculator') .shadow() From b7d8a7517f308e1760821c5b09fccf31f2d128fb Mon Sep 17 00:00:00 2001 From: ayangster Date: Mon, 2 Oct 2023 11:10:17 -0700 Subject: [PATCH 20/21] Clean up incentives card with new project selections --- src/state-calculator.ts | 2 +- src/state-incentive-details.ts | 28 +++++++++++++++++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/state-calculator.ts b/src/state-calculator.ts index 96791d0..a09bce3 100644 --- a/src/state-calculator.ts +++ b/src/state-calculator.ts @@ -98,7 +98,7 @@ export class RewiringAmericaStateCalculator extends LitElement { selectedProjectTab: Project | undefined; @property({ type: String }) - selectedOtherTab: Project = 'battery'; + selectedOtherTab: Project | undefined; /** * This is a hack to deal with a quirk of the UI. diff --git a/src/state-incentive-details.ts b/src/state-incentive-details.ts index abe5957..96ff2ba 100644 --- a/src/state-incentive-details.ts +++ b/src/state-incentive-details.ts @@ -395,7 +395,7 @@ export const stateIncentivesTemplate = ( selectedProjects: Project[], onOtherTabSelected: (newOtherSelection: Project) => void, onTabSelected: (newSelection: Project) => void, - selectedOtherTab: Project, + selectedOtherTab?: Project, selectedProjectTab?: Project, ) => { const allEligible = response.incentives.filter(i => i.eligible); @@ -407,6 +407,11 @@ export const stateIncentivesTemplate = ( ]), ) as Record; + const nonSelectedProjects = Object.entries(PROJECTS) + .filter(([project, _]) => !selectedProjects.includes(project as Project)) + .sort(([a], [b]) => shortLabel(a).localeCompare(shortLabel(b))) + .map(([project, _]) => project); + // Only offer "other" tabs if there are incentives for that project. const otherTabs = ( Object.entries(incentivesByProject) as [Project, Incentive[]][] @@ -418,8 +423,21 @@ export const stateIncentivesTemplate = ( .sort(([a], [b]) => shortLabel(a).localeCompare(shortLabel(b))) .map(([project]) => project); - const projectTab = selectedProjectTab ?? selectedProjects[0]; + const projectTab = + selectedProjectTab && + selectedProjects.includes(selectedProjectTab as Project) + ? selectedProjectTab + : selectedProjects[0]; + const otherTab = + selectedOtherTab && + nonSelectedProjects.includes(selectedOtherTab as Project) + ? selectedOtherTab + : nonSelectedProjects[0]; + const selectedIncentives = incentivesByProject[projectTab] ?? []; + const selectedOtherIncentives = + incentivesByProject[otherTab as Project] ?? []; + const otherIncentivesLabel = selectedIncentives.length == 0 ? 'Incentives available to you' @@ -430,15 +448,15 @@ export const stateIncentivesTemplate = ( "Incentives you're interested in", selectedIncentives, selectedProjects, - selectedProjects.includes(projectTab) ? projectTab : selectedProjects[0], + projectTab, onTabSelected, )} ${gridTemplate( otherIncentivesLabel, - incentivesByProject[selectedOtherTab] ?? [], + selectedOtherIncentives, otherTabs, // If a nonexistent tab is selected, pretend the first one is selected. - otherTabs.includes(selectedOtherTab) ? selectedOtherTab : otherTabs[0], + otherTab as Project, onOtherTabSelected, )} ${authorityLogosTemplate(response)}`; From 0c31cfaf41f23208c2c04f8ecf8eede9bab0b921 Mon Sep 17 00:00:00 2001 From: ayangster Date: Mon, 2 Oct 2023 11:13:55 -0700 Subject: [PATCH 21/21] Modify cypress test --- cypress/e2e/state-calculator.cy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/e2e/state-calculator.cy.ts b/cypress/e2e/state-calculator.cy.ts index f09a9ab..8cb2e29 100644 --- a/cypress/e2e/state-calculator.cy.ts +++ b/cypress/e2e/state-calculator.cy.ts @@ -12,7 +12,7 @@ describe('template spec', () => { cy.get('rewiring-america-state-calculator') .shadow() .find('sl-select#projects') - .invoke('attr', 'value', 'battery'); + .invoke('attr', 'value', 'hvac'); cy.get('rewiring-america-state-calculator') .shadow()