diff --git a/pwa/package-lock.json b/pwa/package-lock.json index 2202d87..d085876 100644 --- a/pwa/package-lock.json +++ b/pwa/package-lock.json @@ -2198,9 +2198,9 @@ }, "dependencies": { "acorn": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz", - "integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==" + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==" } } }, @@ -7866,9 +7866,9 @@ }, "dependencies": { "acorn": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz", - "integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==" + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==" }, "jsdom": { "version": "14.1.0", @@ -8807,9 +8807,9 @@ }, "dependencies": { "acorn": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", - "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==" + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==" } } }, diff --git a/pwa/src/App.tsx b/pwa/src/App.tsx index 8ac8774..db766c4 100644 --- a/pwa/src/App.tsx +++ b/pwa/src/App.tsx @@ -8,6 +8,7 @@ import Login from './components/Login' import LandingPage from './components/LandingPage' import Wrapper from './Wrapper' import PatientInfoForm from './components/PatientInfoForm' +import DiagnosisForm from './components/DiagnosisForm' import './scss/App.scss' import './antd-mobile.css' @@ -33,6 +34,15 @@ const App: React.FunctionComponent = () => { + + + + + diff --git a/pwa/src/components/DiagnosisFields.json b/pwa/src/components/DiagnosisFields.json new file mode 100644 index 0000000..7c43993 --- /dev/null +++ b/pwa/src/components/DiagnosisFields.json @@ -0,0 +1,189 @@ +{ + "diagnoses": [ + { + "name": "Asthma", + "id": "asthma" + }, + { + "name": "Allergies", + "id": "allergies" + }, + { + "name": "Anemia", + "id": "anemia" + }, + { + "name": "Malnutrition", + "id": "malnutrition" + }, + { + "name": "Depression", + "id": "depression" + }, + { + "name": "Diabetes", + "id": "diabetes" + }, + { + "id": "eye", + "group-title": "Eye", + "group": [ + { + "name": "Ptergium", + "id": "ptergium" + }, + { + "name": "Cataract", + "id": "cataract" + }, + { + "name": "Dry Eye", + "id": "dry-eye" + }, + { + "name": "Conjuntivitis", + "id": "conjuntivitis" + } + ] + }, + { + "name": "Fever", + "id": "fever" + }, + { + "name": "Possible Malaria", + "id": "malaria" + }, + { + "name": "Filariasis", + "id": "filariasis" + }, + { + "id": "intestinal", + "group-title": "Intestinal", + "group": [ + { "name": "Gastritis", "id": "gastritis" }, + { "name": "GERD", "id": "gerd" }, + { "name": "Gastroenteritis", "id": "gastroenteritis" }, + { "name": "Dysentery", "id": "dysentery" }, + { "name": "Possible Cholera", "id": "cholera" } + ] + }, + { + "name": "Headache", + "id": "headache" + }, + { + "group-title": "Hernia", + "id": "hernia", + "group": [ + { "name": "Inguinal", "id": "inguinal" }, + { "name": "Umbilical", "id": "umbilical" } + ] + }, + { + "id": "cardiovascular", + "group-title": "Cardiovascular", + "group": [ + { "name": "Hypertension", "id": "hypertension" }, + { "name": "Stroke", "id": "stroke" }, + { "name": "Angina", "id": "angina" }, + { "name": "CHF", "id": "chf" } + ] + }, + { + "name": "Healthy", + "id": "healthy" + }, + + { + "name": "Musculoskeletal Pain", + "id": "musculoskeletal" + }, + { + "group-title": "Otitis", + "id": "otitis", + "group": [ + { "name": "Right", "id": "otitis-right" }, + { "name": "Left", "id": "otitis-left" } + ] + }, + { + "name": "Pneumonia", + "id": "pneumonia" + }, + { + "id": "sinusitis", + "name": "Sinusitis" + }, + { + "name": "URI", + "id": "uri" + }, + { + "name": "Pregnancy", + "id": "pregnancy", + "text-input-title": "Weeks", + "text-input-type": "number", + "max-length": 2, + "text-input-id": "pregnancy-weeks" + }, + { + "group-title": "Dermatological", + "id": "dermatological", + "group": [ + { "name": "Atopic Derm", "id": "atopic-derm" }, + { "name": "Fungal", "id": "fungal" }, + { "name": "Scabies", "id": "scabies" }, + { "name": "Impetigo", "id": "Impetigo" }, + { "name": "Tinea Capitus (kerion)", "id": "tinea-capitus" } + ] + }, + { + "name": "Possible STD", + "id": "std" + }, + { + "name": "Possible TB", + "id": "tb" + }, + { + "name": "Possible Parasites", + "id": "parasites" + }, + { + "name": "UTI", + "id": "uti" + }, + { + "group-title": "Vaginal", + "id": "vaginal", + "group": [ + { "name": "Vaginitis", "id": "vaginitis" }, + { "name": "Candidal", "id": "candidal" }, + { "name": "Trich", "id": "trich" }, + { "name": "BV", "id": "bv" } + ] + }, + { + "name": "Needs to see Dentist", + "id": "dentist" + }, + { + "name": "Needs to see Eye Doctor", + "id": "eye-doctor" + }, + { + "name": "Surgical candidate", + "id": "surgical-candidate" + }, + { + "text-input-title": "Other Diagnosis", + "text-input-id": "other-diagnosis" + }, + { + "text-input-title": "Diagnoses Comments", + "text-input-id": "comments" + } + ] +} diff --git a/pwa/src/components/DiagnosisForm.test.tsx b/pwa/src/components/DiagnosisForm.test.tsx new file mode 100644 index 0000000..767f94a --- /dev/null +++ b/pwa/src/components/DiagnosisForm.test.tsx @@ -0,0 +1,135 @@ +import React from 'react' +import { render, fireEvent } from '@testing-library/react' +import DiagnosisForm from './DiagnosisForm' +import * as data from './DiagnosisFields.json' + +/* + +Diagnosis Fields are stored in DiagnosisFields.json in the format: + +{ + "diagnoses" : [ + { + "name": "Name", + "id": "id", + "text-input-title": "Title", + "text-input-type": "type" (i.e. number) + "group-title": "Group Title", + "group": [ + { "name": "Name1", "id": "id1" }, + { "name": "Name2", "id": "id2" } + ] + }, + {...} + ] +} +*/ + +const diagnosisFields = data.diagnoses + +test('renders the form', async () => { + const dom = render() + expect(dom.getByTestId('diagnosis-form-wrapper')).toBeTruthy() +}) + +test('renders each diagnoses.name data field', () => { + const { getByText } = render() + + diagnosisFields.forEach((diagnosis) => { + if (diagnosis.name != null) { + const re = new RegExp(diagnosis.name, 'gi') + const field = getByText(re) + expect(field).toBeInTheDocument() + } + }) +}) + +test(' each diagnoses.name has a corresponding checkbox and is able to be checked/unchecked', () => { + const { getByTestId } = render() + diagnosisFields.forEach((diagnosis) => { + if (diagnosis.name != null && diagnosis.id != null) { + const re = new RegExp(diagnosis.id, 'gi') + const checkboxItem = getByTestId(re) + expect(checkboxItem).toHaveClass('am-checkbox-item') + + const checkBox = checkboxItem.getElementsByTagName('span')[0] + expect(checkBox).not.toHaveClass('am-checkbox-checked') + const checkBoxInput = checkboxItem.getElementsByClassName( + 'am-checkbox-input', + )[0] + fireEvent.click(checkBoxInput) + expect(checkBox).toHaveClass('am-checkbox-checked') + } + }) +}) + +test('renders each diagnoses.text-input-title data field as a text entry field', () => { + const { getByTestId } = render() + + diagnosisFields.forEach((diagnosis) => { + if ( + diagnosis['text-input-title'] != null && + diagnosis['text-input-title'] != null + ) { + const re = new RegExp(diagnosis['text-input-id'], 'gi') + const field = getByTestId(re) + expect(field).toBeInTheDocument() + expect(field).toHaveAttribute('type', 'text') + } + }) +}) + +test('renders each diagnoses.group-title data field as a header', () => { + const { getByText } = render() + diagnosisFields.forEach((diagnosis) => { + if (diagnosis['group-title'] != null && diagnosis.group != null) { + const re = new RegExp(`\^${diagnosis['group-title']}\$`, 'gi') + const field = getByText(re) + expect(field).toBeInTheDocument() + expect(field).toHaveClass('am-list-header') + } + }) +}) + +test('renders each diagnoses.group entry in the same list element', () => { + const { getByTestId } = render() + diagnosisFields.forEach((diagnosis) => { + if (diagnosis.group != null && diagnosis.id != null) { + // get the list by diagnosis.id + const re = new RegExp(`\^${diagnosis.id}\$`, 'gi') + const field = getByTestId(re) + expect(field).toBeInTheDocument() + expect(field).toHaveClass('am-list') + + // For each element in the group, ensure it is contained in the list + diagnosis.group.forEach((groupElement) => { + const el = getByTestId(groupElement.id) + expect(field).toContainElement(el) + }) + } + }) +}) + +test('renders each diagnoses.[el] as a checkbox that can be checked/unchecked', () => { + const { getByTestId } = render() + diagnosisFields.forEach((diagnosis) => { + if (diagnosis.group != null) { + diagnosis.group.forEach((groupElement) => { + const re = new RegExp(groupElement.id, 'gi') + const groupCheckboxItem = getByTestId(re) + expect(groupCheckboxItem).toBeInTheDocument() + expect(groupCheckboxItem).toHaveClass('am-checkbox-item') + + const checkBox = groupCheckboxItem.getElementsByTagName( + 'span', + )[0] + expect(checkBox).not.toHaveClass('am-checkbox-checked') + const checkBoxInput = groupCheckboxItem.getElementsByClassName( + 'am-checkbox-input', + )[0] + fireEvent.click(checkBoxInput) + expect(checkBox).toHaveClass('am-checkbox-checked') + }) + } + }) +}) diff --git a/pwa/src/components/DiagnosisForm.tsx b/pwa/src/components/DiagnosisForm.tsx new file mode 100644 index 0000000..a242d96 --- /dev/null +++ b/pwa/src/components/DiagnosisForm.tsx @@ -0,0 +1,117 @@ +import React, { ReactElement } from 'react' +import { List, Checkbox, InputItem, WhiteSpace } from 'antd-mobile' +import '../scss/DiagnosisForm.scss' +import * as data from './DiagnosisFields.json' + +/* + +Diagnosis Fields are stored in DiagnosisFields.json in the format: + +{ + "diagnoses" : [ + { + "name": "Name", + "id": "id", + "text-input-title": "Title", + "text-input-id": "id" for `id` and `data-testid` attributes + "text-input-type": "type" (i.e. number) + "max-length": 3 + "group-title": "Group Title", + "group": [ + { "name": "Name1", "id": "id1" }, + { "name": "Name2", "id": "id2" } + ] + }, + {...} + ] +} +*/ + +const CheckboxItem = Checkbox.CheckboxItem +const diagnosisFields = data.diagnoses + +export default function DiagnosisForm(): ReactElement { + return ( +
+ {diagnosisFields.map((field) => { + const formElements: ReactElement[] = [] + + if (field.name != null) { + formElements.push( + + + {field.name} + + , + ) + } + + if ( + field['text-input-title'] != null && + field['text-input-id'] != null + ) { + formElements.push( + field['text-input-title']}> + + , + ) + } + + if (field.group != null) { + const groupElements: ReactElement[] = [] + + // add each group element to the internalElement array + field.group.forEach((ele: any) => { + if (ele.id != null && ele.name != null) { + groupElements.push( + + {ele.name} + , + ) + } + }) + + // Render the entire list with the internal elements + if (field['group-title'] != null && field.id !== null) { + formElements.push( + field['group-title']} + data-testid={field.id} + > + {groupElements} + + , + ) + } + } + + return <> {formElements} + })} +
+ ) +} diff --git a/pwa/src/scss/DiagnosisForm.scss b/pwa/src/scss/DiagnosisForm.scss new file mode 100644 index 0000000..3ee4ed2 --- /dev/null +++ b/pwa/src/scss/DiagnosisForm.scss @@ -0,0 +1,5 @@ +#diagnosis-form-wrapper { + .list-whitespace { + background-color: #f5f5f9; + } +}