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

adds validation for fields. #2550

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion res/css/_components.scss
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
@import "./views/elements/_SyntaxHighlight.scss";
@import "./views/elements/_ToggleSwitch.scss";
@import "./views/elements/_ToolTipButton.scss";
@import "./views/elements/_Tooltip.scss";
@import "./views/globals/_MatrixToolbar.scss";
@import "./views/groups/_GroupPublicityToggle.scss";
@import "./views/groups/_GroupRoomList.scss";
Expand Down Expand Up @@ -126,7 +127,6 @@
@import "./views/rooms/_RoomRecoveryReminder.scss";
@import "./views/rooms/_RoomSettings.scss";
@import "./views/rooms/_RoomTile.scss";
@import "./views/rooms/_RoomTooltip.scss";
@import "./views/rooms/_RoomUpgradeWarningBar.scss";
@import "./views/rooms/_SearchBar.scss";
@import "./views/rooms/_SearchableEntityList.scss";
Expand Down
33 changes: 33 additions & 0 deletions res/css/views/elements/_Field.scss
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,36 @@ limitations under the License.
background-color: $field-focused-label-bg-color;
color: $greyed-fg-color;
}

.mx_Field_valid input,
.mx_Field_valid select,
.mx_Field_valid textarea {
border-color: $input-valid-border-color ! important;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What triggered all the important? Would adding an extra selector like .mx_Field.mx_Field_valid avoid the need for it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's so that the valid/invalid state clobbers the focus-selected colour. i can't remember the specificity of :focus, hence clobbering with !important, which i agree is ugly. someone with more css-fu would be welcome to switch the selectors to be more elegant ;)

}

.mx_Field_valid input + label,
.mx_Field_valid select + label,
.mx_Field_valid textarea + label {
color: $input-valid-border-color ! important;
}

.mx_Field_invalid input,
.mx_Field_invalid select,
.mx_Field_invalid textarea {
border-color: $input-invalid-border-color ! important;
}

.mx_Field_invalid input + label,
.mx_Field_invalid select + label,
.mx_Field_invalid textarea + label {
color: $input-invalid-border-color ! important;
}

.mx_Field_tooltip {
margin-top: -12px;
margin-left: 4px;
}

