forked from patternfly/patternfly-react
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(LoginForm): Added the PF4 LoginForm Component (patternfly#874)
* feat(LoginForm): Addeds the PF4 LoginForm Component patternfly#825 * feat(LoginForm): Addeds the PF4 LoginForm Component patternfly#825 * updates based on comments * update snapshot test * Update example * Update based on review * make checkbox optional * updates from commnets
- Loading branch information
Showing
8 changed files
with
655 additions
and
6 deletions.
There are no files selected for viewing
27 changes: 27 additions & 0 deletions
27
packages/patternfly-4/react-core/src/components/LoginPage/LoginForm.d.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { SFC, HTMLProps, ReactNode } from 'react'; | ||
|
||
export interface LoginFormProps extends HTMLProps<HTMLFormElement> { | ||
children?: ReactNode; | ||
usernameLabel?: string; | ||
usernameValue?: string; | ||
onChangeUsername?: Function; | ||
usernameHelperText?: string; | ||
usernameHelperTextInvalid?: string; | ||
isValidUsername?: boolean; | ||
passwordLabel?: string; | ||
PasswordValue?: string; | ||
onChangePassword?: Function; | ||
passwordHelperText?: string; | ||
passwordHelperTextInvalid?: string; | ||
isValidPassword?: boolean; | ||
loginButtonLabel?: string; | ||
onLoginButtonClick?: Function; | ||
rememberMeLabel?: string; | ||
isRememberMeChecked?: boolean; | ||
onChangeRememberMe?: Function; | ||
rememberMeAriaLabel?: string; | ||
} | ||
|
||
declare const LoginForm: SFC<LoginFormProps>; | ||
|
||
export default LoginForm; |
185 changes: 185 additions & 0 deletions
185
packages/patternfly-4/react-core/src/components/LoginPage/LoginForm.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { css } from '@patternfly/react-styles'; | ||
import flexStyles from '@patternfly/patternfly-next/utilities/Flex/flex.css'; | ||
import displayStyles from '@patternfly/patternfly-next/utilities/Display/display.css'; | ||
import alignmentStyles from '@patternfly/patternfly-next/utilities/Alignment/alignment.css'; | ||
import spacingStyles from '@patternfly/patternfly-next/utilities/Spacing/spacing.css'; | ||
import { Form, FormGroup, ActionGroup } from '../Form'; | ||
import { TextInput } from '../TextInput'; | ||
import { Button } from '../Button'; | ||
import { Checkbox } from '../Checkbox'; | ||
|
||
const propTypes = { | ||
/** Additional classes added to the LoginBox Body */ | ||
className: PropTypes.string, | ||
/** Label for the Username Input Field */ | ||
usernameLabel: PropTypes.string, | ||
/** Value for the Username */ | ||
usernameValue: PropTypes.string, | ||
/** Function that handles the onChange event for the Username */ | ||
onChangeUsername: PropTypes.func, | ||
/** Helper Text for the Username Input Field */ | ||
usernameHelperText: PropTypes.string, | ||
/** Helper Text for the Username Input Field when it is invalid */ | ||
usernameHelperTextInvalid: PropTypes.string, | ||
/** Flag indicating if the Username is valid */ | ||
isValidUsername: PropTypes.bool, | ||
/** Label for the Password Input Field */ | ||
passwordLabel: PropTypes.string, | ||
/** Value for the Password */ | ||
passwordValue: PropTypes.string, | ||
/** Function that handles the onChange event for the Password */ | ||
onChangePassword: PropTypes.func, | ||
/** Helper Text for the Username Input Field */ | ||
passwordHelperText: PropTypes.string, | ||
/** Helper Text for the Password Input Field when it is invalid */ | ||
passwordHelperTextInvalid: PropTypes.string, | ||
/** Flag indicating if the Password is valid */ | ||
isValidPassword: PropTypes.bool, | ||
/** Label for the Log in Button Input */ | ||
loginButtonLabel: PropTypes.string, | ||
/** Flag indicating if the Login Button is disabled */ | ||
isLoginButtonDisabled: PropTypes.bool, | ||
/** Function that is called when the Login button is clicked */ | ||
onLoginButtonClick: PropTypes.func, | ||
/** Label for the Remember Me Checkbox that indicates the user should be kept logged in. If the label is not provided, the checkbox will not show. */ | ||
rememberMeLabel: PropTypes.string, | ||
/** Flag indicating if the remember me Checkbox is checked. */ | ||
isRememberMeChecked: PropTypes.bool, | ||
/** Function that handles the onChange event for the Remember Me Checkbox */ | ||
onChangeRememberMe: PropTypes.func, | ||
/** Aria Label for the Remember me checkbox */ | ||
rememberMeAriaLabel: props => { | ||
if (props.rememberMeLabel && !props.rememberMeAriaLabel) { | ||
return new Error('rememberMeAriaLabel is required with the Remember me checkbox'); | ||
} | ||
return null; | ||
} | ||
}; | ||
|
||
const defaultProps = { | ||
className: '', | ||
usernameLabel: 'Username', | ||
usernameValue: '', | ||
onChangeUsername: () => undefined, | ||
usernameHelperText: '', | ||
usernameHelperTextInvalid: '', | ||
isValidUsername: true, | ||
passwordLabel: 'Password', | ||
passwordValue: '', | ||
onChangePassword: () => undefined, | ||
passwordHelperText: '', | ||
passwordHelperTextInvalid: '', | ||
isValidPassword: true, | ||
loginButtonLabel: 'Log In', | ||
isLoginButtonDisabled: false, | ||
onLoginButtonClick: () => undefined, | ||
rememberMeLabel: '', | ||
isRememberMeChecked: false, | ||
onChangeRememberMe: () => undefined, | ||
rememberMeAriaLabel: '' | ||
}; | ||
|
||
const LoginForm = ({ | ||
className, | ||
usernameLabel, | ||
usernameValue, | ||
onChangeUsername, | ||
usernameHelperText, | ||
usernameHelperTextInvalid, | ||
isValidUsername, | ||
passwordLabel, | ||
passwordValue, | ||
onChangePassword, | ||
passwordHelperText, | ||
passwordHelperTextInvalid, | ||
isValidPassword, | ||
loginButtonLabel, | ||
isLoginButtonDisabled, | ||
onLoginButtonClick, | ||
rememberMeLabel, | ||
isRememberMeChecked, | ||
onChangeRememberMe, | ||
rememberMeAriaLabel, | ||
...props | ||
}) => ( | ||
<Form className={className} {...props}> | ||
<FormGroup | ||
label={usernameLabel} | ||
isRequired | ||
helperText={usernameHelperText} | ||
helperTextInvalid={usernameHelperTextInvalid} | ||
isValid={isValidUsername} | ||
fieldId="pf-login-username-id" | ||
> | ||
<TextInput | ||
id="pf-login-username-id" | ||
isRequired | ||
isValid={isValidUsername} | ||
type="text" | ||
mame="pf-login-username-id" | ||
value={usernameValue} | ||
onChange={onChangeUsername} | ||
/> | ||
</FormGroup> | ||
<FormGroup | ||
label={passwordLabel} | ||
isRequired | ||
helperText={passwordHelperText} | ||
helperTextInvalid={passwordHelperTextInvalid} | ||
isValid={isValidPassword} | ||
fieldId="pf-login-password-id" | ||
> | ||
<TextInput | ||
isRequired | ||
type="password" | ||
id="pf-login-password-id" | ||
name="pf-login-password-id" | ||
isValid={isValidPassword} | ||
value={passwordValue} | ||
onChange={onChangePassword} | ||
/> | ||
</FormGroup> | ||
<ActionGroup | ||
className={css( | ||
displayStyles.displayFlex, | ||
flexStyles.alignItemsCenter, | ||
flexStyles.flexDirectionColumn, | ||
flexStyles.flexDirectionRowOnMd | ||
)} | ||
> | ||
<Button | ||
className={css( | ||
spacingStyles.mrLgOnMd, | ||
spacingStyles.pxXl, | ||
flexStyles.alignSelfStretch, | ||
flexStyles.alignSelfFlexStartOnMd | ||
)} | ||
variant="primary" | ||
type="submit" | ||
onClick={onLoginButtonClick} | ||
isDisabled={isLoginButtonDisabled} | ||
> | ||
{loginButtonLabel} | ||
</Button> | ||
<div className={css(alignmentStyles.textAlignCenter, alignmentStyles.textAlignLeftOnMd)}> | ||
{rememberMeLabel.length > 0 && ( | ||
<Checkbox | ||
className={css(spacingStyles.mMd, flexStyles.alignItemsCenter, displayStyles.displayFlex)} | ||
id="pf-login-remember-me-id" | ||
label={rememberMeLabel} | ||
checked={isRememberMeChecked} | ||
onChange={onChangeRememberMe} | ||
aria-label={rememberMeAriaLabel} | ||
/> | ||
)} | ||
</div> | ||
</ActionGroup> | ||
</Form> | ||
); | ||
|
||
LoginForm.propTypes = propTypes; | ||
LoginForm.defaultProps = defaultProps; | ||
|
||
export default LoginForm; |
76 changes: 76 additions & 0 deletions
76
packages/patternfly-4/react-core/src/components/LoginPage/LoginForm.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import React from 'react'; | ||
import { shallow } from 'enzyme'; | ||
import LoginForm from './LoginForm'; | ||
|
||
test('should render Login form', () => { | ||
const view = shallow(<LoginForm />); | ||
expect(view).toMatchSnapshot(); | ||
}); | ||
|
||
test('default function onChangeUsername', () => { | ||
const view = shallow(<LoginForm rememberMeLabel="Login Form" rememberMeAriaLabel="Login Form" />); | ||
view.find('#pf-login-username-id').simulate('change'); | ||
const result = LoginForm.defaultProps.onChangeUsername(); | ||
expect(result).toBe(undefined); | ||
}); | ||
|
||
test('should call onChangeUsername callback', () => { | ||
const mockFn = jest.fn(); | ||
const view = shallow( | ||
<LoginForm onChangeUsername={mockFn} rememberMeLabel="Login Form" rememberMeAriaLabel="Login Form" /> | ||
); | ||
view.find('#pf-login-username-id').simulate('change'); | ||
expect(mockFn).toHaveBeenCalled(); | ||
}); | ||
|
||
test('default function onChangePassword', () => { | ||
const view = shallow(<LoginForm rememberMeLabel="Login Form" rememberMeAriaLabel="Login Form" />); | ||
view.find('#pf-login-password-id').simulate('change'); | ||
const result = LoginForm.defaultProps.onChangePassword(); | ||
expect(result).toBe(undefined); | ||
}); | ||
|
||
test('should call onChangePassword callback', () => { | ||
const mockFn = jest.fn(); | ||
const view = shallow( | ||
<LoginForm onChangePassword={mockFn} rememberMeLabel="Login Form" rememberMeAriaLabel="Login Form" /> | ||
); | ||
view.find('#pf-login-password-id').simulate('change'); | ||
expect(mockFn).toHaveBeenCalled(); | ||
}); | ||
|
||
test('default function onChangeRememberMe', () => { | ||
const view = shallow(<LoginForm rememberMeLabel="Login Form" rememberMeAriaLabel="Login Form" />); | ||
view.find('#pf-login-remember-me-id').simulate('change'); | ||
const result = LoginForm.defaultProps.onChangeRememberMe(); | ||
expect(result).toBe(undefined); | ||
}); | ||
|
||
test('should call onChangeRememberMe callback', () => { | ||
const mockFn = jest.fn(); | ||
const view = shallow( | ||
<LoginForm onChangeRememberMe={mockFn} rememberMeLabel="Login Form" rememberMeAriaLabel="Login Form" /> | ||
); | ||
view.find('#pf-login-remember-me-id').simulate('change'); | ||
expect(mockFn).toHaveBeenCalled(); | ||
}); | ||
|
||
test('LoginForm with rememberMeLabel and no rememberMeAriaLabel generates console error', () => { | ||
const myMock = jest.fn(); | ||
const mockFn = jest.fn(); | ||
global.console = { error: myMock }; | ||
const view = shallow(<LoginForm onChangeRememberMe={mockFn} rememberMeLabel="remember me" />); | ||
expect(view).toMatchSnapshot(); | ||
expect(myMock).toBeCalled(); | ||
}); | ||
|
||
test('LoginForm with rememberMeLabel and rememberMeAriaLabel does not generates console error', () => { | ||
const myMock = jest.fn(); | ||
const mockFn = jest.fn(); | ||
global.console = { error: myMock }; | ||
const view = shallow( | ||
<LoginForm onChangeRememberMe={mockFn} rememberMeAriaLabel="remember me" rememberMeLabel="remember me" /> | ||
); | ||
expect(view).toMatchSnapshot(); | ||
expect(myMock).not.toBeCalled(); | ||
}); |
9 changes: 5 additions & 4 deletions
9
packages/patternfly-4/react-core/src/components/LoginPage/LoginPage.docs.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,12 @@ | ||
import { LoginPage } from '@patternfly/react-core'; | ||
import Simple from './examples/SimpleLoginPage'; | ||
import { LoginPage, LoginForm } from '@patternfly/react-core'; | ||
import SimpleLoginPage from './examples/SimpleLoginPage'; | ||
|
||
export default { | ||
title: 'LoginPage', | ||
components: { | ||
LoginPage | ||
LoginPage, | ||
LoginForm | ||
}, | ||
examples: [Simple], | ||
examples: [SimpleLoginPage], | ||
fullPageOnly: true | ||
}; |
Oops, something went wrong.