@@ -50,7 +68,10 @@ function LloydGeorgeRecordDetails({
{userIsGpAdminNonBSOL ? (
-
+
Download and remove record
diff --git a/app/src/components/blocks/lloydGeorgeRecordStage/LloydGeorgeRecordStage.test.tsx b/app/src/components/blocks/lloydGeorgeRecordStage/LloydGeorgeRecordStage.test.tsx
index fcc2306f2..347096914 100644
--- a/app/src/components/blocks/lloydGeorgeRecordStage/LloydGeorgeRecordStage.test.tsx
+++ b/app/src/components/blocks/lloydGeorgeRecordStage/LloydGeorgeRecordStage.test.tsx
@@ -12,6 +12,7 @@ import usePatient from '../../../helpers/hooks/usePatient';
import useRole from '../../../helpers/hooks/useRole';
import useIsBSOL from '../../../helpers/hooks/useIsBSOL';
import { REPOSITORY_ROLE } from '../../../types/generic/authRole';
+
const mockPdf = buildLgSearchResult();
const mockPatientDetails = buildPatientDetails();
@@ -22,6 +23,7 @@ const mockedUsePatient = usePatient as jest.Mock;
const mockNavigate = jest.fn();
const mockedUseRole = useRole as jest.Mock;
const mockedIsBSOL = useIsBSOL as jest.Mock;
+const mockSetStage = jest.fn();
jest.mock('react-router', () => ({
useNavigate: () => mockNavigate,
@@ -113,18 +115,149 @@ describe('LloydGeorgeRecordStage', () => {
expect(screen.getByText('Lloyd George record')).toBeInTheDocument();
});
});
+ describe('User is GP admin and non BSOL', () => {
+ const renderComponentForNonBSOLGPAdmin = () => {
+ mockedUseRole.mockReturnValue(REPOSITORY_ROLE.GP_ADMIN);
+ mockedIsBSOL.mockReturnValue(false);
+ renderComponent();
+ };
+
+ const showConfirmationMessage = async () => {
+ const greenDownloadButton = screen.getByRole('button', {
+ name: 'Download and remove record',
+ });
+
+ act(() => {
+ userEvent.click(greenDownloadButton);
+ });
+ await waitFor(() => {
+ expect(
+ screen.getByText('Are you sure you want to download and remove this record?'),
+ ).toBeInTheDocument();
+ });
+ };
+
+ const clickRedDownloadButton = () => {
+ const redDownloadButton = screen.getByRole('button', {
+ name: 'Yes, download and remove',
+ });
+
+ act(() => {
+ userEvent.click(redDownloadButton);
+ });
+ };
+
+ it('renders warning callout, header and button', async () => {
+ renderComponentForNonBSOLGPAdmin();
+
+ expect(screen.getByText('Before downloading')).toBeInTheDocument();
+ expect(screen.getByText('Available records')).toBeInTheDocument();
+ expect(
+ screen.getByRole('button', { name: 'Download and remove record' }),
+ ).toBeInTheDocument();
+ });
- it('renders warning callout, header and button when user is GP admin and non BSOL', async () => {
- mockedUseRole.mockReturnValue(REPOSITORY_ROLE.GP_ADMIN);
- mockedIsBSOL.mockReturnValue(false);
+ it('clicking the green download button should show confirmation message, checkbox, red download button and cancel button', async () => {
+ renderComponentForNonBSOLGPAdmin();
+
+ const downloadButton = screen.getByRole('button', {
+ name: 'Download and remove record',
+ });
+
+ act(() => {
+ userEvent.click(downloadButton);
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.getByText('Are you sure you want to download and remove this record?'),
+ ).toBeInTheDocument();
+ });
+ expect(
+ screen.getByText(
+ "If you download this record, it removes from our storage. You must keep the patient's record safe.",
+ ),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByRole('checkbox', {
+ name: 'I understand that downloading this record removes it from storage.',
+ }),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByRole('button', { name: 'Yes, download and remove' }),
+ ).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument();
+ });
- renderComponent();
+ it('when checkbox is unchecked, clicking red download button should show an alert and not allowing download', async () => {
+ renderComponentForNonBSOLGPAdmin();
+ await showConfirmationMessage();
+
+ clickRedDownloadButton();
+
+ await waitFor(() => {
+ expect(
+ screen.getByRole('alert', { name: 'There is a problem' }),
+ ).toBeInTheDocument();
+ });
+ expect(
+ screen.getByText('You must confirm if you want to download and remove this record'),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText('Confirm if you want to download and remove this record'),
+ ).toBeInTheDocument();
+ expect(mockSetStage).not.toBeCalled();
+ });
- expect(screen.getByText('Before downloading')).toBeInTheDocument();
- expect(screen.getByText('Available records')).toBeInTheDocument();
- expect(
- screen.getByRole('button', { name: 'Download and remove record' }),
- ).toBeInTheDocument();
+ it('when checkbox is checked, clicking red download button should proceed to download and delete process', async () => {
+ renderComponentForNonBSOLGPAdmin();
+ await showConfirmationMessage();
+
+ act(() => {
+ userEvent.click(screen.getByRole('checkbox'));
+ });
+
+ clickRedDownloadButton();
+
+ await waitFor(() => {
+ expect(mockSetStage).toBeCalledWith(LG_RECORD_STAGE.DOWNLOAD_ALL);
+ });
+ });
+
+ it('when checkbox is toggled 2 times ( = unchecked), red download button should not proceed to download', async () => {
+ renderComponentForNonBSOLGPAdmin();
+ await showConfirmationMessage();
+
+ const checkBox = screen.getByRole('checkbox');
+ act(() => {
+ userEvent.click(checkBox);
+ userEvent.click(checkBox);
+ });
+
+ clickRedDownloadButton();
+
+ await waitFor(() => {
+ expect(
+ screen.getByRole('alert', { name: 'There is a problem' }),
+ ).toBeInTheDocument();
+ });
+ expect(mockSetStage).not.toBeCalled();
+ });
+
+ it('clicking cancel button will hide the confirmation message', async () => {
+ renderComponentForNonBSOLGPAdmin();
+ await showConfirmationMessage();
+
+ act(() => {
+ userEvent.click(screen.getByRole('button', { name: 'Cancel' }));
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.queryByText('Are you sure you want to download and remove this record?'),
+ ).not.toBeInTheDocument();
+ });
+ });
});
it('does not render warning callout, header and button when user is GP admin and BSOL', async () => {
@@ -167,8 +300,7 @@ describe('LloydGeorgeRecordStage', () => {
});
});
const TestApp = (props: Omit
) => {
- const [stage, setStage] = useState(LG_RECORD_STAGE.RECORD);
- return ;
+ return ;
};
const renderComponent = (propsOverride?: Partial) => {
diff --git a/app/src/components/blocks/lloydGeorgeRecordStage/LloydGeorgeRecordStage.tsx b/app/src/components/blocks/lloydGeorgeRecordStage/LloydGeorgeRecordStage.tsx
index 58720ae4d..ee10d1c7c 100644
--- a/app/src/components/blocks/lloydGeorgeRecordStage/LloydGeorgeRecordStage.tsx
+++ b/app/src/components/blocks/lloydGeorgeRecordStage/LloydGeorgeRecordStage.tsx
@@ -1,6 +1,8 @@
import React, { Dispatch, SetStateAction, useState } from 'react';
import {
BackLink,
+ Button,
+ ButtonLink,
Card,
Checkboxes,
Details,
@@ -10,7 +12,6 @@ import {
} from 'nhsuk-react-components';
import { getFormattedDate } from '../../../helpers/utils/formatDate';
import { DOWNLOAD_STAGE } from '../../../types/generic/downloadStage';
-import { Button } from 'nhsuk-react-components';
import PdfViewer from '../../generic/pdfViewer/PdfViewer';
import LloydGeorgeRecordDetails from '../lloydGeorgeRecordDetails/LloydGeorgeRecordDetails';
import { formatNhsNumber } from '../../../helpers/utils/formatNhsNumber';
@@ -20,6 +21,7 @@ import LloydGeorgeRecordError from '../lloydGeorgeRecordError/LloydGeorgeRecordE
import useRole from '../../../helpers/hooks/useRole';
import { REPOSITORY_ROLE } from '../../../types/generic/authRole';
import useIsBSOL from '../../../helpers/hooks/useIsBSOL';
+import WarningText from '../../generic/warningText/WarningText';
import ErrorBox from '../../layout/errorBox/ErrorBox';
import { useForm } from 'react-hook-form';
import { InputRef } from '../../../types/generic/inputRef';
@@ -44,17 +46,18 @@ function LloydGeorgeRecordStage({
stage,
}: Props) {
const [fullScreen, setFullScreen] = useState(false);
+ const [downloadRemoveButtonClicked, setDownloadRemoveButtonClicked] = useState(false);
const patientDetails = usePatient();
const dob: String = patientDetails?.birthDate
? getFormattedDate(new Date(patientDetails.birthDate))
: '';
- const {
- register,
- handleSubmit,
- formState: { errors },
- } = useForm({ reValidateMode: 'onSubmit' });
- const { ref: inputRef, ...checkboxProps } = register('confirmBsol', { required: true });
+ const { register, handleSubmit, formState, clearErrors, setError, setFocus } = useForm({
+ reValidateMode: 'onSubmit',
+ });
+ const { ref: inputRef, ...checkboxProps } = register('confirmDownloadRemove', {
+ required: true,
+ });
const nhsNumber: string = patientDetails?.nhsNumber || '';
const formattedNhsNumber = formatNhsNumber(nhsNumber);
@@ -72,7 +75,11 @@ function LloydGeorgeRecordStage({
numberOfFiles,
totalFileSizeInByte,
setStage,
- userIsGpAdminNonBSOL: userIsGpAdminNonBSOL,
+ userIsGpAdminNonBSOL,
+ setDownloadRemoveButtonClicked,
+ downloadRemoveButtonClicked,
+ setError,
+ setFocus,
};
return ;
} else {
@@ -80,11 +87,20 @@ function LloydGeorgeRecordStage({
}
};
+ const handleConfirmDownloadAndRemoveButton = () => {
+ setStage(LG_RECORD_STAGE.DOWNLOAD_ALL);
+ };
+
+ const handleCancelButton = () => {
+ setDownloadRemoveButtonClicked(false);
+ clearErrors('confirmDownloadRemove');
+ };
+
return (
- {errors.confirmBsol && (
+ {formState.errors.confirmDownloadRemove && (
@@ -102,7 +118,7 @@ function LloydGeorgeRecordStage({
)}
{!fullScreen && userIsGpAdminNonBSOL && (
-
+
Before downloading
@@ -115,47 +131,64 @@ function LloydGeorgeRecordStage({
should follow data protection principles as outlined in UK General Data
Protection Regulation (GDPR).
-
-
-
-
+
+ I understand that downloading this record removes it
+ from storage.
+
+
+
+
+ Yes, download and remove
+
+
+ Cancel
+
+
+
+ )}
-
Available records
)}
diff --git a/app/src/styles/App.scss b/app/src/styles/App.scss
index cfc3a4135..61b7ce396 100644
--- a/app/src/styles/App.scss
+++ b/app/src/styles/App.scss
@@ -161,6 +161,25 @@ $govuk-compatibility-govukelements: true;
top: 30px;
}
}
+ &_gp-admin-non-bsol {
+ &_inset-text {
+ padding-bottom: 0;
+ padding-top: 0;
+ &_confirm-download-remove-button {
+ background-color: #b61105;
+ box-shadow: #9a0c02;
+ &:hover {
+ background-color: #9a0c02;
+ }
+ &:active {
+ background-color: #9a0c02;
+ }
+ }
+ &_checkbox {
+ margin-bottom: 30px;
+ }
+ }
+ }
}
&_downloadall-stage {
&_header {
@@ -307,3 +326,8 @@ $govuk-compatibility-govukelements: true;
background: $color_nhsuk-yellow;
color: $nhsuk-text-color;
}
+
+.govuk-warning-text {
+ display: flex;
+ align-items: center;
+}