FIX: Limit displayed groups in <GroupChooser /> to 100 groups (#31288)

Follow-up to https://github.com/discourse/discourse/pull/31271

In the linked PR, we made `<GroupChooser />` use the site's preloaded
list of groups instead of fetching the list from the servers every time
the component is triggered. However, when a site has thousands of
groups, the performance issue has shifted from the server to the browser
— `<GroupChooser />` takes several seconds to render in the browser for
a site with thousands of groups and the sites becomes completely
unresponsive while the component is rendering.

This PR changes the `<GroupChooser />` so it limits the displayed groups
to 100, with ability to filter the list to show more groups when there
are more than 100 groups.
This commit is contained in:
Osama Sayegh 2025-02-12 03:14:57 +03:00 committed by GitHub
parent cf6bab3d85
commit d0498c9e1d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 128 additions and 1 deletions

View File

@ -0,0 +1,88 @@
import { hash } from "@ember/helper";
import { render } from "@ember/test-helpers";
import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import selectKit from "discourse/tests/helpers/select-kit-helper";
import GroupChooser from "select-kit/components/group-chooser";
module("Integration | Component | select-kit/group-chooser", function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function () {
this.subject = selectKit();
});
test("limiting the displayed groups", async function (assert) {
const content = [
{
id: 1,
name: "A",
},
{
id: 2,
name: "AB",
},
{
id: 3,
name: "ABC",
},
];
await render(<template>
<GroupChooser
@content={{content}}
@options={{hash displayedGroupsLimit=1}}
/>
</template>);
await this.subject.expand();
assert.strictEqual(
this.subject.rows().length,
1,
"only 1 group is displayed"
);
assert.strictEqual(
this.subject.rowByIndex(0).name(),
"A",
"the first group in the list is displayed"
);
assert
.dom(this.subject.el().querySelector(".filter-for-more"))
.exists("has indicator that there are more groups");
await this.subject.fillInFilter("AB");
assert.strictEqual(
this.subject.rows().length,
1,
"only 1 group is displayed"
);
assert.strictEqual(
this.subject.rowByIndex(0).name(),
"AB",
"the first group that matches the filter in the list is displayed"
);
assert
.dom(this.subject.el().querySelector(".filter-for-more"))
.exists("has indicator that there are more groups matching the filter");
await this.subject.fillInFilter("C");
assert.strictEqual(
this.subject.rows().length,
1,
"only 1 group is displayed"
);
assert.strictEqual(
this.subject.rowByIndex(0).name(),
"ABC",
"the first group that matches the filter in the list is displayed"
);
assert
.dom(this.subject.el().querySelector(".filter-for-more"))
.doesNotExist(
"doesn't have an indicator when there are no more matching elements"
);
});
});

View File

@ -1,13 +1,52 @@
import { classNames } from "@ember-decorators/component";
import FilterForMore from "select-kit/components/filter-for-more";
import MultiSelectComponent from "select-kit/components/multi-select";
import {
MAIN_COLLECTION,
pluginApiIdentifiers,
selectKitOptions,
} from "select-kit/components/select-kit";
const FILTER_FOR_MORE_GROUPS_COLLECTION = "MORE_GROUPS_COLLECTION";
@classNames("group-chooser")
@selectKitOptions({
allowAny: false,
displayedGroupsLimit: 100,
})
@pluginApiIdentifiers("group-chooser")
export default class GroupChooser extends MultiSelectComponent {}
export default class GroupChooser extends MultiSelectComponent {
init() {
super.init(...arguments);
this.insertAfterCollection(
MAIN_COLLECTION,
FILTER_FOR_MORE_GROUPS_COLLECTION
);
}
modifyComponentForCollection(identifier) {
if (identifier === FILTER_FOR_MORE_GROUPS_COLLECTION) {
return FilterForMore;
}
}
modifyContent(content) {
const limit = this.selectKit.options.displayedGroupsLimit;
if (content.length > limit) {
this.showFilterForMore = true;
content = content.slice(0, limit);
} else {
this.showFilterForMore = false;
}
return content;
}
modifyContentForCollection(identifier) {
if (identifier === FILTER_FOR_MORE_GROUPS_COLLECTION) {
return {
shouldShowMoreTip: this.showFilterForMore,
};
}
}
}