Parcourir la source

Dashboard - convert some files to typescript (#5359)

* FileProgress.ts - convert to typescript

* Buttons.tsx - convert to typescript

Buttons.tsx - convert to typescript (partial)

Buttons.tsx - convert to typescript (partial)

Buttons.tsx - convert to typescript

* everywhere - remove `ComponentChild`

* FileItem.tsx - convert to typescript

* FileList.jsx - fix the comments, they got a bit shuffled some time ago

* FileList.tsx - convert to typscript

* Dashboard.tsx - add back missing props

* FileInfo.tsx - convert to typescript

FileInfo.tsx - convert to typescript (partial)

FileInfo.tsx - convert to typescript

* everywhere - remove excessive props
Evgenia Karunus il y a 9 mois
Parent
commit
18cd1db114

+ 1 - 1
packages/@uppy/dashboard/src/Dashboard.tsx

@@ -101,7 +101,7 @@ interface TargetWithRender extends Target {
   render: () => ComponentChild
 }
 
-interface DashboardState<M extends Meta, B extends Body> {
+export interface DashboardState<M extends Meta, B extends Body> {
   targets: Target[]
   activePickerPanel: Target | undefined
   showAddFilesPanel: boolean

+ 2 - 3
packages/@uppy/dashboard/src/components/Dashboard.tsx

@@ -170,11 +170,9 @@ export default function Dashboard(props: $TSFixMe) {
             showFileList ?
               <FileList
                 id={props.id}
-                error={props.error}
                 i18n={props.i18n}
                 uppy={props.uppy}
                 files={props.files}
-                acquirers={props.acquirers}
                 resumableUploads={props.resumableUploads}
                 hideRetryButton={props.hideRetryButton}
                 hidePauseResumeButton={props.hidePauseResumeButton}
@@ -183,7 +181,6 @@ export default function Dashboard(props: $TSFixMe) {
                 showRemoveButtonAfterComplete={
                   props.showRemoveButtonAfterComplete
                 }
-                isWide={props.isWide}
                 metaFields={props.metaFields}
                 toggleFileCard={props.toggleFileCard}
                 handleRequestThumbnail={props.handleRequestThumbnail}
@@ -195,6 +192,8 @@ export default function Dashboard(props: $TSFixMe) {
                 toggleAddFilesPanel={props.toggleAddFilesPanel}
                 isSingleFile={isSingleFile}
                 itemsPerRow={itemsPerRow}
+                containerWidth={props.containerWidth}
+                containerHeight={props.containerHeight}
               />
               // eslint-disable-next-line react/jsx-props-no-spreading
             : <AddFiles {...props} isSizeMD={isSizeMD} />

+ 60 - 25
packages/@uppy/dashboard/src/components/FileItem/Buttons/index.tsx

@@ -1,16 +1,27 @@
-import { h, type ComponentChild } from 'preact'
+import { h } from 'preact'
+import type { Body, Meta, UppyFile } from '@uppy/utils/lib/UppyFile'
+import type Uppy from '@uppy/core'
+import type { I18n } from '@uppy/utils/lib/Translator'
 import copyToClipboard from '../../../utils/copyToClipboard.ts'
+import type { DashboardState } from '../../../Dashboard.js'
 
 type $TSFixMe = any
 
-function EditButton({
+function EditButton<M extends Meta, B extends Body>({
   file,
   uploadInProgressOrComplete,
   metaFields,
   canEditFile,
   i18n,
   onClick,
-}: $TSFixMe) {
+}: {
+  file: UppyFile<M, B>
+  uploadInProgressOrComplete: boolean
+  metaFields: DashboardState<M, B>['metaFields']
+  canEditFile: (f: UppyFile<M, B>) => boolean
+  i18n: I18n
+  onClick: () => void
+}) {
   if (
     (!uploadInProgressOrComplete && metaFields && metaFields.length > 0) ||
     (!uploadInProgressOrComplete && canEditFile(file))
@@ -49,7 +60,15 @@ function EditButton({
   return null
 }
 
-function RemoveButton({ i18n, onClick, file }: $TSFixMe) {
+function RemoveButton<M extends Meta, B extends Body>({
+  i18n,
+  onClick,
+  file,
+}: {
+  i18n: I18n
+  onClick: () => void
+  file: UppyFile<M, B>
+}) {
   return (
     <button
       className="uppy-u-reset uppy-Dashboard-Item-action uppy-Dashboard-Item-action--remove"
@@ -76,22 +95,25 @@ function RemoveButton({ i18n, onClick, file }: $TSFixMe) {
   )
 }
 
-const copyLinkToClipboard = (event: $TSFixMe, props: $TSFixMe) => {
-  copyToClipboard(
-    props.file.uploadURL,
-    props.i18n('copyLinkToClipboardFallback'),
-  )
-    .then(() => {
-      props.uppy.log('Link copied to clipboard.')
-      props.uppy.info(props.i18n('copyLinkToClipboardSuccess'), 'info', 3000)
-    })
-    .catch(props.uppy.log)
-    // avoid losing focus
-    .then(() => event.target.focus({ preventScroll: true }))
-}
-
-function CopyLinkButton(props: $TSFixMe) {
-  const { i18n } = props
+function CopyLinkButton<M extends Meta, B extends Body>({
+  file,
+  uppy,
+  i18n,
+}: {
+  file: UppyFile<M, B>
+  uppy: Uppy<M, B>
+  i18n: I18n
+}) {
+  const copyLinkToClipboard = (event: $TSFixMe) => {
+    copyToClipboard(file.uploadURL, i18n('copyLinkToClipboardFallback'))
+      .then(() => {
+        uppy.log('Link copied to clipboard.')
+        uppy.info(i18n('copyLinkToClipboardSuccess'), 'info', 3000)
+      })
+      .catch(uppy.log)
+      // avoid losing focus
+      .then(() => event.target.focus({ preventScroll: true }))
+  }
 
   return (
     <button
@@ -99,7 +121,7 @@ function CopyLinkButton(props: $TSFixMe) {
       type="button"
       aria-label={i18n('copyLink')}
       title={i18n('copyLink')}
-      onClick={(event) => copyLinkToClipboard(event, props)}
+      onClick={(event) => copyLinkToClipboard(event)}
     >
       <svg
         aria-hidden="true"
@@ -115,7 +137,22 @@ function CopyLinkButton(props: $TSFixMe) {
   )
 }
 
-export default function Buttons(props: $TSFixMe): ComponentChild {
+type ButtonsProps<M extends Meta, B extends Body> = {
+  uppy: Uppy<M, B>
+  file: UppyFile<M, B>
+  i18n: I18n
+  uploadInProgressOrComplete: boolean
+  canEditFile: (file: UppyFile<M, B>) => boolean
+  metaFields: DashboardState<M, B>['metaFields']
+  showLinkToFileUploadResult: boolean
+  showRemoveButton: boolean
+  toggleFileCard: (show: boolean, fileId: string) => void
+  openFileEditor: (file: UppyFile<M, B>) => void
+}
+
+export default function Buttons<M extends Meta, B extends Body>(
+  props: ButtonsProps<M, B>,
+) {
   const {
     uppy,
     file,
@@ -128,7 +165,6 @@ export default function Buttons(props: $TSFixMe): ComponentChild {
     toggleFileCard,
     openFileEditor,
   } = props
-
   const editAction = () => {
     if (metaFields && metaFields.length > 0) {
       toggleFileCard(true, file.id)
@@ -154,8 +190,7 @@ export default function Buttons(props: $TSFixMe): ComponentChild {
         <RemoveButton
           i18n={i18n}
           file={file}
-          uppy={uppy}
-          onClick={() => uppy.removeFile(file.id, 'removed-by-user')}
+          onClick={() => uppy.removeFile(file.id)}
         />
       : null}
     </div>

+ 60 - 23
packages/@uppy/dashboard/src/components/FileItem/FileInfo/index.tsx

@@ -1,12 +1,18 @@
 /* eslint-disable react/destructuring-assignment */
-import { h, Fragment, type ComponentChild } from 'preact'
+import { h } from 'preact'
 import prettierBytes from '@transloadit/prettier-bytes'
 import truncateString from '@uppy/utils/lib/truncateString'
+import type { I18n } from '@uppy/utils/lib/Translator'
+import type { UppyFile } from '@uppy/core'
 import MetaErrorMessage from '../MetaErrorMessage.tsx'
+import type { DashboardState } from '../../../Dashboard.js'
 
-type $TSFixMe = any
-
-const renderFileName = (props: $TSFixMe) => {
+const renderFileName = (props: {
+  file: UppyFile<any, any>
+  isSingleFile: boolean
+  containerHeight: number
+  containerWidth: number
+}) => {
   const { author, name } = props.file.meta
 
   function getMaxNameLength() {
@@ -32,7 +38,7 @@ const renderFileName = (props: $TSFixMe) => {
   )
 }
 
-const renderAuthor = (props: $TSFixMe) => {
+const renderAuthor = (props: { file: UppyFile<any, any> }) => {
   const { author } = props.file.meta
   const providerName = props.file.remote?.providerName
   const dot = `\u00B7`
@@ -61,14 +67,18 @@ const renderAuthor = (props: $TSFixMe) => {
   )
 }
 
-const renderFileSize = (props: $TSFixMe) =>
+const renderFileSize = (props: { file: UppyFile<any, any> }) =>
   props.file.size && (
     <div className="uppy-Dashboard-Item-statusSize">
       {prettierBytes(props.file.size)}
     </div>
   )
 
-const ReSelectButton = (props: $TSFixMe) =>
+const ReSelectButton = (props: {
+  file: UppyFile<any, any>
+  toggleAddFilesPanel: () => void
+  i18n: I18n
+}) =>
   props.file.isGhost && (
     <span>
       {' \u2022 '}
@@ -82,7 +92,13 @@ const ReSelectButton = (props: $TSFixMe) =>
     </span>
   )
 
-const ErrorButton = ({ file, onClick }: $TSFixMe) => {
+const ErrorButton = ({
+  file,
+  onClick,
+}: {
+  file: UppyFile<any, any>
+  onClick: () => void
+}) => {
   if (file.error) {
     return (
       <button
@@ -100,31 +116,52 @@ const ErrorButton = ({ file, onClick }: $TSFixMe) => {
   return null
 }
 
-export default function FileInfo(props: $TSFixMe): ComponentChild {
-  const { file } = props
+type FileInfoProps = {
+  file: UppyFile<any, any>
+  containerWidth: number
+  containerHeight: number
+  i18n: I18n
+  toggleAddFilesPanel: () => void
+  toggleFileCard: (show: boolean, fileId: string) => void
+  metaFields: DashboardState<any, any>['metaFields']
+  isSingleFile: boolean
+}
+
+export default function FileInfo(props: FileInfoProps) {
+  const {
+    file,
+    i18n,
+    toggleFileCard,
+    metaFields,
+    toggleAddFilesPanel,
+    isSingleFile,
+    containerHeight,
+    containerWidth,
+  } = props
   return (
     <div
       className="uppy-Dashboard-Item-fileInfo"
       data-uppy-file-source={file.source}
     >
       <div className="uppy-Dashboard-Item-fileName">
-        {renderFileName(props)}
-        <ErrorButton
-          file={props.file}
-          // eslint-disable-next-line no-alert
-          onClick={() => alert(props.file.error)} // TODO: move to a custom alert implementation
-        />
+        {renderFileName({
+          file,
+          isSingleFile,
+          containerHeight,
+          containerWidth,
+        })}
+        <ErrorButton file={file} onClick={() => alert(file.error)} />
       </div>
       <div className="uppy-Dashboard-Item-status">
-        {renderAuthor(props)}
-        {renderFileSize(props)}
-        {ReSelectButton(props)}
+        {renderAuthor({ file })}
+        {renderFileSize({ file })}
+        {ReSelectButton({ file, toggleAddFilesPanel, i18n })}
       </div>
       <MetaErrorMessage
-        file={props.file}
-        i18n={props.i18n}
-        toggleFileCard={props.toggleFileCard}
-        metaFields={props.metaFields}
+        file={file}
+        i18n={i18n}
+        toggleFileCard={toggleFileCard}
+        metaFields={metaFields}
       />
     </div>
   )

+ 2 - 2
packages/@uppy/dashboard/src/components/FileItem/FilePreviewAndLink/index.tsx

@@ -1,11 +1,11 @@
-import { h, type ComponentChild } from 'preact'
+import { h } from 'preact'
 import FilePreview from '../../FilePreview.tsx'
 import MetaErrorMessage from '../MetaErrorMessage.tsx'
 import getFileTypeIcon from '../../../utils/getFileTypeIcon.tsx'
 
 type $TSFixMe = any
 
-export default function FilePreviewAndLink(props: $TSFixMe): ComponentChild {
+export default function FilePreviewAndLink(props: $TSFixMe) {
   const { file, i18n, toggleFileCard, metaFields, showLinkToFileUploadResult } =
     props
   const white = 'rgba(255, 255, 255, 0.5)'

+ 36 - 8
packages/@uppy/dashboard/src/components/FileItem/FileProgress/index.tsx

@@ -1,9 +1,27 @@
+/* eslint-disable react/no-unused-prop-types */
 /* eslint-disable react/destructuring-assignment */
+import type { State, Uppy, UppyFile } from '@uppy/core'
+import type { I18n } from '@uppy/utils/lib/Translator'
+import type { Body, Meta } from '@uppy/utils/lib/UppyFile'
 import { h, type ComponentChild } from 'preact'
 
-type $TSFixMe = any
+interface Props<M extends Meta, B extends Body> {
+  uppy: Uppy<M, B>
+  file: UppyFile<M, B>
+  isUploaded: boolean
+  error: string | false
+  recoveredState: State<M, B>['recoveredState']
+  hideRetryButton: boolean
+  hidePauseResumeButton: boolean
+  hideCancelButton: boolean
+  resumableUploads: boolean
+  individualCancellation: boolean
+  i18n: I18n
+}
 
-function onPauseResumeCancelRetry(props: $TSFixMe) {
+function onPauseResumeCancelRetry<M extends Meta, B extends Body>(
+  props: Props<M, B>,
+) {
   if (props.isUploaded) return
 
   if (props.error && !props.hideRetryButton) {
@@ -18,7 +36,9 @@ function onPauseResumeCancelRetry(props: $TSFixMe) {
   }
 }
 
-function progressIndicatorTitle(props: $TSFixMe) {
+function progressIndicatorTitle<M extends Meta, B extends Body>(
+  props: Props<M, B>,
+) {
   if (props.isUploaded) {
     return props.i18n('uploadComplete')
   }
@@ -40,7 +60,9 @@ function progressIndicatorTitle(props: $TSFixMe) {
   return ''
 }
 
-function ProgressIndicatorButton(props: $TSFixMe) {
+function ProgressIndicatorButton<M extends Meta, B extends Body>(
+  props: Props<M, B> & { children: ComponentChild },
+) {
   return (
     <div className="uppy-Dashboard-Item-progress">
       <button
@@ -56,7 +78,7 @@ function ProgressIndicatorButton(props: $TSFixMe) {
   )
 }
 
-function ProgressCircleContainer({ children }: $TSFixMe) {
+function ProgressCircleContainer({ children }: { children: ComponentChild }) {
   return (
     <svg
       aria-hidden="true"
@@ -71,7 +93,7 @@ function ProgressCircleContainer({ children }: $TSFixMe) {
   )
 }
 
-function ProgressCircle({ progress }: $TSFixMe) {
+function ProgressCircle({ progress }: { progress: number }) {
   // circle length equals 2 * PI * R
   const circleLength = 2 * Math.PI * 15
 
@@ -100,12 +122,18 @@ function ProgressCircle({ progress }: $TSFixMe) {
   )
 }
 
-export default function FileProgress(props: $TSFixMe): ComponentChild {
+export default function FileProgress<M extends Meta, B extends Body>(
+  props: Props<M, B>,
+) {
   // Nothing if upload has not started
   if (!props.file.progress.uploadStarted) {
     return null
   }
 
+  if (props.file.progress.percentage === undefined) {
+    return null
+  }
+
   // Green checkmark when complete
   if (props.isUploaded) {
     return (
@@ -125,7 +153,7 @@ export default function FileProgress(props: $TSFixMe): ComponentChild {
   }
 
   if (props.recoveredState) {
-    return undefined
+    return null
   }
 
   // Retry button for error

+ 37 - 14
packages/@uppy/dashboard/src/components/FileItem/index.tsx

@@ -1,18 +1,46 @@
-// eslint-disable-next-line @typescript-eslint/ban-ts-comment
-// @ts-nocheck Typing this file requires more work, skipping it to unblock the rest of the transition.
-
 /* eslint-disable react/destructuring-assignment */
 import { h, Component, type ComponentChild } from 'preact'
 import classNames from 'classnames'
 import { shallowEqualObjects } from 'shallow-equal'
+import type { UppyFile, Body, Meta } from '@uppy/utils/lib/UppyFile'
+import type { I18n } from '@uppy/utils/lib/Translator'
+import type Uppy from '@uppy/core'
+import type { State } from '@uppy/core'
 import FilePreviewAndLink from './FilePreviewAndLink/index.tsx'
 import FileProgress from './FileProgress/index.tsx'
 import FileInfo from './FileInfo/index.tsx'
 import Buttons from './Buttons/index.tsx'
+import type { DashboardState } from '../../Dashboard.js'
 
-type $TSFixMe = any
+type Props<M extends Meta, B extends Body> = {
+  file: UppyFile<M, B>
+  handleRequestThumbnail: (file: UppyFile<M, B>) => void
+  handleCancelThumbnail: (file: UppyFile<M, B>) => void
+  individualCancellation: boolean
+  showRemoveButtonAfterComplete: boolean
+  recoveredState: State<M, B>['recoveredState']
+  resumableUploads: boolean
+  i18n: I18n
+  role: string
+  showLinkToFileUploadResult: boolean
+  toggleFileCard: (show: boolean, fileId: string) => void
+  metaFields: DashboardState<M, B>['metaFields']
+  id: string
+  containerWidth: number
+  containerHeight: number
+  toggleAddFilesPanel: () => void
+  isSingleFile: boolean
+  hideRetryButton: boolean
+  hideCancelButton: boolean
+  hidePauseResumeButton: boolean
+  canEditFile: (file: UppyFile<M, B>) => boolean
+  openFileEditor: (file: UppyFile<M, B>) => void
+  uppy: Uppy<M, B>
+}
 
-export default class FileItem extends Component {
+export default class FileItem<M extends Meta, B extends Body> extends Component<
+  Props<M, B>
+> {
   componentDidMount(): void {
     const { file } = this.props
     if (!file.preview) {
@@ -20,7 +48,7 @@ export default class FileItem extends Component {
     }
   }
 
-  shouldComponentUpdate(nextProps: $TSFixMe): boolean {
+  shouldComponentUpdate(nextProps: Props<M, B>): boolean {
     return !shallowEqualObjects(this.props, nextProps)
   }
 
@@ -45,9 +73,9 @@ export default class FileItem extends Component {
 
     const isProcessing = file.progress.preprocess || file.progress.postprocess
     const isUploaded =
-      file.progress.uploadComplete && !isProcessing && !file.error
+      !!file.progress.uploadComplete && !isProcessing && !file.error
     const uploadInProgressOrComplete =
-      file.progress.uploadStarted || isProcessing
+      !!file.progress.uploadStarted || !!isProcessing
     const uploadInProgress =
       (file.progress.uploadStarted && !file.progress.uploadComplete) ||
       isProcessing
@@ -91,7 +119,7 @@ export default class FileItem extends Component {
             toggleFileCard={this.props.toggleFileCard}
             metaFields={this.props.metaFields}
           />
-          <FileProgress
+          <FileProgress<M, B>
             uppy={this.props.uppy}
             file={file}
             error={error}
@@ -100,9 +128,6 @@ export default class FileItem extends Component {
             hideCancelButton={this.props.hideCancelButton}
             hidePauseResumeButton={this.props.hidePauseResumeButton}
             recoveredState={this.props.recoveredState}
-            showRemoveButtonAfterComplete={
-              this.props.showRemoveButtonAfterComplete
-            }
             resumableUploads={this.props.resumableUploads}
             individualCancellation={this.props.individualCancellation}
             i18n={this.props.i18n}
@@ -112,8 +137,6 @@ export default class FileItem extends Component {
         <div className="uppy-Dashboard-Item-fileInfoAndButtons">
           <FileInfo
             file={file}
-            id={this.props.id}
-            acquirers={this.props.acquirers}
             containerWidth={this.props.containerWidth}
             containerHeight={this.props.containerHeight}
             i18n={this.props.i18n}

+ 47 - 23
packages/@uppy/dashboard/src/components/FileList.tsx

@@ -3,14 +3,42 @@ import { useMemo } from 'preact/hooks'
 // eslint-disable-next-line @typescript-eslint/ban-ts-comment
 // @ts-ignore untyped
 import VirtualList from '@uppy/utils/lib/VirtualList'
+import type { UppyFile, Uppy, State } from '@uppy/core'
+import type { I18n } from '@uppy/utils/lib/Translator'
+import type { Body, Meta } from '@uppy/utils/lib/UppyFile'
 import FileItem from './FileItem/index.tsx'
+import type { DashboardState } from '../Dashboard.js'
 
-type $TSFixMe = any
+type FileListProps<M extends Meta, B extends Body> = {
+  id: string
+  i18n: I18n
+  uppy: Uppy<M, B>
+  files: State<M, B>['files']
+  resumableUploads: boolean
+  hideRetryButton: boolean
+  hidePauseResumeButton: boolean
+  hideCancelButton: boolean
+  showLinkToFileUploadResult: boolean
+  showRemoveButtonAfterComplete: boolean
+  metaFields: DashboardState<M, B>['metaFields']
+  isSingleFile: boolean
+  toggleFileCard: (show: boolean, fileId: string) => void
+  handleRequestThumbnail: (file: UppyFile<M, B>) => void
+  handleCancelThumbnail: (file: UppyFile<M, B>) => void
+  recoveredState: State<M, B>['recoveredState']
+  individualCancellation: boolean
+  itemsPerRow: number
+  openFileEditor: (file: UppyFile<M, B>) => void
+  canEditFile: (file: UppyFile<M, B>) => boolean
+  toggleAddFilesPanel: () => void
+  containerWidth: number
+  containerHeight: number
+}
 
-function chunks(list: $TSFixMe, size: $TSFixMe) {
-  const chunked: $TSFixMe[] = []
-  let currentChunk: $TSFixMe[] = []
-  list.forEach((item: $TSFixMe) => {
+function chunks<T>(list: T[], size: number): T[][] {
+  const chunked: T[][] = []
+  let currentChunk: T[] = []
+  list.forEach((item: T) => {
     if (currentChunk.length < size) {
       currentChunk.push(item)
     } else {
@@ -22,20 +50,17 @@ function chunks(list: $TSFixMe, size: $TSFixMe) {
   return chunked
 }
 
-export default function FileList({
+export default function FileList<M extends Meta, B extends Body>({
   id,
-  error,
   i18n,
   uppy,
   files,
-  acquirers,
   resumableUploads,
   hideRetryButton,
   hidePauseResumeButton,
   hideCancelButton,
   showLinkToFileUploadResult,
   showRemoveButtonAfterComplete,
-  isWide,
   metaFields,
   isSingleFile,
   toggleFileCard,
@@ -49,7 +74,7 @@ export default function FileList({
   toggleAddFilesPanel,
   containerWidth,
   containerHeight,
-}: $TSFixMe) {
+}: FileListProps<M, B>) {
   // It's not great that this is hardcoded!
   // It's ESPECIALLY not great that this is checking against `itemsPerRow`!
   const rowHeight =
@@ -61,32 +86,32 @@ export default function FileList({
 
   // Sort files by file.isGhost, ghost files first, only if recoveredState is present
   const rows = useMemo(() => {
-    const sortByGhostComesFirst = (file1: $TSFixMe, file2: $TSFixMe) =>
-      files[file2].isGhost - files[file1].isGhost
+    const sortByGhostComesFirst = (file1: string, file2: string) =>
+      Number(files[file2].isGhost) - Number(files[file1].isGhost)
 
     const fileIds = Object.keys(files)
     if (recoveredState) fileIds.sort(sortByGhostComesFirst)
     return chunks(fileIds, itemsPerRow)
   }, [files, itemsPerRow, recoveredState])
 
-  const renderRow = (
-    row: $TSFixMe, // The `role="presentation` attribute ensures that the list items are properly
-  ) => (
-    // associated with the `VirtualList` element.
-    // We use the first file ID as the key—this should not change across scroll rerenders
-    <div class="uppy-Dashboard-filesInner" role="presentation" key={row[0]}>
-      {row.map((fileID: $TSFixMe) => (
+  const renderRow = (row: string[]) => (
+    <div
+      class="uppy-Dashboard-filesInner"
+      // The `role="presentation` attribute ensures that the list items are properly
+      // associated with the `VirtualList` element.
+      role="presentation"
+      // We use the first file ID as the key — this should not change across scroll rerenders.
+      key={row[0]}
+    >
+      {row.map((fileID: string) => (
         <FileItem
           key={fileID}
-          // @ts-expect-error it's fine
           uppy={uppy}
           // FIXME This is confusing, it's actually the Dashboard's plugin ID
           id={id}
-          error={error}
           // TODO move this to context
           i18n={i18n}
           // features
-          acquirers={acquirers}
           resumableUploads={resumableUploads}
           individualCancellation={individualCancellation}
           // visual options
@@ -95,7 +120,6 @@ export default function FileList({
           hideCancelButton={hideCancelButton}
           showLinkToFileUploadResult={showLinkToFileUploadResult}
           showRemoveButtonAfterComplete={showRemoveButtonAfterComplete}
-          isWide={isWide}
           metaFields={metaFields}
           recoveredState={recoveredState}
           isSingleFile={isSingleFile}

+ 1 - 0
packages/@uppy/utils/src/FileProgress.ts

@@ -12,6 +12,7 @@ export type FileProcessingInfo =
   | IndeterminateFileProcessing
   | DeterminateFileProcessing
 
+// TODO explore whether all of these properties need to be optional
 interface FileProgressBase {
   uploadComplete?: boolean
   percentage?: number