diff --git a/src/data/constants.js b/src/data/constants.js
index 65c330ef6d..6e97135e33 100644
--- a/src/data/constants.js
+++ b/src/data/constants.js
@@ -53,3 +53,7 @@ export const VisibilityTypes = /** @type {const} */ ({
UNSCHEDULED: 'unscheduled',
NEEDS_ATTENTION: 'needs_attention',
});
+
+export const TOTAL_LENGTH_KEY = 'total-length';
+
+export const MAX_TOTAL_LENGTH = 65;
diff --git a/src/generic/create-or-rerun-course/CreateOrRerunCourseForm.jsx b/src/generic/create-or-rerun-course/CreateOrRerunCourseForm.jsx
index cffbf53f24..36bafc5974 100644
--- a/src/generic/create-or-rerun-course/CreateOrRerunCourseForm.jsx
+++ b/src/generic/create-or-rerun-course/CreateOrRerunCourseForm.jsx
@@ -16,7 +16,7 @@ import TypeaheadDropdown from '../../editors/sharedComponents/TypeaheadDropdown'
import AlertMessage from '../alert-message';
import { STATEFUL_BUTTON_STATES } from '../../constants';
-import { RequestStatus } from '../../data/constants';
+import { RequestStatus, TOTAL_LENGTH_KEY } from '../../data/constants';
import { getSavingStatus } from '../data/selectors';
import { getStudioHomeData } from '../../studio-home/data/selectors';
import { updatePostErrors } from '../data/slice';
@@ -132,6 +132,8 @@ const CreateOrRerunCourseForm = ({
},
];
+ const errorMessage = errors[TOTAL_LENGTH_KEY] || postErrors?.errMsg;
+
const createButtonState = {
labels: {
default: intl.formatMessage(isCreateNewCourse ? messages.createButton : messages.rerunCreateButton),
@@ -202,11 +204,11 @@ const CreateOrRerunCourseForm = ({
return (
- {showErrorBanner ? (
+ {(errors[TOTAL_LENGTH_KEY] || showErrorBanner) ? (
', () => {
expect(rerunBtn).toBeDisabled();
});
+ it('shows error message when total length exceeds 65 characters', async () => {
+ const updatedProps = {
+ ...props,
+ initialValues: {
+ displayName: 'Long Title Course',
+ org: 'long-org',
+ number: 'number',
+ run: '2024',
+ },
+ };
+
+ render();
+ await mockStore();
+ const numberInput = screen.getByPlaceholderText(messages.courseNumberPlaceholder.defaultMessage);
+
+ fireEvent.change(numberInput, { target: { value: 'long-name-which-is-longer-than-65-characters-to-check-for-errors' } });
+
+ waitFor(() => {
+ expect(screen.getByText(messages.totalLengthError)).toBeInTheDocument();
+ });
+ });
+
it('should be disabled create button if form has error', async () => {
render();
await mockStore();
diff --git a/src/generic/create-or-rerun-course/hooks.jsx b/src/generic/create-or-rerun-course/hooks.jsx
index a5c5196a80..2c0a8d0b71 100644
--- a/src/generic/create-or-rerun-course/hooks.jsx
+++ b/src/generic/create-or-rerun-course/hooks.jsx
@@ -6,7 +6,7 @@ import * as Yup from 'yup';
import { useNavigate } from 'react-router-dom';
import { REGEX_RULES } from '../../constants';
-import { RequestStatus } from '../../data/constants';
+import { RequestStatus, MAX_TOTAL_LENGTH, TOTAL_LENGTH_KEY } from '../../data/constants';
import { getStudioHomeData } from '../../studio-home/data/selectors';
import {
getRedirectUrlObj,
@@ -60,6 +60,12 @@ const useCreateOrRerunCourse = (initialValues) => {
intl.formatMessage(messages.disallowedCharsError),
)
.matches(noSpaceRule, intl.formatMessage(messages.noSpaceError)),
+ }).test(TOTAL_LENGTH_KEY, intl.formatMessage(messages.totalLengthError), function validateTotalLength() {
+ const { org, number, run } = this?.options.originalValue || {};
+ if ((org?.length || 0) + (number?.length || 0) + (run?.length || 0) > MAX_TOTAL_LENGTH) {
+ return this.createError({ path: TOTAL_LENGTH_KEY, message: intl.formatMessage(messages.totalLengthError) });
+ }
+ return true;
});
const {
diff --git a/src/generic/create-or-rerun-course/messages.js b/src/generic/create-or-rerun-course/messages.js
index 16a07af20e..194271d544 100644
--- a/src/generic/create-or-rerun-course/messages.js
+++ b/src/generic/create-or-rerun-course/messages.js
@@ -1,4 +1,5 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
+import { MAX_TOTAL_LENGTH } from '../../data/constants';
const messages = defineMessages({
courseDisplayNameLabel: {
@@ -117,6 +118,10 @@ const messages = defineMessages({
id: 'course-authoring.create-or-rerun-course.no-space.error',
defaultMessage: 'Please do not use any spaces in this field.',
},
+ totalLengthError: {
+ id: 'course-authoring.create-or-rerun-course.total-length-error.error',
+ defaultMessage: `The combined length of the organization, course number and course run fields cannot be more than ${MAX_TOTAL_LENGTH} characters.`,
+ },
alertErrorExistsAriaLabelledBy: {
id: 'course-authoring.create-or-rerun-course.error.already-exists.labelledBy',
defaultMessage: 'alert-already-exists-title',