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
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 }) => (
)}
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",