figma / code-connect

A tool for connecting your design system components in code with your design system in Figma
MIT License
949 stars 68 forks source link

Howto: code-connect with (icon) subcomponents #86

Closed glomotion closed 3 weeks ago

glomotion commented 4 months ago

So our codebase applies icons in a slightly different way to MUI and the figma code-connect demo icon scripts etc.

Essentially, we have a root component, called Icon.

This icon component looks a bit like:

// simplified Icon.tsx
const icons = [
  "Add",
  "Alert",
  "Apple",
  ...
] as const;

export function Icon({ icon }: { icon: (typeof icons)[number] }) {
  const iconPaths = useSomeHookToGetSvgPathsData(icon);
  return <SvgIcon>{iconPaths}</SvgIcon>
}

Other components, like our <Button> component for example, support the Icon component as a subcomponent. eg:

<Button>
  <Button.Icon icon="Apple" />
  Some text
</Button>

The Button.Icon subcomponent looks something like this:

export function ButtonIcon({ icon }: { (typeof icons)[number] }) {
  return <Icon style={{ /* some unique styles */ }} icon={icon} />;
}
ButtonIcon.displayName = 'Button.Icon';

This allows us to apply Button specific styles to the <Button.Icon /> subcomponent.

Im kinda struggling to work out how to get this to work within code-connect.

Until today, the below code-connect file was kinda working within FIGMA: (the below code no longer renders anything for code-connect inside FIGMA, when either of the "Icon Left" or Icon Right" toggles are enabled inside of figma. No errors happen when the below code is published to figma, however 🤔 ).

// packages/react/src/components/Clickable/Buttons/Button.figma.tsx
import { figma } from "@figma/code-connect";

import { Button } from "@/components/Clickable/Buttons";

const FIGMA_URL =
  "https://www.figma.com/design/oHsoXoDsvnhjlbpzSIbHvU/BIOME?node-id=2-3486&m=dev";

const COMMON_PROPS = {
  children: figma.string("Label"),
  size: figma.enum("Size", {
    Large: "large",
    Medium: "medium",
    Small: "small",
  }),
  disabled: figma.enum("State", {
    Disabled: true,
  }),
  leftIcon: figma.boolean("Icon Left", {
    true: <Button.Icon icon="Apple" />,
    false: undefined,
  }),
  rightIcon: figma.boolean("Icon Right", {
    true: <Button.Icon icon="Apple" />,
    false: undefined,
  }),
};

const imports = ["import { Button } from '@biom3/react'"];
const example = ({
  fullVariant,
  children,
  size,
  disabled,
  leftIcon,
  rightIcon,
  // biome-ignore lint/suspicious/noExplicitAny: <explanation>
}: any) => {
  return (
    <Button variant={fullVariant} size={size} disabled={disabled}>
      {leftIcon}
      {children}
      {rightIcon}
    </Button>
  );
};

figma.connect(Button, FIGMA_URL, {
  variant: { Variant: "Primary" },
  props: {
    ...COMMON_PROPS,
    fullVariant: figma.enum("Type", {
      Fatal: "primary/fatal",
      Inverse: "primary/inverse",
      Default: "primary",
    }),
  },
  imports,
  example,
});

/* ... more figma.connect() statements */

However the downside of the above was obviously that the <Button.Icon /> subcomponent was always simply using icon="Apple" - instead of whatever icon was actually being set in figma. :(

Is there:

  1. a way to evolve the code connect above, so that the icon="" prop being passed to <Button.Icon> is the actual icon name in figma?
  2. any ideas why the code above no longer works inside of figma? Any known bugs/outages etc?
jyyang0 commented 4 months ago

Hey @glomotion, thanks for the report -- for the issue of the code connections not being displayed, this is an issue we're currently aware of and in the process of fixing, I'll post an update when it is resolved

As for the issue with your icons -- As you've identified here, in code connect we only parse your code so currently it will not have a connection to a property in code. The way you would implement this depends on how the icons are setup -- Are the icons their own components in Figma? If so, if the icon itself has a Code Connect associated with it, you could use a figma.children("Left Icon") (If the icon is just a nested instance) or the figma.instance (If the icon is an instance swap property) helpers in order to represent the nested code.

However, let me know if your components are set up differently.

glomotion commented 4 months ago

OK so I have done a bit more digging, and have gathered a bit more info on the structure of the actual icon entities themselves inside figma (by making some JSON api calls and looking at the data which comes back)

Screenshot 2024-07-04 at 2 43 10 PM

Basically, the designers have setup a page full of "COMPONENT_SET" entities. Each one has a name: eg the name of the Icon.

// component set JSON:
{
  id: '16204:38405',
  name: 'Notification',
  type: 'COMPONENT_SET',
  ...
}

Inside each icon component set, there are 2 children:

children: [
    {
      id: '16204:38406',
      name: 'Bold=Yes',
      type: 'COMPONENT',
     ...
    }, {
     id: '16204:38409',
      name: 'Bold=No',
      type: 'COMPONENT',
      ...
    }
]

Notice how the child components have nothing in their JSON about the fact they they are icons, and that they are variants of the "Notification" icon.

Somehow, these component_set > component entities, need to be mapped to their equivalents in code, which look like:

<Icon icon="Notification" variant="regular" />
// or
<Button.Icon icon="Nofitication" variant="bold" />
// or 
<MenuItem.Icon icon="Notification" />
// etc etc

Basically, over a dozen components across our design system actually support the <Icon /> component as a child subcomponent. So constructing a script to make a CodeConnect for every single icon, of every single possible subcomponent, seems a bit crazy. Thus, i'm hoping there is another way. 😅

karlpetersson commented 4 months ago

Hey @glomotion, thank you for this feedback. Agree that this seems far from ideal. We're currently thinking about how we can make connecting icons both easier and at the same time cover more use cases. I don't have anything concrete to share right now, but I'll bring this to the team to discuss and keep you posted!

glomotion commented 3 months ago

hey there @karlpetersson - just wondering if there is any updates you can share here yet? 🥺 Would still really love to try and get code connect integrated within our figma icons etc.

karlpetersson commented 2 months ago

Hi @glomotion - this is currently a high priority for us to resolve, and while it likely won't be ready for the coming release, we're aiming to support this asap.

slees-figma commented 3 weeks ago

Hey @glomotion! We've released some APIs in 1.2.0 to improve Code Connect for icons. For your example, you could initially connect icons as normal:

figma.connect("icon", {
  props: {
    iconId: "something",
    variant: figma.enum("Variant", {
      "regular": "regular",
      "bold": "bold",
    })
  },
  example: ({ iconId }) => <Icon id={iconId} />
})

Then use the new .render() API to get access to those icon props and render different JSX for each of your "parent" components, e.g. Button.Icon:

figma.connect("parent", {
  props: {
    leftIcon: figma.boolean('Icon Left', {
      true: figma
        .instance('IconInstance')
        .render<{
          iconId: string
          variant: 'regular' | 'bold'
        }>(({ iconId, variant }) => <Button.Icon icon={iconId} variant={variant} />),
      false: undefined,
    }),
  },
})

Let us know if you experience any issues getting set up here! For more info, see the docs: https://github.com/figma/code-connect/blob/main/docs/react.md#installation