Skip to content

Commit

Permalink
Update to react-to-print 3.0.0-beta-1 and use CSS properties
Browse files Browse the repository at this point in the history
  • Loading branch information
ibacher authored and denniskigen committed Aug 20, 2024
1 parent 25ba865 commit f91b7cc
Show file tree
Hide file tree
Showing 10 changed files with 142 additions and 169 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"@hookform/resolvers": "^3.3.1",
"classnames": "^2.3.2",
"react-hook-form": "^7.46.2",
"react-to-print": "^2.14.13",
"react-to-print": "^3.0.0-beta-1",
"zod": "^3.22.2"
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import React from 'react';
import React, { forwardRef } from 'react';
import { useConfig } from '@openmrs/esm-framework';
import { type ConfigObject } from '../config-schema';
import styles from './print-identifier-sticker-content.scss';
import IdentifierSticker from './print-identifier-sticker.component';
import { useImperativeHandle } from 'react';
import { useRef } from 'react';
import { useEffect } from 'react';

interface PrintIdentifierStickerContentProps {
numberOfLabelRowsPerPage: number;
Expand All @@ -11,58 +14,58 @@ interface PrintIdentifierStickerContentProps {
patient: fhir.Patient;
}

const PrintIdentifierStickerContent: React.FC<PrintIdentifierStickerContentProps> = ({
numberOfLabelRowsPerPage,
numberOfLabelColumns,
labels,
patient,
}) => {
const { printIdentifierStickerWidth, printIdentifierStickerHeight, printIdentifierStickerPaperSize } =
useConfig<ConfigObject>();
const PrintIdentifierStickerContent = forwardRef<HTMLDivElement, PrintIdentifierStickerContentProps>(
({ numberOfLabelRowsPerPage, numberOfLabelColumns, labels, patient }, ref) => {
const { printIdentifierStickerWidth, printIdentifierStickerHeight, printIdentifierStickerPaperSize } =
useConfig<ConfigObject>();
const divRef = useRef<HTMLDivElement>();

if (numberOfLabelColumns < 1 || numberOfLabelRowsPerPage < 1 || labels.length < 1) {
return;
}
const maxLabelsPerPage = numberOfLabelRowsPerPage * numberOfLabelColumns;
const pages = [];
useImperativeHandle(ref, () => divRef.current, []);

for (let i = 0; i < labels.length; i += maxLabelsPerPage) {
pages.push(labels.slice(i, i + maxLabelsPerPage));
}
useEffect(() => {
if (divRef.current) {
const style = divRef.current.style;
style.setProperty('--omrs-print-label-paper-size', printIdentifierStickerPaperSize);
style.setProperty('--omrs-print-label-colums', numberOfLabelColumns.toString());
style.setProperty('--omrs-print-label-rows', numberOfLabelRowsPerPage.toString());
style.setProperty('--omrs-print-label-sticker-height', printIdentifierStickerHeight);
style.setProperty('--omrs-print-label-sticker-width', printIdentifierStickerWidth);
}
}, [
printIdentifierStickerPaperSize,
numberOfLabelColumns,
numberOfLabelRowsPerPage,
printIdentifierStickerHeight,
printIdentifierStickerWidth,
]);

return (
<div>
<style type="text/css" media="print">
{`
@page {
size: ${printIdentifierStickerPaperSize};
}
@media print { html, body { height: initial !important; overflow: initial !important; background-color: white; }}
`}
</style>
{pages.map((pageLabels, pageIndex) => (
<div key={pageIndex} className={pageIndex < pages.length - 1 ? styles.pageBreak : ''}>
<div
className={styles.labelsContainer}
style={{
gridTemplateColumns: `repeat(${numberOfLabelColumns}, 1fr)`,
gridTemplateRows: `repeat(${numberOfLabelRowsPerPage}, auto)`,
}}
>
{pageLabels.map((_, index) => (
<div
key={index}
className={styles.printContainer}
style={{ height: printIdentifierStickerHeight, width: printIdentifierStickerWidth }}
>
<IdentifierSticker patient={patient} />
</div>
))}
const maxLabelsPerPage = numberOfLabelRowsPerPage * numberOfLabelColumns;
const pages: Array<typeof labels> = [];

for (let i = 0; i < labels.length; i += maxLabelsPerPage) {
pages.push(labels.slice(i, i + maxLabelsPerPage));
}

if (numberOfLabelColumns < 1 || numberOfLabelRowsPerPage < 1 || labels.length < 1) {
return;
}

return (
<div ref={divRef} className={styles.printRoot}>
{pages.map((pageLabels, pageIndex) => (
<div key={pageIndex} className={pageIndex < pages.length - 1 ? styles.pageBreak : ''}>
<div className={styles.labelsContainer}>
{pageLabels.map((label, index) => (
<div key={index} className={styles.printContainer}>
<IdentifierSticker patient={patient} />
</div>
))}
</div>
</div>
</div>
))}
</div>
);
};
))}
</div>
);
},
);

export default PrintIdentifierStickerContent;
Original file line number Diff line number Diff line change
@@ -1,10 +1,30 @@
@use '@carbon/layout';
@use '@carbon/type';
@import '@openmrs/esm-styleguide/src/vars';
@use '@openmrs/esm-styleguide/src/vars' as *;

.printRoot {
@media print {
@page {
size: var(--omrs-print-label-paper-size, auto);
}

html,
body {
height: initial !important;
overflow: initial !important;
background-color: white;
}
}

.labelsContainer {
grid-template-columns: repeat(var(--omrs-print-label-colums, 1), 1fr);
grid-template-rows: repeat(var(--omrs-print-label-rows, 1), auto);
}
}

.printContainer {
height: 11rem;
width: 13rem;
height: var(--omrs-print-label-sticker-height, 11rem);
width: var(--omrs-print-label-sticker-width, 13rem);
background-color: $ui-01;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useMemo } from 'react';
import React, { forwardRef, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { age, getPatientName, useConfig, getCoreTranslation } from '@openmrs/esm-framework';
import { type ConfigObject } from '../config-schema';
Expand All @@ -8,7 +8,22 @@ interface IdentifierStickerProps {
patient: fhir.Patient;
}

const IdentifierSticker: React.FC<IdentifierStickerProps> = ({ patient }) => {
const getGender = (gender: string): string => {
switch (gender) {
case 'male':
return getCoreTranslation('male', 'Male');
case 'female':
return getCoreTranslation('female', 'Female');
case 'other':
return getCoreTranslation('other', 'Other');
case 'unknown':
return getCoreTranslation('unknown', 'Unknown');
default:
return gender;
}
};

const IdentifierSticker = forwardRef<HTMLDivElement, IdentifierStickerProps>(({ patient }, ref) => {
const { t } = useTranslation();
const { printIdentifierStickerFields, excludePatientIdentifierCodeTypes } = useConfig<ConfigObject>();

Expand All @@ -17,21 +32,6 @@ const IdentifierSticker: React.FC<IdentifierStickerProps> = ({ patient }) => {
return {};
}

const getGender = (gender: string): string => {
switch (gender) {
case 'male':
return getCoreTranslation('male', 'Male');
case 'female':
return getCoreTranslation('female', 'Female');
case 'other':
return getCoreTranslation('other', 'Other');
case 'unknown':
return getCoreTranslation('unknown', 'Unknown');
default:
return gender;
}
};

const identifiers =
patient.identifier?.filter(
(identifier) => !excludePatientIdentifierCodeTypes?.uuids.includes(identifier.type.coding[0].code),
Expand All @@ -50,26 +50,26 @@ const IdentifierSticker: React.FC<IdentifierStickerProps> = ({ patient }) => {
}, [excludePatientIdentifierCodeTypes?.uuids, patient]);

return (
<div className={styles.stickerContainer}>
<div ref={ref} className={styles.stickerContainer}>
{printIdentifierStickerFields.includes('name') && <div className={styles.patientName}>{patientDetails.name}</div>}
{patientDetails.identifiers.map((identifier) => {
return (
<p key={identifier?.id}>
{identifier?.type?.text}: <strong>{identifier?.value}</strong>
{identifier?.type?.text}: <span className="patient-identifier">{identifier?.value}</span>
</p>
);
})}
<p>
{getCoreTranslation('sex', 'Sex')}: <strong>{patientDetails.gender}</strong>
{getCoreTranslation('sex', 'Sex')}: <span className="patient-gender">{patientDetails.gender}</span>
</p>
<p>
{t('dob', 'DOB')}: <strong>{patientDetails.dateOfBirth}</strong>
{t('dob', 'DOB')}: <span className="patient-dob">{patientDetails.dateOfBirth}</span>
</p>
<p>
{getCoreTranslation('age', 'Age')}: <strong>{patientDetails.age}</strong>
{getCoreTranslation('age', 'Age')}: <span className="patient-age">{patientDetails.age}</span>
</p>
</div>
);
};
});

export default IdentifierSticker;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useReactToPrint } from 'react-to-print';
import { useReactToPrint, type UseReactToPrintOptions } from 'react-to-print';
import { Button, InlineLoading, ModalBody, ModalFooter, ModalHeader } from '@carbon/react';
import { getPatientName, showSnackbar, useConfig, getCoreTranslation } from '@openmrs/esm-framework';
import { type ConfigObject } from '../config-schema';
Expand All @@ -18,8 +18,7 @@ const PrintIdentifierStickerModal: React.FC<PrintIdentifierStickerModalProps> =
const { t } = useTranslation();
const { numberOfPatientIdStickers, numberOfPatientIdStickerRowsPerPage, numberOfPatientIdStickerColumns } =
useConfig<ConfigObject>();
const contentToPrintRef = useRef(null);
const onBeforeGetContentResolve = useRef<() => void | null>(null);
const contentToPrintRef = useRef<HTMLDivElement | null>(null);
const [numberOfLabelColumns, setNumberOfLabelColumns] = useState<number>(numberOfPatientIdStickerColumns);
const [numberOfLabelRowsPerPage, setNumberOfLabelRowsPerPage] = useState<number>(numberOfPatientIdStickerRowsPerPage);
const [numberOfLabels, setNumberOfLabels] = useState<number>(numberOfPatientIdStickers);
Expand All @@ -29,49 +28,30 @@ const PrintIdentifierStickerModal: React.FC<PrintIdentifierStickerModalProps> =

const labels = Array.from({ length: numberOfLabels });

useEffect(() => {
if (isPrinting && onBeforeGetContentResolve.current) {
onBeforeGetContentResolve.current();
}
}, [isPrinting]);

const handleBeforeGetContent = useCallback(
() =>
new Promise<void>((resolve) => {
if (patient && headerTitle) {
onBeforeGetContentResolve.current = resolve;
setIsPrinting(true);
}
}),
[headerTitle, patient],
);
const handleBeforePrint = useCallback(() => setIsPrinting(true), []);

const handleAfterPrint = useCallback(() => {
onBeforeGetContentResolve.current = null;
setIsPrinting(false);
closeModal();
}, [closeModal]);

const handlePrintError = useCallback((errorLocation, error) => {
onBeforeGetContentResolve.current = null;

const handlePrintError = useCallback<UseReactToPrintOptions['onPrintError']>((errorLocation, error) => {
showSnackbar({
isLowContrast: false,
kind: 'error',
title: getCoreTranslation('printError', 'Print error'),
subtitle:
getCoreTranslation('printErrorExplainer', 'An error occurred in "{{errorLocation}}": ', { errorLocation }) +
getCoreTranslation('printErrorExplainer', 'An error occurred during "{{errorLocation}}": ', { errorLocation }) +
error,
});

setIsPrinting(false);
}, []);

const handlePrint = useReactToPrint({
content: () => contentToPrintRef.current,
contentRef: contentToPrintRef,
documentTitle: `${patient ? getPatientName(patient) : ''} - ${headerTitle}`,
onAfterPrint: handleAfterPrint,
onBeforeGetContent: handleBeforeGetContent,
onPrintError: handlePrintError,
});

Expand All @@ -86,23 +66,29 @@ const PrintIdentifierStickerModal: React.FC<PrintIdentifierStickerModalProps> =
id="numberOfColumnsInput"
label={t('numberOfLabelColumns', 'Number of patient Id sticker columns')}
min={1}
onChange={(event) => setNumberOfLabelColumns(parseInt(event.target.value || 1))}
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
setNumberOfLabelColumns(parseInt(event.target.value || '1'))
}
value={numberOfLabelColumns}
hideSteppers={true}
/>
<NumberInput
id="numberOfRowsPerPageInput"
label={t('numberOfLabelRowsPerPage', 'Number of patient Id sticker rows per page')}
min={1}
onChange={(event) => setNumberOfLabelRowsPerPage(parseInt(event.target.value || 1))}
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
setNumberOfLabelRowsPerPage(parseInt(event.target.value || '1'))
}
value={numberOfLabelRowsPerPage}
hideSteppers={true}
/>
<NumberInput
id="numberOfLabels"
label={t('numberOfLabels', 'Number of Patient Id Stickers')}
min={1}
onChange={(event) => setNumberOfLabels(parseInt(event.target.value || 1))}
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
setNumberOfLabels(parseInt(event.target.value || '1'))
}
value={numberOfLabels}
hideSteppers={true}
/>
Expand All @@ -119,7 +105,7 @@ const PrintIdentifierStickerModal: React.FC<PrintIdentifierStickerModalProps> =
<Button kind="secondary" onClick={closeModal}>
{getCoreTranslation('cancel', 'Cancel')}
</Button>
<Button className={styles.button} disabled={isPrinting} onClick={handlePrint} kind="primary">
<Button className={styles.button} disabled={isPrinting} onClick={() => handlePrint()} kind="primary">
{isPrinting ? (
<InlineLoading className={styles.loader} description={getCoreTranslation('printing', 'Printing') + '...'} />
) : (
Expand All @@ -130,6 +116,7 @@ const PrintIdentifierStickerModal: React.FC<PrintIdentifierStickerModalProps> =
<div className={`${styles.previewContainer} ${!isPreviewVisible ? styles.hideResultsPreview : ''}`}>
<div ref={contentToPrintRef}>
<PrintIdentifierStickerContent
ref={contentToPrintRef}
numberOfLabelRowsPerPage={numberOfLabelRowsPerPage}
numberOfLabelColumns={numberOfLabelColumns}
labels={labels}
Expand Down
Loading

0 comments on commit f91b7cc

Please sign in to comment.