diff --git a/res/css/views/elements/_Tooltip.scss b/res/css/views/elements/_Tooltip.scss
index 2f35bd338e..43ddf6dde5 100644
--- a/res/css/views/elements/_Tooltip.scss
+++ b/res/css/views/elements/_Tooltip.scss
@@ -50,7 +50,6 @@ limitations under the License.
.mx_Tooltip {
display: none;
- animation: mx_fadein 0.2s;
position: fixed;
border: 1px solid $menu-border-color;
border-radius: 4px;
@@ -66,4 +65,12 @@ limitations under the License.
max-width: 200px;
word-break: break-word;
margin-right: 50px;
+
+ &.mx_Tooltip_visible {
+ animation: mx_fadein 0.2s forwards;
+ }
+
+ &.mx_Tooltip_invisible {
+ animation: mx_fadeout 0.1s forwards;
+ }
}
diff --git a/src/components/views/elements/Field.js b/src/components/views/elements/Field.js
index 91447a8846..93bea70fc8 100644
--- a/src/components/views/elements/Field.js
+++ b/src/components/views/elements/Field.js
@@ -99,10 +99,23 @@ export default class Field extends React.PureComponent {
focused,
allowEmpty,
});
- this.setState({
- valid,
- feedback,
- });
+
+ if (feedback) {
+ this.setState({
+ valid,
+ feedback,
+ feedbackVisible: true,
+ });
+ } else {
+ // When we receive null `feedback`, we want to hide the tooltip.
+ // We leave the previous `feedback` content in state without updating it,
+ // so that we can hide the tooltip containing the most recent feedback
+ // via CSS animation.
+ this.setState({
+ valid,
+ feedbackVisible: false,
+ });
+ }
}
validateOnChange = throttle(() => {
@@ -147,6 +160,7 @@ export default class Field extends React.PureComponent {
if (this.state.feedback) {
tooltip =