Skip to content

Commit

Permalink
Add slider component for adding the disk size (#1206)
Browse files Browse the repository at this point in the history
  • Loading branch information
Andres Martinez Gotor authored Oct 8, 2019
1 parent 122f4aa commit 32d70fd
Show file tree
Hide file tree
Showing 12 changed files with 757 additions and 1 deletion.
1 change: 1 addition & 0 deletions dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"raf": "^3.4.0",
"react": "^16.2.0",
"react-ace": "^5.9.0",
"react-compound-slider": "^2.3.0",
"react-dom": "^16.2.0",
"react-feather": "^1.0.8",
"react-jsonschema-form": "^1.0.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
color: #5f6369;
font-size: 0.9em;
}

.disk_size_input {
width: 75%;
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ const defaultProps = {
blogName: { path: "blogName", value: "my-blog", type: "string" } as IBasicFormParam,
},
},
{
description: "renders a basic deployment with a disk size",
params: {
diskSize: { path: "size", value: "10Gi", type: "string" } as IBasicFormParam,
},
},
{
description: "renders a basic deployment with username, password, email and a generic string",
params: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { IBasicFormParam } from "shared/types";
import StringParam from "./StringParam";

import "./BasicDeploymentForm.css";
import DiskSizeParam from "./DiskSizeParam";

export interface IBasicDeploymentFormProps {
params: { [name: string]: IBasicFormParam };
Expand Down Expand Up @@ -55,6 +56,17 @@ class BasicDeploymentForm extends React.Component<IBasicDeploymentFormProps> {
param={param}
/>
);
case "diskSize":
return (
<DiskSizeParam
label="Disk Size"
handleBasicFormParamChange={this.props.handleBasicFormParamChange}
key={id}
id={id}
name={name}
param={param}
/>
);
default:
if (param.type === "string") {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { shallow } from "enzyme";
import * as React from "react";
import { IBasicFormParam } from "shared/types";
import Slider from "../../../components/Slider";
import DiskSizeParam from "./DiskSizeParam";

const defaultProps = {
id: "disk",
name: "diskSize",
label: "Disk Size",
param: {
value: "10Gi",
type: "string",
path: "disk",
} as IBasicFormParam,
handleBasicFormParamChange: jest.fn(),
};

it("renders a disk size param with a default value", () => {
const wrapper = shallow(<DiskSizeParam {...defaultProps} />);
expect(wrapper.state("Gi")).toBe(10);
expect(wrapper).toMatchSnapshot();
});

it("changes the value of the param when the slider changes", () => {
const param = {
value: "10Gi",
type: "string",
path: "disk",
} as IBasicFormParam;
const handleBasicFormParamChange = jest.fn(() => {
param.value = "20Gi";
return jest.fn();
});

const wrapper = shallow(
<DiskSizeParam
{...defaultProps}
param={param}
handleBasicFormParamChange={handleBasicFormParamChange}
/>,
);
expect(wrapper.state("Gi")).toBe(10);

const slider = wrapper.find(Slider);
(slider.prop("onChange") as (values: number[]) => void)([20]);

expect(param.value).toBe("20Gi");
expect(handleBasicFormParamChange.mock.calls[0]).toEqual([
"diskSize",
{ value: "20Gi", type: "string", path: "disk" },
]);
});

it("updates state but does not change param value during slider update (only when dropped in a point)", () => {
const handleBasicFormParamChange = jest.fn();
const wrapper = shallow(
<DiskSizeParam {...defaultProps} handleBasicFormParamChange={handleBasicFormParamChange} />,
);
expect(wrapper.state("Gi")).toBe(10);

const slider = wrapper.find(Slider);
(slider.prop("onUpdate") as (values: number[]) => void)([20]);

expect(wrapper.state("Gi")).toBe(20);
expect(handleBasicFormParamChange).not.toHaveBeenCalled();
});

describe("when changing the value in the input", () => {
it("parses a number and forwards it", () => {
const valueChange = jest.fn();
const handleBasicFormParamChange = jest.fn(() => valueChange);
const wrapper = shallow(
<DiskSizeParam {...defaultProps} handleBasicFormParamChange={handleBasicFormParamChange} />,
);
expect(wrapper.state("Gi")).toBe(10);

const input = wrapper.find("input#disk");
const event = { currentTarget: { value: "20" } } as React.FormEvent<HTMLInputElement>;
(input.prop("onChange") as ((e: React.FormEvent<HTMLInputElement>) => void))(event);

expect(wrapper.state("Gi")).toBe(20);
expect(valueChange.mock.calls[0]).toEqual([{ currentTarget: { value: "20Gi" } }]);
});

it("ignores values in the input that are not digits", () => {
const valueChange = jest.fn();
const handleBasicFormParamChange = jest.fn(() => valueChange);
const wrapper = shallow(
<DiskSizeParam {...defaultProps} handleBasicFormParamChange={handleBasicFormParamChange} />,
);
expect(wrapper.state("Gi")).toBe(10);

const input = wrapper.find("input#disk");
const event = { currentTarget: { value: "foo20*#@$" } } as React.FormEvent<HTMLInputElement>;
(input.prop("onChange") as ((e: React.FormEvent<HTMLInputElement>) => void))(event);

expect(wrapper.state("Gi")).toBe(20);
expect(valueChange.mock.calls[0]).toEqual([{ currentTarget: { value: "20Gi" } }]);
});

it("accept decimal values", () => {
const valueChange = jest.fn();
const handleBasicFormParamChange = jest.fn(() => valueChange);
const wrapper = shallow(
<DiskSizeParam {...defaultProps} handleBasicFormParamChange={handleBasicFormParamChange} />,
);
expect(wrapper.state("Gi")).toBe(10);

const input = wrapper.find("input#disk");
const event = { currentTarget: { value: "20.5" } } as React.FormEvent<HTMLInputElement>;
(input.prop("onChange") as ((e: React.FormEvent<HTMLInputElement>) => void))(event);

expect(wrapper.state("Gi")).toBe(20.5);
expect(valueChange.mock.calls[0]).toEqual([{ currentTarget: { value: "20.5Gi" } }]);
});

it("modifies the max value of the slider if the input is bigger than 100", () => {
const valueChange = jest.fn();
const handleBasicFormParamChange = jest.fn(() => valueChange);
const wrapper = shallow(
<DiskSizeParam {...defaultProps} handleBasicFormParamChange={handleBasicFormParamChange} />,
);
expect(wrapper.state("Gi")).toBe(10);

const input = wrapper.find("input#disk");
const event = { currentTarget: { value: "200" } } as React.FormEvent<HTMLInputElement>;
(input.prop("onChange") as ((e: React.FormEvent<HTMLInputElement>) => void))(event);

expect(wrapper.state("Gi")).toBe(200);
const slider = wrapper.find(Slider);
expect(slider.prop("max")).toBe(200);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import * as React from "react";
import { IBasicFormParam } from "shared/types";
import Slider from "../../../components/Slider";

export interface IDiskSizeParamProps {
id: string;
name: string;
label: string;
param: IBasicFormParam;
handleBasicFormParamChange: (
name: string,
p: IBasicFormParam,
) => (e: React.FormEvent<HTMLInputElement>) => void;
}

export interface IDiskSizeParamState {
Gi: number;
}

function toNumber(value: string) {
// Force to return a Number from a string removing any character that is not a digit
return Number(value.replace(/[^\d\.]/g, ""));
}

class DiskSizeParam extends React.Component<IDiskSizeParamProps, IDiskSizeParamState> {
public state: IDiskSizeParamState = {
Gi: toNumber(this.props.param.value) || 10,
};

// onChangeSlider is executed when the slider is dropped at one point
// at that point we update the parameter
public onChangeSlider = (values: number[]) => {
this.handleParamChange(values[0]);
};

// onUpdateSlider is executed when dragging the slider
// we just update the state here for a faster response
public onUpdateSlider = (values: number[]) => {
this.setState({ Gi: values[0] });
};

public onChangeInput = (e: React.FormEvent<HTMLInputElement>) => {
const value = toNumber(e.currentTarget.value);
this.setState({ Gi: value });
this.handleParamChange(value);
};

public render() {
const { param, label } = this.props;
return (
<div>
<label htmlFor={this.props.id}>
{label}
{param.description && (
<>
<br />
<span className="description">{param.description}</span>
</>
)}
<div className="row">
<div className="col-10">
<Slider
min={1}
max={Math.max(100, this.state.Gi)}
default={this.state.Gi}
onChange={this.onChangeSlider}
onUpdate={this.onUpdateSlider}
values={this.state.Gi}
/>
</div>
<div className="col-2">
<input
className="disk_size_input"
id={this.props.id}
onChange={this.onChangeInput}
value={this.state.Gi}
/>
<span className="margin-l-normal">Gi</span>
</div>
</div>
</label>
</div>
);
}

private handleParamChange = (value: number) => {
this.props.handleBasicFormParamChange(this.props.name, this.props.param)({
currentTarget: { value: `${value}Gi` },
} as React.FormEvent<HTMLInputElement>);
};
}

export default DiskSizeParam;
Loading

0 comments on commit 32d70fd

Please sign in to comment.