feat: Add notify all learners discussion post checkbox (#779)

* feat: Add notify all learners discussion post checkbox

* refactor: refactored post editor

* test: added test cases

* fix: fixed lint errors

---------

Co-authored-by: Hassan Raza <h.raza@arbisoft.com>
Co-authored-by: Ayesha Waris <ayesha.waris@192.168.10.6>
This commit is contained in:
ayesha waris
2025-06-27 19:42:39 +05:00
committed by GitHub
parent 5d75e0361d
commit 7ebdf1be3e
6 changed files with 71 additions and 0 deletions

View File

@@ -31,6 +31,8 @@ export const selectEnableInContext = state => state.config.enableInContext;
export const selectIsPostingEnabled = state => state.config.isPostingEnabled;
export const selectIsNotifyAllLearnersEnabled = state => state.config.isNotifyAllLearnersEnabled;
export const selectModerationSettings = state => ({
postCloseReasons: state.config.postCloseReasons,
editReasons: state.config.editReasons,

View File

@@ -98,6 +98,7 @@ export const postThread = async (
cohort,
anonymous,
anonymousToPeers,
notifyAllLearners,
} = {},
enableInContextSidebar = false,
) => {
@@ -112,6 +113,7 @@ export const postThread = async (
anonymousToPeers,
groupId: cohort,
enableInContextSidebar,
notifyAllLearners,
});
const { data } = await getAuthenticatedHttpClient()
.post(getThreadsApiUrl(), postData);

View File

@@ -204,6 +204,7 @@ export function createNewThread({
anonymousToPeers,
cohort,
enableInContextSidebar,
notifyAllLearners,
}) {
return async (dispatch) => {
try {
@@ -217,12 +218,14 @@ export function createNewThread({
anonymous,
anonymousToPeers,
cohort,
notifyAllLearners,
}));
const data = await postThread(courseId, topicId, type, title, content, {
cohort,
following,
anonymous,
anonymousToPeers,
notifyAllLearners,
}, enableInContextSidebar);
dispatch(postThreadSuccess(camelCaseObject(data)));
} catch (error) {

View File

@@ -29,6 +29,7 @@ import {
selectAnonymousPostingConfig,
selectDivisionSettings,
selectEnableInContext,
selectIsNotifyAllLearnersEnabled,
selectModerationSettings,
selectUserHasModerationPrivileges,
selectUserIsGroupTa,
@@ -79,6 +80,7 @@ const PostEditor = ({
const userIsStaff = useSelector(selectUserIsStaff);
const archivedTopics = useSelector(selectArchivedTopics);
const postEditorId = `post-editor-${editExisting ? postId : 'new'}`;
const isNotifyAllLearnersEnabled = useSelector(selectIsNotifyAllLearnersEnabled);
const canDisplayEditReason = (editExisting
&& (userHasModerationPrivileges || userIsGroupTa || userIsStaff)
@@ -108,6 +110,7 @@ const PostEditor = ({
title: post?.title || '',
comment: post?.rawBody || '',
follow: isEmpty(post?.following) ? true : post?.following,
notifyAllLearners: false,
anonymous: allowAnonymous ? false : undefined,
anonymousToPeers: allowAnonymousToPeers ? false : undefined,
cohort: post?.cohort || 'default',
@@ -161,6 +164,7 @@ const PostEditor = ({
anonymousToPeers: allowAnonymousToPeers ? values.anonymousToPeers : undefined,
cohort,
enableInContextSidebar,
notifyAllLearners: values.notifyAllLearners,
}));
}
/* istanbul ignore if: TinyMCE is mocked so this cannot be easily tested */
@@ -216,6 +220,8 @@ const PostEditor = ({
anonymousToPeers: Yup.bool()
.default(false)
.nullable(),
notifyAllLearners: Yup.bool()
.default(false),
cohort: Yup.string()
.nullable()
.default(null),
@@ -417,6 +423,21 @@ const PostEditor = ({
<div className="d-flex flex-row mt-n4 w-75 text-primary font-style">
{!editExisting && (
<>
{isNotifyAllLearnersEnabled && (
<Form.Group>
<Form.Checkbox
name="notifyAllLearners"
checked={values.notifyAllLearners}
onChange={handleChange}
onBlur={handleBlur}
className="mr-4.5"
>
<span>
{intl.formatMessage(messages.notifyAllLearners)}
</span>
</Form.Checkbox>
</Form.Group>
)}
<Form.Group>
<Form.Checkbox
name="follow"

View File

@@ -19,6 +19,8 @@ import { initializeStore } from '../../../store';
import executeThunk from '../../../test-utils';
import { getCohortsApiUrl } from '../../cohorts/data/api';
import DiscussionContext from '../../common/context';
import { getCourseConfigApiUrl } from '../../data/api';
import fetchCourseConfig from '../../data/thunks';
import fetchCourseTopics from '../../topics/data/thunks';
import { getThreadsApiUrl } from '../data/api';
import { fetchThread } from '../data/thunks';
@@ -31,6 +33,7 @@ import '../data/__factories__';
const courseId = 'course-v1:edX+DemoX+Demo_Course';
const topicsApiUrl = `${getApiBaseUrl()}/api/discussion/v1/course_topics/${courseId}`;
const courseConfigApiUrl = getCourseConfigApiUrl();
const threadsApiUrl = getThreadsApiUrl();
let store;
let axiosMock;
@@ -143,6 +146,42 @@ describe('PostEditor', () => {
});
});
describe.each([
{
isNotifyAllLearnersEnabled: true,
description: 'when "Notify All Learners" is enabled',
},
{
isNotifyAllLearnersEnabled: false,
description: 'when "Notify All Learners" is disabled',
},
])('$description', ({ isNotifyAllLearnersEnabled }) => {
beforeEach(async () => {
store = initializeStore({
config: {
provider: 'legacy',
is_notify_all_learners_enabled: isNotifyAllLearnersEnabled,
moderationSettings: {},
},
});
axiosMock
.onGet(`${courseConfigApiUrl}${courseId}/`)
.reply(200, { is_notify_all_learners_enabled: isNotifyAllLearnersEnabled });
await store.dispatch(fetchCourseConfig(courseId));
renderComponent();
});
test(`should ${isNotifyAllLearnersEnabled ? 'show' : 'not show'} the "Notify All Learners" option`, async () => {
if (isNotifyAllLearnersEnabled) {
await waitFor(() => expect(screen.queryByText('Notify All Learners')).toBeInTheDocument());
} else {
await waitFor(() => expect(screen.queryByText('Notify All Learners')).not.toBeInTheDocument());
}
});
});
describe('cohorting', () => {
const dividedncw = ['ncw-topic-2'];
const dividedcw = ['category-1-topic-2', 'category-2-topic-1', 'category-2-topic-2'];

View File

@@ -89,6 +89,10 @@ const messages = defineMessages({
id: 'discussions.post.editor.anonymousToPeersPost',
defaultMessage: 'Post anonymously to peers',
},
notifyAllLearners: {
id: 'discussions.post.editor.notifyAllLearners',
defaultMessage: 'Notify All Learners',
},
submit: {
id: 'discussions.editor.submit',
defaultMessage: 'Submit',