Compare commits

...

10 Commits

Author SHA1 Message Date
Ihor Romaniuk
87dc89ec73 fix: block overflow when editing comment (#705) 2024-10-09 17:59:17 +05:00
Stanislav
b5c4cbd369 fix: Discussions UI fixes on mobile resolutions (#690) 2024-04-04 17:46:22 +05:00
Eugene Dyudyunov
efa0c1c9b7 fix: fixed redirection to learners tab in inContext view (#680)
* fix: redirection to learners tab in inContext view

* fix: changed username to simple text for incontext view

* test: username is not clickable in incontext view

---------

Co-authored-by: ayesha waris <73840786+ayesha-waris@users.noreply.github.com>
Co-authored-by: sohailfatima <23100065@lums.edu.pk>
Co-authored-by: Fatima Sohail <68312464+sohailfatima@users.noreply.github.com>
Co-authored-by: Awais Ansari <79941147+awais-ansari@users.noreply.github.com>
2024-03-21 15:55:32 +05:00
Stanislav
5ca0042802 fix: fixed post card border color issue (#640) (#677)
Co-authored-by: Awais Ansari <79941147+awais-ansari@users.noreply.github.com>
2024-03-08 14:50:00 +05:00
vladislavkeblysh
2dafb6ad82 feat: Editor bar visibility (quince.master) (#583)
* feat: fixed editor bar visibility

* feat: fixed z index
2024-01-03 17:49:04 +05:00
vladislavkeblysh
e9e3db5193 feat: Enhancements to page (quince.master) (#578)
* feat: fixed page styles
2023-12-14 11:52:32 +05:00
Kshitij Sobti
cbb35e7cad fix: null error at useRouteMatch when running on tutor (#623)
tutor sets the PUBLIC_PATH to '/discussions' which causes frontend-platform to
treat all URLs for matching etc to be relative to this path. Since many places
include '/discussions' in the match it causes those matches to break.

This change makes the default PUBLIC_PATH in .env.development to match the one
set by tutor and removes it from the base path of the router letting frontend
platform handle the prefix.

This also allows for deployments to customise this path to be something other
than 'discussions'.
2023-12-06 17:24:37 +05:00
Awais Ansari
cf8ef159e0 fix: resolved load more posts delay issue (#614) 2023-11-30 14:47:15 +05:00
Ihor Romaniuk
eb127cd8e1 fix: unify font-family with paragon component styles (#597) 2023-11-08 18:33:56 +05:00
Ihor Romaniuk
68841d03ee fix: container indents and style imports (#601) 2023-11-07 14:49:38 +05:00
14 changed files with 125 additions and 77 deletions

View File

@@ -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;

View File

@@ -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: {

View File

@@ -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 = {

View File

@@ -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 () => {

View File

@@ -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>

View File

@@ -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,
})}

View File

@@ -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');

View File

@@ -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"
>

View File

@@ -129,9 +129,9 @@ const Reply = ({ responseId }) => {
</div>
<div
className="bg-light-300 pl-4 pt-2.5 pr-2.5 pb-10px flex-fill"
style={{ borderRadius: '0rem 0.375rem 0.375rem' }}
style={{ borderRadius: '0rem 0.375rem 0.375rem', maxWidth: 'calc(100% - 50px)' }}
>
<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}

View File

@@ -26,6 +26,7 @@ Factory.define('thread')
'type',
'voted',
'pinned',
'copy_link',
],
author: 'test_user',
author_label: 'Staff',

View File

@@ -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'}` }}
>

View File

@@ -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(

View File

@@ -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>

View File

@@ -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;
}