DEV: implements <DropdownMenu /> (#26917)

DropdownMenu component is meant as a way to describe the content of menus.

Syntax:

```
<DropdownMenu as |dm|>
  <dm.item class="test">
    First
  </dm.item>
  <dm.divider class="foo" />
  <dm.item class="bar">
    Second
  </dm.item>
</DropdownMenu>
```
This commit is contained in:
Joffrey JAFFEUX 2024-05-08 09:08:42 +02:00 committed by GitHub
parent 21bce2d07e
commit cf8b81771f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 468 additions and 411 deletions

View File

@ -4,6 +4,7 @@ import { action } from "@ember/object";
import { service } from "@ember/service";
import { and, not, or } from "truth-helpers";
import DButton from "discourse/components/d-button";
import DropdownMenu from "discourse/components/dropdown-menu";
import concatClass from "discourse/helpers/concat-class";
export default class AdminPostMenu extends Component {
@ -46,20 +47,20 @@ export default class AdminPostMenu extends Component {
}
<template>
<ul>
<DropdownMenu as |dropdown|>
{{#if this.currentUser.staff}}
<li>
<dropdown.item>
<DButton
@label="review.moderation_history"
@icon="list"
class="btn btn-transparent moderation-history"
@href={{this.reviewUrl}}
/>
</li>
</dropdown.item>
{{/if}}
{{#if (and this.currentUser.staff (not @data.transformedPost.isWhisper))}}
<li>
<dropdown.item>
<DButton
@label={{if
@data.transformedPost.isModeratorAction
@ -73,11 +74,11 @@ export default class AdminPostMenu extends Component {
}}
@action={{fn this.topicAction "togglePostType"}}
/>
</li>
</dropdown.item>
{{/if}}
{{#if @data.transformedPost.canEditStaffNotes}}
<li>
<dropdown.item>
<DButton
@icon="user-shield"
@label={{if
@ -93,18 +94,18 @@ export default class AdminPostMenu extends Component {
}}
@action={{fn this.topicAction "changeNotice"}}
/>
</li>
</dropdown.item>
{{/if}}
{{#if (and this.currentUser.staff @data.transformedPost.hidden)}}
<li>
<dropdown.item>
<DButton
@label="post.controls.unhide"
@icon="far-eye"
class="btn btn-transparent unhide-post"
@action={{fn this.topicAction "unhidePost"}}
/>
</li>
</dropdown.item>
{{/if}}
{{#if
@ -116,7 +117,7 @@ export default class AdminPostMenu extends Component {
)
)
}}
<li>
<dropdown.item>
<DButton
@label="post.controls.change_owner"
@icon="user"
@ -124,23 +125,23 @@ export default class AdminPostMenu extends Component {
class="btn btn-transparent change-owner"
@action={{fn this.topicAction "changePostOwner"}}
/>
</li>
</dropdown.item>
{{/if}}
{{#if (and @data.transformedPost.user_id this.currentUser.staff)}}
{{#if this.siteSettings.enable_badges}}
<li>
<dropdown.item>
<DButton
@label="post.controls.grant_badge"
@icon="certificate"
class="btn btn-transparent grant-badge"
@action={{fn this.topicAction "grantBadge"}}
/>
</li>
</dropdown.item>
{{/if}}
{{#if @data.transformedPost.locked}}
<li>
<dropdown.item>
<DButton
@label="post.controls.unlock_post"
@icon="unlock"
@ -151,9 +152,9 @@ export default class AdminPostMenu extends Component {
}}
@action={{fn this.topicAction "unlockPost"}}
/>
</li>
</dropdown.item>
{{else}}
<li>
<dropdown.item>
<DButton
@label="post.controls.lock_post"
@icon="lock"
@ -161,24 +162,24 @@ export default class AdminPostMenu extends Component {
class="btn btn-transparent lock-post"
@action={{fn this.topicAction "lockPost"}}
/>
</li>
</dropdown.item>
{{/if}}
{{/if}}
{{#if @data.transformedPost.canPermanentlyDelete}}
<li>
<dropdown.item>
<DButton
@label="post.controls.permanently_delete"
@icon="trash-alt"
class="btn btn-transparent permanently-delete"
@action={{fn this.topicAction "permanentlyDeletePost"}}
/>
</li>
</dropdown.item>
{{/if}}
{{#if (or @data.transformedPost.canManage @data.transformedPost.canWiki)}}
{{#if @data.transformedPost.wiki}}
<li>
<dropdown.item>
<DButton
@label="post.controls.unwiki"
@icon="far-edit"
@ -188,43 +189,43 @@ export default class AdminPostMenu extends Component {
}}
@action={{fn this.topicAction "toggleWiki"}}
/>
</li>
</dropdown.item>
{{else}}
<li>
<dropdown.item>
<DButton
@label="post.controls.wiki"
@icon="far-edit"
class="btn btn-transparent wiki"
@action={{fn this.topicAction "toggleWiki"}}
/>
</li>
</dropdown.item>
{{/if}}
{{/if}}
{{#if @data.transformedPost.canPublishPage}}
<li>
<dropdown.item>
<DButton
@label="post.controls.publish_page"
@icon="file"
class="btn btn-transparent publish-page"
@action={{fn this.topicAction "showPagePublish"}}
/>
</li>
</dropdown.item>
{{/if}}
{{#if @data.transformedPost.canManage}}
<li>
<dropdown.item>
<DButton
@label="post.controls.rebake"
@icon="sync-alt"
class="btn btn-transparent rebuild-html"
@action={{fn this.topicAction "rebakePost"}}
/>
</li>
</dropdown.item>
{{/if}}
{{#each this.extraButtons as |button|}}
<li>
<dropdown.item>
<DButton
@label={{button.label}}
@translatedLabel={{button.translatedLabel}}
@ -232,8 +233,8 @@ export default class AdminPostMenu extends Component {
class={{concatClass "btn btn-transparent" button.className}}
@action={{fn this.extraAction button}}
/>
</li>
</dropdown.item>
{{/each}}
</ul>
</DropdownMenu>
</template>
}

View File

@ -5,6 +5,7 @@ import { action } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import { inject as service } from "@ember/service";
import DButton from "discourse/components/d-button";
import DropdownMenu from "discourse/components/dropdown-menu";
import BookmarkModal from "discourse/components/modal/bookmark";
import { popupAjaxError } from "discourse/lib/ajax-error";
import {
@ -249,65 +250,69 @@ export default class BookmarkMenu extends Component {
@arrow={{false}}
>
<:content>
<div class="bookmark-menu__body">
<DropdownMenu as |dropdown|>
{{#unless this.showEditDeleteMenu}}
<div class="bookmark-menu__title">{{icon "check-circle"}}<span
>{{i18n "bookmarks.bookmarked_success"}}</span>
</div>
<dropdown.item class="bookmark-menu__title">
{{icon "check-circle"}}
<span>{{i18n "bookmarks.bookmarked_success"}}</span>
</dropdown.item>
{{/unless}}
{{#if this.showEditDeleteMenu}}
{{#if this.site.mobileView}}
<div class="bookmark-menu__title">{{icon "bookmark"}}<span>{{i18n
"bookmarks.bookmark"
}}</span>
</div>
<dropdown.item class="bookmark-menu__title">
{{icon "bookmark"}}
<span>{{i18n "bookmarks.bookmark"}}</span>
</dropdown.item>
{{/if}}
<ul class="bookmark-menu__actions">
<li class="bookmark-menu__row -edit" data-menu-option-id="edit">
<DButton
@icon="pencil-alt"
@label="edit"
@action={{this.onEditBookmark}}
@class="bookmark-menu__row-btn btn-transparent"
/>
</li>
<li
class="bookmark-menu__row --remove"
role="button"
tabindex="0"
data-menu-option-id="delete"
<dropdown.item
class="bookmark-menu__row -edit"
data-menu-option-id="edit"
>
<DButton
@icon="pencil-alt"
@label="edit"
@action={{this.onEditBookmark}}
@class="bookmark-menu__row-btn btn-transparent"
/>
</dropdown.item>
<dropdown.item
class="bookmark-menu__row --remove"
role="button"
tabindex="0"
data-menu-option-id="delete"
>
<DButton
@icon="trash-alt"
@label="delete"
@action={{this.onRemoveBookmark}}
@class="bookmark-menu__row-btn btn-transparent btn-danger"
/>
</dropdown.item>
{{else}}
<dropdown.item class="bookmark-menu__row-title">
{{i18n "bookmarks.also_set_reminder"}}
</dropdown.item>
<dropdown.divider />
{{#each this.reminderAtOptions as |option|}}
<dropdown.item
class="bookmark-menu__row"
data-menu-option-id={{option.id}}
>
<DButton
@icon="trash-alt"
@label="delete"
@action={{this.onRemoveBookmark}}
@class="bookmark-menu__row-btn btn-transparent btn-danger"
@label={{option.label}}
@translatedTitle={{this.reminderShortcutTimeTitle option}}
@action={{fn this.onChooseReminderOption option}}
@class="bookmark-menu__row-btn btn-transparent"
/>
</li>
</ul>
{{else}}
<span class="bookmark-menu__row-title">{{i18n
"bookmarks.also_set_reminder"
}}</span>
<ul class="bookmark-menu__actions">
{{#each this.reminderAtOptions as |option|}}
<li
class="bookmark-menu__row"
data-menu-option-id={{option.id}}
>
<DButton
@label={{option.label}}
@translatedTitle={{this.reminderShortcutTimeTitle option}}
@action={{fn this.onChooseReminderOption option}}
@class="bookmark-menu__row-btn btn-transparent"
/>
</li>
{{/each}}
</ul>
</dropdown.item>
{{/each}}
{{/if}}
</div>
</DropdownMenu>
</:content>
</DMenu>
</template>

View File

@ -1,22 +1,29 @@
import Component from "@glimmer/component";
import { fn } from "@ember/helper";
import { action } from "@ember/object";
import { service } from "@ember/service";
import DButton from "discourse/components/d-button";
import DropdownMenu from "discourse/components/dropdown-menu";
import BulkTopicActions, {
addBulkDropdownAction,
} from "discourse/components/modal/bulk-topic-actions";
import concatClass from "discourse/helpers/concat-class";
import icon from "discourse-common/helpers/d-icon";
import i18n from "discourse-common/helpers/i18n";
import DropdownSelectBoxComponent from "select-kit/components/dropdown-select-box";
import DMenu from "float-kit/components/d-menu";
const _customButtons = [];
const _customOnSelection = {};
export function addBulkDropdownButton(opts) {
_customButtons.push({
id: opts.label,
id: opts.id,
icon: opts.icon,
name: i18n(opts.label),
visible: opts.visible,
class: opts.class,
});
addBulkDropdownAction(opts.label, opts.action);
addBulkDropdownAction(opts.id, opts.action);
const actionOpts = {
label: opts.label,
setComponent: true,
@ -24,27 +31,17 @@ export function addBulkDropdownButton(opts) {
if (opts.actionType === "performAndRefresh") {
actionOpts.setComponent = false;
}
_customOnSelection[opts.label] = actionOpts;
_customOnSelection[opts.id] = actionOpts;
}
export default DropdownSelectBoxComponent.extend({
classNames: ["bulk-select-topics-dropdown"],
headerIcon: null,
showFullTitle: true,
selectKitOptions: {
showCaret: true,
showFullTitle: true,
none: "select_kit.components.bulk_select_topics_dropdown.title",
},
export default class BulkSelectTopicsDropdown extends Component {
@service modal;
@service router;
@service currentUser;
@service siteSettings;
modal: service(),
router: service(),
currentUser: service(),
siteSettings: service(),
computeContent() {
let options = [];
options = options.concat([
get buttons() {
let options = [
{
id: "update-category",
icon: "pencil-alt",
@ -119,12 +116,12 @@ export default DropdownSelectBoxComponent.extend({
name: i18n("topic_bulk_actions.delete_topics.name"),
visible: ({ currentUser }) => currentUser.staff,
},
]);
];
return [...options, ..._customButtons].filter(({ visible }) => {
if (visible) {
return visible({
topics: this.bulkSelectHelper.selected,
topics: this.args.bulkSelectHelper.selected,
currentUser: this.currentUser,
siteSettings: this.siteSettings,
});
@ -132,7 +129,7 @@ export default DropdownSelectBoxComponent.extend({
return true;
}
});
},
}
showBulkTopicActionsModal(actionName, title, opts = {}) {
let allowSilent = false;
@ -160,14 +157,14 @@ export default DropdownSelectBoxComponent.extend({
action: actionName,
title,
description,
bulkSelectHelper: this.bulkSelectHelper,
bulkSelectHelper: this.args.bulkSelectHelper,
refreshClosure: () => this.router.refresh(),
allowSilent,
initialAction,
initialActionLabel,
},
});
},
}
@action
onSelect(id) {
@ -228,5 +225,43 @@ export default DropdownSelectBoxComponent.extend({
});
}
}
},
});
this.dMenu.close();
}
@action
onRegisterApi(api) {
this.dMenu = api;
}
<template>
<DMenu
@modalForMobile={{true}}
@autofocus={{true}}
@identifier="bulk-select-topics-dropdown"
@onRegisterApi={{this.onRegisterApi}}
>
<:trigger>
<span class="d-button-label">
{{i18n "select_kit.components.bulk_select_topics_dropdown.title"}}
</span>
{{icon "angle-down"}}
</:trigger>
<:content>
<DropdownMenu as |dropdown|>
{{#each this.buttons as |button|}}
<dropdown.item>
<DButton
@translatedLabel={{button.name}}
@icon={{button.icon}}
class={{concatClass "btn-transparent" button.id button.class}}
@action={{fn this.onSelect button.id}}
/>
</dropdown.item>
{{/each}}
</DropdownMenu>
</:content>
</DMenu>
</template>
}

View File

@ -0,0 +1,17 @@
import { hash } from "@ember/helper";
const DropdownItem = <template>
<li class="dropdown-menu__item" ...attributes>{{yield}}</li>
</template>;
const DropdownDivider = <template>
<li ...attributes><hr class="dropdown-menu__divider" /></li>
</template>;
const DropdownMenu = <template>
<ul class="dropdown-menu" ...attributes>
{{yield (hash item=DropdownItem divider=DropdownDivider)}}
</ul>
</template>;
export default DropdownMenu;

View File

@ -4,6 +4,7 @@ import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import { and, not, or } from "truth-helpers";
import DButton from "discourse/components/d-button";
import DropdownMenu from "discourse/components/dropdown-menu";
import concatClass from "discourse/helpers/concat-class";
import icon from "discourse-common/helpers/d-icon";
import getURL from "discourse-common/lib/get-url";
@ -85,249 +86,230 @@ export default class TopicAdminMenu extends Component {
<span class="topic-admin-menu-button-container">
<span class="topic-admin-menu-button">
<DMenu
@identifier="topic-admin-menu"
@onRegisterApi={{this.onRegisterApi}}
@triggerClass="toggle-admin-menu"
@modalForMobile={{true}}
@autofocus={{true}}
>
<:trigger>
{{icon "wrench"}}
</:trigger>
<:content>
<div class="popup-menu topic-admin-popup-menu">
<ul>
<ul class="topic-admin-menu-topic">
{{#if
(or
this.currentUser.canManageTopic
this.details.can_split_merge_topic
)
}}
<li class="topic-admin-multi-select">
<DButton
class="btn-transparent"
@label="topic.actions.multi_select"
@action={{fn this.onButtonAction "toggleMultiSelect"}}
@icon="tasks"
/>
</li>
{{/if}}
<DropdownMenu as |dropdown|>
{{#if
(or
this.currentUser.canManageTopic
this.details.can_split_merge_topic
)
}}
<dropdown.item class="topic-admin-multi-select">
<DButton
class="btn-transparent"
@label="topic.actions.multi_select"
@action={{fn this.onButtonAction "toggleMultiSelect"}}
@icon="tasks"
/>
</dropdown.item>
{{/if}}
{{#if
(or
this.currentUser.canManageTopic
this.details.can_moderate_category
)
}}
{{#if this.canDelete}}
<li class="topic-admin-delete">
<DButton
@label="topic.actions.delete"
@action={{fn this.onButtonAction "deleteTopic"}}
@icon="far-trash-alt"
class="popup-menu-btn-danger btn-danger btn-transparent"
/>
</li>
{{else if this.canRecover}}
<li class="topic-admin-recover">
<DButton
class="btn-transparent"
@label="topic.actions.recover"
@action={{fn this.onButtonAction "recoverTopic"}}
@icon="undo"
/>
</li>
{{/if}}
{{/if}}
{{#if this.details.can_close_topic}}
<li
class={{if
@topic.closed
"topic-admin-open"
"topic-admin-close"
}}
>
<DButton
class="btn-transparent"
@label={{if
@topic.closed
"topic.actions.open"
"topic.actions.close"
}}
@action={{fn this.onButtonAction "toggleClosed"}}
@icon={{if @topic.closed "unlock" "lock"}}
/>
</li>
{{/if}}
{{#if
(and
this.details.can_pin_unpin_topic
(not this.isPrivateMessage)
(or this.visible this.featured)
)
}}
<li class="topic-admin-pin">
<DButton
class="btn-transparent"
@label={{if
this.featured
"topic.actions.unpin"
"topic.actions.pin"
}}
@action={{fn this.onButtonAction "showFeatureTopic"}}
@icon="thumbtack"
/>
</li>
{{/if}}
{{#if
(and
this.details.can_archive_topic
(not this.isPrivateMessage)
)
}}
<li class="topic-admin-archive">
<DButton
class="btn-transparent"
@label={{if
this.archived
"topic.actions.unarchive"
"topic.actions.archive"
}}
@action={{fn this.onButtonAction "toggleArchived"}}
@icon="folder"
/>
</li>
{{/if}}
{{#if this.details.can_toggle_topic_visibility}}
<li class="topic-admin-visible">
<DButton
class="btn-transparent"
@label={{if
this.visible
"topic.actions.invisible"
"topic.actions.visible"
}}
@action={{fn this.onButtonAction "toggleVisibility"}}
@icon={{if this.visible "far-eye-slash" "far-eye"}}
/>
</li>
{{/if}}
{{#if (and this.details.can_convert_topic)}}
<li class="topic-admin-convert">
<DButton
class="btn-transparent"
@label={{if
this.isPrivateMessage
"topic.actions.make_public"
"topic.actions.make_private"
}}
@action={{fn
this.onButtonAction
(if
this.isPrivateMessage
"convertToPublicTopic"
"convertToPrivateMessage"
)
}}
@icon={{if
this.isPrivateMessage
"comment"
"envelope"
}}
/>
</li>
{{/if}}
</ul>
<ul class="topic-admin-menu-time">
{{#if this.currentUser.canManageTopic}}
<li class="admin-topic-timer-update">
<DButton
class="btn-transparent"
@label="topic.actions.timed_update"
@action={{fn
this.onButtonAction
"showTopicTimerModal"
}}
@icon="far-clock"
/>
</li>
{{#if this.currentUser.staff}}
<li class="topic-admin-change-timestamp">
<DButton
class="btn-transparent"
@label="topic.change_timestamp.title"
@action={{fn
this.onButtonAction
"showChangeTimestamp"
}}
@icon="calendar-alt"
/>
</li>
{{/if}}
<li class="topic-admin-reset-bump-date">
<DButton
class="btn-transparent"
@label="topic.actions.reset_bump_date"
@action={{fn this.onButtonAction "resetBumpDate"}}
@icon="anchor"
/>
</li>
<li class="topic-admin-slow-mode">
<DButton
class="btn-transparent"
@label="topic.actions.slow_mode"
@action={{fn
this.onButtonAction
"showTopicSlowModeUpdate"
}}
@icon="hourglass-start"
/>
</li>
{{/if}}
</ul>
{{#if (or this.currentUser.staff this.extraButtons.length)}}
<ul class="topic-admin-menu-undefined">
{{#if this.currentUser.staff}}
<li class="topic-admin-moderation-history">
<DButton
class="btn-transparent"
@label="review.moderation_history"
@href={{this.topicModerationHistoryUrl}}
@icon="list"
/>
</li>
{{/if}}
{{#each this.extraButtons as |button|}}
<li>
<DButton
@label={{button.label}}
@translatedLabel={{button.translatedLabel}}
@icon={{button.icon}}
class={{concatClass
"btn-transparent"
button.className
}}
@action={{fn
this.onExtraButtonAction
button.action
}}
/>
</li>
{{/each}}
</ul>
{{#if
(or
this.currentUser.canManageTopic
this.details.can_moderate_category
)
}}
{{#if this.canDelete}}
<dropdown.item class="topic-admin-delete">
<DButton
@label="topic.actions.delete"
@action={{fn this.onButtonAction "deleteTopic"}}
@icon="far-trash-alt"
class="popup-menu-btn-danger btn-danger btn-transparent"
/>
</dropdown.item>
{{else if this.canRecover}}
<dropdown.item class="topic-admin-recover">
<DButton
class="btn-transparent"
@label="topic.actions.recover"
@action={{fn this.onButtonAction "recoverTopic"}}
@icon="undo"
/>
</dropdown.item>
{{/if}}
</ul>
</div>
{{/if}}
{{#if this.details.can_close_topic}}
<dropdown.item
class={{if
@topic.closed
"topic-admin-open"
"topic-admin-close"
}}
>
<DButton
class="btn-transparent"
@label={{if
@topic.closed
"topic.actions.open"
"topic.actions.close"
}}
@action={{fn this.onButtonAction "toggleClosed"}}
@icon={{if @topic.closed "unlock" "lock"}}
/>
</dropdown.item>
{{/if}}
{{#if
(and
this.details.can_pin_unpin_topic
(not this.isPrivateMessage)
(or this.visible this.featured)
)
}}
<dropdown.item class="topic-admin-pin">
<DButton
class="btn-transparent"
@label={{if
this.featured
"topic.actions.unpin"
"topic.actions.pin"
}}
@action={{fn this.onButtonAction "showFeatureTopic"}}
@icon="thumbtack"
/>
</dropdown.item>
{{/if}}
{{#if
(and
this.details.can_archive_topic (not this.isPrivateMessage)
)
}}
<dropdown.item class="topic-admin-archive">
<DButton
class="btn-transparent"
@label={{if
this.archived
"topic.actions.unarchive"
"topic.actions.archive"
}}
@action={{fn this.onButtonAction "toggleArchived"}}
@icon="folder"
/>
</dropdown.item>
{{/if}}
{{#if this.details.can_toggle_topic_visibility}}
<dropdown.item class="topic-admin-visible">
<DButton
class="btn-transparent"
@label={{if
this.visible
"topic.actions.invisible"
"topic.actions.visible"
}}
@action={{fn this.onButtonAction "toggleVisibility"}}
@icon={{if this.visible "far-eye-slash" "far-eye"}}
/>
</dropdown.item>
{{/if}}
{{#if (and this.details.can_convert_topic)}}
<dropdown.item class="topic-admin-convert">
<DButton
class="btn-transparent"
@label={{if
this.isPrivateMessage
"topic.actions.make_public"
"topic.actions.make_private"
}}
@action={{fn
this.onButtonAction
(if
this.isPrivateMessage
"convertToPublicTopic"
"convertToPrivateMessage"
)
}}
@icon={{if this.isPrivateMessage "comment" "envelope"}}
/>
</dropdown.item>
{{/if}}
<dropdown.divider />
{{#if this.currentUser.canManageTopic}}
<dropdown.item class="admin-topic-timer-update">
<DButton
class="btn-transparent"
@label="topic.actions.timed_update"
@action={{fn this.onButtonAction "showTopicTimerModal"}}
@icon="far-clock"
/>
</dropdown.item>
{{#if this.currentUser.staff}}
<dropdown.item class="topic-admin-change-timestamp">
<DButton
class="btn-transparent"
@label="topic.change_timestamp.title"
@action={{fn this.onButtonAction "showChangeTimestamp"}}
@icon="calendar-alt"
/>
</dropdown.item>
{{/if}}
<dropdown.item class="topic-admin-reset-bump-date">
<DButton
class="btn-transparent"
@label="topic.actions.reset_bump_date"
@action={{fn this.onButtonAction "resetBumpDate"}}
@icon="anchor"
/>
</dropdown.item>
<dropdown.item class="topic-admin-slow-mode">
<DButton
class="btn-transparent"
@label="topic.actions.slow_mode"
@action={{fn
this.onButtonAction
"showTopicSlowModeUpdate"
}}
@icon="hourglass-start"
/>
</dropdown.item>
<dropdown.divider />
{{/if}}
{{#if (or this.currentUser.staff this.extraButtons.length)}}
{{#if this.currentUser.staff}}
<dropdown.item class="topic-admin-moderation-history">
<DButton
class="btn-transparent"
@label="review.moderation_history"
@href={{this.topicModerationHistoryUrl}}
@icon="list"
/>
</dropdown.item>
{{/if}}
{{#each this.extraButtons as |button|}}
<dropdown.item>
<DButton
@label={{button.label}}
@translatedLabel={{button.translatedLabel}}
@icon={{button.icon}}
class={{concatClass "btn-transparent" button.className}}
@action={{fn this.onExtraButtonAction button.action}}
/>
</dropdown.item>
{{/each}}
{{/if}}
</DropdownMenu>
</:content>
</DMenu>
</span>

View File

@ -1,5 +1,6 @@
import $ from "jquery";
import { h } from "virtual-dom";
import { addBulkDropdownButton } from "discourse/components/bulk-select-topics-dropdown";
import {
addApiImageWrapperButtonClickEvent,
addComposerUploadHandler,
@ -141,7 +142,6 @@ import {
replaceIcon,
} from "discourse-common/lib/icon-library";
import { addImageWrapperButton } from "discourse-markdown-it/features/image-controls";
import { addBulkDropdownButton } from "select-kit/components/bulk-select-topics-dropdown";
import { CUSTOM_USER_SEARCH_OPTIONS } from "select-kit/components/user-chooser";
import { modifySelectKit } from "select-kit/mixins/plugin-api";

View File

@ -1,7 +1,7 @@
import EmberObject from "@ember/object";
import BulkSelectTopicsDropdown from "discourse/components/bulk-select-topics-dropdown";
import rawRenderGlimmer from "discourse/lib/raw-render-glimmer";
import i18n from "discourse-common/helpers/i18n";
import BulkSelectTopicsDropdown from "select-kit/components/bulk-select-topics-dropdown";
export default class extends EmberObject {
get selectedCount() {

View File

@ -827,8 +827,8 @@ export default createWidget("post-menu", {
this.menu.show(event.target, {
identifier: "admin-post-menu",
component: AdminPostMenu,
extraClassName: "popup-menu",
modalForMobile: true,
autofocus: true,
data: {
scheduleRerender: this.scheduleRerender.bind(this),
transformedPost: this.attrs,

View File

@ -0,0 +1,37 @@
import { render } from "@ember/test-helpers";
import { module, test } from "qunit";
import DropdownMenu from "discourse/components/dropdown-menu";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
module("Integration | Component | <DropdownMenu />", function (hooks) {
setupRenderingTest(hooks);
test("dropdown menu", async function (assert) {
await render(<template><DropdownMenu class="test" /></template>);
assert
.dom("ul.dropdown-menu.test")
.exists("it renders the dropdown menu with custom class");
});
test("dropdown menu item", async function (assert) {
await render(<template>
<DropdownMenu as |dm|><dm.item class="test">test</dm.item></DropdownMenu>
</template>);
assert
.dom("li.dropdown-menu__item.test")
.exists("it renders the item with custom class")
.hasText("test");
});
test("dropdown menu divider", async function (assert) {
await render(<template>
<DropdownMenu as |dm|><dm.divider class="test" /></DropdownMenu>
</template>);
assert
.dom("li.test hr.dropdown-menu__divider")
.exists("it renders the divider with custom class");
});
});

View File

@ -1,7 +1,9 @@
import Component from "@glimmer/component";
import { concat } from "@ember/helper";
import { inject as service } from "@ember/service";
import { and } from "truth-helpers";
import DModal from "discourse/components/d-modal";
import concatClass from "discourse/helpers/concat-class";
import DFloatBody from "float-kit/components/d-float-body";
export default class DInlineFloat extends Component {
@ -15,6 +17,10 @@ export default class DInlineFloat extends Component {
@hideHeader={{true}}
data-identifier={{@instance.options.identifier}}
data-content
class={{concatClass
"fk-d-menu-modal"
(concat @instance.options.identifier "-content")
}}
>
{{#if @instance.options.component}}
<@instance.options.component

View File

@ -99,6 +99,8 @@ export default class DMenu extends Component {
(concat this.options.identifier "-content")
}}
@inline={{(isTesting)}}
data-identifier={{@instance.options.identifier}}
data-content
>
{{#if (has-block)}}
{{yield this.componentArgs}}

View File

@ -48,4 +48,4 @@
@import "user-stream-item";
@import "user-stream";
@import "widget-dropdown";
@import "admin-post-menu";
@import "dropdown-menu";

View File

@ -1,32 +0,0 @@
[data-content][data-identifier="admin-post-menu"] {
.d-modal__body {
padding: 0;
}
ul {
padding: 0.5rem;
margin: 0;
list-style: none;
li .btn {
width: 100%;
justify-content: flex-start;
}
li {
margin-bottom: 2px;
border: none;
&:last-child {
margin-bottom: 0;
}
}
.btn {
justify-content: left;
text-align: left;
width: 100%;
padding: 0.5em;
}
}
}

View File

@ -1,22 +1,16 @@
.bookmark-menu-content {
.bookmark-menu__body {
background: var(--secondary);
display: flex;
flex-direction: column;
.dropdown-menu {
padding: 0;
min-width: 200px;
}
.bookmark-menu__actions {
margin: 0;
padding: 0;
list-style: none;
}
.bookmark-menu__title {
display: flex;
align-items: center;
gap: 0.75em;
background: var(--tertiary-low);
color: var(--tertiary);
padding: 0.75rem;
padding: 0.75rem 1rem;
font-weight: bold;
.d-icon {
@ -25,7 +19,6 @@
}
.bookmark-menu__row {
width: 100%;
display: flex;
&:hover,
@ -39,19 +32,18 @@
}
.bookmark-menu__row-title {
padding: 0.75rem;
border-bottom: 1px solid var(--primary-low);
padding: 0.75rem 1rem;
font-weight: bold;
}
.bookmark-menu__row-btn {
margin: 0;
padding: 0.75rem !important;
width: 100%;
text-align: left;
justify-content: left !important;
gap: 0.75em;
gap: 0.75rem;
color: var(--primary);
&:hover,
&:focus {
color: var(--primary) !important;

View File

@ -0,0 +1,17 @@
.dropdown-menu {
padding: 0;
margin: 0;
list-style: none;
&__item {
.btn {
padding: 0.65rem 1rem;
width: 100%;
justify-content: flex-start;
}
}
&__divider {
margin: 0rem;
}
}

View File

@ -3,3 +3,4 @@
@import "user-stream-item";
@import "more-topics";
@import "bookmark-menu";
@import "dropdown-menu";

View File

@ -1,11 +0,0 @@
.bookmark-menu {
&__row {
border-bottom: 1px solid var(--primary-low);
&:last-child {
border-bottom: none;
}
}
&__row-title {
padding: 0.75rem;
}
}

View File

@ -0,0 +1,7 @@
.dropdown-menu {
&__item {
.btn {
padding: 0.75rem 1rem;
}
}
}

View File

@ -8,7 +8,7 @@ module PageObjects
end
def open?
has_css?(".bookmark-menu__body")
has_css?(".bookmark-menu-content")
end
end
end

View File

@ -20,14 +20,12 @@ module PageObjects
def has_bulk_select_topics_dropdown?
page.has_css?(
"#{TOPIC_LIST_HEADER_SELECTOR} .bulk-select-topics div.bulk-select-topics-dropdown",
"#{TOPIC_LIST_HEADER_SELECTOR} .bulk-select-topics .bulk-select-topics-dropdown",
)
end
def click_bulk_select_topics_dropdown
find(
"#{TOPIC_LIST_HEADER_SELECTOR} .bulk-select-topics div.bulk-select-topics-dropdown",
).click
find("#{TOPIC_LIST_HEADER_SELECTOR} .bulk-select-topics .bulk-select-topics-dropdown").click
end
def click_bulk_button(name)
@ -68,7 +66,7 @@ module PageObjects
private
def bulk_select_dropdown_item(name)
"#{TOPIC_LIST_HEADER_SELECTOR} .bulk-select-topics div.bulk-select-topics-dropdown li[data-value='#{name}']"
".bulk-select-topics-dropdown-content li.dropdown-menu__item .btn.#{name}"
end
end
end

View File

@ -76,14 +76,14 @@ describe "Topic page", type: :system do
visit("/t/#{topic.slug}/#{topic.id}")
expect(".topic-admin-menu-button-container").to be_present
expect(".toggle-admin-menu").to be_present
send_keys([:shift, "a"])
expect(page).to have_css(".topic-admin-popup-menu")
expect(page).to have_css(".topic-admin-menu-content")
send_keys([:shift, "a"])
expect(page).to have_no_css(".topic-admin-popup-menu")
expect(page).to have_no_css(".topic-admin-menu-content")
end
end