From af5bc1a664ecfa6fbef9652cbe36ffc84fbf5809 Mon Sep 17 00:00:00 2001 From: Mehak Nasir Date: Fri, 27 Jan 2023 17:15:44 +0500 Subject: [PATCH] fix: fixed post style according to figma --- src/discussions/comments/CommentsView.jsx | 33 +- .../comments/CommentsView.test.jsx | 477 +++++++++--------- .../comments/comment-icons/CommentIcons.jsx | 2 +- src/discussions/comments/comment/Comment.jsx | 58 +-- .../comments/comment/CommentHeader.jsx | 1 + src/discussions/comments/comment/Reply.jsx | 5 +- src/discussions/comments/messages.js | 12 +- src/discussions/common/ActionsDropdown.jsx | 4 + src/discussions/common/AlertBanner.jsx | 23 +- src/discussions/common/AuthorLabel.jsx | 56 +- .../common/EndorsedAlertBanner.jsx | 29 +- src/discussions/common/HoverCard.jsx | 35 +- src/discussions/common/HoverCard.test.jsx | 157 ++++++ src/discussions/posts/post/LikeButton.jsx | 41 +- src/discussions/posts/post/Post.jsx | 39 +- src/discussions/posts/post/PostFooter.jsx | 70 +-- .../posts/post/PostFooter.test.jsx | 5 - src/discussions/posts/post/PostHeader.jsx | 3 +- src/index.scss | 72 ++- 19 files changed, 663 insertions(+), 459 deletions(-) create mode 100644 src/discussions/common/HoverCard.test.jsx diff --git a/src/discussions/comments/CommentsView.jsx b/src/discussions/comments/CommentsView.jsx index f2e0acab..775b1808 100644 --- a/src/discussions/comments/CommentsView.jsx +++ b/src/discussions/comments/CommentsView.jsx @@ -88,16 +88,16 @@ function DiscussionCommentsView({ const handleDefinition = (message, commentsLength) => (
{intl.formatMessage(message, { num: commentsLength })}
); - const handleComments = (postComments, showLoadMoreResponses = false, marginBottom = true) => ( + const handleComments = (postComments, showLoadMoreResponses = false) => (
{postComments.map((comment, index) => ( ))} @@ -114,7 +114,7 @@ function DiscussionCommentsView({ onClick={handleLoadMoreResponses} variant="link" block="true" - className="px-4 py-0 mb-2 font-weight-500 font-size-14" + className="px-4 mt-3 py-0 mb-2 font-style-normal font-family-inter font-weight-500 font-size-14" style={{ lineHeight: '24px', border: '0px', @@ -125,8 +125,8 @@ function DiscussionCommentsView({ )} {isLoading && !showLoadMoreResponses && ( -
- +
+
)}
@@ -139,22 +139,22 @@ function DiscussionCommentsView({ <> {handleDefinition(messages.endorsedResponseCount, endorsedComments.length)} {endorsed === EndorsementStatus.DISCUSSION - ? handleComments(endorsedComments, true, false) - : handleComments(endorsedComments, false, false)} + ? handleComments(endorsedComments, true) + : handleComments(endorsedComments, false)} )} {endorsed !== EndorsementStatus.ENDORSED && ( <> {handleDefinition(messages.responseCount, unEndorsedComments.length)} {unEndorsedComments.length === 0 &&
} - {handleComments(unEndorsedComments, false, true)} + {handleComments(unEndorsedComments, false)} {(userCanAddThreadInBlackoutDate && !!unEndorsedComments.length && !isClosed) && (
{!addingResponse && (
diff --git a/src/discussions/comments/comment/Reply.jsx b/src/discussions/comments/comment/Reply.jsx index e7cb17c0..a2a5cbbc 100644 --- a/src/discussions/comments/comment/Reply.jsx +++ b/src/discussions/comments/comment/Reply.jsx @@ -108,16 +108,17 @@ function Reply({ />
-
+
{content.lastEdit?.reason && ( -
+
{intl.formatMessage(messages.editedBy)} - + + + + {intl.formatMessage(messages.fullStop)} {intl.formatMessage(messages.reason)}: {content.lastEdit.reason}
@@ -52,13 +58,20 @@ function AlertBanner({ )} {content.closed && ( -
+
{intl.formatMessage(messages.closedBy)} - + - + + {intl.formatMessage(messages.fullStop)} + + {content.closeReason && (`${intl.formatMessage(messages.reason)}: ${content.closeReason}`)} +
)} diff --git a/src/discussions/common/AuthorLabel.jsx b/src/discussions/common/AuthorLabel.jsx index 25f9fd26..7beb46f7 100644 --- a/src/discussions/common/AuthorLabel.jsx +++ b/src/discussions/common/AuthorLabel.jsx @@ -25,6 +25,7 @@ function AuthorLabel({ alert, postCreatedAt, authorToolTip, + postOrComment, }) { const location = useLocation(); const { courseId } = useContext(DiscussionContext); @@ -43,13 +44,13 @@ function AuthorLabel({ const isRetiredUser = author ? author.startsWith('retired__user') : false; - const className = classNames('d-flex align-items-center mb-0.5', labelColor); + const className = classNames('d-flex align-items-center', { 'mb-0.5': !postOrComment }, labelColor); const showUserNameAsLink = useShowLearnersTab() && linkToProfile && author && author !== intl.formatMessage(messages.anonymous); const labelContents = ( -
+
{!alert && ( - {authorLabelMessage && ( - - {authorLabelMessage} - - )} +
- { - postCreatedAt && ( - - {timeago.format(postCreatedAt, 'time-locale')} - - ) - } + {authorLabelMessage && ( + + {authorLabelMessage} + + )} + {postCreatedAt && ( + + {timeago.format(postCreatedAt, 'time-locale')} + + )}
); @@ -139,6 +139,7 @@ AuthorLabel.propTypes = { alert: PropTypes.bool, postCreatedAt: PropTypes.string, authorToolTip: PropTypes.bool, + postOrComment: PropTypes.bool, }; AuthorLabel.defaultProps = { @@ -148,6 +149,7 @@ AuthorLabel.defaultProps = { alert: false, postCreatedAt: null, authorToolTip: false, + postOrComment: false, }; export default injectIntl(AuthorLabel); diff --git a/src/discussions/common/EndorsedAlertBanner.jsx b/src/discussions/common/EndorsedAlertBanner.jsx index 56382f29..0d6320be 100644 --- a/src/discussions/common/EndorsedAlertBanner.jsx +++ b/src/discussions/common/EndorsedAlertBanner.jsx @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import * as timeago from 'timeago.js'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; -import { Alert } from '@edx/paragon'; +import { Alert, Icon } from '@edx/paragon'; import { CheckCircle, Verified } from '@edx/paragon/icons'; import { ThreadType } from '../../data/constants'; @@ -27,18 +27,26 @@ function EndorsedAlertBanner({ content.endorsed && (
- {intl.formatMessage( - isQuestion - ? messages.answer - : messages.endorsed, - )} - - +
+ + {intl.formatMessage( + isQuestion + ? messages.answer + : messages.endorsed, + )} + +
+
diff --git a/src/discussions/common/HoverCard.jsx b/src/discussions/common/HoverCard.jsx index 708d4f9e..925c689d 100644 --- a/src/discussions/common/HoverCard.jsx +++ b/src/discussions/common/HoverCard.jsx @@ -33,14 +33,15 @@ function HoverCard({ return (
{userCanAddThreadInBlackoutDate && (
)} - +
+ { + e.preventDefault(); + onLike(); + }} + /> +
{commentOrPost.following !== undefined && (
{ e.preventDefault(); onFollow(); @@ -84,20 +98,9 @@ function HoverCard({ />
)} -
- { - e.preventDefault(); - onLike(); - }} - /> -
+
- +
); diff --git a/src/discussions/common/HoverCard.test.jsx b/src/discussions/common/HoverCard.test.jsx new file mode 100644 index 00000000..532e9385 --- /dev/null +++ b/src/discussions/common/HoverCard.test.jsx @@ -0,0 +1,157 @@ +// import { +// act, fireEvent, render, screen, waitFor, within, +// } from '@testing-library/react'; +// import MockAdapter from 'axios-mock-adapter'; +// import { IntlProvider } from 'react-intl'; +// import { MemoryRouter, Route } from 'react-router'; +// import { Factory } from 'rosie'; + +// import { camelCaseObject, initializeMockApp } from '@edx/frontend-platform'; +// import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; +// import { AppProvider } from '@edx/frontend-platform/react'; + +// import { initializeStore } from '../../store'; +// import { executeThunk } from '../../test-utils'; +// import { getCourseConfigApiUrl } from '../data/api'; +// import { fetchCourseConfig } from '../data/thunks'; +// import DiscussionContent from '../discussions-home/DiscussionContent'; +// import { getThreadsApiUrl } from '../posts/data/api'; +// import { fetchThreads } from '../posts/data/thunks'; +// import { getCommentsApiUrl } from './data/api'; +// import { DiscussionContext } from './context'; + +// import '../posts/data/__factories__'; +// import './data/__factories__'; + +// const courseConfigApiUrl = getCourseConfigApiUrl(); +// const commentsApiUrl = getCommentsApiUrl(); +// const threadsApiUrl = getThreadsApiUrl(); +// const discussionPostId = 'thread-1'; +// const questionPostId = 'thread-2'; +// const closedPostId = 'thread-2'; +// const courseId = 'course-v1:edX+TestX+Test_Course'; +// let store; +// let axiosMock; +// let testLocation; + +// function mockAxiosReturnPagedComments() { +// [null, false, true].forEach(endorsed => { +// const postId = endorsed === null ? discussionPostId : questionPostId; +// [1, 2].forEach(page => { +// axiosMock +// .onGet(commentsApiUrl, { +// params: { +// thread_id: postId, +// page, +// page_size: undefined, +// requested_fields: 'profile_image', +// endorsed, +// }, +// }) +// .reply(200, Factory.build('commentsResult', { can_delete: true }, { +// threadId: postId, +// page, +// pageSize: 1, +// count: 2, +// endorsed, +// childCount: page === 1 ? 2 : 0, +// })); +// }); +// }); +// } + +// function mockAxiosReturnPagedCommentsResponses() { +// const parentId = 'comment-1'; +// const commentsResponsesApiUrl = `${commentsApiUrl}${parentId}/`; +// const paramsTemplate = { +// page: undefined, +// page_size: undefined, +// requested_fields: 'profile_image', +// }; + +// for (let page = 1; page <= 2; page++) { +// axiosMock +// .onGet(commentsResponsesApiUrl, { params: { ...paramsTemplate, page } }) +// .reply(200, Factory.build('commentsResult', null, { +// parentId, +// page, +// pageSize: 1, +// count: 2, +// })); +// } +// } + +// function renderComponent(postId) { +// render( +// +// +// +// +// +// { +// testLocation = location; +// return null; +// }} +// /> +// +// +// +// , +// ); +// } + +// describe('HoverCard', () => { +// beforeEach(() => { +// initializeMockApp({ +// authenticatedUser: { +// userId: 3, +// username: 'abc123', +// administrator: true, +// roles: [], +// }, +// }); + +// store = initializeStore(); +// Factory.resetAll(); +// axiosMock = new MockAdapter(getAuthenticatedHttpClient()); +// axiosMock.onGet(threadsApiUrl) +// .reply(200, Factory.build('threadsResult')); +// axiosMock.onPatch(new RegExp(`${commentsApiUrl}*`)).reply(({ +// url, +// data, +// }) => { +// const commentId = url.match(/comments\/(?[a-z1-9-]+)\//).groups.id; +// const { +// rawBody, +// } = camelCaseObject(JSON.parse(data)); +// return [200, Factory.build('comment', { +// id: commentId, +// rendered_body: rawBody, +// raw_body: rawBody, +// })]; +// }); +// axiosMock.onPost(commentsApiUrl) +// .reply(({ data }) => { +// const { +// rawBody, +// threadId, +// } = camelCaseObject(JSON.parse(data)); +// return [200, Factory.build( +// 'comment', +// { +// rendered_body: rawBody, +// raw_body: rawBody, +// thread_id: threadId, +// }, +// )]; +// }); + +// executeThunk(fetchThreads(courseId), store.dispatch, store.getState); +// mockAxiosReturnPagedComments(); +// mockAxiosReturnPagedCommentsResponses(); +// }); +// }); diff --git a/src/discussions/posts/post/LikeButton.jsx b/src/discussions/posts/post/LikeButton.jsx index 30cb66ad..70b9ef37 100644 --- a/src/discussions/posts/post/LikeButton.jsx +++ b/src/discussions/posts/post/LikeButton.jsx @@ -2,7 +2,9 @@ import React from 'react'; import PropTypes from 'prop-types'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; -import { Icon, IconButtonWithTooltip } from '@edx/paragon'; +import { + Icon, IconButton, OverlayTrigger, Tooltip, +} from '@edx/paragon'; import { ThumbUpFilled, ThumbUpOutline } from '../../../components/icons'; import messages from './messages'; @@ -12,7 +14,6 @@ function LikeButton({ intl, onClick, voted, - preview, }) { const handleClick = (e) => { e.preventDefault(); @@ -24,19 +25,27 @@ function LikeButton({ return (
- - {(count && count > 0) ? count : null} + + {intl.formatMessage(voted ? messages.removeLike : messages.like)} + + )} + > + + +
+ {(count && count > 0) ? count : null} +
+
); } @@ -46,13 +55,11 @@ LikeButton.propTypes = { intl: intlShape.isRequired, onClick: PropTypes.func, voted: PropTypes.bool, - preview: PropTypes.bool, }; LikeButton.defaultProps = { voted: false, onClick: undefined, - preview: false, }; export default injectIntl(LikeButton); diff --git a/src/discussions/posts/post/Post.jsx b/src/discussions/posts/post/Post.jsx index b7a5a9ea..d2470838 100644 --- a/src/discussions/posts/post/Post.jsx +++ b/src/discussions/posts/post/Post.jsx @@ -1,4 +1,4 @@ -import React, { useContext, useState } from 'react'; +import React, { useContext } from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; @@ -41,7 +41,6 @@ function Post({ const [isDeleting, showDeleteConfirmation, hideDeleteConfirmation] = useToggle(false); const [isReporting, showReportConfirmation, hideReportConfirmation] = useToggle(false); const [isClosing, showClosePostModal, hideClosePostModal] = useToggle(false); - const [showHoverCard, setShowHoverCard] = useState(false); const handleAbusedFlag = () => { if (post.abuseFlagged) { @@ -91,10 +90,8 @@ function Post({ return (
setShowHoverCard(true)} - onMouseLeave={() => setShowHoverCard(false)} + className="d-flex flex-column w-100 mw-100 post-card-comment" + aria-level={5} > )} - {showHoverCard && ( - dispatch(updateExistingThread(post.id, { voted: !post.voted }))} - onFollow={() => dispatch(updateExistingThread(post.id, { following: !post.following }))} - isClosedPost={post.closed} - /> - )} + + dispatch(updateExistingThread(post.id, { voted: !post.voted }))} + onFollow={() => dispatch(updateExistingThread(post.id, { following: !post.following }))} + isClosedPost={post.closed} + /> + -
- +
+
{topicContext && topic && (
- {intl.formatMessage(messages.relatedTo)}{' '} + {intl.formatMessage(messages.relatedTo)}{' '} +
{post.voteCount !== 0 && ( dispatch(updateExistingThread(post.id, { voted: !post.voted }))} voted={post.voted} - preview={preview} /> )} {post.following && ( - { - e.preventDefault(); - dispatch(updateExistingThread(post.id, { following: !post.following })); - return true; - }} - size={preview ? 'inline' : 'sm'} - className={preview && 'p-3'} - iconClassNames={preview && 'icon-size'} - /> - )} - {preview && post.commentCount > 1 && ( -
- + {intl.formatMessage(post.following ? messages.unFollow : messages.follow)} + + )} + > + { + e.preventDefault(); + dispatch(updateExistingThread(post.id, { following: !post.following })); + return true; + }} iconAs={Icon} - alt="Comment Count" - size="inline" - className="p-3 mr-0.5" - iconClassNames="icon-size" + iconClassNames="follow-icon-dimentions" + className="post-footer-icon-dimentions" + alt="Follow" /> - {post.commentCount} -
- )} - {showNewCountLabel && preview && post?.unreadCommentCount > 0 && post.commentCount > 1 && ( - - {intl.formatMessage(messages.newLabel, { count: post.unreadCommentCount })} - + )}
{post.groupId && userHasModerationPrivileges && ( @@ -101,7 +79,7 @@ function PostFooter({ )} - {!preview && post.closed + {post.closed && ( { }); }); - it("shows 'x new' badge for new comments in case of read post only", () => { - renderComponent(mockPost, true, true); - expect(screen.getByText('2 New')).toBeTruthy(); - }); - it("doesn't have 'new' badge when there are 0 new comments", () => { renderComponent({ ...mockPost, unreadCommentCount: 0 }); expect(screen.queryByText('2 New')).toBeFalsy(); diff --git a/src/discussions/posts/post/PostHeader.jsx b/src/discussions/posts/post/PostHeader.jsx index 2f2976c6..cf455872 100644 --- a/src/discussions/posts/post/PostHeader.jsx +++ b/src/discussions/posts/post/PostHeader.jsx @@ -108,13 +108,14 @@ function PostHeader({ && {intl.formatMessage(messages.answered)}}
) - :
{post.title}
} + :
{post.title}
}
diff --git a/src/index.scss b/src/index.scss index 4703c01a..61e8a444 100755 --- a/src/index.scss +++ b/src/index.scss @@ -45,6 +45,14 @@ $fa-font-path: "~font-awesome/fonts"; font-size: 14px; } +.font-size-12 { + font-size: 12px; +} + +.font-size-8 { + font-size: 8px; +} + .font-weight-500 { font-weight: 500; } @@ -57,9 +65,24 @@ $fa-font-path: "~font-awesome/fonts"; font-family: "Inter"; } -.icon-size { - height: 15px !important; - width: 15px !important; +.post-footer-icon-dimentions { + width: 32px !important; + height: 32px !important; +} + +.like-icon-dimentions { + width: 21px !important; + height: 23px !important; +} + +.follow-icon-dimentions { + width: 21px !important; + height: 24px !important; +} + +.dropdown-icon-dimentions { + width: 20px !important; + height: 21px !important; } .post-summary-icons-dimensions { @@ -67,11 +90,6 @@ $fa-font-path: "~font-awesome/fonts"; width: 16px !important; } -.footer-icons-dimensions { - height: 16px !important; - width: 16px !important; -} - .post-summary-timestamp { font-size: 12px !important; line-height: 20px !important; @@ -155,6 +173,14 @@ $fa-font-path: "~font-awesome/fonts"; padding-bottom: 8px; } +.pb-10px { + padding-bottom: 10px; +} + +.pt-10px { + padding-top: 10px !important; +} + .px-10px { padding-left: 10px; padding-right: 10px; @@ -357,7 +383,7 @@ header { } .post-card-padding { - padding: 24px 24px 6px 24px; + padding: 24px 24px 10px 24px; } .post-card-margin { @@ -381,7 +407,9 @@ header { } .hover-button:hover { - background-color: #F2F0EF; + background-color: #F2F0EF !important; + height: 36px; + border: none; } .btn-tertiary:hover { @@ -393,14 +421,26 @@ header { background-color: transparent; } -.comment-card-padding { - margin: 24px 24px 0px 24px; -} - .disable-div { pointer-events: none; } -[role=listitem]:focus { - border: 2px solid black; +.on-focus:focus { + outline: 2px solid black; +} + +.html-loader p:last-child { + margin-bottom: 0px; +} + +.post-card-comment:hover, +.post-card-comment:focus { + .hover-card { + display: flex !important; + } +} + +.spinner-dimentions { + height: 24px; + width: 24px; }