import { LitElement, html, nothing } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; // Import USWDS core styles // Import USWDS web components import '@uswds-wc/feedback'; import '@uswds-wc/navigation'; import '@uswds-wc/layout'; import '@uswds-wc/actions'; import '@uswds-wc/forms'; import '@uswds-wc/patterns'; /** * Create account form data interface */ export interface CreateAccountData { email: string; password: string; confirmPassword: string; firstName?: string; lastName?: string; agreeToTerms?: boolean; } /** * USA Create Account Template * * Registration page template following USWDS patterns. * Composes name and email patterns with password fields. * * **Template Structure (from USWDS):** * - Skip navigation + Banner + Header * - Registration form in centered card * - Sign-in link for existing users * - Footer + Identifier * * @element usa-create-account-template * * @slot form-header - Custom content above the form * @slot form-fields - Additional form fields * @slot form-footer - Custom content below the form (terms, links) * @slot header - Site header * @slot footer - Site footer * * @fires {CustomEvent} create-account-submit - Fired when form is submitted * @fires {CustomEvent} template-ready - Fired when template initializes * * @example Basic usage * ```html * * ``` * * @uswds-template https://designsystem.digital.gov/templates/create-account-page/ */ @customElement('usa-create-account-template') export class USACreateAccountTemplate extends LitElement { // Use Light DOM for USWDS compatibility protected override createRenderRoot() { return this; } /** * Page language attribute */ @property({ type: String }) override lang = 'en'; /** * Whether to show the government banner */ @property({ type: Boolean, attribute: 'show-banner' }) showBanner = true; /** * Whether to show the identifier at the bottom */ @property({ type: Boolean, attribute: 'show-identifier' }) showIdentifier = true; /** * Page heading */ @property({ type: String }) heading = 'Create account'; /** * Form legend (accessibility) */ @property({ type: String, attribute: 'form-legend' }) formLegend = 'Create your account'; /** * Whether to show name fields */ @property({ type: Boolean, attribute: 'show-name-fields' }) showNameFields = true; /** * Whether to show password requirements */ @property({ type: Boolean, attribute: 'show-password-requirements' }) showPasswordRequirements = true; /** * Password requirements text */ @property({ type: String, attribute: 'password-requirements-text' }) passwordRequirementsText = 'Password must be at least 8 characters and include at least one uppercase letter, one lowercase letter, one number, and one special character.'; /** * Whether to show terms checkbox */ @property({ type: Boolean, attribute: 'show-terms' }) showTerms = true; /** * Terms of service URL */ @property({ type: String, attribute: 'terms-url' }) termsUrl = '/terms'; /** * Privacy policy URL */ @property({ type: String, attribute: 'privacy-url' }) privacyUrl = '/privacy'; /** * Submit button text */ @property({ type: String, attribute: 'submit-text' }) submitText = 'Create account'; /** * Whether to show sign-in link */ @property({ type: Boolean, attribute: 'show-sign-in-link' }) showSignInLink = true; /** * Sign-in URL */ @property({ type: String, attribute: 'sign-in-url' }) signInUrl = '/sign-in'; /** * Sign-in link text */ @property({ type: String, attribute: 'sign-in-text' }) signInText = 'Sign in'; /** * Sign-in label text */ @property({ type: String, attribute: 'sign-in-label' }) signInLabel = 'Already have an account?'; /** * Form action URL */ @property({ type: String, attribute: 'action-url' }) actionUrl = ''; /** * Form method */ @property({ type: String }) method = 'POST'; /** * Current form data state */ @state() private formData: CreateAccountData = { email: '', password: '', confirmPassword: '', firstName: '', lastName: '', agreeToTerms: false, }; /** * Password mismatch error state */ @state() private passwordMismatch = false; override connectedCallback() { super.connectedCallback(); if (this.lang) { this.setAttribute('lang', this.lang); } } override firstUpdated(changedProperties: Map) { super.firstUpdated(changedProperties); this.dispatchEvent( new CustomEvent('template-ready', { detail: { template: 'create-account' }, bubbles: true, composed: true, }) ); } /** * Handle form field changes */ private handleFieldChange(field: keyof CreateAccountData, value: string | boolean) { this.formData = { ...this.formData, [field]: value }; // Check password match if (field === 'password' || field === 'confirmPassword') { this.passwordMismatch = this.formData.password !== '' && this.formData.confirmPassword !== '' && this.formData.password !== this.formData.confirmPassword; } } /** * Handle form submission */ private handleSubmit(e: Event) { e.preventDefault(); // Validate passwords match if (this.formData.password !== this.formData.confirmPassword) { this.passwordMismatch = true; return; } this.dispatchEvent( new CustomEvent('create-account-submit', { detail: { formData: { ...this.formData } }, bubbles: true, composed: true, }) ); // If action URL is set, submit the form if (this.actionUrl) { const form = e.target as HTMLFormElement; form.submit(); } } /** * Render name fields */ private renderNameFields() { if (!this.showNameFields) { return nothing; } return html`
`; } /** * Render the registration form */ private renderForm() { return html`
${this.formLegend} ${this.renderNameFields()} ${this.showPasswordRequirements ? html`
${this.passwordRequirementsText}
` : nothing} ${this.showTerms ? html` I agree to the terms of service and privacy policy ` : nothing} ${this.submitText}
`; } override render() { return html`
${this.showBanner ? html`` : nothing}

${this.heading}

${this.renderForm()}
${this.showSignInLink ? html`

${this.signInLabel} ${this.signInText}.

` : nothing}
${this.showIdentifier ? html`` : nothing}
`; } /** * Public API: Get form data */ getFormData(): CreateAccountData { return { ...this.formData }; } /** * Public API: Reset form */ resetForm(): void { this.formData = { email: '', password: '', confirmPassword: '', firstName: '', lastName: '', agreeToTerms: false, }; this.passwordMismatch = false; // Reset form elements const form = this.querySelector('form'); if (form) { form.reset(); } } /** * Public API: Validate form */ validateForm(): boolean { if (this.formData.password !== this.formData.confirmPassword) { this.passwordMismatch = true; return false; } return true; } }