Fix bug where 'other homeserver' would unfocus

It turns out the answer to this was not all that complex: we had
two nested <label>s: one for the radio button and one for the Field,
which presumably causes both of them to generate click events and
focus something and what ends up focused is some kind of race condition
depending on, apparently, how fast you click.

Notes: Fix bug where the 'other homeserver' field in the server selection dialog would become briefly focus and then unfocus when clicked.
This commit is contained in:
David Baker 2021-07-16 22:27:31 +01:00
parent b55f69a74c
commit f9b45677d6
3 changed files with 34 additions and 7 deletions

View file

@ -46,7 +46,7 @@ limitations under the License.
width: $font-16px;
}
> input[type=radio] {
input[type=radio] {
// Remove the OS's representation
margin: 0;
padding: 0;
@ -112,6 +112,12 @@ limitations under the License.
}
}
}
.mx_RadioButton_innerLabel {
display: flex;
position: relative;
top: 4px;
}
}
.mx_RadioButton_outlined {

View file

@ -205,13 +205,14 @@ export default class ServerPickerDialog extends React.PureComponent<IProps, ISta
className="mx_ServerPickerDialog_otherHomeserverRadio"
checked={!this.state.defaultChosen}
onChange={this.onOtherChosen}
childrenInLabel={false}
>
<Field
type="text"
className="mx_ServerPickerDialog_otherHomeserver"
label={_t("Other homeserver")}
onChange={this.onHomeserverChange}
onClick={this.onOtherChosen}
onFocus={this.onOtherChosen}
ref={this.fieldRef}
onValidate={this.onHomeserverValidate}
value={this.state.otherHomeserver}

View file

@ -20,6 +20,10 @@ import { replaceableComponent } from "../../../utils/replaceableComponent";
interface IProps extends React.InputHTMLAttributes<HTMLInputElement> {
outlined?: boolean;
// If true (default), the children will be contained within a <label> element
// If false, they'll be in a div. Putting interactive components like Fields in labels can
// cause strange bugs like https://github.com/vector-im/element-web/issues/18031
childrenInLabel?: boolean;
}
interface IState {
@ -29,10 +33,11 @@ interface IState {
export default class StyledRadioButton extends React.PureComponent<IProps, IState> {
public static readonly defaultProps = {
className: '',
childrenInLabel: true,
};
public render() {
const { children, className, disabled, outlined, ...otherProps } = this.props;
const { children, className, disabled, outlined, childrenInLabel, ...otherProps } = this.props;
const _className = classnames(
'mx_RadioButton',
className,
@ -42,12 +47,27 @@ export default class StyledRadioButton extends React.PureComponent<IProps, IStat
"mx_RadioButton_checked": this.props.checked,
"mx_RadioButton_outlined": outlined,
});
return <label className={_className}>
const radioButton = <React.Fragment>
<input type='radio' disabled={disabled} {...otherProps} />
{/* Used to render the radio button circle */}
<div><div /></div>
<div className="mx_RadioButton_content">{children}</div>
<div className="mx_RadioButton_spacer" />
</label>;
</React.Fragment>;
if (childrenInLabel) {
return <label className={_className}>
{radioButton}
<div className="mx_RadioButton_content">{children}</div>
<div className="mx_RadioButton_spacer" />
</label>;
} else {
return <div className={_className}>
<label className="mx_RadioButton_innerLabel">
{radioButton}
</label>
<div className="mx_RadioButton_content">{children}</div>
<div className="mx_RadioButton_spacer" />
</div>;
}
}
}