From 9e6db89311513400c3244bd2f40fdf3e3759b0cf Mon Sep 17 00:00:00 2001 From: David <33458145+davidmcpowell@users.noreply.github.com> Date: Wed, 13 Aug 2025 10:21:19 +0100 Subject: [PATCH] Merge pull request #27811 from overleaf/dp-file-view-typescript Convert file-view components and test files to typescript GitOrigin-RevId: 277aa8fd4f3d06a322dc9d0b372eebefb26fd285 --- .../file-view/components/file-view-icons.jsx | 12 ---- .../file-view/components/file-view-icons.tsx | 14 +++++ .../{file-view.jsx => file-view.tsx} | 18 ++---- .../features/file-view/types/binary-file.ts | 2 +- .../js/features/ide-react/util/file-view.ts | 4 +- .../js/shared/components/material-icon.tsx | 2 +- ...der.test.jsx => file-view-header.test.tsx} | 43 ++----------- ...mage.test.jsx => file-view-image.test.tsx} | 13 +--- ....jsx => file-view-refresh-button.test.tsx} | 19 +----- .../file-view-refresh-error.test.tsx | 22 +------ ...-text.test.jsx => file-view-text.test.tsx} | 16 +---- ...{file-view.test.jsx => file-view.test.tsx} | 23 +------ .../frontend/features/file-view/util/files.ts | 62 +++++++++++++++++++ 13 files changed, 99 insertions(+), 151 deletions(-) delete mode 100644 services/web/frontend/js/features/file-view/components/file-view-icons.jsx create mode 100644 services/web/frontend/js/features/file-view/components/file-view-icons.tsx rename services/web/frontend/js/features/file-view/components/{file-view.jsx => file-view.tsx} (83%) rename services/web/test/frontend/features/file-view/components/{file-view-header.test.jsx => file-view-header.test.tsx} (72%) rename services/web/test/frontend/features/file-view/components/{file-view-image.test.jsx => file-view-image.test.tsx} (57%) rename services/web/test/frontend/features/file-view/components/{file-view-refresh-button.test.jsx => file-view-refresh-button.test.tsx} (66%) rename services/web/test/frontend/features/file-view/components/{file-view-text.test.jsx => file-view-text.test.tsx} (67%) rename services/web/test/frontend/features/file-view/components/{file-view.test.jsx => file-view.test.tsx} (78%) create mode 100644 services/web/test/frontend/features/file-view/util/files.ts diff --git a/services/web/frontend/js/features/file-view/components/file-view-icons.jsx b/services/web/frontend/js/features/file-view/components/file-view-icons.jsx deleted file mode 100644 index 8016d098cb..0000000000 --- a/services/web/frontend/js/features/file-view/components/file-view-icons.jsx +++ /dev/null @@ -1,12 +0,0 @@ -import MaterialIcon from '@/shared/components/material-icon' - -export const LinkedFileIcon = props => { - return ( - - ) -} diff --git a/services/web/frontend/js/features/file-view/components/file-view-icons.tsx b/services/web/frontend/js/features/file-view/components/file-view-icons.tsx new file mode 100644 index 0000000000..168f7be5e8 --- /dev/null +++ b/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 +) => { + return ( + + ) +} diff --git a/services/web/frontend/js/features/file-view/components/file-view.jsx b/services/web/frontend/js/features/file-view/components/file-view.tsx similarity index 83% rename from services/web/frontend/js/features/file-view/components/file-view.jsx rename to services/web/frontend/js/features/file-view/components/file-view.tsx index 711d125fe9..b84ee383eb 100644 --- a/services/web/frontend/js/features/file-view/components/file-view.jsx +++ b/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() { ) } - -FileView.propTypes = { - file: PropTypes.shape({ - id: PropTypes.string, - name: PropTypes.string, - hash: PropTypes.string, - }).isRequired, -} diff --git a/services/web/frontend/js/features/file-view/types/binary-file.ts b/services/web/frontend/js/features/file-view/types/binary-file.ts index a52bc0edb9..4a4104615c 100644 --- a/services/web/frontend/js/features/file-view/types/binary-file.ts +++ b/services/web/frontend/js/features/file-view/types/binary-file.ts @@ -21,7 +21,7 @@ export type BinaryFile = { _id: string name: string - created: Date + created: Date | string id: string type: string selected: boolean diff --git a/services/web/frontend/js/features/ide-react/util/file-view.ts b/services/web/frontend/js/features/ide-react/util/file-view.ts index e32f3bb4ee..7247450c71 100644 --- a/services/web/frontend/js/features/ide-react/util/file-view.ts +++ b/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(), } } diff --git a/services/web/frontend/js/shared/components/material-icon.tsx b/services/web/frontend/js/shared/components/material-icon.tsx index be351d16bb..a5bd97e83c 100644 --- a/services/web/frontend/js/shared/components/material-icon.tsx +++ b/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, diff --git a/services/web/test/frontend/features/file-view/components/file-view-header.test.jsx b/services/web/test/frontend/features/file-view/components/file-view-header.test.tsx similarity index 72% rename from services/web/test/frontend/features/file-view/components/file-view-header.test.jsx rename to services/web/test/frontend/features/file-view/components/file-view-header.test.tsx index 11a61d9905..730727e33e 100644 --- a/services/web/test/frontend/features/file-view/components/file-view-header.test.jsx +++ b/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('', 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('', function () { }) it('Renders the correct text for a file with the project_file provider', function () { - renderWithEditorContext() + renderWithEditorContext() 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('', function () { }) it('Renders the correct text for a file with the project_output_file provider', function () { - renderWithEditorContext( - {}} - /> - ) + renderWithEditorContext() 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('', 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', diff --git a/services/web/test/frontend/features/file-view/components/file-view-image.test.jsx b/services/web/test/frontend/features/file-view/components/file-view-image.test.tsx similarity index 57% rename from services/web/test/frontend/features/file-view/components/file-view-image.test.jsx rename to services/web/test/frontend/features/file-view/components/file-view-image.test.tsx index 7ff204cc2e..7a2486a0b2 100644 --- a/services/web/test/frontend/features/file-view/components/file-view-image.test.jsx +++ b/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('', 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( - {}} onLoad={() => {}} /> + {}} onLoad={() => {}} /> ) screen.getByRole('img') }) diff --git a/services/web/test/frontend/features/file-view/components/file-view-refresh-button.test.jsx b/services/web/test/frontend/features/file-view/components/file-view-refresh-button.test.tsx similarity index 66% rename from services/web/test/frontend/features/file-view/components/file-view-refresh-button.test.jsx rename to services/web/test/frontend/features/file-view/components/file-view-refresh-button.test.tsx index da29252be0..c421f94771 100644 --- a/services/web/test/frontend/features/file-view/components/file-view-refresh-button.test.jsx +++ b/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('', 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('', function () { ) renderWithEditorContext( - + ) fireEvent.click(screen.getByRole('button', { name: 'Refresh' })) diff --git a/services/web/test/frontend/features/file-view/components/file-view-refresh-error.test.tsx b/services/web/test/frontend/features/file-view/components/file-view-refresh-error.test.tsx index c4aa796fcc..8d66975655 100644 --- a/services/web/test/frontend/features/file-view/components/file-view-refresh-error.test.tsx +++ b/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('', 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( - + ) screen.getByText('Access Denied: An error message') diff --git a/services/web/test/frontend/features/file-view/components/file-view-text.test.jsx b/services/web/test/frontend/features/file-view/components/file-view-text.test.tsx similarity index 67% rename from services/web/test/frontend/features/file-view/components/file-view-text.test.jsx rename to services/web/test/frontend/features/file-view/components/file-view-text.test.tsx index 1ca50fbb8c..6c59bf3fe8 100644 --- a/services/web/test/frontend/features/file-view/components/file-view-text.test.jsx +++ b/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('', 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('', function () { ) renderWithEditorContext( - {}} onLoad={() => {}} /> + {}} onLoad={() => {}} /> ) await screen.findByText('Text file content', { exact: false }) diff --git a/services/web/test/frontend/features/file-view/components/file-view.test.jsx b/services/web/test/frontend/features/file-view/components/file-view.test.tsx similarity index 78% rename from services/web/test/frontend/features/file-view/components/file-view.test.jsx rename to services/web/test/frontend/features/file-view/components/file-view.test.tsx index 7159f1ceaf..a3b6de7661 100644 --- a/services/web/test/frontend/features/file-view/components/file-view.test.jsx +++ b/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('', 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) diff --git a/services/web/test/frontend/features/file-view/util/files.ts b/services/web/test/frontend/features/file-view/util/files.ts new file mode 100644 index 0000000000..ff9355d721 --- /dev/null +++ b/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, +}