Skip to content

Commit

Permalink
feat: add mathFractionDigits option resolveMath transform, default fr…
Browse files Browse the repository at this point in the history
…om 3 to 4 (#290)
  • Loading branch information
Razinsky authored Jul 4, 2024
1 parent b454787 commit ed10715
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/two-jokes-give.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tokens-studio/sd-transforms': minor
---

Allow changing the resolve math transform amount of decimals to round for using platform options `mathFractionDigits`, change default value from 3 to 4.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,26 @@ This transform checks and evaluates math expressions

**matches**: All tokens that have string values.

You can adjust to how many decimals the result should be rounded using `PlatformConfig.mathFractionDigits`:

```json
{
"source": ["tokens.json"],
"platforms": {
"css": {
"mathFractionDigits": 3,
"transformGroup": "tokens-studio",
"files": [
{
"format": "css/variables",
"destination": "output.css"
}
]
}
}
}
```

#### before

```json
Expand Down
14 changes: 9 additions & 5 deletions src/checkAndEvaluateMath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { DesignToken } from 'style-dictionary/types';
import { Parser } from 'expr-eval-fork';
import { parse, reduceExpression } from '@bundled-es-modules/postcss-calc-ast-parser';

const defaultFractionDigits = 4;
const mathChars = ['+', '-', '*', '/'];

const parser = new Parser();
Expand Down Expand Up @@ -74,7 +75,7 @@ function splitMultiIntoSingleValues(expr: string): string[] {
return [expr];
}

function parseAndReduce(expr: string): string | number {
function parseAndReduce(expr: string, fractionDigits = defaultFractionDigits): string | number {
let result: string | number = expr;

let evaluated;
Expand Down Expand Up @@ -127,12 +128,15 @@ function parseAndReduce(expr: string): string | number {
}

// the outer Number() gets rid of insignificant trailing zeros of decimal numbers
const reducedTo3Fixed = Number(Number.parseFloat(`${result}`).toFixed(3));
result = resultUnit ? `${reducedTo3Fixed}${resultUnit}` : reducedTo3Fixed;
const reducedToFixed = Number(Number.parseFloat(`${result}`).toFixed(fractionDigits));
result = resultUnit ? `${reducedToFixed}${resultUnit}` : reducedToFixed;
return result;
}

export function checkAndEvaluateMath(token: DesignToken): DesignToken['value'] {
export function checkAndEvaluateMath(
token: DesignToken,
fractionDigits?: number,
): DesignToken['value'] {
const expr = token.$value ?? token.value;
const type = token.$type ?? token.type;

Expand All @@ -145,7 +149,7 @@ export function checkAndEvaluateMath(token: DesignToken): DesignToken['value'] {
return expr;
}
const exprs = splitMultiIntoSingleValues(expr);
const reducedExprs = exprs.map(_expr => parseAndReduce(_expr));
const reducedExprs = exprs.map(_expr => parseAndReduce(_expr, fractionDigits));
if (reducedExprs.length === 1) {
return reducedExprs[0];
}
Expand Down
2 changes: 1 addition & 1 deletion src/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export async function register(sd: typeof StyleDictionary, transformOpts?: Trans
type: 'value',
transitive: true,
filter: token => ['string', 'object'].includes(typeof (token.$value ?? token.value)),
transform: token => checkAndEvaluateMath(token),
transform: (token, config) => checkAndEvaluateMath(token, config.options?.mathFractionDigits),
});

sd.registerTransform({
Expand Down
36 changes: 35 additions & 1 deletion test/spec/checkAndEvaluateMath.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { expect } from 'chai';
import { checkAndEvaluateMath } from '../../src/checkAndEvaluateMath.js';
import { runTransformSuite } from '../suites/transform-suite.spec.js';
import { cleanup, init } from '../integration/utils.js';
import { TransformedToken } from 'style-dictionary/types';

runTransformSuite(checkAndEvaluateMath as (value: unknown) => unknown, {});

Expand All @@ -15,7 +17,7 @@ describe('check and evaluate math', () => {
expect(checkAndEvaluateMath({ value: '4 * 7rem', type: 'dimension' })).to.equal('28rem');
expect(
checkAndEvaluateMath({ value: '(15 + 20 - 17 * 8 / 3) * 7px', type: 'dimension' }),
).to.equal('-72.333px');
).to.equal('-72.3333px');
});

it('supports expression of type number', () => {
Expand Down Expand Up @@ -110,6 +112,38 @@ describe('check and evaluate math', () => {
);
});

it('allows a `mathFractionDigits` option to control the rounding of values in math', async () => {
const dict = await init({
tokens: {
foo: {
value: '5',
type: 'dimension',
},
bar: {
value: '{foo} / 16',
type: 'dimension',
},
},
platforms: {
css: {
transformGroup: 'tokens-studio',
options: {
mathFractionDigits: 3,
},
files: [
{
format: 'css/variables',
destination: 'foo.css',
},
],
},
},
});
const enrichedTokens = await dict?.exportPlatform('css'); // platform to parse for is 'css' in this case
cleanup(dict);
expect((enrichedTokens?.bar as TransformedToken).value).to.eql('0.313px');
});

it('supports boolean values', () => {
expect(checkAndEvaluateMath({ value: false, type: 'boolean' })).to.equal(false);
expect(checkAndEvaluateMath({ value: true, type: 'boolean' })).to.equal(true);
Expand Down

0 comments on commit ed10715

Please sign in to comment.