diff --git a/app/components/Attachment/AttachmentTableRow.tsx b/app/components/Attachment/AttachmentTableRow.tsx index 42742b1c47..e94285a552 100644 --- a/app/components/Attachment/AttachmentTableRow.tsx +++ b/app/components/Attachment/AttachmentTableRow.tsx @@ -1,9 +1,6 @@ import { Button } from "@button-inc/bcgov-theme"; import Link from "next/link"; -import { - getAttachmentDownloadRoute, - getAttachmentViewPageRoute, -} from "pageRoutes"; +import { getAttachmentDownloadRoute } from "pageRoutes"; import { useFragment, graphql } from "react-relay"; import { AttachmentTableRow_attachment$key } from "__generated__/AttachmentTableRow_attachment.graphql"; @@ -44,10 +41,6 @@ const AttachmentTableRow: React.FC = ({ attachment }) => { {fullName} {createdAt} - - - -   diff --git a/app/components/TaskList/AttachmentsTaskListSection.tsx b/app/components/TaskList/AttachmentsTaskListSection.tsx new file mode 100644 index 0000000000..bf809483af --- /dev/null +++ b/app/components/TaskList/AttachmentsTaskListSection.tsx @@ -0,0 +1,62 @@ +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { IconDefinition } from "@fortawesome/fontawesome-svg-core"; +import Link from "next/link"; +import { useRouter } from "next/router"; + +interface Props { + icon: IconDefinition; + title: string; + linkUrl: { pathname: string; query: { projectRevision: string } }; +} + +const AttachmentsTaskListSection: React.FC = ({ + icon, + title, + linkUrl, +}) => { + const router = useRouter(); + return ( +
  • + +

    + + {title} + +

    + + +
  • + ); +}; + +export default AttachmentsTaskListSection; diff --git a/app/components/TaskList/index.tsx b/app/components/TaskList/index.tsx index 5dc61df82b..2d330b812f 100644 --- a/app/components/TaskList/index.tsx +++ b/app/components/TaskList/index.tsx @@ -1,11 +1,14 @@ +import { faPaperclip } from "@fortawesome/free-solid-svg-icons"; import { useRouter } from "next/router"; import { getProjectRevisionPageRoute, getProjectRevisionFormPageRoute, + getProjectRevisionAttachmentsPageRoute, } from "pageRoutes"; import { useMemo } from "react"; import { graphql, useFragment } from "react-relay"; import { TaskList_projectRevision$key } from "__generated__/TaskList_projectRevision.graphql"; +import AttachmentsTaskListSection from "./AttachmentsTaskListSection"; import TaskListItem from "./TaskListItem"; import TaskListSection from "./TaskListSection"; import { TaskListMode } from "./types"; @@ -175,6 +178,15 @@ const TaskList: React.FC = ({ projectRevision, mode }) => { /> )} + + {/* Attachments Section */} + {mode === "view" && ( + + )} + + ); +} + +export default withRelay(ProjectAttachments, pageQuery, withRelayOptions); diff --git a/app/pages/cif/project/[project]/attachments.tsx b/app/pages/cif/project/[project]/attachments.tsx deleted file mode 100644 index e0f05a445f..0000000000 --- a/app/pages/cif/project/[project]/attachments.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import DefaultLayout from "components/Layout/DefaultLayout"; -import { withRelay, RelayProps } from "relay-nextjs"; -import { graphql, usePreloadedQuery } from "react-relay"; -import { attachmentsQuery } from "__generated__/attachmentsQuery.graphql"; -import withRelayOptions from "lib/relay/withRelayOptions"; -import useRedirectTo404IfFalsy from "hooks/useRedirectTo404IfFalsy"; -import Table from "components/Table"; -import { NoHeaderFilter, TextFilter } from "components/Table/Filters"; -import AttachmentTableRow from "components/Attachment/AttachmentTableRow"; -import Link from "next/link"; - -const pageQuery = graphql` - query attachmentsQuery($project: ID!) { - session { - ...DefaultLayout_session - } - project(id: $project) { - id - projectName - } - allAttachments(first: 2147483647) - @connection(key: "connection_allAttachments") { - totalCount - edges { - node { - file - ...AttachmentTableRow_attachment - } - } - } - } -`; - -const tableFilters = [ - new TextFilter("File Name", "fileName"), - new TextFilter("Type", "type"), - new TextFilter("Size", "size"), - new TextFilter("Uploaded by", "uploadedBy"), - new TextFilter("Received", "received"), - new NoHeaderFilter(), -]; - -export function ProjectAttachments({ - preloadedQuery, -}: RelayProps<{}, attachmentsQuery>) { - const { session, project, allAttachments } = usePreloadedQuery( - pageQuery, - preloadedQuery - ); - - const isRedirecting = useRedirectTo404IfFalsy(project); - if (isRedirecting) return null; - - return ( - -

    {project.projectName}

    -

    Attachments List

    - - Upload New Attachment - - - {allAttachments.edges.map(({ node }) => ( - - ))} -
    - -
    - ); -} - -export default withRelay(ProjectAttachments, pageQuery, withRelayOptions); diff --git a/app/pages/cif/project/[project]/upload-attachment.tsx b/app/pages/cif/project/[project]/upload-attachment.tsx deleted file mode 100644 index 123b90cd56..0000000000 --- a/app/pages/cif/project/[project]/upload-attachment.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import DefaultLayout from "components/Layout/DefaultLayout"; -import { withRelay, RelayProps } from "relay-nextjs"; -import { graphql, usePreloadedQuery } from "react-relay/hooks"; -import { uploadAttachmentQuery } from "__generated__/uploadAttachmentQuery.graphql"; -import withRelayOptions from "lib/relay/withRelayOptions"; -import { useCreateAttachment } from "mutations/attachment/createAttachment"; -import { FilePicker } from "@button-inc/bcgov-theme"; -import bytesToSize from "lib/helpers/bytesToText"; -import { getAttachmentsPageRoute } from "pageRoutes"; -import { useRouter } from "next/router"; - -const pageQuery = graphql` - query uploadAttachmentQuery($project: ID!) { - session { - ...DefaultLayout_session - } - project(id: $project) { - id - rowId - projectName - } - } -`; - -export function UploadAttachment({ - preloadedQuery, -}: RelayProps<{}, uploadAttachmentQuery>) { - const { session, project } = usePreloadedQuery(pageQuery, preloadedQuery); - const router = useRouter(); - - const [createAttachment, isCreatingAttachment] = useCreateAttachment(); - - const goToProjectAttachmentsView = async () => { - await router.push(getAttachmentsPageRoute(project.id)); - }; - - const saveAttachment = async (e) => { - var file = e.target.files[0]; - const variables = { - input: { - attachment: { - file: file, - fileName: file.name, - fileSize: bytesToSize(file.size), - fileType: file.type, - projectId: project.rowId, - }, - }, - }; - console.log(variables); - createAttachment({ - variables, - onError: (err) => console.error(err), - onCompleted: goToProjectAttachmentsView, - }); - }; - - return ( - -

    {project.projectName}

    - {isCreatingAttachment ? ( - <> - Uploading file... - - ) : ( - - Upload Attachment - - )} -
    - ); -} - -export default withRelay(UploadAttachment, pageQuery, withRelayOptions); diff --git a/app/tests/unit/components/Attachment/AttachmentTableRow.test.tsx b/app/tests/unit/components/Attachment/AttachmentTableRow.test.tsx index 085b51a3d3..c73453a410 100644 --- a/app/tests/unit/components/Attachment/AttachmentTableRow.test.tsx +++ b/app/tests/unit/components/Attachment/AttachmentTableRow.test.tsx @@ -72,19 +72,6 @@ describe("The Attachment table row component", () => { expect(screen.getByText("Cif User")).toBeInTheDocument(); expect(screen.getByText("2019-01-01")).toBeInTheDocument(); }); - it("has a working view button", () => { - componentTestingHelper.loadQuery(); - componentTestingHelper.renderComponent(); - - const viewButton = screen.getByText("View"); - viewButton.click(); - - expect(componentTestingHelper.router.push).toHaveBeenCalledWith( - "/cif/attachments/[attachment]?attachment=Cif+Test+Attachment+ID", - expect.anything(), - expect.anything() - ); - }); it("has a working download button", () => { componentTestingHelper.loadQuery(); componentTestingHelper.renderComponent(); diff --git a/app/tests/unit/components/TaskList.test.tsx b/app/tests/unit/components/TaskList.test.tsx index 103459605b..1b0553d74a 100644 --- a/app/tests/unit/components/TaskList.test.tsx +++ b/app/tests/unit/components/TaskList.test.tsx @@ -59,12 +59,21 @@ describe("The ProjectManagerForm", () => { expect( screen.getByText("Editing: test-project-proposal-reference") ).toBeInTheDocument(); + expect(screen.queryByText("Attachments")).not.toBeInTheDocument(); }); it("Renders the TaskList In create", () => { componentTestingHelper.loadQuery(); componentTestingHelper.renderComponent(undefined, { mode: "create" }); expect(screen.getByText("Add a Project")).toBeInTheDocument(); + expect(screen.queryByText("Attachments")).not.toBeInTheDocument(); + }); + + it("Renders the TaskList in view", () => { + componentTestingHelper.loadQuery(); + componentTestingHelper.renderComponent(undefined, { mode: "view" }); + + expect(screen.queryByText("Attachments")).toBeInTheDocument(); }); it("Renders the proper form statuses", () => { @@ -171,4 +180,16 @@ describe("The ProjectManagerForm", () => { expect.any(Object) ); }); + + it("Calls the proper getRoute function when clicking Attachments", () => { + componentTestingHelper.loadQuery(); + componentTestingHelper.renderComponent(undefined, { mode: "view" }); + + fireEvent.click(screen.getByText(/Attachments/i)); + expect(componentTestingHelper.router.push).toHaveBeenCalledWith( + "/cif/project-revision/[projectRevision]/attachments?projectRevision=test-project-revision-id", + "/cif/project-revision/test-project-revision-id/attachments", + expect.any(Object) + ); + }); }); diff --git a/app/tests/unit/pages/project/attachments.test.tsx b/app/tests/unit/pages/project/attachments.test.tsx index 5de1ea3aaa..f1d08adcaa 100644 --- a/app/tests/unit/pages/project/attachments.test.tsx +++ b/app/tests/unit/pages/project/attachments.test.tsx @@ -1,18 +1,19 @@ import { screen } from "@testing-library/react"; -import { ProjectAttachments } from "pages/cif/project/[project]/attachments"; +import userEvent from "@testing-library/user-event"; +import { ProjectAttachments } from "pages/cif/project-revision/[projectRevision]/attachments"; import PageTestingHelper from "tests/helpers/pageTestingHelper"; import compiledAttachmentsQuery, { attachmentsQuery, } from "__generated__/attachmentsQuery.graphql"; const defaultQueryResolver = { - Query() { + Project() { return { - project: { - id: "test-cif-project", - projectName: "Test CIF Project", - }, - allAttachments: { + id: "test-cif-project", + rowId: 12345, + projectName: "Test CIF Project", + attachments: { + __id: "test-attachments-connection!", totalCount: 2, edges: [ { @@ -35,7 +36,7 @@ const pageTestingHelper = new PageTestingHelper({ pageComponent: ProjectAttachments, compiledQuery: compiledAttachmentsQuery, defaultQueryResolver: defaultQueryResolver, - defaultQueryVariables: { project: "test-cif-project" }, + defaultQueryVariables: { projectRevision: "test-cif-project-revision" }, }); describe("The project's attachment page", () => { @@ -50,15 +51,30 @@ describe("The project's attachment page", () => { // 5 rows: 1 header, 1 filter, 1 for the pagination, and 2 for the attachments expect(screen.getAllByRole("row")).toHaveLength(5); }); + + // eslint-disable-next-line jest/expect-expect it("has a button to upload an attachment", () => { pageTestingHelper.loadQuery(); pageTestingHelper.renderPage(); - screen.getByText("Upload New Attachment").click(); - expect(pageTestingHelper.router.push).toHaveBeenCalledWith( - "/cif/project/[project]/upload-attachment?project=test-cif-project", - expect.anything(), - expect.anything() - ); + const testFile = new File(["O.o"], "eyes.jpg", { type: "image/jpeg" }); + const uploadControl = screen.getByLabelText("upload-attachment"); + + userEvent.upload(uploadControl, [testFile]); + + pageTestingHelper.expectMutationToBeCalled("createAttachmentMutation", { + input: { + attachment: { + file: expect.any(File), + fileName: "eyes.jpg", + fileSize: "3 Bytes", + fileType: "image/jpeg", + projectId: 12345, + }, + }, + connections: [ + "client:test-cif-project:__connection_attachments_connection", + ], + }); }); }); diff --git a/app/tests/unit/pages/project/upload-attachment.test.tsx b/app/tests/unit/pages/project/upload-attachment.test.tsx deleted file mode 100644 index fa84df276d..0000000000 --- a/app/tests/unit/pages/project/upload-attachment.test.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { screen } from "@testing-library/react"; -import userEvent from "@testing-library/user-event"; -import { UploadAttachment } from "pages/cif/project/[project]/upload-attachment"; -import PageTestingHelper from "tests/helpers/pageTestingHelper"; -import compiledUploadAttachmentQuery, { - uploadAttachmentQuery, -} from "__generated__/uploadAttachmentQuery.graphql"; - -const defaultQueryResolver = { - Query() { - return { - project: { - id: "project-id-1", - rowId: "1", - projectName: "Test Project", - }, - }; - }, -}; - -const pageTestingHelper = new PageTestingHelper({ - pageComponent: UploadAttachment, - compiledQuery: compiledUploadAttachmentQuery, - defaultQueryResolver: defaultQueryResolver, - defaultQueryVariables: { project: "test-cif-project" }, -}); - -describe("The upload attachment page", () => { - beforeEach(() => { - jest.resetModules(); - pageTestingHelper.reinit(); - }); - it("Has an upload button that triggers an upload mutation", () => { - pageTestingHelper.loadQuery(); - pageTestingHelper.renderPage(); - - const testFile = new File(["O.o"], "eyes.jpg", { type: "image/jpeg" }); - - const uploadButton = screen.getByLabelText("upload-attachment"); - userEvent.upload(uploadButton, testFile); - - const mutationCall = - pageTestingHelper.environment.mock.getMostRecentOperation(); - - expect(mutationCall.request.node.operation.name).toBe( - "createAttachmentMutation" - ); - expect(mutationCall.request).toMatchObject({ - variables: { - input: { - attachment: { - file: expect.any(File), - fileName: "eyes.jpg", - fileSize: "3 Bytes", - fileType: "image/jpeg", - projectId: "1", - }, - }, - }, - }); - }); -});