From c37ba2d1a187963b3b5aeee5b951bcf9c129dcd2 Mon Sep 17 00:00:00 2001 From: Hendrik de Graaf Date: Tue, 3 Oct 2023 14:42:15 +0200 Subject: [PATCH] feat(pivot-table): truncate title and show full in tooltip (#1579) * feat(pivot-table): truncate title and show full in tooltip * fix(pivot-table): set padding on hoverable div instead of container th * fix(pivot-table): correctly show tooltip after resizing the container * chore: simplify code and remove redundant prop --- src/__demo__/PivotTable.stories.js | 41 ++++++++++++ .../PivotTable/PivotTableTitleRow.js | 62 ++++++++++++++----- .../PivotTable/PivotTableTitleRows.js | 12 ---- .../PivotTable/styles/PivotTable.style.js | 18 +++++- 4 files changed, 105 insertions(+), 28 deletions(-) diff --git a/src/__demo__/PivotTable.stories.js b/src/__demo__/PivotTable.stories.js index b4c46f346..e06782146 100644 --- a/src/__demo__/PivotTable.stories.js +++ b/src/__demo__/PivotTable.stories.js @@ -1119,3 +1119,44 @@ storiesOf('PivotTable', module).add('DEGS', (_, { pivotTableOptions }) => { ) }) + +storiesOf('PivotTable', module).add( + 'Truncated header cell', + (_, { pivotTableOptions }) => { + const widths = [250, 200, 500] + const [width, setWidth] = useState(250) + const toggleWidth = () => + setWidth( + (currentWidth) => + widths[widths.indexOf(currentWidth) + 1] ?? widths[0] + ) + const visualization = { + ...narrativeVisualization, + ...visualizationReset, + ...pivotTableOptions, + columns: narrativeVisualization.filters, + filters: narrativeVisualization.columns, + rowTotals: true, + colTotals: true, + } + + const data = { + ...narrativeData, + rows: [narrativeData.rows[0]], + } + + return ( +
+ + +
+ ) + } +) diff --git a/src/components/PivotTable/PivotTableTitleRow.js b/src/components/PivotTable/PivotTableTitleRow.js index 681eaf1ab..ae9bfb576 100644 --- a/src/components/PivotTable/PivotTableTitleRow.js +++ b/src/components/PivotTable/PivotTableTitleRow.js @@ -1,5 +1,6 @@ +import { Tooltip } from '@dhis2/ui' import PropTypes from 'prop-types' -import React, { useState, useEffect } from 'react' +import React, { useRef, useState, useEffect } from 'react' import { PivotTableCell } from './PivotTableCell.js' import { usePivotTableEngine } from './PivotTableEngineContext.js' import { cell as cellStyle } from './styles/PivotTable.style.js' @@ -8,34 +9,66 @@ export const PivotTableTitleRow = ({ title, scrollPosition, containerWidth, - totalWidth, }) => { + const containerRef = useRef(null) + const [scrollWidth, setScrollWidth] = useState(0) + const [isTitleTruncated, setIsTitleTruncated] = useState(false) const engine = usePivotTableEngine() const columnCount = engine.width + engine.rowDepth + const maxWidth = containerWidth - (engine.cellPadding * 2 + 2) + const marginLeft = Math.max(0, scrollPosition?.x ?? 0) - const [position, setPosition] = useState(scrollPosition.x) useEffect(() => { - setPosition( - Math.max(0, Math.min(scrollPosition.x, totalWidth - containerWidth)) - ) - }, [containerWidth, scrollPosition.x, totalWidth]) + if (containerRef.current) { + /* Only set `scrollWidth` once, because during a CSS transition + * the reported value can sometimes be equal to `clientWidth` + * even though the text doesn't fit. Due to `white-space: nowrap` + * and `overflow: hidden` the `scrollWidth` should remain constant, + * so we can assume this initial value is correct. */ + if (!scrollWidth) { + setScrollWidth(containerRef.current.scrollWidth) + } + const currentScrollWidth = + scrollWidth ?? containerRef.current.scrollWidth + const newIsTitleTruncated = + currentScrollWidth > containerRef.current.clientWidth + if (newIsTitleTruncated !== isTitleTruncated) { + setIsTitleTruncated(newIsTitleTruncated) + } + } + }, [containerWidth, scrollWidth, isTitleTruncated]) + return (
- {title} + {isTitleTruncated ? ( + + {({ ref: tooltipRef, onMouseOver, onMouseOut }) => ( +
+ {title} +
+ )} +
+ ) : ( + title + )}
@@ -47,5 +80,4 @@ PivotTableTitleRow.propTypes = { scrollPosition: PropTypes.shape({ x: PropTypes.number.isRequired }) .isRequired, title: PropTypes.string.isRequired, - totalWidth: PropTypes.number.isRequired, } diff --git a/src/components/PivotTable/PivotTableTitleRows.js b/src/components/PivotTable/PivotTableTitleRows.js index 34ad04e33..8a3439c9d 100644 --- a/src/components/PivotTable/PivotTableTitleRows.js +++ b/src/components/PivotTable/PivotTableTitleRows.js @@ -13,10 +13,6 @@ export const PivotTableTitleRows = ({ clippingResult, width }) => { title={engine.options.title} scrollPosition={clippingResult.scrollPosition} containerWidth={width} - totalWidth={ - engine.adaptiveClippingController.columns.totalSize + - engine.adaptiveClippingController.columns.headerSize - } /> ) : null} {engine.options.subtitle ? ( @@ -24,10 +20,6 @@ export const PivotTableTitleRows = ({ clippingResult, width }) => { title={engine.options.subtitle} scrollPosition={clippingResult.scrollPosition} containerWidth={width} - totalWidth={ - engine.adaptiveClippingController.columns.totalSize + - engine.adaptiveClippingController.columns.headerSize - } /> ) : null} {engine.visualization.filters?.length ? ( @@ -38,10 +30,6 @@ export const PivotTableTitleRows = ({ clippingResult, width }) => { )} scrollPosition={clippingResult.scrollPosition} containerWidth={width} - totalWidth={ - engine.adaptiveClippingController.columns.totalSize + - engine.adaptiveClippingController.columns.headerSize - } /> ) : null} diff --git a/src/components/PivotTable/styles/PivotTable.style.js b/src/components/PivotTable/styles/PivotTable.style.js index 23cfab64b..00bf7c6a6 100644 --- a/src/components/PivotTable/styles/PivotTable.style.js +++ b/src/components/PivotTable/styles/PivotTable.style.js @@ -109,9 +109,25 @@ export const cell = css` align-items: center; justify-content: center; } - .title { + .title-cell { font-weight: bold; background-color: #cddaed; + padding: 0; + } + .title-cell-content { + text-align: center; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + .title-cell.displaydensity-COMPACT > .title-cell-content { + padding: ${DISPLAY_DENSITY_PADDING_COMPACT}px; + } + .title-cell.displaydensity-NORMAL > .title-cell-content { + padding: ${DISPLAY_DENSITY_PADDING_NORMAL}px; + } + .title-cell.displaydensity-COMFORTABLE > .title-cell-content { + padding: ${DISPLAY_DENSITY_PADDING_COMFORTABLE}px; } .row-header { background-color: #dae6f8;