diff --git a/.cursor/rules/figma-rules.mdc b/.cursor/rules/figma-rules.mdc index 62b008a2771..b8c1ec878e2 100644 --- a/.cursor/rules/figma-rules.mdc +++ b/.cursor/rules/figma-rules.mdc @@ -3,23 +3,22 @@ description: globs: alwaysApply: true --- - + --- description: Figma Dev Mode MCP rules -globs: +globs: alwaysApply: true --- - The Figma Dev Mode MCP Server provides an assets endpoint which can serve image and SVG assets - - IMPORTANT: If the Figma Dev Mode MCP Server returns a localhost source for an image or an SVG, use that image or SVG source directly - - IMPORTANT: DO NOT import/add new icon packages, all the assets should be in the Figma payload - IMPORTANT: do NOT use or create placeholders if a localhost source is provided - IMPORTANT: Always use components from `/packages` whenever possible - - Prioritize using semantic tokens and component props over Figma fidelity - - Avoid hardcoded values, use semantic design tokens from Figma whenever possible - - Follow WCAG requirements for accessibility - - Add component documentation - - Place UI components in `/package/gamut`; avoid inline styles unless truly necessary - - Add Best Practices blah blah blah - - - + - Prioritize using semantic tokens and component props over Figma fidelity + - Avoid hardcoded values, use semantic design tokens whenever possible. The Background component is the exception - use color token names (i.e, navy, white, etc) and not a semantic token + - IMPORTANT: Do not use inline styles, whenever possible use emotion and the css-in-js utilities from `/packages/gamut-styles`. Follow the rules from `packages/styleguide/src/lib/Foundations/System` + - IMPORTANT: Follow WCAG requirements for accessibility + - Always follow best practices from `/packages/styleguide/src/lib/Meta/Best Practices.mdx` + - IMPORTANT: All patterns should come from `/packages/gamut-patterns` - match the Figma component name to the pattern component + - IMPORTANT: All icons should come from `/packages/gamut-icons` - match the Figma component name to the icon component + - Check if the Figma component name matches a Gamut component name and use that component + - Use the tokens from `packages/gamut-styles/src/variables` + - Use the CodeConnect implementation \ No newline at end of file diff --git a/figma.config.json b/figma.config.json index f8d35f39eda..6da98ba9501 100644 --- a/figma.config.json +++ b/figma.config.json @@ -1,6 +1,6 @@ { "codeConnect": { - "include": ["packages/gamut/src/**/*.{tsx,jsx}"], + "include": ["packages/gamut/src/**/*.{tsx,jsx}","packages/gamut-icons/figma/*.{tsx,jsx}"], "label": "React", "interactiveSetupFigmaFileUrl": "https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=23-5&p=f&m=dev" } diff --git a/packages/gamut/src/Badge/index.figma.tsx b/packages/gamut/src/Badge/index.figma.tsx index a0dabd0d32e..e0ba72c8a69 100644 --- a/packages/gamut/src/Badge/index.figma.tsx +++ b/packages/gamut/src/Badge/index.figma.tsx @@ -1,6 +1,7 @@ -import React from "react" -import { Badge } from "./index" -import figma from "@figma/code-connect" +import figma from '@figma/code-connect'; +import React from 'react'; + +import { Badge } from './index'; /** * -- This file was auto-generated by Code Connect -- @@ -12,24 +13,23 @@ import figma from "@figma/code-connect" figma.connect( Badge, - "https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=8200%3A8349", + 'https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=8200%3A8349', { props: { - label: figma.string("✏️ label"), - leadingicon: figma.boolean("leading-icon"), - icon: figma.instance("↳ icon"), - variant: figma.enum("variant", { - primary: "primary", - secondary: "secondary", - tertiary: "tertiary", - "tertiary-fill": "tertiary-fill", - accent: "accent", + label: figma.string('✏️ label'), + icon: figma.instance('↳ icon'), + variant: figma.enum('variant', { + primary: 'primary', + secondary: 'secondary', + tertiary: 'tertiary', + 'tertiary-fill': 'tertiary-fill', + accent: 'accent', }), - size: figma.enum("size", { - base: "base", - sm: "sm", + size: figma.enum('size', { + base: 'base', + sm: 'sm', }), }, - example: (props) => , - }, -) + example: (props) => , + } +); diff --git a/packages/gamut/src/Disclosure/index.figma.tsx b/packages/gamut/src/Disclosure/index.figma.tsx index b8ddcd31860..ad9651dc125 100644 --- a/packages/gamut/src/Disclosure/index.figma.tsx +++ b/packages/gamut/src/Disclosure/index.figma.tsx @@ -1,6 +1,7 @@ -import React from "react" -import { Disclosure } from "./index" -import figma from "@figma/code-connect" +import figma from '@figma/code-connect'; +import React from 'react'; + +import { Disclosure } from './index'; /** * -- This file was auto-generated by Code Connect -- @@ -12,22 +13,22 @@ import figma from "@figma/code-connect" figma.connect( Disclosure, - "https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=43343%3A19299", + 'https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=43343%3A19299', { props: { - variant: figma.enum("variant", { - default: "default", - subtle: "subtle", - transparent: "transparent", + variant: figma.enum('variant', { + default: 'default', + subtle: 'subtle', + transparent: 'transparent', }), - hasBorder: figma.boolean("hasBorder"), - spacing: figma.enum("spacing", { - normal: "normal", - condensed: "condensed", - compact: "compact", + hasBorder: figma.boolean('hasBorder'), + spacing: figma.enum('spacing', { + normal: 'normal', + condensed: 'condensed', + compact: 'compact', }), - isExpanded: figma.boolean("isExpanded"), + isExpanded: figma.boolean('isExpanded'), }, - example: (props) => , - }, -) + example: (props) => , + } +); diff --git a/packages/gamut/src/Menu/Menu.figma.tsx b/packages/gamut/src/Menu/Menu.figma.tsx index 37b30fc8614..64e867d84a5 100644 --- a/packages/gamut/src/Menu/Menu.figma.tsx +++ b/packages/gamut/src/Menu/Menu.figma.tsx @@ -1,6 +1,7 @@ -import React from "react" -import { Menu } from "./Menu" -import figma from "@figma/code-connect" +import figma from '@figma/code-connect'; +import React from 'react'; + +import { Menu } from './Menu'; /** * -- This file was auto-generated by Code Connect -- @@ -12,23 +13,20 @@ import figma from "@figma/code-connect" figma.connect( Menu, - "https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=1971%3A2562", + 'https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=1971%3A2562', { props: { - option3: figma.boolean("option-3"), - option4: figma.boolean("option-4"), - option5: figma.boolean("option-5"), - option6: figma.boolean("option-6"), - menuSeparator: figma.boolean("menuSeparator"), - variant: figma.enum("Variant", { - popover: "popover", - fixed: "fixed", + menuSeparator: figma.boolean('menuSeparator'), + variant: figma.enum('Variant', { + popover: 'popover', + fixed: 'fixed', }), - spacing: figma.enum("Spacing", { - normal: "normal", - condensed: "condensed", + spacing: figma.enum('Spacing', { + normal: 'normal', + condensed: 'condensed', }), + children: figma.children('*'), }, - example: (props) => , - }, -) + example: (props) => , + } +); diff --git a/packages/gamut/src/Menu/MenuItem.figma.tsx b/packages/gamut/src/Menu/MenuItem.figma.tsx deleted file mode 100644 index 0c565e318e3..00000000000 --- a/packages/gamut/src/Menu/MenuItem.figma.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from "react" -import { MenuItem } from "./MenuItem" -import figma from "@figma/code-connect" - -/** - * -- This file was auto-generated by Code Connect -- - * `props` includes a mapping from Figma properties and variants to - * suggested values. You should update this to match the props of your - * code component, and update the `example` function to return the - * code example you'd like to see in Figma - */ - -figma.connect( - MenuItem, - "https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=57295%3A20592", - { - props: { - leadingIcon: figma.boolean("leadingIcon"), - trailingIcon: figma.boolean("trailingIcon"), - label: figma.string("label"), - state: figma.enum("state", { - default: "default", - hover: "hover", - selected: "selected", - }), - }, - example: (props) => , - }, -) diff --git a/packages/gamut/src/Menu/MenuItemFixed.figma.tsx b/packages/gamut/src/Menu/MenuItemFixed.figma.tsx new file mode 100644 index 00000000000..337937aad5d --- /dev/null +++ b/packages/gamut/src/Menu/MenuItemFixed.figma.tsx @@ -0,0 +1,33 @@ +import figma from '@figma/code-connect'; +import React from 'react'; + +import { MenuItem } from './MenuItem'; + +/** + * -- This file was auto-generated by Code Connect -- + * `props` includes a mapping from Figma properties and variants to + * suggested values. You should update this to match the props of your + * code component, and update the `example` function to return the + * code example you'd like to see in Figma + */ + +figma.connect( + MenuItem, + 'https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=1971-2476', + { + props: { + active: figma.boolean('active'), + hover: figma.boolean('hover'), + label: figma.string('label'), + leadingIcon: figma.boolean('leading-icon'), + icon: figma.children('icon*'), + }, + example: ({ label, leadingIcon, icon, ...props }) => { + return ( + + {label} + + ); + }, + } +); diff --git a/packages/gamut/src/Menu/MenuItemPopover.figma.tsx b/packages/gamut/src/Menu/MenuItemPopover.figma.tsx new file mode 100644 index 00000000000..c84562afb0a --- /dev/null +++ b/packages/gamut/src/Menu/MenuItemPopover.figma.tsx @@ -0,0 +1,33 @@ +import figma from '@figma/code-connect'; +import React from 'react'; + +import { MenuItem } from './MenuItem'; + +/** + * -- This file was auto-generated by Code Connect -- + * `props` includes a mapping from Figma properties and variants to + * suggested values. You should update this to match the props of your + * code component, and update the `example` function to return the + * code example you'd like to see in Figma + */ + +figma.connect( + MenuItem, + 'https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=1971-2489', + { + props: { + active: figma.boolean('active'), + hover: figma.boolean('hover'), + label: figma.string('label'), + leadingIcon: figma.boolean('leading-icon'), + icon: figma.children('icon*'), + }, + example: ({ label, leadingIcon, icon, ...props }) => { + return ( + + {label} + + ); + }, + } +); diff --git a/script/jest/icon-figma.js b/script/jest/icon-figma.js new file mode 100644 index 00000000000..d0530cd12ef --- /dev/null +++ b/script/jest/icon-figma.js @@ -0,0 +1,52 @@ +import { client } from '@figma/code-connect'; +import fs from 'fs'; + +async function generateIcons() { + // fetch components from a figma file. If the `node-id` query parameter is used, + // only components within those frames will be included. This is useful if your + // file is very large, as this will speed up the query by a lot + let components = await client.getComponents( + 'https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=38228-2739' + ); + + // Converts icon names from e.g `icon-32-list` to `Icon32List` + components = components + .filter(({ name }) => { + return name.includes('icon'); + }) + .map((component) => ({ + ...component, + name: component.name + .split(/[.-]/g) + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + .join(''), + })); + + const uniqueNames = new Set([...components.map((c) => c.name)]); + + fs.writeFileSync( + 'icons.figma.tsx', + `\ + import figma from '@figma/code-connect' + + import { + ${Array.from(uniqueNames) + .map((iconName) => ` ${iconName},`) + .join('\n')} + } from './packages/gamut-icons/icons/regular' + + const props = { + height: figma.string('Height'), + width: figma.string('Width'), + color + +} + + ${components + .map((c) => `figma.connect(${c.name}, '${c.figmaUrl}')`) + .join('\n')} + ` + ); +} + +generateIcons();