mirror of
https://github.com/discourse/discourse.git
synced 2024-11-23 09:26:54 -06:00
UX: Convert buttons to d-button
This commit is contained in:
parent
75d10a4098
commit
8dd7c0c984
@ -0,0 +1,7 @@
|
||||
import Button from 'discourse/components/d-button';
|
||||
|
||||
export default Button.extend({
|
||||
tabindex: 5,
|
||||
classNameBindings: [':btn-primary', ':create', 'disableSubmit:disabled'],
|
||||
title: 'composer.title',
|
||||
});
|
@ -6,7 +6,7 @@ export default Ember.Component.extend({
|
||||
|
||||
tagName: 'button',
|
||||
classNameBindings: [':btn', 'noText', 'btnType'],
|
||||
attributeBindings: ['disabled', 'translatedTitle:title'],
|
||||
attributeBindings: ['disabled', 'translatedTitle:title', 'tabindex'],
|
||||
|
||||
btnIcon: Ember.computed.notEmpty('icon'),
|
||||
|
||||
@ -23,7 +23,7 @@ export default Ember.Component.extend({
|
||||
|
||||
@computed("title")
|
||||
translatedTitle(title) {
|
||||
if (title) return I18n.t(title);
|
||||
return title ? I18n.t(title) : this.get('translatedLabel');
|
||||
},
|
||||
|
||||
@computed("label")
|
||||
|
@ -0,0 +1,13 @@
|
||||
import Button from 'discourse/components/d-button';
|
||||
|
||||
export default Button.extend({
|
||||
classNames: ['share'],
|
||||
icon: 'link',
|
||||
title: 'topic.share.help',
|
||||
label: 'topic.share.title',
|
||||
attributeBindings: ['url:data-share-url'],
|
||||
|
||||
click() {
|
||||
return true;
|
||||
}
|
||||
});
|
@ -146,11 +146,10 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||
}
|
||||
}.property('selected.name_key', 'userDetails.can_be_deleted', 'userDetails.can_delete_all_posts'),
|
||||
|
||||
canSendWarning: function() {
|
||||
if (this.get("flagTopic")) return false;
|
||||
|
||||
return (Discourse.User.currentProp('staff') && this.get('selected.name_key') === 'notify_user');
|
||||
}.property('selected.name_key'),
|
||||
@computed('flagTopic', 'selected.name_key')
|
||||
canSendWarning(flagTopic, nameKey) {
|
||||
return !flagTopic && this.currentUser.get('staff') && nameKey === 'notify_user';
|
||||
},
|
||||
|
||||
usernameChanged: function() {
|
||||
this.set('userDetails', null);
|
||||
|
@ -240,25 +240,25 @@ const Composer = RestModel.extend({
|
||||
return (this.get('titleLength') <= this.siteSettings.max_topic_title_length);
|
||||
}.property('minimumTitleLength', 'titleLength', 'post.static_doc'),
|
||||
|
||||
// The icon for the save button
|
||||
saveIcon: function () {
|
||||
switch (this.get('action')) {
|
||||
case EDIT: return iconHTML('pencil');
|
||||
case REPLY: return iconHTML('reply');
|
||||
case CREATE_TOPIC: iconHTML('plus');
|
||||
case PRIVATE_MESSAGE: iconHTML('envelope');
|
||||
@computed('action')
|
||||
saveIcon(action) {
|
||||
switch (action) {
|
||||
case EDIT: return 'pencil';
|
||||
case REPLY: return 'reply';
|
||||
case CREATE_TOPIC: 'plus';
|
||||
case PRIVATE_MESSAGE: 'envelope';
|
||||
}
|
||||
}.property('action'),
|
||||
},
|
||||
|
||||
// The text for the save button
|
||||
saveText: function() {
|
||||
switch (this.get('action')) {
|
||||
case EDIT: return I18n.t('composer.save_edit');
|
||||
case REPLY: return I18n.t('composer.reply');
|
||||
case CREATE_TOPIC: return I18n.t('composer.create_topic');
|
||||
case PRIVATE_MESSAGE: return I18n.t('composer.create_pm');
|
||||
@computed('action')
|
||||
saveLabel(action) {
|
||||
switch (action) {
|
||||
case EDIT: return 'composer.save_edit';
|
||||
case REPLY: return 'composer.reply';
|
||||
case CREATE_TOPIC: return 'composer.create_topic';
|
||||
case PRIVATE_MESSAGE: return 'composer.create_pm';
|
||||
}
|
||||
}.property('action'),
|
||||
},
|
||||
|
||||
hasMetaData: function() {
|
||||
const metaData = this.get('metaData');
|
||||
|
@ -27,10 +27,7 @@
|
||||
icon="bookmark"
|
||||
action=toggleBookmark}}
|
||||
|
||||
<button class="btn share" data-share-url={{topic.shareUrl}} title={{i18n "topic.share.help"}}>
|
||||
{{d-icon "link"}}
|
||||
{{i18n "topic.share.title"}}
|
||||
</button>
|
||||
{{share-button url=topic.shareUrl}}
|
||||
|
||||
{{#if topic.details.can_flag_topic}}
|
||||
{{d-button class="flag-topic"
|
||||
|
@ -76,7 +76,7 @@
|
||||
{{popup-input-tip validation=categoryValidation}}
|
||||
</div>
|
||||
{{#if model.archetype.hasOptions}}
|
||||
<button class='btn' {{action "showOptions"}}>{{i18n 'topic.options'}}</button>
|
||||
{{d-button action="showOptions" label="topic.options"}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
@ -107,7 +107,12 @@
|
||||
{{#if canEditTags}}
|
||||
{{tag-chooser tags=model.tags tabIndex="4" categoryId=model.categoryId}}
|
||||
{{/if}}
|
||||
<button {{action "save"}} tabindex="5" class="btn btn-primary create {{if disableSubmit 'disabled'}}" title="{{i18n 'composer.title'}}">{{{model.saveIcon}}}{{model.saveText}}</button>
|
||||
{{composer-save-button
|
||||
action=(action "save")
|
||||
icon=model.saveIcon
|
||||
label=model.saveLabel
|
||||
disableSubmit=disableSubmit}}
|
||||
|
||||
<a href {{action "cancel"}} class='cancel' tabindex="6">{{i18n 'cancel'}}</a>
|
||||
|
||||
{{#if site.mobileView}}
|
||||
|
@ -13,17 +13,38 @@
|
||||
{{/d-modal-body}}
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class='btn btn-primary' {{action "createFlag"}} disabled={{submitDisabled}} title="{{i18n 'flagging.submit_tooltip'}}">{{{submitText}}}</button>
|
||||
{{d-button
|
||||
class="btn-primary"
|
||||
action=(action "createFlag")
|
||||
disabled=submitDisabled
|
||||
title="flagging.submit_tooltip"
|
||||
translatedLabel=submitText}}
|
||||
|
||||
{{#if canSendWarning}}
|
||||
<button class="btn btn-danger" {{action "createFlagAsWarning" }} disabled={{submitDisabled}} title="{{i18n 'flagging.official_warning'}}">{{d-icon "exclamation-triangle"}} {{i18n 'flagging.official_warning'}}</button>
|
||||
{{d-button
|
||||
class="btn-danger"
|
||||
action=(action "createFlagAsWarning")
|
||||
disabled=submitDisabled
|
||||
icon="exclamation-triangle"
|
||||
label="flagging.official_warning"}}
|
||||
{{/if}}
|
||||
|
||||
{{#if canTakeAction}}
|
||||
<button class='btn btn-danger' {{action "takeAction"}} disabled={{submitDisabled}} title="{{i18n 'flagging.take_action_tooltip'}}">{{d-icon "gavel"}}{{i18n 'flagging.take_action'}}</button>
|
||||
{{d-button
|
||||
class="btn-danger"
|
||||
action=(action "takeAction")
|
||||
disabled=submitDisabled
|
||||
title="flagging.take_action_tooltip"
|
||||
icon="gavel"
|
||||
label="flagging.take_action"}}
|
||||
{{/if}}
|
||||
|
||||
{{#if canDeleteSpammer}}
|
||||
<button class="btn btn-danger" {{action "deleteSpammer" userDetails}} disabled={{submitDisabled}} title="{{i18n 'flagging.delete_spammer'}}">{{d-icon "exclamation-triangle"}} {{i18n 'flagging.delete_spammer'}}</button>
|
||||
{{d-button
|
||||
class="btn-danger"
|
||||
action=(route-action "deleteSpammer" userDetails)
|
||||
disabled=submitDisabled
|
||||
icon="exclamation-triangle"
|
||||
label="flagging.delete_spammer"}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
@ -2,7 +2,7 @@ import { createWidget } from 'discourse/widgets/widget';
|
||||
import { iconNode } from 'discourse-common/lib/icon-library';
|
||||
import { h } from 'virtual-dom';
|
||||
|
||||
const ButtonClass = {
|
||||
export const ButtonClass = {
|
||||
tagName: 'button.widget-button.btn',
|
||||
|
||||
buildClasses(attrs) {
|
||||
@ -20,7 +20,7 @@ const ButtonClass = {
|
||||
className += '-text';
|
||||
}
|
||||
} else if (hasText) {
|
||||
className += 'btn-text';
|
||||
className += ' btn-text';
|
||||
}
|
||||
|
||||
return className;
|
||||
|
@ -90,11 +90,18 @@ createWidget('header-dropdown', jQuery.extend({
|
||||
body.push(attrs.contents.call(this));
|
||||
}
|
||||
|
||||
return h('a.icon', { attributes: { href: attrs.href,
|
||||
'data-auto-route': true,
|
||||
title,
|
||||
'aria-label': title,
|
||||
id: attrs.iconId } }, body);
|
||||
return h(
|
||||
'a.icon.btn-flat',
|
||||
{ attributes: {
|
||||
href: attrs.href,
|
||||
'data-auto-route': true,
|
||||
title,
|
||||
'aria-label': title,
|
||||
id: attrs.iconId
|
||||
}
|
||||
},
|
||||
body
|
||||
);
|
||||
}
|
||||
}, dropdown));
|
||||
|
||||
|
@ -1,20 +1,15 @@
|
||||
import { iconNode } from 'discourse-common/lib/icon-library';
|
||||
import { createWidget } from 'discourse/widgets/widget';
|
||||
import { h } from 'virtual-dom';
|
||||
import { ButtonClass } from 'discourse/widgets/button';
|
||||
|
||||
createWidget('post-admin-menu-button', {
|
||||
createWidget('post-admin-menu-button', jQuery.extend(ButtonClass, {
|
||||
tagName: 'li.btn',
|
||||
buildClasses(attrs) {
|
||||
return attrs.className;
|
||||
},
|
||||
html(attrs) {
|
||||
return [iconNode(attrs.icon), I18n.t(attrs.label)];
|
||||
},
|
||||
click() {
|
||||
this.sendWidgetAction('closeAdminMenu');
|
||||
return this.sendWidgetAction(this.attrs.action);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
export default createWidget('post-admin-menu', {
|
||||
tagName: 'div.post-admin-menu.popup-menu',
|
||||
|
@ -5,7 +5,6 @@
|
||||
// Base
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
|
@ -2,11 +2,12 @@ import componentTest from 'helpers/component-test';
|
||||
moduleForComponent('d-button', {integration: true});
|
||||
|
||||
componentTest('icon only button', {
|
||||
template: '{{d-button icon="plus"}}',
|
||||
template: '{{d-button icon="plus" tabindex="3"}}',
|
||||
|
||||
test(assert) {
|
||||
assert.ok(this.$('button.btn.btn-icon.no-text').length, 'it has all the classes');
|
||||
assert.ok(this.$('button .d-icon.d-icon-plus').length, 'it has the icon');
|
||||
assert.equal(this.$('button').attr('tabindex'), "3", 'it has the tabindex');
|
||||
}
|
||||
});
|
||||
|
||||
|
16
test/javascripts/components/share-button-test.js.es6
Normal file
16
test/javascripts/components/share-button-test.js.es6
Normal file
@ -0,0 +1,16 @@
|
||||
import componentTest from 'helpers/component-test';
|
||||
moduleForComponent('share-button', {integration: true});
|
||||
|
||||
componentTest('share button', {
|
||||
template: '{{share-button url="https://eviltrout.com"}}',
|
||||
|
||||
test(assert) {
|
||||
assert.ok(this.$(`button.share`).length, 'it has all the classes');
|
||||
|
||||
assert.ok(
|
||||
this.$(`button[data-share-url="https://eviltrout.com"]`).length,
|
||||
'it has the data attribute for sharing'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -1,104 +0,0 @@
|
||||
import createStore from 'helpers/create-store';
|
||||
import AdminUser from 'admin/models/admin-user';
|
||||
import { mapRoutes } from 'discourse/mapping-router';
|
||||
|
||||
var buildPost = function(args) {
|
||||
return Discourse.Post.create(_.merge({
|
||||
id: 1,
|
||||
can_delete: true,
|
||||
version: 1
|
||||
}, args || {}));
|
||||
};
|
||||
|
||||
var buildAdminUser = function(args) {
|
||||
return AdminUser.create(_.merge({
|
||||
id: 11,
|
||||
username: 'urist'
|
||||
}, args || {}));
|
||||
};
|
||||
|
||||
moduleFor("controller:flag", "controller:flag", {
|
||||
beforeEach() {
|
||||
this.registry.register('router:main', mapRoutes());
|
||||
},
|
||||
needs: ['controller:modal']
|
||||
});
|
||||
|
||||
QUnit.test("canDeleteSpammer not staff", function(assert) {
|
||||
const store = createStore();
|
||||
|
||||
var flagController = this.subject({ model: buildPost() });
|
||||
sandbox.stub(Discourse.User, 'currentProp').withArgs('staff').returns(false);
|
||||
|
||||
const spamFlag = store.createRecord('post-action-type', {name_key: 'spam'});
|
||||
flagController.set('selected', spamFlag);
|
||||
assert.equal(flagController.get('canDeleteSpammer'), false, 'false if current user is not staff');
|
||||
});
|
||||
|
||||
var canDeleteSpammer = function(assert, flagController, postActionType, expected, testName) {
|
||||
const store = createStore();
|
||||
const flag = store.createRecord('post-action-type', {name_key: postActionType});
|
||||
flagController.set('selected', flag);
|
||||
|
||||
assert.equal(flagController.get('canDeleteSpammer'), expected, testName);
|
||||
};
|
||||
|
||||
QUnit.test("canDeleteSpammer spam not selected", function(assert) {
|
||||
sandbox.stub(Discourse.User, 'currentProp').withArgs('staff').returns(true);
|
||||
var flagController = this.subject({ model: buildPost() });
|
||||
flagController.set('userDetails', buildAdminUser({can_delete_all_posts: true, can_be_deleted: true}));
|
||||
canDeleteSpammer(assert, flagController, 'off_topic', false, 'false if current user is staff, but selected is off_topic');
|
||||
canDeleteSpammer(assert, flagController, 'inappropriate', false, 'false if current user is staff, but selected is inappropriate');
|
||||
canDeleteSpammer(assert, flagController, 'notify_user', false, 'false if current user is staff, but selected is notify_user');
|
||||
canDeleteSpammer(assert, flagController, 'notify_moderators', false, 'false if current user is staff, but selected is notify_moderators');
|
||||
});
|
||||
|
||||
QUnit.test("canDeleteSpammer spam selected", function(assert) {
|
||||
sandbox.stub(Discourse.User, 'currentProp').withArgs('staff').returns(true);
|
||||
var flagController = this.subject({ model: buildPost() });
|
||||
flagController.set('userDetails', buildAdminUser({can_delete_all_posts: true, can_be_deleted: true}));
|
||||
canDeleteSpammer(assert, flagController, 'spam', true, 'true if current user is staff, selected is spam, posts and user can be deleted');
|
||||
|
||||
flagController.set('userDetails', buildAdminUser({can_delete_all_posts: false, can_be_deleted: true}));
|
||||
canDeleteSpammer(assert, flagController, 'spam', false, 'false if current user is staff, selected is spam, posts cannot be deleted');
|
||||
|
||||
flagController.set('userDetails', buildAdminUser({can_delete_all_posts: true, can_be_deleted: false}));
|
||||
canDeleteSpammer(assert, flagController, 'spam', false, 'false if current user is staff, selected is spam, user cannot be deleted');
|
||||
|
||||
flagController.set('userDetails', buildAdminUser({can_delete_all_posts: false, can_be_deleted: false}));
|
||||
canDeleteSpammer(assert, flagController, 'spam', false, 'false if current user is staff, selected is spam, user cannot be deleted');
|
||||
});
|
||||
|
||||
QUnit.test("canSendWarning not staff", function(assert) {
|
||||
const store = createStore();
|
||||
|
||||
var flagController = this.subject({ model: buildPost() });
|
||||
sandbox.stub(Discourse.User, 'currentProp').withArgs('staff').returns(false);
|
||||
|
||||
const notifyUserFlag = store.createRecord('post-action-type', {name_key: 'notify_user'});
|
||||
flagController.set('selected', notifyUserFlag);
|
||||
assert.equal(flagController.get('canSendWarning'), false, 'false if current user is not staff');
|
||||
});
|
||||
|
||||
var canSendWarning = function(assert, flagController, postActionType, expected, testName) {
|
||||
const store = createStore();
|
||||
const flag = store.createRecord('post-action-type', {name_key: postActionType});
|
||||
flagController.set('selected', flag);
|
||||
|
||||
assert.equal(flagController.get('canSendWarning'), expected, testName);
|
||||
};
|
||||
|
||||
QUnit.test("canSendWarning notify_user not selected", function(assert) {
|
||||
sandbox.stub(Discourse.User, 'currentProp').withArgs('staff').returns(true);
|
||||
var flagController = this.subject({ model: buildPost() });
|
||||
canSendWarning(assert, flagController, 'off_topic', false, 'false if current user is staff, but selected is off_topic');
|
||||
canSendWarning(assert, flagController, 'inappropriate', false, 'false if current user is staff, but selected is inappropriate');
|
||||
canSendWarning(assert, flagController, 'spam', false, 'false if current user is staff, but selected is spam');
|
||||
canSendWarning(assert, flagController, 'notify_moderators', false, 'false if current user is staff, but selected is notify_moderators');
|
||||
});
|
||||
|
||||
QUnit.test("canSendWarning notify_user selected", function(assert) {
|
||||
sandbox.stub(Discourse.User, 'currentProp').withArgs('staff').returns(true);
|
||||
var flagController = this.subject({ model: buildPost() });
|
||||
canSendWarning(assert, flagController, 'notify_user', true, 'true if current user is staff, selected is notify_user');
|
||||
});
|
Loading…
Reference in New Issue
Block a user