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:
Alan Guo Xiang Tan 2022-06-09 11:14:01 +08:00 committed by GitHub
parent 03f674070a
commit cd8c97debc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 327 additions and 4 deletions

View File

@ -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 });
});
}
}

View File

@ -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 }));
}
}

View File

@ -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";

View File

@ -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);
}
}

View File

@ -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>

View File

@ -0,0 +1,5 @@
<div class="sidebar-section-message-wrapper">
<div class="sidebar-section-message">
{{yield}}
</div>
</div>

View File

@ -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`
);
}
);
});

View File

@ -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 {

View File

@ -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: