FEATURE: Replace hamburger dropdown with Sidebar when undock (#17600)
When the experimental Sidebar is enabled, the hamburger drop down is replaced by a sidebar drop down. A user is given the ability to dock and undock the sidebar depending on their personal preference. Do also note that the experimental sidebar is well, considered experimental at this point so I do not intend for the features here to be perfect. What I aim to do here is to ship the changes fast so that it can be used internally by the team to provide feedback. Custom links added by plugins and dark mode toggle has not been implemented as part of this commit as I aim to tackle it in another commit. Co-authored-by: awesomerobot <kris.aubuchon@discourse.org>
This commit is contained in:
parent
71eb8d2e8e
commit
99073338de
|
@ -1,3 +1,4 @@
|
|||
import { inject as service } from "@ember/service";
|
||||
import { empty, equal, notEmpty } from "@ember/object/computed";
|
||||
import Component from "@ember/component";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
|
@ -22,6 +23,7 @@ export default Component.extend({
|
|||
forwardEvent: false,
|
||||
preventFocus: false,
|
||||
onKeyDown: null,
|
||||
router: service(),
|
||||
|
||||
isLoading: computed({
|
||||
set(key, value) {
|
||||
|
@ -149,6 +151,10 @@ export default Component.extend({
|
|||
}
|
||||
}
|
||||
|
||||
if (this.route) {
|
||||
this.router.transitionTo(this.route);
|
||||
}
|
||||
|
||||
if (this.href && this.href.length) {
|
||||
DiscourseURL.routeTo(this.href);
|
||||
}
|
||||
|
|
|
@ -10,10 +10,15 @@ export default class SidebarSection extends GlimmerComponent {
|
|||
constructor() {
|
||||
super(...arguments);
|
||||
|
||||
this.displaySection =
|
||||
this.keyValueStore.getItem(this.collapsedSidebarSectionKey) === undefined
|
||||
? true
|
||||
: false;
|
||||
if (this.args.collapsable) {
|
||||
this.displaySection =
|
||||
this.keyValueStore.getItem(this.collapsedSidebarSectionKey) ===
|
||||
undefined
|
||||
? true
|
||||
: false;
|
||||
} else {
|
||||
this.displaySection = true;
|
||||
}
|
||||
}
|
||||
|
||||
willDestroy() {
|
||||
|
|
|
@ -231,10 +231,22 @@ const SiteHeaderComponent = MountWidget.extend(
|
|||
|
||||
this.appEvents.on("header:show-topic", this, "setTopic");
|
||||
this.appEvents.on("header:hide-topic", this, "setTopic");
|
||||
|
||||
if (this.currentUser?.redesigned_user_menu_enabled) {
|
||||
this.appEvents.on("user-menu:rendered", this, "_animateMenu");
|
||||
}
|
||||
|
||||
if (
|
||||
this.currentUser?.experimental_sidebar_enabled &&
|
||||
!this.site.mobileView
|
||||
) {
|
||||
this.appEvents.on(
|
||||
"sidebar:docked-state-updated",
|
||||
this,
|
||||
"queueRerender"
|
||||
);
|
||||
}
|
||||
|
||||
this.dispatch("notifications:changed", "user-notifications");
|
||||
this.dispatch("header:keyboard-trigger", "header");
|
||||
this.dispatch("user-menu:navigation", "user-menu");
|
||||
|
@ -351,6 +363,17 @@ const SiteHeaderComponent = MountWidget.extend(
|
|||
this.appEvents.off("user-menu:rendered", this, "_animateMenu");
|
||||
}
|
||||
|
||||
if (
|
||||
this.currentUser?.experimental_sidebar_enabled &&
|
||||
!this.site.mobileView
|
||||
) {
|
||||
this.appEvents.off(
|
||||
"sidebar:docked-state-updated",
|
||||
this,
|
||||
"queueRerender"
|
||||
);
|
||||
}
|
||||
|
||||
if (this.currentUser) {
|
||||
this.currentUser.off("status-changed", this, "queueRerender");
|
||||
}
|
||||
|
@ -367,6 +390,7 @@ const SiteHeaderComponent = MountWidget.extend(
|
|||
return {
|
||||
topic: this._topic,
|
||||
canSignUp: this.canSignUp,
|
||||
sidebarDocked: this.sidebarDocked,
|
||||
};
|
||||
},
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ export default Controller.extend({
|
|||
discourseDebounce(this, this._mainOutletAnimate, 250);
|
||||
|
||||
this.toggleProperty("showSidebar");
|
||||
this.appEvents.trigger("sidebar:docked-state-updated");
|
||||
|
||||
if (!this.site.mobileView) {
|
||||
if (this.showSidebar) {
|
||||
|
|
|
@ -12,6 +12,7 @@ export default {
|
|||
const site = container.lookup("site:main");
|
||||
|
||||
site.set("mobileView", Mobile.mobileView);
|
||||
site.set("desktopView", !Mobile.mobileView);
|
||||
site.set("isMobileDevice", Mobile.isMobileDevice);
|
||||
|
||||
setResolverOption("mobileView", Mobile.mobileView);
|
||||
|
|
|
@ -51,6 +51,10 @@ const ApplicationRoute = DiscourseRoute.extend(OpenComposer, {
|
|||
mobile.toggleMobileView();
|
||||
},
|
||||
|
||||
toggleSidebar() {
|
||||
this.controllerFor("application").send("toggleSidebar");
|
||||
},
|
||||
|
||||
logout: unlessReadOnly(
|
||||
"_handleLogout",
|
||||
I18n.t("read_only_mode.logout_disabled")
|
||||
|
|
|
@ -2,7 +2,18 @@
|
|||
<a href="#main-container" id="skip-link">{{i18n "skip_to_main_content"}}</a>
|
||||
<DDocument />
|
||||
<PluginOutlet @name="above-site-header" @connectorTagName="div" />
|
||||
<SiteHeader @canSignUp={{this.canSignUp}} @showCreateAccount={{route-action "showCreateAccount"}} @showLogin={{route-action "showLogin"}} @showKeyboard={{route-action "showKeyboardShortcutsHelp"}} @toggleMobileView={{route-action "toggleMobileView"}} @toggleAnonymous={{route-action "toggleAnonymous"}} @logout={{route-action "logout"}} @toggleSidebar={{action "toggleSidebar"}} />
|
||||
|
||||
<SiteHeader
|
||||
@canSignUp={{this.canSignUp}}
|
||||
@showCreateAccount={{route-action "showCreateAccount"}}
|
||||
@showLogin={{route-action "showLogin"}}
|
||||
@showKeyboard={{route-action "showKeyboardShortcutsHelp"}}
|
||||
@toggleMobileView={{route-action "toggleMobileView"}}
|
||||
@toggleAnonymous={{route-action "toggleAnonymous"}}
|
||||
@logout={{route-action "logout"}}
|
||||
@sidebarDocked={{and this.currentUser.experimental_sidebar_enabled this.showSidebar this.site.desktopView}}
|
||||
@toggleSidebar={{action "toggleSidebar"}} />
|
||||
|
||||
<SoftwareUpdatePrompt />
|
||||
|
||||
<PluginOutlet @name="below-site-header" @connectorTagName="div" @args={{hash currentPath=this.router._router.currentPath}} />
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<DSection @pageClass="has-sidebar" @class="sidebar-container" @scrollTop={{false}}>
|
||||
<div class="sidebar-scroll-wrap">
|
||||
<Sidebar::Sections />
|
||||
<Sidebar::Sections @collapsableSections={{true}}/>
|
||||
<Sidebar::Footer @toggleSidebar={{@toggleSidebar}} @tagName=""/>
|
||||
</div>
|
||||
</DSection>
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
@headerLinkText={{i18n "sidebar.sections.categories.header_link_text"}}
|
||||
@headerLinkTitle={{i18n "sidebar.sections.categories.header_link_title"}}
|
||||
@headerActions={{array (hash action=this.editTracked title=(i18n "sidebar.sections.categories.header_action_title"))}}
|
||||
@headerActionsIcon="pencil-alt" >
|
||||
@headerActionsIcon="pencil-alt"
|
||||
@collapsable={{@collapsable}} >
|
||||
|
||||
{{#if (gt this.sectionLinks.length 0)}}
|
||||
{{#each this.sectionLinks as |sectionLink|}}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<div class="sidebar-footer-wrapper">
|
||||
<div class="sidebar-footer-container">
|
||||
<div class="sidebar-footer-links">
|
||||
<LinkTo @route="about" title={{i18n "about.simple_title"}} class="sidebar-footer-link sidebar-footer-link-about">
|
||||
{{i18n "about.simple_title"}}
|
||||
</LinkTo>
|
||||
|
||||
{{#if this.currentUser.admin}}
|
||||
<LinkTo @route="adminSiteSettings" title={{i18n "admin.site_settings.title"}} class="sidebar-footer-link sidebar-footer-link-site-settings">
|
||||
{{i18n "admin.site_settings.title"}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="sidebar-footer-actions">
|
||||
<DButton
|
||||
@action={{route-action "showKeyboardShortcutsHelp"}}
|
||||
@title={{i18n "keyboard_shortcuts_help.title"}}
|
||||
@icon="keyboard"
|
||||
@class="sidebar-footer-actions-button sidebar-footer-actions-keyboard-shortcuts" />
|
||||
|
||||
{{#if this.site.desktopView}}
|
||||
<DButton
|
||||
@action={{@toggleSidebar}}
|
||||
@icon="thumbtack"
|
||||
@class="sidebar-footer-actions-button sidebar-footer-actions-dock-toggle"/>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,12 @@
|
|||
<div class="hamburger-panel">
|
||||
<div class="hamburger-menu revamped menu-panel drop-down" data-max-width="320">
|
||||
<div class="panel-body">
|
||||
<div class="panel-body-content">
|
||||
<div class="sidebar-hamburger-dropdown">
|
||||
<Sidebar::Sections @collapsableSections={{false}}/>
|
||||
<Sidebar::Footer @toggleSidebar={{route-action "toggleSidebar"}} @tagName="" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -6,7 +6,8 @@
|
|||
@headerActions={{array (hash action=(fn (route-action "composePrivateMessage") null null))}}
|
||||
@headerActionsIcon="plus"
|
||||
@headerLinkText={{i18n "sidebar.sections.messages.header_link_text"}}
|
||||
@headerLinkTitle={{i18n "sidebar.sections.messages.header_link_title"}} >
|
||||
@headerLinkTitle={{i18n "sidebar.sections.messages.header_link_title"}}
|
||||
@collapsable={{@collapsable}}>
|
||||
|
||||
{{#each this.personalMessagesSectionLinks as |personalMessageSectionLink|}}
|
||||
{{#if personalMessageSectionLink.shouldDisplay}}
|
||||
|
@ -21,8 +22,6 @@
|
|||
{{/each}}
|
||||
|
||||
{{#if (gt this.groupMessagesSectionLinks.length 0)}}
|
||||
<hr>
|
||||
|
||||
{{#each this.groupMessagesSectionLinks as |groupMessageSectionLink|}}
|
||||
{{#if groupMessageSectionLink.shouldDisplay}}
|
||||
<Sidebar::SectionLink
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
<div class={{concat "sidebar-section-wrapper sidebar-section-" @sectionName}}>
|
||||
<div class="sidebar-section-header">
|
||||
<button
|
||||
type="button"
|
||||
class="sidebar-section-header-caret"
|
||||
title="toggle section"
|
||||
{{on "click" this.toggleSectionDisplay}}
|
||||
>
|
||||
{{d-icon this.headerCaretIcon}}
|
||||
</button>
|
||||
{{#if @collapsable}}
|
||||
<button
|
||||
type="button"
|
||||
class="sidebar-section-header-caret"
|
||||
title="toggle section"
|
||||
{{on "click" this.toggleSectionDisplay}}
|
||||
>
|
||||
{{d-icon this.headerCaretIcon}}
|
||||
</button>
|
||||
{{/if}}
|
||||
|
||||
{{#if @headerRoute}}
|
||||
<LinkTo
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<div class="sidebar-sections">
|
||||
<Sidebar::TopicsSection />
|
||||
<Sidebar::CategoriesSection />
|
||||
<Sidebar::TopicsSection @collapsable={{@collapsableSections}}/>
|
||||
<Sidebar::CategoriesSection @collapsable={{@collapsableSections}}/>
|
||||
|
||||
{{#if this.siteSettings.tagging_enabled}}
|
||||
<Sidebar::TagsSection />
|
||||
<Sidebar::TagsSection @collapsable={{@collapsableSections}}/>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.siteSettings.enable_personal_messages}}
|
||||
<Sidebar::MessagesSection />
|
||||
<Sidebar::MessagesSection @collapsable={{@collapsableSections}}/>
|
||||
{{/if}}
|
||||
|
||||
{{#each this.customSections as |customSection|}}
|
||||
|
@ -18,7 +18,8 @@
|
|||
@headerLinkTitle={{customSection.title}}
|
||||
@headerActionsIcon={{customSection.actionsIcon}}
|
||||
@headerActions={{customSection.actions}}
|
||||
@willDestroy={{customSection.willDestroy}}>
|
||||
@willDestroy={{customSection.willDestroy}}
|
||||
@collapsable={{@collapsableSections}}>
|
||||
|
||||
{{#each customSection.links as |link|}}
|
||||
<Sidebar::SectionLink
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
@headerLinkText={{i18n "sidebar.sections.tags.header_link_text"}}
|
||||
@headerLinkTitle={{i18n "sidebar.sections.tags.header_link_title"}}
|
||||
@headerActions={{array (hash action=this.editTracked title=(i18n "sidebar.sections.tags.header_action_title"))}}
|
||||
@headerActionsIcon="pencil-alt" >
|
||||
@headerActionsIcon="pencil-alt"
|
||||
@collapsable={{@collapsable}}>
|
||||
|
||||
{{#if (gt this.sectionLinks.length 0)}}
|
||||
{{#each this.sectionLinks as |sectionLink|}}
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
@headerLinkText={{i18n "sidebar.sections.topics.header_link_text"}}
|
||||
@headerLinkTitle={{i18n "sidebar.sections.topics.header_link_title"}}
|
||||
@headerActionsIcon="plus"
|
||||
@headerActions={{array (hash action=this.composeTopic title=(i18n "sidebar.sections.topics.header_action_title"))}}>
|
||||
@headerActions={{array (hash action=this.composeTopic title=(i18n "sidebar.sections.topics.header_action_title"))}}
|
||||
@collapsable={{@collapsable}} >
|
||||
|
||||
{{#each this.sectionLinks as |sectionLink|}}
|
||||
<Sidebar::SectionLink
|
||||
|
|
|
@ -4,9 +4,6 @@ import hbs from "discourse/widgets/hbs-compiler";
|
|||
createWidget("header-contents", {
|
||||
tagName: "div.contents.clearfix",
|
||||
template: hbs`
|
||||
{{#if attrs.sidebarEnabled}}
|
||||
{{sidebar-toggle attrs=attrs}}
|
||||
{{/if}}
|
||||
{{home-logo attrs=attrs}}
|
||||
{{#if attrs.topic}}
|
||||
{{header-topic-info attrs=attrs}}
|
||||
|
|
|
@ -244,32 +244,34 @@ createWidget("header-icons", {
|
|||
|
||||
icons.push(search);
|
||||
|
||||
const hamburger = this.attach("header-dropdown", {
|
||||
title: "hamburger_menu",
|
||||
icon: "bars",
|
||||
iconId: "toggle-hamburger-menu",
|
||||
active: attrs.hamburgerVisible,
|
||||
action: "toggleHamburger",
|
||||
href: "",
|
||||
classNames: ["hamburger-dropdown"],
|
||||
if (!attrs.sidebarDocked) {
|
||||
const hamburger = this.attach("header-dropdown", {
|
||||
title: "hamburger_menu",
|
||||
icon: "bars",
|
||||
iconId: "toggle-hamburger-menu",
|
||||
active: attrs.hamburgerVisible,
|
||||
action: "toggleHamburger",
|
||||
href: "",
|
||||
classNames: ["hamburger-dropdown"],
|
||||
|
||||
contents() {
|
||||
let { currentUser } = this;
|
||||
if (currentUser && currentUser.reviewable_count) {
|
||||
return h(
|
||||
"div.badge-notification.reviewables",
|
||||
{
|
||||
attributes: {
|
||||
title: I18n.t("notifications.reviewable_items"),
|
||||
contents() {
|
||||
let { currentUser } = this;
|
||||
if (currentUser && currentUser.reviewable_count) {
|
||||
return h(
|
||||
"div.badge-notification.reviewables",
|
||||
{
|
||||
attributes: {
|
||||
title: I18n.t("notifications.reviewable_items"),
|
||||
},
|
||||
},
|
||||
},
|
||||
this.currentUser.reviewable_count
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
this.currentUser.reviewable_count
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
icons.push(hamburger);
|
||||
icons.push(hamburger);
|
||||
}
|
||||
|
||||
if (attrs.user) {
|
||||
icons.push(
|
||||
|
@ -332,6 +334,26 @@ export function attachAdditionalPanel(name, toggle, transformAttrs) {
|
|||
additionalPanels.push({ name, toggle, transformAttrs });
|
||||
}
|
||||
|
||||
createWidget("revamped-hamburger-menu-wrapper", {
|
||||
buildAttributes() {
|
||||
return { "data-click-outside": true };
|
||||
},
|
||||
|
||||
html() {
|
||||
return [
|
||||
new RenderGlimmer(
|
||||
this,
|
||||
"div.widget-component-connector",
|
||||
hbs`<Sidebar::HamburgerDropdown />`
|
||||
),
|
||||
];
|
||||
},
|
||||
|
||||
clickOutside() {
|
||||
this.sendWidgetAction("toggleHamburger");
|
||||
},
|
||||
});
|
||||
|
||||
createWidget("revamped-user-menu-wrapper", {
|
||||
buildAttributes() {
|
||||
return { "data-click-outside": true };
|
||||
|
@ -380,6 +402,10 @@ export default createWidget("header", {
|
|||
inTopicRoute = this.router.currentRouteName.startsWith("topic.");
|
||||
}
|
||||
|
||||
if (attrs.sidebarDocked) {
|
||||
state.hamburgerVisible = false;
|
||||
}
|
||||
|
||||
let contents = () => {
|
||||
const headerIcons = this.attach("header-icons", {
|
||||
hamburgerVisible: state.hamburgerVisible,
|
||||
|
@ -387,6 +413,7 @@ export default createWidget("header", {
|
|||
searchVisible: state.searchVisible,
|
||||
ringBackdrop: state.ringBackdrop,
|
||||
flagCount: attrs.flagCount,
|
||||
sidebarDocked: attrs.sidebarDocked,
|
||||
user: this.currentUser,
|
||||
});
|
||||
|
||||
|
@ -403,7 +430,11 @@ export default createWidget("header", {
|
|||
})
|
||||
);
|
||||
} else if (state.hamburgerVisible) {
|
||||
panels.push(this.attach("hamburger-menu"));
|
||||
if (this.currentUser?.experimental_sidebar_enabled) {
|
||||
panels.push(this.attach("revamped-hamburger-menu-wrapper", {}));
|
||||
} else {
|
||||
panels.push(this.attach("hamburger-menu"));
|
||||
}
|
||||
} else if (state.userVisible) {
|
||||
if (this.currentUser.redesigned_user_menu_enabled) {
|
||||
panels.push(this.attach("revamped-user-menu-wrapper", {}));
|
||||
|
@ -433,7 +464,6 @@ export default createWidget("header", {
|
|||
const contentsAttrs = {
|
||||
contents,
|
||||
minimized: !!attrs.topic,
|
||||
sidebarEnabled: this.currentUser?.experimental_sidebar_enabled,
|
||||
};
|
||||
|
||||
return h(
|
||||
|
@ -516,13 +546,20 @@ export default createWidget("header", {
|
|||
},
|
||||
|
||||
toggleHamburger() {
|
||||
this.state.hamburgerVisible = !this.state.hamburgerVisible;
|
||||
this.toggleBodyScrolling(this.state.hamburgerVisible);
|
||||
if (
|
||||
this.currentUser?.experimental_sidebar_enabled &&
|
||||
this.site.mobileView
|
||||
) {
|
||||
this.sendWidgetAction("toggleSidebar");
|
||||
} else {
|
||||
this.state.hamburgerVisible = !this.state.hamburgerVisible;
|
||||
this.toggleBodyScrolling(this.state.hamburgerVisible);
|
||||
|
||||
// auto focus on first link in dropdown
|
||||
schedule("afterRender", () => {
|
||||
document.querySelector(".hamburger-panel .menu-links a")?.focus();
|
||||
});
|
||||
// auto focus on first link in dropdown
|
||||
schedule("afterRender", () => {
|
||||
document.querySelector(".hamburger-panel .menu-links a")?.focus();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
toggleBodyScrolling(bool) {
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
import { createWidget } from "discourse/widgets/widget";
|
||||
|
||||
export default createWidget("sidebar-toggle", {
|
||||
tagName: "span.header-sidebar-toggle",
|
||||
|
||||
html() {
|
||||
return [
|
||||
this.attach("button", {
|
||||
title: "",
|
||||
icon: "bars",
|
||||
action: "toggleSidebar",
|
||||
className: "btn btn-flat btn-sidebar-toggle",
|
||||
}),
|
||||
];
|
||||
},
|
||||
});
|
|
@ -416,9 +416,11 @@ export default class Widget {
|
|||
if (this.clickOutside) {
|
||||
properties["widget-click-outside"] = new WidgetClickOutsideHook(this);
|
||||
}
|
||||
|
||||
if (this.click) {
|
||||
properties["widget-click"] = new WidgetClickHook(this);
|
||||
}
|
||||
|
||||
if (this.doubleClick) {
|
||||
properties["widget-double-click"] = new WidgetDoubleClickHook(this);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,9 @@ import {
|
|||
query,
|
||||
updateCurrentUser,
|
||||
} from "discourse/tests/helpers/qunit-helpers";
|
||||
|
||||
import { undockSidebar } from "discourse/tests/helpers/sidebar-helpers";
|
||||
|
||||
import Site from "discourse/models/site";
|
||||
import discoveryFixture from "discourse/tests/fixtures/discovery-fixtures";
|
||||
import categoryFixture from "discourse/tests/fixtures/category-fixtures";
|
||||
|
@ -387,7 +390,7 @@ acceptance("Sidebar - Categories Section", function (needs) {
|
|||
);
|
||||
});
|
||||
|
||||
test("clean up topic tracking state state changed callbacks when section is destroyed", async function (assert) {
|
||||
test("clean up topic tracking state state changed callbacks when Sidebar is collapsed", async function (assert) {
|
||||
setupUserSidebarCategories();
|
||||
|
||||
await visit("/");
|
||||
|
@ -400,12 +403,11 @@ acceptance("Sidebar - Categories Section", function (needs) {
|
|||
topicTrackingState.stateChangeCallbacks
|
||||
).length;
|
||||
|
||||
await click(".header-sidebar-toggle .btn");
|
||||
await click(".header-sidebar-toggle .btn");
|
||||
await undockSidebar();
|
||||
|
||||
assert.strictEqual(
|
||||
Object.keys(topicTrackingState.stateChangeCallbacks).length,
|
||||
initialCallbackCount
|
||||
assert.ok(
|
||||
Object.keys(topicTrackingState.stateChangeCallbacks).length <
|
||||
initialCallbackCount
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -15,7 +15,12 @@ acceptance("Sidebar - Mobile - User with sidebar enabled", function (needs) {
|
|||
test("clicking outside sidebar collapses it", async function (assert) {
|
||||
await visit("/");
|
||||
|
||||
await click(".btn-sidebar-toggle");
|
||||
await click(".hamburger-dropdown");
|
||||
|
||||
assert.notOk(
|
||||
exists(".sidebar-footer-actions-dock-toggle"),
|
||||
"button to dock sidebar is not displayed"
|
||||
);
|
||||
|
||||
assert.ok(exists(".sidebar-container"), "sidebar is displayed");
|
||||
|
||||
|
@ -27,7 +32,7 @@ acceptance("Sidebar - Mobile - User with sidebar enabled", function (needs) {
|
|||
test("clicking on a link or button in sidebar collapses it", async function (assert) {
|
||||
await visit("/");
|
||||
|
||||
await click(".btn-sidebar-toggle");
|
||||
await click(".hamburger-dropdown");
|
||||
await click(".sidebar-section-link-tracked");
|
||||
|
||||
assert.ok(
|
||||
|
@ -35,7 +40,7 @@ acceptance("Sidebar - Mobile - User with sidebar enabled", function (needs) {
|
|||
"sidebar is collapsed when a button in sidebar is clicked"
|
||||
);
|
||||
|
||||
await click(".btn-sidebar-toggle");
|
||||
await click(".hamburger-dropdown");
|
||||
await click(".sidebar-section-header-link");
|
||||
|
||||
assert.ok(
|
||||
|
@ -47,7 +52,7 @@ acceptance("Sidebar - Mobile - User with sidebar enabled", function (needs) {
|
|||
test("collapsing sidebar sections does not collapse sidebar", async function (assert) {
|
||||
await visit("/");
|
||||
|
||||
await click(".btn-sidebar-toggle");
|
||||
await click(".hamburger-dropdown");
|
||||
await click(".sidebar-section-header-caret");
|
||||
|
||||
assert.ok(
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
import { resetSidebarSection } from "discourse/lib/sidebar/custom-sections";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
import { undockSidebar } from "discourse/tests/helpers/sidebar-helpers";
|
||||
|
||||
acceptance("Sidebar - Plugin API", function (needs) {
|
||||
needs.user({ experimental_sidebar_enabled: true });
|
||||
|
@ -338,7 +339,7 @@ acceptance("Sidebar - Plugin API", function (needs) {
|
|||
"displays hover button with correct title"
|
||||
);
|
||||
|
||||
await click(".header-sidebar-toggle button");
|
||||
await undockSidebar();
|
||||
|
||||
assert.strictEqual(
|
||||
linkDestroy,
|
||||
|
|
|
@ -9,6 +9,9 @@ import {
|
|||
query,
|
||||
updateCurrentUser,
|
||||
} from "discourse/tests/helpers/qunit-helpers";
|
||||
|
||||
import { undockSidebar } from "discourse/tests/helpers/sidebar-helpers";
|
||||
|
||||
import discoveryFixture from "discourse/tests/fixtures/discovery-fixtures";
|
||||
import { cloneJSON } from "discourse-common/lib/object";
|
||||
|
||||
|
@ -327,12 +330,11 @@ acceptance("Sidebar - Tags section", function (needs) {
|
|||
topicTrackingState.stateChangeCallbacks
|
||||
).length;
|
||||
|
||||
await click(".header-sidebar-toggle .btn");
|
||||
await click(".header-sidebar-toggle .btn");
|
||||
await undockSidebar();
|
||||
|
||||
assert.strictEqual(
|
||||
Object.keys(topicTrackingState.stateChangeCallbacks).length,
|
||||
initialCallbackCount
|
||||
assert.ok(
|
||||
Object.keys(topicTrackingState.stateChangeCallbacks).length <
|
||||
initialCallbackCount
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
import { test } from "qunit";
|
||||
import { click, visit } from "@ember/test-helpers";
|
||||
import { acceptance, exists } from "discourse/tests/helpers/qunit-helpers";
|
||||
import { click, currentRouteName, visit } from "@ember/test-helpers";
|
||||
import {
|
||||
acceptance,
|
||||
exists,
|
||||
updateCurrentUser,
|
||||
} from "discourse/tests/helpers/qunit-helpers";
|
||||
import { undockSidebar } from "discourse/tests/helpers/sidebar-helpers";
|
||||
|
||||
acceptance("Sidebar - Anon User", function () {
|
||||
// Don't show sidebar for anon user until we know what we want to display
|
||||
|
@ -34,7 +39,39 @@ acceptance("Sidebar - User with sidebar disabled", function (needs) {
|
|||
acceptance("Sidebar - User with sidebar enabled", function (needs) {
|
||||
needs.user({ experimental_sidebar_enabled: true });
|
||||
|
||||
test("hiding and displaying sidebar", async function (assert) {
|
||||
test("navigating to about route using sidebar", async function (assert) {
|
||||
await visit("/");
|
||||
await click(".sidebar-footer-link-about");
|
||||
|
||||
assert.strictEqual(currentRouteName(), "about");
|
||||
});
|
||||
|
||||
test("viewing keyboard shortcuts using sidebar", async function (assert) {
|
||||
await visit("/");
|
||||
await click(".sidebar-footer-actions-keyboard-shortcuts");
|
||||
|
||||
assert.ok(
|
||||
exists("#keyboard-shortcuts-help"),
|
||||
"keyboard shortcuts help is displayed"
|
||||
);
|
||||
});
|
||||
|
||||
test("navigating to site setting route using sidebar", async function (assert) {
|
||||
await visit("/");
|
||||
await click(".sidebar-footer-link-site-settings");
|
||||
|
||||
assert.strictEqual(currentRouteName(), "adminSiteSettingsCategory");
|
||||
});
|
||||
|
||||
test("site setting link is not shown in sidebar for non-admin user", async function (assert) {
|
||||
updateCurrentUser({ admin: false });
|
||||
|
||||
await visit("/");
|
||||
|
||||
assert.notOk(exists(".sidebar-footer-link-site-settings"));
|
||||
});
|
||||
|
||||
test("undocking and docking sidebar", async function (assert) {
|
||||
await visit("/");
|
||||
|
||||
assert.ok(
|
||||
|
@ -44,17 +81,27 @@ acceptance("Sidebar - User with sidebar enabled", function (needs) {
|
|||
|
||||
assert.ok(exists(".sidebar-container"), "displays the sidebar by default");
|
||||
|
||||
await click(".header-sidebar-toggle .btn");
|
||||
await undockSidebar();
|
||||
|
||||
assert.ok(
|
||||
!document.body.classList.contains("has-sidebar-page"),
|
||||
"removes sidebar utility class to body"
|
||||
"removes sidebar utility class from body"
|
||||
);
|
||||
|
||||
assert.ok(!exists(".sidebar-container"), "hides the sidebar");
|
||||
|
||||
await click(".header-sidebar-toggle .btn");
|
||||
await click(".hamburger-dropdown");
|
||||
|
||||
assert.ok(exists(".sidebar-container"), "displays the sidebar");
|
||||
assert.ok(
|
||||
exists(".sidebar-hamburger-dropdown"),
|
||||
"displays the sidebar in hamburger dropdown"
|
||||
);
|
||||
|
||||
await click("button.sidebar-footer-actions-dock-toggle");
|
||||
|
||||
assert.ok(
|
||||
exists(".sidebar-container"),
|
||||
"displays the sidebar after docking"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
publishToMessageBus,
|
||||
query,
|
||||
} from "discourse/tests/helpers/qunit-helpers";
|
||||
import { undockSidebar } from "discourse/tests/helpers/sidebar-helpers";
|
||||
import topicFixtures from "discourse/tests/fixtures/discovery-fixtures";
|
||||
import { cloneJSON } from "discourse-common/lib/object";
|
||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
|
@ -725,12 +726,11 @@ acceptance("Sidebar - Topics Section", function (needs) {
|
|||
topicTrackingState.stateChangeCallbacks
|
||||
).length;
|
||||
|
||||
await click(".header-sidebar-toggle .btn");
|
||||
await click(".header-sidebar-toggle .btn");
|
||||
await undockSidebar();
|
||||
|
||||
assert.strictEqual(
|
||||
Object.keys(topicTrackingState.stateChangeCallbacks).length,
|
||||
initialCallbackCount
|
||||
assert.ok(
|
||||
Object.keys(topicTrackingState.stateChangeCallbacks).length <
|
||||
initialCallbackCount
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
import { click } from "@ember/test-helpers";
|
||||
|
||||
export async function undockSidebar() {
|
||||
await click("button.sidebar-footer-actions-dock-toggle");
|
||||
}
|
|
@ -18,6 +18,7 @@
|
|||
--always-black-rgb: 0, 0, 0;
|
||||
--primary-rgb: #{hexToRGB($primary)};
|
||||
--primary-low-rgb: #{hexToRGB($primary-low)};
|
||||
--primary-very-low-rgb: #{hexToRGB($primary-very-low)};
|
||||
--secondary-rgb: #{hexToRGB($secondary)};
|
||||
--header_background-rgb: #{hexToRGB($header_background)};
|
||||
--tertiary-rgb: #{hexToRGB($tertiary)};
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
@import "share_link";
|
||||
@import "shared-drafts";
|
||||
@import "sidebar";
|
||||
@import "sidebar-footer";
|
||||
@import "sidebar-section";
|
||||
@import "sidebar-section-link";
|
||||
@import "tagging";
|
||||
|
|
|
@ -17,7 +17,10 @@
|
|||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
transition: padding-left var(--d-sidebar-animation-time)
|
||||
var(--d-sidebar-animation-ease) !important; // only works with an important... :/
|
||||
}
|
||||
.contents {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
|
@ -568,3 +568,33 @@ div.menu-links-header {
|
|||
color: var(--primary-med-or-secondary-med);
|
||||
}
|
||||
}
|
||||
|
||||
// Sidebar-hamburger hybrid
|
||||
|
||||
.hamburger-menu.revamped {
|
||||
.sidebar-section-wrapper {
|
||||
.sidebar-section-header-button {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.sidebar-section-link.active {
|
||||
font-weight: normal;
|
||||
color: var(--primary-high);
|
||||
background: var(--tertiary-low);
|
||||
}
|
||||
|
||||
.sidebar-section-header-link,
|
||||
.sidebar-section-header-button,
|
||||
.sidebar-section-link {
|
||||
&:hover {
|
||||
background: var(--highlight-medium);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-footer-actions-button.btn {
|
||||
&:hover {
|
||||
background: var(--highlight-medium);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
.sidebar-wrapper {
|
||||
.sidebar-footer-wrapper {
|
||||
.sidebar-footer-container {
|
||||
margin-left: 1.5em;
|
||||
margin-right: 0.15em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-footer-wrapper {
|
||||
border-top: 1.5px solid var(--primary-low);
|
||||
padding: 0.5em 0 0.5em 0.33em;
|
||||
|
||||
.sidebar-footer-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.sidebar-footer-link {
|
||||
display: inline-block;
|
||||
font-size: var(--font-down-1);
|
||||
color: var(--primary-high);
|
||||
padding: 0.25em 0;
|
||||
height: 100%;
|
||||
|
||||
&:not(:first-child):before {
|
||||
content: "•";
|
||||
padding: 0 0.25em;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-footer-actions {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.sidebar-footer-actions-button.btn {
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 0.25em 0.4em;
|
||||
|
||||
.d-icon {
|
||||
font-size: var(--font-down-1);
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--primary-low);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.has-sidebar-page {
|
||||
.sidebar-footer-actions-dock-toggle {
|
||||
.d-icon {
|
||||
transform: rotate(180deg);
|
||||
top: -0.1em; // optical alignment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-footer-wrapper {
|
||||
background: var(--primary-very-low);
|
||||
.desktop-view & {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
.sidebar-footer-container {
|
||||
position: relative;
|
||||
&:before {
|
||||
// fade to make scroll more apparent
|
||||
position: absolute;
|
||||
content: "";
|
||||
display: block;
|
||||
height: 1.5em;
|
||||
top: calc(-2em - 1px);
|
||||
left: -0.5em;
|
||||
right: -0.5em;
|
||||
pointer-events: none;
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
transparent 0%,
|
||||
rgba(var(--primary-very-low-rgb), 100%)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hamburger-menu.revamped {
|
||||
.sidebar-footer-wrapper {
|
||||
background: var(--secondary);
|
||||
.sidebar-footer-container {
|
||||
&:before {
|
||||
// fade to make scroll more apparent
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
transparent 0%,
|
||||
rgba(var(--secondary-rgb), 100%)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
align-items: center;
|
||||
|
||||
.sidebar-section-link {
|
||||
box-sizing: border-box;
|
||||
display: inline-flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
|
|
|
@ -49,9 +49,12 @@
|
|||
background: none;
|
||||
border: none;
|
||||
padding: 0.25em 0.5em;
|
||||
opacity: 0;
|
||||
transition: opacity 0.25s;
|
||||
transition-delay: 0.5s;
|
||||
|
||||
.d-icon {
|
||||
font-size: $font-down-1;
|
||||
font-size: var(--font-down-1);
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
|
||||
|
@ -92,6 +95,9 @@
|
|||
opacity: 0;
|
||||
transition: opacity 0.25s;
|
||||
transition-delay: 0.5s;
|
||||
.d-icon {
|
||||
top: -0.1em; // visual alignment
|
||||
}
|
||||
.discourse-no-touch & {
|
||||
&:hover {
|
||||
svg {
|
||||
|
|
|
@ -4,46 +4,6 @@
|
|||
--d-sidebar-animation-ease: ease-in-out;
|
||||
}
|
||||
|
||||
.header-sidebar-toggle {
|
||||
--toggle-padding: 0.5em;
|
||||
margin-right: 0.75em;
|
||||
// extending the toggle beyond the page when space allows
|
||||
// for better logo alignment with content
|
||||
@media screen and (min-width: 1380px) {
|
||||
margin-left: -3.5em;
|
||||
}
|
||||
|
||||
// align on icon, because button is transparent
|
||||
@media screen and (max-width: 1480px) {
|
||||
margin-left: calc(var(--toggle-padding) * -1.3);
|
||||
}
|
||||
|
||||
// prevents toggle overflow on smaller screens
|
||||
@media screen and (max-width: 1379px) {
|
||||
:not(.mobile-view) .has-sidebar-page & {
|
||||
margin-left: initial;
|
||||
}
|
||||
}
|
||||
|
||||
transition: margin var(--d-sidebar-animation-speed)
|
||||
var(--d-sidebar-animation-ease);
|
||||
|
||||
button {
|
||||
position: relative;
|
||||
font-size: var(--font-up-2);
|
||||
padding: var(--toggle-padding);
|
||||
|
||||
.discourse-no-touch & {
|
||||
&:hover {
|
||||
background: var(--primary-low);
|
||||
.d-icon {
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#main-outlet-wrapper {
|
||||
.sidebar-wrapper {
|
||||
grid-area: sidebar;
|
||||
|
@ -53,8 +13,8 @@
|
|||
align-self: start;
|
||||
overflow-y: auto;
|
||||
background-color: var(--primary-very-low);
|
||||
.discourse-touch &,
|
||||
&:hover {
|
||||
.sidebar-section-header-button,
|
||||
.sidebar-section-header-caret {
|
||||
opacity: 1;
|
||||
transition-delay: 0s;
|
||||
|
@ -63,10 +23,12 @@
|
|||
}
|
||||
|
||||
.sidebar-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
width: var(--d-sidebar-width);
|
||||
padding: 1em 0;
|
||||
padding: 1em 0 0;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
|
||||
|
@ -99,10 +61,13 @@
|
|||
.sidebar-scroll-wrap {
|
||||
// limit the wrapper width, so when the scrollbar is added the content doesn't shift
|
||||
max-width: calc(var(--d-sidebar-width) - var(--scrollbarWidth));
|
||||
box-sizing: border-box;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.sidebar-toggle {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
.sidebar-sections {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -193,7 +193,7 @@ body.has-sidebar-page {
|
|||
max-width: calc(var(--d-sidebar-width) + var(--d-max-width));
|
||||
}
|
||||
.d-header .wrap {
|
||||
padding-left: 0;
|
||||
padding-left: 1.85em;
|
||||
}
|
||||
#main-outlet-wrapper {
|
||||
grid-template-columns: var(--d-sidebar-width) minmax(0, 1fr);
|
||||
|
@ -202,16 +202,18 @@ body.has-sidebar-page {
|
|||
}
|
||||
}
|
||||
|
||||
body.sidebar-animate {
|
||||
#main-outlet-wrapper {
|
||||
// grid-template-columns transition supported in Firefox, Chrome support coming summer 2022
|
||||
transition-property: grid-template-columns, max-width;
|
||||
transition-timing-function: var(--d-sidebar-animation-ease);
|
||||
transition-duration: var(--d-sidebar-animation-time);
|
||||
}
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
body.sidebar-animate {
|
||||
#main-outlet-wrapper {
|
||||
// grid-template-columns transition supported in Firefox, Chrome support coming summer 2022
|
||||
transition-property: grid-template-columns, max-width;
|
||||
transition-timing-function: var(--d-sidebar-animation-ease);
|
||||
transition-duration: var(--d-sidebar-animation-time);
|
||||
}
|
||||
|
||||
.d-header-wrap .wrap {
|
||||
transition: max-width var(--d-sidebar-animation-time)
|
||||
var(--d-sidebar-animation-ease);
|
||||
.d-header-wrap .wrap {
|
||||
transition: max-width var(--d-sidebar-animation-time)
|
||||
var(--d-sidebar-animation-ease);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -143,15 +143,19 @@ blockquote {
|
|||
|
||||
// Sidebar styles
|
||||
|
||||
.sidebar-wrapper {
|
||||
width: 0;
|
||||
transition: width 0.2s ease-in-out;
|
||||
z-index: z("modal", "content");
|
||||
}
|
||||
|
||||
#main-outlet-wrapper {
|
||||
grid-template-columns: 0 minmax(0, 100vw);
|
||||
grid-template-columns: minmax(0, 100vw);
|
||||
grid-template-areas: "content";
|
||||
gap: 0;
|
||||
|
||||
.sidebar-wrapper {
|
||||
width: 0;
|
||||
transition: width 0.2s ease-in-out;
|
||||
z-index: z("modal", "content");
|
||||
grid-area: content;
|
||||
justify-self: end;
|
||||
}
|
||||
|
||||
.sidebar-container {
|
||||
padding-bottom: 6.6em; // extra space to watch out for navbar
|
||||
}
|
||||
|
@ -166,10 +170,11 @@ body.has-sidebar-page {
|
|||
z-index: z("modal", "content") + 1;
|
||||
}
|
||||
|
||||
.sidebar-wrapper {
|
||||
width: var(--d-sidebar-width);
|
||||
grid-area: content;
|
||||
margin-left: -10px; // compensate for main-outlet-wrapper padding
|
||||
#main-outlet-wrapper {
|
||||
.sidebar-wrapper {
|
||||
width: var(--d-sidebar-width);
|
||||
margin-right: -10px; // compensate for main-outlet-wrapper padding
|
||||
}
|
||||
}
|
||||
|
||||
#main-outlet {
|
||||
|
|
|
@ -10,3 +10,9 @@
|
|||
font-size: var(--font-0);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-wrapper .sidebar-footer-wrapper .sidebar-footer-container {
|
||||
margin-top: 0.5em;
|
||||
margin-right: 0;
|
||||
padding-left: 0.25em;
|
||||
}
|
||||
|
|
|
@ -3735,6 +3735,7 @@ en:
|
|||
shortcut_delimiter_slash: "%{shortcut1}/%{shortcut2}"
|
||||
shortcut_delimiter_space: "%{shortcut1} %{shortcut2}"
|
||||
title: "Keyboard Shortcuts"
|
||||
short_title: "Shortcuts"
|
||||
jump_to:
|
||||
title: "Jump To"
|
||||
home: "%{shortcut} Home"
|
||||
|
|
|
@ -141,6 +141,7 @@ module SvgSprite
|
|||
"info-circle",
|
||||
"italic",
|
||||
"key",
|
||||
"keyboard",
|
||||
"layer-group",
|
||||
"link",
|
||||
"list",
|
||||
|
|
Loading…
Reference in New Issue