FIX: ensures minimum tags logic is correct and shared (#14723)

Also fixes a bug where select-kit was not updating noneItem in multi-selects.
This commit is contained in:
Joffrey JAFFEUX 2021-11-12 14:04:48 +01:00 committed by GitHub
parent 362c47ce6a
commit 904d509cce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 124 additions and 87 deletions

View File

@ -1,3 +1,4 @@
import Category from "discourse/models/category";
import Controller, { inject as controller } from "@ember/controller"; import Controller, { inject as controller } from "@ember/controller";
import DiscourseURL, { userPath } from "discourse/lib/url"; import DiscourseURL, { userPath } from "discourse/lib/url";
import { alias, and, not, or } from "@ember/object/computed"; import { alias, and, not, or } from "@ember/object/computed";
@ -212,11 +213,9 @@ export default Controller.extend(bufferedProperty("model"), {
); );
}, },
@discourseComputed("model.category") @discourseComputed("buffered.category_id")
minimumRequiredTags(category) { minimumRequiredTags(categoryId) {
return category && category.minimum_required_tags > 0 return Category.findById(categoryId)?.minimumRequiredTags || 0;
? category.minimum_required_tags
: null;
}, },
_removeDeleteOnOwnerReplyBookmarks() { _removeDeleteOnOwnerReplyBookmarks() {

View File

@ -42,6 +42,19 @@ const Category = RestModel.extend({
} }
}, },
@discourseComputed(
"required_tag_groups",
"min_tags_from_required_group",
"minimum_required_tags"
)
minimumRequiredTags() {
if (this.required_tag_groups) {
return this.min_tags_from_required_group;
} else {
return this.minimum_required_tags > 0 ? this.minimum_required_tags : null;
}
},
@discourseComputed @discourseComputed
availablePermissions() { availablePermissions() {
return [ return [

View File

@ -157,26 +157,9 @@ const Composer = RestModel.extend({
return categoryId ? this.site.categories.findBy("id", categoryId) : null; return categoryId ? this.site.categories.findBy("id", categoryId) : null;
}, },
@discourseComputed("category") @discourseComputed("category.minimumRequiredTags")
minimumRequiredTags(category) { minimumRequiredTags(minimumRequiredTags) {
if (category) { return minimumRequiredTags || 0;
if (category.required_tag_groups) {
return category.min_tags_from_required_group;
} else {
return category.minimum_required_tags > 0
? category.minimum_required_tags
: null;
}
}
return null;
},
@discourseComputed("category")
requiredTagGroups(category) {
return category && category.required_tag_groups
? category.required_tag_groups
: null;
}, },
creatingTopic: equal("action", CREATE_TOPIC), creatingTopic: equal("action", CREATE_TOPIC),

View File

@ -99,7 +99,6 @@
options=(hash options=(hash
categoryId=model.categoryId categoryId=model.categoryId
minimum=model.minimumRequiredTags minimum=model.minimumRequiredTags
requiredTagGroups=model.requiredTagGroups
) )
}} }}
{{popup-input-tip validation=tagValidation}} {{popup-input-tip validation=tagValidation}}

View File

