@strictly/react-form 0.0.1

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 (239) hide show
  1. package/.eslintrc.cjs +26 -0
  2. package/.out/.storybook/main.d.ts +3 -0
  3. package/.out/.storybook/main.js +32 -0
  4. package/.out/.storybook/preview.d.ts +4 -0
  5. package/.out/.storybook/preview.js +20 -0
  6. package/.out/.vitest/install_deterministic_random.d.ts +2 -0
  7. package/.out/.vitest/install_deterministic_random.js +15 -0
  8. package/.out/.vitest/install_storybook_preview.d.ts +1 -0
  9. package/.out/.vitest/install_storybook_preview.js +7 -0
  10. package/.out/.vitest/match_media.d.ts +1 -0
  11. package/.out/.vitest/match_media.js +5 -0
  12. package/.out/.vitest/resize_observer.d.ts +1 -0
  13. package/.out/.vitest/resize_observer.js +4 -0
  14. package/.out/core/mobx/field_adapter.d.ts +9 -0
  15. package/.out/core/mobx/field_adapter.js +1 -0
  16. package/.out/core/mobx/field_adapter_builder.d.ts +22 -0
  17. package/.out/core/mobx/field_adapter_builder.js +56 -0
  18. package/.out/core/mobx/flattened_adapters_of_fields.d.ts +9 -0
  19. package/.out/core/mobx/flattened_adapters_of_fields.js +1 -0
  20. package/.out/core/mobx/flattened_list_type_defs_of.d.ts +8 -0
  21. package/.out/core/mobx/flattened_list_type_defs_of.js +1 -0
  22. package/.out/core/mobx/form_presenter.d.ts +61 -0
  23. package/.out/core/mobx/form_presenter.js +425 -0
  24. package/.out/core/mobx/specs/flattened_adapters_of_fields.tests.d.ts +1 -0
  25. package/.out/core/mobx/specs/flattened_adapters_of_fields.tests.js +13 -0
  26. package/.out/core/mobx/specs/flattened_list_type_defs_of.tests.d.ts +1 -0
  27. package/.out/core/mobx/specs/flattened_list_type_defs_of.tests.js +16 -0
  28. package/.out/core/mobx/specs/form_presenter.tests.d.ts +1 -0
  29. package/.out/core/mobx/specs/form_presenter.tests.js +697 -0
  30. package/.out/core/mobx/types.d.ts +19 -0
  31. package/.out/core/mobx/types.js +1 -0
  32. package/.out/core/props.d.ts +12 -0
  33. package/.out/core/props.js +1 -0
  34. package/.out/field_converters/chain_field_converter.d.ts +3 -0
  35. package/.out/field_converters/chain_field_converter.js +46 -0
  36. package/.out/field_converters/identity_converter.d.ts +3 -0
  37. package/.out/field_converters/identity_converter.js +14 -0
  38. package/.out/field_converters/integer_to_string_converter.d.ts +7 -0
  39. package/.out/field_converters/integer_to_string_converter.js +26 -0
  40. package/.out/field_converters/list_converter.d.ts +2 -0
  41. package/.out/field_converters/list_converter.js +8 -0
  42. package/.out/field_converters/maybe_identity_converter.d.ts +8 -0
  43. package/.out/field_converters/maybe_identity_converter.js +15 -0
  44. package/.out/field_converters/nullable_to_boolean_converter.d.ts +11 -0
  45. package/.out/field_converters/nullable_to_boolean_converter.js +31 -0
  46. package/.out/field_converters/select_value_type_converter.d.ts +23 -0
  47. package/.out/field_converters/select_value_type_converter.js +60 -0
  48. package/.out/field_converters/trimming_string_converter.d.ts +6 -0
  49. package/.out/field_converters/trimming_string_converter.js +14 -0
  50. package/.out/field_converters/validating_converter.d.ts +3 -0
  51. package/.out/field_converters/validating_converter.js +21 -0
  52. package/.out/field_validators/minimum_string_length_field_validator.d.ts +2 -0
  53. package/.out/field_validators/minimum_string_length_field_validator.js +8 -0
  54. package/.out/field_value_factories/prototyping_field_value_factory.d.ts +2 -0
  55. package/.out/field_value_factories/prototyping_field_value_factory.js +5 -0
  56. package/.out/index.d.ts +16 -0
  57. package/.out/index.js +16 -0
  58. package/.out/mantine/create_checkbox.d.ts +9 -0
  59. package/.out/mantine/create_checkbox.js +37 -0
  60. package/.out/mantine/create_list.d.ts +15 -0
  61. package/.out/mantine/create_list.js +16 -0
  62. package/.out/mantine/create_pill.d.ts +7 -0
  63. package/.out/mantine/create_pill.js +15 -0
  64. package/.out/mantine/create_radio.d.ts +8 -0
  65. package/.out/mantine/create_radio.js +10 -0
  66. package/.out/mantine/create_radio_group.d.ts +9 -0
  67. package/.out/mantine/create_radio_group.js +34 -0
  68. package/.out/mantine/create_text_input.d.ts +19 -0
  69. package/.out/mantine/create_text_input.js +38 -0
  70. package/.out/mantine/create_value_input.d.ts +17 -0
  71. package/.out/mantine/create_value_input.js +38 -0
  72. package/.out/mantine/hooks.d.ts +56 -0
  73. package/.out/mantine/hooks.js +135 -0
  74. package/.out/mantine/specs/checkbox_constants.d.ts +1 -0
  75. package/.out/mantine/specs/checkbox_constants.js +1 -0
  76. package/.out/mantine/specs/checkbox_hooks.stories.d.ts +13 -0
  77. package/.out/mantine/specs/checkbox_hooks.stories.js +63 -0
  78. package/.out/mantine/specs/checkbox_hooks.tests.d.ts +1 -0
  79. package/.out/mantine/specs/checkbox_hooks.tests.js +74 -0
  80. package/.out/mantine/specs/list_hooks.stories.d.ts +11 -0
  81. package/.out/mantine/specs/list_hooks.stories.js +48 -0
  82. package/.out/mantine/specs/list_hooks.tests.d.ts +1 -0
  83. package/.out/mantine/specs/list_hooks.tests.js +12 -0
  84. package/.out/mantine/specs/radio_group_constants.d.ts +4 -0
  85. package/.out/mantine/specs/radio_group_constants.js +11 -0
  86. package/.out/mantine/specs/radio_group_hooks.stories.d.ts +14 -0
  87. package/.out/mantine/specs/radio_group_hooks.stories.js +68 -0
  88. package/.out/mantine/specs/radio_group_hooks.tests.d.ts +1 -0
  89. package/.out/mantine/specs/radio_group_hooks.tests.js +62 -0
  90. package/.out/mantine/specs/select_hooks.stories.d.ts +12 -0
  91. package/.out/mantine/specs/select_hooks.stories.js +57 -0
  92. package/.out/mantine/specs/select_hooks.tests.d.ts +1 -0
  93. package/.out/mantine/specs/select_hooks.tests.js +12 -0
  94. package/.out/mantine/specs/select_hooks_constant.d.ts +1 -0
  95. package/.out/mantine/specs/select_hooks_constant.js +1 -0
  96. package/.out/mantine/specs/text_input_constants.d.ts +1 -0
  97. package/.out/mantine/specs/text_input_constants.js +1 -0
  98. package/.out/mantine/specs/text_input_hooks.stories.d.ts +21 -0
  99. package/.out/mantine/specs/text_input_hooks.stories.js +88 -0
  100. package/.out/mantine/specs/text_input_hooks.tests.d.ts +1 -0
  101. package/.out/mantine/specs/text_input_hooks.tests.js +79 -0
  102. package/.out/mantine/specs/value_input_constants.d.ts +2 -0
  103. package/.out/mantine/specs/value_input_constants.js +2 -0
  104. package/.out/mantine/specs/value_input_hooks.stories.d.ts +23 -0
  105. package/.out/mantine/specs/value_input_hooks.stories.js +124 -0
  106. package/.out/mantine/specs/value_input_hooks.tests.d.ts +1 -0
  107. package/.out/mantine/specs/value_input_hooks.tests.js +12 -0
  108. package/.out/mantine/types.d.ts +11 -0
  109. package/.out/mantine/types.js +1 -0
  110. package/.out/tsconfig.json +27 -0
  111. package/.out/tsconfig.tsbuildinfo +1 -0
  112. package/.out/tsup.config.d.ts +3 -0
  113. package/.out/tsup.config.js +12 -0
  114. package/.out/types/all_fields_of_fields.d.ts +5 -0
  115. package/.out/types/all_fields_of_fields.js +1 -0
  116. package/.out/types/boolean_fields_of_fields.d.ts +5 -0
  117. package/.out/types/boolean_fields_of_fields.js +1 -0
  118. package/.out/types/error_type_of_field.d.ts +2 -0
  119. package/.out/types/error_type_of_field.js +1 -0
  120. package/.out/types/field.d.ts +7 -0
  121. package/.out/types/field.js +1 -0
  122. package/.out/types/field_converters.d.ts +29 -0
  123. package/.out/types/field_converters.js +5 -0
  124. package/.out/types/field_validator.d.ts +3 -0
  125. package/.out/types/field_validator.js +1 -0
  126. package/.out/types/flattened_form_fields_of.d.ts +9 -0
  127. package/.out/types/flattened_form_fields_of.js +1 -0
  128. package/.out/types/list_fields_of_fields.d.ts +5 -0
  129. package/.out/types/list_fields_of_fields.js +1 -0
  130. package/.out/types/specs/boolean_fields_of_fields.tests.d.ts +1 -0
  131. package/.out/types/specs/boolean_fields_of_fields.tests.js +11 -0
  132. package/.out/types/specs/error_type_of_field.tests.d.ts +1 -0
  133. package/.out/types/specs/error_type_of_field.tests.js +7 -0
  134. package/.out/types/specs/flattened_form_fields_of.tests.d.ts +1 -0
  135. package/.out/types/specs/flattened_form_fields_of.tests.js +13 -0
  136. package/.out/types/specs/string_fields_of_fields.tests.d.ts +1 -0
  137. package/.out/types/specs/string_fields_of_fields.tests.js +19 -0
  138. package/.out/types/specs/value_type_of_field.tests.d.ts +1 -0
  139. package/.out/types/specs/value_type_of_field.tests.js +7 -0
  140. package/.out/types/string_fields_of_fields.d.ts +5 -0
  141. package/.out/types/string_fields_of_fields.js +1 -0
  142. package/.out/types/value_type_of_field.d.ts +2 -0
  143. package/.out/types/value_type_of_field.js +1 -0
  144. package/.out/util/partial.d.ts +11 -0
  145. package/.out/util/partial.js +74 -0
  146. package/.out/vitest.workspace.d.ts +2 -0
  147. package/.out/vitest.workspace.js +22 -0
  148. package/.storybook/main.ts +40 -0
  149. package/.storybook/preview.tsx +28 -0
  150. package/.storybook/vite.config.mts +38 -0
  151. package/.turbo/turbo-build.log +18 -0
  152. package/.turbo/turbo-check-types.log +3 -0
  153. package/.turbo/turbo-release$colon$exports.log +3 -0
  154. package/.vitest/install_deterministic_random.ts +17 -0
  155. package/.vitest/install_storybook_preview.ts +9 -0
  156. package/.vitest/match_media.ts +7 -0
  157. package/.vitest/resize_observer.ts +5 -0
  158. package/README.md +2 -0
  159. package/core/mobx/field_adapter.ts +32 -0
  160. package/core/mobx/field_adapter_builder.ts +313 -0
  161. package/core/mobx/flattened_adapters_of_fields.ts +35 -0
  162. package/core/mobx/flattened_list_type_defs_of.ts +17 -0
  163. package/core/mobx/form_presenter.ts +705 -0
  164. package/core/mobx/specs/flattened_adapters_of_fields.tests.ts +72 -0
  165. package/core/mobx/specs/flattened_list_type_defs_of.tests.ts +35 -0
  166. package/core/mobx/specs/form_presenter.tests.ts +989 -0
  167. package/core/mobx/types.ts +54 -0
  168. package/core/props.ts +21 -0
  169. package/dist/index.cjs +11479 -0
  170. package/dist/index.d.cts +345 -0
  171. package/dist/index.d.ts +345 -0
  172. package/dist/index.js +11486 -0
  173. package/field_converters/chain_field_converter.ts +74 -0
  174. package/field_converters/identity_converter.ts +39 -0
  175. package/field_converters/integer_to_string_converter.ts +32 -0
  176. package/field_converters/list_converter.ts +15 -0
  177. package/field_converters/maybe_identity_converter.ts +23 -0
  178. package/field_converters/nullable_to_boolean_converter.ts +56 -0
  179. package/field_converters/select_value_type_converter.ts +141 -0
  180. package/field_converters/trimming_string_converter.ts +23 -0
  181. package/field_converters/validating_converter.ts +35 -0
  182. package/field_validators/minimum_string_length_field_validator.ts +13 -0
  183. package/field_value_factories/prototyping_field_value_factory.ts +11 -0
  184. package/index.ts +16 -0
  185. package/mantine/create_checkbox.tsx +79 -0
  186. package/mantine/create_list.tsx +58 -0
  187. package/mantine/create_pill.tsx +43 -0
  188. package/mantine/create_radio.tsx +36 -0
  189. package/mantine/create_radio_group.tsx +71 -0
  190. package/mantine/create_text_input.tsx +80 -0
  191. package/mantine/create_value_input.tsx +81 -0
  192. package/mantine/hooks.tsx +394 -0
  193. package/mantine/specs/__snapshots__/check_box_hooks.tests.tsx.snap +227 -0
  194. package/mantine/specs/__snapshots__/checkbox_hooks.tests.tsx.snap +227 -0
  195. package/mantine/specs/__snapshots__/list_hooks.tests.tsx.snap +68 -0
  196. package/mantine/specs/__snapshots__/radio_group_hooks.tests.tsx.snap +695 -0
  197. package/mantine/specs/__snapshots__/select_hooks.tests.tsx.snap +225 -0
  198. package/mantine/specs/__snapshots__/text_input_hooks.tests.tsx.snap +202 -0
  199. package/mantine/specs/__snapshots__/value_input_hooks.tests.tsx.snap +613 -0
  200. package/mantine/specs/checkbox_constants.ts +1 -0
  201. package/mantine/specs/checkbox_hooks.stories.tsx +79 -0
  202. package/mantine/specs/checkbox_hooks.tests.tsx +100 -0
  203. package/mantine/specs/list_hooks.stories.tsx +83 -0
  204. package/mantine/specs/list_hooks.tests.tsx +15 -0
  205. package/mantine/specs/radio_group_constants.ts +12 -0
  206. package/mantine/specs/radio_group_hooks.stories.tsx +103 -0
  207. package/mantine/specs/radio_group_hooks.tests.tsx +92 -0
  208. package/mantine/specs/select_hooks.stories.tsx +77 -0
  209. package/mantine/specs/select_hooks.tests.tsx +14 -0
  210. package/mantine/specs/select_hooks_constant.ts +1 -0
  211. package/mantine/specs/text_input_constants.ts +1 -0
  212. package/mantine/specs/text_input_hooks.stories.tsx +124 -0
  213. package/mantine/specs/text_input_hooks.tests.tsx +106 -0
  214. package/mantine/specs/value_input_constants.ts +2 -0
  215. package/mantine/specs/value_input_hooks.stories.tsx +182 -0
  216. package/mantine/specs/value_input_hooks.tests.tsx +14 -0
  217. package/mantine/types.ts +13 -0
  218. package/package.exports.json +18 -0
  219. package/package.json +74 -0
  220. package/tsconfig.build.json +13 -0
  221. package/tsconfig.json +27 -0
  222. package/tsup.config.ts +16 -0
  223. package/types/all_fields_of_fields.ts +9 -0
  224. package/types/boolean_fields_of_fields.ts +8 -0
  225. package/types/error_type_of_field.ts +3 -0
  226. package/types/field.ts +9 -0
  227. package/types/field_converters.ts +64 -0
  228. package/types/field_validator.ts +7 -0
  229. package/types/flattened_form_fields_of.ts +16 -0
  230. package/types/list_fields_of_fields.ts +7 -0
  231. package/types/specs/boolean_fields_of_fields.tests.ts +23 -0
  232. package/types/specs/error_type_of_field.tests.ts +10 -0
  233. package/types/specs/flattened_form_fields_of.tests.ts +43 -0
  234. package/types/specs/string_fields_of_fields.tests.ts +40 -0
  235. package/types/specs/value_type_of_field.tests.ts +10 -0
  236. package/types/string_fields_of_fields.ts +6 -0
  237. package/types/value_type_of_field.ts +3 -0
  238. package/util/partial.tsx +200 -0
  239. package/vitest.workspace.ts +26 -0
