Skip to content

Commit 60979a8

Browse files
feat: add Code Connect template mappings and publish workflow (#832)
* feat: add code-connect support * feat: add code connect workflow * chore: add worflow dispatch * chore: add mask for figma files * fix: improve code connect files * fix: remove code connect defaults * fix: update emptystate code connect
1 parent eb21a6c commit 60979a8

78 files changed

Lines changed: 2312 additions & 552 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
name: Publish Figma Code Connect
2+
3+
# Code Connect mappings are version-independent — they only change when the
4+
# template files do. Publish to Figma whenever those files land on main,
5+
# or trigger manually from the Actions tab.
6+
on:
7+
workflow_dispatch:
8+
push:
9+
branches: [main]
10+
paths:
11+
- "packages/raystack/figma/**"
12+
- "packages/raystack/figma.config.json"
13+
- "packages/raystack/scripts/generate-icons-code-connect.js"
14+
- ".github/workflows/code-connect.yaml"
15+
16+
# Avoid overlapping publishes racing against the same Figma file.
17+
concurrency:
18+
group: code-connect
19+
cancel-in-progress: true
20+
21+
jobs:
22+
publish:
23+
name: Publish Figma Code Connect
24+
runs-on: ubuntu-latest
25+
timeout-minutes: 10
26+
steps:
27+
- name: Checkout 🛎️
28+
uses: actions/checkout@v4
29+
30+
- name: Setup pnpm 9
31+
uses: pnpm/action-setup@v4
32+
with:
33+
version: 9.3.0
34+
35+
- name: Setup Node.js 22.x
36+
uses: actions/setup-node@v4
37+
with:
38+
node-version: 22.x
39+
cache: "pnpm"
40+
41+
- name: Install Dependencies 🔧
42+
run: pnpm install --frozen-lockfile
43+
44+
# Redact the Figma file keys from all subsequent step logs. The publish
45+
# command prints every mapped node URL; masking turns the file key into
46+
# *** so links read https://figma.com/file/***/?node-id=... in CI logs.
47+
- name: Mask Figma file keys 🔒
48+
env:
49+
FIGMA_FILE_URL: ${{ vars.FIGMA_FILE_URL }}
50+
FIGMA_ICONS_FILE_URL: ${{ vars.FIGMA_ICONS_FILE_URL }}
51+
run: |
52+
for url in "$FIGMA_FILE_URL" "$FIGMA_ICONS_FILE_URL"; do
53+
key=$(printf '%s' "$url" | sed -E 's#.*/(design|file)/([^/?]+).*#\2#')
54+
if [ -n "$key" ] && [ "$key" != "$url" ]; then
55+
echo "::add-mask::$key"
56+
fi
57+
done
58+
59+
# Generate the per-icon batch JSON from the Figma icons file.
60+
- name: Generate icon mappings 🎨
61+
working-directory: ./packages/raystack
62+
env:
63+
FIGMA_ACCESS_TOKEN: ${{ secrets.FIGMA_ACCESS_TOKEN }}
64+
FIGMA_ICONS_FILE_URL: ${{ vars.FIGMA_ICONS_FILE_URL }}
65+
run: pnpm figma:generate-icons
66+
67+
# Substitute the real Figma file URL into figma.config.json.
68+
- name: Update Figma config 🔧
69+
working-directory: ./packages/raystack
70+
env:
71+
FIGMA_FILE_URL: ${{ vars.FIGMA_FILE_URL }}
72+
run: pnpm figma:update-config
73+
74+
# Redact the file key from publish output at the source (belt-and-suspenders
75+
# with the add-mask step). pipefail preserves figma:publish's exit code.
76+
- name: Publish to Figma 🚀
77+
working-directory: ./packages/raystack
78+
env:
79+
FIGMA_ACCESS_TOKEN: ${{ secrets.FIGMA_ACCESS_TOKEN }}
80+
run: |
81+
set -o pipefail
82+
pnpm figma:publish 2>&1 | sed -E 's#(/(design|file)/)[^/?]+#\1***#g'

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,6 @@ yarn-error.log*
4343

4444
# npm pack artifacts
4545
*.tgz
46+
47+
# generated Code Connect batch file (created by figma:generate-icons at publish time)
48+
packages/raystack/figma/icons.figma.batch.json

packages/raystack/figma.config.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
{
22
"codeConnect": {
3-
"include": ["components/**/*", "figma/**/*"],
3+
"include": [
4+
"figma/**/*.figma.ts",
5+
"figma/**/*.figma.batch.ts",
6+
"figma/**/*.figma.batch.json"
7+
],
48
"exclude": [],
59
"parser": "react",
10+
"label": "React",
11+
"language": "jsx",
612
"documentUrlSubstitutions": {
713
"<FIGMA_LINK>": "FIGMA_FILE_URL"
814
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// url=<FIGMA_LINK>?node-id=8089-1150
2+
// source=https://github.com/raystack/apsara/blob/main/packages/raystack/components/accordion/accordion.tsx
3+
// component=Accordion
4+
5+
import figma from 'figma';
6+
7+
// State (Collapsed/Hover/Expanded) is visual-only — the Accordion root exposes no
8+
// matching prop, so it is not mapped. A composed example uses the public sub-component API.
9+
10+
export default {
11+
id: 'Accordion',
12+
imports: ["import { Accordion } from '@raystack/apsara'"],
13+
example: figma.code`<Accordion>
14+
<Accordion.Item value='item-1'>
15+
<Accordion.Trigger>Trigger</Accordion.Trigger>
16+
<Accordion.Content>Content</Accordion.Content>
17+
</Accordion.Item>
18+
</Accordion>`,
19+
metadata: { nestable: true }
20+
};

packages/raystack/figma/accordion.figma.tsx

Lines changed: 0 additions & 19 deletions
This file was deleted.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// url=<FIGMA_LINK>?node-id=6633-2337
2+
// source=https://github.com/raystack/apsara/blob/main/packages/raystack/components/alert-dialog/alert-dialog.tsx
3+
// component=AlertDialog
4+
5+
import figma from 'figma';
6+
7+
const instance = figma.selectedInstance;
8+
9+
// Title/Description booleans toggle whether those sub-components are rendered.
10+
const title = instance.getBoolean('Title', {
11+
true: `
12+
<AlertDialog.Title>Are you sure?</AlertDialog.Title>`,
13+
false: ''
14+
});
15+
const description = instance.getBoolean('Description', {
16+
true: `
17+
<AlertDialog.Description>
18+
This action cannot be undone.
19+
</AlertDialog.Description>`,
20+
false: ''
21+
});
22+
23+
export default {
24+
id: 'AlertDialog',
25+
imports: ["import { AlertDialog } from '@raystack/apsara'"],
26+
example: figma.code`<AlertDialog>
27+
<AlertDialog.Trigger>Open</AlertDialog.Trigger>
28+
<AlertDialog.Content>
29+
<AlertDialog.Header>${title}${description}
30+
</AlertDialog.Header>
31+
<AlertDialog.Footer>
32+
<AlertDialog.Close>Cancel</AlertDialog.Close>
33+
<AlertDialog.Close>Continue</AlertDialog.Close>
34+
</AlertDialog.Footer>
35+
</AlertDialog.Content>
36+
</AlertDialog>`,
37+
metadata: { nestable: false }
38+
};
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// url=<FIGMA_LINK>?node-id=613-990
2+
// source=https://github.com/raystack/apsara/blob/main/packages/raystack/components/announcement-bar/announcement-bar.tsx
3+
// component=AnnouncementBar
4+
5+
import figma from 'figma';
6+
7+
const variant = figma.selectedInstance.getEnum('Variant', {
8+
Error: 'error',
9+
Gradient: 'gradient'
10+
});
11+
const text = figma.selectedInstance.getString('Message');
12+
const leadingIcon = figma.selectedInstance.getBoolean('Leading icon', {
13+
true: figma.selectedInstance.getInstanceSwap('Icon')?.executeTemplate()
14+
.example,
15+
false: undefined
16+
});
17+
18+
export default {
19+
id: 'AnnouncementBar',
20+
imports: ["import { AnnouncementBar } from '@raystack/apsara'"],
21+
example: figma.code`<AnnouncementBar${figma.helpers.react.renderProp(
22+
'variant',
23+
variant
24+
)}${figma.helpers.react.renderProp(
25+
'text',
26+
text
27+
)}${figma.helpers.react.renderProp('leadingIcon', leadingIcon)}/>`,
28+
metadata: { nestable: true }
29+
};

packages/raystack/figma/announcement-bar.tsx

Lines changed: 0 additions & 19 deletions
This file was deleted.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// url=<FIGMA_LINK>?node-id=1-24
2+
// source=https://github.com/raystack/apsara/blob/main/packages/raystack/components/avatar/avatar.tsx
3+
// component=Avatar
4+
5+
import figma from 'figma';
6+
7+
const radius = figma.selectedInstance.getEnum('Radius', {
8+
Full: 'full'
9+
});
10+
const size = figma.selectedInstance.getEnum('Size', {
11+
'1': 1,
12+
'2': 2,
13+
'4': 4,
14+
'5': 5,
15+
'6': 6,
16+
'7': 7,
17+
'8': 8,
18+
'9': 9,
19+
'10': 10,
20+
'11': 11,
21+
'12': 12,
22+
'13': 13
23+
}); // '3' removed (default)
24+
const fallback = figma.selectedInstance.getString('Initials');
25+
const src = figma.selectedInstance.getBoolean('Image', {
26+
true: 'https://example.com/avatar.png',
27+
false: undefined
28+
});
29+
30+
export default {
31+
id: 'Avatar',
32+
imports: ["import { Avatar } from '@raystack/apsara'"],
33+
example: figma.code`<Avatar${figma.helpers.react.renderProp(
34+
'radius',
35+
radius
36+
)}${figma.helpers.react.renderProp(
37+
'size',
38+
size
39+
)}${figma.helpers.react.renderProp(
40+
'src',
41+
src
42+
)}${figma.helpers.react.renderProp('fallback', fallback)}/>`,
43+
metadata: { nestable: true }
44+
};

packages/raystack/figma/avatar.figma.tsx

Lines changed: 0 additions & 29 deletions
This file was deleted.

0 commit comments

Comments
 (0)