From e2c2ce4f671b17deb887e32f483c8650cb0ad6c1 Mon Sep 17 00:00:00 2001 From: knguyenrise8 <159168836+knguyenrise8@users.noreply.github.com> Date: Fri, 29 Nov 2024 15:29:40 -0600 Subject: [PATCH] feat(RV-420): Add home page first time UX (#444) --- frontend/src/App.scss | 53 +++++++++++++++++++ frontend/src/App.test.tsx | 89 +++++++++++++++++++++++++++++++ frontend/src/App.tsx | 94 +++++++++++++++++++++++++-------- frontend/src/assets/comment.svg | 19 +++++++ frontend/src/assets/csv.svg | 13 +++++ 5 files changed, 247 insertions(+), 21 deletions(-) create mode 100644 frontend/src/App.test.tsx create mode 100644 frontend/src/assets/comment.svg create mode 100644 frontend/src/assets/csv.svg diff --git a/frontend/src/App.scss b/frontend/src/App.scss index 68a99ec5..5aaf2db9 100644 --- a/frontend/src/App.scss +++ b/frontend/src/App.scss @@ -25,3 +25,56 @@ .nav-link { text-decoration: none; } + +.home-header { + height: 80px; + display: flex; + align-items: center; + border-bottom: 1px solid rgba(0, 94, 162, 0.14); + box-shadow: 0px 2px 4px 0px rgba(0, 94, 162, 0.04); +} + +.first-time-content { + padding-inline: 116px; + width: 800px; + height: 700px; + background: white; + border: 1px solid #F0F0F0 !important; +} + +.first-time-header { + color: #000; + font-size: 22px; + font-style: normal; + font-weight: 700; + line-height: normal; +} + +.first-time-copy { + color: #000; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 162%; +} + +.first-time-card { + border-radius: 5px; + border: 2px solid #F0F0F0; + background: #009EC1; + height: 300px; + width: 550px; +} + +.img-copy { + color: #FFF; + font-size: 22px; + font-style: normal; + font-weight: 700; + line-height: normal; +} + +.card-column-image { + padding-left: 36px; + width: 200px; +} \ No newline at end of file diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx new file mode 100644 index 00000000..7eb9e153 --- /dev/null +++ b/frontend/src/App.test.tsx @@ -0,0 +1,89 @@ +import { render, screen, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { BrowserRouter } from 'react-router-dom'; +import App from './App'; + +// Mock the imports +vi.mock('./components/AppHeader/AppHeader.tsx', () => ({ + AppHeader: () =>
AppHeader
, +})); + +vi.mock('./components/TemplatesIndex/TemplatesIndex.tsx', () => ({ + TemplatesIndex: () =>
TemplatesIndex
, +})); + +vi.mock('./assets/comment.svg', () => ({ + default: 'comment.svg', +})); + +vi.mock('./assets/csv.svg', () => ({ + default: 'csv.svg', +})); + +describe('App component', () => { + beforeEach(() => { + localStorage.clear(); + }); + + const renderComponent = () => { + render( + + + + ); + }; + + it('renders the AppHeader component', () => { + renderComponent(); + expect(screen.getByTestId('app-header')).toBeInTheDocument(); + }); + + it('displays first-time experience content on initial visit', () => { + renderComponent(); + expect(screen.getByTestId('first-time-exp')).toBeInTheDocument(); + }); + + it('displays regular content on subsequent visits', () => { + localStorage.setItem('hasVisited', 'true'); + renderComponent(); + expect(screen.queryByTestId('first-time-exp')).not.toBeInTheDocument(); + expect(screen.getByTestId('templates-index')).toBeInTheDocument(); + }); + + it('navigates to the correct URL when a navigation link is clicked (0)', () => { + renderComponent(); + const navLink = screen.getByTestId('nav-link-0'); + fireEvent.click(navLink); + expect(navLink).toHaveAttribute('href', '/'); + }); + + it('navigates to the correct URL when a navigation link is clicked (1)', () => { + renderComponent(); + const navLink = screen.getByTestId('nav-link-1'); + fireEvent.click(navLink); + expect(navLink).toHaveAttribute('href', '/labels'); + }); + + it('navigates to the correct URL when a navigation link is clicked (2)', () => { + renderComponent(); + const navLink = screen.getByTestId('nav-link-2'); + fireEvent.click(navLink); + expect(navLink).toHaveAttribute('href', '/dashboard'); + }); + + it('navigates to the new template upload page when the button is clicked', () => { + renderComponent(); + const newTemplateButton = screen.getByTestId('new-template-button'); + fireEvent.click(newTemplateButton); + expect(window.location.pathname).toBe('/new-template/upload'); + }); + + it('renders navigation links correctly', () => { + renderComponent(); + const navLinks = ['Annotate and Extract', 'Label Management', 'Dashboard']; + navLinks.forEach((_, idx) => { + expect(screen.getByTestId(`nav-link-${idx}`)).toBeInTheDocument(); + }); + }); +}); \ No newline at end of file diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 219898e9..32201b91 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,13 +1,27 @@ -import "./App.scss"; -import { Link } from "@trussworks/react-uswds"; -import { useLocation } from "react-router-dom"; +import { useState, useEffect } from "react"; +import { Button, Link } from "@trussworks/react-uswds"; +import { useLocation, useNavigate } from "react-router-dom"; import { AppHeader } from "./components/AppHeader/AppHeader.tsx"; - import { TemplatesIndex } from "./components/TemplatesIndex/TemplatesIndex.tsx"; +import Comment from './assets/comment.svg'; +import CSV from './assets/csv.svg'; +import "./App.scss"; + function App() { const { pathname } = useLocation(); + const navigate = useNavigate(); + const [isFirstVisit, setIsFirstVisit] = useState(false); + + useEffect(() => { + const hasVisited = localStorage.getItem("hasVisited"); + if (!hasVisited) { + setIsFirstVisit(true); + localStorage.setItem("hasVisited", "true"); + } + }, []); + const navLinks = [ { text: "Annotate and Extract", url: "/" }, { @@ -19,10 +33,10 @@ function App() { return ( <> -
+
-
-
+
+
{navLinks.map((i, idx) => { return ( @@ -30,6 +44,7 @@ function App() { key={idx} href={i.url} className={`border-left-2px padding-left-1 padding-top-2 padding-bottom-2 nav-link ${i.url === pathname ? "text-bold" : "border-primary-dark"}`} + data-testid={`nav-link-${idx}`} > {i.text} @@ -37,19 +52,56 @@ function App() { })}
- -
-

Annotate and Extract

-
-

- Welcome Blake, -

-

- Extract data from any PDFs, or images to send to your - surveillance systems using data from your saved templates or - create new segmentations. -

- +
+

Annotate and Extract

+
+ {isFirstVisit ? ( +
+

+ Welcome to Report Vision +

+

+ Let ReportVision take your lab reports from faxes and PDF’s and extract the data for seamless ingestion into your surveillance system. +

+
+
+ comment + csv +
+
+

+ 1. Create templates for new lab reports. +

+

+ 2. Extract data and download it. +

+
+
+

+ Let’s create your first annotated template for a lab report to enable quick extractions. +

+ +
+ ) : ( +
+

+ Welcome Blake, +

+

+ Extract data from any PDFs, or images to send to your + surveillance systems using data from your saved templates or + create new segmentations. +

+ +
+ )}
@@ -58,4 +110,4 @@ function App() { ); } -export default App; +export default App; \ No newline at end of file diff --git a/frontend/src/assets/comment.svg b/frontend/src/assets/comment.svg new file mode 100644 index 00000000..ca942b77 --- /dev/null +++ b/frontend/src/assets/comment.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/csv.svg b/frontend/src/assets/csv.svg new file mode 100644 index 00000000..14ea28b9 --- /dev/null +++ b/frontend/src/assets/csv.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + +