Skip to content

Commit 7bffc1a

Browse files
authored
Merge pull request #1850 from chanzuckerberg/release-v13.11.0
## [13.11.0](v13.11.0-alpha.0...v13.11.0) (2024-02-20) [Storybook](https://61313967cde49b003ae2a860-xfzakymyle.chromatic.com/) ### Features * **script:** add import from figma script ([#1844](#1844)) ([9ed90e5](9ed90e5)) * **Select:** enable multi-select ([ced70d5](ced70d5)) * **Select:** add handling for long text in select field ([7c79a6e](7c79a6e)) ### Bug Fixes * **PopoverListItem:** adjust size of list item when selected ([0496f56](0496f56)) * **Select:** set trigger type to button to prevent submits ([#1843](#1843)) ([d7ea037](d7ea037))
2 parents 49c5b75 + 6ebce18 commit 7bffc1a

19 files changed

+3641
-2939
lines changed

.eslintrc

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
},
5454
"overrides": [
5555
{
56-
"files": ["*.test.ts", "*.test.tsx", "**/test/**"],
56+
"files": ["*.test.{js,ts}", "*.test.tsx", "**/test/**"],
5757
"plugins": ["jest"],
5858
"extends": ["plugin:jest/recommended"],
5959
"env": {

.github/workflows/test.yml

+24-5
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,29 @@ jobs:
102102
- name: Build design tokens 🛠️
103103
run: yarn build:tokens
104104

105-
- name: Lint Commit Messages 👕
106-
uses: wagoid/commitlint-github-action@v5
107-
env:
108-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
109-
110105
- name: Lint Javascript + SCSS 👕
111106
run: yarn run lint --max-warnings=0
107+
108+
commitlint:
109+
name: Lint Commit Messages 👕
110+
runs-on: ubuntu-latest
111+
steps:
112+
- uses: actions/checkout@v3
113+
with:
114+
fetch-depth: 0
115+
- name: Setup node 😻
116+
uses: actions/setup-node@v3
117+
with:
118+
node-version-file: '.node-version'
119+
cache: 'yarn'
120+
- name: Install Dependencies 🧶
121+
run: yarn install --immutable
122+
- name: Validate commits commitlint
123+
# This workflow can also be triggered via "workflow_call".
124+
# Since it's a push event we have access to these properties https://docs.github.com/en/webhooks/webhook-events-and-payloads#push
125+
if: github.event_name == 'push'
126+
run: |
127+
COMMIT_COUNT=$(echo '${{ toJSON(github.event.commits) }}' | jq length)
128+
echo "Number of commits in the push: $COMMIT_COUNT"
129+
130+
yarn commitlint --from HEAD~$COMMIT_COUNT --to HEAD --verbose

.storybook/components/Docs/Guidelines/Theming.stories.mdx

+32-1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,36 @@ Once run, you will have a set of theme files written to the configured `dest` pa
7070

7171
To use, add this file to your core app root file **after** where the imported EDS's `@chanzuckerberg/eds/index.css` file is inserted.
7272

73+
### eds-import-from-Figma
74+
75+
Command to run: `npx eds-import-from-figma`
76+
77+
Designers can define and tweak the values of a custom theme. For this reason, we want to automate the application of those changes to the project's existing theme file.
78+
79+
To use this command, there are a few prerequisites:
80+
81+
* In Figma, install the [Export/Import Variables Plugin][export-import-plugin].
82+
83+
With the prerequisites set up, you can download a JSON file, containing the existing token definitions and values. Here's how:
84+
85+
* Open the design(s) in Figma
86+
* Open the Resources Panel in the toolbar (or use <kbd>Shift + I</kbd>)
87+
* Activate the "Export/Import Variables" plugin by first clicking the "Plugins" tab, then the "Export/Import Variables" option.
88+
* Once it opens, click the "Export..." button for the Listed collections and save to your local machine
89+
* Finally, us the above command, by passing in the file path to each collection.
90+
91+
Example:
92+
93+
`$ npx eds-import-from-figma path/to/export-file.json`
94+
95+
When using the script, it will read in the available Figma Modes in the file. Select the value you want (e.g., New Theme), then hit <kbd>Enter</kbd>
96+
97+
The script will run and apply new token values to the code in the appropriate places.
98+
99+
**NOTE**: currently, we only handle color tokens, and will skip any tokens storing other types of token values (number, etc.).
100+
101+
[export-import-plugin]: https://www.figma.com/community/plugin/1256972111705530093
102+
73103
## Custom Theming and Tailwind
74104

75105
When you have your own custom theme, you can use the tokens provided in `app-tailwind-theme.config.json` to do advanced tailwind configuration. This file contains all the tokens in JSON format, mapped to the literal values in your local theme.
@@ -80,7 +110,7 @@ You can use similar import values to what is in `@chanzuckerberg/eds/tailwind.co
80110
// in your tailwind.config.ts file
81111
import type { Config } from 'tailwindcss';
82112
import baseConfig from '@chanzuckerberg/eds/tailwind.config';
83-
import {eds as customTokens} from "<edsrc.json:dest>/app-tailwind-theme.config"; // where <edsrc.json:test> is the path configured in .edsrc.json
113+
import {eds as customTokens} from "./<edsrc.json:dest>/app-tailwind-theme.config.json"; // where <edsrc.json:dest> is the path configured in .edsrc.json
84114

85115
const {
86116
background: backgroundColorTokens,
@@ -97,6 +127,7 @@ export default {
97127
...colorTokens,
98128
},
99129
extend: {
130+
...baseConfig.theme.extend,
100131
backgroundColor: {
101132
...backgroundColorTokens,
102133
},

.yarn/releases/yarn-4.0.2.cjs renamed to .yarn/releases/yarn-4.1.0.cjs

+336-336
Large diffs are not rendered by default.

.yarnrc.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ enableGlobalCache: false
44

55
nodeLinker: node-modules
66

7-
yarnPath: .yarn/releases/yarn-4.0.2.cjs
7+
yarnPath: .yarn/releases/yarn-4.1.0.cjs

CHANGELOG.md

+25
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,31 @@
22

33
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
44

5+
## [13.11.0](https://github.com/chanzuckerberg/edu-design-system/compare/v13.11.0-alpha.0...v13.11.0) (2024-02-20)
6+
7+
8+
### Features
9+
10+
* **Select:** add handling for long text in select field ([7c79a6e](https://github.com/chanzuckerberg/edu-design-system/commit/7c79a6e57c9fd0056cc5ec6223a4fd1439a7b210))
11+
12+
13+
### Bug Fixes
14+
15+
* **PopoverListItem:** adjust size of list item when selected ([0496f56](https://github.com/chanzuckerberg/edu-design-system/commit/0496f5608923ae674054d1dad40a232bb0def659))
16+
17+
## [13.11.0-alpha.0](https://github.com/chanzuckerberg/edu-design-system/compare/v13.10.0...v13.11.0-alpha.0) (2024-02-15)
18+
19+
20+
### Features
21+
22+
* **script:** add import from figma script ([#1844](https://github.com/chanzuckerberg/edu-design-system/issues/1844)) ([9ed90e5](https://github.com/chanzuckerberg/edu-design-system/commit/9ed90e5bf1a42ce55c7c2b0f3cad1add3c71057c))
23+
* **Select:** enable multi-select ([ced70d5](https://github.com/chanzuckerberg/edu-design-system/commit/ced70d59b653e5132e145e9361c0bc2b358d1b48))
24+
25+
26+
### Bug Fixes
27+
28+
* **Select:** set trigger type to button to prevent submits ([#1843](https://github.com/chanzuckerberg/edu-design-system/issues/1843)) ([d7ea037](https://github.com/chanzuckerberg/edu-design-system/commit/d7ea0370e698a09516eacb9de61af3a63e8c1432))
29+
530
## [13.10.0](https://github.com/chanzuckerberg/edu-design-system/compare/v13.9.0...v13.10.0) (2024-02-01)
631

732

bin/_util.js

+110
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,114 @@ module.exports = {
140140
}
141141
}
142142
},
143+
/**
144+
* Determine the write path by taking the collection and variable name, and looking it up in
145+
* the existing local theme. If there's a path in the local theme file, we can write there (using lodash/set
146+
* or similar).
147+
*
148+
* @param {object} localTheme JSON file loaded, representing the data for the local theme
149+
* @param {string} collectionName Name of the exported collection
150+
* @param {string} variableName current variable name from figma (e.g., color/text/neutral/default)
151+
* @returns {string|null} representation of the path to write to in the local theme JSON file
152+
*/
153+
getWritePath: function (localTheme, collectionName, variableName) {
154+
const at = require('lodash/at');
155+
156+
const workingPath =
157+
module.exports.getTokenPrefix(collectionName) +
158+
module.exports.tokenNameToPath(variableName);
159+
160+
const found = at(localTheme, workingPath).filter(
161+
(entries) => typeof entries !== 'undefined',
162+
);
163+
164+
if (found.length) {
165+
// handle case where we should look for @ in the file, then pluck the value object properly
166+
if (found[0]['@']?.value) {
167+
// update the write path to mark the @ and value
168+
return workingPath + '[email protected]';
169+
}
170+
171+
// handle case where it's just value
172+
if (found[0]?.value) {
173+
// update the write path to mark the value
174+
return workingPath + '.value';
175+
}
176+
}
177+
178+
// There is no write path based on what's in the local theme so we return null signal it's a missing token
179+
return null;
180+
},
181+
/**
182+
* Utilities for handling parsing of Figma Theme Data.
183+
*
184+
* These functions are set up to handle, transform, and read data coming from figma API Structure.
185+
* For more information on this API format, refer to:
186+
* - https://help.figma.com/hc/en-us/articles/15339657135383-Guide-to-variables-in-Figma
187+
* - https://www.figma.com/plugin-docs/api/VariableCollection/
188+
*/
189+
/**
190+
* Given the value sourced from figma, translate it into a
191+
* style-dictionary format. When encountering an unrecognized type,
192+
* throw an error.
193+
*
194+
* Data Types:
195+
* - Type COLOR: https://www.figma.com/plugin-docs/api/RGB/
196+
*
197+
* @param {string} type Figma type for the token value (Set:)
198+
* @param {object} figmaResolvedValue
199+
* @returns {string} value using the type
200+
* @throws {TypeError} using `details` on the data read from figma
201+
*/
202+
parseResolvedValue: function (type, figmaResolvedValue) {
203+
if (type === 'COLOR') {
204+
const r = Math.floor(figmaResolvedValue.r * 255);
205+
const g = Math.floor(figmaResolvedValue.g * 255);
206+
const b = Math.floor(figmaResolvedValue.b * 255);
207+
const a = figmaResolvedValue.a;
208+
if (figmaResolvedValue.a > 0 && figmaResolvedValue.a < 1) {
209+
return `rgba(${r}, ${g}, ${b}, ${a})`;
210+
} else {
211+
// print hex instead
212+
return (
213+
'#' +
214+
[r, g, b]
215+
.map((x) => x.toString(16))
216+
.map((x) => (x.length === 1 ? '0' + x : x))
217+
.join('')
218+
.toUpperCase()
219+
);
220+
}
221+
} else {
222+
throw new TypeError('unknown resolved type: ' + type, {
223+
details: figmaResolvedValue,
224+
});
225+
}
226+
},
227+
/**
228+
* Given the "type" of import file (named after the collection name), produce
229+
* a prefix to the token name that corresponds to the prefix used for those
230+
* tokens.
231+
*
232+
* @param {string} collectionName The key to write to
233+
* @returns {string|null} a text prefix for where to write the token value or null when no prefix is found
234+
*/
235+
getTokenPrefix: function (collectionName) {
236+
switch (collectionName) {
237+
case 'themes':
238+
return 'eds.theme.';
239+
case 'primitives':
240+
return 'eds.';
241+
default:
242+
return null;
243+
}
244+
},
245+
/**
246+
* Conversion of the figma token name (e.g., some/path/to/token) to the equivalent path in a JSON object
247+
* @param {string} figmaTokenName The name from the figma variables panel (slash separated)
248+
* @returns {string} a lodash-compatible string representing the path to the token value in JSON
249+
*/
250+
tokenNameToPath: function (figmaTokenName) {
251+
return figmaTokenName.replaceAll('/', '.').toLowerCase();
252+
},
143253
};

bin/_util.test.js

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
const utils = require('./_util');
2+
3+
describe('utils', function () {
4+
describe('getWritePath', function () {
5+
const exampleTheme = {
6+
eds: {
7+
theme: {
8+
color: {
9+
body: {
10+
background: {
11+
neutral: {
12+
default: {
13+
'@': {
14+
value: '#FFFFFF',
15+
},
16+
hover: {
17+
value: '#F4F6F8',
18+
},
19+
},
20+
},
21+
},
22+
},
23+
},
24+
},
25+
},
26+
};
27+
28+
it('generates a write path for a root-at value', () => {
29+
expect(
30+
utils.getWritePath(
31+
exampleTheme,
32+
'themes',
33+
'color/body/background/neutral/default',
34+
),
35+
).toEqual('[email protected]');
36+
});
37+
38+
it('generates a write path for a non-root-at value', () => {
39+
expect(
40+
utils.getWritePath(
41+
exampleTheme,
42+
'themes',
43+
'color/body/background/neutral/default/hover',
44+
),
45+
).toEqual('eds.theme.color.body.background.neutral.default.hover.value');
46+
});
47+
48+
it('generates a null when the path is not in the local theme', () => {
49+
expect(
50+
utils.getWritePath(
51+
exampleTheme,
52+
'themes',
53+
'color/body/background/neutral/default/focus',
54+
),
55+
).toEqual(null);
56+
});
57+
});
58+
59+
describe('getTokenPath', function () {
60+
it.each([
61+
['themes', 'eds.theme.'],
62+
['primitives', 'eds.'],
63+
['some-unknown', null],
64+
['', null],
65+
])(
66+
'parses the collection %s to token prefix "%s"',
67+
(collection, expected) => {
68+
expect(utils.getTokenPrefix(collection)).toEqual(expected);
69+
},
70+
);
71+
});
72+
73+
describe('tokenNameToPath', function () {
74+
it('properly converts a token name to a lodash-compatible path', function () {
75+
expect(utils.tokenNameToPath('some/path/to/token')).toEqual(
76+
'some.path.to.token',
77+
);
78+
});
79+
});
80+
81+
describe('parseResolvedValue', function () {
82+
const space500 = {
83+
r: 0.12941177189350128,
84+
g: 0.1568627506494522,
85+
b: 0.4000000059604645,
86+
a: 1,
87+
};
88+
89+
const blueprint300 = {
90+
r: 0.027450980618596077,
91+
g: 0.30588236451148987,
92+
b: 0.9098039269447327,
93+
a: 1,
94+
};
95+
96+
const neutral800 = {
97+
r: 0.12941177189350128,
98+
g: 0.15294118225574493,
99+
b: 0.1764705926179886,
100+
a: 1,
101+
};
102+
103+
const backgroundVeil = {
104+
r: 0.12941177189350128,
105+
g: 0.15294118225574493,
106+
b: 0.1764705926179886,
107+
a: 0.5,
108+
};
109+
110+
it.each`
111+
figmaColor | type | expected
112+
${space500} | ${'COLOR'} | ${'#212866'}
113+
${blueprint300} | ${'COLOR'} | ${'#074EE8'}
114+
${neutral800} | ${'COLOR'} | ${'#21272D'}
115+
${backgroundVeil} | ${'COLOR'} | ${'rgba(33, 39, 45, 0.5)'}
116+
`(
117+
'will use type $type to convert $figmaColor to $expected',
118+
({ figmaColor, type, expected }) => {
119+
expect(utils.parseResolvedValue(type, figmaColor)).toEqual(expected);
120+
},
121+
);
122+
123+
it('will throw on unrecognized types', () => {
124+
const test = () => {
125+
utils.parseResolvedValue('FLOAT', 0.5);
126+
};
127+
expect(test).toThrow(TypeError);
128+
});
129+
});
130+
});

0 commit comments

Comments
 (0)