Skip to content

Commit 96c51c1

Browse files
committed
DEV: Improve header dropdown toggle on mobile view
1 parent ab180af commit 96c51c1

File tree

3 files changed

+96
-3
lines changed

3 files changed

+96
-3
lines changed

common/common.scss

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,19 @@
3838
}
3939
}
4040

41+
&.has-dropdown {
42+
&.is-open {
43+
.custom-header-link-caret svg {
44+
transform: rotate(0deg);
45+
}
46+
}
47+
&:not(.is-open) {
48+
.custom-header-dropdown {
49+
transform: scale(0);
50+
}
51+
}
52+
}
53+
4154
&:hover {
4255
color: $main_link_hover_color;
4356

javascripts/discourse/components/custom-header-link.gjs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
import { tracked } from "@glimmer/tracking";
12
import Component from "@ember/component";
23
import { fn } from "@ember/helper";
34
import { on } from "@ember/modifier";
45
import { action } from "@ember/object";
56
import { notEmpty } from "@ember/object/computed";
6-
import { inject as service } from "@ember/service";
7+
import { service } from "@ember/service";
8+
import { and, not } from "truth-helpers";
79
import concatClass from "discourse/helpers/concat-class";
810
import DiscourseURL from "discourse/lib/url";
911
import dIcon from "discourse-common/helpers/d-icon";
@@ -14,6 +16,8 @@ export default class CustomHeaderLink extends Component {
1416
@service site;
1517
@service currentUser;
1618

19+
@tracked dropdownOpen = true;
20+
1721
@notEmpty("dropdownLinks") hasDropdown;
1822

1923
get shouldDisplay() {
@@ -48,7 +52,11 @@ export default class CustomHeaderLink extends Component {
4852
}
4953

5054
get showCaret() {
51-
return settings.show_caret_icons && this.hasDropdown;
55+
return (
56+
settings.show_caret_icons &&
57+
this.hasDropdown &&
58+
(!this.site.mobileView || !this.item.url)
59+
);
5260
}
5361

5462
get dropdownLinks() {
@@ -64,7 +72,12 @@ export default class CustomHeaderLink extends Component {
6472
}
6573

6674
@action
67-
redirectToUrl(item) {
75+
toggleDropdown() {
76+
this.dropdownOpen = !this.dropdownOpen;
77+
}
78+
79+
@action
80+
redirectToUrl(item, event) {
6881
if (this.site.mobileView) {
6982
this.toggleHeaderLinks();
7083
}
@@ -85,9 +98,14 @@ export default class CustomHeaderLink extends Component {
8598
"custom-header-link"
8699
(if @item.url "with-url")
87100
(if this.hasDropdown "has-dropdown")
101+
(if this.dropdownOpen "is-open")
88102
}}
89103
title={{@item.title}}
90104
{{(if @item.url (modifier on "click" (fn this.redirectToUrl @item)))}}
105+
{{(if
106+
(and (not @item.url) this.site.mobileView this.hasDropdown)
107+
(modifier on "click" (fn this.toggleDropdown))
108+
)}}
91109
>
92110
<CustomIcon @icon={{@item.icon}} />
93111
<span class="custom-header-link-title">{{@item.title}}</span>

test/acceptance/dropdown-header-test.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { test } from "qunit";
33
import sinon from "sinon";
44
import {
55
acceptance,
6+
query,
67
queryAll,
78
visible,
89
} from "discourse/tests/helpers/qunit-helpers";
@@ -494,3 +495,64 @@ acceptance("Dropdown header - Redirect to URL", function (needs) {
494495
);
495496
});
496497
});
498+
499+
acceptance("Dropdown header - Mobile view", function (needs) {
500+
needs.mobileView();
501+
502+
needs.hooks.beforeEach(() => {
503+
settings.header_links = JSON.stringify(headerLinksSettingFixtures);
504+
settings.dropdown_links = JSON.stringify(dropdownLinksSettingFixtures);
505+
});
506+
507+
needs.hooks.afterEach(() => {
508+
settings.header_links = "[]";
509+
settings.dropdown_links = "[]";
510+
});
511+
512+
test("it toggles the header dropdown on click", async function (assert) {
513+
await visit("/");
514+
515+
const headerLinkElement = query(".custom-header-links button");
516+
await click(headerLinkElement);
517+
518+
assert.ok(
519+
visible(".top-level-links"),
520+
"The top header dropdown is visible"
521+
);
522+
523+
const linksElements = queryAll(".custom-header-link");
524+
const linkElement = linksElements[0];
525+
526+
assert.ok(
527+
linkElement.classList.contains("is-open"),
528+
"The header dropdown is visible initially"
529+
);
530+
531+
await click(linkElement);
532+
533+
assert.notOk(
534+
linkElement.classList.contains("is-open"),
535+
"The header dropdown is hidden after click"
536+
);
537+
});
538+
539+
test("it doesn't render the caret icon on header link with URL defined", async function (assert) {
540+
await visit("/");
541+
542+
const headerLinkElement = query(".custom-header-links button");
543+
await click(headerLinkElement);
544+
545+
const linksElements = queryAll(".custom-header-link");
546+
const caretElement = linksElements[1].querySelector(
547+
".custom-header-link-caret"
548+
);
549+
550+
assert.strictEqual(caretElement, null, `The caret wrapper is not rendered`);
551+
assert.notOk(
552+
caretElement
553+
?.querySelector("svg")
554+
?.classList.contains("d-icon-caret-down"),
555+
`The icon is not rendered`
556+
);
557+
});
558+
});

0 commit comments

Comments
 (0)