Skip to content
This repository has been archived by the owner on Apr 28, 2020. It is now read-only.

Commit

Permalink
Implement Create VM Wizard component (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
rawagner authored and mareklibra committed Sep 26, 2018
1 parent 24bf17d commit 3afe91c
Show file tree
Hide file tree
Showing 59 changed files with 2,994 additions and 9 deletions.
7 changes: 6 additions & 1 deletion config/cosmos.config.js
Expand Up @@ -9,7 +9,12 @@ module.exports = {
fileMatch: '**/*.fixture.js',

// additional modules to load along with every component
globalImports: ['@babel/polyfill'],
globalImports: [
'@babel/polyfill',
'patternfly-react/dist/css/patternfly-react.css',
'patternfly/dist/css/patternfly.css',
'patternfly/dist/css/patternfly-additions.css'
],

// path to Cosmos proxies
proxiesPath: `${paths.src}/cosmos/proxies.js`,
Expand Down
8 changes: 8 additions & 0 deletions config/cosmos.webpack.config.js
Expand Up @@ -17,6 +17,14 @@ module.exports = {
include: paths.src,
loader: 'babel-loader',
options: babelOptions
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(eot|svg|ttf|woff|woff2|gif|jpg|png)$/,
loader: 'url-loader'
}
]
}
Expand Down
9 changes: 7 additions & 2 deletions package.json
Expand Up @@ -24,7 +24,7 @@
"test:watch": "yarn test --watch",
"prebuild": "yarn test --ci && shx rm -rf dist",
"build": "yarn build:js && yarn build:sass",
"build:js": "babel src --config-file ./config/babel.config.js --out-dir dist/js --only 'src/components/**/*.js,src/index.js' --ignore '**/tests/**,**/fixtures/**'",
"build:js": "babel src --config-file ./config/babel.config.js --out-dir dist/js --only 'src/**/*.js' --ignore 'src/jest,src/cosmos,**/tests/**,**/fixtures/**'",
"build:sass": "shx cp -r sass dist",
"cosmos": "cosmos --config config/cosmos.config.js",
"cosmos:export": "shx rm -rf cosmos && NODE_ENV=production cosmos-export --config config/cosmos.config.js",
Expand All @@ -49,6 +49,7 @@
"babel-loader": "8.x",
"chalk": "2.x",
"coveralls": "3.x",
"css-loader": "1.0.x",
"enzyme": "3.x",
"enzyme-adapter-react-16": "1.x",
"enzyme-to-json": "3.x",
Expand All @@ -68,6 +69,7 @@
"glob": "7.x",
"html-webpack-plugin": "3.x",
"jest": "23.x",
"lodash": "4.17.x",
"patternfly-react": "2.x",
"prettier": "1.x",
"prop-types": "15.x",
Expand All @@ -77,10 +79,13 @@
"react-dom": "16.5.x",
"require-all": "3.x",
"shx": "0.x",
"style-loader": "0.23.x",
"stylelint": "9.x",
"stylelint-config-standard": "18.x",
"stylelint-scss": "3.x",
"webpack": "4.x"
"url-loader": "1.1.x",
"webpack": "4.x",
"js-yaml": "3.12.x"
},
"engines": {
"node": ">=8.6.x",
Expand Down
5 changes: 5 additions & 0 deletions sass/_components.scss
@@ -1 +1,6 @@
@import './components/HelloWorld';
@import './components/ButtonWithIcon';
@import './components/CreateVmWizard';
@import './components/Dropdown';
@import './components/FormFactory';
@import './components/NewVmWizard';
4 changes: 2 additions & 2 deletions sass/_dependencies.scss
@@ -1,2 +1,2 @@
@import 'patternfly/dist/sass/patternfly';
@import 'patternfly-react/dist/sass/patternfly-react';
@import '~patternfly/dist/sass/patternfly';
@import '~patternfly-react/dist/sass/patternfly-react';
19 changes: 19 additions & 0 deletions sass/components/_ButtonWithIcon.scss
@@ -0,0 +1,19 @@
.modal .btn-with-icon {
text-align: center;
display: inline-block;
padding: 10px;
margin: 50px;
border: 2px solid #f5f5f5;
height: 200px;
width: 200px;
white-space: pre-line;
}

.modal .btn-with-icon:hover {
border: 2px solid;
text-decoration: none;
}

.modal .label-column {
margin-top: 15px;
}
3 changes: 3 additions & 0 deletions sass/components/_CreateVmWizard.scss
@@ -0,0 +1,3 @@
.modal .wizard-pf-main {
padding-top: 10px;
}
18 changes: 18 additions & 0 deletions sass/components/_Dropdown.scss
@@ -0,0 +1,18 @@
.modal .form-dropdown {
text-align: left;
}

.modal .form-dropdown .caret {
position: absolute;
right: 5px;
top: 5px;
}

.modal .dropdown-menu {
width: 100%;
display: none;
}

.modal .open > .dropdown-menu {
display: block;
}
8 changes: 8 additions & 0 deletions sass/components/_FormFactory.scss
@@ -0,0 +1,8 @@
.modal .form-group-no-bottom {
margin-bottom: 2px;
}

.modal .popover {
max-width: 400px;
}

5 changes: 5 additions & 0 deletions sass/components/_NewVmWizard.scss
@@ -0,0 +1,5 @@
.modal .wizard-content {
align-items: center;
display: flex;
justify-content: center;
}
21 changes: 21 additions & 0 deletions src/components/Buttons/ButtonWithIcon.js
@@ -0,0 +1,21 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Icon, Col } from 'patternfly-react';

export const ButtonWithIcon = ({ onClick, iconType, icon, label }) => (
<Button onClick={onClick} bsClass="btn btn-link btn-with-icon">
<Col md={12}>
<Icon type={iconType} name={icon} className="fa-5x" />
</Col>
<Col md={12} className="label-column">
<span className="lead">{label}</span>
</Col>
</Button>
);

ButtonWithIcon.propTypes = {
onClick: PropTypes.func.isRequired,
iconType: PropTypes.string.isRequired,
icon: PropTypes.string.isRequired,
label: PropTypes.string.isRequired
};
11 changes: 11 additions & 0 deletions src/components/Buttons/fixtures/ButtonWithIcon.fixture.js
@@ -0,0 +1,11 @@
import { ButtonWithIcon } from '..';

export default {
component: ButtonWithIcon,
props: {
onClick: () => {},
iconType: 'pf',
icon: 'virtual-machine',
label: 'Button With Icon'
}
};
1 change: 1 addition & 0 deletions src/components/Buttons/index.js
@@ -0,0 +1 @@
export { ButtonWithIcon } from './ButtonWithIcon';
14 changes: 14 additions & 0 deletions src/components/Buttons/tests/ButtonWithIcon.test.js
@@ -0,0 +1,14 @@
import createTestContext from '../../../cosmos/enzyme';
import fixture from '../fixtures/ButtonWithIcon.fixture';

const { mount, getWrapper } = createTestContext({ fixture });

beforeEach(mount);

test('renders Button With Icon', () => {
expect(getWrapper().text()).toEqual('Button With Icon');
});

test('matches snapshot', () => {
expect(getWrapper()).toMatchSnapshot();
});
@@ -0,0 +1,68 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`matches snapshot 1`] = `
<ButtonWithIcon
icon="virtual-machine"
iconType="pf"
label="Button With Icon"
onClick={[MockFunction]}
>
<Button
active={false}
block={false}
bsClass="btn btn-link btn-with-icon"
bsStyle="default"
disabled={false}
onClick={[MockFunction]}
>
<button
className="btn btn-link btn-with-icon btn btn-link btn-with-icon-default"
disabled={false}
onClick={[MockFunction]}
type="button"
>
<Col
bsClass="col"
componentClass="div"
md={12}
>
<div
className="col-md-12"
>
<Icon
className="fa-5x"
name="virtual-machine"
type="pf"
>
<PatternflyIcon
className="fa-5x"
name="virtual-machine"
>
<span
aria-hidden="true"
className="pficon pficon-virtual-machine fa-5x"
/>
</PatternflyIcon>
</Icon>
</div>
</Col>
<Col
bsClass="col"
className="label-column"
componentClass="div"
md={12}
>
<div
className="label-column col-md-12"
>
<span
className="lead"
>
Button With Icon
</span>
</div>
</Col>
</button>
</Button>
</ButtonWithIcon>
`;
16 changes: 16 additions & 0 deletions src/components/Forms/Checkbox.js
@@ -0,0 +1,16 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Checkbox as PfCheckbox } from 'patternfly-react';

export const Checkbox = ({ fieldKey, checked, title, onChange }) => (
<PfCheckbox checked={checked} onChange={event => onChange(event.target.checked, fieldKey)}>
{title}
</PfCheckbox>
);

Checkbox.propTypes = {
fieldKey: PropTypes.string.isRequired,
checked: PropTypes.bool.isRequired,
title: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired
};
28 changes: 28 additions & 0 deletions src/components/Forms/Dropdown.js
@@ -0,0 +1,28 @@
import React from 'react';
import PropTypes from 'prop-types';
import { ButtonGroup, DropdownButton, MenuItem } from 'patternfly-react';

export const Dropdown = ({ fieldKey, value, choices, onChange }) => (
<ButtonGroup justified>
<DropdownButton
id={`dropdown-${fieldKey}`}
bsStyle="default"
className="form-dropdown"
title={value}
onSelect={v => onChange(v, fieldKey)}
>
{choices.map(choice => (
<MenuItem key={choice} eventKey={choice}>
{choice}
</MenuItem>
))}
</DropdownButton>
</ButtonGroup>
);

Dropdown.propTypes = {
fieldKey: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
choices: PropTypes.array.isRequired,
onChange: PropTypes.func.isRequired
};
69 changes: 69 additions & 0 deletions src/components/Forms/FormFactory.js
@@ -0,0 +1,69 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FormGroup, Col, ControlLabel, HelpBlock, Form, FieldLevelHelp } from 'patternfly-react';
import { get, has } from 'lodash';
import { TextArea, Dropdown, Checkbox, Text } from '.';

export const FormFactory = ({ fields, fieldsValues, onFormChange }) => {
const formGroups = Object.keys(fields)
.filter(key => !fields[key].isVisible || fields[key].isVisible(fieldsValues))
.map(key => {
let child;
const validMsg = get(fieldsValues[key], 'validMsg');
switch (fields[key].type) {
case 'textarea':
child = <TextArea fieldKey={key} value={get(fieldsValues[key], 'value', '')} onChange={onFormChange} />;
break;
case 'dropdown':
child = (
<Dropdown
fieldKey={key}
value={has(fieldsValues[key], 'value') ? fieldsValues[key].value : fields[key].default}
choices={typeof fields[key].values === 'function' ? fields[key].values() : fields[key].values}
onChange={onFormChange}
/>
);
break;
case 'checkbox':
child = (
<Checkbox
fieldKey={key}
checked={get(fieldsValues[key], 'value', false)}
title={fields[key].title}
onChange={onFormChange}
/>
);
break;
case 'text':
default:
child = <Text fieldKey={key} value={get(fieldsValues[key], 'value', '')} onChange={onFormChange} />;
}
return (
<FormGroup
key={key}
validationState={validMsg ? 'error' : null}
className={fields[key].noBottom ? 'form-group-no-bottom' : undefined}
>
<Col sm={3} className="text-right">
{fields[key].type === 'checkbox' ? null : (
<React.Fragment>
<ControlLabel className={fields[key].required ? 'required-pf' : null}>{fields[key].title}</ControlLabel>
{fields[key].help ? <FieldLevelHelp placement="right" content={fields[key].help()} /> : undefined}
</React.Fragment>
)}
</Col>
<Col sm={5}>
{child}
<HelpBlock>{validMsg}</HelpBlock>
</Col>
</FormGroup>
);
});
return <Form horizontal>{formGroups}</Form>;
};

FormFactory.propTypes = {
fields: PropTypes.object.isRequired,
fieldsValues: PropTypes.object.isRequired,
onFormChange: PropTypes.func.isRequired
};
13 changes: 13 additions & 0 deletions src/components/Forms/Text.js
@@ -0,0 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FormControl } from 'patternfly-react';

export const Text = ({ fieldKey, value, onChange }) => (
<FormControl type="text" value={value} onChange={event => onChange(event.target.value, fieldKey)} />
);

Text.propTypes = {
fieldKey: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired
};

0 comments on commit 3afe91c

Please sign in to comment.