This repository has been archived by the owner on Apr 28, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add edit functionality to Vm Details overview
- Loading branch information
Showing
37 changed files
with
1,441 additions
and
222 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
|
||
import { getDescription } from '../../../utils'; | ||
import { InlineEdit } from '../../InlineEdit'; | ||
import { Loading } from '../../Loading/Loading'; | ||
|
||
const descriptionFormFields = { | ||
description: { | ||
id: 'description-textarea', | ||
type: 'textarea', | ||
}, | ||
}; | ||
|
||
export const Description = ({ formValues, vm, editing, updating, LoadingComponent, onFormChange }) => { | ||
if (!(formValues && formValues.description)) { | ||
onFormChange({ value: getDescription(vm) }, 'description', true); | ||
} | ||
|
||
return ( | ||
<InlineEdit | ||
formFields={descriptionFormFields} | ||
fieldsValues={formValues} | ||
editing={editing} | ||
updating={updating} | ||
LoadingComponent={LoadingComponent} | ||
onFormChange={(newValue, key) => onFormChange(newValue, key, true)} | ||
> | ||
{getDescription(vm) || 'VM has no description'} | ||
</InlineEdit> | ||
); | ||
}; | ||
|
||
Description.propTypes = { | ||
formValues: PropTypes.object, | ||
vm: PropTypes.object.isRequired, | ||
editing: PropTypes.bool, | ||
updating: PropTypes.bool, | ||
LoadingComponent: PropTypes.func, | ||
onFormChange: PropTypes.func, | ||
}; | ||
|
||
Description.defaultProps = { | ||
formValues: undefined, | ||
editing: false, | ||
updating: false, | ||
LoadingComponent: Loading, | ||
onFormChange: () => {}, | ||
}; |
46 changes: 46 additions & 0 deletions
46
src/components/Details/Description/fixtures/Description.fixture.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { Description } from '..'; | ||
import { cloudInitTestVm } from '../../../../k8s/mock_vm/cloudInitTestVm.mock'; | ||
|
||
export default [ | ||
{ | ||
component: Description, | ||
name: 'Description', | ||
props: { | ||
vm: cloudInitTestVm, | ||
onFormChange: () => {}, | ||
formValues: { | ||
description: { | ||
value: 'vm description', | ||
}, | ||
}, | ||
}, | ||
}, | ||
{ | ||
component: Description, | ||
name: 'Description edit', | ||
props: { | ||
vm: cloudInitTestVm, | ||
editing: true, | ||
onFormChange: () => {}, | ||
formValues: { | ||
description: { | ||
value: 'vm description', | ||
}, | ||
}, | ||
}, | ||
}, | ||
{ | ||
component: Description, | ||
name: 'Description updating', | ||
props: { | ||
vm: cloudInitTestVm, | ||
updating: true, | ||
onFormChange: () => {}, | ||
formValues: { | ||
description: { | ||
value: 'vm description', | ||
}, | ||
}, | ||
}, | ||
}, | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './Description'; |
14 changes: 14 additions & 0 deletions
14
src/components/Details/Description/tests/Description.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import React from 'react'; | ||
import { shallow } from 'enzyme'; | ||
|
||
import { Description } from '..'; | ||
import { default as DescriptionFixture } from '../fixtures/Description.fixture'; | ||
|
||
const testDescription = () => <Description {...DescriptionFixture[0].props} />; | ||
|
||
describe('<Description />', () => { | ||
it('renders correctly', () => { | ||
const component = shallow(testDescription()); | ||
expect(component).toMatchSnapshot(); | ||
}); | ||
}); |
27 changes: 27 additions & 0 deletions
27
src/components/Details/Description/tests/__snapshots__/Description.test.js.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`<Description /> renders correctly 1`] = ` | ||
<InlineEdit | ||
LoadingComponent={[Function]} | ||
editing={false} | ||
fieldsValues={ | ||
Object { | ||
"description": Object { | ||
"value": "vm description", | ||
}, | ||
} | ||
} | ||
formFields={ | ||
Object { | ||
"description": Object { | ||
"id": "description-textarea", | ||
"type": "textarea", | ||
}, | ||
} | ||
} | ||
onFormChange={[Function]} | ||
updating={false} | ||
> | ||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec lacus nibh, convallis vel nunc id,tempus vulputate augue. Proin eget nisl vel ante tincidunt accumsan vel at elit. Fusce eget tincidunt sem. Fusce cursus orci vitae nisl hendrerit mollis. Nullam at nulla ut ipsum malesuada laoreet a sit amet est. | ||
</InlineEdit> | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
|
||
import { getCpu, getFlavor, getMemory, getVmTemplate } from '../../../utils'; | ||
import { InlineEdit } from '../../InlineEdit/InlineEdit'; | ||
import { TemplateModel } from '../../../models'; | ||
import { CUSTOM_FLAVOR, VALIDATION_ERROR_TYPE } from '../../../constants'; | ||
import { getTemplateFlavors, settingsValue } from '../../../k8s/selectors'; | ||
import { Loading } from '../../Loading/Loading'; | ||
|
||
export class Flavor extends React.Component { | ||
constructor(props) { | ||
super(props); | ||
this.state = { | ||
loadingTemplate: false, | ||
template: null, | ||
}; | ||
this.resolveInitialValues(); | ||
} | ||
|
||
resolveInitialValues = () => { | ||
const flavor = getFlavor(this.props.vm) || CUSTOM_FLAVOR; | ||
const cpu = getCpu(this.props.vm); | ||
const memory = getMemory(this.props.vm); | ||
const memoryInt = memory ? parseInt(memory, 10) : undefined; | ||
this.props.onFormChange({ value: flavor }, 'flavor'); | ||
if (flavor === CUSTOM_FLAVOR) { | ||
this.props.onFormChange({ value: cpu }, 'cpu', !!cpu); | ||
this.props.onFormChange({ value: memoryInt }, 'memory', !!memoryInt); | ||
} | ||
}; | ||
|
||
componentDidMount() { | ||
const template = getVmTemplate(this.props.vm); | ||
if (template) { | ||
this.setState({ | ||
loadingTemplate: true, | ||
}); | ||
const getTemplatePromise = this.props.k8sGet(TemplateModel, template.name, template.namespace); | ||
getTemplatePromise | ||
.then(result => { | ||
this.props.onFormChange(result, 'template'); | ||
return this.setState({ | ||
loadingTemplate: false, | ||
template: result, | ||
}); | ||
}) | ||
.catch(error => { | ||
this.props.onLoadError(error.message || 'An error occurred while loading vm flavors. Please try again.'); | ||
this.setState({ | ||
loadingTemplate: false, | ||
template: null, | ||
}); | ||
}); | ||
} | ||
} | ||
|
||
getFlavorDescription = () => { | ||
const cpu = settingsValue(this.props.formValues, 'cpu'); | ||
const memory = settingsValue(this.props.formValues, 'memory'); | ||
const cpuStr = cpu ? `${cpu} CPU` : ''; | ||
const memoryStr = memory ? `${memory} Memory` : ''; | ||
const resourceStr = cpuStr && memoryStr ? `${cpuStr}, ${memoryStr}` : `${cpuStr}${memoryStr}`; | ||
return resourceStr ? <div>{resourceStr}</div> : undefined; | ||
}; | ||
|
||
getFlavorChoices = () => { | ||
const flavors = []; | ||
if (this.state.template) { | ||
flavors.push(...getTemplateFlavors([this.state.template])); | ||
} | ||
if (!flavors.some(flavor => flavor === CUSTOM_FLAVOR)) { | ||
flavors.push(CUSTOM_FLAVOR); | ||
} | ||
return flavors; | ||
}; | ||
|
||
flavorFormFields = () => ({ | ||
flavor: { | ||
id: 'flavor-dropdown', | ||
type: 'dropdown', | ||
choices: this.getFlavorChoices(), | ||
}, | ||
cpu: { | ||
id: 'flavor-cpu', | ||
title: 'CPU', | ||
type: 'positive-number', | ||
required: true, | ||
isVisible: formFields => settingsValue(formFields, 'flavor') === CUSTOM_FLAVOR, | ||
}, | ||
memory: { | ||
id: 'flavor-memory', | ||
title: 'Memory (GB)', | ||
type: 'positive-number', | ||
required: true, | ||
isVisible: formFields => settingsValue(formFields, 'flavor') === CUSTOM_FLAVOR, | ||
}, | ||
}); | ||
|
||
onFormChange = (newValue, key) => { | ||
let valid = true; | ||
if (this.props.formValues) { | ||
valid = | ||
valid && | ||
!Object.keys(this.props.formValues) | ||
.filter(formValueKey => formValueKey !== key) | ||
.some(formValueKey => formValueKey.validation && formValueKey.validation.type === VALIDATION_ERROR_TYPE); | ||
} | ||
valid = valid && newValue.validation ? newValue.validation.type !== VALIDATION_ERROR_TYPE : true; | ||
this.props.onFormChange(newValue, key, valid); | ||
}; | ||
|
||
render() { | ||
const { editing, updating, LoadingComponent } = this.props; | ||
const formFields = this.flavorFormFields(); | ||
|
||
return ( | ||
<InlineEdit | ||
formFields={formFields} | ||
editing={editing} | ||
updating={updating || (editing && this.state.loadingTemplate)} | ||
LoadingComponent={LoadingComponent} | ||
onFormChange={this.onFormChange} | ||
fieldsValues={this.props.formValues} | ||
> | ||
<div>{settingsValue(this.props.formValues, 'flavor')}</div> | ||
{this.getFlavorDescription()} | ||
</InlineEdit> | ||
); | ||
} | ||
} | ||
|
||
Flavor.propTypes = { | ||
vm: PropTypes.object.isRequired, | ||
onFormChange: PropTypes.func.isRequired, | ||
updating: PropTypes.bool, | ||
editing: PropTypes.bool, | ||
k8sGet: PropTypes.func.isRequired, | ||
LoadingComponent: PropTypes.func, | ||
formValues: PropTypes.object, | ||
onLoadError: PropTypes.func, | ||
}; | ||
|
||
Flavor.defaultProps = { | ||
updating: false, | ||
editing: false, | ||
LoadingComponent: Loading, | ||
formValues: undefined, | ||
onLoadError: () => {}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { Flavor } from '..'; | ||
import { cloudInitTestVm } from '../../../../k8s/mock_vm/cloudInitTestVm.mock'; | ||
import { fedora28 } from '../../../../k8s/mock_templates/fedora28.mock'; | ||
|
||
export default [ | ||
{ | ||
component: Flavor, | ||
name: 'Flavor', | ||
props: { | ||
vm: cloudInitTestVm, | ||
onFormChange: () => {}, | ||
k8sGet: () => | ||
new Promise(resolve => { | ||
resolve(fedora28); | ||
}), | ||
}, | ||
}, | ||
{ | ||
component: Flavor, | ||
name: 'Flavor editing', | ||
props: { | ||
vm: cloudInitTestVm, | ||
formValues: { | ||
cpu: { | ||
value: '2', | ||
}, | ||
memory: { | ||
value: '2', | ||
}, | ||
flavor: { | ||
value: 'Custom', | ||
}, | ||
}, | ||
editing: true, | ||
onFormChange: () => {}, | ||
k8sGet: () => | ||
new Promise(resolve => { | ||
resolve(fedora28); | ||
}), | ||
}, | ||
}, | ||
{ | ||
component: Flavor, | ||
name: 'Flavor updating', | ||
props: { | ||
vm: cloudInitTestVm, | ||
updating: true, | ||
onFormChange: () => {}, | ||
k8sGet: () => | ||
new Promise(resolve => { | ||
resolve(fedora28); | ||
}), | ||
}, | ||
}, | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './Flavor'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import React from 'react'; | ||
import { shallow } from 'enzyme'; | ||
|
||
import { Flavor } from '..'; | ||
import { default as FlavorFixture } from '../fixtures/Flavor.fixture'; | ||
|
||
const testFlavor = () => <Flavor {...FlavorFixture[0].props} />; | ||
|
||
describe('<Flavor />', () => { | ||
it('renders correctly', () => { | ||
const component = shallow(testFlavor()); | ||
expect(component).toMatchSnapshot(); | ||
}); | ||
}); |
Oops, something went wrong.