Skip to content

Commit ea44ed3

Browse files
committed
Add ability to use custom URL in cloud upload dialog
1 parent 227bba9 commit ea44ed3

4 files changed

Lines changed: 140 additions & 13 deletions

File tree

src/components/modebar/buttons.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,10 @@ modeBarButtons.sendChartToCloud = {
8989
return;
9090
}
9191

92-
confirmCloudDialog(gd, baseUrl, function() {
93-
Plots.sendDataToCloud(gd, baseUrl);
92+
// The dialog pre-fills baseUrl but lets the user override it with a
93+
// custom server URL, which is passed back here as `chosenUrl`.
94+
confirmCloudDialog(gd, baseUrl, function(chosenUrl) {
95+
Plots.sendDataToCloud(gd, chosenUrl);
9496
});
9597
}
9698
};

src/components/modebar/cloud_confirm.js

Lines changed: 79 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,15 @@ var _ = require('../../lib')._;
1111
* so it is centered over the plot rather than the whole viewport. It can be
1212
* dismissed by clicking Cancel, clicking the backdrop, or pressing Escape.
1313
*
14+
* By default the chart is uploaded to the configured plotlyServerURL. A
15+
* "Use a custom server URL" link reveals an editable "Server URL" field
16+
* pre-filled with that default; whatever the user leaves in the field when they
17+
* confirm becomes the destination, so a custom URL entered here overrides the
18+
* plotlyServerURL config for that upload.
19+
*
1420
* @param {DOM node} gd - the graph div, used to scope the dialog to the plot
15-
* @param {string} serverUrl - destination shown in the dialog message
16-
* @param {function} onConfirm - called when the user confirms the upload
21+
* @param {string} serverUrl - default destination, pre-filled into the URL input
22+
* @param {function} onConfirm - called with the (possibly edited) server URL when confirmed
1723
*/
1824
module.exports = function confirmCloudDialog(gd, serverUrl, onConfirm) {
1925
var container = d3.select(gd._fullLayout._paperdiv.node());
@@ -30,11 +36,34 @@ module.exports = function confirmCloudDialog(gd, serverUrl, onConfirm) {
3036

3137
dialog.append('div')
3238
.classed('plotly-cloud-dialog-title', true)
33-
.text(_(gd, 'Share with Plotly Cloud'));
39+
.text(_(gd, 'Share Chart'));
3440

35-
dialog.append('div')
41+
var description = dialog.append('div')
3642
.classed('plotly-cloud-dialog-message', true)
37-
.text(_(gd, 'This chart and its data will be sent to') + ' ' + serverUrl + '.');
43+
.text(_(gd, 'This chart and its data will be sent to') + ' ' + serverUrl + '. ');
44+
45+
// The custom-URL field stays hidden until the user opts in. By default the
46+
// chart is shared to the configured serverUrl; a button in the button row
47+
// reveals this input for anyone who wants to override that destination.
48+
var urlField = dialog.append('div')
49+
.classed('plotly-cloud-dialog-url-field', true)
50+
.style('display', 'none');
51+
52+
urlField.append('label')
53+
.classed('plotly-cloud-dialog-label', true)
54+
.attr('for', 'plotly-cloud-dialog-url')
55+
.text(_(gd, 'Dash Enterprise URL'));
56+
57+
var input = urlField.append('input')
58+
.classed('plotly-cloud-dialog-input', true)
59+
.attr('id', 'plotly-cloud-dialog-url')
60+
.attr('type', 'text')
61+
.attr('spellcheck', false)
62+
.attr('placeholder', 'https://<your-dash-enterprise-instance>/newchart');
63+
64+
var error = dialog.append('div')
65+
.classed('plotly-cloud-dialog-error', true)
66+
.style('display', 'none');
3867

3968
var buttons = dialog.append('div')
4069
.classed('plotly-cloud-dialog-buttons', true);
@@ -54,6 +83,50 @@ module.exports = function confirmCloudDialog(gd, serverUrl, onConfirm) {
5483
if(d3.event.target === overlay.node()) close();
5584
});
5685

86+
function confirm() {
87+
var url = (input.property('value') || serverUrl).trim();
88+
89+
if(!url) {
90+
error.text(_(gd, 'Please enter a server URL.')).style('display', '');
91+
input.node().focus();
92+
return;
93+
}
94+
95+
try {
96+
new URL(url);
97+
} catch(e) {
98+
error.text(_(gd, 'Please enter a valid server URL.')).style('display', '');
99+
input.node().focus();
100+
return;
101+
}
102+
103+
close();
104+
onConfirm(url);
105+
}
106+
107+
// Hide the error and allow Enter to confirm from the input.
108+
input.on('input', function() {
109+
error.style('display', 'none');
110+
});
111+
input.on('keydown', function() {
112+
if(d3.event.key === 'Enter' || d3.event.keyCode === 13) confirm();
113+
});
114+
115+
// Sits at the left of the button row, in line with Cancel/Share. Reveals
116+
// the Server URL field and hides itself once a custom URL is requested.
117+
var customBtn = buttons.append('button')
118+
.classed('plotly-cloud-dialog-btn', true)
119+
.classed('plotly-cloud-dialog-btn--custom', true)
120+
.attr('type', 'button')
121+
.text(_(gd, 'Share with Dash Enterprise'));
122+
123+
customBtn.on('click', function() {
124+
customBtn.style('display', 'none');
125+
urlField.style('display', '');
126+
description.text(_(gd, 'This chart and its data will be sent to the server URL you provide.'));
127+
input.node().focus();
128+
});
129+
57130
buttons.append('button')
58131
.classed('plotly-cloud-dialog-btn', true)
59132
.classed('plotly-cloud-dialog-btn--cancel', true)
@@ -64,8 +137,5 @@ module.exports = function confirmCloudDialog(gd, serverUrl, onConfirm) {
64137
.classed('plotly-cloud-dialog-btn', true)
65138
.classed('plotly-cloud-dialog-btn--confirm', true)
66139
.text(_(gd, 'Share'))
67-
.on('click', function() {
68-
close();
69-
onConfirm();
70-
});
140+
.on('click', confirm);
71141
};

src/css/_cloud_dialog.scss

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,37 @@
4343
word-wrap: break-word;
4444
}
4545

