DEV: Remove the old header widgets code (#28390)

Remove the header widget code.

More info can be found in https://meta.discourse.org/t/upcoming-header-changes-preparing-themes-and-plugins/296544
This commit is contained in:
Sérgio Saquetim 2024-09-04 14:50:53 -03:00 committed by GitHub
parent a23773f83d
commit 7c3ad27de6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 54 additions and 2573 deletions

View File

@ -1,558 +0,0 @@
import { DEBUG } from "@glimmer/env";
import { getOwner } from "@ember/owner";
import { schedule } from "@ember/runloop";
import { waitForPromise } from "@ember/test-waiters";
import ItsATrap from "@discourse/itsatrap";
import MountWidget from "discourse/components/mount-widget";
import { topicTitleDecorators } from "discourse/components/topic-title";
import scrollLock from "discourse/lib/scroll-lock";
import SwipeEvents, {
getMaxAnimationTimeMs,
shouldCloseMenu,
} from "discourse/lib/swipe-events";
import { isDocumentRTL } from "discourse/lib/text-direction";
import Docking from "discourse/mixins/docking";
import RerenderOnDoNotDisturbChange from "discourse/mixins/rerender-on-do-not-disturb-change";
import { isTesting } from "discourse-common/config/environment";
import discourseLater from "discourse-common/lib/later";
import { bind, observes } from "discourse-common/utils/decorators";
let _menuPanelClassesToForceDropdown = [];
const SiteHeaderComponent = MountWidget.extend(
Docking,
RerenderOnDoNotDisturbChange,
{
widget: "header",
docAt: null,
dockedHeader: null,
_animate: false,
_swipeMenuOrigin: "right",
_topic: null,
_itsatrap: null,
_applicationElement: null,
_PANEL_WIDTH: 340,
_swipeEvents: null,
@observes(
"currentUser.unread_notifications",
"currentUser.unread_high_priority_notifications",
"currentUser.all_unread_notifications_count",
"currentUser.reviewable_count",
"currentUser.unseen_reviewable_count",
"session.defaultColorSchemeIsDark",
"session.darkModeAvailable"
)
notificationsChanged() {
this.queueRerender();
},
@observes("site.narrowDesktopView")
narrowDesktopViewChanged() {
this.eventDispatched("dom:clean", "header");
if (this._dropDownHeaderEnabled()) {
this.appEvents.on(
"sidebar-hamburger-dropdown:rendered",
this,
"_animateMenu"
);
}
},
_animateOpening(panel, event = null) {
const headerCloak = document.querySelector(".header-cloak");
let durationMs = getMaxAnimationTimeMs();
if (event && this.pxClosed > 0) {
durationMs = getMaxAnimationTimeMs(
this.pxClosed / Math.abs(event.velocityX)
);
}
const timing = {
duration: durationMs,
fill: "forwards",
easing: "ease-out",
};
panel.animate([{ transform: `translate3d(0, 0, 0)` }], timing);
headerCloak.animate([{ opacity: 1 }], timing);
this.pxClosed = null;
},
_animateClosing(event, panel, menuOrigin) {
this._animate = true;
const headerCloak = document.querySelector(".header-cloak");
let durationMs = getMaxAnimationTimeMs();
if (event && this.pxClosed > 0) {
const distancePx = this._PANEL_WIDTH - this.pxClosed;
durationMs = getMaxAnimationTimeMs(
distancePx / Math.abs(event.velocityX)
);
}
const timing = {
duration: durationMs,
fill: "forwards",
};
let endPosition = -this._PANEL_WIDTH; //origin left
if (menuOrigin === "right") {
endPosition = this._PANEL_WIDTH;
}
panel
.animate([{ transform: `translate3d(${endPosition}px, 0, 0)` }], timing)
.finished.then(() => {
schedule("afterRender", () => {
this.eventDispatched("dom:clean", "header");
});
});
headerCloak.animate([{ opacity: 0 }], timing);
this.pxClosed = null;
},
_leftMenuClass() {
return isDocumentRTL() ? "user-menu" : "hamburger-panel";
},
@bind
onSwipeStart(event) {
const e = event.detail;
const center = e.center;
const swipeOverValidElement = document
.elementsFromPoint(center.x, center.y)
.some(
(ele) =>
ele.classList.contains("panel-body") ||
ele.classList.contains("header-cloak")
);
if (
swipeOverValidElement &&
(e.direction === "left" || e.direction === "right")
) {
this.movingElement = document.querySelector(".menu-panel");
this.cloakElement = document.querySelector(".header-cloak");
scrollLock(true, document.querySelector(".panel-body"));
} else {
event.preventDefault();
}
},
@bind
onSwipeEnd(event) {
const e = event.detail;
const menuPanels = document.querySelectorAll(".menu-panel");
const menuOrigin = this._swipeMenuOrigin;
scrollLock(false, document.querySelector(".panel-body"));
menuPanels.forEach((panel) => {
if (shouldCloseMenu(e, menuOrigin)) {
this._animateClosing(e, panel, menuOrigin);
} else {
this._animateOpening(panel, e);
}
});
},
@bind
onSwipeCancel() {
const menuPanels = document.querySelectorAll(".menu-panel");
scrollLock(false, document.querySelector(".panel-body"));
menuPanels.forEach((panel) => {
this._animateOpening(panel);
});
},
@bind
onSwipe(event) {
const e = event.detail;
const panel = this.movingElement;
const headerCloak = this.cloakElement;
//origin left
this.pxClosed = Math.max(0, -e.deltaX);
let translation = -this.pxClosed;
if (this._swipeMenuOrigin === "right") {
this.pxClosed = Math.max(0, e.deltaX);
translation = this.pxClosed;
}
panel.animate([{ transform: `translate3d(${translation}px, 0, 0)` }], {
fill: "forwards",
});
headerCloak.animate(
[
{
opacity: (this._PANEL_WIDTH - this.pxClosed) / this._PANEL_WIDTH,
},
],
{ fill: "forwards" }
);
},
dockCheck() {
const header = this.header;
if (this.docAt === null) {
if (!header) {
return;
}
this.docAt = header.offsetTop;
}
const main = (this._applicationElement ??=
document.querySelector(".ember-application"));
const offsetTop = main ? main.offsetTop : 0;
const offset = window.pageYOffset - offsetTop;
if (offset >= this.docAt) {
if (!this.dockedHeader) {
document.body.classList.add("docked");
this.dockedHeader = true;
}
} else {
if (this.dockedHeader) {
document.body.classList.remove("docked");
this.dockedHeader = false;
}
}
},
setTopic() {
const header = getOwner(this).lookup("service:header");
if (header.topicInfoVisible) {
this._topic = header.topicInfo;
} else {
this._topic = null;
}
this.eventDispatched("dom:clean", "header");
this.queueRerender();
},
willRender() {
this._super(...arguments);
if (this.get("currentUser.staff")) {
document.body.classList.add("staff");
}
},
didInsertElement() {
this._super(...arguments);
this._resizeDiscourseMenuPanel = () => this.afterRender();
window.addEventListener("resize", this._resizeDiscourseMenuPanel);
const headerService = getOwner(this).lookup("service:header");
headerService.addObserver("topicInfoVisible", this, "setTopic");
this.setTopic();
this.appEvents.on("user-menu:rendered", this, "_animateMenu");
if (this._dropDownHeaderEnabled()) {
this.appEvents.on(
"sidebar-hamburger-dropdown:rendered",
this,
"_animateMenu"
);
}
this.dispatch("notifications:changed", "user-notifications");
this.dispatch("header:keyboard-trigger", "header");
this.dispatch("user-menu:navigation", "user-menu");
this.appEvents.on("dom:clean", this, "_cleanDom");
if (this.currentUser) {
this.currentUser.on("status-changed", this, "queueRerender");
}
const header = document.querySelector("header.d-header");
this._itsatrap = new ItsATrap(header);
const dirs = ["up", "down"];
this._itsatrap.bind(dirs, (e) => this._handleArrowKeysNav(e));
},
_handleArrowKeysNav(event) {
const activeTab = document.querySelector(
".menu-tabs-container .btn.active"
);
if (activeTab) {
let activeTabNumber = Number(
document.activeElement.dataset.tabNumber ||
activeTab.dataset.tabNumber
);
const maxTabNumber =
document.querySelectorAll(".menu-tabs-container .btn").length - 1;
const isNext = event.key === "ArrowDown";
let nextTab = isNext ? activeTabNumber + 1 : activeTabNumber - 1;
if (isNext && nextTab > maxTabNumber) {
nextTab = 0;
}
if (!isNext && nextTab < 0) {
nextTab = maxTabNumber;
}
event.preventDefault();
document
.querySelector(
`.menu-tabs-container .btn[data-tab-number='${nextTab}']`
)
.focus();
}
},
_cleanDom() {
// For performance, only trigger a re-render if any menu panels are visible
if (this.element.querySelector(".menu-panel")) {
this.eventDispatched("dom:clean", "header");
}
},
willDestroyElement() {
this._super(...arguments);
window.removeEventListener("resize", this._resizeDiscourseMenuPanel);
getOwner(this)
.lookup("service:header")
.removeObserver("topicInfoVisible", this, "setTopic");
this.appEvents.off("dom:clean", this, "_cleanDom");
this.appEvents.off("user-menu:rendered", this, "_animateMenu");
if (this._dropDownHeaderEnabled()) {
this.appEvents.off(
"sidebar-hamburger-dropdown:rendered",
this,
"_animateMenu"
);
}
if (this.currentUser) {
this.currentUser.off("status-changed", this, "queueRerender");
}
this._itsatrap?.destroy();
this._itsatrap = null;
},
buildArgs() {
return {
topic: this._topic,
canSignUp: this.canSignUp,
sidebarEnabled: this.sidebarEnabled,
showSidebar: this.showSidebar,
navigationMenuQueryParamOverride: this.navigationMenuQueryParamOverride,
};
},
afterRender() {
const headerTitle = document.querySelector(".header-title .topic-link");
if (headerTitle && this._topic) {
topicTitleDecorators.forEach((cb) =>
cb(this._topic, headerTitle, "header-title")
);
}
this._animateMenu();
},
_animateMenu() {
const menuPanels = document.querySelectorAll(".menu-panel");
if (menuPanels.length === 0) {
this._animate = this.site.mobileView || this.site.narrowDesktopView;
return;
}
let viewMode =
this.site.mobileView || this.site.narrowDesktopView
? "slide-in"
: "drop-down";
menuPanels.forEach((panel) => {
if (menuPanelContainsClass(panel)) {
viewMode = "drop-down";
this._animate = false;
}
const headerCloak = document.querySelector(".header-cloak");
panel.classList.remove("drop-down");
panel.classList.remove("slide-in");
panel.classList.add(viewMode);
if (this._animate) {
let animationFinished = null;
let finalPosition = this._PANEL_WIDTH;
this._swipeMenuOrigin = "right";
if (
(this.site.mobileView || this.site.narrowDesktopView) &&
panel.parentElement.classList.contains(this._leftMenuClass())
) {
this._swipeMenuOrigin = "left";
finalPosition = -this._PANEL_WIDTH;
}
animationFinished = panel.animate(
[{ transform: `translate3d(${finalPosition}px, 0, 0)` }],
{
fill: "forwards",
}
).finished;
if (isTesting()) {
waitForPromise(animationFinished);
}
headerCloak.animate([{ opacity: 0 }], { fill: "forwards" });
headerCloak.style.display = "block";
animationFinished.then(() => {
if (isTesting()) {
this._animateOpening(panel);
} else {
discourseLater(() => this._animateOpening(panel));
}
});
}
this._animate = false;
});
},
_dropDownHeaderEnabled() {
return !this.sidebarEnabled || this.site.narrowDesktopView;
},
}
);
function menuPanelContainsClass(menuPanel) {
if (!_menuPanelClassesToForceDropdown) {
return false;
}
// Check if any of the classNames are present in the node's classList
for (let className of _menuPanelClassesToForceDropdown) {
if (menuPanel.classList.contains(className)) {
// Found a matching class
return true;
}
}
// No matching class found
return false;
}
export function forceDropdownForMenuPanels(classNames) {
// If classNames is a string, convert it to an array
if (typeof classNames === "string") {
classNames = [classNames];
}
return _menuPanelClassesToForceDropdown.push(...classNames);
}
export default SiteHeaderComponent.extend({
classNames: ["d-header-wrap"],
classNameBindings: ["site.mobileView::drop-down-mode"],
headerWrap: null,
header: null,
init() {
this._super(...arguments);
this._resizeObserver = null;
},
@bind
updateHeaderOffset() {
// Safari likes overscolling the page (on both iOS and macOS).
// This shows up as a negative value in window.scrollY.
// We can use this to offset the headerWrap's top offset to avoid
// jitteriness and bad positioning.
const windowOverscroll = Math.min(0, window.scrollY);
// The headerWrap's top offset can also be a negative value on Safari,
// because of the changing height of the viewport (due to the URL bar).
// For our use case, it's best to ensure this is clamped to 0.
const headerWrapTop = Math.max(
0,
Math.floor(this.headerWrap.getBoundingClientRect().top)
);
let offsetTop = headerWrapTop + windowOverscroll;
if (DEBUG && isTesting()) {
offsetTop -= document
.getElementById("ember-testing-container")
.getBoundingClientRect().top;
offsetTop -= 1; // For 1px border on testing container
}
const documentStyle = document.documentElement.style;
const currentValue =
parseInt(documentStyle.getPropertyValue("--header-offset"), 10) || 0;
const newValue = this.headerWrap.offsetHeight + offsetTop;
if (currentValue !== newValue) {
documentStyle.setProperty("--header-offset", `${newValue}px`);
}
},
@bind
onScroll() {
schedule("afterRender", this.updateHeaderOffset);
},
didInsertElement() {
this._super(...arguments);
this.appEvents.on("site-header:force-refresh", this, "queueRerender");
this.headerWrap = document.querySelector(".d-header-wrap");
if (this.headerWrap) {
schedule("afterRender", () => {
this.header = this.headerWrap.querySelector("header.d-header");
this.updateHeaderOffset();
const headerTop = this.header.offsetTop;
document.documentElement.style.setProperty(
"--header-top",
`${headerTop}px`
);
});
window.addEventListener("scroll", this.onScroll, {
passive: true,
});
}
this._resizeObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
if (entry.contentRect) {
const headerTop = this.header?.offsetTop;
document.documentElement.style.setProperty(
"--header-top",
`${headerTop}px`
);
this.updateHeaderOffset();
}
}
});
this._resizeObserver.observe(this.headerWrap);
this._swipeEvents = new SwipeEvents(this.element);
if (this.site.mobileView) {
this._swipeEvents.addTouchListeners();
this.element.addEventListener("swipestart", this.onSwipeStart);
this.element.addEventListener("swipeend", this.onSwipeEnd);
this.element.addEventListener("swipecancel", this.onSwipeCancel);
this.element.addEventListener("swipe", this.onSwipe);
}
},
willDestroyElement() {
this._super(...arguments);
window.removeEventListener("scroll", this.onScroll);
this._resizeObserver?.disconnect();
this.appEvents.off("site-header:force-refresh", this, "queueRerender");
if (this.site.mobileView) {
this.element.removeEventListener("swipestart", this.onSwipeStart);
this.element.removeEventListener("swipeend", this.onSwipeEnd);
this.element.removeEventListener("swipecancel", this.onSwipeCancel);
this.element.removeEventListener("swipe", this.onSwipe);
this._swipeEvents.removeTouchListeners();
}
},
});

