Skip to content

Commit

Permalink
feat: computed calc()
Browse files Browse the repository at this point in the history
- fixes jsdom/jsdom#1332
- replace getParts by splitTokens (not extracted into a single commit for better understanding)
- rename percent to percentage for matching with spec
- rename measurement to lengthOrPercentage for consistency with angleOrPercentage (later commit)
  • Loading branch information
cdoublev committed Jun 11, 2021
1 parent 5f743d8 commit 497db7c
Show file tree
Hide file tree
Showing 15 changed files with 419 additions and 136 deletions.
414 changes: 323 additions & 91 deletions lib/parsers.js

Large diffs are not rendered by default.

60 changes: 54 additions & 6 deletions lib/parsers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ describe('parseLength', () => {
});
it.todo('more tests');
});
describe('parsePercent', () => {
describe('parsePercentage', () => {
it('works with calc', () => {
expect(parsers.parsePercent('calc(1% + 1%)')).toBe('calc(2%)');
expect(parsers.parsePercentage('calc(1% + 1%)')).toBe('calc(2%)');
});
it.todo('more tests');
});
describe('parseMeasurement', () => {
describe('parseLengthOrPercentage', () => {
it.todo('test');
});
describe('parseAngle', () => {
Expand Down Expand Up @@ -73,9 +73,7 @@ describe('parseCalc', () => {
expect(parsers.parseCalc('calc(1px + 1pc)', resolveLengthOrPercentage)).toBe('calc(17px)');
expect(parsers.parseCalc('calc(1turn + 180deg)', resolveAngle)).toBe('calc(180deg)');
expect(parsers.parseCalc('calc(1s + 1000ms)', resolveTime)).toBe('calc(2000ms)');
expect(parsers.parseCalc('CALc(1px + 1em)', resolveLengthOrPercentage)).toBe(
'calc(1px + 1em)'
);
expect(parsers.parseCalc('CALc(1px + 1em)', resolveLengthOrPercentage)).toBe('calc(1px + 1em)');
});
});
describe('parseKeyword', () => {
Expand Down Expand Up @@ -263,3 +261,53 @@ describe('createShorthandGetter', () => {
describe('idlAttributeToCSSProperty', () => {
it.todo('test');
});
describe('splitTokens', () => {
it('should split value components separated by space', () => {
expect(parsers.splitTokens('1px solid red')).toEqual([['1px', 'solid', 'red'], [' ', ' ']]);
});
it('should split value components separated by space and forward slash', () => {
expect(parsers.splitTokens('url(bg.jpq) center / 50%', /[ /]/)).toEqual([
['url(bg.jpq)', 'center', '50%'],
[' ', ' / '],
]);
});
it('should split a list of values', () => {
expect(parsers.splitTokens('1px 1px black, 2px 2px silver', /,/)).toEqual([
['1px 1px black', '2px 2px silver'],
[', '],
]);
});
it('should split color function arguments', () => {
const separators = /[,/ ]/;
expect(parsers.splitTokens('0,0,0', separators)).toEqual([['0', '0', '0'], [',', ',']]);
expect(parsers.splitTokens('0,1%,2%', separators)).toEqual([['0', '1%', '2%'], [',', ',']]);
expect(parsers.splitTokens('0 , 0 , 0', separators)).toEqual([
['0', '0', '0'],
[' , ', ' , '],
]);
expect(parsers.splitTokens('0deg, 1%, 2%', separators)).toEqual([
['0deg', '1%', '2%'],
[', ', ', '],
]);
expect(parsers.splitTokens('0deg 1% / 2%', separators)).toEqual([
['0deg', '1%', '2%'],
[' ', ' / '],
]);
});
it('should split nested function arguments', () => {
expect(parsers.splitTokens('calc(45deg * 2) to left, rgb(255, 0, 0), cyan', /,/)).toEqual([
['calc(45deg * 2) to left', 'rgb(255, 0, 0)', 'cyan'],
[', ', ', '],
]);
expect(parsers.splitTokens('calc(1 + 1) + var(--number, calc(1 + 1)) * 1', /[+*]/)).toEqual([
['calc(1 + 1)', 'var(--number, calc(1 + 1))', '1'],
[' + ', ' * '],
]);
});
it('should split function with empty arguments', () => {
expect(parsers.splitTokens(', , ,, 1%,', /,/)).toEqual([
['', '', '', '', '1%', ''],
[', ', ', ', ',', ', ', ','],
]);
});
});
9 changes: 6 additions & 3 deletions lib/properties/backgroundPosition.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

const { parseKeyword, parseMeasurement } = require('../parsers');
const { parseKeyword, parseLengthOrPercentage } = require('../parsers');

var valid_keywords = ['top', 'center', 'bottom', 'left', 'right'];

Expand All @@ -10,13 +10,16 @@ var parse = function parse(v) {
return null;
}
if (parts.length === 1) {
const value = parseMeasurement(parts[0]) || parseKeyword(parts[0], valid_keywords);
const value = parseLengthOrPercentage(parts[0]) || parseKeyword(parts[0], valid_keywords);
if (value) {
return `${value} center`;
}
return null;
}
if (parseMeasurement(parts[0]) !== null && parseMeasurement(parts[1]) !== null) {
if (
parseLengthOrPercentage(parts[0]) !== null &&
parseLengthOrPercentage(parts[1]) !== null
) {
return v;
}
if (
Expand Down
8 changes: 4 additions & 4 deletions lib/properties/bottom.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
'use strict';

var parseMeasurement = require('../parsers').parseMeasurement;
const { parseLengthOrPercentage } = require('../parsers');

module.exports.definition = {
set: function(v) {
this._setProperty('bottom', parseMeasurement(v));
set(v) {
this._setProperty('bottom', parseLengthOrPercentage(v));
},
get: function() {
get() {
return this.getPropertyValue('bottom');
},
enumerable: true,
Expand Down
4 changes: 2 additions & 2 deletions lib/properties/clip.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

const { parseKeyword, parseMeasurement } = require('../parsers');
const { parseKeyword, parseLengthOrPercentage } = require('../parsers');

var shape_regex = /^rect\((.*)\)$/i;

Expand All @@ -18,7 +18,7 @@ function parse(val) {
return null;
}
var valid = parts.every(function(part, index) {
const measurement = parseMeasurement(part.toLowerCase());
const measurement = parseLengthOrPercentage(part);
parts[index] = measurement;
return measurement !== null;
});
Expand Down
4 changes: 2 additions & 2 deletions lib/properties/flexBasis.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
'use strict';

const { parseKeyword, parseMeasurement } = require('../parsers');
const { parseKeyword, parseLengthOrPercentage } = require('../parsers');

function parse(v) {
return parseKeyword(v, ['auto']) || parseMeasurement(v);
return parseKeyword(v, ['auto']) || parseLengthOrPercentage(v);
}

module.exports.definition = {
Expand Down
4 changes: 2 additions & 2 deletions lib/properties/fontSize.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

const { parseKeyword, parseMeasurement } = require('../parsers');
const { parseKeyword, parseLengthOrPercentage } = require('../parsers');

const sizes = [
// Absolute
Expand All @@ -17,7 +17,7 @@ const sizes = [
];

function parse(v) {
return parseKeyword(v, sizes) || parseMeasurement(v);
return parseKeyword(v, sizes) || parseLengthOrPercentage(v);
}

module.exports.definition = {
Expand Down
4 changes: 2 additions & 2 deletions lib/properties/height.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'use strict';

const { parseKeyword, parseMeasurement } = require('../parsers');
const { parseKeyword, parseLengthOrPercentage } = require('../parsers');

module.exports.definition = {
set(v) {
this._setProperty('height', parseKeyword(v, ['auto']) || parseMeasurement(v));
this._setProperty('height', parseKeyword(v, ['auto']) || parseLengthOrPercentage(v));
},
get() {
return this.getPropertyValue('height');
Expand Down
8 changes: 4 additions & 4 deletions lib/properties/left.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
'use strict';

var parseMeasurement = require('../parsers').parseMeasurement;
const { parseLengthOrPercentage } = require('../parsers');

module.exports.definition = {
set: function(v) {
this._setProperty('left', parseMeasurement(v));
set(v) {
this._setProperty('left', parseLengthOrPercentage(v));
},
get: function() {
get() {
return this.getPropertyValue('left');
},
enumerable: true,
Expand Down
4 changes: 2 additions & 2 deletions lib/properties/lineHeight.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
'use strict';

const { parseKeyword, parseMeasurement, parseNumber } = require('../parsers');
const { parseKeyword, parseLengthOrPercentage, parseNumber } = require('../parsers');

function parse(v) {
return parseKeyword(v, ['normal']) || parseNumber(v) || parseMeasurement(v);
return parseKeyword(v, ['normal']) || parseNumber(v) || parseLengthOrPercentage(v);
}

module.exports.definition = {
Expand Down
4 changes: 2 additions & 2 deletions lib/properties/margin.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ const {
createShorthandSetter,
parseImplicitShorthand,
parseKeyword,
parseMeasurement,
parseLengthOrPercentage,
serializeImplicitShorthand,
} = require('../parsers');

function parse(v) {
return parseKeyword(v, ['auto']) || parseMeasurement(v);
return parseKeyword(v, ['auto']) || parseLengthOrPercentage(v);
}

const longhands = {
Expand Down
12 changes: 6 additions & 6 deletions lib/properties/padding.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ const {
createShorthandGetter,
createShorthandSetter,
parseImplicitShorthand,
parseMeasurement,
parseLengthOrPercentage,
serializeImplicitShorthand,
} = require('../parsers');

const longhands = {
'padding-top': { parse: parseMeasurement },
'padding-right': { parse: parseMeasurement },
'padding-bottom': { parse: parseMeasurement },
'padding-left': { parse: parseMeasurement },
'padding-top': { parse: parseLengthOrPercentage },
'padding-right': { parse: parseLengthOrPercentage },
'padding-bottom': { parse: parseLengthOrPercentage },
'padding-left': { parse: parseLengthOrPercentage },
};

module.exports.definition = {
Expand All @@ -22,5 +22,5 @@ module.exports.definition = {
configurable: true,
};
module.exports.longhands = Object.keys(longhands);
module.exports.parse = parseMeasurement;
module.exports.parse = parseLengthOrPercentage;
module.exports.serialize = serializeImplicitShorthand;
8 changes: 4 additions & 4 deletions lib/properties/right.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
'use strict';

var parseMeasurement = require('../parsers').parseMeasurement;
const { parseLengthOrPercentage } = require('../parsers');

module.exports.definition = {
set: function(v) {
this._setProperty('right', parseMeasurement(v));
set(v) {
this._setProperty('right', parseLengthOrPercentage(v));
},
get: function() {
get() {
return this.getPropertyValue('right');
},
enumerable: true,
Expand Down
8 changes: 4 additions & 4 deletions lib/properties/top.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
'use strict';

var parseMeasurement = require('../parsers').parseMeasurement;
const { parseLengthOrPercentage } = require('../parsers');

module.exports.definition = {
set: function(v) {
this._setProperty('top', parseMeasurement(v));
set(v) {
this._setProperty('top', parseLengthOrPercentage(v));
},
get: function() {
get() {
return this.getPropertyValue('top');
},
enumerable: true,
Expand Down
4 changes: 2 additions & 2 deletions lib/properties/width.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'use strict';

const { parseKeyword, parseMeasurement } = require('../parsers');
const { parseKeyword, parseLengthOrPercentage } = require('../parsers');

module.exports.definition = {
set(v) {
this._setProperty('width', parseKeyword(v, ['auto']) || parseMeasurement(v));
this._setProperty('width', parseKeyword(v, ['auto']) || parseLengthOrPercentage(v));
},
get() {
return this.getPropertyValue('width');
Expand Down

0 comments on commit 497db7c

Please sign in to comment.