Add language switcher to login page
Closes-Bug: #1667064 Change-Id: Ifa5d8f3a19b77ddfe0a8e3b0d1dba61582065719
This commit is contained in:
parent
db3988d0e4
commit
1c88c9ea30
5
releasenotes/notes/i18n-login-e5464553686d53c8.yaml
Normal file
5
releasenotes/notes/i18n-login-e5464553686d53c8.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
Fixes `bug 1667064 <https://bugs.launchpad.net/tripleo/+bug/1667064>`__
|
||||
Add a language picker to the login page
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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'
|
||||
? <DropdownItem
|
||||
key={`lang-${lang}`}
|
||||
active={active}
|
||||
onClick={this.props.chooseLanguage.bind(this, lang)}
|
||||
>
|
||||
{configLanguages[lang]}
|
||||
</DropdownItem>
|
||||
: null;
|
||||
});
|
||||
return getEnabledLanguages()
|
||||
.map((langName, langKey) => {
|
||||
const active = enabledLang === langKey;
|
||||
return MESSAGES[langKey] || langKey === 'en'
|
||||
? <DropdownItem
|
||||
key={`lang-${langKey}`}
|
||||
active={active}
|
||||
onClick={this.props.chooseLanguage.bind(this, langKey)}
|
||||
>
|
||||
{langName}
|
||||
</DropdownItem>
|
||||
: null;
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
73
src/js/components/login/LanguageInput.js
Normal file
73
src/js/components/login/LanguageInput.js
Normal 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);
|
@ -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));
|
@ -70,5 +70,5 @@ export function getEnabledLanguages() {
|
||||
delete configLanguages[language];
|
||||
});
|
||||
|
||||
return configLanguages;
|
||||
return Map(configLanguages).sort();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user