Add language switcher to login page

Closes-Bug: #1667064
Change-Id: Ifa5d8f3a19b77ddfe0a8e3b0d1dba61582065719
This commit is contained in:
Honza Pokorny 2017-03-03 14:58:20 -04:00
parent db3988d0e4
commit 1c88c9ea30
7 changed files with 128 additions and 34 deletions

View File

@ -0,0 +1,5 @@
---
fixes:
- |
Fixes `bug 1667064 <https://bugs.launchpad.net/tripleo/+bug/1667064>`__
Add a language picker to the login page

View File

@ -17,7 +17,7 @@
import React from 'react';
import ReactTestUtils from 'react-dom/test-utils';
import Login from '../../js/components/Login';
import Login from '../../js/components/login/Login';
import LoginActions from '../../js/actions/LoginActions';
let loginInstance;

View File

@ -17,7 +17,7 @@
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import Login from './Login';
import Login from './login/Login';
import UserAuthenticator from './UserAuthenticator';
export default class App extends React.Component {

View File

@ -22,9 +22,9 @@ import React from 'react';
import Dropdown from '../ui/dropdown/Dropdown';
import DropdownToggle from '../ui/dropdown/DropdownToggle';
import DropdownItem from '../ui/dropdown/DropdownItem';
import { getEnabledLanguages } from '../../services/utils';
import I18nActions from '../../actions/I18nActions';
import { MESSAGES } from './messages';
import { getEnabledLanguages } from '../../services/utils';
const messages = defineMessages({
language: {
@ -35,25 +35,21 @@ const messages = defineMessages({
class I18nDropdown extends React.Component {
_renderDropdownItems() {
const configLanguages = getEnabledLanguages();
const langList = Object.keys(configLanguages).sort(
(a, b) => configLanguages[a] > configLanguages[b]
);
const enabledLang = this.props.language;
return langList.map(lang => {
const active = enabledLang === lang;
return MESSAGES[lang] || lang === 'en'
return getEnabledLanguages()
.map((langName, langKey) => {
const active = enabledLang === langKey;
return MESSAGES[langKey] || langKey === 'en'
? <DropdownItem
key={`lang-${lang}`}
key={`lang-${langKey}`}
active={active}
onClick={this.props.chooseLanguage.bind(this, lang)}
onClick={this.props.chooseLanguage.bind(this, langKey)}
>
{configLanguages[lang]}
{langName}
</DropdownItem>
: null;
});
})
.toList();
}
render() {

View File

@ -0,0 +1,73 @@
/**
* Copyright 2017 Red Hat Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl } from 'react-intl';
import { getEnabledLanguages } from '../../services/utils';
class LanguageInput extends React.Component {
_renderOptions() {
return getEnabledLanguages()
.map((langName, langKey) => {
return (
<option key={`lang-${langKey}`} value={langKey}>
{langName}
</option>
);
})
.toList();
}
_onChange(event) {
this.props.chooseLanguage(event.currentTarget.value);
}
render() {
return (
<div className="form-group">
<label
className="col-sm-2 col-md-2 control-label"
htmlFor={this.props.name}
/>
<div className="col-sm-4 col-sm-offset-6 col-md-4 col-md-offset-6">
<select
className="combobox form-control"
name={this.props.name}
value={this.props.language}
autoFocus={this.props.autoFocus}
onChange={this._onChange.bind(this)}
>
{this._renderOptions()}
</select>
</div>
</div>
);
}
}
LanguageInput.propTypes = {
autoFocus: PropTypes.bool,
chooseLanguage: PropTypes.func.isRequired,
language: PropTypes.string,
name: PropTypes.string.isRequired
};
LanguageInput.defaultProps = {
autoFocus: false
};
export default injectIntl(LanguageInput);

View File

@ -23,14 +23,16 @@ import React from 'react';
import ReactDOM from 'react-dom';
import { Redirect } from 'react-router-dom';
import FormErrorList from './ui/forms/FormErrorList';
import Loader from './ui/Loader';
import LoginInput from './ui/forms/LoginInput';
import LoginActions from '../actions/LoginActions';
import NotificationsToaster from './notifications/NotificationsToaster';
import FormErrorList from '../ui/forms/FormErrorList';
import I18nActions from '../../actions/I18nActions';
import Loader from '../ui/Loader';
import LoginInput from '../ui/forms/LoginInput';
import LanguageInput from './LanguageInput';
import LoginActions from '../../actions/LoginActions';
import NotificationsToaster from '../notifications/NotificationsToaster';
import LogoSvg from '../../img/logo.svg';
import TripleoOwlSvg from '../../img/tripleo-owl.svg';
import LogoSvg from '../../../img/logo.svg';
import TripleoOwlSvg from '../../../img/tripleo-owl.svg';
const messages = defineMessages({
authenticating: {
@ -103,7 +105,7 @@ class Login extends React.Component {
handleLogin(formData, resetForm, invalidateForm) {
const formFields = Object.keys(this.refs.form.inputs);
this.props.dispatch(LoginActions.authenticateUser(formData, formFields));
this.props.authenticateUser(formData, formFields);
}
render() {
@ -143,6 +145,11 @@ class Login extends React.Component {
onValid={this._enableButton.bind(this)}
onInvalid={this._disableButton.bind(this)}
>
<LanguageInput
name="language"
chooseLanguage={this.props.chooseLanguage}
language={this.props.language}
/>
<LoginInput
name="username"
placeholder={formatMessage(messages.username)}
@ -192,13 +199,16 @@ class Login extends React.Component {
}
}
Login.propTypes = {
dispatch: PropTypes.func.isRequired,
authenticateUser: PropTypes.func.isRequired,
chooseLanguage: PropTypes.func.isRequired,
formErrors: ImmutablePropTypes.list.isRequired,
formFieldErrors: ImmutablePropTypes.map.isRequired,
intl: PropTypes.object,
isAuthenticated: PropTypes.bool.isRequired,
isAuthenticating: PropTypes.bool.isRequired,
location: PropTypes.object
language: PropTypes.string,
location: PropTypes.object,
userLoggedIn: PropTypes.bool.isRequired
};
function mapStateToProps(state) {
@ -206,8 +216,18 @@ function mapStateToProps(state) {
formErrors: state.login.getIn(['loginForm', 'formErrors']),
formFieldErrors: state.login.getIn(['loginForm', 'formFieldErrors']),
isAuthenticated: state.login.isAuthenticated,
isAuthenticating: state.login.isAuthenticating
isAuthenticating: state.login.get('isAuthenticating'),
language: state.i18n.get('language', 'en'),
userLoggedIn: state.login.hasIn(['keystoneAccess', 'user'])
};
}
export default injectIntl(connect(mapStateToProps)(Login));
const mapDispatchToProps = dispatch => {
return {
chooseLanguage: language => dispatch(I18nActions.chooseLanguage(language)),
authenticateUser: (formData, formFields, nextPath) =>
dispatch(LoginActions.authenticateUser(formData, formFields, nextPath))
};
};
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(Login));

View File

@ -70,5 +70,5 @@ export function getEnabledLanguages() {
delete configLanguages[language];
});
return configLanguages;
return Map(configLanguages).sort();
}