Skip to content

Commit

Permalink
feat: Support additional file types for opening docs in browser (#1214)
Browse files Browse the repository at this point in the history
* support additional mime types

* update tests for additional mime types

* update open doc code for landing page
  • Loading branch information
abbyoung authored Feb 21, 2024
1 parent 8f0b0cb commit 0c67389
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 46 deletions.
19 changes: 6 additions & 13 deletions src/components/AnnouncementInfo/AnnouncementInfo.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,8 @@ import {
testAnnouncementWithJpgDocument,
} from '__fixtures__/data/cmsAnnouncments'

// Spy on the functions that test files and opens PDFs in the browser
// We are not mocking because we want to test isPdf
jest.spyOn(openDocumentLink, 'handleOpenPdfLink').mockImplementation()
jest.spyOn(openDocumentLink, 'isPdf')
// Spy on the function that opens PDFs in the browser
jest.spyOn(openDocumentLink, 'openFileInNewTab').mockImplementation()

describe('AnnouncementInfo component', () => {
test('renders an announcement with a url CTA', () => {
Expand Down Expand Up @@ -73,13 +71,11 @@ describe('AnnouncementInfo component', () => {
expect(link).toHaveAttribute('href', pdfString)

fireEvent.click(link)
expect(openDocumentLink.isPdf).toHaveBeenCalledTimes(1)
expect(openDocumentLink.isPdf).toHaveBeenCalledWith(pdfString)
expect(openDocumentLink.handleOpenPdfLink).toHaveBeenCalledTimes(1)
expect(openDocumentLink.handleOpenPdfLink).toHaveBeenCalledWith(pdfString)
expect(openDocumentLink.openFileInNewTab).toHaveBeenCalledTimes(1)
expect(openDocumentLink.openFileInNewTab).toHaveBeenCalledWith(pdfString)
})

test('renders an announcement with a non-pdf document CTA', async () => {
test('renders an announcement with a jpg document CTA', async () => {
jest.resetAllMocks()

const jpgString =
Expand All @@ -97,9 +93,6 @@ describe('AnnouncementInfo component', () => {
expect(link).toHaveAttribute('href', jpgString)

fireEvent.click(link)
expect(openDocumentLink.isPdf).toHaveBeenCalledTimes(1)
expect(openDocumentLink.isPdf).toHaveBeenCalledWith(jpgString)

expect(openDocumentLink.handleOpenPdfLink).toHaveBeenCalledTimes(0)
expect(openDocumentLink.openFileInNewTab).toHaveBeenCalledTimes(1)
})
})
8 changes: 3 additions & 5 deletions src/components/AnnouncementInfo/AnnouncementInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Link from 'next/link'
import styles from './AnnouncementInfo.module.scss'
import { AnnouncementRecord } from 'types'
import AnnouncementDate from 'components/AnnouncementDate/AnnouncementDate'
import { isPdf, handleOpenPdfLink } from 'helpers/openDocumentLink'
import { openFileInNewTab } from 'helpers/openDocumentLink'

type valueObject = {
data: {
Expand Down Expand Up @@ -73,10 +73,8 @@ const AnnouncementInfo = ({
<Link
legacyBehavior={false}
onClick={(e) => {
if (isPdf(fileUrl)) {
e.preventDefault()
handleOpenPdfLink(fileUrl)
} else return
e.preventDefault()
openFileInNewTab(fileUrl)
}}
href={fileUrl}
rel="noreferrer"
Expand Down
17 changes: 11 additions & 6 deletions src/helpers/openDocumentLink.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @jest-environment jsdom
*/
import axios from 'axios'
import { isPdf, handleOpenPdfLink } from './openDocumentLink'
import { mimeTypes, getMimeType, openFileInNewTab } from './openDocumentLink'

jest.mock('axios', () => ({
get: jest.fn(() => Promise.resolve({ data: { blob: () => 'test' } })),
Expand All @@ -19,19 +19,24 @@ const mockWindowOpen = jest.fn()
window.open = mockWindowOpen

describe('isPdf', () => {
test('returns true if url ends with .pdf', () => {
expect(isPdf('https://www.google.com/test.pdf')).toBe(true)
Object.entries(mimeTypes).forEach(([extension, type]) => {
test('returns correct type if url ends with extension', () => {
const fileName = `https://www.google.com/test.${extension}`
expect(getMimeType(fileName)).toBe(type)
})
})

test('returns false if url does not end with .pdf', () => {
expect(isPdf('https://www.google.com/test')).toBe(false)
test('returns octet if url ends with extension not supported', () => {
expect(getMimeType('https://www.google.com/test')).toBe(
'application/octet-stream'
)
})
})

describe('handleOpenPdfLink', () => {
test('opens a new window if the url is a pdf', async () => {
const pdfString = 'https://www.google.com/test.pdf'
await handleOpenPdfLink(pdfString)
await openFileInNewTab(pdfString)
expect(axios.get).toHaveBeenCalled()
expect(mockCreateObjectURL).toHaveBeenCalled()
expect(mockWindowOpen).toHaveBeenCalled()
Expand Down
48 changes: 36 additions & 12 deletions src/helpers/openDocumentLink.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,43 @@
import axios from 'axios'

export const isPdf = (url: string) => {
return url.toString().includes('.pdf')
// Helper function to determine MIME type from the Keystone file URL
export const mimeTypes: Record<string, string> = {
pdf: 'application/pdf',
jpg: 'image/jpeg',
jpeg: 'image/jpeg',
png: 'image/png',
webp: 'image/webp',
gif: 'image/gif',
svg: 'image/svg+xml',
webm: 'video/webm',
mp4: 'video/mp4',
mov: 'video/quicktime',
mp3: 'audio/mpeg',
wav: 'audio/wav',
txt: 'text/plain',
csv: 'text/csv',
xls: 'application/vnd.ms-excel',
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
// Add more mappings as needed
}
export const getMimeType = (url: string): string => {
const extension = url.split('.').pop()?.toLowerCase()
return mimeTypes[extension || ''] || 'application/octet-stream' // Default MIME type
}
// Function to open files in a new tab with TypeScript annotations
export const openFileInNewTab = async (fileUrl: string): Promise<void> => {
try {
const response = await axios.get<Blob>(fileUrl, { responseType: 'blob' })
const mimeType = getMimeType(fileUrl)

export const handleOpenPdfLink = async (pdfString: string) => {
// Fetch the file from Keystone / S3
const res = await axios.get(pdfString, { responseType: 'blob' })
const file = new Blob([response.data], { type: mimeType })
const fileURL = URL.createObjectURL(file)

// Create a blob from the file and open it in a new tab
const blobData = await res.data
const file = new Blob([blobData], { type: 'application/pdf' })
const fileUrl = URL.createObjectURL(file)
// If the browser cannot open the file, it will download it automatically
window.open(fileURL, '_blank')

window.open(fileUrl)
// Let the browser know not to keep the reference to the file any longer.
URL.revokeObjectURL(fileUrl)
URL.revokeObjectURL(fileURL)
} catch (error) {
console.error('Error opening file:', error)
}
}
8 changes: 3 additions & 5 deletions src/pages/landing/[landingPage]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { withLandingPageLayout } from 'layout/DefaultLayout/LandingPageLayout'
import { client } from 'lib/keystoneClient'
import { GET_LANDING_PAGE } from 'operations/cms/queries/getLandingPage'
import { CMSBookmark, CollectionRecord } from 'types'
import { isPdf, handleOpenPdfLink } from 'helpers/openDocumentLink'
import { openFileInNewTab } from 'helpers/openDocumentLink'
import { getSession } from 'lib/session'
import { formatDisplayDate, isCmsUser, isPublished } from 'helpers/index'

Expand Down Expand Up @@ -90,10 +90,8 @@ const LandingPage = ({
return (
<Link
onClick={(e) => {
if (isPdf(doc.file.url)) {
e.preventDefault()
handleOpenPdfLink(doc.file.url)
} else return
e.preventDefault()
openFileInNewTab(doc.file.url)
}}
key={index}
rel="noreferrer noopener"
Expand Down
8 changes: 3 additions & 5 deletions src/pages/ussf-documentation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { GET_DOCUMENTS_PAGE } from 'operations/cms/queries/getDocumentsPage'
import { DocumentType, DocumentPageType, DocumentSectionType } from 'types'
import { useUser } from 'hooks/useUser'
import Loader from 'components/Loader/Loader'
import { isPdf, handleOpenPdfLink } from 'helpers/openDocumentLink'
import { openFileInNewTab } from 'helpers/openDocumentLink'

const USSFDocumentation = ({
documentsPage,
Expand Down Expand Up @@ -42,10 +42,8 @@ const USSFDocumentation = ({
{s.document.map((d: DocumentType) => (
<Link
onClick={(e) => {
if (isPdf(d.file.url)) {
e.preventDefault()
handleOpenPdfLink(d.file.url)
} else return
e.preventDefault()
openFileInNewTab(d.file.url)
}}
key={d.id}
rel="noreferrer noopener"
Expand Down

0 comments on commit 0c67389

Please sign in to comment.