From c449f9af7a8d58ea21b37082f3542f3dee1f4381 Mon Sep 17 00:00:00 2001 From: Ben Furber Date: Fri, 14 Jun 2024 11:59:07 +0100 Subject: [PATCH] feat: add donation request to local howto files --- .../DonationRequest/DonationRequest.test.tsx | 2 +- .../src/DonationRequest/DonationRequest.tsx | 1 + .../DownloadButton/DownloadButton.stories.tsx | 12 ++- .../DownloadButton/DownloadButton.test.tsx | 12 ++- .../src/DownloadButton/DownloadButton.tsx | 17 ++++- .../DownloadFileFromLink.tsx | 2 +- .../DownloadStaticFile/DownloadStaticFile.tsx | 24 +++++- src/common/DownloadWithDonationAsk.test.tsx | 73 +++++++++++++++++-- src/common/DownloadWithDonationAsk.tsx | 71 +++++++++++++++--- .../Howto/HowtoDownloads/HowtoDownloads.tsx | 39 ++-------- 10 files changed, 190 insertions(+), 63 deletions(-) diff --git a/packages/components/src/DonationRequest/DonationRequest.test.tsx b/packages/components/src/DonationRequest/DonationRequest.test.tsx index b8ddcc7761..51dbbc0a73 100644 --- a/packages/components/src/DonationRequest/DonationRequest.test.tsx +++ b/packages/components/src/DonationRequest/DonationRequest.test.tsx @@ -4,7 +4,7 @@ import { fireEvent } from '@testing-library/react' import { describe, expect, it, vi } from 'vitest' import { render } from '../test/utils' -import { BUTTON_LABEL,DonationRequest } from './DonationRequest' +import { BUTTON_LABEL, DonationRequest } from './DonationRequest' describe('DonationRequest', () => { it('shows the expected content', async () => { diff --git a/packages/components/src/DonationRequest/DonationRequest.tsx b/packages/components/src/DonationRequest/DonationRequest.tsx index c4981f130c..6663ee9cd7 100644 --- a/packages/components/src/DonationRequest/DonationRequest.tsx +++ b/packages/components/src/DonationRequest/DonationRequest.tsx @@ -99,6 +99,7 @@ export const DonationRequest = (props: IProps) => { href={link} onClick={callback} data-cy="DonationRequestSkip" + data-testid="DonationRequestSkip" > diff --git a/packages/components/src/DownloadButton/DownloadButton.stories.tsx b/packages/components/src/DownloadButton/DownloadButton.stories.tsx index 5966326cc0..95984d6be6 100644 --- a/packages/components/src/DownloadButton/DownloadButton.stories.tsx +++ b/packages/components/src/DownloadButton/DownloadButton.stories.tsx @@ -7,4 +7,14 @@ export default { component: DownloadButton, } as Meta -export const Default: StoryFn = () => +export const Default: StoryFn = () => ( + {}} /> +) + +export const CustomDetails: StoryFn = () => ( + {}} + glyph="download-cloud" + label="Hello there" + /> +) diff --git a/packages/components/src/DownloadButton/DownloadButton.test.tsx b/packages/components/src/DownloadButton/DownloadButton.test.tsx index 6d626be9aa..930ed05509 100644 --- a/packages/components/src/DownloadButton/DownloadButton.test.tsx +++ b/packages/components/src/DownloadButton/DownloadButton.test.tsx @@ -3,14 +3,22 @@ import '@testing-library/jest-dom/vitest' import { describe, expect, it } from 'vitest' import { render } from '../test/utils' -import { Default } from './DownloadButton.stories' +import { CustomDetails, Default } from './DownloadButton.stories' import type { IProps } from './DownloadButton' describe('DownloadButton', () => { - it('validates the component behaviour', () => { + it('renders the default state', () => { const { getByText } = render() expect(getByText('Download files')).toBeInTheDocument() }) + + it('renders the custom options', () => { + const { getByText } = render( + , + ) + + expect(getByText('Hello there')).toBeInTheDocument() + }) }) diff --git a/packages/components/src/DownloadButton/DownloadButton.tsx b/packages/components/src/DownloadButton/DownloadButton.tsx index ea663ee146..b16c29e12f 100644 --- a/packages/components/src/DownloadButton/DownloadButton.tsx +++ b/packages/components/src/DownloadButton/DownloadButton.tsx @@ -3,12 +3,21 @@ import { Flex, Text } from 'theme-ui' import { Icon } from '../Icon/Icon' import { Tooltip } from '../Tooltip/Tooltip' +import type { IGlyphs } from '../Icon/types' + export interface IProps { - onClick?: () => void + onClick: () => void isLoggedIn?: boolean + label?: string + glyph?: keyof IGlyphs } -export const DownloadButton = ({ onClick, isLoggedIn }: IProps) => { +export const DownloadButton = ({ + glyph, + isLoggedIn, + label, + onClick, +}: IProps) => { return ( <> { data-testid="downloadButton" data-tip={!isLoggedIn ? 'Login to download' : ''} > - + - Download files + {label ? label : 'Download files'} diff --git a/packages/components/src/DownloadFileFromLink/DownloadFileFromLink.tsx b/packages/components/src/DownloadFileFromLink/DownloadFileFromLink.tsx index fe435f71c6..1649e4d6e0 100644 --- a/packages/components/src/DownloadFileFromLink/DownloadFileFromLink.tsx +++ b/packages/components/src/DownloadFileFromLink/DownloadFileFromLink.tsx @@ -15,7 +15,7 @@ export const DownloadFileFromLink = (props: DownloadFileFromLinkProps) => { href={props.link} onClick={() => props.handleClick && props.handleClick()} > - + {}} /> ) : ( Promise + handleClick?: () => void redirectToSignIn?: () => Promise } @@ -72,6 +75,8 @@ export const DownloadStaticFile = ({ allowDownload, handleClick, redirectToSignIn, + forDonationRequest, + isLoggedIn, }: IProps) => { const size = bytesToSize(file.size || 0) @@ -79,9 +84,11 @@ export const DownloadStaticFile = ({ return null } + const forDownload = allowDownload && file.downloadUrl && !redirectToSignIn + return ( <> - {allowDownload && file.downloadUrl && !redirectToSignIn ? ( + {forDownload && ( handleClick && handleClick()} @@ -91,7 +98,18 @@ export const DownloadStaticFile = ({ > - ) : ( + )} + + {forDonationRequest && ( + handleClick && handleClick()} + isLoggedIn={isLoggedIn} + label={`${file.name} (${size})`} + glyph="download-cloud" + /> + )} + + {!forDownload && !forDonationRequest && ( ({ __esModule: true, useCommonStores: vi.fn(), })) -const userToMock = (user) => { +const userToMock = (user?: IUserPPDB) => { return (useCommonStores as Mock).mockImplementation(() => ({ - stores: { userStore: { user } }, + stores: { + userStore: { user: user ?? undefined }, + themeStore: { + currentTheme: { + donations: { + body: '', + iframeSrc: '', + imageURL: '', + }, + }, + }, + }, })) } describe('DownloadFileFromLink', () => { it('when logged out, requires users to login', () => { + userToMock() const { getAllByTestId } = render( , ) @@ -41,7 +57,7 @@ describe('DownloadFileFromLink', () => { expect(mockedUsedNavigate).toHaveBeenCalledWith('/sign-in') }) - it('when logged in, opens the link via handleClick', () => { + it('when logged in, opens via handleClick', () => { const user = FactoryUser() userToMock(user) @@ -50,7 +66,9 @@ describe('DownloadFileFromLink', () => { , ) @@ -60,23 +78,64 @@ describe('DownloadFileFromLink', () => { expect(handleClick).toHaveBeenCalled() }) - it('when a beta-tester, opens the donation modal', () => { + it('when a beta-tester, opens the donation modal for fileLink', () => { const user = FactoryUser({ userRoles: [UserRole.BETA_TESTER] }) userToMock(user) const handleClick = vi.fn() + const fileLink = 'http://youtube.com/' const { getAllByTestId } = render( , ) const downloadButton = getAllByTestId('downloadButton')[0] fireEvent.click(downloadButton) + expect(getAllByTestId('DonationRequestSkip')[0]).toHaveAttribute( + 'href', + fileLink, + ) + expect(getAllByTestId('DonationRequest')[0]).toBeInTheDocument() + expect(handleClick).not.toHaveBeenCalled() + }) + + it('when a beta-tester, opens the donation modal for files', () => { + const user = FactoryUser({ userRoles: [UserRole.BETA_TESTER] }) + userToMock(user) + + const downloadUrl = 'http://great-url.com/' + const handleClick = vi.fn() + + const { getAllByTestId } = render( + , + ) + + const downloadButton = getAllByTestId('downloadButton')[0] + fireEvent.click(downloadButton) + + expect(getAllByTestId('DonationRequestSkip')[0]).toHaveAttribute( + 'href', + downloadUrl, + ) expect(getAllByTestId('DonationRequest')[0]).toBeInTheDocument() expect(handleClick).not.toHaveBeenCalled() }) diff --git a/src/common/DownloadWithDonationAsk.tsx b/src/common/DownloadWithDonationAsk.tsx index c7431ccf87..90360b0e41 100644 --- a/src/common/DownloadWithDonationAsk.tsx +++ b/src/common/DownloadWithDonationAsk.tsx @@ -3,18 +3,23 @@ import { useNavigate } from 'react-router-dom' import { DonationRequestModal, DownloadButton, + DownloadCounter, DownloadFileFromLink, + DownloadStaticFile, } from 'oa-components' import { useCommonStores } from './hooks/useCommonStores' import { AuthWrapper } from './AuthWrapper' import type { UserRole } from 'src/models' +import type { IUploadedFileMeta } from 'src/stores/storage' export interface IProps { handleClick: () => Promise isLoggedIn: boolean - link: string + fileDownloadCount: number + fileLink: string | undefined + files: (IUploadedFileMeta | File | null)[] | undefined } /* @@ -23,8 +28,9 @@ export interface IProps { can/should move to the component library. */ export const DownloadWithDonationAsk = (props: IProps) => { - const { handleClick, isLoggedIn, link } = props + const { handleClick, isLoggedIn, fileDownloadCount, fileLink, files } = props const [isModalOpen, setIsModalOpen] = useState(false) + const [link, setLink] = useState('') const navigate = useNavigate() const { themeStore } = useCommonStores().stores @@ -35,14 +41,9 @@ export const DownloadWithDonationAsk = (props: IProps) => { toggleIsModalOpen() } - if (!isLoggedIn) { - return ( - navigate('/sign-in')} - isLoggedIn={false} - /> - ) - } + const filteredFiles: IUploadedFileMeta[] | undefined = files?.filter( + (file): file is IUploadedFileMeta => file !== null && 'downloadUrl' in file, + ) return ( <> @@ -59,11 +60,57 @@ export const DownloadWithDonationAsk = (props: IProps) => { + <> + {!isLoggedIn && ( + navigate('/sign-in')} + isLoggedIn={false} + /> + )} + + {isLoggedIn && fileLink && ( + + )} + {isLoggedIn && + filteredFiles && + filteredFiles.map((file, index) => ( + + ))} + } > - + <> + {fileLink && ( + { + setLink(fileLink) + toggleIsModalOpen() + }} + isLoggedIn + /> + )} + {filteredFiles && + filteredFiles.map((file, index) => ( + { + setLink(file.downloadUrl) + toggleIsModalOpen() + }} + forDonationRequest + isLoggedIn + /> + ))} + + ) } diff --git a/src/pages/Howto/Content/Howto/HowtoDownloads/HowtoDownloads.tsx b/src/pages/Howto/Content/Howto/HowtoDownloads/HowtoDownloads.tsx index af80baa456..5f01e09c35 100644 --- a/src/pages/Howto/Content/Howto/HowtoDownloads/HowtoDownloads.tsx +++ b/src/pages/Howto/Content/Howto/HowtoDownloads/HowtoDownloads.tsx @@ -1,6 +1,4 @@ import { useState } from 'react' -import { useNavigate } from 'react-router-dom' -import { DownloadCounter, DownloadStaticFile } from 'oa-components' import { DownloadWithDonationAsk } from 'src/common/DownloadWithDonationAsk' import { useCommonStores } from 'src/common/hooks/useCommonStores' import { Flex } from 'theme-ui' @@ -23,13 +21,8 @@ export const HowtoDownloads = ({ howto, loggedInUser }: IProps) => { const { _id, files, fileLink, total_downloads } = howto const [fileDownloadCount, setFileDownloadCount] = useState(total_downloads) - const navigate = useNavigate() const { howtoStore } = useCommonStores().stores - const redirectToSignIn = async () => { - navigate('/sign-in') - } - const incrementDownloadCount = async () => { const updatedDownloadCount = await howtoStore.incrementDownloadCount(_id) setFileDownloadCount(updatedDownloadCount!) @@ -54,31 +47,13 @@ export const HowtoDownloads = ({ howto, loggedInUser }: IProps) => { return ( - {fileLink && ( - - )} - {files && - files - .filter(Boolean) - .map( - (file, index) => - file && ( - - ), - )} - + ) }