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.
|
||||
*/
|
||||
|
||||
.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 {
|
||||
@mixin mx_DialogButton;
|
||||
width: 100%;
|
||||
|
@ -69,74 +58,24 @@ limitations under the License.
|
|||
color: $warning-color;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
/*
|
||||
height: 24px;
|
||||
*/
|
||||
margin-top: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.mx_Login_type_container {
|
||||
display: flex;
|
||||
margin-bottom: 14px;
|
||||
align-items: center;
|
||||
color: $authpage-primary-color;
|
||||
|
||||
.mx_Field {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_Login_type_label {
|
||||
flex-grow: 1;
|
||||
line-height: 35px;
|
||||
}
|
||||
|
||||
.mx_Login_type_dropdown {
|
||||
display: inline-block;
|
||||
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;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
|
|
@ -78,16 +78,16 @@ limitations under the License.
|
|||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.mx_AuthBody_fieldRow > * {
|
||||
.mx_AuthBody_fieldRow > .mx_Field {
|
||||
margin: 0 5px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.mx_AuthBody_fieldRow > *:first-child {
|
||||
.mx_AuthBody_fieldRow > .mx_Field:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.mx_AuthBody_fieldRow > *:last-child {
|
||||
.mx_AuthBody_fieldRow > .mx_Field:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,8 +17,16 @@ limitations under the License.
|
|||
/* TODO: Consider unifying with general input styles in _light.scss */
|
||||
|
||||
.mx_Field {
|
||||
display: flex;
|
||||
position: relative;
|
||||
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,
|
||||
|
@ -27,9 +35,10 @@ limitations under the License.
|
|||
font-weight: normal;
|
||||
font-family: $font-family;
|
||||
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;
|
||||
transition: border-color 0.25s;
|
||||
border: 1px solid $input-border-color;
|
||||
padding: 8px 9px;
|
||||
color: $primary-fg-color;
|
||||
background-color: $primary-bg-color;
|
||||
|
@ -55,11 +64,14 @@ limitations under the License.
|
|||
pointer-events: none;
|
||||
}
|
||||
|
||||
.mx_Field:focus-within {
|
||||
border-color: $input-focused-border-color;
|
||||
}
|
||||
|
||||
.mx_Field input:focus,
|
||||
.mx_Field select:focus,
|
||||
.mx_Field textarea:focus {
|
||||
outline: 0;
|
||||
border-color: $input-focused-border-color;
|
||||
}
|
||||
|
||||
.mx_Field input::placeholder,
|
||||
|
@ -99,7 +111,8 @@ limitations under the License.
|
|||
.mx_Field input:not(:placeholder-shown) + label,
|
||||
.mx_Field textarea:focus + 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:
|
||||
font-size 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;
|
||||
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() {
|
||||
const Field = sdk.getComponent('elements.Field');
|
||||
|
||||
let errorText = null;
|
||||
const err = this.state.errorText || this.props.defaultServerDiscoveryError;
|
||||
if (err) {
|
||||
|
@ -275,23 +277,33 @@ module.exports = React.createClass({
|
|||
{errorText}
|
||||
<form onSubmit={this.onSubmitForm}>
|
||||
<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
|
||||
type="text"
|
||||
label={_t('Email')}
|
||||
value={this.state.email}
|
||||
onChange={this.onInputChanged.bind(this, "email")}
|
||||
placeholder={_t('Email')} autoFocus />
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
<div className="mx_AuthBody_fieldRow">
|
||||
<input className="mx_Login_field" type="password"
|
||||
<Field
|
||||
id="mx_ForgotPassword_password"
|
||||
name="reset_password"
|
||||
type="password"
|
||||
label={_t('Password')}
|
||||
value={this.state.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"
|
||||
type="password"
|
||||
label={_t('Confirm')}
|
||||
value={this.state.password2}
|
||||
onChange={this.onInputChanged.bind(this, "password2")}
|
||||
placeholder={_t('Confirm')} />
|
||||
/>
|
||||
</div>
|
||||
<span>{_t(
|
||||
'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);
|
||||
}
|
||||
|
||||
onLoginTypeChange(loginType) {
|
||||
onLoginTypeChange(ev) {
|
||||
const loginType = ev.target.value;
|
||||
this.props.onError(null); // send a null error to clear any error messages
|
||||
this.setState({
|
||||
loginType: loginType,
|
||||
|
@ -169,67 +170,70 @@ class PasswordLogin extends React.Component {
|
|||
}
|
||||
|
||||
renderLoginField(loginType) {
|
||||
const classes = {
|
||||
mx_Login_field: true,
|
||||
};
|
||||
const Field = sdk.getComponent('elements.Field');
|
||||
|
||||
const classes = {};
|
||||
|
||||
switch (loginType) {
|
||||
case PasswordLogin.LOGIN_FIELD_EMAIL:
|
||||
classes.error = this.props.loginIncorrect && !this.state.username;
|
||||
return <input
|
||||
className="mx_Login_field"
|
||||
ref={(e) => {this._loginField = e;}}
|
||||
return <Field
|
||||
className={classNames(classes)}
|
||||
id="mx_PasswordLogin_email"
|
||||
ref={(e) => { this._loginField = e; }}
|
||||
name="username" // make it a little easier for browser's remember-password
|
||||
key="email_input"
|
||||
type="text"
|
||||
name="username" // make it a little easier for browser's remember-password
|
||||
onChange={this.onUsernameChanged}
|
||||
onBlur={this.onUsernameBlur}
|
||||
label={_t("Email")}
|
||||
placeholder="joe@example.com"
|
||||
value={this.state.username}
|
||||
onChange={this.onUsernameChanged}
|
||||
onBlur={this.onUsernameBlur}
|
||||
autoFocus
|
||||
/>;
|
||||
case PasswordLogin.LOGIN_FIELD_MXID:
|
||||
classes.error = this.props.loginIncorrect && !this.state.username;
|
||||
return <input
|
||||
return <Field
|
||||
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"
|
||||
type="text"
|
||||
name="username" // make it a little easier for browser's remember-password
|
||||
label={SdkConfig.get().disable_custom_urls ?
|
||||
_t("Username on %(hs)s", {
|
||||
hs: this.props.hsUrl.replace(/^https?:\/\//, ''),
|
||||
}) : _t("Username")}
|
||||
value={this.state.username}
|
||||
onChange={this.onUsernameChanged}
|
||||
onBlur={this.onUsernameBlur}
|
||||
placeholder={SdkConfig.get().disable_custom_urls ?
|
||||
_t("Username on %(hs)s", {
|
||||
hs: this.props.hsUrl.replace(/^https?:\/\//, ''),
|
||||
}) : _t("Username")}
|
||||
value={this.state.username}
|
||||
autoFocus
|
||||
/>;
|
||||
case PasswordLogin.LOGIN_FIELD_PHONE: {
|
||||
const CountryDropdown = sdk.getComponent('views.auth.CountryDropdown');
|
||||
classes.mx_Login_field_has_prefix = true;
|
||||
classes.error = this.props.loginIncorrect && !this.state.phoneNumber;
|
||||
return <div className="mx_Login_phoneSection">
|
||||
<CountryDropdown
|
||||
className="mx_Login_phoneCountry mx_Login_field_prefix"
|
||||
onOptionChange={this.onPhoneCountryChanged}
|
||||
value={this.state.phoneCountry}
|
||||
isSmall={true}
|
||||
showPrefix={true}
|
||||
/>
|
||||
<input
|
||||
className={classNames(classes)}
|
||||
ref={(e) => {this._loginField = e;}}
|
||||
key="phone_input"
|
||||
type="text"
|
||||
name="phoneNumber"
|
||||
onChange={this.onPhoneNumberChanged}
|
||||
onBlur={this.onPhoneNumberBlur}
|
||||
placeholder={_t("Mobile phone number")}
|
||||
value={this.state.phoneNumber}
|
||||
autoFocus
|
||||
/>
|
||||
</div>;
|
||||
|
||||
const phoneCountry = <CountryDropdown
|
||||
value={this.state.phoneCountry}
|
||||
isSmall={true}
|
||||
showPrefix={true}
|
||||
onOptionChange={this.onPhoneCountryChanged}
|
||||
/>;
|
||||
|
||||
return <Field
|
||||
className={classNames(classes)}
|
||||
id="mx_PasswordLogin_phoneNumber"
|
||||
ref={(e) => { this._loginField = e; }}
|
||||
name="phoneNumber"
|
||||
key="phone_input"
|
||||
type="text"
|
||||
label={_t("Phone")}
|
||||
value={this.state.phoneNumber}
|
||||
prefix={phoneCountry}
|
||||
onChange={this.onPhoneNumberChanged}
|
||||
onBlur={this.onPhoneNumberBlur}
|
||||
autoFocus
|
||||
/>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -245,6 +249,8 @@ class PasswordLogin extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const Field = sdk.getComponent('elements.Field');
|
||||
|
||||
let forgotPasswordJsx;
|
||||
|
||||
if (this.props.onForgotPasswordClick) {
|
||||
|
@ -286,12 +292,9 @@ class PasswordLogin extends React.Component {
|
|||
}
|
||||
|
||||
const pwFieldClass = classNames({
|
||||
mx_Login_field: true,
|
||||
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);
|
||||
|
||||
let loginType;
|
||||
|
@ -299,14 +302,32 @@ class PasswordLogin extends React.Component {
|
|||
loginType = (
|
||||
<div className="mx_Login_type_container">
|
||||
<label className="mx_Login_type_label">{ _t('Sign in with') }</label>
|
||||
<Dropdown
|
||||
<Field
|
||||
className="mx_Login_type_dropdown"
|
||||
id="mx_PasswordLogin_type"
|
||||
element="select"
|
||||
value={this.state.loginType}
|
||||
onOptionChange={this.onLoginTypeChange}>
|
||||
<span key={PasswordLogin.LOGIN_FIELD_MXID}>{ _t('Username') }</span>
|
||||
<span key={PasswordLogin.LOGIN_FIELD_EMAIL}>{ _t('Email address') }</span>
|
||||
<span key={PasswordLogin.LOGIN_FIELD_PHONE}>{ _t('Phone') }</span>
|
||||
</Dropdown>
|
||||
onChange={this.onLoginTypeChange}
|
||||
>
|
||||
<option
|
||||
key={PasswordLogin.LOGIN_FIELD_MXID}
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
@ -318,15 +339,19 @@ class PasswordLogin extends React.Component {
|
|||
{editLink}
|
||||
</h3>
|
||||
<form onSubmit={this.onSubmitForm}>
|
||||
{ loginType }
|
||||
{ loginField }
|
||||
<input className={pwFieldClass} ref={(e) => {this._passwordField = e;}} type="password"
|
||||
{loginType}
|
||||
{loginField}
|
||||
<Field
|
||||
className={pwFieldClass}
|
||||
id="mx_PasswordLogin_password"
|
||||
ref={(e) => { this._passwordField = e; }}
|
||||
type="password"
|
||||
name="password"
|
||||
value={this.state.password} onChange={this.onPasswordChanged}
|
||||
placeholder={_t('Password')}
|
||||
label={_t('Password')}
|
||||
value={this.state.password}
|
||||
onChange={this.onPasswordChanged}
|
||||
/>
|
||||
<br />
|
||||
{ forgotPasswordJsx }
|
||||
{forgotPasswordJsx}
|
||||
<input className="mx_Login_submit"
|
||||
type="submit"
|
||||
value={_t('Sign in')}
|
||||
|
|
|
@ -306,6 +306,8 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
render: function() {
|
||||
const Field = sdk.getComponent('elements.Field');
|
||||
|
||||
let yourMatrixAccountText = _t('Create your Matrix account');
|
||||
if (this.props.hsName) {
|
||||
yourMatrixAccountText = _t('Create your Matrix account on %(serverName)s', {
|
||||
|
@ -338,14 +340,16 @@ module.exports = React.createClass({
|
|||
_t("Email (optional)");
|
||||
|
||||
emailSection = (
|
||||
<div>
|
||||
<input type="text" ref="email"
|
||||
placeholder={emailPlaceholder}
|
||||
defaultValue={this.props.defaultEmail}
|
||||
className={this._classForField(FIELD_EMAIL, 'mx_Login_field')}
|
||||
onBlur={this.onEmailBlur}
|
||||
value={this.state.email} />
|
||||
</div>
|
||||
<Field
|
||||
className={this._classForField(FIELD_EMAIL)}
|
||||
id="mx_RegistrationForm_email"
|
||||
ref="email"
|
||||
type="text"
|
||||
label={emailPlaceholder}
|
||||
defaultValue={this.props.defaultEmail}
|
||||
value={this.state.email}
|
||||
onBlur={this.onEmailBlur}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -353,40 +357,33 @@ module.exports = React.createClass({
|
|||
const CountryDropdown = sdk.getComponent('views.auth.CountryDropdown');
|
||||
let phoneSection;
|
||||
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 (optional)");
|
||||
phoneSection = (
|
||||
<div className="mx_Login_phoneSection">
|
||||
<CountryDropdown ref="phone_country"
|
||||
className="mx_Login_phoneCountry mx_Login_field_prefix"
|
||||
value={this.state.phoneCountry}
|
||||
isSmall={true}
|
||||
showPrefix={true}
|
||||
onOptionChange={this.onPhoneCountryChange}
|
||||
/>
|
||||
<input type="text" ref="phoneNumber"
|
||||
placeholder={phonePlaceholder}
|
||||
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}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
const phoneCountry = <CountryDropdown
|
||||
value={this.state.phoneCountry}
|
||||
isSmall={true}
|
||||
showPrefix={true}
|
||||
onOptionChange={this.onPhoneCountryChange}
|
||||
/>;
|
||||
|
||||
phoneSection = <Field
|
||||
className={this._classForField(FIELD_PHONE_NUMBER)}
|
||||
id="mx_RegistrationForm_phoneNumber"
|
||||
ref="phoneNumber"
|
||||
type="text"
|
||||
label={phoneLabel}
|
||||
defaultValue={this.props.defaultPhoneNumber}
|
||||
value={this.state.phoneNumber}
|
||||
prefix={phoneCountry}
|
||||
onBlur={this.onPhoneNumberBlur}
|
||||
/>;
|
||||
}
|
||||
|
||||
const registerButton = (
|
||||
<input className="mx_Login_submit" type="submit" value={_t("Register")} />
|
||||
);
|
||||
|
||||
const placeholderUsername = _t("Username");
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3>
|
||||
|
@ -395,22 +392,36 @@ module.exports = React.createClass({
|
|||
</h3>
|
||||
<form onSubmit={this.onSubmit}>
|
||||
<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}
|
||||
placeholder={placeholderUsername} defaultValue={this.props.defaultUsername}
|
||||
className={this._classForField(FIELD_USERNAME, 'mx_Login_field')}
|
||||
onBlur={this.onUsernameBlur} />
|
||||
label={_t("Username")}
|
||||
defaultValue={this.props.defaultUsername}
|
||||
onBlur={this.onUsernameBlur}
|
||||
/>
|
||||
</div>
|
||||
<div className="mx_AuthBody_fieldRow">
|
||||
<input type="password" ref="password"
|
||||
className={this._classForField(FIELD_PASSWORD, 'mx_Login_field')}
|
||||
<Field
|
||||
className={this._classForField(FIELD_PASSWORD)}
|
||||
id="mx_RegistrationForm_password"
|
||||
ref="password"
|
||||
type="password"
|
||||
label={_t("Password")}
|
||||
defaultValue={this.props.defaultPassword}
|
||||
onBlur={this.onPasswordBlur}
|
||||
placeholder={_t("Password")} defaultValue={this.props.defaultPassword} />
|
||||
<input type="password" ref="passwordConfirm"
|
||||
placeholder={_t("Confirm")}
|
||||
className={this._classForField(FIELD_PASSWORD_CONFIRM, 'mx_Login_field')}
|
||||
/>
|
||||
<Field
|
||||
className={this._classForField(FIELD_PASSWORD_CONFIRM)}
|
||||
id="mx_RegistrationForm_passwordConfirm"
|
||||
ref="passwordConfirm"
|
||||
type="password"
|
||||
label={_t("Confirm")}
|
||||
defaultValue={this.props.defaultPassword}
|
||||
onBlur={this.onPasswordConfirmBlur}
|
||||
defaultValue={this.props.defaultPassword} />
|
||||
/>
|
||||
</div>
|
||||
<div className="mx_AuthBody_fieldRow">
|
||||
{ emailSection }
|
||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
|||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
|
||||
export default class Field extends React.PureComponent {
|
||||
static propTypes = {
|
||||
|
@ -30,6 +31,8 @@ export default class Field extends React.PureComponent {
|
|||
label: PropTypes.string,
|
||||
// The field's placeholder string. Defaults to the label.
|
||||
placeholder: PropTypes.string,
|
||||
// Optional component to include inside the field before the input.
|
||||
prefix: PropTypes.node,
|
||||
// All other props pass through to the <input>.
|
||||
};
|
||||
|
||||
|
@ -46,7 +49,7 @@ export default class Field extends React.PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { element, children, ...inputProps } = this.props;
|
||||
const { element, prefix, children, ...inputProps } = this.props;
|
||||
|
||||
const inputElement = element || "input";
|
||||
|
||||
|
@ -57,7 +60,20 @@ export default class Field extends React.PureComponent {
|
|||
|
||||
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}
|
||||
<label htmlFor={this.props.id}>{this.props.label}</label>
|
||||
</div>;
|
||||
|
|
|
@ -1258,19 +1258,18 @@
|
|||
"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 password field must not be blank.": "The password field must not be blank.",
|
||||
"Email": "Email",
|
||||
"Username on %(hs)s": "Username on %(hs)s",
|
||||
"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>",
|
||||
"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",
|
||||
"Change": "Change",
|
||||
"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?",
|
||||
"Create your Matrix account": "Create your Matrix account",
|
||||
"Create your Matrix account on %(serverName)s": "Create your Matrix account on %(serverName)s",
|
||||
"Email": "Email",
|
||||
"Email (optional)": "Email (optional)",
|
||||
"Phone (optional)": "Phone (optional)",
|
||||
"Confirm": "Confirm",
|
||||
|
|
Loading…
Reference in a new issue