I got assigned to take a React app and make it very print-friendly. The pages are mostly reports so lots of tables and graphs. So I'm covering things like:
- Removing buttons like save/export, or other call-to-actions, on print
- Replacing drop-downs with headers on print
- Resizing text for print (things that look great on the screen are often not sized appropriately for print)
- Resizing table columns or hiding/showing columns based on print to make them fit. (no side-scroll on print)
Those things above are all tedious but simple. They can be accomplished with basic style changes using `@media print` queries in my LESS files or applying the proper bootstrap3 classes.
Now I'm getting into more complicated changes like:
- Taking dynamically scaled elements like SVG charts/graphs and making them look right on print. The types of scaling which happens via JS, not CSS.
- Turning a paginated table into one which fetches more/all results. In the UI you may want only the top 10 items per page, but for print, it is helpful to have the exhaustive list or top 100.
- I have header status boxes which resize the font based on its contents. So `$10` appears in a larger font than `$10,000` so that they can sit in the same size boxes on a dashboard.
This latter group is not so easy. It looks like Browser Manufacturers haven't put much focus on adding events/hooks into the browsers print modal events. As far as I can tell older versions of IE & Firefox have events, but they're unreliable.
I've identified a few ways to handle this, none of which are great IMO:
- Render the chart component twice, nested in 2 different divs. One which is shown for print, the other for the screen. The one for print has a hard-coded width, the other uses the responsive container. PROS: It requires no custom JS to be executed. The user can use the browsers CMD+P, FILE->PRINT to print. CONS: This is gross b/c for every chart on the screen it is rendered twice, and only one is shown at a time. This means a decent bit of overhead, especially on pages with many charts.
- Have my own "Print" button/icon on the screen which the user must click to trigger a set of custom pre/post print hooks. So the user clicks "Print", I run some PRE JS setting the page into a PRINT state, calling the window.print() method, and then using the returned promise to trigger my POST JS unsetting the PRINT state and returning the page to normal. PROS: This gives me the ability to cascade the PRE-PRINT property into my different components and let them handle setting themself up for print. The components have complete control of doing their own custom print state. CONS: The user cannot use CMD+P or FILE>Print. If they do my JS won't be aware of the print being triggered. If a component needs to make an ASYNC request say to fetch more table rows, it's going to cause me to have to add a delay to the `window.print()` firing. Otherwise, it'll fire before the results are added.
- GUT all JS driven responsive design. I could make it so the chart containers are all set-width columns, and let the other containers resize around them. I could remove the dynamic resizing of text based on its content. PROS: Reduces complexity around print. It doesn't require any special page state to be used. Users can print via browsers built-in mechanisms. CONS: UI/UX will not be as good. Screens and paper are not the same widths and so making it work for only 1 will result in the other not looking as good. For instance, it may be desirable to have a chart be 100% of the width of the screen and 100% of the printed pages width. Without a dynamic sizing, this isn't possible.
If you were in my shoes what would you do? Have you faced this problem before? Let me know!