Skip to content
Permalink
Browse files

feat: add suppoprt for custom checkboxes and radios (#3343)

  • Loading branch information
Elad Levy authored and jquense committed Nov 9, 2018
1 parent 50f54b4 commit 97a5b2f7634872d2b75e37e3bcfbe47f470ed7d7
@@ -45,6 +45,9 @@ class FormCheck extends React.Component {
title: PropTypes.string,
label: PropTypes.node,

/** Use Bootstrap's custom form elements to replace the browser defaults */
custom: PropTypes.bool,

/** The type of checkable. */
type: PropTypes.oneOf(['radio', 'checkbox']).isRequired,

@@ -83,6 +86,7 @@ class FormCheck extends React.Component {
type,
label,
children,
custom,
...props
} = this.props;

@@ -102,14 +106,18 @@ class FormCheck extends React.Component {

return (
<FormContext.Transform
mapToValue={({ controlId }) => ({ controlId: id || controlId })}
mapToValue={({ controlId }) => ({
controlId: id || controlId,
custom,
})}
>
<div
style={style}
className={classNames(
className,
bsPrefix,
inline && `${bsPrefix}-inline`,
!custom && bsPrefix,
custom && `custom-control custom-${type}`,
inline && `${custom ? 'custom-control' : bsPrefix}-inline`,
)}
>
{children || (
@@ -51,14 +51,15 @@ function FormCheckInput({
}) {
return (
<FormContext.Consumer>
{({ controlId }) => (
{({ controlId, custom }) => (
<input
{...props}
ref={innerRef}
id={id || controlId}
className={classNames(
className,
bsPrefix,
!custom && bsPrefix,
custom && 'custom-control-input',
isValid && 'is-valid',
isInvalid && 'is-invalid',
isStatic && 'position-static',
@@ -27,12 +27,16 @@ const defaultProps = {
function FormCheckLabel({ bsPrefix, className, innerRef, htmlFor, ...props }) {
return (
<FormContext.Consumer>
{({ controlId }) => (
{({ controlId, custom }) => (
<label // eslint-disable-line jsx-a11y/label-has-associated-control
{...props}
ref={innerRef}
htmlFor={htmlFor || controlId}
className={classNames(className, bsPrefix)}
className={classNames(
className,
!custom && bsPrefix,
custom && 'custom-control-label',
)}
/>
)}
</FormContext.Consumer>
@@ -80,4 +80,20 @@ describe('<FormCheck>', () => {

expect(instance.input.tagName).to.equal('INPUT');
});

it('should support custom', () => {
const wrapper = mount(<FormCheck custom label="My label" />);

wrapper
.assertSingle('div.custom-control')
.assertSingle('div.custom-checkbox')
.assertSingle('input.custom-control-input');

wrapper.assertSingle('label.custom-control-label');
});

it('should support custom with inline', () => {
const wrapper = mount(<FormCheck custom inline label="My label" />);
wrapper.assertSingle('div.custom-control-inline');
});
});
@@ -11,7 +11,7 @@
<Form.Label>Password</Form.Label>
<Form.Control type="password" placeholder="Password" />
</Form.Group>
<Form.Group id="formBasicChecbox">
<Form.Group controlId="formBasicChecbox">
<Form.Check type="checkbox" label="Check me out" />
</Form.Group>
<Button variant="primary" type="submit">
@@ -1,7 +1,7 @@
<Form>
{['checkbox', 'radio'].map(type => (
<div key={type} className="mb-3">
<Form.Check id={`check-api-${type}`}>
<Form.Check type={type} id={`check-api-${type}`}>
<Form.Check.Input type={type} isValid />
<Form.Check.Label>{`Custom api ${type}`}</Form.Check.Label>
<Form.Control.Feedback type="valid">You did it!</Form.Control.Feedback>
@@ -0,0 +1,20 @@
<Form>
{['checkbox', 'radio'].map(type => (
<div key={`custom-${type}`} className="mb-3">
<Form.Check // prettier-ignore
custom
type={type}
id={`custom-${type}`}
label={`Check this custom ${type}`}
/>

<Form.Check
custom
disabled
type={type}
label={`disabled ${type}`}
id={`disabled-custom-${type}`}
/>
</div>
))}
</Form>;
@@ -0,0 +1,28 @@
<Form>
{['checkbox', 'radio'].map(type => (
<div key={`custom-inline-${type}`} className="mb-3">
<Form.Check
custom
inline
label="1"
type={type}
id={`custom-inline-${type}-1`}
/>
<Form.Check
custom
inline
label="2"
type={type}
id={`custom-inline-${type}-2`}
/>
<Form.Check
custom
inline
disabled
label="3 (disabled)"
type={type}
id={`custom-inline-${type}-3`}
/>
</div>
))}
</Form>;
@@ -21,6 +21,8 @@ import Horizontal from '../../examples/Form/Horizontal';
import ValidationNative from '../../examples/Form/ValidationNative';
import ValidationFormik from '../../examples/Form/ValidationFormik';
import CheckApi from '../../examples/Form/CheckApi';
import CheckCustom from '../../examples/Form/CheckCustom';
import CheckCustomInline from '../../examples/Form/CheckCustomInline';

export default withLayout(function FormControlsSection({ data }) {
return (
@@ -235,6 +237,36 @@ export default withLayout(function FormControlsSection({ data }) {
Examples
</LinkedHeading>

<LinkedHeading h="2" id="forms-custom">
Custom forms
</LinkedHeading>
<p>
For even more customization and cross browser consistency, use our
completely custom form elements to replace the browser defaults. They’re
built on top of semantic and accessible markup, so they’re solid
replacements for any default form control.
</p>
<LinkedHeading h="3" id="forms-custom-checkboxes-and-radios">
Checkboxes and radios
</LinkedHeading>
<p>
Custom checkbox and radio styles are achieved with a resourceful use of
the <code>:checked</code> selector and <code>:after</code> psuedo
elements, but are Structurally similar to the default{' '}
<code>FormCheck</code>. By default the checked and indeterminate icons
use embedded svg icons from{' '}
<a href="https://useiconic.com/open">Open Iconic</a>.
</p>

<p>
Apply Bootstrap's custom elements by adding the <code>custom</code>{' '}
prop.
</p>
<ReactPlayground codeText={CheckCustom} />

<h3>Inline</h3>
<ReactPlayground codeText={CheckCustomInline} />

<LinkedHeading h="2" id="forms-api">
API
</LinkedHeading>

0 comments on commit 97a5b2f

Please sign in to comment.
You can’t perform that action at this time.