Skip to content
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

Merged
merged 6 commits into from
Feb 3, 2017

Conversation

gscshoyru
Copy link
Contributor

@gscshoyru gscshoyru commented Jan 25, 2017

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.

@gscshoyru gscshoyru force-pushed the gsc/add-copy-on-shortcuts branch from 003d4e0 to 171ee5a Compare January 25, 2017 18:50
@adidahiya adidahiya changed the title Add option for letting cmd+c copy Support copying table cells with cmd+c hotkey Jan 25, 2017
@adidahiya adidahiya changed the title Support copying table cells with cmd+c hotkey Add support for copying table cells with cmd+c hotkey Jan 25, 2017
Copy link
Contributor

@giladgray giladgray left a 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.

* 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
Copy link
Contributor

Choose a reason for hiding this comment

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

trailing period please

};

return <Hotkeys>
<Hotkey label="copy selected table cells" group="table" combo="mod+c" onKeyDown={doCopy} />
Copy link
Contributor

Choose a reason for hiding this comment

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

"Copy selected..." sentence casing

@@ -349,6 +360,30 @@ export class Table extends AbstractComponent<ITableProps, ITableState> {
);
}

public renderHotkeys() {
const doCopy = (e: KeyboardEvent) => {
Copy link
Contributor

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?

return Utils.toBase26Alpha(col) + (row + 1);
};

const tablePropsWithDefaults = Object.assign({numRows, getCellData}, ...tableProps);
Copy link
Contributor

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 }

Copy link
Contributor

@adidahiya adidahiya Jan 25, 2017

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?

@giladgray
Copy link
Contributor

looks like it's time to increase table test coverage... can't keep lowering the threshold.

@gscshoyru
Copy link
Contributor Author

That's fine I should add tests anyways; just wanted eyes on it first :)

@llorca llorca requested a review from themadcreator January 25, 2017 20:04
@leebyp
Copy link
Contributor

leebyp commented Feb 2, 2017

you can put the browser checks into the compatibility file introduced here #590

@blueprint-bot
Copy link

Check hypothesis about mod key keyboard events

Preview: docs | landing | table
Coverage: core | datetime

/>
);
} else {
return null;
Copy link
Contributor

Choose a reason for hiding this comment

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

return undefined please

Copy link
Contributor

Choose a reason for hiding this comment

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

ping

}

/**
* Use feature detection to determine current browser.
Copy link
Contributor

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.

Copy link
Contributor Author

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...

};

const onCopy = () => {
console.log("did copy");
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oooh better idea. 👍

Copy link
Contributor Author

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.

Copy link
Contributor

@themadcreator themadcreator Feb 3, 2017

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?

* 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.
Copy link
Contributor

@adidahiya adidahiya Feb 3, 2017

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 as textContent 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?

Copy link
Contributor Author

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 :-/

Copy link
Contributor

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.

Copy link
Contributor

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.

@giladgray giladgray added the P0 label Feb 3, 2017
if (sparse != null) {
const success = Clipboard.copyCells(sparse);
if (onCopy != null) {
onCopy(success);
Copy link
Contributor

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

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor

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.

@@ -660,6 +711,22 @@ export class Table extends AbstractComponent<ITableProps, ITableState> {
});
}

private maybeRenderCopyHotkey() {
const {getCellData} = this.props;
Copy link
Contributor

Choose a reason for hiding this comment

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

spaces in braces

/>
);
} else {
return null;
Copy link
Contributor

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.
Copy link
Contributor

@themadcreator themadcreator left a 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

* 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.
Copy link
Contributor

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.

if (sparse != null) {
const success = Clipboard.copyCells(sparse);
if (onCopy != null) {
onCopy(success);
Copy link
Contributor

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.

return (
<Hotkey
label="Copy selected table cells"
group="table"
Copy link
Contributor

Choose a reason for hiding this comment

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

Should group be capitalized? You can see the result my pressing shift + ? when the table is focused:
image


(event as any).initKeyboardEvent(eventType, true, true, window, key, 0, ctrlKey, false, false, metaKey);

// Hack around these readonly properties in WebKit and Chrome
Copy link
Contributor

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]);
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

@gscshoyru gscshoyru force-pushed the gsc/add-copy-on-shortcuts branch from eac003e to 5bd6c37 Compare February 3, 2017 21:20
@themadcreator
Copy link
Contributor

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,
Copy link
Contributor

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

@gscshoyru gscshoyru force-pushed the gsc/add-copy-on-shortcuts branch from 1aec1cf to 2f88f83 Compare February 3, 2017 21:45
@@ -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,
Copy link
Contributor

Choose a reason for hiding this comment

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

please remove commented code

@blueprint-bot
Copy link

Better user agent check

Preview: docs | landing | table
Coverage: core | datetime

@blueprint-bot
Copy link

Remove commented code (whoops)

Preview: docs | landing | table
Coverage: core | datetime

@giladgray giladgray merged commit b019cde into master Feb 3, 2017
@giladgray giladgray deleted the gsc/add-copy-on-shortcuts branch February 3, 2017 22:10
@giladgray giladgray removed the P0 label Feb 3, 2017
@giladgray giladgray mentioned this pull request Feb 3, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants