diff --git a/src/components/addSourceWizard/__tests__/__snapshots__/addSourceWizardStepOne.test.js.snap b/src/components/addSourceWizard/__tests__/__snapshots__/addSourceWizardStepOne.test.js.snap index b8576739..48fec1b0 100644 --- a/src/components/addSourceWizard/__tests__/__snapshots__/addSourceWizardStepOne.test.js.snap +++ b/src/components/addSourceWizard/__tests__/__snapshots__/addSourceWizardStepOne.test.js.snap @@ -10,6 +10,7 @@ exports[`AddSourceWizardStepOne Component should render a non-connected componen
-
+
-
+
+
+ +
-
+
+
+ +
+ vCenter Server +
diff --git a/src/components/addSourceWizard/addSourceWizardStepOne.js b/src/components/addSourceWizard/addSourceWizardStepOne.js index 90da4d37..907bc67e 100644 --- a/src/components/addSourceWizard/addSourceWizardStepOne.js +++ b/src/components/addSourceWizard/addSourceWizardStepOne.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Form as Pf3Form, Radio } from 'patternfly-react'; import { Form } from '@patternfly/react-core'; +import { Radio } from '../form/radio'; import { connect, store, reduxSelectors, reduxTypes } from '../../redux'; import { FormGroup } from '../form/formGroup'; import { FormState } from '../formState/formState'; @@ -24,33 +24,31 @@ class AddSourceWizardStepOne extends React.Component { {({ values, handleOnEvent, handleOnSubmit }) => (
- - - - Network Range - - - Satellite - - - vCenter Server - - + + + + )} diff --git a/src/components/form/__tests__/__snapshots__/radio.test.js.snap b/src/components/form/__tests__/__snapshots__/radio.test.js.snap new file mode 100644 index 00000000..0fc960dc --- /dev/null +++ b/src/components/form/__tests__/__snapshots__/radio.test.js.snap @@ -0,0 +1,113 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Radio Component should handle children as a label: children label radio 1`] = ` +
+ + +
+`; + +exports[`Radio Component should handle disabled, checked: active 1`] = ` +
+ +
+`; + +exports[`Radio Component should handle disabled, checked: checked 1`] = ` +
+ +
+`; + +exports[`Radio Component should handle disabled, checked: disabled 1`] = ` +
+ +
+`; + +exports[`Radio Component should render a basic component: basic component 1`] = ` +
+ +
+`; + +exports[`Radio Component should return an emulated onChange event: emulated event 1`] = ` +Object { + "checked": true, + "currentTarget": Object {}, + "id": "generatedid-", + "keyCode": undefined, + "name": "generatedid-", + "persist": [Function], + "target": Object {}, + "value": undefined, +} +`; diff --git a/src/components/form/__tests__/radio.test.js b/src/components/form/__tests__/radio.test.js new file mode 100644 index 00000000..130f8798 --- /dev/null +++ b/src/components/form/__tests__/radio.test.js @@ -0,0 +1,54 @@ +import React from 'react'; +import { mount, shallow } from 'enzyme'; +import { Radio as PfRadio } from '@patternfly/react-core/dist/js/components/Radio'; +import { Radio } from '../radio'; +import { helpers } from '../../../common'; + +describe('Radio Component', () => { + it('should render a basic component', () => { + const props = {}; + + const component = mount(); + expect(component.render()).toMatchSnapshot('basic component'); + }); + + it('should handle disabled, checked', () => { + const props = { + isDisabled: true + }; + + const component = shallow(); + expect(component.render()).toMatchSnapshot('disabled'); + + component.setProps({ + isDisabled: false + }); + expect(component.render()).toMatchSnapshot('active'); + + component.setProps({ + isDisabled: false, + isChecked: true + }); + + expect(component.render()).toMatchSnapshot('checked'); + }); + + it('should handle children as a label', () => { + const props = {}; + const component = mount(lorem ipsum); + expect(component.render()).toMatchSnapshot('children label radio'); + }); + + it('should return an emulated onChange event', done => { + const props = {}; + + props.onChange = event => { + expect(event).toMatchSnapshot('emulated event'); + done(); + }; + + const component = shallow(lorem ipsum); + const mockEvent = { currentTarget: {}, target: {}, persist: helpers.noop }; + component.find(PfRadio).simulate('change', true, mockEvent); + }); +}); diff --git a/src/components/form/radio.js b/src/components/form/radio.js new file mode 100644 index 00000000..b5ad64a4 --- /dev/null +++ b/src/components/form/radio.js @@ -0,0 +1,126 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Radio as PfRadio } from '@patternfly/react-core/dist/js/components/Radio'; +import { createMockEvent } from './formHelpers'; +import { helpers } from '../../common'; + +/** + * FixMe: PF Radio has an issue associated with how it handles multiple checked props. + * Logic within the component creates a potential issue that can be seen when a consumer sets + * both props independently, or sets "checked" to false but NOT "isChecked" making the checked attribute + * "undefined" and throwing the controlled/uncontrolled warning. https://bit.ly/3B1oCQB + */ +/** + * Render a radio form element. Provides restructured event data. + * + * @fires onRadioChange + * @param {object} props + * @param {string} props.ariaLabel + * @param {boolean} props.checked + * @param {React.ReactNode} props.children + * @param {string} props.className + * @param {string} props.id + * @param {*} props.isChecked + * @param {boolean} props.isDisabled + * @param {React.ReactNode} props.label + * @param {string} props.name + * @param {Function} props.onChange + * @param {*} props.value + * @returns {React.ReactNode} + */ +const Radio = ({ + ariaLabel, + checked, + children, + className, + id, + isChecked, + isDisabled, + label, + name, + onChange, + value, + ...props +}) => { + const updatedChecked = checked ?? isChecked ?? false; + const updatedName = name || helpers.generateId(); + const updatedId = id || updatedName; + + /** + * onChange event, provide restructured event. + * + * @event onRadioChange + * @param {boolean} eventChecked + * @param {object} event + */ + const onRadioChange = (eventChecked, event) => { + const mockEvent = { + ...createMockEvent(event), + id: updatedId, + name: updatedName, + value, + checked: eventChecked + }; + + onChange(mockEvent); + }; + + return ( + + ); +}; + +/** + * Prop types. + * + * @type {{onChange: Function, children: React.ReactNode, name: string, checked: boolean, className: string, + * id: string, isDisabled: boolean, label: React.ReactNode, isChecked: boolean, value: any, + * ariaLabel: string}} + */ +Radio.propTypes = { + ariaLabel: PropTypes.string, + checked: PropTypes.bool, + children: PropTypes.node, + className: PropTypes.string, + id: PropTypes.string, + isChecked: PropTypes.bool, + isDisabled: PropTypes.bool, + label: PropTypes.node, + name: PropTypes.string, + onChange: PropTypes.func, + value: PropTypes.any +}; + +/** + * Default props. + * + * @type {{onChange: Function, children: null, name: null, checked: null, className: string, id: null, + * isDisabled: boolean, label: string, isChecked: boolean, value: undefined, ariaLabel: string}} + */ +Radio.defaultProps = { + ariaLabel: 'radio input', + checked: null, + children: null, + className: '', + id: null, + isChecked: false, + isDisabled: false, + label: '', + name: null, + onChange: helpers.noop, + value: undefined +}; + +export { Radio as default, Radio }; diff --git a/src/styles/app/_patternflyOverrides.scss b/src/styles/app/_patternflyOverrides.scss index 5d7ef1e4..51ad0d10 100644 --- a/src/styles/app/_patternflyOverrides.scss +++ b/src/styles/app/_patternflyOverrides.scss @@ -8,3 +8,8 @@ .pf-c-select.pf-m-expanded input[type="checkbox"] { margin-top: inherit; } + +// rcue overrides, remove as part of clean up +.quipucords-form__radio input { + margin: inherit; +} diff --git a/tests/__snapshots__/dist.test.js.snap b/tests/__snapshots__/dist.test.js.snap index 4e6ad477..9597ff92 100644 --- a/tests/__snapshots__/dist.test.js.snap +++ b/tests/__snapshots__/dist.test.js.snap @@ -648,6 +648,8 @@ Array [ "./dist/client/static/css/44*chunk.css", "./dist/client/static/css/45*chunk*map", "./dist/client/static/css/45*chunk.css", + "./dist/client/static/css/46*chunk*map", + "./dist/client/static/css/46*chunk.css", "./dist/client/static/css/5*chunk*map", "./dist/client/static/css/5*chunk.css", "./dist/client/static/css/6*chunk*map", @@ -739,6 +741,8 @@ Array [ "./dist/client/static/js/44*chunk.js", "./dist/client/static/js/45*chunk*map", "./dist/client/static/js/45*chunk.js", + "./dist/client/static/js/46*chunk*map", + "./dist/client/static/js/46*chunk.js", "./dist/client/static/js/5*chunk*map", "./dist/client/static/js/5*chunk.js", "./dist/client/static/js/6*chunk*map",