Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[added] styleMaps.addStyle and CustomPropTypes.keyOf #496

Merged
merged 1 commit into from
Apr 20, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions src/BootstrapMixin.js
Original file line number Diff line number Diff line change
@@ -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;
}
Expand All @@ -32,7 +32,7 @@ const BootstrapMixin = {
},

prefixClass(subClass) {
return constants.CLASSES[this.props.bsClass] + '-' + subClass;
return styleMaps.CLASSES[this.props.bsClass] + '-' + subClass;
}
};

Expand Down
6 changes: 3 additions & 3 deletions src/Col.js
Original file line number Diff line number Diff line change
@@ -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: {
Expand Down Expand Up @@ -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 + '-';

Expand Down
4 changes: 2 additions & 2 deletions src/Glyphicon.js
Original file line number Diff line number Diff line change
@@ -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() {
Expand Down
4 changes: 2 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -99,5 +99,5 @@ export default {
TabPane,
Tooltip,
Well,
constants
styleMaps
};
7 changes: 6 additions & 1 deletion src/constants.js → src/styleMaps.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export default {
const styleMaps = {
CLASSES: {
'alert': 'alert',
'button': 'btn',
Expand Down Expand Up @@ -31,6 +31,9 @@ export default {
'tabs': 'tabs',
'pills': 'pills'
},
addStyle: function(name) {
styleMaps.STYLES[name] = name;
},
SIZES: {
'large': 'lg',
'medium': 'md',
Expand Down Expand Up @@ -299,3 +302,5 @@ export default {
'menu-up'
]
};

export default styleMaps;
25 changes: 24 additions & 1 deletion src/utils/CustomPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
};

/**
Expand Down Expand Up @@ -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;
11 changes: 11 additions & 0 deletions test/BootstrapMixinSpec.js
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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(
<Component bsClass='button' bsStyle='wacky'>
content
</Component>
);
assert.deepEqual(instance.getBsClassSet(), {'btn': true, 'btn-wacky': true});
});
});
});
22 changes: 22 additions & 0 deletions test/CustomPropTypesSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,26 @@ describe('CustomPropTypes', function () {
assert.isUndefined(validate(ReactTestUtils.renderIntoDocument(<div />)));
});
});

describe('keyOf', function () {
let obj = {'foo': 1};
function validate(prop) {
return CustomPropTypes.keyOf(obj)({p: prop}, 'p', 'Component');
}
function validateRequired(prop) {
return CustomPropTypes.keyOf(obj).isRequired({p: prop}, 'p', 'Component');
}

it('Should return error with non-key values', function() {
assert.instanceOf(validateRequired(), Error);
assert.instanceOf(validateRequired(null), Error);
assert.instanceOf(validate('bar'), Error);
});
it('Should return undefined with key values', function() {
assert.isUndefined(validate());
assert.isUndefined(validate('foo'));
obj.bar = 2;
assert.isUndefined(validate('bar'));
});
});
});