Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/react-core/src/components/LoginPage/LoginForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export interface LoginFormProps extends Omit<React.HTMLProps<HTMLFormElement>, '
usernameLabel?: string;
/** Value for the username */
usernameValue?: string;
/** Autocomplete value for the username input field */
usernameAutoComplete?: string;
/** Function that handles the onChange event for the username */
onChangeUsername?: (event: React.FormEvent<HTMLInputElement>, value: string) => void;
/** Flag indicating if the username is valid */
Expand All @@ -34,6 +36,8 @@ export interface LoginFormProps extends Omit<React.HTMLProps<HTMLFormElement>, '
passwordLabel?: string;
/** Value for the password */
passwordValue?: string;
/** Autocomplete value for the password input field */
passwordAutoComplete?: string;
/** Function that handles the onChange event for the password */
onChangePassword?: (event: React.FormEvent<HTMLInputElement>, value: string) => void;
/** Flag indicating if the password is valid */
Expand Down Expand Up @@ -66,11 +70,13 @@ export const LoginForm: React.FunctionComponent<LoginFormProps> = ({
helperTextIcon = null,
usernameLabel = 'Username',
usernameValue = '',
usernameAutoComplete,
onChangeUsername = () => undefined as any,
isValidUsername = true,
isPasswordRequired = true,
passwordLabel = 'Password',
passwordValue = '',
passwordAutoComplete,
onChangePassword = () => undefined as any,
isShowPasswordEnabled = false,
hidePasswordAriaLabel = 'Hide password',
Expand All @@ -88,6 +94,7 @@ export const LoginForm: React.FunctionComponent<LoginFormProps> = ({

const passwordInput = (
<TextInput
autoComplete={passwordAutoComplete}
isRequired={isPasswordRequired}
type={passwordHidden ? 'password' : 'text'}
id="pf-login-password-id"
Expand All @@ -111,6 +118,7 @@ export const LoginForm: React.FunctionComponent<LoginFormProps> = ({
)}
<FormGroup label={usernameLabel} isRequired fieldId="pf-login-username-id">
<TextInput
autoComplete={usernameAutoComplete}
autoFocus={!noAutoFocus}
id="pf-login-username-id"
isRequired
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,59 @@ describe('LoginForm', () => {
const passwordField = screen.getByLabelText(/password/i);
expect(passwordField).not.toBeRequired();
});

test('preserves form autocomplete without setting input autocomplete by default', () => {
render(<LoginForm aria-label="Login" autoComplete="off" />);

expect(screen.getByRole('form', { name: 'Login' })).toHaveAttribute('autocomplete', 'off');
expect(screen.getByRole('textbox', { name: /username/i })).not.toHaveAttribute('autocomplete');
expect(screen.getByLabelText(/password/i)).not.toHaveAttribute('autocomplete');
});

test.each(['username', 'email', 'off'])('sets username autocomplete to %s', (usernameAutoComplete) => {
render(<LoginForm usernameAutoComplete={usernameAutoComplete} />);

expect(screen.getByRole('textbox', { name: /username/i })).toHaveAttribute('autocomplete', usernameAutoComplete);
expect(screen.getByLabelText(/password/i)).not.toHaveAttribute('autocomplete');
});

test.each(['current-password', 'new-password', 'off'])('sets password autocomplete to %s', (passwordAutoComplete) => {
render(<LoginForm passwordAutoComplete={passwordAutoComplete} />);

expect(screen.getByLabelText(/password/i)).toHaveAttribute('autocomplete', passwordAutoComplete);
expect(screen.getByRole('textbox', { name: /username/i })).not.toHaveAttribute('autocomplete');
});

test('sets input autocomplete independently of form autocomplete', () => {
render(
<LoginForm
aria-label="Login"
autoComplete="off"
usernameAutoComplete="username"
passwordAutoComplete="current-password"
/>
);

expect(screen.getByRole('form', { name: 'Login' })).toHaveAttribute('autocomplete', 'off');
expect(screen.getByRole('textbox', { name: /username/i })).toHaveAttribute('autocomplete', 'username');
expect(screen.getByLabelText(/password/i)).toHaveAttribute('autocomplete', 'current-password');
});

test('preserves password autocomplete when showing and hiding the password', async () => {
const user = userEvent.setup();
render(<LoginForm passwordAutoComplete="new-password" isShowPasswordEnabled />);

expect(screen.getByLabelText(/^password/i)).toHaveAttribute('type', 'password');
expect(screen.getByLabelText(/^password/i)).toHaveAttribute('autocomplete', 'new-password');

await user.click(screen.getByRole('button', { name: 'Show password' }));

expect(screen.getByLabelText(/^password/i)).toHaveAttribute('type', 'text');
expect(screen.getByLabelText(/^password/i)).toHaveAttribute('autocomplete', 'new-password');

await user.click(screen.getByRole('button', { name: 'Hide password' }));

expect(screen.getByLabelText(/^password/i)).toHaveAttribute('type', 'password');
expect(screen.getByLabelText(/^password/i)).toHaveAttribute('autocomplete', 'new-password');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import GitlabIcon from '@patternfly/react-icons/dist/esm/icons/gitlab-icon';

By default, a login page requires users to enter both a username and a password into their respective fields. The username must always be a required field, but you can make the password optional by passing the `isPasswordRequired` property to the `<LoginForm>`.

Use `usernameAutoComplete` and `passwordAutoComplete` to set autocomplete values on the respective inputs. This example uses `"username"` and `"current-password"` for an existing account. Use `passwordAutoComplete="new-password"` when users are creating a password. Both properties are optional; when omitted, the inputs inherit the form's autocomplete setting.

This example uses `brandImgProps` to pass the brand image source, alt text, and an extra class, which will be preferred over `brandImgSrc` when both are provided.
```ts file='./LoginPageBasic.tsx' isFullscreen

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,12 @@ export const SimpleLoginPage: React.FunctionComponent = () => {
helperTextIcon={<RhUiErrorFillIcon />}
usernameLabel="Username"
usernameValue={username}
usernameAutoComplete="username"
onChangeUsername={handleUsernameChange}
isValidUsername={isValidUsername}
passwordLabel="Password"
passwordValue={password}
passwordAutoComplete="current-password"
onChangePassword={handlePasswordChange}
isValidPassword={isValidPassword}
rememberMeLabel="Keep me logged in for 30 days."
Expand Down