@fpkit/acss 0.4.4

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 (297) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +52 -0
  3. package/dist/chunk-77CZU5XZ.cjs +9 -0
  4. package/dist/chunk-77CZU5XZ.cjs.map +1 -0
  5. package/dist/chunk-D43FJIRQ.cjs +31 -0
  6. package/dist/chunk-D43FJIRQ.cjs.map +1 -0
  7. package/dist/chunk-GJWMCDFS.js +9 -0
  8. package/dist/chunk-GJWMCDFS.js.map +1 -0
  9. package/dist/chunk-PCDUGD3C.js +5 -0
  10. package/dist/chunk-PCDUGD3C.js.map +1 -0
  11. package/dist/hooks.cjs +10 -0
  12. package/dist/hooks.cjs.map +1 -0
  13. package/dist/hooks.d.cts +32 -0
  14. package/dist/hooks.d.ts +32 -0
  15. package/dist/hooks.js +8 -0
  16. package/dist/hooks.js.map +1 -0
  17. package/dist/icon-e6044c73.d.ts +227 -0
  18. package/dist/icons.cjs +73 -0
  19. package/dist/icons.cjs.map +1 -0
  20. package/dist/icons.d.cts +252 -0
  21. package/dist/icons.d.ts +252 -0
  22. package/dist/icons.js +4 -0
  23. package/dist/icons.js.map +1 -0
  24. package/dist/index.cjs +59 -0
  25. package/dist/index.cjs.map +1 -0
  26. package/dist/index.d.cts +566 -0
  27. package/dist/index.d.ts +566 -0
  28. package/dist/index.js +11 -0
  29. package/dist/index.js.map +1 -0
  30. package/libs/chunk-GCGKYLDG.js +7 -0
  31. package/libs/chunk-GCGKYLDG.js.map +1 -0
  32. package/libs/chunk-PDD4N5P5.cjs +10 -0
  33. package/libs/chunk-PDD4N5P5.cjs.map +1 -0
  34. package/libs/chunk-QHIABQNQ.js +8 -0
  35. package/libs/chunk-QHIABQNQ.js.map +1 -0
  36. package/libs/chunk-ZOHIKF6I.cjs +31 -0
  37. package/libs/chunk-ZOHIKF6I.cjs.map +1 -0
  38. package/libs/components/badge/badge.css +1 -0
  39. package/libs/components/badge/badge.css.map +1 -0
  40. package/libs/components/badge/badge.min.css +3 -0
  41. package/libs/components/breadcrumbs/breadcrumb.css +1 -0
  42. package/libs/components/breadcrumbs/breadcrumb.css.map +1 -0
  43. package/libs/components/breadcrumbs/breadcrumb.min.css +3 -0
  44. package/libs/components/buttons/button.css +1 -0
  45. package/libs/components/buttons/button.css.map +1 -0
  46. package/libs/components/buttons/button.min.css +3 -0
  47. package/libs/components/cards/card-style.css +1 -0
  48. package/libs/components/cards/card-style.css.map +1 -0
  49. package/libs/components/cards/card-style.min.css +3 -0
  50. package/libs/components/cards/card.css +1 -0
  51. package/libs/components/cards/card.css.map +1 -0
  52. package/libs/components/cards/card.min.css +3 -0
  53. package/libs/components/details/details.css +1 -0
  54. package/libs/components/details/details.css.map +1 -0
  55. package/libs/components/details/details.min.css +3 -0
  56. package/libs/components/form/form.css +1 -0
  57. package/libs/components/form/form.css.map +1 -0
  58. package/libs/components/form/form.min.css +3 -0
  59. package/libs/components/icons/icon.css +1 -0
  60. package/libs/components/icons/icon.css.map +1 -0
  61. package/libs/components/icons/icon.min.css +3 -0
  62. package/libs/components/images/img.css +1 -0
  63. package/libs/components/images/img.css.map +1 -0
  64. package/libs/components/images/img.min.css +3 -0
  65. package/libs/components/layout/landmarks.css +1 -0
  66. package/libs/components/layout/landmarks.css.map +1 -0
  67. package/libs/components/layout/landmarks.min.css +3 -0
  68. package/libs/components/link/link.css +1 -0
  69. package/libs/components/link/link.css.map +1 -0
  70. package/libs/components/link/link.min.css +3 -0
  71. package/libs/components/nav/nav.css +1 -0
  72. package/libs/components/nav/nav.css.map +1 -0
  73. package/libs/components/nav/nav.min.css +3 -0
  74. package/libs/components/progress/progress.css +1 -0
  75. package/libs/components/progress/progress.css.map +1 -0
  76. package/libs/components/progress/progress.min.css +3 -0
  77. package/libs/components/styles/index.css +1 -0
  78. package/libs/components/styles/index.css.map +1 -0
  79. package/libs/components/styles/index.min.css +3 -0
  80. package/libs/components/tag/tag.css +1 -0
  81. package/libs/components/tag/tag.css.map +1 -0
  82. package/libs/components/tag/tag.min.css +3 -0
  83. package/libs/components/text-to-speech/text-to-speech.css +1 -0
  84. package/libs/components/text-to-speech/text-to-speech.css.map +1 -0
  85. package/libs/components/text-to-speech/text-to-speech.min.css +3 -0
  86. package/libs/hooks.cjs +12 -0
  87. package/libs/hooks.cjs.map +1 -0
  88. package/libs/hooks.d.cts +32 -0
  89. package/libs/hooks.d.ts +32 -0
  90. package/libs/hooks.js +3 -0
  91. package/libs/hooks.js.map +1 -0
  92. package/libs/icons-1f5afc0c.d.ts +318 -0
  93. package/libs/icons.cjs +12 -0
  94. package/libs/icons.cjs.map +1 -0
  95. package/libs/icons.d.cts +2 -0
  96. package/libs/icons.d.ts +2 -0
  97. package/libs/icons.js +3 -0
  98. package/libs/icons.js.map +1 -0
  99. package/libs/index.cjs +71 -0
  100. package/libs/index.cjs.map +1 -0
  101. package/libs/index.css +1 -0
  102. package/libs/index.css.map +1 -0
  103. package/libs/index.d.cts +551 -0
  104. package/libs/index.d.ts +551 -0
  105. package/libs/index.js +11 -0
  106. package/libs/index.js.map +1 -0
  107. package/package.json +125 -0
  108. package/src/App.css +42 -0
  109. package/src/App.tsx +35 -0
  110. package/src/__snapshots__/App.test.tsx.snap +56 -0
  111. package/src/components/.gitkeep +0 -0
  112. package/src/components/__snapshots__/fp.test.tsx.snap +3 -0
  113. package/src/components/badge/badge.scss +20 -0
  114. package/src/components/badge/badge.stories.tsx +54 -0
  115. package/src/components/badge/badge.tsx +17 -0
  116. package/src/components/breadcrumbs/bc-item.tsx +20 -0
  117. package/src/components/breadcrumbs/breadcrumb.scss +35 -0
  118. package/src/components/breadcrumbs/breadcrumb.stories.tsx +92 -0
  119. package/src/components/breadcrumbs/breadcrumb.tsx +218 -0
  120. package/src/components/buttons/button.scss +115 -0
  121. package/src/components/buttons/button.stories.tsx +57 -0
  122. package/src/components/buttons/button.test.tsx +104 -0
  123. package/src/components/buttons/button.tsx +64 -0
  124. package/src/components/cards/card-style.scss +0 -0
  125. package/src/components/cards/card.scss +43 -0
  126. package/src/components/cards/card.stories.tsx +114 -0
  127. package/src/components/cards/card.test.tsx +30 -0
  128. package/src/components/cards/card.tsx +135 -0
  129. package/src/components/cards/flex-card.tsx +15 -0
  130. package/src/components/details/details.scss +75 -0
  131. package/src/components/details/details.stories.tsx +122 -0
  132. package/src/components/details/details.tsx +77 -0
  133. package/src/components/form/README.mdx +70 -0
  134. package/src/components/form/fields.tsx +45 -0
  135. package/src/components/form/form.scss +87 -0
  136. package/src/components/form/form.stories.tsx +49 -0
  137. package/src/components/form/form.tsx +71 -0
  138. package/src/components/form/input.stories.tsx +155 -0
  139. package/src/components/form/inputs.tsx +84 -0
  140. package/src/components/form/select.stories.tsx +38 -0
  141. package/src/components/form/select.tsx +112 -0
  142. package/src/components/form/textarea.tsx +87 -0
  143. package/src/components/fp.test.tsx +56 -0
  144. package/src/components/fp.tsx +78 -0
  145. package/src/components/heading/heading.stories.tsx +75 -0
  146. package/src/components/heading/heading.tsx +27 -0
  147. package/src/components/icons/components/add.tsx +42 -0
  148. package/src/components/icons/components/arrow-down.tsx +52 -0
  149. package/src/components/icons/components/arrow-left.tsx +49 -0
  150. package/src/components/icons/components/arrow-right.tsx +52 -0
  151. package/src/components/icons/components/arrow-up.tsx +49 -0
  152. package/src/components/icons/components/chat.tsx +44 -0
  153. package/src/components/icons/components/code.tsx +50 -0
  154. package/src/components/icons/components/copy.tsx +51 -0
  155. package/src/components/icons/components/down.tsx +33 -0
  156. package/src/components/icons/components/home.tsx +57 -0
  157. package/src/components/icons/components/left.tsx +43 -0
  158. package/src/components/icons/components/minus.tsx +42 -0
  159. package/src/components/icons/components/pause-solid.tsx +48 -0
  160. package/src/components/icons/components/pause.tsx +63 -0
  161. package/src/components/icons/components/play-solid.tsx +44 -0
  162. package/src/components/icons/components/play.tsx +51 -0
  163. package/src/components/icons/components/remove.tsx +42 -0
  164. package/src/components/icons/components/resume-solid.tsx +52 -0
  165. package/src/components/icons/components/resume.tsx +57 -0
  166. package/src/components/icons/components/right.tsx +43 -0
  167. package/src/components/icons/components/star.tsx +38 -0
  168. package/src/components/icons/components/stop-solid.tsx +44 -0
  169. package/src/components/icons/components/stop.tsx +54 -0
  170. package/src/components/icons/components/svg.tsx +44 -0
  171. package/src/components/icons/components/up.tsx +31 -0
  172. package/src/components/icons/components/user.tsx +46 -0
  173. package/src/components/icons/icon.scss +15 -0
  174. package/src/components/icons/icon.stories.tsx +208 -0
  175. package/src/components/icons/icon.tsx +100 -0
  176. package/src/components/icons/index.ts +29 -0
  177. package/src/components/icons/types.ts +12 -0
  178. package/src/components/images/README.mdx +43 -0
  179. package/src/components/images/figure.stories.tsx +34 -0
  180. package/src/components/images/figure.tsx +44 -0
  181. package/src/components/images/img.scss +43 -0
  182. package/src/components/images/img.stories.tsx +24 -0
  183. package/src/components/images/img.test.tsx +43 -0
  184. package/src/components/images/img.tsx +93 -0
  185. package/src/components/images/place-holder.png +0 -0
  186. package/src/components/kit.tsx +56 -0
  187. package/src/components/layout/_header.scss +72 -0
  188. package/src/components/layout/footer.stories.tsx +34 -0
  189. package/src/components/layout/landmarks.scss +51 -0
  190. package/src/components/layout/landmarks.stories.tsx +54 -0
  191. package/src/components/layout/landmarks.tsx +149 -0
  192. package/src/components/layout/main.stories.tsx +90 -0
  193. package/src/components/link/link.scss +92 -0
  194. package/src/components/link/link.stories.tsx +74 -0
  195. package/src/components/link/link.tsx +48 -0
  196. package/src/components/list/list.stories.tsx +52 -0
  197. package/src/components/list/list.tsx +74 -0
  198. package/src/components/modal/dialog.tsx +50 -0
  199. package/src/components/modal/modal.tsx +85 -0
  200. package/src/components/nav/nav.scss +90 -0
  201. package/src/components/nav/nav.stories.tsx +96 -0
  202. package/src/components/nav/nav.tsx +76 -0
  203. package/src/components/popover/node_modules/.vitest/results.json +1 -0
  204. package/src/components/popover/popover.stories.tsx +31 -0
  205. package/src/components/popover/popover.test.tsx +39 -0
  206. package/src/components/popover/popover.tsx +85 -0
  207. package/src/components/progress/progress.scss +70 -0
  208. package/src/components/progress/progress.stories.tsx +51 -0
  209. package/src/components/progress/progress.tsx +82 -0
  210. package/src/components/readme.stories.mdx +7 -0
  211. package/src/components/styles/index.css +520 -0
  212. package/src/components/styles/index.css.map +1 -0
  213. package/src/components/tables/table-elements.tsx +57 -0
  214. package/src/components/tables/table.tsx +57 -0
  215. package/src/components/tag/tag.scss +56 -0
  216. package/src/components/tag/tag.stories.tsx +39 -0
  217. package/src/components/tag/tag.tsx +25 -0
  218. package/src/components/text/text.stories.tsx +67 -0
  219. package/src/components/text/text.tsx +93 -0
  220. package/src/components/text-to-speech/README.mdx +192 -0
  221. package/src/components/text-to-speech/TextInput.tsx +19 -0
  222. package/src/components/text-to-speech/TextToSpeech.stories.tsx +145 -0
  223. package/src/components/text-to-speech/TextToSpeech.tsx +94 -0
  224. package/src/components/text-to-speech/text-to-speech.scss +31 -0
  225. package/src/components/text-to-speech/useTextToSpeech.mdx +182 -0
  226. package/src/components/text-to-speech/useTextToSpeech.tsx +176 -0
  227. package/src/components/text-to-speech/views/TextToSpeechControls.tsx +117 -0
  228. package/src/components/ui.tsx +67 -0
  229. package/src/favicon.svg +15 -0
  230. package/src/hooks/popover/__snapshots__/popover.test.tsx.snap +88 -0
  231. package/src/hooks/popover/node_modules/.vitest/results.json +1 -0
  232. package/src/hooks/popover/popover.tsx +71 -0
  233. package/src/hooks/popover/use-popover.tsx +83 -0
  234. package/src/hooks.ts +1 -0
  235. package/src/icons.ts +1 -0
  236. package/src/index.css +13 -0
  237. package/src/index.scss +19 -0
  238. package/src/index.ts +35 -0
  239. package/src/libs/content.ts +30 -0
  240. package/src/logo.svg +7 -0
  241. package/src/main.tsx +10 -0
  242. package/src/patterns/.gitkeep +0 -0
  243. package/src/patterns/page/page-header.stories.tsx +44 -0
  244. package/src/patterns/page/page-header.tsx +78 -0
  245. package/src/sass/_elements.scss +17 -0
  246. package/src/sass/_globals.scss +162 -0
  247. package/src/sass/_layout.scss +51 -0
  248. package/src/sass/_loading-animation.scss +35 -0
  249. package/src/sass/_mixins.scss +10 -0
  250. package/src/sass/_properties.scss +106 -0
  251. package/src/sass/_reset.scss +183 -0
  252. package/src/sass/_type.scss +43 -0
  253. package/src/setupTest.ts +1 -0
  254. package/src/styles/badge/badge.css +22 -0
  255. package/src/styles/badge/badge.css.map +1 -0
  256. package/src/styles/breadcrumbs/breadcrumb.css +42 -0
  257. package/src/styles/breadcrumbs/breadcrumb.css.map +1 -0
  258. package/src/styles/buttons/button.css +93 -0
  259. package/src/styles/buttons/button.css.map +1 -0
  260. package/src/styles/cards/card-style.css +3 -0
  261. package/src/styles/cards/card-style.css.map +1 -0
  262. package/src/styles/cards/card.css +48 -0
  263. package/src/styles/cards/card.css.map +1 -0
  264. package/src/styles/details/details.css +69 -0
  265. package/src/styles/details/details.css.map +1 -0
  266. package/src/styles/dropdowns/dropdown.css.map +1 -0
  267. package/src/styles/form/form.css +93 -0
  268. package/src/styles/form/form.css.map +1 -0
  269. package/src/styles/form/style.css.map +1 -0
  270. package/src/styles/icons/icon.css +16 -0
  271. package/src/styles/icons/icon.css.map +1 -0
  272. package/src/styles/images/img.css +42 -0
  273. package/src/styles/images/img.css.map +1 -0
  274. package/src/styles/index.css +1330 -0
  275. package/src/styles/index.css.map +1 -0
  276. package/src/styles/layout/landmarks.css +155 -0
  277. package/src/styles/layout/landmarks.css.map +1 -0
  278. package/src/styles/link/link.css +88 -0
  279. package/src/styles/link/link.css.map +1 -0
  280. package/src/styles/nav/nav.css +85 -0
  281. package/src/styles/nav/nav.css.map +1 -0
  282. package/src/styles/progress/progress.css +54 -0
  283. package/src/styles/progress/progress.css.map +1 -0
  284. package/src/styles/progress/sass/progress.css.map +1 -0
  285. package/src/styles/styles/index.css +562 -0
  286. package/src/styles/styles/index.css.map +1 -0
  287. package/src/styles/tag/badge.css.map +1 -0
  288. package/src/styles/tag/tag.css +71 -0
  289. package/src/styles/tag/tag.css.map +1 -0
  290. package/src/styles/text-to-speech/text-to-speech.css +32 -0
  291. package/src/styles/text-to-speech/text-to-speech.css.map +1 -0
  292. package/src/test/setup.ts +6 -0
  293. package/src/types/component-props.ts +36 -0
  294. package/src/types/index.ts +2 -0
  295. package/src/types/input-props.ts +28 -0
  296. package/src/types/shared.ts +57 -0
  297. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,87 @@
