Merge pull request #2749 from jryans/auth-field
Use Field component in auth flows
This commit is contained in:
commit
8bf5e1d19f
8 changed files with 212 additions and 186 deletions
|
@ -15,17 +15,6 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_Login_field {
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border-radius: 3px;
|
|
||||||
border: 1px solid $strong-input-border-color;
|
|
||||||
font-weight: 300;
|
|
||||||
font-size: 13px;
|
|
||||||
padding: 9px;
|
|
||||||
margin-bottom: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_Login_submit {
|
.mx_Login_submit {
|
||||||
@mixin mx_DialogButton;
|
@mixin mx_DialogButton;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -69,74 +58,24 @@ limitations under the License.
|
||||||
color: $warning-color;
|
color: $warning-color;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
/*
|
|
||||||
height: 24px;
|
|
||||||
*/
|
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Login_type_container {
|
.mx_Login_type_container {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-bottom: 14px;
|
align-items: center;
|
||||||
color: $authpage-primary-color;
|
color: $authpage-primary-color;
|
||||||
|
|
||||||
|
.mx_Field {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Login_type_label {
|
.mx_Login_type_label {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
line-height: 35px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Login_type_dropdown {
|
.mx_Login_type_dropdown {
|
||||||
display: inline-block;
|
min-width: 200px;
|
||||||
min-width: 170px;
|
|
||||||
align-self: flex-end;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_Login_field_prefix {
|
|
||||||
height: 38px;
|
|
||||||
padding: 0px 5px;
|
|
||||||
line-height: 38px;
|
|
||||||
|
|
||||||
background-color: #eee;
|
|
||||||
border: 1px solid #c7c7c7;
|
|
||||||
border-right: 0px;
|
|
||||||
border-radius: 3px 0px 0px 3px;
|
|
||||||
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_Login_field_has_prefix {
|
|
||||||
border-top-left-radius: 0px;
|
|
||||||
border-bottom-left-radius: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_Login_phoneSection {
|
|
||||||
display:flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_Login_phoneCountry {
|
|
||||||
margin-bottom: 14px;
|
|
||||||
|
|
||||||
/* To override mx_Login_field_prefix */
|
|
||||||
text-align: left;
|
|
||||||
padding: 0px;
|
|
||||||
background-color: $primary-bg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_Login_field_prefix .mx_Dropdown_input {
|
|
||||||
/* To use prefix border instead of dropdown border */
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_Login_phoneCountry .mx_Dropdown_option {
|
|
||||||
/* To match height of mx_Login_field */
|
|
||||||
height: 38px;
|
|
||||||
line-height: 38px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_Login_phoneCountry .mx_Dropdown_option img {
|
|
||||||
margin: 3px;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,16 +78,16 @@ limitations under the License.
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AuthBody_fieldRow > * {
|
.mx_AuthBody_fieldRow > .mx_Field {
|
||||||
margin: 0 5px;
|
margin: 0 5px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AuthBody_fieldRow > *:first-child {
|
.mx_AuthBody_fieldRow > .mx_Field:first-child {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AuthBody_fieldRow > *:last-child {
|
.mx_AuthBody_fieldRow > .mx_Field:last-child {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,16 @@ limitations under the License.
|
||||||
/* TODO: Consider unifying with general input styles in _light.scss */
|
/* TODO: Consider unifying with general input styles in _light.scss */
|
||||||
|
|
||||||
.mx_Field {
|
.mx_Field {
|
||||||
|
display: flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 1em 0;
|
margin: 1em 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: border-color 0.25s;
|
||||||
|
border: 1px solid $input-border-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_Field_prefix {
|
||||||
|
border-right: 1px solid $input-border-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Field input,
|
.mx_Field input,
|
||||||
|
@ -27,9 +35,10 @@ limitations under the License.
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-family: $font-family;
|
font-family: $font-family;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
border: none;
|
||||||
|
// Even without a border here, we still need this avoid overlapping the rounded
|
||||||
|
// corners on the field above.
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
transition: border-color 0.25s;
|
|
||||||
border: 1px solid $input-border-color;
|
|
||||||
padding: 8px 9px;
|
padding: 8px 9px;
|
||||||
color: $primary-fg-color;
|
color: $primary-fg-color;
|
||||||
background-color: $primary-bg-color;
|
background-color: $primary-bg-color;
|
||||||
|
@ -55,11 +64,14 @@ limitations under the License.
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_Field:focus-within {
|
||||||
|
border-color: $input-focused-border-color;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_Field input:focus,
|
.mx_Field input:focus,
|
||||||
.mx_Field select:focus,
|
.mx_Field select:focus,
|
||||||
.mx_Field textarea:focus {
|
.mx_Field textarea:focus {
|
||||||
outline: 0;
|
outline: 0;
|
||||||
border-color: $input-focused-border-color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Field input::placeholder,
|
.mx_Field input::placeholder,
|
||||||
|
@ -99,7 +111,8 @@ limitations under the License.
|
||||||
.mx_Field input:not(:placeholder-shown) + label,
|
.mx_Field input:not(:placeholder-shown) + label,
|
||||||
.mx_Field textarea:focus + label,
|
.mx_Field textarea:focus + label,
|
||||||
.mx_Field textarea:not(:placeholder-shown) + label,
|
.mx_Field textarea:not(:placeholder-shown) + label,
|
||||||
.mx_Field select + label /* Always show a select's label on top to not collide with the value */ {
|
.mx_Field select + label /* Always show a select's label on top to not collide with the value */,
|
||||||
|
.mx_Field_labelAlwaysTopLeft label {
|
||||||
transition:
|
transition:
|
||||||
font-size 0.25s ease-out 0s,
|
font-size 0.25s ease-out 0s,
|
||||||
color 0.25s ease-out 0s,
|
color 0.25s ease-out 0s,
|
||||||
|
@ -127,3 +140,14 @@ limitations under the License.
|
||||||
background-color: $field-focused-label-bg-color;
|
background-color: $field-focused-label-bg-color;
|
||||||
color: $greyed-fg-color;
|
color: $greyed-fg-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Customise other components when placed inside a Field
|
||||||
|
|
||||||
|
.mx_Field .mx_Dropdown_input {
|
||||||
|
border: initial;
|
||||||
|
border-radius: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_Field .mx_CountryDropdown {
|
||||||
|
width: 67px;
|
||||||
|
}
|
||||||
|
|
|
@ -230,6 +230,8 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
renderForgot() {
|
renderForgot() {
|
||||||
|
const Field = sdk.getComponent('elements.Field');
|
||||||
|
|
||||||
let errorText = null;
|
let errorText = null;
|
||||||
const err = this.state.errorText || this.props.defaultServerDiscoveryError;
|
const err = this.state.errorText || this.props.defaultServerDiscoveryError;
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -275,23 +277,33 @@ module.exports = React.createClass({
|
||||||
{errorText}
|
{errorText}
|
||||||
<form onSubmit={this.onSubmitForm}>
|
<form onSubmit={this.onSubmitForm}>
|
||||||
<div className="mx_AuthBody_fieldRow">
|
<div className="mx_AuthBody_fieldRow">
|
||||||
<input className="mx_Login_field" type="text"
|
<Field
|
||||||
|
id="mx_ForgotPassword_email"
|
||||||
name="reset_email" // define a name so browser's password autofill gets less confused
|
name="reset_email" // define a name so browser's password autofill gets less confused
|
||||||
|
type="text"
|
||||||
|
label={_t('Email')}
|
||||||
value={this.state.email}
|
value={this.state.email}
|
||||||
onChange={this.onInputChanged.bind(this, "email")}
|
onChange={this.onInputChanged.bind(this, "email")}
|
||||||
placeholder={_t('Email')} autoFocus />
|
autoFocus
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_AuthBody_fieldRow">
|
<div className="mx_AuthBody_fieldRow">
|
||||||
<input className="mx_Login_field" type="password"
|
<Field
|
||||||
|
id="mx_ForgotPassword_password"
|
||||||
name="reset_password"
|
name="reset_password"
|
||||||
|
type="password"
|
||||||
|
label={_t('Password')}
|
||||||
value={this.state.password}
|
value={this.state.password}
|
||||||
onChange={this.onInputChanged.bind(this, "password")}
|
onChange={this.onInputChanged.bind(this, "password")}
|
||||||
placeholder={_t('Password')} />
|
/>
|
||||||
<input className="mx_Login_field" type="password"
|
<Field
|
||||||
|
id="mx_ForgotPassword_passwordConfirm"
|
||||||
name="reset_password_confirm"
|
name="reset_password_confirm"
|
||||||
|
type="password"
|
||||||
|
label={_t('Confirm')}
|
||||||
value={this.state.password2}
|
value={this.state.password2}
|
||||||
onChange={this.onInputChanged.bind(this, "password2")}
|
onChange={this.onInputChanged.bind(this, "password2")}
|
||||||
placeholder={_t('Confirm')} />
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span>{_t(
|
<span>{_t(
|
||||||
'A verification email will be sent to your inbox to confirm ' +
|
'A verification email will be sent to your inbox to confirm ' +
|
||||||
|
|
|
@ -138,7 +138,8 @@ class PasswordLogin extends React.Component {
|
||||||
this.props.onUsernameBlur(ev.target.value);
|
this.props.onUsernameBlur(ev.target.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoginTypeChange(loginType) {
|
onLoginTypeChange(ev) {
|
||||||
|
const loginType = ev.target.value;
|
||||||
this.props.onError(null); // send a null error to clear any error messages
|
this.props.onError(null); // send a null error to clear any error messages
|
||||||
this.setState({
|
this.setState({
|
||||||
loginType: loginType,
|
loginType: loginType,
|
||||||
|
@ -169,67 +170,70 @@ class PasswordLogin extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLoginField(loginType) {
|
renderLoginField(loginType) {
|
||||||
const classes = {
|
const Field = sdk.getComponent('elements.Field');
|
||||||
mx_Login_field: true,
|
|
||||||
};
|
const classes = {};
|
||||||
|
|
||||||
switch (loginType) {
|
switch (loginType) {
|
||||||
case PasswordLogin.LOGIN_FIELD_EMAIL:
|
case PasswordLogin.LOGIN_FIELD_EMAIL:
|
||||||
classes.error = this.props.loginIncorrect && !this.state.username;
|
classes.error = this.props.loginIncorrect && !this.state.username;
|
||||||
return <input
|
return <Field
|
||||||
className="mx_Login_field"
|
className={classNames(classes)}
|
||||||
ref={(e) => {this._loginField = e;}}
|
id="mx_PasswordLogin_email"
|
||||||
|
ref={(e) => { this._loginField = e; }}
|
||||||
|
name="username" // make it a little easier for browser's remember-password
|
||||||
key="email_input"
|
key="email_input"
|
||||||
type="text"
|
type="text"
|
||||||
name="username" // make it a little easier for browser's remember-password
|
label={_t("Email")}
|
||||||
onChange={this.onUsernameChanged}
|
|
||||||
onBlur={this.onUsernameBlur}
|
|
||||||
placeholder="joe@example.com"
|
placeholder="joe@example.com"
|
||||||
value={this.state.username}
|
value={this.state.username}
|
||||||
|
onChange={this.onUsernameChanged}
|
||||||
|
onBlur={this.onUsernameBlur}
|
||||||
autoFocus
|
autoFocus
|
||||||
/>;
|
/>;
|
||||||
case PasswordLogin.LOGIN_FIELD_MXID:
|
case PasswordLogin.LOGIN_FIELD_MXID:
|
||||||
classes.error = this.props.loginIncorrect && !this.state.username;
|
classes.error = this.props.loginIncorrect && !this.state.username;
|
||||||
return <input
|
return <Field
|
||||||
className={classNames(classes)}
|
className={classNames(classes)}
|
||||||
ref={(e) => {this._loginField = e;}}
|
id="mx_PasswordLogin_username"
|
||||||
|
ref={(e) => { this._loginField = e; }}
|
||||||
|
name="username" // make it a little easier for browser's remember-password
|
||||||
key="username_input"
|
key="username_input"
|
||||||
type="text"
|
type="text"
|
||||||
name="username" // make it a little easier for browser's remember-password
|
label={SdkConfig.get().disable_custom_urls ?
|
||||||
onChange={this.onUsernameChanged}
|
|
||||||
onBlur={this.onUsernameBlur}
|
|
||||||
placeholder={SdkConfig.get().disable_custom_urls ?
|
|
||||||
_t("Username on %(hs)s", {
|
_t("Username on %(hs)s", {
|
||||||
hs: this.props.hsUrl.replace(/^https?:\/\//, ''),
|
hs: this.props.hsUrl.replace(/^https?:\/\//, ''),
|
||||||
}) : _t("Username")}
|
}) : _t("Username")}
|
||||||
value={this.state.username}
|
value={this.state.username}
|
||||||
|
onChange={this.onUsernameChanged}
|
||||||
|
onBlur={this.onUsernameBlur}
|
||||||
autoFocus
|
autoFocus
|
||||||
/>;
|
/>;
|
||||||
case PasswordLogin.LOGIN_FIELD_PHONE: {
|
case PasswordLogin.LOGIN_FIELD_PHONE: {
|
||||||
const CountryDropdown = sdk.getComponent('views.auth.CountryDropdown');
|
const CountryDropdown = sdk.getComponent('views.auth.CountryDropdown');
|
||||||
classes.mx_Login_field_has_prefix = true;
|
|
||||||
classes.error = this.props.loginIncorrect && !this.state.phoneNumber;
|
classes.error = this.props.loginIncorrect && !this.state.phoneNumber;
|
||||||
return <div className="mx_Login_phoneSection">
|
|
||||||
<CountryDropdown
|
const phoneCountry = <CountryDropdown
|
||||||
className="mx_Login_phoneCountry mx_Login_field_prefix"
|
|
||||||
onOptionChange={this.onPhoneCountryChanged}
|
|
||||||
value={this.state.phoneCountry}
|
value={this.state.phoneCountry}
|
||||||
isSmall={true}
|
isSmall={true}
|
||||||
showPrefix={true}
|
showPrefix={true}
|
||||||
/>
|
onOptionChange={this.onPhoneCountryChanged}
|
||||||
<input
|
/>;
|
||||||
|
|
||||||
|
return <Field
|
||||||
className={classNames(classes)}
|
className={classNames(classes)}
|
||||||
ref={(e) => {this._loginField = e;}}
|
id="mx_PasswordLogin_phoneNumber"
|
||||||
|
ref={(e) => { this._loginField = e; }}
|
||||||
|
name="phoneNumber"
|
||||||
key="phone_input"
|
key="phone_input"
|
||||||
type="text"
|
type="text"
|
||||||
name="phoneNumber"
|
label={_t("Phone")}
|
||||||
|
value={this.state.phoneNumber}
|
||||||
|
prefix={phoneCountry}
|
||||||
onChange={this.onPhoneNumberChanged}
|
onChange={this.onPhoneNumberChanged}
|
||||||
onBlur={this.onPhoneNumberBlur}
|
onBlur={this.onPhoneNumberBlur}
|
||||||
placeholder={_t("Mobile phone number")}
|
|
||||||
value={this.state.phoneNumber}
|
|
||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>;
|
||||||
</div>;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -245,6 +249,8 @@ class PasswordLogin extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const Field = sdk.getComponent('elements.Field');
|
||||||
|
|
||||||
let forgotPasswordJsx;
|
let forgotPasswordJsx;
|
||||||
|
|
||||||
if (this.props.onForgotPasswordClick) {
|
if (this.props.onForgotPasswordClick) {
|
||||||
|
@ -286,12 +292,9 @@ class PasswordLogin extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
const pwFieldClass = classNames({
|
const pwFieldClass = classNames({
|
||||||
mx_Login_field: true,
|
|
||||||
error: this.props.loginIncorrect && !this.isLoginEmpty(), // only error password if error isn't top field
|
error: this.props.loginIncorrect && !this.isLoginEmpty(), // only error password if error isn't top field
|
||||||
});
|
});
|
||||||
|
|
||||||
const Dropdown = sdk.getComponent('elements.Dropdown');
|
|
||||||
|
|
||||||
const loginField = this.renderLoginField(this.state.loginType);
|
const loginField = this.renderLoginField(this.state.loginType);
|
||||||
|
|
||||||
let loginType;
|
let loginType;
|
||||||
|
@ -299,14 +302,32 @@ class PasswordLogin extends React.Component {
|
||||||
loginType = (
|
loginType = (
|
||||||
<div className="mx_Login_type_container">
|
<div className="mx_Login_type_container">
|
||||||
<label className="mx_Login_type_label">{ _t('Sign in with') }</label>
|
<label className="mx_Login_type_label">{ _t('Sign in with') }</label>
|
||||||
<Dropdown
|
<Field
|
||||||
className="mx_Login_type_dropdown"
|
className="mx_Login_type_dropdown"
|
||||||
|
id="mx_PasswordLogin_type"
|
||||||
|
element="select"
|
||||||
value={this.state.loginType}
|
value={this.state.loginType}
|
||||||
onOptionChange={this.onLoginTypeChange}>
|
onChange={this.onLoginTypeChange}
|
||||||
<span key={PasswordLogin.LOGIN_FIELD_MXID}>{ _t('Username') }</span>
|
>
|
||||||
<span key={PasswordLogin.LOGIN_FIELD_EMAIL}>{ _t('Email address') }</span>
|
<option
|
||||||
<span key={PasswordLogin.LOGIN_FIELD_PHONE}>{ _t('Phone') }</span>
|
key={PasswordLogin.LOGIN_FIELD_MXID}
|
||||||
</Dropdown>
|
value={PasswordLogin.LOGIN_FIELD_MXID}
|
||||||
|
>
|
||||||
|
{_t('Username')}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
key={PasswordLogin.LOGIN_FIELD_EMAIL}
|
||||||
|
value={PasswordLogin.LOGIN_FIELD_EMAIL}
|
||||||
|
>
|
||||||
|
{_t('Email address')}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
key={PasswordLogin.LOGIN_FIELD_PHONE}
|
||||||
|
value={PasswordLogin.LOGIN_FIELD_PHONE}
|
||||||
|
>
|
||||||
|
{_t('Phone')}
|
||||||
|
</option>
|
||||||
|
</Field>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -318,15 +339,19 @@ class PasswordLogin extends React.Component {
|
||||||
{editLink}
|
{editLink}
|
||||||
</h3>
|
</h3>
|
||||||
<form onSubmit={this.onSubmitForm}>
|
<form onSubmit={this.onSubmitForm}>
|
||||||
{ loginType }
|
{loginType}
|
||||||
{ loginField }
|
{loginField}
|
||||||
<input className={pwFieldClass} ref={(e) => {this._passwordField = e;}} type="password"
|
<Field
|
||||||
|
className={pwFieldClass}
|
||||||
|
id="mx_PasswordLogin_password"
|
||||||
|
ref={(e) => { this._passwordField = e; }}
|
||||||
|
type="password"
|
||||||
name="password"
|
name="password"
|
||||||
value={this.state.password} onChange={this.onPasswordChanged}
|
label={_t('Password')}
|
||||||
placeholder={_t('Password')}
|
value={this.state.password}
|
||||||
|
onChange={this.onPasswordChanged}
|
||||||
/>
|
/>
|
||||||
<br />
|
{forgotPasswordJsx}
|
||||||
{ forgotPasswordJsx }
|
|
||||||
<input className="mx_Login_submit"
|
<input className="mx_Login_submit"
|
||||||
type="submit"
|
type="submit"
|
||||||
value={_t('Sign in')}
|
value={_t('Sign in')}
|
||||||
|
|
|
@ -306,6 +306,8 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
const Field = sdk.getComponent('elements.Field');
|
||||||
|
|
||||||
let yourMatrixAccountText = _t('Create your Matrix account');
|
let yourMatrixAccountText = _t('Create your Matrix account');
|
||||||
if (this.props.hsName) {
|
if (this.props.hsName) {
|
||||||
yourMatrixAccountText = _t('Create your Matrix account on %(serverName)s', {
|
yourMatrixAccountText = _t('Create your Matrix account on %(serverName)s', {
|
||||||
|
@ -338,14 +340,16 @@ module.exports = React.createClass({
|
||||||
_t("Email (optional)");
|
_t("Email (optional)");
|
||||||
|
|
||||||
emailSection = (
|
emailSection = (
|
||||||
<div>
|
<Field
|
||||||
<input type="text" ref="email"
|
className={this._classForField(FIELD_EMAIL)}
|
||||||
placeholder={emailPlaceholder}
|
id="mx_RegistrationForm_email"
|
||||||
|
ref="email"
|
||||||
|
type="text"
|
||||||
|
label={emailPlaceholder}
|
||||||
defaultValue={this.props.defaultEmail}
|
defaultValue={this.props.defaultEmail}
|
||||||
className={this._classForField(FIELD_EMAIL, 'mx_Login_field')}
|
value={this.state.email}
|
||||||
onBlur={this.onEmailBlur}
|
onBlur={this.onEmailBlur}
|
||||||
value={this.state.email} />
|
/>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,40 +357,33 @@ module.exports = React.createClass({
|
||||||
const CountryDropdown = sdk.getComponent('views.auth.CountryDropdown');
|
const CountryDropdown = sdk.getComponent('views.auth.CountryDropdown');
|
||||||
let phoneSection;
|
let phoneSection;
|
||||||
if (threePidLogin && this._authStepIsUsed('m.login.msisdn')) {
|
if (threePidLogin && this._authStepIsUsed('m.login.msisdn')) {
|
||||||
const phonePlaceholder = this._authStepIsRequired('m.login.msisdn') ?
|
const phoneLabel = this._authStepIsRequired('m.login.msisdn') ?
|
||||||
_t("Phone") :
|
_t("Phone") :
|
||||||
_t("Phone (optional)");
|
_t("Phone (optional)");
|
||||||
phoneSection = (
|
const phoneCountry = <CountryDropdown
|
||||||
<div className="mx_Login_phoneSection">
|
|
||||||
<CountryDropdown ref="phone_country"
|
|
||||||
className="mx_Login_phoneCountry mx_Login_field_prefix"
|
|
||||||
value={this.state.phoneCountry}
|
value={this.state.phoneCountry}
|
||||||
isSmall={true}
|
isSmall={true}
|
||||||
showPrefix={true}
|
showPrefix={true}
|
||||||
onOptionChange={this.onPhoneCountryChange}
|
onOptionChange={this.onPhoneCountryChange}
|
||||||
/>
|
/>;
|
||||||
<input type="text" ref="phoneNumber"
|
|
||||||
placeholder={phonePlaceholder}
|
phoneSection = <Field
|
||||||
|
className={this._classForField(FIELD_PHONE_NUMBER)}
|
||||||
|
id="mx_RegistrationForm_phoneNumber"
|
||||||
|
ref="phoneNumber"
|
||||||
|
type="text"
|
||||||
|
label={phoneLabel}
|
||||||
defaultValue={this.props.defaultPhoneNumber}
|
defaultValue={this.props.defaultPhoneNumber}
|
||||||
className={this._classForField(
|
|
||||||
FIELD_PHONE_NUMBER,
|
|
||||||
'mx_Login_phoneNumberField',
|
|
||||||
'mx_Login_field',
|
|
||||||
'mx_Login_field_has_prefix',
|
|
||||||
)}
|
|
||||||
onBlur={this.onPhoneNumberBlur}
|
|
||||||
value={this.state.phoneNumber}
|
value={this.state.phoneNumber}
|
||||||
/>
|
prefix={phoneCountry}
|
||||||
</div>
|
onBlur={this.onPhoneNumberBlur}
|
||||||
);
|
/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const registerButton = (
|
const registerButton = (
|
||||||
<input className="mx_Login_submit" type="submit" value={_t("Register")} />
|
<input className="mx_Login_submit" type="submit" value={_t("Register")} />
|
||||||
);
|
);
|
||||||
|
|
||||||
const placeholderUsername = _t("Username");
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h3>
|
<h3>
|
||||||
|
@ -395,22 +392,36 @@ module.exports = React.createClass({
|
||||||
</h3>
|
</h3>
|
||||||
<form onSubmit={this.onSubmit}>
|
<form onSubmit={this.onSubmit}>
|
||||||
<div className="mx_AuthBody_fieldRow">
|
<div className="mx_AuthBody_fieldRow">
|
||||||
<input type="text" ref="username"
|
<Field
|
||||||
|
className={this._classForField(FIELD_USERNAME)}
|
||||||
|
id="mx_RegistrationForm_username"
|
||||||
|
ref="username"
|
||||||
|
type="text"
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
placeholder={placeholderUsername} defaultValue={this.props.defaultUsername}
|
label={_t("Username")}
|
||||||
className={this._classForField(FIELD_USERNAME, 'mx_Login_field')}
|
defaultValue={this.props.defaultUsername}
|
||||||
onBlur={this.onUsernameBlur} />
|
onBlur={this.onUsernameBlur}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_AuthBody_fieldRow">
|
<div className="mx_AuthBody_fieldRow">
|
||||||
<input type="password" ref="password"
|
<Field
|
||||||
className={this._classForField(FIELD_PASSWORD, 'mx_Login_field')}
|
className={this._classForField(FIELD_PASSWORD)}
|
||||||
|
id="mx_RegistrationForm_password"
|
||||||
|
ref="password"
|
||||||
|
type="password"
|
||||||
|
label={_t("Password")}
|
||||||
|
defaultValue={this.props.defaultPassword}
|
||||||
onBlur={this.onPasswordBlur}
|
onBlur={this.onPasswordBlur}
|
||||||
placeholder={_t("Password")} defaultValue={this.props.defaultPassword} />
|
/>
|
||||||
<input type="password" ref="passwordConfirm"
|
<Field
|
||||||
placeholder={_t("Confirm")}
|
className={this._classForField(FIELD_PASSWORD_CONFIRM)}
|
||||||
className={this._classForField(FIELD_PASSWORD_CONFIRM, 'mx_Login_field')}
|
id="mx_RegistrationForm_passwordConfirm"
|
||||||
|
ref="passwordConfirm"
|
||||||
|
type="password"
|
||||||
|
label={_t("Confirm")}
|
||||||
|
defaultValue={this.props.defaultPassword}
|
||||||
onBlur={this.onPasswordConfirmBlur}
|
onBlur={this.onPasswordConfirmBlur}
|
||||||
defaultValue={this.props.defaultPassword} />
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_AuthBody_fieldRow">
|
<div className="mx_AuthBody_fieldRow">
|
||||||
{ emailSection }
|
{ emailSection }
|
||||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
export default class Field extends React.PureComponent {
|
export default class Field extends React.PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -30,6 +31,8 @@ export default class Field extends React.PureComponent {
|
||||||
label: PropTypes.string,
|
label: PropTypes.string,
|
||||||
// The field's placeholder string. Defaults to the label.
|
// The field's placeholder string. Defaults to the label.
|
||||||
placeholder: PropTypes.string,
|
placeholder: PropTypes.string,
|
||||||
|
// Optional component to include inside the field before the input.
|
||||||
|
prefix: PropTypes.node,
|
||||||
// All other props pass through to the <input>.
|
// All other props pass through to the <input>.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -46,7 +49,7 @@ export default class Field extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { element, children, ...inputProps } = this.props;
|
const { element, prefix, children, ...inputProps } = this.props;
|
||||||
|
|
||||||
const inputElement = element || "input";
|
const inputElement = element || "input";
|
||||||
|
|
||||||
|
@ -57,7 +60,20 @@ export default class Field extends React.PureComponent {
|
||||||
|
|
||||||
const fieldInput = React.createElement(inputElement, inputProps, children);
|
const fieldInput = React.createElement(inputElement, inputProps, children);
|
||||||
|
|
||||||
return <div className={`mx_Field mx_Field_${inputElement}`}>
|
let prefixContainer = null;
|
||||||
|
if (prefix) {
|
||||||
|
prefixContainer = <span className="mx_Field_prefix">{prefix}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const classes = classNames("mx_Field", `mx_Field_${inputElement}`, {
|
||||||
|
// If we have a prefix element, leave the label always at the top left and
|
||||||
|
// don't animate it, as it looks a bit clunky and would add complexity to do
|
||||||
|
// properly.
|
||||||
|
mx_Field_labelAlwaysTopLeft: prefix,
|
||||||
|
});
|
||||||
|
|
||||||
|
return <div className={classes}>
|
||||||
|
{prefixContainer}
|
||||||
{fieldInput}
|
{fieldInput}
|
||||||
<label htmlFor={this.props.id}>{this.props.label}</label>
|
<label htmlFor={this.props.id}>{this.props.label}</label>
|
||||||
</div>;
|
</div>;
|
||||||
|
|
|
@ -1258,19 +1258,18 @@
|
||||||
"The username field must not be blank.": "The username field must not be blank.",
|
"The username field must not be blank.": "The username field must not be blank.",
|
||||||
"The phone number field must not be blank.": "The phone number field must not be blank.",
|
"The phone number field must not be blank.": "The phone number field must not be blank.",
|
||||||
"The password field must not be blank.": "The password field must not be blank.",
|
"The password field must not be blank.": "The password field must not be blank.",
|
||||||
|
"Email": "Email",
|
||||||
"Username on %(hs)s": "Username on %(hs)s",
|
"Username on %(hs)s": "Username on %(hs)s",
|
||||||
"Username": "Username",
|
"Username": "Username",
|
||||||
"Mobile phone number": "Mobile phone number",
|
"Phone": "Phone",
|
||||||
"Not sure of your password? <a>Set a new one</a>": "Not sure of your password? <a>Set a new one</a>",
|
"Not sure of your password? <a>Set a new one</a>": "Not sure of your password? <a>Set a new one</a>",
|
||||||
"Sign in to your Matrix account": "Sign in to your Matrix account",
|
"Sign in to your Matrix account": "Sign in to your Matrix account",
|
||||||
"Sign in to your Matrix account on %(serverName)s": "Sign in to your Matrix account on %(serverName)s",
|
"Sign in to your Matrix account on %(serverName)s": "Sign in to your Matrix account on %(serverName)s",
|
||||||
"Change": "Change",
|
"Change": "Change",
|
||||||
"Sign in with": "Sign in with",
|
"Sign in with": "Sign in with",
|
||||||
"Phone": "Phone",
|
|
||||||
"If you don't specify an email address, you won't be able to reset your password. Are you sure?": "If you don't specify an email address, you won't be able to reset your password. Are you sure?",
|
"If you don't specify an email address, you won't be able to reset your password. Are you sure?": "If you don't specify an email address, you won't be able to reset your password. Are you sure?",
|
||||||
"Create your Matrix account": "Create your Matrix account",
|
"Create your Matrix account": "Create your Matrix account",
|
||||||
"Create your Matrix account on %(serverName)s": "Create your Matrix account on %(serverName)s",
|
"Create your Matrix account on %(serverName)s": "Create your Matrix account on %(serverName)s",
|
||||||
"Email": "Email",
|
|
||||||
"Email (optional)": "Email (optional)",
|
"Email (optional)": "Email (optional)",
|
||||||
"Phone (optional)": "Phone (optional)",
|
"Phone (optional)": "Phone (optional)",
|
||||||
"Confirm": "Confirm",
|
"Confirm": "Confirm",
|
||||||
|
|
Loading…
Reference in a new issue