-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Visualization and dashboard state, URLs, and sharing #7899
Comments
Via @w33ble
Via @tbragin
|
Don't break the backwards/forwards buttons, you will regret it, heavily. I broke it in Kibana 3 and it was a total nightmare. Make it a top priority, don't let the change happen without making sure it works. Implementing state management using the built in browser functionality makes it a lot easier for plugin authors as well as our own developers. |
Undo/redo via history@rashidkpc @w33ble Regarding maintaining the current "undo/redo via history" functionality. I think we can retain the existing functionality by pushing the dashboard/visualization state into the browser history. So whenever a user makes a change that currently updates the search string, we will instead do something like this: history.pushState({
visualizationState: {
// State goes here
}
}); And then in an This will also put us in a good position for someday migrating to an undo/redo feature that is mediated via the app UI, because then we'll just need to push the state into an internal array instead of using the History API. (This is essentially how Redux gets you undo/redo for "free"). I don't think we need localStorage for this solution. Firefox allows you to pass state objects of up to 640kb to pushState, Chrome allows 10mb and IE11 allows 1mb (SO answer). Linking to sessionStorageIf we need to be able to store larger history snapshots (e.g. if we allow embedding images in a dashboard, as @rashidkpc mentions below), we can use sessionStorage, since we want the stored undo/redo history to expire when the tab is closed. We can do this by saving state in sessionStorage, referenced by a unique key per snapshot, and then pass this key to pushState. |
@cjcenizal 640K ought to be enough for anybody ;-) |
@cjcenizal consider wiring it to localStorage if it isn't any more work. In the case that we denormalize dashboards at some point and allow for resource uploading (say, images) it could be possible to bump into the 640k limit fairly quickly. Certainly we could introduce work arounds, but would probably be better if we didn't have to. |
localStorage's 5MB limit (seems to be low end, unless we count old Android at 2MB) is more appealing. Plus, both localStorage and sessionStorage are evented, so updating history via |
Can we use a different term than "Fork" in the UI? I don't think any non-devs know what "Fork" means. |
@Bargs "Clone"? |
Clone, Copy, Duplicate, one of those might work. OSX uses "Duplicate" in finder's right click menu. Google Drive uses "Make a Copy" |
Also, when pushing/popping history entries would you also be changing the URL? I don't feel great about adding history entries without changing the URL. My intuition as a user is that when I click back/forward, I'm navigating between locations that I could share with someone via copy paste. |
@Bargs I agree with you. Maybe we should just include the "undo/redo" UI controls as part of this task. I think that might be the only way of fulfilling our goals:
My only concern is that does increase the amount of time it will take to complete this feature, and it also means we can't make these improvements incrementally. |
If other folks agree that's the right solution, I think it's better to rip off the bandaid now rather than break backwards compatibility in multiple releases. Removing state from the URL will be one backwards compatibility break (because users can no longer copy/paste from url bar), then moving undo/redo from the browser nav buttons to in-app UI will be another break. |
Application state should be sticky@w33ble pointed out to me that if a user is viewing a cloned visualization, navigates to another app, and then comes back to the Visualization app, then they would expect the app to default to the state in which they left it (i.e. displaying the cloned visualization). This just means that we shouldn't clear a loaded cloned visualization until the user has explicitly done so via the Visualization UI (e.g. by creating a new visualization or opening an existing one). |
State "dirty" flagsLet's look ahead for a moment. We have plugins such as Reporting which need to know whether a state is "dirty" or not, i.e. has it been edited but not yet saved. Currently, this information is provided implicitly via a view controller's consumption of the Ideally, Reporting (and other plugins) should explicitly depend on this information, by retrieving it from an Angular service or Redux store. As we reassess the way we store state, we should also think about how to surface "dirty" state in a globally-accessible manner, so we can make these implicit dependencies explicit. |
Test use casesAfter doing some testing with @LeeDr, he suggested that we record some test use cases to ensure our long-term solutions works for all of them:
|
Next steps
|
Is the browser back/forward button still going to function? |
The browser back/forward buttons will be used to help the user support their navigational history (e.g. changing apps, changing sections within an app). Reloading the browser will probably wipe the undo/redo history (this is the behavior in Google Docs), but of course the browser history back/forward buttons will be unaffected. We could theoretically retain the undo/redo history of a document by storing it in localStorage, so that a reload wouldn't wipe it, but I doubt this scenario will occur often enough for it to be useful. |
Pinging @elastic/kibana-app |
This appears to be resolved - linked issues are closed. There is a setting that can help for Vega - |
Meta issue
Child issues are listed below: #7899 (comment)
Background on the problem
This topic is rooted in an IE bug caused by a hard limit on the length of characters IE accepts into its URL bar. We've been able to verify that JavaScript can read from a URL that's over this limit if a user clicks a link (but not if the URL is copy/pasted).
This topic overlaps with additional feedback we've gotten about the difficulty with sharing dashboards/visualizations and updating shared dashboards/visualizations. The problem with sharing these documents is that sharing a URL defaults to sharing a "fork" of the document, instead of sharing a link to the original document. This creates a problem when the document is changed, and other viewers of the document don't see the changes -- e.g. a bookmarked dashboard will be stale.
References
Current system
Visualization state is stored in both the URL and on the server. Users share visualizations by copy/pasting URLs to each other. They access visualizations by bookmarking them. There is some demand for being able to create, edit, and delete visualizations programmatically.
New visualizations
Data stored in URL:
Saved visualizations
Data stored in URL:
Shared visualizations
You can share a visualization with someone by sending them a link with just the visualization name in it.
Clicking the link will load the app, which appends the interval and visualization state – the same as the saved visualization.
Short URLs
Short URLs are hashes that reference visualizations persisted on the server. This is a very similar user-facing interface and persistence mechanism as the "Save visualization" feature.
Browser history
Making changes to the visualization pushes a new state to the browser history. Clicking the back and forward buttons allow you to navigation the different states the visualization has gone through as you've edited it. This is essentially an "undo/redo" feature.
Proposed system
The current system uses the URL as the source of truth for the data visualization, and stores the visualization state in memory as a secondary step.
This creates several problems: long URLs become unwieldy to share, super-long URLs cause IE bugs, and URLs that are copy/pasted default to "forking" (where each user has his/her own visualization state), instead of the generally-preferred "sharing" (where all users have the same visualization state).
We can solve these problems by inverting the roles of memory and URL. The single source of truth for the data visualization will live within memory, and the URL will be populated with the visualization state as a secondary, optional step (when forking).
In general, the URL search string will only be used for storing data for functionality such as refresh interval, time period, search, filtering, pagination, etc (ways of adjusting perspective).
As an additional and supplemental improvement, we can also use the visualization ID to reference it in the URL, instead of using its name. This will enable name-editing functionality in the future.
Users can create, edit, and delete visualizations programmatically via ES API calls. They can also programmatically create "forked visualizations" (see below).
Note: the goals of this proposed system can be largely met by the Intermediate system, below.
Data stored in URL:
New visualizations
New visualizations will not have an ID associated with them until they're saved. For this reason, we'll need to surface information to the user that the visualization needs to be saved before it can be shared (e.g. a notification, banner, or warning message). This information could be surfaced when the user clicks the play button to re-render the visualization.
Note: the visualization can still be forked without being saved.
Data stored in URL:
Saved visualizations
Data stored in URL:
Shared visualizations
This has the same URL format as a saved visualization. Visualizations can be shared by copy/pasting this URL to users.
As a bonus, we can remove the URL shortener, since the URL will already be short. We should leave the server-side routing functionality in place, to preserve backwards-compatibility. The short URL will direct to the old URL format, which will be handled gracefully by the client (see "Migrations", below).
In the future, we can add a polling function to detect when the visualization has changed. This change can either be automatically applied to the visualization (useful for public dashboards) if there are no local unsaved changes, or surface a "conflict" message to the user if there are local unsaved changes.
Forked visualizations
If you want to share the state of your visualization without sharing the original, you can fork it. This essentially duplicates our current URL-sharing functionality (adding index pattern and visualization state to the URL).
The user can click a "Fork" button, to generate a copy/pasteable link in a text field:
When a user clicks this link, the app will load up, parse the URL, store visualization state in memory, and replace the URL with a format that follows the "new visualization" pattern:
At this point the user can share the visualization by saving or forking it.
This supports the use-case of one of our customers:
Note: This feature will work for IE users as long as they click a forked URL link. If they copy/paste a URL into the browser and the URL exceeds IE's length limit, then the visualization data will be un-parseable. In this scenario, we'll need to surface an error message to the user informing them of the problem and how to solve it (by clicking a link instead of copy/pasting).
Browser history
Other document-centric web apps use the browser history for storing route changes (i.e. navigation changes), and surface undo/redo functionality separately (e.g. with buttons or keyboard shortcuts). We should leverage user expectations by conforming to these patterns. We can add this functionality in the future.
Migration
It's important that the new system still handle the current system's URL format gracefully. There are two migration scenarios.
Saved visualizations
A saved visualization's URL contains either a) the name and visualization state or b) just the name. In the event a user opens a saved visualization, here's what happens:
This way, the old URL will still load the most recently saved state of the visualization.
Non-saved visualizations
A user can currently create a visualization and bookmark it without saving it. If a user opens this bookmark, here's what happens:
Intermediate system
We can work our way towards the Proposed system in steps. As an intermediate step, we can build the Proposed system, but keep using visualization names instead of switching to IDs. We can also preserve existing "undo/redo" functionality using history.pushState.
The text was updated successfully, but these errors were encountered: