From 7c3ad27de6e51e52b5e24078e287c783147c841d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Saquetim?= <1108771+megothss@users.noreply.github.com> Date: Wed, 4 Sep 2024 14:50:53 -0300 Subject: [PATCH] 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 --- .../discourse/app/components/site-header.js | 558 ------------- .../discourse/app/lib/plugin-api.gjs | 86 +- .../discourse/app/services/header.js | 27 - .../discourse/app/templates/application.hbs | 37 +- .../app/widgets/after-header-panel-outlet.js | 8 - .../app/widgets/before-header-logo-outlet.js | 8 - .../app/widgets/before-header-panel-outlet.js | 8 - .../app/widgets/header-bootstrap-mode.js | 8 - .../discourse/app/widgets/header-contents.js | 36 - .../app/widgets/header-topic-info.js | 260 ------- .../app/widgets/header-user-tip-shim.js | 8 - .../discourse/app/widgets/header.js | 735 ------------------ .../app/widgets/home-logo-wrapper-outlet.js | 12 - .../discourse/app/widgets/home-logo.js | 152 ---- .../discourse/app/widgets/sidebar-toggle.js | 25 - .../app/widgets/user-status-bubble.js | 20 - .../tests/acceptance/header-api-test.gjs | 31 - .../tests/acceptance/mobile-pan-test.js | 3 - .../discourse/tests/helpers/qunit-helpers.js | 6 - .../components/site-header-test.js | 235 ------ .../components/widgets/header-test.js | 87 --- .../components/widgets/home-logo-test.gjs | 231 ------ config/locales/server.en.yml | 1 - config/site_settings.yml | 8 - ...4500_remove_glimmer_header_mode_setting.rb | 14 + spec/system/header_spec.rb | 1 - spec/system/legacy_widget_header_spec.rb | 22 - 27 files changed, 54 insertions(+), 2573 deletions(-) delete mode 100644 app/assets/javascripts/discourse/app/components/site-header.js delete mode 100644 app/assets/javascripts/discourse/app/widgets/after-header-panel-outlet.js delete mode 100644 app/assets/javascripts/discourse/app/widgets/before-header-logo-outlet.js delete mode 100644 app/assets/javascripts/discourse/app/widgets/before-header-panel-outlet.js delete mode 100644 app/assets/javascripts/discourse/app/widgets/header-bootstrap-mode.js delete mode 100644 app/assets/javascripts/discourse/app/widgets/header-contents.js delete mode 100644 app/assets/javascripts/discourse/app/widgets/header-topic-info.js delete mode 100644 app/assets/javascripts/discourse/app/widgets/header-user-tip-shim.js delete mode 100644 app/assets/javascripts/discourse/app/widgets/header.js delete mode 100644 app/assets/javascripts/discourse/app/widgets/home-logo-wrapper-outlet.js delete mode 100644 app/assets/javascripts/discourse/app/widgets/home-logo.js delete mode 100644 app/assets/javascripts/discourse/app/widgets/sidebar-toggle.js delete mode 100644 app/assets/javascripts/discourse/app/widgets/user-status-bubble.js delete mode 100644 app/assets/javascripts/discourse/tests/integration/components/site-header-test.js delete mode 100644 app/assets/javascripts/discourse/tests/integration/components/widgets/header-test.js delete mode 100644 app/assets/javascripts/discourse/tests/integration/components/widgets/home-logo-test.gjs create mode 100644 db/migrate/20240815234500_remove_glimmer_header_mode_setting.rb delete mode 100644 spec/system/legacy_widget_header_spec.rb diff --git a/app/assets/javascripts/discourse/app/components/site-header.js b/app/assets/javascripts/discourse/app/components/site-header.js deleted file mode 100644 index d51061671df..00000000000 --- a/app/assets/javascripts/discourse/app/components/site-header.js +++ /dev/null @@ -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(); - } - }, -}); diff --git a/app/assets/javascripts/discourse/app/lib/plugin-api.gjs b/app/assets/javascripts/discourse/app/lib/plugin-api.gjs index 470fe28a53e..a923fa31214 100644 --- a/app/assets/javascripts/discourse/app/lib/plugin-api.gjs +++ b/app/assets/javascripts/discourse/app/lib/plugin-api.gjs @@ -23,9 +23,7 @@ import { forceDropdownForMenuPanels as glimmerForceDropdownForMenuPanels } from import { addGlobalNotice } from "discourse/components/global-notice"; import { headerButtonsDAG } from "discourse/components/header"; import { headerIconsDAG } from "discourse/components/header/icons"; -import MountWidget, { - addWidgetCleanCallback, -} from "discourse/components/mount-widget"; +import { addWidgetCleanCallback } from "discourse/components/mount-widget"; import { addPluginOutletDecorator } from "discourse/components/plugin-connector"; import { addPluginReviewableParam, @@ -40,7 +38,6 @@ import { } from "discourse/components/search-menu/results/random-quick-tip"; 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 { forceDropdownForMenuPanels } from "discourse/components/site-header"; import { addTopicParticipantClassesCallback } from "discourse/components/topic-map/topic-participant"; import { setDesktopScrollAreaHeight } from "discourse/components/topic-timeline/container"; import { addTopicTitleDecorator } from "discourse/components/topic-title"; @@ -127,7 +124,6 @@ import { import { setNewCategoryDefaultColors } from "discourse/routes/new-category"; import { setNotificationsLimit } from "discourse/routes/user-notifications"; import { addComposerSaveErrorCallback } from "discourse/services/composer"; -import { attachAdditionalPanel } from "discourse/widgets/header"; import { addPostClassesCallback } from "discourse/widgets/post"; import { addDecorator } from "discourse/widgets/post-cooked"; 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 { 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(); // This helper prevents us from applying the same `modifyClass` over and over in test mode. @@ -732,7 +714,7 @@ class PluginApi { **/ decorateWidget(name, fn) { const widgetName = name.split(":")[0]; - this.#deprecatedHeaderWidgetOverride(widgetName, "decorateWidget"); + this.#deprecatedWidgetOverride(widgetName, "decorateWidget"); decorateWidget(name, fn); } @@ -763,7 +745,7 @@ class PluginApi { return; } - this.#deprecatedHeaderWidgetOverride(widget, "attachWidgetAction"); + this.#deprecatedWidgetOverride(widget, "attachWidgetAction"); widgetClass.prototype[actionName] = fn; } @@ -1108,7 +1090,7 @@ class PluginApi { * **/ changeWidgetSetting(widgetName, settingName, newValue) { - this.#deprecatedHeaderWidgetOverride(widgetName, "changeWidgetSetting"); + this.#deprecatedWidgetOverride(widgetName, "changeWidgetSetting"); changeSetting(widgetName, settingName, newValue); } @@ -1142,7 +1124,7 @@ class PluginApi { **/ reopenWidget(name, args) { - this.#deprecatedHeaderWidgetOverride(name, "reopenWidget"); + this.#deprecatedWidgetOverride(name, "reopenWidget"); return reopenWidget(name, args); } @@ -1169,16 +1151,12 @@ class PluginApi { * and returns a hash of values to pass to attach * **/ - addHeaderPanel(name, toggle, transformAttrs) { - deprecated( - "addHeaderPanel will be removed as part of the glimmer header upgrade. Use api.headerIcons instead.", - { - id: "discourse.add-header-panel", - url: "https://meta.discourse.org/t/296544", - } + addHeaderPanel() { + // eslint-disable-next-line no-console + console.error( + consolePrefix(), + `api.addHeaderPanel: This API was decommissioned. Use api.headerIcons instead.` ); - 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) { - deprecated( - "addToHeaderIcons has been deprecated. Use api.headerIcons instead.", - { - id: "discourse.add-header-icons", - url: "https://meta.discourse.org/t/296544", - } - ); - - this.headerIcons.add( - icon, - , - { before: "search" } + // eslint-disable-next-line no-console + console.error( + consolePrefix(), + `api.addToHeaderIcons: This API was decommissioned. Use api.headerIcons instead.` ); } @@ -2397,7 +2368,6 @@ class PluginApi { * */ forceDropdownForMenuPanels(classNames) { - forceDropdownForMenuPanels(classNames); glimmerForceDropdownForMenuPanels(classNames); } @@ -3282,18 +3252,20 @@ class PluginApi { addLegacyAboutPageStat(name); } - #deprecatedHeaderWidgetOverride(widgetName, override) { - if (DEPRECATED_HEADER_WIDGETS.includes(widgetName)) { - this.container.lookup("service:header").anyWidgetHeaderOverrides = true; - deprecated( - `The ${widgetName} widget has been deprecated and ${override} is no longer a supported override.`, - { - since: "v3.3.0.beta1-dev", - id: "discourse.header-widget-overrides", - url: "https://meta.discourse.org/t/316549", - } - ); - } + // eslint-disable-next-line no-unused-vars + #deprecatedWidgetOverride(widgetName, override) { + // insert here the code to handle widget deprecations, e.g. for the header widgets we used: + // if (DEPRECATED_HEADER_WIDGETS.includes(widgetName)) { + // this.container.lookup("service:header").anyWidgetHeaderOverrides = true; + // deprecated( + // `The ${widgetName} widget has been deprecated and ${override} is no longer a supported override.`, + // { + // since: "v3.3.0.beta1-dev", + // id: "discourse.header-widget-overrides", + // url: "https://meta.discourse.org/t/316549", + // } + // ); + // } } } diff --git a/app/assets/javascripts/discourse/app/services/header.js b/app/assets/javascripts/discourse/app/services/header.js index dafe0cf9e88..57992494915 100644 --- a/app/assets/javascripts/discourse/app/services/header.js +++ b/app/assets/javascripts/discourse/app/services/header.js @@ -28,7 +28,6 @@ export default class Header extends Service { @tracked hamburgerVisible = false; @tracked userVisible = false; - @tracked anyWidgetHeaderOverrides = false; #hiders = new TrackedMap(); @@ -72,32 +71,6 @@ export default class Header extends Service { 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) { const validButtons = buttons .map((button) => { diff --git a/app/assets/javascripts/discourse/app/templates/application.hbs b/app/assets/javascripts/discourse/app/templates/application.hbs index 73159bb226b..c14292ae9c4 100644 --- a/app/assets/javascripts/discourse/app/templates/application.hbs +++ b/app/assets/javascripts/discourse/app/templates/application.hbs @@ -12,32 +12,17 @@ /> {{#if this.showSiteHeader}} - {{#if this.header.useGlimmerHeader}} - - {{else}} - - {{/if}} + {{/if}} diff --git a/app/assets/javascripts/discourse/app/widgets/after-header-panel-outlet.js b/app/assets/javascripts/discourse/app/widgets/after-header-panel-outlet.js deleted file mode 100644 index a293e58d9e6..00000000000 --- a/app/assets/javascripts/discourse/app/widgets/after-header-panel-outlet.js +++ /dev/null @@ -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` ` -); diff --git a/app/assets/javascripts/discourse/app/widgets/before-header-logo-outlet.js b/app/assets/javascripts/discourse/app/widgets/before-header-logo-outlet.js deleted file mode 100644 index 1fdf13214ef..00000000000 --- a/app/assets/javascripts/discourse/app/widgets/before-header-logo-outlet.js +++ /dev/null @@ -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` ` -); diff --git a/app/assets/javascripts/discourse/app/widgets/before-header-panel-outlet.js b/app/assets/javascripts/discourse/app/widgets/before-header-panel-outlet.js deleted file mode 100644 index 334dbdd13e7..00000000000 --- a/app/assets/javascripts/discourse/app/widgets/before-header-panel-outlet.js +++ /dev/null @@ -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` ` -); diff --git a/app/assets/javascripts/discourse/app/widgets/header-bootstrap-mode.js b/app/assets/javascripts/discourse/app/widgets/header-bootstrap-mode.js deleted file mode 100644 index 0f11aeed11c..00000000000 --- a/app/assets/javascripts/discourse/app/widgets/header-bootstrap-mode.js +++ /dev/null @@ -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`` -); diff --git a/app/assets/javascripts/discourse/app/widgets/header-contents.js b/app/assets/javascripts/discourse/app/widgets/header-contents.js deleted file mode 100644 index 203b78c2ddd..00000000000 --- a/app/assets/javascripts/discourse/app/widgets/header-contents.js +++ /dev/null @@ -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}} - - - - {{after-header-panel-outlet attrs=attrs}} - `, -}); diff --git a/app/assets/javascripts/discourse/app/widgets/header-topic-info.js b/app/assets/javascripts/discourse/app/widgets/header-topic-info.js deleted file mode 100644 index 77585b1fad1..00000000000 --- a/app/assets/javascripts/discourse/app/widgets/header-topic-info.js +++ /dev/null @@ -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: `${fancyTitle}` }); - 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``, - { - 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, - }); - } - }, -}); diff --git a/app/assets/javascripts/discourse/app/widgets/header-user-tip-shim.js b/app/assets/javascripts/discourse/app/widgets/header-user-tip-shim.js deleted file mode 100644 index 949350e5d27..00000000000 --- a/app/assets/javascripts/discourse/app/widgets/header-user-tip-shim.js +++ /dev/null @@ -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`` -); diff --git a/app/assets/javascripts/discourse/app/widgets/header.js b/app/assets/javascripts/discourse/app/widgets/header.js deleted file mode 100644 index 7f6b42d5695..00000000000 --- a/app/assets/javascripts/discourse/app/widgets/header.js +++ /dev/null @@ -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`` - ), - ]; - }, - - 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``, - { - 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``, - { - 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` - - `, - { 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; - } - }, -}); diff --git a/app/assets/javascripts/discourse/app/widgets/home-logo-wrapper-outlet.js b/app/assets/javascripts/discourse/app/widgets/home-logo-wrapper-outlet.js deleted file mode 100644 index 60c472db2f2..00000000000 --- a/app/assets/javascripts/discourse/app/widgets/home-logo-wrapper-outlet.js +++ /dev/null @@ -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` - - - - ` -); diff --git a/app/assets/javascripts/discourse/app/widgets/home-logo.js b/app/assets/javascripts/discourse/app/widgets/home-logo.js deleted file mode 100644 index eb1d1b2fd0a..00000000000 --- a/app/assets/javascripts/discourse/app/widgets/home-logo.js +++ /dev/null @@ -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; - }, -}); diff --git a/app/assets/javascripts/discourse/app/widgets/sidebar-toggle.js b/app/assets/javascripts/discourse/app/widgets/sidebar-toggle.js deleted file mode 100644 index 2555300db39..00000000000 --- a/app/assets/javascripts/discourse/app/widgets/sidebar-toggle.js +++ /dev/null @@ -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", - }), - ]; - }, -}); diff --git a/app/assets/javascripts/discourse/app/widgets/user-status-bubble.js b/app/assets/javascripts/discourse/app/widgets/user-status-bubble.js deleted file mode 100644 index 4f772f69ab4..00000000000 --- a/app/assets/javascripts/discourse/app/widgets/user-status-bubble.js +++ /dev/null @@ -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 }); - }, -}); diff --git a/app/assets/javascripts/discourse/tests/acceptance/header-api-test.gjs b/app/assets/javascripts/discourse/tests/acceptance/header-api-test.gjs index ea31b972aee..d31f1c8202f 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/header-api-test.gjs +++ b/app/assets/javascripts/discourse/tests/acceptance/header-api-test.gjs @@ -6,34 +6,6 @@ import { acceptance } from "discourse/tests/helpers/qunit-helpers"; // TODO: Consolidate these tests into a single acceptance test once the Glimmer // 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", ); - }); - - 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", ); - }); - - await visit("/"); - assert.dom(".test-icon").exists("icon is displayed"); - }); -}); acceptance("Header API - anonymous", function () { 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) { needs.user({ groups: AUTO_GROUPS.everyone }); - needs.settings({ - glimmer_header_mode: "enabled", - }); test("can add buttons to the header", async function (assert) { withPluginApi("1.29.0", (api) => { diff --git a/app/assets/javascripts/discourse/tests/acceptance/mobile-pan-test.js b/app/assets/javascripts/discourse/tests/acceptance/mobile-pan-test.js index cebb1b3d7a2..c04f78493b5 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/mobile-pan-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/mobile-pan-test.js @@ -65,9 +65,6 @@ async function triggerSwipeEnd({ x, y, touchTarget }) { acceptance("Mobile - menu swipes", function (needs) { needs.mobileView(); needs.user(); - needs.settings({ - glimmer_header_mode: "enabled", - }); chromeTest("swipe to close hamburger", async function (assert) { await visit("/"); diff --git a/app/assets/javascripts/discourse/tests/helpers/qunit-helpers.js b/app/assets/javascripts/discourse/tests/helpers/qunit-helpers.js index c661d7eef65..60e56b37e0f 100644 --- a/app/assets/javascripts/discourse/tests/helpers/qunit-helpers.js +++ b/app/assets/javascripts/discourse/tests/helpers/qunit-helpers.js @@ -89,10 +89,6 @@ import { currentSettings, mergeSettings, } from "discourse/tests/helpers/site-settings"; -import { - clearExtraHeaderButtons, - clearExtraHeaderIcons, -} from "discourse/widgets/header"; import { resetDecorators as resetPostCookedDecorators } from "discourse/widgets/post-cooked"; import { resetPostMenuExtraButtons } from "discourse/widgets/post-menu"; import { resetDecorators } from "discourse/widgets/widget"; @@ -236,8 +232,6 @@ export function testCleanup(container, app) { resetSidebarPanels(); clearExtraGlimmerHeaderIcons(); clearExtraGlimmerHeaderButtons(); - clearExtraHeaderIcons(); - clearExtraHeaderButtons(); resetOnKeyUpCallbacks(); resetLogSearchLinkClickedCallbacks(); resetItemSelectCallbacks(); diff --git a/app/assets/javascripts/discourse/tests/integration/components/site-header-test.js b/app/assets/javascripts/discourse/tests/integration/components/site-header-test.js deleted file mode 100644 index aad268db647..00000000000 --- a/app/assets/javascripts/discourse/tests/integration/components/site-header-test.js +++ /dev/null @@ -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``); - 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``); - assert.ok(!exists(".hamburger-dropdown .badge-notification")); - }); - - test("clicking outside the revamped menu closes it", async function (assert) { - await render(hbs``); - 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``); - - 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``); - 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``); - - 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``); - - 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``); - - 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" - ); - }); -}); diff --git a/app/assets/javascripts/discourse/tests/integration/components/widgets/header-test.js b/app/assets/javascripts/discourse/tests/integration/components/widgets/header-test.js deleted file mode 100644 index 28a2c1161fe..00000000000 --- a/app/assets/javascripts/discourse/tests/integration/components/widgets/header-test.js +++ /dev/null @@ -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``); - - 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` - - `); - - 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` - - `); - - 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` - - `); - - 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")); - }); -}); diff --git a/app/assets/javascripts/discourse/tests/integration/components/widgets/home-logo-test.gjs b/app/assets/javascripts/discourse/tests/integration/components/widgets/home-logo-test.gjs deleted file mode 100644 index 93fdf82617d..00000000000 --- a/app/assets/javascripts/discourse/tests/integration/components/widgets/home-logo-test.gjs +++ /dev/null @@ -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(); - - 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(); - - 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(); - - 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(); - - 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(); - - 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(); - - 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(); - - 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(); - - 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(); - - 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(); - - 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(); - - 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(); - - 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(); - - 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(); - - 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" - ); - }); -}); diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 7ef6271d897..c9c9b1c40b1 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -2692,7 +2692,6 @@ en: 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" 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_form_templates: "EXPERIMENTAL: Enable the form templates feature. After enabled, manage the templates at Customize / Templates." admin_sidebar_enabled_groups: "Enable sidebar navigation for the admin UI for the specified groups, which replaces the top-level admin navigation buttons." diff --git a/config/site_settings.yml b/config/site_settings.yml index 253dd41bc5c..fcdcf2af574 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -2432,14 +2432,6 @@ developer: instrument_gc_stat_per_request: default: false hidden: true - glimmer_header_mode: - client: true - type: enum - choices: - - disabled - - auto - - enabled - default: enabled admin_sidebar_enabled_groups: type: group_list list_type: compact diff --git a/db/migrate/20240815234500_remove_glimmer_header_mode_setting.rb b/db/migrate/20240815234500_remove_glimmer_header_mode_setting.rb new file mode 100644 index 00000000000..91877d2e771 --- /dev/null +++ b/db/migrate/20240815234500_remove_glimmer_header_mode_setting.rb @@ -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 diff --git a/spec/system/header_spec.rb b/spec/system/header_spec.rb index 84850b3e7e2..04452e54176 100644 --- a/spec/system/header_spec.rb +++ b/spec/system/header_spec.rb @@ -5,7 +5,6 @@ RSpec.describe "Glimmer Header", type: :system do let(:search) { PageObjects::Pages::Search.new } fab!(:current_user) { Fabricate(:user) } fab!(:topic) - before { SiteSetting.glimmer_header_mode = "enabled" } it "renders basics" do visit "/" diff --git a/spec/system/legacy_widget_header_spec.rb b/spec/system/legacy_widget_header_spec.rb deleted file mode 100644 index 6b0ce779d2e..00000000000 --- a/spec/system/legacy_widget_header_spec.rb +++ /dev/null @@ -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