diff --git a/eslint.config.ts b/eslint.config.ts index cdbc5aa62..844817204 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + /* eslint-disable @typescript-eslint/no-explicit-any */ import js from "@eslint/js"; diff --git a/packages/angular/jest.config.ts b/packages/angular/jest.config.ts index 5b1d8abf3..d6d1aff08 100644 --- a/packages/angular/jest.config.ts +++ b/packages/angular/jest.config.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import type { Config } from "jest"; import { createCjsPreset } from "jest-preset-angular/presets/index.js"; diff --git a/packages/angular/src/lib/auth/forms/email-link-auth-form.ts b/packages/angular/src/lib/auth/forms/email-link-auth-form.ts index 8be54e5c9..223297376 100644 --- a/packages/angular/src/lib/auth/forms/email-link-auth-form.ts +++ b/packages/angular/src/lib/auth/forms/email-link-auth-form.ts @@ -64,6 +64,12 @@ import { injectEmailLinkAuthFormSchema, injectTranslation, injectUI } from "../. } `, }) +/** + * A form component for email link authentication. + * + * Sends a sign-in link to the user's email address and automatically completes sign-in + * if the user arrives via an email link. + */ export class EmailLinkAuthFormComponent { private ui = injectUI(); private formSchema = injectEmailLinkAuthFormSchema(); @@ -75,7 +81,9 @@ export class EmailLinkAuthFormComponent { emailSentMessage = injectTranslation("messages", "signInLinkSent"); unknownErrorLabel = injectTranslation("errors", "unknownError"); + /** Event emitter fired when sign-in link email is sent. */ @Output() emailSent = new EventEmitter(); + /** Event emitter for successful sign-in. */ @Output() signIn = new EventEmitter(); form = injectForm({ diff --git a/packages/angular/src/lib/auth/forms/forgot-password-auth-form.ts b/packages/angular/src/lib/auth/forms/forgot-password-auth-form.ts index fa72689eb..40a190f13 100644 --- a/packages/angular/src/lib/auth/forms/forgot-password-auth-form.ts +++ b/packages/angular/src/lib/auth/forms/forgot-password-auth-form.ts @@ -73,6 +73,11 @@ import { injectForgotPasswordAuthFormSchema, injectTranslation, injectUI } from } `, }) +/** + * A form component for requesting a password reset email. + * + * Displays a success message after the email is sent. + */ export class ForgotPasswordAuthFormComponent { private ui = injectUI(); private formSchema = injectForgotPasswordAuthFormSchema(); @@ -85,8 +90,10 @@ export class ForgotPasswordAuthFormComponent { checkEmailForResetMessage = injectTranslation("messages", "checkEmailForReset"); unknownErrorLabel = injectTranslation("errors", "unknownError"); + /** Event emitter for back to sign in action. */ backToSignIn = input>(); + /** Event emitter fired when password reset email is sent. */ @Output() passwordSent = new EventEmitter(); form = injectForm({ diff --git a/packages/angular/src/lib/auth/forms/mfa/sms-multi-factor-assertion-form.ts b/packages/angular/src/lib/auth/forms/mfa/sms-multi-factor-assertion-form.ts index 353b816c7..b45742577 100644 --- a/packages/angular/src/lib/auth/forms/mfa/sms-multi-factor-assertion-form.ts +++ b/packages/angular/src/lib/auth/forms/mfa/sms-multi-factor-assertion-form.ts @@ -63,10 +63,15 @@ type PhoneMultiFactorInfo = MultiFactorInfo & { `, }) +/** + * A form component for requesting SMS verification code during MFA assertion. + */ export class SmsMultiFactorAssertionPhoneFormComponent { private ui = injectUI(); + /** The multi-factor info hint containing phone number details. */ hint = input.required(); + /** Event emitter fired when verification ID is received. */ @Output() onSubmit = new EventEmitter(); sendCodeLabel = injectTranslation("labels", "sendCode"); @@ -164,11 +169,16 @@ export class SmsMultiFactorAssertionPhoneFormComponent { `, }) +/** + * A form component for verifying SMS code during MFA assertion. + */ export class SmsMultiFactorAssertionVerifyFormComponent { private ui = injectUI(); private formSchema = injectMultiFactorPhoneAuthVerifyFormSchema(); + /** The verification ID received from the phone form. */ verificationId = input.required(); + /** Event emitter for successful MFA assertion. */ @Output() onSuccess = new EventEmitter(); verificationCodeLabel = injectTranslation("labels", "verificationCode"); @@ -238,8 +248,15 @@ export class SmsMultiFactorAssertionVerifyFormComponent { `, }) +/** + * A form component for SMS multi-factor authentication assertion. + * + * Manages the flow between requesting and verifying SMS codes for MFA. + */ export class SmsMultiFactorAssertionFormComponent { + /** The multi-factor info hint containing phone number details. */ hint = input.required(); + /** Event emitter for successful MFA assertion. */ @Output() onSuccess = new EventEmitter(); verification = signal<{ verificationId: string } | null>(null); diff --git a/packages/angular/src/lib/auth/forms/mfa/sms-multi-factor-enrollment-form.ts b/packages/angular/src/lib/auth/forms/mfa/sms-multi-factor-enrollment-form.ts index 7f65985ce..6a949b6d0 100644 --- a/packages/angular/src/lib/auth/forms/mfa/sms-multi-factor-enrollment-form.ts +++ b/packages/angular/src/lib/auth/forms/mfa/sms-multi-factor-enrollment-form.ts @@ -111,6 +111,11 @@ import { `, }) +/** + * A form component for SMS multi-factor authentication enrollment. + * + * Manages the flow between phone number entry and verification code entry for MFA enrollment. + */ export class SmsMultiFactorEnrollmentFormComponent { private ui = injectUI(); private phoneFormSchema = injectMultiFactorPhoneAuthNumberFormSchema(); @@ -128,6 +133,7 @@ export class SmsMultiFactorEnrollmentFormComponent { verifyCodeLabel = injectTranslation("labels", "verifyCode"); smsVerificationPrompt = injectTranslation("prompts", "smsVerificationPrompt"); + /** Event emitter fired when MFA enrollment is completed. */ @Output() onEnrollment = new EventEmitter(); recaptchaContainer = viewChild.required>("recaptchaContainer"); diff --git a/packages/angular/src/lib/auth/forms/mfa/totp-multi-factor-assertion-form.ts b/packages/angular/src/lib/auth/forms/mfa/totp-multi-factor-assertion-form.ts index 4471e66da..ecf9190c6 100644 --- a/packages/angular/src/lib/auth/forms/mfa/totp-multi-factor-assertion-form.ts +++ b/packages/angular/src/lib/auth/forms/mfa/totp-multi-factor-assertion-form.ts @@ -58,11 +58,18 @@ import { TotpMultiFactorGenerator, type MultiFactorInfo, type UserCredential } f `, }) +/** + * A form component for TOTP multi-factor authentication assertion. + * + * Allows users to enter a TOTP code from their authenticator app. + */ export class TotpMultiFactorAssertionFormComponent { private ui = injectUI(); private formSchema = injectMultiFactorTotpAuthVerifyFormSchema(); + /** The multi-factor info hint containing TOTP details. */ hint = input.required(); + /** Event emitter for successful MFA assertion. */ @Output() onSuccess = new EventEmitter(); verificationCodeLabel = injectTranslation("labels", "verificationCode"); diff --git a/packages/angular/src/lib/auth/forms/mfa/totp-multi-factor-enrollment-form.ts b/packages/angular/src/lib/auth/forms/mfa/totp-multi-factor-enrollment-form.ts index e96732791..157dde79f 100644 --- a/packages/angular/src/lib/auth/forms/mfa/totp-multi-factor-enrollment-form.ts +++ b/packages/angular/src/lib/auth/forms/mfa/totp-multi-factor-enrollment-form.ts @@ -66,10 +66,14 @@ import { `, }) +/** + * A form component for generating a TOTP secret and display name during MFA enrollment. + */ export class TotpMultiFactorSecretGenerationFormComponent { private ui = injectUI(); private formSchema = injectMultiFactorTotpAuthNumberFormSchema(); + /** Event emitter fired when TOTP secret is generated. */ @Output() onSubmit = new EventEmitter<{ secret: TotpSecret; displayName: string }>(); displayNameLabel = injectTranslation("labels", "displayName"); @@ -150,12 +154,20 @@ export class TotpMultiFactorSecretGenerationFormComponent { `, }) +/** + * A form component for verifying TOTP code during MFA enrollment. + * + * Displays a QR code and allows users to verify their authenticator app setup. + */ export class TotpMultiFactorVerificationFormComponent { private ui = injectUI(); private formSchema = injectMultiFactorTotpAuthVerifyFormSchema(); + /** The TOTP secret generated in the previous step. */ secret = input.required(); + /** The display name for the TOTP factor. */ displayName = input.required(); + /** Event emitter fired when MFA enrollment is completed. */ @Output() onEnrollment = new EventEmitter(); verificationCodeLabel = injectTranslation("labels", "verificationCode"); @@ -224,10 +236,16 @@ export class TotpMultiFactorVerificationFormComponent { `, }) +/** + * A form component for TOTP multi-factor authentication enrollment. + * + * Manages the flow between secret generation and verification for TOTP MFA enrollment. + */ export class TotpMultiFactorEnrollmentFormComponent { private ui = injectUI(); enrollment = signal<{ secret: TotpSecret; displayName: string } | null>(null); + /** Event emitter fired when MFA enrollment is completed. */ @Output() onEnrollment = new EventEmitter(); constructor() { diff --git a/packages/angular/src/lib/auth/forms/multi-factor-auth-assertion-form.ts b/packages/angular/src/lib/auth/forms/multi-factor-auth-assertion-form.ts index 55fcc1b33..b078d2a8e 100644 --- a/packages/angular/src/lib/auth/forms/multi-factor-auth-assertion-form.ts +++ b/packages/angular/src/lib/auth/forms/multi-factor-auth-assertion-form.ts @@ -59,6 +59,11 @@ import { ButtonComponent } from "../../components/button"; `, }) +/** + * A form component for multi-factor authentication assertion. + * + * Allows users to select and complete MFA verification using SMS or TOTP. + */ export class MultiFactorAuthAssertionFormComponent { private ui = injectUI(); @@ -71,6 +76,7 @@ export class MultiFactorAuthAssertionFormComponent { }); } + /** Event emitter for successful MFA assertion. */ @Output() onSuccess = new EventEmitter(); resolver = computed(() => { diff --git a/packages/angular/src/lib/auth/forms/multi-factor-auth-enrollment-form.ts b/packages/angular/src/lib/auth/forms/multi-factor-auth-enrollment-form.ts index 30ee40402..559fae251 100644 --- a/packages/angular/src/lib/auth/forms/multi-factor-auth-enrollment-form.ts +++ b/packages/angular/src/lib/auth/forms/multi-factor-auth-enrollment-form.ts @@ -60,8 +60,15 @@ type Hint = (typeof FactorId)[keyof typeof FactorId]; `, }) +/** + * A form component for multi-factor authentication enrollment. + * + * Allows users to enroll in MFA using SMS or TOTP methods. + */ export class MultiFactorAuthEnrollmentFormComponent implements OnInit { + /** The available MFA factor types for enrollment. */ hints = input([FactorId.TOTP, FactorId.PHONE]); + /** Event emitter fired when MFA enrollment is completed. */ @Output() onEnrollment = new EventEmitter(); selectedHint = signal(undefined); diff --git a/packages/angular/src/lib/auth/forms/phone-auth-form.ts b/packages/angular/src/lib/auth/forms/phone-auth-form.ts index acd988c2c..39c6a77b8 100644 --- a/packages/angular/src/lib/auth/forms/phone-auth-form.ts +++ b/packages/angular/src/lib/auth/forms/phone-auth-form.ts @@ -79,11 +79,16 @@ import { `, }) +/** + * A form component for entering a phone number and requesting a verification code. + */ export class PhoneNumberFormComponent { private ui = injectUI(); private formSchema = injectPhoneAuthFormSchema(); + /** Event emitter fired when phone number is verified and verification ID is received. */ @Output() onSubmit = new EventEmitter<{ verificationId: string; phoneNumber: string }>(); + /** The selected country code for phone number formatting. */ country = signal(countryData[0].code); phoneNumberLabel = injectTranslation("labels", "phoneNumber"); @@ -187,11 +192,16 @@ export class PhoneNumberFormComponent { `, }) +/** + * A form component for entering and verifying the SMS verification code. + */ export class VerificationFormComponent { private ui = injectUI(); private formSchema = injectPhoneAuthVerifyFormSchema(); + /** The verification ID received from the phone number form. */ verificationId = input.required(); + /** Event emitter for successful sign-in. */ @Output() signIn = new EventEmitter(); verificationCodeLabel = injectTranslation("labels", "verificationCode"); @@ -259,8 +269,14 @@ export class VerificationFormComponent { `, }) +/** + * A form component for phone number authentication. + * + * Manages the flow between phone number entry and verification code entry. + */ export class PhoneAuthFormComponent { verificationId = signal(null); + /** Event emitter for successful sign-in. */ @Output() signIn = new EventEmitter(); handlePhoneSubmit(data: { verificationId: string; phoneNumber: string }) { diff --git a/packages/angular/src/lib/auth/forms/sign-in-auth-form.ts b/packages/angular/src/lib/auth/forms/sign-in-auth-form.ts index 94ba33aaa..a88cb3379 100644 --- a/packages/angular/src/lib/auth/forms/sign-in-auth-form.ts +++ b/packages/angular/src/lib/auth/forms/sign-in-auth-form.ts @@ -87,6 +87,9 @@ import { `, }) +/** + * A form component for signing in with email and password. + */ export class SignInAuthFormComponent { private ui = injectUI(); private formSchema = injectSignInAuthFormSchema(); @@ -99,9 +102,12 @@ export class SignInAuthFormComponent { signUpLabel = injectTranslation("labels", "signUp"); unknownErrorLabel = injectTranslation("errors", "unknownError"); + /** Event emitter for forgot password action. */ forgotPassword = input>(); + /** Event emitter for sign up action. */ signUp = input>(); + /** Event emitter for successful sign-in. */ @Output() signIn = new EventEmitter(); form = injectForm({ diff --git a/packages/angular/src/lib/auth/forms/sign-up-auth-form.ts b/packages/angular/src/lib/auth/forms/sign-up-auth-form.ts index 5c3a9d35a..266d2904d 100644 --- a/packages/angular/src/lib/auth/forms/sign-up-auth-form.ts +++ b/packages/angular/src/lib/auth/forms/sign-up-auth-form.ts @@ -78,6 +78,11 @@ import { `, }) +/** + * A form component for signing up with email and password. + * + * Optionally includes a display name field if the requireDisplayName behavior is enabled. + */ export class SignUpAuthFormComponent { private ui = injectUI(); private formSchema = injectSignUpAuthFormSchema(); @@ -94,8 +99,10 @@ export class SignUpAuthFormComponent { signInLabel = injectTranslation("labels", "signIn"); unknownErrorLabel = injectTranslation("errors", "unknownError"); + /** Event emitter for sign in action. */ signIn = input>(); + /** Event emitter for successful sign-up. */ @Output() signUp = new EventEmitter(); form = injectForm({ diff --git a/packages/angular/src/lib/auth/oauth/apple-sign-in-button.ts b/packages/angular/src/lib/auth/oauth/apple-sign-in-button.ts index 8d760d5cf..bdc9e81f0 100644 --- a/packages/angular/src/lib/auth/oauth/apple-sign-in-button.ts +++ b/packages/angular/src/lib/auth/oauth/apple-sign-in-button.ts @@ -35,14 +35,20 @@ import { AppleLogoComponent } from "../../components/logos/apple"; `, }) +/** + * A button component for signing in with Apple. + */ export class AppleSignInButtonComponent { ui = injectUI(); signInWithAppleLabel = injectTranslation("labels", "signInWithApple"); + /** Whether to use themed styling. */ themed = input(false); + /** Event emitter for successful sign-in. */ signIn = output(); private defaultProvider = new OAuthProvider("apple.com"); + /** Optional custom OAuth provider configuration. */ provider = input(); get appleProvider() { diff --git a/packages/angular/src/lib/auth/oauth/facebook-sign-in-button.ts b/packages/angular/src/lib/auth/oauth/facebook-sign-in-button.ts index 9d65c8030..6c2ed949e 100644 --- a/packages/angular/src/lib/auth/oauth/facebook-sign-in-button.ts +++ b/packages/angular/src/lib/auth/oauth/facebook-sign-in-button.ts @@ -35,14 +35,20 @@ import { FacebookLogoComponent } from "../../components/logos/facebook"; `, }) +/** + * A button component for signing in with Facebook. + */ export class FacebookSignInButtonComponent { ui = injectUI(); signInWithFacebookLabel = injectTranslation("labels", "signInWithFacebook"); + /** Whether to use themed styling. */ themed = input(false); + /** Event emitter for successful sign-in. */ signIn = output(); private defaultProvider = new FacebookAuthProvider(); + /** Optional custom OAuth provider configuration. */ provider = input(); get facebookProvider() { diff --git a/packages/angular/src/lib/auth/oauth/github-sign-in-button.ts b/packages/angular/src/lib/auth/oauth/github-sign-in-button.ts index 049d6f54e..3e41e82ed 100644 --- a/packages/angular/src/lib/auth/oauth/github-sign-in-button.ts +++ b/packages/angular/src/lib/auth/oauth/github-sign-in-button.ts @@ -35,13 +35,19 @@ import { GithubLogoComponent } from "../../components/logos/github"; `, }) +/** + * A button component for signing in with GitHub. + */ export class GitHubSignInButtonComponent { signInWithGitHubLabel = injectTranslation("labels", "signInWithGitHub"); + /** Whether to use themed styling. */ themed = input(false); + /** Event emitter for successful sign-in. */ signIn = output(); private defaultProvider = new GithubAuthProvider(); + /** Optional custom OAuth provider configuration. */ provider = input(); get githubProvider() { diff --git a/packages/angular/src/lib/auth/oauth/google-sign-in-button.ts b/packages/angular/src/lib/auth/oauth/google-sign-in-button.ts index 3875734e2..11119857e 100644 --- a/packages/angular/src/lib/auth/oauth/google-sign-in-button.ts +++ b/packages/angular/src/lib/auth/oauth/google-sign-in-button.ts @@ -35,14 +35,20 @@ import { GoogleLogoComponent } from "../../components/logos/google"; `, }) +/** + * A button component for signing in with Google. + */ export class GoogleSignInButtonComponent { ui = injectUI(); signInWithGoogleLabel = injectTranslation("labels", "signInWithGoogle"); + /** Whether to use themed styling. */ themed = input(false); + /** Event emitter for successful sign-in. */ signIn = output(); private defaultProvider = new GoogleAuthProvider(); + /** Optional custom OAuth provider configuration. */ provider = input(); get googleProvider() { diff --git a/packages/angular/src/lib/auth/oauth/microsoft-sign-in-button.ts b/packages/angular/src/lib/auth/oauth/microsoft-sign-in-button.ts index 5472aeca8..d292fcfc9 100644 --- a/packages/angular/src/lib/auth/oauth/microsoft-sign-in-button.ts +++ b/packages/angular/src/lib/auth/oauth/microsoft-sign-in-button.ts @@ -35,13 +35,19 @@ import { MicrosoftLogoComponent } from "../../components/logos/microsoft"; `, }) +/** + * A button component for signing in with Microsoft. + */ export class MicrosoftSignInButtonComponent { signInWithMicrosoftLabel = injectTranslation("labels", "signInWithMicrosoft"); + /** Whether to use themed styling. */ themed = input(false); + /** Event emitter for successful sign-in. */ signIn = output(); private defaultProvider = new OAuthProvider("microsoft.com"); + /** Optional custom OAuth provider configuration. */ provider = input(); get microsoftProvider() { diff --git a/packages/angular/src/lib/auth/oauth/oauth-button.ts b/packages/angular/src/lib/auth/oauth/oauth-button.ts index e0308bc2d..1556808e7 100644 --- a/packages/angular/src/lib/auth/oauth/oauth-button.ts +++ b/packages/angular/src/lib/auth/oauth/oauth-button.ts @@ -49,11 +49,17 @@ import { FirebaseUIError, signInWithProvider, getTranslation } from "@invertase/ `, }) +/** + * A generic OAuth button component for signing in with any OAuth provider. + */ export class OAuthButtonComponent { ui = injectUI(); + /** The OAuth provider to use for sign-in. */ provider = input.required(); + /** Whether to use themed styling. */ themed = input(); error = signal(null); + /** Event emitter for successful sign-in. */ signIn = output(); buttonVariant = computed(() => { diff --git a/packages/angular/src/lib/auth/oauth/twitter-sign-in-button.ts b/packages/angular/src/lib/auth/oauth/twitter-sign-in-button.ts index 56b8471ff..cba57a801 100644 --- a/packages/angular/src/lib/auth/oauth/twitter-sign-in-button.ts +++ b/packages/angular/src/lib/auth/oauth/twitter-sign-in-button.ts @@ -35,13 +35,19 @@ import { TwitterLogoComponent } from "../../components/logos/twitter"; `, }) +/** + * A button component for signing in with Twitter/X. + */ export class TwitterSignInButtonComponent { signInWithTwitterLabel = injectTranslation("labels", "signInWithTwitter"); + /** Whether to use themed styling. */ themed = input(false); + /** Event emitter for successful sign-in. */ signIn = output(); private defaultProvider = new TwitterAuthProvider(); + /** Optional custom OAuth provider configuration. */ provider = input(); get twitterProvider() { diff --git a/packages/angular/src/lib/auth/screens/email-link-auth-screen.ts b/packages/angular/src/lib/auth/screens/email-link-auth-screen.ts index f0a744659..eaf33575f 100644 --- a/packages/angular/src/lib/auth/screens/email-link-auth-screen.ts +++ b/packages/angular/src/lib/auth/screens/email-link-auth-screen.ts @@ -66,6 +66,11 @@ import { User } from "@angular/fire/auth"; } `, }) +/** + * A screen component for email link authentication. + * + * Automatically displays the MFA assertion screen if a multi-factor resolver is present. + */ export class EmailLinkAuthScreenComponent { private ui = injectUI(); @@ -80,6 +85,8 @@ export class EmailLinkAuthScreenComponent { }); } + /** Event emitter fired when sign-in link email is sent. */ @Output() emailSent = new EventEmitter(); + /** Event emitter for successful sign-in. */ @Output() signIn = new EventEmitter(); } diff --git a/packages/angular/src/lib/auth/screens/forgot-password-auth-screen.ts b/packages/angular/src/lib/auth/screens/forgot-password-auth-screen.ts index 93248d871..ed17b083e 100644 --- a/packages/angular/src/lib/auth/screens/forgot-password-auth-screen.ts +++ b/packages/angular/src/lib/auth/screens/forgot-password-auth-screen.ts @@ -55,10 +55,15 @@ import { ForgotPasswordAuthFormComponent } from "../forms/forgot-password-auth-f `, }) +/** + * A screen component for requesting a password reset. + */ export class ForgotPasswordAuthScreenComponent { titleText = injectTranslation("labels", "resetPassword"); subtitleText = injectTranslation("prompts", "enterEmailToReset"); + /** Event emitter fired when password reset email is sent. */ @Output() passwordSent = new EventEmitter(); + /** Event emitter for back to sign in action. */ @Output() backToSignIn = new EventEmitter(); } diff --git a/packages/angular/src/lib/auth/screens/multi-factor-auth-assertion-screen.ts b/packages/angular/src/lib/auth/screens/multi-factor-auth-assertion-screen.ts index 038244804..2a66aed1e 100644 --- a/packages/angular/src/lib/auth/screens/multi-factor-auth-assertion-screen.ts +++ b/packages/angular/src/lib/auth/screens/multi-factor-auth-assertion-screen.ts @@ -56,7 +56,13 @@ import { `, }) +/** + * A screen component for multi-factor authentication assertion. + * + * Displays the MFA assertion form for completing multi-factor verification. + */ export class MultiFactorAuthAssertionScreenComponent { + /** Event emitter for successful MFA assertion. */ @Output() onSuccess = new EventEmitter(); titleText = injectTranslation("labels", "multiFactorAssertion"); diff --git a/packages/angular/src/lib/auth/screens/multi-factor-auth-enrollment-screen.ts b/packages/angular/src/lib/auth/screens/multi-factor-auth-enrollment-screen.ts index 69faaac2a..f759d66b5 100644 --- a/packages/angular/src/lib/auth/screens/multi-factor-auth-enrollment-screen.ts +++ b/packages/angular/src/lib/auth/screens/multi-factor-auth-enrollment-screen.ts @@ -58,8 +58,15 @@ type Hint = (typeof FactorId)[keyof typeof FactorId]; `, }) +/** + * A screen component for multi-factor authentication enrollment. + * + * Displays the MFA enrollment form for setting up multi-factor authentication. + */ export class MultiFactorAuthEnrollmentScreenComponent { + /** The available MFA factor types for enrollment. */ hints = input([FactorId.TOTP, FactorId.PHONE]); + /** Event emitter fired when MFA enrollment is completed. */ @Output() onEnrollment = new EventEmitter(); titleText = injectTranslation("labels", "multiFactorEnrollment"); diff --git a/packages/angular/src/lib/auth/screens/oauth-screen.ts b/packages/angular/src/lib/auth/screens/oauth-screen.ts index 3fa0a9808..d12851600 100644 --- a/packages/angular/src/lib/auth/screens/oauth-screen.ts +++ b/packages/angular/src/lib/auth/screens/oauth-screen.ts @@ -68,6 +68,12 @@ import { type User } from "@angular/fire/auth"; } `, }) +/** + * A screen component for OAuth authentication. + * + * Automatically displays the MFA assertion screen if a multi-factor resolver is present. + * Use this screen to display OAuth sign-in buttons. + */ export class OAuthScreenComponent { private ui = injectUI(); @@ -82,5 +88,6 @@ export class OAuthScreenComponent { }); } + /** Event emitter for successful sign-in. */ @Output() onSignIn = new EventEmitter(); } diff --git a/packages/angular/src/lib/auth/screens/phone-auth-screen.ts b/packages/angular/src/lib/auth/screens/phone-auth-screen.ts index 96a5bde86..392666aac 100644 --- a/packages/angular/src/lib/auth/screens/phone-auth-screen.ts +++ b/packages/angular/src/lib/auth/screens/phone-auth-screen.ts @@ -66,6 +66,11 @@ import { User } from "@angular/fire/auth"; } `, }) +/** + * A screen component for phone number authentication. + * + * Automatically displays the MFA assertion screen if a multi-factor resolver is present. + */ export class PhoneAuthScreenComponent { private ui = injectUI(); @@ -80,5 +85,6 @@ export class PhoneAuthScreenComponent { }); } + /** Event emitter for successful sign-in. */ @Output() signIn = new EventEmitter(); } diff --git a/packages/angular/src/lib/auth/screens/sign-in-auth-screen.ts b/packages/angular/src/lib/auth/screens/sign-in-auth-screen.ts index 14b0dc36d..86cba026b 100644 --- a/packages/angular/src/lib/auth/screens/sign-in-auth-screen.ts +++ b/packages/angular/src/lib/auth/screens/sign-in-auth-screen.ts @@ -66,6 +66,11 @@ import { Auth, authState, User, UserCredential } from "@angular/fire/auth"; } `, }) +/** + * A screen component for email/password sign-in. + * + * Automatically displays the MFA assertion screen if a multi-factor resolver is present. + */ export class SignInAuthScreenComponent { private ui = injectUI(); @@ -79,7 +84,10 @@ export class SignInAuthScreenComponent { }); } + /** Event emitter for forgot password action. */ @Output() forgotPassword = new EventEmitter(); + /** Event emitter for sign up action. */ @Output() signUp = new EventEmitter(); + /** Event emitter for successful sign-in. */ @Output() signIn = new EventEmitter(); } diff --git a/packages/angular/src/lib/auth/screens/sign-up-auth-screen.ts b/packages/angular/src/lib/auth/screens/sign-up-auth-screen.ts index f479917a1..9ceaaab22 100644 --- a/packages/angular/src/lib/auth/screens/sign-up-auth-screen.ts +++ b/packages/angular/src/lib/auth/screens/sign-up-auth-screen.ts @@ -67,6 +67,11 @@ import { } `, }) +/** + * A screen component for email/password sign-up. + * + * Automatically displays the MFA assertion screen if a multi-factor resolver is present. + */ export class SignUpAuthScreenComponent { private ui = injectUI(); @@ -81,6 +86,8 @@ export class SignUpAuthScreenComponent { }); } + /** Event emitter for successful sign-up. */ @Output() signUp = new EventEmitter(); + /** Event emitter for sign in action. */ @Output() signIn = new EventEmitter(); } diff --git a/packages/angular/src/lib/components/button.ts b/packages/angular/src/lib/components/button.ts index dd73008c7..70654bcfd 100644 --- a/packages/angular/src/lib/components/button.ts +++ b/packages/angular/src/lib/components/button.ts @@ -22,7 +22,11 @@ import { buttonVariant, type ButtonVariant } from "@invertase/firebaseui-styles" template: ``, standalone: true, }) +/** + * A customizable button component with multiple variants. + */ export class ButtonComponent { + /** The visual variant of the button. */ variant = input(); @HostBinding("class") diff --git a/packages/angular/src/lib/components/card.ts b/packages/angular/src/lib/components/card.ts index 48fe36cac..02aa8bba9 100644 --- a/packages/angular/src/lib/components/card.ts +++ b/packages/angular/src/lib/components/card.ts @@ -30,6 +30,9 @@ import { CommonModule } from "@angular/common"; `, }) +/** + * A card container component for grouping related content. + */ export class CardComponent {} @Component({ @@ -45,6 +48,9 @@ export class CardComponent {} `, }) +/** + * The header section of a card. + */ export class CardHeaderComponent {} @Component({ @@ -61,6 +67,9 @@ export class CardHeaderComponent {} `, }) +/** + * The title of a card. + */ export class CardTitleComponent {} @Component({ @@ -77,6 +86,9 @@ export class CardTitleComponent {}

`, }) +/** + * The subtitle of a card. + */ export class CardSubtitleComponent {} @Component({ @@ -89,4 +101,7 @@ export class CardSubtitleComponent {} }, template: ` `, }) +/** + * The content section of a card. + */ export class CardContentComponent {} diff --git a/packages/angular/src/lib/components/content.ts b/packages/angular/src/lib/components/content.ts index ce3ddc4b9..b426dea79 100644 --- a/packages/angular/src/lib/components/content.ts +++ b/packages/angular/src/lib/components/content.ts @@ -32,6 +32,9 @@ import { injectTranslation } from "../provider"; `, }) +/** + * A content wrapper component that displays a divider and children content. + */ export class ContentComponent { dividerOrLabel = injectTranslation("messages", "dividerOr"); } diff --git a/packages/angular/src/lib/components/country-selector.ts b/packages/angular/src/lib/components/country-selector.ts index 4ac72704c..72c8969c2 100644 --- a/packages/angular/src/lib/components/country-selector.ts +++ b/packages/angular/src/lib/components/country-selector.ts @@ -47,9 +47,15 @@ import { injectCountries, injectDefaultCountry } from "../provider"; `, }) +/** + * A country selector component for phone number input. + * + * Displays a dropdown with country flags, dial codes, and names for selecting a country. + */ export class CountrySelectorComponent { countries = injectCountries(); defaultCountry = injectDefaultCountry(); + /** The selected country code (two-way binding). */ value = model(); selected = computed(() => { diff --git a/packages/angular/src/lib/components/divider.ts b/packages/angular/src/lib/components/divider.ts index 40b3e43db..63c0ba8d3 100644 --- a/packages/angular/src/lib/components/divider.ts +++ b/packages/angular/src/lib/components/divider.ts @@ -34,6 +34,10 @@ import { CommonModule } from "@angular/common"; `, }) +/** + * A divider component that can display a line or a line with text in the middle. + */ export class DividerComponent { + /** Optional label text to display in the center of the divider. */ label = input(); } diff --git a/packages/angular/src/lib/components/form.ts b/packages/angular/src/lib/components/form.ts index 034cb860c..856a96fa7 100644 --- a/packages/angular/src/lib/components/form.ts +++ b/packages/angular/src/lib/components/form.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { Component, computed, input } from "@angular/core"; import { AnyFieldApi, AnyFormState, injectField } from "@tanstack/angular-form"; import { ButtonComponent } from "./button"; @@ -18,7 +34,11 @@ import { ButtonComponent } from "./button"; } `, }) +/** + * A component that displays form field metadata, such as validation errors. + */ export class FormMetadataComponent { + /** The form field API instance. */ field = input.required(); errors = computed(() => this.field() @@ -60,10 +80,16 @@ export class FormMetadataComponent { `, }) +/** + * A form input component with label, description, and validation support. + */ export class FormInputComponent { field = injectField(); + /** The label text for the input field. */ label = input.required(); + /** The input type (e.g., "text", "email", "password"). */ type = input("text"); + /** Optional description text displayed below the label. */ description = input(); } @@ -76,6 +102,9 @@ export class FormInputComponent { }, template: ` `, }) +/** + * A button component for form actions (e.g., "Forgot Password?" link). + */ export class FormActionComponent {} @Component({ @@ -92,8 +121,15 @@ export class FormActionComponent {} `, }) +/** + * A submit button component for forms. + * + * Automatically disables when the form is submitting. + */ export class FormSubmitComponent { + /** Optional additional CSS classes. */ class = input(); + /** The form state for tracking submission status. */ state = input.required(); isSubmitting = computed(() => this.state().isSubmitting); @@ -113,7 +149,13 @@ export class FormSubmitComponent { } `, }) +/** + * A component that displays form-level error messages. + * + * Shows errors from form submission, not validation errors. + */ export class FormErrorMessageComponent { + /** The form state containing error information. */ state = input.required(); errorMessage = computed(() => { diff --git a/packages/angular/src/lib/components/logos/apple.ts b/packages/angular/src/lib/components/logos/apple.ts index 7e9727b1d..5506447e7 100644 --- a/packages/angular/src/lib/components/logos/apple.ts +++ b/packages/angular/src/lib/components/logos/apple.ts @@ -30,8 +30,14 @@ import { Component, input } from "@angular/core"; `, }) +/** + * The Apple logo SVG component. + */ export class AppleLogoComponent { + /** The width of the logo. */ width = input("1em"); + /** The height of the logo. */ height = input("1em"); + /** Optional additional CSS class names. */ className = input(""); } diff --git a/packages/angular/src/lib/components/logos/facebook.ts b/packages/angular/src/lib/components/logos/facebook.ts index 7812040d3..c022c5db1 100644 --- a/packages/angular/src/lib/components/logos/facebook.ts +++ b/packages/angular/src/lib/components/logos/facebook.ts @@ -30,8 +30,14 @@ import { Component, input } from "@angular/core"; `, }) +/** + * The Facebook logo SVG component. + */ export class FacebookLogoComponent { + /** The width of the logo. */ width = input("1em"); + /** The height of the logo. */ height = input("1em"); + /** Optional additional CSS class names. */ className = input(""); } diff --git a/packages/angular/src/lib/components/logos/github.ts b/packages/angular/src/lib/components/logos/github.ts index fce4b33c6..bd417c8fd 100644 --- a/packages/angular/src/lib/components/logos/github.ts +++ b/packages/angular/src/lib/components/logos/github.ts @@ -29,8 +29,14 @@ import { Component, input } from "@angular/core"; `, }) +/** + * The GitHub logo SVG component. + */ export class GithubLogoComponent { + /** The width of the logo. */ width = input("1em"); + /** The height of the logo. */ height = input("1em"); + /** Optional additional CSS class names. */ className = input(""); } diff --git a/packages/angular/src/lib/components/logos/google.ts b/packages/angular/src/lib/components/logos/google.ts index 8acab00de..805114852 100644 --- a/packages/angular/src/lib/components/logos/google.ts +++ b/packages/angular/src/lib/components/logos/google.ts @@ -42,8 +42,14 @@ import { Component, input } from "@angular/core"; `, }) +/** + * The Google logo SVG component. + */ export class GoogleLogoComponent { + /** The width of the logo. */ width = input("1em"); + /** The height of the logo. */ height = input("1em"); + /** Optional additional CSS class names. */ className = input(""); } diff --git a/packages/angular/src/lib/components/logos/index.ts b/packages/angular/src/lib/components/logos/index.ts index 677873a2b..9385b0a33 100644 --- a/packages/angular/src/lib/components/logos/index.ts +++ b/packages/angular/src/lib/components/logos/index.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + export { AppleLogoComponent } from "./apple"; export { FacebookLogoComponent } from "./facebook"; export { GithubLogoComponent } from "./github"; diff --git a/packages/angular/src/lib/components/logos/line.ts b/packages/angular/src/lib/components/logos/line.ts index d3b98fd3f..ce7c69923 100644 --- a/packages/angular/src/lib/components/logos/line.ts +++ b/packages/angular/src/lib/components/logos/line.ts @@ -34,8 +34,14 @@ import { Component, input } from "@angular/core"; `, }) +/** + * The Line logo SVG component. + */ export class LineLogoComponent { + /** The width of the logo. */ width = input("1em"); + /** The height of the logo. */ height = input("1em"); + /** Optional additional CSS class names. */ className = input(""); } diff --git a/packages/angular/src/lib/components/logos/microsoft.ts b/packages/angular/src/lib/components/logos/microsoft.ts index 88006fafe..c4563b0d8 100644 --- a/packages/angular/src/lib/components/logos/microsoft.ts +++ b/packages/angular/src/lib/components/logos/microsoft.ts @@ -30,8 +30,14 @@ import { Component, input } from "@angular/core"; `, }) +/** + * The Microsoft logo SVG component. + */ export class MicrosoftLogoComponent { + /** The width of the logo. */ width = input("1em"); + /** The height of the logo. */ height = input("1em"); + /** Optional additional CSS class names. */ className = input(""); } diff --git a/packages/angular/src/lib/components/logos/snapchat.ts b/packages/angular/src/lib/components/logos/snapchat.ts index c11ec3c00..0b129fc39 100644 --- a/packages/angular/src/lib/components/logos/snapchat.ts +++ b/packages/angular/src/lib/components/logos/snapchat.ts @@ -30,8 +30,14 @@ import { Component, input } from "@angular/core"; `, }) +/** + * The Snapchat logo SVG component. + */ export class SnapchatLogoComponent { + /** The width of the logo. */ width = input("1em"); + /** The height of the logo. */ height = input("1em"); + /** Optional additional CSS class names. */ className = input(""); } diff --git a/packages/angular/src/lib/components/logos/twitter.ts b/packages/angular/src/lib/components/logos/twitter.ts index 3acbbb71d..9630ea8e6 100644 --- a/packages/angular/src/lib/components/logos/twitter.ts +++ b/packages/angular/src/lib/components/logos/twitter.ts @@ -29,8 +29,14 @@ import { Component, input } from "@angular/core"; `, }) +/** + * The Twitter/X logo SVG component. + */ export class TwitterLogoComponent { + /** The width of the logo. */ width = input("1em"); + /** The height of the logo. */ height = input("1em"); + /** Optional additional CSS class names. */ className = input(""); } diff --git a/packages/angular/src/lib/components/policies.ts b/packages/angular/src/lib/components/policies.ts index b8675b7e1..efe22bc16 100644 --- a/packages/angular/src/lib/components/policies.ts +++ b/packages/angular/src/lib/components/policies.ts @@ -48,6 +48,11 @@ type PolicyPart = } `, }) +/** + * A component that displays terms of service and privacy policy links. + * + * Parses the terms and privacy policy template and renders clickable links. + */ export class PoliciesComponent { private readonly policies = injectPolicies(); diff --git a/packages/angular/src/lib/components/redirect-error.ts b/packages/angular/src/lib/components/redirect-error.ts index 224e5be99..9ef2a9bc7 100644 --- a/packages/angular/src/lib/components/redirect-error.ts +++ b/packages/angular/src/lib/components/redirect-error.ts @@ -30,6 +30,11 @@ import { injectRedirectError } from "../provider"; } `, }) +/** + * A component that displays redirect error messages. + * + * Shows errors that occurred during OAuth redirect flows. + */ export class RedirectErrorComponent { error = injectRedirectError(); } diff --git a/packages/angular/src/lib/provider.ts b/packages/angular/src/lib/provider.ts index 0c1d1b480..41e1f75c9 100644 --- a/packages/angular/src/lib/provider.ts +++ b/packages/angular/src/lib/provider.ts @@ -53,11 +53,22 @@ import { const FIREBASE_UI_STORE = new InjectionToken("firebaseui.store"); const FIREBASE_UI_POLICIES = new InjectionToken("firebaseui.policies"); +/** Configuration for terms of service and privacy policy links. */ type PolicyConfig = { + /** The URL to the terms of service page. */ termsOfServiceUrl: string; + /** The URL to the privacy policy page. */ privacyPolicyUrl: string; }; +/** + * Provides FirebaseUI configuration for the Angular application. + * + * This function must be called in your application's providers array to enable FirebaseUI functionality. + * + * @param uiFactory - Factory function that creates a FirebaseUIStore from Firebase apps. + * @returns Environment providers for FirebaseUI. + */ export function provideFirebaseUI(uiFactory: (apps: FirebaseApps) => FirebaseUIStore): EnvironmentProviders { const providers: Provider[] = [ // TODO: This should depend on the FirebaseAuth provider via deps, @@ -78,12 +89,25 @@ export function provideFirebaseUI(uiFactory: (apps: FirebaseApps) => FirebaseUIS return makeEnvironmentProviders(providers); } +/** + * Provides policy configuration (terms of service and privacy policy URLs) for FirebaseUI components. + * + * @param factory - Factory function that returns the policy configuration. + * @returns Environment providers for FirebaseUI policies. + */ export function provideFirebaseUIPolicies(factory: () => PolicyConfig) { const providers: Provider[] = [{ provide: FIREBASE_UI_POLICIES, useFactory: factory }]; return makeEnvironmentProviders(providers); } +/** + * Injects the FirebaseUI store as a reactive signal. + * + * Returns a readonly signal that updates when the UI state changes. + * + * @returns A readonly signal containing the current FirebaseUI state. + */ export function injectUI() { const store = inject(FIREBASE_UI_STORE); const ui = signal(store.get()); @@ -95,6 +119,13 @@ export function injectUI() { return ui.asReadonly(); } +/** + * Injects a callback that is called when a user is authenticated. + * + * The callback is only triggered for non-anonymous users. + * + * @param onAuthenticated - Callback function called when a user is authenticated. + */ export function injectUserAuthenticated(onAuthenticated: (user: User) => void) { const auth = inject(Auth); const state = authState(auth); @@ -112,6 +143,14 @@ export function injectUserAuthenticated(onAuthenticated: (user: User) => void) { }); } +/** + * Injects a reCAPTCHA verifier for phone authentication. + * + * Automatically renders the reCAPTCHA widget in the provided element when available. + * + * @param element - Function that returns the element reference where reCAPTCHA should be rendered. + * @returns A computed signal containing the reCAPTCHA verifier instance, or null if not available. + */ export function injectRecaptchaVerifier(element: () => ElementRef) { const ui = injectUI(); const platformId = inject(PLATFORM_ID); @@ -137,41 +176,85 @@ export function injectRecaptchaVerifier(element: () => ElementRef getTranslation(ui(), category as any, key as any)); } +/** + * Injects the sign-in authentication form schema as a reactive signal. + * + * @returns A computed signal containing the sign-in form schema. + */ export function injectSignInAuthFormSchema(): Signal> { const ui = injectUI(); return computed(() => createSignInAuthFormSchema(ui())); } +/** + * Injects the sign-up authentication form schema as a reactive signal. + * + * @returns A computed signal containing the sign-up form schema. + */ export function injectSignUpAuthFormSchema(): Signal> { const ui = injectUI(); return computed(() => createSignUpAuthFormSchema(ui())); } +/** + * Injects the forgot password authentication form schema as a reactive signal. + * + * @returns A computed signal containing the forgot password form schema. + */ export function injectForgotPasswordAuthFormSchema(): Signal> { const ui = injectUI(); return computed(() => createForgotPasswordAuthFormSchema(ui())); } +/** + * Injects the email link authentication form schema as a reactive signal. + * + * @returns A computed signal containing the email link auth form schema. + */ export function injectEmailLinkAuthFormSchema(): Signal> { const ui = injectUI(); return computed(() => createEmailLinkAuthFormSchema(ui())); } +/** + * Injects the phone authentication number form schema as a reactive signal. + * + * @returns A computed signal containing the phone auth number form schema. + */ export function injectPhoneAuthFormSchema(): Signal> { const ui = injectUI(); return computed(() => createPhoneAuthNumberFormSchema(ui())); } +/** + * Injects the phone authentication verification form schema as a reactive signal. + * + * @returns A computed signal containing the phone auth verification form schema. + */ export function injectPhoneAuthVerifyFormSchema(): Signal> { const ui = injectUI(); return computed(() => createPhoneAuthVerifyFormSchema(ui())); } +/** + * Injects the multi-factor phone authentication number form schema as a reactive signal. + * + * @returns A computed signal containing the MFA phone auth number form schema. + */ export function injectMultiFactorPhoneAuthNumberFormSchema(): Signal< ReturnType > { @@ -179,6 +262,11 @@ export function injectMultiFactorPhoneAuthNumberFormSchema(): Signal< return computed(() => createMultiFactorPhoneAuthNumberFormSchema(ui())); } +/** + * Injects the multi-factor phone authentication assertion form schema as a reactive signal. + * + * @returns A computed signal containing the MFA phone auth assertion form schema. + */ export function injectMultiFactorPhoneAuthAssertionFormSchema(): Signal< ReturnType > { @@ -186,6 +274,11 @@ export function injectMultiFactorPhoneAuthAssertionFormSchema(): Signal< return computed(() => createMultiFactorPhoneAuthAssertionFormSchema(ui())); } +/** + * Injects the multi-factor phone authentication verification form schema as a reactive signal. + * + * @returns A computed signal containing the MFA phone auth verification form schema. + */ export function injectMultiFactorPhoneAuthVerifyFormSchema(): Signal< ReturnType > { @@ -193,6 +286,11 @@ export function injectMultiFactorPhoneAuthVerifyFormSchema(): Signal< return computed(() => createMultiFactorPhoneAuthVerifyFormSchema(ui())); } +/** + * Injects the multi-factor TOTP authentication number form schema as a reactive signal. + * + * @returns A computed signal containing the MFA TOTP auth number form schema. + */ export function injectMultiFactorTotpAuthNumberFormSchema(): Signal< ReturnType > { @@ -200,6 +298,11 @@ export function injectMultiFactorTotpAuthNumberFormSchema(): Signal< return computed(() => createMultiFactorTotpAuthNumberFormSchema(ui())); } +/** + * Injects the multi-factor TOTP authentication verification form schema as a reactive signal. + * + * @returns A computed signal containing the MFA TOTP auth verification form schema. + */ export function injectMultiFactorTotpAuthVerifyFormSchema(): Signal< ReturnType > { @@ -207,20 +310,42 @@ export function injectMultiFactorTotpAuthVerifyFormSchema(): Signal< return computed(() => createMultiFactorTotpAuthVerifyFormSchema(ui())); } +/** + * Injects the policy configuration (terms of service and privacy policy URLs). + * + * @returns The policy configuration, or null if not provided. + */ export function injectPolicies(): PolicyConfig | null { return inject(FIREBASE_UI_POLICIES, { optional: true }); } +/** + * Injects the list of allowed countries for phone authentication as a reactive signal. + * + * @returns A computed signal containing the array of allowed country data. + */ export function injectCountries(): Signal { const ui = injectUI(); return computed(() => getBehavior(ui(), "countryCodes")().allowedCountries); } +/** + * Injects the default country for phone authentication as a reactive signal. + * + * @returns A computed signal containing the default country data. + */ export function injectDefaultCountry(): Signal { const ui = injectUI(); return computed(() => getBehavior(ui(), "countryCodes")().defaultCountry); } +/** + * Injects the redirect error message as a reactive signal. + * + * Returns the error message if a redirect error occurred, undefined otherwise. + * + * @returns A computed signal containing the redirect error message, or undefined if no error. + */ export function injectRedirectError(): Signal { const ui = injectUI(); return computed(() => { diff --git a/packages/angular/src/lib/tests/test-helpers.ts b/packages/angular/src/lib/tests/test-helpers.ts index f606320d8..8b8dd83cb 100644 --- a/packages/angular/src/lib/tests/test-helpers.ts +++ b/packages/angular/src/lib/tests/test-helpers.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + // Mock implementations for @invertase/firebaseui-core to avoid ESM issues in tests export const sendPasswordResetEmail = jest.fn(); export const sendSignInLinkToEmail = jest.fn(); diff --git a/packages/core/src/auth.test.ts b/packages/core/src/auth.test.ts index 07908689a..692757355 100644 --- a/packages/core/src/auth.test.ts +++ b/packages/core/src/auth.test.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { signInWithEmailAndPassword, diff --git a/packages/core/src/auth.ts b/packages/core/src/auth.ts index c8ab743cf..60b0bf08f 100644 --- a/packages/core/src/auth.ts +++ b/packages/core/src/auth.ts @@ -64,6 +64,16 @@ function setPendingState(ui: FirebaseUI) { ui.setState("pending"); } +/** + * Signs in with an email and password. + * + * If the `autoUpgradeAnonymousUsers` behavior is enabled, it will attempt to upgrade an anonymous user to a regular user. + * + * @param ui - The FirebaseUI instance. + * @param email - The email to sign in with. + * @param password - The password to sign in with. + * @returns {Promise} A promise containing the user credential. + */ export async function signInWithEmailAndPassword( ui: FirebaseUI, email: string, @@ -90,6 +100,18 @@ export async function signInWithEmailAndPassword( } } +/** + * Creates a new user account with an email and password. + * + * If the `requireDisplayName` behavior is enabled, a display name must be provided. + * If the `autoUpgradeAnonymousUsers` behavior is enabled, it will attempt to upgrade an anonymous user to a regular user. + * + * @param ui - The FirebaseUI instance. + * @param email - The email address for the new account. + * @param password - The password for the new account. + * @param displayName - Optional display name for the user. + * @returns {Promise} A promise containing the user credential. + */ export async function createUserWithEmailAndPassword( ui: FirebaseUI, email: string, @@ -130,6 +152,18 @@ export async function createUserWithEmailAndPassword( } } +/** + * Verifies a phone number for authentication. + * + * Supports regular phone authentication, MFA enrollment, and MFA assertion flows. + * + * @param ui - The FirebaseUI instance. + * @param phoneNumber - The phone number to verify. + * @param appVerifier - The application verifier (reCAPTCHA). + * @param mfaUser - Optional multi-factor user for MFA enrollment flow. + * @param mfaHint - Optional multi-factor info hint for MFA assertion flow. + * @returns {Promise} A promise containing the verification ID. + */ export async function verifyPhoneNumber( ui: FirebaseUI, phoneNumber: string, @@ -171,6 +205,16 @@ export async function verifyPhoneNumber( } } +/** + * Confirms a phone number verification code and signs in the user. + * + * If the `autoUpgradeAnonymousUsers` behavior is enabled and the current user is anonymous, it will attempt to upgrade the anonymous user to a regular user. + * + * @param ui - The FirebaseUI instance. + * @param verificationId - The verification ID from the phone verification process. + * @param verificationCode - The verification code sent to the phone. + * @returns {Promise} A promise containing the user credential. + */ export async function confirmPhoneNumber( ui: FirebaseUI, verificationId: string, @@ -198,6 +242,13 @@ export async function confirmPhoneNumber( } } +/** + * Sends a password reset email to the specified email address. + * + * @param ui - The FirebaseUI instance. + * @param email - The email address to send the password reset email to. + * @returns {Promise} A promise that resolves when the email is sent. + */ export async function sendPasswordResetEmail(ui: FirebaseUI, email: string): Promise { try { setPendingState(ui); @@ -209,6 +260,15 @@ export async function sendPasswordResetEmail(ui: FirebaseUI, email: string): Pro } } +/** + * Sends a sign-in link to the specified email address. + * + * The email address is stored in localStorage for later use during the sign-in process. + * + * @param ui - The FirebaseUI instance. + * @param email - The email address to send the sign-in link to. + * @returns {Promise} A promise that resolves when the email is sent. + */ export async function sendSignInLinkToEmail(ui: FirebaseUI, email: string): Promise { try { setPendingState(ui); @@ -228,11 +288,28 @@ export async function sendSignInLinkToEmail(ui: FirebaseUI, email: string): Prom } } +/** + * Signs in a user using an email link. + * + * @param ui - The FirebaseUI instance. + * @param email - The email address associated with the sign-in link. + * @param link - The sign-in link from the email. + * @returns {Promise} A promise containing the user credential. + */ export async function signInWithEmailLink(ui: FirebaseUI, email: string, link: string): Promise { const credential = EmailAuthProvider.credentialWithLink(email, link); return signInWithCredential(ui, credential); } +/** + * Signs in a user with an authentication credential. + * + * If the `autoUpgradeAnonymousUsers` behavior is enabled, it will attempt to upgrade an anonymous user to a regular user. + * + * @param ui - The FirebaseUI instance. + * @param credential - The authentication credential to sign in with. + * @returns {Promise} A promise containing the user credential. + */ export async function signInWithCredential(ui: FirebaseUI, credential: AuthCredential): Promise { try { setPendingState(ui); @@ -255,6 +332,13 @@ export async function signInWithCredential(ui: FirebaseUI, credential: AuthCrede } } +/** + * Signs in a user with a custom token. + * + * @param ui - The FirebaseUI instance. + * @param customToken - The custom token to sign in with. + * @returns {Promise} A promise containing the user credential. + */ export async function signInWithCustomToken(ui: FirebaseUI, customToken: string): Promise { try { setPendingState(ui); @@ -267,6 +351,12 @@ export async function signInWithCustomToken(ui: FirebaseUI, customToken: string) } } +/** + * Signs in a user anonymously. + * + * @param ui - The FirebaseUI instance. + * @returns {Promise} A promise containing the user credential. + */ export async function signInAnonymously(ui: FirebaseUI): Promise { try { setPendingState(ui); @@ -279,6 +369,16 @@ export async function signInAnonymously(ui: FirebaseUI): Promise } } +/** + * Signs in a user with an authentication provider (e.g., Google, Facebook, etc.). + * + * If the `autoUpgradeAnonymousProvider` behavior is enabled, it will attempt to upgrade an anonymous user to a regular user. + * The sign-in strategy (popup or redirect) is determined by the `providerSignInStrategy` behavior. + * + * @param ui - The FirebaseUI instance. + * @param provider - The authentication provider to sign in with. + * @returns {Promise} A promise containing the user credential, or never if using redirect strategy. + */ export async function signInWithProvider(ui: FirebaseUI, provider: AuthProvider): Promise { try { setPendingState(ui); @@ -305,6 +405,16 @@ export async function signInWithProvider(ui: FirebaseUI, provider: AuthProvider) } } +/** + * Completes the email link sign-in process using the current URL. + * + * Checks if the current URL is a valid email link sign-in URL and retrieves the email from localStorage. + * Returns null if the URL is not a valid email link or if no email is found in localStorage. + * + * @param ui - The FirebaseUI instance. + * @param currentUrl - The current URL to check for email link sign-in. + * @returns {Promise} A promise containing the user credential, or null if the sign-in cannot be completed. + */ export async function completeEmailLinkSignIn(ui: FirebaseUI, currentUrl: string): Promise { try { if (!_isSignInWithEmailLink(ui.auth, currentUrl)) { @@ -322,6 +432,18 @@ export async function completeEmailLinkSignIn(ui: FirebaseUI, currentUrl: string } } +/** + * Generates a QR code data URL for TOTP (Time-based One-Time Password) multi-factor authentication. + * + * The QR code can be scanned by an authenticator app to set up TOTP MFA for the user. + * + * @param ui - The FirebaseUI instance. + * @param secret - The TOTP secret to generate the QR code for. + * @param accountName - Optional account name for the QR code. Defaults to the user's email if not provided. + * @param issuer - Optional issuer name for the QR code. + * @returns {string} A data URL containing the QR code image. + * @throws {Error} Throws an error if the user is not authenticated. + */ export function generateTotpQrCode(ui: FirebaseUI, secret: TotpSecret, accountName?: string, issuer?: string): string { const currentUser = ui.auth.currentUser; @@ -337,6 +459,15 @@ export function generateTotpQrCode(ui: FirebaseUI, secret: TotpSecret, accountNa return qr.createDataURL(); } +/** + * Signs in a user using a multi-factor assertion. + * + * Resolves the multi-factor challenge using the provided assertion and clears the multi-factor resolver from the UI state. + * + * @param ui - The FirebaseUI instance. + * @param assertion - The multi-factor assertion to use for sign-in. + * @returns {Promise} A promise containing the user credential. + */ export async function signInWithMultiFactorAssertion(ui: FirebaseUI, assertion: MultiFactorAssertion) { try { setPendingState(ui); @@ -350,6 +481,14 @@ export async function signInWithMultiFactorAssertion(ui: FirebaseUI, assertion: } } +/** + * Enrolls a multi-factor authentication method for the current user. + * + * @param ui - The FirebaseUI instance. + * @param assertion - The multi-factor assertion to enroll. + * @param displayName - Optional display name for the enrolled MFA method. + * @returns {Promise} A promise that resolves when the enrollment is complete. + */ export async function enrollWithMultiFactorAssertion( ui: FirebaseUI, assertion: MultiFactorAssertion, @@ -365,6 +504,12 @@ export async function enrollWithMultiFactorAssertion( } } +/** + * Generates a TOTP (Time-based One-Time Password) secret for multi-factor authentication enrollment. + * + * @param ui - The FirebaseUI instance. + * @returns {Promise} A promise containing the TOTP secret. + */ export async function generateTotpSecret(ui: FirebaseUI): Promise { try { setPendingState(ui); diff --git a/packages/core/src/behaviors/anonymous-upgrade.test.ts b/packages/core/src/behaviors/anonymous-upgrade.test.ts index a8e6fdaee..e551a25ad 100644 --- a/packages/core/src/behaviors/anonymous-upgrade.test.ts +++ b/packages/core/src/behaviors/anonymous-upgrade.test.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { describe, it, expect, vi, beforeEach } from "vitest"; import { Auth, diff --git a/packages/core/src/behaviors/anonymous-upgrade.ts b/packages/core/src/behaviors/anonymous-upgrade.ts index ce534de17..1f93fc311 100644 --- a/packages/core/src/behaviors/anonymous-upgrade.ts +++ b/packages/core/src/behaviors/anonymous-upgrade.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { type AuthCredential, type AuthProvider, linkWithCredential, type UserCredential } from "firebase/auth"; import { type FirebaseUI } from "~/config"; import { getBehavior } from "~/behaviors"; diff --git a/packages/core/src/behaviors/auto-anonymous-login.test.ts b/packages/core/src/behaviors/auto-anonymous-login.test.ts index 9dd033a96..c2648a284 100644 --- a/packages/core/src/behaviors/auto-anonymous-login.test.ts +++ b/packages/core/src/behaviors/auto-anonymous-login.test.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { describe, it, expect, vi, beforeEach } from "vitest"; import { Auth, signInAnonymously, User } from "firebase/auth"; import { autoAnonymousLoginHandler } from "./auto-anonymous-login"; diff --git a/packages/core/src/behaviors/auto-anonymous-login.ts b/packages/core/src/behaviors/auto-anonymous-login.ts index 8d625f383..f2bf23cff 100644 --- a/packages/core/src/behaviors/auto-anonymous-login.ts +++ b/packages/core/src/behaviors/auto-anonymous-login.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { signInAnonymously } from "firebase/auth"; import { type InitHandler } from "./utils"; diff --git a/packages/core/src/behaviors/country-codes.test.ts b/packages/core/src/behaviors/country-codes.test.ts index b348b0556..b3c5cce8a 100644 --- a/packages/core/src/behaviors/country-codes.test.ts +++ b/packages/core/src/behaviors/country-codes.test.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { describe, it, expect, vi } from "vitest"; import { countryCodesHandler, CountryCodesOptions } from "./country-codes"; import { countryData } from "../country-data"; diff --git a/packages/core/src/behaviors/country-codes.ts b/packages/core/src/behaviors/country-codes.ts index 4c3e503aa..a4965f621 100644 --- a/packages/core/src/behaviors/country-codes.ts +++ b/packages/core/src/behaviors/country-codes.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { type CountryCode, countryData } from "../country-data"; export type CountryCodesOptions = { diff --git a/packages/core/src/behaviors/index.test.ts b/packages/core/src/behaviors/index.test.ts index 305b289d1..0994558eb 100644 --- a/packages/core/src/behaviors/index.test.ts +++ b/packages/core/src/behaviors/index.test.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { describe, it, expect, vi, beforeEach } from "vitest"; import { createMockUI } from "~/tests/utils"; import { diff --git a/packages/core/src/behaviors/index.ts b/packages/core/src/behaviors/index.ts index 417fa0564..5841d41e5 100644 --- a/packages/core/src/behaviors/index.ts +++ b/packages/core/src/behaviors/index.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import type { FirebaseUI } from "~/config"; import type { RecaptchaVerifier, UserCredential } from "firebase/auth"; import * as anonymousUpgradeHandlers from "./anonymous-upgrade"; @@ -37,19 +53,37 @@ type Registry = { countryCodes: CallableBehavior; }; +/** A behavior or set of behaviors from the registry. */ export type Behavior = Pick; +/** All available behaviors, with each behavior being optional. */ export type Behaviors = Partial; +/** + * Enables automatic anonymous login when the app initializes. + * + * @returns A behavior that automatically signs in users anonymously on app initialization. + */ export function autoAnonymousLogin(): Behavior<"autoAnonymousLogin"> { return { autoAnonymousLogin: initBehavior(autoAnonymousLoginHandlers.autoAnonymousLoginHandler), }; } +/** Options for the auto-upgrade anonymous users behavior. */ export type AutoUpgradeAnonymousUsersOptions = { + /** Optional callback function that is called when an anonymous user is upgraded. */ onUpgrade?: anonymousUpgradeHandlers.OnUpgradeCallback; }; +/** + * Automatically upgrades anonymous users to regular users when they sign in with a credential or provider. + * + * This behavior handles upgrading anonymous users for credential-based sign-ins, provider-based sign-ins, + * and redirect-based authentication flows. + * + * @param options - Optional configuration including an upgrade callback. + * @returns Behaviors for automatically upgrading anonymous users. + */ export function autoUpgradeAnonymousUsers( options?: AutoUpgradeAnonymousUsersOptions ): Behavior< @@ -68,8 +102,15 @@ export function autoUpgradeAnonymousUsers( }; } +/** Options for reCAPTCHA verification behavior. */ export type RecaptchaVerificationOptions = recaptchaHandlers.RecaptchaVerificationOptions; +/** + * Configures reCAPTCHA verification for phone authentication. + * + * @param options - Optional reCAPTCHA verification options. + * @returns A behavior that handles reCAPTCHA verification for phone authentication. + */ export function recaptchaVerification(options?: RecaptchaVerificationOptions): Behavior<"recaptchaVerification"> { return { recaptchaVerification: callableBehavior((ui, element) => @@ -78,6 +119,11 @@ export function recaptchaVerification(options?: RecaptchaVerificationOptions): B }; } +/** + * Configures provider authentication to use redirect strategy instead of popup. + * + * @returns Behaviors for provider sign-in and linking using redirect strategy. + */ export function providerRedirectStrategy(): Behavior<"providerSignInStrategy" | "providerLinkStrategy"> { return { providerSignInStrategy: callableBehavior(providerStrategyHandlers.signInWithRediectHandler), @@ -85,6 +131,13 @@ export function providerRedirectStrategy(): Behavior<"providerSignInStrategy" | }; } +/** + * Configures provider authentication to use popup strategy instead of redirect. + * + * This is the default strategy for provider authentication. + * + * @returns Behaviors for provider sign-in and linking using popup strategy. + */ export function providerPopupStrategy(): Behavior<"providerSignInStrategy" | "providerLinkStrategy"> { return { providerSignInStrategy: callableBehavior(providerStrategyHandlers.signInWithPopupHandler), @@ -92,30 +145,63 @@ export function providerPopupStrategy(): Behavior<"providerSignInStrategy" | "pr }; } +/** Options for Google One Tap sign-in behavior. */ export type OneTapSignInOptions = oneTapSignInHandlers.OneTapSignInOptions; +/** + * Enables Google One Tap sign-in functionality. + * + * @param options - Configuration options for Google One Tap sign-in. + * @returns A behavior that handles Google One Tap sign-in initialization. + */ export function oneTapSignIn(options: OneTapSignInOptions): Behavior<"oneTapSignIn"> { return { oneTapSignIn: initBehavior((ui) => oneTapSignInHandlers.oneTapSignInHandler(ui, options)), }; } +/** + * Requires users to provide a display name when creating an account. + * + * @returns A behavior that enforces display name requirement during user registration. + */ export function requireDisplayName(): Behavior<"requireDisplayName"> { return { requireDisplayName: callableBehavior(requireDisplayNameHandlers.requireDisplayNameHandler), }; } +/** + * Configures country code selection for phone number input. + * + * @param options - Optional configuration for country code behavior. + * @returns A behavior that provides country code functionality for phone authentication. + */ export function countryCodes(options?: countryCodesHandlers.CountryCodesOptions): Behavior<"countryCodes"> { return { countryCodes: callableBehavior(() => countryCodesHandlers.countryCodesHandler(options)), }; } +/** + * Checks if a specific behavior is enabled for the given FirebaseUI instance. + * + * @param ui - The FirebaseUI instance. + * @param key - The behavior key to check. + * @returns True if the behavior is enabled, false otherwise. + */ export function hasBehavior(ui: FirebaseUI, key: T): boolean { return !!ui.behaviors[key]; } +/** + * Gets the handler function for a specific behavior. + * + * @param ui - The FirebaseUI instance. + * @param key - The behavior key to retrieve. + * @returns The handler function for the specified behavior. + * @throws {Error} Throws an error if the behavior is not found. + */ export function getBehavior(ui: FirebaseUI, key: T): Registry[T]["handler"] { if (!hasBehavior(ui, key)) { throw new Error(`Behavior ${key} not found`); @@ -124,6 +210,7 @@ export function getBehavior(ui: FirebaseUI, key: T): R return (ui.behaviors[key] as Registry[T]).handler; } +/** Default behaviors that are enabled by default for all FirebaseUI instances. */ export const defaultBehaviors: Behavior<"recaptchaVerification"> = { ...recaptchaVerification(), ...providerPopupStrategy(), diff --git a/packages/core/src/behaviors/one-tap.test.ts b/packages/core/src/behaviors/one-tap.test.ts index 43a6c7de6..6017b28b7 100644 --- a/packages/core/src/behaviors/one-tap.test.ts +++ b/packages/core/src/behaviors/one-tap.test.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { Auth, User } from "firebase/auth"; import { oneTapSignInHandler, type OneTapSignInOptions } from "./one-tap"; diff --git a/packages/core/src/behaviors/one-tap.ts b/packages/core/src/behaviors/one-tap.ts index 5154a4f79..93d608f38 100644 --- a/packages/core/src/behaviors/one-tap.ts +++ b/packages/core/src/behaviors/one-tap.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { GoogleAuthProvider } from "firebase/auth"; import type { IdConfiguration } from "google-one-tap"; import type { FirebaseUI } from "~/config"; diff --git a/packages/core/src/behaviors/provider-strategy.test.ts b/packages/core/src/behaviors/provider-strategy.test.ts index 9eda32cb1..08a158687 100644 --- a/packages/core/src/behaviors/provider-strategy.test.ts +++ b/packages/core/src/behaviors/provider-strategy.test.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { describe, it, expect, vi, beforeEach } from "vitest"; import { Auth, diff --git a/packages/core/src/behaviors/provider-strategy.ts b/packages/core/src/behaviors/provider-strategy.ts index 32c395ac6..a0f02ab9c 100644 --- a/packages/core/src/behaviors/provider-strategy.ts +++ b/packages/core/src/behaviors/provider-strategy.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { type AuthProvider, linkWithPopup, diff --git a/packages/core/src/behaviors/recaptcha.test.ts b/packages/core/src/behaviors/recaptcha.test.ts index 08fce46f2..543e176bc 100644 --- a/packages/core/src/behaviors/recaptcha.test.ts +++ b/packages/core/src/behaviors/recaptcha.test.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { describe, it, expect, vi, beforeEach } from "vitest"; import { RecaptchaVerifier } from "firebase/auth"; import { recaptchaVerificationHandler, type RecaptchaVerificationOptions } from "./recaptcha"; diff --git a/packages/core/src/behaviors/recaptcha.ts b/packages/core/src/behaviors/recaptcha.ts index 707070ee2..206b0087e 100644 --- a/packages/core/src/behaviors/recaptcha.ts +++ b/packages/core/src/behaviors/recaptcha.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { RecaptchaVerifier } from "firebase/auth"; import { type FirebaseUI } from "~/config"; diff --git a/packages/core/src/behaviors/require-display-name.test.ts b/packages/core/src/behaviors/require-display-name.test.ts index e9134de0f..8e0b207b7 100644 --- a/packages/core/src/behaviors/require-display-name.test.ts +++ b/packages/core/src/behaviors/require-display-name.test.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { describe, it, expect, vi, beforeEach } from "vitest"; import { User } from "firebase/auth"; import { requireDisplayNameHandler } from "./require-display-name"; diff --git a/packages/core/src/behaviors/require-display-name.ts b/packages/core/src/behaviors/require-display-name.ts index 1402d1a48..d40ad177d 100644 --- a/packages/core/src/behaviors/require-display-name.ts +++ b/packages/core/src/behaviors/require-display-name.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { updateProfile, type User } from "firebase/auth"; import { type FirebaseUI } from "~/config"; diff --git a/packages/core/src/behaviors/utils.test.ts b/packages/core/src/behaviors/utils.test.ts index abfc34c0a..f37afc51f 100644 --- a/packages/core/src/behaviors/utils.test.ts +++ b/packages/core/src/behaviors/utils.test.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { describe, it, expect, vi, beforeEach } from "vitest"; import { callableBehavior, diff --git a/packages/core/src/behaviors/utils.ts b/packages/core/src/behaviors/utils.ts index 7d332e7a7..6f30e39c6 100644 --- a/packages/core/src/behaviors/utils.ts +++ b/packages/core/src/behaviors/utils.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import type { UserCredential } from "firebase/auth"; import type { FirebaseUI } from "~/config"; diff --git a/packages/core/src/config.test.ts b/packages/core/src/config.test.ts index 1312c8ca9..84bb48859 100644 --- a/packages/core/src/config.test.ts +++ b/packages/core/src/config.test.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { FirebaseApp } from "firebase/app"; import { Auth, MultiFactorResolver } from "firebase/auth"; import { describe, it, expect, vi, beforeEach } from "vitest"; diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts index 43655865c..b342c03a5 100644 --- a/packages/core/src/config.ts +++ b/packages/core/src/config.ts @@ -23,32 +23,82 @@ import type { InitBehavior, RedirectBehavior } from "./behaviors/utils"; import { type FirebaseUIState } from "./state"; import { handleFirebaseError } from "./errors"; +/** + * Configuration options for initializing FirebaseUI. + */ export type FirebaseUIOptions = { + /** A required Firebase App instance, e.g. from `initializeApp`. */ app: FirebaseApp; + /** An optional Firebase Auth instance, e.g. from `getAuth`. If not provided, it will be created using the app instance. */ auth?: Auth; + /** A default locale to use. Defaults to `enUs`. */ locale?: RegisteredLocale; + /** An optional array of behaviors, e.g. from `requireDisplayName`. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any behaviors?: Behavior[]; }; +/** + * The main FirebaseUI instance that provides access to Firebase Auth and UI state management. + * + * This type encapsulates all the necessary components for managing authentication UI state, + * including Firebase app and auth instances, locale settings, behaviors, and multi-factor + * authentication state. + */ export type FirebaseUI = { + /** The Firebase App instance. */ app: FirebaseApp; + /** The Firebase Auth instance. */ auth: Auth; + /** Sets the locale for translations. */ setLocale: (locale: RegisteredLocale) => void; + /** The current UI state (e.g., "idle", "pending", "loading"). */ state: FirebaseUIState; + /** Sets the UI state. */ setState: (state: FirebaseUIState) => void; + /** The current locale for translations. */ locale: RegisteredLocale; + /** The configured behaviors that customize authentication flows. */ behaviors: Behaviors; + /** The multi-factor resolver, if a multi-factor challenge is in progress. */ multiFactorResolver?: MultiFactorResolver; + /** Sets the multi-factor resolver. */ setMultiFactorResolver: (multiFactorResolver?: MultiFactorResolver) => void; + /** Any error that occurred during a redirect-based authentication flow. */ redirectError?: Error; + /** Sets the redirect error. */ setRedirectError: (error?: Error) => void; }; export const $config = map>>({}); +/** + * A reactive store containing a FirebaseUI instance. + * + * This store allows for reactive updates to the FirebaseUI state, enabling UI components + * to automatically update when the authentication state or configuration changes. + */ export type FirebaseUIStore = DeepMapStore; +/** + * Initializes a FirebaseUI instance with the provided configuration. + * + * Creates a reactive store containing the FirebaseUI instance, sets up behaviors, + * and handles initialization and redirect flows if running client-side. + * + * Example: + * ```typescript + * const ui = initializeUI({ + * app: firebaseApp, + * locale: enUs, + * behaviors: [requireDisplayName()], + * }); + * ``` + * + * @param config - The configuration options for FirebaseUI. + * @param name - Optional name for the FirebaseUI instance. Defaults to "[DEFAULT]". + * @returns {FirebaseUIStore} A reactive store containing the initialized FirebaseUI instance. + */ export function initializeUI(config: FirebaseUIOptions, name: string = "[DEFAULT]"): FirebaseUIStore { // Reduce the behaviors to a single object. const behaviors = config.behaviors?.reduce((acc, behavior) => { diff --git a/packages/core/src/country-data.test.ts b/packages/core/src/country-data.test.ts index d13bc7c8f..6a23cb81f 100644 --- a/packages/core/src/country-data.test.ts +++ b/packages/core/src/country-data.test.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { describe, it, expect } from "vitest"; import { countryData, formatPhoneNumber, CountryData, CountryCode } from "./country-data"; diff --git a/packages/core/src/country-data.ts b/packages/core/src/country-data.ts index 20a563473..ddc4915a8 100644 --- a/packages/core/src/country-data.ts +++ b/packages/core/src/country-data.ts @@ -16,6 +16,9 @@ import { formatIncompletePhoneNumber, parsePhoneNumberWithError, type CountryCode } from "libphonenumber-js"; +/** + * An array of country data objects containing name, dial code, country code, and emoji for all supported countries. + */ export const countryData = [ { name: "Afghanistan", dialCode: "+93", code: "AF", emoji: "🇦🇫" }, { name: "Albania", dialCode: "+355", code: "AL", emoji: "🇦🇱" }, @@ -265,15 +268,29 @@ export const countryData = [ { name: "Åland Islands", dialCode: "+358", code: "AX", emoji: "🇦🇽" }, ] as const satisfies CountryData[]; +/** + * A country data object containing name, dial code, country code, and emoji for a supported country. + */ export type CountryData = { + /** The name of the country. */ name: string; + /** The dial code of the country. */ dialCode: string; + /** The country code of the country. */ code: CountryCode; + /** The emoji of the country. */ emoji: string; }; export type { CountryCode }; +/** + * Formats a phone number according to the specified country data. + * + * @param phoneNumber - The phone number to format. + * @param countryData - The country data to use for formatting. + * @returns {string} The formatted phone number in E164 format. + */ export function formatPhoneNumber(phoneNumber: string, countryData: CountryData): string { try { const parsedNumber = parsePhoneNumberWithError(phoneNumber, countryData.code); diff --git a/packages/core/src/errors.test.ts b/packages/core/src/errors.test.ts index a4226ea2b..c59f5b264 100644 --- a/packages/core/src/errors.test.ts +++ b/packages/core/src/errors.test.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { describe, it, expect, vi, beforeEach } from "vitest"; import { FirebaseError } from "firebase/app"; import { Auth, AuthCredential, MultiFactorResolver } from "firebase/auth"; diff --git a/packages/core/src/errors.ts b/packages/core/src/errors.ts index 782f2191d..ec1fe1e7c 100644 --- a/packages/core/src/errors.ts +++ b/packages/core/src/errors.ts @@ -19,6 +19,10 @@ import { FirebaseError } from "firebase/app"; import { type AuthCredential, getMultiFactorResolver, type MultiFactorError } from "firebase/auth"; import { type FirebaseUI } from "./config"; import { getTranslation } from "./translations"; + +/** + * A custom error class that extends FirebaseError and provides a translated error message based on the configured locale. + */ export class FirebaseUIError extends FirebaseError { constructor(ui: FirebaseUI, error: FirebaseError) { const message = getTranslation(ui, "errors", ERROR_CODE_MAP[error.code as ErrorCode]); @@ -29,6 +33,17 @@ export class FirebaseUIError extends FirebaseError { } } +/** + * Handles a Firebase error and throws a FirebaseUIError if it is a Firebase error. + * + * Addtionally, handles the following error codes: + * - auth/account-exists-with-different-credential - stores the credential in sessionStorage. + * - auth/multi-factor-auth-required - updates the UI instance with the multi-factor resolver. + * + * @param ui - The FirebaseUI instance. + * @param error - The error to handle. + * @returns {never} A never type. + */ export function handleFirebaseError(ui: FirebaseUI, error: unknown): never { // If it's not a Firebase error, then we just throw it and preserve the original error. if (!isFirebaseError(error)) { diff --git a/packages/core/src/register-framework.test.ts b/packages/core/src/register-framework.test.ts index 30b0f16c8..c9034626c 100644 --- a/packages/core/src/register-framework.test.ts +++ b/packages/core/src/register-framework.test.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { describe, it, expect, vi, beforeEach } from "vitest"; import { registerFramework } from "./register-framework"; diff --git a/packages/core/src/register-framework.ts b/packages/core/src/register-framework.ts index 3817ad7c0..dce604e81 100644 --- a/packages/core/src/register-framework.ts +++ b/packages/core/src/register-framework.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { registerVersion } from "firebase/app"; /** diff --git a/packages/core/src/schemas.test.ts b/packages/core/src/schemas.test.ts index 13edc30e3..4f215193d 100644 --- a/packages/core/src/schemas.test.ts +++ b/packages/core/src/schemas.test.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { describe, it, expect, vi } from "vitest"; import { createMockUI } from "~/tests/utils"; import { diff --git a/packages/core/src/schemas.ts b/packages/core/src/schemas.ts index 509193d99..2e03b42ca 100644 --- a/packages/core/src/schemas.ts +++ b/packages/core/src/schemas.ts @@ -19,10 +19,14 @@ import { getTranslation } from "./translations"; import { type FirebaseUI } from "./config"; import { hasBehavior } from "./behaviors"; -export const LoginTypes = ["email", "phone", "anonymous", "emailLink", "google"] as const; -export type LoginType = (typeof LoginTypes)[number]; -export type AuthMode = "signIn" | "signUp"; - +/** + * Creates a Zod schema for sign-in form validation. + * + * Validates email format and password minimum length (6 characters). + * + * @param ui - The FirebaseUI instance. + * @returns A Zod schema for sign-in form validation. + */ export function createSignInAuthFormSchema(ui: FirebaseUI) { return z.object({ email: z.email(getTranslation(ui, "errors", "invalidEmail")), @@ -30,6 +34,15 @@ export function createSignInAuthFormSchema(ui: FirebaseUI) { }); } +/** + * Creates a Zod schema for sign-up form validation. + * + * Validates email format, password minimum length (6 characters), and optionally requires a display name + * if the `requireDisplayName` behavior is enabled. + * + * @param ui - The FirebaseUI instance. + * @returns A Zod schema for sign-up form validation. + */ export function createSignUpAuthFormSchema(ui: FirebaseUI) { const requireDisplayName = hasBehavior(ui, "requireDisplayName"); const displayNameRequiredMessage = getTranslation(ui, "errors", "displayNameRequired"); @@ -43,18 +56,42 @@ export function createSignUpAuthFormSchema(ui: FirebaseUI) { }); } +/** + * Creates a Zod schema for forgot password form validation. + * + * Validates email format. + * + * @param ui - The FirebaseUI instance. + * @returns A Zod schema for forgot password form validation. + */ export function createForgotPasswordAuthFormSchema(ui: FirebaseUI) { return z.object({ email: z.email(getTranslation(ui, "errors", "invalidEmail")), }); } +/** + * Creates a Zod schema for email link authentication form validation. + * + * Validates email format. + * + * @param ui - The FirebaseUI instance. + * @returns A Zod schema for email link authentication form validation. + */ export function createEmailLinkAuthFormSchema(ui: FirebaseUI) { return z.object({ email: z.email(getTranslation(ui, "errors", "invalidEmail")), }); } +/** + * Creates a Zod schema for phone number form validation. + * + * Validates that the phone number is provided and has a maximum length of 10 characters. + * + * @param ui - The FirebaseUI instance. + * @returns A Zod schema for phone number form validation. + */ export function createPhoneAuthNumberFormSchema(ui: FirebaseUI) { return z.object({ phoneNumber: z @@ -64,6 +101,14 @@ export function createPhoneAuthNumberFormSchema(ui: FirebaseUI) { }); } +/** + * Creates a Zod schema for phone verification code form validation. + * + * Validates that the verification ID is provided and the verification code is at least 6 characters long. + * + * @param ui - The FirebaseUI instance. + * @returns A Zod schema for phone verification form validation. + */ export function createPhoneAuthVerifyFormSchema(ui: FirebaseUI) { return z.object({ verificationId: z.string().min(1, getTranslation(ui, "errors", "missingVerificationId")), @@ -73,6 +118,14 @@ export function createPhoneAuthVerifyFormSchema(ui: FirebaseUI) { }); } +/** + * Creates a Zod schema for multi-factor phone authentication number form validation. + * + * Extends the phone number schema with a required display name field. + * + * @param ui - The FirebaseUI instance. + * @returns A Zod schema for multi-factor phone authentication number form validation. + */ export function createMultiFactorPhoneAuthNumberFormSchema(ui: FirebaseUI) { const base = createPhoneAuthNumberFormSchema(ui); return base.extend({ @@ -80,20 +133,52 @@ export function createMultiFactorPhoneAuthNumberFormSchema(ui: FirebaseUI) { }); } +/** + * Creates a Zod schema for multi-factor phone authentication assertion form validation. + * + * Uses the same validation as the phone number form schema. + * + * @param ui - The FirebaseUI instance. + * @returns A Zod schema for multi-factor phone authentication assertion form validation. + */ export function createMultiFactorPhoneAuthAssertionFormSchema(ui: FirebaseUI) { return createPhoneAuthNumberFormSchema(ui); } +/** + * Creates a Zod schema for multi-factor phone authentication verification form validation. + * + * Uses the same validation as the phone verification form schema. + * + * @param ui - The FirebaseUI instance. + * @returns A Zod schema for multi-factor phone authentication verification form validation. + */ export function createMultiFactorPhoneAuthVerifyFormSchema(ui: FirebaseUI) { return createPhoneAuthVerifyFormSchema(ui); } +/** + * Creates a Zod schema for multi-factor TOTP authentication number form validation. + * + * Validates that a display name is provided. + * + * @param ui - The FirebaseUI instance. + * @returns A Zod schema for multi-factor TOTP authentication number form validation. + */ export function createMultiFactorTotpAuthNumberFormSchema(ui: FirebaseUI) { return z.object({ displayName: z.string().min(1, getTranslation(ui, "errors", "displayNameRequired")), }); } +/** + * Creates a Zod schema for multi-factor TOTP authentication verification form validation. + * + * Validates that the verification code is exactly 6 characters long. + * + * @param ui - The FirebaseUI instance. + * @returns A Zod schema for multi-factor TOTP authentication verification form validation. + */ export function createMultiFactorTotpAuthVerifyFormSchema(ui: FirebaseUI) { return z.object({ verificationCode: z.string().refine((val) => val.length === 6, { @@ -102,14 +187,23 @@ export function createMultiFactorTotpAuthVerifyFormSchema(ui: FirebaseUI) { }); } +/** The inferred type for the sign-in authentication form schema. */ export type SignInAuthFormSchema = z.infer>; +/** The inferred type for the sign-up authentication form schema. */ export type SignUpAuthFormSchema = z.infer>; +/** The inferred type for the forgot password authentication form schema. */ export type ForgotPasswordAuthFormSchema = z.infer>; +/** The inferred type for the email link authentication form schema. */ export type EmailLinkAuthFormSchema = z.infer>; +/** The inferred type for the phone authentication number form schema. */ export type PhoneAuthNumberFormSchema = z.infer>; +/** The inferred type for the phone authentication verification form schema. */ export type PhoneAuthVerifyFormSchema = z.infer>; +/** The inferred type for the multi-factor phone authentication number form schema. */ export type MultiFactorPhoneAuthNumberFormSchema = z.infer< ReturnType >; +/** The inferred type for the multi-factor TOTP authentication number form schema. */ export type MultiFactorTotpAuthNumberFormSchema = z.infer>; +/** The inferred type for the multi-factor TOTP authentication verification form schema. */ export type MultiFactorTotpAuthVerifyFormSchema = z.infer>; diff --git a/packages/core/src/translations.test.ts b/packages/core/src/translations.test.ts index 3118eb25a..62cd98362 100644 --- a/packages/core/src/translations.test.ts +++ b/packages/core/src/translations.test.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { describe, expect, it, vi } from "vitest"; // Mock the translations module first diff --git a/packages/core/src/translations.ts b/packages/core/src/translations.ts index e2139699e..f8dd6c56f 100644 --- a/packages/core/src/translations.ts +++ b/packages/core/src/translations.ts @@ -21,6 +21,20 @@ import { } from "@invertase/firebaseui-translations"; import { type FirebaseUI } from "./config"; +/** + * Gets a translated string for a given category and key. + * + * Example: + * ```typescript + * const translation = getTranslation(ui, "errors", "userNotFound"); + * ``` + * + * @param ui - The FirebaseUI instance. + * @param category - The translation category. + * @param key - The translation key. + * @param replacements - Optional replacements for placeholders. + * @returns The translated string. + */ export function getTranslation( ui: FirebaseUI, category: T, diff --git a/packages/core/tests/utils.ts b/packages/core/tests/utils.ts index 948540cac..28b573968 100644 --- a/packages/core/tests/utils.ts +++ b/packages/core/tests/utils.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { vi } from "vitest"; import type { FirebaseApp } from "firebase/app"; diff --git a/packages/core/vite.config.ts b/packages/core/vite.config.ts index 8220a98e6..aee7ce7f8 100644 --- a/packages/core/vite.config.ts +++ b/packages/core/vite.config.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { defineConfig } from "vite"; import path from "node:path"; import { fileURLToPath } from "node:url"; diff --git a/packages/react/src/auth/forms/email-link-auth-form.tsx b/packages/react/src/auth/forms/email-link-auth-form.tsx index f6b1d03ef..c23bad858 100644 --- a/packages/react/src/auth/forms/email-link-auth-form.tsx +++ b/packages/react/src/auth/forms/email-link-auth-form.tsx @@ -28,11 +28,19 @@ import { form } from "~/components/form"; import { Policies } from "~/components/policies"; import { useCallback, useEffect, useState } from "react"; +/** Props for the EmailLinkAuthForm component. */ export type EmailLinkAuthFormProps = { + /** Callback function called when the sign-in link email is sent. */ onEmailSent?: () => void; + /** Callback function called when sign-in is completed via the email link. */ onSignIn?: (credential: UserCredential) => void; }; +/** + * Creates a memoized action function for sending a sign-in link to an email address. + * + * @returns A callback function that sends a sign-in link to the specified email address. + */ export function useEmailLinkAuthFormAction() { const ui = useUI(); @@ -53,6 +61,12 @@ export function useEmailLinkAuthFormAction() { ); } +/** + * Creates a form hook for email link authentication. + * + * @param onSuccess - Optional callback function called when the sign-in link email is sent. + * @returns A form instance configured for email link authentication. + */ export function useEmailLinkAuthForm(onSuccess?: EmailLinkAuthFormProps["onEmailSent"]) { const schema = useEmailLinkAuthFormSchema(); const action = useEmailLinkAuthFormAction(); @@ -75,6 +89,13 @@ export function useEmailLinkAuthForm(onSuccess?: EmailLinkAuthFormProps["onEmail }); } +/** + * Hook that automatically completes the email link sign-in process when the component mounts. + * + * Checks if the current URL contains a valid email link sign-in link and completes the authentication. + * + * @param onSignIn - Optional callback function called when sign-in is completed. + */ export function useEmailLinkAuthFormCompleteSignIn(onSignIn?: EmailLinkAuthFormProps["onSignIn"]) { const ui = useUI(); @@ -91,6 +112,14 @@ export function useEmailLinkAuthFormCompleteSignIn(onSignIn?: EmailLinkAuthFormP }, [onSignIn]); } +/** + * A form component for email link authentication. + * + * Sends a sign-in link to the user's email address and automatically completes sign-in + * if the user arrives via an email link. + * + * @returns The email link auth form component. + */ export function EmailLinkAuthForm({ onEmailSent, onSignIn }: EmailLinkAuthFormProps) { const ui = useUI(); const [emailSent, setEmailSent] = useState(false); diff --git a/packages/react/src/auth/forms/forgot-password-auth-form.tsx b/packages/react/src/auth/forms/forgot-password-auth-form.tsx index cd0946110..f6d84a78d 100644 --- a/packages/react/src/auth/forms/forgot-password-auth-form.tsx +++ b/packages/react/src/auth/forms/forgot-password-auth-form.tsx @@ -22,11 +22,19 @@ import { form } from "~/components/form"; import { Policies } from "~/components/policies"; import { useCallback, useState } from "react"; +/** Props for the ForgotPasswordAuthForm component. */ export type ForgotPasswordAuthFormProps = { + /** Callback function called when the password reset email is sent. */ onPasswordSent?: () => void; + /** Callback function called when the back to sign in link is clicked. */ onBackToSignInClick?: () => void; }; +/** + * Creates a memoized action function for sending a password reset email. + * + * @returns A callback function that sends a password reset email to the specified address. + */ export function useForgotPasswordAuthFormAction() { const ui = useUI(); @@ -47,6 +55,12 @@ export function useForgotPasswordAuthFormAction() { ); } +/** + * Creates a form hook for forgot password authentication. + * + * @param onSuccess - Optional callback function called when the password reset email is sent. + * @returns A form instance configured for forgot password. + */ export function useForgotPasswordAuthForm(onSuccess?: ForgotPasswordAuthFormProps["onPasswordSent"]) { const schema = useForgotPasswordAuthFormSchema(); const action = useForgotPasswordAuthFormAction(); @@ -69,6 +83,13 @@ export function useForgotPasswordAuthForm(onSuccess?: ForgotPasswordAuthFormProp }); } +/** + * A form component for requesting a password reset email. + * + * Displays a success message after the email is sent. + * + * @returns The forgot password form component. + */ export function ForgotPasswordAuthForm({ onBackToSignInClick, onPasswordSent }: ForgotPasswordAuthFormProps) { const ui = useUI(); const [emailSent, setEmailSent] = useState(false); diff --git a/packages/react/src/auth/forms/mfa/sms-multi-factor-assertion-form.tsx b/packages/react/src/auth/forms/mfa/sms-multi-factor-assertion-form.tsx index 28a784339..904cccd3d 100644 --- a/packages/react/src/auth/forms/mfa/sms-multi-factor-assertion-form.tsx +++ b/packages/react/src/auth/forms/mfa/sms-multi-factor-assertion-form.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { useCallback, useRef, useState } from "react"; import { PhoneAuthProvider, @@ -20,6 +36,11 @@ type PhoneMultiFactorInfo = MultiFactorInfo & { phoneNumber?: string; }; +/** + * Creates a memoized action function for verifying a phone number during SMS multi-factor assertion. + * + * @returns A callback function that verifies a phone number using the provided hint and reCAPTCHA verifier. + */ export function useSmsMultiFactorAssertionPhoneFormAction() { const ui = useUI(); @@ -31,12 +52,22 @@ export function useSmsMultiFactorAssertionPhoneFormAction() { ); } +/** Options for the SMS multi-factor assertion phone form hook. */ type UseSmsMultiFactorAssertionPhoneForm = { + /** The multi-factor info hint containing phone number information. */ hint: MultiFactorInfo; + /** The reCAPTCHA verifier instance. */ recaptchaVerifier: RecaptchaVerifier; + /** Callback function called when phone verification is successful. */ onSuccess: (verificationId: string) => void; }; +/** + * Creates a form hook for SMS multi-factor assertion phone number verification. + * + * @param options - The phone form options. + * @returns A form instance configured for phone number verification. + */ export function useSmsMultiFactorAssertionPhoneForm({ hint, recaptchaVerifier, @@ -104,6 +135,11 @@ function SmsMultiFactorAssertionPhoneForm(props: SmsMultiFactorAssertionPhoneFor ); } +/** + * Creates a memoized action function for verifying the SMS verification code during multi-factor assertion. + * + * @returns A callback function that verifies the code and signs in with the multi-factor assertion. + */ export function useSmsMultiFactorAssertionVerifyFormAction() { const ui = useUI(); @@ -117,11 +153,20 @@ export function useSmsMultiFactorAssertionVerifyFormAction() { ); } +/** Options for the SMS multi-factor assertion verify form hook. */ type UseSmsMultiFactorAssertionVerifyForm = { + /** The verification ID from the phone verification step. */ verificationId: string; + /** Callback function called when verification is successful. */ onSuccess: (credential: UserCredential) => void; }; +/** + * Creates a form hook for SMS multi-factor assertion verification code input. + * + * @param options - The verify form options. + * @returns A form instance configured for verification code input. + */ export function useSmsMultiFactorAssertionVerifyForm({ verificationId, onSuccess, @@ -190,11 +235,21 @@ function SmsMultiFactorAssertionVerifyForm(props: SmsMultiFactorAssertionVerifyF ); } +/** Props for the SmsMultiFactorAssertionForm component. */ export type SmsMultiFactorAssertionFormProps = { + /** The multi-factor info hint containing phone number information. */ hint: MultiFactorInfo; + /** Optional callback function called when multi-factor assertion is successful. */ onSuccess?: (credential: UserCredential) => void; }; +/** + * A form component for SMS multi-factor authentication assertion. + * + * Handles the two-step process: first verifying the phone number, then verifying the SMS code. + * + * @returns The SMS multi-factor assertion form component. + */ export function SmsMultiFactorAssertionForm(props: SmsMultiFactorAssertionFormProps) { const [verification, setVerification] = useState<{ verificationId: string; diff --git a/packages/react/src/auth/forms/mfa/sms-multi-factor-enrollment-form.tsx b/packages/react/src/auth/forms/mfa/sms-multi-factor-enrollment-form.tsx index 7a521d0e3..4c9d5157c 100644 --- a/packages/react/src/auth/forms/mfa/sms-multi-factor-enrollment-form.tsx +++ b/packages/react/src/auth/forms/mfa/sms-multi-factor-enrollment-form.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { useCallback, useRef, useState } from "react"; import { multiFactor, PhoneAuthProvider, PhoneMultiFactorGenerator, type RecaptchaVerifier } from "firebase/auth"; import { @@ -16,6 +32,11 @@ import { useUI, } from "~/hooks"; +/** + * Creates a memoized action function for verifying a phone number during SMS multi-factor enrollment. + * + * @returns A callback function that verifies a phone number for MFA enrollment using the provided reCAPTCHA verifier. + */ export function useSmsMultiFactorEnrollmentPhoneAuthFormAction() { const ui = useUI(); @@ -28,12 +49,22 @@ export function useSmsMultiFactorEnrollmentPhoneAuthFormAction() { ); } +/** Options for the SMS multi-factor enrollment phone number form hook. */ export type UseSmsMultiFactorEnrollmentPhoneNumberForm = { + /** The reCAPTCHA verifier instance. */ recaptchaVerifier: RecaptchaVerifier; + /** Callback function called when phone verification is successful. */ onSuccess: (verificationId: string, displayName?: string) => void; + /** Optional function to format the phone number before verification. */ formatPhoneNumber?: (phoneNumber: string) => string; }; +/** + * Creates a form hook for SMS multi-factor enrollment phone number verification. + * + * @param options - The phone number form options. + * @returns A form instance configured for phone number input and verification for MFA enrollment. + */ export function useSmsMultiFactorEnrollmentPhoneNumberForm({ recaptchaVerifier, onSuccess, @@ -115,6 +146,11 @@ function MultiFactorEnrollmentPhoneNumberForm(props: MultiFactorEnrollmentPhoneN ); } +/** + * Creates a memoized action function for verifying the SMS verification code during multi-factor enrollment. + * + * @returns A callback function that verifies the code and enrolls the phone number as a multi-factor authentication method. + */ export function useMultiFactorEnrollmentVerifyPhoneNumberFormAction() { const ui = useUI(); return useCallback( @@ -135,12 +171,22 @@ export function useMultiFactorEnrollmentVerifyPhoneNumberFormAction() { ); } +/** Options for the multi-factor enrollment verify phone number form hook. */ type UseMultiFactorEnrollmentVerifyPhoneNumberForm = { + /** The verification ID from the phone verification step. */ verificationId: string; + /** Optional display name for the enrolled MFA method. */ displayName?: string; + /** Callback function called when enrollment is successful. */ onSuccess: () => void; }; +/** + * Creates a form hook for SMS multi-factor enrollment verification code input. + * + * @param options - The verify phone number form options. + * @returns A form instance configured for verification code input during MFA enrollment. + */ export function useMultiFactorEnrollmentVerifyPhoneNumberForm({ verificationId, displayName, @@ -168,12 +214,21 @@ export function useMultiFactorEnrollmentVerifyPhoneNumberForm({ }); } +/** Props for the MultiFactorEnrollmentVerifyPhoneNumberForm component. */ type MultiFactorEnrollmentVerifyPhoneNumberFormProps = { + /** The verification ID from the phone verification step. */ verificationId: string; + /** Optional display name for the enrolled MFA method. */ displayName?: string; + /** Callback function called when enrollment is successful. */ onSuccess: () => void; }; +/** + * A form component for verifying the SMS code during multi-factor enrollment. + * + * @returns The verify phone number form component. + */ export function MultiFactorEnrollmentVerifyPhoneNumberForm(props: MultiFactorEnrollmentVerifyPhoneNumberFormProps) { const ui = useUI(); const form = useMultiFactorEnrollmentVerifyPhoneNumberForm({ @@ -211,10 +266,20 @@ export function MultiFactorEnrollmentVerifyPhoneNumberForm(props: MultiFactorEnr ); } +/** Props for the SmsMultiFactorEnrollmentForm component. */ export type SmsMultiFactorEnrollmentFormProps = { + /** Optional callback function called when enrollment is successful. */ onSuccess?: () => void; }; +/** + * A form component for SMS multi-factor authentication enrollment. + * + * Handles the two-step process: first entering the phone number and display name, then verifying the SMS code. + * + * @returns The SMS multi-factor enrollment form component. + * @throws {Error} Throws an error if the user is not authenticated. + */ export function SmsMultiFactorEnrollmentForm(props: SmsMultiFactorEnrollmentFormProps) { const ui = useUI(); diff --git a/packages/react/src/auth/forms/mfa/totp-multi-factor-assertion-form.test.tsx b/packages/react/src/auth/forms/mfa/totp-multi-factor-assertion-form.test.tsx index a3eb97af6..de7a5d145 100644 --- a/packages/react/src/auth/forms/mfa/totp-multi-factor-assertion-form.test.tsx +++ b/packages/react/src/auth/forms/mfa/totp-multi-factor-assertion-form.test.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + /** * @license * Copyright 2024 Google LLC diff --git a/packages/react/src/auth/forms/mfa/totp-multi-factor-assertion-form.tsx b/packages/react/src/auth/forms/mfa/totp-multi-factor-assertion-form.tsx index 0a69fb230..ad9896982 100644 --- a/packages/react/src/auth/forms/mfa/totp-multi-factor-assertion-form.tsx +++ b/packages/react/src/auth/forms/mfa/totp-multi-factor-assertion-form.tsx @@ -1,9 +1,30 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { useCallback } from "react"; import { TotpMultiFactorGenerator, type MultiFactorInfo, type UserCredential } from "firebase/auth"; import { signInWithMultiFactorAssertion, FirebaseUIError, getTranslation } from "@invertase/firebaseui-core"; import { form } from "~/components/form"; import { useMultiFactorTotpAuthVerifyFormSchema, useUI } from "~/hooks"; +/** + * Creates a memoized action function for verifying a TOTP code during multi-factor assertion. + * + * @returns A callback function that verifies the TOTP code and signs in with the multi-factor assertion. + */ export function useTotpMultiFactorAssertionFormAction() { const ui = useUI(); @@ -16,11 +37,20 @@ export function useTotpMultiFactorAssertionFormAction() { ); } +/** Options for the TOTP multi-factor assertion form hook. */ export type UseTotpMultiFactorAssertionForm = { + /** The multi-factor info hint containing TOTP information. */ hint: MultiFactorInfo; + /** Callback function called when verification is successful. */ onSuccess: (credential: UserCredential) => void; }; +/** + * Creates a form hook for TOTP multi-factor assertion verification code input. + * + * @param options - The TOTP assertion form options. + * @returns A form instance configured for TOTP verification code input. + */ export function useTotpMultiFactorAssertionForm({ hint, onSuccess }: UseTotpMultiFactorAssertionForm) { const action = useTotpMultiFactorAssertionFormAction(); const schema = useMultiFactorTotpAuthVerifyFormSchema(); @@ -43,11 +73,21 @@ export function useTotpMultiFactorAssertionForm({ hint, onSuccess }: UseTotpMult }); } +/** Props for the TotpMultiFactorAssertionForm component. */ export type TotpMultiFactorAssertionFormProps = { + /** The multi-factor info hint containing TOTP information. */ hint: MultiFactorInfo; + /** Optional callback function called when multi-factor assertion is successful. */ onSuccess?: (credential: UserCredential) => void; }; +/** + * A form component for TOTP multi-factor authentication assertion. + * + * Allows users to enter a 6-digit TOTP code from their authenticator app. + * + * @returns The TOTP multi-factor assertion form component. + */ export function TotpMultiFactorAssertionForm(props: TotpMultiFactorAssertionFormProps) { const ui = useUI(); const form = useTotpMultiFactorAssertionForm({ diff --git a/packages/react/src/auth/forms/mfa/totp-multi-factor-enrollment-form.tsx b/packages/react/src/auth/forms/mfa/totp-multi-factor-enrollment-form.tsx index ef36fe05e..d800d48d5 100644 --- a/packages/react/src/auth/forms/mfa/totp-multi-factor-enrollment-form.tsx +++ b/packages/react/src/auth/forms/mfa/totp-multi-factor-enrollment-form.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { useCallback, useState } from "react"; import { TotpMultiFactorGenerator, type TotpSecret } from "firebase/auth"; import { @@ -10,6 +26,11 @@ import { import { form } from "~/components/form"; import { useMultiFactorTotpAuthNumberFormSchema, useMultiFactorTotpAuthVerifyFormSchema, useUI } from "~/hooks"; +/** + * Creates a memoized action function for generating a TOTP secret for multi-factor enrollment. + * + * @returns A callback function that generates a TOTP secret. + */ export function useTotpMultiFactorSecretGenerationFormAction() { const ui = useUI(); @@ -18,10 +39,18 @@ export function useTotpMultiFactorSecretGenerationFormAction() { }, [ui]); } +/** Options for the TOTP multi-factor enrollment form hook. */ export type UseTotpMultiFactorEnrollmentForm = { + /** Callback function called when the TOTP secret is generated. */ onSuccess: (secret: TotpSecret, displayName: string) => void; }; +/** + * Creates a form hook for TOTP multi-factor enrollment secret generation. + * + * @param options - The TOTP enrollment form options. + * @returns A form instance configured for display name input and secret generation. + */ export function useTotpMultiFactorSecretGenerationForm({ onSuccess }: UseTotpMultiFactorEnrollmentForm) { const action = useTotpMultiFactorSecretGenerationFormAction(); const schema = useMultiFactorTotpAuthNumberFormSchema(); @@ -78,6 +107,11 @@ function TotpMultiFactorSecretGenerationForm(props: TotpMultiFactorSecretGenerat ); } +/** + * Creates a memoized action function for verifying the TOTP code during multi-factor enrollment. + * + * @returns A callback function that verifies the TOTP code and enrolls it as a multi-factor authentication method. + */ export function useMultiFactorEnrollmentVerifyTotpFormAction() { const ui = useUI(); return useCallback( @@ -97,12 +131,22 @@ export function useMultiFactorEnrollmentVerifyTotpFormAction() { ); } +/** Options for the multi-factor enrollment verify TOTP form hook. */ type UseMultiFactorEnrollmentVerifyTotpForm = { + /** The TOTP secret generated in the previous step. */ secret: TotpSecret; + /** The display name for the enrolled MFA method. */ displayName: string; + /** Callback function called when enrollment is successful. */ onSuccess: () => void; }; +/** + * Creates a form hook for TOTP multi-factor enrollment verification code input. + * + * @param options - The verify TOTP form options. + * @returns A form instance configured for TOTP verification code input during MFA enrollment. + */ export function useMultiFactorEnrollmentVerifyTotpForm({ secret, displayName, @@ -129,12 +173,24 @@ export function useMultiFactorEnrollmentVerifyTotpForm({ }); } +/** Props for the MultiFactorEnrollmentVerifyTotpForm component. */ type MultiFactorEnrollmentVerifyTotpFormProps = { + /** The TOTP secret generated in the previous step. */ secret: TotpSecret; + /** The display name for the enrolled MFA method. */ displayName: string; + /** Callback function called when enrollment is successful. */ onSuccess: () => void; }; +/** + * A form component for verifying the TOTP code during multi-factor enrollment. + * + * Displays a QR code and secret key for the user to scan with their authenticator app, + * then allows them to verify the enrollment with a TOTP code. + * + * @returns The verify TOTP form component. + */ export function MultiFactorEnrollmentVerifyTotpForm(props: MultiFactorEnrollmentVerifyTotpFormProps) { const ui = useUI(); const form = useMultiFactorEnrollmentVerifyTotpForm({ @@ -179,10 +235,20 @@ export function MultiFactorEnrollmentVerifyTotpForm(props: MultiFactorEnrollment ); } +/** Props for the TotpMultiFactorEnrollmentForm component. */ export type TotpMultiFactorEnrollmentFormProps = { + /** Optional callback function called when enrollment is successful. */ onSuccess?: () => void; }; +/** + * A form component for TOTP multi-factor authentication enrollment. + * + * Handles the two-step process: first generating a TOTP secret and QR code, then verifying the TOTP code. + * + * @returns The TOTP multi-factor enrollment form component. + * @throws {Error} Throws an error if the user is not authenticated. + */ export function TotpMultiFactorEnrollmentForm(props: TotpMultiFactorEnrollmentFormProps) { const ui = useUI(); diff --git a/packages/react/src/auth/forms/multi-factor-auth-assertion-form.tsx b/packages/react/src/auth/forms/multi-factor-auth-assertion-form.tsx index a06eb79ed..7135f1991 100644 --- a/packages/react/src/auth/forms/multi-factor-auth-assertion-form.tsx +++ b/packages/react/src/auth/forms/multi-factor-auth-assertion-form.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { PhoneMultiFactorGenerator, TotpMultiFactorGenerator, @@ -11,10 +27,17 @@ import { SmsMultiFactorAssertionForm } from "../forms/mfa/sms-multi-factor-asser import { Button } from "~/components/button"; import { getTranslation } from "@invertase/firebaseui-core"; +/** Props for the MultiFactorAuthAssertionForm component. */ export type MultiFactorAuthAssertionFormProps = { + /** Optional callback function called when multi-factor assertion is successful. */ onSuccess?: (credential: UserCredential) => void; }; +/** + * Hook that cleans up the multi-factor resolver when the component unmounts. + * + * Ensures the resolver is cleared from the UI state to prevent stale state. + */ export function useMultiFactorAssertionCleanup() { const ui = useUI(); @@ -26,6 +49,15 @@ export function useMultiFactorAssertionCleanup() { }, []); } +/** + * A form component for multi-factor authentication assertion. + * + * Displays the appropriate MFA form (SMS or TOTP) based on the available hints. + * If only one hint is available, it is automatically selected. + * + * @returns The multi-factor auth assertion form component. + * @throws {Error} Throws an error if no multi-factor resolver is available. + */ export function MultiFactorAuthAssertionForm(props: MultiFactorAuthAssertionFormProps) { const ui = useUI(); const resolver = ui.multiFactorResolver; diff --git a/packages/react/src/auth/forms/multi-factor-auth-enrollment-form.tsx b/packages/react/src/auth/forms/multi-factor-auth-enrollment-form.tsx index a3a41ad34..6b9ec9ce2 100644 --- a/packages/react/src/auth/forms/multi-factor-auth-enrollment-form.tsx +++ b/packages/react/src/auth/forms/multi-factor-auth-enrollment-form.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { FactorId } from "firebase/auth"; import { getTranslation } from "@invertase/firebaseui-core"; import { type ComponentProps, useState } from "react"; @@ -9,13 +25,25 @@ import { useUI } from "~/hooks"; type Hint = (typeof FactorId)[keyof typeof FactorId]; +/** Props for the MultiFactorAuthEnrollmentForm component. */ export type MultiFactorAuthEnrollmentFormProps = { + /** Optional callback function called when enrollment is successful. */ onEnrollment?: () => void; + /** Optional array of factor IDs to allow enrollment for. Defaults to TOTP and PHONE. */ hints?: Hint[]; }; const DEFAULT_HINTS = [FactorId.TOTP, FactorId.PHONE] as const; +/** + * A form component for multi-factor authentication enrollment. + * + * Displays the appropriate MFA enrollment form (SMS or TOTP) based on the provided hints. + * If only one hint is provided, it is automatically selected. + * + * @returns The multi-factor auth enrollment form component. + * @throws {Error} Throws an error if no hints are provided or if an unknown hint type is encountered. + */ export function MultiFactorAuthEnrollmentForm(props: MultiFactorAuthEnrollmentFormProps) { const hints = props.hints ?? DEFAULT_HINTS; diff --git a/packages/react/src/auth/forms/phone-auth-form.tsx b/packages/react/src/auth/forms/phone-auth-form.tsx index cbaa7032e..b6c29cbc6 100644 --- a/packages/react/src/auth/forms/phone-auth-form.tsx +++ b/packages/react/src/auth/forms/phone-auth-form.tsx @@ -30,6 +30,11 @@ import { form } from "~/components/form"; import { Policies } from "~/components/policies"; import { CountrySelector, type CountrySelectorRef } from "~/components/country-selector"; +/** + * Creates a memoized action function for verifying a phone number. + * + * @returns A callback function that verifies a phone number using the provided reCAPTCHA verifier. + */ export function usePhoneNumberFormAction() { const ui = useUI(); @@ -41,12 +46,22 @@ export function usePhoneNumberFormAction() { ); } +/** Options for the phone number form hook. */ type UsePhoneNumberForm = { + /** The reCAPTCHA verifier instance. */ recaptchaVerifier: RecaptchaVerifier; + /** Callback function called when phone verification is successful. */ onSuccess: (verificationId: string) => void; + /** Optional function to format the phone number before verification. */ formatPhoneNumber?: (phoneNumber: string) => string; }; +/** + * Creates a form hook for phone number verification. + * + * @param options - The phone number form options. + * @returns A form instance configured for phone number input and verification. + */ export function usePhoneNumberForm({ recaptchaVerifier, onSuccess, formatPhoneNumber }: UsePhoneNumberForm) { const action = usePhoneNumberFormAction(); const schema = usePhoneAuthNumberFormSchema(); @@ -70,10 +85,19 @@ export function usePhoneNumberForm({ recaptchaVerifier, onSuccess, formatPhoneNu }); } +/** Props for the PhoneNumberForm component. */ type PhoneNumberFormProps = { + /** Callback function called when phone verification is successful. */ onSubmit: (verificationId: string) => void; }; +/** + * A form component for entering and verifying a phone number. + * + * Includes a country selector and reCAPTCHA verification. + * + * @returns The phone number form component. + */ export function PhoneNumberForm(props: PhoneNumberFormProps) { const ui = useUI(); const recaptchaContainerRef = useRef(null); @@ -119,6 +143,11 @@ export function PhoneNumberForm(props: PhoneNumberFormProps) { ); } +/** + * Creates a memoized action function for verifying a phone verification code. + * + * @returns A callback function that confirms the phone number using the verification ID and code. + */ export function useVerifyPhoneNumberFormAction() { const ui = useUI(); @@ -130,11 +159,20 @@ export function useVerifyPhoneNumberFormAction() { ); } +/** Options for the verify phone number form hook. */ type UseVerifyPhoneNumberForm = { + /** The verification ID from the phone verification step. */ verificationId: string; + /** Callback function called when verification is successful. */ onSuccess: (credential: UserCredential) => void; }; +/** + * Creates a form hook for phone verification code input. + * + * @param options - The verify phone number form options. + * @returns A form instance configured for verification code input. + */ export function useVerifyPhoneNumberForm({ verificationId, onSuccess }: UseVerifyPhoneNumberForm) { const schema = usePhoneAuthVerifyFormSchema(); const action = useVerifyPhoneNumberFormAction(); @@ -197,10 +235,19 @@ function VerifyPhoneNumberForm(props: VerifyPhoneNumberFormProps) { ); } +/** Props for the PhoneAuthForm component. */ export type PhoneAuthFormProps = { + /** Optional callback function called when sign-in is successful. */ onSignIn?: (credential: UserCredential) => void; }; +/** + * A form component for phone authentication. + * + * Handles the two-step process: first entering the phone number, then verifying the SMS code. + * + * @returns The phone auth form component. + */ export function PhoneAuthForm(props: PhoneAuthFormProps) { const [verificationId, setVerificationId] = useState(null); diff --git a/packages/react/src/auth/forms/sign-in-auth-form.tsx b/packages/react/src/auth/forms/sign-in-auth-form.tsx index c5a25ce7c..1812ccfd5 100644 --- a/packages/react/src/auth/forms/sign-in-auth-form.tsx +++ b/packages/react/src/auth/forms/sign-in-auth-form.tsx @@ -23,12 +23,21 @@ import { form } from "~/components/form"; import { Policies } from "~/components/policies"; import { useCallback } from "react"; +/** Props for the SignInAuthForm component. */ export type SignInAuthFormProps = { + /** Callback function called when sign-in is successful. */ onSignIn?: (credential: UserCredential) => void; + /** Callback function called when the forgot password link is clicked. */ onForgotPasswordClick?: () => void; + /** Callback function called when the sign up link is clicked. */ onSignUpClick?: () => void; }; +/** + * Creates a memoized action function for signing in with email and password. + * + * @returns A callback function that signs in a user with email and password. + */ export function useSignInAuthFormAction() { const ui = useUI(); @@ -49,6 +58,12 @@ export function useSignInAuthFormAction() { ); } +/** + * Creates a form hook for sign-in authentication. + * + * @param onSuccess - Optional callback function called when sign-in is successful. + * @returns A form instance configured for sign-in. + */ export function useSignInAuthForm(onSuccess?: SignInAuthFormProps["onSignIn"]) { const schema = useSignInAuthFormSchema(); const action = useSignInAuthFormAction(); @@ -72,6 +87,11 @@ export function useSignInAuthForm(onSuccess?: SignInAuthFormProps["onSignIn"]) { }); } +/** + * A form component for signing in with email and password. + * + * @returns The sign-in form component. + */ export function SignInAuthForm({ onSignIn, onForgotPasswordClick, onSignUpClick }: SignInAuthFormProps) { const ui = useUI(); const form = useSignInAuthForm(onSignIn); diff --git a/packages/react/src/auth/forms/sign-up-auth-form.tsx b/packages/react/src/auth/forms/sign-up-auth-form.tsx index 876f19ee2..f40c4e382 100644 --- a/packages/react/src/auth/forms/sign-up-auth-form.tsx +++ b/packages/react/src/auth/forms/sign-up-auth-form.tsx @@ -29,16 +29,29 @@ import { Policies } from "~/components/policies"; import { useCallback } from "react"; import { type z } from "zod"; +/** + * Checks if the requireDisplayName behavior is enabled. + * + * @returns True if display name is required, false otherwise. + */ export function useRequireDisplayName() { const ui = useUI(); return hasBehavior(ui, "requireDisplayName"); } +/** Props for the SignUpAuthForm component. */ export type SignUpAuthFormProps = { + /** Callback function called when sign-up is successful. */ onSignUp?: (credential: UserCredential) => void; + /** Callback function called when the sign in link is clicked. */ onSignInClick?: () => void; }; +/** + * Creates a memoized action function for signing up with email and password. + * + * @returns A callback function that creates a new user account with email and password. + */ export function useSignUpAuthFormAction() { const ui = useUI(); @@ -59,6 +72,12 @@ export function useSignUpAuthFormAction() { ); } +/** + * Creates a form hook for sign-up authentication. + * + * @param onSuccess - Optional callback function called when sign-up is successful. + * @returns A form instance configured for sign-up. + */ export function useSignUpAuthForm(onSuccess?: SignUpAuthFormProps["onSignUp"]) { const schema = useSignUpAuthFormSchema(); const action = useSignUpAuthFormAction(); @@ -84,6 +103,13 @@ export function useSignUpAuthForm(onSuccess?: SignUpAuthFormProps["onSignUp"]) { }); } +/** + * A form component for signing up with email and password. + * + * Optionally includes a display name field if the requireDisplayName behavior is enabled. + * + * @returns The sign-up form component. + */ export function SignUpAuthForm({ onSignInClick, onSignUp }: SignUpAuthFormProps) { const ui = useUI(); const form = useSignUpAuthForm(onSignUp); diff --git a/packages/react/src/auth/oauth/apple-sign-in-button.tsx b/packages/react/src/auth/oauth/apple-sign-in-button.tsx index 6d91226d6..18c970d3b 100644 --- a/packages/react/src/auth/oauth/apple-sign-in-button.tsx +++ b/packages/react/src/auth/oauth/apple-sign-in-button.tsx @@ -23,12 +23,21 @@ import { OAuthButton } from "./oauth-button"; import AppleSvgLogo from "~/components/logos/apple/Logo"; import { cn } from "~/utils/cn"; +/** Props for the AppleSignInButton component. */ export type AppleSignInButtonProps = { + /** Optional OAuth provider instance. Defaults to Apple provider. */ provider?: OAuthProvider; + /** Whether to apply themed styling. */ themed?: boolean; + /** Callback function called when sign-in is successful. */ onSignIn?: (credential: UserCredential) => void; }; +/** + * A button component for signing in with Apple. + * + * @returns The Apple sign-in button component. + */ export function AppleSignInButton({ provider, ...props }: AppleSignInButtonProps) { const ui = useUI(); @@ -40,6 +49,11 @@ export function AppleSignInButton({ provider, ...props }: AppleSignInButtonProps ); } +/** + * The Apple logo SVG component. + * + * @returns The Apple logo component. + */ export function AppleLogo({ className, ...props }: React.SVGProps) { return ; } diff --git a/packages/react/src/auth/oauth/facebook-sign-in-button.tsx b/packages/react/src/auth/oauth/facebook-sign-in-button.tsx index be4bfc794..6e5d50841 100644 --- a/packages/react/src/auth/oauth/facebook-sign-in-button.tsx +++ b/packages/react/src/auth/oauth/facebook-sign-in-button.tsx @@ -23,12 +23,21 @@ import { OAuthButton } from "./oauth-button"; import FacebookSvgLogo from "~/components/logos/facebook/Logo"; import { cn } from "~/utils/cn"; +/** Props for the FacebookSignInButton component. */ export type FacebookSignInButtonProps = { + /** Optional OAuth provider instance. Defaults to Facebook provider. */ provider?: FacebookAuthProvider; + /** Whether to apply themed styling. */ themed?: boolean; + /** Callback function called when sign-in is successful. */ onSignIn?: (credential: UserCredential) => void; }; +/** + * A button component for signing in with Facebook. + * + * @returns The Facebook sign-in button component. + */ export function FacebookSignInButton({ provider, ...props }: FacebookSignInButtonProps) { const ui = useUI(); @@ -40,6 +49,11 @@ export function FacebookSignInButton({ provider, ...props }: FacebookSignInButto ); } +/** + * The Facebook logo SVG component. + * + * @returns The Facebook logo component. + */ export function FacebookLogo({ className, ...props }: React.SVGProps) { return ; } diff --git a/packages/react/src/auth/oauth/github-sign-in-button.tsx b/packages/react/src/auth/oauth/github-sign-in-button.tsx index e9e52206e..41ae37a2a 100644 --- a/packages/react/src/auth/oauth/github-sign-in-button.tsx +++ b/packages/react/src/auth/oauth/github-sign-in-button.tsx @@ -23,12 +23,21 @@ import { OAuthButton } from "./oauth-button"; import GitHubSvgLogo from "~/components/logos/github/Logo"; import { cn } from "~/utils/cn"; +/** Props for the GitHubSignInButton component. */ export type GitHubSignInButtonProps = { + /** Optional OAuth provider instance. Defaults to GitHub provider. */ provider?: GithubAuthProvider; + /** Whether to apply themed styling. */ themed?: boolean; + /** Callback function called when sign-in is successful. */ onSignIn?: (credential: UserCredential) => void; }; +/** + * A button component for signing in with GitHub. + * + * @returns The GitHub sign-in button component. + */ export function GitHubSignInButton({ provider, ...props }: GitHubSignInButtonProps) { const ui = useUI(); @@ -40,6 +49,11 @@ export function GitHubSignInButton({ provider, ...props }: GitHubSignInButtonPro ); } +/** + * The GitHub logo SVG component. + * + * @returns The GitHub logo component. + */ export function GitHubLogo({ className, ...props }: React.SVGProps) { return ; } diff --git a/packages/react/src/auth/oauth/google-sign-in-button.tsx b/packages/react/src/auth/oauth/google-sign-in-button.tsx index 4d33ea2d9..37eaa2c51 100644 --- a/packages/react/src/auth/oauth/google-sign-in-button.tsx +++ b/packages/react/src/auth/oauth/google-sign-in-button.tsx @@ -23,12 +23,21 @@ import { OAuthButton } from "./oauth-button"; import GoogleSvgLogo from "~/components/logos/google/Logo"; import { cn } from "~/utils/cn"; +/** Props for the GoogleSignInButton component. */ export type GoogleSignInButtonProps = { + /** Optional OAuth provider instance. Defaults to Google provider. */ provider?: GoogleAuthProvider; + /** Whether to apply themed styling. Can be true, false, or "neutral". */ themed?: boolean | "neutral"; + /** Callback function called when sign-in is successful. */ onSignIn?: (credential: UserCredential) => void; }; +/** + * A button component for signing in with Google. + * + * @returns The Google sign-in button component. + */ export function GoogleSignInButton({ provider, ...props }: GoogleSignInButtonProps) { const ui = useUI(); @@ -40,6 +49,11 @@ export function GoogleSignInButton({ provider, ...props }: GoogleSignInButtonPro ); } +/** + * The Google logo SVG component. + * + * @returns The Google logo component. + */ export function GoogleLogo({ className, ...props }: React.SVGProps) { return ; } diff --git a/packages/react/src/auth/oauth/microsoft-sign-in-button.tsx b/packages/react/src/auth/oauth/microsoft-sign-in-button.tsx index 2e85ad512..7342a6843 100644 --- a/packages/react/src/auth/oauth/microsoft-sign-in-button.tsx +++ b/packages/react/src/auth/oauth/microsoft-sign-in-button.tsx @@ -23,12 +23,21 @@ import { OAuthButton } from "./oauth-button"; import MicrosoftSvgLogo from "~/components/logos/microsoft/Logo"; import { cn } from "~/utils/cn"; +/** Props for the MicrosoftSignInButton component. */ export type MicrosoftSignInButtonProps = { + /** Optional OAuth provider instance. Defaults to Microsoft provider. */ provider?: OAuthProvider; + /** Whether to apply themed styling. */ themed?: boolean; + /** Callback function called when sign-in is successful. */ onSignIn?: (credential: UserCredential) => void; }; +/** + * A button component for signing in with Microsoft. + * + * @returns The Microsoft sign-in button component. + */ export function MicrosoftSignInButton({ provider, ...props }: MicrosoftSignInButtonProps) { const ui = useUI(); @@ -40,6 +49,11 @@ export function MicrosoftSignInButton({ provider, ...props }: MicrosoftSignInBut ); } +/** + * The Microsoft logo SVG component. + * + * @returns The Microsoft logo component. + */ export function MicrosoftLogo({ className, ...props }: React.SVGProps) { return ; } diff --git a/packages/react/src/auth/oauth/oauth-button.tsx b/packages/react/src/auth/oauth/oauth-button.tsx index 82a2c80ea..83427e35d 100644 --- a/packages/react/src/auth/oauth/oauth-button.tsx +++ b/packages/react/src/auth/oauth/oauth-button.tsx @@ -23,12 +23,23 @@ import { useCallback, useState } from "react"; import { Button } from "~/components/button"; import { useUI } from "~/hooks"; +/** Props for the OAuthButton component. */ export type OAuthButtonProps = PropsWithChildren<{ + /** The authentication provider to sign in with. */ provider: AuthProvider; + /** Whether to use themed styling for the button. */ themed?: boolean | string; + /** Callback function called when sign-in is successful. */ onSignIn?: (credential: UserCredential) => void; }>; +/** + * Hook for signing in with an OAuth provider. + * + * @param provider - The authentication provider to sign in with. + * @param onSignIn - Optional callback function called when sign-in is successful. + * @returns An object containing the error state and a callback function to trigger sign-in. + */ export function useSignInWithProvider(provider: AuthProvider, onSignIn?: (credential: UserCredential) => void) { const ui = useUI(); const [error, setError] = useState(null); @@ -51,6 +62,11 @@ export function useSignInWithProvider(provider: AuthProvider, onSignIn?: (creden return { error, callback }; } +/** + * A button component for signing in with an OAuth provider. + * + * @returns The OAuth button component. + */ export function OAuthButton({ provider, children, themed, onSignIn }: OAuthButtonProps) { const ui = useUI(); diff --git a/packages/react/src/auth/oauth/twitter-sign-in-button.tsx b/packages/react/src/auth/oauth/twitter-sign-in-button.tsx index 492f43d51..7f14db802 100644 --- a/packages/react/src/auth/oauth/twitter-sign-in-button.tsx +++ b/packages/react/src/auth/oauth/twitter-sign-in-button.tsx @@ -23,12 +23,21 @@ import { OAuthButton } from "./oauth-button"; import TwitterSvgLogo from "~/components/logos/twitter/Logo"; import { cn } from "~/utils/cn"; +/** Props for the TwitterSignInButton component. */ export type TwitterSignInButtonProps = { + /** Optional OAuth provider instance. Defaults to Twitter provider. */ provider?: TwitterAuthProvider; + /** Whether to apply themed styling. */ themed?: boolean; + /** Callback function called when sign-in is successful. */ onSignIn?: (credential: UserCredential) => void; }; +/** + * A button component for signing in with Twitter. + * + * @returns The Twitter sign-in button component. + */ export function TwitterSignInButton({ provider, ...props }: TwitterSignInButtonProps) { const ui = useUI(); @@ -40,6 +49,11 @@ export function TwitterSignInButton({ provider, ...props }: TwitterSignInButtonP ); } +/** + * The Twitter logo SVG component. + * + * @returns The Twitter logo component. + */ export function TwitterLogo({ className, ...props }: React.SVGProps) { return ; } diff --git a/packages/react/src/auth/screens/email-link-auth-screen.tsx b/packages/react/src/auth/screens/email-link-auth-screen.tsx index 7072b9c90..268a79988 100644 --- a/packages/react/src/auth/screens/email-link-auth-screen.tsx +++ b/packages/react/src/auth/screens/email-link-auth-screen.tsx @@ -24,12 +24,21 @@ import { EmailLinkAuthForm, type EmailLinkAuthFormProps } from "../forms/email-l import { RedirectError } from "~/components/redirect-error"; import { MultiFactorAuthAssertionScreen } from "./multi-factor-auth-assertion-screen"; +/** Props for the EmailLinkAuthScreen component. */ export type EmailLinkAuthScreenProps = PropsWithChildren< Pick & { + /** Callback function called when sign-in is successful. */ onSignIn?: (user: User) => void; } >; +/** + * A screen component for email link authentication. + * + * Displays a card with the email link auth form and handles multi-factor authentication if required. + * + * @returns The email link auth screen component. + */ export function EmailLinkAuthScreen({ children, onEmailSent, onSignIn }: EmailLinkAuthScreenProps) { const ui = useUI(); diff --git a/packages/react/src/auth/screens/forgot-password-auth-screen.tsx b/packages/react/src/auth/screens/forgot-password-auth-screen.tsx index 6dbbe79ba..944fbea12 100644 --- a/packages/react/src/auth/screens/forgot-password-auth-screen.tsx +++ b/packages/react/src/auth/screens/forgot-password-auth-screen.tsx @@ -19,8 +19,16 @@ import { useUI } from "~/hooks"; import { Card, CardContent, CardHeader, CardSubtitle, CardTitle } from "../../components/card"; import { ForgotPasswordAuthForm, type ForgotPasswordAuthFormProps } from "../forms/forgot-password-auth-form"; +/** Props for the ForgotPasswordAuthScreen component. */ export type ForgotPasswordAuthScreenProps = ForgotPasswordAuthFormProps; +/** + * A screen component for requesting a password reset. + * + * Displays a card with the forgot password form. + * + * @returns The forgot password screen component. + */ export function ForgotPasswordAuthScreen(props: ForgotPasswordAuthScreenProps) { const ui = useUI(); diff --git a/packages/react/src/auth/screens/multi-factor-auth-assertion-screen.tsx b/packages/react/src/auth/screens/multi-factor-auth-assertion-screen.tsx index 1d6ff4b97..79c5c3e69 100644 --- a/packages/react/src/auth/screens/multi-factor-auth-assertion-screen.tsx +++ b/packages/react/src/auth/screens/multi-factor-auth-assertion-screen.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { getTranslation } from "@invertase/firebaseui-core"; import { Card, CardContent, CardHeader, CardSubtitle, CardTitle } from "~/components/card"; import { useUI } from "~/hooks"; @@ -6,8 +22,16 @@ import { type MultiFactorAuthAssertionFormProps, } from "../forms/multi-factor-auth-assertion-form"; +/** Props for the MultiFactorAuthAssertionScreen component. */ export type MultiFactorAuthAssertionScreenProps = MultiFactorAuthAssertionFormProps; +/** + * A screen component for multi-factor authentication assertion. + * + * Displays a card with the multi-factor assertion form. + * + * @returns The multi-factor auth assertion screen component. + */ export function MultiFactorAuthAssertionScreen(props: MultiFactorAuthAssertionScreenProps) { const ui = useUI(); diff --git a/packages/react/src/auth/screens/multi-factor-auth-enrollment-screen.tsx b/packages/react/src/auth/screens/multi-factor-auth-enrollment-screen.tsx index 7c4275925..f76716b41 100644 --- a/packages/react/src/auth/screens/multi-factor-auth-enrollment-screen.tsx +++ b/packages/react/src/auth/screens/multi-factor-auth-enrollment-screen.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { getTranslation } from "@invertase/firebaseui-core"; import { Card, CardContent, CardHeader, CardSubtitle, CardTitle } from "~/components/card"; import { useUI } from "~/hooks"; @@ -6,8 +22,16 @@ import { type MultiFactorAuthEnrollmentFormProps, } from "../forms/multi-factor-auth-enrollment-form"; +/** Props for the MultiFactorAuthEnrollmentScreen component. */ export type MultiFactorAuthEnrollmentScreenProps = MultiFactorAuthEnrollmentFormProps; +/** + * A screen component for multi-factor authentication enrollment. + * + * Displays a card with the multi-factor enrollment form. + * + * @returns The multi-factor auth enrollment screen component. + */ export function MultiFactorAuthEnrollmentScreen(props: MultiFactorAuthEnrollmentScreenProps) { const ui = useUI(); diff --git a/packages/react/src/auth/screens/oauth-screen.tsx b/packages/react/src/auth/screens/oauth-screen.tsx index c3591e370..86d551002 100644 --- a/packages/react/src/auth/screens/oauth-screen.tsx +++ b/packages/react/src/auth/screens/oauth-screen.tsx @@ -23,10 +23,20 @@ import { Policies } from "~/components/policies"; import { MultiFactorAuthAssertionScreen } from "./multi-factor-auth-assertion-screen"; import { RedirectError } from "~/components/redirect-error"; +/** Props for the OAuthScreen component. */ export type OAuthScreenProps = PropsWithChildren<{ + /** Callback function called when sign-in is successful. */ onSignIn?: (user: User) => void; }>; +/** + * A screen component for OAuth provider authentication. + * + * Displays a card that should contain OAuth sign-in buttons as children. + * Handles multi-factor authentication if required. + * + * @returns The OAuth screen component. + */ export function OAuthScreen({ children, onSignIn }: OAuthScreenProps) { const ui = useUI(); diff --git a/packages/react/src/auth/screens/phone-auth-screen.tsx b/packages/react/src/auth/screens/phone-auth-screen.tsx index 53436370b..095e6558b 100644 --- a/packages/react/src/auth/screens/phone-auth-screen.tsx +++ b/packages/react/src/auth/screens/phone-auth-screen.tsx @@ -24,10 +24,19 @@ import { MultiFactorAuthAssertionScreen } from "./multi-factor-auth-assertion-sc import { RedirectError } from "~/components/redirect-error"; import type { User } from "firebase/auth"; +/** Props for the PhoneAuthScreen component. */ export type PhoneAuthScreenProps = PropsWithChildren<{ + /** Callback function called when sign-in is successful. */ onSignIn?: (user: User) => void; }>; +/** + * A screen component for phone authentication. + * + * Displays a card with the phone auth form and handles multi-factor authentication if required. + * + * @returns The phone auth screen component. + */ export function PhoneAuthScreen({ children, ...props }: PhoneAuthScreenProps) { const ui = useUI(); diff --git a/packages/react/src/auth/screens/sign-in-auth-screen.tsx b/packages/react/src/auth/screens/sign-in-auth-screen.tsx index 0da74d2ff..a56af1a2d 100644 --- a/packages/react/src/auth/screens/sign-in-auth-screen.tsx +++ b/packages/react/src/auth/screens/sign-in-auth-screen.tsx @@ -24,10 +24,19 @@ import { SignInAuthForm, type SignInAuthFormProps } from "../forms/sign-in-auth- import { MultiFactorAuthAssertionScreen } from "./multi-factor-auth-assertion-screen"; import { RedirectError } from "~/components/redirect-error"; +/** Props for the SignInAuthScreen component. */ export type SignInAuthScreenProps = PropsWithChildren> & { + /** Callback function called when sign-in is successful. */ onSignIn?: (user: User) => void; }; +/** + * A screen component for signing in with email and password. + * + * Displays a card with the sign-in form and handles multi-factor authentication if required. + * + * @returns The sign-in screen component. + */ export function SignInAuthScreen({ children, onSignIn, ...props }: SignInAuthScreenProps) { const ui = useUI(); diff --git a/packages/react/src/auth/screens/sign-up-auth-screen.tsx b/packages/react/src/auth/screens/sign-up-auth-screen.tsx index 5dac1981c..09a6115cf 100644 --- a/packages/react/src/auth/screens/sign-up-auth-screen.tsx +++ b/packages/react/src/auth/screens/sign-up-auth-screen.tsx @@ -24,10 +24,19 @@ import { getTranslation } from "@invertase/firebaseui-core"; import { RedirectError } from "~/components/redirect-error"; import { MultiFactorAuthAssertionScreen } from "./multi-factor-auth-assertion-screen"; +/** Props for the SignUpAuthScreen component. */ export type SignUpAuthScreenProps = PropsWithChildren> & { + /** Callback function called when sign-up is successful. */ onSignUp?: (user: User) => void; }; +/** + * A screen component for signing up with email and password. + * + * Displays a card with the sign-up form and handles multi-factor authentication if required. + * + * @returns The sign-up screen component. + */ export function SignUpAuthScreen({ children, onSignUp, ...props }: SignUpAuthScreenProps) { const ui = useUI(); diff --git a/packages/react/src/components/button.tsx b/packages/react/src/components/button.tsx index b7445c57e..3e6813155 100644 --- a/packages/react/src/components/button.tsx +++ b/packages/react/src/components/button.tsx @@ -19,11 +19,19 @@ import { Slot } from "@radix-ui/react-slot"; import { buttonVariant, type ButtonVariant } from "@invertase/firebaseui-styles"; import { cn } from "~/utils/cn"; +/** Props for the Button component. */ export type ButtonProps = ComponentProps<"button"> & { + /** The visual variant of the button. */ variant?: ButtonVariant; + /** If true, the button will render as a child component using Radix UI's Slot. */ asChild?: boolean; }; +/** + * A customizable button component with multiple variants. + * + * @returns The button component. + */ export function Button({ className, variant = "primary", asChild, ...props }: ButtonProps) { const Comp = asChild ? Slot : "button"; return ; diff --git a/packages/react/src/components/card.tsx b/packages/react/src/components/card.tsx index c382af907..6f485a08e 100644 --- a/packages/react/src/components/card.tsx +++ b/packages/react/src/components/card.tsx @@ -17,8 +17,14 @@ import type { ComponentProps, PropsWithChildren } from "react"; import { cn } from "~/utils/cn"; +/** Props for the Card component. */ export type CardProps = PropsWithChildren>; +/** + * A card container component for grouping related content. + * + * @returns The card component. + */ export function Card({ children, className, ...props }: CardProps) { return (
@@ -27,6 +33,11 @@ export function Card({ children, className, ...props }: CardProps) { ); } +/** + * The header section of a card. + * + * @returns The card header component. + */ export function CardHeader({ children, className, ...props }: CardProps) { return (
@@ -35,6 +46,11 @@ export function CardHeader({ children, className, ...props }: CardProps) { ); } +/** + * The title of a card. + * + * @returns The card title component. + */ export function CardTitle({ children, className, ...props }: ComponentProps<"h2">) { return (

@@ -43,6 +59,11 @@ export function CardTitle({ children, className, ...props }: ComponentProps<"h2" ); } +/** + * The subtitle of a card. + * + * @returns The card subtitle component. + */ export function CardSubtitle({ children, className, ...props }: ComponentProps<"p">) { return (

@@ -51,6 +72,11 @@ export function CardSubtitle({ children, className, ...props }: ComponentProps<" ); } +/** + * The content section of a card. + * + * @returns The card content component. + */ export function CardContent({ children, className, ...props }: ComponentProps<"div">) { return (

diff --git a/packages/react/src/components/country-selector.tsx b/packages/react/src/components/country-selector.tsx index c6c8a85cb..bfa4bfbdc 100644 --- a/packages/react/src/components/country-selector.tsx +++ b/packages/react/src/components/country-selector.tsx @@ -21,23 +21,45 @@ import { type ComponentProps, forwardRef, useImperativeHandle, useState, useCall import { useUI } from "~/hooks"; import { cn } from "~/utils/cn"; +/** Ref methods for the CountrySelector component. */ export interface CountrySelectorRef { + /** Gets the currently selected country. */ getCountry: () => CountryData; + /** Sets the selected country by country code. */ setCountry: (code: CountryCode) => void; } +/** Props for the CountrySelector component. */ export type CountrySelectorProps = ComponentProps<"div">; +/** + * Gets the list of allowed countries from the country codes behavior. + * + * @returns The list of allowed countries. + */ export function useCountries() { const ui = useUI(); return getBehavior(ui, "countryCodes")().allowedCountries; } +/** + * Gets the default country from the country codes behavior. + * + * @returns The default country data. + */ export function useDefaultCountry() { const ui = useUI(); return getBehavior(ui, "countryCodes")().defaultCountry; } +/** + * A country selector component for phone number input. + * + * Displays a dropdown with country flags, dial codes, and names for selecting a country. + * + * @param ref - A ref to access the country selector methods. + * @returns The country selector component. + */ export const CountrySelector = forwardRef(({ className, ...props }, ref) => { const countries = useCountries(); const defaultCountry = useDefaultCountry(); diff --git a/packages/react/src/components/divider.tsx b/packages/react/src/components/divider.tsx index f7258eccc..03f030f07 100644 --- a/packages/react/src/components/divider.tsx +++ b/packages/react/src/components/divider.tsx @@ -17,8 +17,14 @@ import { type ComponentProps, type PropsWithChildren } from "react"; import { cn } from "~/utils/cn"; +/** Props for the Divider component. */ export type DividerProps = PropsWithChildren>; +/** + * A divider component that can display a line or a line with text in the middle. + * + * @returns The divider component. + */ export function Divider({ className, children, ...props }: DividerProps) { if (!children) { return ( diff --git a/packages/react/src/components/form.test.tsx b/packages/react/src/components/form.test.tsx index db1557dff..c609c80ae 100644 --- a/packages/react/src/components/form.test.tsx +++ b/packages/react/src/components/form.test.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { describe, it, expect, afterEach, vi, beforeEach } from "vitest"; import { render, screen, cleanup, renderHook, act, waitFor } from "@testing-library/react"; import { form } from "./form"; diff --git a/packages/react/src/components/form.tsx b/packages/react/src/components/form.tsx index a27e9501f..897cf01ee 100644 --- a/packages/react/src/components/form.tsx +++ b/packages/react/src/components/form.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { type ComponentProps, type PropsWithChildren, type ReactNode } from "react"; import { type AnyFieldApi, createFormHook, createFormHookContexts } from "@tanstack/react-form"; import { Button } from "./button"; @@ -91,6 +107,12 @@ function ErrorMessage() { ); } +/** + * A form hook factory for creating forms with validation and error handling. + * + * Provides field components (Input) and form components (SubmitButton, ErrorMessage, Action) + * for building accessible forms with TanStack Form. + */ export const form = createFormHook({ fieldComponents: { Input, diff --git a/packages/react/src/components/logos/apple/Logo.tsx b/packages/react/src/components/logos/apple/Logo.tsx index 844d6c48c..760b9c761 100644 --- a/packages/react/src/components/logos/apple/Logo.tsx +++ b/packages/react/src/components/logos/apple/Logo.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import type { SVGProps } from "react"; const SvgLogo = (props: SVGProps) => ( diff --git a/packages/react/src/components/logos/facebook/Logo.tsx b/packages/react/src/components/logos/facebook/Logo.tsx index 19c6abd48..2b2f78126 100644 --- a/packages/react/src/components/logos/facebook/Logo.tsx +++ b/packages/react/src/components/logos/facebook/Logo.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import type { SVGProps } from "react"; const SvgLogo = (props: SVGProps) => ( diff --git a/packages/react/src/components/logos/github/Logo.tsx b/packages/react/src/components/logos/github/Logo.tsx index 207382960..6991e839b 100644 --- a/packages/react/src/components/logos/github/Logo.tsx +++ b/packages/react/src/components/logos/github/Logo.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import type { SVGProps } from "react"; const SvgLogo = (props: SVGProps) => ( diff --git a/packages/react/src/components/logos/google/Logo.tsx b/packages/react/src/components/logos/google/Logo.tsx index f8d84c7eb..142ecbc90 100644 --- a/packages/react/src/components/logos/google/Logo.tsx +++ b/packages/react/src/components/logos/google/Logo.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import type { SVGProps } from "react"; const SvgLogo = (props: SVGProps) => ( diff --git a/packages/react/src/components/logos/line/Logo.tsx b/packages/react/src/components/logos/line/Logo.tsx index ec6db6eba..b7f22ce61 100644 --- a/packages/react/src/components/logos/line/Logo.tsx +++ b/packages/react/src/components/logos/line/Logo.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import type { SVGProps } from "react"; const SvgLogo = (props: SVGProps) => ( diff --git a/packages/react/src/components/logos/microsoft/Logo.tsx b/packages/react/src/components/logos/microsoft/Logo.tsx index 1427b32e0..d0cf0f1cc 100644 --- a/packages/react/src/components/logos/microsoft/Logo.tsx +++ b/packages/react/src/components/logos/microsoft/Logo.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import type { SVGProps } from "react"; const SvgLogo = (props: SVGProps) => ( diff --git a/packages/react/src/components/logos/snapchat/Logo.tsx b/packages/react/src/components/logos/snapchat/Logo.tsx index ad751c49a..d4053b853 100644 --- a/packages/react/src/components/logos/snapchat/Logo.tsx +++ b/packages/react/src/components/logos/snapchat/Logo.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import type { SVGProps } from "react"; const SvgLogo = (props: SVGProps) => ( diff --git a/packages/react/src/components/logos/twitter/Logo.tsx b/packages/react/src/components/logos/twitter/Logo.tsx index dc360cae9..461129238 100644 --- a/packages/react/src/components/logos/twitter/Logo.tsx +++ b/packages/react/src/components/logos/twitter/Logo.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import type { SVGProps } from "react"; const SvgLogo = (props: SVGProps) => ( diff --git a/packages/react/src/components/policies.tsx b/packages/react/src/components/policies.tsx index 67779887c..d3ae91061 100644 --- a/packages/react/src/components/policies.tsx +++ b/packages/react/src/components/policies.tsx @@ -18,20 +18,40 @@ import { getTranslation } from "@invertase/firebaseui-core"; import { cloneElement, createContext, useContext } from "react"; import { useUI } from "~/hooks"; +/** A URL for a policy document (terms of service or privacy policy). */ export type PolicyURL = string | URL; +/** Configuration for terms of service and privacy policy links. */ export interface PolicyProps { + /** The URL to the terms of service page. */ termsOfServiceUrl: PolicyURL; + /** The URL to the privacy policy page. */ privacyPolicyUrl: PolicyURL; + /** Optional callback function for handling navigation to policy pages. */ onNavigate?: (url: PolicyURL) => void; } export const PolicyContext = createContext(undefined); +/** + * Provides policy configuration to child components. + * + * @returns The policy provider component. + */ export function PolicyProvider({ children, policies }: { children: React.ReactNode; policies?: PolicyProps }) { return {children}; } +/** + * Displays terms of service and privacy policy links. + * + * The links are formatted according to the translated message template which may contain + * placeholders like {tos} and {privacy}. + * + * Returns null if no policy configuration is provided. + * + * @returns The policies component, or null if no policies are configured. + */ export function Policies() { const ui = useUI(); const policies = useContext(PolicyContext); diff --git a/packages/react/src/components/redirect-error.tsx b/packages/react/src/components/redirect-error.tsx index fd747536a..7f2cea41f 100644 --- a/packages/react/src/components/redirect-error.tsx +++ b/packages/react/src/components/redirect-error.tsx @@ -1,5 +1,28 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { useRedirectError } from "~/hooks"; +/** + * Displays any error that occurred during a redirect-based authentication flow. + * + * Returns null if there is no redirect error. + * + * @returns The redirect error component, or null if there is no error. + */ export function RedirectError() { const error = useRedirectError(); diff --git a/packages/react/src/context.tsx b/packages/react/src/context.tsx index 21697d7dd..63abe9bdf 100644 --- a/packages/react/src/context.tsx +++ b/packages/react/src/context.tsx @@ -21,12 +21,23 @@ import { createContext } from "react"; export const FirebaseUIContext = createContext(null as unknown as FirebaseUI); +/** Props for the FirebaseUIProvider component. */ export type FirebaseUIProviderProps = { + /** The child components to render. */ children: React.ReactNode; + /** The FirebaseUI store instance. */ ui: FirebaseUIStore; + /** Optional policy configuration for terms of service and privacy policy links. */ policies?: PolicyProps; }; +/** + * Provides FirebaseUI context to all child components. + * + * This provider must wrap your application or the components that use FirebaseUI hooks. + * + * @returns The provider component. + */ export function FirebaseUIProvider({ children, ui, policies }: FirebaseUIProviderProps) { const value = useStore(ui); return ( diff --git a/packages/react/src/hooks.ts b/packages/react/src/hooks.ts index b86791d49..b220c10be 100644 --- a/packages/react/src/hooks.ts +++ b/packages/react/src/hooks.ts @@ -32,7 +32,10 @@ import { import { FirebaseUIContext } from "./context"; /** - * Get the UI configuration from the context. + * Gets the FirebaseUI instance from the React context. + * + * @returns The FirebaseUI instance from the context. + * @throws {Error} Throws an error if the hook is used outside of a FirebaseUIProvider. */ export function useUI() { const ui = useContext(FirebaseUIContext); @@ -53,6 +56,11 @@ const ui = initializeUI(...); return ui; } +/** + * Sets up a callback that is called when a user is authenticated (non-anonymous). + * + * @param callback - Optional callback function that receives the authenticated user. + */ export function useOnUserAuthenticated(callback?: (user: User) => void) { const ui = useUI(); const auth = ui.auth; @@ -66,6 +74,11 @@ export function useOnUserAuthenticated(callback?: (user: User) => void) { }, [auth, callback]); } +/** + * Gets the redirect error message, if any, from the FirebaseUI instance. + * + * @returns The error message as a string, or undefined if there is no redirect error. + */ export function useRedirectError() { const ui = useUI(); return useMemo(() => { @@ -77,56 +90,114 @@ export function useRedirectError() { }, [ui.redirectError]); } +/** + * Gets a memoized Zod schema for sign-in form validation. + * + * @returns A Zod schema for sign-in form validation. + */ export function useSignInAuthFormSchema() { const ui = useUI(); return useMemo(() => createSignInAuthFormSchema(ui), [ui]); } +/** + * Gets a memoized Zod schema for sign-up form validation. + * + * @returns A Zod schema for sign-up form validation. + */ export function useSignUpAuthFormSchema() { const ui = useUI(); return useMemo(() => createSignUpAuthFormSchema(ui), [ui]); } +/** + * Gets a memoized Zod schema for forgot password form validation. + * + * @returns A Zod schema for forgot password form validation. + */ export function useForgotPasswordAuthFormSchema() { const ui = useUI(); return useMemo(() => createForgotPasswordAuthFormSchema(ui), [ui]); } +/** + * Gets a memoized Zod schema for email link authentication form validation. + * + * @returns A Zod schema for email link authentication form validation. + */ export function useEmailLinkAuthFormSchema() { const ui = useUI(); return useMemo(() => createEmailLinkAuthFormSchema(ui), [ui]); } +/** + * Gets a memoized Zod schema for phone number form validation. + * + * @returns A Zod schema for phone number form validation. + */ export function usePhoneAuthNumberFormSchema() { const ui = useUI(); return useMemo(() => createPhoneAuthNumberFormSchema(ui), [ui]); } +/** + * Gets a memoized Zod schema for phone verification code form validation. + * + * @returns A Zod schema for phone verification form validation. + */ export function usePhoneAuthVerifyFormSchema() { const ui = useUI(); return useMemo(() => createPhoneAuthVerifyFormSchema(ui), [ui]); } +/** + * Gets a memoized Zod schema for multi-factor phone authentication number form validation. + * + * @returns A Zod schema for multi-factor phone authentication number form validation. + */ export function useMultiFactorPhoneAuthNumberFormSchema() { const ui = useUI(); return useMemo(() => createMultiFactorPhoneAuthNumberFormSchema(ui), [ui]); } +/** + * Gets a memoized Zod schema for multi-factor phone authentication verification form validation. + * + * @returns A Zod schema for multi-factor phone authentication verification form validation. + */ export function useMultiFactorPhoneAuthVerifyFormSchema() { const ui = useUI(); return useMemo(() => createMultiFactorPhoneAuthVerifyFormSchema(ui), [ui]); } +/** + * Gets a memoized Zod schema for multi-factor TOTP authentication number form validation. + * + * @returns A Zod schema for multi-factor TOTP authentication number form validation. + */ export function useMultiFactorTotpAuthNumberFormSchema() { const ui = useUI(); return useMemo(() => createMultiFactorTotpAuthNumberFormSchema(ui), [ui]); } +/** + * Gets a memoized Zod schema for multi-factor TOTP authentication verification form validation. + * + * @returns A Zod schema for multi-factor TOTP authentication verification form validation. + */ export function useMultiFactorTotpAuthVerifyFormSchema() { const ui = useUI(); return useMemo(() => createMultiFactorTotpAuthVerifyFormSchema(ui), [ui]); } +/** + * Creates and manages a reCAPTCHA verifier instance for phone authentication. + * + * The verifier is automatically rendered to the provided element and cleaned up when the element changes. + * + * @param ref - A React ref to the HTML element where the reCAPTCHA should be rendered. + * @returns The reCAPTCHA verifier instance, or null if the element is not available. + */ export function useRecaptchaVerifier(ref: React.RefObject) { const ui = useUI(); const verifierRef = useRef(null); diff --git a/packages/react/src/utils/cn.ts b/packages/react/src/utils/cn.ts index cb2771ed2..742f7ba50 100644 --- a/packages/react/src/utils/cn.ts +++ b/packages/react/src/utils/cn.ts @@ -17,6 +17,14 @@ import { twMerge } from "tailwind-merge"; import { clsx } from "clsx"; +/** + * Utility function for merging and deduplicating CSS class names. + * + * Combines clsx for conditional class names with tailwind-merge to handle Tailwind CSS class conflicts. + * + * @param inputs - Variable number of class name arguments (strings, objects, arrays, etc.). + * @returns A merged and deduplicated string of class names. + */ export function cn(...inputs: Parameters) { return twMerge(clsx(...inputs)); } diff --git a/packages/react/tests/utils.tsx b/packages/react/tests/utils.tsx index a9956ed2d..16da68d0c 100644 --- a/packages/react/tests/utils.tsx +++ b/packages/react/tests/utils.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import type { FirebaseApp } from "firebase/app"; import type { Auth } from "firebase/auth"; import { enUs } from "@invertase/firebaseui-translations"; diff --git a/packages/shadcn/build.ts b/packages/shadcn/build.ts index 758bc36a8..cfd2254ac 100644 --- a/packages/shadcn/build.ts +++ b/packages/shadcn/build.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import parser from "yargs-parser"; import fs from "fs"; import path from "path"; diff --git a/packages/shadcn/registry-spec.test.ts b/packages/shadcn/registry-spec.test.ts index 02eb72d61..b8ee380bd 100644 --- a/packages/shadcn/registry-spec.test.ts +++ b/packages/shadcn/registry-spec.test.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { describe, it, expect, vi, afterEach, beforeEach } from "vitest"; import fs from "fs"; import { fail } from "assert"; diff --git a/packages/shadcn/src/components/apple-sign-in-button.tsx b/packages/shadcn/src/components/apple-sign-in-button.tsx index beebd8c1d..4aa656fff 100644 --- a/packages/shadcn/src/components/apple-sign-in-button.tsx +++ b/packages/shadcn/src/components/apple-sign-in-button.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import { OAuthProvider } from "firebase/auth"; diff --git a/packages/shadcn/src/components/country-selector.tsx b/packages/shadcn/src/components/country-selector.tsx index 371c48407..aa9c38e39 100644 --- a/packages/shadcn/src/components/country-selector.tsx +++ b/packages/shadcn/src/components/country-selector.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import { forwardRef, useCallback, useImperativeHandle, useState } from "react"; diff --git a/packages/shadcn/src/components/email-link-auth-form.tsx b/packages/shadcn/src/components/email-link-auth-form.tsx index eea16df10..f34c7a304 100644 --- a/packages/shadcn/src/components/email-link-auth-form.tsx +++ b/packages/shadcn/src/components/email-link-auth-form.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import { standardSchemaResolver } from "@hookform/resolvers/standard-schema"; diff --git a/packages/shadcn/src/components/email-link-auth-screen.tsx b/packages/shadcn/src/components/email-link-auth-screen.tsx index 171a55de7..dcb962dfa 100644 --- a/packages/shadcn/src/components/email-link-auth-screen.tsx +++ b/packages/shadcn/src/components/email-link-auth-screen.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import { getTranslation } from "@invertase/firebaseui-core"; diff --git a/packages/shadcn/src/components/facebook-sign-in-button.tsx b/packages/shadcn/src/components/facebook-sign-in-button.tsx index 04aba6ac8..0afdc44a8 100644 --- a/packages/shadcn/src/components/facebook-sign-in-button.tsx +++ b/packages/shadcn/src/components/facebook-sign-in-button.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import { FacebookAuthProvider } from "firebase/auth"; diff --git a/packages/shadcn/src/components/forgot-password-auth-form.tsx b/packages/shadcn/src/components/forgot-password-auth-form.tsx index 28c721b68..c7fad3fe8 100644 --- a/packages/shadcn/src/components/forgot-password-auth-form.tsx +++ b/packages/shadcn/src/components/forgot-password-auth-form.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import type { ForgotPasswordAuthFormSchema } from "@invertase/firebaseui-core"; diff --git a/packages/shadcn/src/components/forgot-password-auth-screen.tsx b/packages/shadcn/src/components/forgot-password-auth-screen.tsx index 98991d51d..6f92dd180 100644 --- a/packages/shadcn/src/components/forgot-password-auth-screen.tsx +++ b/packages/shadcn/src/components/forgot-password-auth-screen.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import { getTranslation } from "@invertase/firebaseui-core"; diff --git a/packages/shadcn/src/components/github-sign-in-button.tsx b/packages/shadcn/src/components/github-sign-in-button.tsx index a1f1ca130..094160352 100644 --- a/packages/shadcn/src/components/github-sign-in-button.tsx +++ b/packages/shadcn/src/components/github-sign-in-button.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import { GithubAuthProvider } from "firebase/auth"; diff --git a/packages/shadcn/src/components/google-sign-in-button.tsx b/packages/shadcn/src/components/google-sign-in-button.tsx index 2e648fa60..3cb91d41f 100644 --- a/packages/shadcn/src/components/google-sign-in-button.tsx +++ b/packages/shadcn/src/components/google-sign-in-button.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import { GoogleAuthProvider } from "firebase/auth"; diff --git a/packages/shadcn/src/components/microsoft-sign-in-button.tsx b/packages/shadcn/src/components/microsoft-sign-in-button.tsx index 916c4c2ad..b2aa2a3c0 100644 --- a/packages/shadcn/src/components/microsoft-sign-in-button.tsx +++ b/packages/shadcn/src/components/microsoft-sign-in-button.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import { OAuthProvider } from "firebase/auth"; diff --git a/packages/shadcn/src/components/multi-factor-auth-assertion-form.test.tsx b/packages/shadcn/src/components/multi-factor-auth-assertion-form.test.tsx index 86a1fda2c..e3995cfb6 100644 --- a/packages/shadcn/src/components/multi-factor-auth-assertion-form.test.tsx +++ b/packages/shadcn/src/components/multi-factor-auth-assertion-form.test.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { render, screen, fireEvent, cleanup } from "@testing-library/react"; import { MultiFactorAuthAssertionForm } from "./multi-factor-auth-assertion-form"; diff --git a/packages/shadcn/src/components/multi-factor-auth-assertion-form.tsx b/packages/shadcn/src/components/multi-factor-auth-assertion-form.tsx index 4ad2eeeb8..24930fb1d 100644 --- a/packages/shadcn/src/components/multi-factor-auth-assertion-form.tsx +++ b/packages/shadcn/src/components/multi-factor-auth-assertion-form.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import { getTranslation } from "@invertase/firebaseui-core"; diff --git a/packages/shadcn/src/components/multi-factor-auth-assertion-screen.tsx b/packages/shadcn/src/components/multi-factor-auth-assertion-screen.tsx index 17a1dda7d..b841181c3 100644 --- a/packages/shadcn/src/components/multi-factor-auth-assertion-screen.tsx +++ b/packages/shadcn/src/components/multi-factor-auth-assertion-screen.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import { getTranslation } from "@invertase/firebaseui-core"; diff --git a/packages/shadcn/src/components/multi-factor-auth-enrollment-form.tsx b/packages/shadcn/src/components/multi-factor-auth-enrollment-form.tsx index 5935c159e..451757dfd 100644 --- a/packages/shadcn/src/components/multi-factor-auth-enrollment-form.tsx +++ b/packages/shadcn/src/components/multi-factor-auth-enrollment-form.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import { type ComponentProps, useState } from "react"; diff --git a/packages/shadcn/src/components/multi-factor-auth-enrollment-screen.tsx b/packages/shadcn/src/components/multi-factor-auth-enrollment-screen.tsx index c226b87ea..eba761160 100644 --- a/packages/shadcn/src/components/multi-factor-auth-enrollment-screen.tsx +++ b/packages/shadcn/src/components/multi-factor-auth-enrollment-screen.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import { getTranslation } from "@invertase/firebaseui-core"; diff --git a/packages/shadcn/src/components/oauth-button.tsx b/packages/shadcn/src/components/oauth-button.tsx index 548d32a64..2d59b3553 100644 --- a/packages/shadcn/src/components/oauth-button.tsx +++ b/packages/shadcn/src/components/oauth-button.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import { useUI, type OAuthButtonProps, useSignInWithProvider } from "@invertase/firebaseui-react"; diff --git a/packages/shadcn/src/components/oauth-screen.tsx b/packages/shadcn/src/components/oauth-screen.tsx index a586527d2..97cd600ce 100644 --- a/packages/shadcn/src/components/oauth-screen.tsx +++ b/packages/shadcn/src/components/oauth-screen.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import { getTranslation } from "@invertase/firebaseui-core"; diff --git a/packages/shadcn/src/components/phone-auth-form.tsx b/packages/shadcn/src/components/phone-auth-form.tsx index c1a4dd1d1..adbe84f78 100644 --- a/packages/shadcn/src/components/phone-auth-form.tsx +++ b/packages/shadcn/src/components/phone-auth-form.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import { diff --git a/packages/shadcn/src/components/phone-auth-screen.tsx b/packages/shadcn/src/components/phone-auth-screen.tsx index 7908c58c8..28d7e78e4 100644 --- a/packages/shadcn/src/components/phone-auth-screen.tsx +++ b/packages/shadcn/src/components/phone-auth-screen.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import type { PropsWithChildren } from "react"; diff --git a/packages/shadcn/src/components/policies.tsx b/packages/shadcn/src/components/policies.tsx index b0cfef637..f15645afb 100644 --- a/packages/shadcn/src/components/policies.tsx +++ b/packages/shadcn/src/components/policies.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { cn } from "@/lib/utils"; import { getTranslation } from "@invertase/firebaseui-core"; import { useUI, PolicyContext } from "@invertase/firebaseui-react"; diff --git a/packages/shadcn/src/components/redirect-error.tsx b/packages/shadcn/src/components/redirect-error.tsx index b76348cb4..3bef7da6d 100644 --- a/packages/shadcn/src/components/redirect-error.tsx +++ b/packages/shadcn/src/components/redirect-error.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import { useRedirectError } from "@invertase/firebaseui-react"; diff --git a/packages/shadcn/src/components/sign-in-auth-form.tsx b/packages/shadcn/src/components/sign-in-auth-form.tsx index 3109ba05e..71c62a4b9 100644 --- a/packages/shadcn/src/components/sign-in-auth-form.tsx +++ b/packages/shadcn/src/components/sign-in-auth-form.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import type { SignInAuthFormSchema } from "@invertase/firebaseui-core"; diff --git a/packages/shadcn/src/components/sign-in-auth-screen.tsx b/packages/shadcn/src/components/sign-in-auth-screen.tsx index 3397fac0d..ba2f8812b 100644 --- a/packages/shadcn/src/components/sign-in-auth-screen.tsx +++ b/packages/shadcn/src/components/sign-in-auth-screen.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import { getTranslation } from "@invertase/firebaseui-core"; diff --git a/packages/shadcn/src/components/sign-up-auth-form.tsx b/packages/shadcn/src/components/sign-up-auth-form.tsx index 565ca3430..fca4bb3ab 100644 --- a/packages/shadcn/src/components/sign-up-auth-form.tsx +++ b/packages/shadcn/src/components/sign-up-auth-form.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import type { SignUpAuthFormSchema } from "@invertase/firebaseui-core"; diff --git a/packages/shadcn/src/components/sign-up-auth-screen.tsx b/packages/shadcn/src/components/sign-up-auth-screen.tsx index f358a163e..9b0ca46ff 100644 --- a/packages/shadcn/src/components/sign-up-auth-screen.tsx +++ b/packages/shadcn/src/components/sign-up-auth-screen.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import { getTranslation } from "@invertase/firebaseui-core"; diff --git a/packages/shadcn/src/components/sms-multi-factor-assertion-form.test.tsx b/packages/shadcn/src/components/sms-multi-factor-assertion-form.test.tsx index 6e2f4b9f3..9d7e5aac7 100644 --- a/packages/shadcn/src/components/sms-multi-factor-assertion-form.test.tsx +++ b/packages/shadcn/src/components/sms-multi-factor-assertion-form.test.tsx @@ -1,10 +1,24 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { render, screen, fireEvent, cleanup, waitFor } from "@testing-library/react"; import { SmsMultiFactorAssertionForm } from "./sms-multi-factor-assertion-form"; import { createFirebaseUIProvider, createMockUI } from "../../tests/utils"; import { registerLocale } from "@invertase/firebaseui-translations"; -import { PhoneMultiFactorGenerator } from "firebase/auth"; -import { verifyPhoneNumber, signInWithMultiFactorAssertion } from "@invertase/firebaseui-core"; import { useSmsMultiFactorAssertionPhoneFormAction, useSmsMultiFactorAssertionVerifyFormAction, diff --git a/packages/shadcn/src/components/sms-multi-factor-assertion-form.tsx b/packages/shadcn/src/components/sms-multi-factor-assertion-form.tsx index 7f6c424b0..0be341003 100644 --- a/packages/shadcn/src/components/sms-multi-factor-assertion-form.tsx +++ b/packages/shadcn/src/components/sms-multi-factor-assertion-form.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import { useRef, useState } from "react"; diff --git a/packages/shadcn/src/components/sms-multi-factor-enrollment-form.tsx b/packages/shadcn/src/components/sms-multi-factor-enrollment-form.tsx index db6d3ddc7..cff880858 100644 --- a/packages/shadcn/src/components/sms-multi-factor-enrollment-form.tsx +++ b/packages/shadcn/src/components/sms-multi-factor-enrollment-form.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import { useRef, useState } from "react"; diff --git a/packages/shadcn/src/components/totp-multi-factor-assertion-form.test.tsx b/packages/shadcn/src/components/totp-multi-factor-assertion-form.test.tsx index f18b269d9..6bcdcae72 100644 --- a/packages/shadcn/src/components/totp-multi-factor-assertion-form.test.tsx +++ b/packages/shadcn/src/components/totp-multi-factor-assertion-form.test.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { render, screen, fireEvent, cleanup, waitFor } from "@testing-library/react"; import { TotpMultiFactorAssertionForm } from "./totp-multi-factor-assertion-form"; diff --git a/packages/shadcn/src/components/totp-multi-factor-assertion-form.tsx b/packages/shadcn/src/components/totp-multi-factor-assertion-form.tsx index 1c0324c52..a960d12f1 100644 --- a/packages/shadcn/src/components/totp-multi-factor-assertion-form.tsx +++ b/packages/shadcn/src/components/totp-multi-factor-assertion-form.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import { type UserCredential, type MultiFactorInfo } from "firebase/auth"; diff --git a/packages/shadcn/src/components/totp-multi-factor-enrollment-form.tsx b/packages/shadcn/src/components/totp-multi-factor-enrollment-form.tsx index bd9aa2f84..734ebef61 100644 --- a/packages/shadcn/src/components/totp-multi-factor-enrollment-form.tsx +++ b/packages/shadcn/src/components/totp-multi-factor-enrollment-form.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import { useState } from "react"; diff --git a/packages/shadcn/src/components/twitter-sign-in-button.tsx b/packages/shadcn/src/components/twitter-sign-in-button.tsx index 7a3ee30ff..de379fc0b 100644 --- a/packages/shadcn/src/components/twitter-sign-in-button.tsx +++ b/packages/shadcn/src/components/twitter-sign-in-button.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + "use client"; import { TwitterAuthProvider } from "firebase/auth"; diff --git a/packages/shadcn/src/lib/utils.ts b/packages/shadcn/src/lib/utils.ts index a5ef19350..c57a731e8 100644 --- a/packages/shadcn/src/lib/utils.ts +++ b/packages/shadcn/src/lib/utils.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { clsx, type ClassValue } from "clsx"; import { twMerge } from "tailwind-merge"; diff --git a/packages/shadcn/tests/utils.tsx b/packages/shadcn/tests/utils.tsx index 0d232fbce..12f571779 100644 --- a/packages/shadcn/tests/utils.tsx +++ b/packages/shadcn/tests/utils.tsx @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import type { FirebaseApp } from "firebase/app"; import type { Auth } from "firebase/auth"; import { enUs } from "@invertase/firebaseui-translations"; diff --git a/packages/shadcn/vite.config.ts b/packages/shadcn/vite.config.ts index f14c3ef93..85684f436 100644 --- a/packages/shadcn/vite.config.ts +++ b/packages/shadcn/vite.config.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import tailwindcss from "@tailwindcss/vite"; import react from "@vitejs/plugin-react"; import path from "path"; diff --git a/packages/styles/src/index.ts b/packages/styles/src/index.ts index be69079ea..f743df957 100644 --- a/packages/styles/src/index.ts +++ b/packages/styles/src/index.ts @@ -1,5 +1,26 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { cva, type VariantProps } from "cva"; +/** + * Class variance authority configuration for button variants. + * + * Defines the CSS class variants for buttons with primary and secondary styles. + */ export const buttonVariant = cva({ base: "fui-button", variants: { @@ -13,4 +34,5 @@ export const buttonVariant = cva({ }, }); +/** Button variant type derived from the buttonVariant configuration. */ export type ButtonVariant = VariantProps["variant"]; diff --git a/packages/translations/src/index.ts b/packages/translations/src/index.ts index f7c085700..d6fa3bba7 100644 --- a/packages/translations/src/index.ts +++ b/packages/translations/src/index.ts @@ -20,8 +20,17 @@ import { type Translations } from "./types"; export type * from "./types"; export * from "./mapping"; +/** Locale identifier string. Supports BCP 47 format (e.g., "en-US") or any string. */ export type Locale = "en-US" | `${string}-${string}` | string; +/** + * Registers a locale with its translations and optional fallback locale. + * + * @param locale - The locale identifier (e.g., "en-US", "fr-FR"). + * @param translations - The translation object for this locale. + * @param fallback - Optional fallback locale to use when a translation is missing. + * @returns A registered locale object. + */ export function registerLocale( locale: Locale, translations: Translations, @@ -34,10 +43,15 @@ export function registerLocale( }; } +/** Pre-registered English US locale with default translations. */ export const enUs = registerLocale("en-US", enUS); +/** A registered locale with its translations and optional fallback. */ export type RegisteredLocale = { + /** The locale identifier. */ locale: Locale; + /** The translation object for this locale. */ translations: Translations; + /** Optional fallback locale to use when a translation is missing. */ fallback?: RegisteredLocale; }; diff --git a/packages/translations/src/locales/en-us.ts b/packages/translations/src/locales/en-us.ts index 8df880f66..2cb38358d 100644 --- a/packages/translations/src/locales/en-us.ts +++ b/packages/translations/src/locales/en-us.ts @@ -16,6 +16,7 @@ import { type Translations } from "../types"; +/** English US (en-US) translation set with all default translations. */ export const enUS = { errors: { userNotFound: "No account found with this email address", diff --git a/packages/translations/src/mapping.ts b/packages/translations/src/mapping.ts index 41fd2195f..0ff0eaf12 100644 --- a/packages/translations/src/mapping.ts +++ b/packages/translations/src/mapping.ts @@ -18,6 +18,7 @@ import { enUS } from "./locales/en-us"; import { type RegisteredLocale } from "."; import type { ErrorKey, TranslationCategory, TranslationKey, TranslationSet } from "./types"; +/** Maps Firebase authentication error codes to translation keys. */ export const ERROR_CODE_MAP = { "auth/user-not-found": "userNotFound", "auth/wrong-password": "wrongPassword", @@ -48,8 +49,21 @@ export const ERROR_CODE_MAP = { "auth/second-factor-already-in-use": "secondFactorAlreadyInUse", } satisfies Record; +/** Firebase authentication error code type. */ export type ErrorCode = keyof typeof ERROR_CODE_MAP; +/** + * Retrieves a translation string for a given locale, category, and key. + * + * Falls back to the locale's fallback locale or English US if the translation is not found. + * Supports string replacements using {placeholder} syntax. + * + * @param locale - The registered locale to get the translation from. + * @param category - The translation category (e.g., "errors", "labels"). + * @param key - The translation key within the category. + * @param replacements - Optional object with replacement values for placeholders in the translation string. + * @returns The translated string, or an empty string if not found. + */ export function getTranslation( locale: RegisteredLocale, category: T, diff --git a/packages/translations/src/types.ts b/packages/translations/src/types.ts index 8c3d17214..26e4ce82b 100644 --- a/packages/translations/src/types.ts +++ b/packages/translations/src/types.ts @@ -14,103 +14,204 @@ * limitations under the License. */ +/** Category keys for translation sets (e.g., "errors", "messages", "labels", "prompts"). */ export type TranslationCategory = keyof Required; + +/** Generic type for translation keys within a specific category. */ export type TranslationKey = keyof Required[T]; + +/** Record type representing a complete set of translations for a specific category. */ export type TranslationSet = Record, string>; + +/** Keys for error translation messages. */ export type ErrorKey = keyof Required["errors"]; + +/** Keys for informational message translations. */ export type MessageKey = keyof Required["messages"]; + +/** Keys for UI label translations. */ export type LabelKey = keyof Required["labels"]; + +/** Keys for prompt/instruction translations. */ export type PromptKey = keyof Required["prompts"]; + +/** Configuration type for translations, mapping locale identifiers to partial translation objects. */ export type TranslationsConfig = Partial>>; +/** Complete translations interface containing all translation categories and their keys. */ export type Translations = { + /** Error message translations. */ errors?: { + /** Translation for when a user is not found. */ userNotFound?: string; + /** Translation for incorrect password. */ wrongPassword?: string; + /** Translation for invalid email address. */ invalidEmail?: string; + /** Translation for disabled user account. */ userDisabled?: string; + /** Translation for unverified email address. */ unverifiedEmail?: string; + /** Translation for network request failure. */ networkRequestFailed?: string; + /** Translation for too many requests. */ tooManyRequests?: string; + /** Translation for email already in use. */ emailAlreadyInUse?: string; + /** Translation for missing verification code. */ missingVerificationCode?: string; + /** Translation for invalid credentials. */ invalidCredential?: string; + /** Translation for weak password. */ weakPassword?: string; + /** Translation for operation not allowed. */ operationNotAllowed?: string; + /** Translation for invalid phone number. */ invalidPhoneNumber?: string; + /** Translation for missing phone number. */ missingPhoneNumber?: string; + /** Translation for SMS quota exceeded. */ quotaExceeded?: string; + /** Translation for expired verification code. */ codeExpired?: string; + /** Translation for reCAPTCHA check failure. */ captchaCheckFailed?: string; + /** Translation for missing verification ID. */ missingVerificationId?: string; + /** Translation for missing email address. */ missingEmail?: string; + /** Translation for required display name. */ displayNameRequired?: string; + /** Translation for invalid action code. */ invalidActionCode?: string; + /** Translation for credential already in use. */ credentialAlreadyInUse?: string; + /** Translation for operation requiring recent login. */ requiresRecentLogin?: string; + /** Translation for provider already linked. */ providerAlreadyLinked?: string; + /** Translation for invalid verification code. */ invalidVerificationCode?: string; + /** Translation for unknown error. */ unknownError?: string; + /** Translation for popup closed by user. */ popupClosed?: string; + /** Translation for account existing with different credential. */ accountExistsWithDifferentCredential?: string; + /** Translation for second factor already in use. */ secondFactorAlreadyInUse?: string; }; + /** Informational message translations. */ messages?: { + /** Translation for password reset email sent confirmation. */ passwordResetEmailSent?: string; + /** Translation for sign-in link sent confirmation. */ signInLinkSent?: string; + /** Translation for verification code required first. */ verificationCodeFirst?: string; + /** Translation for checking email for reset instructions. */ checkEmailForReset?: string; + /** Translation for "or" divider text. */ dividerOr?: string; + /** Translation for terms and privacy policy notice. */ termsAndPrivacy?: string; + /** Translation for MFA SMS assertion prompt message. */ mfaSmsAssertionPrompt?: string; }; + /** UI label translations. */ labels?: { + /** Translation for email address label. */ emailAddress?: string; + /** Translation for password label. */ password?: string; + /** Translation for display name label. */ displayName?: string; + /** Translation for forgot password link. */ forgotPassword?: string; + /** Translation for sign up button/link. */ signUp?: string; + /** Translation for sign in button/link. */ signIn?: string; + /** Translation for reset password button. */ resetPassword?: string; + /** Translation for create account button. */ createAccount?: string; + /** Translation for back to sign in link. */ backToSignIn?: string; + /** Translation for sign in with phone button. */ signInWithPhone?: string; + /** Translation for phone number label. */ phoneNumber?: string; + /** Translation for verification code label. */ verificationCode?: string; + /** Translation for send code button. */ sendCode?: string; + /** Translation for verify code button. */ verifyCode?: string; + /** Translation for sign in with Google button. */ signInWithGoogle?: string; + /** Translation for sign in with Facebook button. */ signInWithFacebook?: string; + /** Translation for sign in with Apple button. */ signInWithApple?: string; + /** Translation for sign in with Twitter/X button. */ signInWithTwitter?: string; + /** Translation for sign in with Microsoft button. */ signInWithMicrosoft?: string; + /** Translation for sign in with GitHub button. */ signInWithGitHub?: string; + /** Translation for sign in with email link button. */ signInWithEmailLink?: string; + /** Translation for send sign-in link button. */ sendSignInLink?: string; + /** Translation for terms of service link. */ termsOfService?: string; + /** Translation for privacy policy link. */ privacyPolicy?: string; + /** Translation for resend code button. */ resendCode?: string; + /** Translation for sending state text. */ sending?: string; + /** Translation for multi-factor enrollment label. */ multiFactorEnrollment?: string; + /** Translation for multi-factor assertion label. */ multiFactorAssertion?: string; + /** Translation for TOTP verification label. */ mfaTotpVerification?: string; + /** Translation for SMS verification label. */ mfaSmsVerification?: string; + /** Translation for generate QR code button. */ generateQrCode?: string; }; + /** Prompt and instruction translations. */ prompts?: { + /** Translation for "don't have an account" prompt. */ noAccount?: string; + /** Translation for "already have an account" prompt. */ haveAccount?: string; + /** Translation for enter email to reset password prompt. */ enterEmailToReset?: string; + /** Translation for sign in to account prompt. */ signInToAccount?: string; + /** Translation for enter details to create account prompt. */ enterDetailsToCreate?: string; + /** Translation for SMS verification code prompt. */ smsVerificationPrompt?: string; + /** Translation for enter phone number prompt. */ enterPhoneNumber?: string; + /** Translation for enter verification code prompt. */ enterVerificationCode?: string; + /** Translation for enter email for link prompt. */ enterEmailForLink?: string; + /** Translation for MFA enrollment prompt. */ mfaEnrollmentPrompt?: string; + /** Translation for MFA assertion prompt. */ mfaAssertionPrompt?: string; + /** Translation for MFA assertion factor selection prompt. */ mfaAssertionFactorPrompt?: string; + /** Translation for TOTP QR code prompt. */ mfaTotpQrCodePrompt?: string; + /** Translation for TOTP enrollment verification prompt. */ mfaTotpEnrollmentVerificationPrompt?: string; }; };