-
-
Notifications
You must be signed in to change notification settings - Fork 73
3.1 issue142 copy paste #180
Changes from 6 commits
e6f273b
2b4a3e3
004a10d
4ff1e5c
a9f6b11
c34c35c
6de8ece
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import * as R from 'ramda'; | ||
|
||
import Logger from 'core/Logger'; | ||
|
||
import { ActiveCell, Columns, Data, ColumnType } from 'dash-table/components/Table/props'; | ||
import isEditable from 'dash-table/derived/cell/isEditable'; | ||
|
||
export default ( | ||
values: string[][], | ||
activeCell: ActiveCell, | ||
derived_viewport_indices: number[], | ||
columns: Columns, | ||
data: Data, | ||
overflowColumns: boolean = true, | ||
overflowRows: boolean = true | ||
): { data: Data, columns: Columns } | void => { | ||
if (!overflowRows) { | ||
Logger.debug(`Clipboard -- Sorting or filtering active, do not create new rows`); | ||
} | ||
|
||
if (!overflowColumns) { | ||
Logger.debug(`Clipboard -- Do not create new columns`); | ||
} | ||
|
||
let newData = data; | ||
const newColumns = columns; | ||
|
||
if (overflowColumns && values[0].length + activeCell[1] >= columns.length) { | ||
for ( | ||
let i = columns.length; | ||
i < values[0].length + activeCell[1]; | ||
i++ | ||
) { | ||
newColumns.push({ | ||
id: `Column ${i + 1}`, | ||
name: `Column ${i + 1}`, | ||
type: ColumnType.Text | ||
}); | ||
newData.forEach(row => (row[`Column ${i}`] = '')); | ||
} | ||
} | ||
|
||
const realActiveRow = derived_viewport_indices[activeCell[0]]; | ||
if (overflowRows && values.length + realActiveRow >= data.length) { | ||
const emptyRow: any = {}; | ||
columns.forEach(c => (emptyRow[c.id] = '')); | ||
newData = R.concat( | ||
newData, | ||
R.repeat( | ||
emptyRow, | ||
values.length + realActiveRow - data.length | ||
) | ||
); | ||
} | ||
|
||
const lastEntry = derived_viewport_indices.slice(-1)[0] || 0; | ||
const viewportSize = derived_viewport_indices.length; | ||
|
||
values.forEach((row: string[], i: number) => | ||
row.forEach((cell: string, j: number) => { | ||
const viewportIndex = activeCell[0] + i; | ||
|
||
let iRealCell: number | undefined = viewportSize > viewportIndex ? | ||
derived_viewport_indices[viewportIndex] : | ||
overflowRows ? | ||
lastEntry + (viewportIndex - viewportSize + 1) : | ||
undefined; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is where the main change is. If the row falls in the existing (non-appended) data, map it directly to the viewport indices, if not, map it to the last entry of the viewport + offset, if overflow is not supported return undefined instead (will be validated below to exclude those rows from being processed) |
||
|
||
if (iRealCell === undefined) { | ||
return; | ||
} | ||
|
||
const jOffset = activeCell[1] + j; | ||
const col = newColumns[jOffset]; | ||
if (col && isEditable(true, col.editable)) { | ||
newData = R.set( | ||
R.lensPath([iRealCell, col.id]), | ||
cell, | ||
newData | ||
); | ||
} | ||
}) | ||
); | ||
|
||
return { data: newData, columns: newColumns }; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
import * as R from 'ramda'; | ||
|
||
import applyClipboardToData from 'dash-table/utils/applyClipboardToData'; | ||
|
||
describe('clipboard', () => { | ||
describe('with column/row overflow allowed', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Various tests to validate behavior when copying n rows into a m rows dataframe with overflow allowed (new rows get appended) |
||
it('pastes one line at [0, 0] in one line df', () => { | ||
const res = applyClipboardToData( | ||
R.range(0, 1).map(value => [`${value}`]), | ||
[0, 0], | ||
R.range(0, 1), | ||
['c1'].map(id => ({ id: id, name: id })), | ||
R.range(0, 1).map(() => ({ c1: 'c1' })), | ||
true, | ||
true | ||
); | ||
|
||
expect(res).to.not.equal(undefined); | ||
|
||
if (res) { | ||
expect(res.data.length).to.equal(1); | ||
expect(res.data[0].c1).to.equal('0'); | ||
} | ||
}); | ||
|
||
it('pastes two lines at [0, 0] in one line df', () => { | ||
const res = applyClipboardToData( | ||
R.range(0, 2).map(value => [`${value}`]), | ||
[0, 0], | ||
R.range(0, 1), | ||
['c1'].map(id => ({ id: id, name: id })), | ||
R.range(0, 1).map(() => ({ c1: 'c1' })), | ||
true, | ||
true | ||
); | ||
|
||
expect(res).to.not.equal(undefined); | ||
|
||
if (res) { | ||
expect(res.data.length).to.equal(2); | ||
expect(res.data[0].c1).to.equal('0'); | ||
expect(res.data[1].c1).to.equal('1'); | ||
} | ||
}); | ||
|
||
it('pastes ten lines at [0, 0] in three line df', () => { | ||
const res = applyClipboardToData( | ||
R.range(0, 10).map(value => [`${value}`]), | ||
[0, 0], | ||
R.range(0, 3), | ||
['c1'].map(id => ({ id: id, name: id })), | ||
R.range(0, 3).map(() => ({ c1: 'c1' })), | ||
true, | ||
true | ||
); | ||
|
||
expect(res).to.not.equal(undefined); | ||
|
||
if (res) { | ||
expect(res.data.length).to.equal(10); | ||
for (let i = 0; i < 10; ++i) { | ||
expect(res.data[i].c1).to.equal(`${i}`); | ||
} | ||
} | ||
}); | ||
|
||
it('pastes ten lines at [1, 0] in three line df', () => { | ||
const res = applyClipboardToData( | ||
R.range(0, 10).map(value => [`${value}`]), | ||
[1, 0], | ||
R.range(0, 3), | ||
['c1'].map(id => ({ id: id, name: id })), | ||
R.range(0, 3).map(() => ({ c1: 'c1' })), | ||
true, | ||
true | ||
); | ||
|
||
expect(res).to.not.equal(undefined); | ||
|
||
if (res) { | ||
expect(res.data.length).to.equal(11); | ||
expect(res.data[0].c1).to.equal('c1'); | ||
for (let i = 0; i < 10; ++i) { | ||
expect(res.data[i + 1].c1).to.equal(`${i}`); | ||
} | ||
} | ||
}); | ||
}); | ||
|
||
describe('with column overflow allowed, row overflow not allowed', () => { | ||
it('pastes one line at [0, 0] in one line df', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Various tests to validate behavior when copying n rows into a m rows dataframe with overflow unallowed (no new rows appended) |
||
const res = applyClipboardToData( | ||
R.range(0, 1).map(value => [`${value}`]), | ||
[0, 0], | ||
R.range(0, 1), | ||
['c1'].map(id => ({ id: id, name: id })), | ||
R.range(0, 1).map(() => ({ c1: 'c1' })), | ||
true, | ||
false | ||
); | ||
|
||
expect(res).to.not.equal(undefined); | ||
|
||
if (res) { | ||
expect(res.data.length).to.equal(1); | ||
expect(res.data[0].c1).to.equal('0'); | ||
} | ||
}); | ||
|
||
it('pastes two lines at [0, 0] in one line df', () => { | ||
const res = applyClipboardToData( | ||
R.range(0, 2).map(value => [`${value}`]), | ||
[0, 0], | ||
R.range(0, 1), | ||
['c1'].map(id => ({ id: id, name: id })), | ||
R.range(0, 1).map(() => ({ c1: 'c1' })), | ||
true, | ||
false | ||
); | ||
|
||
expect(res).to.not.equal(undefined); | ||
|
||
if (res) { | ||
expect(res.data.length).to.equal(1); | ||
expect(res.data[0].c1).to.equal('0'); | ||
} | ||
}); | ||
|
||
it('pastes ten lines at [0, 0] in three line df', () => { | ||
const res = applyClipboardToData( | ||
R.range(0, 10).map(value => [`${value}`]), | ||
[0, 0], | ||
R.range(0, 3), | ||
['c1'].map(id => ({ id: id, name: id })), | ||
R.range(0, 3).map(() => ({ c1: 'c1' })), | ||
true, | ||
false | ||
); | ||
|
||
expect(res).to.not.equal(undefined); | ||
|
||
if (res) { | ||
expect(res.data.length).to.equal(3); | ||
for (let i = 0; i < 3; ++i) { | ||
expect(res.data[i].c1).to.equal(`${i}`); | ||
} | ||
} | ||
}); | ||
|
||
it('pastes ten lines at [1, 0] in three line df', () => { | ||
const res = applyClipboardToData( | ||
R.range(0, 10).map(value => [`${value}`]), | ||
[1, 0], | ||
R.range(0, 3), | ||
['c1'].map(id => ({ id: id, name: id })), | ||
R.range(0, 3).map(() => ({ c1: 'c1' })), | ||
true, | ||
false | ||
); | ||
|
||
expect(res).to.not.equal(undefined); | ||
|
||
if (res) { | ||
expect(res.data.length).to.equal(3); | ||
expect(res.data[0].c1).to.equal('c1'); | ||
for (let i = 0; i < 2; ++i) { | ||
expect(res.data[i + 1].c1).to.equal(`${i}`); | ||
} | ||
} | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isolated the pasting logic so it's not dependent on sheetclip and the clipboard event. Makes unit testing cleaner.