46+
.plotly-cloud-dialog-label {
47+
display: block;
48+
margin-top: 16px;
49+
margin-bottom: 6px;
50+
font-weight: bold;
51+
}
52+
53+
.plotly-cloud-dialog-input {
54+
box-sizing: border-box;
55+
width: 100%;
56+
padding: 7px 10px;
57+
58+
font-family: inherit;
59+
font-size: 13px;
60+
color: #2a3f5f;
61+
62+
background-color: vars.$color-bg-light;
63+
border: 1px solid vars.$color-bg-darker;
64+
border-radius: 3px;
65+
66+
&:focus {
67+
outline: none;
68+
border-color: vars.$color-brand-primary;
69+
}
70+
}
71+
72+
.plotly-cloud-dialog-error {
73+
margin-top: 6px;
74+
color: vars.$color-inline-code;
75+
}
76+
4677
.plotly-cloud-dialog-buttons {
4778
display: flex;
4879
justify-content: flex-end;
@@ -65,6 +96,30 @@
6596
}
6697
}
6798

99+
.plotly-cloud-dialog-btn--custom {
100+
// Push this button to the left so Cancel/Share stay right-aligned,
101+
// while all three share the same row (vertical) baseline.
102+
margin-left: 0;
103+
margin-right: auto;
104+
105+
// Zero the padding and any UA button chrome so the button text
106+
// lines up on its left edge with the message/label text above it.
107+
padding-left: 0;
108+
padding-right: 0;
109+
text-align: left;
110+
appearance: none;
111+
-webkit-appearance: none;
112+
113+
background: none;
114+
border: none;
115+
color: vars.$color-brand-primary;
116+
text-decoration: underline;
117+
118+
&:hover {
119+
color: vars.$color-brand-accent;
120+
}
121+
}
122+
68123
.plotly-cloud-dialog-btn--cancel {
69124
background-color: vars.$color-bg-light;
70125
border-color: vars.$color-bg-darker;

src/plot_api/plot_config.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ var configAttributes = {
3333

3434
plotlyServerURL: {
3535
valType: 'string',
36-
dflt: '',
36+
dflt: 'https://cloud.plotly.com/newchart',
3737
description: [
3838
'Sets the URL for the `sendChartToCloud` modebar button.',
3939
'When clicked, the button will send the chart data to this URL.',
@@ -271,7 +271,7 @@ var configAttributes = {
271271
},
272272
showSendToCloud: {
273273
valType: 'boolean',
274-
dflt: false,
274+
dflt: true,
275275
description: [
276276
'Should we include a modebar button that sends this chart to a URL',
277277
'specified by `plotlyServerURL`, for sharing the chart with others?',

0 commit comments

Comments
 (0)