diff --git a/package-lock.json b/package-lock.json index 87d3f99..dc4a7dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "react": "16.14.0", "react-dom": "16.14.0", "react-helmet": "6.1.0", + "react-instantsearch-hooks-web": "^6.40.1", "react-redux": "7.2.9", "react-router": "5.3.4", "react-router-dom": "5.3.4", @@ -129,6 +130,11 @@ "@algolia/transporter": "4.6.0" } }, + "node_modules/@algolia/events": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz", + "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" + }, "node_modules/@algolia/logger-common": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.6.0.tgz", @@ -173,6 +179,20 @@ "@algolia/requester-common": "4.6.0" } }, + "node_modules/@algolia/ui-components-highlight-vdom": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@algolia/ui-components-highlight-vdom/-/ui-components-highlight-vdom-1.2.1.tgz", + "integrity": "sha512-IlYgIaCUEkz9ezNbwugwKv991oOHhveyq6nzL0F1jDzg1p3q5Yj/vO4KpNG910r2dwGCG3nEm5GtChcLnarhFA==", + "dependencies": { + "@algolia/ui-components-shared": "1.2.1", + "@babel/runtime": "^7.0.0" + } + }, + "node_modules/@algolia/ui-components-shared": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@algolia/ui-components-shared/-/ui-components-shared-1.2.1.tgz", + "integrity": "sha512-a7mYHf/GVQfhAx/HRiMveKkFvHspQv/REdG+C/FIOosiSmNZxX7QebDwJkrGSmDWdXO12D0Qv1xn3AytFcEDlQ==" + }, "node_modules/@ampproject/remapping": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", @@ -4496,6 +4516,11 @@ "version": "0.3.3", "license": "MIT" }, + "node_modules/@types/dom-speech-recognition": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@types/dom-speech-recognition/-/dom-speech-recognition-0.0.1.tgz", + "integrity": "sha512-udCxb8DvjcDKfk1WTBzDsxFbLgYxmQGKrE/ricoMqHRNjSlSUCcamVTA5lIQqzY10mY5qCY0QDwBfFEwhfoDPw==" + }, "node_modules/@types/eslint": { "version": "8.4.10", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", @@ -4564,6 +4589,11 @@ "@types/node": "*" } }, + "node_modules/@types/google.maps": { + "version": "3.52.0", + "resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.52.0.tgz", + "integrity": "sha512-cIwkgSBUOCerEwEpAahg1SxUqqGV+D786TkVWrcZZyPvuCozmXFtzQcpOzvUXBtTUqDzEbCDGlAXDfDSYFXFIw==" + }, "node_modules/@types/graceful-fs": { "version": "4.1.6", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", @@ -4573,6 +4603,11 @@ "@types/node": "*" } }, + "node_modules/@types/hogan.js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/hogan.js/-/hogan.js-3.0.1.tgz", + "integrity": "sha512-D03i/2OY7kGyMq9wdQ7oD8roE49z/ZCZThe/nbahtvuqCNZY9T2MfedOWyeBdbEpY2W8Gnh/dyJLdFtUCOkYbg==" + }, "node_modules/@types/hoist-non-react-statics": { "version": "3.3.1", "license": "MIT", @@ -4681,8 +4716,7 @@ "node_modules/@types/qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", - "dev": true + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" }, "node_modules/@types/range-parser": { "version": "1.2.4", @@ -5071,6 +5105,11 @@ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", "dev": true }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -5298,6 +5337,17 @@ "@algolia/transporter": "4.6.0" } }, + "node_modules/algoliasearch-helper": { + "version": "3.11.3", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.11.3.tgz", + "integrity": "sha512-TbaEvLwiuGygHQIB8y+OsJKQQ40+JKUua5B91X66tMUHyyhbNHvqyr0lqd3wCoyKx7WybyQrC0WJvzoIeh24Aw==", + "dependencies": { + "@algolia/events": "^4.0.1" + }, + "peerDependencies": { + "algoliasearch": ">= 3.1 < 6" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -10334,6 +10384,27 @@ "value-equal": "^1.0.1" } }, + "node_modules/hogan.js": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/hogan.js/-/hogan.js-3.0.2.tgz", + "integrity": "sha512-RqGs4wavGYJWE07t35JQccByczmNUXQT0E12ZYV1VKYu5UiAU9lsos/yBAcf840+zrUQQxgVduCR5/B8nNtibg==", + "dependencies": { + "mkdirp": "0.3.0", + "nopt": "1.0.10" + }, + "bin": { + "hulk": "bin/hulk" + } + }, + "node_modules/hogan.js/node_modules/mkdirp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "integrity": "sha512-OHsdUcVAQ6pOtg5JYWpCBo9W/GySVuwvP9hueRMW7UqshC0tbfzLv8wjySTPm3tfUZ/21CE9E1pJagOA91Pxew==", + "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", + "engines": { + "node": "*" + } + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "license": "BSD-3-Clause", @@ -10400,6 +10471,11 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/htm": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/htm/-/htm-3.1.1.tgz", + "integrity": "sha512-983Vyg8NwUE7JkZ6NmOqpCZ+sh1bKv2iYTlUkzlWmA5JD2acKoxd4KVxbMmxX/85mtfdnDmTFoNKcg5DGAvxNQ==" + }, "node_modules/html-element-map": { "version": "1.3.1", "dev": true, @@ -10885,6 +10961,40 @@ "dev": true, "license": "ISC" }, + "node_modules/instantsearch.js": { + "version": "4.51.1", + "resolved": "https://registry.npmjs.org/instantsearch.js/-/instantsearch.js-4.51.1.tgz", + "integrity": "sha512-l4nETzassgSMBqOhNxntNH2MGG0KvFagwVwnXFis9P06nlspAyOdFLCQ0HA5wnlRDDev2WAZg9xRcTPCTLrwZw==", + "dependencies": { + "@algolia/events": "^4.0.1", + "@algolia/ui-components-highlight-vdom": "^1.2.1", + "@algolia/ui-components-shared": "^1.2.1", + "@types/dom-speech-recognition": "^0.0.1", + "@types/google.maps": "^3.45.3", + "@types/hogan.js": "^3.0.0", + "@types/qs": "^6.5.3", + "algoliasearch-helper": "^3.11.3", + "hogan.js": "^3.0.2", + "htm": "^3.0.0", + "preact": "^10.10.0", + "qs": "^6.5.1 < 6.10", + "search-insights": "^2.1.0" + }, + "peerDependencies": { + "algoliasearch": ">= 3.1 < 6" + } + }, + "node_modules/instantsearch.js/node_modules/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/internal-slot": { "version": "1.0.3", "dev": true, @@ -13524,6 +13634,20 @@ "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==", "dev": true }, + "node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/normalize-package-data": { "version": "3.0.3", "dev": true, @@ -14920,6 +15044,15 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, + "node_modules/preact": { + "version": "10.12.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz", + "integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, "node_modules/prebuild-install": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", @@ -15502,6 +15635,36 @@ "react": ">=16.3.0" } }, + "node_modules/react-instantsearch-hooks": { + "version": "6.40.1", + "resolved": "https://registry.npmjs.org/react-instantsearch-hooks/-/react-instantsearch-hooks-6.40.1.tgz", + "integrity": "sha512-Nqbmencg5816UVDkWUFpGyurAhp8WUBQgHiYj/bTnYQ7KuwXbbxIppIwexZqzXG8Q3DKVBWZw4PnmJiStLK2CA==", + "dependencies": { + "@babel/runtime": "^7.1.2", + "algoliasearch-helper": "^3.11.3", + "instantsearch.js": "4.51.1", + "use-sync-external-store": "^1.0.0" + }, + "peerDependencies": { + "algoliasearch": ">= 3.1 < 5", + "react": ">= 16.8.0 < 19" + } + }, + "node_modules/react-instantsearch-hooks-web": { + "version": "6.40.1", + "resolved": "https://registry.npmjs.org/react-instantsearch-hooks-web/-/react-instantsearch-hooks-web-6.40.1.tgz", + "integrity": "sha512-MbsS1nhyZWPJjSVRzkDVmb0TM4L2kjHANsr0e1NDwkVWicP8LtXdX6UINDnekMKVnk1OmoF10NAJt4LmcSOqaA==", + "dependencies": { + "@babel/runtime": "^7.1.2", + "instantsearch.js": "4.51.1", + "react-instantsearch-hooks": "6.40.1" + }, + "peerDependencies": { + "algoliasearch": ">= 3.1 < 5", + "react": ">= 16.8.0 < 19", + "react-dom": ">= 16.8.0 < 19" + } + }, "node_modules/react-intl": { "version": "5.25.1", "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-5.25.1.tgz", @@ -16951,6 +17114,14 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/search-insights": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.3.0.tgz", + "integrity": "sha512-0v/TTO4fbd6I91sFBK/e2zNfD0f51A+fMoYNkMplmR77NpThUye/7gIxNoJ3LejKpZH6Z2KNBIpxxFmDKj10Yw==", + "engines": { + "node": ">=8.16.0" + } + }, "node_modules/select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -19189,6 +19360,14 @@ } } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "dev": true, @@ -20173,6 +20352,11 @@ "@algolia/transporter": "4.6.0" } }, + "@algolia/events": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz", + "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" + }, "@algolia/logger-common": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.6.0.tgz", @@ -20217,6 +20401,20 @@ "@algolia/requester-common": "4.6.0" } }, + "@algolia/ui-components-highlight-vdom": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@algolia/ui-components-highlight-vdom/-/ui-components-highlight-vdom-1.2.1.tgz", + "integrity": "sha512-IlYgIaCUEkz9ezNbwugwKv991oOHhveyq6nzL0F1jDzg1p3q5Yj/vO4KpNG910r2dwGCG3nEm5GtChcLnarhFA==", + "requires": { + "@algolia/ui-components-shared": "1.2.1", + "@babel/runtime": "^7.0.0" + } + }, + "@algolia/ui-components-shared": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@algolia/ui-components-shared/-/ui-components-shared-1.2.1.tgz", + "integrity": "sha512-a7mYHf/GVQfhAx/HRiMveKkFvHspQv/REdG+C/FIOosiSmNZxX7QebDwJkrGSmDWdXO12D0Qv1xn3AytFcEDlQ==" + }, "@ampproject/remapping": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", @@ -23399,6 +23597,11 @@ "@types/cookie": { "version": "0.3.3" }, + "@types/dom-speech-recognition": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@types/dom-speech-recognition/-/dom-speech-recognition-0.0.1.tgz", + "integrity": "sha512-udCxb8DvjcDKfk1WTBzDsxFbLgYxmQGKrE/ricoMqHRNjSlSUCcamVTA5lIQqzY10mY5qCY0QDwBfFEwhfoDPw==" + }, "@types/eslint": { "version": "8.4.10", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", @@ -23467,6 +23670,11 @@ "@types/node": "*" } }, + "@types/google.maps": { + "version": "3.52.0", + "resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.52.0.tgz", + "integrity": "sha512-cIwkgSBUOCerEwEpAahg1SxUqqGV+D786TkVWrcZZyPvuCozmXFtzQcpOzvUXBtTUqDzEbCDGlAXDfDSYFXFIw==" + }, "@types/graceful-fs": { "version": "4.1.6", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", @@ -23476,6 +23684,11 @@ "@types/node": "*" } }, + "@types/hogan.js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/hogan.js/-/hogan.js-3.0.1.tgz", + "integrity": "sha512-D03i/2OY7kGyMq9wdQ7oD8roE49z/ZCZThe/nbahtvuqCNZY9T2MfedOWyeBdbEpY2W8Gnh/dyJLdFtUCOkYbg==" + }, "@types/hoist-non-react-statics": { "version": "3.3.1", "requires": { @@ -23577,8 +23790,7 @@ "@types/qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", - "dev": true + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" }, "@types/range-parser": { "version": "1.2.4", @@ -23945,6 +24157,11 @@ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", "dev": true }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -24116,6 +24333,14 @@ "@algolia/transporter": "4.6.0" } }, + "algoliasearch-helper": { + "version": "3.11.3", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.11.3.tgz", + "integrity": "sha512-TbaEvLwiuGygHQIB8y+OsJKQQ40+JKUua5B91X66tMUHyyhbNHvqyr0lqd3wCoyKx7WybyQrC0WJvzoIeh24Aw==", + "requires": { + "@algolia/events": "^4.0.1" + } + }, "ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -27838,6 +28063,22 @@ "value-equal": "^1.0.1" } }, + "hogan.js": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/hogan.js/-/hogan.js-3.0.2.tgz", + "integrity": "sha512-RqGs4wavGYJWE07t35JQccByczmNUXQT0E12ZYV1VKYu5UiAU9lsos/yBAcf840+zrUQQxgVduCR5/B8nNtibg==", + "requires": { + "mkdirp": "0.3.0", + "nopt": "1.0.10" + }, + "dependencies": { + "mkdirp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "integrity": "sha512-OHsdUcVAQ6pOtg5JYWpCBo9W/GySVuwvP9hueRMW7UqshC0tbfzLv8wjySTPm3tfUZ/21CE9E1pJagOA91Pxew==" + } + } + }, "hoist-non-react-statics": { "version": "3.3.2", "requires": { @@ -27901,6 +28142,11 @@ } } }, + "htm": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/htm/-/htm-3.1.1.tgz", + "integrity": "sha512-983Vyg8NwUE7JkZ6NmOqpCZ+sh1bKv2iYTlUkzlWmA5JD2acKoxd4KVxbMmxX/85mtfdnDmTFoNKcg5DGAvxNQ==" + }, "html-element-map": { "version": "1.3.1", "dev": true, @@ -28221,6 +28467,33 @@ "version": "1.3.8", "dev": true }, + "instantsearch.js": { + "version": "4.51.1", + "resolved": "https://registry.npmjs.org/instantsearch.js/-/instantsearch.js-4.51.1.tgz", + "integrity": "sha512-l4nETzassgSMBqOhNxntNH2MGG0KvFagwVwnXFis9P06nlspAyOdFLCQ0HA5wnlRDDev2WAZg9xRcTPCTLrwZw==", + "requires": { + "@algolia/events": "^4.0.1", + "@algolia/ui-components-highlight-vdom": "^1.2.1", + "@algolia/ui-components-shared": "^1.2.1", + "@types/dom-speech-recognition": "^0.0.1", + "@types/google.maps": "^3.45.3", + "@types/hogan.js": "^3.0.0", + "@types/qs": "^6.5.3", + "algoliasearch-helper": "^3.11.3", + "hogan.js": "^3.0.2", + "htm": "^3.0.0", + "preact": "^10.10.0", + "qs": "^6.5.1 < 6.10", + "search-insights": "^2.1.0" + }, + "dependencies": { + "qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==" + } + } + }, "internal-slot": { "version": "1.0.3", "dev": true, @@ -30206,6 +30479,14 @@ "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==", "dev": true }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "requires": { + "abbrev": "1" + } + }, "normalize-package-data": { "version": "3.0.3", "dev": true, @@ -31136,6 +31417,11 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, + "preact": { + "version": "10.12.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz", + "integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==" + }, "prebuild-install": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", @@ -31566,6 +31852,27 @@ "react-side-effect": "^2.1.0" } }, + "react-instantsearch-hooks": { + "version": "6.40.1", + "resolved": "https://registry.npmjs.org/react-instantsearch-hooks/-/react-instantsearch-hooks-6.40.1.tgz", + "integrity": "sha512-Nqbmencg5816UVDkWUFpGyurAhp8WUBQgHiYj/bTnYQ7KuwXbbxIppIwexZqzXG8Q3DKVBWZw4PnmJiStLK2CA==", + "requires": { + "@babel/runtime": "^7.1.2", + "algoliasearch-helper": "^3.11.3", + "instantsearch.js": "4.51.1", + "use-sync-external-store": "^1.0.0" + } + }, + "react-instantsearch-hooks-web": { + "version": "6.40.1", + "resolved": "https://registry.npmjs.org/react-instantsearch-hooks-web/-/react-instantsearch-hooks-web-6.40.1.tgz", + "integrity": "sha512-MbsS1nhyZWPJjSVRzkDVmb0TM4L2kjHANsr0e1NDwkVWicP8LtXdX6UINDnekMKVnk1OmoF10NAJt4LmcSOqaA==", + "requires": { + "@babel/runtime": "^7.1.2", + "instantsearch.js": "4.51.1", + "react-instantsearch-hooks": "6.40.1" + } + }, "react-intl": { "version": "5.25.1", "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-5.25.1.tgz", @@ -32592,6 +32899,11 @@ "ajv-keywords": "^3.5.2" } }, + "search-insights": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.3.0.tgz", + "integrity": "sha512-0v/TTO4fbd6I91sFBK/e2zNfD0f51A+fMoYNkMplmR77NpThUye/7gIxNoJ3LejKpZH6Z2KNBIpxxFmDKj10Yw==" + }, "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -34297,6 +34609,12 @@ "tslib": "^2.0.0" } }, + "use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "requires": {} + }, "util-deprecate": { "version": "1.0.2", "dev": true diff --git a/package.json b/package.json index d603f1f..c345402 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "react": "16.14.0", "react-dom": "16.14.0", "react-helmet": "6.1.0", + "react-instantsearch-hooks-web": "^6.40.1", "react-redux": "7.2.9", "react-router": "5.3.4", "react-router-dom": "5.3.4", diff --git a/src/skills-builder/data/constants.js b/src/skills-builder/data/constants.js index 5d801c6..65847df 100644 --- a/src/skills-builder/data/constants.js +++ b/src/skills-builder/data/constants.js @@ -1,7 +1,9 @@ +// Actions for Skills Context export const SET_GOAL = 'SET_GOAL'; export const SET_CURRENT_JOB_TITLE = 'SET_CURRENT_JOB_TITLE'; export const ADD_CAREER_INTEREST = 'ADD_CAREER_INTEREST'; export const REMOVE_CAREER_INTEREEST = 'REMOVE_CAREER_INTEREEST'; +// Stepper keys export const STEP1 = 'select-your-preferences'; export const STEP2 = 'review-your-results'; diff --git a/src/skills-builder/skills-builder-context/SkillsBuilderProvider.jsx b/src/skills-builder/skills-builder-context/SkillsBuilderProvider.jsx index 2df4004..f5984c4 100644 --- a/src/skills-builder/skills-builder-context/SkillsBuilderProvider.jsx +++ b/src/skills-builder/skills-builder-context/SkillsBuilderProvider.jsx @@ -1,12 +1,24 @@ import React, { createContext, useReducer, useMemo } from 'react'; import PropTypes from 'prop-types'; import reducer, { skillsInitialState } from '../data/reducer'; +import { useAlgoliaSearch } from '../utils/search'; export const SkillsBuilderContext = createContext(); export const SkillsBuilderProvider = ({ children }) => { const [state, dispatch] = useReducer(reducer, skillsInitialState); - const value = useMemo(() => ([state, dispatch]), [state]); + + const [searchClient, productSearchIndex, jobSearchIndex] = useAlgoliaSearch(); + + const value = useMemo(() => ({ + state, + dispatch, + algolia: { + searchClient, + productSearchIndex, + jobSearchIndex, + }, + }), [state, searchClient, productSearchIndex, jobSearchIndex]); return ( diff --git a/src/skills-builder/skills-builder-modal/SelectPreferences.jsx b/src/skills-builder/skills-builder-modal/SelectPreferences.jsx deleted file mode 100644 index 43320d1..0000000 --- a/src/skills-builder/skills-builder-modal/SelectPreferences.jsx +++ /dev/null @@ -1,63 +0,0 @@ -import React, { useContext } from 'react'; -import { - Button, -} from '@edx/paragon'; -import { - setGoal, - setCurrentJobTitle, - addCareerInterest, - removeCareerInterest, -} from '../data/actions'; -import { SkillsBuilderContext } from '../skills-builder-context'; -import { useAlgoliaSearch } from '../utils/search'; - -const SelectPreferences = () => { - // TODO: Temporarily disable the no-unused-vars check, we'll see these later - // eslint-disable-next-line no-unused-vars - const [algoliaClient, productSearchIndex, jobSearchIndex] = useAlgoliaSearch(); - const [{ currentGoal, currentJobTitle, careerInterests }, dispatch] = useContext(SkillsBuilderContext); - - return ( - <> -
-

