Merge remote-tracking branch 'origin/patient-info-form' into feature/diagnosis-component

This commit is contained in:
mlmclaughlin committed 2020-04-15 11:07:06 -05:00
commit 8d5dc9623c
15 files changed
+740 -67

No files matched your search

+126
View File
@@ -1678,6 +1678,11 @@
"@types/node": "*"
}
},
"@types/history": {
"version": "4.7.5",
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.5.tgz",
"integrity": "sha512-wLD/Aq2VggCJXSjxEwrMafIP51Z+13H78nXIX0ABEuIGhmB5sNGbR113MOKo+yfw+RDo1ZU3DM6yfnnRF/+ouw=="
},
"@types/istanbul-lib-coverage": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz",
@@ -1755,6 +1760,25 @@
"@types/react": "*"
}
},
"@types/react-router": {
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.5.tgz",
"integrity": "sha512-RZPdCtZympi6X7EkGyaU7ISiAujDYTWgqMF9owE3P6efITw27IWQykcti0BvA5h4Mu1LLl5rxrpO3r8kHyUZ/Q==",
"requires": {
"@types/history": "*",
"@types/react": "*"
}
},
"@types/react-router-dom": {
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.4.tgz",
"integrity": "sha512-LO0z5qqSfWdYtCNsRm8/OMnnkv52hwADJKrAfpKIyfHclORllcgAMGypEA7ajHm38+jOonKLx8nPygNAAZGxtg==",
"requires": {
"@types/history": "*",
"@types/react": "*",
"@types/react-router": "*"
}
},
"@types/react-test-renderer": {
"version": "16.9.2",
"resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-16.9.2.tgz",
@@ -6636,6 +6660,11 @@
"resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE="
},
"gud": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
"integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw=="
},
"gzip-size": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz",
@@ -6771,6 +6800,19 @@
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
},
"history": {
"version": "4.10.1",
"resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
"integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==",
"requires": {
"@babel/runtime": "^7.1.2",
"loose-envify": "^1.2.0",
"resolve-pathname": "^3.0.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0",
"value-equal": "^1.0.1"
}
},
"hmac-drbg": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
@@ -6781,6 +6823,14 @@
"minimalistic-crypto-utils": "^1.0.1"
}
},
"hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"requires": {
"react-is": "^16.7.0"
}
},
"hosted-git-info": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz",
@@ -9586,6 +9636,16 @@
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.0.tgz",
"integrity": "sha1-z8RcN+nsDY8KDsPdTvf3w6vjklY="
},
"mini-create-react-context": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.3.2.tgz",
"integrity": "sha512-2v+OeetEyliMt5VHMXsBhABoJ0/M4RCe7fatd/fBy6SMiKazUSEt3gxxypfnk2SHMkdBYvorHRoQxuGoiwbzAw==",
"requires": {
"@babel/runtime": "^7.4.0",
"gud": "^1.0.0",
"tiny-warning": "^1.0.2"
}
},
"mini-css-extract-plugin": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz",
@@ -12269,6 +12329,52 @@
"react-tween-state": "^0.1.5"
}
},
"react-router": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.1.2.tgz",
"integrity": "sha512-yjEuMFy1ONK246B+rsa0cUam5OeAQ8pyclRDgpxuSCrAlJ1qN9uZ5IgyKC7gQg0w8OM50NXHEegPh/ks9YuR2A==",
"requires": {
"@babel/runtime": "^7.1.2",
"history": "^4.9.0",
"hoist-non-react-statics": "^3.1.0",
"loose-envify": "^1.3.1",
"mini-create-react-context": "^0.3.0",
"path-to-regexp": "^1.7.0",
"prop-types": "^15.6.2",
"react-is": "^16.6.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0"
},
"dependencies": {
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
"path-to-regexp": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
"integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
"requires": {
"isarray": "0.0.1"
}
}
}
},
"react-router-dom": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.1.2.tgz",
"integrity": "sha512-7BPHAaIwWpZS074UKaw1FjVdZBSVWEk8IuDXdB+OkLb8vd/WRQIpA4ag9WQk61aEfQs47wHyjWUoUGGZxpQXew==",
"requires": {
"@babel/runtime": "^7.1.2",
"history": "^4.9.0",
"loose-envify": "^1.3.1",
"prop-types": "^15.6.2",
"react-router": "5.1.2",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0"
}
},
"react-scripts": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.3.1.tgz",
@@ -12980,6 +13086,11 @@
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
"integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
},
"resolve-pathname": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz",
"integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng=="
},
"resolve-url": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
@@ -14891,6 +15002,16 @@
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
},
"tiny-invariant": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz",
"integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw=="
},
"tiny-warning": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
},
"tinycolor2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
@@ -15364,6 +15485,11 @@
"spdx-expression-parse": "^3.0.0"
}
},
"value-equal": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
"integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+2
View File
@@ -11,10 +11,12 @@
"@types/node": "^12.12.26",
"@types/react": "^16.9.19",
"@types/react-dom": "^16.9.5",
"@types/react-router-dom": "^5.1.4",
"antd-mobile": "^2.3.1",
"node-sass": "^4.13.1",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-router-dom": "^5.1.2",
"react-scripts": "3.3.1",
"typescript": "^3.7.5"
},
+29 -5
View File
@@ -1,14 +1,38 @@
import React from "react";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Login from "./components/Login";
import LandingPage from "./components/LandingPage";
import PatientInfoForm from "./components/PatientInfoForm";
import Wrapper from "./Wrapper";
import "./scss/App.scss";
import "./antd-mobile.css";
import Login from "./components/Login";
const App = () => {
return (
<>
<Login />
</>
<Router>
<Switch>
<Route path="/login">
<Login />
</Route>
<Route path="/landing">
<LandingPage />
</Route>
<Route path="/patientinfo">
<Wrapper
navTitle="Patient Information"
leftArrowText="Home"
leftArrowRoute="/"
>
<PatientInfoForm />
</Wrapper>
</Route>
<Route path="/">
<Login />
</Route>
</Switch>
</Router>
);
};
+74
View File
@@ -0,0 +1,74 @@
import React from "react";
import { render, queryByAttribute, fireEvent } from "@testing-library/react";
import { useHistory } from "react-router-dom";
import Wrapper from "./Wrapper";
const mockHistoryPush = jest.fn();
jest.mock("react-router-dom", () => ({
useHistory: () => ({
push: mockHistoryPush,
}),
}));
test("renders it's inner contents", () => {
const dom = render(
<Wrapper leftArrowText="back">
<div>Hello!</div>
</Wrapper>
);
const textContents = dom.getByText("Hello!");
expect(textContents).toBeInTheDocument();
});
test("renders navBar when given navTitle", () => {
const dom = render(
<Wrapper navTitle="CHI">
<div>Hello!</div>
</Wrapper>
);
const getById = queryByAttribute.bind(null, "id");
const renderedNavBar = getById(dom.container, "nav-bar");
const navTitle = dom.getByText("CHI");
expect(renderedNavBar).toBeTruthy();
expect(navTitle).toBeInTheDocument();
});
test("renders no navBar when not given props", () => {
const dom = render(
<Wrapper>
<div>Hello!</div>
</Wrapper>
);
const getById = queryByAttribute.bind(null, "id");
const renderedNavBar = getById(dom.container, "nav-bar");
expect(renderedNavBar).toBeFalsy();
});
test("renders leftArrowText", () => {
const dom = render(
<Wrapper leftArrowText="back">
<div>Hello!</div>
</Wrapper>
);
const leftArrowText = dom.getByText("back");
expect(leftArrowText).toBeInTheDocument();
});
test("redirects on left arrow click ", () => {
const dom = render(
<Wrapper leftArrowText="back" leftArrowRoute="/">
<div>Hello!</div>
</Wrapper>
);
const leftArrowText = dom.getByText("back");
fireEvent.click(leftArrowText);
expect(useHistory().push).toBeCalledWith("/");
});
+42
View File
@@ -0,0 +1,42 @@
import React from "react";
import { NavBar, Icon } from "antd-mobile";
import { useHistory } from "react-router-dom";
import "./scss/Wrapper.scss";
type props = {
navTitle?: string;
leftArrowText?: string;
leftArrowRoute?: string;
children: React.ReactNode;
};
const Wrapper = (props: props) => {
const { navTitle, leftArrowText, leftArrowRoute } = props;
const hist = useHistory();
const needNavBar = navTitle || leftArrowText || leftArrowRoute;
const leftContentArr = leftArrowText
? [<Icon type="left" />, `${leftArrowText}`]
: [];
const leftRoute = leftArrowRoute ? `${leftArrowRoute}` : "/";
return (
<div id="primary-wrapper">
<div id="wrapper-contents">
{needNavBar && (
<NavBar
id="nav-bar"
mode="light"
onLeftClick={() => hist.push(leftRoute)}
leftContent={leftContentArr}
>
{`${navTitle}`}
</NavBar>
)}
{props.children}
</div>
</div>
);
};
export default Wrapper;
+2 -1
View File
@@ -1,11 +1,12 @@
import React, { ReactElement } from "react";
import { WhiteSpace, Button } from "antd-mobile";
import Icon, { UserAddOutlined } from "@ant-design/icons";
import { UserAddOutlined } from "@ant-design/icons";
import "../scss/LandingPage.scss";
export default function LandingPage(): ReactElement {
/* istanbul ignore next */
// TODO: these onClick functions could be mocked in a unit test
const patientLookup = () => {
console.log("Clicked Patient Lookup");
};
+16 -18
View File
@@ -1,31 +1,29 @@
import React, { ReactElement } from 'react'
import useApi from '../hooks/useApi'
import {InputItem, Button} from 'antd-mobile'
import '../scss/Login.scss'
import React, { ReactElement } from "react";
import useApi from "../hooks/useApi";
import { InputItem, Button } from "antd-mobile";
import "../scss/Login.scss";
export default function Login(): ReactElement {
const {response, error, isLoading} = useApi("")
const { response, isLoading } = useApi("");
return (
<>
<div id="loginWrapper">
<div id="loginGrid">
<div id="iconCard">
<div id="imgWrap">
<img src="/logoIcon512.jpg" alt="CHI Logo"></img>
</div>
</div>
<InputItem placeholder="username"/>
<InputItem placeholder="password" type="password"/>
<div id="buttonFlex">
<Button>Login</Button>
<div id="iconCard">
<div id="imgWrap">
<img src="/logoIcon512.jpg" alt="CHI Logo"></img>
</div>
</div>
<InputItem placeholder="username" />
<InputItem placeholder="password" type="password" />
<div id="buttonFlex">
<Button>Login</Button>
</div>
</div>
</div>
<p>Loading API Fetch: {isLoading.toString()}</p>
{response &&
<p>API Fetch Result: {JSON.stringify(response)}</p>
}
{response && <p>API Fetch Result: {JSON.stringify(response)}</p>}
</>
)
);
}
+105
View File
@@ -0,0 +1,105 @@
import React from "react";
import { render, fireEvent, prettyDOM } from "@testing-library/react";
import PatientInfoForm from "./PatientInfoForm";
test("female-only div is only displayed if the sex is selected as female", async () => {
const femaleOnlyFormFields = ["Pregnant?", "LMP", "Wants Planning?"];
const dom = render(<PatientInfoForm />);
// ensure the female-only div is not displayed
femaleOnlyFormFields.forEach((fieldText) => {
expect(dom.queryByText(fieldText)).toBeNull();
});
// click the radio button for 'female'
const femaleButton = dom.getByTestId("female-button");
fireEvent.click(femaleButton);
femaleOnlyFormFields.forEach((fieldText) => {
expect(dom.queryByText(fieldText)).toBeTruthy();
});
});
test("sets visit input", () => {
const dom = render(<PatientInfoForm />);
const visitYesInput = dom.queryByTestId("visit-Yes");
const checkBox = visitYesInput.getElementsByTagName("span")[0];
expect(checkBox).not.toHaveClass("am-radio-checked");
fireEvent.click(visitYesInput);
expect(checkBox).toHaveClass("am-radio-checked");
});
test("sets pregnant input", () => {
const dom = render(<PatientInfoForm />);
const femaleButton = dom.getByTestId("female-button");
fireEvent.click(femaleButton);
const pregnantYesInput = dom.queryByTestId("pregnant-Yes");
const checkBox = pregnantYesInput.getElementsByTagName("span")[0];
expect(checkBox).not.toHaveClass("am-radio-checked");
fireEvent.click(pregnantYesInput);
expect(checkBox).toHaveClass("am-radio-checked");
});
test("sets planning input", () => {
const dom = render(<PatientInfoForm />);
const femaleButton = dom.getByTestId("female-button");
fireEvent.click(femaleButton);
const planningYesInput = dom.queryByTestId("planning-Yes");
const checkBox = planningYesInput.getElementsByTagName("span")[0];
expect(checkBox).not.toHaveClass("am-radio-checked");
fireEvent.click(planningYesInput);
expect(checkBox).toHaveClass("am-radio-checked");
});
test("sets albendazole input", () => {
const dom = render(<PatientInfoForm />);
const femaleButton = dom.getByTestId("female-button");
const albendazoleYesInput = dom.queryByTestId("albendazole-Yes");
const checkBox = albendazoleYesInput.getElementsByTagName("span")[0];
expect(checkBox).not.toHaveClass("am-radio-checked");
fireEvent.click(albendazoleYesInput);
expect(checkBox).toHaveClass("am-radio-checked");
});
test("renders the Patient Info Form fields", () => {
const formFields = [
"First Name",
"First Name",
"Last Name",
"Last Name",
"Sex",
"Birth",
"Age",
"Phone",
"Town",
"Visit",
"Nickname",
"BP",
"Temp",
"Pulse",
"Weight",
"Height",
"Z-Score",
"Albendazole",
"Alergies",
"Medicines",
"Chief Complaint",
"History",
"Exam",
];
const { getByText } = render(<PatientInfoForm />);
formFields.forEach((fieldText) => {
let re = new RegExp(fieldText, "gi");
expect(getByText(re)).toBeInTheDocument();
});
});
+264
View File
@@ -0,0 +1,264 @@
import React, { ReactElement, useState } from "react";
import {
DatePicker,
InputItem,
List,
TextareaItem,
Button,
Radio,
WhiteSpace,
} from "antd-mobile";
import enUs from "antd-mobile/lib/date-picker/locale/en_US";
import "../scss/PatientInfoForm.scss";
const RadioItem = Radio.RadioItem;
export default function Form(): ReactElement {
// State Hooks for Radio Buttons
// useState returns a value, and a function to set that value
const [sex, setSex] = useState(null as Number); // Default: null
const [albendazole, setAlbendazole] = useState(0); // Default: No
const [visit, setVisit] = useState(0); // Default: No
const [pregnant, setPregnant] = useState(0); // Default: No
const [planning, setPlanning] = useState(0); // Default: No
// Data for Radio Buttons
const sexValues = [
{ value: 0, label: "Male" },
{ value: 1, label: "Female" },
];
const radioYesNo = [
{ value: 0, label: "No" },
{ value: 1, label: "Yes" },
];
/* istanbul ignore next */
const onSubmit = () => {
// This shows how to access the values of the form
const firstNameField: HTMLInputElement = document.getElementById(
"firstname"
) as HTMLInputElement;
const lastNameField: HTMLInputElement = document.getElementById(
"lastname"
) as HTMLInputElement;
const nicknameField: HTMLInputElement = document.getElementById(
"nickname"
) as HTMLInputElement;
const birthdateField: HTMLInputElement = document.getElementById(
"birthdate"
) as HTMLInputElement;
const complaintField1: HTMLInputElement = document.getElementById(
"complaint1"
) as HTMLInputElement;
const complaintField2: HTMLInputElement = document.getElementById(
"complaint2"
) as HTMLInputElement;
const complaintField3: HTMLInputElement = document.getElementById(
"complaint3"
) as HTMLInputElement;
console.log("First name:", firstNameField.value);
console.log("Last name:", lastNameField.value);
console.log("Nickname:", nicknameField.value);
console.log("Birth Date:", birthdateField.value);
console.log(
"Complaints: ",
complaintField1.value +
" " +
complaintField2.value +
" " +
complaintField3.value
);
console.log("Wants Planning?: " + planning);
};
return (
<div id="patient-info-wrapper">
<TextareaItem
title="First Name"
placeholder="First Name"
autoHeight
id="firstname"
/>
<TextareaItem
title="Last Name"
placeholder="Last Name"
id="lastname"
autoHeight
/>
<TextareaItem
title="Nickname"
placeholder="Nickname"
id="nickname"
autoHeight
/>
<List renderHeader={() => "Sex"}>
{sexValues.map((i) => (
<RadioItem
data-testid={`${i.label.toLowerCase()}-button`}
id={`${i.label.toLowerCase()}-button`}
key={i.value}
name="sex"
checked={sex === i.value}
onClick={() => setSex(i.value)}
>
{i.label}
</RadioItem>
))}
</List>
<WhiteSpace />
<InputItem placeholder="YYYY-MM-DD" id="birthdate">
Birth Date
</InputItem>
<InputItem type="number" placeholder="Age" id="age">
Age{" "}
</InputItem>
<InputItem type="number" placeholder="Phone" id="phone">
Phone
</InputItem>
<TextareaItem title="Town" placeholder="Town" id="town" autoHeight />
<List renderHeader={() => "Visit?"}>
{radioYesNo.map((i) => (
<RadioItem
key={i.value}
data-testid={`visit-${i.label}`}
name="visit"
checked={visit === i.value}
onClick={() => setVisit(i.value)}
>
{i.label}
</RadioItem>
))}
</List>
{/* Only display the 'female-only' div if the patient is female */}
{sex ? (
<div id="female-only">
<WhiteSpace />
<List renderHeader={() => "Pregnant?"}>
{radioYesNo.map((i) => (
<RadioItem
data-testid={`pregnant-${i.label}`}
key={i.value}
name="pregnant"
checked={pregnant === i.value}
onClick={() => setPregnant(i.value)}
>
{i.label}
</RadioItem>
))}
</List>
<InputItem title="G" type="number" id="gravida">
G
</InputItem>
<InputItem title="P" type="number" id="para">
P
</InputItem>
<InputItem title="A" type="number" id="abortus">
A
</InputItem>
<TextareaItem title="LMP" id="lmp" />
<List renderHeader={() => "Wants Planning?"}>
{radioYesNo.map((i) => (
<RadioItem
data-testid={`planning-${i.label}`}
key={i.value}
name="planning"
checked={planning === i.value}
onClick={() => setPlanning(i.value)}
>
{i.label}
</RadioItem>
))}
</List>
</div>
) : null}
<WhiteSpace />
<InputItem type="number" placeholder="Weight" id="weight">
Weight (kg)
</InputItem>
<InputItem type="number" placeholder="Height" id="height">
Height (cm)
</InputItem>
<InputItem type="number" placeholder="Blood Pressure" id="bp">
BP
</InputItem>
<InputItem type="number" placeholder="Temp" id="temp">
Temp (F)
</InputItem>
<InputItem type="number" placeholder="Pulse" id="pulse">
Pulse
</InputItem>
<InputItem type="number" placeholder="Z-Score" id="z-score">
Z-Score
</InputItem>
<List renderHeader={() => "Albendazole?"}>
{radioYesNo.map((i) => (
<RadioItem
data-testid={`albendazole-${i.label}`}
key={i.value}
checked={albendazole === i.value}
onClick={() => setAlbendazole(i.value)}
>
{i.label}
</RadioItem>
))}
</List>
<List renderHeader={() => "Alergies"}>
<TextareaItem autoHeight id="alergies" />
</List>
<List renderHeader={() => "Medicines"}>
<TextareaItem autoHeight id="medicines" />
</List>
<List renderHeader={() => "Chief Complaint"}>
<TextareaItem
placeholder="1."
// autoHeight
autoHeight
id="complaint1"
/>
<TextareaItem placeholder="2." autoHeight id="complaint2" />
<TextareaItem placeholder="3." autoHeight id="complaint3" />
</List>
<List renderHeader={() => "History"}>
<TextareaItem autoHeight id="history" />
</List>
<List renderHeader={() => "Exam"}>
<TextareaItem autoHeight id="exam" />
</List>
<WhiteSpace />
<Button onClick={onSubmit}>Submit</Button>
</div>
);
}
+25 -13
View File
@@ -1,16 +1,28 @@
import { renderHook } from '@testing-library/react-hooks'
import useApi from './useApi'
import { renderHook } from "@testing-library/react-hooks";
import useApi from "./useApi";
test('should return loading and then a response', async () => {
const { result, waitForNextUpdate } = renderHook(() => useApi(""))
expect(result.current.response).toBeNull()
expect(result.current.error).toBeNull()
expect(result.current.isLoading).toBeTruthy()
test("should return loading and then a response", async () => {
const { result, waitForNextUpdate } = renderHook(() => useApi(""));
await waitForNextUpdate()
expect(result.current.response).toBeNull();
expect(result.current.error).toBeNull();
expect(result.current.isLoading).toBeTruthy();
expect(result.current.response).toBeTruthy()
expect(result.current.error).toBeNull()
expect(result.current.isLoading).toBeFalsy()
})
await waitForNextUpdate();
expect(result.current.response).toBeTruthy();
expect(result.current.error).toBeNull();
expect(result.current.isLoading).toBeFalsy();
});
test("should return an error on a non existent route", async () => {
const { result, waitForNextUpdate } = renderHook(() => useApi("/404"));
expect(result.current.response).toBeNull();
expect(result.current.error).toBeNull();
await waitForNextUpdate();
expect(result.current.response).toBeFalsy();
expect(result.current.error).toBeTruthy();
});
+10 -10
View File
@@ -1,19 +1,18 @@
import React from 'react'
import React from "react";
const useApi = (route: string) => {
const [response, setResponse] = React.useState(null);
const [error, setError] = React.useState(null);
const [isLoading, setIsLoading] = React.useState(false);
/* istanbul ignore next */
const getUrl = () => {
if (process.env.environment === "docker"){
return "http://api:4000/"
if (process.env.environment === "docker") {
return "http://api:4000/";
} else {
return "http://localhost:4000/";
}
else {
return "http://localhost:4000/"
}
}
};
React.useEffect(() => {
const fetchData = async () => {
@@ -22,14 +21,15 @@ const useApi = (route: string) => {
const res = await fetch(getUrl() + route);
const json = await res.json();
setResponse(json);
setIsLoading(false)
setIsLoading(false);
} catch (error) {
setError(error);
}
};
fetchData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return { response, error, isLoading };
};
export default useApi
export default useApi;
-2
View File
@@ -1,6 +1,4 @@
.button-flex {
animation-name: fade-in;
animation-duration: 2s;
display: flex;
justify-content: center;
margin: 2em 0;
+26 -18
View File
@@ -21,7 +21,7 @@
#imgWrap {
width: 28%;
margin: auto;
padding: .2rem;
padding: 0.2rem;
background-color: #eee;
border-radius: 5px;
img {
@@ -37,13 +37,13 @@
animation-name: fade-in;
animation-duration: 2s;
}
#buttonFlex{
#buttonFlex {
animation-name: fade-in;
animation-duration: 2s;
display: flex;
justify-content: center;
.am-button {
background-color: #9BD7FF;
background-color: #9bd7ff;
width: 40%;
&.am-button-active {
background-color: #ccebff;
@@ -51,21 +51,29 @@
}
}
}
}
$animationDelay: 1;
@for $i from 1 through 15 {
.loginGrid :nth-child(#{$i}) {
animation-delay: #{0.3+ ($i)/30}s;
}
}
$animationDelay: 1;
@for $i from 1 through 15 {
.loginGrid :nth-child(#{$i}) {
animation-delay: #{0.3+($i)/30}s;
@keyframes fade-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes fade-in-from-up {
0% {
opacity: 0;
transform: translateY(-100px);
}
100% {
opacity: 1;
}
}
}
@keyframes fade-in {
0% { opacity: 0; }
100% { opacity: 1; }
}
@keyframes fade-in-from-up {
0% { opacity: 0; transform: translateY(-100px)}
100% { opacity: 1; }
}
+8
View File
@@ -0,0 +1,8 @@
#patient-info-wrapper {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
}
+11
View File
@@ -0,0 +1,11 @@
#primary-wrapper {
width: 100%;
height: 100%;
display: flex;
justify-content: space-around;
#wrapper-contents {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
max-width: 50em;
max-height: 94.6vh;
}
}