DEV: Add profile tab to the experimental user menu (#17982)
This commit adds the profile tab to the experimental user menu. We're adding it to the user menu because it contains links/buttons that are not available anywhere else. We may remove the tab again if we find better places for those links/buttons, but for now it'll stay. For more context on the experimental user menu, see https://github.com/discourse/discourse/pull/17379.
This commit is contained in:
parent
66376a6569
commit
67bb0d8a55
|
@ -11,37 +11,24 @@
|
|||
<div class="menu-tabs-container" role="tablist" aria-orientation="vertical" aria-label={{i18n "user_menu.sr_menu_tabs"}}>
|
||||
<div class="top-tabs tabs-list">
|
||||
{{#each this.topTabs as |tab|}}
|
||||
<button
|
||||
class={{concat "btn btn-flat btn-icon no-text" (if (eq tab.id this.currentTabId) " active")}}
|
||||
type="button"
|
||||
role="tab"
|
||||
id={{concat "user-menu-button-" tab.id}}
|
||||
tabindex={{if (eq tab.id this.currentTabId) "0" "-1"}}
|
||||
aria-selected={{if (eq tab.id this.currentTabId) "true" "false"}}
|
||||
aria-controls={{concat "quick-access-" tab.id}}
|
||||
data-tab-number={{tab.position}}
|
||||
{{on "click" (fn this.changeTab tab)}}
|
||||
{{!-- template-lint-disable require-context-role --}}
|
||||
<UserMenu::TabButton
|
||||
@tab={{tab}}
|
||||
@currentTabId={{this.currentTabId}}
|
||||
@changeTabFunction={{fn this.changeTab tab}}
|
||||
>
|
||||
{{d-icon tab.icon}}
|
||||
{{#if tab.count}}
|
||||
<span class="badge-notification">{{tab.count}}</span>
|
||||
{{/if}}
|
||||
</button>
|
||||
</UserMenu::TabButton>
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="bottom-tabs tabs-list">
|
||||
{{#each this.bottomTabs as |tab|}}
|
||||
<a
|
||||
class="btn btn-flat btn-icon no-text"
|
||||
role="tab"
|
||||
tabindex="-1"
|
||||
href={{tab.href}}
|
||||
data-tab-number={{tab.position}}
|
||||
{{!-- template-lint-disable require-context-role --}}
|
||||
>
|
||||
{{d-icon tab.icon}}
|
||||
</a>
|
||||
<UserMenu::TabButton
|
||||
@tab={{tab}}
|
||||
@currentTabId={{this.currentTabId}}
|
||||
@changeTabFunction={{fn this.changeTab tab}}
|
||||
/>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -136,6 +136,22 @@ const CORE_TOP_TABS = [
|
|||
},
|
||||
];
|
||||
|
||||
const CORE_BOTTOM_TABS = [
|
||||
class extends UserMenuTab {
|
||||
get id() {
|
||||
return "profile";
|
||||
}
|
||||
|
||||
get icon() {
|
||||
return "user";
|
||||
}
|
||||
|
||||
get panelComponent() {
|
||||
return "user-menu/profile-tab-content";
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
export default class UserMenu extends Component {
|
||||
@service currentUser;
|
||||
@service siteSettings;
|
||||
|
@ -185,8 +201,17 @@ export default class UserMenu extends Component {
|
|||
}
|
||||
|
||||
get _bottomTabs() {
|
||||
const tabs = [];
|
||||
|
||||
CORE_BOTTOM_TABS.forEach((tabClass) => {
|
||||
const tab = new tabClass(this.currentUser, this.siteSettings, this.site);
|
||||
if (tab.shouldDisplay) {
|
||||
tabs.push(tab);
|
||||
}
|
||||
});
|
||||
|
||||
const topTabsLength = this.topTabs.length;
|
||||
return this._coreBottomTabs.map((tab, index) => {
|
||||
return tabs.map((tab, index) => {
|
||||
tab.position = index + topTabsLength;
|
||||
return tab;
|
||||
});
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
<ul>
|
||||
<li class="summary">
|
||||
<LinkTo @route="user.summary" @model={{this.currentUser}}>
|
||||
{{d-icon "user"}}
|
||||
<span class="item-label">
|
||||
{{i18n "user.summary.title"}}
|
||||
</span>
|
||||
</LinkTo>
|
||||
</li>
|
||||
|
||||
<li class="activity">
|
||||
<LinkTo @route="userActivity" @model={{this.currentUser}}>
|
||||
{{d-icon "stream"}}
|
||||
<span class="item-label">
|
||||
{{i18n "user.activity_stream"}}
|
||||
</span>
|
||||
</LinkTo>
|
||||
</li>
|
||||
|
||||
{{#if this.currentUser.can_invite_to_forum}}
|
||||
<li class="invites">
|
||||
<LinkTo @route="userInvited" @model={{this.currentUser}}>
|
||||
{{d-icon "user-plus"}}
|
||||
<span class="item-label">
|
||||
{{i18n "user.invited.title"}}
|
||||
</span>
|
||||
</LinkTo>
|
||||
</li>
|
||||
{{/if}}
|
||||
|
||||
<li class="drafts">
|
||||
<LinkTo @route="userActivity.drafts" @model={{this.currentUser}}>
|
||||
{{d-icon "pencil-alt"}}
|
||||
<span class="item-label">
|
||||
{{#if this.currentUser.draft_count}}
|
||||
{{i18n "drafts.label_with_count" count=this.currentUser.draft_count}}
|
||||
{{else}}
|
||||
{{i18n "drafts.label"}}
|
||||
{{/if}}
|
||||
</span>
|
||||
</LinkTo>
|
||||
</li>
|
||||
|
||||
<li class="preferences">
|
||||
<LinkTo @route="preferences" @model={{this.currentUser}}>
|
||||
{{d-icon "cog"}}
|
||||
<span class="item-label">
|
||||
{{i18n "user.preferences"}}
|
||||
</span>
|
||||
</LinkTo>
|
||||
</li>
|
||||
|
||||
<li class="do-not-disturb">
|
||||
<DButton @class="btn-flat profile-tab-btn" @action={{this.doNotDisturbClick}}>
|
||||
{{d-icon (if this.isInDoNotDisturb "toggle-on" "toggle-off")}}
|
||||
<span class="item-label">
|
||||
{{#if this.isInDoNotDisturb}}
|
||||
<span>{{i18n "do_not_disturb.label"}}</span>
|
||||
<span
|
||||
title={{this.doNotDisturbDateTitle}}
|
||||
data-time={{this.doNotDisturbDateTime}}
|
||||
data-format="tiny"
|
||||
class="relative-date"
|
||||
>
|
||||
{{this.doNotDisturbDateContent}}
|
||||
</span>
|
||||
{{else}}
|
||||
{{i18n "do_not_disturb.label"}}
|
||||
{{/if}}
|
||||
</span>
|
||||
</DButton>
|
||||
</li>
|
||||
|
||||
{{#if this.showToggleAnonymousButton}}
|
||||
<li class={{if this.currentUser.is_anonymous "disable-anonymous" "enable-anonymous"}}>
|
||||
<DButton @class="btn-flat profile-tab-btn" @action={{route-action "toggleAnonymous"}}>
|
||||
{{#if this.currentUser.is_anonymous}}
|
||||
{{d-icon "ban"}}
|
||||
<span class="item-label">
|
||||
{{i18n "switch_from_anon"}}
|
||||
</span>
|
||||
{{else}}
|
||||
{{d-icon "user-secret"}}
|
||||
<span class="item-label">
|
||||
{{i18n "switch_to_anon"}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</DButton>
|
||||
</li>
|
||||
{{/if}}
|
||||
|
||||
<li class="logout">
|
||||
<DButton @class="btn-flat profile-tab-btn" @action={{route-action "logout"}}>
|
||||
{{d-icon "sign-out-alt"}}
|
||||
<span class="item-label">
|
||||
{{i18n "user.log_out"}}
|
||||
</span>
|
||||
</DButton>
|
||||
</li>
|
||||
</ul>
|
|
@ -0,0 +1,63 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { action } from "@ember/object";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
import { longDate, relativeAge } from "discourse/lib/formatter";
|
||||
|
||||
export default class UserMenuProfileTabContent extends Component {
|
||||
@service currentUser;
|
||||
@service siteSettings;
|
||||
saving = false;
|
||||
|
||||
get showToggleAnonymousButton() {
|
||||
return (
|
||||
(this.siteSettings.allow_anonymous_posting &&
|
||||
this.currentUser.trust_level >=
|
||||
this.siteSettings.anonymous_posting_min_trust_level) ||
|
||||
this.currentUser.is_anonymous
|
||||
);
|
||||
}
|
||||
|
||||
get isInDoNotDisturb() {
|
||||
return !!this.#doNotDisturbUntilDate;
|
||||
}
|
||||
|
||||
get doNotDisturbDateTitle() {
|
||||
return longDate(this.#doNotDisturbUntilDate);
|
||||
}
|
||||
|
||||
get doNotDisturbDateContent() {
|
||||
return relativeAge(this.#doNotDisturbUntilDate);
|
||||
}
|
||||
|
||||
get doNotDisturbDateTime() {
|
||||
return this.#doNotDisturbUntilDate.getTime();
|
||||
}
|
||||
|
||||
get #doNotDisturbUntilDate() {
|
||||
if (!this.currentUser.get("do_not_disturb_until")) {
|
||||
return;
|
||||
}
|
||||
const date = new Date(this.currentUser.get("do_not_disturb_until"));
|
||||
if (date < new Date()) {
|
||||
return;
|
||||
}
|
||||
return date;
|
||||
}
|
||||
|
||||
@action
|
||||
doNotDisturbClick() {
|
||||
if (this.saving) {
|
||||
return;
|
||||
}
|
||||
this.saving = true;
|
||||
if (this.currentUser.do_not_disturb_until) {
|
||||
return this.currentUser.leaveDoNotDisturb().finally(() => {
|
||||
this.saving = false;
|
||||
});
|
||||
} else {
|
||||
this.saving = false;
|
||||
showModal("do-not-disturb");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<button
|
||||
class={{concat "btn btn-flat btn-icon no-text" (if (eq @tab.id @currentTabId) " active")}}
|
||||
type="button"
|
||||
role="tab"
|
||||
id={{concat "user-menu-button-" @tab.id}}
|
||||
tabindex={{if (eq @tab.id @currentTabId) "0" "-1"}}
|
||||
aria-selected={{if (eq @tab.id @currentTabId) "true" "false"}}
|
||||
aria-controls={{concat "quick-access-" @tab.id}}
|
||||
data-tab-number={{@tab.position}}
|
||||
{{on "click" @changeTabFunction}}
|
||||
{{!-- template-lint-disable require-context-role --}}
|
||||
>
|
||||
{{d-icon @tab.icon}}
|
||||
{{yield}}
|
||||
</button>
|
|
@ -0,0 +1,3 @@
|
|||
import templateOnly from "@ember/component/template-only";
|
||||
|
||||
export default templateOnly();
|
|
@ -100,3 +100,113 @@ acceptance("Do not disturb", function (needs) {
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
acceptance("Do not disturb - new user menu", function (needs) {
|
||||
needs.user({ redesigned_user_menu_enabled: true });
|
||||
needs.pretender((server, helper) => {
|
||||
server.post("/do-not-disturb.json", () => {
|
||||
const now = new Date();
|
||||
now.setHours(now.getHours() + 1);
|
||||
return helper.response({ ends_at: now });
|
||||
});
|
||||
server.delete("/do-not-disturb.json", () =>
|
||||
helper.response({ success: true })
|
||||
);
|
||||
});
|
||||
|
||||
test("when turned off, it is turned on from modal", async function (assert) {
|
||||
updateCurrentUser({ do_not_disturb_until: null });
|
||||
|
||||
await visit("/");
|
||||
await click(".header-dropdown-toggle.current-user");
|
||||
await click("#user-menu-button-profile");
|
||||
await click("#quick-access-profile .do-not-disturb .btn");
|
||||
|
||||
assert.ok(exists(".do-not-disturb-modal"), "modal to choose time appears");
|
||||
|
||||
let tiles = queryAll(".do-not-disturb-tile");
|
||||
assert.ok(tiles.length === 4, "There are 4 duration choices");
|
||||
|
||||
await click(tiles[0]);
|
||||
|
||||
assert.ok(query(".do-not-disturb-modal.hidden"), "modal is hidden");
|
||||
|
||||
assert.ok(
|
||||
exists(".header-dropdown-toggle .do-not-disturb-background .d-icon-moon"),
|
||||
"moon icon is present in header"
|
||||
);
|
||||
});
|
||||
|
||||
test("Can be invoked via keyboard", async function (assert) {
|
||||
updateCurrentUser({ do_not_disturb_until: null });
|
||||
|
||||
await visit("/");
|
||||
await click(".header-dropdown-toggle.current-user");
|
||||
await click("#user-menu-button-profile");
|
||||
await click("#quick-access-profile .do-not-disturb .btn");
|
||||
|
||||
assert.ok(exists(".do-not-disturb-modal"), "DND modal is displayed");
|
||||
|
||||
assert.strictEqual(
|
||||
count(".do-not-disturb-tile"),
|
||||
4,
|
||||
"There are 4 duration choices"
|
||||
);
|
||||
|
||||
await triggerKeyEvent(
|
||||
".do-not-disturb-tile:nth-child(1)",
|
||||
"keydown",
|
||||
"Enter"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
query(".do-not-disturb-modal.hidden"),
|
||||
"DND modal is hidden after making a choice"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
exists(".header-dropdown-toggle .do-not-disturb-background .d-icon-moon"),
|
||||
"moon icon is shown in header avatar"
|
||||
);
|
||||
});
|
||||
|
||||
test("when turned on, it can be turned off", async function (assert) {
|
||||
const now = new Date();
|
||||
now.setHours(now.getHours() + 1);
|
||||
updateCurrentUser({ do_not_disturb_until: now });
|
||||
|
||||
await visit("/");
|
||||
|
||||
assert.ok(
|
||||
exists(".do-not-disturb-background"),
|
||||
"The active moon icon is shown"
|
||||
);
|
||||
|
||||
await click(".header-dropdown-toggle.current-user");
|
||||
await click("#user-menu-button-profile");
|
||||
assert.strictEqual(
|
||||
query(".do-not-disturb .relative-date").textContent.trim(),
|
||||
"1h",
|
||||
"the Do Not Disturb button shows how much time is left for DND mode"
|
||||
);
|
||||
assert.ok(
|
||||
exists(".do-not-disturb .d-icon-toggle-on"),
|
||||
"the Do Not Disturb button has the toggle-on icon"
|
||||
);
|
||||
|
||||
await click("#quick-access-profile .do-not-disturb .btn");
|
||||
|
||||
assert.notOk(
|
||||
exists(".do-not-disturb-background"),
|
||||
"The active moon icons are removed"
|
||||
);
|
||||
assert.notOk(
|
||||
exists(".do-not-disturb .relative-date"),
|
||||
"the text showing how much time is left for DND mode is gone"
|
||||
);
|
||||
assert.ok(
|
||||
exists(".do-not-disturb .d-icon-toggle-off"),
|
||||
"the Do Not Disturb button has the toggle-off icon"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
publishToMessageBus,
|
||||
query,
|
||||
queryAll,
|
||||
updateCurrentUser,
|
||||
} from "discourse/tests/helpers/qunit-helpers";
|
||||
import { test } from "qunit";
|
||||
import { cloneJSON } from "discourse-common/lib/object";
|
||||
|
@ -19,7 +20,14 @@ acceptance("User menu", function (needs) {
|
|||
needs.user({
|
||||
redesigned_user_menu_enabled: true,
|
||||
unread_high_priority_notifications: 73,
|
||||
trust_level: 3,
|
||||
});
|
||||
|
||||
needs.settings({
|
||||
allow_anonymous_posting: true,
|
||||
anonymous_posting_min_trust_level: 3,
|
||||
});
|
||||
|
||||
let requestHeaders = {};
|
||||
|
||||
needs.pretender((server, helper) => {
|
||||
|
@ -178,6 +186,238 @@ acceptance("User menu", function (needs) {
|
|||
"the tab's content is now displayed in the panel"
|
||||
);
|
||||
});
|
||||
|
||||
test("the profile tab", async function (assert) {
|
||||
updateCurrentUser({ draft_count: 13 });
|
||||
await visit("/");
|
||||
await click(".d-header-icons .current-user");
|
||||
await click("#user-menu-button-profile");
|
||||
|
||||
const summaryLink = query("#quick-access-profile ul li.summary a");
|
||||
assert.ok(
|
||||
summaryLink.href.endsWith("/u/eviltrout/summary"),
|
||||
"has a link to the summary page of the user"
|
||||
);
|
||||
assert.strictEqual(
|
||||
summaryLink.textContent.trim(),
|
||||
I18n.t("user.summary.title"),
|
||||
"summary link has the right label"
|
||||
);
|
||||
assert.ok(
|
||||
summaryLink.querySelector(".d-icon-user"),
|
||||
"summary link has the right icon"
|
||||
);
|
||||
|
||||
const activityLink = query("#quick-access-profile ul li.activity a");
|
||||
assert.ok(
|
||||
activityLink.href.endsWith("/u/eviltrout/activity"),
|
||||
"has a link to the activity page of the user"
|
||||
);
|
||||
assert.strictEqual(
|
||||
activityLink.textContent.trim(),
|
||||
I18n.t("user.activity_stream"),
|
||||
"activity link has the right label"
|
||||
);
|
||||
assert.ok(
|
||||
activityLink.querySelector(".d-icon-stream"),
|
||||
"activity link has the right icon"
|
||||
);
|
||||
|
||||
const invitesLink = query("#quick-access-profile ul li.invites a");
|
||||
assert.ok(
|
||||
invitesLink.href.endsWith("/u/eviltrout/invited"),
|
||||
"has a link to the invites page of the user"
|
||||
);
|
||||
assert.strictEqual(
|
||||
invitesLink.textContent.trim(),
|
||||
I18n.t("user.invited.title"),
|
||||
"invites link has the right label"
|
||||
);
|
||||
assert.ok(
|
||||
invitesLink.querySelector(".d-icon-user-plus"),
|
||||
"invites link has the right icon"
|
||||
);
|
||||
|
||||
await click("header.d-header"); // close the menu
|
||||
updateCurrentUser({ can_invite_to_forum: false });
|
||||
await click(".d-header-icons .current-user");
|
||||
await click("#user-menu-button-profile");
|
||||
|
||||
assert.notOk(
|
||||
exists("#quick-access-profile ul li.invites"),
|
||||
"invites link not shown when the user can't invite"
|
||||
);
|
||||
|
||||
const dratsLink = query("#quick-access-profile ul li.drafts a");
|
||||
assert.ok(
|
||||
dratsLink.href.endsWith("/u/eviltrout/activity/drafts"),
|
||||
"has a link to the drafts page of the user"
|
||||
);
|
||||
assert.strictEqual(
|
||||
dratsLink.textContent.trim(),
|
||||
I18n.t("drafts.label_with_count", { count: 13 }),
|
||||
"drafts link has the right label with count of the user's drafts"
|
||||
);
|
||||
assert.ok(
|
||||
dratsLink.querySelector(".d-icon-pencil-alt"),
|
||||
"drafts link has the right icon"
|
||||
);
|
||||
|
||||
const preferencesLink = query("#quick-access-profile ul li.preferences a");
|
||||
assert.ok(
|
||||
preferencesLink.href.endsWith("/u/eviltrout/preferences"),
|
||||
"has a link to the preferences page of the user"
|
||||
);
|
||||
assert.strictEqual(
|
||||
preferencesLink.textContent.trim(),
|
||||
I18n.t("user.preferences"),
|
||||
"preferences link has the right label"
|
||||
);
|
||||
assert.ok(
|
||||
preferencesLink.querySelector(".d-icon-cog"),
|
||||
"preferences link has the right icon"
|
||||
);
|
||||
|
||||
let doNotDisturbButton = query(
|
||||
"#quick-access-profile ul li.do-not-disturb .btn"
|
||||
);
|
||||
assert.strictEqual(
|
||||
doNotDisturbButton.textContent
|
||||
.replaceAll(/\s+/g, " ")
|
||||
.replaceAll(/\u200B/g, "")
|
||||
.trim(),
|
||||
I18n.t("do_not_disturb.label"),
|
||||
"Do Not Disturb button has the right label"
|
||||
);
|
||||
assert.ok(
|
||||
doNotDisturbButton.querySelector(".d-icon-toggle-off"),
|
||||
"Do Not Disturb button has the right icon"
|
||||
);
|
||||
|
||||
await click("header.d-header"); // close the menu
|
||||
const date = new Date();
|
||||
date.setHours(date.getHours() + 2);
|
||||
updateCurrentUser({ do_not_disturb_until: date.toISOString() });
|
||||
await click(".d-header-icons .current-user");
|
||||
await click("#user-menu-button-profile");
|
||||
|
||||
doNotDisturbButton = query(
|
||||
"#quick-access-profile ul li.do-not-disturb .btn"
|
||||
);
|
||||
assert.strictEqual(
|
||||
doNotDisturbButton.textContent
|
||||
.replaceAll(/\s+/g, " ")
|
||||
.replaceAll(/\u200B/g, "")
|
||||
.trim(),
|
||||
`${I18n.t("do_not_disturb.label")} 2h`,
|
||||
"Do Not Disturb button has the right label when Do Not Disturb is enabled"
|
||||
);
|
||||
assert.ok(
|
||||
doNotDisturbButton.querySelector(".d-icon-toggle-on"),
|
||||
"Do Not Disturb button has the right icon when Do Not Disturb is enabled"
|
||||
);
|
||||
|
||||
let toggleAnonButton = query(
|
||||
"#quick-access-profile ul li.enable-anonymous .btn"
|
||||
);
|
||||
assert.strictEqual(
|
||||
toggleAnonButton.textContent
|
||||
.replaceAll(/\s+/g, " ")
|
||||
.replaceAll(/\u200B/g, "")
|
||||
.trim(),
|
||||
I18n.t("switch_to_anon"),
|
||||
"toggle anonymous button has the right label when the user isn't anonymous"
|
||||
);
|
||||
assert.ok(
|
||||
toggleAnonButton.querySelector(".d-icon-user-secret"),
|
||||
"toggle anonymous button has the right icon when the user isn't anonymous"
|
||||
);
|
||||
|
||||
await click("header.d-header"); // close the menu
|
||||
updateCurrentUser({ is_anonymous: true });
|
||||
await click(".d-header-icons .current-user");
|
||||
await click("#user-menu-button-profile");
|
||||
|
||||
toggleAnonButton = query(
|
||||
"#quick-access-profile ul li.disable-anonymous .btn"
|
||||
);
|
||||
assert.strictEqual(
|
||||
toggleAnonButton.textContent
|
||||
.replaceAll(/\s+/g, " ")
|
||||
.replaceAll(/\u200B/g, "")
|
||||
.trim(),
|
||||
I18n.t("switch_from_anon"),
|
||||
"toggle anonymous button has the right label when the user is anonymous"
|
||||
);
|
||||
assert.ok(
|
||||
toggleAnonButton.querySelector(".d-icon-ban"),
|
||||
"toggle anonymous button has the right icon when the user is anonymous"
|
||||
);
|
||||
|
||||
await click("header.d-header"); // close the menu
|
||||
updateCurrentUser({ is_anonymous: false, trust_level: 2 });
|
||||
await click(".d-header-icons .current-user");
|
||||
await click("#user-menu-button-profile");
|
||||
|
||||
assert.notOk(
|
||||
exists("#quick-access-profile ul li.enable-anonymous"),
|
||||
"toggle anon button isn't shown when the user can't use it"
|
||||
);
|
||||
assert.notOk(
|
||||
exists("#quick-access-profile ul li.disable-anonymous"),
|
||||
"toggle anon button isn't shown when the user can't use it"
|
||||
);
|
||||
|
||||
await click("header.d-header"); // close the menu
|
||||
updateCurrentUser({ is_anonymous: true, trust_level: 2 });
|
||||
this.siteSettings.allow_anonymous_posting = false;
|
||||
this.siteSettings.anonymous_posting_min_trust_level = 3;
|
||||
await click(".d-header-icons .current-user");
|
||||
await click("#user-menu-button-profile");
|
||||
|
||||
assert.ok(
|
||||
exists("#quick-access-profile ul li.disable-anonymous"),
|
||||
"toggle anon button is always shown if the user is anonymous"
|
||||
);
|
||||
|
||||
await click("header.d-header"); // close the menu
|
||||
updateCurrentUser({ is_anonymous: false, trust_level: 4 });
|
||||
this.siteSettings.allow_anonymous_posting = false;
|
||||
this.siteSettings.anonymous_posting_min_trust_level = 3;
|
||||
await click(".d-header-icons .current-user");
|
||||
await click("#user-menu-button-profile");
|
||||
|
||||
assert.notOk(
|
||||
exists("#quick-access-profile ul li.enable-anonymous"),
|
||||
"toggle anon button is not shown if the allow_anonymous_posting setting is false"
|
||||
);
|
||||
|
||||
await click("header.d-header"); // close the menu
|
||||
updateCurrentUser({ is_anonymous: false, trust_level: 2 });
|
||||
this.siteSettings.allow_anonymous_posting = true;
|
||||
this.siteSettings.anonymous_posting_min_trust_level = 3;
|
||||
await click(".d-header-icons .current-user");
|
||||
await click("#user-menu-button-profile");
|
||||
|
||||
assert.notOk(
|
||||
exists("#quick-access-profile ul li.enable-anonymous"),
|
||||
"toggle anon button is not shown if the user doesn't have a high enough trust level"
|
||||
);
|
||||
|
||||
const logoutButton = query("#quick-access-profile ul li.logout .btn");
|
||||
assert.strictEqual(
|
||||
logoutButton.textContent
|
||||
.replaceAll(/\s+/g, " ")
|
||||
.replaceAll(/\u200B/g, "")
|
||||
.trim(),
|
||||
I18n.t("user.log_out"),
|
||||
"logout button has the right label"
|
||||
);
|
||||
assert.ok(
|
||||
logoutButton.querySelector(".d-icon-sign-out-alt"),
|
||||
"logout button has the right icon"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
acceptance("User menu - Dismiss button", function (needs) {
|
||||
|
|
|
@ -164,8 +164,9 @@ module("Integration | Component | site-header", function (hooks) {
|
|||
await triggerKeyEvent(document, "keydown", "ArrowDown");
|
||||
|
||||
focusedTab = document.activeElement;
|
||||
assert.ok(
|
||||
focusedTab.href.endsWith("/u/eviltrout/preferences"),
|
||||
assert.strictEqual(
|
||||
focusedTab.id,
|
||||
"user-menu-button-profile",
|
||||
"the down arrow key can move the focus to the bottom tabs"
|
||||
);
|
||||
|
||||
|
@ -179,8 +180,9 @@ module("Integration | Component | site-header", function (hooks) {
|
|||
|
||||
await triggerKeyEvent(document, "keydown", "ArrowUp");
|
||||
focusedTab = document.activeElement;
|
||||
assert.ok(
|
||||
focusedTab.href.endsWith("/u/eviltrout/preferences"),
|
||||
assert.strictEqual(
|
||||
focusedTab.id,
|
||||
"user-menu-button-profile",
|
||||
"the up arrow key moves the focus in the opposite direction"
|
||||
);
|
||||
});
|
||||
|
|
|
@ -70,10 +70,10 @@ module("Integration | Component | user-menu", function (hooks) {
|
|||
await render(template);
|
||||
const tabs = queryAll(".bottom-tabs.tabs-list .btn");
|
||||
assert.strictEqual(tabs.length, 1);
|
||||
const preferencesTab = tabs[0];
|
||||
assert.ok(preferencesTab.href.endsWith("/u/eviltrout/preferences"));
|
||||
assert.strictEqual(preferencesTab.dataset.tabNumber, "6");
|
||||
assert.strictEqual(preferencesTab.getAttribute("tabindex"), "-1");
|
||||
const profileTab = tabs[0];
|
||||
assert.strictEqual(profileTab.id, "user-menu-button-profile");
|
||||
assert.strictEqual(profileTab.dataset.tabNumber, "6");
|
||||
assert.strictEqual(profileTab.getAttribute("tabindex"), "-1");
|
||||
});
|
||||
|
||||
test("likes tab is hidden if current user's like notifications frequency is 'never'", async function (assert) {
|
||||
|
|
|
@ -94,6 +94,28 @@
|
|||
right: 0;
|
||||
width: 320px;
|
||||
padding: 0;
|
||||
#quick-access-profile {
|
||||
ul {
|
||||
flex-wrap: nowrap;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
overflow-y: auto; // really short viewports
|
||||
}
|
||||
li {
|
||||
flex: 1 1 auto;
|
||||
max-height: 3em; // prevent buttons from getting too tall
|
||||
> * {
|
||||
// button, a, and everything else
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
padding: 0 0.5em;
|
||||
}
|
||||
.d-icon {
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.panel-body-bottom {
|
||||
flex: 0;
|
||||
|
@ -102,9 +124,8 @@
|
|||
.menu-tabs-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
border-left: 1px solid var(--primary-low);
|
||||
padding: 0.75em 0;
|
||||
padding: 0.75em 0 0;
|
||||
}
|
||||
|
||||
.tabs-list {
|
||||
|
@ -137,6 +158,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.bottom-tabs {
|
||||
border-top: 1px solid var(--primary-low);
|
||||
}
|
||||
|
||||
.panel-body-contents {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
@ -179,6 +204,31 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#quick-access-profile {
|
||||
display: inline;
|
||||
|
||||
.profile-tab-btn {
|
||||
justify-content: unset;
|
||||
line-height: $line-height-large;
|
||||
width: 100%;
|
||||
|
||||
.d-icon {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.do-not-disturb {
|
||||
.relative-date {
|
||||
font-size: $font-down-3;
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
|
||||
.d-icon-toggle-on {
|
||||
color: var(--tertiary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove when the widgets-based implementation of the user menu is removed
|
||||
|
@ -399,7 +449,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
a {
|
||||
a,
|
||||
.profile-tab-btn {
|
||||
display: flex;
|
||||
margin: 0.25em;
|
||||
padding: 0em 0.25em;
|
||||
|
|
Loading…
Reference in New Issue