Skip to content
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

removes the angular dependency from tag cloud #15779

Merged

Conversation

thomasneirynck
Copy link
Contributor

@thomasneirynck thomasneirynck commented Dec 27, 2017

Removes the angular dependency from tag cloud.

@thomasneirynck thomasneirynck added Feature:Visualizations Generic visualization features (in case no more specific feature label is available) chore labels Dec 27, 2017
constructor(node, vis) {
this._containerNode = node;

const nodeContens = document.createElement('div');
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: nodeContents is misspelled

this._resize();
}

await this._renderComplete$.take(1).toPromise();
Copy link
Contributor

Choose a reason for hiding this comment

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

Observables don't start "listening" until they are subscribed to, which is being done by the .toPromise call. If the renderComplete event was fired before we got to this line, then this isn't going to do what we want.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For each render() call, we're only interested in the first render-complete-call starting after line 59. Do we need to subscribe to that observable up front, or at the start of the method, or in the constructor when we create it?

Copy link
Contributor

@kobelb kobelb Dec 29, 2017

Choose a reason for hiding this comment

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

If the renderComplete is only (and always) called after line 59 then the current approach should work.

@thomasneirynck
Copy link
Contributor Author

@ppisljar still failing test, but could benefit from first look already

Copy link
Member

@ppisljar ppisljar left a comment

Choose a reason for hiding this comment

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

some small comments, but i really like the fact we are getting rid of angular! :)


const nodeContens = document.createElement('div');
nodeContens.innerHTML = `
<div class="tagcloud-vis">
Copy link
Member

Choose a reason for hiding this comment

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

extract into html file and import it ?

if (!this._bucketAgg) {
return;
}
const aggConfigResult = new AggConfigResult(this._bucketAgg, false, event, event);
Copy link
Member

Choose a reason for hiding this comment

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

this is now changed in master to use the bucket.createFilter and API.addFilters

const truncatedMessage = this._containerNode.querySelector('.tagcloud-truncated-message');
const incompleteMessage = this._containerNode.querySelector('.tagcloud-incomplete-message');

if (!this._vis.aggs[0] || !this._vis.aggs[1]) {
Copy link
Member

Choose a reason for hiding this comment

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

extract this condition to a new variable hasAggsDefined so its easier to read ?

}

const data = response.tables[0];
this._bucketAgg = data.columns[0].aggConfig;
Copy link
Member

Choose a reason for hiding this comment

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

get the agg from vis.aggs instead of data ?

@thomasneirynck
Copy link
Contributor Author

rebasing

}

const bucketName = this._containerNode.querySelector('.tagcloud-custom-label');
bucketName.innerHTML = `${this._vis.aggs[0].makeLabel()} - ${this._vis.aggs[1].makeLabel()}`;
Copy link
Member

Choose a reason for hiding this comment

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

use textContent instead of innerHTML

@thomasneirynck
Copy link
Contributor Author

jenkins, test this

@@ -0,0 +1,121 @@
import TagCloud from './tag_cloud';
import tagCloudContainer from './tag_cloud_container.html';
Copy link
Contributor

Choose a reason for hiding this comment

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

What made you choose to manually specify the container's html that is then modified using DOM methods/attributes as opposed to using React? This seems like it'd be a lot easier if we used React, and then we wouldn't have to manually ensure that we weren't opening ourselves up to XSS by ensure we're using the correct attributes on the elements themselves, and it'd be done automatically for us.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Couple reasons imho:

What would React offer beyond adding another abstraction? What's the attack vector here that React would solve? The template is static content.

The async nature of the visualization is wrapped cleanly in the async .render() call instead of wrapping this in a React component and then having to work around it.

We recommend that community users use the createBaseVisualization since it is the most straightforward one (and the most simple interface to maintain). I think we should eat our own dogfood in a couple of places, here as good a place as any.

Copy link
Member

Choose a reason for hiding this comment

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

i think we should change that suggestion to use createBaseVisualization with kibana 6.3 and suggest to use createReactVisualization. and limit usage of createBaseVisualization only for when visualization is based on some other rendering library (like d3 or leaflet).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

^ very much disagree :)

Copy link
Contributor

Choose a reason for hiding this comment

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

What would React offer beyond adding another abstraction? What's the attack vector here that React would solve? The template is static content.

We're using node.textContent appropriately below when setting the label, but if we were to set innerHtml we open ourselves up to a potential XSS vulnerability (based on where the data comes from).

Additionally, we're dynamically setting styling throughout based on parameters when React provides a clear way to do so.

Copy link
Member

Choose a reason for hiding this comment

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

dangerouslySetInnerHtml is just as dangerous :)

i think with tagcloud (if going react way) we would need to rewrite whole tagcloud library. If the goal is getting rid of angular i think this current approach is good enough.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we should not at all impose a rendering-technology on our community developers. This almost as a matter of principle. We are not creating flexible interfaces when users need to follow the implementation-detail of the day. It used to be angular in Kibana, now it is React. Those are decisions that make sense for us (enforcing consistent tooling, enforcing consistent L&F ...), but have much less relevance for outside users.

Also, we need to really determine what the added value React brings as a wrapper for a Visualization-plugin. IMO this is very little for 80% of the visualizations. A visualization, almost by definition, is going to be not something textual or vanilla DOM-like and will rely on some other visualization library, that will introduce SVG, canvas, GL, clever use of HTML, ... What is React offering beyond a templating engine for that? Visualizations also should be a lot more asynchronous and stateful, so we can make efficient use of the DOM and animations. React doesn't really help with that, because those implementation details of how to do that efficiently will depend very much from visualization to visualization.

Copy link
Member

Choose a reason for hiding this comment

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

btw: not saying we shouldn't rewrite this in react, just justificating my LGTM :)

Copy link
Contributor

Choose a reason for hiding this comment

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

dangerouslySetInnerHtml is just as dangerous :)

Agreed, but we wouldn't need that here. We could grab the ref and pass it to the TagCloud and let it do it's thing. It doesn't mean that the TagCloud itself couldn't do some nasty stuff, but at least the code that we currently "own" would get the default security mechanisms.

Also, we need to really determine what the added value React brings as a wrapper for a Visualization-plugin.

As soon as we end up straying from standard HTML elements, I agree with you. However, in this situation a templating language for HTML is exactly what we need.

I'm all for us getting rid of AngularJS, but I'm not following why we're doing all this manually in this situation. Using React here would be less code, more consistent with the current direction and more secure by default.

Copy link
Contributor

Choose a reason for hiding this comment

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

My comment regarding the discussion can be found in the general PR comments :-)

@thomasneirynck
Copy link
Contributor Author

hey @timroes, can you review too? need decision on whether we should use React here or not. thx!

@timroes
Copy link
Contributor

timroes commented Jan 26, 2018

Before reviewing this, I also want to share my 2 cents regarding the React discussion.

I think we shouldn't put any recommendations on the community, what they are using, so they can use base, React, etc. whatever they want - maybe we can get rid of the Angular one at some point, since the digest cycle is really causing troubles.

Regarding what we should use and why to build the templates: I would totally agree with Brandon here, and think we gain a lot of benefit of using React also for some smaller things like that.
Of course you can also use dangerouslySetInnerHtml in React, but it's way more unlikely that you do it by accident, than using innerHTML and "accidentally" insert user content:

container.innerHTML = `<div class="foo">
  <div class="content">${vis.name}</div>