1
+ :root {
2
+ --input-border-color: gray;
3
+ --input-appearance: none;
4
+ --input-bg: inherit;
5
+ --input-border: none;
6
+ --input-outline: thin solid var(--input-border-color);
7
+ --input-px: 0.6rem;
8
+ --input-py: 0.4rem;
9
+ --input-radius: --var(--radius);
10
+ --input-fs: var(--fs);
11
+ --input-w: clamp(200px, 100%, 500px);
12
+ --placeholder-color: gray;
13
+ --placeholder-style: italic;
14
+ --placeholder-fs: smaller;
15
+ --form-direction: column;
16
+ --select-arrow: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='20' height='20'><polyline points='6,9 10,13 14,9' stroke='%23000000' stroke-width='1.5' fill='none' /></svg>");
17
+ }
18
+
19
+ form {
20
+ display: flex;
21
+ flex-direction: var(--form-direction);
22
+ gap: 1rem;
23
+ > div {
24
+ display: flex;
25
+ gap: 1rem;
26
+ flex-direction: var(--form-direction);
27
+ }
28
+
29
+ label {
30
+ display: block;
31
+ }
32
+ }
33
+
34
+ input[type]:not([type='checkbox'], [type='radio']),
35
+ textarea,
36
+ select {
37
+ -webkit-appearance: var(--input-appearance);
38
+ -moz-appearance: var(--input-appearance);
39
+ appearance: var(--input-appearance);
40
+ width: var(--input-w);
41
+ border: var(--input-border);
42
+ outline: var(--input-outline);
43
+ padding-inline: var(--input-px);
44
+ padding-block: var(--input-py);
45
+ border-radius: var(--input-radius);
46
+ background-color: var(--input-bg, #fff);
47
+
48
+ &::placeholder {
49
+ color: var(--placeholder-color);
50
+ font-style: var(--placeholder-style);
51
+ font-size: var(--placeholder-fs);
52
+ text-transform: capitalize;
53
+ }
54
+
55
+ &:focus-visible, &:focus {
56
+ outline-width: medium;
57
+ }
58
+
59
+
60
+ &[aria-required='true'] {
61
+ &::placeholder {
62
+ color: var(--color-required, var(--placeholder-color));
63
+ font-weight: 600;
64
+ &::after {
65
+ content: '* ';
66
+ }
67
+ }
68
+ }
69
+
70
+ &[aria-disabled='true'] {
71
+ --input-border-color: lightgray;
72
+ cursor: not-allowed;
73
+ text-transform: capitalize;
74
+ text-decoration: line-through;
75
+ }
76
+ }
77
+
78
+ select {
79
+ border: var(--input-outline);
80
+ outline: none;
81
+ -webkit-appearance: none; /* Remove default arrow in Chrome and Safari */
82
+ -moz-appearance: none; /* Remove default arrow in Firefox */
83
+ appearance: none; /* Remove default arrow in other browsers */
84
+ background: var(--select-arrow) no-repeat;
85
+ background-position: right 0.5rem top 50%;
86
+ padding-inline-end: 0;
87
+ }
@@ -0,0 +1,49 @@
1
+ import { StoryObj, Meta } from '@storybook/react'
2
+ import { within, userEvent, screen } from '@storybook/testing-library'
3
+ import { expect } from '@storybook/jest'
4
+
5
+ import Form from './form'
6
+ import './form.scss'
7
+
8
+ const meta: Meta<typeof Form> = {
9
+ title: 'FP.REACT Forms/Examples',
10
+ component: Form,
11
+ parameters: {
12
+ docs: {
13
+ description: {
14
+ component: 'Form description here...',
15
+ },
16
+ },
17
+ },
18
+ args: {
19
+ children: 'Link',
20
+ name: 'my-form',
21
+ },
22
+ } as Story
23
+
24
+ export default meta
25
+ type Story = StoryObj<typeof Form>
26
+
27
+ export const FormComponent: Story = {
28
+ args: {
29
+ children: (
30
+ <>
31
+ <Form.Field label="Name" labelFor="name" id="name-field">
32
+ <Form.Input id="name" name="name" />
33
+ </Form.Field>
34
+ <Form.Field label="Email" labelFor="email">
35
+ <Form.Input id="email" name="email" type="email" />
36
+ </Form.Field>
37
+ <Form.Field label="Message" labelFor="message">
38
+ <Form.Textarea id="message" name="message" />
39
+ </Form.Field>
40
+ <button type="submit">Submit Form</button>
41
+ </>
42
+ ),
43
+ },
44
+ play: async ({ canvasElement }) => {
45
+ const canvas = within(canvasElement)
46
+ const form = canvas.getByRole('form')
47
+ await expect(form).toBeInTheDocument()
48
+ },
49
+ } as Story
@@ -0,0 +1,71 @@
1
+ import UI from '#components/fp'
2
+ import React from 'react'
3
+
4
+ import Input from './inputs'
5
+ import Field from './fields'
6
+ import Select from './select'
7
+ import Textarea from './textarea'
8
+
9
+ export type FormProps = Partial<React.ComponentProps<typeof UI>> &
10
+ React.ComponentProps<'form'>
11
+
12
+ /**
13
+ * Form component
14
+ * @param {Object} props - Form component props
15
+ * @param {string} [id] - Unique identifier for form
16
+ * @param {string} [name] - Name for form
17
+ * @param {Object} [styles] - Inline styles
18
+ * @param {string} [classes] - Additional classes
19
+ * @param {ReactNode} children - Child elements
20
+ * @param {string} [action] - Form action URL
21
+ * @param {('get'|'post')} [formMethod='post'] - Form method
22
+ * @param {Function} [onSubmit] - Submit callback
23
+ * @param {string} [target] - Form submit target
24
+ * @param {boolean} [noValidate=false] - Disable validation
25
+ * @param {Object} ...props - Additional props
26
+ */
27
+ export const Form = ({
28
+ id,
29
+ name,
30
+ styles,
31
+ classes,
32
+ children,
33
+ action,
34
+ formMethod,
35
+ onSubmit,
36
+ target,
37
+ noValidate,
38
+ ...props
39
+ }: FormProps) => {
40
+ const onSubmitCallback = (e: React.FormEvent<HTMLFormElement>) => {
41
+ if (onSubmit) {
42
+ e.preventDefault()
43
+ onSubmit?.(e)
44
+ }
45
+ }
46
+
47
+ return (
48
+ <UI
49
+ as="form"
50
+ id={id}
51
+ name={name}
52
+ className={classes}
53
+ styles={styles}
54
+ action={action}
55
+ novalidate={noValidate}
56
+ method={formMethod}
57
+ onSubmit={onSubmitCallback}
58
+ target={target}
59
+ {...props}
60
+ >
61
+ {children}
62
+ </UI>
63
+ )
64
+ }
65
+
66
+ export default Form
67
+ Form.displayName = 'Form'
68
+ Form.Field = Field
69
+ Form.Input = Input
70
+ Form.Select = Select
71
+ Form.Textarea = Textarea
@@ -0,0 +1,155 @@
1
+ import { StoryObj, Meta } from '@storybook/react'
2
+ import { within, userEvent, screen } from '@storybook/testing-library'
3
+ import { expect } from '@storybook/jest'
4
+
5
+ import Input from './inputs'
6
+ import './form.scss'
7
+
8
+ const meta: Meta<typeof Input> = {
9
+ title: 'FP.REACT Forms/Inputs',
10
+ component: Input,
11
+ args: {},
12
+ parameters: {
13
+ docs: {
14
+ description: {
15
+ component:
16
+ 'Use the `<Input type="***"/>` component to render an any input element -- text, email, number etc. Pass props like `name`, `value`, `placeholder` etc to control the input.',
17
+ },
18
+ },
19
+ },
20
+ } as Story
21
+
22
+ export default meta
23
+ type Story = StoryObj<typeof Input>
24
+
25
+ export const InputComponent: Story = {
26
+ args: {},
27
+ play: async ({ canvasElement }) => {
28
+ const canvas = within(canvasElement)
29
+ expect(canvas.getByRole('textbox')).toBeInTheDocument()
30
+ },
31
+ }
32
+
33
+ //required input story
34
+ export const RequiredInput: Story = {
35
+ parameters: {
36
+ docs: {
37
+ description: {
38
+ story:
39
+ 'Displays a required input `aria-required="true"` on any input type the placeholder displays an `*` at the start of a default placeholder text to indicate it is required',
40
+ },
41
+ },
42
+ },
43
+ args: {
44
+ type: 'text',
45
+ required: true,
46
+ placeholder: 'This Field is required (placeholder)',
47
+ },
48
+
49
+ play: async ({ canvasElement }) => {
50
+ const canvas = within(canvasElement)
51
+ const input = canvas.getByRole('textbox')
52
+ expect(input).toBeRequired()
53
+
54
+ await userEvent.type(input, 'test')
55
+ expect(input).toBeValid()
56
+
57
+ await userEvent.clear(input)
58
+
59
+ userEvent.type(input, '\n')
60
+ expect(input).toBeInvalid()
61
+ },
62
+ } as Story
63
+
64
+ export const DefaultRequired: Story = {
65
+ args: {
66
+ type: 'text',
67
+ required: true,
68
+ },
69
+ } as Story
70
+
71
+ export const InputDisabled: Story = {
72
+ parameters: {
73
+ docs: {
74
+ description: {
75
+ story:
76
+ 'Displays a disabled input `aria-disabled="true"` on any input type',
77
+ },
78
+ },
79
+ },
80
+ args: {
81
+ type: 'text',
82
+ isDisabled: true,
83
+ },
84
+ } as Story
85
+
86
+ export const EmailInput: Story = {
87
+ args: {
88
+ type: 'email',
89
+ },
90
+ play: async ({ canvasElement }) => {
91
+ const canvas = within(canvasElement)
92
+ const input = canvas.getByRole('textbox')
93
+ expect(input).toHaveAttribute('type', 'email')
94
+
95
+ await userEvent.type(input, 'test@example.com')
96
+ expect(input).toHaveValue('test@example.com')
97
+ },
98
+ } as Story
99
+
100
+ export const PasswordInput: Story = {
101
+ args: {
102
+ type: 'password',
103
+ },
104
+ play: async ({ canvasElement }) => {
105
+ const canvas = within(canvasElement)
106
+ const input = canvas.getByPlaceholderText(/password/i)
107
+ expect(input).toHaveAttribute('type', 'password')
108
+
109
+ await userEvent.type(input, 'password')
110
+ expect(input).toHaveValue('password')
111
+ },
112
+ } as Story
113
+
114
+ export const SearchInput: Story = {
115
+ args: {
116
+ type: 'search',
117
+ },
118
+ play: async ({ canvasElement }) => {
119
+ const canvas = within(canvasElement)
120
+ const input = canvas.getByRole('searchbox')
121
+ expect(input).toHaveAttribute('type', 'search')
122
+
123
+ await userEvent.type(input, 'search term')
124
+ expect(input).toHaveValue('search term')
125
+ },
126
+ } as Story
127
+
128
+ export const TelInput: Story = {
129
+ args: {
130
+ type: 'tel',
131
+ },
132
+ play: async ({ canvasElement }) => {
133
+ const canvas = within(canvasElement)
134
+ const input = canvas.getByRole('textbox')
135
+ expect(input).toHaveAttribute('type', 'tel')
136
+
137
+ await userEvent.type(input, '1234567890')
138
+ expect(input).toHaveValue('1234567890')
139
+ },
140
+ } as Story
141
+
142
+ // URL text input story
143
+ export const UrlInput: Story = {
144
+ args: {
145
+ type: 'url',
146
+ },
147
+ play: async ({ canvasElement }) => {
148
+ const canvas = within(canvasElement)
149
+ const input = canvas.getByRole('textbox')
150
+ expect(input).toHaveAttribute('type', 'url')
151
+
152
+ await userEvent.type(input, 'https://example.com')
153
+ expect(input).toHaveValue('https://example.com')
154
+ },
155
+ } as Story
@@ -0,0 +1,84 @@
1
+ import React from 'react'
2
+ import FP from '../fp'
3
+
4
+ export type InputProps = {
5
+ /**
6
+ * The type of the input.
7
+ */
8
+ type?: 'text' | 'password' | 'email' | 'number' | 'tel' | 'url' | 'search'
9
+
10
+ /**
11
+ * Set the element as disabled
12
+ */
13
+ isDisabled?: boolean
14
+ } & React.ComponentProps<typeof FP>
15
+
16
+ /**
17
+ * Input component that renders an HTML input element.
18
+ * @param {InputProps} props - The input component props.
19
+ * @returns {JSX.Element} - The input component.
20
+ */
21
+ export const Input = ({
22
+ type = 'text',
23
+ name,
24
+ value,
25
+ placeholder,
26
+ id,
27
+ styles,
28
+ classes,
29
+ isDisabled,
30
+ disabled,
31
+ readonly,
32
+ required,
33
+ ref,
34
+ onChange,
35
+ onBlur,
36
+ onPointerDown,
37
+ ...props
38
+ }: InputProps): JSX.Element => {
39
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
40
+ if (onChange && !disabled) {
41
+ onChange?.(e)
42
+ }
43
+ }
44
+
45
+ const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
46
+ if (onBlur && !disabled) {
47
+ onBlur?.(e)
48
+ }
49
+ }
50
+
51
+ const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
52
+ if (onPointerDown && !disabled) {
53
+ e.preventDefault()
54
+ onPointerDown?.(e)
55
+ }
56
+ }
57
+
58
+ return (
59
+ <FP
60
+ as="input"
61
+ id={id}
62
+ type={type}
63
+ placeholder={placeholder || `${required ? '*' : ''} ${type} input `}
64
+ className={classes}
65
+ styles={styles}
66
+ onChange={handleChange}
67
+ onBlur={handleBlur}
68
+ onKeyDown={handleKeyDown}
69
+ value={value}
70
+ name={name}
71
+ ref={ref}
72
+ aria-disabled={isDisabled}
73
+ tabIndex={isDisabled ? -1 : undefined}
74
+ aria-readonly={readonly}
75
+ aria-required={required}
76
+ required={required}
77
+ readOnly={readonly}
78
+ {...props}
79
+ />
80
+ )
81
+ }
82
+
83
+ Input.displayName = 'Input'
84
+ export default Input
@@ -0,0 +1,38 @@
1
+ import { StoryObj, Meta } from '@storybook/react'
2
+ import { within, userEvent, screen } from '@storybook/testing-library'
3
+ import { expect } from '@storybook/jest'
4
+
5
+ import Select from './select'
6
+ import React from 'react'
7
+ const meta: Meta<typeof Select> = {
8
+ title: 'FP.REACT Forms/Select',
9
+ component: Select,
10
+ parameters: {
11
+ docs: {
12
+ description: {
13
+ component: 'Base select/Combobox component',
14
+ },
15
+ },
16
+ },
17
+ args: {
18
+ // @ts-ignore
19
+ children: (
20
+ <>
21
+ <Select.Option selectValue="value" selectLabel="Option 1" />
22
+ <Select.Option selectValue="value" selectLabel="Option 2" />
23
+ <Select.Option selectValue="value" selectLabel="Option 3" />
24
+ </>
25
+ ),
26
+ },
27
+ } as Story
28
+
29
+ export default meta
30
+ type Story = StoryObj<typeof Select>
31
+
32
+ export const SelectComponent: Story = {
33
+ args: {},
34
+ play: async ({ canvasElement }) => {
35
+ // const canvas = within(canvasElement)
36
+ // expect(canvas.getByRole('combobox')).toBeInTheDocument()
37
+ },
38
+ }
@@ -0,0 +1,112 @@
1
+ import UI from '../ui'
2
+ import React from 'react'
3
+
4
+ export type SelectProps = React.ComponentProps<typeof UI>
5
+
6
+ export type SelectOptionsProps = {
7
+ /**
8
+ * Label for the select option
9
+ */
10
+ selectLabel: string
11
+
12
+ /**
13
+ * Value for the select option. Can be a number or string.
14
+ */
15
+ selectValue: string
16
+ } & SelectProps
17
+
18
+ /**
19
+ * Option component for select.
20
+ * @param {SelectOptionsProps} param0 - The component props.
21
+ * @param {string} param0.selectValue - Value for the option.
22
+ * @param {string} [param0.selectLabel] - Label for the option.
23
+ */
24
+ export const Option = ({ selectValue, selectLabel }: SelectOptionsProps) => {
25
+ return (
26
+ <option role="option" value={selectValue}>
27
+ {selectLabel || selectValue}
28
+ </option>
29
+ )
30
+ }
31
+
32
+ /**
33
+ * Select component props.
34
+ * @param {string} [id] - Unique id for the select.
35
+ * @param {string} [name] - Name for the select input.
36
+ * @param {React.CSSProperties} [styles] - Inline styles.
37
+ * @param {string} [classes] - CSS classes.
38
+ * @param {boolean} [disabled] - Whether select is disabled.
39
+ * @param {React.ReactNode} [children] - Child elements.
40
+ * @param {boolean} [required] - Whether select is required.
41
+ * @param {string | number | string[] | undefined} [selected] - Selected option value(s).
42
+ * @param {React.FocusEventHandler<HTMLSelectElement>} [onBlur] - Blur event handler.
43
+ * @param {React.ChangeEventHandler<HTMLSelectElement>} [onChange] - Change event handler.
44
+ * @param {(e: React.ChangeEvent<HTMLSelectElement>) => void} [onSelectionChange] - Selection change handler.
45
+ * @param {(e: React.PointerEvent<HTMLSelectElement>) => void} [onPointerDown] - Pointer down handler.
46
+ * @param {React.Ref<HTMLSelectElement>} [ref] - Ref for the select element.
47
+ */
48
+ export const Select = ({
49
+ id,
50
+ name,
51
+ styles,
52
+ classes,
53
+ disabled,
54
+ children,
55
+ required,
56
+ selected,
57
+ onBlur,
58
+ onSelectionChange,
59
+ onPointerDown,
60
+ ref,
61
+ ...props
62
+ }: SelectProps) => {
63
+ const handleOnChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
64
+ if (onSelectionChange && !disabled) onSelectionChange?.(e)
65
+ }
66
+
67
+ const handlePointerDown = (e: React.PointerEvent<HTMLSelectElement>) => {
68
+ if (onPointerDown && !disabled) onPointerDown?.(e)
69
+ }
70
+
71
+ const handleOnBlur = (e: React.FocusEvent<HTMLSelectElement>) => {
72
+ if (onBlur && !disabled) onBlur?.(e)
73
+ const handleOnChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
74
+ if (onSelectionChange && !disabled) onSelectionChange?.(e)
75
+ }
76
+
77
+ const handlePointerDown = (e: React.PointerEvent<HTMLSelectElement>) => {
78
+ if (onPointerDown && !disabled) onPointerDown?.(e)
79
+ }
80
+
81
+ const handleOnBlur = (e: React.FocusEvent<HTMLSelectElement>) => {
82
+ if (onBlur && !disabled) onBlur?.(e)
83
+ }
84
+
85
+ return (
86
+ <UI
87
+ as="select"
88
+ id={id}
89
+ ref={ref}
90
+ name={name}
91
+ className={classes}
92
+ selected={selected}
93
+ onChange={handleOnChange}
94
+ onPointerDown={handlePointerDown}
95
+ onBlur={handleOnBlur}
96
+ required={required}
97
+ aria-required={required} // Accessibility
98
+ disabled={disabled}
99
+ aria-disabled={disabled ? true : false}
100
+ style={styles}
101
+ {...props} // Accessibility
102
+ >
103
+ <option value="" />
104
+ </UI>
105
+ )
106
+ }
107
+ }
108
+ export default Select
109
+ Select.displayName = 'Select' // Remove this line
110
+ Select.Option = Option // Remove this line
111
+
112
+ // export const MemoizedSelect = React.memo(Select)
@@ -0,0 +1,87 @@
1
+ import React from 'react'
2
+ import UI from '../ui'
3
+
4
+ export type TextareaProps = React.ComponentProps<'textarea'> &
5
+ React.ComponentProps<typeof UI>
6
+
7
+ /**
8
+ * Textarea component.
9
+ *
10
+ * @param value - The value of the textarea.
11
+ * @param rows - The number of rows.
12
+ * @param cols - The number of columns.
13
+ * @param id - The id of the textarea.
14
+ * @param name - The name of the textarea.
15
+ * @param required - Whether the textarea is required.
16
+ * @param disabled - Whether the textarea is disabled.
17
+ * @param readOnly - Whether the textarea is read only.
18
+ * @param onBlur - Blur event handler.
19
+ * @param onPointerDown - Pointer down event handler.
20
+ * @param onChange - Change event handler.
21
+ * @param ref - Ref for the textarea.
22
+ * @param styles - Styles object for the textarea.
23
+ * @param textareaRef - Ref specifically for the textarea element.
24
+ * @param props - Other props.
25
+ */
26
+ export const Textarea = ({
27
+ id,
28
+ classes,
29
+ value,
30
+ rows = 5,
31
+ cols = 25,
32
+ name,
33
+ required,
34
+ disabled,
35
+ readOnly,
36
+ onBlur,
37
+ onPointerDown,
38
+ onChange,
39
+ ref,
40
+ styles,
41
+ placeholder,
42
+ ...props
43
+ }: TextareaProps) => {
44
+ const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
45
+ if (onChange && !disabled) {
46
+ onChange?.(e)
47
+ }
48
+ }
49
+
50
+ const handleBlur = (e: React.FocusEvent<HTMLTextAreaElement>) => {
51
+ if (onBlur && !disabled) {
52
+ onBlur?.(e)
53
+ }
54
+ }
55
+
56
+ const handlePointerDown = (e: React.PointerEvent<HTMLTextAreaElement>) => {
57
+ if (onPointerDown && !disabled) {
58
+ onPointerDown?.(e)
59
+ }
60
+ }
61
+
62
+ return (
63
+ <UI
64
+ as="textarea"
65
+ id={id}
66
+ name={name}
67
+ rows={rows}
68
+ cols={cols}
69
+ styles={styles}
70
+ className={classes}
71
+ data-style="textarea"
72
+ required={required}
73
+ value={value}
74
+ aria-disabled={disabled}
75
+ readOnly={readOnly}
76
+ onChange={handleChange}
77
+ onBlur={handleBlur}
78
+ onPointerDown={handlePointerDown}
79
+ ref={ref}
80
+ placeholder={placeholder || `${required ? '*' : ''} Message`}
81
+ {...props}
82
+ />
83
+ )
84
+ }
85
+
86
+ export default Textarea
87
+ Textarea.displayName = 'Textarea'