@jongh/ui 1.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. package/.storybook/index.css +1 -0
  2. package/.storybook/main.ts +40 -0
  3. package/.storybook/preview.ts +16 -0
  4. package/.storybook/vitest.setup.ts +9 -0
  5. package/.turbo/turbo-build.log +79 -0
  6. package/CHANGELOG.md +54 -0
  7. package/chromatic.config.json +6 -0
  8. package/eslint.config.mjs +14 -0
  9. package/index.html +13 -0
  10. package/package.json +58 -0
  11. package/panda.config.ts +13 -0
  12. package/postcss.config.cjs +3 -0
  13. package/preset.ts +134 -0
  14. package/src/component/accordion/ui/index.tsx +80 -0
  15. package/src/component/accordion/ui/recipe.ts +65 -0
  16. package/src/component/avatar/ui/index.tsx +47 -0
  17. package/src/component/avatar/ui/recipe.ts +30 -0
  18. package/src/component/button/ui/index.tsx +29 -0
  19. package/src/component/button/ui/recipe.ts +102 -0
  20. package/src/component/chip/ui/index.tsx +30 -0
  21. package/src/component/chip/ui/recipe.ts +112 -0
  22. package/src/component/dialog/ui/index.tsx +73 -0
  23. package/src/component/dialog/ui/recipe.ts +92 -0
  24. package/src/component/select/ui/index.tsx +128 -0
  25. package/src/component/select/ui/recipe.ts +142 -0
  26. package/src/component/slider/ui/index.tsx +61 -0
  27. package/src/component/slider/ui/recipe.ts +52 -0
  28. package/src/component/tabs/ui/index.tsx +61 -0
  29. package/src/component/tabs/ui/recipe.ts +59 -0
  30. package/src/hooks/createContext.tsx +34 -0
  31. package/src/hooks/useComposedRefs.ts +20 -0
  32. package/src/hooks/useControllableState.ts +87 -0
  33. package/src/hooks/useKeyboardEvent.ts +58 -0
  34. package/src/stories/accordion.stories.tsx +63 -0
  35. package/src/stories/avatar.stories.tsx +33 -0
  36. package/src/stories/button.stories.tsx +53 -0
  37. package/src/stories/chip.stories.tsx +69 -0
  38. package/src/stories/dialog.stories.tsx +134 -0
  39. package/src/stories/select.stories.tsx +35 -0
  40. package/src/stories/slider.stories.tsx +31 -0
  41. package/src/stories/tabs.stories.tsx +63 -0
  42. package/src/test/accordion.test.tsx +66 -0
  43. package/src/test/tabs.test.tsx +30 -0
  44. package/src/types/index.ts +1 -0
  45. package/src/utils/createStyleContext.tsx +108 -0
  46. package/src/utils/defineSafe.ts +46 -0
  47. package/src/utils/getObjectKeys.ts +20 -0
  48. package/src/vite-env.d.ts +1 -0
  49. package/storybook-static/assets/Color-F6OSRLHC-CrKta8Qx.js +1 -0
  50. package/storybook-static/assets/DocsRenderer-CFRXHY34-Crd9Mepd.js +586 -0
  51. package/storybook-static/assets/_commonjsHelpers-Cpj98o6Y.js +1 -0
  52. package/storybook-static/assets/accordion.stories-0Om0AnMm.js +50 -0
  53. package/storybook-static/assets/avatar.stories-DMDsrSks.js +16 -0
  54. package/storybook-static/assets/axe-Dzet_oDA.js +30 -0
  55. package/storybook-static/assets/button.stories-CuVC-3x-.js +40 -0
  56. package/storybook-static/assets/chip.stories-FBIp8XNi.js +48 -0
  57. package/storybook-static/assets/chunk-XP5HYGXS-D5tuasO7.js +1 -0
  58. package/storybook-static/assets/createLucideIcon-DmXe7Z8R.js +21 -0
  59. package/storybook-static/assets/dialog.stories-3WsEvXt4.js +106 -0
  60. package/storybook-static/assets/entry-preview-Bkgdgq1b.js +10 -0
  61. package/storybook-static/assets/entry-preview-docs-zLz1Es7d.js +46 -0
  62. package/storybook-static/assets/iframe-LPPPXabV.js +199 -0
  63. package/storybook-static/assets/index-Bklpsb39.js +1 -0
  64. package/storybook-static/assets/index-BrlJLjep.js +24 -0
  65. package/storybook-static/assets/index-C2tRLqin.js +41 -0
  66. package/storybook-static/assets/index-CBft-ReK.js +8 -0
  67. package/storybook-static/assets/index-CSbWd7UJ.js +1 -0
  68. package/storybook-static/assets/index-ClcD9ViR.js +9 -0
  69. package/storybook-static/assets/index-Cu4lwwaE.js +1 -0
  70. package/storybook-static/assets/index-Cvkemp2J.js +1 -0
  71. package/storybook-static/assets/index-D59aU5s_.js +240 -0
  72. package/storybook-static/assets/index-DNo8LKN3.js +1 -0
  73. package/storybook-static/assets/index-DUy19JZU.js +1 -0
  74. package/storybook-static/assets/index-D_35mznO.js +1 -0
  75. package/storybook-static/assets/index-DrFu-skq.js +6 -0
  76. package/storybook-static/assets/index-DvbTp2M2.js +1 -0
  77. package/storybook-static/assets/index-rtmQni2k.js +1 -0
  78. package/storybook-static/assets/jsx-runtime-BYYWji4R.js +9 -0
  79. package/storybook-static/assets/matchers-57EZJSQE-oeXCqs4p.js +14 -0
  80. package/storybook-static/assets/preview-1l6V4UsQ.js +1 -0
  81. package/storybook-static/assets/preview-B3PTUHS7.js +1 -0
  82. package/storybook-static/assets/preview-BBWR9nbA.js +1 -0
  83. package/storybook-static/assets/preview-BCV5r-Ja.js +1 -0
  84. package/storybook-static/assets/preview-BWzBA1C2.js +396 -0
  85. package/storybook-static/assets/preview-Bb56VplF.js +2 -0
  86. package/storybook-static/assets/preview-Bl8rsKRy.js +1 -0
  87. package/storybook-static/assets/preview-Bm-a94Ct.js +2 -0
  88. package/storybook-static/assets/preview-Br-h4d84.js +1 -0
  89. package/storybook-static/assets/preview-D77C14du.js +34 -0
  90. package/storybook-static/assets/preview-DC8N7cZb.css +1 -0
  91. package/storybook-static/assets/preview-DGUiP6tS.js +7 -0
  92. package/storybook-static/assets/react-18-BPs3tMGP.js +1 -0
  93. package/storybook-static/assets/select.stories-AN8Hb81Z.js +20 -0
  94. package/storybook-static/assets/slider.stories-CxOczq0y.js +12 -0
  95. package/storybook-static/assets/tabs.stories-BbTU4QU3.js +28 -0
  96. package/storybook-static/favicon.svg +1 -0
  97. package/storybook-static/iframe.html +666 -0
  98. package/storybook-static/index.html +193 -0
  99. package/storybook-static/index.json +1 -0
  100. package/storybook-static/nunito-sans-bold-italic.woff2 +0 -0
  101. package/storybook-static/nunito-sans-bold.woff2 +0 -0
  102. package/storybook-static/nunito-sans-italic.woff2 +0 -0
  103. package/storybook-static/nunito-sans-regular.woff2 +0 -0
  104. package/storybook-static/project.json +1 -0
  105. package/storybook-static/sb-addons/a11y-12/manager-bundle.js +3 -0
  106. package/storybook-static/sb-addons/a11y-12/manager-bundle.js.LEGAL.txt +0 -0
  107. package/storybook-static/sb-addons/chromatic-com-storybook-10/manager-bundle.js +331 -0
  108. package/storybook-static/sb-addons/chromatic-com-storybook-10/manager-bundle.js.LEGAL.txt +51 -0
  109. package/storybook-static/sb-addons/controls-11/manager-bundle.js +391 -0
  110. package/storybook-static/sb-addons/controls-11/manager-bundle.js.LEGAL.txt +0 -0
  111. package/storybook-static/sb-addons/essentials-actions-3/manager-bundle.js +3 -0
  112. package/storybook-static/sb-addons/essentials-actions-3/manager-bundle.js.LEGAL.txt +0 -0
  113. package/storybook-static/sb-addons/essentials-backgrounds-5/manager-bundle.js +12 -0
  114. package/storybook-static/sb-addons/essentials-backgrounds-5/manager-bundle.js.LEGAL.txt +0 -0
  115. package/storybook-static/sb-addons/essentials-controls-2/manager-bundle.js +391 -0
  116. package/storybook-static/sb-addons/essentials-controls-2/manager-bundle.js.LEGAL.txt +0 -0
  117. package/storybook-static/sb-addons/essentials-docs-4/manager-bundle.js +230 -0
  118. package/storybook-static/sb-addons/essentials-docs-4/manager-bundle.js.LEGAL.txt +0 -0
  119. package/storybook-static/sb-addons/essentials-measure-8/manager-bundle.js +3 -0
  120. package/storybook-static/sb-addons/essentials-measure-8/manager-bundle.js.LEGAL.txt +0 -0
  121. package/storybook-static/sb-addons/essentials-outline-9/manager-bundle.js +3 -0
  122. package/storybook-static/sb-addons/essentials-outline-9/manager-bundle.js.LEGAL.txt +0 -0
  123. package/storybook-static/sb-addons/essentials-toolbars-7/manager-bundle.js +3 -0
  124. package/storybook-static/sb-addons/essentials-toolbars-7/manager-bundle.js.LEGAL.txt +0 -0
  125. package/storybook-static/sb-addons/essentials-viewport-6/manager-bundle.js +3 -0
  126. package/storybook-static/sb-addons/essentials-viewport-6/manager-bundle.js.LEGAL.txt +0 -0
  127. package/storybook-static/sb-addons/links-1/manager-bundle.js +3 -0
  128. package/storybook-static/sb-addons/links-1/manager-bundle.js.LEGAL.txt +0 -0
  129. package/storybook-static/sb-addons/storybook-core-core-server-presets-0/common-manager-bundle.js +3 -0
  130. package/storybook-static/sb-addons/storybook-core-core-server-presets-0/common-manager-bundle.js.LEGAL.txt +0 -0
  131. package/storybook-static/sb-addons/storybook-experimental-addon-test-13/manager-bundle.js +211 -0
  132. package/storybook-static/sb-addons/storybook-experimental-addon-test-13/manager-bundle.js.LEGAL.txt +0 -0
  133. package/storybook-static/sb-common-assets/favicon.svg +1 -0
  134. package/storybook-static/sb-common-assets/nunito-sans-bold-italic.woff2 +0 -0
  135. package/storybook-static/sb-common-assets/nunito-sans-bold.woff2 +0 -0
  136. package/storybook-static/sb-common-assets/nunito-sans-italic.woff2 +0 -0
  137. package/storybook-static/sb-common-assets/nunito-sans-regular.woff2 +0 -0
  138. package/storybook-static/sb-manager/globals-module-info.js +1037 -0
  139. package/storybook-static/sb-manager/globals-runtime.js +40857 -0
  140. package/storybook-static/sb-manager/globals.js +48 -0
  141. package/storybook-static/sb-manager/runtime.js +11963 -0
  142. package/styled-system/css/conditions.mjs +36 -0
  143. package/styled-system/css/css.d.ts +22 -0
  144. package/styled-system/css/css.mjs +45 -0
  145. package/styled-system/css/cva.d.ts +6 -0
  146. package/styled-system/css/cva.mjs +87 -0
  147. package/styled-system/css/cx.d.ts +5 -0
  148. package/styled-system/css/cx.mjs +15 -0
  149. package/styled-system/css/index.d.ts +5 -0
  150. package/styled-system/css/index.mjs +4 -0
  151. package/styled-system/css/sva.d.ts +4 -0
  152. package/styled-system/css/sva.mjs +41 -0
  153. package/styled-system/helpers.mjs +336 -0
  154. package/styled-system/jsx/aspect-ratio.d.ts +10 -0
  155. package/styled-system/jsx/aspect-ratio.mjs +14 -0
  156. package/styled-system/jsx/bleed.d.ts +10 -0
  157. package/styled-system/jsx/bleed.mjs +14 -0
  158. package/styled-system/jsx/box.d.ts +10 -0
  159. package/styled-system/jsx/box.mjs +14 -0
  160. package/styled-system/jsx/center.d.ts +10 -0
  161. package/styled-system/jsx/center.mjs +14 -0
  162. package/styled-system/jsx/circle.d.ts +10 -0
  163. package/styled-system/jsx/circle.mjs +14 -0
  164. package/styled-system/jsx/container.d.ts +10 -0
  165. package/styled-system/jsx/container.mjs +14 -0
  166. package/styled-system/jsx/cq.d.ts +10 -0
  167. package/styled-system/jsx/cq.mjs +14 -0
  168. package/styled-system/jsx/divider.d.ts +10 -0
  169. package/styled-system/jsx/divider.mjs +14 -0
  170. package/styled-system/jsx/factory-helper.mjs +22 -0
  171. package/styled-system/jsx/factory.d.ts +3 -0
  172. package/styled-system/jsx/factory.mjs +80 -0
  173. package/styled-system/jsx/flex.d.ts +10 -0
  174. package/styled-system/jsx/flex.mjs +14 -0
  175. package/styled-system/jsx/float.d.ts +10 -0
  176. package/styled-system/jsx/float.mjs +14 -0
  177. package/styled-system/jsx/grid-item.d.ts +10 -0
  178. package/styled-system/jsx/grid-item.mjs +14 -0
  179. package/styled-system/jsx/grid.d.ts +10 -0
  180. package/styled-system/jsx/grid.mjs +14 -0
  181. package/styled-system/jsx/hstack.d.ts +10 -0
  182. package/styled-system/jsx/hstack.mjs +14 -0
  183. package/styled-system/jsx/index.d.ts +24 -0
  184. package/styled-system/jsx/index.mjs +22 -0
  185. package/styled-system/jsx/is-valid-prop.d.ts +11 -0
  186. package/styled-system/jsx/is-valid-prop.mjs +17 -0
  187. package/styled-system/jsx/link-overlay.d.ts +10 -0
  188. package/styled-system/jsx/link-overlay.mjs +14 -0
  189. package/styled-system/jsx/spacer.d.ts +10 -0
  190. package/styled-system/jsx/spacer.mjs +14 -0
  191. package/styled-system/jsx/square.d.ts +10 -0
  192. package/styled-system/jsx/square.mjs +14 -0
  193. package/styled-system/jsx/stack.d.ts +10 -0
  194. package/styled-system/jsx/stack.mjs +14 -0
  195. package/styled-system/jsx/visually-hidden.d.ts +10 -0
  196. package/styled-system/jsx/visually-hidden.mjs +14 -0
  197. package/styled-system/jsx/vstack.d.ts +10 -0
  198. package/styled-system/jsx/vstack.mjs +14 -0
  199. package/styled-system/jsx/wrap.d.ts +10 -0
  200. package/styled-system/jsx/wrap.mjs +14 -0
  201. package/styled-system/patterns/aspect-ratio.d.ts +21 -0
  202. package/styled-system/patterns/aspect-ratio.mjs +38 -0
  203. package/styled-system/patterns/bleed.d.ts +22 -0
  204. package/styled-system/patterns/bleed.mjs +24 -0
  205. package/styled-system/patterns/box.d.ts +21 -0
  206. package/styled-system/patterns/box.mjs +15 -0
  207. package/styled-system/patterns/center.d.ts +21 -0
  208. package/styled-system/patterns/center.mjs +21 -0
  209. package/styled-system/patterns/circle.d.ts +21 -0
  210. package/styled-system/patterns/circle.mjs +25 -0
  211. package/styled-system/patterns/container.d.ts +21 -0
  212. package/styled-system/patterns/container.mjs +21 -0
  213. package/styled-system/patterns/cq.d.ts +22 -0
  214. package/styled-system/patterns/cq.mjs +21 -0
  215. package/styled-system/patterns/divider.d.ts +23 -0
  216. package/styled-system/patterns/divider.mjs +25 -0
  217. package/styled-system/patterns/flex.d.ts +27 -0
  218. package/styled-system/patterns/flex.mjs +26 -0
  219. package/styled-system/patterns/float.d.ts +24 -0
  220. package/styled-system/patterns/float.mjs +52 -0
  221. package/styled-system/patterns/grid-item.d.ts +26 -0
  222. package/styled-system/patterns/grid-item.mjs +25 -0
  223. package/styled-system/patterns/grid.d.ts +25 -0
  224. package/styled-system/patterns/grid.mjs +27 -0
  225. package/styled-system/patterns/hstack.d.ts +22 -0
  226. package/styled-system/patterns/hstack.mjs +24 -0
  227. package/styled-system/patterns/index.d.ts +21 -0
  228. package/styled-system/patterns/index.mjs +20 -0
  229. package/styled-system/patterns/link-overlay.d.ts +21 -0
  230. package/styled-system/patterns/link-overlay.mjs +24 -0
  231. package/styled-system/patterns/spacer.d.ts +21 -0
  232. package/styled-system/patterns/spacer.mjs +21 -0
  233. package/styled-system/patterns/square.d.ts +21 -0
  234. package/styled-system/patterns/square.mjs +24 -0
  235. package/styled-system/patterns/stack.d.ts +24 -0
  236. package/styled-system/patterns/stack.mjs +24 -0
  237. package/styled-system/patterns/visually-hidden.d.ts +21 -0
  238. package/styled-system/patterns/visually-hidden.mjs +18 -0
  239. package/styled-system/patterns/vstack.d.ts +22 -0
  240. package/styled-system/patterns/vstack.mjs +24 -0
  241. package/styled-system/patterns/wrap.d.ts +25 -0
  242. package/styled-system/patterns/wrap.mjs +25 -0
  243. package/styled-system/tokens/index.d.ts +9 -0
  244. package/styled-system/tokens/index.mjs +1992 -0
  245. package/styled-system/tokens/tokens.d.ts +60 -0
  246. package/styled-system/types/composition.d.ts +164 -0
  247. package/styled-system/types/conditions.d.ts +274 -0
  248. package/styled-system/types/csstype.d.ts +21298 -0
  249. package/styled-system/types/global.d.ts +20 -0
  250. package/styled-system/types/index.d.ts +8 -0
  251. package/styled-system/types/jsx.d.ts +52 -0
  252. package/styled-system/types/parts.d.ts +8 -0
  253. package/styled-system/types/pattern.d.ts +78 -0
  254. package/styled-system/types/prop-type.d.ts +235 -0
  255. package/styled-system/types/recipe.d.ts +181 -0
  256. package/styled-system/types/selectors.d.ts +59 -0
  257. package/styled-system/types/static-css.d.ts +56 -0
  258. package/styled-system/types/style-props.d.ts +7468 -0
  259. package/styled-system/types/system-types.d.ts +109 -0
  260. package/tsconfig.json +13 -0
  261. package/turbo/generators/config.cjs +7 -0
  262. package/turbo/generators/stories.cjs +23 -0
  263. package/turbo/generators/templates/stories.tsx.hbs +16 -0
  264. package/turbo/generators/templates/ui.tsx.hbs +8 -0
  265. package/turbo/generators/ui.cjs +19 -0
  266. package/vite.config.ts +7 -0
  267. package/vitest.config.ts +45 -0
