Skip to content

Commit

Permalink
feat: add Range input and allow for custom prop for select and range …
Browse files Browse the repository at this point in the history
…input (#5034)

* #3047 add range and custom range input and custom prop for selects

#3047 add range and custom range input and custom prop for selects

* #3047 update FormControlSpec.js

#3047 Add tests

* #3047 update docs

* Update FormControl.js

clean up leftover commented code

* #3047 simplify custom error check

* Update FormControl.js

Re-add removed @types

* #3047 add types for custom

* Update src/FormControl.js

Co-Authored-By: Jimmy Jia <tesrin@gmail.com>

* Update src/FormControl.js

Co-Authored-By: Jason Quense <monastic.panic@gmail.com>

Co-authored-by: Tor Raswill <tor.raswill@dqc.se>
Co-authored-by: Jimmy Jia <tesrin@gmail.com>
Co-authored-by: Jason Quense <monastic.panic@gmail.com>
  • Loading branch information
4 people committed Mar 10, 2020
1 parent 8856820 commit d000274
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 2 deletions.
34 changes: 32 additions & 2 deletions src/FormControl.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import classNames from 'classnames';
import PropTypes from 'prop-types';
import all from 'prop-types-extra/lib/all';
import React, { useContext } from 'react';
import warning from 'warning';
import Feedback from './Feedback';
Expand All @@ -12,6 +13,13 @@ const propTypes = {
*/
bsPrefix: PropTypes.string,

/**
* A seperate bsPrefix used for custom controls
*
* @default 'custom'
*/
bsCustomPrefix: PropTypes.string,

/**
* The FormControl `ref` will be forwarded to the underlying input element,
* which means unless `as` is a composite component,
Expand Down Expand Up @@ -60,6 +68,18 @@ const propTypes = {
/** A callback fired when the `value` prop changes */
onChange: PropTypes.func,

/**
* Use Bootstrap's custom form elements to replace the browser defaults
* @type boolean
*/
custom: all(PropTypes.bool, ({ as, type, custom }) =>
custom === true && type !== 'range' && as !== 'select'
? Error(
'`custom` can only be set to `true` when the input type is `range`, or `select`',
)
: null,
),

/**
* The HTML input `type`, which is only relevant if `as` is `'input'` (the default).
*/
Expand All @@ -81,6 +101,7 @@ const FormControl = React.forwardRef(
(
{
bsPrefix,
bsCustomPrefix,
type,
size,
id,
Expand All @@ -89,20 +110,29 @@ const FormControl = React.forwardRef(
isInvalid,
plaintext,
readOnly,
custom,
// Need to define the default "as" during prop destructuring to be compatible with styled-components github.com/react-bootstrap/react-bootstrap/issues/3595
as: Component = 'input',
...props
},
ref,
) => {
const { controlId } = useContext(FormContext);

bsPrefix = useBootstrapPrefix(bsPrefix, 'form-control');
bsPrefix = custom
? useBootstrapPrefix(bsCustomPrefix, 'custom')
: useBootstrapPrefix(bsPrefix, 'form-control');
let classes;
if (plaintext) {
classes = { [`${bsPrefix}-plaintext`]: true };
} else if (type === 'file') {
classes = { [`${bsPrefix}-file`]: true };
} else if (type === 'range') {
classes = { [`${bsPrefix}-range`]: true };
} else if (Component === 'select' && custom) {
classes = {
[`${bsPrefix}-select`]: true,
[`${bsPrefix}-select-${size}`]: size,
};
} else {
classes = {
[bsPrefix]: true,
Expand Down
18 changes: 18 additions & 0 deletions test/FormControlSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,30 @@ describe('<FormControl>', () => {
mount(<FormControl as="select" />).assertSingle('select.form-control');
});

it('should support custom select', () => {
mount(<FormControl as="select" custom />)
.assertSingle('select.custom-select')
.assertNone('.form-control');
});

it('should support type=file', () => {
mount(<FormControl type="file" />)
.assertSingle('[type="file"].form-control-file')
.assertNone('.form-control');
});

it('should support type=range', () => {
mount(<FormControl type="range" />)
.assertSingle('[type="range"].form-control-range')
.assertNone('.form-control');
});

it('should support custom type=range', () => {
mount(<FormControl type="range" custom />)
.assertSingle('[type="range"].custom-range')
.assertNone('.form-control-range')
.assertNone('.form-control');
});
it('should support plaintext inputs', () => {
mount(<FormControl plaintext />).assertSingle(
'input.form-control-plaintext',
Expand Down
1 change: 1 addition & 0 deletions types/components/FormControl.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface FormControlProps {
disabled?: boolean;
value?: string | string[] | number;
onChange?: React.FormEventHandler<FormControlElement>;
custom?: boolean;
type?: string;
id?: string;
isValid?: boolean;
Expand Down
6 changes: 6 additions & 0 deletions www/src/examples/Form/Range.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<Form>
<Form.Group controlId="formBasicRange">
<Form.Label>Range</Form.Label>
<Form.Control type="range" />
</Form.Group>
</Form>;
6 changes: 6 additions & 0 deletions www/src/examples/Form/RangeCustom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<Form>
<Form.Group controlId="formBasicRangeCustom">
<Form.Label>Range</Form.Label>
<Form.Control type="range" custom />
</Form.Group>
</Form>;
12 changes: 12 additions & 0 deletions www/src/examples/Form/SelectCustom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Form>
<Form.Group controlId="exampleForm.SelectCustom">
<Form.Label>Custom select</Form.Label>
<Form.Control as="select" custom>
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</Form.Control>
</Form.Group>
</Form>;
22 changes: 22 additions & 0 deletions www/src/examples/Form/SelectCustomSize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Form>
<Form.Group controlId="exampleForm.SelectCustomSizeSm">
<Form.Label>Custom select Small</Form.Label>
<Form.Control as="select" size="sm" custom>
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</Form.Control>
</Form.Group>
<Form.Group controlId="exampleForm.SelectCustomSizeLg">
<Form.Label>Custom select Large</Form.Label>
<Form.Control as="select" size="lg" custom>
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</Form.Control>
</Form.Group>
</Form>;
38 changes: 38 additions & 0 deletions www/src/pages/components/forms.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ import FormInputSizes from '../../examples/Form/InputSizes';
import NoLabels from '../../examples/Form/NoLabels';
import Plaintext from '../../examples/Form/Plaintext';
import Switch from '../../examples/Form/Switch';
import Range from '../../examples/Form/Range';
import RangeCustom from '../../examples/Form/RangeCustom';
import SelectCustom from '../../examples/Form/SelectCustom';
import SelectCustomSize from '../../examples/Form/SelectCustomSize';
import FormTextControls from '../../examples/Form/TextControls';
import ValidationFormik from '../../examples/Form/ValidationFormik';
import ValidationNative from '../../examples/Form/ValidationNative';
Expand Down Expand Up @@ -85,6 +89,10 @@ export default withLayout(function FormControlsSection({ data }) {
field styling and preserve the correct margin and padding.
</p>
<ReactPlayground codeText={Plaintext} />
<LinkedHeading h="2" id="forms-range">
Range Inputs
</LinkedHeading>
<ReactPlayground codeText={Range} />
<LinkedHeading h="2" id="forms-form-check">
Checkboxes and Radios
</LinkedHeading>
Expand Down Expand Up @@ -292,6 +300,36 @@ export default withLayout(function FormControlsSection({ data }) {
<h3>Inline</h3>
<ReactPlayground codeText={CheckCustomInline} />

<LinkedHeading h="3" id="forms-custom-select">
Select
</LinkedHeading>
<p>
For the <code>select</code> form control you can pass the{' '}
<code>custom</code> prop to get custom styling of the select element.
Custom styles are limited to the <code>select</code> initial appearance
and cannot modify the <code>option</code> styling due to browser
limitations.
</p>
<ReactPlayground codeText={SelectCustom} />
<h4>Sizing</h4>
<p>
The custom <code>select</code> element supports sizing.
</p>
<ReactPlayground codeText={SelectCustomSize} />

<LinkedHeading h="3" id="forms-custom-range">
Range
</LinkedHeading>
<p>
For the <code>range</code> form control you can pass the{' '}
<code>custom</code> prop to get custom styling of the select element.
The track (the background) and thumb (the value) are both styled to
appear the same across browsers. As only IE and Firefox support
“filling” their track from the left or right of the thumb as a means to
visually indicate progress, we do not currently support it.
</p>
<ReactPlayground codeText={RangeCustom} />

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

0 comments on commit d000274

Please sign in to comment.