View File

@ -23,9 +23,7 @@ import { forceDropdownForMenuPanels as glimmerForceDropdownForMenuPanels } from
import { addGlobalNotice } from "discourse/components/global-notice"; import { addGlobalNotice } from "discourse/components/global-notice";
import { headerButtonsDAG } from "discourse/components/header"; import { headerButtonsDAG } from "discourse/components/header";
import { headerIconsDAG } from "discourse/components/header/icons"; import { headerIconsDAG } from "discourse/components/header/icons";
import MountWidget, { import { addWidgetCleanCallback } from "discourse/components/mount-widget";
addWidgetCleanCallback,
} from "discourse/components/mount-widget";
import { addPluginOutletDecorator } from "discourse/components/plugin-connector"; import { addPluginOutletDecorator } from "discourse/components/plugin-connector";
import { import {
addPluginReviewableParam, addPluginReviewableParam,
@ -40,7 +38,6 @@ import {
} from "discourse/components/search-menu/results/random-quick-tip"; } from "discourse/components/search-menu/results/random-quick-tip";
import { addOnKeyUpCallback } from "discourse/components/search-menu/search-term"; import { addOnKeyUpCallback } from "discourse/components/search-menu/search-term";
import { REFRESH_COUNTS_APP_EVENT_NAME as REFRESH_USER_SIDEBAR_CATEGORIES_SECTION_COUNTS_APP_EVENT_NAME } from "discourse/components/sidebar/user/categories-section"; import { REFRESH_COUNTS_APP_EVENT_NAME as REFRESH_USER_SIDEBAR_CATEGORIES_SECTION_COUNTS_APP_EVENT_NAME } from "discourse/components/sidebar/user/categories-section";
import { forceDropdownForMenuPanels } from "discourse/components/site-header";
import { addTopicParticipantClassesCallback } from "discourse/components/topic-map/topic-participant"; import { addTopicParticipantClassesCallback } from "discourse/components/topic-map/topic-participant";
import { setDesktopScrollAreaHeight } from "discourse/components/topic-timeline/container"; import { setDesktopScrollAreaHeight } from "discourse/components/topic-timeline/container";
import { addTopicTitleDecorator } from "discourse/components/topic-title"; import { addTopicTitleDecorator } from "discourse/components/topic-title";
@ -127,7 +124,6 @@ import {
import { setNewCategoryDefaultColors } from "discourse/routes/new-category"; import { setNewCategoryDefaultColors } from "discourse/routes/new-category";
import { setNotificationsLimit } from "discourse/routes/user-notifications"; import { setNotificationsLimit } from "discourse/routes/user-notifications";
import { addComposerSaveErrorCallback } from "discourse/services/composer"; import { addComposerSaveErrorCallback } from "discourse/services/composer";
import { attachAdditionalPanel } from "discourse/widgets/header";
import { addPostClassesCallback } from "discourse/widgets/post"; import { addPostClassesCallback } from "discourse/widgets/post";
import { addDecorator } from "discourse/widgets/post-cooked"; import { addDecorator } from "discourse/widgets/post-cooked";
import { import {
@ -165,20 +161,6 @@ import { addImageWrapperButton } from "discourse-markdown-it/features/image-cont
import { CUSTOM_USER_SEARCH_OPTIONS } from "select-kit/components/user-chooser"; import { CUSTOM_USER_SEARCH_OPTIONS } from "select-kit/components/user-chooser";
import { modifySelectKit } from "select-kit/mixins/plugin-api"; import { modifySelectKit } from "select-kit/mixins/plugin-api";
const DEPRECATED_HEADER_WIDGETS = [
"header",
"site-header",
"header-contents",
"header-buttons",
"user-status-bubble",
"sidebar-toggle",
"header-icons",
"header-topic-info",
"header-notifications",
"home-logo",
"user-dropdown",
];
const appliedModificationIds = new WeakMap(); const appliedModificationIds = new WeakMap();
// This helper prevents us from applying the same `modifyClass` over and over in test mode. // This helper prevents us from applying the same `modifyClass` over and over in test mode.
@ -732,7 +714,7 @@ class PluginApi {
**/ **/
decorateWidget(name, fn) { decorateWidget(name, fn) {
const widgetName = name.split(":")[0]; const widgetName = name.split(":")[0];
this.#deprecatedHeaderWidgetOverride(widgetName, "decorateWidget"); this.#deprecatedWidgetOverride(widgetName, "decorateWidget");
decorateWidget(name, fn); decorateWidget(name, fn);
} }
@ -763,7 +745,7 @@ class PluginApi {
return; return;
} }
this.#deprecatedHeaderWidgetOverride(widget, "attachWidgetAction"); this.#deprecatedWidgetOverride(widget, "attachWidgetAction");
widgetClass.prototype[actionName] = fn; widgetClass.prototype[actionName] = fn;
} }
@ -1108,7 +1090,7 @@ class PluginApi {
* *
**/ **/
changeWidgetSetting(widgetName, settingName, newValue) { changeWidgetSetting(widgetName, settingName, newValue) {
this.#deprecatedHeaderWidgetOverride(widgetName, "changeWidgetSetting"); this.#deprecatedWidgetOverride(widgetName, "changeWidgetSetting");
changeSetting(widgetName, settingName, newValue); changeSetting(widgetName, settingName, newValue);
} }
@ -1142,7 +1124,7 @@ class PluginApi {
**/ **/
reopenWidget(name, args) { reopenWidget(name, args) {
this.#deprecatedHeaderWidgetOverride(name, "reopenWidget"); this.#deprecatedWidgetOverride(name, "reopenWidget");
return reopenWidget(name, args); return reopenWidget(name, args);
} }
@ -1169,16 +1151,12 @@ class PluginApi {
* and returns a hash of values to pass to attach * and returns a hash of values to pass to attach
* *
**/ **/
addHeaderPanel(name, toggle, transformAttrs) { addHeaderPanel() {
deprecated( // eslint-disable-next-line no-console
"addHeaderPanel will be removed as part of the glimmer header upgrade. Use api.headerIcons instead.", console.error(
{ consolePrefix(),
id: "discourse.add-header-panel", `api.addHeaderPanel: This API was decommissioned. Use api.headerIcons instead.`
url: "https://meta.discourse.org/t/296544",
}
); );
this.container.lookup("service:header").anyWidgetHeaderOverrides = true;
attachAdditionalPanel(name, toggle, transformAttrs);
} }
/** /**
@ -2141,19 +2119,12 @@ class PluginApi {
* ``` * ```
* *
**/ **/
// eslint-disable-next-line no-unused-vars
addToHeaderIcons(icon) { addToHeaderIcons(icon) {
deprecated( // eslint-disable-next-line no-console
"addToHeaderIcons has been deprecated. Use api.headerIcons instead.", console.error(
{ consolePrefix(),
id: "discourse.add-header-icons", `api.addToHeaderIcons: This API was decommissioned. Use api.headerIcons instead.`
url: "https://meta.discourse.org/t/296544",
}
);
this.headerIcons.add(
icon,
<template><MountWidget @widget={{icon}} /></template>,
{ before: "search" }
); );
} }
@ -2397,7 +2368,6 @@ class PluginApi {
* *
*/ */
forceDropdownForMenuPanels(classNames) { forceDropdownForMenuPanels(classNames) {
forceDropdownForMenuPanels(classNames);
glimmerForceDropdownForMenuPanels(classNames); glimmerForceDropdownForMenuPanels(classNames);
} }
@ -3282,18 +3252,20 @@ class PluginApi {
addLegacyAboutPageStat(name); addLegacyAboutPageStat(name);
} }
#deprecatedHeaderWidgetOverride(widgetName, override) { // eslint-disable-next-line no-unused-vars
if (DEPRECATED_HEADER_WIDGETS.includes(widgetName)) { #deprecatedWidgetOverride(widgetName, override) {
this.container.lookup("service:header").anyWidgetHeaderOverrides = true; // insert here the code to handle widget deprecations, e.g. for the header widgets we used:
deprecated( // if (DEPRECATED_HEADER_WIDGETS.includes(widgetName)) {
`The ${widgetName} widget has been deprecated and ${override} is no longer a supported override.`, // this.container.lookup("service:header").anyWidgetHeaderOverrides = true;
{ // deprecated(
since: "v3.3.0.beta1-dev", // `The ${widgetName} widget has been deprecated and ${override} is no longer a supported override.`,
id: "discourse.header-widget-overrides", // {
url: "https://meta.discourse.org/t/316549", // since: "v3.3.0.beta1-dev",
} // id: "discourse.header-widget-overrides",
); // url: "https://meta.discourse.org/t/316549",
} // }
// );
// }
} }
} }

View File

@ -28,7 +28,6 @@ export default class Header extends Service {
@tracked hamburgerVisible = false; @tracked hamburgerVisible = false;
@tracked userVisible = false; @tracked userVisible = false;
@tracked anyWidgetHeaderOverrides = false;
#hiders = new TrackedMap(); #hiders = new TrackedMap();
@ -72,32 +71,6 @@ export default class Header extends Service {
return true; return true;
} }
get useGlimmerHeader() {
if (this.siteSettings.glimmer_header_mode === "disabled") {
return false;
} else if (this.siteSettings.glimmer_header_mode === "enabled") {
if (this.anyWidgetHeaderOverrides) {
// eslint-disable-next-line no-console
console.error(
"Using modern 'glimmer' header, even though there are themes and/or plugins using deprecated APIs. Deprecated header customizations will be broken. (glimmer_header_mode: enabled) https://meta.discourse.org/t/316549"
);
}
return true;
} else {
// Auto
if (this.anyWidgetHeaderOverrides) {
// eslint-disable-next-line no-console
console.warn(
"Using legacy 'widget' header because themes and/or plugins are using deprecated APIs. (glimmer_header_mode: auto) https://meta.discourse.org/t/316549"
);
return false;
} else {
return true;
}
}
}
registerHider(ref, buttons) { registerHider(ref, buttons) {
const validButtons = buttons const validButtons = buttons
.map((button) => { .map((button) => {

View File

@ -12,32 +12,17 @@
/> />
{{#if this.showSiteHeader}} {{#if this.showSiteHeader}}
{{#if this.header.useGlimmerHeader}} <GlimmerSiteHeader
<GlimmerSiteHeader @canSignUp={{this.canSignUp}}
@canSignUp={{this.canSignUp}} @showCreateAccount={{route-action "showCreateAccount"}}
@showCreateAccount={{route-action "showCreateAccount"}} @showLogin={{route-action "showLogin"}}
@showLogin={{route-action "showLogin"}} @showKeyboard={{route-action "showKeyboardShortcutsHelp"}}
@showKeyboard={{route-action "showKeyboardShortcutsHelp"}} @toggleMobileView={{route-action "toggleMobileView"}}
@toggleMobileView={{route-action "toggleMobileView"}} @logout={{route-action "logout"}}
@logout={{route-action "logout"}} @sidebarEnabled={{this.sidebarEnabled}}
@sidebarEnabled={{this.sidebarEnabled}} @showSidebar={{this.showSidebar}}
@showSidebar={{this.showSidebar}} @toggleSidebar={{this.toggleSidebar}}
@toggleSidebar={{this.toggleSidebar}} />
/>
{{else}}
<SiteHeader
@canSignUp={{this.canSignUp}}
@showCreateAccount={{route-action "showCreateAccount"}}
@showLogin={{route-action "showLogin"}}
@showKeyboard={{route-action "showKeyboardShortcutsHelp"}}
@toggleMobileView={{route-action "toggleMobileView"}}
@logout={{route-action "logout"}}
@sidebarEnabled={{this.sidebarEnabled}}
@navigationMenuQueryParamOverride={{this.navigationMenuQueryParamOverride}}
@showSidebar={{this.showSidebar}}
@toggleSidebar={{action "toggleSidebar"}}
/>
{{/if}}
{{/if}} {{/if}}
<SoftwareUpdatePrompt /> <SoftwareUpdatePrompt />

View File

@ -1,8 +0,0 @@
import { hbs } from "ember-cli-htmlbars";
import { registerWidgetShim } from "discourse/widgets/render-glimmer";
registerWidgetShim(
"after-header-panel-outlet",
"div.after-header-panel-outlet",
hbs`<PluginOutlet @name="after-header-panel" @outletArgs={{hash topic=@data.topic}} /> `
);

View File

@ -1,8 +0,0 @@
import { hbs } from "ember-cli-htmlbars";
import { registerWidgetShim } from "discourse/widgets/render-glimmer";
registerWidgetShim(
"before-header-logo-outlet",
"div.before-header-logo-outlet",
hbs`<PluginOutlet @name="before-header-logo" @outletArgs={{hash attrs=@data}} /> `
);

View File

@ -1,8 +0,0 @@
import { hbs } from "ember-cli-htmlbars";
import { registerWidgetShim } from "discourse/widgets/render-glimmer";
registerWidgetShim(
"before-header-panel-outlet",
"div.before-header-panel-outlet",
hbs`<PluginOutlet @name="before-header-panel" @outletArgs={{hash topic=@data.topic}} /> `
);

View File

@ -1,8 +0,0 @@
import { hbs } from "ember-cli-htmlbars";
import { registerWidgetShim } from "discourse/widgets/render-glimmer";
registerWidgetShim(
"header-bootstrap-mode",
"div.d-header-mode",
hbs`<BootstrapModeNotice />`
);

View File

@ -1,36 +0,0 @@
import hbs from "discourse/widgets/hbs-compiler";
import { createWidget } from "discourse/widgets/widget";
createWidget("header-contents", {
tagName: "div.contents",
transform() {
return {
showBootstrapMode: this.currentUser?.staff && this.site.desktopView,
};
},
template: hbs`
{{#if this.site.desktopView}}
{{#if attrs.sidebarEnabled}}
{{sidebar-toggle attrs=attrs}}
{{/if}}
{{/if}}
{{before-header-logo-outlet attrs=attrs}}
{{home-logo-wrapper-outlet attrs=attrs}}
{{#if attrs.topic}}
{{header-topic-info attrs=attrs}}
{{else if this.siteSettings.bootstrap_mode_enabled}}
{{#if transformed.showBootstrapMode}}
{{header-bootstrap-mode attrs=attrs}}
{{/if}}
{{/if}}
{{before-header-panel-outlet attrs=attrs}}
<div class="panel" role="navigation">{{yield}}</div>
{{after-header-panel-outlet attrs=attrs}}
`,
});

View File

@ -1,260 +0,0 @@
import { hbs } from "ember-cli-htmlbars";
import { h } from "virtual-dom";
import renderTags from "discourse/lib/render-tags";
import { topicFeaturedLinkNode } from "discourse/lib/render-topic-featured-link";
import DiscourseURL from "discourse/lib/url";
import { avatarImg } from "discourse/widgets/post";
import RawHtml from "discourse/widgets/raw-html";
import RenderGlimmer from "discourse/widgets/render-glimmer";
import { applyDecorators, createWidget } from "discourse/widgets/widget";
import getURL from "discourse-common/lib/get-url";
import { iconNode } from "discourse-common/lib/icon-library";
import I18n from "discourse-i18n";
createWidget("topic-header-participant", {
tagName: "span",
buildClasses(attrs) {
return `trigger-${attrs.type}-card`;
},
html(attrs) {
const { user, group } = attrs;
let content, url;
if (attrs.type === "user") {
content = avatarImg("tiny", {
template: user.avatar_template,
username: user.username,
});
url = user.get("path");
} else {
content = [iconNode("users")];
url = getURL(`/g/${group.name}`);
content.push(h("span", group.name));
}
return h(
"a.icon",
{
attributes: {
href: url,
"data-auto-route": true,
title: attrs.username,
},
},
content
);
},
click(e) {
this.appEvents.trigger(
`topic-header:trigger-${this.attrs.type}-card`,
this.attrs.username,
e.target,
e
);
e.preventDefault();
},
});
export default createWidget("header-topic-info", {
tagName: "div.extra-info-wrapper",
contents: null,
title: null,
buildClasses(attrs, state) {
this.buildAttributes(attrs, state);
return this.containerClassName();
},
buildFancyTitleClass() {
const baseClass = ["topic-link"];
const flatten = (array) => [].concat.apply([], array);
const extraClass = flatten(
applyDecorators(this, "fancyTitleClass", this.attrs, this.state)
);
return baseClass.concat(extraClass).filter(Boolean).join(" ");
},
buildAttributes(attrs, state) {
const topic = attrs.topic;
const heading = [];
const showPM = !topic.get("is_warning") && topic.get("isPrivateMessage");
if (showPM) {
const href = this.currentUser && this.currentUser.pmPath(topic);
if (href) {
heading.push(
h(
"a.private-message-glyph-wrapper",
{
attributes: { href, "aria-label": I18n.t("user.messages.inbox") },
},
iconNode("envelope", { class: "private-message-glyph" })
)
);
}
}
const loaded = topic.get("details.loaded");
const fancyTitle = topic.get("fancyTitle");
const href = topic.get("url");
if (fancyTitle && href) {
heading.push(this.attach("topic-status", attrs));
const titleHTML = new RawHtml({ html: `<span>${fancyTitle}</span>` });
heading.push(
this.attach("link", {
className: this.buildFancyTitleClass(),
action: "jumpToTopPost",
href,
attributes: { "data-topic-id": topic.get("id") },
contents: () => titleHTML,
})
);
heading.push(
new RenderGlimmer(
this,
"span.header-topic-title-suffix",
hbs`<PluginOutlet @name="header-topic-title-suffix" @outletArgs={{@data.outletArgs}}/>`,
{
outletArgs: {
topic,
},
}
)
);
}
this.headerElements = [h("h1.header-title", heading)];
const category = topic.get("category");
if (loaded || category) {
if (
category &&
(!category.isUncategorizedCategory ||
!this.siteSettings.suppress_uncategorized_badge)
) {
const parentCategory = category.get("parentCategory");
const categories = [];
if (parentCategory) {
if (
this.siteSettings.max_category_nesting > 2 &&
this.site.desktopView
) {
const grandParentCategory = parentCategory.get("parentCategory");
if (grandParentCategory) {
categories.push(
this.attach("category-link", { category: grandParentCategory })
);
}
}
categories.push(
this.attach("category-link", { category: parentCategory })
);
}
categories.push(
this.attach("category-link", { category, hideParent: true })
);
this.headerElements.push(h("div.categories-wrapper", categories));
}
let extra = [];
const tags = renderTags(topic);
if (tags && tags.length > 0) {
extra.push(new RawHtml({ html: tags }));
}
if (showPM) {
const maxHeaderParticipants = extra.length > 0 ? 5 : 10;
const participants = [];
const topicDetails = topic.get("details");
const totalParticipants =
topicDetails.allowed_users.length +
topicDetails.allowed_groups.length;
topicDetails.allowed_users.some((user) => {
if (participants.length >= maxHeaderParticipants) {
return true;
}
participants.push(
this.attach("topic-header-participant", {
type: "user",
user,
username: user.username,
})
);
});
topicDetails.allowed_groups.some((group) => {
if (participants.length >= maxHeaderParticipants) {
return true;
}
participants.push(
this.attach("topic-header-participant", {
type: "group",
group,
username: group.name,
})
);
});
if (totalParticipants > maxHeaderParticipants) {
const remaining = totalParticipants - maxHeaderParticipants;
participants.push(
this.attach("link", {
className: "more-participants",
action: "jumpToTopPost",
href,
attributes: { "data-topic-id": topic.get("id") },
contents: () => `+${remaining}`,
})
);
}
extra.push(h("div.topic-header-participants", participants));
}
extra = extra.concat(applyDecorators(this, "after-tags", attrs, state));
if (this.siteSettings.topic_featured_link_enabled) {
const featured = topicFeaturedLinkNode(attrs.topic);
if (featured) {
extra.push(featured);
}
}
if (extra.length) {
this.headerElements.push(h("div.topic-header-extra", extra));
}
}
this.contents = h("div.title-wrapper", this.headerElements);
},
html() {
return h(
"div.extra-info",
{ className: this.containerClassName() },
this.contents
);
},
containerClassName() {
return this.headerElements.length > 1 ? "two-rows" : "";
},
jumpToTopPost() {
const topic = this.attrs.topic;
if (topic) {
DiscourseURL.routeTo(topic.get("firstPostUrl"), {
keepFilter: true,
});
}
},
});

View File

@ -1,8 +0,0 @@
import { hbs } from "ember-cli-htmlbars";
import { registerWidgetShim } from "discourse/widgets/render-glimmer";
registerWidgetShim(
"header-user-tip-shim",
"div.header-user-tip-shim",
hbs`<UserTip @id="first_notification" @triggerSelector=".header-dropdown-toggle.current-user" @placement="bottom-end" @titleText={{i18n "user_tips.first_notification.title"}} @contentText={{i18n "user_tips.first_notification.content"}} @showSkipButton={{true}} />`
);

View File

@ -1,735 +0,0 @@
import { schedule } from "@ember/runloop";
import { hbs } from "ember-cli-htmlbars";
import $ from "jquery";
import { h } from "virtual-dom";
import { headerButtonsDAG } from "discourse/components/header";
import { headerIconsDAG } from "discourse/components/header/icons";
import { addExtraUserClasses } from "discourse/helpers/user-avatar";
import { wantsNewWindow } from "discourse/lib/intercept-click";
import scrollLock from "discourse/lib/scroll-lock";
import { isDocumentRTL } from "discourse/lib/text-direction";
import DiscourseURL from "discourse/lib/url";
import { scrollTop } from "discourse/mixins/scroll-top";
import { avatarImg } from "discourse/widgets/post";
import RenderGlimmer, {
registerWidgetShim,
} from "discourse/widgets/render-glimmer";
import { createWidget } from "discourse/widgets/widget";
import { isTesting } from "discourse-common/config/environment";
import getURL from "discourse-common/lib/get-url";
import { iconNode } from "discourse-common/lib/icon-library";
import discourseLater from "discourse-common/lib/later";
import I18n from "discourse-i18n";
const SEARCH_BUTTON_ID = "search-button";
let _extraHeaderIcons;
clearExtraHeaderIcons();
let _extraHeaderButtons;
clearExtraHeaderButtons();
export function clearExtraHeaderIcons() {
_extraHeaderIcons = headerIconsDAG();
}
export function clearExtraHeaderButtons() {
_extraHeaderButtons = headerButtonsDAG();
}
export const dropdown = {
buildClasses(attrs) {
let classes = attrs.classNames || [];
if (attrs.active) {
classes.push("active");
}
return classes;
},
click(e) {
if (wantsNewWindow(e)) {
return;
}
e.preventDefault();
if (!this.attrs.active) {
this.sendWidgetAction(this.attrs.action);
}
},
};
createWidget("header-notifications", {
services: ["user-tips"],
settings: {
avatarSize: "medium",
},
html(attrs) {
const { user } = attrs;
let avatarAttrs = {
template: user.get("avatar_template"),
username: user.get("username"),
};
if (this.siteSettings.enable_names) {
avatarAttrs.name = user.get("name");
}
const contents = [
avatarImg(
this.settings.avatarSize,
Object.assign(
{ alt: "user.avatar.header_title" },
addExtraUserClasses(user, avatarAttrs)
)
),
];
if (this.currentUser && this._shouldHighlightAvatar()) {
contents.push(this.attach("header-user-tip-shim"));
}
if (this.currentUser.status) {
contents.push(this.attach("user-status-bubble", this.currentUser.status));
}
if (user.isInDoNotDisturb()) {
contents.push(
h("div.do-not-disturb-background", iconNode("discourse-dnd"))
);
} else {
if (user.new_personal_messages_notifications_count) {
contents.push(
this.attach("link", {
action: attrs.action,
className: "badge-notification with-icon new-pms",
icon: "envelope",
omitSpan: true,
title: "notifications.tooltip.new_message_notification",
titleOptions: {
count: user.new_personal_messages_notifications_count,
},
attributes: {
"aria-label": I18n.t(
"notifications.tooltip.new_message_notification",
{
count: user.new_personal_messages_notifications_count,
}
),
},
})
);
} else if (user.unseen_reviewable_count) {
contents.push(
this.attach("link", {
action: attrs.action,
className: "badge-notification with-icon new-reviewables",
icon: "flag",
omitSpan: true,
title: "notifications.tooltip.new_reviewable",
titleOptions: { count: user.unseen_reviewable_count },
attributes: {
"aria-label": I18n.t("notifications.tooltip.new_reviewable", {
count: user.unseen_reviewable_count,
}),
},
})
);
} else if (user.all_unread_notifications_count) {
contents.push(
this.attach("link", {
action: attrs.action,
className: "badge-notification unread-notifications",
rawLabel: user.all_unread_notifications_count,
omitSpan: true,
title: "notifications.tooltip.regular",
titleOptions: { count: user.all_unread_notifications_count },
attributes: {
"aria-label": I18n.t("user.notifications"),
},
})
);
}
}
return contents;
},
_shouldHighlightAvatar() {
const attrs = this.attrs;
const { user } = attrs;
return (
!user.read_first_notification &&
!user.enforcedSecondFactor &&
!attrs.active
);
},
});
createWidget(
"user-dropdown",
Object.assign(
{
tagName: "li.header-dropdown-toggle.current-user",
buildId() {
return "current-user";
},
html(attrs) {
return h(
"button.icon.btn.no-text.btn-flat",
{
attributes: {
"aria-haspopup": true,
"aria-expanded": attrs.active,
"aria-label": I18n.t("user.account_possessive", {
name: attrs.user.name || attrs.user.username,
}),
},
},
this.attach("header-notifications", attrs)
);
},
},
dropdown
)
);
createWidget(
"header-dropdown",
Object.assign(
{
tagName: "li.header-dropdown-toggle",
html(attrs) {
const title = I18n.t(attrs.title);
const body = [iconNode(attrs.icon)];
if (attrs.contents) {
body.push(attrs.contents.call(this));
}
return h(
"button.icon.btn.no-text.btn-flat",
{
attributes: {
"aria-expanded": attrs.active,
"aria-haspopup": true,
title,
"aria-label": title,
id: attrs.iconId,
},
},
body
);
},
},
dropdown
)
);
createWidget("header-icons", {
services: ["search", "header"],
tagName: "ul.icons.d-header-icons",
init() {
registerWidgetShim("extra-icon", "span.wrapper", hbs`<@data.component />`);
},
html(attrs) {
if (this.siteSettings.login_required && !this.currentUser) {
return [];
}
const icons = [];
const resolvedIcons = _extraHeaderIcons.resolve();
resolvedIcons.forEach((icon) => {
if (icon.key === "search") {
if (!this.header.headerButtonsHidden.includes("search")) {
icons.push(
this.attach("header-dropdown", {
title: "search.title",
icon: "search",
iconId: SEARCH_BUTTON_ID,
action: "toggleSearchMenu",
active: this.search.visible,
href: getURL("/search"),
classNames: ["search-dropdown"],
})
);
}
} else if (icon.key === "user-menu" && attrs.user) {
icons.push(
this.attach("user-dropdown", {
active: attrs.userVisible,
action: "toggleUserMenu",
user: attrs.user,
})
);
} else if (
icon.key === "hamburger" &&
(!attrs.sidebarEnabled || this.site.mobileView)
) {
icons.push(
this.attach("header-dropdown", {
title: "hamburger_menu",
icon: "bars",
iconId: "toggle-hamburger-menu",
active: attrs.hamburgerVisible,
action: "toggleHamburger",
href: "",
classNames: ["hamburger-dropdown"],
})
);
} else {
icons.push(this.attach("extra-icon", { component: icon.value }));
}
});
return icons;
},
});
createWidget("header-buttons", {
services: ["header"],
tagName: "span.auth-buttons",
html(attrs) {
if (this.currentUser) {
return;
}
const buttons = [];
if (
attrs.canSignUp &&
!attrs.topic &&
!this.header.headerButtonsHidden.includes("signup")
) {
buttons.push(
this.attach("button", {
label: "sign_up",
className: "btn-primary btn-small sign-up-button",
action: "showCreateAccount",
})
);
}
if (!this.header.headerButtonsHidden.includes("login")) {
buttons.push(
this.attach("button", {
label: "log_in",
className: "btn-primary btn-small login-button",
action: "showLogin",
icon: "user",
})
);
}
return buttons;
},
});
createWidget("header-cloak", {
tagName: "div.header-cloak",
html() {
return "";
},
click() {},
scheduleRerender() {},
});
let additionalPanels = [];
export function attachAdditionalPanel(name, toggle, transformAttrs) {
additionalPanels.push({ name, toggle, transformAttrs });
}
createWidget("hamburger-dropdown-wrapper", {
buildAttributes() {
return { "data-click-outside": true };
},
html() {
return [
new RenderGlimmer(
this,
"div.widget-component-connector",
hbs`<Sidebar::HamburgerDropdown />`
),
];
},
click(event) {
if (
event.target.closest(".sidebar-section-header-button") ||
event.target.closest(".sidebar-section-link")
) {
this.sendWidgetAction("toggleHamburger");
}
},
clickOutside(e) {
if (
e.target.classList.contains("header-cloak") &&
!window.matchMedia("(prefers-reduced-motion: reduce)").matches
) {
const panel = document.querySelector(".menu-panel");
const headerCloak = document.querySelector(".header-cloak");
const finishPosition = isDocumentRTL() ? "340px" : "-340px";
panel
.animate([{ transform: `translate3d(${finishPosition}, 0, 0)` }], {
duration: 200,
fill: "forwards",
easing: "ease-in",
})
.finished.then(() => {
if (isTesting()) {
this.sendWidgetAction("toggleHamburger");
} else {
discourseLater(() => this.sendWidgetAction("toggleHamburger"));
}
});
headerCloak.animate([{ opacity: 0 }], {
duration: 200,
fill: "forwards",
easing: "ease-in",
});
} else {
this.sendWidgetAction("toggleHamburger");
}
},
});
createWidget("revamped-user-menu-wrapper", {
buildAttributes() {
return { "data-click-outside": true };
},
html() {
return [
new RenderGlimmer(
this,
"div.widget-component-connector",
hbs`<UserMenu::Menu @closeUserMenu={{@data.closeUserMenu}} />`,
{
closeUserMenu: this.closeUserMenu.bind(this),
}
),
];
},
closeUserMenu() {
this.sendWidgetAction("toggleUserMenu");
},
clickOutside(e) {
if (
e.target.classList.contains("header-cloak") &&
!window.matchMedia("(prefers-reduced-motion: reduce)").matches
) {
const panel = document.querySelector(".menu-panel");
const headerCloak = document.querySelector(".header-cloak");
const finishPosition = isDocumentRTL() ? "-340px" : "340px";
panel
.animate([{ transform: `translate3d(${finishPosition}, 0, 0)` }], {
duration: 200,
fill: "forwards",
easing: "ease-in",
})
.finished.then(() => {
if (isTesting) {
this.closeUserMenu();
} else {
discourseLater(() => this.closeUserMenu());
}
});
headerCloak.animate([{ opacity: 0 }], {
duration: 200,
fill: "forwards",
easing: "ease-in",
});
} else {
this.closeUserMenu();
}
},
});
createWidget("search-menu-wrapper", {
services: ["search"],
buildAttributes() {
return { "aria-live": "polite" };
},
buildClasses() {
return ["search-menu"];
},
html() {
return [
new RenderGlimmer(
this,
"div.widget-component-connector",
hbs`<SearchMenuPanel @closeSearchMenu={{@data.closeSearchMenu}} />`,
{
closeSearchMenu: this.closeSearchMenu.bind(this),
}
),
];
},
closeSearchMenu() {
this.sendWidgetAction("toggleSearchMenu");
document.getElementById(SEARCH_BUTTON_ID)?.focus();
},
clickOutside() {
this.closeSearchMenu();
},
});
export default createWidget("header", {
tagName: "header.d-header",
buildKey: () => `header`,
services: ["router", "search"],
init() {
registerWidgetShim(
"extra-button",
"span.wrapper",
hbs`<@data.component />`
);
},
defaultState() {
let states = {
searchVisible: false,
hamburgerVisible: false,
userVisible: false,
inTopicContext: false,
};
if (this.site.mobileView) {
states.skipSearchContext = true;
}
return states;
},
html(attrs, state) {
let inTopicRoute = false;
if (this.search.inTopicContext) {
inTopicRoute = this.router.currentRouteName.startsWith("topic.");
}
let contents = () => {
const headerIcons = this.attach("header-icons", {
hamburgerVisible: state.hamburgerVisible,
userVisible: state.userVisible,
searchVisible: this.search.visible,
flagCount: attrs.flagCount,
user: this.currentUser,
sidebarEnabled: attrs.sidebarEnabled,
});
if (attrs.onlyIcons) {
return headerIcons;
}
const buttons = [];
const resolvedButtons = _extraHeaderButtons.resolve();
resolvedButtons.forEach((button) => {
if (button.key === "auth") {
buttons.push(this.attach("header-buttons", attrs));
}
buttons.push(this.attach("extra-button", { component: button.value }));
});
const panels = [];
panels.push(h("span.header-buttons", buttons), headerIcons);
if (this.search.visible) {
this.search.inTopicContext = this.search.inTopicContext && inTopicRoute;
panels.push(this.attach("search-menu-wrapper"));
} else if (state.hamburgerVisible) {
panels.push(this.attach("hamburger-dropdown-wrapper", {}));
} else if (state.userVisible) {
panels.push(this.attach("revamped-user-menu-wrapper", {}));
}
additionalPanels.map((panel) => {
if (this.state[panel.toggle]) {
panels.push(
this.attach(
panel.name,
panel.transformAttrs.call(this, attrs, state)
)
);
}
});
if (this.site.mobileView || this.site.narrowDesktopView) {
panels.push(this.attach("header-cloak"));
}
return panels;
};
const contentsAttrs = {
contents,
minimized: !!attrs.topic,
};
return [
h(
"div.wrap",
this.attach("header-contents", { ...attrs, ...contentsAttrs })
),
new RenderGlimmer(
this,
"div.widget-component-connector",
hbs`
<PluginOutlet
@name="after-header"
@outletArgs={{hash minimized=@data.minimized}}
/>
`,
{ minimized: !!attrs.topic }
),
];
},
updateHighlight() {
if (!this.search.visible) {
this.search.highlightTerm = "";
}
},
closeAll() {
this.state.userVisible = false;
this.state.hamburgerVisible = false;
this.search.visible = false;
this.toggleBodyScrolling(false);
},
toggleSearchMenu() {
if (this.site.mobileView) {
const context = this.search.searchContext;
let params = "";
if (context) {
params = `?context=${context.type}&context_id=${context.id}&skip_context=${this.state.skipSearchContext}`;
}
if (this.router.currentRouteName === "full-page-search") {
scrollTop();
$(".full-page-search").focus();
return false;
} else {
return DiscourseURL.routeTo("/search" + params);
}
}
this.search.visible = !this.search.visible;
this.updateHighlight();
if (!this.search.searchVisible) {
this.search.inTopicContext = false;
}
},
toggleUserMenu() {
this.state.userVisible = !this.state.userVisible;
this.toggleBodyScrolling(this.state.userVisible);
// auto focus on first button in dropdown
schedule("afterRender", () =>
document.querySelector(".user-menu button")?.focus()
);
},
toggleHamburger() {
if (this.attrs.sidebarEnabled && !this.site.narrowDesktopView) {
if (!this.attrs.showSidebar) {
this.sendWidgetAction("toggleSidebar");
this.closeAll();
} else {
this.state.hamburgerVisible = !this.state.hamburgerVisible;
}
} else {
this.state.hamburgerVisible = !this.state.hamburgerVisible;
this.toggleBodyScrolling(this.state.hamburgerVisible);
schedule("afterRender", () => {
// Remove focus from hamburger toggle button
document.querySelector("#toggle-hamburger-menu")?.blur();
});
}
},
toggleBodyScrolling(bool) {
if (this.site.mobileView) {
scrollLock(bool);
}
},
togglePageSearch() {
this.search.inTopicContext = false;
let showSearch = this.router.currentRouteName.startsWith("topic.");
// If we're viewing a topic, only intercept search if there are cloaked posts
if (showSearch) {
const controller = this.register.lookup("controller:topic");
const total = controller.get("model.postStream.stream.length") || 0;
const chunkSize = controller.get("model.chunk_size") || 0;
showSearch =
total > chunkSize &&
$(".topic-post .cooked, .small-action:not(.time-gap)").length < total;
}
if (this.search.visible) {
this.toggleSearchMenu();
return showSearch;
}
if (showSearch) {
this.search.inTopicContext = true;
this.toggleSearchMenu();
return false;
}
return true;
},
domClean() {
const { state } = this;
if (this.search.visible || state.hamburgerVisible || state.userVisible) {
this.closeAll();
}
},
headerKeyboardTrigger(msg) {
switch (msg.type) {
case "search":
this.toggleSearchMenu();
break;
case "user":
this.toggleUserMenu();
break;
case "hamburger":
this.toggleHamburger();
break;
case "page-search":
if (!this.togglePageSearch()) {
msg.event.preventDefault();
msg.event.stopPropagation();
}
break;
}
},
});

View File

@ -1,12 +0,0 @@
import { hbs } from "ember-cli-htmlbars";
import { registerWidgetShim } from "discourse/widgets/render-glimmer";
registerWidgetShim(
"home-logo-wrapper-outlet",
"div.home-logo-wrapper-outlet",
hbs`<PluginOutlet @name="home-logo-wrapper">
<PluginOutlet @name="home-logo" @outletArgs={{hash minimized=@data.topic}}>
<MountWidget @widget="home-logo" @args={{@data}} />
</PluginOutlet>
</PluginOutlet>`
);

View File

@ -1,152 +0,0 @@
// deprecated in favor of components/header/home-logo.gjs
import { h } from "virtual-dom";
import { wantsNewWindow } from "discourse/lib/intercept-click";
import { applyValueTransformer } from "discourse/lib/transformer";
import DiscourseURL from "discourse/lib/url";
import Session from "discourse/models/session";
import { createWidget } from "discourse/widgets/widget";
import getURL from "discourse-common/lib/get-url";
import { iconNode } from "discourse-common/lib/icon-library";
export default createWidget("home-logo", {
services: ["session"],
tagName: "div.title",
settings: {
href: getURL("/"),
},
buildClasses() {
if (this.attrs.minimized) {
return "title--minimized";
}
},
href() {
const href = this.settings.href;
return applyValueTransformer(
"home-logo-href",
typeof href === "function" ? href() : href
);
},
logoUrl(opts = {}) {
return this.logoResolver("logo", opts);
},
mobileLogoUrl(opts = {}) {
return this.logoResolver("mobile_logo", opts);
},
smallLogoUrl(opts = {}) {
return this.logoResolver("logo_small", opts);
},
logo() {
const darkModeOptions = this.session.darkModeAvailable
? { dark: true }
: {};
const mobileLogoUrl = this.mobileLogoUrl(),
mobileLogoUrlDark = this.mobileLogoUrl(darkModeOptions);
const showMobileLogo = this.site.mobileView && mobileLogoUrl.length > 0;
const logoUrl = this.logoUrl(),
logoUrlDark = this.logoUrl(darkModeOptions);
const title = this.siteSettings.title;
if (this.attrs.minimized) {
const logoSmallUrl = this.smallLogoUrl(),
logoSmallUrlDark = this.smallLogoUrl(darkModeOptions);
if (logoSmallUrl.length) {
return this.logoElement(
"logo-small",
logoSmallUrl,
title,
logoSmallUrlDark
);
} else {
return iconNode("home");
}
} else if (showMobileLogo) {
return this.logoElement(
"logo-mobile",
mobileLogoUrl,
title,
mobileLogoUrlDark
);
} else if (logoUrl.length) {
return this.logoElement("logo-big", logoUrl, title, logoUrlDark);
} else {
return h("h1#site-text-logo.text-logo", { key: "logo-text" }, title);
}
},
logoResolver(name, opts = {}) {
const { siteSettings } = this;
// get alternative logos for browser dark dark mode switching
if (opts.dark) {
return siteSettings[`site_${name}_dark_url`];
}
// try dark logos first when color scheme is dark
// this is independent of browser dark mode
// hence the fallback to normal logos
if (Session.currentProp("defaultColorSchemeIsDark")) {
return (
siteSettings[`site_${name}_dark_url`] ||
siteSettings[`site_${name}_url`] ||
""
);
}
return siteSettings[`site_${name}_url`] || "";
},
logoElement(key, url, title, darkUrl = null) {
const attributes =
key === "logo-small"
? { src: getURL(url), width: 36, alt: title }
: { src: getURL(url), alt: title };
const imgElement = h(`img#site-logo.${key}`, {
key,
attributes,
});
if (darkUrl && url !== darkUrl) {
return h("picture", [
h("source", {
attributes: {
srcset: getURL(darkUrl),
media: "(prefers-color-scheme: dark)",
},
}),
imgElement,
]);
}
return imgElement;
},
html() {
return h(
"a",
{ attributes: { href: this.href(), "data-auto-route": true } },
this.logo()
);
},
click(e) {
if (wantsNewWindow(e)) {
return false;
}
e.preventDefault();
DiscourseURL.routeToTag(e.target.closest("a"));
return false;
},
});

View File

@ -1,25 +0,0 @@
// Deprecated in favor of app/assets/javascripts/discourse/app/components/header/sidebar-toggle.gjs
import { createWidget } from "discourse/widgets/widget";
export default createWidget("sidebar-toggle", {
tagName: "span.header-sidebar-toggle",
html() {
const attrs = this.attrs;
return [
this.attach("button", {
title: "sidebar.title",
icon: "bars",
action: this.site.narrowDesktopView
? "toggleHamburger"
: "toggleSidebar",
className: `btn btn-flat btn-sidebar-toggle ${
this.site.narrowDesktopView ? "narrow-desktop" : ""
}`,
ariaExpanded: attrs.showSidebar ? "true" : "false",
ariaControls: "d-sidebar",
}),
];
},
});

View File

@ -1,20 +0,0 @@
// deprecated in favor of app/components/header/user-dropdown/user-status-bubble.gjs
import { createWidget } from "discourse/widgets/widget";
import I18n from "discourse-i18n";
export default createWidget("user-status-bubble", {
tagName: "div.user-status-background",
html(attrs) {
let title = attrs.description;
if (attrs.ends_at) {
const until = moment
.tz(attrs.ends_at, this.currentUser.user_option.timezone)
.format(I18n.t("dates.long_date_without_year"));
title += `\n${I18n.t("until")} ${until}`;
}
return this.attach("emoji", { name: attrs.emoji, title });
},
});

View File

@ -6,34 +6,6 @@ import { acceptance } from "discourse/tests/helpers/qunit-helpers";
// TODO: Consolidate these tests into a single acceptance test once the Glimmer // TODO: Consolidate these tests into a single acceptance test once the Glimmer
// header is the default. // header is the default.
acceptance("Header API - authenticated", function (needs) {
needs.user();
needs.settings({
glimmer_header_mode: "disabled",
});
test("can add buttons to the header", async function (assert) {
withPluginApi("1.29.0", (api) => {
api.headerButtons.add("test", <template>
<button class="test-button">Test</button>
</template>);
});
await visit("/");
assert.dom("button.test-button").exists("button is displayed");
});
test("can add icons to the header", async function (assert) {
withPluginApi("1.29.0", (api) => {
api.headerIcons.add("test", <template>
<span class="test-icon">Test</span>
</template>);
});
await visit("/");
assert.dom(".test-icon").exists("icon is displayed");
});
});
acceptance("Header API - anonymous", function () { acceptance("Header API - anonymous", function () {
test("can add buttons to the header", async function (assert) { test("can add buttons to the header", async function (assert) {
@ -95,9 +67,6 @@ acceptance("Header API - anonymous", function () {
acceptance("Glimmer Header API - authenticated", function (needs) { acceptance("Glimmer Header API - authenticated", function (needs) {
needs.user({ groups: AUTO_GROUPS.everyone }); needs.user({ groups: AUTO_GROUPS.everyone });
needs.settings({
glimmer_header_mode: "enabled",
});
test("can add buttons to the header", async function (assert) { test("can add buttons to the header", async function (assert) {
withPluginApi("1.29.0", (api) => { withPluginApi("1.29.0", (api) => {

View File

@ -65,9 +65,6 @@ async function triggerSwipeEnd({ x, y, touchTarget }) {
acceptance("Mobile - menu swipes", function (needs) { acceptance("Mobile - menu swipes", function (needs) {
needs.mobileView(); needs.mobileView();
needs.user(); needs.user();
needs.settings({
glimmer_header_mode: "enabled",
});
chromeTest("swipe to close hamburger", async function (assert) { chromeTest("swipe to close hamburger", async function (assert) {
await visit("/"); await visit("/");

View File

@ -89,10 +89,6 @@ import {
currentSettings, currentSettings,
mergeSettings, mergeSettings,
} from "discourse/tests/helpers/site-settings"; } from "discourse/tests/helpers/site-settings";
import {
clearExtraHeaderButtons,
clearExtraHeaderIcons,
} from "discourse/widgets/header";
import { resetDecorators as resetPostCookedDecorators } from "discourse/widgets/post-cooked"; import { resetDecorators as resetPostCookedDecorators } from "discourse/widgets/post-cooked";
import { resetPostMenuExtraButtons } from "discourse/widgets/post-menu"; import { resetPostMenuExtraButtons } from "discourse/widgets/post-menu";
import { resetDecorators } from "discourse/widgets/widget"; import { resetDecorators } from "discourse/widgets/widget";
@ -236,8 +232,6 @@ export function testCleanup(container, app) {
resetSidebarPanels(); resetSidebarPanels();
clearExtraGlimmerHeaderIcons(); clearExtraGlimmerHeaderIcons();
clearExtraGlimmerHeaderButtons(); clearExtraGlimmerHeaderButtons();
clearExtraHeaderIcons();
clearExtraHeaderButtons();
resetOnKeyUpCallbacks(); resetOnKeyUpCallbacks();
resetLogSearchLinkClickedCallbacks(); resetLogSearchLinkClickedCallbacks();
resetItemSelectCallbacks(); resetItemSelectCallbacks();

View File

@ -1,235 +0,0 @@
// deprecated in favor of spec/system/header/site_header_spec.rb
import {
click,
render,
settled,
triggerKeyEvent,
waitUntil,
} from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import { exists, query } from "discourse/tests/helpers/qunit-helpers";
import I18n from "discourse-i18n";
module("Integration | Component | site-header", function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function () {
this.currentUser.set("unread_high_priority_notifications", 1);
this.currentUser.set("read_first_notification", false);
});
test("unread notifications count rerenders when user's notifications count is updated", async function (assert) {
this.currentUser.set("all_unread_notifications_count", 1);
await render(hbs`<SiteHeader />`);
let unreadBadge = query(
".header-dropdown-toggle.current-user .unread-notifications"
);
assert.strictEqual(unreadBadge.textContent, "1");
this.currentUser.set("all_unread_notifications_count", 5);
await settled();
unreadBadge = query(
".header-dropdown-toggle.current-user .unread-notifications"
);
assert.strictEqual(unreadBadge.textContent, "5");
});
test("hamburger menu icon doesn't show pending reviewables count for non-legacy navigation menu", async function (assert) {
this.currentUser.set("reviewable_count", 1);
this.siteSettings.navigation_menu = "sidebar";
await render(hbs`<SiteHeader />`);
assert.ok(!exists(".hamburger-dropdown .badge-notification"));
});
test("clicking outside the revamped menu closes it", async function (assert) {
await render(hbs`<SiteHeader />`);
await click(".header-dropdown-toggle.current-user");
assert.ok(exists(".user-menu.revamped"));
await click("header.d-header");
assert.ok(!exists(".user-menu.revamped"));
});
test("header's height is setting css property", async function (assert) {
await render(hbs`<SiteHeader />`);
function getProperty() {
const rawValue = getComputedStyle(document.body).getPropertyValue(
"--header-offset"
);
const roundedValue = Math.floor(parseFloat(rawValue));
return roundedValue + "px";
}
document.querySelector(".d-header").style.height = 90 + "px";
await waitUntil(() => getProperty() === "90px", { timeout: 100 });
assert.strictEqual(getProperty(), "90px");
document.querySelector(".d-header").style.height = 60 + "px";
await waitUntil(() => getProperty() === "60px", { timeout: 100 });
assert.strictEqual(getProperty(), "60px");
});
test("arrow up/down keys move focus between the tabs", async function (assert) {
this.currentUser.set("can_send_private_messages", true);
await render(hbs`<SiteHeader />`);
await click(".header-dropdown-toggle.current-user");
let activeTab = query(".menu-tabs-container .btn.active");
assert.strictEqual(activeTab.id, "user-menu-button-all-notifications");
await triggerKeyEvent(document, "keydown", "ArrowDown");
let focusedTab = document.activeElement;
assert.strictEqual(
focusedTab.id,
"user-menu-button-replies",
"pressing the down arrow key moves focus to the next tab towards the bottom"
);
await triggerKeyEvent(document, "keydown", "ArrowDown");
await triggerKeyEvent(document, "keydown", "ArrowDown");
await triggerKeyEvent(document, "keydown", "ArrowDown");
await triggerKeyEvent(document, "keydown", "ArrowDown");
await triggerKeyEvent(document, "keydown", "ArrowDown");
focusedTab = document.activeElement;
assert.strictEqual(
focusedTab.id,
"user-menu-button-profile",
"the down arrow key can move the focus to the bottom tabs"
);
await triggerKeyEvent(document, "keydown", "ArrowDown");
focusedTab = document.activeElement;
assert.strictEqual(
focusedTab.id,
"user-menu-button-all-notifications",
"the focus moves back to the top after reaching the bottom"
);
await triggerKeyEvent(document, "keydown", "ArrowUp");
focusedTab = document.activeElement;
assert.strictEqual(
focusedTab.id,
"user-menu-button-profile",
"the up arrow key moves the focus in the opposite direction"
);
});
test("new personal messages bubble is prioritized over unseen reviewables and regular notifications bubbles", async function (assert) {
this.currentUser.set("all_unread_notifications_count", 5);
this.currentUser.set("new_personal_messages_notifications_count", 2);
this.currentUser.set("unseen_reviewable_count", 3);
await render(hbs`<SiteHeader />`);
assert.notOk(
exists(
".header-dropdown-toggle.current-user .badge-notification.unread-notifications"
),
"regular notifications bubble isn't displayed when there are new personal messages notifications"
);
assert.notOk(
exists(
".header-dropdown-toggle.current-user .badge-notification.with-icon.new-reviewables"
),
"reviewables bubble isn't displayed when there are new personal messages notifications"
);
const pmsBubble = query(
".header-dropdown-toggle.current-user .badge-notification.with-icon.new-pms"
);
assert.strictEqual(
pmsBubble.textContent.trim(),
"",
"personal messages bubble has no count"
);
assert.ok(
pmsBubble.querySelector(".d-icon-envelope"),
"personal messages bubble has envelope icon"
);
assert.strictEqual(
pmsBubble.title,
I18n.t("notifications.tooltip.new_message_notification", { count: 2 }),
"personal messages bubble bubble has a title"
);
});
test("unseen reviewables bubble is prioritized over regular notifications", async function (assert) {
this.currentUser.set("all_unread_notifications_count", 5);
this.currentUser.set("new_personal_messages_notifications_count", 0);
this.currentUser.set("unseen_reviewable_count", 3);
await render(hbs`<SiteHeader />`);
assert.notOk(
exists(
".header-dropdown-toggle.current-user .badge-notification.unread-notifications"
),
"regular notifications bubble isn't displayed when there are unseen reviewables notifications"
);
const reviewablesBubble = query(
".header-dropdown-toggle.current-user .badge-notification.with-icon.new-reviewables"
);
assert.strictEqual(
reviewablesBubble.textContent.trim(),
"",
"reviewables bubble has no count"
);
assert.ok(
reviewablesBubble.querySelector(".d-icon-flag"),
"reviewables bubble has flag icon"
);
assert.strictEqual(
reviewablesBubble.title,
I18n.t("notifications.tooltip.new_reviewable", { count: 3 }),
"reviewables bubble has a title"
);
assert.notOk(
exists(
".header-dropdown-toggle.current-user .badge-notification.with-icon.new-pms"
),
"personal messages bubble isn't displayed"
);
});
test("regular notifications bubble is shown if there are neither new personal messages nor unseen reviewables", async function (assert) {
this.currentUser.set("all_unread_notifications_count", 5);
this.currentUser.set("new_personal_messages_notifications_count", 0);
this.currentUser.set("unseen_reviewable_count", 0);
await render(hbs`<SiteHeader />`);
const regularNotificationsBubble = query(
".header-dropdown-toggle.current-user .badge-notification.unread-notifications"
);
assert.strictEqual(
regularNotificationsBubble.textContent,
"5",
"regular notifications bubble has a count"
);
assert.strictEqual(
regularNotificationsBubble.title,
I18n.t("notifications.tooltip.regular", { count: 5 }),
"regular notifications bubble has a title"
);
assert.notOk(
exists(
".header-dropdown-toggle.current-user .badge-notification.with-icon.new-reviewables"
),
"reviewables bubble isn't displayed"
);
assert.notOk(
exists(
".header-dropdown-toggle.current-user .badge-notification.with-icon.new-pms"
),
"personal messages bubble isn't displayed"
);
});
});

View File

@ -1,87 +0,0 @@
// deprecated in favor of spec/system/header/header_spec.rb
import { click, render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import { exists } from "discourse/tests/helpers/qunit-helpers";
module("Integration | Component | Widget | header", function (hooks) {
setupRenderingTest(hooks);
test("rendering basics", async function (assert) {
await render(hbs`<MountWidget @widget="header" />`);
assert.ok(exists("header.d-header"));
assert.ok(exists("#site-logo"));
});
test("sign up / login buttons", async function (assert) {
this.owner.unregister("service:current-user");
this.set("args", { canSignUp: true });
this.set("showCreateAccount", () => (this.signupShown = true));
this.set("showLogin", () => (this.loginShown = true));
await render(hbs`
<MountWidget
@widget="header"
@showCreateAccount={{this.showCreateAccount}}
@showLogin={{this.showLogin}}
@args={{this.args}}
/>
`);
assert.ok(exists("button.sign-up-button"));
assert.ok(exists("button.login-button"));
await click("button.sign-up-button");
assert.ok(this.signupShown);
await click("button.login-button");
assert.ok(this.loginShown);
});
test("anon when login required", async function (assert) {
this.owner.unregister("service:current-user");
this.set("args", { canSignUp: true });
this.set("showCreateAccount", () => (this.signupShown = true));
this.set("showLogin", () => (this.loginShown = true));
this.siteSettings.login_required = true;
await render(hbs`
<MountWidget
@widget="header"
@showCreateAccount={{this.showCreateAccount}}
@showLogin={{this.showLogin}}
@args={{this.args}}
/>
`);
assert.ok(exists("button.login-button"));
assert.ok(exists("button.sign-up-button"));
assert.ok(!exists("#search-button"));
assert.ok(!exists("#toggle-hamburger-menu"));
});
test("logged in when login required", async function (assert) {
this.set("args", { canSignUp: true });
this.set("showCreateAccount", () => (this.signupShown = true));
this.set("showLogin", () => (this.loginShown = true));
this.siteSettings.login_required = true;
await render(hbs`
<MountWidget
@widget="header"
@showCreateAccount={{this.showCreateAccount}}
@showLogin={{this.showLogin}}
@args={{this.args}}
/>
`);
assert.ok(!exists("button.login-button"));
assert.ok(!exists("button.sign-up-button"));
assert.ok(exists("#search-button"));
assert.ok(exists("#toggle-hamburger-menu"));
assert.ok(exists("#current-user"));
});
});

View File

@ -1,231 +0,0 @@
// deprecated in favor of discourse/tests/integration/components/home-logo-test.gjs
import { getOwner } from "@ember/owner";
import { render } from "@ember/test-helpers";
import { module, test } from "qunit";
import MountWidget from "discourse/components/mount-widget";
import { withPluginApi } from "discourse/lib/plugin-api";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import { count, exists, query } from "discourse/tests/helpers/qunit-helpers";
const bigLogo = "/images/d-logo-sketch.png?test";
const smallLogo = "/images/d-logo-sketch-small.png?test";
const mobileLogo = "/images/d-logo-sketch.png?mobile";
const darkLogo = "/images/d-logo-sketch.png?dark";
const title = "Cool Forum";
const prefersDark = "(prefers-color-scheme: dark)";
module("Integration | Component | Widget | home-logo", function (hooks) {
setupRenderingTest(hooks);
hooks.afterEach(function () {
this.session = getOwner(this).lookup("service:session");
this.session.set("darkModeAvailable", null);
this.session.set("defaultColorSchemeIsDark", null);
});
test("basics", async function (assert) {
this.siteSettings.site_logo_url = bigLogo;
this.siteSettings.site_logo_small_url = smallLogo;
this.siteSettings.title = title;
const args = { minimized: false };
await render(<template>
<MountWidget @widget="home-logo" @args={{args}} />
</template>);
assert.strictEqual(count(".title"), 1);
assert.strictEqual(count("img#site-logo.logo-big"), 1);
assert.strictEqual(query("#site-logo").getAttribute("src"), bigLogo);
assert.strictEqual(query("#site-logo").getAttribute("alt"), title);
});
test("basics - minimized", async function (assert) {
this.siteSettings.site_logo_url = bigLogo;
this.siteSettings.site_logo_small_url = smallLogo;
this.siteSettings.title = title;
const args = { minimized: true };
await render(<template>
<MountWidget @widget="home-logo" @args={{args}} />
</template>);
assert.strictEqual(count("img.logo-small"), 1);
assert.strictEqual(query("img.logo-small").getAttribute("src"), smallLogo);
assert.strictEqual(query("img.logo-small").getAttribute("alt"), title);
assert.strictEqual(query("img.logo-small").getAttribute("width"), "36");
});
test("no logo", async function (assert) {
this.siteSettings.site_logo_url = "";
this.siteSettings.site_logo_small_url = "";
this.siteSettings.title = title;
const args = { minimized: false };
await render(<template>
<MountWidget @widget="home-logo" @args={{args}} />
</template>);
assert.strictEqual(count("h1#site-text-logo.text-logo"), 1);
assert.strictEqual(query("#site-text-logo").innerText, title);
});
test("no logo - minimized", async function (assert) {
this.siteSettings.site_logo_url = "";
this.siteSettings.site_logo_small_url = "";
this.siteSettings.title = title;
const args = { minimized: true };
await render(<template>
<MountWidget @widget="home-logo" @args={{args}} />
</template>);
assert.strictEqual(count(".d-icon-home"), 1);
});
test("mobile logo", async function (assert) {
this.siteSettings.site_mobile_logo_url = mobileLogo;
this.siteSettings.site_logo_small_url = smallLogo;
this.site.mobileView = true;
await render(<template><MountWidget @widget="home-logo" /></template>);
assert.strictEqual(count("img#site-logo.logo-mobile"), 1);
assert.strictEqual(query("#site-logo").getAttribute("src"), mobileLogo);
});
test("mobile without logo", async function (assert) {
this.siteSettings.site_logo_url = bigLogo;
this.site.mobileView = true;
await render(<template><MountWidget @widget="home-logo" /></template>);
assert.strictEqual(count("img#site-logo.logo-big"), 1);
assert.strictEqual(query("#site-logo").getAttribute("src"), bigLogo);
});
test("logo with dark mode alternative", async function (assert) {
this.siteSettings.site_logo_url = bigLogo;
this.siteSettings.site_logo_dark_url = darkLogo;
this.session.set("darkModeAvailable", true);
await render(<template><MountWidget @widget="home-logo" /></template>);
assert.strictEqual(count("img#site-logo.logo-big"), 1);
assert.strictEqual(query("#site-logo").getAttribute("src"), bigLogo);
assert.strictEqual(
query("picture source").getAttribute("media"),
prefersDark,
"includes dark mode media attribute"
);
assert.strictEqual(
query("picture source").getAttribute("srcset"),
darkLogo,
"includes dark mode alternative logo source"
);
});
test("mobile logo with dark mode alternative", async function (assert) {
this.siteSettings.site_logo_url = bigLogo;
this.siteSettings.site_mobile_logo_url = mobileLogo;
this.siteSettings.site_mobile_logo_dark_url = darkLogo;
this.session.set("darkModeAvailable", true);
this.site.mobileView = true;
await render(<template><MountWidget @widget="home-logo" /></template>);
assert.strictEqual(query("#site-logo").getAttribute("src"), mobileLogo);
assert.strictEqual(
query("picture source").getAttribute("media"),
prefersDark,
"includes dark mode media attribute"
);
assert.strictEqual(
query("picture source").getAttribute("srcset"),
darkLogo,
"includes dark mode alternative logo source"
);
});
test("dark mode enabled but no dark logo set", async function (assert) {
this.siteSettings.site_logo_url = bigLogo;
this.siteSettings.site_logo_dark_url = "";
this.session.set("darkModeAvailable", true);
await render(<template><MountWidget @widget="home-logo" /></template>);
assert.strictEqual(count("img#site-logo.logo-big"), 1);
assert.strictEqual(query("#site-logo").getAttribute("src"), bigLogo);
assert.ok(!exists("picture"), "does not include alternative logo");
});
test("dark logo set but no dark mode", async function (assert) {
this.siteSettings.site_logo_url = bigLogo;
this.siteSettings.site_logo_dark_url = darkLogo;
await render(<template><MountWidget @widget="home-logo" /></template>);
assert.strictEqual(count("img#site-logo.logo-big"), 1);
assert.strictEqual(query("#site-logo").getAttribute("src"), bigLogo);
assert.ok(!exists("picture"), "does not include alternative logo");
});
test("dark color scheme and dark logo set", async function (assert) {
this.siteSettings.site_logo_url = bigLogo;
this.siteSettings.site_logo_dark_url = darkLogo;
this.session.set("defaultColorSchemeIsDark", true);
await render(<template><MountWidget @widget="home-logo" /></template>);
assert.strictEqual(count("img#site-logo.logo-big"), 1);
assert.strictEqual(
query("#site-logo").getAttribute("src"),
darkLogo,
"uses dark logo"
);
assert.ok(!exists("picture"), "does not add dark mode alternative");
});
test("dark color scheme and dark logo not set", async function (assert) {
this.siteSettings.site_logo_url = bigLogo;
this.siteSettings.site_logo_dark_url = "";
this.session.set("defaultColorSchemeIsDark", true);
await render(<template><MountWidget @widget="home-logo" /></template>);
assert.strictEqual(count("img#site-logo.logo-big"), 1);
assert.strictEqual(
query("#site-logo").getAttribute("src"),
bigLogo,
"uses regular logo on dark scheme if no dark logo"
);
});
test("the home logo href url defaults to /", async function (assert) {
await render(<template><MountWidget @widget="home-logo" /></template>);
const anchorElement = query("#site-logo").closest("a");
assert.strictEqual(
anchorElement.getAttribute("href"),
"/",
"home logo href equals /"
);
});
test("api.registerHomeLogoHrefCallback can be used to change the logo href url", async function (assert) {
withPluginApi("1.32.0", (api) => {
api.registerHomeLogoHrefCallback(() => "https://example.com");
});
await render(<template><MountWidget @widget="home-logo" /></template>);
const anchorElement = query("#site-logo").closest("a");
assert.strictEqual(
anchorElement.getAttribute("href"),
"https://example.com",
"home logo href equals the one set by the callback"
);
});
});

View File

@ -2692,7 +2692,6 @@ en:
enable_experimental_lightbox: "EXPERIMENTAL: Replace the default image lightbox with the revamped design." enable_experimental_lightbox: "EXPERIMENTAL: Replace the default image lightbox with the revamped design."
enable_experimental_bookmark_redesign_groups: "EXPERIMENTAL: Show a quick access menu for bookmarks on posts and a new redesigned modal" enable_experimental_bookmark_redesign_groups: "EXPERIMENTAL: Show a quick access menu for bookmarks on posts and a new redesigned modal"
experimental_redesigned_about_page_groups: "EXPERIMENTAL: Enable the redesigned /about page for specific groups." experimental_redesigned_about_page_groups: "EXPERIMENTAL: Enable the redesigned /about page for specific groups."
glimmer_header_mode: "Control whether the new 'glimmer' header implementation is used. 'auto' will enable automatically once all your themes and plugins are ready. https://meta.discourse.org/t/316549"
experimental_glimmer_topic_list_groups: "EXPERIMENTAL: Enable the new 'glimmer' topic list implementation. This implementation is under active development, and is not intended for production use. Do not develop themes/plugins against it until the implementation is finalized and announced." experimental_glimmer_topic_list_groups: "EXPERIMENTAL: Enable the new 'glimmer' topic list implementation. This implementation is under active development, and is not intended for production use. Do not develop themes/plugins against it until the implementation is finalized and announced."
experimental_form_templates: "EXPERIMENTAL: Enable the form templates feature. <b>After enabled,</b> manage the templates at <a href='%{base_path}/admin/customize/form-templates'>Customize / Templates</a>." experimental_form_templates: "EXPERIMENTAL: Enable the form templates feature. <b>After enabled,</b> manage the templates at <a href='%{base_path}/admin/customize/form-templates'>Customize / Templates</a>."
admin_sidebar_enabled_groups: "Enable sidebar navigation for the admin UI for the specified groups, which replaces the top-level admin navigation buttons." admin_sidebar_enabled_groups: "Enable sidebar navigation for the admin UI for the specified groups, which replaces the top-level admin navigation buttons."

View File

@ -2432,14 +2432,6 @@ developer:
instrument_gc_stat_per_request: instrument_gc_stat_per_request:
default: false default: false
hidden: true hidden: true
glimmer_header_mode:
client: true
type: enum
choices:
- disabled
- auto
- enabled
default: enabled
admin_sidebar_enabled_groups: admin_sidebar_enabled_groups:
type: group_list type: group_list
list_type: compact list_type: compact

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
#
class RemoveGlimmerHeaderModeSetting < ActiveRecord::Migration[7.1]
def up
execute <<~SQL
DELETE FROM site_settings
WHERE name = 'glimmer_header_mode'
SQL
end
def down
raise ActiveRecord::IrreversibleMigration
end
end

View File

@ -5,7 +5,6 @@ RSpec.describe "Glimmer Header", type: :system do
let(:search) { PageObjects::Pages::Search.new } let(:search) { PageObjects::Pages::Search.new }
fab!(:current_user) { Fabricate(:user) } fab!(:current_user) { Fabricate(:user) }
fab!(:topic) fab!(:topic)
before { SiteSetting.glimmer_header_mode = "enabled" }
it "renders basics" do it "renders basics" do
visit "/" visit "/"

View File

@ -1,22 +0,0 @@
# frozen_string_literal: true
RSpec.describe "Legacy Widget Header", type: :system do
before { SiteSetting.glimmer_header_mode = "disabled" }
context "when resetting password" do
fab!(:current_user) { Fabricate(:user) }
it "does not show search, login, or signup buttons" do
email_token =
current_user.email_tokens.create!(
email: current_user.email,
scope: EmailToken.scopes[:password_reset],
)
visit "/u/password-reset/#{email_token.token}"
expect(page).not_to have_css("button.login-button")
expect(page).not_to have_css("button.sign-up-button")
expect(page).not_to have_css(".search-dropdown #search-button")
end
end
end