diff --git a/.oxlintrc.json b/.oxlintrc.json
index 7aa3afaeef..7aa558b4d9 100644
--- a/.oxlintrc.json
+++ b/.oxlintrc.json
@@ -3,9 +3,17 @@
"categories": {
"correctness": "warn"
},
+ "plugins": ["react"],
"rules": {
- "eslint/no-unused-vars": "off",
- "typescript/unbound-method": "off", // 🛑 TEMPORARY
+ "eslint/no-unused-vars": ["warn", {
+ // Allow using {ignoredProp, ...keepTheRest} to omit a prop like 'ignoredProp' from an object.
+ "ignoreRestSiblings": true,
+ }],
+ // We disable exhaustive-deps because: it's noisy, and we often include extra deps when we want a memoized thing to
+ // re-calculate after some change, even if we're not using that thing in the calculation.
+ "react-hooks/exhaustive-deps": "off",
+ // Rule of hooks is useful, but not on by default:
+ "react/rules-of-hooks": "warn",
"typescript/no-floating-promises": ["error", {
"allowForKnownSafeCalls": [
// queryClient.invalidateQueries returns a promise that can be awaited
diff --git a/plugins/course-apps/teams/GroupEditor.jsx b/plugins/course-apps/teams/GroupEditor.jsx
index b43348b51b..5d4d457b5c 100644
--- a/plugins/course-apps/teams/GroupEditor.jsx
+++ b/plugins/course-apps/teams/GroupEditor.jsx
@@ -48,7 +48,7 @@ const GroupEditor = ({
? (
{intl.formatMessage(messages.groupDeleteHeading)}
- {intl.formatMessage(messages.groupDeleteBody).split('\n').map(text =>
{text}
)}
+ {intl.formatMessage(messages.groupDeleteBody).split('\n').map(text =>
{text}
)}
{intl.formatMessage(messages.cancel)}
diff --git a/src/advanced-settings/AdvancedSettings.tsx b/src/advanced-settings/AdvancedSettings.tsx
index c02b3ecd50..6c042ea0d2 100644
--- a/src/advanced-settings/AdvancedSettings.tsx
+++ b/src/advanced-settings/AdvancedSettings.tsx
@@ -282,7 +282,7 @@ const AdvancedSettings = () => {
role="dialog"
actions={[
!isQueryPending ? (
-
+
{intl.formatMessage(messages.buttonCancelText)}
) : /* istanbul ignore next */ null,
diff --git a/src/course-libraries/OutOfSyncAlert.tsx b/src/course-libraries/OutOfSyncAlert.tsx
index 5a365a2737..fe61e345d2 100644
--- a/src/course-libraries/OutOfSyncAlert.tsx
+++ b/src/course-libraries/OutOfSyncAlert.tsx
@@ -67,9 +67,7 @@ export const OutOfSyncAlert: React.FC = ({
variant="info"
onClose={dismissAlert}
actions={[
-
+
{intl.formatMessage(messages.outOfSyncCountAlertReviewBtn)}
,
]}
diff --git a/src/course-outline/CourseOutline.tsx b/src/course-outline/CourseOutline.tsx
index b9fb53a692..c67a5671e1 100644
--- a/src/course-outline/CourseOutline.tsx
+++ b/src/course-outline/CourseOutline.tsx
@@ -28,7 +28,6 @@ import { UnlinkModal } from '@src/generic/unlink-modal';
import AlertMessage from '@src/generic/alert-message';
import getPageHeadTitle from '@src/generic/utils';
import CourseOutlineHeaderActionsSlot from '@src/plugin-slots/CourseOutlineHeaderActionsSlot';
-import { XBlock } from '@src/data/types';
import { useCourseAuthoringContext } from '@src/CourseAuthoringContext';
import { useCourseOutlineContext } from './CourseOutlineContext';
import LegacyLibContentBlockAlert from '@src/course-libraries/LegacyLibContentBlockAlert';
@@ -72,8 +71,6 @@ const CourseOutline = () => {
closeUnlinkModal,
} = useCourseAuthoringContext();
const {
- handleAddBlock,
- handleAddAndOpenUnit,
currentSelection,
sections,
restoreSectionList,
diff --git a/src/course-outline/CourseOutlineContext.tsx b/src/course-outline/CourseOutlineContext.tsx
index 65098cf5fb..38c8c4824f 100644
--- a/src/course-outline/CourseOutlineContext.tsx
+++ b/src/course-outline/CourseOutlineContext.tsx
@@ -5,7 +5,7 @@ import { useDispatch, useSelector } from 'react-redux';
import { useToggle } from '@openedx/paragon';
import { arrayMove } from '@dnd-kit/sortable';
-import { SelectionState, type UnitXBlock, type XBlock } from '@src/data/types';
+import { SelectionState, type XBlock } from '@src/data/types';
import { useToggleWithValue } from '@src/hooks';
import { getBlockType } from '@src/generic/key-utils';
import { COURSE_BLOCK_NAMES } from '@src/constants';
diff --git a/src/course-outline/hooks.jsx b/src/course-outline/hooks.jsx
index 2804b9b1b3..ab2d1e07c6 100644
--- a/src/course-outline/hooks.jsx
+++ b/src/course-outline/hooks.jsx
@@ -38,7 +38,6 @@ import {
enableCourseHighlightsEmailsQuery,
fetchCourseBestPracticesQuery,
fetchCourseLaunchQuery,
- fetchCourseOutlineIndexQuery,
fetchCourseReindexQuery,
setVideoSharingOptionQuery,
dismissNotificationQuery,
@@ -52,7 +51,6 @@ const useCourseOutline = ({ courseId }) => {
handleAddBlock,
setCurrentSelection,
currentSelection,
- isDuplicatingItem,
isDeleteModalOpen,
openDeleteModal,
closeDeleteModal,
diff --git a/src/course-outline/page-alerts/PageAlerts.jsx b/src/course-outline/page-alerts/PageAlerts.jsx
index dad989010e..b949fe2e36 100644
--- a/src/course-outline/page-alerts/PageAlerts.jsx
+++ b/src/course-outline/page-alerts/PageAlerts.jsx
@@ -104,6 +104,7 @@ const PageAlerts = ({
onClose={onDismiss}
actions={[
@@ -267,6 +268,7 @@ const PageAlerts = ({
onClose={onDismiss}
actions={[
@@ -329,6 +331,7 @@ const PageAlerts = ({
onClose={onDismiss}
actions={[
diff --git a/src/course-unit/CourseUnit.tsx b/src/course-unit/CourseUnit.tsx
index cce4e5caa8..8cfeff516f 100644
--- a/src/course-unit/CourseUnit.tsx
+++ b/src/course-unit/CourseUnit.tsx
@@ -273,14 +273,12 @@ const CourseUnit = () => {
{courseUnit.upstreamInfo?.upstreamLink && (
- {intl.formatMessage(messages.alertLibraryUnitReadOnlyLinkText)}
+
+
),
},
diff --git a/src/course-unit/clipboard/paste-notification/index.jsx b/src/course-unit/clipboard/paste-notification/index.jsx
index 20eed888df..53f437a185 100644
--- a/src/course-unit/clipboard/paste-notification/index.jsx
+++ b/src/course-unit/clipboard/paste-notification/index.jsx
@@ -47,6 +47,7 @@ const PastNotificationAlert = ({ staticFileNotices, courseId }) => {
icon={WarningIcon}
dismissible
actions={[
+ // oxlint-disable-next-line react/jsx-key (Paragon adds its own key for action buttons)
{
icon={InfoIcon}
dismissible
actions={[
+ // oxlint-disable-next-line react/jsx-key (Paragon adds its own key for action buttons)
{
{templatesByType.advanced?.templates.map((advancedTypeObj) => (
handleSelection('advanced', advancedTypeObj.category)}
@@ -223,6 +224,7 @@ const AddNewContent = () => {
{blockTypes.map((blockTypeObj) => (
handleSelection(blockTypeObj.blockType)}
onClickTemplate={(boilerplateName: string) => handleSelection(blockTypeObj.blockType, boilerplateName)}
diff --git a/src/course-updates/CourseUpdates.tsx b/src/course-updates/CourseUpdates.tsx
index b081186876..fa2b88b577 100644
--- a/src/course-updates/CourseUpdates.tsx
+++ b/src/course-updates/CourseUpdates.tsx
@@ -166,6 +166,7 @@ const CourseUpdates = () => {
{courseUpdates.map((courseUpdate, index) => (
isInnerFormOpen(courseUpdate.id) ? (
{
/>
) : (
handleOpenUpdateForm(REQUEST_TYPES.edit_update, courseUpdate)}
diff --git a/src/editors/EditorContainer.tsx b/src/editors/EditorContainer.tsx
index 0df4c816fa..6b7cd9af56 100644
--- a/src/editors/EditorContainer.tsx
+++ b/src/editors/EditorContainer.tsx
@@ -63,6 +63,7 @@ const EditorContainer: React.FC = ({
description={intl.formatMessage(messages.libraryBlockEditWarningDescription)}
actions={[
(
= 0}
diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseSelector.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseSelector.jsx
index 7e21f5df30..a8727554da 100644
--- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseSelector.jsx
+++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseSelector.jsx
@@ -42,9 +42,9 @@ const LicenseSelector = ({
onChange={(e) => onLicenseChange(e.target.value)}
>
{Object.entries(LicenseNames).map(([key, text]) => {
- if (license === key) { return ({text} ); }
- if (key === LicenseTypes.select) { return ({text} ); }
- return ({text} );
+ if (license === key) { return ({text} ); }
+ if (key === LicenseTypes.select) { return ({text} ); }
+ return ({text} );
})}
{level !== LicenseLevel.course ? (
diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.jsx
index ad441c562d..058a74a445 100644
--- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.jsx
+++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.jsx
@@ -9,7 +9,7 @@ import {
} from '@openedx/paragon';
import { Check } from '@openedx/paragon/icons';
-import { connect, useDispatch } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
import { useIntl } from '@edx/frontend-platform/i18n';
import { thunkActions, selectors } from '../../../../../../data/redux';
import { videoTranscriptLanguages } from '../../../../../../data/constants/video';
@@ -47,9 +47,8 @@ export const hooks = {
const LanguageSelector = ({
index, // For a unique id for the form control
language,
- // Redux
- openLanguages, // Only allow those languages not already associated with a transcript to be selected
}) => {
+ const openLanguages = useSelector(selectors.video.openLanguages);
const intl = useIntl();
const [localLang, setLocalLang] = React.useState(language);
const input = fileInput({ onAddFile: hooks.addFileCallback({ dispatch: useDispatch(), localLang }) });
@@ -95,12 +94,14 @@ const LanguageSelector = ({
{Object.entries(videoTranscriptLanguages).map(([lang, text]) => {
if (language === lang) {
- return ({text} );
+ return {text} ;
}
if (openLanguages.some(row => row.includes(lang))) {
- return ( onLanguageChange({ newLang: lang })}>{text} );
+ return (
+ onLanguageChange({ newLang: lang })}>{text}
+ );
}
- return ({text} );
+ return ({text} );
})}
@@ -109,21 +110,9 @@ const LanguageSelector = ({
);
};
-LanguageSelector.defaultProps = {
- openLanguages: [],
-};
-
LanguageSelector.propTypes = {
- openLanguages: PropTypes.arrayOf(PropTypes.string),
index: PropTypes.number.isRequired,
language: PropTypes.string.isRequired,
};
-export const mapStateToProps = (state) => ({
- openLanguages: selectors.video.openLanguages(state),
-});
-
-export const mapDispatchToProps = {};
-
-export const LanguageSelectorInternal = LanguageSelector; // For testing only
-export default connect(mapStateToProps, mapDispatchToProps)(LanguageSelector);
+export default LanguageSelector;
diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.test.jsx
index 3fc95017b7..360455f737 100644
--- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.test.jsx
+++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.test.jsx
@@ -2,8 +2,9 @@ import React from 'react';
import {
render, screen, initializeMocks, fireEvent,
} from '@src/testUtils';
+import { useDispatch } from 'react-redux';
import LanguageSelector from './LanguageSelector';
-import { selectors } from '../../../../../../data/redux';
+import { thunkActions, selectors } from '../../../../../../data/redux';
const lang1 = 'kLinGon';
const lang1Code = 'kl';
@@ -12,6 +13,14 @@ const lang2Code = 'el';
const lang3 = 'sImLisH';
const lang3Code = 'sl';
+jest.mock('react-redux', () => {
+ const dispatchFn = jest.fn().mockName('mockDispatch');
+ return {
+ ...jest.requireActual('react-redux'),
+ useDispatch: jest.fn(() => dispatchFn),
+ };
+});
+
jest.mock('../../../../../../data/constants/video', () => ({
videoTranscriptLanguages: {
[lang1Code]: lang1,
@@ -20,6 +29,20 @@ jest.mock('../../../../../../data/constants/video', () => ({
},
}));
+jest.mock('../../../../../../data/redux', () => ({
+ thunkActions: {
+ video: {
+ updateTranscriptLanguage: jest.fn((args) => ({ updateTranscriptLanguage: args })).mockName('thunkActions.video.updateTranscriptLanguage'),
+ uploadTranscript: jest.fn().mockName('thunkActions.video.uploadTranscript'),
+ },
+ },
+ selectors: {
+ video: {
+ openLanguages: jest.fn(),
+ },
+ },
+}));
+
describe('LanguageSelector', () => {
const props = {
onSelect: jest.fn().mockName('props.OnSelect'),
@@ -55,4 +78,19 @@ describe('LanguageSelector', () => {
const disabledItems = container.querySelectorAll('.disabled.dropdown-item');
expect(disabledItems.length).toBe(3);
});
+
+ test('clicking an open language item dispatches updateTranscriptLanguage', () => {
+ const mockDispatch = jest.fn();
+ useDispatch.mockReturnValue(mockDispatch);
+ const { video } = selectors;
+ jest.spyOn(video, 'openLanguages').mockReturnValue([[lang2Code, lang2], [lang3Code, lang3]]);
+ render( );
+ fireEvent.click(screen.getByRole('button', { name: 'Languages' }));
+ fireEvent.click(screen.getByText(lang2));
+ expect(thunkActions.video.updateTranscriptLanguage).toHaveBeenCalledWith({
+ newLanguageCode: lang2Code,
+ languageBeforeChange: lang1Code,
+ });
+ expect(mockDispatch).toHaveBeenCalled();
+ });
});
diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/index.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/index.jsx
index c3226115e5..cf18123e61 100644
--- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/index.jsx
+++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/index.jsx
@@ -147,6 +147,7 @@ const TranscriptWidget = ({
{transcripts.map((language, index) => (
- {Object.keys(duplicateFiles).map(file => {file} )}
+ {Object.keys(duplicateFiles).map(file => {file} )}
diff --git a/src/files-and-videos/generic/table-components/sort-and-filter-modal/SortAndFilterModal.jsx b/src/files-and-videos/generic/table-components/sort-and-filter-modal/SortAndFilterModal.jsx
index d878093439..3c3e291cfb 100644
--- a/src/files-and-videos/generic/table-components/sort-and-filter-modal/SortAndFilterModal.jsx
+++ b/src/files-and-videos/generic/table-components/sort-and-filter-modal/SortAndFilterModal.jsx
@@ -147,7 +147,7 @@ const SortAndFilterModal = ({
isInline
>
{filterOptions.map(({ name, value }) => (
- {name}
+ {name}
))}
diff --git a/src/files-and-videos/videos-page/info-sidebar/TranscriptTab.jsx b/src/files-and-videos/videos-page/info-sidebar/TranscriptTab.jsx
index 7392932318..c46fe8a305 100644
--- a/src/files-and-videos/videos-page/info-sidebar/TranscriptTab.jsx
+++ b/src/files-and-videos/videos-page/info-sidebar/TranscriptTab.jsx
@@ -112,8 +112,10 @@ const TranscriptTab = ({
))}
- {previousSelection.map(transcript => (
+ {previousSelection.map((transcript, idx) => (
{
- const intl = useIntl();
const { data, isLoading, isError } = useUserAgreement(agreementType);
const mutation = useUserAgreementRecordUpdater(agreementType);
const showAlert = data && !isLoading && !isError;
@@ -30,8 +29,8 @@ const AlertAgreement = ({ agreementType }: { agreementType: string }) => {
variant="warning"
icon={Policy}
actions={[
- {intl.formatMessage(messages.learnMoreLinkLabel)} ,
- {intl.formatMessage(messages.agreeButtonLabel)} ,
+ ,
+ ,
]}
>
{name}
diff --git a/src/generic/sidebar/BlockCardButton.tsx b/src/generic/sidebar/BlockCardButton.tsx
index 569fdf0ac9..bab788f83d 100644
--- a/src/generic/sidebar/BlockCardButton.tsx
+++ b/src/generic/sidebar/BlockCardButton.tsx
@@ -52,7 +52,7 @@ export const BlockCardButton = ({
>
{templates.map((template) => (
- onClickTemplate?.(template.boilerplateName)}>
+ onClickTemplate?.(template.boilerplateName)} key={template.boilerplateName}>
{template.displayName}
))}
diff --git a/src/generic/sidebar/Sidebar.tsx b/src/generic/sidebar/Sidebar.tsx
index ebfd06aee4..02de034933 100644
--- a/src/generic/sidebar/Sidebar.tsx
+++ b/src/generic/sidebar/Sidebar.tsx
@@ -163,7 +163,7 @@ export function Sidebar({
}
return (
-
+
);
})}
diff --git a/src/grading-settings/GradingSettings.jsx b/src/grading-settings/GradingSettings.jsx
index a359087db4..a1efc34a4f 100644
--- a/src/grading-settings/GradingSettings.jsx
+++ b/src/grading-settings/GradingSettings.jsx
@@ -259,7 +259,7 @@ const GradingSettings = () => {
role="dialog"
actions={[
!isQueryPending && (
-
+
{intl.formatMessage(messages.buttonCancelText)}
),
diff --git a/src/group-configurations/index.jsx b/src/group-configurations/index.tsx
similarity index 91%
rename from src/group-configurations/index.jsx
rename to src/group-configurations/index.tsx
index 88501e16f3..8b069d4047 100644
--- a/src/group-configurations/index.jsx
+++ b/src/group-configurations/index.tsx
@@ -16,6 +16,7 @@ import EnrollmentTrackGroupsSection from './enrollment-track-groups-section';
import GroupConfigurationSidebar from './group-configuration-sidebar';
import { useGroupConfigurations } from './hooks';
import ConnectionErrorAlert from '../generic/ConnectionErrorAlert';
+import { AvailableGroup } from './types';
const GroupConfigurations = () => {
const { formatMessage } = useIntl();
@@ -36,7 +37,7 @@ const GroupConfigurations = () => {
} = useGroupConfigurations(courseId);
document.title = getPageHeadTitle(
- courseDetails?.name,
+ courseDetails?.name ?? '',
formatMessage(messages.headingTitle),
);
@@ -56,13 +57,13 @@ const GroupConfigurations = () => {
);
}
- const enrollmentTrackGroup = shouldShowEnrollmentTrack
+ const enrollmentTrackGroup: AvailableGroup = shouldShowEnrollmentTrack
? allGroupConfigurations.find((group) => group.scheme === 'enrollment_track')
: null;
- const contentGroup = allGroupConfigurations.find((group) => group.scheme === 'cohort');
+ const contentGroup: AvailableGroup[] = allGroupConfigurations.find((group) => group.scheme === 'cohort');
- const teamGroups = allGroupConfigurations.filter((group) => group.scheme === 'team');
+ const teamGroups: AvailableGroup[] = allGroupConfigurations.filter((group) => group.scheme === 'team');
return (
<>
@@ -87,6 +88,7 @@ const GroupConfigurations = () => {
{!!teamGroups && teamGroups.length > 0 && (
teamGroups.map((teamGroup) => (
))
diff --git a/src/library-authoring/LibraryContent.tsx b/src/library-authoring/LibraryContent.tsx
index 416d1a57b3..fa20658e16 100644
--- a/src/library-authoring/LibraryContent.tsx
+++ b/src/library-authoring/LibraryContent.tsx
@@ -110,6 +110,7 @@ const LibraryContent = ({ contentType = ContentType.home }: LibraryContentProps)
})}
{showPlaceholderBlocks && placeholderData?.hits?.map((item) => (
diff --git a/src/library-authoring/history-log/HistoryLog.tsx b/src/library-authoring/history-log/HistoryLog.tsx
deleted file mode 100644
index 54260dc71c..0000000000
--- a/src/library-authoring/history-log/HistoryLog.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Stack } from "@openedx/paragon";
-
-const HistoryLog = () => {
- return (
-
- History
-
- )
-};
diff --git a/src/library-authoring/import-course/ImportDetailsPage.tsx b/src/library-authoring/import-course/ImportDetailsPage.tsx
index 763712cfb3..fdb8d1d1f9 100644
--- a/src/library-authoring/import-course/ImportDetailsPage.tsx
+++ b/src/library-authoring/import-course/ImportDetailsPage.tsx
@@ -208,7 +208,7 @@ const ImportDetailsContent = () => {
});
navigate(`../import/${courseImportDetails.source}/${newMigrationTask.uuid}`);
setDisableReimport(false);
- } catch (error) {
+ } catch {
showToast(intl.formatMessage(messages.importCourseCompleteFailedToastMessage, {
courseName: courseDetails.title,
}));
@@ -234,6 +234,7 @@ const ImportDetailsContent = () => {
stacked
actions={[
navigate(collectionLink())}
@@ -289,6 +290,7 @@ const ImportDetailsContent = () => {
stacked
actions={[
{
stacked
actions={[
navigate(collectionLink())}
diff --git a/src/library-authoring/import-course/stepper/ImportStepperPage.tsx b/src/library-authoring/import-course/stepper/ImportStepperPage.tsx
index cddf341685..79990d0377 100644
--- a/src/library-authoring/import-course/stepper/ImportStepperPage.tsx
+++ b/src/library-authoring/import-course/stepper/ImportStepperPage.tsx
@@ -85,7 +85,7 @@ export const ImportStepperPage = () => {
compositionLevel: 'section',
});
navigate(`../import/${selectedCourseId}/${migrationTask.uuid}`);
- } catch (error) {
+ } catch {
showToast(intl.formatMessage(messages.importCourseCompleteFailedToastMessage, {
courseName: courseData?.title,
}));
diff --git a/src/optimizer-page/scan-results/ScanResults.tsx b/src/optimizer-page/scan-results/ScanResults.tsx
index 2ca8d1e537..068019d86c 100644
--- a/src/optimizer-page/scan-results/ScanResults.tsx
+++ b/src/optimizer-page/scan-results/ScanResults.tsx
@@ -23,7 +23,7 @@ import { useDispatch } from 'react-redux';
import messages from './messages';
import SectionCollapsible from './SectionCollapsible';
import BrokenLinkTable from './BrokenLinkTable';
-import { LinkCheckResult } from '../types';
+import type { LinkCheckResult, Section } from '../types';
import { countBrokenLinks, isDataEmpty } from '../utils';
import FilterModal from './filterModal';
import { useWaffleFlags } from '../../data/apiHooks';
@@ -131,7 +131,7 @@ const ScanResults: FC = ({
}, [data?.courseUpdates, data?.customPages, intl]);
// Combine renderable sections with regular sections
- const allSections = useMemo(
+ const allSections: Section[] = useMemo(
() => [...renderableSections, ...(sections || [])],
[renderableSections, sections],
);
@@ -1087,7 +1087,7 @@ const ScanResults: FC = ({
{section.subsections.map((subsection) => (
<>
{subsection.units.map((unit) => (
-
+
{filterOptions.map(({ name, value }) => (
-
+
{name}
{ value === 'brokenLinks' && }
diff --git a/src/studio-home/tabs-section/courses-tab/index.tsx b/src/studio-home/tabs-section/courses-tab/index.tsx
index 337a0e8d30..310375cd22 100644
--- a/src/studio-home/tabs-section/courses-tab/index.tsx
+++ b/src/studio-home/tabs-section/courses-tab/index.tsx
@@ -88,6 +88,7 @@ const CardList = ({
url,
}) => (
onClickCard?.(courseKey)}
itemId={courseKey}
diff --git a/src/textbooks/textbook-form/TextbookForm.jsx b/src/textbooks/textbook-form/TextbookForm.jsx
index a667654fef..506064bbf9 100644
--- a/src/textbooks/textbook-form/TextbookForm.jsx
+++ b/src/textbooks/textbook-form/TextbookForm.jsx
@@ -81,7 +81,8 @@ const TextbookForm = ({
render={(arrayHelpers) => (
<>
{!!values?.chapters.length && values.chapters.map(({ title, url }, index) => (
-
+ // eslint-disable-next-line react/no-array-index-key
+
{intl.formatMessage(messages.chapterTitleLabel)} *
diff --git a/webpack.dev.config.js b/webpack.dev.config.js
index 34cc439e2a..199e00beb8 100644
--- a/webpack.dev.config.js
+++ b/webpack.dev.config.js
@@ -24,7 +24,7 @@ const config = createConfig('webpack-dev', {
try {
// Try to resolve the real package. If it exists, do nothing.
require.resolve('@edx/frontend-plugin-notifications');
- } catch (e) {
+ } catch {
// Package not found → point to the stub we created.
// eslint-disable-next-line no-param-reassign
resource.request = path.resolve(__dirname, 'src/stubs/empty-notifications-plugin.tsx');
diff --git a/webpack.prod.config.js b/webpack.prod.config.js
index f1fec7d6b8..51faf70aed 100644
--- a/webpack.prod.config.js
+++ b/webpack.prod.config.js
@@ -24,7 +24,7 @@ const config = createConfig('webpack-prod', {
try {
// Try to resolve the real package. If it exists, do nothing.
require.resolve('@edx/frontend-plugin-notifications');
- } catch (e) {
+ } catch {
// Package not found → point to the stub we created.
// eslint-disable-next-line no-param-reassign
resource.request = path.resolve(__dirname, 'src/stubs/empty-notifications-plugin.tsx');