From 970364e86919dedfbeb71bd1e1631d2e17e2156d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yhoan=20Alejandro=20Guzm=C3=A1n=20Garc=C3=ADa?= <41337901+yaguzmang@users.noreply.github.com> Date: Tue, 21 Jan 2025 01:28:50 -0500 Subject: [PATCH] 4001 - Datatable use Datagrid - Editable borders corrections (#4230) * 4001 - Move grid utils to DataGrid * 4001 - Correct cell border rendering when adjacent cells are non-editable * 4001 - Rename vars to lastEditableRow and lastEditableCol * 4001 - Put editable border scss vars together --- .../ButtonGridExport/ButtonGridExport.tsx | 4 +- .../DataGrid/DataCell/DataCell.scss | 7 +- .../DataGrid/{ButtonGridExport => }/utils.ts | 76 +++++++++++++------ .../ButtonCopyValues/hooks/useCopyValues.ts | 2 +- .../pages/Section/DataTable/Table/Table.tsx | 2 + .../Table/hooks/useCellBorderCorrection.ts | 51 +++++++++++++ 6 files changed, 112 insertions(+), 30 deletions(-) rename src/client/components/DataGrid/{ButtonGridExport => }/utils.ts (62%) create mode 100644 src/client/pages/Section/DataTable/Table/hooks/useCellBorderCorrection.ts diff --git a/src/client/components/DataGrid/ButtonGridExport/ButtonGridExport.tsx b/src/client/components/DataGrid/ButtonGridExport/ButtonGridExport.tsx index 657cf488ef..7bb52e1bef 100644 --- a/src/client/components/DataGrid/ButtonGridExport/ButtonGridExport.tsx +++ b/src/client/components/DataGrid/ButtonGridExport/ButtonGridExport.tsx @@ -4,10 +4,10 @@ import { CSVLink } from 'react-csv' import { useIsDataLocked } from 'client/store/ui/dataLock' import { useIsPrintRoute } from 'client/hooks/useIsRoute' import { ButtonProps, useButtonClassName } from 'client/components/Buttons/Button' +import { getDataGridData } from 'client/components/DataGrid/utils' import Icon from 'client/components/Icon' import { useFilename } from './hooks/useFilename' -import * as Utils from './utils' type Props = Pick & { disabled?: boolean @@ -35,7 +35,7 @@ const ButtonGridExport: React.FC = (props) => { data={data} filename={`${filename}.csv`} onClick={(_, done) => { - setData(Utils.getDataGridData(gridRef.current)) + setData(getDataGridData(gridRef.current)) done() }} target="_blank" diff --git a/src/client/components/DataGrid/DataCell/DataCell.scss b/src/client/components/DataGrid/DataCell/DataCell.scss index 17133112ab..63f94b934e 100644 --- a/src/client/components/DataGrid/DataCell/DataCell.scss +++ b/src/client/components/DataGrid/DataCell/DataCell.scss @@ -79,10 +79,12 @@ $highlight-top-bottom: inset 0 1px $ui-accent, inset 0 -1px $ui-accent; border-left: $border-editable; border-top: $border-editable; - &.lastCol { + &.lastCol, + &.lastEditableCol { border-right: $border-editable; } + &.lastEditableRow, &.lastRow { border-bottom: $border-editable; } @@ -156,7 +158,8 @@ $highlight-top-bottom: inset 0 1px $ui-accent, inset 0 -1px $ui-accent; border-left: unset; border-right: $border-editable; - &.lastCol { + &.lastCol, + &.lastEditableCol { border-left: $border-editable; } } diff --git a/src/client/components/DataGrid/ButtonGridExport/utils.ts b/src/client/components/DataGrid/utils.ts similarity index 62% rename from src/client/components/DataGrid/ButtonGridExport/utils.ts rename to src/client/components/DataGrid/utils.ts index 4988774311..7b3a9268df 100644 --- a/src/client/components/DataGrid/ButtonGridExport/utils.ts +++ b/src/client/components/DataGrid/utils.ts @@ -64,10 +64,10 @@ const _getCellSpans = (props: { return { colSpan, rowSpan } } -type DataRow = Array -type TableData = Array +type DataRow = Array +type GridData = Array> -export const getDataGridData = (grid: HTMLDivElement): TableData => { +export const getDataGridElementMatrix = (grid: HTMLDivElement): GridData => { if (!grid) { return [] } @@ -75,14 +75,14 @@ export const getDataGridData = (grid: HTMLDivElement): TableData => { const columnCount = gridStyle.getPropertyValue('grid-template-columns').split(' ').length const rowCount = gridStyle.getPropertyValue('grid-template-rows').split(' ').length - const data: TableData = Array.from({ length: rowCount }, () => new Array(columnCount).fill(null)) + const elementMatrix: GridData = Array.from({ length: rowCount }, () => new Array(columnCount).fill(null)) const cells = Array.from(grid.children) const findNextAvailablePosition = (): { row: number; col: number } | null => { - for (let row = 0; row < data.length; row += 1) { - for (let col = 0; col < data[row].length; col += 1) { - if (Objects.isNil(data[row][col])) { + for (let row = 0; row < elementMatrix.length; row += 1) { + for (let col = 0; col < elementMatrix[row].length; col += 1) { + if (Objects.isNil(elementMatrix[row][col])) { return { col, row } } } @@ -95,31 +95,57 @@ export const getDataGridData = (grid: HTMLDivElement): TableData => { if (Objects.isNil(nextAvailablePosition)) return const { col, row } = nextAvailablePosition - const isNoticeMessage = cell.classList.contains('table-grid__notice-message-cell') - - let cellContent = _getElementText(cell as HTMLElement) - const spaceFreeContent = cellContent.replace(/\s/g, '') - cellContent = - Number.isNaN(Number.parseFloat(spaceFreeContent)) || Number.isNaN(Number(spaceFreeContent)) - ? cellContent - : spaceFreeContent - const { rowSpan, colSpan } = _getCellSpans({ cell, columnCount, rowCount }) for (let r = row; r < row + rowSpan && r < rowCount; r += 1) { for (let c = col; c < col + colSpan && c < columnCount; c += 1) { - // Place notice message only in the first cell of the row - if (isNoticeMessage) { - if (r === row && c === col) { - data[r][c] = cellContent - } else { - data[r][c] = '' - } + elementMatrix[r][c] = cell + } + } + }) + + return elementMatrix +} + +export const getDataGridData = (grid: HTMLDivElement): GridData => { + if (!grid) { + return [] + } + const elementMatrix = getDataGridElementMatrix(grid) + + const rowCount = elementMatrix.length + const columnCount = elementMatrix[0]?.length || 0 + const data: GridData = Array.from({ length: rowCount }, () => new Array(columnCount).fill(null)) + + elementMatrix.forEach((row, rowIndex) => { + let noticeMessageAdded = false + + row.forEach((cell, colIndex) => { + if (Objects.isEmpty(cell)) { + data[rowIndex][colIndex] = '' + return + } + + const isNoticeMessage = cell.classList.contains('table-grid__notice-message-cell') + + let cellContent = _getElementText(cell as HTMLElement) + const spaceFreeContent = cellContent.replace(/\s/g, '') + cellContent = + Number.isNaN(Number.parseFloat(spaceFreeContent)) || Number.isNaN(Number(spaceFreeContent)) + ? cellContent + : spaceFreeContent + + if (isNoticeMessage) { + if (!noticeMessageAdded) { + data[rowIndex][colIndex] = cellContent + noticeMessageAdded = true } else { - data[r][c] = cellContent + data[rowIndex][colIndex] = '' } + } else { + data[rowIndex][colIndex] = cellContent } - } + }) }) return data diff --git a/src/client/pages/Section/DataTable/Table/ButtonCopyValues/hooks/useCopyValues.ts b/src/client/pages/Section/DataTable/Table/ButtonCopyValues/hooks/useCopyValues.ts index a72d0e4343..4df495a719 100644 --- a/src/client/pages/Section/DataTable/Table/ButtonCopyValues/hooks/useCopyValues.ts +++ b/src/client/pages/Section/DataTable/Table/ButtonCopyValues/hooks/useCopyValues.ts @@ -7,7 +7,7 @@ import { TableNames } from 'meta/assessment' import { useUser } from 'client/store/user' import { useCycleRouteParams } from 'client/hooks/useRouteParams' -import { getDataGridData } from 'client/components/DataGrid/ButtonGridExport/utils' +import { getDataGridData } from 'client/components/DataGrid/utils' import { CopyValuesProps } from 'client/pages/Section/DataTable/Table/ButtonCopyValues/types' // Cycle -> Table name -> Variables to be coppied to clipboard diff --git a/src/client/pages/Section/DataTable/Table/Table.tsx b/src/client/pages/Section/DataTable/Table/Table.tsx index c70a575721..9f009d7752 100644 --- a/src/client/pages/Section/DataTable/Table/Table.tsx +++ b/src/client/pages/Section/DataTable/Table/Table.tsx @@ -17,6 +17,7 @@ import GridHeadCell from 'client/pages/Section/DataTable/Table/GridHeadCell' import RowData from 'client/pages/Section/DataTable/Table/RowData' import RowNoticeMessage from 'client/pages/Section/DataTable/Table/RowNoticeMessage' +import { useCellBorderCorrection } from './hooks/useCellBorderCorrection' import { useGridTemplateColumns } from './hooks/useGridTemplateColumns' import { useParsedTable } from './hooks/useParsedTable' import DataValidations from './DataValidations' @@ -46,6 +47,7 @@ const Table: React.FC = (props) => { }) const gridTemplateColumns = useGridTemplateColumns({ headers, table }) + useCellBorderCorrection({ disabled, gridRef, rowsData, rowsHeader }) const canViewReview = useCanViewReview(sectionName) const withActions = withReview && canViewReview diff --git a/src/client/pages/Section/DataTable/Table/hooks/useCellBorderCorrection.ts b/src/client/pages/Section/DataTable/Table/hooks/useCellBorderCorrection.ts new file mode 100644 index 0000000000..8c97a074e1 --- /dev/null +++ b/src/client/pages/Section/DataTable/Table/hooks/useCellBorderCorrection.ts @@ -0,0 +1,51 @@ +import { MutableRefObject, useEffect } from 'react' + +import { Objects } from 'utils/objects' + +import { Row } from 'meta/assessment' + +import { getDataGridElementMatrix } from 'client/components/DataGrid/utils' + +type Props = { + disabled: boolean + gridRef: MutableRefObject + rowsData: Array + rowsHeader: Array +} + +export const useCellBorderCorrection = (props: Props) => { + const { disabled, gridRef, rowsData, rowsHeader } = props + + useEffect(() => { + if (!gridRef?.current) return + + if (disabled) return + + const dataGridElementMatrix = getDataGridElementMatrix(gridRef.current) + + dataGridElementMatrix.forEach((row, rowIndex) => { + row.forEach((cell, colIndex) => { + if (Objects.isEmpty(cell)) return + + const isEditable = cell.classList.contains('editable') + + if (!isEditable) return + + const rightCell = row[colIndex + 1] + const bottomCell = dataGridElementMatrix[rowIndex + 1]?.[colIndex] + + if (!Objects.isEmpty(rightCell) && !rightCell.classList.contains('editable')) { + if (!rightCell.classList.contains('actions')) { + cell.classList.add('lastEditableCol') + } + } + + if (!Objects.isEmpty(bottomCell) && !bottomCell.classList.contains('editable')) { + if (!bottomCell.classList.contains('actions')) { + cell.classList.add('lastEditableRow') + } + } + }) + }) + }, [disabled, gridRef, rowsData.length, rowsHeader.length]) +}