DEV: Convert header buttons to use new `headerButtons` API (#26014)

This commit is contained in:
Isaac Janzen 2024-03-07 12:15:47 -07:00 committed by GitHub
parent 0bc0efbd0a
commit 91f52e79ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 194 additions and 31 deletions

View File

@ -5,7 +5,8 @@ import { action } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import { service } from "@ember/service";
import { modifier } from "ember-modifier";
import { and, not, or } from "truth-helpers";
import { and, eq, not, or } from "truth-helpers";
import DAG from "discourse/lib/dag";
import scrollLock from "discourse/lib/scroll-lock";
import DiscourseURL from "discourse/lib/url";
import { scrollTop } from "discourse/mixins/scroll-top";
@ -15,10 +16,25 @@ import HamburgerDropdownWrapper from "./glimmer-header/hamburger-dropdown-wrappe
import Icons from "./glimmer-header/icons";
import SearchMenuWrapper from "./glimmer-header/search-menu-wrapper";
import UserMenuWrapper from "./glimmer-header/user-menu-wrapper";
import PluginOutlet from "./plugin-outlet";
const SEARCH_BUTTON_ID = "search-button";
let headerButtons;
resetHeaderButtons();
function resetHeaderButtons() {
headerButtons = new DAG({ defaultPosition: { before: "auth" } });
headerButtons.add("auth");
}
export function headerButtonsDAG() {
return headerButtons;
}
export function clearExtraHeaderButtons() {
resetHeaderButtons();
}
export default class GlimmerHeader extends Component {
@service router;
@service search;
@ -166,17 +182,17 @@ export default class GlimmerHeader extends Component {
>
<span class="header-buttons">
<PluginOutlet @name="before-header-buttons" />
{{#unless this.currentUser}}
<AuthButtons
@showCreateAccount={{@showCreateAccount}}
@showLogin={{@showLogin}}
@canSignUp={{@canSignUp}}
/>
{{/unless}}
<PluginOutlet @name="after-header-buttons" />
{{#each (headerButtons.resolve) as |entry|}}
{{#if (and (eq entry.key "auth") (not this.currentUser))}}
<AuthButtons
@showCreateAccount={{@showCreateAccount}}
@showLogin={{@showLogin}}
@canSignUp={{@canSignUp}}
/>
{{else if entry.value}}
<entry.value />
{{/if}}
{{/each}}
</span>
{{#if

View File

@ -9,6 +9,7 @@ import {
import { addPluginDocumentTitleCounter } from "discourse/components/d-document";
import { addToolbarCallback } from "discourse/components/d-editor";
import { addCategorySortCriteria } from "discourse/components/edit-category-settings";
import { headerButtonsDAG } from "discourse/components/glimmer-header";
import { headerIconsDAG } from "discourse/components/glimmer-header/icons";
import { forceDropdownForMenuPanels as glimmerForceDropdownForMenuPanels } from "discourse/components/glimmer-site-header";
import { addGlobalNotice } from "discourse/components/global-notice";
@ -143,7 +144,7 @@ import { modifySelectKit } from "select-kit/mixins/plugin-api";
// docs/CHANGELOG-JAVASCRIPT-PLUGIN-API.md whenever you change the version
// using the format described at https://keepachangelog.com/en/1.0.0/.
export const PLUGIN_API_VERSION = "1.28.0";
export const PLUGIN_API_VERSION = "1.29.0";
const DEPRECATED_HEADER_WIDGETS = [
"header",
@ -1864,6 +1865,40 @@ class PluginApi {
return headerIconsDAG();
}
/**
* Allows for manipulation of the header buttons. This includes, adding, removing, or modifying the order of buttons.
*
* Only the passing of components is supported, and by default the buttons are added to the left of exisiting buttons.
*
* Example: Add a `foo` button to the header buttons after the auth buttons
* ```
* api.headerButtons.add(
* "foo",
* FooComponent,
* { after: "auth" }
* )
* ```
*
* Example: Remove the `foo` button from the header buttons
* ```
* api.headerButtons.delete("foo")
* ```
*
* Example: Reposition the `foo` button to be before the `bar` and after the `baz` button
* ```
* api.headerButtons.reposition("foo", { before: "bar", after: "baz" })
* ```
*
* Example: Check if the `foo` button is present in the header buttons (returns true of false)
* ```
* api.headerButtons.has("foo")
* ```
*
**/
get headerButtons() {
return headerButtonsDAG();
}
/**
* Adds a widget to the header-icon ul. The widget must already be created. You can create new widgets
* in a theme or plugin via an initializer prior to calling this function.

View File

@ -2,6 +2,7 @@ import { schedule } from "@ember/runloop";
import { hbs } from "ember-cli-htmlbars";
import $ from "jquery";
import { h } from "virtual-dom";
import { headerButtonsDAG } from "discourse/components/glimmer-header";
import { headerIconsDAG } from "discourse/components/glimmer-header/icons";
import { addExtraUserClasses } from "discourse/helpers/user-avatar";
import { wantsNewWindow } from "discourse/lib/intercept-click";
@ -26,10 +27,17 @@ export const PANEL_WRAPPER_ID = "additional-panel-wrapper";
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 || [];
@ -496,6 +504,10 @@ export default createWidget("header", {
buildKey: () => `header`,
services: ["router", "search"],
init() {
registerWidgetShim("extra-button", "div.wrapper", hbs`<@data.component />`);
},
defaultState() {
let states = {
searchVisible: false,
@ -531,22 +543,19 @@ export default createWidget("header", {
return headerIcons;
}
const panels = [
h("span.header-buttons", [
new RenderGlimmer(
this,
"span.before-header-buttons",
hbs`<PluginOutlet @name="before-header-buttons"/>`
),
this.attach("header-buttons", attrs),
new RenderGlimmer(
this,
"span.after-header-buttons",
hbs`<PluginOutlet @name="after-header-buttons"/>`
),
]),
headerIcons,
];
const buttons = [];
const resolvedButtons = _extraHeaderButtons.resolve();
resolvedButtons.forEach((button) => {
if (button.key === "auth") {
return;
}
buttons.push(this.attach("extra-button", { component: button.value }));
});
buttons.push(this.attach("header-buttons", attrs));
const panels = [];
panels.push(h("span.header-buttons", buttons), headerIcons);
if (this.search.visible) {
this.search.inTopicContext = this.search.inTopicContext && inTopicRoute;

View File

@ -0,0 +1,93 @@
import { visit } from "@ember/test-helpers";
import { test } from "qunit";
import { AUTO_GROUPS } from "discourse/lib/constants";
import { withPluginApi } from "discourse/lib/plugin-api";
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();
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");
});
});
acceptance("Header API - anonymous", function () {
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("buttons are positioned to the left of the auth buttons by default", async function (assert) {
withPluginApi("1.29.0", (api) => {
api.headerButtons.add("test", <template>
<button class="test-button">Test</button>
</template>);
});
await visit("/");
const testButton = document.querySelector(".test-button");
const authButtons = document.querySelector(".auth-buttons");
assert.equal(
testButton.compareDocumentPosition(authButtons),
Node.DOCUMENT_POSITION_FOLLOWING,
"Test button is positioned before auth-buttons"
);
});
});
acceptance("Glimmer Header API - authenticated", function (needs) {
needs.user({ groups: AUTO_GROUPS.everyone });
needs.settings({
experimental_glimmer_header_groups: AUTO_GROUPS.everyone,
});
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("buttons can be repositioned", async function (assert) {
withPluginApi("1.29.0", (api) => {
api.headerButtons.add("test1", <template>
<button class="test1-button">Test1</button>
</template>);
api.headerButtons.add(
"test2",
<template><button class="test2-button">Test2</button></template>,
{ before: "test1" }
);
});
await visit("/");
const test1 = document.querySelector(".test1-button");
const test2 = document.querySelector(".test2-button");
assert.equal(
test2.compareDocumentPosition(test1),
Node.DOCUMENT_POSITION_FOLLOWING,
"Test2 button is positioned before Test1 button"
);
});
});

View File

@ -18,6 +18,7 @@ import {
cleanUpComposerUploadPreProcessor,
} from "discourse/components/composer-editor";
import { clearToolbarCallbacks } from "discourse/components/d-editor";
import { clearExtraHeaderButtons as clearExtraGlimmerHeaderButtons } from "discourse/components/glimmer-header";
import { clearExtraHeaderIcons as clearExtraGlimmerHeaderIcons } from "discourse/components/glimmer-header/icons";
import { clearBulkButtons } from "discourse/components/modal/topic-bulk-actions";
import { resetWidgetCleanCallbacks } from "discourse/components/mount-widget";
@ -83,7 +84,10 @@ import {
currentSettings,
mergeSettings,
} from "discourse/tests/helpers/site-settings";
import { clearExtraHeaderIcons } from "discourse/widgets/header";
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";
@ -225,7 +229,9 @@ export function testCleanup(container, app) {
resetNotificationTypeRenderers();
resetSidebarPanels();
clearExtraGlimmerHeaderIcons();
clearExtraGlimmerHeaderButtons();
clearExtraHeaderIcons();
clearExtraHeaderButtons();
resetOnKeyUpCallbacks();
resetItemSelectCallbacks();
resetUserMenuTabs();

View File

@ -7,6 +7,10 @@ in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.29.0] - 2024-03-05
- added `headerButtons` which allows for manipulation of the header butttons. This includes, adding, removing, or modifying the order of buttons.
## [1.28.0] - 2024-02-21
- added `headerIcons` which allows for manipulation of the header icons. This includes, adding, removing, or modifying the order of icons.