mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 13:09:18 +08:00
DEV: Untangle the admin sidebar from the sidebar code (#27640)
This commit is contained in:
parent
640dccd224
commit
b36cbc7d21
|
@ -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 SectionLink from "./section-link";
|
||||
|
||||
export default class SidebarApiSection extends Component {
|
||||
@service sidebarState;
|
||||
const SidebarApiSection = <template>
|
||||
{{#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() {
|
||||
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>
|
||||
}
|
||||
export default SidebarApiSection;
|
||||
|
|
|
@ -1,32 +1,89 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { cached } from "@glimmer/tracking";
|
||||
import { getOwner, setOwner } from "@ember/owner";
|
||||
import { service } from "@ember/service";
|
||||
import AdminHeader from "./admin-header";
|
||||
import ApiSection from "./api-section";
|
||||
import FilterNoResults from "./filter-no-results";
|
||||
import PanelHeader from "./panel-header";
|
||||
|
||||
export default class SidebarApiSections extends Component {
|
||||
@service sidebarState;
|
||||
|
||||
get sections() {
|
||||
let sectionConfigs;
|
||||
|
||||
if (this.sidebarState.combinedMode) {
|
||||
return this.sidebarState.panels
|
||||
sectionConfigs = this.sidebarState.panels
|
||||
.filter((panel) => !panel.hidden)
|
||||
.flatMap((panel) => panel.sections);
|
||||
} 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>
|
||||
<AdminHeader />
|
||||
<PanelHeader @sections={{this.filteredSections}} />
|
||||
|
||||
{{#each this.sections as |sectionConfig|}}
|
||||
<ApiSection
|
||||
@sectionConfig={{sectionConfig}}
|
||||
@collapsable={{@collapsable}}
|
||||
/>
|
||||
{{#each this.filteredSections as |section|}}
|
||||
<ApiSection @section={{section}} @collapsable={{@collapsable}} />
|
||||
{{/each}}
|
||||
|
||||
<FilterNoResults />
|
||||
</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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,28 +1,17 @@
|
|||
import Component from "@glimmer/component";
|
||||
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 icon from "discourse-common/helpers/d-icon";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
|
||||
export default class BackToForum extends Component {
|
||||
@service sidebarState;
|
||||
const BackToForum = <template>
|
||||
<LinkTo
|
||||
@route="discovery.{{(defaultHomepage)}}"
|
||||
class="sidebar-sections__back-to-forum"
|
||||
>
|
||||
{{icon "arrow-left"}}
|
||||
|
||||
get shouldDisplay() {
|
||||
return this.sidebarState.isCurrentPanel(ADMIN_PANEL);
|
||||
}
|
||||
<span>{{i18n "sidebar.back_to_forum"}}</span>
|
||||
</LinkTo>
|
||||
</template>;
|
||||
|
||||
<template>
|
||||
{{#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>
|
||||
}
|
||||
export default BackToForum;
|
||||
|
|
|
@ -1,32 +1,21 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { service } from "@ember/service";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
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 {
|
||||
@service sidebarState;
|
||||
|
||||
/**
|
||||
* Component is rendered when panel is filtreable
|
||||
* Visibility is additionally controlled by CSS rule `.sidebar-section-wrapper + .sidebar-no-results`
|
||||
*/
|
||||
get shouldDisplay() {
|
||||
return this.sidebarState.currentPanel.filterable;
|
||||
return (
|
||||
this.sidebarState.currentPanel.filterable &&
|
||||
!!(this.args.sections?.length === 0)
|
||||
);
|
||||
}
|
||||
|
||||
get noResultsDescription() {
|
||||
const params = {
|
||||
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));
|
||||
return this.sidebarState.currentPanel.filterNoResultsDescription(
|
||||
this.sidebarState.filter
|
||||
);
|
||||
}
|
||||
|
||||
<template>
|
||||
|
@ -35,9 +24,11 @@ export default class FilterNoResults extends Component {
|
|||
<h4 class="sidebar-no-results__title">{{i18n
|
||||
"sidebar.no_results.title"
|
||||
}}</h4>
|
||||
<p
|
||||
class="sidebar-no-results__description"
|
||||
>{{this.noResultsDescription}}</p>
|
||||
{{#if this.noResultsDescription}}
|
||||
<p class="sidebar-no-results__description">
|
||||
{{this.noResultsDescription}}
|
||||
</p>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</template>
|
||||
|
|
|
@ -5,7 +5,7 @@ import DButton from "discourse/components/d-button";
|
|||
import SidebarSectionForm from "discourse/components/modal/sidebar-section-form";
|
||||
import PluginOutlet from "discourse/components/plugin-outlet";
|
||||
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 {
|
||||
@service capabilities;
|
||||
|
@ -16,7 +16,7 @@ export default class SidebarFooter extends Component {
|
|||
@service sidebarState;
|
||||
|
||||
get showManageSectionsButton() {
|
||||
return this.currentUser && !this.sidebarState.isCurrentPanel(ADMIN_PANEL);
|
||||
return this.currentUser && this.sidebarState.isCurrentPanel(MAIN_PANEL);
|
||||
}
|
||||
|
||||
get showToggleMobileButton() {
|
||||
|
|
|
@ -1,28 +1,29 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { service } from "@ember/service";
|
||||
import { ADMIN_PANEL } from "discourse/lib/sidebar/panels";
|
||||
import BackToForum from "./back-to-forum";
|
||||
import Filter from "./filter";
|
||||
import FilterNoResults from "./filter-no-results";
|
||||
import ToggleAllSections from "./toggle-all-sections";
|
||||
|
||||
export default class AdminHeader extends Component {
|
||||
export default class PanelHeader extends Component {
|
||||
@service sidebarState;
|
||||
|
||||
get shouldDisplay() {
|
||||
return this.sidebarState.isCurrentPanel(ADMIN_PANEL);
|
||||
return this.sidebarState.currentPanel.displayHeader;
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#if this.shouldDisplay}}
|
||||
<div class="sidebar-admin-header">
|
||||
<div class="sidebar-admin-header__row">
|
||||
<div class="sidebar-panel-header">
|
||||
<div class="sidebar-panel-header__row">
|
||||
<BackToForum />
|
||||
<ToggleAllSections @sections={{@sections}} />
|
||||
</div>
|
||||
<div class="sidebar-admin-header__row">
|
||||
<div class="sidebar-panel-header__row">
|
||||
<Filter />
|
||||
</div>
|
||||
</div>
|
||||
<FilterNoResults @sections={{@sections}} />
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
|
@ -5,7 +5,7 @@ const SidebarSectionHeader = <template>
|
|||
<DButton
|
||||
@title="sidebar.toggle_section"
|
||||
@action={{@toggleSectionDisplay}}
|
||||
aria-controls={{@sidebarSectionContentID}}
|
||||
aria-controls={{@sidebarSectionContentId}}
|
||||
aria-expanded={{if @isExpanded "true" "false"}}
|
||||
class="sidebar-section-header sidebar-section-header-collapsable btn-transparent"
|
||||
>
|
||||
|
|
|
@ -5,6 +5,10 @@ import { action } from "@ember/object";
|
|||
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
|
||||
import { service } from "@ember/service";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import {
|
||||
getCollapsedSidebarSectionKey,
|
||||
getSidebarSectionContentId,
|
||||
} from "discourse/lib/sidebar/helpers";
|
||||
import icon from "discourse-common/helpers/d-icon";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
|
@ -16,8 +20,10 @@ export default class SidebarSection extends Component {
|
|||
@service keyValueStore;
|
||||
@service sidebarState;
|
||||
|
||||
sidebarSectionContentID = `sidebar-section-content-${this.args.sectionName}`;
|
||||
collapsedSidebarSectionKey = `sidebar-section-${this.args.sectionName}-collapsed`;
|
||||
sidebarSectionContentId = getSidebarSectionContentId(this.args.sectionName);
|
||||
collapsedSidebarSectionKey = getCollapsedSidebarSectionKey(
|
||||
this.args.sectionName
|
||||
);
|
||||
|
||||
willDestroy() {
|
||||
super.willDestroy(...arguments);
|
||||
|
@ -116,7 +122,7 @@ export default class SidebarSection extends Component {
|
|||
<div class="sidebar-section-header-wrapper sidebar-row">
|
||||
<SectionHeader
|
||||
@collapsable={{@collapsable}}
|
||||
@sidebarSectionContentID={{this.sidebarSectionContentID}}
|
||||
@sidebarSectionContentId={{this.sidebarSectionContentId}}
|
||||
@toggleSectionDisplay={{this.toggleSectionDisplay}}
|
||||
@isExpanded={{this.displaySectionContent}}
|
||||
>
|
||||
|
@ -174,7 +180,7 @@ export default class SidebarSection extends Component {
|
|||
|
||||
{{#if this.displaySectionContent}}
|
||||
<ul
|
||||
id={{this.sidebarSectionContentID}}
|
||||
id={{this.sidebarSectionContentId}}
|
||||
class="sidebar-section-content"
|
||||
>
|
||||
{{yield}}
|
||||
|
|
|
@ -2,24 +2,30 @@ import Component from "@glimmer/component";
|
|||
import { action } from "@ember/object";
|
||||
import { service } from "@ember/service";
|
||||
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 {
|
||||
@service sidebarState;
|
||||
@service keyValueStore;
|
||||
|
||||
get collapsableSections() {
|
||||
return this.args.sections.filter(
|
||||
(section) => section.displaySection && !section.hideSectionHeader
|
||||
);
|
||||
}
|
||||
|
||||
get allSectionsExpanded() {
|
||||
return ADMIN_NAV_MAP.every((adminNav) => {
|
||||
return this.collapsableSections.every((section) => {
|
||||
return !this.sidebarState.collapsedSections.has(
|
||||
`sidebar-section-${this.sidebarState.currentPanel.key}-${adminNav.name}-collapsed`
|
||||
getCollapsedSidebarSectionKey(section.name)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
get title() {
|
||||
return this.allSectionsExpanded
|
||||
? "admin.collapse_all_sections"
|
||||
: "admin.expand_all_sections";
|
||||
? "sidebar.collapse_all_sections"
|
||||
: "sidebar.expand_all_sections";
|
||||
}
|
||||
|
||||
get icon() {
|
||||
|
@ -30,12 +36,11 @@ export default class ToggleAllSections extends Component {
|
|||
toggleAllSections() {
|
||||
const collapse = this.allSectionsExpanded;
|
||||
|
||||
ADMIN_NAV_MAP.forEach((adminNav) => {
|
||||
const key = `${this.sidebarState.currentPanel.key}-${adminNav.name}`;
|
||||
this.collapsableSections.forEach((section) => {
|
||||
if (collapse) {
|
||||
this.sidebarState.collapseSection(key);
|
||||
this.sidebarState.collapseSection(section.name);
|
||||
} else {
|
||||
this.sidebarState.expandSection(key);
|
||||
this.sidebarState.expandSection(section.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { cached } from "@glimmer/tracking";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import PreloadStore from "discourse/lib/preload-store";
|
||||
import { ADMIN_NAV_MAP } from "discourse/lib/sidebar/admin-nav-map";
|
||||
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 { ADMIN_PANEL } from "discourse/lib/sidebar/panels";
|
||||
import { getOwnerWithFallback } from "discourse-common/lib/get-owner";
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import I18n from "discourse-i18n";
|
||||
|
||||
let additionalAdminSidebarSectionLinks = {};
|
||||
|
@ -81,6 +83,7 @@ class SidebarAdminSectionLink extends BaseCustomSidebarSectionLink {
|
|||
|
||||
return this.adminSidebarNavLink.route;
|
||||
}
|
||||
|
||||
get keywords() {
|
||||
return (
|
||||
this.adminSidebarStateManager.keywords[this.adminSidebarNavLink.name] || {
|
||||
|
@ -257,6 +260,7 @@ function installedPluginsLinkKeywords() {
|
|||
export default class AdminSidebarPanel extends BaseCustomSidebarPanel {
|
||||
key = ADMIN_PANEL;
|
||||
hidden = true;
|
||||
displayHeader = true;
|
||||
|
||||
@cached
|
||||
get sections() {
|
||||
|
@ -346,4 +350,18 @@ export default class AdminSidebarPanel extends BaseCustomSidebarPanel {
|
|||
get filterable() {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,13 @@ export default class BaseCustomSidebarPanel {
|
|||
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
|
||||
*/
|
||||
|
@ -50,6 +57,17 @@ export default class BaseCustomSidebarPanel {
|
|||
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() {
|
||||
throw "not implemented";
|
||||
}
|
||||
|
|
|
@ -15,3 +15,11 @@ export function hasDefaultSidebarCategories(siteSettings) {
|
|||
export function hasDefaultSidebarTags(siteSettings) {
|
||||
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`;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
currentPanelKey,
|
||||
customPanels as panels,
|
||||
} from "discourse/lib/sidebar/custom-sections";
|
||||
import { getCollapsedSidebarSectionKey } from "discourse/lib/sidebar/helpers";
|
||||
import {
|
||||
COMBINED_MODE,
|
||||
MAIN_PANEL,
|
||||
|
@ -81,13 +82,15 @@ export default class SidebarState extends Service {
|
|||
}
|
||||
|
||||
collapseSection(sectionKey) {
|
||||
const collapsedSidebarSectionKey = `sidebar-section-${sectionKey}-collapsed`;
|
||||
const collapsedSidebarSectionKey =
|
||||
getCollapsedSidebarSectionKey(sectionKey);
|
||||
this.keyValueStore.setItem(collapsedSidebarSectionKey, true);
|
||||
this.collapsedSections.add(collapsedSidebarSectionKey);
|
||||
}
|
||||
|
||||
expandSection(sectionKey) {
|
||||
const collapsedSidebarSectionKey = `sidebar-section-${sectionKey}-collapsed`;
|
||||
const collapsedSidebarSectionKey =
|
||||
getCollapsedSidebarSectionKey(sectionKey);
|
||||
this.keyValueStore.setItem(collapsedSidebarSectionKey, false);
|
||||
this.collapsedSections.delete(collapsedSidebarSectionKey);
|
||||
}
|
||||
|
|
|
@ -338,7 +338,7 @@
|
|||
font-size: var(--font-down-1);
|
||||
}
|
||||
|
||||
.sidebar-admin-header__row {
|
||||
.sidebar-panel-header__row {
|
||||
width: calc(320px - 2 * var(--d-sidebar-row-horizontal-padding));
|
||||
}
|
||||
|
||||
|
|
|
@ -373,11 +373,8 @@
|
|||
.sidebar-no-results {
|
||||
display: block;
|
||||
}
|
||||
.sidebar-section-wrapper + .sidebar-no-results {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar-admin-header__row {
|
||||
.sidebar-panel-header__row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
|
|
|
@ -4806,6 +4806,9 @@ en:
|
|||
panels:
|
||||
forum:
|
||||
label: Forum
|
||||
back_to_forum: "Back to Forum"
|
||||
collapse_all_sections: "Collapse all sections"
|
||||
expand_all_sections: "Expand all sections"
|
||||
filter: "Filter..."
|
||||
clear_filter: "Clear filter"
|
||||
no_results:
|
||||
|
@ -4926,10 +4929,7 @@ en:
|
|||
admin:
|
||||
title: "Discourse Admin"
|
||||
moderator: "Moderator"
|
||||
back_to_forum: "Back to Forum"
|
||||
filter_reports: Filter reports
|
||||
expand_all_sections: "Expand all sections"
|
||||
collapse_all_sections: "Collapse all sections"
|
||||
|
||||
tags:
|
||||
remove_muted_tags_from_latest:
|
||||
|
|
|
@ -31,6 +31,11 @@ describe "Admin Revamp | Sidebar Navigation", type: :system do
|
|||
expect(sidebar).to have_no_section("admin-root")
|
||||
end
|
||||
|
||||
it "displays the panel header" do
|
||||
visit("/admin")
|
||||
expect(sidebar).to have_panel_header
|
||||
end
|
||||
|
||||
it "collapses sections by default" do
|
||||
visit("/admin")
|
||||
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")
|
||||
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 couldn’t 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
|
||||
visit("/admin")
|
||||
links = page.all(".sidebar-section-link-content-text")
|
||||
|
|
|
@ -49,6 +49,14 @@ module PageObjects
|
|||
find("#discourse-modal-title")
|
||||
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
|
||||
find(".sidebar-toggle-all-sections").click
|
||||
end
|
||||
|
|
|
@ -136,4 +136,10 @@ describe "Viewing sidebar as logged in user", type: :system do
|
|||
expect(sidebar).to have_all_tags_section_link
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue
Block a user