Skip to content

Commit

Permalink
feat(CustomInput): add custom checkboxes/radios (#985)
Browse files Browse the repository at this point in the history
  • Loading branch information
tpict authored and TheSharpieOne committed Apr 30, 2018
1 parent 539d35d commit 312e729
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 0 deletions.
13 changes: 13 additions & 0 deletions docs/lib/Components/FormPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ const InputGridSizingExampleSource = require('!!raw!../examples/InputGridSizing'
import LabelHiddenExample from '../examples/LabelHidden';
const LabelHiddenExampleSource = require('!!raw!../examples/LabelHidden');

import CustomControlsExample from '../examples/CustomControls';
const CustomControlsExampleSource = require('!!raw!../examples/CustomControls');

export default class FormPage extends React.Component {
render() {
return (
Expand Down Expand Up @@ -148,6 +151,16 @@ export default class FormPage extends React.Component {
{LabelHiddenExampleSource}
</PrismCode>
</pre>

<SectionTitle>Custom Controls</SectionTitle>
<div className="docs-example">
<CustomControlsExample />
</div>
<pre>
<PrismCode className="language-jsx">
{CustomControlsExampleSource}
</PrismCode>
</pre>
</div>
);
}
Expand Down
34 changes: 34 additions & 0 deletions docs/lib/examples/CustomControls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import { CustomCheckbox, CustomRadio, Form, FormGroup, Label } from 'reactstrap';

export default class Example extends React.Component {
render() {
return (
<Form>
<FormGroup>
<Label for="exampleCheckbox">Checkboxes</Label>
<div>
<CustomCheckbox id="exampleCustomCheckbox" description="Check this custom checkbox" />
<CustomCheckbox id="exampleCustomCheckbox2" description="Or this one" />
<CustomCheckbox id="exampleCustomCheckbox3" description="But not this disabled one" disabled />
</div>
</FormGroup>
<FormGroup>
<Label for="exampleCheckbox">Radios</Label>
<div>
<CustomRadio id="exampleCustomRadio" name="customRadio" description="Select this custom radio" />
<CustomRadio id="exampleCustomRadio2" name="customRadio" description="Or this one" />
<CustomRadio id="exampleCustomRadio3" description="But not this disabled one" disabled />
</div>
</FormGroup>
<FormGroup>
<Label for="exampleCheckbox">Inline</Label>
<div>
<CustomCheckbox id="exampleCustomInline" description="An inline custom input" inline />
<CustomCheckbox id="exampleCustomInline2" description="and another one" inline />
</div>
</FormGroup>
</Form>
);
}
}
9 changes: 9 additions & 0 deletions src/CustomCheckbox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';

import CustomCheckboxOrRadio from './CustomCheckboxOrRadio';

function CustomCheckbox(props) {
return <CustomCheckboxOrRadio {...props} type="checkbox" />;
}

export default CustomCheckbox;
51 changes: 51 additions & 0 deletions src/CustomCheckboxOrRadio.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { mapToCssModules } from './utils';

const propTypes = {
className: PropTypes.string,
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
type: PropTypes.string.isRequired,
description: PropTypes.string,
inline: PropTypes.bool,
cssModule: PropTypes.object,
children: PropTypes.oneOfType(PropTypes.node, PropTypes.array, PropTypes.func)
};

function CustomCheckboxOrRadio(props) {
const {
className,
id,
description,
type,
inline,
cssModule,
children,
...attributes
} = props;

const classes = mapToCssModules(classNames(
className,
'custom-control',
`custom-${type}`,
{ 'custom-control-inline': inline }
), cssModule);

return (
<div className={classes}>
<input
{...attributes}
id={id}
type={type}
className="custom-control-input"
/>
<label className="custom-control-label" htmlFor={id}>{description}</label>
{children}
</div>
);
}

CustomCheckboxOrRadio.propTypes = propTypes;

export default CustomCheckboxOrRadio;
9 changes: 9 additions & 0 deletions src/CustomRadio.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';

import CustomCheckboxOrRadio from './CustomCheckboxOrRadio';

function CustomRadio(props) {
return <CustomCheckboxOrRadio {...props} type="radio" />;
}

export default CustomRadio;
32 changes: 32 additions & 0 deletions src/__tests__/CustomCheckbox.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import { mount } from 'enzyme';
import { CustomCheckbox } from '../';

describe('CustomCheckbox', () => {
it('should render an optional description', () => {
const checkbox = mount(<CustomCheckbox description="Yo!" />);
expect(checkbox.find('label').text()).toBe('Yo!');
});

it('should render children in the outer div', () => {
const checkbox = mount(<CustomCheckbox><h1>Yo!</h1></CustomCheckbox>);
expect(checkbox.find('h1').parent().type()).toBe('div');
});

it('should pass id to both the input and label nodes', () => {
const checkbox = mount(<CustomCheckbox id="yo" />);
expect(checkbox.find('input').prop('id')).toBe('yo');
expect(checkbox.find('label').prop('htmlFor')).toBe('yo');
});

it('should pass classNames to the outer div', () => {
const checkbox = mount(<CustomCheckbox className="yo" />);
expect(checkbox.find('.custom-control').prop('className')).toBe(
'yo custom-control custom-checkbox');
});

it('should pass all other props to the input node', () => {
const checkbox = mount(<CustomCheckbox data-testprop="yo" />);
expect(checkbox.find('input').prop('data-testprop')).toBe('yo');
});
});
32 changes: 32 additions & 0 deletions src/__tests__/CustomRadio.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import { mount } from 'enzyme';
import { CustomRadio } from '../';

describe('CustomRadio', () => {
it('should render an optional description', () => {
const radio = mount(<CustomRadio description="Yo!" />);
expect(radio.find('label').text()).toBe('Yo!');
});

it('should render children in the outer div', () => {
const radio = mount(<CustomRadio><h1>Yo!</h1></CustomRadio>);
expect(radio.find('h1').parent().type()).toBe('div');
});

it('should pass id to both the input and label nodes', () => {
const radio = mount(<CustomRadio id="yo" />);
expect(radio.find('input').prop('id')).toBe('yo');
expect(radio.find('label').prop('htmlFor')).toBe('yo');
});

it('should pass classNames to the outer div', () => {
const radio = mount(<CustomRadio className="yo" />);
expect(radio.find('.custom-control').prop('className')).toBe(
'yo custom-control custom-radio');
});

it('should pass all other props to the input node', () => {
const radio = mount(<CustomRadio data-testprop="yo" />);
expect(radio.find('input').prop('data-testprop')).toBe('yo');
});
});
4 changes: 4 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ import CarouselCaption from './CarouselCaption';
import CardSubtitle from './CardSubtitle';
import CardText from './CardText';
import CardTitle from './CardTitle';
import CustomCheckbox from './CustomCheckbox';
import CustomRadio from './CustomRadio';
import PopperContent from './PopperContent';
import PopperTargetHelper from './PopperTargetHelper';
import Popover from './Popover';
Expand Down Expand Up @@ -155,6 +157,8 @@ export {
InputGroupButtonDropdown,
InputGroupText,
Label,
CustomCheckbox,
CustomRadio,
Media,
Pagination,
PaginationItem,
Expand Down

0 comments on commit 312e729

Please sign in to comment.