Skip to content

Commit

Permalink
fix(references): value object references now work (#623)
Browse files Browse the repository at this point in the history
Fixes #615

Token values that are objects as opposed to simple strings were not working with `outputReferences` and throwing an error. These changes fix some behavior and make sure certain scenarios don't fail. This makes explicit what `outputReferences` can and can't do:
* The `value` must be a string at time of formatting. This should be the case anyways, if not you will get `[object Object]` in output files. With `outputReferences` this was failing because an object does not have the `.replace` function. 
* The `value` must have the referenced values in it for `outputReferences` to replace the resolved values with the reference's name. For example if you define a color using an HSL object and transform it to a hex value, we cannot undo that transform to understand which parts of the value to use the reference's name. 
* References to non-token's will not work with `outputReferences` because the references will not have a `name` attribute. Just like the previous point, it will build fine, but just won't output the reference and instead output the transformed and resolved value. 

Changes:
* In `createPropertyFormatter`, rather than looking at the *original* value and replacing references with the referenced token's name, we are now looking at the *transformed* value and replacing any referenced *values* with the reference's name. This allows us to still use `outputReferences` with a value object. 
* Updated `getReferences` to mirror `usesReference` with respect to object values. `usesReference` checks the object values, whereas `getReferences` did not.
  • Loading branch information
dbanksdesign authored May 12, 2021
1 parent f6a9fc0 commit 23de306
Show file tree
Hide file tree
Showing 6 changed files with 435 additions and 22 deletions.
93 changes: 93 additions & 0 deletions __integration__/__snapshots__/objectValues.test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`integration object values css/variables border should match snapshot 1`] = `
"/**
* Do not edit directly
* Generated on Sat, 01 Jan 2000 00:00:00 GMT
*/
:root {
--border-primary: 0.125rem solid #ff0000;
}
"
`;

exports[`integration object values css/variables border with references should match snapshot 1`] = `
"/**
* Do not edit directly
* Generated on Sat, 01 Jan 2000 00:00:00 GMT
*/
:root {
--border-primary: var(--size-border) solid var(--color-red);
}
"
`;

exports[`integration object values css/variables hex syntax should match snapshot 1`] = `
"/**
* Do not edit directly
* Generated on Sat, 01 Jan 2000 00:00:00 GMT
*/
:root {
--color-red: #ff0000;
--color-green: #40bf40;
}
"
`;

exports[`integration object values css/variables hex syntax with references should match snapshot 1`] = `
"/**
* Do not edit directly
* Generated on Sat, 01 Jan 2000 00:00:00 GMT
*/
:root {
--color-red: #ff0000;
--color-green: #40bf40;
}
"
`;

exports[`integration object values css/variables hsl syntax should match snapshot 1`] = `
"/**
* Do not edit directly
* Generated on Sat, 01 Jan 2000 00:00:00 GMT
*/
:root {
--color-red: #ff0000;
--color-green: hsl(120, 50%, 50%);
}
"
`;

exports[`integration object values css/variables hsl syntax with references should match snapshot 1`] = `
"/**
* Do not edit directly
* Generated on Sat, 01 Jan 2000 00:00:00 GMT
*/
:root {
--color-red: #ff0000;
--color-green: hsl(120, 50%, 50%);
}
"
`;

exports[`integration object values scss/variables should match snapshot 1`] = `
"
// Do not edit directly
// Generated on Sat, 01 Jan 2000 00:00:00 GMT
$border-primary: 0.125rem solid #ff0000;"
`;

exports[`integration object values scss/variables with references should match snapshot 1`] = `
"
// Do not edit directly
// Generated on Sat, 01 Jan 2000 00:00:00 GMT
$border-primary: $size-border solid $color-red;"
`;
211 changes: 211 additions & 0 deletions __integration__/objectValues.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
/*
* Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with
* the License. A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
* and limitations under the License.
*/

const fs = require('fs-extra');
const Color = require('tinycolor2');
const StyleDictionary = require('../index');
const {buildPath} = require('./_constants');

const options = {
outputReferences: true
}

describe('integration', () => {
describe('object values', () => {
StyleDictionary.extend({
properties: {
hue: `120`,
saturation: `50%`,
lightness: `50%`,
color: {
red: { value: "#f00" },
green: {
value: {
h: "{hue}",
s: "{saturation}",
l: "{lightness}"
}
}
},
size: {
border: { value: 0.125 }
},
border: {
primary: {
// getReferences should work on objects like this:
value: {
color: "{color.red.value}",
width: "{size.border.value}",
style: "solid"
}
},
}
},
transform: {
hsl: {
type: 'value',
transitive: true,
matcher: (token) => token.original.value.h,
transformer: (token) => {
return `hsl(${token.value.h}, ${token.value.s}, ${token.value.l})`
}
},
hslToHex: {
type: 'value',
transitive: true,
matcher: (token) => token.original.value.h,
transformer: (token) => {
return Color(`hsl(${token.value.h}, ${token.value.s}, ${token.value.l})`).toHexString();
}
},
cssBorder: {
type: 'value',
transitive: true,
matcher: (token) => token.path[0] === `border`,
transformer: (token) => {
return `${token.value.width} ${token.value.style} ${token.value.color}`
}
}
},
platforms: {
// This will test to see if a value object for an hsl color works
// with and without `outputReferences`
cssHsl: {
buildPath,
transforms: StyleDictionary.transformGroup.css.concat([`hsl`]),
files: [{
destination: `hsl.css`,
format: `css/variables`,
filter: (token) => token.attributes.category === `color`
},{
destination: `hslWithReferences.css`,
format: `css/variables`,
filter: (token) => token.attributes.category === `color`,
options
}]
},

// This will test to see if a value object for an hsl that has been
// transformed to a hex color works with and without `outputReferences`
cssHex: {
buildPath,
transforms: StyleDictionary.transformGroup.css.concat([`cssBorder`,`hslToHex`]),
files: [{
destination: 'hex.css',
format: 'css/variables',
filter: (token) => token.attributes.category === `color`,
},{
destination: 'hexWithReferences.css',
format: 'css/variables',
filter: (token) => token.attributes.category === `color`,
options
}]
},

// This will test to see if a value object for a border
// works with and without `outputReferences`
cssBorder: {
buildPath,
transforms: StyleDictionary.transformGroup.css.concat([`cssBorder`]),
files: [{
destination: 'border.css',
format: 'css/variables',
filter: (token) => token.attributes.category === `border`,
},{
destination: 'borderWithReferences.css',
format: 'css/variables',
filter: (token) => token.attributes.category === `border`,
options
}]
},

scss: {
buildPath,
transforms: StyleDictionary.transformGroup.css.concat([`cssBorder`,`hslToHex`]),
files: [{
destination: 'border.scss',
format: 'scss/variables',
filter: (token) => token.attributes.category === `border`,
},{
destination: 'borderWithReferences.scss',
format: 'scss/variables',
filter: (token) => token.attributes.category === `border`,
options
}]
},
}
}).buildAllPlatforms();

describe('css/variables', () => {
describe(`hsl syntax`, () => {
const output = fs.readFileSync(`${buildPath}hsl.css`, {encoding:'UTF-8'});
it(`should match snapshot`, () => {
expect(output).toMatchSnapshot();
});

describe(`with references`, () => {
const output = fs.readFileSync(`${buildPath}hslWithReferences.css`, {encoding:'UTF-8'});
it(`should match snapshot`, () => {
expect(output).toMatchSnapshot();
});
});
});

describe(`hex syntax`, () => {
const output = fs.readFileSync(`${buildPath}hex.css`, {encoding:'UTF-8'});
it(`should match snapshot`, () => {
expect(output).toMatchSnapshot();
});

describe(`with references`, () => {
const output = fs.readFileSync(`${buildPath}hexWithReferences.css`, {encoding:'UTF-8'});
it(`should match snapshot`, () => {
expect(output).toMatchSnapshot();
});
});
});

describe(`border`, () => {
const output = fs.readFileSync(`${buildPath}border.css`, {encoding:'UTF-8'});
it(`should match snapshot`, () => {
expect(output).toMatchSnapshot();
});

describe(`with references`, () => {
const output = fs.readFileSync(`${buildPath}borderWithReferences.css`, {encoding:'UTF-8'});
it(`should match snapshot`, () => {
expect(output).toMatchSnapshot();
});
});
});
});

describe('scss/variables', () => {
const output = fs.readFileSync(`${buildPath}border.scss`, {encoding:'UTF-8'});
it(`should match snapshot`, () => {
expect(output).toMatchSnapshot();
});

describe(`with references`, () => {
const output = fs.readFileSync(`${buildPath}borderWithReferences.scss`, {encoding:'UTF-8'});
it(`should match snapshot`, () => {
expect(output).toMatchSnapshot();
});
});
});
});
});

afterAll(() => {
fs.emptyDirSync(buildPath);
});
78 changes: 78 additions & 0 deletions __tests__/utils/reference/getReferences.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with
* the License. A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
* and limitations under the License.
*/

// `.getReferences` is bound to a dictionary object, so to test it we will
// create a dictionary object and then call `.getReferences` on it.
const createDictionary = require('../../../lib/utils/createDictionary');

const properties = {
color: {
red: { value: "#f00" },
danger: { value: "{color.red.value}" }
},
size: {
border: { value: "2px" }
},
border: {
primary: {
// getReferences should work on objects like this:
value: {
color: "{color.red.value}",
width: "{size.border.value}",
style: "solid"
}
},
secondary: {
// getReferences should work on interpolated values like this:
value: "{size.border.value} solid {color.red.value}"
}
}
}

const dictionary = createDictionary({ properties });

describe('utils', () => {
describe('reference', () => {
describe('getReferences()', () => {
it(`should return an empty array if the value has no references`, () => {
expect(dictionary.getReferences(properties.color.red.value)).toEqual([]);
});

it(`should work with a single reference`, () => {
expect(dictionary.getReferences(properties.color.danger.value)).toEqual(
expect.arrayContaining([
{value: "#f00"}
])
);
});

it(`should work with object values`, () => {
expect(dictionary.getReferences(properties.border.primary.value)).toEqual(
expect.arrayContaining([
{value: "2px"},
{value: "#f00"}
])
);
});

it(`should work with interpolated values`, () => {
expect(dictionary.getReferences(properties.border.secondary.value)).toEqual(
expect.arrayContaining([
{value: "2px"},
{value: "#f00"}
])
);
});
});
});
});
Loading

0 comments on commit 23de306

Please sign in to comment.