FEATURE: Add section links to categories section to exp sidebar (#17035)
This commit adds a section link to the categories section for each category that is tracked by the user in the experimental sidebar.
This commit is contained in:
parent
03f674070a
commit
cd8c97debc
|
@ -1,3 +1,10 @@
|
|||
import GlimmerComponent from "discourse/components/glimmer";
|
||||
import CategorySectionLink from "discourse/lib/sidebar/categories-section/category-section-link";
|
||||
|
||||
export default class SidebarCategoriesSection extends GlimmerComponent {}
|
||||
export default class SidebarCategoriesSection extends GlimmerComponent {
|
||||
get sectionLinks() {
|
||||
return this.site.trackedCategoriesList.map((trackedCategory) => {
|
||||
return new CategorySectionLink({ category: trackedCategory });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
import { htmlSafe } from "@ember/template";
|
||||
|
||||
import { categoryBadgeHTML } from "discourse/helpers/category-link";
|
||||
import Category from "discourse/models/category";
|
||||
|
||||
export default class CategorySectionLink {
|
||||
constructor({ category }) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.category.slug;
|
||||
}
|
||||
|
||||
get route() {
|
||||
return "discovery.latestCategory";
|
||||
}
|
||||
|
||||
get model() {
|
||||
return `${Category.slugFor(this.category)}/${this.category.id}`;
|
||||
}
|
||||
|
||||
get currentWhen() {
|
||||
return "discovery.unreadCategory discovery.topCategory discovery.newCategory discovery.latestCategory";
|
||||
}
|
||||
|
||||
get title() {
|
||||
return this.category.description_excerpt;
|
||||
}
|
||||
|
||||
get text() {
|
||||
return htmlSafe(categoryBadgeHTML(this.category, { link: false }));
|
||||
}
|
||||
}
|
|
@ -187,6 +187,11 @@ const Category = RestModel.extend({
|
|||
return seconds ? seconds / 60 : null;
|
||||
},
|
||||
|
||||
@discourseComputed("notification_level")
|
||||
isTracked(notificationLevel) {
|
||||
return notificationLevel >= NotificationLevels.TRACKING;
|
||||
},
|
||||
|
||||
save() {
|
||||
const id = this.id;
|
||||
const url = id ? `/categories/${id}` : "/categories";
|
||||
|
|
|
@ -11,7 +11,6 @@ import discourseComputed from "discourse-common/utils/decorators";
|
|||
import { getOwner } from "discourse-common/lib/get-owner";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import { NotificationLevels } from "discourse/lib/notification-levels";
|
||||
|
||||
const Site = RestModel.extend({
|
||||
isReadOnly: alias("is_readonly"),
|
||||
|
@ -84,12 +83,12 @@ const Site = RestModel.extend({
|
|||
: this.sortedCategories;
|
||||
},
|
||||
|
||||
@discourseComputed("categories.[]")
|
||||
@discourseComputed("categories.[]", "categories.@each.notification_level")
|
||||
trackedCategoriesList(categories) {
|
||||
const trackedCategories = [];
|
||||
|
||||
for (const category of categories) {
|
||||
if (category.notification_level >= NotificationLevels.TRACKING) {
|
||||
if (category.isTracked) {
|
||||
trackedCategories.push(category);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,4 +4,21 @@
|
|||
@headerLinkText={{i18n "sidebar.sections.categories.header_link_text"}}
|
||||
@headerLinkTitle={{i18n "sidebar.sections.categories.header_link_title"}} >
|
||||
|
||||
{{#if (gt this.sectionLinks.length 0)}}
|
||||
{{#each this.sectionLinks as |sectionLink|}}
|
||||
<Sidebar::SectionLink
|
||||
@linkName={{sectionLink.name}}
|
||||
@route={{sectionLink.route}}
|
||||
@query={{sectionLink.query}}
|
||||
@title={{sectionLink.title}}
|
||||
@content={{sectionLink.text}}
|
||||
@currentWhen={{sectionLink.currentWhen}}
|
||||
@model={{sectionLink.model}}>
|
||||
</Sidebar::SectionLink>
|
||||
{{/each}}
|
||||
{{else}}
|
||||
<Sidebar::SectionMessage>
|
||||
{{i18n "sidebar.sections.categories.no_tracked_categories"}}
|
||||
</Sidebar::SectionMessage>
|
||||
{{/if}}
|
||||
</Sidebar::Section>
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<div class="sidebar-section-message-wrapper">
|
||||
<div class="sidebar-section-message">
|
||||
{{yield}}
|
||||
</div>
|
||||
</div>
|
|
@ -1,14 +1,53 @@
|
|||
import I18n from "I18n";
|
||||
|
||||
import { click, currentURL, visit } from "@ember/test-helpers";
|
||||
|
||||
import {
|
||||
acceptance,
|
||||
conditionalTest,
|
||||
exists,
|
||||
query,
|
||||
queryAll,
|
||||
} from "discourse/tests/helpers/qunit-helpers";
|
||||
import selectKit from "discourse/tests/helpers/select-kit-helper";
|
||||
import { isLegacyEmber } from "discourse-common/config/environment";
|
||||
import Site from "discourse/models/site";
|
||||
import { NotificationLevels } from "discourse/lib/notification-levels";
|
||||
import discoveryFixture from "discourse/tests/fixtures/discovery-fixtures";
|
||||
import categoryFixture from "discourse/tests/fixtures/category-fixtures";
|
||||
import { cloneJSON } from "discourse-common/lib/object";
|
||||
|
||||
acceptance("Sidebar - Categories Section", function (needs) {
|
||||
needs.user({ experimental_sidebar_enabled: true });
|
||||
|
||||
needs.pretender((server, helper) => {
|
||||
["latest", "top", "new", "unread"].forEach((type) => {
|
||||
server.get(`/c/:categorySlug/:categoryId/l/${type}.json`, () => {
|
||||
return helper.response(
|
||||
cloneJSON(discoveryFixture["/c/bug/1/l/latest.json"])
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
server.get("/c/:categorySlug/:categoryId/find_by_slug.json", () => {
|
||||
return helper.response(cloneJSON(categoryFixture["/c/1/show.json"]));
|
||||
});
|
||||
|
||||
server.post("/category/:categoryId/notifications", () => {
|
||||
return helper.response({});
|
||||
});
|
||||
});
|
||||
|
||||
const setupTrackedCategories = function () {
|
||||
const categories = Site.current().categories;
|
||||
const category1 = categories[0];
|
||||
const category2 = categories[1];
|
||||
category1.set("notification_level", NotificationLevels.TRACKING);
|
||||
category2.set("notification_level", NotificationLevels.TRACKING);
|
||||
|
||||
return { category1, category2 };
|
||||
};
|
||||
|
||||
conditionalTest(
|
||||
"clicking on section header link",
|
||||
!isLegacyEmber(),
|
||||
|
@ -23,4 +62,205 @@ acceptance("Sidebar - Categories Section", function (needs) {
|
|||
);
|
||||
}
|
||||
);
|
||||
|
||||
conditionalTest(
|
||||
"category section links when user does not have any tracked categories",
|
||||
!isLegacyEmber(),
|
||||
async function (assert) {
|
||||
await visit("/");
|
||||
|
||||
assert.strictEqual(
|
||||
query(".sidebar-section-message").textContent.trim(),
|
||||
I18n.t("sidebar.sections.categories.no_tracked_categories"),
|
||||
"the no tracked categories message is displayed"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
conditionalTest(
|
||||
"category section links for tracked categories",
|
||||
!isLegacyEmber(),
|
||||
async function (assert) {
|
||||
const { category1, category2 } = setupTrackedCategories();
|
||||
|
||||
await visit("/");
|
||||
|
||||
assert.strictEqual(
|
||||
queryAll(".sidebar-section-categories .sidebar-section-link").length,
|
||||
2,
|
||||
"there should only be two section link under the section"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
exists(`.sidebar-section-link-${category1.slug} .badge-category`),
|
||||
"category1 section link is rendered with category badge"
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
query(`.sidebar-section-link-${category1.slug}`).textContent.trim(),
|
||||
category1.name,
|
||||
"displays category1's name for the link text"
|
||||
);
|
||||
|
||||
await click(`.sidebar-section-link-${category1.slug}`);
|
||||
|
||||
assert.strictEqual(
|
||||
currentURL(),
|
||||
`/c/${category1.slug}/${category1.id}/l/latest`,
|
||||
"it should transition to the category1's discovery page"
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
queryAll(".sidebar-section-categories .sidebar-section-link.active")
|
||||
.length,
|
||||
1,
|
||||
"only one link is marked as active"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
exists(`.sidebar-section-link-${category1.slug}.active`),
|
||||
"the category1 section link is marked as active"
|
||||
);
|
||||
|
||||
await click(`.sidebar-section-link-${category2.slug}`);
|
||||
|
||||
assert.strictEqual(
|
||||
currentURL(),
|
||||
`/c/${category2.slug}/${category2.id}/l/latest`,
|
||||
"it should transition to the category2's discovery page"
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
queryAll(".sidebar-section-categories .sidebar-section-link.active")
|
||||
.length,
|
||||
1,
|
||||
"only one link is marked as active"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
exists(`.sidebar-section-link-${category2.slug}.active`),
|
||||
"the category2 section link is marked as active"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
conditionalTest(
|
||||
"visiting category discovery new route for tracked categories",
|
||||
!isLegacyEmber(),
|
||||
async function (assert) {
|
||||
const { category1 } = setupTrackedCategories();
|
||||
|
||||
await visit(`/c/${category1.slug}/${category1.id}/l/new`);
|
||||
|
||||
assert.strictEqual(
|
||||
queryAll(".sidebar-section-categories .sidebar-section-link.active")
|
||||
.length,
|
||||
1,
|
||||
"only one link is marked as active"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
exists(`.sidebar-section-link-${category1.slug}.active`),
|
||||
"the category1 section link is marked as active for the new route"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
conditionalTest(
|
||||
"visiting category discovery unread route for tracked categories",
|
||||
!isLegacyEmber(),
|
||||
async function (assert) {
|
||||
const { category1 } = setupTrackedCategories();
|
||||
|
||||
await visit(`/c/${category1.slug}/${category1.id}/l/unread`);
|
||||
|
||||
assert.strictEqual(
|
||||
queryAll(".sidebar-section-categories .sidebar-section-link.active")
|
||||
.length,
|
||||
1,
|
||||
"only one link is marked as active"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
exists(`.sidebar-section-link-${category1.slug}.active`),
|
||||
"the category1 section link is marked as active for the unread route"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
conditionalTest(
|
||||
"visiting category discovery top route for tracked categories",
|
||||
!isLegacyEmber(),
|
||||
async function (assert) {
|
||||
const { category1 } = setupTrackedCategories();
|
||||
|
||||
await visit(`/c/${category1.slug}/${category1.id}/l/top`);
|
||||
|
||||
assert.strictEqual(
|
||||
queryAll(".sidebar-section-categories .sidebar-section-link.active")
|
||||
.length,
|
||||
1,
|
||||
"only one link is marked as active"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
exists(`.sidebar-section-link-${category1.slug}.active`),
|
||||
"the category1 section link is marked as active for the top route"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
conditionalTest(
|
||||
"updating category notification level",
|
||||
!isLegacyEmber(),
|
||||
async function (assert) {
|
||||
const { category1, category2 } = setupTrackedCategories();
|
||||
|
||||
await visit(`/c/${category1.slug}/${category1.id}/l/top`);
|
||||
|
||||
assert.ok(
|
||||
exists(`.sidebar-section-link-${category1.slug}`),
|
||||
`has ${category1.name} section link in sidebar`
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
exists(`.sidebar-section-link-${category2.slug}`),
|
||||
`has ${category2.name} section link in sidebar`
|
||||
);
|
||||
|
||||
const notificationLevelsDropdown = selectKit(".notifications-button");
|
||||
|
||||
await notificationLevelsDropdown.expand();
|
||||
|
||||
await notificationLevelsDropdown.selectRowByValue(
|
||||
NotificationLevels.REGULAR
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
!exists(`.sidebar-section-link-${category1.slug}`),
|
||||
`does not have ${category1.name} section link in sidebar`
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
exists(`.sidebar-section-link-${category2.slug}`),
|
||||
`has ${category2.name} section link in sidebar`
|
||||
);
|
||||
|
||||
await notificationLevelsDropdown.expand();
|
||||
|
||||
await notificationLevelsDropdown.selectRowByValue(
|
||||
NotificationLevels.TRACKING
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
exists(`.sidebar-section-link-${category1.slug}`),
|
||||
`has ${category1.name} section link in sidebar`
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
exists(`.sidebar-section-link-${category2.slug}`),
|
||||
`has ${category2.name} section link in sidebar`
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -103,6 +103,21 @@
|
|||
color: var(--primary);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.badge-wrapper {
|
||||
font-size: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-section-message-wrapper {
|
||||
display: flex;
|
||||
margin-left: 1.5em;
|
||||
}
|
||||
|
||||
.sidebar-section-message {
|
||||
padding: 0.25em 0.5em;
|
||||
font-size: var(--font-down-1);
|
||||
color: var(--primary-high);
|
||||
}
|
||||
|
||||
.sidebar-section-link-content-badge {
|
||||
|
|
|
@ -4042,6 +4042,7 @@ en:
|
|||
new_count: "%{count} new"
|
||||
sections:
|
||||
categories:
|
||||
no_tracked_categories: "You are not tracking any categories."
|
||||
header_link_title: "all categories"
|
||||
header_link_text: "Categories"
|
||||
topics:
|
||||
|
|
Loading…
Reference in New Issue