From a9abaf408d5afe812f5eef33a01cb767ea9c266b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9rgio=20Saquetim?=
<1108771+megothss@users.noreply.github.com>
Date: Wed, 7 Aug 2024 14:47:34 -0300
Subject: [PATCH] DEV: Auto expand active sections and scroll active link into
view (#28237)
---
.../app/components/sidebar/api-panels.gjs | 6 +-
.../app/components/sidebar/api-section.gjs | 10 +-
.../app/components/sidebar/api-sections.gjs | 52 ++++-
.../app/components/sidebar/section-link.gjs | 25 +++
.../app/components/sidebar/section.gjs | 46 +++++
.../app/lib/sidebar/admin-sidebar.js | 4 +-
.../lib/sidebar/base-custom-sidebar-panel.js | 8 +
.../base-custom-sidebar-section-link.js | 5 +
.../discourse/app/services/sidebar-state.js | 5 +
.../acceptance/sidebar-plugin-api-test.js | 189 ++++++++++++++++++
10 files changed, 343 insertions(+), 7 deletions(-)
diff --git a/app/assets/javascripts/discourse/app/components/sidebar/api-panels.gjs b/app/assets/javascripts/discourse/app/components/sidebar/api-panels.gjs
index 61562707abb..8f1059002e7 100644
--- a/app/assets/javascripts/discourse/app/components/sidebar/api-panels.gjs
+++ b/app/assets/javascripts/discourse/app/components/sidebar/api-panels.gjs
@@ -11,7 +11,11 @@ export default class SidebarApiPanels extends Component {
}
diff --git a/app/assets/javascripts/discourse/app/components/sidebar/api-section.gjs b/app/assets/javascripts/discourse/app/components/sidebar/api-section.gjs
index 3cc7fa9a059..2b5d117cd5d 100644
--- a/app/assets/javascripts/discourse/app/components/sidebar/api-section.gjs
+++ b/app/assets/javascripts/discourse/app/components/sidebar/api-section.gjs
@@ -1,3 +1,4 @@
+import { and, eq } from "truth-helpers";
import Section from "./section";
import SectionLink from "./section-link";
@@ -14,6 +15,9 @@ const SidebarApiSection =
@displaySection={{@section.displaySection}}
@hideSectionHeader={{@section.hideSectionHeader}}
@collapsedByDefault={{@section.collapsedByDefault}}
+ @activeLink={{@section.activeLink}}
+ @expandWhenActive={{@expandWhenActive}}
+ @scrollActiveLinkIntoView={{@scrollActiveLinkIntoView}}
>
{{#each @section.filteredLinks key="name" as |link|}}
@model={{link.model}}
@query={{link.query}}
@models={{link.models}}
+ @currentWhen={{link.currentWhen}}
@href={{link.href}}
@title={{link.title}}
@contentCSSClass={{link.contentCSSClass}}
@@ -38,7 +43,6 @@ const SidebarApiSection =
@hoverValue={{link.hoverValue}}
@hoverAction={{link.hoverAction}}
@hoverTitle={{link.hoverTitle}}
- @currentWhen={{link.currentWhen}}
@didInsert={{link.didInsert}}
@willDestroy={{link.willDestroy}}
@content={{link.text}}
@@ -46,6 +50,10 @@ const SidebarApiSection =
link.contentComponent
status=link.contentComponentArgs
}}
+ @scrollIntoView={{and
+ @scrollActiveLinkIntoView
+ (eq link.name @section.activeLink.name)
+ }}
/>
{{/each}}
diff --git a/app/assets/javascripts/discourse/app/components/sidebar/api-sections.gjs b/app/assets/javascripts/discourse/app/components/sidebar/api-sections.gjs
index 58fe7861537..510ce67a490 100644
--- a/app/assets/javascripts/discourse/app/components/sidebar/api-sections.gjs
+++ b/app/assets/javascripts/discourse/app/components/sidebar/api-sections.gjs
@@ -6,6 +6,7 @@ import ApiSection from "./api-section";
import PanelHeader from "./panel-header";
export default class SidebarApiSections extends Component {
+ @service router;
@service sidebarState;
get sections() {
@@ -20,7 +21,7 @@ export default class SidebarApiSections extends Component {
}
return sectionConfigs.map((Section) => {
- const SidebarSection = prepareSidebarSectionClass(Section);
+ const SidebarSection = prepareSidebarSectionClass(Section, this.router);
const sectionInstance = new SidebarSection({
filterable:
@@ -43,14 +44,19 @@ export default class SidebarApiSections extends Component {
{{#each this.filteredSections as |section|}}
-
+
{{/each}}
}
// extends the class provided for the section to add functionality we don't want to be overridable when defining custom
// sections using the plugin API, like for example the filtering capabilities
-function prepareSidebarSectionClass(Section) {
+function prepareSidebarSectionClass(Section, routerService) {
return class extends Section {
constructor({ filterable, sidebarState }) {
super();
@@ -82,6 +88,46 @@ function prepareSidebarSectionClass(Section) {
});
}
+ get activeLink() {
+ return this.filteredLinks.find((link) => {
+ try {
+ const currentWhen = link.currentWhen;
+
+ if (typeof currentWhen === "boolean") {
+ return currentWhen;
+ }
+
+ // TODO detect active links using the href field
+
+ const queryParams = link.query || {};
+ let models;
+
+ if (link.model) {
+ models = [link.model];
+ } else if (link.models) {
+ models = link.models;
+ } else {
+ models = [];
+ }
+
+ if (typeof currentWhen === "string") {
+ return currentWhen.split(" ").some((route) =>
+ routerService.isActive(route, ...models, {
+ queryParams,
+ })
+ );
+ }
+
+ return routerService.isActive(link.route, ...models, {
+ queryParams,
+ });
+ } catch (e) {
+ // false if ember throws an exception while checking the routes
+ return false;
+ }
+ });
+ }
+
get filtered() {
return !this.filterable || this.filteredLinks?.length > 0;
}
diff --git a/app/assets/javascripts/discourse/app/components/sidebar/section-link.gjs b/app/assets/javascripts/discourse/app/components/sidebar/section-link.gjs
index 74901b3df30..6b748fe2350 100644
--- a/app/assets/javascripts/discourse/app/components/sidebar/section-link.gjs
+++ b/app/assets/javascripts/discourse/app/components/sidebar/section-link.gjs
@@ -1,12 +1,16 @@
import Component from "@glimmer/component";
import { hash } from "@ember/helper";
import { on } from "@ember/modifier";
+import didInsert from "@ember/render-modifiers/modifiers/did-insert";
+import didUpdate from "@ember/render-modifiers/modifiers/did-update";
import { LinkTo } from "@ember/routing";
+import { schedule } from "@ember/runloop";
import { service } from "@ember/service";
import { eq, or } from "truth-helpers";
import concatClass from "discourse/helpers/concat-class";
import icon from "discourse-common/helpers/d-icon";
import deprecated from "discourse-common/lib/deprecated";
+import { bind } from "discourse-common/utils/decorators";
import SectionLinkPrefix from "./section-link-prefix";
/**
@@ -61,6 +65,14 @@ export default class SectionLink extends Component {
classNames.push(this.args.class);
}
+ if (
+ this.args.href &&
+ typeof this.args.currentWhen === "boolean" &&
+ this.args.currentWhen
+ ) {
+ classNames.push("active");
+ }
+
return classNames.join(" ");
}
@@ -101,9 +113,22 @@ export default class SectionLink extends Component {
}
}
+ @bind
+ maybeScrollIntoView(element) {
+ if (this.args.scrollIntoView) {
+ schedule("afterRender", () => {
+ element.scrollIntoView({
+ block: "center",
+ });
+ });
+ }
+ }
+
{{#if this.shouldDisplay}}