Skip to content

Commit bb23402

Browse files
p4blochgajus
p4bloch
authored andcommitted
feat: implement handleNotFoundStyleName (#249)
* implement handleNotFoundStyleName * disable linter only for console * docs: reword
1 parent 76d8270 commit bb23402

File tree

7 files changed

+65
-32
lines changed

7 files changed

+65
-32
lines changed

README.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ React CSS Modules implement automatic mapping of CSS modules. Every CSS class is
3737
- [Decorator](#decorator)
3838
- [Options](#options)
3939
- [`allowMultiple`](#allowmultiple)
40-
- [`errorWhenNotFound`](#errorwhennotfound)
40+
- [`handleNotFoundStyleName`](#handlenotfoundstylename)
4141
- [SASS, SCSS, LESS and other CSS Preprocessors](#sass-scss-less-and-other-css-preprocessors)
4242
- [Enable Sourcemaps](#enable-sourcemaps)
4343
- [Class Composition](#class-composition)
@@ -127,7 +127,7 @@ Using `react-css-modules`:
127127
<div className='global-css' styleName='local-module'></div>
128128
```
129129

130-
* You are warned when `styleName` refers to an undefined CSS Module ([`errorWhenNotFound`](#errorwhennotfound) option).
130+
* You are warned when `styleName` refers to an undefined CSS Module ([`handleNotFoundStyleName`](#handlenotfoundstylename) option).
131131
* You can enforce use of a single CSS module per `ReactElement` ([`allowMultiple`](#allowmultiple) option).
132132

133133
## The Implementation
@@ -408,7 +408,7 @@ export default CSSModules(CustomList, styles);
408408
* @typedef CSSModules~Options
409409
* @see {@link https://github.com/gajus/react-css-modules#options}
410410
* @property {Boolean} allowMultiple
411-
* @property {Boolean} errorWhenNotFound
411+
* @property {String} handleNotFoundStyleName
412412
*/
413413
414414
/**
@@ -492,11 +492,17 @@ When `false`, the following will cause an error:
492492
<div styleName='foo bar' />
493493
```
494494

495-
#### `errorWhenNotFound`
495+
#### `handleNotFoundStyleName`
496496

497-
Default: `true`.
497+
Default: `throw`.
498498

499-
Throws an error when `styleName` cannot be mapped to an existing CSS Module.
499+
Defines the desired action when `styleName` cannot be mapped to an existing CSS Module.
500+
501+
Available options:
502+
503+
* `throw` throws an error
504+
* `log` logs a warning using `console.warn`
505+
* `ignore` silently ignores the missing style name
500506

501507
## SASS, SCSS, LESS and other CSS Preprocessors
502508

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"babel-preset-stage-0": "^6.16.0",
3535
"babel-register": "^6.18.0",
3636
"chai": "^4.0.0-canary.1",
37+
"chai-spies": "^0.7.1",
3738
"eslint": "^3.10.0",
3839
"eslint-config-canonical": "^5.5.0",
3940
"husky": "^0.11.9",

src/generateAppendClassName.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const CustomMap = typeof Map === 'undefined' ? SimpleMap : Map;
44

55
const stylesIndex = new CustomMap();
66

7-
export default (styles, styleNames: Array<string>, errorWhenNotFound: boolean): string => {
7+
export default (styles, styleNames: Array<string>, handleNotFoundStyleName: "throw" | "log" | "ignore"): string => {
88
let appendClassName;
99
let stylesIndexMap;
1010

@@ -29,8 +29,14 @@ export default (styles, styleNames: Array<string>, errorWhenNotFound: boolean):
2929

3030
if (className) {
3131
appendClassName += ' ' + className;
32-
} else if (errorWhenNotFound === true) {
33-
throw new Error('"' + styleNames[styleName] + '" CSS module is undefined.');
32+
} else {
33+
if (handleNotFoundStyleName === 'throw') {
34+
throw new Error('"' + styleNames[styleName] + '" CSS module is undefined.');
35+
}
36+
if (handleNotFoundStyleName === 'log') {
37+
// eslint-disable-next-line no-console
38+
console.warn('"' + styleNames[styleName] + '" CSS module is undefined.');
39+
}
3440
}
3541
}
3642
}

src/linkClass.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ const linkElement = (element: ReactElement, styles: Object, configuration: Objec
6868
});
6969

7070
if (styleNames.length) {
71-
appendClassName = generateAppendClassName(styles, styleNames, configuration.errorWhenNotFound);
71+
appendClassName = generateAppendClassName(styles, styleNames, configuration.handleNotFoundStyleName);
7272

7373
if (appendClassName) {
7474
if (elementShallowCopy.props.className) {

src/makeConfiguration.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import _ from 'lodash';
44
* @typedef CSSModules~Options
55
* @see {@link https://github.com/gajus/react-css-modules#options}
66
* @property {boolean} allowMultiple
7-
* @property {boolean} errorWhenNotFound
7+
* @property {string} handleNotFoundStyleName
88
*/
99

1010
/**
@@ -14,16 +14,20 @@ import _ from 'lodash';
1414
export default (userConfiguration = {}) => {
1515
const configuration = {
1616
allowMultiple: false,
17-
errorWhenNotFound: true
17+
handleNotFoundStyleName: 'throw'
1818
};
1919

2020
_.forEach(userConfiguration, (value, name) => {
2121
if (_.isUndefined(configuration[name])) {
2222
throw new Error('Unknown configuration property "' + name + '".');
2323
}
2424

25-
if (!_.isBoolean(value)) {
26-
throw new Error('"' + name + '" property value must be a boolean.');
25+
if (name === 'allowMultiple' && !_.isBoolean(value)) {
26+
throw new Error('"allowMultiple" property value must be a boolean.');
27+
}
28+
29+
if (name === 'handleNotFoundStyleName' && !['throw', 'log', 'ignore'].includes(value)) {
30+
throw new Error('"handleNotFoundStyleName" property value must be "throw", "log" or "ignore".');
2731
}
2832

2933
configuration[name] = value;

tests/linkClass.js

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
/* eslint-disable max-nested-callbacks, react/prefer-stateless-function, class-methods-use-this */
1+
/* eslint-disable max-nested-callbacks, react/prefer-stateless-function, class-methods-use-this, no-console */
22

3-
import {
3+
import chai, {
44
expect
55
} from 'chai';
6+
import spies from 'chai-spies';
67
import React from 'react';
78
import TestUtils from 'react-addons-test-utils';
89
import jsdom from 'jsdom';
910
import linkClass from './../src/linkClass';
1011

12+
chai.use(spies);
13+
1114
describe('linkClass', () => {
1215
context('ReactElement does not define styleName', () => {
1316
it('does not affect element properties', () => {
@@ -264,24 +267,37 @@ describe('linkClass', () => {
264267
});
265268
});
266269

267-
describe('options.errorWhenNotFound', () => {
270+
describe('options.handleNotFoundStyleName', () => {
268271
context('when styleName does not match an existing CSS module', () => {
269-
context('when false', () => {
270-
it('ignores the missing CSS module', () => {
271-
let subject;
272-
273-
subject = <div styleName='foo' />;
274-
275-
subject = linkClass(subject, {}, {errorWhenNotFound: false});
272+
context('when throw', () => {
273+
it('throws an error', () => {
274+
expect(() => {
275+
linkClass(<div styleName='foo' />, {}, {handleNotFoundStyleName: 'throw'});
276+
}).to.throw(Error, '"foo" CSS module is undefined.');
277+
});
278+
});
279+
context('when log', () => {
280+
it('logs a warning to the console', () => {
281+
const warnSpy = chai.spy(() => {});
276282

277-
expect(subject.props.className).to.be.an('undefined');
283+
console.warn = warnSpy;
284+
linkClass(<div styleName='foo' />, {}, {handleNotFoundStyleName: 'log'});
285+
expect(warnSpy).to.have.been.called();
278286
});
279287
});
280-
context('when is true', () => {
281-
it('throws an error', () => {
288+
context('when ignore', () => {
289+
it('does not log a warning', () => {
290+
const warnSpy = chai.spy(() => {});
291+
292+
console.warn = warnSpy;
293+
linkClass(<div styleName='foo' />, {}, {handleNotFoundStyleName: 'ignore'});
294+
expect(warnSpy).to.not.have.been.called();
295+
});
296+
297+
it('does not throw an error', () => {
282298
expect(() => {
283-
linkClass(<div styleName='foo' />, {}, {errorWhenNotFound: true});
284-
}).to.throw(Error, '"foo" CSS module is undefined.');
299+
linkClass(<div styleName='foo' />, {}, {handleNotFoundStyleName: 'ignore'});
300+
}).to.not.throw(Error, '"foo" CSS module is undefined.');
285301
});
286302
});
287303
});

tests/makeConfiguration.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ describe('makeConfiguration', () => {
1717
expect(configuration.allowMultiple).to.equal(false);
1818
});
1919
});
20-
describe('errorWhenNotFound property', () => {
21-
it('defaults to true', () => {
22-
expect(configuration.errorWhenNotFound).to.equal(true);
20+
describe('handleNotFoundStyleName property', () => {
21+
it('defaults to "throw"', () => {
22+
expect(configuration.handleNotFoundStyleName).to.equal('throw');
2323
});
2424
});
2525
});

0 commit comments

Comments
 (0)