From 4dbc17572a8a91c839683def8448a14677cf1ff3 Mon Sep 17 00:00:00 2001 From: Ran Byron Date: Tue, 12 Feb 2019 16:11:08 +0200 Subject: [PATCH] Converted Share modal to Ant (#3424) --- client/app/assets/less/ant.less | 9 ++ client/app/components/InputWithCopy.jsx | 57 ++++++++ .../pages/dashboards/ShareDashboardDialog.jsx | 122 ++++++++++++++++++ client/app/pages/dashboards/dashboard.js | 53 +------- .../app/pages/dashboards/share-dashboard.html | 17 --- 5 files changed, 190 insertions(+), 68 deletions(-) create mode 100644 client/app/components/InputWithCopy.jsx create mode 100644 client/app/pages/dashboards/ShareDashboardDialog.jsx delete mode 100644 client/app/pages/dashboards/share-dashboard.html diff --git a/client/app/assets/less/ant.less b/client/app/assets/less/ant.less index 34ae3a4984..d6a387cefb 100644 --- a/client/app/assets/less/ant.less +++ b/client/app/assets/less/ant.less @@ -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 @@ -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; } \ No newline at end of file diff --git a/client/app/components/InputWithCopy.jsx b/client/app/components/InputWithCopy.jsx new file mode 100644 index 0000000000..c2e4571aaa --- /dev/null +++ b/client/app/components/InputWithCopy.jsx @@ -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 = ( + + + + ); + + return ( + + ); + } +} diff --git a/client/app/pages/dashboards/ShareDashboardDialog.jsx b/client/app/pages/dashboards/ShareDashboardDialog.jsx new file mode 100644 index 0000000000..5d8569d58c --- /dev/null +++ b/client/app/pages/dashboards/ShareDashboardDialog.jsx @@ -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 ( + + Share Dashboard +
+ Allow public access to this dashboard with a secret address.{' '} + + { /* eslint-disable-next-line react/jsx-no-target-blank */} + Learn more + +
+
+ ); + } + + 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 ( + +
+ + + + {dashboard.public_url && ( + + + + )} +
+
+ ); + } +} + +export default wrapDialog(ShareDashboardDialog); diff --git a/client/app/pages/dashboards/dashboard.js b/client/app/pages/dashboards/dashboard.js index aa89682812..4ebbf2ebe7 100644 --- a/client/app/pages/dashboards/dashboard.js +++ b/client/app/pages/dashboards/dashboard.js @@ -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'; @@ -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, diff --git a/client/app/pages/dashboards/share-dashboard.html b/client/app/pages/dashboards/share-dashboard.html deleted file mode 100644 index 14400de462..0000000000 --- a/client/app/pages/dashboards/share-dashboard.html +++ /dev/null @@ -1,17 +0,0 @@ - -