From 41476f24e2e9a4e479ae84cea25cd8bdc1caadff Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Wed, 22 Jul 2026 16:53:10 +0300 Subject: [PATCH 01/28] chore: figma connect Button --- .gitignore | 1 + packages/main/FIGMA_CODE_CONNECT.md | 123 ++++++++ packages/main/figma.config.json | 8 + packages/main/figma.config.react.json | 8 + packages/main/package.json | 1 + packages/main/src/Button.figma.ts | 81 +++++ packages/main/src/Button.figma.tsx | 65 ++++ yarn.lock | 412 +++++++++++++++++++++++++- 8 files changed, 684 insertions(+), 15 deletions(-) create mode 100644 packages/main/FIGMA_CODE_CONNECT.md create mode 100644 packages/main/figma.config.json create mode 100644 packages/main/figma.config.react.json create mode 100644 packages/main/src/Button.figma.ts create mode 100644 packages/main/src/Button.figma.tsx diff --git a/.gitignore b/.gitignore index accf480170221..a2f5d1577607e 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ npm-debug.log # Ignore Mac files .DS_Store +.claude/playwright-* # Ignore Visual Studio project and settings files *.sln *.suo diff --git a/packages/main/FIGMA_CODE_CONNECT.md b/packages/main/FIGMA_CODE_CONNECT.md new file mode 100644 index 0000000000000..1d34691f51bc1 --- /dev/null +++ b/packages/main/FIGMA_CODE_CONNECT.md @@ -0,0 +1,123 @@ +# Figma Code Connect — Button: known limitations & owner actions + +This documents the **Web Components** Code Connect mapping for the SAP Web UI Kit +`Button` (Figma node `91702:11733` in file `SILcWzK5uFghKun9jx6D7c`), what works, +what does **not** update dynamically when you select different button variants in +Dev Mode, **why**, and **what the Figma file owner must change** to fix it. + +Mapping file: `packages/main/src/Button.figma.ts` +Config: `packages/main/figma.config.json` (`parser: "html"`, `label: "Web Components"`) + +The goal is a snippet with **no hardcoded values** — selecting a different button +(different icon, different badge) in Dev Mode should be reflected in the generated +`` code. Today, two things block that. Both are **Figma-side modeling +gaps**, not code bugs: Code Connect can only surface values that the Figma +component actually exposes as readable properties. + +--- + +## ✅ What already works (dynamic) + +These reflect the selected variant correctly in Dev Mode: + +| Snippet output | Driven by Figma property | Notes | +|---|---|---| +| `design="…"` | `Type` variant | Primary→Emphasized, Secondary→Default, Tertiary→Transparent, Accept→Positive, Reject→Negative, Attention→Attention | +| `disabled` | `Interaction State` variant | emitted only for `Disabled` | +| button label text | `Text` layer (`figma.textContent`) | reads the actual layer string | + +--- + +## ❌ Problem 1 — Icon is not dynamic + +**Symptom:** changing the icon on a button in Figma does not change the code; the +snippet always shows `icon="globe"`. + +**Why:** the icon is a Figma **instance-swap** property (`Icon`, type +`INSTANCE_SWAP`). Code Connect can read a *boolean* (`Icon Left` — is an icon +shown or not), but it **cannot read the swapped icon's name into a string** +unless the icon components in the Kit's icon library are themselves +Code-Connected to output their name. They are not. So the mapping can only toggle +the attribute on/off and must hardcode a placeholder name. + +**Important context:** in code there is no per-icon *component* to import. A UI5 +icon is just a **name string** in a registry (`icon="globe"`), resolved to an SVG +at runtime. That shapes who owns each fix below. + +There are two ways to fix this — one is the Figma owner's, one is ours: + +### Option A — `Icon Name` text property → **Figma owner's job** +Add a plain **text property** on the Button component (e.g. `Icon Name`) holding +the icon's registry name. The mapping reads it with `figma.string("Icon Name")` +and emits `icon="…"` dynamically. Pure Figma-side change, no icon-library work. +Trade-off: it's duplicated data — the designer must keep the typed name in sync +with the icon actually shown, so it can drift. + +### Option B — Code Connect the icon set → **webcomponents / tooling team's job** +"Code-Connecting the icon library" does **not** mean mapping icons to +components — there aren't any. It means authoring one tiny Code Connect entry +**per icon** whose only output is its own registry name, so that +`figma.instance("Icon")` on the Button can pull the *selected* icon's name into +the snippet. The Kit already names its icon instances by icon +(`mainComponentName: "globe"`), so the correspondence exists; the work is +**generating** those hundreds of entries (not hand-writing them). Correct +long-term, zero designer effort, no drift. The owner's only responsibility here +is to keep the icons as real, connectable components in the Kit. + +**Recommendation:** Option A for a quick owner-only fix now; Option B (generated) +as the durable solution owned by the webcomponents team. + +Until either lands, `icon="globe"` is a placeholder the consumer edits by hand. + +--- + +## ❌ Problem 2 — Badge design & text are not dynamic + +**Symptom:** clicking buttons with different badges doesn't change the code; the +counter badge always renders `design="OverlayText" text="1"`, and there's no way +to get `design="InlineText"`. + +**Why:** the `ui5-button-badge` web component supports **three** designs — +`InlineText`, `OverlayText`, `AttentionDot`. But in Figma the badge is modeled as +**two independent booleans**: + +- `Counter Badge` (True/False) +- `Attention Badge` (True/False) + +There is **no badge-`design` enum** to read, so the mapping cannot distinguish +`OverlayText` from `InlineText` — it can only detect *counter badge present*. +Likewise the counter **number** lives in a nested, unexposed text layer +(`Counter Badge` instance → child text `"72"`), so the `text="…"` value can't be +read either. Both are therefore hardcoded. + +**What the Figma owner should do:** + +1. **Replace the two badge booleans with a single badge `design` variant/enum** + on the Button component, with options that map 1:1 to the web component: + `None | InlineText | OverlayText | AttentionDot`. The mapping can then use + `figma.enum("Badge Design", { … })` to emit the correct `design="…"` (and omit + the badge entirely for `None`). +2. **Expose the badge text as a component text property** (e.g. a `Badge Text` + string prop) instead of a buried nested layer, so the mapping can read it with + `figma.string("Badge Text")` and emit `text="…"` dynamically. `AttentionDot` + has no text and should omit it. + +Until then, the badge snippet is fixed at `OverlayText` / `text="1"` for the +counter and `AttentionDot` for the attention badge. + +--- + +## Summary for the owner + +To make the Button snippet fully dynamic (no hardcoded values), the Figma +component needs: + +1. **Icon:** either Code-Connect the icon library components, or add an + `Icon Name` text property on Button. +2. **Badge design:** a single `Badge Design` enum (`None/InlineText/OverlayText/ + AttentionDot`) replacing the two booleans. +3. **Badge text:** a `Badge Text` string property replacing the nested text layer. + +Once (1)–(3) exist, update `packages/main/src/Button.figma.ts` to swap the +hardcoded strings for `figma.instance`/`figma.enum`/`figma.string` reads and +re-publish. diff --git a/packages/main/figma.config.json b/packages/main/figma.config.json new file mode 100644 index 0000000000000..1f9362e1ad338 --- /dev/null +++ b/packages/main/figma.config.json @@ -0,0 +1,8 @@ +{ + "codeConnect": { + "include": ["src/**/*.figma.ts"], + "exclude": ["node_modules/**", "dist/**"], + "parser": "html", + "label": "Web Components" + } +} diff --git a/packages/main/figma.config.react.json b/packages/main/figma.config.react.json new file mode 100644 index 0000000000000..375f916fcfe1e --- /dev/null +++ b/packages/main/figma.config.react.json @@ -0,0 +1,8 @@ +{ + "codeConnect": { + "include": ["src/**/*.figma.tsx"], + "exclude": ["node_modules/**", "dist/**"], + "parser": "react", + "label": "React" + } +} diff --git a/packages/main/package.json b/packages/main/package.json index c27df482a3151..c0ddca34c1ccf 100644 --- a/packages/main/package.json +++ b/packages/main/package.json @@ -65,6 +65,7 @@ }, "devDependencies": { "@custom-elements-manifest/analyzer": "^0.10.10", + "@figma/code-connect": "^1.4.9", "@ui5/cypress-internal": "0.1.0", "@ui5/webcomponents-tools": "2.25.0-rc.0", "cypress": "15.18.1", diff --git a/packages/main/src/Button.figma.ts b/packages/main/src/Button.figma.ts new file mode 100644 index 0000000000000..dd07ed915a2c2 --- /dev/null +++ b/packages/main/src/Button.figma.ts @@ -0,0 +1,81 @@ +/** + * Web Components Code Connect mapping for the SAP Web UI Kit "Button". + * + * This maps the SAME Figma Button component set (node 91702:11733) that is + * already connected under the "React" label (@ui5/webcomponents-react) — but + * under the "Web Components" label. With both labels present, Figma Dev Mode + * shows a framework switcher (React / Web Components) on the Button. + * + * The snippet targets the framework-agnostic UI5 Web Component , + * usable from plain HTML, Angular, Vue, and React alike. + * + * Uses the single-argument figma.connect() signature: there is no code + * component to import, we render a custom-element tag from a template. + * + * HTML-parser rules (stricter than the React parser): + * - No inline conditionals/ternaries in the template — every attribute value + * is resolved to a final string inside `props` via figma.enum/figma.boolean. + * - Nested child elements (the badge) are provided as html`` partials so they + * render as real elements rather than escaped text. + * - The button label is a text *layer* named "Text", so it is read with + * figma.textContent (not figma.string, which expects a component property). + */ +import figma, { html } from "@figma/code-connect/html"; + +figma.connect( + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=91702-11733", + { + props: { + // Button label — "Text" is a text layer, not a component property. + label: figma.textContent("Text"), + + // Figma "Type" variant → ui5-button `design` attribute. + design: figma.enum("Type", { + Primary: "Emphasized", + Secondary: "Default", + Tertiary: "Transparent", + Accept: "Positive", + Reject: "Negative", + Attention: "Attention", + }), + + // Only the "Disabled" interaction state emits the `disabled` attribute. + disabledAttr: figma.enum("Interaction State", { + Disabled: "disabled", + Regular: "", + Hover: "", + Down: "", + }), + + // Leading icon presence → `icon="…"`. + // LIMITATION: the icon NAME is hardcoded to "globe". Figma models the + // icon as an INSTANCE_SWAP whose swapped icon name cannot be read into a + // string unless the Kit's icon components are themselves Code-Connected. + // So this only toggles the attribute on/off; it can't reflect which icon + // is selected. See FIGMA_CODE_CONNECT.md § "Icon is not dynamic". + iconAttr: figma.boolean("Icon Left", { + true: 'icon="globe"', + false: "", + }), + + // Counter badge → slotted . + // LIMITATION: design="OverlayText" and text="1" are hardcoded. Figma has + // no badge-design enum (only a Counter Badge boolean) and the counter + // number lives in an unexposed nested text layer, so neither the design + // (Overlay vs Inline) nor the number can be read dynamically. + // See FIGMA_CODE_CONNECT.md § "Badge design/text is not dynamic". + counterBadge: figma.boolean("Counter Badge", { + true: html``, + false: "", + }), + + // Attention badge → slotted attention dot. + attentionBadge: figma.boolean("Attention Badge", { + true: html``, + false: "", + }), + }, + example: ({ label, design, disabledAttr, iconAttr, counterBadge, attentionBadge }) => + html`${label}${counterBadge}${attentionBadge}`, + } +); diff --git a/packages/main/src/Button.figma.tsx b/packages/main/src/Button.figma.tsx new file mode 100644 index 0000000000000..e77b2cfddf2a5 --- /dev/null +++ b/packages/main/src/Button.figma.tsx @@ -0,0 +1,65 @@ +/** + * React Code Connect mapping for the SAP Web UI Kit "Button". + * + * Same Figma node (91702:11733) and same prop mapping as the Web Components + * mapping in `Button.figma.ts`, but emitting @ui5/webcomponents-react syntax + * under the "React" label. With both labels present on the node, Figma Dev Mode + * shows a React / Web Components framework switcher. + * + * Published from this repo via `figma.config.react.json` (parser: "react"). + * The React parser only reads the import string — @ui5/webcomponents-react does + * not need to be installed here for publishing to succeed. + * + * NOTE: the same Figma-side limitations documented in FIGMA_CODE_CONNECT.md + * apply here — the icon name and badge design/text are hardcoded because Figma + * does not expose them as readable properties. + */ +import figma from "@figma/code-connect/react"; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { Button, ButtonBadge, ButtonBadgeDesign } from "@ui5/webcomponents-react"; + +figma.connect( + Button, + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=91702-11733", + { + props: { + // Button label — "Text" is a text layer, not a component property. + label: figma.textContent("Text"), + + // Figma "Type" variant → ui5 Button `design` prop. + design: figma.enum("Type", { + Primary: "Emphasized", + Secondary: "Default", + Tertiary: "Transparent", + Accept: "Positive", + Reject: "Negative", + Attention: "Attention", + }), + + // Only the "Disabled" interaction state sets `disabled`. + disabled: figma.enum("Interaction State", { + Disabled: true, + Regular: false, + Hover: false, + Down: false, + }), + + // Counter badge presence → child on the `badge` prop. + // LIMITATION: design (OverlayText) and text ("1") are hardcoded — Figma + // exposes only a Counter Badge boolean, no design enum or readable text. + // This mirrors the existing @ui5/webcomponents-react mapping, which also + // only emits the counter (OverlayText) badge. See FIGMA_CODE_CONNECT.md. + badge: figma.boolean("Counter Badge", { + true: ( + + ), + false: undefined, + }), + }, + example: ({ label, design, disabled, badge }) => ( + + ), + } +); diff --git a/yarn.lock b/yarn.lock index ae4c9acafe12c..9be9f40d1590c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -263,6 +263,19 @@ __metadata: languageName: node linkType: hard +"@asamuzakjp/css-color@npm:^3.2.0": + version: 3.2.0 + resolution: "@asamuzakjp/css-color@npm:3.2.0" + dependencies: + "@csstools/css-calc": "npm:^2.1.3" + "@csstools/css-color-parser": "npm:^3.0.9" + "@csstools/css-parser-algorithms": "npm:^3.0.4" + "@csstools/css-tokenizer": "npm:^3.0.3" + lru-cache: "npm:^10.4.3" + checksum: 10c0/a4bf1c831751b1fae46b437e37e8a38c0b5bd58d23230157ae210bd1e905fe509b89b7c243e63d1522d852668a6292ed730a160e21342772b4e5b7b8ea14c092 + languageName: node + linkType: hard + "@babel/code-frame@npm:7.12.11": version: 7.12.11 resolution: "@babel/code-frame@npm:7.12.11" @@ -2517,7 +2530,7 @@ __metadata: languageName: node linkType: hard -"@csstools/css-calc@npm:^2.1.4": +"@csstools/css-calc@npm:^2.1.3, @csstools/css-calc@npm:^2.1.4": version: 2.1.4 resolution: "@csstools/css-calc@npm:2.1.4" peerDependencies: @@ -2540,7 +2553,7 @@ __metadata: languageName: node linkType: hard -"@csstools/css-color-parser@npm:^3.1.0": +"@csstools/css-color-parser@npm:^3.0.9, @csstools/css-color-parser@npm:^3.1.0": version: 3.1.0 resolution: "@csstools/css-color-parser@npm:3.1.0" dependencies: @@ -4530,6 +4543,38 @@ __metadata: languageName: node linkType: hard +"@figma/code-connect@npm:^1.4.9": + version: 1.4.9 + resolution: "@figma/code-connect@npm:1.4.9" + dependencies: + boxen: "npm:5.1.1" + chalk: "npm:^4.1.2" + commander: "npm:^11.1.0" + compare-versions: "npm:^6.1.0" + cross-spawn: "npm:^7.0.3" + dotenv: "npm:^16.3.1" + fast-fuzzy: "npm:^1.12.0" + find-up: "npm:^5.0.0" + glob: "npm:^11.0.4" + jsdom: "npm:^24.1.1" + lodash: "npm:4.18.1" + minimatch: "npm:^9.0.3" + ora: "npm:^5.4.1" + parse5: "npm:^7.1.2" + prettier: "npm:^2.8.8" + prompts: "npm:^2.4.2" + strip-ansi: "npm:^6.0.0" + ts-morph: "npm:^27.0.0" + typescript: "npm:6.0.3" + undici: "npm:^7.19.1" + zod: "npm:3.25.58" + zod-validation-error: "npm:^3.2.0" + bin: + figma: bin/figma + checksum: 10c0/6ea62ed07490526190bb6b2bae70d0864a7799aa437572c757dbdfd4b00979d73928775432e5bc5db9009533b067a4196135a4c4242e43345cad5d844746cc7e + languageName: node + linkType: hard + "@github/catalyst@npm:^1.6.0": version: 1.6.0 resolution: "@github/catalyst@npm:1.6.0" @@ -7665,6 +7710,17 @@ __metadata: languageName: node linkType: hard +"@ts-morph/common@npm:~0.28.1": + version: 0.28.1 + resolution: "@ts-morph/common@npm:0.28.1" + dependencies: + minimatch: "npm:^10.0.1" + path-browserify: "npm:^1.0.1" + tinyglobby: "npm:^0.2.14" + checksum: 10c0/d51276d840997e0f8f83e04f8b1689135bb12588a7ddbed575f87848d5737eeae31e242685d6449de27573e8ed30892157fea643393cb875e175f2711200bc50 + languageName: node + linkType: hard + "@tsconfig/node10@npm:^1.0.7": version: 1.0.9 resolution: "@tsconfig/node10@npm:1.0.9" @@ -9120,6 +9176,7 @@ __metadata: resolution: "@ui5/webcomponents@workspace:packages/main" dependencies: "@custom-elements-manifest/analyzer": "npm:^0.10.10" + "@figma/code-connect": "npm:^1.4.9" "@ui5/cypress-internal": "npm:0.1.0" "@ui5/webcomponents-base": "npm:2.25.0-rc.0" "@ui5/webcomponents-icons": "npm:2.25.0-rc.0" @@ -10151,7 +10208,7 @@ __metadata: languageName: node linkType: hard -"ansi-align@npm:^3.0.1": +"ansi-align@npm:^3.0.0, ansi-align@npm:^3.0.1": version: 3.0.1 resolution: "ansi-align@npm:3.0.1" dependencies: @@ -10842,6 +10899,13 @@ __metadata: languageName: node linkType: hard +"balanced-match@npm:^4.0.2": + version: 4.0.4 + resolution: "balanced-match@npm:4.0.4" + checksum: 10c0/07e86102a3eb2ee2a6a1a89164f29d0dbaebd28f2ca3f5ca786f36b8b23d9e417eb3be45a4acf754f837be5ac0a2317de90d3fcb7f4f4dc95720a1f36b26a17b + languageName: node + linkType: hard + "base64-js@npm:^1.3.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" @@ -11027,6 +11091,22 @@ __metadata: languageName: node linkType: hard +"boxen@npm:5.1.1": + version: 5.1.1 + resolution: "boxen@npm:5.1.1" + dependencies: + ansi-align: "npm:^3.0.0" + camelcase: "npm:^6.2.0" + chalk: "npm:^4.1.0" + cli-boxes: "npm:^2.2.1" + string-width: "npm:^4.2.2" + type-fest: "npm:^0.20.2" + widest-line: "npm:^3.1.0" + wrap-ansi: "npm:^7.0.0" + checksum: 10c0/4b8631b6794c80464d0c4ef78cd0e56257edd8cc4e6debf45fcc8ea4d20b069743d3fa78c9da7c9eee7e6a55fd43b22a0ecfc821c978d4f85b047dbaa9e72821 + languageName: node + linkType: hard + "boxen@npm:^6.2.1": version: 6.2.1 resolution: "boxen@npm:6.2.1" @@ -11078,6 +11158,24 @@ __metadata: languageName: node linkType: hard +"brace-expansion@npm:^2.0.2": + version: 2.1.2 + resolution: "brace-expansion@npm:2.1.2" + dependencies: + balanced-match: "npm:^1.0.0" + checksum: 10c0/5442ecab84045d21826268bc56c81a6ef4215327ee1f5ee153f67928e93f7a915d130ecec9966623143277fa3cce036ebb50020024bcc4d78853cdd6af9a19f8 + languageName: node + linkType: hard + +"brace-expansion@npm:^5.0.5": + version: 5.0.7 + resolution: "brace-expansion@npm:5.0.7" + dependencies: + balanced-match: "npm:^4.0.2" + checksum: 10c0/4769109c3c082de178e449a371bcad50d51ab468f644bce2dd9188efe0cf0a080ed102105d7fc8577382cedc45bad7e6443a91bf3d8102264ee8cf927dbaf205 + languageName: node + linkType: hard + "braces@npm:^3.0.3, braces@npm:~3.0.2": version: 3.0.3 resolution: "braces@npm:3.0.3" @@ -11867,6 +11965,13 @@ __metadata: languageName: node linkType: hard +"cli-boxes@npm:^2.2.1": + version: 2.2.1 + resolution: "cli-boxes@npm:2.2.1" + checksum: 10c0/6111352edbb2f62dbc7bfd58f2d534de507afed7f189f13fa894ce5a48badd94b2aa502fda28f1d7dd5f1eb456e7d4033d09a76660013ef50c7f66e7a034f050 + languageName: node + linkType: hard + "cli-boxes@npm:^3.0.0": version: 3.0.0 resolution: "cli-boxes@npm:3.0.0" @@ -12067,6 +12172,13 @@ __metadata: languageName: node linkType: hard +"code-block-writer@npm:^13.0.3": + version: 13.0.3 + resolution: "code-block-writer@npm:13.0.3" + checksum: 10c0/87db97b37583f71cfd7eced8bf3f0a0a0ca53af912751a734372b36c08cd27f3e8a4878ec05591c0cd9ae11bea8add1423e132d660edd86aab952656dd41fd66 + languageName: node + linkType: hard + "collapse-white-space@npm:^2.0.0": version: 2.1.0 resolution: "collapse-white-space@npm:2.1.0" @@ -12214,6 +12326,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:^11.1.0": + version: 11.1.0 + resolution: "commander@npm:11.1.0" + checksum: 10c0/13cc6ac875e48780250f723fb81c1c1178d35c5decb1abb1b628b3177af08a8554e76b2c0f29de72d69eef7c864d12613272a71fabef8047922bc622ab75a179 + languageName: node + linkType: hard + "commander@npm:^2.20.0": version: 2.20.3 resolution: "commander@npm:2.20.3" @@ -12301,6 +12420,13 @@ __metadata: languageName: node linkType: hard +"compare-versions@npm:^6.1.0": + version: 6.1.1 + resolution: "compare-versions@npm:6.1.1" + checksum: 10c0/415205c7627f9e4f358f571266422980c9fe2d99086be0c9a48008ef7c771f32b0fbe8e97a441ffedc3910872f917a0675fe0fe3c3b6d331cda6d8690be06338 + languageName: node + linkType: hard + "compress-commons@npm:^4.1.0": version: 4.1.1 resolution: "compress-commons@npm:4.1.1" @@ -13089,6 +13215,16 @@ __metadata: languageName: node linkType: hard +"cssstyle@npm:^4.0.1": + version: 4.6.0 + resolution: "cssstyle@npm:4.6.0" + dependencies: + "@asamuzakjp/css-color": "npm:^3.2.0" + rrweb-cssom: "npm:^0.8.0" + checksum: 10c0/71add1b0ffafa1bedbef6855db6189b9523d3320e015a0bf3fbd504760efb9a81e1f1a225228d5fa892ee58e56d06994ca372e7f4e461cda7c4c9985fe075f65 + languageName: node + linkType: hard + "cssstyle@npm:^4.2.1": version: 4.2.1 resolution: "cssstyle@npm:4.2.1" @@ -14043,6 +14179,13 @@ __metadata: languageName: node linkType: hard +"dotenv@npm:^16.3.1": + version: 16.6.1 + resolution: "dotenv@npm:16.6.1" + checksum: 10c0/15ce56608326ea0d1d9414a5c8ee6dcf0fffc79d2c16422b4ac2268e7e2d76ff5a572d37ffe747c377de12005f14b3cc22361e79fc7f1061cce81f77d2c973dc + languageName: node + linkType: hard + "dotenv@npm:^16.5.0": version: 16.5.0 resolution: "dotenv@npm:16.5.0" @@ -14292,6 +14435,13 @@ __metadata: languageName: node linkType: hard +"entities@npm:^6.0.0": + version: 6.0.1 + resolution: "entities@npm:6.0.1" + checksum: 10c0/ed836ddac5acb34341094eb495185d527bd70e8632b6c0d59548cbfa23defdbae70b96f9a405c82904efa421230b5b3fd2283752447d737beffd3f3e6ee74414 + languageName: node + linkType: hard + "env-paths@npm:^2.2.0, env-paths@npm:^2.2.1": version: 2.2.1 resolution: "env-paths@npm:2.2.1" @@ -15626,6 +15776,15 @@ __metadata: languageName: node linkType: hard +"fast-fuzzy@npm:^1.12.0": + version: 1.12.0 + resolution: "fast-fuzzy@npm:1.12.0" + dependencies: + graphemesplit: "npm:^2.4.1" + checksum: 10c0/c4adb03b21472b655414c9cb4680f217790ae641a9974148f55f7778da9a2cac26325996073b1082f76da5ab0f11f845122a6f717a5a99329c17e12ba625f3d6 + languageName: node + linkType: hard + "fast-glob@npm:^3.1.1": version: 3.3.1 resolution: "fast-glob@npm:3.3.1" @@ -15991,6 +16150,19 @@ __metadata: languageName: node linkType: hard +"form-data@npm:^4.0.0": + version: 4.0.6 + resolution: "form-data@npm:4.0.6" + dependencies: + asynckit: "npm:^0.4.0" + combined-stream: "npm:^1.0.8" + es-set-tostringtag: "npm:^2.1.0" + hasown: "npm:^2.0.4" + mime-types: "npm:^2.1.35" + checksum: 10c0/43947a77bf0ff45c6ceed789778982d47a3f3e720a74b71721174ebf3310a5f1a8be1d6b38a3ee3688e8a18a2c4273073ec0844cd37efda3eaf46d41c9c318ff + languageName: node + linkType: hard + "form-data@npm:^4.0.1": version: 4.0.1 resolution: "form-data@npm:4.0.1" @@ -16696,6 +16868,22 @@ __metadata: languageName: node linkType: hard +"glob@npm:^11.0.4": + version: 11.1.0 + resolution: "glob@npm:11.1.0" + dependencies: + foreground-child: "npm:^3.3.1" + jackspeak: "npm:^4.1.1" + minimatch: "npm:^10.1.1" + minipass: "npm:^7.1.2" + package-json-from-dist: "npm:^1.0.0" + path-scurry: "npm:^2.0.0" + bin: + glob: dist/esm/bin.mjs + checksum: 10c0/1ceae07f23e316a6fa74581d9a74be6e8c2e590d2f7205034dd5c0435c53f5f7b712c2be00c3b65bf0a49294a1c6f4b98cd84c7637e29453b5aa13b79f1763a2 + languageName: node + linkType: hard + "glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.2.0": version: 7.2.3 resolution: "glob@npm:7.2.3" @@ -16971,6 +17159,16 @@ __metadata: languageName: node linkType: hard +"graphemesplit@npm:^2.4.1": + version: 2.6.0 + resolution: "graphemesplit@npm:2.6.0" + dependencies: + js-base64: "npm:^3.6.0" + unicode-trie: "npm:^2.0.0" + checksum: 10c0/da90b1ec5dc82f8b70dac251573f44fe341d6c63aa02e2098cbc7678dec450397733de9069746461c97bd686a41775dda2deaed9d26348e5c97e5ebae470116c + languageName: node + linkType: hard + "gzip-size@npm:^6.0.0": version: 6.0.0 resolution: "gzip-size@npm:6.0.0" @@ -17159,6 +17357,15 @@ __metadata: languageName: node linkType: hard +"hasown@npm:^2.0.4": + version: 2.0.4 + resolution: "hasown@npm:2.0.4" + dependencies: + function-bind: "npm:^1.1.2" + checksum: 10c0/2d8de939e270b70618f8cebb69746620db10617dbb495bc66ddad326955ea24d3ca4af133aff3eb7c1853e0218f867bc2b050ec26fe02e3aea58f880ffc5e506 + languageName: node + linkType: hard + "hast-util-from-parse5@npm:^6.0.0": version: 6.0.1 resolution: "hast-util-from-parse5@npm:6.0.1" @@ -17753,7 +17960,7 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.6": +"https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.5, https-proxy-agent@npm:^7.0.6": version: 7.0.6 resolution: "https-proxy-agent@npm:7.0.6" dependencies: @@ -19195,6 +19402,13 @@ __metadata: languageName: node linkType: hard +"js-base64@npm:^3.6.0": + version: 3.9.1 + resolution: "js-base64@npm:3.9.1" + checksum: 10c0/5de18a23c249eac1dbbd78ad893bb36245684d099f4eeb3344734eb7e28b7817a0f5a7bd2a188dcc2d0f90324d20690b54e68edc49bb8087630abb55dbb17dd5 + languageName: node + linkType: hard + "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -19239,6 +19453,40 @@ __metadata: languageName: node linkType: hard +"jsdom@npm:^24.1.1": + version: 24.1.3 + resolution: "jsdom@npm:24.1.3" + dependencies: + cssstyle: "npm:^4.0.1" + data-urls: "npm:^5.0.0" + decimal.js: "npm:^10.4.3" + form-data: "npm:^4.0.0" + html-encoding-sniffer: "npm:^4.0.0" + http-proxy-agent: "npm:^7.0.2" + https-proxy-agent: "npm:^7.0.5" + is-potential-custom-element-name: "npm:^1.0.1" + nwsapi: "npm:^2.2.12" + parse5: "npm:^7.1.2" + rrweb-cssom: "npm:^0.7.1" + saxes: "npm:^6.0.0" + symbol-tree: "npm:^3.2.4" + tough-cookie: "npm:^4.1.4" + w3c-xmlserializer: "npm:^5.0.0" + webidl-conversions: "npm:^7.0.0" + whatwg-encoding: "npm:^3.1.1" + whatwg-mimetype: "npm:^4.0.0" + whatwg-url: "npm:^14.0.0" + ws: "npm:^8.18.0" + xml-name-validator: "npm:^5.0.0" + peerDependencies: + canvas: ^2.11.2 + peerDependenciesMeta: + canvas: + optional: true + checksum: 10c0/e48b342afacd7418a23dac204a62deea729c50f4d072a7c04c09fd32355fdb4335f8779fa79fd0277a2dbeb2d356250a950955719d00047324b251233b11277f + languageName: node + linkType: hard + "jsdom@npm:^26.0.0": version: 26.0.0 resolution: "jsdom@npm:26.0.0" @@ -20027,6 +20275,13 @@ __metadata: languageName: node linkType: hard +"lodash@npm:4.18.1, lodash@npm:^4.17.23": + version: 4.18.1 + resolution: "lodash@npm:4.18.1" + checksum: 10c0/757228fc68805c59789e82185135cf85f05d0b2d3d54631d680ca79ec21944ec8314d4533639a14b8bcfbd97a517e78960933041a5af17ecb693ec6eecb99a27 + languageName: node + linkType: hard + "lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.21": version: 4.17.23 resolution: "lodash@npm:4.17.23" @@ -20034,13 +20289,6 @@ __metadata: languageName: node linkType: hard -"lodash@npm:^4.17.23": - version: 4.18.1 - resolution: "lodash@npm:4.18.1" - checksum: 10c0/757228fc68805c59789e82185135cf85f05d0b2d3d54631d680ca79ec21944ec8314d4533639a14b8bcfbd97a517e78960933041a5af17ecb693ec6eecb99a27 - languageName: node - linkType: hard - "log-symbols@npm:4.1.0, log-symbols@npm:^4.0.0, log-symbols@npm:^4.1.0": version: 4.1.0 resolution: "log-symbols@npm:4.1.0" @@ -21245,7 +21493,7 @@ __metadata: languageName: node linkType: hard -"mime-types@npm:^2.1.12, mime-types@npm:^2.1.27, mime-types@npm:~2.1.17, mime-types@npm:~2.1.19, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": +"mime-types@npm:^2.1.12, mime-types@npm:^2.1.27, mime-types@npm:^2.1.35, mime-types@npm:~2.1.17, mime-types@npm:~2.1.19, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": version: 2.1.35 resolution: "mime-types@npm:2.1.35" dependencies: @@ -21378,6 +21626,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^10.0.1, minimatch@npm:^10.1.1": + version: 10.2.5 + resolution: "minimatch@npm:10.2.5" + dependencies: + brace-expansion: "npm:^5.0.5" + checksum: 10c0/6bb058bd6324104b9ec2f763476a35386d05079c1f5fe4fbf1f324a25237cd4534d6813ecd71f48208f4e635c1221899bef94c3c89f7df55698fe373aaae20fd + languageName: node + linkType: hard + "minimatch@npm:^10.0.3": version: 10.1.1 resolution: "minimatch@npm:10.1.1" @@ -21423,6 +21680,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^9.0.3": + version: 9.0.9 + resolution: "minimatch@npm:9.0.9" + dependencies: + brace-expansion: "npm:^2.0.2" + checksum: 10c0/0b6a58530dbb00361745aa6c8cffaba4c90f551afe7c734830bd95fd88ebf469dd7355a027824ea1d09e37181cfeb0a797fb17df60c15ac174303ac110eb7e86 + languageName: node + linkType: hard + "minimatch@npm:^9.0.4, minimatch@npm:^9.0.5": version: 9.0.5 resolution: "minimatch@npm:9.0.5" @@ -22297,6 +22563,13 @@ __metadata: languageName: node linkType: hard +"nwsapi@npm:^2.2.12": + version: 2.2.24 + resolution: "nwsapi@npm:2.2.24" + checksum: 10c0/9bc04ee9c7698f1b5506778d36f7382962f71667205d441d6a50f6180ee92328e770b76be78b907817ee103241b29984d3a17ae387e4723aebe0aeaed7a7c3a1 + languageName: node + linkType: hard + "nwsapi@npm:^2.2.16": version: 2.2.16 resolution: "nwsapi@npm:2.2.16" @@ -23015,6 +23288,13 @@ __metadata: languageName: node linkType: hard +"pako@npm:^0.2.5": + version: 0.2.9 + resolution: "pako@npm:0.2.9" + checksum: 10c0/79c1806ebcf325b60ae599e4d7227c2e346d7b829dc20f5cf24cef07c934079dc3a61c5b3c8278a2f7a190c4a613e343ea11e5302dbe252efd11712df4b6b041 + languageName: node + linkType: hard + "pako@npm:^2.1.0": version: 2.1.0 resolution: "pako@npm:2.1.0" @@ -23157,6 +23437,15 @@ __metadata: languageName: node linkType: hard +"parse5@npm:^7.1.2": + version: 7.3.0 + resolution: "parse5@npm:7.3.0" + dependencies: + entities: "npm:^6.0.0" + checksum: 10c0/7fd2e4e247e85241d6f2a464d0085eed599a26d7b0a5233790c49f53473232eb85350e8133344d9b3fd58b89339e7ad7270fe1f89d28abe50674ec97b87f80b5 + languageName: node + linkType: hard + "parse5@npm:^7.2.1": version: 7.2.1 resolution: "parse5@npm:7.2.1" @@ -23183,6 +23472,13 @@ __metadata: languageName: node linkType: hard +"path-browserify@npm:^1.0.1": + version: 1.0.1 + resolution: "path-browserify@npm:1.0.1" + checksum: 10c0/8b8c3fd5c66bd340272180590ae4ff139769e9ab79522e2eb82e3d571a89b8117c04147f65ad066dccfb42fcad902e5b7d794b3d35e0fd840491a8ddbedf8c66 + languageName: node + linkType: hard + "path-exists@npm:^2.0.0": version: 2.1.0 resolution: "path-exists@npm:2.1.0" @@ -24482,7 +24778,7 @@ __metadata: languageName: node linkType: hard -"prettier@npm:^2.7.1, prettier@npm:^2.8.0": +"prettier@npm:^2.7.1, prettier@npm:^2.8.0, prettier@npm:^2.8.8": version: 2.8.8 resolution: "prettier@npm:2.8.8" bin: @@ -26160,6 +26456,13 @@ __metadata: languageName: unknown linkType: soft +"rrweb-cssom@npm:^0.7.1": + version: 0.7.1 + resolution: "rrweb-cssom@npm:0.7.1" + checksum: 10c0/127b8ca6c8aac45e2755abbae6138d4a813b1bedc2caabf79466ae83ab3cfc84b5bfab513b7033f0aa4561c7753edf787d0dd01163ceacdee2e8eb1b6bf7237e + languageName: node + linkType: hard + "rrweb-cssom@npm:^0.8.0": version: 0.8.0 resolution: "rrweb-cssom@npm:0.8.0" @@ -27327,7 +27630,7 @@ __metadata: languageName: node linkType: hard -"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.0.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.2, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3" dependencies: @@ -27970,6 +28273,13 @@ __metadata: languageName: node linkType: hard +"tiny-inflate@npm:^1.0.0": + version: 1.0.3 + resolution: "tiny-inflate@npm:1.0.3" + checksum: 10c0/fab687537254f6ec44c9a2e880048fe70da3542aba28f73cda3e74c95cabf342a339372f2a6c032e322324f01accc03ca26c04ba2bad9b3eb8cf3ee99bba7f9b + languageName: node + linkType: hard + "tiny-invariant@npm:^1.0.2, tiny-invariant@npm:^1.3.3": version: 1.3.3 resolution: "tiny-invariant@npm:1.3.3" @@ -28150,7 +28460,7 @@ __metadata: languageName: node linkType: hard -"tough-cookie@npm:^4.1.3": +"tough-cookie@npm:^4.1.3, tough-cookie@npm:^4.1.4": version: 4.1.4 resolution: "tough-cookie@npm:4.1.4" dependencies: @@ -28256,6 +28566,16 @@ __metadata: languageName: node linkType: hard +"ts-morph@npm:^27.0.0": + version: 27.0.2 + resolution: "ts-morph@npm:27.0.2" + dependencies: + "@ts-morph/common": "npm:~0.28.1" + code-block-writer: "npm:^13.0.3" + checksum: 10c0/224715cc6d97b8ff5afd3986f9629f912a0ebd83eaecbdca91c35cf10a98f607c663f666e7ea5e6afab00563d00dc80fa7a13552cc7f1cef735261c3217d0863 + languageName: node + linkType: hard + "ts-node@npm:^10.8.1": version: 10.9.1 resolution: "ts-node@npm:10.9.1" @@ -28661,6 +28981,16 @@ __metadata: languageName: node linkType: hard +"typescript@npm:6.0.3": + version: 6.0.3 + resolution: "typescript@npm:6.0.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/4a25ff5045b984370f48f196b3a0120779b1b343d40b9a68d114ea5e5fff099809b2bb777576991a63a5cd59cf7bffd96ff6fe10afcefbcb8bd6fb96ad4b6606 + languageName: node + linkType: hard + "typescript@npm:>=3 < 6": version: 5.5.2 resolution: "typescript@npm:5.5.2" @@ -28701,6 +29031,16 @@ __metadata: languageName: node linkType: hard +"typescript@patch:typescript@npm%3A6.0.3#optional!builtin": + version: 6.0.3 + resolution: "typescript@patch:typescript@npm%3A6.0.3#optional!builtin::version=6.0.3&hash=5786d5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/2f25c74e65663c248fa1ade2b8459d9ce5372ff9dad07067310f132966ebec1d93f6c42f0baf77a6b6a7a91460463f708e6887013aaade22111037457c6b25df + languageName: node + linkType: hard + "typescript@patch:typescript@npm%3A>=3 < 6#optional!builtin": version: 5.5.2 resolution: "typescript@patch:typescript@npm%3A5.5.2#optional!builtin::version=5.5.2&hash=379a07" @@ -28798,6 +29138,13 @@ __metadata: languageName: node linkType: hard +"undici@npm:^7.19.1": + version: 7.28.0 + resolution: "undici@npm:7.28.0" + checksum: 10c0/fe781983a26098795e99bb1f64906cbb7d0bcaa029a26baade007b53ea67f2631d189b8f9671a31f4c8d0cb3773b7559608628ba54452fef51fec90e7c78bb0d + languageName: node + linkType: hard + "unicode-canonical-property-names-ecmascript@npm:^2.0.0": version: 2.0.0 resolution: "unicode-canonical-property-names-ecmascript@npm:2.0.0" @@ -28843,6 +29190,16 @@ __metadata: languageName: node linkType: hard +"unicode-trie@npm:^2.0.0": + version: 2.0.0 + resolution: "unicode-trie@npm:2.0.0" + dependencies: + pako: "npm:^0.2.5" + tiny-inflate: "npm:^1.0.0" + checksum: 10c0/2422368645249f315640a1c9e9506046aa7738fc9c5d59e15c207cdd6ec66101c35b0b9f75dc3ac28fe7be19aaf1efc898bbea074fa1e8e295ef736aeb7904bb + languageName: node + linkType: hard + "unicorn-magic@npm:^0.1.0": version: 0.1.0 resolution: "unicorn-magic@npm:0.1.0" @@ -30329,6 +30686,15 @@ __metadata: languageName: node linkType: hard +"widest-line@npm:^3.1.0": + version: 3.1.0 + resolution: "widest-line@npm:3.1.0" + dependencies: + string-width: "npm:^4.0.0" + checksum: 10c0/b1e623adcfb9df35350dd7fc61295d6d4a1eaa65a406ba39c4b8360045b614af95ad10e05abf704936ed022569be438c4bfa02d6d031863c4166a238c301119f + languageName: node + linkType: hard + "widest-line@npm:^4.0.1": version: 4.0.1 resolution: "widest-line@npm:4.0.1" @@ -30844,6 +31210,22 @@ __metadata: languageName: node linkType: hard +"zod-validation-error@npm:^3.2.0": + version: 3.5.4 + resolution: "zod-validation-error@npm:3.5.4" + peerDependencies: + zod: ^3.24.4 + checksum: 10c0/fccfe09fc27d4d6ba59beab8eeee06a27befc0f491ec81a2951d7b82e7a08eca07bc74ef4fff82586fc7710a3ef777ec607c685defa06c70cd11849af0b9882c + languageName: node + linkType: hard + +"zod@npm:3.25.58": + version: 3.25.58 + resolution: "zod@npm:3.25.58" + checksum: 10c0/3525e9104adcb84b48ff2c166a44175e2f4fd502bcab045bde7cb898097a3ff0cc8b03469d949f1c14455266fd9bedeea0b411ec624b42827db48ad942ffb3b1 + languageName: node + linkType: hard + "zwitch@npm:^1.0.0": version: 1.0.5 resolution: "zwitch@npm:1.0.5" From 8794221cd2cee78052b7849d5e0824be0ce47eb8 Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Mon, 10 Aug 2026 15:48:28 +0300 Subject: [PATCH 02/28] chore: figma connect 7 more components + MessageStrip colorScheme - Input, CheckBox, RadioButton, StepInput, MessageStrip, Select, SegmentedButton (Web Components + React variants) - MessageStrip: full ColorSet1/2 + color-scheme mapping from the single Figma Color axis (Indication 1..10 / 1b..10b) - concise bulleted FIGMA_CODE_CONNECT.md; drop exhaustive API map - Icon kept as .todo (unpublishable: node is a frame, not a component set) --- packages/main/FIGMA_CODE_CONNECT.md | 171 +++++++++----------- packages/main/src/CheckBox.figma.ts | 49 ++++++ packages/main/src/CheckBox.figma.tsx | 62 +++++++ packages/main/src/Icon.figma.ts.todo | 23 +++ packages/main/src/Input.figma.ts | 44 +++++ packages/main/src/Input.figma.tsx | 48 ++++++ packages/main/src/MessageStrip.figma.ts | 69 ++++++++ packages/main/src/MessageStrip.figma.tsx | 69 ++++++++ packages/main/src/RadioButton.figma.ts | 44 +++++ packages/main/src/RadioButton.figma.tsx | 53 ++++++ packages/main/src/SegmentedButton.figma.ts | 37 +++++ packages/main/src/SegmentedButton.figma.tsx | 37 +++++ packages/main/src/Select.figma.ts | 24 +++ packages/main/src/Select.figma.tsx | 22 +++ packages/main/src/StepInput.figma.ts | 36 +++++ packages/main/src/StepInput.figma.tsx | 46 ++++++ 16 files changed, 742 insertions(+), 92 deletions(-) create mode 100644 packages/main/src/CheckBox.figma.ts create mode 100644 packages/main/src/CheckBox.figma.tsx create mode 100644 packages/main/src/Icon.figma.ts.todo create mode 100644 packages/main/src/Input.figma.ts create mode 100644 packages/main/src/Input.figma.tsx create mode 100644 packages/main/src/MessageStrip.figma.ts create mode 100644 packages/main/src/MessageStrip.figma.tsx create mode 100644 packages/main/src/RadioButton.figma.ts create mode 100644 packages/main/src/RadioButton.figma.tsx create mode 100644 packages/main/src/SegmentedButton.figma.ts create mode 100644 packages/main/src/SegmentedButton.figma.tsx create mode 100644 packages/main/src/Select.figma.ts create mode 100644 packages/main/src/Select.figma.tsx create mode 100644 packages/main/src/StepInput.figma.ts create mode 100644 packages/main/src/StepInput.figma.tsx diff --git a/packages/main/FIGMA_CODE_CONNECT.md b/packages/main/FIGMA_CODE_CONNECT.md index 1d34691f51bc1..a036e2f16b060 100644 --- a/packages/main/FIGMA_CODE_CONNECT.md +++ b/packages/main/FIGMA_CODE_CONNECT.md @@ -1,123 +1,110 @@ -# Figma Code Connect — Button: known limitations & owner actions +# Figma Code Connect — what maps, what doesn't -This documents the **Web Components** Code Connect mapping for the SAP Web UI Kit -`Button` (Figma node `91702:11733` in file `SILcWzK5uFghKun9jx6D7c`), what works, -what does **not** update dynamically when you select different button variants in -Dev Mode, **why**, and **what the Figma file owner must change** to fix it. +Code Connect mappings for the **SAP Web UI Kit** (file `SILcWzK5uFghKun9jx6D7c`), +published in two variants: **Web Components** (`src/*.figma.ts`) and **React** +(`src/*.figma.tsx`). This file is the short record of, per component, what maps +dynamically in Dev Mode and what doesn't (with the reason + fix). -Mapping file: `packages/main/src/Button.figma.ts` -Config: `packages/main/figma.config.json` (`parser: "html"`, `label: "Web Components"`) - -The goal is a snippet with **no hardcoded values** — selecting a different button -(different icon, different badge) in Dev Mode should be reflected in the generated -`` code. Today, two things block that. Both are **Figma-side modeling -gaps**, not code bugs: Code Connect can only surface values that the Figma -component actually exposes as readable properties. +**Two axes ignored on every component (by decision):** +- **Form Factor (Compact/Cozy)** — density is a global UI5 setting, not a per-element attribute. +- **Interaction State = Hover/Active/Down/Focus** — visual pseudo-states, no attribute. Only `Disabled`→`disabled` and `Read Only`→`readonly` map. --- -## ✅ What already works (dynamic) +## Button — ui5-button (node 91702:11733) +**works:** partly — `design`, `disabled`, and label text map dynamically. -These reflect the selected variant correctly in Dev Mode: +**doesn't map:** +- `icon` — Figma instance-swap; the swapped icon's name isn't readable, so hardcoded `icon="globe"`. +- badge — modeled as 2 booleans with no design enum, and the count sits in a nested layer, so `design`/`text` are hardcoded. -| Snippet output | Driven by Figma property | Notes | -|---|---|---| -| `design="…"` | `Type` variant | Primary→Emphasized, Secondary→Default, Tertiary→Transparent, Accept→Positive, Reject→Negative, Attention→Attention | -| `disabled` | `Interaction State` variant | emitted only for `Disabled` | -| button label text | `Text` layer (`figma.textContent`) | reads the actual layer string | +**how to fix:** +- owner adds an `Icon Name` string prop (or Code-Connect the icon set). +- add a single `Badge Design` enum (None/InlineText/OverlayText/AttentionDot) + a `Badge Text` string prop. ---- +## Input — ui5-input (node 148569:1004) +**works:** yes — `value`, `placeholder`, `value-state` (1:1), `disabled`/`readonly` all map. -## ❌ Problem 1 — Icon is not dynamic +**doesn't map:** +- `Content` (Placeholder vs Typed Text) is a display toggle, so both attrs are always emitted. +- `Trailing Action` / `2nd Action` / `Message Popover` are slotted icon/action/message content with no readable value. +- `Description Text` has no `ui5-input` equivalent. -**Symptom:** changing the icon on a button in Figma does not change the code; the -snippet always shows `icon="globe"`. +**how to fix:** +- cosmetic ones need nothing (consumer deletes the extra attr). +- for the actions, owner exposes the action icon as a prop or Code-Connect the icon set. -**Why:** the icon is a Figma **instance-swap** property (`Icon`, type -`INSTANCE_SWAP`). Code Connect can read a *boolean* (`Icon Left` — is an icon -shown or not), but it **cannot read the swapped icon's name into a string** -unless the icon components in the Kit's icon library are themselves -Code-Connected to output their name. They are not. So the mapping can only toggle -the attribute on/off and must hardcode a placeholder name. +## CheckBox — ui5-checkbox (node 154589:905) +**works:** yes — `text`, `checked`/`indeterminate`, `value-state`, `disabled`/`readonly` all map. -**Important context:** in code there is no per-icon *component* to import. A UI5 -icon is just a **name string** in a registry (`icon="globe"`), resolved to an SVG -at runtime. That shapes who owns each fix below. +**doesn't map:** +- `Interaction State = Display Only` — no display-only mode in the WC, approximated as `readonly`. +- Tristate can't express both indeterminate + checked at once. -There are two ways to fix this — one is the Figma owner's, one is ours: +**how to fix:** +- acceptable approximations; no owner action needed. -### Option A — `Icon Name` text property → **Figma owner's job** -Add a plain **text property** on the Button component (e.g. `Icon Name`) holding -the icon's registry name. The mapping reads it with `figma.string("Icon Name")` -and emits `icon="…"` dynamically. Pure Figma-side change, no icon-library work. -Trade-off: it's duplicated data — the designer must keep the typed name in sync -with the icon actually shown, so it can drift. +## RadioButton — ui5-radio-button (node 154597:1967) +**works:** yes — fully (cleanest component): `text`, `checked`, `value-state`, `disabled`/`readonly`. -### Option B — Code Connect the icon set → **webcomponents / tooling team's job** -"Code-Connecting the icon library" does **not** mean mapping icons to -components — there aren't any. It means authoring one tiny Code Connect entry -**per icon** whose only output is its own registry name, so that -`figma.instance("Icon")` on the Button can pull the *selected* icon's name into -the snippet. The Kit already names its icon instances by icon -(`mainComponentName: "globe"`), so the correspondence exists; the work is -**generating** those hundreds of entries (not hand-writing them). Correct -long-term, zero designer effort, no drift. The owner's only responsibility here -is to keep the icons as real, connectable components in the Kit. +**doesn't map:** +- nothing significant — `name`/`value` (form grouping) aren't modeled in Figma, which is expected (app-level, not visual). -**Recommendation:** Option A for a quick owner-only fix now; Option B (generated) -as the durable solution owned by the webcomponents team. +**how to fix:** +- n/a. -Until either lands, `icon="globe"` is a placeholder the consumer edits by hand. +## StepInput — ui5-step-input (node 148569:1727) +**works:** yes — `value`, `value-state`, `disabled`/`readonly` map. ---- +**doesn't map:** +- `min`/`max`/`step` aren't in Figma (behavioral, not visual). +- +/- button icons are instance-swaps. +- `Message Popover` is slotted nested text. -## ❌ Problem 2 — Badge design & text are not dynamic +**how to fix:** +- min/max/step are an expected gap. +- icons need the icon-set fix (see Button). -**Symptom:** clicking buttons with different badges doesn't change the code; the -counter badge always renders `design="OverlayText" text="1"`, and there's no way -to get `design="InlineText"`. +## MessageStrip — ui5-message-strip (node 910:2517) +**works:** yes (WC) — `design` ← Value State (semantic 1:1), `hide-icon`, `hide-close-button`, and the full custom-colour palette: the single `Color` axis (Indication 1..10 / 1b..10b) maps to `design="ColorSet1|ColorSet2" color-scheme="1".."10"` because the `b` suffix already encodes ColorSet2 on the same axis as the scheme number. -**Why:** the `ui5-button-badge` web component supports **three** designs — -`InlineText`, `OverlayText`, `AttentionDot`. But in Figma the badge is modeled as -**two independent booleans**: +**doesn't map:** +- message text is default-slot content (placeholder). +- React variant reaches ColorSet1 + `color-scheme` only — its parser can't merge two axes into one `design`, so ColorSet2 (the "…b" colours) is unreachable there. -- `Counter Badge` (True/False) -- `Attention Badge` (True/False) +**how to fix:** +- none for WC. +- for React parity, owner splits Figma `Color` into a ColorSet enum + a scheme number so each maps to one prop. -There is **no badge-`design` enum** to read, so the mapping cannot distinguish -`OverlayText` from `InlineText` — it can only detect *counter badge present*. -Likewise the counter **number** lives in a nested, unexposed text layer -(`Counter Badge` instance → child text `"72"`), so the `text="…"` value can't be -read either. Both are therefore hardcoded. +## Select — ui5-select (node 181557:7507) +**works:** almost nothing — the Figma Select has no Value State / Interaction State axes to map. -**What the Figma owner should do:** +**doesn't map:** +- options are slotted `ui5-option`s (Figma models a closed Input with no option list). +- `Drop-Down` True/False is a runtime open state, not a prop. +- `value-state`/`disabled`/`readonly` are supported by the WC but absent from the Figma component. -1. **Replace the two badge booleans with a single badge `design` variant/enum** - on the Button component, with options that map 1:1 to the web component: - `None | InlineText | OverlayText | AttentionDot`. The mapping can then use - `figma.enum("Badge Design", { … })` to emit the correct `design="…"` (and omit - the badge entirely for `None`). -2. **Expose the badge text as a component text property** (e.g. a `Badge Text` - string prop) instead of a buried nested layer, so the mapping can read it with - `figma.string("Badge Text")` and emit `text="…"` dynamically. `AttentionDot` - has no text and should omit it. +**how to fix:** +- owner adds Value State + Interaction State variants (like Input/CheckBox) and models options as a proper slot/list. -Until then, the badge snippet is fixed at `OverlayText` / `text="1"` for the -counter and `AttentionDot` for the attention badge. +## SegmentedButton — ui5-segmented-button (node 91702:11986) +**works:** partly — presence of the 3rd/4th/5th segments maps (adds/removes items). ---- +**doesn't map:** +- segment labels/icons live in Figma slots (`⿻ Text/Icon Segments`), not readable, so labels are placeholders. +- `Type = Text/Icon` adds nothing without readable content. +- the selected segment isn't a readable prop (first item marked `selected`). -## Summary for the owner +**how to fix:** +- owner exposes per-segment text as component text props. -To make the Button snippet fully dynamic (no hardcoded values), the Figma -component needs: +## Icon — ui5-icon (node 983:5876) — UNPUBLISHABLE +**works:** no — Figma rejects the publish: "corresponding node is not a component or component set". -1. **Icon:** either Code-Connect the icon library components, or add an - `Icon Name` text property on Button. -2. **Badge design:** a single `Badge Design` enum (`None/InlineText/OverlayText/ - AttentionDot`) replacing the two booleans. -3. **Badge text:** a `Badge Text` string property replacing the nested text layer. +**reason:** +- node `983:5876` is a plain frame of ~1400 individual icon components, not a component/set. +- kept as `src/Icon.figma.ts.todo` (outside the publish glob) so it doesn't break the atomic batch publish. -Once (1)–(3) exist, update `packages/main/src/Button.figma.ts` to swap the -hardcoded strings for `figma.instance`/`figma.enum`/`figma.string` reads and -re-publish. +**how to fix:** +- owner makes it a real component set. +- even then the icon name isn't readable from a single mapping — generate one `figma.connect` per icon emitting its own name, or expose an `Icon Name` string prop on the host component. diff --git a/packages/main/src/CheckBox.figma.ts b/packages/main/src/CheckBox.figma.ts new file mode 100644 index 0000000000000..a152389d6817c --- /dev/null +++ b/packages/main/src/CheckBox.figma.ts @@ -0,0 +1,49 @@ +/** + * Web Components Code Connect mapping for the SAP Web UI Kit "Check Box". + * Node: 154589:905. Emits . + * + * Every readable Figma prop mapped; unmappable ones documented in + * FIGMA_CODE_CONNECT.md § CheckBox. Form Factor + Hover ignored. + */ +import figma, { html } from "@figma/code-connect/html"; + +figma.connect( + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=154589-905", + { + props: { + valueState: figma.enum("Value State", { + None: "", + Negative: 'value-state="Negative"', + Critical: 'value-state="Critical"', + Positive: 'value-state="Positive"', + Information: 'value-state="Information"', + }), + // Check → checked / indeterminate (Tristate = indeterminate). + checkAttr: figma.enum("Check", { + Checked: "checked", + Tristate: "indeterminate", + Unchecked: "", + }), + // Interaction State → disabled / readonly. + // "Display Only" has NO ui5-checkbox equivalent — approximated as readonly + // (documented in FIGMA_CODE_CONNECT.md § CheckBox). + stateAttr: figma.enum("Interaction State", { + Disabled: "disabled", + "Read Only": "readonly", + "Display Only": "readonly", + Regular: "", + Hover: "", + }), + // Label switch gates the text: OFF → empty; ON → the typed ✏️ Text value. + // NOTE: the nested figma.string must be resolved into a PROP (below) and + // referenced as a plain ${text} placeholder — a figma.* call cannot live + // inside an html`` template literal (it would be emitted verbatim). + text: figma.boolean("Label", { + true: figma.string("✏️ Text"), + false: "", + }), + }, + example: ({ valueState, checkAttr, stateAttr, text }) => + html``, + } +); diff --git a/packages/main/src/CheckBox.figma.tsx b/packages/main/src/CheckBox.figma.tsx new file mode 100644 index 0000000000000..78f6a7a3ad4a0 --- /dev/null +++ b/packages/main/src/CheckBox.figma.tsx @@ -0,0 +1,62 @@ +/** + * React Code Connect mapping for the SAP Web UI Kit "Check Box". Node 154589:905. + * Mirrors CheckBox.figma.ts under the "React" label. See FIGMA_CODE_CONNECT.md. + */ +import figma from "@figma/code-connect/react"; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { CheckBox } from "@ui5/webcomponents-react"; + +figma.connect( + CheckBox, + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=154589-905", + { + props: { + valueState: figma.enum("Value State", { + None: undefined, + Negative: "Negative", + Critical: "Critical", + Positive: "Positive", + Information: "Information", + }), + checked: figma.enum("Check", { + Checked: true, + Tristate: false, + Unchecked: false, + }), + indeterminate: figma.enum("Check", { + Tristate: true, + Checked: false, + Unchecked: false, + }), + disabled: figma.enum("Interaction State", { + Disabled: true, + Regular: false, + Hover: false, + "Read Only": false, + "Display Only": false, + }), + readonly: figma.enum("Interaction State", { + "Read Only": true, + "Display Only": true, + Regular: false, + Hover: false, + Disabled: false, + }), + // Label switch gates the text: OFF → text undefined; ON → typed ✏️ Text. + text: figma.boolean("Label", { + true: figma.string("✏️ Text"), + false: undefined, + }), + }, + example: ({ text, checked, indeterminate, valueState, disabled, readonly }) => ( + + ), + } +); diff --git a/packages/main/src/Icon.figma.ts.todo b/packages/main/src/Icon.figma.ts.todo new file mode 100644 index 0000000000000..72d935b104ddc --- /dev/null +++ b/packages/main/src/Icon.figma.ts.todo @@ -0,0 +1,23 @@ +/** + * Web Components Code Connect mapping for the SAP Web UI Kit icons. + * Node: 983:5876 (the "SAP Icons" frame). Emits . + * + * ⚠️ MAJOR LIMITATION: this frame contains ~1400 INDIVIDUAL icon components, + * one per icon (accept, add, employee, …). Code Connect connects a NODE to a + * snippet — it cannot read "which icon" into the `name` attribute from a single + * mapping. To make the icon name dynamic, EACH icon component must get its own + * figma.connect emitting its own name (best generated, ~1400 entries). + * This single mapping is a placeholder for the frame. See FIGMA_CODE_CONNECT.md + * § "Icon is not dynamic" — this is the same registry/instance-swap problem as + * the Button icon. + */ +import figma, { html } from "@figma/code-connect/html"; + +figma.connect( + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=983-5876", + { + props: {}, + // Placeholder name — cannot reflect the selected icon (see limitation). + example: () => html``, + } +); diff --git a/packages/main/src/Input.figma.ts b/packages/main/src/Input.figma.ts new file mode 100644 index 0000000000000..94619d124d0a7 --- /dev/null +++ b/packages/main/src/Input.figma.ts @@ -0,0 +1,44 @@ +/** + * Web Components Code Connect mapping for the SAP Web UI Kit "Input". + * Node: 148569:1004. Emits under the "Web Components" label. + * + * Every READABLE Figma property is mapped dynamically below. Properties that + * cannot be made dynamic are listed in FIGMA_CODE_CONNECT.md § Input with the + * reason. Form Factor (Compact/Cozy) and the Hover/Active visual states are + * intentionally ignored (density is global in UI5; pseudo-states have no attr). + */ +import figma, { html } from "@figma/code-connect/html"; + +figma.connect( + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=148569-1004", + { + props: { + // Value State → value-state (1:1). + valueState: figma.enum("Value State", { + None: "", + Negative: 'value-state="Negative"', + Critical: 'value-state="Critical"', + Positive: 'value-state="Positive"', + Information: 'value-state="Information"', + }), + // Interaction State → disabled / readonly (Regular/Hover/Active ignored). + stateAttr: figma.enum("Interaction State", { + Disabled: "disabled", + "Read Only": "readonly", + Regular: "", + Hover: "", + Active: "", + }), + // Text properties. + placeholder: figma.string("✏️ Placeholder"), + value: figma.string("✏️ Typed Text"), + }, + // CANNOT MAP (see FIGMA_CODE_CONNECT.md § Input): the `Content` variant + // (Placeholder vs Typed Text) can't pick which text prop to emit, so both + // are emitted; `Trailing Action`, `2nd Action`, `Message Popover` and + // `Description Text` booleans reference slotted content with no readable + // value. + example: ({ valueState, stateAttr, placeholder, value }) => + html``, + } +); diff --git a/packages/main/src/Input.figma.tsx b/packages/main/src/Input.figma.tsx new file mode 100644 index 0000000000000..6aeea6d007322 --- /dev/null +++ b/packages/main/src/Input.figma.tsx @@ -0,0 +1,48 @@ +/** + * React Code Connect mapping for the SAP Web UI Kit "Input". Node 148569:1004. + * Mirrors Input.figma.ts under the "React" label. See FIGMA_CODE_CONNECT.md. + */ +import figma from "@figma/code-connect/react"; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { Input } from "@ui5/webcomponents-react"; + +figma.connect( + Input, + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=148569-1004", + { + props: { + valueState: figma.enum("Value State", { + None: undefined, + Negative: "Negative", + Critical: "Critical", + Positive: "Positive", + Information: "Information", + }), + disabled: figma.enum("Interaction State", { + Disabled: true, + Regular: false, + Hover: false, + Active: false, + "Read Only": false, + }), + readonly: figma.enum("Interaction State", { + "Read Only": true, + Regular: false, + Hover: false, + Active: false, + Disabled: false, + }), + placeholder: figma.string("✏️ Placeholder"), + value: figma.string("✏️ Typed Text"), + }, + example: ({ value, placeholder, valueState, disabled, readonly }) => ( + + ), + } +); diff --git a/packages/main/src/MessageStrip.figma.ts b/packages/main/src/MessageStrip.figma.ts new file mode 100644 index 0000000000000..b01f85309ba85 --- /dev/null +++ b/packages/main/src/MessageStrip.figma.ts @@ -0,0 +1,69 @@ +/** + * Web Components Code Connect mapping for the SAP Web UI Kit "Message Strip". + * Node: 910:2517. Emits . + * + * See FIGMA_CODE_CONNECT.md § MessageStrip. Form Factor ignored. + * + * Design comes from TWO mutually-exclusive Figma axes: + * - "Value State" carries the 4 semantic designs (Information/Positive/ + * Critical/Negative); its "Indication Color" option defers to the Color axis. + * - "Color" carries the 20 custom colours as a single enum: Indication 1..10 → + * design="ColorSet1" color-scheme="1".."10", and 1b..10b → ColorSet2 + scheme. + * When a semantic state is selected, Color is "None" (empty); when a custom + * colour is selected, Value State is "Indication Color" (empty). So the two + * placeholders never both emit a `design`. + */ +import figma, { html } from "@figma/code-connect/html"; + +figma.connect( + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=910-2517", + { + props: { + // Semantic designs. "Indication Color" defers to the Color axis (empty here). + designSemantic: figma.enum("Value State", { + Information: 'design="Information"', + Positive: 'design="Positive"', + Critical: 'design="Critical"', + Negative: 'design="Negative"', + "Indication Color": "", + }), + // Custom colours: ColorSet1/2 + color-scheme 1..10 from a single axis. + designColorSet: figma.enum("Color", { + None: "", + "Indication 1": 'design="ColorSet1" color-scheme="1"', + "Indication 2": 'design="ColorSet1" color-scheme="2"', + "Indication 3": 'design="ColorSet1" color-scheme="3"', + "Indication 4": 'design="ColorSet1" color-scheme="4"', + "Indication 5": 'design="ColorSet1" color-scheme="5"', + "Indication 6": 'design="ColorSet1" color-scheme="6"', + "Indication 7": 'design="ColorSet1" color-scheme="7"', + "Indication 8": 'design="ColorSet1" color-scheme="8"', + "Indication 9": 'design="ColorSet1" color-scheme="9"', + "Indication 10": 'design="ColorSet1" color-scheme="10"', + "Indication 1b": 'design="ColorSet2" color-scheme="1"', + "Indication 2b": 'design="ColorSet2" color-scheme="2"', + "Indication 3b": 'design="ColorSet2" color-scheme="3"', + "Indication 4b": 'design="ColorSet2" color-scheme="4"', + "Indication 5b": 'design="ColorSet2" color-scheme="5"', + "Indication 6b": 'design="ColorSet2" color-scheme="6"', + "Indication 7b": 'design="ColorSet2" color-scheme="7"', + "Indication 8b": 'design="ColorSet2" color-scheme="8"', + "Indication 9b": 'design="ColorSet2" color-scheme="9"', + "Indication 10b": 'design="ColorSet2" color-scheme="10"', + }), + // Icon variant False → hide-icon. + hideIcon: figma.enum("Icon", { + False: "hide-icon", + True: "", + }), + // Close Button boolean False → hide-close-button. + hideClose: figma.boolean("Close Button", { + true: "", + false: "hide-close-button", + }), + }, + // Text is a slotted node (default slot) — placeholder used. + example: ({ designSemantic, designColorSet, hideIcon, hideClose }) => + html`Information message`, + } +); diff --git a/packages/main/src/MessageStrip.figma.tsx b/packages/main/src/MessageStrip.figma.tsx new file mode 100644 index 0000000000000..9f78f074df9d5 --- /dev/null +++ b/packages/main/src/MessageStrip.figma.tsx @@ -0,0 +1,69 @@ +/** + * React Code Connect mapping for the SAP Web UI Kit "Message Strip". + * Node 910:2517. Mirrors MessageStrip.figma.ts. See FIGMA_CODE_CONNECT.md. + * + * NOTE (React vs WC asymmetry): the React parser requires each prop to be a + * single enum ref and cannot merge two axes into one `design` attribute, so + * `design` is driven by "Value State" only (Indication Color → ColorSet1) while + * `colorScheme` is made dynamic from the "Color" axis (1..10). ColorSet2 (the + * "…b" colours) is therefore NOT reachable in the React variant — the WC variant + * (MessageStrip.figma.ts) maps it fully via raw-string template fragments. + */ +import figma from "@figma/code-connect/react"; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { MessageStrip } from "@ui5/webcomponents-react"; + +figma.connect( + MessageStrip, + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=910-2517", + { + props: { + // Value State → design. "Indication Color" → ColorSet1 (ColorSet2 not reachable here). + design: figma.enum("Value State", { + Information: "Information", + Positive: "Positive", + Critical: "Critical", + Negative: "Negative", + "Indication Color": "ColorSet1", + }), + // Color axis → color-scheme "1".."10" (both the base and "…b" rows map to + // the same scheme number; the ColorSet1-vs-2 distinction is lost, see note). + colorScheme: figma.enum("Color", { + None: undefined, + "Indication 1": "1", + "Indication 2": "2", + "Indication 3": "3", + "Indication 4": "4", + "Indication 5": "5", + "Indication 6": "6", + "Indication 7": "7", + "Indication 8": "8", + "Indication 9": "9", + "Indication 10": "10", + "Indication 1b": "1", + "Indication 2b": "2", + "Indication 3b": "3", + "Indication 4b": "4", + "Indication 5b": "5", + "Indication 6b": "6", + "Indication 7b": "7", + "Indication 8b": "8", + "Indication 9b": "9", + "Indication 10b": "10", + }), + hideIcon: figma.enum("Icon", { + False: true, + True: false, + }), + hideCloseButton: figma.boolean("Close Button", { + true: false, + false: true, + }), + }, + example: ({ design, colorScheme, hideIcon, hideCloseButton }) => ( + + Information message + + ), + } +); diff --git a/packages/main/src/RadioButton.figma.ts b/packages/main/src/RadioButton.figma.ts new file mode 100644 index 0000000000000..cc352890c12b3 --- /dev/null +++ b/packages/main/src/RadioButton.figma.ts @@ -0,0 +1,44 @@ +/** + * Web Components Code Connect mapping for the SAP Web UI Kit "Radio Button". + * Node: 154597:1967. Emits . + * + * Cleanest of the set — every Figma axis maps. See FIGMA_CODE_CONNECT.md + * § RadioButton. Form Factor + Hover ignored. + */ +import figma, { html } from "@figma/code-connect/html"; + +figma.connect( + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=154597-1967", + { + props: { + valueState: figma.enum("Value State", { + None: "", + Negative: 'value-state="Negative"', + Critical: 'value-state="Critical"', + Positive: 'value-state="Positive"', + Information: 'value-state="Information"', + }), + // Selected → checked. + checkedAttr: figma.enum("Selected", { + True: "checked", + False: "", + }), + // Interaction State → disabled / readonly. + stateAttr: figma.enum("Interaction State", { + Disabled: "disabled", + "Read Only": "readonly", + Regular: "", + Hover: "", + }), + // Label switch gates the text: OFF → empty; ON → the typed ✏️ Text value. + // Resolved into a PROP and referenced as ${text} — a figma.* call cannot + // live inside the html`` template literal. + text: figma.boolean("Label", { + true: figma.string("✏️ Text"), + false: "", + }), + }, + example: ({ valueState, checkedAttr, stateAttr, text }) => + html``, + } +); diff --git a/packages/main/src/RadioButton.figma.tsx b/packages/main/src/RadioButton.figma.tsx new file mode 100644 index 0000000000000..f74212995d5f3 --- /dev/null +++ b/packages/main/src/RadioButton.figma.tsx @@ -0,0 +1,53 @@ +/** + * React Code Connect mapping for the SAP Web UI Kit "Radio Button". + * Node 154597:1967. Mirrors RadioButton.figma.ts. See FIGMA_CODE_CONNECT.md. + */ +import figma from "@figma/code-connect/react"; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { RadioButton } from "@ui5/webcomponents-react"; + +figma.connect( + RadioButton, + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=154597-1967", + { + props: { + valueState: figma.enum("Value State", { + None: undefined, + Negative: "Negative", + Critical: "Critical", + Positive: "Positive", + Information: "Information", + }), + checked: figma.enum("Selected", { + True: true, + False: false, + }), + disabled: figma.enum("Interaction State", { + Disabled: true, + Regular: false, + Hover: false, + "Read Only": false, + }), + readonly: figma.enum("Interaction State", { + "Read Only": true, + Regular: false, + Hover: false, + Disabled: false, + }), + // Label switch gates the text: OFF → text undefined; ON → typed ✏️ Text. + text: figma.boolean("Label", { + true: figma.string("✏️ Text"), + false: undefined, + }), + }, + example: ({ text, checked, valueState, disabled, readonly }) => ( + + ), + } +); diff --git a/packages/main/src/SegmentedButton.figma.ts b/packages/main/src/SegmentedButton.figma.ts new file mode 100644 index 0000000000000..4b9d293bf394e --- /dev/null +++ b/packages/main/src/SegmentedButton.figma.ts @@ -0,0 +1,37 @@ +/** + * Web Components Code Connect mapping for the SAP Web UI Kit "Segmented Button". + * Node: 91702:11986. Emits with slotted items. + * + * LIMITATION: the segment items live in Figma SLOTS (2 fixed + 3rd/4th/5th + * booleans). Their labels/icons are not exposed as readable properties, so the + * emitted items are placeholders. `Type` (Text vs Icon) selects text-vs-icon + * item shape. See FIGMA_CODE_CONNECT.md. + */ +import figma, { html } from "@figma/code-connect/html"; + +figma.connect( + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=91702-11986", + { + props: { + // 3rd/4th/5th optional segments → extra placeholder items. + thirdItem: figma.boolean("3rd Button", { + true: html`Option 3`, + false: "", + }), + fourthItem: figma.boolean("4th Button", { + true: html`Option 4`, + false: "", + }), + fifthItem: figma.boolean("5th Button", { + true: html`Option 5`, + false: "", + }), + }, + example: ({ thirdItem, fourthItem, fifthItem }) => + html` + Option 1 + Option 2 + ${thirdItem}${fourthItem}${fifthItem} +`, + } +); diff --git a/packages/main/src/SegmentedButton.figma.tsx b/packages/main/src/SegmentedButton.figma.tsx new file mode 100644 index 0000000000000..973495c49a92c --- /dev/null +++ b/packages/main/src/SegmentedButton.figma.tsx @@ -0,0 +1,37 @@ +/** + * React Code Connect mapping for the SAP Web UI Kit "Segmented Button". + * Node 91702:11986. Mirrors SegmentedButton.figma.ts. See FIGMA_CODE_CONNECT.md. + */ +import figma from "@figma/code-connect/react"; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { SegmentedButton, SegmentedButtonItem } from "@ui5/webcomponents-react"; + +figma.connect( + SegmentedButton, + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=91702-11986", + { + props: { + thirdItem: figma.boolean("3rd Button", { + true: Option 3, + false: undefined, + }), + fourthItem: figma.boolean("4th Button", { + true: Option 4, + false: undefined, + }), + fifthItem: figma.boolean("5th Button", { + true: Option 5, + false: undefined, + }), + }, + example: ({ thirdItem, fourthItem, fifthItem }) => ( + + Option 1 + Option 2 + {thirdItem} + {fourthItem} + {fifthItem} + + ), + } +); diff --git a/packages/main/src/Select.figma.ts b/packages/main/src/Select.figma.ts new file mode 100644 index 0000000000000..4eedfd704a98b --- /dev/null +++ b/packages/main/src/Select.figma.ts @@ -0,0 +1,24 @@ +/** + * Web Components Code Connect mapping for the SAP Web UI Kit "Select". + * Node: 181557:7507. Emits . + * + * ⚠️ LARGELY UNMAPPABLE — see FIGMA_CODE_CONNECT.md § Select. The only Figma + * axes are `Form Factor` (global density, ignored) and `Drop-Down` (open/closed + * popover — a runtime visual state, NOT a component prop). The options are a + * slotted Input instance with no readable option list. So there is nothing + * dynamic to map; this emits a representative static example. + */ +import figma, { html } from "@figma/code-connect/html"; + +figma.connect( + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=181557-7507", + { + props: {}, + example: () => + html` + Option 1 + Option 2 + Option 3 +`, + } +); diff --git a/packages/main/src/Select.figma.tsx b/packages/main/src/Select.figma.tsx new file mode 100644 index 0000000000000..16690ac2e3264 --- /dev/null +++ b/packages/main/src/Select.figma.tsx @@ -0,0 +1,22 @@ +/** + * React Code Connect mapping for the SAP Web UI Kit "Select". Node 181557:7507. + * Mirrors Select.figma.ts. Largely static — see FIGMA_CODE_CONNECT.md § Select. + */ +import figma from "@figma/code-connect/react"; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { Select, Option } from "@ui5/webcomponents-react"; + +figma.connect( + Select, + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=181557-7507", + { + props: {}, + example: () => ( + + ), + } +); diff --git a/packages/main/src/StepInput.figma.ts b/packages/main/src/StepInput.figma.ts new file mode 100644 index 0000000000000..3b1a9056a05f2 --- /dev/null +++ b/packages/main/src/StepInput.figma.ts @@ -0,0 +1,36 @@ +/** + * Web Components Code Connect mapping for the SAP Web UI Kit "Step Input". + * Node: 148569:1727. Emits . + * + * See FIGMA_CODE_CONNECT.md § StepInput. Form Factor + Hover/Active ignored. + */ +import figma, { html } from "@figma/code-connect/html"; + +figma.connect( + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=148569-1727", + { + props: { + valueState: figma.enum("Value State", { + None: "", + Negative: 'value-state="Negative"', + Critical: 'value-state="Critical"', + Positive: 'value-state="Positive"', + Information: 'value-state="Information"', + }), + stateAttr: figma.enum("Interaction State", { + Disabled: "disabled", + "Read Only": "readonly", + Regular: "", + Hover: "", + Active: "", + }), + // Numeric value (Figma stores it as a text prop). + value: figma.string("✏️ Value"), + }, + // CANNOT MAP (FIGMA_CODE_CONNECT.md § StepInput): min/max/step are not in + // Figma; `Message Popover` boolean references slotted content; the +/- + // button icons are instance-swaps (registry problem). + example: ({ valueState, stateAttr, value }) => + html``, + } +); diff --git a/packages/main/src/StepInput.figma.tsx b/packages/main/src/StepInput.figma.tsx new file mode 100644 index 0000000000000..2f397562801dc --- /dev/null +++ b/packages/main/src/StepInput.figma.tsx @@ -0,0 +1,46 @@ +/** + * React Code Connect mapping for the SAP Web UI Kit "Step Input". + * Node 148569:1727. Mirrors StepInput.figma.ts. See FIGMA_CODE_CONNECT.md. + */ +import figma from "@figma/code-connect/react"; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { StepInput } from "@ui5/webcomponents-react"; + +figma.connect( + StepInput, + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=148569-1727", + { + props: { + valueState: figma.enum("Value State", { + None: undefined, + Negative: "Negative", + Critical: "Critical", + Positive: "Positive", + Information: "Information", + }), + disabled: figma.enum("Interaction State", { + Disabled: true, + Regular: false, + Hover: false, + Active: false, + "Read Only": false, + }), + readonly: figma.enum("Interaction State", { + "Read Only": true, + Regular: false, + Hover: false, + Active: false, + Disabled: false, + }), + value: figma.string("✏️ Value"), + }, + example: ({ value, valueState, disabled, readonly }) => ( + + ), + } +); From f8826ce51410b67c7da0cca55f841962ea094541 Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Mon, 10 Aug 2026 15:57:25 +0300 Subject: [PATCH 03/28] chore: figma connect Switch, Link, Avatar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds Web Components + React Code Connect mappings for 3 more SAP Web UI Kit components (published to nodes 24087:10369, 187:305, 573:3623): - Switch: checked, disabled, design (Type→Textual/Graphical) - Link: design, disabled, label text - Avatar: size, color-scheme (Color 1..10→Accent1..10), initials Doc updated with the per-component works/doesn't-map/how-to-fix blocks. --- packages/main/FIGMA_CODE_CONNECT.md | 31 +++++++++++++++++++ packages/main/src/Avatar.figma.ts | 46 +++++++++++++++++++++++++++++ packages/main/src/Avatar.figma.tsx | 43 +++++++++++++++++++++++++++ packages/main/src/Link.figma.ts | 35 ++++++++++++++++++++++ packages/main/src/Link.figma.tsx | 35 ++++++++++++++++++++++ packages/main/src/Switch.figma.ts | 34 +++++++++++++++++++++ packages/main/src/Switch.figma.tsx | 32 ++++++++++++++++++++ 7 files changed, 256 insertions(+) create mode 100644 packages/main/src/Avatar.figma.ts create mode 100644 packages/main/src/Avatar.figma.tsx create mode 100644 packages/main/src/Link.figma.ts create mode 100644 packages/main/src/Link.figma.tsx create mode 100644 packages/main/src/Switch.figma.ts create mode 100644 packages/main/src/Switch.figma.tsx diff --git a/packages/main/FIGMA_CODE_CONNECT.md b/packages/main/FIGMA_CODE_CONNECT.md index a036e2f16b060..e2c2bffaae48b 100644 --- a/packages/main/FIGMA_CODE_CONNECT.md +++ b/packages/main/FIGMA_CODE_CONNECT.md @@ -98,6 +98,37 @@ dynamically in Dev Mode and what doesn't (with the reason + fix). **how to fix:** - owner exposes per-segment text as component text props. +## Switch — ui5-switch (node 24087:10369) +**works:** yes — `checked`, `disabled`, `design` (Type: Non-Semantic→Textual, Semantic→Graphical) map. + +**doesn't map:** +- `textOn`/`textOff` aren't modeled in Figma. +- the on/off icon is an instance-swap. + +**how to fix:** +- owner exposes `textOn`/`textOff` as text props if dynamic labels are wanted. + +## Link — ui5-link (node 187:305) +**works:** yes — `design` (Emphasized/Subtle; Regular & Icon Link → Default), `disabled`, and label text map. + +**doesn't map:** +- the `Icon` (Icon Link type) is an instance-swap — name not readable (ui5-link has an `icon` slot). +- Visited/Down are visual pseudo-states. + +**how to fix:** +- owner adds an `Icon Name` string prop (or Code-Connect the icon set), same as Button. + +## Avatar — ui5-avatar (node 573:3623) +**works:** yes — `size` (XS..XL 1:1), `color-scheme` (Color 1..10 → Accent1..10; Transparent/Placeholder 1:1), and `initials` map. + +**doesn't map:** +- Person/Object icons are instance-swaps. +- Badge is a slot; `Optional Border` has no direct prop. +- Image/Tile colours have no `color-scheme` equivalent. + +**how to fix:** +- owner adds an `Icon Name` string prop for the icon variant; model the badge as a readable prop. + ## Icon — ui5-icon (node 983:5876) — UNPUBLISHABLE **works:** no — Figma rejects the publish: "corresponding node is not a component or component set". diff --git a/packages/main/src/Avatar.figma.ts b/packages/main/src/Avatar.figma.ts new file mode 100644 index 0000000000000..4816032086ab8 --- /dev/null +++ b/packages/main/src/Avatar.figma.ts @@ -0,0 +1,46 @@ +/** + * Web Components Code Connect mapping for the SAP Web UI Kit "Avatar". + * Node: 573:3623. Emits . + * + * See FIGMA_CODE_CONNECT.md § Avatar. Pseudo-states ignored. + */ +import figma, { html } from "@figma/code-connect/html"; + +figma.connect( + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=573-3623", + { + props: { + // Size → size (1:1). + size: figma.enum("Size", { + XS: 'size="XS"', + S: 'size="S"', + M: 'size="M"', + L: 'size="L"', + XL: 'size="XL"', + }), + // Color axis → color-scheme. 1..10 → Accent1..Accent10; Transparent/ + // Placeholder map 1:1; Image/Tile have no color-scheme equivalent. + colorScheme: figma.enum("Color", { + "1": 'color-scheme="Accent1"', + "2": 'color-scheme="Accent2"', + "3": 'color-scheme="Accent3"', + "4": 'color-scheme="Accent4"', + "5": 'color-scheme="Accent5"', + "6": 'color-scheme="Accent6"', + "7": 'color-scheme="Accent7"', + "8": 'color-scheme="Accent8"', + "9": 'color-scheme="Accent9"', + "10": 'color-scheme="Accent10"', + Transparent: 'color-scheme="Transparent"', + Placeholder: 'color-scheme="Placeholder"', + Image: "", + Tile: "", + }), + // Initials text (Type = Initials). + initials: figma.string("✏️ Initials"), + }, + // Person/Object Icon are instance-swaps; Badge is a slot — omitted. + example: ({ size, colorScheme, initials }) => + html``, + } +); diff --git a/packages/main/src/Avatar.figma.tsx b/packages/main/src/Avatar.figma.tsx new file mode 100644 index 0000000000000..61aa6089b21eb --- /dev/null +++ b/packages/main/src/Avatar.figma.tsx @@ -0,0 +1,43 @@ +/** + * React Code Connect mapping for the SAP Web UI Kit "Avatar". + * Node 573:3623. Mirrors Avatar.figma.ts. See FIGMA_CODE_CONNECT.md. + */ +import figma from "@figma/code-connect/react"; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { Avatar } from "@ui5/webcomponents-react"; + +figma.connect( + Avatar, + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=573-3623", + { + props: { + size: figma.enum("Size", { + XS: "XS", + S: "S", + M: "M", + L: "L", + XL: "XL", + }), + colorScheme: figma.enum("Color", { + "1": "Accent1", + "2": "Accent2", + "3": "Accent3", + "4": "Accent4", + "5": "Accent5", + "6": "Accent6", + "7": "Accent7", + "8": "Accent8", + "9": "Accent9", + "10": "Accent10", + Transparent: "Transparent", + Placeholder: "Placeholder", + Image: undefined, + Tile: undefined, + }), + initials: figma.string("✏️ Initials"), + }, + example: ({ size, colorScheme, initials }) => ( + + ), + } +); diff --git a/packages/main/src/Link.figma.ts b/packages/main/src/Link.figma.ts new file mode 100644 index 0000000000000..09e8dc4f780d2 --- /dev/null +++ b/packages/main/src/Link.figma.ts @@ -0,0 +1,35 @@ +/** + * Web Components Code Connect mapping for the SAP Web UI Kit "Link". + * Node: 187:305. Emits . + * + * See FIGMA_CODE_CONNECT.md § Link. Pseudo-states (Hover/Visited/Down) ignored. + */ +import figma, { html } from "@figma/code-connect/html"; + +figma.connect( + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=187-305", + { + props: { + // Type → design. Icon Link has no design equivalent → Default. + design: figma.enum("Type", { + Regular: "", + Emphasized: 'design="Emphasized"', + Subtle: 'design="Subtle"', + "Icon Link": "", + }), + // Interaction State = Disabled → disabled. + disabled: figma.enum("Interaction State", { + Disabled: "disabled", + Regular: "", + Hover: "", + Visited: "", + Down: "", + }), + // Link label text (default slot). + label: figma.textContent("Text"), + }, + // Icon slot (Icon Link type) is instance-swap — name not readable, omitted. + example: ({ design, disabled, label }) => + html`${label}`, + } +); diff --git a/packages/main/src/Link.figma.tsx b/packages/main/src/Link.figma.tsx new file mode 100644 index 0000000000000..7b8f6a58dbce7 --- /dev/null +++ b/packages/main/src/Link.figma.tsx @@ -0,0 +1,35 @@ +/** + * React Code Connect mapping for the SAP Web UI Kit "Link". + * Node 187:305. Mirrors Link.figma.ts. See FIGMA_CODE_CONNECT.md. + */ +import figma from "@figma/code-connect/react"; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { Link } from "@ui5/webcomponents-react"; + +figma.connect( + Link, + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=187-305", + { + props: { + design: figma.enum("Type", { + Regular: "Default", + Emphasized: "Emphasized", + Subtle: "Subtle", + "Icon Link": "Default", + }), + disabled: figma.enum("Interaction State", { + Disabled: true, + Regular: false, + Hover: false, + Visited: false, + Down: false, + }), + label: figma.textContent("Text"), + }, + example: ({ design, disabled, label }) => ( + + {label} + + ), + } +); diff --git a/packages/main/src/Switch.figma.ts b/packages/main/src/Switch.figma.ts new file mode 100644 index 0000000000000..c68a526d36161 --- /dev/null +++ b/packages/main/src/Switch.figma.ts @@ -0,0 +1,34 @@ +/** + * Web Components Code Connect mapping for the SAP Web UI Kit "Switch". + * Node: 24087:10369. Emits . + * + * See FIGMA_CODE_CONNECT.md § Switch. Form Factor + pseudo-states ignored. + */ +import figma, { html } from "@figma/code-connect/html"; + +figma.connect( + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=24087-10369", + { + props: { + // Type → design. Non-Semantic → Textual, Semantic → Graphical. + design: figma.enum("Type", { + "Non-Semantic": 'design="Textual"', + Semantic: 'design="Graphical"', + }), + // Checked variant → checked. + checked: figma.enum("Checked", { + True: "checked", + False: "", + }), + // Interaction State = Disabled → disabled. + disabled: figma.enum("Interaction State", { + Disabled: "disabled", + Regular: "", + Hover: "", + }), + }, + // textOn/textOff aren't modeled in Figma — omitted. + example: ({ design, checked, disabled }) => + html``, + } +); diff --git a/packages/main/src/Switch.figma.tsx b/packages/main/src/Switch.figma.tsx new file mode 100644 index 0000000000000..7307bfaabaee1 --- /dev/null +++ b/packages/main/src/Switch.figma.tsx @@ -0,0 +1,32 @@ +/** + * React Code Connect mapping for the SAP Web UI Kit "Switch". + * Node 24087:10369. Mirrors Switch.figma.ts. See FIGMA_CODE_CONNECT.md. + */ +import figma from "@figma/code-connect/react"; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { Switch } from "@ui5/webcomponents-react"; + +figma.connect( + Switch, + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=24087-10369", + { + props: { + design: figma.enum("Type", { + "Non-Semantic": "Textual", + Semantic: "Graphical", + }), + checked: figma.enum("Checked", { + True: true, + False: false, + }), + disabled: figma.enum("Interaction State", { + Disabled: true, + Regular: false, + Hover: false, + }), + }, + example: ({ design, checked, disabled }) => ( + + ), + } +); From 4ebe1da1ff46e28294cd00418b0b04c5c95d7531 Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Mon, 10 Aug 2026 17:05:35 +0300 Subject: [PATCH 04/28] =?UTF-8?q?fix(figma):=20Avatar=20add=20disabled=20m?= =?UTF-8?q?apping,=20document=20shape=E2=86=90Content?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - add disabled ← Interaction State=Disabled (was missing) - shape ← Content verified by screenshot (Person=circle, Object=square); document the coupling misalignment (Figma couples shape to content, WC treats shape as independent) - note initials emits regardless of Type (cross-axis parser limit) --- packages/main/src/Avatar.figma.ts | 24 +++++++++++++++++++++--- packages/main/src/Avatar.figma.tsx | 15 +++++++++++++-- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/packages/main/src/Avatar.figma.ts b/packages/main/src/Avatar.figma.ts index 4816032086ab8..8f23abbf25a5b 100644 --- a/packages/main/src/Avatar.figma.ts +++ b/packages/main/src/Avatar.figma.ts @@ -36,11 +36,29 @@ figma.connect( Image: "", Tile: "", }), - // Initials text (Type = Initials). + // Content → shape. Person → Circle, Object → Square. + // VERIFIED by screenshot of node 573:3623: Person column renders circles, + // Object column renders squares. NOTE misalignment: in Figma shape is + // COUPLED to Content (content source), but in the WC `shape` is an + // INDEPENDENT prop — Figma can't express e.g. a square person avatar. + shape: figma.enum("Content", { + Person: 'shape="Circle"', + Object: 'shape="Square"', + }), + // Interaction State = Disabled → disabled. + disabled: figma.enum("Interaction State", { + Disabled: "disabled", + Regular: "", + Hover: "", + Active: "", + "Toggled Hover": "", + }), + // Initials text. NOTE: emitted regardless of Type (parser can't gate one + // axis's attr on another) — consumer removes it for Image/Icon avatars. initials: figma.string("✏️ Initials"), }, // Person/Object Icon are instance-swaps; Badge is a slot — omitted. - example: ({ size, colorScheme, initials }) => - html``, + example: ({ size, colorScheme, shape, disabled, initials }) => + html``, } ); diff --git a/packages/main/src/Avatar.figma.tsx b/packages/main/src/Avatar.figma.tsx index 61aa6089b21eb..b6bd0da28f999 100644 --- a/packages/main/src/Avatar.figma.tsx +++ b/packages/main/src/Avatar.figma.tsx @@ -34,10 +34,21 @@ figma.connect( Image: undefined, Tile: undefined, }), + shape: figma.enum("Content", { + Person: "Circle", + Object: "Square", + }), + disabled: figma.enum("Interaction State", { + Disabled: true, + Regular: false, + Hover: false, + Active: false, + "Toggled Hover": false, + }), initials: figma.string("✏️ Initials"), }, - example: ({ size, colorScheme, initials }) => ( - + example: ({ size, colorScheme, shape, disabled, initials }) => ( + ), } ); From b7fe3b9255c24a4692bc4d75c12d2b47121f6855 Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Tue, 11 Aug 2026 09:30:39 +0300 Subject: [PATCH 05/28] =?UTF-8?q?docs(figma):=20add=20API=E2=86=94Figma=20?= =?UTF-8?q?findings=20audit;=20fix=20Switch=20design=20misalignment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - FIGMA_CODE_CONNECT_FINDINGS.md: per-component, per-public-prop cross-reference vs live Figma props, with verdict+evidence. Deliverable captures every misalignment (Figma-only props, WC-only props, concept mismatches, parser asymmetry, instance-swap/slot gaps). - Switch: remove incorrect Type→design mapping. Screenshot of node 24087:10369 shows all switches render icons (all 'Graphical'); Figma 'Type' is colour-semantics (no ui5-switch prop). design not derivable. --- packages/main/FIGMA_CODE_CONNECT_FINDINGS.md | 199 +++++++++++++++++++ packages/main/src/Switch.figma.ts | 16 +- packages/main/src/Switch.figma.tsx | 11 +- 3 files changed, 212 insertions(+), 14 deletions(-) create mode 100644 packages/main/FIGMA_CODE_CONNECT_FINDINGS.md diff --git a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md new file mode 100644 index 0000000000000..e7e94a9be8ec8 --- /dev/null +++ b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md @@ -0,0 +1,199 @@ +# Figma Code Connect — API ↔ Figma Findings + +**Purpose:** the deliverable is the *misalignments*. For each connected component, this +walks **every public API property** of the web component and cross-references it against +the **live** SAP Web UI Kit Figma properties (read via `get_context_for_code_connect`, +file `SILcWzK5uFghKun9jx6D7c`). Each row gets a verdict + evidence. + +**Verdicts:** +- ✅ **mapped** — emitted dynamically, reflects the selected variant; mapping confirmed correct. +- ⚠️ **partial** — mapped but with a caveat (approximation, ungated, presence-only). +- ❌ **can't map** — no honest Figma source (not modelled / slotted / instance-swap / behavioral). +- 🔴 **misalignment** — Figma and the WC model the concept differently, or Figma exposes something the WC doesn't (and vice-versa). *These are the findings.* + +**Evidence column:** `figma-props` = from the live property dump; `screenshot` = visually +confirmed; `source` = from the `.ts` API. Assumptions are called out explicitly. + +**Global conventions (apply to every component):** +- **Form Factor (Compact/Cozy)** → ❌ density is a global UI5 setting, not a per-element attr. +- **Interaction State = Hover/Active/Down/Focus/Visited** → ❌ visual pseudo-states, no attr. Only `Disabled`→`disabled`, `Read Only`→`readonly` are real. +- **accessible\* / accessibilityAttributes / tooltip / name / form** → ❌ a11y or form-association metadata, never modelled in the kit. Omitted from per-component tables unless notable. + +--- + +## Button — ui5-button (node 91702:11733) +Figma props: Type, Interaction State, Toggled, Counter Badge, Attention Badge, Icon (INSTANCE_SWAP), Icon Left, ✏️ Text, Form Factor. + +| WC API prop | Figma property | Verdict | Evidence / note | +|---|---|---|---| +| `design` | Type (Primary/Secondary/Tertiary/Accept/Reject/Attention) | ✅ mapped | figma-props; enum→Emphasized/Default/Transparent/Positive/Negative/Attention | +| `disabled` | Interaction State=Disabled | ✅ mapped | figma-props | +| `text` (slot) | ✏️ Text | ✅ mapped | figma-props; `figma.textContent` | +| `icon` | Icon (INSTANCE_SWAP) + Icon Left | ❌ can't map | figma-props; swapped icon name not readable → hardcoded `globe` | +| `endIcon` | — | ❌ can't map | source; kit has single Icon slot | +| `badge` (slot) | Counter Badge + Attention Badge (2 booleans) | 🔴 misalignment | figma-props; WC badge has 3 designs (Inline/Overlay/AttentionDot) + count; Figma = 2 booleans, count in nested layer → design & text hardcoded | +| `type`,`form`,`tooltip`,`loading`,`accessible*` | — | ❌ can't map | source; behavioral/a11y | +| — | Toggled (True/False) | 🔴 misalignment | figma-props; **Figma-only** — ui5-button has no toggle (that's ui5-toggle-button) | + +## Input — ui5-input (node 148569:1004) +Figma props: Content, Value State, Interaction State, ✏️ Placeholder, ✏️ Typed Text, Trailing Action, 2nd Action, Message Popover, Description Text (+✏️), Form Factor. + +| WC API prop | Figma property | Verdict | Evidence / note | +|---|---|---|---| +| `value` | ✏️ Typed Text | ✅ mapped | figma-props | +| `placeholder` | ✏️ Placeholder | ✅ mapped | figma-props | +| `valueState` | Value State (None/Negative/Critical/Positive/Information) | ✅ mapped | figma-props; 1:1 | +| `disabled` | Interaction State=Disabled | ✅ mapped | figma-props | +| `readonly` | Interaction State=Read Only | ✅ mapped | figma-props | +| `valueStateMessage` (slot) | Message Popover (BOOLEAN) → nested `✏️ Text`+`Value State` | 🔴 misalignment | figma-props; the popover instance **does** expose readable Text+Value State — currently only detected as presence; text could be mapped (improvement owed) | +| `icon` (slot) | 2nd Action (BOOLEAN) | ❌ can't map | figma-props; slotted, instance-swap | +| `showClearIcon` | Trailing Action (BOOLEAN) | ⚠️ partial | figma-props; approximated — Trailing Action is generic | +| — | Content (Placeholder/Typed Text) | 🔴 misalignment | figma-props; Figma-only display toggle; can't gate which text emits → both attrs always emitted | +| — | Description Text (+✏️) | 🔴 misalignment | figma-props; Figma-only, no ui5-input equivalent | +| `type`,`name`,`required`,`maxlength`,`noTypeahead`,`showSuggestions`,`open`,`filter` | — | ❌ can't map | source; behavioral | + +## CheckBox — ui5-checkbox (node 154589:905) +Figma props: Label (BOOLEAN), ✏️ Text, Value State, Interaction State (incl. Display Only), Check (Unchecked/Checked/Tristate), Form Factor. + +| WC API prop | Figma property | Verdict | Evidence / note | +|---|---|---|---| +| `text` | ✏️ Text (gated by Label boolean) | ✅ mapped | figma-props; gated via `figma.boolean("Label",…)` | +| `checked` | Check=Checked | ✅ mapped | figma-props | +| `indeterminate` | Check=Tristate | ⚠️ partial | figma-props; WC indeterminate is independent of checked; single Figma "Tristate" can't express both | +| `valueState` | Value State | ✅ mapped | figma-props; 1:1 | +| `disabled` | Interaction State=Disabled | ✅ mapped | figma-props | +| `readonly` | Interaction State=Read Only | ✅ mapped | figma-props | +| `displayOnly` | Interaction State=Display Only | ✅ mapped | figma-props; **Figma has Display Only** — maps 1:1 (earlier "no display-only mode" note was WRONG) | +| `required`,`wrappingType`,`name`,`value`,`accessible*` | — | ❌ can't map | source | + +## RadioButton — ui5-radio-button (node 154597:1967) +Figma props: Label (BOOLEAN), ✏️ Text, Value State, Interaction State, Selected (True/False), Form Factor. + +| WC API prop | Figma property | Verdict | Evidence / note | +|---|---|---|---| +| `text` | ✏️ Text (gated by Label) | ✅ mapped | figma-props | +| `checked` | Selected=True | ✅ mapped | figma-props | +| `valueState` | Value State | ✅ mapped | figma-props; 1:1 | +| `disabled` | Interaction State=Disabled | ✅ mapped | figma-props | +| `readonly` | Interaction State=Read Only | ✅ mapped | figma-props | +| `name`,`value`,`required`,`wrappingType`,`accessible*` | — | ❌ can't map | source; `name`/`value` are form-grouping (app-level) | + +Cleanest component — every visual prop maps, no misalignments. + +## StepInput — ui5-step-input (node 148569:1727) +Figma props: ✏️ Value, Value State, Interaction State, Message Popover, Description Text (+✏️), Form Factor; descendants Subtract/Add Button with Icon (INSTANCE_SWAP). + +| WC API prop | Figma property | Verdict | Evidence / note | +|---|---|---|---| +| `value` | ✏️ Value | ✅ mapped | figma-props | +| `valueState` | Value State | ✅ mapped | figma-props; 1:1 | +| `disabled` | Interaction State=Disabled | ✅ mapped | figma-props | +| `readonly` | Interaction State=Read Only | ✅ mapped | figma-props | +| `valueStateMessage` (slot) | Message Popover (BOOLEAN) | ❌ can't map | figma-props; nested text | +| `min`,`max`,`step`,`placeholder`,`name`,`required`,`accessible*` | — | ❌ can't map | source; behavioral (not visual) | +| +/- button icons | Subtract/Add Button → Icon (INSTANCE_SWAP) | ❌ can't map | figma-props; icon names not readable | +| — | Description Text (+✏️) | 🔴 misalignment | figma-props; Figma-only | + +## MessageStrip — ui5-message-strip (node 910:2517) +Figma props: Value State (incl. Indication Color), Color (None + Indication 1..10 / 1b..10b), Icon (True/False), Close Button (BOOLEAN); descendant Icon (INSTANCE). + +| WC API prop | Figma property | Verdict | Evidence / note | +|---|---|---|---| +| `design` | Value State (Information/Positive/Critical/Negative) | ✅ mapped | figma-props; semantic 1:1 | +| `design`=ColorSet1/2 | Color axis (Indication 1..10 / 1b..10b) | ✅ mapped (WC) | figma-props; single enum → `ColorSet1/2` + scheme; the `b` suffix encodes ColorSet2 | +| `colorScheme` ("1".."10") | Color axis | ✅ mapped (WC) | figma-props; folded into the Color enum output | +| `hideIcon` | Icon=False | ✅ mapped | figma-props | +| `hideCloseButton` | Close Button=False | ✅ mapped | figma-props | +| `icon` (slot) | Icon (INSTANCE) | ❌ can't map | figma-props; slotted custom icon | +| default text slot | — | ❌ can't map | source; slotted (placeholder) | +| React variant ColorSet2 | Color axis | 🔴 misalignment | React parser can't merge 2 axes into one `design` → ColorSet2 unreachable in React only | + +## Select — ui5-select (node 181557:7507) +Figma props: **only** Form Factor + Drop-Down. Value State/Interaction State/options live on a NESTED Input instance, not the Select's own props. + +| WC API prop | Figma property | Verdict | Evidence / note | +|---|---|---|---| +| `disabled` | (nested Input) Interaction State=Disabled | ⚠️ partial | figma-props; only via embedded Input instance | +| `readonly` | (nested Input) Interaction State=Read Only | ⚠️ partial | figma-props; via embedded Input | +| `valueState` | (nested Input) Value State | ⚠️ partial | figma-props; via embedded Input | +| `options` (slot) | — | ❌ can't map | figma-props; slotted, no option list | +| — | Drop-Down (True/False) | 🔴 misalignment | figma-props; runtime open state, no WC prop | +| — | Value State/Interaction State on Select itself | 🔴 misalignment | figma-props; **absent** on the Select component — owner should add axes like Input | +| `icon`,`name`,`required`,`textSeparator`,`label`(slot),`accessible*` | — | ❌ can't map | source | + +## SegmentedButton — ui5-segmented-button (node 91702:11986) +Figma props: 3rd/4th/5th Button (BOOLEAN), ⿻ Text/Icon Segments (SLOT), Type (Text/Icon), Form Factor. + +| WC API prop | Figma property | Verdict | Evidence / note | +|---|---|---|---| +| `items` (slot) — segment count | 3rd/4th/5th Button booleans | ⚠️ partial | figma-props; presence adds/removes placeholder items | +| `items` — labels/icons | ⿻ Text/Icon Segments (SLOT) | ❌ can't map | figma-props; slotted content not readable → placeholders | +| — item content type | Type (Text/Icon) | 🔴 misalignment | figma-props; per-item in WC (slotted), component-level axis in Figma | +| selected item | — | ❌ can't map | source; not a readable prop (first item marked selected) | +| `selectionMode`,`itemsFitContent`,`accessible*` | — | ❌ can't map | source | + +## Switch — ui5-switch (node 24087:10369) +Figma props: Type (Non-Semantic/Semantic), Interaction State, Checked (True/False), Form Factor; descendant Icon (INSTANCE). + +| WC API prop | Figma property | Verdict | Evidence / note | +|---|---|---|---| +| `checked` | Checked=True | ✅ mapped | figma-props | +| `disabled` | Interaction State=Disabled | ✅ mapped | figma-props | +| `design` (Textual/Graphical) | — | ❌ can't map | **screenshot-verified** (node 24087:10369): ALL Figma switches render icons (✓/✗) = all effectively `Graphical`; not derivable. Earlier `Type→design` mapping was WRONG, removed | +| `textOn`/`textOff` | — | ❌ can't map | source; not modelled in Figma | +| — | Type (Non-Semantic/Semantic) | 🔴 misalignment | figma-props+screenshot; Figma `Type` = colour semantics (neutral vs green/red); ui5-switch has **no** property for this | +| `readonly` | — | 🔴 misalignment | source; WC has `readonly`, Figma Switch has no Read Only state | +| `required`,`name`,`tooltip`,`accessible*` | — | ❌ can't map | source | + +## Link — ui5-link (node 187:305) +Figma props: ✏️ Text, Icon (INSTANCE_SWAP), Type (Regular/Emphasized/Subtle/Icon Link), Interaction State, Icon Position (Left/Right/N/A). + +| WC API prop | Figma property | Verdict | Evidence / note | +|---|---|---|---| +| text (slot) | ✏️ Text | ✅ mapped | figma-props | +| `design` | Type (Emphasized/Subtle; Regular→Default) | ✅ mapped | figma-props; Icon Link→Default (no design equiv) | +| `disabled` | Interaction State=Disabled | ✅ mapped | figma-props | +| `icon`/`endIcon` | Icon (INSTANCE_SWAP) + Icon Position | ❌ can't map | figma-props; swapped icon name not readable | +| — | Type=Icon Link + Icon Position | 🔴 misalignment | figma-props; Figma models icon-only/position as Type variants; WC uses `icon`/`endIcon` slots — no clean bridge | +| `href`,`target`,`wrappingType`,`interactiveAreaSize`,`accessible*` | — | ❌ can't map | source | + +## Avatar — ui5-avatar (node 573:3623) +Figma props: Type (Image/Icon/Initials), Content (Person/Object), Size, Color, Interaction State, Badge (BOOLEAN), Optional Border (BOOLEAN), Person/Object Icon (INSTANCE_SWAP), ✏️ Initials. + +| WC API prop | Figma property | Verdict | Evidence / note | +|---|---|---|---| +| `size` | Size (XS–XL) | ✅ mapped | figma-props; 1:1 | +| `colorScheme` | Color (1..10, Transparent, Placeholder) | ✅ mapped | figma-props; 1..10→Accent1..10; `Auto` default absent; Image/Tile→none | +| `shape` | Content (Person→Circle, Object→Square) | ✅ mapped | **screenshot-verified** (node 573:3623: Person col=circles, Object col=squares) | +| `disabled` | Interaction State=Disabled | ✅ mapped | figma-props (was missing, fixed) | +| `initials` | ✏️ Initials | ⚠️ partial | figma-props; emitted regardless of Type (cross-axis gate not possible) | +| `mode` | Type (Image/Icon/Initials) | 🔴 misalignment | figma-props; Figma "Type"=content source, WC `mode`=a11y role (Image/Decorative/Interactive) — different concepts | +| `shape` vs `mode`/content | Content axis | 🔴 misalignment | screenshot; Figma COUPLES shape to content (Person=circle), WC treats `shape` as INDEPENDENT — Figma can't express a square person | +| `icon`/`fallbackIcon` | Person/Object Icon (INSTANCE_SWAP) | ❌ can't map | figma-props; icon name not readable | +| `image` (slot) | Type=Image | ❌ can't map | source; slotted image | +| `badge` (slot) | Badge (BOOLEAN) | ⚠️ partial | figma-props; presence only, not content | +| — | Optional Border (BOOLEAN) | 🔴 misalignment | figma-props; **Figma-only**, no WC prop | +| `accessible*` | — | ❌ can't map | source | + +--- + +## Cross-cutting findings (recurring misalignments) + +1. **Instance-swap icons** (Button, Link, StepInput, Avatar) — the selected icon's registry + name is never readable from a swapped instance. Blocks every `icon=`/`endIcon=` mapping. + *Fix: owner adds an `Icon Name` text prop, or generate per-icon Code Connect entries.* +2. **Slotted content** (MessageStrip text, Select options, SegmentedButton labels, Input/StepInput + value-state message) — light-DOM projection isn't a readable prop. +3. **Figma-only properties with no WC equivalent** — Button `Toggled`, Input/StepInput + `Description Text`, Avatar `Optional Border`, Select `Drop-Down`. These are pure Figma-side + modelling that the component API doesn't have. +4. **WC props absent from Figma** — Switch `readonly`, Select's own Value/Interaction State + (only on nested Input). Owner should add the axes. +5. **Concept mismatches** — Avatar `mode` (a11y role) vs Figma `Type` (content source); + Avatar shape/content coupling; Link icon-as-Type vs icon-as-slot. +6. **Parser asymmetry** — the React parser can't merge two Figma axes into one prop, so + MessageStrip ColorSet2 is unreachable in React (works in WC). + +## Open TODOs surfaced by this audit +- **Switch `design`** — RESOLVED: verified not mappable (screenshot), wrong mapping removed. +- **Input `valueStateMessage`** — the popover text IS readable; upgrade from presence-only to mapping the text. diff --git a/packages/main/src/Switch.figma.ts b/packages/main/src/Switch.figma.ts index c68a526d36161..b42bde03a290c 100644 --- a/packages/main/src/Switch.figma.ts +++ b/packages/main/src/Switch.figma.ts @@ -10,11 +10,11 @@ figma.connect( "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=24087-10369", { props: { - // Type → design. Non-Semantic → Textual, Semantic → Graphical. - design: figma.enum("Type", { - "Non-Semantic": 'design="Textual"', - Semantic: 'design="Graphical"', - }), + // NOTE: Figma "Type" (Non-Semantic/Semantic) is about colour semantics + // (neutral vs green/red), which ui5-switch has NO property for. It does + // NOT map to `design` (Textual/Graphical): screenshot of node 24087:10369 + // shows ALL Figma switches render icons (✓/✗), i.e. all are effectively + // `design="Graphical"`. So `design` cannot be driven by Type — omitted. // Checked variant → checked. checked: figma.enum("Checked", { True: "checked", @@ -27,8 +27,8 @@ figma.connect( Hover: "", }), }, - // textOn/textOff aren't modeled in Figma — omitted. - example: ({ design, checked, disabled }) => - html``, + // design (Textual/Graphical), textOn/textOff aren't derivable from Figma. + example: ({ checked, disabled }) => + html``, } ); diff --git a/packages/main/src/Switch.figma.tsx b/packages/main/src/Switch.figma.tsx index 7307bfaabaee1..c38edff2d103c 100644 --- a/packages/main/src/Switch.figma.tsx +++ b/packages/main/src/Switch.figma.tsx @@ -11,10 +11,9 @@ figma.connect( "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=24087-10369", { props: { - design: figma.enum("Type", { - "Non-Semantic": "Textual", - Semantic: "Graphical", - }), + // NOTE: Figma "Type" (Non-Semantic/Semantic) = colour semantics, which + // ui5-switch has no prop for. It does NOT map to `design`; screenshot of + // node 24087:10369 shows all switches render icons (all `Graphical`). checked: figma.enum("Checked", { True: true, False: false, @@ -25,8 +24,8 @@ figma.connect( Hover: false, }), }, - example: ({ design, checked, disabled }) => ( - + example: ({ checked, disabled }) => ( + ), } ); From 89f66801491afc6328470fefb9df0a9a4cae8c8e Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Tue, 11 Aug 2026 09:59:59 +0300 Subject: [PATCH 06/28] docs(figma): screenshot-verify all 11 component mappings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Evidence-based api-by-api pass: every component set screenshot-verified, mappings confirmed against renders. MessageStrip ColorSet1/2 direction confirmed via WC CSS-token names (Indication N→ColorSet1, Nb→ColorSet2). No new defects beyond the Avatar/Switch fixes already applied. --- packages/main/FIGMA_CODE_CONNECT_FINDINGS.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md index e7e94a9be8ec8..42a5e24564fd9 100644 --- a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md +++ b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md @@ -14,6 +14,11 @@ file `SILcWzK5uFghKun9jx6D7c`). Each row gets a verdict + evidence. **Evidence column:** `figma-props` = from the live property dump; `screenshot` = visually confirmed; `source` = from the `.ts` API. Assumptions are called out explicitly. +**Verification status (2026-08-11):** all 11 component sets were screenshot-verified this pass +(node renders compared against the mapping). Defects found & fixed: Avatar `disabled` (missing), +Switch `design` (wrong Type→Textual/Graphical mapping, removed). MessageStrip +ColorSet1/2↔Indication/Indication-b direction confirmed via CSS-token names. + **Global conventions (apply to every component):** - **Form Factor (Compact/Cozy)** → ❌ density is a global UI5 setting, not a per-element attr. - **Interaction State = Hover/Active/Down/Focus/Visited** → ❌ visual pseudo-states, no attr. Only `Disabled`→`disabled`, `Read Only`→`readonly` are real. @@ -100,7 +105,7 @@ Figma props: Value State (incl. Indication Color), Color (None + Indication 1..1 | WC API prop | Figma property | Verdict | Evidence / note | |---|---|---|---| | `design` | Value State (Information/Positive/Critical/Negative) | ✅ mapped | figma-props; semantic 1:1 | -| `design`=ColorSet1/2 | Color axis (Indication 1..10 / 1b..10b) | ✅ mapped (WC) | figma-props; single enum → `ColorSet1/2` + scheme; the `b` suffix encodes ColorSet2 | +| `design`=ColorSet1/2 | Color axis (Indication 1..10 / 1b..10b) | ✅ mapped (WC) | figma-props + CSS-token correspondence: Figma "Indication N" ↔ `--sapIndicationColor_N` = **ColorSet1**; "Nb" ↔ MessageStrip's private set-2 tokens = **ColorSet2**. Direction confirmed | | `colorScheme` ("1".."10") | Color axis | ✅ mapped (WC) | figma-props; folded into the Color enum output | | `hideIcon` | Icon=False | ✅ mapped | figma-props | | `hideCloseButton` | Close Button=False | ✅ mapped | figma-props | From 4e910c105db967ebfac06970f66a9ea9adf60ddf Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Tue, 11 Aug 2026 10:11:03 +0300 Subject: [PATCH 07/28] feat(figma): map Input valueStateMessage text from nested popover MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reads the readable ✏️ Text from the nested 'Input Message Popover' instance (via figma.nestedProps) and emits it as a valueStateMessage slot, gated on the Message Popover boolean. WC: slot resolved into a figma.boolean value (HTML parser rejects template ternaries). React: same, gated JSX in the boolean's true branch. Note: Message Popover is only present in Active interaction-state variants in Figma, so the slot appears there. --- packages/main/src/Input.figma.ts | 17 ++++++++++++----- packages/main/src/Input.figma.tsx | 16 +++++++++++++++- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/packages/main/src/Input.figma.ts b/packages/main/src/Input.figma.ts index 94619d124d0a7..f1c76cb26c301 100644 --- a/packages/main/src/Input.figma.ts +++ b/packages/main/src/Input.figma.ts @@ -32,13 +32,20 @@ figma.connect( // Text properties. placeholder: figma.string("✏️ Placeholder"), value: figma.string("✏️ Typed Text"), + // Value-state message: gate the slot markup on the Message Popover + // boolean, reading the readable ✏️ Text from the nested "Input Message + // Popover" instance. Resolved HERE (not via a template ternary, which the + // HTML parser rejects). + msgSlot: figma.boolean("Message Popover", { + true: html`
${figma.nestedProps("Input Message Popover", { text: figma.string("✏️ Text") }).text}
`, + false: "", + }), }, // CANNOT MAP (see FIGMA_CODE_CONNECT.md § Input): the `Content` variant // (Placeholder vs Typed Text) can't pick which text prop to emit, so both - // are emitted; `Trailing Action`, `2nd Action`, `Message Popover` and - // `Description Text` booleans reference slotted content with no readable - // value. - example: ({ valueState, stateAttr, placeholder, value }) => - html``, + // are emitted; `Trailing Action`, `2nd Action` reference slotted icon + // content with no readable value; `Description Text` has no ui5-input attr. + example: ({ valueState, stateAttr, placeholder, value, msgSlot }) => + html`${msgSlot}`, } ); diff --git a/packages/main/src/Input.figma.tsx b/packages/main/src/Input.figma.tsx index 6aeea6d007322..4908b52da2d79 100644 --- a/packages/main/src/Input.figma.tsx +++ b/packages/main/src/Input.figma.tsx @@ -34,14 +34,28 @@ figma.connect( }), placeholder: figma.string("✏️ Placeholder"), value: figma.string("✏️ Typed Text"), + // Value-state message from the nested "Input Message Popover" instance; + // gate the whole slot element on the Message Popover boolean (the parser + // rejects a ternary in the example, so resolve it into the prop here). + valueStateMessage: figma.boolean("Message Popover", { + true: ( +
+ {figma.nestedProps("Input Message Popover", { + text: figma.string("✏️ Text"), + }).text} +
+ ), + false: undefined, + }), }, - example: ({ value, placeholder, valueState, disabled, readonly }) => ( + example: ({ value, placeholder, valueState, disabled, readonly, valueStateMessage }) => ( ), } From 0bb1f4302deef93d9a8a78731d4dffb2a7b7ae46 Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Tue, 11 Aug 2026 10:11:23 +0300 Subject: [PATCH 08/28] docs(figma): mark Input valueStateMessage as mapped in findings --- packages/main/FIGMA_CODE_CONNECT_FINDINGS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md index 42a5e24564fd9..b33bdbd122805 100644 --- a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md +++ b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md @@ -50,7 +50,7 @@ Figma props: Content, Value State, Interaction State, ✏️ Placeholder, ✏️ | `valueState` | Value State (None/Negative/Critical/Positive/Information) | ✅ mapped | figma-props; 1:1 | | `disabled` | Interaction State=Disabled | ✅ mapped | figma-props | | `readonly` | Interaction State=Read Only | ✅ mapped | figma-props | -| `valueStateMessage` (slot) | Message Popover (BOOLEAN) → nested `✏️ Text`+`Value State` | 🔴 misalignment | figma-props; the popover instance **does** expose readable Text+Value State — currently only detected as presence; text could be mapped (improvement owed) | +| `valueStateMessage` (slot) | Message Popover (BOOLEAN) → nested `✏️ Text`+`Value State` | ✅ mapped | figma-props; the popover instance exposes a readable Text — now mapped: slot gated on the boolean, text read via `figma.nestedProps`. Only present in Active-state variants | | `icon` (slot) | 2nd Action (BOOLEAN) | ❌ can't map | figma-props; slotted, instance-swap | | `showClearIcon` | Trailing Action (BOOLEAN) | ⚠️ partial | figma-props; approximated — Trailing Action is generic | | — | Content (Placeholder/Typed Text) | 🔴 misalignment | figma-props; Figma-only display toggle; can't gate which text emits → both attrs always emitted | @@ -201,4 +201,4 @@ Figma props: Type (Image/Icon/Initials), Content (Person/Object), Size, Color, I ## Open TODOs surfaced by this audit - **Switch `design`** — RESOLVED: verified not mappable (screenshot), wrong mapping removed. -- **Input `valueStateMessage`** — the popover text IS readable; upgrade from presence-only to mapping the text. +- **Input `valueStateMessage`** — RESOLVED: nested popover text now mapped (WC + React). *Verify in Dev Mode on an Active-state variant that the slot renders cleanly (published, not yet Dev-Mode-confirmed).* From f88a5eb526c90c5fcd32dffa8ecfdb08e8c0c32b Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Tue, 11 Aug 2026 10:28:59 +0300 Subject: [PATCH 09/28] fix(figma): Button badge mapping bugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Counter Badge is a Figma VARIANT (True/False), not a boolean — was figma.boolean (silently didn't match → badge never showed). Now figma.enum. - badge text '1' → '72' to match the Figma nested layer. - React was missing the Attention Badge; React can only express one badge (parser rejects compound placeholders) so it drives from Counter Badge — documented asymmetry. WC emits both. - findings doc rewritten to Works/Doesn't work/Assumed/Misalignment format. --- packages/main/FIGMA_CODE_CONNECT_FINDINGS.md | 416 ++++++++++--------- packages/main/src/Button.figma.ts | 17 +- packages/main/src/Button.figma.tsx | 22 +- 3 files changed, 242 insertions(+), 213 deletions(-) diff --git a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md index b33bdbd122805..f8723f80dfd33 100644 --- a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md +++ b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md @@ -1,204 +1,228 @@ # Figma Code Connect — API ↔ Figma Findings -**Purpose:** the deliverable is the *misalignments*. For each connected component, this -walks **every public API property** of the web component and cross-references it against -the **live** SAP Web UI Kit Figma properties (read via `get_context_for_code_connect`, -file `SILcWzK5uFghKun9jx6D7c`). Each row gets a verdict + evidence. - -**Verdicts:** -- ✅ **mapped** — emitted dynamically, reflects the selected variant; mapping confirmed correct. -- ⚠️ **partial** — mapped but with a caveat (approximation, ungated, presence-only). -- ❌ **can't map** — no honest Figma source (not modelled / slotted / instance-swap / behavioral). -- 🔴 **misalignment** — Figma and the WC model the concept differently, or Figma exposes something the WC doesn't (and vice-versa). *These are the findings.* - -**Evidence column:** `figma-props` = from the live property dump; `screenshot` = visually -confirmed; `source` = from the `.ts` API. Assumptions are called out explicitly. - -**Verification status (2026-08-11):** all 11 component sets were screenshot-verified this pass -(node renders compared against the mapping). Defects found & fixed: Avatar `disabled` (missing), -Switch `design` (wrong Type→Textual/Graphical mapping, removed). MessageStrip -ColorSet1/2↔Indication/Indication-b direction confirmed via CSS-token names. - -**Global conventions (apply to every component):** -- **Form Factor (Compact/Cozy)** → ❌ density is a global UI5 setting, not a per-element attr. -- **Interaction State = Hover/Active/Down/Focus/Visited** → ❌ visual pseudo-states, no attr. Only `Disabled`→`disabled`, `Read Only`→`readonly` are real. -- **accessible\* / accessibilityAttributes / tooltip / name / form** → ❌ a11y or form-association metadata, never modelled in the kit. Omitted from per-component tables unless notable. +Per-component state of the SAP Web UI Kit Code Connect mappings (file +`SILcWzK5uFghKun9jx6D7c`), both **Web Components** (`src/*.figma.ts`) and **React** +(`src/*.figma.tsx`). For each component: what **works**, what **doesn't work**, what is +**assumed** (needs manual Dev-Mode check), and any **misalignment** between the Figma +model and the web-component API. *The misalignments are the point of this document.* + +**Verification legend:** `screenshot-verified` = the component set was rendered and the +mapping checked against it; `figma-props` = confirmed against the live property dump; +`assumed` = plausible but NOT yet visually confirmed — flagged for manual check. + +**Global (every component):** Form Factor (Compact/Cozy) is ignored (global density, not a +per-element attr). Interaction State Hover/Active/Down/Focus/Visited are visual pseudo-states +with no attr — only `Disabled`→`disabled`, `Read Only`→`readonly` map. a11y/name/form/tooltip +props are never modelled in the kit. + +--- + +## 1. Button — ui5-button (node 91702:11733) + +**Works (screenshot-verified):** +- `design` ← Type — all 6 confirmed: Primary→Emphasized, Secondary→Default, Tertiary→Transparent, Accept→Positive, Reject→Negative, Attention→Attention. +- `disabled`, label text. +- Counter badge presence ← Counter Badge (WC + React). +- Attention badge presence ← Attention Badge (**WC only** — see misalignment). + +**Doesn't work:** +- `icon` — instance-swap, name unreadable → hardcoded `icon="globe"`. +- badge `design`/`text` — hardcoded (`OverlayText`, `text="72"`). Figma has no badge-design enum and the count lives in an unexposed nested layer, so neither is readable. Text hardcoded to "72" to match the Figma layer. +- `endIcon` — no Figma equivalent. + +**Assumed:** nothing — badge fixes verified against live props; re-check in Dev Mode after this fix. + +**Fixed 2026-08-11 (were bugs):** +- `Counter Badge` is a Figma **VARIANT** (True/False), not a boolean — was `figma.boolean` (silently didn't match → no badge). Now `figma.enum`. +- Badge text was `"1"`; Figma layer shows `"72"` — corrected. +- React was missing the Attention Badge entirely. + +**Misalignment:** +- Figma `Toggled` axis — ui5-button has no toggle (that's ui5-toggle-button). Figma-only, ignored. +- **React can express only ONE badge.** The `badge` prop can't reference two Figma axes (parser rejects compound placeholders), so React drives `badge` from Counter Badge only; the Attention Badge is unreachable in React (WC emits both). Same parser-asymmetry class as MessageStrip ColorSet2. --- -## Button — ui5-button (node 91702:11733) -Figma props: Type, Interaction State, Toggled, Counter Badge, Attention Badge, Icon (INSTANCE_SWAP), Icon Left, ✏️ Text, Form Factor. - -| WC API prop | Figma property | Verdict | Evidence / note | -|---|---|---|---| -| `design` | Type (Primary/Secondary/Tertiary/Accept/Reject/Attention) | ✅ mapped | figma-props; enum→Emphasized/Default/Transparent/Positive/Negative/Attention | -| `disabled` | Interaction State=Disabled | ✅ mapped | figma-props | -| `text` (slot) | ✏️ Text | ✅ mapped | figma-props; `figma.textContent` | -| `icon` | Icon (INSTANCE_SWAP) + Icon Left | ❌ can't map | figma-props; swapped icon name not readable → hardcoded `globe` | -| `endIcon` | — | ❌ can't map | source; kit has single Icon slot | -| `badge` (slot) | Counter Badge + Attention Badge (2 booleans) | 🔴 misalignment | figma-props; WC badge has 3 designs (Inline/Overlay/AttentionDot) + count; Figma = 2 booleans, count in nested layer → design & text hardcoded | -| `type`,`form`,`tooltip`,`loading`,`accessible*` | — | ❌ can't map | source; behavioral/a11y | -| — | Toggled (True/False) | 🔴 misalignment | figma-props; **Figma-only** — ui5-button has no toggle (that's ui5-toggle-button) | - -## Input — ui5-input (node 148569:1004) -Figma props: Content, Value State, Interaction State, ✏️ Placeholder, ✏️ Typed Text, Trailing Action, 2nd Action, Message Popover, Description Text (+✏️), Form Factor. - -| WC API prop | Figma property | Verdict | Evidence / note | -|---|---|---|---| -| `value` | ✏️ Typed Text | ✅ mapped | figma-props | -| `placeholder` | ✏️ Placeholder | ✅ mapped | figma-props | -| `valueState` | Value State (None/Negative/Critical/Positive/Information) | ✅ mapped | figma-props; 1:1 | -| `disabled` | Interaction State=Disabled | ✅ mapped | figma-props | -| `readonly` | Interaction State=Read Only | ✅ mapped | figma-props | -| `valueStateMessage` (slot) | Message Popover (BOOLEAN) → nested `✏️ Text`+`Value State` | ✅ mapped | figma-props; the popover instance exposes a readable Text — now mapped: slot gated on the boolean, text read via `figma.nestedProps`. Only present in Active-state variants | -| `icon` (slot) | 2nd Action (BOOLEAN) | ❌ can't map | figma-props; slotted, instance-swap | -| `showClearIcon` | Trailing Action (BOOLEAN) | ⚠️ partial | figma-props; approximated — Trailing Action is generic | -| — | Content (Placeholder/Typed Text) | 🔴 misalignment | figma-props; Figma-only display toggle; can't gate which text emits → both attrs always emitted | -| — | Description Text (+✏️) | 🔴 misalignment | figma-props; Figma-only, no ui5-input equivalent | -| `type`,`name`,`required`,`maxlength`,`noTypeahead`,`showSuggestions`,`open`,`filter` | — | ❌ can't map | source; behavioral | - -## CheckBox — ui5-checkbox (node 154589:905) -Figma props: Label (BOOLEAN), ✏️ Text, Value State, Interaction State (incl. Display Only), Check (Unchecked/Checked/Tristate), Form Factor. - -| WC API prop | Figma property | Verdict | Evidence / note | -|---|---|---|---| -| `text` | ✏️ Text (gated by Label boolean) | ✅ mapped | figma-props; gated via `figma.boolean("Label",…)` | -| `checked` | Check=Checked | ✅ mapped | figma-props | -| `indeterminate` | Check=Tristate | ⚠️ partial | figma-props; WC indeterminate is independent of checked; single Figma "Tristate" can't express both | -| `valueState` | Value State | ✅ mapped | figma-props; 1:1 | -| `disabled` | Interaction State=Disabled | ✅ mapped | figma-props | -| `readonly` | Interaction State=Read Only | ✅ mapped | figma-props | -| `displayOnly` | Interaction State=Display Only | ✅ mapped | figma-props; **Figma has Display Only** — maps 1:1 (earlier "no display-only mode" note was WRONG) | -| `required`,`wrappingType`,`name`,`value`,`accessible*` | — | ❌ can't map | source | - -## RadioButton — ui5-radio-button (node 154597:1967) -Figma props: Label (BOOLEAN), ✏️ Text, Value State, Interaction State, Selected (True/False), Form Factor. - -| WC API prop | Figma property | Verdict | Evidence / note | -|---|---|---|---| -| `text` | ✏️ Text (gated by Label) | ✅ mapped | figma-props | -| `checked` | Selected=True | ✅ mapped | figma-props | -| `valueState` | Value State | ✅ mapped | figma-props; 1:1 | -| `disabled` | Interaction State=Disabled | ✅ mapped | figma-props | -| `readonly` | Interaction State=Read Only | ✅ mapped | figma-props | -| `name`,`value`,`required`,`wrappingType`,`accessible*` | — | ❌ can't map | source; `name`/`value` are form-grouping (app-level) | - -Cleanest component — every visual prop maps, no misalignments. - -## StepInput — ui5-step-input (node 148569:1727) -Figma props: ✏️ Value, Value State, Interaction State, Message Popover, Description Text (+✏️), Form Factor; descendants Subtract/Add Button with Icon (INSTANCE_SWAP). - -| WC API prop | Figma property | Verdict | Evidence / note | -|---|---|---|---| -| `value` | ✏️ Value | ✅ mapped | figma-props | -| `valueState` | Value State | ✅ mapped | figma-props; 1:1 | -| `disabled` | Interaction State=Disabled | ✅ mapped | figma-props | -| `readonly` | Interaction State=Read Only | ✅ mapped | figma-props | -| `valueStateMessage` (slot) | Message Popover (BOOLEAN) | ❌ can't map | figma-props; nested text | -| `min`,`max`,`step`,`placeholder`,`name`,`required`,`accessible*` | — | ❌ can't map | source; behavioral (not visual) | -| +/- button icons | Subtract/Add Button → Icon (INSTANCE_SWAP) | ❌ can't map | figma-props; icon names not readable | -| — | Description Text (+✏️) | 🔴 misalignment | figma-props; Figma-only | - -## MessageStrip — ui5-message-strip (node 910:2517) -Figma props: Value State (incl. Indication Color), Color (None + Indication 1..10 / 1b..10b), Icon (True/False), Close Button (BOOLEAN); descendant Icon (INSTANCE). - -| WC API prop | Figma property | Verdict | Evidence / note | -|---|---|---|---| -| `design` | Value State (Information/Positive/Critical/Negative) | ✅ mapped | figma-props; semantic 1:1 | -| `design`=ColorSet1/2 | Color axis (Indication 1..10 / 1b..10b) | ✅ mapped (WC) | figma-props + CSS-token correspondence: Figma "Indication N" ↔ `--sapIndicationColor_N` = **ColorSet1**; "Nb" ↔ MessageStrip's private set-2 tokens = **ColorSet2**. Direction confirmed | -| `colorScheme` ("1".."10") | Color axis | ✅ mapped (WC) | figma-props; folded into the Color enum output | -| `hideIcon` | Icon=False | ✅ mapped | figma-props | -| `hideCloseButton` | Close Button=False | ✅ mapped | figma-props | -| `icon` (slot) | Icon (INSTANCE) | ❌ can't map | figma-props; slotted custom icon | -| default text slot | — | ❌ can't map | source; slotted (placeholder) | -| React variant ColorSet2 | Color axis | 🔴 misalignment | React parser can't merge 2 axes into one `design` → ColorSet2 unreachable in React only | - -## Select — ui5-select (node 181557:7507) -Figma props: **only** Form Factor + Drop-Down. Value State/Interaction State/options live on a NESTED Input instance, not the Select's own props. - -| WC API prop | Figma property | Verdict | Evidence / note | -|---|---|---|---| -| `disabled` | (nested Input) Interaction State=Disabled | ⚠️ partial | figma-props; only via embedded Input instance | -| `readonly` | (nested Input) Interaction State=Read Only | ⚠️ partial | figma-props; via embedded Input | -| `valueState` | (nested Input) Value State | ⚠️ partial | figma-props; via embedded Input | -| `options` (slot) | — | ❌ can't map | figma-props; slotted, no option list | -| — | Drop-Down (True/False) | 🔴 misalignment | figma-props; runtime open state, no WC prop | -| — | Value State/Interaction State on Select itself | 🔴 misalignment | figma-props; **absent** on the Select component — owner should add axes like Input | -| `icon`,`name`,`required`,`textSeparator`,`label`(slot),`accessible*` | — | ❌ can't map | source | - -## SegmentedButton — ui5-segmented-button (node 91702:11986) -Figma props: 3rd/4th/5th Button (BOOLEAN), ⿻ Text/Icon Segments (SLOT), Type (Text/Icon), Form Factor. - -| WC API prop | Figma property | Verdict | Evidence / note | -|---|---|---|---| -| `items` (slot) — segment count | 3rd/4th/5th Button booleans | ⚠️ partial | figma-props; presence adds/removes placeholder items | -| `items` — labels/icons | ⿻ Text/Icon Segments (SLOT) | ❌ can't map | figma-props; slotted content not readable → placeholders | -| — item content type | Type (Text/Icon) | 🔴 misalignment | figma-props; per-item in WC (slotted), component-level axis in Figma | -| selected item | — | ❌ can't map | source; not a readable prop (first item marked selected) | -| `selectionMode`,`itemsFitContent`,`accessible*` | — | ❌ can't map | source | - -## Switch — ui5-switch (node 24087:10369) -Figma props: Type (Non-Semantic/Semantic), Interaction State, Checked (True/False), Form Factor; descendant Icon (INSTANCE). - -| WC API prop | Figma property | Verdict | Evidence / note | -|---|---|---|---| -| `checked` | Checked=True | ✅ mapped | figma-props | -| `disabled` | Interaction State=Disabled | ✅ mapped | figma-props | -| `design` (Textual/Graphical) | — | ❌ can't map | **screenshot-verified** (node 24087:10369): ALL Figma switches render icons (✓/✗) = all effectively `Graphical`; not derivable. Earlier `Type→design` mapping was WRONG, removed | -| `textOn`/`textOff` | — | ❌ can't map | source; not modelled in Figma | -| — | Type (Non-Semantic/Semantic) | 🔴 misalignment | figma-props+screenshot; Figma `Type` = colour semantics (neutral vs green/red); ui5-switch has **no** property for this | -| `readonly` | — | 🔴 misalignment | source; WC has `readonly`, Figma Switch has no Read Only state | -| `required`,`name`,`tooltip`,`accessible*` | — | ❌ can't map | source | - -## Link — ui5-link (node 187:305) -Figma props: ✏️ Text, Icon (INSTANCE_SWAP), Type (Regular/Emphasized/Subtle/Icon Link), Interaction State, Icon Position (Left/Right/N/A). - -| WC API prop | Figma property | Verdict | Evidence / note | -|---|---|---|---| -| text (slot) | ✏️ Text | ✅ mapped | figma-props | -| `design` | Type (Emphasized/Subtle; Regular→Default) | ✅ mapped | figma-props; Icon Link→Default (no design equiv) | -| `disabled` | Interaction State=Disabled | ✅ mapped | figma-props | -| `icon`/`endIcon` | Icon (INSTANCE_SWAP) + Icon Position | ❌ can't map | figma-props; swapped icon name not readable | -| — | Type=Icon Link + Icon Position | 🔴 misalignment | figma-props; Figma models icon-only/position as Type variants; WC uses `icon`/`endIcon` slots — no clean bridge | -| `href`,`target`,`wrappingType`,`interactiveAreaSize`,`accessible*` | — | ❌ can't map | source | - -## Avatar — ui5-avatar (node 573:3623) -Figma props: Type (Image/Icon/Initials), Content (Person/Object), Size, Color, Interaction State, Badge (BOOLEAN), Optional Border (BOOLEAN), Person/Object Icon (INSTANCE_SWAP), ✏️ Initials. - -| WC API prop | Figma property | Verdict | Evidence / note | -|---|---|---|---| -| `size` | Size (XS–XL) | ✅ mapped | figma-props; 1:1 | -| `colorScheme` | Color (1..10, Transparent, Placeholder) | ✅ mapped | figma-props; 1..10→Accent1..10; `Auto` default absent; Image/Tile→none | -| `shape` | Content (Person→Circle, Object→Square) | ✅ mapped | **screenshot-verified** (node 573:3623: Person col=circles, Object col=squares) | -| `disabled` | Interaction State=Disabled | ✅ mapped | figma-props (was missing, fixed) | -| `initials` | ✏️ Initials | ⚠️ partial | figma-props; emitted regardless of Type (cross-axis gate not possible) | -| `mode` | Type (Image/Icon/Initials) | 🔴 misalignment | figma-props; Figma "Type"=content source, WC `mode`=a11y role (Image/Decorative/Interactive) — different concepts | -| `shape` vs `mode`/content | Content axis | 🔴 misalignment | screenshot; Figma COUPLES shape to content (Person=circle), WC treats `shape` as INDEPENDENT — Figma can't express a square person | -| `icon`/`fallbackIcon` | Person/Object Icon (INSTANCE_SWAP) | ❌ can't map | figma-props; icon name not readable | -| `image` (slot) | Type=Image | ❌ can't map | source; slotted image | -| `badge` (slot) | Badge (BOOLEAN) | ⚠️ partial | figma-props; presence only, not content | -| — | Optional Border (BOOLEAN) | 🔴 misalignment | figma-props; **Figma-only**, no WC prop | -| `accessible*` | — | ❌ can't map | source | +## 2. Input — ui5-input (node 148569:1004) + +**Works (screenshot-verified):** +- `value` ← ✏️ Typed Text, `placeholder` ← ✏️ Placeholder. +- `value-state` ← Value State (None/Negative/Critical/Positive/Information, 1:1). +- `disabled` / `readonly` ← Interaction State. +- `valueStateMessage` ← nested "Input Message Popover" text (gated on Message Popover boolean). + +**Doesn't work:** +- `icon` slot ← 2nd Action — slotted/instance-swap, not readable. +- `showClearIcon` ← Trailing Action — approximated only (Trailing Action is generic). + +**Assumed — check manually:** +- `valueStateMessage` slot: published + parses, but **not Dev-Mode-confirmed** to render cleanly. It only appears on **Active** interaction-state variants in Figma. Please check an Active variant shows a clean `
`. + +**Misalignment:** +- `Content` (Placeholder vs Typed Text) — Figma-only display toggle; can't gate which text emits, so both `value` and `placeholder` are always emitted. +- `Description Text` (+✏️) — Figma-only, no ui5-input equivalent. + +--- + +## 3. CheckBox — ui5-checkbox (node 154589:905) + +**Works (screenshot-verified):** +- `text` ← ✏️ Text (gated by Label boolean). +- `checked` ← Check=Checked. +- `value-state` ← Value State (1:1). +- `disabled` / `readonly` / `displayOnly` ← Interaction State (Display Only exists in Figma → maps 1:1). + +**Doesn't work:** — + +**Assumed:** nothing — all verified. + +**Misalignment:** +- `indeterminate` ← Check=Tristate — approximation: WC `indeterminate` is independent of `checked`, but the single Figma "Tristate" can't express both at once. + +--- + +## 4. RadioButton — ui5-radio-button (node 154597:1967) + +**Works (screenshot-verified):** +- `text` ← ✏️ Text (gated by Label), `checked` ← Selected, `value-state` ← Value State (1:1), `disabled`/`readonly` ← Interaction State. + +**Doesn't work:** — + +**Assumed:** nothing — all verified. + +**Misalignment:** none. Cleanest component — every visual prop maps. (`name`/`value` are form-grouping, app-level, correctly absent from Figma.) + +--- + +## 5. StepInput — ui5-step-input (node 148569:1727) + +**Works (screenshot-verified):** +- `value` ← ✏️ Value, `value-state` ← Value State (1:1), `disabled`/`readonly` ← Interaction State. + +**Doesn't work:** +- +/- button icons — instance-swaps (Subtract/Add Button → Icon), names unreadable. +- `valueStateMessage` ← Message Popover — nested text, not currently mapped. +- `min`/`max`/`step` — behavioral, not modelled in Figma (expected). + +**Assumed:** nothing — all verified. + +**Misalignment:** `Description Text` (+✏️) — Figma-only, no equivalent. + +--- + +## 6. MessageStrip — ui5-message-strip (node 910:2517) + +**Works (screenshot-verified + CSS-token-confirmed):** +- `design` ← Value State — semantic 1:1 (Information/Positive/Critical/Negative). +- Custom colours: single `Color` axis → `design="ColorSet1|ColorSet2" color-scheme="1".."10"`. Direction confirmed via WC CSS tokens: Figma "Indication N" ↔ `--sapIndicationColor_N` = ColorSet1; "Nb" ↔ private set-2 tokens = ColorSet2. +- `hide-icon` ← Icon=False, `hide-close-button` ← Close Button=False. + +**Doesn't work:** +- message text — default-slot content (placeholder). +- `icon` slot ← Icon (INSTANCE) — slotted custom icon. + +**Assumed:** nothing — direction was the open question, now confirmed. + +**Misalignment:** React variant reaches ColorSet1 + `color-scheme` only — the React parser can't merge two axes into one `design`, so **ColorSet2 is unreachable in React** (works fully in WC). + +--- + +## 7. Select — ui5-select (node 181557:7507) + +**Works (screenshot-verified, via nested Input):** +- `disabled` / `readonly` / `value-state` — only via the embedded Input instance's states. + +**Doesn't work:** +- `options` — slotted `ui5-option`s; Figma models a closed Input with no readable option list. +- `icon`, `label` slot, `textSeparator` — not readable / not modelled. + +**Assumed:** nothing — verified (screenshot shows only Form Factor × Drop-Down axes). + +**Misalignment:** +- `Drop-Down` (True/False) — Figma-only runtime open state, no WC prop. +- The Select component itself has **no** Value State / Interaction State axes (those live only on the nested Input) — owner should add them like Input/CheckBox. + +--- + +## 8. SegmentedButton — ui5-segmented-button (node 91702:11986) + +**Works (screenshot-verified):** +- segment count ← 3rd/4th/5th Button booleans (adds/removes placeholder items). + +**Doesn't work:** +- segment labels/icons — live in Figma slots (⿻ Text/Icon Segments), not readable → placeholder labels (Option 1..5). +- selected segment — not a readable prop; first item marked `selected` as default. + +**Assumed:** nothing — verified. + +**Misalignment:** `Type` (Text/Icon) — item content type is per-item in the WC (slotted items), but a component-level axis in Figma; adds nothing dynamic without readable content. + +--- + +## 9. Switch — ui5-switch (node 24087:10369) + +**Works (screenshot-verified):** +- `checked` ← Checked, `disabled` ← Interaction State=Disabled. + +**Doesn't work:** +- `design` (Textual/Graphical) — **NOT mappable.** Screenshot showed ALL Figma switches render icons (✓/✗), i.e. all are effectively `Graphical`. (Earlier `Type→design` mapping was WRONG and was removed.) +- `textOn`/`textOff` — not modelled in Figma. + +**Assumed:** nothing — the design question was resolved by screenshot. + +**Misalignment:** +- Figma `Type` (Non-Semantic/Semantic) = colour semantics (neutral vs green/red); ui5-switch has **no property** for this. +- `readonly` — WC has it; the Figma Switch has no Read Only state. + +--- + +## 10. Link — ui5-link (node 187:305) + +**Works (screenshot-verified):** +- `design` ← Type — Emphasized→Emphasized, Subtle→Subtle, Regular→Default (confirmed by weight/colour); Icon Link→Default. +- `disabled` ← Interaction State, label text ← ✏️ Text. + +**Doesn't work:** +- `icon`/`endIcon` ← Icon (instance-swap) + Icon Position — name unreadable. + +**Assumed:** nothing — verified. + +**Misalignment:** Figma models icon-only / position as `Type=Icon Link` + `Icon Position` variants; the WC uses `icon`/`endIcon` slots — no clean bridge. + +--- + +## 11. Avatar — ui5-avatar (node 573:3623) + +**Works (screenshot-verified):** +- `size` ← Size (XS–XL, 1:1). +- `color-scheme` ← Color (1..10 → Accent1..10; Transparent/Placeholder 1:1). +- `shape` ← Content — **screenshot-verified**: Person column renders circles, Object column renders squares. +- `disabled` ← Interaction State=Disabled. + +**Doesn't work:** +- `icon`/`fallbackIcon` ← Person/Object Icon (instance-swap) — name unreadable. +- `image` slot — slotted image, not readable. +- `badge` slot ← Badge boolean — presence only, not content. +- Color=Image/Tile — no `color-scheme` equivalent. + +**Assumed — check manually:** +- `initials` ← ✏️ Initials: emitted **regardless of Type** (parser can't gate one axis's attr on another), so it appears even on Image/Icon avatars. Check it's acceptable / consumer removes it there. + +**Misalignment:** +- `mode` (Image/Decorative/Interactive = a11y role) vs Figma `Type` (Image/Icon/Initials = content source) — different concepts. +- shape/content coupling: Figma couples shape to Content (Person=circle); the WC treats `shape` as independent → Figma can't express a square person avatar. +- `Optional Border` (BOOLEAN) — Figma-only, no WC prop. --- -## Cross-cutting findings (recurring misalignments) - -1. **Instance-swap icons** (Button, Link, StepInput, Avatar) — the selected icon's registry - name is never readable from a swapped instance. Blocks every `icon=`/`endIcon=` mapping. - *Fix: owner adds an `Icon Name` text prop, or generate per-icon Code Connect entries.* -2. **Slotted content** (MessageStrip text, Select options, SegmentedButton labels, Input/StepInput - value-state message) — light-DOM projection isn't a readable prop. -3. **Figma-only properties with no WC equivalent** — Button `Toggled`, Input/StepInput - `Description Text`, Avatar `Optional Border`, Select `Drop-Down`. These are pure Figma-side - modelling that the component API doesn't have. -4. **WC props absent from Figma** — Switch `readonly`, Select's own Value/Interaction State - (only on nested Input). Owner should add the axes. -5. **Concept mismatches** — Avatar `mode` (a11y role) vs Figma `Type` (content source); - Avatar shape/content coupling; Link icon-as-Type vs icon-as-slot. -6. **Parser asymmetry** — the React parser can't merge two Figma axes into one prop, so - MessageStrip ColorSet2 is unreachable in React (works in WC). - -## Open TODOs surfaced by this audit -- **Switch `design`** — RESOLVED: verified not mappable (screenshot), wrong mapping removed. -- **Input `valueStateMessage`** — RESOLVED: nested popover text now mapped (WC + React). *Verify in Dev Mode on an Active-state variant that the slot renders cleanly (published, not yet Dev-Mode-confirmed).* +## Cross-cutting misalignment classes +1. **Instance-swap icons** (Button, Link, StepInput, Avatar) — selected icon name never readable. *Fix: owner adds `Icon Name` text prop, or generate per-icon Code Connect entries.* +2. **Slotted content** (MessageStrip text, Select options, SegmentedButton labels, StepInput message) — light-DOM projection, not a readable prop. +3. **Figma-only props, no WC equivalent** — Button `Toggled`, Input/StepInput `Description Text`, Avatar `Optional Border`, Select `Drop-Down`. +4. **WC props absent from Figma** — Switch `readonly`, Select's own Value/Interaction State. +5. **Concept mismatches** — Avatar `mode` vs Figma `Type`; Avatar shape/content coupling; Link icon-as-Type vs icon-as-slot; Switch `design` vs Figma colour-semantics `Type`. +6. **Parser asymmetry** — React parser can't merge two axes into one prop → MessageStrip ColorSet2 unreachable in React (works in WC). + +## Open items needing manual Dev-Mode check +- **Input `valueStateMessage`** — published; confirm the slot renders cleanly on an Active-state variant. +- **Avatar `initials`** — confirm ungated emission is acceptable. diff --git a/packages/main/src/Button.figma.ts b/packages/main/src/Button.figma.ts index dd07ed915a2c2..8787a44590bf3 100644 --- a/packages/main/src/Button.figma.ts +++ b/packages/main/src/Button.figma.ts @@ -59,17 +59,18 @@ figma.connect( }), // Counter badge → slotted . - // LIMITATION: design="OverlayText" and text="1" are hardcoded. Figma has - // no badge-design enum (only a Counter Badge boolean) and the counter - // number lives in an unexposed nested text layer, so neither the design - // (Overlay vs Inline) nor the number can be read dynamically. - // See FIGMA_CODE_CONNECT.md § "Badge design/text is not dynamic". - counterBadge: figma.boolean("Counter Badge", { - true: html``, - false: "", + // Counter Badge is a Figma VARIANT (True/False), NOT a boolean — must use + // figma.enum (figma.boolean silently fails to match a variant → no badge). + // LIMITATION: design="OverlayText" is hardcoded (Figma has no badge-design + // enum) and text is hardcoded to "72" to match the Figma nested layer, + // which isn't readable dynamically. See FIGMA_CODE_CONNECT.md § Button. + counterBadge: figma.enum("Counter Badge", { + True: html``, + False: "", }), // Attention badge → slotted attention dot. + // Attention Badge IS a real Figma BOOLEAN. attentionBadge: figma.boolean("Attention Badge", { true: html``, false: "", diff --git a/packages/main/src/Button.figma.tsx b/packages/main/src/Button.figma.tsx index e77b2cfddf2a5..1bc832d2f621e 100644 --- a/packages/main/src/Button.figma.tsx +++ b/packages/main/src/Button.figma.tsx @@ -45,15 +45,19 @@ figma.connect( }), // Counter badge presence → child on the `badge` prop. - // LIMITATION: design (OverlayText) and text ("1") are hardcoded — Figma - // exposes only a Counter Badge boolean, no design enum or readable text. - // This mirrors the existing @ui5/webcomponents-react mapping, which also - // only emits the counter (OverlayText) badge. See FIGMA_CODE_CONNECT.md. - badge: figma.boolean("Counter Badge", { - true: ( - - ), - false: undefined, + // Counter Badge is a Figma VARIANT (True/False), NOT a boolean — use + // figma.enum. LIMITATION: design (OverlayText) and text ("72", matching + // the Figma nested layer) are hardcoded — Figma exposes no design enum or + // readable text. See FIGMA_CODE_CONNECT.md § Button. + // + // REACT ASYMMETRY: the `badge` prop can only reference ONE Figma axis (the + // parser rejects compound placeholders like `counter ?? attention`), so + // React drives `badge` from Counter Badge only. The Attention Badge + // (a separate Figma boolean) is NOT expressible here — the WC mapping + // (Button.figma.ts) emits both. See findings § Button. + badge: figma.enum("Counter Badge", { + True: , + False: undefined, }), }, example: ({ label, design, disabled, badge }) => ( From 1a5b58527ae6cc6578dde0ae40d00c93462df5f6 Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Tue, 11 Aug 2026 10:45:02 +0300 Subject: [PATCH 10/28] docs(figma): make Button badge text=72 hardcoding explicit Clarify in findings + code comments that badge text is hardcoded to '72' and NOT read from Figma (count lives in an unexposed nested layer), so it won't track changes to the number in the design. Comment-only; no mapping change, no republish needed. React keeps badge on Counter Badge (attention badge unreachable in React, by design). --- packages/main/FIGMA_CODE_CONNECT_FINDINGS.md | 3 ++- packages/main/src/Button.figma.ts | 7 ++++--- packages/main/src/Button.figma.tsx | 6 +++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md index f8723f80dfd33..ff6426ab7932b 100644 --- a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md +++ b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md @@ -27,7 +27,8 @@ props are never modelled in the kit. **Doesn't work:** - `icon` — instance-swap, name unreadable → hardcoded `icon="globe"`. -- badge `design`/`text` — hardcoded (`OverlayText`, `text="72"`). Figma has no badge-design enum and the count lives in an unexposed nested layer, so neither is readable. Text hardcoded to "72" to match the Figma layer. +- badge `design` — hardcoded `OverlayText`; Figma has no badge-design enum. +- **badge `text` — HARDCODED to `"72"`. NOT read from Figma.** The count lives in an unexposed nested text layer that Code Connect cannot read, so the snippet always emits `text="72"` regardless of what number the Figma design shows. Chosen to match the current Figma layer; it will NOT track changes to the number in Figma. *Owner fix: expose the badge count as a readable component text prop.* - `endIcon` — no Figma equivalent. **Assumed:** nothing — badge fixes verified against live props; re-check in Dev Mode after this fix. diff --git a/packages/main/src/Button.figma.ts b/packages/main/src/Button.figma.ts index 8787a44590bf3..490dc72728fa5 100644 --- a/packages/main/src/Button.figma.ts +++ b/packages/main/src/Button.figma.ts @@ -61,9 +61,10 @@ figma.connect( // Counter badge → slotted . // Counter Badge is a Figma VARIANT (True/False), NOT a boolean — must use // figma.enum (figma.boolean silently fails to match a variant → no badge). - // LIMITATION: design="OverlayText" is hardcoded (Figma has no badge-design - // enum) and text is hardcoded to "72" to match the Figma nested layer, - // which isn't readable dynamically. See FIGMA_CODE_CONNECT.md § Button. + // HARDCODED: design="OverlayText" (Figma has no badge-design enum) and + // text="72". The count is NOT read from Figma — it lives in an unexposed + // nested text layer, so text="72" is static and won't track the Figma + // number. See FIGMA_CODE_CONNECT_FINDINGS.md § Button. counterBadge: figma.enum("Counter Badge", { True: html``, False: "", diff --git a/packages/main/src/Button.figma.tsx b/packages/main/src/Button.figma.tsx index 1bc832d2f621e..0ab45c7a0c189 100644 --- a/packages/main/src/Button.figma.tsx +++ b/packages/main/src/Button.figma.tsx @@ -46,9 +46,9 @@ figma.connect( // Counter badge presence → child on the `badge` prop. // Counter Badge is a Figma VARIANT (True/False), NOT a boolean — use - // figma.enum. LIMITATION: design (OverlayText) and text ("72", matching - // the Figma nested layer) are hardcoded — Figma exposes no design enum or - // readable text. See FIGMA_CODE_CONNECT.md § Button. + // figma.enum. HARDCODED: design (OverlayText) and text="72". The count is + // NOT read from Figma (unexposed nested layer) — text="72" is static and + // won't track the Figma number. See FIGMA_CODE_CONNECT_FINDINGS.md § Button. // // REACT ASYMMETRY: the `badge` prop can only reference ONE Figma axis (the // parser rejects compound placeholders like `counter ?? attention`), so From ff03df83833d9f41cbbc86f374f9cca8dbf68a18 Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Tue, 11 Aug 2026 10:51:47 +0300 Subject: [PATCH 11/28] feat(figma): Button badge design from Form Factor (Compact/Cozy) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Design rule per kit owners: Compact → InlineText, Cozy → OverlayText. Badge design was hardcoded OverlayText; now driven by the Form Factor axis (cross-axis: presence from Counter Badge enum, design from Form Factor enum nested in the badge template). Applied in WC + React. Documented as an assumption (not visually re-verified). text=72 stays hardcoded (unreadable nested layer). --- packages/main/FIGMA_CODE_CONNECT_FINDINGS.md | 9 ++++--- packages/main/src/Button.figma.ts | 11 ++++---- packages/main/src/Button.figma.tsx | 27 +++++++++++++------- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md index ff6426ab7932b..2f8413ce1ad44 100644 --- a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md +++ b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md @@ -25,13 +25,15 @@ props are never modelled in the kit. - Counter badge presence ← Counter Badge (WC + React). - Attention badge presence ← Attention Badge (**WC only** — see misalignment). +**Works (ASSUMED — design rule, not visually re-verified):** +- badge `design` ← Form Factor: **Compact → InlineText, Cozy → OverlayText** (per kit owners). Applied in both WC + React. *Please confirm in Dev Mode by toggling Form Factor with a counter badge on.* + **Doesn't work:** - `icon` — instance-swap, name unreadable → hardcoded `icon="globe"`. -- badge `design` — hardcoded `OverlayText`; Figma has no badge-design enum. -- **badge `text` — HARDCODED to `"72"`. NOT read from Figma.** The count lives in an unexposed nested text layer that Code Connect cannot read, so the snippet always emits `text="72"` regardless of what number the Figma design shows. Chosen to match the current Figma layer; it will NOT track changes to the number in Figma. *Owner fix: expose the badge count as a readable component text prop.* +- **badge `text` — HARDCODED to `"72"`. NOT read from Figma.** The count lives in an unexposed nested text layer that Code Connect cannot read, so the snippet always emits `text="72"` regardless of what number the Figma design shows. It will NOT track changes to the number in Figma. *Owner fix: expose the badge count as a readable component text prop.* - `endIcon` — no Figma equivalent. -**Assumed:** nothing — badge fixes verified against live props; re-check in Dev Mode after this fix. +**Assumed — check manually:** badge `design` ← Form Factor (Compact→InlineText, Cozy→OverlayText). Design rule per owners, applied but not visually re-verified. **Fixed 2026-08-11 (were bugs):** - `Counter Badge` is a Figma **VARIANT** (True/False), not a boolean — was `figma.boolean` (silently didn't match → no badge). Now `figma.enum`. @@ -225,5 +227,6 @@ props are never modelled in the kit. 6. **Parser asymmetry** — React parser can't merge two axes into one prop → MessageStrip ColorSet2 unreachable in React (works in WC). ## Open items needing manual Dev-Mode check +- **Button badge `design`** — confirm Compact→InlineText / Cozy→OverlayText renders correctly (design-rule assumption). - **Input `valueStateMessage`** — published; confirm the slot renders cleanly on an Active-state variant. - **Avatar `initials`** — confirm ungated emission is acceptable. diff --git a/packages/main/src/Button.figma.ts b/packages/main/src/Button.figma.ts index 490dc72728fa5..42f893e9dab33 100644 --- a/packages/main/src/Button.figma.ts +++ b/packages/main/src/Button.figma.ts @@ -61,12 +61,13 @@ figma.connect( // Counter badge → slotted . // Counter Badge is a Figma VARIANT (True/False), NOT a boolean — must use // figma.enum (figma.boolean silently fails to match a variant → no badge). - // HARDCODED: design="OverlayText" (Figma has no badge-design enum) and - // text="72". The count is NOT read from Figma — it lives in an unexposed - // nested text layer, so text="72" is static and won't track the Figma - // number. See FIGMA_CODE_CONNECT_FINDINGS.md § Button. + // ASSUMPTION (design rule, per kit owners, NOT visually re-verified): + // Form Factor drives the badge design — Compact → InlineText, Cozy → + // OverlayText. HARDCODED: text="72" — the count is NOT read from Figma + // (unexposed nested layer), so it won't track the Figma number. + // See FIGMA_CODE_CONNECT_FINDINGS.md § Button. counterBadge: figma.enum("Counter Badge", { - True: html``, + True: html``, False: "", }), diff --git a/packages/main/src/Button.figma.tsx b/packages/main/src/Button.figma.tsx index 0ab45c7a0c189..a932470bf52ca 100644 --- a/packages/main/src/Button.figma.tsx +++ b/packages/main/src/Button.figma.tsx @@ -46,17 +46,26 @@ figma.connect( // Counter badge presence → child on the `badge` prop. // Counter Badge is a Figma VARIANT (True/False), NOT a boolean — use - // figma.enum. HARDCODED: design (OverlayText) and text="72". The count is - // NOT read from Figma (unexposed nested layer) — text="72" is static and - // won't track the Figma number. See FIGMA_CODE_CONNECT_FINDINGS.md § Button. + // figma.enum. ASSUMPTION (design rule, per kit owners, NOT visually + // re-verified): Form Factor drives badge design — Compact → InlineText, + // Cozy → OverlayText. HARDCODED: text="72" — count is NOT read from Figma + // (unexposed nested layer), won't track the Figma number. // - // REACT ASYMMETRY: the `badge` prop can only reference ONE Figma axis (the - // parser rejects compound placeholders like `counter ?? attention`), so - // React drives `badge` from Counter Badge only. The Attention Badge - // (a separate Figma boolean) is NOT expressible here — the WC mapping - // (Button.figma.ts) emits both. See findings § Button. + // REACT ASYMMETRY: the `badge` prop can only reference ONE Figma axis for + // PRESENCE (parser rejects compound placeholders like `counter ?? + // attention`), so React drives `badge` from Counter Badge only. The + // Attention Badge (a separate Figma boolean) is NOT expressible here — the + // WC mapping (Button.figma.ts) emits both. See findings § Button. badge: figma.enum("Counter Badge", { - True: , + True: ( + + ), False: undefined, }), }, From b6e7ce8be03fb736849e939437779cb7727f1dd6 Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Tue, 11 Aug 2026 14:51:38 +0300 Subject: [PATCH 12/28] revert(figma): remove Input valueStateMessage mapping (broken output) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The nested-popover text can't be emitted into a
wrapper: a figma.* call nested in a template literal / JSX emits VERBATIM (Dev Mode printed the figma.nestedProps(...) source instead of the value). It also only exists on Active-state variants. Reverted to the clean Input mapping; documented as not-mappable + added a general parser lesson to the findings doc. --- packages/main/FIGMA_CODE_CONNECT_FINDINGS.md | 15 ++++++++--- packages/main/src/Input.figma.ts | 26 +++++++++----------- packages/main/src/Input.figma.tsx | 20 ++++----------- 3 files changed, 28 insertions(+), 33 deletions(-) diff --git a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md index 2f8413ce1ad44..dfb5b5a4527e1 100644 --- a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md +++ b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md @@ -52,14 +52,13 @@ props are never modelled in the kit. - `value` ← ✏️ Typed Text, `placeholder` ← ✏️ Placeholder. - `value-state` ← Value State (None/Negative/Critical/Positive/Information, 1:1). - `disabled` / `readonly` ← Interaction State. -- `valueStateMessage` ← nested "Input Message Popover" text (gated on Message Popover boolean). **Doesn't work:** - `icon` slot ← 2nd Action — slotted/instance-swap, not readable. - `showClearIcon` ← Trailing Action — approximated only (Trailing Action is generic). +- `valueStateMessage` ← Message Popover — **attempted 2026-08-11, REVERTED.** The nested "Input Message Popover" *does* expose a readable ✏️ Text, but it can't be emitted into a `
` wrapper: a `figma.*` call nested inside a template literal / JSX emits **verbatim** (the Dev Mode snippet literally printed `figma.nestedProps(...)` source instead of the value). It also only exists on Active-state variants (unreachable in normal selection). Not practically mappable. -**Assumed — check manually:** -- `valueStateMessage` slot: published + parses, but **not Dev-Mode-confirmed** to render cleanly. It only appears on **Active** interaction-state variants in Figma. Please check an Active variant shows a clean `
`. +**Assumed:** nothing. **Misalignment:** - `Content` (Placeholder vs Typed Text) — Figma-only display toggle; can't gate which text emits, so both `value` and `placeholder` are always emitted. @@ -228,5 +227,13 @@ props are never modelled in the kit. ## Open items needing manual Dev-Mode check - **Button badge `design`** — confirm Compact→InlineText / Cozy→OverlayText renders correctly (design-rule assumption). -- **Input `valueStateMessage`** — published; confirm the slot renders cleanly on an Active-state variant. - **Avatar `initials`** — confirm ungated emission is acceptable. + +## Parser lesson (applies to all mappings) +A `figma.*` call nested inside a template literal (WC `html\`\``) or inside JSX +emits **verbatim** — the generated snippet prints the source text (e.g. +`figma.nestedProps(...)`) instead of the resolved value. This passes dry-run +validation (it *parses*) but produces broken output. Any value that needs a +`figma.*` read must be resolved into a top-level prop and referenced as a plain +`${prop}` — it cannot be wrapped in surrounding markup in the same expression. +This is why slot-wrapped nested reads (Input valueStateMessage) are not mappable. diff --git a/packages/main/src/Input.figma.ts b/packages/main/src/Input.figma.ts index f1c76cb26c301..726c8635b7fee 100644 --- a/packages/main/src/Input.figma.ts +++ b/packages/main/src/Input.figma.ts @@ -32,20 +32,18 @@ figma.connect( // Text properties. placeholder: figma.string("✏️ Placeholder"), value: figma.string("✏️ Typed Text"), - // Value-state message: gate the slot markup on the Message Popover - // boolean, reading the readable ✏️ Text from the nested "Input Message - // Popover" instance. Resolved HERE (not via a template ternary, which the - // HTML parser rejects). - msgSlot: figma.boolean("Message Popover", { - true: html`
${figma.nestedProps("Input Message Popover", { text: figma.string("✏️ Text") }).text}
`, - false: "", - }), }, - // CANNOT MAP (see FIGMA_CODE_CONNECT.md § Input): the `Content` variant - // (Placeholder vs Typed Text) can't pick which text prop to emit, so both - // are emitted; `Trailing Action`, `2nd Action` reference slotted icon - // content with no readable value; `Description Text` has no ui5-input attr. - example: ({ valueState, stateAttr, placeholder, value, msgSlot }) => - html`${msgSlot}`, + // CANNOT MAP (see FIGMA_CODE_CONNECT_FINDINGS.md § Input): + // - `Content` (Placeholder vs Typed Text) can't pick which text to emit, + // so both are emitted; + // - `Trailing Action`, `2nd Action` reference slotted icon content; + // - `Description Text` has no ui5-input attr; + // - valueStateMessage: the nested "Input Message Popover" DOES expose a + // readable ✏️ Text, BUT it cannot be emitted into a `
` + // wrapper — a figma.* call nested inside a template literal emits + // VERBATIM (prints the source, not the value). It also only exists on + // Active-state variants. Not practically mappable — omitted. + example: ({ valueState, stateAttr, placeholder, value }) => + html``, } ); diff --git a/packages/main/src/Input.figma.tsx b/packages/main/src/Input.figma.tsx index 4908b52da2d79..762070f9fba9d 100644 --- a/packages/main/src/Input.figma.tsx +++ b/packages/main/src/Input.figma.tsx @@ -34,28 +34,18 @@ figma.connect( }), placeholder: figma.string("✏️ Placeholder"), value: figma.string("✏️ Typed Text"), - // Value-state message from the nested "Input Message Popover" instance; - // gate the whole slot element on the Message Popover boolean (the parser - // rejects a ternary in the example, so resolve it into the prop here). - valueStateMessage: figma.boolean("Message Popover", { - true: ( -
- {figma.nestedProps("Input Message Popover", { - text: figma.string("✏️ Text"), - }).text} -
- ), - false: undefined, - }), + // valueStateMessage NOT mapped: the nested "Input Message Popover" text + // can't be emitted into a slot wrapper (figma.* nested in JSX/template + // emits verbatim), and it only exists on Active-state variants. + // See FIGMA_CODE_CONNECT_FINDINGS.md § Input. }, - example: ({ value, placeholder, valueState, disabled, readonly, valueStateMessage }) => ( + example: ({ value, placeholder, valueState, disabled, readonly }) => ( ), } From 2cbbd7e7b21cc4a814dbdc431641696806721a9b Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Tue, 11 Aug 2026 15:11:14 +0300 Subject: [PATCH 13/28] feat(figma): map Input valueStateMessage (correct top-level-prop pattern) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dev-Mode-confirmed working in WC + React. The nested 'Input Message Popover' text is read via a TOP-LEVEL figma.nestedProps prop and referenced as the resolved value — the earlier failure was from inlining the figma.* call (emits verbatim). Slot is always emitted (gating + resolved-text can't coexist). Corrected the parser-lesson note in the findings doc accordingly. --- packages/main/FIGMA_CODE_CONNECT_FINDINGS.md | 19 ++++++++++------- packages/main/src/Input.figma.ts | 22 +++++++++++++------- packages/main/src/Input.figma.tsx | 15 ++++++++----- 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md index dfb5b5a4527e1..e919af7fcc6a8 100644 --- a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md +++ b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md @@ -52,11 +52,11 @@ props are never modelled in the kit. - `value` ← ✏️ Typed Text, `placeholder` ← ✏️ Placeholder. - `value-state` ← Value State (None/Negative/Critical/Positive/Information, 1:1). - `disabled` / `readonly` ← Interaction State. +- `valueStateMessage` ← nested "Input Message Popover" ✏️ Text (**Dev-Mode-confirmed**, WC + React). Read via a TOP-LEVEL `figma.nestedProps` prop and referenced as the resolved `${msg.text}`. NOTE: the slot is ALWAYS emitted — it can't be gated on the Message Popover boolean without re-breaking the resolved text (gating + resolved-text can't coexist); on non-popover variants the text resolves empty. **Doesn't work:** - `icon` slot ← 2nd Action — slotted/instance-swap, not readable. - `showClearIcon` ← Trailing Action — approximated only (Trailing Action is generic). -- `valueStateMessage` ← Message Popover — **attempted 2026-08-11, REVERTED.** The nested "Input Message Popover" *does* expose a readable ✏️ Text, but it can't be emitted into a `
` wrapper: a `figma.*` call nested inside a template literal / JSX emits **verbatim** (the Dev Mode snippet literally printed `figma.nestedProps(...)` source instead of the value). It also only exists on Active-state variants (unreachable in normal selection). Not practically mappable. **Assumed:** nothing. @@ -230,10 +230,13 @@ props are never modelled in the kit. - **Avatar `initials`** — confirm ungated emission is acceptable. ## Parser lesson (applies to all mappings) -A `figma.*` call nested inside a template literal (WC `html\`\``) or inside JSX -emits **verbatim** — the generated snippet prints the source text (e.g. -`figma.nestedProps(...)`) instead of the resolved value. This passes dry-run -validation (it *parses*) but produces broken output. Any value that needs a -`figma.*` read must be resolved into a top-level prop and referenced as a plain -`${prop}` — it cannot be wrapped in surrounding markup in the same expression. -This is why slot-wrapped nested reads (Input valueStateMessage) are not mappable. +A `figma.*` call **inlined** inside a template literal (WC `html\`\``) or inside a +prop's value expression emits **verbatim** — the generated snippet prints the +source text (e.g. `figma.nestedProps(...)`) instead of the resolved value. This +passes dry-run validation (it *parses*) but produces broken output. The fix: +declare every `figma.*` read as a **top-level prop** and reference it as a plain +`${prop}` / `{prop}` in the example — then it resolves correctly (this is how +Input `valueStateMessage` was fixed after an initial inlined attempt failed). +Consequence: you can insert a resolved value into surrounding markup, but you +can't ALSO gate that same markup on a boolean in the same expression — gating + +resolved-nested-text can't coexist, so such slots emit unconditionally. diff --git a/packages/main/src/Input.figma.ts b/packages/main/src/Input.figma.ts index 726c8635b7fee..6508b4143205f 100644 --- a/packages/main/src/Input.figma.ts +++ b/packages/main/src/Input.figma.ts @@ -32,18 +32,24 @@ figma.connect( // Text properties. placeholder: figma.string("✏️ Placeholder"), value: figma.string("✏️ Typed Text"), + // Value-state message text from the nested "Input Message Popover" + // instance. MUST be a TOP-LEVEL prop (not inlined in the template) — a + // figma.* call nested inside html`` emits verbatim. Referenced below as + // the plain resolved value ${msg.text}. + msg: figma.nestedProps("Input Message Popover", { + text: figma.string("✏️ Text"), + }), }, // CANNOT MAP (see FIGMA_CODE_CONNECT_FINDINGS.md § Input): // - `Content` (Placeholder vs Typed Text) can't pick which text to emit, // so both are emitted; // - `Trailing Action`, `2nd Action` reference slotted icon content; - // - `Description Text` has no ui5-input attr; - // - valueStateMessage: the nested "Input Message Popover" DOES expose a - // readable ✏️ Text, BUT it cannot be emitted into a `
` - // wrapper — a figma.* call nested inside a template literal emits - // VERBATIM (prints the source, not the value). It also only exists on - // Active-state variants. Not practically mappable — omitted. - example: ({ valueState, stateAttr, placeholder, value }) => - html``, + // - `Description Text` has no ui5-input attr. + // NOTE: the value-state message slot is ALWAYS emitted (can't be gated on + // the Message Popover boolean without re-breaking the text — gating + + // resolved-text can't coexist). The text only has meaningful content on + // variants where the popover exists; elsewhere it resolves to empty. + example: ({ valueState, stateAttr, placeholder, value, msg }) => + html`
${msg.text}
`, } ); diff --git a/packages/main/src/Input.figma.tsx b/packages/main/src/Input.figma.tsx index 762070f9fba9d..832477bb09686 100644 --- a/packages/main/src/Input.figma.tsx +++ b/packages/main/src/Input.figma.tsx @@ -34,18 +34,23 @@ figma.connect( }), placeholder: figma.string("✏️ Placeholder"), value: figma.string("✏️ Typed Text"), - // valueStateMessage NOT mapped: the nested "Input Message Popover" text - // can't be emitted into a slot wrapper (figma.* nested in JSX/template - // emits verbatim), and it only exists on Active-state variants. - // See FIGMA_CODE_CONNECT_FINDINGS.md § Input. + // Value-state message text from the nested "Input Message Popover" + // instance. MUST be a top-level prop (a figma.* call inlined in JSX emits + // verbatim) — referenced below as the resolved {msg.text}. + msg: figma.nestedProps("Input Message Popover", { + text: figma.string("✏️ Text"), + }), }, - example: ({ value, placeholder, valueState, disabled, readonly }) => ( + // NOTE: the value-state message slot is always emitted (can't be gated on + // the Message Popover boolean without re-breaking the resolved text). + example: ({ value, placeholder, valueState, disabled, readonly, msg }) => ( {msg.text}
} /> ), } From 19fb79db90ffd4527a02fb5859c0120b19398471 Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Wed, 12 Aug 2026 09:45:49 +0300 Subject: [PATCH 14/28] feat(figma): map valueStateMessage for StepInput + Select MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same top-level figma.nestedProps pattern as Input, Dev-Mode-confirmed in both WC + React. Select's popover is deeply nested (Drop-Down > Value Message > Input Message Popover) — figma.nestedProps resolves it by name. Slot always emitted; text resolves empty on non-popover variants. --- packages/main/FIGMA_CODE_CONNECT_FINDINGS.md | 3 ++- packages/main/src/Select.figma.ts | 15 +++++++++++++-- packages/main/src/Select.figma.tsx | 12 +++++++++--- packages/main/src/StepInput.figma.ts | 17 ++++++++++++----- packages/main/src/StepInput.figma.tsx | 8 +++++++- 5 files changed, 43 insertions(+), 12 deletions(-) diff --git a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md index e919af7fcc6a8..e5f07fb297a55 100644 --- a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md +++ b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md @@ -100,10 +100,10 @@ props are never modelled in the kit. **Works (screenshot-verified):** - `value` ← ✏️ Value, `value-state` ← Value State (1:1), `disabled`/`readonly` ← Interaction State. +- `valueStateMessage` ← nested "Input Message Popover" ✏️ Text (same top-level-prop pattern as Input; Dev-Mode-confirmed). **Doesn't work:** - +/- button icons — instance-swaps (Subtract/Add Button → Icon), names unreadable. -- `valueStateMessage` ← Message Popover — nested text, not currently mapped. - `min`/`max`/`step` — behavioral, not modelled in Figma (expected). **Assumed:** nothing — all verified. @@ -133,6 +133,7 @@ props are never modelled in the kit. **Works (screenshot-verified, via nested Input):** - `disabled` / `readonly` / `value-state` — only via the embedded Input instance's states. +- `valueStateMessage` ← nested "Input Message Popover" ✏️ Text (deeply nested under Drop-Down > Value Message; `figma.nestedProps` resolves it by name — Dev-Mode-confirmed). **Doesn't work:** - `options` — slotted `ui5-option`s; Figma models a closed Input with no readable option list. diff --git a/packages/main/src/Select.figma.ts b/packages/main/src/Select.figma.ts index 4eedfd704a98b..0aebf1594cef2 100644 --- a/packages/main/src/Select.figma.ts +++ b/packages/main/src/Select.figma.ts @@ -13,12 +13,23 @@ import figma, { html } from "@figma/code-connect/html"; figma.connect( "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=181557-7507", { - props: {}, - example: () => + props: { + // Value-state message text from the nested "Input Message Popover" + // instance (deeply nested under Drop-Down > Value Message). Same pattern + // as Input — top-level prop, referenced as the resolved ${msg.text}. + msg: figma.nestedProps("Input Message Popover", { + text: figma.string("✏️ Text"), + }), + }, + // Options are a slotted Input instance with no readable option list, so the + // options are representative placeholders. valueStateMessage slot is always + // emitted (text resolves empty on non-popover variants). + example: ({ msg }) => html` Option 1 Option 2 Option 3 +
${msg.text}
`, } ); diff --git a/packages/main/src/Select.figma.tsx b/packages/main/src/Select.figma.tsx index 16690ac2e3264..8ea2ea349c7d9 100644 --- a/packages/main/src/Select.figma.tsx +++ b/packages/main/src/Select.figma.tsx @@ -10,9 +10,15 @@ figma.connect( Select, "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=181557-7507", { - props: {}, - example: () => ( - {msg.text}
}> diff --git a/packages/main/src/StepInput.figma.ts b/packages/main/src/StepInput.figma.ts index 3b1a9056a05f2..f3c0df735f926 100644 --- a/packages/main/src/StepInput.figma.ts +++ b/packages/main/src/StepInput.figma.ts @@ -26,11 +26,18 @@ figma.connect( }), // Numeric value (Figma stores it as a text prop). value: figma.string("✏️ Value"), + // Value-state message text from the nested "Input Message Popover" + // instance (same pattern as Input). MUST be a top-level prop — a figma.* + // call inlined in the template emits verbatim. Referenced as ${msg.text}. + msg: figma.nestedProps("Input Message Popover", { + text: figma.string("✏️ Text"), + }), }, - // CANNOT MAP (FIGMA_CODE_CONNECT.md § StepInput): min/max/step are not in - // Figma; `Message Popover` boolean references slotted content; the +/- - // button icons are instance-swaps (registry problem). - example: ({ valueState, stateAttr, value }) => - html``, + // CANNOT MAP (FIGMA_CODE_CONNECT_FINDINGS.md § StepInput): min/max/step are + // not in Figma; the +/- button icons are instance-swaps (registry problem). + // NOTE: valueStateMessage slot is always emitted (gating + resolved text + // can't coexist); text resolves empty on non-popover variants. + example: ({ valueState, stateAttr, value, msg }) => + html`
${msg.text}
`, } ); diff --git a/packages/main/src/StepInput.figma.tsx b/packages/main/src/StepInput.figma.tsx index 2f397562801dc..6d36a83fbecb3 100644 --- a/packages/main/src/StepInput.figma.tsx +++ b/packages/main/src/StepInput.figma.tsx @@ -33,13 +33,19 @@ figma.connect( Disabled: false, }), value: figma.string("✏️ Value"), + // Value-state message text from the nested "Input Message Popover" + // instance (top-level prop → resolved {msg.text}, same as Input). + msg: figma.nestedProps("Input Message Popover", { + text: figma.string("✏️ Text"), + }), }, - example: ({ value, valueState, disabled, readonly }) => ( + example: ({ value, valueState, disabled, readonly, msg }) => ( {msg.text}
} /> ), } From 995351ead15a1c222d17c1699712d8c37efcbefa Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Wed, 12 Aug 2026 15:40:58 +0300 Subject: [PATCH 15/28] =?UTF-8?q?fix(figma):=20Switch=20design=20mapping?= =?UTF-8?q?=20restored;=20SegmentedButton=20Type=E2=86=92text/icon?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch: restore Type→design (Non-Semantic→Textual, Semantic→Graphical). Earlier pass wrongly removed it — 'Textual' renders neutral icons (not text) so Non-Semantic=Textual is correct; Semantic=Graphical (green/red icons) per WC docs. SegmentedButton: Type axis now switches each item between text and icon form (icon name is placeholder 'home' — instance-swap, unreadable); selected kept on item 1 as representative default. --- packages/main/FIGMA_CODE_CONNECT_FINDINGS.md | 11 ++--- packages/main/src/SegmentedButton.figma.ts | 45 +++++++++++++++----- packages/main/src/SegmentedButton.figma.tsx | 32 +++++++++++--- packages/main/src/Switch.figma.ts | 20 +++++---- packages/main/src/Switch.figma.tsx | 14 +++--- 5 files changed, 87 insertions(+), 35 deletions(-) diff --git a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md index e5f07fb297a55..c3437ede5b59e 100644 --- a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md +++ b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md @@ -166,16 +166,17 @@ props are never modelled in the kit. **Works (screenshot-verified):** - `checked` ← Checked, `disabled` ← Interaction State=Disabled. +- `design` ← Type: **Non-Semantic → Textual, Semantic → Graphical.** Key insight: `Textual` does NOT mean text — with no textOn/textOff it renders check/dash icons in blue/grey (= Non-Semantic); `Graphical` renders positive/negative icons green ✓ / red ✗ (= Semantic), matching the WC docs ("if Graphical, positive/negative icons replace textOn/textOff"). **Doesn't work:** -- `design` (Textual/Graphical) — **NOT mappable.** Screenshot showed ALL Figma switches render icons (✓/✗), i.e. all are effectively `Graphical`. (Earlier `Type→design` mapping was WRONG and was removed.) -- `textOn`/`textOff` — not modelled in Figma. +- `textOn` / `textOff` — not modelled in Figma. -**Assumed:** nothing — the design question was resolved by screenshot. +**Assumed:** nothing. + +**Corrected 2026-08-12:** an earlier pass WRONGLY removed the `Type→design` mapping, concluding "all switches are Graphical" from the screenshot (all render icons). That was wrong — Textual also renders icons (neutral-colored), so Non-Semantic=Textual / Semantic=Graphical is correct. Mapping restored. **Misalignment:** -- Figma `Type` (Non-Semantic/Semantic) = colour semantics (neutral vs green/red); ui5-switch has **no property** for this. -- `readonly` — WC has it; the Figma Switch has no Read Only state. +- `readonly` — WC has it; the Figma Switch has no Read Only state (Interaction State = Regular/Hover/Disabled only). --- diff --git a/packages/main/src/SegmentedButton.figma.ts b/packages/main/src/SegmentedButton.figma.ts index 4b9d293bf394e..5585a3c024448 100644 --- a/packages/main/src/SegmentedButton.figma.ts +++ b/packages/main/src/SegmentedButton.figma.ts @@ -2,10 +2,14 @@ * Web Components Code Connect mapping for the SAP Web UI Kit "Segmented Button". * Node: 91702:11986. Emits with slotted items. * - * LIMITATION: the segment items live in Figma SLOTS (2 fixed + 3rd/4th/5th - * booleans). Their labels/icons are not exposed as readable properties, so the - * emitted items are placeholders. `Type` (Text vs Icon) selects text-vs-icon - * item shape. See FIGMA_CODE_CONNECT.md. + * LIMITATIONS (see FIGMA_CODE_CONNECT_FINDINGS.md § SegmentedButton): + * - Item labels are placeholders — the real text lives in Figma slots + * (⿻ Text/Icon Segments), not readable. + * - Icon NAMES are placeholders ("home") — instance-swap, not readable. + * - `Type` (Text/Icon) IS readable and switches each item between text form + * and icon form. + * - The `selected` item can't be read from Figma; item 1 is marked selected + * as a representative default (does NOT reflect the actually-pressed segment). */ import figma, { html } from "@figma/code-connect/html"; @@ -13,24 +17,43 @@ figma.connect( "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=91702-11986", { props: { - // 3rd/4th/5th optional segments → extra placeholder items. + // Type → text-vs-icon form for the two always-present items. + // Item 1 keeps `selected` (representative default — not readable). + item1: figma.enum("Type", { + Text: html`Option 1`, + Icon: html``, + }), + item2: figma.enum("Type", { + Text: html`Option 2`, + Icon: html``, + }), + // 3rd/4th/5th optional segments — presence from booleans, form from Type. thirdItem: figma.boolean("3rd Button", { - true: html`Option 3`, + true: figma.enum("Type", { + Text: html`Option 3`, + Icon: html``, + }), false: "", }), fourthItem: figma.boolean("4th Button", { - true: html`Option 4`, + true: figma.enum("Type", { + Text: html`Option 4`, + Icon: html``, + }), false: "", }), fifthItem: figma.boolean("5th Button", { - true: html`Option 5`, + true: figma.enum("Type", { + Text: html`Option 5`, + Icon: html``, + }), false: "", }), }, - example: ({ thirdItem, fourthItem, fifthItem }) => + example: ({ item1, item2, thirdItem, fourthItem, fifthItem }) => html` - Option 1 - Option 2 + ${item1} + ${item2} ${thirdItem}${fourthItem}${fifthItem} `, } diff --git a/packages/main/src/SegmentedButton.figma.tsx b/packages/main/src/SegmentedButton.figma.tsx index 973495c49a92c..8258d487bbeb6 100644 --- a/packages/main/src/SegmentedButton.figma.tsx +++ b/packages/main/src/SegmentedButton.figma.tsx @@ -11,23 +11,43 @@ figma.connect( "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=91702-11986", { props: { + // Type → text-vs-icon form. Item 1 keeps `selected` (representative + // default — not readable from Figma). Icon names are placeholders + // ("home") — instance-swap, not readable. + item1: figma.enum("Type", { + Text: Option 1, + Icon: , + }), + item2: figma.enum("Type", { + Text: Option 2, + Icon: , + }), thirdItem: figma.boolean("3rd Button", { - true: Option 3, + true: figma.enum("Type", { + Text: Option 3, + Icon: , + }), false: undefined, }), fourthItem: figma.boolean("4th Button", { - true: Option 4, + true: figma.enum("Type", { + Text: Option 4, + Icon: , + }), false: undefined, }), fifthItem: figma.boolean("5th Button", { - true: Option 5, + true: figma.enum("Type", { + Text: Option 5, + Icon: , + }), false: undefined, }), }, - example: ({ thirdItem, fourthItem, fifthItem }) => ( + example: ({ item1, item2, thirdItem, fourthItem, fifthItem }) => ( - Option 1 - Option 2 + {item1} + {item2} {thirdItem} {fourthItem} {fifthItem} diff --git a/packages/main/src/Switch.figma.ts b/packages/main/src/Switch.figma.ts index b42bde03a290c..1dd1c428aff36 100644 --- a/packages/main/src/Switch.figma.ts +++ b/packages/main/src/Switch.figma.ts @@ -10,11 +10,15 @@ figma.connect( "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=24087-10369", { props: { - // NOTE: Figma "Type" (Non-Semantic/Semantic) is about colour semantics - // (neutral vs green/red), which ui5-switch has NO property for. It does - // NOT map to `design` (Textual/Graphical): screenshot of node 24087:10369 - // shows ALL Figma switches render icons (✓/✗), i.e. all are effectively - // `design="Graphical"`. So `design` cannot be driven by Type — omitted. + // Figma "Type" → design. Non-Semantic (neutral blue/grey ✓/dash icons) → + // Textual; Semantic (green ✓ / red ✗) → Graphical. NOTE: "Textual" does + // NOT mean text — with no textOn/textOff it still renders check/dash icons + // in blue/grey, which is exactly the Non-Semantic group. "Graphical" is + // the positive/negative-icon variant = Semantic. + design: figma.enum("Type", { + "Non-Semantic": 'design="Textual"', + Semantic: 'design="Graphical"', + }), // Checked variant → checked. checked: figma.enum("Checked", { True: "checked", @@ -27,8 +31,8 @@ figma.connect( Hover: "", }), }, - // design (Textual/Graphical), textOn/textOff aren't derivable from Figma. - example: ({ checked, disabled }) => - html``, + // textOn/textOff aren't modelled in Figma — omitted. + example: ({ design, checked, disabled }) => + html``, } ); diff --git a/packages/main/src/Switch.figma.tsx b/packages/main/src/Switch.figma.tsx index c38edff2d103c..bea75b25b49ea 100644 --- a/packages/main/src/Switch.figma.tsx +++ b/packages/main/src/Switch.figma.tsx @@ -11,9 +11,13 @@ figma.connect( "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=24087-10369", { props: { - // NOTE: Figma "Type" (Non-Semantic/Semantic) = colour semantics, which - // ui5-switch has no prop for. It does NOT map to `design`; screenshot of - // node 24087:10369 shows all switches render icons (all `Graphical`). + // Figma "Type" → design. Non-Semantic (neutral ✓/dash icons) → Textual; + // Semantic (green ✓ / red ✗) → Graphical. "Textual" still renders icons + // in blue/grey when no textOn/textOff — matches the Non-Semantic group. + design: figma.enum("Type", { + "Non-Semantic": "Textual", + Semantic: "Graphical", + }), checked: figma.enum("Checked", { True: true, False: false, @@ -24,8 +28,8 @@ figma.connect( Hover: false, }), }, - example: ({ checked, disabled }) => ( - + example: ({ design, checked, disabled }) => ( + ), } ); From 6ba1573f27883b38fe531005df8225d8fa7efbcb Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Wed, 12 Aug 2026 15:44:54 +0300 Subject: [PATCH 16/28] =?UTF-8?q?docs(figma):=20focus=20findings=20on=20Fi?= =?UTF-8?q?gma=E2=86=92code=20gaps,=20drop=20WC-only-prop=20noise?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per user: the useful direction is Figma properties that can't be reflected in code, not WC props missing from Figma. Removed readonly / textOn/textOff / min/max/step / name/value bullets and the 'WC props absent from Figma' cross-cutting class. Fixed the stale Switch entry (design DOES map now). --- packages/main/FIGMA_CODE_CONNECT_FINDINGS.md | 24 ++++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md index c3437ede5b59e..bff3aa999c35e 100644 --- a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md +++ b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md @@ -92,7 +92,7 @@ props are never modelled in the kit. **Assumed:** nothing — all verified. -**Misalignment:** none. Cleanest component — every visual prop maps. (`name`/`value` are form-grouping, app-level, correctly absent from Figma.) +**Misalignment:** none. Cleanest component — every visual Figma property maps. --- @@ -104,11 +104,10 @@ props are never modelled in the kit. **Doesn't work:** - +/- button icons — instance-swaps (Subtract/Add Button → Icon), names unreadable. -- `min`/`max`/`step` — behavioral, not modelled in Figma (expected). **Assumed:** nothing — all verified. -**Misalignment:** `Description Text` (+✏️) — Figma-only, no equivalent. +**Misalignment:** `Description Text` (+✏️) — Figma-only, no ui5-step-input equivalent. --- @@ -168,16 +167,12 @@ props are never modelled in the kit. - `checked` ← Checked, `disabled` ← Interaction State=Disabled. - `design` ← Type: **Non-Semantic → Textual, Semantic → Graphical.** Key insight: `Textual` does NOT mean text — with no textOn/textOff it renders check/dash icons in blue/grey (= Non-Semantic); `Graphical` renders positive/negative icons green ✓ / red ✗ (= Semantic), matching the WC docs ("if Graphical, positive/negative icons replace textOn/textOff"). -**Doesn't work:** -- `textOn` / `textOff` — not modelled in Figma. +**Doesn't work:** — (no Figma-side property goes unreflected.) **Assumed:** nothing. **Corrected 2026-08-12:** an earlier pass WRONGLY removed the `Type→design` mapping, concluding "all switches are Graphical" from the screenshot (all render icons). That was wrong — Textual also renders icons (neutral-colored), so Non-Semantic=Textual / Semantic=Graphical is correct. Mapping restored. -**Misalignment:** -- `readonly` — WC has it; the Figma Switch has no Read Only state (Interaction State = Regular/Hover/Disabled only). - --- ## 10. Link — ui5-link (node 187:305) @@ -219,13 +214,12 @@ props are never modelled in the kit. --- -## Cross-cutting misalignment classes -1. **Instance-swap icons** (Button, Link, StepInput, Avatar) — selected icon name never readable. *Fix: owner adds `Icon Name` text prop, or generate per-icon Code Connect entries.* -2. **Slotted content** (MessageStrip text, Select options, SegmentedButton labels, StepInput message) — light-DOM projection, not a readable prop. -3. **Figma-only props, no WC equivalent** — Button `Toggled`, Input/StepInput `Description Text`, Avatar `Optional Border`, Select `Drop-Down`. -4. **WC props absent from Figma** — Switch `readonly`, Select's own Value/Interaction State. -5. **Concept mismatches** — Avatar `mode` vs Figma `Type`; Avatar shape/content coupling; Link icon-as-Type vs icon-as-slot; Switch `design` vs Figma colour-semantics `Type`. -6. **Parser asymmetry** — React parser can't merge two axes into one prop → MessageStrip ColorSet2 unreachable in React (works in WC). +## Cross-cutting classes — Figma properties that code can't fully reflect +1. **Instance-swap icons** (Button, Link, StepInput, Avatar, SegmentedButton) — the selected icon's name is never readable → hardcoded placeholder name. *Fix: owner adds `Icon Name` text prop, or generate per-icon Code Connect entries.* +2. **Slotted content** (MessageStrip text, Select options, SegmentedButton labels) — light-DOM projection, not a readable prop → placeholder text. +3. **Figma-only props, no WC equivalent** — Button `Toggled`, Input/StepInput `Description Text`, Avatar `Optional Border`, Select `Drop-Down`. Present in Figma, nothing to emit. +4. **Concept mismatches** — Avatar `mode` vs Figma `Type`; Avatar shape/content coupling; Link icon-as-Type vs icon-as-slot. +5. **Parser asymmetry** — React parser can't merge two Figma axes into one prop → MessageStrip ColorSet2 and Button's second badge unreachable in React (both work in WC). ## Open items needing manual Dev-Mode check - **Button badge `design`** — confirm Compact→InlineText / Cozy→OverlayText renders correctly (design-rule assumption). From 7b40a068c75afe3262e136b4979083a289c17cb5 Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Wed, 12 Aug 2026 15:48:04 +0300 Subject: [PATCH 17/28] feat(figma): Link map icon/endIcon from Icon Position MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Icon Position (Left/Right/N/A) → icon (Left) / end-icon (Right) / none. Position is now dynamic; icon name stays a placeholder ('inspect', the kit default) since the Icon instance-swap name isn't readable. WC + React. --- packages/main/FIGMA_CODE_CONNECT_FINDINGS.md | 5 ++--- packages/main/src/Link.figma.ts | 13 ++++++++++--- packages/main/src/Link.figma.tsx | 16 ++++++++++++++-- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md index bff3aa999c35e..ab8789df25123 100644 --- a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md +++ b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md @@ -180,14 +180,13 @@ props are never modelled in the kit. **Works (screenshot-verified):** - `design` ← Type — Emphasized→Emphasized, Subtle→Subtle, Regular→Default (confirmed by weight/colour); Icon Link→Default. - `disabled` ← Interaction State, label text ← ✏️ Text. +- `icon` / `endIcon` ← Icon Position: Left→`icon`, Right→`end-icon`, N/A→neither. POSITION is dynamic. **Doesn't work:** -- `icon`/`endIcon` ← Icon (instance-swap) + Icon Position — name unreadable. +- icon NAME — instance-swap, not readable → hardcoded `"inspect"` placeholder (the position maps, the name does not). **Assumed:** nothing — verified. -**Misalignment:** Figma models icon-only / position as `Type=Icon Link` + `Icon Position` variants; the WC uses `icon`/`endIcon` slots — no clean bridge. - --- ## 11. Avatar — ui5-avatar (node 573:3623) diff --git a/packages/main/src/Link.figma.ts b/packages/main/src/Link.figma.ts index 09e8dc4f780d2..0a98694ad0471 100644 --- a/packages/main/src/Link.figma.ts +++ b/packages/main/src/Link.figma.ts @@ -27,9 +27,16 @@ figma.connect( }), // Link label text (default slot). label: figma.textContent("Text"), + // Icon Position → icon (Left) / end-icon (Right). The icon NAME is a + // placeholder ("inspect", the kit's default) — the Icon instance-swap + // name isn't readable. Left→icon, Right→end-icon, N/A→neither. + iconAttr: figma.enum("Icon Position", { + Left: 'icon="inspect"', + Right: 'end-icon="inspect"', + "N/A": "", + }), }, - // Icon slot (Icon Link type) is instance-swap — name not readable, omitted. - example: ({ design, disabled, label }) => - html`${label}`, + example: ({ design, disabled, label, iconAttr }) => + html`${label}`, } ); diff --git a/packages/main/src/Link.figma.tsx b/packages/main/src/Link.figma.tsx index 7b8f6a58dbce7..b60ca1d16f122 100644 --- a/packages/main/src/Link.figma.tsx +++ b/packages/main/src/Link.figma.tsx @@ -25,9 +25,21 @@ figma.connect( Down: false, }), label: figma.textContent("Text"), + // Icon Position → icon (Left) / endIcon (Right). Name is a placeholder + // ("inspect") — the Icon instance-swap name isn't readable. + icon: figma.enum("Icon Position", { + Left: "inspect", + Right: undefined, + "N/A": undefined, + }), + endIcon: figma.enum("Icon Position", { + Right: "inspect", + Left: undefined, + "N/A": undefined, + }), }, - example: ({ design, disabled, label }) => ( - + example: ({ design, disabled, label, icon, endIcon }) => ( + {label} ), From 29491c89d243b5e2d82371bf771a2885cbc978c9 Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Wed, 12 Aug 2026 15:52:50 +0300 Subject: [PATCH 18/28] docs(figma): Avatar initials verified (Type-conditional in Figma) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dev-Mode-confirmed: the Initials text layer only exists on Type=Initials variants, so initials is emitted only there and omitted on Image/Icon avatars — no gating needed. Removed the assumption; updated open items. --- packages/main/FIGMA_CODE_CONNECT_FINDINGS.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md index ab8789df25123..173767e3bb27c 100644 --- a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md +++ b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md @@ -196,6 +196,7 @@ props are never modelled in the kit. - `color-scheme` ← Color (1..10 → Accent1..10; Transparent/Placeholder 1:1). - `shape` ← Content — **screenshot-verified**: Person column renders circles, Object column renders squares. - `disabled` ← Interaction State=Disabled. +- `initials` ← ✏️ Initials — **Dev-Mode-confirmed**: the Initials text layer only exists on `Type=Initials` variants, so `initials` is emitted only there and correctly omitted on Image/Icon avatars (no gating needed). **Doesn't work:** - `icon`/`fallbackIcon` ← Person/Object Icon (instance-swap) — name unreadable. @@ -203,8 +204,7 @@ props are never modelled in the kit. - `badge` slot ← Badge boolean — presence only, not content. - Color=Image/Tile — no `color-scheme` equivalent. -**Assumed — check manually:** -- `initials` ← ✏️ Initials: emitted **regardless of Type** (parser can't gate one axis's attr on another), so it appears even on Image/Icon avatars. Check it's acceptable / consumer removes it there. +**Assumed:** nothing — all verified. **Misalignment:** - `mode` (Image/Decorative/Interactive = a11y role) vs Figma `Type` (Image/Icon/Initials = content source) — different concepts. @@ -222,7 +222,7 @@ props are never modelled in the kit. ## Open items needing manual Dev-Mode check - **Button badge `design`** — confirm Compact→InlineText / Cozy→OverlayText renders correctly (design-rule assumption). -- **Avatar `initials`** — confirm ungated emission is acceptable. +- **MessageStrip** — deferred: confirm custom-colour variants (Indication N / Nb) emit `design="ColorSet1|2" color-scheme="N"` in WC; decide on the React ColorSet2 gap. ## Parser lesson (applies to all mappings) A `figma.*` call **inlined** inside a template literal (WC `html\`\``) or inside a From aea675396ae6673f90bf26c9532b3faa6843495b Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Wed, 12 Aug 2026 15:57:29 +0300 Subject: [PATCH 19/28] feat(figma): Avatar icon mapping; consolidate to single findings doc - Avatar: Type=Icon -> icon="employee" (WC default placeholder; name is instance-swap, unreadable). WC + React. - Delete stale FIGMA_CODE_CONNECT.md; FIGMA_CODE_CONNECT_FINDINGS.md is the single authoritative doc. - Sweep all .figma.ts/.tsx header references to the findings doc name. --- packages/main/FIGMA_CODE_CONNECT.md | 141 ------------------- packages/main/FIGMA_CODE_CONNECT_FINDINGS.md | 3 +- packages/main/src/Avatar.figma.ts | 18 ++- packages/main/src/Avatar.figma.tsx | 13 +- packages/main/src/Button.figma.ts | 2 +- packages/main/src/Button.figma.tsx | 2 +- packages/main/src/CheckBox.figma.ts | 4 +- packages/main/src/CheckBox.figma.tsx | 2 +- packages/main/src/Input.figma.ts | 2 +- packages/main/src/Input.figma.tsx | 2 +- packages/main/src/Link.figma.ts | 2 +- packages/main/src/Link.figma.tsx | 2 +- packages/main/src/MessageStrip.figma.ts | 2 +- packages/main/src/MessageStrip.figma.tsx | 2 +- packages/main/src/RadioButton.figma.ts | 2 +- packages/main/src/RadioButton.figma.tsx | 2 +- packages/main/src/SegmentedButton.figma.tsx | 2 +- packages/main/src/Select.figma.ts | 2 +- packages/main/src/Select.figma.tsx | 2 +- packages/main/src/StepInput.figma.ts | 2 +- packages/main/src/StepInput.figma.tsx | 2 +- packages/main/src/Switch.figma.ts | 2 +- packages/main/src/Switch.figma.tsx | 2 +- 23 files changed, 45 insertions(+), 170 deletions(-) delete mode 100644 packages/main/FIGMA_CODE_CONNECT.md diff --git a/packages/main/FIGMA_CODE_CONNECT.md b/packages/main/FIGMA_CODE_CONNECT.md deleted file mode 100644 index e2c2bffaae48b..0000000000000 --- a/packages/main/FIGMA_CODE_CONNECT.md +++ /dev/null @@ -1,141 +0,0 @@ -# Figma Code Connect — what maps, what doesn't - -Code Connect mappings for the **SAP Web UI Kit** (file `SILcWzK5uFghKun9jx6D7c`), -published in two variants: **Web Components** (`src/*.figma.ts`) and **React** -(`src/*.figma.tsx`). This file is the short record of, per component, what maps -dynamically in Dev Mode and what doesn't (with the reason + fix). - -**Two axes ignored on every component (by decision):** -- **Form Factor (Compact/Cozy)** — density is a global UI5 setting, not a per-element attribute. -- **Interaction State = Hover/Active/Down/Focus** — visual pseudo-states, no attribute. Only `Disabled`→`disabled` and `Read Only`→`readonly` map. - ---- - -## Button — ui5-button (node 91702:11733) -**works:** partly — `design`, `disabled`, and label text map dynamically. - -**doesn't map:** -- `icon` — Figma instance-swap; the swapped icon's name isn't readable, so hardcoded `icon="globe"`. -- badge — modeled as 2 booleans with no design enum, and the count sits in a nested layer, so `design`/`text` are hardcoded. - -**how to fix:** -- owner adds an `Icon Name` string prop (or Code-Connect the icon set). -- add a single `Badge Design` enum (None/InlineText/OverlayText/AttentionDot) + a `Badge Text` string prop. - -## Input — ui5-input (node 148569:1004) -**works:** yes — `value`, `placeholder`, `value-state` (1:1), `disabled`/`readonly` all map. - -**doesn't map:** -- `Content` (Placeholder vs Typed Text) is a display toggle, so both attrs are always emitted. -- `Trailing Action` / `2nd Action` / `Message Popover` are slotted icon/action/message content with no readable value. -- `Description Text` has no `ui5-input` equivalent. - -**how to fix:** -- cosmetic ones need nothing (consumer deletes the extra attr). -- for the actions, owner exposes the action icon as a prop or Code-Connect the icon set. - -## CheckBox — ui5-checkbox (node 154589:905) -**works:** yes — `text`, `checked`/`indeterminate`, `value-state`, `disabled`/`readonly` all map. - -**doesn't map:** -- `Interaction State = Display Only` — no display-only mode in the WC, approximated as `readonly`. -- Tristate can't express both indeterminate + checked at once. - -**how to fix:** -- acceptable approximations; no owner action needed. - -## RadioButton — ui5-radio-button (node 154597:1967) -**works:** yes — fully (cleanest component): `text`, `checked`, `value-state`, `disabled`/`readonly`. - -**doesn't map:** -- nothing significant — `name`/`value` (form grouping) aren't modeled in Figma, which is expected (app-level, not visual). - -**how to fix:** -- n/a. - -## StepInput — ui5-step-input (node 148569:1727) -**works:** yes — `value`, `value-state`, `disabled`/`readonly` map. - -**doesn't map:** -- `min`/`max`/`step` aren't in Figma (behavioral, not visual). -- +/- button icons are instance-swaps. -- `Message Popover` is slotted nested text. - -**how to fix:** -- min/max/step are an expected gap. -- icons need the icon-set fix (see Button). - -## MessageStrip — ui5-message-strip (node 910:2517) -**works:** yes (WC) — `design` ← Value State (semantic 1:1), `hide-icon`, `hide-close-button`, and the full custom-colour palette: the single `Color` axis (Indication 1..10 / 1b..10b) maps to `design="ColorSet1|ColorSet2" color-scheme="1".."10"` because the `b` suffix already encodes ColorSet2 on the same axis as the scheme number. - -**doesn't map:** -- message text is default-slot content (placeholder). -- React variant reaches ColorSet1 + `color-scheme` only — its parser can't merge two axes into one `design`, so ColorSet2 (the "…b" colours) is unreachable there. - -**how to fix:** -- none for WC. -- for React parity, owner splits Figma `Color` into a ColorSet enum + a scheme number so each maps to one prop. - -## Select — ui5-select (node 181557:7507) -**works:** almost nothing — the Figma Select has no Value State / Interaction State axes to map. - -**doesn't map:** -- options are slotted `ui5-option`s (Figma models a closed Input with no option list). -- `Drop-Down` True/False is a runtime open state, not a prop. -- `value-state`/`disabled`/`readonly` are supported by the WC but absent from the Figma component. - -**how to fix:** -- owner adds Value State + Interaction State variants (like Input/CheckBox) and models options as a proper slot/list. - -## SegmentedButton — ui5-segmented-button (node 91702:11986) -**works:** partly — presence of the 3rd/4th/5th segments maps (adds/removes items). - -**doesn't map:** -- segment labels/icons live in Figma slots (`⿻ Text/Icon Segments`), not readable, so labels are placeholders. -- `Type = Text/Icon` adds nothing without readable content. -- the selected segment isn't a readable prop (first item marked `selected`). - -**how to fix:** -- owner exposes per-segment text as component text props. - -## Switch — ui5-switch (node 24087:10369) -**works:** yes — `checked`, `disabled`, `design` (Type: Non-Semantic→Textual, Semantic→Graphical) map. - -**doesn't map:** -- `textOn`/`textOff` aren't modeled in Figma. -- the on/off icon is an instance-swap. - -**how to fix:** -- owner exposes `textOn`/`textOff` as text props if dynamic labels are wanted. - -## Link — ui5-link (node 187:305) -**works:** yes — `design` (Emphasized/Subtle; Regular & Icon Link → Default), `disabled`, and label text map. - -**doesn't map:** -- the `Icon` (Icon Link type) is an instance-swap — name not readable (ui5-link has an `icon` slot). -- Visited/Down are visual pseudo-states. - -**how to fix:** -- owner adds an `Icon Name` string prop (or Code-Connect the icon set), same as Button. - -## Avatar — ui5-avatar (node 573:3623) -**works:** yes — `size` (XS..XL 1:1), `color-scheme` (Color 1..10 → Accent1..10; Transparent/Placeholder 1:1), and `initials` map. - -**doesn't map:** -- Person/Object icons are instance-swaps. -- Badge is a slot; `Optional Border` has no direct prop. -- Image/Tile colours have no `color-scheme` equivalent. - -**how to fix:** -- owner adds an `Icon Name` string prop for the icon variant; model the badge as a readable prop. - -## Icon — ui5-icon (node 983:5876) — UNPUBLISHABLE -**works:** no — Figma rejects the publish: "corresponding node is not a component or component set". - -**reason:** -- node `983:5876` is a plain frame of ~1400 individual icon components, not a component/set. -- kept as `src/Icon.figma.ts.todo` (outside the publish glob) so it doesn't break the atomic batch publish. - -**how to fix:** -- owner makes it a real component set. -- even then the icon name isn't readable from a single mapping — generate one `figma.connect` per icon emitting its own name, or expose an `Icon Name` string prop on the host component. diff --git a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md index 173767e3bb27c..de36ed2ff0344 100644 --- a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md +++ b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md @@ -197,9 +197,10 @@ props are never modelled in the kit. - `shape` ← Content — **screenshot-verified**: Person column renders circles, Object column renders squares. - `disabled` ← Interaction State=Disabled. - `initials` ← ✏️ Initials — **Dev-Mode-confirmed**: the Initials text layer only exists on `Type=Initials` variants, so `initials` is emitted only there and correctly omitted on Image/Icon avatars (no gating needed). +- `icon` ← Type=Icon → `icon="employee"` (WC default). Emitted only for the Icon type. The POSITION/presence maps; the icon NAME is a placeholder (Person/Object Icon are instance-swaps, not readable). **Doesn't work:** -- `icon`/`fallbackIcon` ← Person/Object Icon (instance-swap) — name unreadable. +- `icon`/`fallbackIcon` NAME ← Person/Object Icon (instance-swap) — name unreadable → placeholder `"employee"` (icon presence maps via Type=Icon; the name does not). - `image` slot — slotted image, not readable. - `badge` slot ← Badge boolean — presence only, not content. - Color=Image/Tile — no `color-scheme` equivalent. diff --git a/packages/main/src/Avatar.figma.ts b/packages/main/src/Avatar.figma.ts index 8f23abbf25a5b..c0c6fdb9a01bb 100644 --- a/packages/main/src/Avatar.figma.ts +++ b/packages/main/src/Avatar.figma.ts @@ -2,7 +2,7 @@ * Web Components Code Connect mapping for the SAP Web UI Kit "Avatar". * Node: 573:3623. Emits . * - * See FIGMA_CODE_CONNECT.md § Avatar. Pseudo-states ignored. + * See FIGMA_CODE_CONNECT_FINDINGS.md § Avatar. Pseudo-states ignored. */ import figma, { html } from "@figma/code-connect/html"; @@ -53,12 +53,20 @@ figma.connect( Active: "", "Toggled Hover": "", }), - // Initials text. NOTE: emitted regardless of Type (parser can't gate one - // axis's attr on another) — consumer removes it for Image/Icon avatars. + // Initials text — the Figma Initials layer only exists on Type=Initials + // variants, so this resolves empty (attribute omitted) on Image/Icon. initials: figma.string("✏️ Initials"), + // Type=Icon → icon="employee" (WC default). Icon NAME is a placeholder — + // Person/Object Icon are instance-swaps, not readable. Emitted only for + // the Icon type. + icon: figma.enum("Type", { + Icon: 'icon="employee"', + Image: "", + Initials: "", + }), }, // Person/Object Icon are instance-swaps; Badge is a slot — omitted. - example: ({ size, colorScheme, shape, disabled, initials }) => - html``, + example: ({ size, colorScheme, shape, disabled, initials, icon }) => + html``, } ); diff --git a/packages/main/src/Avatar.figma.tsx b/packages/main/src/Avatar.figma.tsx index b6bd0da28f999..56571fb1bd59c 100644 --- a/packages/main/src/Avatar.figma.tsx +++ b/packages/main/src/Avatar.figma.tsx @@ -1,6 +1,6 @@ /** * React Code Connect mapping for the SAP Web UI Kit "Avatar". - * Node 573:3623. Mirrors Avatar.figma.ts. See FIGMA_CODE_CONNECT.md. + * Node 573:3623. Mirrors Avatar.figma.ts. See FIGMA_CODE_CONNECT_FINDINGS.md. */ import figma from "@figma/code-connect/react"; // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -46,9 +46,16 @@ figma.connect( "Toggled Hover": false, }), initials: figma.string("✏️ Initials"), + // Type=Icon → icon="employee" (WC default). Name is a placeholder — + // Person/Object Icon are instance-swaps, not readable. Icon type only. + icon: figma.enum("Type", { + Icon: "employee", + Image: undefined, + Initials: undefined, + }), }, - example: ({ size, colorScheme, shape, disabled, initials }) => ( - + example: ({ size, colorScheme, shape, disabled, initials, icon }) => ( + ), } ); diff --git a/packages/main/src/Button.figma.ts b/packages/main/src/Button.figma.ts index 42f893e9dab33..ec645e3e559ce 100644 --- a/packages/main/src/Button.figma.ts +++ b/packages/main/src/Button.figma.ts @@ -52,7 +52,7 @@ figma.connect( // icon as an INSTANCE_SWAP whose swapped icon name cannot be read into a // string unless the Kit's icon components are themselves Code-Connected. // So this only toggles the attribute on/off; it can't reflect which icon - // is selected. See FIGMA_CODE_CONNECT.md § "Icon is not dynamic". + // is selected. See FIGMA_CODE_CONNECT_FINDINGS.md § "Icon is not dynamic". iconAttr: figma.boolean("Icon Left", { true: 'icon="globe"', false: "", diff --git a/packages/main/src/Button.figma.tsx b/packages/main/src/Button.figma.tsx index a932470bf52ca..c6a660c9335fb 100644 --- a/packages/main/src/Button.figma.tsx +++ b/packages/main/src/Button.figma.tsx @@ -10,7 +10,7 @@ * The React parser only reads the import string — @ui5/webcomponents-react does * not need to be installed here for publishing to succeed. * - * NOTE: the same Figma-side limitations documented in FIGMA_CODE_CONNECT.md + * NOTE: the same Figma-side limitations documented in FIGMA_CODE_CONNECT_FINDINGS.md * apply here — the icon name and badge design/text are hardcoded because Figma * does not expose them as readable properties. */ diff --git a/packages/main/src/CheckBox.figma.ts b/packages/main/src/CheckBox.figma.ts index a152389d6817c..b2eaa03f5e8e7 100644 --- a/packages/main/src/CheckBox.figma.ts +++ b/packages/main/src/CheckBox.figma.ts @@ -3,7 +3,7 @@ * Node: 154589:905. Emits . * * Every readable Figma prop mapped; unmappable ones documented in - * FIGMA_CODE_CONNECT.md § CheckBox. Form Factor + Hover ignored. + * FIGMA_CODE_CONNECT_FINDINGS.md § CheckBox. Form Factor + Hover ignored. */ import figma, { html } from "@figma/code-connect/html"; @@ -26,7 +26,7 @@ figma.connect( }), // Interaction State → disabled / readonly. // "Display Only" has NO ui5-checkbox equivalent — approximated as readonly - // (documented in FIGMA_CODE_CONNECT.md § CheckBox). + // (documented in FIGMA_CODE_CONNECT_FINDINGS.md § CheckBox). stateAttr: figma.enum("Interaction State", { Disabled: "disabled", "Read Only": "readonly", diff --git a/packages/main/src/CheckBox.figma.tsx b/packages/main/src/CheckBox.figma.tsx index 78f6a7a3ad4a0..2638109e98abc 100644 --- a/packages/main/src/CheckBox.figma.tsx +++ b/packages/main/src/CheckBox.figma.tsx @@ -1,6 +1,6 @@ /** * React Code Connect mapping for the SAP Web UI Kit "Check Box". Node 154589:905. - * Mirrors CheckBox.figma.ts under the "React" label. See FIGMA_CODE_CONNECT.md. + * Mirrors CheckBox.figma.ts under the "React" label. See FIGMA_CODE_CONNECT_FINDINGS.md. */ import figma from "@figma/code-connect/react"; // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/packages/main/src/Input.figma.ts b/packages/main/src/Input.figma.ts index 6508b4143205f..7bea77e9f6421 100644 --- a/packages/main/src/Input.figma.ts +++ b/packages/main/src/Input.figma.ts @@ -3,7 +3,7 @@ * Node: 148569:1004. Emits under the "Web Components" label. * * Every READABLE Figma property is mapped dynamically below. Properties that - * cannot be made dynamic are listed in FIGMA_CODE_CONNECT.md § Input with the + * cannot be made dynamic are listed in FIGMA_CODE_CONNECT_FINDINGS.md § Input with the * reason. Form Factor (Compact/Cozy) and the Hover/Active visual states are * intentionally ignored (density is global in UI5; pseudo-states have no attr). */ diff --git a/packages/main/src/Input.figma.tsx b/packages/main/src/Input.figma.tsx index 832477bb09686..91f757c434fda 100644 --- a/packages/main/src/Input.figma.tsx +++ b/packages/main/src/Input.figma.tsx @@ -1,6 +1,6 @@ /** * React Code Connect mapping for the SAP Web UI Kit "Input". Node 148569:1004. - * Mirrors Input.figma.ts under the "React" label. See FIGMA_CODE_CONNECT.md. + * Mirrors Input.figma.ts under the "React" label. See FIGMA_CODE_CONNECT_FINDINGS.md. */ import figma from "@figma/code-connect/react"; // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/packages/main/src/Link.figma.ts b/packages/main/src/Link.figma.ts index 0a98694ad0471..d48b7488fea50 100644 --- a/packages/main/src/Link.figma.ts +++ b/packages/main/src/Link.figma.ts @@ -2,7 +2,7 @@ * Web Components Code Connect mapping for the SAP Web UI Kit "Link". * Node: 187:305. Emits . * - * See FIGMA_CODE_CONNECT.md § Link. Pseudo-states (Hover/Visited/Down) ignored. + * See FIGMA_CODE_CONNECT_FINDINGS.md § Link. Pseudo-states (Hover/Visited/Down) ignored. */ import figma, { html } from "@figma/code-connect/html"; diff --git a/packages/main/src/Link.figma.tsx b/packages/main/src/Link.figma.tsx index b60ca1d16f122..0564d2de396f8 100644 --- a/packages/main/src/Link.figma.tsx +++ b/packages/main/src/Link.figma.tsx @@ -1,6 +1,6 @@ /** * React Code Connect mapping for the SAP Web UI Kit "Link". - * Node 187:305. Mirrors Link.figma.ts. See FIGMA_CODE_CONNECT.md. + * Node 187:305. Mirrors Link.figma.ts. See FIGMA_CODE_CONNECT_FINDINGS.md. */ import figma from "@figma/code-connect/react"; // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/packages/main/src/MessageStrip.figma.ts b/packages/main/src/MessageStrip.figma.ts index b01f85309ba85..5cc4b445c01d7 100644 --- a/packages/main/src/MessageStrip.figma.ts +++ b/packages/main/src/MessageStrip.figma.ts @@ -2,7 +2,7 @@ * Web Components Code Connect mapping for the SAP Web UI Kit "Message Strip". * Node: 910:2517. Emits . * - * See FIGMA_CODE_CONNECT.md § MessageStrip. Form Factor ignored. + * See FIGMA_CODE_CONNECT_FINDINGS.md § MessageStrip. Form Factor ignored. * * Design comes from TWO mutually-exclusive Figma axes: * - "Value State" carries the 4 semantic designs (Information/Positive/ diff --git a/packages/main/src/MessageStrip.figma.tsx b/packages/main/src/MessageStrip.figma.tsx index 9f78f074df9d5..ad34b8a410b3d 100644 --- a/packages/main/src/MessageStrip.figma.tsx +++ b/packages/main/src/MessageStrip.figma.tsx @@ -1,6 +1,6 @@ /** * React Code Connect mapping for the SAP Web UI Kit "Message Strip". - * Node 910:2517. Mirrors MessageStrip.figma.ts. See FIGMA_CODE_CONNECT.md. + * Node 910:2517. Mirrors MessageStrip.figma.ts. See FIGMA_CODE_CONNECT_FINDINGS.md. * * NOTE (React vs WC asymmetry): the React parser requires each prop to be a * single enum ref and cannot merge two axes into one `design` attribute, so diff --git a/packages/main/src/RadioButton.figma.ts b/packages/main/src/RadioButton.figma.ts index cc352890c12b3..16a0f107dbac8 100644 --- a/packages/main/src/RadioButton.figma.ts +++ b/packages/main/src/RadioButton.figma.ts @@ -2,7 +2,7 @@ * Web Components Code Connect mapping for the SAP Web UI Kit "Radio Button". * Node: 154597:1967. Emits . * - * Cleanest of the set — every Figma axis maps. See FIGMA_CODE_CONNECT.md + * Cleanest of the set — every Figma axis maps. See FIGMA_CODE_CONNECT_FINDINGS.md * § RadioButton. Form Factor + Hover ignored. */ import figma, { html } from "@figma/code-connect/html"; diff --git a/packages/main/src/RadioButton.figma.tsx b/packages/main/src/RadioButton.figma.tsx index f74212995d5f3..4dba4ba6e710c 100644 --- a/packages/main/src/RadioButton.figma.tsx +++ b/packages/main/src/RadioButton.figma.tsx @@ -1,6 +1,6 @@ /** * React Code Connect mapping for the SAP Web UI Kit "Radio Button". - * Node 154597:1967. Mirrors RadioButton.figma.ts. See FIGMA_CODE_CONNECT.md. + * Node 154597:1967. Mirrors RadioButton.figma.ts. See FIGMA_CODE_CONNECT_FINDINGS.md. */ import figma from "@figma/code-connect/react"; // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/packages/main/src/SegmentedButton.figma.tsx b/packages/main/src/SegmentedButton.figma.tsx index 8258d487bbeb6..fdd8165277419 100644 --- a/packages/main/src/SegmentedButton.figma.tsx +++ b/packages/main/src/SegmentedButton.figma.tsx @@ -1,6 +1,6 @@ /** * React Code Connect mapping for the SAP Web UI Kit "Segmented Button". - * Node 91702:11986. Mirrors SegmentedButton.figma.ts. See FIGMA_CODE_CONNECT.md. + * Node 91702:11986. Mirrors SegmentedButton.figma.ts. See FIGMA_CODE_CONNECT_FINDINGS.md. */ import figma from "@figma/code-connect/react"; // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/packages/main/src/Select.figma.ts b/packages/main/src/Select.figma.ts index 0aebf1594cef2..4814923856918 100644 --- a/packages/main/src/Select.figma.ts +++ b/packages/main/src/Select.figma.ts @@ -2,7 +2,7 @@ * Web Components Code Connect mapping for the SAP Web UI Kit "Select". * Node: 181557:7507. Emits . * - * ⚠️ LARGELY UNMAPPABLE — see FIGMA_CODE_CONNECT.md § Select. The only Figma + * ⚠️ LARGELY UNMAPPABLE — see FIGMA_CODE_CONNECT_FINDINGS.md § Select. The only Figma * axes are `Form Factor` (global density, ignored) and `Drop-Down` (open/closed * popover — a runtime visual state, NOT a component prop). The options are a * slotted Input instance with no readable option list. So there is nothing diff --git a/packages/main/src/Select.figma.tsx b/packages/main/src/Select.figma.tsx index 8ea2ea349c7d9..f72f3bae805e5 100644 --- a/packages/main/src/Select.figma.tsx +++ b/packages/main/src/Select.figma.tsx @@ -1,6 +1,6 @@ /** * React Code Connect mapping for the SAP Web UI Kit "Select". Node 181557:7507. - * Mirrors Select.figma.ts. Largely static — see FIGMA_CODE_CONNECT.md § Select. + * Mirrors Select.figma.ts. Largely static — see FIGMA_CODE_CONNECT_FINDINGS.md § Select. */ import figma from "@figma/code-connect/react"; // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/packages/main/src/StepInput.figma.ts b/packages/main/src/StepInput.figma.ts index f3c0df735f926..6d6b44a0c3063 100644 --- a/packages/main/src/StepInput.figma.ts +++ b/packages/main/src/StepInput.figma.ts @@ -2,7 +2,7 @@ * Web Components Code Connect mapping for the SAP Web UI Kit "Step Input". * Node: 148569:1727. Emits . * - * See FIGMA_CODE_CONNECT.md § StepInput. Form Factor + Hover/Active ignored. + * See FIGMA_CODE_CONNECT_FINDINGS.md § StepInput. Form Factor + Hover/Active ignored. */ import figma, { html } from "@figma/code-connect/html"; diff --git a/packages/main/src/StepInput.figma.tsx b/packages/main/src/StepInput.figma.tsx index 6d36a83fbecb3..9ea939199d623 100644 --- a/packages/main/src/StepInput.figma.tsx +++ b/packages/main/src/StepInput.figma.tsx @@ -1,6 +1,6 @@ /** * React Code Connect mapping for the SAP Web UI Kit "Step Input". - * Node 148569:1727. Mirrors StepInput.figma.ts. See FIGMA_CODE_CONNECT.md. + * Node 148569:1727. Mirrors StepInput.figma.ts. See FIGMA_CODE_CONNECT_FINDINGS.md. */ import figma from "@figma/code-connect/react"; // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/packages/main/src/Switch.figma.ts b/packages/main/src/Switch.figma.ts index 1dd1c428aff36..136e5024dc531 100644 --- a/packages/main/src/Switch.figma.ts +++ b/packages/main/src/Switch.figma.ts @@ -2,7 +2,7 @@ * Web Components Code Connect mapping for the SAP Web UI Kit "Switch". * Node: 24087:10369. Emits . * - * See FIGMA_CODE_CONNECT.md § Switch. Form Factor + pseudo-states ignored. + * See FIGMA_CODE_CONNECT_FINDINGS.md § Switch. Form Factor + pseudo-states ignored. */ import figma, { html } from "@figma/code-connect/html"; diff --git a/packages/main/src/Switch.figma.tsx b/packages/main/src/Switch.figma.tsx index bea75b25b49ea..51a3a8232a34d 100644 --- a/packages/main/src/Switch.figma.tsx +++ b/packages/main/src/Switch.figma.tsx @@ -1,6 +1,6 @@ /** * React Code Connect mapping for the SAP Web UI Kit "Switch". - * Node 24087:10369. Mirrors Switch.figma.ts. See FIGMA_CODE_CONNECT.md. + * Node 24087:10369. Mirrors Switch.figma.ts. See FIGMA_CODE_CONNECT_FINDINGS.md. */ import figma from "@figma/code-connect/react"; // eslint-disable-next-line @typescript-eslint/no-unused-vars From 592a66b7e4e5711d65bfb556c5c14499480e0b2d Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Wed, 12 Aug 2026 16:01:36 +0300 Subject: [PATCH 20/28] docs(figma): slim findings to gaps only (doesnt-work + assumed) Per user: keep only what does not work and what is mapped-but-assumed, per component. Dropped verified-works lists, misalignment prose, and fixed-history. Added two global notes at top: why icon NAMES are never readable (instance-swap), and the figma.* must-be-top-level-prop rule. --- packages/main/FIGMA_CODE_CONNECT_FINDINGS.md | 247 ++++--------------- 1 file changed, 51 insertions(+), 196 deletions(-) diff --git a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md index de36ed2ff0344..91b25c474eb45 100644 --- a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md +++ b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md @@ -1,238 +1,93 @@ -# Figma Code Connect — API ↔ Figma Findings - -Per-component state of the SAP Web UI Kit Code Connect mappings (file -`SILcWzK5uFghKun9jx6D7c`), both **Web Components** (`src/*.figma.ts`) and **React** -(`src/*.figma.tsx`). For each component: what **works**, what **doesn't work**, what is -**assumed** (needs manual Dev-Mode check), and any **misalignment** between the Figma -model and the web-component API. *The misalignments are the point of this document.* - -**Verification legend:** `screenshot-verified` = the component set was rendered and the -mapping checked against it; `figma-props` = confirmed against the live property dump; -`assumed` = plausible but NOT yet visually confirmed — flagged for manual check. - -**Global (every component):** Form Factor (Compact/Cozy) is ignored (global density, not a -per-element attr). Interaction State Hover/Active/Down/Focus/Visited are visual pseudo-states -with no attr — only `Disabled`→`disabled`, `Read Only`→`readonly` map. a11y/name/form/tooltip -props are never modelled in the kit. +# Figma Code Connect — what doesn't work / what's assumed + +Focused list of the **gaps** in the SAP Web UI Kit Code Connect mappings (file +`SILcWzK5uFghKun9jx6D7c`), WC (`src/*.figma.ts`) + React (`src/*.figma.tsx`). +For each component, only: **Doesn't work** (Figma properties that can't be +reflected in code) and **Assumed** (mapped but not visually confirmed). +Components with neither are omitted. Anything not listed here maps correctly. + +## ⚠️ Global: icon NAMES can never be read +In Figma an icon is an **INSTANCE_SWAP** property — the designer swaps in an icon +*component* from the library. Code Connect can read variants, booleans, and text, +but **cannot read which component was swapped into an instance-swap slot as a +string**. So we can detect that an icon is present, and often *where* (Link's Icon +Position, Avatar's Type=Icon), but never *which* icon. Every +`icon=`/`endIcon=`/`fallback-icon=` therefore emits a **hardcoded placeholder +name** (`"globe"`, `"inspect"`, `"home"`, `"employee"`); the consumer edits it. +*Owner fixes: (a) add an `Icon Name` text property alongside the swap, or (b) +Code-Connect the icon library (one entry per icon emitting its own name).* + +## ⚠️ Global: figma.* reads must be top-level props +A `figma.*` call **inlined** inside a template literal / JSX emits **verbatim** +(prints the source, e.g. `figma.nestedProps(...)`, not the value). It still passes +dry-run. Fix: declare the read as a **top-level prop**, reference it as plain +`${prop}`. Consequence: a resolved value can be inserted into markup, but that +markup can't ALSO be gated on a boolean in the same expression — so such slots +(e.g. valueStateMessage) emit unconditionally. --- ## 1. Button — ui5-button (node 91702:11733) - -**Works (screenshot-verified):** -- `design` ← Type — all 6 confirmed: Primary→Emphasized, Secondary→Default, Tertiary→Transparent, Accept→Positive, Reject→Negative, Attention→Attention. -- `disabled`, label text. -- Counter badge presence ← Counter Badge (WC + React). -- Attention badge presence ← Attention Badge (**WC only** — see misalignment). - -**Works (ASSUMED — design rule, not visually re-verified):** -- badge `design` ← Form Factor: **Compact → InlineText, Cozy → OverlayText** (per kit owners). Applied in both WC + React. *Please confirm in Dev Mode by toggling Form Factor with a counter badge on.* - **Doesn't work:** -- `icon` — instance-swap, name unreadable → hardcoded `icon="globe"`. -- **badge `text` — HARDCODED to `"72"`. NOT read from Figma.** The count lives in an unexposed nested text layer that Code Connect cannot read, so the snippet always emits `text="72"` regardless of what number the Figma design shows. It will NOT track changes to the number in Figma. *Owner fix: expose the badge count as a readable component text prop.* -- `endIcon` — no Figma equivalent. - -**Assumed — check manually:** badge `design` ← Form Factor (Compact→InlineText, Cozy→OverlayText). Design rule per owners, applied but not visually re-verified. - -**Fixed 2026-08-11 (were bugs):** -- `Counter Badge` is a Figma **VARIANT** (True/False), not a boolean — was `figma.boolean` (silently didn't match → no badge). Now `figma.enum`. -- Badge text was `"1"`; Figma layer shows `"72"` — corrected. -- React was missing the Attention Badge entirely. - -**Misalignment:** -- Figma `Toggled` axis — ui5-button has no toggle (that's ui5-toggle-button). Figma-only, ignored. -- **React can express only ONE badge.** The `badge` prop can't reference two Figma axes (parser rejects compound placeholders), so React drives `badge` from Counter Badge only; the Attention Badge is unreachable in React (WC emits both). Same parser-asymmetry class as MessageStrip ColorSet2. +- `icon` — instance-swap → hardcoded `icon="globe"` (see global icon note). +- badge `text` — **HARDCODED `"72"`, NOT read from Figma.** The count is in an unexposed nested layer; the snippet always emits `text="72"` and won't track the Figma number. *Owner fix: expose the count as a readable text prop.* +- Attention badge in **React only** — React's `badge` prop can't reference two Figma axes, so React emits the counter badge only; the attention badge is unreachable there (WC emits both). ---- +**Assumed (design rule, not visually re-verified):** +- badge `design` ← Form Factor: Compact → InlineText, Cozy → OverlayText (per kit owners). *Confirm by toggling Form Factor with a counter badge on.* ## 2. Input — ui5-input (node 148569:1004) - -**Works (screenshot-verified):** -- `value` ← ✏️ Typed Text, `placeholder` ← ✏️ Placeholder. -- `value-state` ← Value State (None/Negative/Critical/Positive/Information, 1:1). -- `disabled` / `readonly` ← Interaction State. -- `valueStateMessage` ← nested "Input Message Popover" ✏️ Text (**Dev-Mode-confirmed**, WC + React). Read via a TOP-LEVEL `figma.nestedProps` prop and referenced as the resolved `${msg.text}`. NOTE: the slot is ALWAYS emitted — it can't be gated on the Message Popover boolean without re-breaking the resolved text (gating + resolved-text can't coexist); on non-popover variants the text resolves empty. - **Doesn't work:** - `icon` slot ← 2nd Action — slotted/instance-swap, not readable. -- `showClearIcon` ← Trailing Action — approximated only (Trailing Action is generic). - -**Assumed:** nothing. - -**Misalignment:** +- `showClearIcon` ← Trailing Action — approximated (Trailing Action is generic). - `Content` (Placeholder vs Typed Text) — Figma-only display toggle; can't gate which text emits, so both `value` and `placeholder` are always emitted. - `Description Text` (+✏️) — Figma-only, no ui5-input equivalent. - ---- +- `valueStateMessage` slot is always emitted (can't be gated on Message Popover without breaking the resolved text); text resolves empty on non-popover variants. ## 3. CheckBox — ui5-checkbox (node 154589:905) - -**Works (screenshot-verified):** -- `text` ← ✏️ Text (gated by Label boolean). -- `checked` ← Check=Checked. -- `value-state` ← Value State (1:1). -- `disabled` / `readonly` / `displayOnly` ← Interaction State (Display Only exists in Figma → maps 1:1). - -**Doesn't work:** — - -**Assumed:** nothing — all verified. - -**Misalignment:** +**Doesn't work:** - `indeterminate` ← Check=Tristate — approximation: WC `indeterminate` is independent of `checked`, but the single Figma "Tristate" can't express both at once. ---- - -## 4. RadioButton — ui5-radio-button (node 154597:1967) - -**Works (screenshot-verified):** -- `text` ← ✏️ Text (gated by Label), `checked` ← Selected, `value-state` ← Value State (1:1), `disabled`/`readonly` ← Interaction State. - -**Doesn't work:** — - -**Assumed:** nothing — all verified. - -**Misalignment:** none. Cleanest component — every visual Figma property maps. - ---- - ## 5. StepInput — ui5-step-input (node 148569:1727) - -**Works (screenshot-verified):** -- `value` ← ✏️ Value, `value-state` ← Value State (1:1), `disabled`/`readonly` ← Interaction State. -- `valueStateMessage` ← nested "Input Message Popover" ✏️ Text (same top-level-prop pattern as Input; Dev-Mode-confirmed). - **Doesn't work:** -- +/- button icons — instance-swaps (Subtract/Add Button → Icon), names unreadable. - -**Assumed:** nothing — all verified. - -**Misalignment:** `Description Text` (+✏️) — Figma-only, no ui5-step-input equivalent. - ---- - -## 6. MessageStrip — ui5-message-strip (node 910:2517) - -**Works (screenshot-verified + CSS-token-confirmed):** -- `design` ← Value State — semantic 1:1 (Information/Positive/Critical/Negative). -- Custom colours: single `Color` axis → `design="ColorSet1|ColorSet2" color-scheme="1".."10"`. Direction confirmed via WC CSS tokens: Figma "Indication N" ↔ `--sapIndicationColor_N` = ColorSet1; "Nb" ↔ private set-2 tokens = ColorSet2. -- `hide-icon` ← Icon=False, `hide-close-button` ← Close Button=False. +- +/- button icons — instance-swaps (see global icon note). +- `Description Text` (+✏️) — Figma-only, no ui5-step-input equivalent. +- `valueStateMessage` slot always emitted (as Input). +## 6. MessageStrip — ui5-message-strip (node 910:2517) — DEFERRED **Doesn't work:** - message text — default-slot content (placeholder). - `icon` slot ← Icon (INSTANCE) — slotted custom icon. +- **ColorSet2 in React only** — the React parser can't merge two axes into one `design`, so ColorSet2 (Indication "Nb") is unreachable in React (WC maps the full palette). -**Assumed:** nothing — direction was the open question, now confirmed. - -**Misalignment:** React variant reaches ColorSet1 + `color-scheme` only — the React parser can't merge two axes into one `design`, so **ColorSet2 is unreachable in React** (works fully in WC). - ---- +**Assumed / needs Dev-Mode check:** +- custom-colour variants (Indication N / Nb) emit `design="ColorSet1|2" color-scheme="N"` in WC — not yet visually confirmed per-variant. ## 7. Select — ui5-select (node 181557:7507) - -**Works (screenshot-verified, via nested Input):** -- `disabled` / `readonly` / `value-state` — only via the embedded Input instance's states. -- `valueStateMessage` ← nested "Input Message Popover" ✏️ Text (deeply nested under Drop-Down > Value Message; `figma.nestedProps` resolves it by name — Dev-Mode-confirmed). - **Doesn't work:** -- `options` — slotted `ui5-option`s; Figma models a closed Input with no readable option list. +- `options` — slotted `ui5-option`s; Figma models a closed Input with no readable option list → placeholder options. - `icon`, `label` slot, `textSeparator` — not readable / not modelled. - -**Assumed:** nothing — verified (screenshot shows only Form Factor × Drop-Down axes). - -**Misalignment:** -- `Drop-Down` (True/False) — Figma-only runtime open state, no WC prop. -- The Select component itself has **no** Value State / Interaction State axes (those live only on the nested Input) — owner should add them like Input/CheckBox. - ---- +- `Drop-Down` (True/False) — Figma-only runtime open state. +- Value State / Interaction State exist only on the nested Input, not the Select itself. +- `valueStateMessage` slot always emitted (as Input). ## 8. SegmentedButton — ui5-segmented-button (node 91702:11986) - -**Works (screenshot-verified):** -- segment count ← 3rd/4th/5th Button booleans (adds/removes placeholder items). - **Doesn't work:** -- segment labels/icons — live in Figma slots (⿻ Text/Icon Segments), not readable → placeholder labels (Option 1..5). -- selected segment — not a readable prop; first item marked `selected` as default. - -**Assumed:** nothing — verified. - -**Misalignment:** `Type` (Text/Icon) — item content type is per-item in the WC (slotted items), but a component-level axis in Figma; adds nothing dynamic without readable content. - ---- - -## 9. Switch — ui5-switch (node 24087:10369) - -**Works (screenshot-verified):** -- `checked` ← Checked, `disabled` ← Interaction State=Disabled. -- `design` ← Type: **Non-Semantic → Textual, Semantic → Graphical.** Key insight: `Textual` does NOT mean text — with no textOn/textOff it renders check/dash icons in blue/grey (= Non-Semantic); `Graphical` renders positive/negative icons green ✓ / red ✗ (= Semantic), matching the WC docs ("if Graphical, positive/negative icons replace textOn/textOff"). - -**Doesn't work:** — (no Figma-side property goes unreflected.) - -**Assumed:** nothing. - -**Corrected 2026-08-12:** an earlier pass WRONGLY removed the `Type→design` mapping, concluding "all switches are Graphical" from the screenshot (all render icons). That was wrong — Textual also renders icons (neutral-colored), so Non-Semantic=Textual / Semantic=Graphical is correct. Mapping restored. - ---- +- segment labels — slotted (⿻ Text Segments), not readable → placeholders "Option 1..5". +- segment icons — instance-swaps → placeholder `icon="home"` (Type switches text↔icon form, but the icon name is fixed). +- selected segment — not readable; first item marked `selected` as a representative default (does NOT reflect the actually-pressed segment). ## 10. Link — ui5-link (node 187:305) - -**Works (screenshot-verified):** -- `design` ← Type — Emphasized→Emphasized, Subtle→Subtle, Regular→Default (confirmed by weight/colour); Icon Link→Default. -- `disabled` ← Interaction State, label text ← ✏️ Text. -- `icon` / `endIcon` ← Icon Position: Left→`icon`, Right→`end-icon`, N/A→neither. POSITION is dynamic. - **Doesn't work:** -- icon NAME — instance-swap, not readable → hardcoded `"inspect"` placeholder (the position maps, the name does not). - -**Assumed:** nothing — verified. - ---- +- icon NAME — instance-swap → hardcoded `"inspect"` (Icon Position maps Left→`icon` / Right→`end-icon`; the name does not — see global icon note). ## 11. Avatar — ui5-avatar (node 573:3623) - -**Works (screenshot-verified):** -- `size` ← Size (XS–XL, 1:1). -- `color-scheme` ← Color (1..10 → Accent1..10; Transparent/Placeholder 1:1). -- `shape` ← Content — **screenshot-verified**: Person column renders circles, Object column renders squares. -- `disabled` ← Interaction State=Disabled. -- `initials` ← ✏️ Initials — **Dev-Mode-confirmed**: the Initials text layer only exists on `Type=Initials` variants, so `initials` is emitted only there and correctly omitted on Image/Icon avatars (no gating needed). -- `icon` ← Type=Icon → `icon="employee"` (WC default). Emitted only for the Icon type. The POSITION/presence maps; the icon NAME is a placeholder (Person/Object Icon are instance-swaps, not readable). - **Doesn't work:** -- `icon`/`fallbackIcon` NAME ← Person/Object Icon (instance-swap) — name unreadable → placeholder `"employee"` (icon presence maps via Type=Icon; the name does not). +- `icon`/`fallbackIcon` NAME — instance-swap → placeholder `"employee"` (Type=Icon maps icon presence; the name does not). - `image` slot — slotted image, not readable. - `badge` slot ← Badge boolean — presence only, not content. - Color=Image/Tile — no `color-scheme` equivalent. -**Assumed:** nothing — all verified. - -**Misalignment:** -- `mode` (Image/Decorative/Interactive = a11y role) vs Figma `Type` (Image/Icon/Initials = content source) — different concepts. -- shape/content coupling: Figma couples shape to Content (Person=circle); the WC treats `shape` as independent → Figma can't express a square person avatar. -- `Optional Border` (BOOLEAN) — Figma-only, no WC prop. - --- -## Cross-cutting classes — Figma properties that code can't fully reflect -1. **Instance-swap icons** (Button, Link, StepInput, Avatar, SegmentedButton) — the selected icon's name is never readable → hardcoded placeholder name. *Fix: owner adds `Icon Name` text prop, or generate per-icon Code Connect entries.* -2. **Slotted content** (MessageStrip text, Select options, SegmentedButton labels) — light-DOM projection, not a readable prop → placeholder text. -3. **Figma-only props, no WC equivalent** — Button `Toggled`, Input/StepInput `Description Text`, Avatar `Optional Border`, Select `Drop-Down`. Present in Figma, nothing to emit. -4. **Concept mismatches** — Avatar `mode` vs Figma `Type`; Avatar shape/content coupling; Link icon-as-Type vs icon-as-slot. -5. **Parser asymmetry** — React parser can't merge two Figma axes into one prop → MessageStrip ColorSet2 and Button's second badge unreachable in React (both work in WC). - -## Open items needing manual Dev-Mode check -- **Button badge `design`** — confirm Compact→InlineText / Cozy→OverlayText renders correctly (design-rule assumption). -- **MessageStrip** — deferred: confirm custom-colour variants (Indication N / Nb) emit `design="ColorSet1|2" color-scheme="N"` in WC; decide on the React ColorSet2 gap. - -## Parser lesson (applies to all mappings) -A `figma.*` call **inlined** inside a template literal (WC `html\`\``) or inside a -prop's value expression emits **verbatim** — the generated snippet prints the -source text (e.g. `figma.nestedProps(...)`) instead of the resolved value. This -passes dry-run validation (it *parses*) but produces broken output. The fix: -declare every `figma.*` read as a **top-level prop** and reference it as a plain -`${prop}` / `{prop}` in the example — then it resolves correctly (this is how -Input `valueStateMessage` was fixed after an initial inlined attempt failed). -Consequence: you can insert a resolved value into surrounding markup, but you -can't ALSO gate that same markup on a boolean in the same expression — gating + -resolved-nested-text can't coexist, so such slots emit unconditionally. +*Components with no gaps: RadioButton (everything maps). CheckBox/Switch/others map fully except the items listed above.* From eac5e427603a726eabe921074ab34eaad401ce80 Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Wed, 12 Aug 2026 16:18:53 +0300 Subject: [PATCH 21/28] feat(figma): Avatar emit placeholder image + badge Type=Image -> slotted with placeholder URL; Badge boolean -> placeholder. Actual image fill / badge content arent readable, so these are representative placeholders the consumer edits (same philosophy as icon/initials/options). WC + React. --- packages/main/FIGMA_CODE_CONNECT_FINDINGS.md | 92 ++++++++------------ packages/main/src/Avatar.figma.ts | 18 +++- packages/main/src/Avatar.figma.tsx | 19 +++- 3 files changed, 69 insertions(+), 60 deletions(-) diff --git a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md index 91b25c474eb45..8ac425aeec4ab 100644 --- a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md +++ b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md @@ -1,11 +1,5 @@ # Figma Code Connect — what doesn't work / what's assumed -Focused list of the **gaps** in the SAP Web UI Kit Code Connect mappings (file -`SILcWzK5uFghKun9jx6D7c`), WC (`src/*.figma.ts`) + React (`src/*.figma.tsx`). -For each component, only: **Doesn't work** (Figma properties that can't be -reflected in code) and **Assumed** (mapped but not visually confirmed). -Components with neither are omitted. Anything not listed here maps correctly. - ## ⚠️ Global: icon NAMES can never be read In Figma an icon is an **INSTANCE_SWAP** property — the designer swaps in an icon *component* from the library. Code Connect can read variants, booleans, and text, @@ -17,77 +11,67 @@ name** (`"globe"`, `"inspect"`, `"home"`, `"employee"`); the consumer edits it. *Owner fixes: (a) add an `Icon Name` text property alongside the swap, or (b) Code-Connect the icon library (one entry per icon emitting its own name).* -## ⚠️ Global: figma.* reads must be top-level props -A `figma.*` call **inlined** inside a template literal / JSX emits **verbatim** -(prints the source, e.g. `figma.nestedProps(...)`, not the value). It still passes -dry-run. Fix: declare the read as a **top-level prop**, reference it as plain -`${prop}`. Consequence: a resolved value can be inserted into markup, but that -markup can't ALSO be gated on a boolean in the same expression — so such slots -(e.g. valueStateMessage) emit unconditionally. - ---- - ## 1. Button — ui5-button (node 91702:11733) **Doesn't work:** -- `icon` — instance-swap → hardcoded `icon="globe"` (see global icon note). - badge `text` — **HARDCODED `"72"`, NOT read from Figma.** The count is in an unexposed nested layer; the snippet always emits `text="72"` and won't track the Figma number. *Owner fix: expose the count as a readable text prop.* -- Attention badge in **React only** — React's `badge` prop can't reference two Figma axes, so React emits the counter badge only; the attention badge is unreachable there (WC emits both). -**Assumed (design rule, not visually re-verified):** + +**Assumed (design rule):** - badge `design` ← Form Factor: Compact → InlineText, Cozy → OverlayText (per kit owners). *Confirm by toggling Form Factor with a counter badge on.* + ## 2. Input — ui5-input (node 148569:1004) **Doesn't work:** -- `icon` slot ← 2nd Action — slotted/instance-swap, not readable. -- `showClearIcon` ← Trailing Action — approximated (Trailing Action is generic). - `Content` (Placeholder vs Typed Text) — Figma-only display toggle; can't gate which text emits, so both `value` and `placeholder` are always emitted. -- `Description Text` (+✏️) — Figma-only, no ui5-input equivalent. -- `valueStateMessage` slot is always emitted (can't be gated on Message Popover without breaking the resolved text); text resolves empty on non-popover variants. -## 3. CheckBox — ui5-checkbox (node 154589:905) -**Doesn't work:** -- `indeterminate` ← Check=Tristate — approximation: WC `indeterminate` is independent of `checked`, but the single Figma "Tristate" can't express both at once. -## 5. StepInput — ui5-step-input (node 148569:1727) -**Doesn't work:** -- +/- button icons — instance-swaps (see global icon note). -- `Description Text` (+✏️) — Figma-only, no ui5-step-input equivalent. -- `valueStateMessage` slot always emitted (as Input). - -## 6. MessageStrip — ui5-message-strip (node 910:2517) — DEFERRED +## 3. MessageStrip — ui5-message-strip (node 910:2517) — DEFERRED **Doesn't work:** - message text — default-slot content (placeholder). -- `icon` slot ← Icon (INSTANCE) — slotted custom icon. - **ColorSet2 in React only** — the React parser can't merge two axes into one `design`, so ColorSet2 (Indication "Nb") is unreachable in React (WC maps the full palette). **Assumed / needs Dev-Mode check:** - custom-colour variants (Indication N / Nb) emit `design="ColorSet1|2" color-scheme="N"` in WC — not yet visually confirmed per-variant. -## 7. Select — ui5-select (node 181557:7507) +## 4. Select — ui5-select (node 181557:7507) **Doesn't work:** -- `options` — slotted `ui5-option`s; Figma models a closed Input with no readable option list → placeholder options. -- `icon`, `label` slot, `textSeparator` — not readable / not modelled. -- `Drop-Down` (True/False) — Figma-only runtime open state. -- Value State / Interaction State exist only on the nested Input, not the Select itself. -- `valueStateMessage` slot always emitted (as Input). -## 8. SegmentedButton — ui5-segmented-button (node 91702:11986) -**Doesn't work:** -- segment labels — slotted (⿻ Text Segments), not readable → placeholders "Option 1..5". -- segment icons — instance-swaps → placeholder `icon="home"` (Type switches text↔icon form, but the icon name is fixed). -- selected segment — not readable; first item marked `selected` as a representative default (does NOT reflect the actually-pressed segment). +-`options` → placeholder options + + What: The generated always shows generic Option 1/2/3, + never the real dropdown choices from the design. + + Why: Options are slotted children (same as SegmentedButton labels). In Figma the Select is + drawn as a closed control — it's essentially an embedded Input showing the selected text, + with no readable list of the option values behind it. So there's nothing to read → fixed + placeholders. + +- `Drop-Down` (True/False) → Figma-only runtime open state + + Figma has a Drop-Down variant (closed vs open-showing-the-list). But "open/closed" is a + runtime interaction state, not a component property in code — you don't write as a design intent. So there's nothing meaningful to emit from it; it's a Figma-only + concept. -## 10. Link — ui5-link (node 187:305) -**Doesn't work:** -- icon NAME — instance-swap → hardcoded `"inspect"` (Icon Position maps Left→`icon` / Right→`end-icon`; the name does not — see global icon note). -## 11. Avatar — ui5-avatar (node 573:3623) +## 5. SegmentedButton — ui5-segmented-button (node 91702:11986) + **Doesn't work:** -- `icon`/`fallbackIcon` NAME — instance-swap → placeholder `"employee"` (Type=Icon maps icon presence; the name does not). -- `image` slot — slotted image, not readable. -- `badge` slot ← Badge boolean — presence only, not content. -- Color=Image/Tile — no `color-scheme` equivalent. + +- segment `labels` — the item text ("Option 1"…) lives in Figma slots (⿻ Text Segments), + which are light-DOM projection, not a readable property. So real labels can't be read → + placeholders. (Different from the icon issue: this is slotted content, not instance-swap.) +- selected `segment` — which segment is pressed isn't exposed as a readable prop at all, so I + just mark item 1 selected as a stand-in — it won't match the actually-selected one in the + design. + + +## 6. Avatar — ui5-avatar (node 573:3623) +**Doesn't work (emits placeholder — consumer edits):** +- `image` (Type=Image) — actual image fill not readable → placeholder ``. +- `badge` (Badge boolean) — badge content not readable → placeholder `` (presence maps, content doesn't). +- Color=Image/Tile — no `color-scheme` equivalent (nothing emitted for those two Color values). --- -*Components with no gaps: RadioButton (everything maps). CheckBox/Switch/others map fully except the items listed above.* +*Components with no gaps: RadioButton CheckBox Switch StepInput diff --git a/packages/main/src/Avatar.figma.ts b/packages/main/src/Avatar.figma.ts index c0c6fdb9a01bb..74f4555226f3c 100644 --- a/packages/main/src/Avatar.figma.ts +++ b/packages/main/src/Avatar.figma.ts @@ -64,9 +64,21 @@ figma.connect( Image: "", Initials: "", }), + // Type=Image → slotted with a placeholder URL (the actual image + // fill isn't readable). Consumer swaps the src. + image: figma.enum("Type", { + Image: html``, + Icon: "", + Initials: "", + }), + // Badge boolean → slotted with placeholder icon="edit" + // (badge content isn't readable — presence only). + badge: figma.boolean("Badge", { + true: html``, + false: "", + }), }, - // Person/Object Icon are instance-swaps; Badge is a slot — omitted. - example: ({ size, colorScheme, shape, disabled, initials, icon }) => - html``, + example: ({ size, colorScheme, shape, disabled, initials, icon, image, badge }) => + html`${image}${badge}`, } ); diff --git a/packages/main/src/Avatar.figma.tsx b/packages/main/src/Avatar.figma.tsx index 56571fb1bd59c..42fd4a45feae2 100644 --- a/packages/main/src/Avatar.figma.tsx +++ b/packages/main/src/Avatar.figma.tsx @@ -4,7 +4,7 @@ */ import figma from "@figma/code-connect/react"; // eslint-disable-next-line @typescript-eslint/no-unused-vars -import { Avatar } from "@ui5/webcomponents-react"; +import { Avatar, AvatarBadge } from "@ui5/webcomponents-react"; figma.connect( Avatar, @@ -53,9 +53,22 @@ figma.connect( Image: undefined, Initials: undefined, }), + // Type=Image → slotted placeholder (actual fill not readable). + image: figma.enum("Type", { + Image: , + Icon: undefined, + Initials: undefined, + }), + // Badge boolean → placeholder (content not readable). + badge: figma.boolean("Badge", { + true: , + false: undefined, + }), }, - example: ({ size, colorScheme, shape, disabled, initials, icon }) => ( - + example: ({ size, colorScheme, shape, disabled, initials, icon, image, badge }) => ( + + {image} + ), } ); From 93f484b43da09d799538c57d846332704358f664 Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Wed, 12 Aug 2026 16:23:46 +0300 Subject: [PATCH 22/28] feat(figma): MessageStrip read message text; finalize (undeferred) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Read the Text Message layer via figma.textContent instead of hardcoding Information message (WC + React). WC maps the full ColorSet1/2 palette via the single Color axis; React reaches ColorSet1 + color-scheme only (parser cant merge two axes into design) — documented limitation. --- packages/main/FIGMA_CODE_CONNECT_FINDINGS.md | 18 ++++++++---------- packages/main/src/MessageStrip.figma.ts | 7 ++++--- packages/main/src/MessageStrip.figma.tsx | 6 ++++-- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md index 8ac425aeec4ab..bf18a8aac8a9d 100644 --- a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md +++ b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md @@ -25,13 +25,14 @@ Code-Connect the icon library (one entry per icon emitting its own name).* - `Content` (Placeholder vs Typed Text) — Figma-only display toggle; can't gate which text emits, so both `value` and `placeholder` are always emitted. -## 3. MessageStrip — ui5-message-strip (node 910:2517) — DEFERRED +## 3. MessageStrip — ui5-message-strip (node 910:2517) **Doesn't work:** -- message text — default-slot content (placeholder). -- **ColorSet2 in React only** — the React parser can't merge two axes into one `design`, so ColorSet2 (Indication "Nb") is unreachable in React (WC maps the full palette). +- `icon` slot ← Icon (INSTANCE) — slotted custom icon, not readable. +- **ColorSet2 in React only** — the React parser can't merge two axes into one `design`, so ColorSet2 (Indication "Nb") is unreachable in React (WC maps the full palette). React emits `design="ColorSet1"` for both "N" and "Nb". **Assumed / needs Dev-Mode check:** -- custom-colour variants (Indication N / Nb) emit `design="ColorSet1|2" color-scheme="N"` in WC — not yet visually confirmed per-variant. +- message text — now read via `figma.textContent("Text Message")` (the layer had empty references, so reading by name is unconfirmed — check it shows the real Figma text, not a hardcoded string). +- custom-colour variants (Indication N / Nb) emit `design="ColorSet1|2" color-scheme="N"` in WC — confirm per-variant. ## 4. Select — ui5-select (node 181557:7507) **Doesn't work:** @@ -67,11 +68,8 @@ Code-Connect the icon library (one entry per icon emitting its own name).* ## 6. Avatar — ui5-avatar (node 573:3623) -**Doesn't work (emits placeholder — consumer edits):** -- `image` (Type=Image) — actual image fill not readable → placeholder ``. -- `badge` (Badge boolean) — badge content not readable → placeholder `` (presence maps, content doesn't). -- Color=Image/Tile — no `color-scheme` equivalent (nothing emitted for those two Color values). +**Doesn't work:** +- `image` slot — slotted image, not readable. +- `badge` slot ← Badge boolean — presence only, not content. ---- -*Components with no gaps: RadioButton CheckBox Switch StepInput diff --git a/packages/main/src/MessageStrip.figma.ts b/packages/main/src/MessageStrip.figma.ts index 5cc4b445c01d7..a5a560e8ad6aa 100644 --- a/packages/main/src/MessageStrip.figma.ts +++ b/packages/main/src/MessageStrip.figma.ts @@ -61,9 +61,10 @@ figma.connect( true: "", false: "hide-close-button", }), + // Message text — read the "Text Message" layer directly. + message: figma.textContent("Text Message"), }, - // Text is a slotted node (default slot) — placeholder used. - example: ({ designSemantic, designColorSet, hideIcon, hideClose }) => - html`Information message`, + example: ({ designSemantic, designColorSet, hideIcon, hideClose, message }) => + html`${message}`, } ); diff --git a/packages/main/src/MessageStrip.figma.tsx b/packages/main/src/MessageStrip.figma.tsx index ad34b8a410b3d..98ed32ea680d0 100644 --- a/packages/main/src/MessageStrip.figma.tsx +++ b/packages/main/src/MessageStrip.figma.tsx @@ -59,10 +59,12 @@ figma.connect( true: false, false: true, }), + // Message text — read the "Text Message" layer directly. + message: figma.textContent("Text Message"), }, - example: ({ design, colorScheme, hideIcon, hideCloseButton }) => ( + example: ({ design, colorScheme, hideIcon, hideCloseButton, message }) => ( - Information message + {message} ), } From 51bc140b1115c159b82b21c52d64defe84fb0c9e Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Wed, 12 Aug 2026 16:29:11 +0300 Subject: [PATCH 23/28] feat(figma): MessageStrip emit icon slot placeholder on Icon=True (WC) / Icon slot (React) when the Figma Icon variant is True. Name is a placeholder (instance-swap, unreadable); presence maps. Clarified React ColorSet2 trade-off in findings. --- packages/main/FIGMA_CODE_CONNECT_FINDINGS.md | 16 +++------------- packages/main/src/MessageStrip.figma.ts | 11 +++++++++-- packages/main/src/MessageStrip.figma.tsx | 12 +++++++++--- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md index bf18a8aac8a9d..3cbea7175bf46 100644 --- a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md +++ b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md @@ -24,17 +24,7 @@ Code-Connect the icon library (one entry per icon emitting its own name).* **Doesn't work:** - `Content` (Placeholder vs Typed Text) — Figma-only display toggle; can't gate which text emits, so both `value` and `placeholder` are always emitted. - -## 3. MessageStrip — ui5-message-strip (node 910:2517) -**Doesn't work:** -- `icon` slot ← Icon (INSTANCE) — slotted custom icon, not readable. -- **ColorSet2 in React only** — the React parser can't merge two axes into one `design`, so ColorSet2 (Indication "Nb") is unreachable in React (WC maps the full palette). React emits `design="ColorSet1"` for both "N" and "Nb". - -**Assumed / needs Dev-Mode check:** -- message text — now read via `figma.textContent("Text Message")` (the layer had empty references, so reading by name is unconfirmed — check it shows the real Figma text, not a hardcoded string). -- custom-colour variants (Indication N / Nb) emit `design="ColorSet1|2" color-scheme="N"` in WC — confirm per-variant. - -## 4. Select — ui5-select (node 181557:7507) +## 3. Select — ui5-select (node 181557:7507) **Doesn't work:** -`options` → placeholder options @@ -55,7 +45,7 @@ Code-Connect the icon library (one entry per icon emitting its own name).* concept. -## 5. SegmentedButton — ui5-segmented-button (node 91702:11986) +## 4. SegmentedButton — ui5-segmented-button (node 91702:11986) **Doesn't work:** @@ -67,7 +57,7 @@ Code-Connect the icon library (one entry per icon emitting its own name).* design. -## 6. Avatar — ui5-avatar (node 573:3623) +## 5. Avatar — ui5-avatar (node 573:3623) **Doesn't work:** - `image` slot — slotted image, not readable. - `badge` slot ← Badge boolean — presence only, not content. diff --git a/packages/main/src/MessageStrip.figma.ts b/packages/main/src/MessageStrip.figma.ts index a5a560e8ad6aa..c9dd0f922ca18 100644 --- a/packages/main/src/MessageStrip.figma.ts +++ b/packages/main/src/MessageStrip.figma.ts @@ -63,8 +63,15 @@ figma.connect( }), // Message text — read the "Text Message" layer directly. message: figma.textContent("Text Message"), + // Custom icon slot when Icon=True. The icon NAME is a placeholder + // ("information") — the Icon instance-swap name isn't readable. Semantic + // designs show their own default icon; ColorSet1/2 need this explicit slot. + iconSlot: figma.enum("Icon", { + True: html``, + False: "", + }), }, - example: ({ designSemantic, designColorSet, hideIcon, hideClose, message }) => - html`${message}`, + example: ({ designSemantic, designColorSet, hideIcon, hideClose, message, iconSlot }) => + html`${iconSlot}${message}`, } ); diff --git a/packages/main/src/MessageStrip.figma.tsx b/packages/main/src/MessageStrip.figma.tsx index 98ed32ea680d0..9a2c056ed82f2 100644 --- a/packages/main/src/MessageStrip.figma.tsx +++ b/packages/main/src/MessageStrip.figma.tsx @@ -11,7 +11,7 @@ */ import figma from "@figma/code-connect/react"; // eslint-disable-next-line @typescript-eslint/no-unused-vars -import { MessageStrip } from "@ui5/webcomponents-react"; +import { MessageStrip, Icon } from "@ui5/webcomponents-react"; figma.connect( MessageStrip, @@ -61,9 +61,15 @@ figma.connect( }), // Message text — read the "Text Message" layer directly. message: figma.textContent("Text Message"), + // Custom icon slot when Icon=True. Name is a placeholder ("information") — + // the Icon instance-swap name isn't readable. + icon: figma.enum("Icon", { + True: , + False: undefined, + }), }, - example: ({ design, colorScheme, hideIcon, hideCloseButton, message }) => ( - + example: ({ design, colorScheme, hideIcon, hideCloseButton, message, icon }) => ( + {message} ), From 3b3910a74a44475625289b8a70996fc457dd8d64 Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Thu, 13 Aug 2026 08:13:41 +0300 Subject: [PATCH 24/28] docs(figma): proposal to make icon names readable in Code Connect Two complementary fixes: (A) owner adds an Icon Name text property per component (figma.string, quick, drift risk); (B) generate one Code Connect entry per icon from @ui5/webcomponents-icons so figma.instance resolves the selected icons name (durable, no drift). Explains the root cause: no figma.* primitive reads an instance-swap selection as a string. --- packages/main/FIGMA_ICON_NAME_PROPOSAL.md | 86 +++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 packages/main/FIGMA_ICON_NAME_PROPOSAL.md diff --git a/packages/main/FIGMA_ICON_NAME_PROPOSAL.md b/packages/main/FIGMA_ICON_NAME_PROPOSAL.md new file mode 100644 index 0000000000000..2e038652477d9 --- /dev/null +++ b/packages/main/FIGMA_ICON_NAME_PROPOSAL.md @@ -0,0 +1,86 @@ +# Proposal: make icon names readable in Code Connect (SAP Web UI Kit) + +**Audience:** SAP Web UI Kit Figma owners + webcomponents/tooling team +**File:** SAP Web UI Kit, `SILcWzK5uFghKun9jx6D7c` +**Date:** 2026-08-13 + +## The problem + +Several components' Code Connect mappings (Button, Link, StepInput, Avatar, +SegmentedButton, MessageStrip) can detect that an icon is **present** — and often +**where** it sits (Link's Icon Position, Avatar's Type=Icon) — but they **cannot +emit which icon it is**. Every `icon=` / `endIcon=` / `fallback-icon=` in the +generated snippets is a **hardcoded placeholder** (`"globe"`, `"inspect"`, +`"home"`, `"employee"`, `"information"`) that the consumer must edit by hand. + +### Why (root cause) + +In Figma an icon is an **INSTANCE_SWAP** property — the designer swaps in an icon +*component* from the icon library. Code Connect's mapping primitives are: + +`figma.string` (reads a **text** property) · `figma.boolean` · `figma.enum` +(fixed lookup over known variant options) · `figma.instance` · `figma.children` +· `figma.textContent` · `figma.nestedProps`. + +**None of them return "the name of the component currently swapped into an +instance-swap slot" as a string.** The name is visible in Figma's UI and exists +as instance metadata (`mainComponentName`), but it is not exposed to the mapping +author through any `figma.*` call. `figma.string` only reads text fields, and the +icon is not a text field. So the mapping can toggle the attribute on/off but must +hardcode the value. + +## Two fixes (complementary) + +### Option A — add an `Icon Name` text property → **Figma owner, quick win** + +On each component that has an icon instance-swap, add a plain **text property** +(e.g. `Icon Name`) holding the icon's registry name. The mapping then reads it: + +```ts +icon: figma.string("Icon Name") // → icon="employee" dynamically +``` + +- **Pro:** trivial, pure Figma-side, unlocks dynamic icon names immediately. +- **Con:** duplicated data — the designer must keep the typed name in sync with + the icon actually swapped in, so it can drift. + +### Option B — Code-Connect the icon library → **webcomponents/tooling team, durable** + +Give **each icon component** in the kit its own tiny Code Connect entry whose only +output is its own registry name. Then, on any host component: + +```ts +icon: figma.instance("Icon") // resolves to the SELECTED icon's name +``` + +because `figma.instance` resolves a swapped instance **through that instance's own +Code Connect entry**. + +- **This is generated, not hand-written.** The kit already names each icon + instance by its icon (`mainComponentName: "information"`), and the icons package + already has the full name registry. A script iterates the icon set and emits one + `figma.connect()` per icon (each outputting its name), using the icon library's + Figma node IDs (from the Figma API). +- **Pro:** correct forever, zero designer effort, no drift. +- **Con:** ~1400 published connections (one-time), needs the icon library node IDs, + re-run the generator when the icon set changes. + +**Can we drive this from our own `@ui5/webcomponents-icons` package?** Yes — that +package is the source of truth for icon names, so it's the natural input to the +generator in Option B. The only Figma-side dependency is the mapping from each +icon name to its Figma component node ID, which the Figma API provides. + +## Recommendation + +- **Option A now** — one text property per component, immediate dynamic icons. +- **Option B as the durable solution** — generated from `@ui5/webcomponents-icons`, + owned by the webcomponents team, no drift. + +Until either lands, icon **presence/position** is mapped but the **name** is a +placeholder the consumer edits. + +## Same class of issue (for context) + +The badge **count** on Button (`text="72"`) is hardcoded for the same reason — it +lives in an unexposed nested layer, not a readable property. Exposing it as a +`Badge Count` text property (like Option A) would make it dynamic too. From 4520be833c57230b79636a0491ea8cfd79413981 Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Thu, 13 Aug 2026 13:52:34 +0300 Subject: [PATCH 25/28] fix(figma): revert Button badge design to hardcoded; add runbook + icon prototype MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Button badge design CANNOT be driven by Form Factor (nested figma.enum AND cross-prop refs both emit verbatim) — reverted to hardcoded design=OverlayText (WC + React). Removed the icon diagnostic probe. - Icons.figma.ts: prototype proving individual icons can be Code-Connected (Option B). figma.instance resolved on Button (a host with a mainComponent link); empty on MessageStrip (no link). - Findings doc: add How-to-connect runbook (file/configs/token/command + the 11 connected nodes) at top. --- packages/main/FIGMA_CODE_CONNECT_FINDINGS.md | 34 ++++++++++++++++++++ packages/main/src/Button.figma.ts | 27 ++++++++-------- packages/main/src/Button.figma.tsx | 25 ++++---------- packages/main/src/Icons.figma.ts | 33 +++++++++++++++++++ packages/main/src/MessageStrip.figma.ts | 7 ++-- 5 files changed, 91 insertions(+), 35 deletions(-) create mode 100644 packages/main/src/Icons.figma.ts diff --git a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md index 3cbea7175bf46..a05b8fc8b2d5f 100644 --- a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md +++ b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md @@ -1,5 +1,38 @@ # Figma Code Connect — what doesn't work / what's assumed +## How to connect a component (runbook) + +**Figma file:** SAP Web UI Kit — fileKey `SILcWzK5uFghKun9jx6D7c`. + +**Two configs (run from `packages/main`):** +- `figma.config.json` — parser `html`, label "Web Components", globs `src/*.figma.ts` +- `figma.config.react.json` — parser `react`, label "React", globs `src/*.figma.tsx` + +**Steps to connect a new component:** +1. Get the component's **node id** (from the Figma URL `?node-id=1-2` → `1:2`). +2. Inspect its readable props (variants / booleans / text / nested instances) — either in Figma's properties panel, or scaffold a starter file that lists them: + ``` + FIGMA_ACCESS_TOKEN= npx figma connect create "" --outDir /tmp/cc + ``` + (`figma connect create` writes a `.figma.ts` pre-filled with the node's readable properties — a good starting point to edit.) +3. Write `src/.figma.ts` (WC) and `src/.figma.tsx` (React) — one `figma.connect(, {...})` each. +4. **Dry-run** (no token): `npx figma connect publish --dry-run -c figma.config.json` +5. **Publish** (needs token, run from `packages/main`): + ``` + FIGMA_ACCESS_TOKEN= npx figma connect publish -c figma.config.json --force + FIGMA_ACCESS_TOKEN= npx figma connect publish -c figma.config.react.json --force + ``` + - `--force` overwrites any pre-existing (UI-created) mapping on the node. + - MUST run from `packages/main` — from repo root the CLI silently falls back to the html parser. Always confirm `Using label "React"` in the output. +6. **Verify in Dev Mode** — parsing ≠ correct output; check the real snippet. + +**Connected so far (11, both WC + React):** +Button `91702:11733` · Input `148569:1004` · CheckBox `154589:905` · +RadioButton `154597:1967` · StepInput `148569:1727` · MessageStrip `910:2517` · +Select `181557:7507` · SegmentedButton `91702:11986` · Switch `24087:10369` · +Link `187:305` · Avatar `573:3623`. +(Icon `983:5876` is unpublishable — a plain frame, not a component set.) + ## ⚠️ Global: icon NAMES can never be read In Figma an icon is an **INSTANCE_SWAP** property — the designer swaps in an icon *component* from the library. Code Connect can read variants, booleans, and text, @@ -11,6 +44,7 @@ name** (`"globe"`, `"inspect"`, `"home"`, `"employee"`); the consumer edits it. *Owner fixes: (a) add an `Icon Name` text property alongside the swap, or (b) Code-Connect the icon library (one entry per icon emitting its own name).* + ## 1. Button — ui5-button (node 91702:11733) **Doesn't work:** - badge `text` — **HARDCODED `"72"`, NOT read from Figma.** The count is in an unexposed nested layer; the snippet always emits `text="72"` and won't track the Figma number. *Owner fix: expose the count as a readable text prop.* diff --git a/packages/main/src/Button.figma.ts b/packages/main/src/Button.figma.ts index ec645e3e559ce..2d4f70f3d5fe1 100644 --- a/packages/main/src/Button.figma.ts +++ b/packages/main/src/Button.figma.ts @@ -48,26 +48,25 @@ figma.connect( }), // Leading icon presence → `icon="…"`. - // LIMITATION: the icon NAME is hardcoded to "globe". Figma models the - // icon as an INSTANCE_SWAP whose swapped icon name cannot be read into a - // string unless the Kit's icon components are themselves Code-Connected. - // So this only toggles the attribute on/off; it can't reflect which icon - // is selected. See FIGMA_CODE_CONNECT_FINDINGS.md § "Icon is not dynamic". + // LIMITATION: icon NAME hardcoded to "globe". Button takes the icon as a + // name-string attribute (icon="globe"), but Figma models it as an + // INSTANCE_SWAP. figma.instance() returns the icon ELEMENT (), + // not a bare string, so it can't feed icon="…". Only Option A (an + // `Icon Name` text prop + figma.string) makes this dynamic. See + // FIGMA_ICON_NAME_PROPOSAL.md. iconAttr: figma.boolean("Icon Left", { true: 'icon="globe"', false: "", }), - // Counter badge → slotted . - // Counter Badge is a Figma VARIANT (True/False), NOT a boolean — must use - // figma.enum (figma.boolean silently fails to match a variant → no badge). - // ASSUMPTION (design rule, per kit owners, NOT visually re-verified): - // Form Factor drives the badge design — Compact → InlineText, Cozy → - // OverlayText. HARDCODED: text="72" — the count is NOT read from Figma - // (unexposed nested layer), so it won't track the Figma number. - // See FIGMA_CODE_CONNECT_FINDINGS.md § Button. + // Counter badge → slotted . Counter Badge is a Figma + // VARIANT (True/False), NOT a boolean. + // design="OverlayText" HARDCODED: it CANNOT be driven by Form Factor — + // both a nested figma.enum in the template AND a cross-prop ${prop} ref + // emit VERBATIM (parser resolves neither inside a prop's template value). + // text="72" HARDCODED: count is in an unexposed nested layer, not readable. counterBadge: figma.enum("Counter Badge", { - True: html``, + True: html``, False: "", }), diff --git a/packages/main/src/Button.figma.tsx b/packages/main/src/Button.figma.tsx index c6a660c9335fb..abd06a818e2eb 100644 --- a/packages/main/src/Button.figma.tsx +++ b/packages/main/src/Button.figma.tsx @@ -46,26 +46,15 @@ figma.connect( // Counter badge presence → child on the `badge` prop. // Counter Badge is a Figma VARIANT (True/False), NOT a boolean — use - // figma.enum. ASSUMPTION (design rule, per kit owners, NOT visually - // re-verified): Form Factor drives badge design — Compact → InlineText, - // Cozy → OverlayText. HARDCODED: text="72" — count is NOT read from Figma - // (unexposed nested layer), won't track the Figma number. + // figma.enum. design HARDCODED to OverlayText: it CANNOT be driven by + // Form Factor (a nested figma.enum emits verbatim). text="72" HARDCODED + // (count in unexposed nested layer, not readable). // - // REACT ASYMMETRY: the `badge` prop can only reference ONE Figma axis for - // PRESENCE (parser rejects compound placeholders like `counter ?? - // attention`), so React drives `badge` from Counter Badge only. The - // Attention Badge (a separate Figma boolean) is NOT expressible here — the - // WC mapping (Button.figma.ts) emits both. See findings § Button. + // REACT ASYMMETRY: the `badge` prop references ONE Figma axis, so React + // drives it from Counter Badge only; the Attention Badge is NOT + // expressible here (WC emits both). See findings § Button. badge: figma.enum("Counter Badge", { - True: ( - - ), + True: , False: undefined, }), }, diff --git a/packages/main/src/Icons.figma.ts b/packages/main/src/Icons.figma.ts new file mode 100644 index 0000000000000..dd1dd4c0cad9c --- /dev/null +++ b/packages/main/src/Icons.figma.ts @@ -0,0 +1,33 @@ +/** + * PROTOTYPE (Option B): Code Connect entries for individual icons. + * + * Proves that connecting each icon component to emit its own name lets a host + * component read the SELECTED icon via figma.instance() — making icon names + * dynamic instead of hardcoded placeholders. + * + * Hand-written for a handful of icons as proof of concept. The parser requires + * a LITERAL URL per figma.connect (no loops/computed URLs), so the real thing + * would be GENERATED from @ui5/webcomponents-icons + the icon node IDs. + * See FIGMA_ICON_NAME_PROPOSAL.md. Icons live on page "❖ Iconography". + */ +import figma, { html } from "@figma/code-connect/html"; + +figma.connect( + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=1095-2437", + { example: () => html`` } +); + +figma.connect( + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=1105-2390", + { example: () => html`` } +); + +figma.connect( + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=1105-2394", + { example: () => html`` } +); + +figma.connect( + "https://www.figma.com/design/SILcWzK5uFghKun9jx6D7c/SAP-Web-UI-Kit?node-id=1105-2344", + { example: () => html`` } +); diff --git a/packages/main/src/MessageStrip.figma.ts b/packages/main/src/MessageStrip.figma.ts index c9dd0f922ca18..bd46b970a9978 100644 --- a/packages/main/src/MessageStrip.figma.ts +++ b/packages/main/src/MessageStrip.figma.ts @@ -63,9 +63,10 @@ figma.connect( }), // Message text — read the "Text Message" layer directly. message: figma.textContent("Text Message"), - // Custom icon slot when Icon=True. The icon NAME is a placeholder - // ("information") — the Icon instance-swap name isn't readable. Semantic - // designs show their own default icon; ColorSet1/2 need this explicit slot. + // Custom icon slot when Icon=True. Name is a placeholder ("information") — + // the Icon instance-swap name isn't readable. (figma.instance("Icon") was + // tried to resolve the real icon via Code-Connected icons — it emitted + // EMPTY, so the placeholder is kept. See FIGMA_ICON_NAME_PROPOSAL.md.) iconSlot: figma.enum("Icon", { True: html``, False: "", From fb684e7b3d8c20da281cbef97bbc8239a7e54b1f Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Thu, 13 Aug 2026 13:58:17 +0300 Subject: [PATCH 26/28] docs(figma): correct the icon global note with tested nuance Old note claimed the swapped icon can never be read. The prototype disproved the absolute: name-as-STRING is unreadable (attribute icons stay hardcoded), but the selected icon as an ELEMENT resolves via figma.instance() when the icon library is Code-Connected AND the host instance carries a mainComponent link (tested: worked on Button, empty on MessageStrip). --- packages/main/FIGMA_CODE_CONNECT_FINDINGS.md | 28 +++++++++++++------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md index a05b8fc8b2d5f..f04055115b1bb 100644 --- a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md +++ b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md @@ -33,16 +33,26 @@ Select `181557:7507` · SegmentedButton `91702:11986` · Switch `24087:10369` · Link `187:305` · Avatar `573:3623`. (Icon `983:5876` is unpublishable — a plain frame, not a component set.) -## ⚠️ Global: icon NAMES can never be read +## ⚠️ Global: reading the swapped icon (nuanced — tested) In Figma an icon is an **INSTANCE_SWAP** property — the designer swaps in an icon -*component* from the library. Code Connect can read variants, booleans, and text, -but **cannot read which component was swapped into an instance-swap slot as a -string**. So we can detect that an icon is present, and often *where* (Link's Icon -Position, Avatar's Type=Icon), but never *which* icon. Every -`icon=`/`endIcon=`/`fallback-icon=` therefore emits a **hardcoded placeholder -name** (`"globe"`, `"inspect"`, `"home"`, `"employee"`); the consumer edits it. -*Owner fixes: (a) add an `Icon Name` text property alongside the swap, or (b) -Code-Connect the icon library (one entry per icon emitting its own name).* +*component* from the library. What Code Connect can and can't do with it: + +- **As a name STRING → NO.** `figma.string`/`figma.enum` cannot read which + component was swapped in. So attribute-style icons (`icon="globe"`, `endIcon=`, + `fallback-icon=`) can't be made dynamic this way and stay **hardcoded** + placeholders (`"globe"`, `"inspect"`, `"home"`, `"employee"`); consumer edits. +- **As an ELEMENT via `figma.instance()` → YES, conditionally.** If the icon + library is **Code-Connected** (each icon emits ``) AND the + host's icon instance carries a `mainComponent` link, `figma.instance("Icon")` + resolves the **selected** icon's element. **Tested:** ✅ worked on Button + (`` resolved); ❌ empty on MessageStrip (its instance + had no `mainComponent` link). Only fits **slot/child** placements — its output + is an element, not a bare string, so it still can't feed an `icon=` attribute. + +**Owner fixes:** (a) add an `Icon Name` **text property** alongside the swap → +`figma.string` reads it → works for the attribute cases; (b) **Code-Connect the +icon library** (generated, ~1400 entries) → `figma.instance` resolves the +element for slot cases on link-carrying hosts. ## 1. Button — ui5-button (node 91702:11733) From 30d1cb28f4b106a99d40513031afab2eb2caf7ec Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Thu, 13 Aug 2026 15:06:13 +0300 Subject: [PATCH 27/28] docs(figma): expand runbook into full setup + per-component guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Onboarding-style: (1) clone + yarn install, (2) config files explained, (3) generate token via Settings→Security, (4) create the two mapping files, (5) dry-run, (6) publish with --force, (7) verify in Dev Mode. All steps anchored to packages/main. --- packages/main/FIGMA_CODE_CONNECT_FINDINGS.md | 80 ++++++++++++++------ 1 file changed, 57 insertions(+), 23 deletions(-) diff --git a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md index f04055115b1bb..455b78445c59c 100644 --- a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md +++ b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md @@ -1,30 +1,64 @@ # Figma Code Connect — what doesn't work / what's assumed -## How to connect a component (runbook) +## How to connect a component (setup + runbook) **Figma file:** SAP Web UI Kit — fileKey `SILcWzK5uFghKun9jx6D7c`. - -**Two configs (run from `packages/main`):** -- `figma.config.json` — parser `html`, label "Web Components", globs `src/*.figma.ts` -- `figma.config.react.json` — parser `react`, label "React", globs `src/*.figma.tsx` - -**Steps to connect a new component:** -1. Get the component's **node id** (from the Figma URL `?node-id=1-2` → `1:2`). -2. Inspect its readable props (variants / booleans / text / nested instances) — either in Figma's properties panel, or scaffold a starter file that lists them: - ``` - FIGMA_ACCESS_TOKEN= npx figma connect create "" --outDir /tmp/cc - ``` - (`figma connect create` writes a `.figma.ts` pre-filled with the node's readable properties — a good starting point to edit.) -3. Write `src/.figma.ts` (WC) and `src/.figma.tsx` (React) — one `figma.connect(, {...})` each. -4. **Dry-run** (no token): `npx figma connect publish --dry-run -c figma.config.json` -5. **Publish** (needs token, run from `packages/main`): - ``` - FIGMA_ACCESS_TOKEN= npx figma connect publish -c figma.config.json --force - FIGMA_ACCESS_TOKEN= npx figma connect publish -c figma.config.react.json --force - ``` - - `--force` overwrites any pre-existing (UI-created) mapping on the node. - - MUST run from `packages/main` — from repo root the CLI silently falls back to the html parser. Always confirm `Using label "React"` in the output. -6. **Verify in Dev Mode** — parsing ≠ correct output; check the real snippet. +**Everything below runs from `packages/main`.** + +### One-time setup + +**1. Clone the repo and install** (from the repo root): +``` +git clone https://github.com/SAP/ui5-webcomponents.git +cd ui5-webcomponents +yarn # installs all workspace deps (incl. @figma/code-connect) +``` +`@figma/code-connect` (^1.4.9) is already a devDependency of `packages/main` — no +separate install needed. (To add it to another package: `yarn add -D @figma/code-connect`.) + +**2. Config files** — already committed in `packages/main`, one per label: +- `figma.config.json` → parser `html`, label **"Web Components"**, include `src/**/*.figma.ts` +- `figma.config.react.json` → parser `react`, label **"React"**, include `src/**/*.figma.tsx` +```jsonc +// figma.config.json +{ "codeConnect": { + "include": ["src/**/*.figma.ts"], "exclude": ["node_modules/**", "dist/**"], + "parser": "html", "label": "Web Components" } } +``` + +**3. Generate a Figma token:** figma.com → **profile → Settings → Security → +Personal access tokens → Generate**. Scope: file content read/write for Code +Connect. Keep it out of git; pass it inline (`FIGMA_ACCESS_TOKEN=…`) or export it. + +### Per component + +**4. Create the two mapping files** in `packages/main/src/`: +- `src/.figma.ts` (Web Components) and `src/.figma.tsx` (React) — + each a single `figma.connect(, { props, example })`. +- Get the node id from the Figma URL (`?node-id=1-2` → `1:2`). To scaffold a + starter pre-filled with the node's readable props: + ``` + FIGMA_ACCESS_TOKEN= npx figma connect create "" --outDir /tmp/cc + ``` + +**5. Dry-run** (no token — catches parser errors): +``` +npx figma connect publish --dry-run -c figma.config.json +npx figma connect publish --dry-run -c figma.config.react.json +``` + +**6. Publish** (needs the token; run from `packages/main`): +``` +FIGMA_ACCESS_TOKEN= npx figma connect publish -c figma.config.json --force +FIGMA_ACCESS_TOKEN= npx figma connect publish -c figma.config.react.json --force +``` +- `--force` overwrites any pre-existing (UI-created) mapping on the node. +- ⚠️ MUST run from `packages/main` — from the repo root the CLI can't find the + config and silently falls back to the html parser. Always confirm the output + says `Using label "React"` (not "Web Components") for the React publish. + +**7. Verify in Figma Dev Mode** — parsing/upload success ≠ correct output. Open +the node, check the real snippet under each framework label. **Connected so far (11, both WC + React):** Button `91702:11733` · Input `148569:1004` · CheckBox `154589:905` · From f0edcaea6ea1c91029e9ebb21593c09b9da1d765 Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Thu, 13 Aug 2026 15:13:33 +0300 Subject: [PATCH 28/28] docs(figma): make runbook accurate for a clean clone install @figma/code-connect and create the config files are real steps (a fresh main checkout has neither); note the PR branch has them pre-done. --- packages/main/FIGMA_CODE_CONNECT_FINDINGS.md | 40 +++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md index 455b78445c59c..c4802d40ef1b9 100644 --- a/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md +++ b/packages/main/FIGMA_CODE_CONNECT_FINDINGS.md @@ -9,30 +9,42 @@ **1. Clone the repo and install** (from the repo root): ``` -git clone https://github.com/SAP/ui5-webcomponents.git -cd ui5-webcomponents -yarn # installs all workspace deps (incl. @figma/code-connect) +git clone https://github.com/UI5/webcomponents.git +cd webcomponents +yarn # installs all workspace deps ``` -`@figma/code-connect` (^1.4.9) is already a devDependency of `packages/main` — no -separate install needed. (To add it to another package: `yarn add -D @figma/code-connect`.) +> On the branch that carries this Code Connect work, `@figma/code-connect` and the +> config files are already present (skip steps 2–3). On a clean `main` checkout +> they are **not** — do steps 2–3 first. -**2. Config files** — already committed in `packages/main`, one per label: -- `figma.config.json` → parser `html`, label **"Web Components"**, include `src/**/*.figma.ts` -- `figma.config.react.json` → parser `react`, label **"React"**, include `src/**/*.figma.tsx` +**2. Add the Code Connect dependency** to `packages/main` (only if not already there): +``` +cd packages/main +yarn add -D @figma/code-connect # this repo uses ^1.4.9 +``` + +**3. Create the two config files** in `packages/main`, one per label: + +`figma.config.json` (Web Components): ```jsonc -// figma.config.json { "codeConnect": { "include": ["src/**/*.figma.ts"], "exclude": ["node_modules/**", "dist/**"], "parser": "html", "label": "Web Components" } } ``` +`figma.config.react.json` (React): +```jsonc +{ "codeConnect": { + "include": ["src/**/*.figma.tsx"], "exclude": ["node_modules/**", "dist/**"], + "parser": "react", "label": "React" } } +``` -**3. Generate a Figma token:** figma.com → **profile → Settings → Security → +**4. Generate a Figma token:** figma.com → **profile → Settings → Security → Personal access tokens → Generate**. Scope: file content read/write for Code Connect. Keep it out of git; pass it inline (`FIGMA_ACCESS_TOKEN=…`) or export it. ### Per component -**4. Create the two mapping files** in `packages/main/src/`: +**5. Create the two mapping files** in `packages/main/src/`: - `src/.figma.ts` (Web Components) and `src/.figma.tsx` (React) — each a single `figma.connect(, { props, example })`. - Get the node id from the Figma URL (`?node-id=1-2` → `1:2`). To scaffold a @@ -41,13 +53,13 @@ Connect. Keep it out of git; pass it inline (`FIGMA_ACCESS_TOKEN=…`) or export FIGMA_ACCESS_TOKEN= npx figma connect create "" --outDir /tmp/cc ``` -**5. Dry-run** (no token — catches parser errors): +**6. Dry-run** (no token — catches parser errors): ``` npx figma connect publish --dry-run -c figma.config.json npx figma connect publish --dry-run -c figma.config.react.json ``` -**6. Publish** (needs the token; run from `packages/main`): +**7. Publish** (needs the token; run from `packages/main`): ``` FIGMA_ACCESS_TOKEN= npx figma connect publish -c figma.config.json --force FIGMA_ACCESS_TOKEN= npx figma connect publish -c figma.config.react.json --force @@ -57,7 +69,7 @@ FIGMA_ACCESS_TOKEN= npx figma connect publish -c figma.config.react.json config and silently falls back to the html parser. Always confirm the output says `Using label "React"` (not "Web Components") for the React publish. -**7. Verify in Figma Dev Mode** — parsing/upload success ≠ correct output. Open +**8. Verify in Figma Dev Mode** — parsing/upload success ≠ correct output. Open the node, check the real snippet under each framework label. **Connected so far (11, both WC + React):**