handleKeyDown(e)}>
diff --git a/src/discussions/posts/post/PostLink.jsx b/src/discussions/posts/post/PostLink.jsx
index d83f4553..472b152b 100644
--- a/src/discussions/posts/post/PostLink.jsx
+++ b/src/discussions/posts/post/PostLink.jsx
@@ -63,7 +63,7 @@ function PostLink({
style={{ lineHeight: '21px' }}
aria-current={isSelected(post.id) ? 'page' : undefined}
role="option"
- tabindex={(isSelected(post.id) || idx === 0) ? 0 : -1}
+ tabIndex={(isSelected(post.id) || idx === 0) ? 0 : -1}
>
(filter
- ? item.name.toLowerCase()
- .includes(filter)
- : true
- ),
- )
- .map(topic => (
-
+ const filteredNonCoursewareTopics = nonCoursewareTopics.filter(item => (filter
+ ? item.name.toLowerCase().includes(filter)
+ : true
+ ));
+
+ return (nonCoursewareTopics && category === undefined)
+ && filteredNonCoursewareTopics.map((topic, index) => (
+
));
}
@@ -76,6 +80,20 @@ function TopicsView() {
const { courseId } = useContext(DiscussionContext);
const dispatch = useDispatch();
+ const handleKeyDown = (event) => {
+ const { key } = event;
+ if (key !== 'ArrowDown' && key !== 'ArrowUp') { return; }
+ const option = event.target;
+
+ let selectedOption;
+ if (key === 'ArrowDown') { selectedOption = option.nextElementSibling; }
+ if (key === 'ArrowUp') { selectedOption = option.previousElementSibling; }
+
+ if (selectedOption) {
+ selectedOption.focus();
+ }
+ };
+
useEffect(() => {
// Don't load till the provider information is available
if (provider) {
@@ -89,19 +107,19 @@ function TopicsView() {
}, [topicFilter]);
return (
-
-
- {
- topicFilter &&
dispatch(setFilter(''))} />
- }
-
-
- {provider === DiscussionProvider.OPEN_EDX && }
- {provider === DiscussionProvider.LEGACY && }
-
+
+ {topicFilter && (
+
dispatch(setFilter(''))}
+ />
+ )}
+ handleKeyDown(e)}>
+
+ {provider === DiscussionProvider.OPEN_EDX && }
+ {provider === DiscussionProvider.LEGACY && }
{
filteredTopicsCount === 0
diff --git a/src/discussions/topics/TopicsView.test.jsx b/src/discussions/topics/TopicsView.test.jsx
index 09c573c2..430d795d 100644
--- a/src/discussions/topics/TopicsView.test.jsx
+++ b/src/discussions/topics/TopicsView.test.jsx
@@ -159,7 +159,7 @@ describe('TopicsView', () => {
renderComponent();
const archivedTopicGroup = screen.queryAllByTestId('topic-group').pop();
expect(archivedTopicGroup).toHaveTextContent(/archived/i);
- const archivedTopicLinks = within(archivedTopicGroup).queryAllByRole('link');
+ const archivedTopicLinks = within(archivedTopicGroup).queryAllByRole('option');
expect(archivedTopicLinks).toHaveLength(2);
});
}
diff --git a/src/discussions/topics/messages.js b/src/discussions/topics/messages.js
index 6645e818..6586cc60 100644
--- a/src/discussions/topics/messages.js
+++ b/src/discussions/topics/messages.js
@@ -1,6 +1,34 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
+ discussions: {
+ id: 'discussions.topics.discussions',
+ defaultMessage: `{count, plural,
+ =0 {Discussion}
+ one {# Discussion}
+ other {# Discussions}
+ }`,
+ description: 'Display tooltip text used to indicate how many posts type are discussion',
+ },
+ questions: {
+ id: 'discussions.topics.questions',
+ defaultMessage: `{count, plural,
+ =0 {Question}
+ one {# Question}
+ other {# Questions}
+ }`,
+ description: 'Display tooltip text used to indicate how many posts type are questions',
+ },
+ reported: {
+ id: 'discussions.topics.reported',
+ defaultMessage: '{reported} reported',
+ description: 'Display tooltip text used to indicate how many posts are reported',
+ },
+ previouslyReported: {
+ id: 'discussions.topics.previouslyReported',
+ defaultMessage: '{previouslyReported} previously reported',
+ description: 'Display tooltip text used to indicate how many posts are previously reported',
+ },
sortedBy: {
id: 'discussions.topics.sort.message',
defaultMessage: 'Sorted by {sortBy}',
diff --git a/src/discussions/topics/topic-group/TopicGroupBase.jsx b/src/discussions/topics/topic-group/TopicGroupBase.jsx
index 9fae7223..337dcad6 100644
--- a/src/discussions/topics/topic-group/TopicGroupBase.jsx
+++ b/src/discussions/topics/topic-group/TopicGroupBase.jsx
@@ -24,45 +24,50 @@ function TopicGroupBase({
const filter = useSelector(selectTopicFilter);
const hasTopics = topics.length > 0;
const matchesFilter = filter
- ? groupTitle?.toLowerCase()
- .includes(filter)
+ ? groupTitle?.toLowerCase().includes(filter)
: true;
- const topicElements = topics.filter(
- topic => (
- filter
- ? (topic.name.toLowerCase()
- .includes(filter) || matchesFilter)
- : true
+
+ const filteredTopicElements = topics.filter(
+ topic => (filter
+ ? (topic.name.toLowerCase().includes(filter) || matchesFilter)
+ : true
),
- )
- .map(topic => ());
- const hasFilteredSubtopics = (topicElements.length > 0);
+ );
+
+ const hasFilteredSubtopics = (filteredTopicElements.length > 0);
if (!hasTopics || (!matchesFilter && !hasFilteredSubtopics)) {
return null;
}
return (
- {linkToGroup && groupId
- ? (
-
- {groupTitle}
-
- ) : (
-
- {groupTitle || intl.formatMessage(messages.unnamedTopicCategories)}
-
- )}
- {topicElements}
+
+ {linkToGroup && groupId
+ ? (
+
+ {groupTitle}
+
+ ) : (
+ groupTitle || intl.formatMessage(messages.unnamedTopicCategories)
+ )}
+
+ {filteredTopicElements.map((topic, index) => (
+
+ ))}
);
}
diff --git a/src/discussions/topics/topic-group/topic/Topic.jsx b/src/discussions/topics/topic-group/topic/Topic.jsx
index 08c0b4a2..57ab1ce8 100644
--- a/src/discussions/topics/topic-group/topic/Topic.jsx
+++ b/src/discussions/topics/topic-group/topic/Topic.jsx
@@ -2,65 +2,120 @@
import React from 'react';
import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import { useSelector } from 'react-redux';
import { useParams } from 'react-router';
import { Link } from 'react-router-dom';
-import { Icon } from '@edx/paragon';
-import { Error as ErrorIcon, Help, PostOutline } from '@edx/paragon/icons';
+import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
+import { Icon, OverlayTrigger, Tooltip } from '@edx/paragon';
+import { HelpOutline, PostOutline, Report } from '@edx/paragon/icons';
import { Routes } from '../../../../data/constants';
+import { selectUserHasModerationPrivileges, selectUserIsGroupTa } from '../../../data/selectors';
import { discussionsPath } from '../../../utils';
+import messages from '../../messages';
-function Topic({ topic }) {
+function Topic({
+ topic,
+ showDivider,
+ index,
+ intl,
+}) {
const { courseId } = useParams();
const topicUrl = discussionsPath(Routes.TOPICS.TOPIC, {
courseId,
topicId: topic.id,
});
- const icons = [
- {
- key: 'discussions',
- icon: PostOutline,
- count: topic.threadCounts?.discussion || 0,
- },
- {
- key: 'questions',
- icon: Help,
- count: topic.threadCounts?.question || 0,
- },
- ];
+ const userHasModerationPrivileges = useSelector(selectUserHasModerationPrivileges);
+ const userIsGroupTa = useSelector(selectUserIsGroupTa);
+ const { inactiveFlags, activeFlags } = topic;
+ const canSeeReportedStats = (activeFlags || inactiveFlags) && (userHasModerationPrivileges || userIsGroupTa);
+ const isSelected = (id) => window.location.pathname.includes(id);
+
return (
isSelected(topic.id)}
+ aria-current={isSelected(topic.id) ? 'page' : undefined}
+ role="option"
+ tabIndex={(isSelected(topic.id) || index === 0) ? 0 : -1}
>
-
- {topic.name}
-
-
- {
- icons.map(({
- key,
- icon,
- count,
- }) => (
-
-
- {/* Reserve some space for larger counts */}
-
- {count}
-
+
+
+
+
+ {topic.name}
- ))
- }
- {topic?.flags && (
-
-
- {topic.flags}
- )}
+
+
+
+ {intl.formatMessage(messages.discussions, {
+ count: topic.threadCounts?.discussion || 0,
+ })}
+
+
+ )}
+ >
+
+
+ {topic.threadCounts?.discussion || 0}
+
+
+
+
+ {intl.formatMessage(messages.questions, {
+ count: topic.threadCounts?.question || 0,
+ })}
+
+
+ )}
+ >
+
+
+ {topic.threadCounts?.question || 0}
+
+
+ {Boolean(canSeeReportedStats) && (
+
+
+ {Boolean(activeFlags) && (
+
+ {intl.formatMessage(messages.reported, { reported: activeFlags })}
+
+ )}
+ {Boolean(inactiveFlags) && (
+
+ {intl.formatMessage(messages.previouslyReported, { previouslyReported: inactiveFlags })}
+
+ )}
+
+
+ )}
+ >
+
+
+ {activeFlags}{Boolean(inactiveFlags) && `/${inactiveFlags}`}
+
+
+ )}
+
+
+ {!showDivider &&
}
);
}
@@ -73,7 +128,15 @@ export const topicShape = PropTypes.shape({
flags: PropTypes.number,
});
Topic.propTypes = {
+ intl: intlShape.isRequired,
topic: topicShape.isRequired,
+ showDivider: PropTypes.bool,
+ index: PropTypes.number,
};
-export default Topic;
+Topic.defaultProps = {
+ showDivider: true,
+ index: -1,
+};
+
+export default injectIntl(Topic);
diff --git a/src/index.scss b/src/index.scss
index 485297d4..308eb0ae 100755
--- a/src/index.scss
+++ b/src/index.scss
@@ -232,3 +232,11 @@ header {
position: sticky;
top: 0;
}
+
+.breadcrumb-menu {
+ z-index: 1;
+}
+
+.discussion-topic-group:last-of-type .divider{
+ display: none;
+}