.mx_Field_tooltip.mx_Field_valid {
animation: mx_fadeout 1s 2s forwards;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2019 New Vector Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -14,41 +15,55 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

.mx_RoomTooltip_chevron {
@keyframes mx_fadein {
from { opacity: 0; }
to { opacity: 1; }
}

@keyframes mx_fadeout {
from { opacity: 1; }
to { opacity: 0; }
}

.mx_Tooltip_chevron {
position: absolute;
left: -8px;
top: 4px;
left: -7px;
top: 10px;
width: 0;
height: 0;
border-top: 8px solid transparent;
border-right: 8px solid $menu-bg-color;
border-bottom: 8px solid transparent;
border-top: 7px solid transparent;
border-right: 7px solid $menu-border-color;
border-bottom: 7px solid transparent;
}

.mx_RoomTooltip_chevron:after {
.mx_Tooltip_chevron:after {
content:'';
width: 0;
height: 0;
border-top: 7px solid transparent;
border-right: 7px solid $primary-bg-color;
border-bottom: 7px solid transparent;
position:absolute;
top: -7px;
border-top: 6px solid transparent;
border-right: 6px solid $menu-bg-color;
border-bottom: 6px solid transparent;
position: absolute;
top: -6px;
left: 1px;
}

.mx_RoomTooltip {
.mx_Tooltip {
display: none;
animation: mx_fadein 0.2s;
position: fixed;
border-radius: 5px;
box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.21);
background-color: $primary-bg-color;
border: 1px solid $menu-border-color;
border-radius: 4px;
box-shadow: 4px 4px 12px 0 rgba(118, 131, 156, 0.6);
background-color: $menu-bg-color;
z-index: 2000;
padding: 5px;
padding: 10px;
pointer-events: none;
line-height: 14px;
font-size: 13px;
font-size: 12px;
font-weight: 600;
color: $primary-fg-color;
max-width: 600px;
max-width: 200px;
word-break: break-word;
margin-right: 50px;
}
7 changes: 4 additions & 3 deletions res/themes/dharma/css/_dharma.scss
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,12 @@ $input-darker-fg-color: #9fa9ba;
$input-lighter-bg-color: #f2f5f8;
$input-lighter-fg-color: $input-darker-fg-color;
$input-focused-border-color: #238cf5;
$input-valid-border-color: #7ac9a1;
$input-valid-border-color: $accent-color;
$input-invalid-border-color: $warning-color;

$field-focused-label-bg-color: #ffffff;

$button-bg-color: #7ac9a1;
$button-bg-color: $accent-color;
$button-fg-color: white;

// apart from login forms, which have stronger border
Expand All @@ -89,7 +90,7 @@ $input-fg-color: rgba(74, 74, 74, 0.9);
$scrollbar-thumb-color: rgba(0, 0, 0, 0.2);
$scrollbar-track-color: transparent;
// context menus
$menu-border-color: #ebedf8;
$menu-border-color: #e7e7e7;
$menu-bg-color: #fff;
$menu-selected-color: #f5f8fa;

Expand Down
3 changes: 2 additions & 1 deletion res/themes/light/css/_base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ $button-fg-color: white;
// apart from login forms, which have stronger border
$strong-input-border-color: #c7c7c7;
$input-focused-border-color: #238cf5;
$input-valid-border-color: #7ac9a1;
$input-valid-border-color: $accent-color;
$input-invalid-border-color: $warning-color;

$field-focused-label-bg-color: #ffffff;

Expand Down
4 changes: 2 additions & 2 deletions src/components/structures/BottomLeftMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,8 @@ module.exports = React.createClass({
// Get the label/tooltip to show
getLabel: function(label, show) {
if (show) {
const RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
return <RoomTooltip className="mx_BottomLeftMenu_tooltip" label={label} />;
const Tooltip = sdk.getComponent("elements.Tooltip");
return <Tooltip className="mx_BottomLeftMenu_tooltip" label={label} />;
}
},

Expand Down
10 changes: 10 additions & 0 deletions src/components/views/auth/ServerConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,15 @@ export default class ServerConfig extends React.PureComponent {
this.setState({ hsUrl });
}

onHomeserverValidate = (value) => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is placeholder to show how it's meant to be used.

jryans marked this conversation as resolved.
Show resolved Hide resolved
try {
new URL(value);
return { valid: true, feedback: <div>Valid URL!</div> };
} catch (_) {
return { valid: false, feedback: <div>Invalid URL!</div>};
}
}

onIdentityServerBlur = (ev) => {
this._isTimeoutId = this._waitThenInvoke(this._isTimeoutId, () => {
this.props.onServerConfigChange({
Expand Down Expand Up @@ -134,6 +143,7 @@ export default class ServerConfig extends React.PureComponent {
value={this.state.hsUrl}
onBlur={this.onHomeserverBlur}
onChange={this.onHomeserverChange}
onValidate={this.onHomeserverValidate}
/>
<Field id="mx_ServerConfig_isUrl"
label={_t("Identity Server URL")}
Expand Down
4 changes: 2 additions & 2 deletions src/components/views/elements/ActionButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ export default React.createClass({

let tooltip;
if (this.state.showTooltip) {
const RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
tooltip = <RoomTooltip className="mx_RoleButton_tooltip" label={this.props.label} />;
const Tooltip = sdk.getComponent("elements.Tooltip");
tooltip = <Tooltip className="mx_RoleButton_tooltip" label={this.props.label} />;
}

const icon = this.props.iconPath ?
Expand Down
40 changes: 38 additions & 2 deletions src/components/views/elements/Field.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ limitations under the License.

import React from 'react';
import PropTypes from 'prop-types';
import sdk from '../../../index';

export default class Field extends React.PureComponent {
static propTypes = {
Expand All @@ -30,9 +31,22 @@ export default class Field extends React.PureComponent {
// The type of field to create. Defaults to "input". Should be "input" or "select".
// To define options for a select, use <Field><option ... /></Field>
element: PropTypes.string,
// The callback called whenever the contents of the field
// changes. Returns an object with `valid` boolean field
// and a `feedback` react component field to provide feedback
// to the user.
onValidate: PropTypes.function,
// All other props pass through to the <input>.
};

constructor() {
super();
this.state = {
valid: undefined,
feedback: undefined,
};
}

get value() {
if (!this.refs.fieldInput) return null;
return this.refs.fieldInput.value;
Expand All @@ -45,24 +59,46 @@ export default class Field extends React.PureComponent {
this.refs.fieldInput.value = newValue;
}

onChange = (ev) => {
if (this.props.onValidate) {
const result = this.props.onValidate(this.value);
this.setState({
valid: result.valid,
feedback: result.feedback
});
}
};

render() {
const extraProps = Object.assign({}, this.props);

// Remove explicit properties that shouldn't be copied
delete extraProps.element;
delete extraProps.children;
delete extraProps.onValidate;

// Set some defaults for the element
extraProps.type = extraProps.type || "text";
extraProps.ref = "fieldInput";
extraProps.placeholder = extraProps.placeholder || extraProps.label;

extraProps.onChange = this.onChange;
// make sure we use the current `value` for the field and not the original one
if (this.value != undefined) {
extraProps.value = this.value;
}
const element = this.props.element || "input";
const fieldInput = React.createElement(element, extraProps, this.props.children);

return <div className={`mx_Field mx_Field_${element}`}>
// handle displaying feedback on validity
const validClass = this.state.valid === true ? "mx_Field_valid" :
this.state.valid === false ? "mx_Field_invalid" : "";
const Tooltip = sdk.getComponent("elements.Tooltip");
const feedback = this.state.feedback ? <Tooltip tooltipClassName={`mx_Field_tooltip ${validClass}`} label={ this.state.feedback }/> : "";

return <div className={`mx_Field mx_Field_${element} ${validClass}`}>
{fieldInput}
<label htmlFor={this.props.id}>{this.props.label}</label>
{feedback}
</div>;
}
}
4 changes: 2 additions & 2 deletions src/components/views/elements/TagTile.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ export default React.createClass({
render: function() {
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
const RoomTooltip = sdk.getComponent('rooms.RoomTooltip');
const Tooltip = sdk.getComponent('elements.Tooltip');
const profile = this.state.profile || {};
const name = profile.name || this.props.tag;
const avatarHeight = 40;
Expand All @@ -169,7 +169,7 @@ export default React.createClass({
});

const tip = this.state.hover ?
<RoomTooltip className="mx_TagTile_tooltip" label={name} /> :
<Tooltip className="mx_TagTile_tooltip" label={name} /> :
<div />;
const contextButton = this.state.hover || this.state.menuDisplayed ?
<div className="mx_TagTile_context_button" onClick={this.onContextButtonClick}>
Expand Down
4 changes: 2 additions & 2 deletions src/components/views/elements/ToolTipButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ module.exports = React.createClass({
},

render: function() {
const RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
const tip = this.state.hover ? <RoomTooltip
const Tooltip = sdk.getComponent("elements.Tooltip");
const tip = this.state.hover ? <Tooltip
className="mx_ToolTipButton_container"
tooltipClassName="mx_ToolTipButton_helpText"
label={this.props.helpText}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2019 New Vector Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -23,22 +24,21 @@ import classNames from 'classnames';
const MIN_TOOLTIP_HEIGHT = 25;

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

propTypes: {
// Class applied to the element used to position the tooltip
className: React.PropTypes.string.isRequired,
className: React.PropTypes.string,
// Class applied to the tooltip itself
tooltipClassName: React.PropTypes.string,
// The tooltip is derived from either the room name or a label
room: React.PropTypes.object,
// the react element to put into the tooltip
label: React.PropTypes.node,
},

// Create a wrapper for the tooltip outside the parent and attach it to the body element
componentDidMount: function() {
this.tooltipContainer = document.createElement("div");
this.tooltipContainer.className = "mx_RoomTileTooltip_wrapper";
this.tooltipContainer.className = "mx_Tooltip_wrapper";
document.body.appendChild(this.tooltipContainer);
window.addEventListener('scroll', this._renderTooltip, true);

Expand Down Expand Up @@ -85,11 +85,11 @@ module.exports = React.createClass({
style = this._updatePosition(style);
style.display = "block";

const tooltipClasses = classNames("mx_RoomTooltip", this.props.tooltipClassName);
const tooltipClasses = classNames("mx_Tooltip", this.props.tooltipClassName);

const tooltip = (
<div className={tooltipClasses} style={style}>
<div className="mx_RoomTooltip_chevron" />
<div className="mx_Tooltip_chevron" />
{ this.props.label }
</div>
);
Expand Down
4 changes: 2 additions & 2 deletions src/components/views/groups/GroupInviteTile.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ export default React.createClass({

let tooltip;
if (this.props.collapsed && this.state.hover) {
const RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
tooltip = <RoomTooltip className="mx_RoomTile_tooltip" label={groupName} dir="auto" />;
const Tooltip = sdk.getComponent("elements.Tooltip");
tooltip = <Tooltip className="mx_RoomTile_tooltip" label={groupName} dir="auto" />;
}

const classes = classNames('mx_RoomTile mx_RoomTile_highlight', {
Expand Down
4 changes: 2 additions & 2 deletions src/components/views/messages/MStickerBody.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ export default class MStickerBody extends MImageBody {

if (!content || !content.body || !content.info || !content.info.w) return null;

const RoomTooltip = sdk.getComponent('rooms.RoomTooltip');
const Tooltip = sdk.getComponent('elements.Tooltip');
return <div style={{left: content.info.w + 'px'}} className="mx_MStickerBody_tooltip">
<RoomTooltip label={content.body} />
<Tooltip label={content.body} />
</div>;
}

Expand Down
Loading