From a26a60cc678411b7a2483cc1c20164da2eac2957 Mon Sep 17 00:00:00 2001 From: Benjamin Goering Date: Mon, 27 Feb 2017 19:16:27 +0800 Subject: [PATCH 01/35] minial best button uionly and e2e --- client/coral-embed-stream/src/Comment.js | 5 ++ client/coral-embed-stream/style/default.css | 10 ++++ client/coral-plugin-best/BestButton.js | 65 +++++++++++++++++++++ client/coral-plugin-best/translations.json | 10 ++++ client/coral-plugin-replies/ReplyButton.js | 3 +- test/e2e/pages/embedStreamPage.js | 5 +- test/e2e/tests/Moderator/BestCommentTest.js | 42 +++++++++++++ 7 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 client/coral-plugin-best/BestButton.js create mode 100644 client/coral-plugin-best/translations.json create mode 100644 test/e2e/tests/Moderator/BestCommentTest.js diff --git a/client/coral-embed-stream/src/Comment.js b/client/coral-embed-stream/src/Comment.js index e52c4aff4..0a11ee6a5 100644 --- a/client/coral-embed-stream/src/Comment.js +++ b/client/coral-embed-stream/src/Comment.js @@ -17,6 +17,7 @@ import PubDate from 'coral-plugin-pubdate/PubDate'; import {ReplyBox, ReplyButton} from 'coral-plugin-replies'; import FlagComment from 'coral-plugin-flags/FlagComment'; import LikeButton from 'coral-plugin-likes/LikeButton'; +import BestButton from 'coral-plugin-best/BestButton'; import LoadMore from 'coral-embed-stream/src/LoadMore'; import styles from './Comment.css'; @@ -125,6 +126,10 @@ class Comment extends React.Component { parentCommentId={parentId || comment.id} currentUserId={currentUser && currentUser.id} banned={false} /> + {}} + removeBestTag={() => {}} />
diff --git a/client/coral-embed-stream/style/default.css b/client/coral-embed-stream/style/default.css index 52876ff79..46edc44e1 100644 --- a/client/coral-embed-stream/style/default.css +++ b/client/coral-embed-stream/style/default.css @@ -213,10 +213,20 @@ hr { width: 50%; } +.comment__action-button { + cursor: pointer; +} + +.comment__action-button--nowrap { + white-space: nowrap; +} + .material-icons { font-size: 12px !important; margin-left: 3px; vertical-align: middle; + width: 1em; + overflow: hidden; } .likedButton { diff --git a/client/coral-plugin-best/BestButton.js b/client/coral-plugin-best/BestButton.js new file mode 100644 index 000000000..c88b750ce --- /dev/null +++ b/client/coral-plugin-best/BestButton.js @@ -0,0 +1,65 @@ +import React, {Component, PropTypes} from 'react'; +import {I18n} from '../coral-framework'; +import translations from './translations.json'; +import classnames from 'classnames'; + +const name = 'coral-plugin-best'; +const lang = new I18n(translations); + +/** + * Button that lets a moderator tag a comment as "Best". + * Used to recognize really good comments. + */ +class BestButton extends Component { + static propTypes = { + + // whether the comment is already tagged as best + isBest: PropTypes.bool.isRequired, + + // set that this comment is best + setBestTag: PropTypes.func.isRequired, + + // remove the best status + removeBestTag: PropTypes.func.isRequired, + } + + state = { + best: false + } + + constructor(props) { + super(props); + this.onClickSetBest = this.onClickSetBest.bind(this); + this.onClickUnsetBest = this.onClickUnsetBest.bind(this); + } + + onClickSetBest(e) { + e.preventDefault(); + this.setState({isBest: true}); + } + + onClickUnsetBest(e) { + e.preventDefault(); + this.setState({isBest: false}); + } + + render() { + + // @TODO(bengo) Consider adding the comment__action classes to other buttons to add cursor:pointer and never wrap the icons + // @TODO(bengo) Should I reuse another element like coral-ui button? Just doing what LikeButton does for now + // Oh. I think that's styled for the admin. Don't use coral-ui button until the whole comment bottom bar does. + const {isBest} = this.state; + return
+ +
; + } +} + +export default BestButton; diff --git a/client/coral-plugin-best/translations.json b/client/coral-plugin-best/translations.json new file mode 100644 index 000000000..cfdfc04a1 --- /dev/null +++ b/client/coral-plugin-best/translations.json @@ -0,0 +1,10 @@ +{ + "en": { + "setBest": "Tag as Best", + "unsetBest": "Untag as Best" + }, + "es": { + "like": "Establecer como mejor", + "liked": "Desarmado como mejor" + } +} diff --git a/client/coral-plugin-replies/ReplyButton.js b/client/coral-plugin-replies/ReplyButton.js index 68374fd2f..cd1859afc 100644 --- a/client/coral-plugin-replies/ReplyButton.js +++ b/client/coral-plugin-replies/ReplyButton.js @@ -1,13 +1,14 @@ import React, {PropTypes} from 'react'; import {I18n} from '../coral-framework'; import translations from './translations.json'; +import classnames from 'classnames'; const name = 'coral-plugin-replies'; const ReplyButton = ({banned, onClick}) => { return (
diff --git a/client/coral-plugin-best/BestButton.js b/client/coral-plugin-best/BestButton.js index c88b750ce..3010db7c6 100644 --- a/client/coral-plugin-best/BestButton.js +++ b/client/coral-plugin-best/BestButton.js @@ -6,6 +6,17 @@ import classnames from 'classnames'; const name = 'coral-plugin-best'; const lang = new I18n(translations); +// It would be best if the backend/api held this business logic +const canModifyBestTag = ({roles = []} = {}) => roles && ['ADMIN', 'MODERATOR'].some(role => roles.includes(role)); + +/** + * Component that only renders children if the provided user prop can modify best tags + */ +export const IfUserCanModifyBest = ({user, children}) => { + if ( ! ( user && canModifyBestTag(user))) {return null;} + return children; +}; + /** * Button that lets a moderator tag a comment as "Best". * Used to recognize really good comments. diff --git a/test/e2e/pages/embedStreamPage.js b/test/e2e/pages/embedStreamPage.js index a861b4c2e..5799fa8c9 100644 --- a/test/e2e/pages/embedStreamPage.js +++ b/test/e2e/pages/embedStreamPage.js @@ -163,6 +163,12 @@ module.exports = { }, registerButton: { selector: '#signInDialog #coralRegister' + }, + setBestButton: { + selector: '.e2e__set-best-comment' + }, + unsetBestButton: { + selector: '.e2e__unset-best-comment' } } }; diff --git a/test/e2e/tests/Commenter/BestCommentTest.js b/test/e2e/tests/Commenter/BestCommentTest.js new file mode 100644 index 000000000..f5747bd6e --- /dev/null +++ b/test/e2e/tests/Commenter/BestCommentTest.js @@ -0,0 +1,25 @@ +module.exports = { + '@tags': ['like', 'comments', 'commenter'], + before: client => { + const embedStreamPage = client.page.embedStreamPage(); + const {users} = client.globals; + + embedStreamPage + .navigate() + .ready(); + + embedStreamPage + .login(users.commenter); + }, + 'Commenters should not see the set-best-comment button': client => { + const embedStreamPage = client.page.embedStreamPage(); + + embedStreamPage + .likeComment() + .waitForElementVisible('@likesCount', 2000) + .expect.element('@setBestButton').to.not.be.present; + }, + after: client => { + client.end(); + } +}; diff --git a/test/e2e/tests/Moderator/BestCommentTest.js b/test/e2e/tests/Moderator/BestCommentTest.js index ca8cacda4..f25fd7d93 100644 --- a/test/e2e/tests/Moderator/BestCommentTest.js +++ b/test/e2e/tests/Moderator/BestCommentTest.js @@ -9,22 +9,19 @@ module.exports = { .ready(); embedStreamPage - .login(users.commenter); + .login(users.moderator); }, 'Moderator marks their comment as BEST': client => { const embedStreamPage = client.page.embedStreamPage(); - const setBestCommentButton = '.comment:nth-of-type(1) .e2e__set-best-comment'; - const unsetBestCommentButton = '.comment:nth-of-type(1) .e2e__unset-best-comment'; + const setBestCommentButton = '@setBestButton'; + const unsetBestCommentButton = '@unsetBestButton'; embedStreamPage .postComment('Hi everyone. Isn\'t this the BEST comment!?') - .waitForElementVisible(setBestCommentButton, 2000, () => { - client.expect.element(setBestCommentButton).text.to.contain('Tag as Best'); - }) + .waitForElementVisible(setBestCommentButton, 2000) .click(setBestCommentButton) .waitForElementVisible(unsetBestCommentButton, 2000, () => { client.assert.elementNotPresent(setBestCommentButton); - client.expect.element(unsetBestCommentButton).text.to.contain('Untag as Best'); }); // on refresh, it should still be tagged as best :) From fc9c67b05cf3cdd7802f2986dfa9270f22f2d854 Mon Sep 17 00:00:00 2001 From: Benjamin Goering Date: Mon, 27 Feb 2017 21:24:56 +0800 Subject: [PATCH 03/35] BestButton sends mutations to backend, but they aren't implemented yet --- client/coral-embed-stream/src/Comment.js | 32 +++++++++++--- client/coral-embed-stream/src/Embed.js | 14 ++++++- client/coral-embed-stream/src/Stream.js | 14 ++++++- client/coral-embed-stream/style/default.css | 4 ++ .../graphql/mutations/addCommentTag.graphql | 12 ++++++ .../graphql/mutations/index.js | 24 +++++++++++ .../mutations/removeCommentTag.graphql | 12 ++++++ client/coral-plugin-best/BestButton.js | 42 +++++++++++++------ 8 files changed, 131 insertions(+), 23 deletions(-) create mode 100644 client/coral-framework/graphql/mutations/addCommentTag.graphql create mode 100644 client/coral-framework/graphql/mutations/removeCommentTag.graphql diff --git a/client/coral-embed-stream/src/Comment.js b/client/coral-embed-stream/src/Comment.js index 0c7591ddf..e4ca10743 100644 --- a/client/coral-embed-stream/src/Comment.js +++ b/client/coral-embed-stream/src/Comment.js @@ -17,7 +17,7 @@ import PubDate from 'coral-plugin-pubdate/PubDate'; import {ReplyBox, ReplyButton} from 'coral-plugin-replies'; import FlagComment from 'coral-plugin-flags/FlagComment'; import LikeButton from 'coral-plugin-likes/LikeButton'; -import BestButton, {IfUserCanModifyBest} from 'coral-plugin-best/BestButton'; +import {BestButton, IfUserCanModifyBest, BEST_TAG, commentIsBest} from 'coral-plugin-best/BestButton'; import LoadMore from 'coral-embed-stream/src/LoadMore'; import styles from './Comment.css'; @@ -74,7 +74,13 @@ class Comment extends React.Component { id: PropTypes.string.isRequired, name: PropTypes.string.isRequired }).isRequired - }).isRequired + }).isRequired, + + // dispatch action to add a tag to a comment + addCommentTag: React.PropTypes.func, + + // dispatch action to remove a tag from a comment + removeCommentTag: React.PropTypes.func, } render () { @@ -93,13 +99,27 @@ class Comment extends React.Component { loadMore, setActiveReplyBox, activeReplyBox, - deleteAction + deleteAction, + addCommentTag, + removeCommentTag, } = this.props; const like = getActionSummary('LikeActionSummary', comment); const flag = getActionSummary('FlagActionSummary', comment); const dontagree = getActionSummary('DontAgreeActionSummary', comment); + // @TODO(bengo) Should I do this for addCommentTag and/or best button + + // @TODO(bengo) Would be best to only create these funcs on prop change + const addBestTag = () => addCommentTag({ + comment_id: comment.id, + tag: BEST_TAG, + }); + const removeBestTag = () => removeCommentTag({ + comment_id: comment.id, + tag: BEST_TAG, + }); + return (
{}} - removeBestTag={() => {}} /> + isBest={commentIsBest(comment)} + addBest={addBestTag} + removeBest={removeBestTag} />
diff --git a/client/coral-embed-stream/src/Embed.js b/client/coral-embed-stream/src/Embed.js index ac9c09eac..3290e2c8e 100644 --- a/client/coral-embed-stream/src/Embed.js +++ b/client/coral-embed-stream/src/Embed.js @@ -13,7 +13,7 @@ const {addNotification, clearNotification} = notificationActions; const {fetchAssetSuccess} = assetActions; import {queryStream} from 'coral-framework/graphql/queries'; -import {postComment, postFlag, postLike, postDontAgree, deleteAction} from 'coral-framework/graphql/mutations'; +import {postComment, postFlag, postLike, postDontAgree, deleteAction, addCommentTag, removeCommentTag} from 'coral-framework/graphql/mutations'; import {editName} from 'coral-framework/actions/user'; import {updateCountCache} from 'coral-framework/actions/asset'; import {Notification, notificationActions, authActions, assetActions, pym} from 'coral-framework'; @@ -54,7 +54,13 @@ class Embed extends Component { data: React.PropTypes.shape({ loading: React.PropTypes.bool, error: React.PropTypes.object - }).isRequired + }).isRequired, + + // dispatch action to add a tag to a comment + addCommentTag: React.PropTypes.func, + + // dispatch action to remove a tag from a comment + removeCommentTag: React.PropTypes.func, } componentDidMount () { @@ -176,6 +182,8 @@ class Embed extends Component { postLike={this.props.postLike} postFlag={this.props.postFlag} postDontAgree={this.props.postDontAgree} + addCommentTag={this.props.addCommentTag} + removeCommentTag={this.props.removeCommentTag} getCounts={this.props.getCounts} updateCountCache={this.props.updateCountCache} loadMore={this.props.loadMore} @@ -250,6 +258,8 @@ export default compose( postFlag, postLike, postDontAgree, + addCommentTag, + removeCommentTag, deleteAction, queryStream )(Embed); diff --git a/client/coral-embed-stream/src/Stream.js b/client/coral-embed-stream/src/Stream.js index a7f360d59..a1756c50e 100644 --- a/client/coral-embed-stream/src/Stream.js +++ b/client/coral-embed-stream/src/Stream.js @@ -13,7 +13,13 @@ class Stream extends React.Component { currentUser: PropTypes.shape({ username: PropTypes.string, id: PropTypes.string - }) + }), + + // dispatch action to add a tag to a comment + addCommentTag: React.PropTypes.func, + + // dispatch action to remove a tag from a comment + removeCommentTag: React.PropTypes.func, } constructor(props) { @@ -64,7 +70,9 @@ class Stream extends React.Component { loadMore, deleteAction, showSignInDialog, - refetch + refetch, + addCommentTag, + removeCommentTag, } = this.props; return ( @@ -83,6 +91,8 @@ class Stream extends React.Component { postLike={postLike} postFlag={postFlag} postDontAgree={postDontAgree} + addCommentTag={addCommentTag} + removeCommentTag={removeCommentTag} loadMore={loadMore} deleteAction={deleteAction} showSignInDialog={showSignInDialog} diff --git a/client/coral-embed-stream/style/default.css b/client/coral-embed-stream/style/default.css index 46edc44e1..143b0f78c 100644 --- a/client/coral-embed-stream/style/default.css +++ b/client/coral-embed-stream/style/default.css @@ -217,6 +217,10 @@ hr { cursor: pointer; } +.comment__action-button[disabled] { + cursor: inherit; +} + .comment__action-button--nowrap { white-space: nowrap; } diff --git a/client/coral-framework/graphql/mutations/addCommentTag.graphql b/client/coral-framework/graphql/mutations/addCommentTag.graphql new file mode 100644 index 000000000..27d369775 --- /dev/null +++ b/client/coral-framework/graphql/mutations/addCommentTag.graphql @@ -0,0 +1,12 @@ +mutation AddCommentTag ($comment_id: ID!, $tag: String!) { + addCommentTag(comment_id:$comment_id, tag:$tag) { + comment { + tags { + name + } + } + errors { + translation_key + } + } +} diff --git a/client/coral-framework/graphql/mutations/index.js b/client/coral-framework/graphql/mutations/index.js index 67af2df4a..b65a6a339 100644 --- a/client/coral-framework/graphql/mutations/index.js +++ b/client/coral-framework/graphql/mutations/index.js @@ -4,6 +4,8 @@ import POST_FLAG from './postFlag.graphql'; import POST_LIKE from './postLike.graphql'; import POST_DONT_AGREE from './postDontAgree.graphql'; import DELETE_ACTION from './deleteAction.graphql'; +import ADD_COMMENT_TAG from './addCommentTag.graphql'; +import REMOVE_COMMENT_TAG from './removeCommentTag.graphql'; import commentView from '../fragments/commentView.graphql'; @@ -122,3 +124,25 @@ export const deleteAction = graphql(DELETE_ACTION, { }); }}), }); + +export const addCommentTag = graphql(ADD_COMMENT_TAG, { + props: ({mutate}) => ({ + addCommentTag: (tag) => { + return mutate({ + variables: { + tag + } + }); + }}), +}); + +export const removeCommentTag = graphql(REMOVE_COMMENT_TAG, { + props: ({mutate}) => ({ + removeCommentTag: (tag) => { + return mutate({ + variables: { + tag + } + }); + }}), +}); diff --git a/client/coral-framework/graphql/mutations/removeCommentTag.graphql b/client/coral-framework/graphql/mutations/removeCommentTag.graphql new file mode 100644 index 000000000..2871a2fae --- /dev/null +++ b/client/coral-framework/graphql/mutations/removeCommentTag.graphql @@ -0,0 +1,12 @@ +mutation RemoveCommentTag ($comment_id: ID!, $tag: String!) { + removeCommentTag(comment_id:$comment_id, tag:$tag) { + comment { + tags { + name + } + } + errors { + translation_key + } + } +} diff --git a/client/coral-plugin-best/BestButton.js b/client/coral-plugin-best/BestButton.js index 3010db7c6..9af8b1638 100644 --- a/client/coral-plugin-best/BestButton.js +++ b/client/coral-plugin-best/BestButton.js @@ -3,6 +3,10 @@ import {I18n} from '../coral-framework'; import translations from './translations.json'; import classnames from 'classnames'; +// tag string for best comments +export const BEST_TAG = 'BEST'; +export const commentIsBest = ({tags} = {}) => Array.isArray(tags) && tags.some(t => t === BEST_TAG); + const name = 'coral-plugin-best'; const lang = new I18n(translations); @@ -21,17 +25,17 @@ export const IfUserCanModifyBest = ({user, children}) => { * Button that lets a moderator tag a comment as "Best". * Used to recognize really good comments. */ -class BestButton extends Component { +export class BestButton extends Component { static propTypes = { // whether the comment is already tagged as best isBest: PropTypes.bool.isRequired, // set that this comment is best - setBestTag: PropTypes.func.isRequired, + addBest: PropTypes.func.isRequired, // remove the best status - removeBestTag: PropTypes.func.isRequired, + removeBest: PropTypes.func.isRequired, } state = { @@ -40,18 +44,30 @@ class BestButton extends Component { constructor(props) { super(props); - this.onClickSetBest = this.onClickSetBest.bind(this); - this.onClickUnsetBest = this.onClickUnsetBest.bind(this); + this.onClickAddBest = this.onClickAddBest.bind(this); + this.onClickRemoveBest = this.onClickRemoveBest.bind(this); } - onClickSetBest(e) { + async onClickAddBest(e) { e.preventDefault(); - this.setState({isBest: true}); + const {addBest} = this.props; + if ( ! addBest) { + console.warn('BestButton#onClickAddBest called even though there is no addBest prop. doing nothing'); + return; + } + const addBestRet = await addBest(); + console.log('addBestRet', addBestRet); } - onClickUnsetBest(e) { + async onClickRemoveBest(e) { e.preventDefault(); - this.setState({isBest: false}); + const {removeBest} = this.props; + if ( ! removeBest) { + console.warn('BestButton#onClickAddBest called even though there is no removeBest prop. doing nothing'); + return; + } + const removeBestRet = await removeBest(); + console.log('removeBestRet', removeBestRet); } render() { @@ -59,10 +75,12 @@ class BestButton extends Component { // @TODO(bengo) Consider adding the comment__action classes to other buttons to add cursor:pointer and never wrap the icons // @TODO(bengo) Should I reuse another element like coral-ui button? Just doing what LikeButton does for now // Oh. I think that's styled for the admin. Don't use coral-ui button until the whole comment bottom bar does. - const {isBest} = this.state; + let {isBest} = this.state; + const {addBest, removeBest} = this.props; return
-
; } } - -export default BestButton; From 6e338290a1ebeb286221f040fef147b600a013ff Mon Sep 17 00:00:00 2001 From: Benjamin Goering Date: Tue, 28 Feb 2017 02:31:02 +0800 Subject: [PATCH 04/35] addCommentTag mutation works --- .eslintrc.json | 1 + client/coral-embed-stream/src/Comment.js | 4 +- .../graphql/mutations/addCommentTag.graphql | 15 +- .../graphql/mutations/index.js | 3 +- client/coral-plugin-best/BestButton.js | 12 +- graph/mutators/comment.js | 15 +- graph/resolvers/root_mutation.js | 3 + graph/typeDefs.graphql | 10 + models/user.js | 3 +- package.json | 1 + services/comments.js | 53 +- test/e2e/tests/Moderator/BestCommentTest.js | 11 +- test/graph/mutations/addCommentTag.js | 42 ++ test/mocha.opts | 1 + test/services/comments.js | 47 +- yarn.lock | 681 +++++++++--------- 16 files changed, 523 insertions(+), 379 deletions(-) create mode 100644 test/graph/mutations/addCommentTag.js diff --git a/.eslintrc.json b/.eslintrc.json index 34b46b83b..eca2dda1f 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -3,6 +3,7 @@ "es6": true, "node": true }, + "parser": "babel-eslint", "extends": "eslint:recommended", "rules": { "indent": ["error", diff --git a/client/coral-embed-stream/src/Comment.js b/client/coral-embed-stream/src/Comment.js index e4ca10743..47fd86cc8 100644 --- a/client/coral-embed-stream/src/Comment.js +++ b/client/coral-embed-stream/src/Comment.js @@ -112,11 +112,11 @@ class Comment extends React.Component { // @TODO(bengo) Would be best to only create these funcs on prop change const addBestTag = () => addCommentTag({ - comment_id: comment.id, + id: comment.id, tag: BEST_TAG, }); const removeBestTag = () => removeCommentTag({ - comment_id: comment.id, + id: comment.id, tag: BEST_TAG, }); diff --git a/client/coral-framework/graphql/mutations/addCommentTag.graphql b/client/coral-framework/graphql/mutations/addCommentTag.graphql index 27d369775..5fd63868e 100644 --- a/client/coral-framework/graphql/mutations/addCommentTag.graphql +++ b/client/coral-framework/graphql/mutations/addCommentTag.graphql @@ -1,10 +1,11 @@ -mutation AddCommentTag ($comment_id: ID!, $tag: String!) { - addCommentTag(comment_id:$comment_id, tag:$tag) { - comment { - tags { - name - } - } +mutation AddCommentTag ($id: ID!, $tag: String!) { + addCommentTag(id:$id, tag:$tag) { + comment { + id + tags { + name + } + } errors { translation_key } diff --git a/client/coral-framework/graphql/mutations/index.js b/client/coral-framework/graphql/mutations/index.js index b65a6a339..6f6cdc04b 100644 --- a/client/coral-framework/graphql/mutations/index.js +++ b/client/coral-framework/graphql/mutations/index.js @@ -127,9 +127,10 @@ export const deleteAction = graphql(DELETE_ACTION, { export const addCommentTag = graphql(ADD_COMMENT_TAG, { props: ({mutate}) => ({ - addCommentTag: (tag) => { + addCommentTag: ({id, tag}) => { return mutate({ variables: { + id, tag } }); diff --git a/client/coral-plugin-best/BestButton.js b/client/coral-plugin-best/BestButton.js index 9af8b1638..52367c4e0 100644 --- a/client/coral-plugin-best/BestButton.js +++ b/client/coral-plugin-best/BestButton.js @@ -5,7 +5,10 @@ import classnames from 'classnames'; // tag string for best comments export const BEST_TAG = 'BEST'; -export const commentIsBest = ({tags} = {}) => Array.isArray(tags) && tags.some(t => t === BEST_TAG); +export const commentIsBest = ({tags} = {}) => { + const isBest = Array.isArray(tags) && tags.some(t => t.name === BEST_TAG); + return isBest; +}; const name = 'coral-plugin-best'; const lang = new I18n(translations); @@ -38,10 +41,6 @@ export class BestButton extends Component { removeBest: PropTypes.func.isRequired, } - state = { - best: false - } - constructor(props) { super(props); this.onClickAddBest = this.onClickAddBest.bind(this); @@ -75,8 +74,7 @@ export class BestButton extends Component { // @TODO(bengo) Consider adding the comment__action classes to other buttons to add cursor:pointer and never wrap the icons // @TODO(bengo) Should I reuse another element like coral-ui button? Just doing what LikeButton does for now // Oh. I think that's styled for the admin. Don't use coral-ui button until the whole comment bottom bar does. - let {isBest} = this.state; - const {addBest, removeBest} = this.props; + const {isBest, addBest, removeBest} = this.props; return
-
; + ) } } From c2d199a21cd4d8fd4bb7dc86a56bca68f1e2ff25 Mon Sep 17 00:00:00 2001 From: Benjamin Goering Date: Tue, 28 Feb 2017 16:28:23 +0800 Subject: [PATCH 13/35] Remove BestButton copy for now --- client/coral-plugin-best/BestButton.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/coral-plugin-best/BestButton.js b/client/coral-plugin-best/BestButton.js index 0ba36d553..b7a6a4766 100644 --- a/client/coral-plugin-best/BestButton.js +++ b/client/coral-plugin-best/BestButton.js @@ -95,11 +95,11 @@ export class BestButton extends Component { className={classnames('comment__action-button', `${name}-button`, 'comment__action-button--nowrap', /* Can I do this to all buttons? 'comment__action-button--cursor-pointer', */ `e2e__${isBest ? 'unset' : 'set'}-best-comment`)}> - {lang.t(isBest ? 'unsetBest' : 'setBest')} + /* {lang.t(isBest ? 'unsetBest' : 'setBest')} */ { isBest ? 'favorite' : 'favorite_border' } - ) + ); } } From af23717988f5711032b50cc67c8819c85228c527 Mon Sep 17 00:00:00 2001 From: Benjamin Goering Date: Tue, 28 Feb 2017 17:19:11 +0800 Subject: [PATCH 14/35] Indicate best comments. Add some aria labels --- client/coral-embed-stream/src/Comment.js | 12 ++++++++---- client/coral-plugin-best/BestButton.js | 11 +++++++++-- client/coral-plugin-best/translations.json | 6 ++++-- client/coral-plugin-tag-label/TagLabel.js | 4 ++-- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/client/coral-embed-stream/src/Comment.js b/client/coral-embed-stream/src/Comment.js index 19af229dc..679699c94 100644 --- a/client/coral-embed-stream/src/Comment.js +++ b/client/coral-embed-stream/src/Comment.js @@ -17,7 +17,7 @@ import PubDate from 'coral-plugin-pubdate/PubDate'; import {ReplyBox, ReplyButton} from 'coral-plugin-replies'; import FlagComment from 'coral-plugin-flags/FlagComment'; import LikeButton from 'coral-plugin-likes/LikeButton'; -import {BestButton, IfUserCanModifyBest, BEST_TAG, commentIsBest} from 'coral-plugin-best/BestButton'; +import {BestButton, IfUserCanModifyBest, BEST_TAG, commentIsBest, BestIndicator} from 'coral-plugin-best/BestButton'; import LoadMore from 'coral-embed-stream/src/LoadMore'; import styles from './Comment.css'; @@ -127,11 +127,15 @@ class Comment extends React.Component { { isStaff(comment.tags) - ? + ? Staff + : null } + + { commentIsBest(comment) + ? : null } -
+
-
+
roles && ['ADMIN', 'MODERATOR'].some(role => roles.includes(role)); +// Put this on a comment to show that it is best +export const BestIndicator = ({children = favorite}) => ( + + { children } + +); + /** * Component that only renders children if the provided user prop can modify best tags */ @@ -94,8 +101,8 @@ export class BestButton extends Component { disabled={disabled} className={classnames('comment__action-button', `${name}-button`, 'comment__action-button--nowrap', /* Can I do this to all buttons? 'comment__action-button--cursor-pointer', */ - `e2e__${isBest ? 'unset' : 'set'}-best-comment`)}> - /* {lang.t(isBest ? 'unsetBest' : 'setBest')} */ + `e2e__${isBest ? 'unset' : 'set'}-best-comment`)} + aria-label={lang.t(isBest ? 'unsetBest' : 'setBest')}> { isBest ? 'favorite' : 'favorite_border' } diff --git a/client/coral-plugin-best/translations.json b/client/coral-plugin-best/translations.json index cfdfc04a1..c7dd42be8 100644 --- a/client/coral-plugin-best/translations.json +++ b/client/coral-plugin-best/translations.json @@ -1,10 +1,12 @@ { "en": { "setBest": "Tag as Best", - "unsetBest": "Untag as Best" + "unsetBest": "Untag as Best", + "commentIsBest": "This comment is one of the best" }, "es": { "like": "Establecer como mejor", - "liked": "Desarmado como mejor" + "liked": "Desarmado como mejor", + "commentIsBest": "Este comentario es uno de los mejores" } } diff --git a/client/coral-plugin-tag-label/TagLabel.js b/client/coral-plugin-tag-label/TagLabel.js index ba7403f5c..4a5d3632f 100644 --- a/client/coral-plugin-tag-label/TagLabel.js +++ b/client/coral-plugin-tag-label/TagLabel.js @@ -1,7 +1,7 @@ import React from 'react'; -const TagLabel = ({isStaff}) =>
- {isStaff ? 'Staff' : ''} +const TagLabel = ({children}) =>
+ {children}
; export default TagLabel; From 96d1447602841bd79170ba07abb1b4b777e433ae Mon Sep 17 00:00:00 2001 From: Benjamin Goering Date: Tue, 28 Feb 2017 17:27:19 +0800 Subject: [PATCH 15/35] remove debugging code from embedStreamPage --- test/e2e/pages/embedStreamPage.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/e2e/pages/embedStreamPage.js b/test/e2e/pages/embedStreamPage.js index 5799fa8c9..60a54f0f7 100644 --- a/test/e2e/pages/embedStreamPage.js +++ b/test/e2e/pages/embedStreamPage.js @@ -38,10 +38,7 @@ const embedStreamCommands = { .setValue('@signInDialogEmail', user.email) .setValue('@signInDialogPassword', user.pass) .waitForElementVisible('@logInButton') - .click('@logInButton', () => { - - // this.api.pause() - }) + .click('@logInButton') .waitForElementVisible('@logoutButton', 5000); }, logout() { From fe8cf2c61220bd7b6da647b8888e2cdfe1923554 Mon Sep 17 00:00:00 2001 From: Benjamin Goering Date: Tue, 28 Feb 2017 18:57:27 +0800 Subject: [PATCH 16/35] Style multiple tag labels and material-icon therein (edited dist/../default.css before :/) --- client/coral-embed-stream/style/default.css | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/client/coral-embed-stream/style/default.css b/client/coral-embed-stream/style/default.css index 143b0f78c..6a8c58059 100644 --- a/client/coral-embed-stream/style/default.css +++ b/client/coral-embed-stream/style/default.css @@ -172,7 +172,7 @@ hr { .coral-plugin-author-name-text { display: inline-block; - margin: 10px 8px 10px 0; + margin: 10px 5px 10px 0; font-weight: bold; } @@ -186,7 +186,7 @@ hr { background-color: #4C1066; color: white; display: inline-block; - margin: 10px 10px; + margin: 0px 5px; padding: 5px 5px; border-radius: 2px; } @@ -213,6 +213,11 @@ hr { width: 50%; } +.comment__action-container .material-icons { + font-size: 12px !important; + margin-left: 3px; +} + .comment__action-button { cursor: pointer; } @@ -225,11 +230,10 @@ hr { white-space: nowrap; } -.material-icons { - font-size: 12px !important; - margin-left: 3px; +.commentStream .material-icons { vertical-align: middle; width: 1em; + font-size: 1em; overflow: hidden; } @@ -245,6 +249,7 @@ hr { color: #696969; display: inline-block; font-size: .75rem; + margin-left: 5px; } .coral-plugin-permalinks-container { From f8479a86612cce1ff2d1a479871454b1e7abc4dc Mon Sep 17 00:00:00 2001 From: Benjamin Goering Date: Tue, 28 Feb 2017 19:30:25 +0800 Subject: [PATCH 17/35] fix e2e tests with .comment selector that avoids hidden dialog --- client/coral-embed-stream/src/Embed.js | 36 +++++++++++---------- client/coral-plugin-likes/LikeButton.js | 2 +- test/e2e/pages/embedStreamPage.js | 28 ++++++++-------- test/e2e/tests/Commenter/LikeCommentTest.js | 1 + test/e2e/tests/EmbedStreamTests.js | 17 ++++------ 5 files changed, 42 insertions(+), 42 deletions(-) diff --git a/client/coral-embed-stream/src/Embed.js b/client/coral-embed-stream/src/Embed.js index 3290e2c8e..4f092bb7c 100644 --- a/client/coral-embed-stream/src/Embed.js +++ b/client/coral-embed-stream/src/Embed.js @@ -173,23 +173,25 @@ class Embed extends Component { assetId={asset.id} updateCountCache={this.props.updateCountCache} /> - +
+ +
-