mirror of
https://github.com/discourse/discourse.git
synced 2024-11-26 05:03:39 +08:00
DEV: Convert experimental user menu tabs to links when they're active (#18158)
This PR restores a small feature which was present in the old menu and allowed users to click on the active tab in the menu to navigate to some page that showed the same items in the menu but with more details. For example, if you switch to the PMs tab and then click on it again, currently nothing happens. However, with this change, clicking on the tab again will take you to your messages page at `/my/messages`. Note: plugins that register custom tabs in the menu can provide a `linkWhenActive` property for their tab if they wish to mimic core's tabs, but it's optional; if they don't provide one, the tab will do nothing if the user clicks on it again. Internal topic: t/73349.
This commit is contained in:
parent
d00cd3295e
commit
12ebdf0ff0
|
@ -0,0 +1,15 @@
|
|||
<button
|
||||
type="button"
|
||||
role="tab"
|
||||
class={{this.classNames}}
|
||||
id={{this.id}}
|
||||
tabindex={{this.tabIndex}}
|
||||
aria-selected={{if this.isActive "true" "false"}}
|
||||
aria-controls={{this.ariaControls}}
|
||||
data-tab-number={{@tab.position}}
|
||||
{{on "click" @onTabClick}}
|
||||
{{!-- template-lint-disable require-context-role --}}
|
||||
>
|
||||
{{d-icon @tab.icon}}
|
||||
{{yield}}
|
||||
</button>
|
|
@ -0,0 +1,27 @@
|
|||
import Component from "@glimmer/component";
|
||||
|
||||
export default class UserMenuTab extends Component {
|
||||
get isActive() {
|
||||
return this.args.tab.id === this.args.currentTabId;
|
||||
}
|
||||
|
||||
get classNames() {
|
||||
const list = ["btn", "btn-flat", "btn-icon", "no-text", "user-menu-tab"];
|
||||
if (this.isActive) {
|
||||
list.push("active");
|
||||
}
|
||||
return list.join(" ");
|
||||
}
|
||||
|
||||
get id() {
|
||||
return `user-menu-button-${this.args.tab.id}`;
|
||||
}
|
||||
|
||||
get tabIndex() {
|
||||
return this.isActive ? "0" : "-1";
|
||||
}
|
||||
|
||||
get ariaControls() {
|
||||
return `quick-access-${this.args.tab.id}`;
|
||||
}
|
||||
}
|
|
@ -11,23 +11,23 @@
|
|||
<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|}}
|
||||
<UserMenu::TabButton
|
||||
<UserMenu::MenuTab
|
||||
@tab={{tab}}
|
||||
@currentTabId={{this.currentTabId}}
|
||||
@changeTabFunction={{fn this.changeTab tab}}
|
||||
@onTabClick={{fn this.handleTabClick tab}}
|
||||
>
|
||||
{{#if tab.count}}
|
||||
<span class="badge-notification">{{tab.count}}</span>
|
||||
{{/if}}
|
||||
</UserMenu::TabButton>
|
||||
</UserMenu::MenuTab>
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="bottom-tabs tabs-list">
|
||||
{{#each this.bottomTabs as |tab|}}
|
||||
<UserMenu::TabButton
|
||||
<UserMenu::MenuTab
|
||||
@tab={{tab}}
|
||||
@currentTabId={{this.currentTabId}}
|
||||
@changeTabFunction={{fn this.changeTab tab}}
|
||||
@onTabClick={{fn this.handleTabClick tab}}
|
||||
/>
|
||||
{{/each}}
|
||||
</div>
|
||||
|
|
|
@ -4,6 +4,8 @@ import { action } from "@ember/object";
|
|||
import { NO_REMINDER_ICON } from "discourse/models/bookmark";
|
||||
import UserMenuTab, { CUSTOM_TABS_CLASSES } from "discourse/lib/user-menu/tab";
|
||||
import { inject as service } from "@ember/service";
|
||||
import getUrl from "discourse-common/lib/get-url";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
|
||||
const DEFAULT_TAB_ID = "all-notifications";
|
||||
const DEFAULT_PANEL_COMPONENT = "user-menu/notifications-list";
|
||||
|
@ -23,6 +25,10 @@ const CORE_TOP_TABS = [
|
|||
get panelComponent() {
|
||||
return DEFAULT_PANEL_COMPONENT;
|
||||
}
|
||||
|
||||
get linkWhenActive() {
|
||||
return `${this.currentUser.path}/notifications`;
|
||||
}
|
||||
},
|
||||
|
||||
class extends UserMenuTab {
|
||||
|
@ -45,6 +51,10 @@ const CORE_TOP_TABS = [
|
|||
get notificationTypes() {
|
||||
return ["replied"];
|
||||
}
|
||||
|
||||
get linkWhenActive() {
|
||||
return `${this.currentUser.path}/notifications/responses`;
|
||||
}
|
||||
},
|
||||
|
||||
class extends UserMenuTab {
|
||||
|
@ -67,6 +77,10 @@ const CORE_TOP_TABS = [
|
|||
get notificationTypes() {
|
||||
return ["mentioned"];
|
||||
}
|
||||
|
||||
get linkWhenActive() {
|
||||
return `${this.currentUser.path}/notifications/mentions`;
|
||||
}
|
||||
},
|
||||
|
||||
class extends UserMenuTab {
|
||||
|
@ -96,6 +110,10 @@ const CORE_TOP_TABS = [
|
|||
get notificationTypes() {
|
||||
return ["liked", "liked_consolidated", "reaction"];
|
||||
}
|
||||
|
||||
get linkWhenActive() {
|
||||
return `${this.currentUser.path}/notifications/likes-received`;
|
||||
}
|
||||
},
|
||||
|
||||
class extends UserMenuTab {
|
||||
|
@ -120,9 +138,14 @@ const CORE_TOP_TABS = [
|
|||
this.siteSettings.enable_personal_messages || this.currentUser.staff
|
||||
);
|
||||
}
|
||||
|
||||
get notificationTypes() {
|
||||
return ["private_message"];
|
||||
}
|
||||
|
||||
get linkWhenActive() {
|
||||
return `${this.currentUser.path}/messages`;
|
||||
}
|
||||
},
|
||||
|
||||
class extends UserMenuTab {
|
||||
|
@ -145,6 +168,10 @@ const CORE_TOP_TABS = [
|
|||
get notificationTypes() {
|
||||
return ["bookmark_reminder"];
|
||||
}
|
||||
|
||||
get linkWhenActive() {
|
||||
return `${this.currentUser.path}/activity/bookmarks`;
|
||||
}
|
||||
},
|
||||
|
||||
class extends UserMenuTab {
|
||||
|
@ -167,6 +194,10 @@ const CORE_TOP_TABS = [
|
|||
get count() {
|
||||
return this.currentUser.get("reviewable_count");
|
||||
}
|
||||
|
||||
get linkWhenActive() {
|
||||
return getUrl("/review");
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -183,6 +214,10 @@ const CORE_BOTTOM_TABS = [
|
|||
get panelComponent() {
|
||||
return "user-menu/profile-tab-content";
|
||||
}
|
||||
|
||||
get linkWhenActive() {
|
||||
return `${this.currentUser.path}/summary`;
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -300,11 +335,13 @@ export default class UserMenu extends Component {
|
|||
}
|
||||
|
||||
@action
|
||||
changeTab(tab) {
|
||||
handleTabClick(tab) {
|
||||
if (this.currentTabId !== tab.id) {
|
||||
this.currentTabId = tab.id;
|
||||
this.currentPanelComponent = tab.panelComponent;
|
||||
this.currentNotificationTypes = tab.notificationTypes;
|
||||
} else if (tab.linkWhenActive) {
|
||||
DiscourseURL.routeTo(tab.linkWhenActive);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
<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>
|
|
@ -1,3 +0,0 @@
|
|||
import templateOnly from "@ember/component/template-only";
|
||||
|
||||
export default templateOnly();
|
|
@ -551,6 +551,89 @@ acceptance("User menu", function (needs) {
|
|||
"logout button has the right icon"
|
||||
);
|
||||
});
|
||||
|
||||
test("the active tab can be clicked again to navigate to a page", async function (assert) {
|
||||
withPluginApi("0.1", (api) => {
|
||||
api.registerUserMenuTab((UserMenuTab) => {
|
||||
return class extends UserMenuTab {
|
||||
get id() {
|
||||
return "custom-tab-1";
|
||||
}
|
||||
|
||||
get icon() {
|
||||
return "wrench";
|
||||
}
|
||||
|
||||
get panelComponent() {
|
||||
return "d-button";
|
||||
}
|
||||
|
||||
get linkWhenActive() {
|
||||
return "/u/eviltrout/preferences";
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
api.registerUserMenuTab((UserMenuTab) => {
|
||||
return class extends UserMenuTab {
|
||||
get id() {
|
||||
return "custom-tab-2";
|
||||
}
|
||||
|
||||
get icon() {
|
||||
return "plus";
|
||||
}
|
||||
|
||||
get panelComponent() {
|
||||
return "d-button";
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
await visit("/");
|
||||
await click(".d-header-icons .current-user");
|
||||
await click("#user-menu-button-all-notifications");
|
||||
assert.strictEqual(
|
||||
currentURL(),
|
||||
"/u/eviltrout/notifications",
|
||||
"clicking on active tab navigates to the page it links to"
|
||||
);
|
||||
assert.notOk(exists(".user-menu"), "user menu is closed after navigating");
|
||||
|
||||
const tabs = [
|
||||
["#user-menu-button-custom-tab-1", "/u/eviltrout/preferences/account"],
|
||||
["#user-menu-button-replies", "/u/eviltrout/notifications/responses"],
|
||||
["#user-menu-button-messages", "/u/eviltrout/messages"],
|
||||
["#user-menu-button-mentions", "/u/eviltrout/notifications/mentions"],
|
||||
["#user-menu-button-bookmarks", "/u/eviltrout/activity/bookmarks"],
|
||||
["#user-menu-button-likes", "/u/eviltrout/notifications/likes-received"],
|
||||
["#user-menu-button-custom-tab-2", null],
|
||||
["#user-menu-button-review-queue", "/review"],
|
||||
["#user-menu-button-profile", "/u/eviltrout/summary"],
|
||||
];
|
||||
for (const [id, expectedLink] of tabs) {
|
||||
await click(".d-header-icons .current-user");
|
||||
await click(id);
|
||||
await click(id);
|
||||
if (expectedLink) {
|
||||
assert.strictEqual(
|
||||
currentURL(),
|
||||
expectedLink,
|
||||
`clicking on the ${id} tab navigates to ${expectedLink}`
|
||||
);
|
||||
assert.notOk(
|
||||
exists(".user-menu"),
|
||||
"user menu is closed after navigating"
|
||||
);
|
||||
} else {
|
||||
assert.ok(
|
||||
exists(".user-menu"),
|
||||
"user menu remains open if tab doesn't link to anywhere"
|
||||
);
|
||||
}
|
||||
await click("#site-logo");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
acceptance("User menu - Dismiss button", function (needs) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user