@@ -0,0 +1,100 @@
1
+ import { composeStories } from '@storybook/react'
2
+ import { toArray } from '@strictly/base'
3
+ import {
4
+ fireEvent,
5
+ render,
6
+ type RenderResult,
7
+ } from '@testing-library/react'
8
+ import {
9
+ type Mock,
10
+ vi,
11
+ } from 'vitest'
12
+ import { CHECKBOX_LABEL } from './checkbox_constants'
13
+ import * as stories from './checkbox_hooks.stories'
14
+
15
+ const composedStories = composeStories(stories)
16
+ const {
17
+ Off,
18
+ On,
19
+ } = composedStories
20
+
21
+ describe('mantine checkbox hooks', function () {
22
+ it.each(toArray(composedStories))('renders %s', function (_name, Story) {
23
+ const wrapper = render(<Story />)
24
+ expect(wrapper.container).toMatchSnapshot()
25
+ })
26
+
27
+ describe.each([
28
+ [
29
+ 'Off',
30
+ Off,
31
+ true,
32
+ ],
33
+ [
34
+ 'On',
35
+ On,
36
+ false,
37
+ ],
38
+ ] as const)('value change %s', function (_name, Component, expectedValue) {
39
+ let onFieldValueChange: Mock<(path: '$', value: boolean) => void>
40
+ let wrapper: RenderResult
41
+ let checkbox: HTMLElement
42
+
43
+ beforeEach(async function () {
44
+ onFieldValueChange = vi.fn()
45
+ wrapper = render(<Component onFieldValueChange={onFieldValueChange} />)
46
+ checkbox = await wrapper.findByLabelText(CHECKBOX_LABEL)
47
+ })
48
+
49
+ it('requests toggle', function () {
50
+ fireEvent.click(checkbox)
51
+ expect(onFieldValueChange).toHaveBeenCalledOnce()
52
+ expect(onFieldValueChange).toHaveBeenCalledWith('$', expectedValue)
53
+ })
54
+ })
55
+
56
+ describe('other events', function () {
57
+ let onFieldFocus: Mock<(path: '$') => void>
58
+ let onFieldBlur: Mock<(path: '$') => void>
59
+ let wrapper: RenderResult
60
+ let checkbox: HTMLElement
61
+
62
+ beforeEach(async function () {
63
+ onFieldFocus = vi.fn()
64
+ onFieldBlur = vi.fn()
65
+ wrapper = render((
66
+ <Off
67
+ onFieldBlur={onFieldBlur}
68
+ onFieldFocus={onFieldFocus}
69
+ />
70
+ ))
71
+ checkbox = await wrapper.findByLabelText(CHECKBOX_LABEL)
72
+ })
73
+
74
+ describe('focus', function () {
75
+ beforeEach(function () {
76
+ fireEvent.focus(checkbox)
77
+ })
78
+
79
+ it('fires focus event', function () {
80
+ expect(onFieldFocus).toHaveBeenCalledOnce()
81
+ expect(onFieldFocus).toHaveBeenCalledWith('$')
82
+ })
83
+
84
+ describe('blur', function () {
85
+ beforeEach(function () {
86
+ fireEvent.blur(checkbox)
87
+ })
88
+
89
+ it('fires blur event', function () {
90
+ expect(onFieldBlur).toHaveBeenCalledOnce()
91
+ expect(onFieldBlur).toHaveBeenCalledWith('$')
92
+ })
93
+
94
+ it('does not refire focus event', function () {
95
+ expect(onFieldFocus).toHaveBeenCalledOnce()
96
+ })
97
+ })
98
+ })
99
+ })
100
+ })
@@ -0,0 +1,83 @@
1
+ import {
2
+ Code,
3
+ Paper,
4
+ Stack,
5
+ } from '@mantine/core'
6
+ import { action } from '@storybook/addon-actions'
7
+ import {
8
+ type Meta,
9
+ type StoryObj,
10
+ } from '@storybook/react'
11
+ import { type FormProps } from 'core/props'
12
+ import { useMantineForm } from 'mantine/hooks'
13
+ import { type Field } from 'types/field'
14
+
15
+ type ListPath = `$.${number}`
16
+
17
+ function Component(props: FormProps<{
18
+ $: Field<string[], string>,
19
+ }>) {
20
+ const form = useMantineForm(props)
21
+ const List = form.list('$')
22
+ return (
23
+ <Paper
24
+ p='sm'
25
+ withBorder={true}
26
+ >
27
+ <Stack>
28
+ <List>
29
+ {function (valuePath: ListPath) {
30
+ return (
31
+ <Code key={valuePath}>
32
+ ValuePath: {valuePath}
33
+ </Code>
34
+ )
35
+ }}
36
+ </List>
37
+ </Stack>
38
+ </Paper>
39
+ )
40
+ }
41
+
42
+ const meta: Meta<typeof Component> = {
43
+ component: Component,
44
+ args: {
45
+ onFieldBlur: action('onFieldBlur'),
46
+ onFieldFocus: action('onFieldFocus'),
47
+ onFieldSubmit: action('onFieldSubmit'),
48
+ onFieldValueChange: action('onFieldValueChange'),
49
+ },
50
+ }
51
+
52
+ export default meta
53
+
54
+ type Story = StoryObj<typeof Component>
55
+
56
+ export const Empty: Story = {
57
+ args: {
58
+ fields: {
59
+ $: {
60
+ disabled: false,
61
+ required: false,
62
+ value: [],
63
+ },
64
+ },
65
+ },
66
+ }
67
+
68
+ export const Populated: Story = {
69
+ args: {
70
+ fields: {
71
+ $: {
72
+ disabled: false,
73
+ required: false,
74
+ value: [
75
+ '$.4',
76
+ '$.6',
77
+ '$.19',
78
+ '$.0',
79
+ ],
80
+ },
81
+ },
82
+ },
83
+ }
@@ -0,0 +1,15 @@
1
+ import { composeStories } from '@storybook/react'
2
+ import { toArray } from '@strictly/base'
3
+ import {
4
+ render,
5
+ } from '@testing-library/react'
6
+ import * as stories from './list_hooks.stories'
7
+
8
+ const composedStories = composeStories(stories)
9
+
10
+ describe('mantine list hooks', function () {
11
+ it.each(toArray(composedStories))('renders %s', function (_name, Story) {
12
+ const wrapper = render(<Story />)
13
+ expect(wrapper.container).toMatchSnapshot()
14
+ })
15
+ })
@@ -0,0 +1,12 @@
1
+ export const RADIO_GROUP_LABEL = 'Radio Group'
2
+ export const RADIO_VALUES = [
3
+ '1',
4
+ '2',
5
+ '3',
6
+ ] as const
7
+ export type RadioValue = typeof RADIO_VALUES[number]
8
+ export const RADIO_LABELS: Record<RadioValue, string> = {
9
+ 1: 'One',
10
+ 2: 'Two',
11
+ 3: 'Three',
12
+ }
@@ -0,0 +1,103 @@
1
+ import {
2
+ Stack,
3
+ } from '@mantine/core'
4
+ import { action } from '@storybook/addon-actions'
5
+ import {
6
+ type Meta,
7
+ type StoryObj,
8
+ } from '@storybook/react'
9
+ import { type FormProps } from 'core/props'
10
+ import { useMantineForm } from 'mantine/hooks'
11
+ import { type Field } from 'types/field'
12
+ import {
13
+ RADIO_GROUP_LABEL,
14
+ RADIO_LABELS,
15
+ RADIO_VALUES,
16
+ type RadioValue,
17
+ } from './radio_group_constants'
18
+
19
+ function Component(props: FormProps<{
20
+ $: Field<RadioValue | null, string>,
21
+ }>) {
22
+ const form = useMantineForm(props)
23
+ const RadioGroupComponent = form.radioGroup('$')
24
+
25
+ return (
26
+ <RadioGroupComponent label={RADIO_GROUP_LABEL}>
27
+ <Stack>
28
+ {RADIO_VALUES.map(function (value: RadioValue) {
29
+ const label = RADIO_LABELS[value]
30
+ const RadioComponent = form.radio('$', value)
31
+ return (
32
+ <RadioComponent
33
+ key={label}
34
+ label={label}
35
+ />
36
+ )
37
+ })}
38
+ </Stack>
39
+ </RadioGroupComponent>
40
+ )
41
+ }
42
+
43
+ const meta: Meta<typeof Component> = {
44
+ component: Component,
45
+ args: {
46
+ onFieldBlur: action('onFieldBlur'),
47
+ onFieldFocus: action('onFieldFocus'),
48
+ onFieldSubmit: action('onFieldSubmit'),
49
+ onFieldValueChange: action('onFieldValueChange'),
50
+ },
51
+ }
52
+
53
+ export default meta
54
+
55
+ type Story = StoryObj<typeof Component>
56
+
57
+ export const Empty: Story = {
58
+ args: {
59
+ fields: {
60
+ $: {
61
+ disabled: false,
62
+ required: false,
63
+ value: null,
64
+ },
65
+ },
66
+ },
67
+ }
68
+
69
+ export const Populated: Story = {
70
+ args: {
71
+ fields: {
72
+ $: {
73
+ disabled: false,
74
+ required: false,
75
+ value: '3',
76
+ },
77
+ },
78
+ },
79
+ }
80
+
81
+ export const Required: Story = {
82
+ args: {
83
+ fields: {
84
+ $: {
85
+ disabled: false,
86
+ required: true,
87
+ value: '1',
88
+ },
89
+ },
90
+ },
91
+ }
92
+
93
+ export const Disabled: Story = {
94
+ args: {
95
+ fields: {
96
+ $: {
97
+ disabled: true,
98
+ required: false,
99
+ value: '2',
100
+ },
101
+ },
102
+ },
103
+ }
@@ -0,0 +1,92 @@
1
+ import { composeStories } from '@storybook/react'
2
+ import { toArray } from '@strictly/base'
3
+ import {
4
+ fireEvent,
5
+ render,
6
+ type RenderResult,
7
+ } from '@testing-library/react'
8
+ import {
9
+ type Mock,
10
+ vi,
11
+ } from 'vitest'
12
+ import {
13
+ RADIO_GROUP_LABEL,
14
+ RADIO_LABELS,
15
+ RADIO_VALUES,
16
+ type RadioValue,
17
+ } from './radio_group_constants'
18
+ import * as stories from './radio_group_hooks.stories'
19
+
20
+ const composedStories = composeStories(stories)
21
+ const {
22
+ Empty,
23
+ } = composedStories
24
+
25
+ describe('mantine radio group hooks', function () {
26
+ it.each(toArray(composedStories))('renders %s', function (_name, Story) {
27
+ const wrapper = render(<Story />)
28
+ expect(wrapper.container).toMatchSnapshot()
29
+ })
30
+
31
+ describe('events', function () {
32
+ let onFieldValueChange: Mock<(path: '$', value: RadioValue) => void>
33
+ let onFieldFocus: Mock<(path: '$') => void>
34
+ let onFieldBlur: Mock<(path: '$') => void>
35
+ let wrapper: RenderResult
36
+ let radioGroup: HTMLElement
37
+
38
+ beforeEach(async function () {
39
+ onFieldValueChange = vi.fn()
40
+ onFieldFocus = vi.fn()
41
+ onFieldBlur = vi.fn()
42
+ wrapper = render((
43
+ <Empty
44
+ onFieldBlur={onFieldBlur}
45
+ onFieldFocus={onFieldFocus}
46
+ onFieldValueChange={onFieldValueChange}
47
+ />
48
+ ))
49
+ radioGroup = await wrapper.findByLabelText(RADIO_GROUP_LABEL)
50
+ })
51
+
52
+ describe.each(RADIO_VALUES)('selects %s', function (value) {
53
+ let radio: HTMLElement
54
+ beforeEach(async function () {
55
+ const label = RADIO_LABELS[value]
56
+ radio = await wrapper.findByLabelText(label)
57
+ fireEvent.click(radio)
58
+ })
59
+
60
+ it('fires onFieldValueChange', function () {
61
+ expect(onFieldValueChange).toHaveBeenCalledOnce()
62
+ expect(onFieldValueChange).toHaveBeenCalledWith('$', value)
63
+ })
64
+ })
65
+
66
+ describe('focus', function () {
67
+ beforeEach(function () {
68
+ fireEvent.focus(radioGroup)
69
+ })
70
+
71
+ it('fires focus event', function () {
72
+ expect(onFieldFocus).toHaveBeenCalledOnce()
73
+ expect(onFieldFocus).toHaveBeenCalledWith('$')
74
+ })
75
+
76
+ describe('blur', function () {
77
+ beforeEach(function () {
78
+ fireEvent.blur(radioGroup)
79
+ })
80
+
81
+ it('fires blur event', function () {
82
+ expect(onFieldBlur).toHaveBeenCalledOnce()
83
+ expect(onFieldBlur).toHaveBeenCalledWith('$')
84
+ })
85
+
86
+ it('does not refire focus event', function () {
87
+ expect(onFieldFocus).toHaveBeenCalledOnce()
88
+ })
89
+ })
90
+ })
91
+ })
92
+ })
@@ -0,0 +1,77 @@
1
+ import { action } from '@storybook/addon-actions'
2
+ import {
3
+ type Meta,
4
+ type StoryObj,
5
+ } from '@storybook/react'
6
+ import { type FormProps } from 'core/props'
7
+ import { useMantineForm } from 'mantine/hooks'
8
+ import { type Field } from 'types/field'
9
+ import { SELECT_LABEL } from './select_hooks_constant'
10
+
11
+ function Component(props: FormProps<{
12
+ $: Field<string | null, string>,
13
+ }>) {
14
+ const form = useMantineForm(props)
15
+ const SelectComponent = form.select('$')
16
+ return (
17
+ <SelectComponent
18
+ data={[
19
+ 'a',
20
+ 'b',
21
+ 'c',
22
+ ]}
23
+ label={SELECT_LABEL}
24
+ />
25
+ )
26
+ }
27
+
28
+ const meta: Meta<typeof Component> = {
29
+ component: Component,
30
+ args: {
31
+ onFieldBlur: action('onFieldBlur'),
32
+ onFieldFocus: action('onFieldFocus'),
33
+ onFieldSubmit: action('onFieldSubmit'),
34
+ onFieldValueChange: action('onFieldValueChange'),
35
+ },
36
+ }
37
+
38
+ export default meta
39
+
40
+ type Story = StoryObj<typeof Component>
41
+
42
+ export const EmptySelect: Story = {
43
+ args: {
44
+ fields: {
45
+ $: {
46
+ disabled: false,
47
+ required: true,
48
+ value: '',
49
+ },
50
+ },
51
+ },
52
+ }
53
+
54
+ export const PopulatedSelect: Story = {
55
+ args: {
56
+ fields: {
57
+ $: {
58
+ disabled: false,
59
+ required: true,
60
+ value: 'a',
61
+ },
62
+ },
63
+ },
64
+ }
65
+
66
+ export const InvalidSelect: Story = {
67
+ args: {
68
+ fields: {
69
+ $: {
70
+ disabled: false,
71
+ required: true,
72
+ value: 'd',
73
+ error: 'invalid option',
74
+ },
75
+ },
76
+ },
77
+ }
@@ -0,0 +1,14 @@
1
+ import { composeStories } from '@storybook/react'
2
+ import { toArray } from '@strictly/base'
3
+ import {
4
+ render,
5
+ } from '@testing-library/react'
6
+ import * as stories from './select_hooks.stories'
7
+
8
+ const composedStories = composeStories(stories)
9
+ describe('mantine select hooks', function () {
10
+ it.each(toArray(composedStories))('renders %s', function (_name, Story) {
11
+ const wrapper = render(<Story />)
12
+ expect(wrapper.container).toMatchSnapshot()
13
+ })
14
+ })
@@ -0,0 +1 @@
1
+ export const SELECT_LABEL = 'Select'
@@ -0,0 +1 @@
1
+ export const TEXT_INPUT_LABEL = 'Text Input'
@@ -0,0 +1,124 @@
1
+ import {
2
+ PillsInputField,
3
+ Textarea,
4
+ type TextInputProps,
5
+ } from '@mantine/core'
6
+ import { action } from '@storybook/addon-actions'
7
+ import {
8
+ type Meta,
9
+ type StoryObj,
10
+ } from '@storybook/react'
11
+ import { type FormProps } from 'core/props'
12
+ import {
13
+ type SuppliedTextInputProps,
14
+ type TextInputTarget,
15
+ } from 'mantine/create_text_input'
16
+ import { useMantineForm } from 'mantine/hooks'
17
+ import { type ComponentType } from 'react'
18
+ import { type Field } from 'types/field'
19
+ import { TEXT_INPUT_LABEL } from './text_input_constants'
20
+
21
+ type StoryTextInputProps<T extends TextInputTarget> = SuppliedTextInputProps<T> & Pick<TextInputProps, 'label'>
22
+
23
+ function Component<T extends TextInputTarget>({
24
+ TextInput,
25
+ ...props
26
+ }: FormProps<{
27
+ $: Field<string, string>,
28
+ }> & {
29
+ TextInput?: ComponentType<StoryTextInputProps<T>>,
30
+ }) {
31
+ const form = useMantineForm(props)
32
+ const TextInputComponent = form.textInput<'$', StoryTextInputProps<T>>('$', TextInput)
33
+ return <TextInputComponent label={TEXT_INPUT_LABEL} />
34
+ }
35
+
36
+ const meta: Meta<typeof Component> = {
37
+ component: Component,
38
+ args: {
39
+ onFieldBlur: action('onFieldBlur'),
40
+ onFieldFocus: action('onFieldFocus'),
41
+ onFieldSubmit: action('onFieldSubmit'),
42
+ onFieldValueChange: action('onFieldValueChange'),
43
+ },
44
+ }
45
+
46
+ export default meta
47
+
48
+ type Story<
49
+ T extends TextInputTarget = HTMLInputElement,
50
+ > = StoryObj<typeof Component<T>>
51
+
52
+ export const Empty: Story = {
53
+ args: {
54
+ fields: {
55
+ $: {
56
+ disabled: false,
57
+ required: false,
58
+ value: '',
59
+ },
60
+ },
61
+ },
62
+ }
63
+
64
+ export const Populated: Story = {
65
+ args: {
66
+ fields: {
67
+ $: {
68
+ disabled: false,
69
+ required: false,
70
+ value: 'Hello',
71
+ },
72
+ },
73
+ },
74
+ }
75
+
76
+ export const Required: Story = {
77
+ args: {
78
+ fields: {
79
+ $: {
80
+ disabled: false,
81
+ required: true,
82
+ value: 'xxx',
83
+ },
84
+ },
85
+ },
86
+ }
87
+
88
+ export const Disabled: Story = {
89
+ args: {
90
+ fields: {
91
+ $: {
92
+ disabled: true,
93
+ required: false,
94
+ value: 'xxx',
95
+ },
96
+ },
97
+ },
98
+ }
99
+
100
+ export const OverriddenTextarea: Story<HTMLTextAreaElement> = {
101
+ args: {
102
+ fields: {
103
+ $: {
104
+ disabled: false,
105
+ required: false,
106
+ value: 'Textarea',
107
+ },
108
+ },
109
+ TextInput: Textarea,
110
+ },
111
+ }
112
+
113
+ export const OverriddenPillsInputField: Story = {
114
+ args: {
115
+ fields: {
116
+ $: {
117
+ disabled: false,
118
+ required: false,
119
+ value: 'PillsInputField',
120
+ },
121
+ },
122
+ TextInput: PillsInputField,
123
+ },
124
+ }