Skip to content

Commit

Permalink
Converted Share modal to Ant (#3424)
Browse files Browse the repository at this point in the history
  • Loading branch information
ranbena authored Feb 12, 2019
1 parent 330c5a8 commit 4dbc175
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 68 deletions.
9 changes: 9 additions & 0 deletions client/app/assets/less/ant.less
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
@import '~antd/lib/icon/style/index';
@import '~antd/lib/tag/style/index';
@import '~antd/lib/grid/style/index';
@import '~antd/lib/switch/style/index';
@import 'inc/ant-variables';

// Remove bold in labels for Ant checkboxes and radio buttons
Expand Down Expand Up @@ -195,4 +196,12 @@
line-height: 46px;
}
}
}

// description in modal header
.modal-header-desc {
font-size: @font-size-base;
color: @text-color-secondary;
font-weight: normal;
margin-top: 4px;
}
57 changes: 57 additions & 0 deletions client/app/components/InputWithCopy.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React from 'react';
import Input from 'antd/lib/input';
import Icon from 'antd/lib/icon';
import Tooltip from 'antd/lib/tooltip';

export default class InputWithCopy extends React.Component {
constructor(props) {
super(props);
this.state = { copied: null };
this.ref = React.createRef();
this.copyFeatureSupported = document.queryCommandSupported('copy');
this.resetCopyState = null;
}

componentWillUnmount() {
if (this.resetCopyState) {
clearTimeout(this.resetCopyState);
}
}

copy = () => {
// select text
this.ref.current.select();

// copy
try {
const success = document.execCommand('copy');
if (!success) {
throw new Error();
}
this.setState({ copied: 'Copied!' });
} catch (err) {
this.setState({
copied: 'Copy failed',
});
}

// reset tooltip
this.resetCopyState = setTimeout(() => this.setState({ copied: null }), 2000);
};

render() {
const copyButton = (
<Tooltip title={this.state.copied || 'Copy'}>
<Icon
type="copy"
style={{ cursor: 'pointer' }}
onClick={this.copy}
/>
</Tooltip>
);

return (
<Input {...this.props} ref={this.ref} addonAfter={this.copyFeatureSupported && copyButton} />
);
}
}
122 changes: 122 additions & 0 deletions client/app/pages/dashboards/ShareDashboardDialog.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { replace } from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import Switch from 'antd/lib/switch';
import Modal from 'antd/lib/modal';
import Form from 'antd/lib/form';
import Tooltip from 'antd/lib/tooltip';
import { $http, toastr } from '@/services/ng';
import { wrap as wrapDialog, DialogPropType } from '@/components/DialogWrapper';
import InputWithCopy from '@/components/InputWithCopy';

const API_SHARE_URL = 'api/dashboards/{id}/share';
const HELP_URL = 'https://redash.io/help/user-guide/dashboards/sharing-dashboards?source=dialog';

class ShareDashboardDialog extends React.Component {
static propTypes = {
dashboard: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
dialog: DialogPropType.isRequired,
};

formItemProps = {
labelCol: { span: 8 },
wrapperCol: { span: 16 },
style: { marginBottom: 7 },
}

constructor(props) {
super(props);
this.state = {
saving: false,
};
this.apiUrl = replace(API_SHARE_URL, '{id}', props.dashboard.id);
}

static get headerContent() {
return (
<React.Fragment>
Share Dashboard
<div className="modal-header-desc">
Allow public access to this dashboard with a secret address.{' '}
<Tooltip title="Guide: Sharing and Embedding Dashboards">
{ /* eslint-disable-next-line react/jsx-no-target-blank */}
<a href={HELP_URL} target="_blank" rel="noopener">Learn more</a>
</Tooltip>
</div>
</React.Fragment>
);
}

enable = () => {
const { dashboard } = this.props;
this.setState({ saving: true });

$http
.post(this.apiUrl)
.success((data) => {
dashboard.publicAccessEnabled = true;
dashboard.public_url = data.public_url;
})
.error(() => {
toastr.error('Failed to turn on sharing for this dashboard');
})
.finally(() => {
this.setState({ saving: false });
});
}

disable = () => {
const { dashboard } = this.props;
this.setState({ saving: true });

$http
.delete(this.apiUrl)
.success(() => {
dashboard.publicAccessEnabled = false;
delete dashboard.public_url;
})
.error(() => {
toastr.error('Failed to turn off sharing for this dashboard');
})
.finally(() => {
this.setState({ saving: false });
});
}

onChange = (checked) => {
if (checked) {
this.enable();
} else {
this.disable();
}
};

render() {
const { dialog, dashboard } = this.props;

return (
<Modal
{...dialog.props}
title={this.constructor.headerContent}
footer={null}
>
<Form layout="horizontal">
<Form.Item label="Allow public access" {...this.formItemProps}>
<Switch
checked={dashboard.publicAccessEnabled}
onChange={this.onChange}
loading={this.state.saving}
/>
</Form.Item>
{dashboard.public_url && (
<Form.Item label="Secret address" {...this.formItemProps}>
<InputWithCopy value={dashboard.public_url} />
</Form.Item>
)}
</Form>
</Modal>
);
}
}

export default wrapDialog(ShareDashboardDialog);
53 changes: 2 additions & 51 deletions client/app/pages/dashboards/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import getTags from '@/services/getTags';
import { policy } from '@/services/policy';
import { durationHumanize } from '@/filters';
import template from './dashboard.html';
import shareDashboardTemplate from './share-dashboard.html';
import ShareDashboardDialog from './ShareDashboardDialog';
import AddWidgetDialog from '@/components/dashboards/AddWidgetDialog';
import './dashboard.less';

Expand Down Expand Up @@ -392,60 +392,11 @@ function DashboardCtrl(
}

this.openShareForm = () => {
$uibModal.open({
component: 'shareDashboard',
resolve: {
dashboard: this.dashboard,
},
});
ShareDashboardDialog.showModal({ dashboard: this.dashboard });
};
}

const ShareDashboardComponent = {
template: shareDashboardTemplate,
bindings: {
resolve: '<',
close: '&',
dismiss: '&',
},
controller($http) {
'ngInject';

this.dashboard = this.resolve.dashboard;

this.toggleSharing = () => {
const url = `api/dashboards/${this.dashboard.id}/share`;

if (!this.dashboard.publicAccessEnabled) {
// disable
$http
.delete(url)
.success(() => {
this.dashboard.publicAccessEnabled = false;
delete this.dashboard.public_url;
})
.error(() => {
this.dashboard.publicAccessEnabled = true;
// TODO: show message
});
} else {
$http
.post(url)
.success((data) => {
this.dashboard.publicAccessEnabled = true;
this.dashboard.public_url = data.public_url;
})
.error(() => {
this.dashboard.publicAccessEnabled = false;
// TODO: show message
});
}
};
},
};

export default function init(ngModule) {
ngModule.component('shareDashboard', ShareDashboardComponent);
ngModule.component('dashboardPage', {
template,
controller: DashboardCtrl,
Expand Down
17 changes: 0 additions & 17 deletions client/app/pages/dashboards/share-dashboard.html

This file was deleted.

0 comments on commit 4dbc175

Please sign in to comment.