Skip to content

Commit

Permalink
feat(LoginPage): add LoginPage component with storybook and tests. (p…
Browse files Browse the repository at this point in the history
…atternfly#362)

* Update to current Patternfly-react version.

* Fixed HTML structure in the BasicLoginPage to be exactley like the pattern, removed all custom styles.

* Rebase, Add 'noop' wherever it's needed, Removed 'onInputKeyUp' for no use.

* Add additionalFields prop that allow adding more elements inside the LoginCardForm, under the password field.

* Refactor capslock validation to support all browsers, Update tests, Fix language dropdown classes, Refactor social-columns.

* Removed the hr from social-login-page

* Export all login page iner components, Removed all images and import them from patternfly instead, Rename ForgotPassword, InputWarning and RememberMe
  • Loading branch information
Ron-Lavi authored and dgutride committed Aug 13, 2018
1 parent df87b06 commit 111bebd
Show file tree
Hide file tree
Showing 43 changed files with 2,991 additions and 1 deletion.
1 change: 1 addition & 0 deletions out8492
Submodule out8492 added at 30cace
2 changes: 2 additions & 0 deletions packages/patternfly-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@
"react-bootstrap-typeahead": "^3.1.3",
"react-c3js": "^0.1.20",
"react-click-outside": "^3.0.1",
"react-collapse": "^4.0.3",
"react-fontawesome": "^1.6.1",
"react-motion": "^0.5.2",
"reactabular-table": "^8.14.0",
"recompose": "^0.26.0"
},
Expand Down
89 changes: 89 additions & 0 deletions packages/patternfly-react/src/components/LoginPage/LoginPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React from 'react';
import PropTypes from 'prop-types';
import Container from './components/LoginPageComponents/LoginPageContainer';
import Header from './components/LoginPageComponents/LoginPageHeader';
import Footer from './components/LoginPageComponents/LoginPageFooter';
import FooterLinks from './components/LoginPageComponents/LoginFooterLinks';
import LoginCard from './components/LoginCardComponents/LoginCard';
import WithTranslation from './components/LoginPageComponents/LoginPageWithTranslation';
import LoginPageAlert from './components/LoginPageComponents/LoginPageAlert';
import LoginPageLink from './components/LoginPageComponents/LoginPageLink';
import SocialLoginPage from './SocialLoginPage';
import SocialLoginPageContainer from './components/LoginPageComponents/SocialLoginPageContainer';
import BasicLoginPageLayout from './components/LoginPageComponents/BasicLoginPageLayout';

const LoginPagePattern = ({ container, header, footerLinks, card }) => (
<LoginPage.Container {...container}>
<LoginPage.Alert {...container.alert} />
<LoginPage.BasicLayout>
<LoginPage.Header {...header} />
<LoginCard.BasicLayout>
<LoginCard>
<LoginCard.Header>
<LoginCard.LanguagePicker
selectedLanguage={card.selectedLanguage}
availableLanguages={card.availableLanguages}
onLanguageChange={card.onLanguageChange}
/>
<h1>{card.title}</h1>
</LoginCard.Header>
<LoginCard.WithValidation {...card.form}>
<LoginCard.Form />
</LoginCard.WithValidation>
<LoginCard.SignUp {...card.signUp} />
</LoginCard>
<LoginPage.Footer links={footerLinks} />
</LoginCard.BasicLayout>
</LoginPage.BasicLayout>
</LoginPage.Container>
);

const LoginPage = props => (
<LoginPage.WithTranslation {...props}>
<LoginPage.Pattern />
</LoginPage.WithTranslation>
);

LoginPage.Container = Container;
LoginPage.Header = Header;
LoginPage.Footer = Footer;
LoginPage.Card = LoginCard;
LoginPage.FooterLinks = FooterLinks;
LoginPage.WithTranslation = WithTranslation;
LoginPage.Alert = LoginPageAlert;
LoginPage.Pattern = LoginPagePattern;
LoginPage.Social = SocialLoginPage;
LoginPage.SocialContainer = SocialLoginPageContainer;
LoginPage.BasicLayout = BasicLoginPageLayout;
LoginPage.Link = LoginPageLink;

LoginPagePattern.propTypes = {
container: PropTypes.shape({ ...LoginPage.Container.propTypes }),
header: PropTypes.shape({ ...LoginPage.Header.propTypes }),
card: PropTypes.shape({
...LoginCard.LanguagePicker.propTypes,
...LoginCard.Form.propTypes,
...LoginCard.SignUp.propTypes,
...LoginCard.RememberMe.propTypes,
...LoginCard.ForgotPassword.propTypes
}),
footerLinks: PropTypes.array
};

LoginPagePattern.defaultProps = {
container: { ...LoginPage.Container.defaultProps },
header: { ...LoginPage.Header.defaultProps },
card: {
...LoginCard.LanguagePicker.defaultProps,
...LoginCard.Form.defaultProps,
...LoginCard.SignUp.defaultProps,
...LoginCard.RememberMe.defaultProps,
...LoginCard.ForgotPassword.defaultProps
},
footerLinks: [...LoginPage.Footer.defaultProps.links]
};

LoginPage.propTypes = { ...LoginPagePattern.propTypes };
LoginPage.defaultProps = { ...LoginPagePattern.defaultProps };

