Просмотр исходного кода

Merge pull request #27811 from overleaf/dp-file-view-typescript

Convert file-view components and test files to typescript

GitOrigin-RevId: 277aa8fd4f3d06a322dc9d0b372eebefb26fd285
David 1 год назад
Родитель
Сommit
9e6db89311

+ 0 - 12
services/web/frontend/js/features/file-view/components/file-view-icons.jsx

@@ -1,12 +0,0 @@
-import MaterialIcon from '@/shared/components/material-icon'
-
-export const LinkedFileIcon = props => {
-  return (
-    <MaterialIcon
-      type="open_in_new"
-      modifier="rotate-180"
-      className="align-middle linked-file-icon"
-      {...props}
-    />
-  )
-}

+ 14 - 0
services/web/frontend/js/features/file-view/components/file-view-icons.tsx

@@ -0,0 +1,14 @@
+import MaterialIcon, { IconProps } from '@/shared/components/material-icon'
+
+export const LinkedFileIcon = (
+  props: Omit<IconProps, 'type' | 'modifier' | 'className' | 'unfilled'>
+) => {
+  return (
+    <MaterialIcon
+      type="open_in_new"
+      modifier="rotate-180"
+      className="align-middle linked-file-icon"
+      {...props}
+    />
+  )
+}

+ 5 - 13
services/web/frontend/js/features/file-view/components/file-view.jsx → services/web/frontend/js/features/file-view/components/file-view.tsx

@@ -1,5 +1,4 @@
 import { useState, useCallback } from 'react'
-import PropTypes from 'prop-types'
 import { useTranslation } from 'react-i18next'
 
 import FileViewHeader from './file-view-header'
@@ -8,10 +7,11 @@ import FileViewPdf from './file-view-pdf'
 import FileViewText from './file-view-text'
 import LoadingSpinner from '@/shared/components/loading-spinner'
 import getMeta from '@/utils/meta'
+import { BinaryFile } from '../types/binary-file'
 
 const imageExtensions = ['png', 'jpg', 'jpeg', 'gif']
 
