diff --git a/src/BootstrapMixin.js b/src/BootstrapMixin.js
index 00eb552f99..f158c58bc8 100644
--- a/src/BootstrapMixin.js
+++ b/src/BootstrapMixin.js
@@ -1,28 +1,28 @@
-import React from 'react';
-import constants from './constants';
+import styleMaps from './styleMaps';
+import CustomPropTypes from './utils/CustomPropTypes';
const BootstrapMixin = {
propTypes: {
- bsClass: React.PropTypes.oneOf(Object.keys(constants.CLASSES)),
- bsStyle: React.PropTypes.oneOf(Object.keys(constants.STYLES)),
- bsSize: React.PropTypes.oneOf(Object.keys(constants.SIZES))
+ bsClass: CustomPropTypes.keyOf(styleMaps.CLASSES),
+ bsStyle: CustomPropTypes.keyOf(styleMaps.STYLES),
+ bsSize: CustomPropTypes.keyOf(styleMaps.SIZES)
},
getBsClassSet() {
let classes = {};
- let bsClass = this.props.bsClass && constants.CLASSES[this.props.bsClass];
+ let bsClass = this.props.bsClass && styleMaps.CLASSES[this.props.bsClass];
if (bsClass) {
classes[bsClass] = true;
let prefix = bsClass + '-';
- let bsSize = this.props.bsSize && constants.SIZES[this.props.bsSize];
+ let bsSize = this.props.bsSize && styleMaps.SIZES[this.props.bsSize];
if (bsSize) {
classes[prefix + bsSize] = true;
}
- let bsStyle = this.props.bsStyle && constants.STYLES[this.props.bsStyle];
+ let bsStyle = this.props.bsStyle && styleMaps.STYLES[this.props.bsStyle];
if (this.props.bsStyle) {
classes[prefix + bsStyle] = true;
}
@@ -32,7 +32,7 @@ const BootstrapMixin = {
},
prefixClass(subClass) {
- return constants.CLASSES[this.props.bsClass] + '-' + subClass;
+ return styleMaps.CLASSES[this.props.bsClass] + '-' + subClass;
}
};
diff --git a/src/Col.js b/src/Col.js
index 23ae471ad1..0898e10b2e 100644
--- a/src/Col.js
+++ b/src/Col.js
@@ -1,6 +1,6 @@
import React from 'react';
import classNames from 'classnames';
-import constants from './constants';
+import styleMaps from './styleMaps';
const Col = React.createClass({
propTypes: {
@@ -33,8 +33,8 @@ const Col = React.createClass({
let ComponentClass = this.props.componentClass;
let classes = {};
- Object.keys(constants.SIZES).forEach(function (key) {
- let size = constants.SIZES[key];
+ Object.keys(styleMaps.SIZES).forEach(function (key) {
+ let size = styleMaps.SIZES[key];
let prop = size;
let classPart = size + '-';
diff --git a/src/Glyphicon.js b/src/Glyphicon.js
index bb3e41ea19..e406feaa31 100644
--- a/src/Glyphicon.js
+++ b/src/Glyphicon.js
@@ -1,13 +1,13 @@
import React from 'react';
import classNames from 'classnames';
import BootstrapMixin from './BootstrapMixin';
-import constants from './constants';
+import styleMaps from './styleMaps';
const Glyphicon = React.createClass({
mixins: [BootstrapMixin],
propTypes: {
- glyph: React.PropTypes.oneOf(constants.GLYPHS).isRequired
+ glyph: React.PropTypes.oneOf(styleMaps.GLYPHS).isRequired
},
getDefaultProps() {
diff --git a/src/index.js b/src/index.js
index b7e54269fb..ab8c3f01c2 100644
--- a/src/index.js
+++ b/src/index.js
@@ -47,7 +47,7 @@ import Table from './Table';
import TabPane from './TabPane';
import Tooltip from './Tooltip';
import Well from './Well';
-import constants from './constants';
+import styleMaps from './styleMaps';
export default {
Accordion,
@@ -99,5 +99,5 @@ export default {
TabPane,
Tooltip,
Well,
- constants
+ styleMaps
};
diff --git a/src/constants.js b/src/styleMaps.js
similarity index 97%
rename from src/constants.js
rename to src/styleMaps.js
index 02fdc0d057..83435899c6 100644
--- a/src/constants.js
+++ b/src/styleMaps.js
@@ -1,4 +1,4 @@
-export default {
+const styleMaps = {
CLASSES: {
'alert': 'alert',
'button': 'btn',
@@ -31,6 +31,9 @@ export default {
'tabs': 'tabs',
'pills': 'pills'
},
+ addStyle: function(name) {
+ styleMaps.STYLES[name] = name;
+ },
SIZES: {
'large': 'lg',
'medium': 'md',
@@ -299,3 +302,5 @@ export default {
'menu-up'
]
};
+
+export default styleMaps;
diff --git a/src/utils/CustomPropTypes.js b/src/utils/CustomPropTypes.js
index 73984de7df..c56a0a4e8a 100644
--- a/src/utils/CustomPropTypes.js
+++ b/src/utils/CustomPropTypes.js
@@ -13,7 +13,16 @@ let CustomPropTypes = {
* @param componentName
* @returns {Error|undefined}
*/
- mountable: createMountableChecker()
+ mountable: createMountableChecker(),
+ /**
+ * Checks whether a prop matches a key of an associated object
+ *
+ * @param props
+ * @param propName
+ * @param componentName
+ * @returns {Error|undefined}
+ */
+ keyOf: createKeyOfChecker
};
/**
@@ -57,4 +66,18 @@ function createMountableChecker() {
return createChainableTypeChecker(validate);
}
+function createKeyOfChecker(obj) {
+ function validate(props, propName, componentName) {
+ let propValue = props[propName];
+ if (!obj.hasOwnProperty(propValue)) {
+ let valuesString = JSON.stringify(Object.keys(obj));
+ return new Error(
+ `Invalid prop '${propName}' of value '${propValue}' ` +
+ `supplied to '${componentName}', expected one of ${valuesString}.`
+ );
+ }
+ }
+ return createChainableTypeChecker(validate);
+}
+
export default CustomPropTypes;
diff --git a/test/BootstrapMixinSpec.js b/test/BootstrapMixinSpec.js
index bf6d4fd628..dea0bac9ce 100644
--- a/test/BootstrapMixinSpec.js
+++ b/test/BootstrapMixinSpec.js
@@ -1,6 +1,7 @@
import React from 'react';
import ReactTestUtils from 'react/lib/ReactTestUtils';
import BootstrapMixin from '../src/BootstrapMixin';
+import styleMaps from '../src/styleMaps';
let Component;
@@ -195,5 +196,15 @@ describe('BootstrapMixin', function () {
);
assert.equal(instance.prefixClass('title'), 'btn-title');
});
+
+ it('should return "btn btn-wacky"', function () {
+ styleMaps.addStyle('wacky');
+ let instance = ReactTestUtils.renderIntoDocument(
+