Skip to content

Commit

Permalink
(fix) Flag required fields upon form submission
Browse files Browse the repository at this point in the history
  • Loading branch information
denniskigen committed Apr 24, 2024
1 parent 3fa8c75 commit 1cb92b8
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 156 deletions.
90 changes: 47 additions & 43 deletions src/components/inputs/date/ohri-date.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import dayjs from 'dayjs';
import classNames from 'classnames';
import { useTranslation } from 'react-i18next';
import { useField } from 'formik';
import { DatePicker, DatePickerInput, TimePicker } from '@carbon/react';
import { DatePicker, DatePickerInput, Layer, TimePicker } from '@carbon/react';
import { formatDate } from '@openmrs/esm-framework';
import { fieldRequiredErrCode, isEmpty } from '../../../validators/ohri-form-validator';
import { isInlineView } from '../../../utils/ohri-form-helper';
import { isTrue } from '../../../utils/boolean-utils';
import { OHRIFormFieldProps } from '../../../api/types';
import { OHRIFormContext } from '../../../ohri-form-context';
import { OHRIFieldValueView } from '../../value/view/ohri-field-value-view.component';
import styles from './ohri-date.scss';
import { formatDate } from '@openmrs/esm-framework';

const locale = window.i18next.language == 'en' ? 'en-GB' : window.i18next.language;
const dateFormatter = new Intl.DateTimeFormat(locale);
Expand Down Expand Up @@ -152,50 +152,54 @@ const OHRIDate: React.FC<OHRIFormFieldProps> = ({ question, onChange, handler, p
<>
<div className={styles.datetime}>
<div>
<DatePicker
datePickerType="single"
onChange={onDateChange}
className={classNames(styles.boldedLabel, { [styles.errorLabel]: isFieldRequiredError })}
dateFormat={carbonDateFormat}>
<DatePickerInput
id={question.id}
placeholder={placeholder}
labelText={question.label}
value={field.value instanceof Date ? field.value.toLocaleDateString(locale) : field.value}
// Added for testing purposes.
// Notes:
// Something strange is happening with the way events are propagated and handled by Carbon.
// When we manually trigger an onchange event using the 'fireEvent' lib, the handler below will
// be triggered as opposed to the former handler that only gets triggered at runtime.
onChange={(e) => onDateChange([dayjs(e.target.value, placeholder.toUpperCase()).toDate()])}
disabled={question.disabled}
invalid={!isFieldRequiredError && errors.length > 0}
invalidText={errors[0]?.message}
warn={warnings.length > 0}
warnText={warnings[0]?.message}
readOnly={question.readonly}
/>
</DatePicker>
<Layer>
<DatePicker
datePickerType="single"
onChange={onDateChange}
className={classNames(styles.boldedLabel, { [styles.errorLabel]: isFieldRequiredError })}
dateFormat={carbonDateFormat}>
<DatePickerInput
id={question.id}
placeholder={placeholder}
labelText={question.label}
value={field.value instanceof Date ? field.value.toLocaleDateString(locale) : field.value}
// Added for testing purposes.
// Notes:
// Something strange is happening with the way events are propagated and handled by Carbon.
// When we manually trigger an onchange event using the 'fireEvent' lib, the handler below will
// be triggered as opposed to the former handler that only gets triggered at runtime.
onChange={(e) => onDateChange([dayjs(e.target.value, placeholder.toUpperCase()).toDate()])}
disabled={question.disabled}
invalid={!isFieldRequiredError && errors.length > 0}
invalidText={errors[0]?.message}
warn={warnings.length > 0}
warnText={warnings[0]?.message}
readOnly={question.readonly}
/>
</DatePicker>
</Layer>
</div>
{question?.questionOptions.rendering === 'datetime' ? (
<div className={styles.timePickerSpacing}>
<TimePicker
className={styles.boldedLabel}
id={question.id}
labelText={t('time', 'Time')}
placeholder="HH:MM"
pattern="(1[012]|[1-9]):[0-5][0-9])$"
type="time"
disabled={!field.value ? true : false}
value={
time
? time
: field.value instanceof Date
? field.value.toLocaleDateString(window.navigator.language)
: field.value
}
onChange={onTimeChange}
/>{' '}
<Layer>
<TimePicker
className={styles.boldedLabel}
id={question.id}
labelText={t('time', 'Time')}
placeholder="HH:MM"
pattern="(1[012]|[1-9]):[0-5][0-9])$"
type="time"
disabled={!field.value ? true : false}
value={
time
? time
: field.value instanceof Date
? field.value.toLocaleDateString(window.navigator.language)
: field.value
}
onChange={onTimeChange}
/>
</Layer>
</div>
) : (
''
Expand Down
38 changes: 20 additions & 18 deletions src/components/inputs/multi-select/ohri-multi-select.component.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useEffect, useMemo, useState } from 'react';
import { FilterableMultiSelect, UnorderedList } from '@carbon/react';
import { FilterableMultiSelect, Layer, UnorderedList } from '@carbon/react';
import classNames from 'classnames';
import { useField } from 'formik';
import { useTranslation } from 'react-i18next';
Expand Down Expand Up @@ -94,23 +94,25 @@ export const OHRIMultiSelect: React.FC<OHRIFormFieldProps> = ({ question, onChan
!question.isHidden && (
<>
<div className={classNames(styles.boldedLabel, { [styles.errorLabel]: isFieldRequiredError })}>
<FilterableMultiSelect
placeholder={t('search', 'Search') + '...'}
onChange={handleSelectItemsChange}
id={question.label}
items={questionItems}
initialSelectedItems={initiallySelectedQuestionItems}
label={''}
titleText={question.label}
key={counter}
itemToString={(item) => (item ? item.label : ' ')}
disabled={question.disabled}
invalid={!isFieldRequiredError && errors.length > 0}
invalidText={errors[0]?.message}
warn={warnings.length > 0}
warnText={warnings[0]?.message}
readOnly={question.readonly}
/>
<Layer>
<FilterableMultiSelect
placeholder={t('search', 'Search') + '...'}
onChange={handleSelectItemsChange}
id={question.label}
items={questionItems}
initialSelectedItems={initiallySelectedQuestionItems}
label={''}
titleText={question.label}
key={counter}
itemToString={(item) => (item ? item.label : ' ')}
disabled={question.disabled}
invalid={!isFieldRequiredError && errors.length > 0}
invalidText={errors[0]?.message}
warn={warnings.length > 0}
warnText={warnings[0]?.message}
readOnly={question.readonly}
/>
</Layer>
</div>
<div className={styles.selectionDisplay}>
{field.value?.length ? (
Expand Down
6 changes: 3 additions & 3 deletions src/components/inputs/number/ohri-number.component.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useEffect, useMemo, useState } from 'react';
import { NumberInput } from '@carbon/react';
import { Layer, NumberInput } from '@carbon/react';
import classNames from 'classnames';
import { useField } from 'formik';
import { isTrue } from '../../../utils/boolean-utils';
Expand Down Expand Up @@ -65,7 +65,7 @@ const OHRINumber: React.FC<OHRIFormFieldProps> = ({ question, onChange, handler,
/>
</div>
) : (
<div>
<Layer>
<NumberInput
{...field}
id={question.id}
Expand All @@ -90,7 +90,7 @@ const OHRINumber: React.FC<OHRIFormFieldProps> = ({ question, onChange, handler,
warnText={warnings[0]?.message}
step={0.01}
/>
</div>
</Layer>
);
};

Expand Down
38 changes: 20 additions & 18 deletions src/components/inputs/select/ohri-dropdown.component.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useEffect, useMemo, useState } from 'react';
import classNames from 'classnames';
import { useTranslation } from 'react-i18next';
import { Dropdown } from '@carbon/react';
import { Dropdown, Layer } from '@carbon/react';
import { useField } from 'formik';
import { fieldRequiredErrCode, isEmpty } from '../../../validators/ohri-form-validator';
import { isInlineView } from '../../../utils/ohri-form-helper';
Expand Down Expand Up @@ -71,23 +71,25 @@ const OHRIDropdown: React.FC<OHRIFormFieldProps> = ({ question, onChange, handle
) : (
!question.isHidden && (
<div className={classNames(styles.boldedLabel, { [styles.errorLabel]: isFieldRequiredError })}>
<Dropdown
id={question.id}
titleText={question.label}
label={t('chooseAnOption', 'Choose an option')}
items={question.questionOptions.answers
.filter((answer) => !answer.isHidden)
.map((item) => item.value || item.concept)}
itemToString={itemToString}
selectedItem={field.value}
onChange={({ selectedItem }) => handleChange(selectedItem)}
disabled={question.disabled}
readOnly={question.readonly}
invalid={!isFieldRequiredError && errors.length > 0}
invalidText={errors.length && errors[0].message}
warn={warnings.length > 0}
warnText={warnings.length ? warnings[0].message : ''}
/>
<Layer>
<Dropdown
id={question.id}
titleText={question.label}
label={t('chooseAnOption', 'Choose an option')}
items={question.questionOptions.answers
.filter((answer) => !answer.isHidden)
.map((item) => item.value || item.concept)}
itemToString={itemToString}
selectedItem={field.value}
onChange={({ selectedItem }) => handleChange(selectedItem)}
disabled={question.disabled}
readOnly={question.readonly}
invalid={isFieldRequiredError && errors.length > 0}
invalidText={errors.length && errors[0].message}
warn={warnings.length > 0}
warnText={warnings.length ? warnings[0].message : ''}
/>
</Layer>
</div>
)
);
Expand Down
34 changes: 18 additions & 16 deletions src/components/inputs/text-area/ohri-text-area.component.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useEffect, useMemo, useState } from 'react';
import classNames from 'classnames';
import { TextArea } from '@carbon/react';
import { Layer, TextArea } from '@carbon/react';
import { useField } from 'formik';
import { fieldRequiredErrCode, isEmpty } from '../../../validators/ohri-form-validator';
import { isInlineView } from '../../../utils/ohri-form-helper';
Expand Down Expand Up @@ -69,21 +69,23 @@ const OHRITextArea: React.FC<OHRIFormFieldProps> = ({
[styles.errorLabel]: isFieldRequiredError,
[styles.boldedLabel]: !isFieldRequiredError,
})}>
<TextArea
{...field}
id={question.id}
labelText={question.label}
name={question.id}
value={field.value || ''}
onFocus={() => setPreviousValue(field.value)}
rows={question.questionOptions.rows || 4}
disabled={question.disabled}
readOnly={question.readonly}
invalid={!isFieldRequiredError && errors.length > 0}
invalidText={errors.length && errors[0].message}
warn={warnings.length > 0}
warnText={warnings.length && warnings[0].message}
/>
<Layer>
<TextArea
{...field}
id={question.id}
labelText={question.label}
name={question.id}
value={field.value || ''}
onFocus={() => setPreviousValue(field.value)}
rows={question.questionOptions.rows || 4}
disabled={question.disabled}
readOnly={question.readonly}
invalid={!isFieldRequiredError && errors.length > 0}
invalidText={errors.length && errors[0].message}
warn={warnings.length > 0}
warnText={warnings.length && warnings[0].message}
/>
</Layer>
</div>
)
);
Expand Down
34 changes: 18 additions & 16 deletions src/components/inputs/text/ohri-text.component.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useEffect, useMemo, useState } from 'react';
import classNames from 'classnames';
import isEmpty from 'lodash-es/isEmpty';
import { TextInput } from '@carbon/react';
import { Layer, TextInput } from '@carbon/react';
import { useField } from 'formik';
import { OHRIFormFieldProps } from '../../../api/types';
import { OHRIFormContext } from '../../../ohri-form-context';
Expand Down Expand Up @@ -68,21 +68,23 @@ const OHRIText: React.FC<OHRIFormFieldProps> = ({ question, onChange, handler, p
!question.isHidden && (
<>
<div className={classNames(styles.boldedLabel, { [styles.errorLabel]: isFieldRequiredError })}>
<TextInput
{...field}
id={question.id}
labelText={question.label}
name={question.id}
value={field.value || ''}
disabled={question.disabled}
readOnly={Boolean(question.readonly)}
invalid={!isFieldRequiredError && errors.length > 0}
invalidText={errors.length && errors[0].message}
warn={warnings.length > 0}
warnText={warnings.length && warnings[0].message}
onInvalid={(e) => e.preventDefault()}
maxLength={question.questionOptions.max || TextInput.maxLength}
/>
<Layer>
<TextInput
{...field}
id={question.id}
labelText={question.label}
name={question.id}
value={field.value || ''}
disabled={question.disabled}
readOnly={Boolean(question.readonly)}
invalid={!isFieldRequiredError && errors.length > 0}
invalidText={errors.length && errors[0].message}
warn={warnings.length > 0}
warnText={warnings.length && warnings[0].message}
onInvalid={(e) => e.preventDefault()}
maxLength={question.questionOptions.max || TextInput.maxLength}
/>
</Layer>
</div>
</>
)
Expand Down
Loading

0 comments on commit 1cb92b8

Please sign in to comment.