Compare commits
9 Commits
aansari/si
...
open-relea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5c4cbd369 | ||
|
|
efa0c1c9b7 | ||
|
|
5ca0042802 | ||
|
|
2dafb6ad82 | ||
|
|
e9e3db5193 | ||
|
|
cbb35e7cad | ||
|
|
cf8ef159e0 | ||
|
|
eb127cd8e1 | ||
|
|
68841d03ee |
@@ -1,10 +1,11 @@
|
||||
@import "@edx/brand/paragon/fonts.scss";
|
||||
@import "@edx/brand/paragon/variables.scss";
|
||||
@import "@edx/paragon/scss/core/core.scss";
|
||||
@import "@edx/brand/paragon/overrides.scss";
|
||||
@import "~@edx/brand/paragon/fonts.scss";
|
||||
@import "~@edx/brand/paragon/variables.scss";
|
||||
@import "~@edx/paragon/scss/core/core.scss";
|
||||
@import "~@edx/brand/paragon/overrides.scss";
|
||||
|
||||
$fa-font-path: "~font-awesome/fonts";
|
||||
@import "~font-awesome/scss/font-awesome";
|
||||
|
||||
.course-tabs-navigation {
|
||||
border-bottom: solid 1px #eaeaea;
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
|
||||
export const getApiBaseUrl = () => getConfig().LMS_BASE_URL;
|
||||
export const getFullUrl = (path) => (
|
||||
new URL(`${getConfig().PUBLIC_PATH.replace(/\/$/, '')}/${path}`, window.location.origin).href
|
||||
);
|
||||
|
||||
/**
|
||||
* Enum for thread types.
|
||||
@@ -137,7 +140,7 @@ export const DiscussionProvider = {
|
||||
OPEN_EDX: 'openedx',
|
||||
};
|
||||
|
||||
const BASE_PATH = `${getConfig().PUBLIC_PATH}:courseId`;
|
||||
const BASE_PATH = '/:courseId';
|
||||
|
||||
export const Routes = {
|
||||
DISCUSSIONS: {
|
||||
|
||||
@@ -28,7 +28,7 @@ const AuthorLabel = ({
|
||||
}) => {
|
||||
timeago.register('time-locale', timeLocale);
|
||||
const intl = useIntl();
|
||||
const { courseId } = useContext(DiscussionContext);
|
||||
const { courseId, enableInContextSidebar } = useContext(DiscussionContext);
|
||||
let icon = null;
|
||||
let authorLabelMessage = null;
|
||||
|
||||
@@ -47,11 +47,11 @@ const AuthorLabel = ({
|
||||
const className = classNames('d-flex align-items-center', { 'mb-0.5': !postOrComment }, labelColor);
|
||||
|
||||
const showUserNameAsLink = useShowLearnersTab()
|
||||
&& linkToProfile && author && author !== intl.formatMessage(messages.anonymous);
|
||||
&& linkToProfile && author && author !== intl.formatMessage(messages.anonymous) && !enableInContextSidebar;
|
||||
|
||||
const authorName = useMemo(() => (
|
||||
<span
|
||||
className={classNames('mr-1.5 font-size-14 font-style font-weight-500', {
|
||||
className={classNames('mr-1.5 font-size-14 font-style font-weight-500 author-name', {
|
||||
'text-gray-700': isRetiredUser,
|
||||
'text-primary-500': !authorLabelMessage && !isRetiredUser,
|
||||
})}
|
||||
@@ -100,7 +100,7 @@ const AuthorLabel = ({
|
||||
{postCreatedAt && (
|
||||
<span
|
||||
title={postCreatedAt}
|
||||
className={classNames('font-family-inter align-content-center', {
|
||||
className={classNames('align-content-center', {
|
||||
'text-white': alert,
|
||||
'text-gray-500': !alert,
|
||||
})}
|
||||
@@ -114,7 +114,7 @@ const AuthorLabel = ({
|
||||
|
||||
return showUserNameAsLink
|
||||
? (
|
||||
<div className={className}>
|
||||
<div className={`${className} flex-wrap`}>
|
||||
<Link
|
||||
data-testid="learner-posts-link"
|
||||
id="learner-posts-link"
|
||||
@@ -127,7 +127,7 @@ const AuthorLabel = ({
|
||||
{labelContents}
|
||||
</div>
|
||||
)
|
||||
: <div className={className}>{authorName}{labelContents}</div>;
|
||||
: <div className={`${className} flex-wrap`}>{authorName}{labelContents}</div>;
|
||||
};
|
||||
|
||||
AuthorLabel.propTypes = {
|
||||
|
||||
@@ -21,11 +21,11 @@ let store;
|
||||
let axiosMock;
|
||||
let container;
|
||||
|
||||
function renderComponent(author, authorLabel, linkToProfile, labelColor) {
|
||||
function renderComponent(author, authorLabel, linkToProfile, labelColor, enableInContextSidebar) {
|
||||
const wrapper = render(
|
||||
<IntlProvider locale="en">
|
||||
<AppProvider store={store}>
|
||||
<DiscussionContext.Provider value={{ courseId }}>
|
||||
<DiscussionContext.Provider value={{ courseId, enableInContextSidebar }}>
|
||||
<AuthorLabel
|
||||
author={author}
|
||||
authorLabel={authorLabel}
|
||||
@@ -79,9 +79,9 @@ describe('Author label', () => {
|
||||
);
|
||||
|
||||
it(
|
||||
`it is "${!linkToProfile && 'not'}" clickable when linkToProfile is ${!!linkToProfile}`,
|
||||
`it is "${(!linkToProfile) && 'not'}" clickable when linkToProfile is ${!!linkToProfile} and enableInContextSidebar is false`,
|
||||
async () => {
|
||||
renderComponent(author, authorLabel, linkToProfile, labelColor);
|
||||
renderComponent(author, authorLabel, linkToProfile, labelColor, false);
|
||||
|
||||
if (linkToProfile) {
|
||||
expect(screen.queryByTestId('learner-posts-link')).toBeInTheDocument();
|
||||
@@ -91,6 +91,15 @@ describe('Author label', () => {
|
||||
},
|
||||
);
|
||||
|
||||
it(
|
||||
'it is not clickable when enableInContextSidebar is true',
|
||||
async () => {
|
||||
renderComponent(author, authorLabel, linkToProfile, labelColor, true);
|
||||
|
||||
expect(screen.queryByTestId('learner-posts-link')).not.toBeInTheDocument();
|
||||
},
|
||||
);
|
||||
|
||||
it(
|
||||
`it has "${!linkToProfile && 'not'}" label text and label color when linkToProfile is ${!!linkToProfile}`,
|
||||
async () => {
|
||||
|
||||
@@ -43,7 +43,7 @@ const EndorsedAlertBanner = ({
|
||||
height: '20px',
|
||||
}}
|
||||
/>
|
||||
<strong className="ml-2 font-family-inter">
|
||||
<strong className="ml-2">
|
||||
{intl.formatMessage(isQuestion ? messages.answer : messages.endorsed)}
|
||||
</strong>
|
||||
</div>
|
||||
|
||||
@@ -55,7 +55,7 @@ const DiscussionSidebar = ({ displaySidebar, postActionBarRef }) => {
|
||||
'd-none': !displaySidebar,
|
||||
'd-flex overflow-auto box-shadow-centered-1': displaySidebar,
|
||||
'w-100': !isOnDesktop,
|
||||
'sidebar-desktop-width': isOnDesktop && !isOnXLDesktop,
|
||||
'w-25 sidebar-desktop-width': isOnDesktop && !isOnXLDesktop,
|
||||
'w-25 sidebar-XL-width': isOnXLDesktop,
|
||||
'min-content-height': !enableInContextSidebar,
|
||||
})}
|
||||
|
||||
@@ -465,6 +465,20 @@ describe('ThreadView', () => {
|
||||
assertLastUpdateData({ pinned: false });
|
||||
});
|
||||
|
||||
it('should allow copying a link to the post', async () => {
|
||||
await waitFor(() => renderComponent(discussionPostId));
|
||||
const post = await screen.findByTestId('post-thread-1');
|
||||
const hoverCard = within(post).getByTestId('hover-card-thread-1');
|
||||
Object.assign(navigator, { clipboard: { writeText: jest.fn() } });
|
||||
await act(async () => {
|
||||
fireEvent.click(within(hoverCard).getByRole('button', { name: /actions menu/i }));
|
||||
});
|
||||
await act(async () => {
|
||||
fireEvent.click(within(hoverCard).getByRole('button', { name: /copy link/i }));
|
||||
});
|
||||
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(`http://localhost/${courseId}/posts/${discussionPostId}`);
|
||||
});
|
||||
|
||||
it('should allow reporting the post', async () => {
|
||||
await waitFor(() => renderComponent(discussionPostId));
|
||||
const post = await screen.findByTestId('post-thread-1');
|
||||
|
||||
@@ -36,7 +36,7 @@ const CommentsView = ({ endorsed }) => {
|
||||
|
||||
const handleDefinition = useCallback((message, commentsLength) => (
|
||||
<div
|
||||
className="mx-4 my-14px text-gray-700 font-style"
|
||||
className="comment-line mx-4 my-14px text-gray-700 font-style"
|
||||
role="heading"
|
||||
aria-level="2"
|
||||
>
|
||||
|
||||
@@ -131,7 +131,7 @@ const Reply = ({ responseId }) => {
|
||||
className="bg-light-300 pl-4 pt-2.5 pr-2.5 pb-10px flex-fill"
|
||||
style={{ borderRadius: '0rem 0.375rem 0.375rem' }}
|
||||
>
|
||||
<div className="d-flex flex-row justify-content-between" style={{ height: '24px' }}>
|
||||
<div className="d-flex flex-row justify-content-between">
|
||||
<AuthorLabel
|
||||
author={author}
|
||||
authorLabel={authorLabel}
|
||||
|
||||
@@ -26,6 +26,7 @@ Factory.define('thread')
|
||||
'type',
|
||||
'voted',
|
||||
'pinned',
|
||||
'copy_link',
|
||||
],
|
||||
author: 'test_user',
|
||||
author_label: 'Staff',
|
||||
|
||||
@@ -19,9 +19,9 @@ const PostTypeCard = ({
|
||||
<label htmlFor={`post-type-${value}`} className="d-flex p-0 my-0 mr-3">
|
||||
<Form.Radio value={value} id={`post-type-${value}`} className="sr-only">{type}</Form.Radio>
|
||||
<Card
|
||||
className={classNames('border-2 shadow-none', {
|
||||
'border-primary': selected,
|
||||
'border-light-400': !selected,
|
||||
className={classNames('shadow-none', {
|
||||
'border-primary-500-2': selected,
|
||||
'border-light-400-2': !selected,
|
||||
})}
|
||||
style={{ cursor: 'pointer', width: `${enableInContextSidebar ? '10.021rem' : '14.25rem'}` }}
|
||||
>
|
||||
|
||||
@@ -11,7 +11,7 @@ import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { Hyperlink, useToggle } from '@edx/paragon';
|
||||
|
||||
import HTMLLoader from '../../../components/HTMLLoader';
|
||||
import { ContentActions } from '../../../data/constants';
|
||||
import { ContentActions, getFullUrl } from '../../../data/constants';
|
||||
import { selectorForUnitSubsection, selectTopicContext } from '../../../data/selectors';
|
||||
import { AlertBanner, Confirmation } from '../../common';
|
||||
import { DiscussionContext } from '../../common/context';
|
||||
@@ -37,7 +37,7 @@ const Post = ({ handleAddResponseButton }) => {
|
||||
const location = useLocation();
|
||||
const history = useHistory();
|
||||
const dispatch = useDispatch();
|
||||
const courseId = useSelector((state) => state.config.id);
|
||||
const { courseId } = useContext(DiscussionContext);
|
||||
const topic = useSelector(selectTopic(topicId));
|
||||
const getTopicSubsection = useSelector(selectorForUnitSubsection);
|
||||
const topicContext = useSelector(selectTopicContext(topicId));
|
||||
@@ -78,8 +78,7 @@ const Post = ({ handleAddResponseButton }) => {
|
||||
}, [closed, postId, reasonCodesEnabled, showClosePostModal]);
|
||||
|
||||
const handlePostCopyLink = useCallback(() => {
|
||||
const postURL = new URL(`${getConfig().PUBLIC_PATH}${courseId}/posts/${postId}`, window.location.origin);
|
||||
navigator.clipboard.writeText(postURL.href);
|
||||
navigator.clipboard.writeText(getFullUrl(`${courseId}/posts/${postId}`));
|
||||
}, [window.location.origin, postId, courseId]);
|
||||
|
||||
const handlePostPin = useCallback(() => dispatch(
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable react/no-unknown-property */
|
||||
import React, { useContext, useMemo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
@@ -7,7 +6,7 @@ import { useSelector } from 'react-redux';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { Badge, Icon, Truncate } from '@edx/paragon';
|
||||
import { Badge, Icon } from '@edx/paragon';
|
||||
import { CheckCircle } from '@edx/paragon/icons';
|
||||
|
||||
import { PushPin } from '../../../components/icons';
|
||||
@@ -87,48 +86,46 @@ const PostLink = ({
|
||||
/>
|
||||
<div className="d-flex flex-column flex-fill" style={{ minWidth: 0 }}>
|
||||
<div className="d-flex flex-column justify-content-start mw-100 flex-fill" style={{ marginBottom: '-3px' }}>
|
||||
<div className="d-flex align-items-center pb-0 mb-0 flex-fill font-weight-500">
|
||||
<Truncate lines={1} className="mr-1.5" whiteSpace>
|
||||
<span
|
||||
class={
|
||||
classNames(
|
||||
'font-weight-500 font-size-14 text-primary-500 font-style align-bottom',
|
||||
{ 'font-weight-bolder': !read },
|
||||
)
|
||||
}
|
||||
<div className="d-flex align-items-center pb-0 mb-0 flex-fill">
|
||||
<div className="text-truncate mr-1">
|
||||
<span className={classNames(
|
||||
'font-weight-500 font-size-14 text-primary-500 font-style align-bottom mr-1',
|
||||
{ 'font-weight-bolder': !read },
|
||||
)}
|
||||
>
|
||||
{title}
|
||||
</span>
|
||||
<span class="align-bottom"> </span>
|
||||
<span
|
||||
class="text-gray-700 font-weight-normal font-size-14 font-style align-bottom"
|
||||
>
|
||||
{isPostPreviewAvailable(previewBody)
|
||||
? previewBody
|
||||
: intl.formatMessage(messages.postWithoutPreview)}
|
||||
<span className="text-gray-700 font-weight-normal font-size-14 font-style align-bottom">
|
||||
{isPostPreviewAvailable(previewBody) ? previewBody : intl.formatMessage(messages.postWithoutPreview)}
|
||||
</span>
|
||||
</Truncate>
|
||||
</div>
|
||||
{showAnsweredBadge && (
|
||||
<Icon src={CheckCircle} className="text-success font-weight-500 ml-auto badge-padding" data-testid="check-icon">
|
||||
<span className="sr-only">{' '}answered</span>
|
||||
</Icon>
|
||||
<Icon
|
||||
data-testid="check-icon"
|
||||
src={CheckCircle}
|
||||
className="text-success font-weight-500 ml-auto badge-padding"
|
||||
>
|
||||
<span className="sr-only">{' '}answered</span>
|
||||
</Icon>
|
||||
)}
|
||||
{canSeeReportedBadge && (
|
||||
<Badge
|
||||
variant="danger"
|
||||
data-testid="reported-post"
|
||||
className={`font-weight-500 badge-padding ${showAnsweredBadge ? 'ml-2' : 'ml-auto'}`}
|
||||
>
|
||||
{intl.formatMessage(messages.contentReported)}
|
||||
<span className="sr-only">{' '}reported</span>
|
||||
</Badge>
|
||||
<Badge
|
||||
variant="danger"
|
||||
data-testid="reported-post"
|
||||
className={`font-weight-500 badge-padding ${showAnsweredBadge ? 'ml-2' : 'ml-auto'}`}
|
||||
>
|
||||
{intl.formatMessage(messages.contentReported)}
|
||||
<span className="sr-only">{' '}reported</span>
|
||||
</Badge>
|
||||
)}
|
||||
{pinned && (
|
||||
<Icon
|
||||
src={PushPin}
|
||||
className={`post-summary-icons-dimensions text-gray-700
|
||||
${canSeeReportedBadge || showAnsweredBadge ? 'ml-2' : 'ml-auto'}`}
|
||||
/>
|
||||
<Icon
|
||||
src={PushPin}
|
||||
className={classNames('post-summary-icons-dimensions text-gray-700', {
|
||||
'ml-2': canSeeReportedBadge || showAnsweredBadge,
|
||||
'ml-auto': !canSeeReportedBadge && !showAnsweredBadge,
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@import "@edx/brand/paragon/fonts.scss";
|
||||
@import "@edx/brand/paragon/variables.scss";
|
||||
@import "@edx/paragon/scss/core/core.scss";
|
||||
@import "@edx/brand/paragon/overrides.scss";
|
||||
@import "~@edx/brand/paragon/fonts.scss";
|
||||
@import "~@edx/brand/paragon/variables.scss";
|
||||
@import "~@edx/paragon/scss/core/core.scss";
|
||||
@import "~@edx/brand/paragon/overrides.scss";
|
||||
|
||||
@import "~@edx/frontend-component-footer/dist/footer";
|
||||
@import "~@edx/frontend-component-header/dist/index";
|
||||
@@ -65,10 +65,6 @@ $fa-font-path: "~font-awesome/fonts";
|
||||
font-style: normal !important;
|
||||
}
|
||||
|
||||
.font-family-inter {
|
||||
font-family: "Inter";
|
||||
}
|
||||
|
||||
.post-footer-icon-dimentions {
|
||||
width: 32px !important;
|
||||
height: 32px !important;
|
||||
@@ -245,6 +241,10 @@ header {
|
||||
|
||||
.sidebar-desktop-width {
|
||||
min-width: 29rem !important;
|
||||
|
||||
@include media-breakpoint-down(lg) {
|
||||
min-width: 25rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-XL-width {
|
||||
@@ -255,8 +255,13 @@ header {
|
||||
background-color: #e9e6e4 !important;
|
||||
}
|
||||
|
||||
.border-2 {
|
||||
border: 2px solid #cccccc !important;
|
||||
.border-light-400-2 {
|
||||
border: 2px solid $light-400 !important;
|
||||
border-width: 2px !important;
|
||||
}
|
||||
|
||||
.border-primary-500-2 {
|
||||
border: 2px solid $primary-500 !important;
|
||||
border-width: 2px !important;
|
||||
}
|
||||
|
||||
@@ -278,7 +283,6 @@ header {
|
||||
|
||||
header {
|
||||
line-height: 28px;
|
||||
font-family: Inter, Helvetica Neue, Arial, sans-serif;
|
||||
font-size: 18px !important;
|
||||
|
||||
.user-dropdown {
|
||||
@@ -314,12 +318,10 @@ header {
|
||||
|
||||
#courseTabsNavigation {
|
||||
font-size: 18px !important;
|
||||
font-family: Inter, Helvetica Neue, Arial, sans-serif;
|
||||
z-index: 3;
|
||||
background-color: #fff;
|
||||
|
||||
.container-xl {
|
||||
padding-left: 31px;
|
||||
font-size: 1.125rem;
|
||||
|
||||
.nav {
|
||||
@@ -338,7 +340,7 @@ header {
|
||||
|
||||
.header-action-bar {
|
||||
background-color: #fff;
|
||||
z-index: 3;
|
||||
z-index: 2 !important;
|
||||
box-shadow: 0px 2px 4px rgb(0 0 0 / 15%), 0px 2px 8px rgb(0 0 0 / 15%);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
@@ -347,11 +349,19 @@ header {
|
||||
.nav-item:not(:last-child){
|
||||
.nav-link {
|
||||
border-right: 0;
|
||||
|
||||
@media screen and (max-width: 567px) {
|
||||
border-right: solid 1px #e9e6e4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tox-tinymce-aux {
|
||||
z-index: 1 !important;
|
||||
}
|
||||
|
||||
.breadcrumb-menu {
|
||||
z-index: 1;
|
||||
}
|
||||
@@ -467,6 +477,10 @@ header {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.pgn__avatar {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.spinner-dimentions {
|
||||
@@ -479,7 +493,6 @@ header {
|
||||
}
|
||||
|
||||
.font-style {
|
||||
font-family: "Inter";
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@@ -496,6 +509,11 @@ header {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.comment-line {
|
||||
width: calc(100% - 180px);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.post-preview,
|
||||
.discussion-comments {
|
||||
blockquote {
|
||||
@@ -537,3 +555,9 @@ header {
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.author-name {
|
||||
line-height: 1.5;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user