Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WCMS-21488: Manage Columns Feature for Data Table Tab #234

Merged
merged 13 commits into from
Aug 9, 2024
Merged
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
"author": "",
"license": "GPL-3.0",
"dependencies": {
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/modifiers": "^7.0.0",
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@popperjs/core": "^2.11.6",
"@tanstack/react-query": "^5.14.1",
"@tanstack/react-table": "^8.7.9",
Expand Down
6 changes: 5 additions & 1 deletion src/components/DatasetTableTab/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type DatasetTableTabProps = {
customColumns: Array<ColumnType>,
jsonUrl?: string,
dataDictionaryBanner: boolean,
manageColumnsEnabled: boolean,
}

const DatasetTable = ({
Expand All @@ -33,7 +34,8 @@ const DatasetTable = ({
rootUrl,
customColumns = [],
jsonUrl = undefined,
dataDictionaryBanner
dataDictionaryBanner,
manageColumnsEnabled,
}
: DatasetTableTabProps
) => {
Expand Down Expand Up @@ -83,13 +85,15 @@ const DatasetTable = ({
/> }
<div className="ds-u-border-x--1 ds-u-border-bottom--1">
<DataTable
id={id}
data={resource.values}
canResize={true}
columns={columns}
setSort={resource.setSort}
sortTransform={transformTableSortToQuerySort}
tablePadding={'ds-u-padding-y--2'}
loading={resource.loading}
manageColumnsEnabled={manageColumnsEnabled}
/>
</div>
{!resource.loading && (
Expand Down
40 changes: 37 additions & 3 deletions src/components/Datatable/Datatable.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useState, useMemo } from "react";
import {
useReactTable,
flexRender,
Expand All @@ -10,15 +10,18 @@ import { Spinner, Alert } from "@cmsgov/design-system";
import TruncatedResizeableTHead from "./TruncatedResizeableTHead";
import FixedSizeTHead from "./FixedSizeTHead";
import "./datatable.scss";
import ManageColumns from "../ManageColumns/ManageColumns";

const DataTable = ({
id,
data,
columns,
setSort,
sortTransform,
tablePadding,
canResize,
loading = false,
manageColumnsEnabled,
}) => {
const [ sorting, setSorting ] = React.useState([])
const [ariaLiveFeedback, setAriaLiveFeedback] = useState('')
Expand All @@ -38,6 +41,19 @@ const DataTable = ({
})
)
})
const localStorageData = JSON.parse(localStorage.getItem(id));
const [columnOrder, setColumnOrder] = useState(() => {
if (manageColumnsEnabled && localStorageData)
return localStorageData.tablecolumnOrder;
else
return table_columns.map(c => c.accessorKey);
})
const [columnVisibility, setColumnVisibility] = useState(() => {
if (manageColumnsEnabled && localStorageData)
return localStorageData.tableColumnVisibility;
else
return {};
})

const sortElement = (isSorted, onClickFn) => {
if(isSorted === 'asc') {
Expand All @@ -54,12 +70,14 @@ const DataTable = ({
columns: table_columns,
manualSorting: true,
state: {

columnOrder,
columnVisibility,
sorting,
},
columnResizeMode: 'onChange',
onSortingChange: setSorting,

onColumnOrderChange: setColumnOrder,
onColumnVisibilityChange: setColumnVisibility,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
debugTable: false,
Expand All @@ -70,8 +88,24 @@ const DataTable = ({
setSort(normalizedSort);
}, [sorting]);

const defaultColumnOrder = useMemo(() => table_columns.map(column => {
return column.accessorKey
}));

return(
<>
{ manageColumnsEnabled && (
<div>
<ManageColumns
id={id}
columns={table.getAllLeafColumns()}
columnOrder={columnOrder}
defaultColumnOrder={defaultColumnOrder}
setColumnOrder={setColumnOrder}
setColumnVisibility={setColumnVisibility}
/>
</div>
)}
<div className="dc-c-datatable-wrapper" tabIndex={0}>
<table
{...{
Expand Down
2 changes: 1 addition & 1 deletion src/components/Datatable/HeaderResizeElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const HeaderResizeElement = ({table, header, sortElement, setAriaLiveFeedback} :
className="ds-u-border-y--2 ds-u-padding--2 ds-u-border--dark ds-u-font-weight--bold"
>
<div className="ds-u-display--flex">
<span style={{maxWidth: header.getSize() - 16}} title={header.column.columnDef.header}>
<span style={{maxWidth: header.getSize() - 16}} title={typeof(header.column.columnDef.header) === "string" ? header.column.columnDef.header : ''}>
{header.isPlaceholder
? null
: flexRender(
Expand Down
62 changes: 62 additions & 0 deletions src/components/ManageColumns/Card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from "react";
import { CSSProperties } from "react";
import { Choice } from "@cmsgov/design-system";
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";

const Card = ({id, visible, updateVisibility}: {id: string, visible: boolean, updateVisibility: Function}) => {
const {attributes, listeners, setNodeRef, transform, transition, isDragging} = useSortable({
id: id,
});

const style: CSSProperties = {
transform: CSS.Transform.toString(transform),
transition,
opacity: isDragging ? 0.7 : 1,
zIndex: isDragging ? 1 : 0,
position: 'relative',
background: 'white',
touchAction: 'none'
};

return (
<li
className="ds-u-display--flex ds-u-justify-content--between ds-u-border-bottom--1"
ref={setNodeRef}
style={style}
{...listeners}
{...attributes}
onPointerUp={(e) => {
// Small hack to get around a chrome / webkit rendering bug = force chrome to repaint the checkbox
// For whatever reason the way dnd-kit handles events doesn't work well with chrome
// Without this code checkboxes can end up visually out of sync with app state until a repaint is forced
// this code forces the repaint without user interaction
const target = e.target as HTMLElement;
if (isDragging && target.tagName.toLowerCase() === "label") {
setTimeout(() => {
target.parentNode!.querySelector('input')!.checked = visible
}, 1)
}
}}
>
<Choice
type="checkbox"
label={id}
name={id + "_visibility"}
checked={visible}
className="ds-l-col--10 ds-u-margin-top--0 ds-u-margin-y--1 ds-u-padding-x--3"
labelClassName="dc-truncate"
title="Toggle Column Visible"
brdunfield marked this conversation as resolved.
Show resolved Hide resolved
value=""
onChange={() => {
updateVisibility(id, !visible)
}}
/>
<button className={`ds-l-col--2 dkan-manage-columns-reorder-button ${isDragging && 'grabbed'}`} aria-label={`Reorder ${id} column`}>
<span className="fa fa-sort"></span>
</button>
</li>
)
}

export default Card;
Loading