From fcce1df7a69dca708dbdd0af8d9e648b1935d763 Mon Sep 17 00:00:00 2001 From: Brayan Ceron Date: Wed, 8 Apr 2026 16:54:57 -0500 Subject: [PATCH 1/6] feat: implement grading permissions and authorization hooks --- src/authz/constants.ts | 3 + src/authz/hooks.ts | 81 +++++++++++++++++++ src/authz/permissionHelpers.ts | 12 +++ src/grading-settings/GradingSettings.jsx | 23 +++++- .../assignments/AssignmentItem.jsx | 4 + .../assignments/AssignmentTypeName.jsx | 4 + .../assignment-section/index.jsx | 9 +++ src/grading-settings/credit-section/index.jsx | 7 ++ .../deadline-section/index.jsx | 4 + .../grading-scale/GradingScale.jsx | 7 +- .../components/GradingScaleHandle.jsx | 10 ++- .../components/GradingScaleSegment.tsx | 7 +- 12 files changed, 165 insertions(+), 6 deletions(-) create mode 100644 src/authz/hooks.ts create mode 100644 src/authz/permissionHelpers.ts diff --git a/src/authz/constants.ts b/src/authz/constants.ts index 88d4b4da58..aa148ea08b 100644 --- a/src/authz/constants.ts +++ b/src/authz/constants.ts @@ -17,4 +17,7 @@ export const CONTENT_LIBRARY_PERMISSIONS = { export const COURSE_PERMISSIONS = { MANAGE_ADVANCED_SETTINGS: 'courses.manage_advanced_settings', + + VIEW_GRADING_SETTINGS: 'courses.view_grading_settings', + EDIT_GRADING_SETTINGS: 'courses.edit_grading_settings', }; diff --git a/src/authz/hooks.ts b/src/authz/hooks.ts new file mode 100644 index 0000000000..40fb0903f6 --- /dev/null +++ b/src/authz/hooks.ts @@ -0,0 +1,81 @@ +import { useWaffleFlags } from '@src/data/apiHooks'; +import { useUserPermissions } from '@src/authz/data/apiHooks'; +import { PermissionValidationQuery, PermissionValidationAnswer } from '@src/authz/types'; + +/** + * Return type for the useUserPermissionsWithAuthzCourse hook + */ +interface UseUserPermissionsWithAuthzCourseReturn { + /** Whether permissions are currently loading */ + isLoading: boolean; + /** Object containing permission results with boolean values */ + permissions: PermissionValidationAnswer; + /** Whether authorization is enabled for the course */ + isAuthzEnabled: boolean; +} + +/** + * Custom hook to handle user permissions with course authorization waffle flag + * + * This hook abstracts the common pattern of: + * 1. Checking if authz is enabled via waffle flag + * 2. Fetching user permissions when authz is enabled + * 3. Defaulting all permissions to true when authz is disabled + * 4. Providing fallback values for undefined permissions + * + * @param courseId - The course ID to check permissions for + * @param permissions - Object mapping permission names to their action/scope definitions + * @returns Object containing loading state, permissions results, and authz status + * + * @example + * ```tsx + * const { isLoading, permissions, isAuthzEnabled } = useUserPermissionsWithAuthzCourse( + * courseId, + * { + * canViewGradingSettings: { + * action: COURSE_PERMISSIONS.VIEW_GRADING_SETTINGS, + * scope: courseId, + * }, + * canEditGradingSettings: { + * action: COURSE_PERMISSIONS.EDIT_GRADING_SETTINGS, + * scope: courseId, + * }, + * } + * ); + * + * const { canViewGradingSettings, canEditGradingSettings } = permissions; + * ``` + */ +export const useUserPermissionsWithAuthzCourse = ( + courseId: string, + permissions: PermissionValidationQuery, +): UseUserPermissionsWithAuthzCourseReturn => { + const waffleFlags = useWaffleFlags(courseId); + const isAuthzEnabled: boolean = waffleFlags?.enableAuthzCourseAuthoring ?? false; + + const { + isLoading: isLoadingUserPermissions, + data: userPermissions, + } = useUserPermissions(permissions, isAuthzEnabled); + + // Build permission results object + const permissionResults: PermissionValidationAnswer = {}; + + if (isAuthzEnabled && !isLoadingUserPermissions) { + // Authz is enabled and permissions loaded, use actual permission values with fallback to false + Object.keys(permissions).forEach((permissionKey: string) => { + permissionResults[permissionKey] = userPermissions?.[permissionKey] ?? false; + }); + } else if (!isLoadingUserPermissions) { + // Authz is disabled, default all to true + Object.keys(permissions).forEach((permissionKey: string) => { + permissionResults[permissionKey] = true; + }); + } + + return { + isLoading: isAuthzEnabled ? isLoadingUserPermissions : false, + permissions: permissionResults, + isAuthzEnabled, + }; +}; diff --git a/src/authz/permissionHelpers.ts b/src/authz/permissionHelpers.ts new file mode 100644 index 0000000000..76585ea0bf --- /dev/null +++ b/src/authz/permissionHelpers.ts @@ -0,0 +1,12 @@ +import { COURSE_PERMISSIONS } from './constants'; + +export const getGradingPermissions = (courseId: string) => ({ + canViewGradingSettings: { + action: COURSE_PERMISSIONS.VIEW_GRADING_SETTINGS, + scope: courseId, + }, + canEditGradingSettings: { + action: COURSE_PERMISSIONS.EDIT_GRADING_SETTINGS, + scope: courseId, + }, +}); diff --git a/src/grading-settings/GradingSettings.jsx b/src/grading-settings/GradingSettings.jsx index 5fc02a8588..f0b4e8804f 100644 --- a/src/grading-settings/GradingSettings.jsx +++ b/src/grading-settings/GradingSettings.jsx @@ -12,7 +12,10 @@ import { Helmet } from 'react-helmet'; import { useCourseAuthoringContext } from '@src/CourseAuthoringContext'; import { STATEFUL_BUTTON_STATES } from '@src/constants'; import { useCourseSettings } from '@src/data/apiHooks'; +import { useUserPermissionsWithAuthzCourse } from '@src/authz/hooks'; +import { getGradingPermissions } from '@src/authz/permissionHelpers'; import ConnectionErrorAlert from '@src/generic/ConnectionErrorAlert'; +import PermissionDeniedAlert from '@src/generic/PermissionDeniedAlert'; import SectionSubHeader from '@src/generic/section-sub-header'; import SubHeader from '@src/generic/sub-header/SubHeader'; import AlertMessage from '@src/generic/alert-message'; @@ -34,6 +37,12 @@ import messages from './messages'; const GradingSettings = () => { const intl = useIntl(); const { courseId, courseDetails } = useCourseAuthoringContext(); + + const { + isLoading: isLoadingUserPermissions, + permissions: userPermissions, + } = useUserPermissionsWithAuthzCourse(courseId, getGradingPermissions(courseId)); + const { data: gradingSettings, isLoading: isGradingSettingsLoading, @@ -55,7 +64,7 @@ const GradingSettings = () => { const courseGradingDetails = gradingSettings?.courseDetails; const isLoadingDenied = isGradingSettingsError || isCourseSettingsError; const [showSuccessAlert, setShowSuccessAlert] = useState(false); - const isLoading = isCourseSettingsLoading || isGradingSettingsLoading; + const isLoading = isCourseSettingsLoading || isGradingSettingsLoading || isLoadingUserPermissions; const [isQueryPending, setIsQueryPending] = useState(false); const [showOverrideInternetConnectionAlert, setOverrideInternetConnectionAlert] = useState(false); const [eligibleGrade, setEligibleGrade] = useState(null); @@ -93,6 +102,10 @@ const GradingSettings = () => { } }, [savePending]); + if (!isLoadingUserPermissions && !userPermissions.canViewGradingSettings) { + return ; + } + if (isLoadingDenied) { return ( @@ -105,6 +118,8 @@ const GradingSettings = () => { return null; } + const isEditable = !isLoadingUserPermissions && userPermissions.canEditGradingSettings; + const handleQueryProcessing = () => { setShowSuccessAlert(false); updateGradingSettings(gradingData); @@ -177,6 +192,7 @@ const GradingSettings = () => { setOverrideInternetConnectionAlert={setOverrideInternetConnectionAlert} setEligibleGrade={setEligibleGrade} defaultGradeDesignations={gradingSettings?.defaultGradeDesignations} + isEditable={isEditable} /> {courseSettingsData.creditEligibilityEnabled && courseSettingsData.isCreditCourse && ( @@ -191,6 +207,7 @@ const GradingSettings = () => { minimumGradeCredit={minimumGradeCredit} setGradingData={setGradingData} setShowSuccessAlert={setShowSuccessAlert} + isEditable={isEditable} /> )} @@ -204,6 +221,7 @@ const GradingSettings = () => { gracePeriod={gracePeriod} setGradingData={setGradingData} setShowSuccessAlert={setShowSuccessAlert} + isEditable={isEditable} />
@@ -222,11 +240,13 @@ const GradingSettings = () => { setGradingData={setGradingData} courseAssignmentLists={courseAssignmentLists} setShowSuccessAlert={setShowSuccessAlert} + isEditable={isEditable} /> @@ -270,6 +290,7 @@ const GradingSettings = () => { key="statefulBtn" onClick={handleSendGradingSettingsData} state={isQueryPending ? STATEFUL_BUTTON_STATES.pending : STATEFUL_BUTTON_STATES.default} + disabled={!isEditable} {...updateValuesButtonState} />, ].filter(Boolean)} diff --git a/src/grading-settings/assignment-section/assignments/AssignmentItem.jsx b/src/grading-settings/assignment-section/assignments/AssignmentItem.jsx index 209877f62d..68ec326e64 100644 --- a/src/grading-settings/assignment-section/assignments/AssignmentItem.jsx +++ b/src/grading-settings/assignment-section/assignments/AssignmentItem.jsx @@ -20,6 +20,7 @@ const AssignmentItem = ({ secondErrorMsg, gradeField, trailingElement, + disabled, }) => (
  • {descriptions} @@ -65,6 +67,7 @@ AssignmentItem.defaultProps = { errorEffort: false, gradeField: undefined, trailingElement: undefined, + disabled: false, }; AssignmentItem.propTypes = { @@ -82,6 +85,7 @@ AssignmentItem.propTypes = { value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), gradeField: PropTypes.shape(defaultAssignmentsPropTypes), trailingElement: PropTypes.string, + disabled: PropTypes.bool, }; export default AssignmentItem; diff --git a/src/grading-settings/assignment-section/assignments/AssignmentTypeName.jsx b/src/grading-settings/assignment-section/assignments/AssignmentTypeName.jsx index 056fc489bc..441ebf4a0c 100644 --- a/src/grading-settings/assignment-section/assignments/AssignmentTypeName.jsx +++ b/src/grading-settings/assignment-section/assignments/AssignmentTypeName.jsx @@ -11,6 +11,7 @@ const AssignmentTypeName = ({ value, errorEffort, onChange, + disabled, }) => { const intl = useIntl(); const initialAssignmentName = useRef(value); @@ -32,6 +33,7 @@ const AssignmentTypeName = ({ onChange={onChange} value={value} isInvalid={Boolean(errorEffort)} + disabled={disabled} /> {intl.formatMessage(messages.assignmentTypeNameDescription)} @@ -61,12 +63,14 @@ const AssignmentTypeName = ({ AssignmentTypeName.defaultProps = { errorEffort: false, + disabled: false, }; AssignmentTypeName.propTypes = { onChange: PropTypes.func.isRequired, errorEffort: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), value: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]).isRequired, + disabled: PropTypes.bool, }; export default AssignmentTypeName; diff --git a/src/grading-settings/assignment-section/index.jsx b/src/grading-settings/assignment-section/index.jsx index 487e8fc37a..7599974c1a 100644 --- a/src/grading-settings/assignment-section/index.jsx +++ b/src/grading-settings/assignment-section/index.jsx @@ -22,6 +22,7 @@ const AssignmentSection = ({ setGradingData, courseAssignmentLists, setShowSuccessAlert, + isEditable, }) => { const intl = useIntl(); const [errorList, setErrorList] = useState({}); @@ -87,6 +88,7 @@ const AssignmentSection = ({ value={gradeField.type} errorEffort={errorList[`${type}-${gradeField.id}`]} onChange={(e) => handleAssignmentChange(e, gradeField.id)} + disabled={!isEditable} /> handleAssignmentChange(e, gradeField.id)} + disabled={!isEditable} /> handleAssignmentChange(e, gradeField.id)} errorEffort={errorList[`${weight}-${gradeField.id}`]} trailingElement="%" + disabled={!isEditable} /> handleAssignmentChange(e, gradeField.id)} errorEffort={errorList[`${minCount}-${gradeField.id}`]} + disabled={!isEditable} /> {showDefinedCaseAlert && ( @@ -187,6 +193,7 @@ const AssignmentSection = ({ variant="outline-primary" size="sm" onClick={() => handleRemoveAssignment(gradeField.id)} + disabled={!isEditable} > {intl.formatMessage(messages.assignmentDeleteButton)} @@ -200,6 +207,7 @@ const AssignmentSection = ({ AssignmentSection.defaultProps = { courseAssignmentLists: undefined, graders: undefined, + isEditable: true, }; AssignmentSection.propTypes = { @@ -211,6 +219,7 @@ AssignmentSection.propTypes = { graders: PropTypes.arrayOf( PropTypes.shape(defaultAssignmentsPropTypes), ), + isEditable: PropTypes.bool, }; export default AssignmentSection; diff --git a/src/grading-settings/credit-section/index.jsx b/src/grading-settings/credit-section/index.jsx index 3b706849d2..4b9c204d4e 100644 --- a/src/grading-settings/credit-section/index.jsx +++ b/src/grading-settings/credit-section/index.jsx @@ -12,6 +12,7 @@ const CreditSection = ({ minimumGradeCredit, setGradingData, setShowSuccessAlert, + isEditable, }) => { const intl = useIntl(); const [errorEffort, setErrorEffort] = useState(false); @@ -51,6 +52,7 @@ const CreditSection = ({ value={Math.round(parseFloat(minimumGradeCredit) * 100) || ''} name="minimum_grade_credit" onChange={handleCreditChange} + disabled={!isEditable} /> {intl.formatMessage(messages.creditEligibilityDescription)} @@ -64,12 +66,17 @@ const CreditSection = ({ ); }; +CreditSection.defaultProps = { + isEditable: true, +}; + CreditSection.propTypes = { eligibleGrade: PropTypes.number.isRequired, setShowSavePrompt: PropTypes.func.isRequired, setGradingData: PropTypes.func.isRequired, setShowSuccessAlert: PropTypes.func.isRequired, minimumGradeCredit: PropTypes.number.isRequired, + isEditable: PropTypes.bool, }; export default CreditSection; diff --git a/src/grading-settings/deadline-section/index.jsx b/src/grading-settings/deadline-section/index.jsx index 60d95842ac..85ed398d61 100644 --- a/src/grading-settings/deadline-section/index.jsx +++ b/src/grading-settings/deadline-section/index.jsx @@ -13,6 +13,7 @@ const DeadlineSection = ({ gracePeriod, setGradingData, setShowSuccessAlert, + isEditable, }) => { const intl = useIntl(); const timeStampValue = gracePeriod @@ -57,6 +58,7 @@ const DeadlineSection = ({ value={newDeadlineValue} onChange={handleDeadlineChange} placeholder={TIME_FORMAT.toUpperCase()} + disabled={!isEditable} /> {intl.formatMessage(messages.gracePeriodOnDeadlineDescription)} @@ -72,6 +74,7 @@ const DeadlineSection = ({ DeadlineSection.defaultProps = { gracePeriod: null, + isEditable: true, }; DeadlineSection.propTypes = { @@ -82,6 +85,7 @@ DeadlineSection.propTypes = { hours: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), minutes: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), }), + isEditable: PropTypes.bool, }; export default DeadlineSection; diff --git a/src/grading-settings/grading-scale/GradingScale.jsx b/src/grading-settings/grading-scale/GradingScale.jsx index 0ae0f0aafa..72444a477d 100644 --- a/src/grading-settings/grading-scale/GradingScale.jsx +++ b/src/grading-settings/grading-scale/GradingScale.jsx @@ -23,6 +23,7 @@ const GradingScale = ({ setOverrideInternetConnectionAlert, setEligibleGrade, defaultGradeDesignations, + isEditable, }) => { const intl = useIntl(); const [gradingSegments, setGradingSegments] = useState(sortedGrades); @@ -207,7 +208,7 @@ const GradingScale = ({ = (defaultGradeDesignations.length + 1)} + disabled={!isEditable || gradingSegments.length >= (defaultGradeDesignations.length + 1)} data-testid="grading-scale-btn-add-segment" className="mr-3" src={IconAdd} @@ -229,6 +230,7 @@ const GradingScale = ({ idx={idx} handleLetterChange={handleLetterChange} letters={letters} + isEditable={isEditable} /> ))} {handles.map(({ value, getHandleProps }, idx) => ( @@ -238,6 +240,7 @@ const GradingScale = ({ gradingSegments={gradingSegments} value={value} idx={idx} + isEditable={isEditable} /> ))} @@ -261,10 +264,12 @@ GradingScale.propTypes = { ).isRequired, setEligibleGrade: PropTypes.func.isRequired, defaultGradeDesignations: PropTypes.arrayOf(PropTypes.string), + isEditable: PropTypes.bool, }; GradingScale.defaultProps = { defaultGradeDesignations: DEFAULT_GRADE_LETTERS, + isEditable: true, }; export default GradingScale; diff --git a/src/grading-settings/grading-scale/components/GradingScaleHandle.jsx b/src/grading-settings/grading-scale/components/GradingScaleHandle.jsx index c24879d9a1..b4b8e0b309 100644 --- a/src/grading-settings/grading-scale/components/GradingScaleHandle.jsx +++ b/src/grading-settings/grading-scale/components/GradingScaleHandle.jsx @@ -8,14 +8,15 @@ const GradingScaleHandle = ({ value, gradingSegments, getHandleProps, + isEditable, }) => ( From 90aee6af133c1a3e65ffa117e1444840eec68600 Mon Sep 17 00:00:00 2001 From: Brayan Ceron Date: Wed, 8 Apr 2026 17:24:08 -0500 Subject: [PATCH 2/6] fix: use default params instead of defaultProps for new props, add permission tests --- src/grading-settings/GradingSettings.test.jsx | 83 +++++++++++++++++++ .../assignments/AssignmentItem.jsx | 3 +- .../assignments/AssignmentTypeName.jsx | 3 +- .../assignment-section/index.jsx | 3 +- src/grading-settings/credit-section/index.jsx | 6 +- .../deadline-section/index.jsx | 3 +- .../grading-scale/GradingScale.jsx | 3 +- .../components/GradingScaleHandle.jsx | 6 +- 8 files changed, 90 insertions(+), 20 deletions(-) diff --git a/src/grading-settings/GradingSettings.test.jsx b/src/grading-settings/GradingSettings.test.jsx index c9ebc64ad0..96eaa5c5ff 100644 --- a/src/grading-settings/GradingSettings.test.jsx +++ b/src/grading-settings/GradingSettings.test.jsx @@ -7,6 +7,8 @@ import { } from '@src/testUtils'; import { CourseAuthoringProvider } from '@src/CourseAuthoringContext'; import { getCourseSettingsApiUrl } from '@src/data/api'; +import { mockWaffleFlags } from '@src/data/apiHooks.mock'; +import { useUserPermissionsWithAuthzCourse } from '@src/authz/hooks'; import gradingSettings from './__mocks__/gradingSettings'; import { getGradingSettingsApiUrl } from './data/api'; @@ -14,6 +16,16 @@ import * as apiHooks from './data/apiHooks'; import GradingSettings from './GradingSettings'; import messages from './messages'; +jest.mock('@src/authz/hooks', () => ({ + useUserPermissionsWithAuthzCourse: jest.fn().mockReturnValue({ + isLoading: false, + permissions: { + canViewGradingSettings: true, + canEditGradingSettings: true, + }, + }), +})); + const courseId = '123'; let axiosMock; @@ -129,3 +141,74 @@ describe('', () => { expect(screen.getByTestId('connectionErrorAlert')).toBeInTheDocument(); }); }); + +describe(' permissions', () => { + const setupMocks = () => { + const mocks = initializeMocks(); + Object.defineProperty(window, 'scrollTo', { value: jest.fn(), writable: true }); + const { axiosMock: mock } = mocks; + mock.onGet(getGradingSettingsApiUrl(courseId)).reply(200, gradingSettings); + mock.onPost(getGradingSettingsApiUrl(courseId)).reply(200, {}); + mock.onGet(getCourseSettingsApiUrl(courseId)).reply(200, {}); + return mock; + }; + + beforeEach(() => { + jest.mocked(useUserPermissionsWithAuthzCourse).mockReturnValue({ + isLoading: false, + permissions: { canViewGradingSettings: true, canEditGradingSettings: true }, + }); + }); + + it('should render normally when authz flag is disabled (no regression)', async () => { + mockWaffleFlags({ enableAuthzCourseAuthoring: false }); + setupMocks(); + render(); + expect(await screen.findAllByText(messages.headingTitle.defaultMessage)).not.toHaveLength(0); + }); + + it('should render normally when user has view and edit permissions', async () => { + mockWaffleFlags({ enableAuthzCourseAuthoring: true }); + setupMocks(); + render(); + expect(await screen.findAllByText(messages.headingTitle.defaultMessage)).not.toHaveLength(0); + }); + + it('should show permission denied alert when user lacks view permission', async () => { + mockWaffleFlags({ enableAuthzCourseAuthoring: true }); + jest.mocked(useUserPermissionsWithAuthzCourse).mockReturnValue({ + isLoading: false, + permissions: { canViewGradingSettings: false, canEditGradingSettings: false }, + }); + setupMocks(); + render(); + expect(await screen.findByTestId('permissionDeniedAlert')).toBeInTheDocument(); + }); + + it('should disable inputs when user has view but not edit permission', async () => { + mockWaffleFlags({ enableAuthzCourseAuthoring: true }); + jest.mocked(useUserPermissionsWithAuthzCourse).mockReturnValue({ + isLoading: false, + permissions: { canViewGradingSettings: true, canEditGradingSettings: false }, + }); + setupMocks(); + render(); + const segmentInputs = await screen.findAllByTestId('grading-scale-segment-input'); + segmentInputs.forEach((input) => expect(input).toBeDisabled()); + }); + + it('should disable save button when user lacks edit permission', async () => { + mockWaffleFlags({ enableAuthzCourseAuthoring: true }); + jest.mocked(useUserPermissionsWithAuthzCourse).mockReturnValue({ + isLoading: false, + permissions: { canViewGradingSettings: true, canEditGradingSettings: false }, + }); + setupMocks(); + render(); + const segmentInputs = await screen.findAllByTestId('grading-scale-segment-input'); + // Trigger a change to show the save alert + fireEvent.change(segmentInputs[1], { target: { value: 'Test' } }); + const saveBtn = screen.getByTestId('grading-settings-save-alert').querySelector('button[type="button"]:last-child'); + expect(saveBtn).toBeDisabled(); + }); +}); diff --git a/src/grading-settings/assignment-section/assignments/AssignmentItem.jsx b/src/grading-settings/assignment-section/assignments/AssignmentItem.jsx index 68ec326e64..22906e159c 100644 --- a/src/grading-settings/assignment-section/assignments/AssignmentItem.jsx +++ b/src/grading-settings/assignment-section/assignments/AssignmentItem.jsx @@ -20,7 +20,7 @@ const AssignmentItem = ({ secondErrorMsg, gradeField, trailingElement, - disabled, + disabled = false, }) => (
  • { const intl = useIntl(); const initialAssignmentName = useRef(value); @@ -63,7 +63,6 @@ const AssignmentTypeName = ({ AssignmentTypeName.defaultProps = { errorEffort: false, - disabled: false, }; AssignmentTypeName.propTypes = { diff --git a/src/grading-settings/assignment-section/index.jsx b/src/grading-settings/assignment-section/index.jsx index 7599974c1a..84ebb15e20 100644 --- a/src/grading-settings/assignment-section/index.jsx +++ b/src/grading-settings/assignment-section/index.jsx @@ -22,7 +22,7 @@ const AssignmentSection = ({ setGradingData, courseAssignmentLists, setShowSuccessAlert, - isEditable, + isEditable = true, }) => { const intl = useIntl(); const [errorList, setErrorList] = useState({}); @@ -207,7 +207,6 @@ const AssignmentSection = ({ AssignmentSection.defaultProps = { courseAssignmentLists: undefined, graders: undefined, - isEditable: true, }; AssignmentSection.propTypes = { diff --git a/src/grading-settings/credit-section/index.jsx b/src/grading-settings/credit-section/index.jsx index 4b9c204d4e..68de0c3ac0 100644 --- a/src/grading-settings/credit-section/index.jsx +++ b/src/grading-settings/credit-section/index.jsx @@ -12,7 +12,7 @@ const CreditSection = ({ minimumGradeCredit, setGradingData, setShowSuccessAlert, - isEditable, + isEditable = true, }) => { const intl = useIntl(); const [errorEffort, setErrorEffort] = useState(false); @@ -66,10 +66,6 @@ const CreditSection = ({ ); }; -CreditSection.defaultProps = { - isEditable: true, -}; - CreditSection.propTypes = { eligibleGrade: PropTypes.number.isRequired, setShowSavePrompt: PropTypes.func.isRequired, diff --git a/src/grading-settings/deadline-section/index.jsx b/src/grading-settings/deadline-section/index.jsx index 85ed398d61..450fe33396 100644 --- a/src/grading-settings/deadline-section/index.jsx +++ b/src/grading-settings/deadline-section/index.jsx @@ -13,7 +13,7 @@ const DeadlineSection = ({ gracePeriod, setGradingData, setShowSuccessAlert, - isEditable, + isEditable = true, }) => { const intl = useIntl(); const timeStampValue = gracePeriod @@ -74,7 +74,6 @@ const DeadlineSection = ({ DeadlineSection.defaultProps = { gracePeriod: null, - isEditable: true, }; DeadlineSection.propTypes = { diff --git a/src/grading-settings/grading-scale/GradingScale.jsx b/src/grading-settings/grading-scale/GradingScale.jsx index 72444a477d..3d45e04ffa 100644 --- a/src/grading-settings/grading-scale/GradingScale.jsx +++ b/src/grading-settings/grading-scale/GradingScale.jsx @@ -23,7 +23,7 @@ const GradingScale = ({ setOverrideInternetConnectionAlert, setEligibleGrade, defaultGradeDesignations, - isEditable, + isEditable = true, }) => { const intl = useIntl(); const [gradingSegments, setGradingSegments] = useState(sortedGrades); @@ -269,7 +269,6 @@ GradingScale.propTypes = { GradingScale.defaultProps = { defaultGradeDesignations: DEFAULT_GRADE_LETTERS, - isEditable: true, }; export default GradingScale; diff --git a/src/grading-settings/grading-scale/components/GradingScaleHandle.jsx b/src/grading-settings/grading-scale/components/GradingScaleHandle.jsx index b4b8e0b309..fa012a8c7b 100644 --- a/src/grading-settings/grading-scale/components/GradingScaleHandle.jsx +++ b/src/grading-settings/grading-scale/components/GradingScaleHandle.jsx @@ -8,7 +8,7 @@ const GradingScaleHandle = ({ value, gradingSegments, getHandleProps, - isEditable, + isEditable = true, }) => (