Skip to content

Commit

Permalink
feat: add ability to use unlabeled Radio and Checkbox components in r…
Browse files Browse the repository at this point in the history
…espective Form sets (#1988)

* feat: add ability to use unlabeled Radio and Checkbox components in respective Form sets
* docs: add unlabeled control for `RadioControl` component
  • Loading branch information
viktorrusakov authored Jun 2, 2023
1 parent f95ddb4 commit 309189d
Show file tree
Hide file tree
Showing 11 changed files with 87 additions and 98 deletions.
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

0 comments on commit 309189d

Please sign in to comment.