Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BD-46] feat: add ability to use unlabeled Radio and Checkbox components in respective Form sets #1988

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/Form/FormCheckbox.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,19 @@ import FormControlFeedback from './FormControlFeedback';

const CheckboxControl = React.forwardRef(
({ isIndeterminate, ...props }, ref) => {
const { getCheckboxControlProps, hasCheckboxSetProvider } = useCheckboxSetContext();
const defaultRef = React.useRef();
const resolvedRef = ref || defaultRef;
const { getControlProps } = useFormGroupContext();
const checkboxProps = getControlProps({
let checkboxProps = getControlProps({
...props,
className: classNames('pgn__form-checkbox-input', props.className),
});

if (hasCheckboxSetProvider) {
checkboxProps = getCheckboxControlProps(checkboxProps);
}

React.useEffect(() => {
// this if(resolvedRef.current) prevents console errors in testing
if (resolvedRef.current) {
Expand Down
9 changes: 8 additions & 1 deletion src/Form/FormRadio.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,16 @@ import FormControlFeedback from './FormControlFeedback';

const RadioControl = React.forwardRef((props, ref) => {
const { getControlProps } = useFormGroupContext();
const radioProps = getControlProps({
const { getRadioControlProps, hasRadioSetProvider } = useRadioSetContext();
let radioProps = getControlProps({
...props,
className: classNames('pgn__form-radio-input', props.className),
});

if (hasRadioSetProvider) {
radioProps = getRadioControlProps(radioProps);
}

return (
<input {...radioProps} type="radio" ref={ref} />
);
Expand Down Expand Up @@ -95,4 +101,5 @@ FormRadio.defaultProps = {
isValid: false,
};

export { RadioControl };
export default FormRadio;
2 changes: 2 additions & 0 deletions src/Form/FormRadioSetContext.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const identityFn = props => props;

const FormRadioSetContext = React.createContext({
getRadioControlProps: identityFn,
hasRadioSetProvider: false,
});

const useRadioSetContext = () => useContext(FormRadioSetContext);
Expand Down Expand Up @@ -40,6 +41,7 @@ function FormRadioSetContextProvider({
onBlur,
onFocus,
onChange,
hasRadioSetProvider: true,
};
return (
<FormRadioSetContext.Provider value={contextValue}>
Expand Down
16 changes: 16 additions & 0 deletions src/Form/form-radio.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,22 @@ underlying `input` node. See MDN for documentation on available
}
```
## Unlabeled Control
```jsx live
() => {
const [value, setValue] = useState('green');
const handleChange = e => setValue(e.target.value);

return (
<Form.RadioSet name="colors-unlabeled" onChange={handleChange} value={value}>
<RadioControl value="red" />
<RadioControl value="green" />
</Form.RadioSet>
)
}
```
## Uncontrolled Usage
```jsx live
Expand Down
3 changes: 2 additions & 1 deletion src/Form/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import FormGroup from './FormGroup';
import FormControlFeedback from './FormControlFeedback';
import FormText from './FormText';
import FormControlDecoratorGroup from './FormControlDecoratorGroup';
import FormRadio from './FormRadio';
import FormRadio, { RadioControl } from './FormRadio';
import FormRadioSet from './FormRadioSet';
import FormRadioSetContext from './FormRadioSetContext';
import FormAutosuggest from './FormAutosuggest';
Expand Down Expand Up @@ -48,6 +48,7 @@ export {
FormControlFeedback,
FormText,
CheckboxControl,
RadioControl,
SwitchControl,
FormSwitchSet,
useCheckboxSetValues,
Expand Down
8 changes: 4 additions & 4 deletions src/SelectableBox/tests/SelectableBox.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { mount } from 'enzyme';
import renderer from 'react-test-renderer';

import SelectableBox from '..';
import { Form } from '../..';
import { RadioControl, CheckboxControl } from '../../Form';

const checkboxType = 'checkbox';
const checkboxText = 'SelectableCheckbox';
Expand All @@ -29,11 +29,11 @@ describe('<SelectableBox />', () => {
});
it('correct render when type prop is changed', () => {
const boxWrapper = mount(<SelectableRadio />);
expect(boxWrapper.find(Form.Radio).length).toBeGreaterThan(0);
expect(boxWrapper.find(RadioControl).length).toBeGreaterThan(0);
boxWrapper.setProps({ type: 'radio' });
expect(boxWrapper.find(Form.Radio).length).toBeGreaterThan(0);
expect(boxWrapper.find(RadioControl).length).toBeGreaterThan(0);
boxWrapper.setProps({ type: 'checkbox' });
expect(boxWrapper.find(Form.Checkbox).length).toBeGreaterThan(0);
expect(boxWrapper.find(CheckboxControl).length).toBeGreaterThan(0);
});
it('renders with radio input type if neither checkbox nor radio is passed', () => {
// Mock the `console.error` is intentional because an invalid `type` prop
Expand Down
27 changes: 8 additions & 19 deletions src/SelectableBox/tests/__snapshots__/SelectableBox.test.jsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,14 @@ exports[`<SelectableBox /> correct rendering renders without props 1`] = `
role="button"
tabIndex={0}
>
<div
className="pgn__form-radio"
>
<input
checked={false}
className="pgn__form-radio-input"
hidden={true}
id="form-field1"
onChange={[Function]}
tabIndex={-1}
type="radio"
/>
<div>
<label
className="pgn__form-label"
htmlFor="form-field1"
/>
</div>
</div>
<input
checked={false}
className="pgn__form-radio-input"
hidden={true}
onChange={[Function]}
tabIndex={-1}
type="radio"
/>
SelectableBox
</div>
`;
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,16 @@ exports[`<SelectableBox.Set /> correct rendering renders without props 1`] = `
role="button"
tabIndex={0}
>
<div
className="pgn__form-radio"
>
<input
className="pgn__form-radio-input"
defaultChecked={false}
hidden={true}
id="form-field1"
name="testName"
onChange={[Function]}
tabIndex={-1}
type="radio"
value={1}
/>
<div>
<label
className="pgn__form-label"
htmlFor="form-field1"
/>
</div>
</div>
<input
className="pgn__form-radio-input"
defaultChecked={false}
hidden={true}
name="testName"
onChange={[Function]}
tabIndex={-1}
type="radio"
value={1}
/>
SelectableRadio1
</div>
<div
Expand All @@ -44,27 +33,16 @@ exports[`<SelectableBox.Set /> correct rendering renders without props 1`] = `
role="button"
tabIndex={0}
>
<div
className="pgn__form-radio"
>
<input
className="pgn__form-radio-input"
defaultChecked={false}
hidden={true}
id="form-field2"
name="testName"
onChange={[Function]}
tabIndex={-1}
type="radio"
value={2}
/>
<div>
<label
className="pgn__form-label"
htmlFor="form-field2"
/>
</div>
</div>
<input
className="pgn__form-radio-input"
defaultChecked={false}
hidden={true}
name="testName"
onChange={[Function]}
tabIndex={-1}
type="radio"
value={2}
/>
SelectableRadio2
</div>
<div
Expand All @@ -75,27 +53,16 @@ exports[`<SelectableBox.Set /> correct rendering renders without props 1`] = `
role="button"
tabIndex={0}
>
<div
className="pgn__form-radio"
>
<input
className="pgn__form-radio-input"
defaultChecked={false}
hidden={true}
id="form-field3"
name="testName"
onChange={[Function]}
tabIndex={-1}
type="radio"
value={3}
/>
<div>
<label
className="pgn__form-label"
htmlFor="form-field3"
/>
</div>
</div>
<input
className="pgn__form-radio-input"
defaultChecked={false}
hidden={true}
name="testName"
onChange={[Function]}
tabIndex={-1}
type="radio"
value={3}
/>
SelectableRadio3
</div>
</div>
Expand Down
10 changes: 5 additions & 5 deletions src/SelectableBox/tests/utils.test.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Form } from '../..';
import Form, { CheckboxControl, RadioControl } from '../../Form';
import { getInputType } from '../utils';

describe('utils', () => {
it('getInputType returns correct type', () => {
expect(getInputType('SelectableBox', undefined)).toEqual(Form.Radio);
expect(getInputType('SelectableBox', 'wrongtype')).toEqual(Form.Radio);
expect(getInputType('SelectableBox', 'radio')).toEqual(Form.Radio);
expect(getInputType('SelectableBox', 'checkbox')).toEqual(Form.Checkbox);
expect(getInputType('SelectableBox', undefined)).toEqual(RadioControl);
expect(getInputType('SelectableBox', 'wrongtype')).toEqual(RadioControl);
expect(getInputType('SelectableBox', 'radio')).toEqual(RadioControl);
expect(getInputType('SelectableBox', 'checkbox')).toEqual(CheckboxControl);
expect(getInputType('SelectableBoxSet', undefined)).toEqual(Form.RadioSet);
expect(getInputType('SelectableBoxSet', 'wrongtype')).toEqual(Form.RadioSet);
expect(getInputType('SelectableBoxSet', 'radio')).toEqual(Form.RadioSet);
Expand Down
8 changes: 4 additions & 4 deletions src/SelectableBox/utils.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import Form from '../Form';
import Form, { CheckboxControl, RadioControl } from '../Form';

// eslint-disable-next-line import/prefer-default-export,consistent-return
export const getInputType = (component, type) => {
if (component === 'SelectableBox') {
switch (type) {
case 'radio':
return Form.Radio;
return RadioControl;
case 'checkbox':
return Form.Checkbox;
return CheckboxControl;
default:
return Form.Radio;
return RadioControl;
}
} else if (component === 'SelectableBoxSet') {
switch (type) {
Expand Down
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export { default as Fade } from './Fade';
export { default as Fieldset } from './Fieldset';
export {
default as Form,
RadioControl,
CheckboxControl,
SwitchControl,
FormSwitchSet,
Expand Down