Skip to content

Commit e301860

Browse files
authored
Merge pull request #83 from kristerkari/feature/allow-css-length-units-not-supported-by-react-native
Allow unsupported length units to be used
2 parents e6ac1a6 + ce64163 commit e301860

File tree

5 files changed

+166
-16
lines changed

5 files changed

+166
-16
lines changed

src/__tests__/units.js

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import transformCss from '..'
2+
3+
// List of units from:
4+
// https://developer.mozilla.org/en-US/docs/Web/CSS/length
5+
const lengthUnits = [
6+
'ch',
7+
'em',
8+
'ex',
9+
'rem',
10+
'vh',
11+
'vw',
12+
'vmin',
13+
'vmax',
14+
'cm',
15+
'mm',
16+
'in',
17+
'pc',
18+
'pt',
19+
]
20+
21+
lengthUnits.forEach(unit => {
22+
const value = `2${unit}`
23+
24+
it('allows CSS length units in transformed values', () => {
25+
expect(transformCss([['margin', value]])).toEqual({
26+
marginTop: value,
27+
marginRight: value,
28+
marginBottom: value,
29+
marginLeft: value,
30+
})
31+
expect(transformCss([['padding', value]])).toEqual({
32+
paddingTop: value,
33+
paddingRight: value,
34+
paddingBottom: value,
35+
paddingLeft: value,
36+
})
37+
})
38+
39+
it('allows CSS length units with 0 and unit', () => {
40+
expect(transformCss([['padding', `0${unit}`]])).toEqual({
41+
paddingTop: `0${unit}`,
42+
paddingRight: `0${unit}`,
43+
paddingBottom: `0${unit}`,
44+
paddingLeft: `0${unit}`,
45+
})
46+
})
47+
48+
it('allows mixed units in transformed values', () => {
49+
expect(transformCss([['margin', `10px ${value}`]])).toEqual({
50+
marginTop: 10,
51+
marginRight: value,
52+
marginBottom: 10,
53+
marginLeft: value,
54+
})
55+
})
56+
57+
it('allows units to be used with border shorthand property', () => {
58+
expect(transformCss([['border', `#f00 ${value} dashed`]])).toEqual({
59+
borderWidth: value,
60+
borderColor: '#f00',
61+
borderStyle: 'dashed',
62+
})
63+
64+
expect(transformCss([['border', value]])).toEqual({
65+
borderWidth: value,
66+
borderColor: 'black',
67+
borderStyle: 'solid',
68+
})
69+
})
70+
71+
it('allows units to be used with border-width', () => {
72+
expect(transformCss([['border-width', `1px 2px ${value} 4px`]])).toEqual({
73+
borderTopWidth: 1,
74+
borderRightWidth: 2,
75+
borderBottomWidth: value,
76+
borderLeftWidth: 4,
77+
})
78+
})
79+
80+
it('allows units to be used with border-radius', () => {
81+
expect(transformCss([['border-radius', `1px ${value} 3px 4px`]])).toEqual({
82+
borderTopLeftRadius: 1,
83+
borderTopRightRadius: value,
84+
borderBottomRightRadius: 3,
85+
borderBottomLeftRadius: 4,
86+
})
87+
})
88+
89+
it('allows units to be used with font-size', () => {
90+
expect(transformCss([['font-size', value]])).toEqual({
91+
fontSize: value,
92+
})
93+
})
94+
95+
it('allows units to be used with font shorthand property', () => {
96+
expect(
97+
transformCss([['font', `bold italic ${value}/${value} "Helvetica"`]])
98+
).toEqual({
99+
fontFamily: 'Helvetica',
100+
fontSize: value,
101+
fontWeight: 'bold',
102+
fontStyle: 'italic',
103+
fontVariant: [],
104+
lineHeight: value,
105+
})
106+
})
107+
108+
it('allows untis to be used with text-shadow ', () => {
109+
expect(transformCss([['text-shadow', `10px ${value} red`]])).toEqual({
110+
textShadowOffset: { width: 10, height: value },
111+
textShadowRadius: 0,
112+
textShadowColor: 'red',
113+
})
114+
})
115+
116+
it('allows untis to be used with box-shadow', () => {
117+
expect(
118+
transformCss([['box-shadow', `10px ${value} ${value} red`]])
119+
).toEqual({
120+
shadowOffset: { width: 10, height: value },
121+
shadowRadius: value,
122+
shadowColor: 'red',
123+
shadowOpacity: 1,
124+
})
125+
})
126+
})
127+
128+
it('throws for unit that is not supported', () => {
129+
expect(() => transformCss([['margin', '10ic']])).toThrow(
130+
'Failed to parse declaration "margin: 10ic"'
131+
)
132+
})

