-
Notifications
You must be signed in to change notification settings - Fork 74
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
THREESCALE-2286: Refactor Service Discovery into a React component #763
THREESCALE-2286: Refactor Service Discovery into a React component #763
Conversation
Codecov Report
@@ Coverage Diff @@
## master #763 +/- ##
=========================================
+ Coverage 92.9% 92.9% +<.01%
=========================================
Files 2414 2414
Lines 78366 78366
=========================================
+ Hits 72806 72809 +3
+ Misses 5560 5557 -3
Continue to review full report at Codecov.
|
406b073
to
a12ea99
Compare
b72b5a0
to
7dfa8c3
Compare
77b3d2f
to
53ba8a0
Compare
app/javascript/packs/polyfills.js
Outdated
@@ -0,0 +1,2 @@ | |||
import 'babel-polyfill' | |||
import 'whatwg-fetch' |
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 needed to make fetch and promises work in IE11.
I've found (in this GH Issue) this is the recommended way to do it when using webpacker.
Do you know a better way @didierofrivia @josemigallas ?
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.
You've used it differently in the past: https://github.com/3scale/porta/blob/master/app/javascript/src/Stats/inlinechart/index.jsx#L6. However, if this new one is a preferred way by webpackers, I'd just keep it consistent.
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 will keep it consistent with inlinechart component. Later, when we update webpacker gem, we can change it if we decide that.
|
||
const ServiceDiscoveryForm = (props: ServiceDiscoveryFormType) => { | ||
const {isVisible, providerAdminServiceDiscoveryServicesPath, namespaces, services, selectedNamespace, handleChangeNamespaces} = props | ||
return (<form className={`formtastic ${isVisible ? '' : 'is-hidden'}`} id="service_discovery" action={providerAdminServiceDiscoveryServicesPath} acceptCharset="UTF-8" method="post"> |
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 the moment, we don't have a generic form component for React.
But as we are close to implement PF4-React and it includes all kind of form elements, I think we should keep this form (to match the styles of other forms generated by rails), and later replace it with PF4 forms
@josemigallas @didierofrivia @thomasmaas
app/javascript/packs/new_service.js
Outdated
|
||
document.addEventListener('DOMContentLoaded', () => { | ||
const newServiceWrapper = document.getElementById('new_service_wrapper') | ||
const newServiceData = JSON.parse(newServiceWrapper.dataset.newServiceData) |
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.
you could use our json_utils here. Maybe decide what to do when the config is wrong...
import React from 'react' | ||
// TODO: Get authenticityToken from rails | ||
// Is this needed, or was just generated by formtastic ? | ||
const authenticityToken = '' |
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 renderSelectOptions = (options: string[]) => { | ||
return options.length > 0 | ||
? options.map(option => <option key={option} value={option}>{option}</option>) | ||
: undefined |
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 there any value in returning explicitly undefined
?
|
||
import React from 'react' | ||
// TODO: Get authenticityToken from rails | ||
const authenticityToken = '' |
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.
commented above regarding getting this. You could extract that function to utils
app/javascript/packs/new_service.js
Outdated
} | ||
render(<NewServiceWrapper {...props}/>, newServiceWrapper) |
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.
there's no component to tell the user there's no service data?
return response.json() | ||
}) | ||
.then(data => data[dataItem].reduce((acc, current) => acc.concat(current.metadata.name), [])) | ||
.catch(error => console.error(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.
you are catching the error that you are throwing 5 lines above, and using just the console. It's this meant to be like this? No message to the user?
} | ||
|
||
// eslint-disable max-lines-per-function | ||
const NewServiceWrapper = (props: 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.
you could break this huge fn into smaller ones, and use composition and keeping the state separated.
|
||
const ServiceDiscoveryForm = (props: ServiceDiscoveryFormType) => { | ||
const {isVisible, providerAdminServiceDiscoveryServicesPath, namespaces, services, selectedNamespace, handleChangeNamespaces} = props | ||
return (<form className={`formtastic ${isVisible ? '' : 'is-hidden'}`} id="service_discovery" action={providerAdminServiceDiscoveryServicesPath} acceptCharset="UTF-8" method="post"> |
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.
You are using the same form here and in ServiceNewForm
, you might want to extract that one in a separated component
handleChangeNamespaces: jest.fn() | ||
} | ||
|
||
it('should render properly ', () => { |
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.
it should render properly, I'm expecting some specifics not only the shallow component exists
@thomasmaas using 0.75 rems of space |
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.
Apart from the comments, I think there are many meaningless specs. We should start focusing a bit more on that, we are adding a lot of new logic and components that are not tested and makes it less maintainable.
app/javascript/packs/new_service.js
Outdated
|
||
document.addEventListener('DOMContentLoaded', () => { | ||
const newServiceWrapper = document.getElementById('new_service_wrapper') | ||
const newServiceData = safeFromJsonString(newServiceWrapper.dataset.newServiceData) |
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 think it should be called newServiceFormProps
to be more explicit.
import React from 'react' | ||
|
||
const ErrorMessage = ({error}: {error: string}) => { | ||
const classNames = `errorMessage ${error ? '' : 'is-hidden'}` |
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 proper way to prevent a component from rendering is to return null not using css.
app/javascript/src/NewService/components/FormElements/ErrorMessage.jsx
Outdated
Show resolved
Hide resolved
const ErrorMessage = ({error}: {error: string}) => { | ||
const classNames = `errorMessage ${error ? '' : 'is-hidden'}` | ||
return <p className={classNames}> | ||
{`Sorry, your request has failed with the error: ${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.
Why a request? Is it an API error message? Why not simply rendering 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.
The error comes from the request response, so yes.
What error do you think is more clear to the user @thomasmaas ?
We have something like this:
Sorry, your request has failed with the error: Cannot read property 'projects' of undefined
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 error comes from the request response, so yes.
Ok, in that case it could be renamed FetchErrorMessage
or NetworkErrorMessage
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.
@damianpm in order to write something in 'normal' english i first need to know what the error represents.
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.
@thomasmaas It will be a fetch error:
Cannot read property 'projects' of undefined
or
Cannot read property 'services' of undefined
Additionally, the browser shows a full error message like:
|
||
Enzyme.configure({adapter: new Adapter()}) | ||
|
||
const 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.
PascalCase is reserved for classes, component, types... Use better camel or UPPER.
const wrapper = shallow(<ServiceDiscoveryForm {...Props}/>) | ||
expect(wrapper.find('#service_source').exists()).toEqual(true) | ||
}) | ||
it('should render error message hidden by default', () => { |
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 means 'by default'?
} | ||
|
||
describe('Service Discovery Form', () => { | ||
it('should render properly', () => { |
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's 'properly'?
app/javascript/src/NewService/components/FormElements/ServiceDiscoveryListItems.jsx
Show resolved
Hide resolved
|
||
Enzyme.configure({adapter: new Adapter()}) | ||
|
||
describe('Service Source Form', () => { |
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.
Since you're including a describe block by default, perhaps it would help to use a more descriptive name for the specs. ServiceSourceForm
doesn't mean much itself.
d4eeda8
to
bede262
Compare
d74726a
to
4298083
Compare
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.
Keep it up Damian! thanks for the hard work, we are nearly the end now :)
ps: you might want to review the tests also.
app/javascript/src/NewService/components/FormElements/ErrorMessage.jsx
Outdated
Show resolved
Hide resolved
app/javascript/src/NewService/components/FormElements/FormWrapper.jsx
Outdated
Show resolved
Hide resolved
) | ||
} catch (error) { | ||
console.error(error) | ||
return 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.
Here, as my previous comment, you could instead of returning null
, could return the input with the value null
. That way you'll be addressing the type signature implicit of the function.
On a different matter, when @josemigallas suggested not to explicitly return the type of a React.Component
I was fearing this case... since it would have failed the type check otherwise ;)
4dd1aab
to
2071204
Compare
2071204
to
628b9c5
Compare
expect(wrapper.find('#service_discovery').exists()).toEqual(true) | ||
}) | ||
|
||
it('should render `Import from OpenShift` input not disabled when `isServiceDiscoveryUsable` is true', () => { |
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.
There are two things here:
- The description of the test should be more "human", since it's meant to communicate what you will assert. This one looks more like pseudo code to me. You could go with something like
it('should render "Import from OS" input enabled when service discovery is selected', () =>...
- I'd expect to see the text "Import from OpenShift" and that property
isServiceDiscoveryUsable
asserted since you are mentioning them in the description, but they are not... These apparently are, as I understand, not important and end up being a bit misleading (In this case)
This applies to the rest of the tests below.
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! Don't forget to deploy first to OS and confirm it works as expected. Thanks for the hard work! 🎖
What this PR does / why we need it:
Refactor Service discovery into a React component
Add flow type checking
Add js tests
Which issue(s) this PR fixes
https://issues.jboss.org/browse/THREESCALE-2286
Verification steps
Screenshots:
New service:
Service Discovery:
Error:
Note:
Please test in IE11 and Firefox <61