Render Question 1

- -

Goal: {currentGoal}

-
- - {currentGoal && ( -
-

Render question 2

- -

Current Job Title: {currentJobTitle}

-
- )} - - {currentJobTitle && ( -
-

Render Question 3

- -

- Career Interests (click to remove): - {careerInterests.map(interest => ( - - ))} -

-
- )} - - ); -}; - -export default SelectPreferences; diff --git a/src/skills-builder/skills-builder-modal/SkillsBuilderModal.jsx b/src/skills-builder/skills-builder-modal/SkillsBuilderModal.jsx index eaee071..470618e 100644 --- a/src/skills-builder/skills-builder-modal/SkillsBuilderModal.jsx +++ b/src/skills-builder/skills-builder-modal/SkillsBuilderModal.jsx @@ -1,27 +1,25 @@ import React, { useState, useContext } from 'react'; import { - Button, - Container, - Stepper, - ModalDialog, + Button, Container, Stepper, ModalDialog, } from '@edx/paragon'; -import { FormattedMessage } from '@edx/frontend-platform/i18n'; +import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n'; import { useHistory } from 'react-router'; import { - STEP1, - STEP2, + STEP1, STEP2, } from '../data/constants'; import messages from './messages'; import { SkillsBuilderContext } from '../skills-builder-context'; import { SkillsBuilderHeader } from '../skills-builder-header'; -import SelectPreferences from './SelectPreferences'; -import ViewResults from './ViewResults'; +import { SelectPreferences } from './select-preferences'; +import ViewResults from './view-results/ViewResults'; import headerImage from '../images/headerImage.png'; const SkillsBuilderModal = () => { - const [{ careerInterests }] = useContext(SkillsBuilderContext); + const intl = useIntl(); + const { state } = useContext(SkillsBuilderContext); + const { careerInterests } = state; const [currentStep, setCurrentStep] = useState(STEP1); const history = useHistory(); @@ -48,13 +46,15 @@ const SkillsBuilderModal = () => { + + - - + + - + @@ -62,7 +62,7 @@ const SkillsBuilderModal = () => { - diff --git a/src/skills-builder/skills-builder-modal/messages.js b/src/skills-builder/skills-builder-modal/messages.js index 6938a8c..66f207f 100644 --- a/src/skills-builder/skills-builder-modal/messages.js +++ b/src/skills-builder/skills-builder-modal/messages.js @@ -17,6 +17,16 @@ const messages = defineMessages({ defaultMessage: 'Exit', description: 'Button that exits the Skills Builder.', }, + selectPreferences: { + id: 'select.preferences', + defaultMessage: 'Select preferences', + description: 'The first step of the Skills Builder for selecting a goal, a current job/occupation, and career interests', + }, + reviewResults: { + id: 'review.results', + defaultMessage: 'Review results', + description: 'The second step of the Skills Builder for rendering results from learner input', + }, }); export default messages; diff --git a/src/skills-builder/skills-builder-modal/select-preferences/CareerInterestSelect.jsx b/src/skills-builder/skills-builder-modal/select-preferences/CareerInterestSelect.jsx new file mode 100644 index 0000000..68f314f --- /dev/null +++ b/src/skills-builder/skills-builder-modal/select-preferences/CareerInterestSelect.jsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { FormattedMessage } from '@edx/frontend-platform/i18n'; +import messages from './messages'; + +const CareerInterestSelect = () => ( +
+

