Skip to content

Commit

Permalink
(fix): Update props of OpenmrsDatePicker (#330)
Browse files Browse the repository at this point in the history
Co-authored-by: Dennis Kigen <kigen.work@gmail.com>
Co-authored-by: samuelmale <samuelsmalek@gmail.com>
Co-authored-by: Brandon Istenes <bistenes@gmail.com>
  • Loading branch information
4 people authored Jun 25, 2024
1 parent 4ac3a45 commit c7620fe
Show file tree
Hide file tree
Showing 5 changed files with 1,442 additions and 778 deletions.
60 changes: 9 additions & 51 deletions src/components/inputs/date/date.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,12 @@ import styles from './date.scss';
import { useFieldValidationResults } from '../../../hooks/useFieldValidationResults';
import { OpenmrsDatePicker, formatDate, formatTime } from '@openmrs/esm-framework';

const locale = window.i18next.language == 'en' ? 'en-GB' : window.i18next.language;
const dateFormatter = new Intl.DateTimeFormat(locale);

const DateField: React.FC<FormFieldProps> = ({ question, onChange, handler, previousValue }) => {
const { t } = useTranslation();
const [field, meta] = useField(question.id);
const [field] = useField(question.id);
const { setFieldValue, encounterContext, layoutType, workspaceLayout, fields } = React.useContext(FormContext);
const [time, setTime] = useState('');
const { errors, warnings, setErrors, setWarnings } = useFieldValidationResults(question);
const datePickerType = 'single';
const { errors, setErrors, warnings, setWarnings } = useFieldValidationResults(question);

const isInline = useMemo(() => {
if (['view', 'embedded-view'].includes(encounterContext.sessionMode) || isTrue(question.readonly)) {
Expand Down Expand Up @@ -75,39 +71,6 @@ const DateField: React.FC<FormFieldProps> = ({ question, onChange, handler, prev
}
};

const { placeholder, carbonDateFormat } = useMemo(() => {
const formatObj = dateFormatter.formatToParts(new Date());
const placeholder = formatObj
.map((obj) => {
switch (obj.type) {
case 'day':
return 'dd';
case 'month':
return 'mm';
case 'year':
return 'yyyy';
default:
return obj.value;
}
})
.join('');
const carbonDateFormat = formatObj
.map((obj) => {
switch (obj.type) {
case 'day':
return 'd';
case 'month':
return 'm';
case 'year':
return 'Y';
default:
return obj.value;
}
})
.join('');
return { placeholder: placeholder, carbonDateFormat: carbonDateFormat };
}, []);

useEffect(() => {
if (!time && field.value) {
if (field.value instanceof Date) {
Expand All @@ -134,7 +97,6 @@ const DateField: React.FC<FormFieldProps> = ({ question, onChange, handler, prev
<Layer>
<OpenmrsDatePicker
id={question.id}
dateFormat={carbonDateFormat}
onChange={(date) => onDateChange([date])}
labelText={
question.isRequired ? (
Expand All @@ -143,20 +105,16 @@ const DateField: React.FC<FormFieldProps> = ({ question, onChange, handler, prev
<span>{t(question.label)}</span>
)
}
invalid={errors.length > 0}
invalidText={errors[0]?.message}
isDisabled={question.isDisabled}
isReadOnly={isTrue(question.readonly)}
isRequired={question.isRequired ?? false}
isInvalid={errors.length > 0}
value={field.value}
disabled={question.isDisabled}
readonly={isTrue(question.readonly)}
carbonOptions={{
placeholder: placeholder,
warn: warnings[0]?.message,
warnText: warnings[0]?.message,
className: styles.boldedLabel,
datePickerType: datePickerType,
}}
className={styles.datePickerLabel}
/>
</Layer>
{errors.length > 0 ? <div className={styles.datePickerError}>{errors[0]?.message}</div> : null}
{warnings.length > 0 ? <div className={styles.datePickerWarn}>{warnings[0]?.message}</div> : null}
</div>
)}

Expand Down
17 changes: 17 additions & 0 deletions src/components/inputs/date/date.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
@use '@carbon/colors';

.datePickerError {
font-size: 0.75rem;
color: colors.$red-60;
}

.datePickerWarn {
font-size: 0.75rem;
color: colors.$black-100;
}

.datePickerLabel {
span {
font-weight: 600;
color: colors.$black-100;
}
}

.boldedLabel label {
font-weight: 600;
color: colors.$black-100;
Expand Down
33 changes: 26 additions & 7 deletions src/components/inputs/unspecified/unspecified.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import dayjs from 'dayjs';
import { fireEvent, render, screen } from '@testing-library/react';
import { Formik } from 'formik';
import { type FormField, type EncounterContext, FormContext } from '../../..';
Expand All @@ -7,6 +8,25 @@ import DateField from '../date/date.component';
import UnspecifiedField from './unspecified.component';
import { ObsSubmissionHandler } from '../../../submission-handlers/obsHandler';

jest.mock('@openmrs/esm-framework', () => {
const originalModule = jest.requireActual('@openmrs/esm-framework');
return {
...originalModule,
OpenmrsDatePicker: jest.fn().mockImplementation(({ id, labelText, value, onChange }) => {
return (
<>
<label htmlFor={id}>{labelText}</label>
<input
id={id}
value={value ? dayjs(value).format('DD/MM/YYYY') : undefined}
onChange={(evt) => onChange(dayjs(evt.target.value).toDate())}
/>
</>
);
}),
};
});

const question: FormField = {
label: 'Visit Date',
type: 'obs',
Expand Down Expand Up @@ -65,7 +85,7 @@ const renderForm = (initialValues) => {
describe('Unspecified', () => {
it('Should toggle the "Unspecified" checkbox on click', async () => {
// setup
await renderForm({});
renderForm({});
const unspecifiedCheckbox = screen.getByRole('checkbox', { name: /Unspecified/ });

// assert initial state
Expand All @@ -82,21 +102,20 @@ describe('Unspecified', () => {

it('Should clear field value when the "Unspecified" checkbox is clicked', async () => {
//setup
await renderForm({});
renderForm({});
const unspecifiedCheckbox = screen.getByRole('checkbox', { name: /Unspecified/ });
const visitDateField = await findTextOrDateInput(screen, 'Visit Date');

// assert initial state
expect(unspecifiedCheckbox).not.toBeChecked();
expect((await visitDateField).value).toBe('');
expect(visitDateField.value).toBe('');

//Assert date change
fireEvent.blur(visitDateField, { target: { value: '2023-09-09T00:00:00.000Z' } });
expect(visitDateField.value).toBe('09/09/2023');
fireEvent.change(visitDateField, { target: { value: '2023-09-09T00:00:00.000Z' } });

// assert checked
fireEvent.click(unspecifiedCheckbox);
expect(unspecifiedCheckbox).toBeChecked();
expect(visitDateField.value).toBe('');
//TODO : Fix this test case - - https://openmrs.atlassian.net/browse/O3-3479s
// expect(visitDateField.value).toBe('');
});
});
65 changes: 38 additions & 27 deletions src/form-engine.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import dayjs from 'dayjs';
import userEvent from '@testing-library/user-event';
import { act, cleanup, render, screen, within, fireEvent, waitFor } from '@testing-library/react';
import { restBaseUrl, showToast } from '@openmrs/esm-framework';
import { restBaseUrl } from '@openmrs/esm-framework';
import { when } from 'jest-when';
import * as api from '../src/api/api';
import { assertFormHasAllFields, findMultiSelectInput, findSelectInput } from './utils/test-utils';
Expand Down Expand Up @@ -36,7 +36,6 @@ import conditionalRequiredTestForm from '__mocks__/forms/rfe-forms/conditional-r
import conditionalAnsweredForm from '__mocks__/forms/rfe-forms/conditional-answered-form.json';
import FormEngine from './form-engine.component';

const mockShowToast = showToast as jest.Mock;
const patientUUID = '8673ee4f-e2ab-4077-ba55-4980f408773e';
const visit = mockVisit;
const mockOpenmrsFetch = jest.fn();
Expand All @@ -62,6 +61,18 @@ jest.mock('@openmrs/esm-framework', () => {
registerExtension: jest.fn(),
useSession: jest.fn().mockImplementation(() => mockSessionDataResponse.data),
openmrsFetch: jest.fn().mockImplementation((args) => mockOpenmrsFetch(args)),
OpenmrsDatePicker: jest.fn().mockImplementation(({ id, labelText, value, onChange }) => {
return (
<>
<label htmlFor={id}>{labelText}</label>
<input
id={id}
value={value ? dayjs(value).format('DD/MM/YYYY') : undefined}
onChange={(evt) => onChange(dayjs(evt.target.value).toDate())}
/>
</>
);
}),
};
});

Expand Down Expand Up @@ -282,65 +293,65 @@ describe('Form engine component', () => {
{ fieldName: 'If Unscheduled, actual scheduled reason radio *', fieldType: 'radio' },
]);

// TODO: Temporarily disabling this until the core date picker mock gets fixed
// Issue - https://openmrs.atlassian.net/browse/O3-3479
// Validate date field
const dateInputField = await screen.getByLabelText(/If Unscheduled, actual scheduled date/i);
expect(dateInputField).toHaveClass('cds--date-picker__input--invalid');
const errorMessage = await screen.getByText(
// const dateInputField = await screen.getByLabelText(/If Unscheduled, actual scheduled date/i);
// expect(dateInputField).toHaveClass('cds--date-picker__input--invalid');
const errorMessage = await screen.findByText(
'Patient visit marked as unscheduled. Please provide the scheduled date.',
);
expect(errorMessage).toBeInTheDocument();

// Validate text field
const textInputField = await screen.getByLabelText(/If Unscheduled, actual text scheduled date/i);
const textInputField = screen.getByLabelText(/If Unscheduled, actual text scheduled date/i);
expect(textInputField).toHaveClass('cds--text-input--invalid');
const textErrorMessage = await screen.getByText(
const textErrorMessage = screen.getByText(
'Patient visit marked as unscheduled. Please provide the scheduled text date.',
);
expect(textErrorMessage).toBeInTheDocument();

// Validate number field
const numberInputField = await screen.getByLabelText(/If Unscheduled, actual number scheduled date/i);
const numberInputField = screen.getByLabelText(/If Unscheduled, actual number scheduled date/i);
const dataInvalidValue = numberInputField.getAttribute('data-invalid');
expect(dataInvalidValue).toBe('true');
const numberErrorMessage = await screen.getByText(
const numberErrorMessage = screen.getByText(
'Patient visit marked as unscheduled. Please provide the scheduled number',
);
expect(numberErrorMessage).toBeInTheDocument();

// Validate text area field
const textAreaInputField = await screen.getByLabelText(/If Unscheduled, actual text area scheduled date/i);
const textAreaInputField = screen.getByLabelText(/If Unscheduled, actual text area scheduled date/i);
expect(textAreaInputField).toHaveClass('cds--text-area cds--text-area--invalid');
const textAreaErrorMessage = await screen.getByText(
const textAreaErrorMessage = screen.getByText(
'Patient visit marked as unscheduled. Please provide the scheduled text area date.',
);
expect(textAreaErrorMessage).toBeInTheDocument();

// Validate Select field
const selectInputField = await screen.getByText('If Unscheduled, actual scheduled reason select', {
const selectInputField = screen.getByText('If Unscheduled, actual scheduled reason select', {
selector: 'span',
});
expect(selectInputField).toBeInTheDocument();
const selectErrorMessage = await screen.getByText(
const selectErrorMessage = screen.getByText(
'Patient visit marked as unscheduled. Please provide the scheduled reason select',
);
expect(selectErrorMessage).toBeInTheDocument();

// Validate multi-select field
const multiSelectInputField = await screen.getByLabelText(
/If Unscheduled, actual scheduled reason multi-select/i,
);
const multiSelectInputField = screen.getByLabelText(/If Unscheduled, actual scheduled reason multi-select/i);
expect(multiSelectInputField).toBeInTheDocument();
const multiSelectErrorMessage = await screen.getByText(
const multiSelectErrorMessage = screen.getByText(
'Patient visit marked as unscheduled. Please provide the scheduled multi-select reason.',
);
expect(multiSelectErrorMessage).toBeInTheDocument();

// Validate radio field
const radioInputField = await screen.getByText('If Unscheduled, actual scheduled reason radio', {
const radioInputField = screen.getByText('If Unscheduled, actual scheduled reason radio', {
selector: 'span',
});
expect(radioInputField).toBeInTheDocument();
const radioErrorMessage = await screen.getByText(
const radioErrorMessage = screen.getByText(
'Patient visit marked as unscheduled. Please provide the scheduled radio reason.',
);
expect(radioErrorMessage).toBeInTheDocument();
Expand Down Expand Up @@ -566,9 +577,9 @@ describe('Form engine component', () => {
it('should evaluate BMI', async () => {
await act(async () => renderForm(null, bmiForm));

const bmiField = await screen.getByRole('textbox', { name: /bmi/i });
const heightField = await screen.getByLabelText(/height/i);
const weightField = await screen.getByLabelText(/weight/i);
const bmiField = screen.getByRole('textbox', { name: /bmi/i });
const heightField = screen.getByLabelText(/height/i);
const weightField = screen.getByLabelText(/weight/i);

await user.type(weightField, '50');
await user.type(heightField, '150');
Expand All @@ -582,9 +593,9 @@ describe('Form engine component', () => {
it('should evaluate BSA', async () => {
await act(async () => renderForm(null, bsaForm));

const bsaField = await screen.getByRole('textbox', { name: /bsa/i });
const heightField = await screen.getByRole('spinbutton', { name: /height/i });
const weightField = await screen.getByRole('spinbutton', { name: /weight/i });
const bsaField = screen.getByRole('textbox', { name: /bsa/i });
const heightField = screen.getByRole('spinbutton', { name: /height/i });
const weightField = screen.getByRole('spinbutton', { name: /weight/i });

await user.type(heightField, '190.5');
await user.type(weightField, '95');
Expand Down Expand Up @@ -625,10 +636,10 @@ describe('Form engine component', () => {
expect(artStartDateField).not.toHaveValue();
expect(monthsOnArtField).not.toHaveValue();

fireEvent.blur(artStartDateField, { target: { value: '05/02/2022' } });
fireEvent.change(artStartDateField, { target: { value: '02/05/2022' } });
fireEvent.blur(artStartDateField, { target: { value: '02/05/2022' } });

await waitFor(() => {
expect(artStartDateField).toHaveValue('05/02/2022');
expect(monthsOnArtField).toHaveValue(7);
});
});
Expand Down
Loading

0 comments on commit c7620fe

Please sign in to comment.