Pārlūkot izejas kodu

[ui-sb] fixes file preview for non-readable file (#4061)

Ram Prasad Agarwal 6 mēneši atpakaļ
vecāks
revīzija
7f9d2936e6

+ 1 - 1
desktop/core/src/desktop/js/apps/storageBrowser/StorageFilePage/StorageFilePage.scss

@@ -130,7 +130,7 @@
         height: 90%;
       }
 
-      .preview__compressed {
+      .preview__unsupported {
         font-size: vars.$font-size-lg;
       }
     }

+ 35 - 57
desktop/core/src/desktop/js/apps/storageBrowser/StorageFilePage/StorageFilePage.test.tsx

@@ -37,17 +37,20 @@ jest.mock('../../../api/utils', () => ({
   post: () => mockSave()
 }));
 
-const mockData = jest.fn().mockReturnValue({
-  contents: 'Initial file content',
-  compression: 'none'
+const mockLoadData = jest.fn().mockReturnValue({
+  contents: 'Initial file content'
 });
 
-jest.mock('../../../utils/hooks/useLoadData/useLoadData', () => {
-  return jest.fn(() => ({
-    data: mockData(),
-    loading: false
-  }));
-});
+const mockError = jest.fn().mockImplementationOnce(() => null);
+
+jest.mock('../../../utils/hooks/useLoadData/useLoadData', () => ({
+  __esModule: true,
+  default: jest.fn(() => ({
+    data: mockLoadData(),
+    loading: false,
+    error: mockError()
+  }))
+}));
 
 const mockConfig = {
   storage_browser: {
@@ -72,14 +75,11 @@ const mockFileStats: FileStats = {
   replication: 1,
   type: BrowserViewType.file
 };
-const mockFileName = 'file.txt';
 const mockReload = jest.fn();
 
 describe('StorageFilePage', () => {
-  it('should render file metadata and content', () => {
-    render(
-      <StorageFilePage fileName={mockFileName} fileStats={mockFileStats} onReload={mockReload} />
-    );
+  it('should render file metadata and content', async () => {
+    render(<StorageFilePage fileStats={mockFileStats} onReload={mockReload} />);
 
     expect(screen.getByText('Size')).toBeInTheDocument();
     expect(screen.getByText('120.56 KB')).toBeInTheDocument();
@@ -99,33 +99,23 @@ describe('StorageFilePage', () => {
 
   // TODO: fix this test when mocking of useLoadData onSuccess callback is mproperly mocked
   it.skip('should show edit button and hides save/cancel buttons initially', () => {
-    render(
-      <StorageFilePage fileName={mockFileName} fileStats={mockFileStats} onReload={mockReload} />
-    );
+    render(<StorageFilePage fileStats={mockFileStats} onReload={mockReload} />);
 
     expect(screen.getByRole('button', { name: 'Edit' })).toBeVisible();
     expect(screen.queryByRole('button', { name: 'Save' })).toBeNull();
     expect(screen.queryByRole('button', { name: 'Cancel' })).toBeNull();
   });
 
-  // TODO: fix this test when mocking of useLoadData onSuccess callback is mproperly mocked
-  it.skip('should hide edit button when compression is available', async () => {
-    mockData.mockImplementation(() => ({
-      contents: 'Initial file content',
-      compression: 'zip'
-    }));
-    render(
-      <StorageFilePage fileName={mockFileName} fileStats={mockFileStats} onReload={mockReload} />
-    );
+  it('should hide edit button when file type is not text', async () => {
+    const fileStats = { ...mockFileStats, path: '/path/to/file.zip' };
+    render(<StorageFilePage fileStats={fileStats} onReload={mockReload} />);
 
     expect(screen.queryByRole('button', { name: 'Edit' })).toBeNull();
   });
 
   it('should show save and cancel buttons when editing', async () => {
     const user = userEvent.setup();
-    render(
-      <StorageFilePage fileName={mockFileName} fileStats={mockFileStats} onReload={mockReload} />
-    );
+    render(<StorageFilePage fileStats={mockFileStats} onReload={mockReload} />);
 
     expect(screen.getByRole('button', { name: 'Edit' })).toBeVisible();
     expect(screen.queryByRole('button', { name: 'Save' })).toBeNull();
@@ -143,9 +133,7 @@ describe('StorageFilePage', () => {
 
   it('should update textarea value and calls handleSave', async () => {
     const user = userEvent.setup();
-    render(
-      <StorageFilePage fileName={mockFileName} fileStats={mockFileStats} onReload={mockReload} />
-    );
+    render(<StorageFilePage fileStats={mockFileStats} onReload={mockReload} />);
 
     await user.click(screen.getByRole('button', { name: 'Edit' }));
 
@@ -167,9 +155,7 @@ describe('StorageFilePage', () => {
 
   it('should cancel editing and reverts textarea value', async () => {
     const user = userEvent.setup();
-    render(
-      <StorageFilePage fileName={mockFileName} fileStats={mockFileStats} onReload={mockReload} />
-    );
+    render(<StorageFilePage fileStats={mockFileStats} onReload={mockReload} />);
 
     await user.click(screen.getByRole('button', { name: 'Edit' }));
 
@@ -181,6 +167,7 @@ describe('StorageFilePage', () => {
     expect(textarea).toHaveValue('Updated file content');
 
     await user.click(screen.getByRole('button', { name: 'Cancel' }));
+
     expect(textarea).toHaveValue('Initial file content');
     expect(screen.queryByRole('button', { name: 'Save' })).toBeNull();
     expect(screen.queryByRole('button', { name: 'Cancel' })).toBeNull();
@@ -189,9 +176,7 @@ describe('StorageFilePage', () => {
 
   it('should download a file when download button is clicked', async () => {
     const user = userEvent.setup();
-    render(
-      <StorageFilePage fileName={mockFileName} fileStats={mockFileStats} onReload={mockReload} />
-    );
+    render(<StorageFilePage fileStats={mockFileStats} onReload={mockReload} />);
 
     await user.click(screen.getByRole('button', { name: 'Download' }));
 
@@ -205,9 +190,7 @@ describe('StorageFilePage', () => {
 
   it('should download a file when download button is clicked', async () => {
     const user = userEvent.setup();
-    render(
-      <StorageFilePage fileName={mockFileName} fileStats={mockFileStats} onReload={mockReload} />
-    );
+    render(<StorageFilePage fileStats={mockFileStats} onReload={mockReload} />);
 
     await user.click(screen.getByRole('button', { name: 'Download' }));
 
@@ -222,9 +205,7 @@ describe('StorageFilePage', () => {
   it('should not render the download button when show_download_button is false', () => {
     mockConfig.storage_browser.enable_file_download_button = false;
 
-    render(
-      <StorageFilePage fileName={mockFileName} fileStats={mockFileStats} onReload={mockReload} />
-    );
+    render(<StorageFilePage fileStats={mockFileStats} onReload={mockReload} />);
 
     expect(screen.queryByRole('button', { name: 'Download' })).toBeNull();
     expect(screen.queryByRole('link', { name: 'Download' })).toBeNull();
@@ -232,9 +213,7 @@ describe('StorageFilePage', () => {
 
   // TODO: fix this test when mocking of useLoadData onSuccess callback is properly mocked
   it('should render a textarea for text files', () => {
-    render(
-      <StorageFilePage fileName={mockFileName} fileStats={mockFileStats} onReload={mockReload} />
-    );
+    render(<StorageFilePage fileStats={mockFileStats} onReload={mockReload} />);
 
     const textarea = screen.getByRole('textbox');
     expect(textarea).toBeInTheDocument();
@@ -242,9 +221,7 @@ describe('StorageFilePage', () => {
   });
 
   it('should render a textarea for other files', () => {
-    render(
-      <StorageFilePage fileName={'dockerfile'} fileStats={mockFileStats} onReload={mockReload} />
-    );
+    render(<StorageFilePage fileStats={mockFileStats} onReload={mockReload} />);
 
     const textarea = screen.getByRole('textbox');
     expect(textarea).toBeInTheDocument();
@@ -254,7 +231,6 @@ describe('StorageFilePage', () => {
   it('should render an image for image files', () => {
     render(
       <StorageFilePage
-        fileName="imagefile.png"
         fileStats={{ ...mockFileStats, path: '/path/to/imagefile.png' }}
         onReload={mockReload}
       />
@@ -268,7 +244,6 @@ describe('StorageFilePage', () => {
   it('should render a preview button for document files', () => {
     render(
       <StorageFilePage
-        fileName="documentfile.pdf"
         fileStats={{ ...mockFileStats, path: '/path/to/documentfile.pdf' }}
         onReload={mockReload}
       />
@@ -280,7 +255,6 @@ describe('StorageFilePage', () => {
   it('should render an audio player for audio files', () => {
     render(
       <StorageFilePage
-        fileName="audiofile.mp3"
         fileStats={{ ...mockFileStats, path: '/path/to/audiofile.mp3' }}
         onReload={mockReload}
       />
@@ -294,7 +268,6 @@ describe('StorageFilePage', () => {
   it('should render a video player for video files', () => {
     render(
       <StorageFilePage
-        fileName="videofile.mp4"
         fileStats={{ ...mockFileStats, path: '/path/to/videofile.mp4' }}
         onReload={mockReload}
       />
@@ -305,18 +278,23 @@ describe('StorageFilePage', () => {
     expect(video.children[0]).toHaveAttribute('src', expect.stringContaining('videofile.mp4'));
   });
 
-  it('should display a message for compressed file types', () => {
+  it('should display a message for unsupported file types', () => {
+    mockError.mockImplementationOnce(() => ({
+      response: {
+        status: 422
+      }
+    }));
+
     render(
       <StorageFilePage
         fileStats={{
           ...mockFileStats,
           path: '/path/to/compressed.zip'
         }}
-        fileName="compressed.zip"
         onReload={mockReload}
       />
     );
 
-    expect(screen.getByText(/preview not available for compressed file/i)).toBeInTheDocument();
+    expect(screen.getByText(/Preview is not available for this file/i)).toBeInTheDocument();
   });
 });

+ 9 - 10
desktop/core/src/desktop/js/apps/storageBrowser/StorageFilePage/StorageFilePage.tsx

@@ -110,6 +110,7 @@ const StorageFilePage = ({ fileStats, onReload }: StorageFilePageProps): JSX.Ele
   const filePreviewUrl = `${fileDownloadUrl}&&disposition=inline`;
 
   const isEditingEnabled =
+    !error &&
     !isEditing &&
     config?.storage_browser.max_file_editor_size &&
     config?.storage_browser.max_file_editor_size > fileStats.size &&
@@ -125,7 +126,7 @@ const StorageFilePage = ({ fileStats, onReload }: StorageFilePageProps): JSX.Ele
 
   const errorConfig = [
     {
-      enabled: !!error,
+      enabled: !!error && error.response?.status !== 422,
       message: t('An error occurred while fetching file content for path "{{path}}".', {
         path: fileStats.path
       }),
@@ -189,7 +190,7 @@ const StorageFilePage = ({ fileStats, onReload }: StorageFilePageProps): JSX.Ele
             </div>
 
             <div className="preview__content">
-              {[SupportedFileTypes.TEXT, SupportedFileTypes.OTHER].includes(fileType) && (
+              {error?.response?.status !== 422 && (
                 <div className="preview__editable-file">
                   <textarea
                     value={fileContent}
@@ -203,6 +204,12 @@ const StorageFilePage = ({ fileStats, onReload }: StorageFilePageProps): JSX.Ele
                 </div>
               )}
 
+              {(error?.response?.status === 422 || fileType === SupportedFileTypes.COMPRESSED) && (
+                <div className="preview__unsupported">
+                  {t('Preview is not available for this file. Please download the file instead.')}
+                </div>
+              )}
+
               {fileType === SupportedFileTypes.IMAGE && <img src={filePreviewUrl} alt={fileName} />}
 
               {fileType === SupportedFileTypes.DOCUMENT && (
@@ -231,14 +238,6 @@ const StorageFilePage = ({ fileStats, onReload }: StorageFilePageProps): JSX.Ele
                   <track kind="captions" src="" srcLang="en" label="English" />
                 </video>
               )}
-
-              {fileType === SupportedFileTypes.COMPRESSED && (
-                <div className="preview__compresed">
-                  {t(
-                    'Preview not available for compressed file. Please download the file to view.'
-                  )}
-                </div>
-              )}
             </div>
           </div>
         </LoadingErrorWrapper>