diff --git a/src/pages-and-resources/discussions/app-config-form/apps/legacy/LegacyConfigForm.jsx b/src/pages-and-resources/discussions/app-config-form/apps/legacy/LegacyConfigForm.jsx
index 983c82a91..ab89ffe66 100644
--- a/src/pages-and-resources/discussions/app-config-form/apps/legacy/LegacyConfigForm.jsx
+++ b/src/pages-and-resources/discussions/app-config-form/apps/legacy/LegacyConfigForm.jsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { Card, Form } from '@edx/paragon';
import { Formik } from 'formik';
@@ -9,6 +9,7 @@ import DivisionByGroupFields from '../shared/DivisionByGroupFields';
import AnonymousPostingFields from '../shared/AnonymousPostingFields';
import DiscussionTopics from '../shared/discussion-topics/DiscussionTopics';
import BlackoutDatesField, { blackoutDatesRegex } from '../shared/BlackoutDatesField';
+import LegacyConfigFormProvider from './LegacyConfigFormProvider';
import messages from '../shared/messages';
import AppConfigFormDivider from '../shared/AppConfigFormDivider';
@@ -36,6 +37,7 @@ Yup.addMethod(Yup.object, 'uniqueProperty', function (propertyName, message) {
function LegacyConfigForm({
appConfig, onSubmit, formRef, intl, title,
}) {
+ const [validDiscussionTopics, setValidDiscussionTopics] = useState(appConfig.discussionTopics);
const legacyFormValidationSchema = Yup.object().shape({
blackoutDates: Yup.string().matches(
blackoutDatesRegex,
@@ -72,30 +74,37 @@ function LegacyConfigForm({
&& errors.discussionTopics
&& errors?.discussionTopics[index]?.name,
));
+ const contextValue = {
+ validDiscussionTopics,
+ setValidDiscussionTopics,
+ discussionTopicErrors,
+ };
return (
-
-
-
+
+
+
+
+
);
}}
diff --git a/src/pages-and-resources/discussions/app-config-form/apps/legacy/LegacyConfigFormProvider.jsx b/src/pages-and-resources/discussions/app-config-form/apps/legacy/LegacyConfigFormProvider.jsx
new file mode 100644
index 000000000..72ac50f82
--- /dev/null
+++ b/src/pages-and-resources/discussions/app-config-form/apps/legacy/LegacyConfigFormProvider.jsx
@@ -0,0 +1,26 @@
+import React, { createContext } from 'react';
+import PropTypes from 'prop-types';
+
+export const LegacyConfigFormContext = createContext({});
+
+export default function LegacyConfigFormProvider({ children, value }) {
+ return (
+
+ {children}
+
+ );
+}
+
+LegacyConfigFormProvider.propTypes = {
+ children: PropTypes.node.isRequired,
+ value: PropTypes.shape({
+ discussionTopicErrors: PropTypes.arrayOf(PropTypes.bool),
+ validDiscussionTopics: PropTypes.arrayOf(PropTypes.shape({
+ validTopics: PropTypes.arrayOf(PropTypes.shape({
+ name: PropTypes.string,
+ id: PropTypes.string,
+ })),
+ setValidDiscussionTopics: PropTypes.func,
+ })),
+ }).isRequired,
+};
diff --git a/src/pages-and-resources/discussions/app-config-form/apps/shared/DivisionByGroupFields.jsx b/src/pages-and-resources/discussions/app-config-form/apps/shared/DivisionByGroupFields.jsx
index a213740c1..21aac43f0 100644
--- a/src/pages-and-resources/discussions/app-config-form/apps/shared/DivisionByGroupFields.jsx
+++ b/src/pages-and-resources/discussions/app-config-form/apps/shared/DivisionByGroupFields.jsx
@@ -1,13 +1,14 @@
-import React, { useEffect } from 'react';
-import PropTypes from 'prop-types';
+import React, { useEffect, useContext } from 'react';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Form, TransitionReplace } from '@edx/paragon';
import { FieldArray, useFormikContext } from 'formik';
import FormSwitchGroup from '../../../../../generic/FormSwitchGroup';
import messages from './messages';
import AppConfigFormDivider from './AppConfigFormDivider';
+import { LegacyConfigFormContext } from '../legacy/LegacyConfigFormProvider';
-const DivisionByGroupFields = ({ intl, discussionTopicErrors }) => {
+const DivisionByGroupFields = ({ intl }) => {
+ const { validDiscussionTopics } = useContext(LegacyConfigFormContext);
const {
handleChange,
handleBlur,
@@ -104,8 +105,8 @@ const DivisionByGroupFields = ({ intl, discussionTopicErrors }) => {
onBlur={handleBlur}
defaultValue={divideDiscussionIds}
>
- {discussionTopics.map((topic, index) => (
- topic.name && !discussionTopicErrors[index] ? (
+ {validDiscussionTopics.map((topic) => (
+ topic.name ? (
{
DivisionByGroupFields.propTypes = {
intl: intlShape.isRequired,
- discussionTopicErrors: PropTypes.arrayOf(PropTypes.bool).isRequired,
};
export default injectIntl(DivisionByGroupFields);
diff --git a/src/pages-and-resources/discussions/app-config-form/apps/shared/discussion-topics/DiscussionTopics.jsx b/src/pages-and-resources/discussions/app-config-form/apps/shared/discussion-topics/DiscussionTopics.jsx
index 973fd34e0..ef9cc6a15 100644
--- a/src/pages-and-resources/discussions/app-config-form/apps/shared/discussion-topics/DiscussionTopics.jsx
+++ b/src/pages-and-resources/discussions/app-config-form/apps/shared/discussion-topics/DiscussionTopics.jsx
@@ -1,24 +1,28 @@
-import React, { useEffect } from 'react';
+import React, { useEffect, useContext } from 'react';
import { useDispatch } from 'react-redux';
import { Add } from '@edx/paragon/icons';
import { Button } from '@edx/paragon';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { FieldArray, useFormikContext } from 'formik';
import { v4 as uuid } from 'uuid';
-import PropTypes from 'prop-types';
import messages from '../messages';
import TopicItem from './TopicItem';
import { updateValidationStatus } from '../../../../data/slice';
+import { LegacyConfigFormContext } from '../../legacy/LegacyConfigFormProvider';
-const DiscussionTopics = ({ intl, discussionTopicErrors }) => {
+const DiscussionTopics = ({ intl }) => {
const {
values: appConfig,
- setFieldValue,
validateForm,
} = useFormikContext();
- const { discussionTopics, divideDiscussionIds } = appConfig;
+ const { discussionTopics } = appConfig;
const dispatch = useDispatch();
+ const {
+ discussionTopicErrors,
+ validDiscussionTopics,
+ setValidDiscussionTopics,
+ } = useContext(LegacyConfigFormContext);
const isFormInvalid = discussionTopicErrors.some((error) => error === true);
useEffect(() => {
@@ -28,16 +32,13 @@ const DiscussionTopics = ({ intl, discussionTopicErrors }) => {
const handleTopicDelete = async (topicIndex, topicId, remove) => {
await remove(topicIndex);
validateForm();
- const updatedDividedDiscussionsIds = divideDiscussionIds.filter(
- (id) => id !== topicId,
- );
- setFieldValue('divideDiscussionIds', updatedDividedDiscussionsIds);
+ const validTopics = validDiscussionTopics.filter(topic => topic.id !== topicId);
+ setValidDiscussionTopics(validTopics);
};
const addNewTopic = (push) => {
const payload = { name: '', id: uuid() };
push(payload);
- setFieldValue('divideDiscussionIds', [...divideDiscussionIds, payload.id]);
};
return (
@@ -85,7 +86,6 @@ const DiscussionTopics = ({ intl, discussionTopicErrors }) => {
DiscussionTopics.propTypes = {
intl: intlShape.isRequired,
- discussionTopicErrors: PropTypes.arrayOf(PropTypes.bool).isRequired,
};
export default injectIntl(DiscussionTopics);
diff --git a/src/pages-and-resources/discussions/app-config-form/apps/shared/discussion-topics/DiscussionTopics.test.jsx b/src/pages-and-resources/discussions/app-config-form/apps/shared/discussion-topics/DiscussionTopics.test.jsx
index 752d223cd..22368938e 100644
--- a/src/pages-and-resources/discussions/app-config-form/apps/shared/discussion-topics/DiscussionTopics.test.jsx
+++ b/src/pages-and-resources/discussions/app-config-form/apps/shared/discussion-topics/DiscussionTopics.test.jsx
@@ -20,6 +20,7 @@ import { getAppsUrl } from '../../../../data/api';
import { fetchApps } from '../../../../data/thunks';
import executeThunk from '../../../../../../utils';
import { legacyApiResponse } from '../../../../factories/mockApiResponses';
+import LegacyConfigFormProvider from '../../legacy/LegacyConfigFormProvider';
const appConfig = {
id: 'legacy',
@@ -36,7 +37,15 @@ const appConfig = {
blackoutDates: '[]',
};
-const discussionTopicErrors = [false, false];
+const contextValue = {
+ validDiscussionTopics: [
+ { name: 'General', id: 'course' },
+ { name: 'Edx', id: '13f106c6-6735-4e84-b097-0456cff55960' },
+ ],
+ setValidDiscussionTopics: jest.fn(),
+ discussionTopicErrors: [false, false],
+};
+
const courseId = 'course-v1:edX+TestX+Test_Course';
describe('DiscussionTopics', () => {
@@ -66,9 +75,11 @@ describe('DiscussionTopics', () => {
const wrapper = render(
-
-
-
+
+
+
+
+
,
);
diff --git a/src/pages-and-resources/discussions/app-config-form/apps/shared/discussion-topics/TopicItem.jsx b/src/pages-and-resources/discussions/app-config-form/apps/shared/discussion-topics/TopicItem.jsx
index 8269a3d64..24830b7af 100644
--- a/src/pages-and-resources/discussions/app-config-form/apps/shared/discussion-topics/TopicItem.jsx
+++ b/src/pages-and-resources/discussions/app-config-form/apps/shared/discussion-topics/TopicItem.jsx
@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { useState, useContext, useEffect } from 'react';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import {
Button,
@@ -13,15 +13,46 @@ import { Delete, ExpandLess, ExpandMore } from '@edx/paragon/icons';
import { useFormikContext } from 'formik';
import PropTypes from 'prop-types';
import messages from '../messages';
+import { LegacyConfigFormContext } from '../../legacy/LegacyConfigFormProvider';
+import uniqueItems from '../../../utils';
const TopicItem = ({
intl, index, name, onDelete, id, hasError,
}) => {
- const { handleChange, handleBlur, errors } = useFormikContext();
+ const {
+ handleChange, handleBlur, errors, values: appConfig, setFieldValue,
+ } = useFormikContext();
const [inFocus, setInFocus] = useState(false);
const [showDeletePopup, setShowDeletePopup] = useState(false);
const [collapseIsOpen, setCollapseOpen] = useState();
const isGeneralTopic = id === 'course';
+ const {
+ validDiscussionTopics,
+ setValidDiscussionTopics,
+ } = useContext(LegacyConfigFormContext);
+ const { discussionTopics, divideDiscussionIds } = appConfig;
+
+ /**
+ * Update valid discussion topics & divided discussion topics.
+ * Removes a specific topic from valid discussion topics & divided discussion topics
+ * if it is invalid.
+ * Adds a specific topic to valid discussion topics & divided discussion topics
+ * if it is invalid.
+ */
+ useEffect(() => {
+ if (hasError) {
+ const validTopicsIds = validDiscussionTopics.filter(topic => topic.id !== id);
+ setValidDiscussionTopics(validTopicsIds);
+ setFieldValue('divideDiscussionIds', divideDiscussionIds.filter(topic => topic.id !== id));
+ } else {
+ const validTopicsIds = uniqueItems(validDiscussionTopics.map(topic => topic.id), [id]);
+ const validTopics = discussionTopics.filter(
+ topic => validTopicsIds.includes(topic.id),
+ );
+ setValidDiscussionTopics(validTopics);
+ setFieldValue('divideDiscussionIds', uniqueItems(divideDiscussionIds, [id]));
+ }
+ }, [hasError, inFocus]);
const getHeading = (isOpen = false) => {
let heading;
diff --git a/src/pages-and-resources/discussions/app-config-form/utils.js b/src/pages-and-resources/discussions/app-config-form/utils.js
new file mode 100644
index 000000000..ceeee2f63
--- /dev/null
+++ b/src/pages-and-resources/discussions/app-config-form/utils.js
@@ -0,0 +1,6 @@
+/** Append items and makes sure the items are unique. */
+const uniqueItems = (sourceArray, items = []) => (
+ [...new Set([...sourceArray, ...items])]
+);
+
+export default uniqueItems;