diff --git a/config/cosmos.config.js b/config/cosmos.config.js
index c9e461ba0..f72e920d9 100644
--- a/config/cosmos.config.js
+++ b/config/cosmos.config.js
@@ -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`,
diff --git a/config/cosmos.webpack.config.js b/config/cosmos.webpack.config.js
index e860838ed..80aa5cb70 100644
--- a/config/cosmos.webpack.config.js
+++ b/config/cosmos.webpack.config.js
@@ -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'
}
]
}
diff --git a/package.json b/package.json
index 175ed05ef..2ae85742c 100644
--- a/package.json
+++ b/package.json
@@ -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",
@@ -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",
@@ -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",
@@ -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",
diff --git a/sass/_components.scss b/sass/_components.scss
index 6efac55da..e10f1b1e1 100644
--- a/sass/_components.scss
+++ b/sass/_components.scss
@@ -1 +1,6 @@
@import './components/HelloWorld';
+@import './components/ButtonWithIcon';
+@import './components/CreateVmWizard';
+@import './components/Dropdown';
+@import './components/FormFactory';
+@import './components/NewVmWizard';
diff --git a/sass/_dependencies.scss b/sass/_dependencies.scss
index 1615ae137..6ae539f4c 100644
--- a/sass/_dependencies.scss
+++ b/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';
diff --git a/sass/components/_ButtonWithIcon.scss b/sass/components/_ButtonWithIcon.scss
new file mode 100644
index 000000000..99e878e1a
--- /dev/null
+++ b/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;
+}
diff --git a/sass/components/_CreateVmWizard.scss b/sass/components/_CreateVmWizard.scss
new file mode 100644
index 000000000..59c058c0c
--- /dev/null
+++ b/sass/components/_CreateVmWizard.scss
@@ -0,0 +1,3 @@
+.modal .wizard-pf-main {
+ padding-top: 10px;
+}
diff --git a/sass/components/_Dropdown.scss b/sass/components/_Dropdown.scss
new file mode 100644
index 000000000..9b2947986
--- /dev/null
+++ b/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;
+}
diff --git a/sass/components/_FormFactory.scss b/sass/components/_FormFactory.scss
new file mode 100644
index 000000000..bf4f7feba
--- /dev/null
+++ b/sass/components/_FormFactory.scss
@@ -0,0 +1,8 @@
+.modal .form-group-no-bottom {
+ margin-bottom: 2px;
+}
+
+.modal .popover {
+ max-width: 400px;
+}
+
diff --git a/sass/components/_NewVmWizard.scss b/sass/components/_NewVmWizard.scss
new file mode 100644
index 000000000..8f604d974
--- /dev/null
+++ b/sass/components/_NewVmWizard.scss
@@ -0,0 +1,5 @@
+.modal .wizard-content {
+ align-items: center;
+ display: flex;
+ justify-content: center;
+}
diff --git a/src/components/Buttons/ButtonWithIcon.js b/src/components/Buttons/ButtonWithIcon.js
new file mode 100644
index 000000000..2430fec46
--- /dev/null
+++ b/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 }) => (
+
+);
+
+ButtonWithIcon.propTypes = {
+ onClick: PropTypes.func.isRequired,
+ iconType: PropTypes.string.isRequired,
+ icon: PropTypes.string.isRequired,
+ label: PropTypes.string.isRequired
+};
diff --git a/src/components/Buttons/fixtures/ButtonWithIcon.fixture.js b/src/components/Buttons/fixtures/ButtonWithIcon.fixture.js
new file mode 100644
index 000000000..8897776d6
--- /dev/null
+++ b/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'
+ }
+};
diff --git a/src/components/Buttons/index.js b/src/components/Buttons/index.js
new file mode 100644
index 000000000..608f30f13
--- /dev/null
+++ b/src/components/Buttons/index.js
@@ -0,0 +1 @@
+export { ButtonWithIcon } from './ButtonWithIcon';
diff --git a/src/components/Buttons/tests/ButtonWithIcon.test.js b/src/components/Buttons/tests/ButtonWithIcon.test.js
new file mode 100644
index 000000000..a52848ce0
--- /dev/null
+++ b/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();
+});
diff --git a/src/components/Buttons/tests/__snapshots__/ButtonWithIcon.test.js.snap b/src/components/Buttons/tests/__snapshots__/ButtonWithIcon.test.js.snap
new file mode 100644
index 000000000..74c903c86
--- /dev/null
+++ b/src/components/Buttons/tests/__snapshots__/ButtonWithIcon.test.js.snap
@@ -0,0 +1,68 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`matches snapshot 1`] = `
+
+
+
+`;
diff --git a/src/components/Forms/Checkbox.js b/src/components/Forms/Checkbox.js
new file mode 100644
index 000000000..80fd10537
--- /dev/null
+++ b/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 }) => (
+ onChange(event.target.checked, fieldKey)}>
+ {title}
+
+);
+
+Checkbox.propTypes = {
+ fieldKey: PropTypes.string.isRequired,
+ checked: PropTypes.bool.isRequired,
+ title: PropTypes.string.isRequired,
+ onChange: PropTypes.func.isRequired
+};
diff --git a/src/components/Forms/Dropdown.js b/src/components/Forms/Dropdown.js
new file mode 100644
index 000000000..5e52c137f
--- /dev/null
+++ b/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 }) => (
+
+ onChange(v, fieldKey)}
+ >
+ {choices.map(choice => (
+
+ ))}
+
+
+);
+
+Dropdown.propTypes = {
+ fieldKey: PropTypes.string.isRequired,
+ value: PropTypes.string.isRequired,
+ choices: PropTypes.array.isRequired,
+ onChange: PropTypes.func.isRequired
+};
diff --git a/src/components/Forms/FormFactory.js b/src/components/Forms/FormFactory.js
new file mode 100644
index 000000000..fe64c6338
--- /dev/null
+++ b/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 = ;
+ break;
+ case 'dropdown':
+ child = (
+
+ );
+ break;
+ case 'checkbox':
+ child = (
+
+ );
+ break;
+ case 'text':
+ default:
+ child = ;
+ }
+ return (
+
+
+ {fields[key].type === 'checkbox' ? null : (
+
+ {fields[key].title}
+ {fields[key].help ? : undefined}
+
+ )}
+
+
+ {child}
+ {validMsg}
+
+
+ );
+ });
+ return
;
+};
+
+FormFactory.propTypes = {
+ fields: PropTypes.object.isRequired,
+ fieldsValues: PropTypes.object.isRequired,
+ onFormChange: PropTypes.func.isRequired
+};
diff --git a/src/components/Forms/Text.js b/src/components/Forms/Text.js
new file mode 100644
index 000000000..c22868af8
--- /dev/null
+++ b/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 }) => (
+ onChange(event.target.value, fieldKey)} />
+);
+
+Text.propTypes = {
+ fieldKey: PropTypes.string.isRequired,
+ value: PropTypes.string.isRequired,
+ onChange: PropTypes.func.isRequired
+};
diff --git a/src/components/Forms/TextArea.js b/src/components/Forms/TextArea.js
new file mode 100644
index 000000000..60bf80d99
--- /dev/null
+++ b/src/components/Forms/TextArea.js
@@ -0,0 +1,13 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { FormControl } from 'patternfly-react';
+
+export const TextArea = ({ fieldKey, value, onChange }) => (
+ onChange(event.target.value, fieldKey)} />
+);
+
+TextArea.propTypes = {
+ fieldKey: PropTypes.string.isRequired,
+ value: PropTypes.string.isRequired,
+ onChange: PropTypes.func.isRequired
+};
diff --git a/src/components/Forms/fixtures/Checkbox.fixture.js b/src/components/Forms/fixtures/Checkbox.fixture.js
new file mode 100644
index 000000000..e0c66b4da
--- /dev/null
+++ b/src/components/Forms/fixtures/Checkbox.fixture.js
@@ -0,0 +1,11 @@
+import { Checkbox } from '..';
+
+export default {
+ component: Checkbox,
+ props: {
+ fieldKey: 'fieldKey',
+ checked: true,
+ title: 'Checked checkbox',
+ onChange: () => {}
+ }
+};
diff --git a/src/components/Forms/fixtures/Dropdown.fixture.js b/src/components/Forms/fixtures/Dropdown.fixture.js
new file mode 100644
index 000000000..76d7b175f
--- /dev/null
+++ b/src/components/Forms/fixtures/Dropdown.fixture.js
@@ -0,0 +1,11 @@
+import { Dropdown } from '..';
+
+export default {
+ component: Dropdown,
+ props: {
+ fieldKey: 'fieldKey',
+ value: 'This is dropdown button',
+ choices: ['choice1', 'choice2'],
+ onChange: () => {}
+ }
+};
diff --git a/src/components/Forms/fixtures/FormFactory.fixture.js b/src/components/Forms/fixtures/FormFactory.fixture.js
new file mode 100644
index 000000000..b338917ad
--- /dev/null
+++ b/src/components/Forms/fixtures/FormFactory.fixture.js
@@ -0,0 +1,54 @@
+import { FormFactory } from '..';
+
+export default {
+ component: FormFactory,
+ props: {
+ fields: {
+ textField: {
+ title: 'textField'
+ },
+ requiredField: {
+ title: 'requiredField',
+ required: true
+ },
+ invisibleField: {
+ title: 'invisibleField',
+ isVisible: () => false
+ },
+ dropdownField: {
+ title: 'dropdownField',
+ type: 'dropdown',
+ default: 'default',
+ values: ['value1', 'value2']
+ },
+ textAreaField: {
+ type: 'textarea'
+ },
+ checkboxField: {
+ type: 'checkbox',
+ title: 'checkboxFieldTitle'
+ }
+ },
+ fieldsValues: {
+ textField: {
+ value: 'textField'
+ },
+ requiredField: {
+ tivaluetle: 'requiredField'
+ },
+ invisibleField: {
+ value: 'invisibleField'
+ },
+ dropdownField: {
+ value: 'dropdownField'
+ },
+ textAreaField: {
+ value: 'textarea'
+ },
+ checkboxField: {
+ value: true
+ }
+ },
+ onFormChange: () => {}
+ }
+};
diff --git a/src/components/Forms/fixtures/Text.fixture.js b/src/components/Forms/fixtures/Text.fixture.js
new file mode 100644
index 000000000..afdceffe8
--- /dev/null
+++ b/src/components/Forms/fixtures/Text.fixture.js
@@ -0,0 +1,10 @@
+import { Text } from '..';
+
+export default {
+ component: Text,
+ props: {
+ fieldKey: 'fieldKey',
+ value: 'some text',
+ onChange: () => {}
+ }
+};
diff --git a/src/components/Forms/fixtures/TextArea.fixture.js b/src/components/Forms/fixtures/TextArea.fixture.js
new file mode 100644
index 000000000..39b3a872e
--- /dev/null
+++ b/src/components/Forms/fixtures/TextArea.fixture.js
@@ -0,0 +1,10 @@
+import { TextArea } from '..';
+
+export default {
+ component: TextArea,
+ props: {
+ fieldKey: 'fieldKey',
+ value: 'some text',
+ onChange: () => {}
+ }
+};
diff --git a/src/components/Forms/index.js b/src/components/Forms/index.js
new file mode 100644
index 000000000..deae12219
--- /dev/null
+++ b/src/components/Forms/index.js
@@ -0,0 +1,5 @@
+export { TextArea } from './TextArea';
+export { Text } from './Text';
+export { Dropdown } from './Dropdown';
+export { Checkbox } from './Checkbox';
+export { FormFactory } from './FormFactory';
diff --git a/src/components/Forms/tests/Checkbox.test.js b/src/components/Forms/tests/Checkbox.test.js
new file mode 100644
index 000000000..7edcfc3a5
--- /dev/null
+++ b/src/components/Forms/tests/Checkbox.test.js
@@ -0,0 +1,12 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+import { Checkbox } from '../Checkbox';
+
+const testCheckboxControl = () => {}} title="title" />;
+
+describe('', () => {
+ it('renders correctly', () => {
+ const component = shallow(testCheckboxControl());
+ expect(component).toMatchSnapshot();
+ });
+});
diff --git a/src/components/Forms/tests/Dropdown.test.js b/src/components/Forms/tests/Dropdown.test.js
new file mode 100644
index 000000000..73ddc469a
--- /dev/null
+++ b/src/components/Forms/tests/Dropdown.test.js
@@ -0,0 +1,12 @@
+import createTestContext from '../../../cosmos/enzyme';
+import fixture from '../fixtures/Dropdown.fixture';
+
+const { mount, getWrapper } = createTestContext({ fixture });
+
+beforeEach(mount);
+
+describe('', () => {
+ it('renders correctly', () => {
+ expect(getWrapper()).toMatchSnapshot();
+ });
+});
diff --git a/src/components/Forms/tests/FormFactory.test.js b/src/components/Forms/tests/FormFactory.test.js
new file mode 100644
index 000000000..00a0bc5e3
--- /dev/null
+++ b/src/components/Forms/tests/FormFactory.test.js
@@ -0,0 +1,24 @@
+import createTestContext from '../../../cosmos/enzyme';
+import fixture from '../fixtures/FormFactory.fixture';
+
+const { mount, getWrapper } = createTestContext({ fixture });
+
+beforeEach(mount);
+
+describe('', () => {
+ it('renders correctly', () => {
+ expect(getWrapper()).toMatchSnapshot();
+ });
+ /*
+ it('required field is marked', () => {
+ expect(getWrapper().find('.required-pf')).toHaveLength(1);
+ });
+ */
+ it('not visible field is not rendered', () => {
+ expect(
+ getWrapper()
+ .find('.control-label')
+ .find(node => node.text() === 'invisibleField')
+ ).toHaveLength(0);
+ });
+});
diff --git a/src/components/Forms/tests/Text.test.js b/src/components/Forms/tests/Text.test.js
new file mode 100644
index 000000000..a085204be
--- /dev/null
+++ b/src/components/Forms/tests/Text.test.js
@@ -0,0 +1,12 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+import { Text } from '../Text';
+
+const testTextControl = () => {}} value="val" />;
+
+describe('', () => {
+ it('renders correctly', () => {
+ const component = shallow(testTextControl());
+ expect(component).toMatchSnapshot();
+ });
+});
diff --git a/src/components/Forms/tests/TextArea.test.js b/src/components/Forms/tests/TextArea.test.js
new file mode 100644
index 000000000..d6e7b1696
--- /dev/null
+++ b/src/components/Forms/tests/TextArea.test.js
@@ -0,0 +1,12 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+import { TextArea } from '../TextArea';
+
+const testTextAreaControl = () =>