From b13dbcfa77ca4799307ad04cee41369fcb5ac2a9 Mon Sep 17 00:00:00 2001 From: Chris Svenningsen Date: Fri, 11 Sep 2020 17:46:52 -0700 Subject: [PATCH] Migrate components to eslint --- .eslintignore | 7 +- .eslintrc.js | 18 +- _locales/en/messages.json | 24 + ts/components/Avatar.stories.tsx | 5 +- ts/components/Avatar.tsx | 19 +- ts/components/AvatarPopup.stories.tsx | 7 +- ts/components/AvatarPopup.tsx | 9 +- ts/components/CallManager.stories.tsx | 9 +- ts/components/CallScreen.stories.tsx | 15 +- ts/components/CallScreen.tsx | 54 ++- .../CallingDeviceSelection.stories.tsx | 11 +- ts/components/CallingDeviceSelection.tsx | 21 +- ts/components/CaptionEditor.stories.tsx | 4 - ts/components/CaptionEditor.tsx | 33 +- ts/components/CompositionArea.stories.tsx | 11 +- ts/components/CompositionArea.tsx | 19 +- ts/components/CompositionInput.stories.tsx | 10 +- ts/components/CompositionInput.tsx | 423 +++++++++--------- ts/components/ConfirmationDialog.stories.tsx | 11 +- ts/components/ConfirmationDialog.tsx | 4 +- ts/components/ConfirmationModal.tsx | 14 +- ts/components/ContactListItem.stories.tsx | 6 +- ts/components/ContactListItem.tsx | 5 +- .../ConversationListItem.stories.tsx | 18 +- ts/components/ConversationListItem.tsx | 24 +- ts/components/Countdown.tsx | 15 +- ts/components/ExpiredBuildDialog.stories.tsx | 11 +- ts/components/ExpiredBuildDialog.tsx | 4 +- ts/components/InContactsIcon.stories.tsx | 5 +- ts/components/InContactsIcon.tsx | 2 + ts/components/IncomingCallBar.stories.tsx | 13 +- ts/components/IncomingCallBar.tsx | 2 +- ts/components/Intl.stories.tsx | 9 +- ts/components/Intl.tsx | 29 +- ts/components/LeftPane.stories.tsx | 4 +- ts/components/LeftPane.tsx | 75 ++-- ts/components/Lightbox.stories.tsx | 4 +- ts/components/Lightbox.tsx | 94 ++-- ts/components/LightboxGallery.stories.tsx | 12 +- ts/components/LightboxGallery.tsx | 7 +- ts/components/MainHeader.stories.tsx | 5 +- ts/components/MainHeader.tsx | 42 +- .../MessageBodyHighlight.stories.tsx | 5 +- ts/components/MessageBodyHighlight.tsx | 23 +- ts/components/MessageSearchResult.stories.tsx | 17 +- ts/components/MessageSearchResult.tsx | 11 +- ts/components/NetworkStatus.stories.tsx | 11 +- ts/components/NetworkStatus.tsx | 23 +- ts/components/RelinkDialog.stories.tsx | 11 +- ts/components/RelinkDialog.tsx | 4 +- .../SafetyNumberChangeDialog.stories.tsx | 13 +- ts/components/SafetyNumberChangeDialog.tsx | 5 +- ts/components/SafetyNumberViewer.stories.tsx | 13 +- ts/components/SafetyNumberViewer.tsx | 17 +- ts/components/SearchResults.stories.tsx | 41 +- ts/components/SearchResults.tsx | 145 +++--- ts/components/ShortcutGuide.stories.tsx | 4 - ts/components/ShortcutGuide.tsx | 14 +- ts/components/ShortcutGuideModal.tsx | 57 ++- ts/components/Spinner.stories.tsx | 2 +- ts/components/Spinner.tsx | 70 ++- .../StartNewConversation.stories.tsx | 11 +- ts/components/StartNewConversation.tsx | 8 +- ts/components/UpdateDialog.stories.tsx | 11 +- ts/components/UpdateDialog.tsx | 7 +- ts/components/_util.ts | 11 +- ts/components/emoji/lib.ts | 6 +- ts/util/lint/exceptions.json | 18 +- tslint.json | 3 +- 69 files changed, 875 insertions(+), 800 deletions(-) diff --git a/.eslintignore b/.eslintignore index cdc0de02b2..9c7eff025c 100644 --- a/.eslintignore +++ b/.eslintignore @@ -30,8 +30,9 @@ webpack.config.ts # Temporarily ignored during TSLint transition # JIRA: DESKTOP-304 -ts/components/*.ts -ts/components/*.tsx +sticker-creator/**/*.ts +sticker-creator/**/*.tsx +ts/*.ts ts/components/conversation/** ts/components/stickers/** ts/shims/** @@ -44,5 +45,3 @@ ts/textsecure/** ts/types/** ts/updater/** ts/util/** -sticker-creator/**/*.ts -sticker-creator/**/*.tsx diff --git a/.eslintrc.js b/.eslintrc.js index 7dcd2f1e6f..e6827f54ef 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -59,7 +59,24 @@ const rules = { }, ], + 'react/jsx-props-no-spreading': 'off', + + // Updated to reflect future airbnb standard + // Allows for declaring defaultProps inside a class + 'react/static-property-placement': ['error', 'static public field'], + + // JIRA: DESKTOP-657 + 'react/sort-comp': 'off', + + // We don't have control over the media we're sharing, so can't require + // captions. + 'jsx-a11y/media-has-caption': 'off', + + // We prefer named exports 'import/prefer-default-export': 'off', + + // Prefer functional components with default params + 'react/require-default-props': 'off', }; module.exports = { @@ -101,7 +118,6 @@ module.exports = { rules: { ...rules, 'import/no-extraneous-dependencies': 'off', - 'react/jsx-props-no-spreading': 'off', }, }, ], diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 9de50fac56..858f91fb1e 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -671,6 +671,10 @@ "message": "Search", "description": "Placeholder text in the search input" }, + "clearSearch": { + "message": "Clear Search", + "description": "Aria label for clear search button" + }, "searchIn": { "message": "Search in $conversationName$", "description": "Shown in the search box before text is entered when searching in a specific conversation", @@ -3568,5 +3572,25 @@ "example": "5" } } + }, + "close": { + "message": "close", + "description": "Generic close label" + }, + "previous": { + "message": "previous", + "description": "Generic previous label" + }, + "next": { + "message": "next", + "description": "Generic next label" + }, + "CompositionArea--expand": { + "message": "Expand", + "description": "Aria label for expanding composition area" + }, + "CompositionArea--attach-file": { + "message": "Attach file", + "description": "Aria label for file attachment button in composition area" } } diff --git a/ts/components/Avatar.stories.tsx b/ts/components/Avatar.stories.tsx index e802206954..e4ce0fca37 100644 --- a/ts/components/Avatar.stories.tsx +++ b/ts/components/Avatar.stories.tsx @@ -1,14 +1,11 @@ import * as React from 'react'; -import { Avatar, Props } from './Avatar'; import { storiesOf } from '@storybook/react'; import { boolean, select, text } from '@storybook/addon-knobs'; import { action } from '@storybook/addon-actions'; -// @ts-ignore +import { Avatar, Props } from './Avatar'; import { setup as setupI18n } from '../../js/modules/i18n'; - -// @ts-ignore import enMessages from '../../_locales/en/messages.json'; import { Colors, ColorType } from '../types/Colors'; diff --git a/ts/components/Avatar.tsx b/ts/components/Avatar.tsx index 0e8afde31a..d0ffcb1e16 100644 --- a/ts/components/Avatar.tsx +++ b/ts/components/Avatar.tsx @@ -56,15 +56,16 @@ export class Avatar extends React.Component { return state; } - public handleImageError() { - // tslint:disable-next-line no-console - console.log('Avatar: Image failed to load; failing over to placeholder'); + public handleImageError(): void { + window.log.info( + 'Avatar: Image failed to load; failing over to placeholder' + ); this.setState({ imageBroken: true, }); } - public renderImage() { + public renderImage(): JSX.Element | null { const { avatarPath, i18n, title } = this.props; const { imageBroken } = this.state; @@ -81,7 +82,7 @@ export class Avatar extends React.Component { ); } - public renderNoImage() { + public renderNoImage(): JSX.Element { const { conversationType, name, @@ -129,7 +130,7 @@ export class Avatar extends React.Component { ); } - public render() { + public render(): JSX.Element { const { avatarPath, color, @@ -151,7 +152,11 @@ export class Avatar extends React.Component { if (onClick) { contents = ( - ); diff --git a/ts/components/AvatarPopup.stories.tsx b/ts/components/AvatarPopup.stories.tsx index 586423c00c..e65d1eecd8 100644 --- a/ts/components/AvatarPopup.stories.tsx +++ b/ts/components/AvatarPopup.stories.tsx @@ -2,14 +2,11 @@ import * as React from 'react'; import { storiesOf } from '@storybook/react'; import { action } from '@storybook/addon-actions'; -import { AvatarPopup, Props } from './AvatarPopup'; -import { Colors, ColorType } from '../types/Colors'; import { boolean, select, text } from '@storybook/addon-knobs'; -// @ts-ignore +import { AvatarPopup, Props } from './AvatarPopup'; +import { Colors, ColorType } from '../types/Colors'; import { setup as setupI18n } from '../../js/modules/i18n'; - -// @ts-ignore import enMessages from '../../_locales/en/messages.json'; const i18n = setupI18n('en', enMessages); diff --git a/ts/components/AvatarPopup.tsx b/ts/components/AvatarPopup.tsx index 11f600bc33..03b5a22fc4 100644 --- a/ts/components/AvatarPopup.tsx +++ b/ts/components/AvatarPopup.tsx @@ -17,7 +17,7 @@ export type Props = { style: React.CSSProperties; } & AvatarProps; -export const AvatarPopup = (props: Props) => { +export const AvatarPopup = (props: Props): JSX.Element => { const focusRef = React.useRef(null); const { i18n, @@ -54,6 +54,7 @@ export const AvatarPopup = (props: Props) => {
- ); @@ -55,9 +55,14 @@ type StateType = { }; export class CallScreen extends React.Component { + // eslint-disable-next-line @typescript-eslint/no-explicit-any private interval: any; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any private controlsFadeTimer: any; + private readonly localVideoRef: React.RefObject; + private readonly remoteVideoRef: React.RefObject; constructor(props: PropsType) { @@ -75,18 +80,22 @@ export class CallScreen extends React.Component { this.remoteVideoRef = React.createRef(); } - public componentDidMount() { + public componentDidMount(): void { + const { setLocalPreview, setRendererCanvas } = this.props; + // It's really jump with a value of 500ms. this.interval = setInterval(this.updateAcceptedTimer, 100); this.fadeControls(); document.addEventListener('keydown', this.handleKeyDown); - this.props.setLocalPreview({ element: this.localVideoRef }); - this.props.setRendererCanvas({ element: this.remoteVideoRef }); + setLocalPreview({ element: this.localVideoRef }); + setRendererCanvas({ element: this.remoteVideoRef }); } - public componentWillUnmount() { + public componentWillUnmount(): void { + const { setLocalPreview, setRendererCanvas } = this.props; + document.removeEventListener('keydown', this.handleKeyDown); if (this.interval) { @@ -95,11 +104,12 @@ export class CallScreen extends React.Component { if (this.controlsFadeTimer) { clearTimeout(this.controlsFadeTimer); } - this.props.setLocalPreview({ element: undefined }); - this.props.setRendererCanvas({ element: undefined }); + + setLocalPreview({ element: undefined }); + setRendererCanvas({ element: undefined }); } - updateAcceptedTimer = () => { + updateAcceptedTimer = (): void => { const { acceptedTime } = this.state; const { callState } = this.props; @@ -119,7 +129,7 @@ export class CallScreen extends React.Component { } }; - handleKeyDown = (event: KeyboardEvent) => { + handleKeyDown = (event: KeyboardEvent): void => { const { callDetails } = this.props; if (!callDetails) { @@ -143,8 +153,10 @@ export class CallScreen extends React.Component { } }; - showControls = () => { - if (!this.state.showControls) { + showControls = (): void => { + const { showControls } = this.state; + + if (!showControls) { this.setState({ showControls: true, }); @@ -153,7 +165,7 @@ export class CallScreen extends React.Component { this.fadeControls(); }; - fadeControls = () => { + fadeControls = (): void => { if (this.controlsFadeTimer) { clearTimeout(this.controlsFadeTimer); } @@ -165,7 +177,7 @@ export class CallScreen extends React.Component { }, 5000); }; - toggleAudio = () => { + toggleAudio = (): void => { const { callDetails, hasLocalAudio, setLocalAudio } = this.props; if (!callDetails) { @@ -178,7 +190,7 @@ export class CallScreen extends React.Component { }); }; - toggleVideo = () => { + toggleVideo = (): void => { const { callDetails, hasLocalVideo, setLocalVideo } = this.props; if (!callDetails) { @@ -188,7 +200,7 @@ export class CallScreen extends React.Component { setLocalVideo({ callId: callDetails.callId, enabled: !hasLocalVideo }); }; - public render() { + public render(): JSX.Element | null { const { callDetails, callState, @@ -238,6 +250,7 @@ export class CallScreen extends React.Component { {this.renderMessage(callState)}
@@ -144,14 +146,13 @@ export const CallingDeviceSelection = ({ {i18n('callingDeviceSelection__settings')} -