diff --git a/app/assets/javascripts/discourse/app/components/sidebar/community-section.js b/app/assets/javascripts/discourse/app/components/sidebar/community-section.js index c6cc1f59d67..dd4668febcc 100644 --- a/app/assets/javascripts/discourse/app/components/sidebar/community-section.js +++ b/app/assets/javascripts/discourse/app/components/sidebar/community-section.js @@ -12,18 +12,26 @@ import UsersSectionLink from "discourse/lib/sidebar/community-section/users-sect import { action } from "@ember/object"; import { next } from "@ember/runloop"; -const DEFAULT_SECTION_LINKS = [ +const MAIN_SECTION_LINKS = [ EverythingSectionLink, TrackedSectionLink, - GroupsSectionLink, - UsersSectionLink, MyPostsSectionLink, ]; -export default class SidebarCommunitySection extends GlimmerComponent { - configuredSectionLinks = [...DEFAULT_SECTION_LINKS, ...customSectionLinks]; +const MORE_SECTION_LINKS = [GroupsSectionLink, UsersSectionLink]; - sectionLinks = this.configuredSectionLinks.map((sectionLinkClass) => { +export default class SidebarCommunitySection extends GlimmerComponent { + moreSectionLinks = [...MORE_SECTION_LINKS, ...customSectionLinks].map( + (sectionLinkClass) => { + return new sectionLinkClass({ + topicTrackingState: this.topicTrackingState, + currentUser: this.currentUser, + appEvents: this.appEvents, + }); + } + ); + + sectionLinks = MAIN_SECTION_LINKS.map((sectionLinkClass) => { return new sectionLinkClass({ topicTrackingState: this.topicTrackingState, currentUser: this.currentUser, diff --git a/app/assets/javascripts/discourse/app/components/sidebar/more-section-links.js b/app/assets/javascripts/discourse/app/components/sidebar/more-section-links.js new file mode 100644 index 00000000000..a38ccdd4be0 --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/sidebar/more-section-links.js @@ -0,0 +1,92 @@ +import { tracked } from "@glimmer/tracking"; +import { action } from "@ember/object"; +import { inject as service } from "@ember/service"; +import { bind } from "discourse-common/utils/decorators"; +import GlimmerComponent from "discourse/components/glimmer"; + +export default class SidebarMoreSectionLinks extends GlimmerComponent { + @tracked shouldDisplaySectionLinks = false; + @tracked activeSectionLink; + @service router; + + constructor() { + super(...arguments); + this.#setActiveSectionLink(); + this.router.on("routeDidChange", this, this.#setActiveSectionLink); + } + + willDestroy() { + this.#removeClickEventListener(); + this.router.off("routeDidChange", this, this.#setActiveSectionLink); + } + + get sectionLinks() { + if (this.activeSectionLink) { + return this.args.sectionLinks.filter((sectionLink) => { + return sectionLink.name !== this.activeSectionLink.name; + }); + } else { + return this.args.sectionLinks; + } + } + + @bind + closeDetails(event) { + if (this.shouldDisplaySectionLinks) { + const isLinkClick = event.target.className.includes( + "sidebar-section-link" + ); + + if (isLinkClick || this.#isOutsideDetailsClick(event)) { + document + .querySelector(".sidebar-more-section-links-details") + ?.removeAttribute("open"); + + this.toggleSectionLinks(); + } + } + } + + @action + registerClickListener() { + this.#addClickEventListener(); + } + + @action + unregisterClickListener() { + this.#removeClickEventListener(); + } + + @action + toggleSectionLinks() { + this.shouldDisplaySectionLinks = !this.shouldDisplaySectionLinks; + } + + #removeClickEventListener() { + document.removeEventListener("click", this.closeDetails); + } + + #addClickEventListener() { + document.addEventListener("click", this.closeDetails); + } + + #isOutsideDetailsClick(event) { + return !event.composedPath().some((element) => { + return element.className === "sidebar-more-section-links-details"; + }); + } + + #setActiveSectionLink() { + const activeSectionLink = this.args.sectionLinks.find((sectionLink) => { + const args = [sectionLink.route]; + + if (sectionLink.model) { + args.push(sectionLink.model); + } + + return this.router.isActive(...args) && sectionLink; + }); + + this.activeSectionLink = activeSectionLink; + } +} diff --git a/app/assets/javascripts/discourse/app/templates/components/sidebar/community-section.hbs b/app/assets/javascripts/discourse/app/templates/components/sidebar/community-section.hbs index 5a17d28bd21..93dcb3a8bc2 100644 --- a/app/assets/javascripts/discourse/app/templates/components/sidebar/community-section.hbs +++ b/app/assets/javascripts/discourse/app/templates/components/sidebar/community-section.hbs @@ -19,4 +19,6 @@ @badgeText={{sectionLink.badgeText}} @model={{sectionLink.model}} /> {{/each}} + + diff --git a/app/assets/javascripts/discourse/app/templates/components/sidebar/more-section-links.hbs b/app/assets/javascripts/discourse/app/templates/components/sidebar/more-section-links.hbs new file mode 100644 index 00000000000..d3f978f997d --- /dev/null +++ b/app/assets/javascripts/discourse/app/templates/components/sidebar/more-section-links.hbs @@ -0,0 +1,36 @@ +{{#if this.activeSectionLink}} + +{{/if}} + + diff --git a/app/assets/javascripts/discourse/tests/acceptance/sidebar-community-section-test.js b/app/assets/javascripts/discourse/tests/acceptance/sidebar-community-section-test.js index 1b95741c5cb..390f73c97bf 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/sidebar-community-section-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/sidebar-community-section-test.js @@ -114,6 +114,45 @@ acceptance("Sidebar - Community Section", function (needs) { ); }); + test("clicking on more... link", async function (assert) { + await visit("/"); + + await click( + ".sidebar-section-community .sidebar-more-section-links-details-summary" + ); + + assert.ok( + exists( + ".sidebar-section-community .sidebar-more-section-links-details-content" + ), + "additional section links are displayed" + ); + + await click( + ".sidebar-section-community .sidebar-more-section-links-details-summary" + ); + + assert.notOk( + exists( + ".sidebar-section-community .sidebar-more-section-links-details-content" + ), + "additional section links are hidden" + ); + + await click( + ".sidebar-section-community .sidebar-more-section-links-details-summary" + ); + + await click("#main-outlet"); + + assert.notOk( + exists( + ".sidebar-section-community .sidebar-more-section-links-details-content" + ), + "additional section links are hidden when clicking outside" + ); + }); + test("clicking on everything link", async function (assert) { await visit("/t/280"); await click(".sidebar-section-community .sidebar-section-link-everything"); @@ -162,6 +201,16 @@ acceptance("Sidebar - Community Section", function (needs) { test("clicking on users link", async function (assert) { await visit("/t/280"); + + assert.notOk( + exists(".sidebar-section-community .sidebar-section-link-users"), + "users link is not displayed in sidebar when it is not the active route" + ); + + await click( + ".sidebar-section-community .sidebar-more-section-links-details-summary" + ); + await click(".sidebar-section-community .sidebar-section-link-users"); assert.strictEqual( @@ -180,10 +229,35 @@ acceptance("Sidebar - Community Section", function (needs) { exists(".sidebar-section-community .sidebar-section-link-users.active"), "the users link is marked as active" ); + + assert.strictEqual( + query( + ".sidebar-section-community .sidebar-more-section-links-details-summary" + ).textContent.trim(), + I18n.t("sidebar.more_count", { count: 1 }), + "displays the right count as users link is currently active" + ); + + await visit("/u"); + + assert.ok( + exists(".sidebar-section-community .sidebar-section-link-users.active"), + "users link is displayed in sidebar when it is the active route" + ); }); test("clicking on groups link", async function (assert) { await visit("/t/280"); + + assert.notOk( + exists(".sidebar-section-community .sidebar-section-link-groups"), + "groups link is not displayed in sidebar when it is not the active route" + ); + + await click( + ".sidebar-section-community .sidebar-more-section-links-details-summary" + ); + await click(".sidebar-section-community .sidebar-section-link-groups"); assert.strictEqual( @@ -202,6 +276,21 @@ acceptance("Sidebar - Community Section", function (needs) { exists(".sidebar-section-community .sidebar-section-link-groups.active"), "the groups link is marked as active" ); + + assert.strictEqual( + query( + ".sidebar-section-community .sidebar-more-section-links-details-summary" + ).textContent.trim(), + I18n.t("sidebar.more_count", { count: 1 }), + "displays the right count as groups link is currently active" + ); + + await visit("/g"); + + assert.ok( + exists(".sidebar-section-community .sidebar-section-link-groups.active"), + "groups link is displayed in sidebar when it is the active route" + ); }); test("clicking on my posts link", async function (assert) { @@ -679,6 +768,10 @@ acceptance("Sidebar - Community Section", function (needs) { await visit("/"); + await click( + ".sidebar-section-community .sidebar-more-section-links-details-summary" + ); + assert.strictEqual( query(".sidebar-section-link-unread").textContent.trim(), "unread topics", @@ -724,6 +817,11 @@ acceptance("Sidebar - Community Section", function (needs) { }); await visit("/"); + + await click( + ".sidebar-section-community .sidebar-more-section-links-details-summary" + ); + await click(".sidebar-section-link-user-summary"); assert.strictEqual( diff --git a/app/assets/stylesheets/common/base/_index.scss b/app/assets/stylesheets/common/base/_index.scss index ca3f8b20991..6f6103e87a2 100644 --- a/app/assets/stylesheets/common/base/_index.scss +++ b/app/assets/stylesheets/common/base/_index.scss @@ -47,6 +47,7 @@ @import "sidebar"; @import "sidebar-footer"; @import "sidebar-section"; +@import "sidebar-more-section-links"; @import "sidebar-section-link"; @import "tagging"; @import "tooltip"; diff --git a/app/assets/stylesheets/common/base/sidebar-more-section-links.scss b/app/assets/stylesheets/common/base/sidebar-more-section-links.scss new file mode 100644 index 00000000000..92c36017e3c --- /dev/null +++ b/app/assets/stylesheets/common/base/sidebar-more-section-links.scss @@ -0,0 +1,32 @@ +.sidebar-more-section-links-details { + margin-left: 1.5em; + + .sidebar-more-section-links-details-summary { + padding: 0.35em 0.5em; + color: var(--tertiary); + font-size: var(--font-down-1); + transition: background-color 0.25s; + + &:hover { + background: var(--d-sidebar-highlight-color); + } + + list-style: none; + + &::before { + display: none; + } + } + + .sidebar-more-section-links-details-content { + position: absolute; + background-color: var(--secondary); + width: 100%; + box-shadow: shadow("dropdown"); + z-index: z("base") + 1; + + .sidebar-section-link-wrapper { + margin-left: none; + } + } +} diff --git a/app/assets/stylesheets/common/base/sidebar-section-link.scss b/app/assets/stylesheets/common/base/sidebar-section-link.scss index 7abae61ed5f..2f6251c33ee 100644 --- a/app/assets/stylesheets/common/base/sidebar-section-link.scss +++ b/app/assets/stylesheets/common/base/sidebar-section-link.scss @@ -134,6 +134,8 @@ } } -#main-outlet-wrapper .sidebar-section-link-wrapper { +#main-outlet-wrapper + .sidebar-section-link-wrapper:not(.sidebar-more-section-links-details-content + .sidebar-section-link-wrapper) { margin-left: 1.5em; } diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index e299b4bcf4a..17dcbfd8991 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -4077,6 +4077,7 @@ en: one: "%{count} new" other: "%{count} new" toggle_section: "toggle section" + more_count: "%{count} more..." sections: messages: header_link_title: "personal messages"