@ -221,6 +221,50 @@ module("Unit | Model | category", function () {
assert.deepEqual(Category.findBySlugPathWithID("foo/baz/3"), baz); assert.deepEqual(Category.findBySlugPathWithID("foo/baz/3"), baz);
}); });
test("minimumRequiredTags", function (assert) {
const store = createStore();
let foo = store.createRecord("category", {
id: 1,
slug: "foo",
required_tag_groups: ["bar"],
min_tags_from_required_group: 2,
});
assert.equal(foo.minimumRequiredTags, 2);
foo = store.createRecord("category", {
id: 2,
slug: "foo",
});
assert.equal(foo.minimumRequiredTags, null);
foo = store.createRecord("category", {
id: 3,
slug: "foo",
minimum_required_tags: 0,
});
assert.equal(foo.minimumRequiredTags, null);
foo = store.createRecord("category", {
id: 4,
slug: "foo",
minimum_required_tags: 2,
});
assert.equal(foo.minimumRequiredTags, 2);
foo = store.createRecord("category", {
id: 5,
slug: "foo",
min_tags_from_required_group: 2,
});
assert.equal(foo.minimumRequiredTags, null);
});
test("search with category name", function (assert) { test("search with category name", function (assert) {
const store = createStore(), const store = createStore(),
category1 = store.createRecord("category", { category1 = store.createRecord("category", {

View File

@ -20,6 +20,7 @@ export default ComboBoxComponent.extend({
permissionType: PermissionType.FULL, permissionType: PermissionType.FULL,
excludeCategoryId: null, excludeCategoryId: null,
scopedCategoryId: null, scopedCategoryId: null,
prioritizedCategoryId: null,
}, },
modifyComponentForRow() { modifyComponentForRow() {

View File

@ -12,6 +12,7 @@ export default SingleSelectComponent.extend({
caretUpIcon: "caret-up", caretUpIcon: "caret-up",
caretDownIcon: "caret-down", caretDownIcon: "caret-down",
showCaret: false, showCaret: false,
customStyle: null,
}, },
modifyComponentForRow() { modifyComponentForRow() {

View File

@ -23,10 +23,8 @@ export default MultiSelectComponent.extend(TagsMixin, {
termMatchesForbidden: false, termMatchesForbidden: false,
categoryId: null, categoryId: null,
everyTag: false, everyTag: false,
none: "tagging.choose_for_topic",
closeOnChange: false, closeOnChange: false,
maximum: "maximumSelectedTags", maximum: "maxTagsPerTopic",
minimum: "minimumSelectedTags",
autoInsertNoneItem: false, autoInsertNoneItem: false,
}, },
@ -38,31 +36,20 @@ export default MultiSelectComponent.extend(TagsMixin, {
return "tag-row"; return "tag-row";
}, },
allowAnyTag: or("allowCreate", "site.can_create_tag"), modifyNoSelection() {
if (this.selectKit.options.minimum > 0) {
maximumSelectedTags: computed(function () { return this.defaultItem(
return parseInt( null,
this.options.limit || I18n.t("tagging.choose_for_topic_required", {
this.selectKit.options.maximum || count: this.selectKit.options.minimum,
this.maxTagsPerTopic, })
10 );
); } else {
}), return this.defaultItem(null, I18n.t("tagging.choose_for_topic"));
minimumSelectedTags: computed(function () {
if (
this.selectKit.options.minimum ||
this.selectKit.options.requiredTagGroups
) {
const minimum = parseInt(this.selectKit.options.minimum, 10);
if (minimum > 0) {
return this.defaultItem(
null,
I18n.t("select_kit.min_content_not_reached", { count: minimum })
);
}
} }
}), },
allowAnyTag: or("allowCreate", "site.can_create_tag"),
caretIcon: computed("value.[]", "content.[]", function () { caretIcon: computed("value.[]", "content.[]", function () {
const maximum = this.selectKit.options.maximum; const maximum = this.selectKit.options.maximum;

View File

@ -124,35 +124,40 @@ export default SelectKitComponent.extend({
} }
}, },
selectedContent: computed("value.[]", "content.[]", function () { selectedContent: computed(
const value = makeArray(this.value).map((v) => "value.[]",
this.selectKit.options.castInteger && this._isNumeric(v) ? Number(v) : v "content.[]",
); "selectKit.noneItem",
function () {
const value = makeArray(this.value).map((v) =>
this.selectKit.options.castInteger && this._isNumeric(v) ? Number(v) : v
);
if (value.length) { if (value.length) {
let content = []; let content = [];
value.forEach((v) => { value.forEach((v) => {
if (this.selectKit.valueProperty) { if (this.selectKit.valueProperty) {
const c = makeArray(this.content).findBy( const c = makeArray(this.content).findBy(
this.selectKit.valueProperty, this.selectKit.valueProperty,
v v
); );
if (c) { if (c) {
content.push(c); content.push(c);
}
} else {
if (makeArray(this.content).includes(v)) {
content.push(v);
}
} }
} else { });
if (makeArray(this.content).includes(v)) {
content.push(v);
}
}
});
return this.selectKit.modifySelection(content); return this.selectKit.modifySelection(content);
}
return this.selectKit.noneItem;
} }
),
return null;
}),
_onKeydown(event) { _onKeydown(event) {
if ( if (

View File

@ -209,8 +209,12 @@ export default Component.extend(
didReceiveAttrs() { didReceiveAttrs() {
this._super(...arguments); this._super(...arguments);
const computedOptions = {};
Object.keys(this.selectKitOptions).forEach((key) => { Object.keys(this.selectKitOptions).forEach((key) => {
if (isPresent(this.options[key])) {
this.selectKit.options.set(key, this.options[key]);
return;
}
const value = this.selectKitOptions[key]; const value = this.selectKitOptions[key];
if ( if (
@ -219,9 +223,9 @@ export default Component.extend(
key === "componentForCollection" key === "componentForCollection"
) { ) {
if (typeof value === "string") { if (typeof value === "string") {
computedOptions[key] = () => value; this.selectKit.options.set(key, () => value);
} else { } else {
computedOptions[key] = bind(this, value); this.selectKit.options.set(key, bind(this, value));
} }
return; return;
@ -234,15 +238,13 @@ export default Component.extend(
) { ) {
const computedValue = get(this, value); const computedValue = get(this, value);
if (typeof computedValue !== "function") { if (typeof computedValue !== "function") {
computedOptions[key] = get(this, value); this.selectKit.options.set(key, computedValue);
return; return;
} }
} }
computedOptions[key] = value;
this.selectKit.options.set(key, value);
}); });
this.selectKit.options.setProperties(
Object.assign(computedOptions, this.options || {})
);
this.selectKit.setProperties({ this.selectKit.setProperties({
hasSelection: !isEmpty(this.value), hasSelection: !isEmpty(this.value),
@ -264,6 +266,7 @@ export default Component.extend(
}, },
selectKitOptions: { selectKitOptions: {
allowAny: false,
showFullTitle: true, showFullTitle: true,
none: null, none: null,
translatedNone: null, translatedNone: null,
@ -277,7 +280,6 @@ export default Component.extend(
maximum: null, maximum: null,
maximumLabel: null, maximumLabel: null,
minimum: null, minimum: null,
minimumLabel: null,
autoInsertNoneItem: true, autoInsertNoneItem: true,
closeOnChange: true, closeOnChange: true,
limitMatches: null, limitMatches: null,

View File

@ -13,7 +13,7 @@
id=(concat selectKit.uniqueID "-filter") id=(concat selectKit.uniqueID "-filter")
}} }}
{{#if selectedContent}} {{#if selectedContent.length}}
<div class="selected-content"> <div class="selected-content">
{{#each selectedContent as |item|}} {{#each selectedContent as |item|}}
{{component selectKit.options.selectedChoiceComponent {{component selectKit.options.selectedChoiceComponent

View File

@ -3768,6 +3768,9 @@ en:
changed: "tags changed:" changed: "tags changed:"
tags: "Tags" tags: "Tags"
choose_for_topic: "optional tags" choose_for_topic: "optional tags"
choose_for_topic_required:
one: "select at least %{count} tag..."
other: "select at least %{count} tags..."
info: "Info" info: "Info"
default_info: "This tag isn't restricted to any categories, and has no synonyms. To add restrictions, put this tag in a <a href=%{basePath}/tag_groups>tag group</a>." default_info: "This tag isn't restricted to any categories, and has no synonyms. To add restrictions, put this tag in a <a href=%{basePath}/tag_groups>tag group</a>."
category_restricted: "This tag is restricted to categories you don't have permission to access." category_restricted: "This tag is restricted to categories you don't have permission to access."