mirror of
https://github.com/apache/superset.git
synced 2026-04-10 11:55:24 +00:00
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
This commit is contained in:
committed by
Hugh A. Miles II
parent
fc8acf27c8
commit
eb408d71c4
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user