code-connect icon indicating copy to clipboard operation
code-connect copied to clipboard

Reference instance swap components by name (for web components)

Open plasticmind opened this issue 11 months ago • 2 comments

  • Code Connect CLI version 1.3.1
  • Operating system: macOS 13.3
  • Code Connect file, Figma design and/or relevant code snippet that could help us get more context
import figma, { html } from "@figma/code-connect/html";

figma.connect("<FIGMA_BUTTONS_BUTTON>", {
  props: {
    labelText: figma.string("Label Text"),
    size: figma.enum("Size", {
      sm: "sm",
      lg: "lg",
    }),
    variant: figma.enum("Variant", {
      outline: "outline",
      ghost: "ghost",
      text: "text",
    }),
    inverted: figma.boolean("Inverted"),
    disabled: figma.boolean("Disabled"),
    prefixIcon: figma.instance("Prefix Icon"),
    suffixIcon: figma.instance("Suffix Icon"),
  },
  example: (props) => html`
    Prefix Icon Shape: ${props.prefixIcon}\n
    Suffix Icon Shape: ${props.suffixIcon}\n
    <nys-button
      label="${props.labelText}"
      size="${props.size}"
      variant="${props.variant}"
      inverted="${props.inverted}"
      disabled="${props.disabled}"
    ></nys-button>
  `,
});

We're using the html version of Code Connect for our custom web component library. Everything here is working, except prefixIcon: figma.instance("Prefix Icon"), or "Suffix Icon" in this instance returns a blank.

Image

Technically, the <nys-button> element expects a suffixIcon and/or prefixIcon attribute with the name of the icon shape, which gets passed to <nys-icon> inside the custom element. You can see, the Prefix Icon is an instance swap that has a prop called "shape" which can reference our library of icons.

Image

This shape name is the name that we need to get to pass into the attribute:

Image

When I tried to nested properties approach:

    prefixIcon: figma.nestedProps("Prefix Icon", {
      shape: figma.string("shape"),
    }),
    suffixIcon: figma.nestedProps("Suffix Icon", {
      shape: figma.string("shape"),
    }),

    ...

    Prefix Icon Shape: ${props.prefixIcon.shape}
    Suffix Icon Shape: ${props.suffixIcon.shape}

And output the value of "shape", I'm getting something that looks like an ID:

Image

Is there any way I can capture the name here to pass as a string?

I came across this script for generating a full map of references for each icon, but in addition to this being really cumbersome and hard to maintain, it also seems to work only with React. Any recommendations for custom web components?

Thanks!

plasticmind avatar Feb 16 '25 02:02 plasticmind

it looks like you have a generic prefix and suffix icon component as a child, and then that component has an instance swap. If I understand correctly, this should be all that you for the button:

figma.connect("<FIGMA_BUTTONS_BUTTON>", {
  props: {
    // ...
    prefixIcon: figma.children("Prefix Icon"),
    suffixIcon: figma.children("Suffix Icon"),
  },
  example: (props) => html`
    Prefix Icon Shape: ${props.prefixIcon}\n
    Suffix Icon Shape: ${props.suffixIcon}\n
  `,
});

Then, in each of those two components (Prefix Icon and Suffix Icon) you need to add code connect that looks something like this:

figma.connect("<FIGMA_PREFIX_ICON>", {
  props: { icon: figma.instance("shape") },
  example: (props) => html`${props.icon}`,
});
figma.connect("<FIGMA_SUFFFIX_ICON>", {
  props: { icon: figma.instance("shape") },
  example: (props) => html`${props.icon}`,
});

Then, for each icon, you to return whatever name you want to see as a string:

figma.connect("<FIGMA_ICONS_CHEVRON_LEFT>", { example: () => html`chevron_left` });
figma.connect("<FIGMA_ICONS_CHEVRON_RIGHT>", { example: () => html`chevron_right` });

Does that make sense? This is a complicated case with the extra layer. Ideally the bulk script can be modified for the icons to generate the above html parser docs instead of the React. LMK if you need more direction here!

jake-figma avatar Mar 03 '25 19:03 jake-figma

@jake-figma I follow what you're saying but let me clarify a few things:

The Prefix Icon and Suffix Icon components are both instances of a single, unpublished component called _Fixed Icon. Our designer created this because he wanted the instance swap from the icon bubbling up to button, but not the size props. I think this is making thing a bit more complicated than they should be.

Image

Here's a simplified summary of our setup:

figma.connect("<FIGMA_BUTTON>", {
  props: {
    prefixIcon: figma.children("Prefix Icon"),
    suffixIcon: figma.children("Suffix Icon"),
  },
  example: (props) => html`
    <nys-button
      ${props.prefixIcon}
      ${props.suffixIcon}
    ></nys-button>
  `,
});

figma.connect("<FIGMA_ICON_FIXED>", {
  props: { icon: figma.instance("shape") },
  example: (props) => html`${props.icon}`,
});

When I try to use Figma connect on that component, I'm getting the error:

Validation failed for undefined (https://www.figma.com/design/url-for-fixed-icon-components/): corresponding node is not a component or component set

I have a suspicion that the issue is that we're not using our NYC Icon component in this instance.

Let me share our code from the NYS Icon component, as that's another related issue:

figma.connect("<FIGMA_ICON>", {
  props: {
    name: figma.instance("shape"),
  },
  example: (props) => html`
    <nys-icon ${props.name} size="${props.size}"> </nys-icon>
  `,
});

figma.connect("<FIGMA_ICONS_ACCOUNT_CIRCLE>", { example: () => html`account_circle` });
// Registered each icon shape component that can be pulled in from the `shape` instance swap

However, it shows up as a linked icon component, but not as a string for the prop, e.g. name="account_circle"

Image

If we can solve for NYS Icon, I'm sure we can solve for the NYS Button example. Thanks.

plasticmind avatar Mar 04 '25 15:03 plasticmind