From 168e11abd225934ba5b7ce96ccc90907d119137e Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Wed, 26 Aug 2015 20:45:16 +0100 Subject: [PATCH 001/229] Added useModuleName. --- README.md | 23 ++++++++++++++++++++--- dist/index.js | 4 ++++ dist/linkClass.js | 14 +++++++++++--- src/index.js | 4 ++++ src/linkClass.js | 14 +++++++++++--- 5 files changed, 50 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 9221f03..1ac8d2e 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ React CSS Modules implement automatic mapping of class names to CSS modules. Eve - [Browserify](#browserify) - [Decorator](#decorator) - [Options](#options) + - [`useModuleName`](#usemodulename) - [`allowMultiple`](#allowmultiple) - [`keepOriginal`](#keeporiginal) - [`errorNotFound`](#errornotfound) @@ -191,9 +192,21 @@ or as a second parameter when using `CSSModules` as a decorator: @CSSModules(styles, options); ``` +#### `useModuleName` + +Default: `false`. + +When enabled then CSS Modules are loaded using `moduleName` property and `className` is used only for global CSS, e.g. + +```js +
+``` + #### `allowMultiple` -Allows multiple CSS class names. Default: `true`. +Default: `true`. + +Allows multiple CSS class names. When `false`, the following will cause an error: @@ -203,7 +216,9 @@ When `false`, the following will cause an error: #### `keepOriginal` -Keeps original CSS class name in addition to the names of the CSS Modules. Default: `true`. +Default: `true`. + +Keeps original CSS class name in addition to the names of the CSS Modules. When `true`, the following `ReactElement`: @@ -215,7 +230,9 @@ will be rendered with a `className` property `foo component__foo___2w27N bar com #### `errorNotFound` -Throws an error when class name cannot be mapped to a CSS Module. Default: `false`. +Default: `false`. + +Throws an error when class name cannot be mapped to a CSS Module. ## SASS, SCSS, LESS and other CSS Preprocessors diff --git a/dist/index.js b/dist/index.js index 7477f0a..6424571 100644 --- a/dist/index.js +++ b/dist/index.js @@ -56,6 +56,10 @@ functionConstructor = function (Component, styles) { options.errorNotFound = false; } + if (options.useModuleNames !== true) { + options.useModuleNames = false; + } + return (0, _linkClass2['default'])(_get(Object.getPrototypeOf(_class.prototype), 'render', this).call(this), styles, options); } }]); diff --git a/dist/linkClass.js b/dist/linkClass.js index 3b8fc21..b296ed5 100644 --- a/dist/linkClass.js +++ b/dist/linkClass.js @@ -17,6 +17,7 @@ var linkClass = undefined; * @property {Boolean} allowMultiple Determines whether `className` can have multiple class names. Throws an error when the constrained is not met. Default: true. * @property {Boolean} keepOriginal Determines whether the original `className` value is kept in addition to the appended CSS modules styles CSS class name. Default: true. * @property {Boolean} errorNotFound Determines whether an error is raised if `className` defines a CSS class(es) that is not present in the CSS modules styles. Default: false. + * @property {Boolean} useModuleName When enabled then CSS Modules are loaded using `moduleName` property and `className` is used only for global CSS. Default: false. */ /** @@ -32,10 +33,17 @@ linkClass = function (element) { var newProps = undefined, newClassName = undefined, newChildren = undefined, - childrenCount = undefined; + childrenCount = undefined, + moduleName = undefined; - if (element.props.className) { - newClassName = element.props.className.split(' '); + if (options.useModuleName) { + moduleName = element.props.moduleName; + } else { + moduleName = element.props.className; + } + + if (moduleName) { + newClassName = moduleName.split(' '); if (options.allowMultiple === false && newClassName.length > 1) { throw new Error('ReactElement defines multiple class names ("' + element.props.className + '") in className declaration.'); diff --git a/src/index.js b/src/index.js index 0c71a14..b3fce2f 100644 --- a/src/index.js +++ b/src/index.js @@ -26,6 +26,10 @@ functionConstructor = (Component, styles, options = {}) => { options.errorNotFound = false; } + if (options.useModuleNames !== true) { + options.useModuleNames = false; + } + return linkClass(super.render(), styles, options); } }; diff --git a/src/linkClass.js b/src/linkClass.js index c2f6993..acecc8b 100644 --- a/src/linkClass.js +++ b/src/linkClass.js @@ -7,6 +7,7 @@ let linkClass; * @property {Boolean} allowMultiple Determines whether `className` can have multiple class names. Throws an error when the constrained is not met. Default: true. * @property {Boolean} keepOriginal Determines whether the original `className` value is kept in addition to the appended CSS modules styles CSS class name. Default: true. * @property {Boolean} errorNotFound Determines whether an error is raised if `className` defines a CSS class(es) that is not present in the CSS modules styles. Default: false. + * @property {Boolean} useModuleName When enabled then CSS Modules are loaded using `moduleName` property and `className` is used only for global CSS. Default: false. */ /** @@ -19,10 +20,17 @@ linkClass = (element, styles = {}, options = {}) => { let newProps, newClassName, newChildren, - childrenCount; + childrenCount, + moduleName; - if (element.props.className) { - newClassName = element.props.className.split(' '); + if (options.useModuleName) { + moduleName = element.props.moduleName; + } else { + moduleName = element.props.className; + } + + if (moduleName) { + newClassName = moduleName.split(' '); if (options.allowMultiple === false && newClassName.length > 1) { throw new Error(`ReactElement defines multiple class names ("${element.props.className}") in className declaration.`); From 5ad88aa691811080581d9f4fd90008bca931f5a6 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Wed, 26 Aug 2015 20:52:42 +0100 Subject: [PATCH 002/229] includeOriginal must be false when useModulName is true --- dist/linkClass.js | 2 ++ src/linkClass.js | 2 ++ test/linkClass.js | 17 +++++++++++++++++ 3 files changed, 21 insertions(+) diff --git a/dist/linkClass.js b/dist/linkClass.js index b296ed5..82883fb 100644 --- a/dist/linkClass.js +++ b/dist/linkClass.js @@ -38,6 +38,8 @@ linkClass = function (element) { if (options.useModuleName) { moduleName = element.props.moduleName; + + options.includeOriginal = false; } else { moduleName = element.props.className; } diff --git a/src/linkClass.js b/src/linkClass.js index acecc8b..1dcdbb4 100644 --- a/src/linkClass.js +++ b/src/linkClass.js @@ -25,6 +25,8 @@ linkClass = (element, styles = {}, options = {}) => { if (options.useModuleName) { moduleName = element.props.moduleName; + + options.includeOriginal = false; } else { moduleName = element.props.className; } diff --git a/test/linkClass.js b/test/linkClass.js index 9c58286..b9b7c82 100644 --- a/test/linkClass.js +++ b/test/linkClass.js @@ -129,6 +129,23 @@ describe('linkClass', () => { }); }); + describe('when options.useModuleName is true', () => { + it('does not lookup the className property', () => { + let subject; + + subject = linkClass(
, {foo: 'foo-1'}, {useModuleName: true}); + + expect(subject.props.className).to.equal('foo'); + }); + it('appends CSS Modules using modulName', () => { + let subject; + + subject = linkClass(
, {foo: 'foo-1'}, {useModuleName: true}); + + expect(subject.props.className).to.equal('foo-1'); + }); + }); + describe('when options.allowMultiple is false', () => { describe('when it finds multiple CSS class names in a className', () => { it('throws an error', () => { From afeb2667ee69a8c7ca8a75da57f682341d3e997c Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Wed, 26 Aug 2015 20:52:49 +0100 Subject: [PATCH 003/229] 1.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2f5fc53..3bb785a 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "css", "modules" ], - "version": "1.1.0", + "version": "1.2.0", "author": { "name": "Gajus Kuizinas", "email": "gk@anuary.com", From 60c1f69d5b61834ab1b108fb4b6bd3363b91ba54 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Wed, 26 Aug 2015 21:35:36 +0100 Subject: [PATCH 004/229] Stricted config validation. --- dist/index copy.js | 127 --------------------------------------------- dist/index.js | 22 ++------ dist/linkClass.js | 8 --- dist/makeConfig.js | 60 +++++++++++++++++++++ dist/utils.js | 26 ++++++++++ src/index.js | 19 +------ src/linkClass.js | 8 --- src/makeConfig.js | 50 ++++++++++++++++++ src/utils.js | 9 ++++ test/makeConfig.js | 54 +++++++++++++++++++ 10 files changed, 206 insertions(+), 177 deletions(-) delete mode 100644 dist/index copy.js create mode 100644 dist/makeConfig.js create mode 100644 dist/utils.js create mode 100644 src/makeConfig.js create mode 100644 src/utils.js create mode 100644 test/makeConfig.js diff --git a/dist/index copy.js b/dist/index copy.js deleted file mode 100644 index 2a27213..0000000 --- a/dist/index copy.js +++ /dev/null @@ -1,127 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, '__esModule', { - value: true -}); - -var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - -var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - -function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } - -var _react = require('react'); - -var _react2 = _interopRequireDefault(_react); - -var _reactDom = require('react-dom'); - -var _reactDom2 = _interopRequireDefault(_reactDom); - -var _lodashLangIsArray = require('lodash/lang/isArray'); - -var _lodashLangIsArray2 = _interopRequireDefault(_lodashLangIsArray); - -var _lodash = require('lodash'); - -var _lodash2 = _interopRequireDefault(_lodash); - -var unfreeze = undefined; - -/** - * Make a shallow copy of the object. - * - * @param {Object} source Frozen object. - * @return {Object} - */ -unfreeze = function (source) { - var property = undefined, - target = undefined; - - target = {}; - - for (property in source) { - target[property] = source[property]; - } - - return target; -}; - -/** - * @param {ReactClass} Target - * @return {ReactClass} - */ - -exports['default'] = function (Target, styles) { - var linkClass = undefined; - - console.log('styles', styles); - - /** - * @param {ReactElement} element - * @return {ReactElement} - */ - linkClass = function (element) { - var isFrozen = undefined; - - if (Object.isFrozen && Object.isFrozen(element)) { - isFrozen = true; - - // https://github.com/facebook/react/blob/v0.13.3/src/classic/element/ReactElement.js#L131 - element = unfreeze(element); - element.props = unfreeze(element.props); - } - - if (element.props.className) { - element.props.className = element.props.className.split(' ').map(function (className) { - if (styles[className]) { - return className + ' ' + styles[className]; - } else { - return className; - } - }).join(' '); - } - - if ((0, _lodashLangIsArray2['default'])(element.props.children)) { - element.props.children = element.props.children.map(function (node) { - if (_react2['default'].isValidElement(node)) { - return linkClass(node); - } else { - return node; - } - }); - } - - if (isFrozen) { - Object.freeze(element); - Object.freeze(element.props); - } - - return element; - }; - - return (function (_Target) { - _inherits(_class, _Target); - - function _class() { - _classCallCheck(this, _class); - - _get(Object.getPrototypeOf(_class.prototype), 'constructor', this).apply(this, arguments); - } - - _createClass(_class, [{ - key: 'render', - value: function render() { - return linkClass(_get(Object.getPrototypeOf(_class.prototype), 'render', this).call(this)); - } - }]); - - return _class; - })(Target); -}; - -module.exports = exports['default']; \ No newline at end of file diff --git a/dist/index.js b/dist/index.js index 6424571..b99afe3 100644 --- a/dist/index.js +++ b/dist/index.js @@ -18,6 +18,10 @@ var _linkClass = require('./linkClass'); var _linkClass2 = _interopRequireDefault(_linkClass); +var _makeConfig = require('./makeConfig'); + +var _makeConfig2 = _interopRequireDefault(_makeConfig); + var functionConstructor = undefined, decoratorConstructor = undefined; @@ -44,23 +48,7 @@ functionConstructor = function (Component, styles) { _createClass(_class, [{ key: 'render', value: function render() { - if (options.allowMultiple !== false) { - options.allowMultiple = true; - } - - if (options.includeOriginal !== false) { - options.includeOriginal = true; - } - - if (options.errorNotFound !== true) { - options.errorNotFound = false; - } - - if (options.useModuleNames !== true) { - options.useModuleNames = false; - } - - return (0, _linkClass2['default'])(_get(Object.getPrototypeOf(_class.prototype), 'render', this).call(this), styles, options); + return (0, _linkClass2['default'])(_get(Object.getPrototypeOf(_class.prototype), 'render', this).call(this), styles, (0, _makeConfig2['default'])(options)); } }]); diff --git a/dist/linkClass.js b/dist/linkClass.js index 82883fb..d258b34 100644 --- a/dist/linkClass.js +++ b/dist/linkClass.js @@ -12,14 +12,6 @@ var _react2 = _interopRequireDefault(_react); var linkClass = undefined; -/** - * @typedef CSSModules~Options - * @property {Boolean} allowMultiple Determines whether `className` can have multiple class names. Throws an error when the constrained is not met. Default: true. - * @property {Boolean} keepOriginal Determines whether the original `className` value is kept in addition to the appended CSS modules styles CSS class name. Default: true. - * @property {Boolean} errorNotFound Determines whether an error is raised if `className` defines a CSS class(es) that is not present in the CSS modules styles. Default: false. - * @property {Boolean} useModuleName When enabled then CSS Modules are loaded using `moduleName` property and `className` is used only for global CSS. Default: false. - */ - /** * @param {ReactElement} element * @param {Object} styles CSS modules class map. diff --git a/dist/makeConfig.js b/dist/makeConfig.js new file mode 100644 index 0000000..9f3808a --- /dev/null +++ b/dist/makeConfig.js @@ -0,0 +1,60 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _utils = require('./utils'); + +var _utils2 = _interopRequireDefault(_utils); + +/** + * @see {@link https://github.com/gajus/react-css-modules#options} + * @property {Boolean} allowMultiple + * @property {Boolean} keepOriginal + * @property {Boolean} errorNotFound + * @property {Boolean} useModuleNam + */ + +exports['default'] = function () { + var userConfig = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; + + var knownProperties = undefined, + unknownProperties = undefined; + + knownProperties = ['allowMultiple', 'includeOriginal', 'errorNotFound', 'useModuleName']; + + unknownProperties = _utils2['default'].difference(_utils2['default'].keys(userConfig), knownProperties); + + if (unknownProperties.length) { + throw new Error('Unknown config property "' + unknownProperties[0] + '".'); + } + + _utils2['default'].forEach(userConfig, function (value, name) { + if (typeof value !== 'boolean') { + throw new Error('"' + name + '" property value must be a boolean.'); + } + }); + + if (typeof userConfig.allowMultiple === 'undefined') { + userConfig.allowMultiple = true; + } + + if (typeof userConfig.includeOriginal === 'undefined') { + userConfig.includeOriginal = true; + } + + if (typeof userConfig.errorNotFound === 'undefined') { + userConfig.errorNotFound = false; + } + + if (typeof userConfig.useModuleName === 'undefined') { + userConfig.useModuleName = false; + } + + return userConfig; +}; + +module.exports = exports['default']; \ No newline at end of file diff --git a/dist/utils.js b/dist/utils.js new file mode 100644 index 0000000..94ee160 --- /dev/null +++ b/dist/utils.js @@ -0,0 +1,26 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _lodashObjectKeys = require('lodash/object/keys'); + +var _lodashObjectKeys2 = _interopRequireDefault(_lodashObjectKeys); + +var _lodashArrayDifference = require('lodash/array/difference'); + +var _lodashArrayDifference2 = _interopRequireDefault(_lodashArrayDifference); + +var _lodashCollectionForEach = require('lodash/collection/forEach'); + +var _lodashCollectionForEach2 = _interopRequireDefault(_lodashCollectionForEach); + +exports['default'] = { + keys: _lodashObjectKeys2['default'], + difference: _lodashArrayDifference2['default'], + forEach: _lodashCollectionForEach2['default'] +}; +module.exports = exports['default']; \ No newline at end of file diff --git a/src/index.js b/src/index.js index b3fce2f..f933809 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,5 @@ import linkClass from './linkClass'; +import makeConfig from './makeConfig'; let functionConstructor, decoratorConstructor; @@ -14,23 +15,7 @@ let functionConstructor, functionConstructor = (Component, styles, options = {}) => { return class extends Component { render () { - if (options.allowMultiple !== false) { - options.allowMultiple = true; - } - - if (options.includeOriginal !== false) { - options.includeOriginal = true; - } - - if (options.errorNotFound !== true) { - options.errorNotFound = false; - } - - if (options.useModuleNames !== true) { - options.useModuleNames = false; - } - - return linkClass(super.render(), styles, options); + return linkClass(super.render(), styles, makeConfig(options)); } }; }; diff --git a/src/linkClass.js b/src/linkClass.js index 1dcdbb4..d54a045 100644 --- a/src/linkClass.js +++ b/src/linkClass.js @@ -2,14 +2,6 @@ import React from 'react'; let linkClass; -/** - * @typedef CSSModules~Options - * @property {Boolean} allowMultiple Determines whether `className` can have multiple class names. Throws an error when the constrained is not met. Default: true. - * @property {Boolean} keepOriginal Determines whether the original `className` value is kept in addition to the appended CSS modules styles CSS class name. Default: true. - * @property {Boolean} errorNotFound Determines whether an error is raised if `className` defines a CSS class(es) that is not present in the CSS modules styles. Default: false. - * @property {Boolean} useModuleName When enabled then CSS Modules are loaded using `moduleName` property and `className` is used only for global CSS. Default: false. - */ - /** * @param {ReactElement} element * @param {Object} styles CSS modules class map. diff --git a/src/makeConfig.js b/src/makeConfig.js new file mode 100644 index 0000000..d3c66f7 --- /dev/null +++ b/src/makeConfig.js @@ -0,0 +1,50 @@ +import _ from './utils'; + +/** + * @see {@link https://github.com/gajus/react-css-modules#options} + * @property {Boolean} allowMultiple + * @property {Boolean} keepOriginal + * @property {Boolean} errorNotFound + * @property {Boolean} useModuleNam + */ +export default (userConfig = {}) => { + let knownProperties, + unknownProperties; + + knownProperties = [ + 'allowMultiple', + 'includeOriginal', + 'errorNotFound', + 'useModuleName' + ]; + + unknownProperties = _.difference(_.keys(userConfig), knownProperties); + + if (unknownProperties.length) { + throw new Error(`Unknown config property "${unknownProperties[0]}".`); + } + + _.forEach(userConfig, (value, name) => { + if (typeof value !== 'boolean') { + throw new Error(`"${name}" property value must be a boolean.`); + } + }); + + if (typeof userConfig.allowMultiple === 'undefined') { + userConfig.allowMultiple = true; + } + + if (typeof userConfig.includeOriginal === 'undefined') { + userConfig.includeOriginal = true; + } + + if (typeof userConfig.errorNotFound === 'undefined') { + userConfig.errorNotFound = false; + } + + if (typeof userConfig.useModuleName === 'undefined') { + userConfig.useModuleName = false; + } + + return userConfig; +}; diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 0000000..861dd24 --- /dev/null +++ b/src/utils.js @@ -0,0 +1,9 @@ +import keys from 'lodash/object/keys'; +import difference from 'lodash/array/difference'; +import forEach from 'lodash/collection/forEach'; + +export default { + keys, + difference, + forEach +}; diff --git a/test/makeConfig.js b/test/makeConfig.js new file mode 100644 index 0000000..3b8edad --- /dev/null +++ b/test/makeConfig.js @@ -0,0 +1,54 @@ +import { + expect +} from 'chai'; + +import makeConfig from './../dist/makeConfig'; + +describe('makeConfig', () => { + describe('when using default config', () => { + let options; + beforeEach(() => { + options = makeConfig(); + }); + describe('allowMultiple property', () => { + it('defaults to true', () => { + expect(options.allowMultiple).to.equal(true); + }); + }); + describe('includeOriginal property', () => { + it('defaults to true', () => { + expect(options.includeOriginal).to.equal(true); + }); + }); + + describe('errorNotFound property', () => { + it('defaults to true', () => { + expect(options.errorNotFound).to.equal(false); + }); + }); + + describe('useModuleName property', () => { + it('defaults to true', () => { + expect(options.useModuleName).to.equal(false); + }); + }); + }); + describe('when unknown property is provided', () => { + it('throws an error', () => { + expect(() => { + makeConfig({ + unknownProperty: true + }); + }).to.throw(Error, 'Unknown config property "unknownProperty".'); + }); + }); + describe('when property value is not boolean', () => { + it('throws an error', () => { + expect(() => { + makeConfig({ + useModuleName: 1 + }); + }).to.throw(Error, '"useModuleName" property value must be a boolean.'); + }); + }); +}); From d45feb69c361f1d042876bd3541b63ffec69f451 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Wed, 26 Aug 2015 21:35:43 +0100 Subject: [PATCH 005/229] 1.3.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3bb785a..6778312 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "css", "modules" ], - "version": "1.2.0", + "version": "1.3.0", "author": { "name": "Gajus Kuizinas", "email": "gk@anuary.com", From 015c86094e9870548b0a2f67df15aa9f7c115413 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Wed, 26 Aug 2015 22:01:20 +0100 Subject: [PATCH 006/229] Use eslint in the build process. --- .eslintrc | 9 ++++++--- dist/index.js | 4 ++-- dist/linkClass.js | 15 ++++++++------- dist/makeConfig.js | 1 + package.json | 5 ++++- src/index.js | 4 ++-- src/linkClass.js | 17 +++++++++-------- src/makeConfig.js | 1 + test/linkClass.js | 2 +- test/makeConfig.js | 1 + 10 files changed, 35 insertions(+), 24 deletions(-) diff --git a/.eslintrc b/.eslintrc index 140c7d1..a2a446a 100644 --- a/.eslintrc +++ b/.eslintrc @@ -254,7 +254,7 @@ "no-array-constructor": 2, "no-continue": 2, "no-inline-comments": 2, - "no-lonely-if": 2, + "no-lonely-if": 0, "no-mixed-spaces-and-tabs": 2, "no-multiple-empty-lines": [ 2, @@ -382,9 +382,12 @@ "react" ], "parser": "babel-eslint", + "globals": { + "global": true + }, "env": { - "browser": true - // "mocha": true, + "browser": true, + "mocha": true // "node": true } } diff --git a/dist/index.js b/dist/index.js index b99afe3..a4008bd 100644 --- a/dist/index.js +++ b/dist/index.js @@ -22,8 +22,8 @@ var _makeConfig = require('./makeConfig'); var _makeConfig2 = _interopRequireDefault(_makeConfig); -var functionConstructor = undefined, - decoratorConstructor = undefined; +var decoratorConstructor = undefined, + functionConstructor = undefined; /** * When used as a function. diff --git a/dist/linkClass.js b/dist/linkClass.js index d258b34..d28ab89 100644 --- a/dist/linkClass.js +++ b/dist/linkClass.js @@ -22,11 +22,12 @@ linkClass = function (element) { var styles = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; - var newProps = undefined, - newClassName = undefined, + var childrenCount = undefined, + clonedElement = undefined, + moduleName = undefined, newChildren = undefined, - childrenCount = undefined, - moduleName = undefined; + newClassName = undefined, + newProps = undefined; if (options.useModuleName) { moduleName = element.props.moduleName; @@ -95,12 +96,12 @@ linkClass = function (element) { } if (newChildren) { - element = _react2['default'].cloneElement(element, newProps, newChildren); + clonedElement = _react2['default'].cloneElement(element, newProps, newChildren); } else { - element = _react2['default'].cloneElement(element, newProps); + clonedElement = _react2['default'].cloneElement(element, newProps); } - return element; + return clonedElement; }; exports['default'] = linkClass; diff --git a/dist/makeConfig.js b/dist/makeConfig.js index 9f3808a..dbff05c 100644 --- a/dist/makeConfig.js +++ b/dist/makeConfig.js @@ -16,6 +16,7 @@ var _utils2 = _interopRequireDefault(_utils); * @property {Boolean} keepOriginal * @property {Boolean} errorNotFound * @property {Boolean} useModuleNam + * @return {Object} */ exports['default'] = function () { diff --git a/package.json b/package.json index 6778312..71a46e1 100644 --- a/package.json +++ b/package.json @@ -27,14 +27,17 @@ }, "devDependencies": { "babel": "^5.8.21", + "babel-eslint": "^4.1.0", "chai": "^3.2.0", + "eslint": "^1.2.1", + "eslint-plugin-react": "^3.3.0", "jsdom": "^6.2.0", "mocha": "^2.2.5", "react": "^0.14.0-beta3", "react-addons-test-utils": "^0.14.0-beta3" }, "scripts": { - "test": "mocha", + "test": "./node_modules/.bin/eslint ./src/ ./test/ && mocha", "build": "babel ./src/ --out-dir ./dist/", "watch": "babel --watch ./src/ --out-dir ./dist/" } diff --git a/src/index.js b/src/index.js index f933809..7bb6ecb 100644 --- a/src/index.js +++ b/src/index.js @@ -1,8 +1,8 @@ import linkClass from './linkClass'; import makeConfig from './makeConfig'; -let functionConstructor, - decoratorConstructor; +let decoratorConstructor, + functionConstructor; /** * When used as a function. diff --git a/src/linkClass.js b/src/linkClass.js index d54a045..82fb397 100644 --- a/src/linkClass.js +++ b/src/linkClass.js @@ -9,11 +9,12 @@ let linkClass; * @return {ReactElement} */ linkClass = (element, styles = {}, options = {}) => { - let newProps, - newClassName, + let childrenCount, + clonedElement, + moduleName, newChildren, - childrenCount, - moduleName; + newClassName, + newProps; if (options.useModuleName) { moduleName = element.props.moduleName; @@ -50,7 +51,7 @@ linkClass = (element, styles = {}, options = {}) => { } }); - newClassName = newClassName.filter(function (className) { + newClassName = newClassName.filter((className) => { return className.length; }); @@ -83,12 +84,12 @@ linkClass = (element, styles = {}, options = {}) => { } if (newChildren) { - element = React.cloneElement(element, newProps, newChildren); + clonedElement = React.cloneElement(element, newProps, newChildren); } else { - element = React.cloneElement(element, newProps); + clonedElement = React.cloneElement(element, newProps); } - return element; + return clonedElement; }; export default linkClass; diff --git a/src/makeConfig.js b/src/makeConfig.js index d3c66f7..9d860ea 100644 --- a/src/makeConfig.js +++ b/src/makeConfig.js @@ -6,6 +6,7 @@ import _ from './utils'; * @property {Boolean} keepOriginal * @property {Boolean} errorNotFound * @property {Boolean} useModuleNam + * @return {Object} */ export default (userConfig = {}) => { let knownProperties, diff --git a/test/linkClass.js b/test/linkClass.js index b9b7c82..12d158a 100644 --- a/test/linkClass.js +++ b/test/linkClass.js @@ -169,7 +169,7 @@ describe('linkClass', () => { describe('when options.errorNotFound is true', () => { it('throws an error when className defines a CSS class that does not exist in CSS modules styles', () => { expect(() => { - linkClass(
, {}, {errorNotFound: true}) + linkClass(
, {}, {errorNotFound: true}); }).to.throw(Error, '"foo" CSS class name is not found in CSS modules styles.'); }); }); diff --git a/test/makeConfig.js b/test/makeConfig.js index 3b8edad..465cb74 100644 --- a/test/makeConfig.js +++ b/test/makeConfig.js @@ -7,6 +7,7 @@ import makeConfig from './../dist/makeConfig'; describe('makeConfig', () => { describe('when using default config', () => { let options; + beforeEach(() => { options = makeConfig(); }); From ed12c02dcfb9544146782a5bd3ec138f3a762003 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Thu, 27 Aug 2015 11:12:11 +0100 Subject: [PATCH 007/229] V2 using moduleName. --- README.md | 124 ++++++++++++++++------------------- dist/index.js | 4 +- dist/linkClass.js | 56 +++++++--------- dist/makeConfig.js | 27 ++++---- src/index.js | 4 +- src/linkClass.js | 56 +++++++--------- src/makeConfig.js | 29 ++++---- test/linkClass.js | 160 ++++++++++++++++++--------------------------- test/makeConfig.js | 29 ++------ 9 files changed, 198 insertions(+), 291 deletions(-) diff --git a/README.md b/README.md index 1ac8d2e..3f9d770 100644 --- a/README.md +++ b/README.md @@ -3,28 +3,27 @@ [![Travis build status](http://img.shields.io/travis/gajus/react-css-modules/master.svg?style=flat)](https://travis-ci.org/gajus/react-css-modules) [![NPM version](http://img.shields.io/npm/v/react-css-modules.svg?style=flat)](https://www.npmjs.org/package/react-css-modules) -React CSS Modules implement automatic mapping of class names to CSS modules. Every CSS class is assigned a local-scoped identifier with a global unique name. CSS Modules enable a modular and reusable CSS! +React CSS Modules implement automatic mapping of CSS modules. Every CSS class is assigned a local-scoped identifier with a global unique name. CSS Modules enable a modular and reusable CSS! - [What's the Problem?](#whats-the-problem) +- [The Implementation](#theimplementation) - [Usage](#usage) - [Module Bundler](#module-bundler) - [webpack](#webpack) - [Browserify](#browserify) - [Decorator](#decorator) - [Options](#options) - - [`useModuleName`](#usemodulename) - [`allowMultiple`](#allowmultiple) - - [`keepOriginal`](#keeporiginal) - - [`errorNotFound`](#errornotfound) + - [`errorWhenNotFound`](#errorwhennotfound) - [SASS, SCSS, LESS and other CSS Preprocessors](#sass-scss-less-and-other-css-preprocessors) - [Global CSS](#global-css) - [Multiple CSS Classes](#multiple-css-classes) ## What's the Problem? -[CSS modules](https://github.com/css-modules/css-modules) are awesome. If you are not familiar with CSS modules, it is a concept of using a module bundler such as [webpack](http://webpack.github.io/docs/) to load CSS scoped to a particular document. CSS modules loader will generate a unique name for a each CSS class at the time of loading the CSS. Refer to [webpack-demo](https://css-modules.github.io/webpack-demo/) for a full example. +[CSS Modules](https://github.com/css-modules/css-modules) are awesome. If you are not familiar with CSS Modules, it is a concept of using a module bundler such as [webpack](http://webpack.github.io/docs/) to load CSS scoped to a particular document. CSS module loader will generate a unique name for a each CSS class at the time of loading the CSS document ([Interoperable CSS](https://github.com/css-modules/icss) to be precise). To see CSS Modules in practice, [webpack-demo](https://css-modules.github.io/webpack-demo/). -In the context of React, this looks like this: +In the context of React, CSS Modules look like this: ```js import React from 'react'; @@ -43,9 +42,9 @@ export default class Car extends React.Component { Rendering the component will produce a markup similar to: ```js -
-
front-door
-
back-door
+
+
front-door
+
back-door
``` @@ -57,8 +56,10 @@ However, this approach has several disadvantages: * You have to use `camelCase` CSS class names. * You have to use `styles` object whenever constructing a `className`. +* Mixing CSS Modules and global CSS classes is cumbersome. +* Reference to an undefined CSS Module resolves to `undefined` without a warning. -React CSS Modules enables seamless CSS modules for React, e.g. +React CSS Modules component automates loading of CSS Modules using `moduleName` property, e.g. ```js import React from 'react'; @@ -67,9 +68,9 @@ import CSSModules from 'react-css-modules'; class Car extends React.Component { render () { - return
-
-
+ return
+
+
; } } @@ -77,9 +78,22 @@ class Car extends React.Component { export default CSSModules(Car, styles); ``` -`CSSModules` extends `Car` `render` method. It will look for CSS classes in `./car.css` that match CSS class names in `ReactElement` `className` and will replace/append the matching unique class names to `className` declaration. +Using `react-css-modules`: + +* You are not forced to use the `camelCase` naming convention. +* You do not need to refer to the `styles` object every time you use a CSS Module. +* There is clear distinction between global CSS and CSS Modules, e.g. + +```js +
+``` + +* You are warned when `moduleName` refers to an undefined CSS Module ([`errorWhenNotFound`](#errorwhennotfound) option). +* You can enforce use of a single CSS module per `ReactElement` ([`allowMultiple`](#allowmultiple) option). -Refer to the [react-css-modules-examples](https://github.com/gajus/react-css-modules-examples) repository for a complete usage example. +## The Implementation + +`react-css-modules` extends `render` method of the target component. It will use the value of `moduleName` to look for CSS Modules in the associated styles object and will append the matching unique CSS class names to the `ReactElement` `className` property value. [Awesome!](https://twitter.com/intent/retweet?tweet_id=636497036603428864) @@ -87,7 +101,7 @@ Refer to the [react-css-modules-examples](https://github.com/gajus/react-css-mod Setup consists of: -* Setting up a module bundler to load your [ICSS](https://github.com/css-modules/icss). +* Setting up a [module bundler](#modulebundler) to load the [Interoperable CSS](https://github.com/css-modules/icss). * [Decorating](#decorator) your component using `react-css-modules`. ### Module Bundler @@ -95,8 +109,8 @@ Setup consists of: #### webpack * Install [`style-loader`](https://www.npmjs.com/package/style-loader) and [`css-loader`](https://www.npmjs.com/package/css-loader). -* You will also need to use [`extract-text-webpack-plugin`](https://www.npmjs.com/package/extract-text-webpack-plugin) to aggregate the CSS into a single file. -* Setup a `/\.css$/` loader: +* You need to use [`extract-text-webpack-plugin`](https://www.npmjs.com/package/extract-text-webpack-plugin) to aggregate the CSS into a single file. +* Setup `/\.css$/` loader: ```js { @@ -113,7 +127,7 @@ new ExtractTextPlugin('app.css', { }) ``` -Refer to [webpack-demo](https://github.com/css-modules/webpack-demo) or [react-css-modules-examples](https://github.com/gajus/react-css-modules-examples) for a complete setup. +Refer to [webpack-demo](https://github.com/css-modules/webpack-demo) or [react-css-modules-examples](https://github.com/gajus/react-css-modules-examples) for an example of a complete setup. #### Browserify @@ -124,16 +138,16 @@ Refer to [`css-modulesify`](https://github.com/css-modules/css-modulesify). ```js /** * @typedef CSSModules~Options - * @property {Boolean} allowMultiple Determines whether `className` can have multiple class names. Throws an error when the constrained is not met. Default: true. - * @property {Boolean} keepOriginal Determines whether the original `className` value is kept in addition to the appended CSS modules styles CSS class name. Default: true. - * @property {Boolean} errorNotFound Determines whether an error is raised if `className` defines a CSS class(es) that is not present in the CSS modules styles. Default: false. + * @see {@link https://github.com/gajus/react-css-modules#options} + * @property {Boolean} allowMultiple + * @property {Boolean} errorWhenNotFound */ /** - * @param {ReactClass} Component - * @param {Object} styles CSS modules class map. + * @param {Function} Component + * @param {Object} styles CSS Modules class map. * @param {CSSModules~Options} options - * @return {ReactClass} + * @return {Function} */ ``` @@ -178,6 +192,8 @@ export default class extends React.Component { [Awesome!](https://twitter.com/intent/retweet?tweet_id=636497036603428864) +Refer to the [react-css-modules-examples](https://github.com/gajus/react-css-modules-examples) repository for an example of webpack setup. + ### Options Options are supplied as the third parameter to the `CSSModules` function. @@ -186,57 +202,33 @@ Options are supplied as the third parameter to the `CSSModules` function. CSSModules(Component, styles, options); ``` -or as a second parameter when using `CSSModules` as a decorator: +or as a second parameter to the decorator: ```js @CSSModules(styles, options); ``` -#### `useModuleName` - -Default: `false`. - -When enabled then CSS Modules are loaded using `moduleName` property and `className` is used only for global CSS, e.g. - -```js -
-``` - #### `allowMultiple` -Default: `true`. +Default: `false`. -Allows multiple CSS class names. +Allows multiple CSS Module names. When `false`, the following will cause an error: ```js -
+
``` -#### `keepOriginal` +#### `errorWhenNotFound` Default: `true`. -Keeps original CSS class name in addition to the names of the CSS Modules. - -When `true`, the following `ReactElement`: - -```js -
-``` - -will be rendered with a `className` property `foo component__foo___2w27N bar component__bar__1oVw5`. - -#### `errorNotFound` - -Default: `false`. - -Throws an error when class name cannot be mapped to a CSS Module. +Throws an error when `moduleName` cannot be mapped to an existing CSS Module. ## SASS, SCSS, LESS and other CSS Preprocessors -[ICSS](https://github.com/css-modules/icss) is compatible with the CSS Preprocessors. All you need is to add the preprocessor to the chain of loaders, e.g. in the case of webpack it is as simple as installing `sass-loader` and adding `!sass` to the end of the `style-loader` loader chain declaration (loaders are processed from right to left): +[Interoperable CSS](https://github.com/css-modules/icss) is compatible with the CSS Preprocessors. To use a preprocessor, all you need to do is add the preprocessor to the chain of loaders, e.g. in the case of webpack it is as simple as installing `sass-loader` and adding `!sass` to the end of the `style-loader` loader query (loaders are processed from right to left): ```js { @@ -255,13 +247,11 @@ CSS Modules does not restrict you from using global CSS. } ``` -When using global CSS, you need to enable [`keepOriginal`](#keeporiginal) option. +However, use global CSS with caution. With CSS Modules, there are only a handful of valid use cases for global CSS (e.g. [normalization](https://github.com/necolas/normalize.css/)). -Use global CSS with caution. With CSS Modules, there are only a handful of valid use cases for global CSS (e.g. [normalization](https://github.com/necolas/normalize.css/)). +## Multiple CSS Modules -## Multiple CSS Classes - -CSS modules promote composition pattern, i.e. every CSS class that is used in a component should define all properties required to describe the element, e.g. +CSS Modules promote composition pattern, i.e. every CSS Module that is used in a component should define all properties required to describe an element, e.g. ```css .button { @@ -281,9 +271,11 @@ CSS modules promote composition pattern, i.e. every CSS class that is used in a } ``` -To learn more about composing CSS rules, I suggest reading Glen Maddern article about [CSS Modules](http://glenmaddern.com/articles/css-modules) and the official [CSS modules spec](https://github.com/css-modules/css-modules). +Composition promotes better separation of markup and style using semantics that would be hard to achieve without CSS Modules. + +To learn more about composing CSS rules, I suggest reading Glen Maddern article about [CSS Modules](http://glenmaddern.com/articles/css-modules) and the official [spec of the CSS Modules](https://github.com/css-modules/css-modules). -Using React CSS Modules, you can map as many CSS classes to the element as you want. `CSSModules` will append a unique class name for every class name it matches in the `className` declaration, e.g. +That said, if you enable [`allowMultiple`](#allowmultiple) option, you can map multiple CSS Modules to a single `ReactElement`. `react-css-modules` will append a unique class name for every CSS Module it matches in the `moduleName` declaration, e.g. ```css .button { @@ -296,9 +288,7 @@ Using React CSS Modules, you can map as many CSS classes to the element as you w ``` ```js -
+
``` -This will map both [ICSS](https://github.com/css-modules/icss) CSS classes to the target element. - -However, I encourage you to use composition whenever possible. Composition promotes better separation of markup from style sheets using semantics that would be hard to achieve without CSS modules. You can enforce one CSS class name per `className` using [`allowMultiple` option](#usage). +This will map both [Interoperable CSS](https://github.com/css-modules/icss) CSS classes to the target element. diff --git a/dist/index.js b/dist/index.js index a4008bd..f7f655d 100644 --- a/dist/index.js +++ b/dist/index.js @@ -29,7 +29,7 @@ var decoratorConstructor = undefined, * When used as a function. * * @param {Function} Component - * @param {Object} styles CSS modules class map. + * @param {Object} styles CSS Modules class map. * @param {Object} options {@link https://github.com/gajus/react-css-modules#options} * @return {Function} */ @@ -59,7 +59,7 @@ functionConstructor = function (Component, styles) { /** * When used as a ES7 decorator. * - * @param {Object} styles CSS modules class map. + * @param {Object} styles CSS Modules class map. * @param {Object} options {@link https://github.com/gajus/react-css-modules#options} * @return {Function} */ diff --git a/dist/linkClass.js b/dist/linkClass.js index d28ab89..7962d68 100644 --- a/dist/linkClass.js +++ b/dist/linkClass.js @@ -24,51 +24,37 @@ linkClass = function (element) { var childrenCount = undefined, clonedElement = undefined, - moduleName = undefined, + moduleNames = undefined, newChildren = undefined, - newClassName = undefined, - newProps = undefined; + newProps = undefined, + appendClassName = undefined; - if (options.useModuleName) { - moduleName = element.props.moduleName; + moduleNames = element.props.moduleName; - options.includeOriginal = false; - } else { - moduleName = element.props.className; - } - - if (moduleName) { - newClassName = moduleName.split(' '); + if (moduleNames) { + moduleNames = moduleNames.split(' '); - if (options.allowMultiple === false && newClassName.length > 1) { - throw new Error('ReactElement defines multiple class names ("' + element.props.className + '") in className declaration.'); + if (options.allowMultiple === false && moduleNames.length > 1) { + throw new Error('ReactElement moduleName property defines multiple module names ("' + element.props.moduleName + '").'); } - newClassName = newClassName.map(function (className) { - if (!styles[className] && options.errorNotFound === true) { - throw new Error('"' + className + '" CSS class name is not found in CSS modules styles.'); - } - - if (options.includeOriginal === false) { - if (styles[className]) { - return styles[className]; - } else { - return ''; - } + appendClassName = moduleNames.map(function (moduleName) { + if (styles[moduleName]) { + return styles[moduleName]; } else { - if (styles[className]) { - return className + ' ' + styles[className]; - } else { - return className; + if (options.errorWhenNotFound === true) { + throw new Error('"' + moduleName + '" CSS module is undefined.'); } + + return ''; } }); - newClassName = newClassName.filter(function (className) { + appendClassName = appendClassName.filter(function (className) { return className.length; }); - newClassName = newClassName.join(' '); + appendClassName = appendClassName.join(' '); } // A child can be either an array, a sole object or a string. @@ -89,9 +75,13 @@ linkClass = function (element) { } } - if (newClassName) { + if (appendClassName) { + if (element.props.className) { + appendClassName = element.props.className + ' ' + appendClassName; + } + newProps = { - className: newClassName + className: appendClassName }; } diff --git a/dist/makeConfig.js b/dist/makeConfig.js index dbff05c..b286719 100644 --- a/dist/makeConfig.js +++ b/dist/makeConfig.js @@ -11,12 +11,15 @@ var _utils = require('./utils'); var _utils2 = _interopRequireDefault(_utils); /** + * @typedef CSSModules~Options * @see {@link https://github.com/gajus/react-css-modules#options} * @property {Boolean} allowMultiple - * @property {Boolean} keepOriginal - * @property {Boolean} errorNotFound - * @property {Boolean} useModuleNam - * @return {Object} + * @property {Boolean} errorWhenNotFound + */ + +/** + * @param {Options} userConfig + * @return {CSSModules~Options} */ exports['default'] = function () { @@ -25,7 +28,7 @@ exports['default'] = function () { var knownProperties = undefined, unknownProperties = undefined; - knownProperties = ['allowMultiple', 'includeOriginal', 'errorNotFound', 'useModuleName']; + knownProperties = ['allowMultiple', 'errorWhenNotFound']; unknownProperties = _utils2['default'].difference(_utils2['default'].keys(userConfig), knownProperties); @@ -40,19 +43,11 @@ exports['default'] = function () { }); if (typeof userConfig.allowMultiple === 'undefined') { - userConfig.allowMultiple = true; - } - - if (typeof userConfig.includeOriginal === 'undefined') { - userConfig.includeOriginal = true; - } - - if (typeof userConfig.errorNotFound === 'undefined') { - userConfig.errorNotFound = false; + userConfig.allowMultiple = false; } - if (typeof userConfig.useModuleName === 'undefined') { - userConfig.useModuleName = false; + if (typeof userConfig.errorWhenNotFound === 'undefined') { + userConfig.errorWhenNotFound = false; } return userConfig; diff --git a/src/index.js b/src/index.js index 7bb6ecb..aab47ea 100644 --- a/src/index.js +++ b/src/index.js @@ -8,7 +8,7 @@ let decoratorConstructor, * When used as a function. * * @param {Function} Component - * @param {Object} styles CSS modules class map. + * @param {Object} styles CSS Modules class map. * @param {Object} options {@link https://github.com/gajus/react-css-modules#options} * @return {Function} */ @@ -23,7 +23,7 @@ functionConstructor = (Component, styles, options = {}) => { /** * When used as a ES7 decorator. * - * @param {Object} styles CSS modules class map. + * @param {Object} styles CSS Modules class map. * @param {Object} options {@link https://github.com/gajus/react-css-modules#options} * @return {Function} */ diff --git a/src/linkClass.js b/src/linkClass.js index 82fb397..f295866 100644 --- a/src/linkClass.js +++ b/src/linkClass.js @@ -11,51 +11,37 @@ let linkClass; linkClass = (element, styles = {}, options = {}) => { let childrenCount, clonedElement, - moduleName, + moduleNames, newChildren, - newClassName, - newProps; + newProps, + appendClassName; - if (options.useModuleName) { - moduleName = element.props.moduleName; + moduleNames = element.props.moduleName; - options.includeOriginal = false; - } else { - moduleName = element.props.className; - } - - if (moduleName) { - newClassName = moduleName.split(' '); + if (moduleNames) { + moduleNames = moduleNames.split(' '); - if (options.allowMultiple === false && newClassName.length > 1) { - throw new Error(`ReactElement defines multiple class names ("${element.props.className}") in className declaration.`); + if (options.allowMultiple === false && moduleNames.length > 1) { + throw new Error(`ReactElement moduleName property defines multiple module names ("${element.props.moduleName}").`); } - newClassName = newClassName.map((className) => { - if (!styles[className] && options.errorNotFound === true) { - throw new Error(`"${className}" CSS class name is not found in CSS modules styles.`); - } - - if (options.includeOriginal === false) { - if (styles[className]) { - return styles[className]; - } else { - return ''; - } + appendClassName = moduleNames.map((moduleName) => { + if (styles[moduleName]) { + return styles[moduleName]; } else { - if (styles[className]) { - return `${className} ${styles[className]}`; - } else { - return className; + if (options.errorWhenNotFound === true) { + throw new Error(`"${moduleName}" CSS module is undefined.`); } + + return ''; } }); - newClassName = newClassName.filter((className) => { + appendClassName = appendClassName.filter((className) => { return className.length; }); - newClassName = newClassName.join(' '); + appendClassName = appendClassName.join(' '); } // A child can be either an array, a sole object or a string. @@ -77,9 +63,13 @@ linkClass = (element, styles = {}, options = {}) => { } - if (newClassName) { + if (appendClassName) { + if (element.props.className) { + appendClassName = element.props.className + ' ' + appendClassName; + } + newProps = { - className: newClassName + className: appendClassName }; } diff --git a/src/makeConfig.js b/src/makeConfig.js index 9d860ea..f9c8715 100644 --- a/src/makeConfig.js +++ b/src/makeConfig.js @@ -1,12 +1,15 @@ import _ from './utils'; /** + * @typedef CSSModules~Options * @see {@link https://github.com/gajus/react-css-modules#options} * @property {Boolean} allowMultiple - * @property {Boolean} keepOriginal - * @property {Boolean} errorNotFound - * @property {Boolean} useModuleNam - * @return {Object} + * @property {Boolean} errorWhenNotFound + */ + +/** + * @param {Options} userConfig + * @return {CSSModules~Options} */ export default (userConfig = {}) => { let knownProperties, @@ -14,9 +17,7 @@ export default (userConfig = {}) => { knownProperties = [ 'allowMultiple', - 'includeOriginal', - 'errorNotFound', - 'useModuleName' + 'errorWhenNotFound' ]; unknownProperties = _.difference(_.keys(userConfig), knownProperties); @@ -32,19 +33,11 @@ export default (userConfig = {}) => { }); if (typeof userConfig.allowMultiple === 'undefined') { - userConfig.allowMultiple = true; - } - - if (typeof userConfig.includeOriginal === 'undefined') { - userConfig.includeOriginal = true; - } - - if (typeof userConfig.errorNotFound === 'undefined') { - userConfig.errorNotFound = false; + userConfig.allowMultiple = false; } - if (typeof userConfig.useModuleName === 'undefined') { - userConfig.useModuleName = false; + if (typeof userConfig.errorWhenNotFound === 'undefined') { + userConfig.errorWhenNotFound = false; } return userConfig; diff --git a/test/linkClass.js b/test/linkClass.js index 12d158a..d00fb15 100644 --- a/test/linkClass.js +++ b/test/linkClass.js @@ -8,19 +8,23 @@ import jsdom from 'jsdom'; import linkClass from './../dist/linkClass'; describe('linkClass', () => { - describe('when elements do not define className', () => { - it('does not affect the element declaration', () => { + context('when ReactElement does not define moduleName', () => { + it('does not affect element properties', () => { expect(linkClass(
)).to.deep.equal(
); }); - it('does not affect element with a single element child', () => { + it('does not affect element properties with a single element child', () => { expect(linkClass(

)).to.deep.equal(

); }); - it('does not affect element with a single text child', () => { + it('does not affect element properties with a single text child', () => { expect(linkClass(
test
)).to.deep.equal(
test
); }); + it('does not affect the className', () => { + expect(linkClass(
)).to.deep.equal(
); + }); + // Using array instead of object causes the following error: // Warning: Each child in an array or iterator should have a unique "key" prop. // Check the render method of _class. See https://fb.me/react-warning-keys for more information. @@ -29,74 +33,85 @@ describe('linkClass', () => { }); }); - describe('when element className does not match an existing CSS class', () => { - it('does not affect element className', () => { - let subject; - - subject =
; - - subject = linkClass(subject, {}); - - expect(subject.props.className).to.deep.equal('foo'); - }); - }); + context('when moduleName matches an existing CSS module', () => { + context('when ReactElement does not have an existing className', () => { + it('uses the generated class name to set the className property', () => { + let subject; - describe('when element className matches an existing CSS class', () => { - it('appends the generated class name to the className property', () => { - let subject; + subject =
; - subject =
; + subject = linkClass(subject, { + foo: 'foo-1' + }); - subject = linkClass(subject, { - foo: 'foo-1' + expect(subject.props.className).to.deep.equal('foo-1'); }); - - expect(subject.props.className).to.deep.equal('foo foo-1'); }); - }); - - describe('when element classNames refers to multiple CSS classes', () => { - describe('when all referenced CSS classes exist', () => { - it('appends a generated class name for every referenced CSS class', () => { + context('when ReactElement has an existing className', () => { + it('appends the generated class name to the className property', () => { let subject; - subject =
; + subject =
; subject = linkClass(subject, { - foo: 'foo-1', bar: 'bar-1' }); - expect(subject.props.className).to.deep.equal('foo foo-1 bar bar-1'); + expect(subject.props.className).to.deep.equal('foo bar-1'); }); }); - describe('when some referenced CSS classes exist', () => { - it('appends a generated class name for the matched CSS classes', () => { - let subject; - - subject =
; + }); - subject = linkClass(subject, { - foo: 'foo-1' + describe('options.allowMultiple', () => { + context('when multiple module names are used', () => { + context('when false', () => { + it('throws an error', () => { + expect(() => { + linkClass(
, {}, {allowMultiple: false}); + }).to.throw(Error, 'ReactElement moduleName property defines multiple module names ("foo bar").'); }); + }); + context('when true', () => { + it('appends a generated class name for every referenced CSS module', () => { + let subject; + + subject =
; + + subject = linkClass(subject, { + foo: 'foo-1', + bar: 'bar-1' + }, {allowMultiple: true}); - expect(subject.props.className).to.deep.equal('foo foo-1 bar'); + expect(subject.props.className).to.deep.equal('foo-1 bar-1'); + }); }); }); - describe('when none of the referenced CSS classes exist', () => { - it('does not append anything', () => { - let subject; + }); - subject =
; + describe('options.errorWhenNotFound', () => { + context('when moduleName does not match an existing CSS module', () => { + context('when false', () => { + it('ignores the missing CSS module', () => { + let subject; - subject = linkClass(subject, {}); + subject =
; - expect(subject.props.className).to.deep.equal('foo bar'); + subject = linkClass(subject, {}, {errorWhenNotFound: false}); + + expect(subject.props.className).to.be.an('undefined'); + }); + }); + context('when is true', () => { + it('throws an error', () => { + expect(() => { + linkClass(
, {}, {errorWhenNotFound: true}); + }).to.throw(Error, '"foo" CSS module is undefined.'); + }); }); }); }); - describe('when ReactElement includes ReactComponent', () => { + context('when ReactElement includes ReactComponent', () => { let Foo, nodeList; @@ -115,62 +130,17 @@ describe('linkClass', () => { Foo = class extends React.Component { render () { - return
Hello
; + return
Hello
; } }; - nodeList = TestUtils.renderIntoDocument(linkClass(
, {foo: 'foo-1'})); + nodeList = TestUtils.renderIntoDocument(linkClass(
, {foo: 'foo-1'})); }); it('processes ReactElement nodes', () => { - expect(nodeList.className).to.equal('foo foo-1'); + expect(nodeList.className).to.equal('foo-1'); }); it('does not process ReactComponent nodes', () => { - expect(nodeList.firstChild.className).to.equal('foo'); - }); - }); - - describe('when options.useModuleName is true', () => { - it('does not lookup the className property', () => { - let subject; - - subject = linkClass(
, {foo: 'foo-1'}, {useModuleName: true}); - - expect(subject.props.className).to.equal('foo'); - }); - it('appends CSS Modules using modulName', () => { - let subject; - - subject = linkClass(
, {foo: 'foo-1'}, {useModuleName: true}); - - expect(subject.props.className).to.equal('foo-1'); - }); - }); - - describe('when options.allowMultiple is false', () => { - describe('when it finds multiple CSS class names in a className', () => { - it('throws an error', () => { - expect(() => { - linkClass(
, {}, {allowMultiple: false}); - }).to.throw(Error, 'ReactElement defines multiple class names ("foo bar") in className declaration.'); - }); - }); - }); - - describe('when options.includeOriginal is false', () => { - it('does not include the original class name', () => { - let subject; - - subject = linkClass(
, {foo: 'foo-1'}, {includeOriginal: false}); - - expect(subject.props.className).to.equal('foo-1'); - }); - }); - - describe('when options.errorNotFound is true', () => { - it('throws an error when className defines a CSS class that does not exist in CSS modules styles', () => { - expect(() => { - linkClass(
, {}, {errorNotFound: true}); - }).to.throw(Error, '"foo" CSS class name is not found in CSS modules styles.'); + expect(nodeList.firstChild.className).to.equal(''); }); }); }); diff --git a/test/makeConfig.js b/test/makeConfig.js index 465cb74..a48bd86 100644 --- a/test/makeConfig.js +++ b/test/makeConfig.js @@ -12,25 +12,13 @@ describe('makeConfig', () => { options = makeConfig(); }); describe('allowMultiple property', () => { - it('defaults to true', () => { - expect(options.allowMultiple).to.equal(true); - }); - }); - describe('includeOriginal property', () => { - it('defaults to true', () => { - expect(options.includeOriginal).to.equal(true); + it('defaults to false', () => { + expect(options.allowMultiple).to.equal(false); }); }); - - describe('errorNotFound property', () => { + describe('errorWhenNotFound property', () => { it('defaults to true', () => { - expect(options.errorNotFound).to.equal(false); - }); - }); - - describe('useModuleName property', () => { - it('defaults to true', () => { - expect(options.useModuleName).to.equal(false); + expect(options.errorWhenNotFound).to.equal(false); }); }); }); @@ -43,13 +31,4 @@ describe('makeConfig', () => { }).to.throw(Error, 'Unknown config property "unknownProperty".'); }); }); - describe('when property value is not boolean', () => { - it('throws an error', () => { - expect(() => { - makeConfig({ - useModuleName: 1 - }); - }).to.throw(Error, '"useModuleName" property value must be a boolean.'); - }); - }); }); From 203e4e5a2e0ccb4bbbb58ca86961e09178a8436f Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Thu, 27 Aug 2015 11:12:16 +0100 Subject: [PATCH 008/229] 2.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 71a46e1..d07eb46 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "css", "modules" ], - "version": "1.3.0", + "version": "2.0.0", "author": { "name": "Gajus Kuizinas", "email": "gk@anuary.com", From 66e599663aaa6a94a9c52435479333f126846137 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Thu, 27 Aug 2015 11:12:58 +0100 Subject: [PATCH 009/229] 3.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d07eb46..84373ef 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "css", "modules" ], - "version": "2.0.0", + "version": "3.0.0", "author": { "name": "Gajus Kuizinas", "email": "gk@anuary.com", From ed7fa4fd107adfbb2fbc75bbad17bfea6cc01c33 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Thu, 27 Aug 2015 11:14:19 +0100 Subject: [PATCH 010/229] Ignore npm-debug.log. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 07e6e47..6edd850 100755 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /node_modules +npm-debug.log From 9e9b482531af64f9083d00365f20920c75a838ad Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Thu, 27 Aug 2015 11:14:24 +0100 Subject: [PATCH 011/229] 4.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 84373ef..db9a0e9 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "css", "modules" ], - "version": "3.0.0", + "version": "4.0.0", "author": { "name": "Gajus Kuizinas", "email": "gk@anuary.com", From b59e0403ccdc9ddcb887ce48d827716599c46b5c Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Thu, 27 Aug 2015 11:15:14 +0100 Subject: [PATCH 012/229] V2. Using localClassName instead of overloading className. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index db9a0e9..bc01bbf 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "css", "modules" ], - "version": "4.0.0", + "version": "5.0.0", "author": { "name": "Gajus Kuizinas", "email": "gk@anuary.com", From ba67d3641a09493f8b0bc02e89dddace231bffe5 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Thu, 27 Aug 2015 11:47:03 +0100 Subject: [PATCH 013/229] ESLint compliance. --- README.md | 22 +++++++++++----------- dist/linkClass.js | 26 +++++++++++++------------- package.json | 2 +- src/linkClass.js | 28 ++++++++++++++-------------- test/linkClass.js | 24 ++++++++++++------------ 5 files changed, 51 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index 3f9d770..74c7337 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ However, this approach has several disadvantages: * Mixing CSS Modules and global CSS classes is cumbersome. * Reference to an undefined CSS Module resolves to `undefined` without a warning. -React CSS Modules component automates loading of CSS Modules using `moduleName` property, e.g. +React CSS Modules component automates loading of CSS Modules using `localClassName` property, e.g. ```js import React from 'react'; @@ -68,9 +68,9 @@ import CSSModules from 'react-css-modules'; class Car extends React.Component { render () { - return
-
-
+ return
+
+
; } } @@ -85,15 +85,15 @@ Using `react-css-modules`: * There is clear distinction between global CSS and CSS Modules, e.g. ```js -
+
``` -* You are warned when `moduleName` refers to an undefined CSS Module ([`errorWhenNotFound`](#errorwhennotfound) option). +* You are warned when `localClassName` refers to an undefined CSS Module ([`errorWhenNotFound`](#errorwhennotfound) option). * You can enforce use of a single CSS module per `ReactElement` ([`allowMultiple`](#allowmultiple) option). ## The Implementation -`react-css-modules` extends `render` method of the target component. It will use the value of `moduleName` to look for CSS Modules in the associated styles object and will append the matching unique CSS class names to the `ReactElement` `className` property value. +`react-css-modules` extends `render` method of the target component. It will use the value of `localClassName` to look for CSS Modules in the associated styles object and will append the matching unique CSS class names to the `ReactElement` `className` property value. [Awesome!](https://twitter.com/intent/retweet?tweet_id=636497036603428864) @@ -217,14 +217,14 @@ Allows multiple CSS Module names. When `false`, the following will cause an error: ```js -
+
``` #### `errorWhenNotFound` Default: `true`. -Throws an error when `moduleName` cannot be mapped to an existing CSS Module. +Throws an error when `localClassName` cannot be mapped to an existing CSS Module. ## SASS, SCSS, LESS and other CSS Preprocessors @@ -275,7 +275,7 @@ Composition promotes better separation of markup and style using semantics that To learn more about composing CSS rules, I suggest reading Glen Maddern article about [CSS Modules](http://glenmaddern.com/articles/css-modules) and the official [spec of the CSS Modules](https://github.com/css-modules/css-modules). -That said, if you enable [`allowMultiple`](#allowmultiple) option, you can map multiple CSS Modules to a single `ReactElement`. `react-css-modules` will append a unique class name for every CSS Module it matches in the `moduleName` declaration, e.g. +That said, if you enable [`allowMultiple`](#allowmultiple) option, you can map multiple CSS Modules to a single `ReactElement`. `react-css-modules` will append a unique class name for every CSS Module it matches in the `localClassName` declaration, e.g. ```css .button { @@ -288,7 +288,7 @@ That said, if you enable [`allowMultiple`](#allowmultiple) option, you can map m ``` ```js -
+
``` This will map both [Interoperable CSS](https://github.com/css-modules/icss) CSS classes to the target element. diff --git a/dist/linkClass.js b/dist/linkClass.js index 7962d68..111a10f 100644 --- a/dist/linkClass.js +++ b/dist/linkClass.js @@ -22,28 +22,28 @@ linkClass = function (element) { var styles = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; - var childrenCount = undefined, + var appendClassName = undefined, + childrenCount = undefined, clonedElement = undefined, - moduleNames = undefined, + localClassNames = undefined, newChildren = undefined, - newProps = undefined, - appendClassName = undefined; + newProps = undefined; - moduleNames = element.props.moduleName; + localClassNames = element.props.localClassName; - if (moduleNames) { - moduleNames = moduleNames.split(' '); + if (localClassNames) { + localClassNames = localClassNames.split(' '); - if (options.allowMultiple === false && moduleNames.length > 1) { - throw new Error('ReactElement moduleName property defines multiple module names ("' + element.props.moduleName + '").'); + if (options.allowMultiple === false && localClassNames.length > 1) { + throw new Error('ReactElement localClassName property defines multiple module names ("' + element.props.localClassName + '").'); } - appendClassName = moduleNames.map(function (moduleName) { - if (styles[moduleName]) { - return styles[moduleName]; + appendClassName = localClassNames.map(function (localClassName) { + if (styles[localClassName]) { + return styles[localClassName]; } else { if (options.errorWhenNotFound === true) { - throw new Error('"' + moduleName + '" CSS module is undefined.'); + throw new Error('"' + localClassName + '" CSS module is undefined.'); } return ''; diff --git a/package.json b/package.json index bc01bbf..d07eb46 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "css", "modules" ], - "version": "5.0.0", + "version": "2.0.0", "author": { "name": "Gajus Kuizinas", "email": "gk@anuary.com", diff --git a/src/linkClass.js b/src/linkClass.js index f295866..9c7dffe 100644 --- a/src/linkClass.js +++ b/src/linkClass.js @@ -9,28 +9,28 @@ let linkClass; * @return {ReactElement} */ linkClass = (element, styles = {}, options = {}) => { - let childrenCount, + let appendClassName, + childrenCount, clonedElement, - moduleNames, + localClassNames, newChildren, - newProps, - appendClassName; + newProps; - moduleNames = element.props.moduleName; + localClassNames = element.props.localClassName; - if (moduleNames) { - moduleNames = moduleNames.split(' '); + if (localClassNames) { + localClassNames = localClassNames.split(' '); - if (options.allowMultiple === false && moduleNames.length > 1) { - throw new Error(`ReactElement moduleName property defines multiple module names ("${element.props.moduleName}").`); + if (options.allowMultiple === false && localClassNames.length > 1) { + throw new Error(`ReactElement localClassName property defines multiple module names ("${element.props.localClassName}").`); } - appendClassName = moduleNames.map((moduleName) => { - if (styles[moduleName]) { - return styles[moduleName]; + appendClassName = localClassNames.map((localClassName) => { + if (styles[localClassName]) { + return styles[localClassName]; } else { if (options.errorWhenNotFound === true) { - throw new Error(`"${moduleName}" CSS module is undefined.`); + throw new Error(`"${localClassName}" CSS module is undefined.`); } return ''; @@ -65,7 +65,7 @@ linkClass = (element, styles = {}, options = {}) => { if (appendClassName) { if (element.props.className) { - appendClassName = element.props.className + ' ' + appendClassName; + appendClassName = `${element.props.className} ${appendClassName}`; } newProps = { diff --git a/test/linkClass.js b/test/linkClass.js index d00fb15..d805d51 100644 --- a/test/linkClass.js +++ b/test/linkClass.js @@ -8,7 +8,7 @@ import jsdom from 'jsdom'; import linkClass from './../dist/linkClass'; describe('linkClass', () => { - context('when ReactElement does not define moduleName', () => { + context('when ReactElement does not define localClassName', () => { it('does not affect element properties', () => { expect(linkClass(
)).to.deep.equal(
); }); @@ -33,12 +33,12 @@ describe('linkClass', () => { }); }); - context('when moduleName matches an existing CSS module', () => { + context('when localClassName matches an existing CSS module', () => { context('when ReactElement does not have an existing className', () => { it('uses the generated class name to set the className property', () => { let subject; - subject =
; + subject =
; subject = linkClass(subject, { foo: 'foo-1' @@ -51,7 +51,7 @@ describe('linkClass', () => { it('appends the generated class name to the className property', () => { let subject; - subject =
; + subject =
; subject = linkClass(subject, { bar: 'bar-1' @@ -67,15 +67,15 @@ describe('linkClass', () => { context('when false', () => { it('throws an error', () => { expect(() => { - linkClass(
, {}, {allowMultiple: false}); - }).to.throw(Error, 'ReactElement moduleName property defines multiple module names ("foo bar").'); + linkClass(
, {}, {allowMultiple: false}); + }).to.throw(Error, 'ReactElement localClassName property defines multiple module names ("foo bar").'); }); }); context('when true', () => { it('appends a generated class name for every referenced CSS module', () => { let subject; - subject =
; + subject =
; subject = linkClass(subject, { foo: 'foo-1', @@ -89,12 +89,12 @@ describe('linkClass', () => { }); describe('options.errorWhenNotFound', () => { - context('when moduleName does not match an existing CSS module', () => { + context('when localClassName does not match an existing CSS module', () => { context('when false', () => { it('ignores the missing CSS module', () => { let subject; - subject =
; + subject =
; subject = linkClass(subject, {}, {errorWhenNotFound: false}); @@ -104,7 +104,7 @@ describe('linkClass', () => { context('when is true', () => { it('throws an error', () => { expect(() => { - linkClass(
, {}, {errorWhenNotFound: true}); + linkClass(
, {}, {errorWhenNotFound: true}); }).to.throw(Error, '"foo" CSS module is undefined.'); }); }); @@ -130,11 +130,11 @@ describe('linkClass', () => { Foo = class extends React.Component { render () { - return
Hello
; + return
Hello
; } }; - nodeList = TestUtils.renderIntoDocument(linkClass(
, {foo: 'foo-1'})); + nodeList = TestUtils.renderIntoDocument(linkClass(
, {foo: 'foo-1'})); }); it('processes ReactElement nodes', () => { expect(nodeList.className).to.equal('foo-1'); From 4b00b4206e6bd7ce0678077f229591b2dc795e98 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Thu, 27 Aug 2015 12:13:28 +0100 Subject: [PATCH 014/229] V3. Reading Christopher Chedeau presentation on CSS in JS made me rethik my decision of using localClassName convention. styleName it is. --- dist/linkClass.js | 20 ++++++++++---------- package.json | 2 +- src/linkClass.js | 20 ++++++++++---------- test/linkClass.js | 24 ++++++++++++------------ 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/dist/linkClass.js b/dist/linkClass.js index 111a10f..6c44a3d 100644 --- a/dist/linkClass.js +++ b/dist/linkClass.js @@ -25,25 +25,25 @@ linkClass = function (element) { var appendClassName = undefined, childrenCount = undefined, clonedElement = undefined, - localClassNames = undefined, + styleNames = undefined, newChildren = undefined, newProps = undefined; - localClassNames = element.props.localClassName; + styleNames = element.props.styleName; - if (localClassNames) { - localClassNames = localClassNames.split(' '); + if (styleNames) { + styleNames = styleNames.split(' '); - if (options.allowMultiple === false && localClassNames.length > 1) { - throw new Error('ReactElement localClassName property defines multiple module names ("' + element.props.localClassName + '").'); + if (options.allowMultiple === false && styleNames.length > 1) { + throw new Error('ReactElement styleName property defines multiple module names ("' + element.props.styleName + '").'); } - appendClassName = localClassNames.map(function (localClassName) { - if (styles[localClassName]) { - return styles[localClassName]; + appendClassName = styleNames.map(function (styleName) { + if (styles[styleName]) { + return styles[styleName]; } else { if (options.errorWhenNotFound === true) { - throw new Error('"' + localClassName + '" CSS module is undefined.'); + throw new Error('"' + styleName + '" CSS module is undefined.'); } return ''; diff --git a/package.json b/package.json index d07eb46..84373ef 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "css", "modules" ], - "version": "2.0.0", + "version": "3.0.0", "author": { "name": "Gajus Kuizinas", "email": "gk@anuary.com", diff --git a/src/linkClass.js b/src/linkClass.js index 9c7dffe..088afa3 100644 --- a/src/linkClass.js +++ b/src/linkClass.js @@ -12,25 +12,25 @@ linkClass = (element, styles = {}, options = {}) => { let appendClassName, childrenCount, clonedElement, - localClassNames, + styleNames, newChildren, newProps; - localClassNames = element.props.localClassName; + styleNames = element.props.styleName; - if (localClassNames) { - localClassNames = localClassNames.split(' '); + if (styleNames) { + styleNames = styleNames.split(' '); - if (options.allowMultiple === false && localClassNames.length > 1) { - throw new Error(`ReactElement localClassName property defines multiple module names ("${element.props.localClassName}").`); + if (options.allowMultiple === false && styleNames.length > 1) { + throw new Error(`ReactElement styleName property defines multiple module names ("${element.props.styleName}").`); } - appendClassName = localClassNames.map((localClassName) => { - if (styles[localClassName]) { - return styles[localClassName]; + appendClassName = styleNames.map((styleName) => { + if (styles[styleName]) { + return styles[styleName]; } else { if (options.errorWhenNotFound === true) { - throw new Error(`"${localClassName}" CSS module is undefined.`); + throw new Error(`"${styleName}" CSS module is undefined.`); } return ''; diff --git a/test/linkClass.js b/test/linkClass.js index d805d51..f88884b 100644 --- a/test/linkClass.js +++ b/test/linkClass.js @@ -8,7 +8,7 @@ import jsdom from 'jsdom'; import linkClass from './../dist/linkClass'; describe('linkClass', () => { - context('when ReactElement does not define localClassName', () => { + context('when ReactElement does not define styleName', () => { it('does not affect element properties', () => { expect(linkClass(
)).to.deep.equal(
); }); @@ -33,12 +33,12 @@ describe('linkClass', () => { }); }); - context('when localClassName matches an existing CSS module', () => { + context('when styleName matches an existing CSS module', () => { context('when ReactElement does not have an existing className', () => { it('uses the generated class name to set the className property', () => { let subject; - subject =
; + subject =
; subject = linkClass(subject, { foo: 'foo-1' @@ -51,7 +51,7 @@ describe('linkClass', () => { it('appends the generated class name to the className property', () => { let subject; - subject =
; + subject =
; subject = linkClass(subject, { bar: 'bar-1' @@ -67,15 +67,15 @@ describe('linkClass', () => { context('when false', () => { it('throws an error', () => { expect(() => { - linkClass(
, {}, {allowMultiple: false}); - }).to.throw(Error, 'ReactElement localClassName property defines multiple module names ("foo bar").'); + linkClass(
, {}, {allowMultiple: false}); + }).to.throw(Error, 'ReactElement styleName property defines multiple module names ("foo bar").'); }); }); context('when true', () => { it('appends a generated class name for every referenced CSS module', () => { let subject; - subject =
; + subject =
; subject = linkClass(subject, { foo: 'foo-1', @@ -89,12 +89,12 @@ describe('linkClass', () => { }); describe('options.errorWhenNotFound', () => { - context('when localClassName does not match an existing CSS module', () => { + context('when styleName does not match an existing CSS module', () => { context('when false', () => { it('ignores the missing CSS module', () => { let subject; - subject =
; + subject =
; subject = linkClass(subject, {}, {errorWhenNotFound: false}); @@ -104,7 +104,7 @@ describe('linkClass', () => { context('when is true', () => { it('throws an error', () => { expect(() => { - linkClass(
, {}, {errorWhenNotFound: true}); + linkClass(
, {}, {errorWhenNotFound: true}); }).to.throw(Error, '"foo" CSS module is undefined.'); }); }); @@ -130,11 +130,11 @@ describe('linkClass', () => { Foo = class extends React.Component { render () { - return
Hello
; + return
Hello
; } }; - nodeList = TestUtils.renderIntoDocument(linkClass(
, {foo: 'foo-1'})); + nodeList = TestUtils.renderIntoDocument(linkClass(
, {foo: 'foo-1'})); }); it('processes ReactElement nodes', () => { expect(nodeList.className).to.equal('foo-1'); From fae35f8ff89b4a6faa0ffe31f418808a03a4e745 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Thu, 27 Aug 2015 12:14:45 +0100 Subject: [PATCH 015/229] ESLint compliance. --- dist/linkClass.js | 4 ++-- src/linkClass.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dist/linkClass.js b/dist/linkClass.js index 6c44a3d..6176156 100644 --- a/dist/linkClass.js +++ b/dist/linkClass.js @@ -25,9 +25,9 @@ linkClass = function (element) { var appendClassName = undefined, childrenCount = undefined, clonedElement = undefined, - styleNames = undefined, newChildren = undefined, - newProps = undefined; + newProps = undefined, + styleNames = undefined; styleNames = element.props.styleName; diff --git a/src/linkClass.js b/src/linkClass.js index 088afa3..b058657 100644 --- a/src/linkClass.js +++ b/src/linkClass.js @@ -12,9 +12,9 @@ linkClass = (element, styles = {}, options = {}) => { let appendClassName, childrenCount, clonedElement, - styleNames, newChildren, - newProps; + newProps, + styleNames; styleNames = element.props.styleName; From e6196344df087b2e52fafc8fb818a4e65fc0a30c Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Thu, 27 Aug 2015 12:17:19 +0100 Subject: [PATCH 016/229] Docs. --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 74c7337..947819f 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ However, this approach has several disadvantages: * Mixing CSS Modules and global CSS classes is cumbersome. * Reference to an undefined CSS Module resolves to `undefined` without a warning. -React CSS Modules component automates loading of CSS Modules using `localClassName` property, e.g. +React CSS Modules component automates loading of CSS Modules using `styleName` property, e.g. ```js import React from 'react'; @@ -68,9 +68,9 @@ import CSSModules from 'react-css-modules'; class Car extends React.Component { render () { - return
-
-
+ return
+
+
; } } @@ -85,15 +85,15 @@ Using `react-css-modules`: * There is clear distinction between global CSS and CSS Modules, e.g. ```js -
+
``` -* You are warned when `localClassName` refers to an undefined CSS Module ([`errorWhenNotFound`](#errorwhennotfound) option). +* You are warned when `styleName` refers to an undefined CSS Module ([`errorWhenNotFound`](#errorwhennotfound) option). * You can enforce use of a single CSS module per `ReactElement` ([`allowMultiple`](#allowmultiple) option). ## The Implementation -`react-css-modules` extends `render` method of the target component. It will use the value of `localClassName` to look for CSS Modules in the associated styles object and will append the matching unique CSS class names to the `ReactElement` `className` property value. +`react-css-modules` extends `render` method of the target component. It will use the value of `styleName` to look for CSS Modules in the associated styles object and will append the matching unique CSS class names to the `ReactElement` `className` property value. [Awesome!](https://twitter.com/intent/retweet?tweet_id=636497036603428864) @@ -217,14 +217,14 @@ Allows multiple CSS Module names. When `false`, the following will cause an error: ```js -
+
``` #### `errorWhenNotFound` Default: `true`. -Throws an error when `localClassName` cannot be mapped to an existing CSS Module. +Throws an error when `styleName` cannot be mapped to an existing CSS Module. ## SASS, SCSS, LESS and other CSS Preprocessors @@ -275,7 +275,7 @@ Composition promotes better separation of markup and style using semantics that To learn more about composing CSS rules, I suggest reading Glen Maddern article about [CSS Modules](http://glenmaddern.com/articles/css-modules) and the official [spec of the CSS Modules](https://github.com/css-modules/css-modules). -That said, if you enable [`allowMultiple`](#allowmultiple) option, you can map multiple CSS Modules to a single `ReactElement`. `react-css-modules` will append a unique class name for every CSS Module it matches in the `localClassName` declaration, e.g. +That said, if you enable [`allowMultiple`](#allowmultiple) option, you can map multiple CSS Modules to a single `ReactElement`. `react-css-modules` will append a unique class name for every CSS Module it matches in the `styleName` declaration, e.g. ```css .button { @@ -288,7 +288,7 @@ That said, if you enable [`allowMultiple`](#allowmultiple) option, you can map m ``` ```js -
+
``` This will map both [Interoperable CSS](https://github.com/css-modules/icss) CSS classes to the target element. From 27b48c96a5dd0756d814b8c50bd792ebf4605e48 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Thu, 27 Aug 2015 12:18:47 +0100 Subject: [PATCH 017/229] Docs. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 947819f..4e395bd 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ React CSS Modules implement automatic mapping of CSS modules. Every CSS class is assigned a local-scoped identifier with a global unique name. CSS Modules enable a modular and reusable CSS! - [What's the Problem?](#whats-the-problem) -- [The Implementation](#theimplementation) +- [The Implementation](#the-implementation) - [Usage](#usage) - [Module Bundler](#module-bundler) - [webpack](#webpack) From 25a5ddf0015835b71ffd3c992dd5e155d9589cde Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Thu, 27 Aug 2015 21:28:50 +0100 Subject: [PATCH 018/229] Removed peerDependencies to enable support for React 0.13 and 0.14. --- package.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/package.json b/package.json index 84373ef..e6e3c62 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,6 @@ "url": "http://gajus.com" }, "license": "BSD-3-Clause", - "peerDependencies": { - "react": "^0.14.0-beta3" - }, "dependencies": { "lodash": "^3.10.1" }, From db153b42d13efe5f9c74ad6fd793fd81ddb48d61 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Thu, 27 Aug 2015 21:29:13 +0100 Subject: [PATCH 019/229] 3.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e6e3c62..ad165e2 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "css", "modules" ], - "version": "3.0.0", + "version": "3.0.1", "author": { "name": "Gajus Kuizinas", "email": "gk@anuary.com", From 058171acb46b5b9ef98dea4072a4a107f7fe95a6 Mon Sep 17 00:00:00 2001 From: Glen Maddern Date: Thu, 27 Aug 2015 15:17:29 -0700 Subject: [PATCH 020/229] Made the later examples use styleName --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4e395bd..b6096a4 100644 --- a/README.md +++ b/README.md @@ -160,9 +160,9 @@ import CSSModules from 'react-css-modules'; class Car extends React.Component { render () { - return
-
-
+ return
+
+
; } } @@ -182,9 +182,9 @@ import CSSModules from 'react-css-modules'; @CSSModules(styles) export default class extends React.Component { render () { - return
-
front-door
-
back-door
+ return
+
front-door
+
back-door
; } } From 15505416d7d76307b87820016543d6a2c09a3fd7 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Fri, 28 Aug 2015 00:15:35 +0100 Subject: [PATCH 021/229] A dedicated section to Class Composition. --- README.md | 151 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 134 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 4e395bd..224c265 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,9 @@ React CSS Modules implement automatic mapping of CSS modules. Every CSS class is - [Options](#options) - [`allowMultiple`](#allowmultiple) - [`errorWhenNotFound`](#errorwhennotfound) +- [Class Composition](#class-composition) + - [What Problems does Class Composition Solve?](#what-problems-does-class-composition-solve) + - [A Variation of Class Composition Using CSS Preprocessors](#a-variation-of-class-composition-using-css-preprocessors) - [SASS, SCSS, LESS and other CSS Preprocessors](#sass-scss-less-and-other-css-preprocessors) - [Global CSS](#global-css) - [Multiple CSS Classes](#multiple-css-classes) @@ -228,7 +231,7 @@ Throws an error when `styleName` cannot be mapped to an existing CSS Module. ## SASS, SCSS, LESS and other CSS Preprocessors -[Interoperable CSS](https://github.com/css-modules/icss) is compatible with the CSS Preprocessors. To use a preprocessor, all you need to do is add the preprocessor to the chain of loaders, e.g. in the case of webpack it is as simple as installing `sass-loader` and adding `!sass` to the end of the `style-loader` loader query (loaders are processed from right to left): +[Interoperable CSS](https://github.com/css-modules/icss) is compatible with the CSS preprocessors. To use a preprocessor, all you need to do is add the preprocessor to the chain of loaders, e.g. in the case of webpack it is as simple as installing `sass-loader` and adding `!sass` to the end of the `style-loader` loader query (loaders are processed from right to left): ```js { @@ -237,45 +240,159 @@ Throws an error when `styleName` cannot be mapped to an existing CSS Module. } ``` -## Global CSS +## Class Composition -CSS Modules does not restrict you from using global CSS. +CSS Modules promote composition pattern, i.e. every CSS Module that is used in a component should define all properties required to describe an element, e.g. ```css -:global .foo { +.box { + width: 100px; + height: 100px; +} +.empty { + composes: box; + + background: #4CAF50; +} + +.full { + composes: box; + + background: #F44336; } ``` -However, use global CSS with caution. With CSS Modules, there are only a handful of valid use cases for global CSS (e.g. [normalization](https://github.com/necolas/normalize.css/)). +Composition promotes better separation of markup and style using semantics that would be hard to achieve without CSS Modules. -## Multiple CSS Modules +Because CSS Module names are local, it is perfectly fine to use generic style names such as "empty" or "full", without "box-" prefix. -CSS Modules promote composition pattern, i.e. every CSS Module that is used in a component should define all properties required to describe an element, e.g. +To learn more about composing CSS rules, I suggest reading Glen Maddern article about [CSS Modules](http://glenmaddern.com/articles/css-modules) and the official [spec of the CSS Modules](https://github.com/css-modules/css-modules). + +### What Problems does Class Composition Solve? + +Consider the same example in CSS and HTML: ```css -.button { +.box { + width: 100px; + height: 100px; +} +.box-empty { + background: #4CAF50; } -.active { - composes: common; +.box-full { + background: #F44336; +} +``` + +```html +
+``` + +This pattern emerged with the advent of OOCSS. The biggest disadvantage of this implementation is that you will need to change HTML almost every time you want to change the style. + +### A Variation of Class Composition Using CSS Preprocessors + +This section of the document is included as a learning exercise to broaden the understanding about the origin of Class Composition. CSS Modules support a native method of composting CSS Modules using [`composes`](https://github.com/css-modules/css-modules#composition) keyword. CSS Preprocessor is not required. + +You can write compositions in SCSS using [`@extend`](http://sass-lang.com/documentation/file.SASS_REFERENCE.html#extend) keyword and using [Mixin Directives](http://sass-lang.com/documentation/file.SASS_REFERENCE.html#mixins), e.g. + +Using `@extend`: - /* anything that only applies to active state of the button */ +```css +%box { + width: 100px; + height: 100px; } -.disabled { - composes: common; +.box-empty { + @extend %box; - /* anything that only applies to disabled state of the button */ + background: #4CAF50; +} + +.box-full { + @extend %box; + + background: #F44336; } ``` -Composition promotes better separation of markup and style using semantics that would be hard to achieve without CSS Modules. +This translates to: -To learn more about composing CSS rules, I suggest reading Glen Maddern article about [CSS Modules](http://glenmaddern.com/articles/css-modules) and the official [spec of the CSS Modules](https://github.com/css-modules/css-modules). +```css +.box-empty, +.box-full { + width: 100px; + height: 100px; +} + +.box-empty { + background: #4CAF50; +} + +.box-full { + background: #F44336; +} +``` + +Using mixins: + +```css +@mixin box { + width: 100px; + height: 100px; +} + +.box-empty { + @include box; + + background: #4CAF50; +} + +.box-full { + @include box; + + background: #F44336; +} +``` + +This translates to: + +```css +.box-empty { + width: 100px; + height: 100px; + background: #4CAF50; +} + +.box-full { + width: 100px; + height: 100px; + background: #F44336; +} +``` + +## Global CSS + +CSS Modules does not restrict you from using global CSS. + +```css +:global .foo { + +} +``` + +However, use global CSS with caution. With CSS Modules, there are only a handful of valid use cases for global CSS (e.g. [normalization](https://github.com/necolas/normalize.css/)). + +## Multiple CSS Modules + +Avoid using multiple CSS Modules to describe a single element. Read about [Class Composition](#class-compositon). -That said, if you enable [`allowMultiple`](#allowmultiple) option, you can map multiple CSS Modules to a single `ReactElement`. `react-css-modules` will append a unique class name for every CSS Module it matches in the `styleName` declaration, e.g. +That said, if you require to use multiple CSS Modules to describe an element, enable the [`allowMultiple`](#allowmultiple) option. When multiple CSS Modules are used to describe an element, `react-css-modules` will append a unique class name for every CSS Module it matches in the `styleName` declaration, e.g. ```css .button { From 80ee6d019441a6fd80636d987ce9c3de4214c2c6 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Fri, 28 Aug 2015 00:17:12 +0100 Subject: [PATCH 022/229] Docs. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3d821e0..2ac7bb5 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ React CSS Modules implement automatic mapping of CSS modules. Every CSS class is - [`errorWhenNotFound`](#errorwhennotfound) - [Class Composition](#class-composition) - [What Problems does Class Composition Solve?](#what-problems-does-class-composition-solve) - - [A Variation of Class Composition Using CSS Preprocessors](#a-variation-of-class-composition-using-css-preprocessors) + - [Class Composition Using CSS Preprocessors](#class-composition-using-css-preprocessors) - [SASS, SCSS, LESS and other CSS Preprocessors](#sass-scss-less-and-other-css-preprocessors) - [Global CSS](#global-css) - [Multiple CSS Classes](#multiple-css-classes) @@ -294,7 +294,7 @@ Consider the same example in CSS and HTML: This pattern emerged with the advent of OOCSS. The biggest disadvantage of this implementation is that you will need to change HTML almost every time you want to change the style. -### A Variation of Class Composition Using CSS Preprocessors +### Class Composition Using CSS Preprocessors This section of the document is included as a learning exercise to broaden the understanding about the origin of Class Composition. CSS Modules support a native method of composting CSS Modules using [`composes`](https://github.com/css-modules/css-modules#composition) keyword. CSS Preprocessor is not required. From 0a5e507d504a0b88b609f9b977bf647d684c0673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Faruk=20Ate=C5=9F?= Date: Thu, 27 Aug 2015 16:55:04 -0700 Subject: [PATCH 023/229] Typo in anchor pointer --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2ac7bb5..62b0c20 100644 --- a/README.md +++ b/README.md @@ -390,7 +390,7 @@ However, use global CSS with caution. With CSS Modules, there are only a handful ## Multiple CSS Modules -Avoid using multiple CSS Modules to describe a single element. Read about [Class Composition](#class-compositon). +Avoid using multiple CSS Modules to describe a single element. Read about [Class Composition](#class-composition). That said, if you require to use multiple CSS Modules to describe an element, enable the [`allowMultiple`](#allowmultiple) option. When multiple CSS Modules are used to describe an element, `react-css-modules` will append a unique class name for every CSS Module it matches in the `styleName` declaration, e.g. From dedef78c8b673ea6c54aaa2d94088d766ce708cc Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Fri, 28 Aug 2015 14:43:43 +0100 Subject: [PATCH 024/229] errorWhenNotFound default value must be true --- dist/makeConfig.js | 2 +- src/makeConfig.js | 2 +- test/makeConfig.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/makeConfig.js b/dist/makeConfig.js index b286719..52c3fd1 100644 --- a/dist/makeConfig.js +++ b/dist/makeConfig.js @@ -47,7 +47,7 @@ exports['default'] = function () { } if (typeof userConfig.errorWhenNotFound === 'undefined') { - userConfig.errorWhenNotFound = false; + userConfig.errorWhenNotFound = true; } return userConfig; diff --git a/src/makeConfig.js b/src/makeConfig.js index f9c8715..31665df 100644 --- a/src/makeConfig.js +++ b/src/makeConfig.js @@ -37,7 +37,7 @@ export default (userConfig = {}) => { } if (typeof userConfig.errorWhenNotFound === 'undefined') { - userConfig.errorWhenNotFound = false; + userConfig.errorWhenNotFound = true; } return userConfig; diff --git a/test/makeConfig.js b/test/makeConfig.js index a48bd86..c430e6d 100644 --- a/test/makeConfig.js +++ b/test/makeConfig.js @@ -18,7 +18,7 @@ describe('makeConfig', () => { }); describe('errorWhenNotFound property', () => { it('defaults to true', () => { - expect(options.errorWhenNotFound).to.equal(false); + expect(options.errorWhenNotFound).to.equal(true); }); }); }); From ca2f72efccec5b1a9a408e102c112d69d23cbef4 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Fri, 28 Aug 2015 14:43:47 +0100 Subject: [PATCH 025/229] 3.0.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ad165e2..5de15dc 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "css", "modules" ], - "version": "3.0.1", + "version": "3.0.2", "author": { "name": "Gajus Kuizinas", "email": "gk@anuary.com", From 8cca1c452bd73737675dcce22ddd1097daa4617d Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Sat, 29 Aug 2015 11:48:56 +0100 Subject: [PATCH 026/229] Added react-css-modules logo and a section dedicated to CSS Modules. --- README.md | 14 ++++++++++++-- README/react-css-modules.png | Bin 0 -> 46508 bytes README/react-css-modules.sketch | Bin 0 -> 90112 bytes 3 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 README/react-css-modules.png create mode 100644 README/react-css-modules.sketch diff --git a/README.md b/README.md index 62b0c20..2b8d85e 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,12 @@ [![Travis build status](http://img.shields.io/travis/gajus/react-css-modules/master.svg?style=flat)](https://travis-ci.org/gajus/react-css-modules) [![NPM version](http://img.shields.io/npm/v/react-css-modules.svg?style=flat)](https://www.npmjs.org/package/react-css-modules) + + React CSS Modules implement automatic mapping of CSS modules. Every CSS class is assigned a local-scoped identifier with a global unique name. CSS Modules enable a modular and reusable CSS! +- [CSS Modules](#css-modules) + - [webpack `css-loader`](#webpack-css-loader) - [What's the Problem?](#whats-the-problem) - [The Implementation](#the-implementation) - [Usage](#usage) @@ -22,7 +26,7 @@ React CSS Modules implement automatic mapping of CSS modules. Every CSS class is - [Global CSS](#global-css) - [Multiple CSS Classes](#multiple-css-classes) -## What's the Problem? +## CSS Modules [CSS Modules](https://github.com/css-modules/css-modules) are awesome. If you are not familiar with CSS Modules, it is a concept of using a module bundler such as [webpack](http://webpack.github.io/docs/) to load CSS scoped to a particular document. CSS module loader will generate a unique name for a each CSS class at the time of loading the CSS document ([Interoperable CSS](https://github.com/css-modules/icss) to be precise). To see CSS Modules in practice, [webpack-demo](https://css-modules.github.io/webpack-demo/). @@ -55,7 +59,13 @@ and a corresponding CSS file that matches those CSS classes. Awesome! -However, this approach has several disadvantages: +### webpack `css-loader` + +[CSS Modules](https://github.com/css-modules/css-modules) is a specification that can be implemented in multiple ways. `react-css-modules` leverages as existing CSS Modules implementation webpack [css-loader](https://github.com/webpack/css-loader#css-modules). + +## What's the Problem? + +webpack [css-loader](https://github.com/webpack/css-loader#css-modules) itself has several disadvantages: * You have to use `camelCase` CSS class names. * You have to use `styles` object whenever constructing a `className`. diff --git a/README/react-css-modules.png b/README/react-css-modules.png new file mode 100644 index 0000000000000000000000000000000000000000..2614951ab7b6ec971422202f5b4424e5d263514f GIT binary patch literal 46508 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 literal 0 HcmV?d00001 diff --git a/README/react-css-modules.sketch b/README/react-css-modules.sketch new file mode 100644 index 0000000000000000000000000000000000000000..a014d753a90a6eddb38d41660334d04f785a9ee1 GIT binary patch literal 90112 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

<%Ke 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%0D$?jx`rI*EQEO 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 literal 0 HcmV?d00001 From dc8d90f4f928b83bceea7e57f1974eeb06961af4 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Sat, 29 Aug 2015 11:49:47 +0100 Subject: [PATCH 027/229] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2b8d85e..022cd88 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Travis build status](http://img.shields.io/travis/gajus/react-css-modules/master.svg?style=flat)](https://travis-ci.org/gajus/react-css-modules) [![NPM version](http://img.shields.io/npm/v/react-css-modules.svg?style=flat)](https://www.npmjs.org/package/react-css-modules) - + React CSS Modules implement automatic mapping of CSS modules. Every CSS class is assigned a local-scoped identifier with a global unique name. CSS Modules enable a modular and reusable CSS! From 024cfd6098d4eabcb41ef7c4ce8fa3e085f6b846 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Sun, 30 Aug 2015 16:19:22 +0100 Subject: [PATCH 028/229] Less clutter when setting the configuration. --- dist/index.js | 12 +++------ dist/linkClass.js | 22 +++++++++------ dist/makeConfig.js | 56 --------------------------------------- dist/makeConfiguration.js | 50 ++++++++++++++++++++++++++++++++++ src/index.js | 5 ++-- src/linkClass.js | 16 ++++++----- src/makeConfig.js | 44 ------------------------------ src/makeConfiguration.js | 35 ++++++++++++++++++++++++ src/utils.js | 4 --- test/linkClass.js | 2 +- test/makeConfig.js | 34 ------------------------ test/makeConfiguration.js | 43 ++++++++++++++++++++++++++++++ 12 files changed, 158 insertions(+), 165 deletions(-) delete mode 100644 dist/makeConfig.js create mode 100644 dist/makeConfiguration.js delete mode 100644 src/makeConfig.js create mode 100644 src/makeConfiguration.js delete mode 100644 test/makeConfig.js create mode 100644 test/makeConfiguration.js diff --git a/dist/index.js b/dist/index.js index f7f655d..c50da0c 100644 --- a/dist/index.js +++ b/dist/index.js @@ -6,7 +6,7 @@ Object.defineProperty(exports, '__esModule', { var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); -var _get = function get(_x2, _x3, _x4) { var _again = true; _function: while (_again) { var object = _x2, property = _x3, receiver = _x4; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x2 = parent; _x3 = property; _x4 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } @@ -18,10 +18,6 @@ var _linkClass = require('./linkClass'); var _linkClass2 = _interopRequireDefault(_linkClass); -var _makeConfig = require('./makeConfig'); - -var _makeConfig2 = _interopRequireDefault(_makeConfig); - var decoratorConstructor = undefined, functionConstructor = undefined; @@ -33,9 +29,7 @@ var decoratorConstructor = undefined, * @param {Object} options {@link https://github.com/gajus/react-css-modules#options} * @return {Function} */ -functionConstructor = function (Component, styles) { - var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; - +functionConstructor = function (Component, styles, options) { return (function (_Component) { _inherits(_class, _Component); @@ -48,7 +42,7 @@ functionConstructor = function (Component, styles) { _createClass(_class, [{ key: 'render', value: function render() { - return (0, _linkClass2['default'])(_get(Object.getPrototypeOf(_class.prototype), 'render', this).call(this), styles, (0, _makeConfig2['default'])(options)); + return (0, _linkClass2['default'])(_get(Object.getPrototypeOf(_class.prototype), 'render', this).call(this), styles, options); } }]); diff --git a/dist/linkClass.js b/dist/linkClass.js index 6176156..38b0f43 100644 --- a/dist/linkClass.js +++ b/dist/linkClass.js @@ -10,31 +10,37 @@ var _react = require('react'); var _react2 = _interopRequireDefault(_react); +var _makeConfiguration = require('./makeConfiguration'); + +var _makeConfiguration2 = _interopRequireDefault(_makeConfiguration); + var linkClass = undefined; /** * @param {ReactElement} element * @param {Object} styles CSS modules class map. - * @param {CSSModules~Options} options + * @param {CSSModules~Options} userConfiguration * @return {ReactElement} */ -linkClass = function (element) { - var styles = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; - var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; +linkClass = function (element, styles, userConfiguration) { + if (styles === undefined) styles = {}; var appendClassName = undefined, childrenCount = undefined, clonedElement = undefined, + configuration = undefined, newChildren = undefined, newProps = undefined, styleNames = undefined; + configuration = (0, _makeConfiguration2['default'])(userConfiguration); + styleNames = element.props.styleName; if (styleNames) { styleNames = styleNames.split(' '); - if (options.allowMultiple === false && styleNames.length > 1) { + if (configuration.allowMultiple === false && styleNames.length > 1) { throw new Error('ReactElement styleName property defines multiple module names ("' + element.props.styleName + '").'); } @@ -42,7 +48,7 @@ linkClass = function (element) { if (styles[styleName]) { return styles[styleName]; } else { - if (options.errorWhenNotFound === true) { + if (configuration.errorWhenNotFound === true) { throw new Error('"' + styleName + '" CSS module is undefined.'); } @@ -65,13 +71,13 @@ linkClass = function (element) { if (childrenCount > 1) { newChildren = _react2['default'].Children.map(element.props.children, function (node) { if (_react2['default'].isValidElement(node)) { - return linkClass(node, styles, options); + return linkClass(node, styles, configuration); } else { return node; } }); } else if (childrenCount === 1) { - newChildren = linkClass(_react2['default'].Children.only(element.props.children), styles, options); + newChildren = linkClass(_react2['default'].Children.only(element.props.children), styles, configuration); } } diff --git a/dist/makeConfig.js b/dist/makeConfig.js deleted file mode 100644 index 52c3fd1..0000000 --- a/dist/makeConfig.js +++ /dev/null @@ -1,56 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, '__esModule', { - value: true -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - -var _utils = require('./utils'); - -var _utils2 = _interopRequireDefault(_utils); - -/** - * @typedef CSSModules~Options - * @see {@link https://github.com/gajus/react-css-modules#options} - * @property {Boolean} allowMultiple - * @property {Boolean} errorWhenNotFound - */ - -/** - * @param {Options} userConfig - * @return {CSSModules~Options} - */ - -exports['default'] = function () { - var userConfig = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; - - var knownProperties = undefined, - unknownProperties = undefined; - - knownProperties = ['allowMultiple', 'errorWhenNotFound']; - - unknownProperties = _utils2['default'].difference(_utils2['default'].keys(userConfig), knownProperties); - - if (unknownProperties.length) { - throw new Error('Unknown config property "' + unknownProperties[0] + '".'); - } - - _utils2['default'].forEach(userConfig, function (value, name) { - if (typeof value !== 'boolean') { - throw new Error('"' + name + '" property value must be a boolean.'); - } - }); - - if (typeof userConfig.allowMultiple === 'undefined') { - userConfig.allowMultiple = false; - } - - if (typeof userConfig.errorWhenNotFound === 'undefined') { - userConfig.errorWhenNotFound = true; - } - - return userConfig; -}; - -module.exports = exports['default']; \ No newline at end of file diff --git a/dist/makeConfiguration.js b/dist/makeConfiguration.js new file mode 100644 index 0000000..d42ad79 --- /dev/null +++ b/dist/makeConfiguration.js @@ -0,0 +1,50 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _utils = require('./utils'); + +var _utils2 = _interopRequireDefault(_utils); + +/** + * @typedef CSSModules~Options + * @see {@link https://github.com/gajus/react-css-modules#options} + * @property {Boolean} allowMultiple + * @property {Boolean} errorWhenNotFound + */ + +/** + * @param {CSSModules~Options} userConfiguration + * @return {CSSModules~Options} + */ + +exports['default'] = function () { + var userConfiguration = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; + + var configuration = undefined; + + configuration = { + allowMultiple: false, + errorWhenNotFound: true + }; + + _utils2['default'].forEach(userConfiguration, function (value, name) { + if (typeof configuration[name] === 'undefined') { + throw new Error('Unknown configuration property "' + name + '".'); + } + + if (typeof value !== 'boolean') { + throw new Error('"' + name + '" property value must be a boolean.'); + } + + configuration[name] = value; + }); + + return configuration; +}; + +module.exports = exports['default']; \ No newline at end of file diff --git a/src/index.js b/src/index.js index aab47ea..75c3bff 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,4 @@ import linkClass from './linkClass'; -import makeConfig from './makeConfig'; let decoratorConstructor, functionConstructor; @@ -12,10 +11,10 @@ let decoratorConstructor, * @param {Object} options {@link https://github.com/gajus/react-css-modules#options} * @return {Function} */ -functionConstructor = (Component, styles, options = {}) => { +functionConstructor = (Component, styles, options) => { return class extends Component { render () { - return linkClass(super.render(), styles, makeConfig(options)); + return linkClass(super.render(), styles, options); } }; }; diff --git a/src/linkClass.js b/src/linkClass.js index b058657..cad5d7a 100644 --- a/src/linkClass.js +++ b/src/linkClass.js @@ -1,27 +1,31 @@ import React from 'react'; +import makeConfiguration from './makeConfiguration'; let linkClass; /** * @param {ReactElement} element * @param {Object} styles CSS modules class map. - * @param {CSSModules~Options} options + * @param {CSSModules~Options} userConfiguration * @return {ReactElement} */ -linkClass = (element, styles = {}, options = {}) => { +linkClass = (element, styles = {}, userConfiguration) => { let appendClassName, childrenCount, clonedElement, + configuration, newChildren, newProps, styleNames; + configuration = makeConfiguration(userConfiguration); + styleNames = element.props.styleName; if (styleNames) { styleNames = styleNames.split(' '); - if (options.allowMultiple === false && styleNames.length > 1) { + if (configuration.allowMultiple === false && styleNames.length > 1) { throw new Error(`ReactElement styleName property defines multiple module names ("${element.props.styleName}").`); } @@ -29,7 +33,7 @@ linkClass = (element, styles = {}, options = {}) => { if (styles[styleName]) { return styles[styleName]; } else { - if (options.errorWhenNotFound === true) { + if (configuration.errorWhenNotFound === true) { throw new Error(`"${styleName}" CSS module is undefined.`); } @@ -52,13 +56,13 @@ linkClass = (element, styles = {}, options = {}) => { if (childrenCount > 1) { newChildren = React.Children.map(element.props.children, (node) => { if (React.isValidElement(node)) { - return linkClass(node, styles, options); + return linkClass(node, styles, configuration); } else { return node; } }); } else if (childrenCount === 1) { - newChildren = linkClass(React.Children.only(element.props.children), styles, options); + newChildren = linkClass(React.Children.only(element.props.children), styles, configuration); } } diff --git a/src/makeConfig.js b/src/makeConfig.js deleted file mode 100644 index 31665df..0000000 --- a/src/makeConfig.js +++ /dev/null @@ -1,44 +0,0 @@ -import _ from './utils'; - -/** - * @typedef CSSModules~Options - * @see {@link https://github.com/gajus/react-css-modules#options} - * @property {Boolean} allowMultiple - * @property {Boolean} errorWhenNotFound - */ - -/** - * @param {Options} userConfig - * @return {CSSModules~Options} - */ -export default (userConfig = {}) => { - let knownProperties, - unknownProperties; - - knownProperties = [ - 'allowMultiple', - 'errorWhenNotFound' - ]; - - unknownProperties = _.difference(_.keys(userConfig), knownProperties); - - if (unknownProperties.length) { - throw new Error(`Unknown config property "${unknownProperties[0]}".`); - } - - _.forEach(userConfig, (value, name) => { - if (typeof value !== 'boolean') { - throw new Error(`"${name}" property value must be a boolean.`); - } - }); - - if (typeof userConfig.allowMultiple === 'undefined') { - userConfig.allowMultiple = false; - } - - if (typeof userConfig.errorWhenNotFound === 'undefined') { - userConfig.errorWhenNotFound = true; - } - - return userConfig; -}; diff --git a/src/makeConfiguration.js b/src/makeConfiguration.js new file mode 100644 index 0000000..e521af5 --- /dev/null +++ b/src/makeConfiguration.js @@ -0,0 +1,35 @@ +import _ from './utils'; + +/** + * @typedef CSSModules~Options + * @see {@link https://github.com/gajus/react-css-modules#options} + * @property {Boolean} allowMultiple + * @property {Boolean} errorWhenNotFound + */ + +/** + * @param {CSSModules~Options} userConfiguration + * @return {CSSModules~Options} + */ +export default (userConfiguration = {}) => { + let configuration; + + configuration = { + allowMultiple: false, + errorWhenNotFound: true + }; + + _.forEach(userConfiguration, (value, name) => { + if (typeof configuration[name] === 'undefined') { + throw new Error(`Unknown configuration property "${name}".`); + } + + if (typeof value !== 'boolean') { + throw new Error(`"${name}" property value must be a boolean.`); + } + + configuration[name] = value; + }); + + return configuration; +}; diff --git a/src/utils.js b/src/utils.js index 861dd24..41f4480 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,9 +1,5 @@ -import keys from 'lodash/object/keys'; -import difference from 'lodash/array/difference'; import forEach from 'lodash/collection/forEach'; export default { - keys, - difference, forEach }; diff --git a/test/linkClass.js b/test/linkClass.js index f88884b..649d7cb 100644 --- a/test/linkClass.js +++ b/test/linkClass.js @@ -5,7 +5,7 @@ import { import React from 'react'; import TestUtils from 'react-addons-test-utils'; import jsdom from 'jsdom'; -import linkClass from './../dist/linkClass'; +import linkClass from './../src/linkClass'; describe('linkClass', () => { context('when ReactElement does not define styleName', () => { diff --git a/test/makeConfig.js b/test/makeConfig.js deleted file mode 100644 index c430e6d..0000000 --- a/test/makeConfig.js +++ /dev/null @@ -1,34 +0,0 @@ -import { - expect -} from 'chai'; - -import makeConfig from './../dist/makeConfig'; - -describe('makeConfig', () => { - describe('when using default config', () => { - let options; - - beforeEach(() => { - options = makeConfig(); - }); - describe('allowMultiple property', () => { - it('defaults to false', () => { - expect(options.allowMultiple).to.equal(false); - }); - }); - describe('errorWhenNotFound property', () => { - it('defaults to true', () => { - expect(options.errorWhenNotFound).to.equal(true); - }); - }); - }); - describe('when unknown property is provided', () => { - it('throws an error', () => { - expect(() => { - makeConfig({ - unknownProperty: true - }); - }).to.throw(Error, 'Unknown config property "unknownProperty".'); - }); - }); -}); diff --git a/test/makeConfiguration.js b/test/makeConfiguration.js new file mode 100644 index 0000000..f805dde --- /dev/null +++ b/test/makeConfiguration.js @@ -0,0 +1,43 @@ +import { + expect +} from 'chai'; + +import makeConfiguration from './../src/makeConfiguration'; + +describe('makeConfiguration', () => { + describe('when using default configuration', () => { + let configuration; + + beforeEach(() => { + configuration = makeConfiguration(); + }); + describe('allowMultiple property', () => { + it('defaults to false', () => { + expect(configuration.allowMultiple).to.equal(false); + }); + }); + describe('errorWhenNotFound property', () => { + it('defaults to true', () => { + expect(configuration.errorWhenNotFound).to.equal(true); + }); + }); + }); + describe('when unknown property is provided', () => { + it('throws an error', () => { + expect(() => { + makeConfiguration({ + unknownProperty: true + }); + }).to.throw(Error, 'Unknown configuration property "unknownProperty".'); + }); + }); + it('does not mutate user configuration', () => { + let userConfiguration; + + userConfiguration = {}; + + makeConfiguration(userConfiguration); + + expect(userConfiguration).to.deep.equal({}); + }); +}); From 13078eb3d417fd6d4447efc6c5ce3af50aa33fd2 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Sun, 30 Aug 2015 16:19:39 +0100 Subject: [PATCH 029/229] Less clutter when setting the configuration. --- dist/utils.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/dist/utils.js b/dist/utils.js index 94ee160..ec988cd 100644 --- a/dist/utils.js +++ b/dist/utils.js @@ -6,21 +6,11 @@ Object.defineProperty(exports, '__esModule', { function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } -var _lodashObjectKeys = require('lodash/object/keys'); - -var _lodashObjectKeys2 = _interopRequireDefault(_lodashObjectKeys); - -var _lodashArrayDifference = require('lodash/array/difference'); - -var _lodashArrayDifference2 = _interopRequireDefault(_lodashArrayDifference); - var _lodashCollectionForEach = require('lodash/collection/forEach'); var _lodashCollectionForEach2 = _interopRequireDefault(_lodashCollectionForEach); exports['default'] = { - keys: _lodashObjectKeys2['default'], - difference: _lodashArrayDifference2['default'], forEach: _lodashCollectionForEach2['default'] }; module.exports = exports['default']; \ No newline at end of file From 72643f8293332f3189b43652cfa7d7c873e16a27 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Wed, 2 Sep 2015 07:27:20 +0100 Subject: [PATCH 030/229] Fix Invariant Violation when a single element child is contained in an array. --- src/linkClass.js | 5 ++++- src/utils.js | 6 +++++- test/linkClass.js | 40 +++++++++++++++++++++++++++++++++++----- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/src/linkClass.js b/src/linkClass.js index cad5d7a..7f339de 100644 --- a/src/linkClass.js +++ b/src/linkClass.js @@ -1,5 +1,6 @@ import React from 'react'; import makeConfiguration from './makeConfiguration'; +import _ from './utils'; let linkClass; @@ -53,7 +54,7 @@ linkClass = (element, styles = {}, userConfiguration) => { if (typeof element.props.children !== 'string') { childrenCount = React.Children.count(element.props.children); - if (childrenCount > 1) { + if (childrenCount > 1 || _.isArray(element.props.children)) { newChildren = React.Children.map(element.props.children, (node) => { if (React.isValidElement(node)) { return linkClass(node, styles, configuration); @@ -61,6 +62,8 @@ linkClass = (element, styles = {}, userConfiguration) => { return node; } }); + // https://github.com/facebook/react/issues/4723#issuecomment-135555277 + newChildren = _.values(newChildren); } else if (childrenCount === 1) { newChildren = linkClass(React.Children.only(element.props.children), styles, configuration); } diff --git a/src/utils.js b/src/utils.js index 41f4480..0574ca3 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,5 +1,9 @@ import forEach from 'lodash/collection/forEach'; +import values from 'lodash/object/values'; +import isArray from 'lodash/lang/isArray'; export default { - forEach + forEach, + values, + isArray }; diff --git a/test/linkClass.js b/test/linkClass.js index 649d7cb..23f1365 100644 --- a/test/linkClass.js +++ b/test/linkClass.js @@ -25,11 +25,41 @@ describe('linkClass', () => { expect(linkClass(

)).to.deep.equal(
); }); - // Using array instead of object causes the following error: - // Warning: Each child in an array or iterator should have a unique "key" prop. - // Check the render method of _class. See https://fb.me/react-warning-keys for more information. - xit('does not affect element with multiple children', () => { - expect(linkClass(

)).to.deep.equal(

); + // Does not affect + it('does not affect element with a single children when that children is contained in an array', () => { + let outcome, + subject; + + subject = React.createElement('div', null, [ + React.createElement('p') + ]); + outcome = React.createElement('div', null, [ + React.createElement('p') + ]); + + expect(linkClass(subject)).to.deep.equal(outcome); + }); + + it('does not affect element with multiple children', () => { + // Using array instead of object causes the following error: + // Warning: Each child in an array or iterator should have a unique "key" prop. + // Check the render method of _class. See https://fb.me/react-warning-keys for more information. + // @see https://github.com/facebook/react/issues/4723#issuecomment-135555277 + // expect(linkClass(

)).to.deep.equal(

); + + let outcome, + subject; + + subject = React.createElement('div', null, [ + React.createElement('p'), + React.createElement('p') + ]); + outcome = React.createElement('div', null, [ + React.createElement('p'), + React.createElement('p') + ]); + + expect(linkClass(subject)).to.deep.equal(outcome); }); }); From cf46b7a075b5ac307c9c6220344385b20a86c88e Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Wed, 2 Sep 2015 07:27:37 +0100 Subject: [PATCH 031/229] 3.0.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5de15dc..502109f 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "css", "modules" ], - "version": "3.0.2", + "version": "3.0.3", "author": { "name": "Gajus Kuizinas", "email": "gk@anuary.com", From f1ea7c4c3b5cc5c8b2617455a54a35cf61afbe3b Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Thu, 3 Sep 2015 16:49:23 +0100 Subject: [PATCH 032/229] This ought to fix the invariant violation error. --- dist/linkClass.js | 18 ++++++++++++++---- dist/utils.js | 12 +++++++++++- src/linkClass.js | 8 ++++++-- test/linkClass.js | 5 ++--- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/dist/linkClass.js b/dist/linkClass.js index 38b0f43..d260d81 100644 --- a/dist/linkClass.js +++ b/dist/linkClass.js @@ -14,6 +14,10 @@ var _makeConfiguration = require('./makeConfiguration'); var _makeConfiguration2 = _interopRequireDefault(_makeConfiguration); +var _utils = require('./utils'); + +var _utils2 = _interopRequireDefault(_utils); + var linkClass = undefined; /** @@ -65,10 +69,12 @@ linkClass = function (element, styles, userConfiguration) { // A child can be either an array, a sole object or a string. //
test
- if (typeof element.props.children !== 'string') { + if (_utils2['default'].isArray(element.props.children)) { childrenCount = _react2['default'].Children.count(element.props.children); - if (childrenCount > 1) { + // console.log('childrenCount', childrenCount, 'element.props.children', element.props.children); + + if (childrenCount > 1 || _utils2['default'].isArray(element.props.children)) { newChildren = _react2['default'].Children.map(element.props.children, function (node) { if (_react2['default'].isValidElement(node)) { return linkClass(node, styles, configuration); @@ -76,9 +82,13 @@ linkClass = function (element, styles, userConfiguration) { return node; } }); + // https://github.com/facebook/react/issues/4723#issuecomment-135555277 + // Forcing children into an array produces the following error: + // Warning: A ReactFragment is an opaque type. Accessing any of its properties is deprecated. Pass it to one of the React.Children helpers. + // newChildren = _.values(newChildren); } else if (childrenCount === 1) { - newChildren = linkClass(_react2['default'].Children.only(element.props.children), styles, configuration); - } + newChildren = linkClass(_react2['default'].Children.only(element.props.children), styles, configuration); + } } if (appendClassName) { diff --git a/dist/utils.js b/dist/utils.js index ec988cd..651f52c 100644 --- a/dist/utils.js +++ b/dist/utils.js @@ -10,7 +10,17 @@ var _lodashCollectionForEach = require('lodash/collection/forEach'); var _lodashCollectionForEach2 = _interopRequireDefault(_lodashCollectionForEach); +var _lodashObjectValues = require('lodash/object/values'); + +var _lodashObjectValues2 = _interopRequireDefault(_lodashObjectValues); + +var _lodashLangIsArray = require('lodash/lang/isArray'); + +var _lodashLangIsArray2 = _interopRequireDefault(_lodashLangIsArray); + exports['default'] = { - forEach: _lodashCollectionForEach2['default'] + forEach: _lodashCollectionForEach2['default'], + values: _lodashObjectValues2['default'], + isArray: _lodashLangIsArray2['default'] }; module.exports = exports['default']; \ No newline at end of file diff --git a/src/linkClass.js b/src/linkClass.js index 7f339de..ec80e2f 100644 --- a/src/linkClass.js +++ b/src/linkClass.js @@ -51,9 +51,11 @@ linkClass = (element, styles = {}, userConfiguration) => { // A child can be either an array, a sole object or a string. //
test
- if (typeof element.props.children !== 'string') { + if (_.isArray(element.props.children)) { childrenCount = React.Children.count(element.props.children); + // console.log('childrenCount', childrenCount, 'element.props.children', element.props.children); + if (childrenCount > 1 || _.isArray(element.props.children)) { newChildren = React.Children.map(element.props.children, (node) => { if (React.isValidElement(node)) { @@ -63,7 +65,9 @@ linkClass = (element, styles = {}, userConfiguration) => { } }); // https://github.com/facebook/react/issues/4723#issuecomment-135555277 - newChildren = _.values(newChildren); + // Forcing children into an array produces the following error: + // Warning: A ReactFragment is an opaque type. Accessing any of its properties is deprecated. Pass it to one of the React.Children helpers. + // newChildren = _.values(newChildren); } else if (childrenCount === 1) { newChildren = linkClass(React.Children.only(element.props.children), styles, configuration); } diff --git a/test/linkClass.js b/test/linkClass.js index 23f1365..9a14d53 100644 --- a/test/linkClass.js +++ b/test/linkClass.js @@ -25,8 +25,7 @@ describe('linkClass', () => { expect(linkClass(
)).to.deep.equal(
); }); - // Does not affect - it('does not affect element with a single children when that children is contained in an array', () => { + xit('does not affect element with a single children when that children is contained in an array', () => { let outcome, subject; @@ -40,7 +39,7 @@ describe('linkClass', () => { expect(linkClass(subject)).to.deep.equal(outcome); }); - it('does not affect element with multiple children', () => { + xit('does not affect element with multiple children', () => { // Using array instead of object causes the following error: // Warning: Each child in an array or iterator should have a unique "key" prop. // Check the render method of _class. See https://fb.me/react-warning-keys for more information. From fb3272f1684b5f3b7e71d9f6a7b02caaf747cec5 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Thu, 3 Sep 2015 16:49:28 +0100 Subject: [PATCH 033/229] 3.0.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 502109f..4488917 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "css", "modules" ], - "version": "3.0.3", + "version": "3.0.4", "author": { "name": "Gajus Kuizinas", "email": "gk@anuary.com", From 716afd57a94b25b9f5f6badc39dcccd1d45a8934 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Thu, 3 Sep 2015 20:04:38 +0100 Subject: [PATCH 034/229] Single ReactElement not an array. --- dist/linkClass.js | 4 +--- src/linkClass.js | 4 +--- test/linkClass.js | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/dist/linkClass.js b/dist/linkClass.js index d260d81..f04da0b 100644 --- a/dist/linkClass.js +++ b/dist/linkClass.js @@ -67,9 +67,7 @@ linkClass = function (element, styles, userConfiguration) { appendClassName = appendClassName.join(' '); } - // A child can be either an array, a sole object or a string. - //
test
- if (_utils2['default'].isArray(element.props.children)) { + if (_utils2['default'].isArray(element.props.children) || _react2['default'].isValidElement(element.props.children)) { childrenCount = _react2['default'].Children.count(element.props.children); // console.log('childrenCount', childrenCount, 'element.props.children', element.props.children); diff --git a/src/linkClass.js b/src/linkClass.js index ec80e2f..bef0e5a 100644 --- a/src/linkClass.js +++ b/src/linkClass.js @@ -49,9 +49,7 @@ linkClass = (element, styles = {}, userConfiguration) => { appendClassName = appendClassName.join(' '); } - // A child can be either an array, a sole object or a string. - //
test
- if (_.isArray(element.props.children)) { + if (_.isArray(element.props.children) || React.isValidElement(element.props.children)) { childrenCount = React.Children.count(element.props.children); // console.log('childrenCount', childrenCount, 'element.props.children', element.props.children); diff --git a/test/linkClass.js b/test/linkClass.js index 9a14d53..773cb37 100644 --- a/test/linkClass.js +++ b/test/linkClass.js @@ -63,6 +63,39 @@ describe('linkClass', () => { }); context('when styleName matches an existing CSS module', () => { + context('when a descendant element has styleName', () => { + it('assigns a generated className', () => { + let subject; + + subject =
+

+
; + + subject = linkClass(subject, { + foo: 'foo-1' + }); + + expect(subject.props.children.props.className).to.equal('foo-1'); + }); + }); + context('when multiple descendant elements have styleName', () => { + it('assigns a generated className', () => { + let subject; + + subject =
+

+

+
; + + subject = linkClass(subject, { + foo: 'foo-1', + bar: 'bar-1' + }); + + expect(subject.props.children['.0'].props.className).to.equal('foo-1'); + expect(subject.props.children['.1'].props.className).to.equal('bar-1'); + }); + }); context('when ReactElement does not have an existing className', () => { it('uses the generated class name to set the className property', () => { let subject; From b35c38b120c49c2d8272cb955025af9b4102cce5 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Thu, 3 Sep 2015 20:04:42 +0100 Subject: [PATCH 035/229] 3.0.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4488917..a6a1197 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "css", "modules" ], - "version": "3.0.4", + "version": "3.0.5", "author": { "name": "Gajus Kuizinas", "email": "gk@anuary.com", From 771ad8fad25119b9ed426a356547ea0b0931f4aa Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Sun, 6 Sep 2015 22:32:26 +0100 Subject: [PATCH 036/229] Implement code coverage. --- .coveralls.yml | 1 + .gitignore | 1 + README.md | 1 + package.json | 6 +++++- 4 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 .coveralls.yml diff --git a/.coveralls.yml b/.coveralls.yml new file mode 100644 index 0000000..1d423a5 --- /dev/null +++ b/.coveralls.yml @@ -0,0 +1 @@ +repo_token: 5kn7Xw8YAtWbwqqwmtPT55Sil0knFJz62 diff --git a/.gitignore b/.gitignore index 6edd850..2d3bb39 100755 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /node_modules +/coverage npm-debug.log diff --git a/README.md b/README.md index 022cd88..2b47230 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Travis build status](http://img.shields.io/travis/gajus/react-css-modules/master.svg?style=flat)](https://travis-ci.org/gajus/react-css-modules) [![NPM version](http://img.shields.io/npm/v/react-css-modules.svg?style=flat)](https://www.npmjs.org/package/react-css-modules) +[![Coverage Status](https://coveralls.io/repos/gajus/react-css-modules/badge.svg?branch=master&service=github)](https://coveralls.io/github/gajus/react-css-modules?branch=master) diff --git a/package.json b/package.json index a6a1197..2a68388 100644 --- a/package.json +++ b/package.json @@ -25,16 +25,20 @@ "devDependencies": { "babel": "^5.8.21", "babel-eslint": "^4.1.0", + "babel-istanbul": "^0.3.19", "chai": "^3.2.0", + "coveralls": "^2.11.4", "eslint": "^1.2.1", "eslint-plugin-react": "^3.3.0", "jsdom": "^6.2.0", "mocha": "^2.2.5", + "mocha-lcov-reporter": "0.0.2", "react": "^0.14.0-beta3", "react-addons-test-utils": "^0.14.0-beta3" }, "scripts": { - "test": "./node_modules/.bin/eslint ./src/ ./test/ && mocha", + "lint": "./node_modules/.bin/eslint ./src/ ./test/", + "test": "npm run lint && node ./node_modules/.bin/babel-istanbul cover ./node_modules/.bin/_mocha && cat ./coverage/lcov.info | coveralls", "build": "babel ./src/ --out-dir ./dist/", "watch": "babel --watch ./src/ --out-dir ./dist/" } From 3005914a2642d6601050fce3379b24adcab622cc Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Sun, 6 Sep 2015 22:33:05 +0100 Subject: [PATCH 037/229] Should not be public. --- .coveralls.yml | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .coveralls.yml diff --git a/.coveralls.yml b/.coveralls.yml deleted file mode 100644 index 1d423a5..0000000 --- a/.coveralls.yml +++ /dev/null @@ -1 +0,0 @@ -repo_token: 5kn7Xw8YAtWbwqqwmtPT55Sil0knFJz62 From 0da0eeb896f8f7f85a950fcf398e4ac17352f65b Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Sun, 6 Sep 2015 22:34:19 +0100 Subject: [PATCH 038/229] Ignore gitignore. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 2d3bb39..e88d4be 100755 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /node_modules /coverage +/.coveralls.yml npm-debug.log From 3f0365d6bd51ab86650039fd142ac8e8f48ae8d1 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Sun, 6 Sep 2015 22:35:56 +0100 Subject: [PATCH 039/229] Seems like token is required. --- .coveralls.yml | 1 + .gitignore | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 .coveralls.yml diff --git a/.coveralls.yml b/.coveralls.yml new file mode 100644 index 0000000..c099fdc --- /dev/null +++ b/.coveralls.yml @@ -0,0 +1 @@ +repo_token: vnqpbiGFbHmVwNwcJnseZAZ5X33epllne diff --git a/.gitignore b/.gitignore index e88d4be..2d3bb39 100755 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ /node_modules /coverage -/.coveralls.yml npm-debug.log From 02240c3ab44593387fc652ab3ff7174ac85aca62 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Wed, 16 Sep 2015 13:01:32 +0100 Subject: [PATCH 040/229] Updated depedencies. --- .coveralls.yml | 2 +- .gitignore | 1 + LICENSE | 24 ++++++++++++++++++++++++ package.json | 25 +++++++++++++------------ test/linkClass.js | 6 ++++-- test/makeConfiguration.js | 2 ++ 6 files changed, 45 insertions(+), 15 deletions(-) create mode 100644 LICENSE diff --git a/.coveralls.yml b/.coveralls.yml index c099fdc..65bf7c7 100644 --- a/.coveralls.yml +++ b/.coveralls.yml @@ -1 +1 @@ -repo_token: vnqpbiGFbHmVwNwcJnseZAZ5X33epllne +repo_token: bqiXmaOA0xarSUociqxGKJ72bp8SSHlQC diff --git a/.gitignore b/.gitignore index 2d3bb39..e0b6b81 100755 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /node_modules /coverage npm-debug.log +.coveralls.yml diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..183e8d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2015, Gajus Kuizinas (http://gajus.com/) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Gajus Kuizinas (http://gajus.com/) nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ANUARY BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/package.json b/package.json index 2a68388..2e6a2aa 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-css-modules", "description": "Seamless mapping of class names to CSS modules inside of React components.", - "main": "dist/index.js", + "main": "./dist/index.js", "repository": { "type": "git", "url": "https://github.com/gajus/react-css-modules" @@ -23,22 +23,23 @@ "lodash": "^3.10.1" }, "devDependencies": { - "babel": "^5.8.21", - "babel-eslint": "^4.1.0", - "babel-istanbul": "^0.3.19", + "babel": "^5.8.23", + "babel-eslint": "^4.1.2", + "babel-istanbul": "^0.3.20", "chai": "^3.2.0", "coveralls": "^2.11.4", - "eslint": "^1.2.1", - "eslint-plugin-react": "^3.3.0", - "jsdom": "^6.2.0", - "mocha": "^2.2.5", - "mocha-lcov-reporter": "0.0.2", - "react": "^0.14.0-beta3", - "react-addons-test-utils": "^0.14.0-beta3" + "eslint": "^1.4.3", + "eslint-plugin-react": "^3.3.2", + "jsdom": "^6.5.0", + "mocha": "^2.3.2", + "mocha-lcov-reporter": "^0.0.2", + "react": "^0.14.0-rc1", + "react-addons-test-utils": "^0.14.0-rc1" }, "scripts": { "lint": "./node_modules/.bin/eslint ./src/ ./test/", - "test": "npm run lint && node ./node_modules/.bin/babel-istanbul cover ./node_modules/.bin/_mocha && cat ./coverage/lcov.info | coveralls", + "quick-test": "npm run lint && node ./node_modules/.bin/babel-istanbul cover ./node_modules/.bin/_mocha", + "test": "npm run quick-test && cat ./coverage/lcov.info | ./node_modules/.bin/coveralls", "build": "babel ./src/ --out-dir ./dist/", "watch": "babel --watch ./src/ --out-dir ./dist/" } diff --git a/test/linkClass.js b/test/linkClass.js index 773cb37..273e4d8 100644 --- a/test/linkClass.js +++ b/test/linkClass.js @@ -1,3 +1,5 @@ +/* eslint-disable max-nested-callbacks */ + import { expect } from 'chai'; @@ -92,8 +94,8 @@ describe('linkClass', () => { bar: 'bar-1' }); - expect(subject.props.children['.0'].props.className).to.equal('foo-1'); - expect(subject.props.children['.1'].props.className).to.equal('bar-1'); + expect(subject.props.children[0].props.className).to.equal('foo-1'); + expect(subject.props.children[1].props.className).to.equal('bar-1'); }); }); context('when ReactElement does not have an existing className', () => { diff --git a/test/makeConfiguration.js b/test/makeConfiguration.js index f805dde..3a74ae7 100644 --- a/test/makeConfiguration.js +++ b/test/makeConfiguration.js @@ -1,3 +1,5 @@ +/* eslint-disable max-nested-callbacks */ + import { expect } from 'chai'; From a088497fe0c1aa755cbcfe8ae4a177306d7ce37e Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Wed, 16 Sep 2015 13:01:36 +0100 Subject: [PATCH 041/229] 3.0.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2e6a2aa..c025307 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "css", "modules" ], - "version": "3.0.5", + "version": "3.0.6", "author": { "name": "Gajus Kuizinas", "email": "gk@anuary.com", From c4bd4c19021e8f3b4800a8eac159acc22d270ebc Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Wed, 16 Sep 2015 13:59:56 +0100 Subject: [PATCH 042/229] Simplified the logic for determining when to link children. --- dist/linkClass.js | 38 ++++++++++++++++++++------------------ src/linkClass.js | 41 +++++++++++++++++++++-------------------- 2 files changed, 41 insertions(+), 38 deletions(-) diff --git a/dist/linkClass.js b/dist/linkClass.js index f04da0b..2944de5 100644 --- a/dist/linkClass.js +++ b/dist/linkClass.js @@ -30,7 +30,6 @@ linkClass = function (element, styles, userConfiguration) { if (styles === undefined) styles = {}; var appendClassName = undefined, - childrenCount = undefined, clonedElement = undefined, configuration = undefined, newChildren = undefined, @@ -67,26 +66,29 @@ linkClass = function (element, styles, userConfiguration) { appendClassName = appendClassName.join(' '); } - if (_utils2['default'].isArray(element.props.children) || _react2['default'].isValidElement(element.props.children)) { - childrenCount = _react2['default'].Children.count(element.props.children); + // element.props.children can be one of the following: + // 'text' + // ['text'] + // [ReactElement, 'text'] + // ReactElement - // console.log('childrenCount', childrenCount, 'element.props.children', element.props.children); + // console.log(`element.props.children`, element.props.children, `React.Children.count(element.props.children)`, React.Children.count(element.props.children)); - if (childrenCount > 1 || _utils2['default'].isArray(element.props.children)) { - newChildren = _react2['default'].Children.map(element.props.children, function (node) { - if (_react2['default'].isValidElement(node)) { - return linkClass(node, styles, configuration); - } else { - return node; - } - }); - // https://github.com/facebook/react/issues/4723#issuecomment-135555277 - // Forcing children into an array produces the following error: - // Warning: A ReactFragment is an opaque type. Accessing any of its properties is deprecated. Pass it to one of the React.Children helpers. - // newChildren = _.values(newChildren); - } else if (childrenCount === 1) { - newChildren = linkClass(_react2['default'].Children.only(element.props.children), styles, configuration); + if (_react2['default'].isValidElement(element.props.children)) { + newChildren = linkClass(_react2['default'].Children.only(element.props.children), styles, configuration); + } else if (_utils2['default'].isArray(element.props.children)) { + newChildren = _react2['default'].Children.map(element.props.children, function (node) { + if (_react2['default'].isValidElement(node)) { + return linkClass(node, styles, configuration); + } else { + return node; } + }); + + // https://github.com/facebook/react/issues/4723#issuecomment-135555277 + // Forcing children into an array produces the following error: + // Warning: A ReactFragment is an opaque type. Accessing any of its properties is deprecated. Pass it to one of the React.Children helpers. + // newChildren = _.values(newChildren); } if (appendClassName) { diff --git a/src/linkClass.js b/src/linkClass.js index bef0e5a..ba28f56 100644 --- a/src/linkClass.js +++ b/src/linkClass.js @@ -12,7 +12,6 @@ let linkClass; */ linkClass = (element, styles = {}, userConfiguration) => { let appendClassName, - childrenCount, clonedElement, configuration, newChildren, @@ -49,28 +48,30 @@ linkClass = (element, styles = {}, userConfiguration) => { appendClassName = appendClassName.join(' '); } - if (_.isArray(element.props.children) || React.isValidElement(element.props.children)) { - childrenCount = React.Children.count(element.props.children); + // element.props.children can be one of the following: + // 'text' + // ['text'] + // [ReactElement, 'text'] + // ReactElement - // console.log('childrenCount', childrenCount, 'element.props.children', element.props.children); + // console.log(`element.props.children`, element.props.children, `React.Children.count(element.props.children)`, React.Children.count(element.props.children)); - if (childrenCount > 1 || _.isArray(element.props.children)) { - newChildren = React.Children.map(element.props.children, (node) => { - if (React.isValidElement(node)) { - return linkClass(node, styles, configuration); - } else { - return node; - } - }); - // https://github.com/facebook/react/issues/4723#issuecomment-135555277 - // Forcing children into an array produces the following error: - // Warning: A ReactFragment is an opaque type. Accessing any of its properties is deprecated. Pass it to one of the React.Children helpers. - // newChildren = _.values(newChildren); - } else if (childrenCount === 1) { - newChildren = linkClass(React.Children.only(element.props.children), styles, configuration); - } - } + if (React.isValidElement(element.props.children)) { + newChildren = linkClass(React.Children.only(element.props.children), styles, configuration); + } else if (_.isArray(element.props.children)) { + newChildren = React.Children.map(element.props.children, (node) => { + if (React.isValidElement(node)) { + return linkClass(node, styles, configuration); + } else { + return node; + } + }); + // https://github.com/facebook/react/issues/4723#issuecomment-135555277 + // Forcing children into an array produces the following error: + // Warning: A ReactFragment is an opaque type. Accessing any of its properties is deprecated. Pass it to one of the React.Children helpers. + // newChildren = _.values(newChildren); + } if (appendClassName) { if (element.props.className) { From 9cad95b6a067e9dfb831cf43f53ac530c127e347 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Wed, 16 Sep 2015 14:04:31 +0100 Subject: [PATCH 043/229] Using babel-plugin-lodash. --- .babelrc | 5 ++++- dist/linkClass.js | 12 ++++++------ dist/makeConfiguration.js | 10 +++++----- package.json | 1 + src/linkClass.js | 2 +- src/makeConfiguration.js | 2 +- src/utils.js | 9 --------- 7 files changed, 18 insertions(+), 23 deletions(-) delete mode 100644 src/utils.js diff --git a/.babelrc b/.babelrc index 12606a3..89c0540 100644 --- a/.babelrc +++ b/.babelrc @@ -1,3 +1,6 @@ { - "stage": 0 + "stage": 0, + "plugins": [ + "lodash" + ] } diff --git a/dist/linkClass.js b/dist/linkClass.js index 2944de5..bcbda87 100644 --- a/dist/linkClass.js +++ b/dist/linkClass.js @@ -1,11 +1,13 @@ 'use strict'; +var _lodashLangIsArray2 = require('lodash/lang/isArray'); + +var _lodashLangIsArray3 = _interopRequireDefault(_lodashLangIsArray2); + Object.defineProperty(exports, '__esModule', { value: true }); -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - var _react = require('react'); var _react2 = _interopRequireDefault(_react); @@ -14,9 +16,7 @@ var _makeConfiguration = require('./makeConfiguration'); var _makeConfiguration2 = _interopRequireDefault(_makeConfiguration); -var _utils = require('./utils'); - -var _utils2 = _interopRequireDefault(_utils); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var linkClass = undefined; @@ -76,7 +76,7 @@ linkClass = function (element, styles, userConfiguration) { if (_react2['default'].isValidElement(element.props.children)) { newChildren = linkClass(_react2['default'].Children.only(element.props.children), styles, configuration); - } else if (_utils2['default'].isArray(element.props.children)) { + } else if ((0, _lodashLangIsArray3['default'])(element.props.children)) { newChildren = _react2['default'].Children.map(element.props.children, function (node) { if (_react2['default'].isValidElement(node)) { return linkClass(node, styles, configuration); diff --git a/dist/makeConfiguration.js b/dist/makeConfiguration.js index d42ad79..b528fa5 100644 --- a/dist/makeConfiguration.js +++ b/dist/makeConfiguration.js @@ -1,15 +1,15 @@ 'use strict'; +var _lodashCollectionForEach2 = require('lodash/collection/forEach'); + +var _lodashCollectionForEach3 = _interopRequireDefault(_lodashCollectionForEach2); + Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } -var _utils = require('./utils'); - -var _utils2 = _interopRequireDefault(_utils); - /** * @typedef CSSModules~Options * @see {@link https://github.com/gajus/react-css-modules#options} @@ -32,7 +32,7 @@ exports['default'] = function () { errorWhenNotFound: true }; - _utils2['default'].forEach(userConfiguration, function (value, name) { + (0, _lodashCollectionForEach3['default'])(userConfiguration, function (value, name) { if (typeof configuration[name] === 'undefined') { throw new Error('Unknown configuration property "' + name + '".'); } diff --git a/package.json b/package.json index c025307..31fc90f 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "babel": "^5.8.23", "babel-eslint": "^4.1.2", "babel-istanbul": "^0.3.20", + "babel-plugin-lodash": "^0.2.0", "chai": "^3.2.0", "coveralls": "^2.11.4", "eslint": "^1.4.3", diff --git a/src/linkClass.js b/src/linkClass.js index ba28f56..6438591 100644 --- a/src/linkClass.js +++ b/src/linkClass.js @@ -1,6 +1,6 @@ import React from 'react'; import makeConfiguration from './makeConfiguration'; -import _ from './utils'; +import _ from 'lodash'; let linkClass; diff --git a/src/makeConfiguration.js b/src/makeConfiguration.js index e521af5..6889c75 100644 --- a/src/makeConfiguration.js +++ b/src/makeConfiguration.js @@ -1,4 +1,4 @@ -import _ from './utils'; +import _ from 'lodash'; /** * @typedef CSSModules~Options diff --git a/src/utils.js b/src/utils.js deleted file mode 100644 index 0574ca3..0000000 --- a/src/utils.js +++ /dev/null @@ -1,9 +0,0 @@ -import forEach from 'lodash/collection/forEach'; -import values from 'lodash/object/values'; -import isArray from 'lodash/lang/isArray'; - -export default { - forEach, - values, - isArray -}; From 764cc6e9dfa68418e1ae4e894ea9782ee712b385 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Wed, 16 Sep 2015 15:09:19 +0100 Subject: [PATCH 044/229] Using table example instead of car. --- README.md | 60 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 2b47230..0c9beb2 100644 --- a/README.md +++ b/README.md @@ -35,13 +35,15 @@ In the context of React, CSS Modules look like this: ```js import React from 'react'; -import styles from './car.css'; +import styles from './table.css'; -export default class Car extends React.Component { +export default class Table extends React.Component { render () { - return
-
-
+ return
+
+
A0
+
B0
+
; } } @@ -50,9 +52,11 @@ export default class Car extends React.Component { Rendering the component will produce a markup similar to: ```js -
-
front-door
-
back-door
+
+
+
A0
+
B0
+
``` @@ -77,19 +81,21 @@ React CSS Modules component automates loading of CSS Modules using `styleName` p ```js import React from 'react'; -import styles from './car.css'; import CSSModules from 'react-css-modules'; +import styles from './table.css'; -class Car extends React.Component { +class Table extends React.Component { render () { - return
-
-
+ return
+
+
A0
+
B0
+
; } } -export default CSSModules(Car, styles); +export default CSSModules(Table, styles); ``` Using `react-css-modules`: @@ -159,7 +165,7 @@ Refer to [`css-modulesify`](https://github.com/css-modules/css-modulesify). /** * @param {Function} Component - * @param {Object} styles CSS Modules class map. + * @param {Object} defaultStyles CSS Modules class map. * @param {CSSModules~Options} options * @return {Function} */ @@ -169,19 +175,21 @@ You need to decorate your component using `react-css-modules`, e.g. ```js import React from 'react'; -import styles from './car.css'; import CSSModules from 'react-css-modules'; +import styles from './table.css'; -class Car extends React.Component { +class Table extends React.Component { render () { - return
-
-
+ return
+
+
A0
+
B0
+
; } } -export default CSSModules(Car, styles); +export default CSSModules(Table, styles); ``` Thats it! @@ -190,15 +198,17 @@ As the name implies, `react-css-modules` is compatible with the [ES7 decorators] ```js import React from 'react'; -import styles from './car.css'; import CSSModules from 'react-css-modules'; +import styles from './table.css'; @CSSModules(styles) export default class extends React.Component { render () { - return
-
front-door
-
back-door
+ return
+
+
A0
+
B0
+
; } } From 94d66ee001e74dc39bbfe7a7af05672f74908cf9 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Wed, 16 Sep 2015 15:30:10 +0100 Subject: [PATCH 045/229] Added "styles" property. --- README.md | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++ dist/index.js | 28 ++++++++++++++++------ src/index.js | 21 +++++++++++++---- 3 files changed, 102 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 0c9beb2..3f2eb1c 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ React CSS Modules implement automatic mapping of CSS modules. Every CSS class is - [Module Bundler](#module-bundler) - [webpack](#webpack) - [Browserify](#browserify) + - [Extending CSS Styles](#extending-css-styles) - [Decorator](#decorator) - [Options](#options) - [`allowMultiple`](#allowmultiple) @@ -153,6 +154,70 @@ Refer to [webpack-demo](https://github.com/css-modules/webpack-demo) or [react-c Refer to [`css-modulesify`](https://github.com/css-modules/css-modulesify). +### Extending CSS Styles + +Use `styles` property to overwrite the default component styles. + +Explanation using `Table` component: + +```js +import React from 'react'; +import CSSModules from 'react-css-modules'; +import styles from './table.css'; + +class Table extends React.Component { + render () { + return
+
+
A0
+
B0
+
+
; + } +} + +export default CSSModules(Table, styles); +``` + +In this example, `CSSModules` is used to decorate `Table` component using `./table.css` CSS Modules. When `Table` component is rendered, it will use the properties of the `styles` object to construct `className` values. + +Using `styles` property you can overwrite the default component `styles` object, e.g. + +```js +import customStyles from './table-custom-styles.css'; + +; +``` + +[Interoperable CSS](https://github.com/css-modules/icss) can [extend other ICSS](https://github.com/css-modules/css-modules#dependencies). Use this feature to extend default styles, e.g. + +```css +/* table-custom-styles.css */ +.table { + composes: table from './table.css'; +} + +.row { + composes: row from './table.css'; +} + +/* .cell { + composes: cell from './table.css'; +} */ + +.table { + width: 400px; +} + +.cell { + float: left; width: 154px; background: #eee; padding: 10px; margin: 10px 0 10px 10px; +} +``` + +In this example, `table-custom-styles.css` selectively extends `table.css` (the default styles of `Table` component). + +Refer to the [`UsingStylesProperty` example](https://github.com/gajus/react-css-modules-examples/tree/master/src/UsingStylesProperty) for an example of a working implementation. + ### Decorator ```js diff --git a/dist/index.js b/dist/index.js index c50da0c..9c0a983 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,5 +1,9 @@ 'use strict'; +var _lodashLangIsObject2 = require('lodash/lang/isObject'); + +var _lodashLangIsObject3 = _interopRequireDefault(_lodashLangIsObject2); + Object.defineProperty(exports, '__esModule', { value: true }); @@ -8,8 +12,6 @@ var _createClass = (function () { function defineProperties(target, props) { for var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } @@ -18,6 +20,8 @@ var _linkClass = require('./linkClass'); var _linkClass2 = _interopRequireDefault(_linkClass); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + var decoratorConstructor = undefined, functionConstructor = undefined; @@ -25,11 +29,11 @@ var decoratorConstructor = undefined, * When used as a function. * * @param {Function} Component - * @param {Object} styles CSS Modules class map. + * @param {Object} defaultStyles CSS Modules class map. * @param {Object} options {@link https://github.com/gajus/react-css-modules#options} * @return {Function} */ -functionConstructor = function (Component, styles, options) { +functionConstructor = function (Component, defaultStyles, options) { return (function (_Component) { _inherits(_class, _Component); @@ -42,6 +46,16 @@ functionConstructor = function (Component, styles, options) { _createClass(_class, [{ key: 'render', value: function render() { + var styles = undefined; + + if (this.props.styles) { + styles = this.props.styles; + } else if ((0, _lodashLangIsObject3['default'])(defaultStyles)) { + styles = defaultStyles; + } else { + styles = {}; + } + return (0, _linkClass2['default'])(_get(Object.getPrototypeOf(_class.prototype), 'render', this).call(this), styles, options); } }]); @@ -53,13 +67,13 @@ functionConstructor = function (Component, styles, options) { /** * When used as a ES7 decorator. * - * @param {Object} styles CSS Modules class map. + * @param {Object} defaultStyles CSS Modules class map. * @param {Object} options {@link https://github.com/gajus/react-css-modules#options} * @return {Function} */ -decoratorConstructor = function (styles, options) { +decoratorConstructor = function (defaultStyles, options) { return function (Component) { - return functionConstructor(Component, styles, options); + return functionConstructor(Component, defaultStyles, options); }; }; diff --git a/src/index.js b/src/index.js index 75c3bff..3a0e728 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,5 @@ import linkClass from './linkClass'; +import _ from 'lodash'; let decoratorConstructor, functionConstructor; @@ -7,13 +8,23 @@ let decoratorConstructor, * When used as a function. * * @param {Function} Component - * @param {Object} styles CSS Modules class map. + * @param {Object} defaultStyles CSS Modules class map. * @param {Object} options {@link https://github.com/gajus/react-css-modules#options} * @return {Function} */ -functionConstructor = (Component, styles, options) => { +functionConstructor = (Component, defaultStyles, options) => { return class extends Component { render () { + let styles; + + if (this.props.styles) { + styles = this.props.styles; + } else if (_.isObject(defaultStyles)) { + styles = defaultStyles; + } else { + styles = {}; + } + return linkClass(super.render(), styles, options); } }; @@ -22,13 +33,13 @@ functionConstructor = (Component, styles, options) => { /** * When used as a ES7 decorator. * - * @param {Object} styles CSS Modules class map. + * @param {Object} defaultStyles CSS Modules class map. * @param {Object} options {@link https://github.com/gajus/react-css-modules#options} * @return {Function} */ -decoratorConstructor = (styles, options) => { +decoratorConstructor = (defaultStyles, options) => { return (Component) => { - return functionConstructor(Component, styles, options); + return functionConstructor(Component, defaultStyles, options); }; }; From 83bc3582454fb2a087c011d3da0a130635b2b8ff Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Wed, 16 Sep 2015 15:30:14 +0100 Subject: [PATCH 046/229] 3.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 31fc90f..3f3319b 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "css", "modules" ], - "version": "3.0.6", + "version": "3.1.0", "author": { "name": "Gajus Kuizinas", "email": "gk@anuary.com", From b30527c5921d584515afc2c5e84296abc4929e79 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Wed, 16 Sep 2015 15:32:34 +0100 Subject: [PATCH 047/229] Renamed README section. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3f2eb1c..68c603c 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ React CSS Modules implement automatic mapping of CSS modules. Every CSS class is - [Module Bundler](#module-bundler) - [webpack](#webpack) - [Browserify](#browserify) - - [Extending CSS Styles](#extending-css-styles) + - [Extending Component Styles](#extending-component-styles) - [Decorator](#decorator) - [Options](#options) - [`allowMultiple`](#allowmultiple) @@ -154,7 +154,7 @@ Refer to [webpack-demo](https://github.com/css-modules/webpack-demo) or [react-c Refer to [`css-modulesify`](https://github.com/css-modules/css-modulesify). -### Extending CSS Styles +### Extending Component Styles Use `styles` property to overwrite the default component styles. From b6558b574da667b43df04236232fd9ff1b125612 Mon Sep 17 00:00:00 2001 From: goncalvesjoao Date: Thu, 17 Sep 2015 12:00:26 +0100 Subject: [PATCH 048/229] rendering