Skip to content
This repository has been archived by the owner on Aug 30, 2023. It is now read-only.

Figma to Angular component converter =) #16

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions figma-to-angular/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Editor configuration, see https://editorconfig.org
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

[*.md]
max_line_length = off
trim_trailing_whitespace = false
46 changes: 46 additions & 0 deletions figma-to-angular/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.

# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out

# dependencies
/node_modules

# profiling files
chrome-profiler-events.json
speed-measure-plugin.json

# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*

# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings

# System Files
.DS_Store
Thumbs.db
81 changes: 81 additions & 0 deletions figma-to-angular/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Figma to Angular Converter

This demonstrates using the Figma REST API to convert a Figma document to Angular Components and Figma components module.
Disclaimer: this code is likely incomplete, and may have bugs. It is not intended to be used
in production. This is simply a proof of concept to show what possibilities exist.

## API Usage

We use 2 endpoints in this project:

- `GET /v1/files/:file_key` - Get the JSON tree from a file. This is the main workhorse of this project and lays the skeleton of the React Components.
- `GET /v1/images/:file_key` - When we identify nodes that are vectors or other nodes that can't directly be represented by `div`s, we have to render them as svgs.

## Set up

1. Install [Node](https://nodejs.org/). You'll need a recent version that supports `async / await`
2. In this directory, run `npm install`
3. Run the converter per instructions below

## Usage

By default this project comes with a prerendered component in `src/figmaComponents`. You can see a page that uses this component if you
run `npm start`. This will start a React server and a webpage will open to the root page. This webpage will automatically refresh as
you make changes to the source documents. To follow along with the example component, the source Figma file is located [here](https://www.figma.com/file/VGULlnz44R0Ooe4FZKDxlhh4/Untitled).

When we run the converter, we will convert any *top level frames* in the document to Angular Components *as long as their name starts with `#`*.
In the example document you can see that we have one top level frame named `#Clock`. The component resulting from this will be exported in
`src/components/CFClock.component` as `CFClock`, a `dump component`.

In addition, *any* node with a name starting with a `#` will have a code stub generated for it in `src/components`. These code stubs can be
modified to affect the rendering of those components as well as modifying variables within the component (see variables section below).

To run the converter on a file, you will need a personal access token from Figma. Refer to the [Figma API documentation](https://www.figma.com/developers/docs)
for more information on how to obtain a token. The other piece of information you will need is the file key of the file you wish to convert,
which is located in the file's URL (for example, this is `VGULlnz44R0Ooe4FZKDxlhh4` for the example file). So, an example conversion would look
like:

```
node main.js VGULlnz44R0Ooe4FZKDxlhh4 <API_TOKEN_HERE>
```

where `<API_TOKEN_HERE>` would be repaced with your developer token.

You can also change the .env file and insert your token there

```
//.env
DEV_TOKEN=<YOUR_TOKEN>
```
then you only need to pass the project id
```
node main.js VGULlnz44R0Ooe4FZKDxlhh4
```



*IMPORTANT*: The index.css file is required to be included for components to render completely.

## Variables

The real vision of this converter is to separate design concerns from coding concerns. Toward this end, we introduce the concept of
`variables` in Figma. Variables in a Figma file are denoted by text nodes (this can be expanded in the future) with names starting with
`$`. In the example document there are three variables: `$time`, `$seconds`, and `$ampm`. By setting state in the component stubs defined in
`src/components`, we can *change the text of the variable nodes*. For an example, take a look at `src/app/app.component`. This
allows us to change the design of a component without touching the code at all.

## Why Angular?

Because it's one of the best frameworks

## Examples

Here's an example of using Figma to React to update the style on a sortable list:

![duck-list](https://static.figma.com/uploads/9e647f547b9487af4d879627de3bae84591671c1)


Here's an example of using Figma to React to attach code to a component dragged in from the Team Library:

![clock](https://static.figma.com/uploads/3e4a5e166e295433c29c0e3e78d3a436efc64353)

124 changes: 124 additions & 0 deletions figma-to-angular/angular.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"figma-to-angular": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/figma-to-angular",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": false,
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.scss"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "figma-to-angular:build"
},
"configurations": {
"production": {
"browserTarget": "figma-to-angular:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "figma-to-angular:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.scss"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "figma-to-angular:serve"
},
"configurations": {
"production": {
"devServerTarget": "figma-to-angular:serve:production"
}
}
}
}
}},
"defaultProject": "figma-to-angular"
}
12 changes: 12 additions & 0 deletions figma-to-angular/browserslist
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries

# You can see what browsers were selected by your queries by running:
# npx browserslist

> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11 # For IE 9-11 support, remove 'not'.
32 changes: 32 additions & 0 deletions figma-to-angular/e2e/protractor.conf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts

const { SpecReporter } = require('jasmine-spec-reporter');

/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};
23 changes: 23 additions & 0 deletions figma-to-angular/e2e/src/app.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';

describe('workspace-project App', () => {
let page: AppPage;

beforeEach(() => {
page = new AppPage();
});

it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('Welcome to figma-to-angular!');
});

afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});
11 changes: 11 additions & 0 deletions figma-to-angular/e2e/src/app.po.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { browser, by, element } from 'protractor';

export class AppPage {
navigateTo() {
return browser.get(browser.baseUrl) as Promise<any>;
}

getTitleText() {
return element(by.css('app-root h1')).getText() as Promise<string>;
}
}
13 changes: 13 additions & 0 deletions figma-to-angular/e2e/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}
32 changes: 32 additions & 0 deletions figma-to-angular/karma.conf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html

module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage/figma-to-angular'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};
Loading