From f832056b63598f1e8c15a5effa32ae0c128571cb Mon Sep 17 00:00:00 2001 From: Rabi <134292357+Rabi94@users.noreply.github.com> Date: Thu, 23 Jul 2026 11:56:04 -0300 Subject: [PATCH 1/3] Improve role chip overflow in Family Screen V2 requirements --- .../src/Families/ApprovalsDataGridV2.tsx | 51 +++++++++++++++++-- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/src/caretogether-pwa/src/Families/ApprovalsDataGridV2.tsx b/src/caretogether-pwa/src/Families/ApprovalsDataGridV2.tsx index 45bf768b..770f7009 100644 --- a/src/caretogether-pwa/src/Families/ApprovalsDataGridV2.tsx +++ b/src/caretogether-pwa/src/Families/ApprovalsDataGridV2.tsx @@ -1,5 +1,5 @@ import ChevronRightIcon from '@mui/icons-material/ChevronRight'; -import { Box, Chip, Typography, useTheme } from '@mui/material'; +import { Box, Chip, Tooltip, Typography, useTheme } from '@mui/material'; import { DataGrid, GridColDef, GridToolbar } from '@mui/x-data-grid'; import { useRef } from 'react'; import { useUserLookup } from '../Model/DirectoryModel'; @@ -22,14 +22,28 @@ type ApprovalsDataGridV2Props = { rows: ApprovalLedgerRow[]; }; -function ChipList({ labels }: { labels: string[] }) { +const MAX_VISIBLE_ROLE_CHIPS = 3; + +function OverflowRoleChipList({ labels }: { labels: string[] }) { if (labels.length === 0) { return -; } + const visibleLabels = labels.slice(0, MAX_VISIBLE_ROLE_CHIPS); + const hiddenLabels = labels.slice(MAX_VISIBLE_ROLE_CHIPS); + return ( - - {labels.map((label) => ( + + {visibleLabels.map((label) => ( ))} + {hiddenLabels.length > 0 && ( + + {hiddenLabels.map((label) => ( + + {label} + + ))} + + } + > + + + )} ); } @@ -128,7 +169,7 @@ function buildColumns( flex: 0.9, valueGetter: (_value, row) => row.neededForRoleLabels.join(', '), renderCell: ({ row }) => ( - + ), }, { From 3617afedbcb0e66c193ac8e9bfd909e9bed212bc Mon Sep 17 00:00:00 2001 From: PabloDinella Date: Wed, 29 Jul 2026 12:07:47 -0300 Subject: [PATCH 2/3] Show needed role tooltip on chip list --- .../src/Families/ApprovalsDataGridV2.tsx | 63 +++++++++++-------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/src/caretogether-pwa/src/Families/ApprovalsDataGridV2.tsx b/src/caretogether-pwa/src/Families/ApprovalsDataGridV2.tsx index 770f7009..ee155a39 100644 --- a/src/caretogether-pwa/src/Families/ApprovalsDataGridV2.tsx +++ b/src/caretogether-pwa/src/Families/ApprovalsDataGridV2.tsx @@ -29,11 +29,28 @@ function OverflowRoleChipList({ labels }: { labels: string[] }) { return -; } + const hasRoleTooltip = labels.length > 1; const visibleLabels = labels.slice(0, MAX_VISIBLE_ROLE_CHIPS); const hiddenLabels = labels.slice(MAX_VISIBLE_ROLE_CHIPS); + const roleTooltipTitle = ( + + {labels.map((label) => ( + + {label} + + ))} + + ); - return ( + const chipList = ( {visibleLabels.map((label) => ( @@ -53,34 +71,27 @@ function OverflowRoleChipList({ labels }: { labels: string[] }) { /> ))} {hiddenLabels.length > 0 && ( - - {hiddenLabels.map((label) => ( - - {label} - - ))} - - } - > - - + )} ); + + if (!hasRoleTooltip) { + return chipList; + } + + return ( + + {chipList} + + ); } function AppliesToChips({ row }: { row: ApprovalLedgerRow }) { From b83a92750aa04538f1458c52b2fa53515ff40558 Mon Sep 17 00:00:00 2001 From: PabloDinella Date: Wed, 29 Jul 2026 13:28:56 -0300 Subject: [PATCH 3/3] Show hidden role count by cell width --- .../src/Families/ApprovalsDataGridV2.tsx | 187 ++++++++++++++++-- 1 file changed, 167 insertions(+), 20 deletions(-) diff --git a/src/caretogether-pwa/src/Families/ApprovalsDataGridV2.tsx b/src/caretogether-pwa/src/Families/ApprovalsDataGridV2.tsx index ee155a39..2ba94a7d 100644 --- a/src/caretogether-pwa/src/Families/ApprovalsDataGridV2.tsx +++ b/src/caretogether-pwa/src/Families/ApprovalsDataGridV2.tsx @@ -1,7 +1,7 @@ import ChevronRightIcon from '@mui/icons-material/ChevronRight'; import { Box, Chip, Tooltip, Typography, useTheme } from '@mui/material'; import { DataGrid, GridColDef, GridToolbar } from '@mui/x-data-grid'; -import { useRef } from 'react'; +import { useCallback, useLayoutEffect, useRef, useState } from 'react'; import { useUserLookup } from '../Model/DirectoryModel'; import { PersonName } from './PersonName'; import type { ApprovalLedgerRow } from './approvalLedgerViewModel'; @@ -22,16 +22,120 @@ type ApprovalsDataGridV2Props = { rows: ApprovalLedgerRow[]; }; -const MAX_VISIBLE_ROLE_CHIPS = 3; - function OverflowRoleChipList({ labels }: { labels: string[] }) { + const chipListRef = useRef(null); + const chipStripRef = useRef(null); + const moreIndicatorMeasurementsRef = useRef(null); + const moreIndicatorRef = useRef(null); + const [widthHiddenLabelCount, setWidthHiddenLabelCount] = useState(0); + + const measureWidthOverflow = useCallback(() => { + const chipList = chipListRef.current; + const chipStrip = chipStripRef.current; + const moreIndicatorMeasurements = moreIndicatorMeasurementsRef.current; + + if (!chipList || !chipStrip || !moreIndicatorMeasurements) { + return; + } + + const chipListWidth = chipList.clientWidth; + const chipWidths = Array.from(chipStrip.children).flatMap((child) => + child instanceof HTMLElement ? [child.offsetWidth] : [] + ); + const gap = parseFloat(window.getComputedStyle(chipList).columnGap) || 0; + const totalChipWidth = + chipWidths.reduce((total, width) => total + width, 0) + + Math.max(0, chipWidths.length - 1) * gap; + + if (totalChipWidth <= Math.ceil(chipListWidth)) { + setWidthHiddenLabelCount(0); + return; + } + + const moreIndicatorWidth = (hiddenLabelCount: number) => { + const measurement = moreIndicatorMeasurements.querySelector( + `[data-hidden-label-count="${hiddenLabelCount}"]` + ); + + if (!(measurement instanceof HTMLElement)) { + return 0; + } + + return measurement.offsetWidth; + }; + const chipWidthSums = chipWidths.reduce( + (widthSums, chipWidth) => [ + ...widthSums, + widthSums[widthSums.length - 1] + chipWidth, + ], + [0] + ); + + for ( + let visibleChipCount = labels.length - 1; + visibleChipCount >= 0; + visibleChipCount -= 1 + ) { + const hiddenLabelCount = labels.length - visibleChipCount; + const visibleChipGapCount = Math.max(0, visibleChipCount - 1); + const indicatorGapCount = visibleChipCount > 0 ? 1 : 0; + const requiredWidth = + chipWidthSums[visibleChipCount] + + visibleChipGapCount * gap + + indicatorGapCount * gap + + moreIndicatorWidth(hiddenLabelCount); + + if (requiredWidth <= Math.ceil(chipListWidth)) { + setWidthHiddenLabelCount(hiddenLabelCount); + return; + } + } + + setWidthHiddenLabelCount(labels.length); + }, [labels.length]); + + useLayoutEffect(() => { + measureWidthOverflow(); + + const chipList = chipListRef.current; + const chipStrip = chipStripRef.current; + + if (!chipList || !chipStrip) { + return; + } + + const animationFrame = window.requestAnimationFrame(measureWidthOverflow); + + if (typeof ResizeObserver === 'undefined') { + window.addEventListener('resize', measureWidthOverflow); + + return () => { + window.cancelAnimationFrame(animationFrame); + window.removeEventListener('resize', measureWidthOverflow); + }; + } + + const resizeObserver = new ResizeObserver(measureWidthOverflow); + resizeObserver.observe(chipList); + resizeObserver.observe(chipStrip); + + if (moreIndicatorRef.current) { + resizeObserver.observe(moreIndicatorRef.current); + } + + return () => { + window.cancelAnimationFrame(animationFrame); + resizeObserver.disconnect(); + }; + }, [labels, measureWidthOverflow]); + if (labels.length === 0) { return -; } const hasRoleTooltip = labels.length > 1; - const visibleLabels = labels.slice(0, MAX_VISIBLE_ROLE_CHIPS); - const hiddenLabels = labels.slice(MAX_VISIBLE_ROLE_CHIPS); + const hiddenLabelCount = widthHiddenLabelCount; + const hasMoreIndicator = hiddenLabelCount > 0; const roleTooltipTitle = ( {labels.map((label) => ( @@ -48,34 +152,77 @@ function OverflowRoleChipList({ labels }: { labels: string[] }) { const chipList = ( - {visibleLabels.map((label) => ( - {label}} - size="small" - variant="outlined" - /> - ))} - {hiddenLabels.length > 0 && ( + + {labels.map((_label, index) => { + const hiddenLabelCount = index + 1; + + return ( + + ); + })} + + + {labels.map((label) => ( + {label} + } + size="small" + sx={{ flex: '0 0 auto' }} + variant="outlined" + /> + ))} + + {hasMoreIndicator && (