From b5d84c16027d301edbbc28103bab9792dfd01ebb Mon Sep 17 00:00:00 2001 From: Nikita Gubchenko Date: Sun, 2 Apr 2017 20:12:01 +0300 Subject: [PATCH 01/36] feat: allow any whitespace character to separate multiple styles (#226) Currently it is impossible to use delimiters in styleName other than a single space symbol. The change allows to use any whitespace characters, such as a newline character which is handy if you use multiple class names in a single styleName attribute combined with ES6 string literal. --- src/parseStyleName.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parseStyleName.js b/src/parseStyleName.js index 7b5ee55..937b2d6 100644 --- a/src/parseStyleName.js +++ b/src/parseStyleName.js @@ -8,7 +8,7 @@ export default (styleNamePropertyValue: string, allowMultiple: boolean): Array Date: Mon, 10 Apr 2017 10:55:23 +0300 Subject: [PATCH 02/36] docs: update ExtractTextPlugin v2x setup docs (#229) Source: https://github.com/webpack-contrib/extract-text-webpack-plugin#usage Otherwise breaking change error will be thrown, e.g. ``` fallbackLoader option has been deprecated - replace with "fallback" ``` --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1a154c4..7340c6d 100644 --- a/README.md +++ b/README.md @@ -210,8 +210,8 @@ Setup: { test: /\.css$/, loader: ExtractTextPlugin.extract({ - notExtractLoader: 'style-loader', - loader: 'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]!resolve-url!postcss', + fallback: 'style-loader', + use: 'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]!resolve-url!postcss', }), } ``` From 1eff8c3a8edb7ccbbd0109f9598a50bee9354d2f Mon Sep 17 00:00:00 2001 From: Federico Zivolo Date: Thu, 20 Apr 2017 20:59:41 +0200 Subject: [PATCH 03/36] fix: Make `styles` property always not enumerable (#231) fixes #217 --- package.json | 2 +- src/extendReactClass.js | 23 ++++++++++------------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 6faad43..0c9501f 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "css", "modules" ], - "version": "4.0.3", + "version": "4.0.4", "author": { "name": "Gajus Kuizinas", "email": "gajus@gajus.com", diff --git a/src/extendReactClass.js b/src/extendReactClass.js index 33858bd..6eba084 100644 --- a/src/extendReactClass.js +++ b/src/extendReactClass.js @@ -15,37 +15,34 @@ import renderNothing from './renderNothing'; export default (Component: Object, defaultStyles: Object, options: Object) => { const WrappedComponent = class extends Component { render () { - let propsChanged; let styles; - propsChanged = false; + const hasDefaultstyles = _.isObject(defaultStyles); - if (this.props.styles) { - styles = this.props.styles; - } else if (_.isObject(defaultStyles)) { + if (this.props.styles || hasDefaultstyles) { const props = Object.assign({}, this.props); + if (this.props.styles) { + styles = this.props.styles; + } else if (hasDefaultstyles) { + styles = defaultStyles; + delete this.props.styles; + } + Object.defineProperty(props, 'styles', { configurable: true, enumerable: false, - value: defaultStyles, + value: styles, writable: false }); this.props = props; - - propsChanged = true; - styles = defaultStyles; } else { styles = {}; } const renderResult = super.render(); - if (propsChanged) { - delete this.props.styles; - } - if (renderResult) { return linkClass(renderResult, styles, options); } From b8b10cd0cb72f00ed740086679d26abd9e0578e3 Mon Sep 17 00:00:00 2001 From: Abraham Berzunza Date: Sat, 22 Apr 2017 04:01:58 -0500 Subject: [PATCH 04/36] docs: detail about the configuration in production webpack v2x modified (#232) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7340c6d..47072fa 100644 --- a/README.md +++ b/README.md @@ -209,9 +209,9 @@ Setup: ```js { test: /\.css$/, - loader: ExtractTextPlugin.extract({ + use: ExtractTextPlugin.extract({ fallback: 'style-loader', - use: 'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]!resolve-url!postcss', + use: 'css-loader?modules,localIdentName="[name]-[local]-[hash:base64:6]"' }), } ``` From 1c43518f2e6d79df024e91a5dc88ce3facecd310 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Sun, 23 Apr 2017 12:09:28 +0100 Subject: [PATCH 05/36] =?UTF-8?q?docs:=20add=20=E2=9A=A0=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 47072fa..4721800 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ 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! +> ⚠️⚠️⚠️ +> > Note: > > If you are considering to use `react-css-modules`, evaluate if [`babel-plugin-react-css-modules`](https://github.com/gajus/babel-plugin-react-css-modules) covers your use case. From 067be0a96e1b5d8a0b96cc600fbaa7d859df3dc9 Mon Sep 17 00:00:00 2001 From: Federico Zivolo Date: Fri, 28 Apr 2017 18:23:49 +0200 Subject: [PATCH 06/36] fix #217 for functional components (#235) Revert "fix: Make `styles` property always not enumerable" This reverts commit f2e1bee75cc23aea2d4a66512d0c0b1f0341bdca. --- src/wrapStatelessFunction.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/wrapStatelessFunction.js b/src/wrapStatelessFunction.js index 304fd58..b6b9162 100644 --- a/src/wrapStatelessFunction.js +++ b/src/wrapStatelessFunction.js @@ -12,23 +12,23 @@ export default (Component: Function, defaultStyles: Object, options: Object): Fu const WrappedComponent = (props = {}, ...args) => { let styles; let useProps; + const hasDefaultstyles = _.isObject(defaultStyles); - if (props.styles) { - useProps = props; - styles = props.styles; - } else if (_.isObject(defaultStyles)) { - useProps = _.assign({}, props, { - styles: defaultStyles - }); + if (props.styles || hasDefaultstyles) { + useProps = Object.assign({}, props); + + if (props.styles) { + styles = props.styles; + } else { + styles = defaultStyles; + } Object.defineProperty(useProps, 'styles', { configurable: true, enumerable: false, - value: defaultStyles, + value: styles, writable: false }); - - styles = defaultStyles; } else { useProps = props; styles = {}; From ee53de7c825a1654b61b0d323e9ec9a4d7c6c52f Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Mon, 1 May 2017 13:59:41 +0100 Subject: [PATCH 07/36] 4.0.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0c9501f..ee27d54 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "css", "modules" ], - "version": "4.0.4", + "version": "4.0.5", "author": { "name": "Gajus Kuizinas", "email": "gajus@gajus.com", From 113d553c2f57cb27f7860dc893aa911b23f8ced7 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Tue, 16 May 2017 13:31:39 +0100 Subject: [PATCH 08/36] fix: force new release (fixes #238) From 1e7f84885911c0a56a22639ad78d561988090454 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Wed, 17 May 2017 12:49:25 +0100 Subject: [PATCH 09/36] 4.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ee27d54..03af054 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "css", "modules" ], - "version": "4.0.5", + "version": "4.1.0", "author": { "name": "Gajus Kuizinas", "email": "gajus@gajus.com", From 623954c1410e026fa7a039e7135a3c5b90dbdeb6 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Wed, 17 May 2017 12:49:43 +0100 Subject: [PATCH 10/36] 4.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 03af054..ddfeabf 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "css", "modules" ], - "version": "4.1.0", + "version": "4.2.0", "author": { "name": "Gajus Kuizinas", "email": "gajus@gajus.com", From 144a494d4249811a72b2030dd2d0e325cef5e3c7 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Wed, 17 May 2017 12:49:51 +0100 Subject: [PATCH 11/36] 4.3.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ddfeabf..3253cc6 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "css", "modules" ], - "version": "4.2.0", + "version": "4.3.0", "author": { "name": "Gajus Kuizinas", "email": "gajus@gajus.com", From 407cdb492e738d7194be4e6152742ddeacce4e20 Mon Sep 17 00:00:00 2001 From: Leonid Nikiforenko Date: Sat, 1 Jul 2017 14:15:06 +0300 Subject: [PATCH 12/36] feat: do not change keys on children (#242) --- src/linkClass.js | 16 +++++++++++++++- tests/linkClass.js | 14 ++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/linkClass.js b/src/linkClass.js index 6e1a362..a9883dd 100644 --- a/src/linkClass.js +++ b/src/linkClass.js @@ -7,6 +7,20 @@ import isIterable from './isIterable'; import parseStyleName from './parseStyleName'; import generateAppendClassName from './generateAppendClassName'; +const mapChildrenWithoutKeyPrefix = (children: ReactElement, mapper: Function, context: Object) => { + if (typeof children === 'undefined' || children === null) { + return children; + } + + const result = []; + + React.Children.forEach(children, (child, index) => { + result.push(mapper.call(context, child, index)); + }); + + return result; +}; + const linkElement = (element: ReactElement, styles: Object, configuration: Object): ReactElement => { let appendClassName; let elementIsFrozen; @@ -27,7 +41,7 @@ const linkElement = (element: ReactElement, styles: Object, configuration: Objec if (React.isValidElement(elementShallowCopy.props.children)) { elementShallowCopy.props.children = linkElement(React.Children.only(elementShallowCopy.props.children), styles, configuration); } else if (_.isArray(elementShallowCopy.props.children) || isIterable(elementShallowCopy.props.children)) { - elementShallowCopy.props.children = React.Children.map(elementShallowCopy.props.children, (node) => { + elementShallowCopy.props.children = mapChildrenWithoutKeyPrefix(elementShallowCopy.props.children, (node) => { if (React.isValidElement(node)) { return linkElement(node, styles, configuration); } else { diff --git a/tests/linkClass.js b/tests/linkClass.js index d2cd061..62fbc83 100644 --- a/tests/linkClass.js +++ b/tests/linkClass.js @@ -290,4 +290,18 @@ describe('linkClass', () => { expect(subject.props.children[0].props.className).to.deep.equal('bar-1'); expect(subject.props.children[0].props).not.to.have.property('styleName'); }); + + it('does not change defined keys of children if there are multiple children', () => { + let subject; + + subject =
+ + +
; + + subject = linkClass(subject); + + expect(subject.props.children[0].key).to.equal('foo'); + expect(subject.props.children[1].key).to.equal('bar'); + }); }); From b51b1f3dbfe50a5bda5e61019d352122cf188c7d Mon Sep 17 00:00:00 2001 From: Leonid Nikiforenko Date: Tue, 4 Jul 2017 00:01:05 +0300 Subject: [PATCH 13/36] feat: add support for non-`children` props (#243) --- src/linkClass.js | 41 ++++++++++++++++++++++++---- tests/linkClass.js | 67 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 101 insertions(+), 7 deletions(-) diff --git a/src/linkClass.js b/src/linkClass.js index a9883dd..cef60e8 100644 --- a/src/linkClass.js +++ b/src/linkClass.js @@ -21,6 +21,19 @@ const mapChildrenWithoutKeyPrefix = (children: ReactElement, mapper: Function, c return result; }; +const linkArray = (array: Array, styles: Object, configuration: Object) => { + return _.map(array, (value) => { + if (React.isValidElement(value)) { + // eslint-disable-next-line no-use-before-define + return linkElement(React.Children.only(value), styles, configuration); + } else if (_.isArray(value)) { + return linkArray(value, styles, configuration); + } + + return value; + }); +}; + const linkElement = (element: ReactElement, styles: Object, configuration: Object): ReactElement => { let appendClassName; let elementIsFrozen; @@ -37,19 +50,37 @@ const linkElement = (element: ReactElement, styles: Object, configuration: Objec } const styleNames = parseStyleName(elementShallowCopy.props.styleName || '', configuration.allowMultiple); + const {children, ...restProps} = elementShallowCopy.props; - if (React.isValidElement(elementShallowCopy.props.children)) { - elementShallowCopy.props.children = linkElement(React.Children.only(elementShallowCopy.props.children), styles, configuration); - } else if (_.isArray(elementShallowCopy.props.children) || isIterable(elementShallowCopy.props.children)) { - elementShallowCopy.props.children = mapChildrenWithoutKeyPrefix(elementShallowCopy.props.children, (node) => { + if (React.isValidElement(children)) { + elementShallowCopy.props.children = linkElement(React.Children.only(children), styles, configuration); + } else if (_.isArray(children) || isIterable(children)) { + elementShallowCopy.props.children = mapChildrenWithoutKeyPrefix(children, (node) => { if (React.isValidElement(node)) { - return linkElement(node, styles, configuration); + // eslint-disable-next-line no-use-before-define + return linkElement(React.Children.only(node), styles, configuration); } else { return node; } }); } + _.forEach(restProps, (propValue, propName) => { + if (React.isValidElement(propValue)) { + elementShallowCopy.props[propName] = linkElement(React.Children.only(propValue), styles, configuration); + } else if (_.isArray(propValue)) { + elementShallowCopy.props[propName] = _.map(propValue, (node) => { + if (React.isValidElement(node)) { + return linkElement(React.Children.only(node), styles, configuration); + } else if (_.isArray(node)) { + return linkArray(node, styles, configuration); + } + + return node; + }); + } + }); + if (styleNames.length) { appendClassName = generateAppendClassName(styles, styleNames, configuration.errorWhenNotFound); diff --git a/tests/linkClass.js b/tests/linkClass.js index 62fbc83..0e3cd06 100644 --- a/tests/linkClass.js +++ b/tests/linkClass.js @@ -18,6 +18,10 @@ describe('linkClass', () => { expect(linkClass(

)).to.deep.equal(

); }); + it('does not affect element properties with a single element child in non-`children` prop', () => { + expect(linkClass(
} />)).to.deep.equal(
} />); + }); + it('does not affect element properties with a single text child', () => { expect(linkClass(
test
)).to.deep.equal(
test
); }); @@ -81,6 +85,26 @@ describe('linkClass', () => { expect(subject.props.children.props.className).to.equal('foo-1'); }); }); + context('when a descendant element in non-`children` prop has styleName', () => { + it('assigns a generated className', () => { + let subject; + + subject =
} + els={[

, [

]]} + />; + + subject = linkClass(subject, { + bar: 'bar-1', + baz: 'baz-1', + foo: 'foo-1' + }); + + expect(subject.props.el.props.className).to.equal('foo-1'); + expect(subject.props.els[0].props.className).to.equal('bar-1'); + expect(subject.props.els[1][0].props.className).to.equal('baz-1'); + }); + }); context('when multiple descendant elements have styleName', () => { it('assigns a generated className', () => { let subject; @@ -139,6 +163,32 @@ describe('linkClass', () => { expect(subject.props.children[1].props.className).to.equal('bar-1'); }); }); + context('when non-`children` prop is an iterable', () => { + it('it is left untouched', () => { + let subject; + + const iterable = { + 0:

, + 1:

, + length: 2, + + // eslint-disable-next-line no-use-extend-native/no-use-extend-native + [Symbol.iterator]: Array.prototype[Symbol.iterator] + }; + + subject =

; + + subject = linkClass(subject, { + bar: 'bar-1', + foo: 'foo-1' + }); + + expect(subject.props.els[0].props.styleName).to.equal('foo'); + expect(subject.props.els[1].props.styleName).to.equal('bar'); + expect(subject.props.els[0].props).not.to.have.property('className'); + expect(subject.props.els[1].props).not.to.have.property('className'); + }); + }); context('when ReactElement does not have an existing className', () => { it('uses the generated class name to set the className property', () => { let subject; @@ -277,24 +327,35 @@ describe('linkClass', () => { it('deletes styleName property from the target element (deep)', () => { let subject; - subject =
+ subject =
} + els={[, []]} + styleName='foo' + >
; subject = linkClass(subject, { bar: 'bar-1', + baz: 'baz-1', foo: 'foo-1' }); expect(subject.props.children[0].props.className).to.deep.equal('bar-1'); expect(subject.props.children[0].props).not.to.have.property('styleName'); + expect(subject.props.el.props.className).to.deep.equal('baz-1'); + expect(subject.props.el.props).not.to.have.property('styleName'); + expect(subject.props.els[0].props.className).to.deep.equal('foo-1'); + expect(subject.props.els[0].props).not.to.have.property('styleName'); + expect(subject.props.els[1][0].props.className).to.deep.equal('bar-1'); + expect(subject.props.els[1][0].props).not.to.have.property('styleName'); }); it('does not change defined keys of children if there are multiple children', () => { let subject; - subject =
+ subject =
, ]}>
; @@ -303,5 +364,7 @@ describe('linkClass', () => { expect(subject.props.children[0].key).to.equal('foo'); expect(subject.props.children[1].key).to.equal('bar'); + expect(subject.props.els[0].key).to.equal('foo'); + expect(subject.props.els[1].key).to.equal('bar'); }); }); From 44746ac4f9b78161f1c64bd2272f0a17e57221d3 Mon Sep 17 00:00:00 2001 From: Leonid Nikiforenko Date: Tue, 4 Jul 2017 10:06:19 +0300 Subject: [PATCH 14/36] perf: optimize `linkArray` by replacing elements in original array (#244) --- src/linkClass.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/linkClass.js b/src/linkClass.js index cef60e8..e463b9d 100644 --- a/src/linkClass.js +++ b/src/linkClass.js @@ -22,16 +22,16 @@ const mapChildrenWithoutKeyPrefix = (children: ReactElement, mapper: Function, c }; const linkArray = (array: Array, styles: Object, configuration: Object) => { - return _.map(array, (value) => { + _.forEach(array, (value, index) => { if (React.isValidElement(value)) { // eslint-disable-next-line no-use-before-define - return linkElement(React.Children.only(value), styles, configuration); + array[index] = linkElement(React.Children.only(value), styles, configuration); } else if (_.isArray(value)) { - return linkArray(value, styles, configuration); + array[index] = linkArray(value, styles, configuration); } - - return value; }); + + return array; }; const linkElement = (element: ReactElement, styles: Object, configuration: Object): ReactElement => { From 10b6c01c1d46a173ad0bece8e0c24aef053fefb6 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Tue, 4 Jul 2017 18:52:29 +0100 Subject: [PATCH 15/36] fix: force release (fixes #244) From 76d8270986b7bcfb620cacabda7a978984daef09 Mon Sep 17 00:00:00 2001 From: Leonid Nikiforenko Date: Wed, 5 Jul 2017 19:37:51 +0300 Subject: [PATCH 16/36] fix: restore prefixing of react children keys (#246) --- src/linkClass.js | 16 +--------------- tests/linkClass.js | 16 ---------------- 2 files changed, 1 insertion(+), 31 deletions(-) diff --git a/src/linkClass.js b/src/linkClass.js index e463b9d..1a05878 100644 --- a/src/linkClass.js +++ b/src/linkClass.js @@ -7,20 +7,6 @@ import isIterable from './isIterable'; import parseStyleName from './parseStyleName'; import generateAppendClassName from './generateAppendClassName'; -const mapChildrenWithoutKeyPrefix = (children: ReactElement, mapper: Function, context: Object) => { - if (typeof children === 'undefined' || children === null) { - return children; - } - - const result = []; - - React.Children.forEach(children, (child, index) => { - result.push(mapper.call(context, child, index)); - }); - - return result; -}; - const linkArray = (array: Array, styles: Object, configuration: Object) => { _.forEach(array, (value, index) => { if (React.isValidElement(value)) { @@ -55,7 +41,7 @@ const linkElement = (element: ReactElement, styles: Object, configuration: Objec if (React.isValidElement(children)) { elementShallowCopy.props.children = linkElement(React.Children.only(children), styles, configuration); } else if (_.isArray(children) || isIterable(children)) { - elementShallowCopy.props.children = mapChildrenWithoutKeyPrefix(children, (node) => { + elementShallowCopy.props.children = React.Children.map(children, (node) => { if (React.isValidElement(node)) { // eslint-disable-next-line no-use-before-define return linkElement(React.Children.only(node), styles, configuration); diff --git a/tests/linkClass.js b/tests/linkClass.js index 0e3cd06..0475563 100644 --- a/tests/linkClass.js +++ b/tests/linkClass.js @@ -351,20 +351,4 @@ describe('linkClass', () => { expect(subject.props.els[1][0].props.className).to.deep.equal('bar-1'); expect(subject.props.els[1][0].props).not.to.have.property('styleName'); }); - - it('does not change defined keys of children if there are multiple children', () => { - let subject; - - subject =
, ]}> - - -
; - - subject = linkClass(subject); - - expect(subject.props.children[0].key).to.equal('foo'); - expect(subject.props.children[1].key).to.equal('bar'); - expect(subject.props.els[0].key).to.equal('foo'); - expect(subject.props.els[1].key).to.equal('bar'); - }); }); From bb23402a6aa275cfe6c7a9fc6d9a40a5571b9e70 Mon Sep 17 00:00:00 2001 From: p4bloch Date: Wed, 26 Jul 2017 17:16:57 -0300 Subject: [PATCH 17/36] feat: implement handleNotFoundStyleName (#249) * implement handleNotFoundStyleName * disable linter only for console * docs: reword --- README.md | 18 ++++++++----- package.json | 1 + src/generateAppendClassName.js | 12 ++++++--- src/linkClass.js | 2 +- src/makeConfiguration.js | 12 ++++++--- tests/linkClass.js | 46 +++++++++++++++++++++++----------- tests/makeConfiguration.js | 6 ++--- 7 files changed, 65 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 4721800..02e006d 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ React CSS Modules implement automatic mapping of CSS modules. Every CSS class is - [Decorator](#decorator) - [Options](#options) - [`allowMultiple`](#allowmultiple) - - [`errorWhenNotFound`](#errorwhennotfound) + - [`handleNotFoundStyleName`](#handlenotfoundstylename) - [SASS, SCSS, LESS and other CSS Preprocessors](#sass-scss-less-and-other-css-preprocessors) - [Enable Sourcemaps](#enable-sourcemaps) - [Class Composition](#class-composition) @@ -127,7 +127,7 @@ Using `react-css-modules`:
``` -* You are warned when `styleName` refers to an undefined CSS Module ([`errorWhenNotFound`](#errorwhennotfound) option). +* You are warned when `styleName` refers to an undefined CSS Module ([`handleNotFoundStyleName`](#handlenotfoundstylename) option). * You can enforce use of a single CSS module per `ReactElement` ([`allowMultiple`](#allowmultiple) option). ## The Implementation @@ -408,7 +408,7 @@ export default CSSModules(CustomList, styles); * @typedef CSSModules~Options * @see {@link https://github.com/gajus/react-css-modules#options} * @property {Boolean} allowMultiple - * @property {Boolean} errorWhenNotFound + * @property {String} handleNotFoundStyleName */ /** @@ -492,11 +492,17 @@ When `false`, the following will cause an error:
``` -#### `errorWhenNotFound` +#### `handleNotFoundStyleName` -Default: `true`. +Default: `throw`. -Throws an error when `styleName` cannot be mapped to an existing CSS Module. +Defines the desired action when `styleName` cannot be mapped to an existing CSS Module. + +Available options: + +* `throw` throws an error +* `log` logs a warning using `console.warn` +* `ignore` silently ignores the missing style name ## SASS, SCSS, LESS and other CSS Preprocessors diff --git a/package.json b/package.json index 3253cc6..e276aa4 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "babel-preset-stage-0": "^6.16.0", "babel-register": "^6.18.0", "chai": "^4.0.0-canary.1", + "chai-spies": "^0.7.1", "eslint": "^3.10.0", "eslint-config-canonical": "^5.5.0", "husky": "^0.11.9", diff --git a/src/generateAppendClassName.js b/src/generateAppendClassName.js index 9479698..127aa2a 100644 --- a/src/generateAppendClassName.js +++ b/src/generateAppendClassName.js @@ -4,7 +4,7 @@ const CustomMap = typeof Map === 'undefined' ? SimpleMap : Map; const stylesIndex = new CustomMap(); -export default (styles, styleNames: Array, errorWhenNotFound: boolean): string => { +export default (styles, styleNames: Array, handleNotFoundStyleName: "throw" | "log" | "ignore"): string => { let appendClassName; let stylesIndexMap; @@ -29,8 +29,14 @@ export default (styles, styleNames: Array, errorWhenNotFound: boolean): if (className) { appendClassName += ' ' + className; - } else if (errorWhenNotFound === true) { - throw new Error('"' + styleNames[styleName] + '" CSS module is undefined.'); + } else { + if (handleNotFoundStyleName === 'throw') { + throw new Error('"' + styleNames[styleName] + '" CSS module is undefined.'); + } + if (handleNotFoundStyleName === 'log') { + // eslint-disable-next-line no-console + console.warn('"' + styleNames[styleName] + '" CSS module is undefined.'); + } } } } diff --git a/src/linkClass.js b/src/linkClass.js index 1a05878..959d599 100644 --- a/src/linkClass.js +++ b/src/linkClass.js @@ -68,7 +68,7 @@ const linkElement = (element: ReactElement, styles: Object, configuration: Objec }); if (styleNames.length) { - appendClassName = generateAppendClassName(styles, styleNames, configuration.errorWhenNotFound); + appendClassName = generateAppendClassName(styles, styleNames, configuration.handleNotFoundStyleName); if (appendClassName) { if (elementShallowCopy.props.className) { diff --git a/src/makeConfiguration.js b/src/makeConfiguration.js index 0eae15f..2e3a2d6 100644 --- a/src/makeConfiguration.js +++ b/src/makeConfiguration.js @@ -4,7 +4,7 @@ import _ from 'lodash'; * @typedef CSSModules~Options * @see {@link https://github.com/gajus/react-css-modules#options} * @property {boolean} allowMultiple - * @property {boolean} errorWhenNotFound + * @property {string} handleNotFoundStyleName */ /** @@ -14,7 +14,7 @@ import _ from 'lodash'; export default (userConfiguration = {}) => { const configuration = { allowMultiple: false, - errorWhenNotFound: true + handleNotFoundStyleName: 'throw' }; _.forEach(userConfiguration, (value, name) => { @@ -22,8 +22,12 @@ export default (userConfiguration = {}) => { throw new Error('Unknown configuration property "' + name + '".'); } - if (!_.isBoolean(value)) { - throw new Error('"' + name + '" property value must be a boolean.'); + if (name === 'allowMultiple' && !_.isBoolean(value)) { + throw new Error('"allowMultiple" property value must be a boolean.'); + } + + if (name === 'handleNotFoundStyleName' && !['throw', 'log', 'ignore'].includes(value)) { + throw new Error('"handleNotFoundStyleName" property value must be "throw", "log" or "ignore".'); } configuration[name] = value; diff --git a/tests/linkClass.js b/tests/linkClass.js index 0475563..fdda06b 100644 --- a/tests/linkClass.js +++ b/tests/linkClass.js @@ -1,13 +1,16 @@ -/* eslint-disable max-nested-callbacks, react/prefer-stateless-function, class-methods-use-this */ +/* eslint-disable max-nested-callbacks, react/prefer-stateless-function, class-methods-use-this, no-console */ -import { +import chai, { expect } from 'chai'; +import spies from 'chai-spies'; import React from 'react'; import TestUtils from 'react-addons-test-utils'; import jsdom from 'jsdom'; import linkClass from './../src/linkClass'; +chai.use(spies); + describe('linkClass', () => { context('ReactElement does not define styleName', () => { it('does not affect element properties', () => { @@ -264,24 +267,37 @@ describe('linkClass', () => { }); }); - describe('options.errorWhenNotFound', () => { + describe('options.handleNotFoundStyleName', () => { context('when styleName does not match an existing CSS module', () => { - context('when false', () => { - it('ignores the missing CSS module', () => { - let subject; - - subject =
; - - subject = linkClass(subject, {}, {errorWhenNotFound: false}); + context('when throw', () => { + it('throws an error', () => { + expect(() => { + linkClass(
, {}, {handleNotFoundStyleName: 'throw'}); + }).to.throw(Error, '"foo" CSS module is undefined.'); + }); + }); + context('when log', () => { + it('logs a warning to the console', () => { + const warnSpy = chai.spy(() => {}); - expect(subject.props.className).to.be.an('undefined'); + console.warn = warnSpy; + linkClass(
, {}, {handleNotFoundStyleName: 'log'}); + expect(warnSpy).to.have.been.called(); }); }); - context('when is true', () => { - it('throws an error', () => { + context('when ignore', () => { + it('does not log a warning', () => { + const warnSpy = chai.spy(() => {}); + + console.warn = warnSpy; + linkClass(
, {}, {handleNotFoundStyleName: 'ignore'}); + expect(warnSpy).to.not.have.been.called(); + }); + + it('does not throw an error', () => { expect(() => { - linkClass(
, {}, {errorWhenNotFound: true}); - }).to.throw(Error, '"foo" CSS module is undefined.'); + linkClass(
, {}, {handleNotFoundStyleName: 'ignore'}); + }).to.not.throw(Error, '"foo" CSS module is undefined.'); }); }); }); diff --git a/tests/makeConfiguration.js b/tests/makeConfiguration.js index 94129fc..4193311 100644 --- a/tests/makeConfiguration.js +++ b/tests/makeConfiguration.js @@ -17,9 +17,9 @@ describe('makeConfiguration', () => { expect(configuration.allowMultiple).to.equal(false); }); }); - describe('errorWhenNotFound property', () => { - it('defaults to true', () => { - expect(configuration.errorWhenNotFound).to.equal(true); + describe('handleNotFoundStyleName property', () => { + it('defaults to "throw"', () => { + expect(configuration.handleNotFoundStyleName).to.equal('throw'); }); }); }); From 6d043d57ab1706fe3676b6115b1ff0b66c9ae0e8 Mon Sep 17 00:00:00 2001 From: Leonid Nikiforenko Date: Fri, 11 Aug 2017 13:53:55 +0300 Subject: [PATCH 18/36] feat: avoid cloning top level arrays in props (#253) --- src/linkClass.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/linkClass.js b/src/linkClass.js index 959d599..48142e1 100644 --- a/src/linkClass.js +++ b/src/linkClass.js @@ -55,15 +55,7 @@ const linkElement = (element: ReactElement, styles: Object, configuration: Objec if (React.isValidElement(propValue)) { elementShallowCopy.props[propName] = linkElement(React.Children.only(propValue), styles, configuration); } else if (_.isArray(propValue)) { - elementShallowCopy.props[propName] = _.map(propValue, (node) => { - if (React.isValidElement(node)) { - return linkElement(React.Children.only(node), styles, configuration); - } else if (_.isArray(node)) { - return linkArray(node, styles, configuration); - } - - return node; - }); + elementShallowCopy.props[propName] = linkArray(propValue, styles, configuration); } }); From 1952fb559fb9b071f33fd415b1cea780dfa32166 Mon Sep 17 00:00:00 2001 From: Leonid Nikiforenko Date: Wed, 16 Aug 2017 22:59:50 +0300 Subject: [PATCH 19/36] fix: use includes from `lodash` instead of a native one (#255) --- src/makeConfiguration.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/makeConfiguration.js b/src/makeConfiguration.js index 2e3a2d6..cc3169c 100644 --- a/src/makeConfiguration.js +++ b/src/makeConfiguration.js @@ -26,7 +26,7 @@ export default (userConfiguration = {}) => { throw new Error('"allowMultiple" property value must be a boolean.'); } - if (name === 'handleNotFoundStyleName' && !['throw', 'log', 'ignore'].includes(value)) { + if (name === 'handleNotFoundStyleName' && !_.includes(['throw', 'log', 'ignore'], value)) { throw new Error('"handleNotFoundStyleName" property value must be "throw", "log" or "ignore".'); } From 76a760fb897ec875fc3b9398a73c1405d603d6d1 Mon Sep 17 00:00:00 2001 From: Emma Simon Date: Wed, 21 Mar 2018 08:14:06 -0400 Subject: [PATCH 20/36] fix: React 16 issues (fixes #269, #200, #259) * Check if both element and props are frozen or not extensible (#200) Fixes #200 * Only check extensiblity of props Also run preventExtensions on it after modifications * Check if just the props are frozen (#266) * Recursively map element arrays Fixes #259 * Add tests For element arrays and frozen elements/props --- src/linkClass.js | 23 ++++++++++++--- tests/linkClass.js | 72 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 5 deletions(-) diff --git a/src/linkClass.js b/src/linkClass.js index 48142e1..4618446 100644 --- a/src/linkClass.js +++ b/src/linkClass.js @@ -22,17 +22,26 @@ const linkArray = (array: Array, styles: Object, configuration: Object) => { const linkElement = (element: ReactElement, styles: Object, configuration: Object): ReactElement => { let appendClassName; - let elementIsFrozen; let elementShallowCopy; elementShallowCopy = element; - if (Object.isFrozen && Object.isFrozen(elementShallowCopy)) { - elementIsFrozen = true; + if (Array.isArray(elementShallowCopy)) { + return elementShallowCopy.map((arrayElement) => { + return linkElement(arrayElement, styles, configuration); + }); + } - // https://github.com/facebook/react/blob/v0.13.3/src/classic/element/ReactElement.js#L131 + const elementIsFrozen = Object.isFrozen && Object.isFrozen(elementShallowCopy); + const propsFrozen = Object.isFrozen && Object.isFrozen(elementShallowCopy.props); + const propsNotExtensible = Object.isExtensible && !Object.isExtensible(elementShallowCopy.props); + + if (elementIsFrozen) { + // https://github.com/facebook/react/blob/v0.13.3/src/classic/element/ReactElement.js#L131 elementShallowCopy = objectUnfreeze(elementShallowCopy); elementShallowCopy.props = objectUnfreeze(elementShallowCopy.props); + } else if (propsFrozen || propsNotExtensible) { + elementShallowCopy.props = objectUnfreeze(elementShallowCopy.props); } const styleNames = parseStyleName(elementShallowCopy.props.styleName || '', configuration.allowMultiple); @@ -76,6 +85,12 @@ const linkElement = (element: ReactElement, styles: Object, configuration: Objec if (elementIsFrozen) { Object.freeze(elementShallowCopy.props); Object.freeze(elementShallowCopy); + } else if (propsFrozen) { + Object.freeze(elementShallowCopy.props); + } + + if (propsNotExtensible) { + Object.preventExtensions(elementShallowCopy.props); } return elementShallowCopy; diff --git a/tests/linkClass.js b/tests/linkClass.js index fdda06b..6a13f21 100644 --- a/tests/linkClass.js +++ b/tests/linkClass.js @@ -1,4 +1,4 @@ -/* eslint-disable max-nested-callbacks, react/prefer-stateless-function, class-methods-use-this, no-console */ +/* eslint-disable max-nested-callbacks, react/prefer-stateless-function, class-methods-use-this, no-console, no-unused-expressions */ import chai, { expect @@ -239,6 +239,76 @@ describe('linkClass', () => { }); }); + context('can\'t write to properties', () => { + context('when the element is frozen', () => { + it('adds className but is still frozen', () => { + let subject; + + subject =
; + + Object.freeze(subject); + subject = linkClass(subject, { + foo: 'foo-1' + }); + + expect(subject).to.be.frozen; + expect(subject.props.className).to.equal('foo-1'); + }); + }); + context('when the element\'s props are frozen', () => { + it('adds className and only props are still frozen', () => { + let subject; + + subject =
; + + Object.freeze(subject.props); + subject = linkClass(subject, { + foo: 'foo-1' + }); + + expect(subject.props).to.be.frozen; + expect(subject.props.className).to.equal('foo-1'); + }); + }); + context('when the element\'s props are not extensible', () => { + it('adds className and props are still not extensible', () => { + let subject; + + subject =
; + + Object.preventExtensions(subject.props); + subject = linkClass(subject, { + foo: 'foo-1' + }); + + expect(subject.props).to.not.be.extensible; + expect(subject.props.className).to.equal('foo-1'); + }); + }); + }); + + context('when element is an array', () => { + it('handles each element individually', () => { + let subject; + + subject = [ +
, +
+

+

+ ]; + + subject = linkClass(subject, { + bar: 'bar-1', + foo: 'foo-1' + }); + + expect(subject).to.be.an('array'); + expect(subject[0].props.className).to.equal('foo-1'); + expect(subject[1].props.children.props.className).to.equal('bar-1'); + }); + }); + describe('options.allowMultiple', () => { context('when multiple module names are used', () => { context('when false', () => { From 3699934cb4f09ff6eaa12646f242cce5a23c980d Mon Sep 17 00:00:00 2001 From: Saurav Kadavath Date: Mon, 16 Apr 2018 03:39:09 -0700 Subject: [PATCH 21/36] docs: update README.md to add '-loader' suffix in config (#274) It's no longer allowed to omit the '-loader' suffix when using loaders. You need to specify 'style-loader' instead of 'style', and similarly for 'css-loader' --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 02e006d..d16c474 100644 --- a/README.md +++ b/README.md @@ -161,8 +161,8 @@ Setup: { test: /\.css$/, loaders: [ - 'style?sourceMap', - 'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]' + 'style-loader?sourceMap', + 'css-loader?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]' ] } ``` From c4971f0d757ddba947fd407e5bcfdea2afb72345 Mon Sep 17 00:00:00 2001 From: Roman Hudec Date: Wed, 16 May 2018 10:08:37 +0200 Subject: [PATCH 22/36] fix: Flow "Cannot resolve module" errors on Windows (#280) Flow on Windows seems to be unable to correctly resolve imports from `react-css-modules` package, because its `main` entry in `package.json` points to a directory, instead of directly pointing to the script `dist/index.js`. Fixing it here seems to be easier than looking for a platform specific issue in Flow. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e276aa4..7e8a698 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/", + "main": "./dist/index.js", "repository": { "type": "git", "url": "https://github.com/gajus/react-css-modules" From da83f51bfb2dbc74c1dc0c11fc1167753e5ee99a Mon Sep 17 00:00:00 2001 From: Teemu Lahti Date: Mon, 9 Jul 2018 13:41:32 +0300 Subject: [PATCH 23/36] fix: use local copy of props instead of this.props (#284) --- src/extendReactClass.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/extendReactClass.js b/src/extendReactClass.js index 6eba084..f1f41a6 100644 --- a/src/extendReactClass.js +++ b/src/extendReactClass.js @@ -22,11 +22,11 @@ export default (Component: Object, defaultStyles: Object, options: Object) => { if (this.props.styles || hasDefaultstyles) { const props = Object.assign({}, this.props); - if (this.props.styles) { - styles = this.props.styles; + if (props.styles) { + styles = props.styles; } else if (hasDefaultstyles) { styles = defaultStyles; - delete this.props.styles; + delete props.styles; } Object.defineProperty(props, 'styles', { From 5d1bf06726645798b13fba6642e15b134bf42cff Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Mon, 9 Jul 2018 11:43:40 +0100 Subject: [PATCH 24/36] fix: use latest node to run tests --- .babelrc | 27 ++++++++++++++++----------- .travis.yml | 5 ++--- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/.babelrc b/.babelrc index 197c614..3751ba8 100644 --- a/.babelrc +++ b/.babelrc @@ -1,14 +1,19 @@ { - "presets": [ - "es2015", - "stage-0", - "react" + "plugins": [ + "add-module-exports", + "lodash", + "transform-class-properties", + [ + "transform-es2015-classes", + { + "loose": true + } ], - "plugins": [ - "add-module-exports", - "lodash", - "transform-class-properties", - ["transform-es2015-classes", { "loose": true }], - "transform-proto-to-assign" - ] + "transform-proto-to-assign" + ], + "presets": [ + "es2015", + "stage-0", + "react" + ] } diff --git a/.travis.yml b/.travis.yml index bfa13bf..a786167 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,7 @@ language: node_js node_js: - - 7 - - 6 - - 5 + - node + - 8 before_install: - npm config set depth 0 notifications: From c305e5b33fb4e52392b90646e77f763c3cd1edc1 Mon Sep 17 00:00:00 2001 From: Sam Kauffman Date: Tue, 31 Jul 2018 19:15:25 -0400 Subject: [PATCH 25/36] fix: update dependencies for React 16.3+ support (#289) bump hoist-non-react-statics to >= 2.5.0 to prevent incorrectly hoisted getDerivedStateFromProps --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7e8a698..00e1ffa 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ }, "license": "BSD-3-Clause", "dependencies": { - "hoist-non-react-statics": "^1.2.0", + "hoist-non-react-statics": "^2.5.5", "lodash": "^4.16.6", "object-unfreeze": "^1.1.0" }, From 54b4367b4e2addc8f41cd7b5fc538850895fa340 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Thu, 9 Aug 2018 11:07:10 +0100 Subject: [PATCH 26/36] docs: add DEPRECATION NOTICE --- README.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d16c474..28c29da 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,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! -> ⚠️⚠️⚠️ -> -> Note: +> ## ⚠️⚠️⚠️ DEPRECATION NOTICE ⚠️⚠️⚠️ > > If you are considering to use `react-css-modules`, evaluate if [`babel-plugin-react-css-modules`](https://github.com/gajus/babel-plugin-react-css-modules) covers your use case. > `babel-plugin-react-css-modules` is a lightweight alternative of `react-css-modules`. @@ -18,8 +16,7 @@ React CSS Modules implement automatic mapping of CSS modules. Every CSS class is > `babel-plugin-react-css-modules` is not a drop-in replacement and does not cover all the use cases of `react-css-modules`. > However, it has a lot smaller performance overhead (0-10% vs +50%; see [Performance](https://github.com/gajus/babel-plugin-react-css-modules#performance)) and a lot smaller size footprint (less than 2kb vs +17kb). > -> It is easy to get started! -> See the demo https://github.com/gajus/babel-plugin-react-css-modules/tree/master/demo +> It is easy to get started! See the demo https://github.com/gajus/babel-plugin-react-css-modules/tree/master/demo - [CSS Modules](#css-modules) - [webpack `css-loader`](#webpack-css-loader) From 982fcf53fefab011a7c9ce07d3a68473f34e3eca Mon Sep 17 00:00:00 2001 From: Vitaliy Mazurenko Date: Fri, 10 Aug 2018 11:47:01 +0300 Subject: [PATCH 27/36] fix: #285 react-css-modules causes modifying key property of the component children (#286) --- src/linkClass.js | 2 +- tests/linkClass.js | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/linkClass.js b/src/linkClass.js index 4618446..73783b4 100644 --- a/src/linkClass.js +++ b/src/linkClass.js @@ -50,7 +50,7 @@ const linkElement = (element: ReactElement, styles: Object, configuration: Objec if (React.isValidElement(children)) { elementShallowCopy.props.children = linkElement(React.Children.only(children), styles, configuration); } else if (_.isArray(children) || isIterable(children)) { - elementShallowCopy.props.children = React.Children.map(children, (node) => { + elementShallowCopy.props.children = _.map(children, (node) => { if (React.isValidElement(node)) { // eslint-disable-next-line no-use-before-define return linkElement(React.Children.only(node), styles, configuration); diff --git a/tests/linkClass.js b/tests/linkClass.js index 6a13f21..90a66b2 100644 --- a/tests/linkClass.js +++ b/tests/linkClass.js @@ -141,6 +141,22 @@ describe('linkClass', () => { expect(subject.props.children[0].props).not.to.have.property('styleName'); expect(subject.props.children[1].props).not.to.have.property('styleName'); }); + it('preserves original keys', () => { + let subject; + + subject =
+

+

+

; + + subject = linkClass(subject, { + bar: 'bar-1', + foo: 'foo-1' + }); + + expect(subject.props.children[0].key).to.equal('1'); + expect(subject.props.children[1].key).to.equal('2'); + }); }); context('when multiple descendants have styleName and are iterable', () => { it('assigns a generated className', () => { From 7a87ee39cac01b100596a3dbefbb8f6624337f34 Mon Sep 17 00:00:00 2001 From: Vitaliy Mazurenko Date: Wed, 22 Aug 2018 14:19:57 +0300 Subject: [PATCH 28/36] fix: assign a generated className to elements inside nested arrays (#292) --- src/linkClass.js | 9 +-------- tests/linkClass.js | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/linkClass.js b/src/linkClass.js index 73783b4..d296742 100644 --- a/src/linkClass.js +++ b/src/linkClass.js @@ -50,14 +50,7 @@ const linkElement = (element: ReactElement, styles: Object, configuration: Objec if (React.isValidElement(children)) { elementShallowCopy.props.children = linkElement(React.Children.only(children), styles, configuration); } else if (_.isArray(children) || isIterable(children)) { - elementShallowCopy.props.children = _.map(children, (node) => { - if (React.isValidElement(node)) { - // eslint-disable-next-line no-use-before-define - return linkElement(React.Children.only(node), styles, configuration); - } else { - return node; - } - }); + elementShallowCopy.props.children = linkArray(objectUnfreeze(children), styles, configuration); } _.forEach(restProps, (propValue, propName) => { diff --git a/tests/linkClass.js b/tests/linkClass.js index 90a66b2..cf7c289 100644 --- a/tests/linkClass.js +++ b/tests/linkClass.js @@ -125,6 +125,33 @@ describe('linkClass', () => { expect(subject.props.children[0].props.className).to.equal('foo-1'); expect(subject.props.children[1].props.className).to.equal('bar-1'); }); + it('assigns a generated className to elements inside nested arrays', () => { + let subject; + + subject =
+ {[ + [ +

, +

+ ], + [ +

, +

+ ] + ]} +

; + + subject = linkClass(subject, { + bar: 'bar-1', + foo: 'foo-1' + }); + + expect(subject.props.children[0][0].props.className).to.equal('foo-1'); + expect(subject.props.children[0][1].props.className).to.equal('bar-1'); + + expect(subject.props.children[1][0].props.className).to.equal('foo-1'); + expect(subject.props.children[1][1].props.className).to.equal('bar-1'); + }); it('styleName is deleted from props', () => { let subject; From 5e787d41192c96d5650a3036443cff2bb977cd39 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Fri, 30 Nov 2018 14:36:09 +0000 Subject: [PATCH 29/36] fix: implement a fix for React warning --- src/extendReactClass.js | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/extendReactClass.js b/src/extendReactClass.js index f1f41a6..23f251b 100644 --- a/src/extendReactClass.js +++ b/src/extendReactClass.js @@ -18,7 +18,9 @@ export default (Component: Object, defaultStyles: Object, options: Object) => { let styles; const hasDefaultstyles = _.isObject(defaultStyles); - + + let renderResult; + if (this.props.styles || hasDefaultstyles) { const props = Object.assign({}, this.props); @@ -35,14 +37,31 @@ export default (Component: Object, defaultStyles: Object, options: Object) => { value: styles, writable: false }); + + const originalProps = this.props; + + let renderIsSuccessful = false; - this.props = props; + try { + this.props = props; + + renderResult = super.render(); + + renderIsSuccessful = true; + } finally { + this.props = originalProps; + } + + // @see https://github.com/facebook/react/issues/14224 + if (!renderIsSuccessful) { + renderResult = super.render(); + } } else { styles = {}; + + renderResult = super.render(); } - const renderResult = super.render(); - if (renderResult) { return linkClass(renderResult, styles, options); } From 0d96a9f943c1a74db6fefa4dd74162ca202dba37 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Fri, 30 Nov 2018 14:39:48 +0000 Subject: [PATCH 30/36] style: remove trailing spaces --- src/extendReactClass.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/extendReactClass.js b/src/extendReactClass.js index 23f251b..4741bea 100644 --- a/src/extendReactClass.js +++ b/src/extendReactClass.js @@ -18,9 +18,9 @@ export default (Component: Object, defaultStyles: Object, options: Object) => { let styles; const hasDefaultstyles = _.isObject(defaultStyles); - + let renderResult; - + if (this.props.styles || hasDefaultstyles) { const props = Object.assign({}, this.props); @@ -37,28 +37,28 @@ export default (Component: Object, defaultStyles: Object, options: Object) => { value: styles, writable: false }); - + const originalProps = this.props; - + let renderIsSuccessful = false; try { this.props = props; - + renderResult = super.render(); - + renderIsSuccessful = true; } finally { this.props = originalProps; } - + // @see https://github.com/facebook/react/issues/14224 if (!renderIsSuccessful) { renderResult = super.render(); } } else { styles = {}; - + renderResult = super.render(); } From f65a5512a71399247e97a6ed8575dca18555bcfb Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Fri, 30 Nov 2018 14:40:47 +0000 Subject: [PATCH 31/36] chore: add .editorconfig --- .editorconfig | 9 +++++++++ .gitignore | 7 ++++--- 2 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0f17867 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.gitignore b/.gitignore index d54786c..df978de 100755 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,10 @@ coverage dist *.log .* -!.README +!.babelrc +!.editorconfig +!.eslintrc !.gitignore !.npmignore -!.babelrc +!.README !.travis.yml -!.eslintrc From 4d6a71ebc3b1ce4f19b1f13951f2ddc5e0e0e56e Mon Sep 17 00:00:00 2001 From: Vitaliy Date: Thu, 3 Jan 2019 19:41:07 +0200 Subject: [PATCH 32/36] fix: unfreeze nested children array before passing it to linkArray (#293) --- src/linkClass.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/linkClass.js b/src/linkClass.js index d296742..4ed3975 100644 --- a/src/linkClass.js +++ b/src/linkClass.js @@ -13,7 +13,9 @@ const linkArray = (array: Array, styles: Object, configuration: Object) => { // eslint-disable-next-line no-use-before-define array[index] = linkElement(React.Children.only(value), styles, configuration); } else if (_.isArray(value)) { - array[index] = linkArray(value, styles, configuration); + const unfreezedValue = Object.isFrozen(value) ? objectUnfreeze(value) : value; + + array[index] = linkArray(unfreezedValue, styles, configuration); } }); From 4f143d358a7fdca48b5e651cf55e942c2857a8e3 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Thu, 9 May 2019 17:52:52 +0100 Subject: [PATCH 33/36] docs: add GitSpo mentions badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 28c29da..484f9e7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # React CSS Modules +![[GitSpo Mentions](https://gitspo.com/mentions/gajus/react-css-modules)](https://gitspo.com/badges/gajus/react-css-modules?style=flat-square) [![Travis build status](http://img.shields.io/travis/gajus/react-css-modules/master.svg?style=flat-square)](https://travis-ci.org/gajus/react-css-modules) [![NPM version](http://img.shields.io/npm/v/react-css-modules.svg?style=flat-square)](https://www.npmjs.org/package/react-css-modules) [![js-canonical-style](https://img.shields.io/badge/code%20style-canonical-blue.svg?style=flat-square)](https://github.com/gajus/canonical) From 3d29360dc3b1d400efa4523c77620f591f969546 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Thu, 9 May 2019 17:59:01 +0100 Subject: [PATCH 34/36] fix: correct GitSpo badge markdown --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 484f9e7..4d7f5af 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # React CSS Modules -![[GitSpo Mentions](https://gitspo.com/mentions/gajus/react-css-modules)](https://gitspo.com/badges/gajus/react-css-modules?style=flat-square) +[![GitSpo Mentions](https://gitspo.com/badges/gajus/react-css-modules?style=flat-square)](https://gitspo.com/mentions/gajus/react-css-modules) [![Travis build status](http://img.shields.io/travis/gajus/react-css-modules/master.svg?style=flat-square)](https://travis-ci.org/gajus/react-css-modules) [![NPM version](http://img.shields.io/npm/v/react-css-modules.svg?style=flat-square)](https://www.npmjs.org/package/react-css-modules) [![js-canonical-style](https://img.shields.io/badge/code%20style-canonical-blue.svg?style=flat-square)](https://github.com/gajus/canonical) From b1a8dcfcea558c3f963ae300a28bf13d23295fbd Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Sat, 11 May 2019 09:25:53 +0100 Subject: [PATCH 35/36] fix: update GitSpo badge URL --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4d7f5af..a6b7399 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # React CSS Modules -[![GitSpo Mentions](https://gitspo.com/badges/gajus/react-css-modules?style=flat-square)](https://gitspo.com/mentions/gajus/react-css-modules) +[![GitSpo Mentions](https://gitspo.com/badges/mentions/gajus/react-css-modules?style=flat-square)](https://gitspo.com/mentions/gajus/react-css-modules) [![Travis build status](http://img.shields.io/travis/gajus/react-css-modules/master.svg?style=flat-square)](https://travis-ci.org/gajus/react-css-modules) [![NPM version](http://img.shields.io/npm/v/react-css-modules.svg?style=flat-square)](https://www.npmjs.org/package/react-css-modules) [![js-canonical-style](https://img.shields.io/badge/code%20style-canonical-blue.svg?style=flat-square)](https://github.com/gajus/canonical) From 629e80e25069e1b7597da76534395c21a9d499d4 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Wed, 19 Jun 2019 10:12:55 +0200 Subject: [PATCH 36/36] Create FUNDING.yml --- .github/FUNDING.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..2f093a7 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: gajus +patreon: gajus