FEATURE: Add "+ subcategories" option back (#26035)

This option was introduced at some point in the past, but was removed
during the work necessary to make Discourse work with a large number of
categories.

Follow up to commit 2e68ead45b.
This commit is contained in:
Bianca Nenciu 2024-03-06 20:14:36 +02:00 committed by GitHub
parent 99b6068ede
commit 8dbcfef3fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 128 additions and 27 deletions

View File

@ -135,10 +135,10 @@ export function defaultCategoryLinkRenderer(category, opts) {
dataAttributes += ` data-parent-category-id="${parentCat.id}"`;
}
html += `<span
${dataAttributes}
data-drop-close="true"
class="${classNames}"
html += `<span
${dataAttributes}
data-drop-close="true"
class="${classNames}"
${
opts.previewColor
? `style="--category-badge-color: #${category.color}"`
@ -169,6 +169,13 @@ export function defaultCategoryLinkRenderer(category, opts) {
html += buildTopicCount(opts.topicCount);
}
if (opts.subcategoryCount) {
html += `<span class="plus-subcategories">${I18n.t(
"category_row.subcategory_count",
{ count: opts.subcategoryCount }
)}</span>`;
}
if (href) {
href = ` href="${href}" `;
}

View File

@ -464,6 +464,17 @@ export default class Category extends RestModel {
return [...(parentAncestors || []), this];
}
@discourseComputed("subcategories")
descendants() {
const descendants = [this];
for (let i = 0; i < descendants.length; i++) {
if (descendants[i].subcategories) {
descendants.push(...descendants[i].subcategories);
}
}
return descendants;
}
@discourseComputed("parentCategory.level")
level(parentLevel) {
if (!parentLevel) {

View File

@ -0,0 +1,57 @@
import { render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { module, test } from "qunit";
import Category from "discourse/models/category";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import selectKit from "discourse/tests/helpers/select-kit-helper";
module(
"Integration | Component | select-kit/category-selector",
function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function () {
this.set("subject", selectKit());
});
test("with value", async function (assert) {
const category = Category.findById(1001);
const subcategory = Category.findById(1002);
this.set("value", [category, subcategory]);
await render(hbs`
<CategorySelector
@categories={{this.value}}
/>
`);
assert.strictEqual(this.subject.header().value(), "1001,1002");
assert.strictEqual(
this.subject.header().label(),
"Parent Category, Sub Category"
);
});
test("has +subcategories row", async function (assert) {
this.set("value", []);
await render(hbs`
<CategorySelector
@categories={{this.value}}
/>
`);
await this.subject.expand();
await this.subject.fillInFilter("Parent Category");
assert.equal(this.subject.rows().length, 2);
assert.equal(
this.subject.rowByIndex(0).el().innerText.replace("\n", " "),
"Parent Category × 95"
);
assert.equal(
this.subject.rowByIndex(1).el().innerText.replace("\n", " "),
"Parent Category + 2 subcategories"
);
});
}
);

View File

@ -64,7 +64,7 @@ export default class CategoryRow extends Component {
}
get label() {
return this.args.item?.name;
return this.args.item?.name || this.args.item?.label;
}
get displayCategoryDescription() {

View File

@ -1,5 +1,6 @@
import { computed } from "@ember/object";
import EmberObject, { computed } from "@ember/object";
import { mapBy } from "@ember/object/computed";
import { categoryBadgeHTML } from "discourse/helpers/category-link";
import Category from "discourse/models/category";
import { makeArray } from "discourse-common/lib/helpers";
import CategoryRow from "select-kit/components/category-row";
@ -51,34 +52,51 @@ export default MultiSelectComponent.extend({
},
async search(filter) {
if (!this.site.lazy_load_categories) {
return this._super(filter);
let categories;
if (this.site.lazy_load_categories) {
const rejectCategoryIds = new Set([
...this.categories.map((c) => c.id),
...this.blockedCategories.map((c) => c.id),
]);
categories = await Category.asyncSearch(filter, {
includeUncategorized:
this.options?.allowUncategorized !== undefined
? this.options.allowUncategorized
: this.selectKit.options.allowUncategorized,
rejectCategoryIds: Array.from(rejectCategoryIds),
});
} else {
categories = this._super(filter);
}
const rejectCategoryIds = new Set([
...this.categories.map((c) => c.id),
...this.blockedCategories.map((c) => c.id),
]);
// If there is a single match and it has subcategories, add a row for
// selecting all
if (categories.length === 1) {
const descendants = categories[0].descendants;
if (descendants.length > 1) {
categories.push(
EmberObject.create({
label: categoryBadgeHTML(descendants[0], {
link: false,
recursive: true,
subcategoryCount: descendants.length - 1,
}),
categories: [...descendants],
})
);
}
}
return await Category.asyncSearch(filter, {
includeUncategorized:
this.options?.allowUncategorized !== undefined
? this.options.allowUncategorized
: this.selectKit.options.allowUncategorized,
rejectCategoryIds: Array.from(rejectCategoryIds),
});
return categories;
},
select(value, item) {
if (item.multiCategory) {
const items = item.multiCategory.map((id) =>
Category.findById(parseInt(id, 10))
if (item.categories) {
this.selectKit.change(
makeArray(this.value).concat(item.categories.mapBy("id")),
makeArray(this.selectedContent).concat(item.categories)
);
const newValues = makeArray(this.value).concat(items.map((i) => i.id));
const newContent = makeArray(this.selectedContent).concat(items);
this.selectKit.change(newValues, newContent);
} else {
this._super(value, item);
}

View File

@ -11,6 +11,11 @@
.topic-count {
margin-left: 0.25em;
}
.plus-subcategories {
font-size: var(--font-down-2);
margin-left: 0.25em;
}
}
.selected-choice-category {

View File

@ -2340,6 +2340,9 @@ en:
loading: Loading…
category_row:
subcategory_count:
one: "+ %{count} subcategory"
other: "+ %{count} subcategories"
topic_count:
one: "%{count} topic in this category"
other: "%{count} topics in this category"