From 1737458a3acd5d689db9394e3686010385a9488a Mon Sep 17 00:00:00 2001 From: Takis Kakalis <80459599+Takaros999@users.noreply.github.com> Date: Thu, 28 May 2026 11:53:13 +0300 Subject: [PATCH] fix(react): add terminal widget errors --- .../react/src/__tests__/widgets.test.tsx | 60 +++++++++++++++++++ .../src/components/States/ErrorState.tsx | 21 +++++++ js/packages/react/src/lang/translations/en.ts | 6 ++ js/packages/react/src/lang/translations/es.ts | 7 +++ js/packages/react/src/lang/translations/th.ts | 8 +++ js/packages/react/src/lang/types.ts | 4 ++ 6 files changed, 106 insertions(+) diff --git a/js/packages/react/src/__tests__/widgets.test.tsx b/js/packages/react/src/__tests__/widgets.test.tsx index beee8736..0a156e70 100644 --- a/js/packages/react/src/__tests__/widgets.test.tsx +++ b/js/packages/react/src/__tests__/widgets.test.tsx @@ -257,6 +257,66 @@ describe("widgets", () => { expect(flow.reset).not.toHaveBeenCalled(); }); + it("request widget shows requirements state for identity attribute mismatches", async () => { + const flow = createFlow({ + isError: true, + errorCode: IDKitErrorCodes.IdentityAttributesNotMatched, + }); + useIDKitRequestMock.mockReturnValue(flow); + + const onError = vi.fn(); + const onOpenChange = vi.fn(); + + render( + , + ); + + await waitFor(() => { + expect(onError).toHaveBeenCalledWith( + IDKitErrorCodes.IdentityAttributesNotMatched, + ); + }); + expect(screen.getByText("Verification requirements not met")).toBeDefined(); + expect( + screen.getByText( + "Your World ID doesn't meet the requirements for this verification.", + ), + ).toBeDefined(); + + fireEvent.click(screen.getByRole("button", { name: "Close" })); + expect(onOpenChange).toHaveBeenCalledWith(false); + expect(flow.reset).not.toHaveBeenCalled(); + }); + + it("request widget shows terminal state for user presence failures", async () => { + const flow = createFlow({ + isError: true, + errorCode: IDKitErrorCodes.UserPresenceFailed, + }); + useIDKitRequestMock.mockReturnValue(flow); + + const onError = vi.fn(); + const onOpenChange = vi.fn(); + + render( + , + ); + + await waitFor(() => { + expect(onError).toHaveBeenCalledWith(IDKitErrorCodes.UserPresenceFailed); + }); + expect(screen.getByText("Presence check failed")).toBeDefined(); + expect( + screen.getByText( + "World App couldn't confirm your presence for this request.", + ), + ).toBeDefined(); + + fireEvent.click(screen.getByRole("button", { name: "Close" })); + expect(onOpenChange).toHaveBeenCalledWith(false); + expect(flow.reset).not.toHaveBeenCalled(); + }); + it("request widget waits for handleVerify to resolve before calling onSuccess", async () => { const flow = createFlow({ isSuccess: true, diff --git a/js/packages/react/src/components/States/ErrorState.tsx b/js/packages/react/src/components/States/ErrorState.tsx index 1b1cb659..7d4236be 100644 --- a/js/packages/react/src/components/States/ErrorState.tsx +++ b/js/packages/react/src/components/States/ErrorState.tsx @@ -16,12 +16,15 @@ type ErrorVariant = | "configuration_error" | "connection" | "host_verification" + | "identity_attributes_not_matched" + | "user_presence_failed" | "generic"; const errorCodeVariants: Partial> = { [IDKitErrorCodes.UserRejected]: "cancelled", [IDKitErrorCodes.VerificationRejected]: "cancelled", [IDKitErrorCodes.Cancelled]: "cancelled", + [IDKitErrorCodes.UserPresenceFailed]: "user_presence_failed", [IDKitErrorCodes.ConnectionFailed]: "connection", [IDKitErrorCodes.FailedByHostApp]: "host_verification", [IDKitErrorCodes.InvalidRpSignature]: "configuration_error", @@ -33,6 +36,8 @@ const errorCodeVariants: Partial> = { [IDKitErrorCodes.TimestampTooFarInFuture]: "configuration_error", [IDKitErrorCodes.InvalidTimestamp]: "configuration_error", [IDKitErrorCodes.RpSignatureExpired]: "configuration_error", + [IDKitErrorCodes.IdentityAttributesNotMatched]: + "identity_attributes_not_matched", [IDKitErrorCodes.InvalidRpIdFormat]: "configuration_error", }; @@ -74,6 +79,22 @@ const variantConfig = { actionLabel: "Try Again" as const, action: "retry" as const, }, + identity_attributes_not_matched: { + title: "Verification requirements not met" as const, + message: + "Your World ID doesn't meet the requirements for this verification." as const, + Icon: WarningIcon, + actionLabel: "Close" as const, + action: "close" as const, + }, + user_presence_failed: { + title: "Presence check failed" as const, + message: + "World App couldn't confirm your presence for this request." as const, + Icon: WarningIcon, + actionLabel: "Close" as const, + action: "close" as const, + }, generic: { title: "Something went wrong" as const, message: "We couldn't complete your request. Please try again." as const, diff --git a/js/packages/react/src/lang/translations/en.ts b/js/packages/react/src/lang/translations/en.ts index 415840ad..db330dbf 100644 --- a/js/packages/react/src/lang/translations/en.ts +++ b/js/packages/react/src/lang/translations/en.ts @@ -19,6 +19,12 @@ export const en: TranslationStrings = { "Verification declined": "Verification declined", "Failed to verify your credential proof. Please contact the website owner.": "Failed to verify your credential proof. Please contact the website owner.", + "Verification requirements not met": "Verification requirements not met", + "Your World ID doesn't meet the requirements for this verification.": + "Your World ID doesn't meet the requirements for this verification.", + "Presence check failed": "Presence check failed", + "World App couldn't confirm your presence for this request.": + "World App couldn't confirm your presence for this request.", "We couldn't complete your request. Please try again.": "We couldn't complete your request. Please try again.", "Try Again": "Try Again", diff --git a/js/packages/react/src/lang/translations/es.ts b/js/packages/react/src/lang/translations/es.ts index 36d33283..772fd2e6 100644 --- a/js/packages/react/src/lang/translations/es.ts +++ b/js/packages/react/src/lang/translations/es.ts @@ -19,6 +19,13 @@ export const es: TranslationStrings = { "Verification declined": "Verificaci\u00f3n rechazada", "Failed to verify your credential proof. Please contact the website owner.": "No se pudo verificar tu prueba de credencial. Por favor contacta al propietario del sitio web.", + "Verification requirements not met": + "No se cumplen los requisitos de verificacion", + "Your World ID doesn't meet the requirements for this verification.": + "Tu World ID no cumple los requisitos para esta verificacion.", + "Presence check failed": "Fallo la comprobacion de presencia", + "World App couldn't confirm your presence for this request.": + "World App no pudo confirmar tu presencia para esta solicitud.", "We couldn't complete your request. Please try again.": "No pudimos completar tu solicitud. Por favor intenta de nuevo.", "Try Again": "Intentar de nuevo", diff --git a/js/packages/react/src/lang/translations/th.ts b/js/packages/react/src/lang/translations/th.ts index 31fa0dc6..cbf376e7 100644 --- a/js/packages/react/src/lang/translations/th.ts +++ b/js/packages/react/src/lang/translations/th.ts @@ -26,6 +26,14 @@ export const th: TranslationStrings = { "\u0e01\u0e32\u0e23\u0e22\u0e37\u0e19\u0e22\u0e31\u0e19\u0e16\u0e39\u0e01\u0e1b\u0e0f\u0e34\u0e40\u0e2a\u0e18", "Failed to verify your credential proof. Please contact the website owner.": "\u0e44\u0e21\u0e48\u0e2a\u0e32\u0e21\u0e32\u0e23\u0e16\u0e22\u0e37\u0e19\u0e22\u0e31\u0e19\u0e2b\u0e25\u0e31\u0e01\u0e10\u0e32\u0e19\u0e02\u0e2d\u0e07 Credential \u0e44\u0e14\u0e49 \u0e42\u0e1b\u0e23\u0e14\u0e15\u0e34\u0e14\u0e15\u0e48\u0e2d\u0e40\u0e08\u0e49\u0e32\u0e02\u0e2d\u0e07\u0e40\u0e27\u0e47\u0e1a\u0e44\u0e0b\u0e15\u0e4c", + "Verification requirements not met": + "\u0e44\u0e21\u0e48\u0e40\u0e1b\u0e47\u0e19\u0e44\u0e1b\u0e15\u0e32\u0e21\u0e02\u0e49\u0e2d\u0e01\u0e33\u0e2b\u0e19\u0e14\u0e01\u0e32\u0e23\u0e22\u0e37\u0e19\u0e22\u0e31\u0e19", + "Your World ID doesn't meet the requirements for this verification.": + "World ID \u0e02\u0e2d\u0e07\u0e04\u0e38\u0e13\u0e44\u0e21\u0e48\u0e15\u0e23\u0e07\u0e15\u0e32\u0e21\u0e02\u0e49\u0e2d\u0e01\u0e33\u0e2b\u0e19\u0e14\u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e01\u0e32\u0e23\u0e22\u0e37\u0e19\u0e22\u0e31\u0e19\u0e19\u0e35\u0e49", + "Presence check failed": + "\u0e01\u0e32\u0e23\u0e15\u0e23\u0e27\u0e08\u0e2a\u0e2d\u0e1a\u0e01\u0e32\u0e23\u0e21\u0e35\u0e2d\u0e22\u0e39\u0e48\u0e25\u0e49\u0e21\u0e40\u0e2b\u0e25\u0e27", + "World App couldn't confirm your presence for this request.": + "World App \u0e44\u0e21\u0e48\u0e2a\u0e32\u0e21\u0e32\u0e23\u0e16\u0e22\u0e37\u0e19\u0e22\u0e31\u0e19\u0e01\u0e32\u0e23\u0e21\u0e35\u0e2d\u0e22\u0e39\u0e48\u0e02\u0e2d\u0e07\u0e04\u0e38\u0e13\u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e04\u0e33\u0e02\u0e2d\u0e19\u0e35\u0e49\u0e44\u0e14\u0e49", "We couldn't complete your request. Please try again.": "\u0e40\u0e23\u0e32\u0e44\u0e21\u0e48\u0e2a\u0e32\u0e21\u0e32\u0e23\u0e16\u0e14\u0e33\u0e40\u0e19\u0e34\u0e19\u0e01\u0e32\u0e23\u0e15\u0e32\u0e21\u0e04\u0e33\u0e02\u0e2d\u0e02\u0e2d\u0e07\u0e04\u0e38\u0e13\u0e44\u0e14\u0e49 \u0e01\u0e23\u0e38\u0e13\u0e32\u0e25\u0e2d\u0e07\u0e2d\u0e35\u0e01\u0e04\u0e23\u0e31\u0e49\u0e07", "Try Again": diff --git a/js/packages/react/src/lang/types.ts b/js/packages/react/src/lang/types.ts index 080fd5b5..78bc6bc6 100644 --- a/js/packages/react/src/lang/types.ts +++ b/js/packages/react/src/lang/types.ts @@ -18,6 +18,10 @@ export interface TranslationStrings { "Please check your connection and try again.": string; "Verification declined": string; "Failed to verify your credential proof. Please contact the website owner.": string; + "Verification requirements not met": string; + "Your World ID doesn't meet the requirements for this verification.": string; + "Presence check failed": string; + "World App couldn't confirm your presence for this request.": string; "We couldn't complete your request. Please try again.": string; "Try Again": string; Close: string;