Update React Lint rules for TSX

(cherry picked from commit 1299a97579bec52ee3d16ab8d05c9e22edd80330)
This commit is contained in:
Mark McDowall 2024-07-30 17:44:39 -07:00 committed by Bogdan
parent ef19673a76
commit 5cbbd060a4
20 changed files with 133 additions and 81 deletions

View file

@ -357,11 +357,16 @@ module.exports = {
], ],
rules: Object.assign(typescriptEslintRecommended.rules, { rules: Object.assign(typescriptEslintRecommended.rules, {
'no-shadow': 'off', '@typescript-eslint/no-unused-vars': [
// These should be enabled after cleaning things up 'error',
'@typescript-eslint/no-unused-vars': 'warn', {
args: 'after-used',
argsIgnorePattern: '^_',
ignoreRestSiblings: true
}
],
'@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/explicit-function-return-type': 'off',
'react/prop-types': 'off', 'no-shadow': 'off',
'prettier/prettier': 'error', 'prettier/prettier': 'error',
'simple-import-sort/imports': [ 'simple-import-sort/imports': [
'error', 'error',
@ -374,7 +379,41 @@ module.exports = {
['^@?\\w', `^(${dirs})(/.*|$)`, '^\\.', '^\\..*css$'] ['^@?\\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

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,6 +2,7 @@ import { uniqBy } from 'lodash';
import React from 'react'; import React from 'react';
import Label from 'Components/Label'; import Label from 'Components/Label';
import { IndexerCapabilities } from 'Indexer/Indexer'; import { IndexerCapabilities } from 'Indexer/Indexer';
import translate from 'Utilities/String/translate';
interface CapabilitiesLabelProps { interface CapabilitiesLabelProps {
capabilities: IndexerCapabilities; 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> </span>
); );
} }

View file

@ -46,11 +46,7 @@ const columnsSelector = createSelector(
(columns) => columns (columns) => columns
); );
const Row: React.FC<ListChildComponentProps<RowItemData>> = ({ function Row({ index, style, data }: ListChildComponentProps<RowItemData>) {
index,
style,
data,
}) => {
const { items, sortKey, columns, isSelectMode, onCloneIndexerPress } = data; const { items, sortKey, columns, isSelectMode, onCloneIndexerPress } = data;
if (index >= items.length) { if (index >= items.length) {
@ -77,7 +73,7 @@ const Row: React.FC<ListChildComponentProps<RowItemData>> = ({
/> />
</div> </div>
); );
}; }
function getWindowScrollTopPosition() { function getWindowScrollTopPosition() {
return document.documentElement.scrollTop || document.body.scrollTop || 0; 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 { useSelector } from 'react-redux';
import FormGroup from 'Components/Form/FormGroup'; import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup'; import FormInputGroup from 'Components/Form/FormInputGroup';
@ -32,19 +32,17 @@ function IndexerIndexTableOptions(props: IndexerIndexTableOptionsProps) {
); );
return ( return (
<Fragment> <FormGroup>
<FormGroup> <FormLabel>{translate('ShowSearch')}</FormLabel>
<FormLabel>{translate('ShowSearch')}</FormLabel>
<FormInputGroup <FormInputGroup
type={inputTypes.CHECK} type={inputTypes.CHECK}
name="showSearchAction" name="showSearchAction"
value={showSearchAction} value={showSearchAction}
helpText={translate('ShowSearchHelpText')} helpText={translate('ShowSearchHelpText')}
onChange={onTableOptionChangeWrapper} onChange={onTableOptionChangeWrapper}
/> />
</FormGroup> </FormGroup>
</Fragment>
); );
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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