-
Notifications
You must be signed in to change notification settings - Fork 14k
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
Tagging frontend #7418
Tagging frontend #7418
Conversation
Codecov Report
@@ Coverage Diff @@
## master #7418 +/- ##
==========================================
- Coverage 65.08% 64.92% -0.16%
==========================================
Files 429 434 +5
Lines 21002 21261 +259
Branches 2338 2365 +27
==========================================
+ Hits 13669 13804 +135
- Misses 7217 7334 +117
- Partials 116 123 +7
Continue to review full report at Codecov.
|
Any plans on getting this merged? |
@1AB9502 sorry for the delay with this. I need to add some integration tests. Let me put this up for review in the meantime. |
cc: @khtruong |
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
/** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add a quick comment of what this div element is.
No worries! We are just really looking forward to getting this feature! |
@@ -0,0 +1,213 @@ | |||
/** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it necessary to be a less
file? From quick skimming, a *.css
extension should be enough.
QUERY: 'query', | ||
}); | ||
|
||
export function fetchTags({ objectType, objectId, includeTypes = false }, callback, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer returning Promise
over callback
. SupersetClient.get()
already returns Promise
which can be chained.
renderTable(type) { | ||
const data = this.state.objects[type].map(o => ({ | ||
[type]: <a href={o.url}>{o.name}</a>, | ||
creator: unsafe(o.creator), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does unsafe
do?
{ key: 'creator', label: 'Creator' }, | ||
{ key: 'modified', label: 'Modified' }, | ||
]} | ||
defaultSort={{ column: 'modified', direction: 'desc' }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
create const
outside class to avoid creating new object every time.
constructor(props) { | ||
super(props); | ||
|
||
this.fetchTags = fetchTags.bind(this, { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same parameter with addTag()
|
||
this.fetchTags = fetchTags.bind(this, { | ||
objectType: OBJECT_TYPES.CHART, | ||
objectId: props.chart.id, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if the props
and chart
change? This id
will not be updated.
return ( | ||
<div className="react-tags-rw"> | ||
{this.state.tags.map(tag => ( | ||
<Label bsStyle="info"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing key
} | ||
|
||
renderEditableTags() { | ||
const Tag = props => ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This component should be declared somewhere else instead of redeclaring everytime in this render function. Can also be its own file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might be missing context from when this was originally proposed, but I had some higher level questions here:
Is there a reason why you choose to create individual API functions instead of a set of redux actions to fetch and update tags? It makes sense to pull the tagging actions into its own file because it would be shared between both Explore and Dashboard views, but I think that maintaining the state of tags in its own redux store would be more typical and follow the patterns i've seen throughout superset already.
I would recommend creating a new directory in assets/src/tags
to keep the component files, the main tags
page, the redux actions and the reducers and then wire up the required state to both explore and dashboard components. It would make tagging consistent with the rest of the app, clearer, and easier to maintain
@@ -0,0 +1,16 @@ | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this file be here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My mistake, let me remove it.
const defaultTab = isFeatureEnabled(FeatureFlag.TAGGING_SYSTEM) | ||
? 'tags' | ||
: 'favorites'; | ||
const parsedUrl = new URI(window.location); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems odd that we're adding the tags page as a subpage of welcome. Why not create a new page at /superset/tags
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, if all of this is only required for the TAGGING_SYSTEM feature flag, then can't we wrap all the logic in that flag? Right now even if you turn off the feature flag, all this code is executed, which seems to remove the safety that feature flags provide
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I added the feature flag later (since we've been using this for a while), will improve the wrapped logic.
updateURL(key, value) { | ||
const parsedUrl = new URI(window.location); | ||
parsedUrl.search(data => ({ ...data, [key]: value })); | ||
window.history.pushState({ value }, value, parsedUrl.href()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
manually adjusting the window's history is really error prone... i'm worried that there are a bunch of edge cases here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. If we opt to do this (I saw your comment below) I can write comprehensive unit tests.
} | ||
handleSelect(key) { | ||
// store selected tab in URL | ||
window.history.pushState({ tab: key }, key, `#${key}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using the URL to maintain application state seems like an antipattern. Why not use the redux store?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is so we can deep link to a particular tag search when the user clicks a tag (though this could be done encoding the info on the path), and also so that the user can share search results for a tag.
Great question. Yeah, we've been running this code at Lyft for maybe a year now, so it's definitely outdated and would be written differently from scratch today. IIRC we went with API functions since the state lives outside the applications (explore, dashboards, SQL Lab). Maybe @mistercrunch remembers better?
+1 |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. For admin, please label this issue |
Looks like this PR has gone stale, any idea on what version we can expect this feature? |
Echoing @senduren's comment above, it has been about half a year since the last commit in this PR. Based on @betodealmeida's last comment, does it look like a full rewrite will be necessary? Is it this feature still on the roadmap for future versions of Superset? Thanks a lot! |
Chiming in as well. Our users love this feature. During the latest upgrade we had to add everything back manually. Would love to see this feature merged! |
Hi All, First thanks for the nice feature implementation. I have synced the source files for this PR with the latest master branch on incubator-superset and sent a PR to lyft/incubator-superset fork (lyft#98). If it is accepted we can continue with that. Or, I can also send another PR to incubator-superset if it is requested. We think the functionality is really nice to have. All the best, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but tagging (grouping) the dashboard is a good feature but the expection was is it saved in backend Db
in this PR, and the one send to lyft/incubator-superset fork (lyft#98), it is being saved to db. |
CATEGORY
Choose one
SUMMARY
This PR introduces the frontend for tags, behind a feature flag:
When the feature is enabled, owners can tag charts:
And dashboards:
Note that tags can be autocompleted.
A user who's not an owner will see non-editable tags:
When clicking on a tag, the user is taken to a page with content that has that tag:
TEST PLAN
Tested locally. We've also been using this at Lyft for months.
ADDITIONAL INFORMATION
REVIEWERS
@mistercrunch @williaster @khtruong