Skip to content

Commit

Permalink
Fixes #30784 - replace memory allocation input to react comp
Browse files Browse the repository at this point in the history
  • Loading branch information
yifatmakias authored and ezr-ondrej committed Mar 24, 2021
1 parent 9d44364 commit 8a9922f
Show file tree
Hide file tree
Showing 12 changed files with 113 additions and 147 deletions.
10 changes: 7 additions & 3 deletions app/views/compute_resources_vms/form/libvirt/_base.html.erb
Expand Up @@ -5,9 +5,13 @@
<%= counter_f f, :cpus, :disabled => !new_vm, :label => _('CPUs'), :label_size => 'col-md-2', :'data-soft-max' => compute_resource.max_cpu_count %>
<%= select_f f, :cpu_mode, Foreman::Model::Libvirt::CPU_MODES, :to_s, :to_s, { }, :label => _("CPU mode"), :label_size => "col-md-2" %>
<%= byte_size_f f, :memory, :disabled => !new_vm, :label => _('Memory'), :label_size => "col-md-2", :'data-soft-max' => compute_resource.max_memory %>

<%= react_component('MemoryAllocationInput',
{label: 'Memory',
disabled: !new_vm,
recommendedMaxValue: compute_resource.max_memory.to_i / 1.megabyte,
defaultValue: f.object.memory.present? ? f.object.memory.to_i / 1.megabytes : 1024,
id: "#{f.object_name.gsub(/[\[\]]/, '[' => '_', ']' => '_')}memory",
name: "#{f.object_name}[memory]"}) %>
<!--TODO # Move to a helper-->
<% checked = params[:host] && params[:host][:compute_attributes] && params[:host][:compute_attributes][:start] || '1' %>
<%= checkbox_f f, :start, { :checked => (checked == '1'), :help_inline => _("Power ON this machine"), :label => _('Start'), :label_size => "col-md-2"} if new_vm && controller_name != "compute_attributes" %>
Expand Down
11 changes: 8 additions & 3 deletions app/views/compute_resources_vms/form/ovirt/_base.html.erb
Expand Up @@ -38,9 +38,14 @@ disable_mem = !new_vm || (new_vm && instance_type && instance_type.memory.presen
<%= counter_f f, :cores, :disabled => disable_cores, :label => _('Cores per socket'), :label_size => 'col-md-2' %>
<%= counter_f f, :sockets, :disabled => disable_sockets, :label => _('Sockets'), :label_size => 'col-md-2' %>
<%= byte_size_f f, :memory, :disabled => disable_mem, :label => _('Memory'), :label_size => "col-md-2" %>
<div id="memory-input">
<%= react_component('MemoryAllocationInput',
{label: 'Memory',
disabled: disable_mem,
defaultValue: f.object.memory.present? ? f.object.memory.to_i / 1.megabytes : 1024,
id: "#{f.object_name.gsub(/[\[\]]/, '[' => '_', ']' => '_')}memory",
name: "#{f.object_name}[memory]"}) %>
</div>
<% checked = params[:host] && params[:host][:compute_attributes] && params[:host][:compute_attributes][:start] || '1' %>
<%= checkbox_f f, :ha, { :checked => (f.object.ha == '1') , :label => _('Highly Available'), :label_size => "col-md-2" } %>
<%= checkbox_f f, :start, { :checked => (checked == '1'), :help_inline => _("Power ON this machine"), :label => _('Start'), :label_size => "col-md-2" } if new_vm && controller_name != "compute_attributes" %>
Expand Down
37 changes: 30 additions & 7 deletions webpack/assets/javascripts/compute_resource/ovirt.js
Expand Up @@ -14,6 +14,8 @@ import $ from 'jquery';
import { showSpinner } from '../foreman_tools';
import { testConnection } from '../foreman_compute_resource';

const megabyte = 1024 * 1024;

export function templateSelected(item) {
const template = $(item).val();

Expand All @@ -32,9 +34,16 @@ export function templateSelected(item) {
// As Instance Type values will take precence over templates values,
// we don't update memory/cores values if instance type is already selected
if (!$('#host_compute_attributes_instance_type').val()) {
$('[id$=_memory]')
.val(result.memory)
.trigger('change');
const memoryInputElement = document
.getElementById('memory-input')
.getElementsByTagName('foreman-react-component')[0];
memoryInputElement.setAttribute(
'data-props',
JSON.stringify({
...memoryInputElement.reactProps,
defaultValue: result.memory / megabyte,
})
);
$('[id$=_cores]').val(result.cores);
$('[id$=_sockets]').val(result.sockets);
$('[id$=_ha]').prop('checked', result.ha);
Expand Down Expand Up @@ -77,15 +86,29 @@ export function instanceTypeSelected(item) {
url,
data: `instance_type_id=${instanceType}`,
success(result) {
const memoryInputElement = document
.getElementById('memory-input')
.getElementsByTagName('foreman-react-component')[0];
if (result.name != null) {
$('[id$=_memory]')
.val(result.memory)
.trigger('change');
memoryInputElement.setAttribute(
'data-props',
JSON.stringify({
...memoryInputElement.reactProps,
defaultValue: result.memory / megabyte,
})
);
$('[id$=_cores]').val(result.cores);
$('[id$=_sockets]').val(result.sockets);
$('[id$=_ha]').prop('checked', result.ha);
}
['_memory', '_cores', '_sockets', '_ha'].forEach(name =>
memoryInputElement.setAttribute(
'data-props',
JSON.stringify({
...memoryInputElement.reactProps,
disabled: result.name != null,
})
);
['_cores', '_sockets', '_ha'].forEach(name =>
$(`[id$=${name}]`).prop('readOnly', result.name != null)
);
const instanceTypeSelector = $(
Expand Down
Expand Up @@ -22,6 +22,7 @@ exports[`CPUCoresInput rendering should render with default props 1`] = `
className=""
disabled={false}
format={null}
id=""
label="CPUs"
minValue={1}
name=""
Expand Down
Expand Up @@ -3,22 +3,28 @@ import PropTypes from 'prop-types';
import { WarningTriangleIcon, ErrorCircleOIcon } from '@patternfly/react-icons';
import { HelpBlock, Grid, Col, Row } from 'patternfly-react';
import { translate as __ } from '../../common/I18n';
import NumericInput from '../common/forms/NumericInput';
import { GB_FORMAT, MB_FORMAT, GB_STEP, MB_STEP } from './constants';
import { MB_FORMAT, MEGABYTES } from './constants';
import './memoryAllocationInput.scss';
import { noop } from '../../common/helpers';
import NumericInput from '../common/forms/NumericInput';

const MemoryAllocationInput = ({
label,
defaultValue,
label,
onChange,
maxValue,
minValue,
recommendedMaxValue,
name,
id,
disabled,
}) => {
const [value, setValue] = useState(defaultValue);
const [validationState, setValidationState] = useState(undefined);
const [value, setValue] = useState(defaultValue);

useEffect(() => {
setValue(defaultValue);
}, [defaultValue]);

useEffect(() => {
if (value > maxValue && maxValue !== undefined) {
Expand All @@ -34,27 +40,15 @@ const MemoryAllocationInput = ({
}, [recommendedMaxValue, maxValue, value]);

const handleValueIncrease = () => {
if (value >= GB_STEP) {
setValue(value + GB_STEP);
} else {
setValue(value + MB_STEP);
}
setValue(value * 2);
};

const handleValueDecrease = () => {
if (value <= GB_STEP) {
setValue(value - MB_STEP);
} else {
setValue(value - GB_STEP);
}
setValue(value / 2);
};

const handleTypedValue = v => {
if (v > GB_STEP) {
setValue(Math.round(v / GB_STEP) * GB_STEP);
} else {
setValue(Math.round(v / MB_STEP) * MB_STEP);
}
setValue(v);
};

const handleChange = v => {
Expand All @@ -68,18 +62,7 @@ const MemoryAllocationInput = ({
onChange(value);
};

const format = v => {
// used the buttons
if (v % MB_STEP === 0) {
if (v >= GB_STEP) {
return `${v / GB_STEP} ${GB_FORMAT}`;
}
return `${v} ${MB_FORMAT}`;
}

// typed value
return v;
};
const format = v => `${v} ${MB_FORMAT}`;

const helpBlock = () => {
if (validationState === 'warning') {
Expand Down Expand Up @@ -107,13 +90,15 @@ const MemoryAllocationInput = ({
<Col>
<NumericInput
value={value}
id={id}
format={format}
parser={parser}
onChange={handleChange}
label={label}
name={name}
disabled={disabled}
minValue={minValue}
/>
<input type="hidden" name={name} value={value * MEGABYTES} />
</Col>
</Row>
<Row>
Expand All @@ -129,16 +114,20 @@ const MemoryAllocationInput = ({
MemoryAllocationInput.propTypes = {
/** Set the label of the memory allocation input */
label: PropTypes.string,
/** Set the default value of the numeric input */
/** Set the default value of the memory allocation input */
defaultValue: PropTypes.number,
/** Set the recommended max value of the numeric input */
recommendedMaxValue: PropTypes.number,
/** Set the max value of the numeric input */
maxValue: PropTypes.number,
/** Set the min value of the numeric input */
minValue: PropTypes.number,
/** Set the onChange function of the numeric input */
onChange: PropTypes.func,
/** Set the name of the numeric input */
name: NumericInput.propTypes.name,
/** Set the id of the numeric input */
id: NumericInput.propTypes.id,
/** Set whether the numeric input will be disabled or not */
disabled: NumericInput.propTypes.disabled,
};
Expand All @@ -149,7 +138,9 @@ MemoryAllocationInput.defaultProps = {
onChange: noop,
recommendedMaxValue: undefined,
maxValue: undefined,
minValue: 1,
name: '',
id: '',
disabled: false,
};

Expand Down
Expand Up @@ -12,11 +12,11 @@ export default {
};

export const UseMemoryAllocationInput = () => (
<MemoryAllocationInput
label={text('Label', 'Memory')}
defaultValue={number('DefaultValue', 1024)}
onChange={action('Value was changed')}
recommendedMaxValue={number('RecommendedMaxValue', 10240)}
maxValue={number('MaxValue', 20480)}
/>
);
<MemoryAllocationInput
label={text('Label', 'Memory')}
defaultValue={number('DefaultValue', 1024)}
onChange={action('Value was changed')}
recommendedMaxValue={number('RecommendedMaxValue', 10240)}
maxValue={number('MaxValue', 20480)}
/>
);
@@ -1,46 +1,29 @@
import React from 'react';
import { mount } from '@theforeman/test';
import { testComponentSnapshotsWithFixtures } from '../../../common/testHelpers';
import MemoryAllocationInput from '../MemoryAllocationInput';
import { Provider } from 'react-redux';
import MemoryAllocationInput from '../';
import Store from "../../../redux";

const props = {
label: 'Memory',
};

const fixtures = {
'should render with default props': props,
};

describe('MemoryAllocationInput', () => {
describe('rendering', () => {
testComponentSnapshotsWithFixtures(MemoryAllocationInput, fixtures);
});

it('MB to GB change', async () => {
const component = mount(<MemoryAllocationInput defaultValue={768} />);
expect(component.find('input').prop('value')).toEqual('768 MB');
component
.find('.foreman-numeric-input-handler-up')
.at(0)
.simulate('mousedown');

component.update();
expect(component.find('input').prop('value')).toEqual('1 GB');
it('warning alert', async () => {
const component = mount(
<Provider store={Store}>
<MemoryAllocationInput defaultValue={11264} recommendedMaxValue={10240} />
</Provider>
);
expect(component.find('.foreman-numeric-input-input').prop('value')).toEqual('11264 MB');
expect(component.find('.warning-icon').exists()).toBeTruthy();
});
});

it('warning alert', async () => {
const component = mount(
<MemoryAllocationInput defaultValue={11264} recommendedMaxValue={10240} />
);
expect(component.find('input').prop('value')).toEqual('11 GB');
expect(component.find('.warning-icon').exists()).toBeTruthy();
});

it('error alert', async () => {
const component = mount(
<MemoryAllocationInput defaultValue={21504} maxValue={20480} />
);
expect(component.find('input').prop('value')).toEqual('21 GB');
expect(component.find('.error-icon').exists()).toBeTruthy();
it('error alert', async () => {
const component = mount(
<Provider store={Store}>
<MemoryAllocationInput defaultValue={21504} maxValue={20480} />
</Provider>
);
expect(component.find('.foreman-numeric-input-input').prop('value')).toEqual('21504 MB');
expect(component.find('.error-icon').exists()).toBeTruthy();
});
});

This file was deleted.

@@ -1,7 +1,3 @@
export const GB_FORMAT = 'GB';

export const MB_FORMAT = 'MB';

export const GB_STEP = 1024;

export const MB_STEP = 256;
export const MEGABYTES = 1048576;
@@ -1,5 +1,9 @@
@import '../../common/colors.scss';

.container {
max-width: none !important;
}

.warning-icon {
margin-right: 3px;
color: $pf-gold-400;
Expand Down

0 comments on commit 8a9922f

Please sign in to comment.