diff --git a/api.planx.uk/modules/pay/controller.ts b/api.planx.uk/modules/pay/controller.ts
index 57ae9a94b7..e8661aba8d 100644
--- a/api.planx.uk/modules/pay/controller.ts
+++ b/api.planx.uk/modules/pay/controller.ts
@@ -1,7 +1,7 @@
import assert from "assert";
import { Request } from "express";
import { responseInterceptor } from "http-proxy-middleware";
-import { logPaymentStatus } from "./helpers.js";
+import { handleGovPayErrors, logPaymentStatus } from "./helpers.js";
import { usePayProxy } from "./proxy.js";
import { $api } from "../../client/index.js";
import { ServerError } from "../../errors/index.js";
@@ -46,9 +46,12 @@ export const makePaymentViaProxy: PaymentProxyController = async (
pathRewrite: (path) => path.replace(/^\/pay.*$/, ""),
selfHandleResponse: true,
onProxyRes: responseInterceptor(
- async (responseBuffer, _proxyRes, _req, _res) => {
+ async (responseBuffer, _proxyRes, _req, { statusCode }) => {
const responseString = responseBuffer.toString("utf8");
const govUkResponse = JSON.parse(responseString);
+
+ if (statusCode >= 400) return handleGovPayErrors(govUkResponse);
+
await logPaymentStatus({
sessionId,
flowId,
@@ -79,27 +82,32 @@ export const makeInviteToPayPaymentViaProxy: PaymentRequestProxyController = (
{
pathRewrite: (path) => path.replace(/^\/pay.*$/, ""),
selfHandleResponse: true,
- onProxyRes: responseInterceptor(async (responseBuffer) => {
- const responseString = responseBuffer.toString("utf8");
- const govUkResponse = JSON.parse(responseString);
- await logPaymentStatus({
- sessionId,
- flowId,
- teamSlug,
- govUkResponse,
- });
-
- try {
- await addGovPayPaymentIdToPaymentRequest(
- paymentRequestId,
+ onProxyRes: responseInterceptor(
+ async (responseBuffer, _proxyRes, _req, { statusCode }) => {
+ const responseString = responseBuffer.toString("utf8");
+ const govUkResponse = JSON.parse(responseString);
+
+ if (statusCode >= 400) return handleGovPayErrors(govUkResponse);
+
+ await logPaymentStatus({
+ sessionId,
+ flowId,
+ teamSlug,
govUkResponse,
- );
- } catch (error) {
- throw Error(error as string);
- }
+ });
- return responseBuffer;
- }),
+ try {
+ await addGovPayPaymentIdToPaymentRequest(
+ paymentRequestId,
+ govUkResponse,
+ );
+ } catch (error) {
+ throw Error(error as string);
+ }
+
+ return responseBuffer;
+ },
+ ),
},
req,
res,
@@ -125,32 +133,36 @@ export function fetchPaymentViaProxyWithCallback(
{
pathRewrite: () => `/${req.params.paymentId}`,
selfHandleResponse: true,
- onProxyRes: responseInterceptor(async (responseBuffer) => {
- const govUkResponse = JSON.parse(responseBuffer.toString("utf8"));
-
- await logPaymentStatus({
- sessionId,
- flowId,
- teamSlug,
- govUkResponse,
- });
-
- try {
- await callback(req, govUkResponse);
- } catch (e) {
- throw Error(e as string);
- }
-
- // only return payment status, filter out PII
- return JSON.stringify({
- payment_id: govUkResponse.payment_id,
- amount: govUkResponse.amount,
- state: govUkResponse.state,
- _links: {
- next_url: govUkResponse._links?.next_url,
- },
- });
- }),
+ onProxyRes: responseInterceptor(
+ async (responseBuffer, _proxyRes, _req, { statusCode }) => {
+ const govUkResponse = JSON.parse(responseBuffer.toString("utf8"));
+
+ if (statusCode >= 400) return handleGovPayErrors(govUkResponse);
+
+ await logPaymentStatus({
+ sessionId,
+ flowId,
+ teamSlug,
+ govUkResponse,
+ });
+
+ try {
+ await callback(req, govUkResponse);
+ } catch (e) {
+ throw Error(e as string);
+ }
+
+ // only return payment status, filter out PII
+ return JSON.stringify({
+ payment_id: govUkResponse.payment_id,
+ amount: govUkResponse.amount,
+ state: govUkResponse.state,
+ _links: {
+ next_url: govUkResponse._links?.next_url,
+ },
+ });
+ },
+ ),
},
req,
res,
diff --git a/api.planx.uk/modules/pay/helpers.ts b/api.planx.uk/modules/pay/helpers.ts
index 76d8296c3c..f513339751 100644
--- a/api.planx.uk/modules/pay/helpers.ts
+++ b/api.planx.uk/modules/pay/helpers.ts
@@ -2,6 +2,17 @@ import { gql } from "graphql-request";
import airbrake from "../../airbrake.js";
import { $api } from "../../client/index.js";
+/**
+ * Gracefully handle GovPay errors
+ * Docs: https://docs.payments.service.gov.uk/api_reference/#responses
+ */
+export const handleGovPayErrors = (res: unknown) =>
+ JSON.stringify({
+ message:
+ "GovPay responded with an error when attempting to proxy to their API",
+ govPayResponse: res,
+ });
+
export async function logPaymentStatus({
sessionId,
flowId,
diff --git a/api.planx.uk/modules/pay/index.test.ts b/api.planx.uk/modules/pay/index.test.ts
index 38dff1e36c..b52f72d4fd 100644
--- a/api.planx.uk/modules/pay/index.test.ts
+++ b/api.planx.uk/modules/pay/index.test.ts
@@ -168,3 +168,38 @@ describe("fetching status of a GOV.UK payment", () => {
});
});
});
+
+test("handling GovPay error responses", async () => {
+ const govUKErrorResponse = {
+ code: "govUKErrorResponse",
+ description:
+ "Account is not fully configured. Please refer to documentation to setup your account or contact support with your error code - https://www.payments.service.gov.uk/support/ .",
+ };
+
+ nock("https://publicapi.payments.service.gov.uk/v1/payments")
+ .post("")
+ .reply(400, govUKErrorResponse);
+
+ await supertest(app)
+ .post(
+ "/pay/southwark?flowId=7cd1c4b4-4229-424f-8d04-c9fdc958ef4e&sessionId=f2d8ca1d-a43b-43ec-b3d9-a9fec63ff19c",
+ )
+ .send({
+ amount: 100,
+ reference: "12343543",
+ description: "New application",
+ return_url: "https://editor.planx.uk",
+ metadata: {
+ source: "PlanX",
+ flow: "apply-for-a-lawful-development-certificate",
+ inviteToPay: false,
+ },
+ })
+ .expect(400)
+ .then((res) => {
+ expect(res.body.message).toMatch(
+ /GovPay responded with an error when attempting to proxy to their API/,
+ );
+ expect(res.body.govPayResponse).toEqual(govUKErrorResponse);
+ });
+});
diff --git a/editor.planx.uk/package.json b/editor.planx.uk/package.json
index 932dfd8f44..3aa3a6911a 100644
--- a/editor.planx.uk/package.json
+++ b/editor.planx.uk/package.json
@@ -162,7 +162,7 @@
"stream-browserify": "^3.0.0",
"ts-jest": "^29.1.3",
"tsconfig-paths-webpack-plugin": "^4.0.1",
- "typescript": "^5.4.3",
+ "typescript": "^5.6.2",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^1.6.0",
"vitest-axe": "1.0.0-pre.3",
diff --git a/editor.planx.uk/pnpm-lock.yaml b/editor.planx.uk/pnpm-lock.yaml
index ac10529f31..12aee51472 100644
--- a/editor.planx.uk/pnpm-lock.yaml
+++ b/editor.planx.uk/pnpm-lock.yaml
@@ -276,7 +276,7 @@ dependencies:
version: 0.1.4(jest@27.5.1)(vite@5.2.11)
vite-plugin-svgr:
specifier: ^4.2.0
- version: 4.2.0(rollup@2.79.1)(typescript@5.4.3)(vite@5.2.11)
+ version: 4.2.0(rollup@2.79.1)(typescript@5.6.2)(vite@5.2.11)
wkt:
specifier: ^0.1.1
version: 0.1.1
@@ -335,13 +335,13 @@ devDependencies:
version: 8.2.9(storybook@8.2.9)
'@storybook/preset-create-react-app':
specifier: ^8.2.9
- version: 8.2.9(react-refresh@0.14.0)(react-scripts@5.0.1)(storybook@8.2.9)(typescript@5.4.3)(webpack@5.91.0)
+ version: 8.2.9(react-refresh@0.14.0)(react-scripts@5.0.1)(storybook@8.2.9)(typescript@5.6.2)(webpack@5.91.0)
'@storybook/react':
specifier: ^8.2.9
- version: 8.2.9(react-dom@18.2.0)(react@18.2.0)(storybook@8.2.9)(typescript@5.4.3)
+ version: 8.2.9(react-dom@18.2.0)(react@18.2.0)(storybook@8.2.9)(typescript@5.6.2)
'@storybook/react-vite':
specifier: ^8.2.9
- version: 8.2.9(react-dom@18.2.0)(react@18.2.0)(rollup@2.79.1)(storybook@8.2.9)(typescript@5.4.3)(vite@5.2.11)
+ version: 8.2.9(react-dom@18.2.0)(react@18.2.0)(rollup@2.79.1)(storybook@8.2.9)(typescript@5.6.2)(vite@5.2.11)
'@storybook/test':
specifier: ^8.2.9
version: 8.2.9(@types/jest@27.5.2)(jest@27.5.1)(storybook@8.2.9)(vitest@1.6.0)
@@ -401,10 +401,10 @@ devDependencies:
version: 9.0.7
'@typescript-eslint/eslint-plugin':
specifier: ^5.62.0
- version: 5.62.0(@typescript-eslint/parser@5.58.0)(eslint@8.44.0)(typescript@5.4.3)
+ version: 5.62.0(@typescript-eslint/parser@5.58.0)(eslint@8.44.0)(typescript@5.6.2)
'@typescript-eslint/parser':
specifier: ^5.58.0
- version: 5.58.0(eslint@8.44.0)(typescript@5.4.3)
+ version: 5.58.0(eslint@8.44.0)(typescript@5.6.2)
autoprefixer:
specifier: ^10.4.16
version: 10.4.16(postcss@8.4.32)
@@ -434,7 +434,7 @@ devDependencies:
version: 12.1.0(eslint@8.44.0)
eslint-plugin-testing-library:
specifier: ^5.11.1
- version: 5.11.1(eslint@8.44.0)(typescript@5.4.3)
+ version: 5.11.1(eslint@8.44.0)(typescript@5.6.2)
husky:
specifier: ^8.0.3
version: 8.0.3
@@ -479,16 +479,16 @@ devDependencies:
version: 3.0.0
ts-jest:
specifier: ^29.1.3
- version: 29.1.3(@babel/core@7.22.5)(esbuild@0.21.3)(jest@27.5.1)(typescript@5.4.3)
+ version: 29.1.3(@babel/core@7.22.5)(esbuild@0.21.3)(jest@27.5.1)(typescript@5.6.2)
tsconfig-paths-webpack-plugin:
specifier: ^4.0.1
version: 4.0.1
typescript:
- specifier: ^5.4.3
- version: 5.4.3
+ specifier: ^5.6.2
+ version: 5.6.2
vite-tsconfig-paths:
specifier: ^4.3.2
- version: 4.3.2(typescript@5.4.3)(vite@5.2.11)
+ version: 4.3.2(typescript@5.6.2)(vite@5.2.11)
vitest:
specifier: ^1.6.0
version: 1.6.0(@types/node@17.0.45)(sass@1.71.1)
@@ -4889,7 +4889,7 @@ packages:
chalk: 4.1.2
dev: true
- /@joshwooding/vite-plugin-react-docgen-typescript@0.3.1(typescript@5.4.3)(vite@5.2.11):
+ /@joshwooding/vite-plugin-react-docgen-typescript@0.3.1(typescript@5.6.2)(vite@5.2.11):
resolution: {integrity: sha512-pdoMZ9QaPnVlSM+SdU/wgg0nyD/8wQ7y90ttO2CMCyrrm7RxveYIJ5eNfjPaoMFqW41LZra7QO9j+xV4Y18Glw==}
peerDependencies:
typescript: '>= 4.3.x'
@@ -4901,8 +4901,8 @@ packages:
glob: 7.2.3
glob-promise: 4.2.2(glob@7.2.3)
magic-string: 0.27.0
- react-docgen-typescript: 2.2.2(typescript@5.4.3)
- typescript: 5.4.3
+ react-docgen-typescript: 2.2.2(typescript@5.6.2)
+ typescript: 5.6.2
vite: 5.2.11(@types/node@17.0.45)(sass@1.71.1)
dev: true
@@ -5785,7 +5785,7 @@ packages:
'@react-theming/theme-name': 1.0.3
'@react-theming/theme-swatch': 1.0.0(react@18.2.0)
'@storybook/addon-devkit': 1.4.2(@storybook/addons@6.5.16)(@storybook/react@8.2.9)(react-dom@18.2.0)(react@18.2.0)
- '@storybook/react': 8.2.9(react-dom@18.2.0)(react@18.2.0)(storybook@8.2.9)(typescript@5.4.3)
+ '@storybook/react': 8.2.9(react-dom@18.2.0)(react@18.2.0)(storybook@8.2.9)(typescript@5.6.2)
'@storybook/theming': 8.2.9(storybook@8.2.9)
'@usulpro/react-json-view': 2.0.1(react-dom@18.2.0)(react@18.2.0)
color-string: 1.9.1
@@ -6093,7 +6093,7 @@ packages:
'@reach/rect': 0.2.1(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0)
'@storybook/addons': 6.5.16(react-dom@18.2.0)(react@18.2.0)
'@storybook/core-events': 6.5.16
- '@storybook/react': 8.2.9(react-dom@18.2.0)(react@18.2.0)(storybook@8.2.9)(typescript@5.4.3)
+ '@storybook/react': 8.2.9(react-dom@18.2.0)(react@18.2.0)(storybook@8.2.9)(typescript@5.6.2)
'@storybook/theming': 6.5.16(react-dom@18.2.0)(react@18.2.0)
deep-equal: 2.2.3
prop-types: 15.8.1
@@ -6285,7 +6285,7 @@ packages:
util-deprecate: 1.0.2
dev: true
- /@storybook/builder-vite@8.2.9(storybook@8.2.9)(typescript@5.4.3)(vite@5.2.11):
+ /@storybook/builder-vite@8.2.9(storybook@8.2.9)(typescript@5.6.2)(vite@5.2.11):
resolution: {integrity: sha512-MHD3ezRjKkJkOl0u7CRQoQD/LKd28YMWIcaz4YrV6ygokc0c3RFTlOefICQFgboc+1RwIUowxN1CJ2kJ7p4SWw==}
peerDependencies:
'@preact/preset-vite': '*'
@@ -6311,7 +6311,7 @@ packages:
magic-string: 0.30.10
storybook: 8.2.9(@babel/preset-env@7.22.6)
ts-dedent: 2.2.0
- typescript: 5.4.3
+ typescript: 5.6.2
vite: 5.2.11(@types/node@17.0.45)(sass@1.71.1)
transitivePeerDependencies:
- supports-color
@@ -6451,17 +6451,17 @@ packages:
storybook: 8.2.9(@babel/preset-env@7.22.6)
dev: true
- /@storybook/preset-create-react-app@8.2.9(react-refresh@0.14.0)(react-scripts@5.0.1)(storybook@8.2.9)(typescript@5.4.3)(webpack@5.91.0):
+ /@storybook/preset-create-react-app@8.2.9(react-refresh@0.14.0)(react-scripts@5.0.1)(storybook@8.2.9)(typescript@5.6.2)(webpack@5.91.0):
resolution: {integrity: sha512-ntCJ0vf9DQqkDtR3QuwNbSBH76xDudFYcd6V+2uyN3qCc3G3QpuxPngVpQzcbsct6BmTudGzf45SkLgAsnWrYw==}
peerDependencies:
react-scripts: '>=5.0.0'
storybook: ^8.2.9
dependencies:
'@pmmmwh/react-refresh-webpack-plugin': 0.5.15(react-refresh@0.14.0)(webpack@5.91.0)
- '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.4.3)(webpack@5.91.0)
+ '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.6.2)(webpack@5.91.0)
'@types/semver': 7.5.8
- pnp-webpack-plugin: 1.7.0(typescript@5.4.3)
- react-scripts: 5.0.1(@babel/plugin-syntax-flow@7.23.3)(@babel/plugin-transform-react-jsx@7.23.4)(esbuild@0.21.3)(eslint@8.44.0)(react@18.2.0)(sass@1.71.1)(typescript@5.4.3)
+ pnp-webpack-plugin: 1.7.0(typescript@5.6.2)
+ react-scripts: 5.0.1(@babel/plugin-syntax-flow@7.23.3)(@babel/plugin-transform-react-jsx@7.23.4)(esbuild@0.21.3)(eslint@8.44.0)(react@18.2.0)(sass@1.71.1)(typescript@5.6.2)
semver: 7.6.3
storybook: 8.2.9(@babel/preset-env@7.22.6)
transitivePeerDependencies:
@@ -6485,7 +6485,7 @@ packages:
storybook: 8.2.9(@babel/preset-env@7.22.6)
dev: true
- /@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.4.3)(webpack@5.91.0):
+ /@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.6.2)(webpack@5.91.0):
resolution: {integrity: sha512-KUqXC3oa9JuQ0kZJLBhVdS4lOneKTOopnNBK4tUAgoxWQ3u/IjzdueZjFr7gyBrXMoU6duutk3RQR9u8ZpYJ4Q==}
peerDependencies:
typescript: '>= 4.x'
@@ -6496,9 +6496,9 @@ packages:
find-cache-dir: 3.3.2
flat-cache: 3.2.0
micromatch: 4.0.7
- react-docgen-typescript: 2.2.2(typescript@5.4.3)
+ react-docgen-typescript: 2.2.2(typescript@5.6.2)
tslib: 2.6.3
- typescript: 5.4.3
+ typescript: 5.6.2
webpack: 5.91.0(esbuild@0.21.3)
transitivePeerDependencies:
- supports-color
@@ -6528,7 +6528,7 @@ packages:
storybook: 8.2.9(@babel/preset-env@7.22.6)
dev: true
- /@storybook/react-vite@8.2.9(react-dom@18.2.0)(react@18.2.0)(rollup@2.79.1)(storybook@8.2.9)(typescript@5.4.3)(vite@5.2.11):
+ /@storybook/react-vite@8.2.9(react-dom@18.2.0)(react@18.2.0)(rollup@2.79.1)(storybook@8.2.9)(typescript@5.6.2)(vite@5.2.11):
resolution: {integrity: sha512-Lw6FzcAaL7jX8Y8EsDzg32Lp0NdeNJZpj0LVwX5sLOQQA6w4i3PqlFINXDY28qCGo6wqKT+w44zhgwUcU5V0Ow==}
engines: {node: '>=18.0.0'}
peerDependencies:
@@ -6537,10 +6537,10 @@ packages:
storybook: ^8.2.9
vite: ^4.0.0 || ^5.0.0
dependencies:
- '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.1(typescript@5.4.3)(vite@5.2.11)
+ '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.1(typescript@5.6.2)(vite@5.2.11)
'@rollup/pluginutils': 5.1.0(rollup@2.79.1)
- '@storybook/builder-vite': 8.2.9(storybook@8.2.9)(typescript@5.4.3)(vite@5.2.11)
- '@storybook/react': 8.2.9(react-dom@18.2.0)(react@18.2.0)(storybook@8.2.9)(typescript@5.4.3)
+ '@storybook/builder-vite': 8.2.9(storybook@8.2.9)(typescript@5.6.2)(vite@5.2.11)
+ '@storybook/react': 8.2.9(react-dom@18.2.0)(react@18.2.0)(storybook@8.2.9)(typescript@5.6.2)
find-up: 5.0.0
magic-string: 0.30.10
react: 18.2.0
@@ -6558,7 +6558,7 @@ packages:
- vite-plugin-glimmerx
dev: true
- /@storybook/react@8.2.9(react-dom@18.2.0)(react@18.2.0)(storybook@8.2.9)(typescript@5.4.3):
+ /@storybook/react@8.2.9(react-dom@18.2.0)(react@18.2.0)(storybook@8.2.9)(typescript@5.6.2):
resolution: {integrity: sha512-F2xZcTDxxjpbqt7eP8rEHmlksiKmE/qtPusEWEY4N4jK01kN+ncxSl8gkJpUohMEmAnVC5t/1v/sU57xv1DYpg==}
engines: {node: '>=18.0.0'}
peerDependencies:
@@ -6593,7 +6593,7 @@ packages:
storybook: 8.2.9(@babel/preset-env@7.22.6)
ts-dedent: 2.2.0
type-fest: 2.19.0
- typescript: 5.4.3
+ typescript: 5.6.2
util-deprecate: 1.0.2
dev: true
@@ -6828,14 +6828,14 @@ packages:
- supports-color
dev: true
- /@svgr/core@8.1.0(typescript@5.4.3):
+ /@svgr/core@8.1.0(typescript@5.6.2):
resolution: {integrity: sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==}
engines: {node: '>=14'}
dependencies:
'@babel/core': 7.24.9
'@svgr/babel-preset': 8.1.0(@babel/core@7.24.9)
camelcase: 6.3.0
- cosmiconfig: 8.3.6(typescript@5.4.3)
+ cosmiconfig: 8.3.6(typescript@5.6.2)
snake-case: 3.0.4
transitivePeerDependencies:
- supports-color
@@ -6877,7 +6877,7 @@ packages:
dependencies:
'@babel/core': 7.24.9
'@svgr/babel-preset': 8.1.0(@babel/core@7.24.9)
- '@svgr/core': 8.1.0(typescript@5.4.3)
+ '@svgr/core': 8.1.0(typescript@5.6.2)
'@svgr/hast-util-to-babel-ast': 8.0.0
svg-parser: 2.0.4
transitivePeerDependencies:
@@ -7994,7 +7994,7 @@ packages:
'@types/yargs-parser': 21.0.3
dev: true
- /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.58.0)(eslint@8.44.0)(typescript@5.4.3):
+ /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.58.0)(eslint@8.44.0)(typescript@5.6.2):
resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -8006,36 +8006,36 @@ packages:
optional: true
dependencies:
'@eslint-community/regexpp': 4.11.0
- '@typescript-eslint/parser': 5.58.0(eslint@8.44.0)(typescript@5.4.3)
+ '@typescript-eslint/parser': 5.58.0(eslint@8.44.0)(typescript@5.6.2)
'@typescript-eslint/scope-manager': 5.62.0
- '@typescript-eslint/type-utils': 5.62.0(eslint@8.44.0)(typescript@5.4.3)
- '@typescript-eslint/utils': 5.62.0(eslint@8.44.0)(typescript@5.4.3)
+ '@typescript-eslint/type-utils': 5.62.0(eslint@8.44.0)(typescript@5.6.2)
+ '@typescript-eslint/utils': 5.62.0(eslint@8.44.0)(typescript@5.6.2)
debug: 4.3.6
eslint: 8.44.0
graphemer: 1.4.0
ignore: 5.3.1
natural-compare-lite: 1.4.0
semver: 7.6.3
- tsutils: 3.21.0(typescript@5.4.3)
- typescript: 5.4.3
+ tsutils: 3.21.0(typescript@5.6.2)
+ typescript: 5.6.2
transitivePeerDependencies:
- supports-color
dev: true
- /@typescript-eslint/experimental-utils@5.62.0(eslint@8.44.0)(typescript@5.4.3):
+ /@typescript-eslint/experimental-utils@5.62.0(eslint@8.44.0)(typescript@5.6.2):
resolution: {integrity: sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
dependencies:
- '@typescript-eslint/utils': 5.62.0(eslint@8.44.0)(typescript@5.4.3)
+ '@typescript-eslint/utils': 5.62.0(eslint@8.44.0)(typescript@5.6.2)
eslint: 8.44.0
transitivePeerDependencies:
- supports-color
- typescript
dev: true
- /@typescript-eslint/parser@5.58.0(eslint@8.44.0)(typescript@5.4.3):
+ /@typescript-eslint/parser@5.58.0(eslint@8.44.0)(typescript@5.6.2):
resolution: {integrity: sha512-ixaM3gRtlfrKzP8N6lRhBbjTow1t6ztfBvQNGuRM8qH1bjFFXIJ35XY+FC0RRBKn3C6cT+7VW1y8tNm7DwPHDQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -8047,10 +8047,10 @@ packages:
dependencies:
'@typescript-eslint/scope-manager': 5.58.0
'@typescript-eslint/types': 5.58.0
- '@typescript-eslint/typescript-estree': 5.58.0(typescript@5.4.3)
+ '@typescript-eslint/typescript-estree': 5.58.0(typescript@5.6.2)
debug: 4.3.6
eslint: 8.44.0
- typescript: 5.4.3
+ typescript: 5.6.2
transitivePeerDependencies:
- supports-color
dev: true
@@ -8071,7 +8071,7 @@ packages:
'@typescript-eslint/visitor-keys': 5.62.0
dev: true
- /@typescript-eslint/type-utils@5.62.0(eslint@8.44.0)(typescript@5.4.3):
+ /@typescript-eslint/type-utils@5.62.0(eslint@8.44.0)(typescript@5.6.2):
resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -8081,12 +8081,12 @@ packages:
typescript:
optional: true
dependencies:
- '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.3)
- '@typescript-eslint/utils': 5.62.0(eslint@8.44.0)(typescript@5.4.3)
+ '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.6.2)
+ '@typescript-eslint/utils': 5.62.0(eslint@8.44.0)(typescript@5.6.2)
debug: 4.3.6
eslint: 8.44.0
- tsutils: 3.21.0(typescript@5.4.3)
- typescript: 5.4.3
+ tsutils: 3.21.0(typescript@5.6.2)
+ typescript: 5.6.2
transitivePeerDependencies:
- supports-color
dev: true
@@ -8101,7 +8101,7 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
- /@typescript-eslint/typescript-estree@5.58.0(typescript@5.4.3):
+ /@typescript-eslint/typescript-estree@5.58.0(typescript@5.6.2):
resolution: {integrity: sha512-cRACvGTodA+UxnYM2uwA2KCwRL7VAzo45syNysqlMyNyjw0Z35Icc9ihPJZjIYuA5bXJYiJ2YGUB59BqlOZT1Q==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -8116,13 +8116,13 @@ packages:
globby: 11.1.0
is-glob: 4.0.3
semver: 7.6.3
- tsutils: 3.21.0(typescript@5.4.3)
- typescript: 5.4.3
+ tsutils: 3.21.0(typescript@5.6.2)
+ typescript: 5.6.2
transitivePeerDependencies:
- supports-color
dev: true
- /@typescript-eslint/typescript-estree@5.62.0(typescript@5.4.3):
+ /@typescript-eslint/typescript-estree@5.62.0(typescript@5.6.2):
resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -8137,13 +8137,13 @@ packages:
globby: 11.1.0
is-glob: 4.0.3
semver: 7.6.3
- tsutils: 3.21.0(typescript@5.4.3)
- typescript: 5.4.3
+ tsutils: 3.21.0(typescript@5.6.2)
+ typescript: 5.6.2
transitivePeerDependencies:
- supports-color
dev: true
- /@typescript-eslint/utils@5.62.0(eslint@8.44.0)(typescript@5.4.3):
+ /@typescript-eslint/utils@5.62.0(eslint@8.44.0)(typescript@5.6.2):
resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -8154,7 +8154,7 @@ packages:
'@types/semver': 7.5.8
'@typescript-eslint/scope-manager': 5.62.0
'@typescript-eslint/types': 5.62.0
- '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.3)
+ '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.6.2)
eslint: 8.44.0
eslint-scope: 5.1.1
semver: 7.6.3
@@ -9260,7 +9260,7 @@ packages:
dev: true
/batch@0.6.1:
- resolution: {integrity: sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=}
+ resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==}
dev: true
/bfj@7.1.0:
@@ -9400,7 +9400,7 @@ packages:
dev: true
/bytes@3.0.0:
- resolution: {integrity: sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=}
+ resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==}
engines: {node: '>= 0.8'}
dev: true
@@ -10058,7 +10058,7 @@ packages:
path-type: 4.0.0
yaml: 1.10.2
- /cosmiconfig@8.3.6(typescript@5.4.3):
+ /cosmiconfig@8.3.6(typescript@5.6.2):
resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==}
engines: {node: '>=14'}
peerDependencies:
@@ -10071,7 +10071,7 @@ packages:
js-yaml: 4.1.0
parse-json: 5.2.0
path-type: 4.0.0
- typescript: 5.4.3
+ typescript: 5.6.2
dev: false
/crelt@1.0.6:
@@ -11271,7 +11271,7 @@ packages:
eslint: 8.44.0
dev: true
- /eslint-config-react-app@7.0.1(@babel/plugin-syntax-flow@7.23.3)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.44.0)(jest@27.5.1)(typescript@5.4.3):
+ /eslint-config-react-app@7.0.1(@babel/plugin-syntax-flow@7.23.3)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.44.0)(jest@27.5.1)(typescript@5.6.2):
resolution: {integrity: sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==}
engines: {node: '>=14.0.0'}
peerDependencies:
@@ -11284,19 +11284,19 @@ packages:
'@babel/core': 7.24.9
'@babel/eslint-parser': 7.25.1(@babel/core@7.24.9)(eslint@8.44.0)
'@rushstack/eslint-patch': 1.10.4
- '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.58.0)(eslint@8.44.0)(typescript@5.4.3)
- '@typescript-eslint/parser': 5.58.0(eslint@8.44.0)(typescript@5.4.3)
+ '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.58.0)(eslint@8.44.0)(typescript@5.6.2)
+ '@typescript-eslint/parser': 5.58.0(eslint@8.44.0)(typescript@5.6.2)
babel-preset-react-app: 10.0.1
confusing-browser-globals: 1.0.11
eslint: 8.44.0
eslint-plugin-flowtype: 8.0.3(@babel/plugin-syntax-flow@7.23.3)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.44.0)
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.58.0)(eslint@8.44.0)
- eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.62.0)(eslint@8.44.0)(jest@27.5.1)(typescript@5.4.3)
+ eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.62.0)(eslint@8.44.0)(jest@27.5.1)(typescript@5.6.2)
eslint-plugin-jsx-a11y: 6.7.1(eslint@8.44.0)
eslint-plugin-react: 7.35.0(eslint@8.44.0)
eslint-plugin-react-hooks: 4.6.0(eslint@8.44.0)
- eslint-plugin-testing-library: 5.11.1(eslint@8.44.0)(typescript@5.4.3)
- typescript: 5.4.3
+ eslint-plugin-testing-library: 5.11.1(eslint@8.44.0)(typescript@5.6.2)
+ typescript: 5.6.2
transitivePeerDependencies:
- '@babel/plugin-syntax-flow'
- '@babel/plugin-transform-react-jsx'
@@ -11337,7 +11337,7 @@ packages:
eslint-import-resolver-webpack:
optional: true
dependencies:
- '@typescript-eslint/parser': 5.58.0(eslint@8.44.0)(typescript@5.4.3)
+ '@typescript-eslint/parser': 5.58.0(eslint@8.44.0)(typescript@5.6.2)
debug: 3.2.7
eslint: 8.44.0
eslint-import-resolver-node: 0.3.9
@@ -11370,7 +11370,7 @@ packages:
'@typescript-eslint/parser':
optional: true
dependencies:
- '@typescript-eslint/parser': 5.58.0(eslint@8.44.0)(typescript@5.4.3)
+ '@typescript-eslint/parser': 5.58.0(eslint@8.44.0)(typescript@5.6.2)
array-includes: 3.1.8
array.prototype.findlastindex: 1.2.5
array.prototype.flat: 1.3.2
@@ -11395,7 +11395,7 @@ packages:
- supports-color
dev: true
- /eslint-plugin-jest@25.7.0(@typescript-eslint/eslint-plugin@5.62.0)(eslint@8.44.0)(jest@27.5.1)(typescript@5.4.3):
+ /eslint-plugin-jest@25.7.0(@typescript-eslint/eslint-plugin@5.62.0)(eslint@8.44.0)(jest@27.5.1)(typescript@5.6.2):
resolution: {integrity: sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
peerDependencies:
@@ -11408,8 +11408,8 @@ packages:
jest:
optional: true
dependencies:
- '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.58.0)(eslint@8.44.0)(typescript@5.4.3)
- '@typescript-eslint/experimental-utils': 5.62.0(eslint@8.44.0)(typescript@5.4.3)
+ '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.58.0)(eslint@8.44.0)(typescript@5.6.2)
+ '@typescript-eslint/experimental-utils': 5.62.0(eslint@8.44.0)(typescript@5.6.2)
eslint: 8.44.0
jest: 27.5.1
transitivePeerDependencies:
@@ -11486,13 +11486,13 @@ packages:
eslint: 8.44.0
dev: true
- /eslint-plugin-testing-library@5.11.1(eslint@8.44.0)(typescript@5.4.3):
+ /eslint-plugin-testing-library@5.11.1(eslint@8.44.0)(typescript@5.6.2):
resolution: {integrity: sha512-5eX9e1Kc2PqVRed3taaLnAAqPZGEX75C+M/rXzUAI3wIg/ZxzUm1OVAwfe/O+vE+6YXOLetSe9g5GKD2ecXipw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0, npm: '>=6'}
peerDependencies:
eslint: ^7.5.0 || ^8.0.0
dependencies:
- '@typescript-eslint/utils': 5.62.0(eslint@8.44.0)(typescript@5.4.3)
+ '@typescript-eslint/utils': 5.62.0(eslint@8.44.0)(typescript@5.6.2)
eslint: 8.44.0
transitivePeerDependencies:
- supports-color
@@ -12128,7 +12128,7 @@ packages:
cross-spawn: 7.0.3
signal-exit: 4.1.0
- /fork-ts-checker-webpack-plugin@6.5.3(eslint@8.44.0)(typescript@5.4.3)(webpack@5.91.0):
+ /fork-ts-checker-webpack-plugin@6.5.3(eslint@8.44.0)(typescript@5.6.2)(webpack@5.91.0):
resolution: {integrity: sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==}
engines: {node: '>=10', yarn: '>=1.0.0'}
peerDependencies:
@@ -12156,7 +12156,7 @@ packages:
schema-utils: 2.7.0
semver: 7.6.3
tapable: 1.1.3
- typescript: 5.4.3
+ typescript: 5.6.2
webpack: 5.91.0(esbuild@0.21.3)
dev: true
@@ -16141,11 +16141,11 @@ packages:
find-up: 3.0.0
dev: true
- /pnp-webpack-plugin@1.7.0(typescript@5.4.3):
+ /pnp-webpack-plugin@1.7.0(typescript@5.6.2):
resolution: {integrity: sha512-2Rb3vm+EXble/sMXNSu6eoBx8e79gKqhNq9F5ZWW6ERNCTE/Q0wQNne5541tE5vKjfM8hpNCYL+LGc1YTfI0dg==}
engines: {node: '>=6'}
dependencies:
- ts-pnp: 1.2.0(typescript@5.4.3)
+ ts-pnp: 1.2.0(typescript@5.6.2)
transitivePeerDependencies:
- typescript
dev: true
@@ -17359,7 +17359,7 @@ packages:
peerDependencies:
react-scripts: '>=2.1.3'
dependencies:
- react-scripts: 5.0.1(@babel/plugin-syntax-flow@7.23.3)(@babel/plugin-transform-react-jsx@7.23.4)(esbuild@0.21.3)(eslint@8.44.0)(react@18.2.0)(sass@1.71.1)(typescript@5.4.3)
+ react-scripts: 5.0.1(@babel/plugin-syntax-flow@7.23.3)(@babel/plugin-transform-react-jsx@7.23.4)(esbuild@0.21.3)(eslint@8.44.0)(react@18.2.0)(sass@1.71.1)(typescript@5.6.2)
semver: 5.7.2
dev: true
@@ -17414,7 +17414,7 @@ packages:
react-dom: 18.3.1(react@18.3.1)
dev: true
- /react-dev-utils@12.0.1(eslint@8.44.0)(typescript@5.4.3)(webpack@5.91.0):
+ /react-dev-utils@12.0.1(eslint@8.44.0)(typescript@5.6.2)(webpack@5.91.0):
resolution: {integrity: sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==}
engines: {node: '>=14'}
peerDependencies:
@@ -17433,7 +17433,7 @@ packages:
escape-string-regexp: 4.0.0
filesize: 8.0.7
find-up: 5.0.0
- fork-ts-checker-webpack-plugin: 6.5.3(eslint@8.44.0)(typescript@5.4.3)(webpack@5.91.0)
+ fork-ts-checker-webpack-plugin: 6.5.3(eslint@8.44.0)(typescript@5.6.2)(webpack@5.91.0)
global-modules: 2.0.0
globby: 11.1.0
gzip-size: 6.0.0
@@ -17448,7 +17448,7 @@ packages:
shell-quote: 1.8.1
strip-ansi: 6.0.1
text-table: 0.2.0
- typescript: 5.4.3
+ typescript: 5.6.2
webpack: 5.91.0(esbuild@0.21.3)
transitivePeerDependencies:
- eslint
@@ -17487,12 +17487,12 @@ packages:
react: 18.2.0
dev: false
- /react-docgen-typescript@2.2.2(typescript@5.4.3):
+ /react-docgen-typescript@2.2.2(typescript@5.6.2):
resolution: {integrity: sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==}
peerDependencies:
typescript: '>= 4.3.x'
dependencies:
- typescript: 5.4.3
+ typescript: 5.6.2
dev: true
/react-docgen@7.0.3:
@@ -17715,7 +17715,7 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
- /react-scripts@5.0.1(@babel/plugin-syntax-flow@7.23.3)(@babel/plugin-transform-react-jsx@7.23.4)(esbuild@0.21.3)(eslint@8.44.0)(react@18.2.0)(sass@1.71.1)(typescript@5.4.3):
+ /react-scripts@5.0.1(@babel/plugin-syntax-flow@7.23.3)(@babel/plugin-transform-react-jsx@7.23.4)(esbuild@0.21.3)(eslint@8.44.0)(react@18.2.0)(sass@1.71.1)(typescript@5.6.2):
resolution: {integrity: sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==}
engines: {node: '>=14.0.0'}
hasBin: true
@@ -17743,7 +17743,7 @@ packages:
dotenv: 10.0.0
dotenv-expand: 5.1.0
eslint: 8.44.0
- eslint-config-react-app: 7.0.1(@babel/plugin-syntax-flow@7.23.3)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.44.0)(jest@27.5.1)(typescript@5.4.3)
+ eslint-config-react-app: 7.0.1(@babel/plugin-syntax-flow@7.23.3)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.44.0)(jest@27.5.1)(typescript@5.6.2)
eslint-webpack-plugin: 3.2.0(eslint@8.44.0)(webpack@5.91.0)
file-loader: 6.2.0(webpack@5.91.0)
fs-extra: 10.1.0
@@ -17761,7 +17761,7 @@ packages:
prompts: 2.4.2
react: 18.2.0
react-app-polyfill: 3.0.0
- react-dev-utils: 12.0.1(eslint@8.44.0)(typescript@5.4.3)(webpack@5.91.0)
+ react-dev-utils: 12.0.1(eslint@8.44.0)(typescript@5.6.2)(webpack@5.91.0)
react-refresh: 0.11.0
resolve: 1.22.8
resolve-url-loader: 4.0.0
@@ -17771,7 +17771,7 @@ packages:
style-loader: 3.3.4(webpack@5.91.0)
tailwindcss: 3.4.7
terser-webpack-plugin: 5.3.10(esbuild@0.21.3)(webpack@5.91.0)
- typescript: 5.4.3
+ typescript: 5.6.2
webpack: 5.91.0(esbuild@0.21.3)
webpack-dev-server: 4.15.2(webpack@5.91.0)
webpack-manifest-plugin: 4.1.1(webpack@5.91.0)
@@ -19076,7 +19076,7 @@ packages:
'@emotion/styled': 10.3.0(@emotion/core@10.3.1)(react@18.2.0)
'@material-ui/core': 4.12.4(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0)
'@storybook/addons': 6.5.16(react-dom@18.2.0)(react@18.2.0)
- '@storybook/react': 8.2.9(react-dom@18.2.0)(react@18.2.0)(storybook@8.2.9)(typescript@5.4.3)
+ '@storybook/react': 8.2.9(react-dom@18.2.0)(react@18.2.0)(storybook@8.2.9)(typescript@5.6.2)
'@usulpro/color-picker': 1.1.4(react@18.2.0)
global: 4.4.0
js-beautify: 1.15.1
@@ -19833,7 +19833,7 @@ packages:
tslib: 2.6.3
dev: false
- /ts-jest@29.1.3(@babel/core@7.22.5)(esbuild@0.21.3)(jest@27.5.1)(typescript@5.4.3):
+ /ts-jest@29.1.3(@babel/core@7.22.5)(esbuild@0.21.3)(jest@27.5.1)(typescript@5.6.2):
resolution: {integrity: sha512-6L9qz3ginTd1NKhOxmkP0qU3FyKjj5CPoY+anszfVn6Pmv/RIKzhiMCsH7Yb7UvJR9I2A64rm4zQl531s2F1iw==}
engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0}
hasBin: true
@@ -19867,11 +19867,11 @@ packages:
lodash.memoize: 4.1.2
make-error: 1.3.6
semver: 7.6.3
- typescript: 5.4.3
+ typescript: 5.6.2
yargs-parser: 21.1.1
dev: true
- /ts-pnp@1.2.0(typescript@5.4.3):
+ /ts-pnp@1.2.0(typescript@5.6.2):
resolution: {integrity: sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==}
engines: {node: '>=6'}
peerDependencies:
@@ -19880,14 +19880,14 @@ packages:
typescript:
optional: true
dependencies:
- typescript: 5.4.3
+ typescript: 5.6.2
dev: true
/ts-toolbelt@6.15.5:
resolution: {integrity: sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A==}
dev: true
- /tsconfck@3.1.1(typescript@5.4.3):
+ /tsconfck@3.1.1(typescript@5.6.2):
resolution: {integrity: sha512-00eoI6WY57SvZEVjm13stEVE90VkEdJAFGgpFLTsZbJyW/LwFQ7uQxJHWpZ2hzSWgCPKc9AnBnNP+0X7o3hAmQ==}
engines: {node: ^18 || >=20}
hasBin: true
@@ -19897,7 +19897,7 @@ packages:
typescript:
optional: true
dependencies:
- typescript: 5.4.3
+ typescript: 5.6.2
dev: true
/tsconfig-paths-webpack-plugin@4.0.1:
@@ -19934,14 +19934,14 @@ packages:
/tslib@2.6.3:
resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==}
- /tsutils@3.21.0(typescript@5.4.3):
+ /tsutils@3.21.0(typescript@5.6.2):
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
engines: {node: '>= 6'}
peerDependencies:
typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
dependencies:
tslib: 1.14.1
- typescript: 5.4.3
+ typescript: 5.6.2
dev: true
/type-check@0.3.2:
@@ -20061,8 +20061,8 @@ packages:
dependencies:
is-typedarray: 1.0.0
- /typescript@5.4.3:
- resolution: {integrity: sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==}
+ /typescript@5.6.2:
+ resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==}
engines: {node: '>=14.17'}
hasBin: true
@@ -20489,13 +20489,13 @@ packages:
- terser
dev: true
- /vite-plugin-svgr@4.2.0(rollup@2.79.1)(typescript@5.4.3)(vite@5.2.11):
+ /vite-plugin-svgr@4.2.0(rollup@2.79.1)(typescript@5.6.2)(vite@5.2.11):
resolution: {integrity: sha512-SC7+FfVtNQk7So0XMjrrtLAbEC8qjFPifyD7+fs/E6aaNdVde6umlVVh0QuwDLdOMu7vp5RiGFsB70nj5yo0XA==}
peerDependencies:
vite: ^2.6.0 || 3 || 4 || 5
dependencies:
'@rollup/pluginutils': 5.1.0(rollup@2.79.1)
- '@svgr/core': 8.1.0(typescript@5.4.3)
+ '@svgr/core': 8.1.0(typescript@5.6.2)
'@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0)
vite: 5.2.11(@types/node@17.0.45)(sass@1.71.1)
transitivePeerDependencies:
@@ -20504,7 +20504,7 @@ packages:
- typescript
dev: false
- /vite-tsconfig-paths@4.3.2(typescript@5.4.3)(vite@5.2.11):
+ /vite-tsconfig-paths@4.3.2(typescript@5.6.2)(vite@5.2.11):
resolution: {integrity: sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==}
peerDependencies:
vite: '*'
@@ -20514,7 +20514,7 @@ packages:
dependencies:
debug: 4.3.6
globrex: 0.1.2
- tsconfck: 3.1.1(typescript@5.4.3)
+ tsconfck: 3.1.1(typescript@5.6.2)
vite: 5.2.11(@types/node@17.0.45)(sass@1.71.1)
transitivePeerDependencies:
- supports-color
@@ -21079,7 +21079,6 @@ packages:
/workbox-google-analytics@6.6.0:
resolution: {integrity: sha512-p4DJa6OldXWd6M9zRl0H6vB9lkrmqYFkRQ2xEiNdBFp9U0LhsGO7hsBscVEyH9H2/3eZZt8c97NB2FD9U2NJ+Q==}
- deprecated: It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained
dependencies:
workbox-background-sync: 6.6.0
workbox-core: 6.6.0
diff --git a/editor.planx.uk/src/@planx/components/MapAndLabel/Public/index.test.tsx b/editor.planx.uk/src/@planx/components/MapAndLabel/Public/index.test.tsx
index 3f2e3c68d6..ee15ba20a9 100644
--- a/editor.planx.uk/src/@planx/components/MapAndLabel/Public/index.test.tsx
+++ b/editor.planx.uk/src/@planx/components/MapAndLabel/Public/index.test.tsx
@@ -1,14 +1,14 @@
import { MyMap } from "@opensystemslab/map";
import { Presentational as MapAndLabel } from "@planx/components/MapAndLabel/Public";
-import { waitFor } from "@testing-library/react";
+import { waitFor, within } from "@testing-library/react";
import React from "react";
import { setup } from "testUtils";
import { vi } from "vitest";
import { axe } from "vitest-axe";
-import { point1 } from "../test/mocks/geojson";
+import { point1, point2 } from "../test/mocks/geojson";
import { props } from "../test/mocks/Trees";
-import { addFeaturesToMap } from "../test/utils";
+import { addFeaturesToMap, addMultipleFeatures } from "../test/utils";
beforeAll(() => {
if (!window.customElements.get("my-map")) {
@@ -84,44 +84,181 @@ describe("Basic UI", () => {
// Schema and field validation is handled in both List and Schema folders - here we're only testing the MapAndLabel specific error handling
describe("validation and error handling", () => {
- test.todo("all fields are required");
- test.todo("all fields are required, for all feature tabs");
- test.todo("an error displays if the minimum number of items is not met");
- test.todo("an error displays if the maximum number of items is exceeded");
- test.todo(
- "an error state is applied to a tabpanel button, when it's associated feature is invalid",
- );
+ it("shows all fields are required", async () => {
+ const { getAllByTestId, getByTestId, getByRole, user } = setup(
+ ,
+ );
+ const map = getByTestId("map-and-label-map");
+ expect(map).toBeInTheDocument();
+
+ addFeaturesToMap(map, [point1]);
+
+ expect(getByRole("tab", { name: /Tree 1/ })).toBeInTheDocument();
+
+ const continueButton = getByRole("button", { name: /Continue/ });
+ expect(continueButton).toBeInTheDocument();
+ await user.click(continueButton);
+
+ const errorMessages = getAllByTestId(/error-message-input/);
+
+ // Date field has been removed so only 4 inputs
+ expect(errorMessages).toHaveLength(4);
+
+ errorMessages.forEach((message) => {
+ expect(message).not.toBeEmptyDOMElement();
+ });
+ });
+
+ // it shows all fields are required in a tab
+ it("should show all fields are required, for all feature tabs", async () => {
+ const { getByTestId, getByRole, user, debug } = setup(
+ ,
+ );
+ const map = getByTestId("map-and-label-map");
+ expect(map).toBeInTheDocument();
+ debug();
+
+ addMultipleFeatures([point1, point2]);
+
+ // vertical side tab query
+ const firstTab = getByRole("tab", { name: /Tree 1/ });
+ const secondTab = getByRole("tab", { name: /Tree 2/ });
+
+ // side tab validation
+ expect(firstTab).toBeInTheDocument();
+ expect(secondTab).toBeInTheDocument();
+
+ // form for each tab
+ const firstTabPanel = getByTestId("vertical-tabpanel-0");
+ const secondTabPanel = getByTestId("vertical-tabpanel-1");
+
+ // default is to start on seond tab panel since we add two points
+ expect(firstTabPanel).not.toBeVisible();
+ expect(secondTabPanel).toBeVisible();
+
+ // Form is generate for secondTabPanel but not the first
+ expect(secondTabPanel.childElementCount).toBeGreaterThan(0);
+ expect(firstTabPanel.childElementCount).toBe(0);
+
+ const continueButton = getByRole("button", { name: /Continue/ });
+ await user.click(continueButton);
+
+ // error messages appear
+ const errorMessagesTabTwo =
+ within(secondTabPanel).getAllByTestId(/error-message-input/);
+ expect(errorMessagesTabTwo).toHaveLength(4);
+
+ // error messages are empty but visible before error state induced
+ // this ensures they contain the error message text
+ errorMessagesTabTwo.forEach((input) => {
+ expect(input).not.toBeEmptyDOMElement();
+ });
+
+ await user.click(firstTab);
+
+ expect(firstTabPanel).toBeVisible();
+
+ // error messages persist
+ const errorMessagesTabOne =
+ within(firstTabPanel).getAllByTestId(/error-message-input/);
+ expect(errorMessagesTabOne).toHaveLength(4);
+ });
+
+ // it shows all fields are required across different tabs
+ it("should show an error if the minimum number of items is not met", async () => {
+ const { getByTestId, getByRole, user } = setup();
+ const map = getByTestId("map-and-label-map");
+ expect(map).toBeInTheDocument();
+
+ const continueButton = getByRole("button", { name: /Continue/ });
+
+ await user.click(continueButton);
+
+ const errorWrapper = getByTestId(/error-wrapper/);
+
+ const errorMessage = within(errorWrapper).getByText(/You must plot /);
+
+ expect(errorMessage).toBeInTheDocument();
+ });
+ // ??
+ it("an error state is applied to a tabpanel button, when it's associated feature is invalid", async () => {
+ const { getByTestId, getByRole, user, getAllByTestId } = setup(
+ ,
+ );
+ const map = getByTestId("map-and-label-map");
+ expect(map).toBeInTheDocument();
+
+ addFeaturesToMap(map, [point1]);
+
+ const tabOne = getByRole("tab", { name: /Tree 1/ });
+
+ expect(tabOne).toBeInTheDocument();
+
+ const continueButton = getByRole("button", { name: /Continue/ });
+ expect(continueButton).toBeInTheDocument();
+ await user.click(continueButton);
+
+ const errorMessages = getAllByTestId(/error-message-input/);
+
+ // check error messages are correct amount and contain info
+ expect(errorMessages).toHaveLength(4);
+
+ errorMessages.forEach((message) => {
+ expect(message).not.toBeEmptyDOMElement();
+ });
+
+ expect(tabOne).toHaveStyle("border-left: 5px solid #D4351C");
+ });
+ // shows the error state on a tab when it's invalid
});
+test.todo("an error displays if the maximum number of items is exceeded");
describe("basic interactions - happy path", () => {
test.todo("adding an item to the map adds a feature tab");
+ // add feature, see a tab (one feature only)
test.todo("a user can input details on a single feature and submit");
+ // only one feature, fill out form, submit
test.todo("adding multiple features to the map adds multiple feature tabs");
+ // add more than one feature, see multiple tabs
test.todo("a user can input details on multiple features and submit");
+ // add details to more than one tab, submit
test.todo("a user can input details on feature tabs in any order");
+ // ??
});
describe("copy feature select", () => {
it.todo("is disabled if only a single feature is present");
+ // no copy select if only one feature
it.todo("is enabled once multiple features are present");
+ // copy select enabled once you add more features
it.todo(
"lists all other features as options (the current feature is not listed)",
);
+ // current tree is not an option in the copy select
it.todo("copies all data from one feature to another");
+ // all data fields are populated from one field to another
it.todo("should not have any accessibility violations");
+ // axe checks
});
describe("remove feature button", () => {
it.todo("removes a feature from the form");
+ // click remove - feature is removed
+ // not tab
it.todo("removes a feature from the map");
+ // click remove - feature is removed
+ // no map icon
});
describe("payload generation", () => {
test.todo("a submitted payload contains a GeoJSON feature collection");
+ // check payload contains GeoJSON feature collection
test.todo(
"the feature collection contains all geospatial data inputted by the user",
);
+ // feature collection matches the mocked data
test.todo(
"each feature's properties correspond with the details entered for that feature",
);
+ // feature properties contain the answers to inputs
});
diff --git a/editor.planx.uk/src/@planx/components/MapAndLabel/Public/index.tsx b/editor.planx.uk/src/@planx/components/MapAndLabel/Public/index.tsx
index b7825ad4ce..4b260271a7 100644
--- a/editor.planx.uk/src/@planx/components/MapAndLabel/Public/index.tsx
+++ b/editor.planx.uk/src/@planx/components/MapAndLabel/Public/index.tsx
@@ -128,6 +128,7 @@ const VerticalFeatureTabs: React.FC = () => {
sx={{ width: "100%" }}
aria-labelledby={`vertical-tab-${i}`}
id={`vertical-tabpanel-${i}`}
+ data-testid={`vertical-tabpanel-${i}`}
>
field.type !== "date"),
+};
+
export const props: PresentationalProps = {
title: "Mock title",
description: "Mock description",
schemaName: "Trees",
fn: "MockFn",
- schema: Trees,
+ schema: mockTreeSchema,
basemap: "OSM",
drawColor: "#00FF00",
drawType: "Point",
diff --git a/editor.planx.uk/src/@planx/components/MapAndLabel/test/mocks/geojson.ts b/editor.planx.uk/src/@planx/components/MapAndLabel/test/mocks/geojson.ts
index 7a4adf8507..7aff35a382 100644
--- a/editor.planx.uk/src/@planx/components/MapAndLabel/test/mocks/geojson.ts
+++ b/editor.planx.uk/src/@planx/components/MapAndLabel/test/mocks/geojson.ts
@@ -10,3 +10,25 @@ export const point1: Feature = {
coordinates: [-3.685929607119201, 57.15301433687542],
},
};
+
+export const point2: Feature = {
+ type: "Feature",
+ properties: {
+ label: "2",
+ },
+ geometry: {
+ type: "Point",
+ coordinates: [-3.686529607119201, 57.15310433687542],
+ },
+};
+
+export const point3: Feature = {
+ type: "Feature",
+ properties: {
+ label: "3",
+ },
+ geometry: {
+ type: "Point",
+ coordinates: [-3.68689607119201, 57.15310833687542],
+ },
+};
diff --git a/editor.planx.uk/src/@planx/components/MapAndLabel/test/utils.ts b/editor.planx.uk/src/@planx/components/MapAndLabel/test/utils.ts
index b335faf094..9baaeea2d1 100644
--- a/editor.planx.uk/src/@planx/components/MapAndLabel/test/utils.ts
+++ b/editor.planx.uk/src/@planx/components/MapAndLabel/test/utils.ts
@@ -1,3 +1,4 @@
+import { screen } from "@testing-library/react";
import { Feature, Point, Polygon } from "geojson";
import { act } from "react-dom/test-utils";
@@ -16,3 +17,14 @@ export const addFeaturesToMap = async (
});
act(() => map.dispatchEvent(mockEvent));
};
+
+export const addMultipleFeatures = (
+ featureArray: Feature[],
+) => {
+ const map = screen.getByTestId("map-and-label-map");
+ const pointsAddedArray: Feature[] = [];
+ featureArray.forEach((feature) => {
+ pointsAddedArray.push(feature);
+ addFeaturesToMap(map, pointsAddedArray);
+ });
+};
diff --git a/editor.planx.uk/src/hooks/useSearch.ts b/editor.planx.uk/src/hooks/useSearch.ts
index 16dbe22f37..a186fe707f 100644
--- a/editor.planx.uk/src/hooks/useSearch.ts
+++ b/editor.planx.uk/src/hooks/useSearch.ts
@@ -9,7 +9,8 @@ interface UseSearchProps {
export interface SearchResult {
item: T;
key: string;
- matchIndices?: [number, number][];
+ matchIndices: [number, number][];
+ refIndex: number;
}
export type SearchResults = SearchResult[];
@@ -39,13 +40,18 @@ export const useSearch = ({
useEffect(() => {
const fuseResults = fuse.search(pattern);
setResults(
- fuseResults.map((result) => ({
- item: result.item,
- key: result.matches?.[0].key || "",
- // We only display the first match
- matchIndices:
- (result.matches?.[0].indices as [number, number][]) || undefined,
- })),
+ fuseResults.map((result) => {
+ // Required type narrowing for FuseResult
+ if (!result.matches) throw Error("Matches missing from FuseResults");
+
+ return {
+ item: result.item,
+ key: result.matches?.[0].key || "",
+ // We only display the first match
+ matchIndices: result.matches[0].indices as [number, number][],
+ refIndex: result.matches[0]?.refIndex || 0,
+ };
+ }),
);
}, [pattern, fuse]);
diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/Search/NodeSearchResults.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/Search/NodeSearchResults.tsx
index c8dfedceaf..7672884536 100644
--- a/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/Search/NodeSearchResults.tsx
+++ b/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/Search/NodeSearchResults.tsx
@@ -2,7 +2,7 @@ import List from "@mui/material/List";
import ListItem from "@mui/material/ListItem";
import { styled } from "@mui/material/styles";
import Typography from "@mui/material/Typography";
-import { ComponentType, IndexedNode } from "@opensystemslab/planx-core/types";
+import { IndexedNode } from "@opensystemslab/planx-core/types";
import type { SearchResults } from "hooks/useSearch";
import React from "react";
@@ -17,32 +17,20 @@ export const Root = styled(List)(({ theme }) => ({
export const NodeSearchResults: React.FC<{
results: SearchResults;
-}> = ({ results }) => {
- /** Temporary guard function to filter out component types not yet supported by SearchResultCard */
- const isSupportedNodeType = (
- result: SearchResults[number],
- ): boolean =>
- ![
- ComponentType.FileUploadAndLabel,
- ComponentType.Calculate,
- ComponentType.List,
- ].includes(result.item.type);
+}> = ({ results }) => (
+ <>
+
+ {!results.length && "No matches found"}
+ {results.length === 1 && "1 result:"}
+ {results.length > 1 && `${results.length} results:`}
+
- return (
- <>
-
- {!results.length && "No matches found"}
- {results.length === 1 && "1 result:"}
- {results.length > 1 && `${results.length} results:`}
-
-
-
- {results.filter(isSupportedNodeType).map((result) => (
-
-
-
- ))}
-
- >
- );
-};
+
+ {results.map((result) => (
+
+
+
+ ))}
+
+ >
+);
diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/Search/SearchResultCard/DataDisplayMap.test.ts b/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/Search/SearchResultCard/DataDisplayMap.test.ts
new file mode 100644
index 0000000000..d8e07894e3
--- /dev/null
+++ b/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/Search/SearchResultCard/DataDisplayMap.test.ts
@@ -0,0 +1,126 @@
+import { ComponentType } from "@opensystemslab/planx-core/types";
+import { useStore } from "pages/FlowEditor/lib/store";
+
+import {
+ mockAnswerResult,
+ mockCalculateFormulaResult,
+ mockCalculateRootResult,
+ mockFileUploadAndLabelResult,
+ mockFlow,
+ mockListAnswerResult,
+ mockListDataResult,
+ mockListRootResult,
+ mockQuestionResult,
+} from "../mocks/DataDisplayMap";
+import { getDisplayDetailsForResult } from "./DataDisplayMap";
+
+type Output = ReturnType;
+
+// Setup flow so that it can be referenced by SearchResults (e.g. getting parent nodes)
+beforeAll(() => useStore.setState({ flow: mockFlow }));
+
+describe("Question component", () => {
+ it("returns the expected display values", () => {
+ const output = getDisplayDetailsForResult(mockQuestionResult);
+
+ expect(output).toStrictEqual