Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(refactor) Refactor reusable patient banner components #1209

Merged
merged 1 commit into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 0 additions & 42 deletions packages/framework/esm-framework/docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,6 @@
- [CustomOverflowMenu](API.md#customoverflowmenu)
- [PatientBannerActionsMenu](API.md#patientbanneractionsmenu)
- [PatientBannerContactDetails](API.md#patientbannercontactdetails)
- [PatientBannerPatientIdentifier](API.md#patientbannerpatientidentifier)
- [PatientBannerPatientInfo](API.md#patientbannerpatientinfo)
- [PatientBannerToggleContactDetailsButton](API.md#patientbannertogglecontactdetailsbutton)
- [PatientPhoto](API.md#patientphoto)
- [getFhirServerPaginationHandlers](API.md#getfhirserverpaginationhandlers)
Expand Down Expand Up @@ -6887,46 +6885,6 @@ ___

___

### PatientBannerPatientIdentifier

▸ **PatientBannerPatientIdentifier**(`__namedParameters`): `Element`

#### Parameters

| Name | Type |
| :------ | :------ |
| `__namedParameters` | `PatientBannerPatientIdentifierProps` |

#### Returns

`Element`

#### Defined in

[packages/framework/esm-styleguide/src/patient-banner/patient-info/patient-banner-patient-identifiers.component.tsx:41](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/patient-banner/patient-info/patient-banner-patient-identifiers.component.tsx#L41)

___

### PatientBannerPatientInfo

▸ **PatientBannerPatientInfo**(`__namedParameters`): `Element`

#### Parameters

| Name | Type |
| :------ | :------ |
| `__namedParameters` | [`PatientBannerPatientInfoProps`](interfaces/PatientBannerPatientInfoProps.md) |

#### Returns

`Element`

#### Defined in

[packages/framework/esm-styleguide/src/patient-banner/patient-info/patient-banner-patient-info.component.tsx:43](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/patient-banner/patient-info/patient-banner-patient-info.component.tsx#L43)

___

### PatientBannerToggleContactDetailsButton

▸ **PatientBannerToggleContactDetailsButton**(`__namedParameters`): `Element`
Expand Down

This file was deleted.

3 changes: 3 additions & 0 deletions packages/framework/esm-react-utils/mock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,7 @@ export const useExtensionSlot = jest.fn();

export const useForceUpdate = jest.fn();

// TODO: Remove this in favour of usePrimaryIdentifierCode below
export const usePrimaryIdentifierResource = jest.fn();

export const usePrimaryIdentifierCode = jest.fn();
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,29 @@ import { useConfig, usePrimaryIdentifierCode } from '@openmrs/esm-react-utils';
import { type StyleguideConfigObject } from '../../config-schema';
import styles from './patient-banner-patient-info.module.scss';

interface IdentifierProps {
interface IdentifiersProps {
showIdentifierLabel: boolean;
type: fhir.CodeableConcept | undefined;
value: string | undefined;
}

interface PatientBannerPatientIdentifierProps {
identifier: fhir.Identifier[] | undefined;
interface PatientBannerPatientIdentifiersProps {
identifiers: fhir.Identifier[] | undefined;
showIdentifierLabel: boolean;
}

function PrimaryIdentifier({ showIdentifierLabel, type, value }: IdentifierProps) {
function PrimaryIdentifier({ showIdentifierLabel, type, value }: IdentifiersProps) {
return (
<span className={styles.primaryIdentifier}>
{showIdentifierLabel && (
<Tag className={styles.tag} type="gray" title={type?.text}>
{type?.text && <span className={styles.label}>{type.text}: </span>}
<span className={styles.value}>{value}</span>
</Tag>
)}
<Tag className={styles.tag} type="gray">
{showIdentifierLabel && type?.text && <span className={styles.label}>{type.text}: </span>}
<span className={styles.value}>{value}</span>
</Tag>
</span>
);
}

function SecondaryIdentifier({ showIdentifierLabel, type, value }: IdentifierProps) {
function SecondaryIdentifier({ showIdentifierLabel, type, value }: IdentifiersProps) {
return (
<FormLabel className={styles.secondaryIdentifier} id={`patient-banner-identifier-${value}`}>
{showIdentifierLabel && <span className={styles.label}>{type?.text}: </span>}
Expand All @@ -38,15 +36,12 @@ function SecondaryIdentifier({ showIdentifierLabel, type, value }: IdentifierPro
);
}

export function PatientBannerPatientIdentifier({
identifier,
showIdentifierLabel,
}: PatientBannerPatientIdentifierProps) {
function PatientBannerPatientIdentifiers({ identifiers, showIdentifierLabel }: PatientBannerPatientIdentifiersProps) {
const { excludePatientIdentifierCodeTypes } = useConfig<StyleguideConfigObject>();
const { primaryIdentifierCode } = usePrimaryIdentifierCode();

const filteredIdentifiers =
identifier?.filter((identifier) => {
identifiers?.filter((identifier) => {
const code = identifier.type?.coding?.[0]?.code;
return code && !excludePatientIdentifierCodeTypes?.uuids.includes(code);
}) ?? [];
Expand All @@ -56,11 +51,11 @@ export function PatientBannerPatientIdentifier({
{filteredIdentifiers?.length
? filteredIdentifiers.map(({ value, type }, index) => (
<React.Fragment key={value}>
<span className={styles.identifierTag}>
<span className={styles.identifier}>
{type?.coding?.[0]?.code === primaryIdentifierCode ? (
<PrimaryIdentifier value={value} type={type} showIdentifierLabel={showIdentifierLabel} />
<PrimaryIdentifier showIdentifierLabel={showIdentifierLabel} type={type} value={value} />
) : (
<SecondaryIdentifier value={value} type={type} showIdentifierLabel={showIdentifierLabel} />
<SecondaryIdentifier showIdentifierLabel={showIdentifierLabel} type={type} value={value} />
)}
</span>
{index < filteredIdentifiers.length - 1 && <span className={styles.separator}>&middot;</span>}
Expand All @@ -71,4 +66,4 @@ export function PatientBannerPatientIdentifier({
);
}

export default PatientBannerPatientIdentifier;
export default PatientBannerPatientIdentifiers;
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { useConfig, usePrimaryIdentifierCode } from '@openmrs/esm-react-utils';
import PatientBannerPatientIdentifiers from './patient-banner-patient-identifiers.component';

const mockUsePrimaryIdentifierCode = jest.mocked(usePrimaryIdentifierCode);
const mockUseConfig = jest.mocked(useConfig);

describe('PatientBannerPatientIdentifiers', () => {
const mockIdentifiers = [
{
use: 'official',
type: {
coding: [{ code: '05a29f94-c0ed-11e2-94be-8c13b969e334' }],
text: 'OpenMRS ID',
},
value: '100GEJ',
},
{
use: 'official',
type: {
coding: [{ code: '4281ec43-388b-4c25-8bb2-deaff0867b2c' }],
text: 'National ID',
},
value: '123456789',
},
];

beforeEach(() => {
mockUsePrimaryIdentifierCode.mockReturnValue({
primaryIdentifierCode: '05a29f94-c0ed-11e2-94be-8c13b969e334',
isLoading: false,
error: undefined,
});
mockUseConfig.mockReturnValue({
excludePatientIdentifierCodeTypes: { uuids: [] },
});
});

it('renders the patient identifiers', async () => {
render(<PatientBannerPatientIdentifiers identifiers={mockIdentifiers} showIdentifierLabel />);

expect(screen.getByText(/openmrs id/i)).toBeInTheDocument();
expect(screen.getByText(/100gej/i)).toBeInTheDocument();
expect(screen.getByText(/national id/i)).toBeInTheDocument();
expect(screen.getByText(/123456789/i)).toBeInTheDocument();
});

it('does not render identifier labels if showIdentifierLabel is false', () => {
render(<PatientBannerPatientIdentifiers identifiers={mockIdentifiers} showIdentifierLabel={false} />);

expect(screen.queryByText(/openmrs id/i)).not.toBeInTheDocument();
expect(screen.getByText(/100gej/i)).toBeInTheDocument();
expect(screen.queryByText(/national id/i)).not.toBeInTheDocument();
expect(screen.getByText(/123456789/i)).toBeInTheDocument();
});

it('renders nothing if identifiers are not provided', () => {
const { container } = render(<PatientBannerPatientIdentifiers identifiers={[]} showIdentifierLabel />);

expect(container).toBeEmptyDOMElement();
});

it('filters out excluded identifier types', () => {
mockUseConfig.mockReturnValue({
excludePatientIdentifierCodeTypes: { uuids: ['4281ec43-388b-4c25-8bb2-deaff0867b2c'] },
});

render(<PatientBannerPatientIdentifiers identifiers={mockIdentifiers} showIdentifierLabel />);

expect(screen.queryByText(/openmrs id/i)).toBeInTheDocument();
expect(screen.queryByText(/national id/i)).not.toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { ExtensionSlot } from '@openmrs/esm-react-utils';
import { getCoreTranslation } from '@openmrs/esm-translations';
import { age, formatDate, parseDate } from '@openmrs/esm-utils';
import { GenderFemaleIcon, GenderMaleIcon, GenderOtherIcon, GenderUnknownIcon } from '../../icons';
import PatientBannerPatientIdentifier from './patient-banner-patient-identifiers.component';
import PatientBannerPatientIdentifiers from './patient-banner-patient-identifiers.component';
import styles from './patient-banner-patient-info.module.scss';

export interface PatientBannerPatientInfoProps {
interface PatientBannerPatientInfoProps {
patient: fhir.Patient;
}

Expand Down Expand Up @@ -40,7 +40,7 @@ const getGender = (gender: string): string => {
return getCoreTranslation(key, gender);
};

export function PatientBannerPatientInfo({ patient }: PatientBannerPatientInfoProps) {
function PatientBannerPatientInfo({ patient }: PatientBannerPatientInfoProps) {
const name = `${patient?.name?.[0]?.given?.join(' ')} ${patient?.name?.[0]?.family}`;
const gender = patient?.gender && getGender(patient.gender);

Expand Down Expand Up @@ -71,8 +71,10 @@ export function PatientBannerPatientInfo({ patient }: PatientBannerPatientInfoPr
<span className={styles.separator}>&middot;</span>
</>
)}
<PatientBannerPatientIdentifier identifier={patient.identifier} showIdentifierLabel={true} />
<PatientBannerPatientIdentifiers identifiers={patient.identifier} showIdentifierLabel />
</div>
</div>
);
}

export default PatientBannerPatientInfo;
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
align-items: center;
}

.identifierTag {
.identifier {
display: flex;
align-items: center;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React from 'react';
import { type i18n } from 'i18next';
import { screen, render } from '@testing-library/react';
import { usePrimaryIdentifierCode } from '@openmrs/esm-react-utils';
import PatientBannerPatientInfo from './patient-banner-patient-info.component';

window.i18next = { language: 'en' } as i18n;
const mockUsePrimaryIdentifierCode = jest.mocked(usePrimaryIdentifierCode);

const nameWithFormat = {
id: 'efdb246f-4142-4c12-a27a-9be60b9592e9',
family: 'Wilson',
given: ['John'],
text: 'Wilson, John',
};

const mockPatient = {
resourceType: 'Patient',
id: '8673ee4f-e2ab-4077-ba55-4980f408773e',
extension: [
{
url: 'http://fhir-es.transcendinsights.com/stu3/StructureDefinition/resource-date-created',
valueDateTime: '2017-01-18T09:42:40+00:00',
},
{
url: 'https://purl.org/elab/fhir/StructureDefinition/Creator-crew-version1',
valueString: 'daemon',
},
],
identifier: [
{
use: 'official',
type: {
coding: [{ code: '05a29f94-c0ed-11e2-94be-8c13b969e334' }],
text: 'OpenMRS ID',
},
value: '100GEJ',
},
{
use: 'official',
type: {
coding: [{ code: '4281ec43-388b-4c25-8bb2-deaff0867b2c' }],
text: 'National ID',
},
value: '123456789',
},
],
active: true,
name: [nameWithFormat],
gender: 'male',
birthDate: '1972-04-04',
deceasedBoolean: false,
address: [],
};

describe('PatientBannerPatientInfo', () => {
beforeEach(() => {
mockUsePrimaryIdentifierCode.mockReturnValue({
primaryIdentifierCode: '05a29f94-c0ed-11e2-94be-8c13b969e334',
isLoading: false,
error: undefined,
});
});

it("renders the patient's name, demographics, and identifier details in the banner", () => {
render(<PatientBannerPatientInfo patient={mockPatient} />);

expect(screen.getByText(/john wilson/i)).toBeInTheDocument();
expect(screen.getByText(/male/i)).toBeInTheDocument();
expect(screen.getByText(/52 yrs/i)).toBeInTheDocument();
expect(screen.getByText(/04-Apr-1972/i)).toBeInTheDocument();
expect(screen.getByText(/openmrs id/i)).toBeInTheDocument();
expect(screen.getByText(/100gej/i)).toBeInTheDocument();
expect(screen.getByText(/national id/i)).toBeInTheDocument();
expect(screen.getByText(/123456789/i)).toBeInTheDocument();
});
});
Loading