-
-
Notifications
You must be signed in to change notification settings - Fork 595
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
feat: column resizable
Showing
17 changed files
with
646 additions
and
118 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,95 +1,107 @@ | ||
import React from 'react'; | ||
import { Resizable } from 'react-resizable'; | ||
import Table from 'rc-table'; | ||
import React, { useState } from 'react'; | ||
import Table, { INTERNAL_HOOKS } from 'rc-table'; | ||
import type { ColumnType } from 'rc-table'; | ||
import '../../assets/index.less'; | ||
import 'react-resizable/css/styles.css'; | ||
import type { ColumnType } from '@/interface'; | ||
|
||
const ResizableTitle = props => { | ||
const { onResize, width, ...restProps } = props; | ||
const data = [ | ||
{ a: '123', b: 'xxxxxxxx xxxxxxxx', d: 3, key: '1' }, | ||
{ a: 'cdd', b: 'edd12221 edd12221', d: 3, key: '2' }, | ||
{ a: '133', c: 'edd12221 edd12221', d: 2, key: '3' }, | ||
{ a: '133', c: 'edd12221 edd12221', d: 2, key: '4' }, | ||
{ a: '133', c: 'edd12221 edd12221', d: 2, key: '5' }, | ||
{ a: '133', c: 'edd12221 edd12221', d: 2, key: '6' }, | ||
{ a: '133', c: 'edd12221 edd12221', d: 2, key: '7' }, | ||
{ a: '133', c: 'edd12221 edd12221', d: 2, key: '8' }, | ||
{ a: '133', c: 'edd12221 edd12221', d: 2, key: '9' }, | ||
]; | ||
|
||
if (!width) { | ||
return <th {...restProps} />; | ||
} | ||
const Demo = () => { | ||
const [widthMap, setWidthMap] = useState<Map<React.Key, number>>(new Map()); | ||
|
||
const columns1 = [ | ||
{ title: 'title1', dataIndex: 'aaa', key: 'aaa', width: 100 }, | ||
{ title: 'title2', dataIndex: 'bbb', key: 'bbb', width: 100 }, | ||
].map(i => ({ | ||
...i, | ||
resizable: true, | ||
width: widthMap.get(i.key ?? i.dataIndex) ?? i.width, | ||
})) as ColumnType<any>[]; | ||
|
||
const columns2 = [ | ||
{ title: 'title1', dataIndex: 'a', key: 'a', fixed: 'left' }, | ||
{ title: 'title2', dataIndex: 'b', key: 'b', fixed: 'left' }, | ||
{ title: 'title3', dataIndex: 'c', key: 'c' }, | ||
{ title: 'title4', dataIndex: 'b', key: 'd' }, | ||
{ title: 'title5', dataIndex: 'b', key: 'e' }, | ||
{ title: 'title6', dataIndex: 'b', key: 'f' }, | ||
{ title: 'title7', dataIndex: 'b', key: 'g' }, | ||
{ title: 'title8', dataIndex: 'b', key: 'h' }, | ||
{ title: 'title9', dataIndex: 'b', key: 'i' }, | ||
{ title: 'title10', dataIndex: 'b', key: 'j' }, | ||
{ title: 'title11', dataIndex: 'b', key: 'k', fixed: 'right' }, | ||
{ title: 'title12', dataIndex: 'b', key: 'l', fixed: 'right' }, | ||
].map(i => ({ | ||
...i, | ||
resizable: true, | ||
width: widthMap.get(i.key ?? i.dataIndex) ?? 150, | ||
})) as ColumnType<any>[]; | ||
|
||
return ( | ||
<Resizable width={width} height={0} onResize={onResize}> | ||
<th {...restProps} /> | ||
</Resizable> | ||
<div> | ||
table width: 800px {'columns=[{width: 100, width: 100}]'} 情况 | ||
<Table | ||
style={{ width: 800 }} | ||
scroll={{ y: 300, x: columns1.reduce((t, c) => t + (c.width as number), 0) }} | ||
columns={columns1} | ||
data={data} | ||
onColumnResizeComplete={({ columnWidths }) => { | ||
setWidthMap(prev => { | ||
const result = new Map(prev); | ||
columnWidths.forEach(i => { | ||
result.set(i.columnKey, i.width); | ||
}); | ||
return result; | ||
}); | ||
}} | ||
internalHooks={INTERNAL_HOOKS} | ||
getContainerWidth={(ele, width) => { | ||
// Minus border | ||
const borderWidth = getComputedStyle( | ||
ele.querySelector('.rc-table-body'), | ||
).borderInlineStartWidth; | ||
const mergedWidth = width - parseInt(borderWidth, 10); | ||
return mergedWidth; | ||
}} | ||
/> | ||
<br /> | ||
大多数情况 | ||
<Table | ||
style={{ width: 800 }} | ||
scroll={{ y: 300, x: columns2.reduce((t, c) => t + (c.width as number), 0) }} | ||
columns={columns2} | ||
data={data} | ||
onColumnResizeComplete={({ columnWidths }) => { | ||
setWidthMap(prev => { | ||
const result = new Map(prev); | ||
columnWidths.forEach(i => { | ||
result.set(i.columnKey, i.width); | ||
}); | ||
return result; | ||
}); | ||
}} | ||
internalHooks={INTERNAL_HOOKS} | ||
getContainerWidth={(ele, width) => { | ||
// Minus border | ||
const borderWidth = getComputedStyle( | ||
ele.querySelector('.rc-table-body'), | ||
).borderInlineStartWidth; | ||
const mergedWidth = width - parseInt(borderWidth, 10); | ||
return mergedWidth; | ||
}} | ||
/> | ||
{/* <Table resizable style={{ width: 800 }} columns={columns} data={data} /> */} | ||
</div> | ||
); | ||
}; | ||
|
||
interface RecordType { | ||
a: string; | ||
b?: string; | ||
c?: string; | ||
d?: number; | ||
key: string; | ||
} | ||
|
||
interface DemoState { | ||
columns: ColumnType<RecordType>[]; | ||
} | ||
|
||
class Demo extends React.Component<{}, DemoState> { | ||
state: DemoState = { | ||
columns: [ | ||
{ title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, | ||
{ title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, | ||
{ title: 'title3', dataIndex: 'c', key: 'c', width: 200 }, | ||
{ | ||
title: 'Operations', | ||
dataIndex: '', | ||
key: 'd', | ||
render() { | ||
return <a href="#">Operations</a>; | ||
}, | ||
}, | ||
], | ||
}; | ||
|
||
components = { | ||
header: { | ||
cell: ResizableTitle, | ||
}, | ||
}; | ||
|
||
data = [ | ||
{ a: '123', key: '1' }, | ||
{ a: 'cdd', b: 'edd', key: '2' }, | ||
{ a: '1333', c: 'eee', d: 2, key: '3' }, | ||
]; | ||
|
||
handleResize = | ||
index => | ||
(e, { size }) => { | ||
this.setState(({ columns }) => { | ||
const nextColumns = [...columns]; | ||
nextColumns[index] = { | ||
...nextColumns[index], | ||
width: size.width, | ||
}; | ||
return { columns: nextColumns }; | ||
}); | ||
}; | ||
|
||
render() { | ||
const columns = this.state.columns.map((col, index) => ({ | ||
...col, | ||
onHeaderCell: (column: ColumnType<RecordType>) => | ||
({ | ||
width: column.width, | ||
onResize: this.handleResize(index), | ||
}) as any, | ||
})); | ||
|
||
return ( | ||
<div> | ||
<h2>Integrate with react-resizable</h2> | ||
<Table components={this.components} columns={columns} data={this.data} /> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
export default Demo; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import * as React from 'react'; | ||
import type { CellProps } from '../Cell'; | ||
import Cell from '../Cell'; | ||
import useCellResize from './useCellResize'; | ||
import { useContext } from '@rc-component/context'; | ||
import TableContext from '../context/TableContext'; | ||
|
||
interface HeaderCellProps<RecordType> extends CellProps<RecordType> { | ||
columnKey?: React.Key; | ||
resizable?: boolean; | ||
minWidth?: number; | ||
} | ||
|
||
function HeaderCell<RecordType>({ | ||
columnKey, | ||
resizable, | ||
minWidth, | ||
...cellProps | ||
}: HeaderCellProps<RecordType>) { | ||
const { supportSticky } = useContext(TableContext, ['supportSticky']); | ||
|
||
const { fixRight, prefixCls } = cellProps; | ||
const isFixRight = typeof fixRight === 'number' && supportSticky; | ||
const cellPrefixCls = `${prefixCls}-cell`; | ||
|
||
const resizeHandleNode = useCellResize(columnKey, isFixRight, cellPrefixCls, resizable, minWidth); | ||
|
||
return <Cell {...cellProps} appendNode={resizeHandleNode} />; | ||
} | ||
|
||
export default HeaderCell; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
import TableContext from '../context/TableContext'; | ||
import { useContext } from '@rc-component/context'; | ||
import { useEvent } from 'rc-util'; | ||
import React, { useEffect, useRef, useState } from 'react'; | ||
import { createPortal } from 'react-dom'; | ||
|
||
export default function useCelResize( | ||
columnKey: React.Key, | ||
isFixRight: boolean, | ||
cellPrefixCls: string, | ||
resizable?: boolean, | ||
minWidth: number = 0, | ||
) { | ||
const { | ||
colsWidths, | ||
colsKeys, | ||
colWidths, | ||
componentWidth, | ||
fullTableRef, | ||
onColumnResizeComplete, | ||
onResizingChange, | ||
} = useContext(TableContext, [ | ||
'colWidths', | ||
'colsKeys', | ||
'colsWidths', | ||
'componentWidth', | ||
'fullTableRef', | ||
'onColumnResizeComplete', | ||
'onResizingChange', | ||
]); | ||
const [isResizing, setIsResizing] = useState(false); | ||
const [lineLeft, setLineLeft] = useState(0); | ||
const lineStartLeftRef = useRef(0); | ||
const startRealWidth = useRef(0); | ||
const startPageX = useRef(0); | ||
const mouseMoveRef = useRef<(event: MouseEvent) => void>(null); | ||
const mouseUpRef = useRef<(event: MouseEvent) => void>(null); | ||
|
||
const removeResizeListener = () => { | ||
document.body.removeEventListener('mousemove', mouseMoveRef.current); | ||
document.body.removeEventListener('mouseup', mouseUpRef.current); | ||
}; | ||
|
||
useEffect(() => removeResizeListener, []); | ||
|
||
const onResize = useEvent((event: MouseEvent, isResizeEnd?: boolean) => { | ||
const offset = event.pageX - startPageX.current; | ||
const oldWidth = colsWidths.get(columnKey); | ||
let newWidth = startRealWidth.current + (isFixRight ? -offset : offset); | ||
|
||
if (newWidth < minWidth) { | ||
newWidth = minWidth; | ||
} | ||
setLineLeft( | ||
lineStartLeftRef.current + | ||
(isFixRight ? startRealWidth.current - newWidth : newWidth - startRealWidth.current), | ||
); | ||
|
||
if (isResizeEnd) { | ||
const totalWidth = colWidths.reduce((total, width) => total + width, 0); | ||
const smallThanWidth = componentWidth - (totalWidth - oldWidth + newWidth); | ||
// If it is less than the width of the table, the remaining width will be allocated to the column on the right. | ||
// If there is no column on the right, it will be allocated to the column on the left. | ||
let addWidthColumnKey: React.Key; | ||
const isDecreasingWidth = oldWidth - newWidth > 0; | ||
if (smallThanWidth > 0 && isDecreasingWidth) { | ||
const index = colsKeys.findIndex(key => key === columnKey); | ||
addWidthColumnKey = colsKeys[index + 1] ?? colsKeys[index - 1]; | ||
} | ||
|
||
const columnWidthsMap = new Map(colsWidths); | ||
columnWidthsMap.set(columnKey, newWidth); | ||
if (addWidthColumnKey) { | ||
const addWidthColumnNewWidth = colsWidths.get(addWidthColumnKey) + smallThanWidth; | ||
columnWidthsMap.set(addWidthColumnKey, addWidthColumnNewWidth); | ||
} | ||
const columnWidths = Array.from(columnWidthsMap).map(([key, width]) => ({ | ||
columnKey: key, | ||
width, | ||
})); | ||
onColumnResizeComplete?.({ columnKey, width: newWidth, columnWidths }); | ||
} | ||
}); | ||
|
||
const onResizeEnd = (event: MouseEvent) => { | ||
setIsResizing(false); | ||
onResizingChange(false); | ||
removeResizeListener(); | ||
onResize(event, true); | ||
}; | ||
|
||
const onResizeStart = (event: React.MouseEvent) => { | ||
// Block selected text | ||
event.preventDefault(); | ||
const left = | ||
(event.target as HTMLElement).parentElement.getBoundingClientRect()[ | ||
isFixRight ? 'left' : 'right' | ||
] - fullTableRef.current.getBoundingClientRect().left; | ||
setLineLeft(left); | ||
lineStartLeftRef.current = left; | ||
startRealWidth.current = colsWidths.get(columnKey); | ||
startPageX.current = event.pageX; | ||
document.body.addEventListener('mousemove', onResize); | ||
document.body.addEventListener('mouseup', onResizeEnd); | ||
mouseMoveRef.current = onResize; | ||
mouseUpRef.current = onResizeEnd; | ||
onResizingChange(true); | ||
setIsResizing(true); | ||
}; | ||
|
||
const resizeHandleNode = resizable && ( | ||
<> | ||
<div | ||
className={`${cellPrefixCls}-resize-handle`} | ||
style={{ display: isResizing ? 'none' : undefined }} | ||
onMouseDown={onResizeStart} | ||
/> | ||
{isResizing && | ||
createPortal( | ||
<div | ||
className={`${cellPrefixCls}-resize-line`} | ||
style={{ | ||
left: lineLeft, | ||
}} | ||
/>, | ||
fullTableRef.current, | ||
)} | ||
</> | ||
); | ||
|
||
return resizeHandleNode; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,276 @@ | ||
import { mount } from 'enzyme'; | ||
import Table from '../src'; | ||
import React from 'react'; | ||
import { act } from 'react-dom/test-utils'; | ||
import RcResizeObserver, { _rs } from 'rc-resize-observer'; | ||
import { safeAct } from './utils'; | ||
import { spyElementPrototype } from 'rc-util/lib/test/domHook'; | ||
|
||
describe('Table.resizable', () => { | ||
let domSpy; | ||
let containerSpy; | ||
|
||
beforeAll(() => { | ||
domSpy = spyElementPrototype(HTMLElement, 'offsetParent', { | ||
get: () => ({}), | ||
}); | ||
containerSpy = spyElementPrototype(HTMLDivElement, 'offsetWidth', { | ||
get: () => 800, | ||
}); | ||
}); | ||
|
||
beforeEach(() => { | ||
vi.useFakeTimers(); | ||
}); | ||
|
||
afterAll(() => { | ||
domSpy.mockRestore(); | ||
containerSpy.mockRestore(); | ||
}); | ||
|
||
it('change width in onColumnResizeComplete', async () => { | ||
const onColumnResizeComplete = vi.fn(); | ||
|
||
const App = () => { | ||
const [widthMap, setWidthMap] = React.useState(new Map()); | ||
|
||
const columns = [ | ||
{ key: 'a', dataIndex: 'a', width: 400, resizable: true }, | ||
{ key: 'b', dataIndex: 'b', width: 400, resizable: true }, | ||
].map(col => ({ ...col, width: widthMap.get(col.key ?? col.dataIndex) || col.width })); | ||
|
||
return ( | ||
<Table | ||
data={[{ a: '123', b: '123', key: '1' }]} | ||
columns={columns} | ||
scroll={{ x: columns.reduce((t, c) => t + c.width, 0) }} | ||
onColumnResizeComplete={info => { | ||
setWidthMap(prev => { | ||
const result = new Map(prev); | ||
info.columnWidths.forEach(i => { | ||
result.set(i.columnKey, i.width); | ||
}); | ||
return result; | ||
}); | ||
onColumnResizeComplete(info); | ||
}} | ||
/> | ||
); | ||
}; | ||
const wrapper = mount(<App />); | ||
|
||
async function triggerResize(resizeList) { | ||
wrapper.find(RcResizeObserver.Collection).first().props().onBatchResize(resizeList); | ||
await safeAct(wrapper); | ||
wrapper.update(); | ||
} | ||
|
||
await triggerResize([ | ||
{ | ||
data: wrapper.find('ResizeObserver').at(1).props().data, | ||
size: { width: 400, offsetWidth: 400 }, | ||
}, | ||
{ | ||
data: wrapper.find('ResizeObserver').at(2).props().data, | ||
size: { width: 400, offsetWidth: 400 }, | ||
}, | ||
]); | ||
|
||
wrapper.find('.rc-table-cell-resize-handle').first().simulate('mousedown', { pageX: 0 }); | ||
|
||
const mousemoveEvent = new Event('mousemove'); | ||
mousemoveEvent.pageX = 100; | ||
|
||
await act(async () => { | ||
document.body.dispatchEvent(mousemoveEvent); | ||
await Promise.resolve(); | ||
wrapper.update(); | ||
}); | ||
|
||
const mouseupEvent = new Event('mouseup'); | ||
mouseupEvent.pageX = 100; | ||
|
||
await act(async () => { | ||
document.body.dispatchEvent(mouseupEvent); | ||
await Promise.resolve(); | ||
wrapper.update(); | ||
}); | ||
|
||
expect(onColumnResizeComplete).toHaveBeenCalledWith({ | ||
columnKey: 'a', | ||
width: 500, | ||
columnWidths: [ | ||
{ columnKey: 'a', width: 500 }, | ||
{ columnKey: 'b', width: 400 }, | ||
], | ||
}); | ||
|
||
expect(wrapper.find('colgroup col').at(0).props().style.width).toBe(500); | ||
expect(wrapper.find('colgroup col').at(1).props().style.width).toBe(400); | ||
}); | ||
|
||
it('columns total width < componentWidth', async () => { | ||
const onColumnResizeComplete = vi.fn(); | ||
|
||
const App = () => { | ||
const [widthMap, setWidthMap] = React.useState(new Map()); | ||
|
||
const columns = [ | ||
{ key: 'a', dataIndex: 'a', width: 100, resizable: true }, | ||
{ key: 'b', dataIndex: 'b', width: 100, resizable: true }, | ||
].map(col => ({ ...col, width: widthMap.get(col.key ?? col.dataIndex) || col.width || 100 })); | ||
|
||
return ( | ||
<Table | ||
style={{ width: 800 }} | ||
data={[{ a: '123', b: 'xxxxxxxx', key: '1' }]} | ||
columns={columns} | ||
scroll={{ x: columns.reduce((t, c) => t + c.width, 0) }} | ||
onColumnResizeComplete={info => { | ||
setWidthMap(prev => { | ||
const result = new Map(prev); | ||
info.columnWidths.forEach(i => { | ||
result.set(i.columnKey, i.width); | ||
}); | ||
return result; | ||
}); | ||
onColumnResizeComplete(info); | ||
}} | ||
/> | ||
); | ||
}; | ||
const wrapper = mount(<App />); | ||
|
||
async function triggerResize(resizeList) { | ||
wrapper.find(RcResizeObserver.Collection).first().props().onBatchResize(resizeList); | ||
await safeAct(wrapper); | ||
wrapper.update(); | ||
} | ||
|
||
wrapper.find(RcResizeObserver).first().props().onResize({ width: 800 }); | ||
|
||
await triggerResize([ | ||
{ | ||
data: wrapper.find('ResizeObserver').at(1).props().data, | ||
size: { width: 400, offsetWidth: 400 }, | ||
}, | ||
{ | ||
data: wrapper.find('ResizeObserver').at(2).props().data, | ||
size: { width: 400, offsetWidth: 400 }, | ||
}, | ||
]); | ||
|
||
wrapper.find('.rc-table-cell-resize-handle').first().simulate('mousedown', { pageX: 0 }); | ||
|
||
const mousemoveEvent = new Event('mousemove'); | ||
mousemoveEvent.pageX = -100; | ||
|
||
await act(async () => { | ||
document.body.dispatchEvent(mousemoveEvent); | ||
await Promise.resolve(); | ||
wrapper.update(); | ||
}); | ||
|
||
const mouseupEvent = new Event('mouseup'); | ||
mouseupEvent.pageX = -100; | ||
|
||
await act(async () => { | ||
document.body.dispatchEvent(mouseupEvent); | ||
await Promise.resolve(); | ||
wrapper.update(); | ||
}); | ||
|
||
expect(onColumnResizeComplete).toHaveBeenCalledWith({ | ||
columnKey: 'a', | ||
width: 300, | ||
columnWidths: [ | ||
{ columnKey: 'a', width: 300 }, | ||
{ columnKey: 'b', width: 500 }, | ||
], | ||
}); | ||
|
||
expect(wrapper.find('colgroup col').at(0).props().style.width).toBe(300); | ||
expect(wrapper.find('colgroup col').at(1).props().style.width).toBe(500); | ||
}); | ||
|
||
it('minWidth should be worked', async () => { | ||
const onColumnResizeComplete = vi.fn(); | ||
|
||
const App = () => { | ||
const [widthMap, setWidthMap] = React.useState(new Map()); | ||
|
||
const columns = [ | ||
{ key: 'a', dataIndex: 'a', width: 800, resizable: true, minWidth: 400 }, | ||
{ key: 'b', dataIndex: 'b', width: 800, resizable: true }, | ||
].map(col => ({ ...col, width: widthMap.get(col.key ?? col.dataIndex) || col.width })); | ||
|
||
return ( | ||
<Table | ||
data={[{ a: '123', b: '123', key: '1' }]} | ||
columns={columns} | ||
scroll={{ x: columns.reduce((t, c) => t + c.width, 0) }} | ||
onColumnResizeComplete={info => { | ||
setWidthMap(prev => { | ||
const result = new Map(prev); | ||
info.columnWidths.forEach(i => { | ||
result.set(i.columnKey, i.width); | ||
}); | ||
return result; | ||
}); | ||
onColumnResizeComplete(info); | ||
}} | ||
/> | ||
); | ||
}; | ||
const wrapper = mount(<App />); | ||
|
||
async function triggerResize(resizeList) { | ||
wrapper.find(RcResizeObserver.Collection).first().props().onBatchResize(resizeList); | ||
await safeAct(wrapper); | ||
wrapper.update(); | ||
} | ||
|
||
await triggerResize([ | ||
{ | ||
data: wrapper.find('ResizeObserver').at(1).props().data, | ||
size: { width: 800, offsetWidth: 800 }, | ||
}, | ||
{ | ||
data: wrapper.find('ResizeObserver').at(2).props().data, | ||
size: { width: 800, offsetWidth: 800 }, | ||
}, | ||
]); | ||
|
||
wrapper.find('.rc-table-cell-resize-handle').first().simulate('mousedown', { pageX: 0 }); | ||
|
||
const mousemoveEvent = new Event('mousemove'); | ||
mousemoveEvent.pageX = -1000; | ||
|
||
await act(async () => { | ||
document.body.dispatchEvent(mousemoveEvent); | ||
await Promise.resolve(); | ||
wrapper.update(); | ||
}); | ||
|
||
const mouseupEvent = new Event('mouseup'); | ||
mouseupEvent.pageX = -1000; | ||
|
||
await act(async () => { | ||
document.body.dispatchEvent(mouseupEvent); | ||
await Promise.resolve(); | ||
wrapper.update(); | ||
}); | ||
|
||
expect(onColumnResizeComplete).toHaveBeenCalledWith({ | ||
columnKey: 'a', | ||
width: 400, | ||
columnWidths: [ | ||
{ columnKey: 'a', width: 400 }, | ||
{ columnKey: 'b', width: 800 }, | ||
], | ||
}); | ||
|
||
expect(wrapper.find('colgroup col').at(0).props().style.width).toBe(400); | ||
expect(wrapper.find('colgroup col').at(1).props().style.width).toBe(800); | ||
}); | ||
}); |