@@ -0,0 +1,53 @@
1
+ import type { Meta, StoryObj } from "@storybook/react"
2
+ import { expect, fn, userEvent, within } from "@storybook/test"
3
+ import type { ComponentProps } from "react"
4
+
5
+ import { Button } from "../component/button/ui"
6
+ export default {
7
+ title: "Button",
8
+ tags: ["autodocs"],
9
+ component: Button,
10
+ } as Meta<typeof Button>
11
+
12
+ type Story = StoryObj<ComponentProps<typeof Button>>
13
+
14
+ export const Primary: Story = {
15
+ args: {
16
+ children: "click",
17
+ onClick: fn(),
18
+ },
19
+ play: async ({ args, canvasElement }) => {
20
+ const canvas = within(canvasElement)
21
+ const button = canvas.getByRole("button")
22
+ await userEvent.click(button)
23
+ await expect(args.onClick).toHaveBeenCalled()
24
+ },
25
+ }
26
+
27
+ export const Secondary: Story = {
28
+ args: {
29
+ children: "click",
30
+ variant: "secondary",
31
+ },
32
+ }
33
+
34
+ export const Ghost: Story = {
35
+ args: {
36
+ children: "click",
37
+ variant: "outline",
38
+ },
39
+ }
40
+
41
+ export const Disabled: Story = {
42
+ args: {
43
+ disabled: true,
44
+ children: "disabled",
45
+ onClick: fn(),
46
+ },
47
+ play: async ({ args, canvasElement }) => {
48
+ const canvas = within(canvasElement)
49
+ const button = canvas.getByRole("button")
50
+ await userEvent.click(button)
51
+ await expect(args.onClick).not.toHaveBeenCalled()
52
+ },
53
+ }
@@ -0,0 +1,69 @@
1
+ import type { Meta, StoryObj } from "@storybook/react"
2
+ import { expect, fn, userEvent } from "@storybook/test"
3
+ import { PlusCircleIcon } from "lucide-react"
4
+ import { ComponentProps } from "react"
5
+
6
+ import { Chip } from "../component/chip/ui"
7
+
8
+ export default {
9
+ title: "Chip",
10
+ tags: ["autodocs"],
11
+ component: Chip,
12
+ } as Meta<typeof Chip>
13
+
14
+ type Story = StoryObj<ComponentProps<typeof Chip>>
15
+
16
+ export const Primary: Story = {
17
+ args: {
18
+ children: <div>확인</div>,
19
+ },
20
+ }
21
+
22
+ export const Outlined: Story = {
23
+ args: {
24
+ children: <div>확인</div>,
25
+ variant: "outlined",
26
+ },
27
+ }
28
+
29
+ export const IconOnly: Story = {
30
+ args: {
31
+ layout: "iconOnly",
32
+ children: <PlusCircleIcon />,
33
+ },
34
+ }
35
+
36
+ export const WithPrefixIcon: Story = {
37
+ args: {
38
+ children: (
39
+ <>
40
+ <PlusCircleIcon />
41
+ ㅎㅇ
42
+ </>
43
+ ),
44
+ },
45
+ }
46
+
47
+ export const WithSuffixIcon: Story = {
48
+ args: {
49
+ children: (
50
+ <>
51
+ ㅎㅇ
52
+ <PlusCircleIcon />
53
+ </>
54
+ ),
55
+ },
56
+ }
57
+
58
+ export const Disabled: Story = {
59
+ args: {
60
+ disabled: true,
61
+ children: "disabled",
62
+ onClick: fn(),
63
+ },
64
+ play: async ({ args, canvas }) => {
65
+ const button = canvas.getByRole("button")
66
+ await userEvent.click(button)
67
+ await expect(args.onClick).not.toHaveBeenCalled()
68
+ },
69
+ }
@@ -0,0 +1,134 @@
1
+ import type { Meta, StoryObj } from "@storybook/react"
2
+ import {
3
+ expect,
4
+ screen,
5
+ userEvent,
6
+ waitForElementToBeRemoved,
7
+ within,
8
+ } from "@storybook/test"
9
+ import { css } from "@styled-system/css"
10
+
11
+ import * as Dialog from "../component/dialog/ui"
12
+
13
+ const meta = {
14
+ title: "Dialog",
15
+ tags: ["autodocs"],
16
+ } satisfies Meta<typeof Dialog>
17
+
18
+ export default meta
19
+
20
+ type Story = StoryObj<typeof meta>
21
+
22
+ export const Primary: Story = {
23
+ render: () => {
24
+ return (
25
+ <Dialog.Root>
26
+ <Dialog.Trigger>Open</Dialog.Trigger>
27
+ <Dialog.Content>
28
+ <Dialog.Header>
29
+ <Dialog.Title>Dialog</Dialog.Title>
30
+ <Dialog.Description>
31
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer
32
+ venenatis feugiat eros aliquam convallis. Donec tincidunt eros sed
33
+ ligula tincidunt, sit amet tristique purus pulvinar. Donec maximus
34
+ nunc vitae vestibulum cursus. Phasellus laoreet, velit non mollis
35
+ semper, nunc dui accumsan orci, consequat accumsan libero quam ac
36
+ est. Pellentesque habitant morbi tristique senectus et netus et
37
+ malesuada fames ac turpis egestas. Phasellus ac turpis neque.
38
+ Class aptent taciti sociosqu ad litora torquent per conubia
39
+ nostra, per inceptos himenaeos.
40
+ </Dialog.Description>
41
+ </Dialog.Header>
42
+ </Dialog.Content>
43
+ </Dialog.Root>
44
+ )
45
+ },
46
+ play: async ({ canvasElement }) => {
47
+ //canvasElement -> storybook의 #storybook-root
48
+ //React portal API를 사용할 경우 다른곳에 렌더링
49
+ //더 넓은 범위의 screen으로 테스트하는게 더 올바른 방법이라고 생각
50
+ const canvas = within(canvasElement)
51
+ const ButtonElement = canvas.getByRole("button")
52
+ await userEvent.click(ButtonElement)
53
+ const CloseElement = screen.getByText("Close")
54
+ expect(CloseElement).not.toBeNull()
55
+
56
+ await userEvent.click(CloseElement)
57
+
58
+ waitForElementToBeRemoved(CloseElement).then(() =>
59
+ expect(CloseElement).not.toBeInTheDocument(),
60
+ )
61
+ },
62
+ }
63
+
64
+ export const LongText: Story = {
65
+ render: () => {
66
+ return (
67
+ <Dialog.Root>
68
+ <Dialog.Trigger
69
+ className={css({
70
+ textStyle: "6xl",
71
+ })}
72
+ >
73
+ Open
74
+ </Dialog.Trigger>
75
+ <Dialog.Content
76
+ className={css({
77
+ bg: "cyan.600",
78
+ })}
79
+ >
80
+ <Dialog.Header>
81
+ <Dialog.Title
82
+ className={css({
83
+ color: "red.100",
84
+ })}
85
+ >
86
+ Dialog
87
+ </Dialog.Title>
88
+ <Dialog.Description
89
+ className={css({
90
+ fontSize: "sm",
91
+ })}
92
+ >
93
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer
94
+ venenatis feugiat eros aliquam convallis. Donec tincidunt eros sed
95
+ ligula tincidunt, sit amet tristique purus pulvinar. Donec maximus
96
+ nunc vitae vestibulum cursus. Phasellus laoreet, velit non mollis
97
+ semper, nunc dui accumsan orci, consequat accumsan libero quam ac
98
+ est. Pellentesque habitant morbi tristique senectus et netus et
99
+ malesuada fames ac turpis egestas. Phasellus ac turpis neque.
100
+ Class aptent taciti sociosqu ad litora torquent per conubia
101
+ nostra, per inceptos himenaeos. Lorem ipsum dolor sit amet,
102
+ consectetur adipiscing elit. Integer venenatis feugiat eros
103
+ aliquam convallis. Donec tincidunt eros sed ligula tincidunt, sit
104
+ amet tristique purus pulvinar. Donec maximus nunc vitae vestibulum
105
+ cursus. Phasellus laoreet, velit non mollis semper, nunc dui
106
+ accumsan orci, consequat accumsan libero quam ac est. Pellentesque
107
+ habitant morbi tristique senectus et netus et malesuada fames ac
108
+ turpis egestas. Phasellus ac turpis neque. Class aptent taciti
109
+ sociosqu ad litora torquent per conubia nostra, per inceptos
110
+ himenaeos. Lorem ipsum dolor sit amet, consectetur adipiscing
111
+ elit. Integer venenatis feugiat eros aliquam convallis. Donec
112
+ tincidunt eros sed ligula tincidunt, sit amet tristique purus
113
+ pulvinar. Donec maximus nunc vitae vestibulum cursus. Phasellus
114
+ laoreet, velit non mollis semper, nunc dui accumsan orci,
115
+ consequat accumsan libero quam ac est. Pellentesque habitant morbi
116
+ tristique senectus et netus et malesuada fames ac turpis egestas.
117
+ Phasellus ac turpis neque. Class aptent taciti sociosqu ad litora
118
+ torquent per conubia nostra, per inceptos himenaeos. Lorem ipsum
119
+ dolor sit amet, consectetur adipiscing elit. Integer venenatis
120
+ feugiat eros aliquam convallis. Donec tincidunt eros sed ligula
121
+ tincidunt, sit amet tristique purus pulvinar. Donec maximus nunc
122
+ vitae vestibulum cursus. Phasellus laoreet, velit non mollis
123
+ semper, nunc dui accumsan orci, consequat accumsan libero quam ac
124
+ est. Pellentesque habitant morbi tristique senectus et netus et
125
+ malesuada fames ac turpis egestas. Phasellus ac turpis neque.
126
+ Class aptent taciti sociosqu ad litora torquent per conubia
127
+ nostra, per inceptos himenaeos.
128
+ </Dialog.Description>
129
+ </Dialog.Header>
130
+ </Dialog.Content>
131
+ </Dialog.Root>
132
+ )
133
+ },
134
+ }
@@ -0,0 +1,35 @@
1
+ import type { Meta, StoryObj } from "@storybook/react"
2
+
3
+ import * as Select from "../component/select/ui"
4
+
5
+ export default {
6
+ title: "Select",
7
+ tags: ["autodocs"],
8
+ } satisfies Meta<typeof Select>
9
+
10
+ type Story = StoryObj<typeof Select>
11
+
12
+ const arr = Array.from({ length: 100 }, (_, i) => i)
13
+ //부모 요소의 width 고정값 or Trigger의 width 고정값 필요
14
+ export const Primary: Story = {
15
+ render: () => {
16
+ return (
17
+ <div style={{ width: "300px" }}>
18
+ <Select.Root>
19
+ <Select.Trigger>
20
+ <Select.Value placeholder="Select a frui" />
21
+ </Select.Trigger>
22
+ <Select.Content>
23
+ <Select.Group>
24
+ {arr.map((num) => (
25
+ <Select.Item key={num} value={num.toString()}>
26
+ {num}
27
+ </Select.Item>
28
+ ))}
29
+ </Select.Group>
30
+ </Select.Content>
31
+ </Select.Root>
32
+ </div>
33
+ )
34
+ },
35
+ }
@@ -0,0 +1,31 @@
1
+ import type { Meta, StoryObj } from "@storybook/react"
2
+ import { css } from "@styled-system/css"
3
+
4
+ import * as Slider from "../component/slider/ui"
5
+
6
+ export default {
7
+ title: "Slider",
8
+ tags: ["autodocs"],
9
+ } satisfies Meta<typeof Slider>
10
+
11
+ type Story = StoryObj<typeof Slider>
12
+
13
+ export const Primary: Story = {
14
+ render: () => {
15
+ return (
16
+ <Slider.Root
17
+ defaultValue={[50, 100]}
18
+ max={100}
19
+ step={2}
20
+ className={css({
21
+ width: "[100px]",
22
+ })}
23
+ >
24
+ <Slider.Track>
25
+ <Slider.Range />
26
+ </Slider.Track>
27
+ <Slider.Thumb />
28
+ </Slider.Root>
29
+ )
30
+ },
31
+ }
@@ -0,0 +1,63 @@
1
+ /* eslint-disable react-hooks/rules-of-hooks */
2
+ import { useState } from "@storybook/preview-api"
3
+ import type { Meta, StoryObj } from "@storybook/react"
4
+
5
+ import * as Tabs from "../component/tabs/ui"
6
+
7
+ const meta = {
8
+ title: "Tabs",
9
+ tags: ["autodocs"],
10
+ } satisfies Meta<typeof Tabs>
11
+
12
+ export default meta
13
+ type Story = StoryObj<typeof meta>
14
+
15
+ const data = [
16
+ { value: "1", label: "Tab 1", content: "1번" },
17
+ { value: "2", label: "Tab 2", content: "2번" },
18
+ { value: "3", label: "Tab 3", content: "3번" },
19
+ ]
20
+
21
+ export const Primary: Story = {
22
+ render: (args) => {
23
+ return (
24
+ <Tabs.Root {...args}>
25
+ <Tabs.List>
26
+ {data.map((item) => (
27
+ <Tabs.Trigger key={item.value} value={item.value}>
28
+ {item.label}
29
+ </Tabs.Trigger>
30
+ ))}
31
+ </Tabs.List>
32
+ {data.map((item) => (
33
+ <Tabs.Content key={item.value} value={item.value}>
34
+ {item.content}
35
+ </Tabs.Content>
36
+ ))}
37
+ </Tabs.Root>
38
+ )
39
+ },
40
+ }
41
+
42
+ // 제어 컴포넌트 예시
43
+ export const Controlled: Story = {
44
+ render: (args) => {
45
+ const [value, setValue] = useState("2")
46
+ return (
47
+ <Tabs.Root value={value} onValueChange={setValue} {...args}>
48
+ <Tabs.List>
49
+ {data.map((item) => (
50
+ <Tabs.Trigger key={item.value} value={item.value}>
51
+ {item.label}
52
+ </Tabs.Trigger>
53
+ ))}
54
+ </Tabs.List>
55
+ {data.map((item) => (
56
+ <Tabs.Content key={item.value} value={item.value}>
57
+ {item.content}
58
+ </Tabs.Content>
59
+ ))}
60
+ </Tabs.Root>
61
+ )
62
+ },
63
+ }
@@ -0,0 +1,66 @@
1
+ import { fireEvent, render, screen } from "@testing-library/react"
2
+ import { useState } from "react"
3
+ import { describe, expect, it, vi } from "vitest"
4
+
5
+ import * as Accordion from "../component/accordion/ui"
6
+
7
+ // 예시 컴포넌트
8
+ const AccordionExample = () => {
9
+ const [selectedItems, setSelectedItems] = useState<string[]>([])
10
+
11
+ return (
12
+ <Accordion.Root
13
+ type="multiple"
14
+ value={selectedItems}
15
+ onValueChange={(items) => setSelectedItems(items)}
16
+ >
17
+ <Accordion.Item value="1">
18
+ <Accordion.Header>
19
+ <Accordion.Trigger data-testid="1">1번</Accordion.Trigger>
20
+ </Accordion.Header>
21
+ <Accordion.Content>내용1</Accordion.Content>
22
+ </Accordion.Item>
23
+ </Accordion.Root>
24
+ )
25
+ }
26
+
27
+ // 테스트
28
+ describe("Accordion with useState", () => {
29
+ it("상태 변경 테스트", async () => {
30
+ render(<AccordionExample />)
31
+
32
+ // 초기 상태 확인
33
+ expect(screen.queryByText("내용1")).toBeDefined()
34
+
35
+ // 트리거 클릭
36
+ const trigger = screen.getByTestId("1")
37
+ await fireEvent.click(trigger)
38
+
39
+ // 상태 변경 후 내용 표시 확인
40
+ expect(screen.getByText("내용1")).toBeDefined()
41
+ })
42
+
43
+ // 외부에서 상태를 제어하는 경우 테스트
44
+ it("외부 상태 제어 테스트", () => {
45
+ const onValueChange = vi.fn()
46
+
47
+ render(
48
+ <Accordion.Root
49
+ type="multiple"
50
+ value={["1"]}
51
+ onValueChange={onValueChange}
52
+ >
53
+ <Accordion.Item value="1">
54
+ <Accordion.Trigger data-testid="trigger-1">1번</Accordion.Trigger>
55
+ <Accordion.Content>내용1</Accordion.Content>
56
+ </Accordion.Item>
57
+ </Accordion.Root>,
58
+ )
59
+ //Fix: getByText로 찾을 시 multiple error 발생
60
+ const trigger = screen.getByTestId("trigger-1")
61
+
62
+ fireEvent.click(trigger)
63
+
64
+ expect(onValueChange).toHaveBeenCalledWith([])
65
+ })
66
+ })
@@ -0,0 +1,30 @@
1
+ import { composeStories } from "@storybook/react"
2
+ import { screen } from "@testing-library/react"
3
+ import userEvent from "@testing-library/user-event"
4
+ import { expect, test } from "vitest"
5
+
6
+ import * as stories from "../stories/tabs.stories"
7
+
8
+ const { Primary } = composeStories(stories)
9
+
10
+ test("초기 렌더링 시 defaultValue가 존재하면 해당되는 탭이 선택됩니다", async () => {
11
+ await Primary.run({ args: { ...Primary.args } })
12
+ expect(screen.getByText("Tab 1")).toBeDefined()
13
+ })
14
+
15
+ test("클릭 시 해당 tab에 대한 content가 노출됩니다", async () => {
16
+ const user = userEvent.setup()
17
+ await Primary.run({ args: { ...Primary.args } })
18
+ await user.click(screen.getByText("Tab 2"))
19
+ expect(screen.getByText("2번")).toBeDefined()
20
+ })
21
+
22
+ test("arrow key를 통해 탭을 조작할 수 있습니다", async () => {
23
+ const user = userEvent.setup()
24
+ await Primary.run()
25
+
26
+ await user.click(screen.getByText("Tab 2"))
27
+ await user.keyboard("{ArrowLeft}")
28
+
29
+ expect(document.activeElement).toBe(screen.getByText("Tab 1"))
30
+ })
@@ -0,0 +1 @@
1
+ export type Assign<T, U> = Omit<T, keyof U> & U
@@ -0,0 +1,108 @@
1
+ import {
2
+ createContext,
3
+ type ElementType,
4
+ forwardRef,
5
+ type ForwardRefExoticComponent,
6
+ type PropsWithoutRef,
7
+ type RefAttributes,
8
+ useContext,
9
+ } from "react"
10
+ import { cx } from "styled-system/css"
11
+ import { isCssProperty, styled, type StyledComponent } from "styled-system/jsx"
12
+
13
+ type Props = Record<string, unknown>
14
+ type Recipe = {
15
+ (props?: Props): Props
16
+ splitVariantProps: (props: Props) => [Props, Props]
17
+ }
18
+ type Slot<R extends Recipe> = keyof ReturnType<R>
19
+ type Options = { forwardProps?: string[] }
20
+
21
+ const shouldForwardProp = (
22
+ prop: string,
23
+ variantKeys: string[],
24
+ options: Options = {},
25
+ ) =>
26
+ options.forwardProps?.includes(prop) ||
27
+ (!variantKeys.includes(prop) && !isCssProperty(prop))
28
+
29
+ export const createStyleContext = <R extends Recipe>(recipe: R) => {
30
+ const StyleContext = createContext<Record<Slot<R>, string> | null>(null)
31
+
32
+ const withRootProvider = (Component: ElementType) => {
33
+ const StyledComponent = (props: Props) => {
34
+ const [variantProps, otherProps] = recipe.splitVariantProps(props)
35
+ const slotStyles = recipe(variantProps) as Record<Slot<R>, string>
36
+
37
+ return (
38
+ <StyleContext.Provider value={slotStyles}>
39
+ <Component {...otherProps} />
40
+ </StyleContext.Provider>
41
+ )
42
+ }
43
+ return StyledComponent
44
+ }
45
+
46
+ const withProvider = <T, P extends { className?: string | undefined }>(
47
+ Component: ElementType,
48
+ slot: Slot<R>,
49
+ options?: Options,
50
+ ): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>> => {
51
+ const StyledComponent = styled(
52
+ Component,
53
+ {},
54
+ {
55
+ shouldForwardProp: (prop, variantKeys) =>
56
+ shouldForwardProp(prop, variantKeys, options),
57
+ },
58
+ ) as StyledComponent<ElementType>
59
+ const StyledSlotProvider = forwardRef<T, P>((props, ref) => {
60
+ const [variantProps, otherProps] = recipe.splitVariantProps(props)
61
+ const slotStyles = recipe(variantProps) as Record<Slot<R>, string>
62
+
63
+ return (
64
+ <StyleContext.Provider value={slotStyles}>
65
+ <StyledComponent
66
+ {...otherProps}
67
+ ref={ref}
68
+ className={cx(slotStyles?.[slot], props.className)}
69
+ />
70
+ </StyleContext.Provider>
71
+ )
72
+ })
73
+ // @ts-expect-error '''
74
+ StyledSlotProvider.displayName = Component.displayName || Component.name
75
+
76
+ return StyledSlotProvider
77
+ }
78
+
79
+ const withContext = <T, P extends { className?: string | undefined }>(
80
+ Component: ElementType,
81
+ slot: Slot<R>,
82
+ ): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>> => {
83
+ const StyledComponent = styled(Component)
84
+ const StyledSlotComponent = forwardRef<T, P>((props, ref) => {
85
+ const slotStyles = useContext(StyleContext)
86
+ return (
87
+ <StyledComponent
88
+ {...props}
89
+ ref={ref}
90
+ className={cx(slotStyles?.[slot], props.className)}
91
+ />
92
+ )
93
+ })
94
+
95
+ StyledSlotComponent.displayName =
96
+ typeof Component === "function"
97
+ ? Component.displayName || Component.name
98
+ : String(Component)
99
+
100
+ return StyledSlotComponent
101
+ }
102
+
103
+ return {
104
+ withRootProvider,
105
+ withProvider,
106
+ withContext,
107
+ }
108
+ }
@@ -0,0 +1,46 @@
1
+ import {
2
+ RecipeConfig,
3
+ SlotRecipeConfig,
4
+ SystemStyleObject,
5
+ TextStyles,
6
+ } from "@pandacss/dev"
7
+ import {
8
+ RecipeConfig as CodegenRecipeConfig,
9
+ RecipeVariantRecord,
10
+ SlotRecipeConfig as CodegenSlotRecipeConfig,
11
+ SlotRecipeVariantRecord,
12
+ SystemStyleObject as CodegenSystemStyleObject,
13
+ } from "@styled-system/types"
14
+ import { TextStyle } from "@styled-system/types/composition"
15
+
16
+ function defineSafeRecipe<T extends RecipeVariantRecord>(
17
+ config: CodegenRecipeConfig<T>,
18
+ ): RecipeConfig {
19
+ return config as RecipeConfig
20
+ }
21
+
22
+ function defineSafeSlotRecipe<
23
+ S extends string,
24
+ T extends SlotRecipeVariantRecord<S>,
25
+ >(config: CodegenSlotRecipeConfig<S, T>): SlotRecipeConfig {
26
+ return config as SlotRecipeConfig
27
+ }
28
+
29
+ interface Token<T> {
30
+ value: T
31
+ description?: string
32
+ }
33
+
34
+ interface Recursive<T> {
35
+ // @ts-expect-error this makes the textStyles typings happy
36
+ value?: T
37
+ [key: string]: Recursive<T> | T
38
+ }
39
+
40
+ export const defineSafe = {
41
+ recipe: defineSafeRecipe,
42
+ slotRecipe: defineSafeSlotRecipe,
43
+ textStyle: (definition: TextStyle) => definition,
44
+ styles: (styles: CodegenSystemStyleObject) => styles as SystemStyleObject,
45
+ textStyles: (defs: Recursive<Token<TextStyle>>) => defs as TextStyles,
46
+ }
@@ -0,0 +1,20 @@
1
+ export type ObjectKeys<T extends Record<PropertyKey, unknown>> =
2
+ `${Exclude<keyof T, symbol>}`
3
+
4
+ export function objectKeys<T extends Record<PropertyKey, unknown>>(
5
+ obj: T,
6
+ ): Array<ObjectKeys<T>> {
7
+ return Object.keys(obj) as Array<ObjectKeys<T>>
8
+ }
9
+
10
+ export function objectValues<T extends Record<PropertyKey, unknown>>(
11
+ obj: T,
12
+ ): Array<T[ObjectKeys<T>]> {
13
+ return Object.values(obj) as Array<T[ObjectKeys<T>]>
14
+ }
15
+
16
+ export function objectEntries<T extends Record<PropertyKey, unknown>>(
17
+ obj: T,
18
+ ): Array<[ObjectKeys<T>, T[ObjectKeys<T>]]> {
19
+ return Object.entries(obj) as Array<[ObjectKeys<T>, T[ObjectKeys<T>]]>
20
+ }
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />