diff --git a/src/components/icons/Help.js b/src/components/icons/Help.js deleted file mode 100644 index 72fcd68b..00000000 --- a/src/components/icons/Help.js +++ /dev/null @@ -1,16 +0,0 @@ -import * as React from 'react'; - -import { _extends } from './common'; - -export default function Help(props) { - return /* #__PURE__ */React.createElement('svg', _extends({ - width: 24, - height: 24, - viewBox: '0 0 24 24', - fill: 'none', - xmlns: 'http://www.w3.org/2000/svg', - }, props), /* #__PURE__ */React.createElement('path', { - d: 'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z', - fill: '#2D494E', - })); -} diff --git a/src/components/icons/People.jsx b/src/components/icons/People.jsx new file mode 100644 index 00000000..02808f14 --- /dev/null +++ b/src/components/icons/People.jsx @@ -0,0 +1,18 @@ +import React from 'react'; + +export default function People() { + return ( + + + + ); +} diff --git a/src/components/icons/Question.jsx b/src/components/icons/Question.jsx new file mode 100644 index 00000000..ff050534 --- /dev/null +++ b/src/components/icons/Question.jsx @@ -0,0 +1,35 @@ +import React from 'react'; + +export default function Question() { + return ( + + + + + + + + + + + + ); +} diff --git a/src/components/icons/QuestionAnswer.js b/src/components/icons/QuestionAnswer.js deleted file mode 100644 index 62ea0911..00000000 --- a/src/components/icons/QuestionAnswer.js +++ /dev/null @@ -1,16 +0,0 @@ -import * as React from 'react'; - -import { _extends } from './common'; - -export default function QuestionAnswer(props) { - return /* #__PURE__ */React.createElement('svg', _extends({ - width: 20, - height: 20, - viewBox: '0 0 20 20', - fill: 'none', - xmlns: 'http://www.w3.org/2000/svg', - }, props), /* #__PURE__ */React.createElement('path', { - d: 'M16.7371 4.00002H14.2371V11.5H3.4038V14H13.4038L16.7371 17.3334V4.00002ZM12.5705 9.83335V0.666687H0.0704651V13.1667L3.4038 9.83335H12.5705Z', - fill: 'currentColor', - })); -} diff --git a/src/components/icons/QuestionAnswer.jsx b/src/components/icons/QuestionAnswer.jsx new file mode 100644 index 00000000..3146afe0 --- /dev/null +++ b/src/components/icons/QuestionAnswer.jsx @@ -0,0 +1,18 @@ +import React from 'react'; + +export default function QuestionAnswer() { + return ( + + + + ); +} diff --git a/src/components/icons/QuestionAnswerOutline.js b/src/components/icons/QuestionAnswerOutline.js deleted file mode 100644 index cdc944dc..00000000 --- a/src/components/icons/QuestionAnswerOutline.js +++ /dev/null @@ -1,16 +0,0 @@ -import * as React from 'react'; - -import { _extends } from './common'; - -export default function QuestionAnswerOutline(props) { - return /* #__PURE__ */React.createElement('svg', _extends({ - width: 20, - height: 20, - viewBox: '0 0 20 20', - fill: 'none', - xmlns: 'http://www.w3.org/2000/svg', - }, props), /* #__PURE__ */React.createElement('path', { - d: 'M 16.7371 4 H 14.2371 V 11.5 H 3.4038 V 14 H 13.4038 L 16.7371 17.3334 V 4 Z M 12.5705 9.8333 V 0.6667 H 0.0705 V 13.1667 L 3.4038 9.8333 H 12.5705 Z M 11.465 8.618 H 1.038 V 1.683 H 11.465Z', - fill: 'currentColor', - })); -} diff --git a/src/components/icons/QuestionAnswerOutline.jsx b/src/components/icons/QuestionAnswerOutline.jsx new file mode 100644 index 00000000..f115c031 --- /dev/null +++ b/src/components/icons/QuestionAnswerOutline.jsx @@ -0,0 +1,18 @@ +import React from 'react'; + +export default function QuestionAnswerOutline() { + return ( + + + + ); +} diff --git a/src/components/icons/StarFilled.js b/src/components/icons/StarFilled.js deleted file mode 100644 index 2afce196..00000000 --- a/src/components/icons/StarFilled.js +++ /dev/null @@ -1,16 +0,0 @@ -import * as React from 'react'; - -import { _extends } from './common'; - -export default function StarFilled(props) { - return /* #__PURE__ */React.createElement('svg', _extends({ - width: 20, - height: 20, - viewBox: '0 0 20 20', - fill: 'none', - xmlns: 'http://www.w3.org/2000/svg', - }, props), /* #__PURE__ */React.createElement('path', { - d: 'M8.4038 13.3917L13.5538 16.5L12.1871 10.6417L16.7371 6.70002L10.7455 6.19169L8.4038 0.666687L6.06213 6.19169L0.0704651 6.70002L4.62047 10.6417L3.2538 16.5L8.4038 13.3917Z', - fill: 'currentColor', - })); -} diff --git a/src/components/icons/StarFilled.jsx b/src/components/icons/StarFilled.jsx new file mode 100644 index 00000000..1eff3ed0 --- /dev/null +++ b/src/components/icons/StarFilled.jsx @@ -0,0 +1,18 @@ +import React from 'react'; + +export default function StarFilled() { + return ( + + + + ); +} diff --git a/src/components/icons/StarOutline.js b/src/components/icons/StarOutline.js deleted file mode 100644 index f340015a..00000000 --- a/src/components/icons/StarOutline.js +++ /dev/null @@ -1,16 +0,0 @@ -import * as React from 'react'; - -import { _extends } from './common'; - -export default function StarOutline(props) { - return /* #__PURE__ */React.createElement('svg', _extends({ - width: 20, - height: 20, - viewBox: '0 0 20 20', - fill: 'none', - xmlns: 'http://www.w3.org/2000/svg', - }, props), /* #__PURE__ */React.createElement('path', { - d: 'M16.7371 6.69999L10.7455 6.18332L8.4038 0.666656L6.06213 6.19166L0.0704651 6.69999L4.62047 10.6417L3.2538 16.5L8.4038 13.3917L13.5538 16.5L12.1955 10.6417L16.7371 6.69999ZM8.4038 11.8333L5.27047 13.725L6.1038 10.1583L3.33713 7.75832L6.98713 7.44166L8.4038 4.08332L9.8288 7.44999L13.4788 7.76666L10.7121 10.1667L11.5455 13.7333L8.4038 11.8333Z', - fill: 'currentColor', - })); -} diff --git a/src/components/icons/StarOutline.jsx b/src/components/icons/StarOutline.jsx new file mode 100644 index 00000000..3e4fa397 --- /dev/null +++ b/src/components/icons/StarOutline.jsx @@ -0,0 +1,18 @@ +import React from 'react'; + +export default function StarOutline() { + return ( + + + + ); +} diff --git a/src/components/icons/ThumbUpFilled.js b/src/components/icons/ThumbUpFilled.js deleted file mode 100644 index 18d0da17..00000000 --- a/src/components/icons/ThumbUpFilled.js +++ /dev/null @@ -1,16 +0,0 @@ -import * as React from 'react'; - -import { _extends } from './common'; - -export default function ThumbUpFilled(props) { - return /* #__PURE__ */React.createElement('svg', _extends({ - width: 20, - height: 20, - viewBox: '0 0 20 20', - fill: 'none', - xmlns: 'http://www.w3.org/2000/svg', - }, props), /* #__PURE__ */React.createElement('path', { - d: 'M11.2122 0.833344L5.23715 6.81668V17.5H15.4955L18.5705 10.3333V6.66668H11.6455L12.5788 2.18334L11.2122 0.833344ZM0.237152 7.50001H3.57049V17.5H0.237152V7.50001Z', - fill: 'currentColor', - })); -} diff --git a/src/components/icons/ThumbUpFilled.jsx b/src/components/icons/ThumbUpFilled.jsx new file mode 100644 index 00000000..51f9c555 --- /dev/null +++ b/src/components/icons/ThumbUpFilled.jsx @@ -0,0 +1,18 @@ +import React from 'react'; + +export default function ThumbUpFilled() { + return ( + + + + ); +} diff --git a/src/components/icons/ThumbUpOutline.js b/src/components/icons/ThumbUpOutline.js deleted file mode 100644 index 9870c900..00000000 --- a/src/components/icons/ThumbUpOutline.js +++ /dev/null @@ -1,16 +0,0 @@ -import * as React from 'react'; - -import { _extends } from './common'; - -export default function ThumbUpOutline(props) { - return /* #__PURE__ */React.createElement('svg', _extends({ - width: 20, - height: 20, - viewBox: '0 0 20 20', - fill: 'none', - xmlns: 'http://www.w3.org/2000/svg', - }, props), /* #__PURE__ */React.createElement('path', { - d: 'M18.5705 6.66668V10.3333L15.4955 17.5L5.23715 17.5L5.23715 6.81668L11.2122 0.833344L12.5788 2.18334L11.6455 6.66668L18.5705 6.66668ZM6.90382 7.50834L6.90382 15.8333L14.3955 15.8333L16.9038 9.99168V8.33334L9.59548 8.33334L10.5205 3.88334L6.90382 7.50834Z M3.57049 17.5H0.237152L0.237152 7.50001H3.57049L3.57049 17.5Z', - fill: 'currentColor', - })); -} diff --git a/src/components/icons/ThumbUpOutline.jsx b/src/components/icons/ThumbUpOutline.jsx new file mode 100644 index 00000000..b4c17c9c --- /dev/null +++ b/src/components/icons/ThumbUpOutline.jsx @@ -0,0 +1,21 @@ +import React from 'react'; + +export default function ThumbUpOutline() { + return ( + + + + + ); +} diff --git a/src/components/icons/common.js b/src/components/icons/common.js deleted file mode 100644 index 7e30aae0..00000000 --- a/src/components/icons/common.js +++ /dev/null @@ -1,21 +0,0 @@ -/* eslint-disable no-underscore-dangle */ -/* eslint-disable no-func-assign */ -/* eslint-disable prefer-object-spread */ -/* eslint-disable prefer-rest-params */ -/* eslint-disable no-param-reassign */ -/* eslint-disable no-restricted-syntax */ -// eslint-disable-next-line import/prefer-default-export -export function _extends() { - _extends = Object.assign || function (target) { - for (let i = 1; i < arguments.length; i++) { - const source = arguments[i]; - for (const key in source) { - if (Object.prototype.hasOwnProperty.call(source, key)) { - target[key] = source[key]; - } - } - } - return target; - }; - return _extends.apply(this, arguments); -} diff --git a/src/components/icons/index.js b/src/components/icons/index.js index 6948c86c..27d2ff35 100644 --- a/src/components/icons/index.js +++ b/src/components/icons/index.js @@ -1,4 +1,5 @@ -export { default as Help } from './Help'; +export { default as People } from './People'; +export { default as Question } from './Question'; export { default as QuestionAnswer } from './QuestionAnswer'; export { default as QuestionAnswerOutline } from './QuestionAnswerOutline'; export { default as StarFilled } from './StarFilled'; diff --git a/src/discussions/common/AuthorLabel.jsx b/src/discussions/common/AuthorLabel.jsx index c0dcded1..ba07a354 100644 --- a/src/discussions/common/AuthorLabel.jsx +++ b/src/discussions/common/AuthorLabel.jsx @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; +import capitalize from 'lodash/capitalize'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { Icon } from '@edx/paragon'; @@ -18,6 +19,7 @@ function AuthorLabel({ }) { let icon = null; let authorLabelMessage = null; + if (authorLabel === 'Staff') { icon = Institution; authorLabelMessage = intl.formatMessage(messages.authorLabelStaff); @@ -26,9 +28,15 @@ function AuthorLabel({ icon = School; authorLabelMessage = intl.formatMessage(messages.authorLabelTA); } + + const fontWeight = authorLabelMessage ? 'font-weight-500' : 'font-weight-normal text-primary-500'; + const className = classNames('d-flex align-items-center', labelColor); + const labelContents = ( <> - {author} + + {capitalize(author)} + {icon && ( )} {authorLabelMessage && ( - + {authorLabelMessage} )} ); - const className = classNames('d-flex align-items-center', labelColor); + return linkToProfile ? React.createElement('a', { href: '#nowhere', className }, labelContents) : React.createElement('div', { className }, labelContents); diff --git a/src/discussions/common/time-locale.js b/src/discussions/common/time-locale.js index 4e955aaa..cea761c8 100644 --- a/src/discussions/common/time-locale.js +++ b/src/discussions/common/time-locale.js @@ -11,8 +11,8 @@ export default function timeLocale(number, index, totalSec) { ['%sd', 'in %s days'], ['1w', 'in 1 week'], ['%sw', 'in %s weeks'], - ['1M', 'in 1 month'], - ['%sM', 'in %s months'], + ['1m', 'in 1 month'], + ['%sm', 'in %s months'], ['1y', 'in 1 year'], ['%sy', 'in %s years'], ][index]; diff --git a/src/discussions/posts/PostsView.test.jsx b/src/discussions/posts/PostsView.test.jsx index d213dbc8..c6ef28e2 100644 --- a/src/discussions/posts/PostsView.test.jsx +++ b/src/discussions/posts/PostsView.test.jsx @@ -114,18 +114,21 @@ describe('PostsView', () => { }); expect(screen.getAllByText(/this is thread-\d+/i)).toHaveLength(threadCount); }); + test('displays a list of user posts', async () => { await act(async () => { await renderComponent({ myPosts: true }); }); - expect(screen.getAllByText('abc123')).toHaveLength(threadCount); + expect(screen.getAllByText('Abc123')).toHaveLength(threadCount); }); + test('displays a list of posts in a topic', async () => { await act(async () => { await renderComponent({ topicId: 'some-topic-1' }); }); expect(screen.getAllByText(/this is thread-\d+ in topic some-topic-1/i)).toHaveLength(Math.ceil(threadCount / 3)); }); + test('displays a list of posts in a category', async () => { await act(async () => { await renderComponent({ category: 'test-usage-key' }); diff --git a/src/discussions/posts/post/LikeButton.jsx b/src/discussions/posts/post/LikeButton.jsx index 14d54582..1baac194 100644 --- a/src/discussions/posts/post/LikeButton.jsx +++ b/src/discussions/posts/post/LikeButton.jsx @@ -2,9 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; -import { - Icon, IconButton, OverlayTrigger, Tooltip, -} from '@edx/paragon'; +import { Icon, IconButtonWithTooltip } from '@edx/paragon'; import { ThumbUpFilled, ThumbUpOutline } from '../../../components/icons'; import messages from './messages'; @@ -24,23 +22,19 @@ function LikeButton({ }; return ( -
- - {intl.formatMessage(voted ? messages.removeLike : messages.like)} - - )} - > - - +
+ {(count && count > 0) ? count : null}
); diff --git a/src/discussions/posts/post/PostFooter.jsx b/src/discussions/posts/post/PostFooter.jsx index 237c8b68..ead2436c 100644 --- a/src/discussions/posts/post/PostFooter.jsx +++ b/src/discussions/posts/post/PostFooter.jsx @@ -6,13 +6,14 @@ import * as timeago from 'timeago.js'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { - Badge, Icon, IconButton, OverlayTrigger, Tooltip, + Badge, Icon, IconButtonWithTooltip, OverlayTrigger, Tooltip, } from '@edx/paragon'; import { - Locked, People, + Locked, } from '@edx/paragon/icons'; import { + People, QuestionAnswer, QuestionAnswerOutline, StarFilled, @@ -31,6 +32,7 @@ function PostFooter({ }) { const dispatch = useDispatch(); timeago.register('time-locale', timeLocale); + return (
dispatch(updateExistingThread(post.id, { voted: !post.voted }))} voted={post.voted} /> - - {intl.formatMessage(post.following ? messages.unfollow : messages.follow)} - - )} - > - { - dispatch(updateExistingThread(post.id, { following: !post.following })); - return true; - }} - alt="Follow" - iconAs={Icon} - size="inline" - src={post.following ? StarFilled : StarOutline} - /> - - {preview && post.commentCount > 1 - && ( + { + dispatch(updateExistingThread(post.id, { following: !post.following })); + return true; + }} + size="inline" + className="p-3" + iconClassNames="icon-size" + /> + {preview && post.commentCount > 1 && ( +
+ + {post.commentCount} +
+ )} + {preview && post?.unreadCommentCount > 0 && post.commentCount > 1 && ( + + {intl.formatMessage(messages.newLabel, { count: post.unreadCommentCount })} + + )} +
+ {post.groupId && ( <> - - - {post.commentCount} + {post.groupName} + )} + > + + + + + + · )} - {post.unreadCommentCount && post.unreadCommentCount > 0 && post.commentCount > 1 ? ( - {intl.formatMessage(messages.newLabel, { count: post.unreadCommentCount })} - ) : null} -
- { - post.groupId - ? ( - <> - - {post.groupName || intl.formatMessage(messages.visibleToAll)} - - )} - > - - - - · - - - ) : null - } {timeago.format(post.createdAt, 'time-locale')} diff --git a/src/discussions/posts/post/PostFooter.test.jsx b/src/discussions/posts/post/PostFooter.test.jsx index 27af98bd..1655d73b 100644 --- a/src/discussions/posts/post/PostFooter.test.jsx +++ b/src/discussions/posts/post/PostFooter.test.jsx @@ -61,7 +61,7 @@ describe('PostFooter', () => { }); it("shows 'x new' badge for new comments", () => { - renderComponent(mockPost); + renderComponent(mockPost, true); expect(screen.getByText('2 New')).toBeTruthy(); }); @@ -84,6 +84,7 @@ describe('PostFooter', () => { renderComponent({ ...mockPost, groupId: 5, groupName: 'Test Cohort' }); expect(screen.getByTestId('cohort-icon')).toBeTruthy(); }); + it.each([[true, /unfollow/i], [false, /follow/i]])('test follow button when following=%s', async (following, message) => { renderComponent({ ...mockPost, following }); const followButton = screen.getByRole('button', { name: /follow/i }); diff --git a/src/discussions/posts/post/PostHeader.jsx b/src/discussions/posts/post/PostHeader.jsx index 0d7f98e5..b1127327 100644 --- a/src/discussions/posts/post/PostHeader.jsx +++ b/src/discussions/posts/post/PostHeader.jsx @@ -6,7 +6,7 @@ import { useSelector } from 'react-redux'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { Avatar, Badge, Icon } from '@edx/paragon'; -import { Help } from '../../../components/icons'; +import { Question } from '../../../components/icons'; import { AvatarBorderAndLabelColors, ThreadType } from '../../../data/constants'; import { ActionsDropdown, AuthorLabel } from '../../common'; import { selectAuthorAvatars } from '../data/selectors'; @@ -16,31 +16,37 @@ import { postShape } from './proptypes'; export function PostAvatar({ post, authorLabel, fromPostLink }) { const authorAvatars = useSelector(selectAuthorAvatars(post.author)); const borderColor = AvatarBorderAndLabelColors[authorLabel]; + return ( -
-
- {post.type === ThreadType.QUESTION && ( +
+ {post.type === ThreadType.QUESTION && ( - )} - -
+ )} +
); } diff --git a/src/discussions/posts/post/PostLink.jsx b/src/discussions/posts/post/PostLink.jsx index 35441f0b..8eaf3ea4 100644 --- a/src/discussions/posts/post/PostLink.jsx +++ b/src/discussions/posts/post/PostLink.jsx @@ -39,11 +39,11 @@ function PostLink({ const authorLabelColor = AvatarBorderAndLabelColors[post.authorLabel]; return ( isSelected(post.id)} + style={{ lineHeight: '21px' }} > {post.pinned && (
@@ -52,44 +52,53 @@ function PostLink({
)}
-
-
-
-
-
- {post.title} -
- {showAnsweredBadge - && ( -
- {intl.formatMessage(messages.answered)} - {' '}answered -
- )} - - {(post.abuseFlagged || post.abuseFlaggedCount) - && ( -
- {intl.formatMessage(messages.contentReported)} - {' '}reported -
- )} +
+
+
+
+ {post.title}
- + + {showAnsweredBadge && ( + + {intl.formatMessage(messages.answered)} + {' '}answered + + )} + + {(post.abuseFlagged || post.abuseFlaggedCount) && ( + + {intl.formatMessage(messages.contentReported)} + {' '}reported + + )}
-
+ +
{isPostPreviewAvailable(post.previewBody) ? post.previewBody : intl.formatMessage(messages.postWithoutPreview)} diff --git a/src/discussions/posts/post/messages.js b/src/discussions/posts/post/messages.js index 8d8764e5..ad376b73 100644 --- a/src/discussions/posts/post/messages.js +++ b/src/discussions/posts/post/messages.js @@ -33,8 +33,8 @@ const messages = defineMessages({ defaultMessage: 'Answered', description: 'Tooltip/alttext for button to unfollow a discussion post', }, - unfollow: { - id: 'discussions.post.unfollow', + unFollow: { + id: 'discussions.post.unFollow', defaultMessage: 'Unfollow', description: 'Tooltip/alttext for button to unfollow a discussion post', }, @@ -45,9 +45,14 @@ const messages = defineMessages({ }, removeLike: { id: 'discussions.post.removeLike', - defaultMessage: 'Remove like', + defaultMessage: 'Unlike', description: 'Tooltip/alttext for button to remove the like applied to a discussion post', }, + viewActivity: { + id: 'discussions.post.viewActivity', + defaultMessage: 'View Activity', + description: 'Tooltip/alttext for button to view the activity of a discussion post', + }, postClosed: { id: 'discussions.post.closed', defaultMessage: 'Post closed for responses and comments', @@ -58,11 +63,6 @@ const messages = defineMessages({ defaultMessage: 'Related to', description: 'Message followed the category and topic of post linking to in-course context', }, - visibleToAll: { - id: 'discussions.post.cohort.everyone', - defaultMessage: 'Everyone', - description: 'Cohort visibility indicator for all people', - }, deletePostTitle: { id: 'discussions.editor.delete.post.title', defaultMessage: 'Delete post', diff --git a/src/index.scss b/src/index.scss index 4f289cf1..ad40eb5e 100755 --- a/src/index.scss +++ b/src/index.scss @@ -6,8 +6,9 @@ $fa-font-path: "~font-awesome/fonts"; @import "~font-awesome/scss/font-awesome"; - -#post, #comment, #reply { +#post, +#comment, +#reply { img { height: auto; max-width: 100%; @@ -22,6 +23,10 @@ $fa-font-path: "~font-awesome/fonts"; border-color: #998200; } +.border-anonymous { + border-color: #F2F0EF; +} + .font-size-14 { font-size: 14px; } @@ -35,5 +40,23 @@ $fa-font-path: "~font-awesome/fonts"; } .font-family-inter { - font-family: 'Inter'; -} \ No newline at end of file + font-family: "Inter"; +} + +.icon-size { + height: 20px !important; + width: 20px !important; +} + +.mr-0\.5 { + margin-right: 2px; +} + +.badge-padding { + padding-top: 1px; + padding-bottom: 1px +} + +.discussion-post:hover { + background-color: unset !important; +}