allow spaces in login and registration input fields

When users typed spaces into username, password, email, or
code fields on Canvas login and registration pages, the input
was immediately trimmed, preventing spaces from being entered.
However, copy-pasting text with spaces worked correctly.

Removed `.trim()` calls from all input change handlers in:
- SignIn (username, password)
- ForgotPassword (email)
- OtpForm (verification code)
- Student registration (username, email, join code)
- Teacher registration (email)
- Parent registration (email, pairing code)

Form validation still prevents empty/whitespace-only
submissions by using `.trim()` in the validateForm() method.

The backend applies its own normalization and validation,
ensuring data integrity.

closes AE-2112
flag=login_registration_ui_identity

[skip-crystalball]

test plan:
- type spaces in username field during login, expect spaces
  to be preserved in the input
- type spaces in password field during login, expect spaces
  to be preserved
- type spaces in email field on forgot password, expect spaces
  to be preserved
- type spaces in verification code on otp form, expect spaces
  to be preserved
- type spaces in registration fields (username, email, codes),
  expect spaces to be preserved
- submit login form with spaces in credentials, expect form to
  submit with spaces intact
- submit form with whitespace-only input, expect validation
  error
- run all new_login tests and verify existing validation tests
  still pass

Change-Id: I42fb4cdafed8a1284e8bfced8af7293171e9b651
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/400831
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Reviewed-by: Weston Dransfield <wdransfield@instructure.com>
QA-Review: Michael Hulse <michael.hulse@instructure.com>
Product-Review: Michael Hulse <michael.hulse@instructure.com>
This commit is contained in:
Michael Hulse
2026-02-11 17:10:29 -08:00
parent a4c9216469
commit 5db9f6d757
8 changed files with 19 additions and 25 deletions

View File

@@ -93,7 +93,7 @@ const ForgotPassword = () => {
}
const handleEmailChange = (_: React.ChangeEvent<HTMLInputElement>, value: string) => {
setEmail(value.trim())
setEmail(value)
}
const handleCancel = () => assignLocation(LOGIN_ENTRY_URL)

View File

@@ -157,7 +157,7 @@ const OtpForm = () => {
_event: React.ChangeEvent<HTMLInputElement>,
value: string,
) => {
setVerificationCode(value.trim())
setVerificationCode(value)
if (verificationCodeError) setVerificationCodeError('')
}

View File

@@ -150,11 +150,11 @@ const SignIn = () => {
}
const handleUsernameChange = (_event: React.ChangeEvent<HTMLInputElement>, value: string) => {
setUsername(value.trim())
setUsername(value)
}
const handlePasswordChange = (_event: React.ChangeEvent<HTMLInputElement>, value: string) => {
setPassword(value.trim())
setPassword(value)
}
if (otpRequired && !isPreviewMode) {

View File

@@ -135,11 +135,11 @@ describe('ForgotPassword', () => {
expect(emailInput).toHaveValue('test@example.com')
})
it('trims whitespace from the email input', async () => {
it('allows spaces to be typed in the email input', async () => {
setup()
const emailInput = screen.getByTestId('email-input')
await userEvent.type(emailInput, ' test@example.com ')
expect(emailInput).toHaveValue('test@example.com')
await userEvent.type(emailInput, 'test user@example.com')
expect(emailInput).toHaveValue('test user@example.com')
})
})

View File

@@ -117,24 +117,18 @@ describe('SignIn', () => {
})
describe('user input', () => {
it('trims whitespace from the username field when typing', async () => {
it('allows spaces to be typed in the username field', async () => {
setup()
const usernameInput = screen.getByTestId('username-input')
const loginButton = screen.getByTestId('login-button')
await userEvent.type(usernameInput, ' user@example.com ')
await userEvent.click(loginButton)
expect(usernameInput).toHaveValue('user@example.com')
expect(usernameInput).not.toHaveValue(' user@example.com ')
await userEvent.type(usernameInput, 'user name')
expect(usernameInput).toHaveValue('user name')
})
it('trims whitespace from the password field when typing', async () => {
it('allows spaces to be typed in the password field', async () => {
setup()
const passwordInput = screen.getByTestId('password-input')
const loginButton = screen.getByTestId('login-button')
await userEvent.type(passwordInput, ' password123 ')
await userEvent.click(loginButton)
expect(passwordInput).toHaveValue('password123')
expect(passwordInput).not.toHaveValue(' password123 ')
await userEvent.type(passwordInput, 'pass word 123')
expect(passwordInput).toHaveValue('pass word 123')
})
})
})

View File

@@ -283,7 +283,7 @@ const Parent = () => {
}
const handleEmailChange = (_: React.ChangeEvent<HTMLInputElement>, value: string) => {
setEmail(value.trim())
setEmail(value)
}
const handlePasswordChange = (_: React.ChangeEvent<HTMLInputElement>, value: string) => {
@@ -295,7 +295,7 @@ const Parent = () => {
}
const handlePairingCodeChange = (_: React.ChangeEvent<HTMLInputElement>, value: string) => {
setPairingCode(value.trim())
setPairingCode(value)
}
const handleTermsChange = (checked: boolean) => {

View File

@@ -306,7 +306,7 @@ const Student = () => {
}
const handleUsernameChange = (_: React.ChangeEvent<HTMLInputElement>, value: string) => {
setUsername(value.trim())
setUsername(value)
}
const handlePasswordChange = (_: React.ChangeEvent<HTMLInputElement>, value: string) => {
@@ -318,11 +318,11 @@ const Student = () => {
}
const handleJoinCodeChange = (_: React.ChangeEvent<HTMLInputElement>, value: string) => {
setJoinCode(value.trim())
setJoinCode(value)
}
const handleEmailChange = (_: React.ChangeEvent<HTMLInputElement>, value: string) => {
setEmail(value.trim())
setEmail(value)
}
const handleTermsChange = (checked: boolean) => {

View File

@@ -199,7 +199,7 @@ const Teacher = () => {
}
const handleEmailChange = (_: React.ChangeEvent<HTMLInputElement>, value: string) => {
setEmail(value.trim())
setEmail(value)
}
const handleNameChange = (_: React.ChangeEvent<HTMLInputElement>, value: string) => {