DEV: Update headerIcons API (#26090)

- Remove panelPortal usage, which allowed for content to be added as a dropdown
- Instead rely on float-kit's DMenu to render a dropdown
This commit is contained in:
Isaac Janzen 2024-04-10 04:37:37 -06:00 committed by GitHub
parent 8509fc2ebc
commit f8dd468caa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 82 additions and 64 deletions

View File

@ -2,7 +2,6 @@ import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { getOwner } from "@ember/application";
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, eq, not, or } from "truth-helpers";
@ -44,7 +43,6 @@ export default class GlimmerHeader extends Component {
@service header;
@tracked skipSearchContext = this.site.mobileView;
@tracked panelElement;
appEventsListeners = modifier(() => {
this.appEvents.on(
@ -166,11 +164,6 @@ export default class GlimmerHeader extends Component {
}
}
@action
setPanelElement(element) {
this.panelElement = element;
}
<template>
<header class="d-header" {{this.appEventsListeners}}>
<div class="wrap">
@ -202,7 +195,6 @@ export default class GlimmerHeader extends Component {
@toggleHamburger={{this.toggleHamburger}}
@toggleUserMenu={{this.toggleUserMenu}}
@searchButtonId={{SEARCH_BUTTON_ID}}
@panelElement={{this.panelElement}}
/>
{{/if}}
@ -216,9 +208,6 @@ export default class GlimmerHeader extends Component {
<UserMenuWrapper @toggleUserMenu={{this.toggleUserMenu}} />
{{/if}}
<div id="additional-panel-wrapper" {{didInsert this.setPanelElement}}>
</div>
{{#if
(and
(or this.site.mobileView this.site.narrowDesktopView)

View File

@ -4,7 +4,6 @@ import { eq, not, or } from "truth-helpers";
import DAG from "discourse/lib/dag";
import getURL from "discourse-common/lib/get-url";
import Dropdown from "./dropdown";
import PanelPortal from "./panel-portal";
import UserDropdown from "./user-dropdown";
let headerIcons;
@ -64,9 +63,7 @@ export default class Icons extends Component {
/>
{{/if}}
{{else if entry.value}}
<entry.value
@panelPortal={{component PanelPortal panelElement=@panelElement}}
/>
<entry.value />
{{/if}}
{{/each}}
</ul>

View File

@ -1,9 +0,0 @@
import ConditionalInElement from "../conditional-in-element";
const PanelPortal = <template>
<ConditionalInElement @element={{@panelElement}}>
{{yield}}
</ConditionalInElement>
</template>;
export default PanelPortal;

View File

@ -1,25 +0,0 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { schedule } from "@ember/runloop";
import { PANEL_WRAPPER_ID } from "discourse/widgets/header";
import PanelPortal from "./header/panel-portal";
export default class LegacyHeaderIconShim extends Component {
@tracked panelElement;
constructor() {
super(...arguments);
schedule("afterRender", () => {
this.panelElement = document.querySelector(`#${PANEL_WRAPPER_ID}`);
});
}
<template>
{{#let
(component PanelPortal panelElement=this.panelElement)
as |panelPortal|
}}
<@component @panelPortal={{panelPortal}} />
{{/let}}
</template>
}

View File

@ -1850,17 +1850,17 @@ class PluginApi {
* api.headerIcons.has("chat")
* ```
*
* Additionally, you can utilize the `@panelPortal` argument to create a dropdown panel. This can be useful when
* If you are looking to add a button with a dropdown, you can implement a `DMenu` which has a `content` block
* you want create a button in the header that opens a dropdown panel with additional content.
*
* ```
* const IconWithDropdown = <template>
* <DButton @icon="icon" @onClick={{this.toggleVisible}} />
* {{#if this.visible}}
* <@panelPortal>
* <div>Panel</div>
* </@panelPortal>
* {{/if}}
* <DMenu @icon="foo" title={{i18n "title"}}>
* <:content as |args|>
* dropdown content here
* <DButton @action={{args.close}} @icon="bar" />
* </:content>
* </DMenu>
* </template>;
*
* api.headerIcons.add("icon-name", IconWithDropdown, { before: "search" })

View File

@ -22,7 +22,6 @@ import discourseLater from "discourse-common/lib/later";
import I18n from "discourse-i18n";
const SEARCH_BUTTON_ID = "search-button";
export const PANEL_WRAPPER_ID = "additional-panel-wrapper";
let _extraHeaderIcons;
clearExtraHeaderIcons();
@ -237,11 +236,7 @@ createWidget("header-icons", {
tagName: "ul.icons.d-header-icons",
init() {
registerWidgetShim(
"extra-icon",
"span.wrapper",
hbs`<LegacyHeaderIconShim @component={{@data.component}} />`
);
registerWidgetShim("extra-icon", "span.wrapper", hbs`<@data.component />`);
},
html(attrs) {
@ -568,8 +563,6 @@ export default createWidget("header", {
}
});
panels.push(h(`div#${PANEL_WRAPPER_ID}`));
if (this.site.mobileView || this.site.narrowDesktopView) {
panels.push(this.attach("header-cloak"));
}

View File

@ -19,6 +19,17 @@ acceptance("Header API - authenticated", function (needs) {
await visit("/");
assert.dom("button.test-button").exists("button is displayed");
});
test("can add icons to the header", async function (assert) {
withPluginApi("1.29.0", (api) => {
api.headerIcons.add("test", <template>
<span class="test-icon">Test</span>
</template>);
});
await visit("/");
assert.dom(".test-icon").exists("icon is displayed");
});
});
acceptance("Header API - anonymous", function () {
@ -49,6 +60,34 @@ acceptance("Header API - anonymous", function () {
"Test button is positioned before auth-buttons"
);
});
test("can add icons to the header", async function (assert) {
withPluginApi("1.29.0", (api) => {
api.headerIcons.add("test", <template>
<span class="test-icon">Test</span>
</template>);
});
await visit("/");
assert.dom(".test-icon").exists("icon is displayed");
});
test("icons are positioned to the left of search icon by default", async function (assert) {
withPluginApi("1.29.0", (api) => {
api.headerIcons.add("test", <template>
<span class="test-icon">Test</span>
</template>);
});
await visit("/");
const testIcon = document.querySelector(".test-icon");
const search = document.querySelector(".search-dropdown");
assert.equal(
testIcon.compareDocumentPosition(search),
Node.DOCUMENT_POSITION_FOLLOWING,
"Test icon is positioned before search icon"
);
});
});
acceptance("Glimmer Header API - authenticated", function (needs) {
@ -90,4 +129,38 @@ acceptance("Glimmer Header API - authenticated", function (needs) {
"Test2 button is positioned before Test1 button"
);
});
test("can add icons to the header", async function (assert) {
withPluginApi("1.29.0", (api) => {
api.headerIcons.add("test", <template>
<span class="test-icon">Test</span>
</template>);
});
await visit("/");
assert.dom(".test-icon").exists("icon is displayed");
});
test("icons can be repositioned", async function (assert) {
withPluginApi("1.29.0", (api) => {
api.headerIcons.add("test1", <template>
<span class="test1-icon">Test1</span>
</template>);
api.headerIcons.add(
"test2",
<template><span class="test2-icon">Test2</span></template>,
{ before: "test1" }
);
});
await visit("/");
const test1 = document.querySelector(".test1-icon");
const test2 = document.querySelector(".test2-icon");
assert.equal(
test2.compareDocumentPosition(test1),
Node.DOCUMENT_POSITION_FOLLOWING,
"Test2 icon is positioned before Test1 icon"
);
});
});