Skip to content

[WEB-3632] - Add Device Names to PDF Export#586

Open
henry-tp wants to merge 42 commits intodevelopfrom
WEB-3632-device-names
Open

[WEB-3632] - Add Device Names to PDF Export#586
henry-tp wants to merge 42 commits intodevelopfrom
WEB-3632-device-names

Conversation

@henry-tp
Copy link
Copy Markdown
Contributor

@henry-tp henry-tp commented Jan 22, 2026


const isPageBreakAtTableStart = !_.isNil(tableLabel) && _.isNil(tableData);

if (isPageBreakAtTableStart) return; // prevent double header on new page
Copy link
Copy Markdown
Contributor Author

@henry-tp henry-tp Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An issue occurs where if the first page has content all the way to the bottom, this table needs to break the page but does not have enough room to print even one line of data before breaking, it will start on the next page with a duplicate header.

Before and After:
image

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch 👍

@henry-tp henry-tp marked this pull request as ready for review January 28, 2026 20:16
@krystophv krystophv requested a review from Copilot January 28, 2026 20:28
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds device name information to PDF exports in the Basics view, addressing ticket WEB-3632. The implementation extracts device names from upload data, provides a utility function to retrieve user-friendly device names with fallback logic, and renders them in a dedicated table section on the PDF.

Changes:

  • Added deviceName property to the Upload data type and related data flow
  • Implemented getDeviceName() utility function to retrieve render-friendly device names with fallback to labels
  • Added device names rendering section in the Basics PDF print view with automatic height calculation for line wrapping
  • Enhanced onPageAdded handler to prevent duplicate headers when page breaks occur at table boundaries
  • Updated test fixtures with device name data

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
data/types.js Added deviceName property to Upload class
src/utils/DataUtil.js Extracted and stored deviceName from upload data in device objects
src/utils/device.js Added getDeviceName utility function with fallback logic from deviceName to label
src/modules/print/PrintView.js Added devices property extraction and improved onPageAdded handler to prevent double headers
src/modules/print/BasicsPrintView.js Implemented renderDeviceNames method to display unique device names in PDF
test/utils/DataUtil.test.js Updated test fixtures and assertions to include deviceName property

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/utils/device.js
Comment thread src/modules/print/BasicsPrintView.js
Comment thread src/modules/print/BasicsPrintView.js Outdated
Comment thread src/utils/device.js Outdated
Comment thread src/utils/device.js Outdated
Comment thread src/utils/device.js Outdated
Comment thread test/utils/DataUtil.test.js Outdated
Copy link
Copy Markdown
Member

@clintonium-119 clintonium-119 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a question about the rendering approach in the basics print view that needs addressing.

Also, I'm not seeing the device list additons to the bgLog and daily print views, though they seem to be required by the Jira ticket.

Comment thread test/utils/device.test.js
});
});

describe('getDeviceName', () => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nit - could add a check to make sure 'Unknown' is passed over as in the utility function for completeness

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/modules/print/BasicsPrintView.js Outdated

if (!deviceNames.length) return;

const rows = [{ label: deviceNames.join('\n\n') }];
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This approach seems strange to me, combining all of the devices into a single table row and then having to calculate the heights, account for double-spacing, etc.

Is there a reason you didn't just have each one be a row, and then let the table work this all out natively?

I believe you should be able to set the table styling to match the figma for this, while using rows in a more idiomatic way.

Copy link
Copy Markdown
Contributor Author

@henry-tp henry-tp Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I was super close to giving up but managed to do it that way. Significant brute force may have been involved 8)

Prior to today, I was not able to make this work, because despite whatever text I put into the box, the container would not dynamically scale to the height of the text - it only added about one line's worth of height

Explanation of changes:

Per my understanding, the voilab-pdf-table library calls the renderer function twice - first with draw = false. The returned value from this call is used for measuring of the contents so the container can be drawn correctly. The second call with draw = true is then used for the actual drawing.

Prior to my changes to the custom renderer function, it would always return ' ' on the first call. This meant it was measuring the height of a whitespace (for a single line) no matter the input text.

Now, I have adjusted it to return the text itself on the first call. This means it will actually measure to get the necessary height of the container.

With due respect to the library creators, this was confusing as heck