</div>

That example above can accidentally happen rather early. If you would use React, you would usually have written that as:

return (<div className="foo">
  <div className="content">{ vis.name }</div>
</div>);

and everything would be fine. Of course you can also create the same problem if you rewrite it to:

return (<div className="foo">
  <div className="content" dangerouslySetInnerHtml={vis.name}></div>
</div>);

But it's way more unlikely that this will happen "by accident" as the above one.

Also as soon as we have something like a loop of element or an condition, we usually already gain a lot of benefits from using React. For example if we look at the metrics visualization. By using React there, we suddenly don't clear the container on every render, and create elements within a loop again, but by using React there for the loop and the rendering, we basically just update the DOM with the stuff that actually changes... even if our render method is called way more often (which in our current infrastructure is the case).

So by changing Metrics and Markdown Vis to React, we basically got from updating the DOM nearly every render approach with a handful of DOM nodes, to basically no update if it's not absolutely required. Also let's not talk about, how imperformant innerHTML actually is, in contrast to using the proper appendChild, etc. nodes.

So my rule of thumb would be (for our visualizations, whether or not we recommend that to the community, I don't mind atm): As long as we need to create more than 1 or 2 elements, that need to be nested together, and for sure as soon as we have conditions or loops in there, let's use React for it. We will be safer and way more performant. If we just attach one canvas element and draw in this, or just one DOM node, that we pass to d3 (or something else), I am fine using Vanilla JS for it.

So regarding this PR and the different error messages, I would second Brandon's opinion and would rather use React for that, since this would make line 50 to 76 imho way better readable and thus maintainable, and also more performant, since we wouldn't need to use querySelector anymore.

@thomasneirynck
Copy link
Contributor Author

@timroes @kobelb @ppisljar understood! I'll change this to React. 🙇

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@thomasneirynck
Copy link
Contributor Author

rebasing with master broke this PR. please discard latest.

@elasticmachine
Copy link
Contributor

💔 Build Failed

@thomasneirynck
Copy link
Contributor Author

reverting this back to the react way of setting the label and feedback message. linting rules prevent setting of .innerHTML directly.

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@thomasneirynck
Copy link
Contributor Author

jenkins, test this

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@thomasneirynck thomasneirynck merged commit a417b11 into elastic:master Mar 15, 2018
thomasneirynck added a commit to thomasneirynck/kibana that referenced this pull request Mar 15, 2018
- Implements tag_cloud as a Base Visualization
- Use React for labels
- Introduce screenshot comparison unit tests
thomasneirynck added a commit that referenced this pull request Mar 15, 2018
- Implements tag_cloud as a Base Visualization
- Use React for labels
- Introduce screenshot comparison unit tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
chore Feature:Visualizations Generic visualization features (in case no more specific feature label is available) v6.3.0 v7.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants