This commit introduces the experimental `registerUserCategorySectionLinkCountable` and `refreshUserSidebarCategoriesSectionCounts` plugin APIs that allows a plugin to register custom countables to category section links on top of the defaults of unread and new.
748 lines
20 KiB
JavaScript
748 lines
20 KiB
JavaScript
import { test } from "qunit";
|
|
import I18n from "I18n";
|
|
import { click, settled, visit } from "@ember/test-helpers";
|
|
import {
|
|
acceptance,
|
|
exists,
|
|
query,
|
|
queryAll,
|
|
updateCurrentUser,
|
|
} from "discourse/tests/helpers/qunit-helpers";
|
|
import { withPluginApi } from "discourse/lib/plugin-api";
|
|
import Site from "discourse/models/site";
|
|
import { resetCustomCountables } from "discourse/lib/sidebar/user/categories-section/category-section-link";
|
|
import { UNREAD_LIST_DESTINATION } from "discourse/controllers/preferences/sidebar";
|
|
import { bind } from "discourse-common/utils/decorators";
|
|
|
|
acceptance("Sidebar - Plugin API", function (needs) {
|
|
needs.user();
|
|
|
|
needs.settings({
|
|
navigation_menu: "sidebar",
|
|
});
|
|
|
|
needs.hooks.afterEach(() => {
|
|
linkDidInsert = undefined;
|
|
linkDestroy = undefined;
|
|
sectionDestroy = undefined;
|
|
});
|
|
|
|
let linkDidInsert, linkDestroy, sectionDestroy;
|
|
|
|
test("Multiple header actions and links", async function (assert) {
|
|
withPluginApi("1.3.0", (api) => {
|
|
api.addSidebarSection(
|
|
(BaseCustomSidebarSection, BaseCustomSidebarSectionLink) => {
|
|
return class extends BaseCustomSidebarSection {
|
|
get name() {
|
|
return "test-chat-channels";
|
|
}
|
|
|
|
get text() {
|
|
return "chat channels text";
|
|
}
|
|
|
|
get actionsIcon() {
|
|
return "cog";
|
|
}
|
|
|
|
get actions() {
|
|
return [
|
|
{
|
|
id: "browseChannels",
|
|
title: "Browse channels",
|
|
action: () => {},
|
|
},
|
|
{
|
|
id: "settings",
|
|
title: "Settings",
|
|
action: () => {},
|
|
},
|
|
];
|
|
}
|
|
|
|
@bind
|
|
willDestroy() {
|
|
sectionDestroy = "section test";
|
|
}
|
|
|
|
get links() {
|
|
return [
|
|
new (class extends BaseCustomSidebarSectionLink {
|
|
get name() {
|
|
return "random-channel";
|
|
}
|
|
|
|
get classNames() {
|
|
return "my-class-name";
|
|
}
|
|
|
|
get route() {
|
|
return "topic";
|
|
}
|
|
|
|
get models() {
|
|
return ["some-slug", 1];
|
|
}
|
|
|
|
get title() {
|
|
return "random channel title";
|
|
}
|
|
|
|
get text() {
|
|
return "random channel text";
|
|
}
|
|
|
|
get prefixType() {
|
|
return "icon";
|
|
}
|
|
|
|
get prefixValue() {
|
|
return "hashtag";
|
|
}
|
|
|
|
get prefixColor() {
|
|
return "FF0000";
|
|
}
|
|
|
|
get prefixBadge() {
|
|
return "lock";
|
|
}
|
|
|
|
get suffixType() {
|
|
return "icon";
|
|
}
|
|
|
|
get suffixValue() {
|
|
return "circle";
|
|
}
|
|
|
|
get suffixCSSClass() {
|
|
return "unread";
|
|
}
|
|
|
|
@bind
|
|
didInsert() {
|
|
linkDidInsert = "link test";
|
|
}
|
|
|
|
@bind
|
|
willDestroy() {
|
|
linkDestroy = "link test";
|
|
}
|
|
})(),
|
|
new (class extends BaseCustomSidebarSectionLink {
|
|
get name() {
|
|
return "dev-channel";
|
|
}
|
|
|
|
get route() {
|
|
return "discovery.latest";
|
|
}
|
|
|
|
get title() {
|
|
return "dev channel title";
|
|
}
|
|
|
|
get text() {
|
|
return "dev channel text";
|
|
}
|
|
|
|
get prefixColor() {
|
|
return "alert";
|
|
}
|
|
|
|
get prefixType() {
|
|
return "text";
|
|
}
|
|
|
|
get prefixValue() {
|
|
return "test text";
|
|
}
|
|
})(),
|
|
new (class extends BaseCustomSidebarSectionLink {
|
|
get name() {
|
|
return "fun-channel";
|
|
}
|
|
|
|
get route() {
|
|
return "discovery.latest";
|
|
}
|
|
|
|
get title() {
|
|
return "fun channel title";
|
|
}
|
|
|
|
get text() {
|
|
return "fun channel text";
|
|
}
|
|
|
|
get prefixType() {
|
|
return "image";
|
|
}
|
|
|
|
get prefixValue() {
|
|
return "/test.png";
|
|
}
|
|
|
|
get hoverType() {
|
|
return "icon";
|
|
}
|
|
|
|
get hoverValue() {
|
|
return "times";
|
|
}
|
|
|
|
get hoverAction() {
|
|
return () => {};
|
|
}
|
|
|
|
get hoverTitle() {
|
|
return "hover button title attribute";
|
|
}
|
|
})(),
|
|
];
|
|
}
|
|
};
|
|
}
|
|
);
|
|
});
|
|
|
|
await visit("/");
|
|
|
|
assert.strictEqual(
|
|
linkDidInsert,
|
|
"link test",
|
|
"calls link didInsert function"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
query(
|
|
".sidebar-section-test-chat-channels .sidebar-section-header-text"
|
|
).textContent.trim(),
|
|
"chat channels text",
|
|
"displays header with correct text"
|
|
);
|
|
|
|
await click(
|
|
".sidebar-section-test-chat-channels .sidebar-section-header-dropdown summary"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
queryAll(
|
|
".sidebar-section-test-chat-channels .sidebar-section-header-dropdown .select-kit-collection li"
|
|
).length,
|
|
2,
|
|
"displays two actions"
|
|
);
|
|
|
|
const actions = queryAll(
|
|
".sidebar-section-test-chat-channels .sidebar-section-header-dropdown .select-kit-collection li"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
actions[0].textContent.trim(),
|
|
"Browse channels",
|
|
"displays first header action with correct text"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
actions[1].textContent.trim(),
|
|
"Settings",
|
|
"displays second header action with correct text"
|
|
);
|
|
|
|
const links = queryAll(
|
|
".sidebar-section-test-chat-channels .sidebar-section-link"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
links[0].textContent.trim(),
|
|
"random channel text",
|
|
"displays first link with correct text"
|
|
);
|
|
|
|
assert.ok(
|
|
exists(".sidebar-section-link.my-class-name"),
|
|
"sets the custom class name for the section link"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
links[0].title,
|
|
"random channel title",
|
|
"displays first link with correct title attribute"
|
|
);
|
|
|
|
assert.ok(
|
|
links[0].href.endsWith("/some-slug/1"),
|
|
"link has the correct href attribute"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
links[0].children[0].style.color,
|
|
"rgb(255, 0, 0)",
|
|
"has correct prefix color"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
links[0].children[0].children[0].classList.contains("d-icon-hashtag"),
|
|
true,
|
|
"displays prefix icon"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
links[0].children[0].children[1].classList.contains("d-icon-lock"),
|
|
true,
|
|
"displays prefix icon badge"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
links[0].children[2].children[0].classList.contains("d-icon-circle"),
|
|
true,
|
|
"displays suffix icon"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
links[1].children[1].textContent.trim(),
|
|
"dev channel text",
|
|
"displays second link with correct text"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
links[1].title,
|
|
"dev channel title",
|
|
"displays second link with correct title attribute"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
links[1].children[0].style.color,
|
|
"",
|
|
"has no color style when value is invalid"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
links[1].children[0].textContent.trim(),
|
|
"test text",
|
|
"displays prefix text"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
links[2].children[1].textContent.trim(),
|
|
"fun channel text",
|
|
"displays third link with correct text"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
links[2].title,
|
|
"fun channel title",
|
|
"displays third link with correct title attribute"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
links[2].children[0].children[0].getAttribute("src"),
|
|
"/test.png",
|
|
"uses correct prefix image url"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
query(".sidebar-section-link-hover button").title,
|
|
"hover button title attribute",
|
|
"displays hover button with correct title"
|
|
);
|
|
|
|
await click(".btn-sidebar-toggle");
|
|
|
|
assert.strictEqual(
|
|
linkDestroy,
|
|
"link test",
|
|
"calls link willDestroy function"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
sectionDestroy,
|
|
"section test",
|
|
"calls section willDestroy function"
|
|
);
|
|
});
|
|
|
|
test("Single header action and no links", async function (assert) {
|
|
withPluginApi("1.3.0", (api) => {
|
|
api.addSidebarSection((BaseCustomSidebarSection) => {
|
|
return class extends BaseCustomSidebarSection {
|
|
get name() {
|
|
return "test-chat-channels";
|
|
}
|
|
|
|
get text() {
|
|
return "chat channels text";
|
|
}
|
|
|
|
get actionsIcon() {
|
|
return "cog";
|
|
}
|
|
|
|
get actions() {
|
|
return [
|
|
{
|
|
id: "browseChannels",
|
|
title: "Browse channels",
|
|
action: () => {},
|
|
},
|
|
];
|
|
}
|
|
|
|
get links() {
|
|
return [];
|
|
}
|
|
};
|
|
});
|
|
});
|
|
|
|
await visit("/");
|
|
|
|
assert.strictEqual(
|
|
query(
|
|
".sidebar-section-test-chat-channels .sidebar-section-header-text"
|
|
).textContent.trim(),
|
|
"chat channels text",
|
|
"displays header with correct text"
|
|
);
|
|
|
|
assert.ok(
|
|
exists("button.sidebar-section-header-button"),
|
|
"displays single header action button"
|
|
);
|
|
|
|
assert.ok(
|
|
!exists(".sidebar-section-test-chat-channels .sidebar-section-content a"),
|
|
"displays no links"
|
|
);
|
|
});
|
|
|
|
test("API bridge for decorating hamburger-menu widget with footer links", async function (assert) {
|
|
withPluginApi("1.3.0", (api) => {
|
|
api.decorateWidget("hamburger-menu:footerLinks", () => {
|
|
return {
|
|
route: "discovery.top",
|
|
rawLabel: "my top",
|
|
className: "my-custom-top",
|
|
};
|
|
});
|
|
});
|
|
|
|
await visit("/");
|
|
|
|
await click(
|
|
".sidebar-section-community .sidebar-more-section-links-details-summary"
|
|
);
|
|
|
|
const myCustomTopSectionLink = query(
|
|
".sidebar-section-community .sidebar-more-section-links-details-content-secondary .sidebar-section-link-my-custom-top"
|
|
);
|
|
|
|
assert.ok(
|
|
myCustomTopSectionLink,
|
|
"adds my custom top section link to community section under the secondary section in the More... links drawer"
|
|
);
|
|
|
|
assert.ok(
|
|
myCustomTopSectionLink.href.endsWith("/top"),
|
|
"sets the right href attribute for the my custom top section link"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
myCustomTopSectionLink.textContent.trim(),
|
|
"my top",
|
|
"displays the right text for my custom top section link"
|
|
);
|
|
});
|
|
|
|
test("API bridge for decorating hamburger-menu widget with general links", async function (assert) {
|
|
withPluginApi("1.3.0", (api) => {
|
|
api.decorateWidget("hamburger-menu:generalLinks", () => {
|
|
return {
|
|
route: "discovery.latest",
|
|
label: "filters.latest.title",
|
|
};
|
|
});
|
|
|
|
api.decorateWidget("hamburger-menu:generalLinks", () => {
|
|
return {
|
|
route: "discovery.unread",
|
|
rawLabel: "my unreads",
|
|
};
|
|
});
|
|
|
|
api.decorateWidget("hamburger-menu:generalLinks", () => {
|
|
return {
|
|
route: "discovery.top",
|
|
rawLabel: "my top",
|
|
className: "my-custom-top",
|
|
};
|
|
});
|
|
|
|
api.decorateWidget("hamburger-menu:generalLinks", () => {
|
|
return {
|
|
href: "/c/bug?status=open",
|
|
rawLabel: "open bugs",
|
|
};
|
|
});
|
|
|
|
api.decorateWidget("hamburger-menu:generalLinks", () => {
|
|
return {
|
|
href: "/t/internationalization-localization/280",
|
|
rawLabel: "my favourite topic",
|
|
};
|
|
});
|
|
});
|
|
|
|
await visit("/");
|
|
|
|
const customLatestSectionLink = query(
|
|
".sidebar-section-community .sidebar-section-link-latest"
|
|
);
|
|
|
|
assert.ok(
|
|
customLatestSectionLink,
|
|
"adds custom latest section link to community section"
|
|
);
|
|
|
|
assert.ok(
|
|
customLatestSectionLink.href.endsWith("/latest"),
|
|
"sets the right href attribute for the custom latest section link"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
customLatestSectionLink.textContent.trim(),
|
|
I18n.t("filters.latest.title"),
|
|
"displays the right text for custom latest section link"
|
|
);
|
|
|
|
await click(
|
|
".sidebar-section-community .sidebar-more-section-links-details-summary"
|
|
);
|
|
|
|
const customUnreadSectionLink = query(
|
|
".sidebar-section-community .sidebar-section-link-my-unreads"
|
|
);
|
|
|
|
assert.ok(
|
|
customUnreadSectionLink,
|
|
"adds custom unread section link to community section"
|
|
);
|
|
|
|
assert.ok(
|
|
customUnreadSectionLink.href.endsWith("/unread"),
|
|
"sets the right href attribute for the custom unread section link"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
customUnreadSectionLink.textContent.trim(),
|
|
"my unreads",
|
|
"displays the right text for custom unread section link"
|
|
);
|
|
|
|
const customTopSectionLInk = query(
|
|
".sidebar-section-community .sidebar-section-link-my-custom-top"
|
|
);
|
|
|
|
assert.ok(
|
|
customTopSectionLInk,
|
|
"adds custom top section link to community section with right link class"
|
|
);
|
|
|
|
const openBugsSectionLink = query(
|
|
".sidebar-section-community .sidebar-section-link-open-bugs"
|
|
);
|
|
|
|
assert.ok(
|
|
openBugsSectionLink,
|
|
"adds custom open bugs section link to community section with right link class"
|
|
);
|
|
|
|
assert.ok(
|
|
openBugsSectionLink.href.endsWith("/c/bug?status=open"),
|
|
"sets the right href attribute for the custom open bugs section link"
|
|
);
|
|
|
|
// close more links
|
|
await click(
|
|
".sidebar-section-community .sidebar-more-section-links-details-summary"
|
|
);
|
|
|
|
await visit("/t/internationalization-localization/280");
|
|
|
|
assert.ok(
|
|
exists(
|
|
".sidebar-section-community .sidebar-section-link-my-favourite-topic.active"
|
|
),
|
|
"displays my favourite topic custom section link when current route matches the link's route"
|
|
);
|
|
|
|
await visit("/t/short-topic-with-two-posts/54077");
|
|
|
|
assert.notOk(
|
|
exists(
|
|
".sidebar-section-community .sidebar-section-link-my-favourite-topic.active"
|
|
),
|
|
"does not display my favourite topic custom section link when current route does not match the link's route"
|
|
);
|
|
});
|
|
|
|
test("Section that is not displayed via displaySection", async function (assert) {
|
|
withPluginApi("1.3.0", (api) => {
|
|
api.addSidebarSection((BaseCustomSidebarSection) => {
|
|
return class extends BaseCustomSidebarSection {
|
|
get name() {
|
|
return "test-chat-channels";
|
|
}
|
|
|
|
get text() {
|
|
return "chat channels text";
|
|
}
|
|
|
|
get actionsIcon() {
|
|
return "cog";
|
|
}
|
|
|
|
get actions() {
|
|
return [
|
|
{
|
|
id: "browseChannels",
|
|
title: "Browse channels",
|
|
action: () => {},
|
|
},
|
|
];
|
|
}
|
|
|
|
get links() {
|
|
return [];
|
|
}
|
|
|
|
get displaySection() {
|
|
return false;
|
|
}
|
|
};
|
|
});
|
|
});
|
|
|
|
await visit("/");
|
|
|
|
assert.notOk(
|
|
exists(".sidebar-section-test-chat-channels"),
|
|
"does not display the section"
|
|
);
|
|
});
|
|
|
|
test("Registering a custom countable for a section link in the user's sidebar categories section", async function (assert) {
|
|
try {
|
|
return await withPluginApi("1.6.0", async (api) => {
|
|
const categories = Site.current().categories;
|
|
const category1 = categories[0];
|
|
const category2 = categories[1];
|
|
|
|
updateCurrentUser({
|
|
sidebar_category_ids: [category1.id, category2.id],
|
|
});
|
|
|
|
// User has one unread topic
|
|
this.container.lookup("service:topic-tracking-state").loadStates([
|
|
{
|
|
topic_id: 2,
|
|
highest_post_number: 12,
|
|
last_read_post_number: 11,
|
|
created_at: "2020-02-09T09:40:02.672Z",
|
|
category_id: category1.id,
|
|
notification_level: 2,
|
|
created_in_new_period: false,
|
|
treat_as_new_topic_start_date: "2022-05-09T03:17:34.286Z",
|
|
},
|
|
]);
|
|
|
|
api.registerUserCategorySectionLinkCountable({
|
|
badgeTextFunction: (count) => {
|
|
return `some custom ${count}`;
|
|
},
|
|
route: "discovery.latestCategory",
|
|
routeQuery: { status: "open" },
|
|
shouldRegister: ({ category }) => {
|
|
if (category.name === category1.name) {
|
|
return true;
|
|
} else if (category.name === category2.name) {
|
|
return false;
|
|
}
|
|
},
|
|
refreshCountFunction: ({ category }) => {
|
|
return category.topic_count;
|
|
},
|
|
prioritizeOverDefaults: ({ category }) => {
|
|
return category.topic_count > 1000;
|
|
},
|
|
});
|
|
|
|
await visit("/");
|
|
|
|
assert.ok(
|
|
exists(
|
|
`.sidebar-section-link-${category1.name} .sidebar-section-link-suffix.unread`
|
|
),
|
|
"the right suffix is displayed when custom countable is active"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
query(`.sidebar-section-link-${category1.name}`).pathname,
|
|
`/c/${category1.name}/${category1.id}`,
|
|
"does not use route configured for custom countable when user has elected not to show any counts in sidebar"
|
|
);
|
|
|
|
assert.notOk(
|
|
exists(
|
|
`.sidebar-section-link-${category2.name} .sidebar-section-link-suffix.unread`
|
|
),
|
|
"does not display suffix when custom countable is not registered"
|
|
);
|
|
|
|
updateCurrentUser({
|
|
sidebar_list_destination: UNREAD_LIST_DESTINATION,
|
|
});
|
|
|
|
assert.strictEqual(
|
|
query(
|
|
`.sidebar-section-link-${category1.name} .sidebar-section-link-content-badge`
|
|
).innerText.trim(),
|
|
I18n.t("sidebar.unread_count", { count: 1 }),
|
|
"displays the right badge text in section link when unread is present and custom countable is not prioritised over unread"
|
|
);
|
|
|
|
category1.set("topic_count", 2000);
|
|
|
|
api.refreshUserSidebarCategoriesSectionCounts();
|
|
|
|
await settled();
|
|
|
|
assert.strictEqual(
|
|
query(
|
|
`.sidebar-section-link-${category1.name} .sidebar-section-link-content-badge`
|
|
).innerText.trim(),
|
|
`some custom ${category1.topic_count}`,
|
|
"displays the right badge text in section link when unread is present but custom countable is prioritised over unread"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
query(`.sidebar-section-link-${category1.name}`).pathname,
|
|
`/c/${category1.name}/${category1.id}/l/latest`,
|
|
"has the right pathname for section link"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
query(`.sidebar-section-link-${category1.name}`).search,
|
|
"?status=open",
|
|
"has the right query params for section link"
|
|
);
|
|
});
|
|
} finally {
|
|
resetCustomCountables();
|
|
}
|
|
});
|
|
});
|