-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
Add support for copying table cells with cmd+c hotkey #542
Conversation
003d4e0
to
171ee5a
Compare
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.
if you don't specify getCells
, hotkey is still defined (will appear in dialog) but it'll never work.
packages/table/src/table.tsx
Outdated
* If exists, a callback that returns the data for a specific cell. This need not | ||
* match the value displayed in the `<Cell>` component. The value will be | ||
* invisibly added as `textContent` into the DOM before copying. If not exists, | ||
* copy via hotkeys (mod+c) will not work |
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.
trailing period please
packages/table/src/table.tsx
Outdated
}; | ||
|
||
return <Hotkeys> | ||
<Hotkey label="copy selected table cells" group="table" combo="mod+c" onKeyDown={doCopy} /> |
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.
"Copy selected..." sentence casing
packages/table/src/table.tsx
Outdated
@@ -349,6 +360,30 @@ export class Table extends AbstractComponent<ITableProps, ITableState> { | |||
); | |||
} | |||
|
|||
public renderHotkeys() { | |||
const doCopy = (e: KeyboardEvent) => { |
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.
why is this defined here instead of private handleCopyHotkey
?
packages/table/preview/index.tsx
Outdated
return Utils.toBase26Alpha(col) + (row + 1); | ||
}; | ||
|
||
const tablePropsWithDefaults = Object.assign({numRows, getCellData}, ...tableProps); |
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.
use the object spread notation that you just reverted. https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html#object-spread-and-rest
{ numRows, getCellData, ...tableProps }
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.
@giladgray can you enable the ban
lint rule to ban Object.assign?
looks like it's time to increase table test coverage... can't keep lowering the threshold. |
That's fine I should add tests anyways; just wanted eyes on it first :) |
you can put the browser checks into the compatibility file introduced here #590 |
packages/table/src/table.tsx
Outdated
/> | ||
); | ||
} else { | ||
return null; |
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.
return undefined
please
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.
ping
packages/table/test/harness.ts
Outdated
} | ||
|
||
/** | ||
* Use feature detection to determine current browser. |
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.
lol this is kinda backwards... you're supposed to use feature detection instead of browser detection: ensure that the feature you want exists, rather than asserting the browser "make." it's version-independent and much less prone to false positives.
is there a more reliable way of doing these checks that we can shim in here quickly?
at the very least move this to its own file.
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.
I stole all of this from hotkeys test framework, that's how it does it. I couldn't figure out how to share test stuff between them. Is there a good way to do this? I'll totally do that.
And yeah agree that it sucks but...
packages/table/preview/index.tsx
Outdated
}; | ||
|
||
const onCopy = () => { | ||
console.log("did copy"); |
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.
how about writing out the copied contents in this log?
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.
Oooh better idea. 👍
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.
Oh wait I can't do this because it's not passing that info in -- it just gets success. I'm not sure what I'd pass to it actually. Lemme think about it.
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.
Maybe the on copy method itself should have an argument that contains the sparse copied cell data?
packages/table/src/table.tsx
Outdated
* If exists, a callback that returns the data for a specific cell. This need not | ||
* match the value displayed in the `<Cell>` component. The value will be | ||
* invisibly added as `textContent` into the DOM before copying. If not exists, | ||
* copy via hotkeys (mod+c) will not work. |
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.
The fact that this is used for mod+c copying should appear sooner in the description. also, friendlier language:
If provided, this callback will be used to get cell contents copied to a user's clipboard during a hotkey (mod+c) interaction in the
<Cell>
component. The value will be invisibly added astextContent
into the DOM before copying.
I really wish we didn't need to make this prop required for the feature to be enabled (of course it should be configurable). Is there any way that users can get mod+c copying for free upon upgrading? Can we look at the rendered contents of their cell renderer?
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.
This works the same as it does for the context menu copy -- you need to give that a callback too. The reason why is what you want rendered (with divs and styles and pretty things) may be radically different from what you want copied (the data). I've talked to @themadcreator at length about this, and this seems like the best way :-/
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.
For example, @adidahiya, if they select a whole column, we'd want to copy all cells in the columns, but due to virtualized rendering, there's no guarantee that all the cells in that column will have ever been rendered. Furthermore, some cells have truncated content (like JSON content), so copying the actual cell text would be incomplete.
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.
But we could consider renaming this getCellClipboardData
since without the naming, users might think this is used to fill cells when rendering.
packages/table/src/table.tsx
Outdated
if (sparse != null) { | ||
const success = Clipboard.copyCells(sparse); | ||
if (onCopy != null) { | ||
onCopy(success); |
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.
use Utils.safeInvoke
from the core package for this pattern please
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.
What is this for? I can see that it exists; I just don't understand why.
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.
safely invokes a maybe-defined callback. it's precisely this pattern (if function exists then call it) expressed as one type-safe statement.
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.
It's to make the classic "check null, then invoke" code go from 3 lines to one.
packages/table/src/table.tsx
Outdated
@@ -660,6 +711,22 @@ export class Table extends AbstractComponent<ITableProps, ITableState> { | |||
}); | |||
} | |||
|
|||
private maybeRenderCopyHotkey() { | |||
const {getCellData} = this.props; |
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.
spaces in braces
packages/table/src/table.tsx
Outdated
/> | ||
); | ||
} else { | ||
return null; |
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.
ping
Part of #298 . You can specify a getCellData in the props and then this will work. Let me know if you all want to go about this differently.
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.
Want to consider renaming the get data method
packages/table/src/table.tsx
Outdated
* If exists, a callback that returns the data for a specific cell. This need not | ||
* match the value displayed in the `<Cell>` component. The value will be | ||
* invisibly added as `textContent` into the DOM before copying. If not exists, | ||
* copy via hotkeys (mod+c) will not work. |
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.
But we could consider renaming this getCellClipboardData
since without the naming, users might think this is used to fill cells when rendering.
packages/table/src/table.tsx
Outdated
if (sparse != null) { | ||
const success = Clipboard.copyCells(sparse); | ||
if (onCopy != null) { | ||
onCopy(success); |
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.
It's to make the classic "check null, then invoke" code go from 3 lines to one.
packages/table/src/table.tsx
Outdated
return ( | ||
<Hotkey | ||
label="Copy selected table cells" | ||
group="table" |
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.
|
||
(event as any).initKeyboardEvent(eventType, true, true, window, key, 0, ctrlKey, false, false, metaKey); | ||
|
||
// Hack around these readonly properties in WebKit and Chrome |
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.
I'm very sorry =(
table.find(TH_SELECTOR).focus(); | ||
table.find(TH_SELECTOR).keyboard("keydown", "C", true); | ||
expect(copyCellsStub.lastCall.args).to.deep.equal([[["A1"], ["A2"], ["A3"], ["A4"], ["A5"], ["A6"], ["A7"]]]); | ||
expect(onCopy.lastCall.args).to.deep.equal([true]); |
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.
👍
eac003e
to
5bd6c37
Compare
Okay, my requests have been addressed, though build is red |
@@ -9,9 +9,11 @@ const userAgent = typeof navigator !== "undefined" ? navigator.userAgent : ""; | |||
const browser = { | |||
isEdge: !!userAgent.match(/Edge/), | |||
isInternetExplorer: (!!userAgent.match(/Trident/) || !!userAgent.match(/rv:11/)), | |||
isWebkit: Object.prototype.toString.call((window as any).HTMLElement).indexOf("Constructor") > 0, |
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.
probably >= 0
?
also make sure window
is defined first
1aec1cf
to
2f88f83
Compare
@@ -9,9 +9,12 @@ const userAgent = typeof navigator !== "undefined" ? navigator.userAgent : ""; | |||
const browser = { | |||
isEdge: !!userAgent.match(/Edge/), | |||
isInternetExplorer: (!!userAgent.match(/Trident/) || !!userAgent.match(/rv:11/)), | |||
isWebkit: !!userAgent.match(/AppleWebKit/), | |||
// isWebkit: Object.prototype.toString.call((window as any).HTMLElement).indexOf("Constructor") > 0, |
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.
please remove commented code
Part of #298
Checklist
Changes proposed in this pull request:
You can specify a getCellData in the props and then
cmd+c will copy selected cells.
Reviewers should focus on:
What I'm doing here is sane, the shortcut only works when it's supposed to, etc.