From 2c49410b5e1e96b43624c2b9f1a9c6d56b4589e9 Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Tue, 8 Sep 2020 09:56:38 +0200 Subject: [PATCH] fix: include title page in edit preview but scroll to below it [DHIS2-9417] (#1038) In contrast to the Print Layout (from View mode), the Edit print preview did not have a title page, which means there was no page break prior to the first item page. This means the first item page had more vertical space in Edit print preview than in Print Layout. The result could lead to more items getting on the first page in Edit preview, than the actual print. The solution is to include the title page in Edit preview as well, so that Edit preview and Print Layout are identical. Then, to allow the user to focus on the important stuff (item print layout), the Edit preview is auto scrolled to the top of the first item page. --- package.json | 1 + .../Dashboard/PrintLayoutDashboard.js | 10 ++-- .../ItemGrid/PrintLayoutItemGrid.js | 38 ++++++++++++- src/modules/__tests__/printUtils.spec.js | 53 ++++++++++++++++++- src/modules/printUtils.js | 21 ++++++-- 5 files changed, 113 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 0b22f2cfc..b2b053572 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@dhis2/ui": "^5.5.4", "@material-ui/core": "^3.9.2", "@material-ui/icons": "^4.9.1", + "classnames": "^2.2.6", "d2": "^31.8.1", "d2-utilizr": "^0.2.16", "i18next": "^19.7.0", diff --git a/src/components/Dashboard/PrintLayoutDashboard.js b/src/components/Dashboard/PrintLayoutDashboard.js index 77fde0684..0f73e7b26 100644 --- a/src/components/Dashboard/PrintLayoutDashboard.js +++ b/src/components/Dashboard/PrintLayoutDashboard.js @@ -2,6 +2,7 @@ import React, { Component } from 'react' import { connect } from 'react-redux' import PropTypes from 'prop-types' import { spacers } from '@dhis2/ui' +import cx from 'classnames' import PrintInfo from './PrintInfo' import PrintActionsBar from './PrintActionsBar' @@ -103,9 +104,7 @@ export class PrintLayoutDashboard extends Component { addPageBreaks(this.props) - if (!this.props.fromEdit) { - this.props.addDashboardItem({ type: PRINT_TITLE_PAGE }) - } + this.props.addDashboardItem({ type: PRINT_TITLE_PAGE }) } } @@ -137,7 +136,10 @@ export class PrintLayoutDashboard extends Component { {!this.props.fromEdit && ( )} -
+
{!this.props.fromEdit && }
{ this.props.updateDashboardLayout(newLayout) } + + isFirstPageBreak = item => { + if (item.type !== PAGEBREAK) { + return false + } + + const pageBreaks = this.props.dashboardItems.filter( + i => i.type === PAGEBREAK + ) + + const sortedPageBreaks = sortBy(pageBreaks, ['y']) + return item.y === sortedPageBreaks[0].y + } + getItemComponent = item => { - const itemClassNames = [item.type, 'print', 'layout'].join(' ') + // the first-page-break class is used in Edit print preview + const itemClassNames = cx('print', 'layout', `${item.type}`, { + 'first-page-break': + this.props.isEditing && this.isFirstPageBreak(item), + }) return (
@@ -65,6 +87,7 @@ export class PrintLayoutItemGrid extends Component { for (let i = sortedElements.length - 1; i >= 0; --i) { const item = sortedElements[i] + if (item.type === PAGEBREAK) { if (!foundNonPageBreak) { item.el.classList.add('removed') @@ -98,6 +121,17 @@ export class PrintLayoutItemGrid extends Component { if (gridElement) { gridElement.style.height = `${maxHeight}px` } + + if (this.props.isEditing) { + //scroll to below the title page - which is middle of the first pagebreak + const firstBreak = document.querySelector('.first-page-break') + if (firstBreak && firstBreak.style && firstBreak.style.transform) { + const yPos = getTransformYPx(firstBreak.style) + const scrollArea = document.querySelector('.scroll-area') + + scrollArea && scrollArea.scroll(0, yPos + 50) + } + } } componentDidMount() { @@ -155,6 +189,7 @@ export class PrintLayoutItemGrid extends Component { PrintLayoutItemGrid.propTypes = { dashboardItems: PropTypes.array, + isEditing: PropTypes.bool, isLoading: PropTypes.bool, updateDashboardLayout: PropTypes.func, } @@ -169,6 +204,7 @@ const mapStateToProps = state => { return { isLoading: sGetSelectedIsLoading(state) || !selectedDashboard, dashboardItems: sGetPrintDashboardItems(state).filter(hasShape), + isEditing: sGetIsEditing(state), } } diff --git a/src/modules/__tests__/printUtils.spec.js b/src/modules/__tests__/printUtils.spec.js index 860ec37fe..e019a9f03 100644 --- a/src/modules/__tests__/printUtils.spec.js +++ b/src/modules/__tests__/printUtils.spec.js @@ -1,4 +1,4 @@ -import { getDomGridItemsSortedByYPos } from '../printUtils' +import { getDomGridItemsSortedByYPos, getTransformYPx } from '../printUtils' describe('printUtils', () => { describe('getDomGridItemsSortedByYPos', () => { @@ -303,4 +303,55 @@ describe('printUtils', () => { ) }) }) + + describe('getTransformYPx', () => { + it('returns null if style is not defined', () => { + const style = undefined + expect(getTransformYPx(style)).toEqual(null) + }) + + it('returns null if no transform property', () => { + const style = {} + expect(getTransformYPx(style)).toEqual(null) + }) + + it('returns null if transform is malformed', () => { + const style = { + transform: 'ab', + } + expect(getTransformYPx(style)).toEqual(null) + }) + + it('returns y position if px in 100s', () => { + const style = { + transform: 'translate(10px, 300px)', + } + + expect(getTransformYPx(style)).toEqual(300) + }) + + it('returns y position if px in 10s', () => { + const style = { + transform: 'translate(10px, 30px)', + } + + expect(getTransformYPx(style)).toEqual(30) + }) + + it('returns y position if px in 1000s', () => { + const style = { + transform: 'translate(10px, 3000px)', + } + + expect(getTransformYPx(style)).toEqual(3000) + }) + + it('returns null if not px units', () => { + const style = { + transform: 'translate(10%, 50%)', + } + + expect(getTransformYPx(style)).toEqual(null) + }) + }) }) diff --git a/src/modules/printUtils.js b/src/modules/printUtils.js index 788498f4d..7fb4c7a81 100644 --- a/src/modules/printUtils.js +++ b/src/modules/printUtils.js @@ -9,6 +9,22 @@ export const a4LandscapeWidthPx = 1102 export const MAX_ITEM_GRID_HEIGHT = 33 +export const getTransformYPx = elStyle => { + if (!elStyle || !elStyle.transform) { + return null + } + + // find valid transforms - those with y pixels + // the code is expecting the transform prop to + // look like: translate(10px, 300px) + const transformY = elStyle.transform.split(' ')[1]?.match(/(\d+)px/) + if (transformY) { + return parseInt(transformY[1]) + } else { + return null + } +} + export const getDomGridItemsSortedByYPos = elements => { const types = Object.keys(itemTypeMap) const elementsWithBoundingRect = orArray(elements).map(el => { @@ -17,10 +33,7 @@ export const getDomGridItemsSortedByYPos = elements => { ) const rect = el.getBoundingClientRect() - - const y = el.style.transform - ? parseInt(el.style.transform?.split(' ')[1]?.slice(0, -3)) - : parseInt(rect.y) + const y = getTransformYPx(el.style) || parseInt(rect.y) return { type,