From 64ac4e020b5972dc114c04715a8a566ca2b05bbe Mon Sep 17 00:00:00 2001 From: Jialecl Date: Wed, 24 Jul 2024 10:26:46 +0200 Subject: [PATCH 01/20] Data grid added --- package-lock.json | 21 + packages/lib/package.json | 1 + .../lib/src/datagrid/Datagrid.stories.tsx | 162 ++++++++ packages/lib/src/datagrid/Datagrid.tsx | 307 +++++++++++++++ packages/lib/src/datagrid/types.ts | 68 ++++ packages/lib/src/datagrid/utils.tsx | 369 ++++++++++++++++++ packages/lib/src/index.ts | 2 + 7 files changed, 930 insertions(+) create mode 100644 packages/lib/src/datagrid/Datagrid.stories.tsx create mode 100644 packages/lib/src/datagrid/Datagrid.tsx create mode 100644 packages/lib/src/datagrid/types.ts create mode 100644 packages/lib/src/datagrid/utils.tsx diff --git a/package-lock.json b/package-lock.json index bfc876f64..fbe2eea2b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10758,6 +10758,14 @@ "node": ">=0.10.0" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -22507,6 +22515,18 @@ "react": "^16.3.0 || ^17.0.1 || ^18.0.0" } }, + "node_modules/react-data-grid": { + "version": "7.0.0-beta.44", + "resolved": "https://registry.npmjs.org/react-data-grid/-/react-data-grid-7.0.0-beta.44.tgz", + "integrity": "sha512-VTql8VPBJEf7E+zkrqYW+jaQT1/m47dxigWzPq59QESJ8LhU59kHyzIx06BUpjdZKdK/tzbWaw2yyJpKoNnt5g==", + "dependencies": { + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^18.0", + "react-dom": "^18.0" + } + }, "node_modules/react-docgen": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-7.0.3.tgz", @@ -27768,6 +27788,7 @@ "@radix-ui/react-tooltip": "^1.1.0", "color": "^4.2.3", "dayjs": "^1.11.11", + "react-data-grid": "^7.0.0-beta.44", "slugify": "^1.6.6" }, "devDependencies": { diff --git a/packages/lib/package.json b/packages/lib/package.json index e6cc2e4ca..081ac0abc 100644 --- a/packages/lib/package.json +++ b/packages/lib/package.json @@ -39,6 +39,7 @@ "@radix-ui/react-tooltip": "^1.1.0", "color": "^4.2.3", "dayjs": "^1.11.11", + "react-data-grid": "^7.0.0-beta.44", "slugify": "^1.6.6" }, "devDependencies": { diff --git a/packages/lib/src/datagrid/Datagrid.stories.tsx b/packages/lib/src/datagrid/Datagrid.stories.tsx new file mode 100644 index 000000000..945064c73 --- /dev/null +++ b/packages/lib/src/datagrid/Datagrid.stories.tsx @@ -0,0 +1,162 @@ +import Title from "../../.storybook/components/Title"; +import ExampleContainer from "../../.storybook/components/ExampleContainer"; +import DxcDataGrid from "./Datagrid"; +import DxcContainer from "../container/Container"; +import { GridColumn } from "./types"; +import { useState } from "react"; + +export default { + title: "Datagrid", + component: DxcDataGrid, +}; + +const columns: GridColumn[] = [ + { + key: "id", + name: "ID", + resizable: true, + sortable: true, + draggable: false, + alignment: "left", + }, + { + key: "task", + name: "Title", + resizable: true, + sortable: true, + draggable: true, + textEditable: true, + alignment: "left", + }, + { + key: "complete", + name: " % Complete", + resizable: true, + sortable: true, + draggable: true, + alignment: "right", + }, + { + key: "priority", + name: "Priority", + resizable: true, + sortable: true, + draggable: true, + alignment: "center", + }, +]; + +const expandableRows = [ + { + id: 1, + task: "Task 1", + complete: 46, + priority: "High", + issueType: "Bug", + expandedContent: Custom content 1, + expandedContentHeight: 470, + }, + { + id: 2, + task: "Task 2", + complete: 51, + priority: "High", + issueType: "Epic", + expandedContent: Custom content 1, + }, + { + id: 3, + task: "Task 3", + complete: 40, + priority: "High", + issueType: "Improvement", + expandedContent: Custom content 1, + }, + { + id: 4, + task: "Task 4", + complete: 10, + priority: "High", + issueType: "Improvement", + expandedContent: Custom content 1, + }, + { + id: 5, + task: "Task 5", + complete: 68, + priority: "High", + issueType: "Improvement", + expandedContent: Custom content 1, + }, + { + id: 6, + task: "Task 6", + complete: 37, + priority: "High", + issueType: "Improvement", + expandedContent: Custom content 1, + }, + { + id: 7, + task: "Task 7", + complete: 73, + priority: "Medium", + issueType: "Story", + expandedContent: Custom content 1, + }, + { + id: 8, + task: "Task 8", + complete: 27, + priority: "Medium", + issueType: "Story", + expandedContent: Custom content 1, + }, + { + id: 9, + task: "Task 9", + complete: 36, + priority: "Critical", + issueType: "Epic", + expandedContent: Custom content 1, + }, +]; + +export const Chromatic = () => { + const [selectedRows, setSelectedRows] = useState((): Set => new Set()); + return ( + <> + + + <DxcDataGrid columns={columns} rows={expandableRows} uniqueRowId="id" /> + </ExampleContainer> + <ExampleContainer> + <Title title="Expandable" theme="light" level={4} /> + <DxcDataGrid columns={columns} rows={expandableRows} uniqueRowId="id" expandable /> + </ExampleContainer> + <ExampleContainer> + <Title title="Selectable" theme="light" level={4} /> + <DxcDataGrid + columns={columns} + rows={expandableRows} + uniqueRowId="id" + selectable + selectedRows={selectedRows} + onSelectRows={setSelectedRows} + /> + </ExampleContainer> + <ExampleContainer> + <Title title="Selectable" theme="light" level={4} /> + <DxcDataGrid + columns={columns} + rows={expandableRows} + uniqueRowId="id" + expandable + selectable + selectedRows={selectedRows} + onSelectRows={setSelectedRows} + /> + </ExampleContainer> + </> + ); +}; diff --git a/packages/lib/src/datagrid/Datagrid.tsx b/packages/lib/src/datagrid/Datagrid.tsx new file mode 100644 index 000000000..194e5969e --- /dev/null +++ b/packages/lib/src/datagrid/Datagrid.tsx @@ -0,0 +1,307 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { useEffect, useMemo, useState } from "react"; +import DataGridPropsType, { HierarchyGridRow, GridRow, ExpandableGridRow } from "./types"; +import DataGrid, { Column, SortColumn } from "react-data-grid"; +import "react-data-grid/lib/styles.css"; + +import { + convertToRDGColumns, + rowKeyGetter, + sortRows, + renderSortStatus, + addRow, + renderCheckbox, + renderHeaderCheckbox, + sortHierarchyRows, + deleteRow, + renderHierarchyTrigger, +} from "./utils"; +import styled from "styled-components"; +import DxcActionIcon from "../action-icon/ActionIcon"; + +const DxcDataGrid = ({ + columns, + rows, + selectable, + expandable, + onSelectRows, + selectedRows, + uniqueRowId, + summaryRow, +}: DataGridPropsType): JSX.Element => { + const [rowsToRender, setRowsToRender] = useState<GridRow[] | HierarchyGridRow[] | ExpandableGridRow[]>(rows); + // Proccess columns prop into usable columns based on other props + const columnsToRender = useMemo(() => { + let expectedColumns = columns + // .filter((column) => visibleColumns.includes(column.name)) + .map((column) => { + // if (!visibleColumns.includes(column.name)) + // return { + // key: column.key, + // name: column.name, + // colSpan() { + // return 0; + // }, + // }; + + return convertToRDGColumns(column, summaryRow); + }); + if (expandable) { + expectedColumns = [ + { + key: "expanded", + name: "", + maxWidth: 50, + width: 50, + colSpan(args) { + return args.type === "ROW" && args.row.isExpandedChildContent + ? columns.length + (selectable ? 2 : 1) + : undefined; + }, + renderCell({ row }) { + if (row.isExpandedChildContent) { + // if it is expanded content + return row.expandedChildContent || <></>; + } + // if row has expandable content + return ( + <ActionContainer> + <DxcActionIcon + icon={row.contentIsExpanded ? "arrow_drop_down" : "arrow_right"} + title="icon" + onClick={() => { + row.contentIsExpanded = !row.contentIsExpanded; + if (row.contentIsExpanded) { + const rowIndex = rowsToRender.findIndex((rowToRender) => row === rowToRender); + setRowsToRender((rows) => { + const newRows = [...rows]; + addRow(newRows, rowIndex + 1, { + isExpandedChildContent: row.contentIsExpanded, + uniqueRowId: rowKeyGetter(row, uniqueRowId) + "_expanded", + expandedChildContent: row.expandedContent, + triggerRowKey: rowKeyGetter(row, uniqueRowId), + expandedContentHeight: row.expandedContentHeight, + }); + return newRows; + }); + } else { + const rowIndex = rowsToRender.findIndex((rowToRender) => row === rowToRender); + setRowsToRender((rows) => { + const newRows = [...rows]; + deleteRow(newRows, rowIndex + 1); + return newRows; + }); + } + }} + /> + </ActionContainer> + ); + }, + }, + ...expectedColumns, + ]; + } + if (!expandable && rows.some((row) => Array.isArray(row.childRows) && row.childRows.length > 0) && uniqueRowId) { + // only the first column will be clickable and will expand the rows + const firstColumnKey = expectedColumns[0].key; + expectedColumns[0] = { + ...expectedColumns[0], + renderCell({ row }) { + if (row.childRows?.length) { + return ( + <HierarchyContainer level={row.rowLevel || 0}> + {renderHierarchyTrigger(rowsToRender, row, uniqueRowId, firstColumnKey, (rows) => + setRowsToRender(rows) + )} + </HierarchyContainer> + ); + } + // TODO: styled component that adds left padding based on row.rowLevel * paddingValue + return ( + <HierarchyContainer level={row.rowLevel || 0} className="ellipsis-cell"> + {row[firstColumnKey]} + </HierarchyContainer> + ); + }, + }; + } + if (selectable) { + expectedColumns = [ + { + key: "selected", + name: "", + maxWidth: 50, + width: 50, + renderCell({ row }) { + if (!row.isExpandedChildContent) { + return ( + <ActionContainer>{renderCheckbox(rows, row, uniqueRowId, selectedRows, onSelectRows)}</ActionContainer> + ); + } + }, + renderHeaderCell: () => { + return ( + <ActionContainer>{renderHeaderCheckbox(rows, uniqueRowId, selectedRows, onSelectRows)}</ActionContainer> + ); + }, + }, + ...expectedColumns, + ]; + } + return expectedColumns; + }, [selectable, expandable, columns, rowsToRender, onSelectRows, rows, summaryRow, uniqueRowId, selectedRows]); + // array with the order of the columns + const [columnsOrder, setColumnsOrder] = useState((): number[] => columnsToRender.map((_, index) => index)); + const [sortColumns, setSortColumns] = useState<readonly SortColumn[]>([]); + // const [visibleColumns, setVisibleColumns] = useState( + // columnsNamesIntoOptions(columns).map((visibleColumn) => { + // return visibleColumn.value; + // }) + // ); + + useEffect(() => { + setColumnsOrder(columnsToRender.map((_, index) => index)); + }, [columnsToRender]); + + const reorderedColumns = useMemo(() => { + // Array ordered by columnsOrder + return columnsOrder.map((index) => columnsToRender[index]); + }, [columnsOrder, columnsToRender]); + + const onColumnsReorder = (sourceKey: string, targetKey: string) => { + setColumnsOrder((columnsOrder) => { + const sourceColumnOrderIndex = columnsOrder.findIndex((index) => columnsToRender[index].key === sourceKey); + const targetColumnOrderIndex = columnsOrder.findIndex((index) => columnsToRender[index].key === targetKey); + const newColumnsOrder = columnsOrder.slice(); + newColumnsOrder.splice(sourceColumnOrderIndex, 1); + newColumnsOrder.splice(targetColumnOrderIndex, 0, columnsOrder[sourceColumnOrderIndex]); + return newColumnsOrder; + }); + }; + + const onRowsChange = (newRows: { [key: string]: string | number | React.ReactNode }[]) => { + // call function to change rows, like when they have been edited + console.log("new rows: "); + console.log(newRows); + }; + + const sortedRows = useMemo((): readonly GridRow[] => { + if (expandable && sortColumns.length >= 0) { + const sortedRows = sortRows( + rowsToRender.filter((row) => !row.isExpandedChildContent), + sortColumns + ); + rowsToRender + .filter((row) => row.isExpandedChildContent) + .map((expandedRow) => + addRow( + sortedRows, + sortedRows.findIndex((trigger) => rowKeyGetter(trigger, uniqueRowId) === expandedRow.triggerRowKey) + 1, + expandedRow + ) + ); + return sortedRows; + } else if (!expandable && sortColumns.length >= 0) { + if (uniqueRowId) return sortHierarchyRows(rowsToRender, sortColumns, uniqueRowId); + } + return rowsToRender; + }, [expandable, rowsToRender, sortColumns, uniqueRowId]); + + return ( + <DataGridContainer> + {/* {columnsVisibilityFilter && ( + <DxcSelect + multiple + options={columnsNamesIntoOptions(columns)} + defaultValue={visibleColumns} + onChange={(event) => setVisibleColumns(event.value)} + /> + )} */} + <DataGrid + columns={reorderedColumns} + rows={sortedRows} + onColumnsReorder={onColumnsReorder} + onRowsChange={onRowsChange} + renderers={{ renderSortStatus }} + sortColumns={sortColumns} + onSortColumnsChange={setSortColumns} + rowKeyGetter={(row) => uniqueRowId && rowKeyGetter(row, uniqueRowId)} + rowHeight={(row) => { + if ( + row.isExpandedChildContent && + typeof row.expandedContentHeight === "number" && + row.expandedContentHeight > 0 + ) { + return row.expandedContentHeight; + } + return 36; + }} + selectedRows={selectedRows} + bottomSummaryRows={summaryRow ? [summaryRow] : undefined} + headerRowHeight={48} + className="fill-grid" + /> + </DataGridContainer> + ); +}; + +const ActionContainer = styled.div` + display: flex; + height: 100%; + align-items: center; + justify-content: center; + font-size: 24px; + width: 100%; +`; + +const HierarchyContainer = styled.div<{ + level: number; +}>` + padding-left: calc(10px * ${(props) => props.level}); + button { + padding: 0px; + } +`; + +const DataGridContainer = styled.div` + width: 100%; + .rdg { + border-radius: 4px; + max-height: 100%; + } + .rdg-header-row { + background-color: rgb(95, 36, 159); + .rdg-cell { + font-family: "Open Sans", sans-serif; + font-size: 0.875rem; + font-style: normal; + font-weight: 400; + color: rgb(255, 255, 255); + text-transform: none; + } + .rdg-cell:first-child { + border-top-left-radius: 4px; + } + .rdg-cell:last-child { + border-top-right-radius: 4px; + } + } + .ellipsis-cell { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + .rdg-cell { + } + .align-left { + text-align: left; + } + .align-center { + text-align: center; + } + .align-right { + text-align: right; + } +`; + +export default DxcDataGrid; diff --git a/packages/lib/src/datagrid/types.ts b/packages/lib/src/datagrid/types.ts new file mode 100644 index 000000000..d5b1ce78e --- /dev/null +++ b/packages/lib/src/datagrid/types.ts @@ -0,0 +1,68 @@ +import React from "react"; + +export type GridColumn = { + key: string; + name: string; + resizable?: boolean; + sortable?: boolean; + draggable?: boolean; + textEditable?: boolean; + summaryKeyToRender?: string; + alignment?: "left" | "right" | "center"; +}; + +export type GridRow = { + [key: string]: React.ReactNode | undefined; +}; + +export type HierarchyGridRow = GridRow & { + childRows?: HierarchyGridRow[]; + rowLevel?: number; +}; + +export type ExpandableGridRow = GridRow & { + expandedContent?: React.ReactNode; + expandedContentHeight?: number; +}; + +export type ExpandableRows = { + columns: GridColumn[]; + rows: ExpandableGridRow[]; + expandable: true; + uniqueRowId: string; + summaryRow?: GridRow; +}; + +export type HierarchyRows = { + columns: GridColumn[]; + rows: HierarchyGridRow[]; + uniqueRowId: string; + expandable?: false; + summaryRow?: GridRow; +}; + +export type SelectableGridProps = + | { + selectable: true; + selectedRows: Set<string | number>; + onSelectRows: (selectedRows: Set<number | string>) => void; + uniqueRowId: string; + } + | { + selectable?: false; + selectedRows?: never; + onSelectRows?: never; + uniqueRowId?: string; + }; + +type Props = + | ({ + columns: GridColumn[]; + rows: GridRow[]; + expandable?: never; + summaryRow?: GridRow; + } & SelectableGridProps) + | (ExpandableRows & SelectableGridProps) + | (HierarchyRows & SelectableGridProps); + +export default Props; diff --git a/packages/lib/src/datagrid/utils.tsx b/packages/lib/src/datagrid/utils.tsx new file mode 100644 index 000000000..019532c3f --- /dev/null +++ b/packages/lib/src/datagrid/utils.tsx @@ -0,0 +1,369 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import DxcCheckbox from "../checkbox/Checkbox"; +import { HalstackProvider } from "../HalstackContext"; +import { GridColumn, HierarchyGridRow, GridRow, ExpandableGridRow } from "./types"; +import { Column, RenderSortStatusProps, SortColumn, textEditor } from "react-data-grid"; + +const checkboxTheme = { + checkbox: { + backgroundColorChecked: "#ffffff", + hoverBackgroundColorChecked: "#e6e6e6", + disabledBackgroundColorChecked: "#999999", + readOnlyBackgroundColorChecked: "#999999", + hoverReadOnlyBackgroundColorChecked: "#808080", + borderColor: "#ffffff", + hoverBorderColor: "#ffffff", + disabledBorderColor: "#999999", + readOnlyBorderColor: "#999999", + hoverReadOnlyBorderColor: "#808080", + checkColor: "#5f249f", + disabledCheckColor: "#ffffff", + readOnlyCheckColor: "#ffffff", + fontFamily: "Open Sans, sans-serif", + fontSize: "0.875rem", + fontWeight: "400", + fontColor: "#ffffff", + disabledFontColor: "#999999", + focusColor: "#0095ff", + checkLabelSpacing: "8px", + }, +}; + +// Column<any, any> type added to avoid conflicts with SelectColumn typing from RDG +export const convertToRDGColumns = (gridColumn: GridColumn, summaryRow?: GridRow): Column<any, any> => { + return { + key: gridColumn.key, + name: gridColumn.name, + resizable: gridColumn.resizable, + sortable: gridColumn.sortable, + draggable: gridColumn.draggable, + editable: gridColumn.textEditable, + headerCellClass: gridColumn.alignment ? `align-${gridColumn.alignment}` : `align-left`, + renderEditCell: gridColumn.textEditable ? textEditor : undefined, + renderCell: ({ row }) => { + return ( + <div className={`ellipsis-cell ${gridColumn.alignment ? "align-" + gridColumn.alignment : "align-left"}`}> + {row[gridColumn.key]} + </div> + ); + }, + renderSummaryCell: () => { + return gridColumn.summaryKeyToRender ? ( + <div className={`ellipsis-cell ${gridColumn.alignment ? "align-" + gridColumn.alignment : "align-left"}`}> + {summaryRow?.[gridColumn.summaryKeyToRender]} + </div> + ) : undefined; + }, + }; +}; + +export const renderSortStatus = ({ sortDirection }: RenderSortStatusProps) => { + //TODO: change to DxcIcons + return <>{sortDirection !== undefined ? (sortDirection === "ASC" ? "\u2B9D" : "\u2B9F") : null}</>; +}; + +export const renderHierarchyTrigger = ( + rows: HierarchyGridRow[], + triggerRow: HierarchyGridRow, + uniqueRowId: string, + columnKey: string, + onRowsToRender: (rows: HierarchyGridRow[]) => void +) => { + return ( + <button + onClick={() => { + let newRowsToRender = [...rows]; + if (!triggerRow.visibleChildren) { + const rowIndex = rows.findIndex((rowToRender) => triggerRow === rowToRender); + triggerRow.childRows?.map((childRow: HierarchyGridRow, index: number) => { + childRow.rowLevel = triggerRow.rowLevel ? triggerRow.rowLevel + 1 : 1; + childRow.parentKey = rowKeyGetter(triggerRow, uniqueRowId); + addRow(newRowsToRender, rowIndex + 1 + index, childRow); + }); + } else { + // The children of the row that is being collapsed are added to an array + const rowsToRemove: HierarchyGridRow[] = [ + ...rows.filter( + (rowToRender) => rowToRender.parentKey && rowToRender.parentKey === rowKeyGetter(triggerRow, uniqueRowId) + ), + ]; + // The children are checked if any of them has any other children of their own + const rowsToCheck = [...rowsToRemove]; + while (rowsToCheck.length > 0) { + const currentRow = rowsToCheck.pop(); + const childRows = currentRow?.visibleChildren && currentRow?.childRows ? currentRow.childRows : []; + + rowsToRemove.push(...childRows); + rowsToCheck.push(...childRows); + } + newRowsToRender = rows.filter( + (row) => + !rowsToRemove + .map((rowToRemove) => { + if (rowToRemove.visibleChildren) { + rowToRemove.visibleChildren = false; + } + return rowKeyGetter(rowToRemove, uniqueRowId); + }) + .includes(rowKeyGetter(row, uniqueRowId)) + ); + } + triggerRow.visibleChildren = !triggerRow.visibleChildren; + onRowsToRender(newRowsToRender); + }} + > + {triggerRow[columnKey]} + </button> + ); +}; + +export const renderCheckbox = ( + rows: GridRow[] | HierarchyGridRow[] | ExpandableGridRow[], + row: GridRow | HierarchyGridRow | ExpandableGridRow, + uniqueRowId: string, + selectedRows: Set<number | string>, + onSelectRows: (selectedRows: Set<number | string>) => void +) => { + return ( + <DxcCheckbox + checked={selectedRows.has(rowKeyGetter(row, uniqueRowId))} + onChange={(checked) => { + const selected = new Set(selectedRows); + checked ? selected.add(rowKeyGetter(row, uniqueRowId)) : selected.delete(rowKeyGetter(row, uniqueRowId)); + if (row.childRows && Array.isArray(row.childRows)) { + getChildrenSelection(row.childRows, uniqueRowId, selected, checked); + } + if (row.parentKey) + getParentSelectedState(rows, rowKeyGetter(row, uniqueRowId), row.parentKey, uniqueRowId, selected, checked); + onSelectRows(selected); + }} + /> + ); +}; + +/** + * Render the specific checkbox in the header + * @param rows + * @param uniqueRowId + * @param selectedRows + * @param onSelectRows + */ +export const renderHeaderCheckbox = ( + rows: GridRow[] | HierarchyGridRow[] | ExpandableGridRow[], + uniqueRowId: string, + selectedRows: Set<number | string>, + onSelectRows: (selectedRows: Set<number | string>) => void +) => { + return ( + <HalstackProvider advancedTheme={checkboxTheme}> + <DxcCheckbox + checked={!rows.some((row) => !selectedRows.has(rowKeyGetter(row, uniqueRowId)))} + onChange={(checked) => { + const selected = new Set<number | string>(); + if (checked) { + rows.map((row) => { + selected.add(rowKeyGetter(row, uniqueRowId)); + if (row.childRows && Array.isArray(row.childRows)) + getChildrenSelection(row.childRows, uniqueRowId, selected, checked); + }); + } + onSelectRows(selected); + }} + /> + </HalstackProvider> + ); +}; + +export const rowKeyGetter = (row: any, uniqueRowId: string) => { + return row[uniqueRowId]; +}; + +export const sortRows = (rows: GridRow[], sortColumns: readonly SortColumn[], reversed?: boolean) => { + return [...rows].sort((a, b) => { + for (const sort of sortColumns) { + const sortValueA = a[sort.columnKey]; + const sortValueB = b[sort.columnKey]; + let compResult = 0; + + if (sortValueA && sortValueB) { + compResult = sortValueA > sortValueB ? 1 : sortValueA < sortValueB ? -1 : 0; + } + + if (compResult !== 0) { + if (reversed) return sort.direction === "ASC" ? -compResult : compResult; + return sort.direction === "ASC" ? compResult : -compResult; + } + } + return 0; + }); +}; + +export const sortHierarchyRows = ( + rows: HierarchyGridRow[], + sortColumns: readonly SortColumn[], + uniqueRowId: string +) => { + const parentsSorted = sortRows( + rows.filter((row) => !row.parentKey), + sortColumns + ); + // check if there are child rows + if (rows.length === parentsSorted.length) return parentsSorted; + else { + let sortedChildren = sortRows( + rows.filter((row) => row.parentKey), + sortColumns, + true + ); + // add children directly under the parent if it is available + while (sortedChildren.length) { + if (uniqueRowId) { + sortedChildren = sortedChildren.reduce( + ( + remainingChilds: GridRow[] | HierarchyGridRow[] | ExpandableGridRow[], + child: GridRow | HierarchyGridRow | ExpandableGridRow + ) => { + const parentIndex = parentsSorted.findIndex( + (parent) => rowKeyGetter(parent, uniqueRowId) === child.parentKey + ); + if (parentIndex >= 0) { + parentsSorted.splice(parentIndex + 1, 0, child); + } else { + remainingChilds.push(child); + } + return remainingChilds; + }, + [] + ); + } + } + return parentsSorted; + } +}; + +// function columnsNamesIntoOptions(columns: GridColumn[]) { +// return columns.map((column) => { +// return { label: column.name, value: column.name }; +// }); +// } + +// export function addUniqueId( +// row: GridRow | HierarchyGridRow | ExpandableGridRow, +// counter: number | string, +// expandable?: boolean +// ) { +// row.uniqueRowId = counter; +// if (!expandable && row.childRows && Array.isArray(row.childRows)) { +// row.childRows.forEach((childRow: HierarchyGridRow, index) => +// addUniqueId(childRow, `${counter}_${index}`) +// ); +// } +// } + +export const addRow = ( + rowList: GridRow[] | HierarchyGridRow[] | ExpandableGridRow[], + index: number, + row: GridRow | HierarchyGridRow | ExpandableGridRow +) => { + rowList.splice(index, 0, row); +}; + +export const deleteRow = (rowList: GridRow[] | HierarchyGridRow[] | ExpandableGridRow[], index: number) => { + rowList.splice(index, 1); +}; + +/** + * @param rowList List of rows in which to look for the row, it will also look for the row in the childRows + * @param uniqueRowId The key that contains the unique value of the row + * @param uniqueRowIdValue Unique value to identify the row + * @returns A copy of the Row + */ +export const rowFinderBasedOnId = ( + rowList: GridRow[] | HierarchyGridRow[] | ExpandableGridRow[], + uniqueRowId: string, + uniqueRowIdValue: React.ReactNode +): GridRow | HierarchyGridRow | ExpandableGridRow | undefined => { + let foundRow: GridRow | HierarchyGridRow | ExpandableGridRow | undefined = undefined; + rowList.forEach((row) => { + if (rowKeyGetter(row, uniqueRowId) === uniqueRowIdValue) { + foundRow = { ...row }; + } + if (row.childRows && Array.isArray(row.childRows) && !foundRow) { + foundRow = rowFinderBasedOnId(row.childRows, uniqueRowId, uniqueRowIdValue); + } + }); + if (foundRow) return foundRow; +}; + +export const getChildrenSelection = ( + rowList: HierarchyGridRow[], + uniqueRowId: string, + selectedRows: Set<number | string>, + checked: boolean +) => { + rowList.forEach((row) => { + if (row.childRows) { + getChildrenSelection(row.childRows, uniqueRowId, selectedRows, checked); + } + if (checked) selectedRows.add(rowKeyGetter(row, uniqueRowId)); + else { + selectedRows.delete(rowKeyGetter(row, uniqueRowId)); + } + }); +}; + +/** + * Check if the parent and its parent should be selected/unselected + * @param rowList + * @param uniqueRowKeyValue Unique value of the selected row + * @param parentKeyValue Unique value of the parent Row + * @param uniqueRowId Key where the unique value is located + * @param changedRows + * @param checkedStateToMatch + */ +export const getParentSelectedState = ( + rowList: HierarchyGridRow[], + uniqueRowKeyValue: React.ReactNode, + parentKeyValue: React.ReactNode, + uniqueRowId: string, + selectedRows: Set<number | string>, + checkedStateToMatch: boolean +) => { + const parentRow = rowFinderBasedOnId(rowList, uniqueRowId, parentKeyValue); + // we are unselecting or any of the other childRows is unselected + if ( + !checkedStateToMatch || + (parentRow?.childRows && + Array.isArray(parentRow.childRows) && + parentRow.childRows + .filter((row) => rowKeyGetter(row, uniqueRowId) !== uniqueRowKeyValue) + .some((row) => !selectedRows.has(rowKeyGetter(row, uniqueRowId)))) + ) { + if (selectedRows.has(rowKeyGetter(parentRow, uniqueRowId))) { + selectedRows.delete(rowKeyGetter(parentRow, uniqueRowId)); + } + } else { + if (parentRow?.childRows && Array.isArray(parentRow.childRows)) { + const isAnyChildUnselected = parentRow.childRows + .filter((row) => rowKeyGetter(row, uniqueRowId) !== uniqueRowKeyValue) + .some((row) => !selectedRows.has(rowKeyGetter(row, uniqueRowId))); + + if (!isAnyChildUnselected) { + parentRow.selected = true; + // instead of pushing the row we should add it to the selected set + selectedRows.add(rowKeyGetter(parentRow, uniqueRowId)); + } + } + } + // add recursiveness if there are more levels + if (parentRow && parentRow.parentKey) { + getParentSelectedState( + rowList, + rowKeyGetter(parentRow, uniqueRowId), + parentRow.parentKey, + uniqueRowId, + selectedRows, + checkedStateToMatch + ); + } +}; diff --git a/packages/lib/src/index.ts b/packages/lib/src/index.ts index b42be4926..4925553a3 100644 --- a/packages/lib/src/index.ts +++ b/packages/lib/src/index.ts @@ -48,6 +48,7 @@ import DxcDivider from "./divider/Divider"; import DxcBreadcrumbs from "./breadcrumbs/Breadcrumbs"; import DxcBarChart from "./bar-chart/BarChart"; import DxcTooltip from "./tooltip/Tooltip"; +import DxcDataGrid from "./datagrid/Datagrid"; import HalstackContext, { HalstackProvider, HalstackLanguageContext } from "./HalstackContext"; @@ -105,4 +106,5 @@ export { DxcBreadcrumbs, DxcBarChart, DxcTooltip, + DxcDataGrid, }; From 25d498a6a7f037a3c621ba1f8563437d724af6bb Mon Sep 17 00:00:00 2001 From: Jialecl <jialestrabajos@gmail.com> Date: Thu, 25 Jul 2024 13:11:08 +0200 Subject: [PATCH 02/20] Added icons + visual test cases --- .../lib/src/datagrid/Datagrid.stories.tsx | 107 +++++++++++++++++- packages/lib/src/datagrid/Datagrid.tsx | 15 ++- packages/lib/src/datagrid/utils.tsx | 29 ++++- 3 files changed, 139 insertions(+), 12 deletions(-) diff --git a/packages/lib/src/datagrid/Datagrid.stories.tsx b/packages/lib/src/datagrid/Datagrid.stories.tsx index 945064c73..124ad07cd 100644 --- a/packages/lib/src/datagrid/Datagrid.stories.tsx +++ b/packages/lib/src/datagrid/Datagrid.stories.tsx @@ -2,7 +2,7 @@ import Title from "../../.storybook/components/Title"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import DxcDataGrid from "./Datagrid"; import DxcContainer from "../container/Container"; -import { GridColumn } from "./types"; +import { GridColumn, HierarchyGridRow } from "./types"; import { useState } from "react"; export default { @@ -40,7 +40,6 @@ const columns: GridColumn[] = [ key: "priority", name: "Priority", resizable: true, - sortable: true, draggable: true, alignment: "center", }, @@ -122,8 +121,91 @@ const expandableRows = [ }, ]; +const childcolumns: GridColumn[] = [ + { + key: "name", + name: "Name", + resizable: true, + sortable: true, + summaryKeyToRender: "label", + alignment: "center", + }, + { + key: "value", + name: "Value", + resizable: true, + sortable: true, + draggable: true, + textEditable: true, + summaryKeyToRender: "total", + alignment: "right", + }, +]; + +const childRows: HierarchyGridRow[] = [ + { + name: "Root Node 1", + value: "1", + id: "a", + childRows: [ + { + name: "Child Node 1.1", + value: "1.1", + id: "aa", + childRows: [ + { + name: "Grandchild Node 1.1.1", + value: "1.1.1", + id: "aaa", + }, + { + name: "Grandchild Node 1.1.2", + value: "1.1.2", + id: "aab", + }, + ], + }, + { + name: "Child Node 1.2", + value: "1.2", + id: "ab", + }, + ], + }, + { + name: "Root Node 2", + value: "2", + id: "b", + childRows: [ + { + name: "Child Node 2.1", + value: "2.1", + id: "ba", + childRows: [ + { + name: "Grandchild Node 2.1.1", + value: "2.1.1", + id: "baa", + }, + ], + }, + { + name: "Child Node 2.2", + value: "2.2", + id: "bb", + }, + { + name: "Child Node 2.3", + value: "2.3", + id: "bc", + }, + ], + }, +]; + export const Chromatic = () => { const [selectedRows, setSelectedRows] = useState((): Set<number | string> => new Set()); + const [selectedChildRows, setSelectedChildRows] = useState((): Set<number | string> => new Set()); return ( <> <ExampleContainer> @@ -139,24 +221,39 @@ export const Chromatic = () => { <DxcDataGrid columns={columns} rows={expandableRows} - uniqueRowId="id" + uniqueRowId="task" selectable selectedRows={selectedRows} onSelectRows={setSelectedRows} /> </ExampleContainer> <ExampleContainer> - <Title title="Selectable" theme="light" level={4} /> + <Title title="Selectable & expandable" theme="light" level={4} /> <DxcDataGrid columns={columns} rows={expandableRows} - uniqueRowId="id" + uniqueRowId="task" expandable selectable selectedRows={selectedRows} onSelectRows={setSelectedRows} /> </ExampleContainer> + <ExampleContainer> + <Title title="DataGrid with children" theme="light" level={4} /> + <DxcDataGrid columns={childcolumns} rows={childRows} uniqueRowId="id" /> + </ExampleContainer> + <ExampleContainer> + <Title title="DataGrid with children" theme="light" level={4} /> + <DxcDataGrid + columns={childcolumns} + rows={childRows} + uniqueRowId="value" + selectable + selectedRows={selectedChildRows} + onSelectRows={setSelectedChildRows} + /> + </ExampleContainer> </> ); }; diff --git a/packages/lib/src/datagrid/Datagrid.tsx b/packages/lib/src/datagrid/Datagrid.tsx index 194e5969e..3ffb8199d 100644 --- a/packages/lib/src/datagrid/Datagrid.tsx +++ b/packages/lib/src/datagrid/Datagrid.tsx @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { useEffect, useMemo, useState } from "react"; import DataGridPropsType, { HierarchyGridRow, GridRow, ExpandableGridRow } from "./types"; -import DataGrid, { Column, SortColumn } from "react-data-grid"; +import DataGrid, { SortColumn } from "react-data-grid"; import "react-data-grid/lib/styles.css"; import { @@ -116,7 +116,6 @@ const DxcDataGrid = ({ </HierarchyContainer> ); } - // TODO: styled component that adds left padding based on row.rowLevel * paddingValue return ( <HierarchyContainer level={row.rowLevel || 0} className="ellipsis-cell"> {row[firstColumnKey]} @@ -260,6 +259,12 @@ const HierarchyContainer = styled.div<{ padding-left: calc(10px * ${(props) => props.level}); button { padding: 0px; + background: transparent; + border: 0px; + cursor: pointer; + width: 100%; + height: 36px; + text-align: left; } `; @@ -278,6 +283,12 @@ const DataGridContainer = styled.div` font-weight: 400; color: rgb(255, 255, 255); text-transform: none; + .sortIconContainer { + margin-left: 0.5rem; + display: flex; + height: 100%; + align-items: center; + } } .rdg-cell:first-child { border-top-left-radius: 4px; diff --git a/packages/lib/src/datagrid/utils.tsx b/packages/lib/src/datagrid/utils.tsx index 019532c3f..b5bf6ea8c 100644 --- a/packages/lib/src/datagrid/utils.tsx +++ b/packages/lib/src/datagrid/utils.tsx @@ -1,6 +1,8 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ import DxcCheckbox from "../checkbox/Checkbox"; +import DxcContainer from "../container/Container"; +import DxcFlex from "../flex/Flex"; import { HalstackProvider } from "../HalstackContext"; +import DxcIcon from "../icon/Icon"; import { GridColumn, HierarchyGridRow, GridRow, ExpandableGridRow } from "./types"; import { Column, RenderSortStatusProps, SortColumn, textEditor } from "react-data-grid"; @@ -58,8 +60,19 @@ export const convertToRDGColumns = (gridColumn: GridColumn, summaryRow?: GridRow }; export const renderSortStatus = ({ sortDirection }: RenderSortStatusProps) => { - //TODO: change to DxcIcons - return <>{sortDirection !== undefined ? (sortDirection === "ASC" ? "\u2B9D" : "\u2B9F") : null}</>; + return ( + <div className="sortIconContainer"> + {sortDirection !== undefined ? ( + sortDirection === "ASC" ? ( + <DxcIcon icon="Keyboard_Arrow_Up" /> + ) : ( + <DxcIcon icon="Keyboard_Arrow_Down" /> + ) + ) : ( + <DxcIcon icon="Expand_All" /> + )} + </div> + ); }; export const renderHierarchyTrigger = ( @@ -67,6 +80,7 @@ export const renderHierarchyTrigger = ( triggerRow: HierarchyGridRow, uniqueRowId: string, columnKey: string, + // eslint-disable-next-line no-unused-vars onRowsToRender: (rows: HierarchyGridRow[]) => void ) => { return ( @@ -112,7 +126,10 @@ export const renderHierarchyTrigger = ( onRowsToRender(newRowsToRender); }} > - {triggerRow[columnKey]} + <DxcFlex gap="0.5rem"> + <DxcIcon icon={triggerRow.visibleChildren ? "Keyboard_Arrow_Down" : "Chevron_Right"} /> + {triggerRow[columnKey]} + </DxcFlex> </button> ); }; @@ -122,6 +139,7 @@ export const renderCheckbox = ( row: GridRow | HierarchyGridRow | ExpandableGridRow, uniqueRowId: string, selectedRows: Set<number | string>, + // eslint-disable-next-line no-unused-vars onSelectRows: (selectedRows: Set<number | string>) => void ) => { return ( @@ -152,7 +170,8 @@ export const renderHeaderCheckbox = ( rows: GridRow[] | HierarchyGridRow[] | ExpandableGridRow[], uniqueRowId: string, selectedRows: Set<number | string>, - onSelectRows: (selectedRows: Set<number | string>) => void + // eslint-disable-next-line no-unused-vars + onSelectRows: (selected: Set<number | string>) => void ) => { return ( <HalstackProvider advancedTheme={checkboxTheme}> From a098e904dd542219f38edca10a8452032ee89419 Mon Sep 17 00:00:00 2001 From: Jialecl <jialestrabajos@gmail.com> Date: Thu, 25 Jul 2024 13:13:41 +0200 Subject: [PATCH 03/20] Corrected files names --- .../Datagrid.stories.tsx => data-grid/DataGrid.stories.tsx} | 2 +- .../lib/src/{datagrid/Datagrid.tsx => data-grid/DataGrid.tsx} | 0 packages/lib/src/{datagrid => data-grid}/types.ts | 2 -- packages/lib/src/{datagrid => data-grid}/utils.tsx | 1 - packages/lib/src/index.ts | 2 +- 5 files changed, 2 insertions(+), 5 deletions(-) rename packages/lib/src/{datagrid/Datagrid.stories.tsx => data-grid/DataGrid.stories.tsx} (99%) rename packages/lib/src/{datagrid/Datagrid.tsx => data-grid/DataGrid.tsx} (100%) rename packages/lib/src/{datagrid => data-grid}/types.ts (98%) rename packages/lib/src/{datagrid => data-grid}/utils.tsx (99%) diff --git a/packages/lib/src/datagrid/Datagrid.stories.tsx b/packages/lib/src/data-grid/DataGrid.stories.tsx similarity index 99% rename from packages/lib/src/datagrid/Datagrid.stories.tsx rename to packages/lib/src/data-grid/DataGrid.stories.tsx index 124ad07cd..a677ac6ae 100644 --- a/packages/lib/src/datagrid/Datagrid.stories.tsx +++ b/packages/lib/src/data-grid/DataGrid.stories.tsx @@ -1,6 +1,6 @@ import Title from "../../.storybook/components/Title"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; -import DxcDataGrid from "./Datagrid"; +import DxcDataGrid from "./DataGrid"; import DxcContainer from "../container/Container"; import { GridColumn, HierarchyGridRow } from "./types"; import { useState } from "react"; diff --git a/packages/lib/src/datagrid/Datagrid.tsx b/packages/lib/src/data-grid/DataGrid.tsx similarity index 100% rename from packages/lib/src/datagrid/Datagrid.tsx rename to packages/lib/src/data-grid/DataGrid.tsx diff --git a/packages/lib/src/datagrid/types.ts b/packages/lib/src/data-grid/types.ts similarity index 98% rename from packages/lib/src/datagrid/types.ts rename to packages/lib/src/data-grid/types.ts index d5b1ce78e..d372368b6 100644 --- a/packages/lib/src/datagrid/types.ts +++ b/packages/lib/src/data-grid/types.ts @@ -1,5 +1,3 @@ -import React from "react"; - export type GridColumn = { key: string; name: string; diff --git a/packages/lib/src/datagrid/utils.tsx b/packages/lib/src/data-grid/utils.tsx similarity index 99% rename from packages/lib/src/datagrid/utils.tsx rename to packages/lib/src/data-grid/utils.tsx index b5bf6ea8c..63a4b273a 100644 --- a/packages/lib/src/datagrid/utils.tsx +++ b/packages/lib/src/data-grid/utils.tsx @@ -1,5 +1,4 @@ import DxcCheckbox from "../checkbox/Checkbox"; -import DxcContainer from "../container/Container"; import DxcFlex from "../flex/Flex"; import { HalstackProvider } from "../HalstackContext"; import DxcIcon from "../icon/Icon"; diff --git a/packages/lib/src/index.ts b/packages/lib/src/index.ts index 4925553a3..0666953ea 100644 --- a/packages/lib/src/index.ts +++ b/packages/lib/src/index.ts @@ -48,7 +48,7 @@ import DxcDivider from "./divider/Divider"; import DxcBreadcrumbs from "./breadcrumbs/Breadcrumbs"; import DxcBarChart from "./bar-chart/BarChart"; import DxcTooltip from "./tooltip/Tooltip"; -import DxcDataGrid from "./datagrid/Datagrid"; +import DxcDataGrid from "./data-grid/Datagrid"; import HalstackContext, { HalstackProvider, HalstackLanguageContext } from "./HalstackContext"; From 200bf985d360c3aa1183828bc9f228b8a1c30006 Mon Sep 17 00:00:00 2001 From: Jialecl <jialestrabajos@gmail.com> Date: Thu, 25 Jul 2024 13:39:26 +0200 Subject: [PATCH 04/20] fixed export name --- packages/lib/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lib/src/index.ts b/packages/lib/src/index.ts index 0666953ea..d0a442fbd 100644 --- a/packages/lib/src/index.ts +++ b/packages/lib/src/index.ts @@ -48,7 +48,7 @@ import DxcDivider from "./divider/Divider"; import DxcBreadcrumbs from "./breadcrumbs/Breadcrumbs"; import DxcBarChart from "./bar-chart/BarChart"; import DxcTooltip from "./tooltip/Tooltip"; -import DxcDataGrid from "./data-grid/Datagrid"; +import DxcDataGrid from "./data-grid/DataGrid"; import HalstackContext, { HalstackProvider, HalstackLanguageContext } from "./HalstackContext"; From 4c7f5eb19236f5021e6e624fd9faf122dd98da08 Mon Sep 17 00:00:00 2001 From: Jialecl <jialestrabajos@gmail.com> Date: Tue, 30 Jul 2024 13:56:01 +0200 Subject: [PATCH 05/20] Fixed build and added summary row visual test --- packages/lib/src/data-grid/DataGrid.stories.tsx | 13 +++++++++++-- packages/lib/src/data-grid/DataGrid.tsx | 13 ++++++++++++- packages/lib/tsconfig.json | 8 +++++++- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/packages/lib/src/data-grid/DataGrid.stories.tsx b/packages/lib/src/data-grid/DataGrid.stories.tsx index a677ac6ae..fb921dda9 100644 --- a/packages/lib/src/data-grid/DataGrid.stories.tsx +++ b/packages/lib/src/data-grid/DataGrid.stories.tsx @@ -35,6 +35,7 @@ const columns: GridColumn[] = [ sortable: true, draggable: true, alignment: "right", + summaryKeyToRender: "label", }, { key: "priority", @@ -42,6 +43,7 @@ const columns: GridColumn[] = [ resizable: true, draggable: true, alignment: "center", + summaryKeyToRender: "total", }, ]; @@ -127,7 +129,6 @@ const childcolumns: GridColumn[] = [ name: "Name", resizable: true, sortable: true, - summaryKeyToRender: "label", alignment: "center", }, { @@ -137,7 +138,6 @@ const childcolumns: GridColumn[] = [ sortable: true, draggable: true, textEditable: true, - summaryKeyToRender: "total", alignment: "right", }, ]; @@ -254,6 +254,15 @@ export const Chromatic = () => { onSelectRows={setSelectedChildRows} /> </ExampleContainer> + <ExampleContainer> + <Title title="Summary row" theme="light" level={4} /> + <DxcDataGrid + columns={columns} + rows={expandableRows} + summaryRow={{ label: "Total", total: 100 }} + uniqueRowId="id" + /> + </ExampleContainer> </> ); }; diff --git a/packages/lib/src/data-grid/DataGrid.tsx b/packages/lib/src/data-grid/DataGrid.tsx index 3ffb8199d..2fae4953c 100644 --- a/packages/lib/src/data-grid/DataGrid.tsx +++ b/packages/lib/src/data-grid/DataGrid.tsx @@ -273,6 +273,7 @@ const DataGridContainer = styled.div` .rdg { border-radius: 4px; max-height: 100%; + border: 0px; } .rdg-header-row { background-color: rgb(95, 36, 159); @@ -302,7 +303,17 @@ const DataGridContainer = styled.div` overflow: hidden; white-space: nowrap; } - .rdg-cell { + .rdg-summary-row { + background-color: #fafafa; + .rdg-cell { + border-top: 0px; + font-weight: 600; + } + } + .rdg-row { + .rdg-cell:last-child { + border-right: 0px; + } } .align-left { text-align: left; diff --git a/packages/lib/tsconfig.json b/packages/lib/tsconfig.json index 55ba60b1d..00fd4ab5d 100644 --- a/packages/lib/tsconfig.json +++ b/packages/lib/tsconfig.json @@ -2,7 +2,13 @@ "extends": "@dxc-technology/typescript-config/react-library.json", "compilerOptions": { "outDir": "dist", - "strict": false + "strict": false, + "target": "es5", + "module": "ESNext", // or "es2020" + "moduleResolution": "node", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "skipLibCheck": true }, "include": ["src"], "exclude": ["node_modules", "dist"] From 99a44c75227decb271ac4017de4d198d06693705 Mon Sep 17 00:00:00 2001 From: Mil4n0r <morenocarmonaenrique@gmail.com> Date: Tue, 30 Jul 2024 14:51:10 +0200 Subject: [PATCH 06/20] Added accessibility exception for data-grid --- packages/lib/src/data-grid/DataGrid.stories.tsx | 12 ++++++++++++ .../rules/specific/data-grid/disabledRules.js | 8 ++++++++ 2 files changed, 20 insertions(+) create mode 100644 packages/lib/test/accessibility/rules/specific/data-grid/disabledRules.js diff --git a/packages/lib/src/data-grid/DataGrid.stories.tsx b/packages/lib/src/data-grid/DataGrid.stories.tsx index fb921dda9..5af81a54d 100644 --- a/packages/lib/src/data-grid/DataGrid.stories.tsx +++ b/packages/lib/src/data-grid/DataGrid.stories.tsx @@ -4,10 +4,22 @@ import DxcDataGrid from "./DataGrid"; import DxcContainer from "../container/Container"; import { GridColumn, HierarchyGridRow } from "./types"; import { useState } from "react"; +import { disabledRules } from "../../test/accessibility/rules/specific/data-grid/disabledRules"; +import preview from "../../.storybook/preview"; export default { title: "Datagrid", component: DxcDataGrid, + parameters: { + a11y: { + config: { + rules: [ + ...disabledRules.map((ruleId) => ({ id: ruleId, reviewOnFail: true })), + ...preview?.parameters?.a11y?.config?.rules, + ], + }, + }, + }, }; const columns: GridColumn[] = [ diff --git a/packages/lib/test/accessibility/rules/specific/data-grid/disabledRules.js b/packages/lib/test/accessibility/rules/specific/data-grid/disabledRules.js new file mode 100644 index 000000000..52c467b57 --- /dev/null +++ b/packages/lib/test/accessibility/rules/specific/data-grid/disabledRules.js @@ -0,0 +1,8 @@ +/** + * Array of accessibility rule IDs to be disabled in both Jest and Storybook for the data grid component. + * + */ +export const disabledRules = [ + // Disable scrollable region focusable rule to prevent errors from having an empty header for the expandable data grids + "empty-table-header", +]; From 5b7dd697dcdd01b31cbd865148cc05e5c9f6ca54 Mon Sep 17 00:00:00 2001 From: Jialecl <jialestrabajos@gmail.com> Date: Thu, 1 Aug 2024 12:00:41 +0200 Subject: [PATCH 07/20] Added more test cases --- .../lib/src/data-grid/DataGrid.stories.tsx | 46 +++++++++++ packages/lib/src/data-grid/DataGrid.test.tsx | 78 +++++++++++++++++++ packages/lib/src/data-grid/DataGrid.tsx | 7 +- 3 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 packages/lib/src/data-grid/DataGrid.test.tsx diff --git a/packages/lib/src/data-grid/DataGrid.stories.tsx b/packages/lib/src/data-grid/DataGrid.stories.tsx index fb921dda9..a3b9a4f59 100644 --- a/packages/lib/src/data-grid/DataGrid.stories.tsx +++ b/packages/lib/src/data-grid/DataGrid.stories.tsx @@ -4,6 +4,7 @@ import DxcDataGrid from "./DataGrid"; import DxcContainer from "../container/Container"; import { GridColumn, HierarchyGridRow } from "./types"; import { useState } from "react"; +import { fireEvent, screen, userEvent, within } from "@storybook/test"; export default { title: "Datagrid", @@ -266,3 +267,48 @@ export const Chromatic = () => { </> ); }; + +const DataGridSortedChildren = () => { + const [selectedChildRows, setSelectedChildRows] = useState((): Set<number | string> => new Set()); + return ( + <ExampleContainer> + <DxcDataGrid + columns={childcolumns} + rows={childRows} + uniqueRowId="id" + selectable + onSelectRows={setSelectedChildRows} + selectedRows={selectedChildRows} + /> + </ExampleContainer> + ); +}; + +export const DataGridSortedWithChildren = DataGridSortedChildren.bind({}); +DataGridSortedWithChildren.play = async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.click(canvas.getAllByRole("checkbox")[0]); + await userEvent.click(canvas.getByText("Root Node 1")); + await userEvent.click(canvas.getByText("Root Node 2")); + await userEvent.click(canvas.getByText("Child Node 1.1")); + await userEvent.click(canvas.getByText("Child Node 2.1")); + await userEvent.click(canvas.getAllByRole("columnheader")[1]); + await userEvent.click(canvas.getAllByRole("columnheader")[1]); + await userEvent.click(canvas.getAllByRole("checkbox")[5]); +}; + +const DataGridSortedExpandable = () => { + return ( + <ExampleContainer> + <DxcDataGrid columns={columns} rows={expandableRows} uniqueRowId="task" expandable /> + </ExampleContainer> + ); +}; + +export const DataGridSortedExpanded = DataGridSortedExpandable.bind({}); +DataGridSortedExpanded.play = async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.click(canvas.getAllByRole("button")[0]); + await userEvent.click(canvas.getAllByRole("button")[1]); + await userEvent.click(canvas.getAllByRole("columnheader")[3]); +}; diff --git a/packages/lib/src/data-grid/DataGrid.test.tsx b/packages/lib/src/data-grid/DataGrid.test.tsx new file mode 100644 index 000000000..4a5933232 --- /dev/null +++ b/packages/lib/src/data-grid/DataGrid.test.tsx @@ -0,0 +1,78 @@ +import { findAllByRole, fireEvent, getAllByRole, render, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import DxcDataGrid from "./DataGrid"; +import { screen } from "@storybook/test"; +import { GridColumn } from "./types"; + +const columns: GridColumn[] = [ + { + key: "id", + name: "ID", + resizable: true, + sortable: true, + draggable: false, + alignment: "left", + }, + { + key: "complete", + name: " % Complete", + resizable: true, + sortable: true, + draggable: true, + }, +]; + +const expandableRows = [ + { + id: 1, + complete: 46, + expandedContent: <div> Custom content 1</div>, + }, + { + id: 2, + complete: 51, + expandedContent: <div> Custom content 2</div>, + }, + { + id: 3, + complete: 40, + expandedContent: <div> Custom content 3</div>, + }, + { + id: 4, + complete: 10, + expandedContent: <div> Custom content 4</div>, + }, + { + id: 5, + complete: 1, + expandedContent: <div> Custom content 5</div>, + }, +]; + +describe("Data grid component tests", () => { + beforeAll(() => { + global.CSS = { + escape: (str) => str, + }; + window.HTMLElement.prototype.scrollIntoView = jest.fn; + }); + test("Renders with correct content", async () => { + const { getByText, getAllByRole } = await render(<DxcDataGrid columns={columns} rows={expandableRows} />); + expect(getByText("46")).toBeTruthy(); + const rows = getAllByRole("row"); + expect(rows.length).toBe(5); + }); + // test("Content is sorted correctly", async () => { + // const { getByText, getAllByRole } = await render(<DxcDataGrid columns={columns} rows={expandableRows} />); + // expect(getByText("% Complete")).toBeTruthy(); + // const headerCell = screen.getAllByRole("columnheader")[1]; + // expect(getAllByRole("gridcell")[0].textContent).toBe("1"); + // expect(headerCell.textContent).toBe(" % Complete"); + // await fireEvent.click(headerCell); + // expect(headerCell.getAttribute("aria-sort")).toBe("ascending"); + // expect(getByText("5")).toBeTruthy(); + // // await waitFor(() => expect(getAllByRole("gridcell")[0].textContent).toBe("4")); + // //waitFor(() => expect(getAllByRole("gridcell").length).toBe(8)); + // }); +}); diff --git a/packages/lib/src/data-grid/DataGrid.tsx b/packages/lib/src/data-grid/DataGrid.tsx index 2fae4953c..191ccfb65 100644 --- a/packages/lib/src/data-grid/DataGrid.tsx +++ b/packages/lib/src/data-grid/DataGrid.tsx @@ -68,7 +68,8 @@ const DxcDataGrid = ({ <ActionContainer> <DxcActionIcon icon={row.contentIsExpanded ? "arrow_drop_down" : "arrow_right"} - title="icon" + title="Expand content" + aria-expanded={row.contentIsExpanded} onClick={() => { row.contentIsExpanded = !row.contentIsExpanded; if (row.contentIsExpanded) { @@ -77,7 +78,7 @@ const DxcDataGrid = ({ const newRows = [...rows]; addRow(newRows, rowIndex + 1, { isExpandedChildContent: row.contentIsExpanded, - uniqueRowId: rowKeyGetter(row, uniqueRowId) + "_expanded", + [uniqueRowId]: rowKeyGetter(row, uniqueRowId) + "_expanded", expandedChildContent: row.expandedContent, triggerRowKey: rowKeyGetter(row, uniqueRowId), expandedContentHeight: row.expandedContentHeight, @@ -272,7 +273,7 @@ const DataGridContainer = styled.div` width: 100%; .rdg { border-radius: 4px; - max-height: 100%; + height: 100%; border: 0px; } .rdg-header-row { From 9bf757994dffa9f607e4990b254c3a6dd6894b17 Mon Sep 17 00:00:00 2001 From: Jialecl <jialestrabajos@gmail.com> Date: Thu, 1 Aug 2024 12:33:15 +0200 Subject: [PATCH 08/20] expandable trigger moved into utils --- packages/lib/src/data-grid/DataGrid.tsx | 37 ++----------------- packages/lib/src/data-grid/utils.tsx | 47 ++++++++++++++++++++++--- 2 files changed, 45 insertions(+), 39 deletions(-) diff --git a/packages/lib/src/data-grid/DataGrid.tsx b/packages/lib/src/data-grid/DataGrid.tsx index 191ccfb65..5a82bd4cc 100644 --- a/packages/lib/src/data-grid/DataGrid.tsx +++ b/packages/lib/src/data-grid/DataGrid.tsx @@ -13,11 +13,10 @@ import { renderCheckbox, renderHeaderCheckbox, sortHierarchyRows, - deleteRow, renderHierarchyTrigger, + renderExpandableTrigger, } from "./utils"; import styled from "styled-components"; -import DxcActionIcon from "../action-icon/ActionIcon"; const DxcDataGrid = ({ columns, @@ -66,35 +65,7 @@ const DxcDataGrid = ({ // if row has expandable content return ( <ActionContainer> - <DxcActionIcon - icon={row.contentIsExpanded ? "arrow_drop_down" : "arrow_right"} - title="Expand content" - aria-expanded={row.contentIsExpanded} - onClick={() => { - row.contentIsExpanded = !row.contentIsExpanded; - if (row.contentIsExpanded) { - const rowIndex = rowsToRender.findIndex((rowToRender) => row === rowToRender); - setRowsToRender((rows) => { - const newRows = [...rows]; - addRow(newRows, rowIndex + 1, { - isExpandedChildContent: row.contentIsExpanded, - [uniqueRowId]: rowKeyGetter(row, uniqueRowId) + "_expanded", - expandedChildContent: row.expandedContent, - triggerRowKey: rowKeyGetter(row, uniqueRowId), - expandedContentHeight: row.expandedContentHeight, - }); - return newRows; - }); - } else { - const rowIndex = rowsToRender.findIndex((rowToRender) => row === rowToRender); - setRowsToRender((rows) => { - const newRows = [...rows]; - deleteRow(newRows, rowIndex + 1); - return newRows; - }); - } - }} - /> + {renderExpandableTrigger(row, rowsToRender, uniqueRowId, setRowsToRender)} </ActionContainer> ); }, @@ -111,9 +82,7 @@ const DxcDataGrid = ({ if (row.childRows?.length) { return ( <HierarchyContainer level={row.rowLevel || 0}> - {renderHierarchyTrigger(rowsToRender, row, uniqueRowId, firstColumnKey, (rows) => - setRowsToRender(rows) - )} + {renderHierarchyTrigger(rowsToRender, row, uniqueRowId, firstColumnKey, setRowsToRender)} </HierarchyContainer> ); } diff --git a/packages/lib/src/data-grid/utils.tsx b/packages/lib/src/data-grid/utils.tsx index 63a4b273a..e26c198a4 100644 --- a/packages/lib/src/data-grid/utils.tsx +++ b/packages/lib/src/data-grid/utils.tsx @@ -1,3 +1,4 @@ +import DxcActionIcon from "../action-icon/ActionIcon"; import DxcCheckbox from "../checkbox/Checkbox"; import DxcFlex from "../flex/Flex"; import { HalstackProvider } from "../HalstackContext"; @@ -74,13 +75,51 @@ export const renderSortStatus = ({ sortDirection }: RenderSortStatusProps) => { ); }; +export const renderExpandableTrigger = ( + row: ExpandableGridRow, + rows: ExpandableGridRow[], + uniqueRowId: string, + setRowsToRender: (value: React.SetStateAction<ExpandableGridRow[] | GridRow[] | HierarchyGridRow[]>) => void +) => { + return ( + <DxcActionIcon + icon={row.contentIsExpanded ? "arrow_drop_down" : "arrow_right"} + title="Expand content" + aria-expanded={row.contentIsExpanded} + onClick={() => { + row.contentIsExpanded = !row.contentIsExpanded; + if (row.contentIsExpanded) { + const rowIndex = rows.findIndex((rowToRender) => row === rowToRender); + setRowsToRender((rows) => { + const newRows = [...rows]; + addRow(newRows, rowIndex + 1, { + isExpandedChildContent: row.contentIsExpanded, + [uniqueRowId]: rowKeyGetter(row, uniqueRowId) + "_expanded", + expandedChildContent: row.expandedContent, + triggerRowKey: rowKeyGetter(row, uniqueRowId), + expandedContentHeight: row.expandedContentHeight, + }); + return newRows; + }); + } else { + const rowIndex = rows.findIndex((rowToRender) => row === rowToRender); + setRowsToRender((rows) => { + const newRows = [...rows]; + deleteRow(newRows, rowIndex + 1); + return newRows; + }); + } + }} + /> + ); +}; + export const renderHierarchyTrigger = ( rows: HierarchyGridRow[], triggerRow: HierarchyGridRow, uniqueRowId: string, columnKey: string, - // eslint-disable-next-line no-unused-vars - onRowsToRender: (rows: HierarchyGridRow[]) => void + setRowsToRender: (value: React.SetStateAction<GridRow[] | ExpandableGridRow[] | HierarchyGridRow[]>) => void ) => { return ( <button @@ -122,7 +161,7 @@ export const renderHierarchyTrigger = ( ); } triggerRow.visibleChildren = !triggerRow.visibleChildren; - onRowsToRender(newRowsToRender); + setRowsToRender(newRowsToRender); }} > <DxcFlex gap="0.5rem"> @@ -138,7 +177,6 @@ export const renderCheckbox = ( row: GridRow | HierarchyGridRow | ExpandableGridRow, uniqueRowId: string, selectedRows: Set<number | string>, - // eslint-disable-next-line no-unused-vars onSelectRows: (selectedRows: Set<number | string>) => void ) => { return ( @@ -169,7 +207,6 @@ export const renderHeaderCheckbox = ( rows: GridRow[] | HierarchyGridRow[] | ExpandableGridRow[], uniqueRowId: string, selectedRows: Set<number | string>, - // eslint-disable-next-line no-unused-vars onSelectRows: (selected: Set<number | string>) => void ) => { return ( From 02eec587904ae492bedde6bdd24f65b95f575a29 Mon Sep 17 00:00:00 2001 From: Jialecl <jialestrabajos@gmail.com> Date: Fri, 2 Aug 2024 10:55:27 +0200 Subject: [PATCH 09/20] added selectable to sorting test --- packages/lib/src/data-grid/DataGrid.stories.tsx | 13 +++++++++++-- packages/lib/src/data-grid/DataGrid.tsx | 3 ++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/lib/src/data-grid/DataGrid.stories.tsx b/packages/lib/src/data-grid/DataGrid.stories.tsx index 3867b2cc3..c59237898 100644 --- a/packages/lib/src/data-grid/DataGrid.stories.tsx +++ b/packages/lib/src/data-grid/DataGrid.stories.tsx @@ -310,9 +310,18 @@ DataGridSortedWithChildren.play = async ({ canvasElement }) => { }; const DataGridSortedExpandable = () => { + const [selectedRows, setSelectedRows] = useState((): Set<number | string> => new Set()); return ( <ExampleContainer> - <DxcDataGrid columns={columns} rows={expandableRows} uniqueRowId="task" expandable /> + <DxcDataGrid + columns={columns} + rows={expandableRows} + uniqueRowId="task" + expandable + selectable + onSelectRows={setSelectedRows} + selectedRows={selectedRows} + /> </ExampleContainer> ); }; @@ -322,5 +331,5 @@ DataGridSortedExpanded.play = async ({ canvasElement }) => { const canvas = within(canvasElement); await userEvent.click(canvas.getAllByRole("button")[0]); await userEvent.click(canvas.getAllByRole("button")[1]); - await userEvent.click(canvas.getAllByRole("columnheader")[3]); + await userEvent.click(canvas.getAllByRole("columnheader")[4]); }; diff --git a/packages/lib/src/data-grid/DataGrid.tsx b/packages/lib/src/data-grid/DataGrid.tsx index 5a82bd4cc..463754918 100644 --- a/packages/lib/src/data-grid/DataGrid.tsx +++ b/packages/lib/src/data-grid/DataGrid.tsx @@ -54,7 +54,7 @@ const DxcDataGrid = ({ width: 50, colSpan(args) { return args.type === "ROW" && args.row.isExpandedChildContent - ? columns.length + (selectable ? 2 : 1) + ? columns.length + (selectable ? 1 : 0) : undefined; }, renderCell({ row }) { @@ -278,6 +278,7 @@ const DataGridContainer = styled.div` .rdg-cell { border-top: 0px; font-weight: 600; + border-right: 0px; } } .rdg-row { From 5d7d492b82e5c55102ee3652871a8b1239e4f27c Mon Sep 17 00:00:00 2001 From: Jialecl <jialestrabajos@gmail.com> Date: Fri, 2 Aug 2024 13:36:34 +0200 Subject: [PATCH 10/20] Documentation pages added to website --- .../pages/components/data-grid/index.tsx | 21 ++ .../components/data-grid/specifications.tsx | 21 ++ .../pages/components/data-grid/usage.tsx | 21 ++ .../screens/common/componentsList.json | 5 + .../data-grid/DatagridPageLayout.tsx | 27 +++ .../data-grid/code/DataGridCodePage.tsx | 220 ++++++++++++++++++ .../data-grid/code/examples/basicUsage.ts | 53 +++++ .../data-grid/code/examples/expandable.ts | 56 +++++ .../data-grid/code/examples/hierarchical.ts | 93 ++++++++ .../code/examples/hierarchicalSelectable.ts | 101 ++++++++ .../data-grid/code/examples/selectable.ts | 64 +++++ .../data-grid/specs/DataGridSpecsPage.tsx | 61 +++++ .../data-grid/usage/DataGridUsagePage.tsx | 28 +++ .../lib/src/data-grid/DataGrid.stories.tsx | 14 +- packages/lib/src/data-grid/DataGrid.test.tsx | 46 +++- packages/lib/src/data-grid/DataGrid.tsx | 13 +- packages/lib/src/data-grid/types.ts | 9 +- packages/lib/src/data-grid/utils.tsx | 9 +- 18 files changed, 835 insertions(+), 27 deletions(-) create mode 100644 apps/website/pages/components/data-grid/index.tsx create mode 100644 apps/website/pages/components/data-grid/specifications.tsx create mode 100644 apps/website/pages/components/data-grid/usage.tsx create mode 100644 apps/website/screens/components/data-grid/DatagridPageLayout.tsx create mode 100644 apps/website/screens/components/data-grid/code/DataGridCodePage.tsx create mode 100644 apps/website/screens/components/data-grid/code/examples/basicUsage.ts create mode 100644 apps/website/screens/components/data-grid/code/examples/expandable.ts create mode 100644 apps/website/screens/components/data-grid/code/examples/hierarchical.ts create mode 100644 apps/website/screens/components/data-grid/code/examples/hierarchicalSelectable.ts create mode 100644 apps/website/screens/components/data-grid/code/examples/selectable.ts create mode 100644 apps/website/screens/components/data-grid/specs/DataGridSpecsPage.tsx create mode 100644 apps/website/screens/components/data-grid/usage/DataGridUsagePage.tsx diff --git a/apps/website/pages/components/data-grid/index.tsx b/apps/website/pages/components/data-grid/index.tsx new file mode 100644 index 000000000..b1e525180 --- /dev/null +++ b/apps/website/pages/components/data-grid/index.tsx @@ -0,0 +1,21 @@ +import Head from "next/head"; +import type { ReactElement } from "react"; +import DataGridCodePage from "screens/components/data-grid/code/DataGridCodePage"; +import DataGridPageLayout from "screens/components/data-grid/DatagridPageLayout"; + +const Usage = () => { + return ( + <> + <Head> + <title>Date Input — Halstack Design System + + + + ); +}; + +Usage.getLayout = function getLayout(page: ReactElement) { + return {page}; +}; + +export default Usage; diff --git a/apps/website/pages/components/data-grid/specifications.tsx b/apps/website/pages/components/data-grid/specifications.tsx new file mode 100644 index 000000000..8f3e3fdd7 --- /dev/null +++ b/apps/website/pages/components/data-grid/specifications.tsx @@ -0,0 +1,21 @@ +import Head from "next/head"; +import type { ReactElement } from "react"; +import DataGridSpecsPage from "screens/components/data-grid/specs/DataGridSpecsPage"; +import DataGridPageLayout from "screens/components/data-grid/DatagridPageLayout"; + +const Specifications = () => { + return ( + <> + + Date Input Specs — Halstack Design System + + + + ); +}; + +Specifications.getLayout = function getLayout(page: ReactElement) { + return {page}; +}; + +export default Specifications; diff --git a/apps/website/pages/components/data-grid/usage.tsx b/apps/website/pages/components/data-grid/usage.tsx new file mode 100644 index 000000000..c426cb722 --- /dev/null +++ b/apps/website/pages/components/data-grid/usage.tsx @@ -0,0 +1,21 @@ +import Head from "next/head"; +import type { ReactElement } from "react"; +import DataGridUsagePage from "screens/components/data-grid/usage/DataGridUsagePage"; +import DataGridPageLayout from "../../../screens/components/data-grid/DatagridPageLayout"; + +const Usage = () => { + return ( + <> + + Data Grid Usage — Halstack Design System + + + + ); +}; + +Usage.getLayout = function getLayout(page: ReactElement) { + return {page}; +}; + +export default Usage; diff --git a/apps/website/screens/common/componentsList.json b/apps/website/screens/common/componentsList.json index 805db0d15..d6e5c77b4 100644 --- a/apps/website/screens/common/componentsList.json +++ b/apps/website/screens/common/componentsList.json @@ -42,6 +42,11 @@ "path": "/components/contextual-menu", "status": "new" }, + { + "label": "Data Grid", + "path": "/components/data-grid", + "status": "experimental" + }, { "label": "Date Input", "path": "/components/date-input", diff --git a/apps/website/screens/components/data-grid/DatagridPageLayout.tsx b/apps/website/screens/components/data-grid/DatagridPageLayout.tsx new file mode 100644 index 000000000..79a59efb0 --- /dev/null +++ b/apps/website/screens/components/data-grid/DatagridPageLayout.tsx @@ -0,0 +1,27 @@ +import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; +import PageHeading from "@/common/PageHeading"; +import TabsPageHeading from "@/common/TabsPageLayout"; +import ComponentHeading from "@/common/ComponentHeading"; + +const DataGridPageHeading = ({ children }: { children: React.ReactNode }) => { + const tabs = [ + { label: "Code", path: "/components/data-grid" }, + { label: "Usage", path: "/components/data-grid/usage" }, + { label: "Specifications", path: "/components/data-grid/specifications" }, + ]; + + return ( + + + + + A data grid is ... (TODO). + + + + {children} + + ); +}; + +export default DataGridPageHeading; diff --git a/apps/website/screens/components/data-grid/code/DataGridCodePage.tsx b/apps/website/screens/components/data-grid/code/DataGridCodePage.tsx new file mode 100644 index 000000000..5f8780a13 --- /dev/null +++ b/apps/website/screens/components/data-grid/code/DataGridCodePage.tsx @@ -0,0 +1,220 @@ +import { DxcFlex, DxcTable } from "@dxc-technology/halstack-react"; +import QuickNavContainer from "@/common/QuickNavContainer"; +import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; +import DocFooter from "@/common/DocFooter"; +import Code from "@/common/Code"; +import Example from "@/common/example/Example"; +import TableCode, { ExtendedTableCode } from "@/common/TableCode"; +import StatusBadge from "@/common/StatusBadge"; +import basicUsage from "./examples/basicUsage"; +import selectable from "./examples/selectable"; +import expandable from "./examples/expandable"; +import hierarchical from "./examples/hierarchical"; +import hierarchicalSelectable from "./examples/hierarchicalSelectable"; + +const columnsTypeString = `{ + key: string; + label: string; + resizable?: boolean; + sortable?: boolean; + draggable?: boolean; + textEditable?: boolean; + summaryKey?: string; + alignment?: "left" | "right" | "center"; +}`; + +const GridRowTypeString = `{ + [key: string]: React.ReactNode | undefined; +}`; + +const HierarchyGridRowTypeString = `GridRow & { + childRows?: HierarchyGridRow[] | GridRow[]; +}`; + +const ExpandableGridRowTypeString = `GridRow & { + expandedContent?: React.ReactNode; + expandedContentHeight?: number; +}`; + +const sections = [ + { + title: "Props", + content: ( + + + + Name + Type + Description + Default + + + + + + + columns + + +

+ GridColumn[] + being GridColumn: +

+ {columnsTypeString} + + + Each GridColumn object has the following properties: +
    +
  • + key: Key that will be rendered from each row in rows. +
  • +
  • + label: Label that will be used for the column header. +
  • +
  • + resizable: Whether the column is resizable or not. +
  • +
  • + sortable: Whether the column is sortable or not. +
  • +
  • + draggable: Whether the column can be dragged or not to another position or not. +
  • +
  • + textEditable: Whether the column cells are editable or not. +
  • +
  • + summaryKey: Value that will be rendered from the summaryRow +
  • +
  • + alignment: St sets the alignment inside the cells. +
  • +
+ + - + + + + + rows + + + GridRow[] | HierarchyGridRow[] | ExpandableGridRow[] +

Each one of them being in order:

+

+ {GridRowTypeString} +

+

+ {HierarchyGridRowTypeString} +

+

+ {ExpandableGridRowTypeString} +

+ + + List of rows that will be rendered in each cell based on the key in each column. + + - + + + expandable + + boolean + + Whether the rows can expand or not. + - + + + summaryRow + + GridRow + + Extra row that will be always visible. + - + + + selectable + + boolean + + Whether the rows are selectable or not. + - + + + selectedRows + + {`Set`} + + + Set of selected rows. This prop is mandatory if selectable is set to true. The{" "} + uniqueRowId key will be used to identify the each row. + + - + + + onSelectRows + + {`(selectedRows: Set) => void`} + + + Function called whenever the selected values changes. This prop is mandatory if selectable is + set to true.The uniqueRowId key will be used to identify the rows. + + - + + + uniqueRowId + + string + + + This prop indicates the unique key that can be used to identify each row. This prop is mandatory if{" "} + selectable is set to true, expandable is set to true or rows is of + type HierarchyGridRow[]. + + - + + +
+ ), + }, + + { + title: "Examples", + subSections: [ + { + title: "Basic usage", + content: , + }, + { + title: "Selectable data grid", + content: , + }, + { + title: "Expandable data grid", + content: , + }, + { + title: "Hierarchical data grid", + content: , + }, + { + title: "Hierarchical and selectable data grid", + content: , + }, + ], + }, +]; + +const DataGridCodePage = () => { + return ( + + + + + + + ); +}; + +export default DataGridCodePage; diff --git a/apps/website/screens/components/data-grid/code/examples/basicUsage.ts b/apps/website/screens/components/data-grid/code/examples/basicUsage.ts new file mode 100644 index 000000000..fcff29029 --- /dev/null +++ b/apps/website/screens/components/data-grid/code/examples/basicUsage.ts @@ -0,0 +1,53 @@ +import { DxcDataGrid, DxcInset } from "@dxc-technology/halstack-react"; + +const code = `() => { + const columns = [ + { + key: "id", + label: "ID", + resizable: true, + sortable: true, + draggable: true, + }, + { + key: "complete", + label: "% Complete", + resizable: true, + sortable: true, + draggable: true, + alignment: "center", + }, + ]; + + const rows = [ + { + id: 1, + complete: 46, + }, + { + id: 2, + complete: 51, + }, + { + id: 3, + complete: 40, + }, + { + id: 4, + complete: 10, + }, + + ]; + return ( + + + + ); +}`; + +const scope = { + DxcDataGrid, + DxcInset, +}; + +export default { code, scope }; diff --git a/apps/website/screens/components/data-grid/code/examples/expandable.ts b/apps/website/screens/components/data-grid/code/examples/expandable.ts new file mode 100644 index 000000000..0f770a0d5 --- /dev/null +++ b/apps/website/screens/components/data-grid/code/examples/expandable.ts @@ -0,0 +1,56 @@ +import { DxcDataGrid, DxcInset } from "@dxc-technology/halstack-react"; + +const code = `() => { + const columns = [ + { + key: "id", + label: "ID", + resizable: true, + sortable: true, + draggable: true, + }, + { + key: "complete", + label: "% Complete", + resizable: true, + sortable: true, + draggable: true, + alignment: "center", + }, + ]; + + const rows = [ + { + id: 1, + complete: 46, + expandedContent: "Expanded content" + }, + { + id: 2, + complete: 51, + expandedContent: "Expanded content", + expandedContentHeight: 100 + }, + { + id: 3, + complete: 40, + }, + { + id: 4, + complete: 10, + }, + + ]; + return ( + + + + ); +}`; + +const scope = { + DxcDataGrid, + DxcInset, +}; + +export default { code, scope }; diff --git a/apps/website/screens/components/data-grid/code/examples/hierarchical.ts b/apps/website/screens/components/data-grid/code/examples/hierarchical.ts new file mode 100644 index 000000000..42fd8d49a --- /dev/null +++ b/apps/website/screens/components/data-grid/code/examples/hierarchical.ts @@ -0,0 +1,93 @@ +import { DxcDataGrid, DxcInset } from "@dxc-technology/halstack-react"; + +const code = `() => { + const columns = [ + { + key: "name", + label: "Label", + summaryKey: "label" + }, + { + key: "value", + label: "Value", + alignment: "center", + summaryKey: "total" + }, + ]; + + const rows = [ + { + name: "Root Node 1", + value: "1", + id: "a", + childRows: [ + { + name: "Child Node 1.1", + value: "1.1", + id: "aa", + childRows: [ + { + name: "Grandchild Node 1.1.1", + value: "1.1.1", + id: "aaa", + }, + { + name: "Grandchild Node 1.1.2", + value: "1.1.2", + id: "aab", + }, + ], + }, + { + name: "Child Node 1.2", + value: "1.2", + id: "ab", + }, + ], + }, + { + name: "Root Node 2", + value: "2", + id: "b", + childRows: [ + { + name: "Child Node 2.1", + value: "2.1", + id: "ba", + childRows: [ + { + name: "Grandchild Node 2.1.1", + value: "2.1.1", + id: "baa", + }, + ], + }, + { + name: "Child Node 2.2", + value: "2.2", + id: "bb", + }, + { + name: "Child Node 2.3", + value: "2.3", + id: "bc", + }, + ], + }, + ]; + + const summaryRow = { label: "Total", total: 100, id: "summary" } + + return ( + + + + ); +}`; + +const scope = { + DxcDataGrid, + DxcInset, +}; + +export default { code, scope }; diff --git a/apps/website/screens/components/data-grid/code/examples/hierarchicalSelectable.ts b/apps/website/screens/components/data-grid/code/examples/hierarchicalSelectable.ts new file mode 100644 index 000000000..19918de61 --- /dev/null +++ b/apps/website/screens/components/data-grid/code/examples/hierarchicalSelectable.ts @@ -0,0 +1,101 @@ +import { DxcDataGrid, DxcInset } from "@dxc-technology/halstack-react"; +import { useState } from "react"; + +const code = `() => { + const columns = [ + { + key: "name", + label: "Label", + summaryKey: "label" + }, + { + key: "value", + label: "Value", + alignment: "center", + summaryKey: "total" + }, + ]; + + const rows = [ + { + name: "Root Node 1", + value: "1", + id: "a", + childRows: [ + { + name: "Child Node 1.1", + value: "1.1", + id: "aa", + childRows: [ + { + name: "Grandchild Node 1.1.1", + value: "1.1.1", + id: "aaa", + }, + { + name: "Grandchild Node 1.1.2", + value: "1.1.2", + id: "aab", + }, + ], + }, + { + name: "Child Node 1.2", + value: "1.2", + id: "ab", + }, + ], + }, + { + name: "Root Node 2", + value: "2", + id: "b", + childRows: [ + { + name: "Child Node 2.1", + value: "2.1", + id: "ba", + childRows: [ + { + name: "Grandchild Node 2.1.1", + value: "2.1.1", + id: "baa", + }, + ], + }, + { + name: "Child Node 2.2", + value: "2.2", + id: "bb", + }, + { + name: "Child Node 2.3", + value: "2.3", + id: "bc", + }, + ], + }, + ]; + + const [selectedRows, setSelectedRows] = useState(new Set()); + return ( + + + + ); +}`; + +const scope = { + DxcDataGrid, + DxcInset, + useState, +}; + +export default { code, scope }; diff --git a/apps/website/screens/components/data-grid/code/examples/selectable.ts b/apps/website/screens/components/data-grid/code/examples/selectable.ts new file mode 100644 index 000000000..982447d3f --- /dev/null +++ b/apps/website/screens/components/data-grid/code/examples/selectable.ts @@ -0,0 +1,64 @@ +import { DxcDataGrid, DxcInset } from "@dxc-technology/halstack-react"; +import { useState } from "react"; + +const code = `() => { + const columns = [ + { + key: "id", + label: "ID", + resizable: true, + sortable: true, + draggable: true, + }, + { + key: "complete", + label: "% Complete", + resizable: true, + sortable: true, + draggable: true, + alignment: "center", + }, + ]; + + const rows = [ + { + id: 1, + complete: 46, + }, + { + id: 2, + complete: 51, + }, + { + id: 3, + complete: 40, + }, + { + id: 4, + complete: 10, + }, + + ]; + + const [selectedRows, setSelectedRows] = useState(new Set()); + return ( + + + + ); +}`; + +const scope = { + DxcDataGrid, + DxcInset, + useState, +}; + +export default { code, scope }; diff --git a/apps/website/screens/components/data-grid/specs/DataGridSpecsPage.tsx b/apps/website/screens/components/data-grid/specs/DataGridSpecsPage.tsx new file mode 100644 index 000000000..392b627ad --- /dev/null +++ b/apps/website/screens/components/data-grid/specs/DataGridSpecsPage.tsx @@ -0,0 +1,61 @@ +import { DxcParagraph, DxcBulletedList, DxcTable, DxcFlex, DxcLink } from "@dxc-technology/halstack-react"; +import Image from "@/common/Image"; +import Link from "next/link"; +import QuickNavContainer from "@/common/QuickNavContainer"; +import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; +import DocFooter from "@/common/DocFooter"; +import Figure from "@/common/Figure"; +import Code from "@/common/Code"; + +const sections = [ + { + title: "Date input", + content: <>TODO, + }, + { + title: "Design tokens", + subSections: [ + { + title: "TODO", + content: ( + + + + Component token + Element + Core token + Value + + + + + + + + ), + }, + ], + }, + { + title: "Accessibility", + subSections: [ + { + title: "WCAG 2.2", + content: <>, + }, + ], + }, +]; + +const DataGridSpecsPage = () => { + return ( + + + + + + + ); +}; + +export default DataGridSpecsPage; diff --git a/apps/website/screens/components/data-grid/usage/DataGridUsagePage.tsx b/apps/website/screens/components/data-grid/usage/DataGridUsagePage.tsx new file mode 100644 index 000000000..343e403d4 --- /dev/null +++ b/apps/website/screens/components/data-grid/usage/DataGridUsagePage.tsx @@ -0,0 +1,28 @@ +import { DxcBulletedList, DxcFlex } from "@dxc-technology/halstack-react"; +import QuickNavContainer from "@/common/QuickNavContainer"; +import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; +import DocFooter from "@/common/DocFooter"; + +const sections = [ + { + title: "Usage", + content: ( + + Use the data grid (TODO). + + ), + }, +]; + +const DataGridUsagePage = () => { + return ( + + + + + + + ); +}; + +export default DataGridUsagePage; diff --git a/packages/lib/src/data-grid/DataGrid.stories.tsx b/packages/lib/src/data-grid/DataGrid.stories.tsx index c59237898..e7fb83cb5 100644 --- a/packages/lib/src/data-grid/DataGrid.stories.tsx +++ b/packages/lib/src/data-grid/DataGrid.stories.tsx @@ -26,7 +26,7 @@ export default { const columns: GridColumn[] = [ { key: "id", - name: "ID", + label: "ID", resizable: true, sortable: true, draggable: false, @@ -34,7 +34,7 @@ const columns: GridColumn[] = [ }, { key: "task", - name: "Title", + label: "Title", resizable: true, sortable: true, draggable: true, @@ -43,7 +43,7 @@ const columns: GridColumn[] = [ }, { key: "complete", - name: " % Complete", + label: " % Complete", resizable: true, sortable: true, draggable: true, @@ -52,7 +52,7 @@ const columns: GridColumn[] = [ }, { key: "priority", - name: "Priority", + label: "Priority", resizable: true, draggable: true, alignment: "center", @@ -139,14 +139,14 @@ const expandableRows = [ const childcolumns: GridColumn[] = [ { key: "name", - name: "Name", + label: "Name", resizable: true, sortable: true, alignment: "center", }, { key: "value", - name: "Value", + label: "Value", resizable: true, sortable: true, draggable: true, @@ -214,7 +214,7 @@ const childRows: HierarchyGridRow[] = [ }, ], }, -]; +] as HierarchyGridRow[]; export const Chromatic = () => { const [selectedRows, setSelectedRows] = useState((): Set => new Set()); diff --git a/packages/lib/src/data-grid/DataGrid.test.tsx b/packages/lib/src/data-grid/DataGrid.test.tsx index 4a5933232..70fef67ba 100644 --- a/packages/lib/src/data-grid/DataGrid.test.tsx +++ b/packages/lib/src/data-grid/DataGrid.test.tsx @@ -1,13 +1,12 @@ import { findAllByRole, fireEvent, getAllByRole, render, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import DxcDataGrid from "./DataGrid"; -import { screen } from "@storybook/test"; -import { GridColumn } from "./types"; +import { GridColumn, HierarchyGridRow } from "./types"; const columns: GridColumn[] = [ { key: "id", - name: "ID", + label: "ID", resizable: true, sortable: true, draggable: false, @@ -15,7 +14,7 @@ const columns: GridColumn[] = [ }, { key: "complete", - name: " % Complete", + label: " % Complete", resizable: true, sortable: true, draggable: true, @@ -50,6 +49,45 @@ const expandableRows = [ }, ]; +const rowsWithChildren: HierarchyGridRow[] = [ + { + id: 1, + complete: 46, + childRows: [ + { + id: 1.1, + complete: 46, + childRows: [ + { + id: "1.1a", + complete: 46, + }, + ], + }, + { + complete: 46, + value: 1.2, + }, + ], + }, + { + id: 2, + complete: 51, + }, + { + id: 3, + complete: 40, + }, + { + id: 4, + complete: 10, + }, + { + id: 5, + complete: 1, + }, +] as HierarchyGridRow[]; + describe("Data grid component tests", () => { beforeAll(() => { global.CSS = { diff --git a/packages/lib/src/data-grid/DataGrid.tsx b/packages/lib/src/data-grid/DataGrid.tsx index 463754918..6df93264b 100644 --- a/packages/lib/src/data-grid/DataGrid.tsx +++ b/packages/lib/src/data-grid/DataGrid.tsx @@ -53,9 +53,7 @@ const DxcDataGrid = ({ maxWidth: 50, width: 50, colSpan(args) { - return args.type === "ROW" && args.row.isExpandedChildContent - ? columns.length + (selectable ? 1 : 0) - : undefined; + return args.type === "ROW" && args.row.isExpandedChildContent ? columns.length + 1 : undefined; }, renderCell({ row }) { if (row.isExpandedChildContent) { @@ -65,7 +63,7 @@ const DxcDataGrid = ({ // if row has expandable content return ( - {renderExpandableTrigger(row, rowsToRender, uniqueRowId, setRowsToRender)} + {row.expandedContent && renderExpandableTrigger(row, rowsToRender, uniqueRowId, setRowsToRender)} ); }, @@ -203,11 +201,12 @@ const DxcDataGrid = ({ ) { return row.expandedContentHeight; } - return 36; + return 60; }} selectedRows={selectedRows} bottomSummaryRows={summaryRow ? [summaryRow] : undefined} - headerRowHeight={48} + headerRowHeight={60} + summaryRowHeight={60} className="fill-grid" /> @@ -233,7 +232,7 @@ const HierarchyContainer = styled.div<{ border: 0px; cursor: pointer; width: 100%; - height: 36px; + height: 60px; text-align: left; } `; diff --git a/packages/lib/src/data-grid/types.ts b/packages/lib/src/data-grid/types.ts index d372368b6..9e69d1c27 100644 --- a/packages/lib/src/data-grid/types.ts +++ b/packages/lib/src/data-grid/types.ts @@ -1,11 +1,11 @@ export type GridColumn = { key: string; - name: string; + label: string; resizable?: boolean; sortable?: boolean; draggable?: boolean; textEditable?: boolean; - summaryKeyToRender?: string; + summaryKey?: string; alignment?: "left" | "right" | "center"; }; @@ -14,8 +14,7 @@ export type GridRow = { }; export type HierarchyGridRow = GridRow & { - childRows?: HierarchyGridRow[]; - rowLevel?: number; + childRows?: HierarchyGridRow[] | GridRow[]; }; export type ExpandableGridRow = GridRow & { @@ -57,7 +56,7 @@ type Props = | ({ columns: GridColumn[]; rows: GridRow[]; - expandable?: never; + expandable?: never | false; summaryRow?: GridRow; } & SelectableGridProps) | (ExpandableRows & SelectableGridProps) diff --git a/packages/lib/src/data-grid/utils.tsx b/packages/lib/src/data-grid/utils.tsx index e26c198a4..151504668 100644 --- a/packages/lib/src/data-grid/utils.tsx +++ b/packages/lib/src/data-grid/utils.tsx @@ -35,7 +35,7 @@ const checkboxTheme = { export const convertToRDGColumns = (gridColumn: GridColumn, summaryRow?: GridRow): Column => { return { key: gridColumn.key, - name: gridColumn.name, + name: gridColumn.label, resizable: gridColumn.resizable, sortable: gridColumn.sortable, draggable: gridColumn.draggable, @@ -50,9 +50,9 @@ export const convertToRDGColumns = (gridColumn: GridColumn, summaryRow?: GridRow ); }, renderSummaryCell: () => { - return gridColumn.summaryKeyToRender ? ( + return gridColumn.summaryKey ? (
- {summaryRow?.[gridColumn.summaryKeyToRender]} + {summaryRow?.[gridColumn.summaryKey]}
) : undefined; }, @@ -128,7 +128,8 @@ export const renderHierarchyTrigger = ( if (!triggerRow.visibleChildren) { const rowIndex = rows.findIndex((rowToRender) => triggerRow === rowToRender); triggerRow.childRows?.map((childRow: HierarchyGridRow, index: number) => { - childRow.rowLevel = triggerRow.rowLevel ? triggerRow.rowLevel + 1 : 1; + childRow.rowLevel = + triggerRow.rowLevel && typeof triggerRow.rowLevel === "number" ? triggerRow.rowLevel + 1 : 1; childRow.parentKey = rowKeyGetter(triggerRow, uniqueRowId); addRow(newRowsToRender, rowIndex + 1 + index, childRow); }); From fc32ba4f81b0c706e14aee5cbea4e03bdba8c6e5 Mon Sep 17 00:00:00 2001 From: Jialecl Date: Fri, 2 Aug 2024 13:43:21 +0200 Subject: [PATCH 11/20] Summary row added back to test --- packages/lib/src/data-grid/DataGrid.stories.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/lib/src/data-grid/DataGrid.stories.tsx b/packages/lib/src/data-grid/DataGrid.stories.tsx index e7fb83cb5..e62acc4b5 100644 --- a/packages/lib/src/data-grid/DataGrid.stories.tsx +++ b/packages/lib/src/data-grid/DataGrid.stories.tsx @@ -48,7 +48,7 @@ const columns: GridColumn[] = [ sortable: true, draggable: true, alignment: "right", - summaryKeyToRender: "label", + summaryKey: "label", }, { key: "priority", @@ -56,7 +56,7 @@ const columns: GridColumn[] = [ resizable: true, draggable: true, alignment: "center", - summaryKeyToRender: "total", + summaryKey: "total", }, ]; From 664e4b7d4059161e6b160c6ace58464bc5d2e3d3 Mon Sep 17 00:00:00 2001 From: Jialecl Date: Mon, 5 Aug 2024 12:13:19 +0200 Subject: [PATCH 12/20] fixed typos and added WAI-ARIA link --- apps/website/pages/components/data-grid/index.tsx | 2 +- .../pages/components/data-grid/specifications.tsx | 2 +- .../data-grid/specs/DataGridSpecsPage.tsx | 15 +++++++++++++-- packages/lib/src/data-grid/DataGrid.tsx | 2 +- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/apps/website/pages/components/data-grid/index.tsx b/apps/website/pages/components/data-grid/index.tsx index b1e525180..7dda5ffb7 100644 --- a/apps/website/pages/components/data-grid/index.tsx +++ b/apps/website/pages/components/data-grid/index.tsx @@ -7,7 +7,7 @@ const Usage = () => { return ( <> - Date Input — Halstack Design System + Data Grid — Halstack Design System diff --git a/apps/website/pages/components/data-grid/specifications.tsx b/apps/website/pages/components/data-grid/specifications.tsx index 8f3e3fdd7..76a440f79 100644 --- a/apps/website/pages/components/data-grid/specifications.tsx +++ b/apps/website/pages/components/data-grid/specifications.tsx @@ -7,7 +7,7 @@ const Specifications = () => { return ( <> - Date Input Specs — Halstack Design System + Data Grid Specs — Halstack Design System diff --git a/apps/website/screens/components/data-grid/specs/DataGridSpecsPage.tsx b/apps/website/screens/components/data-grid/specs/DataGridSpecsPage.tsx index 392b627ad..e82e98cbd 100644 --- a/apps/website/screens/components/data-grid/specs/DataGridSpecsPage.tsx +++ b/apps/website/screens/components/data-grid/specs/DataGridSpecsPage.tsx @@ -40,8 +40,19 @@ const sections = [ title: "Accessibility", subSections: [ { - title: "WCAG 2.2", - content: <>, + title: "WAI-ARIA", + content: ( + <> + + + WAI-ARIA authoring practices -{" "} + + Data Grid Examples + + + + + ), }, ], }, diff --git a/packages/lib/src/data-grid/DataGrid.tsx b/packages/lib/src/data-grid/DataGrid.tsx index 6df93264b..66002edcd 100644 --- a/packages/lib/src/data-grid/DataGrid.tsx +++ b/packages/lib/src/data-grid/DataGrid.tsx @@ -225,7 +225,7 @@ const ActionContainer = styled.div` const HierarchyContainer = styled.div<{ level: number; }>` - padding-left: calc(10px * ${(props) => props.level}); + padding-left: calc(24px * ${(props) => props.level}); button { padding: 0px; background: transparent; From 46423a606bb4519145506c73fc789e6a878104e9 Mon Sep 17 00:00:00 2001 From: Jialecl Date: Tue, 6 Aug 2024 13:12:31 +0200 Subject: [PATCH 13/20] Added reduced and onGridRowChange, improved styles and typing --- .../lib/src/data-grid/DataGrid.stories.tsx | 15 ++++- packages/lib/src/data-grid/DataGrid.tsx | 57 ++++++++++++------- packages/lib/src/data-grid/types.ts | 16 +++--- packages/lib/src/data-grid/utils.tsx | 6 +- 4 files changed, 60 insertions(+), 34 deletions(-) diff --git a/packages/lib/src/data-grid/DataGrid.stories.tsx b/packages/lib/src/data-grid/DataGrid.stories.tsx index e62acc4b5..c567f5e89 100644 --- a/packages/lib/src/data-grid/DataGrid.stories.tsx +++ b/packages/lib/src/data-grid/DataGrid.stories.tsx @@ -9,7 +9,7 @@ import preview from "../../.storybook/preview"; import { userEvent, within } from "@storybook/test"; export default { - title: "Datagrid", + title: "Data Grid", component: DxcDataGrid, parameters: { a11y: { @@ -276,6 +276,19 @@ export const Chromatic = () => { uniqueRowId="id" />
+ + + <DxcDataGrid + columns={childcolumns} + rows={childRows} + uniqueRowId="value" + selectable + selectedRows={selectedChildRows} + onSelectRows={setSelectedChildRows} + summaryRow={{ label: "Total", total: 100 }} + mode="reduced" + /> + </ExampleContainer> </> ); }; diff --git a/packages/lib/src/data-grid/DataGrid.tsx b/packages/lib/src/data-grid/DataGrid.tsx index 66002edcd..d2205371e 100644 --- a/packages/lib/src/data-grid/DataGrid.tsx +++ b/packages/lib/src/data-grid/DataGrid.tsx @@ -27,6 +27,8 @@ const DxcDataGrid = ({ selectedRows, uniqueRowId, summaryRow, + mode = "default", + onGridRowsChange, }: DataGridPropsType): JSX.Element => { const [rowsToRender, setRowsToRender] = useState<GridRow[] | HierarchyGridRow[] | ExpandableGridRow[]>(rows); // Proccess columns prop into usable columns based on other props @@ -62,7 +64,7 @@ const DxcDataGrid = ({ } // if row has expandable content return ( - <ActionContainer> + <ActionContainer id="action"> {row.expandedContent && renderExpandableTrigger(row, rowsToRender, uniqueRowId, setRowsToRender)} </ActionContainer> ); @@ -79,13 +81,13 @@ const DxcDataGrid = ({ renderCell({ row }) { if (row.childRows?.length) { return ( - <HierarchyContainer level={row.rowLevel || 0}> + <HierarchyContainer level={row.rowLevel || 0} mode={mode}> {renderHierarchyTrigger(rowsToRender, row, uniqueRowId, firstColumnKey, setRowsToRender)} </HierarchyContainer> ); } return ( - <HierarchyContainer level={row.rowLevel || 0} className="ellipsis-cell"> + <HierarchyContainer level={row.rowLevel || 0} className="ellipsis-cell" mode={mode}> {row[firstColumnKey]} </HierarchyContainer> ); @@ -102,13 +104,17 @@ const DxcDataGrid = ({ renderCell({ row }) { if (!row.isExpandedChildContent) { return ( - <ActionContainer>{renderCheckbox(rows, row, uniqueRowId, selectedRows, onSelectRows)}</ActionContainer> + <ActionContainer id="action"> + {renderCheckbox(rows, row, uniqueRowId, selectedRows, onSelectRows)} + </ActionContainer> ); } }, renderHeaderCell: () => { return ( - <ActionContainer>{renderHeaderCheckbox(rows, uniqueRowId, selectedRows, onSelectRows)}</ActionContainer> + <ActionContainer id="action"> + {renderHeaderCheckbox(rows, uniqueRowId, selectedRows, onSelectRows)} + </ActionContainer> ); }, }, @@ -146,10 +152,9 @@ const DxcDataGrid = ({ }); }; - const onRowsChange = (newRows: { [key: string]: string | number | React.ReactNode }[]) => { + const onRowsChange = (newRows: GridRow[] | HierarchyGridRow[] | ExpandableGridRow[]) => { // call function to change rows, like when they have been edited - console.log("new rows: "); - console.log(newRows); + onGridRowsChange(newRows); }; const sortedRows = useMemo((): readonly GridRow[] => { @@ -201,12 +206,12 @@ const DxcDataGrid = ({ ) { return row.expandedContentHeight; } - return 60; + return mode === "default" ? 60 : 36; }} selectedRows={selectedRows} bottomSummaryRows={summaryRow ? [summaryRow] : undefined} - headerRowHeight={60} - summaryRowHeight={60} + headerRowHeight={mode === "default" ? 60 : 36} + summaryRowHeight={mode === "default" ? 60 : 36} className="fill-grid" /> </DataGridContainer> @@ -224,16 +229,22 @@ const ActionContainer = styled.div` const HierarchyContainer = styled.div<{ level: number; + mode: "default" | "reduced"; }>` padding-left: calc(24px * ${(props) => props.level}); button { + display: flex; + align-items: center; + gap: 0.5rem; padding: 0px; - background: transparent; border: 0px; - cursor: pointer; width: 100%; - height: 60px; + height: ${(props) => (props.mode === "default" ? 60 : 36)}px; + background: transparent; text-align: left; + font-size: 14px; + font-family: inherit; + cursor: pointer; } `; @@ -244,15 +255,20 @@ const DataGridContainer = styled.div` height: 100%; border: 0px; } + .rdg-cell { + padding: 0px 24px; + font-family: "Open Sans", sans-serif; + font-size: 0.875rem; + font-style: normal; + font-weight: 400; + } + .rdg-cell:has(> #action) { + padding: 0px; + } .rdg-header-row { background-color: rgb(95, 36, 159); .rdg-cell { - font-family: "Open Sans", sans-serif; - font-size: 0.875rem; - font-style: normal; - font-weight: 400; color: rgb(255, 255, 255); - text-transform: none; .sortIconContainer { margin-left: 0.5rem; display: flex; @@ -275,9 +291,8 @@ const DataGridContainer = styled.div` .rdg-summary-row { background-color: #fafafa; .rdg-cell { - border-top: 0px; + border: 0px; font-weight: 600; - border-right: 0px; } } .rdg-row { diff --git a/packages/lib/src/data-grid/types.ts b/packages/lib/src/data-grid/types.ts index 9e69d1c27..c7e2c0347 100644 --- a/packages/lib/src/data-grid/types.ts +++ b/packages/lib/src/data-grid/types.ts @@ -23,19 +23,15 @@ export type ExpandableGridRow = GridRow & { }; export type ExpandableRows = { - columns: GridColumn[]; rows: ExpandableGridRow[]; expandable: true; uniqueRowId: string; - summaryRow?: GridRow; }; export type HierarchyRows = { - columns: GridColumn[]; rows: HierarchyGridRow[]; uniqueRowId: string; expandable?: false; - summaryRow?: GridRow; }; export type SelectableGridProps = @@ -52,14 +48,18 @@ export type SelectableGridProps = uniqueRowId?: string; }; -type Props = +type Props = { + mode?: "default" | "reduced"; + columns: GridColumn[]; + summaryRow?: GridRow; + onGridRowsChange?: (rows: GridRow[] | HierarchyGridRow[] | ExpandableGridRow[]) => void; +} & ( | ({ - columns: GridColumn[]; rows: GridRow[]; expandable?: never | false; - summaryRow?: GridRow; } & SelectableGridProps) | (ExpandableRows & SelectableGridProps) - | (HierarchyRows & SelectableGridProps); + | (HierarchyRows & SelectableGridProps) +); export default Props; diff --git a/packages/lib/src/data-grid/utils.tsx b/packages/lib/src/data-grid/utils.tsx index 151504668..066c7797d 100644 --- a/packages/lib/src/data-grid/utils.tsx +++ b/packages/lib/src/data-grid/utils.tsx @@ -165,10 +165,8 @@ export const renderHierarchyTrigger = ( setRowsToRender(newRowsToRender); }} > - <DxcFlex gap="0.5rem"> - <DxcIcon icon={triggerRow.visibleChildren ? "Keyboard_Arrow_Down" : "Chevron_Right"} /> - {triggerRow[columnKey]} - </DxcFlex> + <DxcIcon icon={triggerRow.visibleChildren ? "Keyboard_Arrow_Down" : "Chevron_Right"} /> + {triggerRow[columnKey]} </button> ); }; From 89b4edc097797599f6b730ee5b0e197ec8c5361b Mon Sep 17 00:00:00 2001 From: Jialecl <jialestrabajos@gmail.com> Date: Wed, 7 Aug 2024 12:03:31 +0200 Subject: [PATCH 14/20] tokens added to the dataGrid + themeGenerator previews --- .../screens/common/themes/advanced-theme.json | 30 +++ .../common/themes/opinionated-theme.json | 5 + .../components/ComponentsPreviewMap.ts | 5 + .../components/previews/DataGrid.tsx | 235 ++++++++++++++++++ .../themes/schemas/advanced.schema.json | 30 +++ .../themes/schemas/opinionated.schema.json | 5 + packages/lib/src/HalstackContext.tsx | 5 + packages/lib/src/common/variables.ts | 63 +++++ packages/lib/src/data-grid/DataGrid.tsx | 132 ++++++---- 9 files changed, 457 insertions(+), 53 deletions(-) create mode 100644 apps/website/screens/theme-generator/components/previews/DataGrid.tsx diff --git a/apps/website/screens/common/themes/advanced-theme.json b/apps/website/screens/common/themes/advanced-theme.json index 07dc71315..498ed858a 100644 --- a/apps/website/screens/common/themes/advanced-theme.json +++ b/apps/website/screens/common/themes/advanced-theme.json @@ -350,6 +350,36 @@ "iconColor": "#333333", "iconSize": "16px" }, + "dataGrid": { + "rowSeparatorThickness": "1px", + "rowSeparatorStyle": "solid", + "rowSeparatorColor": "#cccccc", + "dataBackgroundColor": "#ffffff", + "dataFontFamily": "Open Sans, sans-serif", + "dataFontSize": "0.875rem", + "dataFontStyle": "normal", + "dataFontWeight": "400", + "dataFontColor": "#000000", + "dataFontTextTransform": "none", + "dataPaddingRight": "1.5rem", + "dataPaddingLeft": "1.5rem", + "dataPaddingRightReduced": "1rem", + "dataPaddingLeftReduced": "1rem", + "dataTextLineHeight": "normal", + "headerBackgroundColor": "#5f249f", + "headerBorderRadius": "4px", + "headerFontFamily": "Open Sans, sans-serif", + "headerFontSize": "0.875rem", + "headerFontStyle": "normal", + "headerFontWeight": "400", + "headerFontColor": "#ffffff", + "headerFontTextTransform": "none", + "headerPaddingRight": "1.4rem", + "headerPaddingLeft": "1.5rem", + "headerPaddingRightReduced": "1rem", + "headerPaddingLeftReduced": "1rem", + "headerTextLineHeight": "normal" + }, "dateInput": { "pickerBackgroundColor": "#ffffff", "pickerFontColor": "#000000", diff --git a/apps/website/screens/common/themes/opinionated-theme.json b/apps/website/screens/common/themes/opinionated-theme.json index 68712a8eb..8e31e4f4d 100644 --- a/apps/website/screens/common/themes/opinionated-theme.json +++ b/apps/website/screens/common/themes/opinionated-theme.json @@ -33,6 +33,11 @@ "fontColor": "#333333", "iconColor": "#333333" }, + "dataGrid": { + "baseColor": "#5f249f", + "headerFontColor": "#ffffff", + "cellFontColor": "#000000" + }, "dateInput": { "baseColor": "#5f249f", "selectedFontColor": "#ffffff" diff --git a/apps/website/screens/theme-generator/components/ComponentsPreviewMap.ts b/apps/website/screens/theme-generator/components/ComponentsPreviewMap.ts index 4eff58ffb..fa0ac7468 100644 --- a/apps/website/screens/theme-generator/components/ComponentsPreviewMap.ts +++ b/apps/website/screens/theme-generator/components/ComponentsPreviewMap.ts @@ -33,6 +33,7 @@ import BulletedListPreview from "./previews/BulletedList"; import ParagraphPreview from "./previews/Paragraph"; import NavTabsPreview from "./previews/NavTabs"; import ContextualMenu from "./previews/ContextualMenu"; +import DataGridPreview from "./previews/DataGrid"; const SampleComponents = [ { @@ -71,6 +72,10 @@ const SampleComponents = [ name: "contextualMenu", preview: ContextualMenu, }, + { + name: "dataGrid", + preview: DataGridPreview, + }, { name: "dateInput", preview: DateInputPreview, diff --git a/apps/website/screens/theme-generator/components/previews/DataGrid.tsx b/apps/website/screens/theme-generator/components/previews/DataGrid.tsx new file mode 100644 index 000000000..ddb03a2f8 --- /dev/null +++ b/apps/website/screens/theme-generator/components/previews/DataGrid.tsx @@ -0,0 +1,235 @@ +import React, { useState } from "react"; +import { DxcContainer, DxcDataGrid } from "@dxc-technology/halstack-react"; +import Mode from "../Mode"; +import PreviewContainer from "./PreviewContainer"; + +const columns = [ + { + key: "id", + label: "ID", + resizable: true, + sortable: true, + draggable: false, + alignment: "left", + }, + { + key: "task", + label: "Title", + resizable: true, + sortable: true, + draggable: true, + textEditable: true, + alignment: "left", + }, + { + key: "complete", + label: " % Complete", + resizable: true, + sortable: true, + draggable: true, + alignment: "right", + summaryKey: "label", + }, + { + key: "priority", + label: "Priority", + resizable: true, + draggable: true, + alignment: "center", + summaryKey: "total", + }, +]; + +const expandableRows = [ + { + id: 1, + task: "Task 1", + complete: 46, + priority: "High", + issueType: "Bug", + expandedContent: <DxcContainer> Custom content 1</DxcContainer>, + }, + { + id: 2, + task: "Task 2", + complete: 51, + priority: "High", + issueType: "Epic", + expandedContent: <DxcContainer> Custom content 2</DxcContainer>, + }, + { + id: 3, + task: "Task 3", + complete: 40, + priority: "High", + issueType: "Improvement", + expandedContent: <DxcContainer> Custom content 3</DxcContainer>, + }, + { + id: 4, + task: "Task 4", + complete: 10, + priority: "High", + issueType: "Improvement", + expandedContent: <DxcContainer> Custom content 4</DxcContainer>, + }, + { + id: 5, + task: "Task 5", + complete: 68, + priority: "High", + issueType: "Improvement", + expandedContent: <DxcContainer> Custom content 5</DxcContainer>, + }, + { + id: 6, + task: "Task 6", + complete: 37, + priority: "High", + issueType: "Improvement", + expandedContent: <DxcContainer> Custom content 6</DxcContainer>, + }, + { + id: 7, + task: "Task 7", + complete: 73, + priority: "Medium", + issueType: "Story", + expandedContent: <DxcContainer> Custom content 7</DxcContainer>, + }, + { + id: 8, + task: "Task 8", + complete: 27, + priority: "Medium", + issueType: "Story", + expandedContent: <DxcContainer> Custom content 8</DxcContainer>, + }, + { + id: 9, + task: "Task 9", + complete: 36, + priority: "Critical", + issueType: "Epic", + expandedContent: <DxcContainer> Custom content 9</DxcContainer>, + }, +]; + +const childcolumns = [ + { + key: "name", + label: "Name", + resizable: true, + sortable: true, + alignment: "center", + }, + { + key: "value", + label: "Value", + resizable: true, + sortable: true, + draggable: true, + textEditable: true, + alignment: "right", + }, +]; + +const childRows = [ + { + name: "Root Node 1", + value: "1", + id: "a", + childRows: [ + { + name: "Child Node 1.1", + value: "1.1", + id: "aa", + childRows: [ + { + name: "Grandchild Node 1.1.1", + value: "1.1.1", + id: "aaa", + }, + { + name: "Grandchild Node 1.1.2", + value: "1.1.2", + id: "aab", + }, + ], + }, + { + name: "Child Node 1.2", + value: "1.2", + id: "ab", + }, + ], + }, + { + name: "Root Node 2", + value: "2", + id: "b", + childRows: [ + { + name: "Child Node 2.1", + value: "2.1", + id: "ba", + childRows: [ + { + name: "Grandchild Node 2.1.1", + value: "2.1.1", + id: "baa", + }, + ], + }, + { + name: "Child Node 2.2", + value: "2.2", + id: "bb", + }, + { + name: "Child Node 2.3", + value: "2.3", + id: "bc", + }, + ], + }, +]; + +const DataGridPreview = () => { + const [selectedRows, setSelectedRows] = useState((): Set<number | string> => new Set()); + + return ( + <PreviewContainer> + <Mode text="Default"> + <DxcDataGrid columns={columns} rows={expandableRows} uniqueRowId="id" /> + </Mode> + <Mode text="Expandable"> + <DxcDataGrid columns={columns} rows={expandableRows} uniqueRowId="id" expandable /> + </Mode> + <Mode text="Selectable"> + <DxcDataGrid + columns={columns} + rows={expandableRows} + uniqueRowId="task" + selectable + selectedRows={selectedRows} + onSelectRows={setSelectedRows} + /> + </Mode> + <Mode text="With child rows"> + <DxcDataGrid columns={childcolumns} rows={childRows} uniqueRowId="id" /> + </Mode> + <Mode text="With summary row"> + <DxcDataGrid + columns={childcolumns} + rows={childRows} + uniqueRowId="value" + summaryRow={{ label: "Total", total: 100, value: "summary" }} + mode="reduced" + /> + </Mode> + </PreviewContainer> + ); +}; + +export default DataGridPreview; diff --git a/apps/website/screens/theme-generator/themes/schemas/advanced.schema.json b/apps/website/screens/theme-generator/themes/schemas/advanced.schema.json index 76b97d5aa..4df723fa1 100644 --- a/apps/website/screens/theme-generator/themes/schemas/advanced.schema.json +++ b/apps/website/screens/theme-generator/themes/schemas/advanced.schema.json @@ -350,6 +350,36 @@ "iconColor": "color", "iconSize": "length" }, + "dataGrid": { + "rowSeparatorThickness": "length", + "rowSeparatorStyle": "bStyle", + "rowSeparatorColor": "color", + "dataBackgroundColor": "color", + "dataFontFamily": "fFamily", + "dataFontSize": "length", + "dataFontStyle": "fStyle", + "dataFontWeight": "fWeight", + "dataFontColor": "color", + "dataFontTextTransform": "fTextTransform", + "dataPaddingRight": "length", + "dataPaddingLeft": "length", + "dataPaddingRightReduced": "length", + "dataPaddingLeftReduced": "length", + "dataTextLineHeight": "length", + "headerBackgroundColor": "color", + "headerBorderRadius": "length", + "headerFontFamily": "fFamily", + "headerFontSize": "length", + "headerFontStyle": "fStyle", + "headerFontWeight": "fWeight", + "headerFontColor": "color", + "headerFontTextTransform": "fTextTransform", + "headerPaddingRight": "length", + "headerPaddingLeft": "length", + "headerPaddingRightReduced": "length", + "headerPaddingLeftReduced": "length", + "headerTextLineHeight": "length" + }, "dateInput": { "pickerBackgroundColor": "color", "pickerFontColor": "color", diff --git a/apps/website/screens/theme-generator/themes/schemas/opinionated.schema.json b/apps/website/screens/theme-generator/themes/schemas/opinionated.schema.json index 1d830c018..04b6b21f8 100644 --- a/apps/website/screens/theme-generator/themes/schemas/opinionated.schema.json +++ b/apps/website/screens/theme-generator/themes/schemas/opinionated.schema.json @@ -33,6 +33,11 @@ "fontColor": "color", "iconColor": "color" }, + "dataGrid": { + "baseColor": "color", + "headerFontColor": "color", + "cellFontColor": "color" + }, "dateInput": { "baseColor": "color", "selectedFontColor": "color" diff --git a/packages/lib/src/HalstackContext.tsx b/packages/lib/src/HalstackContext.tsx index 6c8cc9dc0..b5659166d 100644 --- a/packages/lib/src/HalstackContext.tsx +++ b/packages/lib/src/HalstackContext.tsx @@ -137,6 +137,11 @@ const parseTheme = (theme: DeepPartial<OpinionatedTheme>): AdvancedTheme => { contextualMenu.sectionTitleFontColor = theme?.contextualMenu?.fontColor ?? contextualMenu.sectionTitleFontColor; contextualMenu.iconColor = theme?.contextualMenu?.iconColor ?? contextualMenu.iconColor; + const dataGridTokens = componentTokensCopy.dataGrid; + dataGridTokens.headerBackgroundColor = theme?.dataGrid?.baseColor ?? dataGridTokens.headerBackgroundColor; + dataGridTokens.headerFontColor = theme?.dataGrid?.headerFontColor ?? dataGridTokens.headerFontColor; + dataGridTokens.dataFontColor = theme?.dataGrid?.cellFontColor ?? dataGridTokens.dataFontColor; + const dateTokens = componentTokensCopy.dateInput; dateTokens.pickerSelectedBackgroundColor = theme?.dateInput?.baseColor ?? dateTokens.pickerSelectedBackgroundColor; dateTokens.pickerSelectedFontColor = theme?.dateInput?.selectedFontColor ?? dateTokens.pickerSelectedFontColor; diff --git a/packages/lib/src/common/variables.ts b/packages/lib/src/common/variables.ts index 0e44fdd4d..68364b041 100644 --- a/packages/lib/src/common/variables.ts +++ b/packages/lib/src/common/variables.ts @@ -352,6 +352,64 @@ export const componentTokens = { iconColor: CoreTokens.color_grey_900, iconSize: "16px", }, + dataGrid: { + rowSeparatorThickness: "1px", + rowSeparatorStyle: CoreTokens.border_solid, + rowSeparatorColor: CoreTokens.color_grey_300, + dataBackgroundColor: CoreTokens.color_white, + dataFontFamily: CoreTokens.type_sans, + dataFontSize: CoreTokens.type_scale_02, + dataFontStyle: CoreTokens.type_normal, + dataFontWeight: CoreTokens.type_regular, + dataFontColor: CoreTokens.color_black, + dataFontTextTransform: "none", + // dataPaddingTop: CoreTokens.spacing_16,? + // dataPaddingBottom: CoreTokens.spacing_16,? + dataPaddingRight: CoreTokens.spacing_24, + dataPaddingLeft: CoreTokens.spacing_24, + // dataPaddingTopReduced: CoreTokens.spacing_8,? + // dataPaddingBottomReduced: CoreTokens.spacing_8,? + dataPaddingRightReduced: CoreTokens.spacing_16, + dataPaddingLeftReduced: CoreTokens.spacing_16, + // dataTextAlign: "left",? + dataTextLineHeight: "normal", + // firstChildPaddingLeft: CoreTokens.spacing_24,? + // lastChildPaddingRight: CoreTokens.spacing_24,? + // firstChildPaddingLeftReduced: "20px",? + // lastChildPaddingRightReduced: "20px",? + headerBackgroundColor: CoreTokens.color_purple_700, + headerBorderRadius: "4px", + headerFontFamily: CoreTokens.type_sans, + headerFontSize: CoreTokens.type_scale_02, + headerFontStyle: CoreTokens.type_normal, + headerFontWeight: CoreTokens.type_regular, + headerFontColor: CoreTokens.color_white, + headerFontTextTransform: "none", + // headerPaddingTop: CoreTokens.spacing_16,? + // headerPaddingBottom: CoreTokens.spacing_16,? + headerPaddingRight: CoreTokens.spacing_24, + headerPaddingLeft: CoreTokens.spacing_24, + // headerPaddingTopReduced: CoreTokens.spacing_8,? + // headerPaddingBottomReduced: CoreTokens.spacing_8,? + headerPaddingRightReduced: CoreTokens.spacing_16, + headerPaddingLeftReduced: CoreTokens.spacing_16, + // headerTextAlign: "left",? + headerTextLineHeight: "normal", + // missing summary tokens + // scrollBarThumbColor: CoreTokens.color_grey_700, + // scrollBarTrackColor: CoreTokens.color_grey_300, + // sortIconColor: CoreTokens.color_white, + // actionIconColor: CoreTokens.color_purple_700, + // disabledActionIconColor: CoreTokens.color_grey_500, + // hoverActionIconColor: CoreTokens.color_purple_700, + // focusActionIconColor: CoreTokens.color_purple_700, + // activeActionIconColor: CoreTokens.color_purple_700, + // actionBackgroundColor: CoreTokens.color_transparent, + // disabledActionBackgroundColor: CoreTokens.color_transparent, + // hoverActionBackgroundColor: CoreTokens.color_grey_100, + // focusActionBorderColor: CoreTokens.color_blue_600, + // activeActionBackgroundColor: CoreTokens.color_grey_300, + }, dateInput: { pickerBackgroundColor: CoreTokens.color_white, pickerFontColor: CoreTokens.color_black, @@ -1321,6 +1379,11 @@ export type OpinionatedTheme = { fontColor: string; iconColor: string; }; + dataGrid: { + baseColor: string; + headerFontColor: string; + cellFontColor: string; + }; dateInput: { baseColor: string; selectedFontColor: string; diff --git a/packages/lib/src/data-grid/DataGrid.tsx b/packages/lib/src/data-grid/DataGrid.tsx index d2205371e..9d559a8e8 100644 --- a/packages/lib/src/data-grid/DataGrid.tsx +++ b/packages/lib/src/data-grid/DataGrid.tsx @@ -16,7 +16,8 @@ import { renderHierarchyTrigger, renderExpandableTrigger, } from "./utils"; -import styled from "styled-components"; +import styled, { ThemeProvider } from "styled-components"; +import useTheme from "../useTheme"; const DxcDataGrid = ({ columns, @@ -179,9 +180,12 @@ const DxcDataGrid = ({ return rowsToRender; }, [expandable, rowsToRender, sortColumns, uniqueRowId]); + const colorsTheme = useTheme(); + return ( - <DataGridContainer> - {/* {columnsVisibilityFilter && ( + <ThemeProvider theme={colorsTheme.dataGrid}> + <DataGridContainer mode={mode}> + {/* {columnsVisibilityFilter && ( <DxcSelect multiple options={columnsNamesIntoOptions(columns)} @@ -189,32 +193,33 @@ const DxcDataGrid = ({ onChange={(event) => setVisibleColumns(event.value)} /> )} */} - <DataGrid - columns={reorderedColumns} - rows={sortedRows} - onColumnsReorder={onColumnsReorder} - onRowsChange={onRowsChange} - renderers={{ renderSortStatus }} - sortColumns={sortColumns} - onSortColumnsChange={setSortColumns} - rowKeyGetter={(row) => uniqueRowId && rowKeyGetter(row, uniqueRowId)} - rowHeight={(row) => { - if ( - row.isExpandedChildContent && - typeof row.expandedContentHeight === "number" && - row.expandedContentHeight > 0 - ) { - return row.expandedContentHeight; - } - return mode === "default" ? 60 : 36; - }} - selectedRows={selectedRows} - bottomSummaryRows={summaryRow ? [summaryRow] : undefined} - headerRowHeight={mode === "default" ? 60 : 36} - summaryRowHeight={mode === "default" ? 60 : 36} - className="fill-grid" - /> - </DataGridContainer> + <DataGrid + columns={reorderedColumns} + rows={sortedRows} + onColumnsReorder={onColumnsReorder} + onRowsChange={onRowsChange} + renderers={{ renderSortStatus }} + sortColumns={sortColumns} + onSortColumnsChange={setSortColumns} + rowKeyGetter={(row) => uniqueRowId && rowKeyGetter(row, uniqueRowId)} + rowHeight={(row) => { + if ( + row.isExpandedChildContent && + typeof row.expandedContentHeight === "number" && + row.expandedContentHeight > 0 + ) { + return row.expandedContentHeight; + } + return mode === "default" ? 60 : 36; + }} + selectedRows={selectedRows} + bottomSummaryRows={summaryRow ? [summaryRow] : undefined} + headerRowHeight={mode === "default" ? 60 : 36} + summaryRowHeight={mode === "default" ? 60 : 36} + className="fill-grid" + /> + </DataGridContainer> + </ThemeProvider> ); }; @@ -223,7 +228,7 @@ const ActionContainer = styled.div` height: 100%; align-items: center; justify-content: center; - font-size: 24px; + font-size: 14px; width: 100%; `; @@ -231,7 +236,8 @@ const HierarchyContainer = styled.div<{ level: number; mode: "default" | "reduced"; }>` - padding-left: calc(24px * ${(props) => props.level}); + padding-left: ${(props) => + `calc(${props.mode === "reduced" ? props.theme.dataPaddingLeftReduced : props.theme.dataPaddingLeft} * ${props.level})`}; button { display: flex; align-items: center; @@ -242,13 +248,14 @@ const HierarchyContainer = styled.div<{ height: ${(props) => (props.mode === "default" ? 60 : 36)}px; background: transparent; text-align: left; - font-size: 14px; + font-size: ${(props) => props.theme.dataFontSize}; font-family: inherit; + color: inherit; cursor: pointer; } `; -const DataGridContainer = styled.div` +const DataGridContainer = styled.div<{ mode: "default" | "reduced" }>` width: 100%; .rdg { border-radius: 4px; @@ -256,19 +263,44 @@ const DataGridContainer = styled.div` border: 0px; } .rdg-cell { - padding: 0px 24px; - font-family: "Open Sans", sans-serif; - font-size: 0.875rem; - font-style: normal; - font-weight: 400; + display: flex; + align-items: center; + padding: 0px + ${(props) => (props.mode === "default" ? props.theme.dataPaddingRight : props.theme.dataPaddingRightReduced)} 0 + ${(props) => (props.mode === "default" ? props.theme.dataPaddingLeft : props.theme.dataPaddingLeftReduced)}; + font-family: ${(props) => props.theme.dataFontFamily}; + font-size: ${(props) => props.theme.dataFontSize}; + font-style: ${(props) => props.theme.dataFontStyle}; + font-weight: ${(props) => props.theme.dataFontWeight}; + color: ${(props) => props.theme.dataFontColor}; + text-transform: ${(props) => props.theme.dataFontTextTransform}; + line-height: ${(props) => props.theme.dataTextLineHeight}; + border-bottom: ${(props) => + `${props.theme.rowSeparatorThickness} ${props.theme.rowSeparatorStyle} ${props.theme.rowSeparatorColor}`}; + border-right: ${(props) => + `${props.theme.rowSeparatorThickness} ${props.theme.rowSeparatorStyle} ${props.theme.rowSeparatorColor}`}; + background-color: ${(props) => props.theme.dataBackgroundColor}; } .rdg-cell:has(> #action) { padding: 0px; } .rdg-header-row { - background-color: rgb(95, 36, 159); + border-top-left-radius: ${(props) => props.theme.headerBorderRadius}; + border-top-right-radius: ${(props) => props.theme.headerBorderRadius}; .rdg-cell { - color: rgb(255, 255, 255); + font-family: ${(props) => props.theme.headerFontFamily}; + font-size: ${(props) => props.theme.headerFontSize}; + font-style: ${(props) => props.theme.headerFontStyle}; + font-weight: ${(props) => props.theme.headerFontWeight}; + color: ${(props) => props.theme.headerFontColor}; + text-transform: ${(props) => props.theme.headerFontTextTransform}; + padding: 0px + ${(props) => + props.mode === "default" ? props.theme.headerPaddingRight : props.theme.headerPaddingRightReduced} + 0 + ${(props) => (props.mode === "default" ? props.theme.headerPaddingLeft : props.theme.headerPaddingLeftReduced)}; + line-height: ${(props) => props.theme.headerTextLineHeight}; + background-color: ${(props) => props.theme.headerBackgroundColor}; .sortIconContainer { margin-left: 0.5rem; display: flex; @@ -276,18 +308,12 @@ const DataGridContainer = styled.div` align-items: center; } } - .rdg-cell:first-child { - border-top-left-radius: 4px; - } + } + .rdg-row { .rdg-cell:last-child { - border-top-right-radius: 4px; + border-right: 0px; } } - .ellipsis-cell { - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - } .rdg-summary-row { background-color: #fafafa; .rdg-cell { @@ -295,10 +321,10 @@ const DataGridContainer = styled.div` font-weight: 600; } } - .rdg-row { - .rdg-cell:last-child { - border-right: 0px; - } + .ellipsis-cell { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; } .align-left { text-align: left; From 885a67c297bd871fd7086141f92337f62598b42f Mon Sep 17 00:00:00 2001 From: Jialecl <jialestrabajos@gmail.com> Date: Wed, 7 Aug 2024 13:28:49 +0200 Subject: [PATCH 15/20] fixed type error in theme generator preview --- .../theme-generator/components/previews/DataGrid.tsx | 12 ++++++++---- packages/lib/src/index.ts | 1 - 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/website/screens/theme-generator/components/previews/DataGrid.tsx b/apps/website/screens/theme-generator/components/previews/DataGrid.tsx index ddb03a2f8..d245fe520 100644 --- a/apps/website/screens/theme-generator/components/previews/DataGrid.tsx +++ b/apps/website/screens/theme-generator/components/previews/DataGrid.tsx @@ -3,7 +3,11 @@ import { DxcContainer, DxcDataGrid } from "@dxc-technology/halstack-react"; import Mode from "../Mode"; import PreviewContainer from "./PreviewContainer"; -const columns = [ +type DataGridPropsType = React.ComponentProps<typeof DxcDataGrid>; +type DataGridColumnsPropsType = DataGridPropsType["columns"]; +type DataGridRowsPropsType = DataGridPropsType["rows"]; + +const columns: DataGridColumnsPropsType = [ { key: "id", label: "ID", @@ -115,7 +119,7 @@ const expandableRows = [ }, ]; -const childcolumns = [ +const childcolumns: DataGridColumnsPropsType = [ { key: "name", label: "Name", @@ -134,7 +138,7 @@ const childcolumns = [ }, ]; -const childRows = [ +const childRows: DataGridRowsPropsType = [ { name: "Root Node 1", value: "1", @@ -193,7 +197,7 @@ const childRows = [ }, ], }, -]; +] as DataGridRowsPropsType; const DataGridPreview = () => { const [selectedRows, setSelectedRows] = useState((): Set<number | string> => new Set()); diff --git a/packages/lib/src/index.ts b/packages/lib/src/index.ts index 973f57c5a..9a720a501 100644 --- a/packages/lib/src/index.ts +++ b/packages/lib/src/index.ts @@ -46,7 +46,6 @@ import DxcTextarea from "./textarea/Textarea"; import DxcTextInput from "./text-input/TextInput"; import DxcToggleGroup from "./toggle-group/ToggleGroup"; import DxcTooltip from "./tooltip/Tooltip"; - import DxcTypography from "./typography/Typography"; import DxcWizard from "./wizard/Wizard"; From dbe675ee13477cc06d5385c134e1222902c25955 Mon Sep 17 00:00:00 2001 From: Jialecl <jialestrabajos@gmail.com> Date: Thu, 8 Aug 2024 13:40:48 +0200 Subject: [PATCH 16/20] Improved typing based on feedback to make it more readable --- packages/lib/src/data-grid/types.ts | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/lib/src/data-grid/types.ts b/packages/lib/src/data-grid/types.ts index c7e2c0347..389c16786 100644 --- a/packages/lib/src/data-grid/types.ts +++ b/packages/lib/src/data-grid/types.ts @@ -48,18 +48,23 @@ export type SelectableGridProps = uniqueRowId?: string; }; -type Props = { +export type CommonProps = { mode?: "default" | "reduced"; columns: GridColumn[]; summaryRow?: GridRow; onGridRowsChange?: (rows: GridRow[] | HierarchyGridRow[] | ExpandableGridRow[]) => void; -} & ( - | ({ - rows: GridRow[]; - expandable?: never | false; - } & SelectableGridProps) - | (ExpandableRows & SelectableGridProps) - | (HierarchyRows & SelectableGridProps) -); +}; + +export type BasicGridProps = { + rows: GridRow[]; + expandable?: false; +}; + +type Props = CommonProps & + ( + | (BasicGridProps & SelectableGridProps) + | (ExpandableRows & SelectableGridProps) + | (HierarchyRows & SelectableGridProps) + ); export default Props; From 0fe3118cc0ddac0325fdd090ceb5cb469477fa4b Mon Sep 17 00:00:00 2001 From: Jialecl <jialestrabajos@gmail.com> Date: Thu, 8 Aug 2024 13:47:21 +0200 Subject: [PATCH 17/20] removed unused flex --- packages/lib/src/data-grid/utils.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/lib/src/data-grid/utils.tsx b/packages/lib/src/data-grid/utils.tsx index 066c7797d..79c0febd9 100644 --- a/packages/lib/src/data-grid/utils.tsx +++ b/packages/lib/src/data-grid/utils.tsx @@ -1,6 +1,5 @@ import DxcActionIcon from "../action-icon/ActionIcon"; import DxcCheckbox from "../checkbox/Checkbox"; -import DxcFlex from "../flex/Flex"; import { HalstackProvider } from "../HalstackContext"; import DxcIcon from "../icon/Icon"; import { GridColumn, HierarchyGridRow, GridRow, ExpandableGridRow } from "./types"; From a8bfa408491db79a99f8b1099787048bc72c9651 Mon Sep 17 00:00:00 2001 From: Jialecl <jialestrabajos@gmail.com> Date: Fri, 9 Aug 2024 13:02:39 +0200 Subject: [PATCH 18/20] removed usage and specifications for now and added component description --- .../components/data-grid/DatagridPageLayout.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/website/screens/components/data-grid/DatagridPageLayout.tsx b/apps/website/screens/components/data-grid/DatagridPageLayout.tsx index 79a59efb0..8dbeea9ac 100644 --- a/apps/website/screens/components/data-grid/DatagridPageLayout.tsx +++ b/apps/website/screens/components/data-grid/DatagridPageLayout.tsx @@ -6,8 +6,8 @@ import ComponentHeading from "@/common/ComponentHeading"; const DataGridPageHeading = ({ children }: { children: React.ReactNode }) => { const tabs = [ { label: "Code", path: "/components/data-grid" }, - { label: "Usage", path: "/components/data-grid/usage" }, - { label: "Specifications", path: "/components/data-grid/specifications" }, + // { label: "Usage", path: "/components/data-grid/usage" }, + // { label: "Specifications", path: "/components/data-grid/specifications" }, ]; return ( @@ -15,7 +15,12 @@ const DataGridPageHeading = ({ children }: { children: React.ReactNode }) => { <PageHeading> <DxcFlex direction="column" gap="2rem"> <ComponentHeading name="Data Grid" /> - <DxcParagraph>A data grid is ... (TODO).</DxcParagraph> + <DxcParagraph> + A data grid is a component designed to display large volumes in a structured and organized manner. It + structures data into rows and columns, making it easy for users to visualize, analyze, and interact with the + information. The data grid also improves user experience by providing features like sorting, filtering, and + editing. + </DxcParagraph> <TabsPageHeading tabs={tabs}></TabsPageHeading> </DxcFlex> </PageHeading> From b1ef60c2f1a8b2d45c0acd8ca5dc3b17bbcbb21d Mon Sep 17 00:00:00 2001 From: Jialecl <jialestrabajos@gmail.com> Date: Tue, 13 Aug 2024 12:15:21 +0200 Subject: [PATCH 19/20] tokens and theme previews updated + mode removed --- .../screens/common/themes/advanced-theme.json | 27 ++++--- .../components/previews/DataGrid.tsx | 1 - .../themes/schemas/advanced.schema.json | 17 +++-- packages/lib/src/HalstackContext.tsx | 1 + packages/lib/src/common/variables.ts | 32 +++++---- .../lib/src/data-grid/DataGrid.stories.tsx | 15 ++-- packages/lib/src/data-grid/DataGrid.tsx | 72 ++++++++++--------- packages/lib/src/data-grid/types.ts | 1 - packages/lib/src/data-grid/utils.tsx | 45 +++++------- 9 files changed, 113 insertions(+), 98 deletions(-) diff --git a/apps/website/screens/common/themes/advanced-theme.json b/apps/website/screens/common/themes/advanced-theme.json index 498ed858a..132ccfd42 100644 --- a/apps/website/screens/common/themes/advanced-theme.json +++ b/apps/website/screens/common/themes/advanced-theme.json @@ -361,24 +361,31 @@ "dataFontWeight": "400", "dataFontColor": "#000000", "dataFontTextTransform": "none", - "dataPaddingRight": "1.5rem", - "dataPaddingLeft": "1.5rem", - "dataPaddingRightReduced": "1rem", - "dataPaddingLeftReduced": "1rem", + "dataPaddingRight": "0.5rem", + "dataPaddingLeft": "0.5rem", + "dataRowHeight": "36", "dataTextLineHeight": "normal", "headerBackgroundColor": "#5f249f", "headerBorderRadius": "4px", "headerFontFamily": "Open Sans, sans-serif", "headerFontSize": "0.875rem", "headerFontStyle": "normal", - "headerFontWeight": "400", + "headerFontWeight": "bold", "headerFontColor": "#ffffff", "headerFontTextTransform": "none", - "headerPaddingRight": "1.4rem", - "headerPaddingLeft": "1.5rem", - "headerPaddingRightReduced": "1rem", - "headerPaddingLeftReduced": "1rem", - "headerTextLineHeight": "normal" + "headerPaddingRight": "0.5rem", + "headerPaddingLeft": "0.5rem", + "headerRowHeight": "36", + "headerTextLineHeight": "normal", + "headerCheckboxBackgroundColorChecked": "#ffffff", + "headerCheckboxHoverBackgroundColorChecked": "#e6e6e6", + "headerCheckboxBorderColor": "#ffffff", + "headerCheckboxHoverBorderColor": "#ffffff", + "headerCheckboxCheckColor": "#5f249f", + "summaryRowHeight": "36", + "focusColor": "#0095ff", + "scrollBarThumbColor": "#666666", + "scrollBarTrackColor": "#cccccc" }, "dateInput": { "pickerBackgroundColor": "#ffffff", diff --git a/apps/website/screens/theme-generator/components/previews/DataGrid.tsx b/apps/website/screens/theme-generator/components/previews/DataGrid.tsx index d245fe520..72dc444de 100644 --- a/apps/website/screens/theme-generator/components/previews/DataGrid.tsx +++ b/apps/website/screens/theme-generator/components/previews/DataGrid.tsx @@ -229,7 +229,6 @@ const DataGridPreview = () => { rows={childRows} uniqueRowId="value" summaryRow={{ label: "Total", total: 100, value: "summary" }} - mode="reduced" /> </Mode> </PreviewContainer> diff --git a/apps/website/screens/theme-generator/themes/schemas/advanced.schema.json b/apps/website/screens/theme-generator/themes/schemas/advanced.schema.json index 4df723fa1..ad91a8b12 100644 --- a/apps/website/screens/theme-generator/themes/schemas/advanced.schema.json +++ b/apps/website/screens/theme-generator/themes/schemas/advanced.schema.json @@ -363,8 +363,7 @@ "dataFontTextTransform": "fTextTransform", "dataPaddingRight": "length", "dataPaddingLeft": "length", - "dataPaddingRightReduced": "length", - "dataPaddingLeftReduced": "length", + "dataRowHeight": "length", "dataTextLineHeight": "length", "headerBackgroundColor": "color", "headerBorderRadius": "length", @@ -376,9 +375,17 @@ "headerFontTextTransform": "fTextTransform", "headerPaddingRight": "length", "headerPaddingLeft": "length", - "headerPaddingRightReduced": "length", - "headerPaddingLeftReduced": "length", - "headerTextLineHeight": "length" + "headerRowHeight": "length", + "headerTextLineHeight": "length", + "headerCheckboxBackgroundColorChecked": "color", + "headerCheckboxHoverBackgroundColorChecked": "color", + "headerCheckboxBorderColor": "color", + "headerCheckboxHoverBorderColor": "color", + "headerCheckboxCheckColor": "color", + "summaryRowHeight": "length", + "focusColor": "color", + "scrollBarThumbColor": "color", + "scrollBarTrackColor": "color" }, "dateInput": { "pickerBackgroundColor": "color", diff --git a/packages/lib/src/HalstackContext.tsx b/packages/lib/src/HalstackContext.tsx index b5659166d..320443bce 100644 --- a/packages/lib/src/HalstackContext.tsx +++ b/packages/lib/src/HalstackContext.tsx @@ -141,6 +141,7 @@ const parseTheme = (theme: DeepPartial<OpinionatedTheme>): AdvancedTheme => { dataGridTokens.headerBackgroundColor = theme?.dataGrid?.baseColor ?? dataGridTokens.headerBackgroundColor; dataGridTokens.headerFontColor = theme?.dataGrid?.headerFontColor ?? dataGridTokens.headerFontColor; dataGridTokens.dataFontColor = theme?.dataGrid?.cellFontColor ?? dataGridTokens.dataFontColor; + dataGridTokens.headerCheckboxCheckColor = theme?.dataGrid?.baseColor ?? dataGridTokens.headerCheckboxCheckColor; const dateTokens = componentTokensCopy.dateInput; dateTokens.pickerSelectedBackgroundColor = theme?.dateInput?.baseColor ?? dateTokens.pickerSelectedBackgroundColor; diff --git a/packages/lib/src/common/variables.ts b/packages/lib/src/common/variables.ts index 5017934d8..5a2ed0b4f 100644 --- a/packages/lib/src/common/variables.ts +++ b/packages/lib/src/common/variables.ts @@ -365,12 +365,13 @@ export const componentTokens = { dataFontTextTransform: "none", // dataPaddingTop: CoreTokens.spacing_16,? // dataPaddingBottom: CoreTokens.spacing_16,? - dataPaddingRight: CoreTokens.spacing_24, - dataPaddingLeft: CoreTokens.spacing_24, + dataPaddingRight: CoreTokens.spacing_8, + dataPaddingLeft: CoreTokens.spacing_16, + dataRowHeight: 36, // dataPaddingTopReduced: CoreTokens.spacing_8,? // dataPaddingBottomReduced: CoreTokens.spacing_8,? - dataPaddingRightReduced: CoreTokens.spacing_16, - dataPaddingLeftReduced: CoreTokens.spacing_16, + // dataPaddingRightReduced: CoreTokens.spacing_16, + // dataPaddingLeftReduced: CoreTokens.spacing_16, // dataTextAlign: "left",? dataTextLineHeight: "normal", // firstChildPaddingLeft: CoreTokens.spacing_24,? @@ -382,22 +383,29 @@ export const componentTokens = { headerFontFamily: CoreTokens.type_sans, headerFontSize: CoreTokens.type_scale_02, headerFontStyle: CoreTokens.type_normal, - headerFontWeight: CoreTokens.type_regular, + headerFontWeight: CoreTokens.type_bold, headerFontColor: CoreTokens.color_white, headerFontTextTransform: "none", // headerPaddingTop: CoreTokens.spacing_16,? // headerPaddingBottom: CoreTokens.spacing_16,? - headerPaddingRight: CoreTokens.spacing_24, - headerPaddingLeft: CoreTokens.spacing_24, + headerPaddingRight: CoreTokens.spacing_8, + headerPaddingLeft: CoreTokens.spacing_8, + headerRowHeight: 36, // headerPaddingTopReduced: CoreTokens.spacing_8,? // headerPaddingBottomReduced: CoreTokens.spacing_8,? - headerPaddingRightReduced: CoreTokens.spacing_16, - headerPaddingLeftReduced: CoreTokens.spacing_16, + // headerPaddingRightReduced: CoreTokens.spacing_16, + // headerPaddingLeftReduced: CoreTokens.spacing_16, // headerTextAlign: "left",? headerTextLineHeight: "normal", - // missing summary tokens - // scrollBarThumbColor: CoreTokens.color_grey_700, - // scrollBarTrackColor: CoreTokens.color_grey_300, + headerCheckboxBackgroundColorChecked: CoreTokens.color_white, + headerCheckboxHoverBackgroundColorChecked: CoreTokens.color_grey_200, + headerCheckboxBorderColor: CoreTokens.color_white, + headerCheckboxHoverBorderColor: CoreTokens.color_white, + headerCheckboxCheckColor: CoreTokens.color_purple_700, + summaryRowHeight: 36, + focusColor: CoreTokens.color_blue_600, + scrollBarThumbColor: CoreTokens.color_grey_700, + scrollBarTrackColor: CoreTokens.color_grey_300, // sortIconColor: CoreTokens.color_white, // actionIconColor: CoreTokens.color_purple_700, // disabledActionIconColor: CoreTokens.color_grey_500, diff --git a/packages/lib/src/data-grid/DataGrid.stories.tsx b/packages/lib/src/data-grid/DataGrid.stories.tsx index c567f5e89..465b89568 100644 --- a/packages/lib/src/data-grid/DataGrid.stories.tsx +++ b/packages/lib/src/data-grid/DataGrid.stories.tsx @@ -277,17 +277,10 @@ export const Chromatic = () => { /> </ExampleContainer> <ExampleContainer> - <Title title="Reduced Data Grid" theme="light" level={4} /> - <DxcDataGrid - columns={childcolumns} - rows={childRows} - uniqueRowId="value" - selectable - selectedRows={selectedChildRows} - onSelectRows={setSelectedChildRows} - summaryRow={{ label: "Total", total: 100 }} - mode="reduced" - /> + <Title title="Scrollable Data Grid" theme="light" level={4} /> + <DxcContainer height="250px"> + <DxcDataGrid columns={columns} rows={expandableRows} uniqueRowId="id" /> + </DxcContainer> </ExampleContainer> </> ); diff --git a/packages/lib/src/data-grid/DataGrid.tsx b/packages/lib/src/data-grid/DataGrid.tsx index 9d559a8e8..77f171241 100644 --- a/packages/lib/src/data-grid/DataGrid.tsx +++ b/packages/lib/src/data-grid/DataGrid.tsx @@ -28,10 +28,10 @@ const DxcDataGrid = ({ selectedRows, uniqueRowId, summaryRow, - mode = "default", onGridRowsChange, }: DataGridPropsType): JSX.Element => { const [rowsToRender, setRowsToRender] = useState<GridRow[] | HierarchyGridRow[] | ExpandableGridRow[]>(rows); + const colorsTheme = useTheme(); // Proccess columns prop into usable columns based on other props const columnsToRender = useMemo(() => { let expectedColumns = columns @@ -53,8 +53,9 @@ const DxcDataGrid = ({ { key: "expanded", name: "", - maxWidth: 50, - width: 50, + maxWidth: 36, + width: 36, + minWidth: 36, colSpan(args) { return args.type === "ROW" && args.row.isExpandedChildContent ? columns.length + 1 : undefined; }, @@ -82,13 +83,13 @@ const DxcDataGrid = ({ renderCell({ row }) { if (row.childRows?.length) { return ( - <HierarchyContainer level={row.rowLevel || 0} mode={mode}> + <HierarchyContainer level={row.rowLevel || 0}> {renderHierarchyTrigger(rowsToRender, row, uniqueRowId, firstColumnKey, setRowsToRender)} </HierarchyContainer> ); } return ( - <HierarchyContainer level={row.rowLevel || 0} className="ellipsis-cell" mode={mode}> + <HierarchyContainer level={row.rowLevel || 0} className="ellipsis-cell"> {row[firstColumnKey]} </HierarchyContainer> ); @@ -100,8 +101,9 @@ const DxcDataGrid = ({ { key: "selected", name: "", - maxWidth: 50, - width: 50, + maxWidth: 36, + width: 36, + minWidth: 36, renderCell({ row }) { if (!row.isExpandedChildContent) { return ( @@ -114,7 +116,7 @@ const DxcDataGrid = ({ renderHeaderCell: () => { return ( <ActionContainer id="action"> - {renderHeaderCheckbox(rows, uniqueRowId, selectedRows, onSelectRows)} + {renderHeaderCheckbox(rows, uniqueRowId, selectedRows, colorsTheme, onSelectRows)} </ActionContainer> ); }, @@ -180,11 +182,9 @@ const DxcDataGrid = ({ return rowsToRender; }, [expandable, rowsToRender, sortColumns, uniqueRowId]); - const colorsTheme = useTheme(); - return ( <ThemeProvider theme={colorsTheme.dataGrid}> - <DataGridContainer mode={mode}> + <DataGridContainer className="here"> {/* {columnsVisibilityFilter && ( <DxcSelect multiple @@ -210,12 +210,12 @@ const DxcDataGrid = ({ ) { return row.expandedContentHeight; } - return mode === "default" ? 60 : 36; + return colorsTheme.dataGrid.dataRowHeight; }} selectedRows={selectedRows} bottomSummaryRows={summaryRow ? [summaryRow] : undefined} - headerRowHeight={mode === "default" ? 60 : 36} - summaryRowHeight={mode === "default" ? 60 : 36} + headerRowHeight={colorsTheme.dataGrid.headerRowHeight} + summaryRowHeight={colorsTheme.dataGrid.summaryRowHeight} className="fill-grid" /> </DataGridContainer> @@ -234,18 +234,17 @@ const ActionContainer = styled.div` const HierarchyContainer = styled.div<{ level: number; - mode: "default" | "reduced"; }>` - padding-left: ${(props) => - `calc(${props.mode === "reduced" ? props.theme.dataPaddingLeftReduced : props.theme.dataPaddingLeft} * ${props.level})`}; + padding-left: ${(props) => `calc(${props.theme.dataPaddingLeft} * ${props.level})`}; button { - display: flex; + display: grid; + grid-template-columns: auto 1fr; align-items: center; gap: 0.5rem; padding: 0px; border: 0px; width: 100%; - height: ${(props) => (props.mode === "default" ? 60 : 36)}px; + height: ${(props) => props.theme.dataRowHeight}px; background: transparent; text-align: left; font-size: ${(props) => props.theme.dataFontSize}; @@ -255,19 +254,34 @@ const HierarchyContainer = styled.div<{ } `; -const DataGridContainer = styled.div<{ mode: "default" | "reduced" }>` +const DataGridContainer = styled.div` width: 100%; + height: 100%; .rdg { border-radius: 4px; height: 100%; border: 0px; + &::-webkit-scrollbar { + width: 8px; + height: 8px; + } + &::-webkit-scrollbar-thumb { + background-color: ${(props) => props.theme.scrollBarThumbColor}; + border-radius: 6px; + } + &::-webkit-scrollbar-track { + background-color: ${(props) => props.theme.scrollBarTrackColor}; + border-radius: 6px; + } + } + .rdg-cell:has(> #action) { + padding: 0px; } .rdg-cell { - display: flex; + display: grid; align-items: center; - padding: 0px - ${(props) => (props.mode === "default" ? props.theme.dataPaddingRight : props.theme.dataPaddingRightReduced)} 0 - ${(props) => (props.mode === "default" ? props.theme.dataPaddingLeft : props.theme.dataPaddingLeftReduced)}; + width: 100%; + padding: 0px ${(props) => props.theme.dataPaddingRight} 0 ${(props) => props.theme.dataPaddingLeft}; font-family: ${(props) => props.theme.dataFontFamily}; font-size: ${(props) => props.theme.dataFontSize}; font-style: ${(props) => props.theme.dataFontStyle}; @@ -281,9 +295,6 @@ const DataGridContainer = styled.div<{ mode: "default" | "reduced" }>` `${props.theme.rowSeparatorThickness} ${props.theme.rowSeparatorStyle} ${props.theme.rowSeparatorColor}`}; background-color: ${(props) => props.theme.dataBackgroundColor}; } - .rdg-cell:has(> #action) { - padding: 0px; - } .rdg-header-row { border-top-left-radius: ${(props) => props.theme.headerBorderRadius}; border-top-right-radius: ${(props) => props.theme.headerBorderRadius}; @@ -294,11 +305,7 @@ const DataGridContainer = styled.div<{ mode: "default" | "reduced" }>` font-weight: ${(props) => props.theme.headerFontWeight}; color: ${(props) => props.theme.headerFontColor}; text-transform: ${(props) => props.theme.headerFontTextTransform}; - padding: 0px - ${(props) => - props.mode === "default" ? props.theme.headerPaddingRight : props.theme.headerPaddingRightReduced} - 0 - ${(props) => (props.mode === "default" ? props.theme.headerPaddingLeft : props.theme.headerPaddingLeftReduced)}; + padding: 0px ${(props) => props.theme.headerPaddingRight} 0 ${(props) => props.theme.headerPaddingLeft}; line-height: ${(props) => props.theme.headerTextLineHeight}; background-color: ${(props) => props.theme.headerBackgroundColor}; .sortIconContainer { @@ -325,6 +332,7 @@ const DataGridContainer = styled.div<{ mode: "default" | "reduced" }>` text-overflow: ellipsis; overflow: hidden; white-space: nowrap; + width: 100%; } .align-left { text-align: left; diff --git a/packages/lib/src/data-grid/types.ts b/packages/lib/src/data-grid/types.ts index 389c16786..e723c0bde 100644 --- a/packages/lib/src/data-grid/types.ts +++ b/packages/lib/src/data-grid/types.ts @@ -49,7 +49,6 @@ export type SelectableGridProps = }; export type CommonProps = { - mode?: "default" | "reduced"; columns: GridColumn[]; summaryRow?: GridRow; onGridRowsChange?: (rows: GridRow[] | HierarchyGridRow[] | ExpandableGridRow[]) => void; diff --git a/packages/lib/src/data-grid/utils.tsx b/packages/lib/src/data-grid/utils.tsx index 79c0febd9..044259524 100644 --- a/packages/lib/src/data-grid/utils.tsx +++ b/packages/lib/src/data-grid/utils.tsx @@ -1,33 +1,25 @@ +import { Checkbox } from "@cloudscape-design/components"; import DxcActionIcon from "../action-icon/ActionIcon"; import DxcCheckbox from "../checkbox/Checkbox"; -import { HalstackProvider } from "../HalstackContext"; +import { AdvancedTheme } from "../common/variables"; +import { DeepPartial, HalstackProvider } from "../HalstackContext"; import DxcIcon from "../icon/Icon"; import { GridColumn, HierarchyGridRow, GridRow, ExpandableGridRow } from "./types"; import { Column, RenderSortStatusProps, SortColumn, textEditor } from "react-data-grid"; -const checkboxTheme = { - checkbox: { - backgroundColorChecked: "#ffffff", - hoverBackgroundColorChecked: "#e6e6e6", - disabledBackgroundColorChecked: "#999999", - readOnlyBackgroundColorChecked: "#999999", - hoverReadOnlyBackgroundColorChecked: "#808080", - borderColor: "#ffffff", - hoverBorderColor: "#ffffff", - disabledBorderColor: "#999999", - readOnlyBorderColor: "#999999", - hoverReadOnlyBorderColor: "#808080", - checkColor: "#5f249f", - disabledCheckColor: "#ffffff", - readOnlyCheckColor: "#ffffff", - fontFamily: "Open Sans, sans-serif", - fontSize: "0.875rem", - fontWeight: "400", - fontColor: "#ffffff", - disabledFontColor: "#999999", - focusColor: "#0095ff", - checkLabelSpacing: "8px", - }, +const overwriteTheme = (theme: DeepPartial<AdvancedTheme>) => { + const newTheme = { + checkbox: { + backgroundColorChecked: theme.dataGrid.headerCheckboxBackgroundColorChecked, + hoverBackgroundColorChecked: theme.dataGrid.headerCheckboxHoverBackgroundColorChecked, + borderColor: theme.dataGrid.headerCheckboxBorderColor, + hoverBorderColor: theme.dataGrid.headerCheckboxHoverBorderColor, + checkColor: theme.dataGrid.headerCheckboxCheckColor, + focusColor: theme.dataGrid.focusColor, + }, + }; + + return newTheme; }; // Column<any, any> type added to avoid conflicts with SelectColumn typing from RDG @@ -165,7 +157,7 @@ export const renderHierarchyTrigger = ( }} > <DxcIcon icon={triggerRow.visibleChildren ? "Keyboard_Arrow_Down" : "Chevron_Right"} /> - {triggerRow[columnKey]} + <span className="ellipsis-cell">{triggerRow[columnKey]}</span> </button> ); }; @@ -205,10 +197,11 @@ export const renderHeaderCheckbox = ( rows: GridRow[] | HierarchyGridRow[] | ExpandableGridRow[], uniqueRowId: string, selectedRows: Set<number | string>, + colorsTheme: DeepPartial<AdvancedTheme>, onSelectRows: (selected: Set<number | string>) => void ) => { return ( - <HalstackProvider advancedTheme={checkboxTheme}> + <HalstackProvider advancedTheme={overwriteTheme(colorsTheme)}> <DxcCheckbox checked={!rows.some((row) => !selectedRows.has(rowKeyGetter(row, uniqueRowId)))} onChange={(checked) => { From 3f5834b3bb68744b7eaad69f6adaa84a9aeceb3d Mon Sep 17 00:00:00 2001 From: Jialecl <jialestrabajos@gmail.com> Date: Tue, 13 Aug 2024 14:07:17 +0200 Subject: [PATCH 20/20] Removed unused import and debug class --- packages/lib/src/common/variables.ts | 31 +------------------------ packages/lib/src/data-grid/DataGrid.tsx | 2 +- packages/lib/src/data-grid/utils.tsx | 1 - 3 files changed, 2 insertions(+), 32 deletions(-) diff --git a/packages/lib/src/common/variables.ts b/packages/lib/src/common/variables.ts index 5a2ed0b4f..cd81bf93f 100644 --- a/packages/lib/src/common/variables.ts +++ b/packages/lib/src/common/variables.ts @@ -363,21 +363,10 @@ export const componentTokens = { dataFontWeight: CoreTokens.type_regular, dataFontColor: CoreTokens.color_black, dataFontTextTransform: "none", - // dataPaddingTop: CoreTokens.spacing_16,? - // dataPaddingBottom: CoreTokens.spacing_16,? dataPaddingRight: CoreTokens.spacing_8, - dataPaddingLeft: CoreTokens.spacing_16, + dataPaddingLeft: CoreTokens.spacing_8, dataRowHeight: 36, - // dataPaddingTopReduced: CoreTokens.spacing_8,? - // dataPaddingBottomReduced: CoreTokens.spacing_8,? - // dataPaddingRightReduced: CoreTokens.spacing_16, - // dataPaddingLeftReduced: CoreTokens.spacing_16, - // dataTextAlign: "left",? dataTextLineHeight: "normal", - // firstChildPaddingLeft: CoreTokens.spacing_24,? - // lastChildPaddingRight: CoreTokens.spacing_24,? - // firstChildPaddingLeftReduced: "20px",? - // lastChildPaddingRightReduced: "20px",? headerBackgroundColor: CoreTokens.color_purple_700, headerBorderRadius: "4px", headerFontFamily: CoreTokens.type_sans, @@ -386,16 +375,9 @@ export const componentTokens = { headerFontWeight: CoreTokens.type_bold, headerFontColor: CoreTokens.color_white, headerFontTextTransform: "none", - // headerPaddingTop: CoreTokens.spacing_16,? - // headerPaddingBottom: CoreTokens.spacing_16,? headerPaddingRight: CoreTokens.spacing_8, headerPaddingLeft: CoreTokens.spacing_8, headerRowHeight: 36, - // headerPaddingTopReduced: CoreTokens.spacing_8,? - // headerPaddingBottomReduced: CoreTokens.spacing_8,? - // headerPaddingRightReduced: CoreTokens.spacing_16, - // headerPaddingLeftReduced: CoreTokens.spacing_16, - // headerTextAlign: "left",? headerTextLineHeight: "normal", headerCheckboxBackgroundColorChecked: CoreTokens.color_white, headerCheckboxHoverBackgroundColorChecked: CoreTokens.color_grey_200, @@ -406,17 +388,6 @@ export const componentTokens = { focusColor: CoreTokens.color_blue_600, scrollBarThumbColor: CoreTokens.color_grey_700, scrollBarTrackColor: CoreTokens.color_grey_300, - // sortIconColor: CoreTokens.color_white, - // actionIconColor: CoreTokens.color_purple_700, - // disabledActionIconColor: CoreTokens.color_grey_500, - // hoverActionIconColor: CoreTokens.color_purple_700, - // focusActionIconColor: CoreTokens.color_purple_700, - // activeActionIconColor: CoreTokens.color_purple_700, - // actionBackgroundColor: CoreTokens.color_transparent, - // disabledActionBackgroundColor: CoreTokens.color_transparent, - // hoverActionBackgroundColor: CoreTokens.color_grey_100, - // focusActionBorderColor: CoreTokens.color_blue_600, - // activeActionBackgroundColor: CoreTokens.color_grey_300, }, dateInput: { pickerBackgroundColor: CoreTokens.color_white, diff --git a/packages/lib/src/data-grid/DataGrid.tsx b/packages/lib/src/data-grid/DataGrid.tsx index 77f171241..b7c90e1f2 100644 --- a/packages/lib/src/data-grid/DataGrid.tsx +++ b/packages/lib/src/data-grid/DataGrid.tsx @@ -184,7 +184,7 @@ const DxcDataGrid = ({ return ( <ThemeProvider theme={colorsTheme.dataGrid}> - <DataGridContainer className="here"> + <DataGridContainer> {/* {columnsVisibilityFilter && ( <DxcSelect multiple diff --git a/packages/lib/src/data-grid/utils.tsx b/packages/lib/src/data-grid/utils.tsx index 044259524..bf22ed6d2 100644 --- a/packages/lib/src/data-grid/utils.tsx +++ b/packages/lib/src/data-grid/utils.tsx @@ -1,4 +1,3 @@ -import { Checkbox } from "@cloudscape-design/components"; import DxcActionIcon from "../action-icon/ActionIcon"; import DxcCheckbox from "../checkbox/Checkbox"; import { AdvancedTheme } from "../common/variables";