diff --git a/.changeset/grumpy-peaches-flow.md b/.changeset/grumpy-peaches-flow.md new file mode 100644 index 000000000..346e948d9 --- /dev/null +++ b/.changeset/grumpy-peaches-flow.md @@ -0,0 +1,5 @@ +--- +'style-dictionary': patch +--- + +Fix small issue in type w3c delegate utility type tracking. diff --git a/.changeset/ninety-geckos-build.md b/.changeset/ninety-geckos-build.md new file mode 100644 index 000000000..16a3f4768 --- /dev/null +++ b/.changeset/ninety-geckos-build.md @@ -0,0 +1,5 @@ +--- +'style-dictionary': patch +--- + +Expose typeW3CDelegate utility. Don't take "value" into account anymore to determine that it's a design token, use $value. diff --git a/__integration__/__snapshots__/w3c-forward-compat.test.snap.js b/__integration__/__snapshots__/w3c-forward-compat.test.snap.js index 5bc862cdb..38ff8d2f2 100644 --- a/__integration__/__snapshots__/w3c-forward-compat.test.snap.js +++ b/__integration__/__snapshots__/w3c-forward-compat.test.snap.js @@ -10,6 +10,7 @@ snapshots["should match snapshot"] = :root { --colors-black-500: rgb(0, 0, 0); --colors-black-dimension: 5px; /* Some description */ + --colors-foo: rgb(0, 0, 0); } `; /* end snapshot should match snapshot */ diff --git a/__integration__/w3c-forward-compat.test.js b/__integration__/w3c-forward-compat.test.js index b47add1f4..4d1b02238 100644 --- a/__integration__/w3c-forward-compat.test.js +++ b/__integration__/w3c-forward-compat.test.js @@ -48,6 +48,9 @@ describe('integration', () => { $description: 'Some description', }, }, + foo: { + $value: '{colors.black.500}', + }, }, }, transform: { diff --git a/__tests__/utils/preprocess.test.js b/__tests__/utils/preprocess.test.js new file mode 100644 index 000000000..622616c7c --- /dev/null +++ b/__tests__/utils/preprocess.test.js @@ -0,0 +1,96 @@ +/* + * 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. + */ +import { expect } from 'chai'; +import { typeW3CDelegate } from '../../lib/utils/preprocess.js'; + +describe('utils', () => { + describe('typeW3CDelegate', () => { + it('should correctly let tokens inherit the $type property while respecting local overrides', () => { + const tokens = { + dimension: { + $type: 'dimension', + scale: { + $value: '2', + $type: 'math', + }, + xs: { + $value: '4', + }, + nested: { + deep: { + deeper: { + $value: '12', + }, + }, + deep2: { + $type: 'math', + deeper: { + $type: 'other', + evenDeeper: { + $value: '12', + $type: 'math', + }, + evenDeeper2: { + $value: '12', + }, + }, + }, + }, + sm: { + $value: '8', + }, + }, + }; + + expect(typeW3CDelegate(tokens)).to.eql({ + dimension: { + $type: 'dimension', + scale: { + $value: '2', + $type: 'math', + }, + xs: { + $value: '4', + $type: 'dimension', + }, + nested: { + deep: { + deeper: { + $value: '12', + $type: 'dimension', + }, + }, + deep2: { + $type: 'math', + deeper: { + $type: 'other', + evenDeeper: { + $value: '12', + $type: 'math', + }, + evenDeeper2: { + $value: '12', + $type: 'other', + }, + }, + }, + }, + sm: { + $value: '8', + $type: 'dimension', + }, + }, + }); + }); + }); +}); diff --git a/docs/using_reference_utils.md b/docs/using_reference_utils.md index 0b03e00b0..fd2d74f44 100644 --- a/docs/using_reference_utils.md +++ b/docs/using_reference_utils.md @@ -264,3 +264,66 @@ export const ZIndexAboveFold = 1; export const SemanticBgPrimary = ColorsBlack; export const Border = `solid ${Spacing2} ${SemanticBgPrimary}`; ``` + +### typeW3CDelegate + +This function processes your ["W3C Design Token Community Group Draft spec"-compliant](https://design-tokens.github.io/community-group/format/) dictionary of tokens, and ensures that `$type` inheritance is applied. + +We built this utility because it's cheaper to apply the inheritance once, rather than on every access of a token's "$type" property, checking the ancestor tree to find it. + +Input: + +```js +{ + dimensions: { + $type: 'dimension', + sm: { + $value: '5', + }, + md: { + $value: '10', + }, + nested: { + deep: { + lg: { + $value: '15', + }, + }, + }, + nope: { + $value: '20', + $type: 'spacing', + }, + }, +} +``` + +Output: + +```js +{ + dimensions: { + $type: 'dimension', + sm: { + $value: '5', + $type: 'dimension', + }, + md: { + $value: '10', + $type: 'dimension', + }, + nested: { + deep: { + lg: { + $value: '15', + $type: 'dimension', + }, + }, + }, + nope: { + $value: '20', + $type: 'spacing', + }, + }, +} +``` diff --git a/lib/utils/index.js b/lib/utils/index.js index 1339db360..70607e143 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -15,6 +15,7 @@ import usesReferences from './references/usesReferences.js'; import { getReferences } from './references/getReferences.js'; import { resolveReferences } from './references/resolveReferences.js'; import flattenTokens from './flattenTokens.js'; +import { typeW3CDelegate } from './preprocess.js'; // Public style-dictionary/utils API -export { usesReferences, getReferences, resolveReferences, flattenTokens }; +export { usesReferences, getReferences, resolveReferences, flattenTokens, typeW3CDelegate }; diff --git a/lib/utils/preprocess.js b/lib/utils/preprocess.js index 869a04053..c56129b7f 100644 --- a/lib/utils/preprocess.js +++ b/lib/utils/preprocess.js @@ -23,7 +23,7 @@ import isPlainObject from 'is-plain-obj'; * @param {DesignTokens} tokens * @returns */ -function typeW3CDelegate(tokens) { +export function typeW3CDelegate(tokens) { const clone = structuredClone(tokens); /** @@ -31,15 +31,15 @@ function typeW3CDelegate(tokens) { * @param {string} [_type] */ const recurse = (slice, _type) => { - let type = _type; // keep track of type through the stack Object.values(slice).forEach((prop) => { + let type = _type; // keep track of type through the stack if (isPlainObject(prop)) { if (typeof prop.$type === 'string') { type = prop.$type; } // prop is a design token, but no $type prop currently, // so we add it if we know what the type is from our ancestor tree - if ((prop.$value || prop.value) && !prop.$type && type) { + if (prop.$value && !prop.$type && type) { prop.$type = type; } recurse(prop, type);