-
Notifications
You must be signed in to change notification settings - Fork 357
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
TextualSummaries: part of client side rendering with React. #3420
Conversation
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.
Thanks @martinpovolny - I just did a really quick code review - more inline:
|
||
$(function () { | ||
const summary = $('#textual_summary').data('summary'); | ||
ReactDOM.render(<TextualSummary summary={summary} />, document.getElementById('textual_summary')); |
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.
Please use the mounter helper instead ?
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.
yes, will do that
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.
cannot be done right now, unless the component uses only data
:
will wait with this change until that is fixed
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 be resolved in #3228, reviewing tomorrow..
@@ -0,0 +1,30 @@ | |||
import * as React from 'react'; |
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.
as a conversion, please just use .js, no need for .jsx extension imho.
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.
Well. I am not sure if this is a good idea. @himdel, @karelhala, @skateman, @Hyperkid123: what do you think?
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.
To be honest I like to use .jsx
for react specific code, that way you can immediately see that it's react component and nothing else.
I would even recommend using this ${cmp_name}.component.jsx
for components, ${action_name}.action.js
for redux actions and ${reducer_name}.reducer.js
for reducers, but that is my preference and no need to do it that way.
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 here, better explicit than implicit :)
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 disagree :) but thats OK.. there is an ongoing discussion in the foreman
community about how to structure this (and redux etc) at
theforeman/foreman#5240
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.
If we were using just React i would be fine with .js
, but i think it would be better to use .jsx
in our case.
import PropTypes from 'prop-types'; | ||
import GenericTableRow from './generic_table_row'; | ||
|
||
export default class GenericGroup extends React.Component { |
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 can probably be written as a const, e.g.:
const GenericGroup = ({ title, items }) => (
<table className="table table-bordered table-hover table-striped table-summary-screen">
<thead>
<tr>
<th colSpan="2" align="left">
{title}
</th>
</tr>
</thead>
<tbody> {items.map((item, i) => <GenericTableRow key={i} item={item} />)}</tbody>
</table>
);
export default GenericGroup;
and probably consider using patternfly-react table component.
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 can probably be written as a const,
yes, the ones where there'll be just the render
and no other code I'll convert to functions. (I guess you mean "use function rather than class").
and probably consider using patternfly-react table component.
not in the repo at this point, but yes, I will take a look if it's usable in this context
|
||
renderIcon() { | ||
// %i{:class => item[:icon], :title => item[:title]} | ||
return <i className={this.props.icon} title={this.props.title} />; |
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.
please use patternfly-react icon component.
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.
Not now, later when we include patternfly-react in manageiq, we can do it.
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.
TBH I think we should just add it, no point in writing the code twice...
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.
"writing the code twice..." -- you mean the single line ;-).
I will take a look at a later point. It's not a 5 minutes job. After 5 minutes of searching I found the component in patternfly-react. Figured it's not in the storybook https://rawgit.com/patternfly/patternfly-react/gh-pages/index.html. Have to figure what that means, hopefully it does not mean it should not be used.
Then I figured it's actually 2 components: https://github.com/patternfly/patternfly-react/blob/master/src/components/Icon/Icon.js and the resulting markup seems different. So have to check if it renders that same.
All that work is to prevent writing <i className={this.props.icon} title={this.props.title} />;
Probably not. That does not match my sense of effective work.
In this PR I am trying to start this task by moving the rendering to the client side while making sure that the textual summaries render as they did before. W/o extra changes if possible.
Later we can change the markup and/or styling. In a separate PR or at least a separate commit.
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've added patternfly/patternfly-react#234 to track the missing storybook.
the two components are simply because we support two type of components (patternfly icons and font-awesome)
using i assume its only font-awesome?
to use it you can simply do
import { Icon } from 'patternfly-react';
...
export default = () => (
<Icon type='fa' name='react' />
);
image: null, | ||
icon: null, | ||
}; | ||
|
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.
can be written as:
import * as React from 'react';
import PropTypes from 'prop-types';
/**
* Render icon or an image with a title
*/
const IconOrImage = ({ icon, title, image }) => {
const renderIcon = () => <i className={icon} />;
const renderImage = () => <img src={image} alt={title} title={title} />;
return image ? renderIcon() : renderImage();
};
IconOrImage.propTypes = {
title: PropTypes.string,
image: PropTypes.string,
icon: PropTypes.string,
};
IconOrImage.defaultProps = {
title: null,
image: null,
icon: null,
};
export default IconOrImage;
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.
yes, I will move the methods inside the class (I did that in some places already).
But the line return image ? renderIcon() : renderImage();
is not the same as my original code. This simplification would not work.
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.
sorry i did not test the code, what did not work? how about
return (image === null ? renderIcon() : renderImage());
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.
no ;-)
in case of both not present I need an empty string. And nested ternary is discouraged so I have that if
package.json
Outdated
@@ -54,6 +54,8 @@ | |||
"babel-polyfill": "^6.23.0", | |||
"babel-preset-env": "~1.4.0", | |||
"babel-preset-react": "^6.24.1", | |||
"babel-preset-es2015": "^6.24.1", | |||
"babel-preset-stage-1": "^6.24.1", |
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.
are those required?
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.
const renderIcon = () => <i className={icon} />;
Yes. W/o those 2 lines (and the change to .babelrc) this code would not compile ^^^
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 was the error? stage-1 should normally not be required.
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.
at transpile (/home/martin/Projects/manageiq-ui-classic/node_modules/babel-loader/lib/index.js:63:13)
at Object.module.exports (/home/martin/Projects/manageiq-ui-classic/node_modules/babel-loader/lib/index.js:163:20)
@ ./app/javascript/react/textual_summary/textual_group.jsx 20:17-39
@ ./app/javascript/react/textual_summary/textual_row.jsx
@ ./app/javascript/react/textual_summary/index.jsx
@ ./app/javascript/packs/application-common.js
ERROR in ./app/javascript/react/textual_summary/icon_or_image.jsx
Module build failed: SyntaxError: Unexpected token (12:13)
10 | }
11 |
> 12 | renderIcon = () => <i className={this.props.icon} title={this.props.title} />;
| ^
diff --git a/.babelrc b/.babelrc
index 9d3ea8b..936c5c8 100644
--- a/.babelrc
+++ b/.babelrc
@@ -1,5 +1,6 @@
{
- "presets": [["env", { "modules": false }], "react", "es2015", "stage-1"],
+ //"presets": [["env", { "modules": false }], "react", "es2015", "stage-1"],
+ "presets": [["env", { "modules": false }], "react", "es2015"],
"env": {
"test": {
"plugins": ["transform-es2015-modules-commonjs"]
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.
The problem is it's inside the class, so I need class fields. I can change the code to use methods instead or make it a function, not a class.
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.
The missing feature is called class fields: https://github.com/tc39/proposal-class-fields
class Foo {
renderIcon = 5;
}
Currently at stage3 .. so maybe we just need to bump es2015 to a newer version..
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.
Looks like maybe we should just be using env
(and react
) with some settings to target IE9+,
and maybe include
this particual feature..
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.
We need to figure the best way ;-)
Figuring the best way, I have some notes:
I like some of the poweful expressivity of the functional notation. But there are some parts that are really ugly inside JSX:
- the contional inclusion of markup:
(condition && (<your_crap_here>))
{(index === 0) && (
<td rowSpan={item.value.length} className="label">{item.label}</td>
)}
- case construct (hash + lookup):
{{
'shared/summary/textual': (
<GenericGroup items={props.group.items} title={props.group.title} />
),
'shared/summary/textual_tags': (
<TagGroup items={props.group.items} title={props.group.title} />
),
'shared/summary/textual_table': (
<SimpleTable items={props.group.items} title={props.group.title} />
),
}[props.group.partial]}
- I am glad for the
<React.Fragment>
. That helps.
Overall the JSX seems to be just another way to write markup and not particularly better than HAML. How many do we need?
On Mon, Feb 19, 2018 at 5:20 PM, Karel Hala ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In app/javascript/react/textual_summary/generic_group.jsx
<#3420 (comment)>
:
> @@ -0,0 +1,30 @@
+import * as React from 'react';
To be honest I like to use .jsx for react specific code, that way you can
immediately see that it's react component and nothing else.
I would even recommend using this ${cmp_name}.component.jsx for
components, ${action_name}.action.js for redux actions and
${reducer_name}.reducer.js for reducers, but that is my preference and no
need to do it that way.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#3420 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AABOx9SE7Y9TdYvMAVsNC0QU3AwKo8_qks5tWZFBgaJpZM4SI4mV>
.
I disagree :) but thats OK.. there is an ongoing discussion in the foreman
community about how to structure this (and redux etc) at
theforeman/foreman#5240
|
9e57467
to
ac72d55
Compare
@AparnaKarve : can you, please, tell me, why the |
2293a81
to
d61a798
Compare
In the image below, notice the Attributes, Associations and Methods table, that do not have a value. |
Thanks, @AparnaKarve. I saw that much it the code ;-) But I don't understand why we need a separate attribute Thanks! |
@AparnaKarve : to be more explicit: What was the motivation behind the PR that introduced the new attribute? Why testing just I'd like to remove this feature if it's not really needed, but there's no test that would fail if I do nor do I understand it when reading the PR. Thx! |
@martinpovolny the babel stuff .. diff --git a/.babelrc b/.babelrc
index 9d3ea8b30..83a481bc7 100644
--- a/.babelrc
+++ b/.babelrc
@@ -1,5 +1,6 @@
{
- "presets": [["env", { "modules": false }], "react", "es2015", "stage-1"],
+ "presets": [["env", { "modules": false }], "react"],
+ "plugins": ["transform-class-properties", "transform-object-rest-spread"],
"env": {
"test": {
"plugins": ["transform-es2015-modules-commonjs"]
diff --git a/package.json b/package.json
index 394df1c49..77e5ed098 100644
--- a/package.json
+++ b/package.json
@@ -54,12 +54,11 @@
"babel-jest": "^22.0.0",
"babel-loader": "~7.0",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
- "babel-plugin-transform-class-properties": "^6.24.1",
+ "babel-plugin-transform-class-properties": "~6.24.1",
+ "babel-plugin-transform-object-rest-spread": "~6.26.0",
"babel-polyfill": "^6.23.0",
- "babel-preset-env": "~1.4.0",
- "babel-preset-es2015": "^6.24.1",
+ "babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.24.1",
- "babel-preset-stage-1": "^6.24.1",
"coffee-loader": "~0.7.3",
"coffee-script": "~1.12.5",
"compression-webpack-plugin": "~0.4.0", (and making a TODO for myself to update to babel7) |
That's what I thought too and even adjusted the code to check for existence of But turns out that a nil value or a blank value could be a valid value in certain cases - in which case we need to render the blank value cell. |
@AparnaKarve : Can we have a nil value render nothing and an empty string to be rendered as empty string? The 5e2b5a6 that you referenced is what turned out not to work? If so then it is pretty counter-intuitive that it does not work. I would suggest having it documented in a test or at least add a source code comment. Can you remember what the problem was? Taking it from the other side: why cannot the GO behave the same way, why is GO the only entity that needs a special behavior? Would it be a problem if the behavior was consistent? I mean before you changes we already had 2 behaviors for handling empty values:
For some reason, it seems that you implemented a 3rd way:
So this is completely inconsistend with all the other textual summaries. It's a new type of behavior. If I accept that then looking at the code path that one has to go to display:
Is pretty obscure. I mean the functionality to display this is added to the most complex path in the TextualSummary rendering. So what next?I suggest that:
This way we should end up with less code, more tests and more consistend UX. |
@AparnaKarve : I also need you help with this: #3492 It's another area where the behavior is different to the other summaries. It's hard for me to fix that, because I have no data for the physical infra. Given you reviewed it, can you show me how you tested/debugged? I mean do you have a gist or script or database, that I could use? I don't see those two special partials covered in any tests so I need some help. |
This pull request is not mergeable. Please rebase and repush. |
GO instances need to display Attributes, Associations and Methods. Not showing data because it does not exist can be confusing for the user - I think that's what the BZ was about.
With 5e2b5a6, certain tables in Foreman Configured Systems summary page looked like this without the value column, which is incorrect. Compare the above with this -
6_0_dc_MBU_demo_01202018.backup from Dan's folder should have all the data for physical infra. |
08336da
to
d24f12d
Compare
@AparnaKarve : I have redone the empty groups a bit. I created a trivial component to display those, rather than having that behavior mixed onto the complex table. I found that the behavior is different in GO vs GO definitions. A am not sure that is desired. In my eyes it's yet another inconsistency. I did some extra fixing:
Overall it should work better that before and your review is welcome. |
Thanks. I was going to suggest that as well.
Non-admin users cannot view a GO definition. Admins, on the other hand, can view the GO definitions. However, for the sake of consistency, we could also add "No Attributes/Associations/Methods defined" for GO definitions. |
91ae5f6
to
ae83ad6
Compare
@AparnaKarve : I figured one of the Physical summary files is unused and the 2nd can be trivially replaced with a generic one: Please, check the 2 that mention "physical" close to the end. |
The The unused one is a result of this PR #3398. |
This pull request is not mergeable. Please rebase and repush. |
ea0564f
to
e722632
Compare
d6444f9
to
4ef09df
Compare
This pull request is not mergeable. Please rebase and repush. |
4ef09df
to
d972b41
Compare
239399e
to
c23cc30
Compare
c23cc30
to
a055b97
Compare
a055b97
to
f3390cd
Compare
f3390cd
to
f90b7f3
Compare
Checked commits martinpovolny/manageiq-ui-classic@89fcb55~...f90b7f3 with ruby 2.3.3, rubocop 0.52.1, haml-lint 0.20.0, and yamllint 1.10.0 **
|
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.
ping @mzazrivec, @himdel : please, merge (this is going to break already broken printing even more, but that must be fixed separately) |
I'm working on the printing right now, let's get this in so I can test it properly 😉 |
The goal is to implement a top level component for client-side rendering of textual summaries. Such component can then consume a JSON endpoint and later the API.
Extracted PRs
TODO
tests (snapshot testing)linksimages in general (see e.g. http://localhost:3000/ems_cluster/show/10000000000032 missing img)(will be fixed in a separate PR)
PDF rendering: replace with full-screen display with browser print?(dropped)
preprocess inlined images (needed in PDF rendering, so we may drop it)TextualCustom: 2x in physical server:,textual_firmware_table
textual_network_adapter_table
TextualCustom: in app/helpers/vm_helper/textual_summary.rb:textual_normal_operating_ranges
TextualListview: in ops/ vmdb tablesTextualMultilabel -- seems to be a superset of TextualTable with sorting and value expanding(expanding used in app/helpers/container_build_helper/textual_summary.rb)
TextualMultilink(FIXME: specs)app/helpers/ems_cluster_helper/textual_summary.rb: ret.blank? ? nil :
TextualMultilink.new(("OpenStack Status"), :items => ret)
app/helpers/host_helper/textual_summary.rb: TextualMultilink.new(("OpenStack Service Status"),
:items => textual_generate_openstack_status)
TextualTable (andible playbok job plays, security group firewall rules) (scurity group, network router)(dropped)
:spinner => true
used only once inapp/helpers/vm_helper/textual_summary.rb
intextual_genealogy
can we drop it?:no_value => true
-- only used in GO, can we drop it? (introduced in Follow-up for 2846 #2852 I don't understand that change)Next (separate PRs)
share the click behavior using HOC https://reactjs.org/docs/higher-order-components.htmlmove to https://github.com/karelhala/react-ui-components (or move the generic parts)