Update React Lint rules for TSX

This commit is contained in:
Mark McDowall 2024-07-30 17:44:39 -07:00 committed by Mark McDowall
parent 4c0de55672
commit 1299a97579
21 changed files with 113 additions and 103 deletions

View File

@ -359,11 +359,16 @@ module.exports = {
],
rules: Object.assign(typescriptEslintRecommended.rules, {
'no-shadow': 'off',
// These should be enabled after cleaning things up
'@typescript-eslint/no-unused-vars': 'warn',
'@typescript-eslint/no-unused-vars': [
'error',
{
args: 'after-used',
argsIgnorePattern: '^_',
ignoreRestSiblings: true
}
],
'@typescript-eslint/explicit-function-return-type': 'off',
'react/prop-types': 'off',
'no-shadow': 'off',
'prettier/prettier': 'error',
'simple-import-sort/imports': [
'error',
@ -376,7 +381,41 @@ module.exports = {
['^@?\\w', `^(${dirs})(/.*|$)`, '^\\.', '^\\..*css$']
]
}
]
],
// React Hooks
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'error',
// React
'react/function-component-definition': 'error',
'react/hook-use-state': 'error',
'react/jsx-boolean-value': ['error', 'always'],
'react/jsx-curly-brace-presence': [
'error',
{ props: 'never', children: 'never' }
],
'react/jsx-fragments': 'error',
'react/jsx-handler-names': [
'error',
{
eventHandlerPrefix: 'on',
eventHandlerPropPrefix: 'on'
}
],
'react/jsx-no-bind': ['error', { ignoreRefs: true }],
'react/jsx-no-useless-fragment': ['error', { allowExpressions: true }],
'react/jsx-pascal-case': ['error', { allowAllCaps: true }],
'react/jsx-sort-props': [
'error',
{
callbacksLast: true,
noSortAlphabetically: true,
reservedFirst: true
}
],
'react/prop-types': 'off',
'react/self-closing-comp': 'error'
})
},
{

View File

@ -1,4 +1,4 @@
import React, { Fragment, useCallback } from 'react';
import React, { useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import AppState from 'App/State/AppState';
import FormGroup from 'Components/Form/FormGroup';
@ -31,19 +31,17 @@ function QueueOptions() {
);
return (
<Fragment>
<FormGroup>
<FormLabel>{translate('ShowUnknownSeriesItems')}</FormLabel>
<FormGroup>
<FormLabel>{translate('ShowUnknownSeriesItems')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="includeUnknownSeriesItems"
value={includeUnknownSeriesItems}
helpText={translate('ShowUnknownSeriesItemsHelpText')}
onChange={handleOptionChange}
/>
</FormGroup>
</Fragment>
<FormInputGroup
type={inputTypes.CHECK}
name="includeUnknownSeriesItems"
value={includeUnknownSeriesItems}
helpText={translate('ShowUnknownSeriesItemsHelpText')}
onChange={handleOptionChange}
/>
</FormGroup>
);
}

View File

@ -35,6 +35,10 @@ import getPathWithUrlBase from 'Utilities/getPathWithUrlBase';
import CutoffUnmetConnector from 'Wanted/CutoffUnmet/CutoffUnmetConnector';
import MissingConnector from 'Wanted/Missing/MissingConnector';
function RedirectWithUrlBase() {
return <Redirect to={getPathWithUrlBase('/')} />;
}
function AppRoutes() {
return (
<Switch>
@ -51,9 +55,7 @@ function AppRoutes() {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
addUrlBase={false}
render={() => {
return <Redirect to={getPathWithUrlBase('/')} />;
}}
render={RedirectWithUrlBase}
/>
)}
@ -61,21 +63,9 @@ function AppRoutes() {
<Route path="/add/import" component={ImportSeries} />
<Route
path="/serieseditor"
exact={true}
render={() => {
return <Redirect to={getPathWithUrlBase('/')} />;
}}
/>
<Route path="/serieseditor" exact={true} render={RedirectWithUrlBase} />
<Route
path="/seasonpass"
exact={true}
render={() => {
return <Redirect to={getPathWithUrlBase('/')} />;
}}
/>
<Route path="/seasonpass" exact={true} render={RedirectWithUrlBase} />
<Route path="/series/:titleSlug" component={SeriesDetailsPageConnector} />

View File

@ -64,7 +64,7 @@ function ErrorBoundaryError(props: ErrorBoundaryErrorProps) {
<div>{info.componentStack}</div>
)}
{<div className={styles.version}>Version: {window.Sonarr.version}</div>}
<div className={styles.version}>Version: {window.Sonarr.version}</div>
</details>
</div>
);

View File

@ -1,4 +1,4 @@
import React, { Fragment } from 'react';
import React from 'react';
import Icon from 'Components/Icon';
import Popover from 'Components/Tooltip/Popover';
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
@ -82,9 +82,7 @@ function EpisodeNumber(props: EpisodeNumberProps) {
<Popover
anchor={
<span>
{showSeasonNumber && seasonNumber != null && (
<Fragment>{seasonNumber}x</Fragment>
)}
{showSeasonNumber && seasonNumber != null && <>{seasonNumber}x</>}
{showSeasonNumber ? padNumber(episodeNumber, 2) : episodeNumber}
@ -111,9 +109,7 @@ function EpisodeNumber(props: EpisodeNumberProps) {
/>
) : (
<span>
{showSeasonNumber && seasonNumber != null && (
<Fragment>{seasonNumber}x</Fragment>
)}
{showSeasonNumber && seasonNumber != null && <>{seasonNumber}x</>}
{showSeasonNumber ? padNumber(episodeNumber, 2) : episodeNumber}

View File

@ -3,15 +3,15 @@ import { useCallback, useState } from 'react';
export default function useModalOpenState(
initialState: boolean
): [boolean, () => void, () => void] {
const [isOpen, setOpen] = useState(initialState);
const [isOpen, setIsOpen] = useState(initialState);
const setModalOpen = useCallback(() => {
setOpen(true);
}, [setOpen]);
setIsOpen(true);
}, [setIsOpen]);
const setModalClosed = useCallback(() => {
setOpen(false);
}, [setOpen]);
setIsOpen(false);
}, [setIsOpen]);
return [isOpen, setModalOpen, setModalClosed];
}

View File

@ -857,7 +857,7 @@ function InteractiveImportModalContent(
<MenuContent>
<SelectedMenuItem
name={'all'}
name="all"
isSelected={!filterExistingFiles}
onPress={onFilterExistingFilesChange}
>
@ -865,7 +865,7 @@ function InteractiveImportModalContent(
</SelectedMenuItem>
<SelectedMenuItem
name={'new'}
name="new"
isSelected={filterExistingFiles}
onPress={onFilterExistingFilesChange}
>
@ -945,7 +945,7 @@ function InteractiveImportModalContent(
<SelectInput
className={styles.bulkSelect}
name="select"
value={'select'}
value="select"
values={bulkSelectOptions}
isDisabled={!selectedIds.length}
onChange={onSelectModalSelect}

View File

@ -17,7 +17,7 @@ function SelectLanguageModal(props: SelectLanguageModalProps) {
props;
return (
<Modal isOpen={isOpen} onModalClose={onModalClose} size={sizes.MEDIUM}>
<Modal isOpen={isOpen} size={sizes.MEDIUM} onModalClose={onModalClose}>
<SelectLanguageModalContent
languageIds={languageIds}
modalTitle={modalTitle}

View File

@ -64,19 +64,20 @@ interface RowItemData {
onSeriesSelect(seriesId: number): void;
}
const Row: React.FC<ListChildComponentProps<RowItemData>> = ({
index,
style,
data,
}) => {
function Row({ index, style, data }: ListChildComponentProps<RowItemData>) {
const { items, columns, onSeriesSelect } = data;
const series = index >= items.length ? null : items[index];
if (index >= items.length) {
const handlePress = useCallback(() => {
if (series?.id) {
onSeriesSelect(series.id);
}
}, [series?.id, onSeriesSelect]);
if (series == null) {
return null;
}
const series = items[index];
return (
<VirtualTableRowButton
style={{
@ -84,7 +85,7 @@ const Row: React.FC<ListChildComponentProps<RowItemData>> = ({
justifyContent: 'space-between',
...style,
}}
onPress={() => onSeriesSelect(series.id)}
onPress={handlePress}
>
<SelectSeriesRow
key={series.id}
@ -98,7 +99,7 @@ const Row: React.FC<ListChildComponentProps<RowItemData>> = ({
/>
</VirtualTableRowButton>
);
};
}
function SelectSeriesModalContent(props: SelectSeriesModalContentProps) {
const { modalTitle, onSeriesSelect, onModalClose } = props;
@ -197,9 +198,9 @@ function SelectSeriesModalContent(props: SelectSeriesModalContentProps) {
/>
<Scroller
ref={scrollerRef}
className={styles.scroller}
autoFocus={false}
ref={scrollerRef}
>
<SelectSeriesModalTableHeader columns={columns} />
<List<RowItemData>

View File

@ -17,7 +17,7 @@ function SelectDownloadClientModal(props: SelectDownloadClientModalProps) {
props;
return (
<Modal isOpen={isOpen} onModalClose={onModalClose} size={sizes.MEDIUM}>
<Modal isOpen={isOpen} size={sizes.MEDIUM} onModalClose={onModalClose}>
<SelectDownloadClientModalContent
protocol={protocol}
modalTitle={modalTitle}

View File

@ -1,4 +1,4 @@
import React, { Fragment, useCallback, useState } from 'react';
import React, { useCallback, useState } from 'react';
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
import { icons } from 'Helpers/Props';
import ParseModal from 'Parse/ParseModal';
@ -16,7 +16,7 @@ function ParseToolbarButton() {
}, [setIsParseModalOpen]);
return (
<Fragment>
<>
<PageToolbarButton
label={translate('TestParsing')}
iconName={icons.PARSE}
@ -24,7 +24,7 @@ function ParseToolbarButton() {
/>
<ParseModal isOpen={isParseModalOpen} onModalClose={onParseModalClose} />
</Fragment>
</>
);
}

View File

@ -42,11 +42,7 @@ interface SeriesIndexOverviewsProps {
isSmallScreen: boolean;
}
const Row: React.FC<ListChildComponentProps<RowItemData>> = ({
index,
style,
data,
}) => {
function Row({ index, style, data }: ListChildComponentProps<RowItemData>) {
const { items, ...otherData } = data;
if (index >= items.length) {
@ -60,7 +56,7 @@ const Row: React.FC<ListChildComponentProps<RowItemData>> = ({
<SeriesIndexOverview seriesId={series.id} {...otherData} />
</div>
);
};
}
function getWindowScrollTopPosition() {
return document.documentElement.scrollTop || document.body.scrollTop || 0;

View File

@ -60,12 +60,12 @@ const seriesIndexSelector = createSelector(
}
);
const Cell: React.FC<GridChildComponentProps<CellItemData>> = ({
function Cell({
columnIndex,
rowIndex,
style,
data,
}) => {
}: GridChildComponentProps<CellItemData>) {
const { layout, items, sortKey, isSelectMode } = data;
const { columnCount, padding, posterWidth, posterHeight } = layout;
const index = rowIndex * columnCount + columnIndex;
@ -92,7 +92,7 @@ const Cell: React.FC<GridChildComponentProps<CellItemData>> = ({
/>
</div>
);
};
}
function getWindowScrollTopPosition() {
return document.documentElement.scrollTop || document.body.scrollTop || 0;

View File

@ -45,11 +45,7 @@ const columnsSelector = createSelector(
(columns) => columns
);
const Row: React.FC<ListChildComponentProps<RowItemData>> = ({
index,
style,
data,
}) => {
function Row({ index, style, data }: ListChildComponentProps<RowItemData>) {
const { items, sortKey, columns, isSelectMode } = data;
if (index >= items.length) {
@ -75,7 +71,7 @@ const Row: React.FC<ListChildComponentProps<RowItemData>> = ({
/>
</div>
);
};
}
function getWindowScrollTopPosition() {
return document.documentElement.scrollTop || document.body.scrollTop || 0;

View File

@ -1,4 +1,4 @@
import React, { Fragment, useCallback } from 'react';
import React, { useCallback } from 'react';
import { useSelector } from 'react-redux';
import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
@ -32,7 +32,7 @@ function SeriesIndexTableOptions(props: SeriesIndexTableOptionsProps) {
);
return (
<Fragment>
<>
<FormGroup>
<FormLabel>{translate('ShowBanners')}</FormLabel>
@ -56,7 +56,7 @@ function SeriesIndexTableOptions(props: SeriesIndexTableOptionsProps) {
onChange={onTableOptionChangeWrapper}
/>
</FormGroup>
</Fragment>
</>
);
}

View File

@ -1,4 +1,4 @@
import React, { Fragment } from 'react';
import React from 'react';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import PageContent from 'Components/Page/PageContent';
@ -17,11 +17,11 @@ function CustomFormatSettingsPage() {
// @ts-ignore
showSave={false}
additionalButtons={
<Fragment>
<>
<PageToolbarSeparator />
<ParseToolbarButton />
</Fragment>
</>
}
/>

View File

@ -231,9 +231,9 @@ function ManageDownloadClientsModalContent(
selectAll={true}
allSelected={allSelected}
allUnselected={allUnselected}
onSelectAllChange={onSelectAllChange}
sortKey={sortKey}
sortDirection={sortDirection}
onSelectAllChange={onSelectAllChange}
onSortPress={onSortPress}
>
<TableBody>
@ -286,9 +286,9 @@ function ManageDownloadClientsModalContent(
<ManageDownloadClientsEditModal
isOpen={isEditModalOpen}
downloadClientIds={selectedIds}
onModalClose={onEditModalClose}
onSavePress={onSavePress}
downloadClientIds={selectedIds}
/>
<TagsModal

View File

@ -261,9 +261,9 @@ function ManageImportListsModalContent(
<ManageImportListsEditModal
isOpen={isEditModalOpen}
importListIds={selectedIds}
onModalClose={onEditModalClose}
onSavePress={onSavePress}
importListIds={selectedIds}
/>
<TagsModal

View File

@ -226,9 +226,9 @@ function ManageIndexersModalContent(props: ManageIndexersModalContentProps) {
selectAll={true}
allSelected={allSelected}
allUnselected={allUnselected}
onSelectAllChange={onSelectAllChange}
sortKey={sortKey}
sortDirection={sortDirection}
onSelectAllChange={onSelectAllChange}
onSortPress={onSortPress}
>
<TableBody>
@ -281,9 +281,9 @@ function ManageIndexersModalContent(props: ManageIndexersModalContentProps) {
<ManageIndexersEditModal
isOpen={isEditModalOpen}
indexerIds={selectedIds}
onModalClose={onEditModalClose}
onSavePress={onSavePress}
indexerIds={selectedIds}
/>
<TagsModal

View File

@ -66,7 +66,7 @@ function About() {
) : null}
{isDocker ? (
<DescriptionListItem title={translate('Docker')} data={'Yes'} />
<DescriptionListItem title={translate('Docker')} data="Yes" />
) : null}
<DescriptionListItem

View File

@ -1,10 +1,4 @@
import React, {
Fragment,
useCallback,
useEffect,
useMemo,
useState,
} from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
@ -158,7 +152,7 @@ function Updates() {
{translate('InstallLatest')}
</SpinnerButton>
) : (
<Fragment>
<>
<Icon name={icons.WARNING} kind={kinds.WARNING} size={30} />
<div className={styles.message}>
@ -171,7 +165,7 @@ function Updates() {
}
/>
</div>
</Fragment>
</>
)}
{isFetching ? (