From 336462366e033a43ac76c133bd74dea1b0222c35 Mon Sep 17 00:00:00 2001 From: Luke Barnard <lukeb@openmarket.com> Date: Tue, 25 Apr 2017 11:21:47 +0100 Subject: [PATCH] Improve country dropdown UX and expose +prefix A prefix is now exposed through a change to the API for onOptionChange. This now returns the entire country object which includes iso2, prefix etc. This also shows the prefix in the Registration and Login screens as a prefix to the phone number field. --- src/components/views/login/CountryDropdown.js | 18 +++++++-- src/components/views/login/PasswordLogin.js | 40 +++++++++++-------- .../views/login/RegistrationForm.js | 28 ++++++++----- .../views/settings/AddPhoneNumber.js | 7 ++-- 4 files changed, 61 insertions(+), 32 deletions(-) diff --git a/src/components/views/login/CountryDropdown.js b/src/components/views/login/CountryDropdown.js index 7f6b21650d..da4e770093 100644 --- a/src/components/views/login/CountryDropdown.js +++ b/src/components/views/login/CountryDropdown.js @@ -37,6 +37,7 @@ export default class CountryDropdown extends React.Component { constructor(props) { super(props); this._onSearchChange = this._onSearchChange.bind(this); + this._onOptionChange = this._onOptionChange.bind(this); this.state = { searchQuery: '', @@ -48,7 +49,7 @@ export default class CountryDropdown extends React.Component { // If no value is given, we start with the first // country selected, but our parent component // doesn't know this, therefore we do this. - this.props.onOptionChange(COUNTRIES[0].iso2); + this.props.onOptionChange(COUNTRIES[0]); } } @@ -58,6 +59,10 @@ export default class CountryDropdown extends React.Component { }); } + _onOptionChange(iso2) { + this.props.onOptionChange(COUNTRIES_BY_ISO2[iso2]); + } + _flagImgForIso2(iso2) { // Unicode Regional Indicator Symbol letter 'A' const RIS_A = 0x1F1E6; @@ -68,6 +73,10 @@ export default class CountryDropdown extends React.Component { ); } + getCountryPrefix(iso2) { + return COUNTRIES_BY_ISO2[iso2].prefix; + } + render() { const Dropdown = sdk.getComponent('elements.Dropdown'); @@ -102,9 +111,11 @@ export default class CountryDropdown extends React.Component { // values between mounting and the initial value propgating const value = this.props.value || COUNTRIES[0].iso2; + const getShortOption = this.props.isSmall ? this._flagImgForIso2 : undefined; + return <Dropdown className={this.props.className} - onOptionChange={this.props.onOptionChange} onSearchChange={this._onSearchChange} - menuWidth={298} getShortOption={this._flagImgForIso2} + onOptionChange={this._onOptionChange} onSearchChange={this._onSearchChange} + menuWidth={298} getShortOption={getShortOption} value={value} searchEnabled={true} > {options} @@ -114,6 +125,7 @@ export default class CountryDropdown extends React.Component { CountryDropdown.propTypes = { className: React.PropTypes.string, + isSmall: React.PropTypes.bool, onOptionChange: React.PropTypes.func.isRequired, value: React.PropTypes.string, }; diff --git a/src/components/views/login/PasswordLogin.js b/src/components/views/login/PasswordLogin.js index 568461817c..349dd0d139 100644 --- a/src/components/views/login/PasswordLogin.js +++ b/src/components/views/login/PasswordLogin.js @@ -90,8 +90,11 @@ class PasswordLogin extends React.Component { } onPhoneCountryChanged(country) { - this.setState({phoneCountry: country}); - this.props.onPhoneCountryChanged(country); + this.setState({ + phoneCountry: country.iso2, + phonePrefix: country.prefix, + }); + this.props.onPhoneCountryChanged(country.iso2); } onPhoneNumberChanged(ev) { @@ -121,16 +124,17 @@ class PasswordLogin extends React.Component { const mxidInputClasses = classNames({ "mx_Login_field": true, "mx_Login_username": true, + "mx_Login_field_has_prefix": true, "mx_Login_field_has_suffix": Boolean(this.props.hsDomain), }); let suffix = null; if (this.props.hsDomain) { - suffix = <div className="mx_Login_username_suffix"> + suffix = <div className="mx_Login_field_suffix"> :{this.props.hsDomain} </div>; } - return <div className="mx_Login_username_group"> - <div className="mx_Login_username_prefix">@</div> + return <div className="mx_Login_field_group"> + <div className="mx_Login_field_prefix">@</div> <input className={mxidInputClasses} key="username_input" @@ -145,6 +149,7 @@ class PasswordLogin extends React.Component { </div>; case PasswordLogin.LOGIN_FIELD_PHONE: const CountryDropdown = sdk.getComponent('views.login.CountryDropdown'); + const prefix = this.state.phonePrefix; return <div className="mx_Login_phoneSection"> <CountryDropdown className="mx_Login_phoneCountry" @@ -152,17 +157,20 @@ class PasswordLogin extends React.Component { onOptionChange={this.onPhoneCountryChanged} value={this.state.phoneCountry} /> - <input - className="mx_Login_phoneNumberField mx_Login_field" - ref="phoneNumber" - key="phone_input" - type="text" - name="phoneNumber" - onChange={this.onPhoneNumberChanged} - placeholder="Mobile phone number" - value={this.state.phoneNumber} - autoFocus - /> + <div className="mx_Login_field_group"> + <div className="mx_Login_field_prefix">+{prefix}</div> + <input + className="mx_Login_phoneNumberField mx_Login_field mx_Login_field_has_prefix" + ref="phoneNumber" + key="phone_input" + type="text" + name="phoneNumber" + onChange={this.onPhoneNumberChanged} + placeholder="Mobile phone number" + value={this.state.phoneNumber} + autoFocus + /> + </div> </div>; } } diff --git a/src/components/views/login/RegistrationForm.js b/src/components/views/login/RegistrationForm.js index 4868c9de63..a0b56e30ac 100644 --- a/src/components/views/login/RegistrationForm.js +++ b/src/components/views/login/RegistrationForm.js @@ -270,7 +270,8 @@ module.exports = React.createClass({ _onPhoneCountryChange(newVal) { this.setState({ - phoneCountry: newVal, + phoneCountry: newVal.iso2, + phonePrefix: newVal.prefix, }); }, @@ -316,15 +317,22 @@ module.exports = React.createClass({ className="mx_Login_phoneCountry" value={this.state.phoneCountry} /> - <input type="text" ref="phoneNumber" - placeholder="Mobile phone number (optional)" - defaultValue={this.props.defaultPhoneNumber} - className={this._classForField( - FIELD_PHONE_NUMBER, 'mx_Login_phoneNumberField', 'mx_Login_field' - )} - onBlur={function() {self.validateField(FIELD_PHONE_NUMBER);}} - value={self.state.phoneNumber} - /> + + <div className="mx_Login_field_group"> + <div className="mx_Login_field_prefix">+{this.state.phonePrefix}</div> + <input type="text" ref="phoneNumber" + placeholder="Mobile phone number (optional)" + defaultValue={this.props.defaultPhoneNumber} + className={this._classForField( + FIELD_PHONE_NUMBER, + 'mx_Login_phoneNumberField', + 'mx_Login_field', + 'mx_Login_field_has_prefix' + )} + onBlur={function() {self.validateField(FIELD_PHONE_NUMBER);}} + value={self.state.phoneNumber} + /> + </div> </div> ); diff --git a/src/components/views/settings/AddPhoneNumber.js b/src/components/views/settings/AddPhoneNumber.js index 3a348393aa..35dd5548d1 100644 --- a/src/components/views/settings/AddPhoneNumber.js +++ b/src/components/views/settings/AddPhoneNumber.js @@ -50,7 +50,7 @@ export default WithMatrixClient(React.createClass({ }, _onPhoneCountryChange: function(phoneCountry) { - this.setState({ phoneCountry: phoneCountry }); + this.setState({ phoneCountry: phoneCountry.iso2 }); }, _onPhoneNumberChange: function(ev) { @@ -149,10 +149,11 @@ export default WithMatrixClient(React.createClass({ <div className="mx_UserSettings_profileLabelCell"> </div> <div className="mx_UserSettings_profileInputCell"> - <div className="mx_Login_phoneSection"> + <div className="mx_UserSettings_phoneSection"> <CountryDropdown onOptionChange={this._onPhoneCountryChange} - className="mx_Login_phoneCountry" + className="mx_UserSettings_phoneCountry" value={this.state.phoneCountry} + isSmall={true} /> <input type="text" ref={this._collectAddMsisdnInput}