-
Notifications
You must be signed in to change notification settings - Fork 368
Converted existing Region edit form to DDF/React/API form #6978
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
Conversation
bd579bf to
6e0b692
Compare
skateman
left a comment
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'd rather use hooks instead of a class component, all the required stuff for that is available here: https://reactjs.org/docs/hooks-reference.html
| @@ -0,0 +1,20 @@ | |||
| import {validatorTypes} from "@data-driven-forms/react-form-renderer"; | |||
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 spaces around validatorTypes
|
|
||
| RegionForm.propTypes = { | ||
| maxDescLen: PropTypes.number.isRequired, | ||
| regionFormId: PropTypes.number.isRequired, |
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 this is just an id field, why not name it id or regionId if we want to be precise?
| import { Grid } from 'patternfly-react'; | ||
| import createSchema from './region-form.schema'; | ||
| import MiqFormRenderer from '../../forms/data-driven-form'; | ||
| import {API} from '../../http_api'; |
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 spaces around API
| @@ -0,0 +1,71 @@ | |||
| import React, {Component, useState} 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.
Missing spaces
| }; | ||
| } | ||
|
|
||
| componentDidMount() { |
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 useEffect hook instead componentDidMount, then you can simplify it to a function.
| constructor(props) { | ||
| super(props); | ||
| this.state = { | ||
| schema: createSchema(props.maxDescLen), |
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.
Do we really need the schema in the state?
| import handleFailure from "../../helpers/handle-failure"; | ||
| import miqRedirectBack from "../../helpers/miq-redirect-back"; | ||
|
|
||
| class RegionForm extends 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.
Instead of a class component could you make a function component? Then you can eliminate the constructor function and have just useState below.
| } | ||
|
|
||
| onCancel = () => { | ||
| const message = sprintf(__('Edit of Region was cancelled by the user')); |
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.
Do you really need the sprintf here?
| return request().then(() => miqRedirectBack(message, 'success', submitUrl)).catch(miqSparkleOff); | ||
| } | ||
|
|
||
| render() { |
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 you're using a function component, you don't need this render(), just simply return with the markup from the function.
6e0b692 to
3d9f9f8
Compare
|
@skateman please re-review, made suggested changes |
3d9f9f8 to
6354207
Compare
|
|
||
| const RegionForm = ({ id, maxDescLen }) => { | ||
| const [initialValues, setInitialValues] = useState({}); | ||
| const [isLoading, setIsLoading] = useState(!!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.
You don't really need this second one, you can just test against the initialValues in the rendering.
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 you can always use a single state object like this:
const [{ description, isLoading }, setState] = useState({});
But in this case you don't really need it.
| const [isLoading, setIsLoading] = useState(!!id); | ||
|
|
||
| useEffect(() => { | ||
| if (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.
Do you really need this? Can the id be not set?
| }) => setInitialValues({ | ||
| ...data, | ||
| })) | ||
| .then(() => setIsLoading(false)) |
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 can skip this step
| miqSparkleOn(); | ||
| API.get(`/api/regions/${id}?attributes=description`) | ||
| .then(({ | ||
| ...data |
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 can just simply use data without the {...}
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.
Thinking this through, maybe you just need the description field and nothing more, so you don't even need the whole initialValues, just a { description }
| .then(({ | ||
| ...data | ||
| }) => setInitialValues({ | ||
| ...data, |
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.
Probably here too, no need for copying, especially twice.
| .then(() => setIsLoading(false)) | ||
| .then(miqSparkleOff); | ||
| } | ||
| }, []); |
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.
| }, []); | |
| }, [id]); |
| return request().then(() => miqRedirectBack(message, 'success', submitUrl)).catch(miqSparkleOff); | ||
| }; | ||
|
|
||
| if (isLoading) { |
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 (isLoading) { | |
| if (!initialValues) { |
| miqSparkleOn(); | ||
| const message = __('Region was saved'); | ||
| const submitUrl = `/ops/explorer/${id}?button=save`; | ||
| const request = () => API.post(`/api/regions/${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.
No need for the request variable allocation here, you can chain like:
return API.post().then().catch()
as you're not really using the request afterwards.
6354207 to
3356c02
Compare
|
@skateman made suggested changes. |
skateman
left a comment
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's Easter, we shouldn't be working today ![]()
| import miqRedirectBack from "../../helpers/miq-redirect-back"; | ||
|
|
||
| const RegionForm = ({ id, maxDescLen }) => { | ||
| const [description, setInitialValues] = useState({}); |
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 [description, setInitialValues] = useState({}); | |
| const [description, setDescription] = useState(); |
| useEffect(() => { | ||
| miqSparkleOn(); | ||
| API.get(`/api/regions/${id}?attributes=description`) | ||
| .then(({ description }) => setInitialValues({ description })) |
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.
| .then(({ description }) => setInitialValues({ description })) | |
| .then(({ description }) => setDescription(description)) |
| const message = __('Region was saved'); | ||
| const submitUrl = `/ops/explorer/${id}?button=save`; | ||
|
|
||
| return API.post(`/api/regions/${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.
I don't think we need to return here at all
| return ( | ||
| <Grid fluid style={{ paddingTop: 20 }}> | ||
| <MiqFormRenderer | ||
| initialValues={description} |
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.
| initialValues={description} | |
| initialValues={{ description }} |
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 sure why we need those extra curlies here, those populate text field with value [object Object] instead of an actual value of the description.
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's about semantics, the initialValues should contain the description not be it. The extra curly here basically does:
initialValues = { description: description }
app/helpers/view_helper.rb
Outdated
| MAX_NAME_LEN = 255 | ||
| MAX_DESC_LEN = 255 | ||
| MAX_HOSTNAME_LEN = 255 | ||
| MAX_DESC_LEN_SHORT = 50 |
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 this name descriptive enough? Out of context I wouldn't really understand what this is 😕
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 to me that made sense, but if we are nitpicking all variable names here, i would go with MAX_LEN_SMALL_TEXT_FIELD makes sense that is the length of a small text field in the 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.
I just thought that you'd want to use this constant on this single form, but now I understand. It can be either way I guess.
3356c02 to
8cbb2ff
Compare
| return ( | ||
| <Grid fluid style={{ paddingTop: 20 }}> | ||
| <MiqFormRenderer | ||
| initialValues={description} |
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's about semantics, the initialValues should contain the description not be it. The extra curly here basically does:
initialValues = { description: description }
| useEffect(() => { | ||
| miqSparkleOn(); | ||
| API.get(`/api/regions/${id}?attributes=description`) | ||
| .then(({ description }) => setDescription({ description })) |
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.
description = description instead of description = { description: description }
| .then(({ description }) => setDescription({ description })) | |
| .then(({ description }) => setDescription(description)) |
| import miqRedirectBack from "../../helpers/miq-redirect-back"; | ||
|
|
||
| const RegionForm = ({ id, maxDescLen }) => { | ||
| const [description, setDescription] = useState({}); |
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 description should be undefined and not an empty object when mounting the component:
| const [description, setDescription] = useState({}); | |
| const [description, setDescription] = useState(); |
d135cdf to
d56d19c
Compare
|
Code looks good now, I'll test it tomorrow 👍 |
skateman
left a comment
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.
|
|
||
| RegionForm.propTypes = { | ||
| maxDescLen: PropTypes.number.isRequired, | ||
| id: PropTypes.number.isRequired, |
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.
ids must be strings in js (only database ids, because of the high region precision issue)
| const flashLaterSpy = jest.spyOn(window, 'miqFlashLater'); | ||
| beforeEach(() => { | ||
| initialProps = { | ||
| id: 123, |
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.
(and here)
| :force_cancel_button => true, | ||
| :ajax_buttons => true}) | ||
|
|
||
| = react('RegionForm', :maxDescLen => ViewHelper::MAX_LEN_SMALL_TEXT_FIELD, :id => region.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.
region.id.to_s
|
Looks great :) The id thing is the only thing I'd change :) |
d56d19c to
a916e3f
Compare
|
Checked commits h-kataria/manageiq-ui-classic@479e749~...a916e3f with ruby 2.5.7, rubocop 0.69.0, haml-lint 0.28.0, and yamllint |

Fixes #6977