src/tokenTypes.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const identRe = /(^-?[_a-z][_a-z0-9-]*$)/i
3232
const numberRe = /^([+-]?(?:\d*\.)?\d+(?:[Ee][+-]?\d+)?)$/
3333
// Note lengthRe is sneaky: you can omit units for 0
3434
const lengthRe = /^(0$|(?:[+-]?(?:\d*\.)?\d+(?:[Ee][+-]?\d+)?)(?=px$))/
35+
const unsupportedUnitRe = /^([+-]?(?:\d*\.)?\d+(?:[Ee][+-]?\d+)?(ch|em|ex|rem|vh|vw|vmin|vmax|cm|mm|in|pc|pt))$/
3536
const angleRe = /^([+-]?(?:\d*\.)?\d+(?:[Ee][+-]?\d+)?(?:deg|rad))$/
3637
const percentRe = /^([+-]?(?:\d*\.)?\d+(?:[Ee][+-]?\d+)?%)$/
3738

@@ -60,6 +61,7 @@ export const tokens = {
6061
AUTO: regExpToken(autoRe),
6162
NUMBER: regExpToken(numberRe, Number),
6263
LENGTH: regExpToken(lengthRe, Number),
64+
UNSUPPORTED_LENGTH_UNIT: regExpToken(unsupportedUnitRe),
6365
ANGLE: regExpToken(angleRe),
6466
PERCENT: regExpToken(percentRe),
6567
IDENT: regExpToken(identRe),

src/transforms/font.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import parseFontFamily from './fontFamily'
22
import { regExpToken, tokens } from '../tokenTypes'
33

4-
const { SPACE, LENGTH, NUMBER, SLASH } = tokens
4+
const { SPACE, LENGTH, UNSUPPORTED_LENGTH_UNIT, NUMBER, SLASH } = tokens
55
const NORMAL = regExpToken(/^(normal)$/)
66
const STYLE = regExpToken(/^(italic)$/)
77
const WEIGHT = regExpToken(/^([1-9]00|bold)$/)
@@ -37,13 +37,13 @@ export default tokenStream => {
3737
numStyleWeightVariantMatched += 1
3838
}
3939

40-
const fontSize = tokenStream.expect(LENGTH)
40+
const fontSize = tokenStream.expect(LENGTH, UNSUPPORTED_LENGTH_UNIT)
4141

4242
if (tokenStream.matches(SLASH)) {
4343
if (tokenStream.matches(NUMBER)) {
4444
lineHeight = fontSize * tokenStream.lastValue
4545
} else {
46-
lineHeight = tokenStream.expect(LENGTH)
46+
lineHeight = tokenStream.expect(LENGTH, UNSUPPORTED_LENGTH_UNIT)
4747
}
4848
}
4949

src/transforms/index.js

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,30 @@ import textDecorationLine from './textDecorationLine'
99
import transform from './transform'
1010
import { directionFactory, anyOrderFactory, shadowOffsetFactory } from './util'
1111

12-
const { IDENT, WORD, COLOR, LENGTH, PERCENT, AUTO } = tokens
12+
const {
13+
IDENT,
14+
WORD,
15+
COLOR,
16+
LENGTH,
17+
UNSUPPORTED_LENGTH_UNIT,
18+
PERCENT,
19+
AUTO,
20+
} = tokens
1321

1422
const background = tokenStream => ({
1523
$merge: { backgroundColor: tokenStream.expect(COLOR) },
1624
})
1725
const border = anyOrderFactory({
1826
borderWidth: {
19-
token: tokens.LENGTH,
27+
tokens: [LENGTH, UNSUPPORTED_LENGTH_UNIT],
2028
default: 1,
2129
},
2230
borderColor: {
23-
token: COLOR,
31+
tokens: [COLOR],
2432
default: 'black',
2533
},
2634
borderStyle: {
27-
token: regExpToken(/^(solid|dashed|dotted)$/),
35+
tokens: [regExpToken(/^(solid|dashed|dotted)$/)],
2836
default: 'solid',
2937
},
3038
})
@@ -40,17 +48,17 @@ const borderRadius = directionFactory({
4048
})
4149
const borderWidth = directionFactory({ prefix: 'border', suffix: 'Width' })
4250
const margin = directionFactory({
43-
types: [LENGTH, PERCENT, AUTO],
51+
types: [LENGTH, UNSUPPORTED_LENGTH_UNIT, PERCENT, AUTO],
4452
prefix: 'margin',
4553
})
4654
const padding = directionFactory({ prefix: 'padding' })
4755
const flexFlow = anyOrderFactory({
4856
flexWrap: {
49-
token: regExpToken(/(nowrap|wrap|wrap-reverse)/),
57+
tokens: [regExpToken(/(nowrap|wrap|wrap-reverse)/)],
5058
default: 'nowrap',
5159
},
5260
flexDirection: {
53-
token: regExpToken(/(row|row-reverse|column|column-reverse)/),
61+
tokens: [regExpToken(/(row|row-reverse|column|column-reverse)/)],
5462
default: 'row',
5563
},
5664
})

src/transforms/util.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { tokens } from '../tokenTypes'
22

3-
const { LENGTH, PERCENT, COLOR, SPACE, NONE } = tokens
3+
const { LENGTH, UNSUPPORTED_LENGTH_UNIT, PERCENT, COLOR, SPACE, NONE } = tokens
44

55
export const directionFactory = ({
6-
types = [LENGTH, PERCENT],
6+
types = [LENGTH, UNSUPPORTED_LENGTH_UNIT, PERCENT],
77
directions = ['Top', 'Right', 'Bottom', 'Left'],
88
prefix = '',
99
suffix = '',
@@ -48,7 +48,9 @@ export const anyOrderFactory = (properties, delim = SPACE) => tokenStream => {
4848
const matchedPropertyName = propertyNames.find(
4949
propertyName =>
5050
values[propertyName] === undefined &&
51-
tokenStream.matches(properties[propertyName].token)
51+
properties[propertyName].tokens.some(token =>
52+
tokenStream.matches(token)
53+
)
5254
)
5355

5456
if (!matchedPropertyName) {
@@ -96,13 +98,19 @@ export const parseShadow = tokenStream => {
9698
while (tokenStream.hasTokens()) {
9799
if (didParseFirst) tokenStream.expect(SPACE)
98100

99-
if (offsetX === undefined && tokenStream.matches(LENGTH)) {
101+
if (
102+
offsetX === undefined &&
103+
tokenStream.matches(LENGTH, UNSUPPORTED_LENGTH_UNIT)
104+
) {
100105
offsetX = tokenStream.lastValue
101106
tokenStream.expect(SPACE)
102-
offsetY = tokenStream.expect(LENGTH)
107+
offsetY = tokenStream.expect(LENGTH, UNSUPPORTED_LENGTH_UNIT)
103108

104109
tokenStream.saveRewindPoint()
105-
if (tokenStream.matches(SPACE) && tokenStream.matches(LENGTH)) {
110+
if (
111+
tokenStream.matches(SPACE) &&
112+
tokenStream.matches(LENGTH, UNSUPPORTED_LENGTH_UNIT)
113+
) {
106114
radius = tokenStream.lastValue
107115
} else {
108116
tokenStream.rewind()

0 commit comments

Comments
 (0)