From e127c3b710a5639de24cdabdc471b417cc3871f8 Mon Sep 17 00:00:00 2001 From: Thomas O'Brien Date: Sun, 12 Oct 2025 00:07:23 +0100 Subject: [PATCH] Alert, Card, DragPreviewLayer, FieldSet, HeartRating, Icon typescript conversion --- .../Overview/ArtistIndexOverviewInfoRow.tsx | 5 +- frontend/src/Components/Alert.js | 34 --------- frontend/src/Components/Alert.tsx | 18 +++++ frontend/src/Components/Card.js | 60 --------------- frontend/src/Components/Card.tsx | 39 ++++++++++ frontend/src/Components/DragPreviewLayer.js | 22 ------ frontend/src/Components/DragPreviewLayer.tsx | 21 ++++++ frontend/src/Components/FieldSet.js | 41 ----------- frontend/src/Components/FieldSet.tsx | 29 ++++++++ frontend/src/Components/HeartRating.js | 30 -------- frontend/src/Components/HeartRating.tsx | 30 ++++++++ frontend/src/Components/Icon.js | 73 ------------------- frontend/src/Components/Icon.tsx | 60 +++++++++++++++ frontend/src/System/Status/Health/Health.tsx | 4 +- .../src/System/Tasks/Queued/QueuedTaskRow.tsx | 7 +- 15 files changed, 206 insertions(+), 267 deletions(-) delete mode 100644 frontend/src/Components/Alert.js create mode 100644 frontend/src/Components/Alert.tsx delete mode 100644 frontend/src/Components/Card.js create mode 100644 frontend/src/Components/Card.tsx delete mode 100644 frontend/src/Components/DragPreviewLayer.js create mode 100644 frontend/src/Components/DragPreviewLayer.tsx delete mode 100644 frontend/src/Components/FieldSet.js create mode 100644 frontend/src/Components/FieldSet.tsx delete mode 100644 frontend/src/Components/HeartRating.js create mode 100644 frontend/src/Components/HeartRating.tsx delete mode 100644 frontend/src/Components/Icon.js create mode 100644 frontend/src/Components/Icon.tsx diff --git a/frontend/src/Artist/Index/Overview/ArtistIndexOverviewInfoRow.tsx b/frontend/src/Artist/Index/Overview/ArtistIndexOverviewInfoRow.tsx index 5d9b4a069..c44173595 100644 --- a/frontend/src/Artist/Index/Overview/ArtistIndexOverviewInfoRow.tsx +++ b/frontend/src/Artist/Index/Overview/ArtistIndexOverviewInfoRow.tsx @@ -1,11 +1,10 @@ -import { IconDefinition } from '@fortawesome/free-regular-svg-icons'; import React from 'react'; -import Icon from 'Components/Icon'; +import Icon, { IconProps } from 'Components/Icon'; import styles from './ArtistIndexOverviewInfoRow.css'; interface ArtistIndexOverviewInfoRowProps { title?: string; - iconName?: IconDefinition; + iconName: IconProps['name']; label: string | null; } diff --git a/frontend/src/Components/Alert.js b/frontend/src/Components/Alert.js deleted file mode 100644 index 418cbf5e6..000000000 --- a/frontend/src/Components/Alert.js +++ /dev/null @@ -1,34 +0,0 @@ -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React from 'react'; -import { kinds } from 'Helpers/Props'; -import styles from './Alert.css'; - -function Alert(props) { - const { className, kind, children, ...otherProps } = props; - - return ( -
- {children} -
- ); -} - -Alert.propTypes = { - className: PropTypes.string, - kind: PropTypes.oneOf(kinds.all), - children: PropTypes.node.isRequired -}; - -Alert.defaultProps = { - className: styles.alert, - kind: kinds.INFO -}; - -export default Alert; diff --git a/frontend/src/Components/Alert.tsx b/frontend/src/Components/Alert.tsx new file mode 100644 index 000000000..92c89e741 --- /dev/null +++ b/frontend/src/Components/Alert.tsx @@ -0,0 +1,18 @@ +import classNames from 'classnames'; +import React from 'react'; +import { Kind } from 'Helpers/Props/kinds'; +import styles from './Alert.css'; + +interface AlertProps { + className?: string; + kind?: Extract; + children: React.ReactNode; +} + +function Alert(props: AlertProps) { + const { className = styles.alert, kind = 'info', children } = props; + + return
{children}
; +} + +export default Alert; diff --git a/frontend/src/Components/Card.js b/frontend/src/Components/Card.js deleted file mode 100644 index c5a4d164c..000000000 --- a/frontend/src/Components/Card.js +++ /dev/null @@ -1,60 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import Link from 'Components/Link/Link'; -import styles from './Card.css'; - -class Card extends Component { - - // - // Render - - render() { - const { - className, - overlayClassName, - overlayContent, - children, - onPress - } = this.props; - - if (overlayContent) { - return ( -
- - -
- {children} -
-
- ); - } - - return ( - - {children} - - ); - } -} - -Card.propTypes = { - className: PropTypes.string.isRequired, - overlayClassName: PropTypes.string.isRequired, - overlayContent: PropTypes.bool.isRequired, - children: PropTypes.node.isRequired, - onPress: PropTypes.func.isRequired -}; - -Card.defaultProps = { - className: styles.card, - overlayClassName: styles.overlay, - overlayContent: false -}; - -export default Card; diff --git a/frontend/src/Components/Card.tsx b/frontend/src/Components/Card.tsx new file mode 100644 index 000000000..24588c841 --- /dev/null +++ b/frontend/src/Components/Card.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import Link, { LinkProps } from 'Components/Link/Link'; +import styles from './Card.css'; + +interface CardProps extends Pick { + // TODO: Consider using different properties for classname depending if it's overlaying content or not + className?: string; + overlayClassName?: string; + overlayContent?: boolean; + children: React.ReactNode; +} + +function Card(props: CardProps) { + const { + className = styles.card, + overlayClassName = styles.overlay, + overlayContent = false, + children, + onPress, + } = props; + + if (overlayContent) { + return ( +
+ + +
{children}
+
+ ); + } + + return ( + + {children} + + ); +} + +export default Card; diff --git a/frontend/src/Components/DragPreviewLayer.js b/frontend/src/Components/DragPreviewLayer.js deleted file mode 100644 index a111df70e..000000000 --- a/frontend/src/Components/DragPreviewLayer.js +++ /dev/null @@ -1,22 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import styles from './DragPreviewLayer.css'; - -function DragPreviewLayer({ children, ...otherProps }) { - return ( -
- {children} -
- ); -} - -DragPreviewLayer.propTypes = { - children: PropTypes.node, - className: PropTypes.string -}; - -DragPreviewLayer.defaultProps = { - className: styles.dragLayer -}; - -export default DragPreviewLayer; diff --git a/frontend/src/Components/DragPreviewLayer.tsx b/frontend/src/Components/DragPreviewLayer.tsx new file mode 100644 index 000000000..2e578504b --- /dev/null +++ b/frontend/src/Components/DragPreviewLayer.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import styles from './DragPreviewLayer.css'; + +interface DragPreviewLayerProps { + className?: string; + children?: React.ReactNode; +} + +function DragPreviewLayer({ + className = styles.dragLayer, + children, + ...otherProps +}: DragPreviewLayerProps) { + return ( +
+ {children} +
+ ); +} + +export default DragPreviewLayer; diff --git a/frontend/src/Components/FieldSet.js b/frontend/src/Components/FieldSet.js deleted file mode 100644 index 8243fd00c..000000000 --- a/frontend/src/Components/FieldSet.js +++ /dev/null @@ -1,41 +0,0 @@ -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { sizes } from 'Helpers/Props'; -import styles from './FieldSet.css'; - -class FieldSet extends Component { - - // - // Render - - render() { - const { - size, - legend, - children - } = this.props; - - return ( -
- - {legend} - - {children} -
- ); - } - -} - -FieldSet.propTypes = { - size: PropTypes.oneOf(sizes.all).isRequired, - legend: PropTypes.oneOfType([PropTypes.node, PropTypes.string]), - children: PropTypes.node -}; - -FieldSet.defaultProps = { - size: sizes.MEDIUM -}; - -export default FieldSet; diff --git a/frontend/src/Components/FieldSet.tsx b/frontend/src/Components/FieldSet.tsx new file mode 100644 index 000000000..c2ff03a7f --- /dev/null +++ b/frontend/src/Components/FieldSet.tsx @@ -0,0 +1,29 @@ +import classNames from 'classnames'; +import React, { ComponentProps } from 'react'; +import { sizes } from 'Helpers/Props'; +import { Size } from 'Helpers/Props/sizes'; +import styles from './FieldSet.css'; + +interface FieldSetProps { + size?: Size; + legend?: ComponentProps<'legend'>['children']; + children?: React.ReactNode; +} + +function FieldSet({ size = sizes.MEDIUM, legend, children }: FieldSetProps) { + return ( +
+ + {legend} + + {children} +
+ ); +} + +export default FieldSet; diff --git a/frontend/src/Components/HeartRating.js b/frontend/src/Components/HeartRating.js deleted file mode 100644 index fe53a4e5f..000000000 --- a/frontend/src/Components/HeartRating.js +++ /dev/null @@ -1,30 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import Icon from 'Components/Icon'; -import { icons } from 'Helpers/Props'; -import styles from './HeartRating.css'; - -function HeartRating({ rating, iconSize }) { - return ( - - - - {rating * 10}% - - ); -} - -HeartRating.propTypes = { - rating: PropTypes.number.isRequired, - iconSize: PropTypes.number.isRequired -}; - -HeartRating.defaultProps = { - iconSize: 14 -}; - -export default HeartRating; diff --git a/frontend/src/Components/HeartRating.tsx b/frontend/src/Components/HeartRating.tsx new file mode 100644 index 000000000..774cb4239 --- /dev/null +++ b/frontend/src/Components/HeartRating.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import Icon, { IconProps } from 'Components/Icon'; +import Tooltip from 'Components/Tooltip/Tooltip'; +import { icons, kinds, tooltipPositions } from 'Helpers/Props'; +import translate from 'Utilities/String/translate'; +import styles from './HeartRating.css'; + +interface HeartRatingProps { + rating: number; + votes?: number; + iconSize?: IconProps['size']; +} + +function HeartRating({ rating, votes = 0, iconSize = 14 }: HeartRatingProps) { + return ( + + + {rating * 10}% + + } + tooltip={translate('CountVotes', { votes })} + kind={kinds.INVERSE} + position={tooltipPositions.TOP} + /> + ); +} + +export default HeartRating; diff --git a/frontend/src/Components/Icon.js b/frontend/src/Components/Icon.js deleted file mode 100644 index d200b8c08..000000000 --- a/frontend/src/Components/Icon.js +++ /dev/null @@ -1,73 +0,0 @@ -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React, { PureComponent } from 'react'; -import { kinds } from 'Helpers/Props'; -import styles from './Icon.css'; - -class Icon extends PureComponent { - - // - // Render - - render() { - const { - containerClassName, - className, - name, - kind, - size, - title, - isSpinning, - ...otherProps - } = this.props; - - const icon = ( - - ); - - if (title) { - return ( - - {icon} - - ); - } - - return icon; - } -} - -Icon.propTypes = { - containerClassName: PropTypes.string, - className: PropTypes.string, - name: PropTypes.object.isRequired, - kind: PropTypes.string.isRequired, - size: PropTypes.number.isRequired, - title: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), - isSpinning: PropTypes.bool.isRequired, - fixedWidth: PropTypes.bool.isRequired -}; - -Icon.defaultProps = { - kind: kinds.DEFAULT, - size: 14, - isSpinning: false, - fixedWidth: false -}; - -export default Icon; diff --git a/frontend/src/Components/Icon.tsx b/frontend/src/Components/Icon.tsx new file mode 100644 index 000000000..ea5279840 --- /dev/null +++ b/frontend/src/Components/Icon.tsx @@ -0,0 +1,60 @@ +import { + FontAwesomeIcon, + FontAwesomeIconProps, +} from '@fortawesome/react-fontawesome'; +import classNames from 'classnames'; +import React, { ComponentProps } from 'react'; +import { kinds } from 'Helpers/Props'; +import { Kind } from 'Helpers/Props/kinds'; +import styles from './Icon.css'; + +export interface IconProps + extends Omit< + FontAwesomeIconProps, + 'icon' | 'spin' | 'name' | 'title' | 'size' + > { + containerClassName?: ComponentProps<'span'>['className']; + name: FontAwesomeIconProps['icon']; + kind?: Extract; + size?: number; + isSpinning?: FontAwesomeIconProps['spin']; + title?: string | (() => string); +} + +export default function Icon({ + containerClassName, + className, + name, + kind = kinds.DEFAULT, + size = 14, + title, + isSpinning = false, + fixedWidth = false, + ...otherProps +}: IconProps) { + const icon = ( + + ); + + if (title) { + return ( + + {icon} + + ); + } + + return icon; +} diff --git a/frontend/src/System/Status/Health/Health.tsx b/frontend/src/System/Status/Health/Health.tsx index 281d95ac6..087146740 100644 --- a/frontend/src/System/Status/Health/Health.tsx +++ b/frontend/src/System/Status/Health/Health.tsx @@ -3,7 +3,7 @@ import { useDispatch, useSelector } from 'react-redux'; import AppState from 'App/State/AppState'; import Alert from 'Components/Alert'; import FieldSet from 'Components/FieldSet'; -import Icon from 'Components/Icon'; +import Icon, { IconProps } from 'Components/Icon'; import IconButton from 'Components/Link/IconButton'; import SpinnerIconButton from 'Components/Link/SpinnerIconButton'; import LoadingIndicator from 'Components/Loading/LoadingIndicator'; @@ -97,7 +97,7 @@ function Health() { {items.map((item) => { const source = item.source; - let kind = kinds.WARNING; + let kind: IconProps['kind'] = kinds.WARNING; switch (item.type.toLowerCase()) { case 'error': kind = kinds.DANGER; diff --git a/frontend/src/System/Tasks/Queued/QueuedTaskRow.tsx b/frontend/src/System/Tasks/Queued/QueuedTaskRow.tsx index 4511bcbf4..66d762039 100644 --- a/frontend/src/System/Tasks/Queued/QueuedTaskRow.tsx +++ b/frontend/src/System/Tasks/Queued/QueuedTaskRow.tsx @@ -2,7 +2,7 @@ import moment from 'moment'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { CommandBody } from 'Commands/Command'; -import Icon from 'Components/Icon'; +import Icon, { IconProps } from 'Components/Icon'; import IconButton from 'Components/Link/IconButton'; import ConfirmModal from 'Components/Modal/ConfirmModal'; import TableRowCell from 'Components/Table/Cells/TableRowCell'; @@ -19,7 +19,10 @@ import translate from 'Utilities/String/translate'; import QueuedTaskRowNameCell from './QueuedTaskRowNameCell'; import styles from './QueuedTaskRow.css'; -function getStatusIconProps(status: string, message: string | undefined) { +function getStatusIconProps( + status: string, + message: string | undefined +): IconProps { const title = titleCase(status); switch (status) {