Skip to content

Commit 40fb023

Browse files
authored
Merge pull request #47 from styled-components/box-shadow-shorthand
Implement support for box-shadow shorthand
2 parents c3b2972 + 8df00cd commit 40fb023

File tree

5 files changed

+98
-2
lines changed

5 files changed

+98
-2
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ margin: 5px 7px 2px;
6363

6464
Shorthands will only accept values that are supported in React, so `background` will only accept a colour, `backgroundColor`
6565

66+
There is also support for the `box-shadow` shorthand, and this converts into `shadow-` properties. Note that these only work on iOS.
67+
6668
#### Shorthand Notes
6769

6870
`border{Top,Right,Bottom,Left}` shorthands are not supported, because `borderStyle` cannot be applied to individual border sides.

src/TokenStream.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ module.exports = class TokenStream {
5858
return this.throw();
5959
}
6060

61-
matchFunction() {
61+
matchesFunction() {
6262
const node = this.node;
6363
if (node.type !== 'function') return null;
6464
const value = new TokenStream(node.nodes, node);
@@ -69,7 +69,7 @@ module.exports = class TokenStream {
6969
}
7070

7171
expectFunction() {
72-
const value = this.matchFunction();
72+
const value = this.matchesFunction();
7373
if (value !== null) return value;
7474
return this.throw();
7575
}

src/index.test.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,48 @@ it('does not transform invalid flex', () => {
442442
expect(() => transformCss([['flex', '1 2px 3']])).toThrow();
443443
});
444444

445+
it('transforms box-shadow into shadow- properties', () => runTest([
446+
['box-shadow', '10px 20px 30px red'],
447+
], {
448+
shadowOffset: { width: 10, height: 20 },
449+
shadowRadius: 30,
450+
shadowColor: 'red',
451+
}));
452+
453+
it('transforms box-shadow without blur-radius', () => runTest([
454+
['box-shadow', '10px 20px red'],
455+
], {
456+
shadowOffset: { width: 10, height: 20 },
457+
shadowRadius: 0,
458+
shadowColor: 'red',
459+
}));
460+
461+
it('transforms box-shadow without color', () => runTest([
462+
['box-shadow', '10px 20px red'],
463+
], {
464+
shadowOffset: { width: 10, height: 20 },
465+
shadowRadius: 0,
466+
shadowColor: 'red',
467+
}));
468+
469+
it('transforms box-shadow without blur-radius, color', () => runTest([
470+
['box-shadow', '10px 20px'],
471+
], {
472+
shadowOffset: { width: 10, height: 20 },
473+
shadowRadius: 0,
474+
shadowColor: 'black',
475+
}));
476+
477+
it('transforms box-shadow enforces offset to be present', () => {
478+
expect(() => transformCss([['box-shadow', 'red']]))
479+
.toThrow('Failed to parse declaration "boxShadow: red"');
480+
});
481+
482+
it('transforms box-shadow and enforces offset-y if offset-x present', () => {
483+
expect(() => transformCss([['box-shadow', '10px']]))
484+
.toThrow('Failed to parse declaration "boxShadow: 10px"');
485+
});
486+
445487
it('allows blacklisting shorthands', () => {
446488
const actualStyles = transformCss([['border-radius', '50']], ['borderRadius']);
447489
expect(actualStyles).toEqual({ borderRadius: 50 });

src/transforms/boxShadow.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
const { tokens } = require('../tokenTypes');
2+
3+
const { NONE, SPACE, WORD, LENGTH } = tokens;
4+
5+
module.exports = (tokenStream) => {
6+
let offsetX;
7+
let offsetY;
8+
let blurRadius;
9+
let color;
10+
11+
if (tokenStream.matches(NONE)) {
12+
tokenStream.expectEmpty();
13+
return {
14+
$merge: { shadowOffset: { width: 0, height: 0 }, shadowRadius: 0, shadowColor: 'black' },
15+
};
16+
}
17+
18+
let didParseFirst = false;
19+
while (tokenStream.hasTokens()) {
20+
if (didParseFirst) tokenStream.expect(SPACE);
21+
22+
if (offsetX === undefined && tokenStream.matches(LENGTH)) {
23+
offsetX = tokenStream.lastValue;
24+
tokenStream.expect(SPACE);
25+
offsetY = tokenStream.expect(LENGTH);
26+
27+
if (tokenStream.lookAhead().matches(LENGTH)) {
28+
tokenStream.expect(SPACE);
29+
blurRadius = tokenStream.expect(LENGTH);
30+
}
31+
} else if (color === undefined && (
32+
tokenStream.matchesFunction() || tokenStream.matches(WORD)
33+
)) {
34+
color = String(tokenStream.lastValue);
35+
} else {
36+
tokenStream.throw();
37+
}
38+
39+
didParseFirst = true;
40+
}
41+
42+
if (offsetX === undefined) tokenStream.throw();
43+
44+
const $merge = {
45+
shadowOffset: { width: offsetX, height: offsetY },
46+
shadowRadius: blurRadius !== undefined ? blurRadius : 0,
47+
shadowColor: color !== undefined ? color : 'black',
48+
};
49+
return { $merge };
50+
};

src/transforms/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const { regExpToken, tokens } = require('../tokenTypes');
2+
const boxShadow = require('./boxShadow');
23
const flex = require('./flex');
34
const font = require('./font');
45
const fontFamily = require('./fontFamily');
@@ -56,6 +57,7 @@ module.exports = {
5657
borderColor,
5758
borderRadius,
5859
borderWidth,
60+
boxShadow,
5961
flex,
6062
flexFlow,
6163
font,

0 commit comments

Comments
 (0)