+ +

+

+ JobTitleAutosuggest component can be reused here +

+
+); + +export default CareerInterestSelect; diff --git a/src/skills-builder/skills-builder-modal/select-preferences/GoalSelect.jsx b/src/skills-builder/skills-builder-modal/select-preferences/GoalSelect.jsx new file mode 100644 index 0000000..2c2f5f6 --- /dev/null +++ b/src/skills-builder/skills-builder-modal/select-preferences/GoalSelect.jsx @@ -0,0 +1,38 @@ +import React, { useContext } from 'react'; +import { + Form, + Stack, +} from '@edx/paragon'; +import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n'; +import { setGoal } from '../../data/actions'; +import { SkillsBuilderContext } from '../../skills-builder-context'; +import messages from './messages'; + +const GoalDropdown = () => { + const intl = useIntl(); + const { state, dispatch } = useContext(SkillsBuilderContext); + const { currentGoal } = state; + + return ( + +

+ + dispatch(setGoal(e.target.value))} + > + + + + + + + + +
+ + ); +}; + +export default GoalDropdown; diff --git a/src/skills-builder/skills-builder-modal/select-preferences/JobTitleInstantSearch.jsx b/src/skills-builder/skills-builder-modal/select-preferences/JobTitleInstantSearch.jsx new file mode 100644 index 0000000..e147d5d --- /dev/null +++ b/src/skills-builder/skills-builder-modal/select-preferences/JobTitleInstantSearch.jsx @@ -0,0 +1,39 @@ +import React, { useState } from 'react'; +import PropTypes from 'prop-types'; +import { + Form, +} from '@edx/paragon'; +import { useHits, useSearchBox } from 'react-instantsearch-hooks-web'; + +const JobTitleInstantSearch = (props) => { + const { refine } = useSearchBox(props); + const { hits } = useHits(props); + + const [jobInput, setJobInput] = useState(''); + + const handleAutosuggestChange = (value) => { + setJobInput(value); + refine(value); + }; + + return ( + + {hits.map(job => ( + + {job.name} + + ))} + + ); +}; + +JobTitleInstantSearch.propTypes = { + onSelected: PropTypes.func.isRequired, +}; + +export default JobTitleInstantSearch; diff --git a/src/skills-builder/skills-builder-modal/select-preferences/JobTitleSelect.jsx b/src/skills-builder/skills-builder-modal/select-preferences/JobTitleSelect.jsx new file mode 100644 index 0000000..1d02bdd --- /dev/null +++ b/src/skills-builder/skills-builder-modal/select-preferences/JobTitleSelect.jsx @@ -0,0 +1,46 @@ +import React, { useContext } from 'react'; +import { + Form, Stack, +} from '@edx/paragon'; +import { getConfig } from '@edx/frontend-platform'; +import { FormattedMessage } from '@edx/frontend-platform/i18n'; +import { InstantSearch } from 'react-instantsearch-hooks-web'; +import { setCurrentJobTitle } from '../../data/actions'; +import { SkillsBuilderContext } from '../../skills-builder-context'; +import JobTitleInstantSearch from './JobTitleInstantSearch'; +import messages from './messages'; + +const JobTitleSelect = () => { + const { dispatch, algolia } = useContext(SkillsBuilderContext); + const { searchClient } = algolia; + + // Below implementation sets the job title to "student" or "looking_for_work" — this overwrites any previous selection + // This will need to be revisited when we decide what to do with this data + const handleCheckboxChange = (e) => dispatch(setCurrentJobTitle(e.target.value)); + + return ( + +

+ +

+ + dispatch(setCurrentJobTitle(value))} /> + + + + + + + + + + + +
+ ); +}; + +export default JobTitleSelect; diff --git a/src/skills-builder/skills-builder-modal/select-preferences/SelectPreferences.jsx b/src/skills-builder/skills-builder-modal/select-preferences/SelectPreferences.jsx new file mode 100644 index 0000000..11526ab --- /dev/null +++ b/src/skills-builder/skills-builder-modal/select-preferences/SelectPreferences.jsx @@ -0,0 +1,36 @@ +import React, { useContext } from 'react'; +import { + Stack, +} from '@edx/paragon'; +import { FormattedMessage } from '@edx/frontend-platform/i18n'; +import { SkillsBuilderContext } from '../../skills-builder-context'; + +import GoalSelect from './GoalSelect'; +import JobTitleSelect from './JobTitleSelect'; +import CareerInterestSelect from './CareerInterestSelect'; +import messages from './messages'; + +const SelectPreferences = () => { + const { state } = useContext(SkillsBuilderContext); + const { currentGoal, currentJobTitle } = state; + + return ( + +

+ +

+ + + + {currentGoal && ( + + )} + + {currentJobTitle && ( + + )} +
+ ); +}; + +export default SelectPreferences; diff --git a/src/skills-builder/skills-builder-modal/select-preferences/index.js b/src/skills-builder/skills-builder-modal/select-preferences/index.js new file mode 100644 index 0000000..f8d553d --- /dev/null +++ b/src/skills-builder/skills-builder-modal/select-preferences/index.js @@ -0,0 +1,2 @@ +// eslint-disable-next-line import/prefer-default-export +export { default as SelectPreferences } from './SelectPreferences'; diff --git a/src/skills-builder/skills-builder-modal/select-preferences/messages.js b/src/skills-builder/skills-builder-modal/select-preferences/messages.js new file mode 100644 index 0000000..76e124a --- /dev/null +++ b/src/skills-builder/skills-builder-modal/select-preferences/messages.js @@ -0,0 +1,66 @@ +import { defineMessages } from '@edx/frontend-platform/i18n'; + +const messages = defineMessages({ + skillsBuilderDescription: { + id: 'skills.builder.description', + defaultMessage: 'Find the right courses and programs that help you reach your goals.', + description: 'Description of what the Skills Builder seeks to accomplish', + }, + learningGoalPrompt: { + id: 'learning.goal.prompt', + defaultMessage: 'First, tell us what you want to achieve', + description: 'Prompts the user to select their current goal.', + }, + selectLearningGoal: { + id: 'select.learning.goal', + defaultMessage: 'Select a goal', + description: 'Placeholder text for the goal selection component.', + }, + learningGoalStartCareer: { + id: 'learning.goal.start_career', + defaultMessage: 'I want to start my career', + description: 'Selected by user if their goal is to start their career.', + }, + learningGoalAdvanceCareer: { + id: 'learning.goal.advance_career', + defaultMessage: 'I want to advance my career', + description: 'Selected by user if their goal is to advance their career.', + }, + learningGoalChangeCareer: { + id: 'learning.goal.change_career', + defaultMessage: 'I want to change careers', + description: 'Selected by user if their goal is to change careers.', + }, + learningGoalSomethingNew: { + id: 'learning.goal.something.new', + defaultMessage: 'I want to learn something new', + description: 'Selected by user if their goal is to learn something new.', + }, + learningGoalSomethingElse: { + id: 'learning.goal.something.else', + defaultMessage: 'Something else', + description: 'Selected by user if their goal is not described by the other choices.', + }, + jobTitlePrompt: { + id: 'job.title.prompt', + defaultMessage: 'Next, search and select your current job title', + description: 'Prompts the user to select their current job title or occupation.', + }, + studentCheckboxPrompt: { + id: 'student.checkbox.prompt', + defaultMessage: 'I\'m a student', + description: 'Label text for the corresponding checkbox', + }, + currentlyLookingCheckboxPrompt: { + id: 'currently.looking.checkbox.prompt', + defaultMessage: 'I\'m currently looking for work', + description: 'Label text for the corresponding checkbox', + }, + careerInterestPrompt: { + id: 'career.interest.prompt', + defaultMessage: 'What careers are you interested in?', + description: 'Prompts the user to select careers they are interested in pursuing.', + }, +}); + +export default messages; diff --git a/src/skills-builder/skills-builder-modal/ViewResults.jsx b/src/skills-builder/skills-builder-modal/view-results/ViewResults.jsx similarity index 100% rename from src/skills-builder/skills-builder-modal/ViewResults.jsx rename to src/skills-builder/skills-builder-modal/view-results/ViewResults.jsx diff --git a/src/skills-builder/skills-builder-modal/view-results/index.js b/src/skills-builder/skills-builder-modal/view-results/index.js new file mode 100644 index 0000000..b0af023 --- /dev/null +++ b/src/skills-builder/skills-builder-modal/view-results/index.js @@ -0,0 +1,2 @@ +// eslint-disable-next-line import/prefer-default-export +export { default as ViewResults } from './ViewResults'; diff --git a/src/skills-builder/test/SkillsBuilder.test.jsx b/src/skills-builder/test/SkillsBuilder.test.jsx index 2d6f073..0925b15 100644 --- a/src/skills-builder/test/SkillsBuilder.test.jsx +++ b/src/skills-builder/test/SkillsBuilder.test.jsx @@ -1,19 +1,96 @@ import { IntlProvider } from '@edx/frontend-platform/i18n'; import React from 'react'; -import { screen, render } from '@testing-library/react'; +import { + screen, render, cleanup, fireEvent, act, +} from '@testing-library/react'; +import { mergeConfig } from '@edx/frontend-platform'; import { SkillsBuilder } from '..'; +import { SkillsBuilderModal } from '../skills-builder-modal'; +import { SkillsBuilderProvider, SkillsBuilderContext } from '../skills-builder-context'; +import { skillsInitialState } from '../data/reducer'; -const SkillsBuilderWrapper = () => ( +jest.mock('react-instantsearch-hooks-web', () => ({ + // eslint-disable-next-line react/prop-types + InstantSearch: ({ children }) => (
{children}
), + useSearchBox: jest.fn(() => ({ refine: jest.fn() })), + useHits: jest.fn(() => ({ hits: [{ name: 'Text File Engineer' }, { name: 'Screen Viewer' }] })), +})); + +const dispatchMock = jest.fn(); + +const contextValue = { + state: { + ...skillsInitialState, + }, + dispatch: dispatchMock, + algolia: { + // Without this, tests would fail to destructure `searchClient` in the component + searchClient: {}, + }, +}; + +const SkillsBuilderWrapperWithContext = (value) => ( - + + + ); describe('skills-builder', () => { - it('should render a Skills Builder modal', () => { - render( - , - ); + beforeAll(async () => { + await mergeConfig({ + ALGOLIA_JOBS_INDEX_NAME: 'test-job-index-name', + }); + }); + beforeEach(() => cleanup()); + + it('should render a Skills Builder modal with a prompt for the user', () => { + act(() => { + render( + + + + + , + ); + }); expect(screen.getByText('Skills Builder')).toBeTruthy(); + expect(screen.getByText('First, tell us what you want to achieve')).toBeTruthy(); + }); + + it('should render the second prompt if a goal is selected', () => { + render( + SkillsBuilderWrapperWithContext( + { + ...contextValue, + state: { + ...contextValue.state, + currentGoal: 'I want to start my career', + }, + }, + ), + ); + expect(screen.getByText('Next, search and select your current job title')).toBeTruthy(); + + const checkbox = screen.getByRole('checkbox', { name: 'I\'m a student' }); + fireEvent.click(checkbox); + expect(dispatchMock).toHaveBeenCalled(); + }); + + it('should render the third prompt if a goal is selected', () => { + render( + SkillsBuilderWrapperWithContext( + { + ...contextValue, + state: { + ...contextValue.state, + currentGoal: 'I want to start my career', + currentJobTitle: 'Goblin Guide', + }, + }, + ), + ); + expect(screen.getByText('What careers are you interested in?')).toBeTruthy(); }); });