superset-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From h...@apache.org
Subject [incubator-superset] branch master updated: Add copy to clipboard buttons in explore and sqllab (#6461)
Date Fri, 07 Dec 2018 18:03:40 GMT
This is an automated email from the ASF dual-hosted git repository.

hugh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git


The following commit(s) were added to refs/heads/master by this push:
     new eb408d7  Add copy to clipboard buttons in explore and sqllab (#6461)
eb408d7 is described below

commit eb408d71c4e52d6b6d9fe93ff1cced91ef45f6e5
Author: leakingoxide <45320817+leakingoxide@users.noreply.github.com>
AuthorDate: Fri Dec 7 19:03:33 2018 +0100

    Add copy to clipboard buttons in explore and sqllab (#6461)
    
    * Add copy to clipboard buttons in explore and sqllab
    
    * Eslint fixes
    
    * Review changes: deconstruct props, extract function to utils, add tests
---
 .../assets/spec/javascripts/utils/common_spec.jsx  | 15 ++++++++++++-
 .../assets/src/SqlLab/components/ResultSet.jsx     | 12 +++++++++++
 superset/assets/src/components/CopyToClipboard.jsx | 25 +++++++++++++++++++++-
 .../src/explore/components/DisplayQueryButton.jsx  | 10 +++++++++
 superset/assets/src/utils/common.js                |  8 +++++++
 5 files changed, 68 insertions(+), 2 deletions(-)

diff --git a/superset/assets/spec/javascripts/utils/common_spec.jsx b/superset/assets/spec/javascripts/utils/common_spec.jsx
index b6fc39f..eabb69e 100644
--- a/superset/assets/spec/javascripts/utils/common_spec.jsx
+++ b/superset/assets/spec/javascripts/utils/common_spec.jsx
@@ -1,4 +1,4 @@
-import { isTruthy, optionFromValue } from '../../../src/utils/common';
+import { isTruthy, optionFromValue, prepareCopyToClipboardTabularData } from '../../../src/utils/common';
 
 describe('utils/common', () => {
   describe('isTruthy', () => {
@@ -48,4 +48,17 @@ describe('utils/common', () => {
       expect(optionFromValue(5)).toEqual({ value: 5, label: '5' });
     });
   });
+  describe('prepareCopyToClipboardTabularData', () => {
+    it('converts empty array', () => {
+      const array = [];
+      expect(prepareCopyToClipboardTabularData(array)).toEqual('');
+    });
+    it('converts non empty array', () => {
+      const array = [
+          { column1: 'lorem', column2: 'ipsum' },
+          { column1: 'dolor', column2: 'sit', column3: 'amet' },
+      ];
+      expect(prepareCopyToClipboardTabularData(array)).toEqual('lorem\tipsum\ndolor\tsit\tamet\n');
+    });
+  });
 });
diff --git a/superset/assets/src/SqlLab/components/ResultSet.jsx b/superset/assets/src/SqlLab/components/ResultSet.jsx
index c7c10cd..a9416d4 100644
--- a/superset/assets/src/SqlLab/components/ResultSet.jsx
+++ b/superset/assets/src/SqlLab/components/ResultSet.jsx
@@ -9,6 +9,8 @@ import ExploreResultsButton from './ExploreResultsButton';
 import HighlightedSql from './HighlightedSql';
 import FilterableTable from '../../components/FilterableTable/FilterableTable';
 import QueryStateLabel from './QueryStateLabel';
+import CopyToClipboard from '../../components/CopyToClipboard';
+import { prepareCopyToClipboardTabularData } from '../../utils/common';
 
 const propTypes = {
   actions: PropTypes.object,
@@ -112,6 +114,16 @@ export default class ResultSet extends React.PureComponent {
                   <Button bsSize="small" href={'/superset/csv/' + this.props.query.id}>
                     <i className="fa fa-file-text-o" /> {t('.CSV')}
                   </Button>}
+
+                <CopyToClipboard
+                  text={prepareCopyToClipboardTabularData(this.props.query.results.data)}
+                  wrapped={false}
+                  copyNode={
+                    <Button bsSize="small">
+                      <i className="fa fa-clipboard" /> {t('Clipboard')}
+                    </Button>
+                  }
+                />
               </ButtonGroup>
             </div>
             <div className="pull-right">
diff --git a/superset/assets/src/components/CopyToClipboard.jsx b/superset/assets/src/components/CopyToClipboard.jsx
index 199e8d4..433ebcf 100644
--- a/superset/assets/src/components/CopyToClipboard.jsx
+++ b/superset/assets/src/components/CopyToClipboard.jsx
@@ -10,6 +10,7 @@ const propTypes = {
   shouldShowText: PropTypes.bool,
   text: PropTypes.string,
   inMenu: PropTypes.bool,
+  wrapped: PropTypes.bool,
   tooltipText: PropTypes.string,
 };
 
@@ -18,6 +19,7 @@ const defaultProps = {
   onCopyEnd: () => {},
   shouldShowText: true,
   inMenu: false,
+  wrapped: true,
   tooltipText: t('Copy to clipboard'),
 };
 
@@ -94,6 +96,23 @@ export default class CopyToClipboard extends React.Component {
     return this.props.tooltipText;
   }
 
+  renderNotWrapped() {
+    const { copyNode } = this.props;
+    return (
+      <OverlayTrigger
+        placement="top"
+        style={{ cursor: 'pointer' }}
+        overlay={this.renderTooltip()}
+        trigger={['hover']}
+        bsStyle="link"
+        onClick={this.onClick}
+        onMouseOut={this.onMouseOut}
+      >
+        {copyNode}
+      </OverlayTrigger>
+    );
+  }
+
   renderLink() {
     return (
       <span>
@@ -139,7 +158,11 @@ export default class CopyToClipboard extends React.Component {
   }
 
   render() {
-    return this.props.inMenu ? this.renderInMenu() : this.renderLink();
+    const { wrapped, inMenu } = this.props;
+    if (!wrapped) {
+      return this.renderNotWrapped();
+    }
+    return inMenu ? this.renderInMenu() : this.renderLink();
   }
 }
 
diff --git a/superset/assets/src/explore/components/DisplayQueryButton.jsx b/superset/assets/src/explore/components/DisplayQueryButton.jsx
index 042ca24..1497e50 100644
--- a/superset/assets/src/explore/components/DisplayQueryButton.jsx
+++ b/superset/assets/src/explore/components/DisplayQueryButton.jsx
@@ -19,6 +19,7 @@ import Loading from '../../components/Loading';
 import ModalTrigger from './../../components/ModalTrigger';
 import Button from '../../components/Button';
 import RowCountLabel from './RowCountLabel';
+import { prepareCopyToClipboardTabularData } from '../../utils/common';
 
 registerLanguage('markdown', markdownSyntax);
 registerLanguage('html', htmlSyntax);
@@ -130,6 +131,15 @@ export default class DisplayQueryButton extends React.PureComponent {
         <Row>
           <Col md={9}>
             <RowCountLabel rowcount={data.length} suffix={t('rows retrieved')} />
+            <CopyToClipboard
+              text={prepareCopyToClipboardTabularData(data)}
+              wrapped={false}
+              copyNode={
+                <Button style={{ padding: '2px 10px', fontSize: '11px' }}>
+                  <i className="fa fa-clipboard" />
+                </Button>
+              }
+            />
           </Col>
           <Col md={3}>
             <FormControl
diff --git a/superset/assets/src/utils/common.js b/superset/assets/src/utils/common.js
index 93870c2..89b72e8 100644
--- a/superset/assets/src/utils/common.js
+++ b/superset/assets/src/utils/common.js
@@ -100,3 +100,11 @@ export function optionFromValue(opt) {
   // From a list of options, handles special values & labels
   return { value: optionValue(opt), label: optionLabel(opt) };
 }
+
+export function prepareCopyToClipboardTabularData(data) {
+  let result = '';
+  for (let i = 0; i < data.length; ++i) {
+    result += Object.values(data[i]).join('\t') + '\n';
+  }
+  return result;
+}


Mime
View raw message