Skip to content

Commit

Permalink
feat: enable legend toggle button for LL dashboard items (DHIS2-15358) (
Browse files Browse the repository at this point in the history
  • Loading branch information
martinkrulltott authored Jan 17, 2024
1 parent 27ffd7b commit c14551d
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 68 deletions.
3 changes: 2 additions & 1 deletion src/PluginWrapper.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useCacheableSection, CacheableSection } from '@dhis2/app-runtime'
import { CenteredContent, CircularLoader, Layer } from '@dhis2/ui'
import { CenteredContent, CircularLoader, CssVariables, Layer } from '@dhis2/ui'
import postRobot from '@krakenjs/post-robot'
import PropTypes from 'prop-types'
import React, { useEffect, useState } from 'react'
Expand Down Expand Up @@ -114,6 +114,7 @@ const PluginWrapper = () => {
>
<Visualization {...propsFromParent} />
</CacheableSectionWrapper>
<CssVariables colors spacers elevations />
</div>
) : null
}
Expand Down
151 changes: 105 additions & 46 deletions src/components/Visualization/Visualization.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import {
DataTableFoot,
Pagination,
Tooltip,
Button,
IconLegend24,
NoticeBox,
} from '@dhis2/ui'
import cx from 'classnames'
Expand Down Expand Up @@ -67,9 +69,6 @@ import {
export const DEFAULT_SORT_DIRECTION = 'asc'
export const FIRST_PAGE = 1
export const PAGE_SIZE = 100
// +/- min width of "Rows per page" select + 2 * padding
// + 30% in case label text are longer than English
export const PAGINATION_MIN_WIDTH = 250

const getFontSizeClass = (fontSize) => {
switch (fontSize) {
Expand Down Expand Up @@ -125,11 +124,20 @@ export const Visualization = ({
onResponsesReceived,
onColumnHeaderClick,
onError,
forDashboard,
}) => {
const containerRef = useRef(null)
const noTimeDimensionWarningRef = useRef(null)
const dataTableRef = useRef(null)
const fetchContainerRef = useRef(null)
const dataTableHeadRef = useRef(null)
const dataTableFootRef = useRef(null)
const legendKeyRef = useRef(null)

const [uniqueLegendSets, setUniqueLegendSets] = useState([])
const [showLegendKey, setShowLegendKey] = useState(false)
const [measuredDimensions, setMeasuredDimensions] = useState({
containerMaxWidth: 0,
paginationMaxWidth: 0,
noticeBoxMaxWidth: 0,
})
Expand All @@ -150,42 +158,59 @@ export const Visualization = ({
const shouldShowTimeDimensionWarning = isInModal && !hasTimeDimension

const visualizationRef = useRef(visualization)
const legendKeyRef = useRef(null)

const containerCallbackRef = useCallback((node) => {
if (node === null) {
const onResize = useCallback(() => {
if (!containerRef?.current || containerRef.current.clientWidth === 0) {
return
}
const containerInnerWidth = containerRef.current.clientWidth
const scrollBox = containerRef.current.querySelector('.tablescrollbox')
const scrollbarWidth = scrollBox.offsetWidth - scrollBox.clientWidth
const legendKeyWidth =
legendKeyRef.current?.offsetWidth > 0
? legendKeyRef.current.offsetWidth + 4
: 0
const containerMaxWidth =
containerInnerWidth - scrollbarWidth - legendKeyWidth

setMeasuredDimensions({
containerMaxWidth,
paginationMaxWidth: containerMaxWidth - scrollbarWidth,
noticeBoxMaxWidth: scrollBox.offsetWidth,
})
}, [])

const sizeObserver = useMemo(
() => new window.ResizeObserver(onResize),
[onResize]
)

const adjustSize = () => {
if (node.clientWidth === 0) {
const mountAndObserveContainerRef = useCallback(
(node) => {
if (node === null) {
return
}
const containerInnerWidth = node.clientWidth
const scrollBox = node.querySelector('.tablescrollbox')
const scrollbarWidth = scrollBox.offsetWidth - scrollBox.clientWidth
const legendKeyWidth =
legendKeyRef.current?.offsetWidth > 0
? legendKeyRef.current.offsetWidth + 4
: 0
const paginationMaxWidth = Math.max(
containerInnerWidth - scrollbarWidth - legendKeyWidth,
PAGINATION_MIN_WIDTH
)

setMeasuredDimensions({
paginationMaxWidth,
noticeBoxMaxWidth: scrollBox.offsetWidth,
})
}
containerRef.current = node
sizeObserver.observe(node)

const sizeObserver = new window.ResizeObserver(adjustSize)
sizeObserver.observe(node)
return sizeObserver.disconnect
},
[sizeObserver]
)

adjustSize()
const observeVisualizationContainerRef = useCallback(
(node) => {
if (node === null) {
return
}

return sizeObserver.disconnect
}, [])
sizeObserver.observe(node)

return sizeObserver.disconnect
},
[sizeObserver]
)

const setPage = useCallback(
(pageNum) =>
Expand Down Expand Up @@ -221,9 +246,6 @@ export const Visualization = ({
sortDirection,
})

const fetchContainerRef = useRef(null)
const dataTableHeadRef = useRef(null)
const dataTableFootRef = useRef(null)
const fetchIndicatorTop = useMemo(() => {
if (
!fetching ||
Expand Down Expand Up @@ -261,13 +283,15 @@ export const Visualization = ({
allLegendSets.findIndex((a) => a.id === e.id) === index &&
e.legends?.length
)
if (relevantLegendSets.length && visualization.legend?.showKey) {
if (relevantLegendSets.length) {
setUniqueLegendSets(relevantLegendSets)
} else {
setUniqueLegendSets([])
}
setShowLegendKey(visualization.legend?.showKey)
} else {
setUniqueLegendSets([])
setShowLegendKey(false)
}
}, [data, visualization])

Expand Down Expand Up @@ -357,15 +381,43 @@ export const Visualization = ({
)
}

const getLegendKey = () => (
<div
className={styles.legendKeyScrollbox}
data-test="visualization-legend-key"
ref={legendKeyRef}
>
<LegendKey legendSets={uniqueLegendSets} />
</div>
)
const getLegendKey = () =>
forDashboard ? (
<div className={styles.legendKeyContainer} ref={legendKeyRef}>
<div className={styles.legendKeyToggle}>
<Button
small
secondary
onClick={() => {
setShowLegendKey(!showLegendKey)
window.requestAnimationFrame(onResize)
}}
icon={<IconLegend24 />}
toggled={showLegendKey}
/>
</div>
{showLegendKey && (
<div
className={styles.legendKeyWrapper}
data-test="visualization-legend-key"
>
<div className={styles.wrapper}>
<LegendKey legendSets={uniqueLegendSets} />
</div>
</div>
)}
</div>
) : (
showLegendKey && (
<div
className={styles.legendKeyScrollbox}
data-test="visualization-legend-key"
ref={legendKeyRef}
>
<LegendKey legendSets={uniqueLegendSets} />
</div>
)
)

const renderCellContent = ({ columnIndex, value, isUndefined, props }) => (
<DataTableCell
Expand Down Expand Up @@ -412,7 +464,10 @@ export const Visualization = ({
)

return (
<div className={styles.pluginContainer} ref={containerCallbackRef}>
<div
className={styles.pluginContainer}
ref={mountAndObserveContainerRef}
>
<div
data-test="line-list-fetch-container"
className={cx(styles.fetchContainer, {
Expand All @@ -424,7 +479,11 @@ export const Visualization = ({
className={styles.fetchIndicator}
style={{ top: fetchIndicatorTop }}
/>
<div className={styles.visualizationContainer}>
<div
className={styles.visualizationContainer}
ref={observeVisualizationContainerRef}
style={{ maxWidth: measuredDimensions.containerMaxWidth }}
>
{shouldShowTimeDimensionWarning && (
<div
className={styles.noTimeDimensionWarning}
Expand Down Expand Up @@ -555,7 +614,6 @@ export const Visualization = ({
style={{
maxWidth:
measuredDimensions.paginationMaxWidth,
minWidth: PAGINATION_MIN_WIDTH,
}}
>
<PaginationComponent
Expand Down Expand Up @@ -597,8 +655,8 @@ export const Visualization = ({
</DataTableFoot>
</DataTable>
</div>
{Boolean(uniqueLegendSets.length) && getLegendKey()}
</div>
{Boolean(uniqueLegendSets.length) && getLegendKey()}
</div>
)
}
Expand All @@ -615,6 +673,7 @@ Visualization.propTypes = {
visualization: PropTypes.object.isRequired,
onResponsesReceived: PropTypes.func.isRequired,
filters: PropTypes.object,
forDashboard: PropTypes.bool,
onColumnHeaderClick: PropTypes.func,
onError: PropTypes.func,
}
30 changes: 30 additions & 0 deletions src/components/Visualization/styles/Visualization.module.css
Original file line number Diff line number Diff line change
@@ -1,14 +1,44 @@
/* Legend key */
.legendKeyContainer {
display: flex;
flex-direction: column;
align-items: flex-end;
}

.legendKeyWrapper {
border: 1px solid var(--colors-grey400);
overflow: auto;
height: 100%;
}

.legendKey {
height: 100%;
}

.legendKeyScrollbox {
overflow-y: auto;
overflow-x: hidden;
min-width: 190px;
}

.legendKeyToggle {
margin: 1px 4px 3px 0;
}

@media print {
.legendKeyToggle {
display: none;
}
}

.pluginContainer {
display: flex;
justify-content: space-between;
gap: var(--spacers-dp4);
min-width: 0;
flex-grow: 1;
min-width: 0;
width: 100%;
}

.fetchContainer {
Expand Down
25 changes: 4 additions & 21 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1083,14 +1083,7 @@
core-js-pure "^3.25.1"
regenerator-runtime "^0.13.4"

"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.0", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.9", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
version "7.21.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.5.tgz#8492dddda9644ae3bda3b45eabe87382caee7200"
integrity sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==
dependencies:
regenerator-runtime "^0.13.11"

"@babel/runtime@^7.15.4":
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.0", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.9", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
version "7.23.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.4.tgz#36fa1d2b36db873d25ec631dcc4923fdc1cf2e2e"
integrity sha512-2Yv65nlWnWlSpe3fXEyX5i7fx5kIKo4Qbcj+hMO0odwaneFjfXw5fdum+4yL20O0QiaHpia0cYQ9xpNMqrBwHg==
Expand Down Expand Up @@ -8105,12 +8098,7 @@ flush-write-stream@^1.0.2:
inherits "^2.0.3"
readable-stream "^2.3.6"

follow-redirects@^1.0.0, follow-redirects@^1.14.0, follow-redirects@^1.14.7:
version "1.15.2"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==

follow-redirects@^1.14.9:
follow-redirects@^1.0.0, follow-redirects@^1.14.0, follow-redirects@^1.14.7, follow-redirects@^1.14.9:
version "1.15.3"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a"
integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==
Expand Down Expand Up @@ -8271,12 +8259,7 @@ fsevents@^1.2.7:
bindings "^1.5.0"
nan "^2.12.1"

fsevents@^2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==

fsevents@~2.3.2:
fsevents@^2.3.2, fsevents@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
Expand Down Expand Up @@ -13676,7 +13659,7 @@ regenerator-runtime@^0.11.0:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==

regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.9:
regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.9:
version "0.13.11"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
Expand Down

0 comments on commit c14551d

Please sign in to comment.