diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 61a0e359..36b769fa 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1,2 @@ # Each line is a file pattern followed by one or more owners. These owners will be the default owners for everything in the repo. Unless a later match takes precedence,e ach owner will be requested for review when someone opens a pull request. -* @samanthaandrews @jackiequach +* @samanthaandrews @jackiequach @kristojorg @Toxiapo @oliviawongnyc diff --git a/.github/workflows/build-production.yaml b/.github/workflows/build-production.yaml index 090718a4..70239ed6 100644 --- a/.github/workflows/build-production.yaml +++ b/.github/workflows/build-production.yaml @@ -66,6 +66,7 @@ jobs: - name: Build, tag, and push image to Amazon ECR env: + APP_ENV: production ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} ECR_REPOSITORY: sfr-front-end IMAGE_TAG: ${{ github.sha }} diff --git a/.github/workflows/build-qa.yml b/.github/workflows/build-qa.yml index 3e05b4d6..423def7e 100644 --- a/.github/workflows/build-qa.yml +++ b/.github/workflows/build-qa.yml @@ -34,6 +34,7 @@ jobs: - name: Build, tag, and push image to Amazon ECR env: + APP_ENV: qa ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} ECR_REPOSITORY: sfr-front-end IMAGE_TAG: ${{ github.sha }} diff --git a/CHANGELOG.md b/CHANGELOG.md index cfb16dc9..ea1b5b00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # CHANGE LOG +## [0.18.0] + +- Add Physical Edition badge and Scan and Deliver blurb to EDD editions +- Add Library Card Required and Featured Editon badges +- Update styles for work and edition pages +- Add login buttons and info blurb UP items +- Fix: Improve accessibility of CTAs and search bar +- Update README to include info about testing login locally +- Add error Modal for failed download requests +- Update preview item to prioritize UP item +- Hotfix: Update package-lock pdfjs-dist version +- Remove featured edition logic from frontend +- Chore: Add Krist, Jiayong, and Olivia as codeowners +- Add APP_ENV to yml files and update newrelic to v11.12.0 +- Fix: Improve accessibility of Edition Details publishers list + ## [0.17.6] - Upgrade newrelic to v11.5.0 @@ -10,7 +26,12 @@ - Refactor New Relic browser monitoring instrumentation - Update Playwright.yml to use same package versions as package.json - Fix: reverse node to v18.18.2 +- Add Physical Edition badge and Scan and Deliver blurb to EDD editions +- Add Library Card Required and Featured Editon badges +- Update styles for work and edition pages +- Add login buttons and info blurb UP items - SFR-1869: Add assertion step to minimize timeout errors +- Fix: Improve accessibility of CTAs and search bar ## [0.17.5] diff --git a/README.md b/README.md index 2f423053..279b35b3 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,14 @@ Run `npm run dev` to start the local server at `localhost:3000` To view pdfs locally through the webreader, you will need to set up a local proxy. If you used environment variables from `.env.sample` you should be able to pull the [web-reader](https://github.com/NYPL-Simplified/web-reader) repo, install it, and run `npm run cors-proxy`. See the web-reader repo for more [instructions](https://github.com/NYPL-Simplified/web-reader#cors-proxy) +In order to successfully login under a local deployment, you will need to update your machine's `etc/hosts` file. This hosts file maps local host names to ip addresses. + +Add this to your `etc/hosts` file: + +``` + 127.0.0.1 local.nypl.org +``` + ### Dependencies - NextJS diff --git a/config/appConfig.ts b/config/appConfig.ts index 0a9e593d..6d0de008 100644 --- a/config/appConfig.ts +++ b/config/appConfig.ts @@ -8,8 +8,8 @@ const appConfig = { api: { url: { local: "localhost:5000", - development: "http://drb-api-qa.nypl.org", - qa: "http://drb-api-qa.nypl.org", + development: "https://drb-api-qa.nypl.org", + qa: "https://drb-api-qa.nypl.org", production: "http://drb-api-qa.nypl.org", }, searchPath: "/search", diff --git a/package-lock.json b/package-lock.json index 1911b582..61773b1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "sfr-bookfinder-front-end", - "version": "0.17.6", + "version": "0.18.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "sfr-bookfinder-front-end", - "version": "0.17.6", + "version": "0.18.0", "dependencies": { "@chakra-ui/react": "2.5.4", "@newrelic/next": "^0.7.0", @@ -17,7 +17,7 @@ "dotenv": "^16.0.3", "extract-loader": "^5.1.0", "focus-trap-react": "^10.0.0", - "newrelic": "^11.5.0", + "newrelic": "^11.12.0", "next": "^13.5.6", "next-transpile-modules": "^7.0.0", "react": "^18.2.0", @@ -86,586 +86,6 @@ "node": ">=6.0.0" } }, - "node_modules/@aws-crypto/crc32": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", - "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", - "dependencies": { - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/crc32/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@aws-crypto/ie11-detection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", - "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", - "dependencies": { - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@aws-crypto/sha256-browser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", - "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", - "dependencies": { - "@aws-crypto/ie11-detection": "^3.0.0", - "@aws-crypto/sha256-js": "^3.0.0", - "@aws-crypto/supports-web-crypto": "^3.0.0", - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@aws-crypto/sha256-js": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", - "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", - "dependencies": { - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@aws-crypto/supports-web-crypto": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", - "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", - "dependencies": { - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@aws-crypto/util": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", - "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", - "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/util/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@aws-sdk/client-lambda": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-lambda/-/client-lambda-3.468.0.tgz", - "integrity": "sha512-58OuatGnq4F2rxBUs99zxrsjQAaD+zlTYPLy7nP+f39sElMCZiOMfVLM0wd2VErMvRCjILs8SxzxVRiy/SwXaQ==", - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/client-sts": "3.468.0", - "@aws-sdk/core": "3.468.0", - "@aws-sdk/credential-provider-node": "3.468.0", - "@aws-sdk/middleware-host-header": "3.468.0", - "@aws-sdk/middleware-logger": "3.468.0", - "@aws-sdk/middleware-recursion-detection": "3.468.0", - "@aws-sdk/middleware-signing": "3.468.0", - "@aws-sdk/middleware-user-agent": "3.468.0", - "@aws-sdk/region-config-resolver": "3.468.0", - "@aws-sdk/types": "3.468.0", - "@aws-sdk/util-endpoints": "3.468.0", - "@aws-sdk/util-user-agent-browser": "3.468.0", - "@aws-sdk/util-user-agent-node": "3.468.0", - "@smithy/config-resolver": "^2.0.20", - "@smithy/eventstream-serde-browser": "^2.0.15", - "@smithy/eventstream-serde-config-resolver": "^2.0.15", - "@smithy/eventstream-serde-node": "^2.0.15", - "@smithy/fetch-http-handler": "^2.3.1", - "@smithy/hash-node": "^2.0.17", - "@smithy/invalid-dependency": "^2.0.15", - "@smithy/middleware-content-length": "^2.0.17", - "@smithy/middleware-endpoint": "^2.2.2", - "@smithy/middleware-retry": "^2.0.23", - "@smithy/middleware-serde": "^2.0.15", - "@smithy/middleware-stack": "^2.0.9", - "@smithy/node-config-provider": "^2.1.7", - "@smithy/node-http-handler": "^2.2.1", - "@smithy/protocol-http": "^3.0.11", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "@smithy/url-parser": "^2.0.15", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-body-length-browser": "^2.0.1", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.22", - "@smithy/util-defaults-mode-node": "^2.0.28", - "@smithy/util-endpoints": "^1.0.6", - "@smithy/util-retry": "^2.0.8", - "@smithy/util-stream": "^2.0.23", - "@smithy/util-utf8": "^2.0.2", - "@smithy/util-waiter": "^2.0.15", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-sso": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.468.0.tgz", - "integrity": "sha512-NabkDaiFsMP8lBR3+JzdtOVarH8kCJst30fQyBIs2PI0uMfajFJ+SK9JTg1J1YZY6aNJBxo2Bxu3dl0fjZ5N/g==", - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/core": "3.468.0", - "@aws-sdk/middleware-host-header": "3.468.0", - "@aws-sdk/middleware-logger": "3.468.0", - "@aws-sdk/middleware-recursion-detection": "3.468.0", - "@aws-sdk/middleware-user-agent": "3.468.0", - "@aws-sdk/region-config-resolver": "3.468.0", - "@aws-sdk/types": "3.468.0", - "@aws-sdk/util-endpoints": "3.468.0", - "@aws-sdk/util-user-agent-browser": "3.468.0", - "@aws-sdk/util-user-agent-node": "3.468.0", - "@smithy/config-resolver": "^2.0.20", - "@smithy/fetch-http-handler": "^2.3.1", - "@smithy/hash-node": "^2.0.17", - "@smithy/invalid-dependency": "^2.0.15", - "@smithy/middleware-content-length": "^2.0.17", - "@smithy/middleware-endpoint": "^2.2.2", - "@smithy/middleware-retry": "^2.0.23", - "@smithy/middleware-serde": "^2.0.15", - "@smithy/middleware-stack": "^2.0.9", - "@smithy/node-config-provider": "^2.1.7", - "@smithy/node-http-handler": "^2.2.1", - "@smithy/protocol-http": "^3.0.11", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "@smithy/url-parser": "^2.0.15", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-body-length-browser": "^2.0.1", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.22", - "@smithy/util-defaults-mode-node": "^2.0.28", - "@smithy/util-endpoints": "^1.0.6", - "@smithy/util-retry": "^2.0.8", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-sts": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.468.0.tgz", - "integrity": "sha512-EausH7ezv1AIgl/4rfZRNRxrFND5hChbIqkuAf8e5wZ74HUEVBMmD5Jiwfs0WRCso3ejOjsNtS8PAOA3djn28w==", - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/core": "3.468.0", - "@aws-sdk/credential-provider-node": "3.468.0", - "@aws-sdk/middleware-host-header": "3.468.0", - "@aws-sdk/middleware-logger": "3.468.0", - "@aws-sdk/middleware-recursion-detection": "3.468.0", - "@aws-sdk/middleware-sdk-sts": "3.468.0", - "@aws-sdk/middleware-signing": "3.468.0", - "@aws-sdk/middleware-user-agent": "3.468.0", - "@aws-sdk/region-config-resolver": "3.468.0", - "@aws-sdk/types": "3.468.0", - "@aws-sdk/util-endpoints": "3.468.0", - "@aws-sdk/util-user-agent-browser": "3.468.0", - "@aws-sdk/util-user-agent-node": "3.468.0", - "@smithy/config-resolver": "^2.0.20", - "@smithy/fetch-http-handler": "^2.3.1", - "@smithy/hash-node": "^2.0.17", - "@smithy/invalid-dependency": "^2.0.15", - "@smithy/middleware-content-length": "^2.0.17", - "@smithy/middleware-endpoint": "^2.2.2", - "@smithy/middleware-retry": "^2.0.23", - "@smithy/middleware-serde": "^2.0.15", - "@smithy/middleware-stack": "^2.0.9", - "@smithy/node-config-provider": "^2.1.7", - "@smithy/node-http-handler": "^2.2.1", - "@smithy/protocol-http": "^3.0.11", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "@smithy/url-parser": "^2.0.15", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-body-length-browser": "^2.0.1", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.22", - "@smithy/util-defaults-mode-node": "^2.0.28", - "@smithy/util-endpoints": "^1.0.6", - "@smithy/util-retry": "^2.0.8", - "@smithy/util-utf8": "^2.0.2", - "fast-xml-parser": "4.2.5", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/core": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.468.0.tgz", - "integrity": "sha512-ezUJR9VvknKoXzNZ4wvzGi1jdkmm+/1dUYQ9Sw4r8bzlJDTsUnWbyvaDlBQh81RuhLtVkaUfTnQKoec0cwlZKQ==", - "dependencies": { - "@smithy/smithy-client": "^2.1.18", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.468.0.tgz", - "integrity": "sha512-k/1WHd3KZn0EQYjadooj53FC0z24/e4dUZhbSKTULgmxyO62pwh9v3Brvw4WRa/8o2wTffU/jo54tf4vGuP/ZA==", - "dependencies": { - "@aws-sdk/types": "3.468.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.468.0.tgz", - "integrity": "sha512-DBYsptYBq0xC+GTh+3dN3Q9/wRZiPpsHA4yCC1mskEbJfMy7EIZZKtZ8lOkZ24NOI5oea4o3L+wFTxOeFSKntA==", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.468.0", - "@aws-sdk/credential-provider-process": "3.468.0", - "@aws-sdk/credential-provider-sso": "3.468.0", - "@aws-sdk/credential-provider-web-identity": "3.468.0", - "@aws-sdk/types": "3.468.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.468.0.tgz", - "integrity": "sha512-iZlWWZXp6zAH4sP3VrqF7RpAmzl8Qr8tuVkF7ubUZhzyWzKfhLVzqRJqbMYCBPGmfZLAZWjsziPHaBErYkG/5g==", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.468.0", - "@aws-sdk/credential-provider-ini": "3.468.0", - "@aws-sdk/credential-provider-process": "3.468.0", - "@aws-sdk/credential-provider-sso": "3.468.0", - "@aws-sdk/credential-provider-web-identity": "3.468.0", - "@aws-sdk/types": "3.468.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.468.0.tgz", - "integrity": "sha512-OYSn1A/UsyPJ7Z8Q2cNhTf55O36shPmSsvOfND04nSfu1nPaR+VUvvsP7v+brhGpwC/GAKTIdGAo4blH31BS6A==", - "dependencies": { - "@aws-sdk/types": "3.468.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.468.0.tgz", - "integrity": "sha512-eIdGoIw10xyBm7TDcV5Y/W7tzNs2f4H+2G5ZdjG2XGLAELsKCoixe+9ZB662MLtLCxvm7eE1GjOjKsSua6MImQ==", - "dependencies": { - "@aws-sdk/client-sso": "3.468.0", - "@aws-sdk/token-providers": "3.468.0", - "@aws-sdk/types": "3.468.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.468.0.tgz", - "integrity": "sha512-rexymPmXjtkwCPfhnUq3EjO1rSkf39R4Jz9CqiM7OsqK2qlT5Y/V3gnMKn0ZMXsYaQOMfM3cT5xly5R+OKDHlw==", - "dependencies": { - "@aws-sdk/types": "3.468.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.468.0.tgz", - "integrity": "sha512-gwQ+/QhX+lhof304r6zbZ/V5l5cjhGRxLL3CjH1uJPMcOAbw9wUlMdl+ibr8UwBZ5elfKFGiB1cdW/0uMchw0w==", - "dependencies": { - "@aws-sdk/types": "3.468.0", - "@smithy/protocol-http": "^3.0.11", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-logger": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.468.0.tgz", - "integrity": "sha512-X5XHKV7DHRXI3f29SAhJPe/OxWRFgDWDMMCALfzhmJfCi6Jfh0M14cJKoC+nl+dk9lB+36+jKjhjETZaL2bPlA==", - "dependencies": { - "@aws-sdk/types": "3.468.0", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.468.0.tgz", - "integrity": "sha512-vch9IQib2Ng9ucSyRW2eKNQXHUPb5jUPCLA5otTW/8nGjcOU37LxQG4WrxO7uaJ9Oe8hjHO+hViE3P0KISUhtA==", - "dependencies": { - "@aws-sdk/types": "3.468.0", - "@smithy/protocol-http": "^3.0.11", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-sdk-sts": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.468.0.tgz", - "integrity": "sha512-xRy8NKfHbmafHwdbotdWgHBvRs0YZgk20GrhFJKp43bkqVbJ5bNlh3nQXf1DeFY9fARR84Bfotya4fwCUHWgZg==", - "dependencies": { - "@aws-sdk/middleware-signing": "3.468.0", - "@aws-sdk/types": "3.468.0", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-signing": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.468.0.tgz", - "integrity": "sha512-s+7fSB1gdnnTj5O0aCCarX3z5Vppop8kazbNSZADdkfHIDWCN80IH4ZNjY3OWqaAz0HmR4LNNrovdR304ojb4Q==", - "dependencies": { - "@aws-sdk/types": "3.468.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^3.0.11", - "@smithy/signature-v4": "^2.0.0", - "@smithy/types": "^2.7.0", - "@smithy/util-middleware": "^2.0.8", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.468.0.tgz", - "integrity": "sha512-lmqaEChVWK6MvNpM/LH504pRsP3p/IuZugWwxCbelKw4bGVU4IgG3mbjfATiIlHo4rW8ttHh1bTsZIGjWOqNeA==", - "dependencies": { - "@aws-sdk/types": "3.468.0", - "@aws-sdk/util-endpoints": "3.468.0", - "@smithy/protocol-http": "^3.0.11", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.468.0.tgz", - "integrity": "sha512-EkDfaumuBhDJFg4lmvWiBE8Ln4BF6hYNC2YfkjKCTEuePy5BKryFedwylYZZ3CJG/uVyfr8xBy+mvoR8plpHjg==", - "dependencies": { - "@smithy/node-config-provider": "^2.1.7", - "@smithy/types": "^2.7.0", - "@smithy/util-config-provider": "^2.0.0", - "@smithy/util-middleware": "^2.0.8", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/token-providers": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.468.0.tgz", - "integrity": "sha512-IpLbthZmFXotwtgkE1Bw4HcKjwpAsGM+6iTXs4amZJqllJClOgyV/sV5Cze+8AqanfCZoPIFTmXyg8LfJTYwbw==", - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.468.0", - "@aws-sdk/middleware-logger": "3.468.0", - "@aws-sdk/middleware-recursion-detection": "3.468.0", - "@aws-sdk/middleware-user-agent": "3.468.0", - "@aws-sdk/region-config-resolver": "3.468.0", - "@aws-sdk/types": "3.468.0", - "@aws-sdk/util-endpoints": "3.468.0", - "@aws-sdk/util-user-agent-browser": "3.468.0", - "@aws-sdk/util-user-agent-node": "3.468.0", - "@smithy/config-resolver": "^2.0.20", - "@smithy/fetch-http-handler": "^2.3.1", - "@smithy/hash-node": "^2.0.17", - "@smithy/invalid-dependency": "^2.0.15", - "@smithy/middleware-content-length": "^2.0.17", - "@smithy/middleware-endpoint": "^2.2.2", - "@smithy/middleware-retry": "^2.0.23", - "@smithy/middleware-serde": "^2.0.15", - "@smithy/middleware-stack": "^2.0.9", - "@smithy/node-config-provider": "^2.1.7", - "@smithy/node-http-handler": "^2.2.1", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^3.0.11", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "@smithy/url-parser": "^2.0.15", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-body-length-browser": "^2.0.1", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.22", - "@smithy/util-defaults-mode-node": "^2.0.28", - "@smithy/util-endpoints": "^1.0.6", - "@smithy/util-retry": "^2.0.8", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/types": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.468.0.tgz", - "integrity": "sha512-rx/9uHI4inRbp2tw3Y4Ih4PNZkVj32h7WneSg3MVgVjAoVD5Zti9KhS5hkvsBxfgmQmg0AQbE+b1sy5WGAgntA==", - "dependencies": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/util-endpoints": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.468.0.tgz", - "integrity": "sha512-P91EbMG2+1ZToJeTLaRkdO7qM7RI0svuMVLkIdHV9rHR7PeUKUWMpf46xh8rQsIjKC9Arf+I9ueWp3iHJt1T5w==", - "dependencies": { - "@aws-sdk/types": "3.468.0", - "@smithy/util-endpoints": "^1.0.6", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/util-locate-window": { - "version": "3.465.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.465.0.tgz", - "integrity": "sha512-f+QNcWGswredzC1ExNAB/QzODlxwaTdXkNT5cvke2RLX8SFU5pYk6h4uCtWC0vWPELzOfMfloBrJefBzlarhsw==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.468.0.tgz", - "integrity": "sha512-OJyhWWsDEizR3L+dCgMXSUmaCywkiZ7HSbnQytbeKGwokIhD69HTiJcibF/sgcM5gk4k3Mq3puUhGnEZ46GIig==", - "dependencies": { - "@aws-sdk/types": "3.468.0", - "@smithy/types": "^2.7.0", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.468.0.tgz", - "integrity": "sha512-9p+Zyp6xmJUkcryTNmQQwdhRK6gAC6zVEJZLomLGQhD7sWcCzstolw//mAS3AKVQFYWnCEGKrDJdgT0KObCf4g==", - "dependencies": { - "@aws-sdk/types": "3.468.0", - "@smithy/node-config-provider": "^2.1.7", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@aws-sdk/util-utf8-browser": { - "version": "3.259.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", - "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", - "dependencies": { - "tslib": "^2.3.1" - } - }, "node_modules/@axe-core/react": { "version": "4.8.2", "resolved": "https://registry.npmjs.org/@axe-core/react/-/react-4.8.2.tgz", @@ -4854,9 +4274,9 @@ } }, "node_modules/@newrelic/aws-sdk": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@newrelic/aws-sdk/-/aws-sdk-7.0.3.tgz", - "integrity": "sha512-oafBFD+DEAqXTg0w15eEu2rfoedWMS7abyW5CuOJxSb+7OWiFUx9jZPpdFblsYPVNWBehxFdws6/JEio3GklyA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@newrelic/aws-sdk/-/aws-sdk-7.2.0.tgz", + "integrity": "sha512-SiFkYBRc0ymzjTm+Ccrx5ox2slW7PbeunOzsFBDfsCxC+umFoqFZIhS10Z23gbrHwQ6wNXmwcDJdhtsxgxZANg==", "engines": { "node": ">=16.0.0" } @@ -4962,14 +4382,26 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/@newrelic/ritm": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@newrelic/ritm/-/ritm-7.2.0.tgz", + "integrity": "sha512-I4iVhm+wlTEDJXQT8EydF/U5vlR9bBHrtBGyvd/D9WCucoMtrPrCNyILQh9bZ+46E8QRE7zh6QEGyQcnc3qNMg==", + "dependencies": { + "debug": "^4.1.1", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=8.6.0" + } + }, "node_modules/@newrelic/security-agent": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@newrelic/security-agent/-/security-agent-0.5.0.tgz", - "integrity": "sha512-ycbNl362arCjKTumkXPnnbnSUARv2SEYBBpbXOfI3p1pa6PRi5kck95W7JNBzxhw/umqwHXgzHhbK8N2uKWYgA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@newrelic/security-agent/-/security-agent-1.0.1.tgz", + "integrity": "sha512-Ao5MTULa6CzI8a2poRdQ31jJA0Exv99eyewfMHYbJGAY3H4rj6TZs08rGtrDK9ZyjNTalM/DHhyyTTDq+FJM+Q==", "dependencies": { - "@aws-sdk/client-lambda": "^3.436.0", - "axios": "1.6.0", - "check-disk-space": "3.4.0", + "axios": "^1.6.5", + "check-disk-space": "^3.4.0", "content-type": "^1.0.5", "fast-safe-stringify": "^2.1.1", "find-package-json": "^1.2.0", @@ -5019,9 +4451,9 @@ } }, "node_modules/@newrelic/security-agent/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -6347,721 +5779,147 @@ "swr": "^2.0.3" } }, - "node_modules/@nypl/web-reader/node_modules/react-intersection-observer": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-8.34.0.tgz", - "integrity": "sha512-TYKh52Zc0Uptp5/b4N91XydfSGKubEhgZRtcg1rhTKABXijc4Sdr1uTp5lJ8TN27jwUsdXxjHXtHa0kPj704sw==", - "peerDependencies": { - "react": "^15.0.0 || ^16.0.0 || ^17.0.0|| ^18.0.0" - } - }, - "node_modules/@playwright/test": { - "version": "1.40.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.40.1.tgz", - "integrity": "sha512-EaaawMTOeEItCRvfmkI9v6rBkF1svM8wjl/YPRrg2N2Wmp+4qJYkWtJsbew1szfKKDm6fPLy4YAanBhIlf9dWw==", - "dev": true, - "dependencies": { - "playwright": "1.40.1" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@popperjs/core": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, - "node_modules/@prisma/prisma-fmt-wasm": { - "version": "4.17.0-16.27eb2449f178cd9fe1a4b892d732cc4795f75085", - "resolved": "https://registry.npmjs.org/@prisma/prisma-fmt-wasm/-/prisma-fmt-wasm-4.17.0-16.27eb2449f178cd9fe1a4b892d732cc4795f75085.tgz", - "integrity": "sha512-zYz3rFwPB82mVlHGknAPdnSY/a308dhPOblxQLcZgZTDRtDXOE1MgxoRAys+jekwR4/bm3+rZDPs1xsFMsPZig==", - "optional": true - }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" - }, - "node_modules/@rollup/pluginutils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", - "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.6.0.tgz", - "integrity": "sha512-2/U3GXA6YiPYQDLGwtGlnNgKYBSwCFIHf8Y9LUY5VATHdtbLlU0Y1R3QoBnT0aB4qv/BEiVVsj7LJXoQCgJ2vA==", - "dev": true - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@smithy/abort-controller": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.15.tgz", - "integrity": "sha512-JkS36PIS3/UCbq/MaozzV7jECeL+BTt4R75bwY8i+4RASys4xOyUS1HsRyUNSqUXFP4QyCz5aNnh3ltuaxv+pw==", - "dependencies": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/config-resolver": { - "version": "2.0.21", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.0.21.tgz", - "integrity": "sha512-rlLIGT+BeqjnA6C2FWumPRJS1UW07iU5ZxDHtFuyam4W65gIaOFMjkB90ofKCIh+0mLVQrQFrl/VLtQT/6FWTA==", - "dependencies": { - "@smithy/node-config-provider": "^2.1.8", - "@smithy/types": "^2.7.0", - "@smithy/util-config-provider": "^2.0.0", - "@smithy/util-middleware": "^2.0.8", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/credential-provider-imds": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.1.4.tgz", - "integrity": "sha512-cwPJN1fa1YOQzhBlTXRavABEYRRchci1X79QRwzaNLySnIMJfztyv1Zkst0iZPLMnpn8+CnHu3wOHS11J5Dr3A==", - "dependencies": { - "@smithy/node-config-provider": "^2.1.8", - "@smithy/property-provider": "^2.0.16", - "@smithy/types": "^2.7.0", - "@smithy/url-parser": "^2.0.15", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/eventstream-codec": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.0.15.tgz", - "integrity": "sha512-crjvz3j1gGPwA0us6cwS7+5gAn35CTmqu/oIxVbYJo2Qm/sGAye6zGJnMDk3BKhWZw5kcU1G4MxciTkuBpOZPg==", - "dependencies": { - "@aws-crypto/crc32": "3.0.0", - "@smithy/types": "^2.7.0", - "@smithy/util-hex-encoding": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@smithy/eventstream-serde-browser": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-2.0.15.tgz", - "integrity": "sha512-WiFG5N9j3jmS5P0z5Xev6dO0c3lf7EJYC2Ncb0xDnWFvShwXNn741AF71ABr5EcZw8F4rQma0362MMjAwJeZog==", - "dependencies": { - "@smithy/eventstream-serde-universal": "^2.0.15", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-2.0.15.tgz", - "integrity": "sha512-o65d2LRjgCbWYH+VVNlWXtmsI231SO99ZTOL4UuIPa6WTjbSHWtlXvUcJG9libhEKWmEV9DIUiH2IqyPWi7ubA==", - "dependencies": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-node": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-2.0.15.tgz", - "integrity": "sha512-9OOXiIhHq1VeOG6xdHkn2ZayfMYM3vzdUTV3zhcCnt+tMqA3BJK3XXTJFRR2BV28rtRM778DzqbBTf+hqwQPTg==", - "dependencies": { - "@smithy/eventstream-serde-universal": "^2.0.15", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-universal": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-2.0.15.tgz", - "integrity": "sha512-dP8AQp/pXlWBjvL0TaPBJC3rM0GoYv7O0Uim8d/7UKZ2Wo13bFI3/BhQfY/1DeiP1m23iCHFNFtOQxfQNBB8rQ==", - "dependencies": { - "@smithy/eventstream-codec": "^2.0.15", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/fetch-http-handler": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.3.1.tgz", - "integrity": "sha512-6MNk16fqb8EwcYY8O8WxB3ArFkLZ2XppsSNo1h7SQcFdDDwIumiJeO6wRzm7iB68xvsOQzsdQKbdtTieS3hfSQ==", - "dependencies": { - "@smithy/protocol-http": "^3.0.11", - "@smithy/querystring-builder": "^2.0.15", - "@smithy/types": "^2.7.0", - "@smithy/util-base64": "^2.0.1", - "tslib": "^2.5.0" - } - }, - "node_modules/@smithy/hash-node": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.0.17.tgz", - "integrity": "sha512-Il6WuBcI1nD+e2DM7tTADMf01wEPGK8PAhz4D+YmDUVaoBqlA+CaH2uDJhiySifmuKBZj748IfygXty81znKhw==", - "dependencies": { - "@smithy/types": "^2.7.0", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/invalid-dependency": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.0.15.tgz", - "integrity": "sha512-dlEKBFFwVfzA5QroHlBS94NpgYjXhwN/bFfun+7w3rgxNvVy79SK0w05iGc7UAeC5t+D7gBxrzdnD6hreZnDVQ==", - "dependencies": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@smithy/is-array-buffer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", - "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/middleware-content-length": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.0.17.tgz", - "integrity": "sha512-OyadvMcKC7lFXTNBa8/foEv7jOaqshQZkjWS9coEXPRZnNnihU/Ls+8ZuJwGNCOrN2WxXZFmDWhegbnM4vak8w==", - "dependencies": { - "@smithy/protocol-http": "^3.0.11", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/middleware-endpoint": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.2.3.tgz", - "integrity": "sha512-nYfxuq0S/xoAjdLbyn1ixeVB6cyH9wYCMtbbOCpcCRYR5u2mMtqUtVjjPAZ/DIdlK3qe0tpB0Q76szFGNuz+kQ==", - "dependencies": { - "@smithy/middleware-serde": "^2.0.15", - "@smithy/node-config-provider": "^2.1.8", - "@smithy/shared-ini-file-loader": "^2.2.7", - "@smithy/types": "^2.7.0", - "@smithy/url-parser": "^2.0.15", - "@smithy/util-middleware": "^2.0.8", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/middleware-retry": { - "version": "2.0.24", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.0.24.tgz", - "integrity": "sha512-q2SvHTYu96N7lYrn3VSuX3vRpxXHR/Cig6MJpGWxd0BWodUQUWlKvXpWQZA+lTaFJU7tUvpKhRd4p4MU3PbeJg==", - "dependencies": { - "@smithy/node-config-provider": "^2.1.8", - "@smithy/protocol-http": "^3.0.11", - "@smithy/service-error-classification": "^2.0.8", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "@smithy/util-middleware": "^2.0.8", - "@smithy/util-retry": "^2.0.8", - "tslib": "^2.5.0", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/middleware-retry/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/@smithy/middleware-serde": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.0.15.tgz", - "integrity": "sha512-FOZRFk/zN4AT4wzGuBY+39XWe+ZnCFd0gZtyw3f9Okn2CJPixl9GyWe98TIaljeZdqWkgrzGyPre20AcW2UMHQ==", - "dependencies": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/middleware-stack": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.9.tgz", - "integrity": "sha512-bCB5dUtGQ5wh7QNL2ELxmDc6g7ih7jWU3Kx6MYH1h4mZbv9xL3WyhKHojRltThCB1arLPyTUFDi+x6fB/oabtA==", - "dependencies": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/node-config-provider": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.1.8.tgz", - "integrity": "sha512-+w26OKakaBUGp+UG+dxYZtFb5fs3tgHg3/QrRrmUZj+rl3cIuw840vFUXX35cVPTUCQIiTqmz7CpVF7+hdINdQ==", - "dependencies": { - "@smithy/property-provider": "^2.0.16", - "@smithy/shared-ini-file-loader": "^2.2.7", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/node-http-handler": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.2.1.tgz", - "integrity": "sha512-8iAKQrC8+VFHPAT8pg4/j6hlsTQh+NKOWlctJBrYtQa4ExcxX7aSg3vdQ2XLoYwJotFUurg/NLqFCmZaPRrogw==", - "dependencies": { - "@smithy/abort-controller": "^2.0.15", - "@smithy/protocol-http": "^3.0.11", - "@smithy/querystring-builder": "^2.0.15", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/property-provider": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.16.tgz", - "integrity": "sha512-28Ky0LlOqtEjwg5CdHmwwaDRHcTWfPRzkT6HrhwOSRS2RryAvuDfJrZpM+BMcrdeCyEg1mbcgIMoqTla+rdL8Q==", - "dependencies": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/protocol-http": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.11.tgz", - "integrity": "sha512-3ziB8fHuXIRamV/akp/sqiWmNPR6X+9SB8Xxnozzj+Nq7hSpyKdFHd1FLpBkgfGFUTzzcBJQlDZPSyxzmdcx5A==", - "dependencies": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/querystring-builder": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.15.tgz", - "integrity": "sha512-e1q85aT6HutvouOdN+dMsN0jcdshp50PSCvxDvo6aIM57LqeXimjfONUEgfqQ4IFpYWAtVixptyIRE5frMp/2A==", - "dependencies": { - "@smithy/types": "^2.7.0", - "@smithy/util-uri-escape": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/querystring-parser": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.0.15.tgz", - "integrity": "sha512-jbBvoK3cc81Cj1c1TH1qMYxNQKHrYQ2DoTntN9FBbtUWcGhc+T4FP6kCKYwRLXyU4AajwGIZstvNAmIEgUUNTQ==", - "dependencies": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/service-error-classification": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.0.8.tgz", - "integrity": "sha512-jCw9+005im8tsfYvwwSc4TTvd29kXRFkH9peQBg5R/4DD03ieGm6v6Hpv9nIAh98GwgYg1KrztcINC1s4o7/hg==", - "dependencies": { - "@smithy/types": "^2.7.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/shared-ini-file-loader": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.2.7.tgz", - "integrity": "sha512-0Qt5CuiogIuvQIfK+be7oVHcPsayLgfLJGkPlbgdbl0lD28nUKu4p11L+UG3SAEsqc9UsazO+nErPXw7+IgDpQ==", - "dependencies": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/signature-v4": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.0.17.tgz", - "integrity": "sha512-ru5IUbHUAYgJ5ZqZaBi6PEsMjFT/do0Eu21Qt7b07NuRuPlwAMhlqNRDy/KE9QAF20ygehb+xe9ebmyZ26/BSA==", - "dependencies": { - "@smithy/eventstream-codec": "^2.0.15", - "@smithy/is-array-buffer": "^2.0.0", - "@smithy/types": "^2.7.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-middleware": "^2.0.8", - "@smithy/util-uri-escape": "^2.0.0", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/smithy-client": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.1.18.tgz", - "integrity": "sha512-7FqdbaJiVaHJDD9IfDhmzhSDbpjyx+ZsfdYuOpDJF09rl8qlIAIlZNoSaflKrQ3cEXZN2YxGPaNWGhbYimyIRQ==", - "dependencies": { - "@smithy/middleware-stack": "^2.0.9", - "@smithy/types": "^2.7.0", - "@smithy/util-stream": "^2.0.23", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/types": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.7.0.tgz", - "integrity": "sha512-1OIFyhK+vOkMbu4aN2HZz/MomREkrAC/HqY5mlJMUJfGrPRwijJDTeiN8Rnj9zUaB8ogXAfIOtZrrgqZ4w7Wnw==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/url-parser": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.0.15.tgz", - "integrity": "sha512-sADUncUj9rNbOTrdDGm4EXlUs0eQ9dyEo+V74PJoULY4jSQxS+9gwEgsPYyiu8PUOv16JC/MpHonOgqP/IEDZA==", - "dependencies": { - "@smithy/querystring-parser": "^2.0.15", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@smithy/util-base64": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.1.tgz", - "integrity": "sha512-DlI6XFYDMsIVN+GH9JtcRp3j02JEVuWIn/QOZisVzpIAprdsxGveFed0bjbMRCqmIFe8uetn5rxzNrBtIGrPIQ==", - "dependencies": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-body-length-browser": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.0.1.tgz", - "integrity": "sha512-NXYp3ttgUlwkaug4bjBzJ5+yIbUbUx8VsSLuHZROQpoik+gRkIBeEG9MPVYfvPNpuXb/puqodeeUXcKFe7BLOQ==", - "dependencies": { - "tslib": "^2.5.0" - } - }, - "node_modules/@smithy/util-body-length-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.1.0.tgz", - "integrity": "sha512-/li0/kj/y3fQ3vyzn36NTLGmUwAICb7Jbe/CsWCktW363gh1MOcpEcSO3mJ344Gv2dqz8YJCLQpb6hju/0qOWw==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-buffer-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", - "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", - "dependencies": { - "@smithy/is-array-buffer": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" + "node_modules/@nypl/web-reader/node_modules/react-intersection-observer": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-8.34.0.tgz", + "integrity": "sha512-TYKh52Zc0Uptp5/b4N91XydfSGKubEhgZRtcg1rhTKABXijc4Sdr1uTp5lJ8TN27jwUsdXxjHXtHa0kPj704sw==", + "peerDependencies": { + "react": "^15.0.0 || ^16.0.0 || ^17.0.0|| ^18.0.0" } }, - "node_modules/@smithy/util-config-provider": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.0.0.tgz", - "integrity": "sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==", + "node_modules/@playwright/test": { + "version": "1.40.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.40.1.tgz", + "integrity": "sha512-EaaawMTOeEItCRvfmkI9v6rBkF1svM8wjl/YPRrg2N2Wmp+4qJYkWtJsbew1szfKKDm6fPLy4YAanBhIlf9dWw==", + "dev": true, "dependencies": { - "tslib": "^2.5.0" + "playwright": "1.40.1" }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-browser": { - "version": "2.0.22", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.0.22.tgz", - "integrity": "sha512-qcF20IHHH96FlktvBRICDXDhLPtpVmtksHmqNGtotb9B0DYWXsC6jWXrkhrrwF7tH26nj+npVTqh9isiFV1gdA==", - "dependencies": { - "@smithy/property-provider": "^2.0.16", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "bowser": "^2.11.0", - "tslib": "^2.5.0" + "bin": { + "playwright": "cli.js" }, "engines": { - "node": ">= 10.0.0" + "node": ">=16" } }, - "node_modules/@smithy/util-defaults-mode-node": { - "version": "2.0.29", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.0.29.tgz", - "integrity": "sha512-+uG/15VoUh6JV2fdY9CM++vnSuMQ1VKZ6BdnkUM7R++C/vLjnlg+ToiSR1FqKZbMmKBXmsr8c/TsDWMAYvxbxQ==", - "dependencies": { - "@smithy/config-resolver": "^2.0.21", - "@smithy/credential-provider-imds": "^2.1.4", - "@smithy/node-config-provider": "^2.1.8", - "@smithy/property-provider": "^2.0.16", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 10.0.0" + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" } }, - "node_modules/@smithy/util-endpoints": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-1.0.7.tgz", - "integrity": "sha512-Q2gEind3jxoLk6hdKWyESMU7LnXz8aamVwM+VeVjOYzYT1PalGlY/ETa48hv2YpV4+YV604y93YngyzzzQ4IIA==", - "dependencies": { - "@smithy/node-config-provider": "^2.1.8", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 14.0.0" - } + "node_modules/@prisma/prisma-fmt-wasm": { + "version": "4.17.0-16.27eb2449f178cd9fe1a4b892d732cc4795f75085", + "resolved": "https://registry.npmjs.org/@prisma/prisma-fmt-wasm/-/prisma-fmt-wasm-4.17.0-16.27eb2449f178cd9fe1a4b892d732cc4795f75085.tgz", + "integrity": "sha512-zYz3rFwPB82mVlHGknAPdnSY/a308dhPOblxQLcZgZTDRtDXOE1MgxoRAys+jekwR4/bm3+rZDPs1xsFMsPZig==", + "optional": true }, - "node_modules/@smithy/util-hex-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", - "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" }, - "node_modules/@smithy/util-middleware": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.8.tgz", - "integrity": "sha512-qkvqQjM8fRGGA8P2ydWylMhenCDP8VlkPn8kiNuFEaFz9xnUKC2irfqsBSJrfrOB9Qt6pQsI58r3zvvumhFMkw==", - "dependencies": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" }, - "node_modules/@smithy/util-retry": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.0.8.tgz", - "integrity": "sha512-cQTPnVaVFMjjS6cb44WV2yXtHVyXDC5icKyIbejMarJEApYeJWpBU3LINTxHqp/tyLI+MZOUdosr2mZ3sdziNg==", - "dependencies": { - "@smithy/service-error-classification": "^2.0.8", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 14.0.0" - } + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" }, - "node_modules/@smithy/util-stream": { - "version": "2.0.23", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.23.tgz", - "integrity": "sha512-OJMWq99LAZJUzUwTk+00plyxX3ESktBaGPhqNIEVab+53gLULiWN9B/8bRABLg0K6R6Xg4t80uRdhk3B/LZqMQ==", + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", "dependencies": { - "@smithy/fetch-http-handler": "^2.3.1", - "@smithy/node-http-handler": "^2.2.1", - "@smithy/types": "^2.7.0", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" } }, - "node_modules/@smithy/util-uri-escape": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz", - "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==", + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", "dependencies": { - "tslib": "^2.5.0" + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" }, "engines": { "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } } }, - "node_modules/@smithy/util-utf8": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.2.tgz", - "integrity": "sha512-qOiVORSPm6Ce4/Yu6hbSgNHABLP2VMv8QOC3tTDNHHlWY19pPyc++fBTbZPtx6egPXi4HQxKDnMxVxpbtX2GoA==", + "node_modules/@rushstack/eslint-patch": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.6.0.tgz", + "integrity": "sha512-2/U3GXA6YiPYQDLGwtGlnNgKYBSwCFIHf8Y9LUY5VATHdtbLlU0Y1R3QoBnT0aB4qv/BEiVVsj7LJXoQCgJ2vA==", + "dev": true + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, "dependencies": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" + "type-detect": "4.0.8" } }, - "node_modules/@smithy/util-waiter": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-2.0.15.tgz", - "integrity": "sha512-9Y+btzzB7MhLADW7xgD6SjvmoYaRkrb/9SCbNGmNdfO47v38rxb90IGXyDtAK0Shl9bMthTmLgjlfYc+vtz2Qw==", + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, "dependencies": { - "@smithy/abort-controller": "^2.0.15", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" + "@sinonjs/commons": "^3.0.0" } }, "node_modules/@svgr/babel-plugin-add-jsx-attribute": { @@ -7770,9 +6628,9 @@ "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" }, "node_modules/@types/qs": { - "version": "6.9.10", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz", - "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==" + "version": "6.9.12", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.12.tgz", + "integrity": "sha512-bZcOkJ6uWrL0Qb2NAWKa7TBU+mJHPzhx9jjLL1KHF+XpzEcR7EXHvjbHlGtR/IsP1vyPrehuS6XqkmaePy//mg==" }, "node_modules/@types/react": { "version": "18.2.42", @@ -8883,11 +7741,11 @@ } }, "node_modules/axios": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz", - "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==", + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", + "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", "dependencies": { - "follow-redirects": "^1.15.0", + "follow-redirects": "^1.15.4", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -9970,11 +8828,6 @@ "node": ">=8" } }, - "node_modules/bowser": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -10083,13 +8936,18 @@ } }, "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -10956,16 +9814,19 @@ } }, "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-properties": { @@ -11273,6 +10134,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-get-iterator": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", @@ -12584,27 +11464,6 @@ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, - "node_modules/fast-xml-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", - "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", - "funding": [ - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - }, - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "dependencies": { - "strnum": "^1.0.5" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -12783,9 +11642,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", - "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", "funding": [ { "type": "individual", @@ -12988,15 +11847,19 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dependencies": { + "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -13229,11 +12092,11 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dependencies": { - "get-intrinsic": "^1.2.2" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -13402,9 +12265,9 @@ } }, "node_modules/html-entities": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", - "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", "funding": [ { "type": "github", @@ -13592,9 +12455,9 @@ } }, "node_modules/import-in-the-middle": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.5.0.tgz", - "integrity": "sha512-H2hqR0jImhqe9+1k8pYewDKWJHnDeRsWZk5aSztv6MIWD5glmbEOqy1JZrMUC6SJiO1M4A+nVvUUYtWzP5wPYg==", + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.7.3.tgz", + "integrity": "sha512-R2I11NRi0lI3jD2+qjqyVlVEahsejw7LDnYEbGb47QEFjczE3bZYsmWheCTQA+LFs2DzOQxR7Pms7naHW1V4bQ==", "dependencies": { "acorn": "^8.8.2", "acorn-import-assertions": "^1.9.0", @@ -13603,9 +12466,9 @@ } }, "node_modules/import-in-the-middle/node_modules/acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "bin": { "acorn": "bin/acorn" }, @@ -18430,25 +17293,25 @@ "peer": true }, "node_modules/newrelic": { - "version": "11.6.0", - "resolved": "https://registry.npmjs.org/newrelic/-/newrelic-11.6.0.tgz", - "integrity": "sha512-peYHffhstQlPQnjTYnXZmz/VX4rvnk8rE9n/kM0KKDbStQbcQE+COkdliDS7jg82ERf90aawu8YGWQRVD/w/GQ==", + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/newrelic/-/newrelic-11.12.0.tgz", + "integrity": "sha512-02TY9+3bp4FAmVTnajWUJyz0ESu21avajRXbsE+zYxI1XSd0tEr+DoL7z2P1BGH2KsOquoDCjR+J4rY2Q88A1A==", "dependencies": { "@grpc/grpc-js": "^1.9.4", "@grpc/proto-loader": "^0.7.5", - "@newrelic/aws-sdk": "^7.0.2", + "@newrelic/aws-sdk": "^7.1.0", "@newrelic/koa": "^8.0.1", - "@newrelic/security-agent": "0.5.0", + "@newrelic/ritm": "^7.2.0", + "@newrelic/security-agent": "^1.0.1", "@newrelic/superagent": "^7.0.1", "@tyriar/fibonacci-heap": "^2.0.7", "concat-stream": "^2.0.0", "https-proxy-agent": "^7.0.1", - "import-in-the-middle": "^1.4.2", + "import-in-the-middle": "^1.6.0", "json-bigint": "^1.0.0", "json-stringify-safe": "^5.0.0", "module-details-from-path": "^1.0.3", "readable-stream": "^3.6.1", - "require-in-the-middle": "^7.2.0", "semver": "^7.5.2", "winston-transport": "^4.5.0" }, @@ -19140,17 +18003,9 @@ } }, "node_modules/pdfjs-dist": { - "version": "2.12.313", - "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.12.313.tgz", - "integrity": "sha512-1x6iXO4Qnv6Eb+YFdN5JdUzt4pAkxSp3aLAYPX93eQCyg/m7QFzXVWJHJVtoW48CI8HCXju4dSkhQZwoheL5mA==", - "peerDependencies": { - "worker-loader": "^3.0.8" - }, - "peerDependenciesMeta": { - "worker-loader": { - "optional": true - } - } + "version": "2.6.347", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.6.347.tgz", + "integrity": "sha512-QC+h7hG2su9v/nU1wEI3SnpPIrqJODL7GTDFvR74ANKGq1AFJW16PH8VWnhpiTi9YcLSFV9xLeWSgq+ckHLdVQ==" }, "node_modules/picocolors": { "version": "1.0.0", @@ -19574,11 +18429,11 @@ ] }, "node_modules/qs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", - "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.0.tgz", + "integrity": "sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -19749,9 +18604,8 @@ } }, "node_modules/react-pdf": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-5.7.2.tgz", - "integrity": "sha512-hdDwvf007V0i2rPCqQVS1fa70CXut17SN3laJYlRHzuqcu8sLLjEoeXihty6c0Ev5g1mw31b8OT8EwRw1s8C4g==", + "version": "5.3.2", + "integrity": "sha512-gT2xeUAUJem5UWNZYIQSPJAlPDbc3mIIhEgGK8P/qo1B68WIE4R4v3tNFYM9e5nqlKnGZG4Cd68SetlmnPejwA==", "dependencies": { "@babel/runtime": "^7.0.0", "file-loader": "^6.0.0", @@ -19759,17 +18613,15 @@ "make-event-props": "^1.1.0", "merge-class-names": "^1.1.1", "merge-refs": "^1.0.0", - "pdfjs-dist": "2.12.313", - "prop-types": "^15.6.2", - "tiny-invariant": "^1.0.0", - "tiny-warning": "^1.0.0" + "pdfjs-dist": "2.6.347", + "prop-types": "^15.6.2" }, "funding": { "url": "https://github.com/wojtekmaj/react-pdf?sponsor=1" }, "peerDependencies": { - "react": "^16.3.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.3.0 || ^17.0.0 || ^18.0.0" + "react": "^16.3.0 || ^17.0.0-0", + "react-dom": "^16.3.0 || ^17.0.0-0" } }, "node_modules/react-popper": { @@ -20135,19 +18987,6 @@ "node": ">=0.10.0" } }, - "node_modules/require-in-the-middle": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.2.0.tgz", - "integrity": "sha512-3TLx5TGyAY6AOqLBoXmHkNql0HIf2RGbuMgCDT2WO/uGVAPJs6h7Kl+bN6TIZGd9bWhWPwnDnTHGtW8Iu77sdw==", - "dependencies": { - "debug": "^4.1.1", - "module-details-from-path": "^1.0.3", - "resolve": "^1.22.1" - }, - "engines": { - "node": ">=8.6.0" - } - }, "node_modules/requireindex": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", @@ -20257,9 +19096,9 @@ } }, "node_modules/rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", + "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==" }, "node_modules/rimraf": { "version": "3.0.2", @@ -20767,14 +19606,16 @@ } }, "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -20831,13 +19672,17 @@ } }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -21405,11 +20250,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strnum": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" - }, "node_modules/style-value-types": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-4.1.4.tgz", @@ -21802,11 +20642,6 @@ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" }, - "node_modules/tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" - }, "node_modules/tmp": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", diff --git a/package.json b/package.json index 4673a7fc..1a17e8fd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sfr-bookfinder-front-end", - "version": "0.17.6", + "version": "0.18.0", "private": true, "scripts": { "dev": "next dev", @@ -23,7 +23,7 @@ "dotenv": "^16.0.3", "extract-loader": "^5.1.0", "focus-trap-react": "^10.0.0", - "newrelic": "^11.5.0", + "newrelic": "^11.12.0", "next": "^13.5.6", "next-transpile-modules": "^7.0.0", "react": "^18.2.0", diff --git a/src/__tests__/Search.test.tsx b/src/__tests__/Search.test.tsx index 0a364ce0..fb45156a 100644 --- a/src/__tests__/Search.test.tsx +++ b/src/__tests__/Search.test.tsx @@ -41,7 +41,9 @@ const emptySearchResults: ApiSearchResult = { }, }; const clickFiltersButton = async () => - userEvent.click(await screen.findByRole("button", { name: "Filters (0)" })); + userEvent.click( + await screen.findByRole("button", { name: "Refine results" }) + ); describe("Renders Search Results Page", () => { beforeEach(() => { @@ -82,7 +84,7 @@ describe("Renders Search Results Page", () => { describe("Filters modal show and hide", () => { test("Filters button appears", () => { expect( - screen.getByRole("button", { name: "Filters (0)" }) + screen.getByRole("button", { name: "Refine results" }) ).toBeInTheDocument(); }); test("clicking 'filters' button shows filters contents", async () => { @@ -141,7 +143,7 @@ describe("Renders Search Results Page", () => { await userEvent.click(screen.getByRole("button", { name: "Go Back" })); expect( - screen.getByRole("button", { name: "Filters (0)" }) + screen.getByRole("button", { name: "Refine results" }) ).toBeInTheDocument(); }, 15000); }); @@ -163,7 +165,7 @@ describe("Renders Search Results Page", () => { await userEvent.click(screen.getByRole("button", { name: "Go Back" })); expect( - screen.getByRole("button", { name: "Filters (0)" }) + screen.getByRole("button", { name: "Refine results" }) ).toBeInTheDocument(); }, 15000); }); @@ -184,7 +186,7 @@ describe("Renders Search Results Page", () => { }); await userEvent.click(screen.getByRole("button", { name: "Go Back" })); expect( - screen.getByRole("button", { name: "Filters (1)" }) + screen.getByRole("button", { name: "Refine results" }) ).toBeInTheDocument(); }, 15000); }); @@ -217,7 +219,7 @@ describe("Renders Search Results Page", () => { await userEvent.click(screen.getByRole("button", { name: "Go Back" })); await userEvent.click( - screen.getByRole("button", { name: "Filters (1)" }) + screen.getByRole("button", { name: "Refine results" }) ); const languages2 = screen.getByRole("group", { @@ -247,7 +249,7 @@ describe("Renders Search Results Page", () => { }); await userEvent.click(screen.getByRole("button", { name: "Go Back" })); expect( - screen.getByRole("button", { name: "Filters (1)" }) + screen.getByRole("button", { name: "Refine results" }) ).toBeInTheDocument(); }, 15000); }); @@ -275,7 +277,7 @@ describe("Renders Search Results Page", () => { }); await userEvent.click(screen.getByRole("button", { name: "Go Back" })); expect( - screen.getByRole("button", { name: "Filters (1)" }) + screen.getByRole("button", { name: "Refine results" }) ).toBeInTheDocument(); }, 15000); }); diff --git a/src/__tests__/componentHelpers/FilterYears.tsx b/src/__tests__/componentHelpers/FilterYears.tsx index 5bbeaf10..e5d2862a 100644 --- a/src/__tests__/componentHelpers/FilterYears.tsx +++ b/src/__tests__/componentHelpers/FilterYears.tsx @@ -23,7 +23,9 @@ export const FilterYearsTests = ( }) ).toHaveValue((endYear && endYear.value) || null); if (hasApplyButton) { - expect(screen.getByRole("button", { name: "Apply" })).toBeInTheDocument(); + expect( + screen.getByRole("button", { name: "Apply year" }) + ).toBeInTheDocument(); } }); @@ -36,7 +38,7 @@ export const FilterYearsTests = ( name: "From", }); const applyButton = within(yearGroup).getByRole("button", { - name: "Apply", + name: "Apply year", }); await userEvent.type(fromInput, "1990"); expect(fromInput).toHaveValue(1990); @@ -58,7 +60,7 @@ export const FilterYearsTests = ( name: "To", }); const applyButton = within(yearGroup).getByRole("button", { - name: "Apply", + name: "Apply year", }); await userEvent.type(toInput, "1990"); await userEvent.click(applyButton); @@ -83,7 +85,7 @@ export const FilterYearsTests = ( name: "From", }); const applyButton = within(yearGroup).getByRole("button", { - name: "Apply", + name: "Apply year", }); await userEvent.type(fromInput, "1990"); await userEvent.type(toInput, "2000"); @@ -109,14 +111,14 @@ export const FilterYearsTests = ( name: "From", }); const applyButton = within(yearGroup).getByRole("button", { - name: "Apply", + name: "Apply year", }); await userEvent.type(fromInput, "1990"); await userEvent.type(toInput, "1890"); await user.click(applyButton); expect( - screen.getByText("Start date must be before End date") + screen.getByText("Error: Start date must be before End date") ).toBeInTheDocument(); expect(mockRouter).toMatchObject({}); }); diff --git a/src/__tests__/componentHelpers/SearchForm.tsx b/src/__tests__/componentHelpers/SearchForm.tsx index fd12753d..71251d1c 100644 --- a/src/__tests__/componentHelpers/SearchForm.tsx +++ b/src/__tests__/componentHelpers/SearchForm.tsx @@ -53,7 +53,7 @@ export const searchFormTests = (mockRouter) => { "Tom Nook" ); expect( - screen.queryByText("Please enter a search term") + screen.queryByText("Error: Please enter a search term") ).not.toBeInTheDocument(); expect(mockRouter).toMatchObject({ @@ -74,7 +74,7 @@ export const searchFormTests = (mockRouter) => { expect(mockRouter).toMatchObject({}); expect( - screen.getByText("Please enter a search term") + screen.getByText("Error: Please enter a search term") ).toBeInTheDocument(); }); }); diff --git a/src/__tests__/fixtures/EditionCardFixture.ts b/src/__tests__/fixtures/EditionCardFixture.ts index 821c737c..28e01279 100644 --- a/src/__tests__/fixtures/EditionCardFixture.ts +++ b/src/__tests__/fixtures/EditionCardFixture.ts @@ -106,3 +106,35 @@ export const eddEdition: WorkEdition = { }, ], }; + +export const upEdition: WorkEdition = { + ...fullEdition, + items: [ + { + links: [ + { + url: "test-link-url", + link_id: 12, + mediaType: "application/epub+xml", + flags: { + catalog: false, + download: false, + reader: true, + nypl_login: true, + }, + }, + { + url: "test-link-url-2", + link_id: 23, + mediaType: "application/epub+zip", + flags: { + catalog: false, + download: true, + reader: false, + nypl_login: true, + }, + }, + ], + }, + ], +}; diff --git a/src/__tests__/fixtures/InstanceCardFixture.ts b/src/__tests__/fixtures/InstanceCardFixture.ts new file mode 100644 index 00000000..664666fb --- /dev/null +++ b/src/__tests__/fixtures/InstanceCardFixture.ts @@ -0,0 +1,118 @@ +import { Instance } from "~/src/types/DataModel"; + +export const fullInstance: Instance = { + instance_id: 12345, + publishers: [{ name: "publisher_1", roles: ["publisher"] }], + publication_place: "Paris", + title: "title", + items: [ + { + links: [ + { + url: "test-link-url", + link_id: 12, + mediaType: "application/epub+xml", + flags: { + catalog: false, + download: false, + reader: true, + }, + }, + { + url: "test-link-url-2", + link_id: 23, + mediaType: "application/epub+zip", + flags: { + catalog: false, + download: true, + reader: false, + }, + }, + { + url: "test-link-url-3", + link_id: 34, + mediaType: "application/html+edd", + flags: { + catalog: false, + download: false, + reader: false, + edd: true, + }, + }, + ], + rights: [ + { + license: "license content", + rightsStatement: "test rights statement", + }, + ], + }, + ], + identifiers: [ + { + authority: "ddc", + identifier: "300", + }, + { + authority: "oclc", + identifier: "1014189544", + }, + { + authority: "oclc", + identifier: "1030816762", + }, + ], +}; + +export const eddInstance: Instance = { + ...fullInstance, + items: [ + { + links: [ + { + url: "test-link-url", + link_id: 1, + mediaType: "application/html+edd", + flags: { + catalog: false, + download: false, + reader: false, + edd: true, + }, + }, + ], + }, + ], +}; + +export const upInstance: Instance = { + ...fullInstance, + items: [ + { + links: [ + { + url: "test-link-url", + link_id: 12, + mediaType: "application/epub+xml", + flags: { + catalog: false, + download: false, + reader: true, + nypl_login: true, + }, + }, + { + url: "test-link-url-2", + link_id: 23, + mediaType: "application/epub+zip", + flags: { + catalog: false, + download: true, + reader: false, + nypl_login: true, + }, + }, + ], + }, + ], +}; diff --git a/src/components/AuthorsList/AuthorsList.tsx b/src/components/AuthorsList/AuthorsList.tsx new file mode 100644 index 00000000..b5476f17 --- /dev/null +++ b/src/components/AuthorsList/AuthorsList.tsx @@ -0,0 +1,42 @@ +import React from "react"; +import { ApiSearchQuery } from "~/src/types/SearchQuery"; +import Link from "~/src/components/Link/Link"; +import { Agent } from "~/src/types/DataModel"; + +const AuthorsList: React.FC<{ authors: Agent[] }> = ({ authors }) => { + if (!authors || authors.length === 0) return undefined; + + return ( + <> + {authors.map((author: Agent, i: number) => { + const authorLinkText = author.name; + const query: ApiSearchQuery = { + query: author.viaf ? `viaf:${author.viaf}` : `author:${author.name}`, + }; + if (author.viaf) { + query.display = `author:${author.name}`; + } + return ( + + + {authorLinkText} + + {i < authors.length - 1 && ", "} + + ); + })} + + ); +}; + +export default AuthorsList; diff --git a/src/components/CollectionCard/CollectionCard.tsx b/src/components/CollectionCard/CollectionCard.tsx index 62b0959c..5325ef78 100644 --- a/src/components/CollectionCard/CollectionCard.tsx +++ b/src/components/CollectionCard/CollectionCard.tsx @@ -5,7 +5,6 @@ import { CardHeading, } from "@nypl/design-system-react-components"; import { Opds2Feed } from "~/src/types/OpdsModel"; -import CollectionUtils from "~/src/util/CollectionUtils"; import { truncateStringOnWhitespace } from "~/src/util/Util"; import { MAX_DESCRIPTION_LENGTH, @@ -16,11 +15,8 @@ import { export const CollectionCard: React.FC<{ collection: Opds2Feed }> = ({ collection, }) => { - const collectionId = CollectionUtils.getId(collection.links); - return ( { expect(screen.queryByText("Read Online")).toBeInTheDocument(); }); test("Shows cover", () => { - expect(screen.getByAltText("Cover").closest("img").src).toEqual( + expect(screen.getByAltText("").closest("img").src).toEqual( "https://test-sfr-covers.s3.amazonaws.com/default/defaultCover.png" ); }); diff --git a/src/components/CollectionItemCard/CollectionItemCard.tsx b/src/components/CollectionItemCard/CollectionItemCard.tsx index 7429a4b9..93abd1af 100644 --- a/src/components/CollectionItemCard/CollectionItemCard.tsx +++ b/src/components/CollectionItemCard/CollectionItemCard.tsx @@ -31,12 +31,11 @@ export const CollectionItemCard: React.FC<{ src: CollectionUtils.getCover(collectionItem), size: "xsmall", aspectRatio: "original", - alt: "Cover", + alt: ``, }} layout="row" isBordered isAlignedRightActions - id={`card-${CollectionUtils.getId(collectionItem.links)}`} p="s" > {/* If a digital version exists, link directly */} - + ); } if (eddLink) { - return ; + return ; } return <>Not yet available; diff --git a/src/components/CollectionItemCard/DownloadLink.tsx b/src/components/CollectionItemCard/DownloadLink.tsx index 36f09a7e..70919aa7 100644 --- a/src/components/CollectionItemCard/DownloadLink.tsx +++ b/src/components/CollectionItemCard/DownloadLink.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { Button, Icon } from "@nypl/design-system-react-components"; +import { Icon } from "@nypl/design-system-react-components"; import CollectionUtils from "~/src/util/CollectionUtils"; import Link from "~/src/components/Link/Link"; import { OpdsLink } from "~/src/types/OpdsModel"; @@ -26,23 +26,20 @@ const DownloadLink: React.FC<{ links: OpdsLink[]; title: string }> = ({ if (selectedLink && selectedLink.href) { return ( - - + + + Download PDF ); } diff --git a/src/components/CollectionItemCard/EddLink.tsx b/src/components/CollectionItemCard/EddLink.tsx index bf7a973c..cd4904a8 100644 --- a/src/components/CollectionItemCard/EddLink.tsx +++ b/src/components/CollectionItemCard/EddLink.tsx @@ -1,41 +1,36 @@ import React from "react"; -import { Box } from "@nypl/design-system-react-components"; -import { SCAN_AND_DELIVER_LINK } from "~/src/constants/links"; import { OpdsLink } from "~/src/types/OpdsModel"; import Link from "~/src/components/Link/Link"; +import { LOGIN_LINK_BASE } from "~/src/constants/links"; export const EddLink: React.FC<{ eddLink: OpdsLink; isLoggedIn: boolean; -}> = ({ eddLink, isLoggedIn }) => { + title: string; +}> = ({ eddLink, isLoggedIn, title }) => { if (isLoggedIn) { return ( <> - You can request a partial scan via NYPL - - Scan and Deliver - - Request + Request Scan ); } else { return ( <> - May be available via NYPL

