Update React Lint rules for TSX

(cherry picked from commit 1299a97579bec52ee3d16ab8d05c9e22edd80330)

Closes #10248
pull/10296/head
Mark McDowall 4 months ago committed by Bogdan
parent 4628868dfa
commit 5cac5b6068

@ -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'
})
},
{

@ -1,13 +1,9 @@
import React, { Fragment, ReactNode, useCallback, useEffect } from 'react';
import { useCallback, useEffect } from 'react';
import { useSelector } from 'react-redux';
import { createSelector } from 'reselect';
import themes from 'Styles/Themes';
import AppState from './State/AppState';
interface ApplyThemeProps {
children: ReactNode;
}
function createThemeSelector() {
return createSelector(
(state: AppState) => state.settings.ui.item.theme || window.Radarr.theme,
@ -17,7 +13,7 @@ function createThemeSelector() {
);
}
function ApplyTheme({ children }: ApplyThemeProps) {
function ApplyTheme() {
const theme = useSelector(createThemeSelector());
const updateCSSVariables = useCallback(() => {
@ -31,7 +27,7 @@ function ApplyTheme({ children }: ApplyThemeProps) {
updateCSSVariables();
}, [updateCSSVariables, theme]);
return <Fragment>{children}</Fragment>;
return null;
}
export default ApplyTheme;

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

@ -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];
}

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

@ -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}

@ -64,19 +64,20 @@ interface RowItemData {
onMovieSelect(movieId: number): void;
}
const Row: React.FC<ListChildComponentProps<RowItemData>> = ({
index,
style,
data,
}) => {
function Row({ index, style, data }: ListChildComponentProps<RowItemData>) {
const { items, columns, onMovieSelect } = data;
const movie = index >= items.length ? null : items[index];
if (index >= items.length) {
const handlePress = useCallback(() => {
if (movie?.id) {
onMovieSelect(movie.id);
}
}, [movie?.id, onMovieSelect]);
if (movie == null) {
return null;
}
const movie = items[index];
return (
<VirtualTableRowButton
style={{
@ -84,7 +85,7 @@ const Row: React.FC<ListChildComponentProps<RowItemData>> = ({
justifyContent: 'space-between',
...style,
}}
onPress={() => onMovieSelect(movie.id)}
onPress={handlePress}
>
<SelectMovieRow
id={movie.id}
@ -97,7 +98,7 @@ const Row: React.FC<ListChildComponentProps<RowItemData>> = ({
/>
</VirtualTableRowButton>
);
};
}
function SelectMovieModalContent(props: SelectMovieModalContentProps) {
const { modalTitle, onMovieSelect, onModalClose } = props;
@ -196,9 +197,9 @@ function SelectMovieModalContent(props: SelectMovieModalContentProps) {
/>
<Scroller
ref={scrollerRef}
className={styles.scroller}
autoFocus={false}
ref={scrollerRef}
>
<SelectMovieModalTableHeader columns={columns} />
<List<RowItemData>

@ -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}

@ -42,11 +42,7 @@ interface MovieIndexOverviewsProps {
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>> = ({
<MovieIndexOverview movieId={movie.id} {...otherData} />
</div>
);
};
}
function getWindowScrollTopPosition() {
return document.documentElement.scrollTop || document.body.scrollTop || 0;

@ -60,12 +60,12 @@ const movieIndexSelector = 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;
@ -94,7 +94,7 @@ const Cell: React.FC<GridChildComponentProps<CellItemData>> = ({
/>
</div>
);
};
}
function getWindowScrollTopPosition() {
return document.documentElement.scrollTop || document.body.scrollTop || 0;

@ -202,9 +202,7 @@ function EditMoviesModalContent(props: EditMoviesModalContentProps) {
includeNoChange={true}
includeNoChangeDisabled={false}
selectedValueOptions={{ includeFreeSpace: false }}
helpText={
'Moving movies to the same root folder can be used to rename movie folders to match updated title or naming format'
}
helpText="Moving movies to the same root folder can be used to rename movie folders to match updated title or naming format"
onChange={onInputChange}
/>
</FormGroup>

@ -44,11 +44,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) {
@ -74,7 +70,7 @@ const Row: React.FC<ListChildComponentProps<RowItemData>> = ({
/>
</div>
);
};
}
function getWindowScrollTopPosition() {
return document.documentElement.scrollTop || document.body.scrollTop || 0;

@ -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,19 +32,17 @@ function MovieIndexTableOptions(props: MovieIndexTableOptionsProps) {
);
return (
<Fragment>
<FormGroup>
<FormLabel>{translate('ShowSearch')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="showSearchAction"
value={showSearchAction}
helpText={translate('ShowSearchHelpText')}
onChange={onTableOptionChangeWrapper}
/>
</FormGroup>
</Fragment>
<FormGroup>
<FormLabel>{translate('ShowSearch')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="showSearchAction"
value={showSearchAction}
helpText={translate('ShowSearchHelpText')}
onChange={onTableOptionChangeWrapper}
/>
</FormGroup>
);
}

@ -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>
</>
);
}

@ -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>
</>
}
/>

@ -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

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

@ -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

Loading…
Cancel
Save