Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Commit

Permalink
Convert uncontrolled Field usages to controlled
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jryans committed Mar 12, 2019
1 parent cff3c94 commit d4dbba3
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 31 deletions.
9 changes: 0 additions & 9 deletions src/components/views/auth/PasswordLogin.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,6 @@ class PasswordLogin extends React.Component {
this.isLoginEmpty = this.isLoginEmpty.bind(this);
}

componentWillMount() {
this._passwordField = null;
this._loginField = null;
}

onForgotPasswordClick(ev) {
ev.preventDefault();
ev.stopPropagation();
Expand Down Expand Up @@ -180,7 +175,6 @@ class PasswordLogin extends React.Component {
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"
Expand All @@ -196,7 +190,6 @@ class PasswordLogin extends React.Component {
return <Field
className={classNames(classes)}
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"
Expand All @@ -223,7 +216,6 @@ class PasswordLogin extends React.Component {
return <Field
className={classNames(classes)}
id="mx_PasswordLogin_phoneNumber"
ref={(e) => { this._loginField = e; }}
name="phoneNumber"
key="phone_input"
type="text"
Expand Down Expand Up @@ -344,7 +336,6 @@ class PasswordLogin extends React.Component {
<Field
className={pwFieldClass}
id="mx_PasswordLogin_password"
ref={(e) => { this._passwordField = e; }}
type="password"
name="password"
label={_t('Password')}
Expand Down
6 changes: 6 additions & 0 deletions src/components/views/elements/Field.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ export default class Field extends React.PureComponent {
label: PropTypes.string,
// The field's placeholder string. Defaults to the label.
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.
prefix: PropTypes.node,
// The callback called whenever the contents of the field
Expand All @@ -50,6 +53,7 @@ export default class Field extends React.PureComponent {
};
}

/* TODO: Remove me */
get value() {
if (!this.refs.fieldInput) return null;
return this.refs.fieldInput.value;
Expand Down Expand Up @@ -87,6 +91,8 @@ export default class Field extends React.PureComponent {
inputProps.placeholder = inputProps.placeholder || inputProps.label;

inputProps.onChange = this.onChange;

/* TODO: Remove me */
// make sure we use the current `value` for the field and not the original one
if (inputProps.value === undefined) {
inputProps.value = this.value || "";
Expand Down
54 changes: 47 additions & 7 deletions src/components/views/settings/ChangePassword.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import sessionStore from '../../../stores/SessionStore';

module.exports = React.createClass({
displayName: 'ChangePassword',

propTypes: {
onFinished: PropTypes.func,
onError: PropTypes.func,
Expand Down Expand Up @@ -73,6 +74,9 @@ module.exports = React.createClass({
return {
phase: this.Phases.Edit,
cachedPassword: null,
oldPassword: "",
newPassword: "",
newPasswordConfirm: "",
};
},

Expand Down Expand Up @@ -165,6 +169,9 @@ module.exports = React.createClass({
}).finally(() => {
this.setState({
phase: this.Phases.Edit,
oldPassword: "",
newPassword: "",
newPasswordConfirm: "",
});
}).done();
},
Expand Down Expand Up @@ -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) {
ev.preventDefault();
const oldPassword = this.state.cachedPassword || this.refs.old_input.value;
const newPassword = this.refs.new_input.value;
const confirmPassword = this.refs.confirm_input.value;
const oldPassword = this.state.cachedPassword || this.state.oldPassword;
const newPassword = this.state.newPassword;
const confirmPassword = this.state.newPasswordConfirm;
const err = this.props.onCheckPassword(
oldPassword, newPassword, confirmPassword,
);
Expand All @@ -217,7 +242,12 @@ module.exports = React.createClass({
if (!this.state.cachedPassword) {
currentPassword = (
<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>
);
}
Expand All @@ -230,11 +260,21 @@ module.exports = React.createClass({
<form className={this.props.className} onSubmit={this.onClickChange}>
{ currentPassword }
<div className={rowClassName}>
<Field id="password1" type="password" ref="new_input" label={passwordLabel}
autoFocus={this.props.autoFocusNewPasswordInput} />
<Field id="mx_ChangePassword_newPassword"
type="password"
label={passwordLabel}
value={this.state.newPassword}
autoFocus={this.props.autoFocusNewPasswordInput}
onChange={this.onChangeNewPassword}
/>
</div>
<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>
<AccessibleButton className={buttonClassName} kind={this.props.buttonKind} onClick={this.onClickChange}>
{ _t('Change Password') }
Expand Down
25 changes: 19 additions & 6 deletions src/components/views/settings/EmailAddresses.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ export default class EmailAddresses extends React.Component {
verifying: false,
addTask: null,
continueDisabled: false,
newEmailAddress: "",
};
}

Expand All @@ -134,14 +135,20 @@ export default class EmailAddresses extends React.Component {
this.setState({emails: this.state.emails.filter((e) => e !== address)});
};

_onChangeNewEmailAddress = (e) => {
this.setState({
newEmailAddress: e.target.value,
});
};

_onAddClick = (e) => {
e.stopPropagation();
e.preventDefault();

if (!this.refs.newEmailAddress) return;
if (!this.state.newEmailAddress) return;

const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const email = this.refs.newEmailAddress.value;
const email = this.state.newEmailAddress;

// TODO: Inline field validation
if (!Email.looksValid(email)) {
Expand Down Expand Up @@ -173,14 +180,14 @@ export default class EmailAddresses extends React.Component {

this.setState({continueDisabled: true});
this.state.addTask.checkEmailLinkClicked().then(() => {
const email = this.refs.newEmailAddress.value;
const email = this.state.newEmailAddress;
this.setState({
emails: [...this.state.emails, {address: email, medium: "email"}],
addTask: null,
continueDisabled: false,
verifying: false,
newEmailAddress: "",
});
this.refs.newEmailAddress.value = "";
}).catch((err) => {
this.setState({continueDisabled: false});
if (err.errcode !== 'M_THREEPID_AUTH_FAILED') {
Expand Down Expand Up @@ -221,8 +228,14 @@ export default class EmailAddresses extends React.Component {
{existingEmailElements}
<form onSubmit={this._onAddClick} autoComplete={false}
noValidate={true} className="mx_EmailAddresses_new">
<Field id="newEmailAddress" ref="newEmailAddress" label={_t("Email Address")}
type="text" autoComplete="off" disabled={this.state.verifying} />
<Field id="mx_EmailAddressses_newEmailAddress"
type="text"
label={_t("Email Address")}
autoComplete="off"
disabled={this.state.verifying}
value={this.state.newEmailAddress}
onChange={this._onChangeNewEmailAddress}
/>
{addButton}
</form>
</div>
Expand Down
45 changes: 36 additions & 9 deletions src/components/views/settings/PhoneNumbers.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ export default class PhoneNumbers extends React.Component {
addTask: null,
continueDisabled: false,
phoneCountry: "",
newPhoneNumber: "",
newPhoneNumberCode: "",
};
}

Expand All @@ -132,14 +134,26 @@ export default class PhoneNumbers extends React.Component {
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) => {
e.stopPropagation();
e.preventDefault();

if (!this.refs.newPhoneNumber) return;
if (!this.state.newPhoneNumber) return;

const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const phoneNumber = this.refs.newPhoneNumber.value;
const phoneNumber = this.state.newPhoneNumber;
const phoneCountry = this.state.phoneCountry;

const task = new AddThreepid();
Expand All @@ -162,7 +176,7 @@ export default class PhoneNumbers extends React.Component {
e.preventDefault();

this.setState({continueDisabled: true});
const token = this.refs.newPhoneNumberCode.value;
const token = this.state.newPhoneNumberCode;
this.state.addTask.haveMsisdnToken(token).then(() => {
this.setState({
msisdns: [...this.state.msisdns, {address: this.state.verifyMsisdn, medium: "msisdn"}],
Expand All @@ -171,8 +185,9 @@ export default class PhoneNumbers extends React.Component {
verifying: false,
verifyMsisdn: "",
verifyError: null,
newPhoneNumber: "",
newPhoneNumberCode: "",
});
this.refs.newPhoneNumber.value = "";
}).catch((err) => {
this.setState({continueDisabled: false});
if (err.errcode !== 'M_THREEPID_AUTH_FAILED') {
Expand Down Expand Up @@ -213,8 +228,14 @@ export default class PhoneNumbers extends React.Component {
{this.state.verifyError}
</div>
<form onSubmit={this._onContinueClick} autoComplete={false} noValidate={true}>
<Field id="newPhoneNumberCode" ref="newPhoneNumberCode" label={_t("Verification code")}
type="text" autoComplete="off" disabled={this.state.continueDisabled} />
<Field id="mx_PhoneNumbers_newPhoneNumberCode"
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"
disabled={this.state.continueDisabled}>
{_t("Continue")}
Expand All @@ -238,9 +259,15 @@ export default class PhoneNumbers extends React.Component {
<form onSubmit={this._onAddClick} autoComplete={false}
noValidate={true} className="mx_PhoneNumbers_new">
<div className="mx_PhoneNumbers_input">
<Field id="newPhoneNumber" ref="newPhoneNumber" label={_t("Phone Number")}
type="text" autoComplete="off" disabled={this.state.verifying}
prefix={phoneCountry} />
<Field id="mx_PhoneNumbers_newPhoneNumber"
type="text"
label={_t("Phone Number")}
autoComplete="off"
disabled={this.state.verifying}
prefix={phoneCountry}
value={this.state.newPhoneNumber}
onChange={this._onChangeNewPhoneNumber}
/>
</div>
{addVerifySection}
</form>
Expand Down

0 comments on commit d4dbba3

Please sign in to comment.