Skip to content
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions build/plotcss.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ var rules = {
"X .plotly-cloud-dialog .plotly-cloud-dialog-box": "box-sizing:border-box;min-width:300px;max-width:420px;padding:20px 24px;background-color:#fff;border:1px solid #e0e2e5;border-radius:4px;box-shadow:0 4px 16px rgba(0,0,0,.25);font-size:13px;color:#2a3f5f;",
"X .plotly-cloud-dialog .plotly-cloud-dialog-title": "font-size:16px;font-weight:bold;margin-bottom:12px;",
"X .plotly-cloud-dialog .plotly-cloud-dialog-message": "line-height:1.5;overflow-wrap:break-word;word-wrap:break-word;",
"X .plotly-cloud-dialog .plotly-cloud-dialog-label": "display:block;margin-top:16px;margin-bottom:6px;font-weight:bold;",
"X .plotly-cloud-dialog .plotly-cloud-dialog-input": "box-sizing:border-box;width:100%;padding:7px 10px;font-family:inherit;font-size:13px;color:#2a3f5f;background-color:#fff;border:1px solid #e0e2e5;border-radius:3px;",
"X .plotly-cloud-dialog .plotly-cloud-dialog-input:focus": "outline:none;border-color:#447adb;",
"X .plotly-cloud-dialog .plotly-cloud-dialog-error": "margin-top:6px;color:#db44a5;",
"X .plotly-cloud-dialog .plotly-cloud-dialog-buttons": "display:flex;justify-content:flex-end;margin-top:20px;",
"X .plotly-cloud-dialog .plotly-cloud-dialog-btn": "font-family:inherit;font-size:13px;padding:7px 16px;margin-left:8px;border-radius:3px;border:1px solid rgba(0,0,0,0);cursor:pointer;",
"X .plotly-cloud-dialog .plotly-cloud-dialog-btn:focus-visible": "outline:2px solid #447adb;outline-offset:1px;",
Expand Down
1 change: 1 addition & 0 deletions draftlogs/7896_add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Add ability to use custom URL for chart upload from the dialog UI [[#7896](https://github.com/plotly/plotly.js/pull/7896)]
6 changes: 4 additions & 2 deletions src/components/modebar/buttons.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,10 @@ modeBarButtons.sendChartToCloud = {
return;
}

confirmCloudDialog(gd, baseUrl, function() {
Plots.sendDataToCloud(gd, baseUrl);
// The dialog pre-fills baseUrl but lets the user override it with a
// custom server URL, which is passed back here as `chosenUrl`.
confirmCloudDialog(gd, baseUrl, function(chosenUrl) {
Plots.sendDataToCloud(gd, chosenUrl);
});
}
};
Expand Down
96 changes: 86 additions & 10 deletions src/components/modebar/cloud_confirm.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,15 @@ var _ = require('../../lib')._;
* so it is centered over the plot rather than the whole viewport. It can be
* dismissed by clicking Cancel, clicking the backdrop, or pressing Escape.
*
* By default the chart is uploaded to the configured plotlyServerURL. A
* "Use a custom server URL" link reveals an editable "Server URL" field
* pre-filled with that default; whatever the user leaves in the field when they
* confirm becomes the destination, so a custom URL entered here overrides the
* plotlyServerURL config for that upload.
*
* @param {DOM node} gd - the graph div, used to scope the dialog to the plot
* @param {string} serverUrl - destination shown in the dialog message
* @param {function} onConfirm - called when the user confirms the upload
* @param {string} serverUrl - default destination
* @param {function} onConfirm - called with the (possibly edited) server URL when confirmed
*/
module.exports = function confirmCloudDialog(gd, serverUrl, onConfirm) {
var container = d3.select(gd._fullLayout._paperdiv.node());
Expand All @@ -30,11 +36,38 @@ module.exports = function confirmCloudDialog(gd, serverUrl, onConfirm) {

dialog.append('div')
.classed('plotly-cloud-dialog-title', true)
.text(_(gd, 'Share with Plotly Cloud'));
.text(_(gd, 'Share Chart'));

dialog.append('div')
.classed('plotly-cloud-dialog-message', true)
.text(_(gd, 'This chart and its data will be sent to') + ' ' + serverUrl + '.');
var serverUrlText = new URL(serverUrl).hostname;

var description = dialog.append('div');
description.classed('plotly-cloud-dialog-message', true);
description.append('span').text(_(gd, 'This chart and its data will be sent to '));
description.append('span').text(serverUrlText).classed('plotly-cloud-dialog-message--hostname', true);
description.append('span').text('. ');

// The custom-URL field stays hidden until the user opts in. By default the
// chart is shared to the configured serverUrl; a button in the button row
// reveals this input for anyone who wants to override that destination.
var urlField = dialog.append('div')
.classed('plotly-cloud-dialog-url-field', true)
.style('display', 'none');

urlField.append('label')
.classed('plotly-cloud-dialog-label', true)
.attr('for', 'plotly-cloud-dialog-url')
.text(_(gd, 'Dash Enterprise URL'));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chart Server URL

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@emilykl Are you good with this wording?


var input = urlField.append('input')
.classed('plotly-cloud-dialog-input', true)
.attr('id', 'plotly-cloud-dialog-url')
.attr('type', 'text')
.attr('spellcheck', false)
.attr('placeholder', 'https://<your-dash-enterprise-instance>/');

var error = dialog.append('div')
.classed('plotly-cloud-dialog-error', true)
.style('display', 'none');

var buttons = dialog.append('div')
.classed('plotly-cloud-dialog-buttons', true);
Expand All @@ -54,6 +87,52 @@ module.exports = function confirmCloudDialog(gd, serverUrl, onConfirm) {
if(d3.event.target === overlay.node()) close();
});

function confirm() {
var url = (input.property('value') || serverUrl).trim();

if(!url) {
error.text(_(gd, 'Please enter a server URL.')).style('display', '');
input.node().focus();
return;
}

if (!url.startsWith('http://') && !url.startsWith('https://')) {
url = 'https://' + url;
}

try {
new URL(url);
} catch(e) {
error.text(_(gd, 'Please enter a valid server URL.')).style('display', '');
input.node().focus();
return;
}

close();
onConfirm(url);
}

// Hide the error and allow Enter to confirm from the input.
input.on('input', function() {
error.style('display', 'none');
});
input.on('keydown', function() {
if(d3.event.key === 'Enter' || d3.event.keyCode === 13) confirm();
});

var customBtn = buttons.append('button')
.classed('plotly-cloud-dialog-btn', true)
.classed('plotly-cloud-dialog-btn--custom', true)
.attr('type', 'button')
.text(_(gd, 'Share to Dash Enterprise'));

customBtn.on('click', function() {
customBtn.style('display', 'none');
urlField.style('display', '');
description.text(_(gd, 'If your Dash Enterprise instance supports chart sharing, you can enter its URL below. This chart and its data will be sent to the URL you provide.'));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Enter the url of your Dash Enterprise chart server below or contact Plotly support to get set up. This chart and its data will be sent to the URL you provide."

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@emilykl Are you good with this wording?

input.node().focus();
});

buttons.append('button')
.classed('plotly-cloud-dialog-btn', true)
.classed('plotly-cloud-dialog-btn--cancel', true)
Expand All @@ -64,8 +143,5 @@ module.exports = function confirmCloudDialog(gd, serverUrl, onConfirm) {
.classed('plotly-cloud-dialog-btn', true)
.classed('plotly-cloud-dialog-btn--confirm', true)
.text(_(gd, 'Share'))
.on('click', function() {
close();
onConfirm();
});
.on('click', confirm);
};
75 changes: 62 additions & 13 deletions src/css/_cloud_dialog.scss
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,41 @@
line-height: 1.5;
overflow-wrap: break-word;
word-wrap: break-word;

&--hostname {
font-weight: bold;
}
}

.plotly-cloud-dialog-label {
display: block;
margin-top: 16px;
margin-bottom: 6px;
font-weight: bold;
}

.plotly-cloud-dialog-input {
box-sizing: border-box;
width: 100%;
padding: 7px 10px;

font-family: inherit;
font-size: 13px;
color: #2a3f5f;

background-color: vars.$color-bg-light;
border: 1px solid vars.$color-bg-darker;
border-radius: 3px;

&:focus {
outline: none;
border-color: vars.$color-brand-primary;
}
}

.plotly-cloud-dialog-error {
margin-top: 6px;
color: vars.$color-inline-code;
}

.plotly-cloud-dialog-buttons {
Expand All @@ -63,25 +98,39 @@
outline: 2px solid vars.$color-brand-primary;
outline-offset: 1px;
}
}

.plotly-cloud-dialog-btn--cancel {
background-color: vars.$color-bg-light;
border-color: vars.$color-bg-darker;
color: vars.$color-muted-text;

&:hover {
&.plotly-cloud-dialog-btn--cancel {
background-color: vars.$color-bg-base;
margin-left: auto;
color: vars.$color-muted-text;

&:hover {
background-color: vars.$color-bg-darker;
}
}
}

.plotly-cloud-dialog-btn--confirm {
background-color: vars.$color-brand-primary;
color: vars.$color-bg-light;
&.plotly-cloud-dialog-btn--confirm {
background-color: vars.$color-brand-primary;
color: vars.$color-bg-light;

&:hover {
background-color: vars.$color-brand-accent;
&:hover {
background-color: vars.$color-brand-accent;
}
}

&.plotly-cloud-dialog-btn--custom {
background-color: vars.$color-bg-light;
color: vars.$color-muted-text;
font-size: 11px;
padding-left: 0;
margin-left: 0;

&:hover {
text-decoration: underline;
}
}

}

}
}
2 changes: 1 addition & 1 deletion src/fonts/ploticon.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ module.exports = {
cloudupload: {
width: 640,
height: 640,
path: 'M176 544C96.5 544 32 479.5 32 400C32 336.6 73 282.8 129.9 263.5C128.6 255.8 128 248 128 240C128 160.5 192.5 96 272 96C327.4 96 375.5 127.3 399.6 173.1C413.8 164.8 430.4 160 448 160C501 160 544 203 544 256C544 271.7 540.2 286.6 533.5 299.7C577.5 320 608 364.4 608 416C608 486.7 550.7 544 480 544L176 544zM337 255C327.6 245.6 312.4 245.6 303.1 255L231.1 327C221.7 336.4 221.7 351.6 231.1 360.9C240.5 370.2 255.7 370.3 265 360.9L296 329.9L296 432C296 445.3 306.7 456 320 456C333.3 456 344 445.3 344 432L344 329.9L375 360.9C384.4 370.3 399.6 370.3 408.9 360.9C418.2 351.5 418.3 336.3 408.9 327L336.9 255z',
path: 'M451.5 160C434.9 160 418.8 164.5 404.7 172.7C388.9 156.7 370.5 143.3 350.2 133.2C378.4 109.2 414.3 96 451.5 96C537.9 96 608 166 608 252.5C608 294 591.5 333.8 562.2 363.1L491.1 434.2C461.8 463.5 422 480 380.5 480C294.1 480 224 410 224 323.5C224 322 224 320.5 224.1 319C224.6 301.3 239.3 287.4 257 287.9C274.7 288.4 288.6 303.1 288.1 320.8C288.1 321.7 288.1 322.6 288.1 323.4C288.1 374.5 329.5 415.9 380.6 415.9C405.1 415.9 428.6 406.2 446 388.8L517.1 317.7C534.4 300.4 544.2 276.8 544.2 252.3C544.2 201.2 502.8 159.8 451.7 159.8zM307.2 237.3C305.3 236.5 303.4 235.4 301.7 234.2C289.1 227.7 274.7 224 259.6 224C235.1 224 211.6 233.7 194.2 251.1L123.1 322.2C105.8 339.5 96 363.1 96 387.6C96 438.7 137.4 480.1 188.5 480.1C205 480.1 221.1 475.7 235.2 467.5C251 483.5 269.4 496.9 289.8 507C261.6 530.9 225.8 544.2 188.5 544.2C102.1 544.2 32 474.2 32 387.7C32 346.2 48.5 306.4 77.8 277.1L148.9 206C178.2 176.7 218 160.2 259.5 160.2C346.1 160.2 416 230.8 416 317.1C416 318.4 416 319.7 416 321C415.6 338.7 400.9 352.6 383.2 352.2C365.5 351.8 351.6 337.1 352 319.4C352 318.6 352 317.9 352 317.1C352 283.4 334 253.8 307.2 237.5z',

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@emilykl I updated the icon here! I used the one you referenced

@marthacryan marthacryan Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(See the updated icon in the third photo in the description)

transform: 'matrix(1 0 0 1 -15 -15)'
},
drawopenpath: {
Expand Down