Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
- ⚠️ `onGridKeyDown`
- ⚠️ `onGridKeyUp`
- ⚠️ `onRowDoubleClick`
- ⚠️ `onHeaderDrop`
- ⚠️ `draggableHeaderCell`
- Check [#2007](https://github.com/adazzle/react-data-grid/pull/2007) on how to migrate
- ⚠️ `rowsContainer`
- ⚠️ Subrow props: `getSubRowDetails`, `onCellExpand`, `onDeleteSubRow`, and `onAddSubRow`
- Check [#1853](https://github.com/adazzle/react-data-grid/pull/1853) on how to migrate
Expand All @@ -56,6 +59,7 @@
- Check [#1845](https://github.com/adazzle/react-data-grid/pull/1845) on how to migrate
- ⚠️ `column.getRowMetaData`
- ⚠️ `column.filterable`
- ⚠️ `column.draggable`
- ⚠️ `cellRangeSelection.{onStart,onUpdate,onEnd}`
- ⚠️ `fromRowId`, `toRowId`, and `fromRowData` from `onRowsUpdate` argument
- ⚠️ Stopped exporting `HeaderCell`
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@
"mini-css-extract-plugin": "^0.9.0",
"react": "^16.13.1",
"react-contextmenu": "^2.13.0",
"react-dnd": "^10.0.2",
"react-dnd-html5-backend": "^10.0.2",
"react-dom": "^16.13.1",
"react-select": "^3.1.0",
"react-virtualized": "^9.21.2",
Expand Down
5 changes: 0 additions & 5 deletions src/DataGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,6 @@ export interface DataGridProps<R, K extends keyof R, SR = unknown> {
rowRenderer?: React.ComponentType<RowRendererProps<R, SR>>;
rowGroupRenderer?: React.ComponentType;
emptyRowsView?: React.ComponentType<{}>;
/** Component used to render a draggable header cell */
draggableHeaderCell?: React.ComponentType<{ column: CalculatedColumn<R, SR>; onHeaderDrop: () => void }>;

/**
* Event props
Expand All @@ -126,7 +124,6 @@ export interface DataGridProps<R, K extends keyof R, SR = unknown> {
onScroll?: (scrollPosition: ScrollPosition) => void;
/** Called when a column is resized */
onColumnResize?: (idx: number, width: number) => void;
onHeaderDrop?: () => void;
onRowExpandToggle?: (event: RowExpandToggleEvent) => void;
/** Function called whenever selected cell is changed */
onSelectedCellChange?: (position: Position) => void;
Expand Down Expand Up @@ -427,8 +424,6 @@ function DataGrid<R, K extends keyof R, SR>({
columns={viewportColumns}
onColumnResize={handleColumnResize}
lastFrozenColumnIndex={lastFrozenColumnIndex}
draggableHeaderCell={props.draggableHeaderCell}
onHeaderDrop={props.onHeaderDrop}
allRowsSelected={selectedRows?.size === rows.length}
onSelectedRowsChange={onSelectedRowsChange}
sortColumn={props.sortColumn}
Expand Down
18 changes: 0 additions & 18 deletions src/HeaderCell.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ interface Row {
}

describe('HeaderCell', () => {
function DraggableHeaderCell() {
return <div />;
}

function setup(overrideProps = {}, columnProps = {}) {
const props: HeaderCellProps<Row, unknown> = {
column: {
Expand All @@ -27,8 +23,6 @@ describe('HeaderCell', () => {
},
lastFrozenColumnIndex: -1,
onResize: jest.fn(),
onHeaderDrop() { },
draggableHeaderCell: DraggableHeaderCell,
allRowsSelected: false,
onAllRowsSelectionChange() {},
...overrideProps
Expand All @@ -53,16 +47,4 @@ describe('HeaderCell', () => {
expect(props.onResize).toHaveBeenCalledWith(props.column, 200);
});
});

describe('Render draggableHeaderCell', () => {
it('should not render DraggableHeaderCell when draggable is false', () => {
const { wrapper } = setup({}, { draggable: false });
expect(wrapper.find(DraggableHeaderCell)).toHaveLength(0);
});

it('should not render DraggableHeaderCell when draggable is true', () => {
const { wrapper } = setup({}, { draggable: true });
expect(wrapper.find(DraggableHeaderCell)).toHaveLength(1);
});
});
});
14 changes: 0 additions & 14 deletions src/HeaderCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ type SharedHeaderRowProps<R, SR> = Pick<HeaderRowProps<R, never, SR>,
| 'sortColumn'
| 'sortDirection'
| 'onSort'
| 'onHeaderDrop'
| 'allRowsSelected'
| 'draggableHeaderCell'
>;

export interface HeaderCellProps<R, SR> extends SharedHeaderRowProps<R, SR> {
Expand Down Expand Up @@ -78,17 +76,5 @@ export default function HeaderCell<R, SR>({
);
}

const DraggableHeaderCell = props.draggableHeaderCell;
if (column.draggable && DraggableHeaderCell) {
return (
<DraggableHeaderCell
column={column}
onHeaderDrop={props.onHeaderDrop!}
>
{cell}
</DraggableHeaderCell>
);
}

return cell;
}
8 changes: 2 additions & 6 deletions src/HeaderRow.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ describe('HeaderRow', () => {
onColumnResize() { },
onSort: jest.fn(),
sortDirection: 'NONE',
allRowsSelected: false,
onHeaderDrop() { },
draggableHeaderCell: () => <div />
allRowsSelected: false
};

const setup = (testProps?: Partial<HeaderRowProps<Row, 'id', unknown>>) => {
Expand Down Expand Up @@ -77,9 +75,7 @@ describe('HeaderRow', () => {
lastFrozenColumnIndex: 1,
onSort: jest.fn(),
allRowsSelected: false,
onColumnResize: jest.fn(),
onHeaderDrop() { },
draggableHeaderCell: () => <div />
onColumnResize: jest.fn()
};

it('passes classname property', () => {
Expand Down
4 changes: 0 additions & 4 deletions src/HeaderRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import { assertIsValidKey } from './utils';
import { DataGridProps } from './DataGrid';

type SharedDataGridProps<R, K extends keyof R, SR> = Pick<DataGridProps<R, K, SR>,
| 'draggableHeaderCell'
| 'rows'
| 'onHeaderDrop'
| 'onSelectedRowsChange'
| 'sortColumn'
| 'sortDirection'
Expand Down Expand Up @@ -53,10 +51,8 @@ function HeaderRow<R, K extends keyof R, SR>({
column={column}
lastFrozenColumnIndex={props.lastFrozenColumnIndex}
onResize={props.onColumnResize}
onHeaderDrop={props.onHeaderDrop}
allRowsSelected={props.allRowsSelected}
onAllRowsSelectionChange={handleAllRowsSelectionChange}
draggableHeaderCell={props.draggableHeaderCell}
onSort={props.onSort}
sortColumn={props.sortColumn}
sortDirection={props.sortDirection}
Expand Down
4 changes: 1 addition & 3 deletions src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ export interface Column<TRow, TSummaryRow = unknown> {
summaryFormatter?: React.ComponentType<SummaryFormatterProps<TSummaryRow, TRow>>;
/** Enables cell editing. If set and no editor property specified, then a textinput will be used as the cell editor */
editable?: boolean | ((row: TRow) => boolean);
/** Enable dragging of a column */
draggable?: boolean;
/** Determines whether column is frozen or not */
frozen?: boolean;
/** Enable resizing of a column */
Expand Down Expand Up @@ -115,7 +113,7 @@ export interface EditorProps<TValue, TRow = any, TSummaryRow = any> {
onOverrideKeyDown: (e: KeyboardEvent) => void;
}

export interface HeaderRendererProps<TRow, TSummaryRow> {
export interface HeaderRendererProps<TRow, TSummaryRow = unknown> {
column: CalculatedColumn<TRow, TSummaryRow>;
allRowsSelected: boolean;
onAllRowsSelectionChange: (checked: boolean) => void;
Expand Down
2 changes: 1 addition & 1 deletion stories/demos/CellNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ function createRows(): Row[] {
return rows;
}

export default function ScrollToRow() {
export default function CellNavigation() {
const [rows] = useState(createRows);
const [cellNavigatioMode, setCellNavigationMode] = useState<CellNavigationMode>(CellNavigationMode.CHANGE_ROW);

Expand Down
130 changes: 130 additions & 0 deletions stories/demos/ColumnsReordering.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import React, { useState, useCallback, useMemo } from 'react';
import { DndProvider } from 'react-dnd';
import Backend from 'react-dnd-html5-backend';

import { DraggableHeaderRenderer } from './components/HeaderRenderers';
import DataGrid, { Column, HeaderRendererProps, SortDirection } from '../../src';

interface Row {
id: number;
task: string;
complete: number;
priority: string;
issueType: string;
}

function createRows(): Row[] {
const rows = [];
for (let i = 1; i < 500; i++) {
rows.push({
id: i,
task: `Task ${i}`,
complete: Math.min(100, Math.round(Math.random() * 110)),
priority: ['Critical', 'High', 'Medium', 'Low'][Math.floor((Math.random() * 3) + 1)],
issueType: ['Bug', 'Improvement', 'Epic', 'Story'][Math.floor((Math.random() * 3) + 1)]
});
}

return rows;
}

function createColumns(): Column<Row>[] {
return [
{
key: 'id',
name: 'ID',
width: 80
},
{
key: 'task',
name: 'Title',
resizable: true,
sortable: true
},
{
key: 'priority',
name: 'Priority',
resizable: true,
sortable: true
},
{
key: 'issueType',
name: 'Issue Type',
resizable: true,
sortable: true
},
{
key: 'complete',
name: '% Complete',
resizable: true,
sortable: true
}
];
}

export default function ColumnsReordering() {
const [rows] = useState(createRows);
const [columns, setColumns] = useState(createColumns);
const [[sortColumn, sortDirection], setSort] = useState<[string, SortDirection]>(['task', 'NONE']);

const handleSort = useCallback((columnKey: string, direction: SortDirection) => {
setSort([columnKey, direction]);
}, []);

const draggableColumns = useMemo(() => {
function HeaderRenderer(props: HeaderRendererProps<Row>) {
return <DraggableHeaderRenderer {...props} onColumnsReorder={handleColumnsReorder} />;
}

function handleColumnsReorder(sourceKey: string, targetKey: string) {
const sourceColumnIndex = columns.findIndex(c => c.key === sourceKey);
const targetColumnIndex = columns.findIndex(c => c.key === targetKey);
const reorderedColumns = [...columns];

reorderedColumns.splice(
targetColumnIndex,
0,
reorderedColumns.splice(sourceColumnIndex, 1)[0]
);

setColumns(reorderedColumns);
}

return columns.map(c => {
if (c.key === 'id') return c;
return { ...c, headerRenderer: HeaderRenderer };
});
}, [columns]);

const sortedRows = useMemo((): readonly Row[] => {
if (sortDirection === 'NONE') return rows;

let sortedRows: Row[] = [...rows];

switch (sortColumn) {
case 'task':
case 'priority':
case 'issueType':
sortedRows = sortedRows.sort((a, b) => a[sortColumn].localeCompare(b[sortColumn]));
break;
case 'complete':
sortedRows = sortedRows.sort((a, b) => a[sortColumn] - b[sortColumn]);
break;
default:
}

return sortDirection === 'DESC' ? sortedRows.reverse() : sortedRows;
}, [rows, sortDirection, sortColumn]);

return (
<DndProvider backend={Backend}>
<DataGrid
columns={draggableColumns}
rows={sortedRows}
sortColumn={sortColumn}
sortDirection={sortDirection}
onSort={handleSort}
/>
</DndProvider>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React from 'react';
Copy link
Contributor

@qili26 qili26 Apr 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just not sure about this file. What do you guys think if we provide this in the DataGrid and export this as a default DraggableHeaderRenderer?

pros: consumer devs can just use the default column function;
cons: 1. we might need to provide more APIs. 2. We have to include the react-dnd as a dependency...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to keep the dependencies to a minimum and provide a flexible API so users can write their own implementations. Composition is always more maintainable than adding extra props

Copy link
Collaborator

@nstepien nstepien Apr 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it's extra work to maintain the feature + dependency on other libs.
That way users can use whatever implementation they want to header dragging.

import { useDrag, useDrop, DragObjectWithType } from 'react-dnd';

import { HeaderRendererProps } from '../../../../src';

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And we don't need this empty line.


interface ColumnDragObject extends DragObjectWithType {
key: string;
}

function wrapRefs<T>(...refs: React.Ref<T>[]) {
return (handle: T | null) => {
for (const ref of refs) {
if (typeof ref === 'function') {
ref(handle);
} else if (ref !== null) {
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31065
(ref as React.MutableRefObject<T | null>).current = handle;
}
}
};
}

export function DraggableHeaderRenderer<R>({ onColumnsReorder, ...props }: HeaderRendererProps<R> & { onColumnsReorder: (sourceKey: string, targetKey: string) => void }) {
const [{ isDragging }, drag] = useDrag({
item: { key: props.column.key, type: 'COLUMN_DRAG' },
collect: monitor => ({
isDragging: !!monitor.isDragging()
})
});

const [{ isOver }, drop] = useDrop({
accept: 'COLUMN_DRAG',
drop({ key, type }: ColumnDragObject) {
if (type === 'COLUMN_DRAG') {
onColumnsReorder(key, props.column.key);
}
},
collect: monitor => ({
isOver: !!monitor.isOver(),
canDrop: !!monitor.canDrop()
})
});

return (
<div
ref={wrapRefs(drag, drop)}
style={{
opacity: isDragging ? 0.5 : 1,
backgroundColor: isOver ? '#ececec' : 'inherit',
cursor: 'move'
}}
>
{props.column.name}
</div>
);
}
1 change: 1 addition & 0 deletions stories/demos/components/HeaderRenderers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './DraggableHeaderRenderer';
4 changes: 3 additions & 1 deletion stories/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import ContextMenu from './demos/ContextMenu';
import ScrollToRow from './demos/ScrollToRow';
import CellNavigation from './demos/CellNavigation';
import HeaderFilters from './demos/HeaderFilters';
import ColumnsReordering from './demos/ColumnsReordering';

storiesOf('Demos', module)
.add('Common Features', () => <CommonFeatures />)
Expand All @@ -25,4 +26,5 @@ storiesOf('Demos', module)
.add('Context Menu', () => <ContextMenu />)
.add('Scroll To Row', () => <ScrollToRow />)
.add('Cell Navigation', () => <CellNavigation />)
.add('Header Filters', () => <HeaderFilters />);
.add('Header Filters', () => <HeaderFilters />)
.add('Columns Reordering', () => <ColumnsReordering />);