UX: Revamp styling of sidebar

The following changes are made in this commit:

1. Move caret icon in sidebar section header to the right.
1. Each row in sidebar takes the full width which enables us to do a
full width highlight on hover and when sidebar link is active.
1. Ensure each row in Sidebar is of the same height.

Internal refs: /t/70546, /t/72196, /t/71820
This commit is contained in:
Alan Guo Xiang Tan 2022-08-18 15:03:28 +08:00
parent 44c53cb3bf
commit 5990842dd9
28 changed files with 166 additions and 278 deletions

View File

@ -1,8 +1,6 @@
<Sidebar::Section
@sectionName="categories"
@headerRoute="discovery.categories"
@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"
@collapsable={{@collapsable}} >

View File

@ -1,9 +1,6 @@
<Sidebar::Section
@sectionName="community"
@headerRoute="discovery.latest"
@headerQuery={{hash f=undefined}}
@headerLinkText={{i18n "sidebar.sections.community.header_link_text"}}
@headerLinkTitle={{i18n "sidebar.sections.community.header_link_title"}}
@headerActionsIcon="plus"
@headerActions={{array (hash action=this.composeTopic title=(i18n "sidebar.sections.community.header_action_title"))}}
@collapsable={{@collapsable}} >

View File

@ -1,12 +1,9 @@
<Sidebar::Section
@sectionName="messages"
@headerRoute="userPrivateMessages.index"
@headerModel={{this.currentUser}}
@headerActionIcon="plus"
@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"}}
@collapsable={{@collapsable}}>
{{#each this.personalMessagesSectionLinks as |personalMessageSectionLink|}}

View File

@ -3,28 +3,30 @@
{{/if}}
<details class="sidebar-more-section-links-details" {{on "toggle" this.toggleSectionLinks}}>
<summary class="sidebar-more-section-links-details-summary" >
<summary class="sidebar-more-section-links-details-summary sidebar-row" >
{{i18n "sidebar.more"}}
</summary>
{{#if this.shouldDisplaySectionLinks}}
<div class="sidebar-more-section-links-details-content"
<div class="sidebar-more-section-links-details-content-wrapper"
{{did-insert this.registerClickListener}}
{{will-destroy this.unregisterClickListener}} >
<div class="sidebar-more-section-links-details-content-main">
{{#each this.sectionLinks as |sectionLink|}}
<Sidebar::MoreSectionLink @sectionLink={{sectionLink}} />
{{/each}}
</div>
{{#if (gt this.secondarySectionLinks.length 0)}}
<div class="sidebar-more-section-links-details-content-secondary">
{{#each this.secondarySectionLinks as |sectionLink|}}
<div class="sidebar-more-section-link-details-content">
<div class="sidebar-more-section-links-details-content-main">
{{#each this.sectionLinks as |sectionLink|}}
<Sidebar::MoreSectionLink @sectionLink={{sectionLink}} />
{{/each}}
</div>
{{/if}}
{{#if (gt this.secondarySectionLinks.length 0)}}
<div class="sidebar-more-section-links-details-content-secondary">
{{#each this.secondarySectionLinks as |sectionLink|}}
<Sidebar::MoreSectionLink @sectionLink={{sectionLink}} />
{{/each}}
</div>
{{/if}}
</div>
</div>
{{/if}}
</details>

View File

@ -0,0 +1,14 @@
{{#if @collapsable}}
<span
role="button"
title={{i18n "sidebar.toggle_section"}}
class="sidebar-section-header sidebar-section-header-collapsable"
{{on "click" @toggleSectionDisplay}}
>
{{yield}}
</span>
{{else}}
<span class="sidebar-section-header" title={{i18n "sidebar.toggle_section"}}>
{{yield}}
</span>
{{/if}}

View File

@ -0,0 +1,3 @@
import templateOnly from "@ember/component/template-only";
export default templateOnly();

View File

@ -51,21 +51,21 @@
{{/if}}
</span>
{{/if}}
</Sidebar::SectionLinkTo>
{{#if @hoverValue}}
<span class="sidebar-section-link-hover">
<button
type="button"
title={{@hoverTitle}}
class="sidebar-section-hover-button"
{{on "click" @hoverAction}}
>
{{#if (eq @hoverType "icon")}}
{{d-icon @hoverValue class="hover-icon"}}
{{/if}}
</button>
</span>
{{/if}}
{{#if @hoverValue}}
<span class="sidebar-section-link-hover">
<button
type="button"
title={{@hoverTitle}}
class="sidebar-section-hover-button"
{{on "click" @hoverAction}}
>
{{#if (eq @hoverType "icon")}}
{{d-icon @hoverValue class="hover-icon"}}
{{/if}}
</button>
</span>
{{/if}}
</Sidebar::SectionLinkTo>
{{/if}}
</div>

View File

@ -12,6 +12,7 @@ export default class SectionLink extends Component {
let classNames = [
"sidebar-section-link",
`sidebar-section-link-${this.args.linkName}`,
"sidebar-row",
];
if (this.args.class) {

View File

@ -1,4 +1,4 @@
<div class="sidebar-section-message-wrapper">
<div class="sidebar-section-message-wrapper sidebar-row">
<div class="sidebar-section-message">
{{yield}}
</div>

View File

@ -1,39 +1,16 @@
<div class={{concat "sidebar-section-wrapper sidebar-section-" @sectionName}}>
<div class="sidebar-section-header">
{{#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
@route={{@headerRoute}}
@query={{@headerQuery}}
@models={{if
@headerModel
(array @headerModel)
(if @headerModels @headerModels (array))
}}
class="sidebar-section-header-link"
title={{@headerLinkTitle}}
>
{{@headerLinkText}}
</LinkTo>
{{else}}
<span
title={{@headerLinkTitle}}
class="sidebar-section-header-text"
>
<div class="sidebar-section-header-wrapper sidebar-row">
<Sidebar::SectionHeader @collapsable={{@collapsable}} @toggleSectionDisplay={{this.toggleSectionDisplay}}>
<span class="sidebar-section-header-text">
{{@headerLinkText}}
</span>
{{/if}}
{{#if @collapsable}}
<span class="sidebar-section-header-caret">
{{d-icon this.headerCaretIcon}}
</span>
{{/if}}
</Sidebar::SectionHeader>
{{#if this.isSingleHeaderAction}}
{{#each @headerActions as |headerAction|}}

View File

@ -13,7 +13,6 @@
{{#each this.customSections as |customSection|}}
<Sidebar::Section
@sectionName={{customSection.name}}
@headerRoute={{customSection.route}}
@headerLinkText={{customSection.text}}
@headerLinkTitle={{customSection.title}}
@headerActionsIcon={{customSection.actionsIcon}}

View File

@ -1,8 +1,6 @@
<Sidebar::Section
@sectionName="tags"
@headerRoute="tags"
@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"
@collapsable={{@collapsable}}>

View File

@ -13,18 +13,6 @@ export default class BaseCustomSidebarSection {
this._notImplemented();
}
/**
* @returns {string} The Ember route which the section header link should link to.
*/
get route() {}
/**
* @returns {string} Title for the header
*/
get title() {
this._notImplemented();
}
/**
* @returns {string} Text for the header
*/

View File

@ -96,12 +96,11 @@ acceptance("Sidebar - Categories Section", function (needs) {
test("clicking on section header link", async function (assert) {
await visit("/t/280");
await click(".sidebar-section-categories .sidebar-section-header-link");
await click(".sidebar-section-categories .sidebar-section-header");
assert.strictEqual(
currentURL(),
"/categories",
"it should transition to the categories page"
assert.notOk(
exists(".sidebar-section-categories .sidebar-section-content"),
"hides the content of the section"
);
});

View File

@ -67,8 +67,8 @@ acceptance("Sidebar - Community Section", function (needs) {
);
});
test("clicking on section caret button", async function (assert) {
await visit("/");
test("clicking on section header link", async function (assert) {
await visit("/t/280");
assert.ok(
exists(".sidebar-section-community .sidebar-section-content"),
@ -76,19 +76,19 @@ acceptance("Sidebar - Community Section", function (needs) {
);
assert.strictEqual(
query(".sidebar-section-community .sidebar-section-header-caret").title,
query(".sidebar-section-community .sidebar-section-header").title,
I18n.t("sidebar.toggle_section"),
"caret has the right title"
);
await click(".sidebar-section-community .sidebar-section-header-caret");
await click(".sidebar-section-community .sidebar-section-header");
assert.ok(
!exists(".sidebar-section-community .sidebar-section-content"),
"hides content section"
assert.notOk(
exists(".sidebar-section-community .sidebar-section-content"),
"hides the content of the section"
);
await click(".sidebar-section-community .sidebar-section-header-caret");
await click(".sidebar-section-community .sidebar-section-header");
assert.ok(
exists(".sidebar-section-community .sidebar-section-content"),
@ -96,30 +96,6 @@ acceptance("Sidebar - Community Section", function (needs) {
);
});
test("clicking on section header link", async function (assert) {
await visit("/t/280");
await click(".sidebar-section-community .sidebar-section-header-link");
assert.strictEqual(
currentURL(),
"/latest",
"it should transition to the homepage"
);
assert.strictEqual(
count(".sidebar-section-community .sidebar-section-link.active"),
1,
"only one link is marked as active"
);
assert.ok(
exists(
".sidebar-section-community .sidebar-section-link-everything.active"
),
"the everything link is marked as active"
);
});
// TODO(tgxworld): Flaky probably due to assertions running before event listener callbacks have completed.
skip("clicking on more... link", async function (assert) {
await visit("/");

View File

@ -82,12 +82,11 @@ acceptance(
test("clicking on section header link", async function (assert) {
await visit("/");
await click(".sidebar-section-messages .sidebar-section-header-link");
await click(".sidebar-section-messages .sidebar-section-header");
assert.strictEqual(
currentURL(),
`/u/eviltrout/messages`,
"it should transition to the user's messages"
assert.notOk(
exists(".sidebar-section-messages .sidebar-section-content"),
"hides the content of the section"
);
});

View File

@ -48,7 +48,7 @@ acceptance("Sidebar - Mobile - User with sidebar enabled", function (needs) {
);
await click(".hamburger-dropdown");
await click(".sidebar-section-header-link");
await click(".sidebar-section-community .sidebar-section-link-everything");
assert.ok(
!exists(".sidebar-hamburger-dropdown"),

View File

@ -37,14 +37,6 @@ acceptance("Sidebar - Plugin API", function (needs) {
return "test-chat-channels";
}
get route() {
return "discovery.latest";
}
get title() {
return "chat channels title";
}
get text() {
return "chat channels text";
}
@ -208,16 +200,9 @@ acceptance("Sidebar - Plugin API", function (needs) {
await visit("/");
assert.strictEqual(
query(".sidebar-section-test-chat-channels .sidebar-section-header-link")
.title,
"chat channels title",
"displays header with correct title attribute"
);
assert.strictEqual(
query(
".sidebar-section-test-chat-channels .sidebar-section-header-link"
".sidebar-section-test-chat-channels .sidebar-section-header"
).textContent.trim(),
"chat channels text",
"displays header with correct text"
@ -367,14 +352,6 @@ acceptance("Sidebar - Plugin API", function (needs) {
return "test-chat-channels";
}
get route() {
return "discovery.latest";
}
get title() {
return "chat channels title";
}
get text() {
return "chat channels text";
}
@ -404,7 +381,7 @@ acceptance("Sidebar - Plugin API", function (needs) {
assert.strictEqual(
query(
".sidebar-section-test-chat-channels .sidebar-section-header-link"
".sidebar-section-test-chat-channels .sidebar-section-header"
).textContent.trim(),
"chat channels text",
"displays header with correct text"

View File

@ -87,17 +87,6 @@ acceptance("Sidebar - Tags section", function (needs) {
});
});
test("clicking on section header link", async function (assert) {
await visit("/");
await click(".sidebar-section-tags .sidebar-section-header-link");
assert.strictEqual(
currentURL(),
"/tags",
"it should transition to the tags page"
);
});
test("clicking on section header button", async function (assert) {
await visit("/");
await click(".sidebar-section-tags .sidebar-section-header-button");

View File

@ -16,7 +16,7 @@ module("Integration | Component | sidebar | section-link", function (hooks) {
assert.strictEqual(
query("a").className,
"sidebar-section-link sidebar-section-link-test ember-view",
"sidebar-section-link sidebar-section-link-test sidebar-row ember-view",
"has the right class attribute for the link"
);
});
@ -28,7 +28,7 @@ module("Integration | Component | sidebar | section-link", function (hooks) {
assert.strictEqual(
query("a").className,
"sidebar-section-link sidebar-section-link-test 123 abc ember-view",
"sidebar-section-link sidebar-section-link-test sidebar-row 123 abc ember-view",
"has the right class attribute for the link"
);
});

View File

@ -1,37 +1,42 @@
.sidebar-more-section-links-details {
&:focus,
&:hover {
background: var(--d-sidebar-highlight-color);
}
.sidebar-more-section-links-details-summary {
padding: 0.35em 0.5em;
color: var(--primary-high);
font-size: var(--font-down-1);
transition: background-color 0.25s;
display: flex;
align-items: center;
list-style: none;
box-sizing: border-box;
.d-icon {
margin-left: auto;
color: var(--primary-medium);
}
&:focus,
&:hover {
background: var(--d-sidebar-highlight-color);
}
&::before {
display: none;
}
}
.sidebar-more-section-links-details-content {
.sidebar-more-section-links-details-content-wrapper {
position: absolute;
background-color: var(--secondary);
width: 100%;
box-shadow: shadow("dropdown");
z-index: z("base") + 1;
z-index: z("modal", "content") + 1;
background: transparent;
}
.sidebar-section-link-wrapper {
margin-left: none;
.sidebar-more-section-link-details-content {
background-color: var(--secondary);
box-shadow: shadow("dropdown");
border-radius: 5px;
margin: 0 calc(var(--d-sidebar-row-horizontal-padding) * 2 / 3);
.sidebar-row {
padding: calc(var(--d-sidebar-row-horizontal-padding) / 3);
}
}
@ -39,9 +44,3 @@
border-top: 1.5px solid var(--primary-low);
}
}
#main-outlet-wrapper {
.sidebar-more-section-links-details {
margin-left: 1.5em;
}
}

View File

@ -7,22 +7,18 @@
display: inline-flex;
width: 100%;
align-items: center;
padding: 0.35em 0.5em;
color: var(--primary-high);
font-size: var(--font-down-1);
transition: background-color 0.25s;
&:focus,
&:hover {
background: var(--d-sidebar-highlight-color);
.sidebar-section-link-prefix.icon svg.prefix-badge {
background: var(--d-sidebar-highlight-color);
}
}
&.active {
color: var(--primary);
font-weight: bold;
background: var(--d-sidebar-highlight-color);
}
.sidebar-section-link-content-badge {
@ -36,7 +32,8 @@
}
.sidebar-section-link-suffix {
font-size: var(--font-down-1);
margin-left: 0.25rem;
font-size: var(--font-down-4);
}
.sidebar-section-link-content-text {
@ -67,7 +64,9 @@
.sidebar-section-link-group-messages-new,
.sidebar-section-link-group-messages-unread,
.sidebar-section-link-group-messages-archive {
margin-left: 0.5em;
.sidebar-section-link-content-text {
margin-left: 0.5em;
}
}
.sidebar-section-link-prefix {
@ -113,13 +112,9 @@
}
}
.sidebar-section-link-suffix.icon {
align-items: center;
display: flex;
margin-left: 0.5em;
}
.sidebar-section-link-hover {
margin-left: auto;
.sidebar-section-hover-button {
display: none;
color: var(--primary-medium);
@ -146,9 +141,3 @@
}
}
}
#main-outlet-wrapper
.sidebar-section-link-wrapper:not(.sidebar-more-section-links-details-content
.sidebar-section-link-wrapper) {
margin-left: 1.5em;
}

View File

@ -1,12 +1,12 @@
.sidebar-section-wrapper {
margin-bottom: 1em;
.sidebar-section-header {
.sidebar-section-header-wrapper {
display: flex;
text-transform: uppercase;
font-size: 1em;
font-weight: bold;
align-items: stretch;
box-sizing: border-box;
.select-kit {
.btn {
@ -21,29 +21,32 @@
color: var(--primary-medium);
margin-right: 0;
}
summary {
padding: 0.25em 0.5em;
}
}
}
.sidebar-section-header-link,
.sidebar-section-header-text {
.sidebar-section-header {
@include ellipsis;
flex: 1 1 auto;
font-size: var(--font-1);
color: var(--primary);
font-size: var(--font-down-1);
padding: 0.25em 0.5em;
align-items: center;
&.sidebar-section-header-collapsable {
cursor: pointer;
}
}
.sidebar-section-header-link {
&:visited {
color: var(--primary);
}
&:focus,
&:hover {
background: var(--d-sidebar-highlight-color);
}
.sidebar-section-header-text {
font-weight: bold;
margin-right: 0.25em;
}
.sidebar-section-header-caret {
font-size: var(--font-down-2);
}
.sidebar-section-header-button {
@ -77,54 +80,12 @@
}
.sidebar-section-message {
padding: 0.25em 0.5em;
font-size: var(--font-down-1);
color: var(--primary-high);
}
.sidebar-section-header-caret {
flex: 0 0 auto;
width: 1.5em;
display: flex;
justify-content: flex-end;
align-items: center;
border: none;
background: transparent;
padding: 0;
.d-icon {
top: -0.1em; // visual alignment
}
.discourse-no-touch & {
&:hover {
svg {
color: var(--primary-high);
}
}
}
svg {
display: block;
flex: 0 0 auto;
transition: color 0.25s;
}
.d-icon {
font-size: $font-down-1;
color: var(--primary-medium);
}
}
.sidebar-section-content {
hr {
margin: 0em 1.5em;
}
}
}
#main-outlet-wrapper .sidebar-section-wrapper {
.sidebar-section-message-wrapper {
margin-left: 1.5em;
}
}

View File

@ -4,20 +4,26 @@
--d-sidebar-animation-ease: ease-in-out;
}
#main-outlet-wrapper {
.sidebar-wrapper {
--d-sidebar-highlight-color: var(--primary-low);
.sidebar-wrapper {
--d-sidebar-highlight-color: var(--primary-low);
--d-sidebar-row-horizontal-padding: 1.5rem;
grid-area: sidebar;
position: sticky;
top: var(--header-offset);
.footer-nav-ipad & {
top: calc(var(--header-offset) + var(--footer-nav-height));
}
height: calc(100vh - var(--header-offset));
align-self: start;
overflow-y: auto;
background-color: var(--primary-very-low);
grid-area: sidebar;
position: sticky;
top: var(--header-offset);
.footer-nav-ipad & {
top: calc(var(--header-offset) + var(--footer-nav-height));
}
height: calc(100vh - var(--header-offset));
align-self: start;
overflow-y: auto;
background-color: var(--primary-very-low);
.sidebar-row {
padding: 0.25rem var(--d-sidebar-row-horizontal-padding);
height: 27px;
align-items: center;
font-size: var(--font-down-1);
}
.sidebar-container {
@ -44,6 +50,7 @@
border-radius: calc(var(--scrollbarWidth) / 2);
border: calc(var(--scrollbarWidth) / 4) solid var(--primary-very-low);
}
&:hover {
scrollbar-color: var(--scrollbarThumbBg) var(--scrollbarBg);
&::-webkit-scrollbar-thumb {
@ -51,6 +58,7 @@
}
transition-delay: 0s;
}
&::-webkit-scrollbar {
width: var(--scrollbarWidth);
}
@ -58,7 +66,7 @@
.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));
max-width: calc(var(--d-sidebar-width));
box-sizing: border-box;
flex: 1;
display: flex;

View File

@ -12,8 +12,16 @@
.hamburger-panel .revamped {
--d-sidebar-highlight-color: var(--highlight-medium);
--d-sidebar-row-horizontal-padding: 0.5rem;
width: var(--d-sidebar-width);
.sidebar-row {
padding: 0.25rem var(--d-sidebar-row-horizontal-padding);
height: 27px;
align-items: center;
font-size: var(--font-down-1);
}
.panel-body-content {
width: 100%;
min-width: 0; // prevent content blowing out width

View File

@ -67,3 +67,15 @@
height: 100%;
}
}
.hamburger-panel .revamped {
--d-sidebar-row-horizontal-padding: 0.5rem;
--d-sidebar-highlight-color: var(--primary-low);
.sidebar-row {
padding: 0.25rem var(--d-sidebar-row-horizontal-padding);
height: 27px;
align-items: center;
font-size: var(--font-down-1);
}
}

View File

@ -1,7 +1,8 @@
#main-outlet-wrapper {
.sidebar-section-header {
.sidebar-section-header-wrapper {
margin-bottom: 0.25em;
}
.sidebar-section-header-link {
font-size: var(--font-0);
}

View File

@ -4096,7 +4096,6 @@ en:
more: "More..."
sections:
messages:
header_link_title: "personal messages"
header_link_text: "Messages"
header_action_title: "create a personal message"
links:
@ -4110,17 +4109,14 @@ en:
tags:
none: "You have not added any tags."
click_to_get_started: "Click here to get started."
header_link_title: "all tags"
header_link_text: "Tags"
header_action_title: "edit your sidebar tags"
categories:
none: "You have not added any categories."
click_to_get_started: "Click here to get started."
header_link_title: "all categories"
header_link_text: "Categories"
header_action_title: "edit your sidebar categories"
community:
header_link_title: "home"
header_link_text: "Community"
header_action_title: "create a new topic"
links: