figma / code-connect

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

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

Open glomotion opened 4 days ago

glomotion commented 4 days 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 3 days 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 3 days 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 2 days 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!