Update React Lint rules for TSX

(cherry picked from commit 1299a97579bec52ee3d16ab8d05c9e22edd80330)
pull/2197/head
Mark McDowall 5 months ago committed by Bogdan
parent ef19673a76
commit 5cbbd060a4

@ -357,11 +357,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',
@ -374,7 +379,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'
})
},
{

@ -23,6 +23,10 @@ import Tasks from 'System/Tasks/Tasks';
import UpdatesConnector from 'System/Updates/UpdatesConnector';
import getPathWithUrlBase from 'Utilities/getPathWithUrlBase';
function RedirectWithUrlBase() {
return <Redirect to={getPathWithUrlBase('/')} />;
}
function AppRoutes() {
return (
<Switch>
@ -39,9 +43,7 @@ function AppRoutes() {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
addUrlBase={false}
render={() => {
return <Redirect to={getPathWithUrlBase('/')} />;
}}
render={RedirectWithUrlBase}
/>
)}

@ -63,11 +63,7 @@ function ErrorBoundaryError(props: ErrorBoundaryErrorProps) {
<div>{info.componentStack}</div>
)}
{
<div className={styles.version}>
Version: {window.Prowlarr.version}
</div>
}
<div className={styles.version}>Version: {window.Prowlarr.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];
}

@ -29,8 +29,8 @@ function AddIndexerModal({
<Modal
isOpen={isOpen}
size={sizes.EXTRA_LARGE}
onModalClose={onModalClosePress}
className={styles.modal}
onModalClose={onModalClosePress}
>
<AddIndexerModalContent
{...otherProps}

@ -168,6 +168,34 @@ function AddIndexerModalContent(props: AddIndexerModalContentProps) {
[setFilter]
);
const onFilterProtocolsChange = useCallback(
({ value }: { value: string[] }) => {
setFilterProtocols(value);
},
[setFilterProtocols]
);
const onFilterLanguagesChange = useCallback(
({ value }: { value: string[] }) => {
setFilterLanguages(value);
},
[setFilterLanguages]
);
const onFilterPrivacyLevelsChange = useCallback(
({ value }: { value: string[] }) => {
setFilterPrivacyLevels(value);
},
[setFilterPrivacyLevels]
);
const onFilterCategoriesChange = useCallback(
({ value }: { value: number[] }) => {
setFilterCategories(value);
},
[setFilterCategories]
);
const onIndexerSelect = useCallback(
({
implementation,
@ -304,9 +332,7 @@ function AddIndexerModalContent(props: AddIndexerModalContentProps) {
name="indexerProtocols"
value={filterProtocols}
values={PROTOCOLS}
onChange={({ value }: { value: string[] }) =>
setFilterProtocols(value)
}
onChange={onFilterProtocolsChange}
/>
</div>
@ -319,9 +345,7 @@ function AddIndexerModalContent(props: AddIndexerModalContentProps) {
name="indexerLanguages"
value={filterLanguages}
values={languages}
onChange={({ value }: { value: string[] }) =>
setFilterLanguages(value)
}
onChange={onFilterLanguagesChange}
/>
</div>
@ -331,9 +355,7 @@ function AddIndexerModalContent(props: AddIndexerModalContentProps) {
name="indexerPrivacyLevels"
value={filterPrivacyLevels}
values={PRIVACY_LEVELS}
onChange={({ value }: { value: string[] }) =>
setFilterPrivacyLevels(value)
}
onChange={onFilterPrivacyLevelsChange}
/>
</div>
@ -345,9 +367,7 @@ function AddIndexerModalContent(props: AddIndexerModalContentProps) {
<NewznabCategorySelectInputConnector
name="indexerCategories"
value={filterCategories}
onChange={({ value }: { value: number[] }) =>
setFilterCategories(value)
}
onChange={onFilterCategoriesChange}
/>
</div>
</div>

@ -2,6 +2,7 @@ import { uniqBy } from 'lodash';
import React from 'react';
import Label from 'Components/Label';
import { IndexerCapabilities } from 'Indexer/Indexer';
import translate from 'Utilities/String/translate';
interface CapabilitiesLabelProps {
capabilities: IndexerCapabilities;
@ -38,7 +39,7 @@ function CapabilitiesLabel(props: CapabilitiesLabelProps) {
);
})}
{filteredList.length === 0 ? <Label>{'None'}</Label> : null}
{filteredList.length === 0 ? <Label>{translate('None')}</Label> : null}
</span>
);
}

@ -46,11 +46,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, onCloneIndexerPress } = data;
if (index >= items.length) {
@ -77,7 +73,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 IndexerIndexTableOptions(props: IndexerIndexTableOptionsProps) {
);
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>
);
}

@ -38,14 +38,12 @@ function IndexerStatusCell(props: IndexerStatusCellProps) {
return (
<Component className={className} {...otherProps}>
{
<Icon
className={styles.statusIcon}
kind={enabled ? enableKind : kinds.DEFAULT}
name={enabled ? enableIcon : icons.BLOCKLIST}
title={enabled ? enableTitle : translate('Disabled')}
/>
}
<Icon
className={styles.statusIcon}
kind={enabled ? enableKind : kinds.DEFAULT}
name={enabled ? enableIcon : icons.BLOCKLIST}
title={enabled ? enableTitle : translate('Disabled')}
/>
{status ? (
<Popover
className={styles.indexerStatusTooltip}

@ -83,8 +83,8 @@ function IndexerHistoryRow(props: IndexerHistoryRowProps) {
<TableRowCell className={styles.details}>
<IconButton
name={icons.INFO}
onPress={onDetailsModalPress}
title={translate('HistoryDetails')}
onPress={onDetailsModalPress}
/>
</TableRowCell>

@ -28,7 +28,7 @@ function NoIndexer(props: NoIndexerProps) {
</div>
<div className={styles.buttonContainer}>
<Button onPress={onAddIndexerPress} kind={kinds.PRIMARY}>
<Button kind={kinds.PRIMARY} onPress={onAddIndexerPress}>
{translate('AddNewIndexer')}
</Button>
</div>

@ -230,9 +230,9 @@ function IndexerStats() {
selectedFilterKey={selectedFilterKey}
filters={filters}
customFilters={customFilters}
onFilterSelect={onFilterSelect}
filterModalConnectorComponent={IndexerStatsFilterModal}
isDisabled={false}
onFilterSelect={onFilterSelect}
/>
</PageToolbarSection>
</PageToolbar>

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

@ -184,9 +184,9 @@ function SearchIndexRow(props: SearchIndexRowProps) {
if (name === 'select') {
return (
<VirtualTableSelectCell
key={name}
inputClassName={styles.checkInput}
id={guid}
key={name}
isSelected={isSelected}
isDisabled={false}
onSelectedChange={onSelectedChange}

@ -1,4 +1,4 @@
import React, { Fragment, useCallback, useState } from 'react';
import React, { useCallback, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import AppState from 'App/State/AppState';
import { APP_INDEXER_SYNC } from 'Commands/commandNames';
@ -56,7 +56,7 @@ function ApplicationSettings() {
// @ts-ignore
showSave={false}
additionalButtons={
<Fragment>
<>
<PageToolbarSeparator />
<PageToolbarButton
@ -78,7 +78,7 @@ function ApplicationSettings() {
iconName={icons.MANAGE}
onPress={onManageApplicationsPress}
/>
</Fragment>
</>
}
/>

@ -213,9 +213,9 @@ function ManageApplicationsModalContent(
selectAll={true}
allSelected={allSelected}
allUnselected={allUnselected}
onSelectAllChange={onSelectAllChange}
sortKey={sortKey}
sortDirection={sortDirection}
onSelectAllChange={onSelectAllChange}
onSortPress={onSortPress}
>
<TableBody>
@ -268,9 +268,9 @@ function ManageApplicationsModalContent(
<ManageApplicationsEditModal
isOpen={isEditModalOpen}
applicationIds={selectedIds}
onModalClose={onEditModalClose}
onSavePress={onSavePress}
applicationIds={selectedIds}
/>
<TagsModal

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

@ -45,11 +45,12 @@ function About() {
title={translate('PackageVersion')}
data={
packageAuthor ? (
<span>
{' '}
{packageVersion} {' by '}{' '}
<InlineMarkdown data={packageAuthor} />{' '}
</span>
<InlineMarkdown
data={translate('PackageVersionInfo', {
packageVersion,
packageAuthor,
})}
/>
) : (
packageVersion
)
@ -57,16 +58,16 @@ function About() {
/>
)}
{isNetCore && (
{isNetCore ? (
<DescriptionListItem
title={translate('NetCore')}
data={`Yes (${runtimeVersion})`}
/>
)}
) : null}
{isDocker && (
<DescriptionListItem title={translate('Docker')} data={'Yes'} />
)}
{isDocker ? (
<DescriptionListItem title={translate('Docker')} data="Yes" />
) : null}
<DescriptionListItem
title={translate('Database')}

@ -527,6 +527,7 @@
"PackSeedTime": "Pack Seed Time",
"PackSeedTimeHelpText": "The time a pack (season or discography) torrent should be seeded before stopping, empty is app's default",
"PackageVersion": "Package Version",
"PackageVersionInfo": "{packageVersion} by {packageAuthor}",
"PageSize": "Page Size",
"PageSizeHelpText": "Number of items to show on each page",
"Parameters": "Parameters",

Loading…
Cancel
Save