Skip to content

Commit c3a4d63

Browse files
authored
Fix drag preview in TableView (#5687)
The fix ensures that when rowHeader is not present, the drag preview displays the content of the first element, excluding checkboxes and dragButtons.
1 parent 3cdf9cc commit c3a4d63

File tree

4 files changed

+115
-11
lines changed

4 files changed

+115
-11
lines changed

packages/@react-spectrum/table/stories/TableDnD.stories.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {action} from '@storybook/addon-actions';
1414
import {ComponentMeta} from '@storybook/react';
1515
import defaultConfig, {TableStory} from './Table.stories';
1616
import {Divider} from '@react-spectrum/divider';
17-
import {DragBetweenTablesExample, DragBetweenTablesRootOnlyExample, DragExample, DragOntoRowExample, ReorderExample} from './TableDnDExamples';
17+
import {DragBetweenTablesExample, DragBetweenTablesRootOnlyExample, DragExample, DragOntoRowExample, DragWithoutRowHeaderExample, ReorderExample} from './TableDnDExamples';
1818
import {Droppable} from '../../../@react-aria/dnd/stories/dnd.stories';
1919
import {Flex} from '@react-spectrum/layout';
2020
import React from 'react';
@@ -41,6 +41,19 @@ export const DragOutOfTable: TableStory = {
4141
),
4242
name: 'Drag out of table'
4343
};
44+
45+
export const DragOutOfTableWithoutTableHeader: TableStory = {
46+
render: (args) => (
47+
<Flex direction="row" wrap alignItems="center" gap="size-200">
48+
<Droppable />
49+
<DragWithoutRowHeaderExample
50+
dragHookOptions={{onDragStart: action('dragStart'), onDragEnd: action('dragEnd')}}
51+
tableViewProps={args} />
52+
</Flex>
53+
),
54+
name: 'Drag out of table without table header'
55+
};
56+
4457
export const CustomDragPreview: TableStory = {
4558
args: {
4659
disabledKeys: ['Foo 2']

packages/@react-spectrum/table/stories/TableDnDExamples.tsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ let columns = [
2020
{name: 'IP Address', key: 'ip_address'}
2121
];
2222

23+
let columnsWithOutRowHeader = [
24+
{name: 'First name', key: 'first_name'},
25+
{name: 'Last name', key: 'last_name'},
26+
{name: 'Email', key: 'email'},
27+
{name: 'Department', key: 'department'},
28+
{name: 'Job Title', key: 'job_title'},
29+
{name: 'IP Address', key: 'ip_address'}
30+
];
31+
2332
let items = [
2433
{id: 'a', first_name: 'Vin', last_name: 'Charlet', email: 'vcharlet0@123-reg.co.uk', ip_address: '18.45.175.130', department: 'Services', job_title: 'Analog Circuit Design manager'},
2534
{id: 'b', first_name: 'Lexy', last_name: 'Maddison', email: 'lmaddison1@xinhuanet.com', ip_address: '238.210.151.48', department: 'Research and Development', job_title: 'Analog Circuit Design manager'},
@@ -69,6 +78,40 @@ export function DragExample(props?) {
6978
);
7079
}
7180

81+
export function DragWithoutRowHeaderExample(props?) {
82+
let {tableViewProps, dragHookOptions} = props;
83+
let getItems = (keys) => [...keys].map(key => {
84+
let item = items.find(item => item.id === key);
85+
return {
86+
'text/plain': `${item.first_name} ${item.last_name}`
87+
};
88+
});
89+
90+
let {dragAndDropHooks} = useDragAndDrop({
91+
getItems,
92+
getAllowedDropOperations() {
93+
getAllowedDropOperationsAction();
94+
return ['move', 'cancel'];
95+
},
96+
...dragHookOptions
97+
});
98+
99+
return (
100+
<TableView aria-label="TableView with dragging enabled" selectionMode="multiple" width={400} height={300} onSelectionChange={s => onSelectionChange([...s])} dragAndDropHooks={dragAndDropHooks} {...tableViewProps}>
101+
<TableHeader columns={columnsWithOutRowHeader}>
102+
{column => <Column minWidth={100}>{column.name}</Column>}
103+
</TableHeader>
104+
<TableBody items={items}>
105+
{item => (
106+
<Row>
107+
{key => <Cell>{item[key]}</Cell>}
108+
</Row>
109+
)}
110+
</TableBody>
111+
</TableView>
112+
);
113+
}
114+
72115
export function ReorderExample(props) {
73116
let {onDrop, onDragStart, onDragEnd, tableViewProps} = props;
74117
let list = useListData({

packages/@react-spectrum/table/test/TableDnd.test.js

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {CUSTOM_DRAG_TYPE} from '@react-aria/dnd/src/constants';
2424
import {DataTransfer, DataTransferItem, DragEvent, FileSystemDirectoryEntry, FileSystemFileEntry} from '@react-aria/dnd/test/mocks';
2525
import {DIRECTORY_DRAG_TYPE} from '@react-aria/dnd';
2626
import {DragBetweenTablesComplex} from '../stories/TableDnDUtilExamples';
27-
import {DragBetweenTablesExample, DragBetweenTablesRootOnlyExample, DragExample, DragOntoRowExample, ReorderExample} from '../stories/TableDnDExamples';
27+
import {DragBetweenTablesExample, DragBetweenTablesRootOnlyExample, DragExample, DragOntoRowExample, DragWithoutRowHeaderExample, ReorderExample} from '../stories/TableDnDExamples';
2828
import {Droppable} from '@react-aria/dnd/test/examples';
2929
import {Flex} from '@react-spectrum/layout';
3030
import {globalDndState} from '@react-aria/dnd/src/utils';
@@ -114,6 +114,12 @@ describe('TableView', function () {
114114
);
115115
}
116116

117+
function DraggbleWithoutRowHeader(props) {
118+
return (
119+
<DragWithoutRowHeaderExample onDrop={onDrop} onDragStart={onDragStart} onDragEnd={onDragEnd} {...props} />
120+
);
121+
}
122+
117123
function Reorderable(props) {
118124
return (
119125
<ReorderExample disabledKeys={['a']} onDrop={onDrop} onDragStart={onDragStart} onDragEnd={onDragEnd} {...props} />
@@ -197,6 +203,56 @@ describe('TableView', function () {
197203
expect(dataTransfer._dragImage.y).toBe(5);
198204
});
199205

206+
it('should render a drag preview with highlight selection style', function () {
207+
let {getByRole, getAllByText} = render(
208+
<DraggbleWithoutRowHeader tableViewProps={{selectedKeys: ['a'], selectionStyle: 'highlight'}} />
209+
);
210+
211+
let grid = getByRole('grid');
212+
let rowgroups = within(grid).getAllByRole('rowgroup');
213+
let rows = within(rowgroups[1]).getAllByRole('row');
214+
let row = rows[0];
215+
let cell = within(row).getAllByRole('rowheader')[0];
216+
let cellText = getAllByText(cell.textContent);
217+
expect(cellText).toHaveLength(1);
218+
219+
let dataTransfer = new DataTransfer();
220+
221+
fireEvent.pointerDown(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 5, clientY: 5});
222+
fireEvent(cell, new DragEvent('dragstart', {dataTransfer, clientX: 5, clientY: 5}));
223+
expect(dataTransfer._dragImage.node.tagName).toBe('DIV');
224+
225+
// Verify that when no rowHeader is set, the drag preview displays the text of the first element of the row
226+
expect(dataTransfer._dragImage.node.textContent).toBe('Vin');
227+
expect(dataTransfer._dragImage.x).toBe(5);
228+
expect(dataTransfer._dragImage.y).toBe(5);
229+
});
230+
231+
it('should render a drag preview with checkbox selection style', function () {
232+
let {getByRole, getAllByText} = render(
233+
<DraggbleWithoutRowHeader tableViewProps={{selectedKeys: ['a'], selectionStyle: 'checkbox'}} />
234+
);
235+
236+
let grid = getByRole('grid');
237+
let rowgroups = within(grid).getAllByRole('rowgroup');
238+
let rows = within(rowgroups[1]).getAllByRole('row');
239+
let row = rows[0];
240+
let cell = within(row).getAllByRole('rowheader')[0];
241+
let cellText = getAllByText(cell.textContent);
242+
expect(cellText).toHaveLength(1);
243+
244+
let dataTransfer = new DataTransfer();
245+
246+
fireEvent.pointerDown(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 5, clientY: 5});
247+
fireEvent(cell, new DragEvent('dragstart', {dataTransfer, clientX: 5, clientY: 5}));
248+
249+
// Verify that when no rowHeader is set, the drag preview displays the text of the first element of the row
250+
expect(dataTransfer._dragImage.node.tagName).toBe('DIV');
251+
expect(dataTransfer._dragImage.node.textContent).toBe('Vin');
252+
expect(dataTransfer._dragImage.x).toBe(5);
253+
expect(dataTransfer._dragImage.y).toBe(5);
254+
});
255+
200256

201257
it('should allow drag and drop of a single row', async function () {
202258
let {getByRole, getByText} = render(

packages/@react-stately/table/src/TableCollection.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -274,15 +274,7 @@ export class TableCollection<T> extends GridCollection<T> implements ITableColle
274274

275275
// Default row header column to the first one.
276276
if (this.rowHeaderColumnKeys.size === 0) {
277-
if (opts?.showSelectionCheckboxes) {
278-
if (opts?.showDragButtons) {
279-
this.rowHeaderColumnKeys.add(this.columns[2].key);
280-
} else {
281-
this.rowHeaderColumnKeys.add(this.columns[1].key);
282-
}
283-
} else {
284-
this.rowHeaderColumnKeys.add(this.columns[0].key);
285-
}
277+
this.rowHeaderColumnKeys.add(this.columns.find(column => !column.props?.isDragButtonCell && !column.props?.isSelectionCell).key);
286278
}
287279
}
288280

0 commit comments

Comments
 (0)