├── .eslintrc.js ├── .gitignore ├── .husky └── pre-commit ├── .npmignore ├── .prettierrc ├── .storybook ├── main.js └── preview.js ├── .travis.yml ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── __mocks__ ├── figmaPluginMock.ts └── styleMock.ts ├── __tests__ ├── Button.test.tsx ├── Checkbox.test.tsx ├── Disclosure.test.tsx ├── Icon.test.tsx ├── IconButton.test.tsx ├── Input.test.tsx ├── Label.test.tsx ├── OnboardingTip.test.tsx ├── Radio.test.tsx ├── SectionTitle.test.tsx ├── SelectMenu.test.tsx ├── Switch.test.tsx ├── Textarea.test.tsx ├── Type.test.tsx └── __snapshots__ │ ├── Button.test.tsx.snap │ ├── Checkbox.test.tsx.snap │ ├── Disclosure.test.tsx.snap │ ├── Icon.test.tsx.snap │ ├── IconButton.test.tsx.snap │ ├── Input.test.tsx.snap │ ├── Label.test.tsx.snap │ ├── OnboardingTip.test.tsx.snap │ ├── Radio.test.tsx.snap │ ├── SectionTitle.test.tsx.snap │ ├── SelectMenu.test.tsx.snap │ ├── Switch.test.tsx.snap │ ├── Textarea.test.tsx.snap │ └── Type.test.tsx.snap ├── global.d.ts ├── package.json ├── rollup.config.js ├── setupTests.ts ├── src ├── components │ ├── Button.tsx │ ├── Checkbox.tsx │ ├── Disclosure.tsx │ ├── Icon.tsx │ ├── IconButton.tsx │ ├── Input.tsx │ ├── Label.tsx │ ├── OnboardingTip.tsx │ ├── Radio.tsx │ ├── SectionTitle.tsx │ ├── SelectMenu.tsx │ ├── Switch.tsx │ ├── Textarea.tsx │ ├── Type.tsx │ └── index.ts ├── constants │ ├── colorNames.ts │ ├── iconNames.ts │ ├── index.ts │ ├── sizes.ts │ ├── tints.ts │ └── weights.ts ├── index.ts └── types.ts ├── stories ├── Button.stories.tsx ├── Checkbox.stories.tsx ├── Disclosure.stories.tsx ├── Icon.stories.tsx ├── IconButton.stories.tsx ├── Input.stories.tsx ├── Label.stories.tsx ├── OnboardingTip.stories.tsx ├── Radio.stories.tsx ├── SectionTitle.stories.tsx ├── SelectMenu.stories.tsx ├── Switch.stories.tsx ├── Textarea.stories.tsx └── Type.stories.tsx ├── tsconfig.json └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | extends: [ 4 | 'eslint:recommended', 5 | 'plugin:prettier/recommended', 6 | 'plugin:react/recommended', 7 | ], 8 | plugins: ['prettier', 'react-hooks'], 9 | env: { 10 | browser: true, 11 | node: true, 12 | jest: true, 13 | }, 14 | parserOptions: { 15 | ecmaVersion: 6, 16 | sourceType: 'module', 17 | ecmaFeatures: { 18 | jsx: true, 19 | }, 20 | }, 21 | rules: { 22 | 'react/prop-types': 'off', 23 | 'react-hooks/rules-of-hooks': 'error', 24 | 'react-hooks/exhaustive-deps': 'warn', 25 | 'react/display-name': 'off', 26 | 'no-unused-vars': 'off', 27 | }, 28 | settings: { 29 | react: { 30 | version: 'detect', 31 | }, 32 | }, 33 | }; 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | yarn-error.log 4 | .rpt2_cache 5 | storybook-static 6 | coverage 7 | lib 8 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn pre-commit 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | stories 3 | storybook-static 4 | .storybook 5 | __tests__ 6 | __mocks__ 7 | coverage 8 | .husky 9 | tsconfig.json 10 | .travis.yml 11 | .prettierrc 12 | .eslintrc.js 13 | global.d.ts 14 | setupTests.ts 15 | rollup.config.js 16 | yarn-error.log 17 | .rpt2_cache 18 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stories: ['../stories/**/*.stories.tsx'], 3 | addons: [ 4 | '@storybook/addon-docs', 5 | '@storybook/addon-controls', 6 | '@storybook/addon-storysource', 7 | '@storybook/addon-backgrounds', 8 | ], 9 | }; 10 | -------------------------------------------------------------------------------- /.storybook/preview.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { addDecorator } from '@storybook/react'; 3 | 4 | export const parameters = { 5 | previewTabs: { 6 | 'storybook/docs/panel': { 7 | hidden: true, 8 | }, 9 | }, 10 | backgrounds: { 11 | disable: true, 12 | }, 13 | docs: { page: null }, 14 | }; 15 | 16 | addDecorator((Story) => ( 17 |
28 | 29 |
30 | )); 31 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: node 4 | 5 | cache: 6 | directories: 7 | - node_modules 8 | 9 | script: 10 | - yarn install 11 | - yarn test 12 | - bash <(curl -s https://codecov.io/bash) 13 | - yarn build 14 | - yarn build-storybook 15 | 16 | jobs: 17 | include: 18 | - stage: release 19 | node_js: lts/* 20 | deploy: 21 | - provider: script 22 | script: yarn semantic-release 23 | 24 | - provider: pages 25 | github_token: $GH_TOKEN 26 | local_dir: storybook-static 27 | on: 28 | branch: main 29 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # React Figma UI Changelog 2 | 3 | ## 1.1.0 (2021-05-17) 4 | 5 | ### Breaking Changes 6 | 7 | - updated `figma-plugin-ds` to v1 8 | 9 | ### Bug Fix 10 | 11 | - fixed incorrect icon names 12 | - fixed incorrect types for `onChange` event 13 | 14 | ## 1.0.1 (2020-12-02) 15 | 16 | ### Bug Fix 17 | 18 | - added missing `patch-package` & `postinstall-postinstall` to dependencies 19 | 20 | ## 1.0.0 beta 12 / 1.0.0 (2020-08-26) 21 | 22 | ### Bug Fix 23 | 24 | - fixed incorrect tint type ([#13](https://github.com/JB1905/react-figma-ui/issues/13)) 25 | 26 | ### Repository Changes 27 | 28 | - added badges in README.md 29 | - updated CI config 30 | - updated docs 31 | - added coverage logs 32 | 33 | ## 1.0.0 beta 4 / 1.0.0 beta 5 / 1.0.0 beta 6 (2020-06-22) 34 | 35 | ### Internal 36 | 37 | - added `sTrimmer` to remove whitespaces in class names 38 | - cleaned up 39 | 40 | ### Repository Changes 41 | 42 | - updated types for stories 43 | 44 | ## 1.0.0 beta 3 (2020-05-24) 45 | 46 | ### Breaking Changes 47 | 48 | - renamed `DisclosureItem` to `DisclosureTip` 49 | 50 | ### Repository Changes 51 | 52 | - updated stories 53 | - updated tests 54 | 55 | ### Docs 56 | 57 | - updated examples in README.md 58 | 59 | ## 1.0.0 beta 1 / 1.0.0 beta 2 (2020-05-23) 60 | 61 | ### New Feature 62 | 63 | #### Components 64 | 65 | - Button 66 | - Checkbox 67 | - Disclosure 68 | - Icon 69 | - IconButton 70 | - Input 71 | - Label 72 | - Onboarding 73 | - Radio 74 | - SectionTitle 75 | - SelectMenu 76 | - Switch 77 | - Textarea 78 | - Type 79 | 80 | #### Repository 81 | 82 | - added Storybook preview 83 | - added snapshot tests with Jest 84 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-present Jakub Biesiada 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [React Figma UI](https://github.com/JB1905/react-figma-ui) 2 | 3 | [![NPM version](https://img.shields.io/npm/v/react-figma-ui?style=flat-square)](https://www.npmjs.com/package/react-figma-ui) 4 | [![NPM downloads](https://img.shields.io/npm/dm/react-figma-ui?style=flat-square)](https://www.npmjs.com/package/react-figma-ui) 5 | [![NPM license](https://img.shields.io/npm/l/react-figma-ui?style=flat-square)](https://www.npmjs.com/package/react-figma-ui) 6 | [![Codecov](https://img.shields.io/codecov/c/github/JB1905/react-figma-ui?style=flat-square)](https://codecov.io/gh/JB1905/react-figma-ui) 7 | [![Travis](https://img.shields.io/travis/com/JB1905/react-figma-ui/main?style=flat-square)](https://app.travis-ci.com/github/JB1905/react-figma-ui) 8 | [![Bundle size](https://img.shields.io/bundlephobia/min/react-figma-ui?style=flat-square)](https://bundlephobia.com/result?p=react-figma-ui) 9 | 10 | ## About 11 | 12 | React implementation for [figma-plugin-ds](https://github.com/thomas-lowry/figma-plugin-ds/) by [Tom Lowry](https://github.com/thomas-lowry/) 13 | 14 | ### Demo 15 | 16 | [**Playground – play with the library in Storybook**](https://jb1905.github.io/react-figma-ui/) 17 | 18 | ### Alternatives 19 | 20 | - [react-figma-plugin-ds](https://github.com/alexandrtovmach/react-figma-plugin-ds/) by [Alexandr Tovmach](https://github.com/alexandrtovmach/) 21 | - [Figma Styled Components](https://github.com/jhardy/figma-styled-components/) by [Jared](https://github.com/jhardy/) 22 | - [figma-ui-components](https://github.com/lessmess-dev/figma-ui-components/) by [lessmess](https://github.com/lessmess-dev/) 23 | - [Figma React UI Kit](https://github.com/LiamMartens/figma-react-ui-kit/) by [Liam Martens](https://github.com/LiamMartens/) 24 | 25 | ## Contents 26 | 27 | - [How to Install](#how-to-install) 28 | - [Components](#components) 29 | - [Button](#button) 30 | - [Checkbox](#checkbox) 31 | - [Disclosure](#disclosure) 32 | - [Icon](#icon) 33 | - [Icon button](#icon-button) 34 | - [Input](#input) 35 | - [Labels and sections](#labels-and-sections) 36 | - [Onboarding tip](#onboarding-tip) 37 | - [Radio button](#radio-button) 38 | - [Select menu](#select-menu) 39 | - [Switch](#switch) 40 | - [Textarea](#textarea) 41 | - [Type](#type) 42 | 43 | ## How to Install 44 | 45 | First, install the library in your project by npm: 46 | 47 | ```sh 48 | $ npm install react-figma-ui 49 | ``` 50 | 51 | Or Yarn: 52 | 53 | ```sh 54 | $ yarn add react-figma-ui 55 | ``` 56 | 57 | ## Components 58 | 59 | ### Button 60 | 61 | To use the button, use the following component. Each button has a destructive option. Tertiary buttons are styled like hyperlinks. 62 | 63 | ```jsx 64 | import { Button } from 'react-figma-ui'; 65 | 66 | // Primary 67 | 68 | 69 | 70 | 71 | // Secondary 72 | 73 | 74 | 75 | 76 | // Tertiary (Hyperlink style button) 77 | 78 | 79 | 80 | ``` 81 | 82 | #### Available options 83 | 84 | [HTML button element props](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attributes) and dedicated params 85 | 86 | | Param | Description | 87 | | ------------- | -------------------------------------------------------------------------------------- | 88 | | `tint` | Display style for button: primary (filled), secondary (outlined), tertiary (hyperlink) | 89 | | `destructive` | Add red destructive variant for actions such as deleting something | 90 | 91 | --- 92 | 93 | ### Checkbox 94 | 95 | To use the checkbox, use the following component. Remember each checkbox should get a unique ID. 96 | 97 | ```jsx 98 | import { Checkbox } from 'react-figma-ui'; 99 | 100 | // Checkbox unchecked 101 | Label 102 | 103 | // Checkbox checked 104 | Label 105 | 106 | // Checkbox disabled 107 | Label 108 | ``` 109 | 110 | #### Available options 111 | 112 | [HTML input element props](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attributes) and dedicated params 113 | 114 | | Param | Description | 115 | | ----------------------------------------------------------------------------------------------------------------- | ---------------------------- | 116 | | [`containerProps`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#list_of_global_attributes) | Props for checkbox container | 117 | | [`labelProps`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label#attributes) | Props for label element | 118 | 119 | --- 120 | 121 | ### Disclosure 122 | 123 | To use a disclosure panel, you must use the following component. 124 | 125 | ```jsx 126 | import { Disclosure, DisclosureItem } from 'react-figma-ui'; 127 | 128 | // Example items 129 | const items = [ 130 | { heading: 'Heading 1', content: 'Content 1', id: 1 }, 131 | { heading: 'Heading 2', content: 'Content 2', id: 2 }, 132 | { heading: 'Heading 3', content: 'Content 3', id: 3 }, 133 | ]; 134 | 135 | ( 138 | 145 | )} 146 | /> 147 | ``` 148 | 149 | #### Available options 150 | 151 | **Disclosure** 152 | 153 | [HTML ul element props](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#list_of_global_attributes) and dedicated params 154 | 155 | | Param | Description | 156 | | -------- | ------------------------------- | 157 | | `items` | Array with disclosure items | 158 | | `render` | Render props for DisclosureItem | 159 | 160 | **DisclosureItem** 161 | 162 | [HTML li element props](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li#attributes) and dedicated params 163 | 164 | | Param | Description | 165 | | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | 166 | | `heading` | Heading text value | 167 | | `content` | Content text value | 168 | | `section` | Style label like a heading | 169 | | `expanded` | Add this option to have item expanded on load | 170 | | [`labelProps`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#list_of_global_attributes) | Props for label element | 171 | | [`contentProps`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#list_of_global_attributes) | Props for content element | 172 | 173 | --- 174 | 175 | ### Icon 176 | 177 | To use the icon, use the following component. Apply the appropriate icon name to select the item you wish to use, you can also add option to change the color, or even spin the icon. You can also specify no icon name to use a text character as an icon (for example, like found in the width + height icon inputs in Figma) 178 | 179 | ```jsx 180 | import { Icon } from 'react-figma-ui'; 181 | 182 | // Icon 183 | 184 | 185 | // Icon with blue colorName to change color 186 | 187 | 188 | // Spinner Icon with spinning animation 189 | 190 | 191 | // Text Icon 192 | W 193 | ``` 194 | 195 | #### Available options 196 | 197 | [HTML div element props](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#list_of_global_attributes) and dedicated params 198 | 199 | | Param | Description | 200 | | ------------- | ---------------------------------------------------------------------------------- | 201 | | `iconName` | Specify which icon to use (e.g.: `alert`, `draft`, `settings`) | 202 | | `spin` | Causes the icon to spin in an endless loop (e.g.: loader used with `spinner` icon) | 203 | | `colorName`\* | Pass the name of any Figma color var to this prop (e.g.: `blue`, `black3`) | 204 | 205 | \*Colors accepted: `blue`, `purple`, `purple4`, `hot-pink`, `green`, `red`, `yellow`, `black`, `black8`, `black3`, `white`, `white8`, `white4` 206 | 207 | [Preview available icons here](https://github.com/thomas-lowry/figma-plugin-ds/#icon) 208 | 209 | --- 210 | 211 | ### Icon button 212 | 213 | The icon button is essentially a wrapper for the icon component. 214 | 215 | ```jsx 216 | import { IconButton } from 'react-figma-ui'; 217 | 218 | // Icon button with a blend icon 219 | 220 | 221 | // Icon button with selected option 222 | 223 | ``` 224 | 225 | #### Available options 226 | 227 | [HTML div element props](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#list_of_global_attributes) and dedicated params 228 | 229 | | Param | Description | 230 | | ----------------------------------- | ------------------------------------------------- | 231 | | `selected` | Add this option to have selected style for button | 232 | | [`iconProps`](#available-options-3) | Props for icon component | 233 | 234 | --- 235 | 236 | ### Input 237 | 238 | To use the input, use the following component. 239 | 240 | ```jsx 241 | import { Input } from 'react-figma-ui'; 242 | 243 | // Input with placeholder 244 | 245 | 246 | // Input with initial value 247 | 248 | 249 | // Disabled input 250 | 251 | 252 | // Input with icon 253 | 254 | ``` 255 | 256 | #### Available options 257 | 258 | [HTML input element props](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attributes) and dedicated params 259 | 260 | | Param | Description | 261 | | ----------------------------------------------------------------------------------------------------------------- | -------------------------- | 262 | | [`containerProps`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#list_of_global_attributes) | Props for switch container | 263 | | [`iconProps`](#available-options-3) | Props for icon component | 264 | 265 | --- 266 | 267 | ### Labels and sections 268 | 269 | To use a label or section, use following components. 270 | 271 | ```jsx 272 | import { Label, SectionTitle } from 'react-figma-ui'; 273 | 274 | // Label 275 | 276 | 277 | // Section title 278 | Section title 279 | ``` 280 | 281 | #### Available options 282 | 283 | [HTML div element props](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#list_of_global_attributes) 284 | 285 | --- 286 | 287 | ### Onboarding tip 288 | 289 | To create an onboarding tip, use the following component. 290 | 291 | ```jsx 292 | import { OnboardingTip } from 'react-figma-ui'; 293 | 294 | 295 | Onboarding tip goes here. 296 | 297 | ``` 298 | 299 | #### Available options 300 | 301 | [HTML div element props](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#list_of_global_attributes) and dedicated params 302 | 303 | | Param | Description | 304 | | ----------------------------------------------------------------------------------------------------------------- | -------------------------- | 305 | | [`containerProps`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#list_of_global_attributes) | Props for switch container | 306 | | [`iconProps`](#available-options-3) | Props for icon component | 307 | 308 | --- 309 | 310 | ### Radio button 311 | 312 | To create an radio button, use the following component. Remember each group of radio buttons must share the same name so that they are related to one another. Each button should have a unique id so that its label is associated with it and remains part of the clickable hit area. 313 | 314 | ```jsx 315 | import { Radio } from 'react-figma-ui'; 316 | 317 | // Radio button 318 | Radio button 319 | 320 | // Radio button checked 321 | Radio button 322 | 323 | // Radio button disabled 324 | Radio button 325 | ``` 326 | 327 | #### Available options 328 | 329 | [HTML input element props](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attributes) and dedicated params 330 | 331 | | Param | Description | 332 | | ----------------------------------------------------------------------------------------------------------------- | ------------------------- | 333 | | [`containerProps`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#list_of_global_attributes) | Props for radio container | 334 | | [`labelProps`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label#attributes) | Props for label element | 335 | 336 | --- 337 | 338 | ### Select menu 339 | 340 | To create an select menu, use following components. 341 | 342 | The select menu will open and position the menu to the selected object. If there is no vertical room inside your plugin's iFrame, the position of the menu will be moved to ensure it fits inside the iframe. If you have a select menu with too many options to fit within the iFrame, the menu will scroll vertically. 343 | 344 | ```jsx 345 | import { SelectMenu, SelectMenuOption } from 'react-figma-ui'; 346 | 347 | // Example options 348 | const options = [ 349 | { value: 1, label: 'Option 1' }, 350 | { value: 2, label: 'Option 2' }, 351 | { value: 3, label: 'Option 3' }, 352 | ]; 353 | 354 | () => ( 355 | ( 358 | 359 | {label} 360 | 361 | )} 362 | /> 363 | ); 364 | ``` 365 | 366 | #### Available options 367 | 368 | **SelectMenu** 369 | 370 | [HTML select element props](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#list_of_global_attributes) and dedicated params 371 | 372 | | Param | Description | 373 | | --------- | --------------------------------- | 374 | | `options` | Array with select menu options | 375 | | `render` | Render props for SelectMenuOption | 376 | 377 | **SelectMenuOption** 378 | 379 | [HTML option element props](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#list_of_global_attributes) 380 | 381 | --- 382 | 383 | ### Switch 384 | 385 | To use the switch, use the following component. Remember each switch should get a unique ID that is referenced in the label. This ensures the switch and the entire label are clickable. 386 | 387 | ```jsx 388 | import { Switch } from 'react-figma-ui'; 389 | 390 | // Switch 391 | Label 392 | 393 | // Switch checked 394 | Label 395 | 396 | // Switch disabled 397 | Label 398 | ``` 399 | 400 | #### Available options 401 | 402 | [HTML input element props](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attributes) and dedicated params 403 | 404 | | Param | Description | 405 | | ----------------------------------------------------------------------------------------------------------------- | -------------------------- | 406 | | [`containerProps`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#list_of_global_attributes) | Props for switch container | 407 | | [`labelProps`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label#attributes) | Props for label element | 408 | 409 | --- 410 | 411 | ### Textarea 412 | 413 | To use the textarea, use the following component. 414 | 415 | ```jsx 416 | import { Textarea } from 'react-figma-ui'; 417 | 418 | // Textarea 419 | 12 | 13 | `; 14 | 15 | exports[`Textarea should render Textarea with custom className 1`] = ` 16 |
17 | 24 |
25 | `; 26 | 27 | exports[`Textarea should render disabled Textarea 1`] = ` 28 |
29 | 37 |
38 | `; 39 | -------------------------------------------------------------------------------- /__tests__/__snapshots__/Type.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Type should render Type with custom className 1`] = ` 4 |
5 |
8 | Lorem ipsum dolor sit amet 9 |
10 |
11 | `; 12 | 13 | exports[`Type should render Type with default values 1`] = ` 14 |
15 |
18 | Lorem ipsum dolor 19 |
20 |
21 | `; 22 | 23 | exports[`Type should render Type with large size, bold weight and inverse option 1`] = ` 24 |
25 |
28 | Lorem ipsum dolor sit 29 |
30 |
31 | `; 32 | -------------------------------------------------------------------------------- /global.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'figma-plugin-ds'; 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-figma-ui", 3 | "version": "2.0.0", 4 | "description": "React implementation for figma-plugin-ds", 5 | "author": "Jakub Biesiada", 6 | "license": "MIT", 7 | "main": "lib/react-figma-ui.cjs.js", 8 | "module": "lib/react-figma-ui.esm.js", 9 | "types": "lib/index.d.ts", 10 | "scripts": { 11 | "prepack": "yarn prettier && yarn lint && yarn build", 12 | "clean": "rimraf lib/*", 13 | "build": "rollup -c", 14 | "prebuild": "yarn clean", 15 | "test": "jest --coverage", 16 | "watch": "yarn build -- --watch", 17 | "watch:test": "yarn test -- --watch", 18 | "lint": "eslint 'src/**/*.{tsx,ts}' --fix", 19 | "prettier": "prettier --write 'src/**/*.{tsx,ts}'", 20 | "storybook": "start-storybook -p 6006", 21 | "build-storybook": "build-storybook", 22 | "deploy-storybook": "storybook-to-ghpages", 23 | "commit": "git-cz", 24 | "semantic-release": "semantic-release", 25 | "prepare": "husky install", 26 | "pre-commit": "lint-staged" 27 | }, 28 | "repository": { 29 | "type": "git", 30 | "url": "https://github.com/JB1905/react-figma-ui.git" 31 | }, 32 | "keywords": [ 33 | "react", 34 | "components", 35 | "design", 36 | "library", 37 | "ui", 38 | "patterns", 39 | "system", 40 | "figma" 41 | ], 42 | "bugs": { 43 | "url": "https://github.com/JB1905/react-figma-ui/issues" 44 | }, 45 | "homepage": "https://github.com/JB1905/react-figma-ui#readme", 46 | "dependencies": { 47 | "clsx": "^1.2.1", 48 | "figma-plugin-ds": "^1.0.1" 49 | }, 50 | "devDependencies": { 51 | "@rollup/plugin-node-resolve": "^13.3.0", 52 | "@storybook/addon-backgrounds": "^6.5.16", 53 | "@storybook/addon-controls": "^6.5.16", 54 | "@storybook/addon-docs": "^6.5.16", 55 | "@storybook/addon-storysource": "^6.5.16", 56 | "@storybook/addons": "^6.5.16", 57 | "@storybook/react": "^6.5.16", 58 | "@storybook/storybook-deployer": "^2.8.16", 59 | "@testing-library/jest-dom": "^5.16.5", 60 | "@testing-library/react": "^12.1.5", 61 | "@types/react": "^17.0.38", 62 | "@typescript-eslint/parser": "^5.54.1", 63 | "babel-loader": "^8.2.5", 64 | "cz-conventional-changelog": "^3.3.0", 65 | "eslint": "8.35.0", 66 | "eslint-config-prettier": "^8.7.0", 67 | "eslint-plugin-prettier": "^4.2.1", 68 | "eslint-plugin-react": "^7.32.2", 69 | "eslint-plugin-react-hooks": "^4.6.0", 70 | "husky": "^8.0.3", 71 | "jest": "^28.1.1", 72 | "jest-environment-jsdom": "^28.1.1", 73 | "lint-staged": "^13.1.2", 74 | "prettier": "^2.8.4", 75 | "react": "^17.0.2", 76 | "react-dom": "^17.0.2", 77 | "rollup": "^2.75.6", 78 | "rollup-plugin-postcss": "^4.0.2", 79 | "rollup-plugin-terser": "^7.0.2", 80 | "rollup-plugin-typescript2": "^0.32.1", 81 | "semantic-release": "^19.0.3", 82 | "ts-jest": "^28.0.5", 83 | "typescript": "^4.9.5" 84 | }, 85 | "peerDependencies": { 86 | "react": ">=16.8.0", 87 | "react-dom": ">=16.8.0" 88 | }, 89 | "config": { 90 | "commitizen": { 91 | "path": "./node_modules/cz-conventional-changelog" 92 | } 93 | }, 94 | "jest": { 95 | "testEnvironment": "jsdom", 96 | "testPathIgnorePatterns": [ 97 | "/node_modules/" 98 | ], 99 | "setupFilesAfterEnv": [ 100 | "/setupTests.ts" 101 | ], 102 | "moduleNameMapper": { 103 | "figma-plugin-ds": "/__mocks__/figmaPluginMock.ts", 104 | "\\.(css)$": "/__mocks__/styleMock.ts" 105 | }, 106 | "transform": { 107 | "^.+\\.tsx?$": "ts-jest" 108 | } 109 | }, 110 | "lint-staged": { 111 | "src/**/*.{tsx,ts}": [ 112 | "yarn prettier", 113 | "yarn lint" 114 | ] 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve'; 2 | import typescript from 'rollup-plugin-typescript2'; 3 | import { terser } from 'rollup-plugin-terser'; 4 | import postcss from 'rollup-plugin-postcss'; 5 | 6 | import pkg from './package.json'; 7 | 8 | export default { 9 | input: 'src/index.ts', 10 | output: [ 11 | { 12 | file: pkg.main, 13 | format: 'cjs', 14 | sourcemap: true, 15 | }, 16 | { 17 | file: pkg.module, 18 | format: 'es', 19 | sourcemap: true, 20 | }, 21 | ], 22 | plugins: [ 23 | resolve(), 24 | postcss({ 25 | extract: false, 26 | modules: true, 27 | use: ['sass'], 28 | }), 29 | typescript(), 30 | terser(), 31 | ], 32 | external: [ 33 | ...Object.keys(pkg.dependencies || {}), 34 | ...Object.keys(pkg.peerDependencies || {}), 35 | 'figma-plugin-ds/dist/figma-plugin-ds.css', 36 | ], 37 | }; 38 | -------------------------------------------------------------------------------- /setupTests.ts: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom/extend-expect'; 2 | -------------------------------------------------------------------------------- /src/components/Button.tsx: -------------------------------------------------------------------------------- 1 | import React, { DetailedHTMLProps, ButtonHTMLAttributes } from 'react'; 2 | import clsx from 'clsx'; 3 | 4 | import type { Tint } from '../types'; 5 | 6 | interface Props 7 | extends Readonly< 8 | DetailedHTMLProps< 9 | ButtonHTMLAttributes, 10 | HTMLButtonElement 11 | > 12 | > { 13 | readonly tint?: Tint; 14 | readonly destructive?: boolean; 15 | } 16 | 17 | export const Button = ({ 18 | children, 19 | tint, 20 | destructive, 21 | className = '', 22 | ...props 23 | }: Props) => ( 24 | 34 | ); 35 | -------------------------------------------------------------------------------- /src/components/Checkbox.tsx: -------------------------------------------------------------------------------- 1 | import React, { HTMLProps } from 'react'; 2 | import clsx from 'clsx'; 3 | 4 | interface Props 5 | extends Omit>, 'onChange'> { 6 | readonly containerProps?: Readonly>; 7 | readonly labelProps?: Readonly>; 8 | readonly onChange?: (e: React.ChangeEvent) => void; 9 | } 10 | 11 | export const Checkbox = ({ 12 | children, 13 | id, 14 | className = '', 15 | containerProps = {}, 16 | labelProps = {}, 17 | ...props 18 | }: Props) => { 19 | const { className: containerClassName = '', ...containerRest } = 20 | containerProps; 21 | const { className: labelClassName = '', ...labelRest } = labelProps; 22 | 23 | return ( 24 |
25 | 31 | 32 | 39 |
40 | ); 41 | }; 42 | -------------------------------------------------------------------------------- /src/components/Disclosure.tsx: -------------------------------------------------------------------------------- 1 | import React, { 2 | useEffect, 3 | HTMLProps, 4 | ReactElement, 5 | DetailedHTMLProps, 6 | LiHTMLAttributes, 7 | } from 'react'; 8 | import { disclosure } from 'figma-plugin-ds'; 9 | import clsx from 'clsx'; 10 | 11 | interface DisclosureProps extends Readonly> { 12 | readonly items: T[]; 13 | render(...itemData: [T, number, T[]]): ReactElement; 14 | } 15 | 16 | interface DisclosureItemProps 17 | extends Readonly< 18 | DetailedHTMLProps, HTMLLIElement> 19 | > { 20 | readonly heading: string; 21 | readonly content: string; 22 | readonly section?: boolean; 23 | readonly expanded?: boolean; 24 | readonly labelProps?: Readonly>; 25 | readonly contentProps?: Readonly>; 26 | } 27 | 28 | export function Disclosure({ 29 | items, 30 | render, 31 | className = '', 32 | ...props 33 | }: DisclosureProps) { 34 | useEffect(() => { 35 | disclosure.init(); 36 | 37 | return () => disclosure.destroy(); 38 | }, []); 39 | 40 | return ( 41 |
    42 | {items.map(render)} 43 |
44 | ); 45 | } 46 | 47 | export const DisclosureItem = ({ 48 | section, 49 | expanded, 50 | heading, 51 | content, 52 | className = '', 53 | labelProps = {}, 54 | contentProps = {}, 55 | ...props 56 | }: DisclosureItemProps) => { 57 | const { className: labelClassName = '', ...labelRest } = labelProps; 58 | const { className: contentClassName = '', ...contentRest } = contentProps; 59 | 60 | return ( 61 |
  • 69 |
    77 | {heading} 78 |
    79 | 80 |
    84 | {content} 85 |
    86 |
  • 87 | ); 88 | }; 89 | -------------------------------------------------------------------------------- /src/components/Icon.tsx: -------------------------------------------------------------------------------- 1 | import React, { HTMLProps } from 'react'; 2 | import clsx from 'clsx'; 3 | 4 | import type { IconName, ColorName } from '../types'; 5 | 6 | export interface Props extends Readonly> { 7 | readonly iconName?: Readonly; 8 | readonly spin?: boolean; 9 | readonly colorName?: Readonly; 10 | } 11 | 12 | export const Icon = ({ 13 | children, 14 | iconName, 15 | className = '', 16 | spin, 17 | colorName, 18 | ...props 19 | }: Props) => ( 20 |
    30 | {children} 31 |
    32 | ); 33 | -------------------------------------------------------------------------------- /src/components/IconButton.tsx: -------------------------------------------------------------------------------- 1 | import React, { HTMLProps } from 'react'; 2 | import clsx from 'clsx'; 3 | 4 | import { Icon, Props as IconProps } from './Icon'; 5 | 6 | interface Props extends Readonly> { 7 | readonly selected?: boolean; 8 | readonly iconProps: IconProps; 9 | } 10 | 11 | export const IconButton = ({ 12 | selected, 13 | className = '', 14 | iconProps, 15 | ...props 16 | }: Props) => ( 17 |
    25 | 26 |
    27 | ); 28 | -------------------------------------------------------------------------------- /src/components/Input.tsx: -------------------------------------------------------------------------------- 1 | import React, { HTMLProps } from 'react'; 2 | import clsx from 'clsx'; 3 | 4 | import { Icon, Props as IconProps } from './Icon'; 5 | 6 | interface Props extends Readonly> { 7 | readonly containerProps?: Readonly>; 8 | readonly iconProps?: IconProps; 9 | } 10 | 11 | export const Input = ({ 12 | className = '', 13 | type = 'input', 14 | containerProps = {}, 15 | iconProps = {}, 16 | ...props 17 | }: Props) => { 18 | const { className: containerClassName = '', ...containerRest } = 19 | containerProps; 20 | const { iconName } = iconProps; 21 | 22 | return ( 23 |
    31 | {iconName && } 32 | 33 | 38 |
    39 | ); 40 | }; 41 | -------------------------------------------------------------------------------- /src/components/Label.tsx: -------------------------------------------------------------------------------- 1 | import React, { HTMLProps } from 'react'; 2 | import clsx from 'clsx'; 3 | 4 | interface Props extends Readonly> {} 5 | 6 | export const Label = ({ children, className = '', ...props }: Props) => ( 7 |
    8 | {children} 9 |
    10 | ); 11 | -------------------------------------------------------------------------------- /src/components/OnboardingTip.tsx: -------------------------------------------------------------------------------- 1 | import React, { HTMLProps } from 'react'; 2 | import clsx from 'clsx'; 3 | 4 | import { Icon, Props as IconProps } from './Icon'; 5 | 6 | interface Props extends Readonly> { 7 | readonly containerProps?: Readonly>; 8 | readonly iconProps: IconProps; 9 | } 10 | 11 | export const OnboardingTip = ({ 12 | children, 13 | className = '', 14 | containerProps = {}, 15 | iconProps, 16 | ...props 17 | }: Props) => { 18 | const { className: containerClassName = '', ...containerRest } = 19 | containerProps; 20 | 21 | return ( 22 |
    26 | 27 | 28 |
    29 | {children} 30 |
    31 |
    32 | ); 33 | }; 34 | -------------------------------------------------------------------------------- /src/components/Radio.tsx: -------------------------------------------------------------------------------- 1 | import React, { HTMLProps } from 'react'; 2 | import clsx from 'clsx'; 3 | 4 | interface Props 5 | extends Omit>, 'onChange'> { 6 | readonly containerProps?: Readonly>; 7 | readonly labelProps?: Readonly>; 8 | readonly onChange?: (e: React.ChangeEvent) => void; 9 | } 10 | 11 | export const Radio = ({ 12 | children, 13 | id, 14 | className = '', 15 | containerProps = {}, 16 | labelProps = {}, 17 | ...props 18 | }: Props) => { 19 | const { className: containerClassName = '', ...containerRest } = 20 | containerProps; 21 | const { className: labelClassName = '', ...labelRest } = labelProps; 22 | 23 | return ( 24 |
    25 | 31 | 32 | 39 |
    40 | ); 41 | }; 42 | -------------------------------------------------------------------------------- /src/components/SectionTitle.tsx: -------------------------------------------------------------------------------- 1 | import React, { HTMLProps } from 'react'; 2 | import clsx from 'clsx'; 3 | 4 | interface Props extends Readonly> {} 5 | 6 | export const SectionTitle = ({ children, className = '', ...props }: Props) => ( 7 |
    8 | {children} 9 |
    10 | ); 11 | -------------------------------------------------------------------------------- /src/components/SelectMenu.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, HTMLProps, ReactElement } from 'react'; 2 | import { selectMenu } from 'figma-plugin-ds'; 3 | import clsx from 'clsx'; 4 | 5 | interface SelectMenuProps extends Readonly> { 6 | readonly options: T[]; 7 | render(...optionData: [T, number, T[]]): ReactElement; 8 | } 9 | 10 | interface SelectMenuItemProps extends Readonly> {} 11 | 12 | export function SelectMenu({ 13 | options, 14 | render, 15 | className = '', 16 | ...props 17 | }: SelectMenuProps) { 18 | useEffect(() => { 19 | selectMenu.init(); 20 | 21 | return () => selectMenu.destroy(); 22 | }, []); 23 | 24 | return ( 25 | 28 | ); 29 | } 30 | 31 | export const SelectMenuOption = ({ 32 | children, 33 | ...props 34 | }: SelectMenuItemProps) => ; 35 | -------------------------------------------------------------------------------- /src/components/Switch.tsx: -------------------------------------------------------------------------------- 1 | import React, { HTMLProps } from 'react'; 2 | import clsx from 'clsx'; 3 | 4 | interface Props 5 | extends Omit>, 'onChange'> { 6 | readonly containerProps?: Readonly>; 7 | readonly labelProps?: Readonly>; 8 | readonly onChange?: (e: React.ChangeEvent) => void; 9 | } 10 | 11 | export const Switch = ({ 12 | children, 13 | id, 14 | className = '', 15 | containerProps = {}, 16 | labelProps = {}, 17 | ...props 18 | }: Props) => { 19 | const { className: containerClassName = '', ...containerRest } = 20 | containerProps; 21 | const { className: labelClassName = '', ...labelRest } = labelProps; 22 | 23 | return ( 24 |
    25 | 31 | 32 | 39 |
    40 | ); 41 | }; 42 | -------------------------------------------------------------------------------- /src/components/Textarea.tsx: -------------------------------------------------------------------------------- 1 | import React, { HTMLProps } from 'react'; 2 | import clsx from 'clsx'; 3 | 4 | interface Props extends Readonly> {} 5 | 6 | export const Textarea = ({ className = '', ...props }: Props) => ( 7 |