UX: Hide the error tooltip when focusing the topic title (#27531)
This commit is contained in:
parent
e29dfe1380
commit
604ca4d46e
|
@ -195,22 +195,24 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if this.composer.canEditTags}}
|
{{#if this.composer.canEditTags}}
|
||||||
<MiniTagChooser
|
<div class="tags-input">
|
||||||
@value={{this.composer.model.tags}}
|
<MiniTagChooser
|
||||||
@onChange={{fn (mut this.composer.model.tags)}}
|
@value={{this.composer.model.tags}}
|
||||||
@options={{hash
|
@onChange={{fn (mut this.composer.model.tags)}}
|
||||||
disabled=this.composer.disableTagsChooser
|
@options={{hash
|
||||||
categoryId=this.composer.model.categoryId
|
disabled=this.composer.disableTagsChooser
|
||||||
minimum=this.composer.model.minimumRequiredTags
|
categoryId=this.composer.model.categoryId
|
||||||
}}
|
minimum=this.composer.model.minimumRequiredTags
|
||||||
/>
|
}}
|
||||||
<PluginOutlet
|
/>
|
||||||
@name="after-composer-tag-input"
|
<PluginOutlet
|
||||||
@outletArgs={{hash composer=this.composer.model}}
|
@name="after-composer-tag-input"
|
||||||
/>
|
@outletArgs={{hash composer=this.composer.model}}
|
||||||
<PopupInputTip
|
/>
|
||||||
@validation={{this.composer.tagValidation}}
|
<PopupInputTip
|
||||||
/>
|
@validation={{this.composer.tagValidation}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<PluginOutlet
|
<PluginOutlet
|
||||||
|
|
|
@ -15,11 +15,20 @@ export default Component.extend({
|
||||||
classNames: ["title-input"],
|
classNames: ["title-input"],
|
||||||
watchForLink: alias("composer.canEditTopicFeaturedLink"),
|
watchForLink: alias("composer.canEditTopicFeaturedLink"),
|
||||||
disabled: or("composer.loading", "composer.disableTitleInput"),
|
disabled: or("composer.loading", "composer.disableTitleInput"),
|
||||||
|
isTitleFocused: false,
|
||||||
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
|
const titleInput = this.element.querySelector("input");
|
||||||
|
|
||||||
|
this._focusHandler = () => this.set("isTitleFocused", true);
|
||||||
|
this._blurHandler = () => this.set("isTitleFocused", false);
|
||||||
|
|
||||||
|
titleInput.addEventListener("focus", this._focusHandler);
|
||||||
|
titleInput.addEventListener("blur", this._blurHandler);
|
||||||
|
|
||||||
if (this.focusTarget === "title") {
|
if (this.focusTarget === "title") {
|
||||||
putCursorAtEnd(this.element.querySelector("input"));
|
putCursorAtEnd(titleInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.get("composer.titleLength") > 0) {
|
if (this.get("composer.titleLength") > 0) {
|
||||||
|
@ -27,19 +36,34 @@ export default Component.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
willDestroyElement() {
|
||||||
|
this._super(...arguments);
|
||||||
|
const titleInput = this.element.querySelector("input");
|
||||||
|
|
||||||
|
if (titleInput) {
|
||||||
|
titleInput.removeEventListener("focus", this._focusHandler);
|
||||||
|
titleInput.removeEventListener("blur", this._blurHandler);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
@discourseComputed(
|
@discourseComputed(
|
||||||
"composer.titleLength",
|
"composer.titleLength",
|
||||||
"composer.missingTitleCharacters",
|
"composer.missingTitleCharacters",
|
||||||
"composer.minimumTitleLength",
|
"composer.minimumTitleLength",
|
||||||
"lastValidatedAt"
|
"lastValidatedAt",
|
||||||
|
"isTitleFocused"
|
||||||
)
|
)
|
||||||
validation(
|
validation(
|
||||||
titleLength,
|
titleLength,
|
||||||
missingTitleChars,
|
missingTitleChars,
|
||||||
minimumTitleLength,
|
minimumTitleLength,
|
||||||
lastValidatedAt
|
lastValidatedAt,
|
||||||
|
isTitleFocused
|
||||||
) {
|
) {
|
||||||
let reason;
|
let reason;
|
||||||
|
if (isTitleFocused) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (titleLength < 1) {
|
if (titleLength < 1) {
|
||||||
reason = I18n.t("composer.error.title_missing");
|
reason = I18n.t("composer.error.title_missing");
|
||||||
} else if (missingTitleChars > 0) {
|
} else if (missingTitleChars > 0) {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import {
|
||||||
click,
|
click,
|
||||||
currentURL,
|
currentURL,
|
||||||
fillIn,
|
fillIn,
|
||||||
|
focus,
|
||||||
settled,
|
settled,
|
||||||
triggerEvent,
|
triggerEvent,
|
||||||
triggerKeyEvent,
|
triggerKeyEvent,
|
||||||
|
@ -155,8 +156,9 @@ acceptance("Composer", function (needs) {
|
||||||
|
|
||||||
await click("#create-topic");
|
await click("#create-topic");
|
||||||
assert.ok(exists(".d-editor-input"), "the composer input is visible");
|
assert.ok(exists(".d-editor-input"), "the composer input is visible");
|
||||||
|
await focus(".title-input input");
|
||||||
assert.ok(
|
assert.ok(
|
||||||
exists(".title-input .popup-tip.bad.hide"),
|
exists(".title-input .popup-tip.good.hide"),
|
||||||
"title errors are hidden by default"
|
"title errors are hidden by default"
|
||||||
);
|
);
|
||||||
assert.ok(
|
assert.ok(
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { click, fillIn, visit } from "@ember/test-helpers";
|
import { click, fillIn, focus, visit } from "@ember/test-helpers";
|
||||||
import { test } from "qunit";
|
import { test } from "qunit";
|
||||||
import { acceptance, exists } from "discourse/tests/helpers/qunit-helpers";
|
import { acceptance, exists } from "discourse/tests/helpers/qunit-helpers";
|
||||||
import selectKit from "discourse/tests/helpers/select-kit-helper";
|
import selectKit from "discourse/tests/helpers/select-kit-helper";
|
||||||
|
@ -13,8 +13,9 @@ acceptance(
|
||||||
await visit("/");
|
await visit("/");
|
||||||
await click("#create-topic");
|
await click("#create-topic");
|
||||||
assert.ok(exists(".d-editor-input"), "the composer input is visible");
|
assert.ok(exists(".d-editor-input"), "the composer input is visible");
|
||||||
|
await focus(".title-input input");
|
||||||
assert.ok(
|
assert.ok(
|
||||||
exists(".title-input .popup-tip.bad.hide"),
|
exists(".title-input .popup-tip.good.hide"),
|
||||||
"title errors are hidden by default"
|
"title errors are hidden by default"
|
||||||
);
|
);
|
||||||
assert.ok(
|
assert.ok(
|
||||||
|
|
|
@ -235,14 +235,16 @@ html.composer-open {
|
||||||
}
|
}
|
||||||
|
|
||||||
.archetype-private_message & {
|
.archetype-private_message & {
|
||||||
// PMs don's have categories, so we need a wider tag input
|
// PMs don't have categories, so we need a wider tag input
|
||||||
.mini-tag-chooser {
|
.mini-tag-chooser {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.category-input {
|
.category-input {
|
||||||
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1 0 40%;
|
flex: 1 0 40%;
|
||||||
max-width: 40%;
|
max-width: 40%;
|
||||||
|
@ -324,18 +326,22 @@ html.composer-open {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.category-input + .mini-tag-chooser {
|
.category-input + .tags-input {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
width: auto;
|
width: auto;
|
||||||
max-width: calc(50% - 4px);
|
max-width: calc(50% - 4px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mini-tag-chooser {
|
.tags-input {
|
||||||
flex-grow: 1;
|
position: relative;
|
||||||
margin: 0 0 8px 0px;
|
margin: 0 0 8px 0px;
|
||||||
z-index: z("composer", "dropdown");
|
flex-grow: 1;
|
||||||
.select-kit-header {
|
.mini-tag-chooser {
|
||||||
color: var(--primary-high);
|
z-index: z("composer", "dropdown");
|
||||||
|
width: 100%;
|
||||||
|
.select-kit-header {
|
||||||
|
color: var(--primary-high);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
.popup-tip {
|
.popup-tip {
|
||||||
@include form-item-sizing;
|
@include form-item-sizing;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
z-index: z("composer", "dropdown") + 1;
|
z-index: z("composer", "dropdown") + 1;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
|
|
@ -24,9 +24,8 @@
|
||||||
.title-and-category {
|
.title-and-category {
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
.mini-tag-chooser {
|
.tags-input {
|
||||||
max-width: 50%;
|
max-width: 50%;
|
||||||
margin-bottom: 8px; // match title input margin
|
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -205,9 +205,10 @@
|
||||||
z-index: z("base");
|
z-index: z("base");
|
||||||
}
|
}
|
||||||
|
|
||||||
.mini-tag-chooser {
|
.tags-input {
|
||||||
margin: 0 0 6px 6px;
|
margin: 0 0 6px 6px;
|
||||||
max-width: calc(50% - 3px);
|
max-width: calc(50% - 3px);
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue