DEV: Untangle the admin sidebar from the sidebar code (#27640)

This commit is contained in:
Sérgio Saquetim 2024-07-05 13:11:15 -03:00 committed by GitHub
parent 640dccd224
commit b36cbc7d21
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 270 additions and 181 deletions

View File

@ -1,99 +1,55 @@
import Component from "@glimmer/component";
import { getOwner, setOwner } from "@ember/owner";
import { service } from "@ember/service";
import Section from "./section"; import Section from "./section";
import SectionLink from "./section-link"; import SectionLink from "./section-link";
export default class SidebarApiSection extends Component { const SidebarApiSection = <template>
@service sidebarState; {{#if @section.filtered}}
<Section
@sectionName={{@section.name}}
@headerLinkText={{@section.text}}
@headerLinkTitle={{@section.title}}
@headerActionsIcon={{@section.actionsIcon}}
@headerActions={{@section.actions}}
@willDestroy={{@section.willDestroy}}
@collapsable={{@collapsable}}
@displaySection={{@section.displaySection}}
@hideSectionHeader={{@section.hideSectionHeader}}
@collapsedByDefault={{@section.collapsedByDefault}}
>
{{#each @section.filteredLinks key="name" as |link|}}
<SectionLink
@linkName={{link.name}}
@linkClass={{link.classNames}}
@route={{link.route}}
@model={{link.model}}
@query={{link.query}}
@models={{link.models}}
@href={{link.href}}
@title={{link.title}}
@contentCSSClass={{link.contentCSSClass}}
@prefixColor={{link.prefixColor}}
@prefixBadge={{link.prefixBadge}}
@prefixType={{link.prefixType}}
@prefixValue={{link.prefixValue}}
@prefixCSSClass={{link.prefixCSSClass}}
@suffixType={{link.suffixType}}
@suffixValue={{link.suffixValue}}
@suffixCSSClass={{link.suffixCSSClass}}
@hoverType={{link.hoverType}}
@hoverValue={{link.hoverValue}}
@hoverAction={{link.hoverAction}}
@hoverTitle={{link.hoverTitle}}
@currentWhen={{link.currentWhen}}
@didInsert={{link.didInsert}}
@willDestroy={{link.willDestroy}}
@content={{link.text}}
@contentComponent={{component
link.contentComponent
status=link.contentComponentArgs
}}
/>
{{/each}}
</Section>
{{/if}}
</template>;
constructor() { export default SidebarApiSection;
super(...arguments);
this.section = new this.args.sectionConfig();
setOwner(this.section, getOwner(this));
}
get shouldDisplay() {
return (
!this.sidebarState.currentPanel.filterable ||
this.sidebarState.filter.length === 0 ||
this.filteredLinks.length > 0
);
}
get filteredLinks() {
if (!this.sidebarState.filter) {
return this.section.links;
}
if (
this.section.text.toLowerCase().match(this.sidebarState.sanitizedFilter)
) {
return this.section.links;
}
return this.section.links.filter((link) => {
return (
link.text
.toString()
.toLowerCase()
.match(this.sidebarState.sanitizedFilter) ||
link.keywords.navigation.some((keyword) =>
keyword.match(this.sidebarState.filter)
)
);
});
}
<template>
{{#if this.shouldDisplay}}
<Section
@sectionName={{this.section.name}}
@headerLinkText={{this.section.text}}
@headerLinkTitle={{this.section.title}}
@headerActionsIcon={{this.section.actionsIcon}}
@headerActions={{this.section.actions}}
@willDestroy={{this.section.willDestroy}}
@collapsable={{@collapsable}}
@displaySection={{this.section.displaySection}}
@hideSectionHeader={{this.section.hideSectionHeader}}
@collapsedByDefault={{this.section.collapsedByDefault}}
>
{{#each this.filteredLinks key="name" as |link|}}
<SectionLink
@linkName={{link.name}}
@linkClass={{link.classNames}}
@route={{link.route}}
@model={{link.model}}
@query={{link.query}}
@models={{link.models}}
@href={{link.href}}
@title={{link.title}}
@contentCSSClass={{link.contentCSSClass}}
@prefixColor={{link.prefixColor}}
@prefixBadge={{link.prefixBadge}}
@prefixType={{link.prefixType}}
@prefixValue={{link.prefixValue}}
@prefixCSSClass={{link.prefixCSSClass}}
@suffixType={{link.suffixType}}
@suffixValue={{link.suffixValue}}
@suffixCSSClass={{link.suffixCSSClass}}
@hoverType={{link.hoverType}}
@hoverValue={{link.hoverValue}}
@hoverAction={{link.hoverAction}}
@hoverTitle={{link.hoverTitle}}
@currentWhen={{link.currentWhen}}
@didInsert={{link.didInsert}}
@willDestroy={{link.willDestroy}}
@content={{link.text}}
@contentComponent={{component
link.contentComponent
status=link.contentComponentArgs
}}
/>
{{/each}}
</Section>
{{/if}}
</template>
}

View File

@ -1,32 +1,89 @@
import Component from "@glimmer/component"; import Component from "@glimmer/component";
import { cached } from "@glimmer/tracking";
import { getOwner, setOwner } from "@ember/owner";
import { service } from "@ember/service"; import { service } from "@ember/service";
import AdminHeader from "./admin-header";
import ApiSection from "./api-section"; import ApiSection from "./api-section";
import FilterNoResults from "./filter-no-results"; import PanelHeader from "./panel-header";
export default class SidebarApiSections extends Component { export default class SidebarApiSections extends Component {
@service sidebarState; @service sidebarState;
get sections() { get sections() {
let sectionConfigs;
if (this.sidebarState.combinedMode) { if (this.sidebarState.combinedMode) {
return this.sidebarState.panels sectionConfigs = this.sidebarState.panels
.filter((panel) => !panel.hidden) .filter((panel) => !panel.hidden)
.flatMap((panel) => panel.sections); .flatMap((panel) => panel.sections);
} else { } else {
return this.sidebarState.currentPanel.sections; sectionConfigs = this.sidebarState.currentPanel.sections;
} }
return sectionConfigs.map((Section) => {
const SidebarSection = prepareSidebarSectionClass(Section);
const sectionInstance = new SidebarSection({
filterable:
!this.sidebarState.combinedMode &&
this.sidebarState.currentPanel.filterable,
sidebarState: this.sidebarState,
});
setOwner(sectionInstance, getOwner(this));
return sectionInstance;
});
}
get filteredSections() {
return this.sections.filter((section) => section.filtered);
} }
<template> <template>
<AdminHeader /> <PanelHeader @sections={{this.filteredSections}} />
{{#each this.sections as |sectionConfig|}} {{#each this.filteredSections as |section|}}
<ApiSection <ApiSection @section={{section}} @collapsable={{@collapsable}} />
@sectionConfig={{sectionConfig}}
@collapsable={{@collapsable}}
/>
{{/each}} {{/each}}
<FilterNoResults />
</template> </template>
} }
// 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) {
return class extends Section {
constructor({ filterable, sidebarState }) {
super();
this.filterable = filterable;
this.sidebarState = sidebarState;
}
@cached
get filteredLinks() {
if (!this.filterable || !this.sidebarState.filter) {
return this.links;
}
if (this.text.toLowerCase().match(this.sidebarState.sanitizedFilter)) {
return this.links;
}
return this.links.filter((link) => {
return (
link.text
.toString()
.toLowerCase()
.match(this.sidebarState.sanitizedFilter) ||
link.keywords.navigation.some((keyword) =>
keyword.match(this.sidebarState.filter)
)
);
});
}
get filtered() {
return !this.filterable || this.filteredLinks?.length > 0;
}
};
}

View File

@ -1,28 +1,17 @@
import Component from "@glimmer/component";
import { LinkTo } from "@ember/routing"; import { LinkTo } from "@ember/routing";
import { service } from "@ember/service";
import { ADMIN_PANEL } from "discourse/lib/sidebar/panels";
import { defaultHomepage } from "discourse/lib/utilities"; import { defaultHomepage } from "discourse/lib/utilities";
import icon from "discourse-common/helpers/d-icon"; import icon from "discourse-common/helpers/d-icon";
import i18n from "discourse-common/helpers/i18n"; import i18n from "discourse-common/helpers/i18n";
export default class BackToForum extends Component { const BackToForum = <template>
@service sidebarState; <LinkTo
@route="discovery.{{(defaultHomepage)}}"
class="sidebar-sections__back-to-forum"
>
{{icon "arrow-left"}}
get shouldDisplay() { <span>{{i18n "sidebar.back_to_forum"}}</span>
return this.sidebarState.isCurrentPanel(ADMIN_PANEL); </LinkTo>
} </template>;
<template> export default BackToForum;
{{#if this.shouldDisplay}}
<LinkTo
@route="discovery.{{(defaultHomepage)}}"
class="sidebar-sections__back-to-forum"
>
{{icon "arrow-left"}}
<span>{{i18n "admin.back_to_forum"}}</span>
</LinkTo>
{{/if}}
</template>
}

View File

@ -1,32 +1,21 @@
import Component from "@glimmer/component"; import Component from "@glimmer/component";
import { service } from "@ember/service"; import { service } from "@ember/service";
import { htmlSafe } from "@ember/template";
import i18n from "discourse-common/helpers/i18n"; import i18n from "discourse-common/helpers/i18n";
import getURL from "discourse-common/lib/get-url";
import I18n from "discourse-i18n";
export default class FilterNoResults extends Component { export default class FilterNoResults extends Component {
@service sidebarState; @service sidebarState;
/**
* Component is rendered when panel is filtreable
* Visibility is additionally controlled by CSS rule `.sidebar-section-wrapper + .sidebar-no-results`
*/
get shouldDisplay() { get shouldDisplay() {
return this.sidebarState.currentPanel.filterable; return (
this.sidebarState.currentPanel.filterable &&
!!(this.args.sections?.length === 0)
);
} }
get noResultsDescription() { get noResultsDescription() {
const params = { return this.sidebarState.currentPanel.filterNoResultsDescription(
filter: this.sidebarState.filter, this.sidebarState.filter
settings_filter_url: getURL( );
`/admin/site_settings/category/all_results?filter=${this.sidebarState.filter}`
),
user_list_filter_url: getURL(
`/admin/users/list/active?username=${this.sidebarState.filter}`
),
};
return htmlSafe(I18n.t("sidebar.no_results.description", params));
} }
<template> <template>
@ -35,9 +24,11 @@ export default class FilterNoResults extends Component {
<h4 class="sidebar-no-results__title">{{i18n <h4 class="sidebar-no-results__title">{{i18n
"sidebar.no_results.title" "sidebar.no_results.title"
}}</h4> }}</h4>
<p {{#if this.noResultsDescription}}
class="sidebar-no-results__description" <p class="sidebar-no-results__description">
>{{this.noResultsDescription}}</p> {{this.noResultsDescription}}
</p>
{{/if}}
</div> </div>
{{/if}} {{/if}}
</template> </template>

View File

@ -5,7 +5,7 @@ import DButton from "discourse/components/d-button";
import SidebarSectionForm from "discourse/components/modal/sidebar-section-form"; import SidebarSectionForm from "discourse/components/modal/sidebar-section-form";
import PluginOutlet from "discourse/components/plugin-outlet"; import PluginOutlet from "discourse/components/plugin-outlet";
import routeAction from "discourse/helpers/route-action"; import routeAction from "discourse/helpers/route-action";
import { ADMIN_PANEL } from "discourse/lib/sidebar/panels"; import { MAIN_PANEL } from "discourse/lib/sidebar/panels";
export default class SidebarFooter extends Component { export default class SidebarFooter extends Component {
@service capabilities; @service capabilities;
@ -16,7 +16,7 @@ export default class SidebarFooter extends Component {
@service sidebarState; @service sidebarState;
get showManageSectionsButton() { get showManageSectionsButton() {
return this.currentUser && !this.sidebarState.isCurrentPanel(ADMIN_PANEL); return this.currentUser && this.sidebarState.isCurrentPanel(MAIN_PANEL);
} }
get showToggleMobileButton() { get showToggleMobileButton() {

View File

@ -1,28 +1,29 @@
import Component from "@glimmer/component"; import Component from "@glimmer/component";
import { service } from "@ember/service"; import { service } from "@ember/service";
import { ADMIN_PANEL } from "discourse/lib/sidebar/panels";
import BackToForum from "./back-to-forum"; import BackToForum from "./back-to-forum";
import Filter from "./filter"; import Filter from "./filter";
import FilterNoResults from "./filter-no-results";
import ToggleAllSections from "./toggle-all-sections"; import ToggleAllSections from "./toggle-all-sections";
export default class AdminHeader extends Component { export default class PanelHeader extends Component {
@service sidebarState; @service sidebarState;
get shouldDisplay() { get shouldDisplay() {
return this.sidebarState.isCurrentPanel(ADMIN_PANEL); return this.sidebarState.currentPanel.displayHeader;
} }
<template> <template>
{{#if this.shouldDisplay}} {{#if this.shouldDisplay}}
<div class="sidebar-admin-header"> <div class="sidebar-panel-header">
<div class="sidebar-admin-header__row"> <div class="sidebar-panel-header__row">
<BackToForum /> <BackToForum />
<ToggleAllSections @sections={{@sections}} /> <ToggleAllSections @sections={{@sections}} />
</div> </div>
<div class="sidebar-admin-header__row"> <div class="sidebar-panel-header__row">
<Filter /> <Filter />
</div> </div>
</div> </div>
<FilterNoResults @sections={{@sections}} />
{{/if}} {{/if}}
</template> </template>
} }

View File

@ -5,7 +5,7 @@ const SidebarSectionHeader = <template>
<DButton <DButton
@title="sidebar.toggle_section" @title="sidebar.toggle_section"
@action={{@toggleSectionDisplay}} @action={{@toggleSectionDisplay}}
aria-controls={{@sidebarSectionContentID}} aria-controls={{@sidebarSectionContentId}}
aria-expanded={{if @isExpanded "true" "false"}} aria-expanded={{if @isExpanded "true" "false"}}
class="sidebar-section-header sidebar-section-header-collapsable btn-transparent" class="sidebar-section-header sidebar-section-header-collapsable btn-transparent"
> >

View File

@ -5,6 +5,10 @@ import { action } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert"; import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import { service } from "@ember/service"; import { service } from "@ember/service";
import { isEmpty } from "@ember/utils"; import { isEmpty } from "@ember/utils";
import {
getCollapsedSidebarSectionKey,
getSidebarSectionContentId,
} from "discourse/lib/sidebar/helpers";
import icon from "discourse-common/helpers/d-icon"; import icon from "discourse-common/helpers/d-icon";
import i18n from "discourse-common/helpers/i18n"; import i18n from "discourse-common/helpers/i18n";
import { bind } from "discourse-common/utils/decorators"; import { bind } from "discourse-common/utils/decorators";
@ -16,8 +20,10 @@ export default class SidebarSection extends Component {
@service keyValueStore; @service keyValueStore;
@service sidebarState; @service sidebarState;
sidebarSectionContentID = `sidebar-section-content-${this.args.sectionName}`; sidebarSectionContentId = getSidebarSectionContentId(this.args.sectionName);
collapsedSidebarSectionKey = `sidebar-section-${this.args.sectionName}-collapsed`; collapsedSidebarSectionKey = getCollapsedSidebarSectionKey(
this.args.sectionName
);
willDestroy() { willDestroy() {
super.willDestroy(...arguments); super.willDestroy(...arguments);
@ -116,7 +122,7 @@ export default class SidebarSection extends Component {
<div class="sidebar-section-header-wrapper sidebar-row"> <div class="sidebar-section-header-wrapper sidebar-row">
<SectionHeader <SectionHeader
@collapsable={{@collapsable}} @collapsable={{@collapsable}}
@sidebarSectionContentID={{this.sidebarSectionContentID}} @sidebarSectionContentId={{this.sidebarSectionContentId}}
@toggleSectionDisplay={{this.toggleSectionDisplay}} @toggleSectionDisplay={{this.toggleSectionDisplay}}
@isExpanded={{this.displaySectionContent}} @isExpanded={{this.displaySectionContent}}
> >
@ -174,7 +180,7 @@ export default class SidebarSection extends Component {
{{#if this.displaySectionContent}} {{#if this.displaySectionContent}}
<ul <ul
id={{this.sidebarSectionContentID}} id={{this.sidebarSectionContentId}}
class="sidebar-section-content" class="sidebar-section-content"
> >
{{yield}} {{yield}}

View File

@ -2,24 +2,30 @@ import Component from "@glimmer/component";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { service } from "@ember/service"; import { service } from "@ember/service";
import DButton from "discourse/components/d-button"; import DButton from "discourse/components/d-button";
import { ADMIN_NAV_MAP } from "discourse/lib/sidebar/admin-nav-map"; import { getCollapsedSidebarSectionKey } from "discourse/lib/sidebar/helpers";
export default class ToggleAllSections extends Component { export default class ToggleAllSections extends Component {
@service sidebarState; @service sidebarState;
@service keyValueStore; @service keyValueStore;
get collapsableSections() {
return this.args.sections.filter(
(section) => section.displaySection && !section.hideSectionHeader
);
}
get allSectionsExpanded() { get allSectionsExpanded() {
return ADMIN_NAV_MAP.every((adminNav) => { return this.collapsableSections.every((section) => {
return !this.sidebarState.collapsedSections.has( return !this.sidebarState.collapsedSections.has(
`sidebar-section-${this.sidebarState.currentPanel.key}-${adminNav.name}-collapsed` getCollapsedSidebarSectionKey(section.name)
); );
}); });
} }
get title() { get title() {
return this.allSectionsExpanded return this.allSectionsExpanded
? "admin.collapse_all_sections" ? "sidebar.collapse_all_sections"
: "admin.expand_all_sections"; : "sidebar.expand_all_sections";
} }
get icon() { get icon() {
@ -30,12 +36,11 @@ export default class ToggleAllSections extends Component {
toggleAllSections() { toggleAllSections() {
const collapse = this.allSectionsExpanded; const collapse = this.allSectionsExpanded;
ADMIN_NAV_MAP.forEach((adminNav) => { this.collapsableSections.forEach((section) => {
const key = `${this.sidebarState.currentPanel.key}-${adminNav.name}`;
if (collapse) { if (collapse) {
this.sidebarState.collapseSection(key); this.sidebarState.collapseSection(section.name);
} else { } else {
this.sidebarState.expandSection(key); this.sidebarState.expandSection(section.name);
} }
}); });
} }

View File

@ -1,4 +1,5 @@
import { cached } from "@glimmer/tracking"; import { cached } from "@glimmer/tracking";
import { htmlSafe } from "@ember/template";
import PreloadStore from "discourse/lib/preload-store"; import PreloadStore from "discourse/lib/preload-store";
import { ADMIN_NAV_MAP } from "discourse/lib/sidebar/admin-nav-map"; import { ADMIN_NAV_MAP } from "discourse/lib/sidebar/admin-nav-map";
import BaseCustomSidebarPanel from "discourse/lib/sidebar/base-custom-sidebar-panel"; import BaseCustomSidebarPanel from "discourse/lib/sidebar/base-custom-sidebar-panel";
@ -6,6 +7,7 @@ import BaseCustomSidebarSection from "discourse/lib/sidebar/base-custom-sidebar-
import BaseCustomSidebarSectionLink from "discourse/lib/sidebar/base-custom-sidebar-section-link"; import BaseCustomSidebarSectionLink from "discourse/lib/sidebar/base-custom-sidebar-section-link";
import { ADMIN_PANEL } from "discourse/lib/sidebar/panels"; import { ADMIN_PANEL } from "discourse/lib/sidebar/panels";
import { getOwnerWithFallback } from "discourse-common/lib/get-owner"; import { getOwnerWithFallback } from "discourse-common/lib/get-owner";
import getURL from "discourse-common/lib/get-url";
import I18n from "discourse-i18n"; import I18n from "discourse-i18n";
let additionalAdminSidebarSectionLinks = {}; let additionalAdminSidebarSectionLinks = {};
@ -81,6 +83,7 @@ class SidebarAdminSectionLink extends BaseCustomSidebarSectionLink {
return this.adminSidebarNavLink.route; return this.adminSidebarNavLink.route;
} }
get keywords() { get keywords() {
return ( return (
this.adminSidebarStateManager.keywords[this.adminSidebarNavLink.name] || { this.adminSidebarStateManager.keywords[this.adminSidebarNavLink.name] || {
@ -257,6 +260,7 @@ function installedPluginsLinkKeywords() {
export default class AdminSidebarPanel extends BaseCustomSidebarPanel { export default class AdminSidebarPanel extends BaseCustomSidebarPanel {
key = ADMIN_PANEL; key = ADMIN_PANEL;
hidden = true; hidden = true;
displayHeader = true;
@cached @cached
get sections() { get sections() {
@ -346,4 +350,18 @@ export default class AdminSidebarPanel extends BaseCustomSidebarPanel {
get filterable() { get filterable() {
return true; return true;
} }
filterNoResultsDescription(filter) {
const params = {
filter,
settings_filter_url: getURL(
`/admin/site_settings/category/all_results?filter=${filter}`
),
user_list_filter_url: getURL(
`/admin/users/list/active?username=${filter}`
),
};
return htmlSafe(I18n.t("sidebar.no_results.description", params));
}
} }

View File

@ -43,6 +43,13 @@ export default class BaseCustomSidebarPanel {
this.hidden || this.#notImplemented(); this.hidden || this.#notImplemented();
} }
/**
* @returns {boolean} Controls whether the panel will display a header
*/
get displayHeader() {
return false;
}
/** /**
* @returns {boolean} Controls whether the filter is shown * @returns {boolean} Controls whether the filter is shown
*/ */
@ -50,6 +57,17 @@ export default class BaseCustomSidebarPanel {
return false; return false;
} }
/**
* @param {string} filter filter applied
*
* @returns {string | SafeString} Description displayed when the applied filter has no results.
* Use `htmlSafe` from `from "@ember/template` to use HTML strings.
*/
// eslint-disable-next-line no-unused-vars
filterNoResultsDescription(filter) {
return null;
}
#notImplemented() { #notImplemented() {
throw "not implemented"; throw "not implemented";
} }

View File

@ -15,3 +15,11 @@ export function hasDefaultSidebarCategories(siteSettings) {
export function hasDefaultSidebarTags(siteSettings) { export function hasDefaultSidebarTags(siteSettings) {
return siteSettings.default_navigation_menu_tags.length > 0; return siteSettings.default_navigation_menu_tags.length > 0;
} }
export function getSidebarSectionContentId(name) {
return `sidebar-section-content-${name}`;
}
export function getCollapsedSidebarSectionKey(name) {
return `sidebar-section-${name}-collapsed`;
}

View File

@ -7,6 +7,7 @@ import {
currentPanelKey, currentPanelKey,
customPanels as panels, customPanels as panels,
} from "discourse/lib/sidebar/custom-sections"; } from "discourse/lib/sidebar/custom-sections";
import { getCollapsedSidebarSectionKey } from "discourse/lib/sidebar/helpers";
import { import {
COMBINED_MODE, COMBINED_MODE,
MAIN_PANEL, MAIN_PANEL,
@ -81,13 +82,15 @@ export default class SidebarState extends Service {
} }
collapseSection(sectionKey) { collapseSection(sectionKey) {
const collapsedSidebarSectionKey = `sidebar-section-${sectionKey}-collapsed`; const collapsedSidebarSectionKey =
getCollapsedSidebarSectionKey(sectionKey);
this.keyValueStore.setItem(collapsedSidebarSectionKey, true); this.keyValueStore.setItem(collapsedSidebarSectionKey, true);
this.collapsedSections.add(collapsedSidebarSectionKey); this.collapsedSections.add(collapsedSidebarSectionKey);
} }
expandSection(sectionKey) { expandSection(sectionKey) {
const collapsedSidebarSectionKey = `sidebar-section-${sectionKey}-collapsed`; const collapsedSidebarSectionKey =
getCollapsedSidebarSectionKey(sectionKey);
this.keyValueStore.setItem(collapsedSidebarSectionKey, false); this.keyValueStore.setItem(collapsedSidebarSectionKey, false);
this.collapsedSections.delete(collapsedSidebarSectionKey); this.collapsedSections.delete(collapsedSidebarSectionKey);
} }

View File

@ -338,7 +338,7 @@
font-size: var(--font-down-1); font-size: var(--font-down-1);
} }
.sidebar-admin-header__row { .sidebar-panel-header__row {
width: calc(320px - 2 * var(--d-sidebar-row-horizontal-padding)); width: calc(320px - 2 * var(--d-sidebar-row-horizontal-padding));
} }

View File

@ -373,11 +373,8 @@
.sidebar-no-results { .sidebar-no-results {
display: block; display: block;
} }
.sidebar-section-wrapper + .sidebar-no-results {
display: none;
}
.sidebar-admin-header__row { .sidebar-panel-header__row {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: baseline; align-items: baseline;

View File

@ -4806,6 +4806,9 @@ en:
panels: panels:
forum: forum:
label: Forum label: Forum
back_to_forum: "Back to Forum"
collapse_all_sections: "Collapse all sections"
expand_all_sections: "Expand all sections"
filter: "Filter..." filter: "Filter..."
clear_filter: "Clear filter" clear_filter: "Clear filter"
no_results: no_results:
@ -4926,10 +4929,7 @@ en:
admin: admin:
title: "Discourse Admin" title: "Discourse Admin"
moderator: "Moderator" moderator: "Moderator"
back_to_forum: "Back to Forum"
filter_reports: Filter reports filter_reports: Filter reports
expand_all_sections: "Expand all sections"
collapse_all_sections: "Collapse all sections"
tags: tags:
remove_muted_tags_from_latest: remove_muted_tags_from_latest:

View File

@ -31,6 +31,11 @@ describe "Admin Revamp | Sidebar Navigation", type: :system do
expect(sidebar).to have_no_section("admin-root") expect(sidebar).to have_no_section("admin-root")
end end
it "displays the panel header" do
visit("/admin")
expect(sidebar).to have_panel_header
end
it "collapses sections by default" do it "collapses sections by default" do
visit("/admin") visit("/admin")
links = page.all(".sidebar-section-link-content-text") links = page.all(".sidebar-section-link-content-text")
@ -113,6 +118,27 @@ describe "Admin Revamp | Sidebar Navigation", type: :system do
expect(page).to have_no_css(".sidebar-no-results") expect(page).to have_no_css(".sidebar-no-results")
end end
it "displays the no results description message correctly when the filter has no results" do
visit("/admin")
filter.filter("ieeee")
expect(page).to have_no_css(".sidebar-section-link-content-text")
expect(page).to have_css(".sidebar-no-results")
no_results_description = page.find(".sidebar-no-results__description")
expect(no_results_description.text).to eq(
"We couldnt find anything matching ieeee.\n\nDid you want to search site settings or the admin user list?",
)
expect(no_results_description).to have_link(
"search site settings",
href: "/admin/site_settings/category/all_results?filter=ieeee",
)
expect(no_results_description).to have_link(
"admin user list?",
href: "/admin/users/list/active?username=ieeee",
)
end
it "temporarily expands section when filter" do it "temporarily expands section when filter" do
visit("/admin") visit("/admin")
links = page.all(".sidebar-section-link-content-text") links = page.all(".sidebar-section-link-content-text")

View File

@ -49,6 +49,14 @@ module PageObjects
find("#discourse-modal-title") find("#discourse-modal-title")
end end
def has_panel_header?
page.has_css?(".sidebar-panel-header")
end
def has_no_panel_header?
page.has_no_css?(".sidebar-panel-header")
end
def toggle_all_sections def toggle_all_sections
find(".sidebar-toggle-all-sections").click find(".sidebar-toggle-all-sections").click
end end

View File

@ -136,4 +136,10 @@ describe "Viewing sidebar as logged in user", type: :system do
expect(sidebar).to have_all_tags_section_link expect(sidebar).to have_all_tags_section_link
end end
end end
it "shouldn't display the panel header for the main sidebar" do
visit("/latest")
expect(sidebar).to be_visible
expect(sidebar).to have_no_panel_header
end
end end