- Log in for options + Log in to request scan ); diff --git a/src/components/CollectionItemCard/ReadOnlineLink.tsx b/src/components/CollectionItemCard/ReadOnlineLink.tsx index 74f8e85a..7aed23a5 100644 --- a/src/components/CollectionItemCard/ReadOnlineLink.tsx +++ b/src/components/CollectionItemCard/ReadOnlineLink.tsx @@ -5,7 +5,10 @@ import { formatUrl } from "~/src/util/Util"; import Link from "~/src/components/Link/Link"; // "Read Online" button should only show up if the link was flagged as "reader" or "embed" -const ReadOnlineLink: React.FC<{ links: OpdsLink[] }> = ({ links }) => { +const ReadOnlineLink: React.FC<{ links: OpdsLink[]; title: string }> = ({ + links, + title, +}) => { const localLink = CollectionUtils.getReadLink(links, "readable"); const embeddedLink = CollectionUtils.getReadLink(links, "embedable"); @@ -20,6 +23,7 @@ const ReadOnlineLink: React.FC<{ links: OpdsLink[] }> = ({ links }) => { pathname: formatUrl(readOnlineLink.href), }} linkType="button" + aria-label={`${title} Read Online`} > Read Online diff --git a/src/components/EditionCard/CardRequiredBadge.tsx b/src/components/EditionCard/CardRequiredBadge.tsx new file mode 100644 index 00000000..995967c2 --- /dev/null +++ b/src/components/EditionCard/CardRequiredBadge.tsx @@ -0,0 +1,12 @@ +import React from "react"; +import { StatusBadge } from "@nypl/design-system-react-components"; + +const CardRequiredBadge: React.FC = () => { + return ( + + Library Card Required + + ); +}; + +export default CardRequiredBadge; diff --git a/src/components/EditionCard/Ctas.tsx b/src/components/EditionCard/Ctas.tsx new file mode 100644 index 00000000..7389cc5e --- /dev/null +++ b/src/components/EditionCard/Ctas.tsx @@ -0,0 +1,55 @@ +import React from "react"; +import { useCookies } from "react-cookie"; +import { NYPL_SESSION_ID } from "~/src/constants/auth"; +import { ApiItem } from "~/src/types/DataModel"; +import EditionCardUtils from "~/src/util/EditionCardUtils"; +import DownloadLink from "./DownloadLink"; +import EddLink from "./EddLink"; +import ReadOnlineLink from "./ReadOnlineLink"; + +const Ctas: React.FC<{ + item: ApiItem | undefined; + title: string; +}> = ({ item, title }) => { + // cookies defaults to be undefined if not fonud + const [cookies] = useCookies([NYPL_SESSION_ID]); + const loginCookie = cookies[NYPL_SESSION_ID]; + const isLoggedIn = !!loginCookie; + + const readOnlineLink = EditionCardUtils.getReadOnlineLink(item); + const downloadLink = EditionCardUtils.selectDownloadLink(item); + + if (readOnlineLink || downloadLink) { + return ( + <> + {readOnlineLink && ( + + )} + {downloadLink && ( + + )} + + ); + } + + const eddLink = + item && item.links ? item.links.find((link) => link.flags.edd) : undefined; + + // Offer EDD if available + if (eddLink !== undefined) { + return ; + } + + return <>Not yet available; +}; + +export default Ctas; diff --git a/src/components/EditionCard/DownloadLink.tsx b/src/components/EditionCard/DownloadLink.tsx new file mode 100644 index 00000000..0aaad4df --- /dev/null +++ b/src/components/EditionCard/DownloadLink.tsx @@ -0,0 +1,98 @@ +import { + Box, + Flex, + Icon, + useModal, +} from "@nypl/design-system-react-components"; +import { useRouter } from "next/router"; +import React, { useState } from "react"; +import Link from "~/src/components/Link/Link"; +import { LOGIN_LINK_BASE } from "~/src/constants/links"; +import { trackCtaClick } from "~/src/lib/adobe/Analytics"; +import { fulfillFetcher } from "~/src/lib/api/SearchApi"; +import { ItemLink } from "~/src/types/DataModel"; +import { formatUrl } from "~/src/util/Util"; + +const DownloadLink: React.FC<{ + downloadLink: ItemLink; + title: string; + isLoggedIn: boolean; + loginCookie?: any; +}> = ({ downloadLink, title, isLoggedIn, loginCookie }) => { + const router = useRouter(); + let errorModalMessage; + const { onOpen, Modal } = useModal(); + const [modalProps, setModalProps] = useState({ + bodyContent: null, + closeButtonLabel: "", + headingText: null, + }); + if (downloadLink && downloadLink.url) { + let linkText = "Download PDF"; + let linkUrl = formatUrl(downloadLink.url); + + const handleDownload = async (e) => { + if (linkUrl.includes("/fulfill/")) { + e.preventDefault(); + const errorMessage = await fulfillFetcher(linkUrl, loginCookie, router); + if (errorMessage !== undefined) { + errorModalMessage = ( + + We were unable to download your item. The system reports ‘ + {errorMessage}’.
Please try again or{" "} + + contact us + {" "} + for assistance. +
+ ); + const errorHeading = ( + + + Download failed + + ); + setModalProps({ + bodyContent: errorModalMessage, + closeButtonLabel: "OK", + headingText: errorHeading, + }); + onOpen(); + } + } + trackCtaClick({ + cta_section: `${title}`, + cta_text: "Download", + destination_url: `${linkUrl}`, + }); + }; + + if (downloadLink.flags.nypl_login && !isLoggedIn) { + linkText = "Log in to download PDF"; + linkUrl = LOGIN_LINK_BASE + encodeURIComponent(window.location.href); + } + + return ( + + handleDownload(e)} + aria-label={`${title} Download PDF`} + > + + {linkText} + + + + ); + } +}; + +export default DownloadLink; diff --git a/src/components/EditionCard/EddLink.tsx b/src/components/EditionCard/EddLink.tsx new file mode 100644 index 00000000..3d90631b --- /dev/null +++ b/src/components/EditionCard/EddLink.tsx @@ -0,0 +1,40 @@ +import React from "react"; +import Link from "~/src/components/Link/Link"; +import { LOGIN_LINK_BASE } from "~/src/constants/links"; +import { ItemLink } from "~/src/types/DataModel"; + +const EddLink: React.FC<{ + eddLink: ItemLink; + isLoggedIn: boolean; + title: string; +}> = ({ eddLink, isLoggedIn, title }) => { + if (isLoggedIn) { + return ( + <> + + Request Scan + + + ); + } else { + return ( + <> + + Log in to request scan + + + ); + } +}; + +export default EddLink; diff --git a/src/components/EditionCard/EditionCard.test.tsx b/src/components/EditionCard/EditionCard.test.tsx index 2ddb2f86..c4f4a652 100644 --- a/src/components/EditionCard/EditionCard.test.tsx +++ b/src/components/EditionCard/EditionCard.test.tsx @@ -5,9 +5,12 @@ import { PLACEHOLDER_COVER_LINK } from "~/src/constants/editioncard"; import { eddEdition, fullEdition, + upEdition, } from "~/src/__tests__/fixtures/EditionCardFixture"; import { NYPL_SESSION_ID } from "~/src/constants/auth"; +jest.mock("next/router", () => require("next-router-mock")); + describe("Edition Card with Valid Data", () => { beforeEach(() => { render(); @@ -98,17 +101,19 @@ describe("Edition with EDD", () => { expect(screen.queryByText("Download PDF")).toBeInTheDocument(); expect(screen.queryByText("Read Online")).toBeInTheDocument(); - expect(screen.queryByText("Log in for options")).not.toBeInTheDocument(); - expect(screen.queryByText("Request")).not.toBeInTheDocument(); + expect( + screen.queryByText("Log in to request scan") + ).not.toBeInTheDocument(); + expect(screen.queryByText("Request Scan")).not.toBeInTheDocument(); }); test("Shows Login button when EDD is available but user is not logged in", () => { render(); expect( - screen.getByRole("link", { name: "Log in for options" }) + screen.getByRole("link", { name: "Log in to request scan for title" }) ).toBeInTheDocument(); expect( - screen.getByRole("link", { name: "Log in for options" }) + screen.getByRole("link", { name: "Log in to request scan for title" }) ).toHaveAttribute( "href", expect.stringContaining("https://login.nypl.org/auth/login") @@ -117,23 +122,75 @@ describe("Edition with EDD", () => { expect(screen.queryByText("Read Online")).not.toBeInTheDocument(); }); - test("Shows EDD Request button and 'Scan and Deliver' link when user is logged in", () => { + test("Shows EDD Request button when user is logged in", () => { // Set cookie before rendering the component document.cookie = `${NYPL_SESSION_ID}="randomvalue"`; render(); - expect(screen.getByRole("link", { name: "Request" })).toBeInTheDocument(); - expect(screen.getByRole("link", { name: "Request" })).toHaveAttribute( - "href", - expect.stringContaining("test-link-url") - ); + expect( + screen.getByRole("link", { name: "Request scan for title" }) + ).toBeInTheDocument(); + expect( + screen.getByRole("link", { name: "Request scan for title" }) + ).toHaveAttribute("href", expect.stringContaining("test-link-url")); + expect(screen.queryByText("Download PDF")).not.toBeInTheDocument(); + expect(screen.queryByText("Read Online")).not.toBeInTheDocument(); + }); + + test("Shows 'Physical Edition' badge and 'Scan and Deliver' link", () => { + render(); + expect(screen.getByText("Physical Edition")).toBeInTheDocument(); expect( screen.getByRole("link", { name: "Scan and Deliver" }) ).toBeInTheDocument(); expect( screen.getByRole("link", { name: "Scan and Deliver" }) ).toHaveAttribute("href", "https://www.nypl.org/research/scan-and-deliver"); + }); +}); + +describe("Edition with UP", () => { + test("Shows Login button when user is not logged in", () => { + document.cookie = `${NYPL_SESSION_ID}=""`; + render(); + expect( + screen.getByRole("link", { name: "title Log in to read online" }) + ).toBeInTheDocument(); + expect( + screen.getByRole("link", { name: "title Log in to read online" }) + ).toHaveAttribute( + "href", + expect.stringContaining("https://login.nypl.org/auth/login") + ); expect(screen.queryByText("Download PDF")).not.toBeInTheDocument(); expect(screen.queryByText("Read Online")).not.toBeInTheDocument(); }); + test("Shows 'Library Card Required' badge", () => { + render(); + expect(screen.getByText("Library Card Required")).toBeInTheDocument(); + }); + test("Shows Read Online and Download buttons when user is logged in", () => { + // Set cookie before rendering the component + document.cookie = `${NYPL_SESSION_ID}="randomvalue"`; + render(); + + expect( + screen.getByRole("link", { name: "title Read Online" }) + ).toBeInTheDocument(); + expect( + screen.getByRole("link", { name: "title Read Online" }) + ).toHaveAttribute("href", expect.stringContaining("/read/12")); + expect( + screen.getByRole("link", { name: "title Download PDF" }) + ).toBeInTheDocument(); + expect( + screen.getByRole("link", { name: "title Download PDF" }) + ).toHaveAttribute("href", expect.stringContaining("test-link-url")); + }); + test("Shows blurb with publisher", () => { + render(); + expect( + screen.getByText("Digitalized by NYPL with permission of publisher_1") + ).toBeInTheDocument(); + }); }); diff --git a/src/components/EditionCard/EditionCard.tsx b/src/components/EditionCard/EditionCard.tsx index dcd5a6cf..a5143e24 100644 --- a/src/components/EditionCard/EditionCard.tsx +++ b/src/components/EditionCard/EditionCard.tsx @@ -5,20 +5,26 @@ import { CardHeading, Box, CardActions, + Flex, } from "@nypl/design-system-react-components"; import Link from "../Link/Link"; import { WorkEdition } from "~/src/types/DataModel"; import EditionCardUtils from "~/src/util/EditionCardUtils"; import { PLACEHOLDER_COVER_LINK } from "~/src/constants/editioncard"; -import { useCookies } from "react-cookie"; -import { NYPL_SESSION_ID } from "~/src/constants/auth"; - -export const EditionCard: React.FC<{ edition: WorkEdition; title: string }> = ({ - edition, - title, -}) => { - const [cookies] = useCookies([NYPL_SESSION_ID]); +import Ctas from "./Ctas"; +import LanguageDisplayText from "./LanguageDisplayText"; +import PublisherAndLocation from "./PublisherAndLocation"; +import CardRequiredBadge from "./CardRequiredBadge"; +import FeaturedEditionBadge from "./FeaturedEditionBadge"; +import PhysicalEditionBadge from "./PhysicalEditionBadge"; +import ScanAndDeliverBlurb from "./ScanAndDeliverBlurb"; +import UpBlurb from "./UpBlurb"; +export const EditionCard: React.FC<{ + edition: WorkEdition; + title: string; + isFeaturedEdition?: boolean; +}> = ({ edition, title, isFeaturedEdition }) => { const previewItem = EditionCardUtils.getPreviewItem(edition.items); const editionYearElem = (edition: WorkEdition) => { @@ -42,58 +48,92 @@ export const EditionCard: React.FC<{ edition: WorkEdition; title: string }> = ({ }; const coverUrl = EditionCardUtils.getCover(edition.links); + const isPhysicalEdition = EditionCardUtils.isPhysicalEdition(previewItem); + const isUniversityPress = EditionCardUtils.isUniversityPress(previewItem); + const isLoginRequired = isPhysicalEdition || isUniversityPress; return ( - - + {isLoginRequired && } + {isFeaturedEdition && } + + - {editionYearElem(edition)} - - - - {EditionCardUtils.getPublisherAndLocation( - edition.publication_place, - edition.publishers - )} - {EditionCardUtils.getLanguageDisplayText(edition)} - {EditionCardUtils.getLicense(previewItem)} - - - - {EditionCardUtils.getCtas( - previewItem, - title, - !!cookies[NYPL_SESSION_ID] - )} - - + + + {editionYearElem(edition)} + {isPhysicalEdition && } + + + + + + + + {EditionCardUtils.getLicense(previewItem)} + + + {isPhysicalEdition && } + {isUniversityPress && } + + + + +
+ ); }; diff --git a/src/components/EditionCard/FeaturedEditionBadge.tsx b/src/components/EditionCard/FeaturedEditionBadge.tsx new file mode 100644 index 00000000..23accff9 --- /dev/null +++ b/src/components/EditionCard/FeaturedEditionBadge.tsx @@ -0,0 +1,12 @@ +import React from "react"; +import { StatusBadge } from "@nypl/design-system-react-components"; + +const FeaturedEditionBadge: React.FC = () => { + return ( + + Featured Edition + + ); +}; + +export default FeaturedEditionBadge; diff --git a/src/components/EditionCard/LanguageDisplayText.tsx b/src/components/EditionCard/LanguageDisplayText.tsx new file mode 100644 index 00000000..5059e355 --- /dev/null +++ b/src/components/EditionCard/LanguageDisplayText.tsx @@ -0,0 +1,13 @@ +import { Box } from "@nypl/design-system-react-components"; +import React from "react"; +import { WorkEdition } from "~/src/types/DataModel"; +import EditionCardUtils from "~/src/util/EditionCardUtils"; + +const LanguageDisplayText: React.FC<{ edition: WorkEdition }> = ({ + edition, +}) => { + const languageText = EditionCardUtils.getLanguageDisplayText(edition); + return {languageText}; +}; + +export default LanguageDisplayText; diff --git a/src/components/EditionCard/PhysicalEditionBadge.tsx b/src/components/EditionCard/PhysicalEditionBadge.tsx new file mode 100644 index 00000000..6be5bf84 --- /dev/null +++ b/src/components/EditionCard/PhysicalEditionBadge.tsx @@ -0,0 +1,8 @@ +import React from "react"; +import { StatusBadge } from "@nypl/design-system-react-components"; + +const PhysicalEditionBadge: React.FC = () => { + return Physical Edition; +}; + +export default PhysicalEditionBadge; diff --git a/src/components/EditionCard/PublisherAndLocation.tsx b/src/components/EditionCard/PublisherAndLocation.tsx new file mode 100644 index 00000000..c2675564 --- /dev/null +++ b/src/components/EditionCard/PublisherAndLocation.tsx @@ -0,0 +1,24 @@ +import React from "react"; +import { Agent } from "~/src/types/DataModel"; +import EditionCardUtils from "~/src/util/EditionCardUtils"; + +const PublisherAndLocation: React.FC<{ + pubPlace: string; + publishers: Agent[]; +}> = ({ pubPlace, publishers }) => { + const displayLocation = + EditionCardUtils.getPublisherDisplayLocation(pubPlace) || ""; + + const displayName = + EditionCardUtils.getPublishersDisplayText(publishers) || ""; + + return ( + <> + {!displayLocation && !displayName + ? "Publisher and Location Unknown" + : `Published${displayLocation}${displayName}`} + + ); +}; + +export default PublisherAndLocation; diff --git a/src/components/EditionCard/ReadOnlineLink.tsx b/src/components/EditionCard/ReadOnlineLink.tsx new file mode 100644 index 00000000..95e84f95 --- /dev/null +++ b/src/components/EditionCard/ReadOnlineLink.tsx @@ -0,0 +1,38 @@ +import { Box } from "@nypl/design-system-react-components"; +import React from "react"; +import Link from "~/src/components/Link/Link"; +import { LOGIN_LINK_BASE } from "~/src/constants/links"; +import { ItemLink } from "~/src/types/DataModel"; + +// "Read Online" button should only show up if the link was flagged as "reader" or "embed" +const ReadOnlineLink: React.FC<{ + readOnlineLink: ItemLink; + isLoggedIn: boolean; + title: string; +}> = ({ readOnlineLink, isLoggedIn, title }) => { + let linkText = "Read Online"; + let linkUrl: any = { + pathname: `/read/${readOnlineLink.link_id}`, + }; + + if (readOnlineLink.flags.nypl_login && !isLoggedIn) { + linkText = "Log in to read online"; + linkUrl = LOGIN_LINK_BASE + encodeURIComponent(window.location.href); + } + + return ( + readOnlineLink && ( + + + {linkText} + + + ) + ); +}; + +export default ReadOnlineLink; diff --git a/src/components/EditionCard/ScanAndDeliverBlurb.tsx b/src/components/EditionCard/ScanAndDeliverBlurb.tsx new file mode 100644 index 00000000..c2f892bd --- /dev/null +++ b/src/components/EditionCard/ScanAndDeliverBlurb.tsx @@ -0,0 +1,22 @@ +import { Flex, Icon, Text } from "@nypl/design-system-react-components"; +import React from "react"; +import Link from "../Link/Link"; +import { SCAN_AND_DELIVER_LINK } from "~/src/constants/links"; + +const ScanAndDeliverBlurb: React.FC = () => { + return ( + + + + A partial scan of this edition can be requested via NYPL's{" "} + Scan and Deliver service + + + ); +}; + +export default ScanAndDeliverBlurb; diff --git a/src/components/EditionCard/UpBlurb.tsx b/src/components/EditionCard/UpBlurb.tsx new file mode 100644 index 00000000..58f2c11c --- /dev/null +++ b/src/components/EditionCard/UpBlurb.tsx @@ -0,0 +1,22 @@ +import { Flex, Icon, Text } from "@nypl/design-system-react-components"; +import React from "react"; +import { Agent } from "~/src/types/DataModel"; +import EditionCardUtils from "~/src/util/EditionCardUtils"; + +const UpBlurb: React.FC<{ publishers: Agent[] }> = ({ publishers }) => { + const publisher = EditionCardUtils.getUpPublisher(publishers); + return ( + + + + Digitalized by NYPL with permission of {publisher} + + + ); +}; + +export default UpBlurb; diff --git a/src/components/EditionCard/ViewEditionsLink.tsx b/src/components/EditionCard/ViewEditionsLink.tsx new file mode 100644 index 00000000..66271461 --- /dev/null +++ b/src/components/EditionCard/ViewEditionsLink.tsx @@ -0,0 +1,28 @@ +import { Box } from "@nypl/design-system-react-components"; +import React from "react"; +import { ApiWork } from "~/src/types/WorkQuery"; +import Link from "../Link/Link"; + +const ViewEditionsLink: React.FC<{ work: ApiWork }> = ({ work }) => { + const editionCount = work.edition_count; + const previewEdition = work.editions && work.editions[0]; + + return ( + editionCount > 1 && ( + + + {`View All ${editionCount} Editions`} + + + ) + ); +}; + +export default ViewEditionsLink; diff --git a/src/components/EditionDetail/Edition.test.tsx b/src/components/EditionDetail/Edition.test.tsx index fa77af87..7454912b 100644 --- a/src/components/EditionDetail/Edition.test.tsx +++ b/src/components/EditionDetail/Edition.test.tsx @@ -66,15 +66,13 @@ describe("Renders edition component when given valid edition", () => { ); }); }); - test("Three cards show up in page", () => { - expect( - screen.getByRole("heading", { name: "Featured Copy" }) - ).toBeInTheDocument(); + test("Two cards show up in page", () => { + expect(screen.getByText("Featured Edition")).toBeInTheDocument(); const featuredEditionHeadings = screen.getAllByRole("heading", { name: "1923", }); - expect(featuredEditionHeadings.length).toEqual(3); - expect(screen.getAllByAltText("Cover").length).toBe(3); + expect(featuredEditionHeadings.length).toEqual(2); + expect(screen.getAllByAltText("").length).toBe(2); expect( screen .getAllByText("License: Public Domain when viewed in the US")[0] @@ -82,12 +80,12 @@ describe("Renders edition component when given valid edition", () => { ).toContain("/license"); }); - test("Featured Card, which has publisher 'Miller', shows up twice", () => { + test("Featured Card, which has publisher 'Miller', shows up once", () => { expect( screen.getAllByText("Published in Paris, France by Miller,", { selector: "div", }).length - ).toBe(2); + ).toBe(1); }); test("Shows Details Table", () => { expect( @@ -191,12 +189,12 @@ describe("All Copies Toggle", () => { render(); }); - test("Featured Card, which has publisher 'Publisher 1', shows up twice", () => { + test("Featured Card, which has publisher 'Publisher 1', shows up once", () => { expect( screen.getAllByText("Published in Paris, France by Publisher 1", { selector: "div", }).length - ).toBe(2); + ).toBe(1); }); }); }); diff --git a/src/components/EditionDetail/Edition.tsx b/src/components/EditionDetail/Edition.tsx index cac149fa..753c5845 100644 --- a/src/components/EditionDetail/Edition.tsx +++ b/src/components/EditionDetail/Edition.tsx @@ -20,15 +20,12 @@ import EditionDetailDefinitionList from "~/src/components/EditionDetailDefinitio import Link from "~/src/components/Link/Link"; import { InstanceCard } from "../InstanceCard/InstanceCard"; import SearchHeader from "../SearchHeader/SearchHeader"; -import { - joinArrayOfElements, - truncateStringOnWhitespace, -} from "~/src/util/Util"; +import { truncateStringOnWhitespace } from "~/src/util/Util"; import { MAX_TITLE_LENGTH } from "~/src/constants/editioncard"; import { Instance } from "~/src/types/DataModel"; -import EditionCardUtils from "~/src/util/EditionCardUtils"; import { PLACEHOLDER_LINK } from "~/src/constants/collection"; import DrbBreakout from "../DrbBreakout/DrbBreakout"; +import AuthorsList from "../AuthorsList/AuthorsList"; const Edition: React.FC<{ editionResult: EditionResult; backUrl?: string }> = ( props @@ -39,7 +36,6 @@ const Edition: React.FC<{ editionResult: EditionResult; backUrl?: string }> = ( const { pathname, query } = router; const featuredItemId = query.featured as string; const edition: ApiEdition = props.editionResult.data; - const authorsList = EditionCardUtils.getAuthorsList(edition.work_authors); const passedInFeaturedItem = featuredItemId ? edition.instances.find((instance) => { @@ -119,20 +115,20 @@ const Edition: React.FC<{ editionResult: EditionResult; backUrl?: string }> = ( )} {edition.sub_title && {edition.sub_title}} - {authorsList && authorsList.length && ( - By {joinArrayOfElements(authorsList, "")} + {edition.work_authors && edition.work_authors.length && ( + + By + )} {featuredInstance && ( - <> - - Featured Copy - - - - - - + + + )} {edition.inCollections && edition.inCollections.length > 0 && ( @@ -178,11 +174,17 @@ const Edition: React.FC<{ editionResult: EditionResult; backUrl?: string }> = ( const contentPrimaryElement = ( <> - - {edition.instances && ( + {edition.instances && edition.instances.length > 1 && ( + + )} + {edition.instances && edition.instances.length > 1 && ( - All Copies + Other Copies = ( )} - {edition.instances.map((instance) => ( - - ))} + {edition.instances + .filter((instance) => { + if (!featuredInstance) return true; + return instance.instance_id !== featuredInstance.instance_id; + }) + .map((instance) => ( + + ))} ); diff --git a/src/components/EditionDetailDefinitionList/EditionDetailDefinitionList.tsx b/src/components/EditionDetailDefinitionList/EditionDetailDefinitionList.tsx index 29433cd1..c44f5a72 100644 --- a/src/components/EditionDetailDefinitionList/EditionDetailDefinitionList.tsx +++ b/src/components/EditionDetailDefinitionList/EditionDetailDefinitionList.tsx @@ -4,19 +4,19 @@ import { ApiEdition } from "~/src/types/EditionQuery"; import { Agent } from "~/src/types/DataModel"; // Publisher -const getPublishersList = (publishers: Agent[]): JSX.Element[] => { +const getPublishersList = (publishers: Agent[]): JSX.Element => { if (!publishers || publishers.length === 0) { - return [ - Publisher Unavailable, - ]; - } - return publishers.map((publisher: Agent) => { return ( - -
  • {publisher.name}
  • -
    + Publisher Unavailable ); - }); + } + return ( + + {publishers.map((publisher: Agent) => { + return
  • {publisher.name}
  • ; + })} +
    + ); }; export const EditionDetailDefinitionList: React.FC<{ edition: ApiEdition }> = ({ diff --git a/src/components/FilterYears/FilterYears.tsx b/src/components/FilterYears/FilterYears.tsx index 2f180539..62f7a8a5 100644 --- a/src/components/FilterYears/FilterYears.tsx +++ b/src/components/FilterYears/FilterYears.tsx @@ -93,7 +93,7 @@ const FilterYears: React.FC<{ id={isModal ? "year-filter-button-modal" : "year-filter-button"} onClick={() => onSubmit()} > - Apply + Apply year )} diff --git a/src/components/InstanceCard/InstanceCard.test.tsx b/src/components/InstanceCard/InstanceCard.test.tsx index e7827b7f..be54445e 100644 --- a/src/components/InstanceCard/InstanceCard.test.tsx +++ b/src/components/InstanceCard/InstanceCard.test.tsx @@ -3,92 +3,18 @@ import { InstanceCard } from "./InstanceCard"; import { screen, render } from "@testing-library/react"; import { Instance, WorkEdition } from "~/src/types/DataModel"; import { PLACEHOLDER_COVER_LINK } from "~/src/constants/editioncard"; -import { fullEdition } from "~/src/__tests__/fixtures/EditionCardFixture"; +import { + fullEdition, + upEdition, +} from "~/src/__tests__/fixtures/EditionCardFixture"; import { NYPL_SESSION_ID } from "~/src/constants/auth"; +import { + fullInstance, + eddInstance, + upInstance, +} from "~/src/__tests__/fixtures/InstanceCardFixture"; -const fullInstance: Instance = { - instance_id: 12345, - publishers: [{ name: "publisher_1", roles: ["publisher"] }], - publication_place: "Paris", - items: [ - { - links: [ - { - url: "test-link-url", - link_id: 12, - mediaType: "application/epub+xml", - flags: { - catalog: false, - download: false, - reader: true, - }, - }, - { - url: "test-link-url-2", - link_id: 23, - mediaType: "application/epub+zip", - flags: { - catalog: false, - download: true, - reader: false, - }, - }, - { - url: "test-link-url-3", - link_id: 34, - mediaType: "application/html+edd", - flags: { - catalog: false, - download: false, - reader: false, - edd: true, - }, - }, - ], - rights: [ - { - license: "license content", - rightsStatement: "test rights statement", - }, - ], - }, - ], - identifiers: [ - { - authority: "ddc", - identifier: "300", - }, - { - authority: "oclc", - identifier: "1014189544", - }, - { - authority: "oclc", - identifier: "1030816762", - }, - ], -}; - -const eddInstance: Instance = { - ...fullInstance, - items: [ - { - links: [ - { - url: "test-link-url", - link_id: 1, - mediaType: "application/html+edd", - flags: { - catalog: false, - download: false, - reader: false, - edd: true, - }, - }, - ], - }, - ], -}; +jest.mock("next/router", () => require("next-router-mock")); describe("Instance Card with Valid Data", () => { beforeEach(() => { @@ -108,7 +34,7 @@ describe("Instance Card with Valid Data", () => { ); }); test("Shows cover", () => { - expect(screen.getByAltText("Cover").closest("img").src).toEqual( + expect(screen.getByAltText("").closest("img").src).toEqual( "https://test-cover/" ); }); @@ -143,7 +69,7 @@ describe("Instance Card with Minmal Data", () => { expect(screen.getByText("Find in Library Unavailable")).toBeInTheDocument(); }); test("Shows cover", () => { - expect(screen.getByAltText("Cover").closest("img").src).toEqual( + expect(screen.getByAltText("").closest("img").src).toEqual( PLACEHOLDER_COVER_LINK ); }); @@ -166,7 +92,7 @@ describe("Instance with EDD", () => { expect(screen.queryByText("Download PDF")).toBeInTheDocument(); expect(screen.queryByText("Read Online")).toBeInTheDocument(); expect(screen.queryByText("Log in for options")).not.toBeInTheDocument(); - expect(screen.queryByText("Request")).not.toBeInTheDocument(); + expect(screen.queryByText("Request Scan")).not.toBeInTheDocument(); }); test("Shows Login button when EDD is available but user is not logged in", () => { @@ -174,10 +100,10 @@ describe("Instance with EDD", () => { ); expect( - screen.getByRole("link", { name: "Log in for options" }) + screen.getByRole("link", { name: "Log in to request scan for title" }) ).toBeInTheDocument(); expect( - screen.getByRole("link", { name: "Log in for options" }) + screen.getByRole("link", { name: "Log in to request scan for title" }) ).toHaveAttribute( "href", expect.stringContaining("https://login.nypl.org/auth/login") @@ -186,25 +112,77 @@ describe("Instance with EDD", () => { expect(screen.queryByText("Read Online")).not.toBeInTheDocument(); }); - test("Shows EDD Request button and 'Scan and Deliver' link when user is logged in", () => { + test("Shows EDD Request button when user is logged in", () => { // Set cookie before rendering the component document.cookie = `${NYPL_SESSION_ID}="randomvalue"`; render( ); - expect(screen.getByRole("link", { name: "Request" })).toBeInTheDocument(); - expect(screen.getByRole("link", { name: "Request" })).toHaveAttribute( - "href", - expect.stringContaining("test-link-url") - ); + expect( + screen.getByRole("link", { name: "Request scan for title" }) + ).toBeInTheDocument(); + expect( + screen.getByRole("link", { name: "Request scan for title" }) + ).toHaveAttribute("href", expect.stringContaining("test-link-url")); + expect(screen.queryByText("Download PDF")).not.toBeInTheDocument(); + expect(screen.queryByText("Read Online")).not.toBeInTheDocument(); + }); + + test("Shows 'Physical Edition' badge and 'Scan and Deliver' link", () => { + render(); + expect(screen.getByText("Physical Edition")).toBeInTheDocument(); expect( screen.getByRole("link", { name: "Scan and Deliver" }) ).toBeInTheDocument(); expect( screen.getByRole("link", { name: "Scan and Deliver" }) ).toHaveAttribute("href", "https://www.nypl.org/research/scan-and-deliver"); + }); +}); + +describe("Instance with UP", () => { + test("Shows Login button when user is not logged in", () => { + document.cookie = `${NYPL_SESSION_ID}=""`; + render(); + expect( + screen.getByRole("link", { name: "title Log in to read online" }) + ).toBeInTheDocument(); + expect( + screen.getByRole("link", { name: "title Log in to read online" }) + ).toHaveAttribute( + "href", + expect.stringContaining("https://login.nypl.org/auth/login") + ); expect(screen.queryByText("Download PDF")).not.toBeInTheDocument(); expect(screen.queryByText("Read Online")).not.toBeInTheDocument(); }); + test("Shows 'Library Card Required' badge", () => { + render(); + expect(screen.getByText("Library Card Required")).toBeInTheDocument(); + }); + test("Shows Read Online and Download buttons when user is logged in", () => { + // Set cookie before rendering the component + document.cookie = `${NYPL_SESSION_ID}="randomvalue"`; + render(); + + expect( + screen.getByRole("link", { name: "title Read Online" }) + ).toBeInTheDocument(); + expect( + screen.getByRole("link", { name: "title Read Online" }) + ).toHaveAttribute("href", expect.stringContaining("/read/12")); + expect( + screen.getByRole("link", { name: "title Download PDF" }) + ).toBeInTheDocument(); + expect( + screen.getByRole("link", { name: "title Download PDF" }) + ).toHaveAttribute("href", expect.stringContaining("test-link-url")); + }); + test("Shows blurb with publisher", () => { + render(); + expect( + screen.getByText("Digitalized by NYPL with permission of publisher_1") + ).toBeInTheDocument(); + }); }); diff --git a/src/components/InstanceCard/InstanceCard.tsx b/src/components/InstanceCard/InstanceCard.tsx index 86c08639..44f5df04 100644 --- a/src/components/InstanceCard/InstanceCard.tsx +++ b/src/components/InstanceCard/InstanceCard.tsx @@ -2,15 +2,23 @@ import React from "react"; import { Instance, WorkEdition } from "~/src/types/DataModel"; import { + Box, Card, CardActions, CardContent, CardHeading, + Flex, } from "@nypl/design-system-react-components"; import EditionCardUtils from "~/src/util/EditionCardUtils"; import Link from "../Link/Link"; -import { useCookies } from "react-cookie"; -import { NYPL_SESSION_ID } from "~/src/constants/auth"; +import Ctas from "../EditionCard/Ctas"; +import PublisherAndLocation from "../EditionCard/PublisherAndLocation"; +import WorldCat from "./WorldCat"; +import CardRequiredBadge from "../EditionCard/CardRequiredBadge"; +import FeaturedEditionBadge from "../EditionCard/FeaturedEditionBadge"; +import PhysicalEditionBadge from "../EditionCard/PhysicalEditionBadge"; +import ScanAndDeliverBlurb from "../EditionCard/ScanAndDeliverBlurb"; +import UpBlurb from "../EditionCard/UpBlurb"; // Creates an Instance card out of the Edition Year and Instance object // Note: Edition Year only needs to be passed because `instance.publication_date` @@ -20,50 +28,75 @@ import { NYPL_SESSION_ID } from "~/src/constants/auth"; export const InstanceCard: React.FC<{ edition: WorkEdition; instance: Instance; + isFeaturedEdition?: boolean; }> = (props) => { - // cookies defaults to be undefined if not fonud - const [cookies] = useCookies([NYPL_SESSION_ID]); - const edition = props.edition; const instance: Instance = props.instance; + const isFeaturedEdition = props.isFeaturedEdition; const previewItem = EditionCardUtils.getPreviewItem(instance.items); + const isPhysicalEdition = EditionCardUtils.isPhysicalEdition(previewItem); + const isUniversityPress = EditionCardUtils.isUniversityPress(previewItem); + const isLoginRequired = isPhysicalEdition || isUniversityPress; return ( - - - {edition.publication_date - ? edition.publication_date - : "Edition Year Unknown"} - - -
    - {EditionCardUtils.getPublisherAndLocation( - instance.publication_place, - instance.publishers - )} -
    -
    {EditionCardUtils.getWorldCatElem(instance)}
    - {EditionCardUtils.getLicense(previewItem)} -
    - - {EditionCardUtils.getCtas( - previewItem, - instance.title, - !!cookies[NYPL_SESSION_ID] - )} - -
    + + {isLoginRequired && } + {isFeaturedEdition && } + + + + + + {edition.publication_date + ? edition.publication_date + : "Edition Year Unknown"} + + {isPhysicalEdition && } + + + +
    + +
    + + {EditionCardUtils.getLicense(previewItem)} + {isPhysicalEdition && } + {isUniversityPress && } +
    + + + +
    + ); }; diff --git a/src/components/InstanceCard/WorldCat.tsx b/src/components/InstanceCard/WorldCat.tsx new file mode 100644 index 00000000..74a07867 --- /dev/null +++ b/src/components/InstanceCard/WorldCat.tsx @@ -0,0 +1,22 @@ +import { Box } from "@nypl/design-system-react-components"; +import React from "react"; +import Link from "~/src/components/Link/Link"; +import { Instance } from "~/src/types/DataModel"; +import EditionCardUtils from "~/src/util/EditionCardUtils"; + +const WorldCat: React.FC<{ + instance: Instance; +}> = ({ instance }) => { + const oclcLink = EditionCardUtils.getOclcLink(instance); + return ( + + {oclcLink ? ( + Find in a library + ) : ( + <>Find in Library Unavailable + )} + + ); +}; + +export default WorldCat; diff --git a/src/components/Link/Link.tsx b/src/components/Link/Link.tsx index 5b1ffb33..549b3e25 100644 --- a/src/components/Link/Link.tsx +++ b/src/components/Link/Link.tsx @@ -10,12 +10,21 @@ interface IProps extends React.AnchorHTMLAttributes { isUnderlined?: boolean; } -const Link = ({ children, to, linkType, isUnderlined }: IProps) => { +const Link = ({ + children, + to, + linkType, + isUnderlined, + "aria-label": ariaLabel, + onClick, +}: IProps) => { return ( {children} diff --git a/src/components/ResultsList/ResultsList.tsx b/src/components/ResultsList/ResultsList.tsx index a0105836..e5e7fb82 100644 --- a/src/components/ResultsList/ResultsList.tsx +++ b/src/components/ResultsList/ResultsList.tsx @@ -7,25 +7,8 @@ import EmptySearchSvg from "../Svgs/EmptySearchSvg"; import { truncateStringOnWhitespace } from "~/src/util/Util"; import { MAX_TITLE_LENGTH } from "~/src/constants/editioncard"; import { ApiWork } from "~/src/types/WorkQuery"; - -export const getEditionsLinkElement = (work: ApiWork) => { - const editionCount = work.edition_count; - const previewEdition = work.editions && work.editions[0]; - - return editionCount > 1 ? ( - - {`View All ${editionCount} Editions`} - - ) : undefined; -}; +import ViewEditionsLink from "../EditionCard/ViewEditionsLink"; +import AuthorsList from "../AuthorsList/AuthorsList"; const ResultsList: React.FC<{ works: ApiWork[] }> = ({ works }) => { if (works.length === 0) { @@ -66,13 +49,17 @@ const ResultsList: React.FC<{ works: ApiWork[] }> = ({ works }) => { {EditionCardUtils.getSubtitle(work.sub_title)} - {EditionCardUtils.getAuthorsList(work.authors) && ( + {work.authors && work.authors.length && ( - By {EditionCardUtils.getAuthorsList(work.authors)} + By )} - -
    {getEditionsLinkElement(work)}
    + 1} + /> + ); })} diff --git a/src/components/Search/Search.tsx b/src/components/Search/Search.tsx index 009e9140..6770f8ef 100644 --- a/src/components/Search/Search.tsx +++ b/src/components/Search/Search.tsx @@ -9,9 +9,9 @@ import { Flex, Form, useModal, - useNYPLBreakpoints, TemplateAppContainer, Text, + useNYPLBreakpoints, } from "@nypl/design-system-react-components"; import { useRouter } from "next/router"; import { FacetItem, Query } from "~/src/types/DataModel"; @@ -47,7 +47,7 @@ const SearchResults: React.FC<{ const { onClose, onOpen, Modal } = useModal(); - const { isLargerThanMedium } = useNYPLBreakpoints(); + const { isLargerThanLarge } = useNYPLBreakpoints(); const router = useRouter(); @@ -96,13 +96,6 @@ const SearchResults: React.FC<{ return facets; }; - const getFilterCount = (searchQuery: SearchQuery) => { - return searchQuery.showAll !== SearchQueryDefaults.showAll - ? searchQuery.filters.length + 1 - : searchQuery.filters.length; - }; - - const filterCount = getFilterCount(searchQuery); const numberOfWorks = searchResults.data.totalWorks; const works: ApiWork[] = searchResults.data.works; @@ -182,6 +175,84 @@ const SearchResults: React.FC<{ ]} > + + + + + + onChangePerPage(e)} + onChangeSort={(e) => onChangeSort(e)} + /> + +
    + + Refine Results + + { + changeFilters(filters); + }} + changeShowAll={(showAll: boolean) => { + changeShowAll(showAll); + }} + /> + + + } + /> + {searchQuery.filters.length > 0 && !isLargerThanLarge && ( + + )} +
    ); @@ -224,107 +295,33 @@ const SearchResults: React.FC<{ ); const contentSidebarElement = ( - <> - {!isLargerThanMedium && ( - - )} - - - - onChangePerPage(e)} - onChangeSort={(e) => onChangeSort(e)} - /> - -
    - - Refine Results - - { - changeFilters(filters); - }} - changeShowAll={(showAll: boolean) => { - changeShowAll(showAll); - }} - /> - - - } - /> - {searchQuery.filters.length > 0 && !isLargerThanMedium && ( - - )} -
    + - - Refine Results - - { - changeFilters(filters); - }} - changeShowAll={(showAll: boolean) => { - changeShowAll(showAll); - }} - /> - - + Refine Results +
    + { + changeFilters(filters); + }} + changeShowAll={(showAll: boolean) => { + changeShowAll(showAll); + }} + /> + ); const contentPrimaryElement = ( @@ -344,7 +341,7 @@ const SearchResults: React.FC<{ contentTop={contentTopElement} contentSidebar={contentSidebarElement} contentPrimary={contentPrimaryElement} - sidebar="left" + sidebar={"left"} /> ); }; diff --git a/src/components/Work/Work.test.tsx b/src/components/Work/Work.test.tsx index 5289886f..d869e9a1 100644 --- a/src/components/Work/Work.test.tsx +++ b/src/components/Work/Work.test.tsx @@ -54,14 +54,12 @@ describe("Renders Work component when given valid work", () => { }); }); - test("Featured Edition Card shows up twice in page", () => { - expect( - screen.getByRole("heading", { name: "Featured Edition" }) - ).toBeInTheDocument(); + test("Featured Edition Card shows up once in page", () => { + expect(screen.getByText("Featured Edition")).toBeInTheDocument(); const featuredEditionHeadings = screen.getAllByRole("heading", { name: "1967 Edition", }); - expect(featuredEditionHeadings.length).toEqual(2); + expect(featuredEditionHeadings.length).toEqual(1); featuredEditionHeadings.forEach((heading) => { expect( (within(heading).getByRole("link") as HTMLLinkElement).href @@ -71,13 +69,13 @@ describe("Renders Work component when given valid work", () => { ).toContain("?featured=1280883"); }); - expect(screen.getAllByAltText("Cover for 1967 Edition").length).toBe(2); + expect(screen.getAllByAltText("Cover for 1967 Edition").length).toBe(1); expect( screen.getAllByText( "Published by Foreign Service Institute, Dept. of State;" ).length - ).toBe(2); - expect(screen.getAllByText("Languages: English, German").length).toBe(2); + ).toBe(1); + expect(screen.getAllByText("Languages: English, German").length).toBe(1); expect( screen.getAllByText("License: Unknown")[0].closest("a").href ).toContain("/license"); @@ -180,13 +178,11 @@ describe("Edition Cards and toggles", () => { }); test("1980 edition shows up twice", () => { - expect( - screen.getByRole("heading", { name: "Featured Edition" }) - ).toBeInTheDocument(); + expect(screen.getByText("Featured Edition")).toBeInTheDocument(); const featuredEditionHeadings = screen.getAllByRole("heading", { name: "1980 Edition", }); - expect(featuredEditionHeadings.length).toEqual(2); + expect(featuredEditionHeadings.length).toEqual(1); }); }); }); diff --git a/src/components/Work/Work.tsx b/src/components/Work/Work.tsx index c8fdca83..8455e42a 100644 --- a/src/components/Work/Work.tsx +++ b/src/components/Work/Work.tsx @@ -13,10 +13,7 @@ import { CardHeading, TemplateAppContainer, } from "@nypl/design-system-react-components"; -import { - joinArrayOfElements, - truncateStringOnWhitespace, -} from "~/src/util/Util"; +import { truncateStringOnWhitespace } from "~/src/util/Util"; import { EditionCard } from "~/src/components/EditionCard/EditionCard"; import WorkDetailDefinitionList from "~/src/components/WorkDetailDefinitionList/WorkDetailDefinitionList"; import { ApiWork, WorkResult } from "~/src/types/WorkQuery"; @@ -27,6 +24,7 @@ import Link from "../Link/Link"; import { MAX_TITLE_LENGTH } from "~/src/constants/editioncard"; import { PLACEHOLDER_LINK } from "~/src/constants/collection"; import DrbBreakout from "../DrbBreakout/DrbBreakout"; +import AuthorsList from "../AuthorsList/AuthorsList"; const WorkDetail: React.FC<{ workResult: WorkResult; backUrl?: string }> = ( props @@ -37,9 +35,8 @@ const WorkDetail: React.FC<{ workResult: WorkResult; backUrl?: string }> = ( const featuredEditionId = query.featured; const work: ApiWork = props.workResult.data; - //Edition Card Preprocessing - const authorsList = EditionCardUtils.getAuthorsList(work.authors); + //Edition Card Preprocessing const passedInFeaturedEdition = featuredEditionId ? work.editions.find( (edition) => edition.edition_id === Number(featuredEditionId) @@ -81,41 +78,31 @@ const WorkDetail: React.FC<{ workResult: WorkResult; backUrl?: string }> = ( const contentTopElement = ( <> - - - {work.title} - - {props.backUrl && ( - - - Back to search results - - - )} - + + {work.title} + {work.sub_title && {work.sub_title}} - {authorsList && authorsList.length && ( - By {joinArrayOfElements(authorsList, "")} - )} - - {featuredEdition && ( - <> + {work.authors && work.authors.length && ( - - Featured Edition - + By - - + )} + {props.backUrl && ( + + + Back to search results + - + )} + + {featuredEdition && ( + + + )} {work.inCollections && work.inCollections.length > 0 && ( = ( const contentPrimaryElement = ( <> - + {work.editions && work.editions.length > 1 && ( + + )} - {work.editions && ( + {work.editions && work.editions.length > 1 && ( <> - - - All Editions + + + Other Editions = ( /> - {work.editions.map((edition: WorkEdition) => ( - - ))} + {work.editions + .filter( + (edition) => edition.edition_id !== featuredEdition.edition_id + ) + .map((edition: WorkEdition) => ( + + ))} )} diff --git a/src/components/WorkDetailDefinitionList/WorkDetailDefinitionList.tsx b/src/components/WorkDetailDefinitionList/WorkDetailDefinitionList.tsx index b43e810d..976e3057 100644 --- a/src/components/WorkDetailDefinitionList/WorkDetailDefinitionList.tsx +++ b/src/components/WorkDetailDefinitionList/WorkDetailDefinitionList.tsx @@ -3,8 +3,8 @@ import { List } from "@nypl/design-system-react-components"; import Link from "~/src/components/Link/Link"; import { unique, flattenDeep, uniqueAndSortByFrequency } from "~/src/util/Util"; import { Language, Subject } from "~/src/types/DataModel"; -import EditionCardUtils from "~/src/util/EditionCardUtils"; import { ApiWork } from "~/src/types/WorkQuery"; +import AuthorsList from "../AuthorsList/AuthorsList"; // extract unique language array from instances of a work item const getLanguagesForWork = (work: ApiWork) => @@ -76,7 +76,9 @@ const WorkDetailDefinitionList: React.FC<{ work: ApiWork }> = ({ work }) => { )}
    Authors
    -
    {EditionCardUtils.getAuthorsList(work.authors)}
    +
    + +
    {work.subjects && work.subjects.length > 0 && ( <>
    Subjects
    diff --git a/src/constants/labels.ts b/src/constants/labels.ts index 3c37571e..851f53f1 100644 --- a/src/constants/labels.ts +++ b/src/constants/labels.ts @@ -96,6 +96,6 @@ export const FormatTypes = [ ]; export const errorMessagesText = { - emptySearch: "Please enter a search term", - invalidDate: "Start date must be before End date", + emptySearch: "Error: Please enter a search term", + invalidDate: "Error: Start date must be before End date", }; diff --git a/src/constants/links.ts b/src/constants/links.ts index 94c86cc3..7309fa20 100644 --- a/src/constants/links.ts +++ b/src/constants/links.ts @@ -1,2 +1,4 @@ export const SCAN_AND_DELIVER_LINK = "https://www.nypl.org/research/scan-and-deliver"; +export const LOGIN_LINK_BASE = + "https://login.nypl.org/auth/login?redirect_uri="; diff --git a/src/lib/api/SearchApi.ts b/src/lib/api/SearchApi.ts index ffb4e5e3..1f85c9a3 100644 --- a/src/lib/api/SearchApi.ts +++ b/src/lib/api/SearchApi.ts @@ -5,6 +5,9 @@ import { EditionQuery, EditionResult } from "~/src/types/EditionQuery"; import { toLocationQuery } from "~/src/util/apiConversion"; import { LinkResult } from "~/src/types/LinkQuery"; import { ApiLanguageResponse } from "~/src/types/LanguagesQuery"; +import { LOGIN_LINK_BASE } from "~/src/constants/links"; +import { NextRouter } from "next/router"; +import { FulfillResult } from "~/src/types/FulfillQuery"; const apiEnv = process.env["APP_ENV"]; const apiUrl = process.env["API_URL"] || appConfig.api.url[apiEnv]; @@ -120,3 +123,30 @@ export const readFetcher = async (linkId: number) => { throw new Error(`cannot find work with linkId ${linkId}`); } }; + +export const fulfillFetcher = async ( + fulfillUrl: string, + nyplIdentityCookie: any, + router: NextRouter +) => { + const url = new URL(fulfillUrl); + const res = await fetch(url.toString(), { + method: "GET", + headers: { + Authorization: `Bearer ${nyplIdentityCookie.access_token}`, + }, + }); + if (res.ok) { + router.push(res.url); + } else { + // redirect to the NYPL login page if access token is invalid + if (res.status === 401) { + router.push(LOGIN_LINK_BASE + encodeURIComponent(window.location.href)); + } + if (res.status === 404) { + const fulfillResult: FulfillResult = await res.json(); + return fulfillResult.data; + } + } + return undefined; +}; diff --git a/src/types/DataModel.ts b/src/types/DataModel.ts index 807cd2f8..3b434cee 100644 --- a/src/types/DataModel.ts +++ b/src/types/DataModel.ts @@ -64,6 +64,7 @@ export type LinkFlags = { reader: boolean; edd?: boolean; embed?: boolean; + nypl_login?: boolean; }; export type ItemLink = { diff --git a/src/types/FulfillQuery.ts b/src/types/FulfillQuery.ts new file mode 100644 index 00000000..b1e86053 --- /dev/null +++ b/src/types/FulfillQuery.ts @@ -0,0 +1,6 @@ +export type FulfillResult = { + status?: number; + timestamp?: string; + responseType?: string; + data?: string; +}; diff --git a/src/util/EditionCardUtils.tsx b/src/util/EditionCardUtils.tsx index 00e2c5dc..e59b2334 100644 --- a/src/util/EditionCardUtils.tsx +++ b/src/util/EditionCardUtils.tsx @@ -8,8 +8,6 @@ import { WorkEdition, Identifier, } from "../types/DataModel"; -import { Box, Button, Icon } from "@nypl/design-system-react-components"; -import Link from "~/src/components/Link/Link"; import { formatUrl, truncateStringOnWhitespace } from "./Util"; import { MAX_PLACE_LENGTH, @@ -17,9 +15,7 @@ import { MAX_SUBTITILE_LENGTH, PLACEHOLDER_COVER_LINK, } from "../constants/editioncard"; -import { ApiSearchQuery } from "../types/SearchQuery"; import { MediaTypes } from "../constants/mediaTypes"; -import { trackCtaClick } from "../lib/adobe/Analytics"; // EditionCard holds all the methods needed to build an Edition Card export default class EditionCardUtils { @@ -78,35 +74,6 @@ export default class EditionCardUtils { ); } - static getAuthorsList(authors: Agent[]): JSX.Element[] { - if (!authors || authors.length === 0) return null; - return authors.map((author: Agent, i: number) => { - const authorLinkText = author.name; - const query: ApiSearchQuery = { - query: author.viaf ? `viaf:${author.viaf}` : `author:${author.name}`, - }; - if (author.viaf) { - query.display = `author:${author.name}`; - } - return ( - - - {authorLinkText} - - {i < authors.length - 1 && ", "} - - ); - }); - } - /** Get Cover Image * @param covers - The list of covers * @returns The URL of the cover that should be displayed. @@ -120,40 +87,31 @@ export default class EditionCardUtils { return coverLink ? formatUrl(coverLink.url) : PLACEHOLDER_COVER_LINK; } - /** - * Get publisher and publish location - * @param pubPlace - The display name of the place of publication - * @param agents - an array of Agents - * @returns A display element for publisher and location - */ - static getPublisherAndLocation( - pubPlace: string, - publishers: Agent[] - ): JSX.Element { - const publisherDisplayLocation = (pubPlace: string) => { - return pubPlace - ? ` in ${truncateStringOnWhitespace(pubPlace, MAX_PLACE_LENGTH)}` - : ""; - }; + static getPublisherDisplayLocation(pubPlace: string): undefined | string { + return ( + pubPlace && + ` in ${truncateStringOnWhitespace(pubPlace, MAX_PLACE_LENGTH)}` + ); + } - const publisherDisplayText = (publishers: Agent[]) => { - if (!publishers || publishers.length === 0) return ""; - const publisherNames = publishers.map( - (pubAgent: Agent) => pubAgent && pubAgent.name - ); - return ` by ${EditionCardUtils.getFirstAndCountMore(publisherNames)}`; - }; + static getPublishersDisplayText(publishers: Agent[]): undefined | string { + if (!publishers || publishers.length === 0) return ""; + const publisherNames = publishers.map( + (pubAgent: Agent) => pubAgent && pubAgent.name + ); + return ` by ${EditionCardUtils.getFirstAndCountMore(publisherNames)}`; + } - const displayLocation = publisherDisplayLocation(pubPlace); - const displayName = publisherDisplayText(publishers); - if (!displayLocation && !displayName) - return <>Publisher and Location Unknown; - const publisherText = `Published${displayLocation}${displayName}`; - return <>{publisherText}; + static getUpPublisher(publishers: Agent[]): undefined | string { + if (!publishers || publishers.length === 0) return ""; + const publisherNames = publishers.map( + (pubAgent: Agent) => pubAgent && pubAgent.name + ); + return EditionCardUtils.getFirstAndCountMore(publisherNames); } // Language Display - static getLanguageDisplayText(previewEdition: WorkEdition) { + static getLanguageDisplayText(previewEdition: WorkEdition): string { if ( previewEdition && previewEdition.languages && @@ -166,102 +124,51 @@ export default class EditionCardUtils { .map((lang: Language) => lang.language); if (languagesTextList && languagesTextList.length) { const languageText = `Languages: ${languagesTextList.join(", ")}`; - return <>{languageText}; + return languageText; } } - return <>Languages: Undetermined; + return "Languages: Undetermined"; } // Rights - static getLicense(item: ApiItem) { + static getLicense(item: ApiItem): string { return item && item.rights && item.rights.length > 0 ? `License: ${item.rights[0].rightsStatement}` : "License: Unknown"; } - static getReadLink = (item: ApiItem, type: "reader" | "embed") => { + static getReadLink = (item: ApiItem, type: "reader" | "embed"): ItemLink => { if (!item || !item.links) return undefined; return item.links.find((link: ItemLink) => { return link.flags[type]; }); }; - static selectDownloadLink = (item: ApiItem) => { + static selectDownloadLink = (item: ApiItem): ItemLink => { if (!item || !item.links) return undefined; return item.links.find((link: ItemLink) => { return link.flags["download"]; }); }; + static getUpLink = (item: ApiItem): ItemLink => { + if (!item || !item.links) return undefined; + return item.links.find((link: ItemLink) => { + return !link.flags.edd && link.flags.nypl_login; + }); + }; + // "Read Online" button should only show up if the link was flagged as "reader" or "embed" static getReadOnlineLink = (item: ApiItem) => { const localLink = EditionCardUtils.getReadLink(item, "reader"); const embeddedLink = EditionCardUtils.getReadLink(item, "embed"); // Prefer local link over embedded link const readOnlineLink = localLink ?? embeddedLink; - if (readOnlineLink) { - return ( - - Read Online - - ); - } - return undefined; + return readOnlineLink; }; - static getDownloadLink(editionItem: ApiItem, title: string) { - if (!editionItem || !editionItem.links) return undefined; - - const selectedLink = EditionCardUtils.selectDownloadLink(editionItem); - - if (selectedLink && selectedLink.url) { - const formattedUrl = formatUrl(selectedLink.url); - - const trackDownloadCta = () => { - trackCtaClick({ - cta_section: `${title}`, - cta_text: "Download", - destination_url: `${formattedUrl}`, - }); - }; - return ( - - - - ); - } - } - - static getNoLinkElement(showRequestButton: boolean) { - if (showRequestButton) { - return Not Yet Available {showRequestButton}; - } - return <>Not yet available; - } - - static getWorldCatElem(instance: Instance) { + static getOclcLink(instance: Instance): string { const oclc = instance && instance.identifiers ? instance.identifiers.find( @@ -271,105 +178,46 @@ export default class EditionCardUtils { const oclcLink = oclc ? `https://www.worldcat.org/oclc/${oclc.identifier}` : undefined; - return oclc ? ( - Find in a library - ) : ( - <>Find in Library Unavailable - ); + + return oclcLink; } - static getCtas( - item: ApiItem | undefined, - title: string, - isLoggedIn: boolean - ) { - const readOnlineLink = EditionCardUtils.getReadOnlineLink(item); - const downloadLink = EditionCardUtils.getDownloadLink(item, title); + // return first item if links are available + static getPreviewItem(items: ApiItem[] | undefined) { + if (!items) return undefined; - // If a digital version exists, link directly - if (readOnlineLink || downloadLink) { - return ( - <> - {readOnlineLink} - {downloadLink} - - ); - } + const firstItem = items[0]; + + return firstItem && firstItem.links ? firstItem : undefined; + } + + static isAvailableOnline(item: ApiItem) { + return ( + item && + item.links && + item.links.find((link: ItemLink) => { + return ( + link.flags["reader"] || link.flags["embed"] || link.flags["download"] + ); + }) + ); + } + static isPhysicalEdition(item: ApiItem): boolean { + const availableOnline = this.isAvailableOnline(item); const eddLink = item && item.links ? item.links.find((link) => link.flags.edd) : undefined; - // Offer EDD if available - if (eddLink !== undefined) { - const eddElement = EditionCardUtils.getEddLinkElement( - eddLink, - isLoggedIn - ); - return <>{eddElement}; - } - - return <>{EditionCardUtils.getNoLinkElement(false)}; + return !availableOnline && eddLink !== undefined; } - static getEddLinkElement(eddLink: ItemLink, isLoggedIn: boolean) { - if (isLoggedIn) { - return ( - <> - - You can request a partial scan via NYPL - - - Scan and Deliver - - - Request - - - ); - } else { - return ( - <> - May be available via NYPL

    - - Log in for options - - - ); - } - } - - // Get readable item or non-catalog item - static getPreviewItem(items: ApiItem[] | undefined) { - if (!items) return undefined; - - const firstReadableItem = items.find((item) => { - return ( - EditionCardUtils.getReadLink(item, "reader") || - EditionCardUtils.getReadLink(item, "embed") - ); - }); - - // If no readable link found, we just return any link that's not a catalog (edd) - return ( - firstReadableItem ?? - items.find((items) => { - return items.links && items.links.find((link) => !link.flags.catalog); - }) - ); + static isUniversityPress(item: ApiItem): boolean { + const universityPress = + item && item.links + ? item.links.find((link) => !link.flags.edd && link.flags.nypl_login) + : undefined; + return universityPress !== undefined; } }