Skip to content

Commit

Permalink
align search form and results page with designs (#2609)
Browse files Browse the repository at this point in the history
* seed some styles

* pull out alert

* refactor search form

* refactor and make results view component dumb

* make component more readable

* new files

* componentize sidenav

* new folder

* [pre-commit.ci] auto fixes from pre-commit hooks

* move some files around

* imports

* [pre-commit.ci] auto fixes from pre-commit hooks

* copy

* [pre-commit.ci] auto fixes from pre-commit hooks

* add a more reasonable sidenav default

* [pre-commit.ci] auto fixes from pre-commit hooks

* remove log

* docs

* rename

* lint more

* [pre-commit.ci] auto fixes from pre-commit hooks

* more file moving

* new files

* third times the charm

* try this?

* add missing prop

* [pre-commit.ci] auto fixes from pre-commit hooks

* one more

* change padding

* [pre-commit.ci] auto fixes from pre-commit hooks

* fix one test

* [pre-commit.ci] auto fixes from pre-commit hooks

* debug in ci

* [pre-commit.ci] auto fixes from pre-commit hooks

* change to 10

* remove debug log

* [pre-commit.ci] auto fixes from pre-commit hooks

* change selector

* [pre-commit.ci] auto fixes from pre-commit hooks

* change role

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
2 people authored and nickbristow committed Sep 25, 2024
1 parent d4c0455 commit 029f3cb
Show file tree
Hide file tree
Showing 26 changed files with 642 additions and 534 deletions.
30 changes: 14 additions & 16 deletions containers/tefca-viewer/e2e/query_workflow.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ test.describe("querying with the TryTEFCA viewer", () => {
// Make sure we have a results page with a single patient
// Non-interactive 'div' elements in the table should be located by text
await expect(
page.getByRole("heading", { name: "Query Results" }),
page.getByRole("heading", { name: "Patient Record" }),
).toBeVisible();
await expect(page.getByText("Patient Name")).toBeVisible();
await expect(page.getByText("WATERMELON SPROUT MCGEE")).toBeVisible();
Expand All @@ -76,20 +76,18 @@ test.describe("querying with the TryTEFCA viewer", () => {
"Interested in learning more about using the TEFCA Query Connector for your jurisdiction? Send us an email at [email protected]",
);

// Let's get a little schwifty: there are multiple possible resolutions for 'Observations',
// so we can chain things to get the table header to make sure the accordion is open
// Check to see if the accordion button is open
await expect(
page
.getByTestId("accordionItem_observations")
.getByRole("heading", { name: "Observations" }),
page.getByRole("button", { name: "Observations", expanded: true }),
).toBeVisible();

// We can also just directly ask the page to find us filtered table rows
await expect(page.locator("tbody").locator("tr")).toHaveCount(5);

// Now let's use the return to search to go back to a blank form
await page.getByRole("link", { name: "New patient search" }).click();
await page.getByRole("button", { name: "New patient search" }).click();
await expect(
page.getByRole("heading", { name: "Search for a Patient" }),
page.getByRole("heading", { name: "Search for a Patient", exact: true }),
).toBeVisible();
});

Expand All @@ -116,7 +114,7 @@ test.describe("querying with the TryTEFCA viewer", () => {
await expect(page.getByText("There are no patient records")).toBeVisible();
await page.getByRole("link", { name: "Search for a new patient" }).click();
await expect(
page.getByRole("heading", { name: "Search for a Patient" }),
page.getByRole("heading", { name: "Search for a Patient", exact: true }),
).toBeVisible();
});

Expand All @@ -138,7 +136,7 @@ test.describe("querying with the TryTEFCA viewer", () => {
// Among verification, make sure phone number is right
await page.getByRole("button", { name: "Search for patient" }).click();
await expect(
page.getByRole("heading", { name: "Query Results" }),
page.getByRole("heading", { name: "Patient Record" }),
).toBeVisible();
await expect(page.getByText("Patient Name")).toBeVisible();
await expect(page.getByText("Veronica Anne Blackstone")).toBeVisible();
Expand All @@ -158,7 +156,7 @@ test.describe("querying with the TryTEFCA viewer", () => {
await page.getByRole("button", { name: "Fill fields" }).click();
await page.getByRole("button", { name: "Search for patient" }).click();
await expect(
page.getByRole("heading", { name: "Query Results" }),
page.getByRole("heading", { name: "Patient Record" }),
).toBeVisible();
});

Expand All @@ -171,7 +169,7 @@ test.describe("querying with the TryTEFCA viewer", () => {
await page.getByLabel("Phone Number").fill("");
await page.getByRole("button", { name: "Search for patient" }).click();
await expect(
page.getByRole("heading", { name: "Query Results" }),
page.getByRole("heading", { name: "Patient Record" }),
).toBeVisible();
});
});
Expand Down Expand Up @@ -214,7 +212,7 @@ test.describe("Test the user journey of a 'tester'", () => {

// Make sure we have a results page with a single patient
await expect(
page.getByRole("heading", { name: "Query Results" }),
page.getByRole("heading", { name: "Patient Record" }),
).toBeVisible();
await expect(page.getByText("Patient Name")).toBeVisible();
await expect(page.getByText("WATERMELON SPROUT MCGEE")).toBeVisible();
Expand All @@ -240,7 +238,7 @@ test.describe("Test the user journey of a 'tester'", () => {

// Make sure we have a results page with a single patient
await expect(
page.getByRole("heading", { name: "Query Results" }),
page.getByRole("heading", { name: "Patient Record" }),
).toBeVisible();
await expect(page.getByText("Patient Name")).toBeVisible();
await expect(page.getByText("WATERMELON SPROUT MCGEE")).toBeVisible();
Expand Down Expand Up @@ -277,10 +275,10 @@ test.describe("Test the user journey of a 'tester'", () => {

// Make sure we have a results page with a single patient & appropriate back buttons
await expect(
page.getByRole("heading", { name: "Query Results" }),
page.getByRole("heading", { name: "Patient Record" }),
).toBeVisible();
await expect(
page.getByRole("link", { name: "New patient search" }),
page.getByRole("button", { name: "New patient search" }),
).toBeVisible();

await page.getByRole("link", { name: "Return to search results" }).click();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
"use client";

import React, { useState, useEffect } from "react";
import { Button, Icon } from "@trussworks/react-uswds";
import { Button } from "@trussworks/react-uswds";
import { ValueSetType, ValueSetItem } from "../../constants";
import { UseCaseQueryResponse } from "@/app/query-service";
import LoadingView from "./LoadingView";
import { showRedirectConfirmation } from "./RedirectionToast";
import { showRedirectConfirmation } from "../designSystem/redirectToast/RedirectToast";
import styles from "./customizeQuery/customizeQuery.module.css";
import CustomizeQueryAccordionHeader from "./customizeQuery/CustomizeQueryAccordionHeader";
import CustomizeQueryAccordionBody from "./customizeQuery/CustomizeQueryAccordionBody";
import Accordion from "./Accordion";
import Accordion from "../designSystem/Accordion";
import CustomizeQueryNav from "./customizeQuery/CustomizeQueryNav";
import { mapValueSetItemsToValueSetTypes } from "./customizeQuery/customizeQueryUtils";
import Backlink from "./backLink/Backlink";

interface CustomizeQueryProps {
useCaseQueryResponse: UseCaseQueryResponse;
Expand Down Expand Up @@ -160,11 +161,9 @@ const CustomizeQuery: React.FC<CustomizeQueryProps> = ({
}, [valueSetOptions, activeTab]);

return (
<div className="main-container">
<div>
<div className="padding-top-3">
<a href="#" onClick={() => goBack()} className="back-link">
<Icon.ArrowBack /> Return to patient search
</a>
<Backlink onClick={goBack} label="Return to patient search" />
</div>
<LoadingView loading={!useCaseQueryResponse} />
<h1 className="font-sans-2xl text-bold margin-top-205">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from "../../query-service";
import ResultsView from "./ResultsView";
import { ValueSetItem } from "@/app/constants";
import Backlink from "./backLink/Backlink";

/**
* The props for the MultiplePatientSearchResults component.
Expand Down Expand Up @@ -58,10 +59,10 @@ const MultiplePatientSearchResults: React.FC<
goBackToMultiplePatients={() =>
setSingleUseCaseQueryResponse(undefined)
}
queryName={originalRequest.use_case}
/>
);
}

return (
<>
<div className="multiple-patient-search-results">
Expand Down Expand Up @@ -108,9 +109,7 @@ const MultiplePatientSearchResults: React.FC<
</tbody>
</Table>
<h3>Not seeing what you are looking for?</h3>
<a href="#" className="back-link" onClick={() => goBack()}>
Return to patient search
</a>
<Backlink onClick={goBack} label="Return to patient search " />
</div>
</>
);
Expand Down
168 changes: 119 additions & 49 deletions containers/tefca-viewer/src/app/query/components/ResultsView.tsx
Original file line number Diff line number Diff line change
@@ -1,87 +1,157 @@
import { UseCaseQueryResponse } from "../../query-service";
import SideNav from "./SideNav";
import ResultsViewSideNav, {
NavSection,
} from "./resultsView/ResultsViewSideNav";
import React, { useEffect } from "react";
import { Alert, Icon } from "@trussworks/react-uswds";
import ResultsViewTable from "./ResultsViewTable";
import ResultsViewTable from "./resultsView/ResultsViewTable";
import Backlink from "./backLink/Backlink";
import styles from "../page.module.css";
import ConditionsTable from "./ConditionsTable";
import Demographics from "./Demographics";
import DiagnosticReportTable from "./DiagnosticReportTable";
import EncounterTable from "./EncounterTable";
import MedicationRequestTable from "./MedicationRequestTable";
import ObservationTable from "./ObservationTable";

type ResultsViewProps = {
useCaseQueryResponse: UseCaseQueryResponse;
goBack: () => void;
goBackToMultiplePatients?: () => void;
queryName: string;
};

export type ResultsViewAccordionItem = {
title: string;
subtitle?: string;
content?: React.ReactNode;
};

/**
* The QueryView component to render the query results.
* @param props - The props for the QueryView component.
* @param props.useCaseQueryResponse - The response from the query service.
* @param props.goBack - The function to go back to the previous page.
* @param props.goBackToMultiplePatients - The function to go back to the multiple patients selection page.
* @param props.goBackToMultiplePatients - The function to go back to the
* multiple patients selection page.
* @param props.queryName - The name of the saved query to display to the user
* @returns The QueryView component.
*/
const ResultsView: React.FC<ResultsViewProps> = ({
useCaseQueryResponse,
goBack,
goBackToMultiplePatients,
queryName,
}) => {
useEffect(() => {
window.scrollTo(0, 0);
}, []);

const accordionItems =
mapQueryResponseToAccordionDataStructure(useCaseQueryResponse);

const sideNavContent = accordionItems
.map((item) => {
if (item.content) {
return { title: item.title, subtitle: item?.subtitle };
}
})
.filter((i) => Boolean(i)) as NavSection[];

return (
<>
<Alert type="info" headingLevel="h4" slim className="custom-alert">
Interested in learning more about using the TEFCA Query Connector for
your jurisdiction? Send us an email at{" "}
<a
href="mailto:[email protected]"
style={{
color: "inherit",
fontWeight: "bold",
textDecoration: "underline",
}}
>
[email protected]
</a>
</Alert>

<div className="results-banner">
<div className="results-banner-content usa-nav-container">
{goBackToMultiplePatients && (
<>
<a
href="#"
onClick={() => goBackToMultiplePatients()}
className="back-link"
>
<Icon.ArrowBack />
Return to search results
</a>
<div className="results-banner-divider">|</div>
</>
)}
<div className={`${styles.resultsBannerContent}`}>
<Backlink
onClick={goBackToMultiplePatients ?? goBack}
label="Return to search results"
/>

<a href="#" onClick={() => goBack()} className="back-link">
<button
className="usa-button usa-button--outline margin-left-auto"
onClick={() => goBack()}
>
New patient search
</a>
</button>
</div>
</div>
<div className="main-container grid-container grid-row">
<div className="nav-wrapper tablet:grid-col-3">
<nav className="sticky-nav">
<SideNav />
</nav>
<div className="margin-bottom-3">
<h2 className="margin-0" id="ecr-summary">
Patient Record
</h2>
<h3>
Query:{" "}
<span className="text-normal display-inline-block"> {queryName}</span>
</h3>
</div>

<div className=" grid-container grid-row grid-gap-md padding-0 ">
<div className="tablet:grid-col-3">
<ResultsViewSideNav items={sideNavContent} />
</div>
<div className="tablet:grid-col-9">
<div className="ecr-content">
<h2 className="margin-bottom-3" id="ecr-summary">
Query Results
</h2>
<div className="margin-top-6">
<ResultsViewTable queryResponse={useCaseQueryResponse} />
</div>
</div>
<div className="tablet:grid-col-9 ecr-content">
<ResultsViewTable accordionItems={accordionItems} />
</div>
</div>
</>
);
};
export default ResultsView;

function mapQueryResponseToAccordionDataStructure(
useCaseQueryResponse: UseCaseQueryResponse,
) {
const patient =
useCaseQueryResponse.Patient && useCaseQueryResponse.Patient.length === 1
? useCaseQueryResponse.Patient[0]
: null;
const observations = useCaseQueryResponse.Observation
? useCaseQueryResponse.Observation
: null;
const encounters = useCaseQueryResponse.Encounter
? useCaseQueryResponse.Encounter
: null;
const conditions = useCaseQueryResponse.Condition
? useCaseQueryResponse.Condition
: null;
const diagnosticReports = useCaseQueryResponse.DiagnosticReport
? useCaseQueryResponse.DiagnosticReport
: null;
const medicationRequests = useCaseQueryResponse.MedicationRequest
? useCaseQueryResponse.MedicationRequest
: null;

const accordionItems: ResultsViewAccordionItem[] = [
{
title: "Patient Info",
subtitle: "Demographics",
content: patient ? <Demographics patient={patient} /> : null,
},
{
title: "Observations",
content: observations ? (
<ObservationTable observations={observations} />
) : null,
},
{
title: "Encounters",
content: encounters ? <EncounterTable encounters={encounters} /> : null,
},
{
title: "Conditions",
content: conditions ? <ConditionsTable conditions={conditions} /> : null,
},
{
title: "Diagnostic Reports",
content: diagnosticReports ? (
<DiagnosticReportTable diagnosticReports={diagnosticReports} />
) : null,
},
{
title: "Medication Requests",
content: medicationRequests ? (
<MedicationRequestTable medicationRequests={medicationRequests} />
) : null,
},
];
return accordionItems;
}
Loading

0 comments on commit 029f3cb

Please sign in to comment.