export default LoginPage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { withKnobs, number } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
import { defaultTemplate } from 'storybook/decorators/storyTemplates';
import {
storybookPackageName,
STORYBOOK_CATEGORY,
DOCUMENTATION_URL
} from 'storybook/constants/siteConstants';
import LoginPage from './LoginPage';
import englishMessages from './mocks/messages.en';
import frenchMessages from './mocks/messages.fr';
import images from './assets/img';
import { name } from '../../../package.json';

const stories = storiesOf(
`${storybookPackageName(name)}/${
STORYBOOK_CATEGORY.APPLICATION_FRAMEWORK
}/Login Page`,
module
);

stories.addDecorator(withKnobs);
stories.addDecorator(
defaultTemplate({
title: 'Login Page',
documentationLink: `${
DOCUMENTATION_URL.PATTERNFLY_ORG_APP_FRAMEWORK
}login-page`
})
);

const storyAction = (e, message) => {
e.preventDefault();
action(message)();
};

const createProps = () => {
const { header, footerLinks, card } = englishMessages;
footerLinks.forEach(link => {
link.onClick = e => storyAction(e, 'Footer Link was pressed');
});
return {
container: {
backgroundUrl: images.background,
translations: { en: englishMessages, fr: frenchMessages },
className: '',
alert: {
message: header.alert,
onDismiss: e => storyAction(e, 'Notification was dismissed'),
show: true
}
},
header: {
logoSrc: images.brand,
logoTitle: header.logo,
caption: header.caption
},
footerLinks,
card: {
title: card.header.title,
selectedLanguage: card.header.selectedLanguage,
availableLanguages: card.header.availableLanguages,
signUp: {
label: card.signUp.label,
link: {
children: card.signUp.link.label,
href: '#',
onClick: e => storyAction(e, 'sign up was clicked')
}
},
form: {
validate: true,
submitError: card.form.error,
showError: true,
usernameField: {
id: 'card_email',
type: 'email',
placeholder: card.usernameField.placeholder,
errors: card.usernameField.errors,
error: card.usernameField.errors.invalid,
showError: true
},
passwordField: {
id: 'card_password',
type: 'password',
placeholder: card.passwordField.placeholder,
minLength: 8,
errors: card.passwordField.errors,
warnings: card.passwordField.warnings,
warning: card.passwordField.warnings.capsLock,
showWarning: true
},
additionalFields: null,
rememberMe: {
label: card.rememberMe,
onClick: e => action('remember me checkbox was clicked')()
},
forgotPassword: {
label: card.forgotPassword,
href: '#',
onClick: e => storyAction(e, 'Forgot password was clicked')
},
disableSubmit: false,
submitText: card.form.submitText,
onSubmit: e => storyAction(e, 'Form was submitted')
},
social: {
links: createLogoList()
}
}
};
};

const createLogoList = () => {
const socialLinkClick = e => storyAction(e, 'Social Link was clicked');
const {
google,
facebook,
linkedin,
github,
instagram,
stackExchange,
twitter,
git,
openID,
dropbox,
fedora,
skype
} = images;
return [
{
src: google,
alt: 'Google',
text: 'Google',
onClick: e => socialLinkClick(e)
},
{
src: facebook,
alt: 'Facebook',
text: 'Facebook',
onClick: e => socialLinkClick(e)
},
{
src: linkedin,
alt: 'Linkedin',
text: 'Linkedin',
onClick: e => socialLinkClick(e)
},
{
src: github,
alt: 'Github',
text: 'Github',
onClick: e => socialLinkClick(e)
},
{
src: instagram,
alt: 'Instagram',
text: 'Instagram',
onClick: e => socialLinkClick(e)
},
{
src: git,
alt: 'Git',
text: 'Git',
onClick: e => socialLinkClick(e)
},
{
src: openID,
alt: 'OpenID',
text: 'OpenID',
onClick: e => socialLinkClick(e)
},
{
src: dropbox,
alt: 'Dropbox',
text: 'Dropbox',
onClick: e => socialLinkClick(e)
},
{
src: fedora,
alt: 'Fedora',
text: 'Fedora',
onClick: e => socialLinkClick(e)
},
{
src: skype,
alt: 'Skype',
text: 'Skype',
onClick: e => socialLinkClick(e)
},
{
src: twitter,
alt: 'Twitter',
text: 'Twitter',
onClick: e => socialLinkClick(e)
},
{
src: stackExchange,
alt: 'StackExchange',
text: 'StackExchange',
onClick: e => socialLinkClick(e)
}
];
};

stories.addWithInfo('Managed Basic Login Page', () => (
<LoginPage {...createProps()} />
));

stories.addWithInfo('Build Your own Basic Login Page', () => {
const props = { ...createProps() };
props.card.form.validate = false;
return LoginPage.Pattern(props);
});

stories.addWithInfo('Managed Social Login Page', () => {
const logoListCopy = createLogoList();
const listSize = number('Social List Size', 12);
const socialLinks = logoListCopy.splice(0, listSize);

const props = { ...createProps() };
props.card.social.links = socialLinks;

return <LoginPage.Social {...props} />;
});

stories.addWithInfo('Build Your own Social Login Page', () => {
const logoListCopy = createLogoList();
const listSize = number('Social List Size', 12);
const socialLinks = logoListCopy.splice(0, listSize);

const props = { ...createProps() };
props.card.social.links = socialLinks;
props.card.form.validate = false;
return LoginPage.Social.Pattern(props);
});
Loading

0 comments on commit 111bebd

Please sign in to comment.