mirror of
https://github.com/discourse/discourse.git
synced 2024-11-26 02:13:47 +08:00
FEATURE: adds an API to register topic footer buttons
This commit is contained in:
parent
92c52c0724
commit
6c195640b9
|
@ -39,7 +39,12 @@ export default Ember.Component.extend({
|
|||
click() {
|
||||
if (typeof this.get("action") === "string") {
|
||||
this.sendAction("action", this.get("actionParam"));
|
||||
} else {
|
||||
} else if (
|
||||
typeof this.get("action") === "object" &&
|
||||
this.get("action").value
|
||||
) {
|
||||
this.get("action").value(this.get("actionParam"));
|
||||
} else if (typeof this.get("action") === "function") {
|
||||
this.get("action")(this.get("actionParam"));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { getTopicFooterButtons } from "discourse/lib/register-topic-footer-button";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
elementId: "topic-footer-buttons",
|
||||
|
@ -11,6 +12,19 @@ export default Ember.Component.extend({
|
|||
return this.siteSettings.enable_personal_messages && isPM;
|
||||
},
|
||||
|
||||
buttons: getTopicFooterButtons(),
|
||||
|
||||
@computed("buttons.[]")
|
||||
inlineButtons(buttons) {
|
||||
return buttons.filter(button => !button.dropdown);
|
||||
},
|
||||
|
||||
// topic.assigned_to_user is for backward plugin support
|
||||
@computed("buttons.[]", "topic.assigned_to_user")
|
||||
dropdownButtons(buttons) {
|
||||
return buttons.filter(button => button.dropdown);
|
||||
},
|
||||
|
||||
@computed("topic.isPrivateMessage")
|
||||
showNotificationsButton(isPM) {
|
||||
return !isPM || this.siteSettings.enable_personal_messages;
|
||||
|
@ -50,17 +64,5 @@ export default Ember.Component.extend({
|
|||
|
||||
@computed("topic.message_archived")
|
||||
archiveLabel: archived =>
|
||||
archived ? "topic.move_to_inbox.title" : "topic.archive_message.title",
|
||||
|
||||
@computed("topic.bookmarked")
|
||||
bookmarkClass: bookmarked =>
|
||||
bookmarked ? "bookmark bookmarked" : "bookmark",
|
||||
|
||||
@computed("topic.bookmarked")
|
||||
bookmarkLabel: bookmarked =>
|
||||
bookmarked ? "bookmarked.clear_bookmarks" : "bookmarked.title",
|
||||
|
||||
@computed("topic.bookmarked")
|
||||
bookmarkTitle: bookmarked =>
|
||||
bookmarked ? "bookmarked.help.unbookmark" : "bookmarked.help.bookmark"
|
||||
archived ? "topic.move_to_inbox.title" : "topic.archive_message.title"
|
||||
});
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
import { registerTopicFooterButton } from "discourse/lib/register-topic-footer-button";
|
||||
|
||||
export default {
|
||||
name: "topic-footer-buttons",
|
||||
|
||||
initialize() {
|
||||
registerTopicFooterButton({
|
||||
id: "share",
|
||||
icon: "link",
|
||||
priority: 999,
|
||||
label: "topic.share.title",
|
||||
title: "topic.share.help",
|
||||
action() {
|
||||
this.appEvents.trigger(
|
||||
"share:url",
|
||||
this.get("topic.shareUrl"),
|
||||
$("#topic-footer-buttons")
|
||||
);
|
||||
},
|
||||
dropdown() {
|
||||
return this.site.mobileView;
|
||||
},
|
||||
classNames: ["share"],
|
||||
dependentKeys: ["topic.shareUrl", "topic.isPrivateMessage"],
|
||||
displayed() {
|
||||
return !this.get("topic.isPrivateMessage");
|
||||
}
|
||||
});
|
||||
|
||||
registerTopicFooterButton({
|
||||
id: "flag",
|
||||
icon: "flag",
|
||||
priority: 998,
|
||||
label: "topic.flag_topic.title",
|
||||
title: "topic.flag_topic.help",
|
||||
action: "showFlagTopic",
|
||||
dropdown() {
|
||||
return this.site.mobileView;
|
||||
},
|
||||
classNames: ["flag-topic"],
|
||||
dependentKeys: ["topic.details.can_flag_topic", "topic.isPrivateMessage"],
|
||||
displayed() {
|
||||
return (
|
||||
this.get("topic.details.can_flag_topic") &&
|
||||
!this.get("topic.isPrivateMessage")
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
registerTopicFooterButton({
|
||||
id: "invite",
|
||||
icon: "users",
|
||||
priority: 997,
|
||||
label: "topic.invite_reply.title",
|
||||
title: "topic.invite_reply.help",
|
||||
action: "showInvite",
|
||||
dropdown() {
|
||||
return this.site.mobileView;
|
||||
},
|
||||
classNames: ["invite-topic"],
|
||||
dependentKeys: ["canInviteTo", "inviteDisabled"],
|
||||
displayed() {
|
||||
return this.get("canInviteTo");
|
||||
},
|
||||
disabled() {
|
||||
return this.get("inviteDisabled");
|
||||
}
|
||||
});
|
||||
|
||||
registerTopicFooterButton({
|
||||
dependentKeys: ["topic.bookmarked", "topic.isPrivateMessage"],
|
||||
id: "bookmark",
|
||||
icon: "bookmark",
|
||||
priority: 1000,
|
||||
classNames() {
|
||||
const bookmarked = this.get("topic.bookmarked");
|
||||
return bookmarked ? ["bookmark", "bookmarked"] : ["bookmark"];
|
||||
},
|
||||
label() {
|
||||
const bookmarked = this.get("topic.bookmarked");
|
||||
return bookmarked ? "bookmarked.clear_bookmarks" : "bookmarked.title";
|
||||
},
|
||||
title() {
|
||||
const bookmarked = this.get("topic.bookmarked");
|
||||
return bookmarked
|
||||
? "bookmarked.help.unbookmark"
|
||||
: "bookmarked.help.bookmark";
|
||||
},
|
||||
action: "toggleBookmark",
|
||||
dropdown() {
|
||||
return this.site.mobileView;
|
||||
},
|
||||
displayed() {
|
||||
return !this.get("topic.isPrivateMessage");
|
||||
}
|
||||
});
|
||||
|
||||
registerTopicFooterButton({
|
||||
id: "archive",
|
||||
priority: 1001,
|
||||
icon() {
|
||||
return this.get("archiveIcon");
|
||||
},
|
||||
label() {
|
||||
return this.get("archiveLabel");
|
||||
},
|
||||
title() {
|
||||
return this.get("archiveTitle");
|
||||
},
|
||||
action: "toggleArchiveMessage",
|
||||
classNames: ["standard", "archive-topic"],
|
||||
dependentKeys: [
|
||||
"canArchive",
|
||||
"archiveIcon",
|
||||
"archiveLabel",
|
||||
"archiveTitle",
|
||||
"toggleArchiveMessage"
|
||||
],
|
||||
displayed() {
|
||||
return this.get("canArchive");
|
||||
}
|
||||
});
|
||||
|
||||
registerTopicFooterButton({
|
||||
id: "edit-message",
|
||||
priority: 750,
|
||||
icon: "pencil-alt",
|
||||
label: "topic.edit_message.title",
|
||||
title: "topic.edit_message.help",
|
||||
action: "editFirstPost",
|
||||
classNames: ["edit-message"],
|
||||
dependentKeys: ["editFirstPost", "showEditOnFooter"],
|
||||
displayed() {
|
||||
return this.get("showEditOnFooter");
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
|
@ -19,6 +19,7 @@ import { addFlagProperty } from "discourse/components/site-header";
|
|||
import { addPopupMenuOptionsCallback } from "discourse/controllers/composer";
|
||||
import { extraConnectorClass } from "discourse/lib/plugin-connectors";
|
||||
import { addPostSmallActionIcon } from "discourse/widgets/post-small-action";
|
||||
import { registerTopicFooterButton } from "discourse/lib/register-topic-footer-button";
|
||||
import { addDiscoveryQueryParam } from "discourse/controllers/discovery-sortable";
|
||||
import { addTagsHtmlCallback } from "discourse/lib/render-tags";
|
||||
import { addUserMenuGlyph } from "discourse/widgets/user-menu";
|
||||
|
@ -41,7 +42,7 @@ import Sharing from "discourse/lib/sharing";
|
|||
import { addComposerUploadHandler } from "discourse/components/composer-editor";
|
||||
|
||||
// If you add any methods to the API ensure you bump up this number
|
||||
const PLUGIN_API_VERSION = "0.8.27";
|
||||
const PLUGIN_API_VERSION = "0.8.28";
|
||||
|
||||
class PluginApi {
|
||||
constructor(version, container) {
|
||||
|
@ -599,6 +600,21 @@ class PluginApi {
|
|||
extraConnectorClass(`${outletName}/${connectorName}`, klass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a small icon to be used for custom small post actions
|
||||
*
|
||||
* ```javascript
|
||||
* api.registerTopicFooterButton({
|
||||
* key: "flag"
|
||||
* icon: "flag"
|
||||
* action: (context) => console.log(context.get("topic.id"))
|
||||
* });
|
||||
* ```
|
||||
**/
|
||||
registerTopicFooterButton(action) {
|
||||
registerTopicFooterButton(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a small icon to be used for custom small post actions
|
||||
*
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
let _topicFooterButtons = [];
|
||||
|
||||
export function registerTopicFooterButton(button) {
|
||||
const defaultButton = {
|
||||
// id of the button, required
|
||||
id: null,
|
||||
|
||||
// icon displayed on the button
|
||||
icon: null,
|
||||
|
||||
// local key path for title attribute
|
||||
title: null,
|
||||
translatedTitle: null,
|
||||
|
||||
// local key path for label
|
||||
label: null,
|
||||
translatedLabel: null,
|
||||
|
||||
// is this button disaplyed in the mobile dropdown or as an inline button ?
|
||||
dropdown: false,
|
||||
|
||||
// css class appended to the button
|
||||
classNames: [],
|
||||
|
||||
// computed properties which should force a button state refresh
|
||||
// eg: ["topic.bookmarked", "topic.category_id"]
|
||||
dependentKeys: [],
|
||||
|
||||
// should we display this button ?
|
||||
displayed: true,
|
||||
|
||||
// is this button disabled ?
|
||||
disabled: false,
|
||||
|
||||
// display order, higher comes first
|
||||
priority: 0
|
||||
};
|
||||
|
||||
const normalizedButton = Object.assign(defaultButton, button);
|
||||
|
||||
if (!normalizedButton.id) {
|
||||
Ember.error(`Attempted to register a topic button: ${button} with no id.`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!normalizedButton.icon && !normalizedButton.title) {
|
||||
Ember.error(
|
||||
`Attempted to register a topic button: ${
|
||||
button.id
|
||||
} with no icon or title.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
_topicFooterButtons.push(normalizedButton);
|
||||
|
||||
_topicFooterButtons = _topicFooterButtons.uniqBy("id");
|
||||
}
|
||||
|
||||
export function getTopicFooterButtons() {
|
||||
const dependentKeys = [].concat(
|
||||
..._topicFooterButtons.map(tfb => tfb.dependentKeys).filter(x => x)
|
||||
);
|
||||
|
||||
const computedFunc = Ember.computed({
|
||||
get() {
|
||||
const _isFunction = descriptor =>
|
||||
descriptor && typeof descriptor === "function";
|
||||
|
||||
const _compute = (button, property) => {
|
||||
const field = button[property];
|
||||
|
||||
if (_isFunction(field)) {
|
||||
return field.apply(this);
|
||||
}
|
||||
|
||||
return field;
|
||||
};
|
||||
|
||||
return _topicFooterButtons
|
||||
.filter(button => _compute(button, "displayed"))
|
||||
.map(button => {
|
||||
const computedButon = {};
|
||||
|
||||
computedButon.id = button.id;
|
||||
|
||||
const label = _compute(button, "label");
|
||||
computedButon.label = label
|
||||
? I18n.t(label)
|
||||
: _compute(button, "translatedLabel");
|
||||
|
||||
const title = _compute(button, "title");
|
||||
computedButon.title = title
|
||||
? I18n.t(title)
|
||||
: _compute(button, "translatedTitle");
|
||||
|
||||
computedButon.classNames = (
|
||||
_compute(button, "classNames") || []
|
||||
).join(" ");
|
||||
|
||||
computedButon.icon = _compute(button, "icon");
|
||||
computedButon.disabled = _compute(button, "disabled");
|
||||
computedButon.dropdown = _compute(button, "dropdown");
|
||||
computedButon.priority = _compute(button, "priority");
|
||||
|
||||
if (_isFunction(button.action)) {
|
||||
computedButon.action = () => button.action.apply(this);
|
||||
} else {
|
||||
const actionName = button.action;
|
||||
computedButon.action = () => this[actionName]();
|
||||
}
|
||||
|
||||
return computedButon;
|
||||
})
|
||||
.sortBy("priority")
|
||||
.reverse();
|
||||
}
|
||||
});
|
||||
|
||||
return computedFunc.property.apply(computedFunc, dependentKeys);
|
||||
}
|
||||
|
||||
export function clearTopicFooterButtons() {
|
||||
_topicFooterButtons = [];
|
||||
}
|
|
@ -18,55 +18,20 @@
|
|||
convertToPrivateMessage=convertToPrivateMessage}}
|
||||
{{/if}}
|
||||
|
||||
{{#unless topic.isPrivateMessage}}
|
||||
{{#if site.mobileView}}
|
||||
{{topic-footer-mobile-dropdown topic=topic
|
||||
showInvite=showInvite
|
||||
showFlagTopic=showFlagTopic}}
|
||||
{{else}}
|
||||
{{d-button class=(concat "btn-default " bookmarkClass)
|
||||
title=bookmarkTitle
|
||||
label=bookmarkLabel
|
||||
icon="bookmark"
|
||||
action=toggleBookmark}}
|
||||
|
||||
{{share-button url=topic.shareUrl}}
|
||||
|
||||
{{#if topic.details.can_flag_topic}}
|
||||
{{d-button class="btn-default flag-topic"
|
||||
title="topic.flag_topic.help"
|
||||
label="topic.flag_topic.title"
|
||||
icon="flag"
|
||||
action=showFlagTopic}}
|
||||
{{/if}}
|
||||
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
|
||||
{{#if canInviteTo}}
|
||||
{{d-button class="btn-default invite-topic"
|
||||
title="topic.invite_reply.help"
|
||||
label="topic.invite_reply.title"
|
||||
icon="users"
|
||||
action=showInvite
|
||||
disabled=inviteDisabled}}
|
||||
{{#if site.mobileView}}
|
||||
{{topic-footer-mobile-dropdown topic=topic content=dropdownButtons}}
|
||||
{{/if}}
|
||||
|
||||
{{#if canArchive}}
|
||||
{{d-button class="btn-default standard archive-topic"
|
||||
title=archiveTitle
|
||||
label=archiveLabel
|
||||
icon=archiveIcon
|
||||
action=toggleArchiveMessage}}
|
||||
{{/if}}
|
||||
|
||||
{{#if showEditOnFooter}}
|
||||
{{d-button class="btn-default edit-message"
|
||||
title="topic.edit_message.help"
|
||||
label="topic.edit_message.title"
|
||||
icon="pencil-alt"
|
||||
action=editFirstPost}}
|
||||
{{/if}}
|
||||
{{#each inlineButtons as |button|}}
|
||||
{{d-button
|
||||
id=(concat "topic-footer-button-" button.id)
|
||||
class=(concat "btn-default topic-footer-button " button.classNames)
|
||||
action=button.action
|
||||
icon=button.icon
|
||||
translatedLabel=button.label
|
||||
translatedTitle=button.title
|
||||
disabled=button.disabled}}
|
||||
{{/each}}
|
||||
|
||||
{{plugin-outlet name="topic-footer-main-buttons-before-create"
|
||||
args=(hash topic=topic)
|
||||
|
|
|
@ -144,18 +144,16 @@ export default Ember.Component.extend(
|
|||
didComputeAttributes() {},
|
||||
|
||||
willComputeContent(content) {
|
||||
return content;
|
||||
return applyContentPluginApiCallbacks(
|
||||
this.get("pluginApiIdentifiers"),
|
||||
content,
|
||||
this
|
||||
);
|
||||
},
|
||||
computeContent(content) {
|
||||
return content;
|
||||
},
|
||||
_beforeDidComputeContent(content) {
|
||||
content = applyContentPluginApiCallbacks(
|
||||
this.get("pluginApiIdentifiers"),
|
||||
content,
|
||||
this
|
||||
);
|
||||
|
||||
let existingCreatedComputedContent = [];
|
||||
if (!this.get("allowContentReplacement")) {
|
||||
existingCreatedComputedContent = this.get("computedContent").filterBy(
|
||||
|
|
|
@ -16,7 +16,11 @@ export default Ember.Component.extend(UtilsMixin, {
|
|||
"ariaLabel:aria-label",
|
||||
"guid:data-guid"
|
||||
],
|
||||
classNameBindings: ["isHighlighted", "isSelected"],
|
||||
classNameBindings: [
|
||||
"isHighlighted",
|
||||
"isSelected",
|
||||
"computedContent.originalContent.classNames"
|
||||
],
|
||||
|
||||
forceEscape: Ember.computed.alias("options.forceEscape"),
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ export default SelectKitComponent.extend({
|
|||
|
||||
@computed("computedAsyncContent.[]", "computedValue")
|
||||
filteredAsyncComputedContent(computedAsyncContent, computedValue) {
|
||||
computedAsyncContent = computedAsyncContent.filter(c => {
|
||||
computedAsyncContent = (computedAsyncContent || []).filter(c => {
|
||||
return computedValue !== get(c, "value");
|
||||
});
|
||||
|
||||
|
@ -268,12 +268,18 @@ export default SelectKitComponent.extend({
|
|||
if (this.validateSelect(computedContentItem)) {
|
||||
this.willSelect(computedContentItem);
|
||||
this.clearFilter();
|
||||
this.setProperties({
|
||||
highlighted: null,
|
||||
computedValue: computedContentItem.value
|
||||
});
|
||||
|
||||
run.next(() => this.mutateAttributes());
|
||||
const action = computedContentItem.originalContent.action;
|
||||
if (action) {
|
||||
action();
|
||||
} else {
|
||||
this.setProperties({
|
||||
highlighted: null,
|
||||
computedValue: computedContentItem.value
|
||||
});
|
||||
|
||||
run.next(() => this.mutateAttributes());
|
||||
}
|
||||
|
||||
run.schedule("afterRender", () => {
|
||||
this.didSelect(computedContentItem);
|
||||
|
|
|
@ -6,101 +6,29 @@ export default ComboBoxComponent.extend({
|
|||
filterable: false,
|
||||
autoFilterable: false,
|
||||
allowInitialValueMutation: false,
|
||||
allowAutoSelectFirst: false,
|
||||
nameProperty: "label",
|
||||
|
||||
computeHeaderContent() {
|
||||
let content = this._super(...arguments);
|
||||
const content = this._super(...arguments);
|
||||
|
||||
content.name = I18n.t("topic.controls");
|
||||
return content;
|
||||
},
|
||||
|
||||
computeContent(content) {
|
||||
const topic = this.get("topic");
|
||||
const details = topic.get("details");
|
||||
mutateAttributes() {},
|
||||
|
||||
if (details.get("can_invite_to")) {
|
||||
content.push({
|
||||
id: "invite",
|
||||
icon: "users",
|
||||
name: I18n.t("topic.invite_reply.title"),
|
||||
__sk_row_type: "noopRow"
|
||||
});
|
||||
}
|
||||
willComputeContent(content) {
|
||||
content = this._super(content);
|
||||
|
||||
if (
|
||||
(topic.get("bookmarked") && !topic.get("bookmarking")) ||
|
||||
(!topic.get("bookmarked") && topic.get("bookmarking"))
|
||||
) {
|
||||
content.push({
|
||||
id: "bookmark",
|
||||
icon: "bookmark",
|
||||
name: I18n.t("bookmarked.clear_bookmarks"),
|
||||
__sk_row_type: "noopRow"
|
||||
});
|
||||
} else {
|
||||
content.push({
|
||||
id: "bookmark",
|
||||
icon: "bookmark",
|
||||
name: I18n.t("bookmarked.title"),
|
||||
__sk_row_type: "noopRow"
|
||||
});
|
||||
}
|
||||
|
||||
content.push({
|
||||
id: "share",
|
||||
icon: "link",
|
||||
name: I18n.t("topic.share.title"),
|
||||
__sk_row_type: "noopRow"
|
||||
// TODO: this is for backward compat reasons, should be removed
|
||||
// when plugins have been updated for long enough
|
||||
content.forEach(c => {
|
||||
if (c.name) {
|
||||
c.label = c.name;
|
||||
}
|
||||
});
|
||||
|
||||
if (details.get("can_flag_topic")) {
|
||||
content.push({
|
||||
id: "flag",
|
||||
icon: "flag",
|
||||
name: I18n.t("topic.flag_topic.title"),
|
||||
__sk_row_type: "noopRow"
|
||||
});
|
||||
}
|
||||
|
||||
return content;
|
||||
},
|
||||
|
||||
autoHighlight() {},
|
||||
|
||||
actions: {
|
||||
onSelect(value) {
|
||||
const topic = this.get("topic");
|
||||
|
||||
if (!topic.get("id")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const refresh = () => {
|
||||
this._compute();
|
||||
this.deselect();
|
||||
};
|
||||
|
||||
switch (value) {
|
||||
case "flag":
|
||||
this.showFlagTopic();
|
||||
refresh();
|
||||
break;
|
||||
case "bookmark":
|
||||
topic.toggleBookmark().then(refresh());
|
||||
break;
|
||||
case "share":
|
||||
this.appEvents.trigger(
|
||||
"share:url",
|
||||
topic.get("shareUrl"),
|
||||
$("#topic-footer-buttons")
|
||||
);
|
||||
refresh();
|
||||
break;
|
||||
case "invite":
|
||||
this.showInvite();
|
||||
refresh();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -68,13 +68,15 @@ function onSelect(pluginApiIdentifiers, mutationFunction) {
|
|||
export function applyContentPluginApiCallbacks(identifiers, content, context) {
|
||||
identifiers.forEach(key => {
|
||||
(_prependContentCallbacks[key] || []).forEach(c => {
|
||||
content = c().concat(content);
|
||||
content = c()
|
||||
.concat(content)
|
||||
.uniqBy("id");
|
||||
});
|
||||
(_appendContentCallbacks[key] || []).forEach(c => {
|
||||
content = content.concat(c());
|
||||
content = content.concat(c()).uniqBy("id");
|
||||
});
|
||||
(_modifyContentCallbacks[key] || []).forEach(c => {
|
||||
content = c(context, content);
|
||||
content = c(context, content).uniqBy("id");
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
.topic-footer-mobile-dropdown {
|
||||
.select-kit-row {
|
||||
&.bookmarked {
|
||||
.d-icon {
|
||||
color: $tertiary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
import { clearTopicFooterButtons } from "discourse/lib/register-topic-footer-button";
|
||||
import { acceptance } from "helpers/qunit-helpers";
|
||||
|
||||
let _test;
|
||||
|
||||
acceptance("Topic footer buttons mobile", {
|
||||
loggedIn: true,
|
||||
mobileView: true,
|
||||
beforeEach() {
|
||||
I18n.translations[I18n.locale].js.test = {
|
||||
title: "My title",
|
||||
label: "My Label"
|
||||
};
|
||||
|
||||
withPluginApi("0.8.28", api => {
|
||||
api.registerTopicFooterButton({
|
||||
id: "my-button",
|
||||
icon: "user",
|
||||
label: "test.label",
|
||||
title: "test.title",
|
||||
dropdown: true,
|
||||
action() {
|
||||
_test = 2;
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
afterEach() {
|
||||
clearTopicFooterButtons();
|
||||
_test = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.test("default", async assert => {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
|
||||
assert.equal(_test, null);
|
||||
|
||||
const subject = selectKit(".topic-footer-mobile-dropdown");
|
||||
await subject.expand();
|
||||
await subject.selectRowByValue("my-button");
|
||||
|
||||
assert.equal(_test, 2);
|
||||
});
|
|
@ -0,0 +1,184 @@
|
|||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
import componentTest from "helpers/component-test";
|
||||
import Topic from "discourse/models/topic";
|
||||
import { clearTopicFooterButtons } from "discourse/lib/register-topic-footer-button";
|
||||
|
||||
const buildTopic = function() {
|
||||
return Topic.create({
|
||||
id: 1234,
|
||||
title: "Qunit Test Topic"
|
||||
});
|
||||
};
|
||||
|
||||
moduleForComponent("topic-footer-buttons-desktop", {
|
||||
integration: true,
|
||||
beforeEach() {
|
||||
I18n.translations[I18n.locale].js.test = {
|
||||
title: "My title",
|
||||
label: "My Label"
|
||||
};
|
||||
},
|
||||
|
||||
afterEach() {
|
||||
clearTopicFooterButtons();
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("default", {
|
||||
template: "{{topic-footer-buttons topic=topic}}",
|
||||
beforeEach() {
|
||||
withPluginApi("0.8.28", api => {
|
||||
api.registerTopicFooterButton({
|
||||
id: "my-button",
|
||||
icon: "user",
|
||||
label: "test.label",
|
||||
title: "test.title"
|
||||
});
|
||||
});
|
||||
|
||||
this.set("topic", buildTopic());
|
||||
},
|
||||
async test(assert) {
|
||||
const button = await find("#topic-footer-button-my-button");
|
||||
assert.ok(exists(button), "it creates an inline button");
|
||||
|
||||
const icon = await button.find(".d-icon-user");
|
||||
assert.ok(exists(icon), "the button has the correct icon");
|
||||
|
||||
const label = await button.find(".d-button-label");
|
||||
assert.ok(exists(label), "the button has a label");
|
||||
assert.equal(
|
||||
label.text(),
|
||||
I18n.t("test.label"),
|
||||
"the button has the correct label"
|
||||
);
|
||||
|
||||
const title = button.attr("title");
|
||||
assert.equal(
|
||||
title,
|
||||
I18n.t("test.title"),
|
||||
"the button has the correct title"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("priority", {
|
||||
template: "{{topic-footer-buttons topic=topic}}",
|
||||
beforeEach() {
|
||||
withPluginApi("0.8.28", api => {
|
||||
api.registerTopicFooterButton({
|
||||
id: "my-second-button",
|
||||
priority: 750,
|
||||
icon: "user"
|
||||
});
|
||||
|
||||
api.registerTopicFooterButton({
|
||||
id: "my-third-button",
|
||||
priority: 500,
|
||||
icon: "flag"
|
||||
});
|
||||
|
||||
api.registerTopicFooterButton({
|
||||
id: "my-first-button",
|
||||
priority: 1000,
|
||||
icon: "times"
|
||||
});
|
||||
});
|
||||
|
||||
this.set("topic", buildTopic());
|
||||
},
|
||||
async test(assert) {
|
||||
const buttons = await find(".topic-footer-button");
|
||||
const firstButton = find("#topic-footer-button-my-first-button");
|
||||
const secondButton = find("#topic-footer-button-my-second-button");
|
||||
const thirdButton = find("#topic-footer-button-my-third-button");
|
||||
|
||||
assert.ok(buttons.index(firstButton) < buttons.index(secondButton));
|
||||
assert.ok(buttons.index(secondButton) < buttons.index(thirdButton));
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("with functions", {
|
||||
template: "{{topic-footer-buttons topic=topic}}",
|
||||
beforeEach() {
|
||||
withPluginApi("0.8.28", api => {
|
||||
api.registerTopicFooterButton({
|
||||
id: "my-button",
|
||||
icon() {
|
||||
return "user";
|
||||
},
|
||||
label() {
|
||||
return "test.label";
|
||||
},
|
||||
title() {
|
||||
return "test.title";
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.set("topic", buildTopic());
|
||||
},
|
||||
async test(assert) {
|
||||
const button = await find("#topic-footer-button-my-button");
|
||||
assert.ok(exists(button), "it creates an inline button");
|
||||
|
||||
const icon = await button.find(".d-icon-user");
|
||||
assert.ok(exists(icon), "the button has the correct icon");
|
||||
|
||||
const label = await button.find(".d-button-label");
|
||||
assert.ok(exists(label), "the button has a label");
|
||||
assert.equal(
|
||||
label.text(),
|
||||
I18n.t("test.label"),
|
||||
"the button has the correct label"
|
||||
);
|
||||
|
||||
const title = button.attr("title");
|
||||
assert.equal(
|
||||
title,
|
||||
I18n.t("test.title"),
|
||||
"the button has the correct title"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("action", {
|
||||
template: "<div id='test-action'></div>{{topic-footer-buttons topic=topic}}",
|
||||
beforeEach() {
|
||||
withPluginApi("0.8.28", api => {
|
||||
api.registerTopicFooterButton({
|
||||
id: "my-button",
|
||||
icon: "flag",
|
||||
action() {
|
||||
$("#test-action").text(this.get("topic.title"));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.set("topic", buildTopic());
|
||||
},
|
||||
async test(assert) {
|
||||
await click("#topic-footer-button-my-button");
|
||||
|
||||
assert.equal(find("#test-action").text(), this.get("topic.title"));
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("dropdown", {
|
||||
template: "{{topic-footer-buttons topic=topic}}",
|
||||
beforeEach() {
|
||||
withPluginApi("0.8.28", api => {
|
||||
api.registerTopicFooterButton({
|
||||
id: "my-button",
|
||||
icon: "flag",
|
||||
dropdown: true
|
||||
});
|
||||
});
|
||||
|
||||
this.set("topic", buildTopic());
|
||||
},
|
||||
async test(assert) {
|
||||
const button = await find("#topic-footer-button-my-button");
|
||||
assert.notOk(exists(button), "it doesn’t create an inline button");
|
||||
}
|
||||
});
|
|
@ -36,27 +36,11 @@ componentTest("default", {
|
|||
.value(),
|
||||
null
|
||||
);
|
||||
assert.equal(
|
||||
this.get("subject")
|
||||
.rowByIndex(0)
|
||||
.name(),
|
||||
"Bookmark"
|
||||
);
|
||||
assert.equal(
|
||||
this.get("subject")
|
||||
.rowByIndex(1)
|
||||
.name(),
|
||||
"Share"
|
||||
);
|
||||
assert.notOk(
|
||||
this.get("subject")
|
||||
.selectedRow()
|
||||
.exists(),
|
||||
"it doesn’t preselect first row"
|
||||
);
|
||||
|
||||
await this.get("subject").selectRowByValue("share");
|
||||
|
||||
assert.equal(this.get("value"), null, "it resets the value");
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue
Block a user