From 85774cc21492dc686b89269ce9b9a001265e9d4b Mon Sep 17 00:00:00 2001 From: Martin Brennan Date: Tue, 8 Oct 2024 08:28:32 +1000 Subject: [PATCH] UX: Automatically collapse admin page header buttons on mobile (#29040) This commit attempts to improve the mobile experience for admin page header and subheader by automatically collapsing all action buttons in these components into a DMenu when viewing mobile. This is done by using different "list" wrapper components and a DMenu trigger and a DropdownMenu on mobile only, and uses has-block to determine whether to render the DMenu trigger at all. This also removes the `PluginOutlet` in `AdminPluginConfigPage`, it was too inflexible for this `DropdownMenu` case, and since the `:actions` were always rendering we couldn't rely on `has-block`. A new plugin API, `registerPluginHeaderActionComponent`, has been introduced instead to replace it. --- .../components/admin-backups-actions.gjs | 4 +- .../components/admin-page-action-button.gjs | 88 ++++++++++++++++++- .../addon/components/admin-page-header.gjs | 67 ++++++++++++-- .../addon/components/admin-page-subheader.gjs | 54 ++++++++++-- .../components/admin-plugin-config-page.gjs | 18 ++-- .../admin/addon/models/admin-plugin.js | 6 +- .../admin/addon/templates/backups-index.hbs | 27 +++--- .../app/components/uppy-backup-uploader.hbs | 3 +- .../app/lib/admin-plugin-header-actions.js | 13 +++ .../discourse/app/lib/plugin-api.gjs | 19 ++++ .../discourse/tests/helpers/qunit-helpers.js | 2 + .../components/admin-page-header-test.gjs | 69 +++++++++++++++ .../components/admin-page-subheader-test.gjs | 48 ++++++++++ .../common/admin/admin_page_header.scss | 34 ++++++- .../stylesheets/common/admin/badges.scss | 1 - .../components/chat-admin-plugin-actions.gjs | 3 +- .../index.hbs | 1 + .../chat-admin-plugin-configuration-nav.js | 5 +- 18 files changed, 406 insertions(+), 56 deletions(-) create mode 100644 app/assets/javascripts/discourse/app/lib/admin-plugin-header-actions.js diff --git a/app/assets/javascripts/admin/addon/components/admin-backups-actions.gjs b/app/assets/javascripts/admin/addon/components/admin-backups-actions.gjs index a6abf5a5346..b37a8692003 100644 --- a/app/assets/javascripts/admin/addon/components/admin-backups-actions.gjs +++ b/app/assets/javascripts/admin/addon/components/admin-backups-actions.gjs @@ -74,14 +74,13 @@ export default class AdminBackupsActions extends Component { @action={{routeAction "rollback"}} @label="admin.backups.operations.rollback.label" @title="admin.backups.operations.rollback.title" - @icon="truck-medical" @disabled={{this.rollbackDisabled}} + @icon="truck-medical" class="admin-backups__rollback" /> {{/if}} <@actions.Default - @icon={{if this.site.isReadOnly "far-eye-slash" "far-eye"}} @action={{this.toggleReadOnlyMode}} @disabled={{@backups.isOperationRunning}} @title={{if @@ -94,6 +93,7 @@ export default class AdminBackupsActions extends Component { "admin.backups.read_only.disable.label" "admin.backups.read_only.enable.label" }} + @icon={{if this.site.isReadOnly "far-eye-slash" "far-eye"}} class="admin-backups__toggle-read-only" /> diff --git a/app/assets/javascripts/admin/addon/components/admin-page-action-button.gjs b/app/assets/javascripts/admin/addon/components/admin-page-action-button.gjs index fe9edd1cecf..1a1a0c76db8 100644 --- a/app/assets/javascripts/admin/addon/components/admin-page-action-button.gjs +++ b/app/assets/javascripts/admin/addon/components/admin-page-action-button.gjs @@ -1,3 +1,4 @@ +import { hash } from "@ember/helper"; import DButton from "discourse/components/d-button"; export const AdminPageActionButton = ; + module("Integration | Component | AdminPageHeader", function (hooks) { setupRenderingTest(hooks); @@ -174,4 +187,60 @@ module("Integration | Component | AdminPageHeader", function (hooks) { await click(".edit-groupings-btn"); assert.true(actionCalled); }); + + test("@headerActionComponent is rendered with actions arg", async function (assert) { + await render(); + + assert + .dom(".admin-page-header-actions-test-component .award-badge") + .exists(); + }); +}); + +module("Integration | Component | AdminPageHeader | Mobile", function (hooks) { + hooks.beforeEach(function () { + forceMobile(); + }); + + setupRenderingTest(hooks); + + test("action buttons become a dropdown on mobile", async function (assert) { + await render(); + + assert + .dom( + ".admin-page-header__actions .fk-d-menu__trigger.admin-page-header-mobile-actions-trigger" + ) + .exists(); + + await click(".admin-page-header-mobile-actions-trigger"); + + assert + .dom(".dropdown-menu.admin-page-header__mobile-actions .new-badge") + .exists(); + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/admin-page-subheader-test.gjs b/app/assets/javascripts/discourse/tests/integration/components/admin-page-subheader-test.gjs index d705317e24b..217ef7ca2cc 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/admin-page-subheader-test.gjs +++ b/app/assets/javascripts/discourse/tests/integration/components/admin-page-subheader-test.gjs @@ -1,5 +1,6 @@ import { click, render } from "@ember/test-helpers"; import { module, test } from "qunit"; +import { forceMobile } from "discourse/lib/mobile"; import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import i18n from "discourse-common/helpers/i18n"; import AdminPageSubheader from "admin/components/admin-page-subheader"; @@ -127,3 +128,50 @@ module("Integration | Component | AdminPageSubheader", function (hooks) { assert.true(actionCalled); }); }); + +module( + "Integration | Component | AdminPageSubheader | Mobile", + function (hooks) { + hooks.beforeEach(function () { + forceMobile(); + }); + + setupRenderingTest(hooks); + + test("action buttons become a dropdown on mobile", async function (assert) { + await render(); + + assert + .dom( + ".admin-page-subheader .fk-d-menu__trigger.admin-page-subheader-mobile-actions-trigger" + ) + .exists(); + + await click(".admin-page-subheader-mobile-actions-trigger"); + + assert + .dom(".dropdown-menu.admin-page-subheader__mobile-actions .new-badge") + .exists(); + }); + } +); diff --git a/app/assets/stylesheets/common/admin/admin_page_header.scss b/app/assets/stylesheets/common/admin/admin_page_header.scss index 996b81934e6..c29fdb31e1b 100644 --- a/app/assets/stylesheets/common/admin/admin_page_header.scss +++ b/app/assets/stylesheets/common/admin/admin_page_header.scss @@ -6,10 +6,6 @@ align-items: stretch; margin-bottom: var(--space-2); - @media (max-width: $mobile-breakpoint) { - flex-direction: column; - } - h1, h3 { margin: 0; @@ -58,3 +54,33 @@ } } } + +.admin-page-header { + &__title-row { + @media (max-width: $mobile-breakpoint) { + flex-direction: row; + align-items: center; + + .admin-page-header__actions { + button { + margin-bottom: 0; + } + } + } + } +} + +.admin-page-subheader { + &__title-row { + @media (max-width: $mobile-breakpoint) { + flex-direction: row; + align-items: center; + } + } +} + +.admin-page-action-list-item { + .btn-primary { + color: var(--primary); + } +} diff --git a/app/assets/stylesheets/common/admin/badges.scss b/app/assets/stylesheets/common/admin/badges.scss index a4f1fb55543..a6326ce8a1f 100644 --- a/app/assets/stylesheets/common/admin/badges.scss +++ b/app/assets/stylesheets/common/admin/badges.scss @@ -97,7 +97,6 @@ } .award-badge { - margin: 15px 0 0 15px; float: left; max-width: 70%; diff --git a/plugins/chat/admin/assets/javascripts/admin/components/chat-admin-plugin-actions.gjs b/plugins/chat/admin/assets/javascripts/admin/components/chat-admin-plugin-actions.gjs index 66b02e3dcb9..017dda597c3 100644 --- a/plugins/chat/admin/assets/javascripts/admin/components/chat-admin-plugin-actions.gjs +++ b/plugins/chat/admin/assets/javascripts/admin/components/chat-admin-plugin-actions.gjs @@ -29,10 +29,11 @@ export default class ChatAdminPluginActions extends Component { } diff --git a/plugins/chat/admin/assets/javascripts/discourse/templates/admin-plugins/show/discourse-chat-incoming-webhooks/index.hbs b/plugins/chat/admin/assets/javascripts/discourse/templates/admin-plugins/show/discourse-chat-incoming-webhooks/index.hbs index f050a5ff06b..8f494bde0ad 100644 --- a/plugins/chat/admin/assets/javascripts/discourse/templates/admin-plugins/show/discourse-chat-incoming-webhooks/index.hbs +++ b/plugins/chat/admin/assets/javascripts/discourse/templates/admin-plugins/show/discourse-chat-incoming-webhooks/index.hbs @@ -14,6 +14,7 @@ @title="chat.incoming_webhooks.new" @route="adminPlugins.show.discourse-chat-incoming-webhooks.new" @routeModels="chat" + @icon="plus" class="admin-incoming-webhooks-new" /> diff --git a/plugins/chat/assets/javascripts/discourse/initializers/chat-admin-plugin-configuration-nav.js b/plugins/chat/assets/javascripts/discourse/initializers/chat-admin-plugin-configuration-nav.js index 2fbc231a7c7..ae508c97db9 100644 --- a/plugins/chat/assets/javascripts/discourse/initializers/chat-admin-plugin-configuration-nav.js +++ b/plugins/chat/assets/javascripts/discourse/initializers/chat-admin-plugin-configuration-nav.js @@ -19,10 +19,7 @@ export default { }, ]); - api.renderInOutlet( - "admin-plugin-config-page-actions-chat", - ChatAdminPluginActions - ); + api.registerPluginHeaderActionComponent("chat", ChatAdminPluginActions); }); }, };