From 8d3210c188ccc76768ba8e5fc0ad2b0a4f7c3f96 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Tue, 20 Aug 2024 18:25:20 +0200 Subject: [PATCH 01/71] Initial files --- .gitignore | 4 +- package-lock.json | 108 ++++++++ package.json | 6 + tokens/figma/extractJsonData.mjs | 82 ++++++ tokens/figma/index.mjs | 453 +++++++++++++++++++++++++++++++ tokens/figma/utils.mjs | 66 +++++ 6 files changed, 718 insertions(+), 1 deletion(-) create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 tokens/figma/extractJsonData.mjs create mode 100644 tokens/figma/index.mjs create mode 100644 tokens/figma/utils.mjs diff --git a/.gitignore b/.gitignore index cd8bb31a07b..9c4b7c0db35 100644 --- a/.gitignore +++ b/.gitignore @@ -29,4 +29,6 @@ Temporary Items **/node_modules -**/build \ No newline at end of file +**/build + +.env \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000000..3eaefeb98b8 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,108 @@ +{ + "name": "mistica-design", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "dotenv": "^16.4.5", + "node-fetch": "^3.3.2" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "engines": { + "node": ">= 8" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000000..e9bc0601ffb --- /dev/null +++ b/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "dotenv": "^16.4.5", + "node-fetch": "^3.3.2" + } +} diff --git a/tokens/figma/extractJsonData.mjs b/tokens/figma/extractJsonData.mjs new file mode 100644 index 00000000000..d5482025289 --- /dev/null +++ b/tokens/figma/extractJsonData.mjs @@ -0,0 +1,82 @@ +import fs from "fs"; +import path from "path"; + +const extractJsonData = ( + jsonFiles, + directoryPath +) => { + return jsonFiles.reduce((accumulator, file) => { + const filePath = path.resolve( + directoryPath, + file + ); + const fileContent = fs.readFileSync( + filePath, + "utf8" + ); + const parsedContent = JSON.parse(fileContent); + + const lightArray = Object.keys( + parsedContent.light + ).map((key) => ({ + name: key, + value: parsedContent.light[key].value, + })); + + const darkArray = Object.keys( + parsedContent.dark + ).map((key) => ({ + name: key, + value: parsedContent.dark[key].value, + })); + + const paletteArray = Object.keys( + parsedContent.global.palette + ).map((key) => ({ + name: key, + value: + parsedContent.global.palette[key].value, + })); + + const radiusArray = Object.keys( + parsedContent.radius + ).map((key) => ({ + name: key, + value: + typeof parsedContent.radius[key].value === + "string" + ? parsedContent.radius[key].value === + "circle" + ? 999 // If the value is "circle", set it to 999 + : parseFloat( + parsedContent.radius[key].value + ) // Otherwise, convert it to a float + : parsedContent.radius[key].value, // If it's not a string, use the original value + })); + + console.log(parsedContent.text); + + const fontWeightArray = Object.keys( + parsedContent.text.weight + ).map((key) => ({ + name: key, + value: parsedContent.text.weight[key].value, + })); + + // Extract file name without extension + const fileName = file.split(".")[0]; + + // Accumulate results + accumulator[fileName] = { + light: lightArray, + dark: darkArray, + palette: paletteArray, + radius: radiusArray, + fontWeight: fontWeightArray, + }; + + return accumulator; + }, {}); +}; + +export default extractJsonData; diff --git a/tokens/figma/index.mjs b/tokens/figma/index.mjs new file mode 100644 index 00000000000..d4486762e51 --- /dev/null +++ b/tokens/figma/index.mjs @@ -0,0 +1,453 @@ +import fetch from "node-fetch"; +import dotenv from "dotenv"; +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; +import extractJsonData from "./extractJsonData.mjs"; +import { hexToRgba } from "./utils.mjs"; +import { extractPaletteValue } from "./utils.mjs"; + +dotenv.config({ path: "../../.env" }); +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const FILE_KEY = process.env.FILE_KEY; +const postUrl = `https://api.figma.com/v1/files/${FILE_KEY}/variables`; +const getUrl = `https://api.figma.com/v1/files/${FILE_KEY}/variables/local`; + +const tokensPath = path.resolve(__dirname, "../"); + +const files = fs.readdirSync(tokensPath); + +const jsonFiles = files.filter((file) => + file.endsWith("blau.json") +); + +const jsonData = extractJsonData( + jsonFiles, + tokensPath +); + +async function fetchAndUpdateVariables(jsonData) { + try { + const response = await fetch(getUrl, { + method: "GET", + headers: { + "X-Figma-Token": process.env.FIGMA_TOKEN, // Use environment variable + "Content-Type": "application/json", + }, + }); + + const data = await response.json(); + + // Figma variables + + const existingVariables = data.meta.variables; + const existingCollections = + data.meta.variableCollections; + + // JSON variables + + const paletteVariables = + jsonData.blau.palette; + const lightVariables = jsonData.blau.light; + const darkVariables = jsonData.blau.dark; + const radiusVariables = jsonData.blau.radius; + const fontWeightVariables = + jsonData.blau.fontWeight; + + // Initialize the data object for POST request + const newData = { + variableCollections: [], + variableModes: [], + variables: [], + variableModeValues: [], + }; + + const collectionNames = [ + "constants", + "palette", + "font-weight", + "font-size", + "line-height", + "radius", + "themeContext", + ]; + + function generateTempId(name, collection) { + return `tempId_${collection}_${name}`; + } + + function findVariableInCollection( + variableName, + collectionName, + existingVariables, + existingCollections + ) { + return Object.values( + existingVariables + ).find((variable) => { + // Check if the variable name matches + if (variable.name !== variableName) + return false; + + // Find the collection in existingCollections using variableCollectionId + const collection = Object.values( + existingCollections + ).find( + (col) => + col.id === + variable.variableCollectionId + ); + + // Check if the collection exists, its name matches the collectionName, + // and if the variable's id is listed in the collection's variableIds + return ( + collection && + collection.name === collectionName && + collection.variableIds.includes( + variable.id + ) + ); + }); + } + + function updateCollection( + collectionName, + existingCollections + ) { + // Find the existing collection by name + const existingCollection = Object.values( + existingCollections + ).find( + (collection) => + collection.name === collectionName + ); + + if (existingCollection) { + // If the collection exists, update it + newData.variableCollections.push({ + action: "UPDATE", + id: existingCollection.id, + name: collectionName, + }); + } else { + // If the collection doesn't exist, create it + const tempId = generateTempId( + collectionName + ); + newData.variableCollections.push({ + action: "CREATE", + id: tempId, + name: collectionName, + }); + } + } + + // Process each collection name + collectionNames.forEach((collectionName) => { + updateCollection( + collectionName, + existingCollections + ); + }); + + function updatePalette( + variableName, + variableValue, + collectionName, + existingVariables + ) { + // Find the existing variable by name + const existingVariable = + findVariableInCollection( + variableName, + collectionName, + existingVariables, + existingCollections + ); + + const existingMode = Object.values( + existingCollections + ).find( + (collection) => + collection.name === collectionName + ).defaultModeId; + + if (existingVariable) { + // If the variable exists, update it + newData.variables.push({ + action: "UPDATE", + id: existingVariable.id, + name: variableName, + resolvedType: "COLOR", + variableCollectionId: + existingVariable.variableCollectionId, + }); + + newData.variableModeValues.push({ + action: "UPDATE", + variableId: existingVariable.id, + modeId: existingMode, + value: hexToRgba(variableValue), + }); + allVariableNamesInCurrentData.add( + variableName + ); + } else { + // If the variable doesn't exist, create it + const tempId = generateTempId( + variableName, + collectionName + ); + const collectionId = + newData.variableCollections.find( + (collection) => + collection.name === collectionName + ).id; + newData.variables.push({ + action: "CREATE", + id: tempId, + name: variableName, + variableCollectionId: collectionId, + resolvedType: "COLOR", + }); + + newData.variableModeValues.push({ + action: "CREATE", + variableId: tempId, + modeId: existingMode, + value: hexToRgba(variableValue), + }); + + allVariableNamesInCurrentData.add( + variableName + ); + } + } + + function removeUnusedVariables( + existingVariables, + currentJsonVariables + ) { + // Identify and delete variables that are not in the current data + Object.values(existingVariables).forEach( + (variable) => { + if ( + !currentJsonVariables.has( + variable.name + ) + ) { + newData.variables.push({ + action: "DELETE", + id: variable.id, + }); + } + } + ); + } + + const allVariableNamesInCurrentData = + new Set(); + + paletteVariables.forEach((variable) => { + updatePalette( + variable.name, + variable.value, + "palette", + existingVariables, + allVariableNamesInCurrentData + ); + }); + + removeUnusedVariables( + existingVariables, + allVariableNamesInCurrentData + ); + + function updateRadius( + variableName, + variableValue, + collectionName, + existingVariables, + existingCollections + ) { + const existingVariable = + findVariableInCollection( + variableName, + collectionName, + existingVariables, + existingCollections + ); + + const existingMode = Object.values( + existingCollections + ).find( + (collection) => + collection.name === collectionName + ).defaultModeId; + + if (existingVariable) { + newData.variables.push({ + action: "UPDATE", + id: existingVariable.id, + name: variableName, + resolvedType: "FLOAT", + variableCollectionId: + existingVariable.variableCollectionId, + }); + + newData.variableModeValues.push({ + action: "UPDATE", + variableId: existingVariable.id, + modeId: existingMode, + value: variableValue, + }); + } else { + const tempId = generateTempId( + variableName, + collectionName + ); + const collectionId = + newData.variableCollections.find( + (collection) => + collection.name === collectionName + ).id; + + newData.variables.push({ + action: "CREATE", + id: tempId, + name: variableName, + resolvedType: "FLOAT", + variableCollectionId: collectionId, + }); + + newData.variableModeValues.push({ + action: "CREATE", + variableId: tempId, + modeId: existingMode, + value: variableValue, + }); + } + } + + radiusVariables.forEach((variable) => { + updateRadius( + variable.name, + variable.value, + "radius", + existingVariables, + existingCollections + ); + }); + + function updateFontWeight( + variableName, + variableValue, + collectionName, + existingVariables, + existingCollections + ) { + const existingVariable = + findVariableInCollection( + variableName, + collectionName, + existingVariables, + existingCollections + ); + + const existingMode = Object.values( + existingCollections + ).find( + (collection) => + collection.name === collectionName + ).defaultModeId; + + if (existingVariable) { + newData.variables.push({ + action: "UPDATE", + id: existingVariable.id, + name: variableName, + resolvedType: "STRING", + variableCollectionId: + existingVariable.variableCollectionId, + }); + + newData.variableModeValues.push({ + action: "UPDATE", + variableId: existingVariable.id, + modeId: existingMode, + value: variableValue, + }); + } else { + const tempId = generateTempId( + variableName, + collectionName + ); + const collectionId = + newData.variableCollections.find( + (collection) => + collection.name === collectionName + ).id; + + newData.variables.push({ + action: "CREATE", + id: tempId, + name: variableName, + resolvedType: "STRING", + variableCollectionId: collectionId, + }); + + newData.variableModeValues.push({ + action: "CREATE", + variableId: tempId, + modeId: existingMode, + value: variableValue, + }); + } + } + + fontWeightVariables.forEach((variable) => { + updateFontWeight( + variable.name, + variable.value, + "font-weight", + existingVariables, + existingCollections + ); + }); + + // Return the processed data for further use + return newData; + } catch (error) { + console.error("Error:", error); + throw error; // rethrow the error to be handled later + } +} + +// Use an async function to handle the post request after data processing + +async function processAndPostData() { + try { + const newData = await fetchAndUpdateVariables( + jsonData + ); + + const response = await fetch(postUrl, { + method: "POST", + headers: { + "X-Figma-Token": process.env.FIGMA_TOKEN, // Use environment variable + "Content-Type": "application/json", + }, + body: JSON.stringify(newData), + }); + + const data = await response.json(); + console.log("Success:", data); + } catch (error) { + console.error("Error:", error); + } +} + +// Start the process +processAndPostData(); diff --git a/tokens/figma/utils.mjs b/tokens/figma/utils.mjs new file mode 100644 index 00000000000..35e7a69bf68 --- /dev/null +++ b/tokens/figma/utils.mjs @@ -0,0 +1,66 @@ +export function hexToRgba(hex, alpha = 1) { + // Remove the leading # if it's present + hex = hex.replace(/^#/, ""); + + // Expand shorthand form (e.g., "03F") to full form (e.g., "0033FF") + if (hex.length === 3) { + hex = hex + .split("") + .map((char) => char + char) + .join(""); + } + + // Parse the r, g, b values + const bigint = parseInt(hex, 16); + const r = ((bigint >> 16) & 255) / 255; + const g = ((bigint >> 8) & 255) / 255; + const b = (bigint & 255) / 255; + + // Return the RGBA object with normalized values + return { + r, + g, + b, + a: alpha, + }; +} + +export function extractPaletteValue(constant) { + // Convert value to a string if it's not already + const valueStr = String(constant); + + // Default values + let value = ""; + let alpha = 0; + + // Check if the value is an 'rgba' format + if (valueStr.startsWith("rgba")) { + const rgbaMatch = valueStr.match( + /rgba\(\{([^}]+)}\s*,\s*(\d+\.?\d*)\)/ + ); + if (rgbaMatch) { + value = rgbaMatch[1].replace( + /^palette\./, + "" + ); // Remove 'palette.' prefix + alpha = parseFloat(rgbaMatch[2]); + } + } else { + // Extract palette name from curly braces + const paletteMatch = + valueStr.match(/\{([^}]+)\}/); + if (paletteMatch) { + // Remove the 'palette.' prefix if present + const fullPaletteName = paletteMatch[1]; + value = fullPaletteName.replace( + /^palette\./, + "" + ); + } + } + + return { + value, + alpha, + }; +} From 6e4413a585988ea9053d5390ded7a1903ed66099 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Tue, 20 Aug 2024 18:29:58 +0200 Subject: [PATCH 02/71] Add env variable and remove console.log --- tokens/figma/extractJsonData.mjs | 2 -- tokens/figma/index.mjs | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tokens/figma/extractJsonData.mjs b/tokens/figma/extractJsonData.mjs index d5482025289..a3ce69a6f51 100644 --- a/tokens/figma/extractJsonData.mjs +++ b/tokens/figma/extractJsonData.mjs @@ -54,8 +54,6 @@ const extractJsonData = ( : parsedContent.radius[key].value, // If it's not a string, use the original value })); - console.log(parsedContent.text); - const fontWeightArray = Object.keys( parsedContent.text.weight ).map((key) => ({ diff --git a/tokens/figma/index.mjs b/tokens/figma/index.mjs index d4486762e51..f693b4683ad 100644 --- a/tokens/figma/index.mjs +++ b/tokens/figma/index.mjs @@ -12,6 +12,7 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const FILE_KEY = process.env.FILE_KEY; +const FIGMA_TOKEN = process.env.FIGMA_TOKEN; const postUrl = `https://api.figma.com/v1/files/${FILE_KEY}/variables`; const getUrl = `https://api.figma.com/v1/files/${FILE_KEY}/variables/local`; From 8878c177ebebcd49f27650298cabb85dec180061 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Wed, 21 Aug 2024 14:41:35 +0200 Subject: [PATCH 03/71] Add font-size and line-height variables --- tokens/figma/extractJsonData.mjs | 62 ++++++++++++ tokens/figma/index.mjs | 158 ++++++++++++++++++++++++++++++- 2 files changed, 219 insertions(+), 1 deletion(-) diff --git a/tokens/figma/extractJsonData.mjs b/tokens/figma/extractJsonData.mjs index a3ce69a6f51..d9db93f07a0 100644 --- a/tokens/figma/extractJsonData.mjs +++ b/tokens/figma/extractJsonData.mjs @@ -61,6 +61,66 @@ const extractJsonData = ( value: parsedContent.text.weight[key].value, })); + const fontSizeArray = Object.keys( + parsedContent.text.size + ).flatMap((key) => { + const value = + parsedContent.text.size[key].value; + + // Check if the value is an object with mobile and desktop properties + if ( + typeof value === "object" && + value !== null + ) { + return [ + { + name: `mobile/${key}`, + value: parseFloat(value.mobile), + }, + { + name: `desktop/${key}`, + value: parseFloat(value.desktop), + }, + ]; + } + + // If value is not an object, return a single entry + return { + name: key, + value: parseFloat(value), + }; + }); + + const lineHeightArray = Object.keys( + parsedContent.text.lineHeight + ).flatMap((key) => { + const value = + parsedContent.text.lineHeight[key].value; + + // Check if the value is an object with mobile and desktop properties + if ( + typeof value === "object" && + value !== null + ) { + return [ + { + name: `mobile/${key}`, + value: parseFloat(value.mobile), + }, + { + name: `desktop/${key}`, + value: parseFloat(value.desktop), + }, + ]; + } + + // If value is not an object, return a single entry + return { + name: key, + value: parseFloat(value), + }; + }); + // Extract file name without extension const fileName = file.split(".")[0]; @@ -71,6 +131,8 @@ const extractJsonData = ( palette: paletteArray, radius: radiusArray, fontWeight: fontWeightArray, + fontSize: fontSizeArray, + lineHeight: lineHeightArray, }; return accumulator; diff --git a/tokens/figma/index.mjs b/tokens/figma/index.mjs index f693b4683ad..4527cbed29e 100644 --- a/tokens/figma/index.mjs +++ b/tokens/figma/index.mjs @@ -34,7 +34,7 @@ async function fetchAndUpdateVariables(jsonData) { const response = await fetch(getUrl, { method: "GET", headers: { - "X-Figma-Token": process.env.FIGMA_TOKEN, // Use environment variable + "X-Figma-Token": FIGMA_TOKEN, // Use environment variable "Content-Type": "application/json", }, }); @@ -56,6 +56,10 @@ async function fetchAndUpdateVariables(jsonData) { const radiusVariables = jsonData.blau.radius; const fontWeightVariables = jsonData.blau.fontWeight; + const fontSizeVariables = + jsonData.blau.fontSize; + const lineHeightVariables = + jsonData.blau.lineHeight; // Initialize the data object for POST request const newData = { @@ -418,6 +422,158 @@ async function fetchAndUpdateVariables(jsonData) { ); }); + function updateFontSize( + variableName, + variableValue, + collectionName, + existingVariables, + existingCollections + ) { + const existingVariable = + findVariableInCollection( + variableName, + collectionName, + existingVariables, + existingCollections + ); + + const existingMode = Object.values( + existingCollections + ).find( + (collection) => + collection.name === collectionName + ).defaultModeId; + + if (existingVariable) { + newData.variables.push({ + action: "UPDATE", + id: existingVariable.id, + name: variableName, + resolvedType: "FLOAT", + variableCollectionId: + existingVariable.variableCollectionId, + }); + + newData.variableModeValues.push({ + action: "UPDATE", + variableId: existingVariable.id, + modeId: existingMode, + value: variableValue, + }); + } else { + const tempId = generateTempId( + variableName, + collectionName + ); + const collectionId = + newData.variableCollections.find( + (collection) => + collection.name === collectionName + ).id; + + newData.variables.push({ + action: "CREATE", + id: tempId, + name: variableName, + resolvedType: "FLOAT", + variableCollectionId: collectionId, + }); + + newData.variableModeValues.push({ + action: "CREATE", + variableId: tempId, + modeId: existingMode, + value: variableValue, + }); + } + } + + fontSizeVariables.forEach((variable) => { + updateFontSize( + variable.name, + variable.value, + "font-size", + existingVariables, + existingCollections + ); + }); + + function updateLineHeight( + variableName, + variableValue, + collectionName, + existingVariables, + existingCollections + ) { + const existingVariable = + findVariableInCollection( + variableName, + collectionName, + existingVariables, + existingCollections + ); + + const existingMode = Object.values( + existingCollections + ).find( + (collection) => + collection.name === collectionName + ).defaultModeId; + + if (existingVariable) { + newData.variables.push({ + action: "UPDATE", + id: existingVariable.id, + name: variableName, + resolvedType: "FLOAT", + variableCollectionId: + existingVariable.variableCollectionId, + }); + + newData.variableModeValues.push({ + action: "UPDATE", + variableId: existingVariable.id, + modeId: existingMode, + value: variableValue, + }); + } else { + const tempId = generateTempId( + variableName, + collectionName + ); + const collectionId = + newData.variableCollections.find( + (collection) => + collection.name === collectionName + ).id; + + newData.variables.push({ + action: "CREATE", + id: tempId, + name: variableName, + resolvedType: "FLOAT", + variableCollectionId: collectionId, + }); + + newData.variableModeValues.push({ + action: "CREATE", + variableId: tempId, + modeId: existingMode, + value: variableValue, + }); + } + } + + lineHeightVariables.forEach((variable) => { + updateLineHeight( + variable.name, + variable.value, + "line-height", + existingVariables, + existingCollections + ); + }); + // Return the processed data for further use return newData; } catch (error) { From 6b75ba6bbe7eca92f430eeb8f90f10d4605fb7da Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Wed, 21 Aug 2024 15:44:03 +0200 Subject: [PATCH 04/71] Code refactor --- tokens/figma/index.mjs | 322 +++++------------------------------------ 1 file changed, 38 insertions(+), 284 deletions(-) diff --git a/tokens/figma/index.mjs b/tokens/figma/index.mjs index 4527cbed29e..7a229c21b1a 100644 --- a/tokens/figma/index.mjs +++ b/tokens/figma/index.mjs @@ -157,11 +157,13 @@ async function fetchAndUpdateVariables(jsonData) { ); }); - function updatePalette( + function updateVariables( variableName, variableValue, collectionName, - existingVariables + existingVariables, + existingCollections, + variableType ) { // Find the existing variable by name const existingVariable = @@ -185,7 +187,7 @@ async function fetchAndUpdateVariables(jsonData) { action: "UPDATE", id: existingVariable.id, name: variableName, - resolvedType: "COLOR", + resolvedType: variableType, variableCollectionId: existingVariable.variableCollectionId, }); @@ -194,7 +196,10 @@ async function fetchAndUpdateVariables(jsonData) { action: "UPDATE", variableId: existingVariable.id, modeId: existingMode, - value: hexToRgba(variableValue), + value: + variableType === "COLOR" + ? hexToRgba(variableValue) + : variableValue, }); allVariableNamesInCurrentData.add( variableName @@ -215,14 +220,17 @@ async function fetchAndUpdateVariables(jsonData) { id: tempId, name: variableName, variableCollectionId: collectionId, - resolvedType: "COLOR", + resolvedType: variableType, }); newData.variableModeValues.push({ action: "CREATE", variableId: tempId, modeId: existingMode, - value: hexToRgba(variableValue), + value: + variableType === "COLOR" + ? hexToRgba(variableValue) + : variableValue, }); allVariableNamesInCurrentData.add( @@ -256,324 +264,70 @@ async function fetchAndUpdateVariables(jsonData) { new Set(); paletteVariables.forEach((variable) => { - updatePalette( + updateVariables( variable.name, variable.value, "palette", existingVariables, + existingCollections, + "COLOR", allVariableNamesInCurrentData ); }); - removeUnusedVariables( - existingVariables, - allVariableNamesInCurrentData - ); - - function updateRadius( - variableName, - variableValue, - collectionName, - existingVariables, - existingCollections - ) { - const existingVariable = - findVariableInCollection( - variableName, - collectionName, - existingVariables, - existingCollections - ); - - const existingMode = Object.values( - existingCollections - ).find( - (collection) => - collection.name === collectionName - ).defaultModeId; - - if (existingVariable) { - newData.variables.push({ - action: "UPDATE", - id: existingVariable.id, - name: variableName, - resolvedType: "FLOAT", - variableCollectionId: - existingVariable.variableCollectionId, - }); - - newData.variableModeValues.push({ - action: "UPDATE", - variableId: existingVariable.id, - modeId: existingMode, - value: variableValue, - }); - } else { - const tempId = generateTempId( - variableName, - collectionName - ); - const collectionId = - newData.variableCollections.find( - (collection) => - collection.name === collectionName - ).id; - - newData.variables.push({ - action: "CREATE", - id: tempId, - name: variableName, - resolvedType: "FLOAT", - variableCollectionId: collectionId, - }); - - newData.variableModeValues.push({ - action: "CREATE", - variableId: tempId, - modeId: existingMode, - value: variableValue, - }); - } - } - radiusVariables.forEach((variable) => { - updateRadius( + updateVariables( variable.name, variable.value, "radius", existingVariables, - existingCollections + existingCollections, + "FLOAT", + allVariableNamesInCurrentData ); }); - function updateFontWeight( - variableName, - variableValue, - collectionName, - existingVariables, - existingCollections - ) { - const existingVariable = - findVariableInCollection( - variableName, - collectionName, - existingVariables, - existingCollections - ); - - const existingMode = Object.values( - existingCollections - ).find( - (collection) => - collection.name === collectionName - ).defaultModeId; - - if (existingVariable) { - newData.variables.push({ - action: "UPDATE", - id: existingVariable.id, - name: variableName, - resolvedType: "STRING", - variableCollectionId: - existingVariable.variableCollectionId, - }); - - newData.variableModeValues.push({ - action: "UPDATE", - variableId: existingVariable.id, - modeId: existingMode, - value: variableValue, - }); - } else { - const tempId = generateTempId( - variableName, - collectionName - ); - const collectionId = - newData.variableCollections.find( - (collection) => - collection.name === collectionName - ).id; - - newData.variables.push({ - action: "CREATE", - id: tempId, - name: variableName, - resolvedType: "STRING", - variableCollectionId: collectionId, - }); - - newData.variableModeValues.push({ - action: "CREATE", - variableId: tempId, - modeId: existingMode, - value: variableValue, - }); - } - } - fontWeightVariables.forEach((variable) => { - updateFontWeight( + updateVariables( variable.name, variable.value, "font-weight", existingVariables, - existingCollections + existingCollections, + "FLOAT", + allVariableNamesInCurrentData ); }); - function updateFontSize( - variableName, - variableValue, - collectionName, - existingVariables, - existingCollections - ) { - const existingVariable = - findVariableInCollection( - variableName, - collectionName, - existingVariables, - existingCollections - ); - - const existingMode = Object.values( - existingCollections - ).find( - (collection) => - collection.name === collectionName - ).defaultModeId; - - if (existingVariable) { - newData.variables.push({ - action: "UPDATE", - id: existingVariable.id, - name: variableName, - resolvedType: "FLOAT", - variableCollectionId: - existingVariable.variableCollectionId, - }); - - newData.variableModeValues.push({ - action: "UPDATE", - variableId: existingVariable.id, - modeId: existingMode, - value: variableValue, - }); - } else { - const tempId = generateTempId( - variableName, - collectionName - ); - const collectionId = - newData.variableCollections.find( - (collection) => - collection.name === collectionName - ).id; - - newData.variables.push({ - action: "CREATE", - id: tempId, - name: variableName, - resolvedType: "FLOAT", - variableCollectionId: collectionId, - }); - - newData.variableModeValues.push({ - action: "CREATE", - variableId: tempId, - modeId: existingMode, - value: variableValue, - }); - } - } - fontSizeVariables.forEach((variable) => { - updateFontSize( + updateVariables( variable.name, variable.value, "font-size", existingVariables, - existingCollections + existingCollections, + "FLOAT", + allVariableNamesInCurrentData ); }); - function updateLineHeight( - variableName, - variableValue, - collectionName, - existingVariables, - existingCollections - ) { - const existingVariable = - findVariableInCollection( - variableName, - collectionName, - existingVariables, - existingCollections - ); - - const existingMode = Object.values( - existingCollections - ).find( - (collection) => - collection.name === collectionName - ).defaultModeId; - - if (existingVariable) { - newData.variables.push({ - action: "UPDATE", - id: existingVariable.id, - name: variableName, - resolvedType: "FLOAT", - variableCollectionId: - existingVariable.variableCollectionId, - }); - - newData.variableModeValues.push({ - action: "UPDATE", - variableId: existingVariable.id, - modeId: existingMode, - value: variableValue, - }); - } else { - const tempId = generateTempId( - variableName, - collectionName - ); - const collectionId = - newData.variableCollections.find( - (collection) => - collection.name === collectionName - ).id; - - newData.variables.push({ - action: "CREATE", - id: tempId, - name: variableName, - resolvedType: "FLOAT", - variableCollectionId: collectionId, - }); - - newData.variableModeValues.push({ - action: "CREATE", - variableId: tempId, - modeId: existingMode, - value: variableValue, - }); - } - } - lineHeightVariables.forEach((variable) => { - updateLineHeight( + updateVariables( variable.name, variable.value, "line-height", existingVariables, - existingCollections + existingCollections, + "FLOAT", + allVariableNamesInCurrentData ); }); + removeUnusedVariables( + existingVariables, + allVariableNamesInCurrentData + ); + // Return the processed data for further use return newData; } catch (error) { From f600c36bf23da5cf298771a54b78ac6b8e0d3542 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Wed, 21 Aug 2024 15:49:28 +0200 Subject: [PATCH 05/71] Simplify logic --- tokens/figma/index.mjs | 104 ++++++++++++++++++----------------------- 1 file changed, 46 insertions(+), 58 deletions(-) diff --git a/tokens/figma/index.mjs b/tokens/figma/index.mjs index 7a229c21b1a..83a87dcada7 100644 --- a/tokens/figma/index.mjs +++ b/tokens/figma/index.mjs @@ -263,65 +263,53 @@ async function fetchAndUpdateVariables(jsonData) { const allVariableNamesInCurrentData = new Set(); - paletteVariables.forEach((variable) => { - updateVariables( - variable.name, - variable.value, - "palette", - existingVariables, - existingCollections, - "COLOR", - allVariableNamesInCurrentData - ); - }); - - radiusVariables.forEach((variable) => { - updateVariables( - variable.name, - variable.value, - "radius", - existingVariables, - existingCollections, - "FLOAT", - allVariableNamesInCurrentData - ); - }); - - fontWeightVariables.forEach((variable) => { - updateVariables( - variable.name, - variable.value, - "font-weight", - existingVariables, - existingCollections, - "FLOAT", - allVariableNamesInCurrentData - ); - }); - - fontSizeVariables.forEach((variable) => { - updateVariables( - variable.name, - variable.value, - "font-size", - existingVariables, - existingCollections, - "FLOAT", - allVariableNamesInCurrentData - ); - }); + const variableGroups = [ + { + variables: paletteVariables, + collectionName: "palette", + resolvedType: "COLOR", + }, + { + variables: radiusVariables, + collectionName: "radius", + resolvedType: "FLOAT", + }, + { + variables: fontWeightVariables, + collectionName: "font-weight", + resolvedType: "FLOAT", + }, + { + variables: fontSizeVariables, + collectionName: "font-size", + resolvedType: "FLOAT", + }, + { + variables: lineHeightVariables, + collectionName: "line-height", + resolvedType: "FLOAT", + }, + ]; - lineHeightVariables.forEach((variable) => { - updateVariables( - variable.name, - variable.value, - "line-height", - existingVariables, - existingCollections, - "FLOAT", - allVariableNamesInCurrentData - ); - }); + variableGroups.forEach( + ({ + variables, + collectionName, + resolvedType, + }) => { + variables.forEach((variable) => { + updateVariables( + variable.name, + variable.value, + collectionName, + existingVariables, + existingCollections, + resolvedType, + allVariableNamesInCurrentData + ); + }); + } + ); removeUnusedVariables( existingVariables, From ddf97bc9426f5ff587e552804f85a102a9d3c5c8 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Wed, 21 Aug 2024 15:56:35 +0200 Subject: [PATCH 06/71] Fix wrong resolvedType in fontWeight variables --- tokens/figma/index.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tokens/figma/index.mjs b/tokens/figma/index.mjs index 83a87dcada7..cc3a687461b 100644 --- a/tokens/figma/index.mjs +++ b/tokens/figma/index.mjs @@ -277,7 +277,7 @@ async function fetchAndUpdateVariables(jsonData) { { variables: fontWeightVariables, collectionName: "font-weight", - resolvedType: "FLOAT", + resolvedType: "STRING", }, { variables: fontSizeVariables, From 99174a431a2ddb8b7d92be21c8771d7a821bfd3f Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Wed, 21 Aug 2024 16:04:20 +0200 Subject: [PATCH 07/71] Add variable scoping --- tokens/figma/index.mjs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tokens/figma/index.mjs b/tokens/figma/index.mjs index cc3a687461b..ea953e9aaea 100644 --- a/tokens/figma/index.mjs +++ b/tokens/figma/index.mjs @@ -163,7 +163,8 @@ async function fetchAndUpdateVariables(jsonData) { collectionName, existingVariables, existingCollections, - variableType + variableType, + variableScopes ) { // Find the existing variable by name const existingVariable = @@ -190,6 +191,7 @@ async function fetchAndUpdateVariables(jsonData) { resolvedType: variableType, variableCollectionId: existingVariable.variableCollectionId, + scopes: variableScopes, }); newData.variableModeValues.push({ @@ -221,6 +223,7 @@ async function fetchAndUpdateVariables(jsonData) { name: variableName, variableCollectionId: collectionId, resolvedType: variableType, + scopes: variableScopes, }); newData.variableModeValues.push({ @@ -268,26 +271,31 @@ async function fetchAndUpdateVariables(jsonData) { variables: paletteVariables, collectionName: "palette", resolvedType: "COLOR", + variableScopes: ["ALL_SCOPES"], }, { variables: radiusVariables, collectionName: "radius", resolvedType: "FLOAT", + variableScopes: ["CORNER_RADIUS"], }, { variables: fontWeightVariables, collectionName: "font-weight", resolvedType: "STRING", + variableScopes: ["FONT_WEIGHT"], }, { variables: fontSizeVariables, collectionName: "font-size", resolvedType: "FLOAT", + variableScopes: ["FONT_SIZE"], }, { variables: lineHeightVariables, collectionName: "line-height", resolvedType: "FLOAT", + variableScopes: ["LINE_HEIGHT"], }, ]; @@ -296,6 +304,7 @@ async function fetchAndUpdateVariables(jsonData) { variables, collectionName, resolvedType, + variableScopes, }) => { variables.forEach((variable) => { updateVariables( @@ -305,6 +314,7 @@ async function fetchAndUpdateVariables(jsonData) { existingVariables, existingCollections, resolvedType, + variableScopes, allVariableNamesInCurrentData ); }); From 9394288c6c7ee57967d3e6abee8d0bd8de29df65 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Wed, 21 Aug 2024 17:12:45 +0200 Subject: [PATCH 08/71] Allow to update multiple brands --- tokens/figma/README.md | 77 ++++++++++++++++++++++++++++++++++++++ tokens/figma/index.mjs | 84 ++++++++++++++++++++++++------------------ 2 files changed, 125 insertions(+), 36 deletions(-) create mode 100644 tokens/figma/README.md diff --git a/tokens/figma/README.md b/tokens/figma/README.md new file mode 100644 index 00000000000..283d417a90f --- /dev/null +++ b/tokens/figma/README.md @@ -0,0 +1,77 @@ +# Design Tokens Automation Script + +## Objective + +This script automates the process of fetching, updating, and posting design tokens to Figma for multiple brands. It reads JSON data files, extracts relevant variables, compares them with existing data from Figma, updates them if necessary, and then posts the changes back to Figma's API. + +## Overview of the Script + +### Environment Setup + +- The script uses the `dotenv` package to load environment variables (like Figma API tokens and file keys) from a `.env` file. +- It imports necessary modules (`fetch`, `fs`, `path`, etc.) and utility functions (`extractJsonData`, `hexToRgba`, `extractPaletteValue`). + +### File and Data Preparation + +- The script reads JSON files from a specific directory, filters out the ones with a `.json` extension, and extracts their data using the `extractJsonData` function. + +### Brands and URLs + +- An object `brands` maps brand names to their corresponding Figma API URLs. This allows the script to handle multiple brands in one go. + +## Main Functions + +### `fetchAndUpdateVariables(jsonData, brand, url)` + +**Purpose:** +Handles the core logic of fetching existing variables from Figma, comparing them with local JSON data, and preparing a data object for the POST request. + +**Steps:** + +1. Fetch existing variables and collections from Figma. +2. Initialize a `newData` object to store the changes that need to be posted back to Figma. +3. For each collection (like "palette", "radius", "font-weight"): + - Check if it exists. If it does, prepare an update request; otherwise, prepare a create request. +4. For each variable within these collections: + - Check if it exists and needs updating. If it doesn’t exist, prepare a create request. +5. Identify and delete any variables in Figma that are no longer in the local JSON data. + +### `updateVariables(variableName, variableValue, collectionName, existingVariables, existingCollections, variableType, variableScopes)` + +**Purpose:** +Updates or creates variables in the `newData` object based on their existence in the Figma data. + +**Parameters:** + +- `variableName`, `variableValue`: The name and value of the variable to be updated/created. +- `collectionName`: The name of the collection the variable belongs to. +- `existingVariables`, `existingCollections`: The current variables and collections fetched from Figma. +- `variableType`, `variableScopes`: Additional metadata for the variable. + +### `findVariableInCollection(variableName, collectionName, existingVariables, existingCollections)` + +**Purpose:** +Finds a variable within a specific collection in Figma's existing data. + +**Returns:** +The variable object if found, otherwise `undefined`. + +### `processAndPostData(url, brand)` + +**Purpose:** +Processes the data for a specific brand and posts the updated data to Figma. + +**Parameters:** +`url` (Figma API URL), `brand` (name of the brand). + +### `processAllUrls(brands)` + +**Purpose:** +Sequentially processes and posts data for all brands defined in the `brands` object. + +**Parameters:** +`brands` (an object mapping brand names to Figma API URLs). + +## Execution + +The script calls `processAllUrls(brands)` at the end, which triggers the whole process for all defined brands. diff --git a/tokens/figma/index.mjs b/tokens/figma/index.mjs index ea953e9aaea..b7a8ccc33af 100644 --- a/tokens/figma/index.mjs +++ b/tokens/figma/index.mjs @@ -11,17 +11,16 @@ dotenv.config({ path: "../../.env" }); const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); -const FILE_KEY = process.env.FILE_KEY; +const FILE_KEY_1 = process.env.FILE_KEY_1; +const FILE_KEY_2 = process.env.FILE_KEY_2; const FIGMA_TOKEN = process.env.FIGMA_TOKEN; -const postUrl = `https://api.figma.com/v1/files/${FILE_KEY}/variables`; -const getUrl = `https://api.figma.com/v1/files/${FILE_KEY}/variables/local`; const tokensPath = path.resolve(__dirname, "../"); const files = fs.readdirSync(tokensPath); const jsonFiles = files.filter((file) => - file.endsWith("blau.json") + file.endsWith(".json") ); const jsonData = extractJsonData( @@ -29,9 +28,18 @@ const jsonData = extractJsonData( tokensPath ); -async function fetchAndUpdateVariables(jsonData) { +const brands = { + blau: `https://api.figma.com/v1/files/${FILE_KEY_2}/variables`, + "vivo-new": `https://api.figma.com/v1/files/${FILE_KEY_1}/variables`, +}; + +async function fetchAndUpdateVariables( + jsonData, + brand, + url +) { try { - const response = await fetch(getUrl, { + const response = await fetch(`${url}/local`, { method: "GET", headers: { "X-Figma-Token": FIGMA_TOKEN, // Use environment variable @@ -39,27 +47,14 @@ async function fetchAndUpdateVariables(jsonData) { }, }); - const data = await response.json(); + const figmaData = await response.json(); // Figma variables - const existingVariables = data.meta.variables; + const existingVariables = + figmaData.meta.variables; const existingCollections = - data.meta.variableCollections; - - // JSON variables - - const paletteVariables = - jsonData.blau.palette; - const lightVariables = jsonData.blau.light; - const darkVariables = jsonData.blau.dark; - const radiusVariables = jsonData.blau.radius; - const fontWeightVariables = - jsonData.blau.fontWeight; - const fontSizeVariables = - jsonData.blau.fontSize; - const lineHeightVariables = - jsonData.blau.lineHeight; + figmaData.meta.variableCollections; // Initialize the data object for POST request const newData = { @@ -268,31 +263,31 @@ async function fetchAndUpdateVariables(jsonData) { const variableGroups = [ { - variables: paletteVariables, + variables: jsonData[brand].palette, collectionName: "palette", resolvedType: "COLOR", variableScopes: ["ALL_SCOPES"], }, { - variables: radiusVariables, + variables: jsonData[brand].radius, collectionName: "radius", resolvedType: "FLOAT", variableScopes: ["CORNER_RADIUS"], }, { - variables: fontWeightVariables, + variables: jsonData[brand].fontWeight, collectionName: "font-weight", resolvedType: "STRING", variableScopes: ["FONT_WEIGHT"], }, { - variables: fontSizeVariables, + variables: jsonData[brand].fontSize, collectionName: "font-size", resolvedType: "FLOAT", variableScopes: ["FONT_SIZE"], }, { - variables: lineHeightVariables, + variables: jsonData[brand].lineHeight, collectionName: "line-height", resolvedType: "FLOAT", variableScopes: ["LINE_HEIGHT"], @@ -336,27 +331,44 @@ async function fetchAndUpdateVariables(jsonData) { // Use an async function to handle the post request after data processing -async function processAndPostData() { +async function processAndPostData(url, brand) { try { const newData = await fetchAndUpdateVariables( - jsonData + jsonData, + brand, + url ); - const response = await fetch(postUrl, { + const response = await fetch(url, { method: "POST", headers: { - "X-Figma-Token": process.env.FIGMA_TOKEN, // Use environment variable + "X-Figma-Token": FIGMA_TOKEN, "Content-Type": "application/json", }, body: JSON.stringify(newData), }); const data = await response.json(); - console.log("Success:", data); + console.log( + `Success for brand ${brand}:`, + data + ); } catch (error) { - console.error("Error:", error); + console.error( + `Error posting data for brand ${brand}:`, + error + ); + } +} + +//process the data for an array of urls + +async function processAllUrls(brands) { + for (const [brand, url] of Object.entries( + brands + )) { + await processAndPostData(url, brand); } } -// Start the process -processAndPostData(); +processAllUrls(brands); From a803f888014ef7892d1d9af37eb8bbadd270b601 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Thu, 22 Aug 2024 14:42:49 +0200 Subject: [PATCH 09/71] Add light and dark variables support --- tokens/figma/extractJsonData.mjs | 260 +++++++++++++++++++++++++++++-- tokens/figma/index.mjs | 92 +++++++++-- 2 files changed, 333 insertions(+), 19 deletions(-) diff --git a/tokens/figma/extractJsonData.mjs b/tokens/figma/extractJsonData.mjs index d9db93f07a0..5dd2d443e8f 100644 --- a/tokens/figma/extractJsonData.mjs +++ b/tokens/figma/extractJsonData.mjs @@ -1,5 +1,6 @@ import fs from "fs"; import path from "path"; +import { hexToRgba } from "./utils.mjs"; const extractJsonData = ( jsonFiles, @@ -18,24 +19,263 @@ const extractJsonData = ( const lightArray = Object.keys( parsedContent.light - ).map((key) => ({ - name: key, - value: parsedContent.light[key].value, - })); + ).flatMap((key) => { + const colorData = parsedContent.light[key]; + const { value, type } = colorData; + + function getPaletteName(value) { + const regexMatch = value.match( + /{palette\.(.*?)}/ + ); + if (regexMatch) { + return regexMatch[1]; + } + const rgbaMatch = value.match( + /rgba\(\{palette\.(.*?)\},\s*\d*\.?\d*\)/ + ); + if (rgbaMatch) { + return rgbaMatch[1]; + } + throw new Error( + `Unexpected color format: ${value}` + ); + } + + function getPaletteValue(colorName) { + const paletteValue = + parsedContent.global.palette[colorName] + ?.value; + if (!paletteValue) { + throw new Error( + `Color ${colorName} not found in palette` + ); + } + return paletteValue; + } + + // Default color handling + if ( + typeof value === "string" && + !value.startsWith("rgba") + ) { + return { + name: `light/${key}`, + value: getPaletteName(value), + hasAlias: true, + }; + } + + // Color with alpha (rgba) + if ( + typeof value === "string" && + value.startsWith("rgba") + ) { + const alphaMatch = value.match( + /rgba\([^)]+,\s*([^)]+)\)/ + ); + const alpha = alphaMatch + ? alphaMatch[1] + : "1"; + const baseColorName = + getPaletteName(value); + + // If alpha is 1, do nothing, otherwise replace with palette value + return alpha === "1" + ? { + name: `light/${key}`, + value: baseColorName, + hasAlias: true, + } + : { + name: `light/${key}`, + value: hexToRgba( + getPaletteValue(baseColorName), + alpha + ), + hasAlias: false, + }; + } + + // Gradient handling + if ( + type === "linear-gradient" && + typeof value === "object" + ) { + return value.colors.map( + (color, index) => { + const alphaMatch = color.value.match( + /rgba\([^)]+,\s*([^)]+)\)/ + ); + const alpha = alphaMatch + ? alphaMatch[1] + : "1"; + const baseColorName = getPaletteName( + color.value + ); + + // Check if the color has an alpha different than 1 + return alpha === "1" + ? { + name: `light/${key}-stop-${ + index + 1 + }`, + value: baseColorName, + hasAlias: true, + } + : { + name: `light/${key}-stop-${ + index + 1 + }`, + value: hexToRgba( + getPaletteValue( + baseColorName + ), + alpha + ), + hasAlias: false, + }; + } + ); + } + + throw new Error( + `Unexpected color format for key: ${key}` + ); + }); const darkArray = Object.keys( parsedContent.dark - ).map((key) => ({ - name: key, - value: parsedContent.dark[key].value, - })); + ).flatMap((key) => { + const colorData = parsedContent.dark[key]; + const { value, type } = colorData; + + function getPaletteName(value) { + const regexMatch = value.match( + /{palette\.(.*?)}/ + ); + if (regexMatch) { + return regexMatch[1]; + } + const rgbaMatch = value.match( + /rgba\(\{palette\.(.*?)\},\s*\d*\.?\d*\)/ + ); + if (rgbaMatch) { + return rgbaMatch[1]; + } + throw new Error( + `Unexpected color format: ${value}` + ); + } + + function getPaletteValue(colorName) { + const paletteValue = + parsedContent.global.palette[colorName] + ?.value; + if (!paletteValue) { + throw new Error( + `Color ${colorName} not found in palette` + ); + } + return paletteValue; + } + + // Default color handling + if ( + typeof value === "string" && + !value.startsWith("rgba") + ) { + return { + name: `dark/${key}`, + value: getPaletteName(value), + hasAlias: true, + }; + } + + // Color with alpha (rgba) + if ( + typeof value === "string" && + value.startsWith("rgba") + ) { + const alphaMatch = value.match( + /rgba\([^)]+,\s*([^)]+)\)/ + ); + const alpha = alphaMatch + ? alphaMatch[1] + : "1"; + const baseColorName = + getPaletteName(value); + + // If alpha is 1, do nothing, otherwise replace with palette value + return alpha === "1" + ? { + name: `dark/${key}`, + value: baseColorName, + hasAlias: true, + } + : { + name: `dark/${key}`, + value: hexToRgba( + getPaletteValue(baseColorName), + alpha + ), + hasAlias: false, + }; + } + + // Gradient handling + if ( + type === "linear-gradient" && + typeof value === "object" + ) { + return value.colors.map( + (color, index) => { + const alphaMatch = color.value.match( + /rgba\([^)]+,\s*([^)]+)\)/ + ); + const alpha = alphaMatch + ? alphaMatch[1] + : "1"; + const baseColorName = getPaletteName( + color.value + ); + + // Check if the color has an alpha different than 1 + return alpha === "1" + ? { + name: `dark/${key}-stop-${ + index + 1 + }`, + value: baseColorName, + hasAlias: true, + } + : { + name: `dark/${key}-stop-${ + index + 1 + }`, + value: hexToRgba( + getPaletteValue( + baseColorName + ), + alpha + ), + hasAlias: false, + }; + } + ); + } + + throw new Error( + `Unexpected color format for key: ${key}` + ); + }); const paletteArray = Object.keys( parsedContent.global.palette ).map((key) => ({ name: key, - value: - parsedContent.global.palette[key].value, + value: hexToRgba( + parsedContent.global.palette[key].value + ), })); const radiusArray = Object.keys( diff --git a/tokens/figma/index.mjs b/tokens/figma/index.mjs index b7a8ccc33af..e7520b21145 100644 --- a/tokens/figma/index.mjs +++ b/tokens/figma/index.mjs @@ -71,7 +71,6 @@ async function fetchAndUpdateVariables( "font-size", "line-height", "radius", - "themeContext", ]; function generateTempId(name, collection) { @@ -112,6 +111,53 @@ async function fetchAndUpdateVariables( }); } + function getPaletteAlias( + variableName, + existingVariables, + existingCollections + ) { + // Step 1: Find the "palette" collection + const paletteCollection = Object.values( + existingCollections + ).find( + (collection) => + collection.name === "palette" + ); + + if (!paletteCollection) { + console.error( + "Palette collection not found." + ); + return null; + } + + // Step 2: Find the variable in the "palette" collection + const paletteVariable = Object.values( + existingVariables + ).find( + (variable) => + variable.variableCollectionId === + paletteCollection.id && + variable.name === variableName + ); + + if (!paletteVariable) { + console.error( + "Palette variable not found for name:", + variableName + ); + return null; + } + + // Step 3: Retrieve the mode ID for the "palette" collection + const modeId = paletteCollection.id; + + return { + variableId: paletteVariable.id, + modeId: modeId, + }; + } + function updateCollection( collectionName, existingCollections @@ -155,6 +201,7 @@ async function fetchAndUpdateVariables( function updateVariables( variableName, variableValue, + hasAlias, collectionName, existingVariables, existingCollections, @@ -170,6 +217,8 @@ async function fetchAndUpdateVariables( existingCollections ); + // Find the default mode for the collection + const existingMode = Object.values( existingCollections ).find( @@ -193,10 +242,16 @@ async function fetchAndUpdateVariables( action: "UPDATE", variableId: existingVariable.id, modeId: existingMode, - value: - variableType === "COLOR" - ? hexToRgba(variableValue) - : variableValue, + value: hasAlias + ? { + type: "VARIABLE_ALIAS", + id: getPaletteAlias( + variableValue, + existingVariables, + existingCollections + ).variableId, + } + : variableValue, }); allVariableNamesInCurrentData.add( variableName @@ -225,10 +280,16 @@ async function fetchAndUpdateVariables( action: "CREATE", variableId: tempId, modeId: existingMode, - value: - variableType === "COLOR" - ? hexToRgba(variableValue) - : variableValue, + value: hasAlias + ? { + type: "VARIABLE_ALIAS", + id: getPaletteAlias( + variableValue, + existingVariables, + existingCollections + ).variableId, + } + : variableValue, }); allVariableNamesInCurrentData.add( @@ -262,6 +323,18 @@ async function fetchAndUpdateVariables( new Set(); const variableGroups = [ + { + variables: jsonData[brand].light, + collectionName: "constants", + resolvedType: "COLOR", + variableScopes: ["ALL_SCOPES"], + }, + { + variables: jsonData[brand].dark, + collectionName: "constants", + resolvedType: "COLOR", + variableScopes: ["ALL_SCOPES"], + }, { variables: jsonData[brand].palette, collectionName: "palette", @@ -305,6 +378,7 @@ async function fetchAndUpdateVariables( updateVariables( variable.name, variable.value, + variable.hasAlias, collectionName, existingVariables, existingCollections, From a14d6af61d1e84559c112d618091f0ed07032e01 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Thu, 22 Aug 2024 14:50:29 +0200 Subject: [PATCH 10/71] Simplify logic --- tokens/figma/extractJsonData.mjs | 271 +++++++++---------------------- 1 file changed, 79 insertions(+), 192 deletions(-) diff --git a/tokens/figma/extractJsonData.mjs b/tokens/figma/extractJsonData.mjs index 5dd2d443e8f..db5db957c61 100644 --- a/tokens/figma/extractJsonData.mjs +++ b/tokens/figma/extractJsonData.mjs @@ -17,137 +17,14 @@ const extractJsonData = ( ); const parsedContent = JSON.parse(fileContent); - const lightArray = Object.keys( - parsedContent.light - ).flatMap((key) => { - const colorData = parsedContent.light[key]; - const { value, type } = colorData; - - function getPaletteName(value) { - const regexMatch = value.match( - /{palette\.(.*?)}/ - ); - if (regexMatch) { - return regexMatch[1]; - } - const rgbaMatch = value.match( - /rgba\(\{palette\.(.*?)\},\s*\d*\.?\d*\)/ - ); - if (rgbaMatch) { - return rgbaMatch[1]; - } + function processColors(parsedContent, theme) { + if (!["light", "dark"].includes(theme)) { throw new Error( - `Unexpected color format: ${value}` - ); - } - - function getPaletteValue(colorName) { - const paletteValue = - parsedContent.global.palette[colorName] - ?.value; - if (!paletteValue) { - throw new Error( - `Color ${colorName} not found in palette` - ); - } - return paletteValue; - } - - // Default color handling - if ( - typeof value === "string" && - !value.startsWith("rgba") - ) { - return { - name: `light/${key}`, - value: getPaletteName(value), - hasAlias: true, - }; - } - - // Color with alpha (rgba) - if ( - typeof value === "string" && - value.startsWith("rgba") - ) { - const alphaMatch = value.match( - /rgba\([^)]+,\s*([^)]+)\)/ - ); - const alpha = alphaMatch - ? alphaMatch[1] - : "1"; - const baseColorName = - getPaletteName(value); - - // If alpha is 1, do nothing, otherwise replace with palette value - return alpha === "1" - ? { - name: `light/${key}`, - value: baseColorName, - hasAlias: true, - } - : { - name: `light/${key}`, - value: hexToRgba( - getPaletteValue(baseColorName), - alpha - ), - hasAlias: false, - }; - } - - // Gradient handling - if ( - type === "linear-gradient" && - typeof value === "object" - ) { - return value.colors.map( - (color, index) => { - const alphaMatch = color.value.match( - /rgba\([^)]+,\s*([^)]+)\)/ - ); - const alpha = alphaMatch - ? alphaMatch[1] - : "1"; - const baseColorName = getPaletteName( - color.value - ); - - // Check if the color has an alpha different than 1 - return alpha === "1" - ? { - name: `light/${key}-stop-${ - index + 1 - }`, - value: baseColorName, - hasAlias: true, - } - : { - name: `light/${key}-stop-${ - index + 1 - }`, - value: hexToRgba( - getPaletteValue( - baseColorName - ), - alpha - ), - hasAlias: false, - }; - } + `Invalid theme: ${theme}. Expected 'light' or 'dark'.` ); } - throw new Error( - `Unexpected color format for key: ${key}` - ); - }); - - const darkArray = Object.keys( - parsedContent.dark - ).flatMap((key) => { - const colorData = parsedContent.dark[key]; - const { value, type } = colorData; + const themeColors = parsedContent[theme]; function getPaletteName(value) { const regexMatch = value.match( @@ -179,79 +56,45 @@ const extractJsonData = ( return paletteValue; } - // Default color handling - if ( - typeof value === "string" && - !value.startsWith("rgba") - ) { - return { - name: `dark/${key}`, - value: getPaletteName(value), - hasAlias: true, - }; - } - - // Color with alpha (rgba) - if ( - typeof value === "string" && - value.startsWith("rgba") - ) { - const alphaMatch = value.match( - /rgba\([^)]+,\s*([^)]+)\)/ - ); - const alpha = alphaMatch - ? alphaMatch[1] - : "1"; - const baseColorName = - getPaletteName(value); - - // If alpha is 1, do nothing, otherwise replace with palette value - return alpha === "1" - ? { - name: `dark/${key}`, - value: baseColorName, + return Object.keys(themeColors).flatMap( + (key) => { + const colorData = themeColors[key]; + const { value, type } = colorData; + + // Default color handling + if ( + typeof value === "string" && + !value.startsWith("rgba") + ) { + return { + name: `${theme}/${key}`, + value: getPaletteName(value), hasAlias: true, - } - : { - name: `dark/${key}`, - value: hexToRgba( - getPaletteValue(baseColorName), - alpha - ), - hasAlias: false, }; - } + } - // Gradient handling - if ( - type === "linear-gradient" && - typeof value === "object" - ) { - return value.colors.map( - (color, index) => { - const alphaMatch = color.value.match( + // Color with alpha (rgba) + if ( + typeof value === "string" && + value.startsWith("rgba") + ) { + const alphaMatch = value.match( /rgba\([^)]+,\s*([^)]+)\)/ ); const alpha = alphaMatch ? alphaMatch[1] : "1"; - const baseColorName = getPaletteName( - color.value - ); + const baseColorName = + getPaletteName(value); - // Check if the color has an alpha different than 1 return alpha === "1" ? { - name: `dark/${key}-stop-${ - index + 1 - }`, + name: `${theme}/${key}`, value: baseColorName, hasAlias: true, } : { - name: `dark/${key}-stop-${ - index + 1 - }`, + name: `${theme}/${key}`, value: hexToRgba( getPaletteValue( baseColorName @@ -261,13 +104,54 @@ const extractJsonData = ( hasAlias: false, }; } - ); - } - throw new Error( - `Unexpected color format for key: ${key}` + // Gradient handling + if ( + type === "linear-gradient" && + typeof value === "object" + ) { + return value.colors.map( + (color, index) => { + const alphaMatch = + color.value.match( + /rgba\([^)]+,\s*([^)]+)\)/ + ); + const alpha = alphaMatch + ? alphaMatch[1] + : "1"; + const baseColorName = + getPaletteName(color.value); + + return alpha === "1" + ? { + name: `${theme}/${key}-stop-${ + index + 1 + }`, + value: baseColorName, + hasAlias: true, + } + : { + name: `${theme}/${key}-stop-${ + index + 1 + }`, + value: hexToRgba( + getPaletteValue( + baseColorName + ), + alpha + ), + hasAlias: false, + }; + } + ); + } + + throw new Error( + `Unexpected color format for key: ${key}` + ); + } ); - }); + } const paletteArray = Object.keys( parsedContent.global.palette @@ -366,8 +250,11 @@ const extractJsonData = ( // Accumulate results accumulator[fileName] = { - light: lightArray, - dark: darkArray, + light: processColors( + parsedContent, + "light" + ), + dark: processColors(parsedContent, "dark"), palette: paletteArray, radius: radiusArray, fontWeight: fontWeightArray, From 536f4a16dfe0db8369dabcc15fdc81c51ed6fe43 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Thu, 22 Aug 2024 15:03:39 +0200 Subject: [PATCH 11/71] Fix alpha values to be a number instead of a string --- tokens/figma/extractJsonData.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tokens/figma/extractJsonData.mjs b/tokens/figma/extractJsonData.mjs index db5db957c61..ffb80ac8a9d 100644 --- a/tokens/figma/extractJsonData.mjs +++ b/tokens/figma/extractJsonData.mjs @@ -99,7 +99,7 @@ const extractJsonData = ( getPaletteValue( baseColorName ), - alpha + parseFloat(alpha) ), hasAlias: false, }; @@ -138,7 +138,7 @@ const extractJsonData = ( getPaletteValue( baseColorName ), - alpha + parseFloat(alpha) ), hasAlias: false, }; From 5f7364acebe18c4c37e1e4d2519af0fb0a079be8 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Fri, 23 Aug 2024 16:06:49 +0200 Subject: [PATCH 12/71] Refactor code and handle gradients --- tokens/figma/extractJsonData.mjs | 212 ++++++++++++---- tokens/figma/index.mjs | 417 ++++++++++++++++++++++++++----- tokens/figma/utils.mjs | 40 --- tokens/o2-new.json | 4 +- 4 files changed, 514 insertions(+), 159 deletions(-) diff --git a/tokens/figma/extractJsonData.mjs b/tokens/figma/extractJsonData.mjs index ffb80ac8a9d..14e7c6aa735 100644 --- a/tokens/figma/extractJsonData.mjs +++ b/tokens/figma/extractJsonData.mjs @@ -6,6 +6,28 @@ const extractJsonData = ( jsonFiles, directoryPath ) => { + const allParsedContent = {}; // Initialize once to store all parsed content + + // First pass to gather allParsedContent + jsonFiles.forEach((file) => { + const filePath = path.resolve( + directoryPath, + file + ); + const fileContent = fs.readFileSync( + filePath, + "utf8" + ); + const parsedContent = JSON.parse(fileContent); + + // Extract file name without extension to use as a key + const fileName = file.split(".")[0]; + + // Store the parsed content in the allParsedContent object + allParsedContent[fileName] = parsedContent; + }); + + // Second pass to process each file return jsonFiles.reduce((accumulator, file) => { const filePath = path.resolve( directoryPath, @@ -17,7 +39,14 @@ const extractJsonData = ( ); const parsedContent = JSON.parse(fileContent); - function processColors(parsedContent, theme) { + // Extract file name without extension + const fileName = file.split(".")[0]; + + function processColors( + parsedContent, + theme, + allParsedContent + ) { if (!["light", "dark"].includes(theme)) { throw new Error( `Invalid theme: ${theme}. Expected 'light' or 'dark'.` @@ -56,12 +85,135 @@ const extractJsonData = ( return paletteValue; } + function getMaxStopsAcrossBrands( + allParsedContent, + key, + theme + ) { + let maxStops = 0; + let isGradientInAnyBrand = false; + + Object.values(allParsedContent).forEach( + (content) => { + const colors = content[theme][key]; + if ( + colors && + colors.type === "linear-gradient" + ) { + isGradientInAnyBrand = true; + maxStops = Math.max( + maxStops, + colors.value.colors.length + ); + } + } + ); + + return { maxStops, isGradientInAnyBrand }; + } + return Object.keys(themeColors).flatMap( (key) => { const colorData = themeColors[key]; const { value, type } = colorData; - // Default color handling + const { + maxStops, + isGradientInAnyBrand, + } = getMaxStopsAcrossBrands( + allParsedContent, + key, + theme + ); + + // Handle gradients first + if ( + type === "linear-gradient" && + typeof value === "object" + ) { + // Map colors in gradient + return Array.from( + { length: maxStops }, + (_, index) => { + if (index < value.colors.length) { + // Use the actual gradient stop color + const color = + value.colors[index]; + const alphaMatch = + color.value.match( + /rgba\([^)]+,\s*([^)]+)\)/ + ); + const alpha = alphaMatch + ? alphaMatch[1] + : "1"; + const baseColorName = + getPaletteName(color.value); + + return alpha === "1" + ? { + name: `${theme}/${key}-stop-${ + index + 1 + }`, + value: baseColorName, + hasAlias: true, + } + : { + name: `${theme}/${key}-stop-${ + index + 1 + }`, + value: hexToRgba( + getPaletteValue( + baseColorName + ), + parseFloat(alpha) + ), + hasAlias: false, + }; + } else if (isGradientInAnyBrand) { + // If this brand does not have a gradient, repeat the base color to match stops + const baseColorName = + getPaletteName( + value.colors[0].value + ); + return { + name: `${theme}/${key}-stop-${ + index + 1 + }`, + value: hexToRgba( + getPaletteValue( + baseColorName + ) + ), + hasAlias: false, + }; + } + } + ).filter(Boolean); + } + + // Handle solid colors or aliases when a gradient exists in other brands + if ( + type !== "linear-gradient" && + isGradientInAnyBrand + ) { + const baseColorName = + getPaletteName(value); + // Repeat the solid color to match the gradient stops + return Array.from( + { length: maxStops }, + (_, index) => ({ + name: `${theme}/${key}-stop-${ + index + 1 + }`, + value: hexToRgba( + getPaletteValue(baseColorName) + ), + hasAlias: false, + }) + ); + } + + // Handle solid colors or aliases normally if ( typeof value === "string" && !value.startsWith("rgba") @@ -73,7 +225,7 @@ const extractJsonData = ( }; } - // Color with alpha (rgba) + // Handle rgba colors if ( typeof value === "string" && value.startsWith("rgba") @@ -105,47 +257,6 @@ const extractJsonData = ( }; } - // Gradient handling - if ( - type === "linear-gradient" && - typeof value === "object" - ) { - return value.colors.map( - (color, index) => { - const alphaMatch = - color.value.match( - /rgba\([^)]+,\s*([^)]+)\)/ - ); - const alpha = alphaMatch - ? alphaMatch[1] - : "1"; - const baseColorName = - getPaletteName(color.value); - - return alpha === "1" - ? { - name: `${theme}/${key}-stop-${ - index + 1 - }`, - value: baseColorName, - hasAlias: true, - } - : { - name: `${theme}/${key}-stop-${ - index + 1 - }`, - value: hexToRgba( - getPaletteValue( - baseColorName - ), - parseFloat(alpha) - ), - hasAlias: false, - }; - } - ); - } - throw new Error( `Unexpected color format for key: ${key}` ); @@ -153,6 +264,7 @@ const extractJsonData = ( ); } + // Other token processing logic const paletteArray = Object.keys( parsedContent.global.palette ).map((key) => ({ @@ -245,16 +357,18 @@ const extractJsonData = ( }; }); - // Extract file name without extension - const fileName = file.split(".")[0]; - // Accumulate results accumulator[fileName] = { light: processColors( parsedContent, - "light" + "light", + allParsedContent + ), + dark: processColors( + parsedContent, + "dark", + allParsedContent ), - dark: processColors(parsedContent, "dark"), palette: paletteArray, radius: radiusArray, fontWeight: fontWeightArray, diff --git a/tokens/figma/index.mjs b/tokens/figma/index.mjs index e7520b21145..2ab8f7dade8 100644 --- a/tokens/figma/index.mjs +++ b/tokens/figma/index.mjs @@ -4,8 +4,6 @@ import fs from "fs"; import path from "path"; import { fileURLToPath } from "url"; import extractJsonData from "./extractJsonData.mjs"; -import { hexToRgba } from "./utils.mjs"; -import { extractPaletteValue } from "./utils.mjs"; dotenv.config({ path: "../../.env" }); const __filename = fileURLToPath(import.meta.url); @@ -29,11 +27,282 @@ const jsonData = extractJsonData( ); const brands = { - blau: `https://api.figma.com/v1/files/${FILE_KEY_2}/variables`, + "o2-new": `https://api.figma.com/v1/files/${FILE_KEY_2}/variables`, "vivo-new": `https://api.figma.com/v1/files/${FILE_KEY_1}/variables`, }; -async function fetchAndUpdateVariables( +async function updateCollections(url) { + try { + const response = await fetch(`${url}/local`, { + method: "GET", + headers: { + "X-Figma-Token": FIGMA_TOKEN, + "Content-Type": "application/json", + }, + }); + + const figmaData = await response.json(); + + const newData = { + variableCollections: [], + variableModes: [], + variables: [], + variableModeValues: [], + }; + + const existingCollections = + figmaData.meta.variableCollections; + + const collectionNames = [ + "constants", + "palette", + "font-weight", + "font-size", + "line-height", + "radius", + ]; + + function generateTempId(name) { + return `tempId_${name}`; + } + + function updateCollection( + collectionName, + existingCollections + ) { + // Find the existing collection by name + const existingCollection = Object.values( + existingCollections + ).find( + (collection) => + collection.name === collectionName + ); + + if (existingCollection) { + // If the collection exists, update it + newData.variableCollections.push({ + action: "UPDATE", + id: existingCollection.id, + name: collectionName, + }); + } else { + // If the collection doesn't exist, create it + const tempId = generateTempId( + collectionName + ); + newData.variableCollections.push({ + action: "CREATE", + id: tempId, + name: collectionName, + }); + } + } + + // Process each collection name + collectionNames.forEach((collectionName) => { + updateCollection( + collectionName, + existingCollections + ); + }); + + // Return the processed data for further use + return newData; + } catch (error) { + console.error("Error:", error); + throw error; // rethrow the error to be handled later + } +} + +async function updatePalette( + jsonData, + brand, + url +) { + try { + const response = await fetch(`${url}/local`, { + method: "GET", + headers: { + "X-Figma-Token": FIGMA_TOKEN, + "Content-Type": "application/json", + }, + }); + + const figmaData = await response.json(); + + const existingVariables = + figmaData.meta.variables; + const existingCollections = + figmaData.meta.variableCollections; + + const newData = { + variableCollections: [], + variableModes: [], + variables: [], + variableModeValues: [], + }; + + function findVariableInCollection( + variableName, + collectionName, + existingVariables, + existingCollections + ) { + return Object.values( + existingVariables + ).find((variable) => { + // Check if the variable name matches + if (variable.name !== variableName) + return false; + + // Find the collection in existingCollections using variableCollectionId + const collection = Object.values( + existingCollections + ).find( + (col) => + col.id === + variable.variableCollectionId + ); + + // Check if the collection exists, its name matches the collectionName, + // and if the variable's id is listed in the collection's variableIds + return ( + collection && + collection.name === collectionName && + collection.variableIds.includes( + variable.id + ) + ); + }); + } + + function generateTempId(name, collection) { + return `tempId_${collection}_${name}`; + } + + const allVariableNamesInCurrentData = + new Set(); + + function updateVariables( + variableName, + variableValue, + collectionName, + existingVariables, + existingCollections, + variableType, + variableScopes, + allVariableNamesInCurrentData + ) { + // Find the existing variable by name + const existingVariable = + findVariableInCollection( + variableName, + collectionName, + existingVariables, + existingCollections + ); + + // Find the default mode for the collection + + const existingMode = Object.values( + existingCollections + ).find( + (collection) => + collection.name === collectionName + ).defaultModeId; + + if (existingVariable) { + // If the variable exists, update it + newData.variables.push({ + action: "UPDATE", + id: existingVariable.id, + name: variableName, + resolvedType: variableType, + variableCollectionId: + existingVariable.variableCollectionId, + scopes: variableScopes, + }); + + newData.variableModeValues.push({ + action: "UPDATE", + variableId: existingVariable.id, + modeId: existingMode, + value: variableValue, + }); + } else { + const tempId = generateTempId( + variableName, + collectionName + ); + + const collectionId = Object.values( + existingCollections + ).find( + (collection) => + collection.name === collectionName + ).id; + + newData.variables.push({ + action: "CREATE", + id: tempId, + name: variableName, + variableCollectionId: collectionId, + resolvedType: variableType, + scopes: variableScopes, + }); + + newData.variableModeValues.push({ + action: "CREATE", + variableId: tempId, + modeId: existingMode, + value: variableValue, + }); + + allVariableNamesInCurrentData.add( + variableName + ); + } + } + + const variableGroups = [ + { + variables: jsonData[brand].palette, + collectionName: "palette", + resolvedType: "COLOR", + variableScopes: ["ALL_SCOPES"], + }, + ]; + + variableGroups.forEach( + ({ + variables, + collectionName, + resolvedType, + variableScopes, + }) => { + variables.forEach((variable) => { + updateVariables( + variable.name, + variable.value, + collectionName, + existingVariables, + existingCollections, + resolvedType, + variableScopes, + allVariableNamesInCurrentData + ); + }); + } + ); + + return newData; + } catch (error) { + console.error("Error:", error); + throw error; // rethrow the error to be handled later + } +} + +async function updateVariables( jsonData, brand, url @@ -64,15 +333,6 @@ async function fetchAndUpdateVariables( variableModeValues: [], }; - const collectionNames = [ - "constants", - "palette", - "font-weight", - "font-size", - "line-height", - "radius", - ]; - function generateTempId(name, collection) { return `tempId_${collection}_${name}`; } @@ -158,46 +418,6 @@ async function fetchAndUpdateVariables( }; } - function updateCollection( - collectionName, - existingCollections - ) { - // Find the existing collection by name - const existingCollection = Object.values( - existingCollections - ).find( - (collection) => - collection.name === collectionName - ); - - if (existingCollection) { - // If the collection exists, update it - newData.variableCollections.push({ - action: "UPDATE", - id: existingCollection.id, - name: collectionName, - }); - } else { - // If the collection doesn't exist, create it - const tempId = generateTempId( - collectionName - ); - newData.variableCollections.push({ - action: "CREATE", - id: tempId, - name: collectionName, - }); - } - } - - // Process each collection name - collectionNames.forEach((collectionName) => { - updateCollection( - collectionName, - existingCollections - ); - }); - function updateVariables( variableName, variableValue, @@ -262,11 +482,14 @@ async function fetchAndUpdateVariables( variableName, collectionName ); - const collectionId = - newData.variableCollections.find( - (collection) => - collection.name === collectionName - ).id; + + const collectionId = Object.values( + existingCollections + ).find( + (collection) => + collection.name === collectionName + ).id; + newData.variables.push({ action: "CREATE", id: tempId, @@ -324,20 +547,20 @@ async function fetchAndUpdateVariables( const variableGroups = [ { - variables: jsonData[brand].light, - collectionName: "constants", + variables: jsonData[brand].palette, + collectionName: "palette", resolvedType: "COLOR", variableScopes: ["ALL_SCOPES"], }, { - variables: jsonData[brand].dark, + variables: jsonData[brand].light, collectionName: "constants", resolvedType: "COLOR", variableScopes: ["ALL_SCOPES"], }, { - variables: jsonData[brand].palette, - collectionName: "palette", + variables: jsonData[brand].dark, + collectionName: "constants", resolvedType: "COLOR", variableScopes: ["ALL_SCOPES"], }, @@ -405,9 +628,65 @@ async function fetchAndUpdateVariables( // Use an async function to handle the post request after data processing -async function processAndPostData(url, brand) { +async function postCollections(url, brand) { + try { + const newData = await updateCollections(url); + + const response = await fetch(url, { + method: "POST", + headers: { + "X-Figma-Token": FIGMA_TOKEN, + "Content-Type": "application/json", + }, + body: JSON.stringify(newData), + }); + + const data = await response.json(); + console.log( + `Success creating collections for brand ${brand}:`, + data + ); + } catch (error) { + console.error( + `Error creating collections for for brand ${brand}:`, + error + ); + } +} + +async function postPalette(url, brand) { + try { + const newData = await updatePalette( + jsonData, + brand, + url + ); + + const response = await fetch(url, { + method: "POST", + headers: { + "X-Figma-Token": FIGMA_TOKEN, + "Content-Type": "application/json", + }, + body: JSON.stringify(newData), + }); + + const data = await response.json(); + console.log( + `Success updating palette for brand ${brand}:`, + data + ); + } catch (error) { + console.error( + `Error updating palette for brand ${brand}:`, + error + ); + } +} + +async function postVariables(url, brand) { try { - const newData = await fetchAndUpdateVariables( + const newData = await updateVariables( jsonData, brand, url @@ -424,12 +703,12 @@ async function processAndPostData(url, brand) { const data = await response.json(); console.log( - `Success for brand ${brand}:`, + `Success updating variables for brand ${brand}:`, data ); } catch (error) { console.error( - `Error posting data for brand ${brand}:`, + `Error updating variables for brand ${brand}:`, error ); } @@ -441,7 +720,9 @@ async function processAllUrls(brands) { for (const [brand, url] of Object.entries( brands )) { - await processAndPostData(url, brand); + await postCollections(url, brand); + await postPalette(url, brand); + await postVariables(url, brand); } } diff --git a/tokens/figma/utils.mjs b/tokens/figma/utils.mjs index 35e7a69bf68..7f49cd36f2c 100644 --- a/tokens/figma/utils.mjs +++ b/tokens/figma/utils.mjs @@ -24,43 +24,3 @@ export function hexToRgba(hex, alpha = 1) { a: alpha, }; } - -export function extractPaletteValue(constant) { - // Convert value to a string if it's not already - const valueStr = String(constant); - - // Default values - let value = ""; - let alpha = 0; - - // Check if the value is an 'rgba' format - if (valueStr.startsWith("rgba")) { - const rgbaMatch = valueStr.match( - /rgba\(\{([^}]+)}\s*,\s*(\d+\.?\d*)\)/ - ); - if (rgbaMatch) { - value = rgbaMatch[1].replace( - /^palette\./, - "" - ); // Remove 'palette.' prefix - alpha = parseFloat(rgbaMatch[2]); - } - } else { - // Extract palette name from curly braces - const paletteMatch = - valueStr.match(/\{([^}]+)\}/); - if (paletteMatch) { - // Remove the 'palette.' prefix if present - const fullPaletteName = paletteMatch[1]; - value = fullPaletteName.replace( - /^palette\./, - "" - ); - } - } - - return { - value, - alpha, - }; -} diff --git a/tokens/o2-new.json b/tokens/o2-new.json index b67ac957dd3..e4e2ba99d5a 100644 --- a/tokens/o2-new.json +++ b/tokens/o2-new.json @@ -118,9 +118,9 @@ "description": "darkBlue" }, "backgroundBrandBottom": { - "value": "{palette.beyondBlue45}", + "value": "{palette.beyondBlue}", "type": "color", - "description": "beyondBlue45" + "description": "beyondBlue" }, "appBarBackground": { "value": "{palette.white}", From c0981d987f2402b35b37348492528d42e73db5ba Mon Sep 17 00:00:00 2001 From: "Ignacio Ceballos (Yayo)" Date: Wed, 28 Aug 2024 19:03:49 +0200 Subject: [PATCH 13/71] improve file management --- tokens/figma/index.mjs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/tokens/figma/index.mjs b/tokens/figma/index.mjs index 2ab8f7dade8..3be3f2af700 100644 --- a/tokens/figma/index.mjs +++ b/tokens/figma/index.mjs @@ -9,10 +9,17 @@ dotenv.config({ path: "../../.env" }); const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); -const FILE_KEY_1 = process.env.FILE_KEY_1; -const FILE_KEY_2 = process.env.FILE_KEY_2; const FIGMA_TOKEN = process.env.FIGMA_TOKEN; +const FILE_KEYS = { + movistar: process.env.MOVISTAR_FILE_KEY, + "o2-new": process.env.O2_NEW_FILE_KEY, + "vivo-new": process.env.VIVO_NEW_FILE_KEY, + telefonica: process.env.TELEFONICA_FILE_KEY, + blau: process.env.BLAU_FILE_KEY, + tu: process.env.TU_FILE_KEY, +}; + const tokensPath = path.resolve(__dirname, "../"); const files = fs.readdirSync(tokensPath); @@ -26,10 +33,14 @@ const jsonData = extractJsonData( tokensPath ); -const brands = { - "o2-new": `https://api.figma.com/v1/files/${FILE_KEY_2}/variables`, - "vivo-new": `https://api.figma.com/v1/files/${FILE_KEY_1}/variables`, -}; +const brands = Object.fromEntries( + Object.entries(FILE_KEYS).map( + ([brand, FILE_KEY]) => [ + brand, + `https://api.figma.com/v1/files/${FILE_KEY}/variables`, + ] + ) +); async function updateCollections(url) { try { From 5daa54d8f4f5d0c5fd35e8f9cb887e084634a486 Mon Sep 17 00:00:00 2001 From: "Ignacio Ceballos (Yayo)" Date: Wed, 28 Aug 2024 20:06:27 +0200 Subject: [PATCH 14/71] ux scripting + workflow --- .github/workflows/sync-figma-brands.yml | 61 +++++++++++++++++++++++++ tokens/figma/index.mjs | 34 +++++++++++--- 2 files changed, 89 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/sync-figma-brands.yml diff --git a/.github/workflows/sync-figma-brands.yml b/.github/workflows/sync-figma-brands.yml new file mode 100644 index 00000000000..7087efb985f --- /dev/null +++ b/.github/workflows/sync-figma-brands.yml @@ -0,0 +1,61 @@ +name: Update brands variables in Figma + +on: + pull_request: + types: + - closed + branches: + - main + paths: + - "tokens/*.json" + workflow_dispatch: + inputs: + brand: + description: "Select the brand to update variables in Figma" + required: true + default: "all" + type: choice + options: + - all + - movistar + - o2-new + - vivo-new + - telefonica + - blau + - tu + +jobs: + sync-figma-brands: + runs-on: ubuntu-latest + + env: + # Remember to sync these with the index.mjs file + FIGMA_TOKEN: ${{ secrets.FIGMA_TOKEN }} + MOVISTAR_FILE_KEY: ${{ secrets.MOVISTAR_FILE_KEY }} + O2_NEW_FILE_KEY: ${{ secrets.O2_NEW_FILE_KEY }} + VIVO_NEW_FILE_KEY: ${{ secrets.VIVO_NEW_FILE_KEY }} + TELEFONICA_FILE_KEY: ${{ secrets.TELEFONICA_FILE_KEY }} + BLAU_FILE_KEY: ${{ secrets.BLAU_FILE_KEY }} + TU_FILE_KEY: ${{ secrets.TU_FILE_KEY }} + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: "16" + + - name: Install npm dependencies + run: npm install + + - name: Sync ${{ github.event.inputs.brand }} + working-directory: tokens/figma + run: | + node index.mjs ${{ github.event.inputs.brand }} + + # - name: Sync Mística Skins + # working-directory: tokens/figma + # run: | + # node index.mjs ${{ github.event.inputs.brand }} diff --git a/tokens/figma/index.mjs b/tokens/figma/index.mjs index 3be3f2af700..1b40a935505 100644 --- a/tokens/figma/index.mjs +++ b/tokens/figma/index.mjs @@ -12,6 +12,7 @@ const __dirname = path.dirname(__filename); const FIGMA_TOKEN = process.env.FIGMA_TOKEN; const FILE_KEYS = { + // Remember to sync these with the workflow file movistar: process.env.MOVISTAR_FILE_KEY, "o2-new": process.env.O2_NEW_FILE_KEY, "vivo-new": process.env.VIVO_NEW_FILE_KEY, @@ -725,16 +726,37 @@ async function postVariables(url, brand) { } } -//process the data for an array of urls +// Process data for a specific brand -async function processAllUrls(brands) { +async function processBrand(brand, url) { + await postCollections(url, brand); + await postPalette(url, brand); + await postVariables(url, brand); +} + +// Process all brands + +async function processAllBrands(brands) { for (const [brand, url] of Object.entries( brands )) { - await postCollections(url, brand); - await postPalette(url, brand); - await postVariables(url, brand); + await processBrand(brand, url); } } -processAllUrls(brands); +// Get the selected brand from command line arguments + +const selectedBrand = process.argv[2]; + +if (selectedBrand === "all") { + processAllBrands(brands); +} else { + const url = brands[selectedBrand]; + if (url) { + processBrand(selectedBrand, url); + } else { + console.error( + `Brand ${selectedBrand} not found.` + ); + } +} From fe9d6b35529d91554af99a7f64f80eb12338bc8a Mon Sep 17 00:00:00 2001 From: "Ignacio Ceballos (Yayo)" Date: Wed, 28 Aug 2024 20:10:51 +0200 Subject: [PATCH 15/71] rename files --- .../workflows/{sync-figma-brands.yml => tokens-to-figma.yml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{sync-figma-brands.yml => tokens-to-figma.yml} (97%) diff --git a/.github/workflows/sync-figma-brands.yml b/.github/workflows/tokens-to-figma.yml similarity index 97% rename from .github/workflows/sync-figma-brands.yml rename to .github/workflows/tokens-to-figma.yml index 7087efb985f..54130821bd0 100644 --- a/.github/workflows/sync-figma-brands.yml +++ b/.github/workflows/tokens-to-figma.yml @@ -1,4 +1,4 @@ -name: Update brands variables in Figma +name: Sync tokens to Figma on: pull_request: From 7d4e58ea65f0b586eb237b2ad308b55e51e589bf Mon Sep 17 00:00:00 2001 From: "Ignacio Ceballos (Yayo)" Date: Thu, 29 Aug 2024 13:49:43 +0200 Subject: [PATCH 16/71] testing workflow --- .github/workflows/test.yml | 10 +++++++--- tokens/figma/test.mjs | 13 +++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 tokens/figma/test.mjs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9a439d94279..1309e603559 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,8 +4,12 @@ on: push: paths: - "tokens/movistar.json" - - "tokens/vivo.json" + - "tokens/vivo-new.json" + - "tokens/o2-new.json" - "tokens/telefonica.json" + - "tokens/blau.json" + - "tokens/tu.json" + workflow_dispatch: inputs: movistar: @@ -28,7 +32,7 @@ on: default: true jobs: - sync-figma-brand: + test: runs-on: ubuntu-latest env: @@ -80,5 +84,5 @@ jobs: run: | for brand in $BRAND_NAME; do echo "Syncing brand: $brand" - node index.mjs "$brand" + node ./tokens/figma/test.mjs "$brand" done diff --git a/tokens/figma/test.mjs b/tokens/figma/test.mjs new file mode 100644 index 00000000000..b5096ac6840 --- /dev/null +++ b/tokens/figma/test.mjs @@ -0,0 +1,13 @@ +// index.mjs + +// Captura el argumento de la marca pasado al script +const brand = process.argv[2]; + +// Verifica si se ha pasado un argumento +if (!brand) { + console.error("Error: No brand provided."); + process.exit(1); +} + +// Muestra un mensaje en la consola con el nombre de la marca +console.log(`Syncing tokens for brand: ${brand}`); From 0cb76026af6f8ee07a5a89d699fad30b6c673264 Mon Sep 17 00:00:00 2001 From: "Ignacio Ceballos (Yayo)" Date: Thu, 29 Aug 2024 13:51:05 +0200 Subject: [PATCH 17/71] Update test.yml --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1309e603559..b059e373fa0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -84,5 +84,5 @@ jobs: run: | for brand in $BRAND_NAME; do echo "Syncing brand: $brand" - node ./tokens/figma/test.mjs "$brand" + node test.mjs "$brand" done From 97dc0d45e141e1489e56a327ba99687ec3286e29 Mon Sep 17 00:00:00 2001 From: "Ignacio Ceballos (Yayo)" Date: Thu, 29 Aug 2024 13:52:33 +0200 Subject: [PATCH 18/71] Update test.yml --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b059e373fa0..a08a9b64119 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -32,7 +32,7 @@ on: default: true jobs: - test: + sync-figma-brand: runs-on: ubuntu-latest env: From 1b5c105d9ce75767ca8ade0c4b79014036083d3c Mon Sep 17 00:00:00 2001 From: "Ignacio Ceballos (Yayo)" Date: Thu, 29 Aug 2024 13:56:27 +0200 Subject: [PATCH 19/71] Update test.yml --- .github/workflows/test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a08a9b64119..560eaff847a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,6 +2,8 @@ name: Sync tokens to Figma on: push: + branches: + - production paths: - "tokens/movistar.json" - "tokens/vivo-new.json" From cf217ee0098b43c7979f8ada77c776d7d68e8a53 Mon Sep 17 00:00:00 2001 From: "Ignacio Ceballos (Yayo)" Date: Fri, 30 Aug 2024 00:22:44 +0200 Subject: [PATCH 20/71] rename file --- .github/workflows/{test.yml => sync-figma-tokens.yml} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename .github/workflows/{test.yml => sync-figma-tokens.yml} (98%) diff --git a/.github/workflows/test.yml b/.github/workflows/sync-figma-tokens.yml similarity index 98% rename from .github/workflows/test.yml rename to .github/workflows/sync-figma-tokens.yml index 560eaff847a..c2848797ec5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/sync-figma-tokens.yml @@ -2,8 +2,8 @@ name: Sync tokens to Figma on: push: - branches: - - production + # branches: + # - production paths: - "tokens/movistar.json" - "tokens/vivo-new.json" From d6e9db7353852417133893ce8fecbbc68fad78aa Mon Sep 17 00:00:00 2001 From: "Ignacio Ceballos (Yayo)" Date: Fri, 30 Aug 2024 00:22:58 +0200 Subject: [PATCH 21/71] Update sync-figma-tokens.yml --- .github/workflows/sync-figma-tokens.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/sync-figma-tokens.yml b/.github/workflows/sync-figma-tokens.yml index c2848797ec5..ebdc9b71691 100644 --- a/.github/workflows/sync-figma-tokens.yml +++ b/.github/workflows/sync-figma-tokens.yml @@ -6,6 +6,7 @@ on: # - production paths: - "tokens/movistar.json" + - "tokens/movistar2.json" - "tokens/vivo-new.json" - "tokens/o2-new.json" - "tokens/telefonica.json" From 83c1fde2d5cda80e611f6f1d7407469c99efa45b Mon Sep 17 00:00:00 2001 From: "Ignacio Ceballos (Yayo)" Date: Fri, 30 Aug 2024 00:23:19 +0200 Subject: [PATCH 22/71] Create movistar2.json --- tokens/movistar2.json | 1854 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1854 insertions(+) create mode 100644 tokens/movistar2.json diff --git a/tokens/movistar2.json b/tokens/movistar2.json new file mode 100644 index 00000000000..e4623ad8dee --- /dev/null +++ b/tokens/movistar2.json @@ -0,0 +1,1854 @@ +{ + "light": { + "background": { + "value": "{palette.white2}", + "type": "color", + "description": "white" + }, + "backgroundAlternative": { + "value": "{palette.grey1}", + "type": "color", + "description": "grey1" + }, + "backgroundBrand": { + "value": "{palette.movistarBlue}", + "type": "color", + "description": "movistarBlue" + }, + "backgroundBrandSecondary": { + "value": "{palette.movistarBlueDark}", + "type": "color", + "description": "movistarBlueDark" + }, + "backgroundContainer": { + "value": "{palette.white}", + "type": "color", + "description": "white" + }, + "backgroundContainerError": { + "value": "{palette.pepper10}", + "type": "color", + "description": "pepper10" + }, + "backgroundContainerHover": { + "value": "rgba({palette.black}, 0.03)", + "type": "color", + "description": "black" + }, + "backgroundContainerPressed": { + "value": "rgba({palette.black}, 0.05)", + "type": "color", + "description": "black" + }, + "backgroundContainerBrand": { + "value": "{palette.movistarBlue}", + "type": "color", + "description": "movistarBlue" + }, + "backgroundContainerBrandHover": { + "value": "rgba({palette.black}, 0.1)", + "type": "color", + "description": "black" + }, + "backgroundContainerBrandPressed": { + "value": "rgba({palette.black}, 0.2)", + "type": "color", + "description": "black" + }, + "backgroundContainerBrandOverInverse": { + "value": "{palette.movistarBlue55}", + "type": "color", + "description": "movistarBlue55" + }, + "backgroundContainerAlternative": { + "value": "{palette.grey1}", + "type": "color", + "description": "grey1" + }, + "backgroundOverlay": { + "value": "rgba({palette.movistarBlueDark}, 0.6)", + "type": "color", + "description": "movistarBlueDark" + }, + "backgroundSkeleton": { + "value": "{palette.grey2}", + "type": "color", + "description": "grey2" + }, + "backgroundSkeletonInverse": { + "value": "{palette.movistarBlue55}", + "type": "color", + "description": "movistarBlue55" + }, + "backgroundBrandTop": { + "value": "{palette.movistarBlue}", + "type": "color", + "description": "movistarBlue" + }, + "backgroundBrandBottom": { + "value": "{palette.movistarBlue}", + "type": "color", + "description": "movistarBlue" + }, + "appBarBackground": { + "value": "{palette.white}", + "type": "color", + "description": "white" + }, + "navigationBarBackground": { + "value": "{palette.movistarBlue}", + "type": "color", + "description": "movistarBlue" + }, + "skeletonWave": { + "value": "{palette.grey2}", + "type": "color", + "description": "grey2" + }, + "borderLow": { + "value": "{palette.grey1}", + "type": "color", + "description": "grey1" + }, + "border": { + "value": "{palette.grey3}", + "type": "color", + "description": "grey3" + }, + "borderHigh": { + "value": "{palette.grey5}", + "type": "color", + "description": "grey5" + }, + "borderSelected": { + "value": "{palette.movistarBlue}", + "type": "color", + "description": "movistarBlue" + }, + "coverBackgroundHover": { + "value": "rgba({palette.black}, 0.25)", + "type": "color", + "description": "black" + }, + "coverBackgroundPressed": { + "value": "rgba({palette.black}, 0.35)", + "type": "color", + "description": "black" + }, + "buttonDangerBackground": { + "value": "{palette.pepper55}", + "type": "color", + "description": "pepper55" + }, + "buttonDangerBackgroundPressed": { + "value": "{palette.pepper70}", + "type": "color", + "description": "pepper70" + }, + "buttonDangerBackgroundHover": { + "value": "{palette.pepper65}", + "type": "color", + "description": "pepper65" + }, + "buttonLinkDangerBackgroundPressed": { + "value": "{palette.pepper10}", + "type": "color", + "description": "pepper10" + }, + "buttonLinkDangerBackgroundInverse": { + "value": "{palette.white}", + "type": "color", + "description": "white" + }, + "buttonLinkDangerBackgroundInversePressed": { + "value": "{palette.pepper10}", + "type": "color", + "description": "pepper10" + }, + "buttonLinkBackgroundPressed": { + "value": "{palette.movistarBlue10}", + "type": "color", + "description": "movistarBlue10" + }, + "buttonLinkBackgroundInversePressed": { + "value": "rgba({palette.white}, 0.2)", + "type": "color", + "description": "white" + }, + "buttonPrimaryBackground": { + "value": "{palette.movistarBlueHC}", + "type": "color", + "description": "movistarBlueHC" + }, + "buttonPrimaryBackgroundInverse": { + "value": "{palette.white}", + "type": "color", + "description": "white" + }, + "buttonPrimaryBackgroundPressed": { + "value": "{palette.movistarBlueHC65}", + "type": "color", + "description": "movistarBlueHC65" + }, + "buttonPrimaryBackgroundHover": { + "value": "{palette.movistarBlueHC55}", + "type": "color", + "description": "movistarBlueHC55" + }, + "buttonPrimaryBackgroundInversePressed": { + "value": "{palette.movistarBlue10}", + "type": "color", + "description": "movistarBlue10" + }, + "buttonSecondaryBorder": { + "value": "{palette.movistarBlueHC}", + "type": "color", + "description": "movistarBlueHC" + }, + "buttonSecondaryBorderPressed": { + "value": "{palette.movistarBlueHC65}", + "type": "color", + "description": "movistarBlueHC65" + }, + "buttonSecondaryBackgroundHover": { + "value": "{palette.movistarBlue10}", + "type": "color", + "description": "movistarBlue10" + }, + "buttonSecondaryBackgroundPressed": { + "value": "{palette.movistarBlue15}", + "type": "color", + "description": "movistarBlue15" + }, + "buttonSecondaryBorderInverse": { + "value": "{palette.white}", + "type": "color", + "description": "white" + }, + "buttonSecondaryBorderInversePressed": { + "value": "{palette.white}", + "type": "color", + "description": "white" + }, + "buttonSecondaryBackgroundInverseHover": { + "value": "rgba({palette.white}, 0.2)", + "type": "color", + "description": "white" + }, + "buttonSecondaryBackgroundInversePressed": { + "value": "rgba({palette.white}, 0.3)", + "type": "color", + "description": "white" + }, + "textButtonPrimary": { + "value": "{palette.white}", + "type": "color", + "description": "white" + }, + "textButtonPrimaryInverse": { + "value": "{palette.movistarBlueHC}", + "type": "color", + "description": "movistarBlueHC" + }, + "textButtonPrimaryInversePressed": { + "value": "{palette.movistarBlueHC}", + "type": "color", + "description": "movistarBlueHC" + }, + "textButtonSecondary": { + "value": "{palette.movistarBlueHC}", + "type": "color", + "description": "movistarBlueHC" + }, + "textButtonSecondaryPressed": { + "value": "{palette.movistarBlueHC55}", + "type": "color", + "description": "movistarBlueHC55" + }, + "textButtonSecondaryInverse": { + "value": "{palette.white}", + "type": "color", + "description": "white" + }, + "textButtonSecondaryInversePressed": { + "value": "{palette.white}", + "type": "color", + "description": "white" + }, + "textLink": { + "value": "{palette.movistarBlueHC}", + "type": "color", + "description": "movistarBlueHC" + }, + "textLinkInverse": { + "value": "{palette.white}", + "type": "color", + "description": "white" + }, + "textLinkDanger": { + "value": "{palette.pepper60}", + "type": "color", + "description": "pepper60" + }, + "textLinkSnackbar": { + "value": "{palette.movistarBlue30}", + "type": "color", + "description": "movistarBlue30" + }, + "textActivated": { + "value": "{palette.movistarBlueHC}", + "type": "color", + "description": "movistarBlueHC" + }, + "textBrand": { + "value": "{palette.movistarBlueHC}", + "type": "color", + "description": "movistarBlueHC" + }, + "control": { + "value": "{palette.grey4}", + "type": "color", + "description": "grey4" + }, + "controlActivated": { + "value": "{palette.movistarBlue}", + "type": "color", + "description": "movistarBlue" + }, + "controlInverse": { + "value": "{palette.movistarBlue20}", + "type": "color", + "description": "movistarBlue20" + }, + "controlActivatedInverse": { + "value": "{palette.white}", + "type": "color", + "description": "white" + }, + "controlError": { + "value": "{palette.pepper55}", + "type": "color", + "description": "pepper55" + }, + "barTrack": { + "value": "{palette.grey3}", + "type": "color", + "description": "grey3" + }, + "loadingBar": { + "value": "{palette.movistarBlue}", + "type": "color", + "description": "movistarBlue" + }, + "loadingBarBackground": { + "value": "{palette.grey2}", + "type": "color", + "description": "grey2" + }, + "toggleAndroidInactive": { + "value": "{palette.grey2}", + "type": "color", + "description": "grey2" + }, + "toggleAndroidBackgroundActive": { + "value": "{palette.movistarBlue20}", + "type": "color", + "description": "movistarBlue20" + }, + "iosControlKnob": { + "value": "{palette.white}", + "type": "color", + "description": "white" + }, + "divider": { + "value": "{palette.grey3}", + "type": "color", + "description": "grey3" + }, + "dividerInverse": { + "value": "rgba({palette.white}, 0.2)", + "type": "color", + "description": "white" + }, + "navigationBarDivider": { + "value": "{palette.movistarBlue}", + "type": "color", + "description": "movistarBlue" + }, + "badge": { + "value": "{palette.pepper55}", + "type": "color", + "description": "pepper55" + }, + "feedbackErrorBackground": { + "value": "{palette.pepper55}", + "type": "color", + "description": "pepper55" + }, + "feedbackInfoBackground": { + "value": "{palette.movistarBlueDark}", + "type": "color", + "description": "movistarBlueDark" + }, + "brand": { + "value": "{palette.movistarBlue}", + "type": "color", + "description": "movistarBlue" + }, + "brandHigh": { + "value": "{palette.movistarBlue55}", + "type": "color", + "description": "movistarBlue55" + }, + "inverse": { + "value": "{palette.white}", + "type": "color", + "description": "white" + }, + "neutralHigh": { + "value": "{palette.movistarBlueDark}", + "type": "color", + "description": "movistarBlueDark" + }, + "neutralMedium": { + "value": "{palette.grey5}", + "type": "color", + "description": "grey5" + }, + "neutralMediumInverse": { + "value": "{palette.grey5}", + "type": "color", + "description": "grey5" + }, + "neutralLow": { + "value": "{palette.grey1}", + "type": "color", + "description": "grey1" + }, + "neutralLowAlternative": { + "value": "{palette.grey2}", + "type": "color", + "description": "grey2" + }, + "textPrimary": { + "value": "{palette.movistarBlueDark}", + "type": "color", + "description": "movistarBlueDark" + }, + "textPrimaryInverse": { + "value": "{palette.white}", + "type": "color", + "description": "white" + }, + "textSecondary": { + "value": "{palette.grey5}", + "type": "color", + "description": "grey5" + }, + "textSecondaryInverse": { + "value": "{palette.movistarBlue10}", + "type": "color", + "description": "movistarBlue10" + }, + "success": { + "value": "{palette.movistarGreen55}", + "type": "color", + "description": "movistarGreen55" + }, + "warning": { + "value": "{palette.egg55}", + "type": "color", + "description": "egg55" + }, + "error": { + "value": "{palette.pepper55}", + "type": "color", + "description": "pepper55" + }, + "textError": { + "value": "{palette.pepper55}", + "type": "color", + "description": "pepper55" + }, + "textErrorInverse": { + "value": "{palette.white}", + "type": "color", + "description": "white" + }, + "promo": { + "value": "{palette.purple}", + "type": "color", + "description": "purple" + }, + "highlight": { + "value": "{palette.pink55}", + "type": "color", + "description": "pink55" + }, + "successLow": { + "value": "{palette.movistarGreen10}", + "type": "color", + "description": "movistarGreen10" + }, + "warningLow": { + "value": "{palette.egg10}", + "type": "color", + "description": "egg10" + }, + "errorLow": { + "value": "{palette.pepper10}", + "type": "color", + "description": "pepper10" + }, + "promoLow": { + "value": "{palette.purple10}", + "type": "color", + "description": "purple10" + }, + "brandLow": { + "value": "{palette.movistarBlue10}", + "type": "color", + "description": "movistarBlue10" + }, + "successHigh": { + "value": "{palette.movistarGreen70}", + "type": "color", + "description": "movistarGreen70" + }, + "warningHigh": { + "value": "{palette.egg80}", + "type": "color", + "description": "egg80" + }, + "errorHigh": { + "value": "{palette.pepper70}", + "type": "color", + "description": "pepper70" + }, + "promoHigh": { + "value": "{palette.purple70}", + "type": "color", + "description": "purple70" + }, + "successHighInverse": { + "value": "{palette.movistarGreen70}", + "type": "color", + "description": "movistarGreen70" + }, + "warningHighInverse": { + "value": "{palette.egg80}", + "type": "color", + "description": "egg80" + }, + "errorHighInverse": { + "value": "{palette.pepper70}", + "type": "color", + "description": "pepper70" + }, + "promoHighInverse": { + "value": "{palette.purple70}", + "type": "color", + "description": "purple70" + }, + "textNavigationBarPrimary": { + "value": "{palette.white}", + "type": "color", + "description": "white" + }, + "textNavigationBarSecondary": { + "value": "{palette.movistarBlue20}", + "type": "color", + "description": "movistarBlue20" + }, + "textNavigationSearchBarHint": { + "value": "{palette.movistarBlue20}", + "type": "color", + "description": "movistarBlue20" + }, + "textNavigationSearchBarText": { + "value": "{palette.white}", + "type": "color", + "description": "white" + }, + "textAppBar": { + "value": "{palette.grey5}", + "type": "color", + "description": "grey5" + }, + "textAppBarSelected": { + "value": "{palette.movistarBlueHC}", + "type": "color", + "description": "movistarBlueHC" + }, + "customTabsBackground": { + "value": "{palette.white}", + "type": "color", + "description": "white" + }, + "tagTextPromo": { + "value": "{palette.purple70}", + "type": "color", + "description": "purple70" + }, + "tagTextActive": { + "value": "{palette.movistarBlueHC}", + "type": "color", + "description": "movistarBlueHC" + }, + "tagTextInactive": { + "value": "{palette.grey5}", + "type": "color", + "description": "grey5" + }, + "tagTextSuccess": { + "value": "{palette.movistarGreen70}", + "type": "color", + "description": "movistarGreen70" + }, + "tagTextWarning": { + "value": "{palette.egg80}", + "type": "color", + "description": "egg80" + }, + "tagTextError": { + "value": "{palette.pepper70}", + "type": "color", + "description": "pepper70" + }, + "tagBackgroundPromo": { + "value": "{palette.purple10}", + "type": "color", + "description": "purple10" + }, + "tagBackgroundActive": { + "value": "{palette.movistarBlue10}", + "type": "color", + "description": "movistarBlue10" + }, + "tagBackgroundInactive": { + "value": "{palette.grey1}", + "type": "color", + "description": "grey1" + }, + "tagBackgroundSuccess": { + "value": "{palette.movistarGreen10}", + "type": "color", + "description": "movistarGreen10" + }, + "tagBackgroundWarning": { + "value": "{palette.egg10}", + "type": "color", + "description": "egg10" + }, + "tagBackgroundError": { + "value": "{palette.pepper10}", + "type": "color", + "description": "pepper10" + }, + "cardContentOverlay": { + "type": "linear-gradient", + "value": { + "angle": 180, + "colors": [ + { + "value": "rgba({palette.black}, 0)", + "stop": 0 + }, + { + "value": "rgba({palette.black}, 0.4)", + "stop": 0.3 + }, + { + "value": "rgba({palette.black}, 0.7)", + "stop": 1 + } + ] + }, + "description": "black" + } + }, + "dark": { + "background": { + "value": "{palette.darkModeBlack}", + "type": "color", + "description": "darkModeBlack" + }, + "backgroundAlternative": { + "value": "{palette.darkModeBlack}", + "type": "color", + "description": "darkModeBlack" + }, + "backgroundBrand": { + "value": "{palette.darkModeBlack}", + "type": "color", + "description": "darkModeBlack" + }, + "backgroundBrandSecondary": { + "value": "{palette.darkModeBlack}", + "type": "color", + "description": "darkModeBlack" + }, + "backgroundContainer": { + "value": "{palette.darkModeGrey}", + "type": "color", + "description": "darkModeGrey" + }, + "backgroundContainerError": { + "value": "{palette.darkModeGrey}", + "type": "color", + "description": "darkModeGrey" + }, + "backgroundContainerHover": { + "value": "rgba({palette.white}, 0.03)", + "type": "color", + "description": "white" + }, + "backgroundContainerPressed": { + "value": "rgba({palette.white}, 0.05)", + "type": "color", + "description": "white" + }, + "backgroundContainerBrand": { + "value": "{palette.darkModeGrey}", + "type": "color", + "description": "darkModeGrey" + }, + "backgroundContainerBrandHover": { + "value": "rgba({palette.white}, 0.03)", + "type": "color", + "description": "white" + }, + "backgroundContainerBrandPressed": { + "value": "rgba({palette.white}, 0.05)", + "type": "color", + "description": "white" + }, + "backgroundContainerBrandOverInverse": { + "value": "{palette.darkModeGrey}", + "type": "color", + "description": "darkModeGrey" + }, + "backgroundContainerAlternative": { + "value": "{palette.darkModeGrey}", + "type": "color", + "description": "darkModeGrey" + }, + "backgroundOverlay": { + "value": "rgba({palette.darkModeGrey}, 0.8)", + "type": "color", + "description": "darkModeGrey" + }, + "backgroundSkeleton": { + "value": "{palette.darkModeGrey6}", + "type": "color", + "description": "darkModeGrey6" + }, + "backgroundSkeletonInverse": { + "value": "{palette.darkModeGrey6}", + "type": "color", + "description": "darkModeGrey6" + }, + "backgroundBrandTop": { + "value": "{palette.darkModeBlack}", + "type": "color", + "description": "darkModeBlack" + }, + "backgroundBrandBottom": { + "value": "{palette.darkModeBlack}", + "type": "color", + "description": "darkModeBlack" + }, + "appBarBackground": { + "value": "{palette.darkModeGrey}", + "type": "color", + "description": "darkModeGrey" + }, + "navigationBarBackground": { + "value": "{palette.darkModeBlack}", + "type": "color", + "description": "darkModeBlack" + }, + "skeletonWave": { + "value": "{palette.darkModeGrey6}", + "type": "color", + "description": "darkModeGrey6" + }, + "borderLow": { + "value": "{palette.darkModeBlack}", + "type": "color", + "description": "darkModeBlack" + }, + "border": { + "value": "{palette.darkModeGrey}", + "type": "color", + "description": "darkModeGrey" + }, + "borderHigh": { + "value": "{palette.darkModeGrey5}", + "type": "color", + "description": "darkModeGrey5" + }, + "borderSelected": { + "value": "{palette.movistarBlue}", + "type": "color", + "description": "movistarBlue" + }, + "coverBackgroundHover": { + "value": "rgba({palette.darkModeBlack}, 0.25)", + "type": "color", + "description": "darkModeBlack" + }, + "coverBackgroundPressed": { + "value": "rgba({palette.darkModeBlack}, 0.35)", + "type": "color", + "description": "darkModeBlack" + }, + "buttonDangerBackground": { + "value": "{palette.pepper55}", + "type": "color", + "description": "pepper55" + }, + "buttonDangerBackgroundPressed": { + "value": "{palette.pepper70}", + "type": "color", + "description": "pepper70" + }, + "buttonDangerBackgroundHover": { + "value": "{palette.pepper65}", + "type": "color", + "description": "pepper65" + }, + "buttonLinkDangerBackgroundPressed": { + "value": "rgba({palette.white}, 0.08)", + "type": "color", + "description": "white" + }, + "buttonLinkDangerBackgroundInverse": { + "value": "rgba({palette.white}, 0)", + "type": "color", + "description": "white" + }, + "buttonLinkDangerBackgroundInversePressed": { + "value": "rgba({palette.white}, 0.08)", + "type": "color", + "description": "white" + }, + "buttonLinkBackgroundPressed": { + "value": "rgba({palette.white}, 0.08)", + "type": "color", + "description": "white" + }, + "buttonLinkBackgroundInversePressed": { + "value": "rgba({palette.white}, 0.08)", + "type": "color", + "description": "white" + }, + "buttonPrimaryBackground": { + "value": "{palette.movistarBlueHC}", + "type": "color", + "description": "movistarBlueHC" + }, + "buttonPrimaryBackgroundInverse": { + "value": "{palette.movistarBlueHC}", + "type": "color", + "description": "movistarBlueHC" + }, + "buttonPrimaryBackgroundPressed": { + "value": "{palette.movistarBlueHC65}", + "type": "color", + "description": "movistarBlueHC65" + }, + "buttonPrimaryBackgroundHover": { + "value": "{palette.movistarBlueHC55}", + "type": "color", + "description": "movistarBlueHC55" + }, + "buttonPrimaryBackgroundInversePressed": { + "value": "{palette.movistarBlueHC65}", + "type": "color", + "description": "movistarBlueHC65" + }, + "buttonSecondaryBackgroundHover": { + "value": "rgba({palette.white}, 0.15)", + "type": "color", + "description": "white" + }, + "buttonSecondaryBackgroundPressed": { + "value": "rgba({palette.white}, 0.25)", + "type": "color", + "description": "white" + }, + "buttonSecondaryBorder": { + "value": "{palette.white}", + "type": "color", + "description": "white" + }, + "buttonSecondaryBorderPressed": { + "value": "{palette.white}", + "type": "color", + "description": "white" + }, + "buttonSecondaryBorderInverse": { + "value": "{palette.darkModeGrey2}", + "type": "color", + "description": "darkModeGrey2" + }, + "buttonSecondaryBorderInversePressed": { + "value": "{palette.darkModeGrey2}", + "type": "color", + "description": "darkModeGrey2" + }, + "buttonSecondaryBackgroundInverseHover": { + "value": "rgba({palette.white}, 0.15)", + "type": "color", + "description": "white" + }, + "buttonSecondaryBackgroundInversePressed": { + "value": "rgba({palette.white}, 0.25)", + "type": "color", + "description": "white" + }, + "textButtonPrimary": { + "value": "{palette.white}", + "type": "color", + "description": "white" + }, + "textButtonPrimaryInverse": { + "value": "{palette.white}", + "type": "color", + "description": "white" + }, + "textButtonPrimaryInversePressed": { + "value": "{palette.white}", + "type": "color", + "description": "white" + }, + "textButtonSecondary": { + "value": "{palette.darkModeGrey2}", + "type": "color", + "description": "darkModeGrey2" + }, + "textButtonSecondaryPressed": { + "value": "{palette.darkModeGrey2}", + "type": "color", + "description": "darkModeGrey2" + }, + "textButtonSecondaryInverse": { + "value": "{palette.darkModeGrey2}", + "type": "color", + "description": "darkModeGrey2" + }, + "textButtonSecondaryInversePressed": { + "value": "{palette.darkModeGrey2}", + "type": "color", + "description": "darkModeGrey2" + }, + "textLink": { + "value": "{palette.movistarBlue}", + "type": "color", + "description": "movistarBlue" + }, + "textLinkInverse": { + "value": "{palette.movistarBlue}", + "type": "color", + "description": "movistarBlue" + }, + "textLinkDanger": { + "value": "{palette.pepper45}", + "type": "color", + "description": "pepper45" + }, + "textLinkSnackbar": { + "value": "{palette.movistarBlue}", + "type": "color", + "description": "movistarBlue" + }, + "textActivated": { + "value": "{palette.movistarBlue}", + "type": "color", + "description": "movistarBlue" + }, + "textBrand": { + "value": "{palette.movistarBlue}", + "type": "color", + "description": "movistarBlue" + }, + "control": { + "value": "{palette.darkModeGrey4}", + "type": "color", + "description": "darkModeGrey4" + }, + "controlActivated": { + "value": "{palette.movistarBlue}", + "type": "color", + "description": "movistarBlue" + }, + "controlInverse": { + "value": "{palette.darkModeGrey4}", + "type": "color", + "description": "darkModeGrey4" + }, + "controlActivatedInverse": { + "value": "{palette.movistarBlue}", + "type": "color", + "description": "movistarBlue" + }, + "controlError": { + "value": "{palette.pepper45}", + "type": "color", + "description": "pepper45" + }, + "barTrack": { + "value": "{palette.darkModeGrey6}", + "type": "color", + "description": "darkModeGrey6" + }, + "loadingBar": { + "value": "{palette.movistarBlue}", + "type": "color", + "description": "movistarBlue" + }, + "loadingBarBackground": { + "value": "{palette.darkModeGrey6}", + "type": "color", + "description": "darkModeGrey6" + }, + "toggleAndroidInactive": { + "value": "{palette.darkModeGrey2}", + "type": "color", + "description": "darkModeGrey2" + }, + "toggleAndroidBackgroundActive": { + "value": "{palette.movistarBlue20}", + "type": "color", + "description": "movistarBlue20" + }, + "iosControlKnob": { + "value": "{palette.darkModeGrey2}", + "type": "color", + "description": "darkModeGrey2" + }, + "divider": { + "value": "rgba({palette.white}, 0.1)", + "type": "color", + "description": "white" + }, + "dividerInverse": { + "value": "rgba({palette.white}, 0.1)", + "type": "color", + "description": "white" + }, + "navigationBarDivider": { + "value": "{palette.darkModeBlack}", + "type": "color", + "description": "darkModeBlack" + }, + "badge": { + "value": "{palette.pepper55}", + "type": "color", + "description": "pepper55" + }, + "feedbackErrorBackground": { + "value": "{palette.pepper55}", + "type": "color", + "description": "pepper55" + }, + "feedbackInfoBackground": { + "value": "{palette.movistarBlueDark}", + "type": "color", + "description": "movistarBlueDark" + }, + "brand": { + "value": "{palette.movistarBlue}", + "type": "color", + "description": "movistarBlue" + }, + "brandHigh": { + "value": "{palette.movistarBlue40}", + "type": "color", + "description": "movistarBlue40" + }, + "inverse": { + "value": "{palette.darkModeGrey2}", + "type": "color", + "description": "darkModeGrey2" + }, + "neutralHigh": { + "value": "{palette.darkModeGrey2}", + "type": "color", + "description": "darkModeGrey2" + }, + "neutralMedium": { + "value": "{palette.darkModeGrey5}", + "type": "color", + "description": "darkModeGrey5" + }, + "neutralMediumInverse": { + "value": "{palette.grey5}", + "type": "color", + "description": "grey5" + }, + "neutralLow": { + "value": "{palette.darkModeGrey6}", + "type": "color", + "description": "darkModeGrey6" + }, + "neutralLowAlternative": { + "value": "{palette.darkModeGrey6}", + "type": "color", + "description": "darkModeGrey6" + }, + "textPrimary": { + "value": "{palette.darkModeGrey2}", + "type": "color", + "description": "darkModeGrey2" + }, + "textPrimaryInverse": { + "value": "{palette.darkModeGrey2}", + "type": "color", + "description": "darkModeGrey2" + }, + "textSecondary": { + "value": "{palette.darkModeGrey4}", + "type": "color", + "description": "darkModeGrey4" + }, + "textSecondaryInverse": { + "value": "{palette.darkModeGrey4}", + "type": "color", + "description": "darkModeGrey4" + }, + "success": { + "value": "{palette.movistarGreen}", + "type": "color", + "description": "movistarGreen" + }, + "warning": { + "value": "{palette.egg}", + "type": "color", + "description": "egg" + }, + "error": { + "value": "{palette.pepper45}", + "type": "color", + "description": "pepper45" + }, + "textError": { + "value": "{palette.pepper45}", + "type": "color", + "description": "pepper45" + }, + "textErrorInverse": { + "value": "{palette.pepper45}", + "type": "color", + "description": "pepper45" + }, + "promo": { + "value": "{palette.purple40}", + "type": "color", + "description": "purple40" + }, + "highlight": { + "value": "{palette.pink45}", + "type": "color", + "description": "pink45" + }, + "successLow": { + "value": "{palette.darkModeGrey7}", + "type": "color", + "description": "darkModeGrey7" + }, + "warningLow": { + "value": "{palette.darkModeGrey7}", + "type": "color", + "description": "darkModeGrey7" + }, + "errorLow": { + "value": "{palette.darkModeGrey7}", + "type": "color", + "description": "darkModeGrey7" + }, + "promoLow": { + "value": "{palette.darkModeGrey7}", + "type": "color", + "description": "darkModeGrey7" + }, + "brandLow": { + "value": "{palette.darkModeGrey7}", + "type": "color", + "description": "darkModeGrey7" + }, + "successHigh": { + "value": "{palette.movistarGreen40}", + "type": "color", + "description": "movistarGreen40" + }, + "warningHigh": { + "value": "{palette.egg40}", + "type": "color", + "description": "egg40" + }, + "errorHigh": { + "value": "{palette.pepper45}", + "type": "color", + "description": "pepper45" + }, + "promoHigh": { + "value": "{palette.purple35}", + "type": "color", + "description": "purple35" + }, + "successHighInverse": { + "value": "{palette.movistarGreen70}", + "type": "color", + "description": "movistarGreen70" + }, + "warningHighInverse": { + "value": "{palette.egg80}", + "type": "color", + "description": "egg80" + }, + "errorHighInverse": { + "value": "{palette.pepper70}", + "type": "color", + "description": "pepper70" + }, + "promoHighInverse": { + "value": "{palette.purple70}", + "type": "color", + "description": "purple70" + }, + "textNavigationBarPrimary": { + "value": "{palette.darkModeGrey2}", + "type": "color", + "description": "darkModeGrey2" + }, + "textNavigationBarSecondary": { + "value": "{palette.darkModeGrey4}", + "type": "color", + "description": "darkModeGrey4" + }, + "textNavigationSearchBarHint": { + "value": "{palette.darkModeGrey4}", + "type": "color", + "description": "darkModeGrey4" + }, + "textNavigationSearchBarText": { + "value": "{palette.darkModeGrey2}", + "type": "color", + "description": "darkModeGrey2" + }, + "textAppBar": { + "value": "{palette.darkModeGrey4}", + "type": "color", + "description": "darkModeGrey4" + }, + "textAppBarSelected": { + "value": "{palette.movistarBlue}", + "type": "color", + "description": "movistarBlue" + }, + "customTabsBackground": { + "value": "{palette.darkModeBlack}", + "type": "color", + "description": "darkModeBlack" + }, + "tagTextPromo": { + "value": "{palette.purple35}", + "type": "color", + "description": "purple35" + }, + "tagTextActive": { + "value": "{palette.movistarBlue}", + "type": "color", + "description": "movistarBlue" + }, + "tagTextInactive": { + "value": "{palette.darkModeGrey3}", + "type": "color", + "description": "darkModeGrey3" + }, + "tagTextSuccess": { + "value": "{palette.movistarGreen40}", + "type": "color", + "description": "movistarGreen40" + }, + "tagTextWarning": { + "value": "{palette.egg40}", + "type": "color", + "description": "egg40" + }, + "tagTextError": { + "value": "{palette.pepper45}", + "type": "color", + "description": "pepper45" + }, + "tagBackgroundPromo": { + "value": "{palette.darkModeGrey7}", + "type": "color", + "description": "darkModeGrey7" + }, + "tagBackgroundActive": { + "value": "{palette.darkModeGrey7}", + "type": "color", + "description": "darkModeGrey7" + }, + "tagBackgroundInactive": { + "value": "{palette.darkModeGrey7}", + "type": "color", + "description": "darkModeGrey7" + }, + "tagBackgroundSuccess": { + "value": "{palette.darkModeGrey7}", + "type": "color", + "description": "darkModeGrey7" + }, + "tagBackgroundWarning": { + "value": "{palette.darkModeGrey7}", + "type": "color", + "description": "darkModeGrey7" + }, + "tagBackgroundError": { + "value": "{palette.darkModeGrey7}", + "type": "color", + "description": "darkModeGrey7" + }, + "cardContentOverlay": { + "type": "linear-gradient", + "value": { + "angle": 180, + "colors": [ + { + "value": "rgba({palette.black}, 0)", + "stop": 0 + }, + { + "value": "rgba({palette.black}, 0.4)", + "stop": 0.3 + }, + { + "value": "rgba({palette.black}, 0.7)", + "stop": 1 + } + ] + }, + "description": "black" + } + }, + "radius": { + "avatar": { + "value": "circle", + "type": "borderRadius" + }, + "bar": { + "value": "999", + "type": "borderRadius" + }, + "button": { + "value": "4", + "type": "borderRadius" + }, + "checkbox": { + "value": "2", + "type": "borderRadius" + }, + "container": { + "value": "8", + "type": "borderRadius" + }, + "indicator": { + "value": "999", + "type": "borderRadius" + }, + "input": { + "value": "8", + "type": "borderRadius" + }, + "legacyDisplay": { + "value": "16", + "type": "borderRadius" + }, + "popup": { + "value": "8", + "type": "borderRadius" + }, + "sheet": { + "value": "8", + "type": "borderRadius" + }, + "mediaSmall": { + "value": "8", + "type": "borderRadius" + } + }, + "text": { + "weight": { + "cardTitle": { + "value": "bold", + "type": "typography" + }, + "button": { + "value": "medium", + "type": "typography" + }, + "tabsLabel": { + "value": "medium", + "type": "typography" + }, + "link": { + "value": "medium", + "type": "typography" + }, + "title1": { + "value": "medium", + "type": "typography" + }, + "title2": { + "value": "bold", + "type": "typography" + }, + "title3": { + "value": "bold", + "type": "typography" + }, + "indicator": { + "value": "medium", + "type": "typography" + }, + "navigationBar": { + "value": "medium", + "type": "typography" + }, + "text5": { + "value": "bold", + "type": "typography" + }, + "text6": { + "value": "bold", + "type": "typography" + }, + "text7": { + "value": "bold", + "type": "typography" + }, + "text8": { + "value": "bold", + "type": "typography" + }, + "text9": { + "value": "bold", + "type": "typography" + }, + "text10": { + "value": "bold", + "type": "typography" + } + }, + "size": { + "tabsLabel": { + "value": { + "mobile": 16, + "desktop": 18 + }, + "type": "typography" + }, + "title3": { + "value": { + "mobile": 20, + "desktop": 28 + }, + "type": "typography" + }, + "text1": { + "value": { + "mobile": 12, + "desktop": 14 + }, + "type": "typography" + }, + "text2": { + "value": { + "mobile": 14, + "desktop": 16 + }, + "type": "typography" + }, + "text3": { + "value": { + "mobile": 16, + "desktop": 18 + }, + "type": "typography" + }, + "text4": { + "value": { + "mobile": 18, + "desktop": 20 + }, + "type": "typography" + }, + "text5": { + "value": { + "mobile": 20, + "desktop": 28 + }, + "type": "typography" + }, + "text6": { + "value": { + "mobile": 24, + "desktop": 32 + }, + "type": "typography" + }, + "text7": { + "value": { + "mobile": 28, + "desktop": 40 + }, + "type": "typography" + }, + "text8": { + "value": { + "mobile": 32, + "desktop": 48 + }, + "type": "typography" + }, + "text9": { + "value": { + "mobile": 40, + "desktop": 56 + }, + "type": "typography" + }, + "text10": { + "value": { + "mobile": 48, + "desktop": 64 + }, + "type": "typography" + } + }, + "lineHeight": { + "tabsLabel": { + "value": { + "mobile": 24, + "desktop": 24 + }, + "type": "typography" + }, + "title3": { + "value": { + "mobile": 24, + "desktop": 32 + }, + "type": "typography" + }, + "text1": { + "value": { + "mobile": 16, + "desktop": 20 + }, + "type": "typography" + }, + "text2": { + "value": { + "mobile": 20, + "desktop": 24 + }, + "type": "typography" + }, + "text3": { + "value": { + "mobile": 24, + "desktop": 24 + }, + "type": "typography" + }, + "text4": { + "value": { + "mobile": 24, + "desktop": 28 + }, + "type": "typography" + }, + "text5": { + "value": { + "mobile": 24, + "desktop": 32 + }, + "type": "typography" + }, + "text6": { + "value": { + "mobile": 32, + "desktop": 40 + }, + "type": "typography" + }, + "text7": { + "value": { + "mobile": 32, + "desktop": 48 + }, + "type": "typography" + }, + "text8": { + "value": { + "mobile": 40, + "desktop": 56 + }, + "type": "typography" + }, + "text9": { + "value": { + "mobile": 48, + "desktop": 64 + }, + "type": "typography" + }, + "text10": { + "value": { + "mobile": 56, + "desktop": 72 + }, + "type": "typography" + } + } + }, + "themeVariant": { + "successFeedback": { + "value": "default", + "type": "themeVariant" + }, + "brandLoadingScreen": { + "value": "default", + "type": "themeVariant" + } + }, + "global": { + "palette": { + "movistarBlue": { + "value": "#0B9CEA", + "type": "color" + }, + "movistarBlue10": { + "value": "#E6F5FD", + "type": "color" + }, + "movistarBlue15": { + "value": "#CEEBFB", + "type": "color" + }, + "movistarBlue20": { + "value": "#B3E1FB", + "type": "color" + }, + "movistarBlue30": { + "value": "#80CEF9", + "type": "color" + }, + "movistarBlue40": { + "value": "#4DBAF7", + "type": "color" + }, + "movistarBlue55": { + "value": "#008EDD", + "type": "color" + }, + "movistarBlueHC": { + "value": "#066FCB", + "type": "color" + }, + "movistarBlueHC55": { + "value": "#055EAC", + "type": "color" + }, + "movistarBlueHC65": { + "value": "#055398", + "type": "color" + }, + "movistarGreen": { + "value": "#5CB615", + "type": "color" + }, + "movistarGreen10": { + "value": "#EFF8E8", + "type": "color" + }, + "movistarGreen30": { + "value": "#ADDA8A", + "type": "color" + }, + "movistarGreen40": { + "value": "#8DCC5B", + "type": "color" + }, + "movistarGreen55": { + "value": "#52A413", + "type": "color" + }, + "movistarGreen60": { + "value": "#499110", + "type": "color" + }, + "movistarGreen70": { + "value": "#407F0F", + "type": "color" + }, + "pepper": { + "value": "#FF374A", + "type": "color" + }, + "pepper10": { + "value": "#FFEBED", + "type": "color" + }, + "pepper40": { + "value": "#FF7380", + "type": "color" + }, + "pepper45": { + "value": "#FF5F6E", + "type": "color" + }, + "pepper55": { + "value": "#D73241", + "type": "color" + }, + "pepper60": { + "value": "#CC2C3B", + "type": "color" + }, + "pepper65": { + "value": "#BF2937", + "type": "color" + }, + "pepper70": { + "value": "#B22634", + "type": "color" + }, + "egg": { + "value": "#F28D15", + "type": "color" + }, + "egg10": { + "value": "#FEF4E8", + "type": "color" + }, + "egg40": { + "value": "#F6AF5B", + "type": "color" + }, + "egg55": { + "value": "#D97D0D", + "type": "color" + }, + "egg80": { + "value": "#6D3F09", + "type": "color" + }, + "pink": { + "value": "#E63780", + "type": "color" + }, + "pink45": { + "value": "#EB5F99", + "type": "color" + }, + "pink55": { + "value": "#C42F6D", + "type": "color" + }, + "purple": { + "value": "#A13EA1", + "type": "color" + }, + "purple10": { + "value": "#F6ECF6", + "type": "color" + }, + "purple35": { + "value": "#C78BC7", + "type": "color" + }, + "purple40": { + "value": "#BD78BD", + "type": "color" + }, + "purple70": { + "value": "#712B71", + "type": "color" + }, + "grey1": { + "value": "#F6F6F6", + "type": "color" + }, + "grey2": { + "value": "#EEEEEE", + "type": "color" + }, + "grey3": { + "value": "#DDDDDD", + "type": "color" + }, + "grey4": { + "value": "#949494", + "type": "color" + }, + "grey5": { + "value": "#6B6C6F", + "type": "color" + }, + "grey6": { + "value": "#313235", + "type": "color" + }, + "white": { + "value": "#FFFFFF", + "type": "color" + }, + "black": { + "value": "#000000", + "type": "color" + }, + "movistarBlueDark": { + "value": "#0B2739", + "type": "color" + }, + "darkModeBlack": { + "value": "#061824", + "type": "color" + }, + "darkModeGrey": { + "value": "#081F2E", + "type": "color" + }, + "darkModeGrey2": { + "value": "#EAEBEE", + "type": "color" + }, + "darkModeGrey3": { + "value": "#CED4D7", + "type": "color" + }, + "darkModeGrey4": { + "value": "#85939C", + "type": "color" + }, + "darkModeGrey5": { + "value": "#6D7D88", + "type": "color" + }, + "darkModeGrey6": { + "value": "#3C5261", + "type": "color" + }, + "darkModeGrey7": { + "value": "#032F46", + "type": "color" + } + } + } +} From 05af426d0f4f0616c5239baea266aff124422f36 Mon Sep 17 00:00:00 2001 From: "Ignacio Ceballos (Yayo)" Date: Fri, 30 Aug 2024 00:24:06 +0200 Subject: [PATCH 23/71] test --- .github/workflows/sync-figma-tokens.yml | 1 - tokens/movistar2.json | 1854 ----------------------- 2 files changed, 1855 deletions(-) delete mode 100644 tokens/movistar2.json diff --git a/.github/workflows/sync-figma-tokens.yml b/.github/workflows/sync-figma-tokens.yml index ebdc9b71691..c2848797ec5 100644 --- a/.github/workflows/sync-figma-tokens.yml +++ b/.github/workflows/sync-figma-tokens.yml @@ -6,7 +6,6 @@ on: # - production paths: - "tokens/movistar.json" - - "tokens/movistar2.json" - "tokens/vivo-new.json" - "tokens/o2-new.json" - "tokens/telefonica.json" diff --git a/tokens/movistar2.json b/tokens/movistar2.json deleted file mode 100644 index e4623ad8dee..00000000000 --- a/tokens/movistar2.json +++ /dev/null @@ -1,1854 +0,0 @@ -{ - "light": { - "background": { - "value": "{palette.white2}", - "type": "color", - "description": "white" - }, - "backgroundAlternative": { - "value": "{palette.grey1}", - "type": "color", - "description": "grey1" - }, - "backgroundBrand": { - "value": "{palette.movistarBlue}", - "type": "color", - "description": "movistarBlue" - }, - "backgroundBrandSecondary": { - "value": "{palette.movistarBlueDark}", - "type": "color", - "description": "movistarBlueDark" - }, - "backgroundContainer": { - "value": "{palette.white}", - "type": "color", - "description": "white" - }, - "backgroundContainerError": { - "value": "{palette.pepper10}", - "type": "color", - "description": "pepper10" - }, - "backgroundContainerHover": { - "value": "rgba({palette.black}, 0.03)", - "type": "color", - "description": "black" - }, - "backgroundContainerPressed": { - "value": "rgba({palette.black}, 0.05)", - "type": "color", - "description": "black" - }, - "backgroundContainerBrand": { - "value": "{palette.movistarBlue}", - "type": "color", - "description": "movistarBlue" - }, - "backgroundContainerBrandHover": { - "value": "rgba({palette.black}, 0.1)", - "type": "color", - "description": "black" - }, - "backgroundContainerBrandPressed": { - "value": "rgba({palette.black}, 0.2)", - "type": "color", - "description": "black" - }, - "backgroundContainerBrandOverInverse": { - "value": "{palette.movistarBlue55}", - "type": "color", - "description": "movistarBlue55" - }, - "backgroundContainerAlternative": { - "value": "{palette.grey1}", - "type": "color", - "description": "grey1" - }, - "backgroundOverlay": { - "value": "rgba({palette.movistarBlueDark}, 0.6)", - "type": "color", - "description": "movistarBlueDark" - }, - "backgroundSkeleton": { - "value": "{palette.grey2}", - "type": "color", - "description": "grey2" - }, - "backgroundSkeletonInverse": { - "value": "{palette.movistarBlue55}", - "type": "color", - "description": "movistarBlue55" - }, - "backgroundBrandTop": { - "value": "{palette.movistarBlue}", - "type": "color", - "description": "movistarBlue" - }, - "backgroundBrandBottom": { - "value": "{palette.movistarBlue}", - "type": "color", - "description": "movistarBlue" - }, - "appBarBackground": { - "value": "{palette.white}", - "type": "color", - "description": "white" - }, - "navigationBarBackground": { - "value": "{palette.movistarBlue}", - "type": "color", - "description": "movistarBlue" - }, - "skeletonWave": { - "value": "{palette.grey2}", - "type": "color", - "description": "grey2" - }, - "borderLow": { - "value": "{palette.grey1}", - "type": "color", - "description": "grey1" - }, - "border": { - "value": "{palette.grey3}", - "type": "color", - "description": "grey3" - }, - "borderHigh": { - "value": "{palette.grey5}", - "type": "color", - "description": "grey5" - }, - "borderSelected": { - "value": "{palette.movistarBlue}", - "type": "color", - "description": "movistarBlue" - }, - "coverBackgroundHover": { - "value": "rgba({palette.black}, 0.25)", - "type": "color", - "description": "black" - }, - "coverBackgroundPressed": { - "value": "rgba({palette.black}, 0.35)", - "type": "color", - "description": "black" - }, - "buttonDangerBackground": { - "value": "{palette.pepper55}", - "type": "color", - "description": "pepper55" - }, - "buttonDangerBackgroundPressed": { - "value": "{palette.pepper70}", - "type": "color", - "description": "pepper70" - }, - "buttonDangerBackgroundHover": { - "value": "{palette.pepper65}", - "type": "color", - "description": "pepper65" - }, - "buttonLinkDangerBackgroundPressed": { - "value": "{palette.pepper10}", - "type": "color", - "description": "pepper10" - }, - "buttonLinkDangerBackgroundInverse": { - "value": "{palette.white}", - "type": "color", - "description": "white" - }, - "buttonLinkDangerBackgroundInversePressed": { - "value": "{palette.pepper10}", - "type": "color", - "description": "pepper10" - }, - "buttonLinkBackgroundPressed": { - "value": "{palette.movistarBlue10}", - "type": "color", - "description": "movistarBlue10" - }, - "buttonLinkBackgroundInversePressed": { - "value": "rgba({palette.white}, 0.2)", - "type": "color", - "description": "white" - }, - "buttonPrimaryBackground": { - "value": "{palette.movistarBlueHC}", - "type": "color", - "description": "movistarBlueHC" - }, - "buttonPrimaryBackgroundInverse": { - "value": "{palette.white}", - "type": "color", - "description": "white" - }, - "buttonPrimaryBackgroundPressed": { - "value": "{palette.movistarBlueHC65}", - "type": "color", - "description": "movistarBlueHC65" - }, - "buttonPrimaryBackgroundHover": { - "value": "{palette.movistarBlueHC55}", - "type": "color", - "description": "movistarBlueHC55" - }, - "buttonPrimaryBackgroundInversePressed": { - "value": "{palette.movistarBlue10}", - "type": "color", - "description": "movistarBlue10" - }, - "buttonSecondaryBorder": { - "value": "{palette.movistarBlueHC}", - "type": "color", - "description": "movistarBlueHC" - }, - "buttonSecondaryBorderPressed": { - "value": "{palette.movistarBlueHC65}", - "type": "color", - "description": "movistarBlueHC65" - }, - "buttonSecondaryBackgroundHover": { - "value": "{palette.movistarBlue10}", - "type": "color", - "description": "movistarBlue10" - }, - "buttonSecondaryBackgroundPressed": { - "value": "{palette.movistarBlue15}", - "type": "color", - "description": "movistarBlue15" - }, - "buttonSecondaryBorderInverse": { - "value": "{palette.white}", - "type": "color", - "description": "white" - }, - "buttonSecondaryBorderInversePressed": { - "value": "{palette.white}", - "type": "color", - "description": "white" - }, - "buttonSecondaryBackgroundInverseHover": { - "value": "rgba({palette.white}, 0.2)", - "type": "color", - "description": "white" - }, - "buttonSecondaryBackgroundInversePressed": { - "value": "rgba({palette.white}, 0.3)", - "type": "color", - "description": "white" - }, - "textButtonPrimary": { - "value": "{palette.white}", - "type": "color", - "description": "white" - }, - "textButtonPrimaryInverse": { - "value": "{palette.movistarBlueHC}", - "type": "color", - "description": "movistarBlueHC" - }, - "textButtonPrimaryInversePressed": { - "value": "{palette.movistarBlueHC}", - "type": "color", - "description": "movistarBlueHC" - }, - "textButtonSecondary": { - "value": "{palette.movistarBlueHC}", - "type": "color", - "description": "movistarBlueHC" - }, - "textButtonSecondaryPressed": { - "value": "{palette.movistarBlueHC55}", - "type": "color", - "description": "movistarBlueHC55" - }, - "textButtonSecondaryInverse": { - "value": "{palette.white}", - "type": "color", - "description": "white" - }, - "textButtonSecondaryInversePressed": { - "value": "{palette.white}", - "type": "color", - "description": "white" - }, - "textLink": { - "value": "{palette.movistarBlueHC}", - "type": "color", - "description": "movistarBlueHC" - }, - "textLinkInverse": { - "value": "{palette.white}", - "type": "color", - "description": "white" - }, - "textLinkDanger": { - "value": "{palette.pepper60}", - "type": "color", - "description": "pepper60" - }, - "textLinkSnackbar": { - "value": "{palette.movistarBlue30}", - "type": "color", - "description": "movistarBlue30" - }, - "textActivated": { - "value": "{palette.movistarBlueHC}", - "type": "color", - "description": "movistarBlueHC" - }, - "textBrand": { - "value": "{palette.movistarBlueHC}", - "type": "color", - "description": "movistarBlueHC" - }, - "control": { - "value": "{palette.grey4}", - "type": "color", - "description": "grey4" - }, - "controlActivated": { - "value": "{palette.movistarBlue}", - "type": "color", - "description": "movistarBlue" - }, - "controlInverse": { - "value": "{palette.movistarBlue20}", - "type": "color", - "description": "movistarBlue20" - }, - "controlActivatedInverse": { - "value": "{palette.white}", - "type": "color", - "description": "white" - }, - "controlError": { - "value": "{palette.pepper55}", - "type": "color", - "description": "pepper55" - }, - "barTrack": { - "value": "{palette.grey3}", - "type": "color", - "description": "grey3" - }, - "loadingBar": { - "value": "{palette.movistarBlue}", - "type": "color", - "description": "movistarBlue" - }, - "loadingBarBackground": { - "value": "{palette.grey2}", - "type": "color", - "description": "grey2" - }, - "toggleAndroidInactive": { - "value": "{palette.grey2}", - "type": "color", - "description": "grey2" - }, - "toggleAndroidBackgroundActive": { - "value": "{palette.movistarBlue20}", - "type": "color", - "description": "movistarBlue20" - }, - "iosControlKnob": { - "value": "{palette.white}", - "type": "color", - "description": "white" - }, - "divider": { - "value": "{palette.grey3}", - "type": "color", - "description": "grey3" - }, - "dividerInverse": { - "value": "rgba({palette.white}, 0.2)", - "type": "color", - "description": "white" - }, - "navigationBarDivider": { - "value": "{palette.movistarBlue}", - "type": "color", - "description": "movistarBlue" - }, - "badge": { - "value": "{palette.pepper55}", - "type": "color", - "description": "pepper55" - }, - "feedbackErrorBackground": { - "value": "{palette.pepper55}", - "type": "color", - "description": "pepper55" - }, - "feedbackInfoBackground": { - "value": "{palette.movistarBlueDark}", - "type": "color", - "description": "movistarBlueDark" - }, - "brand": { - "value": "{palette.movistarBlue}", - "type": "color", - "description": "movistarBlue" - }, - "brandHigh": { - "value": "{palette.movistarBlue55}", - "type": "color", - "description": "movistarBlue55" - }, - "inverse": { - "value": "{palette.white}", - "type": "color", - "description": "white" - }, - "neutralHigh": { - "value": "{palette.movistarBlueDark}", - "type": "color", - "description": "movistarBlueDark" - }, - "neutralMedium": { - "value": "{palette.grey5}", - "type": "color", - "description": "grey5" - }, - "neutralMediumInverse": { - "value": "{palette.grey5}", - "type": "color", - "description": "grey5" - }, - "neutralLow": { - "value": "{palette.grey1}", - "type": "color", - "description": "grey1" - }, - "neutralLowAlternative": { - "value": "{palette.grey2}", - "type": "color", - "description": "grey2" - }, - "textPrimary": { - "value": "{palette.movistarBlueDark}", - "type": "color", - "description": "movistarBlueDark" - }, - "textPrimaryInverse": { - "value": "{palette.white}", - "type": "color", - "description": "white" - }, - "textSecondary": { - "value": "{palette.grey5}", - "type": "color", - "description": "grey5" - }, - "textSecondaryInverse": { - "value": "{palette.movistarBlue10}", - "type": "color", - "description": "movistarBlue10" - }, - "success": { - "value": "{palette.movistarGreen55}", - "type": "color", - "description": "movistarGreen55" - }, - "warning": { - "value": "{palette.egg55}", - "type": "color", - "description": "egg55" - }, - "error": { - "value": "{palette.pepper55}", - "type": "color", - "description": "pepper55" - }, - "textError": { - "value": "{palette.pepper55}", - "type": "color", - "description": "pepper55" - }, - "textErrorInverse": { - "value": "{palette.white}", - "type": "color", - "description": "white" - }, - "promo": { - "value": "{palette.purple}", - "type": "color", - "description": "purple" - }, - "highlight": { - "value": "{palette.pink55}", - "type": "color", - "description": "pink55" - }, - "successLow": { - "value": "{palette.movistarGreen10}", - "type": "color", - "description": "movistarGreen10" - }, - "warningLow": { - "value": "{palette.egg10}", - "type": "color", - "description": "egg10" - }, - "errorLow": { - "value": "{palette.pepper10}", - "type": "color", - "description": "pepper10" - }, - "promoLow": { - "value": "{palette.purple10}", - "type": "color", - "description": "purple10" - }, - "brandLow": { - "value": "{palette.movistarBlue10}", - "type": "color", - "description": "movistarBlue10" - }, - "successHigh": { - "value": "{palette.movistarGreen70}", - "type": "color", - "description": "movistarGreen70" - }, - "warningHigh": { - "value": "{palette.egg80}", - "type": "color", - "description": "egg80" - }, - "errorHigh": { - "value": "{palette.pepper70}", - "type": "color", - "description": "pepper70" - }, - "promoHigh": { - "value": "{palette.purple70}", - "type": "color", - "description": "purple70" - }, - "successHighInverse": { - "value": "{palette.movistarGreen70}", - "type": "color", - "description": "movistarGreen70" - }, - "warningHighInverse": { - "value": "{palette.egg80}", - "type": "color", - "description": "egg80" - }, - "errorHighInverse": { - "value": "{palette.pepper70}", - "type": "color", - "description": "pepper70" - }, - "promoHighInverse": { - "value": "{palette.purple70}", - "type": "color", - "description": "purple70" - }, - "textNavigationBarPrimary": { - "value": "{palette.white}", - "type": "color", - "description": "white" - }, - "textNavigationBarSecondary": { - "value": "{palette.movistarBlue20}", - "type": "color", - "description": "movistarBlue20" - }, - "textNavigationSearchBarHint": { - "value": "{palette.movistarBlue20}", - "type": "color", - "description": "movistarBlue20" - }, - "textNavigationSearchBarText": { - "value": "{palette.white}", - "type": "color", - "description": "white" - }, - "textAppBar": { - "value": "{palette.grey5}", - "type": "color", - "description": "grey5" - }, - "textAppBarSelected": { - "value": "{palette.movistarBlueHC}", - "type": "color", - "description": "movistarBlueHC" - }, - "customTabsBackground": { - "value": "{palette.white}", - "type": "color", - "description": "white" - }, - "tagTextPromo": { - "value": "{palette.purple70}", - "type": "color", - "description": "purple70" - }, - "tagTextActive": { - "value": "{palette.movistarBlueHC}", - "type": "color", - "description": "movistarBlueHC" - }, - "tagTextInactive": { - "value": "{palette.grey5}", - "type": "color", - "description": "grey5" - }, - "tagTextSuccess": { - "value": "{palette.movistarGreen70}", - "type": "color", - "description": "movistarGreen70" - }, - "tagTextWarning": { - "value": "{palette.egg80}", - "type": "color", - "description": "egg80" - }, - "tagTextError": { - "value": "{palette.pepper70}", - "type": "color", - "description": "pepper70" - }, - "tagBackgroundPromo": { - "value": "{palette.purple10}", - "type": "color", - "description": "purple10" - }, - "tagBackgroundActive": { - "value": "{palette.movistarBlue10}", - "type": "color", - "description": "movistarBlue10" - }, - "tagBackgroundInactive": { - "value": "{palette.grey1}", - "type": "color", - "description": "grey1" - }, - "tagBackgroundSuccess": { - "value": "{palette.movistarGreen10}", - "type": "color", - "description": "movistarGreen10" - }, - "tagBackgroundWarning": { - "value": "{palette.egg10}", - "type": "color", - "description": "egg10" - }, - "tagBackgroundError": { - "value": "{palette.pepper10}", - "type": "color", - "description": "pepper10" - }, - "cardContentOverlay": { - "type": "linear-gradient", - "value": { - "angle": 180, - "colors": [ - { - "value": "rgba({palette.black}, 0)", - "stop": 0 - }, - { - "value": "rgba({palette.black}, 0.4)", - "stop": 0.3 - }, - { - "value": "rgba({palette.black}, 0.7)", - "stop": 1 - } - ] - }, - "description": "black" - } - }, - "dark": { - "background": { - "value": "{palette.darkModeBlack}", - "type": "color", - "description": "darkModeBlack" - }, - "backgroundAlternative": { - "value": "{palette.darkModeBlack}", - "type": "color", - "description": "darkModeBlack" - }, - "backgroundBrand": { - "value": "{palette.darkModeBlack}", - "type": "color", - "description": "darkModeBlack" - }, - "backgroundBrandSecondary": { - "value": "{palette.darkModeBlack}", - "type": "color", - "description": "darkModeBlack" - }, - "backgroundContainer": { - "value": "{palette.darkModeGrey}", - "type": "color", - "description": "darkModeGrey" - }, - "backgroundContainerError": { - "value": "{palette.darkModeGrey}", - "type": "color", - "description": "darkModeGrey" - }, - "backgroundContainerHover": { - "value": "rgba({palette.white}, 0.03)", - "type": "color", - "description": "white" - }, - "backgroundContainerPressed": { - "value": "rgba({palette.white}, 0.05)", - "type": "color", - "description": "white" - }, - "backgroundContainerBrand": { - "value": "{palette.darkModeGrey}", - "type": "color", - "description": "darkModeGrey" - }, - "backgroundContainerBrandHover": { - "value": "rgba({palette.white}, 0.03)", - "type": "color", - "description": "white" - }, - "backgroundContainerBrandPressed": { - "value": "rgba({palette.white}, 0.05)", - "type": "color", - "description": "white" - }, - "backgroundContainerBrandOverInverse": { - "value": "{palette.darkModeGrey}", - "type": "color", - "description": "darkModeGrey" - }, - "backgroundContainerAlternative": { - "value": "{palette.darkModeGrey}", - "type": "color", - "description": "darkModeGrey" - }, - "backgroundOverlay": { - "value": "rgba({palette.darkModeGrey}, 0.8)", - "type": "color", - "description": "darkModeGrey" - }, - "backgroundSkeleton": { - "value": "{palette.darkModeGrey6}", - "type": "color", - "description": "darkModeGrey6" - }, - "backgroundSkeletonInverse": { - "value": "{palette.darkModeGrey6}", - "type": "color", - "description": "darkModeGrey6" - }, - "backgroundBrandTop": { - "value": "{palette.darkModeBlack}", - "type": "color", - "description": "darkModeBlack" - }, - "backgroundBrandBottom": { - "value": "{palette.darkModeBlack}", - "type": "color", - "description": "darkModeBlack" - }, - "appBarBackground": { - "value": "{palette.darkModeGrey}", - "type": "color", - "description": "darkModeGrey" - }, - "navigationBarBackground": { - "value": "{palette.darkModeBlack}", - "type": "color", - "description": "darkModeBlack" - }, - "skeletonWave": { - "value": "{palette.darkModeGrey6}", - "type": "color", - "description": "darkModeGrey6" - }, - "borderLow": { - "value": "{palette.darkModeBlack}", - "type": "color", - "description": "darkModeBlack" - }, - "border": { - "value": "{palette.darkModeGrey}", - "type": "color", - "description": "darkModeGrey" - }, - "borderHigh": { - "value": "{palette.darkModeGrey5}", - "type": "color", - "description": "darkModeGrey5" - }, - "borderSelected": { - "value": "{palette.movistarBlue}", - "type": "color", - "description": "movistarBlue" - }, - "coverBackgroundHover": { - "value": "rgba({palette.darkModeBlack}, 0.25)", - "type": "color", - "description": "darkModeBlack" - }, - "coverBackgroundPressed": { - "value": "rgba({palette.darkModeBlack}, 0.35)", - "type": "color", - "description": "darkModeBlack" - }, - "buttonDangerBackground": { - "value": "{palette.pepper55}", - "type": "color", - "description": "pepper55" - }, - "buttonDangerBackgroundPressed": { - "value": "{palette.pepper70}", - "type": "color", - "description": "pepper70" - }, - "buttonDangerBackgroundHover": { - "value": "{palette.pepper65}", - "type": "color", - "description": "pepper65" - }, - "buttonLinkDangerBackgroundPressed": { - "value": "rgba({palette.white}, 0.08)", - "type": "color", - "description": "white" - }, - "buttonLinkDangerBackgroundInverse": { - "value": "rgba({palette.white}, 0)", - "type": "color", - "description": "white" - }, - "buttonLinkDangerBackgroundInversePressed": { - "value": "rgba({palette.white}, 0.08)", - "type": "color", - "description": "white" - }, - "buttonLinkBackgroundPressed": { - "value": "rgba({palette.white}, 0.08)", - "type": "color", - "description": "white" - }, - "buttonLinkBackgroundInversePressed": { - "value": "rgba({palette.white}, 0.08)", - "type": "color", - "description": "white" - }, - "buttonPrimaryBackground": { - "value": "{palette.movistarBlueHC}", - "type": "color", - "description": "movistarBlueHC" - }, - "buttonPrimaryBackgroundInverse": { - "value": "{palette.movistarBlueHC}", - "type": "color", - "description": "movistarBlueHC" - }, - "buttonPrimaryBackgroundPressed": { - "value": "{palette.movistarBlueHC65}", - "type": "color", - "description": "movistarBlueHC65" - }, - "buttonPrimaryBackgroundHover": { - "value": "{palette.movistarBlueHC55}", - "type": "color", - "description": "movistarBlueHC55" - }, - "buttonPrimaryBackgroundInversePressed": { - "value": "{palette.movistarBlueHC65}", - "type": "color", - "description": "movistarBlueHC65" - }, - "buttonSecondaryBackgroundHover": { - "value": "rgba({palette.white}, 0.15)", - "type": "color", - "description": "white" - }, - "buttonSecondaryBackgroundPressed": { - "value": "rgba({palette.white}, 0.25)", - "type": "color", - "description": "white" - }, - "buttonSecondaryBorder": { - "value": "{palette.white}", - "type": "color", - "description": "white" - }, - "buttonSecondaryBorderPressed": { - "value": "{palette.white}", - "type": "color", - "description": "white" - }, - "buttonSecondaryBorderInverse": { - "value": "{palette.darkModeGrey2}", - "type": "color", - "description": "darkModeGrey2" - }, - "buttonSecondaryBorderInversePressed": { - "value": "{palette.darkModeGrey2}", - "type": "color", - "description": "darkModeGrey2" - }, - "buttonSecondaryBackgroundInverseHover": { - "value": "rgba({palette.white}, 0.15)", - "type": "color", - "description": "white" - }, - "buttonSecondaryBackgroundInversePressed": { - "value": "rgba({palette.white}, 0.25)", - "type": "color", - "description": "white" - }, - "textButtonPrimary": { - "value": "{palette.white}", - "type": "color", - "description": "white" - }, - "textButtonPrimaryInverse": { - "value": "{palette.white}", - "type": "color", - "description": "white" - }, - "textButtonPrimaryInversePressed": { - "value": "{palette.white}", - "type": "color", - "description": "white" - }, - "textButtonSecondary": { - "value": "{palette.darkModeGrey2}", - "type": "color", - "description": "darkModeGrey2" - }, - "textButtonSecondaryPressed": { - "value": "{palette.darkModeGrey2}", - "type": "color", - "description": "darkModeGrey2" - }, - "textButtonSecondaryInverse": { - "value": "{palette.darkModeGrey2}", - "type": "color", - "description": "darkModeGrey2" - }, - "textButtonSecondaryInversePressed": { - "value": "{palette.darkModeGrey2}", - "type": "color", - "description": "darkModeGrey2" - }, - "textLink": { - "value": "{palette.movistarBlue}", - "type": "color", - "description": "movistarBlue" - }, - "textLinkInverse": { - "value": "{palette.movistarBlue}", - "type": "color", - "description": "movistarBlue" - }, - "textLinkDanger": { - "value": "{palette.pepper45}", - "type": "color", - "description": "pepper45" - }, - "textLinkSnackbar": { - "value": "{palette.movistarBlue}", - "type": "color", - "description": "movistarBlue" - }, - "textActivated": { - "value": "{palette.movistarBlue}", - "type": "color", - "description": "movistarBlue" - }, - "textBrand": { - "value": "{palette.movistarBlue}", - "type": "color", - "description": "movistarBlue" - }, - "control": { - "value": "{palette.darkModeGrey4}", - "type": "color", - "description": "darkModeGrey4" - }, - "controlActivated": { - "value": "{palette.movistarBlue}", - "type": "color", - "description": "movistarBlue" - }, - "controlInverse": { - "value": "{palette.darkModeGrey4}", - "type": "color", - "description": "darkModeGrey4" - }, - "controlActivatedInverse": { - "value": "{palette.movistarBlue}", - "type": "color", - "description": "movistarBlue" - }, - "controlError": { - "value": "{palette.pepper45}", - "type": "color", - "description": "pepper45" - }, - "barTrack": { - "value": "{palette.darkModeGrey6}", - "type": "color", - "description": "darkModeGrey6" - }, - "loadingBar": { - "value": "{palette.movistarBlue}", - "type": "color", - "description": "movistarBlue" - }, - "loadingBarBackground": { - "value": "{palette.darkModeGrey6}", - "type": "color", - "description": "darkModeGrey6" - }, - "toggleAndroidInactive": { - "value": "{palette.darkModeGrey2}", - "type": "color", - "description": "darkModeGrey2" - }, - "toggleAndroidBackgroundActive": { - "value": "{palette.movistarBlue20}", - "type": "color", - "description": "movistarBlue20" - }, - "iosControlKnob": { - "value": "{palette.darkModeGrey2}", - "type": "color", - "description": "darkModeGrey2" - }, - "divider": { - "value": "rgba({palette.white}, 0.1)", - "type": "color", - "description": "white" - }, - "dividerInverse": { - "value": "rgba({palette.white}, 0.1)", - "type": "color", - "description": "white" - }, - "navigationBarDivider": { - "value": "{palette.darkModeBlack}", - "type": "color", - "description": "darkModeBlack" - }, - "badge": { - "value": "{palette.pepper55}", - "type": "color", - "description": "pepper55" - }, - "feedbackErrorBackground": { - "value": "{palette.pepper55}", - "type": "color", - "description": "pepper55" - }, - "feedbackInfoBackground": { - "value": "{palette.movistarBlueDark}", - "type": "color", - "description": "movistarBlueDark" - }, - "brand": { - "value": "{palette.movistarBlue}", - "type": "color", - "description": "movistarBlue" - }, - "brandHigh": { - "value": "{palette.movistarBlue40}", - "type": "color", - "description": "movistarBlue40" - }, - "inverse": { - "value": "{palette.darkModeGrey2}", - "type": "color", - "description": "darkModeGrey2" - }, - "neutralHigh": { - "value": "{palette.darkModeGrey2}", - "type": "color", - "description": "darkModeGrey2" - }, - "neutralMedium": { - "value": "{palette.darkModeGrey5}", - "type": "color", - "description": "darkModeGrey5" - }, - "neutralMediumInverse": { - "value": "{palette.grey5}", - "type": "color", - "description": "grey5" - }, - "neutralLow": { - "value": "{palette.darkModeGrey6}", - "type": "color", - "description": "darkModeGrey6" - }, - "neutralLowAlternative": { - "value": "{palette.darkModeGrey6}", - "type": "color", - "description": "darkModeGrey6" - }, - "textPrimary": { - "value": "{palette.darkModeGrey2}", - "type": "color", - "description": "darkModeGrey2" - }, - "textPrimaryInverse": { - "value": "{palette.darkModeGrey2}", - "type": "color", - "description": "darkModeGrey2" - }, - "textSecondary": { - "value": "{palette.darkModeGrey4}", - "type": "color", - "description": "darkModeGrey4" - }, - "textSecondaryInverse": { - "value": "{palette.darkModeGrey4}", - "type": "color", - "description": "darkModeGrey4" - }, - "success": { - "value": "{palette.movistarGreen}", - "type": "color", - "description": "movistarGreen" - }, - "warning": { - "value": "{palette.egg}", - "type": "color", - "description": "egg" - }, - "error": { - "value": "{palette.pepper45}", - "type": "color", - "description": "pepper45" - }, - "textError": { - "value": "{palette.pepper45}", - "type": "color", - "description": "pepper45" - }, - "textErrorInverse": { - "value": "{palette.pepper45}", - "type": "color", - "description": "pepper45" - }, - "promo": { - "value": "{palette.purple40}", - "type": "color", - "description": "purple40" - }, - "highlight": { - "value": "{palette.pink45}", - "type": "color", - "description": "pink45" - }, - "successLow": { - "value": "{palette.darkModeGrey7}", - "type": "color", - "description": "darkModeGrey7" - }, - "warningLow": { - "value": "{palette.darkModeGrey7}", - "type": "color", - "description": "darkModeGrey7" - }, - "errorLow": { - "value": "{palette.darkModeGrey7}", - "type": "color", - "description": "darkModeGrey7" - }, - "promoLow": { - "value": "{palette.darkModeGrey7}", - "type": "color", - "description": "darkModeGrey7" - }, - "brandLow": { - "value": "{palette.darkModeGrey7}", - "type": "color", - "description": "darkModeGrey7" - }, - "successHigh": { - "value": "{palette.movistarGreen40}", - "type": "color", - "description": "movistarGreen40" - }, - "warningHigh": { - "value": "{palette.egg40}", - "type": "color", - "description": "egg40" - }, - "errorHigh": { - "value": "{palette.pepper45}", - "type": "color", - "description": "pepper45" - }, - "promoHigh": { - "value": "{palette.purple35}", - "type": "color", - "description": "purple35" - }, - "successHighInverse": { - "value": "{palette.movistarGreen70}", - "type": "color", - "description": "movistarGreen70" - }, - "warningHighInverse": { - "value": "{palette.egg80}", - "type": "color", - "description": "egg80" - }, - "errorHighInverse": { - "value": "{palette.pepper70}", - "type": "color", - "description": "pepper70" - }, - "promoHighInverse": { - "value": "{palette.purple70}", - "type": "color", - "description": "purple70" - }, - "textNavigationBarPrimary": { - "value": "{palette.darkModeGrey2}", - "type": "color", - "description": "darkModeGrey2" - }, - "textNavigationBarSecondary": { - "value": "{palette.darkModeGrey4}", - "type": "color", - "description": "darkModeGrey4" - }, - "textNavigationSearchBarHint": { - "value": "{palette.darkModeGrey4}", - "type": "color", - "description": "darkModeGrey4" - }, - "textNavigationSearchBarText": { - "value": "{palette.darkModeGrey2}", - "type": "color", - "description": "darkModeGrey2" - }, - "textAppBar": { - "value": "{palette.darkModeGrey4}", - "type": "color", - "description": "darkModeGrey4" - }, - "textAppBarSelected": { - "value": "{palette.movistarBlue}", - "type": "color", - "description": "movistarBlue" - }, - "customTabsBackground": { - "value": "{palette.darkModeBlack}", - "type": "color", - "description": "darkModeBlack" - }, - "tagTextPromo": { - "value": "{palette.purple35}", - "type": "color", - "description": "purple35" - }, - "tagTextActive": { - "value": "{palette.movistarBlue}", - "type": "color", - "description": "movistarBlue" - }, - "tagTextInactive": { - "value": "{palette.darkModeGrey3}", - "type": "color", - "description": "darkModeGrey3" - }, - "tagTextSuccess": { - "value": "{palette.movistarGreen40}", - "type": "color", - "description": "movistarGreen40" - }, - "tagTextWarning": { - "value": "{palette.egg40}", - "type": "color", - "description": "egg40" - }, - "tagTextError": { - "value": "{palette.pepper45}", - "type": "color", - "description": "pepper45" - }, - "tagBackgroundPromo": { - "value": "{palette.darkModeGrey7}", - "type": "color", - "description": "darkModeGrey7" - }, - "tagBackgroundActive": { - "value": "{palette.darkModeGrey7}", - "type": "color", - "description": "darkModeGrey7" - }, - "tagBackgroundInactive": { - "value": "{palette.darkModeGrey7}", - "type": "color", - "description": "darkModeGrey7" - }, - "tagBackgroundSuccess": { - "value": "{palette.darkModeGrey7}", - "type": "color", - "description": "darkModeGrey7" - }, - "tagBackgroundWarning": { - "value": "{palette.darkModeGrey7}", - "type": "color", - "description": "darkModeGrey7" - }, - "tagBackgroundError": { - "value": "{palette.darkModeGrey7}", - "type": "color", - "description": "darkModeGrey7" - }, - "cardContentOverlay": { - "type": "linear-gradient", - "value": { - "angle": 180, - "colors": [ - { - "value": "rgba({palette.black}, 0)", - "stop": 0 - }, - { - "value": "rgba({palette.black}, 0.4)", - "stop": 0.3 - }, - { - "value": "rgba({palette.black}, 0.7)", - "stop": 1 - } - ] - }, - "description": "black" - } - }, - "radius": { - "avatar": { - "value": "circle", - "type": "borderRadius" - }, - "bar": { - "value": "999", - "type": "borderRadius" - }, - "button": { - "value": "4", - "type": "borderRadius" - }, - "checkbox": { - "value": "2", - "type": "borderRadius" - }, - "container": { - "value": "8", - "type": "borderRadius" - }, - "indicator": { - "value": "999", - "type": "borderRadius" - }, - "input": { - "value": "8", - "type": "borderRadius" - }, - "legacyDisplay": { - "value": "16", - "type": "borderRadius" - }, - "popup": { - "value": "8", - "type": "borderRadius" - }, - "sheet": { - "value": "8", - "type": "borderRadius" - }, - "mediaSmall": { - "value": "8", - "type": "borderRadius" - } - }, - "text": { - "weight": { - "cardTitle": { - "value": "bold", - "type": "typography" - }, - "button": { - "value": "medium", - "type": "typography" - }, - "tabsLabel": { - "value": "medium", - "type": "typography" - }, - "link": { - "value": "medium", - "type": "typography" - }, - "title1": { - "value": "medium", - "type": "typography" - }, - "title2": { - "value": "bold", - "type": "typography" - }, - "title3": { - "value": "bold", - "type": "typography" - }, - "indicator": { - "value": "medium", - "type": "typography" - }, - "navigationBar": { - "value": "medium", - "type": "typography" - }, - "text5": { - "value": "bold", - "type": "typography" - }, - "text6": { - "value": "bold", - "type": "typography" - }, - "text7": { - "value": "bold", - "type": "typography" - }, - "text8": { - "value": "bold", - "type": "typography" - }, - "text9": { - "value": "bold", - "type": "typography" - }, - "text10": { - "value": "bold", - "type": "typography" - } - }, - "size": { - "tabsLabel": { - "value": { - "mobile": 16, - "desktop": 18 - }, - "type": "typography" - }, - "title3": { - "value": { - "mobile": 20, - "desktop": 28 - }, - "type": "typography" - }, - "text1": { - "value": { - "mobile": 12, - "desktop": 14 - }, - "type": "typography" - }, - "text2": { - "value": { - "mobile": 14, - "desktop": 16 - }, - "type": "typography" - }, - "text3": { - "value": { - "mobile": 16, - "desktop": 18 - }, - "type": "typography" - }, - "text4": { - "value": { - "mobile": 18, - "desktop": 20 - }, - "type": "typography" - }, - "text5": { - "value": { - "mobile": 20, - "desktop": 28 - }, - "type": "typography" - }, - "text6": { - "value": { - "mobile": 24, - "desktop": 32 - }, - "type": "typography" - }, - "text7": { - "value": { - "mobile": 28, - "desktop": 40 - }, - "type": "typography" - }, - "text8": { - "value": { - "mobile": 32, - "desktop": 48 - }, - "type": "typography" - }, - "text9": { - "value": { - "mobile": 40, - "desktop": 56 - }, - "type": "typography" - }, - "text10": { - "value": { - "mobile": 48, - "desktop": 64 - }, - "type": "typography" - } - }, - "lineHeight": { - "tabsLabel": { - "value": { - "mobile": 24, - "desktop": 24 - }, - "type": "typography" - }, - "title3": { - "value": { - "mobile": 24, - "desktop": 32 - }, - "type": "typography" - }, - "text1": { - "value": { - "mobile": 16, - "desktop": 20 - }, - "type": "typography" - }, - "text2": { - "value": { - "mobile": 20, - "desktop": 24 - }, - "type": "typography" - }, - "text3": { - "value": { - "mobile": 24, - "desktop": 24 - }, - "type": "typography" - }, - "text4": { - "value": { - "mobile": 24, - "desktop": 28 - }, - "type": "typography" - }, - "text5": { - "value": { - "mobile": 24, - "desktop": 32 - }, - "type": "typography" - }, - "text6": { - "value": { - "mobile": 32, - "desktop": 40 - }, - "type": "typography" - }, - "text7": { - "value": { - "mobile": 32, - "desktop": 48 - }, - "type": "typography" - }, - "text8": { - "value": { - "mobile": 40, - "desktop": 56 - }, - "type": "typography" - }, - "text9": { - "value": { - "mobile": 48, - "desktop": 64 - }, - "type": "typography" - }, - "text10": { - "value": { - "mobile": 56, - "desktop": 72 - }, - "type": "typography" - } - } - }, - "themeVariant": { - "successFeedback": { - "value": "default", - "type": "themeVariant" - }, - "brandLoadingScreen": { - "value": "default", - "type": "themeVariant" - } - }, - "global": { - "palette": { - "movistarBlue": { - "value": "#0B9CEA", - "type": "color" - }, - "movistarBlue10": { - "value": "#E6F5FD", - "type": "color" - }, - "movistarBlue15": { - "value": "#CEEBFB", - "type": "color" - }, - "movistarBlue20": { - "value": "#B3E1FB", - "type": "color" - }, - "movistarBlue30": { - "value": "#80CEF9", - "type": "color" - }, - "movistarBlue40": { - "value": "#4DBAF7", - "type": "color" - }, - "movistarBlue55": { - "value": "#008EDD", - "type": "color" - }, - "movistarBlueHC": { - "value": "#066FCB", - "type": "color" - }, - "movistarBlueHC55": { - "value": "#055EAC", - "type": "color" - }, - "movistarBlueHC65": { - "value": "#055398", - "type": "color" - }, - "movistarGreen": { - "value": "#5CB615", - "type": "color" - }, - "movistarGreen10": { - "value": "#EFF8E8", - "type": "color" - }, - "movistarGreen30": { - "value": "#ADDA8A", - "type": "color" - }, - "movistarGreen40": { - "value": "#8DCC5B", - "type": "color" - }, - "movistarGreen55": { - "value": "#52A413", - "type": "color" - }, - "movistarGreen60": { - "value": "#499110", - "type": "color" - }, - "movistarGreen70": { - "value": "#407F0F", - "type": "color" - }, - "pepper": { - "value": "#FF374A", - "type": "color" - }, - "pepper10": { - "value": "#FFEBED", - "type": "color" - }, - "pepper40": { - "value": "#FF7380", - "type": "color" - }, - "pepper45": { - "value": "#FF5F6E", - "type": "color" - }, - "pepper55": { - "value": "#D73241", - "type": "color" - }, - "pepper60": { - "value": "#CC2C3B", - "type": "color" - }, - "pepper65": { - "value": "#BF2937", - "type": "color" - }, - "pepper70": { - "value": "#B22634", - "type": "color" - }, - "egg": { - "value": "#F28D15", - "type": "color" - }, - "egg10": { - "value": "#FEF4E8", - "type": "color" - }, - "egg40": { - "value": "#F6AF5B", - "type": "color" - }, - "egg55": { - "value": "#D97D0D", - "type": "color" - }, - "egg80": { - "value": "#6D3F09", - "type": "color" - }, - "pink": { - "value": "#E63780", - "type": "color" - }, - "pink45": { - "value": "#EB5F99", - "type": "color" - }, - "pink55": { - "value": "#C42F6D", - "type": "color" - }, - "purple": { - "value": "#A13EA1", - "type": "color" - }, - "purple10": { - "value": "#F6ECF6", - "type": "color" - }, - "purple35": { - "value": "#C78BC7", - "type": "color" - }, - "purple40": { - "value": "#BD78BD", - "type": "color" - }, - "purple70": { - "value": "#712B71", - "type": "color" - }, - "grey1": { - "value": "#F6F6F6", - "type": "color" - }, - "grey2": { - "value": "#EEEEEE", - "type": "color" - }, - "grey3": { - "value": "#DDDDDD", - "type": "color" - }, - "grey4": { - "value": "#949494", - "type": "color" - }, - "grey5": { - "value": "#6B6C6F", - "type": "color" - }, - "grey6": { - "value": "#313235", - "type": "color" - }, - "white": { - "value": "#FFFFFF", - "type": "color" - }, - "black": { - "value": "#000000", - "type": "color" - }, - "movistarBlueDark": { - "value": "#0B2739", - "type": "color" - }, - "darkModeBlack": { - "value": "#061824", - "type": "color" - }, - "darkModeGrey": { - "value": "#081F2E", - "type": "color" - }, - "darkModeGrey2": { - "value": "#EAEBEE", - "type": "color" - }, - "darkModeGrey3": { - "value": "#CED4D7", - "type": "color" - }, - "darkModeGrey4": { - "value": "#85939C", - "type": "color" - }, - "darkModeGrey5": { - "value": "#6D7D88", - "type": "color" - }, - "darkModeGrey6": { - "value": "#3C5261", - "type": "color" - }, - "darkModeGrey7": { - "value": "#032F46", - "type": "color" - } - } - } -} From 5ae59f189e4676e53e1f088418af36b7382fe055 Mon Sep 17 00:00:00 2001 From: "Ignacio Ceballos (Yayo)" Date: Fri, 30 Aug 2024 00:24:23 +0200 Subject: [PATCH 24/71] Update movistar.json --- tokens/movistar.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tokens/movistar.json b/tokens/movistar.json index 9595ff320c0..a221893259a 100644 --- a/tokens/movistar.json +++ b/tokens/movistar.json @@ -8,7 +8,7 @@ "backgroundAlternative": { "value": "{palette.grey1}", "type": "color", - "description": "grey1" + "description": "grey2" }, "backgroundBrand": { "value": "{palette.movistarBlue}", From d371e6829936c334fd81bb74d3706cd3a1d5a94b Mon Sep 17 00:00:00 2001 From: "Ignacio Ceballos (Yayo)" Date: Fri, 30 Aug 2024 00:27:15 +0200 Subject: [PATCH 25/71] Update sync-figma-tokens.yml --- .github/workflows/sync-figma-tokens.yml | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/.github/workflows/sync-figma-tokens.yml b/.github/workflows/sync-figma-tokens.yml index c2848797ec5..2ae7486ac3b 100644 --- a/.github/workflows/sync-figma-tokens.yml +++ b/.github/workflows/sync-figma-tokens.yml @@ -44,15 +44,21 @@ jobs: TELEFONICA_FILE_KEY: ${{ secrets.TELEFONICA_FILE_KEY }} steps: - - name: Checkout repository + - name: Checkout repository with full history uses: actions/checkout@v4 + with: + fetch-depth: 0 # Clona todo el historial de commits - name: Determine Execution Mode id: determine-mode run: | if [ "${{ github.event_name }}" == "push" ]; then # Get the name of the modified file (e.g., "movistar.json") - FILE_NAME=$(git diff --name-only HEAD^ HEAD | grep 'tokens/' | xargs basename) + FILE_NAME=$(git diff --name-only HEAD^ HEAD | grep 'tokens/' | xargs basename || true) + if [ -z "$FILE_NAME" ]; then + echo "No changes detected in the tokens directory." + exit 0 + fi # Remove the ".json" extension to get the brand name BRAND_NAME="${FILE_NAME%.json}" echo "Triggered by push. Brand: $BRAND_NAME" @@ -65,18 +71,9 @@ jobs: if [ "${{ github.event.inputs.vivo-new }}" == "true" ]; then selected_brands+=" vivo-new" fi - if [ "${{ github.event.inputs.o2-new }}" == "true" ]; then - selected_brands+=" o2-new" - fi if [ "${{ github.event.inputs.telefonica }}" == "true" ]; then selected_brands+=" telefonica" fi - if [ "${{ github.event.inputs.blau }}" == "true" ]; then - selected_brands+=" blau" - fi - if [ "${{ github.event.inputs.tu }}" == "true" ]; then - selected_brands+=" tu" - fi echo "Triggered by workflow dispatch. Selected brands: $selected_brands" echo "BRAND_NAME=$selected_brands" >> $GITHUB_ENV fi From c399a65b005f7777204da76bb1b1ccd2df357613 Mon Sep 17 00:00:00 2001 From: "Ignacio Ceballos (Yayo)" Date: Fri, 30 Aug 2024 00:27:24 +0200 Subject: [PATCH 26/71] Update movistar.json --- tokens/movistar.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tokens/movistar.json b/tokens/movistar.json index a221893259a..9595ff320c0 100644 --- a/tokens/movistar.json +++ b/tokens/movistar.json @@ -8,7 +8,7 @@ "backgroundAlternative": { "value": "{palette.grey1}", "type": "color", - "description": "grey2" + "description": "grey1" }, "backgroundBrand": { "value": "{palette.movistarBlue}", From 5f0e7ed11cce632a7f467ddee67fb59d615abef1 Mon Sep 17 00:00:00 2001 From: "Ignacio Ceballos (Yayo)" Date: Fri, 30 Aug 2024 00:31:14 +0200 Subject: [PATCH 27/71] Update sync-figma-tokens.yml --- .github/workflows/sync-figma-tokens.yml | 65 ++++++++++++------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/.github/workflows/sync-figma-tokens.yml b/.github/workflows/sync-figma-tokens.yml index 2ae7486ac3b..6e8ec41b3cb 100644 --- a/.github/workflows/sync-figma-tokens.yml +++ b/.github/workflows/sync-figma-tokens.yml @@ -2,36 +2,21 @@ name: Sync tokens to Figma on: push: - # branches: - # - production paths: - "tokens/movistar.json" - - "tokens/vivo-new.json" - - "tokens/o2-new.json" + - "tokens/vivo.json" - "tokens/telefonica.json" - - "tokens/blau.json" - - "tokens/tu.json" - workflow_dispatch: inputs: movistar: type: boolean default: true - vivo-new: - type: boolean - default: true - o2-new: + vivo: type: boolean default: true telefonica: type: boolean default: true - blau: - type: boolean - default: true - tu: - type: boolean - default: true jobs: sync-figma-brand: @@ -44,37 +29,49 @@ jobs: TELEFONICA_FILE_KEY: ${{ secrets.TELEFONICA_FILE_KEY }} steps: - - name: Checkout repository with full history + - name: Checkout repository uses: actions/checkout@v4 + + - name: Filter changes + id: filter + uses: dorny/paths-filter@v2 with: - fetch-depth: 0 # Clona todo el historial de commits + filters: | + movistar: + - 'tokens/movistar.json' + vivo: + - 'tokens/vivo.json' + telefonica: + - 'tokens/telefonica.json' - name: Determine Execution Mode id: determine-mode run: | - if [ "${{ github.event_name }}" == "push" ]; then - # Get the name of the modified file (e.g., "movistar.json") - FILE_NAME=$(git diff --name-only HEAD^ HEAD | grep 'tokens/' | xargs basename || true) - if [ -z "$FILE_NAME" ]; then - echo "No changes detected in the tokens directory." - exit 0 - fi - # Remove the ".json" extension to get the brand name - BRAND_NAME="${FILE_NAME%.json}" - echo "Triggered by push. Brand: $BRAND_NAME" - echo "BRAND_NAME=$BRAND_NAME" >> $GITHUB_ENV - elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then selected_brands="" if [ "${{ github.event.inputs.movistar }}" == "true" ]; then selected_brands+=" movistar" fi - if [ "${{ github.event.inputs.vivo-new }}" == "true" ]; then - selected_brands+=" vivo-new" + if [ "${{ github.event.inputs.vivo }}" == "true" ]; then + selected_brands+=" vivo" fi if [ "${{ github.event.inputs.telefonica }}" == "true" ]; then selected_brands+=" telefonica" fi - echo "Triggered by workflow dispatch. Selected brands: $selected_brands" + echo "Selected brands: $selected_brands" + echo "BRAND_NAME=$selected_brands" >> $GITHUB_ENV + else + selected_brands="" + if [ "${{ steps.filter.outputs.movistar }}" == "true" ]; then + selected_brands+=" movistar" + fi + if [ "${{ steps.filter.outputs.vivo }}" == "true" ]; then + selected_brands+=" vivo" + fi + if [ "${{ steps.filter.outputs.telefonica }}" == "true" ]; then + selected_brands+=" telefonica" + fi + echo "Detected changes in brands: $selected_brands" echo "BRAND_NAME=$selected_brands" >> $GITHUB_ENV fi From 64d745cfe7b8e5a6b607397b4eda13b5285f21c5 Mon Sep 17 00:00:00 2001 From: "Ignacio Ceballos (Yayo)" Date: Fri, 30 Aug 2024 00:31:24 +0200 Subject: [PATCH 28/71] Update movistar.json --- tokens/movistar.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tokens/movistar.json b/tokens/movistar.json index 9595ff320c0..a221893259a 100644 --- a/tokens/movistar.json +++ b/tokens/movistar.json @@ -8,7 +8,7 @@ "backgroundAlternative": { "value": "{palette.grey1}", "type": "color", - "description": "grey1" + "description": "grey2" }, "backgroundBrand": { "value": "{palette.movistarBlue}", From 4c74075c3203af6dc6c7f23d7b2e1a2bd0a3a396 Mon Sep 17 00:00:00 2001 From: "Ignacio Ceballos (Yayo)" Date: Fri, 30 Aug 2024 00:35:54 +0200 Subject: [PATCH 29/71] Update sync-figma-tokens.yml --- .github/workflows/sync-figma-tokens.yml | 54 +++++++++++++++++++++---- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/.github/workflows/sync-figma-tokens.yml b/.github/workflows/sync-figma-tokens.yml index 6e8ec41b3cb..caadd3675cd 100644 --- a/.github/workflows/sync-figma-tokens.yml +++ b/.github/workflows/sync-figma-tokens.yml @@ -4,19 +4,32 @@ on: push: paths: - "tokens/movistar.json" - - "tokens/vivo.json" + - "tokens/vivo-new.json" + - "tokens/o2-new.json" - "tokens/telefonica.json" + - "tokens/blau.json" + - "tokens/tu.json" + workflow_dispatch: inputs: movistar: type: boolean default: true - vivo: + vivo-new: + type: boolean + default: true + o2-new: type: boolean default: true telefonica: type: boolean default: true + blau: + type: boolean + default: true + tu: + type: boolean + default: true jobs: sync-figma-brand: @@ -26,6 +39,7 @@ jobs: FIGMA_TOKEN: ${{ secrets.FIGMA_TOKEN }} MOVISTAR_FILE_KEY: ${{ secrets.MOVISTAR_FILE_KEY }} VIVO_FILE_KEY: ${{ secrets.VIVO_FILE_KEY }} + O2_FILE_KEY: ${{ secrets.O2_FILE_KEY }} TELEFONICA_FILE_KEY: ${{ secrets.TELEFONICA_FILE_KEY }} steps: @@ -39,10 +53,16 @@ jobs: filters: | movistar: - 'tokens/movistar.json' - vivo: - - 'tokens/vivo.json' + vivo-new: + - 'tokens/vivo-new.json' + o2-new: + - 'tokens/o2-new.json' telefonica: - 'tokens/telefonica.json' + blau: + - 'tokens/blau.json' + tu: + - 'tokens/tu.json' - name: Determine Execution Mode id: determine-mode @@ -52,12 +72,21 @@ jobs: if [ "${{ github.event.inputs.movistar }}" == "true" ]; then selected_brands+=" movistar" fi - if [ "${{ github.event.inputs.vivo }}" == "true" ]; then - selected_brands+=" vivo" + if [ "${{ github.event.inputs.vivo-new }}" == "true" ]; then + selected_brands+=" vivo-new" + fi + if [ "${{ github.event.inputs.o2-new }}" == "true" ]; then + selected_brands+=" o2-new" fi if [ "${{ github.event.inputs.telefonica }}" == "true" ]; then selected_brands+=" telefonica" fi + if [ "${{ github.event.inputs.blau }}" == "true" ]; then + selected_brands+=" blau" + fi + if [ "${{ github.event.inputs.tu }}" == "true" ]; then + selected_brands+=" tu" + fi echo "Selected brands: $selected_brands" echo "BRAND_NAME=$selected_brands" >> $GITHUB_ENV else @@ -65,12 +94,21 @@ jobs: if [ "${{ steps.filter.outputs.movistar }}" == "true" ]; then selected_brands+=" movistar" fi - if [ "${{ steps.filter.outputs.vivo }}" == "true" ]; then - selected_brands+=" vivo" + if [ "${{ steps.filter.outputs.vivo-new }}" == "true" ]; then + selected_brands+=" vivo-new" + fi + if [ "${{ steps.filter.outputs.o2-new }}" == "true" ]; then + selected_brands+=" o2-new" fi if [ "${{ steps.filter.outputs.telefonica }}" == "true" ]; then selected_brands+=" telefonica" fi + if [ "${{ steps.filter.outputs.blau }}" == "true" ]; then + selected_brands+=" blau" + fi + if [ "${{ steps.filter.outputs.tu }}" == "true" ]; then + selected_brands+=" tu" + fi echo "Detected changes in brands: $selected_brands" echo "BRAND_NAME=$selected_brands" >> $GITHUB_ENV fi From d314111323c21b01274dbee7a35707ad8fdf5749 Mon Sep 17 00:00:00 2001 From: "Ignacio Ceballos (Yayo)" Date: Fri, 30 Aug 2024 00:36:35 +0200 Subject: [PATCH 30/71] test name for test --- .github/workflows/{sync-figma-tokens.yml => test.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{sync-figma-tokens.yml => test.yml} (100%) diff --git a/.github/workflows/sync-figma-tokens.yml b/.github/workflows/test.yml similarity index 100% rename from .github/workflows/sync-figma-tokens.yml rename to .github/workflows/test.yml From 802e215e9b8270eaf5c3cd34d1d3e0894e0ce5d9 Mon Sep 17 00:00:00 2001 From: "Ignacio Ceballos (Yayo)" Date: Fri, 30 Aug 2024 00:43:20 +0200 Subject: [PATCH 31/71] rename and include fixes in env vars --- .github/workflows/{test.yml => sync-figma-tokens.yml} | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) rename .github/workflows/{test.yml => sync-figma-tokens.yml} (94%) diff --git a/.github/workflows/test.yml b/.github/workflows/sync-figma-tokens.yml similarity index 94% rename from .github/workflows/test.yml rename to .github/workflows/sync-figma-tokens.yml index caadd3675cd..73eb4549957 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/sync-figma-tokens.yml @@ -38,9 +38,11 @@ jobs: env: FIGMA_TOKEN: ${{ secrets.FIGMA_TOKEN }} MOVISTAR_FILE_KEY: ${{ secrets.MOVISTAR_FILE_KEY }} - VIVO_FILE_KEY: ${{ secrets.VIVO_FILE_KEY }} - O2_FILE_KEY: ${{ secrets.O2_FILE_KEY }} + VIVO_NEW_FILE_KEY: ${{ secrets.VIVO_NEW_FILE_KEY }} + O2_NEW_FILE_KEY: ${{ secrets.O2_NEW_FILE_KEY }} TELEFONICA_FILE_KEY: ${{ secrets.TELEFONICA_FILE_KEY }} + BLAU_FILE_KEY: ${{ secrets.BLAU_FILE_KEY }} + TU_FILE_KEY: ${{ secrets.TU_FILE_KEY }} steps: - name: Checkout repository From fafdd36fef856c34da9027ad3bf919f59d7dbe82 Mon Sep 17 00:00:00 2001 From: "Ignacio Ceballos (Yayo)" Date: Fri, 30 Aug 2024 00:43:59 +0200 Subject: [PATCH 32/71] run automatically in production --- .github/workflows/sync-figma-tokens.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/sync-figma-tokens.yml b/.github/workflows/sync-figma-tokens.yml index 73eb4549957..0d4f2ecc218 100644 --- a/.github/workflows/sync-figma-tokens.yml +++ b/.github/workflows/sync-figma-tokens.yml @@ -2,6 +2,8 @@ name: Sync tokens to Figma on: push: + branches: + - production paths: - "tokens/movistar.json" - "tokens/vivo-new.json" From 614d736fb89690e1417b93e6be2e6e19d2e9ae33 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Wed, 11 Sep 2024 13:31:29 +0200 Subject: [PATCH 33/71] Add logic for middleware update --- tokens/figma/extract-json-data-middleware.mjs | 411 +++++++++ ...onData.mjs => extract-json-data-skins.mjs} | 8 +- tokens/figma/update-middleware.mjs | 801 ++++++++++++++++++ tokens/figma/{index.mjs => update-skins.mjs} | 272 +++--- tokens/figma/utils.mjs | 78 ++ 5 files changed, 1405 insertions(+), 165 deletions(-) create mode 100644 tokens/figma/extract-json-data-middleware.mjs rename tokens/figma/{extractJsonData.mjs => extract-json-data-skins.mjs} (98%) create mode 100644 tokens/figma/update-middleware.mjs rename tokens/figma/{index.mjs => update-skins.mjs} (75%) diff --git a/tokens/figma/extract-json-data-middleware.mjs b/tokens/figma/extract-json-data-middleware.mjs new file mode 100644 index 00000000000..e50ba68a747 --- /dev/null +++ b/tokens/figma/extract-json-data-middleware.mjs @@ -0,0 +1,411 @@ +import fs from "fs"; +import path from "path"; +import { hexToRgba } from "./utils.mjs"; + +const extractJsonData = ( + jsonFiles, + directoryPath +) => { + const allParsedContent = {}; // Initialize once to store all parsed content + + // First pass to gather allParsedContent + jsonFiles.forEach((file) => { + const filePath = path.resolve( + directoryPath, + file + ); + const fileContent = fs.readFileSync( + filePath, + "utf8" + ); + const parsedContent = JSON.parse(fileContent); + + // Extract file name without extension to use as a key + const fileName = file.split(".")[0]; + + // Store the parsed content in the allParsedContent object + allParsedContent[fileName] = parsedContent; + }); + + // Second pass to process each file + return jsonFiles.reduce((accumulator, file) => { + const filePath = path.resolve( + directoryPath, + file + ); + const fileContent = fs.readFileSync( + filePath, + "utf8" + ); + const parsedContent = JSON.parse(fileContent); + + // Extract file name without extension + const fileName = file.split(".")[0]; + + function processColors( + parsedContent, + theme, + allParsedContent + ) { + if (!["light", "dark"].includes(theme)) { + throw new Error( + `Invalid theme: ${theme}. Expected 'light' or 'dark'.` + ); + } + + const themeColors = parsedContent[theme]; + + function getPaletteName(value) { + const regexMatch = value.match( + /{palette\.(.*?)}/ + ); + if (regexMatch) { + return regexMatch[1]; + } + const rgbaMatch = value.match( + /rgba\(\{palette\.(.*?)\},\s*\d*\.?\d*\)/ + ); + if (rgbaMatch) { + return rgbaMatch[1]; + } + throw new Error( + `Unexpected color format: ${value}` + ); + } + + function getPaletteValue(colorName) { + const paletteValue = + parsedContent.global.palette[colorName] + ?.value; + if (!paletteValue) { + throw new Error( + `Color ${colorName} not found in palette` + ); + } + return paletteValue; + } + + function getMaxStopsAcrossBrands( + allParsedContent, + key + ) { + let maxStops = 0; + let isGradientInAnyBrand = false; + + // Loop through each brand + Object.values(allParsedContent).forEach( + (content) => { + // Check both 'light' and 'dark' themes + ["light", "dark"].forEach((theme) => { + const colors = + content[theme]?.[key]; + if ( + colors && + colors.type === "linear-gradient" + ) { + isGradientInAnyBrand = true; + maxStops = Math.max( + maxStops, + colors.value.colors.length + ); + } + }); + } + ); + + return { maxStops, isGradientInAnyBrand }; + } + + return Object.keys(themeColors).flatMap( + (key) => { + const colorData = themeColors[key]; + const { value, type } = colorData; + + const { + maxStops, + isGradientInAnyBrand, + } = getMaxStopsAcrossBrands( + allParsedContent, + key, + theme + ); + + // Handle gradients first + if ( + type === "linear-gradient" && + typeof value === "object" + ) { + // Map colors in gradient + return Array.from( + { length: maxStops }, + (_, index) => { + if (index < value.colors.length) { + // Use the actual gradient stop color + const color = + value.colors[index]; + const alphaMatch = + color.value.match( + /rgba\([^)]+,\s*([^)]+)\)/ + ); + const alpha = alphaMatch + ? alphaMatch[1] + : "1"; + const baseColorName = + getPaletteName(color.value); + + return alpha === "1" + ? { + name: `${key}-stop-${ + index + 1 + }`, + value: hexToRgba( + getPaletteValue( + baseColorName + ), + parseFloat(alpha) + ), + hasAlias: true, + description: + baseColorName, + } + : { + name: `${key}-stop-${ + index + 1 + }`, + value: hexToRgba( + getPaletteValue( + baseColorName + ), + parseFloat(alpha) + ), + hasAlias: false, + description: + baseColorName, + }; + } else if (isGradientInAnyBrand) { + // If this brand does not have a gradient, repeat the base color to match stops + const baseColorName = + getPaletteName( + value.colors[0].value + ); + const alpha = 1; + return { + name: `${key}-stop-${ + index + 1 + }`, + value: hexToRgba( + getPaletteValue( + baseColorName + ), + parseFloat(alpha) + ), + + hasAlias: false, + description: baseColorName, + }; + } + } + ).filter(Boolean); + } + + // Handle solid colors or aliases when a gradient exists in other brands + if ( + type !== "linear-gradient" && + isGradientInAnyBrand + ) { + const baseColorName = + getPaletteName(value); + const alpha = 1; + // Repeat the solid color to match the gradient stops + return Array.from( + { length: maxStops }, + (_, index) => ({ + name: `${key}-stop-${index + 1}`, + value: hexToRgba( + getPaletteValue(baseColorName), + parseFloat(alpha) + ), + hasAlias: false, + description: baseColorName, + }) + ); + } + + // Handle solid colors or aliases normally + if ( + typeof value === "string" && + !value.startsWith("rgba") + ) { + const baseColorName = + getPaletteName(value); + const alpha = 1; + return { + name: `${key}`, + value: hexToRgba( + getPaletteValue(baseColorName), + parseFloat(alpha) + ), + + hasAlias: true, + description: baseColorName, + }; + } + + // Handle rgba colors + if ( + typeof value === "string" && + value.startsWith("rgba") + ) { + const alphaMatch = value.match( + /rgba\([^)]+,\s*([^)]+)\)/ + ); + const alpha = alphaMatch + ? alphaMatch[1] + : "1"; + const baseColorName = + getPaletteName(value); + + return alpha === "1" + ? { + name: `${key}`, + value: baseColorName, + hasAlias: true, + description: baseColorName, + } + : { + name: `${key}`, + value: hexToRgba( + getPaletteValue( + baseColorName + ), + parseFloat(alpha) + ), + hasAlias: false, + description: baseColorName, + }; + } + + throw new Error( + `Unexpected color format for key: ${key}` + ); + } + ); + } + + // Other token processing logic + const paletteArray = Object.keys( + parsedContent.global.palette + ).map((key) => ({ + name: key, + value: hexToRgba( + parsedContent.global.palette[key].value + ), + })); + + const radiusArray = Object.keys( + parsedContent.radius + ).map((key) => ({ + name: `radii/${key}`, + value: + typeof parsedContent.radius[key].value === + "string" + ? parsedContent.radius[key].value === + "circle" + ? 999 // If the value is "circle", set it to 999 + : parseFloat( + parsedContent.radius[key].value + ) // Otherwise, convert it to a float + : parsedContent.radius[key].value, // If it's not a string, use the original value + })); + + const fontWeightArray = Object.keys( + parsedContent.text.weight + ).map((key) => ({ + name: `fontWeight/${key}`, + value: parsedContent.text.weight[key].value, + })); + + const fontSizeArray = Object.keys( + parsedContent.text.size + ).flatMap((key) => { + const value = + parsedContent.text.size[key].value; + + // Check if the value is an object with mobile and desktop properties + if ( + typeof value === "object" && + value !== null + ) { + return [ + { + name: `mobile/fontSize/${key}`, + value: parseFloat(value.mobile), + }, + { + name: `desktop/fontSize/${key}`, + value: parseFloat(value.desktop), + }, + ]; + } + + // If value is not an object, return a single entry + return { + name: key, + value: parseFloat(value), + }; + }); + + const lineHeightArray = Object.keys( + parsedContent.text.lineHeight + ).flatMap((key) => { + const value = + parsedContent.text.lineHeight[key].value; + + // Check if the value is an object with mobile and desktop properties + if ( + typeof value === "object" && + value !== null + ) { + return [ + { + name: `mobile/lineHeight/${key}`, + value: parseFloat(value.mobile), + }, + { + name: `desktop/lineHeight/${key}`, + value: parseFloat(value.desktop), + }, + ]; + } + + // If value is not an object, return a single entry + return { + name: key, + value: parseFloat(value), + }; + }); + + // Accumulate results + accumulator[fileName] = { + light: processColors( + parsedContent, + "light", + allParsedContent + ), + dark: processColors( + parsedContent, + "dark", + allParsedContent + ), + palette: paletteArray, + radius: radiusArray, + fontWeight: fontWeightArray, + fontSize: fontSizeArray, + lineHeight: lineHeightArray, + }; + + return accumulator; + }, {}); +}; + +export default extractJsonData; diff --git a/tokens/figma/extractJsonData.mjs b/tokens/figma/extract-json-data-skins.mjs similarity index 98% rename from tokens/figma/extractJsonData.mjs rename to tokens/figma/extract-json-data-skins.mjs index 14e7c6aa735..6532a9bdcb7 100644 --- a/tokens/figma/extractJsonData.mjs +++ b/tokens/figma/extract-json-data-skins.mjs @@ -218,9 +218,15 @@ const extractJsonData = ( typeof value === "string" && !value.startsWith("rgba") ) { + const baseColorName = + getPaletteName(value); + return { name: `${theme}/${key}`, - value: getPaletteName(value), + value: hexToRgba( + getPaletteValue(baseColorName) + ), + hasAlias: true, }; } diff --git a/tokens/figma/update-middleware.mjs b/tokens/figma/update-middleware.mjs new file mode 100644 index 00000000000..43011605e4f --- /dev/null +++ b/tokens/figma/update-middleware.mjs @@ -0,0 +1,801 @@ +import fetch from "node-fetch"; +import dotenv from "dotenv"; +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; +import extractJsonData from "./extract-json-data-middleware.mjs"; +import { updateCollections } from "./utils.mjs"; + +dotenv.config({ path: "../../.env" }); +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const FIGMA_TOKEN = process.env.FIGMA_TOKEN; +const MIDDLEWARE_TOKEN = + process.env.MIDDLEWARE_KEY; + +const tokensPath = path.resolve(__dirname, "../"); + +const files = fs.readdirSync(tokensPath); + +const jsonFiles = files.filter((file) => + file.endsWith(".json") +); +const jsonData = extractJsonData( + jsonFiles, + tokensPath +); + +const brands = [ + "movistar", + "vivo-new", + "o2-new", + "telefonica", + "blau", + "tu", +]; + +function formatBrandName(brand) { + // Check if the brand is "tu" and return it in uppercase + if (brand === "tu") { + return brand.toUpperCase(); + } + + // Check if the brand is telefonica and return it as sentence case and with an accent + if (brand === "telefonica") { + return "Telefónica"; + } + + // For other brands, remove the hyphen and convert to sentence case + return brand + .replace(/-/g, " ") // Remove hyphens and replace with spaces + .toLowerCase() // Convert all to lowercase first + .replace(/\b\w/g, (char) => + char.toUpperCase() + ); // Capitalize the first letter of each word +} + +async function updateTheme( + jsonData, + brand, + FILE_KEY +) { + try { + const response = await fetch( + `https://api.figma.com/v1/files/${FILE_KEY}/variables/local`, + { + method: "GET", + headers: { + "X-Figma-Token": FIGMA_TOKEN, + "Content-Type": "application/json", + }, + } + ); + + const figmaData = await response.json(); + const existingVariables = + figmaData.meta.variables; + const existingCollections = + figmaData.meta.variableCollections; + + const newData = { + variableCollections: [], + variableModes: [], + variables: [], + variableModeValues: [], + }; + + function findVariableByName( + variableName, + collectionName + ) { + return Object.values( + existingVariables + ).find((variable) => { + const collection = + existingCollections[ + variable.variableCollectionId + ]; + return ( + collection && + collection.name === collectionName && + variable.name === variableName + ); + }); + } + + function findModeByName( + collection, + modeName + ) { + return collection.modes.find( + (mode) => mode.name === modeName + ); + } + + function generateUniqueTempId( + name, + collection + ) { + return `tempId_${collection}_${name}_${Date.now()}`; + } + + const updateModes = (collectionName) => { + const collection = Object.values( + existingCollections + ).find( + (col) => col.name === collectionName + ); + if (!collection) return; + + const defaultMode = collection.modes.find( + (mode) => + mode.modeId === collection.defaultModeId + ); + if ( + defaultMode && + defaultMode.name !== "Light" + ) { + newData.variableModes.push({ + action: "UPDATE", + id: defaultMode.modeId, + variableCollectionId: collection.id, + name: "Light", + }); + } + + const darkMode = findModeByName( + collection, + "Dark" + ); + if (!darkMode) { + newData.variableModes.push({ + action: "CREATE", + id: "Dark", + variableCollectionId: collection.id, + name: "Dark", + }); + } + }; + + updateModes("Theme"); + + function createOrUpdateVariables( + lightVariables, + darkVariables, + collectionName + ) { + const collection = Object.values( + existingCollections + ).find( + (col) => col.name === collectionName + ); + if (!collection) { + console.error( + `Collection ${collectionName} not found` + ); + return; + } + + const lightMode = + findModeByName(collection, "Light") || + collection.defaultModeId; + const darkMode = findModeByName( + collection, + "Dark" + ); + + lightVariables.forEach((lightVariable) => { + const prefixedName = `${brand}/${lightVariable.name}`; + const darkVariable = darkVariables.find( + (v) => v.name === lightVariable.name + ); + + const existingVariable = + findVariableByName( + prefixedName, + collectionName + ); + + if (existingVariable) { + newData.variables.push({ + action: "UPDATE", + id: existingVariable.id, + name: prefixedName, + variableCollectionId: + existingVariable.variableCollectionId, + resolvedType: "COLOR", + description: `light: ${lightVariable.description}, dark: ${darkVariable?.description}`, + scopes: [], + }); + + newData.variableModeValues.push({ + action: "UPDATE", + variableId: existingVariable.id, + modeId: lightMode.modeId || lightMode, + value: lightVariable.value, + }); + + if (darkVariable) { + newData.variableModeValues.push({ + action: "UPDATE", + variableId: existingVariable.id, + modeId: darkMode?.modeId, + value: darkVariable.value, + }); + } + } else { + const tempId = generateUniqueTempId( + lightVariable.name, + collectionName + ); + + newData.variables.push({ + action: "CREATE", + id: tempId, + name: prefixedName, + variableCollectionId: collection.id, + resolvedType: "COLOR", + }); + + newData.variableModeValues.push({ + action: "CREATE", + variableId: tempId, + modeId: lightMode.modeId || lightMode, + value: lightVariable.value, + }); + + if (darkVariable) { + newData.variableModeValues.push({ + action: "CREATE", + variableId: tempId, + modeId: darkMode?.modeId || "Dark", + value: darkVariable.value, + }); + } + } + }); + } + + createOrUpdateVariables( + jsonData[brand].light, + jsonData[brand].dark, + "Theme" + ); + + const updateResponse = await fetch( + `https://api.figma.com/v1/files/${FILE_KEY}/variables`, + { + method: "POST", + headers: { + "X-Figma-Token": FIGMA_TOKEN, + "Content-Type": "application/json", + }, + body: JSON.stringify(newData), + } + ); + + if (!updateResponse.ok) { + const errorText = + await updateResponse.text(); + throw new Error( + `Error updating variables and modes: ${updateResponse.statusText}. Response: ${errorText}` + ); + } + + return newData; + } catch (error) { + console.error("Error:", error); + throw error; + } +} + +async function updateSkinColorVariables( + brands, + FILE_KEY +) { + try { + // Step 1: Fetch existing data from "Theme" and "Skin" collections + const response = await fetch( + `https://api.figma.com/v1/files/${FILE_KEY}/variables/local`, + { + method: "GET", + headers: { + "X-Figma-Token": FIGMA_TOKEN, + "Content-Type": "application/json", + }, + } + ); + + const figmaData = await response.json(); + const themeCollections = + figmaData.meta.variableCollections; + + // Step 2: Find the "Theme" and "Skin" collections + const themeCollection = Object.values( + themeCollections + ).find((col) => col.name === "Theme"); + const skinCollection = Object.values( + themeCollections + ).find((col) => col.name === "Skin"); + + if (!themeCollection || !skinCollection) { + throw new Error( + "Theme or Skin collection not found" + ); + } + + // Step 3: Filter theme variables to only include those from the "Theme" collection + const themeVariables = + figmaData.meta.variables || {}; + const existingThemeVariables = Object.values( + themeVariables + ).filter( + (variable) => + variable.variableCollectionId === + themeCollection.id + ); + + const existingSkinVariables = Object.values( + themeVariables + ).filter( + (variable) => + variable.variableCollectionId === + skinCollection.id + ); + + // Step 4: Prepare new variables data for the Skin collection + const newData = { + variables: [], + variableModeValues: [], + variableModes: [], + }; + + // Step 5: Create or update modes based on the brands + + ///////////////////////////// + + // Define the firstBrand before using it + const firstBrand = brands[0]; + const formattedFirstBrand = + formatBrandName(firstBrand); + + const defaultMode = + skinCollection.modes?.find( + (mode) => + mode.name === "Mode 1" || + mode.name === firstBrand || + mode.name === formattedFirstBrand + ); + + // Check if the default mode needs to be updated + if (defaultMode) { + // If the mode name is unformatted, update it to the formatted one + if ( + defaultMode.name === firstBrand || + defaultMode.name === "Mode 1" + ) { + newData.variableModes.push({ + action: "UPDATE", + id: defaultMode.modeId, + name: formattedFirstBrand, + variableCollectionId: skinCollection.id, + }); + } + } else { + // If no matching mode exists, create a new one + newData.variableModes.push({ + action: "CREATE", + name: formattedFirstBrand, + id: `tempId_${firstBrand}`, // Ensure the id is unique + variableCollectionId: skinCollection.id, + }); + } + + // Create or update additional modes for the remaining brands + brands.slice(1).forEach((brand) => { + const formattedBrand = + formatBrandName(brand); + + // Find an existing mode that matches either the unformatted or formatted name + const existingMode = + skinCollection.modes.find( + (mode) => + mode.name === brand || + mode.name === formattedBrand + ); + + if (existingMode) { + // If the mode name matches the unformatted brand name, update it + if (existingMode.name === brand) { + newData.variableModes.push({ + action: "UPDATE", + id: existingMode.modeId, + name: formattedBrand, + variableCollectionId: + skinCollection.id, + }); + } + } else { + // If no mode exists for the brand, create a new one + newData.variableModes.push({ + action: "CREATE", + name: formattedBrand, + id: `tempId_${brand}`, // Ensure the id is unique + variableCollectionId: skinCollection.id, + }); + } + }); + + ///////////////////////////// + + // Step 6: Create a map for color variables from Theme + const variableToBrandMap = new Map(); + + existingThemeVariables.forEach((variable) => { + if (variable.resolvedType === "COLOR") { + const variableName = variable.name + .split("/") + .pop(); + if ( + !variableToBrandMap.has(variableName) + ) { + variableToBrandMap.set( + variableName, + {} + ); + } + const brand = variable.name.split("/")[0]; + variableToBrandMap.get(variableName)[ + brand + ] = variable.id; + } + }); + + // Step 7: Create or update only color variables in the Skin collection + variableToBrandMap.forEach( + (brandMap, variableName) => { + let variableId; + + // Check if the variable already exists in the Skin collection and is a color variable + const existingVariable = + existingSkinVariables.find( + (variable) => + variable.name === variableName && + variable.resolvedType === "COLOR" + ); + + if (existingVariable) { + // Variable exists, use its ID for update + variableId = existingVariable.id; + newData.variables.push({ + action: "UPDATE", + id: variableId, + name: variableName, + variableCollectionId: + skinCollection.id, + resolvedType: "COLOR", + }); + } else { + // Variable does not exist, create it + variableId = `tempId_${skinCollection.id}_${variableName}`; + newData.variables.push({ + action: "CREATE", + id: variableId, + name: variableName, + variableCollectionId: + skinCollection.id, + resolvedType: "COLOR", + }); + } + + // Step 8: Update mode values with the correct aliases for each brand + brands.forEach((brand) => { + const mode = skinCollection.modes?.find( + (mode) => + mode.name === formatBrandName(brand) + ); + + const defaultMode = + skinCollection.modes?.find( + (mode) => mode.name === "Mode 1" + ); + + if (mode) { + newData.variableModeValues.push({ + action: "CREATE", + variableId: variableId, + modeId: mode.modeId, + value: { + type: "VARIABLE_ALIAS", + id: brandMap[brand], // Alias to the Theme variable ID for the brand + }, + }); + } else { + newData.variableModeValues.push({ + action: "CREATE", + variableId: variableId, + modeId: + brand === "movistar" + ? defaultMode.modeId + : `tempId_${brand}`, // Alias to the Theme variable ID for the brand + value: { + type: "VARIABLE_ALIAS", + id: brandMap[brand], + }, + }); + } + }); + } + ); + + // Step 9: Send the data to update the Skin collection (POST) + const updateResponse = await fetch( + `https://api.figma.com/v1/files/${FILE_KEY}/variables`, + { + method: "POST", + headers: { + "X-Figma-Token": FIGMA_TOKEN, + "Content-Type": "application/json", + }, + body: JSON.stringify(newData), + } + ); + + if (!updateResponse.ok) { + const errorText = + await updateResponse.text(); + throw new Error( + `Error updating Skin collection: ${updateResponse.statusText}. Response: ${errorText}` + ); + } + + return newData; // Returning newData for debugging + } catch (error) { + console.error("Error:", error); + throw error; + } +} + +async function updateSkinOtherVariables( + jsonData, + brands, + FILE_KEY +) { + const response = await fetch( + `https://api.figma.com/v1/files/${FILE_KEY}/variables/local`, + { + method: "GET", + headers: { + "X-Figma-Token": FIGMA_TOKEN, + "Content-Type": "application/json", + }, + } + ); + + const figmaData = await response.json(); + const existingVariables = + figmaData.meta.variables; + const existingCollections = + figmaData.meta.variableCollections; + const skinCollection = Object.values( + existingCollections + ).find((col) => col.name === "Skin"); + + if (!skinCollection) { + throw new Error("Skin collection not found."); + } + + const newData = { + variables: [], + variableModeValues: [], + }; + + const fontFamilies = { + movistar: "On Air", + "vivo-new": "Vivo type", + "o2-new": "On Air", + telefonica: "Telefonica Sans", + blau: "SF Pro Text", + tu: "Telefonica Sans", + }; + + // Map to store variable IDs for later use + const variableIdMap = new Map(); + + // Loop through each brand to process its specific tokens + for (const brand of brands) { + const variableGroups = [ + { + variables: jsonData[brand]?.radius || [], + collectionName: "Skin", + resolvedType: "FLOAT", + variableScopes: ["CORNER_RADIUS"], + }, + { + variables: + jsonData[brand]?.fontWeight || [], + collectionName: "Skin", + resolvedType: "STRING", + variableScopes: ["FONT_WEIGHT"], + }, + { + variables: + jsonData[brand]?.fontSize || [], + collectionName: "Skin", + resolvedType: "FLOAT", + variableScopes: ["FONT_SIZE"], + }, + { + variables: + jsonData[brand]?.lineHeight || [], + collectionName: "Skin", + resolvedType: "FLOAT", + variableScopes: ["LINE_HEIGHT"], + }, + { + variables: [ + { + name: "fontFamily/fontFamily", + value: fontFamilies[brand], + }, + ], + collectionName: "Skin", + resolvedType: "STRING", + variableScopes: ["FONT_FAMILY"], + }, + ]; + + for (const group of variableGroups) { + const { + variables, + resolvedType, + variableScopes, + } = group; + + variables.forEach((variable) => { + const variableName = variable.name; + const variableValue = variable.value; + const tempId = `tempId_${skinCollection.id}_${variableName}`; + + let existingVariable = Object.values( + existingVariables + ).find( + (v) => + v.name === variableName && + v.variableCollectionId === + skinCollection.id + ); + + // Create or update the variable + if (!existingVariable) { + newData.variables.push({ + action: "CREATE", + id: tempId, + name: variableName, + variableCollectionId: + skinCollection.id, + resolvedType: resolvedType, + scopes: variableScopes, + }); + variableIdMap.set(variableName, tempId); + } else { + newData.variables.push({ + action: "UPDATE", + id: existingVariable.id, + name: variableName, + variableCollectionId: + skinCollection.id, + resolvedType: resolvedType, + scopes: variableScopes, + }); + variableIdMap.set( + variableName, + existingVariable.id + ); + } + + // Find the mode for the current brand and set the value correctly + const mode = skinCollection.modes.find( + (m) => m.name === formatBrandName(brand) + ); + if (mode) { + newData.variableModeValues.push({ + variableId: + variableIdMap.get(variableName), + modeId: mode.modeId, + value: variableValue, + }); + } + }); + } + } + + // Make the POST request to update the variables and mode values in the Skin collection + const updateResponse = await fetch( + `https://api.figma.com/v1/files/${FILE_KEY}/variables`, + { + method: "POST", + headers: { + "X-Figma-Token": FIGMA_TOKEN, + "Content-Type": "application/json", + }, + body: JSON.stringify(newData), + } + ); + + if (!updateResponse.ok) { + const errorText = await updateResponse.text(); + throw new Error( + `Error updating Skin collection: ${updateResponse.statusText}. Response: ${errorText}` + ); + } + + return newData; +} + +async function postCollections(brand, FILE_KEY) { + const collectionNames = ["Skin", "Theme"]; + + try { + const newData = await updateCollections( + collectionNames, + FILE_KEY, + FIGMA_TOKEN + ); + + const response = await fetch( + `https://api.figma.com/v1/files/${FILE_KEY}/variables/`, + { + method: "POST", + headers: { + "X-Figma-Token": FIGMA_TOKEN, + "Content-Type": "application/json", + }, + body: JSON.stringify(newData), + } + ); + + const data = await response.json(); + console.log( + `Success creating collections for brand ${brand}:`, + data + ); + } catch (error) { + console.error( + `Error creating collections for brand ${brand}:`, + error + ); + } +} + +async function processBrand(brand, FILE_KEY) { + await postCollections(brand, FILE_KEY); + await updateTheme(jsonData, brand, FILE_KEY); +} + +async function processAllBrands(brands) { + for (const brand of brands) { + await processBrand(brand, MIDDLEWARE_TOKEN); + } +} + +async function main() { + await updateSkinColorVariables( + brands, + MIDDLEWARE_TOKEN + ); + await updateSkinOtherVariables( + jsonData, + brands, + MIDDLEWARE_TOKEN + ); +} + +// Execute the main function to ensure proper sequence +main().catch((error) => { + console.error( + "Error in main execution:", + error + ); +}); diff --git a/tokens/figma/index.mjs b/tokens/figma/update-skins.mjs similarity index 75% rename from tokens/figma/index.mjs rename to tokens/figma/update-skins.mjs index 1b40a935505..d763ccb3712 100644 --- a/tokens/figma/index.mjs +++ b/tokens/figma/update-skins.mjs @@ -3,7 +3,8 @@ import dotenv from "dotenv"; import fs from "fs"; import path from "path"; import { fileURLToPath } from "url"; -import extractJsonData from "./extractJsonData.mjs"; +import extractJsonData from "./extract-json-data-skins.mjs"; +import { updateCollections } from "./utils.mjs"; dotenv.config({ path: "../../.env" }); const __filename = fileURLToPath(import.meta.url); @@ -13,12 +14,8 @@ const FIGMA_TOKEN = process.env.FIGMA_TOKEN; const FILE_KEYS = { // Remember to sync these with the workflow file - movistar: process.env.MOVISTAR_FILE_KEY, - "o2-new": process.env.O2_NEW_FILE_KEY, - "vivo-new": process.env.VIVO_NEW_FILE_KEY, - telefonica: process.env.TELEFONICA_FILE_KEY, - blau: process.env.BLAU_FILE_KEY, - tu: process.env.TU_FILE_KEY, + blau: process.env.FILE_KEY_1, + "o2-new": process.env.FILE_KEY_2, }; const tokensPath = path.resolve(__dirname, "../"); @@ -36,109 +33,35 @@ const jsonData = extractJsonData( const brands = Object.fromEntries( Object.entries(FILE_KEYS).map( - ([brand, FILE_KEY]) => [ - brand, - `https://api.figma.com/v1/files/${FILE_KEY}/variables`, - ] + ([brand, FILE_KEY]) => [brand, FILE_KEY] ) ); -async function updateCollections(url) { - try { - const response = await fetch(`${url}/local`, { - method: "GET", - headers: { - "X-Figma-Token": FIGMA_TOKEN, - "Content-Type": "application/json", - }, - }); - - const figmaData = await response.json(); - - const newData = { - variableCollections: [], - variableModes: [], - variables: [], - variableModeValues: [], - }; - - const existingCollections = - figmaData.meta.variableCollections; - - const collectionNames = [ - "constants", - "palette", - "font-weight", - "font-size", - "line-height", - "radius", - ]; - - function generateTempId(name) { - return `tempId_${name}`; - } - - function updateCollection( - collectionName, - existingCollections - ) { - // Find the existing collection by name - const existingCollection = Object.values( - existingCollections - ).find( - (collection) => - collection.name === collectionName - ); - - if (existingCollection) { - // If the collection exists, update it - newData.variableCollections.push({ - action: "UPDATE", - id: existingCollection.id, - name: collectionName, - }); - } else { - // If the collection doesn't exist, create it - const tempId = generateTempId( - collectionName - ); - newData.variableCollections.push({ - action: "CREATE", - id: tempId, - name: collectionName, - }); - } - } - - // Process each collection name - collectionNames.forEach((collectionName) => { - updateCollection( - collectionName, - existingCollections - ); - }); - - // Return the processed data for further use - return newData; - } catch (error) { - console.error("Error:", error); - throw error; // rethrow the error to be handled later - } -} +const collectionNames = [ + "constants", + "palette", + "font-weight", + "font-size", + "line-height", + "radius", +]; async function updatePalette( jsonData, brand, - url + FILE_KEY ) { try { - const response = await fetch(`${url}/local`, { - method: "GET", - headers: { - "X-Figma-Token": FIGMA_TOKEN, - "Content-Type": "application/json", - }, - }); + const response = await fetch( + `https://api.figma.com/v1/files/${FILE_KEY}/variables/local`, + { + method: "GET", + headers: { + "X-Figma-Token": FIGMA_TOKEN, + "Content-Type": "application/json", + }, + } + ); const figmaData = await response.json(); @@ -317,16 +240,19 @@ async function updatePalette( async function updateVariables( jsonData, brand, - url + FILE_KEY ) { try { - const response = await fetch(`${url}/local`, { - method: "GET", - headers: { - "X-Figma-Token": FIGMA_TOKEN, // Use environment variable - "Content-Type": "application/json", - }, - }); + const response = await fetch( + `https://api.figma.com/v1/files/${FILE_KEY}/variables/local`, + { + method: "GET", + headers: { + "X-Figma-Token": FIGMA_TOKEN, // Use environment variable + "Content-Type": "application/json", + }, + } + ); const figmaData = await response.json(); @@ -383,49 +309,48 @@ async function updateVariables( }); } - function getPaletteAlias( + function getVariableAliasId( variableName, + collectionName, existingVariables, existingCollections ) { - // Step 1: Find the "palette" collection - const paletteCollection = Object.values( + // Step 1: Find the collection + const collection = Object.values( existingCollections ).find( (collection) => - collection.name === "palette" + collection.name === collectionName ); - if (!paletteCollection) { - console.error( - "Palette collection not found." - ); + if (!collection) { + console.error("Collection not found."); return null; } // Step 2: Find the variable in the "palette" collection - const paletteVariable = Object.values( + const variable = Object.values( existingVariables ).find( (variable) => variable.variableCollectionId === - paletteCollection.id && + collection.id && variable.name === variableName ); - if (!paletteVariable) { + if (!variable) { console.error( - "Palette variable not found for name:", + "Variable not found for name:", variableName ); return null; } // Step 3: Retrieve the mode ID for the "palette" collection - const modeId = paletteCollection.id; + const modeId = collection.id; return { - variableId: paletteVariable.id, + variableId: variable.id, modeId: modeId, }; } @@ -435,6 +360,7 @@ async function updateVariables( variableValue, hasAlias, collectionName, + aliasedCollectionName, existingVariables, existingCollections, variableType, @@ -477,8 +403,9 @@ async function updateVariables( value: hasAlias ? { type: "VARIABLE_ALIAS", - id: getPaletteAlias( + id: getVariableAliasId( variableValue, + aliasedCollectionName, existingVariables, existingCollections ).variableId, @@ -518,7 +445,7 @@ async function updateVariables( value: hasAlias ? { type: "VARIABLE_ALIAS", - id: getPaletteAlias( + id: getVariableAliasId( variableValue, existingVariables, existingCollections @@ -569,12 +496,14 @@ async function updateVariables( collectionName: "constants", resolvedType: "COLOR", variableScopes: ["ALL_SCOPES"], + aliasedCollectionName: "palette", }, { variables: jsonData[brand].dark, collectionName: "constants", resolvedType: "COLOR", variableScopes: ["ALL_SCOPES"], + aliasedCollectionName: "palette", }, { variables: jsonData[brand].radius, @@ -606,6 +535,7 @@ async function updateVariables( ({ variables, collectionName, + aliasedCollectionName, resolvedType, variableScopes, }) => { @@ -615,6 +545,7 @@ async function updateVariables( variable.value, variable.hasAlias, collectionName, + aliasedCollectionName, existingVariables, existingCollections, resolvedType, @@ -640,18 +571,25 @@ async function updateVariables( // Use an async function to handle the post request after data processing -async function postCollections(url, brand) { +async function postCollections(brand, FILE_KEY) { try { - const newData = await updateCollections(url); + const newData = await updateCollections( + collectionNames, + FILE_KEY, + FIGMA_TOKEN + ); - const response = await fetch(url, { - method: "POST", - headers: { - "X-Figma-Token": FIGMA_TOKEN, - "Content-Type": "application/json", - }, - body: JSON.stringify(newData), - }); + const response = await fetch( + `https://api.figma.com/v1/files/${FILE_KEY}/variables/`, + { + method: "POST", + headers: { + "X-Figma-Token": FIGMA_TOKEN, + "Content-Type": "application/json", + }, + body: JSON.stringify(newData), + } + ); const data = await response.json(); console.log( @@ -666,22 +604,25 @@ async function postCollections(url, brand) { } } -async function postPalette(url, brand) { +async function postPalette(brand, FILE_KEY) { try { const newData = await updatePalette( jsonData, brand, - url + FILE_KEY ); - const response = await fetch(url, { - method: "POST", - headers: { - "X-Figma-Token": FIGMA_TOKEN, - "Content-Type": "application/json", - }, - body: JSON.stringify(newData), - }); + const response = await fetch( + `https://api.figma.com/v1/files/${FILE_KEY}/variables/`, + { + method: "POST", + headers: { + "X-Figma-Token": FIGMA_TOKEN, + "Content-Type": "application/json", + }, + body: JSON.stringify(newData), + } + ); const data = await response.json(); console.log( @@ -696,22 +637,25 @@ async function postPalette(url, brand) { } } -async function postVariables(url, brand) { +async function postVariables(brand, FILE_KEY) { try { const newData = await updateVariables( jsonData, brand, - url + FILE_KEY ); - const response = await fetch(url, { - method: "POST", - headers: { - "X-Figma-Token": FIGMA_TOKEN, - "Content-Type": "application/json", - }, - body: JSON.stringify(newData), - }); + const response = await fetch( + `https://api.figma.com/v1/files/${FILE_KEY}/variables/`, + { + method: "POST", + headers: { + "X-Figma-Token": FIGMA_TOKEN, + "Content-Type": "application/json", + }, + body: JSON.stringify(newData), + } + ); const data = await response.json(); console.log( @@ -728,19 +672,19 @@ async function postVariables(url, brand) { // Process data for a specific brand -async function processBrand(brand, url) { - await postCollections(url, brand); - await postPalette(url, brand); - await postVariables(url, brand); +async function processBrand(brand, FILE_KEY) { + await postCollections(brand, FILE_KEY); + await postPalette(brand, FILE_KEY); + await postVariables(brand, FILE_KEY); } // Process all brands async function processAllBrands(brands) { - for (const [brand, url] of Object.entries( + for (const [brand, FILE_KEY] of Object.entries( brands )) { - await processBrand(brand, url); + await processBrand(brand, FILE_KEY); } } @@ -751,9 +695,9 @@ const selectedBrand = process.argv[2]; if (selectedBrand === "all") { processAllBrands(brands); } else { - const url = brands[selectedBrand]; - if (url) { - processBrand(selectedBrand, url); + const FILE_KEY = brands[selectedBrand]; + if (FILE_KEY) { + processBrand(selectedBrand, FILE_KEY); } else { console.error( `Brand ${selectedBrand} not found.` diff --git a/tokens/figma/utils.mjs b/tokens/figma/utils.mjs index 7f49cd36f2c..990dc2b55ab 100644 --- a/tokens/figma/utils.mjs +++ b/tokens/figma/utils.mjs @@ -24,3 +24,81 @@ export function hexToRgba(hex, alpha = 1) { a: alpha, }; } + +export async function updateCollections( + collections, + FILE_KEY, + FIGMA_TOKEN +) { + try { + const response = await fetch( + `https://api.figma.com/v1/files/${FILE_KEY}/variables/local`, + { + method: "GET", + headers: { + "X-Figma-Token": FIGMA_TOKEN, + "Content-Type": "application/json", + }, + } + ); + + const figmaData = await response.json(); + + const newData = { + variableCollections: [], + }; + + const existingCollections = + figmaData.meta.variableCollections; + + function generateTempId(name) { + return `tempId_${name}`; + } + + function updateCollection( + collectionName, + existingCollections + ) { + // Find the existing collection by name + const existingCollection = Object.values( + existingCollections + ).find( + (collection) => + collection.name === collectionName + ); + + if (existingCollection) { + // If the collection exists, update it + newData.variableCollections.push({ + action: "UPDATE", + id: existingCollection.id, + name: collectionName, + }); + } else { + // If the collection doesn't exist, create it + const tempId = generateTempId( + collectionName + ); + newData.variableCollections.push({ + action: "CREATE", + id: tempId, + name: collectionName, + }); + } + } + + // Process each collection name + collections.forEach((collection) => { + updateCollection( + collection, + existingCollections + ); + }); + + // Return the processed data for further use + return newData; + } catch (error) { + console.error("Error:", error); + throw error; // rethrow the error to be handled later + } +} From 097de09ffe91e4cad2f57ee3d1a89d332f6c5b5c Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Wed, 11 Sep 2024 14:40:36 +0200 Subject: [PATCH 34/71] Update skins to export only the palette collection --- tokens/figma/update-skins.mjs | 83 +---------------------------------- tokens/figma/utils.mjs | 14 ++++++ 2 files changed, 16 insertions(+), 81 deletions(-) diff --git a/tokens/figma/update-skins.mjs b/tokens/figma/update-skins.mjs index d763ccb3712..9ee9c903145 100644 --- a/tokens/figma/update-skins.mjs +++ b/tokens/figma/update-skins.mjs @@ -37,14 +37,7 @@ const brands = Object.fromEntries( ) ); -const collectionNames = [ - "constants", - "palette", - "font-weight", - "font-size", - "line-height", - "radius", -]; +const collectionNames = ["Palette"]; async function updatePalette( jsonData, @@ -202,7 +195,7 @@ async function updatePalette( const variableGroups = [ { variables: jsonData[brand].palette, - collectionName: "palette", + collectionName: "Palette", resolvedType: "COLOR", variableScopes: ["ALL_SCOPES"], }, @@ -491,44 +484,6 @@ async function updateVariables( resolvedType: "COLOR", variableScopes: ["ALL_SCOPES"], }, - { - variables: jsonData[brand].light, - collectionName: "constants", - resolvedType: "COLOR", - variableScopes: ["ALL_SCOPES"], - aliasedCollectionName: "palette", - }, - { - variables: jsonData[brand].dark, - collectionName: "constants", - resolvedType: "COLOR", - variableScopes: ["ALL_SCOPES"], - aliasedCollectionName: "palette", - }, - { - variables: jsonData[brand].radius, - collectionName: "radius", - resolvedType: "FLOAT", - variableScopes: ["CORNER_RADIUS"], - }, - { - variables: jsonData[brand].fontWeight, - collectionName: "font-weight", - resolvedType: "STRING", - variableScopes: ["FONT_WEIGHT"], - }, - { - variables: jsonData[brand].fontSize, - collectionName: "font-size", - resolvedType: "FLOAT", - variableScopes: ["FONT_SIZE"], - }, - { - variables: jsonData[brand].lineHeight, - collectionName: "line-height", - resolvedType: "FLOAT", - variableScopes: ["LINE_HEIGHT"], - }, ]; variableGroups.forEach( @@ -637,45 +592,11 @@ async function postPalette(brand, FILE_KEY) { } } -async function postVariables(brand, FILE_KEY) { - try { - const newData = await updateVariables( - jsonData, - brand, - FILE_KEY - ); - - const response = await fetch( - `https://api.figma.com/v1/files/${FILE_KEY}/variables/`, - { - method: "POST", - headers: { - "X-Figma-Token": FIGMA_TOKEN, - "Content-Type": "application/json", - }, - body: JSON.stringify(newData), - } - ); - - const data = await response.json(); - console.log( - `Success updating variables for brand ${brand}:`, - data - ); - } catch (error) { - console.error( - `Error updating variables for brand ${brand}:`, - error - ); - } -} - // Process data for a specific brand async function processBrand(brand, FILE_KEY) { await postCollections(brand, FILE_KEY); await postPalette(brand, FILE_KEY); - await postVariables(brand, FILE_KEY); } // Process all brands diff --git a/tokens/figma/utils.mjs b/tokens/figma/utils.mjs index 990dc2b55ab..d4a37aeebfc 100644 --- a/tokens/figma/utils.mjs +++ b/tokens/figma/utils.mjs @@ -95,6 +95,20 @@ export async function updateCollections( ); }); + const collectionsToDelete = Object.values( + existingCollections + ).filter( + (collection) => + !collections.includes(collection.name) + ); + + collectionsToDelete.forEach((collection) => { + newData.variableCollections.push({ + action: "DELETE", + id: collection.id, + }); + }); + // Return the processed data for further use return newData; } catch (error) { From 43afa260fca5c29e60f86b8323dd2187a7c1a44d Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Wed, 11 Sep 2024 14:51:53 +0200 Subject: [PATCH 35/71] Add iconSet variable generation and update filekeys for palette --- tokens/figma/update-middleware.mjs | 20 ++++++++++++++++++++ tokens/figma/update-skins.mjs | 8 ++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/tokens/figma/update-middleware.mjs b/tokens/figma/update-middleware.mjs index 43011605e4f..2d8a6b42d5f 100644 --- a/tokens/figma/update-middleware.mjs +++ b/tokens/figma/update-middleware.mjs @@ -600,6 +600,15 @@ async function updateSkinOtherVariables( tu: "Telefonica Sans", }; + const iconSets = { + movistar: "Default", + "vivo-new": "Vivo", + "o2-new": "O2", + telefonica: "Default", + blau: "Blau", + tu: "Default", + }; + // Map to store variable IDs for later use const variableIdMap = new Map(); @@ -644,6 +653,17 @@ async function updateSkinOtherVariables( resolvedType: "STRING", variableScopes: ["FONT_FAMILY"], }, + { + variables: [ + { + name: "icons/iconSet", + value: iconSets[brand], + }, + ], + collectionName: "Skin", + resolvedType: "STRING", + variableScopes: ["ALL_SCOPES"], + }, ]; for (const group of variableGroups) { diff --git a/tokens/figma/update-skins.mjs b/tokens/figma/update-skins.mjs index 9ee9c903145..74ac2d282b4 100644 --- a/tokens/figma/update-skins.mjs +++ b/tokens/figma/update-skins.mjs @@ -14,8 +14,12 @@ const FIGMA_TOKEN = process.env.FIGMA_TOKEN; const FILE_KEYS = { // Remember to sync these with the workflow file - blau: process.env.FILE_KEY_1, - "o2-new": process.env.FILE_KEY_2, + movistar: process.env.MOVISTAR_FILE_KEY, + "o2-new": process.env.O2_NEW_FILE_KEY, + "vivo-new": process.env.VIVO_NEW_FILE_KEY, + telefonica: process.env.TELEFONICA_FILE_KEY, + blau: process.env.BLAU_FILE_KEY, + tu: process.env.TU_FILE_KEY, }; const tokensPath = path.resolve(__dirname, "../"); From fc143e86387ad78261e5a7371326ed0b823fd446 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Wed, 11 Sep 2024 18:31:39 +0200 Subject: [PATCH 36/71] Extract logic to reuse --- tokens/figma/update-middleware.mjs | 117 +++++++++++++-------------- tokens/figma/utils.mjs | 124 +++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+), 62 deletions(-) diff --git a/tokens/figma/update-middleware.mjs b/tokens/figma/update-middleware.mjs index 2d8a6b42d5f..992695c48c1 100644 --- a/tokens/figma/update-middleware.mjs +++ b/tokens/figma/update-middleware.mjs @@ -4,7 +4,11 @@ import fs from "fs"; import path from "path"; import { fileURLToPath } from "url"; import extractJsonData from "./extract-json-data-middleware.mjs"; -import { updateCollections } from "./utils.mjs"; +import { + updateCollections, + updateOrCreateVariable, + updateOrCreateVariableModeValues, +} from "./utils.mjs"; dotenv.config({ path: "../../.env" }); const __filename = fileURLToPath(import.meta.url); @@ -578,13 +582,6 @@ async function updateSkinOtherVariables( figmaData.meta.variables; const existingCollections = figmaData.meta.variableCollections; - const skinCollection = Object.values( - existingCollections - ).find((col) => col.name === "Skin"); - - if (!skinCollection) { - throw new Error("Skin collection not found."); - } const newData = { variables: [], @@ -609,9 +606,6 @@ async function updateSkinOtherVariables( tu: "Default", }; - // Map to store variable IDs for later use - const variableIdMap = new Map(); - // Loop through each brand to process its specific tokens for (const brand of brands) { const variableGroups = [ @@ -620,6 +614,7 @@ async function updateSkinOtherVariables( collectionName: "Skin", resolvedType: "FLOAT", variableScopes: ["CORNER_RADIUS"], + hasAlias: false, }, { variables: @@ -627,6 +622,7 @@ async function updateSkinOtherVariables( collectionName: "Skin", resolvedType: "STRING", variableScopes: ["FONT_WEIGHT"], + hasAlias: false, }, { variables: @@ -634,6 +630,7 @@ async function updateSkinOtherVariables( collectionName: "Skin", resolvedType: "FLOAT", variableScopes: ["FONT_SIZE"], + hasAlias: false, }, { variables: @@ -641,6 +638,7 @@ async function updateSkinOtherVariables( collectionName: "Skin", resolvedType: "FLOAT", variableScopes: ["LINE_HEIGHT"], + hasAlias: false, }, { variables: [ @@ -652,82 +650,75 @@ async function updateSkinOtherVariables( collectionName: "Skin", resolvedType: "STRING", variableScopes: ["FONT_FAMILY"], + hasAlias: false, }, { variables: [ { - name: "icons/iconSet", + name: "icons/Icon Set", value: iconSets[brand], }, ], collectionName: "Skin", resolvedType: "STRING", variableScopes: ["ALL_SCOPES"], + hasAlias: false, }, ]; for (const group of variableGroups) { const { variables, + collectionName, resolvedType, variableScopes, + hasAlias, } = group; - variables.forEach((variable) => { - const variableName = variable.name; - const variableValue = variable.value; - const tempId = `tempId_${skinCollection.id}_${variableName}`; - - let existingVariable = Object.values( - existingVariables - ).find( - (v) => - v.name === variableName && - v.variableCollectionId === - skinCollection.id + for (const variable of variables) { + // Update or create the variable in the collection + const variableUpdateResult = + await updateOrCreateVariable({ + variable: { + ...variable, + resolvedType: resolvedType, + scopes: variableScopes, + hasAlias: hasAlias, + }, + targetCollectionName: collectionName, + existingVariables: existingVariables, + existingCollections: + existingCollections, + }); + + if (!newData.variables) { + newData.variables = []; + } + newData.variables.push( + variableUpdateResult ); - // Create or update the variable - if (!existingVariable) { - newData.variables.push({ - action: "CREATE", - id: tempId, - name: variableName, - variableCollectionId: - skinCollection.id, - resolvedType: resolvedType, - scopes: variableScopes, + // Find the mode for the current brand and set the mode values correctly + const variableModeValuesUpdatedResult = + await updateOrCreateVariableModeValues({ + variable: { + ...variable, + resolvedType: resolvedType, + scopes: variableScopes, + hasAlias: hasAlias, + }, + targetModeName: + formatBrandName(brand), + targetCollectionName: collectionName, + existingCollections: + existingCollections, + existingVariables: existingVariables, }); - variableIdMap.set(variableName, tempId); - } else { - newData.variables.push({ - action: "UPDATE", - id: existingVariable.id, - name: variableName, - variableCollectionId: - skinCollection.id, - resolvedType: resolvedType, - scopes: variableScopes, - }); - variableIdMap.set( - variableName, - existingVariable.id - ); - } - // Find the mode for the current brand and set the value correctly - const mode = skinCollection.modes.find( - (m) => m.name === formatBrandName(brand) + newData.variableModeValues.push( + variableModeValuesUpdatedResult ); - if (mode) { - newData.variableModeValues.push({ - variableId: - variableIdMap.get(variableName), - modeId: mode.modeId, - value: variableValue, - }); - } - }); + } } } @@ -751,6 +742,8 @@ async function updateSkinOtherVariables( ); } + console.log(newData); + return newData; } diff --git a/tokens/figma/utils.mjs b/tokens/figma/utils.mjs index d4a37aeebfc..8662c57bac6 100644 --- a/tokens/figma/utils.mjs +++ b/tokens/figma/utils.mjs @@ -116,3 +116,127 @@ export async function updateCollections( throw error; // rethrow the error to be handled later } } + +export async function updateOrCreateVariable({ + variable, + targetCollectionName, + existingVariables, + existingCollections, +}) { + const collectionId = Object.values( + existingCollections + ).find( + (collection) => + collection.name === targetCollectionName + ).id; + + //If exists retrieve the variable id + const existingVariable = Object.values( + existingVariables + ).find( + (v) => + v.name === variable.name && + v.variableCollectionId === collectionId + ); + + const tempId = `tempId_${targetCollectionName}_${variable.name}`; + + //Retrieve the variableCollectionId with the targetCollectionName + + if (!existingVariable) { + // Create new variable + return { + action: "CREATE", + id: tempId, + name: variable.name, + variableCollectionId: collectionId, + resolvedType: variable.resolvedType, + scopes: variable.scopes, + }; + } else { + // Update existing variable + return { + action: "UPDATE", + id: existingVariable.id, + name: variable.name, + variableCollectionId: collectionId, + resolvedType: variable.resolvedType, + scopes: variable.scopes, + }; + } +} + +export async function updateOrCreateVariableModeValues({ + variable, + targetModeName, + targetCollectionName, + existingCollections, + existingVariables, +}) { + // Find the mode for the given modeName, or use tempId if mode is being created + + const targetCollection = Object.values( + existingCollections + ).find( + (collection) => + collection.name === targetCollectionName + ); + + if (!targetCollection) { + console.warn( + `Collection ${targetCollectionName} not found.` + ); + return; + } + + // Now access the modes from the found collection + const existingMode = + targetCollection.modes.find( + (m) => m.name === targetModeName + ); + + const modeId = existingMode + ? existingMode.modeId + : `tempId_${targetCollectionName}_${targetModeName}`; + + if (!modeId) { + console.warn( + `Mode ${targetModeName} not found and no tempId provided.` + ); + return; + } + + const collectionId = Object.values( + existingCollections + ).find( + (collection) => + collection.name === targetCollectionName + )?.id; + + // Retrieve the variable id if exists + const existingVariable = Object.values( + existingVariables + ).find( + (v) => + v.name === variable.name && + v.variableCollectionId === collectionId + ); + + const tempId = `tempId_${targetCollectionName}_${variable.name}`; + + return { + action: existingVariable + ? "UPDATE" + : "CREATE", + variableId: existingVariable + ? existingVariable.id + : tempId, // Use existing variable ID or a temp one + modeId: modeId, + value: variable.hasAlias + ? { + type: "VARIABLE_ALIAS", + id: variable.value, + } + : variable.value, + }; +} From 763d7ef868673dc2ff880b9908848dba91e89863 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Wed, 11 Sep 2024 21:32:18 +0200 Subject: [PATCH 37/71] Reuse helper funcions for variable creation --- tokens/figma/update-middleware.mjs | 142 ++++++++++++----------------- tokens/figma/utils.mjs | 9 +- 2 files changed, 68 insertions(+), 83 deletions(-) diff --git a/tokens/figma/update-middleware.mjs b/tokens/figma/update-middleware.mjs index 992695c48c1..b1ad0053936 100644 --- a/tokens/figma/update-middleware.mjs +++ b/tokens/figma/update-middleware.mjs @@ -8,6 +8,7 @@ import { updateCollections, updateOrCreateVariable, updateOrCreateVariableModeValues, + generateTempModeId, } from "./utils.mjs"; dotenv.config({ path: "../../.env" }); @@ -359,7 +360,6 @@ async function updateSkinColorVariables( ///////////////////////////// - // Define the firstBrand before using it const firstBrand = brands[0]; const formattedFirstBrand = formatBrandName(firstBrand); @@ -372,9 +372,7 @@ async function updateSkinColorVariables( mode.name === formattedFirstBrand ); - // Check if the default mode needs to be updated if (defaultMode) { - // If the mode name is unformatted, update it to the formatted one if ( defaultMode.name === firstBrand || defaultMode.name === "Mode 1" @@ -387,21 +385,21 @@ async function updateSkinColorVariables( }); } } else { - // If no matching mode exists, create a new one newData.variableModes.push({ action: "CREATE", name: formattedFirstBrand, - id: `tempId_${firstBrand}`, // Ensure the id is unique + id: generateTempModeId( + "Skin", + formattedFirstBrand + ), variableCollectionId: skinCollection.id, }); } - // Create or update additional modes for the remaining brands brands.slice(1).forEach((brand) => { const formattedBrand = formatBrandName(brand); - // Find an existing mode that matches either the unformatted or formatted name const existingMode = skinCollection.modes.find( (mode) => @@ -410,7 +408,6 @@ async function updateSkinColorVariables( ); if (existingMode) { - // If the mode name matches the unformatted brand name, update it if (existingMode.name === brand) { newData.variableModes.push({ action: "UPDATE", @@ -421,11 +418,13 @@ async function updateSkinColorVariables( }); } } else { - // If no mode exists for the brand, create a new one newData.variableModes.push({ action: "CREATE", name: formattedBrand, - id: `tempId_${brand}`, // Ensure the id is unique + id: generateTempModeId( + "Skin", + formattedBrand + ), variableCollectionId: skinCollection.id, }); } @@ -456,82 +455,62 @@ async function updateSkinColorVariables( } }); - // Step 7: Create or update only color variables in the Skin collection - variableToBrandMap.forEach( - (brandMap, variableName) => { - let variableId; + // Step 7: Create or update only color variables in the Skin collection using the helper function + for (let [ + variableName, + brandMap, + ] of variableToBrandMap) { + const variable = { + name: variableName, + resolvedType: "COLOR", + scopes: ["ALL_SCOPES"], + targetCollectionName: "Skin", + }; + + // Use the extracted helper function to create or update the variable + const variableData = + await updateOrCreateVariable({ + variable, + targetCollectionName: + variable.targetCollectionName, + existingVariables: + existingSkinVariables, + existingCollections: themeCollections, + }); - // Check if the variable already exists in the Skin collection and is a color variable - const existingVariable = - existingSkinVariables.find( - (variable) => - variable.name === variableName && - variable.resolvedType === "COLOR" - ); + newData.variables.push(variableData); - if (existingVariable) { - // Variable exists, use its ID for update - variableId = existingVariable.id; - newData.variables.push({ - action: "UPDATE", - id: variableId, - name: variableName, - variableCollectionId: - skinCollection.id, - resolvedType: "COLOR", - }); - } else { - // Variable does not exist, create it - variableId = `tempId_${skinCollection.id}_${variableName}`; - newData.variables.push({ - action: "CREATE", - id: variableId, - name: variableName, - variableCollectionId: - skinCollection.id, - resolvedType: "COLOR", - }); - } + // Step 8: Update mode values with the correct aliases for each brand + for (const brand of brands) { + const formattedBrand = + formatBrandName(brand); - // Step 8: Update mode values with the correct aliases for each brand - brands.forEach((brand) => { - const mode = skinCollection.modes?.find( - (mode) => - mode.name === formatBrandName(brand) - ); + // Call the helper function to create or update variable mode values + const variableModeValuesData = + await updateOrCreateVariableModeValues({ + variable: { + name: variableName, // Assuming variableName is defined earlier + hasAlias: true, + value: brandMap[brand], // Alias to the Theme variable ID for the brand + }, - const defaultMode = - skinCollection.modes?.find( - (mode) => mode.name === "Mode 1" - ); + targetModeName: + brand === brands[0] + ? defaultMode.name + : formattedBrand, + targetCollectionName: "Skin", // Assuming the collection name is 'Skin' + existingCollections: themeCollections, // Pass the fetched collections + existingVariables: + existingSkinVariables, // Pass the existing variables in the Skin collection + }); - if (mode) { - newData.variableModeValues.push({ - action: "CREATE", - variableId: variableId, - modeId: mode.modeId, - value: { - type: "VARIABLE_ALIAS", - id: brandMap[brand], // Alias to the Theme variable ID for the brand - }, - }); - } else { - newData.variableModeValues.push({ - action: "CREATE", - variableId: variableId, - modeId: - brand === "movistar" - ? defaultMode.modeId - : `tempId_${brand}`, // Alias to the Theme variable ID for the brand - value: { - type: "VARIABLE_ALIAS", - id: brandMap[brand], - }, - }); - } - }); + if (variableModeValuesData) { + newData.variableModeValues.push( + variableModeValuesData + ); + } } - ); + } // Step 9: Send the data to update the Skin collection (POST) const updateResponse = await fetch( @@ -742,8 +721,6 @@ async function updateSkinOtherVariables( ); } - console.log(newData); - return newData; } @@ -794,6 +771,7 @@ async function processAllBrands(brands) { } async function main() { + await processAllBrands(brands); await updateSkinColorVariables( brands, MIDDLEWARE_TOKEN diff --git a/tokens/figma/utils.mjs b/tokens/figma/utils.mjs index 8662c57bac6..47e75bb9aea 100644 --- a/tokens/figma/utils.mjs +++ b/tokens/figma/utils.mjs @@ -25,6 +25,13 @@ export function hexToRgba(hex, alpha = 1) { }; } +export function generateTempModeId( + targetMode, + targetCollection +) { + return `tempId_${targetCollection}_${targetMode}`; +} + export async function updateCollections( collections, FILE_KEY, @@ -197,7 +204,7 @@ export async function updateOrCreateVariableModeValues({ const modeId = existingMode ? existingMode.modeId - : `tempId_${targetCollectionName}_${targetModeName}`; + : generateTempModeId(targetCollectionName, targetModeName); if (!modeId) { console.warn( From b321ea78469879be29f4494e54c16fb7dd413892 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Wed, 11 Sep 2024 23:00:36 +0200 Subject: [PATCH 38/71] Code improvements --- tokens/figma/update-middleware.mjs | 286 ++++++++++++----------------- tokens/figma/update-skins.mjs | 1 - tokens/figma/utils.mjs | 82 ++++++++- 3 files changed, 195 insertions(+), 174 deletions(-) diff --git a/tokens/figma/update-middleware.mjs b/tokens/figma/update-middleware.mjs index b1ad0053936..1cd0de9b128 100644 --- a/tokens/figma/update-middleware.mjs +++ b/tokens/figma/update-middleware.mjs @@ -6,9 +6,11 @@ import { fileURLToPath } from "url"; import extractJsonData from "./extract-json-data-middleware.mjs"; import { updateCollections, + updateOrCreateMode, updateOrCreateVariable, updateOrCreateVariableModeValues, generateTempModeId, + VARIABLE_TYPES, } from "./utils.mjs"; dotenv.config({ path: "../../.env" }); @@ -90,182 +92,130 @@ async function updateTheme( variableModeValues: [], }; - function findVariableByName( - variableName, - collectionName - ) { - return Object.values( - existingVariables - ).find((variable) => { - const collection = - existingCollections[ - variable.variableCollectionId - ]; - return ( - collection && - collection.name === collectionName && - variable.name === variableName - ); - }); - } - - function findModeByName( - collection, - modeName - ) { - return collection.modes.find( - (mode) => mode.name === modeName - ); - } + const modeNames = ["Light", "Dark"]; - function generateUniqueTempId( - name, - collection - ) { - return `tempId_${collection}_${name}_${Date.now()}`; - } - - const updateModes = (collectionName) => { - const collection = Object.values( - existingCollections - ).find( - (col) => col.name === collectionName - ); - if (!collection) return; - - const defaultMode = collection.modes.find( - (mode) => - mode.modeId === collection.defaultModeId - ); - if ( - defaultMode && - defaultMode.name !== "Light" - ) { - newData.variableModes.push({ - action: "UPDATE", - id: defaultMode.modeId, - variableCollectionId: collection.id, - name: "Light", - }); - } + for (const modeName of modeNames) { + const modeData = await updateOrCreateMode({ + mode: { + name: modeName, + variableCollectionId: "Theme", + }, + defaultModeName: modeNames[0], + targetCollectionName: "Theme", + existingCollections, + }); - const darkMode = findModeByName( - collection, - "Dark" - ); - if (!darkMode) { - newData.variableModes.push({ - action: "CREATE", - id: "Dark", - variableCollectionId: collection.id, - name: "Dark", - }); + if (modeData) { + newData.variableModes.push(modeData); } - }; - - updateModes("Theme"); + } - function createOrUpdateVariables( + async function processVariables( lightVariables, darkVariables, - collectionName + collectionName, + brand, + existingVariables, + existingCollections, + newData ) { - const collection = Object.values( - existingCollections - ).find( - (col) => col.name === collectionName - ); - if (!collection) { - console.error( - `Collection ${collectionName} not found` - ); - return; - } - - const lightMode = - findModeByName(collection, "Light") || - collection.defaultModeId; - const darkMode = findModeByName( - collection, - "Dark" - ); - - lightVariables.forEach((lightVariable) => { + for (const lightVariable of lightVariables) { const prefixedName = `${brand}/${lightVariable.name}`; const darkVariable = darkVariables.find( (v) => v.name === lightVariable.name ); - const existingVariable = - findVariableByName( - prefixedName, - collectionName - ); + // Find if an existing mode is named "Mode 1" as default mode - if (existingVariable) { - newData.variables.push({ - action: "UPDATE", - id: existingVariable.id, - name: prefixedName, - variableCollectionId: - existingVariable.variableCollectionId, - resolvedType: "COLOR", - description: `light: ${lightVariable.description}, dark: ${darkVariable?.description}`, - scopes: [], - }); - - newData.variableModeValues.push({ - action: "UPDATE", - variableId: existingVariable.id, - modeId: lightMode.modeId || lightMode, - value: lightVariable.value, - }); + const CollectionNameId = Object.values( + existingCollections + ).find( + (collection) => + collection.name === collectionName + ).id; - if (darkVariable) { - newData.variableModeValues.push({ - action: "UPDATE", - variableId: existingVariable.id, - modeId: darkMode?.modeId, - value: darkVariable.value, - }); - } - } else { - const tempId = generateUniqueTempId( - lightVariable.name, - collectionName - ); + const defaultMode = existingCollections[ + CollectionNameId + ].modes.find( + (mode) => + mode.name === DEFAULT_FIGMA_MODENAME + ); - newData.variables.push({ - action: "CREATE", - id: tempId, - name: prefixedName, - variableCollectionId: collection.id, - resolvedType: "COLOR", + // Prepare the variable data + const variableData = + await updateOrCreateVariable({ + variable: { + name: prefixedName, + resolvedType: VARIABLE_TYPES.COLOR, + scopes: ["ALL_SCOPES"], + }, + targetCollectionName: collectionName, + existingVariables: existingVariables, + existingCollections, }); - newData.variableModeValues.push({ - action: "CREATE", - variableId: tempId, - modeId: lightMode.modeId || lightMode, - value: lightVariable.value, + // Prepare the mode values + const modeValueData = + await updateOrCreateVariableModeValues({ + variable: { + name: prefixedName, + value: lightVariable.value, + hasAlias: false, + }, + targetModeName: defaultMode + ? DEFAULT_FIGMA_MODENAME // When created the defaultModeName has not been yet updated to targetModeName + : "Light", + targetCollectionName: collectionName, + existingCollections, + existingVariables, }); - if (darkVariable) { - newData.variableModeValues.push({ - action: "CREATE", - variableId: tempId, - modeId: darkMode?.modeId || "Dark", - value: darkVariable.value, - }); + if (modeValueData) { + newData.variableModeValues.push( + modeValueData + ); + } + + if (darkVariable) { + const darkModeValueData = + await updateOrCreateVariableModeValues( + { + variable: { + name: prefixedName, + value: darkVariable.value, + hasAlias: false, + }, + targetModeName: "Dark", + targetCollectionName: + collectionName, + existingCollections, + existingVariables, + } + ); + + if (darkModeValueData) { + newData.variableModeValues.push( + darkModeValueData + ); } } - }); + + // Update the variable list + newData.variables.push(variableData); + } + + return newData; // Moved the return outside the loop } - createOrUpdateVariables( - jsonData[brand].light, - jsonData[brand].dark, - "Theme" + // Await the processVariables function + await processVariables( + jsonData[brand]?.light, + jsonData[brand]?.dark, + "Theme", + brand, + existingVariables, + existingCollections, + newData ); const updateResponse = await fetch( @@ -358,8 +308,6 @@ async function updateSkinColorVariables( // Step 5: Create or update modes based on the brands - ///////////////////////////// - const firstBrand = brands[0]; const formattedFirstBrand = formatBrandName(firstBrand); @@ -367,7 +315,7 @@ async function updateSkinColorVariables( const defaultMode = skinCollection.modes?.find( (mode) => - mode.name === "Mode 1" || + mode.name === DEFAULT_FIGMA_MODENAME || mode.name === firstBrand || mode.name === formattedFirstBrand ); @@ -375,7 +323,8 @@ async function updateSkinColorVariables( if (defaultMode) { if ( defaultMode.name === firstBrand || - defaultMode.name === "Mode 1" + defaultMode.name === + DEFAULT_FIGMA_MODENAME ) { newData.variableModes.push({ action: "UPDATE", @@ -430,13 +379,14 @@ async function updateSkinColorVariables( } }); - ///////////////////////////// - // Step 6: Create a map for color variables from Theme const variableToBrandMap = new Map(); existingThemeVariables.forEach((variable) => { - if (variable.resolvedType === "COLOR") { + if ( + variable.resolvedType === + VARIABLE_TYPES.COLOR + ) { const variableName = variable.name .split("/") .pop(); @@ -455,19 +405,17 @@ async function updateSkinColorVariables( } }); - // Step 7: Create or update only color variables in the Skin collection using the helper function for (let [ variableName, brandMap, ] of variableToBrandMap) { const variable = { name: variableName, - resolvedType: "COLOR", + resolvedType: VARIABLE_TYPES.COLOR, scopes: ["ALL_SCOPES"], targetCollectionName: "Skin", }; - // Use the extracted helper function to create or update the variable const variableData = await updateOrCreateVariable({ variable, @@ -591,7 +539,7 @@ async function updateSkinOtherVariables( { variables: jsonData[brand]?.radius || [], collectionName: "Skin", - resolvedType: "FLOAT", + resolvedType: VARIABLE_TYPES.FLOAT, variableScopes: ["CORNER_RADIUS"], hasAlias: false, }, @@ -599,7 +547,7 @@ async function updateSkinOtherVariables( variables: jsonData[brand]?.fontWeight || [], collectionName: "Skin", - resolvedType: "STRING", + resolvedType: VARIABLE_TYPES.STRING, variableScopes: ["FONT_WEIGHT"], hasAlias: false, }, @@ -607,7 +555,7 @@ async function updateSkinOtherVariables( variables: jsonData[brand]?.fontSize || [], collectionName: "Skin", - resolvedType: "FLOAT", + resolvedType: VARIABLE_TYPES.FLOAT, variableScopes: ["FONT_SIZE"], hasAlias: false, }, @@ -615,7 +563,7 @@ async function updateSkinOtherVariables( variables: jsonData[brand]?.lineHeight || [], collectionName: "Skin", - resolvedType: "FLOAT", + resolvedType: VARIABLE_TYPES.FLOAT, variableScopes: ["LINE_HEIGHT"], hasAlias: false, }, @@ -627,7 +575,7 @@ async function updateSkinOtherVariables( }, ], collectionName: "Skin", - resolvedType: "STRING", + resolvedType: VARIABLE_TYPES.STRING, variableScopes: ["FONT_FAMILY"], hasAlias: false, }, @@ -639,7 +587,7 @@ async function updateSkinOtherVariables( }, ], collectionName: "Skin", - resolvedType: "STRING", + resolvedType: VARIABLE_TYPES.STRING, variableScopes: ["ALL_SCOPES"], hasAlias: false, }, diff --git a/tokens/figma/update-skins.mjs b/tokens/figma/update-skins.mjs index 74ac2d282b4..dade060b021 100644 --- a/tokens/figma/update-skins.mjs +++ b/tokens/figma/update-skins.mjs @@ -528,7 +528,6 @@ async function updateVariables( } } -// Use an async function to handle the post request after data processing async function postCollections(brand, FILE_KEY) { try { diff --git a/tokens/figma/utils.mjs b/tokens/figma/utils.mjs index 47e75bb9aea..fdaddc96f2b 100644 --- a/tokens/figma/utils.mjs +++ b/tokens/figma/utils.mjs @@ -32,6 +32,8 @@ export function generateTempModeId( return `tempId_${targetCollection}_${targetMode}`; } +export const DEFAULT_FIGMA_MODENAME = "Mode 1"; + export async function updateCollections( collections, FILE_KEY, @@ -124,6 +126,66 @@ export async function updateCollections( } } +export async function updateOrCreateMode({ + mode, + defaultModeName, + targetCollectionName, + existingCollections, +}) { + const collection = Object.values( + existingCollections + ).find( + (collection) => + collection.name === targetCollectionName + ); + const collectionId = collection.id; + + const existingModes = collection.modes; + + const defaultMode = existingModes.find( + (m) => m.name === DEFAULT_FIGMA_MODENAME + ); + + const existingMode = existingModes.find( + (m) => m.name === mode.name + ); + + // If renaming "Default" mode to the first mode name (e.g., "Light") + if ( + !existingMode && + defaultMode && + mode.name === defaultModeName + ) { + return { + action: "UPDATE", + id: defaultMode.modeId, + name: mode.name, // Rename "Default" to "Light" + variableCollectionId: collectionId, + }; + } + + // Create new mode if it doesn't exist + if (!existingMode) { + return { + action: "CREATE", + id: generateTempModeId( + mode, + targetCollectionName + ), + name: mode.name, // Create "Dark" + variableCollectionId: collectionId, + }; + } else { + // Update existing mode if it exists + return { + action: "UPDATE", + id: existingMode.modeId, + name: mode.name, + variableCollectionId: collectionId, + }; + } +} + export async function updateOrCreateVariable({ variable, targetCollectionName, @@ -197,14 +259,17 @@ export async function updateOrCreateVariableModeValues({ } // Now access the modes from the found collection - const existingMode = + const existingModes = targetCollection.modes.find( (m) => m.name === targetModeName ); - const modeId = existingMode - ? existingMode.modeId - : generateTempModeId(targetCollectionName, targetModeName); + const modeId = existingModes + ? existingModes.modeId + : generateTempModeId( + targetModeName, + targetCollectionName + ); if (!modeId) { console.warn( @@ -247,3 +312,12 @@ export async function updateOrCreateVariableModeValues({ : variable.value, }; } + +export const VARIABLE_TYPES = { + COLOR: "COLOR", + FLOAT: "FLOAT", + STRING: "STRING", + FONT_WEIGHT: "FONT_WEIGHT", + FONT_SIZE: "FONT_SIZE", + LINE_HEIGHT: "LINE_HEIGHT", +}; From 5a1dacdb45c783d90578fdac8daa3e6aa293e355 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Wed, 11 Sep 2024 23:36:34 +0200 Subject: [PATCH 39/71] Fixes --- tokens/figma/update-middleware.mjs | 103 ++++++++++++++++++++--------- tokens/figma/utils.mjs | 64 +++++++++++------- 2 files changed, 112 insertions(+), 55 deletions(-) diff --git a/tokens/figma/update-middleware.mjs b/tokens/figma/update-middleware.mjs index 1cd0de9b128..4d647a7e4b4 100644 --- a/tokens/figma/update-middleware.mjs +++ b/tokens/figma/update-middleware.mjs @@ -11,6 +11,7 @@ import { updateOrCreateVariableModeValues, generateTempModeId, VARIABLE_TYPES, + DEFAULT_FIGMA_MODENAME, } from "./utils.mjs"; dotenv.config({ path: "../../.env" }); @@ -62,12 +63,13 @@ function formatBrandName(brand) { ); // Capitalize the first letter of each word } -async function updateTheme( +export async function updateTheme( jsonData, brand, FILE_KEY ) { try { + // Fetch existing variables and collections from Figma const response = await fetch( `https://api.figma.com/v1/files/${FILE_KEY}/variables/local`, { @@ -92,23 +94,54 @@ async function updateTheme( variableModeValues: [], }; - const modeNames = ["Light", "Dark"]; + function findModeByName( + collection, + modeName + ) { + return collection.modes.find( + (mode) => mode.name === modeName + ); + } - for (const modeName of modeNames) { - const modeData = await updateOrCreateMode({ - mode: { - name: modeName, - variableCollectionId: "Theme", - }, - defaultModeName: modeNames[0], - targetCollectionName: "Theme", - existingCollections, - }); + const updateModes = (collectionName) => { + const collection = Object.values( + existingCollections + ).find( + (col) => col.name === collectionName + ); + if (!collection) return; - if (modeData) { - newData.variableModes.push(modeData); + const defaultMode = collection.modes.find( + (mode) => + mode.modeId === collection.defaultModeId + ); + if ( + defaultMode && + defaultMode.name !== "Light" + ) { + newData.variableModes.push({ + action: "UPDATE", + id: defaultMode.modeId, + variableCollectionId: collection.id, + name: "Light", + }); } - } + + const darkMode = findModeByName( + collection, + "Dark" + ); + if (!darkMode) { + newData.variableModes.push({ + action: "CREATE", + id: generateTempModeId("Dark", "Theme"), + variableCollectionId: collection.id, + name: "Dark", + }); + } + }; + + updateModes("Theme"); async function processVariables( lightVariables, @@ -125,18 +158,25 @@ async function updateTheme( (v) => v.name === lightVariable.name ); - // Find if an existing mode is named "Mode 1" as default mode - - const CollectionNameId = Object.values( + // Get the collection ID + const collectionId = Object.values( existingCollections ).find( (collection) => collection.name === collectionName - ).id; + )?.id; + if (!collectionId) { + console.warn( + `Collection ${collectionName} not found.` + ); + continue; + } + + // Get default mode for this collection const defaultMode = existingCollections[ - CollectionNameId - ].modes.find( + collectionId + ]?.modes.find( (mode) => mode.name === DEFAULT_FIGMA_MODENAME ); @@ -150,7 +190,7 @@ async function updateTheme( scopes: ["ALL_SCOPES"], }, targetCollectionName: collectionName, - existingVariables: existingVariables, + existingVariables, existingCollections, }); @@ -163,7 +203,7 @@ async function updateTheme( hasAlias: false, }, targetModeName: defaultMode - ? DEFAULT_FIGMA_MODENAME // When created the defaultModeName has not been yet updated to targetModeName + ? DEFAULT_FIGMA_MODENAME : "Light", targetCollectionName: collectionName, existingCollections, @@ -204,13 +244,13 @@ async function updateTheme( newData.variables.push(variableData); } - return newData; // Moved the return outside the loop + return newData; } - // Await the processVariables function + // Process variables for light and dark themes await processVariables( - jsonData[brand]?.light, - jsonData[brand]?.dark, + jsonData[brand]?.light || [], + jsonData[brand]?.dark || [], "Theme", brand, existingVariables, @@ -218,6 +258,7 @@ async function updateTheme( newData ); + // Update the variables and modes in Figma const updateResponse = await fetch( `https://api.figma.com/v1/files/${FILE_KEY}/variables`, { @@ -338,8 +379,8 @@ async function updateSkinColorVariables( action: "CREATE", name: formattedFirstBrand, id: generateTempModeId( - "Skin", - formattedFirstBrand + formattedFirstBrand, + "Skin" ), variableCollectionId: skinCollection.id, }); @@ -371,8 +412,8 @@ async function updateSkinColorVariables( action: "CREATE", name: formattedBrand, id: generateTempModeId( - "Skin", - formattedBrand + formattedBrand, + "Skin" ), variableCollectionId: skinCollection.id, }); diff --git a/tokens/figma/utils.mjs b/tokens/figma/utils.mjs index fdaddc96f2b..c26081de930 100644 --- a/tokens/figma/utils.mjs +++ b/tokens/figma/utils.mjs @@ -138,49 +138,65 @@ export async function updateOrCreateMode({ (collection) => collection.name === targetCollectionName ); - const collectionId = collection.id; - const existingModes = collection.modes; + if (!collection) { + console.warn( + `Collection ${targetCollectionName} not found.` + ); + return; + } + + const collectionId = collection.id; + const existingModes = collection.modes || []; + // Find the default mode (e.g., "Mode 1" or "Default") const defaultMode = existingModes.find( - (m) => m.name === DEFAULT_FIGMA_MODENAME + (m) => m.name === DEFAULT_FIGMA_MODENAME // Replace with actual default mode name if different ); + // Find the target mode by its name const existingMode = existingModes.find( (m) => m.name === mode.name ); - // If renaming "Default" mode to the first mode name (e.g., "Light") - if ( - !existingMode && - defaultMode && - mode.name === defaultModeName - ) { - return { - action: "UPDATE", - id: defaultMode.modeId, - name: mode.name, // Rename "Default" to "Light" - variableCollectionId: collectionId, - }; - } - - // Create new mode if it doesn't exist - if (!existingMode) { + if (mode.name === defaultModeName) { + if (defaultMode) { + // Rename the default mode to the target mode name + return { + action: "UPDATE", + id: defaultMode.modeId, + name: mode.name, // Rename "Default" to the target mode name + variableCollectionId: collectionId, + }; + } else { + // If default mode does not exist, create it + return { + action: "CREATE", + id: generateTempModeId( + mode.name, // Use mode name for temp ID + targetCollectionName + ), + name: mode.name, // Create the mode with the target name + variableCollectionId: collectionId, + }; + } + } else if (!existingMode) { + // If mode doesn't exist, create it return { action: "CREATE", id: generateTempModeId( - mode, + mode.name, // Use mode name for temp ID targetCollectionName ), - name: mode.name, // Create "Dark" + name: mode.name, // Create the mode with the specified name variableCollectionId: collectionId, }; } else { - // Update existing mode if it exists + // If the mode exists, update it return { action: "UPDATE", - id: existingMode.modeId, - name: mode.name, + id: existingMode.modeId, // Use existing mode ID + name: mode.name, // Update the mode with the correct name variableCollectionId: collectionId, }; } From bc10d4c9d3bcaa7a2c42d54749479160cc894693 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Thu, 12 Sep 2024 10:45:23 +0200 Subject: [PATCH 40/71] Remove test file --- tokens/figma/test.mjs | 13 ------------- tokens/figma/update-middleware.mjs | 2 +- 2 files changed, 1 insertion(+), 14 deletions(-) delete mode 100644 tokens/figma/test.mjs diff --git a/tokens/figma/test.mjs b/tokens/figma/test.mjs deleted file mode 100644 index b5096ac6840..00000000000 --- a/tokens/figma/test.mjs +++ /dev/null @@ -1,13 +0,0 @@ -// index.mjs - -// Captura el argumento de la marca pasado al script -const brand = process.argv[2]; - -// Verifica si se ha pasado un argumento -if (!brand) { - console.error("Error: No brand provided."); - process.exit(1); -} - -// Muestra un mensaje en la consola con el nombre de la marca -console.log(`Syncing tokens for brand: ${brand}`); diff --git a/tokens/figma/update-middleware.mjs b/tokens/figma/update-middleware.mjs index 4d647a7e4b4..20b88281966 100644 --- a/tokens/figma/update-middleware.mjs +++ b/tokens/figma/update-middleware.mjs @@ -187,7 +187,7 @@ export async function updateTheme( variable: { name: prefixedName, resolvedType: VARIABLE_TYPES.COLOR, - scopes: ["ALL_SCOPES"], + scopes: [], }, targetCollectionName: collectionName, existingVariables, From c3860f4af82779ef5de2deb93d691e9efa978848 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Thu, 12 Sep 2024 10:55:59 +0200 Subject: [PATCH 41/71] Update README --- tokens/figma/README.md | 92 +++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 54 deletions(-) diff --git a/tokens/figma/README.md b/tokens/figma/README.md index 283d417a90f..ab6c7e5197c 100644 --- a/tokens/figma/README.md +++ b/tokens/figma/README.md @@ -1,77 +1,61 @@ -# Design Tokens Automation Script +# Project Overview -## Objective +This project is designed to update Figma variables based on a JSON input, primarily focused on managing brand themes, colors, and other design tokens. The project retrieves existing variables from Figma, processes the provided JSON data, and updates or creates new variables in collections "Theme" and "Skin". -This script automates the process of fetching, updating, and posting design tokens to Figma for multiple brands. It reads JSON data files, extracts relevant variables, compares them with existing data from Figma, updates them if necessary, and then posts the changes back to Figma's API. +## Features -## Overview of the Script +- **Fetch Existing Figma Data**: Retrieves the existing variables and collections from Figma. +- **Process JSON Data**: Extracts theme and token data from provided JSON files for each brand. +- **Update or Create Variables**: Adds new variables or updates existing ones based on the brand's light and dark themes. +- **Handle Variable Modes**: Ensures each brand's mode (e.g., "Light", "Dark") is updated or created in the Figma "Skin" collection. +- **Support for Multiple Brands**: Processes multiple brands, mapping each brand's unique variables into Figma's collections. -### Environment Setup +## Setup -- The script uses the `dotenv` package to load environment variables (like Figma API tokens and file keys) from a `.env` file. -- It imports necessary modules (`fetch`, `fs`, `path`, etc.) and utility functions (`extractJsonData`, `hexToRgba`, `extractPaletteValue`). +### Environment Variables: -### File and Data Preparation +- `FIGMA_TOKEN`: The API token to authenticate with Figma. +- `MIDDLEWARE_KEY`: Token for file where the variables need to be created / updated -- The script reads JSON files from a specific directory, filters out the ones with a `.json` extension, and extracts their data using the `extractJsonData` function. +### Dependencies: -### Brands and URLs +- Node.js and packages such as `node-fetch`, `dotenv`, and `fs` are used to manage API requests, read local files, and load environment variables. -- An object `brands` maps brand names to their corresponding Figma API URLs. This allows the script to handle multiple brands in one go. +## Key Functions -## Main Functions +### `updateTheme(jsonData, brand, FILE_KEY)` -### `fetchAndUpdateVariables(jsonData, brand, url)` +This function updates the theme variables in Figma for a specific brand. It: -**Purpose:** -Handles the core logic of fetching existing variables from Figma, comparing them with local JSON data, and preparing a data object for the POST request. +- Fetches the current variables from Figma. +- Updates modes and variables for "Light" and "Dark" themes. +- Sends a POST request to update Figma with the new data. -**Steps:** +### `updateSkinColorVariables(brands, FILE_KEY)` -1. Fetch existing variables and collections from Figma. -2. Initialize a `newData` object to store the changes that need to be posted back to Figma. -3. For each collection (like "palette", "radius", "font-weight"): - - Check if it exists. If it does, prepare an update request; otherwise, prepare a create request. -4. For each variable within these collections: - - Check if it exists and needs updating. If it doesn’t exist, prepare a create request. -5. Identify and delete any variables in Figma that are no longer in the local JSON data. +This function focuses on updating color variables in the "Skin" collection. It: -### `updateVariables(variableName, variableValue, collectionName, existingVariables, existingCollections, variableType, variableScopes)` +- Maps color variables from the "Theme" collection to the "Skin" collection. +- Creates or updates modes for each brand. +- Ensures proper aliasing of variables between collections. -**Purpose:** -Updates or creates variables in the `newData` object based on their existence in the Figma data. +### `updateSkinOtherVariables(jsonData, brands, FILE_KEY)` -**Parameters:** +This function updates non-color variables, such as font families and icon sets, for each brand. It: -- `variableName`, `variableValue`: The name and value of the variable to be updated/created. -- `collectionName`: The name of the collection the variable belongs to. -- `existingVariables`, `existingCollections`: The current variables and collections fetched from Figma. -- `variableType`, `variableScopes`: Additional metadata for the variable. +- Handles specific design tokens like radius, font weight, and line height. +- Adds brand-specific font families and icons. -### `findVariableInCollection(variableName, collectionName, existingVariables, existingCollections)` +## Usage -**Purpose:** -Finds a variable within a specific collection in Figma's existing data. +1. Navigate to the `tokens/figma` directory: -**Returns:** -The variable object if found, otherwise `undefined`. + ```bash + cd tokens/figma -### `processAndPostData(url, brand)` + ``` -**Purpose:** -Processes the data for a specific brand and posts the updated data to Figma. - -**Parameters:** -`url` (Figma API URL), `brand` (name of the brand). - -### `processAllUrls(brands)` - -**Purpose:** -Sequentially processes and posts data for all brands defined in the `brands` object. - -**Parameters:** -`brands` (an object mapping brand names to Figma API URLs). - -## Execution - -The script calls `processAllUrls(brands)` at the end, which triggers the whole process for all defined brands. +2. Run the script + ```bash + node update-middleware.mjs + ``` From 8b725b35c7b71d6585ea61c3d39fbfb1871edca9 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Thu, 12 Sep 2024 11:07:13 +0200 Subject: [PATCH 42/71] Revert changes in JSON files --- tokens/movistar.json | 2 +- tokens/o2-new.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tokens/movistar.json b/tokens/movistar.json index a221893259a..9595ff320c0 100644 --- a/tokens/movistar.json +++ b/tokens/movistar.json @@ -8,7 +8,7 @@ "backgroundAlternative": { "value": "{palette.grey1}", "type": "color", - "description": "grey2" + "description": "grey1" }, "backgroundBrand": { "value": "{palette.movistarBlue}", diff --git a/tokens/o2-new.json b/tokens/o2-new.json index ad5e70a2eea..8fd59a0ac4c 100644 --- a/tokens/o2-new.json +++ b/tokens/o2-new.json @@ -118,9 +118,9 @@ "description": "darkBlue" }, "backgroundBrandBottom": { - "value": "{palette.beyondBlue}", + "value": "{palette.beyondBlue45}", "type": "color", - "description": "beyondBlue" + "description": "beyondBlue45" }, "appBarBackground": { "value": "{palette.white}", From 9acbc963bc9ee2d519787356e3216b1726608053 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Thu, 12 Sep 2024 13:32:03 +0200 Subject: [PATCH 43/71] Rename collections and avoid deleting existing collections --- tokens/figma/update-middleware.mjs | 54 +++++++++++++++++++----------- tokens/figma/utils.mjs | 20 ++++------- 2 files changed, 40 insertions(+), 34 deletions(-) diff --git a/tokens/figma/update-middleware.mjs b/tokens/figma/update-middleware.mjs index 20b88281966..8a7f5569e01 100644 --- a/tokens/figma/update-middleware.mjs +++ b/tokens/figma/update-middleware.mjs @@ -12,6 +12,7 @@ import { generateTempModeId, VARIABLE_TYPES, DEFAULT_FIGMA_MODENAME, + COLLECTION_NAMES, } from "./utils.mjs"; dotenv.config({ path: "../../.env" }); @@ -117,7 +118,7 @@ export async function updateTheme( ); if ( defaultMode && - defaultMode.name !== "Light" + defaultMode.name !== "True" ) { newData.variableModes.push({ action: "UPDATE", @@ -134,14 +135,17 @@ export async function updateTheme( if (!darkMode) { newData.variableModes.push({ action: "CREATE", - id: generateTempModeId("Dark", "Theme"), + id: generateTempModeId( + "Dark", + COLLECTION_NAMES.COLOR_SCHEME + ), variableCollectionId: collection.id, name: "Dark", }); } }; - updateModes("Theme"); + updateModes(COLLECTION_NAMES.COLOR_SCHEME); async function processVariables( lightVariables, @@ -251,7 +255,7 @@ export async function updateTheme( await processVariables( jsonData[brand]?.light || [], jsonData[brand]?.dark || [], - "Theme", + COLLECTION_NAMES.COLOR_SCHEME, brand, existingVariables, existingCollections, @@ -291,7 +295,7 @@ async function updateSkinColorVariables( FILE_KEY ) { try { - // Step 1: Fetch existing data from "Theme" and "Skin" collections + // Step 1: Fetch existing data from "Mode" and "Brand" collections const response = await fetch( `https://api.figma.com/v1/files/${FILE_KEY}/variables/local`, { @@ -307,13 +311,18 @@ async function updateSkinColorVariables( const themeCollections = figmaData.meta.variableCollections; - // Step 2: Find the "Theme" and "Skin" collections + // Step 2: Find the "Mode" and "Brand" collections const themeCollection = Object.values( themeCollections - ).find((col) => col.name === "Theme"); + ).find( + (col) => + col.name === COLLECTION_NAMES.COLOR_SCHEME + ); const skinCollection = Object.values( themeCollections - ).find((col) => col.name === "Skin"); + ).find( + (col) => col.name === COLLECTION_NAMES.BRAND + ); if (!themeCollection || !skinCollection) { throw new Error( @@ -321,7 +330,7 @@ async function updateSkinColorVariables( ); } - // Step 3: Filter theme variables to only include those from the "Theme" collection + // Step 3: Filter theme variables to only include those from the "Mode" collection const themeVariables = figmaData.meta.variables || {}; const existingThemeVariables = Object.values( @@ -380,7 +389,7 @@ async function updateSkinColorVariables( name: formattedFirstBrand, id: generateTempModeId( formattedFirstBrand, - "Skin" + COLLECTION_NAMES.BRAND ), variableCollectionId: skinCollection.id, }); @@ -413,7 +422,7 @@ async function updateSkinColorVariables( name: formattedBrand, id: generateTempModeId( formattedBrand, - "Skin" + COLLECTION_NAMES.BRAND ), variableCollectionId: skinCollection.id, }); @@ -454,7 +463,8 @@ async function updateSkinColorVariables( name: variableName, resolvedType: VARIABLE_TYPES.COLOR, scopes: ["ALL_SCOPES"], - targetCollectionName: "Skin", + targetCollectionName: + COLLECTION_NAMES.BRAND, }; const variableData = @@ -487,7 +497,8 @@ async function updateSkinColorVariables( brand === brands[0] ? defaultMode.name : formattedBrand, - targetCollectionName: "Skin", // Assuming the collection name is 'Skin' + targetCollectionName: + COLLECTION_NAMES.BRAND, // Assuming the collection name is 'Skin' existingCollections: themeCollections, // Pass the fetched collections existingVariables: existingSkinVariables, // Pass the existing variables in the Skin collection @@ -579,7 +590,7 @@ async function updateSkinOtherVariables( const variableGroups = [ { variables: jsonData[brand]?.radius || [], - collectionName: "Skin", + collectionName: COLLECTION_NAMES.BRAND, resolvedType: VARIABLE_TYPES.FLOAT, variableScopes: ["CORNER_RADIUS"], hasAlias: false, @@ -587,7 +598,7 @@ async function updateSkinOtherVariables( { variables: jsonData[brand]?.fontWeight || [], - collectionName: "Skin", + collectionName: COLLECTION_NAMES.BRAND, resolvedType: VARIABLE_TYPES.STRING, variableScopes: ["FONT_WEIGHT"], hasAlias: false, @@ -595,7 +606,7 @@ async function updateSkinOtherVariables( { variables: jsonData[brand]?.fontSize || [], - collectionName: "Skin", + collectionName: COLLECTION_NAMES.BRAND, resolvedType: VARIABLE_TYPES.FLOAT, variableScopes: ["FONT_SIZE"], hasAlias: false, @@ -603,7 +614,7 @@ async function updateSkinOtherVariables( { variables: jsonData[brand]?.lineHeight || [], - collectionName: "Skin", + collectionName: COLLECTION_NAMES.BRAND, resolvedType: VARIABLE_TYPES.FLOAT, variableScopes: ["LINE_HEIGHT"], hasAlias: false, @@ -615,7 +626,7 @@ async function updateSkinOtherVariables( value: fontFamilies[brand], }, ], - collectionName: "Skin", + collectionName: COLLECTION_NAMES.BRAND, resolvedType: VARIABLE_TYPES.STRING, variableScopes: ["FONT_FAMILY"], hasAlias: false, @@ -627,7 +638,7 @@ async function updateSkinOtherVariables( value: iconSets[brand], }, ], - collectionName: "Skin", + collectionName: COLLECTION_NAMES.BRAND, resolvedType: VARIABLE_TYPES.STRING, variableScopes: ["ALL_SCOPES"], hasAlias: false, @@ -714,7 +725,10 @@ async function updateSkinOtherVariables( } async function postCollections(brand, FILE_KEY) { - const collectionNames = ["Skin", "Theme"]; + const collectionNames = [ + COLLECTION_NAMES.BRAND, + COLLECTION_NAMES.COLOR_SCHEME, + ]; try { const newData = await updateCollections( diff --git a/tokens/figma/utils.mjs b/tokens/figma/utils.mjs index c26081de930..fc5ead39c50 100644 --- a/tokens/figma/utils.mjs +++ b/tokens/figma/utils.mjs @@ -104,20 +104,6 @@ export async function updateCollections( ); }); - const collectionsToDelete = Object.values( - existingCollections - ).filter( - (collection) => - !collections.includes(collection.name) - ); - - collectionsToDelete.forEach((collection) => { - newData.variableCollections.push({ - action: "DELETE", - id: collection.id, - }); - }); - // Return the processed data for further use return newData; } catch (error) { @@ -337,3 +323,9 @@ export const VARIABLE_TYPES = { FONT_SIZE: "FONT_SIZE", LINE_HEIGHT: "LINE_HEIGHT", }; + +export const COLLECTION_NAMES = { + BRAND: "Brand", + COLOR_SCHEME: "Mode", + PALETTE: "Palette", +}; From bd3693f51444287f6e9e714754e9bb71e1112852 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Thu, 12 Sep 2024 13:36:56 +0200 Subject: [PATCH 44/71] Change name grouping order for lineheight and fontSize --- tokens/figma/extract-json-data-middleware.mjs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tokens/figma/extract-json-data-middleware.mjs b/tokens/figma/extract-json-data-middleware.mjs index e50ba68a747..65b1be8b441 100644 --- a/tokens/figma/extract-json-data-middleware.mjs +++ b/tokens/figma/extract-json-data-middleware.mjs @@ -338,11 +338,11 @@ const extractJsonData = ( ) { return [ { - name: `mobile/fontSize/${key}`, + name: `fontSize/mobile/${key}`, value: parseFloat(value.mobile), }, { - name: `desktop/fontSize/${key}`, + name: `fontSize/desktop/${key}`, value: parseFloat(value.desktop), }, ]; @@ -368,11 +368,11 @@ const extractJsonData = ( ) { return [ { - name: `mobile/lineHeight/${key}`, + name: `lineHeight/mobile/${key}`, value: parseFloat(value.mobile), }, { - name: `desktop/lineHeight/${key}`, + name: `lineHeight/desktop/${key}`, value: parseFloat(value.desktop), }, ]; From 6cc41ed94318e77db4bb36f7f7d9ada990ae619d Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Thu, 12 Sep 2024 14:44:44 +0200 Subject: [PATCH 45/71] Sync all processes in index file --- tokens/figma/README.md | 2 +- tokens/figma/extract-json-data-skins.mjs | 389 ----------------- ...a-middleware.mjs => extract-json-data.mjs} | 388 ++++++++++++++++- tokens/figma/index.mjs | 72 ++++ tokens/figma/update-middleware.mjs | 113 ++--- tokens/figma/update-skins.mjs | 400 ++---------------- 6 files changed, 555 insertions(+), 809 deletions(-) delete mode 100644 tokens/figma/extract-json-data-skins.mjs rename tokens/figma/{extract-json-data-middleware.mjs => extract-json-data.mjs} (52%) create mode 100644 tokens/figma/index.mjs diff --git a/tokens/figma/README.md b/tokens/figma/README.md index ab6c7e5197c..ee085368fe5 100644 --- a/tokens/figma/README.md +++ b/tokens/figma/README.md @@ -57,5 +57,5 @@ This function updates non-color variables, such as font families and icon sets, 2. Run the script ```bash - node update-middleware.mjs + node index.mjs ``` diff --git a/tokens/figma/extract-json-data-skins.mjs b/tokens/figma/extract-json-data-skins.mjs deleted file mode 100644 index 6532a9bdcb7..00000000000 --- a/tokens/figma/extract-json-data-skins.mjs +++ /dev/null @@ -1,389 +0,0 @@ -import fs from "fs"; -import path from "path"; -import { hexToRgba } from "./utils.mjs"; - -const extractJsonData = ( - jsonFiles, - directoryPath -) => { - const allParsedContent = {}; // Initialize once to store all parsed content - - // First pass to gather allParsedContent - jsonFiles.forEach((file) => { - const filePath = path.resolve( - directoryPath, - file - ); - const fileContent = fs.readFileSync( - filePath, - "utf8" - ); - const parsedContent = JSON.parse(fileContent); - - // Extract file name without extension to use as a key - const fileName = file.split(".")[0]; - - // Store the parsed content in the allParsedContent object - allParsedContent[fileName] = parsedContent; - }); - - // Second pass to process each file - return jsonFiles.reduce((accumulator, file) => { - const filePath = path.resolve( - directoryPath, - file - ); - const fileContent = fs.readFileSync( - filePath, - "utf8" - ); - const parsedContent = JSON.parse(fileContent); - - // Extract file name without extension - const fileName = file.split(".")[0]; - - function processColors( - parsedContent, - theme, - allParsedContent - ) { - if (!["light", "dark"].includes(theme)) { - throw new Error( - `Invalid theme: ${theme}. Expected 'light' or 'dark'.` - ); - } - - const themeColors = parsedContent[theme]; - - function getPaletteName(value) { - const regexMatch = value.match( - /{palette\.(.*?)}/ - ); - if (regexMatch) { - return regexMatch[1]; - } - const rgbaMatch = value.match( - /rgba\(\{palette\.(.*?)\},\s*\d*\.?\d*\)/ - ); - if (rgbaMatch) { - return rgbaMatch[1]; - } - throw new Error( - `Unexpected color format: ${value}` - ); - } - - function getPaletteValue(colorName) { - const paletteValue = - parsedContent.global.palette[colorName] - ?.value; - if (!paletteValue) { - throw new Error( - `Color ${colorName} not found in palette` - ); - } - return paletteValue; - } - - function getMaxStopsAcrossBrands( - allParsedContent, - key, - theme - ) { - let maxStops = 0; - let isGradientInAnyBrand = false; - - Object.values(allParsedContent).forEach( - (content) => { - const colors = content[theme][key]; - if ( - colors && - colors.type === "linear-gradient" - ) { - isGradientInAnyBrand = true; - maxStops = Math.max( - maxStops, - colors.value.colors.length - ); - } - } - ); - - return { maxStops, isGradientInAnyBrand }; - } - - return Object.keys(themeColors).flatMap( - (key) => { - const colorData = themeColors[key]; - const { value, type } = colorData; - - const { - maxStops, - isGradientInAnyBrand, - } = getMaxStopsAcrossBrands( - allParsedContent, - key, - theme - ); - - // Handle gradients first - if ( - type === "linear-gradient" && - typeof value === "object" - ) { - // Map colors in gradient - return Array.from( - { length: maxStops }, - (_, index) => { - if (index < value.colors.length) { - // Use the actual gradient stop color - const color = - value.colors[index]; - const alphaMatch = - color.value.match( - /rgba\([^)]+,\s*([^)]+)\)/ - ); - const alpha = alphaMatch - ? alphaMatch[1] - : "1"; - const baseColorName = - getPaletteName(color.value); - - return alpha === "1" - ? { - name: `${theme}/${key}-stop-${ - index + 1 - }`, - value: baseColorName, - hasAlias: true, - } - : { - name: `${theme}/${key}-stop-${ - index + 1 - }`, - value: hexToRgba( - getPaletteValue( - baseColorName - ), - parseFloat(alpha) - ), - hasAlias: false, - }; - } else if (isGradientInAnyBrand) { - // If this brand does not have a gradient, repeat the base color to match stops - const baseColorName = - getPaletteName( - value.colors[0].value - ); - return { - name: `${theme}/${key}-stop-${ - index + 1 - }`, - value: hexToRgba( - getPaletteValue( - baseColorName - ) - ), - hasAlias: false, - }; - } - } - ).filter(Boolean); - } - - // Handle solid colors or aliases when a gradient exists in other brands - if ( - type !== "linear-gradient" && - isGradientInAnyBrand - ) { - const baseColorName = - getPaletteName(value); - // Repeat the solid color to match the gradient stops - return Array.from( - { length: maxStops }, - (_, index) => ({ - name: `${theme}/${key}-stop-${ - index + 1 - }`, - value: hexToRgba( - getPaletteValue(baseColorName) - ), - hasAlias: false, - }) - ); - } - - // Handle solid colors or aliases normally - if ( - typeof value === "string" && - !value.startsWith("rgba") - ) { - const baseColorName = - getPaletteName(value); - - return { - name: `${theme}/${key}`, - value: hexToRgba( - getPaletteValue(baseColorName) - ), - - hasAlias: true, - }; - } - - // Handle rgba colors - if ( - typeof value === "string" && - value.startsWith("rgba") - ) { - const alphaMatch = value.match( - /rgba\([^)]+,\s*([^)]+)\)/ - ); - const alpha = alphaMatch - ? alphaMatch[1] - : "1"; - const baseColorName = - getPaletteName(value); - - return alpha === "1" - ? { - name: `${theme}/${key}`, - value: baseColorName, - hasAlias: true, - } - : { - name: `${theme}/${key}`, - value: hexToRgba( - getPaletteValue( - baseColorName - ), - parseFloat(alpha) - ), - hasAlias: false, - }; - } - - throw new Error( - `Unexpected color format for key: ${key}` - ); - } - ); - } - - // Other token processing logic - const paletteArray = Object.keys( - parsedContent.global.palette - ).map((key) => ({ - name: key, - value: hexToRgba( - parsedContent.global.palette[key].value - ), - })); - - const radiusArray = Object.keys( - parsedContent.radius - ).map((key) => ({ - name: key, - value: - typeof parsedContent.radius[key].value === - "string" - ? parsedContent.radius[key].value === - "circle" - ? 999 // If the value is "circle", set it to 999 - : parseFloat( - parsedContent.radius[key].value - ) // Otherwise, convert it to a float - : parsedContent.radius[key].value, // If it's not a string, use the original value - })); - - const fontWeightArray = Object.keys( - parsedContent.text.weight - ).map((key) => ({ - name: key, - value: parsedContent.text.weight[key].value, - })); - - const fontSizeArray = Object.keys( - parsedContent.text.size - ).flatMap((key) => { - const value = - parsedContent.text.size[key].value; - - // Check if the value is an object with mobile and desktop properties - if ( - typeof value === "object" && - value !== null - ) { - return [ - { - name: `mobile/${key}`, - value: parseFloat(value.mobile), - }, - { - name: `desktop/${key}`, - value: parseFloat(value.desktop), - }, - ]; - } - - // If value is not an object, return a single entry - return { - name: key, - value: parseFloat(value), - }; - }); - - const lineHeightArray = Object.keys( - parsedContent.text.lineHeight - ).flatMap((key) => { - const value = - parsedContent.text.lineHeight[key].value; - - // Check if the value is an object with mobile and desktop properties - if ( - typeof value === "object" && - value !== null - ) { - return [ - { - name: `mobile/${key}`, - value: parseFloat(value.mobile), - }, - { - name: `desktop/${key}`, - value: parseFloat(value.desktop), - }, - ]; - } - - // If value is not an object, return a single entry - return { - name: key, - value: parseFloat(value), - }; - }); - - // Accumulate results - accumulator[fileName] = { - light: processColors( - parsedContent, - "light", - allParsedContent - ), - dark: processColors( - parsedContent, - "dark", - allParsedContent - ), - palette: paletteArray, - radius: radiusArray, - fontWeight: fontWeightArray, - fontSize: fontSizeArray, - lineHeight: lineHeightArray, - }; - - return accumulator; - }, {}); -}; - -export default extractJsonData; diff --git a/tokens/figma/extract-json-data-middleware.mjs b/tokens/figma/extract-json-data.mjs similarity index 52% rename from tokens/figma/extract-json-data-middleware.mjs rename to tokens/figma/extract-json-data.mjs index 65b1be8b441..bf1e94ec785 100644 --- a/tokens/figma/extract-json-data-middleware.mjs +++ b/tokens/figma/extract-json-data.mjs @@ -2,7 +2,391 @@ import fs from "fs"; import path from "path"; import { hexToRgba } from "./utils.mjs"; -const extractJsonData = ( +export const extractSkinJsonData = ( + jsonFiles, + directoryPath +) => { + const allParsedContent = {}; // Initialize once to store all parsed content + + // First pass to gather allParsedContent + jsonFiles.forEach((file) => { + const filePath = path.resolve( + directoryPath, + file + ); + const fileContent = fs.readFileSync( + filePath, + "utf8" + ); + const parsedContent = JSON.parse(fileContent); + + // Extract file name without extension to use as a key + const fileName = file.split(".")[0]; + + // Store the parsed content in the allParsedContent object + allParsedContent[fileName] = parsedContent; + }); + + // Second pass to process each file + return jsonFiles.reduce((accumulator, file) => { + const filePath = path.resolve( + directoryPath, + file + ); + const fileContent = fs.readFileSync( + filePath, + "utf8" + ); + const parsedContent = JSON.parse(fileContent); + + // Extract file name without extension + const fileName = file.split(".")[0]; + + function processColors( + parsedContent, + theme, + allParsedContent + ) { + if (!["light", "dark"].includes(theme)) { + throw new Error( + `Invalid theme: ${theme}. Expected 'light' or 'dark'.` + ); + } + + const themeColors = parsedContent[theme]; + + function getPaletteName(value) { + const regexMatch = value.match( + /{palette\.(.*?)}/ + ); + if (regexMatch) { + return regexMatch[1]; + } + const rgbaMatch = value.match( + /rgba\(\{palette\.(.*?)\},\s*\d*\.?\d*\)/ + ); + if (rgbaMatch) { + return rgbaMatch[1]; + } + throw new Error( + `Unexpected color format: ${value}` + ); + } + + function getPaletteValue(colorName) { + const paletteValue = + parsedContent.global.palette[colorName] + ?.value; + if (!paletteValue) { + throw new Error( + `Color ${colorName} not found in palette` + ); + } + return paletteValue; + } + + function getMaxStopsAcrossBrands( + allParsedContent, + key, + theme + ) { + let maxStops = 0; + let isGradientInAnyBrand = false; + + Object.values(allParsedContent).forEach( + (content) => { + const colors = content[theme][key]; + if ( + colors && + colors.type === "linear-gradient" + ) { + isGradientInAnyBrand = true; + maxStops = Math.max( + maxStops, + colors.value.colors.length + ); + } + } + ); + + return { maxStops, isGradientInAnyBrand }; + } + + return Object.keys(themeColors).flatMap( + (key) => { + const colorData = themeColors[key]; + const { value, type } = colorData; + + const { + maxStops, + isGradientInAnyBrand, + } = getMaxStopsAcrossBrands( + allParsedContent, + key, + theme + ); + + // Handle gradients first + if ( + type === "linear-gradient" && + typeof value === "object" + ) { + // Map colors in gradient + return Array.from( + { length: maxStops }, + (_, index) => { + if (index < value.colors.length) { + // Use the actual gradient stop color + const color = + value.colors[index]; + const alphaMatch = + color.value.match( + /rgba\([^)]+,\s*([^)]+)\)/ + ); + const alpha = alphaMatch + ? alphaMatch[1] + : "1"; + const baseColorName = + getPaletteName(color.value); + + return alpha === "1" + ? { + name: `${theme}/${key}-stop-${ + index + 1 + }`, + value: baseColorName, + hasAlias: true, + } + : { + name: `${theme}/${key}-stop-${ + index + 1 + }`, + value: hexToRgba( + getPaletteValue( + baseColorName + ), + parseFloat(alpha) + ), + hasAlias: false, + }; + } else if (isGradientInAnyBrand) { + // If this brand does not have a gradient, repeat the base color to match stops + const baseColorName = + getPaletteName( + value.colors[0].value + ); + return { + name: `${theme}/${key}-stop-${ + index + 1 + }`, + value: hexToRgba( + getPaletteValue( + baseColorName + ) + ), + hasAlias: false, + }; + } + } + ).filter(Boolean); + } + + // Handle solid colors or aliases when a gradient exists in other brands + if ( + type !== "linear-gradient" && + isGradientInAnyBrand + ) { + const baseColorName = + getPaletteName(value); + // Repeat the solid color to match the gradient stops + return Array.from( + { length: maxStops }, + (_, index) => ({ + name: `${theme}/${key}-stop-${ + index + 1 + }`, + value: hexToRgba( + getPaletteValue(baseColorName) + ), + hasAlias: false, + }) + ); + } + + // Handle solid colors or aliases normally + if ( + typeof value === "string" && + !value.startsWith("rgba") + ) { + const baseColorName = + getPaletteName(value); + + return { + name: `${theme}/${key}`, + value: hexToRgba( + getPaletteValue(baseColorName) + ), + + hasAlias: true, + }; + } + + // Handle rgba colors + if ( + typeof value === "string" && + value.startsWith("rgba") + ) { + const alphaMatch = value.match( + /rgba\([^)]+,\s*([^)]+)\)/ + ); + const alpha = alphaMatch + ? alphaMatch[1] + : "1"; + const baseColorName = + getPaletteName(value); + + return alpha === "1" + ? { + name: `${theme}/${key}`, + value: baseColorName, + hasAlias: true, + } + : { + name: `${theme}/${key}`, + value: hexToRgba( + getPaletteValue( + baseColorName + ), + parseFloat(alpha) + ), + hasAlias: false, + }; + } + + throw new Error( + `Unexpected color format for key: ${key}` + ); + } + ); + } + + // Other token processing logic + const paletteArray = Object.keys( + parsedContent.global.palette + ).map((key) => ({ + name: key, + value: hexToRgba( + parsedContent.global.palette[key].value + ), + })); + + const radiusArray = Object.keys( + parsedContent.radius + ).map((key) => ({ + name: key, + value: + typeof parsedContent.radius[key].value === + "string" + ? parsedContent.radius[key].value === + "circle" + ? 999 // If the value is "circle", set it to 999 + : parseFloat( + parsedContent.radius[key].value + ) // Otherwise, convert it to a float + : parsedContent.radius[key].value, // If it's not a string, use the original value + })); + + const fontWeightArray = Object.keys( + parsedContent.text.weight + ).map((key) => ({ + name: key, + value: parsedContent.text.weight[key].value, + })); + + const fontSizeArray = Object.keys( + parsedContent.text.size + ).flatMap((key) => { + const value = + parsedContent.text.size[key].value; + + // Check if the value is an object with mobile and desktop properties + if ( + typeof value === "object" && + value !== null + ) { + return [ + { + name: `mobile/${key}`, + value: parseFloat(value.mobile), + }, + { + name: `desktop/${key}`, + value: parseFloat(value.desktop), + }, + ]; + } + + // If value is not an object, return a single entry + return { + name: key, + value: parseFloat(value), + }; + }); + + const lineHeightArray = Object.keys( + parsedContent.text.lineHeight + ).flatMap((key) => { + const value = + parsedContent.text.lineHeight[key].value; + + // Check if the value is an object with mobile and desktop properties + if ( + typeof value === "object" && + value !== null + ) { + return [ + { + name: `mobile/${key}`, + value: parseFloat(value.mobile), + }, + { + name: `desktop/${key}`, + value: parseFloat(value.desktop), + }, + ]; + } + + // If value is not an object, return a single entry + return { + name: key, + value: parseFloat(value), + }; + }); + + // Accumulate results + accumulator[fileName] = { + light: processColors( + parsedContent, + "light", + allParsedContent + ), + dark: processColors( + parsedContent, + "dark", + allParsedContent + ), + palette: paletteArray, + radius: radiusArray, + fontWeight: fontWeightArray, + fontSize: fontSizeArray, + lineHeight: lineHeightArray, + }; + + return accumulator; + }, {}); +}; + +export const extractMiddlewareJsonData = ( jsonFiles, directoryPath ) => { @@ -407,5 +791,3 @@ const extractJsonData = ( return accumulator; }, {}); }; - -export default extractJsonData; diff --git a/tokens/figma/index.mjs b/tokens/figma/index.mjs new file mode 100644 index 00000000000..068db493d49 --- /dev/null +++ b/tokens/figma/index.mjs @@ -0,0 +1,72 @@ +import dotenv from "dotenv"; +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; + +import { updateSkinFiles } from "./update-skins.mjs"; +import { updateMiddleware } from "./update-middleware.mjs"; + +import { + extractSkinJsonData, + extractMiddlewareJsonData, +} from "./extract-json-data.mjs"; + +dotenv.config({ path: "../../.env" }); +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const tokensPath = path.resolve(__dirname, "../"); + +const files = fs.readdirSync(tokensPath); + +const jsonFiles = files.filter((file) => + file.endsWith(".json") +); +const jsonDataForSkin = extractSkinJsonData( + jsonFiles, + tokensPath +); + +const jsonDataForMiddleware = + extractMiddlewareJsonData( + jsonFiles, + tokensPath + ); + +const FIGMA_TOKEN = process.env.FIGMA_TOKEN; +const MIDDLEWARE_TOKEN = + process.env.MIDDLEWARE_KEY; + +const FILE_KEYS = { + // Remember to sync these with the workflow file + movistar: process.env.MOVISTAR_FILE_KEY, + "o2-new": process.env.O2_NEW_FILE_KEY, + "vivo-new": process.env.VIVO_NEW_FILE_KEY, + telefonica: process.env.TELEFONICA_FILE_KEY, + blau: process.env.BLAU_FILE_KEY, + tu: process.env.TU_FILE_KEY, +}; + +const brands = Object.fromEntries( + Object.entries(FILE_KEYS).map( + ([brand, FILE_KEY]) => [brand, FILE_KEY] + ) +); + +const brandNames = Object.keys(brands); + +async function processAll() { + await updateSkinFiles( + jsonDataForSkin, + brands, + FIGMA_TOKEN + ); + await updateMiddleware( + jsonDataForMiddleware, + brandNames, + MIDDLEWARE_TOKEN, + FIGMA_TOKEN + ); +} + +processAll(); diff --git a/tokens/figma/update-middleware.mjs b/tokens/figma/update-middleware.mjs index 8a7f5569e01..73d66830723 100644 --- a/tokens/figma/update-middleware.mjs +++ b/tokens/figma/update-middleware.mjs @@ -1,12 +1,7 @@ import fetch from "node-fetch"; -import dotenv from "dotenv"; -import fs from "fs"; -import path from "path"; -import { fileURLToPath } from "url"; -import extractJsonData from "./extract-json-data-middleware.mjs"; + import { updateCollections, - updateOrCreateMode, updateOrCreateVariable, updateOrCreateVariableModeValues, generateTempModeId, @@ -15,35 +10,6 @@ import { COLLECTION_NAMES, } from "./utils.mjs"; -dotenv.config({ path: "../../.env" }); -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -const FIGMA_TOKEN = process.env.FIGMA_TOKEN; -const MIDDLEWARE_TOKEN = - process.env.MIDDLEWARE_KEY; - -const tokensPath = path.resolve(__dirname, "../"); - -const files = fs.readdirSync(tokensPath); - -const jsonFiles = files.filter((file) => - file.endsWith(".json") -); -const jsonData = extractJsonData( - jsonFiles, - tokensPath -); - -const brands = [ - "movistar", - "vivo-new", - "o2-new", - "telefonica", - "blau", - "tu", -]; - function formatBrandName(brand) { // Check if the brand is "tu" and return it in uppercase if (brand === "tu") { @@ -67,7 +33,8 @@ function formatBrandName(brand) { export async function updateTheme( jsonData, brand, - FILE_KEY + FILE_KEY, + FIGMA_TOKEN ) { try { // Fetch existing variables and collections from Figma @@ -292,7 +259,8 @@ export async function updateTheme( async function updateSkinColorVariables( brands, - FILE_KEY + FILE_KEY, + FIGMA_TOKEN ) { try { // Step 1: Fetch existing data from "Mode" and "Brand" collections @@ -543,7 +511,8 @@ async function updateSkinColorVariables( async function updateSkinOtherVariables( jsonData, brands, - FILE_KEY + FILE_KEY, + FIGMA_TOKEN ) { const response = await fetch( `https://api.figma.com/v1/files/${FILE_KEY}/variables/local`, @@ -724,7 +693,11 @@ async function updateSkinOtherVariables( return newData; } -async function postCollections(brand, FILE_KEY) { +async function postCollections( + brand, + FILE_KEY, + FIGMA_TOKEN +) { const collectionNames = [ COLLECTION_NAMES.BRAND, COLLECTION_NAMES.COLOR_SCHEME, @@ -762,34 +735,62 @@ async function postCollections(brand, FILE_KEY) { } } -async function processBrand(brand, FILE_KEY) { - await postCollections(brand, FILE_KEY); - await updateTheme(jsonData, brand, FILE_KEY); +async function processBrand( + jsonData, + brand, + FILE_KEY, + FIGMA_TOKEN +) { + await postCollections( + brand, + FILE_KEY, + FIGMA_TOKEN + ); + await updateTheme( + jsonData, + brand, + FILE_KEY, + FIGMA_TOKEN + ); } -async function processAllBrands(brands) { +async function processAllBrands( + jsonData, + brands, + FILE_KEY, + FIGMA_TOKEN +) { for (const brand of brands) { - await processBrand(brand, MIDDLEWARE_TOKEN); + await processBrand( + jsonData, + brand, + FILE_KEY, + FIGMA_TOKEN + ); } } -async function main() { - await processAllBrands(brands); +export async function updateMiddleware( + jsonData, + brands, + FILE_KEY, + FIGMA_TOKEN +) { + await processAllBrands( + jsonData, + brands, + FILE_KEY, + FIGMA_TOKEN + ); await updateSkinColorVariables( brands, - MIDDLEWARE_TOKEN + FILE_KEY, + FIGMA_TOKEN ); await updateSkinOtherVariables( jsonData, brands, - MIDDLEWARE_TOKEN + FILE_KEY, + FIGMA_TOKEN ); } - -// Execute the main function to ensure proper sequence -main().catch((error) => { - console.error( - "Error in main execution:", - error - ); -}); diff --git a/tokens/figma/update-skins.mjs b/tokens/figma/update-skins.mjs index dade060b021..c0853730c0b 100644 --- a/tokens/figma/update-skins.mjs +++ b/tokens/figma/update-skins.mjs @@ -1,52 +1,13 @@ import fetch from "node-fetch"; -import dotenv from "dotenv"; -import fs from "fs"; -import path from "path"; -import { fileURLToPath } from "url"; -import extractJsonData from "./extract-json-data-skins.mjs"; import { updateCollections } from "./utils.mjs"; -dotenv.config({ path: "../../.env" }); -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -const FIGMA_TOKEN = process.env.FIGMA_TOKEN; - -const FILE_KEYS = { - // Remember to sync these with the workflow file - movistar: process.env.MOVISTAR_FILE_KEY, - "o2-new": process.env.O2_NEW_FILE_KEY, - "vivo-new": process.env.VIVO_NEW_FILE_KEY, - telefonica: process.env.TELEFONICA_FILE_KEY, - blau: process.env.BLAU_FILE_KEY, - tu: process.env.TU_FILE_KEY, -}; - -const tokensPath = path.resolve(__dirname, "../"); - -const files = fs.readdirSync(tokensPath); - -const jsonFiles = files.filter((file) => - file.endsWith(".json") -); - -const jsonData = extractJsonData( - jsonFiles, - tokensPath -); - -const brands = Object.fromEntries( - Object.entries(FILE_KEYS).map( - ([brand, FILE_KEY]) => [brand, FILE_KEY] - ) -); - const collectionNames = ["Palette"]; async function updatePalette( jsonData, brand, - FILE_KEY + FILE_KEY, + FIGMA_TOKEN ) { try { const response = await fetch( @@ -234,302 +195,11 @@ async function updatePalette( } } -async function updateVariables( - jsonData, +async function postCollections( brand, - FILE_KEY + FILE_KEY, + FIGMA_TOKEN ) { - try { - const response = await fetch( - `https://api.figma.com/v1/files/${FILE_KEY}/variables/local`, - { - method: "GET", - headers: { - "X-Figma-Token": FIGMA_TOKEN, // Use environment variable - "Content-Type": "application/json", - }, - } - ); - - const figmaData = await response.json(); - - // Figma variables - - const existingVariables = - figmaData.meta.variables; - const existingCollections = - figmaData.meta.variableCollections; - - // Initialize the data object for POST request - const newData = { - variableCollections: [], - variableModes: [], - variables: [], - variableModeValues: [], - }; - - function generateTempId(name, collection) { - return `tempId_${collection}_${name}`; - } - - function findVariableInCollection( - variableName, - collectionName, - existingVariables, - existingCollections - ) { - return Object.values( - existingVariables - ).find((variable) => { - // Check if the variable name matches - if (variable.name !== variableName) - return false; - - // Find the collection in existingCollections using variableCollectionId - const collection = Object.values( - existingCollections - ).find( - (col) => - col.id === - variable.variableCollectionId - ); - - // Check if the collection exists, its name matches the collectionName, - // and if the variable's id is listed in the collection's variableIds - return ( - collection && - collection.name === collectionName && - collection.variableIds.includes( - variable.id - ) - ); - }); - } - - function getVariableAliasId( - variableName, - collectionName, - existingVariables, - existingCollections - ) { - // Step 1: Find the collection - const collection = Object.values( - existingCollections - ).find( - (collection) => - collection.name === collectionName - ); - - if (!collection) { - console.error("Collection not found."); - return null; - } - - // Step 2: Find the variable in the "palette" collection - const variable = Object.values( - existingVariables - ).find( - (variable) => - variable.variableCollectionId === - collection.id && - variable.name === variableName - ); - - if (!variable) { - console.error( - "Variable not found for name:", - variableName - ); - return null; - } - - // Step 3: Retrieve the mode ID for the "palette" collection - const modeId = collection.id; - - return { - variableId: variable.id, - modeId: modeId, - }; - } - - function updateVariables( - variableName, - variableValue, - hasAlias, - collectionName, - aliasedCollectionName, - existingVariables, - existingCollections, - variableType, - variableScopes - ) { - // Find the existing variable by name - const existingVariable = - findVariableInCollection( - variableName, - collectionName, - existingVariables, - existingCollections - ); - - // Find the default mode for the collection - - const existingMode = Object.values( - existingCollections - ).find( - (collection) => - collection.name === collectionName - ).defaultModeId; - - if (existingVariable) { - // If the variable exists, update it - newData.variables.push({ - action: "UPDATE", - id: existingVariable.id, - name: variableName, - resolvedType: variableType, - variableCollectionId: - existingVariable.variableCollectionId, - scopes: variableScopes, - }); - - newData.variableModeValues.push({ - action: "UPDATE", - variableId: existingVariable.id, - modeId: existingMode, - value: hasAlias - ? { - type: "VARIABLE_ALIAS", - id: getVariableAliasId( - variableValue, - aliasedCollectionName, - existingVariables, - existingCollections - ).variableId, - } - : variableValue, - }); - allVariableNamesInCurrentData.add( - variableName - ); - } else { - // If the variable doesn't exist, create it - const tempId = generateTempId( - variableName, - collectionName - ); - - const collectionId = Object.values( - existingCollections - ).find( - (collection) => - collection.name === collectionName - ).id; - - newData.variables.push({ - action: "CREATE", - id: tempId, - name: variableName, - variableCollectionId: collectionId, - resolvedType: variableType, - scopes: variableScopes, - }); - - newData.variableModeValues.push({ - action: "CREATE", - variableId: tempId, - modeId: existingMode, - value: hasAlias - ? { - type: "VARIABLE_ALIAS", - id: getVariableAliasId( - variableValue, - existingVariables, - existingCollections - ).variableId, - } - : variableValue, - }); - - allVariableNamesInCurrentData.add( - variableName - ); - } - } - - function removeUnusedVariables( - existingVariables, - currentJsonVariables - ) { - // Identify and delete variables that are not in the current data - Object.values(existingVariables).forEach( - (variable) => { - if ( - !currentJsonVariables.has( - variable.name - ) - ) { - newData.variables.push({ - action: "DELETE", - id: variable.id, - }); - } - } - ); - } - - const allVariableNamesInCurrentData = - new Set(); - - const variableGroups = [ - { - variables: jsonData[brand].palette, - collectionName: "palette", - resolvedType: "COLOR", - variableScopes: ["ALL_SCOPES"], - }, - ]; - - variableGroups.forEach( - ({ - variables, - collectionName, - aliasedCollectionName, - resolvedType, - variableScopes, - }) => { - variables.forEach((variable) => { - updateVariables( - variable.name, - variable.value, - variable.hasAlias, - collectionName, - aliasedCollectionName, - existingVariables, - existingCollections, - resolvedType, - variableScopes, - allVariableNamesInCurrentData - ); - }); - } - ); - - removeUnusedVariables( - existingVariables, - allVariableNamesInCurrentData - ); - - // Return the processed data for further use - return newData; - } catch (error) { - console.error("Error:", error); - throw error; // rethrow the error to be handled later - } -} - - -async function postCollections(brand, FILE_KEY) { try { const newData = await updateCollections( collectionNames, @@ -562,12 +232,18 @@ async function postCollections(brand, FILE_KEY) { } } -async function postPalette(brand, FILE_KEY) { +async function postPalette( + jsonData, + brand, + FILE_KEY, + FIGMA_TOKEN +) { try { const newData = await updatePalette( jsonData, brand, - FILE_KEY + FILE_KEY, + FIGMA_TOKEN ); const response = await fetch( @@ -597,34 +273,38 @@ async function postPalette(brand, FILE_KEY) { // Process data for a specific brand -async function processBrand(brand, FILE_KEY) { - await postCollections(brand, FILE_KEY); - await postPalette(brand, FILE_KEY); +async function processBrand( + jsonData, + brand, + FILE_KEY, + FIGMA_TOKEN +) { + await postCollections( + brand, + FILE_KEY, + FIGMA_TOKEN + ); + await postPalette( + jsonData, + brand, + FILE_KEY, + FIGMA_TOKEN + ); } -// Process all brands - -async function processAllBrands(brands) { +export async function updateSkinFiles( + jsonData, + brands, + FIGMA_TOKEN +) { for (const [brand, FILE_KEY] of Object.entries( brands )) { - await processBrand(brand, FILE_KEY); - } -} - -// Get the selected brand from command line arguments - -const selectedBrand = process.argv[2]; - -if (selectedBrand === "all") { - processAllBrands(brands); -} else { - const FILE_KEY = brands[selectedBrand]; - if (FILE_KEY) { - processBrand(selectedBrand, FILE_KEY); - } else { - console.error( - `Brand ${selectedBrand} not found.` + await processBrand( + jsonData, + brand, + FILE_KEY, + FIGMA_TOKEN ); } } From ec2500607c29d10e16960d013f281767dab14073 Mon Sep 17 00:00:00 2001 From: Alex Bueno Date: Thu, 12 Sep 2024 14:57:00 +0200 Subject: [PATCH 46/71] Update old collection names references Co-authored-by: Ignacio Ceballos (Yayo) --- tokens/figma/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tokens/figma/README.md b/tokens/figma/README.md index ee085368fe5..eab7a589470 100644 --- a/tokens/figma/README.md +++ b/tokens/figma/README.md @@ -1,6 +1,6 @@ # Project Overview -This project is designed to update Figma variables based on a JSON input, primarily focused on managing brand themes, colors, and other design tokens. The project retrieves existing variables from Figma, processes the provided JSON data, and updates or creates new variables in collections "Theme" and "Skin". +This project is designed to update Figma variables based on a JSON input, primarily focused on managing brand themes, colors, and other design tokens. The project retrieves existing variables from Figma, processes the provided JSON data, and updates or creates new variables in collections "Mode" and "Brand". ## Features @@ -33,9 +33,9 @@ This function updates the theme variables in Figma for a specific brand. It: ### `updateSkinColorVariables(brands, FILE_KEY)` -This function focuses on updating color variables in the "Skin" collection. It: +This function focuses on updating color variables in the "Brand" collection. It: -- Maps color variables from the "Theme" collection to the "Skin" collection. +- Maps color variables from the "Mode" collection to the "Brand" collection. - Creates or updates modes for each brand. - Ensures proper aliasing of variables between collections. From fb299f5c76a00e878c5dff4cf37599733ee888bf Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Thu, 12 Sep 2024 18:06:55 +0200 Subject: [PATCH 47/71] Update sync figma tokens action --- .github/workflows/sync-figma-tokens.yml | 101 +----------------------- 1 file changed, 3 insertions(+), 98 deletions(-) diff --git a/.github/workflows/sync-figma-tokens.yml b/.github/workflows/sync-figma-tokens.yml index 0d4f2ecc218..545fd33ee36 100644 --- a/.github/workflows/sync-figma-tokens.yml +++ b/.github/workflows/sync-figma-tokens.yml @@ -12,115 +12,20 @@ on: - "tokens/blau.json" - "tokens/tu.json" - workflow_dispatch: - inputs: - movistar: - type: boolean - default: true - vivo-new: - type: boolean - default: true - o2-new: - type: boolean - default: true - telefonica: - type: boolean - default: true - blau: - type: boolean - default: true - tu: - type: boolean - default: true - jobs: sync-figma-brand: runs-on: ubuntu-latest env: FIGMA_TOKEN: ${{ secrets.FIGMA_TOKEN }} - MOVISTAR_FILE_KEY: ${{ secrets.MOVISTAR_FILE_KEY }} - VIVO_NEW_FILE_KEY: ${{ secrets.VIVO_NEW_FILE_KEY }} - O2_NEW_FILE_KEY: ${{ secrets.O2_NEW_FILE_KEY }} - TELEFONICA_FILE_KEY: ${{ secrets.TELEFONICA_FILE_KEY }} - BLAU_FILE_KEY: ${{ secrets.BLAU_FILE_KEY }} - TU_FILE_KEY: ${{ secrets.TU_FILE_KEY }} steps: - name: Checkout repository uses: actions/checkout@v4 - - name: Filter changes - id: filter - uses: dorny/paths-filter@v2 - with: - filters: | - movistar: - - 'tokens/movistar.json' - vivo-new: - - 'tokens/vivo-new.json' - o2-new: - - 'tokens/o2-new.json' - telefonica: - - 'tokens/telefonica.json' - blau: - - 'tokens/blau.json' - tu: - - 'tokens/tu.json' - - - name: Determine Execution Mode - id: determine-mode - run: | - if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then - selected_brands="" - if [ "${{ github.event.inputs.movistar }}" == "true" ]; then - selected_brands+=" movistar" - fi - if [ "${{ github.event.inputs.vivo-new }}" == "true" ]; then - selected_brands+=" vivo-new" - fi - if [ "${{ github.event.inputs.o2-new }}" == "true" ]; then - selected_brands+=" o2-new" - fi - if [ "${{ github.event.inputs.telefonica }}" == "true" ]; then - selected_brands+=" telefonica" - fi - if [ "${{ github.event.inputs.blau }}" == "true" ]; then - selected_brands+=" blau" - fi - if [ "${{ github.event.inputs.tu }}" == "true" ]; then - selected_brands+=" tu" - fi - echo "Selected brands: $selected_brands" - echo "BRAND_NAME=$selected_brands" >> $GITHUB_ENV - else - selected_brands="" - if [ "${{ steps.filter.outputs.movistar }}" == "true" ]; then - selected_brands+=" movistar" - fi - if [ "${{ steps.filter.outputs.vivo-new }}" == "true" ]; then - selected_brands+=" vivo-new" - fi - if [ "${{ steps.filter.outputs.o2-new }}" == "true" ]; then - selected_brands+=" o2-new" - fi - if [ "${{ steps.filter.outputs.telefonica }}" == "true" ]; then - selected_brands+=" telefonica" - fi - if [ "${{ steps.filter.outputs.blau }}" == "true" ]; then - selected_brands+=" blau" - fi - if [ "${{ steps.filter.outputs.tu }}" == "true" ]; then - selected_brands+=" tu" - fi - echo "Detected changes in brands: $selected_brands" - echo "BRAND_NAME=$selected_brands" >> $GITHUB_ENV - fi + - name: Install dependencies + run: npm install - name: Run sync for the brand(s) working-directory: tokens/figma - run: | - for brand in $BRAND_NAME; do - echo "Syncing brand: $brand" - node test.mjs "$brand" - done + run: node index.mjs From 8ff1f9d702ab134aae8da460105dde1facbed482 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Thu, 12 Sep 2024 18:17:30 +0200 Subject: [PATCH 48/71] Update root README --- README.md | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 974c5f9085a..7dc4e6fe06a 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,12 @@   -| Other Mística Repos | Description | -| :--------------------------------------------------------------- | :-------------------------------------------------------- | -| [mistica-web](https://github.com/Telefonica/mistica-web) | Repository with code libraries for Mística in web | -| [mistica-ios](https://github.com/Telefonica/mistica-ios) | Repository with code libraries for Mística in iOS | -| [mistica-android](https://github.com/Telefonica/mistica-android) | Repository with code libraries for Mística in Android | -| [mistica-icons](https://github.com/Telefonica/mistica-icons) | The source of truth for icons in our digital products | +| Other Mística Repos | Description | +| :--------------------------------------------------------------- | :---------------------------------------------------- | +| [mistica-web](https://github.com/Telefonica/mistica-web) | Repository with code libraries for Mística in web | +| [mistica-ios](https://github.com/Telefonica/mistica-ios) | Repository with code libraries for Mística in iOS | +| [mistica-android](https://github.com/Telefonica/mistica-android) | Repository with code libraries for Mística in Android | +| [mistica-icons](https://github.com/Telefonica/mistica-icons) | The source of truth for icons in our digital products | --- @@ -33,15 +33,5 @@ ## How to sync design tokens -If you want to sync design tokens with Figma files you can use [Figma Tokens plugin](https://www.figma.com/community/plugin/843461159747178978/Figma-Tokens) and setup the plugin with the following information. - -1. Open Figma Tokens Plugin, go to `Settings` and select `Github` in Token Storage -2. Add new credentials - -- **Name:** The name of the brand -- **Personal Access Token:** you have to generate a token from Github. [Read how to do it](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token#creating-a-personal-access-token-classic) -- **Repository:** `Telefonica/mistica-design` -- **Default Branch:** `production` -- **File Path:** `tokens/brandName.json` (see files [here](./tokens/)) - -![image](https://user-images.githubusercontent.com/6722153/166447592-e3d1b545-199d-4155-9024-2fb88351b444.png) 3. Finally, go to `Tokens`, select `Global` and `Apply to document` and clic in `Update` +> [!NOTE] +> We're no longer using [Figma Tokens plugin](https://www.figma.com/community/plugin/843461159747178978/Figma-Tokens) to sync tokens with files. We use Figma API to update variables in our libraries. More about this proccess in [Figma tokens script README](). From 695266ab373c0f7658c1600bd7d4ea9864e44d4a Mon Sep 17 00:00:00 2001 From: "Ignacio Ceballos (Yayo)" Date: Thu, 12 Sep 2024 18:34:46 +0200 Subject: [PATCH 49/71] duplicated file --- .github/workflows/tokens-to-figma.yml | 61 --------------------------- 1 file changed, 61 deletions(-) delete mode 100644 .github/workflows/tokens-to-figma.yml diff --git a/.github/workflows/tokens-to-figma.yml b/.github/workflows/tokens-to-figma.yml deleted file mode 100644 index 54130821bd0..00000000000 --- a/.github/workflows/tokens-to-figma.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: Sync tokens to Figma - -on: - pull_request: - types: - - closed - branches: - - main - paths: - - "tokens/*.json" - workflow_dispatch: - inputs: - brand: - description: "Select the brand to update variables in Figma" - required: true - default: "all" - type: choice - options: - - all - - movistar - - o2-new - - vivo-new - - telefonica - - blau - - tu - -jobs: - sync-figma-brands: - runs-on: ubuntu-latest - - env: - # Remember to sync these with the index.mjs file - FIGMA_TOKEN: ${{ secrets.FIGMA_TOKEN }} - MOVISTAR_FILE_KEY: ${{ secrets.MOVISTAR_FILE_KEY }} - O2_NEW_FILE_KEY: ${{ secrets.O2_NEW_FILE_KEY }} - VIVO_NEW_FILE_KEY: ${{ secrets.VIVO_NEW_FILE_KEY }} - TELEFONICA_FILE_KEY: ${{ secrets.TELEFONICA_FILE_KEY }} - BLAU_FILE_KEY: ${{ secrets.BLAU_FILE_KEY }} - TU_FILE_KEY: ${{ secrets.TU_FILE_KEY }} - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Set up Node.js - uses: actions/setup-node@v3 - with: - node-version: "16" - - - name: Install npm dependencies - run: npm install - - - name: Sync ${{ github.event.inputs.brand }} - working-directory: tokens/figma - run: | - node index.mjs ${{ github.event.inputs.brand }} - - # - name: Sync Mística Skins - # working-directory: tokens/figma - # run: | - # node index.mjs ${{ github.event.inputs.brand }} From f14e760fc30db2c8b5e387fd37860aa8c75674e0 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Fri, 13 Sep 2024 10:13:02 +0200 Subject: [PATCH 50/71] Naming fixes --- tokens/figma/update-middleware.mjs | 38 ++++++++++++++++-------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/tokens/figma/update-middleware.mjs b/tokens/figma/update-middleware.mjs index 73d66830723..6daf61499b3 100644 --- a/tokens/figma/update-middleware.mjs +++ b/tokens/figma/update-middleware.mjs @@ -286,15 +286,15 @@ async function updateSkinColorVariables( (col) => col.name === COLLECTION_NAMES.COLOR_SCHEME ); - const skinCollection = Object.values( + const brandCollection = Object.values( themeCollections ).find( (col) => col.name === COLLECTION_NAMES.BRAND ); - if (!themeCollection || !skinCollection) { + if (!themeCollection || !brandCollection) { throw new Error( - "Theme or Skin collection not found" + "Mode or Brand collection not found" ); } @@ -309,15 +309,15 @@ async function updateSkinColorVariables( themeCollection.id ); - const existingSkinVariables = Object.values( + const existingBrandVariables = Object.values( themeVariables ).filter( (variable) => variable.variableCollectionId === - skinCollection.id + brandCollection.id ); - // Step 4: Prepare new variables data for the Skin collection + // Step 4: Prepare new variables data for the Brand collection const newData = { variables: [], variableModeValues: [], @@ -331,7 +331,7 @@ async function updateSkinColorVariables( formatBrandName(firstBrand); const defaultMode = - skinCollection.modes?.find( + brandCollection.modes?.find( (mode) => mode.name === DEFAULT_FIGMA_MODENAME || mode.name === firstBrand || @@ -348,7 +348,8 @@ async function updateSkinColorVariables( action: "UPDATE", id: defaultMode.modeId, name: formattedFirstBrand, - variableCollectionId: skinCollection.id, + variableCollectionId: + brandCollection.id, }); } } else { @@ -359,7 +360,7 @@ async function updateSkinColorVariables( formattedFirstBrand, COLLECTION_NAMES.BRAND ), - variableCollectionId: skinCollection.id, + variableCollectionId: brandCollection.id, }); } @@ -368,7 +369,7 @@ async function updateSkinColorVariables( formatBrandName(brand); const existingMode = - skinCollection.modes.find( + brandCollection.modes.find( (mode) => mode.name === brand || mode.name === formattedBrand @@ -381,7 +382,7 @@ async function updateSkinColorVariables( id: existingMode.modeId, name: formattedBrand, variableCollectionId: - skinCollection.id, + brandCollection.id, }); } } else { @@ -392,7 +393,8 @@ async function updateSkinColorVariables( formattedBrand, COLLECTION_NAMES.BRAND ), - variableCollectionId: skinCollection.id, + variableCollectionId: + brandCollection.id, }); } }); @@ -441,7 +443,7 @@ async function updateSkinColorVariables( targetCollectionName: variable.targetCollectionName, existingVariables: - existingSkinVariables, + existingBrandVariables, existingCollections: themeCollections, }); @@ -466,10 +468,10 @@ async function updateSkinColorVariables( ? defaultMode.name : formattedBrand, targetCollectionName: - COLLECTION_NAMES.BRAND, // Assuming the collection name is 'Skin' + COLLECTION_NAMES.BRAND, // Assuming the collection name is 'Brand' existingCollections: themeCollections, // Pass the fetched collections existingVariables: - existingSkinVariables, // Pass the existing variables in the Skin collection + existingBrandVariables, // Pass the existing variables in the Brand collection }); if (variableModeValuesData) { @@ -480,7 +482,7 @@ async function updateSkinColorVariables( } } - // Step 9: Send the data to update the Skin collection (POST) + // Step 9: Send the data to update the Brand collection (POST) const updateResponse = await fetch( `https://api.figma.com/v1/files/${FILE_KEY}/variables`, { @@ -497,7 +499,7 @@ async function updateSkinColorVariables( const errorText = await updateResponse.text(); throw new Error( - `Error updating Skin collection: ${updateResponse.statusText}. Response: ${errorText}` + `Error updating Brand collection: ${updateResponse.statusText}. Response: ${errorText}` ); } @@ -538,7 +540,7 @@ async function updateSkinOtherVariables( const fontFamilies = { movistar: "On Air", - "vivo-new": "Vivo type", + "vivo-new": "Vivo Type", "o2-new": "On Air", telefonica: "Telefonica Sans", blau: "SF Pro Text", From 21ef26fb04f3e8519ae74c63185dd0c5c3bc55f6 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Fri, 13 Sep 2024 10:15:16 +0200 Subject: [PATCH 51/71] Remove skin references --- tokens/figma/update-middleware.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tokens/figma/update-middleware.mjs b/tokens/figma/update-middleware.mjs index 6daf61499b3..6fc78205802 100644 --- a/tokens/figma/update-middleware.mjs +++ b/tokens/figma/update-middleware.mjs @@ -672,7 +672,7 @@ async function updateSkinOtherVariables( } } - // Make the POST request to update the variables and mode values in the Skin collection + // Make the POST request to update the variables and mode values in the Brand collection const updateResponse = await fetch( `https://api.figma.com/v1/files/${FILE_KEY}/variables`, { @@ -688,7 +688,7 @@ async function updateSkinOtherVariables( if (!updateResponse.ok) { const errorText = await updateResponse.text(); throw new Error( - `Error updating Skin collection: ${updateResponse.statusText}. Response: ${errorText}` + `Error updating Brand collection: ${updateResponse.statusText}. Response: ${errorText}` ); } From c1f33144c516c8ca7c5091091ede7edf99186afe Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Fri, 13 Sep 2024 10:47:46 +0200 Subject: [PATCH 52/71] Remove modes magic strings --- tokens/figma/update-middleware.mjs | 23 ++++++++--------- tokens/figma/utils.mjs | 40 ++++++++++++++++-------------- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/tokens/figma/update-middleware.mjs b/tokens/figma/update-middleware.mjs index 6fc78205802..1c74a61c3e1 100644 --- a/tokens/figma/update-middleware.mjs +++ b/tokens/figma/update-middleware.mjs @@ -6,8 +6,8 @@ import { updateOrCreateVariableModeValues, generateTempModeId, VARIABLE_TYPES, - DEFAULT_FIGMA_MODENAME, COLLECTION_NAMES, + MODE_NAMES, } from "./utils.mjs"; function formatBrandName(brand) { @@ -91,23 +91,23 @@ export async function updateTheme( action: "UPDATE", id: defaultMode.modeId, variableCollectionId: collection.id, - name: "Light", + name: MODE_NAMES.LIGHT, }); } const darkMode = findModeByName( collection, - "Dark" + MODE_NAMES.DARK ); if (!darkMode) { newData.variableModes.push({ action: "CREATE", id: generateTempModeId( - "Dark", + MODE_NAMES.DARK, COLLECTION_NAMES.COLOR_SCHEME ), variableCollectionId: collection.id, - name: "Dark", + name: MODE_NAMES.DARK, }); } }; @@ -149,7 +149,7 @@ export async function updateTheme( collectionId ]?.modes.find( (mode) => - mode.name === DEFAULT_FIGMA_MODENAME + mode.name === MODE_NAMES.DEFAULT ); // Prepare the variable data @@ -174,8 +174,8 @@ export async function updateTheme( hasAlias: false, }, targetModeName: defaultMode - ? DEFAULT_FIGMA_MODENAME - : "Light", + ? MODE_NAMES.DEFAULT + : MODE_NAMES.LIGHT, targetCollectionName: collectionName, existingCollections, existingVariables, @@ -196,7 +196,7 @@ export async function updateTheme( value: darkVariable.value, hasAlias: false, }, - targetModeName: "Dark", + targetModeName: MODE_NAMES.DARK, targetCollectionName: collectionName, existingCollections, @@ -333,7 +333,7 @@ async function updateSkinColorVariables( const defaultMode = brandCollection.modes?.find( (mode) => - mode.name === DEFAULT_FIGMA_MODENAME || + mode.name === MODE_NAMES.DEFAULT || mode.name === firstBrand || mode.name === formattedFirstBrand ); @@ -341,8 +341,7 @@ async function updateSkinColorVariables( if (defaultMode) { if ( defaultMode.name === firstBrand || - defaultMode.name === - DEFAULT_FIGMA_MODENAME + defaultMode.name === MODE_NAMES.DEFAULT ) { newData.variableModes.push({ action: "UPDATE", diff --git a/tokens/figma/utils.mjs b/tokens/figma/utils.mjs index fc5ead39c50..11cbde7d384 100644 --- a/tokens/figma/utils.mjs +++ b/tokens/figma/utils.mjs @@ -1,3 +1,24 @@ +export const VARIABLE_TYPES = { + COLOR: "COLOR", + FLOAT: "FLOAT", + STRING: "STRING", + FONT_WEIGHT: "FONT_WEIGHT", + FONT_SIZE: "FONT_SIZE", + LINE_HEIGHT: "LINE_HEIGHT", +}; + +export const COLLECTION_NAMES = { + BRAND: "Brand", + COLOR_SCHEME: "Mode", + PALETTE: "Palette", +}; + +export const MODE_NAMES = { + DEFAULT: "Mode 1", + LIGHT: "Light", + DARK: "Dark", +}; + export function hexToRgba(hex, alpha = 1) { // Remove the leading # if it's present hex = hex.replace(/^#/, ""); @@ -32,8 +53,6 @@ export function generateTempModeId( return `tempId_${targetCollection}_${targetMode}`; } -export const DEFAULT_FIGMA_MODENAME = "Mode 1"; - export async function updateCollections( collections, FILE_KEY, @@ -137,7 +156,7 @@ export async function updateOrCreateMode({ // Find the default mode (e.g., "Mode 1" or "Default") const defaultMode = existingModes.find( - (m) => m.name === DEFAULT_FIGMA_MODENAME // Replace with actual default mode name if different + (m) => m.name === DMODE_NAMES.DEFAULT // Replace with actual default mode name if different ); // Find the target mode by its name @@ -314,18 +333,3 @@ export async function updateOrCreateVariableModeValues({ : variable.value, }; } - -export const VARIABLE_TYPES = { - COLOR: "COLOR", - FLOAT: "FLOAT", - STRING: "STRING", - FONT_WEIGHT: "FONT_WEIGHT", - FONT_SIZE: "FONT_SIZE", - LINE_HEIGHT: "LINE_HEIGHT", -}; - -export const COLLECTION_NAMES = { - BRAND: "Brand", - COLOR_SCHEME: "Mode", - PALETTE: "Palette", -}; From 9ebdc95af2e7a0493731ed9df01d7a7a9ce27567 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Fri, 13 Sep 2024 10:55:32 +0200 Subject: [PATCH 53/71] Rename iconSet variable group --- tokens/figma/update-middleware.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tokens/figma/update-middleware.mjs b/tokens/figma/update-middleware.mjs index 1c74a61c3e1..31a1e89f41d 100644 --- a/tokens/figma/update-middleware.mjs +++ b/tokens/figma/update-middleware.mjs @@ -604,7 +604,7 @@ async function updateSkinOtherVariables( { variables: [ { - name: "icons/Icon Set", + name: "icons/iconSet", value: iconSets[brand], }, ], From f4d67fa518a7f9f157c4073103679497172939f5 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Fri, 13 Sep 2024 11:03:39 +0200 Subject: [PATCH 54/71] Add themeVariant variable group --- tokens/figma/extract-json-data.mjs | 9 +++++++++ tokens/figma/update-middleware.mjs | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/tokens/figma/extract-json-data.mjs b/tokens/figma/extract-json-data.mjs index bf1e94ec785..391e81f49aa 100644 --- a/tokens/figma/extract-json-data.mjs +++ b/tokens/figma/extract-json-data.mjs @@ -769,6 +769,14 @@ export const extractMiddlewareJsonData = ( }; }); + const themeVariantArray = Object.keys( + parsedContent.themeVariant + ).map((key) => ({ + name: `themeVariant/${key}`, + value: + parsedContent.themeVariant[key].value, + })); + // Accumulate results accumulator[fileName] = { light: processColors( @@ -786,6 +794,7 @@ export const extractMiddlewareJsonData = ( fontWeight: fontWeightArray, fontSize: fontSizeArray, lineHeight: lineHeightArray, + themeVariant: themeVariantArray, }; return accumulator; diff --git a/tokens/figma/update-middleware.mjs b/tokens/figma/update-middleware.mjs index 31a1e89f41d..d2d11af148a 100644 --- a/tokens/figma/update-middleware.mjs +++ b/tokens/figma/update-middleware.mjs @@ -589,6 +589,14 @@ async function updateSkinOtherVariables( variableScopes: ["LINE_HEIGHT"], hasAlias: false, }, + { + variables: + jsonData[brand]?.themeVariant || [], + collectionName: COLLECTION_NAMES.BRAND, + resolvedType: VARIABLE_TYPES.STRING, + variableScopes: ["ALL_SCOPES"], + hasAlias: false, + }, { variables: [ { From 000073f1654bedda1b21b669e1b40e12f193bd23 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Fri, 13 Sep 2024 11:20:43 +0200 Subject: [PATCH 55/71] remove updateSkins magicStrings --- tokens/figma/update-skins.mjs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tokens/figma/update-skins.mjs b/tokens/figma/update-skins.mjs index c0853730c0b..c03dc938845 100644 --- a/tokens/figma/update-skins.mjs +++ b/tokens/figma/update-skins.mjs @@ -1,7 +1,13 @@ import fetch from "node-fetch"; -import { updateCollections } from "./utils.mjs"; +import { + updateCollections, + COLLECTION_NAMES, + VARIABLE_TYPES, +} from "./utils.mjs"; -const collectionNames = ["Palette"]; +const collectionNames = [ + COLLECTION_NAMES.PALETTE, +]; async function updatePalette( jsonData, @@ -160,8 +166,8 @@ async function updatePalette( const variableGroups = [ { variables: jsonData[brand].palette, - collectionName: "Palette", - resolvedType: "COLOR", + collectionName: COLLECTION_NAMES.PALETTE, + resolvedType: VARIABLE_TYPES.COLOR, variableScopes: ["ALL_SCOPES"], }, ]; From f5c433d681ac3cffa95392eef566f02be0db29bf Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Fri, 13 Sep 2024 16:45:04 +0200 Subject: [PATCH 56/71] simplify logic in updateSkins --- tokens/figma/update-skins.mjs | 190 ++++++++++------------------------ 1 file changed, 52 insertions(+), 138 deletions(-) diff --git a/tokens/figma/update-skins.mjs b/tokens/figma/update-skins.mjs index c03dc938845..90c7dd9568a 100644 --- a/tokens/figma/update-skins.mjs +++ b/tokens/figma/update-skins.mjs @@ -1,8 +1,11 @@ import fetch from "node-fetch"; import { updateCollections, + updateOrCreateVariable, + updateOrCreateVariableModeValues, COLLECTION_NAMES, VARIABLE_TYPES, + MODE_NAMES, } from "./utils.mjs"; const collectionNames = [ @@ -41,158 +44,69 @@ async function updatePalette( variableModeValues: [], }; - function findVariableInCollection( - variableName, - collectionName, - existingVariables, - existingCollections - ) { - return Object.values( - existingVariables - ).find((variable) => { - // Check if the variable name matches - if (variable.name !== variableName) - return false; - - // Find the collection in existingCollections using variableCollectionId - const collection = Object.values( - existingCollections - ).find( - (col) => - col.id === - variable.variableCollectionId - ); - - // Check if the collection exists, its name matches the collectionName, - // and if the variable's id is listed in the collection's variableIds - return ( - collection && - collection.name === collectionName && - collection.variableIds.includes( - variable.id - ) - ); - }); - } - - function generateTempId(name, collection) { - return `tempId_${collection}_${name}`; - } - - const allVariableNamesInCurrentData = - new Set(); - - function updateVariables( - variableName, - variableValue, - collectionName, - existingVariables, - existingCollections, - variableType, - variableScopes, - allVariableNamesInCurrentData - ) { - // Find the existing variable by name - const existingVariable = - findVariableInCollection( - variableName, - collectionName, - existingVariables, - existingCollections - ); - - // Find the default mode for the collection - - const existingMode = Object.values( - existingCollections - ).find( - (collection) => - collection.name === collectionName - ).defaultModeId; - - if (existingVariable) { - // If the variable exists, update it - newData.variables.push({ - action: "UPDATE", - id: existingVariable.id, - name: variableName, - resolvedType: variableType, - variableCollectionId: - existingVariable.variableCollectionId, - scopes: variableScopes, - }); - - newData.variableModeValues.push({ - action: "UPDATE", - variableId: existingVariable.id, - modeId: existingMode, - value: variableValue, - }); - } else { - const tempId = generateTempId( - variableName, - collectionName - ); - - const collectionId = Object.values( - existingCollections - ).find( - (collection) => - collection.name === collectionName - ).id; - - newData.variables.push({ - action: "CREATE", - id: tempId, - name: variableName, - variableCollectionId: collectionId, - resolvedType: variableType, - scopes: variableScopes, - }); - - newData.variableModeValues.push({ - action: "CREATE", - variableId: tempId, - modeId: existingMode, - value: variableValue, - }); - - allVariableNamesInCurrentData.add( - variableName - ); - } - } - const variableGroups = [ { variables: jsonData[brand].palette, collectionName: COLLECTION_NAMES.PALETTE, resolvedType: VARIABLE_TYPES.COLOR, variableScopes: ["ALL_SCOPES"], + hasAlias: false, }, ]; - variableGroups.forEach( - ({ + for (const group of variableGroups) { + const { variables, collectionName, resolvedType, variableScopes, - }) => { - variables.forEach((variable) => { - updateVariables( - variable.name, - variable.value, - collectionName, - existingVariables, - existingCollections, - resolvedType, - variableScopes, - allVariableNamesInCurrentData - ); - }); + hasAlias, + } = group; + + for (const variable of variables) { + // Update or create the variable in the collection + const variableUpdateResult = + await updateOrCreateVariable({ + variable: { + ...variable, + resolvedType: resolvedType, + scopes: variableScopes, + hasAlias: hasAlias, + }, + targetCollectionName: collectionName, + existingVariables: existingVariables, + existingCollections: + existingCollections, + }); + + if (!newData.variables) { + newData.variables = []; + } + newData.variables.push( + variableUpdateResult + ); + + // Find the mode for the current brand and set the mode values correctly + const variableModeValuesUpdatedResult = + await updateOrCreateVariableModeValues({ + variable: { + ...variable, + resolvedType: resolvedType, + scopes: variableScopes, + hasAlias: hasAlias, + }, + targetModeName: MODE_NAMES.DEFAULT, + targetCollectionName: collectionName, + existingCollections: + existingCollections, + existingVariables: existingVariables, + }); + + newData.variableModeValues.push( + variableModeValuesUpdatedResult + ); } - ); + } return newData; } catch (error) { From 083f24617de4fede2162f381adfabb61511d70d7 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Fri, 13 Sep 2024 18:21:50 +0200 Subject: [PATCH 57/71] Reuse updateOrCreateModes function --- tokens/figma/update-middleware.mjs | 183 +++++++++++------------------ tokens/figma/update-skins.mjs | 8 +- tokens/figma/utils.mjs | 100 +++++++++------- 3 files changed, 128 insertions(+), 163 deletions(-) diff --git a/tokens/figma/update-middleware.mjs b/tokens/figma/update-middleware.mjs index d2d11af148a..4f2dc5da7dc 100644 --- a/tokens/figma/update-middleware.mjs +++ b/tokens/figma/update-middleware.mjs @@ -2,8 +2,10 @@ import fetch from "node-fetch"; import { updateCollections, - updateOrCreateVariable, + updateOrCreateModes, + updateOrCreateVariables, updateOrCreateVariableModeValues, + hasDefaultMode, generateTempModeId, VARIABLE_TYPES, COLLECTION_NAMES, @@ -30,7 +32,7 @@ function formatBrandName(brand) { ); // Capitalize the first letter of each word } -export async function updateTheme( +async function updateTheme( jsonData, brand, FILE_KEY, @@ -62,57 +64,39 @@ export async function updateTheme( variableModeValues: [], }; - function findModeByName( - collection, - modeName - ) { - return collection.modes.find( - (mode) => mode.name === modeName - ); - } + const modes = [ + MODE_NAMES.LIGHT, + MODE_NAMES.DARK, + ]; - const updateModes = (collectionName) => { - const collection = Object.values( - existingCollections - ).find( - (col) => col.name === collectionName - ); - if (!collection) return; + // Create or update modes for the collection - const defaultMode = collection.modes.find( - (mode) => - mode.modeId === collection.defaultModeId - ); - if ( - defaultMode && - defaultMode.name !== "True" - ) { - newData.variableModes.push({ - action: "UPDATE", - id: defaultMode.modeId, - variableCollectionId: collection.id, - name: MODE_NAMES.LIGHT, - }); - } + const defaultMode = modes[0]; - const darkMode = findModeByName( - collection, - MODE_NAMES.DARK - ); - if (!darkMode) { - newData.variableModes.push({ - action: "CREATE", - id: generateTempModeId( - MODE_NAMES.DARK, - COLLECTION_NAMES.COLOR_SCHEME - ), - variableCollectionId: collection.id, - name: MODE_NAMES.DARK, + const defaultModeResult = + await updateOrCreateModes({ + mode: { name: defaultMode }, + isDefault: true, + targetCollectionName: + COLLECTION_NAMES.COLOR_SCHEME, + existingCollections: existingCollections, + }); + + newData.variableModes.push(defaultModeResult); + + modes.slice(1).forEach(async (mode) => { + const modeResult = + await updateOrCreateModes({ + mode: { name: mode }, + isDefault: false, + targetCollectionName: + COLLECTION_NAMES.COLOR_SCHEME, + existingCollections: + existingCollections, }); - } - }; - updateModes(COLLECTION_NAMES.COLOR_SCHEME); + newData.variableModes.push(modeResult); + }); async function processVariables( lightVariables, @@ -154,7 +138,7 @@ export async function updateTheme( // Prepare the variable data const variableData = - await updateOrCreateVariable({ + await updateOrCreateVariables({ variable: { name: prefixedName, resolvedType: VARIABLE_TYPES.COLOR, @@ -327,75 +311,34 @@ async function updateSkinColorVariables( // Step 5: Create or update modes based on the brands const firstBrand = brands[0]; - const formattedFirstBrand = - formatBrandName(firstBrand); - - const defaultMode = - brandCollection.modes?.find( - (mode) => - mode.name === MODE_NAMES.DEFAULT || - mode.name === firstBrand || - mode.name === formattedFirstBrand - ); - if (defaultMode) { - if ( - defaultMode.name === firstBrand || - defaultMode.name === MODE_NAMES.DEFAULT - ) { - newData.variableModes.push({ - action: "UPDATE", - id: defaultMode.modeId, - name: formattedFirstBrand, - variableCollectionId: - brandCollection.id, - }); - } - } else { - newData.variableModes.push({ - action: "CREATE", - name: formattedFirstBrand, - id: generateTempModeId( - formattedFirstBrand, - COLLECTION_NAMES.BRAND - ), - variableCollectionId: brandCollection.id, + const firstModeResult = + await updateOrCreateModes({ + mode: { + name: formatBrandName(firstBrand), + }, + isDefault: true, + targetCollectionName: + COLLECTION_NAMES.BRAND, + existingCollections: themeCollections, }); - } - brands.slice(1).forEach((brand) => { + newData.variableModes.push(firstModeResult); + + brands.slice(1).forEach(async (brand) => { const formattedBrand = formatBrandName(brand); - const existingMode = - brandCollection.modes.find( - (mode) => - mode.name === brand || - mode.name === formattedBrand - ); - - if (existingMode) { - if (existingMode.name === brand) { - newData.variableModes.push({ - action: "UPDATE", - id: existingMode.modeId, - name: formattedBrand, - variableCollectionId: - brandCollection.id, - }); - } - } else { - newData.variableModes.push({ - action: "CREATE", - name: formattedBrand, - id: generateTempModeId( - formattedBrand, - COLLECTION_NAMES.BRAND - ), - variableCollectionId: - brandCollection.id, + const modeResult = + await updateOrCreateModes({ + mode: { name: formattedBrand }, + isDefault: false, + targetCollectionName: + COLLECTION_NAMES.BRAND, + existingCollections: themeCollections, }); - } + + newData.variableModes.push(modeResult); }); // Step 6: Create a map for color variables from Theme @@ -437,7 +380,7 @@ async function updateSkinColorVariables( }; const variableData = - await updateOrCreateVariable({ + await updateOrCreateVariables({ variable, targetCollectionName: variable.targetCollectionName, @@ -463,8 +406,11 @@ async function updateSkinColorVariables( }, targetModeName: - brand === brands[0] - ? defaultMode.name + hasDefaultMode( + COLLECTION_NAMES.BRAND, + themeCollections + ) && brand === brands[0] + ? MODE_NAMES.DEFAULT : formattedBrand, targetCollectionName: COLLECTION_NAMES.BRAND, // Assuming the collection name is 'Brand' @@ -635,7 +581,7 @@ async function updateSkinOtherVariables( for (const variable of variables) { // Update or create the variable in the collection const variableUpdateResult = - await updateOrCreateVariable({ + await updateOrCreateVariables({ variable: { ...variable, resolvedType: resolvedType, @@ -665,7 +611,12 @@ async function updateSkinOtherVariables( hasAlias: hasAlias, }, targetModeName: - formatBrandName(brand), + hasDefaultMode( + collectionName, + existingCollections + ) && brand === brands[0] + ? MODE_NAMES.DEFAULT + : formatBrandName(brand), targetCollectionName: collectionName, existingCollections: existingCollections, diff --git a/tokens/figma/update-skins.mjs b/tokens/figma/update-skins.mjs index 90c7dd9568a..0c5c51a33ea 100644 --- a/tokens/figma/update-skins.mjs +++ b/tokens/figma/update-skins.mjs @@ -1,7 +1,7 @@ import fetch from "node-fetch"; import { updateCollections, - updateOrCreateVariable, + updateOrCreateVariables, updateOrCreateVariableModeValues, COLLECTION_NAMES, VARIABLE_TYPES, @@ -65,8 +65,8 @@ async function updatePalette( for (const variable of variables) { // Update or create the variable in the collection - const variableUpdateResult = - await updateOrCreateVariable({ + const variablesUpdateResult = + await updateOrCreateVariables({ variable: { ...variable, resolvedType: resolvedType, @@ -83,7 +83,7 @@ async function updatePalette( newData.variables = []; } newData.variables.push( - variableUpdateResult + variablesUpdateResult ); // Find the mode for the current brand and set the mode values correctly diff --git a/tokens/figma/utils.mjs b/tokens/figma/utils.mjs index 11cbde7d384..ff351b2e134 100644 --- a/tokens/figma/utils.mjs +++ b/tokens/figma/utils.mjs @@ -53,6 +53,32 @@ export function generateTempModeId( return `tempId_${targetCollection}_${targetMode}`; } +export function hasDefaultMode( + targetCollectionName, + existingCollections +) { + const collection = Object.values( + existingCollections + ).find( + (collection) => + collection.name === targetCollectionName + ); + + if (!collection) { + console.warn( + `Collection ${targetCollectionName} not found.` + ); + return false; + } + + const existingModes = collection.modes || []; + + // Return true if a mode named "Default" exists, otherwise false + return existingModes.some( + (m) => m.name === MODE_NAMES.DEFAULT + ); +} + export async function updateCollections( collections, FILE_KEY, @@ -131,9 +157,9 @@ export async function updateCollections( } } -export async function updateOrCreateMode({ +export async function updateOrCreateModes({ mode, - defaultModeName, + isDefault, targetCollectionName, existingCollections, }) { @@ -144,70 +170,58 @@ export async function updateOrCreateMode({ collection.name === targetCollectionName ); + // Handle the case when the collection is not found if (!collection) { console.warn( `Collection ${targetCollectionName} not found.` ); - return; + return null; } const collectionId = collection.id; const existingModes = collection.modes || []; - // Find the default mode (e.g., "Mode 1" or "Default") - const defaultMode = existingModes.find( - (m) => m.name === DMODE_NAMES.DEFAULT // Replace with actual default mode name if different - ); - - // Find the target mode by its name + // Look for the existing mode by name and the default mode const existingMode = existingModes.find( (m) => m.name === mode.name ); + const defaultMode = existingModes.find( + (m) => m.name === MODE_NAMES.DEFAULT + ); - if (mode.name === defaultModeName) { - if (defaultMode) { - // Rename the default mode to the target mode name - return { - action: "UPDATE", - id: defaultMode.modeId, - name: mode.name, // Rename "Default" to the target mode name - variableCollectionId: collectionId, - }; - } else { - // If default mode does not exist, create it - return { - action: "CREATE", - id: generateTempModeId( - mode.name, // Use mode name for temp ID - targetCollectionName - ), - name: mode.name, // Create the mode with the target name - variableCollectionId: collectionId, - }; - } - } else if (!existingMode) { - // If mode doesn't exist, create it + // If it's the default mode, update or rename it + if (isDefault && defaultMode) { + return { + action: "UPDATE", + id: defaultMode.modeId, + name: mode.name, // Rename or update "Default" mode to the target name + variableCollectionId: collectionId, + }; + } + + // If the mode does not exist, create it + if (!existingMode) { return { action: "CREATE", id: generateTempModeId( - mode.name, // Use mode name for temp ID + mode.name, targetCollectionName ), - name: mode.name, // Create the mode with the specified name - variableCollectionId: collectionId, - }; - } else { - // If the mode exists, update it - return { - action: "UPDATE", - id: existingMode.modeId, // Use existing mode ID - name: mode.name, // Update the mode with the correct name + name: mode.name, // Create the mode with the target name variableCollectionId: collectionId, }; } + + // If the mode exists, update it + return { + action: "UPDATE", + id: existingMode.modeId, + name: mode.name, // Update the mode with the correct name + variableCollectionId: collectionId, + }; } -export async function updateOrCreateVariable({ +export async function updateOrCreateVariables({ variable, targetCollectionName, existingVariables, From 9f2b11e0825376c81cfeb78933110d95c31ce523 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Fri, 13 Sep 2024 19:07:15 +0200 Subject: [PATCH 58/71] Improvements in file organization --- tokens/figma/update-middleware.mjs | 142 +++++------------- tokens/figma/update-skins.mjs | 67 +++------ tokens/figma/utils/api-request.mjs | 50 ++++++ tokens/figma/utils/constants.mjs | 20 +++ .../figma/{ => utils}/extract-json-data.mjs | 2 +- .../{utils.mjs => utils/figma-utils.mjs} | 48 +----- tokens/figma/utils/hex-to-rgba.mjs | 28 ++++ 7 files changed, 157 insertions(+), 200 deletions(-) create mode 100644 tokens/figma/utils/api-request.mjs create mode 100644 tokens/figma/utils/constants.mjs rename tokens/figma/{ => utils}/extract-json-data.mjs (99%) rename tokens/figma/{utils.mjs => utils/figma-utils.mjs} (87%) create mode 100644 tokens/figma/utils/hex-to-rgba.mjs diff --git a/tokens/figma/update-middleware.mjs b/tokens/figma/update-middleware.mjs index 4f2dc5da7dc..7e2a57ea3fd 100644 --- a/tokens/figma/update-middleware.mjs +++ b/tokens/figma/update-middleware.mjs @@ -6,11 +6,18 @@ import { updateOrCreateVariables, updateOrCreateVariableModeValues, hasDefaultMode, - generateTempModeId, +} from "./utils/figma-utils.mjs"; + +import { VARIABLE_TYPES, COLLECTION_NAMES, MODE_NAMES, -} from "./utils.mjs"; +} from "./utils/constants.mjs"; + +import { + getFigmaData, + postFigmaVariables, +} from "./utils/api-request.mjs"; function formatBrandName(brand) { // Check if the brand is "tu" and return it in uppercase @@ -39,19 +46,10 @@ async function updateTheme( FIGMA_TOKEN ) { try { - // Fetch existing variables and collections from Figma - const response = await fetch( - `https://api.figma.com/v1/files/${FILE_KEY}/variables/local`, - { - method: "GET", - headers: { - "X-Figma-Token": FIGMA_TOKEN, - "Content-Type": "application/json", - }, - } + const figmaData = await getFigmaData( + FILE_KEY, + FIGMA_TOKEN ); - - const figmaData = await response.json(); const existingVariables = figmaData.meta.variables; const existingCollections = @@ -214,26 +212,12 @@ async function updateTheme( ); // Update the variables and modes in Figma - const updateResponse = await fetch( - `https://api.figma.com/v1/files/${FILE_KEY}/variables`, - { - method: "POST", - headers: { - "X-Figma-Token": FIGMA_TOKEN, - "Content-Type": "application/json", - }, - body: JSON.stringify(newData), - } + await postFigmaVariables( + FILE_KEY, + FIGMA_TOKEN, + newData ); - if (!updateResponse.ok) { - const errorText = - await updateResponse.text(); - throw new Error( - `Error updating variables and modes: ${updateResponse.statusText}. Response: ${errorText}` - ); - } - return newData; } catch (error) { console.error("Error:", error); @@ -247,19 +231,10 @@ async function updateSkinColorVariables( FIGMA_TOKEN ) { try { - // Step 1: Fetch existing data from "Mode" and "Brand" collections - const response = await fetch( - `https://api.figma.com/v1/files/${FILE_KEY}/variables/local`, - { - method: "GET", - headers: { - "X-Figma-Token": FIGMA_TOKEN, - "Content-Type": "application/json", - }, - } + const figmaData = await getFigmaData( + FILE_KEY, + FIGMA_TOKEN ); - - const figmaData = await response.json(); const themeCollections = figmaData.meta.variableCollections; @@ -428,25 +403,12 @@ async function updateSkinColorVariables( } // Step 9: Send the data to update the Brand collection (POST) - const updateResponse = await fetch( - `https://api.figma.com/v1/files/${FILE_KEY}/variables`, - { - method: "POST", - headers: { - "X-Figma-Token": FIGMA_TOKEN, - "Content-Type": "application/json", - }, - body: JSON.stringify(newData), - } - ); - if (!updateResponse.ok) { - const errorText = - await updateResponse.text(); - throw new Error( - `Error updating Brand collection: ${updateResponse.statusText}. Response: ${errorText}` - ); - } + await postFigmaVariables( + FILE_KEY, + FIGMA_TOKEN, + newData + ); return newData; // Returning newData for debugging } catch (error) { @@ -461,18 +423,10 @@ async function updateSkinOtherVariables( FILE_KEY, FIGMA_TOKEN ) { - const response = await fetch( - `https://api.figma.com/v1/files/${FILE_KEY}/variables/local`, - { - method: "GET", - headers: { - "X-Figma-Token": FIGMA_TOKEN, - "Content-Type": "application/json", - }, - } + const figmaData = await getFigmaData( + FILE_KEY, + FIGMA_TOKEN ); - - const figmaData = await response.json(); const existingVariables = figmaData.meta.variables; const existingCollections = @@ -631,24 +585,12 @@ async function updateSkinOtherVariables( } // Make the POST request to update the variables and mode values in the Brand collection - const updateResponse = await fetch( - `https://api.figma.com/v1/files/${FILE_KEY}/variables`, - { - method: "POST", - headers: { - "X-Figma-Token": FIGMA_TOKEN, - "Content-Type": "application/json", - }, - body: JSON.stringify(newData), - } - ); - if (!updateResponse.ok) { - const errorText = await updateResponse.text(); - throw new Error( - `Error updating Brand collection: ${updateResponse.statusText}. Response: ${errorText}` - ); - } + await postFigmaVariables( + FILE_KEY, + FIGMA_TOKEN, + newData + ); return newData; } @@ -670,22 +612,10 @@ async function postCollections( FIGMA_TOKEN ); - const response = await fetch( - `https://api.figma.com/v1/files/${FILE_KEY}/variables/`, - { - method: "POST", - headers: { - "X-Figma-Token": FIGMA_TOKEN, - "Content-Type": "application/json", - }, - body: JSON.stringify(newData), - } - ); - - const data = await response.json(); - console.log( - `Success creating collections for brand ${brand}:`, - data + await postFigmaVariables( + FILE_KEY, + FIGMA_TOKEN, + newData ); } catch (error) { console.error( diff --git a/tokens/figma/update-skins.mjs b/tokens/figma/update-skins.mjs index 0c5c51a33ea..42f3bd4b938 100644 --- a/tokens/figma/update-skins.mjs +++ b/tokens/figma/update-skins.mjs @@ -1,12 +1,19 @@ -import fetch from "node-fetch"; import { updateCollections, updateOrCreateVariables, updateOrCreateVariableModeValues, - COLLECTION_NAMES, +} from "./utils/figma-utils.mjs"; + +import { VARIABLE_TYPES, + COLLECTION_NAMES, MODE_NAMES, -} from "./utils.mjs"; +} from "./utils/constants.mjs"; + +import { + getFigmaData, + postFigmaVariables, +} from "./utils/api-request.mjs"; const collectionNames = [ COLLECTION_NAMES.PALETTE, @@ -19,19 +26,11 @@ async function updatePalette( FIGMA_TOKEN ) { try { - const response = await fetch( - `https://api.figma.com/v1/files/${FILE_KEY}/variables/local`, - { - method: "GET", - headers: { - "X-Figma-Token": FIGMA_TOKEN, - "Content-Type": "application/json", - }, - } + const figmaData = await getFigmaData( + FILE_KEY, + FIGMA_TOKEN ); - const figmaData = await response.json(); - const existingVariables = figmaData.meta.variables; const existingCollections = @@ -127,22 +126,10 @@ async function postCollections( FIGMA_TOKEN ); - const response = await fetch( - `https://api.figma.com/v1/files/${FILE_KEY}/variables/`, - { - method: "POST", - headers: { - "X-Figma-Token": FIGMA_TOKEN, - "Content-Type": "application/json", - }, - body: JSON.stringify(newData), - } - ); - - const data = await response.json(); - console.log( - `Success creating collections for brand ${brand}:`, - data + await postFigmaVariables( + FILE_KEY, + FIGMA_TOKEN, + newData ); } catch (error) { console.error( @@ -166,22 +153,10 @@ async function postPalette( FIGMA_TOKEN ); - const response = await fetch( - `https://api.figma.com/v1/files/${FILE_KEY}/variables/`, - { - method: "POST", - headers: { - "X-Figma-Token": FIGMA_TOKEN, - "Content-Type": "application/json", - }, - body: JSON.stringify(newData), - } - ); - - const data = await response.json(); - console.log( - `Success updating palette for brand ${brand}:`, - data + await postFigmaVariables( + FILE_KEY, + FIGMA_TOKEN, + newData ); } catch (error) { console.error( diff --git a/tokens/figma/utils/api-request.mjs b/tokens/figma/utils/api-request.mjs new file mode 100644 index 00000000000..c7807e39730 --- /dev/null +++ b/tokens/figma/utils/api-request.mjs @@ -0,0 +1,50 @@ +import fetch from "node-fetch"; + +async function getFigmaData( + FILE_KEY, + FIGMA_TOKEN +) { + const response = await fetch( + `https://api.figma.com/v1/files/${FILE_KEY}/variables/local`, + { + method: "GET", + headers: { + "X-Figma-Token": FIGMA_TOKEN, + "Content-Type": "application/json", + }, + } + ); + if (!response.ok) { + throw new Error( + `Error fetching Figma data: ${response.statusText}` + ); + } + return response.json(); +} + +async function postFigmaVariables( + FILE_KEY, + FIGMA_TOKEN, + newData +) { + const response = await fetch( + `https://api.figma.com/v1/files/${FILE_KEY}/variables`, + { + method: "POST", + headers: { + "X-Figma-Token": FIGMA_TOKEN, + "Content-Type": "application/json", + }, + body: JSON.stringify(newData), + } + ); + if (!response.ok) { + const errorText = await response.text(); + throw new Error( + `Error updating variables: ${response.statusText}. Response: ${errorText}` + ); + } + return response.json(); +} + +export { getFigmaData, postFigmaVariables }; diff --git a/tokens/figma/utils/constants.mjs b/tokens/figma/utils/constants.mjs new file mode 100644 index 00000000000..13e30b25994 --- /dev/null +++ b/tokens/figma/utils/constants.mjs @@ -0,0 +1,20 @@ +export const VARIABLE_TYPES = { + COLOR: "COLOR", + FLOAT: "FLOAT", + STRING: "STRING", + FONT_WEIGHT: "FONT_WEIGHT", + FONT_SIZE: "FONT_SIZE", + LINE_HEIGHT: "LINE_HEIGHT", +}; + +export const COLLECTION_NAMES = { + BRAND: "Brand", + COLOR_SCHEME: "Mode", + PALETTE: "Palette", +}; + +export const MODE_NAMES = { + DEFAULT: "Mode 1", + LIGHT: "Light", + DARK: "Dark", +}; diff --git a/tokens/figma/extract-json-data.mjs b/tokens/figma/utils/extract-json-data.mjs similarity index 99% rename from tokens/figma/extract-json-data.mjs rename to tokens/figma/utils/extract-json-data.mjs index 391e81f49aa..2e6cbcc9c62 100644 --- a/tokens/figma/extract-json-data.mjs +++ b/tokens/figma/utils/extract-json-data.mjs @@ -1,6 +1,6 @@ import fs from "fs"; import path from "path"; -import { hexToRgba } from "./utils.mjs"; +import hexToRgba from "./hex-to-rgba.mjs"; export const extractSkinJsonData = ( jsonFiles, diff --git a/tokens/figma/utils.mjs b/tokens/figma/utils/figma-utils.mjs similarity index 87% rename from tokens/figma/utils.mjs rename to tokens/figma/utils/figma-utils.mjs index ff351b2e134..35f8415ecd3 100644 --- a/tokens/figma/utils.mjs +++ b/tokens/figma/utils/figma-utils.mjs @@ -1,50 +1,4 @@ -export const VARIABLE_TYPES = { - COLOR: "COLOR", - FLOAT: "FLOAT", - STRING: "STRING", - FONT_WEIGHT: "FONT_WEIGHT", - FONT_SIZE: "FONT_SIZE", - LINE_HEIGHT: "LINE_HEIGHT", -}; - -export const COLLECTION_NAMES = { - BRAND: "Brand", - COLOR_SCHEME: "Mode", - PALETTE: "Palette", -}; - -export const MODE_NAMES = { - DEFAULT: "Mode 1", - LIGHT: "Light", - DARK: "Dark", -}; - -export function hexToRgba(hex, alpha = 1) { - // Remove the leading # if it's present - hex = hex.replace(/^#/, ""); - - // Expand shorthand form (e.g., "03F") to full form (e.g., "0033FF") - if (hex.length === 3) { - hex = hex - .split("") - .map((char) => char + char) - .join(""); - } - - // Parse the r, g, b values - const bigint = parseInt(hex, 16); - const r = ((bigint >> 16) & 255) / 255; - const g = ((bigint >> 8) & 255) / 255; - const b = (bigint & 255) / 255; - - // Return the RGBA object with normalized values - return { - r, - g, - b, - a: alpha, - }; -} +import { MODE_NAMES } from "./constants.mjs"; export function generateTempModeId( targetMode, diff --git a/tokens/figma/utils/hex-to-rgba.mjs b/tokens/figma/utils/hex-to-rgba.mjs new file mode 100644 index 00000000000..1e9f533d4c8 --- /dev/null +++ b/tokens/figma/utils/hex-to-rgba.mjs @@ -0,0 +1,28 @@ +export function hexToRgba(hex, alpha = 1) { + // Remove the leading # if it's present + hex = hex.replace(/^#/, ""); + + // Expand shorthand form (e.g., "03F") to full form (e.g., "0033FF") + if (hex.length === 3) { + hex = hex + .split("") + .map((char) => char + char) + .join(""); + } + + // Parse the r, g, b values + const bigint = parseInt(hex, 16); + const r = ((bigint >> 16) & 255) / 255; + const g = ((bigint >> 8) & 255) / 255; + const b = (bigint & 255) / 255; + + // Return the RGBA object with normalized values + return { + r, + g, + b, + a: alpha, + }; +} + +export default hexToRgba; From ee3fe13670e57c3e500063e2c15cb02100ea1585 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Fri, 13 Sep 2024 19:12:46 +0200 Subject: [PATCH 59/71] Add link to figma script README in root README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7dc4e6fe06a..9365cb85db6 100644 --- a/README.md +++ b/README.md @@ -34,4 +34,4 @@ ## How to sync design tokens > [!NOTE] -> We're no longer using [Figma Tokens plugin](https://www.figma.com/community/plugin/843461159747178978/Figma-Tokens) to sync tokens with files. We use Figma API to update variables in our libraries. More about this proccess in [Figma tokens script README](). +> We're no longer using [Figma Tokens plugin](https://www.figma.com/community/plugin/843461159747178978/Figma-Tokens) to sync tokens with files. We use Figma API to update variables in our libraries. More about this proccess in [Figma tokens script README](https://github.com/Telefonica/mistica-design/blob/production/tokens/figma/README.md). From 50dab99bda28fbb0974e0a94f445520f5d8b877d Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Fri, 13 Sep 2024 21:29:16 +0200 Subject: [PATCH 60/71] Extract variables and improvements --- tokens/figma/README.md | 2 +- tokens/figma/index.mjs | 18 +- tokens/figma/update-middleware.mjs | 583 +++++++++-------------- tokens/figma/update-skins.mjs | 19 +- tokens/figma/utils/constants.mjs | 21 +- tokens/figma/utils/format-brand-name.mjs | 21 + tokens/figma/variables.mjs | 126 +++++ 7 files changed, 417 insertions(+), 373 deletions(-) create mode 100644 tokens/figma/utils/format-brand-name.mjs create mode 100644 tokens/figma/variables.mjs diff --git a/tokens/figma/README.md b/tokens/figma/README.md index eab7a589470..ba02c8bdcd3 100644 --- a/tokens/figma/README.md +++ b/tokens/figma/README.md @@ -7,7 +7,7 @@ This project is designed to update Figma variables based on a JSON input, primar - **Fetch Existing Figma Data**: Retrieves the existing variables and collections from Figma. - **Process JSON Data**: Extracts theme and token data from provided JSON files for each brand. - **Update or Create Variables**: Adds new variables or updates existing ones based on the brand's light and dark themes. -- **Handle Variable Modes**: Ensures each brand's mode (e.g., "Light", "Dark") is updated or created in the Figma "Skin" collection. +- **Handle Variable Modes**: Ensures each brand's mode (e.g., "Light", "Dark") is updated or created in the Figma "Brand" collection. - **Support for Multiple Brands**: Processes multiple brands, mapping each brand's unique variables into Figma's collections. ## Setup diff --git a/tokens/figma/index.mjs b/tokens/figma/index.mjs index 068db493d49..f446107f393 100644 --- a/tokens/figma/index.mjs +++ b/tokens/figma/index.mjs @@ -5,11 +5,12 @@ import { fileURLToPath } from "url"; import { updateSkinFiles } from "./update-skins.mjs"; import { updateMiddleware } from "./update-middleware.mjs"; +import { BRANDS } from "./utils/constants.mjs"; import { extractSkinJsonData, extractMiddlewareJsonData, -} from "./extract-json-data.mjs"; +} from "./utils/extract-json-data.mjs"; dotenv.config({ path: "../../.env" }); const __filename = fileURLToPath(import.meta.url); @@ -39,12 +40,15 @@ const MIDDLEWARE_TOKEN = const FILE_KEYS = { // Remember to sync these with the workflow file - movistar: process.env.MOVISTAR_FILE_KEY, - "o2-new": process.env.O2_NEW_FILE_KEY, - "vivo-new": process.env.VIVO_NEW_FILE_KEY, - telefonica: process.env.TELEFONICA_FILE_KEY, - blau: process.env.BLAU_FILE_KEY, - tu: process.env.TU_FILE_KEY, + [BRANDS.MOVISTAR]: + process.env.MOVISTAR_FILE_KEY, + [BRANDS.O2_NEW]: process.env.O2_NEW_FILE_KEY, + [BRANDS.VIVO_NEW]: + process.env.VIVO_NEW_FILE_KEY, + [BRANDS.TELEFONICA]: + process.env.TELEFONICA_FILE_KEY, + [BRANDS.BLAU]: process.env.BLAU_FILE_KEY, + [BRANDS.TU]: process.env.TU_FILE_KEY, }; const brands = Object.fromEntries( diff --git a/tokens/figma/update-middleware.mjs b/tokens/figma/update-middleware.mjs index 7e2a57ea3fd..1e3e42349f4 100644 --- a/tokens/figma/update-middleware.mjs +++ b/tokens/figma/update-middleware.mjs @@ -1,5 +1,3 @@ -import fetch from "node-fetch"; - import { updateCollections, updateOrCreateModes, @@ -12,6 +10,7 @@ import { VARIABLE_TYPES, COLLECTION_NAMES, MODE_NAMES, + VARIABLE_SCOPES, } from "./utils/constants.mjs"; import { @@ -19,27 +18,14 @@ import { postFigmaVariables, } from "./utils/api-request.mjs"; -function formatBrandName(brand) { - // Check if the brand is "tu" and return it in uppercase - if (brand === "tu") { - return brand.toUpperCase(); - } +import { + getConstantVariables, + getNonColorVariables, +} from "./variables.mjs"; - // Check if the brand is telefonica and return it as sentence case and with an accent - if (brand === "telefonica") { - return "Telefónica"; - } +import formatBrandName from "./utils/format-brand-name.mjs"; - // For other brands, remove the hyphen and convert to sentence case - return brand - .replace(/-/g, " ") // Remove hyphens and replace with spaces - .toLowerCase() // Convert all to lowercase first - .replace(/\b\w/g, (char) => - char.toUpperCase() - ); // Capitalize the first letter of each word -} - -async function updateTheme( +async function updateModeCollection( jsonData, brand, FILE_KEY, @@ -68,9 +54,7 @@ async function updateTheme( ]; // Create or update modes for the collection - const defaultMode = modes[0]; - const defaultModeResult = await updateOrCreateModes({ mode: { name: defaultMode }, @@ -79,12 +63,11 @@ async function updateTheme( COLLECTION_NAMES.COLOR_SCHEME, existingCollections: existingCollections, }); - newData.variableModes.push(defaultModeResult); - modes.slice(1).forEach(async (mode) => { - const modeResult = - await updateOrCreateModes({ + const modeResults = await Promise.all( + modes.slice(1).map(async (mode) => { + return await updateOrCreateModes({ mode: { name: mode }, isDefault: false, targetCollectionName: @@ -92,125 +75,123 @@ async function updateTheme( existingCollections: existingCollections, }); + }) + ); + newData.variableModes.push(...modeResults); - newData.variableModes.push(modeResult); - }); + // Get color variables using the imported function + const colorVariables = getConstantVariables( + jsonData, + brand + ); - async function processVariables( - lightVariables, - darkVariables, - collectionName, - brand, - existingVariables, - existingCollections, - newData - ) { - for (const lightVariable of lightVariables) { - const prefixedName = `${brand}/${lightVariable.name}`; - const darkVariable = darkVariables.find( - (v) => v.name === lightVariable.name - ); - - // Get the collection ID - const collectionId = Object.values( - existingCollections - ).find( - (collection) => - collection.name === collectionName - )?.id; - - if (!collectionId) { - console.warn( - `Collection ${collectionName} not found.` - ); - continue; - } + const processedVariables = new Map(); - // Get default mode for this collection - const defaultMode = existingCollections[ - collectionId - ]?.modes.find( - (mode) => - mode.name === MODE_NAMES.DEFAULT - ); - - // Prepare the variable data - const variableData = - await updateOrCreateVariables({ - variable: { - name: prefixedName, - resolvedType: VARIABLE_TYPES.COLOR, - scopes: [], - }, - targetCollectionName: collectionName, - existingVariables, - existingCollections, - }); + for (const variableGroup of colorVariables) { + for (const variable of variableGroup.variables) { + const prefixedName = `${brand}/${variable.name}`; - // Prepare the mode values - const modeValueData = - await updateOrCreateVariableModeValues({ - variable: { - name: prefixedName, - value: lightVariable.value, - hasAlias: false, - }, - targetModeName: defaultMode - ? MODE_NAMES.DEFAULT - : MODE_NAMES.LIGHT, - targetCollectionName: collectionName, - existingCollections, - existingVariables, - }); + // Only process if the variable hasn't been created yet + if ( + !processedVariables.has(prefixedName) + ) { + // Create or update the variable + const variableData = + await updateOrCreateVariables({ + variable: { + name: prefixedName, + resolvedType: + VARIABLE_TYPES.COLOR, + scopes: [], + }, + targetCollectionName: + COLLECTION_NAMES.COLOR_SCHEME, + existingVariables: + existingVariables, + existingCollections: + existingCollections, + }); - if (modeValueData) { - newData.variableModeValues.push( - modeValueData + newData.variables.push(variableData); + processedVariables.set( + prefixedName, + variableData ); - } - if (darkVariable) { - const darkModeValueData = - await updateOrCreateVariableModeValues( - { - variable: { - name: prefixedName, - value: darkVariable.value, - hasAlias: false, - }, - targetModeName: MODE_NAMES.DARK, - targetCollectionName: - collectionName, - existingCollections, - existingVariables, - } - ); + // Find values for light and dark modes + const lightValue = ( + jsonData[brand]?.light || [] + ).find( + (v) => v.name === variable.name + )?.value; + const darkValue = ( + jsonData[brand]?.dark || [] + ).find( + (v) => v.name === variable.name + )?.value; + + // Handle light mode value + if (lightValue) { + const lightModeValueData = + await updateOrCreateVariableModeValues( + { + variable: { + name: prefixedName, + value: lightValue, + hasAlias: false, + }, + targetModeName: hasDefaultMode( + COLLECTION_NAMES.COLOR_SCHEME, + existingCollections + ) + ? MODE_NAMES.DEFAULT + : MODE_NAMES.LIGHT, + targetCollectionName: + COLLECTION_NAMES.COLOR_SCHEME, + existingCollections: + existingCollections, + existingVariables: + existingVariables, + } + ); + + if (lightModeValueData) { + newData.variableModeValues.push( + lightModeValueData + ); + } + } - if (darkModeValueData) { - newData.variableModeValues.push( - darkModeValueData - ); + // Handle dark mode value + if (darkValue) { + const darkModeValueData = + await updateOrCreateVariableModeValues( + { + variable: { + name: prefixedName, + value: darkValue, + hasAlias: false, + }, + targetModeName: MODE_NAMES.DARK, + targetCollectionName: + COLLECTION_NAMES.COLOR_SCHEME, + existingCollections: + existingCollections, + existingVariables: + existingVariables, + } + ); + + if (darkModeValueData) { + newData.variableModeValues.push( + darkModeValueData + ); + } } } - - // Update the variable list - newData.variables.push(variableData); } - - return newData; } - // Process variables for light and dark themes - await processVariables( - jsonData[brand]?.light || [], - jsonData[brand]?.dark || [], - COLLECTION_NAMES.COLOR_SCHEME, - brand, - existingVariables, - existingCollections, - newData - ); - // Update the variables and modes in Figma await postFigmaVariables( FILE_KEY, @@ -225,43 +206,46 @@ async function updateTheme( } } -async function updateSkinColorVariables( +async function updateBrandCollection( + jsonData, brands, FILE_KEY, FIGMA_TOKEN ) { try { + // Step 1: Fetch the existing data from Figma + const figmaData = await getFigmaData( FILE_KEY, FIGMA_TOKEN ); - const themeCollections = + const existingCollections = figmaData.meta.variableCollections; - // Step 2: Find the "Mode" and "Brand" collections + const existingVariables = + figmaData.meta.variables || {}; + + // Step 2: Find the Theme and Brand collections + const themeCollection = Object.values( - themeCollections + existingCollections ).find( - (col) => - col.name === COLLECTION_NAMES.COLOR_SCHEME + (collection) => + collection.name === + COLLECTION_NAMES.COLOR_SCHEME ); + const brandCollection = Object.values( - themeCollections + existingCollections ).find( - (col) => col.name === COLLECTION_NAMES.BRAND + (collection) => + collection.name === COLLECTION_NAMES.SKIN ); - if (!themeCollection || !brandCollection) { - throw new Error( - "Mode or Brand collection not found" - ); - } + // Step 3: Filter variables to only include those from the "Mode" collection - // Step 3: Filter theme variables to only include those from the "Mode" collection - const themeVariables = - figmaData.meta.variables || {}; - const existingThemeVariables = Object.values( - themeVariables + const existingModeVariables = Object.values( + existingVariables ).filter( (variable) => variable.variableCollectionId === @@ -269,7 +253,7 @@ async function updateSkinColorVariables( ); const existingBrandVariables = Object.values( - themeVariables + existingVariables ).filter( (variable) => variable.variableCollectionId === @@ -294,8 +278,8 @@ async function updateSkinColorVariables( }, isDefault: true, targetCollectionName: - COLLECTION_NAMES.BRAND, - existingCollections: themeCollections, + COLLECTION_NAMES.SKIN, + existingCollections: existingCollections, }); newData.variableModes.push(firstModeResult); @@ -309,17 +293,19 @@ async function updateSkinColorVariables( mode: { name: formattedBrand }, isDefault: false, targetCollectionName: - COLLECTION_NAMES.BRAND, - existingCollections: themeCollections, + COLLECTION_NAMES.SKIN, + existingCollections: + existingCollections, }); newData.variableModes.push(modeResult); }); - // Step 6: Create a map for color variables from Theme + // Step 6: Create a map for color variables from Mode collection + const variableToBrandMap = new Map(); - existingThemeVariables.forEach((variable) => { + existingModeVariables.forEach((variable) => { if ( variable.resolvedType === VARIABLE_TYPES.COLOR @@ -349,9 +335,9 @@ async function updateSkinColorVariables( const variable = { name: variableName, resolvedType: VARIABLE_TYPES.COLOR, - scopes: ["ALL_SCOPES"], + scopes: [VARIABLE_SCOPES.ALL_SCOPES], targetCollectionName: - COLLECTION_NAMES.BRAND, + COLLECTION_NAMES.SKIN, }; const variableData = @@ -361,7 +347,8 @@ async function updateSkinColorVariables( variable.targetCollectionName, existingVariables: existingBrandVariables, - existingCollections: themeCollections, + existingCollections: + existingCollections, }); newData.variables.push(variableData); @@ -375,23 +362,24 @@ async function updateSkinColorVariables( const variableModeValuesData = await updateOrCreateVariableModeValues({ variable: { - name: variableName, // Assuming variableName is defined earlier + name: variableName, hasAlias: true, value: brandMap[brand], // Alias to the Theme variable ID for the brand }, targetModeName: hasDefaultMode( - COLLECTION_NAMES.BRAND, - themeCollections + COLLECTION_NAMES.SKIN, + existingCollections ) && brand === brands[0] ? MODE_NAMES.DEFAULT : formattedBrand, targetCollectionName: - COLLECTION_NAMES.BRAND, // Assuming the collection name is 'Brand' - existingCollections: themeCollections, // Pass the fetched collections + COLLECTION_NAMES.SKIN, + existingCollections: + existingCollections, existingVariables: - existingBrandVariables, // Pass the existing variables in the Brand collection + existingBrandVariables, }); if (variableModeValuesData) { @@ -402,6 +390,78 @@ async function updateSkinColorVariables( } } + // Loop through each brand to process its specific tokens + for (const brand of brands) { + const nonColorVariables = + getNonColorVariables(jsonData, brand); + + for (const group of nonColorVariables) { + const { + variables, + collectionName, + resolvedType, + variableScopes, + hasAlias, + } = group; + + for (const variable of variables) { + // Update or create the variable in the collection + const variableUpdateResult = + await updateOrCreateVariables({ + variable: { + ...variable, + resolvedType: resolvedType, + scopes: variableScopes, + hasAlias: hasAlias, + }, + targetCollectionName: + collectionName, + existingVariables: + existingVariables, + existingCollections: + existingCollections, + }); + + if (!newData.variables) { + newData.variables = []; + } + newData.variables.push( + variableUpdateResult + ); + + // Find the mode for the current brand and set the mode values correctly + const variableModeValuesUpdatedResult = + await updateOrCreateVariableModeValues( + { + variable: { + ...variable, + resolvedType: resolvedType, + scopes: variableScopes, + hasAlias: hasAlias, + }, + targetModeName: + hasDefaultMode( + collectionName, + existingCollections + ) && brand === brands[0] + ? MODE_NAMES.DEFAULT + : formatBrandName(brand), + targetCollectionName: + collectionName, + existingCollections: + existingCollections, + existingVariables: + existingVariables, + } + ); + + newData.variableModeValues.push( + variableModeValuesUpdatedResult + ); + } + } + } + // Step 9: Send the data to update the Brand collection (POST) await postFigmaVariables( @@ -417,191 +477,13 @@ async function updateSkinColorVariables( } } -async function updateSkinOtherVariables( - jsonData, - brands, - FILE_KEY, - FIGMA_TOKEN -) { - const figmaData = await getFigmaData( - FILE_KEY, - FIGMA_TOKEN - ); - const existingVariables = - figmaData.meta.variables; - const existingCollections = - figmaData.meta.variableCollections; - - const newData = { - variables: [], - variableModeValues: [], - }; - - const fontFamilies = { - movistar: "On Air", - "vivo-new": "Vivo Type", - "o2-new": "On Air", - telefonica: "Telefonica Sans", - blau: "SF Pro Text", - tu: "Telefonica Sans", - }; - - const iconSets = { - movistar: "Default", - "vivo-new": "Vivo", - "o2-new": "O2", - telefonica: "Default", - blau: "Blau", - tu: "Default", - }; - - // Loop through each brand to process its specific tokens - for (const brand of brands) { - const variableGroups = [ - { - variables: jsonData[brand]?.radius || [], - collectionName: COLLECTION_NAMES.BRAND, - resolvedType: VARIABLE_TYPES.FLOAT, - variableScopes: ["CORNER_RADIUS"], - hasAlias: false, - }, - { - variables: - jsonData[brand]?.fontWeight || [], - collectionName: COLLECTION_NAMES.BRAND, - resolvedType: VARIABLE_TYPES.STRING, - variableScopes: ["FONT_WEIGHT"], - hasAlias: false, - }, - { - variables: - jsonData[brand]?.fontSize || [], - collectionName: COLLECTION_NAMES.BRAND, - resolvedType: VARIABLE_TYPES.FLOAT, - variableScopes: ["FONT_SIZE"], - hasAlias: false, - }, - { - variables: - jsonData[brand]?.lineHeight || [], - collectionName: COLLECTION_NAMES.BRAND, - resolvedType: VARIABLE_TYPES.FLOAT, - variableScopes: ["LINE_HEIGHT"], - hasAlias: false, - }, - { - variables: - jsonData[brand]?.themeVariant || [], - collectionName: COLLECTION_NAMES.BRAND, - resolvedType: VARIABLE_TYPES.STRING, - variableScopes: ["ALL_SCOPES"], - hasAlias: false, - }, - { - variables: [ - { - name: "fontFamily/fontFamily", - value: fontFamilies[brand], - }, - ], - collectionName: COLLECTION_NAMES.BRAND, - resolvedType: VARIABLE_TYPES.STRING, - variableScopes: ["FONT_FAMILY"], - hasAlias: false, - }, - { - variables: [ - { - name: "icons/iconSet", - value: iconSets[brand], - }, - ], - collectionName: COLLECTION_NAMES.BRAND, - resolvedType: VARIABLE_TYPES.STRING, - variableScopes: ["ALL_SCOPES"], - hasAlias: false, - }, - ]; - - for (const group of variableGroups) { - const { - variables, - collectionName, - resolvedType, - variableScopes, - hasAlias, - } = group; - - for (const variable of variables) { - // Update or create the variable in the collection - const variableUpdateResult = - await updateOrCreateVariables({ - variable: { - ...variable, - resolvedType: resolvedType, - scopes: variableScopes, - hasAlias: hasAlias, - }, - targetCollectionName: collectionName, - existingVariables: existingVariables, - existingCollections: - existingCollections, - }); - - if (!newData.variables) { - newData.variables = []; - } - newData.variables.push( - variableUpdateResult - ); - - // Find the mode for the current brand and set the mode values correctly - const variableModeValuesUpdatedResult = - await updateOrCreateVariableModeValues({ - variable: { - ...variable, - resolvedType: resolvedType, - scopes: variableScopes, - hasAlias: hasAlias, - }, - targetModeName: - hasDefaultMode( - collectionName, - existingCollections - ) && brand === brands[0] - ? MODE_NAMES.DEFAULT - : formatBrandName(brand), - targetCollectionName: collectionName, - existingCollections: - existingCollections, - existingVariables: existingVariables, - }); - - newData.variableModeValues.push( - variableModeValuesUpdatedResult - ); - } - } - } - - // Make the POST request to update the variables and mode values in the Brand collection - - await postFigmaVariables( - FILE_KEY, - FIGMA_TOKEN, - newData - ); - - return newData; -} - async function postCollections( brand, FILE_KEY, FIGMA_TOKEN ) { const collectionNames = [ - COLLECTION_NAMES.BRAND, + COLLECTION_NAMES.SKIN, COLLECTION_NAMES.COLOR_SCHEME, ]; @@ -636,7 +518,7 @@ async function processBrand( FILE_KEY, FIGMA_TOKEN ); - await updateTheme( + await updateModeCollection( jsonData, brand, FILE_KEY, @@ -672,12 +554,7 @@ export async function updateMiddleware( FILE_KEY, FIGMA_TOKEN ); - await updateSkinColorVariables( - brands, - FILE_KEY, - FIGMA_TOKEN - ); - await updateSkinOtherVariables( + await updateBrandCollection( jsonData, brands, FILE_KEY, diff --git a/tokens/figma/update-skins.mjs b/tokens/figma/update-skins.mjs index 42f3bd4b938..757db437687 100644 --- a/tokens/figma/update-skins.mjs +++ b/tokens/figma/update-skins.mjs @@ -15,6 +15,8 @@ import { postFigmaVariables, } from "./utils/api-request.mjs"; +import { getPaletteVariables } from "./variables.mjs"; + const collectionNames = [ COLLECTION_NAMES.PALETTE, ]; @@ -43,17 +45,12 @@ async function updatePalette( variableModeValues: [], }; - const variableGroups = [ - { - variables: jsonData[brand].palette, - collectionName: COLLECTION_NAMES.PALETTE, - resolvedType: VARIABLE_TYPES.COLOR, - variableScopes: ["ALL_SCOPES"], - hasAlias: false, - }, - ]; - - for (const group of variableGroups) { + const paletteVariables = getPaletteVariables( + jsonData, + brand + ); + + for (const group of paletteVariables) { const { variables, collectionName, diff --git a/tokens/figma/utils/constants.mjs b/tokens/figma/utils/constants.mjs index 13e30b25994..26b9d24739e 100644 --- a/tokens/figma/utils/constants.mjs +++ b/tokens/figma/utils/constants.mjs @@ -5,10 +5,11 @@ export const VARIABLE_TYPES = { FONT_WEIGHT: "FONT_WEIGHT", FONT_SIZE: "FONT_SIZE", LINE_HEIGHT: "LINE_HEIGHT", + FONT_FAMILY: "FONT_FAMILY", }; export const COLLECTION_NAMES = { - BRAND: "Brand", + SKIN: "Brand", COLOR_SCHEME: "Mode", PALETTE: "Palette", }; @@ -18,3 +19,21 @@ export const MODE_NAMES = { LIGHT: "Light", DARK: "Dark", }; + +export const VARIABLE_SCOPES = { + ALL_SCOPES: "ALL_SCOPES", + CORNER_RADIUS: "CORNER_RADIUS", + FONT_WEIGHT: "FONT_WEIGHT", + FONT_SIZE: "FONT_SIZE", + LINE_HEIGHT: "LINE_HEIGHT", + FONT_FAMILY: "FONT_FAMILY", +}; + +export const BRANDS = { + MOVISTAR: "movistar", + VIVO_NEW: "vivo-new", + O2_NEW: "o2-new", + TELEFONICA: "telefonica", + BLAU: "blau", + TU: "tu", +}; diff --git a/tokens/figma/utils/format-brand-name.mjs b/tokens/figma/utils/format-brand-name.mjs new file mode 100644 index 00000000000..a3a2059b4b7 --- /dev/null +++ b/tokens/figma/utils/format-brand-name.mjs @@ -0,0 +1,21 @@ +function formatBrandName(brand) { + // Check if the brand is "tu" and return it in uppercase + if (brand === "tu") { + return brand.toUpperCase(); + } + + // Check if the brand is telefonica and return it as sentence case and with an accent + if (brand === "telefonica") { + return "Telefónica"; + } + + // For other brands, remove the hyphen and convert to sentence case + return brand + .replace(/-/g, " ") // Remove hyphens and replace with spaces + .toLowerCase() // Convert all to lowercase first + .replace(/\b\w/g, (char) => + char.toUpperCase() + ); // Capitalize the first letter of each word + } + + export default formatBrandName; \ No newline at end of file diff --git a/tokens/figma/variables.mjs b/tokens/figma/variables.mjs new file mode 100644 index 00000000000..9a5587367ea --- /dev/null +++ b/tokens/figma/variables.mjs @@ -0,0 +1,126 @@ +import { + BRANDS, + COLLECTION_NAMES, + VARIABLE_TYPES, + VARIABLE_SCOPES, +} from "./utils/constants.mjs"; + +export const FONT_FAMILIES = { + [BRANDS.MOVISTAR]: "On Air", + [BRANDS.VIVO_NEW]: "Vivo Type", + [BRANDS.O2_NEW]: "On Air", + [BRANDS.TELEFONICA]: "Telefonica Sans", + [BRANDS.BLAU]: "SF Pro Text", + [BRANDS.TU]: "Telefonica Sans", +}; + +export const ICON_SETS = { + [BRANDS.MOVISTAR]: "Default", + [BRANDS.VIVO_NEW]: "Vivo", + [BRANDS.O2_NEW]: "O2", + [BRANDS.TELEFONICA]: "Default", + [BRANDS.BLAU]: "Blau", + [BRANDS.TU]: "Default", +}; + +export const getPaletteVariables = ( + jsonData, + brand +) => [ + { + variables: jsonData[brand]?.palette || [], + collectionName: COLLECTION_NAMES.PALETTE, + resolvedType: VARIABLE_TYPES.COLOR, + variableScopes: [VARIABLE_SCOPES.ALL_SCOPES], + hasAlias: false, + }, +]; + + +export const getConstantVariables = ( + jsonData, + brand +) => [ + { + variables: jsonData[brand]?.light || [], + collectionName: COLLECTION_NAMES.COLOR_SCHEME, + resolvedType: VARIABLE_TYPES.COLOR, + variableScopes: [VARIABLE_SCOPES.ALL_SCOPES], + hasAlias: false, + }, + { + variables: jsonData[brand]?.dark || [], + collectionName: COLLECTION_NAMES.COLOR_SCHEME, + resolvedType: VARIABLE_TYPES.COLOR, + variableScopes: [VARIABLE_SCOPES.ALL_SCOPES], + hasAlias: false, + }, +]; + +export const getNonColorVariables = ( + jsonData, + brand +) => [ + { + variables: jsonData[brand]?.radius || [], + collectionName: COLLECTION_NAMES.SKIN, + resolvedType: VARIABLE_TYPES.FLOAT, + variableScopes: [ + VARIABLE_SCOPES.CORNER_RADIUS, + ], + hasAlias: false, + }, + { + variables: jsonData[brand]?.fontWeight || [], + collectionName: COLLECTION_NAMES.SKIN, + resolvedType: VARIABLE_TYPES.STRING, + variableScopes: [VARIABLE_SCOPES.FONT_WEIGHT], + hasAlias: false, + }, + { + variables: jsonData[brand]?.fontSize || [], + collectionName: COLLECTION_NAMES.SKIN, + resolvedType: VARIABLE_TYPES.FLOAT, + variableScopes: [VARIABLE_SCOPES.FONT_SIZE], + hasAlias: false, + }, + { + variables: jsonData[brand]?.lineHeight || [], + collectionName: COLLECTION_NAMES.SKIN, + resolvedType: VARIABLE_TYPES.FLOAT, + variableScopes: [VARIABLE_SCOPES.LINE_HEIGHT], + hasAlias: false, + }, + { + variables: + jsonData[brand]?.themeVariant || [], + collectionName: COLLECTION_NAMES.SKIN, + resolvedType: VARIABLE_TYPES.STRING, + variableScopes: [VARIABLE_SCOPES.ALL_SCOPES], + hasAlias: false, + }, + { + variables: [ + { + name: "fontFamily/fontFamily", + value: FONT_FAMILIES[brand], + }, + ], + collectionName: COLLECTION_NAMES.SKIN, + resolvedType: VARIABLE_TYPES.STRING, + variableScopes: [VARIABLE_SCOPES.FONT_FAMILY], + hasAlias: false, + }, + { + variables: [ + { + name: "icons/iconSet", + value: ICON_SETS[brand], + }, + ], + collectionName: COLLECTION_NAMES.SKIN, + resolvedType: VARIABLE_TYPES.STRING, + variableScopes: [VARIABLE_SCOPES.ALL_SCOPES], + hasAlias: false, + }, +]; From 77caba99f8973c060de4d1945b28d9c8937b5ee7 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Tue, 17 Sep 2024 11:58:58 +0200 Subject: [PATCH 61/71] group iconSet and brandName variables under utils --- tokens/figma/variables.mjs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tokens/figma/variables.mjs b/tokens/figma/variables.mjs index 9a5587367ea..693fbac9cad 100644 --- a/tokens/figma/variables.mjs +++ b/tokens/figma/variables.mjs @@ -5,6 +5,8 @@ import { VARIABLE_SCOPES, } from "./utils/constants.mjs"; +import formatBrandName from "./utils/format-brand-name.mjs"; + export const FONT_FAMILIES = { [BRANDS.MOVISTAR]: "On Air", [BRANDS.VIVO_NEW]: "Vivo Type", @@ -23,6 +25,8 @@ export const ICON_SETS = { [BRANDS.TU]: "Default", }; +export const BRAND_NAMES = Object.values(BRANDS); + export const getPaletteVariables = ( jsonData, brand @@ -36,7 +40,6 @@ export const getPaletteVariables = ( }, ]; - export const getConstantVariables = ( jsonData, brand @@ -114,9 +117,13 @@ export const getNonColorVariables = ( { variables: [ { - name: "icons/iconSet", + name: "utils/iconSet", value: ICON_SETS[brand], }, + { + name: "utils/brandName", + value: formatBrandName(brand), + }, ], collectionName: COLLECTION_NAMES.SKIN, resolvedType: VARIABLE_TYPES.STRING, From 9f08ca31e6f0b89ac45bf52983f1730dbe16b2b9 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Tue, 17 Sep 2024 12:24:11 +0200 Subject: [PATCH 62/71] Handle scopes for gradient variables --- tokens/figma/update-middleware.mjs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tokens/figma/update-middleware.mjs b/tokens/figma/update-middleware.mjs index 1e3e42349f4..01e727540a3 100644 --- a/tokens/figma/update-middleware.mjs +++ b/tokens/figma/update-middleware.mjs @@ -332,10 +332,19 @@ async function updateBrandCollection( variableName, brandMap, ] of variableToBrandMap) { + // Return empty scopes in gradient variables, since they already have a style + let scopes = [VARIABLE_SCOPES.ALL_SCOPES]; + + const stopRegex = /-stop-\d+$/; + + if (stopRegex.test(variableName)) { + scopes = []; + } + const variable = { name: variableName, resolvedType: VARIABLE_TYPES.COLOR, - scopes: [VARIABLE_SCOPES.ALL_SCOPES], + scopes: scopes, targetCollectionName: COLLECTION_NAMES.SKIN, }; From b9048e3417f1e395eeb0edc9146c2427030143f6 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Tue, 17 Sep 2024 14:59:42 +0200 Subject: [PATCH 63/71] Change value of utils/brandName variable --- tokens/figma/variables.mjs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tokens/figma/variables.mjs b/tokens/figma/variables.mjs index 693fbac9cad..e21a81f7108 100644 --- a/tokens/figma/variables.mjs +++ b/tokens/figma/variables.mjs @@ -25,7 +25,14 @@ export const ICON_SETS = { [BRANDS.TU]: "Default", }; -export const BRAND_NAMES = Object.values(BRANDS); +export const BRAND_NAMES = { + [BRANDS.MOVISTAR]: "Movistar", + [BRANDS.VIVO_NEW]: "Vivo", + [BRANDS.O2_NEW]: "O2", + [BRANDS.TELEFONICA]: "Telefónica", + [BRANDS.BLAU]: "Blau", + [BRANDS.TU]: "TU", +}; export const getPaletteVariables = ( jsonData, @@ -122,7 +129,7 @@ export const getNonColorVariables = ( }, { name: "utils/brandName", - value: formatBrandName(brand), + value: BRAND_NAMES[brand], }, ], collectionName: COLLECTION_NAMES.SKIN, From aa13b4186e702d2a8aed59b8447c2812dbe43cf9 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Tue, 24 Sep 2024 15:26:55 +0200 Subject: [PATCH 64/71] Add text-content scope --- tokens/figma/utils/constants.mjs | 1 + tokens/figma/variables.mjs | 21 +++++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/tokens/figma/utils/constants.mjs b/tokens/figma/utils/constants.mjs index 26b9d24739e..ada5bed0b4b 100644 --- a/tokens/figma/utils/constants.mjs +++ b/tokens/figma/utils/constants.mjs @@ -27,6 +27,7 @@ export const VARIABLE_SCOPES = { FONT_SIZE: "FONT_SIZE", LINE_HEIGHT: "LINE_HEIGHT", FONT_FAMILY: "FONT_FAMILY", + TEXT_CONTENT: "TEXT_CONTENT", }; export const BRANDS = { diff --git a/tokens/figma/variables.mjs b/tokens/figma/variables.mjs index e21a81f7108..8fa71939f01 100644 --- a/tokens/figma/variables.mjs +++ b/tokens/figma/variables.mjs @@ -77,6 +77,7 @@ export const getNonColorVariables = ( resolvedType: VARIABLE_TYPES.FLOAT, variableScopes: [ VARIABLE_SCOPES.CORNER_RADIUS, + VARIABLE_SCOPES.TEXT_CONTENT, ], hasAlias: false, }, @@ -84,21 +85,30 @@ export const getNonColorVariables = ( variables: jsonData[brand]?.fontWeight || [], collectionName: COLLECTION_NAMES.SKIN, resolvedType: VARIABLE_TYPES.STRING, - variableScopes: [VARIABLE_SCOPES.FONT_WEIGHT], + variableScopes: [ + VARIABLE_SCOPES.FONT_WEIGHT, + VARIABLE_SCOPES.TEXT_CONTENT, + ], hasAlias: false, }, { variables: jsonData[brand]?.fontSize || [], collectionName: COLLECTION_NAMES.SKIN, resolvedType: VARIABLE_TYPES.FLOAT, - variableScopes: [VARIABLE_SCOPES.FONT_SIZE], + variableScopes: [ + VARIABLE_SCOPES.FONT_SIZE, + VARIABLE_SCOPES.TEXT_CONTENT, + ], hasAlias: false, }, { variables: jsonData[brand]?.lineHeight || [], collectionName: COLLECTION_NAMES.SKIN, resolvedType: VARIABLE_TYPES.FLOAT, - variableScopes: [VARIABLE_SCOPES.LINE_HEIGHT], + variableScopes: [ + VARIABLE_SCOPES.LINE_HEIGHT, + VARIABLE_SCOPES.TEXT_CONTENT, + ], hasAlias: false, }, { @@ -118,7 +128,10 @@ export const getNonColorVariables = ( ], collectionName: COLLECTION_NAMES.SKIN, resolvedType: VARIABLE_TYPES.STRING, - variableScopes: [VARIABLE_SCOPES.FONT_FAMILY], + variableScopes: [ + VARIABLE_SCOPES.FONT_FAMILY, + VARIABLE_SCOPES.TEXT_CONTENT, + ], hasAlias: false, }, { From 64ff354248456d5da8071e636dfa0dedea98a23a Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Wed, 25 Sep 2024 11:30:21 +0200 Subject: [PATCH 65/71] Fixes from CR --- package-lock.json | 108 ----------------------- tokens/figma/README.md | 18 ++-- tokens/figma/index.mjs | 33 ++----- tokens/figma/update-middleware.mjs | 70 ++++----------- tokens/figma/update-skins.mjs | 63 +++---------- tokens/figma/utils/api-request.mjs | 15 ++-- tokens/figma/utils/figma-utils.mjs | 17 +--- tokens/figma/utils/format-brand-name.mjs | 36 ++++---- yarn.lock | 47 ++++++++++ 9 files changed, 122 insertions(+), 285 deletions(-) delete mode 100644 package-lock.json create mode 100644 yarn.lock diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 3eaefeb98b8..00000000000 --- a/package-lock.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "name": "mistica-design", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "dotenv": "^16.4.5", - "node-fetch": "^3.3.2" - } - }, - "node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "engines": { - "node": ">= 12" - } - }, - "node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, - "engines": { - "node": "^12.20 || >= 14.13" - } - }, - "node_modules/formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "dependencies": { - "fetch-blob": "^3.1.2" - }, - "engines": { - "node": ">=12.20.0" - } - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, - "node_modules/web-streams-polyfill": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", - "engines": { - "node": ">= 8" - } - } - } -} diff --git a/tokens/figma/README.md b/tokens/figma/README.md index ba02c8bdcd3..87e29611470 100644 --- a/tokens/figma/README.md +++ b/tokens/figma/README.md @@ -1,18 +1,18 @@ -# Project Overview +# Project overview This project is designed to update Figma variables based on a JSON input, primarily focused on managing brand themes, colors, and other design tokens. The project retrieves existing variables from Figma, processes the provided JSON data, and updates or creates new variables in collections "Mode" and "Brand". ## Features -- **Fetch Existing Figma Data**: Retrieves the existing variables and collections from Figma. -- **Process JSON Data**: Extracts theme and token data from provided JSON files for each brand. -- **Update or Create Variables**: Adds new variables or updates existing ones based on the brand's light and dark themes. -- **Handle Variable Modes**: Ensures each brand's mode (e.g., "Light", "Dark") is updated or created in the Figma "Brand" collection. -- **Support for Multiple Brands**: Processes multiple brands, mapping each brand's unique variables into Figma's collections. +- **Fetch existing Figma data**: Retrieves the existing variables and collections from Figma. +- **Process JSON data**: Extracts theme and token data from provided JSON files for each brand. +- **Update or dreate variables**: Adds new variables or updates existing ones based on the brand's light and dark themes. +- **Handle variable modes**: Ensures each brand's mode (e.g., "Light", "Dark") is updated or created in the Figma "Brand" collection. +- **Support for multiple brands**: Processes multiple brands, mapping each brand's unique variables into Figma's collections. ## Setup -### Environment Variables: +### Environment variables: - `FIGMA_TOKEN`: The API token to authenticate with Figma. - `MIDDLEWARE_KEY`: Token for file where the variables need to be created / updated @@ -21,14 +21,14 @@ This project is designed to update Figma variables based on a JSON input, primar - Node.js and packages such as `node-fetch`, `dotenv`, and `fs` are used to manage API requests, read local files, and load environment variables. -## Key Functions +## Key functions ### `updateTheme(jsonData, brand, FILE_KEY)` This function updates the theme variables in Figma for a specific brand. It: - Fetches the current variables from Figma. -- Updates modes and variables for "Light" and "Dark" themes. +- Updates modes and variables for `"Light"` and `"Dark"` themes. - Sends a POST request to update Figma with the new data. ### `updateSkinColorVariables(brands, FILE_KEY)` diff --git a/tokens/figma/index.mjs b/tokens/figma/index.mjs index f446107f393..b68364726ae 100644 --- a/tokens/figma/index.mjs +++ b/tokens/figma/index.mjs @@ -1,4 +1,3 @@ -import dotenv from "dotenv"; import fs from "fs"; import path from "path"; import { fileURLToPath } from "url"; @@ -12,7 +11,6 @@ import { extractMiddlewareJsonData, } from "./utils/extract-json-data.mjs"; -dotenv.config({ path: "../../.env" }); const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -34,42 +32,23 @@ const jsonDataForMiddleware = tokensPath ); -const FIGMA_TOKEN = process.env.FIGMA_TOKEN; const MIDDLEWARE_TOKEN = - process.env.MIDDLEWARE_KEY; + process.env.MIDDLEWARE_TEST; -const FILE_KEYS = { +const brands = { // Remember to sync these with the workflow file - [BRANDS.MOVISTAR]: - process.env.MOVISTAR_FILE_KEY, - [BRANDS.O2_NEW]: process.env.O2_NEW_FILE_KEY, - [BRANDS.VIVO_NEW]: - process.env.VIVO_NEW_FILE_KEY, - [BRANDS.TELEFONICA]: - process.env.TELEFONICA_FILE_KEY, - [BRANDS.BLAU]: process.env.BLAU_FILE_KEY, - [BRANDS.TU]: process.env.TU_FILE_KEY, + [BRANDS.MOVISTAR]: process.env.FILE_KEY_1, + [BRANDS.O2_NEW]: process.env.FILE_KEY_2, }; -const brands = Object.fromEntries( - Object.entries(FILE_KEYS).map( - ([brand, FILE_KEY]) => [brand, FILE_KEY] - ) -); - const brandNames = Object.keys(brands); async function processAll() { - await updateSkinFiles( - jsonDataForSkin, - brands, - FIGMA_TOKEN - ); + await updateSkinFiles(jsonDataForSkin, brands); await updateMiddleware( jsonDataForMiddleware, brandNames, - MIDDLEWARE_TOKEN, - FIGMA_TOKEN + MIDDLEWARE_TOKEN ); } diff --git a/tokens/figma/update-middleware.mjs b/tokens/figma/update-middleware.mjs index 01e727540a3..fbd4bc1997f 100644 --- a/tokens/figma/update-middleware.mjs +++ b/tokens/figma/update-middleware.mjs @@ -28,13 +28,11 @@ import formatBrandName from "./utils/format-brand-name.mjs"; async function updateModeCollection( jsonData, brand, - FILE_KEY, - FIGMA_TOKEN + FILE_KEY ) { try { const figmaData = await getFigmaData( - FILE_KEY, - FIGMA_TOKEN + FILE_KEY ); const existingVariables = figmaData.meta.variables; @@ -193,11 +191,7 @@ async function updateModeCollection( } // Update the variables and modes in Figma - await postFigmaVariables( - FILE_KEY, - FIGMA_TOKEN, - newData - ); + await postFigmaVariables(FILE_KEY, newData); return newData; } catch (error) { @@ -209,15 +203,13 @@ async function updateModeCollection( async function updateBrandCollection( jsonData, brands, - FILE_KEY, - FIGMA_TOKEN + FILE_KEY ) { try { // Step 1: Fetch the existing data from Figma const figmaData = await getFigmaData( - FILE_KEY, - FIGMA_TOKEN + FILE_KEY ); const existingCollections = figmaData.meta.variableCollections; @@ -473,11 +465,7 @@ async function updateBrandCollection( // Step 9: Send the data to update the Brand collection (POST) - await postFigmaVariables( - FILE_KEY, - FIGMA_TOKEN, - newData - ); + await postFigmaVariables(FILE_KEY, newData); return newData; // Returning newData for debugging } catch (error) { @@ -486,11 +474,7 @@ async function updateBrandCollection( } } -async function postCollections( - brand, - FILE_KEY, - FIGMA_TOKEN -) { +async function postCollections(brand, FILE_KEY) { const collectionNames = [ COLLECTION_NAMES.SKIN, COLLECTION_NAMES.COLOR_SCHEME, @@ -499,15 +483,10 @@ async function postCollections( try { const newData = await updateCollections( collectionNames, - FILE_KEY, - FIGMA_TOKEN + FILE_KEY ); - await postFigmaVariables( - FILE_KEY, - FIGMA_TOKEN, - newData - ); + await postFigmaVariables(FILE_KEY, newData); } catch (error) { console.error( `Error creating collections for brand ${brand}:`, @@ -519,54 +498,39 @@ async function postCollections( async function processBrand( jsonData, brand, - FILE_KEY, - FIGMA_TOKEN + FILE_KEY ) { - await postCollections( - brand, - FILE_KEY, - FIGMA_TOKEN - ); + await postCollections(brand, FILE_KEY); await updateModeCollection( jsonData, brand, - FILE_KEY, - FIGMA_TOKEN + FILE_KEY ); } async function processAllBrands( jsonData, brands, - FILE_KEY, - FIGMA_TOKEN + FILE_KEY ) { for (const brand of brands) { - await processBrand( - jsonData, - brand, - FILE_KEY, - FIGMA_TOKEN - ); + await processBrand(jsonData, brand, FILE_KEY); } } export async function updateMiddleware( jsonData, brands, - FILE_KEY, - FIGMA_TOKEN + FILE_KEY ) { await processAllBrands( jsonData, brands, - FILE_KEY, - FIGMA_TOKEN + FILE_KEY ); await updateBrandCollection( jsonData, brands, - FILE_KEY, - FIGMA_TOKEN + FILE_KEY ); } diff --git a/tokens/figma/update-skins.mjs b/tokens/figma/update-skins.mjs index 757db437687..4a5686edcab 100644 --- a/tokens/figma/update-skins.mjs +++ b/tokens/figma/update-skins.mjs @@ -5,7 +5,6 @@ import { } from "./utils/figma-utils.mjs"; import { - VARIABLE_TYPES, COLLECTION_NAMES, MODE_NAMES, } from "./utils/constants.mjs"; @@ -24,13 +23,11 @@ const collectionNames = [ async function updatePalette( jsonData, brand, - FILE_KEY, - FIGMA_TOKEN + FILE_KEY ) { try { const figmaData = await getFigmaData( - FILE_KEY, - FIGMA_TOKEN + FILE_KEY ); const existingVariables = @@ -75,9 +72,6 @@ async function updatePalette( existingCollections, }); - if (!newData.variables) { - newData.variables = []; - } newData.variables.push( variablesUpdateResult ); @@ -111,23 +105,14 @@ async function updatePalette( } } -async function postCollections( - brand, - FILE_KEY, - FIGMA_TOKEN -) { +async function postCollections(brand, FILE_KEY) { try { const newData = await updateCollections( collectionNames, - FILE_KEY, - FIGMA_TOKEN + FILE_KEY ); - await postFigmaVariables( - FILE_KEY, - FIGMA_TOKEN, - newData - ); + await postFigmaVariables(FILE_KEY, newData); } catch (error) { console.error( `Error creating collections for for brand ${brand}:`, @@ -139,22 +124,16 @@ async function postCollections( async function postPalette( jsonData, brand, - FILE_KEY, - FIGMA_TOKEN + FILE_KEY ) { try { const newData = await updatePalette( jsonData, brand, - FILE_KEY, - FIGMA_TOKEN + FILE_KEY ); - await postFigmaVariables( - FILE_KEY, - FIGMA_TOKEN, - newData - ); + await postFigmaVariables(FILE_KEY, newData); } catch (error) { console.error( `Error updating palette for brand ${brand}:`, @@ -168,35 +147,19 @@ async function postPalette( async function processBrand( jsonData, brand, - FILE_KEY, - FIGMA_TOKEN + FILE_KEY ) { - await postCollections( - brand, - FILE_KEY, - FIGMA_TOKEN - ); - await postPalette( - jsonData, - brand, - FILE_KEY, - FIGMA_TOKEN - ); + await postCollections(brand, FILE_KEY); + await postPalette(jsonData, brand, FILE_KEY); } export async function updateSkinFiles( jsonData, - brands, - FIGMA_TOKEN + brands ) { for (const [brand, FILE_KEY] of Object.entries( brands )) { - await processBrand( - jsonData, - brand, - FILE_KEY, - FIGMA_TOKEN - ); + await processBrand(jsonData, brand, FILE_KEY); } } diff --git a/tokens/figma/utils/api-request.mjs b/tokens/figma/utils/api-request.mjs index c7807e39730..8be702e1f0d 100644 --- a/tokens/figma/utils/api-request.mjs +++ b/tokens/figma/utils/api-request.mjs @@ -1,9 +1,11 @@ import fetch from "node-fetch"; +import dotenv from "dotenv"; -async function getFigmaData( - FILE_KEY, - FIGMA_TOKEN -) { +dotenv.config({ path: "../../.env" }); + +const FIGMA_TOKEN = process.env.FIGMA_TOKEN; + +async function getFigmaData(FILE_KEY) { const response = await fetch( `https://api.figma.com/v1/files/${FILE_KEY}/variables/local`, { @@ -19,12 +21,11 @@ async function getFigmaData( `Error fetching Figma data: ${response.statusText}` ); } - return response.json(); + return await response.json(); } async function postFigmaVariables( FILE_KEY, - FIGMA_TOKEN, newData ) { const response = await fetch( @@ -44,7 +45,7 @@ async function postFigmaVariables( `Error updating variables: ${response.statusText}. Response: ${errorText}` ); } - return response.json(); + return await response.json(); } export { getFigmaData, postFigmaVariables }; diff --git a/tokens/figma/utils/figma-utils.mjs b/tokens/figma/utils/figma-utils.mjs index 35f8415ecd3..cf3040d5e68 100644 --- a/tokens/figma/utils/figma-utils.mjs +++ b/tokens/figma/utils/figma-utils.mjs @@ -1,4 +1,5 @@ import { MODE_NAMES } from "./constants.mjs"; +import { getFigmaData } from "./api-request.mjs"; export function generateTempModeId( targetMode, @@ -35,23 +36,13 @@ export function hasDefaultMode( export async function updateCollections( collections, - FILE_KEY, - FIGMA_TOKEN + FILE_KEY ) { try { - const response = await fetch( - `https://api.figma.com/v1/files/${FILE_KEY}/variables/local`, - { - method: "GET", - headers: { - "X-Figma-Token": FIGMA_TOKEN, - "Content-Type": "application/json", - }, - } + const figmaData = await getFigmaData( + FILE_KEY ); - const figmaData = await response.json(); - const newData = { variableCollections: [], }; diff --git a/tokens/figma/utils/format-brand-name.mjs b/tokens/figma/utils/format-brand-name.mjs index a3a2059b4b7..f9fae2d892d 100644 --- a/tokens/figma/utils/format-brand-name.mjs +++ b/tokens/figma/utils/format-brand-name.mjs @@ -1,21 +1,21 @@ function formatBrandName(brand) { - // Check if the brand is "tu" and return it in uppercase - if (brand === "tu") { - return brand.toUpperCase(); - } - - // Check if the brand is telefonica and return it as sentence case and with an accent - if (brand === "telefonica") { - return "Telefónica"; - } - - // For other brands, remove the hyphen and convert to sentence case - return brand - .replace(/-/g, " ") // Remove hyphens and replace with spaces - .toLowerCase() // Convert all to lowercase first - .replace(/\b\w/g, (char) => - char.toUpperCase() - ); // Capitalize the first letter of each word + // Check if the brand is "tu" and return it in uppercase + if (brand === "tu") { + return brand.toUpperCase(); } - export default formatBrandName; \ No newline at end of file + // Check if the brand is telefonica and return it as sentence case and with an accent + if (brand === "telefonica") { + return "Telefónica"; + } + + // For other brands, remove the hyphen and convert to sentence case + return brand + .replace(/-/g, " ") // Remove hyphens and replace with spaces + .toLowerCase() // Convert all to lowercase first + .replace(/\b\w/g, (char) => + char.toUpperCase() + ); // Capitalize the first letter of each word +} + +export default formatBrandName; diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000000..d850429f57a --- /dev/null +++ b/yarn.lock @@ -0,0 +1,47 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +data-uri-to-buffer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" + integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== + +dotenv@^16.4.5: + version "16.4.5" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" + integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== + +fetch-blob@^3.1.2, fetch-blob@^3.1.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" + integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== + dependencies: + node-domexception "^1.0.0" + web-streams-polyfill "^3.0.3" + +formdata-polyfill@^4.0.10: + version "4.0.10" + resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" + integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== + dependencies: + fetch-blob "^3.1.2" + +node-domexception@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== + +node-fetch@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b" + integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA== + dependencies: + data-uri-to-buffer "^4.0.0" + fetch-blob "^3.1.4" + formdata-polyfill "^4.0.10" + +web-streams-polyfill@^3.0.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b" + integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== From f62778593dab00212c0d7babe3786756a7585286 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Wed, 25 Sep 2024 11:34:42 +0200 Subject: [PATCH 66/71] Fix index with correct filekeys --- tokens/figma/README.md | 1 + tokens/figma/index.mjs | 13 ++++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/tokens/figma/README.md b/tokens/figma/README.md index 87e29611470..b08c0bef024 100644 --- a/tokens/figma/README.md +++ b/tokens/figma/README.md @@ -16,6 +16,7 @@ This project is designed to update Figma variables based on a JSON input, primar - `FIGMA_TOKEN`: The API token to authenticate with Figma. - `MIDDLEWARE_KEY`: Token for file where the variables need to be created / updated +- `{BRAND}_FILE_KEY`: File key of each of the brand library files ### Dependencies: diff --git a/tokens/figma/index.mjs b/tokens/figma/index.mjs index b68364726ae..d3d29ae4f2d 100644 --- a/tokens/figma/index.mjs +++ b/tokens/figma/index.mjs @@ -33,12 +33,19 @@ const jsonDataForMiddleware = ); const MIDDLEWARE_TOKEN = - process.env.MIDDLEWARE_TEST; + process.env.MIDDLEWARE_KEY; const brands = { // Remember to sync these with the workflow file - [BRANDS.MOVISTAR]: process.env.FILE_KEY_1, - [BRANDS.O2_NEW]: process.env.FILE_KEY_2, + [BRANDS.MOVISTAR]: + process.env.MOVISTAR_FILE_KEY, + [BRANDS.O2_NEW]: process.env.O2_NEW_FILE_KEY, + [BRANDS.VIVO_NEW]: + process.env.VIVO_NEW_FILE_KEY, + [BRANDS.TELEFONICA]: + process.env.TELEFONICA_FILE_KEY, + [BRANDS.BLAU]: process.env.BLAU_FILE_KEY, + [BRANDS.TU]: process.env.TU_FILE_KEY, }; const brandNames = Object.keys(brands); From 85f48f051c7a047ba5f8db3d03ba0cc34bb41e7a Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Wed, 2 Oct 2024 12:14:09 +0200 Subject: [PATCH 67/71] Add workflow dispatch to sync figma tokens --- .github/workflows/sync-figma-tokens.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/sync-figma-tokens.yml b/.github/workflows/sync-figma-tokens.yml index 545fd33ee36..dd73bb45751 100644 --- a/.github/workflows/sync-figma-tokens.yml +++ b/.github/workflows/sync-figma-tokens.yml @@ -1,6 +1,7 @@ name: Sync tokens to Figma on: + workflow_dispatch: push: branches: - production From 3b7e72a4ff4eb0a678e531d09c92d4ba66ce15a1 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Fri, 4 Oct 2024 16:50:28 +0200 Subject: [PATCH 68/71] Resolve comments and add config file --- tokens/figma/config.mjs | 20 +++++++ tokens/figma/index.mjs | 28 +-------- tokens/figma/update-middleware.mjs | 91 +++++++++++++----------------- tokens/figma/update-skins.mjs | 20 +++---- tokens/figma/utils/api-request.mjs | 5 +- 5 files changed, 69 insertions(+), 95 deletions(-) create mode 100644 tokens/figma/config.mjs diff --git a/tokens/figma/config.mjs b/tokens/figma/config.mjs new file mode 100644 index 00000000000..da646708cef --- /dev/null +++ b/tokens/figma/config.mjs @@ -0,0 +1,20 @@ +import dotenv from "dotenv"; + +dotenv.config({ path: "../../.env" }); + +import { BRANDS } from "./utils/constants.mjs"; + +export const brands = { + [BRANDS.MOVISTAR]: "ObNHOLPtrIytjy9BH7M9jW", + [BRANDS.O2_NEW]: "CjvgrHEIycSQ6exznxnFXT", + [BRANDS.VIVO_NEW]: "EApRpjaTyUOwW5VQU2ZqgP", + [BRANDS.TELEFONICA]: "m8srmP3eedfvDaqYnbM6PI", + [BRANDS.BLAU]: "czemeClWRGBI8oF7caNa5m", + [BRANDS.TU]: "19IXMaFqdYeC1IIdTwXBgY", +}; + +export const MIDDLEWARE_TOKEN = + "w7fBxCsEb8WrMVVuxDnCQd"; + +export const FIGMA_TOKEN = + process.env.FIGMA_TOKEN; diff --git a/tokens/figma/index.mjs b/tokens/figma/index.mjs index d3d29ae4f2d..7bcfe91623d 100644 --- a/tokens/figma/index.mjs +++ b/tokens/figma/index.mjs @@ -4,7 +4,6 @@ import { fileURLToPath } from "url"; import { updateSkinFiles } from "./update-skins.mjs"; import { updateMiddleware } from "./update-middleware.mjs"; -import { BRANDS } from "./utils/constants.mjs"; import { extractSkinJsonData, @@ -21,6 +20,7 @@ const files = fs.readdirSync(tokensPath); const jsonFiles = files.filter((file) => file.endsWith(".json") ); + const jsonDataForSkin = extractSkinJsonData( jsonFiles, tokensPath @@ -32,31 +32,9 @@ const jsonDataForMiddleware = tokensPath ); -const MIDDLEWARE_TOKEN = - process.env.MIDDLEWARE_KEY; - -const brands = { - // Remember to sync these with the workflow file - [BRANDS.MOVISTAR]: - process.env.MOVISTAR_FILE_KEY, - [BRANDS.O2_NEW]: process.env.O2_NEW_FILE_KEY, - [BRANDS.VIVO_NEW]: - process.env.VIVO_NEW_FILE_KEY, - [BRANDS.TELEFONICA]: - process.env.TELEFONICA_FILE_KEY, - [BRANDS.BLAU]: process.env.BLAU_FILE_KEY, - [BRANDS.TU]: process.env.TU_FILE_KEY, -}; - -const brandNames = Object.keys(brands); - async function processAll() { - await updateSkinFiles(jsonDataForSkin, brands); - await updateMiddleware( - jsonDataForMiddleware, - brandNames, - MIDDLEWARE_TOKEN - ); + await updateSkinFiles(jsonDataForSkin); + await updateMiddleware(jsonDataForMiddleware); } processAll(); diff --git a/tokens/figma/update-middleware.mjs b/tokens/figma/update-middleware.mjs index fbd4bc1997f..8ac16bbcb86 100644 --- a/tokens/figma/update-middleware.mjs +++ b/tokens/figma/update-middleware.mjs @@ -25,14 +25,18 @@ import { import formatBrandName from "./utils/format-brand-name.mjs"; +import { brands } from "./config.mjs"; +import { MIDDLEWARE_TOKEN } from "./config.mjs"; + +const brandNames = Object.keys(brands); + async function updateModeCollection( jsonData, - brand, - FILE_KEY + brand ) { try { const figmaData = await getFigmaData( - FILE_KEY + MIDDLEWARE_TOKEN ); const existingVariables = figmaData.meta.variables; @@ -191,7 +195,10 @@ async function updateModeCollection( } // Update the variables and modes in Figma - await postFigmaVariables(FILE_KEY, newData); + await postFigmaVariables( + MIDDLEWARE_TOKEN, + newData + ); return newData; } catch (error) { @@ -200,16 +207,12 @@ async function updateModeCollection( } } -async function updateBrandCollection( - jsonData, - brands, - FILE_KEY -) { +async function updateBrandCollection(jsonData) { try { // Step 1: Fetch the existing data from Figma const figmaData = await getFigmaData( - FILE_KEY + MIDDLEWARE_TOKEN ); const existingCollections = figmaData.meta.variableCollections; @@ -261,7 +264,7 @@ async function updateBrandCollection( // Step 5: Create or update modes based on the brands - const firstBrand = brands[0]; + const firstBrand = brandNames[0]; const firstModeResult = await updateOrCreateModes({ @@ -276,7 +279,7 @@ async function updateBrandCollection( newData.variableModes.push(firstModeResult); - brands.slice(1).forEach(async (brand) => { + brandNames.slice(1).forEach(async (brand) => { const formattedBrand = formatBrandName(brand); @@ -355,7 +358,7 @@ async function updateBrandCollection( newData.variables.push(variableData); // Step 8: Update mode values with the correct aliases for each brand - for (const brand of brands) { + for (const brand of brandNames) { const formattedBrand = formatBrandName(brand); @@ -372,7 +375,7 @@ async function updateBrandCollection( hasDefaultMode( COLLECTION_NAMES.SKIN, existingCollections - ) && brand === brands[0] + ) && brand === brandNames[0] ? MODE_NAMES.DEFAULT : formattedBrand, targetCollectionName: @@ -392,7 +395,7 @@ async function updateBrandCollection( } // Loop through each brand to process its specific tokens - for (const brand of brands) { + for (const brand of brandNames) { const nonColorVariables = getNonColorVariables(jsonData, brand); @@ -444,7 +447,7 @@ async function updateBrandCollection( hasDefaultMode( collectionName, existingCollections - ) && brand === brands[0] + ) && brand === brandNames[0] ? MODE_NAMES.DEFAULT : formatBrandName(brand), targetCollectionName: @@ -465,7 +468,10 @@ async function updateBrandCollection( // Step 9: Send the data to update the Brand collection (POST) - await postFigmaVariables(FILE_KEY, newData); + await postFigmaVariables( + MIDDLEWARE_TOKEN, + newData + ); return newData; // Returning newData for debugging } catch (error) { @@ -474,7 +480,7 @@ async function updateBrandCollection( } } -async function postCollections(brand, FILE_KEY) { +async function postCollections(brand) { const collectionNames = [ COLLECTION_NAMES.SKIN, COLLECTION_NAMES.COLOR_SCHEME, @@ -483,10 +489,13 @@ async function postCollections(brand, FILE_KEY) { try { const newData = await updateCollections( collectionNames, - FILE_KEY + MIDDLEWARE_TOKEN ); - await postFigmaVariables(FILE_KEY, newData); + await postFigmaVariables( + MIDDLEWARE_TOKEN, + newData + ); } catch (error) { console.error( `Error creating collections for brand ${brand}:`, @@ -495,42 +504,18 @@ async function postCollections(brand, FILE_KEY) { } } -async function processBrand( - jsonData, - brand, - FILE_KEY -) { - await postCollections(brand, FILE_KEY); - await updateModeCollection( - jsonData, - brand, - FILE_KEY - ); +async function processBrand(jsonData, brand) { + await postCollections(brand); + await updateModeCollection(jsonData, brand); } -async function processAllBrands( - jsonData, - brands, - FILE_KEY -) { - for (const brand of brands) { - await processBrand(jsonData, brand, FILE_KEY); +async function processAllBrands(jsonData) { + for (const brand of brandNames) { + await processBrand(jsonData, brand); } } -export async function updateMiddleware( - jsonData, - brands, - FILE_KEY -) { - await processAllBrands( - jsonData, - brands, - FILE_KEY - ); - await updateBrandCollection( - jsonData, - brands, - FILE_KEY - ); +export async function updateMiddleware(jsonData) { + await processAllBrands(jsonData); + await updateBrandCollection(jsonData); } diff --git a/tokens/figma/update-skins.mjs b/tokens/figma/update-skins.mjs index 4a5686edcab..a2a3c1af1d0 100644 --- a/tokens/figma/update-skins.mjs +++ b/tokens/figma/update-skins.mjs @@ -16,6 +16,8 @@ import { import { getPaletteVariables } from "./variables.mjs"; +import { brands } from "./config.mjs"; + const collectionNames = [ COLLECTION_NAMES.PALETTE, ]; @@ -144,22 +146,14 @@ async function postPalette( // Process data for a specific brand -async function processBrand( - jsonData, - brand, - FILE_KEY -) { +async function processBrand(jsonData, brand) { + const FILE_KEY = brands[brand]; await postCollections(brand, FILE_KEY); await postPalette(jsonData, brand, FILE_KEY); } -export async function updateSkinFiles( - jsonData, - brands -) { - for (const [brand, FILE_KEY] of Object.entries( - brands - )) { - await processBrand(jsonData, brand, FILE_KEY); +export async function updateSkinFiles(jsonData) { + for (const brand of Object.keys(brands)) { + await processBrand(jsonData, brand); } } diff --git a/tokens/figma/utils/api-request.mjs b/tokens/figma/utils/api-request.mjs index 8be702e1f0d..e333cc9696e 100644 --- a/tokens/figma/utils/api-request.mjs +++ b/tokens/figma/utils/api-request.mjs @@ -1,9 +1,6 @@ import fetch from "node-fetch"; -import dotenv from "dotenv"; -dotenv.config({ path: "../../.env" }); - -const FIGMA_TOKEN = process.env.FIGMA_TOKEN; +import { FIGMA_TOKEN } from "../config.mjs"; async function getFigmaData(FILE_KEY) { const response = await fetch( From 373c9dc3c28c20bb4b5a673bf178b4698cf6e775 Mon Sep 17 00:00:00 2001 From: Alex Bueno Date: Fri, 4 Oct 2024 16:54:37 +0200 Subject: [PATCH 69/71] Update tokens/figma/README.md Co-authored-by: Marcos Kolodny --- tokens/figma/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tokens/figma/README.md b/tokens/figma/README.md index b08c0bef024..a7ab7f876d1 100644 --- a/tokens/figma/README.md +++ b/tokens/figma/README.md @@ -6,7 +6,7 @@ This project is designed to update Figma variables based on a JSON input, primar - **Fetch existing Figma data**: Retrieves the existing variables and collections from Figma. - **Process JSON data**: Extracts theme and token data from provided JSON files for each brand. -- **Update or dreate variables**: Adds new variables or updates existing ones based on the brand's light and dark themes. +- **Update or create variables**: Adds new variables or updates existing ones based on the brand's light and dark themes. - **Handle variable modes**: Ensures each brand's mode (e.g., "Light", "Dark") is updated or created in the Figma "Brand" collection. - **Support for multiple brands**: Processes multiple brands, mapping each brand's unique variables into Figma's collections. From d648f8c6830aa995dbfffdfac40ca62a5719379c Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Fri, 4 Oct 2024 17:00:38 +0200 Subject: [PATCH 70/71] Update README --- tokens/figma/README.md | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/tokens/figma/README.md b/tokens/figma/README.md index a7ab7f876d1..8f78cb592e5 100644 --- a/tokens/figma/README.md +++ b/tokens/figma/README.md @@ -15,8 +15,6 @@ This project is designed to update Figma variables based on a JSON input, primar ### Environment variables: - `FIGMA_TOKEN`: The API token to authenticate with Figma. -- `MIDDLEWARE_KEY`: Token for file where the variables need to be created / updated -- `{BRAND}_FILE_KEY`: File key of each of the brand library files ### Dependencies: @@ -24,29 +22,23 @@ This project is designed to update Figma variables based on a JSON input, primar ## Key functions -### `updateTheme(jsonData, brand, FILE_KEY)` +### `updateModeCollection(jsonData, brand)` -This function updates the theme variables in Figma for a specific brand. It: +This function updates the color-scheme variables in Figma for a specific brand. It: - Fetches the current variables from Figma. -- Updates modes and variables for `"Light"` and `"Dark"` themes. +- Updates modes and variables for `"Light"` and `"Dark"` color-schemes. - Sends a POST request to update Figma with the new data. -### `updateSkinColorVariables(brands, FILE_KEY)` +### `updateBrandCollection(jsonData)` This function focuses on updating color variables in the "Brand" collection. It: - Maps color variables from the "Mode" collection to the "Brand" collection. +- Adds non-color variables for each brand. - Creates or updates modes for each brand. - Ensures proper aliasing of variables between collections. -### `updateSkinOtherVariables(jsonData, brands, FILE_KEY)` - -This function updates non-color variables, such as font families and icon sets, for each brand. It: - -- Handles specific design tokens like radius, font weight, and line height. -- Adds brand-specific font families and icons. - ## Usage 1. Navigate to the `tokens/figma` directory: From 9aab7ac9e766a5700615762310ae872c5d537ca4 Mon Sep 17 00:00:00 2001 From: Alex Bueno <44420072+aweell@users.noreply.github.com> Date: Tue, 8 Oct 2024 14:26:08 +0200 Subject: [PATCH 71/71] Fixes from CR --- tokens/figma/config.mjs | 4 ++-- tokens/figma/update-middleware.mjs | 20 +++++++++-------- tokens/figma/update-skins.mjs | 36 ++++++++++-------------------- 3 files changed, 25 insertions(+), 35 deletions(-) diff --git a/tokens/figma/config.mjs b/tokens/figma/config.mjs index da646708cef..5e484054a8c 100644 --- a/tokens/figma/config.mjs +++ b/tokens/figma/config.mjs @@ -4,7 +4,7 @@ dotenv.config({ path: "../../.env" }); import { BRANDS } from "./utils/constants.mjs"; -export const brands = { +export const BRAND_KEY = { [BRANDS.MOVISTAR]: "ObNHOLPtrIytjy9BH7M9jW", [BRANDS.O2_NEW]: "CjvgrHEIycSQ6exznxnFXT", [BRANDS.VIVO_NEW]: "EApRpjaTyUOwW5VQU2ZqgP", @@ -13,7 +13,7 @@ export const brands = { [BRANDS.TU]: "19IXMaFqdYeC1IIdTwXBgY", }; -export const MIDDLEWARE_TOKEN = +export const MIDDLEWARE_KEY = "w7fBxCsEb8WrMVVuxDnCQd"; export const FIGMA_TOKEN = diff --git a/tokens/figma/update-middleware.mjs b/tokens/figma/update-middleware.mjs index 8ac16bbcb86..20fce9d2796 100644 --- a/tokens/figma/update-middleware.mjs +++ b/tokens/figma/update-middleware.mjs @@ -25,10 +25,12 @@ import { import formatBrandName from "./utils/format-brand-name.mjs"; -import { brands } from "./config.mjs"; -import { MIDDLEWARE_TOKEN } from "./config.mjs"; +import { + BRAND_KEY, + MIDDLEWARE_KEY, +} from "./config.mjs"; -const brandNames = Object.keys(brands); +const brandNames = Object.keys(BRAND_KEY); async function updateModeCollection( jsonData, @@ -36,7 +38,7 @@ async function updateModeCollection( ) { try { const figmaData = await getFigmaData( - MIDDLEWARE_TOKEN + MIDDLEWARE_KEY ); const existingVariables = figmaData.meta.variables; @@ -196,7 +198,7 @@ async function updateModeCollection( // Update the variables and modes in Figma await postFigmaVariables( - MIDDLEWARE_TOKEN, + MIDDLEWARE_KEY, newData ); @@ -212,7 +214,7 @@ async function updateBrandCollection(jsonData) { // Step 1: Fetch the existing data from Figma const figmaData = await getFigmaData( - MIDDLEWARE_TOKEN + MIDDLEWARE_KEY ); const existingCollections = figmaData.meta.variableCollections; @@ -469,7 +471,7 @@ async function updateBrandCollection(jsonData) { // Step 9: Send the data to update the Brand collection (POST) await postFigmaVariables( - MIDDLEWARE_TOKEN, + MIDDLEWARE_KEY, newData ); @@ -489,11 +491,11 @@ async function postCollections(brand) { try { const newData = await updateCollections( collectionNames, - MIDDLEWARE_TOKEN + MIDDLEWARE_KEY ); await postFigmaVariables( - MIDDLEWARE_TOKEN, + MIDDLEWARE_KEY, newData ); } catch (error) { diff --git a/tokens/figma/update-skins.mjs b/tokens/figma/update-skins.mjs index a2a3c1af1d0..15cac81c8a1 100644 --- a/tokens/figma/update-skins.mjs +++ b/tokens/figma/update-skins.mjs @@ -16,17 +16,14 @@ import { import { getPaletteVariables } from "./variables.mjs"; -import { brands } from "./config.mjs"; +import { BRAND_KEY } from "./config.mjs"; const collectionNames = [ COLLECTION_NAMES.PALETTE, ]; -async function updatePalette( - jsonData, - brand, - FILE_KEY -) { +async function updatePalette(jsonData, brand) { + const FILE_KEY = BRAND_KEY[brand]; try { const figmaData = await getFigmaData( FILE_KEY @@ -107,7 +104,9 @@ async function updatePalette( } } -async function postCollections(brand, FILE_KEY) { +async function postCollections(brand) { + const FILE_KEY = BRAND_KEY[brand]; + try { const newData = await updateCollections( collectionNames, @@ -123,16 +122,12 @@ async function postCollections(brand, FILE_KEY) { } } -async function postPalette( - jsonData, - brand, - FILE_KEY -) { +async function postPalette(jsonData, brand) { + const FILE_KEY = BRAND_KEY[brand]; try { const newData = await updatePalette( jsonData, - brand, - FILE_KEY + brand ); await postFigmaVariables(FILE_KEY, newData); @@ -144,16 +139,9 @@ async function postPalette( } } -// Process data for a specific brand - -async function processBrand(jsonData, brand) { - const FILE_KEY = brands[brand]; - await postCollections(brand, FILE_KEY); - await postPalette(jsonData, brand, FILE_KEY); -} - export async function updateSkinFiles(jsonData) { - for (const brand of Object.keys(brands)) { - await processBrand(jsonData, brand); + for (const brand of Object.keys(BRAND_KEY)) { + await postCollections(brand); + await postPalette(jsonData, brand); } }