DEV: Add displaySection to sidebar sections (#18479)

This PR renames the old Sidebar::Section displaySection function
to displaySectionContent, and changes the meaning of displaySection
to hide the entire sidebar section including the header. This is
implemented via an arg passed to Sidebar::Section, which will default
to true if it is not passed, and the BaseCustomSidebarSection class
implements a default of `return true` for this function.
This commit is contained in:
Martin Brennan 2022-10-05 13:57:52 +10:00 committed by GitHub
parent 90f1e2df80
commit e3f1e0e9bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 179 additions and 43 deletions

View File

@ -1,42 +1,44 @@
<div class={{concat "sidebar-section-wrapper sidebar-section-" @sectionName}}>
<div class="sidebar-section-header-wrapper sidebar-row">
<Sidebar::SectionHeader @collapsable={{@collapsable}} @toggleSectionDisplay={{this.toggleSectionDisplay}}>
{{#if @collapsable}}
<span class="sidebar-section-header-caret">
{{d-icon this.headerCaretIcon}}
{{#if this.displaySection}}
<div class={{concat "sidebar-section-wrapper sidebar-section-" @sectionName}}>
<div class="sidebar-section-header-wrapper sidebar-row">
<Sidebar::SectionHeader @collapsable={{@collapsable}} @toggleSectionDisplay={{this.toggleSectionDisplay}}>
{{#if @collapsable}}
<span class="sidebar-section-header-caret">
{{d-icon this.headerCaretIcon}}
</span>
{{/if}}
<span class="sidebar-section-header-text">
{{@headerLinkText}}
</span>
</Sidebar::SectionHeader>
{{#if this.isSingleHeaderAction}}
{{#each @headerActions as |headerAction|}}
<button
type="button"
class="sidebar-section-header-button"
{{on "click" headerAction.action}}
title={{headerAction.title}}
>
{{d-icon @headerActionsIcon}}
</button>
{{/each}}
{{/if}}
<span class="sidebar-section-header-text">
{{@headerLinkText}}
</span>
</Sidebar::SectionHeader>
{{#if this.isMultipleHeaderActions}}
<DropdownSelectBox
@options={{hash icon=@headerActionsIcon placementStrategy="absolute"}}
@content={{@headerActions}}
@onChange={{action "handleMultipleHeaderActions"}}
@class="sidebar-section-header-dropdown" />
{{/if}}
</div>
{{#if this.isSingleHeaderAction}}
{{#each @headerActions as |headerAction|}}
<button
type="button"
class="sidebar-section-header-button"
{{on "click" headerAction.action}}
title={{headerAction.title}}
>
{{d-icon @headerActionsIcon}}
</button>
{{/each}}
{{/if}}
{{#if this.isMultipleHeaderActions}}
<DropdownSelectBox
@options={{hash icon=@headerActionsIcon placementStrategy="absolute"}}
@content={{@headerActions}}
@onChange={{action "handleMultipleHeaderActions"}}
@class="sidebar-section-header-dropdown" />
{{#if this.displaySectionContent}}
<div class="sidebar-section-content">
{{yield}}
</div>
{{/if}}
</div>
{{#if this.displaySection}}
<div class="sidebar-section-content">
{{yield}}
</div>
{{/if}}
</div>
{{/if}}

View File

@ -7,20 +7,20 @@ import { tracked } from "@glimmer/tracking";
export default class SidebarSection extends Component {
@service keyValueStore;
@tracked displaySection;
@tracked displaySectionContent;
collapsedSidebarSectionKey = `sidebar-section-${this.args.sectionName}-collapsed`;
constructor() {
super(...arguments);
if (this.args.collapsable) {
this.displaySection =
this.displaySectionContent =
this.keyValueStore.getItem(this.collapsedSidebarSectionKey) ===
undefined
? true
: false;
} else {
this.displaySection = true;
this.displaySectionContent = true;
}
}
@ -32,9 +32,9 @@ export default class SidebarSection extends Component {
@action
toggleSectionDisplay() {
this.displaySection = !this.displaySection;
this.displaySectionContent = !this.displaySectionContent;
if (this.displaySection) {
if (this.displaySectionContent) {
this.keyValueStore.remove(this.collapsedSidebarSectionKey);
} else {
this.keyValueStore.setItem(this.collapsedSidebarSectionKey, true);
@ -54,7 +54,7 @@ export default class SidebarSection extends Component {
}
get headerCaretIcon() {
return this.displaySection ? "angle-down" : "angle-right";
return this.displaySectionContent ? "angle-down" : "angle-right";
}
get isSingleHeaderAction() {
@ -64,4 +64,12 @@ export default class SidebarSection extends Component {
get isMultipleHeaderActions() {
return this.args.headerActions?.length > 1;
}
get displaySection() {
if (this.args.displaySection === undefined) {
return true;
}
return this.args.displaySection;
}
}

View File

@ -18,7 +18,8 @@
@headerActionsIcon={{customSection.actionsIcon}}
@headerActions={{customSection.actions}}
@willDestroy={{customSection.willDestroy}}
@collapsable={{@collapsableSections}}>
@collapsable={{@collapsableSections}}
@displaySection={{customSection.displaySection}}>
{{#each customSection.links as |link|}}
<Sidebar::SectionLink

View File

@ -35,6 +35,13 @@ export default class BaseCustomSidebarSection {
*/
get links() {}
/**
* @returns {Boolean} Whether or not to show the entire section including heading.
*/
get displaySection() {
return true;
}
_notImplemented() {
throw "not implemented";
}

View File

@ -564,4 +564,49 @@ acceptance("Sidebar - Plugin API", function (needs) {
"does not display my favourite topic custom section link when current route does not match the link's route"
);
});
test("Section that is not displayed via displaySection", async function (assert) {
withPluginApi("1.3.0", (api) => {
api.addSidebarSection((BaseCustomSidebarSection) => {
return class extends BaseCustomSidebarSection {
get name() {
return "test-chat-channels";
}
get text() {
return "chat channels text";
}
get actionsIcon() {
return "cog";
}
get actions() {
return [
{
id: "browseChannels",
title: "Browse channels",
action: () => {},
},
];
}
get links() {
return [];
}
get displaySection() {
return false;
}
};
});
});
await visit("/");
assert.notOk(
exists(".sidebar-section-test-chat-channels"),
"does not display the section"
);
});
});

View File

@ -0,0 +1,73 @@
import { module, test } from "qunit";
import { hbs } from "ember-cli-htmlbars";
import { click, render } from "@ember/test-helpers";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import { exists } from "discourse/tests/helpers/qunit-helpers";
module("Integration | Component | sidebar | section", function (hooks) {
setupRenderingTest(hooks);
test("default displaySection value for section", async function (assert) {
const template = hbs`
<Sidebar::Section
@sectionName="test"
@headerLinkText="test header"
@headerLinkTitle="some title"
@headerActionsIcon="plus"
@headerActions={{this.headerActions}} />`;
this.headerActions = [];
await render(template);
assert.ok(
exists(".sidebar-section-wrapper"),
"section is displayed by default if no display arg is provided"
);
});
test("displaySection is dynamic based on argument", async function (assert) {
const template = hbs`
<Sidebar::Section
@sectionName="test"
@headerLinkText="test header"
@headerLinkTitle="some title"
@headerActionsIcon="plus"
@headerActions={{this.headerActions}}
@displaySection={{this.displaySection}}/>`;
this.displaySection = false;
this.headerActions = [];
await render(template);
assert.notOk(
exists(".sidebar-section-wrapper"),
"section is not displayed"
);
this.set("displaySection", true);
assert.ok(exists(".sidebar-section-wrapper"), "section is displayed");
});
test("can expand and collapse content when section is collapsible", async function (assert) {
const template = hbs`
<Sidebar::Section
@sectionName="test"
@headerLinkText="test header"
@headerLinkTitle="some title"
@headerActionsIcon="plus"
@headerActions={{this.headerActions}}
@collapsable=true />`;
this.headerActions = [];
await render(template);
assert.ok(exists(".sidebar-section-content"), "shows content by default");
await click(".sidebar-section-header-caret");
assert.notOk(
exists(".sidebar-section-content"),
"does not show content after collapsing"
);
});
});