FIX: Show "no category" in category-chooser (#25917)
CategoryChooser component usually displays just categories, but sometimes it can show two none values: a "no category" or Uncategorized. This commit makes sure that these are rendered correctly. The problem was that the "none" item was automatically inserted in the list of options, but that should not always happen. Toggling option `autoInsertNoneItem` requires setting `none` too.
This commit is contained in:
parent
0bb492c6b6
commit
e74a9efee1
|
@ -19,13 +19,13 @@
|
|||
<label>{{i18n "category.parent"}}</label>
|
||||
<CategoryChooser
|
||||
@value={{this.category.parent_category_id}}
|
||||
@categories={{this.parentCategories}}
|
||||
@allowSubCategories={{true}}
|
||||
@allowRestrictedCategories={{true}}
|
||||
@onChange={{action (mut this.category.parent_category_id)}}
|
||||
@options={{hash
|
||||
allowUncategorized=false
|
||||
excludeCategoryId=this.category.id
|
||||
autoInsertNoneItem=true
|
||||
none=true
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -176,6 +176,25 @@ export default class Category extends RestModel {
|
|||
return category;
|
||||
}
|
||||
|
||||
static async asyncFindBySlugPath(slugPath, opts = {}) {
|
||||
const data = { slug_path: slugPath };
|
||||
if (opts.includePermissions) {
|
||||
data.include_permissions = true;
|
||||
}
|
||||
|
||||
const result = await ajax("/categories/find", { data });
|
||||
|
||||
const categories = result["categories"].map((category) => {
|
||||
category = Site.current().updateCategory(category);
|
||||
if (opts.includePermissions) {
|
||||
category.setupGroupsAndPermissions();
|
||||
}
|
||||
return category;
|
||||
});
|
||||
|
||||
return categories[categories.length - 1];
|
||||
}
|
||||
|
||||
static async asyncFindBySlugPathWithID(slugPathWithID) {
|
||||
const result = await ajax("/categories/find", {
|
||||
data: { slug_path_with_id: slugPathWithID },
|
||||
|
|
|
@ -7,11 +7,9 @@ export default DiscourseRoute.extend({
|
|||
router: service(),
|
||||
|
||||
model(params) {
|
||||
return Category.reloadCategoryWithPermissions(
|
||||
params,
|
||||
this.store,
|
||||
this.site
|
||||
);
|
||||
return this.site.lazy_load_categories
|
||||
? Category.asyncFindBySlugPath(params.slug, { includePermissions: true })
|
||||
: Category.reloadCategoryWithPermissions(params, this.store, this.site);
|
||||
},
|
||||
|
||||
afterModel(model) {
|
||||
|
|
|
@ -145,6 +145,36 @@ acceptance("Category Edit", function (needs) {
|
|||
assert.deepEqual(removePayload.allowed_tag_groups, []);
|
||||
});
|
||||
|
||||
test("Editing parent category (disabled Uncategorized)", async function (assert) {
|
||||
this.siteSettings.allow_uncategorized_topics = false;
|
||||
|
||||
await visit("/c/bug/edit");
|
||||
const categoryChooser = selectKit(".category-chooser");
|
||||
await categoryChooser.expand();
|
||||
await categoryChooser.selectRowByValue(6);
|
||||
|
||||
await categoryChooser.expand();
|
||||
|
||||
const names = [...categoryChooser.rows()].map((row) => row.dataset.name);
|
||||
assert.ok(names.includes("(no category)"));
|
||||
assert.notOk(names.includes("Uncategorized"));
|
||||
});
|
||||
|
||||
test("Editing parent category (enabled Uncategorized)", async function (assert) {
|
||||
this.siteSettings.allow_uncategorized_topics = true;
|
||||
|
||||
await visit("/c/bug/edit");
|
||||
const categoryChooser = selectKit(".category-chooser");
|
||||
await categoryChooser.expand();
|
||||
await categoryChooser.selectRowByValue(6);
|
||||
|
||||
await categoryChooser.expand();
|
||||
|
||||
const names = [...categoryChooser.rows()].map((row) => row.dataset.name);
|
||||
assert.ok(names.includes("(no category)"));
|
||||
assert.notOk(names.includes("Uncategorized"));
|
||||
});
|
||||
|
||||
test("Index Route", async function (assert) {
|
||||
await visit("/c/bug/edit");
|
||||
assert.strictEqual(
|
||||
|
|
|
@ -12,12 +12,13 @@ import ComboBoxComponent from "select-kit/components/combo-box";
|
|||
export default ComboBoxComponent.extend({
|
||||
pluginApiIdentifiers: ["category-chooser"],
|
||||
classNames: ["category-chooser"],
|
||||
allowUncategorizedTopics: setting("allow_uncategorized_topics"),
|
||||
allowUncategorized: setting("allow_uncategorized_topics"),
|
||||
fixedCategoryPositionsOnCreate: setting("fixed_category_positions_on_create"),
|
||||
|
||||
selectKitOptions: {
|
||||
filterable: true,
|
||||
allowUncategorized: false,
|
||||
allowUncategorized: "allowUncategorized",
|
||||
autoInsertNoneItem: false,
|
||||
allowSubCategories: true,
|
||||
permissionType: PermissionType.FULL,
|
||||
excludeCategoryId: null,
|
||||
|
@ -30,6 +31,7 @@ export default ComboBoxComponent.extend({
|
|||
|
||||
if (
|
||||
this.site.lazy_load_categories &&
|
||||
this.value &&
|
||||
!Category.hasAsyncFoundAll([this.value])
|
||||
) {
|
||||
// eslint-disable-next-line no-console
|
||||
|
@ -54,10 +56,7 @@ export default ComboBoxComponent.extend({
|
|||
I18n.t(isString ? this.selectKit.options.none : "category.none")
|
||||
)
|
||||
);
|
||||
} else if (
|
||||
this.allowUncategorizedTopics ||
|
||||
this.selectKit.options.allowUncategorized
|
||||
) {
|
||||
} else if (this.selectKit.options.allowUncategorized) {
|
||||
return Category.findUncategorized();
|
||||
} else {
|
||||
const defaultCategoryId = parseInt(
|
||||
|
@ -94,8 +93,10 @@ export default ComboBoxComponent.extend({
|
|||
search(filter) {
|
||||
if (this.site.lazy_load_categories) {
|
||||
return Category.asyncSearch(this._normalize(filter), {
|
||||
scopedCategoryId: this.selectKit.options?.scopedCategoryId,
|
||||
prioritizedCategoryId: this.selectKit.options?.prioritizedCategoryId,
|
||||
includeUncategorized: this.selectKit.options.allowUncategorized,
|
||||
rejectCategoryIds: [this.selectKit.options.excludeCategoryId],
|
||||
scopedCategoryId: this.selectKit.options.scopedCategoryId,
|
||||
prioritizedCategoryId: this.selectKit.options.prioritizedCategoryId,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ export default ComboBoxComponent.extend({
|
|||
navigateToEdit: false,
|
||||
editingCategory: false,
|
||||
editingCategoryTab: null,
|
||||
allowUncategorized: setting("allow_uncategorized_topics"),
|
||||
|
||||
selectKitOptions: {
|
||||
filterable: true,
|
||||
|
@ -40,7 +41,7 @@ export default ComboBoxComponent.extend({
|
|||
displayCategoryDescription: "displayCategoryDescription",
|
||||
headerComponent: "category-drop/category-drop-header",
|
||||
parentCategory: false,
|
||||
allowUncategorized: setting("allow_uncategorized_topics"),
|
||||
allowUncategorized: "allowUncategorized",
|
||||
},
|
||||
|
||||
modifyComponentForRow() {
|
||||
|
|
|
@ -303,9 +303,17 @@ class CategoriesController < ApplicationController
|
|||
|
||||
def find
|
||||
categories = []
|
||||
serializer = params[:include_permissions] ? CategorySerializer : SiteCategorySerializer
|
||||
|
||||
if params[:ids].present?
|
||||
categories = Category.secured(guardian).where(id: params[:ids])
|
||||
elsif params[:slug_path].present?
|
||||
category = Category.find_by_slug_path(params[:slug_path].split("/"))
|
||||
raise Discourse::NotFound if category.blank?
|
||||
guardian.ensure_can_see!(category)
|
||||
|
||||
ancestors = Category.secured(guardian).with_ancestors(category.id).where.not(id: category.id)
|
||||
categories = [*ancestors, category]
|
||||
elsif params[:slug_path_with_id].present?
|
||||
category = Category.find_by_slug_path_with_id(params[:slug_path_with_id])
|
||||
raise Discourse::NotFound if category.blank?
|
||||
|
@ -319,7 +327,7 @@ class CategoriesController < ApplicationController
|
|||
|
||||
Category.preload_user_fields!(guardian, categories)
|
||||
|
||||
render_serialized(categories, SiteCategorySerializer, root: :categories, scope: guardian)
|
||||
render_serialized(categories, serializer, root: :categories, scope: guardian)
|
||||
end
|
||||
|
||||
def search
|
||||
|
@ -333,8 +341,12 @@ class CategoriesController < ApplicationController
|
|||
true
|
||||
end
|
||||
)
|
||||
select_category_ids = params[:select_category_ids].presence
|
||||
reject_category_ids = params[:reject_category_ids].presence
|
||||
if params[:select_category_ids].is_a?(Array)
|
||||
select_category_ids = params[:select_category_ids].map(&:presence)
|
||||
end
|
||||
if params[:reject_category_ids].is_a?(Array)
|
||||
reject_category_ids = params[:reject_category_ids].map(&:presence)
|
||||
end
|
||||
include_subcategories =
|
||||
if params[:include_subcategories].present?
|
||||
ActiveModel::Type::Boolean.new.cast(params[:include_subcategories])
|
||||
|
|
|
@ -1217,6 +1217,12 @@ RSpec.describe CategoriesController do
|
|||
expect(response.parsed_body["categories"].size).to eq(1)
|
||||
expect(response.parsed_body["categories"].map { |c| c["name"] }).to contain_exactly("Foo")
|
||||
end
|
||||
|
||||
it "works with empty categories list" do
|
||||
get "/categories/search.json", params: { select_category_ids: [""] }
|
||||
|
||||
expect(response.parsed_body["categories"].size).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
context "with reject_category_ids" do
|
||||
|
@ -1230,6 +1236,18 @@ RSpec.describe CategoriesController do
|
|||
"Foobar",
|
||||
)
|
||||
end
|
||||
|
||||
it "works with empty categories list" do
|
||||
get "/categories/search.json", params: { reject_category_ids: [""] }
|
||||
|
||||
expect(response.parsed_body["categories"].size).to eq(4)
|
||||
expect(response.parsed_body["categories"].map { |c| c["name"] }).to contain_exactly(
|
||||
"Uncategorized",
|
||||
"Foo",
|
||||
"Foobar",
|
||||
"Notfoo",
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "with include_subcategories" do
|
||||
|
|
Loading…
Reference in New Issue