);
diff --git a/src/Input.js b/src/Input.js
index 51629b6cc..4d9d2bc9b 100644
--- a/src/Input.js
+++ b/src/Input.js
@@ -69,6 +69,7 @@ class Input extends React.Component {
const fileInput = type === 'file';
const textareaInput = type === 'textarea';
const selectInput = type === 'select';
+ const rangeInput = type === 'range';
let Tag = tag || (selectInput || textareaInput ? type : 'input');
let formControlClass = 'form-control';
@@ -78,6 +79,8 @@ class Input extends React.Component {
Tag = tag || 'input';
} else if (fileInput) {
formControlClass = `${formControlClass}-file`;
+ } else if (rangeInput) {
+ formControlClass = `${formControlClass}-range`;
} else if (checkInput) {
if (addon) {
formControlClass = null;
diff --git a/src/Label.js b/src/Label.js
index 9af0fb297..6891657df 100644
--- a/src/Label.js
+++ b/src/Label.js
@@ -8,6 +8,7 @@ const colWidths = ['xs', 'sm', 'md', 'lg', 'xl'];
const stringOrNumberProp = PropTypes.oneOfType([PropTypes.number, PropTypes.string]);
const columnProps = PropTypes.oneOfType([
+ PropTypes.bool,
PropTypes.string,
PropTypes.number,
PropTypes.shape({
diff --git a/src/Modal.js b/src/Modal.js
index d07ca90a6..ccd19f9b8 100644
--- a/src/Modal.js
+++ b/src/Modal.js
@@ -153,7 +153,7 @@ class Modal extends React.Component {
if (this._element) {
this.destroy();
- if (this.props.isOpen) {
+ if (this.props.isOpen || this.state.isOpen) {
this.close();
}
}
diff --git a/src/Progress.js b/src/Progress.js
index 1cf31b2d9..098393232 100644
--- a/src/Progress.js
+++ b/src/Progress.js
@@ -22,12 +22,14 @@ const propTypes = {
className: PropTypes.string,
barClassName: PropTypes.string,
cssModule: PropTypes.object,
+ style: PropTypes.object,
};
const defaultProps = {
tag: 'div',
value: 0,
max: 100,
+ style: {},
};
const Progress = (props) => {
@@ -44,6 +46,7 @@ const Progress = (props) => {
bar,
multi,
tag: Tag,
+ style,
...attributes
} = props;
@@ -65,7 +68,10 @@ const Progress = (props) => {
const ProgressBar = multi ? children : (
isInDOMSubtree(element, subTreeRoot));
+ return subtreeRoots && subtreeRoots.length && subtreeRoots.filter(subTreeRoot=> isInDOMSubtree(element, subTreeRoot))[0];
}
class TooltipPopoverWrapper extends React.Component {
diff --git a/src/__tests__/Carousel.spec.js b/src/__tests__/Carousel.spec.js
index e3c637e29..6ea8a46d6 100644
--- a/src/__tests__/Carousel.spec.js
+++ b/src/__tests__/Carousel.spec.js
@@ -31,8 +31,8 @@ describe('Carousel', () => {
describe('items', () => {
it('should render custom tag', () => {
- const wrapper = mount(
);
- expect(wrapper.find('image').length).toBe(1);
+ const wrapper = mount(
);
+ expect(wrapper.find('img').length).toBe(1);
});
it('should render an image if one is passed in', () => {
diff --git a/src/__tests__/CustomInput.spec.js b/src/__tests__/CustomInput.spec.js
index 90f3b744a..2f92cd5e3 100644
--- a/src/__tests__/CustomInput.spec.js
+++ b/src/__tests__/CustomInput.spec.js
@@ -5,17 +5,17 @@ import { CustomInput } from '../';
describe('Custom Inputs', () => {
describe('CustomCheckbox', () => {
it('should render an optional label', () => {
- const checkbox = mount(
);
+ const checkbox = mount(
);
expect(checkbox.find('label').text()).toBe('Yo!');
});
it('should render children in the outer div', () => {
- const checkbox = shallow(
);
+ const checkbox = shallow(
);
expect(checkbox.type()).toBe('div');
});
it('should render an input with the type of checkbox', () => {
- const checkbox = mount(
);
+ const checkbox = mount(
);
expect(checkbox.find('input').prop('type')).toBe('checkbox');
});
@@ -32,28 +32,28 @@ describe('Custom Inputs', () => {
});
it('should pass classNames to the outer div', () => {
- const checkbox = mount(
);
+ const checkbox = mount(
);
expect(checkbox.find('.custom-control').prop('className').indexOf('yo') > -1).toBeTruthy();
});
it('should add class is-invalid when invalid is true', () => {
- const checkbox = mount(
);
+ const checkbox = mount(
);
expect(checkbox.find('input').prop('className').indexOf('is-invalid') > -1).toBeTruthy();
});
it('should add class is-valid when valid is true', () => {
- const checkbox = mount(
);
+ const checkbox = mount(
);
expect(checkbox.find('input').prop('className').indexOf('is-valid') > -1).toBeTruthy();
});
it('should pass all other props to the input node', () => {
- const checkbox = mount(
);
+ const checkbox = mount(
);
expect(checkbox.find('input').prop('data-testprop')).toBe('yo');
});
it('should reference innerRef to the input node', () => {
const ref = React.createRef();
- mount(
);
+ mount(
);
expect(ref.current).not.toBeNull();
expect(ref.current).toBeInstanceOf(HTMLInputElement);
});
@@ -61,27 +61,27 @@ describe('Custom Inputs', () => {
describe('CustomRadio', () => {
it('should render an optional label', () => {
- const radio = mount(
);
+ const radio = mount(
);
expect(radio.find('label').text()).toBe('Yo!');
});
it('should render children in the outer div', () => {
- const radio = shallow(
);
+ const radio = shallow(
);
expect(radio.type()).toBe('div');
});
it('should render an input with the type of radio', () => {
- const radio = mount(
);
+ const radio = mount(
);
expect(radio.find('input').prop('type')).toBe('radio');
});
it('should add class is-invalid when invalid is true', () => {
- const radio = mount(
);
+ const radio = mount(
);
expect(radio.find('input').prop('className').indexOf('is-invalid') > -1).toBeTruthy();
});
it('should add class is-valid when valid is true', () => {
- const radio = mount(
);
+ const radio = mount(
);
expect(radio.find('input').prop('className').indexOf('is-valid') > -1).toBeTruthy();
});
@@ -98,18 +98,18 @@ describe('Custom Inputs', () => {
});
it('should pass classNames to the outer div', () => {
- const radio = mount(
);
+ const radio = mount(
);
expect(radio.find('.custom-control').prop('className').indexOf('yo') > -1).toBeTruthy();
});
it('should pass all other props to the input node', () => {
- const radio = mount(
);
+ const radio = mount(
);
expect(radio.find('input').prop('data-testprop')).toBe('yo');
});
it('should reference innerRef to the input node', () => {
const ref = React.createRef();
- mount(
);
+ mount(
);
expect(ref.current).not.toBeNull();
expect(ref.current).toBeInstanceOf(HTMLInputElement);
});
@@ -117,17 +117,17 @@ describe('Custom Inputs', () => {
describe('CustomSwitch', () => {
it('should render an optional label', () => {
- const checkbox = mount(
);
+ const checkbox = mount(
);
expect(checkbox.find('label').text()).toBe('Yo!');
});
it('should render children in the outer div', () => {
- const checkbox = shallow(
);
+ const checkbox = shallow(
);
expect(checkbox.type()).toBe('div');
});
it('should render an input with the type of checkbox', () => {
- const checkbox = mount(
);
+ const checkbox = mount(
);
expect(checkbox.find('input').prop('type')).toBe('checkbox');
});
@@ -144,28 +144,28 @@ describe('Custom Inputs', () => {
});
it('should pass classNames to the outer div', () => {
- const checkbox = mount(
);
+ const checkbox = mount(
);
expect(checkbox.find('.custom-control').prop('className').indexOf('yo') > -1).toBeTruthy();
});
it('should add class is-invalid when invalid is true', () => {
- const checkbox = mount(
);
+ const checkbox = mount(
);
expect(checkbox.find('input').prop('className').indexOf('is-invalid') > -1).toBeTruthy();
});
it('should add class is-valid when valid is true', () => {
- const checkbox = mount(
);
+ const checkbox = mount(
);
expect(checkbox.find('input').prop('className').indexOf('is-valid') > -1).toBeTruthy();
});
it('should pass all other props to the input node', () => {
- const checkbox = mount(
);
+ const checkbox = mount(
);
expect(checkbox.find('input').prop('data-testprop')).toBe('yo');
});
it('should reference innerRef to the input node', () => {
const ref = React.createRef();
- mount(
);
+ mount(
);
expect(ref.current).not.toBeNull();
expect(ref.current).toBeInstanceOf(HTMLInputElement);
});
@@ -173,43 +173,43 @@ describe('Custom Inputs', () => {
describe('CustomSelect', () => {
it('should render children in the outer div', () => {
- const select = shallow(
);
+ const select = shallow(
);
expect(select.type()).toBe('select');
});
it('should add class is-invalid when invalid is true', () => {
- const select = mount(
);
+ const select = mount(
);
expect(select.find('select').prop('className').indexOf('is-invalid') > -1).toBeTruthy();
});
it('should add class is-valid when valid is true', () => {
- const select = mount(
);
+ const select = mount(
);
expect(select.find('select').prop('className').indexOf('is-valid') > -1).toBeTruthy();
});
it('should add the size class when bsSize is provided', () => {
- const select = mount(
);
+ const select = mount(
);
expect(select.find('select').prop('className').indexOf('custom-select-lg') > -1).toBeTruthy();
});
it('should pass classNames to the outer div', () => {
- const select = mount(
);
+ const select = mount(
);
expect(select.find('.custom-select').prop('className').indexOf('yo') > -1).toBeTruthy();
});
it('should pass all other props to the input node', () => {
- const select = mount(
);
+ const select = mount(
);
expect(select.find('select').prop('data-testprop')).toBe('yo');
});
it('should remove type prop from the input node', () => {
- const select = mount(
);
+ const select = mount(
);
expect(select.find('select').prop('type')).toBeUndefined();
});
it('should reference innerRef to the select node', () => {
const ref = React.createRef();
- mount(
);
+ mount(
);
expect(ref.current).not.toBeNull();
expect(ref.current).toBeInstanceOf(HTMLSelectElement);
});
@@ -217,22 +217,22 @@ describe('Custom Inputs', () => {
describe('CustomFile', () => {
it('should render children in the outer div', () => {
- const file = mount(
);
+ const file = mount(
);
expect(file.find('.custom-file').first().type()).toBe('div');
});
it('should add class is-invalid when invalid is true', () => {
- const file = mount(
);
+ const file = mount(
);
expect(file.find('input').prop('className').indexOf('is-invalid') > -1).toBeTruthy();
});
it('should add class is-valid when valid is true', () => {
- const file = mount(
);
+ const file = mount(
);
expect(file.find('input').prop('className').indexOf('is-valid') > -1).toBeTruthy();
});
it('should render an input with the type of file', () => {
- const file = mount(
);
+ const file = mount(
);
expect(file.find('input').prop('type')).toBe('file');
});
@@ -249,23 +249,23 @@ describe('Custom Inputs', () => {
});
it('should pass classNames to the outer div', () => {
- const file = mount(
);
+ const file = mount(
);
expect(file.find('.custom-file').prop('className').indexOf('yo') > -1).toBeTruthy();
});
it('should set the label when provided', () => {
- const file = mount(
);
+ const file = mount(
);
expect(file.find('label').text()).toBe('yo');
});
it('should pass all other props to the input node', () => {
- const file = mount(
);
+ const file = mount(
);
expect(file.find('input').prop('data-testprop')).toBe('yo');
});
it('should reference innerRef to the input node', () => {
const ref = React.createRef();
- mount(
);
+ mount(
);
expect(ref.current).not.toBeNull();
expect(ref.current).toBeInstanceOf(HTMLInputElement);
});
@@ -273,7 +273,7 @@ describe('Custom Inputs', () => {
describe('onChange', () => {
it('calls props.onChange if it exists', () => {
const onChange = jest.fn();
- const file = mount(
);
+ const file = mount(
);
file.find('input').hostNodes().simulate('change');
expect(onChange).toHaveBeenCalled();
@@ -281,7 +281,7 @@ describe('Custom Inputs', () => {
});
it('removes fakepath from file name', () => {
- const file = mount(
);
+ const file = mount(
);
file.find('input').hostNodes().simulate('change', {
target:{
@@ -293,7 +293,7 @@ describe('Custom Inputs', () => {
});
it('lists multiple files when supported', () => {
- const file = mount(
);
+ const file = mount(
);
file.find('input').hostNodes().simulate('change', {
target:{
@@ -312,33 +312,33 @@ describe('Custom Inputs', () => {
describe('CustomRange', () => {
it('should render children in the outer div', () => {
- const range = shallow(
);
+ const range = shallow(
);
expect(range.type()).toBe('input');
});
it('should add class is-invalid when invalid is true', () => {
- const range = mount(
);
+ const range = mount(
);
expect(range.find('input').prop('className').indexOf('is-invalid') > -1).toBeTruthy();
});
it('should add class is-valid when valid is true', () => {
- const range = mount(
);
+ const range = mount(
);
expect(range.find('input').prop('className').indexOf('is-valid') > -1).toBeTruthy();
});
it('should render an input with the type of range', () => {
- const range = mount(
);
+ const range = mount(
);
expect(range.find('input').prop('type')).toBe('range');
});
it('should pass all other props to the input node', () => {
- const range = mount(
);
+ const range = mount(
);
expect(range.find('input').prop('data-testprop')).toBe('yo');
});
it('should reference innerRef to the input node', () => {
const ref = React.createRef();
- mount(
);
+ mount(
);
expect(ref.current).not.toBeNull();
expect(ref.current).toBeInstanceOf(HTMLInputElement);
});
diff --git a/src/__tests__/DropdownMenu.spec.js b/src/__tests__/DropdownMenu.spec.js
index 84999792f..6b3cc4921 100644
--- a/src/__tests__/DropdownMenu.spec.js
+++ b/src/__tests__/DropdownMenu.spec.js
@@ -171,14 +171,14 @@ describe('DropdownMenu', () => {
expect(wrapper.find(Popper).prop('positionFixed')).toBe(true);
});
- it('should not render multiple children when isOpen is false', () => {
+ it('should not render Popper when isOpen is false', () => {
const wrapper = mount(
Ello world
);
- expect(wrapper.childAt(0).children().length).toBe(0);
+ expect(wrapper.find(Popper).length).toBe(0);
});
it('should render custom tag', () => {
diff --git a/src/__tests__/Input.spec.js b/src/__tests__/Input.spec.js
index 1543ef4c2..50f7c914c 100644
--- a/src/__tests__/Input.spec.js
+++ b/src/__tests__/Input.spec.js
@@ -4,7 +4,7 @@ import { Input } from '../';
describe('Input', () => {
it('should render with "input" tag when no type is provided', () => {
- const wrapper = shallow(
Yo!);
+ const wrapper = shallow(
);
expect(wrapper.type()).toBe('input');
});
@@ -16,7 +16,7 @@ describe('Input', () => {
});
it('should render with "textarea" tag when type is "textarea"', () => {
- const wrapper = shallow(
Yo!);
+ const wrapper = shallow(
);
expect(wrapper.type()).toBe('textarea');
});
@@ -111,14 +111,14 @@ describe('Input', () => {
expect(wrapper.hasClass('is-valid')).toBe(true);
});
- it('should render with "form-control-${size}" class when size is "lg" or "sm"', () => {
- const wrapper = shallow(
);
+ it('should render with "form-control-${bsSize}" class when bsSize is "lg" or "sm"', () => {
+ const wrapper = shallow(
);
expect(wrapper.hasClass('form-control-lg')).toBe(true);
});
it('should render with "form-control" class when size is nor "lg" nor "sm"', () => {
- const wrapper = shallow(
);
+ const wrapper = shallow(
);
expect(wrapper.hasClass('form-control-sm')).toBe(false);
expect(wrapper.hasClass('form-control-lg')).toBe(false);
@@ -209,16 +209,23 @@ describe('Input', () => {
});
it('should render additional classes', () => {
- const wrapper = shallow(
Yo!);
+ const wrapper = shallow(
);
expect(wrapper.hasClass('other')).toBe(true);
});
it('should render "select" and "textarea" without type property', () => {
const input = shallow(
Yo!);
- const textarea = shallow(
Yo!);
+ const textarea = shallow(
);
expect(input.find('[type="select"]').exists()).toBe(false);
expect(textarea.find('[type="textarea"]').exists()).toBe(false);
});
+
+ it('should render with "form-control-range" not "form-control" class when type is range', () => {
+ const wrapper = shallow(
);
+
+ expect(wrapper.hasClass('form-control-range')).toBe(true);
+ expect(wrapper.hasClass('form-control')).toBe(false);
+ });
});
diff --git a/src/__tests__/InputGroupButtonDropdown.spec.js b/src/__tests__/InputGroupButtonDropdown.spec.js
index cd307dff6..8edc32275 100644
--- a/src/__tests__/InputGroupButtonDropdown.spec.js
+++ b/src/__tests__/InputGroupButtonDropdown.spec.js
@@ -4,7 +4,7 @@ import { InputGroupButtonDropdown, Dropdown } from '../';
describe('InputGroupButtonDropdown', () => {
it('should render Dropdown', () => {
- const wrapper = shallow(
Yo!);
+ const wrapper = shallow(
Yo!);
expect(wrapper.type()).toBe(Dropdown);
});
diff --git a/src/__tests__/Progress.spec.js b/src/__tests__/Progress.spec.js
index a90d6f08d..3bb8a173f 100644
--- a/src/__tests__/Progress.spec.js
+++ b/src/__tests__/Progress.spec.js
@@ -27,6 +27,12 @@ describe('Progress', () => {
expect(wrapper.prop('max')).toBe(100);
});
+ it('should render with "style" empty object by default', () => {
+ const wrapper = mount(
);
+
+ expect(wrapper.prop('style')).toEqual({});
+ });
+
it('should render with the given "value" when passed as string prop', () => {
const wrapper = mount(
);
diff --git a/src/index.js b/src/index.js
index 522244cf2..9e76d55d4 100644
--- a/src/index.js
+++ b/src/index.js
@@ -89,3 +89,4 @@ export UncontrolledDropdown from './UncontrolledDropdown';
export UncontrolledTooltip from './UncontrolledTooltip';
export Spinner from './Spinner';
export * as Util from './utils';
+export * as Polyfill from './polyfill'
diff --git a/src/polyfill.js b/src/polyfill.js
new file mode 100644
index 000000000..3c08c3e3e
--- /dev/null
+++ b/src/polyfill.js
@@ -0,0 +1,20 @@
+(() => {
+ if ( typeof window.CustomEvent === 'function' ) return;
+
+ const CustomEvent = (( event, params ) => {
+ params = params || { bubbles: false, cancelable: false, detail: null };
+ var evt = document.createEvent( 'CustomEvent' );
+ evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
+ return evt;
+ });
+
+ window.CustomEvent = CustomEvent;
+})();
+
+(() => {
+ if ( typeof Object.values === 'function' ) return;
+
+ const values = ( (O) => Object.keys(O).map((key) => O[key]) );
+
+ Object.values = values;
+})();
diff --git a/webpack.docs.config.js b/webpack.docs.config.js
index 341c75152..12cd7e81a 100644
--- a/webpack.docs.config.js
+++ b/webpack.docs.config.js
@@ -41,6 +41,7 @@ const paths = [
'/utilities/',
'/utilities/colors/',
'/utilities/clearfix/',
+ '/premium-themes/',
'/404.html'
];