diff --git a/src/BootstrapMixin.js b/src/BootstrapMixin.js index df8795de4d..17c8e81d2b 100644 --- a/src/BootstrapMixin.js +++ b/src/BootstrapMixin.js @@ -1,3 +1,4 @@ +import React from 'react'; import styleMaps from './styleMaps'; import CustomPropTypes from './utils/CustomPropTypes'; @@ -12,7 +13,7 @@ const BootstrapMixin = { * Style variants * @type {("default"|"primary"|"success"|"info"|"warning"|"danger"|"link")} */ - bsStyle: CustomPropTypes.keyOf(styleMaps.STYLES), + bsStyle: React.PropTypes.oneOf(styleMaps.STYLES), /** * Size variants * @type {("xsmall"|"small"|"medium"|"large")} @@ -34,9 +35,12 @@ const BootstrapMixin = { classes[prefix + bsSize] = true; } - let bsStyle = this.props.bsStyle && styleMaps.STYLES[this.props.bsStyle]; if (this.props.bsStyle) { - classes[prefix + bsStyle] = true; + if (styleMaps.STYLES.indexOf(this.props.bsStyle) >= 0) { + classes[prefix + this.props.bsStyle] = true; + } else { + classes[this.props.bsStyle] = true; + } } } diff --git a/src/styleMaps.js b/src/styleMaps.js index a200286d33..49ad80f3f0 100644 --- a/src/styleMaps.js +++ b/src/styleMaps.js @@ -21,20 +21,20 @@ const styleMaps = { 'row': 'row', 'well': 'well' }, - STYLES: { - 'default': 'default', - 'primary': 'primary', - 'success': 'success', - 'info': 'info', - 'warning': 'warning', - 'danger': 'danger', - 'link': 'link', - 'inline': 'inline', - 'tabs': 'tabs', - 'pills': 'pills' - }, + STYLES: [ + 'default', + 'primary', + 'success', + 'info', + 'warning', + 'danger', + 'link', + 'inline', + 'tabs', + 'pills' + ], addStyle(name) { - styleMaps.STYLES[name] = name; + styleMaps.STYLES.push(name); }, SIZES: { 'large': 'lg', diff --git a/test/BootstrapMixinSpec.js b/test/BootstrapMixinSpec.js index 333870506b..9c7b519120 100644 --- a/test/BootstrapMixinSpec.js +++ b/test/BootstrapMixinSpec.js @@ -2,6 +2,7 @@ import React from 'react'; import ReactTestUtils from 'react/lib/ReactTestUtils'; import BootstrapMixin from '../src/BootstrapMixin'; import styleMaps from '../src/styleMaps'; +import { shouldWarn } from './helpers'; let Component; @@ -26,169 +27,92 @@ describe('BootstrapMixin', function () { assert.deepEqual(instance.getBsClassSet(), {}); }); - it('should return "col"', function () { - let instance = ReactTestUtils.renderIntoDocument( - - content - - ); - assert.deepEqual(instance.getBsClassSet(), {'col': true}); - }); - - it('should return "btn"', function () { - let instance = ReactTestUtils.renderIntoDocument( - - content - - ); - assert.deepEqual(instance.getBsClassSet(), {'btn': true}); - }); - - it('should return "btn-group"', function () { - let instance = ReactTestUtils.renderIntoDocument( - - content - - ); - assert.deepEqual(instance.getBsClassSet(), {'btn-group': true}); - }); - - it('should return "label"', function () { - let instance = ReactTestUtils.renderIntoDocument( - - content - - ); - assert.deepEqual(instance.getBsClassSet(), {'label': true}); - }); - - it('should return "alert"', function () { - let instance = ReactTestUtils.renderIntoDocument( - - content - - ); - assert.deepEqual(instance.getBsClassSet(), {'alert': true}); - }); - - it('should return "input-group"', function () { - let instance = ReactTestUtils.renderIntoDocument( - - content - - ); - assert.deepEqual(instance.getBsClassSet(), {'input-group': true}); - }); - - it('should return "form"', function () { - let instance = ReactTestUtils.renderIntoDocument( - - content - - ); - assert.deepEqual(instance.getBsClassSet(), {'form': true}); - }); - - it('should return "panel"', function () { - let instance = ReactTestUtils.renderIntoDocument( - - content - - ); - assert.deepEqual(instance.getBsClassSet(), {'panel': true}); - }); - - it('should return "btn btn-default"', function () { - let instance = ReactTestUtils.renderIntoDocument( - - content - - ); - assert.deepEqual(instance.getBsClassSet(), {'btn': true, 'btn-default': true}); - }); - - it('should return "btn btn-primary"', function () { - let instance = ReactTestUtils.renderIntoDocument( - - content - - ); - assert.deepEqual(instance.getBsClassSet(), {'btn': true, 'btn-primary': true}); - }); - - it('should return "btn btn-success"', function () { - let instance = ReactTestUtils.renderIntoDocument( - - content - - ); - assert.deepEqual(instance.getBsClassSet(), {'btn': true, 'btn-success': true}); - }); - - it('should return "btn btn-info"', function () { - let instance = ReactTestUtils.renderIntoDocument( - - content - - ); - assert.deepEqual(instance.getBsClassSet(), {'btn': true, 'btn-info': true}); - }); - - it('should return "btn btn-link"', function () { - let instance = ReactTestUtils.renderIntoDocument( - - content - - ); - assert.deepEqual(instance.getBsClassSet(), {'btn': true, 'btn-link': true}); - }); - - it('should return "btn btn-inline"', function () { - let instance = ReactTestUtils.renderIntoDocument( - - content - - ); - assert.deepEqual(instance.getBsClassSet(), {'btn': true, 'btn-inline': true}); - }); - - it('should return "btn btn-lg"', function () { - let instance = ReactTestUtils.renderIntoDocument( - - content - - ); - assert.deepEqual(instance.getBsClassSet(), {'btn': true, 'btn-lg': true}); - }); - - it('should return "btn btn-md"', function () { - let instance = ReactTestUtils.renderIntoDocument( - - content - - ); - assert.deepEqual(instance.getBsClassSet(), {'btn': true, 'btn-md': true}); - }); - - it('should return "btn btn-sm"', function () { - let instance = ReactTestUtils.renderIntoDocument( - - content - - ); - assert.deepEqual(instance.getBsClassSet(), {'btn': true, 'btn-sm': true}); - }); + it('maps and validates OK default classes', function () { + function instanceClassSet(bsClass) { + let instance = ReactTestUtils.renderIntoDocument( + + content + + ); + return instance.getBsClassSet(); + } - it('should return "btn btn-xs"', function () { - let instance = ReactTestUtils.renderIntoDocument( - - content - - ); - assert.deepEqual(instance.getBsClassSet(), {'btn': true, 'btn-xs': true}); + assert.deepEqual(instanceClassSet('column'), {'col': true}); + assert.deepEqual(instanceClassSet('button'), {'btn': true}); + assert.deepEqual(instanceClassSet('button-group'), {'btn-group': true}); + assert.deepEqual(instanceClassSet('label'), {'label': true}); + assert.deepEqual(instanceClassSet('alert'), {'alert': true}); + assert.deepEqual(instanceClassSet('input-group'), {'input-group': true}); + assert.deepEqual(instanceClassSet('form'), {'form': true}); + assert.deepEqual(instanceClassSet('panel'), {'panel': true}); + }); + + describe('Predefined Bootstrap styles', function () { + it('maps and validates OK default styles', function () { + function instanceClassSet(style) { + let instance = ReactTestUtils.renderIntoDocument( + + content + + ); + return instance.getBsClassSet(); + } + + assert.deepEqual(instanceClassSet('default'), {'btn': true, 'btn-default': true}); + assert.deepEqual(instanceClassSet('primary'), {'btn': true, 'btn-primary': true}); + assert.deepEqual(instanceClassSet('success'), {'btn': true, 'btn-success': true}); + assert.deepEqual(instanceClassSet('info'), {'btn': true, 'btn-info': true}); + assert.deepEqual(instanceClassSet('link'), {'btn': true, 'btn-link': true}); + assert.deepEqual(instanceClassSet('inline'), {'btn': true, 'btn-inline': true}); + }); + }); + + describe('Sizes', function () { + it('maps english words for sizes to bootstrap sizes constants', function () { + function instanceClassSet(size) { + let instance = ReactTestUtils.renderIntoDocument( + + content + + ); + return instance.getBsClassSet(); + } + + assert.deepEqual(instanceClassSet('large'), {'btn': true, 'btn-lg': true}); + assert.deepEqual(instanceClassSet('small'), {'btn': true, 'btn-sm': true}); + assert.deepEqual(instanceClassSet('medium'), {'btn': true, 'btn-md': true}); + assert.deepEqual(instanceClassSet('xsmall'), {'btn': true, 'btn-xs': true}); + }); + }); + + describe('Custom styles', function () { + it('should validate OK custom styles added via "addStyle()"', function () { + + styleMaps.addStyle('wacky'); + + let instance = ReactTestUtils.renderIntoDocument( + + content + + ); + assert.deepEqual(instance.getBsClassSet(), {'btn': true, 'btn-wacky': true}); + }); + + it('should allow custom styles as is but with validation warning', function () { + let instance = ReactTestUtils.renderIntoDocument( + + content + + ); + assert.deepEqual(instance.getBsClassSet(), {'btn': true, 'my-custom-class': true}); + shouldWarn('Invalid prop `bsStyle` of value `my-custom-class`'); + }); }); + }); - it('should return "btn-title"', function () { + // todo: fix bad naming + describe('#prefixClass', function () { + it('allows custom sub-classes', function () { let instance = ReactTestUtils.renderIntoDocument( content @@ -196,15 +120,5 @@ describe('BootstrapMixin', function () { ); assert.equal(instance.prefixClass('title'), 'btn-title'); }); - - it('should return "btn btn-wacky"', function () { - styleMaps.addStyle('wacky'); - let instance = ReactTestUtils.renderIntoDocument( - - content - - ); - assert.deepEqual(instance.getBsClassSet(), {'btn': true, 'btn-wacky': true}); - }); }); });