Skip to content

Commit

Permalink
[#272] Make numeric field widths consistent
Browse files Browse the repository at this point in the history
* Add BloodPressureField component to handle BP entry

Apply custom styles to BloodPressureField so the fields and error messages lay out correctly.
Simplify range validation error message.
Revert the FormInput changes, and remove the valueAsNumber prop, which isn't settable.
Revert changes in _uswds-theme-custom-styles.scss.

* Show an error state on the blood pressure label for range errors

Fix the layout of the O2 field and range error.

---------

Co-authored-by: Francis Li <francisli@users.noreply.github.com>
  • Loading branch information
fwextensions and francisli authored Feb 27, 2023
1 parent ba8ec63 commit 40c5814
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 68 deletions.
64 changes: 29 additions & 35 deletions client/src/Components/FormInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,47 +46,31 @@ function FormInput({

let input = (
<>
<div className="usa-input__wrapper">
<input
id={property}
disabled={disabled}
value={value || ''}
onKeyDown={handleKeyDown}
onBlur={handleOnBlur}
onChange={handleOnChange}
onFocus={() => setFocused(true)}
required={required}
type={type}
min={min}
max={max}
className={classNames('usa-input', {
'usa-input--error': hasError || error?.errorsFor(property),
'usa-input--medium': size === 'medium',
'usa-input--small': size === 'small',
})}
/>
{error?.errorsFor(property) && (
<div className="usa-error-message usa-error-message--static">
<i className="fas fa-exclamation-circle" />{' '}
{error
.errorsFor(property)
.map((e) => e.message)
.join(' ')}
</div>
)}
{!focused && <ValidationMessage validationState={validationState} min={min} max={max} />}
</div>
<input
id={property}
disabled={disabled}
value={value || ''}
onKeyDown={handleKeyDown}
onBlur={handleOnBlur}
onChange={handleOnChange}
onFocus={() => setFocused(true)}
required={required}
type={type}
min={min}
max={max}
className={classNames('usa-input', {
'usa-input--error': hasError || error?.errorsFor(property),
'usa-input--medium': size === 'medium',
'usa-input--small': size === 'small',
})}
/>
{unit && <span className="usa-hint usa-hint--unit">&nbsp;&nbsp;{unit}</span>}
{children}
</>
);

if (isWrapped) {
input = (
<section className="usa-input__group">
<div className="grid-row flex-align-start">{input}</div>
</section>
);
input = <div className="grid-row flex-align-center">{input}</div>;
}

return (
Expand All @@ -104,6 +88,16 @@ function FormInput({
</label>
)}
{input}
{error?.errorsFor(property) && (
<div className="usa-error-message usa-error-message--static">
<i className="fas fa-exclamation-circle" />{' '}
{error
.errorsFor(property)
.map((e) => e.message)
.join(' ')}
</div>
)}
{!focused && <ValidationMessage validationState={validationState} label={label} min={min} max={max} />}
</>
);
}
Expand Down
2 changes: 1 addition & 1 deletion client/src/Components/ValidationMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import classNames from 'classnames';
import { ValidationState } from '../Models/PatientFieldData';

function ValidationMessage({ className, validationState, min, max }) {
const errorMessage = validationState === ValidationState.RANGE_ERROR ? `Valid Range: ${min} - ${max}` : `This is a required field`;
const errorMessage = validationState === ValidationState.RANGE_ERROR ? `Range: ${min} - ${max}` : `This is a required field`;
const errorHtml = (
<div className={classNames('usa-error-message', className)}>
<i className="fas fa-exclamation-circle" /> {errorMessage}
Expand Down
57 changes: 57 additions & 0 deletions client/src/EMS/BloodPressureField.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { ValidationState } from '../Models/PatientFieldData';
import FormInput from '../Components/FormInput';
import { useForm } from '../Components/Form';

import './BloodPressureField.scss';

function BPInput({ metadata, unit }) {
const { data, onChange } = useForm();
const { name, range = {} } = metadata;
const { min, max } = range;

return (
// we need to add a wrapper around the FormInput component because it outputs
// multiple children in a fragment. we need each input's error message to be
// grouped with its input in a div because the fields are arranged horizontally
// in a row. since the errors are absolutely positioned, they'd both shift to
// the left margin of the .bpfield without this extra div, causing an overlap.
<div className="bpfield__input">
<FormInput
type="number"
property={name}
value={data[name]}
validationState={data.getValidationState(name)}
unit={unit || metadata.unit}
min={min}
max={max}
onChange={onChange}
/>
</div>
);
}

export default function BloodPressureField({ systolicMetadata, diastolicMetadata }) {
const { data } = useForm();
const validations = [data.getValidationState(systolicMetadata.name), data.getValidationState(diastolicMetadata.name)];
const hasError = validations.includes(ValidationState.RANGE_ERROR);

return (
<>
<label htmlFor={systolicMetadata.name} className={classNames('usa-label', { 'usa-label--error': hasError })}>
Blood pressure
</label>
<div className="bpfield">
<BPInput metadata={systolicMetadata} unit="/" />
<BPInput metadata={diastolicMetadata} unit="mmHG" />
</div>
</>
);
}

BloodPressureField.propTypes = {
systolicMetadata: PropTypes.object.isRequired,
diastolicMetadata: PropTypes.object.isRequired,
};
22 changes: 22 additions & 0 deletions client/src/EMS/BloodPressureField.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@import '../theme/base';

.bpfield {
display: flex;
gap: 0.6rem;
position: relative;
}

.bpfield__input {
.grid-row {
flex-wrap: nowrap;
}

.usa-input {
width: 13ex;
}

.usa-error-message {
top: 3rem;
max-width: 9rem;
}
}
21 changes: 7 additions & 14 deletions client/src/EMS/PatientFields.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import FormRadio from '../Components/FormRadio';
import FormRadioFieldSet from '../Components/FormRadioFieldSet';
import FormField from '../Components/FormField';
import Heading from '../Components/Heading';
import BloodPressureField from './BloodPressureField';

import Ringdown from '../Models/Ringdown';
import ApiService from '../ApiService';
Expand Down Expand Up @@ -122,18 +123,10 @@ function PatientFields({ ringdown, onChange }) {
<Heading title="Vitals" subtitle="optional" />
<div className="usa-accordion__content">
<fieldset className="usa-fieldset">
<FormField
metadata={Patient.systolicBloodPressure}
label="Blood Pressure"
unit="/"
>
<span className="usa-hint usa-hint--unit">&nbsp;&nbsp;</span>
<FormField
metadata={Patient.diastolicBloodPressure}
unit="mmHG"
isWrapped={false}
/>
</FormField>
<BloodPressureField
systolicMetadata={Patient.systolicBloodPressure}
diastolicMetadata={Patient.diastolicBloodPressure}
/>
<FormField metadata={Patient.heartRateBpm} />
<FormField metadata={Patient.respiratoryRate} />
<FormField metadata={Patient.oxygenSaturation} />
Expand All @@ -146,8 +139,8 @@ function PatientFields({ ringdown, onChange }) {
/>
<FormRadio
label={
<div className="display-flex flex-row flex-align-start position-relative" style={{ top: '-.8rem' }}>
<div className="display-inline-block margin-right-2 radio-field__text">
<div className="display-flex flex-row flex-align-center position-relative" style={{ top: '-.8rem' }}>
<div className="display-inline-block margin-right-2">
O<sub>2</sub>
</div>
<FormField
Expand Down
29 changes: 11 additions & 18 deletions client/src/theme/_uswds-theme-custom-styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,13 @@ textarea + .usa-radio > .usa-radio__label {
position: static;
}

// if an error message appears inside a radio button label, it will be positioned to the right of the label
// instead of below, so shift the error so it aligns better
.usa-radio__label .usa-error-message {
margin-top: 0.5rem;
margin-left: 1rem;
}

.usa-fieldset {
padding: 1.5rem;
}
Expand Down Expand Up @@ -271,14 +278,6 @@ textarea + .usa-radio > .usa-radio__label {
font-size: 1.25rem;
}

.usa-hint--unit,
.radio-field__text {
height: 2.5rem;
margin-top: 0.5rem;
display: flex;
align-items: center;
}

.usa-combo-box__input,
.usa-input,
.usa-select,
Expand All @@ -298,16 +297,10 @@ textarea + .usa-radio > .usa-radio__label {
}
}

.usa-input__wrapper {
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
min-width: 3rem;
max-width: 7rem;
& .usa-error-message {
position: relative;
}
.usa-form .usa-input-small {
// this is the default max-width of .usa-input-small, but we want give an explicit widths to
// inputs so they don't get squished when other things are in the same container
width: 13ex;
}

.usa-label {
Expand Down

0 comments on commit 40c5814

Please sign in to comment.