const isPageBreakAtTableStart = !_.isNil(tableLabel) && _.isNil(tableData);

if (isPageBreakAtTableStart) return; // prevent double header on new page
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch 👍

topEdge: this.margins.top,
};

this.deviceNamesHeader = null; // if needed, call this.generateDeviceNamesHeader();
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted the deviceNameHeader property to be shared logic, but I didn't want it to be initialized in reports that don't need it (e.g. if user is only printing AGP). This is just a funky little pattern so that we are only generating the deviceNameHeader data when it is needed, by calling it in the subclasses that use it

A better way to do this might be defining a fun getter property like

get deviceNamesHeader () {
  if (!deviceNamesHeaderData) {
    this.deviceNamesHeaderData = this.generateDeviceNamesHeader();
  }

  return this.deviceNamesHeaderData;
}

but we don't use any getter properties throughout our codebase so I didn't go with that

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with either approach.

@@ -498,18 +503,30 @@ class PrintView {
}

renderCustomTextCell(tb, data, draw, column, pos, padding, isHeader) {
Copy link
Copy Markdown
Contributor Author

@henry-tp henry-tp Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously, in the pre-draw phase where the container sizes were measured, we were always returning ' '. This meant that we were always measuring the container to fit the height needed for a single whitespace character. We could override this by passing a number for the height, but if left to calculate the necessary height dynamically, voilab-pdf-table would measure based on the whitespace character. This is fine for single-line text cells (which is all we had before) but now, with dynamic-height text cells, we need to give the PDF library the actual string so it can dynamically set the height of the container.

label,
content,
height,
};
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately this is more complicated than I wanted it to be with all of the measurements. Per the requirements, the height needed to be dynamic to accommodate large device lists. This ensures that the daily and bgLog charts can adjust their content start positions based on the list length, like so.

image

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has any product discussion come up about whether or not we should be filtering the devices list to only display those present in the data used for a given view?

It would seem to me to be ideal to only list BGM devices on this view, in particular.
But if you did go to the trouble of filtering, it's probably not too time-consuming to also filter the daily and basics device lists too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @clintonium-119 , I don't know why this eluded me or I didn't think about this.

I made a discussion thread here: https://tidepoolteam.slack.com/archives/C01ELEQUVRD/p1772666366797099

The way I see it, there are two sub-questions:

  • A) Do we filter only for devices for the patient that have data in the current time frame?

  • B) Do we only show BGM devices on BgLog report pages?

For Question A, if we do want to filter only for those, my intuition is to filter the devices array by looking to see if it exists in matchedDevices.

In your opinion, do you think that approach would work for Question A?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@clintonium-119 okay, so the direction from product (slack thread):

Show all devices (regardless of bgm/cgm) but only those with data for the current time frame.

I've made the change to match those with matchedDevices here: 6380d15

@henry-tp
Copy link
Copy Markdown
Contributor Author

henry-tp commented Feb 4, 2026

@clintonium-119 I have revamped the entire PR. Please have another look, but code that you "already reviewed" may have been changed significantly.

Copy link
Copy Markdown
Member

@clintonium-119 clintonium-119 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, but I'd just like confirmation that the product intent is not to show the a filtered device list for each report section, so that, for instance, the BG Log view doesn't list CGMs and pumps that did not provide bg reading for that view, and so on.

topEdge: this.margins.top,
};

this.deviceNamesHeader = null; // if needed, call this.generateDeviceNamesHeader();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with either approach.

label,
content,
height,
};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has any product discussion come up about whether or not we should be filtering the devices list to only display those present in the data used for a given view?

It would seem to me to be ideal to only list BGM devices on this view, in particular.
But if you did go to the trouble of filtering, it's probably not too time-consuming to also filter the daily and basics device lists too.

@henry-tp
Copy link
Copy Markdown
Contributor Author

henry-tp commented Mar 9, 2026

@clintonium-119 ready for another look. Added filtering by matchedDevices

@henry-tp henry-tp requested a review from clintonium-119 March 9, 2026 21:12
@henry-tp
Copy link
Copy Markdown
Contributor Author

henry-tp commented Apr 3, 2026

Hey @clintonium-119 , this is ready for another look

Copy link
Copy Markdown
Member

@clintonium-119 clintonium-119 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🔥

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants