mirror of
https://github.com/element-hq/element-web
synced 2024-11-27 11:47:23 +03:00
Convert uncontrolled Field usages to controlled
As part of adding validation to Field, the logic is simpler to follow if we can assume that all usages of Field use it as a controlled component, instead of supporting both controlled and uncontrolled. This converts the uncontrolled usages to controlled.
This commit is contained in:
parent
cff3c94858
commit
d4dbba3938
5 changed files with 108 additions and 31 deletions
|
@ -70,11 +70,6 @@ class PasswordLogin extends React.Component {
|
||||||
this.isLoginEmpty = this.isLoginEmpty.bind(this);
|
this.isLoginEmpty = this.isLoginEmpty.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
|
||||||
this._passwordField = null;
|
|
||||||
this._loginField = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
onForgotPasswordClick(ev) {
|
onForgotPasswordClick(ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
@ -180,7 +175,6 @@ class PasswordLogin extends React.Component {
|
||||||
return <Field
|
return <Field
|
||||||
className={classNames(classes)}
|
className={classNames(classes)}
|
||||||
id="mx_PasswordLogin_email"
|
id="mx_PasswordLogin_email"
|
||||||
ref={(e) => { this._loginField = e; }}
|
|
||||||
name="username" // make it a little easier for browser's remember-password
|
name="username" // make it a little easier for browser's remember-password
|
||||||
key="email_input"
|
key="email_input"
|
||||||
type="text"
|
type="text"
|
||||||
|
@ -196,7 +190,6 @@ class PasswordLogin extends React.Component {
|
||||||
return <Field
|
return <Field
|
||||||
className={classNames(classes)}
|
className={classNames(classes)}
|
||||||
id="mx_PasswordLogin_username"
|
id="mx_PasswordLogin_username"
|
||||||
ref={(e) => { this._loginField = e; }}
|
|
||||||
name="username" // make it a little easier for browser's remember-password
|
name="username" // make it a little easier for browser's remember-password
|
||||||
key="username_input"
|
key="username_input"
|
||||||
type="text"
|
type="text"
|
||||||
|
@ -223,7 +216,6 @@ class PasswordLogin extends React.Component {
|
||||||
return <Field
|
return <Field
|
||||||
className={classNames(classes)}
|
className={classNames(classes)}
|
||||||
id="mx_PasswordLogin_phoneNumber"
|
id="mx_PasswordLogin_phoneNumber"
|
||||||
ref={(e) => { this._loginField = e; }}
|
|
||||||
name="phoneNumber"
|
name="phoneNumber"
|
||||||
key="phone_input"
|
key="phone_input"
|
||||||
type="text"
|
type="text"
|
||||||
|
@ -344,7 +336,6 @@ class PasswordLogin extends React.Component {
|
||||||
<Field
|
<Field
|
||||||
className={pwFieldClass}
|
className={pwFieldClass}
|
||||||
id="mx_PasswordLogin_password"
|
id="mx_PasswordLogin_password"
|
||||||
ref={(e) => { this._passwordField = e; }}
|
|
||||||
type="password"
|
type="password"
|
||||||
name="password"
|
name="password"
|
||||||
label={_t('Password')}
|
label={_t('Password')}
|
||||||
|
|
|
@ -32,6 +32,9 @@ 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,
|
||||||
|
// The field's value.
|
||||||
|
// This is a controlled component, so the value is required.
|
||||||
|
value: PropTypes.string.isRequired,
|
||||||
// Optional component to include inside the field before the input.
|
// Optional component to include inside the field before the input.
|
||||||
prefix: PropTypes.node,
|
prefix: PropTypes.node,
|
||||||
// The callback called whenever the contents of the field
|
// The callback called whenever the contents of the field
|
||||||
|
@ -50,6 +53,7 @@ export default class Field extends React.PureComponent {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO: Remove me */
|
||||||
get value() {
|
get value() {
|
||||||
if (!this.refs.fieldInput) return null;
|
if (!this.refs.fieldInput) return null;
|
||||||
return this.refs.fieldInput.value;
|
return this.refs.fieldInput.value;
|
||||||
|
@ -87,6 +91,8 @@ export default class Field extends React.PureComponent {
|
||||||
inputProps.placeholder = inputProps.placeholder || inputProps.label;
|
inputProps.placeholder = inputProps.placeholder || inputProps.label;
|
||||||
|
|
||||||
inputProps.onChange = this.onChange;
|
inputProps.onChange = this.onChange;
|
||||||
|
|
||||||
|
/* TODO: Remove me */
|
||||||
// make sure we use the current `value` for the field and not the original one
|
// make sure we use the current `value` for the field and not the original one
|
||||||
if (inputProps.value === undefined) {
|
if (inputProps.value === undefined) {
|
||||||
inputProps.value = this.value || "";
|
inputProps.value = this.value || "";
|
||||||
|
|
|
@ -32,6 +32,7 @@ import sessionStore from '../../../stores/SessionStore';
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'ChangePassword',
|
displayName: 'ChangePassword',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
onFinished: PropTypes.func,
|
onFinished: PropTypes.func,
|
||||||
onError: PropTypes.func,
|
onError: PropTypes.func,
|
||||||
|
@ -73,6 +74,9 @@ module.exports = React.createClass({
|
||||||
return {
|
return {
|
||||||
phase: this.Phases.Edit,
|
phase: this.Phases.Edit,
|
||||||
cachedPassword: null,
|
cachedPassword: null,
|
||||||
|
oldPassword: "",
|
||||||
|
newPassword: "",
|
||||||
|
newPasswordConfirm: "",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -165,6 +169,9 @@ module.exports = React.createClass({
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: this.Phases.Edit,
|
phase: this.Phases.Edit,
|
||||||
|
oldPassword: "",
|
||||||
|
newPassword: "",
|
||||||
|
newPasswordConfirm: "",
|
||||||
});
|
});
|
||||||
}).done();
|
}).done();
|
||||||
},
|
},
|
||||||
|
@ -192,11 +199,29 @@ module.exports = React.createClass({
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onChangeOldPassword(ev) {
|
||||||
|
this.setState({
|
||||||
|
oldPassword: ev.target.value,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onChangeNewPassword(ev) {
|
||||||
|
this.setState({
|
||||||
|
newPassword: ev.target.value,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onChangeNewPasswordConfirm(ev) {
|
||||||
|
this.setState({
|
||||||
|
newPasswordConfirm: ev.target.value,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
onClickChange: function(ev) {
|
onClickChange: function(ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
const oldPassword = this.state.cachedPassword || this.refs.old_input.value;
|
const oldPassword = this.state.cachedPassword || this.state.oldPassword;
|
||||||
const newPassword = this.refs.new_input.value;
|
const newPassword = this.state.newPassword;
|
||||||
const confirmPassword = this.refs.confirm_input.value;
|
const confirmPassword = this.state.newPasswordConfirm;
|
||||||
const err = this.props.onCheckPassword(
|
const err = this.props.onCheckPassword(
|
||||||
oldPassword, newPassword, confirmPassword,
|
oldPassword, newPassword, confirmPassword,
|
||||||
);
|
);
|
||||||
|
@ -217,7 +242,12 @@ module.exports = React.createClass({
|
||||||
if (!this.state.cachedPassword) {
|
if (!this.state.cachedPassword) {
|
||||||
currentPassword = (
|
currentPassword = (
|
||||||
<div className={rowClassName}>
|
<div className={rowClassName}>
|
||||||
<Field id="passwordold" type="password" ref="old_input" label={_t('Current password')} />
|
<Field id="mx_ChangePassword_oldPassword"
|
||||||
|
type="password"
|
||||||
|
label={_t('Current password')}
|
||||||
|
value={this.state.oldPassword}
|
||||||
|
onChange={this.onChangeOldPassword}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -230,11 +260,21 @@ module.exports = React.createClass({
|
||||||
<form className={this.props.className} onSubmit={this.onClickChange}>
|
<form className={this.props.className} onSubmit={this.onClickChange}>
|
||||||
{ currentPassword }
|
{ currentPassword }
|
||||||
<div className={rowClassName}>
|
<div className={rowClassName}>
|
||||||
<Field id="password1" type="password" ref="new_input" label={passwordLabel}
|
<Field id="mx_ChangePassword_newPassword"
|
||||||
autoFocus={this.props.autoFocusNewPasswordInput} />
|
type="password"
|
||||||
|
label={passwordLabel}
|
||||||
|
value={this.state.newPassword}
|
||||||
|
autoFocus={this.props.autoFocusNewPasswordInput}
|
||||||
|
onChange={this.onChangeNewPassword}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={rowClassName}>
|
<div className={rowClassName}>
|
||||||
<Field id="password2" type="password" ref="confirm_input" label={_t("Confirm password")} />
|
<Field id="mx_ChangePassword_newPasswordConfirm"
|
||||||
|
type="password"
|
||||||
|
label={_t("Confirm password")}
|
||||||
|
value={this.state.newPasswordConfirm}
|
||||||
|
onChange={this.onChangeNewPasswordConfirm}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<AccessibleButton className={buttonClassName} kind={this.props.buttonKind} onClick={this.onClickChange}>
|
<AccessibleButton className={buttonClassName} kind={this.props.buttonKind} onClick={this.onClickChange}>
|
||||||
{ _t('Change Password') }
|
{ _t('Change Password') }
|
||||||
|
|
|
@ -119,6 +119,7 @@ export default class EmailAddresses extends React.Component {
|
||||||
verifying: false,
|
verifying: false,
|
||||||
addTask: null,
|
addTask: null,
|
||||||
continueDisabled: false,
|
continueDisabled: false,
|
||||||
|
newEmailAddress: "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,14 +135,20 @@ export default class EmailAddresses extends React.Component {
|
||||||
this.setState({emails: this.state.emails.filter((e) => e !== address)});
|
this.setState({emails: this.state.emails.filter((e) => e !== address)});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_onChangeNewEmailAddress = (e) => {
|
||||||
|
this.setState({
|
||||||
|
newEmailAddress: e.target.value,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
_onAddClick = (e) => {
|
_onAddClick = (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if (!this.refs.newEmailAddress) return;
|
if (!this.state.newEmailAddress) return;
|
||||||
|
|
||||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
const email = this.refs.newEmailAddress.value;
|
const email = this.state.newEmailAddress;
|
||||||
|
|
||||||
// TODO: Inline field validation
|
// TODO: Inline field validation
|
||||||
if (!Email.looksValid(email)) {
|
if (!Email.looksValid(email)) {
|
||||||
|
@ -173,14 +180,14 @@ export default class EmailAddresses extends React.Component {
|
||||||
|
|
||||||
this.setState({continueDisabled: true});
|
this.setState({continueDisabled: true});
|
||||||
this.state.addTask.checkEmailLinkClicked().then(() => {
|
this.state.addTask.checkEmailLinkClicked().then(() => {
|
||||||
const email = this.refs.newEmailAddress.value;
|
const email = this.state.newEmailAddress;
|
||||||
this.setState({
|
this.setState({
|
||||||
emails: [...this.state.emails, {address: email, medium: "email"}],
|
emails: [...this.state.emails, {address: email, medium: "email"}],
|
||||||
addTask: null,
|
addTask: null,
|
||||||
continueDisabled: false,
|
continueDisabled: false,
|
||||||
verifying: false,
|
verifying: false,
|
||||||
|
newEmailAddress: "",
|
||||||
});
|
});
|
||||||
this.refs.newEmailAddress.value = "";
|
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
this.setState({continueDisabled: false});
|
this.setState({continueDisabled: false});
|
||||||
if (err.errcode !== 'M_THREEPID_AUTH_FAILED') {
|
if (err.errcode !== 'M_THREEPID_AUTH_FAILED') {
|
||||||
|
@ -221,8 +228,14 @@ export default class EmailAddresses extends React.Component {
|
||||||
{existingEmailElements}
|
{existingEmailElements}
|
||||||
<form onSubmit={this._onAddClick} autoComplete={false}
|
<form onSubmit={this._onAddClick} autoComplete={false}
|
||||||
noValidate={true} className="mx_EmailAddresses_new">
|
noValidate={true} className="mx_EmailAddresses_new">
|
||||||
<Field id="newEmailAddress" ref="newEmailAddress" label={_t("Email Address")}
|
<Field id="mx_EmailAddressses_newEmailAddress"
|
||||||
type="text" autoComplete="off" disabled={this.state.verifying} />
|
type="text"
|
||||||
|
label={_t("Email Address")}
|
||||||
|
autoComplete="off"
|
||||||
|
disabled={this.state.verifying}
|
||||||
|
value={this.state.newEmailAddress}
|
||||||
|
onChange={this._onChangeNewEmailAddress}
|
||||||
|
/>
|
||||||
{addButton}
|
{addButton}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -117,6 +117,8 @@ export default class PhoneNumbers extends React.Component {
|
||||||
addTask: null,
|
addTask: null,
|
||||||
continueDisabled: false,
|
continueDisabled: false,
|
||||||
phoneCountry: "",
|
phoneCountry: "",
|
||||||
|
newPhoneNumber: "",
|
||||||
|
newPhoneNumberCode: "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,14 +134,26 @@ export default class PhoneNumbers extends React.Component {
|
||||||
this.setState({msisdns: this.state.msisdns.filter((e) => e !== address)});
|
this.setState({msisdns: this.state.msisdns.filter((e) => e !== address)});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_onChangeNewPhoneNumber = (e) => {
|
||||||
|
this.setState({
|
||||||
|
newPhoneNumber: e.target.value,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
_onChangeNewPhoneNumberCode = (e) => {
|
||||||
|
this.setState({
|
||||||
|
newPhoneNumberCode: e.target.value,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
_onAddClick = (e) => {
|
_onAddClick = (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if (!this.refs.newPhoneNumber) return;
|
if (!this.state.newPhoneNumber) return;
|
||||||
|
|
||||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
const phoneNumber = this.refs.newPhoneNumber.value;
|
const phoneNumber = this.state.newPhoneNumber;
|
||||||
const phoneCountry = this.state.phoneCountry;
|
const phoneCountry = this.state.phoneCountry;
|
||||||
|
|
||||||
const task = new AddThreepid();
|
const task = new AddThreepid();
|
||||||
|
@ -162,7 +176,7 @@ export default class PhoneNumbers extends React.Component {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
this.setState({continueDisabled: true});
|
this.setState({continueDisabled: true});
|
||||||
const token = this.refs.newPhoneNumberCode.value;
|
const token = this.state.newPhoneNumberCode;
|
||||||
this.state.addTask.haveMsisdnToken(token).then(() => {
|
this.state.addTask.haveMsisdnToken(token).then(() => {
|
||||||
this.setState({
|
this.setState({
|
||||||
msisdns: [...this.state.msisdns, {address: this.state.verifyMsisdn, medium: "msisdn"}],
|
msisdns: [...this.state.msisdns, {address: this.state.verifyMsisdn, medium: "msisdn"}],
|
||||||
|
@ -171,8 +185,9 @@ export default class PhoneNumbers extends React.Component {
|
||||||
verifying: false,
|
verifying: false,
|
||||||
verifyMsisdn: "",
|
verifyMsisdn: "",
|
||||||
verifyError: null,
|
verifyError: null,
|
||||||
|
newPhoneNumber: "",
|
||||||
|
newPhoneNumberCode: "",
|
||||||
});
|
});
|
||||||
this.refs.newPhoneNumber.value = "";
|
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
this.setState({continueDisabled: false});
|
this.setState({continueDisabled: false});
|
||||||
if (err.errcode !== 'M_THREEPID_AUTH_FAILED') {
|
if (err.errcode !== 'M_THREEPID_AUTH_FAILED') {
|
||||||
|
@ -213,8 +228,14 @@ export default class PhoneNumbers extends React.Component {
|
||||||
{this.state.verifyError}
|
{this.state.verifyError}
|
||||||
</div>
|
</div>
|
||||||
<form onSubmit={this._onContinueClick} autoComplete={false} noValidate={true}>
|
<form onSubmit={this._onContinueClick} autoComplete={false} noValidate={true}>
|
||||||
<Field id="newPhoneNumberCode" ref="newPhoneNumberCode" label={_t("Verification code")}
|
<Field id="mx_PhoneNumbers_newPhoneNumberCode"
|
||||||
type="text" autoComplete="off" disabled={this.state.continueDisabled} />
|
type="text"
|
||||||
|
label={_t("Verification code")}
|
||||||
|
autoComplete="off"
|
||||||
|
disabled={this.state.continueDisabled}
|
||||||
|
value={this.state.newPhoneNumberCode}
|
||||||
|
onChange={this._onChangeNewPhoneNumberCode}
|
||||||
|
/>
|
||||||
<AccessibleButton onClick={this._onContinueClick} kind="primary"
|
<AccessibleButton onClick={this._onContinueClick} kind="primary"
|
||||||
disabled={this.state.continueDisabled}>
|
disabled={this.state.continueDisabled}>
|
||||||
{_t("Continue")}
|
{_t("Continue")}
|
||||||
|
@ -238,9 +259,15 @@ export default class PhoneNumbers extends React.Component {
|
||||||
<form onSubmit={this._onAddClick} autoComplete={false}
|
<form onSubmit={this._onAddClick} autoComplete={false}
|
||||||
noValidate={true} className="mx_PhoneNumbers_new">
|
noValidate={true} className="mx_PhoneNumbers_new">
|
||||||
<div className="mx_PhoneNumbers_input">
|
<div className="mx_PhoneNumbers_input">
|
||||||
<Field id="newPhoneNumber" ref="newPhoneNumber" label={_t("Phone Number")}
|
<Field id="mx_PhoneNumbers_newPhoneNumber"
|
||||||
type="text" autoComplete="off" disabled={this.state.verifying}
|
type="text"
|
||||||
prefix={phoneCountry} />
|
label={_t("Phone Number")}
|
||||||
|
autoComplete="off"
|
||||||
|
disabled={this.state.verifying}
|
||||||
|
prefix={phoneCountry}
|
||||||
|
value={this.state.newPhoneNumber}
|
||||||
|
onChange={this._onChangeNewPhoneNumber}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{addVerifySection}
|
{addVerifySection}
|
||||||
</form>
|
</form>
|
||||||
|
|
Loading…
Reference in a new issue