-export default function FileView({ file }) {
+export default function FileView({ file }: { file: BinaryFile }) {
   const [contentLoading, setContentLoading] = useState(true)
   const [hasError, setHasError] = useState(false)
 
@@ -19,13 +19,13 @@ export default function FileView({ file }) {
 
   const { textExtensions, editableFilenames } = getMeta('ol-ExposedSettings')
 
-  const extension = file.name.split('.').pop().toLowerCase()
+  const extension = file.name.split('.')?.pop()?.toLowerCase()
 
   const isEditableTextFile =
-    textExtensions.includes(extension) ||
+    (extension && textExtensions.includes(extension)) ||
     editableFilenames.includes(file.name.toLowerCase())
 
-  const isImageFile = imageExtensions.includes(extension)
+  const isImageFile = !!extension && imageExtensions.includes(extension)
   const isPdfFile = extension === 'pdf'
   const isUnpreviewableFile = !isEditableTextFile && !isImageFile && !isPdfFile
 
@@ -80,11 +80,3 @@ function FileViewLoadingIndicator() {
     </div>
   )
 }
-
-FileView.propTypes = {
-  file: PropTypes.shape({
-    id: PropTypes.string,
-    name: PropTypes.string,
-    hash: PropTypes.string,
-  }).isRequired,
-}

+ 1 - 1
services/web/frontend/js/features/file-view/types/binary-file.ts

@@ -21,7 +21,7 @@ export type BinaryFile<T extends keyof LinkedFileData = keyof LinkedFileData> =
   {
     _id: string
     name: string
-    created: Date
+    created: Date | string
     id: string
     type: string
     selected: boolean

+ 2 - 2
services/web/frontend/js/features/ide-react/util/file-view.ts

@@ -22,10 +22,10 @@ export function convertFileRefToBinaryFile(fileRef: FileRef): BinaryFile {
 // is the only one making runtime complaints and it seems that other uses of
 // `FileViewHeader` pass in a string for `created`, so that's what this function
 // does too.
-export function fileViewFile(fileRef: FileRef) {
+export function fileViewFile(fileRef: FileRef): BinaryFile {
   const converted = convertFileRefToBinaryFile(fileRef)
   return {
     ...converted,
-    created: converted.created.toISOString(),
+    created: (converted.created as Date).toISOString(),
   }
 }

+ 1 - 1
services/web/frontend/js/shared/components/material-icon.tsx

@@ -20,7 +20,7 @@ type UnfilledIconProps = BaseIconProps & {
   unfilled: true
 }
 
-type IconProps = FilledIconProps | UnfilledIconProps
+export type IconProps = FilledIconProps | UnfilledIconProps
 
 function MaterialIcon({
   type,

+ 5 - 38
services/web/test/frontend/features/file-view/components/file-view-header.test.jsx → services/web/test/frontend/features/file-view/components/file-view-header.test.tsx

@@ -5,39 +5,9 @@ import { renderWithEditorContext } from '../../../helpers/render-with-context'
 import FileViewHeader from '../../../../../frontend/js/features/file-view/components/file-view-header'
 import { USER_ID } from '../../../helpers/editor-providers'
 import { fileViewFile } from '@/features/ide-react/util/file-view'
+import { projectOutputFile, textFile, urlFile } from '../util/files'
 
 describe('<FileViewHeader/>', function () {
-  const urlFile = {
-    name: 'example.tex',
-    linkedFileData: {
-      url: 'https://overleaf.com',
-      provider: 'url',
-    },
-    created: new Date(2021, 1, 17, 3, 24).toISOString(),
-  }
-
-  const projectFile = {
-    name: 'example.tex',
-    linkedFileData: {
-      v1_source_doc_id: 'v1-source-id',
-      source_project_id: 'source-project-id',
-      source_entity_path: '/source-entity-path.ext',
-      provider: 'project_file',
-      importer_id: USER_ID,
-    },
-    created: new Date(2021, 1, 17, 3, 24).toISOString(),
-  }
-
-  const projectOutputFile = {
-    name: 'example.pdf',
-    linkedFileData: {
-      v1_source_doc_id: 'v1-source-id',
-      source_output_file_path: '/source-entity-path.ext',
-      provider: 'project_output_file',
-    },
-    created: new Date(2021, 1, 17, 3, 24).toISOString(),
-  }
-
   beforeEach(function () {
     fetchMock.removeRoutes().clearHistory()
   })
@@ -52,7 +22,7 @@ describe('<FileViewHeader/>', function () {
     })
 
     it('Renders the correct text for a file with the project_file provider', function () {
-      renderWithEditorContext(<FileViewHeader file={projectFile} />)
+      renderWithEditorContext(<FileViewHeader file={textFile} />)
       screen.getByText('Imported from', { exact: false })
       screen.getByText('Another project', { exact: false })
       screen.getByText('/source-entity-path.ext, at 3:24 am Wed, 17th Feb 21', {
@@ -61,12 +31,7 @@ describe('<FileViewHeader/>', function () {
     })
 
     it('Renders the correct text for a file with the project_output_file provider', function () {
-      renderWithEditorContext(
-        <FileViewHeader
-          file={projectOutputFile}
-          storeReferencesKeys={() => {}}
-        />
-      )
+      renderWithEditorContext(<FileViewHeader file={projectOutputFile} />)
       screen.getByText('Imported from the output of', { exact: false })
       screen.getByText('Another project', { exact: false })
       screen.getByText('/source-entity-path.ext, at 3:24 am Wed, 17th Feb 21', {
@@ -85,6 +50,8 @@ describe('<FileViewHeader/>', function () {
 
   it('should use importedAt as timestamp when present in the linked file data', function () {
     const fileFromServer = {
+      _id: 'some-id',
+      hash: 'some-hash',
       name: 'example.tex',
       linkedFileData: {
         v1_source_doc_id: 'v1-source-id',

+ 2 - 11
services/web/test/frontend/features/file-view/components/file-view-image.test.jsx → services/web/test/frontend/features/file-view/components/file-view-image.test.tsx

@@ -2,21 +2,12 @@ import { screen } from '@testing-library/react'
 
 import { renderWithEditorContext } from '../../../helpers/render-with-context'
 import FileViewImage from '../../../../../frontend/js/features/file-view/components/file-view-image'
+import { imageFile } from '../util/files'
 
 describe('<FileViewImage />', function () {
-  const file = {
-    id: '60097ca20454610027c442a8',
-    name: 'file.jpg',
-    hash: 'hash',
-    linkedFileData: {
-      source_entity_path: '/source-entity-path',
-      provider: 'project_file',
-    },
-  }
-
   it('renders an image', function () {
     renderWithEditorContext(
-      <FileViewImage file={file} onError={() => {}} onLoad={() => {}} />
+      <FileViewImage file={imageFile} onError={() => {}} onLoad={() => {}} />
     )
     screen.getByRole('img')
   })

+ 2 - 17
services/web/test/frontend/features/file-view/components/file-view-refresh-button.test.jsx → services/web/test/frontend/features/file-view/components/file-view-refresh-button.test.tsx

@@ -7,21 +7,9 @@ import fetchMock from 'fetch-mock'
 import sinon from 'sinon'
 import FileViewRefreshButton from '@/features/file-view/components/file-view-refresh-button'
 import { renderWithEditorContext } from '../../../helpers/render-with-context'
-import { USER_ID } from '../../../helpers/editor-providers'
+import { textFile } from '../util/files'
 
 describe('<FileViewRefreshButton />', function () {
-  const projectFile = {
-    name: 'example.tex',
-    linkedFileData: {
-      v1_source_doc_id: 'v1-source-id',
-      source_project_id: 'source-project-id',
-      source_entity_path: '/source-entity-path.ext',
-      provider: 'project_file',
-      importer_id: USER_ID,
-    },
-    created: new Date(2021, 1, 17, 3, 24).toISOString(),
-  }
-
   beforeEach(function () {
     fetchMock.removeRoutes().clearHistory()
   })
@@ -36,10 +24,7 @@ describe('<FileViewRefreshButton />', function () {
     )
 
     renderWithEditorContext(
-      <FileViewRefreshButton
-        file={projectFile}
-        setRefreshError={sinon.stub()}
-      />
+      <FileViewRefreshButton file={textFile} setRefreshError={sinon.stub()} />
     )
 
     fireEvent.click(screen.getByRole('button', { name: 'Refresh' }))

+ 2 - 20
services/web/test/frontend/features/file-view/components/file-view-refresh-error.test.tsx

@@ -1,29 +1,11 @@
 import { render, screen } from '@testing-library/react'
 import FileViewRefreshError from '@/features/file-view/components/file-view-refresh-error'
-import type { BinaryFile } from '@/features/file-view/types/binary-file'
+import { imageFile } from '../util/files'
 
 describe('<FileViewRefreshError />', function () {
   it('shows correct error message', function () {
-    const anotherProjectFile: BinaryFile<'project_file'> = {
-      id: '123abc',
-      _id: '123abc',
-      linkedFileData: {
-        provider: 'project_file',
-        source_project_id: 'some-id',
-        source_entity_path: '/path/',
-      },
-      created: new Date(2023, 1, 17, 3, 24),
-      name: 'frog.jpg',
-      type: 'file',
-      selected: true,
-      hash: '42',
-    }
-
     render(
-      <FileViewRefreshError
-        file={anotherProjectFile}
-        refreshError="An error message"
-      />
+      <FileViewRefreshError file={imageFile} refreshError="An error message" />
     )
 
     screen.getByText('Access Denied: An error message')

+ 2 - 14
services/web/test/frontend/features/file-view/components/file-view-text.test.jsx → services/web/test/frontend/features/file-view/components/file-view-text.test.tsx

@@ -3,21 +3,9 @@ import fetchMock from 'fetch-mock'
 
 import { renderWithEditorContext } from '../../../helpers/render-with-context'
 import FileViewText from '../../../../../frontend/js/features/file-view/components/file-view-text'
+import { textFile } from '../util/files'
 
 describe('<FileViewText/>', function () {
-  const file = {
-    id: '123',
-    hash: '1234',
-    name: 'example.tex',
-    linkedFileData: {
-      v1_source_doc_id: 'v1-source-id',
-      source_project_id: 'source-project-id',
-      source_entity_path: '/source-entity-path.ext',
-      provider: 'project_file',
-    },
-    created: new Date(2021, 1, 17, 3, 24).toISOString(),
-  }
-
   beforeEach(function () {
     fetchMock.removeRoutes().clearHistory()
     window.metaAttributesCache.set('ol-preventCompileOnLoad', true)
@@ -34,7 +22,7 @@ describe('<FileViewText/>', function () {
     )
 
     renderWithEditorContext(
-      <FileViewText file={file} onError={() => {}} onLoad={() => {}} />
+      <FileViewText file={textFile} onError={() => {}} onLoad={() => {}} />
     )
 
     await screen.findByText('Text file content', { exact: false })

+ 1 - 22
services/web/test/frontend/features/file-view/components/file-view.test.jsx → services/web/test/frontend/features/file-view/components/file-view.test.tsx

@@ -7,30 +7,9 @@ import fetchMock from 'fetch-mock'
 
 import { renderWithEditorContext } from '../../../helpers/render-with-context'
 import FileView from '../../../../../frontend/js/features/file-view/components/file-view'
+import { imageFile, textFile } from '../util/files'
 
 describe('<FileView/>', function () {
-  const textFile = {
-    id: 'text-file',
-    name: 'example.tex',
-    linkedFileData: {
-      v1_source_doc_id: 'v1-source-id',
-      source_project_id: 'source-project-id',
-      source_entity_path: '/source-entity-path.ext',
-      provider: 'project_file',
-    },
-    hash: '012345678901234567890123',
-    created: new Date(2021, 1, 17, 3, 24).toISOString(),
-  }
-
-  const imageFile = {
-    id: '60097ca20454610027c442a8',
-    name: 'file.jpg',
-    linkedFileData: {
-      source_entity_path: '/source-entity-path',
-      provider: 'project_file',
-    },
-  }
-
   beforeEach(function () {
     fetchMock.removeRoutes().clearHistory()
     window.metaAttributesCache.set('ol-preventCompileOnLoad', true)

+ 62 - 0
services/web/test/frontend/features/file-view/util/files.ts

@@ -0,0 +1,62 @@
+import { BinaryFile } from '@/features/file-view/types/binary-file'
+
+export const textFile: BinaryFile<'project_file'> = {
+  _id: 'text-file',
+  id: 'text-file',
+  name: 'example.tex',
+  linkedFileData: {
+    v1_source_doc_id: 'v1-source-id',
+    source_project_id: 'source-project-id',
+    source_entity_path: '/source-entity-path.ext',
+    provider: 'project_file',
+  },
+  hash: '012345678901234567890123',
+  created: new Date(2021, 1, 17, 3, 24).toISOString(),
+  type: 'file',
+  selected: true,
+}
+
+export const imageFile: BinaryFile<'project_file'> = {
+  _id: '60097ca20454610027c442a8',
+  id: '60097ca20454610027c442a8',
+  name: 'file.jpg',
+  linkedFileData: {
+    source_project_id: 'source-project-id',
+    source_entity_path: '/source-entity-path',
+    provider: 'project_file',
+  },
+  hash: '012345678901234567890123',
+  created: new Date(2021, 1, 17, 3, 24).toISOString(),
+  type: 'file',
+  selected: true,
+}
+
+export const urlFile: BinaryFile<'url'> = {
+  _id: 'url-file',
+  id: 'url-file',
+  name: 'example.tex',
+  linkedFileData: {
+    url: 'https://overleaf.com',
+    provider: 'url',
+  },
+  created: new Date(2021, 1, 17, 3, 24).toISOString(),
+  hash: 'some-hash',
+  type: 'file',
+  selected: true,
+}
+
+export const projectOutputFile: BinaryFile<'project_output_file'> = {
+  _id: 'project-output-file',
+  id: 'project-output-file',
+  name: 'example.pdf',
+  linkedFileData: {
+    v1_source_doc_id: 'v1-source-id',
+    source_output_file_path: '/source-entity-path.ext',
+    provider: 'project_output_file',
+    source_project_id: 'source-project-id',
+  },
+  created: new Date(2021, 1, 17, 3, 24).toISOString(),
+  hash: 'some-hash',
+  type: 'file',
+  selected: true,
+}