UX: the ability to hide the admin header (#30175)

Some pages like new/edit item should not display admin header. New attribute called `@shouldDisplay` was added.

As a proof of concept, the flags page was updated.
This commit is contained in:
Krzysztof Kotlarek 2024-12-10 11:59:47 +11:00 committed by GitHub
parent d69edab611
commit 51a5fa036a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 183 additions and 133 deletions

View File

@ -1,4 +1,5 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { hash } from "@ember/helper";
import { service } from "@ember/service";
import { htmlSafe } from "@ember/template";
@ -7,6 +8,7 @@ import DBreadcrumbsContainer from "discourse/components/d-breadcrumbs-container"
import DBreadcrumbsItem from "discourse/components/d-breadcrumbs-item";
import DropdownMenu from "discourse/components/dropdown-menu";
import HorizontalOverflowNav from "discourse/components/horizontal-overflow-nav";
import { bind } from "discourse-common/utils/decorators";
import { i18n } from "discourse-i18n";
import {
DangerActionListItem,
@ -20,8 +22,23 @@ import {
} from "admin/components/admin-page-action-button";
import DMenu from "float-kit/components/d-menu";
const HEADLESS_ACTIONS = ["new", "edit"];
export default class AdminPageHeader extends Component {
@service site;
@service router;
@tracked shouldDisplay = true;
constructor() {
super(...arguments);
this.router.on("routeDidChange", this, this.#checkIfShouldDisplay);
this.#checkIfShouldDisplay();
}
willDestroy() {
super.willDestroy(...arguments);
this.router.off("routeDidChange", this, this.#checkIfShouldDisplay);
}
get title() {
if (this.args.titleLabelTranslated) {
@ -39,87 +56,106 @@ export default class AdminPageHeader extends Component {
}
}
@bind
#checkIfShouldDisplay() {
if (this.args.shouldDisplay !== undefined) {
return (this.shouldDisplay = this.args.shouldDisplay);
}
const currentPath = this.router._router.currentPath;
if (!currentPath) {
return (this.shouldDisplay = true);
}
const pathSegments = currentPath.split(".");
this.shouldDisplay =
!pathSegments.includes("admin") ||
!HEADLESS_ACTIONS.find((segment) => pathSegments.includes(segment));
}
<template>
<div class="admin-page-header">
<div class="admin-page-header__breadcrumbs">
<DBreadcrumbsContainer />
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
{{yield to="breadcrumbs"}}
</div>
<div class="admin-page-header__title-row">
{{#if this.title}}
<h1 class="admin-page-header__title">{{this.title}}</h1>
{{/if}}
{{#if (or (has-block "actions") @headerActionComponent)}}
<div class="admin-page-header__actions">
{{#if this.site.mobileView}}
<DMenu
@identifier="admin-page-header-mobile-actions"
@title={{i18n "more_options"}}
@icon="ellipsis-vertical"
class="btn-small"
>
<:content>
<DropdownMenu class="admin-page-header__mobile-actions">
{{#let
(hash
Primary=PrimaryActionListItem
Default=DefaultActionListItem
Danger=DangerActionListItem
Wrapped=WrappedActionListItem
)
as |actions|
}}
{{#if (has-block "actions")}}
{{yield actions to="actions"}}
{{else}}
<@headerActionComponent @actions={{actions}} />
{{/if}}
{{/let}}
</DropdownMenu>
</:content>
</DMenu>
{{else}}
{{#let
(hash
Primary=PrimaryButton
Default=DefaultButton
Danger=DangerButton
Wrapped=WrappedButton
)
as |actions|
}}
{{#if (has-block "actions")}}
{{yield actions to="actions"}}
{{else}}
<@headerActionComponent @actions={{actions}} />
{{/if}}
{{/let}}
{{/if}}
</div>
{{/if}}
</div>
{{#if this.description}}
<p class="admin-page-header__description">
{{htmlSafe this.description}}
{{#if @learnMoreUrl}}
<span class="admin-page-header__learn-more">{{htmlSafe
(i18n "learn_more_with_link" url=@learnMoreUrl)
}}</span>
{{/if}}
</p>
{{/if}}
{{#unless @hideTabs}}
<div class="admin-nav-submenu">
<HorizontalOverflowNav class="admin-nav-submenu__tabs">
{{yield to="tabs"}}
</HorizontalOverflowNav>
{{#if this.shouldDisplay}}
<div class="admin-page-header">
<div class="admin-page-header__breadcrumbs">
<DBreadcrumbsContainer />
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
{{yield to="breadcrumbs"}}
</div>
{{/unless}}
</div>
<div class="admin-page-header__title-row">
{{#if this.title}}
<h1 class="admin-page-header__title">{{this.title}}</h1>
{{/if}}
{{#if (or (has-block "actions") @headerActionComponent)}}
<div class="admin-page-header__actions">
{{#if this.site.mobileView}}
<DMenu
@identifier="admin-page-header-mobile-actions"
@title={{i18n "more_options"}}
@icon="ellipsis-vertical"
class="btn-small"
>
<:content>
<DropdownMenu class="admin-page-header__mobile-actions">
{{#let
(hash
Primary=PrimaryActionListItem
Default=DefaultActionListItem
Danger=DangerActionListItem
Wrapped=WrappedActionListItem
)
as |actions|
}}
{{#if (has-block "actions")}}
{{yield actions to="actions"}}
{{else}}
<@headerActionComponent @actions={{actions}} />
{{/if}}
{{/let}}
</DropdownMenu>
</:content>
</DMenu>
{{else}}
{{#let
(hash
Primary=PrimaryButton
Default=DefaultButton
Danger=DangerButton
Wrapped=WrappedButton
)
as |actions|
}}
{{#if (has-block "actions")}}
{{yield actions to="actions"}}
{{else}}
<@headerActionComponent @actions={{actions}} />
{{/if}}
{{/let}}
{{/if}}
</div>
{{/if}}
</div>
{{#if this.description}}
<p class="admin-page-header__description">
{{htmlSafe this.description}}
{{#if @learnMoreUrl}}
<span class="admin-page-header__learn-more">{{htmlSafe
(i18n "learn_more_with_link" url=@learnMoreUrl)
}}</span>
{{/if}}
</p>
{{/if}}
{{#unless @hideTabs}}
<div class="admin-nav-submenu">
<HorizontalOverflowNav class="admin-nav-submenu__tabs">
{{yield to="tabs"}}
</HorizontalOverflowNav>
</div>
{{/unless}}
</div>
{{/if}}
</template>
}

View File

@ -1,12 +0,0 @@
import Controller from "@ember/controller";
import { service } from "@ember/service";
export default class AdminConfigFlagsController extends Controller {
@service router;
get hideTabs() {
return ["adminConfig.flags.new", "adminConfig.flags.edit"].includes(
this.router.currentRouteName
);
}
}

View File

@ -1,38 +1 @@
<AdminPageHeader
@titleLabel="admin.config_areas.flags.header"
@descriptionLabel="admin.config_areas.flags.subheader"
@learnMoreUrl="https://meta.discourse.org/t/moderation-flags/325589"
@hideTabs={{this.hideTabs}}
>
<:breadcrumbs>
<DBreadcrumbsItem
@path="/admin/config/flags"
@label={{i18n "admin.config_areas.flags.header"}}
/>
</:breadcrumbs>
<:actions as |actions|>
<actions.Primary
@route="adminConfig.flags.new"
@title="admin.config_areas.flags.add"
@label="admin.config_areas.flags.add"
@disabled={{this.addFlagButtonDisabled}}
class="admin-flags__header-add-flag"
/>
</:actions>
<:tabs>
<NavItem
@route="adminConfig.flags.settings"
@label="settings"
class="admin-flags-tabs__settings"
/>
<NavItem
@route="adminConfig.flags.index"
@label="admin.config_areas.flags.flags_tab"
class="admin-flags-tabs__flags"
/>
</:tabs>
</AdminPageHeader>
<div class="admin-container admin-config-page__main-area">
<AdminConfigAreas::Flags />
</div>
<AdminConfigAreas::Flags />

View File

@ -0,0 +1,37 @@
<AdminPageHeader
@titleLabel="admin.config_areas.flags.header"
@descriptionLabel="admin.config_areas.flags.subheader"
@learnMoreUrl="https://meta.discourse.org/t/moderation-flags/325589"
>
<:breadcrumbs>
<DBreadcrumbsItem
@path="/admin/config/flags"
@label={{i18n "admin.config_areas.flags.header"}}
/>
</:breadcrumbs>
<:actions as |actions|>
<actions.Primary
@route="adminConfig.flags.new"
@title="admin.config_areas.flags.add"
@label="admin.config_areas.flags.add"
@disabled={{this.addFlagButtonDisabled}}
class="admin-flags__header-add-flag"
/>
</:actions>
<:tabs>
<NavItem
@route="adminConfig.flags.settings"
@label="settings"
class="admin-flags-tabs__settings"
/>
<NavItem
@route="adminConfig.flags.index"
@label="admin.config_areas.flags.flags_tab"
class="admin-flags-tabs__flags"
/>
</:tabs>
</AdminPageHeader>
<div class="admin-container admin-config-page__main-area">
{{outlet}}
</div>

View File

@ -44,6 +44,16 @@ module("Integration | Component | AdminPageHeader", function (hooks) {
assert.dom(".admin-page-header__title").exists().hasText("Wow so cool");
});
test("@shouldDisplay", async function (assert) {
await render(<template>
<AdminPageHeader
@titleLabelTranslated="Wow so cool"
@shouldDisplay={{false}}
/>
</template>);
assert.dom(".admin-page-header").doesNotExist();
});
test("renders base breadcrumbs and yielded <:breadcrumbs>", async function (assert) {
await render(<template>
<AdminPageHeader @titleLabel="admin.titile">

View File

@ -27,7 +27,7 @@ describe "Admin Flags Page", type: :system do
)
admin_flags_page.visit
expect(page).to have_css(".admin-page-header")
expect(admin_header).to be_visible
admin_flags_page.toggle("spam")
topic_page.visit_topic(post.topic).open_flag_topic_modal
@ -81,7 +81,9 @@ describe "Admin Flags Page", type: :system do
expect(admin_flags_page).to have_add_flag_button_enabled
admin_flags_page.click_add_flag
expect(page).not_to have_css(".admin-page-header")
expect(admin_header).to be_hidden
admin_flag_form_page
.fill_in_name("Vulgar")
.fill_in_description("New flag description")
@ -113,7 +115,7 @@ describe "Admin Flags Page", type: :system do
# update
admin_flags_page.visit.click_edit_flag("custom_vulgar")
expect(page).not_to have_css(".admin-page-header")
expect(admin_header).to be_hidden
admin_flag_form_page.fill_in_name("Tasteless").click_save
expect(admin_flags_page).to have_flags(

View File

@ -6,9 +6,11 @@ describe "Admin User Fields", type: :system do
before { sign_in(current_user) }
let(:user_fields_page) { PageObjects::Pages::AdminUserFields.new }
let(:admin_header) { PageObjects::Components::AdminHeader.new }
it "correctly saves user fields" do
user_fields_page.visit
expect(admin_header).to be_visible
user_fields_page.add_field(name: "Occupation", description: "What you do for work")
expect(user_fields_page).to have_user_field("Occupation")
@ -30,6 +32,8 @@ describe "Admin User Fields", type: :system do
user_fields_page.visit
user_fields_page.click_add_field
expect(admin_header).to be_hidden
form = page.find(".user-field")
editable_label = I18n.t("admin_js.admin.user_fields.editable.title")
@ -69,6 +73,8 @@ describe "Admin User Fields", type: :system do
form.find(".user-field-name").fill_in(with: "Favourite Transformer")
expect(admin_header).to be_hidden
form.find(".btn-primary").click
expect(page).to have_no_text(I18n.t("admin_js.admin.user_fields.requirement.confirmation"))

View File

@ -6,6 +6,14 @@ module PageObjects
def has_tabs?(names)
expect(page.all(".admin-nav-submenu__tabs a").map(&:text)).to eq(names)
end
def visible?
has_css?(".admin-page-header")
end
def hidden?
has_no_css?(".admin-page-header")
end
end
end
end